1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * cpcihp_generic.c 4 * 5 * Generic port I/O CompactPCI driver 6 * 7 * Copyright 2002 SOMA Networks, Inc. 8 * Copyright 2001 Intel San Luis Obispo 9 * Copyright 2000,2001 MontaVista Software Inc. 10 * 11 * This generic CompactPCI hotplug driver should allow using the PCI hotplug 12 * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit 13 * in a system register that can be read through standard port I/O. 14 * 15 * Send feedback to <scottm@somanetworks.com> 16 */ 17 18 #include <linux/module.h> 19 #include <linux/init.h> 20 #include <linux/errno.h> 21 #include <linux/pci.h> 22 #include <linux/string.h> 23 #include "cpci_hotplug.h" 24 25 #define DRIVER_VERSION "0.1" 26 #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" 27 #define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver" 28 29 #if !defined(MODULE) 30 #define MY_NAME "cpcihp_generic" 31 #else 32 #define MY_NAME THIS_MODULE->name 33 #endif 34 35 #define dbg(format, arg...) \ 36 do { \ 37 if (debug) \ 38 printk(KERN_DEBUG "%s: " format "\n", \ 39 MY_NAME, ## arg); \ 40 } while (0) 41 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg) 42 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg) 43 #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg) 44 45 /* local variables */ 46 static bool debug; 47 static char *bridge; 48 static u8 bridge_busnr; 49 static u8 bridge_slot; 50 static struct pci_bus *bus; 51 static u8 first_slot; 52 static u8 last_slot; 53 static u16 port; 54 static unsigned int enum_bit; 55 static u8 enum_mask; 56 57 static struct cpci_hp_controller_ops generic_hpc_ops; 58 static struct cpci_hp_controller generic_hpc; 59 60 static int __init validate_parameters(void) 61 { 62 char *str; 63 char *p; 64 unsigned long tmp; 65 66 if (!bridge) { 67 info("not configured, disabling."); 68 return -EINVAL; 69 } 70 str = bridge; 71 if (!*str) 72 return -EINVAL; 73 74 tmp = simple_strtoul(str, &p, 16); 75 if (p == str || tmp > 0xff) { 76 err("Invalid hotplug bus bridge device bus number"); 77 return -EINVAL; 78 } 79 bridge_busnr = (u8) tmp; 80 dbg("bridge_busnr = 0x%02x", bridge_busnr); 81 if (*p != ':') { 82 err("Invalid hotplug bus bridge device"); 83 return -EINVAL; 84 } 85 str = p + 1; 86 tmp = simple_strtoul(str, &p, 16); 87 if (p == str || tmp > 0x1f) { 88 err("Invalid hotplug bus bridge device slot number"); 89 return -EINVAL; 90 } 91 bridge_slot = (u8) tmp; 92 dbg("bridge_slot = 0x%02x", bridge_slot); 93 94 dbg("first_slot = 0x%02x", first_slot); 95 dbg("last_slot = 0x%02x", last_slot); 96 if (!(first_slot && last_slot)) { 97 err("Need to specify first_slot and last_slot"); 98 return -EINVAL; 99 } 100 if (last_slot < first_slot) { 101 err("first_slot must be less than last_slot"); 102 return -EINVAL; 103 } 104 105 dbg("port = 0x%04x", port); 106 dbg("enum_bit = 0x%02x", enum_bit); 107 if (enum_bit > 7) { 108 err("Invalid #ENUM bit"); 109 return -EINVAL; 110 } 111 enum_mask = 1 << enum_bit; 112 return 0; 113 } 114 115 static int query_enum(void) 116 { 117 u8 value; 118 119 value = inb_p(port); 120 return ((value & enum_mask) == enum_mask); 121 } 122 123 static int __init cpcihp_generic_init(void) 124 { 125 int status; 126 struct resource *r; 127 struct pci_dev *dev; 128 129 info(DRIVER_DESC " version: " DRIVER_VERSION); 130 status = validate_parameters(); 131 if (status) 132 return status; 133 134 r = request_region(port, 1, "#ENUM hotswap signal register"); 135 if (!r) 136 return -EBUSY; 137 138 dev = pci_get_domain_bus_and_slot(0, bridge_busnr, 139 PCI_DEVFN(bridge_slot, 0)); 140 if (!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { 141 err("Invalid bridge device %s", bridge); 142 pci_dev_put(dev); 143 return -EINVAL; 144 } 145 bus = dev->subordinate; 146 pci_dev_put(dev); 147 148 memset(&generic_hpc, 0, sizeof(struct cpci_hp_controller)); 149 generic_hpc_ops.query_enum = query_enum; 150 generic_hpc.ops = &generic_hpc_ops; 151 152 status = cpci_hp_register_controller(&generic_hpc); 153 if (status != 0) { 154 err("Could not register cPCI hotplug controller"); 155 return -ENODEV; 156 } 157 dbg("registered controller"); 158 159 status = cpci_hp_register_bus(bus, first_slot, last_slot); 160 if (status != 0) { 161 err("Could not register cPCI hotplug bus"); 162 goto init_bus_register_error; 163 } 164 dbg("registered bus"); 165 166 status = cpci_hp_start(); 167 if (status != 0) { 168 err("Could not started cPCI hotplug system"); 169 goto init_start_error; 170 } 171 dbg("started cpci hp system"); 172 return 0; 173 init_start_error: 174 cpci_hp_unregister_bus(bus); 175 init_bus_register_error: 176 cpci_hp_unregister_controller(&generic_hpc); 177 err("status = %d", status); 178 return status; 179 180 } 181 182 static void __exit cpcihp_generic_exit(void) 183 { 184 cpci_hp_stop(); 185 cpci_hp_unregister_bus(bus); 186 cpci_hp_unregister_controller(&generic_hpc); 187 release_region(port, 1); 188 } 189 190 module_init(cpcihp_generic_init); 191 module_exit(cpcihp_generic_exit); 192 193 MODULE_AUTHOR(DRIVER_AUTHOR); 194 MODULE_DESCRIPTION(DRIVER_DESC); 195 MODULE_LICENSE("GPL"); 196 module_param(debug, bool, S_IRUGO | S_IWUSR); 197 MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 198 module_param(bridge, charp, 0); 199 MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)"); 200 module_param(first_slot, byte, 0); 201 MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number"); 202 module_param(last_slot, byte, 0); 203 MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number"); 204 module_param_hw(port, ushort, ioport, 0); 205 MODULE_PARM_DESC(port, "#ENUM signal I/O port"); 206 module_param(enum_bit, uint, 0); 207 MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)"); 208