2016-05-23 10:44:54 +03:00
/*
* EBI driver for Atmel chips
* inspired by the fsl weim bus driver
*
* Copyright ( C ) 2013 Jean - Jacques Hiblot < jjhiblot @ traphandler . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/atmel-matrix.h>
# include <linux/mfd/syscon/atmel-smc.h>
2016-06-17 03:37:48 +03:00
# include <linux/init.h>
2016-05-23 10:44:54 +03:00
# include <linux/of_device.h>
# include <linux/regmap.h>
2017-03-16 11:30:32 +03:00
struct atmel_ebi_dev_config {
2016-05-23 10:44:54 +03:00
int cs ;
2017-03-16 11:30:29 +03:00
struct atmel_smc_cs_conf smcconf ;
2016-05-23 10:44:54 +03:00
} ;
2017-03-16 11:30:32 +03:00
struct atmel_ebi ;
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:32 +03:00
struct atmel_ebi_dev {
2016-05-23 10:44:54 +03:00
struct list_head node ;
2017-03-16 11:30:32 +03:00
struct atmel_ebi * ebi ;
2016-05-23 10:44:54 +03:00
u32 mode ;
int numcs ;
2017-03-16 11:30:32 +03:00
struct atmel_ebi_dev_config configs [ ] ;
2016-05-23 10:44:54 +03:00
} ;
2017-03-16 11:30:32 +03:00
struct atmel_ebi_caps {
2016-05-23 10:44:54 +03:00
unsigned int available_cs ;
2017-03-16 11:30:30 +03:00
unsigned int ebi_csa_offs ;
2017-03-16 11:30:32 +03:00
void ( * get_config ) ( struct atmel_ebi_dev * ebid ,
struct atmel_ebi_dev_config * conf ) ;
int ( * xlate_config ) ( struct atmel_ebi_dev * ebid ,
2016-05-23 10:44:54 +03:00
struct device_node * configs_np ,
2017-03-16 11:30:32 +03:00
struct atmel_ebi_dev_config * conf ) ;
void ( * apply_config ) ( struct atmel_ebi_dev * ebid ,
struct atmel_ebi_dev_config * conf ) ;
2016-05-23 10:44:54 +03:00
} ;
2017-03-16 11:30:32 +03:00
struct atmel_ebi {
2016-05-23 10:44:54 +03:00
struct clk * clk ;
struct regmap * matrix ;
2017-01-27 12:24:09 +03:00
struct {
struct regmap * regmap ;
struct clk * clk ;
} smc ;
2016-05-23 10:44:54 +03:00
struct device * dev ;
2017-03-16 11:30:32 +03:00
const struct atmel_ebi_caps * caps ;
2016-05-23 10:44:54 +03:00
struct list_head devs ;
} ;
2017-03-16 11:30:29 +03:00
struct atmel_smc_timing_xlate {
const char * name ;
int ( * converter ) ( struct atmel_smc_cs_conf * conf ,
unsigned int shift , unsigned int nycles ) ;
unsigned int shift ;
} ;
# define ATMEL_SMC_SETUP_XLATE(nm, pos) \
{ . name = nm , . converter = atmel_smc_cs_conf_set_setup , . shift = pos }
# define ATMEL_SMC_PULSE_XLATE(nm, pos) \
{ . name = nm , . converter = atmel_smc_cs_conf_set_pulse , . shift = pos }
# define ATMEL_SMC_CYCLE_XLATE(nm, pos) \
2017-07-25 15:00:24 +03:00
{ . name = nm , . converter = atmel_smc_cs_conf_set_cycle , . shift = pos }
2017-03-16 11:30:29 +03:00
2017-03-16 11:30:32 +03:00
static void at91sam9_ebi_get_config ( struct atmel_ebi_dev * ebid ,
struct atmel_ebi_dev_config * conf )
2016-05-23 10:44:54 +03:00
{
2017-03-16 11:30:29 +03:00
atmel_smc_cs_conf_get ( ebid - > ebi - > smc . regmap , conf - > cs ,
& conf - > smcconf ) ;
2016-05-23 10:44:54 +03:00
}
2017-03-16 11:30:32 +03:00
static void sama5_ebi_get_config ( struct atmel_ebi_dev * ebid ,
struct atmel_ebi_dev_config * conf )
2016-05-23 10:44:54 +03:00
{
2017-03-16 11:30:29 +03:00
atmel_hsmc_cs_conf_get ( ebid - > ebi - > smc . regmap , conf - > cs ,
& conf - > smcconf ) ;
2016-05-23 10:44:54 +03:00
}
2017-03-16 11:30:29 +03:00
static const struct atmel_smc_timing_xlate timings_xlate_table [ ] = {
ATMEL_SMC_SETUP_XLATE ( " atmel,smc-ncs-rd-setup-ns " ,
ATMEL_SMC_NCS_RD_SHIFT ) ,
ATMEL_SMC_SETUP_XLATE ( " atmel,smc-ncs-wr-setup-ns " ,
ATMEL_SMC_NCS_WR_SHIFT ) ,
ATMEL_SMC_SETUP_XLATE ( " atmel,smc-nrd-setup-ns " , ATMEL_SMC_NRD_SHIFT ) ,
ATMEL_SMC_SETUP_XLATE ( " atmel,smc-nwe-setup-ns " , ATMEL_SMC_NWE_SHIFT ) ,
ATMEL_SMC_PULSE_XLATE ( " atmel,smc-ncs-rd-pulse-ns " ,
ATMEL_SMC_NCS_RD_SHIFT ) ,
ATMEL_SMC_PULSE_XLATE ( " atmel,smc-ncs-wr-pulse-ns " ,
ATMEL_SMC_NCS_WR_SHIFT ) ,
ATMEL_SMC_PULSE_XLATE ( " atmel,smc-nrd-pulse-ns " , ATMEL_SMC_NRD_SHIFT ) ,
ATMEL_SMC_PULSE_XLATE ( " atmel,smc-nwe-pulse-ns " , ATMEL_SMC_NWE_SHIFT ) ,
ATMEL_SMC_CYCLE_XLATE ( " atmel,smc-nrd-cycle-ns " , ATMEL_SMC_NRD_SHIFT ) ,
ATMEL_SMC_CYCLE_XLATE ( " atmel,smc-nwe-cycle-ns " , ATMEL_SMC_NWE_SHIFT ) ,
} ;
2017-03-16 11:30:32 +03:00
static int atmel_ebi_xslate_smc_timings ( struct atmel_ebi_dev * ebid ,
struct device_node * np ,
struct atmel_smc_cs_conf * smcconf )
2016-05-23 10:44:54 +03:00
{
2017-03-16 11:30:29 +03:00
unsigned int clk_rate = clk_get_rate ( ebid - > ebi - > clk ) ;
unsigned int clk_period_ns = NSEC_PER_SEC / clk_rate ;
bool required = false ;
unsigned int ncycles ;
int ret , i ;
u32 val ;
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
ret = of_property_read_u32 ( np , " atmel,smc-tdf-ns " , & val ) ;
if ( ! ret ) {
required = true ;
ncycles = DIV_ROUND_UP ( val , clk_period_ns ) ;
2017-07-25 15:00:23 +03:00
if ( ncycles > ATMEL_SMC_MODE_TDF_MAX ) {
2017-03-16 11:30:29 +03:00
ret = - EINVAL ;
goto out ;
}
2016-05-23 10:44:54 +03:00
2017-07-25 15:00:23 +03:00
if ( ncycles < ATMEL_SMC_MODE_TDF_MIN )
ncycles = ATMEL_SMC_MODE_TDF_MIN ;
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_TDF ( ncycles ) ;
}
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( timings_xlate_table ) ; i + + ) {
const struct atmel_smc_timing_xlate * xlate ;
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
xlate = & timings_xlate_table [ i ] ;
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
ret = of_property_read_u32 ( np , xlate - > name , & val ) ;
if ( ret ) {
if ( ! required )
continue ;
else
break ;
}
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
if ( ! required ) {
ret = - EINVAL ;
break ;
}
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
ncycles = DIV_ROUND_UP ( val , clk_period_ns ) ;
ret = xlate - > converter ( smcconf , xlate - > shift , ncycles ) ;
if ( ret )
goto out ;
}
2016-05-23 10:44:54 +03:00
out :
2017-03-16 11:30:29 +03:00
if ( ret ) {
2016-05-23 10:44:54 +03:00
dev_err ( ebid - > ebi - > dev ,
" missing or invalid timings definition in %s " ,
np - > full_name ) ;
2017-03-16 11:30:29 +03:00
return ret ;
}
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
return required ;
2016-05-23 10:44:54 +03:00
}
2017-03-16 11:30:32 +03:00
static int atmel_ebi_xslate_smc_config ( struct atmel_ebi_dev * ebid ,
struct device_node * np ,
struct atmel_ebi_dev_config * conf )
2016-05-23 10:44:54 +03:00
{
2017-03-16 11:30:29 +03:00
struct atmel_smc_cs_conf * smcconf = & conf - > smcconf ;
2016-05-23 10:44:54 +03:00
bool required = false ;
const char * tmp_str ;
u32 tmp ;
int ret ;
ret = of_property_read_u32 ( np , " atmel,smc-bus-width " , & tmp ) ;
if ( ! ret ) {
switch ( tmp ) {
case 8 :
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_DBW_8 ;
2016-05-23 10:44:54 +03:00
break ;
case 16 :
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_DBW_16 ;
2016-05-23 10:44:54 +03:00
break ;
case 32 :
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_DBW_32 ;
2016-05-23 10:44:54 +03:00
break ;
default :
return - EINVAL ;
}
required = true ;
}
if ( of_property_read_bool ( np , " atmel,smc-tdf-optimized " ) ) {
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_TDFMODE_OPTIMIZED ;
2016-05-23 10:44:54 +03:00
required = true ;
}
tmp_str = NULL ;
of_property_read_string ( np , " atmel,smc-byte-access-type " , & tmp_str ) ;
if ( tmp_str & & ! strcmp ( tmp_str , " write " ) ) {
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_BAT_WRITE ;
2016-05-23 10:44:54 +03:00
required = true ;
}
tmp_str = NULL ;
of_property_read_string ( np , " atmel,smc-read-mode " , & tmp_str ) ;
if ( tmp_str & & ! strcmp ( tmp_str , " nrd " ) ) {
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_READMODE_NRD ;
2016-05-23 10:44:54 +03:00
required = true ;
}
tmp_str = NULL ;
of_property_read_string ( np , " atmel,smc-write-mode " , & tmp_str ) ;
if ( tmp_str & & ! strcmp ( tmp_str , " nwe " ) ) {
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_WRITEMODE_NWE ;
2016-05-23 10:44:54 +03:00
required = true ;
}
tmp_str = NULL ;
of_property_read_string ( np , " atmel,smc-exnw-mode " , & tmp_str ) ;
if ( tmp_str ) {
if ( ! strcmp ( tmp_str , " frozen " ) )
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_EXNWMODE_FROZEN ;
2016-05-23 10:44:54 +03:00
else if ( ! strcmp ( tmp_str , " ready " ) )
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_EXNWMODE_READY ;
2016-05-23 10:44:54 +03:00
else if ( strcmp ( tmp_str , " disabled " ) )
return - EINVAL ;
required = true ;
}
ret = of_property_read_u32 ( np , " atmel,smc-page-mode " , & tmp ) ;
if ( ! ret ) {
switch ( tmp ) {
case 4 :
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_PS_4 ;
2016-05-23 10:44:54 +03:00
break ;
case 8 :
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_PS_8 ;
2016-05-23 10:44:54 +03:00
break ;
case 16 :
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_PS_16 ;
2016-05-23 10:44:54 +03:00
break ;
case 32 :
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_PS_32 ;
2016-05-23 10:44:54 +03:00
break ;
default :
return - EINVAL ;
}
2017-03-16 11:30:29 +03:00
smcconf - > mode | = ATMEL_SMC_MODE_PMEN ;
2016-05-23 10:44:54 +03:00
required = true ;
}
2017-03-16 11:30:32 +03:00
ret = atmel_ebi_xslate_smc_timings ( ebid , np , & conf - > smcconf ) ;
2017-07-25 15:00:22 +03:00
if ( ret < 0 )
2017-03-16 11:30:29 +03:00
return - EINVAL ;
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
if ( ( ret > 0 & & ! required ) | | ( ! ret & & required ) ) {
dev_err ( ebid - > ebi - > dev , " missing atmel,smc- properties in %s " ,
np - > full_name ) ;
return - EINVAL ;
}
2016-05-23 10:44:54 +03:00
2017-03-16 11:30:29 +03:00
return required ;
2016-05-23 10:44:54 +03:00
}
2017-03-16 11:30:32 +03:00
static void at91sam9_ebi_apply_config ( struct atmel_ebi_dev * ebid ,
struct atmel_ebi_dev_config * conf )
2016-05-23 10:44:54 +03:00
{
2017-03-16 11:30:29 +03:00
atmel_smc_cs_conf_apply ( ebid - > ebi - > smc . regmap , conf - > cs ,
& conf - > smcconf ) ;
2016-05-23 10:44:54 +03:00
}
2017-03-16 11:30:32 +03:00
static void sama5_ebi_apply_config ( struct atmel_ebi_dev * ebid ,
struct atmel_ebi_dev_config * conf )
2016-05-23 10:44:54 +03:00
{
2017-03-16 11:30:29 +03:00
atmel_hsmc_cs_conf_apply ( ebid - > ebi - > smc . regmap , conf - > cs ,
& conf - > smcconf ) ;
2016-05-23 10:44:54 +03:00
}
2017-03-16 11:30:32 +03:00
static int atmel_ebi_dev_setup ( struct atmel_ebi * ebi , struct device_node * np ,
int reg_cells )
2016-05-23 10:44:54 +03:00
{
2017-03-16 11:30:32 +03:00
const struct atmel_ebi_caps * caps = ebi - > caps ;
struct atmel_ebi_dev_config conf = { } ;
2016-05-23 10:44:54 +03:00
struct device * dev = ebi - > dev ;
2017-03-16 11:30:32 +03:00
struct atmel_ebi_dev * ebid ;
2017-01-27 12:10:37 +03:00
unsigned long cslines = 0 ;
int ret , numcs = 0 , nentries , i ;
2016-05-23 10:44:54 +03:00
bool apply = false ;
2017-01-27 12:10:37 +03:00
u32 cs ;
2016-05-23 10:44:54 +03:00
2017-01-27 12:10:37 +03:00
nentries = of_property_count_elems_of_size ( np , " reg " ,
reg_cells * sizeof ( u32 ) ) ;
for ( i = 0 ; i < nentries ; i + + ) {
ret = of_property_read_u32_index ( np , " reg " , i * reg_cells ,
& cs ) ;
if ( ret )
return ret ;
if ( cs > = AT91_MATRIX_EBI_NUM_CS | |
! ( ebi - > caps - > available_cs & BIT ( cs ) ) ) {
dev_err ( dev , " invalid reg property in %s \n " ,
np - > full_name ) ;
return - EINVAL ;
}
if ( ! test_and_set_bit ( cs , & cslines ) )
numcs + + ;
}
if ( ! numcs ) {
2016-05-23 10:44:54 +03:00
dev_err ( dev , " invalid reg property in %s \n " , np - > full_name ) ;
return - EINVAL ;
}
ebid = devm_kzalloc ( ebi - > dev ,
sizeof ( * ebid ) + ( numcs * sizeof ( * ebid - > configs ) ) ,
GFP_KERNEL ) ;
if ( ! ebid )
return - ENOMEM ;
ebid - > ebi = ebi ;
2017-03-16 11:30:33 +03:00
ebid - > numcs = numcs ;
2016-05-23 10:44:54 +03:00
ret = caps - > xlate_config ( ebid , np , & conf ) ;
if ( ret < 0 )
return ret ;
else if ( ret )
apply = true ;
2017-01-27 12:10:37 +03:00
i = 0 ;
for_each_set_bit ( cs , & cslines , AT91_MATRIX_EBI_NUM_CS ) {
2016-05-23 10:44:54 +03:00
ebid - > configs [ i ] . cs = cs ;
if ( apply ) {
conf . cs = cs ;
2017-03-16 11:30:29 +03:00
caps - > apply_config ( ebid , & conf ) ;
2016-05-23 10:44:54 +03:00
}
caps - > get_config ( ebid , & ebid - > configs [ i ] ) ;
/*
* Attach the EBI device to the generic SMC logic if at least
* one " atmel,smc- " property is present .
*/
2017-03-16 11:30:30 +03:00
if ( ebi - > caps - > ebi_csa_offs & & apply )
regmap_update_bits ( ebi - > matrix ,
ebi - > caps - > ebi_csa_offs ,
BIT ( cs ) , 0 ) ;
2017-01-27 12:10:37 +03:00
i + + ;
2016-05-23 10:44:54 +03:00
}
list_add_tail ( & ebid - > node , & ebi - > devs ) ;
return 0 ;
}
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps at91sam9260_ebi_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0xff ,
2017-03-16 11:30:30 +03:00
. ebi_csa_offs = AT91SAM9260_MATRIX_EBICSA ,
2016-05-23 10:44:54 +03:00
. get_config = at91sam9_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2016-05-23 10:44:54 +03:00
. apply_config = at91sam9_ebi_apply_config ,
} ;
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps at91sam9261_ebi_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0xff ,
2017-03-16 11:30:30 +03:00
. ebi_csa_offs = AT91SAM9261_MATRIX_EBICSA ,
2016-05-23 10:44:54 +03:00
. get_config = at91sam9_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2016-05-23 10:44:54 +03:00
. apply_config = at91sam9_ebi_apply_config ,
} ;
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps at91sam9263_ebi0_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0x3f ,
2017-03-16 11:30:30 +03:00
. ebi_csa_offs = AT91SAM9263_MATRIX_EBI0CSA ,
2016-05-23 10:44:54 +03:00
. get_config = at91sam9_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2016-05-23 10:44:54 +03:00
. apply_config = at91sam9_ebi_apply_config ,
} ;
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps at91sam9263_ebi1_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0x7 ,
2017-03-16 11:30:30 +03:00
. ebi_csa_offs = AT91SAM9263_MATRIX_EBI1CSA ,
2016-05-23 10:44:54 +03:00
. get_config = at91sam9_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2016-05-23 10:44:54 +03:00
. apply_config = at91sam9_ebi_apply_config ,
} ;
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps at91sam9rl_ebi_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0x3f ,
2017-03-16 11:30:30 +03:00
. ebi_csa_offs = AT91SAM9RL_MATRIX_EBICSA ,
2016-05-23 10:44:54 +03:00
. get_config = at91sam9_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2016-05-23 10:44:54 +03:00
. apply_config = at91sam9_ebi_apply_config ,
} ;
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps at91sam9g45_ebi_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0x3f ,
2017-03-16 11:30:30 +03:00
. ebi_csa_offs = AT91SAM9G45_MATRIX_EBICSA ,
2016-05-23 10:44:54 +03:00
. get_config = at91sam9_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2016-05-23 10:44:54 +03:00
. apply_config = at91sam9_ebi_apply_config ,
} ;
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps at91sam9x5_ebi_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0x3f ,
2017-03-16 11:30:30 +03:00
. ebi_csa_offs = AT91SAM9X5_MATRIX_EBICSA ,
2016-05-23 10:44:54 +03:00
. get_config = at91sam9_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2016-05-23 10:44:54 +03:00
. apply_config = at91sam9_ebi_apply_config ,
} ;
2017-03-16 11:30:32 +03:00
static const struct atmel_ebi_caps sama5d3_ebi_caps = {
2016-05-23 10:44:54 +03:00
. available_cs = 0xf ,
2017-03-16 11:30:29 +03:00
. get_config = sama5_ebi_get_config ,
2017-03-16 11:30:32 +03:00
. xlate_config = atmel_ebi_xslate_smc_config ,
2017-03-16 11:30:29 +03:00
. apply_config = sama5_ebi_apply_config ,
2016-05-23 10:44:54 +03:00
} ;
2017-03-16 11:30:32 +03:00
static const struct of_device_id atmel_ebi_id_table [ ] = {
2016-05-23 10:44:54 +03:00
{
. compatible = " atmel,at91sam9260-ebi " ,
. data = & at91sam9260_ebi_caps ,
} ,
{
. compatible = " atmel,at91sam9261-ebi " ,
. data = & at91sam9261_ebi_caps ,
} ,
{
. compatible = " atmel,at91sam9263-ebi0 " ,
. data = & at91sam9263_ebi0_caps ,
} ,
{
. compatible = " atmel,at91sam9263-ebi1 " ,
. data = & at91sam9263_ebi1_caps ,
} ,
{
. compatible = " atmel,at91sam9rl-ebi " ,
. data = & at91sam9rl_ebi_caps ,
} ,
{
. compatible = " atmel,at91sam9g45-ebi " ,
. data = & at91sam9g45_ebi_caps ,
} ,
{
. compatible = " atmel,at91sam9x5-ebi " ,
. data = & at91sam9x5_ebi_caps ,
} ,
{
. compatible = " atmel,sama5d3-ebi " ,
. data = & sama5d3_ebi_caps ,
} ,
{ /* sentinel */ }
} ;
2017-03-16 11:30:32 +03:00
static int atmel_ebi_dev_disable ( struct atmel_ebi * ebi , struct device_node * np )
2016-05-23 10:44:54 +03:00
{
struct device * dev = ebi - > dev ;
struct property * newprop ;
newprop = devm_kzalloc ( dev , sizeof ( * newprop ) , GFP_KERNEL ) ;
if ( ! newprop )
return - ENOMEM ;
newprop - > name = devm_kstrdup ( dev , " status " , GFP_KERNEL ) ;
if ( ! newprop - > name )
return - ENOMEM ;
newprop - > value = devm_kstrdup ( dev , " disabled " , GFP_KERNEL ) ;
2016-09-16 16:03:47 +03:00
if ( ! newprop - > value )
2016-05-23 10:44:54 +03:00
return - ENOMEM ;
newprop - > length = sizeof ( " disabled " ) ;
return of_update_property ( np , newprop ) ;
}
2017-03-16 11:30:32 +03:00
static int atmel_ebi_probe ( struct platform_device * pdev )
2016-05-23 10:44:54 +03:00
{
struct device * dev = & pdev - > dev ;
2017-01-27 12:24:09 +03:00
struct device_node * child , * np = dev - > of_node , * smc_np ;
2016-05-23 10:44:54 +03:00
const struct of_device_id * match ;
2017-03-16 11:30:32 +03:00
struct atmel_ebi * ebi ;
2016-05-23 10:44:54 +03:00
int ret , reg_cells ;
struct clk * clk ;
u32 val ;
2017-03-16 11:30:32 +03:00
match = of_match_device ( atmel_ebi_id_table , dev ) ;
2016-05-23 10:44:54 +03:00
if ( ! match | | ! match - > data )
return - EINVAL ;
ebi = devm_kzalloc ( dev , sizeof ( * ebi ) , GFP_KERNEL ) ;
if ( ! ebi )
return - ENOMEM ;
2017-03-16 11:30:34 +03:00
platform_set_drvdata ( pdev , ebi ) ;
2016-05-23 10:44:54 +03:00
INIT_LIST_HEAD ( & ebi - > devs ) ;
ebi - > caps = match - > data ;
ebi - > dev = dev ;
clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
ebi - > clk = clk ;
2017-01-27 12:24:09 +03:00
smc_np = of_parse_phandle ( dev - > of_node , " atmel,smc " , 0 ) ;
ebi - > smc . regmap = syscon_node_to_regmap ( smc_np ) ;
if ( IS_ERR ( ebi - > smc . regmap ) )
return PTR_ERR ( ebi - > smc . regmap ) ;
ebi - > smc . clk = of_clk_get ( smc_np , 0 ) ;
if ( IS_ERR ( ebi - > smc . clk ) ) {
if ( PTR_ERR ( ebi - > smc . clk ) ! = - ENOENT )
return PTR_ERR ( ebi - > smc . clk ) ;
ebi - > smc . clk = NULL ;
}
ret = clk_prepare_enable ( ebi - > smc . clk ) ;
if ( ret )
return ret ;
2016-05-23 10:44:54 +03:00
/*
* The sama5d3 does not provide an EBICSA register and thus does need
* to access the matrix registers .
*/
2017-03-16 11:30:30 +03:00
if ( ebi - > caps - > ebi_csa_offs ) {
2016-05-23 10:44:54 +03:00
ebi - > matrix =
syscon_regmap_lookup_by_phandle ( np , " atmel,matrix " ) ;
if ( IS_ERR ( ebi - > matrix ) )
return PTR_ERR ( ebi - > matrix ) ;
}
ret = of_property_read_u32 ( np , " #address-cells " , & val ) ;
if ( ret ) {
dev_err ( dev , " missing #address-cells property \n " ) ;
return ret ;
}
reg_cells = val ;
ret = of_property_read_u32 ( np , " #size-cells " , & val ) ;
if ( ret ) {
dev_err ( dev , " missing #address-cells property \n " ) ;
return ret ;
}
reg_cells + = val ;
for_each_available_child_of_node ( np , child ) {
if ( ! of_find_property ( child , " reg " , NULL ) )
continue ;
2017-03-16 11:30:32 +03:00
ret = atmel_ebi_dev_setup ( ebi , child , reg_cells ) ;
2016-05-23 10:44:54 +03:00
if ( ret ) {
dev_err ( dev , " failed to configure EBI bus for %s, disabling the device " ,
child - > full_name ) ;
2017-03-16 11:30:32 +03:00
ret = atmel_ebi_dev_disable ( ebi , child ) ;
2016-05-23 10:44:54 +03:00
if ( ret )
return ret ;
}
}
return of_platform_populate ( np , NULL , NULL , dev ) ;
}
2017-04-19 20:48:07 +03:00
static __maybe_unused int atmel_ebi_resume ( struct device * dev )
2017-03-16 11:30:34 +03:00
{
struct atmel_ebi * ebi = dev_get_drvdata ( dev ) ;
struct atmel_ebi_dev * ebid ;
list_for_each_entry ( ebid , & ebi - > devs , node ) {
int i ;
for ( i = 0 ; i < ebid - > numcs ; i + + )
ebid - > ebi - > caps - > apply_config ( ebid , & ebid - > configs [ i ] ) ;
}
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( atmel_ebi_pm_ops , NULL , atmel_ebi_resume ) ;
2017-03-16 11:30:32 +03:00
static struct platform_driver atmel_ebi_driver = {
2016-05-23 10:44:54 +03:00
. driver = {
. name = " atmel-ebi " ,
2017-03-16 11:30:32 +03:00
. of_match_table = atmel_ebi_id_table ,
2017-03-16 11:30:34 +03:00
. pm = & atmel_ebi_pm_ops ,
2016-05-23 10:44:54 +03:00
} ,
} ;
2017-03-16 11:30:32 +03:00
builtin_platform_driver_probe ( atmel_ebi_driver , atmel_ebi_probe ) ;