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