2007-05-01 10:40:36 +04:00
# include <linux/string.h>
# include <linux/kernel.h>
# include <linux/of.h>
2007-05-01 10:49:51 +04:00
# include <linux/of_device.h>
2015-03-03 20:52:09 +03:00
# include <linux/of_address.h>
# include <linux/of_iommu.h>
# include <linux/dma-mapping.h>
2007-05-01 10:40:36 +04:00
# include <linux/init.h>
# include <linux/module.h>
# include <linux/mod_devicetable.h>
# include <linux/slab.h>
# include <asm/errno.h>
2012-12-07 02:55:41 +04:00
# include "of_private.h"
2007-05-01 10:40:36 +04:00
/**
2010-04-14 03:13:22 +04:00
* of_match_device - Tell if a struct device matches an of_device_id list
2007-05-01 10:40:36 +04:00
* @ ids : array of of device match structures to search in
* @ dev : the of device structure to match against
*
2010-08-06 19:25:50 +04:00
* Used by a driver to check whether an platform_device present in the
2007-05-01 10:40:36 +04:00
* system is in its list of supported devices .
*/
const struct of_device_id * of_match_device ( const struct of_device_id * matches ,
2010-04-14 03:13:22 +04:00
const struct device * dev )
2007-05-01 10:40:36 +04:00
{
2010-06-08 17:48:17 +04:00
if ( ( ! matches ) | | ( ! dev - > of_node ) )
2007-05-01 10:40:36 +04:00
return NULL ;
2010-04-14 03:13:22 +04:00
return of_match_node ( matches , dev - > of_node ) ;
2007-05-01 10:40:36 +04:00
}
EXPORT_SYMBOL ( of_match_device ) ;
2010-07-22 23:59:23 +04:00
struct platform_device * of_dev_get ( struct platform_device * dev )
2007-05-01 10:40:36 +04:00
{
struct device * tmp ;
if ( ! dev )
return NULL ;
tmp = get_device ( & dev - > dev ) ;
if ( tmp )
2010-07-22 23:59:23 +04:00
return to_platform_device ( tmp ) ;
2007-05-01 10:40:36 +04:00
else
return NULL ;
}
EXPORT_SYMBOL ( of_dev_get ) ;
2010-07-22 23:59:23 +04:00
void of_dev_put ( struct platform_device * dev )
2007-05-01 10:40:36 +04:00
{
if ( dev )
put_device ( & dev - > dev ) ;
}
EXPORT_SYMBOL ( of_dev_put ) ;
2010-10-20 21:45:13 +04:00
int of_device_add ( struct platform_device * ofdev )
2007-05-01 10:40:36 +04:00
{
2010-04-14 03:12:29 +04:00
BUG_ON ( ofdev - > dev . of_node = = NULL ) ;
2008-10-27 00:51:25 +03:00
2010-06-08 17:48:21 +04:00
/* name and id have to be set so that the platform bus doesn't get
* confused on matching */
ofdev - > name = dev_name ( & ofdev - > dev ) ;
ofdev - > id = - 1 ;
2015-08-25 07:08:22 +03:00
/*
* If this device has not binding numa node in devicetree , that is
* of_node_to_nid returns NUMA_NO_NODE . device_add will assume that this
* device is on the same node as the parent .
*/
set_dev_node ( & ofdev - > dev , of_node_to_nid ( ofdev - > dev . of_node ) ) ;
2008-10-27 00:51:25 +03:00
return device_add ( & ofdev - > dev ) ;
2007-05-01 10:40:36 +04:00
}
2010-10-20 21:45:13 +04:00
2015-03-03 20:52:09 +03:00
/**
* of_dma_configure - Setup DMA configuration
* @ dev : Device to apply DMA configuration
* @ np : Pointer to OF node having DMA configuration
*
* Try to get devices ' s DMA configuration from DT and update it
* accordingly .
*
* If platform code needs to use its own special DMA configuration , it
* can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events
* to fix up DMA configuration .
*/
void of_dma_configure ( struct device * dev , struct device_node * np )
{
u64 dma_addr , paddr , size ;
int ret ;
bool coherent ;
unsigned long offset ;
struct iommu_ops * iommu ;
/*
2015-03-03 23:44:57 +03:00
* Set default coherent_dma_mask to 32 bit . Drivers are expected to
* setup the correct supported mask .
2015-03-03 20:52:09 +03:00
*/
2015-03-03 23:44:57 +03:00
if ( ! dev - > coherent_dma_mask )
dev - > coherent_dma_mask = DMA_BIT_MASK ( 32 ) ;
2015-03-03 20:52:09 +03:00
/*
* Set it to coherent_dma_mask by default if the architecture
* code has not set it .
*/
if ( ! dev - > dma_mask )
dev - > dma_mask = & dev - > coherent_dma_mask ;
ret = of_dma_get_range ( np , & dma_addr , & paddr , & size ) ;
if ( ret < 0 ) {
dma_addr = offset = 0 ;
2015-03-03 20:52:10 +03:00
size = dev - > coherent_dma_mask + 1 ;
2015-03-03 20:52:09 +03:00
} else {
offset = PFN_DOWN ( paddr - dma_addr ) ;
2015-03-03 20:52:10 +03:00
/*
* Add a work around to treat the size as mask + 1 in case
* it is defined in DT as a mask .
*/
if ( size & 1 ) {
dev_warn ( dev , " Invalid size 0x%llx for dma-range \n " ,
size ) ;
size = size + 1 ;
}
if ( ! size ) {
dev_err ( dev , " Adjusted size 0x%llx invalid \n " , size ) ;
return ;
}
2015-03-03 20:52:09 +03:00
dev_dbg ( dev , " dma_pfn_offset(%#08lx) \n " , offset ) ;
}
dev - > dma_pfn_offset = offset ;
2015-03-03 23:44:57 +03:00
/*
* Limit coherent and dma mask based on size and default mask
* set by the driver .
*/
dev - > coherent_dma_mask = min ( dev - > coherent_dma_mask ,
DMA_BIT_MASK ( ilog2 ( dma_addr + size ) ) ) ;
* dev - > dma_mask = min ( ( * dev - > dma_mask ) ,
DMA_BIT_MASK ( ilog2 ( dma_addr + size ) ) ) ;
2015-03-03 20:52:09 +03:00
coherent = of_dma_is_coherent ( np ) ;
dev_dbg ( dev , " device is%sdma coherent \n " ,
coherent ? " " : " not " ) ;
iommu = of_iommu_configure ( dev , np ) ;
dev_dbg ( dev , " device is%sbehind an iommu \n " ,
iommu ? " " : " not " ) ;
arch_setup_dma_ops ( dev , dma_addr , size , iommu , coherent ) ;
}
EXPORT_SYMBOL_GPL ( of_dma_configure ) ;
2010-10-20 21:45:13 +04:00
int of_device_register ( struct platform_device * pdev )
{
device_initialize ( & pdev - > dev ) ;
return of_device_add ( pdev ) ;
}
2007-05-01 10:40:36 +04:00
EXPORT_SYMBOL ( of_device_register ) ;
2010-07-22 23:59:23 +04:00
void of_device_unregister ( struct platform_device * ofdev )
2007-05-01 10:40:36 +04:00
{
device_unregister ( & ofdev - > dev ) ;
}
EXPORT_SYMBOL ( of_device_unregister ) ;
2008-05-16 05:57:45 +04:00
2015-05-06 21:09:09 +03:00
const void * of_device_get_match_data ( const struct device * dev )
{
const struct of_device_id * match ;
match = of_match_device ( dev - > driver - > of_match_table , dev ) ;
if ( ! match )
return NULL ;
return match - > data ;
}
EXPORT_SYMBOL ( of_device_get_match_data ) ;
2010-06-08 17:48:13 +04:00
ssize_t of_device_get_modalias ( struct device * dev , char * str , ssize_t len )
2008-05-16 05:57:45 +04:00
{
const char * compat ;
int cplen , i ;
ssize_t tsize , csize , repend ;
2014-01-14 12:46:38 +04:00
if ( ( ! dev ) | | ( ! dev - > of_node ) )
return - ENODEV ;
2008-05-16 05:57:45 +04:00
/* Name & Type */
2010-06-08 17:48:13 +04:00
csize = snprintf ( str , len , " of:N%sT%s " , dev - > of_node - > name ,
dev - > of_node - > type ) ;
2008-05-16 05:57:45 +04:00
/* Get compatible property if any */
2010-06-08 17:48:13 +04:00
compat = of_get_property ( dev - > of_node , " compatible " , & cplen ) ;
2008-05-16 05:57:45 +04:00
if ( ! compat )
return csize ;
/* Find true end (we tolerate multiple \0 at the end */
for ( i = ( cplen - 1 ) ; i > = 0 & & ! compat [ i ] ; i - - )
cplen - - ;
if ( ! cplen )
return csize ;
cplen + + ;
/* Check space (need cplen+1 chars including final \0) */
tsize = csize + cplen ;
repend = tsize ;
if ( csize > = len ) /* @ the limit, all is already filled */
return tsize ;
if ( tsize > = len ) { /* limit compat list */
cplen = len - csize - 1 ;
repend = len ;
}
/* Copy and do char replacement */
memcpy ( & str [ csize + 1 ] , compat , cplen ) ;
for ( i = csize ; i < repend ; i + + ) {
char c = str [ i ] ;
if ( c = = ' \0 ' )
str [ i ] = ' C ' ;
else if ( c = = ' ' )
str [ i ] = ' _ ' ;
}
return tsize ;
}
2010-06-08 17:48:12 +04:00
/**
* of_device_uevent - Display OF related uevent information
*/
2012-02-01 22:22:22 +04:00
void of_device_uevent ( struct device * dev , struct kobj_uevent_env * env )
2010-06-08 17:48:12 +04:00
{
const char * compat ;
2012-12-07 02:55:41 +04:00
struct alias_prop * app ;
2010-06-08 17:48:12 +04:00
int seen = 0 , cplen , sl ;
if ( ( ! dev ) | | ( ! dev - > of_node ) )
2012-02-01 22:22:22 +04:00
return ;
2010-06-08 17:48:12 +04:00
2012-02-01 22:22:22 +04:00
add_uevent_var ( env , " OF_NAME=%s " , dev - > of_node - > name ) ;
add_uevent_var ( env , " OF_FULLNAME=%s " , dev - > of_node - > full_name ) ;
if ( dev - > of_node - > type & & strcmp ( " <NULL> " , dev - > of_node - > type ) ! = 0 )
add_uevent_var ( env , " OF_TYPE=%s " , dev - > of_node - > type ) ;
2010-06-08 17:48:12 +04:00
/* Since the compatible field can contain pretty much anything
* it ' s not really legal to split it out with commas . We split it
* up using a number of environment variables instead . */
compat = of_get_property ( dev - > of_node , " compatible " , & cplen ) ;
while ( compat & & * compat & & cplen > 0 ) {
2012-02-01 22:22:22 +04:00
add_uevent_var ( env , " OF_COMPATIBLE_%d=%s " , seen , compat ) ;
2010-06-08 17:48:12 +04:00
sl = strlen ( compat ) + 1 ;
compat + = sl ;
cplen - = sl ;
seen + + ;
}
2012-02-01 22:22:22 +04:00
add_uevent_var ( env , " OF_COMPATIBLE_N=%d " , seen ) ;
2012-12-07 02:55:41 +04:00
seen = 0 ;
2014-07-04 20:58:03 +04:00
mutex_lock ( & of_mutex ) ;
2012-12-07 02:55:41 +04:00
list_for_each_entry ( app , & aliases_lookup , link ) {
if ( dev - > of_node = = app - > np ) {
add_uevent_var ( env , " OF_ALIAS_%d=%s " , seen ,
app - > alias ) ;
seen + + ;
}
}
2014-07-04 20:58:03 +04:00
mutex_unlock ( & of_mutex ) ;
2012-02-01 22:22:22 +04:00
}
2010-06-08 17:48:12 +04:00
2012-02-01 22:22:22 +04:00
int of_device_uevent_modalias ( struct device * dev , struct kobj_uevent_env * env )
{
int sl ;
if ( ( ! dev ) | | ( ! dev - > of_node ) )
return - ENODEV ;
2010-06-08 17:48:12 +04:00
2012-02-01 22:22:22 +04:00
/* Devicetree modalias is tricky, we add it in 2 steps */
2010-06-08 17:48:12 +04:00
if ( add_uevent_var ( env , " MODALIAS= " ) )
return - ENOMEM ;
2010-06-08 17:48:13 +04:00
sl = of_device_get_modalias ( dev , & env - > buf [ env - > buflen - 1 ] ,
2010-06-08 17:48:12 +04:00
sizeof ( env - > buf ) - env - > buflen ) ;
if ( sl > = ( sizeof ( env - > buf ) - env - > buflen ) )
return - ENOMEM ;
env - > buflen + = sl ;
return 0 ;
}