2015-06-01 11:04:26 +02:00
/*
* Allwinner SoCs SRAM Controller Driver
*
* Copyright ( C ) 2015 Maxime Ripard
*
* Author : Maxime Ripard < maxime . ripard @ free - electrons . 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/debugfs.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
2018-04-11 22:16:40 +08:00
# include <linux/regmap.h>
2015-06-01 11:04:26 +02:00
# include <linux/soc/sunxi/sunxi_sram.h>
struct sunxi_sram_func {
char * func ;
u8 val ;
2017-08-09 16:56:26 +08:00
u32 reg_val ;
2015-06-01 11:04:26 +02:00
} ;
struct sunxi_sram_data {
char * name ;
u8 reg ;
u8 offset ;
u8 width ;
struct sunxi_sram_func * func ;
struct list_head list ;
} ;
struct sunxi_sram_desc {
struct sunxi_sram_data data ;
bool claimed ;
} ;
2017-08-09 16:56:26 +08:00
# define SUNXI_SRAM_MAP(_reg_val, _val, _func) \
2015-06-01 11:04:26 +02:00
{ \
. func = _func , \
. val = _val , \
2017-08-09 16:56:26 +08:00
. reg_val = _reg_val , \
2015-06-01 11:04:26 +02:00
}
# define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...) \
{ \
. name = _name , \
. reg = _reg , \
. offset = _off , \
. width = _width , \
. func = ( struct sunxi_sram_func [ ] ) { \
__VA_ARGS__ , { } } , \
}
static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = {
. data = SUNXI_SRAM_DATA ( " A3-A4 " , 0x4 , 0x4 , 2 ,
2017-08-09 16:56:26 +08:00
SUNXI_SRAM_MAP ( 0 , 0 , " cpu " ) ,
SUNXI_SRAM_MAP ( 1 , 1 , " emac " ) ) ,
2015-06-01 11:04:26 +02:00
} ;
2018-07-10 10:00:59 +02:00
static struct sunxi_sram_desc sun4i_a10_sram_c1 = {
. data = SUNXI_SRAM_DATA ( " C1 " , 0x0 , 0x0 , 31 ,
SUNXI_SRAM_MAP ( 0 , 0 , " cpu " ) ,
SUNXI_SRAM_MAP ( 0x7fffffff , 1 , " ve " ) ) ,
} ;
2015-06-01 11:04:26 +02:00
static struct sunxi_sram_desc sun4i_a10_sram_d = {
. data = SUNXI_SRAM_DATA ( " D " , 0x4 , 0x0 , 1 ,
2017-08-09 16:56:26 +08:00
SUNXI_SRAM_MAP ( 0 , 0 , " cpu " ) ,
SUNXI_SRAM_MAP ( 1 , 1 , " usb-otg " ) ) ,
2015-06-01 11:04:26 +02:00
} ;
2017-08-09 16:56:27 +08:00
static struct sunxi_sram_desc sun50i_a64_sram_c = {
. data = SUNXI_SRAM_DATA ( " C " , 0x4 , 24 , 1 ,
SUNXI_SRAM_MAP ( 0 , 1 , " cpu " ) ,
SUNXI_SRAM_MAP ( 1 , 0 , " de2 " ) ) ,
} ;
2015-06-01 11:04:26 +02:00
static const struct of_device_id sunxi_sram_dt_ids [ ] = {
{
. compatible = " allwinner,sun4i-a10-sram-a3-a4 " ,
. data = & sun4i_a10_sram_a3_a4 . data ,
} ,
2018-07-10 10:00:59 +02:00
{
. compatible = " allwinner,sun4i-a10-sram-c1 " ,
. data = & sun4i_a10_sram_c1 . data ,
} ,
2015-06-01 11:04:26 +02:00
{
. compatible = " allwinner,sun4i-a10-sram-d " ,
. data = & sun4i_a10_sram_d . data ,
} ,
2017-08-09 16:56:27 +08:00
{
. compatible = " allwinner,sun50i-a64-sram-c " ,
. data = & sun50i_a64_sram_c . data ,
} ,
2015-06-01 11:04:26 +02:00
{ }
} ;
static struct device * sram_dev ;
static LIST_HEAD ( claimed_sram ) ;
static DEFINE_SPINLOCK ( sram_lock ) ;
static void __iomem * base ;
static int sunxi_sram_show ( struct seq_file * s , void * data )
{
struct device_node * sram_node , * section_node ;
const struct sunxi_sram_data * sram_data ;
const struct of_device_id * match ;
struct sunxi_sram_func * func ;
const __be32 * sram_addr_p , * section_addr_p ;
u32 val ;
seq_puts ( s , " Allwinner sunXi SRAM \n " ) ;
seq_puts ( s , " -------------------- \n \n " ) ;
for_each_child_of_node ( sram_dev - > of_node , sram_node ) {
sram_addr_p = of_get_address ( sram_node , 0 , NULL , NULL ) ;
seq_printf ( s , " sram@%08x \n " ,
be32_to_cpu ( * sram_addr_p ) ) ;
for_each_child_of_node ( sram_node , section_node ) {
match = of_match_node ( sunxi_sram_dt_ids , section_node ) ;
if ( ! match )
continue ;
sram_data = match - > data ;
section_addr_p = of_get_address ( section_node , 0 ,
NULL , NULL ) ;
seq_printf ( s , " \t section@%04x \t (%s) \n " ,
be32_to_cpu ( * section_addr_p ) ,
sram_data - > name ) ;
val = readl ( base + sram_data - > reg ) ;
val > > = sram_data - > offset ;
2016-01-27 14:51:13 +01:00
val & = GENMASK ( sram_data - > width - 1 , 0 ) ;
2015-06-01 11:04:26 +02:00
for ( func = sram_data - > func ; func - > func ; func + + ) {
seq_printf ( s , " \t \t %s%c \n " , func - > func ,
2017-08-09 16:56:26 +08:00
func - > reg_val = = val ?
' * ' : ' ' ) ;
2015-06-01 11:04:26 +02:00
}
}
seq_puts ( s , " \n " ) ;
}
return 0 ;
}
2018-11-22 07:59:41 -05:00
DEFINE_SHOW_ATTRIBUTE ( sunxi_sram ) ;
2015-06-01 11:04:26 +02:00
static inline struct sunxi_sram_desc * to_sram_desc ( const struct sunxi_sram_data * data )
{
return container_of ( data , struct sunxi_sram_desc , data ) ;
}
static const struct sunxi_sram_data * sunxi_sram_of_parse ( struct device_node * node ,
2017-08-09 16:56:26 +08:00
unsigned int * reg_value )
2015-06-01 11:04:26 +02:00
{
const struct of_device_id * match ;
2017-08-09 16:56:26 +08:00
const struct sunxi_sram_data * data ;
struct sunxi_sram_func * func ;
2015-06-01 11:04:26 +02:00
struct of_phandle_args args ;
2017-08-09 16:56:26 +08:00
u8 val ;
2015-06-01 11:04:26 +02:00
int ret ;
ret = of_parse_phandle_with_fixed_args ( node , " allwinner,sram " , 1 , 0 ,
& args ) ;
if ( ret )
return ERR_PTR ( ret ) ;
if ( ! of_device_is_available ( args . np ) ) {
ret = - EBUSY ;
goto err ;
}
2017-08-09 16:56:26 +08:00
val = args . args [ 0 ] ;
2015-06-01 11:04:26 +02:00
match = of_match_node ( sunxi_sram_dt_ids , args . np ) ;
if ( ! match ) {
ret = - EINVAL ;
goto err ;
}
2017-08-09 16:56:26 +08:00
data = match - > data ;
if ( ! data ) {
ret = - EINVAL ;
goto err ;
2020-09-10 22:05:46 +08:00
}
2017-08-09 16:56:26 +08:00
for ( func = data - > func ; func - > func ; func + + ) {
if ( val = = func - > val ) {
if ( reg_value )
* reg_value = func - > reg_val ;
break ;
}
}
if ( ! func - > func ) {
ret = - EINVAL ;
goto err ;
}
2015-06-01 11:04:26 +02:00
of_node_put ( args . np ) ;
return match - > data ;
err :
of_node_put ( args . np ) ;
return ERR_PTR ( ret ) ;
}
int sunxi_sram_claim ( struct device * dev )
{
const struct sunxi_sram_data * sram_data ;
struct sunxi_sram_desc * sram_desc ;
unsigned int device ;
u32 val , mask ;
if ( IS_ERR ( base ) )
2017-08-09 16:56:25 +08:00
return PTR_ERR ( base ) ;
if ( ! base )
2015-06-01 11:04:26 +02:00
return - EPROBE_DEFER ;
if ( ! dev | | ! dev - > of_node )
return - EINVAL ;
sram_data = sunxi_sram_of_parse ( dev - > of_node , & device ) ;
if ( IS_ERR ( sram_data ) )
return PTR_ERR ( sram_data ) ;
sram_desc = to_sram_desc ( sram_data ) ;
spin_lock ( & sram_lock ) ;
if ( sram_desc - > claimed ) {
spin_unlock ( & sram_lock ) ;
return - EBUSY ;
}
2016-01-27 14:51:13 +01:00
mask = GENMASK ( sram_data - > offset + sram_data - > width - 1 ,
sram_data - > offset ) ;
2015-06-01 11:04:26 +02:00
val = readl ( base + sram_data - > reg ) ;
val & = ~ mask ;
writel ( val | ( ( device < < sram_data - > offset ) & mask ) ,
base + sram_data - > reg ) ;
spin_unlock ( & sram_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( sunxi_sram_claim ) ;
int sunxi_sram_release ( struct device * dev )
{
const struct sunxi_sram_data * sram_data ;
struct sunxi_sram_desc * sram_desc ;
if ( ! dev | | ! dev - > of_node )
return - EINVAL ;
sram_data = sunxi_sram_of_parse ( dev - > of_node , NULL ) ;
if ( IS_ERR ( sram_data ) )
return - EINVAL ;
sram_desc = to_sram_desc ( sram_data ) ;
spin_lock ( & sram_lock ) ;
sram_desc - > claimed = false ;
spin_unlock ( & sram_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( sunxi_sram_release ) ;
2018-04-11 22:16:40 +08:00
struct sunxi_sramc_variant {
2021-01-27 17:24:48 +00:00
int num_emac_clocks ;
2018-04-11 22:16:40 +08:00
} ;
static const struct sunxi_sramc_variant sun4i_a10_sramc_variant = {
/* Nothing special */
} ;
2018-12-05 10:24:33 +01:00
static const struct sunxi_sramc_variant sun8i_h3_sramc_variant = {
2021-01-27 17:24:48 +00:00
. num_emac_clocks = 1 ,
2018-12-05 10:24:33 +01:00
} ;
2018-04-11 22:16:40 +08:00
static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = {
2021-01-27 17:24:48 +00:00
. num_emac_clocks = 1 ,
} ;
static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = {
. num_emac_clocks = 2 ,
2018-04-11 22:16:40 +08:00
} ;
# define SUNXI_SRAM_EMAC_CLOCK_REG 0x30
static bool sunxi_sram_regmap_accessible_reg ( struct device * dev ,
unsigned int reg )
{
2021-01-27 17:24:48 +00:00
const struct sunxi_sramc_variant * variant ;
variant = of_device_get_match_data ( dev ) ;
if ( reg < SUNXI_SRAM_EMAC_CLOCK_REG )
return false ;
if ( reg > SUNXI_SRAM_EMAC_CLOCK_REG + variant - > num_emac_clocks * 4 )
return false ;
return true ;
2018-04-11 22:16:40 +08:00
}
static struct regmap_config sunxi_sram_emac_clock_regmap = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
/* last defined register */
2021-01-27 17:24:48 +00:00
. max_register = SUNXI_SRAM_EMAC_CLOCK_REG + 4 ,
2018-04-11 22:16:40 +08:00
/* other devices have no business accessing other registers */
. readable_reg = sunxi_sram_regmap_accessible_reg ,
. writeable_reg = sunxi_sram_regmap_accessible_reg ,
} ;
2015-06-01 11:04:26 +02:00
static int sunxi_sram_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct dentry * d ;
2018-04-11 22:16:40 +08:00
struct regmap * emac_clock ;
const struct sunxi_sramc_variant * variant ;
2015-06-01 11:04:26 +02:00
sram_dev = & pdev - > dev ;
2018-04-11 22:16:40 +08:00
variant = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! variant )
return - EINVAL ;
2015-06-01 11:04:26 +02:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
of_platform_populate ( pdev - > dev . of_node , NULL , NULL , & pdev - > dev ) ;
d = debugfs_create_file ( " sram " , S_IRUGO , NULL , NULL ,
& sunxi_sram_fops ) ;
if ( ! d )
return - ENOMEM ;
2021-01-27 17:24:48 +00:00
if ( variant - > num_emac_clocks > 0 ) {
2018-04-11 22:16:40 +08:00
emac_clock = devm_regmap_init_mmio ( & pdev - > dev , base ,
& sunxi_sram_emac_clock_regmap ) ;
if ( IS_ERR ( emac_clock ) )
return PTR_ERR ( emac_clock ) ;
}
2015-06-01 11:04:26 +02:00
return 0 ;
}
static const struct of_device_id sunxi_sram_dt_match [ ] = {
2018-04-11 22:16:40 +08:00
{
. compatible = " allwinner,sun4i-a10-sram-controller " ,
. data = & sun4i_a10_sramc_variant ,
} ,
2018-07-10 10:00:58 +02:00
{
. compatible = " allwinner,sun4i-a10-system-control " ,
. data = & sun4i_a10_sramc_variant ,
} ,
2018-07-11 11:25:07 +02:00
{
. compatible = " allwinner,sun5i-a13-system-control " ,
. data = & sun4i_a10_sramc_variant ,
} ,
{
. compatible = " allwinner,sun8i-a23-system-control " ,
. data = & sun4i_a10_sramc_variant ,
} ,
{
. compatible = " allwinner,sun8i-h3-system-control " ,
2018-12-05 10:24:33 +01:00
. data = & sun8i_h3_sramc_variant ,
2018-07-11 11:25:07 +02:00
} ,
2018-04-11 22:16:40 +08:00
{
. compatible = " allwinner,sun50i-a64-sram-controller " ,
. data = & sun50i_a64_sramc_variant ,
} ,
2018-05-22 01:02:41 +08:00
{
. compatible = " allwinner,sun50i-a64-system-control " ,
. data = & sun50i_a64_sramc_variant ,
} ,
2018-12-05 10:24:35 +01:00
{
. compatible = " allwinner,sun50i-h5-system-control " ,
. data = & sun50i_a64_sramc_variant ,
} ,
2021-01-27 17:24:48 +00:00
{
. compatible = " allwinner,sun50i-h616-system-control " ,
. data = & sun50i_h616_sramc_variant ,
} ,
2015-06-01 11:04:26 +02:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , sunxi_sram_dt_match ) ;
static struct platform_driver sunxi_sram_driver = {
. driver = {
. name = " sunxi-sram " ,
. of_match_table = sunxi_sram_dt_match ,
} ,
. probe = sunxi_sram_probe ,
} ;
module_platform_driver ( sunxi_sram_driver ) ;
MODULE_AUTHOR ( " Maxime Ripard <maxime.ripard@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " Allwinner sunXi SRAM Controller Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;