18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * cpcihp_generic.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Generic port I/O CompactPCI driver 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2002 SOMA Networks, Inc. 88c2ecf20Sopenharmony_ci * Copyright 2001 Intel San Luis Obispo 98c2ecf20Sopenharmony_ci * Copyright 2000,2001 MontaVista Software Inc. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This generic CompactPCI hotplug driver should allow using the PCI hotplug 128c2ecf20Sopenharmony_ci * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit 138c2ecf20Sopenharmony_ci * in a system register that can be read through standard port I/O. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Send feedback to <scottm@somanetworks.com> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/string.h> 238c2ecf20Sopenharmony_ci#include "cpci_hotplug.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRIVER_VERSION "0.1" 268c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" 278c2ecf20Sopenharmony_ci#define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#if !defined(MODULE) 308c2ecf20Sopenharmony_ci#define MY_NAME "cpcihp_generic" 318c2ecf20Sopenharmony_ci#else 328c2ecf20Sopenharmony_ci#define MY_NAME THIS_MODULE->name 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define dbg(format, arg...) \ 368c2ecf20Sopenharmony_ci do { \ 378c2ecf20Sopenharmony_ci if (debug) \ 388c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: " format "\n", \ 398c2ecf20Sopenharmony_ci MY_NAME, ## arg); \ 408c2ecf20Sopenharmony_ci } while (0) 418c2ecf20Sopenharmony_ci#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg) 428c2ecf20Sopenharmony_ci#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg) 438c2ecf20Sopenharmony_ci#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* local variables */ 468c2ecf20Sopenharmony_cistatic bool debug; 478c2ecf20Sopenharmony_cistatic char *bridge; 488c2ecf20Sopenharmony_cistatic u8 bridge_busnr; 498c2ecf20Sopenharmony_cistatic u8 bridge_slot; 508c2ecf20Sopenharmony_cistatic struct pci_bus *bus; 518c2ecf20Sopenharmony_cistatic u8 first_slot; 528c2ecf20Sopenharmony_cistatic u8 last_slot; 538c2ecf20Sopenharmony_cistatic u16 port; 548c2ecf20Sopenharmony_cistatic unsigned int enum_bit; 558c2ecf20Sopenharmony_cistatic u8 enum_mask; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct cpci_hp_controller_ops generic_hpc_ops; 588c2ecf20Sopenharmony_cistatic struct cpci_hp_controller generic_hpc; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int __init validate_parameters(void) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci char *str; 638c2ecf20Sopenharmony_ci char *p; 648c2ecf20Sopenharmony_ci unsigned long tmp; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (!bridge) { 678c2ecf20Sopenharmony_ci info("not configured, disabling."); 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci str = bridge; 718c2ecf20Sopenharmony_ci if (!*str) 728c2ecf20Sopenharmony_ci return -EINVAL; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci tmp = simple_strtoul(str, &p, 16); 758c2ecf20Sopenharmony_ci if (p == str || tmp > 0xff) { 768c2ecf20Sopenharmony_ci err("Invalid hotplug bus bridge device bus number"); 778c2ecf20Sopenharmony_ci return -EINVAL; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci bridge_busnr = (u8) tmp; 808c2ecf20Sopenharmony_ci dbg("bridge_busnr = 0x%02x", bridge_busnr); 818c2ecf20Sopenharmony_ci if (*p != ':') { 828c2ecf20Sopenharmony_ci err("Invalid hotplug bus bridge device"); 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci str = p + 1; 868c2ecf20Sopenharmony_ci tmp = simple_strtoul(str, &p, 16); 878c2ecf20Sopenharmony_ci if (p == str || tmp > 0x1f) { 888c2ecf20Sopenharmony_ci err("Invalid hotplug bus bridge device slot number"); 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci bridge_slot = (u8) tmp; 928c2ecf20Sopenharmony_ci dbg("bridge_slot = 0x%02x", bridge_slot); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci dbg("first_slot = 0x%02x", first_slot); 958c2ecf20Sopenharmony_ci dbg("last_slot = 0x%02x", last_slot); 968c2ecf20Sopenharmony_ci if (!(first_slot && last_slot)) { 978c2ecf20Sopenharmony_ci err("Need to specify first_slot and last_slot"); 988c2ecf20Sopenharmony_ci return -EINVAL; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci if (last_slot < first_slot) { 1018c2ecf20Sopenharmony_ci err("first_slot must be less than last_slot"); 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci dbg("port = 0x%04x", port); 1068c2ecf20Sopenharmony_ci dbg("enum_bit = 0x%02x", enum_bit); 1078c2ecf20Sopenharmony_ci if (enum_bit > 7) { 1088c2ecf20Sopenharmony_ci err("Invalid #ENUM bit"); 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci enum_mask = 1 << enum_bit; 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int query_enum(void) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci u8 value; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci value = inb_p(port); 1208c2ecf20Sopenharmony_ci return ((value & enum_mask) == enum_mask); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int __init cpcihp_generic_init(void) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int status; 1268c2ecf20Sopenharmony_ci struct resource *r; 1278c2ecf20Sopenharmony_ci struct pci_dev *dev; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci info(DRIVER_DESC " version: " DRIVER_VERSION); 1308c2ecf20Sopenharmony_ci status = validate_parameters(); 1318c2ecf20Sopenharmony_ci if (status) 1328c2ecf20Sopenharmony_ci return status; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci r = request_region(port, 1, "#ENUM hotswap signal register"); 1358c2ecf20Sopenharmony_ci if (!r) 1368c2ecf20Sopenharmony_ci return -EBUSY; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci dev = pci_get_domain_bus_and_slot(0, bridge_busnr, 1398c2ecf20Sopenharmony_ci PCI_DEVFN(bridge_slot, 0)); 1408c2ecf20Sopenharmony_ci if (!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { 1418c2ecf20Sopenharmony_ci err("Invalid bridge device %s", bridge); 1428c2ecf20Sopenharmony_ci pci_dev_put(dev); 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci bus = dev->subordinate; 1468c2ecf20Sopenharmony_ci pci_dev_put(dev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci memset(&generic_hpc, 0, sizeof(struct cpci_hp_controller)); 1498c2ecf20Sopenharmony_ci generic_hpc_ops.query_enum = query_enum; 1508c2ecf20Sopenharmony_ci generic_hpc.ops = &generic_hpc_ops; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci status = cpci_hp_register_controller(&generic_hpc); 1538c2ecf20Sopenharmony_ci if (status != 0) { 1548c2ecf20Sopenharmony_ci err("Could not register cPCI hotplug controller"); 1558c2ecf20Sopenharmony_ci return -ENODEV; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci dbg("registered controller"); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci status = cpci_hp_register_bus(bus, first_slot, last_slot); 1608c2ecf20Sopenharmony_ci if (status != 0) { 1618c2ecf20Sopenharmony_ci err("Could not register cPCI hotplug bus"); 1628c2ecf20Sopenharmony_ci goto init_bus_register_error; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci dbg("registered bus"); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci status = cpci_hp_start(); 1678c2ecf20Sopenharmony_ci if (status != 0) { 1688c2ecf20Sopenharmony_ci err("Could not started cPCI hotplug system"); 1698c2ecf20Sopenharmony_ci goto init_start_error; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci dbg("started cpci hp system"); 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ciinit_start_error: 1748c2ecf20Sopenharmony_ci cpci_hp_unregister_bus(bus); 1758c2ecf20Sopenharmony_ciinit_bus_register_error: 1768c2ecf20Sopenharmony_ci cpci_hp_unregister_controller(&generic_hpc); 1778c2ecf20Sopenharmony_ci err("status = %d", status); 1788c2ecf20Sopenharmony_ci return status; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void __exit cpcihp_generic_exit(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci cpci_hp_stop(); 1858c2ecf20Sopenharmony_ci cpci_hp_unregister_bus(bus); 1868c2ecf20Sopenharmony_ci cpci_hp_unregister_controller(&generic_hpc); 1878c2ecf20Sopenharmony_ci release_region(port, 1); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cimodule_init(cpcihp_generic_init); 1918c2ecf20Sopenharmony_cimodule_exit(cpcihp_generic_exit); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 1948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 1958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1968c2ecf20Sopenharmony_cimodule_param(debug, bool, S_IRUGO | S_IWUSR); 1978c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 1988c2ecf20Sopenharmony_cimodule_param(bridge, charp, 0); 1998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)"); 2008c2ecf20Sopenharmony_cimodule_param(first_slot, byte, 0); 2018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(first_slot, "Hotswap bus first slot number"); 2028c2ecf20Sopenharmony_cimodule_param(last_slot, byte, 0); 2038c2ecf20Sopenharmony_ciMODULE_PARM_DESC(last_slot, "Hotswap bus last slot number"); 2048c2ecf20Sopenharmony_cimodule_param_hw(port, ushort, ioport, 0); 2058c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "#ENUM signal I/O port"); 2068c2ecf20Sopenharmony_cimodule_param(enum_bit, uint, 0); 2078c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)"); 208