linux/drivers/pci/hotplug/s390_pci_hpc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * PCI Hot Plug Controller Driver for System z
   4 *
   5 * Copyright 2012 IBM Corp.
   6 *
   7 * Author(s):
   8 *   Jan Glauber <jang@linux.vnet.ibm.com>
   9 */
  10
  11#define KMSG_COMPONENT "zpci"
  12#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  13
  14#include <linux/kernel.h>
  15#include <linux/slab.h>
  16#include <linux/pci.h>
  17#include <linux/pci_hotplug.h>
  18#include <asm/pci_debug.h>
  19#include <asm/sclp.h>
  20
  21#define SLOT_NAME_SIZE  10
  22static LIST_HEAD(s390_hotplug_slot_list);
  23
  24static int zpci_fn_configured(enum zpci_state state)
  25{
  26        return state == ZPCI_FN_STATE_CONFIGURED ||
  27               state == ZPCI_FN_STATE_ONLINE;
  28}
  29
  30/*
  31 * struct slot - slot information for each *physical* slot
  32 */
  33struct slot {
  34        struct list_head slot_list;
  35        struct hotplug_slot *hotplug_slot;
  36        struct zpci_dev *zdev;
  37};
  38
  39static inline int slot_configure(struct slot *slot)
  40{
  41        int ret = sclp_pci_configure(slot->zdev->fid);
  42
  43        zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret);
  44        if (!ret)
  45                slot->zdev->state = ZPCI_FN_STATE_CONFIGURED;
  46
  47        return ret;
  48}
  49
  50static inline int slot_deconfigure(struct slot *slot)
  51{
  52        int ret = sclp_pci_deconfigure(slot->zdev->fid);
  53
  54        zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret);
  55        if (!ret)
  56                slot->zdev->state = ZPCI_FN_STATE_STANDBY;
  57
  58        return ret;
  59}
  60
  61static int enable_slot(struct hotplug_slot *hotplug_slot)
  62{
  63        struct slot *slot = hotplug_slot->private;
  64        int rc;
  65
  66        if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
  67                return -EIO;
  68
  69        rc = slot_configure(slot);
  70        if (rc)
  71                return rc;
  72
  73        rc = zpci_enable_device(slot->zdev);
  74        if (rc)
  75                goto out_deconfigure;
  76
  77        pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN);
  78        pci_lock_rescan_remove();
  79        pci_bus_add_devices(slot->zdev->bus);
  80        pci_unlock_rescan_remove();
  81
  82        return rc;
  83
  84out_deconfigure:
  85        slot_deconfigure(slot);
  86        return rc;
  87}
  88
  89static int disable_slot(struct hotplug_slot *hotplug_slot)
  90{
  91        struct slot *slot = hotplug_slot->private;
  92        struct pci_dev *pdev;
  93        int rc;
  94
  95        if (!zpci_fn_configured(slot->zdev->state))
  96                return -EIO;
  97
  98        pdev = pci_get_slot(slot->zdev->bus, ZPCI_DEVFN);
  99        if (pdev) {
 100                pci_stop_and_remove_bus_device_locked(pdev);
 101                pci_dev_put(pdev);
 102        }
 103
 104        rc = zpci_disable_device(slot->zdev);
 105        if (rc)
 106                return rc;
 107
 108        return slot_deconfigure(slot);
 109}
 110
 111static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 112{
 113        struct slot *slot = hotplug_slot->private;
 114
 115        switch (slot->zdev->state) {
 116        case ZPCI_FN_STATE_STANDBY:
 117                *value = 0;
 118                break;
 119        default:
 120                *value = 1;
 121                break;
 122        }
 123        return 0;
 124}
 125
 126static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 127{
 128        /* if the slot exits it always contains a function */
 129        *value = 1;
 130        return 0;
 131}
 132
 133static struct hotplug_slot_ops s390_hotplug_slot_ops = {
 134        .enable_slot =          enable_slot,
 135        .disable_slot =         disable_slot,
 136        .get_power_status =     get_power_status,
 137        .get_adapter_status =   get_adapter_status,
 138};
 139
 140int zpci_init_slot(struct zpci_dev *zdev)
 141{
 142        struct hotplug_slot *hotplug_slot;
 143        struct hotplug_slot_info *info;
 144        char name[SLOT_NAME_SIZE];
 145        struct slot *slot;
 146        int rc;
 147
 148        if (!zdev)
 149                return 0;
 150
 151        slot = kzalloc(sizeof(*slot), GFP_KERNEL);
 152        if (!slot)
 153                goto error;
 154
 155        hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
 156        if (!hotplug_slot)
 157                goto error_hp;
 158        hotplug_slot->private = slot;
 159
 160        slot->hotplug_slot = hotplug_slot;
 161        slot->zdev = zdev;
 162
 163        info = kzalloc(sizeof(*info), GFP_KERNEL);
 164        if (!info)
 165                goto error_info;
 166        hotplug_slot->info = info;
 167
 168        hotplug_slot->ops = &s390_hotplug_slot_ops;
 169
 170        get_power_status(hotplug_slot, &info->power_status);
 171        get_adapter_status(hotplug_slot, &info->adapter_status);
 172
 173        snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
 174        rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
 175                             ZPCI_DEVFN, name);
 176        if (rc)
 177                goto error_reg;
 178
 179        list_add(&slot->slot_list, &s390_hotplug_slot_list);
 180        return 0;
 181
 182error_reg:
 183        kfree(info);
 184error_info:
 185        kfree(hotplug_slot);
 186error_hp:
 187        kfree(slot);
 188error:
 189        return -ENOMEM;
 190}
 191
 192void zpci_exit_slot(struct zpci_dev *zdev)
 193{
 194        struct slot *slot, *next;
 195
 196        list_for_each_entry_safe(slot, next, &s390_hotplug_slot_list,
 197                                 slot_list) {
 198                if (slot->zdev != zdev)
 199                        continue;
 200                list_del(&slot->slot_list);
 201                pci_hp_deregister(slot->hotplug_slot);
 202                kfree(slot->hotplug_slot->info);
 203                kfree(slot->hotplug_slot);
 204                kfree(slot);
 205        }
 206}
 207