2005-04-17 02:20:36 +04:00
/*
* 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 0213 9 , USA .
*
* Send feedback to < greg @ kroah . com >
*
* Jan 12 , 2003 - Added 66 / 100 / 133 MHz PCI - X support ,
2009-03-31 19:23:11 +04:00
* Torben Mathiasen < torben . mathiasen @ hp . com >
2005-04-17 02:20:36 +04:00
*/
# 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>
2006-10-14 07:05:19 +04:00
# include <linux/pci_hotplug.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/interrupt.h>
# include <asm/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 ] ;
2009-03-31 19:24:02 +04:00
struct irq_routing_table * cpqhp_routing_table ;
2005-04-17 02:20:36 +04:00
/* local variables */
static void __iomem * smbios_table ;
static void __iomem * smbios_start ;
static void __iomem * cpqhp_rom_start ;
static int power_mode ;
static int debug ;
2005-06-02 14:42:37 +04:00
static int initialized ;
2005-04-17 02:20:36 +04:00
# 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 .
*
2007-11-28 20:04:30 +03:00
* Returns pointer to the head of the SMBIOS tables ( or % NULL ) .
2005-04-17 02:20:36 +04:00
*/
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 ;
}
}
2009-03-31 19:23:11 +04:00
2005-04-17 02:20:36 +04:00
if ( ! status )
fp = NULL ;
dbg ( " Discovered SMBIOS Entry point at %p \n " , fp ) ;
return fp ;
}
/**
* init_SERR - Initializes the per slot SERR generation .
2007-11-28 20:04:30 +03:00
* @ ctrl : controller to use
2005-04-17 02:20:36 +04:00
*
* 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 ;
2009-03-31 19:23:16 +04:00
/* Loop through slots */
2005-04-17 02:20:36 +04:00
while ( number_of_slots ) {
physical_slot = tempdword ;
writeb ( 0 , ctrl - > hpc_reg + SLOT_SERR ) ;
tempdword + + ;
number_of_slots - - ;
}
return 0 ;
}
2009-03-31 19:24:02 +04:00
static int init_cpqhp_routing_table ( void )
2005-04-17 02:20:36 +04:00
{
int len ;
2009-03-31 19:24:02 +04:00
cpqhp_routing_table = pcibios_get_irq_routing_table ( ) ;
if ( cpqhp_routing_table = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2009-03-31 19:24:02 +04:00
len = cpqhp_routing_table_length ( ) ;
2005-04-17 02:20:36 +04:00
if ( len = = 0 ) {
2009-03-31 19:24:02 +04:00
kfree ( cpqhp_routing_table ) ;
cpqhp_routing_table = NULL ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2009-03-31 19:24:02 +04:00
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 ( ) ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:24:02 +04:00
dbg ( " bus dev func slot \n " ) ;
2005-04-17 02:20:36 +04:00
for ( loop = 0 ; loop < len ; + + loop ) {
2009-03-31 19:24:02 +04:00
tbus = cpqhp_routing_table - > slots [ loop ] . bus ;
tdevice = cpqhp_routing_table - > slots [ loop ] . devfn ;
tslot = cpqhp_routing_table - > slots [ loop ] . slot ;
2005-04-17 02:20:36 +04:00
dbg ( " %d %d %d %d \n " , tbus , tdevice > > 3 , tdevice & 0x7 , tslot ) ;
}
2009-03-31 19:24:02 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
/**
* get_subsequent_smbios_entry : get the next entry from bios table .
2007-11-28 20:04:30 +03:00
* @ smbios_start : where to start in the SMBIOS table
* @ smbios_table : location of the SMBIOS table
2005-04-17 02:20:36 +04:00
* @ curr : % NULL or pointer to previously returned structure
*
2007-11-28 20:04:30 +03:00
* 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 .
2005-04-17 02:20:36 +04:00
*/
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 )
2009-03-31 19:23:31 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:16 +04:00
/* set p_max to the end of the table */
2005-04-17 02:20:36 +04:00
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
2009-03-31 19:23:16 +04:00
* and the second is the curr
*/
2009-03-31 19:23:31 +04:00
if ( ! previous_byte & & ! ( readb ( p_temp ) ) )
2005-04-17 02:20:36 +04:00
bail = 1 ;
previous_byte = readb ( p_temp ) ;
p_temp + + ;
}
2009-03-31 19:23:31 +04:00
if ( p_temp < p_max )
2005-04-17 02:20:36 +04:00
return p_temp ;
2009-03-31 19:23:31 +04:00
else
2005-04-17 02:20:36 +04:00
return NULL ;
}
/**
2007-11-28 20:04:30 +03:00
* 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
2005-04-17 02:20:36 +04:00
* @ previous : % NULL or pointer to previously returned structure
*
2007-11-28 20:04:30 +03:00
* Gets the first entry of the specified type if previous = = % NULL ;
2005-04-17 02:20:36 +04:00
* Otherwise , returns the next entry of the given type .
2007-11-28 20:04:30 +03:00
* Uses global SMBIOS Table pointer .
* Uses get_subsequent_smbios_entry .
2005-04-17 02:20:36 +04:00
*
2007-11-28 20:04:30 +03:00
* Returns a pointer to an SMBIOS structure or % NULL if none found .
2005-04-17 02:20:36 +04:00
*/
static void __iomem * get_SMBIOS_entry ( void __iomem * smbios_start ,
void __iomem * smbios_table ,
u8 type ,
void __iomem * previous )
{
if ( ! smbios_table )
return NULL ;
2009-03-31 19:23:31 +04:00
if ( ! previous )
2005-04-17 02:20:36 +04:00
previous = smbios_start ;
2009-03-31 19:23:31 +04:00
else
2005-04-17 02:20:36 +04:00
previous = get_subsequent_smbios_entry ( smbios_start ,
smbios_table , previous ) ;
2009-03-31 19:23:31 +04:00
while ( previous )
if ( readb ( previous + SMBIOS_GENERIC_TYPE ) ! = type )
2005-04-17 02:20:36 +04:00
previous = get_subsequent_smbios_entry ( smbios_start ,
smbios_table , previous ) ;
2009-03-31 19:23:31 +04:00
else
2005-04-17 02:20:36 +04:00
break ;
return previous ;
}
static void release_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
kfree ( slot - > hotplug_slot - > info ) ;
kfree ( slot - > hotplug_slot ) ;
kfree ( slot ) ;
}
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 ;
}
2005-12-14 20:37:26 +03:00
cpqhp_remove_debugfs_files ( ctrl ) ;
2009-03-31 19:23:16 +04:00
/* Free IRQ associated with hot plug device */
2005-04-17 02:20:36 +04:00
free_irq ( ctrl - > interrupt , ctrl ) ;
2009-03-31 19:23:16 +04:00
/* Unmap the memory */
2005-04-17 02:20:36 +04:00
iounmap ( ctrl - > hpc_reg ) ;
2009-03-31 19:23:16 +04:00
/* Finally reclaim PCI mem */
2005-04-17 02:20:36 +04:00
release_mem_region ( pci_resource_start ( ctrl - > pci_dev , 0 ) ,
pci_resource_len ( ctrl - > pci_dev , 0 ) ) ;
2009-03-31 19:23:31 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-31 19:23:16 +04:00
/**
* get_slot_mapping - determine logical slot mapping for PCI device
*
* Won ' t work for more than one PCI - PCI bridge in a slot .
*
* @ 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
*/
2005-04-17 02:20:36 +04:00
static int
get_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 ;
2008-03-04 06:09:46 +03:00
dbg ( " %s: %p, %d, %d, %p \n " , __func__ , bus , bus_num , dev_num , slot ) ;
2005-04-17 02:20:36 +04:00
bridgeSlot = 0xFF ;
2009-03-31 19:24:02 +04:00
len = cpqhp_routing_table_length ( ) ;
2005-04-17 02:20:36 +04:00
for ( loop = 0 ; loop < len ; + + loop ) {
2009-03-31 19:24:02 +04:00
tbus = cpqhp_routing_table - > slots [ loop ] . bus ;
tdevice = cpqhp_routing_table - > slots [ loop ] . devfn > > 3 ;
tslot = cpqhp_routing_table - > slots [ loop ] . slot ;
2005-04-17 02:20:36 +04:00
if ( ( tbus = = bus_num ) & & ( tdevice = = dev_num ) ) {
* slot = tslot ;
return 0 ;
} else {
/* Did not get a match on the target PCI device. Check
2009-03-31 19:23:16 +04:00
* if the current IRQ table entry is a PCI - to - PCI
* bridge device . If so , and it ' s 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 .
*/
2005-04-17 02:20:36 +04:00
bus - > number = tbus ;
pci_bus_read_config_dword ( bus , PCI_DEVFN ( tdevice , 0 ) ,
2007-08-28 03:17:38 +04:00
PCI_CLASS_REVISION , & work ) ;
2005-04-17 02:20:36 +04:00
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.
2009-03-31 19:23:31 +04:00
if ( ( ( work > > 8 ) & 0x000000FF ) = = ( long ) bus_num )
2005-04-17 02:20:36 +04:00
bridgeSlot = tslot ;
}
}
}
2009-03-31 19:23:16 +04:00
/* 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 .
*/
2005-04-17 02:20:36 +04:00
if ( bridgeSlot ! = 0xFF ) {
* slot = bridgeSlot ;
return 0 ;
}
2009-03-31 19:23:16 +04:00
/* Couldn't find an entry in the routing table for this PCI device */
2005-04-17 02:20:36 +04:00
return - 1 ;
}
/**
* cpqhp_set_attention_status - Turns the Amber LED for a slot on or off
2007-11-28 20:04:30 +03:00
* @ ctrl : struct controller to use
* @ func : PCI device / function info
* @ status : LED control flag : 1 = LED on , 0 = LED off
2005-04-17 02:20:36 +04:00
*/
static int
cpqhp_set_attention_status ( struct controller * ctrl , struct pci_func * func ,
u32 status )
{
u8 hp_slot ;
if ( func = = NULL )
2009-03-31 19:23:31 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
hp_slot = func - > device - ctrl - > slot_device_offset ;
2009-03-31 19:23:16 +04:00
/* Wait for exclusive access to hardware */
2006-01-13 18:02:15 +03:00
mutex_lock ( & ctrl - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:31 +04:00
if ( status = = 1 )
2005-04-17 02:20:36 +04:00
amber_LED_on ( ctrl , hp_slot ) ;
2009-03-31 19:23:31 +04:00
else if ( status = = 0 )
2005-04-17 02:20:36 +04:00
amber_LED_off ( ctrl , hp_slot ) ;
2009-03-31 19:23:31 +04:00
else {
2009-03-31 19:23:16 +04:00
/* Done with exclusive hardware access */
2006-01-13 18:02:15 +03:00
mutex_unlock ( & ctrl - > crit_sect ) ;
2009-03-31 19:23:31 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
set_SOGO ( ctrl ) ;
2009-03-31 19:23:16 +04:00
/* Wait for SOBS to be unset */
2005-04-17 02:20:36 +04:00
wait_for_ctrl_irq ( ctrl ) ;
2009-03-31 19:23:16 +04:00
/* Done with exclusive hardware access */
2006-01-13 18:02:15 +03:00
mutex_unlock ( & ctrl - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:31 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* set_attention_status - Turns the Amber LED for a slot on or off
2007-11-28 20:04:30 +03:00
* @ hotplug_slot : slot to change LED on
* @ status : LED control flag
2005-04-17 02:20:36 +04:00
*/
static int set_attention_status ( struct hotplug_slot * hotplug_slot , u8 status )
{
struct pci_func * slot_func ;
struct slot * slot = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
u8 bus ;
u8 devfn ;
u8 device ;
u8 function ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
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 = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
u8 bus ;
u8 devfn ;
u8 device ;
u8 function ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
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 = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
u8 bus ;
u8 devfn ;
u8 device ;
u8 function ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
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 ;
2008-03-04 06:09:46 +03:00
dbg ( " In %s, slot_func = %p, ctrl = %p \n " , __func__ , slot_func , ctrl ) ;
2005-04-17 02:20:36 +04:00
return cpqhp_process_SS ( ctrl , slot_func ) ;
}
static int hardware_test ( struct hotplug_slot * hotplug_slot , u32 value )
{
struct slot * slot = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:11 +04:00
return cpqhp_hardware_test ( ctrl , value ) ;
2005-04-17 02:20:36 +04:00
}
static int get_power_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
* value = get_slot_enabled ( ctrl , slot ) ;
return 0 ;
}
static int get_attention_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
2009-03-31 19:23:11 +04:00
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
* value = cpq_get_attention_status ( ctrl , slot ) ;
return 0 ;
}
static int get_latch_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
* value = cpq_get_latch_status ( ctrl , slot ) ;
return 0 ;
}
static int get_adapter_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
struct controller * ctrl = slot - > ctrl ;
2008-10-21 03:41:22 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
* value = get_presence_status ( ctrl , slot ) ;
return 0 ;
}
2009-03-31 19:23:26 +04:00
static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
. 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 ,
} ;
# define SLOT_NAME_SIZE 10
static int ctrl_slot_setup ( struct controller * ctrl ,
void __iomem * smbios_start ,
void __iomem * smbios_table )
{
struct slot * slot ;
struct hotplug_slot * hotplug_slot ;
struct hotplug_slot_info * hotplug_slot_info ;
2009-12-13 16:11:32 +03:00
struct pci_bus * bus = ctrl - > pci_bus ;
2009-03-31 19:23:26 +04:00
u8 number_of_slots ;
u8 slot_device ;
u8 slot_number ;
u8 ctrl_slot ;
u32 tempdword ;
char name [ SLOT_NAME_SIZE ] ;
void __iomem * slot_entry = NULL ;
int result = - ENOMEM ;
dbg ( " %s \n " , __func__ ) ;
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 ) {
slot = kzalloc ( sizeof ( * slot ) , GFP_KERNEL ) ;
if ( ! slot )
goto error ;
slot - > hotplug_slot = kzalloc ( sizeof ( * ( slot - > hotplug_slot ) ) ,
GFP_KERNEL ) ;
if ( ! slot - > hotplug_slot )
goto error_slot ;
hotplug_slot = slot - > hotplug_slot ;
2009-03-31 19:23:31 +04:00
hotplug_slot - > info = kzalloc ( sizeof ( * ( hotplug_slot - > info ) ) ,
2009-03-31 19:23:26 +04:00
GFP_KERNEL ) ;
if ( ! hotplug_slot - > info )
goto error_hpslot ;
hotplug_slot_info = hotplug_slot - > info ;
slot - > ctrl = ctrl ;
slot - > bus = ctrl - > bus ;
slot - > device = slot_device ;
slot - > number = slot_number ;
dbg ( " slot->number = %u \n " , slot - > number ) ;
slot_entry = get_SMBIOS_entry ( smbios_start , smbios_table , 9 ,
slot_entry ) ;
while ( slot_entry & & ( readw ( slot_entry + SMBIOS_SLOT_NUMBER ) ! =
slot - > number ) ) {
slot_entry = get_SMBIOS_entry ( smbios_start ,
smbios_table , 9 , slot_entry ) ;
}
slot - > p_sm_slot = slot_entry ;
init_timer ( & slot - > task_event ) ;
slot - > task_event . expires = jiffies + 5 * HZ ;
slot - > task_event . function = cpqhp_pushbutton_thread ;
/*FIXME: these capabilities aren't used but if they are
* they need to be correctly implemented
*/
slot - > capabilities | = PCISLOT_REPLACE_SUPPORTED ;
slot - > capabilities | = PCISLOT_INTERLOCK_SUPPORTED ;
if ( is_slot64bit ( slot ) )
slot - > capabilities | = PCISLOT_64_BIT_SUPPORTED ;
if ( is_slot66mhz ( slot ) )
slot - > capabilities | = PCISLOT_66_MHZ_SUPPORTED ;
2009-12-13 16:11:32 +03:00
if ( bus - > cur_bus_speed = = PCI_SPEED_66MHz )
2009-03-31 19:23:26 +04:00
slot - > capabilities | = PCISLOT_66_MHZ_OPERATION ;
ctrl_slot =
slot_device - ( readb ( ctrl - > hpc_reg + SLOT_MASK ) > > 4 ) ;
/* Check presence */
slot - > capabilities | =
( ( ( ( ~ tempdword ) > > 23 ) |
( ( ~ tempdword ) > > 15 ) ) > > ctrl_slot ) & 0x02 ;
/* Check the switch state */
slot - > capabilities | =
( ( ~ tempdword & 0xFF ) > > ctrl_slot ) & 0x01 ;
/* Check the slot enable */
slot - > capabilities | =
( ( read_slot_enable ( ctrl ) < < 2 ) > > ctrl_slot ) & 0x04 ;
/* register this slot with the hotplug pci core */
hotplug_slot - > release = & release_slot ;
hotplug_slot - > private = slot ;
snprintf ( name , SLOT_NAME_SIZE , " %u " , slot - > number ) ;
hotplug_slot - > ops = & cpqphp_hotplug_slot_ops ;
hotplug_slot_info - > power_status = get_slot_enabled ( ctrl , slot ) ;
hotplug_slot_info - > attention_status =
cpq_get_attention_status ( ctrl , slot ) ;
hotplug_slot_info - > latch_status =
cpq_get_latch_status ( ctrl , slot ) ;
hotplug_slot_info - > adapter_status =
get_presence_status ( ctrl , slot ) ;
dbg ( " registering bus %d, dev %d, number %d, "
" ctrl->slot_device_offset %d, slot %d \n " ,
slot - > bus , slot - > device ,
slot - > number , ctrl - > slot_device_offset ,
slot_number ) ;
result = pci_hp_register ( hotplug_slot ,
ctrl - > pci_dev - > bus ,
slot - > device ,
name ) ;
if ( result ) {
err ( " pci_hp_register failed with error %d \n " , result ) ;
goto error_info ;
}
slot - > next = ctrl - > slot ;
ctrl - > slot = slot ;
number_of_slots - - ;
slot_device + + ;
slot_number + + ;
}
return 0 ;
error_info :
kfree ( hotplug_slot_info ) ;
error_hpslot :
kfree ( hotplug_slot ) ;
error_slot :
kfree ( slot ) ;
error :
return result ;
}
static int one_time_init ( void )
{
int loop ;
int retval = 0 ;
if ( initialized )
return 0 ;
power_mode = 0 ;
2009-03-31 19:24:02 +04:00
retval = init_cpqhp_routing_table ( ) ;
2009-03-31 19:23:26 +04:00
if ( retval )
goto error ;
2009-03-31 19:24:02 +04:00
if ( cpqhp_debug )
pci_print_IRQ_route ( ) ;
2009-03-31 19:23:26 +04:00
dbg ( " Initialize + Start the notification mechanism \n " ) ;
retval = cpqhp_event_start_thread ( ) ;
if ( retval )
goto error ;
dbg ( " Initialize slot lists \n " ) ;
2009-03-31 19:23:31 +04:00
for ( loop = 0 ; loop < 256 ; loop + + )
2009-03-31 19:23:26 +04:00
cpqhp_slot_list [ loop ] = NULL ;
/* FIXME: We also need to hook the NMI handler eventually.
* this also needs to be worked with Christoph
* register_NMI_handler ( ) ;
*/
/* Map rom address */
cpqhp_rom_start = ioremap ( ROM_PHY_ADDR , ROM_PHY_LEN ) ;
if ( ! cpqhp_rom_start ) {
err ( " Could not ioremap memory region for ROM \n " ) ;
retval = - EIO ;
goto error ;
}
/* Now, map the int15 entry point if we are on compaq specific
* hardware
*/
compaq_nvram_init ( cpqhp_rom_start ) ;
/* Map smbios table entry point structure */
smbios_table = detect_SMBIOS_pointer ( cpqhp_rom_start ,
cpqhp_rom_start + ROM_PHY_LEN ) ;
if ( ! smbios_table ) {
err ( " Could not find the SMBIOS pointer in memory \n " ) ;
retval = - EIO ;
goto error_rom_start ;
}
smbios_start = ioremap ( readl ( smbios_table + ST_ADDRESS ) ,
readw ( smbios_table + ST_LENGTH ) ) ;
if ( ! smbios_start ) {
err ( " Could not ioremap memory region taken from SMBIOS values \n " ) ;
retval = - EIO ;
goto error_smbios_start ;
}
initialized = 1 ;
return retval ;
error_smbios_start :
iounmap ( smbios_start ) ;
error_rom_start :
iounmap ( cpqhp_rom_start ) ;
error :
return retval ;
}
2005-04-17 02:20:36 +04:00
static int cpqhpc_probe ( struct pci_dev * pdev , const struct pci_device_id * ent )
{
u8 num_of_slots = 0 ;
u8 hp_slot = 0 ;
u8 device ;
u8 bus_cap ;
u16 temp_word ;
u16 vendor_id ;
u16 subsystem_vid ;
u16 subsystem_deviceid ;
u32 rc ;
struct controller * ctrl ;
struct pci_func * func ;
2009-12-13 16:11:32 +03:00
struct pci_bus * bus ;
2005-09-29 00:03:08 +04:00
int err ;
err = pci_enable_device ( pdev ) ;
if ( err ) {
printk ( KERN_ERR MY_NAME " : cannot enable PCI device %s (%d) \n " ,
pci_name ( pdev ) , err ) ;
return err ;
}
2010-06-10 00:31:13 +04:00
2009-12-13 16:11:32 +03:00
bus = pdev - > subordinate ;
2010-06-10 00:31:13 +04:00
if ( ! bus ) {
dev_notice ( & pdev - > dev , " the device is not a bridge, "
" skipping \n " ) ;
rc = - ENODEV ;
goto err_disable_device ;
}
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:16 +04:00
/* Need to read VID early b/c it's used to differentiate CPQ and INTC
* discovery
*/
2005-04-17 02:20:36 +04:00
rc = pci_read_config_word ( pdev , PCI_VENDOR_ID , & vendor_id ) ;
if ( rc | | ( ( vendor_id ! = PCI_VENDOR_ID_COMPAQ ) & & ( vendor_id ! = PCI_VENDOR_ID_INTEL ) ) ) {
err ( msg_HPC_non_compaq_or_intel ) ;
2005-09-29 00:03:08 +04:00
rc = - ENODEV ;
goto err_disable_device ;
2005-04-17 02:20:36 +04:00
}
dbg ( " Vendor ID: %x \n " , vendor_id ) ;
2007-06-09 02:46:36 +04:00
dbg ( " revision: %d \n " , pdev - > revision ) ;
if ( ( vendor_id = = PCI_VENDOR_ID_COMPAQ ) & & ( ! pdev - > revision ) ) {
2005-04-17 02:20:36 +04:00
err ( msg_HPC_rev_error ) ;
2005-09-29 00:03:08 +04:00
rc = - ENODEV ;
goto err_disable_device ;
2005-04-17 02:20:36 +04:00
}
2010-03-16 13:47:56 +03:00
/* Check for the proper subsystem ID's
2009-03-31 19:23:11 +04:00
* Intel uses a different SSID programming model than Compaq .
2005-04-17 02:20:36 +04:00
* For Intel , each SSID bit identifies a PHP capability .
* Also Intel HPC ' s may have RID = 0.
*/
2009-03-31 19:23:36 +04:00
if ( ( pdev - > revision < = 2 ) & & ( vendor_id ! = PCI_VENDOR_ID_INTEL ) ) {
err ( msg_HPC_not_supported ) ;
return - ENODEV ;
}
/* TODO: This code can be made to support non-Compaq or Intel
* subsystem IDs
*/
2011-07-13 19:21:25 +04:00
subsystem_vid = pdev - > subsystem_vendor ;
2009-03-31 19:23:36 +04:00
dbg ( " Subsystem Vendor ID: %x \n " , subsystem_vid ) ;
if ( ( subsystem_vid ! = PCI_VENDOR_ID_COMPAQ ) & & ( subsystem_vid ! = PCI_VENDOR_ID_INTEL ) ) {
err ( msg_HPC_non_compaq_or_intel ) ;
rc = - ENODEV ;
goto err_disable_device ;
}
ctrl = kzalloc ( sizeof ( struct controller ) , GFP_KERNEL ) ;
if ( ! ctrl ) {
err ( " %s : out of memory \n " , __func__ ) ;
rc = - ENOMEM ;
goto err_disable_device ;
}
2011-07-13 19:21:25 +04:00
subsystem_deviceid = pdev - > subsystem_device ;
2009-03-31 19:23:36 +04:00
info ( " Hot Plug Subsystem Device ID: %x \n " , subsystem_deviceid ) ;
/* Set Vendor ID, so it can be accessed later from other
* functions
*/
ctrl - > vendor_id = vendor_id ;
switch ( subsystem_vid ) {
case PCI_VENDOR_ID_COMPAQ :
if ( pdev - > revision > = 0x13 ) { /* CIOBX */
ctrl - > push_flag = 1 ;
ctrl - > slot_switch_type = 1 ;
ctrl - > push_button = 1 ;
ctrl - > pci_config_space = 1 ;
ctrl - > defeature_PHP = 1 ;
ctrl - > pcix_support = 1 ;
ctrl - > pcix_speed_capability = 1 ;
pci_read_config_byte ( pdev , 0x41 , & bus_cap ) ;
if ( bus_cap & 0x80 ) {
dbg ( " bus max supports 133MHz PCI-X \n " ) ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_133MHz_PCIX ;
2009-03-31 19:23:36 +04:00
break ;
}
if ( bus_cap & 0x40 ) {
dbg ( " bus max supports 100MHz PCI-X \n " ) ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_100MHz_PCIX ;
2009-03-31 19:23:36 +04:00
break ;
}
if ( bus_cap & 20 ) {
dbg ( " bus max supports 66MHz PCI-X \n " ) ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_66MHz_PCIX ;
2009-03-31 19:23:36 +04:00
break ;
}
if ( bus_cap & 10 ) {
dbg ( " bus max supports 66MHz PCI \n " ) ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_66MHz ;
2009-03-31 19:23:36 +04:00
break ;
}
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:36 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2009-03-31 19:23:36 +04:00
switch ( subsystem_deviceid ) {
case PCI_SUB_HPC_ID :
/* Original 6500/7000 implementation */
ctrl - > slot_switch_type = 1 ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_33MHz ;
2009-03-31 19:23:36 +04:00
ctrl - > push_button = 0 ;
ctrl - > pci_config_space = 1 ;
ctrl - > defeature_PHP = 1 ;
ctrl - > pcix_support = 0 ;
ctrl - > pcix_speed_capability = 0 ;
break ;
case PCI_SUB_HPC_ID2 :
/* First Pushbutton implementation */
ctrl - > push_flag = 1 ;
ctrl - > slot_switch_type = 1 ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_33MHz ;
2009-03-31 19:23:36 +04:00
ctrl - > push_button = 1 ;
ctrl - > pci_config_space = 1 ;
ctrl - > defeature_PHP = 1 ;
ctrl - > pcix_support = 0 ;
ctrl - > pcix_speed_capability = 0 ;
break ;
case PCI_SUB_HPC_ID_INTC :
/* Third party (6500/7000) */
ctrl - > slot_switch_type = 1 ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_33MHz ;
2009-03-31 19:23:36 +04:00
ctrl - > push_button = 0 ;
ctrl - > pci_config_space = 1 ;
ctrl - > defeature_PHP = 1 ;
ctrl - > pcix_support = 0 ;
ctrl - > pcix_speed_capability = 0 ;
break ;
case PCI_SUB_HPC_ID3 :
/* First 66 Mhz implementation */
ctrl - > push_flag = 1 ;
ctrl - > slot_switch_type = 1 ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_66MHz ;
2009-03-31 19:23:36 +04:00
ctrl - > push_button = 1 ;
ctrl - > pci_config_space = 1 ;
ctrl - > defeature_PHP = 1 ;
ctrl - > pcix_support = 0 ;
ctrl - > pcix_speed_capability = 0 ;
break ;
case PCI_SUB_HPC_ID4 :
/* First PCI-X implementation, 100MHz */
ctrl - > push_flag = 1 ;
ctrl - > slot_switch_type = 1 ;
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_100MHz_PCIX ;
2009-03-31 19:23:36 +04:00
ctrl - > push_button = 1 ;
ctrl - > pci_config_space = 1 ;
ctrl - > defeature_PHP = 1 ;
ctrl - > pcix_support = 1 ;
ctrl - > pcix_speed_capability = 0 ;
break ;
default :
err ( msg_HPC_not_supported ) ;
rc = - ENODEV ;
2005-04-17 02:20:36 +04:00
goto err_free_ctrl ;
}
2009-03-31 19:23:36 +04:00
break ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:36 +04:00
case PCI_VENDOR_ID_INTEL :
/* Check for speed capability (0=33, 1=66) */
if ( subsystem_deviceid & 0x0001 )
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_66MHz ;
2009-03-31 19:23:36 +04:00
else
2009-12-13 16:11:32 +03:00
bus - > max_bus_speed = PCI_SPEED_33MHz ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:36 +04:00
/* Check for push button */
if ( subsystem_deviceid & 0x0002 )
ctrl - > push_button = 0 ;
else
ctrl - > push_button = 1 ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:36 +04:00
/* Check for slot switch type (0=mechanical, 1=not mechanical) */
if ( subsystem_deviceid & 0x0004 )
ctrl - > slot_switch_type = 0 ;
else
ctrl - > slot_switch_type = 1 ;
/* PHP Status (0=De-feature PHP, 1=Normal operation) */
if ( subsystem_deviceid & 0x0008 )
ctrl - > defeature_PHP = 1 ; /* PHP supported */
else
ctrl - > defeature_PHP = 0 ; /* PHP not supported */
/* Alternate Base Address Register Interface
* ( 0 = not supported , 1 = supported )
*/
if ( subsystem_deviceid & 0x0010 )
ctrl - > alternate_base_address = 1 ;
else
ctrl - > alternate_base_address = 0 ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:36 +04:00
/* PCI Config Space Index (0=not supported, 1=supported) */
if ( subsystem_deviceid & 0x0020 )
ctrl - > pci_config_space = 1 ;
else
ctrl - > pci_config_space = 0 ;
/* PCI-X support */
if ( subsystem_deviceid & 0x0080 ) {
ctrl - > pcix_support = 1 ;
if ( subsystem_deviceid & 0x0040 )
/* 133MHz PCI-X if bit 7 is 1 */
ctrl - > pcix_speed_capability = 1 ;
else
/* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */
/* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */
ctrl - > pcix_speed_capability = 0 ;
} else {
/* Conventional PCI */
ctrl - > pcix_support = 0 ;
ctrl - > pcix_speed_capability = 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-31 19:23:36 +04:00
break ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:36 +04:00
default :
2005-04-17 02:20:36 +04:00
err ( msg_HPC_not_supported ) ;
2009-03-31 19:23:36 +04:00
rc = - ENODEV ;
goto err_free_ctrl ;
2005-04-17 02:20:36 +04:00
}
2009-03-31 19:23:16 +04:00
/* Tell the user that we found one. */
2005-04-17 02:20:36 +04:00
info ( " Initializing the PCI hot plug controller residing on PCI bus %d \n " ,
pdev - > bus - > number ) ;
dbg ( " Hotplug controller capabilities: \n " ) ;
2009-12-13 16:11:32 +03:00
dbg ( " speed_capability %d \n " , bus - > max_bus_speed ) ;
2005-04-17 02:20:36 +04:00
dbg ( " slot_switch_type %s \n " , ctrl - > slot_switch_type ?
" switch present " : " no switch " ) ;
dbg ( " defeature_PHP %s \n " , ctrl - > defeature_PHP ?
" PHP supported " : " PHP not supported " ) ;
dbg ( " alternate_base_address %s \n " , ctrl - > alternate_base_address ?
" supported " : " not supported " ) ;
dbg ( " pci_config_space %s \n " , ctrl - > pci_config_space ?
" supported " : " not supported " ) ;
dbg ( " pcix_speed_capability %s \n " , ctrl - > pcix_speed_capability ?
" supported " : " not supported " ) ;
dbg ( " pcix_support %s \n " , ctrl - > pcix_support ?
" supported " : " not supported " ) ;
ctrl - > pci_dev = pdev ;
pci_set_drvdata ( pdev , ctrl ) ;
/* make our own copy of the pci bus structure,
* as we like tweaking it a lot */
2010-05-16 01:18:16 +04:00
ctrl - > pci_bus = kmemdup ( pdev - > bus , sizeof ( * ctrl - > pci_bus ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ctrl - > pci_bus ) {
err ( " out of memory \n " ) ;
rc = - ENOMEM ;
goto err_free_ctrl ;
}
ctrl - > bus = pdev - > bus - > number ;
2007-06-09 02:46:36 +04:00
ctrl - > rev = pdev - > revision ;
2005-04-17 02:20:36 +04:00
dbg ( " bus device function rev: %d %d %d %d \n " , ctrl - > bus ,
PCI_SLOT ( pdev - > devfn ) , PCI_FUNC ( pdev - > devfn ) , ctrl - > rev ) ;
2006-01-13 18:02:15 +03:00
mutex_init ( & ctrl - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & ctrl - > queue ) ;
/* initialize our threads if they haven't already been started up */
rc = one_time_init ( ) ;
if ( rc ) {
goto err_free_bus ;
}
2009-03-31 19:23:11 +04:00
2005-04-17 02:20:36 +04:00
dbg ( " pdev = %p \n " , pdev ) ;
2006-06-13 02:14:29 +04:00
dbg ( " pci resource start %llx \n " , ( unsigned long long ) pci_resource_start ( pdev , 0 ) ) ;
dbg ( " pci resource len %llx \n " , ( unsigned long long ) pci_resource_len ( pdev , 0 ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! request_mem_region ( pci_resource_start ( pdev , 0 ) ,
pci_resource_len ( pdev , 0 ) , MY_NAME ) ) {
err ( " cannot reserve MMIO region \n " ) ;
rc = - ENOMEM ;
goto err_free_bus ;
}
ctrl - > hpc_reg = ioremap ( pci_resource_start ( pdev , 0 ) ,
pci_resource_len ( pdev , 0 ) ) ;
if ( ! ctrl - > hpc_reg ) {
2006-06-13 02:14:29 +04:00
err ( " cannot remap MMIO region %llx @ %llx \n " ,
( unsigned long long ) pci_resource_len ( pdev , 0 ) ,
( unsigned long long ) pci_resource_start ( pdev , 0 ) ) ;
2005-04-17 02:20:36 +04:00
rc = - ENODEV ;
goto err_free_mem_region ;
}
2009-03-31 19:23:36 +04:00
/* Check for 66Mhz operation */
2009-12-13 16:11:32 +03:00
bus - > cur_bus_speed = get_controller_speed ( ctrl ) ;
2005-04-17 02:20:36 +04:00
/********************************************************
*
* Save configuration headers for this and
* subordinate PCI buses
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-03-31 19:23:16 +04:00
/* find the physical slot number of the first hot plug slot */
2005-04-17 02:20:36 +04:00
/* Get slot won't work for devices behind bridges, but
* in this case it will always be called for the " base "
* bus / dev / func of a slot .
* CS : this is leveraging the PCIIRQ routing code from the kernel
* ( pci - pc . c : get_irq_routing_table ) */
rc = get_slot_mapping ( ctrl - > pci_bus , pdev - > bus - > number ,
( readb ( ctrl - > hpc_reg + SLOT_MASK ) > > 4 ) ,
& ( ctrl - > first_slot ) ) ;
dbg ( " get_slot_mapping: first_slot = %d, returned = %d \n " ,
ctrl - > first_slot , rc ) ;
if ( rc ) {
err ( msg_initialization_err , rc ) ;
goto err_iounmap ;
}
2009-03-31 19:23:16 +04:00
/* Store PCI Config Space for all devices on this bus */
2005-04-17 02:20:36 +04:00
rc = cpqhp_save_config ( ctrl , ctrl - > bus , readb ( ctrl - > hpc_reg + SLOT_MASK ) ) ;
if ( rc ) {
err ( " %s: unable to save PCI configuration data, error %d \n " ,
2008-03-04 06:09:46 +03:00
__func__ , rc ) ;
2005-04-17 02:20:36 +04:00
goto err_iounmap ;
}
/*
* Get IO , memory , and IRQ resources for new devices
*/
2009-03-31 19:23:16 +04:00
/* The next line is required for cpqhp_find_available_resources */
2005-04-17 02:20:36 +04:00
ctrl - > interrupt = pdev - > irq ;
if ( ctrl - > interrupt < 0x10 ) {
cpqhp_legacy_mode = 1 ;
dbg ( " System seems to be configured for Full Table Mapped MPS mode \n " ) ;
}
ctrl - > cfgspc_irq = 0 ;
pci_read_config_byte ( pdev , PCI_INTERRUPT_LINE , & ctrl - > cfgspc_irq ) ;
rc = cpqhp_find_available_resources ( ctrl , cpqhp_rom_start ) ;
ctrl - > add_support = ! rc ;
if ( rc ) {
dbg ( " cpqhp_find_available_resources = 0x%x \n " , rc ) ;
err ( " unable to locate PCI configuration resources for hot plug add. \n " ) ;
goto err_iounmap ;
}
/*
* Finish setting up the hot plug ctrl device
*/
ctrl - > slot_device_offset = readb ( ctrl - > hpc_reg + SLOT_MASK ) > > 4 ;
dbg ( " NumSlots %d \n " , ctrl - > slot_device_offset ) ;
ctrl - > next_event = 0 ;
/* Setup the slot information structures */
rc = ctrl_slot_setup ( ctrl , smbios_start , smbios_table ) ;
if ( rc ) {
err ( msg_initialization_err , 6 ) ;
err ( " %s: unable to save PCI configuration data, error %d \n " ,
2008-03-04 06:09:46 +03:00
__func__ , rc ) ;
2005-04-17 02:20:36 +04:00
goto err_iounmap ;
}
2009-03-31 19:23:11 +04:00
2005-04-17 02:20:36 +04:00
/* Mask all general input interrupts */
writel ( 0xFFFFFFFFL , ctrl - > hpc_reg + INT_MASK ) ;
/* set up the interrupt */
dbg ( " HPC interrupt = %d \n " , ctrl - > interrupt ) ;
if ( request_irq ( ctrl - > interrupt , cpqhp_ctrl_intr ,
2006-07-02 06:29:41 +04:00
IRQF_SHARED , MY_NAME , ctrl ) ) {
2005-04-17 02:20:36 +04:00
err ( " Can't get irq %d for the hotplug pci controller \n " ,
ctrl - > interrupt ) ;
rc = - ENODEV ;
goto err_iounmap ;
}
2009-03-31 19:23:16 +04:00
/* Enable Shift Out interrupt and clear it, also enable SERR on power
* fault
*/
2005-04-17 02:20:36 +04:00
temp_word = readw ( ctrl - > hpc_reg + MISC ) ;
temp_word | = 0x4006 ;
writew ( temp_word , ctrl - > hpc_reg + MISC ) ;
2009-03-31 19:23:16 +04:00
/* Changed 05/05/97 to clear all interrupts at start */
2005-04-17 02:20:36 +04:00
writel ( 0xFFFFFFFFL , ctrl - > hpc_reg + INT_INPUT_CLEAR ) ;
ctrl - > ctrl_int_comp = readl ( ctrl - > hpc_reg + INT_INPUT_CLEAR ) ;
writel ( 0x0L , ctrl - > hpc_reg + INT_MASK ) ;
if ( ! cpqhp_ctrl_list ) {
cpqhp_ctrl_list = ctrl ;
ctrl - > next = NULL ;
} else {
ctrl - > next = cpqhp_ctrl_list ;
cpqhp_ctrl_list = ctrl ;
}
2009-03-31 19:23:16 +04:00
/* turn off empty slots here unless command line option "ON" set
* Wait for exclusive access to hardware
*/
2006-01-13 18:02:15 +03:00
mutex_lock ( & ctrl - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
num_of_slots = readb ( ctrl - > hpc_reg + SLOT_MASK ) & 0x0F ;
2009-03-31 19:23:16 +04:00
/* find first device number for the ctrl */
2005-04-17 02:20:36 +04:00
device = readb ( ctrl - > hpc_reg + SLOT_MASK ) > > 4 ;
while ( num_of_slots ) {
dbg ( " num_of_slots: %d \n " , num_of_slots ) ;
func = cpqhp_slot_find ( ctrl - > bus , device , 0 ) ;
if ( ! func )
break ;
hp_slot = func - > device - ctrl - > slot_device_offset ;
dbg ( " hp_slot: %d \n " , hp_slot ) ;
2009-03-31 19:23:16 +04:00
/* We have to save the presence info for these slots */
2005-04-17 02:20:36 +04:00
temp_word = ctrl - > ctrl_int_comp > > 16 ;
func - > presence_save = ( temp_word > > hp_slot ) & 0x01 ;
func - > presence_save | = ( temp_word > > ( hp_slot + 7 ) ) & 0x02 ;
2009-03-31 19:23:31 +04:00
if ( ctrl - > ctrl_int_comp & ( 0x1L < < hp_slot ) )
2005-04-17 02:20:36 +04:00
func - > switch_save = 0 ;
2009-03-31 19:23:31 +04:00
else
2005-04-17 02:20:36 +04:00
func - > switch_save = 0x10 ;
2009-03-31 19:23:31 +04:00
if ( ! power_mode )
2005-04-17 02:20:36 +04:00
if ( ! func - > is_a_board ) {
green_LED_off ( ctrl , hp_slot ) ;
slot_disable ( ctrl , hp_slot ) ;
}
device + + ;
num_of_slots - - ;
}
if ( ! power_mode ) {
set_SOGO ( ctrl ) ;
2009-03-31 19:23:16 +04:00
/* Wait for SOBS to be unset */
2005-04-17 02:20:36 +04:00
wait_for_ctrl_irq ( ctrl ) ;
}
rc = init_SERR ( ctrl ) ;
if ( rc ) {
err ( " init_SERR failed \n " ) ;
2006-01-13 18:02:15 +03:00
mutex_unlock ( & ctrl - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
goto err_free_irq ;
}
2009-03-31 19:23:16 +04:00
/* Done with exclusive hardware access */
2006-01-13 18:02:15 +03:00
mutex_unlock ( & ctrl - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
2005-12-14 20:37:26 +03:00
cpqhp_create_debugfs_files ( ctrl ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
err_free_irq :
free_irq ( ctrl - > interrupt , ctrl ) ;
err_iounmap :
iounmap ( ctrl - > hpc_reg ) ;
err_free_mem_region :
release_mem_region ( pci_resource_start ( pdev , 0 ) , pci_resource_len ( pdev , 0 ) ) ;
err_free_bus :
kfree ( ctrl - > pci_bus ) ;
err_free_ctrl :
kfree ( ctrl ) ;
2005-09-29 00:03:08 +04:00
err_disable_device :
pci_disable_device ( pdev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static void __exit unload_cpqphpd ( void )
{
struct pci_func * next ;
struct pci_func * TempSlot ;
int loop ;
u32 rc ;
struct controller * ctrl ;
struct controller * tctrl ;
struct pci_resource * res ;
struct pci_resource * tres ;
rc = compaq_nvram_store ( cpqhp_rom_start ) ;
ctrl = cpqhp_ctrl_list ;
while ( ctrl ) {
if ( ctrl - > hpc_reg ) {
u16 misc ;
rc = read_slot_enable ( ctrl ) ;
2009-03-31 19:23:11 +04:00
2005-04-17 02:20:36 +04:00
writeb ( 0 , ctrl - > hpc_reg + SLOT_SERR ) ;
writel ( 0xFFFFFFC0L | ~ rc , ctrl - > hpc_reg + INT_MASK ) ;
2009-03-31 19:23:11 +04:00
2005-04-17 02:20:36 +04:00
misc = readw ( ctrl - > hpc_reg + MISC ) ;
misc & = 0xFFFD ;
writew ( misc , ctrl - > hpc_reg + MISC ) ;
}
ctrl_slot_cleanup ( ctrl ) ;
res = ctrl - > io_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
res = ctrl - > mem_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
res = ctrl - > p_mem_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
res = ctrl - > bus_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
kfree ( ctrl - > pci_bus ) ;
tctrl = ctrl ;
ctrl = ctrl - > next ;
kfree ( tctrl ) ;
}
for ( loop = 0 ; loop < 256 ; loop + + ) {
next = cpqhp_slot_list [ loop ] ;
while ( next ! = NULL ) {
res = next - > io_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
res = next - > mem_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
res = next - > p_mem_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
res = next - > bus_head ;
while ( res ) {
tres = res ;
res = res - > next ;
kfree ( tres ) ;
}
TempSlot = next ;
next = next - > next ;
kfree ( TempSlot ) ;
}
}
2009-03-31 19:23:16 +04:00
/* Stop the notification mechanism */
2005-06-02 14:42:37 +04:00
if ( initialized )
cpqhp_event_stop_thread ( ) ;
2005-04-17 02:20:36 +04:00
2009-03-31 19:23:16 +04:00
/* unmap the rom address */
2005-04-17 02:20:36 +04:00
if ( cpqhp_rom_start )
iounmap ( cpqhp_rom_start ) ;
if ( smbios_start )
iounmap ( smbios_start ) ;
}
static struct pci_device_id hpcd_pci_tbl [ ] = {
{
/* handle any PCI Hotplug controller */
. class = ( ( PCI_CLASS_SYSTEM_PCI_HOTPLUG < < 8 ) | 0x00 ) ,
. class_mask = ~ 0 ,
2009-03-31 19:23:11 +04:00
2005-04-17 02:20:36 +04:00
/* no matter who makes it */
. vendor = PCI_ANY_ID ,
. device = PCI_ANY_ID ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
2009-03-31 19:23:11 +04:00
2005-04-17 02:20:36 +04:00
} , { /* end: all zeroes */ }
} ;
MODULE_DEVICE_TABLE ( pci , hpcd_pci_tbl ) ;
static struct pci_driver cpqhpc_driver = {
. name = " compaq_pci_hotplug " ,
. id_table = hpcd_pci_tbl ,
. probe = cpqhpc_probe ,
/* remove: cpqhpc_remove_one, */
} ;
static int __init cpqhpc_init ( void )
{
int result ;
cpqhp_debug = debug ;
info ( DRIVER_DESC " version: " DRIVER_VERSION " \n " ) ;
2005-12-14 20:37:26 +03:00
cpqhp_initialize_debugfs ( ) ;
2005-04-17 02:20:36 +04:00
result = pci_register_driver ( & cpqhpc_driver ) ;
dbg ( " pci_register_driver = %d \n " , result ) ;
return result ;
}
static void __exit cpqhpc_cleanup ( void )
{
dbg ( " unload_cpqphpd() \n " ) ;
unload_cpqphpd ( ) ;
dbg ( " pci_unregister_driver \n " ) ;
pci_unregister_driver ( & cpqhpc_driver ) ;
2005-12-14 20:37:26 +03:00
cpqhp_shutdown_debugfs ( ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( cpqhpc_init ) ;
module_exit ( cpqhpc_cleanup ) ;