2005-04-17 02:20:36 +04:00
/*
* PCI Express 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>
# include "../pci.h"
# include "pciehp.h"
2008-02-17 12:45:28 +03:00
static int __ref pciehp_add_bridge ( struct pci_dev * dev )
2005-12-08 23:12:25 +03:00
{
struct pci_bus * parent = dev - > bus ;
int pass , busnr , start = parent - > secondary ;
int end = parent - > subordinate ;
for ( busnr = start ; busnr < = end ; busnr + + ) {
if ( ! pci_find_bus ( pci_domain_nr ( parent ) , busnr ) )
break ;
}
if ( busnr - - > end ) {
err ( " No bus number available for hot-added bridge %s \n " ,
pci_name ( dev ) ) ;
return - 1 ;
}
for ( pass = 0 ; pass < 2 ; pass + + )
busnr = pci_scan_bridge ( parent , dev , busnr , pass ) ;
if ( ! dev - > subordinate )
return - 1 ;
2010-01-22 12:02:26 +03:00
2005-12-08 23:12:25 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2005-11-01 03:20:06 +03:00
int pciehp_configure_device ( struct slot * p_slot )
2005-04-17 02:20:36 +04:00
{
2005-11-01 03:20:06 +03:00
struct pci_dev * dev ;
2010-01-22 12:02:26 +03:00
struct pci_dev * bridge = p_slot - > ctrl - > pcie - > port ;
struct pci_bus * parent = bridge - > subordinate ;
2005-11-01 03:20:06 +03:00
int num , fn ;
2008-09-05 07:11:26 +04:00
struct controller * ctrl = p_slot - > ctrl ;
2005-11-01 03:20:06 +03:00
2009-09-15 12:26:56 +04:00
dev = pci_get_slot ( parent , PCI_DEVFN ( 0 , 0 ) ) ;
2005-11-01 03:20:06 +03:00
if ( dev ) {
2008-10-23 06:47:32 +04:00
ctrl_err ( ctrl , " Device %s already exists "
2009-09-15 12:26:56 +04:00
" at %04x:%02x:00, cannot hot-add \n " , pci_name ( dev ) ,
pci_domain_nr ( parent ) , parent - > number ) ;
2006-05-12 06:22:24 +04:00
pci_dev_put ( dev ) ;
2005-11-01 03:20:06 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2009-09-15 12:26:56 +04:00
num = pci_scan_slot ( parent , PCI_DEVFN ( 0 , 0 ) ) ;
2005-11-01 03:20:06 +03:00
if ( num = = 0 ) {
2008-09-05 07:11:26 +04:00
ctrl_err ( ctrl , " No new device found \n " ) ;
2005-11-01 03:20:06 +03:00
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
2005-11-01 03:20:06 +03:00
for ( fn = 0 ; fn < 8 ; fn + + ) {
2009-09-15 12:26:56 +04:00
dev = pci_get_slot ( parent , PCI_DEVFN ( 0 , fn ) ) ;
2005-12-08 23:12:25 +03:00
if ( ! dev )
2005-11-01 03:20:06 +03:00
continue ;
if ( ( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) | |
( dev - > hdr_type = = PCI_HEADER_TYPE_CARDBUS ) ) {
2005-12-08 23:12:25 +03:00
pciehp_add_bridge ( dev ) ;
2005-11-01 03:20:06 +03:00
}
2010-01-22 12:02:26 +03:00
pci_dev_put ( dev ) ;
}
pci_assign_unassigned_bridge_resources ( bridge ) ;
for ( fn = 0 ; fn < 8 ; fn + + ) {
dev = pci_get_slot ( parent , PCI_DEVFN ( 0 , fn ) ) ;
if ( ! dev )
continue ;
if ( ( dev - > class > > 16 ) = = PCI_BASE_CLASS_DISPLAY ) {
pci_dev_put ( dev ) ;
continue ;
}
2009-09-15 02:35:25 +04:00
pci_configure_slot ( dev ) ;
2006-05-12 06:23:39 +04:00
pci_dev_put ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-01 03:20:06 +03:00
pci_bus_add_devices ( parent ) ;
2010-01-22 12:02:26 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-01 03:20:08 +03:00
int pciehp_unconfigure_device ( struct slot * p_slot )
2005-04-17 02:20:36 +04:00
{
2007-08-10 03:09:31 +04:00
int ret , rc = 0 ;
2005-04-17 02:20:36 +04:00
int j ;
2005-11-01 03:20:08 +03:00
u8 bctl = 0 ;
2007-08-10 03:09:31 +04:00
u8 presence = 0 ;
2009-09-15 12:30:14 +04:00
struct pci_bus * parent = p_slot - > ctrl - > pcie - > port - > subordinate ;
2007-12-20 13:46:33 +03:00
u16 command ;
2008-09-05 07:11:26 +04:00
struct controller * ctrl = p_slot - > ctrl ;
2005-04-17 02:20:36 +04:00
2009-09-15 12:26:56 +04:00
ctrl_dbg ( ctrl , " %s: domain:bus:dev = %04x:%02x:00 \n " ,
__func__ , pci_domain_nr ( parent ) , parent - > number ) ;
2009-09-15 12:30:48 +04:00
ret = pciehp_get_adapter_status ( p_slot , & presence ) ;
2007-12-20 13:46:33 +03:00
if ( ret )
presence = 0 ;
2005-04-17 02:20:36 +04:00
2007-11-06 15:20:17 +03:00
for ( j = 0 ; j < 8 ; j + + ) {
2010-05-20 02:03:12 +04:00
struct pci_dev * temp = pci_get_slot ( parent , PCI_DEVFN ( 0 , j ) ) ;
2005-11-01 03:20:08 +03:00
if ( ! temp )
continue ;
2007-12-20 13:46:33 +03:00
if ( temp - > hdr_type = = PCI_HEADER_TYPE_BRIDGE & & presence ) {
pci_read_config_byte ( temp , PCI_BRIDGE_CONTROL , & bctl ) ;
if ( bctl & PCI_BRIDGE_CTL_VGA ) {
2008-09-05 07:11:26 +04:00
ctrl_err ( ctrl ,
" Cannot remove display device %s \n " ,
pci_name ( temp ) ) ;
2007-12-20 13:46:33 +03:00
pci_dev_put ( temp ) ;
2010-05-21 00:32:22 +04:00
rc = - EINVAL ;
2010-05-20 02:03:12 +04:00
break ;
2005-11-01 03:20:08 +03:00
}
2005-04-17 02:20:36 +04:00
}
2005-11-01 03:20:08 +03:00
pci_remove_bus_device ( temp ) ;
2007-12-20 13:46:33 +03:00
/*
* Ensure that no new Requests will be generated from
* the device .
*/
if ( presence ) {
pci_read_config_word ( temp , PCI_COMMAND , & command ) ;
command & = ~ ( PCI_COMMAND_MASTER | PCI_COMMAND_SERR ) ;
command | = PCI_COMMAND_INTX_DISABLE ;
pci_write_config_word ( temp , PCI_COMMAND , command ) ;
}
2006-05-12 06:22:24 +04:00
pci_dev_put ( temp ) ;
2005-04-17 02:20:36 +04:00
}
2007-08-10 03:09:37 +04:00
2005-04-17 02:20:36 +04:00
return rc ;
}