s390_pci_hpc.c 5.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * PCI Hot Plug Controller Driver for System z
 *
 * Copyright 2012 IBM Corp.
 *
 * Author(s):
 *   Jan Glauber <jang@linux.vnet.ibm.com>
 */

#define COMPONENT "zPCI hpc"
#define pr_fmt(fmt) COMPONENT ": " fmt

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/init.h>
S
Sebastian Ott 已提交
19
#include <asm/pci_debug.h>
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#include <asm/sclp.h>

#define SLOT_NAME_SIZE	10
static LIST_HEAD(s390_hotplug_slot_list);

MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com");
MODULE_DESCRIPTION("Hot Plug PCI Controller for System z");
MODULE_LICENSE("GPL");

static int zpci_fn_configured(enum zpci_state state)
{
	return state == ZPCI_FN_STATE_CONFIGURED ||
	       state == ZPCI_FN_STATE_ONLINE;
}

/*
 * struct slot - slot information for each *physical* slot
 */
struct slot {
	struct list_head slot_list;
	struct hotplug_slot *hotplug_slot;
	struct zpci_dev *zdev;
};

S
Sebastian Ott 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static inline int slot_configure(struct slot *slot)
{
	int ret = sclp_pci_configure(slot->zdev->fid);

	zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret);
	if (!ret)
		slot->zdev->state = ZPCI_FN_STATE_CONFIGURED;

	return ret;
}

static inline int slot_deconfigure(struct slot *slot)
{
	int ret = sclp_pci_deconfigure(slot->zdev->fid);

	zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret);
	if (!ret)
		slot->zdev->state = ZPCI_FN_STATE_STANDBY;

	return ret;
}

66 67 68 69 70 71 72 73
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
	struct slot *slot = hotplug_slot->private;
	int rc;

	if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
		return -EIO;

S
Sebastian Ott 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	rc = slot_configure(slot);
	if (rc)
		return rc;

	rc = zpci_enable_device(slot->zdev);
	if (rc)
		goto out_deconfigure;

	slot->zdev->state = ZPCI_FN_STATE_ONLINE;

	pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN);
	pci_bus_add_devices(slot->zdev->bus);

	return rc;

out_deconfigure:
	slot_deconfigure(slot);
91 92 93 94 95 96 97 98 99 100 101
	return rc;
}

static int disable_slot(struct hotplug_slot *hotplug_slot)
{
	struct slot *slot = hotplug_slot->private;
	int rc;

	if (!zpci_fn_configured(slot->zdev->state))
		return -EIO;

102 103 104
	if (slot->zdev->pdev)
		pci_stop_and_remove_bus_device(slot->zdev->pdev);

105 106 107
	rc = zpci_disable_device(slot->zdev);
	if (rc)
		return rc;
108

S
Sebastian Ott 已提交
109
	return slot_deconfigure(slot);
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
}

static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
	struct slot *slot = hotplug_slot->private;

	switch (slot->zdev->state) {
	case ZPCI_FN_STATE_STANDBY:
		*value = 0;
		break;
	default:
		*value = 1;
		break;
	}
	return 0;
}

static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
	/* if the slot exits it always contains a function */
	*value = 1;
	return 0;
}

static void release_slot(struct hotplug_slot *hotplug_slot)
{
	struct slot *slot = hotplug_slot->private;

	pr_debug("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot));
	kfree(slot->hotplug_slot->info);
	kfree(slot->hotplug_slot);
	kfree(slot);
}

static struct hotplug_slot_ops s390_hotplug_slot_ops = {
	.enable_slot =		enable_slot,
	.disable_slot =		disable_slot,
	.get_power_status =	get_power_status,
	.get_adapter_status =	get_adapter_status,
};

static int init_pci_slot(struct zpci_dev *zdev)
{
	struct hotplug_slot *hotplug_slot;
	struct hotplug_slot_info *info;
	char name[SLOT_NAME_SIZE];
	struct slot *slot;
	int rc;

	if (!zdev)
		return 0;

	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
	if (!slot)
		goto error;

	hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
	if (!hotplug_slot)
		goto error_hp;
	hotplug_slot->private = slot;

	slot->hotplug_slot = hotplug_slot;
	slot->zdev = zdev;

	info = kzalloc(sizeof(*info), GFP_KERNEL);
	if (!info)
		goto error_info;
	hotplug_slot->info = info;

	hotplug_slot->ops = &s390_hotplug_slot_ops;
	hotplug_slot->release = &release_slot;

	get_power_status(hotplug_slot, &info->power_status);
	get_adapter_status(hotplug_slot, &info->adapter_status);

	snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
	rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
			     ZPCI_DEVFN, name);
	if (rc) {
		pr_err("pci_hp_register failed with error %d\n", rc);
		goto error_reg;
	}
	list_add(&slot->slot_list, &s390_hotplug_slot_list);
	return 0;

error_reg:
	kfree(info);
error_info:
	kfree(hotplug_slot);
error_hp:
	kfree(slot);
error:
	return -ENOMEM;
}

static void exit_pci_slot(struct zpci_dev *zdev)
{
	struct list_head *tmp, *n;
	struct slot *slot;

	list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
		slot = list_entry(tmp, struct slot, slot_list);
		if (slot->zdev != zdev)
			continue;
		list_del(&slot->slot_list);
		pci_hp_deregister(slot->hotplug_slot);
	}
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
static struct pci_hp_callback_ops hp_ops = {
	.create_slot = init_pci_slot,
	.remove_slot = exit_pci_slot,
};

static void __init init_pci_slots(void)
{
	struct zpci_dev *zdev;

	/*
	 * Create a structure for each slot, and register that slot
	 * with the pci_hotplug subsystem.
	 */
	mutex_lock(&zpci_list_lock);
	list_for_each_entry(zdev, &zpci_list, entry) {
		init_pci_slot(zdev);
	}
	mutex_unlock(&zpci_list_lock);
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
static void __exit exit_pci_slots(void)
{
	struct list_head *tmp, *n;
	struct slot *slot;

	/*
	 * Unregister all of our slots with the pci_hotplug subsystem.
	 * Memory will be freed in release_slot() callback after slot's
	 * lifespan is finished.
	 */
	list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
		slot = list_entry(tmp, struct slot, slot_list);
		list_del(&slot->slot_list);
		pci_hp_deregister(slot->hotplug_slot);
	}
}

static int __init pci_hotplug_s390_init(void)
{
258
	if (!s390_pci_probe)
259 260
		return -EOPNOTSUPP;

261 262 263 264
	zpci_register_hp_ops(&hp_ops);
	init_pci_slots();

	return 0;
265 266 267 268 269
}

static void __exit pci_hotplug_s390_exit(void)
{
	exit_pci_slots();
270
	zpci_deregister_hp_ops();
271 272 273 274
}

module_init(pci_hotplug_s390_init);
module_exit(pci_hotplug_s390_exit);