summaryrefslogtreecommitdiff
path: root/drivers/pci/hotplug/s390_pci_hpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/hotplug/s390_pci_hpc.c')
-rw-r--r--drivers/pci/hotplug/s390_pci_hpc.c281
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);