2005-04-17 02:20:36 +04:00
/*
* platform . c - platform ' pseudo ' bus for legacy devices
*
* Copyright ( c ) 2002 - 3 Patrick Mochel
* Copyright ( c ) 2002 - 3 Open Source Development Labs
*
* This file is released under the GPLv2
*
* Please see Documentation / driver - model / platform . txt for more
* information .
*/
2009-08-07 03:00:44 +04:00
# include <linux/string.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2010-06-08 17:48:20 +04:00
# include <linux/of_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/dma-mapping.h>
# include <linux/bootmem.h>
# include <linux/err.h>
2005-10-31 02:03:48 +03:00
# include <linux/slab.h>
2009-08-20 22:25:32 +04:00
# include <linux/pm_runtime.h>
2005-04-17 02:20:36 +04:00
2005-10-13 20:54:41 +04:00
# include "base.h"
2008-01-25 09:50:12 +03:00
# define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
driver ) )
2005-11-09 20:23:39 +03:00
2005-04-17 02:20:36 +04:00
struct device platform_bus = {
2008-10-30 03:36:48 +03:00
. init_name = " platform " ,
2005-04-17 02:20:36 +04:00
} ;
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_bus ) ;
2005-04-17 02:20:36 +04:00
2011-06-10 10:52:57 +04:00
/**
* arch_setup_pdev_archdata - Allow manipulation of archdata before its used
2011-07-27 23:11:25 +04:00
* @ pdev : platform device
2011-06-10 10:52:57 +04:00
*
* This is called before platform_device_add ( ) such that any pdev_archdata may
* be setup before the platform_notifier is called . So if a user needs to
* manipulate any relevant information in the pdev_archdata they can do :
*
* platform_devic_alloc ( )
* . . . manipulate . . .
* platform_device_add ( )
*
* And if they don ' t care they can just call platform_device_register ( ) and
* everything will just work out .
*/
void __weak arch_setup_pdev_archdata ( struct platform_device * pdev )
{
}
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* platform_get_resource - get a resource for a device
* @ dev : platform device
* @ type : resource type
* @ num : resource index
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
struct resource * platform_get_resource ( struct platform_device * dev ,
unsigned int type , unsigned int num )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( i = 0 ; i < dev - > num_resources ; i + + ) {
struct resource * r = & dev - > resource [ i ] ;
2008-10-16 09:05:15 +04:00
if ( type = = resource_type ( r ) & & num - - = = 0 )
return r ;
2005-04-17 02:20:36 +04:00
}
return NULL ;
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_get_resource ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* platform_get_irq - get an IRQ for a device
* @ dev : platform device
* @ num : IRQ number index
2005-04-17 02:20:36 +04:00
*/
int platform_get_irq ( struct platform_device * dev , unsigned int num )
{
struct resource * r = platform_get_resource ( dev , IORESOURCE_IRQ , num ) ;
2006-01-19 20:52:27 +03:00
return r ? r - > start : - ENXIO ;
2005-04-17 02:20:36 +04:00
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_get_irq ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* platform_get_resource_byname - get a resource for a device by name
* @ dev : platform device
* @ type : resource type
* @ name : resource name
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
struct resource * platform_get_resource_byname ( struct platform_device * dev ,
2009-04-27 04:38:16 +04:00
unsigned int type ,
const char * name )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( i = 0 ; i < dev - > num_resources ; i + + ) {
struct resource * r = & dev - > resource [ i ] ;
2008-10-16 09:05:15 +04:00
if ( type = = resource_type ( r ) & & ! strcmp ( r - > name , name ) )
return r ;
2005-04-17 02:20:36 +04:00
}
return NULL ;
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_get_resource_byname ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* platform_get_irq - get an IRQ for a device
* @ dev : platform device
* @ name : IRQ name
2005-04-17 02:20:36 +04:00
*/
2009-04-27 04:38:16 +04:00
int platform_get_irq_byname ( struct platform_device * dev , const char * name )
2005-04-17 02:20:36 +04:00
{
2008-01-25 09:50:12 +03:00
struct resource * r = platform_get_resource_byname ( dev , IORESOURCE_IRQ ,
name ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 20:52:27 +03:00
return r ? r - > start : - ENXIO ;
2005-04-17 02:20:36 +04:00
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_get_irq_byname ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* platform_add_devices - add a numbers of platform devices
* @ devs : array of platform devices to add
* @ num : number of platform devices in array
2005-04-17 02:20:36 +04:00
*/
int platform_add_devices ( struct platform_device * * devs , int num )
{
int i , ret = 0 ;
for ( i = 0 ; i < num ; i + + ) {
ret = platform_device_register ( devs [ i ] ) ;
if ( ret ) {
while ( - - i > = 0 )
platform_device_unregister ( devs [ i ] ) ;
break ;
}
}
return ret ;
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_add_devices ) ;
2005-04-17 02:20:36 +04:00
2005-11-06 00:19:33 +03:00
struct platform_object {
struct platform_device pdev ;
char name [ 1 ] ;
} ;
2005-04-17 02:20:36 +04:00
/**
2010-02-14 17:18:53 +03:00
* platform_device_put - destroy a platform device
2008-01-25 09:50:12 +03:00
* @ pdev : platform device to free
2005-11-06 00:19:33 +03:00
*
2008-01-25 09:50:12 +03:00
* Free all memory associated with a platform device . This function must
* _only_ be externally called in error cases . All other usage is a bug .
2005-11-06 00:19:33 +03:00
*/
void platform_device_put ( struct platform_device * pdev )
{
if ( pdev )
put_device ( & pdev - > dev ) ;
}
EXPORT_SYMBOL_GPL ( platform_device_put ) ;
static void platform_device_release ( struct device * dev )
{
2008-01-25 09:50:12 +03:00
struct platform_object * pa = container_of ( dev , struct platform_object ,
pdev . dev ) ;
2005-11-06 00:19:33 +03:00
2010-10-20 21:45:13 +04:00
of_device_node_put ( & pa - > pdev . dev ) ;
2005-11-06 00:19:33 +03:00
kfree ( pa - > pdev . dev . platform_data ) ;
2011-04-08 02:43:01 +04:00
kfree ( pa - > pdev . mfd_cell ) ;
2005-11-06 00:19:33 +03:00
kfree ( pa - > pdev . resource ) ;
kfree ( pa ) ;
}
/**
2010-02-14 17:18:53 +03:00
* platform_device_alloc - create a platform device
2008-01-25 09:50:12 +03:00
* @ name : base name of the device we ' re adding
* @ id : instance id
2005-11-06 00:19:33 +03:00
*
2008-01-25 09:50:12 +03:00
* Create a platform device object which can have other objects attached
* to it , and which will have attached objects freed when it is released .
2005-11-06 00:19:33 +03:00
*/
2007-09-09 14:54:16 +04:00
struct platform_device * platform_device_alloc ( const char * name , int id )
2005-11-06 00:19:33 +03:00
{
struct platform_object * pa ;
pa = kzalloc ( sizeof ( struct platform_object ) + strlen ( name ) , GFP_KERNEL ) ;
if ( pa ) {
strcpy ( pa - > name , name ) ;
pa - > pdev . name = pa - > name ;
pa - > pdev . id = id ;
device_initialize ( & pa - > pdev . dev ) ;
pa - > pdev . dev . release = platform_device_release ;
2011-06-10 10:52:57 +04:00
arch_setup_pdev_archdata ( & pa - > pdev ) ;
2005-11-06 00:19:33 +03:00
}
2005-12-10 09:36:27 +03:00
return pa ? & pa - > pdev : NULL ;
2005-11-06 00:19:33 +03:00
}
EXPORT_SYMBOL_GPL ( platform_device_alloc ) ;
/**
2010-02-14 17:18:53 +03:00
* platform_device_add_resources - add resources to a platform device
2008-01-25 09:50:12 +03:00
* @ pdev : platform device allocated by platform_device_alloc to add resources to
* @ res : set of resources that needs to be allocated for the device
* @ num : number of resources
2005-11-06 00:19:33 +03:00
*
2008-01-25 09:50:12 +03:00
* Add a copy of the resources to the platform device . The memory
* associated with the resources will be freed when the platform device is
* released .
2005-11-06 00:19:33 +03:00
*/
2008-01-25 09:50:12 +03:00
int platform_device_add_resources ( struct platform_device * pdev ,
2009-01-28 23:01:02 +03:00
const struct resource * res , unsigned int num )
2005-11-06 00:19:33 +03:00
{
2011-04-20 11:44:44 +04:00
struct resource * r = NULL ;
2005-11-06 00:19:33 +03:00
2011-04-20 11:44:44 +04:00
if ( res ) {
r = kmemdup ( res , sizeof ( struct resource ) * num , GFP_KERNEL ) ;
if ( ! r )
return - ENOMEM ;
2005-11-06 00:19:33 +03:00
}
2011-04-20 11:44:44 +04:00
2011-04-20 11:44:45 +04:00
kfree ( pdev - > resource ) ;
2011-04-20 11:44:44 +04:00
pdev - > resource = r ;
pdev - > num_resources = num ;
return 0 ;
2005-11-06 00:19:33 +03:00
}
EXPORT_SYMBOL_GPL ( platform_device_add_resources ) ;
/**
2010-02-14 17:18:53 +03:00
* platform_device_add_data - add platform - specific data to a platform device
2008-01-25 09:50:12 +03:00
* @ pdev : platform device allocated by platform_device_alloc to add resources to
* @ data : platform specific data for this platform device
* @ size : size of platform specific data
2005-11-06 00:19:33 +03:00
*
2008-01-25 09:50:12 +03:00
* Add a copy of platform specific data to the platform device ' s
* platform_data pointer . The memory associated with the platform data
* will be freed when the platform device is released .
2005-11-06 00:19:33 +03:00
*/
2008-01-25 09:50:12 +03:00
int platform_device_add_data ( struct platform_device * pdev , const void * data ,
size_t size )
2005-11-06 00:19:33 +03:00
{
2011-04-20 11:44:42 +04:00
void * d = NULL ;
2010-09-07 17:31:49 +04:00
2011-04-20 11:44:42 +04:00
if ( data ) {
d = kmemdup ( data , size , GFP_KERNEL ) ;
if ( ! d )
return - ENOMEM ;
2005-11-06 00:19:33 +03:00
}
2011-04-20 11:44:42 +04:00
2011-04-20 11:44:43 +04:00
kfree ( pdev - > dev . platform_data ) ;
2011-04-20 11:44:42 +04:00
pdev - > dev . platform_data = d ;
return 0 ;
2005-11-06 00:19:33 +03:00
}
EXPORT_SYMBOL_GPL ( platform_device_add_data ) ;
/**
2008-01-25 09:50:12 +03:00
* platform_device_add - add a platform device to device hierarchy
* @ pdev : platform device we ' re adding
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* This is part 2 of platform_device_register ( ) , though may be called
* separately _iff_ pdev was allocated by platform_device_alloc ( ) .
2005-04-17 02:20:36 +04:00
*/
2005-11-06 00:19:33 +03:00
int platform_device_add ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
int i , ret = 0 ;
if ( ! pdev )
return - EINVAL ;
if ( ! pdev - > dev . parent )
pdev - > dev . parent = & platform_bus ;
pdev - > dev . bus = & platform_bus_type ;
if ( pdev - > id ! = - 1 )
2008-10-30 03:36:48 +03:00
dev_set_name ( & pdev - > dev , " %s.%d " , pdev - > name , pdev - > id ) ;
2005-04-17 02:20:36 +04:00
else
2009-06-03 02:39:55 +04:00
dev_set_name ( & pdev - > dev , " %s " , pdev - > name ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < pdev - > num_resources ; i + + ) {
struct resource * p , * r = & pdev - > resource [ i ] ;
if ( r - > name = = NULL )
2008-10-30 03:36:48 +03:00
r - > name = dev_name ( & pdev - > dev ) ;
2005-04-17 02:20:36 +04:00
p = r - > parent ;
if ( ! p ) {
2008-10-16 09:05:15 +04:00
if ( resource_type ( r ) = = IORESOURCE_MEM )
2005-04-17 02:20:36 +04:00
p = & iomem_resource ;
2008-10-16 09:05:15 +04:00
else if ( resource_type ( r ) = = IORESOURCE_IO )
2005-04-17 02:20:36 +04:00
p = & ioport_resource ;
}
2005-11-28 19:15:39 +03:00
if ( p & & insert_resource ( p , r ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR
" %s: failed to claim resource %d \n " ,
2008-10-30 03:36:48 +03:00
dev_name ( & pdev - > dev ) , i ) ;
2005-04-17 02:20:36 +04:00
ret = - EBUSY ;
goto failed ;
}
}
pr_debug ( " Registering platform device '%s'. Parent at %s \n " ,
2008-10-30 03:36:48 +03:00
dev_name ( & pdev - > dev ) , dev_name ( pdev - > dev . parent ) ) ;
2005-04-17 02:20:36 +04:00
2006-05-06 11:15:26 +04:00
ret = device_add ( & pdev - > dev ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 )
return ret ;
failed :
2008-10-16 09:05:15 +04:00
while ( - - i > = 0 ) {
struct resource * r = & pdev - > resource [ i ] ;
unsigned long type = resource_type ( r ) ;
if ( type = = IORESOURCE_MEM | | type = = IORESOURCE_IO )
release_resource ( r ) ;
}
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-11-06 00:19:33 +03:00
EXPORT_SYMBOL_GPL ( platform_device_add ) ;
/**
2008-01-25 09:50:12 +03:00
* platform_device_del - remove a platform - level device
* @ pdev : platform device we ' re removing
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* Note that this function will also release all memory - and port - based
* resources owned by the device ( @ dev - > resource ) . This function must
* _only_ be externally called in error cases . All other usage is a bug .
2005-04-17 02:20:36 +04:00
*/
2005-12-10 09:36:27 +03:00
void platform_device_del ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
int i ;
if ( pdev ) {
2007-05-02 22:55:54 +04:00
device_del ( & pdev - > dev ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < pdev - > num_resources ; i + + ) {
struct resource * r = & pdev - > resource [ i ] ;
2008-10-16 09:05:15 +04:00
unsigned long type = resource_type ( r ) ;
if ( type = = IORESOURCE_MEM | | type = = IORESOURCE_IO )
2005-04-17 02:20:36 +04:00
release_resource ( r ) ;
}
}
}
2005-12-10 09:36:27 +03:00
EXPORT_SYMBOL_GPL ( platform_device_del ) ;
/**
2008-01-25 09:50:12 +03:00
* platform_device_register - add a platform - level device
* @ pdev : platform device we ' re adding
2005-12-10 09:36:27 +03:00
*/
2008-01-25 09:50:12 +03:00
int platform_device_register ( struct platform_device * pdev )
2005-12-10 09:36:27 +03:00
{
device_initialize ( & pdev - > dev ) ;
2011-06-10 10:52:57 +04:00
arch_setup_pdev_archdata ( pdev ) ;
2005-12-10 09:36:27 +03:00
return platform_device_add ( pdev ) ;
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_device_register ) ;
2005-12-10 09:36:27 +03:00
/**
2008-01-25 09:50:12 +03:00
* platform_device_unregister - unregister a platform - level device
* @ pdev : platform device we ' re unregistering
2005-12-10 09:36:27 +03:00
*
2008-01-25 09:50:12 +03:00
* Unregistration is done in 2 steps . First we release all resources
* and remove it from the subsystem , then we drop reference count by
* calling platform_device_put ( ) .
2005-12-10 09:36:27 +03:00
*/
2008-01-25 09:50:12 +03:00
void platform_device_unregister ( struct platform_device * pdev )
2005-12-10 09:36:27 +03:00
{
platform_device_del ( pdev ) ;
platform_device_put ( pdev ) ;
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_device_unregister ) ;
2005-04-17 02:20:36 +04:00
/**
2011-08-25 13:16:00 +04:00
* platform_device_register_full - add a platform - level device with
2010-06-21 18:11:44 +04:00
* resources and platform - specific data
2007-05-08 11:29:39 +04:00
*
2011-08-25 13:16:00 +04:00
* @ pdevinfo : data used to create device
2008-09-23 01:41:40 +04:00
*
2010-03-11 19:11:45 +03:00
* Returns & struct platform_device pointer on success , or ERR_PTR ( ) on error .
2008-09-23 01:41:40 +04:00
*/
2011-08-25 13:16:00 +04:00
struct platform_device * platform_device_register_full (
struct platform_device_info * pdevinfo )
2008-09-23 01:41:40 +04:00
{
2010-06-21 18:11:44 +04:00
int ret = - ENOMEM ;
2008-09-23 01:41:40 +04:00
struct platform_device * pdev ;
2011-08-25 13:16:00 +04:00
pdev = platform_device_alloc ( pdevinfo - > name , pdevinfo - > id ) ;
2010-06-21 18:11:44 +04:00
if ( ! pdev )
2011-08-25 13:16:00 +04:00
goto err_alloc ;
pdev - > dev . parent = pdevinfo - > parent ;
if ( pdevinfo - > dma_mask ) {
/*
* This memory isn ' t freed when the device is put ,
* I don ' t have a nice idea for that though . Conceptually
* dma_mask in struct device should not be a pointer .
* See http : //thread.gmane.org/gmane.linux.kernel.pci/9081
*/
pdev - > dev . dma_mask =
kmalloc ( sizeof ( * pdev - > dev . dma_mask ) , GFP_KERNEL ) ;
if ( ! pdev - > dev . dma_mask )
goto err ;
* pdev - > dev . dma_mask = pdevinfo - > dma_mask ;
pdev - > dev . coherent_dma_mask = pdevinfo - > dma_mask ;
}
2008-09-23 01:41:40 +04:00
2011-08-25 13:16:00 +04:00
ret = platform_device_add_resources ( pdev ,
pdevinfo - > res , pdevinfo - > num_res ) ;
2010-09-07 17:31:54 +04:00
if ( ret )
goto err ;
2010-06-21 18:11:44 +04:00
2011-08-25 13:16:00 +04:00
ret = platform_device_add_data ( pdev ,
pdevinfo - > data , pdevinfo - > size_data ) ;
2010-09-07 17:31:54 +04:00
if ( ret )
goto err ;
2008-09-23 01:41:40 +04:00
2010-06-21 18:11:44 +04:00
ret = platform_device_add ( pdev ) ;
if ( ret ) {
err :
2011-08-25 13:16:00 +04:00
kfree ( pdev - > dev . dma_mask ) ;
err_alloc :
2010-06-21 18:11:44 +04:00
platform_device_put ( pdev ) ;
return ERR_PTR ( ret ) ;
}
2008-09-23 01:41:40 +04:00
return pdev ;
}
2011-08-25 13:16:00 +04:00
EXPORT_SYMBOL_GPL ( platform_device_register_full ) ;
2008-09-23 01:41:40 +04:00
2005-11-09 20:23:39 +03:00
static int platform_drv_probe ( struct device * _dev )
{
struct platform_driver * drv = to_platform_driver ( _dev - > driver ) ;
struct platform_device * dev = to_platform_device ( _dev ) ;
return drv - > probe ( dev ) ;
}
2006-11-17 10:28:47 +03:00
static int platform_drv_probe_fail ( struct device * _dev )
{
return - ENXIO ;
}
2005-11-09 20:23:39 +03:00
static int platform_drv_remove ( struct device * _dev )
{
struct platform_driver * drv = to_platform_driver ( _dev - > driver ) ;
struct platform_device * dev = to_platform_device ( _dev ) ;
return drv - > remove ( dev ) ;
}
static void platform_drv_shutdown ( struct device * _dev )
{
struct platform_driver * drv = to_platform_driver ( _dev - > driver ) ;
struct platform_device * dev = to_platform_device ( _dev ) ;
drv - > shutdown ( dev ) ;
}
/**
2010-02-14 17:18:53 +03:00
* platform_driver_register - register a driver for platform - level devices
2008-01-25 09:50:12 +03:00
* @ drv : platform driver structure
2005-11-09 20:23:39 +03:00
*/
int platform_driver_register ( struct platform_driver * drv )
{
drv - > driver . bus = & platform_bus_type ;
if ( drv - > probe )
drv - > driver . probe = platform_drv_probe ;
if ( drv - > remove )
drv - > driver . remove = platform_drv_remove ;
if ( drv - > shutdown )
drv - > driver . shutdown = platform_drv_shutdown ;
2009-06-05 00:13:33 +04:00
2005-11-09 20:23:39 +03:00
return driver_register ( & drv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( platform_driver_register ) ;
/**
2010-02-14 17:18:53 +03:00
* platform_driver_unregister - unregister a driver for platform - level devices
2008-01-25 09:50:12 +03:00
* @ drv : platform driver structure
2005-11-09 20:23:39 +03:00
*/
void platform_driver_unregister ( struct platform_driver * drv )
{
driver_unregister ( & drv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( platform_driver_unregister ) ;
2006-11-17 10:28:47 +03:00
/**
* platform_driver_probe - register driver for non - hotpluggable device
* @ drv : platform driver structure
* @ probe : the driver probe routine , probably from an __init section
*
* Use this instead of platform_driver_register ( ) when you know the device
* is not hotpluggable and has already been registered , and you want to
* remove its run - once probe ( ) infrastructure from memory after the driver
* has bound to the device .
*
* One typical use for this would be with drivers for controllers integrated
* into system - on - chip processors , where the controller devices have been
* configured as part of board setup .
*
* Returns zero if the driver registered and bound to a device , else returns
* a negative error code and with the driver not registered .
*/
2006-12-05 01:56:36 +03:00
int __init_or_module platform_driver_probe ( struct platform_driver * drv ,
2006-11-17 10:28:47 +03:00
int ( * probe ) ( struct platform_device * ) )
{
int retval , code ;
2009-10-13 07:17:41 +04:00
/* make sure driver won't have bind/unbind attributes */
drv - > driver . suppress_bind_attrs = true ;
2006-11-17 10:28:47 +03:00
/* temporary section violation during probe() */
drv - > probe = probe ;
retval = code = platform_driver_register ( drv ) ;
2009-10-13 07:17:41 +04:00
/*
* Fixup that section violation , being paranoid about code scanning
2006-11-17 10:28:47 +03:00
* the list of drivers in order to probe new devices . Check to see
* if the probe was successful , and make sure any forced probes of
* new devices fail .
*/
2010-08-07 04:12:41 +04:00
spin_lock ( & drv - > driver . bus - > p - > klist_drivers . k_lock ) ;
2006-11-17 10:28:47 +03:00
drv - > probe = NULL ;
2007-11-29 02:59:15 +03:00
if ( code = = 0 & & list_empty ( & drv - > driver . p - > klist_devices . k_list ) )
2006-11-17 10:28:47 +03:00
retval = - ENODEV ;
drv - > driver . probe = platform_drv_probe_fail ;
2010-08-07 04:12:41 +04:00
spin_unlock ( & drv - > driver . bus - > p - > klist_drivers . k_lock ) ;
2006-11-17 10:28:47 +03:00
if ( code ! = retval )
platform_driver_unregister ( drv ) ;
return retval ;
}
EXPORT_SYMBOL_GPL ( platform_driver_probe ) ;
2005-04-17 02:20:36 +04:00
2009-12-30 07:11:20 +03:00
/**
* platform_create_bundle - register driver and create corresponding device
* @ driver : platform driver structure
* @ probe : the driver probe routine , probably from an __init section
* @ res : set of resources that needs to be allocated for the device
* @ n_res : number of resources
* @ data : platform specific data for this platform device
* @ size : size of platform specific data
*
* Use this in legacy - style modules that probe hardware directly and
* register a single platform device and corresponding platform driver .
2010-03-11 19:11:45 +03:00
*
* Returns & struct platform_device pointer on success , or ERR_PTR ( ) on error .
2009-12-30 07:11:20 +03:00
*/
struct platform_device * __init_or_module platform_create_bundle (
struct platform_driver * driver ,
int ( * probe ) ( struct platform_device * ) ,
struct resource * res , unsigned int n_res ,
const void * data , size_t size )
{
struct platform_device * pdev ;
int error ;
pdev = platform_device_alloc ( driver - > driver . name , - 1 ) ;
if ( ! pdev ) {
error = - ENOMEM ;
goto err_out ;
}
2010-09-07 17:31:54 +04:00
error = platform_device_add_resources ( pdev , res , n_res ) ;
if ( error )
goto err_pdev_put ;
2009-12-30 07:11:20 +03:00
2010-09-07 17:31:54 +04:00
error = platform_device_add_data ( pdev , data , size ) ;
if ( error )
goto err_pdev_put ;
2009-12-30 07:11:20 +03:00
error = platform_device_add ( pdev ) ;
if ( error )
goto err_pdev_put ;
error = platform_driver_probe ( driver , probe ) ;
if ( error )
goto err_pdev_del ;
return pdev ;
err_pdev_del :
platform_device_del ( pdev ) ;
err_pdev_put :
platform_device_put ( pdev ) ;
err_out :
return ERR_PTR ( error ) ;
}
EXPORT_SYMBOL_GPL ( platform_create_bundle ) ;
2006-05-29 21:37:33 +04:00
/* modalias support enables more hands-off userspace setup:
* ( a ) environment variable lets new - style hotplug events work once system is
* fully running : " modprobe $MODALIAS "
* ( b ) sysfs attribute lets new - style coldplug recover from hotplug events
* mishandled before system is fully running : " modprobe $(cat modalias) "
*/
2008-01-25 09:50:12 +03:00
static ssize_t modalias_show ( struct device * dev , struct device_attribute * a ,
char * buf )
2006-05-29 21:37:33 +04:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
2007-08-18 06:40:39 +04:00
int len = snprintf ( buf , PAGE_SIZE , " platform:%s \n " , pdev - > name ) ;
2006-05-29 21:37:33 +04:00
return ( len > = PAGE_SIZE ) ? ( PAGE_SIZE - 1 ) : len ;
}
static struct device_attribute platform_dev_attrs [ ] = {
__ATTR_RO ( modalias ) ,
__ATTR_NULL ,
} ;
2007-08-14 17:15:12 +04:00
static int platform_uevent ( struct device * dev , struct kobj_uevent_env * env )
2006-05-29 21:37:33 +04:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
2010-06-08 17:48:21 +04:00
int rc ;
/* Some devices have extra OF data and an OF-style MODALIAS */
rc = of_device_uevent ( dev , env ) ;
if ( rc ! = - ENODEV )
return rc ;
2006-05-29 21:37:33 +04:00
2009-02-04 06:52:40 +03:00
add_uevent_var ( env , " MODALIAS=%s%s " , PLATFORM_MODULE_PREFIX ,
2011-08-15 18:51:22 +04:00
pdev - > name ) ;
2006-05-29 21:37:33 +04:00
return 0 ;
}
2009-02-04 06:52:40 +03:00
static const struct platform_device_id * platform_match_id (
2010-01-26 11:35:00 +03:00
const struct platform_device_id * id ,
2009-02-04 06:52:40 +03:00
struct platform_device * pdev )
{
while ( id - > name [ 0 ] ) {
if ( strcmp ( pdev - > name , id - > name ) = = 0 ) {
pdev - > id_entry = id ;
return id ;
}
id + + ;
}
return NULL ;
}
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* platform_match - bind platform device to platform driver .
* @ dev : device .
* @ drv : driver .
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* Platform device IDs are assumed to be encoded like this :
* " <name><instance> " , where < name > is a short description of the type of
* device , like " pci " or " floppy " , and < instance > is the enumerated
* instance of the device , like ' 0 ' or ' 42 ' . Driver IDs are simply
* " <name> " . So , extract the < name > from the platform_device structure ,
* and compare it against the name of the driver . Return whether they match
* or not .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
static int platform_match ( struct device * dev , struct device_driver * drv )
2005-04-17 02:20:36 +04:00
{
2009-01-31 17:47:44 +03:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2009-02-04 06:52:40 +03:00
struct platform_driver * pdrv = to_platform_driver ( drv ) ;
2010-06-08 17:48:20 +04:00
/* Attempt an OF style match first */
if ( of_driver_match_device ( dev , drv ) )
return 1 ;
/* Then try to match against the id table */
2009-02-04 06:52:40 +03:00
if ( pdrv - > id_table )
return platform_match_id ( pdrv - > id_table , pdev ) ! = NULL ;
2005-04-17 02:20:36 +04:00
2009-02-04 06:52:40 +03:00
/* fall-back to driver name match */
2008-10-30 03:36:48 +03:00
return ( strcmp ( pdev - > name , drv - > name ) = = 0 ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-21 03:40:43 +04:00
# ifdef CONFIG_PM_SLEEP
static int platform_legacy_suspend ( struct device * dev , pm_message_t mesg )
2005-04-17 02:20:36 +04:00
{
2009-06-05 00:13:33 +04:00
struct platform_driver * pdrv = to_platform_driver ( dev - > driver ) ;
struct platform_device * pdev = to_platform_device ( dev ) ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
2009-06-05 00:13:33 +04:00
if ( dev - > driver & & pdrv - > suspend )
ret = pdrv - > suspend ( pdev , mesg ) ;
2006-09-04 00:16:45 +04:00
return ret ;
}
2008-05-21 03:40:43 +04:00
static int platform_legacy_resume ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2009-06-05 00:13:33 +04:00
struct platform_driver * pdrv = to_platform_driver ( dev - > driver ) ;
struct platform_device * pdev = to_platform_device ( dev ) ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
2009-06-05 00:13:33 +04:00
if ( dev - > driver & & pdrv - > resume )
ret = pdrv - > resume ( pdev ) ;
2005-10-28 20:52:56 +04:00
2005-04-17 02:20:36 +04:00
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_prepare ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( drv & & drv - > pm & & drv - > pm - > prepare )
ret = drv - > pm - > prepare ( dev ) ;
return ret ;
}
2011-04-29 02:36:05 +04:00
void platform_pm_complete ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
if ( drv & & drv - > pm & & drv - > pm - > complete )
drv - > pm - > complete ( dev ) ;
}
2011-04-29 02:36:05 +04:00
# endif /* CONFIG_PM_SLEEP */
2009-08-20 22:25:32 +04:00
2008-05-21 03:40:43 +04:00
# ifdef CONFIG_SUSPEND
2011-04-29 02:36:05 +04:00
int platform_pm_suspend ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
return 0 ;
if ( drv - > pm ) {
2008-05-21 03:40:43 +04:00
if ( drv - > pm - > suspend )
ret = drv - > pm - > suspend ( dev ) ;
} else {
ret = platform_legacy_suspend ( dev , PMSG_SUSPEND ) ;
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_suspend_noirq ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
2008-10-07 00:46:05 +04:00
struct device_driver * drv = dev - > driver ;
2008-05-21 03:40:43 +04:00
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
2008-05-21 03:40:43 +04:00
return 0 ;
2008-10-07 00:46:05 +04:00
if ( drv - > pm ) {
if ( drv - > pm - > suspend_noirq )
ret = drv - > pm - > suspend_noirq ( dev ) ;
2008-05-21 03:40:43 +04:00
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_resume ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
return 0 ;
if ( drv - > pm ) {
2008-05-21 03:40:43 +04:00
if ( drv - > pm - > resume )
ret = drv - > pm - > resume ( dev ) ;
} else {
ret = platform_legacy_resume ( dev ) ;
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_resume_noirq ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
2008-10-07 00:46:05 +04:00
struct device_driver * drv = dev - > driver ;
2008-05-21 03:40:43 +04:00
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
2008-05-21 03:40:43 +04:00
return 0 ;
2008-10-07 00:46:05 +04:00
if ( drv - > pm ) {
if ( drv - > pm - > resume_noirq )
ret = drv - > pm - > resume_noirq ( dev ) ;
2008-05-21 03:40:43 +04:00
}
return ret ;
}
2011-04-29 02:36:05 +04:00
# endif /* CONFIG_SUSPEND */
2008-05-21 03:40:43 +04:00
2011-04-12 00:54:42 +04:00
# ifdef CONFIG_HIBERNATE_CALLBACKS
2008-05-21 03:40:43 +04:00
2011-04-29 02:36:05 +04:00
int platform_pm_freeze ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( ! drv )
return 0 ;
if ( drv - > pm ) {
if ( drv - > pm - > freeze )
ret = drv - > pm - > freeze ( dev ) ;
} else {
ret = platform_legacy_suspend ( dev , PMSG_FREEZE ) ;
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_freeze_noirq ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
2008-10-07 00:46:05 +04:00
struct device_driver * drv = dev - > driver ;
2008-05-21 03:40:43 +04:00
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
2008-05-21 03:40:43 +04:00
return 0 ;
2008-10-07 00:46:05 +04:00
if ( drv - > pm ) {
if ( drv - > pm - > freeze_noirq )
ret = drv - > pm - > freeze_noirq ( dev ) ;
2008-05-21 03:40:43 +04:00
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_thaw ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
return 0 ;
if ( drv - > pm ) {
2008-05-21 03:40:43 +04:00
if ( drv - > pm - > thaw )
ret = drv - > pm - > thaw ( dev ) ;
} else {
ret = platform_legacy_resume ( dev ) ;
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_thaw_noirq ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
2008-10-07 00:46:05 +04:00
struct device_driver * drv = dev - > driver ;
2008-05-21 03:40:43 +04:00
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
2008-05-21 03:40:43 +04:00
return 0 ;
2008-10-07 00:46:05 +04:00
if ( drv - > pm ) {
if ( drv - > pm - > thaw_noirq )
ret = drv - > pm - > thaw_noirq ( dev ) ;
2008-05-21 03:40:43 +04:00
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_poweroff ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
return 0 ;
if ( drv - > pm ) {
2008-05-21 03:40:43 +04:00
if ( drv - > pm - > poweroff )
ret = drv - > pm - > poweroff ( dev ) ;
} else {
ret = platform_legacy_suspend ( dev , PMSG_HIBERNATE ) ;
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_poweroff_noirq ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
2008-10-07 00:46:05 +04:00
struct device_driver * drv = dev - > driver ;
2008-05-21 03:40:43 +04:00
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
2008-05-21 03:40:43 +04:00
return 0 ;
2008-10-07 00:46:05 +04:00
if ( drv - > pm ) {
if ( drv - > pm - > poweroff_noirq )
ret = drv - > pm - > poweroff_noirq ( dev ) ;
2008-05-21 03:40:43 +04:00
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_restore ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
return 0 ;
if ( drv - > pm ) {
2008-05-21 03:40:43 +04:00
if ( drv - > pm - > restore )
ret = drv - > pm - > restore ( dev ) ;
} else {
ret = platform_legacy_resume ( dev ) ;
}
return ret ;
}
2011-04-29 02:36:05 +04:00
int platform_pm_restore_noirq ( struct device * dev )
2008-05-21 03:40:43 +04:00
{
2008-10-07 00:46:05 +04:00
struct device_driver * drv = dev - > driver ;
2008-05-21 03:40:43 +04:00
int ret = 0 ;
2008-10-07 00:46:05 +04:00
if ( ! drv )
2008-05-21 03:40:43 +04:00
return 0 ;
2008-10-07 00:46:05 +04:00
if ( drv - > pm ) {
if ( drv - > pm - > restore_noirq )
ret = drv - > pm - > restore_noirq ( dev ) ;
2008-05-21 03:40:43 +04:00
}
return ret ;
}
2011-04-29 02:36:05 +04:00
# endif /* CONFIG_HIBERNATE_CALLBACKS */
2008-05-21 03:40:43 +04:00
2009-07-22 02:37:25 +04:00
static const struct dev_pm_ops platform_dev_pm_ops = {
2011-04-29 02:36:32 +04:00
. runtime_suspend = pm_generic_runtime_suspend ,
. runtime_resume = pm_generic_runtime_resume ,
. runtime_idle = pm_generic_runtime_idle ,
2011-04-29 02:36:05 +04:00
USE_PLATFORM_PM_SLEEP_OPS
2008-05-21 03:40:43 +04:00
} ;
2005-04-17 02:20:36 +04:00
struct bus_type platform_bus_type = {
. name = " platform " ,
2006-05-29 21:37:33 +04:00
. dev_attrs = platform_dev_attrs ,
2005-04-17 02:20:36 +04:00
. match = platform_match ,
2006-05-29 21:37:33 +04:00
. uevent = platform_uevent ,
2009-08-20 22:25:32 +04:00
. pm = & platform_dev_pm_ops ,
2005-04-17 02:20:36 +04:00
} ;
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_bus_type ) ;
2005-04-17 02:20:36 +04:00
int __init platform_bus_init ( void )
{
2006-11-27 12:35:08 +03:00
int error ;
2009-03-31 01:37:25 +04:00
early_platform_cleanup ( ) ;
2006-11-27 12:35:08 +03:00
error = device_register ( & platform_bus ) ;
if ( error )
return error ;
error = bus_register ( & platform_bus_type ) ;
if ( error )
device_unregister ( & platform_bus ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
# ifndef ARCH_HAS_DMA_GET_REQUIRED_MASK
u64 dma_get_required_mask ( struct device * dev )
{
u32 low_totalram = ( ( max_pfn - 1 ) < < PAGE_SHIFT ) ;
u32 high_totalram = ( ( max_pfn - 1 ) > > ( 32 - PAGE_SHIFT ) ) ;
u64 mask ;
if ( ! high_totalram ) {
/* convert to mask just covering totalram */
low_totalram = ( 1 < < ( fls ( low_totalram ) - 1 ) ) ;
low_totalram + = low_totalram - 1 ;
mask = low_totalram ;
} else {
high_totalram = ( 1 < < ( fls ( high_totalram ) - 1 ) ) ;
high_totalram + = high_totalram - 1 ;
mask = ( ( ( u64 ) high_totalram ) < < 32 ) + 0xffffffff ;
}
2008-03-09 19:57:56 +03:00
return mask ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL_GPL ( dma_get_required_mask ) ;
# endif
2009-03-31 01:37:25 +04:00
static __initdata LIST_HEAD ( early_platform_driver_list ) ;
static __initdata LIST_HEAD ( early_platform_device_list ) ;
/**
2010-03-10 14:50:38 +03:00
* early_platform_driver_register - register early platform driver
2009-04-21 18:22:53 +04:00
* @ epdrv : early_platform driver structure
2009-03-31 01:37:25 +04:00
* @ buf : string passed from early_param ( )
2010-03-10 14:50:38 +03:00
*
* Helper function for early_platform_init ( ) / early_platform_init_buffer ( )
2009-03-31 01:37:25 +04:00
*/
int __init early_platform_driver_register ( struct early_platform_driver * epdrv ,
char * buf )
{
2009-11-27 11:38:51 +03:00
char * tmp ;
2009-03-31 01:37:25 +04:00
int n ;
/* Simply add the driver to the end of the global list.
* Drivers will by default be put on the list in compiled - in order .
*/
if ( ! epdrv - > list . next ) {
INIT_LIST_HEAD ( & epdrv - > list ) ;
list_add_tail ( & epdrv - > list , & early_platform_driver_list ) ;
}
/* If the user has specified device then make sure the driver
* gets prioritized . The driver of the last device specified on
* command line will be put first on the list .
*/
n = strlen ( epdrv - > pdrv - > driver . name ) ;
if ( buf & & ! strncmp ( buf , epdrv - > pdrv - > driver . name , n ) ) {
list_move ( & epdrv - > list , & early_platform_driver_list ) ;
2009-11-27 11:38:51 +03:00
/* Allow passing parameters after device name */
if ( buf [ n ] = = ' \0 ' | | buf [ n ] = = ' , ' )
2009-03-31 01:37:25 +04:00
epdrv - > requested_id = - 1 ;
2009-11-27 11:38:51 +03:00
else {
epdrv - > requested_id = simple_strtoul ( & buf [ n + 1 ] ,
& tmp , 10 ) ;
if ( buf [ n ] ! = ' . ' | | ( tmp = = & buf [ n + 1 ] ) ) {
epdrv - > requested_id = EARLY_PLATFORM_ID_ERROR ;
n = 0 ;
} else
n + = strcspn ( & buf [ n + 1 ] , " , " ) + 1 ;
}
if ( buf [ n ] = = ' , ' )
n + + ;
if ( epdrv - > bufsize ) {
memcpy ( epdrv - > buffer , & buf [ n ] ,
min_t ( int , epdrv - > bufsize , strlen ( & buf [ n ] ) + 1 ) ) ;
epdrv - > buffer [ epdrv - > bufsize - 1 ] = ' \0 ' ;
}
2009-03-31 01:37:25 +04:00
}
return 0 ;
}
/**
2010-03-10 14:50:38 +03:00
* early_platform_add_devices - adds a number of early platform devices
2009-03-31 01:37:25 +04:00
* @ devs : array of early platform devices to add
* @ num : number of early platform devices in array
2010-03-10 14:50:38 +03:00
*
* Used by early architecture code to register early platform devices and
* their platform data .
2009-03-31 01:37:25 +04:00
*/
void __init early_platform_add_devices ( struct platform_device * * devs , int num )
{
struct device * dev ;
int i ;
/* simply add the devices to list */
for ( i = 0 ; i < num ; i + + ) {
dev = & devs [ i ] - > dev ;
if ( ! dev - > devres_head . next ) {
INIT_LIST_HEAD ( & dev - > devres_head ) ;
list_add_tail ( & dev - > devres_head ,
& early_platform_device_list ) ;
}
}
}
/**
2010-03-10 14:50:38 +03:00
* early_platform_driver_register_all - register early platform drivers
2009-03-31 01:37:25 +04:00
* @ class_str : string to identify early platform driver class
2010-03-10 14:50:38 +03:00
*
* Used by architecture code to register all early platform drivers
* for a certain class . If omitted then only early platform drivers
* with matching kernel command line class parameters will be registered .
2009-03-31 01:37:25 +04:00
*/
void __init early_platform_driver_register_all ( char * class_str )
{
/* The "class_str" parameter may or may not be present on the kernel
* command line . If it is present then there may be more than one
* matching parameter .
*
* Since we register our early platform drivers using early_param ( )
* we need to make sure that they also get registered in the case
* when the parameter is missing from the kernel command line .
*
* We use parse_early_options ( ) to make sure the early_param ( ) gets
* called at least once . The early_param ( ) may be called more than
* once since the name of the preferred device may be specified on
* the kernel command line . early_platform_driver_register ( ) handles
* this case for us .
*/
parse_early_options ( class_str ) ;
}
/**
2010-03-10 14:50:38 +03:00
* early_platform_match - find early platform device matching driver
2009-04-21 18:22:53 +04:00
* @ epdrv : early platform driver structure
2009-03-31 01:37:25 +04:00
* @ id : id to match against
*/
static __init struct platform_device *
early_platform_match ( struct early_platform_driver * epdrv , int id )
{
struct platform_device * pd ;
list_for_each_entry ( pd , & early_platform_device_list , dev . devres_head )
if ( platform_match ( & pd - > dev , & epdrv - > pdrv - > driver ) )
if ( pd - > id = = id )
return pd ;
return NULL ;
}
/**
2010-03-10 14:50:38 +03:00
* early_platform_left - check if early platform driver has matching devices
2009-04-21 18:22:53 +04:00
* @ epdrv : early platform driver structure
2009-03-31 01:37:25 +04:00
* @ id : return true if id or above exists
*/
static __init int early_platform_left ( struct early_platform_driver * epdrv ,
int id )
{
struct platform_device * pd ;
list_for_each_entry ( pd , & early_platform_device_list , dev . devres_head )
if ( platform_match ( & pd - > dev , & epdrv - > pdrv - > driver ) )
if ( pd - > id > = id )
return 1 ;
return 0 ;
}
/**
2010-03-10 14:50:38 +03:00
* early_platform_driver_probe_id - probe drivers matching class_str and id
2009-03-31 01:37:25 +04:00
* @ class_str : string to identify early platform driver class
* @ id : id to match against
* @ nr_probe : number of platform devices to successfully probe before exiting
*/
static int __init early_platform_driver_probe_id ( char * class_str ,
int id ,
int nr_probe )
{
struct early_platform_driver * epdrv ;
struct platform_device * match ;
int match_id ;
int n = 0 ;
int left = 0 ;
list_for_each_entry ( epdrv , & early_platform_driver_list , list ) {
/* only use drivers matching our class_str */
if ( strcmp ( class_str , epdrv - > class_str ) )
continue ;
if ( id = = - 2 ) {
match_id = epdrv - > requested_id ;
left = 1 ;
} else {
match_id = id ;
left + = early_platform_left ( epdrv , id ) ;
/* skip requested id */
switch ( epdrv - > requested_id ) {
case EARLY_PLATFORM_ID_ERROR :
case EARLY_PLATFORM_ID_UNSET :
break ;
default :
if ( epdrv - > requested_id = = id )
match_id = EARLY_PLATFORM_ID_UNSET ;
}
}
switch ( match_id ) {
case EARLY_PLATFORM_ID_ERROR :
pr_warning ( " %s: unable to parse %s parameter \n " ,
class_str , epdrv - > pdrv - > driver . name ) ;
/* fall-through */
case EARLY_PLATFORM_ID_UNSET :
match = NULL ;
break ;
default :
match = early_platform_match ( epdrv , match_id ) ;
}
if ( match ) {
2010-03-09 09:57:53 +03:00
/*
* Set up a sensible init_name to enable
* dev_name ( ) and others to be used before the
* rest of the driver core is initialized .
*/
2010-05-13 12:56:56 +04:00
if ( ! match - > dev . init_name & & slab_is_available ( ) ) {
2010-03-09 09:57:53 +03:00
if ( match - > id ! = - 1 )
2010-03-29 10:51:35 +04:00
match - > dev . init_name =
kasprintf ( GFP_KERNEL , " %s.%d " ,
match - > name ,
match - > id ) ;
2010-03-09 09:57:53 +03:00
else
2010-03-29 10:51:35 +04:00
match - > dev . init_name =
kasprintf ( GFP_KERNEL , " %s " ,
match - > name ) ;
2010-03-09 09:57:53 +03:00
if ( ! match - > dev . init_name )
return - ENOMEM ;
}
2010-03-29 10:51:35 +04:00
2009-03-31 01:37:25 +04:00
if ( epdrv - > pdrv - > probe ( match ) )
pr_warning ( " %s: unable to probe %s early. \n " ,
class_str , match - > name ) ;
else
n + + ;
}
if ( n > = nr_probe )
break ;
}
if ( left )
return n ;
else
return - ENODEV ;
}
/**
2010-03-10 14:50:38 +03:00
* early_platform_driver_probe - probe a class of registered drivers
2009-03-31 01:37:25 +04:00
* @ class_str : string to identify early platform driver class
* @ nr_probe : number of platform devices to successfully probe before exiting
* @ user_only : only probe user specified early platform devices
2010-03-10 14:50:38 +03:00
*
* Used by architecture code to probe registered early platform drivers
* within a certain class . For probe to happen a registered early platform
* device matching a registered early platform driver is needed .
2009-03-31 01:37:25 +04:00
*/
int __init early_platform_driver_probe ( char * class_str ,
int nr_probe ,
int user_only )
{
int k , n , i ;
n = 0 ;
for ( i = - 2 ; n < nr_probe ; i + + ) {
k = early_platform_driver_probe_id ( class_str , i , nr_probe - n ) ;
if ( k < 0 )
break ;
n + = k ;
if ( user_only )
break ;
}
return n ;
}
/**
* early_platform_cleanup - clean up early platform code
*/
void __init early_platform_cleanup ( void )
{
struct platform_device * pd , * pd2 ;
/* clean up the devres list used to chain devices */
list_for_each_entry_safe ( pd , pd2 , & early_platform_device_list ,
dev . devres_head ) {
list_del ( & pd - > dev . devres_head ) ;
memset ( & pd - > dev . devres_head , 0 , sizeof ( pd - > dev . devres_head ) ) ;
}
}