| 1 | |
| 2 | |
| 3 | * Compaq Hot Plug Controller Driver
|
| 4 | *
|
| 5 | * Copyright (C) 1995,2001 Compaq Computer Corporation
|
| 6 | * Copyright (C) 2001 Greg Kroah-Hartman <[email protected]>
|
| 7 | * Copyright (C) 2001 IBM Corp.
|
| 8 | *
|
| 9 | * All rights reserved.
|
| 10 | *
|
| 11 | * Send feedback to <[email protected]>
|
| 12 | *
|
| 13 | * Jan 12, 2003 - Added 66/100/133MHz PCI-X support,
|
| 14 | * Torben Mathiasen <[email protected]>
|
| 15 |
|
| 16 |
|
| 17 | #include <linux/module.h>
|
| 18 | #include <linux/moduleparam.h>
|
| 19 | #include <linux/kernel.h>
|
| 20 | #include <linux/types.h>
|
| 21 | #include <linux/proc_fs.h>
|
| 22 | #include <linux/slab.h>
|
| 23 | #include <linux/workqueue.h>
|
| 24 | #include <linux/pci.h>
|
| 25 | #include <linux/pci_hotplug.h>
|
| 26 | #include <linux/init.h>
|
| 27 | #include <linux/interrupt.h>
|
| 28 |
|
| 29 | #include <linux/uaccess.h>
|
| 30 |
|
| 31 | #include "cpqphp.h"
|
| 32 | #include "cpqphp_nvram.h"
|
| 33 |
|
| 34 |
|
| 35 |
|
| 36 | int cpqhp_debug;
|
| 37 | int cpqhp_legacy_mode;
|
| 38 | struct controller *cpqhp_ctrl_list;
|
| 39 | struct pci_func *cpqhp_slot_list[256];
|
| 40 | struct irq_routing_table *cpqhp_routing_table;
|
| 41 |
|
| 42 |
|
| 43 | static void __iomem *smbios_table;
|
| 44 | static void __iomem *smbios_start;
|
| 45 | static void __iomem *cpqhp_rom_start;
|
| 46 | static bool power_mode;
|
| 47 | static bool debug;
|
| 48 | static int initialized;
|
| 49 |
|
| 50 | #define DRIVER_VERSION "0.9.8"
|
| 51 | #define DRIVER_AUTHOR "Dan Zink <[email protected]>, Greg Kroah-Hartman <[email protected]>"
|
| 52 | #define DRIVER_DESC "Compaq Hot Plug PCI Controller Driver"
|
| 53 |
|
| 54 | MODULE_AUTHOR(DRIVER_AUTHOR);
|
| 55 | MODULE_DESCRIPTION(DRIVER_DESC);
|
| 56 | MODULE_LICENSE("GPL");
|
| 57 |
|
| 58 | module_param(power_mode, bool, 0644);
|
| 59 | MODULE_PARM_DESC(power_mode, "Power mode enabled or not");
|
| 60 |
|
| 61 | module_param(debug, bool, 0644);
|
| 62 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
| 63 |
|
| 64 | #define CPQHPC_MODULE_MINOR 208
|
| 65 |
|
| 66 | static inline int is_slot64bit(struct slot *slot)
|
| 67 | {
|
| 68 | return (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0;
|
| 69 | }
|
| 70 |
|
| 71 | static inline int is_slot66mhz(struct slot *slot)
|
| 72 | {
|
| 73 | return (readb(slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E) ? 1 : 0;
|
| 74 | }
|
| 75 |
|
| 76 | |
| 77 | * detect_SMBIOS_pointer - find the System Management BIOS Table in mem region.
|
| 78 | * @begin: begin pointer for region to be scanned.
|
| 79 | * @end: end pointer for region to be scanned.
|
| 80 | *
|
| 81 | * Returns pointer to the head of the SMBIOS tables (or %NULL).
|
| 82 |
|
| 83 | static void __iomem *detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end)
|
| 84 | {
|
| 85 | void __iomem *fp;
|
| 86 | void __iomem *endp;
|
| 87 | u8 temp1, temp2, temp3, temp4;
|
| 88 | int status = 0;
|
| 89 |
|
| 90 | endp = (end - sizeof(u32) + 1);
|
| 91 |
|
| 92 | for (fp = begin; fp <= endp; fp += 16) {
|
| 93 | temp1 = readb(fp);
|
| 94 | temp2 = readb(fp+1);
|
| 95 | temp3 = readb(fp+2);
|
| 96 | temp4 = readb(fp+3);
|
| 97 | if (temp1 == '_' &&
|
| 98 | temp2 == 'S' &&
|
| 99 | temp3 == 'M' &&
|
| 100 | temp4 == '_') {
|
| 101 | status = 1;
|
| 102 | break;
|
| 103 | }
|
| 104 | }
|
| 105 |
|
| 106 | if (!status)
|
| 107 | fp = NULL;
|
| 108 |
|
| 109 | dbg("Discovered SMBIOS Entry point at %p\n", fp);
|
| 110 |
|
| 111 | return fp;
|
| 112 | }
|
| 113 |
|
| 114 | |
| 115 | * init_SERR - Initializes the per slot SERR generation.
|
| 116 | * @ctrl: controller to use
|
| 117 | *
|
| 118 | * For unexpected switch opens
|
| 119 |
|
| 120 | static int init_SERR(struct controller *ctrl)
|
| 121 | {
|
| 122 | u32 tempdword;
|
| 123 | u32 number_of_slots;
|
| 124 |
|
| 125 | if (!ctrl)
|
| 126 | return 1;
|
| 127 |
|
| 128 | tempdword = ctrl->first_slot;
|
| 129 |
|
| 130 | number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
|
| 131 |
|
| 132 | while (number_of_slots) {
|
| 133 | writeb(0, ctrl->hpc_reg + SLOT_SERR);
|
| 134 | tempdword++;
|
| 135 | number_of_slots--;
|
| 136 | }
|
| 137 |
|
| 138 | return 0;
|
| 139 | }
|
| 140 |
|
| 141 | static int init_cpqhp_routing_table(void)
|
| 142 | {
|
| 143 | int len;
|
| 144 |
|
| 145 | cpqhp_routing_table = pcibios_get_irq_routing_table();
|
| 146 | if (cpqhp_routing_table == NULL)
|
| 147 | return -ENOMEM;
|
| 148 |
|
| 149 | len = cpqhp_routing_table_length();
|
| 150 | if (len == 0) {
|
| 151 | kfree(cpqhp_routing_table);
|
| 152 | cpqhp_routing_table = NULL;
|
| 153 | return -1;
|
| 154 | }
|
| 155 |
|
| 156 | return 0;
|
| 157 | }
|
| 158 |
|
| 159 |
|
| 160 | static void pci_print_IRQ_route(void)
|
| 161 | {
|
| 162 | int len;
|
| 163 | int loop;
|
| 164 | u8 tbus, tdevice, tslot;
|
| 165 |
|
| 166 | len = cpqhp_routing_table_length();
|
| 167 |
|
| 168 | dbg("bus dev func slot\n");
|
| 169 | for (loop = 0; loop < len; ++loop) {
|
| 170 | tbus = cpqhp_routing_table->slots[loop].bus;
|
| 171 | tdevice = cpqhp_routing_table->slots[loop].devfn;
|
| 172 | tslot = cpqhp_routing_table->slots[loop].slot;
|
| 173 | dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot);
|
| 174 |
|
| 175 | }
|
| 176 | }
|
| 177 |
|
| 178 |
|
| 179 | |
| 180 | * get_subsequent_smbios_entry: get the next entry from bios table.
|
| 181 | * @smbios_start: where to start in the SMBIOS table
|
| 182 | * @smbios_table: location of the SMBIOS table
|
| 183 | * @curr: %NULL or pointer to previously returned structure
|
| 184 | *
|
| 185 | * Gets the first entry if previous == NULL;
|
| 186 | * otherwise, returns the next entry.
|
| 187 | * Uses global SMBIOS Table pointer.
|
| 188 | *
|
| 189 | * Returns a pointer to an SMBIOS structure or NULL if none found.
|
| 190 |
|
| 191 | static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start,
|
| 192 | void __iomem *smbios_table,
|
| 193 | void __iomem *curr)
|
| 194 | {
|
| 195 | u8 bail = 0;
|
| 196 | u8 previous_byte = 1;
|
| 197 | void __iomem *p_temp;
|
| 198 | void __iomem *p_max;
|
| 199 |
|
| 200 | if (!smbios_table || !curr)
|
| 201 | return NULL;
|
| 202 |
|
| 203 |
|
| 204 | p_max = smbios_start + readw(smbios_table + ST_LENGTH);
|
| 205 |
|
| 206 | p_temp = curr;
|
| 207 | p_temp += readb(curr + SMBIOS_GENERIC_LENGTH);
|
| 208 |
|
| 209 | while ((p_temp < p_max) && !bail) {
|
| 210 | |
| 211 | * The first condition is the previous byte
|
| 212 | * and the second is the curr
|
| 213 |
|
| 214 | if (!previous_byte && !(readb(p_temp)))
|
| 215 | bail = 1;
|
| 216 |
|
| 217 | previous_byte = readb(p_temp);
|
| 218 | p_temp++;
|
| 219 | }
|
| 220 |
|
| 221 | if (p_temp < p_max)
|
| 222 | return p_temp;
|
| 223 | else
|
| 224 | return NULL;
|
| 225 | }
|
| 226 |
|
| 227 |
|
| 228 | |
| 229 | * get_SMBIOS_entry - return the requested SMBIOS entry or %NULL
|
| 230 | * @smbios_start: where to start in the SMBIOS table
|
| 231 | * @smbios_table: location of the SMBIOS table
|
| 232 | * @type: SMBIOS structure type to be returned
|
| 233 | * @previous: %NULL or pointer to previously returned structure
|
| 234 | *
|
| 235 | * Gets the first entry of the specified type if previous == %NULL;
|
| 236 | * Otherwise, returns the next entry of the given type.
|
| 237 | * Uses global SMBIOS Table pointer.
|
| 238 | * Uses get_subsequent_smbios_entry.
|
| 239 | *
|
| 240 | * Returns a pointer to an SMBIOS structure or %NULL if none found.
|
| 241 |
|
| 242 | static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start,
|
| 243 | void __iomem *smbios_table,
|
| 244 | u8 type,
|
| 245 | void __iomem *previous)
|
| 246 | {
|
| 247 | if (!smbios_table)
|
| 248 | return NULL;
|
| 249 |
|
| 250 | if (!previous)
|
| 251 | previous = smbios_start;
|
| 252 | else
|
| 253 | previous = get_subsequent_smbios_entry(smbios_start,
|
| 254 | smbios_table, previous);
|
| 255 |
|
| 256 | while (previous)
|
| 257 | if (readb(previous + SMBIOS_GENERIC_TYPE) != type)
|
| 258 | previous = get_subsequent_smbios_entry(smbios_start,
|
| 259 | smbios_table, previous);
|
| 260 | else
|
| 261 | break;
|
| 262 |
|
| 263 | return previous;
|
| 264 | }
|
| 265 |
|
| 266 | static int ctrl_slot_cleanup(struct controller *ctrl)
|
| 267 | {
|
| 268 | struct slot *old_slot, *next_slot;
|
| 269 |
|
| 270 | old_slot = ctrl->slot;
|
| 271 | ctrl->slot = NULL;
|
| 272 |
|
| 273 | while (old_slot) {
|
| 274 | next_slot = old_slot->next;
|
| 275 | pci_hp_deregister(&old_slot->hotplug_slot);
|
| 276 | kfree(old_slot);
|
| 277 | old_slot = next_slot;
|
| 278 | }
|
| 279 |
|
| 280 | cpqhp_remove_debugfs_files(ctrl);
|
| 281 |
|
| 282 |
|
| 283 | free_irq(ctrl->interrupt, ctrl);
|
| 284 |
|
| 285 | iounmap(ctrl->hpc_reg);
|
| 286 |
|
| 287 | release_mem_region(pci_resource_start(ctrl->pci_dev, 0),
|
| 288 | pci_resource_len(ctrl->pci_dev, 0));
|
| 289 |
|
| 290 | return 0;
|
| 291 | }
|
| 292 |
|
| 293 |
|
| 294 | |
| 295 | * get_slot_mapping - determine logical slot mapping for PCI device
|
| 296 | *
|
| 297 | * Won't work for more than one PCI-PCI bridge in a slot.
|
| 298 | *
|
| 299 | * @bus: pointer to the PCI bus structure
|
| 300 | * @bus_num: bus number of PCI device
|
| 301 | * @dev_num: device number of PCI device
|
| 302 | * @slot: Pointer to u8 where slot number will be returned
|
| 303 | *
|
| 304 | * Output: SUCCESS or FAILURE
|
| 305 |
|
| 306 | static int
|
| 307 | get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot)
|
| 308 | {
|
| 309 | u32 work;
|
| 310 | long len;
|
| 311 | long loop;
|
| 312 |
|
| 313 | u8 tbus, tdevice, tslot, bridgeSlot;
|
| 314 |
|
| 315 | dbg("%s: %p, %d, %d, %p\n", __func__, bus, bus_num, dev_num, slot);
|
| 316 |
|
| 317 | bridgeSlot = 0xFF;
|
| 318 |
|
| 319 | len = cpqhp_routing_table_length();
|
| 320 | for (loop = 0; loop < len; ++loop) {
|
| 321 | tbus = cpqhp_routing_table->slots[loop].bus;
|
| 322 | tdevice = cpqhp_routing_table->slots[loop].devfn >> 3;
|
| 323 | tslot = cpqhp_routing_table->slots[loop].slot;
|
| 324 |
|
| 325 | if ((tbus == bus_num) && (tdevice == dev_num)) {
|
| 326 | *slot = tslot;
|
| 327 | return 0;
|
| 328 | } else {
|
| 329 | |
| 330 | * if the current IRQ table entry is a PCI-to-PCI
|
| 331 | * bridge device. If so, and its secondary bus
|
| 332 | * matches the bus number for the target device, I need
|
| 333 | * to save the bridge's slot number. If I can not find
|
| 334 | * an entry for the target device, I will have to
|
| 335 | * assume it's on the other side of the bridge, and
|
| 336 | * assign it the bridge's slot.
|
| 337 |
|
| 338 | bus->number = tbus;
|
| 339 | pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0),
|
| 340 | PCI_CLASS_REVISION, &work);
|
| 341 |
|
| 342 | if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
|
| 343 | pci_bus_read_config_dword(bus,
|
| 344 | PCI_DEVFN(tdevice, 0),
|
| 345 | PCI_PRIMARY_BUS, &work);
|
| 346 | |
| 347 | if (((work >> 8) & 0x000000FF) == (long) bus_num)
|
| 348 | bridgeSlot = tslot;
|
| 349 | }
|
| 350 | }
|
| 351 |
|
| 352 | }
|
| 353 |
|
| 354 | |
| 355 | * the target PCI device. If we did determine that the target device
|
| 356 | * is on the other side of a PCI-to-PCI bridge, return the slot number
|
| 357 | * for the bridge.
|
| 358 |
|
| 359 | if (bridgeSlot != 0xFF) {
|
| 360 | *slot = bridgeSlot;
|
| 361 | return 0;
|
| 362 | }
|
| 363 |
|
| 364 | return -1;
|
| 365 | }
|
| 366 |
|
| 367 |
|
| 368 | |
| 369 | * cpqhp_set_attention_status - Turns the Amber LED for a slot on or off
|
| 370 | * @ctrl: struct controller to use
|
| 371 | * @func: PCI device/function info
|
| 372 | * @status: LED control flag: 1 = LED on, 0 = LED off
|
| 373 |
|
| 374 | static int
|
| 375 | cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
|
| 376 | u32 status)
|
| 377 | {
|
| 378 | u8 hp_slot;
|
| 379 |
|
| 380 | if (func == NULL)
|
| 381 | return 1;
|
| 382 |
|
| 383 | hp_slot = func->device - ctrl->slot_device_offset;
|
| 384 |
|
| 385 |
|
| 386 | mutex_lock(&ctrl->crit_sect);
|
| 387 |
|
| 388 | if (status == 1)
|
| 389 | amber_LED_on(ctrl, hp_slot);
|
| 390 | else if (status == 0)
|
| 391 | amber_LED_off(ctrl, hp_slot);
|
| 392 | else {
|
| 393 |
|
| 394 | mutex_unlock(&ctrl->crit_sect);
|
| 395 | return 1;
|
| 396 | }
|
| 397 |
|
| 398 | set_SOGO(ctrl);
|
| 399 |
|
| 400 |
|
| 401 | wait_for_ctrl_irq(ctrl);
|
| 402 |
|
| 403 |
|
| 404 | mutex_unlock(&ctrl->crit_sect);
|
| 405 |
|
| 406 | return 0;
|
| 407 | }
|
| 408 |
|
| 409 |
|
| 410 | |
| 411 | * set_attention_status - Turns the Amber LED for a slot on or off
|
| 412 | * @hotplug_slot: slot to change LED on
|
| 413 | * @status: LED control flag
|
| 414 |
|
| 415 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
| 416 | {
|
| 417 | struct pci_func *slot_func;
|
| 418 | struct slot *slot = to_slot(hotplug_slot);
|
| 419 | struct controller *ctrl = slot->ctrl;
|
| 420 | u8 bus;
|
| 421 | u8 devfn;
|
| 422 | u8 device;
|
| 423 | u8 function;
|
| 424 |
|
| 425 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 426 |
|
| 427 | if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
|
| 428 | return -ENODEV;
|
| 429 |
|
| 430 | device = devfn >> 3;
|
| 431 | function = devfn & 0x7;
|
| 432 | dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
|
| 433 |
|
| 434 | slot_func = cpqhp_slot_find(bus, device, function);
|
| 435 | if (!slot_func)
|
| 436 | return -ENODEV;
|
| 437 |
|
| 438 | return cpqhp_set_attention_status(ctrl, slot_func, status);
|
| 439 | }
|
| 440 |
|
| 441 |
|
| 442 | static int process_SI(struct hotplug_slot *hotplug_slot)
|
| 443 | {
|
| 444 | struct pci_func *slot_func;
|
| 445 | struct slot *slot = to_slot(hotplug_slot);
|
| 446 | struct controller *ctrl = slot->ctrl;
|
| 447 | u8 bus;
|
| 448 | u8 devfn;
|
| 449 | u8 device;
|
| 450 | u8 function;
|
| 451 |
|
| 452 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 453 |
|
| 454 | if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
|
| 455 | return -ENODEV;
|
| 456 |
|
| 457 | device = devfn >> 3;
|
| 458 | function = devfn & 0x7;
|
| 459 | dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
|
| 460 |
|
| 461 | slot_func = cpqhp_slot_find(bus, device, function);
|
| 462 | if (!slot_func)
|
| 463 | return -ENODEV;
|
| 464 |
|
| 465 | slot_func->bus = bus;
|
| 466 | slot_func->device = device;
|
| 467 | slot_func->function = function;
|
| 468 | slot_func->configured = 0;
|
| 469 | dbg("board_added(%p, %p)\n", slot_func, ctrl);
|
| 470 | return cpqhp_process_SI(ctrl, slot_func);
|
| 471 | }
|
| 472 |
|
| 473 |
|
| 474 | static int process_SS(struct hotplug_slot *hotplug_slot)
|
| 475 | {
|
| 476 | struct pci_func *slot_func;
|
| 477 | struct slot *slot = to_slot(hotplug_slot);
|
| 478 | struct controller *ctrl = slot->ctrl;
|
| 479 | u8 bus;
|
| 480 | u8 devfn;
|
| 481 | u8 device;
|
| 482 | u8 function;
|
| 483 |
|
| 484 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 485 |
|
| 486 | if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
|
| 487 | return -ENODEV;
|
| 488 |
|
| 489 | device = devfn >> 3;
|
| 490 | function = devfn & 0x7;
|
| 491 | dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
|
| 492 |
|
| 493 | slot_func = cpqhp_slot_find(bus, device, function);
|
| 494 | if (!slot_func)
|
| 495 | return -ENODEV;
|
| 496 |
|
| 497 | dbg("In %s, slot_func = %p, ctrl = %p\n", __func__, slot_func, ctrl);
|
| 498 | return cpqhp_process_SS(ctrl, slot_func);
|
| 499 | }
|
| 500 |
|
| 501 |
|
| 502 | static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
|
| 503 | {
|
| 504 | struct slot *slot = to_slot(hotplug_slot);
|
| 505 | struct controller *ctrl = slot->ctrl;
|
| 506 |
|
| 507 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 508 |
|
| 509 | return cpqhp_hardware_test(ctrl, value);
|
| 510 | }
|
| 511 |
|
| 512 |
|
| 513 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
| 514 | {
|
| 515 | struct slot *slot = to_slot(hotplug_slot);
|
| 516 | struct controller *ctrl = slot->ctrl;
|
| 517 |
|
| 518 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 519 |
|
| 520 | *value = get_slot_enabled(ctrl, slot);
|
| 521 | return 0;
|
| 522 | }
|
| 523 |
|
| 524 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
| 525 | {
|
| 526 | struct slot *slot = to_slot(hotplug_slot);
|
| 527 | struct controller *ctrl = slot->ctrl;
|
| 528 |
|
| 529 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 530 |
|
| 531 | *value = cpq_get_attention_status(ctrl, slot);
|
| 532 | return 0;
|
| 533 | }
|
| 534 |
|
| 535 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
| 536 | {
|
| 537 | struct slot *slot = to_slot(hotplug_slot);
|
| 538 | struct controller *ctrl = slot->ctrl;
|
| 539 |
|
| 540 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 541 |
|
| 542 | *value = cpq_get_latch_status(ctrl, slot);
|
| 543 |
|
| 544 | return 0;
|
| 545 | }
|
| 546 |
|
| 547 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
| 548 | {
|
| 549 | struct slot *slot = to_slot(hotplug_slot);
|
| 550 | struct controller *ctrl = slot->ctrl;
|
| 551 |
|
| 552 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
|
| 553 |
|
| 554 | *value = get_presence_status(ctrl, slot);
|
| 555 |
|
| 556 | return 0;
|
| 557 | }
|
| 558 |
|
| 559 | static const struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
|
| 560 | .set_attention_status = set_attention_status,
|
| 561 | .enable_slot = process_SI,
|
| 562 | .disable_slot = process_SS,
|
| 563 | .hardware_test = hardware_test,
|
| 564 | .get_power_status = get_power_status,
|
| 565 | .get_attention_status = get_attention_status,
|
| 566 | .get_latch_status = get_latch_status,
|
| 567 | .get_adapter_status = get_adapter_status,
|
| 568 | };
|
| 569 |
|
| 570 | #define SLOT_NAME_SIZE 10
|
| 571 |
|
| 572 | static int ctrl_slot_setup(struct controller *ctrl,
|
| 573 | void __iomem *smbios_start,
|
| 574 | void __iomem *smbios_table)
|
| 575 | {
|
| 576 | struct slot *slot;
|
| 577 | struct pci_bus *bus = ctrl->pci_bus;
|
| 578 | u8 number_of_slots;
|
| 579 | u8 slot_device;
|
| 580 | u8 slot_number;
|
| 581 | u8 ctrl_slot;
|
| 582 | u32 tempdword;
|
| 583 | char name[SLOT_NAME_SIZE];
|
| 584 | void __iomem *slot_entry = NULL;
|
| 585 | int result;
|
| 586 |
|
| 587 | dbg("%s\n", __func__);
|
| 588 |
|
| 589 | tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
|
| 590 |
|
| 591 | number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
|
| 592 | slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
|
| 593 | slot_number = ctrl->first_slot;
|
| 594 |
|
| 595 | while (number_of_slots) {
|
| 596 | slot = kzalloc_obj(*slot);
|
| 597 | if (!slot) {
|
| 598 | result = -ENOMEM;
|
| 599 | goto error;
|
| 600 | }
|
| 601 |
|
| 602 | slot->ctrl = ctrl;
|
| 603 | slot->bus = ctrl->bus;
|
| 604 | slot->device = slot_device;
|
| 605 | slot->number = slot_number;
|
| 606 | dbg("slot->number = %u\n", slot->number);
|
| 607 |
|
| 608 | slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9,
|
| 609 | slot_entry);
|
| 610 |
|
| 611 | while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) !=
|
| 612 | slot->number)) {
|
| 613 | slot_entry = get_SMBIOS_entry(smbios_start,
|
| 614 | smbios_table, 9, slot_entry);
|
| 615 | }
|
| 616 |
|
| 617 | slot->p_sm_slot = slot_entry;
|
| 618 |
|
| 619 | timer_setup(&slot->task_event, cpqhp_pushbutton_thread, 0);
|
| 620 | slot->task_event.expires = jiffies + 5 * HZ;
|
| 621 |
|
| 622 | |
| 623 | * they need to be correctly implemented
|
| 624 |
|
| 625 | slot->capabilities |= PCISLOT_REPLACE_SUPPORTED;
|
| 626 | slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED;
|
| 627 |
|
| 628 | if (is_slot64bit(slot))
|
| 629 | slot->capabilities |= PCISLOT_64_BIT_SUPPORTED;
|
| 630 | if (is_slot66mhz(slot))
|
| 631 | slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED;
|
| 632 | if (bus->cur_bus_speed == PCI_SPEED_66MHz)
|
| 633 | slot->capabilities |= PCISLOT_66_MHZ_OPERATION;
|
| 634 |
|
| 635 | ctrl_slot =
|
| 636 | slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4);
|
| 637 |
|
| 638 |
|
| 639 | slot->capabilities |=
|
| 640 | ((((~tempdword) >> 23) |
|
| 641 | ((~tempdword) >> 15)) >> ctrl_slot) & 0x02;
|
| 642 |
|
| 643 | slot->capabilities |=
|
| 644 | ((~tempdword & 0xFF) >> ctrl_slot) & 0x01;
|
| 645 |
|
| 646 | slot->capabilities |=
|
| 647 | ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
|
| 648 |
|
| 649 |
|
| 650 | snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
|
| 651 | slot->hotplug_slot.ops = &cpqphp_hotplug_slot_ops;
|
| 652 |
|
| 653 | dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n",
|
| 654 | slot->bus, slot->device,
|
| 655 | slot->number, ctrl->slot_device_offset,
|
| 656 | slot_number);
|
| 657 | result = pci_hp_register(&slot->hotplug_slot,
|
| 658 | ctrl->pci_dev->bus,
|
| 659 | slot->device,
|
| 660 | name);
|
| 661 | if (result) {
|
| 662 | err("pci_hp_register failed with error %d\n", result);
|
| 663 | goto error_slot;
|
| 664 | }
|
| 665 |
|
| 666 | slot->next = ctrl->slot;
|
| 667 | ctrl->slot = slot;
|
| 668 |
|
| 669 | number_of_slots--;
|
| 670 | slot_device++;
|
| 671 | slot_number++;
|
| 672 | }
|
| 673 |
|
| 674 | return 0;
|
| 675 | error_slot:
|
| 676 | kfree(slot);
|
| 677 | error:
|
| 678 | return result;
|
| 679 | }
|
| 680 |
|
| 681 | static int one_time_init(void)
|
| 682 | {
|
| 683 | int loop;
|
| 684 | int retval = 0;
|
| 685 |
|
| 686 | if (initialized)
|
| 687 | return 0;
|
| 688 |
|
| 689 | power_mode = 0;
|
| 690 |
|
| 691 | retval = init_cpqhp_routing_table();
|
| 692 | if (retval)
|
| 693 | goto error;
|
| 694 |
|
| 695 | if (cpqhp_debug)
|
| 696 | pci_print_IRQ_route();
|
| 697 |
|
| 698 | dbg("Initialize + Start the notification mechanism\n");
|
| 699 |
|
| 700 | retval = cpqhp_event_start_thread();
|
| 701 | if (retval)
|
| 702 | goto error;
|
| 703 |
|
| 704 | dbg("Initialize slot lists\n");
|
| 705 | for (loop = 0; loop < 256; loop++)
|
| 706 | cpqhp_slot_list[loop] = NULL;
|
| 707 |
|
| 708 | |
| 709 | * this also needs to be worked with Christoph
|
| 710 | * register_NMI_handler();
|
| 711 |
|
| 712 |
|
| 713 | cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
|
| 714 | if (!cpqhp_rom_start) {
|
| 715 | err("Could not ioremap memory region for ROM\n");
|
| 716 | retval = -EIO;
|
| 717 | goto error;
|
| 718 | }
|
| 719 |
|
| 720 | |
| 721 | * hardware
|
| 722 |
|
| 723 | compaq_nvram_init(cpqhp_rom_start);
|
| 724 |
|
| 725 |
|
| 726 | smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start,
|
| 727 | cpqhp_rom_start + ROM_PHY_LEN);
|
| 728 | if (!smbios_table) {
|
| 729 | err("Could not find the SMBIOS pointer in memory\n");
|
| 730 | retval = -EIO;
|
| 731 | goto error_rom_start;
|
| 732 | }
|
| 733 |
|
| 734 | smbios_start = ioremap(readl(smbios_table + ST_ADDRESS),
|
| 735 | readw(smbios_table + ST_LENGTH));
|
| 736 | if (!smbios_start) {
|
| 737 | err("Could not ioremap memory region taken from SMBIOS values\n");
|
| 738 | retval = -EIO;
|
| 739 | goto error_smbios_start;
|
| 740 | }
|
| 741 |
|
| 742 | initialized = 1;
|
| 743 |
|
| 744 | return retval;
|
| 745 |
|
| 746 | error_smbios_start:
|
| 747 | iounmap(smbios_start);
|
| 748 | error_rom_start:
|
| 749 | iounmap(cpqhp_rom_start);
|
| 750 | error:
|
| 751 | return retval;
|
| 752 | }
|
| 753 |
|
| 754 | static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
| 755 | {
|
| 756 | u8 num_of_slots = 0;
|
| 757 | u8 hp_slot = 0;
|
| 758 | u8 device;
|
| 759 | u8 bus_cap;
|
| 760 | u16 temp_word;
|
| 761 | u16 vendor_id;
|
| 762 | u16 subsystem_vid;
|
| 763 | u16 subsystem_deviceid;
|
| 764 | u32 rc;
|
| 765 | struct controller *ctrl;
|
| 766 | struct pci_func *func;
|
| 767 | struct pci_bus *bus;
|
| 768 | int err;
|
| 769 |
|
| 770 | err = pci_enable_device(pdev);
|
| 771 | if (err) {
|
| 772 | printk(KERN_ERR MY_NAME ": cannot enable PCI device %s (%d)\n",
|
| 773 | pci_name(pdev), err);
|
| 774 | return err;
|
| 775 | }
|
| 776 |
|
| 777 | bus = pdev->subordinate;
|
| 778 | if (!bus) {
|
| 779 | pci_notice(pdev, "the device is not a bridge, skipping\n");
|
| 780 | rc = -ENODEV;
|
| 781 | goto err_disable_device;
|
| 782 | }
|
| 783 |
|
| 784 | |
| 785 | * discovery
|
| 786 |
|
| 787 | vendor_id = pdev->vendor;
|
| 788 | if ((vendor_id != PCI_VENDOR_ID_COMPAQ) &&
|
| 789 | (vendor_id != PCI_VENDOR_ID_INTEL)) {
|
| 790 | err(msg_HPC_non_compaq_or_intel);
|
| 791 | rc = -ENODEV;
|
| 792 | goto err_disable_device;
|
| 793 | }
|
| 794 | dbg("Vendor ID: %x\n", vendor_id);
|
| 795 |
|
| 796 | dbg("revision: %d\n", pdev->revision);
|
| 797 | if ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!pdev->revision)) {
|
| 798 | err(msg_HPC_rev_error);
|
| 799 | rc = -ENODEV;
|
| 800 | goto err_disable_device;
|
| 801 | }
|
| 802 |
|
| 803 | |
| 804 | * Intel uses a different SSID programming model than Compaq.
|
| 805 | * For Intel, each SSID bit identifies a PHP capability.
|
| 806 | * Also Intel HPCs may have RID=0.
|
| 807 |
|
| 808 | if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) {
|
| 809 | err(msg_HPC_not_supported);
|
| 810 | rc = -ENODEV;
|
| 811 | goto err_disable_device;
|
| 812 | }
|
| 813 |
|
| 814 | |
| 815 | * subsystem IDs
|
| 816 |
|
| 817 | subsystem_vid = pdev->subsystem_vendor;
|
| 818 | dbg("Subsystem Vendor ID: %x\n", subsystem_vid);
|
| 819 | if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) {
|
| 820 | err(msg_HPC_non_compaq_or_intel);
|
| 821 | rc = -ENODEV;
|
| 822 | goto err_disable_device;
|
| 823 | }
|
| 824 |
|
| 825 | ctrl = kzalloc_obj(struct controller);
|
| 826 | if (!ctrl) {
|
| 827 | rc = -ENOMEM;
|
| 828 | goto err_disable_device;
|
| 829 | }
|
| 830 |
|
| 831 | subsystem_deviceid = pdev->subsystem_device;
|
| 832 |
|
| 833 | info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid);
|
| 834 |
|
| 835 | |
| 836 | * functions
|
| 837 |
|
| 838 | ctrl->vendor_id = vendor_id;
|
| 839 |
|
| 840 | switch (subsystem_vid) {
|
| 841 | case PCI_VENDOR_ID_COMPAQ:
|
| 842 | if (pdev->revision >= 0x13) {
|
| 843 | ctrl->push_flag = 1;
|
| 844 | ctrl->slot_switch_type = 1;
|
| 845 | ctrl->push_button = 1;
|
| 846 | ctrl->pci_config_space = 1;
|
| 847 | ctrl->defeature_PHP = 1;
|
| 848 | ctrl->pcix_support = 1;
|
| 849 | ctrl->pcix_speed_capability = 1;
|
| 850 | pci_read_config_byte(pdev, 0x41, &bus_cap);
|
| 851 | if (bus_cap & 0x80) {
|
| 852 | dbg("bus max supports 133MHz PCI-X\n");
|
| 853 | bus->max_bus_speed = PCI_SPEED_133MHz_PCIX;
|
| 854 | break;
|
| 855 | }
|
| 856 | if (bus_cap & 0x40) {
|
| 857 | dbg("bus max supports 100MHz PCI-X\n");
|
| 858 | bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
|
| 859 | break;
|
| 860 | }
|
| 861 | if (bus_cap & 0x20) {
|
| 862 | dbg("bus max supports 66MHz PCI-X\n");
|
| 863 | bus->max_bus_speed = PCI_SPEED_66MHz_PCIX;
|
| 864 | break;
|
| 865 | }
|
| 866 | if (bus_cap & 0x10) {
|
| 867 | dbg("bus max supports 66MHz PCI\n");
|
| 868 | bus->max_bus_speed = PCI_SPEED_66MHz;
|
| 869 | break;
|
| 870 | }
|
| 871 |
|
| 872 | break;
|
| 873 | }
|
| 874 |
|
| 875 | switch (subsystem_deviceid) {
|
| 876 | case PCI_SUB_HPC_ID:
|
| 877 |
|
| 878 | ctrl->slot_switch_type = 1;
|
| 879 | bus->max_bus_speed = PCI_SPEED_33MHz;
|
| 880 | ctrl->push_button = 0;
|
| 881 | ctrl->pci_config_space = 1;
|
| 882 | ctrl->defeature_PHP = 1;
|
| 883 | ctrl->pcix_support = 0;
|
| 884 | ctrl->pcix_speed_capability = 0;
|
| 885 | break;
|
| 886 | case PCI_SUB_HPC_ID2:
|
| 887 |
|
| 888 | ctrl->push_flag = 1;
|
| 889 | ctrl->slot_switch_type = 1;
|
| 890 | bus->max_bus_speed = PCI_SPEED_33MHz;
|
| 891 | ctrl->push_button = 1;
|
| 892 | ctrl->pci_config_space = 1;
|
| 893 | ctrl->defeature_PHP = 1;
|
| 894 | ctrl->pcix_support = 0;
|
| 895 | ctrl->pcix_speed_capability = 0;
|
| 896 | break;
|
| 897 | case PCI_SUB_HPC_ID_INTC:
|
| 898 |
|
| 899 | ctrl->slot_switch_type = 1;
|
| 900 | bus->max_bus_speed = PCI_SPEED_33MHz;
|
| 901 | ctrl->push_button = 0;
|
| 902 | ctrl->pci_config_space = 1;
|
| 903 | ctrl->defeature_PHP = 1;
|
| 904 | ctrl->pcix_support = 0;
|
| 905 | ctrl->pcix_speed_capability = 0;
|
| 906 | break;
|
| 907 | case PCI_SUB_HPC_ID3:
|
| 908 |
|
| 909 | ctrl->push_flag = 1;
|
| 910 | ctrl->slot_switch_type = 1;
|
| 911 | bus->max_bus_speed = PCI_SPEED_66MHz;
|
| 912 | ctrl->push_button = 1;
|
| 913 | ctrl->pci_config_space = 1;
|
| 914 | ctrl->defeature_PHP = 1;
|
| 915 | ctrl->pcix_support = 0;
|
| 916 | ctrl->pcix_speed_capability = 0;
|
| 917 | break;
|
| 918 | case PCI_SUB_HPC_ID4:
|
| 919 |
|
| 920 | ctrl->push_flag = 1;
|
| 921 | ctrl->slot_switch_type = 1;
|
| 922 | bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
|
| 923 | ctrl->push_button = 1;
|
| 924 | ctrl->pci_config_space = 1;
|
| 925 | ctrl->defeature_PHP = 1;
|
| 926 | ctrl->pcix_support = 1;
|
| 927 | ctrl->pcix_speed_capability = 0;
|
| 928 | break;
|
| 929 | default:
|
| 930 | err(msg_HPC_not_supported);
|
| 931 | rc = -ENODEV;
|
| 932 | goto err_free_ctrl;
|
| 933 | }
|
| 934 | break;
|
| 935 |
|
| 936 | case PCI_VENDOR_ID_INTEL:
|
| 937 |
|
| 938 | if (subsystem_deviceid & 0x0001)
|
| 939 | bus->max_bus_speed = PCI_SPEED_66MHz;
|
| 940 | else
|
| 941 | bus->max_bus_speed = PCI_SPEED_33MHz;
|
| 942 |
|
| 943 |
|
| 944 | if (subsystem_deviceid & 0x0002)
|
| 945 | ctrl->push_button = 0;
|
| 946 | else
|
| 947 | ctrl->push_button = 1;
|
| 948 |
|
| 949 |
|
| 950 | if (subsystem_deviceid & 0x0004)
|
| 951 | ctrl->slot_switch_type = 0;
|
| 952 | else
|
| 953 | ctrl->slot_switch_type = 1;
|
| 954 |
|
| 955 |
|
| 956 | if (subsystem_deviceid & 0x0008)
|
| 957 | ctrl->defeature_PHP = 1;
|
| 958 | else
|
| 959 | ctrl->defeature_PHP = 0;
|
| 960 |
|
| 961 | |
| 962 | * (0=not supported, 1=supported)
|
| 963 |
|
| 964 | if (subsystem_deviceid & 0x0010)
|
| 965 | ctrl->alternate_base_address = 1;
|
| 966 | else
|
| 967 | ctrl->alternate_base_address = 0;
|
| 968 |
|
| 969 |
|
| 970 | if (subsystem_deviceid & 0x0020)
|
| 971 | ctrl->pci_config_space = 1;
|
| 972 | else
|
| 973 | ctrl->pci_config_space = 0;
|
| 974 |
|
| 975 |
|
| 976 | if (subsystem_deviceid & 0x0080) {
|
| 977 | ctrl->pcix_support = 1;
|
| 978 | if (subsystem_deviceid & 0x0040)
|
| 979 |
|
| 980 | ctrl->pcix_speed_capability = 1;
|
| 981 | else
|
| 982 |
|
| 983 |
|
| 984 | ctrl->pcix_speed_capability = 0;
|
| 985 | } else {
|
| 986 |
|
| 987 | ctrl->pcix_support = 0;
|
| 988 | ctrl->pcix_speed_capability = 0;
|
| 989 | }
|
| 990 | break;
|
| 991 |
|
| 992 | default:
|
| 993 | err(msg_HPC_not_supported);
|
| 994 | rc = -ENODEV;
|
| 995 | goto err_free_ctrl;
|
| 996 | }
|
| 997 |
|
| 998 |
|
| 999 | info("Initializing the PCI hot plug controller residing on PCI bus %d\n",
|
| 1000 | pdev->bus->number);
|
| 1001 |
|
| 1002 | dbg("Hotplug controller capabilities:\n");
|
| 1003 | dbg(" speed_capability %d\n", bus->max_bus_speed);
|
| 1004 | dbg(" slot_switch_type %s\n", ctrl->slot_switch_type ?
|
| 1005 | "switch present" : "no switch");
|
| 1006 | dbg(" defeature_PHP %s\n", ctrl->defeature_PHP ?
|
| 1007 | "PHP supported" : "PHP not supported");
|
| 1008 | dbg(" alternate_base_address %s\n", ctrl->alternate_base_address ?
|
| 1009 | "supported" : "not supported");
|
| 1010 | dbg(" pci_config_space %s\n", ctrl->pci_config_space ?
|
| 1011 | "supported" : "not supported");
|
| 1012 | dbg(" pcix_speed_capability %s\n", ctrl->pcix_speed_capability ?
|
| 1013 | "supported" : "not supported");
|
| 1014 | dbg(" pcix_support %s\n", ctrl->pcix_support ?
|
| 1015 | "supported" : "not supported");
|
| 1016 |
|
| 1017 | ctrl->pci_dev = pdev;
|
| 1018 | pci_set_drvdata(pdev, ctrl);
|
| 1019 |
|
| 1020 | |
| 1021 | * as we like tweaking it a lot
|
| 1022 | ctrl->pci_bus = kmemdup(pdev->bus, sizeof(*ctrl->pci_bus), GFP_KERNEL);
|
| 1023 | if (!ctrl->pci_bus) {
|
| 1024 | err("out of memory\n");
|
| 1025 | rc = -ENOMEM;
|
| 1026 | goto err_free_ctrl;
|
| 1027 | }
|
| 1028 |
|
| 1029 | ctrl->bus = pdev->bus->number;
|
| 1030 | ctrl->rev = pdev->revision;
|
| 1031 | dbg("bus device function rev: %d %d %d %d\n", ctrl->bus,
|
| 1032 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev);
|
| 1033 |
|
| 1034 | mutex_init(&ctrl->crit_sect);
|
| 1035 | init_waitqueue_head(&ctrl->queue);
|
| 1036 |
|
| 1037 |
|
| 1038 | rc = one_time_init();
|
| 1039 | if (rc)
|
| 1040 | goto err_free_bus;
|
| 1041 |
|
| 1042 | dbg("pdev = %p\n", pdev);
|
| 1043 | dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0));
|
| 1044 | dbg("pci resource len %llx\n", (unsigned long long)pci_resource_len(pdev, 0));
|
| 1045 |
|
| 1046 | if (!request_mem_region(pci_resource_start(pdev, 0),
|
| 1047 | pci_resource_len(pdev, 0), MY_NAME)) {
|
| 1048 | err("cannot reserve MMIO region\n");
|
| 1049 | rc = -ENOMEM;
|
| 1050 | goto err_free_bus;
|
| 1051 | }
|
| 1052 |
|
| 1053 | ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0),
|
| 1054 | pci_resource_len(pdev, 0));
|
| 1055 | if (!ctrl->hpc_reg) {
|
| 1056 | err("cannot remap MMIO region %llx @ %llx\n",
|
| 1057 | (unsigned long long)pci_resource_len(pdev, 0),
|
| 1058 | (unsigned long long)pci_resource_start(pdev, 0));
|
| 1059 | rc = -ENODEV;
|
| 1060 | goto err_free_mem_region;
|
| 1061 | }
|
| 1062 |
|
| 1063 |
|
| 1064 | bus->cur_bus_speed = get_controller_speed(ctrl);
|
| 1065 |
|
| 1066 |
|
| 1067 | |
| 1068 | *
|
| 1069 | * Save configuration headers for this and
|
| 1070 | * subordinate PCI buses
|
| 1071 | *
|
| 1072 | *******************************************************
|
| 1073 |
|
| 1074 |
|
| 1075 |
|
| 1076 | |
| 1077 | * in this case it will always be called for the "base"
|
| 1078 | * bus/dev/func of a slot.
|
| 1079 | * CS: this is leveraging the PCIIRQ routing code from the kernel
|
| 1080 | * (pci-pc.c: get_irq_routing_table)
|
| 1081 | rc = get_slot_mapping(ctrl->pci_bus, pdev->bus->number,
|
| 1082 | (readb(ctrl->hpc_reg + SLOT_MASK) >> 4),
|
| 1083 | &(ctrl->first_slot));
|
| 1084 | dbg("get_slot_mapping: first_slot = %d, returned = %d\n",
|
| 1085 | ctrl->first_slot, rc);
|
| 1086 | if (rc) {
|
| 1087 | err(msg_initialization_err, rc);
|
| 1088 | goto err_iounmap;
|
| 1089 | }
|
| 1090 |
|
| 1091 |
|
| 1092 | rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK));
|
| 1093 | if (rc) {
|
| 1094 | err("%s: unable to save PCI configuration data, error %d\n",
|
| 1095 | __func__, rc);
|
| 1096 | goto err_iounmap;
|
| 1097 | }
|
| 1098 |
|
| 1099 | |
| 1100 | * Get IO, memory, and IRQ resources for new devices
|
| 1101 |
|
| 1102 |
|
| 1103 | ctrl->interrupt = pdev->irq;
|
| 1104 | if (ctrl->interrupt < 0x10) {
|
| 1105 | cpqhp_legacy_mode = 1;
|
| 1106 | dbg("System seems to be configured for Full Table Mapped MPS mode\n");
|
| 1107 | }
|
| 1108 |
|
| 1109 | ctrl->cfgspc_irq = 0;
|
| 1110 | pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ctrl->cfgspc_irq);
|
| 1111 |
|
| 1112 | rc = cpqhp_find_available_resources(ctrl, cpqhp_rom_start);
|
| 1113 | ctrl->add_support = !rc;
|
| 1114 | if (rc) {
|
| 1115 | dbg("cpqhp_find_available_resources = 0x%x\n", rc);
|
| 1116 | err("unable to locate PCI configuration resources for hot plug add.\n");
|
| 1117 | goto err_iounmap;
|
| 1118 | }
|
| 1119 |
|
| 1120 | |
| 1121 | * Finish setting up the hot plug ctrl device
|
| 1122 |
|
| 1123 | ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
|
| 1124 | dbg("NumSlots %d\n", ctrl->slot_device_offset);
|
| 1125 |
|
| 1126 | ctrl->next_event = 0;
|
| 1127 |
|
| 1128 |
|
| 1129 | rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table);
|
| 1130 | if (rc) {
|
| 1131 | err(msg_initialization_err, 6);
|
| 1132 | err("%s: unable to save PCI configuration data, error %d\n",
|
| 1133 | __func__, rc);
|
| 1134 | goto err_iounmap;
|
| 1135 | }
|
| 1136 |
|
| 1137 |
|
| 1138 | writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK);
|
| 1139 |
|
| 1140 |
|
| 1141 | dbg("HPC interrupt = %d\n", ctrl->interrupt);
|
| 1142 | if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr,
|
| 1143 | IRQF_SHARED, MY_NAME, ctrl)) {
|
| 1144 | err("Can't get irq %d for the hotplug pci controller\n",
|
| 1145 | ctrl->interrupt);
|
| 1146 | rc = -ENODEV;
|
| 1147 | goto err_iounmap;
|
| 1148 | }
|
| 1149 |
|
| 1150 | |
| 1151 | * fault
|
| 1152 |
|
| 1153 | temp_word = readw(ctrl->hpc_reg + MISC);
|
| 1154 | temp_word |= 0x4006;
|
| 1155 | writew(temp_word, ctrl->hpc_reg + MISC);
|
| 1156 |
|
| 1157 |
|
| 1158 | writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR);
|
| 1159 |
|
| 1160 | ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
|
| 1161 |
|
| 1162 | writel(0x0L, ctrl->hpc_reg + INT_MASK);
|
| 1163 |
|
| 1164 | if (!cpqhp_ctrl_list) {
|
| 1165 | cpqhp_ctrl_list = ctrl;
|
| 1166 | ctrl->next = NULL;
|
| 1167 | } else {
|
| 1168 | ctrl->next = cpqhp_ctrl_list;
|
| 1169 | cpqhp_ctrl_list = ctrl;
|
| 1170 | }
|
| 1171 |
|
| 1172 | |
| 1173 | * Wait for exclusive access to hardware
|
| 1174 |
|
| 1175 | mutex_lock(&ctrl->crit_sect);
|
| 1176 |
|
| 1177 | num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
|
| 1178 |
|
| 1179 |
|
| 1180 | device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
|
| 1181 |
|
| 1182 | while (num_of_slots) {
|
| 1183 | dbg("num_of_slots: %d\n", num_of_slots);
|
| 1184 | func = cpqhp_slot_find(ctrl->bus, device, 0);
|
| 1185 | if (!func)
|
| 1186 | break;
|
| 1187 |
|
| 1188 | hp_slot = func->device - ctrl->slot_device_offset;
|
| 1189 | dbg("hp_slot: %d\n", hp_slot);
|
| 1190 |
|
| 1191 |
|
| 1192 | temp_word = ctrl->ctrl_int_comp >> 16;
|
| 1193 | func->presence_save = (temp_word >> hp_slot) & 0x01;
|
| 1194 | func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
|
| 1195 |
|
| 1196 | if (ctrl->ctrl_int_comp & (0x1L << hp_slot))
|
| 1197 | func->switch_save = 0;
|
| 1198 | else
|
| 1199 | func->switch_save = 0x10;
|
| 1200 |
|
| 1201 | if (!power_mode)
|
| 1202 | if (!func->is_a_board) {
|
| 1203 | green_LED_off(ctrl, hp_slot);
|
| 1204 | slot_disable(ctrl, hp_slot);
|
| 1205 | }
|
| 1206 |
|
| 1207 | device++;
|
| 1208 | num_of_slots--;
|
| 1209 | }
|
| 1210 |
|
| 1211 | if (!power_mode) {
|
| 1212 | set_SOGO(ctrl);
|
| 1213 |
|
| 1214 | wait_for_ctrl_irq(ctrl);
|
| 1215 | }
|
| 1216 |
|
| 1217 | rc = init_SERR(ctrl);
|
| 1218 | if (rc) {
|
| 1219 | err("init_SERR failed\n");
|
| 1220 | mutex_unlock(&ctrl->crit_sect);
|
| 1221 | goto err_free_irq;
|
| 1222 | }
|
| 1223 |
|
| 1224 |
|
| 1225 | mutex_unlock(&ctrl->crit_sect);
|
| 1226 |
|
| 1227 | cpqhp_create_debugfs_files(ctrl);
|
| 1228 |
|
| 1229 | return 0;
|
| 1230 |
|
| 1231 | err_free_irq:
|
| 1232 | free_irq(ctrl->interrupt, ctrl);
|
| 1233 | err_iounmap:
|
| 1234 | iounmap(ctrl->hpc_reg);
|
| 1235 | err_free_mem_region:
|
| 1236 | release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
|
| 1237 | err_free_bus:
|
| 1238 | kfree(ctrl->pci_bus);
|
| 1239 | err_free_ctrl:
|
| 1240 | kfree(ctrl);
|
| 1241 | err_disable_device:
|
| 1242 | pci_disable_device(pdev);
|
| 1243 | return rc;
|
| 1244 | }
|
| 1245 |
|
| 1246 | static void __exit unload_cpqphpd(void)
|
| 1247 | {
|
| 1248 | struct pci_func *next;
|
| 1249 | struct pci_func *TempSlot;
|
| 1250 | int loop;
|
| 1251 | u32 rc;
|
| 1252 | struct controller *ctrl;
|
| 1253 | struct controller *tctrl;
|
| 1254 | struct pci_resource *res;
|
| 1255 | struct pci_resource *tres;
|
| 1256 |
|
| 1257 | compaq_nvram_store(cpqhp_rom_start);
|
| 1258 |
|
| 1259 | ctrl = cpqhp_ctrl_list;
|
| 1260 |
|
| 1261 | while (ctrl) {
|
| 1262 | if (ctrl->hpc_reg) {
|
| 1263 | u16 misc;
|
| 1264 | rc = read_slot_enable(ctrl);
|
| 1265 |
|
| 1266 | writeb(0, ctrl->hpc_reg + SLOT_SERR);
|
| 1267 | writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK);
|
| 1268 |
|
| 1269 | misc = readw(ctrl->hpc_reg + MISC);
|
| 1270 | misc &= 0xFFFD;
|
| 1271 | writew(misc, ctrl->hpc_reg + MISC);
|
| 1272 | }
|
| 1273 |
|
| 1274 | ctrl_slot_cleanup(ctrl);
|
| 1275 |
|
| 1276 | res = ctrl->io_head;
|
| 1277 | while (res) {
|
| 1278 | tres = res;
|
| 1279 | res = res->next;
|
| 1280 | kfree(tres);
|
| 1281 | }
|
| 1282 |
|
| 1283 | res = ctrl->mem_head;
|
| 1284 | while (res) {
|
| 1285 | tres = res;
|
| 1286 | res = res->next;
|
| 1287 | kfree(tres);
|
| 1288 | }
|
| 1289 |
|
| 1290 | res = ctrl->p_mem_head;
|
| 1291 | while (res) {
|
| 1292 | tres = res;
|
| 1293 | res = res->next;
|
| 1294 | kfree(tres);
|
| 1295 | }
|
| 1296 |
|
| 1297 | res = ctrl->bus_head;
|
| 1298 | while (res) {
|
| 1299 | tres = res;
|
| 1300 | res = res->next;
|
| 1301 | kfree(tres);
|
| 1302 | }
|
| 1303 |
|
| 1304 | kfree(ctrl->pci_bus);
|
| 1305 |
|
| 1306 | tctrl = ctrl;
|
| 1307 | ctrl = ctrl->next;
|
| 1308 | kfree(tctrl);
|
| 1309 | }
|
| 1310 |
|
| 1311 | for (loop = 0; loop < 256; loop++) {
|
| 1312 | next = cpqhp_slot_list[loop];
|
| 1313 | while (next != NULL) {
|
| 1314 | res = next->io_head;
|
| 1315 | while (res) {
|
| 1316 | tres = res;
|
| 1317 | res = res->next;
|
| 1318 | kfree(tres);
|
| 1319 | }
|
| 1320 |
|
| 1321 | res = next->mem_head;
|
| 1322 | while (res) {
|
| 1323 | tres = res;
|
| 1324 | res = res->next;
|
| 1325 | kfree(tres);
|
| 1326 | }
|
| 1327 |
|
| 1328 | res = next->p_mem_head;
|
| 1329 | while (res) {
|
| 1330 | tres = res;
|
| 1331 | res = res->next;
|
| 1332 | kfree(tres);
|
| 1333 | }
|
| 1334 |
|
| 1335 | res = next->bus_head;
|
| 1336 | while (res) {
|
| 1337 | tres = res;
|
| 1338 | res = res->next;
|
| 1339 | kfree(tres);
|
| 1340 | }
|
| 1341 |
|
| 1342 | TempSlot = next;
|
| 1343 | next = next->next;
|
| 1344 | kfree(TempSlot);
|
| 1345 | }
|
| 1346 | }
|
| 1347 |
|
| 1348 |
|
| 1349 | if (initialized)
|
| 1350 | cpqhp_event_stop_thread();
|
| 1351 |
|
| 1352 |
|
| 1353 | if (cpqhp_rom_start)
|
| 1354 | iounmap(cpqhp_rom_start);
|
| 1355 | if (smbios_start)
|
| 1356 | iounmap(smbios_start);
|
| 1357 | }
|
| 1358 |
|
| 1359 | static const struct pci_device_id hpcd_pci_tbl[] = {
|
| 1360 | {
|
| 1361 |
|
| 1362 | .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
|
| 1363 | .class_mask = ~0,
|
| 1364 |
|
| 1365 |
|
| 1366 | .vendor = PCI_ANY_ID,
|
| 1367 | .device = PCI_ANY_ID,
|
| 1368 | .subvendor = PCI_ANY_ID,
|
| 1369 | .subdevice = PCI_ANY_ID,
|
| 1370 |
|
| 1371 | }, { }
|
| 1372 | };
|
| 1373 |
|
| 1374 | MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl);
|
| 1375 |
|
| 1376 | static struct pci_driver cpqhpc_driver = {
|
| 1377 | .name = "compaq_pci_hotplug",
|
| 1378 | .id_table = hpcd_pci_tbl,
|
| 1379 | .probe = cpqhpc_probe,
|
| 1380 |
|
| 1381 | };
|
| 1382 |
|
| 1383 | static int __init cpqhpc_init(void)
|
| 1384 | {
|
| 1385 | int result;
|
| 1386 |
|
| 1387 | cpqhp_debug = debug;
|
| 1388 |
|
| 1389 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
| 1390 | cpqhp_initialize_debugfs();
|
| 1391 | result = pci_register_driver(&cpqhpc_driver);
|
| 1392 | dbg("pci_register_driver = %d\n", result);
|
| 1393 | return result;
|
| 1394 | }
|
| 1395 |
|
| 1396 | static void __exit cpqhpc_cleanup(void)
|
| 1397 | {
|
| 1398 | dbg("unload_cpqphpd()\n");
|
| 1399 | unload_cpqphpd();
|
| 1400 |
|
| 1401 | dbg("pci_unregister_driver\n");
|
| 1402 | pci_unregister_driver(&cpqhpc_driver);
|
| 1403 | cpqhp_shutdown_debugfs();
|
| 1404 | }
|
| 1405 |
|
| 1406 | module_init(cpqhpc_init);
|
| 1407 | module_exit(cpqhpc_cleanup);
|