📄 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> * * Jan 12, 2003 - Added 66/100/133MHz PCI-X support, * Torben Mathiasen <torben.mathiasen@hp.com> * */#include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/proc_fs.h>#include <linux/slab.h>#include <linux/workqueue.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include "cpqphp.h"#include "cpqphp_nvram.h"#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... *//* Global variables */int cpqhp_debug;int cpqhp_legacy_mode;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 int power_mode;static int debug;#define DRIVER_VERSION "0.9.8"#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_param(power_mode, bool, 644);MODULE_PARM_DESC(power_mode, "Power mode enabled or not");module_param(debug, bool, 644);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 int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *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, .get_max_bus_speed = get_max_bus_speed, .get_cur_bus_speed = get_cur_bus_speed,};static inline int is_slot64bit(struct slot *slot){ return (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0;}static inline int is_slot66mhz(struct slot *slot){ return (readb(slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E) ? 1 : 0;}/** * detect_SMBIOS_pointer - find the System Management BIOS Table in mem region. * * @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: get the next entry from bios table. * * 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 void release_slot(struct hotplug_slot *hotplug_slot){ struct slot *slot = hotplug_slot->private; dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot->name); kfree(slot->hotplug_slot); kfree(slot);}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 = -ENOMEM; dbg("%s\n", __FUNCTION__); 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 = kmalloc(sizeof(*new_slot), GFP_KERNEL); if (!new_slot) goto error; memset(new_slot, 0, sizeof(struct slot)); new_slot->hotplug_slot = kmalloc(sizeof(*(new_slot->hotplug_slot)), GFP_KERNEL); if (!new_slot->hotplug_slot) goto error_slot; memset(new_slot->hotplug_slot, 0, sizeof(struct hotplug_slot)); new_slot->hotplug_slot->info = kmalloc(sizeof(*(new_slot->hotplug_slot->info)), GFP_KERNEL); if (!new_slot->hotplug_slot->info) goto error_hpslot; 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) goto error_info; 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 == PCI_SPEED_66MHz) 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->release = &release_slot; 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); goto error_name; } new_slot->next = ctrl->slot; ctrl->slot = new_slot; number_of_slots--; slot_device++; slot_number++; } return 0;error_name: kfree(new_slot->hotplug_slot->name);error_info: kfree(new_slot->hotplug_slot->info);error_hpslot: kfree(new_slot->hotplug_slot);error_slot: kfree(new_slot);error: return result;}static int ctrl_slot_cleanup (struct controller * ctrl){ struct slot *old_slot, *next_slot; old_slot = ctrl->slot; ctrl->slot = NULL; while (old_slot) { /* memory will be freed by the release_slot callback */ next_slot = old_slot->next; pci_hp_deregister (old_slot->hotplug_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 intget_slot_mapping(struct pci_bus *bus, 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("%s: %p, %d, %d, %p\n", __FUNCTION__, bus, bus_num, dev_num, slot); bridgeSlot = 0xFF;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -