2008-06-11 01:28:50 +04:00
/*
* drivers / pci / slot . c
* Copyright ( C ) 2006 Matthew Wilcox < matthew @ wil . cx >
* Copyright ( C ) 2006 - 2008 Hewlett - Packard Development Company , L . P .
* Alex Chiang < achiang @ hp . com >
*/
# include <linux/kobject.h>
# include <linux/pci.h>
# include <linux/err.h>
# include "pci.h"
struct kset * pci_slots_kset ;
EXPORT_SYMBOL_GPL ( pci_slots_kset ) ;
static ssize_t pci_slot_attr_show ( struct kobject * kobj ,
struct attribute * attr , char * buf )
{
struct pci_slot * slot = to_pci_slot ( kobj ) ;
struct pci_slot_attribute * attribute = to_pci_slot_attr ( attr ) ;
return attribute - > show ? attribute - > show ( slot , buf ) : - EIO ;
}
static ssize_t pci_slot_attr_store ( struct kobject * kobj ,
struct attribute * attr , const char * buf , size_t len )
{
struct pci_slot * slot = to_pci_slot ( kobj ) ;
struct pci_slot_attribute * attribute = to_pci_slot_attr ( attr ) ;
return attribute - > store ? attribute - > store ( slot , buf , len ) : - EIO ;
}
static struct sysfs_ops pci_slot_sysfs_ops = {
. show = pci_slot_attr_show ,
. store = pci_slot_attr_store ,
} ;
static ssize_t address_read_file ( struct pci_slot * slot , char * buf )
{
if ( slot - > number = = 0xff )
return sprintf ( buf , " %04x:%02x \n " ,
pci_domain_nr ( slot - > bus ) ,
slot - > bus - > number ) ;
else
return sprintf ( buf , " %04x:%02x:%02x \n " ,
pci_domain_nr ( slot - > bus ) ,
slot - > bus - > number ,
slot - > number ) ;
}
static void pci_slot_release ( struct kobject * kobj )
{
2008-09-02 19:40:51 +04:00
struct pci_dev * dev ;
2008-06-11 01:28:50 +04:00
struct pci_slot * slot = to_pci_slot ( kobj ) ;
pr_debug ( " %s: releasing pci_slot on %x:%d \n " , __func__ ,
slot - > bus - > number , slot - > number ) ;
2008-09-02 19:40:51 +04:00
list_for_each_entry ( dev , & slot - > bus - > devices , bus_list )
if ( PCI_SLOT ( dev - > devfn ) = = slot - > number )
dev - > slot = NULL ;
2008-06-11 01:28:50 +04:00
list_del ( & slot - > list ) ;
kfree ( slot ) ;
}
static struct pci_slot_attribute pci_slot_attr_address =
__ATTR ( address , ( S_IFREG | S_IRUGO ) , address_read_file , NULL ) ;
static struct attribute * pci_slot_default_attrs [ ] = {
& pci_slot_attr_address . attr ,
NULL ,
} ;
static struct kobj_type pci_slot_ktype = {
. sysfs_ops = & pci_slot_sysfs_ops ,
. release = & pci_slot_release ,
. default_attrs = pci_slot_default_attrs ,
} ;
/**
* pci_create_slot - create or increment refcount for physical PCI slot
* @ parent : struct pci_bus of parent bridge
* @ slot_nr : PCI_SLOT ( pci_dev - > devfn ) or - 1 for placeholder
* @ name : user visible string presented in / sys / bus / pci / slots / < name >
*
* PCI slots have first class attributes such as address , speed , width ,
* and a & struct pci_slot is used to manage them . This interface will
* either return a new & struct pci_slot to the caller , or if the pci_slot
* already exists , its refcount will be incremented .
*
* Slots are uniquely identified by a @ pci_bus , @ slot_nr , @ name tuple .
*
* Placeholder slots :
* In most cases , @ pci_bus , @ slot_nr will be sufficient to uniquely identify
* a slot . There is one notable exception - pSeries ( rpaphp ) , where the
* @ slot_nr cannot be determined until a device is actually inserted into
* the slot . In this scenario , the caller may pass - 1 for @ slot_nr .
*
* The following semantics are imposed when the caller passes @ slot_nr = =
* - 1. First , the check for existing % struct pci_slot is skipped , as the
* caller may know about several unpopulated slots on a given % struct
* pci_bus , and each slot would have a @ slot_nr of - 1. Uniqueness for
* these slots is then determined by the @ name parameter . We expect
* kobject_init_and_add ( ) to warn us if the caller attempts to create
* multiple slots with the same name . The other change in semantics is
* user - visible , which is the ' address ' parameter presented in sysfs will
* consist solely of a dddd : bb tuple , where dddd is the PCI domain of the
* % struct pci_bus and bb is the bus number . In other words , the devfn of
* the ' placeholder ' slot will not be displayed .
*/
struct pci_slot * pci_create_slot ( struct pci_bus * parent , int slot_nr ,
const char * name )
{
2008-09-02 19:40:51 +04:00
struct pci_dev * dev ;
2008-06-11 01:28:50 +04:00
struct pci_slot * slot ;
int err ;
down_write ( & pci_bus_sem ) ;
if ( slot_nr = = - 1 )
goto placeholder ;
/* If we've already created this slot, bump refcount and return. */
list_for_each_entry ( slot , & parent - > slots , list ) {
if ( slot - > number = = slot_nr ) {
kobject_get ( & slot - > kobj ) ;
pr_debug ( " %s: inc refcount to %d on %04x:%02x:%02x \n " ,
__func__ ,
atomic_read ( & slot - > kobj . kref . refcount ) ,
pci_domain_nr ( parent ) , parent - > number ,
slot_nr ) ;
goto out ;
}
}
placeholder :
slot = kzalloc ( sizeof ( * slot ) , GFP_KERNEL ) ;
if ( ! slot ) {
slot = ERR_PTR ( - ENOMEM ) ;
goto out ;
}
slot - > bus = parent ;
slot - > number = slot_nr ;
slot - > kobj . kset = pci_slots_kset ;
err = kobject_init_and_add ( & slot - > kobj , & pci_slot_ktype , NULL ,
" %s " , name ) ;
if ( err ) {
printk ( KERN_ERR " Unable to register kobject %s \n " , name ) ;
goto err ;
}
INIT_LIST_HEAD ( & slot - > list ) ;
list_add ( & slot - > list , & parent - > slots ) ;
2008-09-02 19:40:51 +04:00
list_for_each_entry ( dev , & parent - > devices , bus_list )
if ( PCI_SLOT ( dev - > devfn ) = = slot_nr )
dev - > slot = slot ;
2008-06-11 01:28:50 +04:00
/* Don't care if debug printk has a -1 for slot_nr */
pr_debug ( " %s: created pci_slot on %04x:%02x:%02x \n " ,
__func__ , pci_domain_nr ( parent ) , parent - > number , slot_nr ) ;
out :
up_write ( & pci_bus_sem ) ;
return slot ;
err :
kfree ( slot ) ;
slot = ERR_PTR ( err ) ;
goto out ;
}
EXPORT_SYMBOL_GPL ( pci_create_slot ) ;
/**
2008-10-21 03:40:47 +04:00
* pci_renumber_slot - update % struct pci_slot - > number
2008-06-11 01:28:50 +04:00
* @ slot - % struct pci_slot to update
* @ slot_nr - new number for slot
*
* The primary purpose of this interface is to allow callers who earlier
* created a placeholder slot in pci_create_slot ( ) by passing a - 1 as
* slot_nr , to update their % struct pci_slot with the correct @ slot_nr .
*/
2008-10-21 03:40:47 +04:00
void pci_renumber_slot ( struct pci_slot * slot , int slot_nr )
2008-06-11 01:28:50 +04:00
{
struct pci_slot * tmp ;
down_write ( & pci_bus_sem ) ;
list_for_each_entry ( tmp , & slot - > bus - > slots , list ) {
WARN_ON ( tmp - > number = = slot_nr ) ;
2008-10-21 03:40:47 +04:00
goto out ;
2008-06-11 01:28:50 +04:00
}
slot - > number = slot_nr ;
2008-10-21 03:40:47 +04:00
out :
2008-06-11 01:28:50 +04:00
up_write ( & pci_bus_sem ) ;
}
2008-10-21 03:40:47 +04:00
EXPORT_SYMBOL_GPL ( pci_renumber_slot ) ;
2008-06-11 01:28:50 +04:00
/**
* pci_destroy_slot - decrement refcount for physical PCI slot
* @ slot : struct pci_slot to decrement
*
* % struct pci_slot is refcounted , so destroying them is really easy ; we
* just call kobject_put on its kobj and let our release methods do the
* rest .
*/
void pci_destroy_slot ( struct pci_slot * slot )
{
pr_debug ( " %s: dec refcount to %d on %04x:%02x:%02x \n " , __func__ ,
atomic_read ( & slot - > kobj . kref . refcount ) - 1 ,
pci_domain_nr ( slot - > bus ) , slot - > bus - > number , slot - > number ) ;
down_write ( & pci_bus_sem ) ;
kobject_put ( & slot - > kobj ) ;
up_write ( & pci_bus_sem ) ;
}
EXPORT_SYMBOL_GPL ( pci_destroy_slot ) ;
static int pci_slot_init ( void )
{
struct kset * pci_bus_kset ;
pci_bus_kset = bus_get_kset ( & pci_bus_type ) ;
pci_slots_kset = kset_create_and_add ( " slots " , NULL ,
& pci_bus_kset - > kobj ) ;
if ( ! pci_slots_kset ) {
printk ( KERN_ERR " PCI: Slot initialization failure \n " ) ;
return - ENOMEM ;
}
return 0 ;
}
subsys_initcall ( pci_slot_init ) ;