2005-04-17 02:20:36 +04:00
/*
* CompactPCI Hot Plug Driver
*
2005-05-28 00:48:52 +04:00
* Copyright ( C ) 2002 , 2005 SOMA Networks , Inc .
2005-04-17 02:20:36 +04:00
* 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 < scottm @ somanetworks . com >
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/smp_lock.h>
2005-05-10 01:31:50 +04:00
# include <asm/atomic.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
# include "pci_hotplug.h"
# include "cpci_hotplug.h"
# define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
# define DRIVER_DESC "CompactPCI Hot Plug Core"
# define MY_NAME "cpci_hotplug"
# define dbg(format, arg...) \
do { \
2005-05-28 00:48:52 +04:00
if ( cpci_debug ) \
2005-04-17 02:20:36 +04:00
printk ( KERN_DEBUG " %s: " format " \n " , \
MY_NAME , # # arg ) ; \
2005-05-28 00:48:52 +04:00
} while ( 0 )
2005-04-17 02:20:36 +04:00
# define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
# define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
# define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
2005-05-10 01:31:50 +04:00
static DECLARE_RWSEM ( list_rwsem ) ;
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( slot_list ) ;
static int slots ;
2005-05-10 01:31:50 +04:00
static atomic_t extracting ;
2005-04-17 02:20:36 +04:00
int cpci_debug ;
static struct cpci_hp_controller * controller ;
static struct semaphore event_semaphore ; /* mutex for process loop (up if something to process) */
static struct semaphore thread_exit ; /* guard ensure thread has exited before calling it quits */
static int thread_finished = 1 ;
static int enable_slot ( struct hotplug_slot * slot ) ;
static int disable_slot ( struct hotplug_slot * slot ) ;
static int set_attention_status ( struct hotplug_slot * slot , u8 value ) ;
static int get_power_status ( struct hotplug_slot * slot , u8 * value ) ;
static int get_attention_status ( struct hotplug_slot * slot , u8 * value ) ;
2005-05-10 01:31:50 +04:00
static int get_adapter_status ( struct hotplug_slot * slot , u8 * value ) ;
static int get_latch_status ( struct hotplug_slot * slot , u8 * value ) ;
2005-04-17 02:20:36 +04:00
static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
. owner = THIS_MODULE ,
. enable_slot = enable_slot ,
. disable_slot = disable_slot ,
. set_attention_status = set_attention_status ,
. get_power_status = get_power_status ,
. get_attention_status = get_attention_status ,
2005-05-10 01:31:50 +04:00
. get_adapter_status = get_adapter_status ,
. get_latch_status = get_latch_status ,
2005-04-17 02:20:36 +04:00
} ;
static int
update_latch_status ( struct hotplug_slot * hotplug_slot , u8 value )
{
struct hotplug_slot_info info ;
memcpy ( & info , hotplug_slot - > info , sizeof ( struct hotplug_slot_info ) ) ;
info . latch_status = value ;
return pci_hp_change_slot_info ( hotplug_slot , & info ) ;
}
static int
update_adapter_status ( struct hotplug_slot * hotplug_slot , u8 value )
{
struct hotplug_slot_info info ;
memcpy ( & info , hotplug_slot - > info , sizeof ( struct hotplug_slot_info ) ) ;
info . adapter_status = value ;
return pci_hp_change_slot_info ( hotplug_slot , & info ) ;
}
static int
enable_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
int retval = 0 ;
dbg ( " %s - physical_slot = %s " , __FUNCTION__ , hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
if ( controller - > ops - > set_power )
2005-04-17 02:20:36 +04:00
retval = controller - > ops - > set_power ( slot , 1 ) ;
return retval ;
}
static int
disable_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
int retval = 0 ;
dbg ( " %s - physical_slot = %s " , __FUNCTION__ , hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
down_write ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
/* Unconfigure device */
dbg ( " %s - unconfiguring slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
if ( ( retval = cpci_unconfigure_slot ( slot ) ) ) {
2005-04-17 02:20:36 +04:00
err ( " %s - could not unconfigure slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
goto disable_error ;
2005-04-17 02:20:36 +04:00
}
dbg ( " %s - finished unconfiguring slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
/* Clear EXT (by setting it) */
2005-05-28 00:48:52 +04:00
if ( cpci_clear_ext ( slot ) ) {
2005-04-17 02:20:36 +04:00
err ( " %s - could not clear EXT for slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
retval = - ENODEV ;
2005-05-28 00:48:52 +04:00
goto disable_error ;
2005-04-17 02:20:36 +04:00
}
cpci_led_on ( slot ) ;
2005-05-28 00:48:52 +04:00
if ( controller - > ops - > set_power )
if ( ( retval = controller - > ops - > set_power ( slot , 0 ) ) )
goto disable_error ;
2005-04-17 02:20:36 +04:00
2005-05-28 00:48:52 +04:00
if ( update_adapter_status ( slot - > hotplug_slot , 0 ) )
2005-04-17 02:20:36 +04:00
warn ( " failure to update adapter file " ) ;
2005-05-28 00:48:52 +04:00
if ( slot - > extracting ) {
2005-05-10 01:31:50 +04:00
slot - > extracting = 0 ;
atomic_dec ( & extracting ) ;
}
2005-05-28 00:48:52 +04:00
disable_error :
up_write ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
static u8
cpci_get_power_status ( struct slot * slot )
{
u8 power = 1 ;
2005-05-28 00:48:52 +04:00
if ( controller - > ops - > get_power )
2005-04-17 02:20:36 +04:00
power = controller - > ops - > get_power ( slot ) ;
return power ;
}
static int
get_power_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
* value = cpci_get_power_status ( slot ) ;
return 0 ;
}
static int
get_attention_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
* value = cpci_get_attention_status ( slot ) ;
return 0 ;
}
static int
set_attention_status ( struct hotplug_slot * hotplug_slot , u8 status )
{
return cpci_set_attention_status ( hotplug_slot - > private , status ) ;
}
2005-05-10 01:31:50 +04:00
static int
get_adapter_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
* value = hotplug_slot - > info - > adapter_status ;
return 0 ;
}
static int
get_latch_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
* value = hotplug_slot - > info - > latch_status ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static void release_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
kfree ( slot - > hotplug_slot - > info ) ;
kfree ( slot - > hotplug_slot - > name ) ;
kfree ( slot - > hotplug_slot ) ;
kfree ( slot ) ;
}
# define SLOT_NAME_SIZE 6
static void
make_slot_name ( struct slot * slot )
{
snprintf ( slot - > hotplug_slot - > name ,
SLOT_NAME_SIZE , " %02x:%02x " , slot - > bus - > number , slot - > number ) ;
}
int
cpci_hp_register_bus ( struct pci_bus * bus , u8 first , u8 last )
{
struct slot * slot ;
struct hotplug_slot * hotplug_slot ;
struct hotplug_slot_info * info ;
char * name ;
int status = - ENOMEM ;
int i ;
2005-05-28 00:48:52 +04:00
if ( ! ( controller & & bus ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
/*
* Create a structure for each slot , and register that slot
* with the pci_hotplug subsystem .
*/
for ( i = first ; i < = last ; + + i ) {
slot = kmalloc ( sizeof ( struct slot ) , GFP_KERNEL ) ;
if ( ! slot )
goto error ;
memset ( slot , 0 , sizeof ( struct slot ) ) ;
hotplug_slot =
kmalloc ( sizeof ( struct hotplug_slot ) , GFP_KERNEL ) ;
if ( ! hotplug_slot )
goto error_slot ;
memset ( hotplug_slot , 0 , sizeof ( struct hotplug_slot ) ) ;
slot - > hotplug_slot = hotplug_slot ;
info = kmalloc ( sizeof ( struct hotplug_slot_info ) , GFP_KERNEL ) ;
if ( ! info )
goto error_hpslot ;
memset ( info , 0 , sizeof ( struct hotplug_slot_info ) ) ;
hotplug_slot - > info = info ;
name = kmalloc ( SLOT_NAME_SIZE , GFP_KERNEL ) ;
if ( ! name )
goto error_info ;
hotplug_slot - > name = name ;
slot - > bus = bus ;
slot - > number = i ;
slot - > devfn = PCI_DEVFN ( i , 0 ) ;
hotplug_slot - > private = slot ;
hotplug_slot - > release = & release_slot ;
make_slot_name ( slot ) ;
hotplug_slot - > ops = & cpci_hotplug_slot_ops ;
/*
* Initialize the slot info structure with some known
* good values .
*/
dbg ( " initializing slot %s " , slot - > hotplug_slot - > name ) ;
info - > power_status = cpci_get_power_status ( slot ) ;
info - > attention_status = cpci_get_attention_status ( slot ) ;
dbg ( " registering slot %s " , slot - > hotplug_slot - > name ) ;
status = pci_hp_register ( slot - > hotplug_slot ) ;
if ( status ) {
err ( " pci_hp_register failed with error %d " , status ) ;
goto error_name ;
}
/* Add slot to our internal list */
2005-05-10 01:31:50 +04:00
down_write ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
list_add ( & slot - > slot_list , & slot_list ) ;
slots + + ;
2005-05-10 01:31:50 +04:00
up_write ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
error_name :
kfree ( name ) ;
error_info :
kfree ( info ) ;
error_hpslot :
kfree ( hotplug_slot ) ;
error_slot :
kfree ( slot ) ;
error :
return status ;
}
int
cpci_hp_unregister_bus ( struct pci_bus * bus )
{
struct slot * slot ;
2005-05-28 00:48:52 +04:00
struct slot * tmp ;
int status = 0 ;
2005-04-17 02:20:36 +04:00
2005-05-10 01:31:50 +04:00
down_write ( & list_rwsem ) ;
2005-05-28 00:48:52 +04:00
if ( ! slots ) {
2005-05-10 01:31:50 +04:00
up_write ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2005-05-28 00:48:52 +04:00
list_for_each_entry_safe ( slot , tmp , & slot_list , slot_list ) {
if ( slot - > bus = = bus ) {
list_del ( & slot - > slot_list ) ;
slots - - ;
2005-04-17 02:20:36 +04:00
dbg ( " deregistering slot %s " , slot - > hotplug_slot - > name ) ;
status = pci_hp_deregister ( slot - > hotplug_slot ) ;
2005-05-28 00:48:52 +04:00
if ( status ) {
2005-04-17 02:20:36 +04:00
err ( " pci_hp_deregister failed with error %d " ,
status ) ;
2005-05-28 00:48:52 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
}
}
2005-05-10 01:31:50 +04:00
up_write ( & list_rwsem ) ;
2005-05-28 00:48:52 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
/* This is the interrupt mode interrupt handler */
static irqreturn_t
cpci_hp_intr ( int irq , void * data , struct pt_regs * regs )
{
dbg ( " entered cpci_hp_intr " ) ;
/* Check to see if it was our interrupt */
2005-05-28 00:48:52 +04:00
if ( ( controller - > irq_flags & SA_SHIRQ ) & &
2005-04-17 02:20:36 +04:00
! controller - > ops - > check_irq ( controller - > dev_id ) ) {
dbg ( " exited cpci_hp_intr, not our interrupt " ) ;
return IRQ_NONE ;
}
/* Disable ENUM interrupt */
controller - > ops - > disable_irq ( ) ;
/* Trigger processing by the event thread */
dbg ( " Signal event_semaphore " ) ;
up ( & event_semaphore ) ;
dbg ( " exited cpci_hp_intr " ) ;
return IRQ_HANDLED ;
}
/*
2005-05-10 01:31:50 +04:00
* According to PICMG 2.1 R2 .0 , section 6.3 .2 , upon
2005-04-17 02:20:36 +04:00
* initialization , the system driver shall clear the
* INS bits of the cold - inserted devices .
*/
static int
2005-05-28 00:48:52 +04:00
init_slots ( int clear_ins )
2005-04-17 02:20:36 +04:00
{
struct slot * slot ;
struct pci_dev * dev ;
dbg ( " %s - enter " , __FUNCTION__ ) ;
2005-05-10 01:31:50 +04:00
down_read ( & list_rwsem ) ;
2005-05-28 00:48:52 +04:00
if ( ! slots ) {
2005-05-10 01:31:50 +04:00
up_read ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2005-05-28 00:48:52 +04:00
list_for_each_entry ( slot , & slot_list , slot_list ) {
2005-04-17 02:20:36 +04:00
dbg ( " %s - looking at slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
if ( clear_ins & & cpci_check_and_clear_ins ( slot ) )
2005-04-17 02:20:36 +04:00
dbg ( " %s - cleared INS for slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
dev = pci_get_slot ( slot - > bus , PCI_DEVFN ( slot - > number , 0 ) ) ;
if ( dev ) {
if ( update_adapter_status ( slot - > hotplug_slot , 1 ) )
warn ( " failure to update adapter file " ) ;
if ( update_latch_status ( slot - > hotplug_slot , 1 ) )
warn ( " failure to update latch file " ) ;
slot - > dev = dev ;
2005-04-17 02:20:36 +04:00
}
}
2005-05-10 01:31:50 +04:00
up_read ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
dbg ( " %s - exit " , __FUNCTION__ ) ;
return 0 ;
}
static int
check_slots ( void )
{
struct slot * slot ;
int extracted ;
int inserted ;
2005-05-10 01:31:50 +04:00
u16 hs_csr ;
2005-04-17 02:20:36 +04:00
2005-05-10 01:31:50 +04:00
down_read ( & list_rwsem ) ;
2005-05-28 00:48:52 +04:00
if ( ! slots ) {
2005-05-10 01:31:50 +04:00
up_read ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
err ( " no slots registered, shutting down " ) ;
return - 1 ;
}
extracted = inserted = 0 ;
2005-05-28 00:48:52 +04:00
list_for_each_entry ( slot , & slot_list , slot_list ) {
2005-04-17 02:20:36 +04:00
dbg ( " %s - looking at slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
if ( cpci_check_and_clear_ins ( slot ) ) {
/*
* Some broken hardware ( e . g . PLX 9054 AB ) asserts
* ENUM # twice . . .
*/
if ( slot - > dev ) {
warn ( " slot %s already inserted " ,
slot - > hotplug_slot - > name ) ;
2005-04-17 02:20:36 +04:00
inserted + + ;
continue ;
}
/* Process insertion */
dbg ( " %s - slot %s inserted " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
/* GSM, debug */
hs_csr = cpci_get_hs_csr ( slot ) ;
dbg ( " %s - slot %s HS_CSR (1) = %04x " ,
__FUNCTION__ , slot - > hotplug_slot - > name , hs_csr ) ;
/* Configure device */
dbg ( " %s - configuring slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
if ( cpci_configure_slot ( slot ) ) {
2005-04-17 02:20:36 +04:00
err ( " %s - could not configure slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
continue ;
}
dbg ( " %s - finished configuring slot %s " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
/* GSM, debug */
hs_csr = cpci_get_hs_csr ( slot ) ;
dbg ( " %s - slot %s HS_CSR (2) = %04x " ,
__FUNCTION__ , slot - > hotplug_slot - > name , hs_csr ) ;
2005-05-28 00:48:52 +04:00
if ( update_latch_status ( slot - > hotplug_slot , 1 ) )
2005-04-17 02:20:36 +04:00
warn ( " failure to update latch file " ) ;
2005-05-28 00:48:52 +04:00
if ( update_adapter_status ( slot - > hotplug_slot , 1 ) )
2005-04-17 02:20:36 +04:00
warn ( " failure to update adapter file " ) ;
cpci_led_off ( slot ) ;
/* GSM, debug */
hs_csr = cpci_get_hs_csr ( slot ) ;
dbg ( " %s - slot %s HS_CSR (3) = %04x " ,
__FUNCTION__ , slot - > hotplug_slot - > name , hs_csr ) ;
inserted + + ;
2005-05-28 00:48:52 +04:00
} else if ( cpci_check_ext ( slot ) ) {
2005-04-17 02:20:36 +04:00
/* Process extraction request */
dbg ( " %s - slot %s extracted " ,
__FUNCTION__ , slot - > hotplug_slot - > name ) ;
/* GSM, debug */
hs_csr = cpci_get_hs_csr ( slot ) ;
dbg ( " %s - slot %s HS_CSR = %04x " ,
__FUNCTION__ , slot - > hotplug_slot - > name , hs_csr ) ;
2005-05-28 00:48:52 +04:00
if ( ! slot - > extracting ) {
if ( update_latch_status ( slot - > hotplug_slot , 0 ) ) {
2005-04-17 02:20:36 +04:00
warn ( " failure to update latch file " ) ;
}
slot - > extracting = 1 ;
2005-05-28 00:48:52 +04:00
atomic_inc ( & extracting ) ;
2005-04-17 02:20:36 +04:00
}
extracted + + ;
2005-05-28 00:48:52 +04:00
} else if ( slot - > extracting ) {
2005-05-10 01:31:50 +04:00
hs_csr = cpci_get_hs_csr ( slot ) ;
2005-05-28 00:48:52 +04:00
if ( hs_csr = = 0xffff ) {
2005-05-10 01:31:50 +04:00
/*
* Hmmm , we ' re likely hosed at this point , should we
* bother trying to tell the driver or not ?
*/
err ( " card in slot %s was improperly removed " ,
slot - > hotplug_slot - > name ) ;
2005-05-28 00:48:52 +04:00
if ( update_adapter_status ( slot - > hotplug_slot , 0 ) )
2005-05-10 01:31:50 +04:00
warn ( " failure to update adapter file " ) ;
slot - > extracting = 0 ;
atomic_dec ( & extracting ) ;
}
2005-04-17 02:20:36 +04:00
}
}
2005-05-10 01:31:50 +04:00
up_read ( & list_rwsem ) ;
dbg ( " inserted=%d, extracted=%d, extracting=%d " ,
inserted , extracted , atomic_read ( & extracting ) ) ;
2005-05-28 00:48:52 +04:00
if ( inserted | | extracted )
2005-04-17 02:20:36 +04:00
return extracted ;
2005-05-28 00:48:52 +04:00
else if ( ! atomic_read ( & extracting ) ) {
2005-04-17 02:20:36 +04:00
err ( " cannot find ENUM# source, shutting down " ) ;
return - 1 ;
}
2005-05-10 01:31:50 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* This is the interrupt mode worker thread body */
static int
event_thread ( void * data )
{
int rc ;
lock_kernel ( ) ;
daemonize ( " cpci_hp_eventd " ) ;
unlock_kernel ( ) ;
dbg ( " %s - event thread started " , __FUNCTION__ ) ;
2005-05-28 00:48:52 +04:00
while ( 1 ) {
2005-04-17 02:20:36 +04:00
dbg ( " event thread sleeping " ) ;
down_interruptible ( & event_semaphore ) ;
dbg ( " event thread woken, thread_finished = %d " ,
thread_finished ) ;
2005-05-28 00:48:52 +04:00
if ( thread_finished | | signal_pending ( current ) )
2005-04-17 02:20:36 +04:00
break ;
2005-05-10 01:31:50 +04:00
do {
2005-04-17 02:20:36 +04:00
rc = check_slots ( ) ;
2005-05-10 01:31:50 +04:00
if ( rc > 0 ) {
2005-04-17 02:20:36 +04:00
/* Give userspace a chance to handle extraction */
msleep ( 500 ) ;
2005-05-10 01:31:50 +04:00
} else if ( rc < 0 ) {
2005-04-17 02:20:36 +04:00
dbg ( " %s - error checking slots " , __FUNCTION__ ) ;
thread_finished = 1 ;
break ;
}
2005-05-28 00:48:52 +04:00
} while ( atomic_read ( & extracting ) & & ! thread_finished ) ;
if ( thread_finished )
break ;
2005-04-17 02:20:36 +04:00
/* Re-enable ENUM# interrupt */
dbg ( " %s - re-enabling irq " , __FUNCTION__ ) ;
controller - > ops - > enable_irq ( ) ;
}
dbg ( " %s - event thread signals exit " , __FUNCTION__ ) ;
up ( & thread_exit ) ;
return 0 ;
}
/* This is the polling mode worker thread body */
static int
poll_thread ( void * data )
{
int rc ;
lock_kernel ( ) ;
daemonize ( " cpci_hp_polld " ) ;
unlock_kernel ( ) ;
2005-05-28 00:48:52 +04:00
while ( 1 ) {
if ( thread_finished | | signal_pending ( current ) )
2005-04-17 02:20:36 +04:00
break ;
2005-05-28 00:48:52 +04:00
if ( controller - > ops - > query_enum ( ) ) {
2005-05-10 01:31:50 +04:00
do {
rc = check_slots ( ) ;
2005-05-28 00:48:52 +04:00
if ( rc > 0 ) {
2005-05-10 01:31:50 +04:00
/* Give userspace a chance to handle extraction */
msleep ( 500 ) ;
2005-05-28 00:48:52 +04:00
} else if ( rc < 0 ) {
2005-05-10 01:31:50 +04:00
dbg ( " %s - error checking slots " , __FUNCTION__ ) ;
thread_finished = 1 ;
break ;
2005-04-17 02:20:36 +04:00
}
2005-05-28 00:48:52 +04:00
} while ( atomic_read ( & extracting ) & & ! thread_finished ) ;
2005-04-17 02:20:36 +04:00
}
msleep ( 100 ) ;
}
dbg ( " poll thread signals exit " ) ;
up ( & thread_exit ) ;
return 0 ;
}
static int
cpci_start_thread ( void )
{
int pid ;
/* initialize our semaphores */
init_MUTEX_LOCKED ( & event_semaphore ) ;
init_MUTEX_LOCKED ( & thread_exit ) ;
thread_finished = 0 ;
2005-05-28 00:48:52 +04:00
if ( controller - > irq )
2005-04-17 02:20:36 +04:00
pid = kernel_thread ( event_thread , NULL , 0 ) ;
2005-05-28 00:48:52 +04:00
else
2005-04-17 02:20:36 +04:00
pid = kernel_thread ( poll_thread , NULL , 0 ) ;
2005-05-28 00:48:52 +04:00
if ( pid < 0 ) {
2005-04-17 02:20:36 +04:00
err ( " Can't start up our thread " ) ;
return - 1 ;
}
dbg ( " Our thread pid = %d " , pid ) ;
return 0 ;
}
static void
cpci_stop_thread ( void )
{
thread_finished = 1 ;
dbg ( " thread finish command given " ) ;
2005-05-28 00:48:52 +04:00
if ( controller - > irq )
2005-04-17 02:20:36 +04:00
up ( & event_semaphore ) ;
dbg ( " wait for thread to exit " ) ;
down ( & thread_exit ) ;
}
int
cpci_hp_register_controller ( struct cpci_hp_controller * new_controller )
{
int status = 0 ;
2005-05-28 00:48:52 +04:00
if ( controller )
return - 1 ;
if ( ! ( new_controller & & new_controller - > ops ) )
return - EINVAL ;
if ( new_controller - > irq ) {
if ( ! ( new_controller - > ops - > enable_irq & &
new_controller - > ops - > disable_irq ) )
status = - EINVAL ;
if ( request_irq ( new_controller - > irq ,
cpci_hp_intr ,
new_controller - > irq_flags ,
MY_NAME ,
new_controller - > dev_id ) ) {
err ( " Can't get irq %d for the hotplug cPCI controller " ,
new_controller - > irq ) ;
status = - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-05-28 00:48:52 +04:00
dbg ( " %s - acquired controller irq %d " ,
__FUNCTION__ , new_controller - > irq ) ;
2005-04-17 02:20:36 +04:00
}
2005-05-28 00:48:52 +04:00
if ( ! status )
controller = new_controller ;
2005-04-17 02:20:36 +04:00
return status ;
}
2005-05-28 00:48:52 +04:00
static void
cleanup_slots ( void )
{
struct slot * slot ;
struct slot * tmp ;
/*
* Unregister all of our slots with the pci_hotplug subsystem ,
* and free up all memory that we had allocated .
*/
down_write ( & list_rwsem ) ;
if ( ! slots )
goto cleanup_null ;
list_for_each_entry_safe ( slot , tmp , & slot_list , slot_list ) {
list_del ( & slot - > slot_list ) ;
pci_hp_deregister ( slot - > hotplug_slot ) ;
}
cleanup_null :
up_write ( & list_rwsem ) ;
return ;
}
2005-04-17 02:20:36 +04:00
int
cpci_hp_unregister_controller ( struct cpci_hp_controller * old_controller )
{
int status = 0 ;
2005-05-28 00:48:52 +04:00
if ( controller ) {
if ( ! thread_finished )
2005-04-17 02:20:36 +04:00
cpci_stop_thread ( ) ;
2005-05-28 00:48:52 +04:00
if ( controller - > irq )
2005-04-17 02:20:36 +04:00
free_irq ( controller - > irq , controller - > dev_id ) ;
controller = NULL ;
2005-05-28 00:48:52 +04:00
cleanup_slots ( ) ;
} else
2005-04-17 02:20:36 +04:00
status = - ENODEV ;
return status ;
}
int
cpci_hp_start ( void )
{
static int first = 1 ;
int status ;
dbg ( " %s - enter " , __FUNCTION__ ) ;
2005-05-28 00:48:52 +04:00
if ( ! controller )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-10 01:31:50 +04:00
down_read ( & list_rwsem ) ;
2005-05-28 00:48:52 +04:00
if ( list_empty ( & slot_list ) ) {
2005-05-10 01:31:50 +04:00
up_read ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2005-05-10 01:31:50 +04:00
up_read ( & list_rwsem ) ;
2005-04-17 02:20:36 +04:00
2005-05-28 00:48:52 +04:00
status = init_slots ( first ) ;
if ( first )
2005-04-17 02:20:36 +04:00
first = 0 ;
2005-05-28 00:48:52 +04:00
if ( status )
return status ;
2005-04-17 02:20:36 +04:00
status = cpci_start_thread ( ) ;
2005-05-28 00:48:52 +04:00
if ( status )
2005-04-17 02:20:36 +04:00
return status ;
dbg ( " %s - thread started " , __FUNCTION__ ) ;
2005-05-28 00:48:52 +04:00
if ( controller - > irq ) {
2005-04-17 02:20:36 +04:00
/* Start enum interrupt processing */
dbg ( " %s - enabling irq " , __FUNCTION__ ) ;
controller - > ops - > enable_irq ( ) ;
}
dbg ( " %s - exit " , __FUNCTION__ ) ;
return 0 ;
}
int
cpci_hp_stop ( void )
{
2005-05-28 00:48:52 +04:00
if ( ! controller )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-28 00:48:52 +04:00
if ( controller - > irq ) {
2005-04-17 02:20:36 +04:00
/* Stop enum interrupt processing */
dbg ( " %s - disabling irq " , __FUNCTION__ ) ;
controller - > ops - > disable_irq ( ) ;
}
cpci_stop_thread ( ) ;
return 0 ;
}
int __init
cpci_hotplug_init ( int debug )
{
cpci_debug = debug ;
return 0 ;
}
void __exit
cpci_hotplug_exit ( void )
{
/*
* Clean everything up .
*/
2005-05-28 00:48:52 +04:00
cpci_hp_stop ( ) ;
cpci_hp_unregister_controller ( controller ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL_GPL ( cpci_hp_register_controller ) ;
EXPORT_SYMBOL_GPL ( cpci_hp_unregister_controller ) ;
EXPORT_SYMBOL_GPL ( cpci_hp_register_bus ) ;
EXPORT_SYMBOL_GPL ( cpci_hp_unregister_bus ) ;
EXPORT_SYMBOL_GPL ( cpci_hp_start ) ;
EXPORT_SYMBOL_GPL ( cpci_hp_stop ) ;