2005-04-17 02:20:36 +04:00
/*
* CompactPCI Hot Plug Driver PCI functions
*
2005-05-28 00:48:52 +04:00
* Copyright ( C ) 2002 , 2005 by SOMA Networks , Inc .
2005-04-17 02:20:36 +04:00
*
* 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/module.h>
# include <linux/kernel.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/proc_fs.h>
# include "../pci.h"
# include "cpci_hotplug.h"
# define MY_NAME "cpci_hotplug"
extern int cpci_debug ;
# 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 " , \
2013-11-14 22:28:18 +04:00
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)
u8 cpci_get_attention_status ( struct slot * slot )
{
int hs_cap ;
u16 hs_csr ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
2005-04-17 02:20:36 +04:00
return hs_csr & 0x0008 ? 1 : 0 ;
}
int cpci_set_attention_status ( struct slot * slot , int status )
{
int hs_cap ;
u16 hs_csr ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
if ( status )
2005-04-17 02:20:36 +04:00
hs_csr | = HS_CSR_LOO ;
2005-05-28 00:48:52 +04:00
else
2005-04-17 02:20:36 +04:00
hs_csr & = ~ HS_CSR_LOO ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_write_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
hs_csr ) )
2005-04-17 02:20:36 +04:00
return 0 ;
return 1 ;
}
u16 cpci_get_hs_csr ( struct slot * slot )
{
int hs_cap ;
u16 hs_csr ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return 0xFFFF ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return 0xFFFF ;
return hs_csr ;
}
int cpci_check_and_clear_ins ( struct slot * slot )
{
int hs_cap ;
u16 hs_csr ;
int ins = 0 ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
if ( hs_csr & HS_CSR_INS ) {
2005-04-17 02:20:36 +04:00
/* Clear INS (by setting it) */
2005-05-28 00:48:52 +04:00
if ( pci_bus_write_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
hs_csr ) )
2005-04-17 02:20:36 +04:00
ins = 0 ;
2005-05-28 00:48:52 +04:00
else
ins = 1 ;
2005-04-17 02:20:36 +04:00
}
return ins ;
}
int cpci_check_ext ( struct slot * slot )
{
int hs_cap ;
u16 hs_csr ;
int ext = 0 ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-05-28 00:48:52 +04:00
if ( hs_csr & HS_CSR_EXT )
2005-04-17 02:20:36 +04:00
ext = 1 ;
return ext ;
}
int cpci_clear_ext ( struct slot * slot )
{
int hs_cap ;
u16 hs_csr ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-28 00:48:52 +04:00
if ( hs_csr & HS_CSR_EXT ) {
2005-04-17 02:20:36 +04:00
/* Clear EXT (by setting it) */
2005-05-28 00:48:52 +04:00
if ( pci_bus_write_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
hs_csr ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
return 0 ;
}
int cpci_led_on ( struct slot * slot )
{
int hs_cap ;
u16 hs_csr ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-28 00:48:52 +04:00
if ( ( hs_csr & HS_CSR_LOO ) ! = HS_CSR_LOO ) {
2005-04-17 02:20:36 +04:00
hs_csr | = HS_CSR_LOO ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_write_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
hs_csr ) ) {
err ( " Could not set LOO for slot %s " ,
2008-10-21 03:41:17 +04:00
hotplug_slot_name ( slot - > hotplug_slot ) ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
}
return 0 ;
}
int cpci_led_off ( struct slot * slot )
{
int hs_cap ;
u16 hs_csr ;
hs_cap = pci_bus_find_capability ( slot - > bus ,
slot - > devfn ,
PCI_CAP_ID_CHSWP ) ;
2005-05-28 00:48:52 +04:00
if ( ! hs_cap )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_read_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
2005-05-28 00:48:52 +04:00
& hs_csr ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-05-28 00:48:52 +04:00
if ( hs_csr & HS_CSR_LOO ) {
2005-04-17 02:20:36 +04:00
hs_csr & = ~ HS_CSR_LOO ;
2005-05-28 00:48:52 +04:00
if ( pci_bus_write_config_word ( slot - > bus ,
2005-04-17 02:20:36 +04:00
slot - > devfn ,
hs_cap + 2 ,
hs_csr ) ) {
err ( " Could not clear LOO for slot %s " ,
2008-10-21 03:41:17 +04:00
hotplug_slot_name ( slot - > hotplug_slot ) ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
}
return 0 ;
}
/*
* Device configuration functions
*/
2008-02-17 12:45:28 +03:00
int __ref cpci_configure_slot ( struct slot * slot )
2005-04-17 02:20:36 +04:00
{
2013-01-15 07:12:20 +04:00
struct pci_dev * dev ;
2006-08-23 03:55:57 +04:00
struct pci_bus * parent ;
2014-01-14 23:03:14 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2008-03-04 06:09:46 +03:00
dbg ( " %s - enter " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2014-01-14 23:03:14 +04:00
pci_lock_rescan_remove ( ) ;
2005-05-28 00:48:52 +04:00
if ( slot - > dev = = NULL ) {
2005-04-17 02:20:36 +04:00
dbg ( " pci_dev null, finding %02x:%02x:%x " ,
slot - > bus - > number , PCI_SLOT ( slot - > devfn ) , PCI_FUNC ( slot - > devfn ) ) ;
2005-05-28 00:48:52 +04:00
slot - > dev = pci_get_slot ( slot - > bus , slot - > devfn ) ;
2005-04-17 02:20:36 +04:00
}
/* Still NULL? Well then scan for it! */
2005-05-28 00:48:52 +04:00
if ( slot - > dev = = NULL ) {
2005-04-17 02:20:36 +04:00
int n ;
dbg ( " pci_dev still null " ) ;
/*
* This will generate pci_dev structures for all functions , but
* we will only call this case when lookup fails .
*/
n = pci_scan_slot ( slot - > bus , slot - > devfn ) ;
2008-03-04 06:09:46 +03:00
dbg ( " %s: pci_scan_slot returned %d " , __func__ , n ) ;
2005-05-28 00:48:52 +04:00
slot - > dev = pci_get_slot ( slot - > bus , slot - > devfn ) ;
if ( slot - > dev = = NULL ) {
2005-04-17 02:20:36 +04:00
err ( " Could not find PCI device for slot %02x " , slot - > number ) ;
2014-01-14 23:03:14 +04:00
ret = - ENODEV ;
goto out ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-23 03:55:57 +04:00
parent = slot - > dev - > bus ;
2013-01-15 07:12:20 +04:00
list_for_each_entry ( dev , & parent - > devices , bus_list )
if ( PCI_SLOT ( dev - > devfn ) ! = PCI_SLOT ( slot - > devfn ) )
2006-08-23 03:55:57 +04:00
continue ;
if ( ( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) | |
2012-05-18 05:58:41 +04:00
( dev - > hdr_type = = PCI_HEADER_TYPE_CARDBUS ) )
pci_hp_add_bridge ( dev ) ;
2013-01-15 07:12:20 +04:00
2005-04-17 02:20:36 +04:00
2012-05-18 05:58:41 +04:00
pci_assign_unassigned_bridge_resources ( parent - > self ) ;
2006-08-23 03:55:57 +04:00
pci_bus_add_devices ( parent ) ;
2005-05-10 01:31:50 +04:00
2014-01-14 23:03:14 +04:00
out :
pci_unlock_rescan_remove ( ) ;
2008-03-04 06:09:46 +03:00
dbg ( " %s - exit " , __func__ ) ;
2014-01-14 23:03:14 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
int cpci_unconfigure_slot ( struct slot * slot )
{
2013-01-15 07:12:20 +04:00
struct pci_dev * dev , * temp ;
2005-04-17 02:20:36 +04:00
2008-03-04 06:09:46 +03:00
dbg ( " %s - enter " , __func__ ) ;
2005-05-28 00:48:52 +04:00
if ( ! slot - > dev ) {
2005-04-17 02:20:36 +04:00
err ( " No device for slot %02x \n " , slot - > number ) ;
return - ENODEV ;
}
2014-01-14 23:03:14 +04:00
pci_lock_rescan_remove ( ) ;
2013-01-15 07:12:20 +04:00
list_for_each_entry_safe ( dev , temp , & slot - > bus - > devices , bus_list ) {
if ( PCI_SLOT ( dev - > devfn ) ! = PCI_SLOT ( slot - > devfn ) )
continue ;
pci_dev_get ( dev ) ;
pci_stop_and_remove_bus_device ( dev ) ;
pci_dev_put ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-06 23:48:04 +04:00
pci_dev_put ( slot - > dev ) ;
slot - > dev = NULL ;
2014-01-14 23:03:14 +04:00
pci_unlock_rescan_remove ( ) ;
2008-03-04 06:09:46 +03:00
dbg ( " %s - exit " , __func__ ) ;
2005-05-10 01:31:50 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}