2020-02-13 15:41:23 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* ARM Integrator Logical Module bus driver
* Copyright ( C ) 2020 Linaro Ltd .
* Author : Linus Walleij < linus . walleij @ linaro . org >
*
* See the device tree bindings for this block for more details on the
* hardware .
*/
# include <linux/module.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/bitops.h>
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
/* All information about the connected logic modules are in here */
# define INTEGRATOR_SC_DEC_OFFSET 0x10
/* Base address for the expansion modules */
# define INTEGRATOR_AP_EXP_BASE 0xc0000000
# define INTEGRATOR_AP_EXP_STRIDE 0x10000000
static int integrator_lm_populate ( int num , struct device * dev )
{
struct device_node * np = dev - > of_node ;
struct device_node * child ;
u32 base ;
int ret ;
base = INTEGRATOR_AP_EXP_BASE + ( num * INTEGRATOR_AP_EXP_STRIDE ) ;
/* Walk over the child nodes and see what chipselects we use */
for_each_available_child_of_node ( np , child ) {
struct resource res ;
ret = of_address_to_resource ( child , 0 , & res ) ;
if ( ret ) {
dev_info ( dev , " no valid address on child \n " ) ;
continue ;
}
/* First populate the syscon then any devices */
if ( res . start = = base ) {
dev_info ( dev , " populate module @0x%08x from DT \n " ,
base ) ;
ret = of_platform_default_populate ( child , NULL , dev ) ;
if ( ret ) {
dev_err ( dev , " failed to populate module \n " ) ;
return ret ;
}
}
}
return 0 ;
}
static const struct of_device_id integrator_ap_syscon_match [ ] = {
{ . compatible = " arm,integrator-ap-syscon " } ,
{ } ,
} ;
static int integrator_ap_lm_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * syscon ;
static struct regmap * map ;
u32 val ;
int ret ;
int i ;
/* Look up the system controller */
syscon = of_find_matching_node ( NULL , integrator_ap_syscon_match ) ;
2020-05-25 12:16:34 +03:00
if ( ! syscon ) {
2020-02-13 15:41:23 +03:00
dev_err ( dev ,
" could not find Integrator/AP system controller \n " ) ;
2020-05-25 12:16:34 +03:00
return - ENODEV ;
2020-02-13 15:41:23 +03:00
}
map = syscon_node_to_regmap ( syscon ) ;
if ( IS_ERR ( map ) ) {
dev_err ( dev ,
" could not find Integrator/AP system controller \n " ) ;
return PTR_ERR ( map ) ;
}
ret = regmap_read ( map , INTEGRATOR_SC_DEC_OFFSET , & val ) ;
if ( ret ) {
dev_err ( dev , " could not read from Integrator/AP syscon \n " ) ;
return ret ;
}
/* Loop over the connected modules */
for ( i = 0 ; i < 4 ; i + + ) {
if ( ! ( val & BIT ( 4 + i ) ) )
continue ;
dev_info ( dev , " detected module in slot %d \n " , i ) ;
ret = integrator_lm_populate ( i , dev ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static const struct of_device_id integrator_ap_lm_match [ ] = {
{ . compatible = " arm,integrator-ap-lm " } ,
{ } ,
} ;
static struct platform_driver integrator_ap_lm_driver = {
. probe = integrator_ap_lm_probe ,
. driver = {
. name = " integratorap-lm " ,
. of_match_table = integrator_ap_lm_match ,
} ,
} ;
module_platform_driver ( integrator_ap_lm_driver ) ;
MODULE_AUTHOR ( " Linus Walleij <linus.walleij@linaro.org> " ) ;
MODULE_DESCRIPTION ( " Integrator AP Logical Module driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;