208 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * cpcihp_generic.c4 *5 * Generic port I/O CompactPCI driver6 *7 * Copyright 2002 SOMA Networks, Inc.8 * Copyright 2001 Intel San Luis Obispo9 * Copyright 2000,2001 MontaVista Software Inc.10 *11 * This generic CompactPCI hotplug driver should allow using the PCI hotplug12 * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit13 * 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#else32#define MY_NAME THIS_MODULE->name33#endif34 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 */46static bool debug;47static char *bridge;48static u8 bridge_busnr;49static u8 bridge_slot;50static struct pci_bus *bus;51static u8 first_slot;52static u8 last_slot;53static u16 port;54static unsigned int enum_bit;55static u8 enum_mask;56 57static struct cpci_hp_controller_ops generic_hpc_ops;58static struct cpci_hp_controller generic_hpc;59 60static 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 115static int query_enum(void)116{117 u8 value;118 119 value = inb_p(port);120 return ((value & enum_mask) == enum_mask);121}122 123static 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;173init_start_error:174 cpci_hp_unregister_bus(bus);175init_bus_register_error:176 cpci_hp_unregister_controller(&generic_hpc);177 err("status = %d", status);178 return status;179 180}181 182static 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 190module_init(cpcihp_generic_init);191module_exit(cpcihp_generic_exit);192 193MODULE_AUTHOR(DRIVER_AUTHOR);194MODULE_DESCRIPTION(DRIVER_DESC);195MODULE_LICENSE("GPL");196module_param(debug, bool, S_IRUGO | S_IWUSR);197MODULE_PARM_DESC(debug, "Debugging mode enabled or not");198module_param(bridge, charp, 0);199MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)");200module_param(first_slot, byte, 0);201MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number");202module_param(last_slot, byte, 0);203MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number");204module_param_hw(port, ushort, ioport, 0);205MODULE_PARM_DESC(port, "#ENUM signal I/O port");206module_param(enum_bit, uint, 0);207MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)");208