2005-04-16 15:20:36 -07: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-16 15:16:10 -07:00
* Send feedback to < greg @ kroah . com > , < kristen . c . accardi @ intel . com >
2005-04-16 15:20:36 -07:00
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/pci.h>
# include "../pci.h"
# include "shpchp.h"
2008-02-17 10:45:28 +01:00
int __ref shpchp_configure_device ( struct slot * p_slot )
2005-04-16 15:20:36 -07:00
{
2005-10-13 12:05:36 -07:00
struct pci_dev * dev ;
2008-10-23 11:52:12 +09:00
struct controller * ctrl = p_slot - > ctrl ;
2012-05-17 18:58:41 -07:00
struct pci_dev * bridge = ctrl - > pci_dev ;
struct pci_bus * parent = bridge - > subordinate ;
int num , fn ;
2005-10-13 12:05:36 -07:00
2005-11-25 12:21:25 +09:00
dev = pci_get_slot ( parent , PCI_DEVFN ( p_slot - > device , 0 ) ) ;
2005-10-13 12:05:36 -07:00
if ( dev ) {
2008-10-23 11:54:39 +09:00
ctrl_err ( ctrl , " Device %s already exists "
" at %04x:%02x:%02x, cannot hot-add \n " , pci_name ( dev ) ,
pci_domain_nr ( parent ) , p_slot - > bus , p_slot - > device ) ;
2005-11-25 12:21:25 +09:00
pci_dev_put ( dev ) ;
2005-10-13 12:05:36 -07:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2005-10-13 12:05:36 -07:00
num = pci_scan_slot ( parent , PCI_DEVFN ( p_slot - > device , 0 ) ) ;
if ( num = = 0 ) {
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl , " No new device found \n " ) ;
2005-10-13 12:05:36 -07:00
return - ENODEV ;
}
2005-04-16 15:20:36 -07:00
2005-10-13 12:05:36 -07:00
for ( fn = 0 ; fn < 8 ; fn + + ) {
2005-11-25 12:21:25 +09:00
dev = pci_get_slot ( parent , PCI_DEVFN ( p_slot - > device , fn ) ) ;
if ( ! dev )
2005-10-13 12:05:36 -07:00
continue ;
if ( ( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) | |
2012-05-17 18:58:41 -07:00
( dev - > hdr_type = = PCI_HEADER_TYPE_CARDBUS ) )
pci_hp_add_bridge ( dev ) ;
pci_dev_put ( dev ) ;
}
pci_assign_unassigned_bridge_resources ( bridge ) ;
for ( fn = 0 ; fn < 8 ; fn + + ) {
dev = pci_get_slot ( parent , PCI_DEVFN ( p_slot - > device , fn ) ) ;
if ( ! dev )
continue ;
2009-09-14 16:35:30 -06:00
pci_configure_slot ( dev ) ;
2005-11-25 12:21:25 +09:00
pci_dev_put ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2005-10-13 12:05:36 -07:00
pci_bus_add_devices ( parent ) ;
2012-05-17 18:58:41 -07:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-10-13 12:05:41 -07:00
int shpchp_unconfigure_device ( struct slot * p_slot )
2005-04-16 15:20:36 -07:00
{
int rc = 0 ;
int j ;
2005-10-13 12:05:41 -07:00
u8 bctl = 0 ;
2005-11-25 12:21:25 +09:00
struct pci_bus * parent = p_slot - > ctrl - > pci_dev - > subordinate ;
2008-10-23 11:52:12 +09:00
struct controller * ctrl = p_slot - > ctrl ;
2005-11-25 12:21:25 +09:00
2008-10-23 11:54:39 +09:00
ctrl_dbg ( ctrl , " %s: domain:bus:dev = %04x:%02x:%02x \n " ,
__func__ , pci_domain_nr ( parent ) , p_slot - > bus , p_slot - > device ) ;
2005-04-16 15:20:36 -07:00
2010-05-20 12:15:01 -05:00
for ( j = 0 ; j < 8 ; j + + ) {
struct pci_dev * temp = pci_get_slot ( parent ,
2005-10-13 12:05:41 -07:00
( p_slot - > device < < 3 ) | j ) ;
if ( ! temp )
continue ;
if ( temp - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) {
pci_read_config_byte ( temp , PCI_BRIDGE_CONTROL , & bctl ) ;
if ( bctl & PCI_BRIDGE_CTL_VGA ) {
2008-10-23 11:52:12 +09:00
ctrl_err ( ctrl ,
" Cannot remove display device %s \n " ,
pci_name ( temp ) ) ;
2005-11-25 12:21:25 +09:00
pci_dev_put ( temp ) ;
2010-05-20 12:15:01 -05:00
rc = - EINVAL ;
break ;
2005-10-13 12:05:41 -07:00
}
2005-04-16 15:20:36 -07:00
}
2012-02-25 13:54:20 -08:00
pci_stop_and_remove_bus_device ( temp ) ;
2005-11-25 12:21:25 +09:00
pci_dev_put ( temp ) ;
2005-04-16 15:20:36 -07:00
}
return rc ;
}