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 " , \
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
{
2006-08-23 03:55:57 +04:00
struct pci_bus * parent ;
int fn ;
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
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 ) ;
2006-08-23 03:55:57 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-23 03:55:57 +04:00
parent = slot - > dev - > bus ;
for ( fn = 0 ; fn < 8 ; fn + + ) {
struct pci_dev * dev ;
dev = pci_get_slot ( parent , PCI_DEVFN ( PCI_SLOT ( slot - > devfn ) , fn ) ) ;
if ( ! dev )
continue ;
if ( ( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) | |
( dev - > hdr_type = = PCI_HEADER_TYPE_CARDBUS ) ) {
/* Find an unused bus number for the new bridge */
struct pci_bus * child ;
2012-05-18 05:51:11 +04:00
unsigned char busnr , start = parent - > busn_res . start ;
unsigned char end = parent - > busn_res . end ;
2012-05-18 05:51:13 +04:00
int max ;
2006-08-23 03:55:57 +04:00
for ( busnr = start ; busnr < = end ; busnr + + ) {
if ( ! pci_find_bus ( pci_domain_nr ( parent ) ,
busnr ) )
break ;
}
if ( busnr > = end ) {
err ( " No free bus for hot-added bridge \n " ) ;
pci_dev_put ( dev ) ;
continue ;
}
child = pci_add_new_bus ( parent , dev , busnr ) ;
if ( ! child ) {
err ( " Cannot add new bus for %s \n " ,
pci_name ( dev ) ) ;
pci_dev_put ( dev ) ;
continue ;
}
2012-05-18 05:51:13 +04:00
max = pci_do_scan_bus ( child ) ;
pci_bus_update_busn_res_end ( child , max ) ;
2006-08-23 03:55:57 +04:00
pci_bus_size_bridges ( child ) ;
}
pci_dev_put ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-23 03:55:57 +04:00
pci_bus_assign_resources ( parent ) ;
pci_bus_add_devices ( parent ) ;
pci_enable_bridges ( parent ) ;
2005-05-10 01:31:50 +04:00
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
}
int cpci_unconfigure_slot ( struct slot * slot )
{
int i ;
struct pci_dev * dev ;
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 ;
}
for ( i = 0 ; i < 8 ; i + + ) {
2005-05-28 00:48:52 +04:00
dev = pci_get_slot ( slot - > bus ,
2005-04-17 02:20:36 +04:00
PCI_DEVFN ( PCI_SLOT ( slot - > devfn ) , i ) ) ;
2005-05-28 00:48:52 +04:00
if ( dev ) {
2012-02-26 01:54:20 +04:00
pci_stop_and_remove_bus_device ( dev ) ;
2005-06-06 23:48:04 +04:00
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 ;
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
}