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.c189
1 files changed, 71 insertions, 118 deletions
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c
index 30ee72268790..a55967082ef6 100644
--- a/drivers/pci/hotplug/s390_pci_hpc.c
+++ b/drivers/pci/hotplug/s390_pci_hpc.c
@@ -8,8 +8,7 @@
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -19,118 +18,99 @@
#include <asm/sclp.h>
#define SLOT_NAME_SIZE 10
-static LIST_HEAD(s390_hotplug_slot_list);
-
-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 struct slot *to_slot(struct hotplug_slot *hotplug_slot)
-{
- return container_of(hotplug_slot, struct slot, hotplug_slot);
-}
-
-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 = to_slot(hotplug_slot);
+ 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;
-
- rc = slot_configure(slot);
- if (rc)
- return rc;
+ mutex_lock(&zdev->state_lock);
+ if (zdev->state != ZPCI_FN_STATE_STANDBY) {
+ rc = -EIO;
+ goto out;
+ }
- rc = zpci_enable_device(slot->zdev);
+ rc = sclp_pci_configure(zdev->fid);
+ zpci_dbg(3, "conf fid:%x, rc:%d\n", zdev->fid, rc);
if (rc)
- goto out_deconfigure;
-
- pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN);
- pci_lock_rescan_remove();
- pci_bus_add_devices(slot->zdev->bus);
- pci_unlock_rescan_remove();
+ goto out;
+ zdev->state = ZPCI_FN_STATE_CONFIGURED;
- return rc;
-
-out_deconfigure:
- slot_deconfigure(slot);
+ rc = zpci_scan_configured_device(zdev, zdev->fh);
+out:
+ mutex_unlock(&zdev->state_lock);
return rc;
}
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
- struct slot *slot = to_slot(hotplug_slot);
- struct pci_dev *pdev;
+ struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
+ hotplug_slot);
+ struct pci_dev *pdev = NULL;
int rc;
- if (!zpci_fn_configured(slot->zdev->state))
- return -EIO;
+ mutex_lock(&zdev->state_lock);
+ if (zdev->state != ZPCI_FN_STATE_CONFIGURED) {
+ rc = -EIO;
+ goto out;
+ }
- pdev = pci_get_slot(slot->zdev->bus, ZPCI_DEVFN);
- if (pdev) {
- pci_stop_and_remove_bus_device_locked(pdev);
- pci_dev_put(pdev);
+ pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
+ if (pdev && pci_num_vf(pdev)) {
+ rc = -EBUSY;
+ goto out;
}
- rc = zpci_disable_device(slot->zdev);
- if (rc)
+ rc = zpci_deconfigure_device(zdev);
+out:
+ if (pdev)
+ pci_dev_put(pdev);
+ mutex_unlock(&zdev->state_lock);
+ return rc;
+}
+
+static int reset_slot(struct hotplug_slot *hotplug_slot, bool probe)
+{
+ struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
+ hotplug_slot);
+ int rc = -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;
- return slot_deconfigure(slot);
+ /* We can reset only if the function is configured */
+ if (zdev->state != ZPCI_FN_STATE_CONFIGURED)
+ goto out;
+
+ if (probe) {
+ rc = 0;
+ goto out;
+ }
+
+ 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 = to_slot(hotplug_slot);
-
- switch (slot->zdev->state) {
- case ZPCI_FN_STATE_STANDBY:
- *value = 0;
- break;
- default:
- *value = 1;
- break;
- }
+ struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
+ hotplug_slot);
+
+ *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;
}
@@ -138,6 +118,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
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,
};
@@ -145,44 +126,16 @@ static const struct hotplug_slot_ops s390_hotplug_slot_ops = {
int zpci_init_slot(struct zpci_dev *zdev)
{
char name[SLOT_NAME_SIZE];
- struct slot *slot;
- int rc;
-
- if (!zdev)
- return 0;
+ struct zpci_bus *zbus = zdev->zbus;
- slot = kzalloc(sizeof(*slot), GFP_KERNEL);
- if (!slot)
- goto error;
-
- slot->zdev = zdev;
- slot->hotplug_slot.ops = &s390_hotplug_slot_ops;
+ 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)
- goto error_reg;
-
- list_add(&slot->slot_list, &s390_hotplug_slot_list);
- return 0;
-
-error_reg:
- kfree(slot);
-error:
- return -ENOMEM;
+ return pci_hp_register(&zdev->hotplug_slot, zbus->bus,
+ zdev->devfn, name);
}
void zpci_exit_slot(struct zpci_dev *zdev)
{
- struct slot *slot, *next;
-
- list_for_each_entry_safe(slot, next, &s390_hotplug_slot_list,
- slot_list) {
- if (slot->zdev != zdev)
- continue;
- list_del(&slot->slot_list);
- pci_hp_deregister(&slot->hotplug_slot);
- kfree(slot);
- }
+ pci_hp_deregister(&zdev->hotplug_slot);
}