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