2018-01-09 19:29:55 +01:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2015 Samsung Electronics Co., Ltd.
// http://www.samsung.com/
//
2020-01-04 16:20:56 +01:00
// Exynos - SROM Controller support
2018-01-09 19:29:55 +01:00
// Author: Pankaj Dubey <pankaj.dubey@samsung.com>
2016-04-11 13:12:24 +05:30
# include <linux/io.h>
2016-06-16 20:37:47 -04:00
# include <linux/init.h>
2016-04-11 13:12:24 +05:30
# include <linux/of.h>
# include <linux/of_address.h>
2016-04-11 13:12:27 +05:30
# include <linux/of_platform.h>
2016-04-11 13:12:24 +05:30
# include <linux/platform_device.h>
# include <linux/slab.h>
# include "exynos-srom.h"
static const unsigned long exynos_srom_offsets [ ] = {
/* SROM side */
EXYNOS_SROM_BW ,
EXYNOS_SROM_BC0 ,
EXYNOS_SROM_BC1 ,
EXYNOS_SROM_BC2 ,
EXYNOS_SROM_BC3 ,
} ;
/**
* struct exynos_srom_reg_dump : register dump of SROM Controller registers .
* @ offset : srom register offset from the controller base address .
* @ value : the value of register under the offset .
*/
struct exynos_srom_reg_dump {
u32 offset ;
u32 value ;
} ;
/**
* struct exynos_srom : platform data for exynos srom controller driver .
* @ dev : platform device pointer
* @ reg_base : srom base address
* @ reg_offset : exynos_srom_reg_dump pointer to hold offset and its value .
*/
struct exynos_srom {
struct device * dev ;
void __iomem * reg_base ;
struct exynos_srom_reg_dump * reg_offset ;
} ;
2020-07-24 20:23:27 +02:00
static struct exynos_srom_reg_dump *
exynos_srom_alloc_reg_dump ( const unsigned long * rdump ,
unsigned long nr_rdump )
2016-04-11 13:12:24 +05:30
{
struct exynos_srom_reg_dump * rd ;
unsigned int i ;
rd = kcalloc ( nr_rdump , sizeof ( * rd ) , GFP_KERNEL ) ;
if ( ! rd )
return NULL ;
for ( i = 0 ; i < nr_rdump ; + + i )
rd [ i ] . offset = rdump [ i ] ;
return rd ;
}
2016-04-11 13:12:27 +05:30
static int exynos_srom_configure_bank ( struct exynos_srom * srom ,
struct device_node * np )
{
u32 bank , width , pmc = 0 ;
u32 timing [ 6 ] ;
u32 cs , bw ;
if ( of_property_read_u32 ( np , " reg " , & bank ) )
return - EINVAL ;
if ( of_property_read_u32 ( np , " reg-io-width " , & width ) )
width = 1 ;
if ( of_property_read_bool ( np , " samsung,srom-page-mode " ) )
pmc = 1 < < EXYNOS_SROM_BCX__PMC__SHIFT ;
if ( of_property_read_u32_array ( np , " samsung,srom-timing " , timing ,
ARRAY_SIZE ( timing ) ) )
return - EINVAL ;
bank * = 4 ; /* Convert bank into shift/offset */
cs = 1 < < EXYNOS_SROM_BW__BYTEENABLE__SHIFT ;
if ( width = = 2 )
cs | = 1 < < EXYNOS_SROM_BW__DATAWIDTH__SHIFT ;
2016-06-17 18:08:10 +01:00
bw = readl_relaxed ( srom - > reg_base + EXYNOS_SROM_BW ) ;
2016-04-11 13:12:27 +05:30
bw = ( bw & ~ ( EXYNOS_SROM_BW__CS_MASK < < bank ) ) | ( cs < < bank ) ;
2016-06-17 18:08:10 +01:00
writel_relaxed ( bw , srom - > reg_base + EXYNOS_SROM_BW ) ;
writel_relaxed ( pmc | ( timing [ 0 ] < < EXYNOS_SROM_BCX__TACP__SHIFT ) |
( timing [ 1 ] < < EXYNOS_SROM_BCX__TCAH__SHIFT ) |
( timing [ 2 ] < < EXYNOS_SROM_BCX__TCOH__SHIFT ) |
( timing [ 3 ] < < EXYNOS_SROM_BCX__TACC__SHIFT ) |
( timing [ 4 ] < < EXYNOS_SROM_BCX__TCOS__SHIFT ) |
( timing [ 5 ] < < EXYNOS_SROM_BCX__TACS__SHIFT ) ,
srom - > reg_base + EXYNOS_SROM_BC0 + bank ) ;
2016-04-11 13:12:27 +05:30
return 0 ;
}
2016-04-11 13:12:24 +05:30
static int exynos_srom_probe ( struct platform_device * pdev )
{
2016-04-11 13:12:27 +05:30
struct device_node * np , * child ;
2016-04-11 13:12:24 +05:30
struct exynos_srom * srom ;
struct device * dev = & pdev - > dev ;
2016-04-11 13:12:27 +05:30
bool bad_bank_config = false ;
2016-04-11 13:12:24 +05:30
np = dev - > of_node ;
if ( ! np ) {
dev_err ( & pdev - > dev , " could not find device info \n " ) ;
return - EINVAL ;
}
srom = devm_kzalloc ( & pdev - > dev ,
2020-07-24 20:23:27 +02:00
sizeof ( struct exynos_srom ) , GFP_KERNEL ) ;
2016-04-11 13:12:24 +05:30
if ( ! srom )
return - ENOMEM ;
srom - > dev = dev ;
srom - > reg_base = of_iomap ( np , 0 ) ;
if ( ! srom - > reg_base ) {
dev_err ( & pdev - > dev , " iomap of exynos srom controller failed \n " ) ;
return - ENOMEM ;
}
platform_set_drvdata ( pdev , srom ) ;
srom - > reg_offset = exynos_srom_alloc_reg_dump ( exynos_srom_offsets ,
2020-07-24 20:23:27 +02:00
ARRAY_SIZE ( exynos_srom_offsets ) ) ;
2016-04-11 13:12:24 +05:30
if ( ! srom - > reg_offset ) {
iounmap ( srom - > reg_base ) ;
return - ENOMEM ;
}
2016-04-11 13:12:27 +05:30
for_each_child_of_node ( np , child ) {
if ( exynos_srom_configure_bank ( srom , child ) ) {
dev_err ( dev ,
2018-08-27 19:57:23 -05:00
" Could not decode bank configuration for %pOFn \n " ,
child ) ;
2016-04-11 13:12:27 +05:30
bad_bank_config = true ;
}
}
/*
* If any bank failed to configure , we still provide suspend / resume ,
* but do not probe child devices
*/
if ( bad_bank_config )
return 0 ;
return of_platform_populate ( np , NULL , NULL , dev ) ;
2016-04-11 13:12:24 +05:30
}
# ifdef CONFIG_PM_SLEEP
static void exynos_srom_save ( void __iomem * base ,
2020-07-24 20:23:27 +02:00
struct exynos_srom_reg_dump * rd ,
unsigned int num_regs )
2016-04-11 13:12:24 +05:30
{
for ( ; num_regs > 0 ; - - num_regs , + + rd )
rd - > value = readl ( base + rd - > offset ) ;
}
static void exynos_srom_restore ( void __iomem * base ,
2020-07-24 20:23:27 +02:00
const struct exynos_srom_reg_dump * rd ,
unsigned int num_regs )
2016-04-11 13:12:24 +05:30
{
for ( ; num_regs > 0 ; - - num_regs , + + rd )
writel ( rd - > value , base + rd - > offset ) ;
}
static int exynos_srom_suspend ( struct device * dev )
{
struct exynos_srom * srom = dev_get_drvdata ( dev ) ;
exynos_srom_save ( srom - > reg_base , srom - > reg_offset ,
2020-07-24 20:23:27 +02:00
ARRAY_SIZE ( exynos_srom_offsets ) ) ;
2016-04-11 13:12:24 +05:30
return 0 ;
}
static int exynos_srom_resume ( struct device * dev )
{
struct exynos_srom * srom = dev_get_drvdata ( dev ) ;
exynos_srom_restore ( srom - > reg_base , srom - > reg_offset ,
2020-07-24 20:23:27 +02:00
ARRAY_SIZE ( exynos_srom_offsets ) ) ;
2016-04-11 13:12:24 +05:30
return 0 ;
}
# endif
static const struct of_device_id of_exynos_srom_ids [ ] = {
{
. compatible = " samsung,exynos4210-srom " ,
} ,
{ } ,
} ;
static SIMPLE_DEV_PM_OPS ( exynos_srom_pm_ops , exynos_srom_suspend , exynos_srom_resume ) ;
static struct platform_driver exynos_srom_driver = {
. probe = exynos_srom_probe ,
. driver = {
. name = " exynos-srom " ,
. of_match_table = of_exynos_srom_ids ,
. pm = & exynos_srom_pm_ops ,
2016-06-16 20:37:47 -04:00
. suppress_bind_attrs = true ,
2016-04-11 13:12:24 +05:30
} ,
} ;
2016-06-16 20:37:47 -04:00
builtin_platform_driver ( exynos_srom_driver ) ;