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 .
*/
2005-10-29 22:07:23 +04:00
# include <linux/platform_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>
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 = {
. bus_id = " platform " ,
} ;
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_bus ) ;
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 ,
unsigned int type , 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
*/
int platform_get_irq_byname ( struct platform_device * dev , char * name )
{
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
/**
2008-01-25 09:50:12 +03:00
* platform_device_put
* @ 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
kfree ( pa - > pdev . dev . platform_data ) ;
kfree ( pa - > pdev . resource ) ;
kfree ( pa ) ;
}
/**
2008-01-25 09:50:12 +03:00
* platform_device_alloc
* @ 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 ;
}
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 ) ;
/**
2008-01-25 09:50:12 +03:00
* platform_device_add_resources
* @ 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 ,
struct resource * res , unsigned int num )
2005-11-06 00:19:33 +03:00
{
struct resource * r ;
r = kmalloc ( sizeof ( struct resource ) * num , GFP_KERNEL ) ;
if ( r ) {
memcpy ( r , res , sizeof ( struct resource ) * num ) ;
pdev - > resource = r ;
pdev - > num_resources = num ;
}
return r ? 0 : - ENOMEM ;
}
EXPORT_SYMBOL_GPL ( platform_device_add_resources ) ;
/**
2008-01-25 09:50:12 +03:00
* platform_device_add_data
* @ 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
{
void * d ;
d = kmalloc ( size , GFP_KERNEL ) ;
if ( d ) {
memcpy ( d , data , size ) ;
pdev - > dev . platform_data = d ;
}
return d ? 0 : - ENOMEM ;
}
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 )
2007-09-09 14:54:16 +04:00
snprintf ( pdev - > dev . bus_id , BUS_ID_SIZE , " %s.%d " , pdev - > name ,
pdev - > id ) ;
2005-04-17 02:20:36 +04:00
else
strlcpy ( pdev - > dev . bus_id , pdev - > name , BUS_ID_SIZE ) ;
for ( i = 0 ; i < pdev - > num_resources ; i + + ) {
struct resource * p , * r = & pdev - > resource [ i ] ;
if ( r - > name = = NULL )
r - > name = pdev - > dev . bus_id ;
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 " ,
pdev - > dev . bus_id , i ) ;
ret = - EBUSY ;
goto failed ;
}
}
pr_debug ( " Registering platform device '%s'. Parent at %s \n " ,
pdev - > dev . bus_id , pdev - > dev . parent - > bus_id ) ;
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 ) ;
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
/**
2008-01-25 09:50:12 +03:00
* platform_device_register_simple
* @ name : base name of the device we ' re adding
* @ id : instance id
* @ res : set of resources that needs to be allocated for the device
* @ num : number of resources
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* This function creates a simple platform device that requires minimal
* resource and memory management . Canned release function freeing memory
* allocated for the device allows drivers using such devices to be
* unloaded without waiting for the last reference to the device to be
* dropped .
2007-05-08 11:29:39 +04:00
*
2008-01-25 09:50:12 +03:00
* This interface is primarily intended for use with legacy drivers which
* probe hardware directly . Because such drivers create sysfs device nodes
* themselves , rather than letting system infrastructure handle such device
* enumeration tasks , they don ' t fully conform to the Linux driver model .
* In particular , when such drivers are built as modules , they can ' t be
* " hotplugged " .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
struct platform_device * platform_device_register_simple ( const char * name ,
int id ,
struct resource * res ,
unsigned int num )
2005-04-17 02:20:36 +04:00
{
2005-11-06 00:19:33 +03:00
struct platform_device * pdev ;
2005-04-17 02:20:36 +04:00
int retval ;
2005-11-06 00:19:33 +03:00
pdev = platform_device_alloc ( name , id ) ;
if ( ! pdev ) {
2005-04-17 02:20:36 +04:00
retval = - ENOMEM ;
goto error ;
}
if ( num ) {
2005-11-06 00:19:33 +03:00
retval = platform_device_add_resources ( pdev , res , num ) ;
if ( retval )
goto error ;
2005-04-17 02:20:36 +04:00
}
2005-11-06 00:19:33 +03:00
retval = platform_device_add ( pdev ) ;
2005-04-17 02:20:36 +04:00
if ( retval )
goto error ;
2005-11-06 00:19:33 +03:00
return pdev ;
2005-04-17 02:20:36 +04:00
error :
2005-11-06 00:19:33 +03:00
platform_device_put ( pdev ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( retval ) ;
}
2005-12-10 09:36:28 +03:00
EXPORT_SYMBOL_GPL ( platform_device_register_simple ) ;
2005-04-17 02:20:36 +04:00
2008-09-23 01:41:40 +04:00
/**
* platform_device_register_data
* @ parent : parent device for the device we ' re adding
* @ name : base name of the device we ' re adding
* @ id : instance id
* @ data : platform specific data for this platform device
* @ size : size of platform specific data
*
* This function creates a simple platform device that requires minimal
* resource and memory management . Canned release function freeing memory
* allocated for the device allows drivers using such devices to be
* unloaded without waiting for the last reference to the device to be
* dropped .
*/
struct platform_device * platform_device_register_data (
struct device * parent ,
const char * name , int id ,
const void * data , size_t size )
{
struct platform_device * pdev ;
int retval ;
pdev = platform_device_alloc ( name , id ) ;
if ( ! pdev ) {
retval = - ENOMEM ;
goto error ;
}
pdev - > dev . parent = parent ;
if ( size ) {
retval = platform_device_add_data ( pdev , data , size ) ;
if ( retval )
goto error ;
}
retval = platform_device_add ( pdev ) ;
if ( retval )
goto error ;
return pdev ;
error :
platform_device_put ( pdev ) ;
return ERR_PTR ( retval ) ;
}
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 ) ;
}
static int platform_drv_suspend ( struct device * _dev , pm_message_t state )
{
struct platform_driver * drv = to_platform_driver ( _dev - > driver ) ;
struct platform_device * dev = to_platform_device ( _dev ) ;
return drv - > suspend ( dev , state ) ;
}
static int platform_drv_resume ( struct device * _dev )
{
struct platform_driver * drv = to_platform_driver ( _dev - > driver ) ;
struct platform_device * dev = to_platform_device ( _dev ) ;
return drv - > resume ( dev ) ;
}
/**
2008-01-25 09:50:12 +03:00
* platform_driver_register
* @ 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 ;
if ( drv - > suspend )
drv - > driver . suspend = platform_drv_suspend ;
if ( drv - > resume )
drv - > driver . resume = platform_drv_resume ;
2008-05-21 03:40:43 +04:00
if ( drv - > pm )
drv - > driver . pm = & drv - > pm - > base ;
2005-11-09 20:23:39 +03:00
return driver_register ( & drv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( platform_driver_register ) ;
/**
2008-01-25 09:50:12 +03:00
* platform_driver_unregister
* @ 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 ;
/* temporary section violation during probe() */
drv - > probe = probe ;
retval = code = platform_driver_register ( drv ) ;
/* Fixup that section violation, being paranoid about code scanning
* 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 .
*/
2007-11-02 05:41:16 +03:00
spin_lock ( & platform_bus_type . 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 ;
2007-11-02 05:41:16 +03:00
spin_unlock ( & platform_bus_type . 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
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 ) ;
2007-08-14 17:15:12 +04:00
add_uevent_var ( env , " MODALIAS=platform:%s " , pdev - > name ) ;
2006-05-29 21:37:33 +04:00
return 0 ;
}
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
{
2008-01-25 09:50:12 +03:00
struct platform_device * pdev ;
2005-04-17 02:20:36 +04:00
2008-01-25 09:50:12 +03:00
pdev = container_of ( dev , struct platform_device , dev ) ;
2005-04-17 02:20:36 +04:00
return ( strncmp ( pdev - > name , drv - > name , BUS_ID_SIZE ) = = 0 ) ;
}
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
{
int ret = 0 ;
2005-10-28 20:52:56 +04:00
if ( dev - > driver & & dev - > driver - > suspend )
2006-09-04 00:16:45 +04:00
ret = dev - > driver - > suspend ( dev , mesg ) ;
return ret ;
}
2008-05-21 03:40:43 +04:00
static int platform_legacy_suspend_late ( struct device * dev , pm_message_t mesg )
2006-09-04 00:16:45 +04:00
{
struct platform_driver * drv = to_platform_driver ( dev - > driver ) ;
2008-01-25 09:50:12 +03:00
struct platform_device * pdev ;
2006-09-04 00:16:45 +04:00
int ret = 0 ;
2008-01-25 09:50:12 +03:00
pdev = container_of ( dev , struct platform_device , dev ) ;
2006-09-04 00:16:45 +04:00
if ( dev - > driver & & drv - > suspend_late )
ret = drv - > suspend_late ( pdev , mesg ) ;
return ret ;
}
2008-05-21 03:40:43 +04:00
static int platform_legacy_resume_early ( struct device * dev )
2006-09-04 00:16:45 +04:00
{
struct platform_driver * drv = to_platform_driver ( dev - > driver ) ;
2008-01-25 09:50:12 +03:00
struct platform_device * pdev ;
2006-09-04 00:16:45 +04:00
int ret = 0 ;
2008-01-25 09:50:12 +03:00
pdev = container_of ( dev , struct platform_device , dev ) ;
2006-09-04 00:16:45 +04:00
if ( dev - > driver & & drv - > resume_early )
ret = drv - > resume_early ( pdev ) ;
2005-10-28 20:52:56 +04:00
2005-04-17 02:20:36 +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
{
int ret = 0 ;
2005-10-28 20:52:56 +04:00
if ( dev - > driver & & dev - > driver - > resume )
ret = dev - > driver - > resume ( dev ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-05-21 03:40:43 +04:00
static int platform_pm_prepare ( struct device * dev )
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( drv & & drv - > pm & & drv - > pm - > prepare )
ret = drv - > pm - > prepare ( dev ) ;
return ret ;
}
static void platform_pm_complete ( struct device * dev )
{
struct device_driver * drv = dev - > driver ;
if ( drv & & drv - > pm & & drv - > pm - > complete )
drv - > pm - > complete ( dev ) ;
}
# ifdef CONFIG_SUSPEND
static int platform_pm_suspend ( struct device * dev )
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( drv & & drv - > pm ) {
if ( drv - > pm - > suspend )
ret = drv - > pm - > suspend ( dev ) ;
} else {
ret = platform_legacy_suspend ( dev , PMSG_SUSPEND ) ;
}
return ret ;
}
static int platform_pm_suspend_noirq ( struct device * dev )
{
struct platform_driver * pdrv ;
int ret = 0 ;
if ( ! dev - > driver )
return 0 ;
pdrv = to_platform_driver ( dev - > driver ) ;
if ( pdrv - > pm ) {
if ( pdrv - > pm - > suspend_noirq )
ret = pdrv - > pm - > suspend_noirq ( dev ) ;
} else {
ret = platform_legacy_suspend_late ( dev , PMSG_SUSPEND ) ;
}
return ret ;
}
static int platform_pm_resume ( struct device * dev )
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( drv & & drv - > pm ) {
if ( drv - > pm - > resume )
ret = drv - > pm - > resume ( dev ) ;
} else {
ret = platform_legacy_resume ( dev ) ;
}
return ret ;
}
static int platform_pm_resume_noirq ( struct device * dev )
{
struct platform_driver * pdrv ;
int ret = 0 ;
if ( ! dev - > driver )
return 0 ;
pdrv = to_platform_driver ( dev - > driver ) ;
if ( pdrv - > pm ) {
if ( pdrv - > pm - > resume_noirq )
ret = pdrv - > pm - > resume_noirq ( dev ) ;
} else {
ret = platform_legacy_resume_early ( dev ) ;
}
return ret ;
}
# else /* !CONFIG_SUSPEND */
# define platform_pm_suspend NULL
# define platform_pm_resume NULL
# define platform_pm_suspend_noirq NULL
# define platform_pm_resume_noirq NULL
# endif /* !CONFIG_SUSPEND */
# ifdef CONFIG_HIBERNATION
static int platform_pm_freeze ( struct device * dev )
{
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 ;
}
static int platform_pm_freeze_noirq ( struct device * dev )
{
struct platform_driver * pdrv ;
int ret = 0 ;
if ( ! dev - > driver )
return 0 ;
pdrv = to_platform_driver ( dev - > driver ) ;
if ( pdrv - > pm ) {
if ( pdrv - > pm - > freeze_noirq )
ret = pdrv - > pm - > freeze_noirq ( dev ) ;
} else {
ret = platform_legacy_suspend_late ( dev , PMSG_FREEZE ) ;
}
return ret ;
}
static int platform_pm_thaw ( struct device * dev )
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( drv & & drv - > pm ) {
if ( drv - > pm - > thaw )
ret = drv - > pm - > thaw ( dev ) ;
} else {
ret = platform_legacy_resume ( dev ) ;
}
return ret ;
}
static int platform_pm_thaw_noirq ( struct device * dev )
{
struct platform_driver * pdrv ;
int ret = 0 ;
if ( ! dev - > driver )
return 0 ;
pdrv = to_platform_driver ( dev - > driver ) ;
if ( pdrv - > pm ) {
if ( pdrv - > pm - > thaw_noirq )
ret = pdrv - > pm - > thaw_noirq ( dev ) ;
} else {
ret = platform_legacy_resume_early ( dev ) ;
}
return ret ;
}
static int platform_pm_poweroff ( struct device * dev )
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( drv & & drv - > pm ) {
if ( drv - > pm - > poweroff )
ret = drv - > pm - > poweroff ( dev ) ;
} else {
ret = platform_legacy_suspend ( dev , PMSG_HIBERNATE ) ;
}
return ret ;
}
static int platform_pm_poweroff_noirq ( struct device * dev )
{
struct platform_driver * pdrv ;
int ret = 0 ;
if ( ! dev - > driver )
return 0 ;
pdrv = to_platform_driver ( dev - > driver ) ;
if ( pdrv - > pm ) {
if ( pdrv - > pm - > poweroff_noirq )
ret = pdrv - > pm - > poweroff_noirq ( dev ) ;
} else {
ret = platform_legacy_suspend_late ( dev , PMSG_HIBERNATE ) ;
}
return ret ;
}
static int platform_pm_restore ( struct device * dev )
{
struct device_driver * drv = dev - > driver ;
int ret = 0 ;
if ( drv & & drv - > pm ) {
if ( drv - > pm - > restore )
ret = drv - > pm - > restore ( dev ) ;
} else {
ret = platform_legacy_resume ( dev ) ;
}
return ret ;
}
static int platform_pm_restore_noirq ( struct device * dev )
{
struct platform_driver * pdrv ;
int ret = 0 ;
if ( ! dev - > driver )
return 0 ;
pdrv = to_platform_driver ( dev - > driver ) ;
if ( pdrv - > pm ) {
if ( pdrv - > pm - > restore_noirq )
ret = pdrv - > pm - > restore_noirq ( dev ) ;
} else {
ret = platform_legacy_resume_early ( dev ) ;
}
return ret ;
}
# else /* !CONFIG_HIBERNATION */
# define platform_pm_freeze NULL
# define platform_pm_thaw NULL
# define platform_pm_poweroff NULL
# define platform_pm_restore NULL
# define platform_pm_freeze_noirq NULL
# define platform_pm_thaw_noirq NULL
# define platform_pm_poweroff_noirq NULL
# define platform_pm_restore_noirq NULL
# endif /* !CONFIG_HIBERNATION */
2008-07-21 23:33:36 +04:00
static struct pm_ext_ops platform_pm_ops = {
2008-05-21 03:40:43 +04:00
. base = {
. prepare = platform_pm_prepare ,
. complete = platform_pm_complete ,
. suspend = platform_pm_suspend ,
. resume = platform_pm_resume ,
. freeze = platform_pm_freeze ,
. thaw = platform_pm_thaw ,
. poweroff = platform_pm_poweroff ,
. restore = platform_pm_restore ,
} ,
. suspend_noirq = platform_pm_suspend_noirq ,
. resume_noirq = platform_pm_resume_noirq ,
. freeze_noirq = platform_pm_freeze_noirq ,
. thaw_noirq = platform_pm_thaw_noirq ,
. poweroff_noirq = platform_pm_poweroff_noirq ,
. restore_noirq = platform_pm_restore_noirq ,
} ;
# define PLATFORM_PM_OPS_PTR &platform_pm_ops
# else /* !CONFIG_PM_SLEEP */
# define PLATFORM_PM_OPS_PTR NULL
# endif /* !CONFIG_PM_SLEEP */
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 ,
2008-05-21 03:40:43 +04:00
. pm = PLATFORM_PM_OPS_PTR ,
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 ;
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