diff options
Diffstat (limited to 'drivers/pci/hotplug/s390_pci_hpc.c')
| -rw-r--r-- | drivers/pci/hotplug/s390_pci_hpc.c | 281 |
1 files changed, 74 insertions, 207 deletions
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index ea3fa90d020a..a55967082ef6 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * PCI Hot Plug Controller Driver for System z * @@ -7,268 +8,134 @@ * Jan Glauber <jang@linux.vnet.ibm.com> */ -#define COMPONENT "zPCI hpc" -#define pr_fmt(fmt) COMPONENT ": " fmt +#define pr_fmt(fmt) "zpci: " 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> #include <asm/pci_debug.h> #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; -}; - -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; -} static int enable_slot(struct hotplug_slot *hotplug_slot) { - struct slot *slot = hotplug_slot->private; + struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, + hotplug_slot); int rc; - if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) - return -EIO; + mutex_lock(&zdev->state_lock); + if (zdev->state != ZPCI_FN_STATE_STANDBY) { + rc = -EIO; + goto out; + } - rc = slot_configure(slot); + rc = sclp_pci_configure(zdev->fid); + zpci_dbg(3, "conf fid:%x, rc:%d\n", zdev->fid, rc); if (rc) - return rc; + goto out; + zdev->state = ZPCI_FN_STATE_CONFIGURED; - rc = zpci_enable_device(slot->zdev); - if (rc) - goto out_deconfigure; + rc = zpci_scan_configured_device(zdev, zdev->fh); +out: + mutex_unlock(&zdev->state_lock); + return rc; +} - slot->zdev->state = ZPCI_FN_STATE_ONLINE; +static int disable_slot(struct hotplug_slot *hotplug_slot) +{ + struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, + hotplug_slot); + struct pci_dev *pdev = NULL; + int rc; - pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); - pci_bus_add_devices(slot->zdev->bus); + mutex_lock(&zdev->state_lock); + if (zdev->state != ZPCI_FN_STATE_CONFIGURED) { + rc = -EIO; + goto out; + } - return rc; + pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); + if (pdev && pci_num_vf(pdev)) { + rc = -EBUSY; + goto out; + } -out_deconfigure: - slot_deconfigure(slot); + rc = zpci_deconfigure_device(zdev); +out: + if (pdev) + pci_dev_put(pdev); + mutex_unlock(&zdev->state_lock); return rc; } -static int disable_slot(struct hotplug_slot *hotplug_slot) +static int reset_slot(struct hotplug_slot *hotplug_slot, bool probe) { - struct slot *slot = hotplug_slot->private; - int rc; + struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, + hotplug_slot); + int rc = -EIO; - if (!zpci_fn_configured(slot->zdev->state)) - return -EIO; + /* + * If we can't get the zdev->state_lock the device state is + * currently undergoing a transition and we bail out - just + * the same as if the device's state is not configured at all. + */ + if (!mutex_trylock(&zdev->state_lock)) + return rc; - if (slot->zdev->pdev) - pci_stop_and_remove_bus_device(slot->zdev->pdev); + /* We can reset only if the function is configured */ + if (zdev->state != ZPCI_FN_STATE_CONFIGURED) + goto out; - rc = zpci_disable_device(slot->zdev); - if (rc) - return rc; + if (probe) { + rc = 0; + goto out; + } - return slot_deconfigure(slot); + rc = zpci_hot_reset_device(zdev); +out: + mutex_unlock(&zdev->state_lock); + return rc; } static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct slot *slot = hotplug_slot->private; + struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, + hotplug_slot); - switch (slot->zdev->state) { - case ZPCI_FN_STATE_STANDBY: - *value = 0; - break; - default: - *value = 1; - break; - } + *value = zpci_is_device_configured(zdev) ? 1 : 0; return 0; } static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - /* if the slot exits it always contains a function */ + /* if the slot exists 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 = { +static const struct hotplug_slot_ops s390_hotplug_slot_ops = { .enable_slot = enable_slot, .disable_slot = disable_slot, + .reset_slot = reset_slot, .get_power_status = get_power_status, .get_adapter_status = get_adapter_status, }; -static int init_pci_slot(struct zpci_dev *zdev) +int zpci_init_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; + struct zpci_bus *zbus = zdev->zbus; - 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); + zdev->hotplug_slot.ops = &s390_hotplug_slot_ops; 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); - } -} - -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); -} - -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) -{ - if (!s390_pci_probe) - return -EOPNOTSUPP; - - zpci_register_hp_ops(&hp_ops); - init_pci_slots(); - - return 0; + return pci_hp_register(&zdev->hotplug_slot, zbus->bus, + zdev->devfn, name); } -static void __exit pci_hotplug_s390_exit(void) +void zpci_exit_slot(struct zpci_dev *zdev) { - exit_pci_slots(); - zpci_deregister_hp_ops(); + pci_hp_deregister(&zdev->hotplug_slot); } - -module_init(pci_hotplug_s390_init); -module_exit(pci_hotplug_s390_exit); |
