17441b062SJan Glauber /* 27441b062SJan Glauber * PCI Hot Plug Controller Driver for System z 37441b062SJan Glauber * 47441b062SJan Glauber * Copyright 2012 IBM Corp. 57441b062SJan Glauber * 67441b062SJan Glauber * Author(s): 77441b062SJan Glauber * Jan Glauber <jang@linux.vnet.ibm.com> 87441b062SJan Glauber */ 97441b062SJan Glauber 107441b062SJan Glauber #define COMPONENT "zPCI hpc" 117441b062SJan Glauber #define pr_fmt(fmt) COMPONENT ": " fmt 127441b062SJan Glauber 137441b062SJan Glauber #include <linux/module.h> 147441b062SJan Glauber #include <linux/kernel.h> 157441b062SJan Glauber #include <linux/slab.h> 167441b062SJan Glauber #include <linux/pci.h> 177441b062SJan Glauber #include <linux/pci_hotplug.h> 187441b062SJan Glauber #include <linux/init.h> 19a2ab8333SSebastian Ott #include <asm/pci_debug.h> 207441b062SJan Glauber #include <asm/sclp.h> 217441b062SJan Glauber 227441b062SJan Glauber #define SLOT_NAME_SIZE 10 237441b062SJan Glauber static LIST_HEAD(s390_hotplug_slot_list); 247441b062SJan Glauber 257441b062SJan Glauber MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com"); 267441b062SJan Glauber MODULE_DESCRIPTION("Hot Plug PCI Controller for System z"); 277441b062SJan Glauber MODULE_LICENSE("GPL"); 287441b062SJan Glauber 297441b062SJan Glauber static int zpci_fn_configured(enum zpci_state state) 307441b062SJan Glauber { 317441b062SJan Glauber return state == ZPCI_FN_STATE_CONFIGURED || 327441b062SJan Glauber state == ZPCI_FN_STATE_ONLINE; 337441b062SJan Glauber } 347441b062SJan Glauber 357441b062SJan Glauber /* 367441b062SJan Glauber * struct slot - slot information for each *physical* slot 377441b062SJan Glauber */ 387441b062SJan Glauber struct slot { 397441b062SJan Glauber struct list_head slot_list; 407441b062SJan Glauber struct hotplug_slot *hotplug_slot; 417441b062SJan Glauber struct zpci_dev *zdev; 427441b062SJan Glauber }; 437441b062SJan Glauber 447441b062SJan Glauber static int enable_slot(struct hotplug_slot *hotplug_slot) 457441b062SJan Glauber { 467441b062SJan Glauber struct slot *slot = hotplug_slot->private; 477441b062SJan Glauber int rc; 487441b062SJan Glauber 497441b062SJan Glauber if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) 507441b062SJan Glauber return -EIO; 517441b062SJan Glauber 527441b062SJan Glauber rc = sclp_pci_configure(slot->zdev->fid); 53a2ab8333SSebastian Ott zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, rc); 547441b062SJan Glauber if (!rc) { 557441b062SJan Glauber slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; 567441b062SJan Glauber /* automatically scan the device after is was configured */ 577441b062SJan Glauber zpci_enable_device(slot->zdev); 587441b062SJan Glauber zpci_scan_device(slot->zdev); 597441b062SJan Glauber } 607441b062SJan Glauber return rc; 617441b062SJan Glauber } 627441b062SJan Glauber 637441b062SJan Glauber static int disable_slot(struct hotplug_slot *hotplug_slot) 647441b062SJan Glauber { 657441b062SJan Glauber struct slot *slot = hotplug_slot->private; 667441b062SJan Glauber int rc; 677441b062SJan Glauber 687441b062SJan Glauber if (!zpci_fn_configured(slot->zdev->state)) 697441b062SJan Glauber return -EIO; 707441b062SJan Glauber 71*cb65a669SSebastian Ott rc = zpci_disable_device(slot->zdev); 72*cb65a669SSebastian Ott if (rc) 73*cb65a669SSebastian Ott return rc; 747441b062SJan Glauber /* TODO: we rely on the user to unbind/remove the device, is that plausible 757441b062SJan Glauber * or do we need to trigger that here? 767441b062SJan Glauber */ 777441b062SJan Glauber rc = sclp_pci_deconfigure(slot->zdev->fid); 78a2ab8333SSebastian Ott zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, rc); 79*cb65a669SSebastian Ott if (!rc) 807441b062SJan Glauber slot->zdev->state = ZPCI_FN_STATE_STANDBY; 817441b062SJan Glauber return rc; 827441b062SJan Glauber } 837441b062SJan Glauber 847441b062SJan Glauber static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) 857441b062SJan Glauber { 867441b062SJan Glauber struct slot *slot = hotplug_slot->private; 877441b062SJan Glauber 887441b062SJan Glauber switch (slot->zdev->state) { 897441b062SJan Glauber case ZPCI_FN_STATE_STANDBY: 907441b062SJan Glauber *value = 0; 917441b062SJan Glauber break; 927441b062SJan Glauber default: 937441b062SJan Glauber *value = 1; 947441b062SJan Glauber break; 957441b062SJan Glauber } 967441b062SJan Glauber return 0; 977441b062SJan Glauber } 987441b062SJan Glauber 997441b062SJan Glauber static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) 1007441b062SJan Glauber { 1017441b062SJan Glauber /* if the slot exits it always contains a function */ 1027441b062SJan Glauber *value = 1; 1037441b062SJan Glauber return 0; 1047441b062SJan Glauber } 1057441b062SJan Glauber 1067441b062SJan Glauber static void release_slot(struct hotplug_slot *hotplug_slot) 1077441b062SJan Glauber { 1087441b062SJan Glauber struct slot *slot = hotplug_slot->private; 1097441b062SJan Glauber 1107441b062SJan Glauber pr_debug("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot)); 1117441b062SJan Glauber kfree(slot->hotplug_slot->info); 1127441b062SJan Glauber kfree(slot->hotplug_slot); 1137441b062SJan Glauber kfree(slot); 1147441b062SJan Glauber } 1157441b062SJan Glauber 1167441b062SJan Glauber static struct hotplug_slot_ops s390_hotplug_slot_ops = { 1177441b062SJan Glauber .enable_slot = enable_slot, 1187441b062SJan Glauber .disable_slot = disable_slot, 1197441b062SJan Glauber .get_power_status = get_power_status, 1207441b062SJan Glauber .get_adapter_status = get_adapter_status, 1217441b062SJan Glauber }; 1227441b062SJan Glauber 1237441b062SJan Glauber static int init_pci_slot(struct zpci_dev *zdev) 1247441b062SJan Glauber { 1257441b062SJan Glauber struct hotplug_slot *hotplug_slot; 1267441b062SJan Glauber struct hotplug_slot_info *info; 1277441b062SJan Glauber char name[SLOT_NAME_SIZE]; 1287441b062SJan Glauber struct slot *slot; 1297441b062SJan Glauber int rc; 1307441b062SJan Glauber 1317441b062SJan Glauber if (!zdev) 1327441b062SJan Glauber return 0; 1337441b062SJan Glauber 1347441b062SJan Glauber slot = kzalloc(sizeof(*slot), GFP_KERNEL); 1357441b062SJan Glauber if (!slot) 1367441b062SJan Glauber goto error; 1377441b062SJan Glauber 1387441b062SJan Glauber hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); 1397441b062SJan Glauber if (!hotplug_slot) 1407441b062SJan Glauber goto error_hp; 1417441b062SJan Glauber hotplug_slot->private = slot; 1427441b062SJan Glauber 1437441b062SJan Glauber slot->hotplug_slot = hotplug_slot; 1447441b062SJan Glauber slot->zdev = zdev; 1457441b062SJan Glauber 1467441b062SJan Glauber info = kzalloc(sizeof(*info), GFP_KERNEL); 1477441b062SJan Glauber if (!info) 1487441b062SJan Glauber goto error_info; 1497441b062SJan Glauber hotplug_slot->info = info; 1507441b062SJan Glauber 1517441b062SJan Glauber hotplug_slot->ops = &s390_hotplug_slot_ops; 1527441b062SJan Glauber hotplug_slot->release = &release_slot; 1537441b062SJan Glauber 1547441b062SJan Glauber get_power_status(hotplug_slot, &info->power_status); 1557441b062SJan Glauber get_adapter_status(hotplug_slot, &info->adapter_status); 1567441b062SJan Glauber 1577441b062SJan Glauber snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); 1587441b062SJan Glauber rc = pci_hp_register(slot->hotplug_slot, zdev->bus, 1597441b062SJan Glauber ZPCI_DEVFN, name); 1607441b062SJan Glauber if (rc) { 1617441b062SJan Glauber pr_err("pci_hp_register failed with error %d\n", rc); 1627441b062SJan Glauber goto error_reg; 1637441b062SJan Glauber } 1647441b062SJan Glauber list_add(&slot->slot_list, &s390_hotplug_slot_list); 1657441b062SJan Glauber return 0; 1667441b062SJan Glauber 1677441b062SJan Glauber error_reg: 1687441b062SJan Glauber kfree(info); 1697441b062SJan Glauber error_info: 1707441b062SJan Glauber kfree(hotplug_slot); 1717441b062SJan Glauber error_hp: 1727441b062SJan Glauber kfree(slot); 1737441b062SJan Glauber error: 1747441b062SJan Glauber return -ENOMEM; 1757441b062SJan Glauber } 1767441b062SJan Glauber 1777441b062SJan Glauber static void exit_pci_slot(struct zpci_dev *zdev) 1787441b062SJan Glauber { 1797441b062SJan Glauber struct list_head *tmp, *n; 1807441b062SJan Glauber struct slot *slot; 1817441b062SJan Glauber 1827441b062SJan Glauber list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { 1837441b062SJan Glauber slot = list_entry(tmp, struct slot, slot_list); 1847441b062SJan Glauber if (slot->zdev != zdev) 1857441b062SJan Glauber continue; 1867441b062SJan Glauber list_del(&slot->slot_list); 1877441b062SJan Glauber pci_hp_deregister(slot->hotplug_slot); 1887441b062SJan Glauber } 1897441b062SJan Glauber } 1907441b062SJan Glauber 19153923354SSebastian Ott static struct pci_hp_callback_ops hp_ops = { 19253923354SSebastian Ott .create_slot = init_pci_slot, 19353923354SSebastian Ott .remove_slot = exit_pci_slot, 19453923354SSebastian Ott }; 19553923354SSebastian Ott 19653923354SSebastian Ott static void __init init_pci_slots(void) 19753923354SSebastian Ott { 19853923354SSebastian Ott struct zpci_dev *zdev; 19953923354SSebastian Ott 20053923354SSebastian Ott /* 20153923354SSebastian Ott * Create a structure for each slot, and register that slot 20253923354SSebastian Ott * with the pci_hotplug subsystem. 20353923354SSebastian Ott */ 20453923354SSebastian Ott mutex_lock(&zpci_list_lock); 20553923354SSebastian Ott list_for_each_entry(zdev, &zpci_list, entry) { 20653923354SSebastian Ott init_pci_slot(zdev); 20753923354SSebastian Ott } 20853923354SSebastian Ott mutex_unlock(&zpci_list_lock); 20953923354SSebastian Ott } 21053923354SSebastian Ott 2117441b062SJan Glauber static void __exit exit_pci_slots(void) 2127441b062SJan Glauber { 2137441b062SJan Glauber struct list_head *tmp, *n; 2147441b062SJan Glauber struct slot *slot; 2157441b062SJan Glauber 2167441b062SJan Glauber /* 2177441b062SJan Glauber * Unregister all of our slots with the pci_hotplug subsystem. 2187441b062SJan Glauber * Memory will be freed in release_slot() callback after slot's 2197441b062SJan Glauber * lifespan is finished. 2207441b062SJan Glauber */ 2217441b062SJan Glauber list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { 2227441b062SJan Glauber slot = list_entry(tmp, struct slot, slot_list); 2237441b062SJan Glauber list_del(&slot->slot_list); 2247441b062SJan Glauber pci_hp_deregister(slot->hotplug_slot); 2257441b062SJan Glauber } 2267441b062SJan Glauber } 2277441b062SJan Glauber 2287441b062SJan Glauber static int __init pci_hotplug_s390_init(void) 2297441b062SJan Glauber { 2301e5635d1SHeiko Carstens if (!s390_pci_probe) 2317441b062SJan Glauber return -EOPNOTSUPP; 2327441b062SJan Glauber 23353923354SSebastian Ott zpci_register_hp_ops(&hp_ops); 23453923354SSebastian Ott init_pci_slots(); 23553923354SSebastian Ott 23653923354SSebastian Ott return 0; 2377441b062SJan Glauber } 2387441b062SJan Glauber 2397441b062SJan Glauber static void __exit pci_hotplug_s390_exit(void) 2407441b062SJan Glauber { 2417441b062SJan Glauber exit_pci_slots(); 24253923354SSebastian Ott zpci_deregister_hp_ops(); 2437441b062SJan Glauber } 2447441b062SJan Glauber 2457441b062SJan Glauber module_init(pci_hotplug_s390_init); 2467441b062SJan Glauber module_exit(pci_hotplug_s390_exit); 247