Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/pci: PCI hotplug support via SCLP

Add SCLP PCI configure/deconfigure and implement a PCI hotplug
controller (s390_pci_hpc). The hotplug controller creates a slot
for every PCI function in stand-by or configured state. The PCI
functions are named after the PCI function ID (fid). By writing to
the power attribute in /sys/bus/pci/slots/<fid>/power the PCI function
is moved to stand-by or configured state. If moved to the configured
state the device is automatically scanned by the s390 PCI layer.

Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Jan Glauber and committed by
Martin Schwidefsky
7441b062 cbc0dd1f

+351 -2
+11
arch/s390/include/asm/pci.h
··· 95 95 enum pci_bus_speed max_bus_speed; 96 96 }; 97 97 98 + struct pci_hp_callback_ops { 99 + int (*create_slot) (struct zpci_dev *zdev); 100 + void (*remove_slot) (struct zpci_dev *zdev); 101 + }; 102 + 98 103 static inline bool zdev_enabled(struct zpci_dev *zdev) 99 104 { 100 105 return (zdev->fh & (1UL << 31)) ? true : false; ··· 144 139 /* DMA */ 145 140 int zpci_dma_init(void); 146 141 void zpci_dma_exit(void); 142 + 143 + /* Hotplug */ 144 + extern struct mutex zpci_list_lock; 145 + extern struct list_head zpci_list; 146 + extern struct pci_hp_callback_ops hotplug_ops; 147 + extern unsigned int pci_probe; 147 148 148 149 #endif
+2
arch/s390/include/asm/sclp.h
··· 55 55 void sclp_get_ipl_info(struct sclp_ipl_info *info); 56 56 bool sclp_has_linemode(void); 57 57 bool sclp_has_vt220(void); 58 + int sclp_pci_configure(u32 fid); 59 + int sclp_pci_deconfigure(u32 fid); 58 60 59 61 #endif /* _ASM_S390_SCLP_H */
+9
arch/s390/pci/pci.c
··· 47 47 48 48 /* list of all detected zpci devices */ 49 49 LIST_HEAD(zpci_list); 50 + EXPORT_SYMBOL_GPL(zpci_list); 50 51 DEFINE_MUTEX(zpci_list_lock); 52 + EXPORT_SYMBOL_GPL(zpci_list_lock); 53 + 54 + struct pci_hp_callback_ops hotplug_ops; 55 + EXPORT_SYMBOL_GPL(hotplug_ops); 51 56 52 57 static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); 53 58 static DEFINE_SPINLOCK(zpci_domain_lock); ··· 940 935 941 936 mutex_lock(&zpci_list_lock); 942 937 list_add_tail(&zdev->entry, &zpci_list); 938 + if (hotplug_ops.create_slot) 939 + hotplug_ops.create_slot(zdev); 943 940 mutex_unlock(&zpci_list_lock); 944 941 945 942 if (zdev->state == ZPCI_FN_STATE_STANDBY) ··· 955 948 out_start: 956 949 mutex_lock(&zpci_list_lock); 957 950 list_del(&zdev->entry); 951 + if (hotplug_ops.remove_slot) 952 + hotplug_ops.remove_slot(zdev); 958 953 mutex_unlock(&zpci_list_lock); 959 954 out_bus: 960 955 zpci_free_domain(zdev);
+11
drivers/pci/hotplug/Kconfig
··· 151 151 152 152 When in doubt, say N. 153 153 154 + config HOTPLUG_PCI_S390 155 + tristate "System z PCI Hotplug Support" 156 + depends on S390 && 64BIT 157 + help 158 + Say Y here if you want to use the System z PCI Hotplug 159 + driver for PCI devices. Without this driver it is not 160 + possible to access stand-by PCI functions nor to deconfigure 161 + PCI functions. 162 + 163 + When in doubt, say Y. 164 + 154 165 endif # HOTPLUG_PCI
+1
drivers/pci/hotplug/Makefile
··· 18 18 obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o 19 19 obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o 20 20 obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o 21 + obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o 21 22 22 23 # acpiphp_ibm extends acpiphp, so should be linked afterwards. 23 24
+252
drivers/pci/hotplug/s390_pci_hpc.c
··· 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 COMPONENT "zPCI hpc" 11 + #define pr_fmt(fmt) 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 <linux/init.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 int enable_slot(struct hotplug_slot *hotplug_slot) 44 + { 45 + struct slot *slot = hotplug_slot->private; 46 + int rc; 47 + 48 + if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) 49 + return -EIO; 50 + 51 + rc = sclp_pci_configure(slot->zdev->fid); 52 + if (!rc) { 53 + slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; 54 + /* automatically scan the device after is was configured */ 55 + zpci_enable_device(slot->zdev); 56 + zpci_scan_device(slot->zdev); 57 + } 58 + return rc; 59 + } 60 + 61 + static int disable_slot(struct hotplug_slot *hotplug_slot) 62 + { 63 + struct slot *slot = hotplug_slot->private; 64 + int rc; 65 + 66 + if (!zpci_fn_configured(slot->zdev->state)) 67 + return -EIO; 68 + 69 + /* TODO: we rely on the user to unbind/remove the device, is that plausible 70 + * or do we need to trigger that here? 71 + */ 72 + rc = sclp_pci_deconfigure(slot->zdev->fid); 73 + if (!rc) { 74 + /* Fixme: better call List-PCI to find the disabled FH 75 + for the FID since the FH should be opaque... */ 76 + slot->zdev->fh &= 0x7fffffff; 77 + slot->zdev->state = ZPCI_FN_STATE_STANDBY; 78 + } 79 + return rc; 80 + } 81 + 82 + static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) 83 + { 84 + struct slot *slot = hotplug_slot->private; 85 + 86 + switch (slot->zdev->state) { 87 + case ZPCI_FN_STATE_STANDBY: 88 + *value = 0; 89 + break; 90 + default: 91 + *value = 1; 92 + break; 93 + } 94 + return 0; 95 + } 96 + 97 + static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) 98 + { 99 + /* if the slot exits it always contains a function */ 100 + *value = 1; 101 + return 0; 102 + } 103 + 104 + static void release_slot(struct hotplug_slot *hotplug_slot) 105 + { 106 + struct slot *slot = hotplug_slot->private; 107 + 108 + pr_debug("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot)); 109 + kfree(slot->hotplug_slot->info); 110 + kfree(slot->hotplug_slot); 111 + kfree(slot); 112 + } 113 + 114 + static struct hotplug_slot_ops s390_hotplug_slot_ops = { 115 + .enable_slot = enable_slot, 116 + .disable_slot = disable_slot, 117 + .get_power_status = get_power_status, 118 + .get_adapter_status = get_adapter_status, 119 + }; 120 + 121 + static int init_pci_slot(struct zpci_dev *zdev) 122 + { 123 + struct hotplug_slot *hotplug_slot; 124 + struct hotplug_slot_info *info; 125 + char name[SLOT_NAME_SIZE]; 126 + struct slot *slot; 127 + int rc; 128 + 129 + if (!zdev) 130 + return 0; 131 + 132 + slot = kzalloc(sizeof(*slot), GFP_KERNEL); 133 + if (!slot) 134 + goto error; 135 + 136 + hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); 137 + if (!hotplug_slot) 138 + goto error_hp; 139 + hotplug_slot->private = slot; 140 + 141 + slot->hotplug_slot = hotplug_slot; 142 + slot->zdev = zdev; 143 + 144 + info = kzalloc(sizeof(*info), GFP_KERNEL); 145 + if (!info) 146 + goto error_info; 147 + hotplug_slot->info = info; 148 + 149 + hotplug_slot->ops = &s390_hotplug_slot_ops; 150 + hotplug_slot->release = &release_slot; 151 + 152 + get_power_status(hotplug_slot, &info->power_status); 153 + get_adapter_status(hotplug_slot, &info->adapter_status); 154 + 155 + snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); 156 + rc = pci_hp_register(slot->hotplug_slot, zdev->bus, 157 + ZPCI_DEVFN, name); 158 + if (rc) { 159 + pr_err("pci_hp_register failed with error %d\n", rc); 160 + goto error_reg; 161 + } 162 + list_add(&slot->slot_list, &s390_hotplug_slot_list); 163 + return 0; 164 + 165 + error_reg: 166 + kfree(info); 167 + error_info: 168 + kfree(hotplug_slot); 169 + error_hp: 170 + kfree(slot); 171 + error: 172 + return -ENOMEM; 173 + } 174 + 175 + static int __init init_pci_slots(void) 176 + { 177 + struct zpci_dev *zdev; 178 + int device = 0; 179 + 180 + /* 181 + * Create a structure for each slot, and register that slot 182 + * with the pci_hotplug subsystem. 183 + */ 184 + mutex_lock(&zpci_list_lock); 185 + list_for_each_entry(zdev, &zpci_list, entry) { 186 + init_pci_slot(zdev); 187 + device++; 188 + } 189 + 190 + mutex_unlock(&zpci_list_lock); 191 + return (device) ? 0 : -ENODEV; 192 + } 193 + 194 + static void exit_pci_slot(struct zpci_dev *zdev) 195 + { 196 + struct list_head *tmp, *n; 197 + struct slot *slot; 198 + 199 + list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { 200 + slot = list_entry(tmp, struct slot, slot_list); 201 + if (slot->zdev != zdev) 202 + continue; 203 + list_del(&slot->slot_list); 204 + pci_hp_deregister(slot->hotplug_slot); 205 + } 206 + } 207 + 208 + static void __exit exit_pci_slots(void) 209 + { 210 + struct list_head *tmp, *n; 211 + struct slot *slot; 212 + 213 + /* 214 + * Unregister all of our slots with the pci_hotplug subsystem. 215 + * Memory will be freed in release_slot() callback after slot's 216 + * lifespan is finished. 217 + */ 218 + list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { 219 + slot = list_entry(tmp, struct slot, slot_list); 220 + list_del(&slot->slot_list); 221 + pci_hp_deregister(slot->hotplug_slot); 222 + } 223 + } 224 + 225 + static int __init pci_hotplug_s390_init(void) 226 + { 227 + /* 228 + * Do specific initialization stuff for your driver here 229 + * like initializing your controller hardware (if any) and 230 + * determining the number of slots you have in the system 231 + * right now. 232 + */ 233 + 234 + if (!pci_probe) 235 + return -EOPNOTSUPP; 236 + 237 + /* register callbacks for slot handling from arch code */ 238 + mutex_lock(&zpci_list_lock); 239 + hotplug_ops.create_slot = init_pci_slot; 240 + hotplug_ops.remove_slot = exit_pci_slot; 241 + mutex_unlock(&zpci_list_lock); 242 + pr_info("registered hotplug slot callbacks\n"); 243 + return init_pci_slots(); 244 + } 245 + 246 + static void __exit pci_hotplug_s390_exit(void) 247 + { 248 + exit_pci_slots(); 249 + } 250 + 251 + module_init(pci_hotplug_s390_init); 252 + module_exit(pci_hotplug_s390_exit);
+2 -1
drivers/s390/char/sclp.h
··· 1 1 /* 2 - * Copyright IBM Corp. 1999, 2009 2 + * Copyright IBM Corp. 1999,2012 3 3 * 4 4 * Author(s): Martin Peschke <mpeschke@de.ibm.com> 5 5 * Martin Schwidefsky <schwidefsky@de.ibm.com> ··· 103 103 #define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) 104 104 #define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL) 105 105 #define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL) 106 + #define SCLP_HAS_PCI_RECONFIG (sclp_facilities & 0x0000000040000000ULL) 106 107 107 108 108 109 struct gds_subvector {
+63 -1
drivers/s390/char/sclp_cmd.c
··· 1 1 /* 2 - * Copyright IBM Corp. 2007, 2009 2 + * Copyright IBM Corp. 2007,2012 3 3 * 4 4 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, 5 5 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> ··· 12 12 #include <linux/init.h> 13 13 #include <linux/errno.h> 14 14 #include <linux/err.h> 15 + #include <linux/export.h> 15 16 #include <linux/slab.h> 16 17 #include <linux/string.h> 17 18 #include <linux/mm.h> ··· 700 699 __initcall(sclp_detect_standby_memory); 701 700 702 701 #endif /* CONFIG_MEMORY_HOTPLUG */ 702 + 703 + /* 704 + * PCI I/O adapter configuration related functions. 705 + */ 706 + #define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 707 + #define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 708 + 709 + #define SCLP_RECONFIG_PCI_ATPYE 2 710 + 711 + struct pci_cfg_sccb { 712 + struct sccb_header header; 713 + u8 atype; /* adapter type */ 714 + u8 reserved1; 715 + u16 reserved2; 716 + u32 aid; /* adapter identifier */ 717 + } __packed; 718 + 719 + static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) 720 + { 721 + struct pci_cfg_sccb *sccb; 722 + int rc; 723 + 724 + if (!SCLP_HAS_PCI_RECONFIG) 725 + return -EOPNOTSUPP; 726 + 727 + sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 728 + if (!sccb) 729 + return -ENOMEM; 730 + 731 + sccb->header.length = PAGE_SIZE; 732 + sccb->atype = SCLP_RECONFIG_PCI_ATPYE; 733 + sccb->aid = fid; 734 + rc = do_sync_request(cmd, sccb); 735 + if (rc) 736 + goto out; 737 + switch (sccb->header.response_code) { 738 + case 0x0020: 739 + case 0x0120: 740 + break; 741 + default: 742 + pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", 743 + cmd, sccb->header.response_code); 744 + rc = -EIO; 745 + break; 746 + } 747 + out: 748 + free_page((unsigned long) sccb); 749 + return rc; 750 + } 751 + 752 + int sclp_pci_configure(u32 fid) 753 + { 754 + return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); 755 + } 756 + EXPORT_SYMBOL(sclp_pci_configure); 757 + 758 + int sclp_pci_deconfigure(u32 fid) 759 + { 760 + return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); 761 + } 762 + EXPORT_SYMBOL(sclp_pci_deconfigure); 703 763 704 764 /* 705 765 * Channel path configuration related functions.