1407 lines · 33.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * 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. * * Send feedback to <greg@kroah.com> * * Jan 12, 2003 -	Added 66/100/133MHz PCI-X support, *			Torben Mathiasen <torben.mathiasen@hp.com> */ #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/pci_hotplug.h>#include <linux/init.h>#include <linux/interrupt.h> #include <linux/uaccess.h> #include "cpqphp.h"#include "cpqphp_nvram.h"  /* Global variables */int cpqhp_debug;int cpqhp_legacy_mode;struct controller *cpqhp_ctrl_list;	/* = NULL */struct pci_func *cpqhp_slot_list[256];struct irq_routing_table *cpqhp_routing_table; /* local variables */static void __iomem *smbios_table;static void __iomem *smbios_start;static void __iomem *cpqhp_rom_start;static bool power_mode;static bool debug;static int initialized; #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, 0644);MODULE_PARM_DESC(power_mode, "Power mode enabled or not"); module_param(debug, bool, 0644);MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); #define CPQHPC_MODULE_MINOR 208 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 __iomem *detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end){	void __iomem *fp;	void __iomem *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. * @ctrl: controller to use * * For unexpected switch opens */static int init_SERR(struct controller *ctrl){	u32 tempdword;	u32 number_of_slots; 	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) {		writeb(0, ctrl->hpc_reg + SLOT_SERR);		tempdword++;		number_of_slots--;	} 	return 0;} static int init_cpqhp_routing_table(void){	int len; 	cpqhp_routing_table = pcibios_get_irq_routing_table();	if (cpqhp_routing_table == NULL)		return -ENOMEM; 	len = cpqhp_routing_table_length();	if (len == 0) {		kfree(cpqhp_routing_table);		cpqhp_routing_table = NULL;		return -1;	} 	return 0;} /* nice debugging output */static void pci_print_IRQ_route(void){	int len;	int loop;	u8 tbus, tdevice, tslot; 	len = cpqhp_routing_table_length(); 	dbg("bus dev func slot\n");	for (loop = 0; loop < len; ++loop) {		tbus = cpqhp_routing_table->slots[loop].bus;		tdevice = cpqhp_routing_table->slots[loop].devfn;		tslot = cpqhp_routing_table->slots[loop].slot;		dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot); 	}}  /** * get_subsequent_smbios_entry: get the next entry from bios table. * @smbios_start: where to start in the SMBIOS table * @smbios_table: location of the SMBIOS table * @curr: %NULL or pointer to previously returned structure * * Gets the first entry if previous == NULL; * otherwise, returns the next entry. * Uses global SMBIOS Table pointer. * * Returns a pointer to an SMBIOS structure or NULL if none found. */static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start,						void __iomem *smbios_table,						void __iomem *curr){	u8 bail = 0;	u8 previous_byte = 1;	void __iomem *p_temp;	void __iomem *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 - return the requested SMBIOS entry or %NULL * @smbios_start: where to start in the SMBIOS table * @smbios_table: location of the SMBIOS table * @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 __iomem *get_SMBIOS_entry(void __iomem *smbios_start,					void __iomem *smbios_table,					u8 type,					void __iomem *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_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);		old_slot = next_slot;	} 	cpqhp_remove_debugfs_files(ctrl); 	/* 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;}  /** * get_slot_mapping - determine logical slot mapping for PCI device * * Won't work for more than one PCI-PCI bridge in a slot. * * @bus: pointer to the PCI bus structure * @bus_num: bus number of PCI device * @dev_num: device number of PCI device * @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){	u32 work;	long len;	long loop; 	u8 tbus, tdevice, tslot, bridgeSlot; 	dbg("%s: %p, %d, %d, %p\n", __func__, bus, bus_num, dev_num, slot); 	bridgeSlot = 0xFF; 	len = cpqhp_routing_table_length();	for (loop = 0; loop < len; ++loop) {		tbus = cpqhp_routing_table->slots[loop].bus;		tdevice = cpqhp_routing_table->slots[loop].devfn >> 3;		tslot = cpqhp_routing_table->slots[loop].slot; 		if ((tbus == bus_num) && (tdevice == dev_num)) {			*slot = tslot;			return 0;		} else {			/* Did not get a match on the target PCI device. Check			 * if the current IRQ table entry is a PCI-to-PCI			 * bridge device.  If so, and its secondary bus			 * matches the bus number for the target device, I need			 * to save the bridge's slot number.  If I can not find			 * an entry for the target device, I will have to			 * assume it's on the other side of the bridge, and			 * assign it the bridge's slot.			 */			bus->number = tbus;			pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0),						PCI_CLASS_REVISION, &work); 			if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {				pci_bus_read_config_dword(bus,							PCI_DEVFN(tdevice, 0),							PCI_PRIMARY_BUS, &work);				// See if bridge's secondary bus matches target bus.				if (((work >> 8) & 0x000000FF) == (long) bus_num)					bridgeSlot = tslot;			}		} 	} 	/* If we got here, we didn't find an entry in the IRQ mapping table for	 * the target PCI device.  If we did determine that the target device	 * is on the other side of a PCI-to-PCI bridge, return the slot number	 * for the bridge.	 */	if (bridgeSlot != 0xFF) {		*slot = bridgeSlot;		return 0;	}	/* Couldn't find an entry in the routing table for this PCI device */	return -1;}  /** * cpqhp_set_attention_status - Turns the Amber LED for a slot on or off * @ctrl: struct controller to use * @func: PCI device/function info * @status: LED control flag: 1 = LED on, 0 = LED off */static intcpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,				u32 status){	u8 hp_slot; 	if (func == NULL)		return 1; 	hp_slot = func->device - ctrl->slot_device_offset; 	/* Wait for exclusive access to hardware */	mutex_lock(&ctrl->crit_sect); 	if (status == 1)		amber_LED_on(ctrl, hp_slot);	else if (status == 0)		amber_LED_off(ctrl, hp_slot);	else {		/* Done with exclusive hardware access */		mutex_unlock(&ctrl->crit_sect);		return 1;	} 	set_SOGO(ctrl); 	/* Wait for SOBS to be unset */	wait_for_ctrl_irq(ctrl); 	/* Done with exclusive hardware access */	mutex_unlock(&ctrl->crit_sect); 	return 0;}  /** * set_attention_status - Turns the Amber LED for a slot on or off * @hotplug_slot: slot to change LED on * @status: LED control flag */static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status){	struct pci_func *slot_func;	struct slot *slot = to_slot(hotplug_slot);	struct controller *ctrl = slot->ctrl;	u8 bus;	u8 devfn;	u8 device;	u8 function; 	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); 	if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)		return -ENODEV; 	device = devfn >> 3;	function = devfn & 0x7;	dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function); 	slot_func = cpqhp_slot_find(bus, device, function);	if (!slot_func)		return -ENODEV; 	return cpqhp_set_attention_status(ctrl, slot_func, status);}  static int process_SI(struct hotplug_slot *hotplug_slot){	struct pci_func *slot_func;	struct slot *slot = to_slot(hotplug_slot);	struct controller *ctrl = slot->ctrl;	u8 bus;	u8 devfn;	u8 device;	u8 function; 	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); 	if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)		return -ENODEV; 	device = devfn >> 3;	function = devfn & 0x7;	dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function); 	slot_func = cpqhp_slot_find(bus, device, function);	if (!slot_func)		return -ENODEV; 	slot_func->bus = bus;	slot_func->device = device;	slot_func->function = function;	slot_func->configured = 0;	dbg("board_added(%p, %p)\n", slot_func, ctrl);	return cpqhp_process_SI(ctrl, slot_func);}  static int process_SS(struct hotplug_slot *hotplug_slot){	struct pci_func *slot_func;	struct slot *slot = to_slot(hotplug_slot);	struct controller *ctrl = slot->ctrl;	u8 bus;	u8 devfn;	u8 device;	u8 function; 	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); 	if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)		return -ENODEV; 	device = devfn >> 3;	function = devfn & 0x7;	dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function); 	slot_func = cpqhp_slot_find(bus, device, function);	if (!slot_func)		return -ENODEV; 	dbg("In %s, slot_func = %p, ctrl = %p\n", __func__, slot_func, ctrl);	return cpqhp_process_SS(ctrl, slot_func);} 

Lines 1–500 of 1407.