cpcihp_zt5550.c (7467B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * cpcihp_zt5550.c 4 * 5 * Intel/Ziatech ZT5550 CompactPCI Host Controller driver 6 * 7 * Copyright 2002 SOMA Networks, Inc. 8 * Copyright 2001 Intel San Luis Obispo 9 * Copyright 2000,2001 MontaVista Software Inc. 10 * 11 * Send feedback to <scottm@somanetworks.com> 12 */ 13 14#include <linux/module.h> 15#include <linux/moduleparam.h> 16#include <linux/init.h> 17#include <linux/errno.h> 18#include <linux/pci.h> 19#include <linux/interrupt.h> 20#include <linux/signal.h> /* IRQF_SHARED */ 21#include "cpci_hotplug.h" 22#include "cpcihp_zt5550.h" 23 24#define DRIVER_VERSION "0.2" 25#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" 26#define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver" 27 28#define MY_NAME "cpcihp_zt5550" 29 30#define dbg(format, arg...) \ 31 do { \ 32 if (debug) \ 33 printk(KERN_DEBUG "%s: " format "\n", \ 34 MY_NAME, ## arg); \ 35 } while (0) 36#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg) 37#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg) 38#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg) 39 40/* local variables */ 41static bool debug; 42static bool poll; 43static struct cpci_hp_controller_ops zt5550_hpc_ops; 44static struct cpci_hp_controller zt5550_hpc; 45 46/* Primary cPCI bus bridge device */ 47static struct pci_dev *bus0_dev; 48static struct pci_bus *bus0; 49 50/* Host controller device */ 51static struct pci_dev *hc_dev; 52 53/* Host controller register addresses */ 54static void __iomem *hc_registers; 55static void __iomem *csr_hc_index; 56static void __iomem *csr_hc_data; 57static void __iomem *csr_int_status; 58static void __iomem *csr_int_mask; 59 60 61static int zt5550_hc_config(struct pci_dev *pdev) 62{ 63 int ret; 64 65 /* Since we know that no boards exist with two HC chips, treat it as an error */ 66 if (hc_dev) { 67 err("too many host controller devices?"); 68 return -EBUSY; 69 } 70 71 ret = pci_enable_device(pdev); 72 if (ret) { 73 err("cannot enable %s\n", pci_name(pdev)); 74 return ret; 75 } 76 77 hc_dev = pdev; 78 dbg("hc_dev = %p", hc_dev); 79 dbg("pci resource start %llx", (unsigned long long)pci_resource_start(hc_dev, 1)); 80 dbg("pci resource len %llx", (unsigned long long)pci_resource_len(hc_dev, 1)); 81 82 if (!request_mem_region(pci_resource_start(hc_dev, 1), 83 pci_resource_len(hc_dev, 1), MY_NAME)) { 84 err("cannot reserve MMIO region"); 85 ret = -ENOMEM; 86 goto exit_disable_device; 87 } 88 89 hc_registers = 90 ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1)); 91 if (!hc_registers) { 92 err("cannot remap MMIO region %llx @ %llx", 93 (unsigned long long)pci_resource_len(hc_dev, 1), 94 (unsigned long long)pci_resource_start(hc_dev, 1)); 95 ret = -ENODEV; 96 goto exit_release_region; 97 } 98 99 csr_hc_index = hc_registers + CSR_HCINDEX; 100 csr_hc_data = hc_registers + CSR_HCDATA; 101 csr_int_status = hc_registers + CSR_INTSTAT; 102 csr_int_mask = hc_registers + CSR_INTMASK; 103 104 /* 105 * Disable host control, fault and serial interrupts 106 */ 107 dbg("disabling host control, fault and serial interrupts"); 108 writeb((u8) HC_INT_MASK_REG, csr_hc_index); 109 writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data); 110 dbg("disabled host control, fault and serial interrupts"); 111 112 /* 113 * Disable timer0, timer1 and ENUM interrupts 114 */ 115 dbg("disabling timer0, timer1 and ENUM interrupts"); 116 writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask); 117 dbg("disabled timer0, timer1 and ENUM interrupts"); 118 return 0; 119 120exit_release_region: 121 release_mem_region(pci_resource_start(hc_dev, 1), 122 pci_resource_len(hc_dev, 1)); 123exit_disable_device: 124 pci_disable_device(hc_dev); 125 return ret; 126} 127 128static int zt5550_hc_cleanup(void) 129{ 130 if (!hc_dev) 131 return -ENODEV; 132 133 iounmap(hc_registers); 134 release_mem_region(pci_resource_start(hc_dev, 1), 135 pci_resource_len(hc_dev, 1)); 136 pci_disable_device(hc_dev); 137 return 0; 138} 139 140static int zt5550_hc_query_enum(void) 141{ 142 u8 value; 143 144 value = inb_p(ENUM_PORT); 145 return ((value & ENUM_MASK) == ENUM_MASK); 146} 147 148static int zt5550_hc_check_irq(void *dev_id) 149{ 150 int ret; 151 u8 reg; 152 153 ret = 0; 154 if (dev_id == zt5550_hpc.dev_id) { 155 reg = readb(csr_int_status); 156 if (reg) 157 ret = 1; 158 } 159 return ret; 160} 161 162static int zt5550_hc_enable_irq(void) 163{ 164 u8 reg; 165 166 if (hc_dev == NULL) 167 return -ENODEV; 168 169 reg = readb(csr_int_mask); 170 reg = reg & ~ENUM_INT_MASK; 171 writeb(reg, csr_int_mask); 172 return 0; 173} 174 175static int zt5550_hc_disable_irq(void) 176{ 177 u8 reg; 178 179 if (hc_dev == NULL) 180 return -ENODEV; 181 182 reg = readb(csr_int_mask); 183 reg = reg | ENUM_INT_MASK; 184 writeb(reg, csr_int_mask); 185 return 0; 186} 187 188static int zt5550_hc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 189{ 190 int status; 191 192 status = zt5550_hc_config(pdev); 193 if (status != 0) 194 return status; 195 196 dbg("returned from zt5550_hc_config"); 197 198 memset(&zt5550_hpc, 0, sizeof(struct cpci_hp_controller)); 199 zt5550_hpc_ops.query_enum = zt5550_hc_query_enum; 200 zt5550_hpc.ops = &zt5550_hpc_ops; 201 if (!poll) { 202 zt5550_hpc.irq = hc_dev->irq; 203 zt5550_hpc.irq_flags = IRQF_SHARED; 204 zt5550_hpc.dev_id = hc_dev; 205 206 zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq; 207 zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq; 208 zt5550_hpc_ops.check_irq = zt5550_hc_check_irq; 209 } else { 210 info("using ENUM# polling mode"); 211 } 212 213 status = cpci_hp_register_controller(&zt5550_hpc); 214 if (status != 0) { 215 err("could not register cPCI hotplug controller"); 216 goto init_hc_error; 217 } 218 dbg("registered controller"); 219 220 /* Look for first device matching cPCI bus's bridge vendor and device IDs */ 221 bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC, 222 PCI_DEVICE_ID_DEC_21154, NULL); 223 if (!bus0_dev) { 224 status = -ENODEV; 225 goto init_register_error; 226 } 227 bus0 = bus0_dev->subordinate; 228 pci_dev_put(bus0_dev); 229 230 status = cpci_hp_register_bus(bus0, 0x0a, 0x0f); 231 if (status != 0) { 232 err("could not register cPCI hotplug bus"); 233 goto init_register_error; 234 } 235 dbg("registered bus"); 236 237 status = cpci_hp_start(); 238 if (status != 0) { 239 err("could not started cPCI hotplug system"); 240 cpci_hp_unregister_bus(bus0); 241 goto init_register_error; 242 } 243 dbg("started cpci hp system"); 244 245 return 0; 246init_register_error: 247 cpci_hp_unregister_controller(&zt5550_hpc); 248init_hc_error: 249 err("status = %d", status); 250 zt5550_hc_cleanup(); 251 return status; 252 253} 254 255static void zt5550_hc_remove_one(struct pci_dev *pdev) 256{ 257 cpci_hp_stop(); 258 cpci_hp_unregister_bus(bus0); 259 cpci_hp_unregister_controller(&zt5550_hpc); 260 zt5550_hc_cleanup(); 261} 262 263 264static const struct pci_device_id zt5550_hc_pci_tbl[] = { 265 { PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, }, 266 { 0, } 267}; 268MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl); 269 270static struct pci_driver zt5550_hc_driver = { 271 .name = "zt5550_hc", 272 .id_table = zt5550_hc_pci_tbl, 273 .probe = zt5550_hc_init_one, 274 .remove = zt5550_hc_remove_one, 275}; 276 277static int __init zt5550_init(void) 278{ 279 struct resource *r; 280 int rc; 281 282 info(DRIVER_DESC " version: " DRIVER_VERSION); 283 r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register"); 284 if (!r) 285 return -EBUSY; 286 287 rc = pci_register_driver(&zt5550_hc_driver); 288 if (rc < 0) 289 release_region(ENUM_PORT, 1); 290 return rc; 291} 292 293static void __exit 294zt5550_exit(void) 295{ 296 pci_unregister_driver(&zt5550_hc_driver); 297 release_region(ENUM_PORT, 1); 298} 299 300module_init(zt5550_init); 301module_exit(zt5550_exit); 302 303MODULE_AUTHOR(DRIVER_AUTHOR); 304MODULE_DESCRIPTION(DRIVER_DESC); 305MODULE_LICENSE("GPL"); 306module_param(debug, bool, 0644); 307MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 308module_param(poll, bool, 0644); 309MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not");