2005-04-16 15:20:36 -07:00
/*
* Standard 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 .
* Copyright ( C ) 2003 - 2004 Intel Corporation
*
* 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 .
*
2005-08-16 15:16:10 -07:00
* Send feedback to < greg @ kroah . com > , < kristen . c . accardi @ intel . com >
2005-04-16 15:20:36 -07:00
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/pci.h>
2006-02-21 15:45:45 -08:00
# include <linux/workqueue.h>
2005-10-13 12:05:36 -07:00
# include "../pci.h"
2005-04-16 15:20:36 -07:00
# include "shpchp.h"
2006-11-22 14:57:56 +00:00
static void interrupt_event_handler ( struct work_struct * work ) ;
2006-02-21 15:45:48 -08:00
static int shpchp_enable_slot ( struct slot * p_slot ) ;
static int shpchp_disable_slot ( struct slot * p_slot ) ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:45 -08:00
static int queue_interrupt_event ( struct slot * p_slot , u32 event_type )
{
struct event_info * info ;
info = kmalloc ( sizeof ( * info ) , GFP_ATOMIC ) ;
if ( ! info )
return - ENOMEM ;
info - > event_type = event_type ;
info - > p_slot = p_slot ;
2006-11-22 14:57:56 +00:00
INIT_WORK ( & info - > work , interrupt_event_handler ) ;
2006-02-21 15:45:45 -08:00
2006-02-21 15:45:48 -08:00
schedule_work ( & info - > work ) ;
2006-02-21 15:45:45 -08:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
2006-12-16 15:25:34 -08:00
u8 shpchp_handle_attention_button ( u8 hp_slot , struct controller * ctrl )
2005-04-16 15:20:36 -07:00
{
struct slot * p_slot ;
2006-02-21 15:45:45 -08:00
u32 event_type ;
2005-04-16 15:20:36 -07:00
/* Attention Button Change */
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " Attention button interrupt received \n " ) ;
2007-01-09 13:03:10 -08:00
2005-04-16 15:20:36 -07:00
p_slot = shpchp_find_slot ( ctrl , hp_slot + ctrl - > slot_device_offset ) ;
2005-10-13 12:05:41 -07:00
p_slot - > hpc_ops - > get_adapter_status ( p_slot , & ( p_slot - > presence_save ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Button pressed - See if need to TAKE ACTION ! ! !
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Button pressed on Slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
event_type = INT_BUTTON_PRESS ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:45 -08:00
queue_interrupt_event ( p_slot , event_type ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2006-12-16 15:25:34 -08:00
u8 shpchp_handle_switch_change ( u8 hp_slot , struct controller * ctrl )
2005-04-16 15:20:36 -07:00
{
struct slot * p_slot ;
u8 getstatus ;
2006-02-21 15:45:45 -08:00
u32 event_type ;
2005-04-16 15:20:36 -07:00
/* Switch Change */
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " Switch interrupt received \n " ) ;
2005-04-16 15:20:36 -07:00
p_slot = shpchp_find_slot ( ctrl , hp_slot + ctrl - > slot_device_offset ) ;
2005-10-13 12:05:41 -07:00
p_slot - > hpc_ops - > get_adapter_status ( p_slot , & ( p_slot - > presence_save ) ) ;
2005-04-16 15:20:36 -07:00
p_slot - > hpc_ops - > get_latch_status ( p_slot , & getstatus ) ;
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " Card present %x Power status %x \n " ,
p_slot - > presence_save , p_slot - > pwr_save ) ;
2005-04-16 15:20:36 -07:00
if ( getstatus ) {
/*
* Switch opened
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Latch open on Slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
event_type = INT_SWITCH_OPEN ;
2005-10-13 12:05:41 -07:00
if ( p_slot - > pwr_save & & p_slot - > presence_save ) {
2006-02-21 15:45:45 -08:00
event_type = INT_POWER_FAULT ;
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " Surprise Removal of card \n " ) ;
2005-04-16 15:20:36 -07:00
}
} else {
/*
* Switch closed
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Latch close on Slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
event_type = INT_SWITCH_CLOSE ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:45 -08:00
queue_interrupt_event ( p_slot , event_type ) ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:45 -08:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
2006-12-16 15:25:34 -08:00
u8 shpchp_handle_presence_change ( u8 hp_slot , struct controller * ctrl )
2005-04-16 15:20:36 -07:00
{
struct slot * p_slot ;
2006-02-21 15:45:45 -08:00
u32 event_type ;
2005-04-16 15:20:36 -07:00
/* Presence Change */
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " Presence/Notify input change \n " ) ;
2005-04-16 15:20:36 -07:00
p_slot = shpchp_find_slot ( ctrl , hp_slot + ctrl - > slot_device_offset ) ;
2007-01-09 13:03:10 -08:00
/*
2005-04-16 15:20:36 -07:00
* Save the presence state
*/
2005-10-13 12:05:41 -07:00
p_slot - > hpc_ops - > get_adapter_status ( p_slot , & ( p_slot - > presence_save ) ) ;
if ( p_slot - > presence_save ) {
2005-04-16 15:20:36 -07:00
/*
* Card Present
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Card present on Slot(%s) \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
event_type = INT_PRESENCE_ON ;
2005-04-16 15:20:36 -07:00
} else {
/*
* Not Present
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Card not present on Slot(%s) \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
event_type = INT_PRESENCE_OFF ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:45 -08:00
queue_interrupt_event ( p_slot , event_type ) ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:45 -08:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
2006-12-16 15:25:34 -08:00
u8 shpchp_handle_power_fault ( u8 hp_slot , struct controller * ctrl )
2005-04-16 15:20:36 -07:00
{
struct slot * p_slot ;
2006-02-21 15:45:45 -08:00
u32 event_type ;
2005-04-16 15:20:36 -07:00
/* Power fault */
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " Power fault interrupt received \n " ) ;
2005-04-16 15:20:36 -07:00
p_slot = shpchp_find_slot ( ctrl , hp_slot + ctrl - > slot_device_offset ) ;
if ( ! ( p_slot - > hpc_ops - > query_power_fault ( p_slot ) ) ) {
/*
* Power fault Cleared
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Power fault cleared on Slot(%s) \n " ,
slot_name ( p_slot ) ) ;
2005-10-13 12:05:41 -07:00
p_slot - > status = 0x00 ;
2006-02-21 15:45:45 -08:00
event_type = INT_POWER_FAULT_CLEAR ;
2005-04-16 15:20:36 -07:00
} else {
/*
* Power fault
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Power fault on Slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
event_type = INT_POWER_FAULT ;
2005-04-16 15:20:36 -07:00
/* set power fault status for this board */
2005-10-13 12:05:41 -07:00
p_slot - > status = 0xFF ;
2008-10-23 11:54:39 +09:00
ctrl_info ( ctrl , " Power fault bit %x set \n " , hp_slot ) ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:45 -08:00
queue_interrupt_event ( p_slot , event_type ) ;
return 1 ;
2005-04-16 15:20:36 -07:00
}
2007-01-09 13:03:10 -08:00
/* The following routines constitute the bulk of the
2005-04-16 15:20:36 -07:00
hotplug controller logic
*/
2005-10-13 12:05:42 -07:00
static int change_bus_speed ( struct controller * ctrl , struct slot * p_slot ,
enum pci_bus_speed speed )
2007-01-09 13:03:10 -08:00
{
2005-10-13 12:05:42 -07:00
int rc = 0 ;
2005-04-16 15:20:36 -07:00
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " Change speed to %d \n " , speed ) ;
2005-04-16 15:20:36 -07:00
if ( ( rc = p_slot - > hpc_ops - > set_bus_speed_mode ( p_slot , speed ) ) ) {
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " %s: Issue of set bus speed mode command "
" failed \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return WRONG_BUS_FREQUENCY ;
}
return rc ;
}
2005-10-13 12:05:42 -07:00
static int fix_bus_speed ( struct controller * ctrl , struct slot * pslot ,
u8 flag , enum pci_bus_speed asp , enum pci_bus_speed bsp ,
enum pci_bus_speed msp )
2007-01-09 13:03:10 -08:00
{
2005-10-13 12:05:42 -07:00
int rc = 0 ;
2006-03-01 14:55:11 +09:00
/*
* If other slots on the same bus are occupied , we cannot
* change the bus speed .
*/
if ( flag ) {
if ( asp < bsp ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Speed of bus %x and adapter %x "
" mismatch \n " , bsp , asp ) ;
2006-03-01 14:55:11 +09:00
rc = WRONG_BUS_FREQUENCY ;
2005-04-16 15:20:36 -07:00
}
2006-03-01 14:55:11 +09:00
return rc ;
}
if ( asp < msp ) {
if ( bsp ! = asp )
rc = change_bus_speed ( ctrl , pslot , asp ) ;
2005-04-16 15:20:36 -07:00
} else {
2006-03-01 14:55:11 +09:00
if ( bsp ! = msp )
rc = change_bus_speed ( ctrl , pslot , msp ) ;
2005-04-16 15:20:36 -07:00
}
return rc ;
}
/**
* board_added - Called after a board has been added to the system .
2007-11-28 09:04:30 -08:00
* @ p_slot : target & slot
2005-04-16 15:20:36 -07:00
*
2007-11-28 09:04:30 -08:00
* Turns power on for the board .
* Configures board .
2005-04-16 15:20:36 -07:00
*/
2005-10-13 12:05:42 -07:00
static int board_added ( struct slot * p_slot )
2005-04-16 15:20:36 -07:00
{
u8 hp_slot ;
u8 slots_not_empty = 0 ;
2005-10-13 12:05:42 -07:00
int rc = 0 ;
2006-03-01 14:55:11 +09:00
enum pci_bus_speed asp , bsp , msp ;
2005-10-13 12:05:41 -07:00
struct controller * ctrl = p_slot - > ctrl ;
2008-10-23 11:54:39 +09:00
struct pci_bus * parent = ctrl - > pci_dev - > subordinate ;
2005-04-16 15:20:36 -07:00
2005-10-13 12:05:41 -07:00
hp_slot = p_slot - > device - ctrl - > slot_device_offset ;
2005-04-16 15:20:36 -07:00
2008-10-23 11:52:12 +09:00
ctrl_dbg ( ctrl ,
" %s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d \n " ,
__func__ , p_slot - > device , ctrl - > slot_device_offset , hp_slot ) ;
2005-04-16 15:20:36 -07:00
/* Power on slot without connecting to bus */
rc = p_slot - > hpc_ops - > power_on_slot ( p_slot ) ;
if ( rc ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Failed to power on slot \n " ) ;
2005-04-16 15:20:36 -07:00
return - 1 ;
}
2007-01-09 13:03:10 -08:00
2005-04-16 15:20:36 -07:00
if ( ( ctrl - > pci_dev - > vendor = = 0x8086 ) & & ( ctrl - > pci_dev - > device = = 0x0332 ) ) {
if ( slots_not_empty )
return WRONG_BUS_FREQUENCY ;
2007-01-09 13:03:10 -08:00
2005-04-16 15:20:36 -07:00
if ( ( rc = p_slot - > hpc_ops - > set_bus_speed_mode ( p_slot , PCI_SPEED_33MHz ) ) ) {
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " %s: Issue of set bus speed mode command "
" failed \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return WRONG_BUS_FREQUENCY ;
}
2007-01-09 13:03:10 -08:00
2005-04-16 15:20:36 -07:00
/* turn on board, blink green LED, turn off Amber LED */
if ( ( rc = p_slot - > hpc_ops - > slot_enable ( p_slot ) ) ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Issue of Slot Enable command failed \n " ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
}
2007-01-09 13:03:10 -08:00
2006-03-01 14:55:11 +09:00
rc = p_slot - > hpc_ops - > get_adapter_speed ( p_slot , & asp ) ;
if ( rc ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Can't get adapter speed or "
" bus mode mismatch \n " ) ;
2005-04-16 15:20:36 -07:00
return WRONG_BUS_FREQUENCY ;
}
2006-03-01 14:55:11 +09:00
rc = p_slot - > hpc_ops - > get_cur_bus_speed ( p_slot , & bsp ) ;
if ( rc ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Can't get bus operation speed \n " ) ;
2005-04-16 15:20:36 -07:00
return WRONG_BUS_FREQUENCY ;
}
2006-03-01 14:55:11 +09:00
rc = p_slot - > hpc_ops - > get_max_bus_speed ( p_slot , & msp ) ;
if ( rc ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Can't get max bus operation speed \n " ) ;
2006-03-01 14:55:11 +09:00
msp = bsp ;
2005-04-16 15:20:36 -07:00
}
/* Check if there are other slots or devices on the same bus */
if ( ! list_empty ( & ctrl - > pci_dev - > subordinate - > devices ) )
slots_not_empty = 1 ;
2008-10-23 11:52:12 +09:00
ctrl_dbg ( ctrl , " %s: slots_not_empty %d, adapter_speed %d, bus_speed %d, "
" max_bus_speed %d \n " , __func__ , slots_not_empty , asp ,
bsp , msp ) ;
2005-04-16 15:20:36 -07:00
2006-03-01 14:55:11 +09:00
rc = fix_bus_speed ( ctrl , p_slot , slots_not_empty , asp , bsp , msp ) ;
if ( rc )
return rc ;
2005-04-16 15:20:36 -07:00
/* turn on board, blink green LED, turn off Amber LED */
if ( ( rc = p_slot - > hpc_ops - > slot_enable ( p_slot ) ) ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Issue of Slot Enable command failed \n " ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
/* Wait for ~1 second */
2006-02-21 15:45:42 -08:00
msleep ( 1000 ) ;
2005-04-16 15:20:36 -07:00
2008-10-23 11:52:12 +09:00
ctrl_dbg ( ctrl , " %s: slot status = %x \n " , __func__ , p_slot - > status ) ;
2005-04-16 15:20:36 -07:00
/* Check for a power fault */
2005-10-13 12:05:41 -07:00
if ( p_slot - > status = = 0xFF ) {
2005-04-16 15:20:36 -07:00
/* power fault occurred, but it was benign */
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " %s: Power fault \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
rc = POWER_FAILURE ;
2005-10-13 12:05:41 -07:00
p_slot - > status = 0 ;
2005-10-13 12:05:36 -07:00
goto err_exit ;
2005-04-16 15:20:36 -07:00
}
2005-10-13 12:05:36 -07:00
if ( shpchp_configure_device ( p_slot ) ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Cannot add device at %04x:%02x:%02x \n " ,
pci_domain_nr ( parent ) , p_slot - > bus , p_slot - > device ) ;
2005-10-13 12:05:36 -07:00
goto err_exit ;
}
2005-04-16 15:20:36 -07:00
2005-10-13 12:05:41 -07:00
p_slot - > status = 0 ;
p_slot - > is_a_board = 0x01 ;
p_slot - > pwr_save = 1 ;
2005-04-16 15:20:36 -07:00
2005-10-13 12:05:36 -07:00
p_slot - > hpc_ops - > green_led_on ( p_slot ) ;
2005-04-16 15:20:36 -07:00
2005-10-13 12:05:36 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
2005-10-13 12:05:36 -07:00
err_exit :
/* turn off slot, turn on Amber LED, turn off Green LED */
rc = p_slot - > hpc_ops - > slot_disable ( p_slot ) ;
if ( rc ) {
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " %s: Issue of Slot Disable command failed \n " ,
__func__ ) ;
2005-10-13 12:05:36 -07:00
return rc ;
}
2005-04-16 15:20:36 -07:00
2005-10-13 12:05:36 -07:00
return ( rc ) ;
2005-04-16 15:20:36 -07:00
}
/**
2007-11-28 09:04:30 -08:00
* remove_board - Turns off slot and LEDs
* @ p_slot : target & slot
2005-04-16 15:20:36 -07:00
*/
2005-10-13 12:05:42 -07:00
static int remove_board ( struct slot * p_slot )
2005-04-16 15:20:36 -07:00
{
2005-10-13 12:05:41 -07:00
struct controller * ctrl = p_slot - > ctrl ;
2005-04-16 15:20:36 -07:00
u8 hp_slot ;
2005-10-13 12:05:42 -07:00
int rc ;
2005-10-13 12:05:40 -07:00
2005-10-13 12:05:41 -07:00
if ( shpchp_unconfigure_device ( p_slot ) )
2005-04-16 15:20:36 -07:00
return ( 1 ) ;
2005-10-13 12:05:41 -07:00
hp_slot = p_slot - > device - ctrl - > slot_device_offset ;
2005-04-16 15:20:36 -07:00
p_slot = shpchp_find_slot ( ctrl , hp_slot + ctrl - > slot_device_offset ) ;
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " %s: hp_slot = %d \n " , __func__ , hp_slot ) ;
2005-04-16 15:20:36 -07:00
/* Change status to shutdown */
2005-10-13 12:05:41 -07:00
if ( p_slot - > is_a_board )
p_slot - > status = 0x01 ;
2005-04-16 15:20:36 -07:00
/* turn off slot, turn on Amber LED, turn off Green LED */
rc = p_slot - > hpc_ops - > slot_disable ( p_slot ) ;
if ( rc ) {
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " %s: Issue of Slot Disable command failed \n " ,
__func__ ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
2007-01-09 13:03:10 -08:00
2005-04-16 15:20:36 -07:00
rc = p_slot - > hpc_ops - > set_attention_status ( p_slot , 0 ) ;
if ( rc ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Issue of Set Attention command failed \n " ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
2005-10-13 12:05:41 -07:00
p_slot - > pwr_save = 0 ;
p_slot - > is_a_board = 0 ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2006-02-21 15:45:48 -08:00
struct pushbutton_work_info {
struct slot * p_slot ;
struct work_struct work ;
} ;
2005-04-16 15:20:36 -07:00
/**
2007-11-28 09:04:30 -08:00
* shpchp_pushbutton_thread - handle pushbutton events
* @ work : & struct work_struct to be handled
2005-04-16 15:20:36 -07:00
*
2007-11-28 09:04:30 -08:00
* Scheduled procedure to handle blocking stuff for the pushbuttons .
2005-04-16 15:20:36 -07:00
* Handles all pending events and exits .
*/
2006-11-22 14:57:56 +00:00
static void shpchp_pushbutton_thread ( struct work_struct * work )
2005-04-16 15:20:36 -07:00
{
2006-11-22 14:57:56 +00:00
struct pushbutton_work_info * info =
container_of ( work , struct pushbutton_work_info , work ) ;
2006-02-21 15:45:48 -08:00
struct slot * p_slot = info - > p_slot ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:48 -08:00
mutex_lock ( & p_slot - > lock ) ;
switch ( p_slot - > state ) {
case POWEROFF_STATE :
mutex_unlock ( & p_slot - > lock ) ;
2005-04-16 15:20:36 -07:00
shpchp_disable_slot ( p_slot ) ;
2006-02-21 15:45:48 -08:00
mutex_lock ( & p_slot - > lock ) ;
2005-04-16 15:20:36 -07:00
p_slot - > state = STATIC_STATE ;
2006-02-21 15:45:48 -08:00
break ;
case POWERON_STATE :
mutex_unlock ( & p_slot - > lock ) ;
2006-01-26 09:59:24 +09:00
if ( shpchp_enable_slot ( p_slot ) )
2005-04-16 15:20:36 -07:00
p_slot - > hpc_ops - > green_led_off ( p_slot ) ;
2006-02-21 15:45:48 -08:00
mutex_lock ( & p_slot - > lock ) ;
2005-04-16 15:20:36 -07:00
p_slot - > state = STATIC_STATE ;
2006-02-21 15:45:48 -08:00
break ;
default :
break ;
}
mutex_unlock ( & p_slot - > lock ) ;
kfree ( info ) ;
}
2007-03-21 11:45:31 -07:00
void shpchp_queue_pushbutton_work ( struct work_struct * work )
2006-02-21 15:45:48 -08:00
{
2006-11-22 14:57:56 +00:00
struct slot * p_slot = container_of ( work , struct slot , work . work ) ;
2006-02-21 15:45:48 -08:00
struct pushbutton_work_info * info ;
info = kmalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info ) {
2008-10-23 11:52:12 +09:00
ctrl_err ( p_slot - > ctrl , " %s: Cannot allocate memory \n " ,
__func__ ) ;
2006-02-21 15:45:48 -08:00
return ;
}
info - > p_slot = p_slot ;
2006-11-22 14:57:56 +00:00
INIT_WORK ( & info - > work , shpchp_pushbutton_thread ) ;
2006-02-21 15:45:48 -08:00
mutex_lock ( & p_slot - > lock ) ;
switch ( p_slot - > state ) {
case BLINKINGOFF_STATE :
p_slot - > state = POWEROFF_STATE ;
break ;
case BLINKINGON_STATE :
p_slot - > state = POWERON_STATE ;
break ;
default :
goto out ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:48 -08:00
queue_work ( shpchp_wq , & info - > work ) ;
out :
mutex_unlock ( & p_slot - > lock ) ;
2005-04-16 15:20:36 -07:00
}
static int update_slot_info ( struct slot * slot )
{
struct hotplug_slot_info * info ;
int result ;
info = kmalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info )
return - ENOMEM ;
slot - > hpc_ops - > get_power_status ( slot , & ( info - > power_status ) ) ;
slot - > hpc_ops - > get_attention_status ( slot , & ( info - > attention_status ) ) ;
slot - > hpc_ops - > get_latch_status ( slot , & ( info - > latch_status ) ) ;
slot - > hpc_ops - > get_adapter_status ( slot , & ( info - > adapter_status ) ) ;
result = pci_hp_change_slot_info ( slot - > hotplug_slot , info ) ;
kfree ( info ) ;
return result ;
}
2006-02-21 15:45:48 -08:00
/*
* Note : This function must be called with slot - > lock held
*/
static void handle_button_press_event ( struct slot * p_slot )
2005-04-16 15:20:36 -07:00
{
u8 getstatus ;
2008-10-23 11:52:12 +09:00
struct controller * ctrl = p_slot - > ctrl ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:48 -08:00
switch ( p_slot - > state ) {
case STATIC_STATE :
2006-02-21 15:45:45 -08:00
p_slot - > hpc_ops - > get_power_status ( p_slot , & getstatus ) ;
if ( getstatus ) {
p_slot - > state = BLINKINGOFF_STATE ;
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " PCI slot #%s - powering off due to "
" button press. \n " , slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
} else {
p_slot - > state = BLINKINGON_STATE ;
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " PCI slot #%s - powering on due to "
" button press. \n " , slot_name ( p_slot ) ) ;
2006-02-21 15:45:45 -08:00
}
/* blink green LED and turn off amber */
p_slot - > hpc_ops - > green_led_blink ( p_slot ) ;
p_slot - > hpc_ops - > set_attention_status ( p_slot , 0 ) ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:48 -08:00
schedule_delayed_work ( & p_slot - > work , 5 * HZ ) ;
break ;
case BLINKINGOFF_STATE :
case BLINKINGON_STATE :
/*
* Cancel if we are still blinking ; this means that we
* press the attention again before the 5 sec . limit
* expires to cancel hot - add or hot - remove
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Button cancel on Slot(%s) \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
cancel_delayed_work ( & p_slot - > work ) ;
if ( p_slot - > state = = BLINKINGOFF_STATE )
p_slot - > hpc_ops - > green_led_on ( p_slot ) ;
else
p_slot - > hpc_ops - > green_led_off ( p_slot ) ;
p_slot - > hpc_ops - > set_attention_status ( p_slot , 0 ) ;
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " PCI slot #%s - action canceled due to "
" button press \n " , slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
p_slot - > state = STATIC_STATE ;
break ;
case POWEROFF_STATE :
case POWERON_STATE :
/*
* Ignore if the slot is on power - on or power - off state ;
* this means that the previous attention button action
* to hot - add or hot - remove is undergoing
*/
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Button ignore on Slot(%s) \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
update_slot_info ( p_slot ) ;
break ;
default :
2008-10-23 11:52:12 +09:00
ctrl_warn ( ctrl , " Not a valid state \n " ) ;
2006-02-21 15:45:48 -08:00
break ;
}
}
2006-11-22 14:57:56 +00:00
static void interrupt_event_handler ( struct work_struct * work )
2006-02-21 15:45:48 -08:00
{
2006-11-22 14:57:56 +00:00
struct event_info * info = container_of ( work , struct event_info , work ) ;
2006-02-21 15:45:48 -08:00
struct slot * p_slot = info - > p_slot ;
mutex_lock ( & p_slot - > lock ) ;
switch ( info - > event_type ) {
case INT_BUTTON_PRESS :
handle_button_press_event ( p_slot ) ;
2006-02-21 15:45:45 -08:00
break ;
case INT_POWER_FAULT :
2008-10-23 11:54:39 +09:00
ctrl_dbg ( p_slot - > ctrl , " %s: Power fault \n " , __func__ ) ;
2006-02-21 15:45:45 -08:00
p_slot - > hpc_ops - > set_attention_status ( p_slot , 1 ) ;
p_slot - > hpc_ops - > green_led_off ( p_slot ) ;
break ;
default :
update_slot_info ( p_slot ) ;
break ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:48 -08:00
mutex_unlock ( & p_slot - > lock ) ;
2005-04-16 15:20:36 -07:00
2006-02-21 15:45:45 -08:00
kfree ( info ) ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:48 -08:00
static int shpchp_enable_slot ( struct slot * p_slot )
2005-04-16 15:20:36 -07:00
{
u8 getstatus = 0 ;
2006-01-26 10:00:33 +09:00
int rc , retval = - ENODEV ;
2008-10-23 11:52:12 +09:00
struct controller * ctrl = p_slot - > ctrl ;
2005-04-16 15:20:36 -07:00
/* Check to see if (latch closed, card present, power off) */
2006-01-13 16:02:15 +01:00
mutex_lock ( & p_slot - > ctrl - > crit_sect ) ;
2005-04-16 15:20:36 -07:00
rc = p_slot - > hpc_ops - > get_adapter_status ( p_slot , & getstatus ) ;
if ( rc | | ! getstatus ) {
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " No adapter on slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-01-26 10:00:33 +09:00
goto out ;
2005-04-16 15:20:36 -07:00
}
rc = p_slot - > hpc_ops - > get_latch_status ( p_slot , & getstatus ) ;
if ( rc | | getstatus ) {
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Latch open on slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-01-26 10:00:33 +09:00
goto out ;
2005-04-16 15:20:36 -07:00
}
rc = p_slot - > hpc_ops - > get_power_status ( p_slot , & getstatus ) ;
if ( rc | | getstatus ) {
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Already enabled on slot(%s) \n " ,
slot_name ( p_slot ) ) ;
2006-01-26 10:00:33 +09:00
goto out ;
2005-04-16 15:20:36 -07:00
}
2005-10-13 12:05:41 -07:00
p_slot - > is_a_board = 1 ;
2005-04-16 15:20:36 -07:00
/* We have to save the presence info for these slots */
2005-10-13 12:05:41 -07:00
p_slot - > hpc_ops - > get_adapter_status ( p_slot , & ( p_slot - > presence_save ) ) ;
p_slot - > hpc_ops - > get_power_status ( p_slot , & ( p_slot - > pwr_save ) ) ;
2008-10-23 11:52:12 +09:00
ctrl_dbg ( ctrl , " %s: p_slot->pwr_save %x \n " , __func__ , p_slot - > pwr_save ) ;
2005-04-16 15:20:36 -07:00
p_slot - > hpc_ops - > get_latch_status ( p_slot , & getstatus ) ;
2006-01-16 15:22:36 -06:00
if ( ( ( p_slot - > ctrl - > pci_dev - > vendor = = PCI_VENDOR_ID_AMD ) | |
( p_slot - > ctrl - > pci_dev - > device = = PCI_DEVICE_ID_AMD_POGO_7458 ) )
& & p_slot - > ctrl - > num_slots = = 1 ) {
/* handle amd pogo errata; this must be done before enable */
amd_pogo_errata_save_misc_reg ( p_slot ) ;
2006-01-26 10:00:33 +09:00
retval = board_added ( p_slot ) ;
2006-01-16 15:22:36 -06:00
/* handle amd pogo errata; this must be done after enable */
amd_pogo_errata_restore_misc_reg ( p_slot ) ;
} else
2006-01-26 10:00:33 +09:00
retval = board_added ( p_slot ) ;
2006-01-16 15:22:36 -06:00
2006-01-26 10:00:33 +09:00
if ( retval ) {
2005-10-13 12:05:41 -07:00
p_slot - > hpc_ops - > get_adapter_status ( p_slot ,
& ( p_slot - > presence_save ) ) ;
2005-04-16 15:20:36 -07:00
p_slot - > hpc_ops - > get_latch_status ( p_slot , & getstatus ) ;
}
2005-10-13 12:05:41 -07:00
update_slot_info ( p_slot ) ;
2006-01-26 10:00:33 +09:00
out :
mutex_unlock ( & p_slot - > ctrl - > crit_sect ) ;
return retval ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:48 -08:00
static int shpchp_disable_slot ( struct slot * p_slot )
2005-04-16 15:20:36 -07:00
{
u8 getstatus = 0 ;
2006-01-26 10:00:33 +09:00
int rc , retval = - ENODEV ;
2008-10-23 11:52:12 +09:00
struct controller * ctrl = p_slot - > ctrl ;
2005-04-16 15:20:36 -07:00
if ( ! p_slot - > ctrl )
2005-05-05 11:57:25 -07:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
/* Check to see if (latch closed, card present, power on) */
2006-01-13 16:02:15 +01:00
mutex_lock ( & p_slot - > ctrl - > crit_sect ) ;
2005-04-16 15:20:36 -07:00
2006-01-26 10:00:33 +09:00
rc = p_slot - > hpc_ops - > get_adapter_status ( p_slot , & getstatus ) ;
if ( rc | | ! getstatus ) {
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " No adapter on slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-01-26 10:00:33 +09:00
goto out ;
2005-04-16 15:20:36 -07:00
}
2006-01-26 10:00:33 +09:00
rc = p_slot - > hpc_ops - > get_latch_status ( p_slot , & getstatus ) ;
if ( rc | | getstatus ) {
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Latch open on slot(%s) \n " , slot_name ( p_slot ) ) ;
2006-01-26 10:00:33 +09:00
goto out ;
2005-04-16 15:20:36 -07:00
}
2006-01-26 10:00:33 +09:00
rc = p_slot - > hpc_ops - > get_power_status ( p_slot , & getstatus ) ;
if ( rc | | ! getstatus ) {
2008-10-23 11:54:39 +09:00
ctrl_info ( ctrl , " Already disabled on slot(%s) \n " ,
2008-10-23 11:52:12 +09:00
slot_name ( p_slot ) ) ;
2006-01-26 10:00:33 +09:00
goto out ;
2005-04-16 15:20:36 -07:00
}
2006-01-26 10:00:33 +09:00
retval = remove_board ( p_slot ) ;
2005-10-13 12:05:41 -07:00
update_slot_info ( p_slot ) ;
2006-01-26 10:00:33 +09:00
out :
mutex_unlock ( & p_slot - > ctrl - > crit_sect ) ;
return retval ;
2005-04-16 15:20:36 -07:00
}
2006-02-21 15:45:48 -08:00
int shpchp_sysfs_enable_slot ( struct slot * p_slot )
{
int retval = - ENODEV ;
2008-10-23 11:52:12 +09:00
struct controller * ctrl = p_slot - > ctrl ;
2006-02-21 15:45:48 -08:00
mutex_lock ( & p_slot - > lock ) ;
switch ( p_slot - > state ) {
case BLINKINGON_STATE :
cancel_delayed_work ( & p_slot - > work ) ;
case STATIC_STATE :
p_slot - > state = POWERON_STATE ;
mutex_unlock ( & p_slot - > lock ) ;
retval = shpchp_enable_slot ( p_slot ) ;
mutex_lock ( & p_slot - > lock ) ;
p_slot - > state = STATIC_STATE ;
break ;
case POWERON_STATE :
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Slot %s is already in powering on state \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
break ;
case BLINKINGOFF_STATE :
case POWEROFF_STATE :
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Already enabled on slot %s \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
break ;
default :
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " Not a valid state on slot %s \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
break ;
}
mutex_unlock ( & p_slot - > lock ) ;
return retval ;
}
int shpchp_sysfs_disable_slot ( struct slot * p_slot )
{
int retval = - ENODEV ;
2008-10-23 11:52:12 +09:00
struct controller * ctrl = p_slot - > ctrl ;
2006-02-21 15:45:48 -08:00
mutex_lock ( & p_slot - > lock ) ;
switch ( p_slot - > state ) {
case BLINKINGOFF_STATE :
cancel_delayed_work ( & p_slot - > work ) ;
case STATIC_STATE :
p_slot - > state = POWEROFF_STATE ;
mutex_unlock ( & p_slot - > lock ) ;
retval = shpchp_disable_slot ( p_slot ) ;
mutex_lock ( & p_slot - > lock ) ;
p_slot - > state = STATIC_STATE ;
break ;
case POWEROFF_STATE :
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Slot %s is already in powering off state \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
break ;
case BLINKINGON_STATE :
case POWERON_STATE :
2008-10-23 11:52:12 +09:00
ctrl_info ( ctrl , " Already disabled on slot %s \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
break ;
default :
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " Not a valid state on slot %s \n " ,
slot_name ( p_slot ) ) ;
2006-02-21 15:45:48 -08:00
break ;
}
mutex_unlock ( & p_slot - > lock ) ;
return retval ;
}