📄 cpqphp_pci.c
字号:
/* * Compaq Hot Plug Controller Driver * * Copyright (c) 1995,2001 Compaq Computer Corporation * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) * Copyright (c) 2001 IBM Corp. * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Send feedback to <greg@kroah.com> * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/proc_fs.h>#include <linux/pci.h>#include "cpqphp.h"#include "cpqphp_nvram.h"#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... */u8 cpqhp_nic_irq;u8 cpqhp_disk_irq;static u16 unused_IRQ;static int is_pci_dev_in_use(struct pci_dev* dev) { /* * dev->driver will be set if the device is in use by a new-style * driver -- otherwise, check the device's regions to see if any * driver has claimed them */ int i, inuse=0; if (dev->driver) return 1; //assume driver feels responsible for (i = 0; !dev->driver && !inuse && (i < 6); i++) { if (!pci_resource_start(dev, i)) continue; if (pci_resource_flags(dev, i) & IORESOURCE_IO) inuse = check_region(pci_resource_start(dev, i), pci_resource_len(dev, i)); else if (pci_resource_flags(dev, i) & IORESOURCE_MEM) inuse = check_mem_region(pci_resource_start(dev, i), pci_resource_len(dev, i)); } return inuse;}static int pci_hp_remove_device(struct pci_dev *dev){ if (is_pci_dev_in_use(dev)) { err("***Cannot safely power down device -- " "it appears to be in use***\n"); return -EBUSY; } pci_remove_device(dev); return 0;}/* * detect_HRT_floating_pointer * * find the Hot Plug Resource Table in the specified region of memory. * */static void *detect_HRT_floating_pointer(void *begin, void *end){ void *fp; void *endp; u8 temp1, temp2, temp3, temp4; int status = 0; endp = (end - sizeof(struct hrt) + 1); for (fp = begin; fp <= endp; fp += 16) { temp1 = readb(fp + SIG0); temp2 = readb(fp + SIG1); temp3 = readb(fp + SIG2); temp4 = readb(fp + SIG3); if (temp1 == '$' && temp2 == 'H' && temp3 == 'R' && temp4 == 'T') { status = 1; break; } } if (!status) fp = NULL; dbg("Discovered Hotplug Resource Table at %p\n", fp); return fp;}static int configure_visit_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) { struct pci_bus* bus = wrapped_bus->bus; struct pci_dev* dev = wrapped_dev->dev; struct pci_func *temp_func; int i=0; //We need to fix up the hotplug function representation with the linux representation do { temp_func = cpqhp_slot_find(dev->bus->number, dev->devfn >> 3, i++); } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); if (temp_func) { temp_func->pci_dev = dev; } else { //We did not even find a hotplug rep of the function, create it //This code might be taken out if we can guarantee the creation of functions //in parallel (hotplug and Linux at the same time). dbg("@@@@@@@@@@@ cpqhp_slot_create in "__FUNCTION__"\n"); temp_func = cpqhp_slot_create(bus->number); if (temp_func == NULL) return -ENOMEM; temp_func->pci_dev = dev; } //Create /proc/bus/pci proc entry for this device and bus device is on //Notify the drivers of the change if (temp_func->pci_dev) { pci_proc_attach_device(temp_func->pci_dev); pci_announce_device_to_drivers(temp_func->pci_dev); } return 0;}static int unconfigure_visit_pci_dev_phase2 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) { struct pci_dev* dev = wrapped_dev->dev; struct pci_func *temp_func; int i=0; //We need to remove the hotplug function representation with the linux representation do { temp_func = cpqhp_slot_find(dev->bus->number, dev->devfn >> 3, i++); if (temp_func) { dbg("temp_func->function = %d\n", temp_func->function); } } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); //Now, remove the Linux Representation if (dev) { if (pci_hp_remove_device(dev) == 0) { kfree(dev); //Now, remove } else { return -1; // problems while freeing, abort visitation } } if (temp_func) { temp_func->pci_dev = NULL; } else { dbg("No pci_func representation for bus, devfn = %d, %x\n", dev->bus->number, dev->devfn); } return 0;}static int unconfigure_visit_pci_bus_phase2 (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev) { struct pci_bus* bus = wrapped_bus->bus; //The cleanup code for proc entries regarding buses should be in the kernel... if (bus->procdir) dbg("detach_pci_bus %s\n", bus->procdir->name); pci_proc_detach_bus(bus); // The cleanup code should live in the kernel... bus->self->subordinate = NULL; // unlink from parent bus list_del(&bus->node); // Now, remove if (bus) kfree(bus); return 0;}static int unconfigure_visit_pci_dev_phase1 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) { struct pci_dev* dev = wrapped_dev->dev; dbg("attempting removal of driver for device (%x, %x, %x)\n", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); //Now, remove the Linux Driver Representation if (dev->driver) { if (dev->driver->remove) { dev->driver->remove(dev); dbg("driver was properly removed\n"); } dev->driver = NULL; } return is_pci_dev_in_use(dev);}static struct pci_visit configure_functions = { visit_pci_dev: configure_visit_pci_dev,};static struct pci_visit unconfigure_functions_phase1 = { post_visit_pci_dev: unconfigure_visit_pci_dev_phase1};static struct pci_visit unconfigure_functions_phase2 = { post_visit_pci_bus: unconfigure_visit_pci_bus_phase2, post_visit_pci_dev: unconfigure_visit_pci_dev_phase2};int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) { unsigned char bus; struct pci_dev dev0; struct pci_bus *child; struct pci_dev* temp; int rc = 0; struct pci_dev_wrapped wrapped_dev; struct pci_bus_wrapped wrapped_bus; memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); memset(&dev0, 0, sizeof(struct pci_dev)); if (func->pci_dev == NULL) func->pci_dev = pci_find_slot(func->bus, (func->device << 3) | (func->function & 0x7)); //Still NULL ? Well then scan for it ! if (func->pci_dev == NULL) { dbg("INFO: pci_dev still null\n"); dev0.bus = ctrl->pci_dev->bus; dev0.devfn = (func->device << 3) + (func->function & 0x7); dev0.sysdata = ctrl->pci_dev->sysdata; //this will generate pci_dev structures for all functions, but we will only call this case when lookup fails func->pci_dev = pci_scan_slot(&dev0); if (func->pci_dev == NULL) { dbg("ERROR: pci_dev still null\n"); return 0; } } if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); pci_do_scan_bus(child); } temp = func->pci_dev; if (temp) { wrapped_dev.dev = temp; wrapped_bus.bus = temp->bus; rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); } return rc;}int cpqhp_unconfigure_device(struct pci_func* func) { int rc = 0; int j; struct pci_dev_wrapped wrapped_dev; struct pci_bus_wrapped wrapped_bus; memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); dbg(__FUNCTION__": bus/dev/func = %x/%x/%x\n",func->bus, func->device, func->function); for (j=0; j<8 ; j++) { struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j); if (temp) { wrapped_dev.dev = temp; wrapped_bus.bus = temp->bus; rc = pci_visit_dev(&unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus); if (rc) break; rc = pci_visit_dev(&unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus); if (rc) break; } } return rc;}static int PCI_RefinedAccessConfig(struct pci_ops *ops, u8 bus, u8 device, u8 function, u8 offset, u32 *value){ u32 vendID = 0; if (pci_read_config_dword_nodev (ops, bus, device, function, PCI_VENDOR_ID, &vendID) == -1) return -1; if (vendID == 0xffffffff) return -1; return pci_read_config_dword_nodev (ops, bus, device, function, offset, value);}/* * cpqhp_set_irq * * @bus_num: bus number of PCI device * @dev_num: device number of PCI device * @slot: pointer to u8 where slot number will be returned */int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num){ int rc; u16 temp_word; struct pci_dev fakedev; struct pci_bus fakebus; fakedev.devfn = dev_num << 3; fakedev.bus = &fakebus; fakebus.number = bus_num; dbg(__FUNCTION__": dev %d, bus %d, pin %d, num %d\n", dev_num, bus_num, int_pin, irq_num); rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); dbg(__FUNCTION__":rc %d\n", rc); if (rc) return rc; // set the Edge Level Control Register (ELCR) temp_word = inb(0x4d0); temp_word |= inb(0x4d1) << 8; temp_word |= 0x01 << irq_num; // This should only be for x86 as it sets the Edge Level Control Register outb((u8) (temp_word & 0xFF), 0x4d0); outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); return 0;}/* * WTF??? This function isn't in the code, yet a function calls it, but the * compiler optimizes it away? strange. Here as a placeholder to keep the * compiler happy. */static int PCI_ScanBusNonBridge (u8 bus, u8 device){ return 0;}static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num){ u8 tdevice; u32 work; u8 tbus; for (tdevice = 0; tdevice < 0x100; tdevice++) { //Scan for access first if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1) continue; dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); //Yep we got one. Not a bridge ? if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) { *dev_num = tdevice; dbg("found it !\n"); return 0; } } for (tdevice = 0; tdevice < 0x100; tdevice++) { //Scan for access first if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1) continue; dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); //Yep we got one. bridge ? if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { pci_read_config_byte_nodev (ctrl->pci_ops, tbus, tdevice, 0, PCI_SECONDARY_BUS, &tbus); dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); if (PCI_ScanBusNonBridge(tbus, tdevice) == 0) return 0; } } return -1;}static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge){ struct irq_routing_table *PCIIRQRoutingInfoLength; long len; long loop; u32 work; u8 tbus, tdevice, tslot;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -