📄 cpqphp_core.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/proc_fs.h>#include <linux/miscdevice.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/uaccess.h>#include "cpqphp.h"#include "cpqphp_nvram.h"#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... *//* Global variables */int cpqhp_debug;struct controller *cpqhp_ctrl_list; /* = NULL */struct pci_func *cpqhp_slot_list[256];/* local variables */static void *smbios_table;static void *smbios_start;static void *cpqhp_rom_start;static u8 power_mode;static int debug;#define DRIVER_VERSION "0.9.6"#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>"#define DRIVER_DESC "Compaq Hot Plug PCI Controller Driver"MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");MODULE_PARM(power_mode, "b");MODULE_PARM_DESC(power_mode, "Power mode enabled or not");MODULE_PARM(debug, "i");MODULE_PARM_DESC(debug, "Debugging mode enabled or not");#define CPQHPC_MODULE_MINOR 208static int one_time_init (void);static int set_attention_status (struct hotplug_slot *slot, u8 value);static int process_SI (struct hotplug_slot *slot);static int process_SS (struct hotplug_slot *slot);static int hardware_test (struct hotplug_slot *slot, u32 value);static int get_power_status (struct hotplug_slot *slot, u8 *value);static int get_attention_status (struct hotplug_slot *slot, u8 *value);static int get_latch_status (struct hotplug_slot *slot, u8 *value);static int get_adapter_status (struct hotplug_slot *slot, u8 *value);static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { owner: THIS_MODULE, set_attention_status: set_attention_status, enable_slot: process_SI, disable_slot: process_SS, hardware_test: hardware_test, get_power_status: get_power_status, get_attention_status: get_attention_status, get_latch_status: get_latch_status, get_adapter_status: get_adapter_status,};static inline int is_slot64bit (struct slot *slot){ if (!slot || !slot->p_sm_slot) return 0; if (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) return 1; return 0;}static inline int is_slot66mhz (struct slot *slot){ if (!slot || !slot->p_sm_slot) return 0; if (readb(slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E) return 1; return 0;}/** * detect_SMBIOS_pointer - find the system Management BIOS Table in the specified region of memory. * * @begin: begin pointer for region to be scanned. * @end: end pointer for region to be scanned. * * Returns pointer to the head of the SMBIOS tables (or NULL) * */static void * detect_SMBIOS_pointer(void *begin, void *end){ void *fp; void *endp; u8 temp1, temp2, temp3, temp4; int status = 0; endp = (end - sizeof(u32) + 1); for (fp = begin; fp <= endp; fp += 16) { temp1 = readb(fp); temp2 = readb(fp+1); temp3 = readb(fp+2); temp4 = readb(fp+3); if (temp1 == '_' && temp2 == 'S' && temp3 == 'M' && temp4 == '_') { status = 1; break; } } if (!status) fp = NULL; dbg("Discovered SMBIOS Entry point at %p\n", fp); return fp;}/** * init_SERR - Initializes the per slot SERR generation. * * For unexpected switch opens * */static int init_SERR(struct controller * ctrl){ u32 tempdword; u32 number_of_slots; u8 physical_slot; if (!ctrl) return 1; tempdword = ctrl->first_slot; number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; // Loop through slots while (number_of_slots) { physical_slot = tempdword; writeb(0, ctrl->hpc_reg + SLOT_SERR); tempdword++; number_of_slots--; } return 0;}/* nice debugging output */static int pci_print_IRQ_route (void){ struct irq_routing_table *routing_table; int len; int loop; u8 tbus, tdevice, tslot; routing_table = pcibios_get_irq_routing_table(); if (routing_table == NULL) { err("No BIOS Routing Table??? Not good\n"); return -ENOMEM; } len = (routing_table->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); // Make sure I got at least one entry if (len == 0) { kfree(routing_table); return -1; } dbg("bus dev func slot\n"); for (loop = 0; loop < len; ++loop) { tbus = routing_table->slots[loop].bus; tdevice = routing_table->slots[loop].devfn; tslot = routing_table->slots[loop].slot; dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot); } kfree(routing_table); return 0;}/* * get_subsequent_smbios_entry * * Gets the first entry if previous == NULL * Otherwise, returns the next entry * Uses global SMBIOS Table pointer * * @curr: %NULL or pointer to previously returned structure * * returns a pointer to an SMBIOS structure or NULL if none found */static void * get_subsequent_smbios_entry(void *smbios_start, void *smbios_table, void *curr){ u8 bail = 0; u8 previous_byte = 1; void *p_temp; void *p_max; if (!smbios_table || !curr) return(NULL); // set p_max to the end of the table p_max = smbios_start + readw(smbios_table + ST_LENGTH); p_temp = curr; p_temp += readb(curr + SMBIOS_GENERIC_LENGTH); while ((p_temp < p_max) && !bail) { // Look for the double NULL terminator // The first condition is the previous byte and the second is the curr if (!previous_byte && !(readb(p_temp))) { bail = 1; } previous_byte = readb(p_temp); p_temp++; } if (p_temp < p_max) { return p_temp; } else { return NULL; }}/** * get_SMBIOS_entry * * @type:SMBIOS structure type to be returned * @previous: %NULL or pointer to previously returned structure * * Gets the first entry of the specified type if previous == NULL * Otherwise, returns the next entry of the given type. * Uses global SMBIOS Table pointer * Uses get_subsequent_smbios_entry * * returns a pointer to an SMBIOS structure or %NULL if none found */static void *get_SMBIOS_entry (void *smbios_start, void *smbios_table, u8 type, void * previous){ if (!smbios_table) return NULL; if (!previous) { previous = smbios_start; } else { previous = get_subsequent_smbios_entry(smbios_start, smbios_table, previous); } while (previous) { if (readb(previous + SMBIOS_GENERIC_TYPE) != type) { previous = get_subsequent_smbios_entry(smbios_start, smbios_table, previous); } else { break; } } return previous;}static int ctrl_slot_setup (struct controller * ctrl, void *smbios_start, void *smbios_table){ struct slot *new_slot; u8 number_of_slots; u8 slot_device; u8 slot_number; u8 ctrl_slot; u32 tempdword; void *slot_entry= NULL; int result; dbg(__FUNCTION__"\n"); tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; slot_number = ctrl->first_slot; while (number_of_slots) { new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL); if (!new_slot) return -ENOMEM; memset(new_slot, 0, sizeof(struct slot)); new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); if (!new_slot->hotplug_slot) { kfree (new_slot); return -ENOMEM; } memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot)); new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); if (!new_slot->hotplug_slot->info) { kfree (new_slot->hotplug_slot); kfree (new_slot); return -ENOMEM; } memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info)); new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL); if (!new_slot->hotplug_slot->name) { kfree (new_slot->hotplug_slot->info); kfree (new_slot->hotplug_slot); kfree (new_slot); return -ENOMEM; } new_slot->magic = SLOT_MAGIC; new_slot->ctrl = ctrl; new_slot->bus = ctrl->bus; new_slot->device = slot_device; new_slot->number = slot_number; dbg("slot->number = %d\n",new_slot->number); slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9, slot_entry); while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) != new_slot->number)) { slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9, slot_entry); } new_slot->p_sm_slot = slot_entry; init_timer(&new_slot->task_event); new_slot->task_event.expires = jiffies + 5 * HZ; new_slot->task_event.function = cpqhp_pushbutton_thread; //FIXME: these capabilities aren't used but if they are // they need to be correctly implemented new_slot->capabilities |= PCISLOT_REPLACE_SUPPORTED; new_slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED; if (is_slot64bit(new_slot)) new_slot->capabilities |= PCISLOT_64_BIT_SUPPORTED; if (is_slot66mhz(new_slot)) new_slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED; if (ctrl->speed == 1) new_slot->capabilities |= PCISLOT_66_MHZ_OPERATION; ctrl_slot = slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4); // Check presence new_slot->capabilities |= ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> ctrl_slot) & 0x02; // Check the switch state new_slot->capabilities |= ((~tempdword & 0xFF) >> ctrl_slot) & 0x01; // Check the slot enable new_slot->capabilities |= ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; /* register this slot with the hotplug pci core */ new_slot->hotplug_slot->private = new_slot; make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot); new_slot->hotplug_slot->ops = &cpqphp_hotplug_slot_ops; new_slot->hotplug_slot->info->power_status = get_slot_enabled(ctrl, new_slot); new_slot->hotplug_slot->info->attention_status = cpq_get_attention_status(ctrl, new_slot); new_slot->hotplug_slot->info->latch_status = cpq_get_latch_status(ctrl, new_slot); new_slot->hotplug_slot->info->adapter_status = get_presence_status(ctrl, new_slot); dbg ("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n", new_slot->bus, new_slot->device, new_slot->number, ctrl->slot_device_offset, slot_number); result = pci_hp_register (new_slot->hotplug_slot); if (result) { err ("pci_hp_register failed with error %d\n", result); kfree (new_slot->hotplug_slot->info); kfree (new_slot->hotplug_slot->name); kfree (new_slot->hotplug_slot); kfree (new_slot); return result; } new_slot->next = ctrl->slot; ctrl->slot = new_slot; number_of_slots--; slot_device++; slot_number++; } return(0);}static int ctrl_slot_cleanup (struct controller * ctrl){ struct slot *old_slot, *next_slot; old_slot = ctrl->slot; ctrl->slot = NULL; while (old_slot) { next_slot = old_slot->next; pci_hp_deregister (old_slot->hotplug_slot); kfree(old_slot->hotplug_slot->info); kfree(old_slot->hotplug_slot->name); kfree(old_slot->hotplug_slot); kfree(old_slot); old_slot = next_slot; } //Free IRQ associated with hot plug device free_irq(ctrl->interrupt, ctrl); //Unmap the memory iounmap(ctrl->hpc_reg); //Finally reclaim PCI mem release_mem_region(pci_resource_start(ctrl->pci_dev, 0), pci_resource_len(ctrl->pci_dev, 0)); return(0);}//============================================================================// function: get_slot_mapping//// Description: Attempts to determine a logical slot mapping for a PCI// device. Won't work for more than one PCI-PCI bridge// in a slot.//// Input: u8 bus_num - bus number of PCI device// u8 dev_num - device number of PCI device// u8 *slot - Pointer to u8 where slot number will// be returned//// Output: SUCCESS or FAILURE//=============================================================================static int get_slot_mapping (struct pci_ops *ops, u8 bus_num, u8 dev_num, u8 *slot){ struct irq_routing_table *PCIIRQRoutingInfoLength; u32 work; long len; long loop; u8 tbus, tdevice, tslot, bridgeSlot; dbg(__FUNCTION__" %p, %d, %d, %p\n", ops, bus_num, dev_num, slot); bridgeSlot = 0xFF; PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -