1252 lines · 30.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * IBM Hot Plug Controller Driver * * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation * * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2001-2003 IBM Corp. * * All rights reserved. * * Send feedback to <gregkh@us.ibm.com> * */ #include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/wait.h>#include "../pci.h"#include <asm/pci_x86.h>		/* for struct irq_routing_table */#include <asm/io_apic.h>#include "ibmphp.h" #define attn_on(sl)  ibmphp_hpc_writeslot(sl, HPC_SLOT_ATTNON)#define attn_off(sl) ibmphp_hpc_writeslot(sl, HPC_SLOT_ATTNOFF)#define attn_LED_blink(sl) ibmphp_hpc_writeslot(sl, HPC_SLOT_BLINKLED)#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot(sl, READ_REVLEVEL, rev)#define get_hpc_options(sl, opt) ibmphp_hpc_readslot(sl, READ_HPCOPTIONS, opt) #define DRIVER_VERSION	"0.6"#define DRIVER_DESC	"IBM Hot Plug PCI Controller Driver" int ibmphp_debug; static bool debug;module_param(debug, bool, S_IRUGO | S_IWUSR);MODULE_PARM_DESC(debug, "Debugging mode enabled or not");MODULE_LICENSE("GPL");MODULE_DESCRIPTION(DRIVER_DESC); struct pci_bus *ibmphp_pci_bus;static int max_slots; static int irqs[16];    /* PIC mode IRQs we're using so far (in case MPS			 * tables don't provide default info for empty slots */ static int init_flag; static inline int get_cur_bus_info(struct slot **sl){	int rc = 1;	struct slot *slot_cur = *sl; 	debug("options = %x\n", slot_cur->ctrl->options);	debug("revision = %x\n", slot_cur->ctrl->revision); 	if (READ_BUS_STATUS(slot_cur->ctrl))		rc = ibmphp_hpc_readslot(slot_cur, READ_BUSSTATUS, NULL); 	if (rc)		return rc; 	slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED(slot_cur->busstatus);	if (READ_BUS_MODE(slot_cur->ctrl))		slot_cur->bus_on->current_bus_mode =				CURRENT_BUS_MODE(slot_cur->busstatus);	else		slot_cur->bus_on->current_bus_mode = 0xFF; 	debug("busstatus = %x, bus_speed = %x, bus_mode = %x\n",			slot_cur->busstatus,			slot_cur->bus_on->current_speed,			slot_cur->bus_on->current_bus_mode); 	*sl = slot_cur;	return 0;} static inline int slot_update(struct slot **sl){	int rc;	rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL);	if (rc)		return rc;	if (!init_flag)		rc = get_cur_bus_info(sl);	return rc;} static int __init get_max_slots(void){	struct slot *slot_cur;	u8 slot_count = 0; 	list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {		/* sometimes the hot-pluggable slots start with 4 (not always from 1) */		slot_count = max(slot_count, slot_cur->number);	}	return slot_count;} /* This routine will put the correct slot->device information per slot.  It's * called from initialization of the slot structures. It will also assign * interrupt numbers per each slot. * Parameters: struct slot * Returns 0 or errors */int ibmphp_init_devno(struct slot **cur_slot){	struct irq_routing_table *rtable;	int len;	int loop;	int i; 	rtable = pcibios_get_irq_routing_table();	if (!rtable) {		err("no BIOS routing table...\n");		return -ENOMEM;	} 	len = (rtable->size - sizeof(struct irq_routing_table)) /			sizeof(struct irq_info); 	if (!len) {		kfree(rtable);		return -1;	}	for (loop = 0; loop < len; loop++) {		if ((*cur_slot)->number == rtable->slots[loop].slot &&		    (*cur_slot)->bus == rtable->slots[loop].bus) {			(*cur_slot)->device = PCI_SLOT(rtable->slots[loop].devfn);			for (i = 0; i < 4; i++)				(*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector((int) (*cur_slot)->bus,						(int) (*cur_slot)->device, i); 			debug("(*cur_slot)->irq[0] = %x\n",					(*cur_slot)->irq[0]);			debug("(*cur_slot)->irq[1] = %x\n",					(*cur_slot)->irq[1]);			debug("(*cur_slot)->irq[2] = %x\n",					(*cur_slot)->irq[2]);			debug("(*cur_slot)->irq[3] = %x\n",					(*cur_slot)->irq[3]); 			debug("rtable->exclusive_irqs = %x\n",					rtable->exclusive_irqs);			debug("rtable->slots[loop].irq[0].bitmap = %x\n",					rtable->slots[loop].irq[0].bitmap);			debug("rtable->slots[loop].irq[1].bitmap = %x\n",					rtable->slots[loop].irq[1].bitmap);			debug("rtable->slots[loop].irq[2].bitmap = %x\n",					rtable->slots[loop].irq[2].bitmap);			debug("rtable->slots[loop].irq[3].bitmap = %x\n",					rtable->slots[loop].irq[3].bitmap); 			debug("rtable->slots[loop].irq[0].link = %x\n",					rtable->slots[loop].irq[0].link);			debug("rtable->slots[loop].irq[1].link = %x\n",					rtable->slots[loop].irq[1].link);			debug("rtable->slots[loop].irq[2].link = %x\n",					rtable->slots[loop].irq[2].link);			debug("rtable->slots[loop].irq[3].link = %x\n",					rtable->slots[loop].irq[3].link);			debug("end of init_devno\n");			kfree(rtable);			return 0;		}	} 	kfree(rtable);	return -1;} static inline int power_on(struct slot *slot_cur){	u8 cmd = HPC_SLOT_ON;	int retval; 	retval = ibmphp_hpc_writeslot(slot_cur, cmd);	if (retval) {		err("power on failed\n");		return retval;	}	if (CTLR_RESULT(slot_cur->ctrl->status)) {		err("command not completed successfully in power_on\n");		return -EIO;	}	msleep(3000);	/* For ServeRAID cards, and some 66 PCI */	return 0;} static inline int power_off(struct slot *slot_cur){	u8 cmd = HPC_SLOT_OFF;	int retval; 	retval = ibmphp_hpc_writeslot(slot_cur, cmd);	if (retval) {		err("power off failed\n");		return retval;	}	if (CTLR_RESULT(slot_cur->ctrl->status)) {		err("command not completed successfully in power_off\n");		retval = -EIO;	}	return retval;} static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value){	int rc = 0;	struct slot *pslot;	u8 cmd = 0x00;     /* avoid compiler warning */ 	debug("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n",			(ulong) hotplug_slot, value);	ibmphp_lock_operations();  	if (hotplug_slot) {		switch (value) {		case HPC_SLOT_ATTN_OFF:			cmd = HPC_SLOT_ATTNOFF;			break;		case HPC_SLOT_ATTN_ON:			cmd = HPC_SLOT_ATTNON;			break;		case HPC_SLOT_ATTN_BLINK:			cmd = HPC_SLOT_BLINKLED;			break;		default:			rc = -ENODEV;			err("set_attention_status - Error : invalid input [%x]\n",					value);			break;		}		if (rc == 0) {			pslot = to_slot(hotplug_slot);			rc = ibmphp_hpc_writeslot(pslot, cmd);		}	} else		rc = -ENODEV; 	ibmphp_unlock_operations(); 	debug("set_attention_status - Exit rc[%d]\n", rc);	return rc;} static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value){	int rc = -ENODEV;	struct slot *pslot;	struct slot myslot; 	debug("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",					(ulong) hotplug_slot, (ulong) value); 	ibmphp_lock_operations();	if (hotplug_slot) {		pslot = to_slot(hotplug_slot);		memcpy(&myslot, pslot, sizeof(struct slot));		rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,					 &myslot.status);		if (!rc)			rc = ibmphp_hpc_readslot(pslot, READ_EXTSLOTSTATUS,						 &myslot.ext_status);		if (!rc)			*value = SLOT_ATTN(myslot.status, myslot.ext_status);	} 	ibmphp_unlock_operations();	debug("get_attention_status - Exit rc[%d] value[%x]\n", rc, *value);	return rc;} static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value){	int rc = -ENODEV;	struct slot *pslot;	struct slot myslot; 	debug("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",					(ulong) hotplug_slot, (ulong) value);	ibmphp_lock_operations();	if (hotplug_slot) {		pslot = to_slot(hotplug_slot);		memcpy(&myslot, pslot, sizeof(struct slot));		rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,					 &myslot.status);		if (!rc)			*value = SLOT_LATCH(myslot.status);	} 	ibmphp_unlock_operations();	debug("get_latch_status - Exit rc[%d] rc[%x] value[%x]\n",			rc, rc, *value);	return rc;}  static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value){	int rc = -ENODEV;	struct slot *pslot;	struct slot myslot; 	debug("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",					(ulong) hotplug_slot, (ulong) value);	ibmphp_lock_operations();	if (hotplug_slot) {		pslot = to_slot(hotplug_slot);		memcpy(&myslot, pslot, sizeof(struct slot));		rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,					 &myslot.status);		if (!rc)			*value = SLOT_PWRGD(myslot.status);	} 	ibmphp_unlock_operations();	debug("get_power_status - Exit rc[%d] rc[%x] value[%x]\n",			rc, rc, *value);	return rc;} static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value){	int rc = -ENODEV;	struct slot *pslot;	u8 present;	struct slot myslot; 	debug("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",					(ulong) hotplug_slot, (ulong) value);	ibmphp_lock_operations();	if (hotplug_slot) {		pslot = to_slot(hotplug_slot);		memcpy(&myslot, pslot, sizeof(struct slot));		rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,					 &myslot.status);		if (!rc) {			present = SLOT_PRESENT(myslot.status);			if (present == HPC_SLOT_EMPTY)				*value = 0;			else				*value = 1;		}	} 	ibmphp_unlock_operations();	debug("get_adapter_present - Exit rc[%d] value[%x]\n", rc, *value);	return rc;} static int get_max_bus_speed(struct slot *slot){	int rc = 0;	u8 mode = 0;	enum pci_bus_speed speed;	struct pci_bus *bus = slot->hotplug_slot.pci_slot->bus; 	debug("%s - Entry slot[%p]\n", __func__, slot); 	ibmphp_lock_operations();	mode = slot->supported_bus_mode;	speed = slot->supported_speed;	ibmphp_unlock_operations(); 	switch (speed) {	case BUS_SPEED_33:		break;	case BUS_SPEED_66:		if (mode == BUS_MODE_PCIX)			speed += 0x01;		break;	case BUS_SPEED_100:	case BUS_SPEED_133:		speed += 0x01;		break;	default:		/* Note (will need to change): there would be soon 256, 512 also */		rc = -ENODEV;	} 	if (!rc)		bus->max_bus_speed = speed; 	debug("%s - Exit rc[%d] speed[%x]\n", __func__, rc, speed);	return rc;} /**************************************************************************** * This routine will initialize the ops data structure used in the validate * function. It will also power off empty slots that are powered on since BIOS * leaves those on, albeit disconnected ****************************************************************************/static int __init init_ops(void){	struct slot *slot_cur;	int retval;	int rc; 	list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {		debug("BEFORE GETTING SLOT STATUS, slot # %x\n",							slot_cur->number);		if (slot_cur->ctrl->revision == 0xFF)			if (get_ctrl_revision(slot_cur,						&slot_cur->ctrl->revision))				return -1; 		if (slot_cur->bus_on->current_speed == 0xFF)			if (get_cur_bus_info(&slot_cur))				return -1;		get_max_bus_speed(slot_cur); 		if (slot_cur->ctrl->options == 0xFF)			if (get_hpc_options(slot_cur, &slot_cur->ctrl->options))				return -1; 		retval = slot_update(&slot_cur);		if (retval)			return retval; 		debug("status = %x\n", slot_cur->status);		debug("ext_status = %x\n", slot_cur->ext_status);		debug("SLOT_POWER = %x\n", SLOT_POWER(slot_cur->status));		debug("SLOT_PRESENT = %x\n", SLOT_PRESENT(slot_cur->status));		debug("SLOT_LATCH = %x\n", SLOT_LATCH(slot_cur->status)); 		if ((SLOT_PWRGD(slot_cur->status)) &&		    !(SLOT_PRESENT(slot_cur->status)) &&		    !(SLOT_LATCH(slot_cur->status))) {			debug("BEFORE POWER OFF COMMAND\n");				rc = power_off(slot_cur);				if (rc)					return rc; 	/*		retval = slot_update(&slot_cur);	 *		if (retval)	 *			return retval;	 *		ibmphp_update_slot_info(slot_cur);	 */		}	}	init_flag = 0;	return 0;} /* This operation will check whether the slot is within the bounds and * the operation is valid to perform on that slot * Parameters: slot, operation * Returns: 0 or error codes */static int validate(struct slot *slot_cur, int opn){	int number;	int retval; 	if (!slot_cur)		return -ENODEV;	number = slot_cur->number;	if ((number > max_slots) || (number < 0))		return -EBADSLT;	debug("slot_number in validate is %d\n", slot_cur->number); 	retval = slot_update(&slot_cur);	if (retval)		return retval; 	switch (opn) {		case ENABLE:			if (!(SLOT_PWRGD(slot_cur->status)) &&			     (SLOT_PRESENT(slot_cur->status)) &&			     !(SLOT_LATCH(slot_cur->status)))				return 0;			break;		case DISABLE:			if ((SLOT_PWRGD(slot_cur->status)) &&			    (SLOT_PRESENT(slot_cur->status)) &&			    !(SLOT_LATCH(slot_cur->status)))				return 0;			break;		default:			break;	}	err("validate failed....\n");	return -EINVAL;} /**************************************************************************** * This routine is for updating the data structures in the hotplug core * Parameters: struct slot * Returns: 0 or error ****************************************************************************/int ibmphp_update_slot_info(struct slot *slot_cur){

Lines 1–500 of 1252.