2015-09-30 15:56:44 +03:00
/*
* Rockchip eFuse Driver
*
* Copyright ( c ) 2015 Rockchip Electronics Co . Ltd .
* Author : Caesar Wang < wxt @ rock - chips . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*/
2015-12-14 12:43:39 +03:00
# include <linux/clk.h>
# include <linux/delay.h>
2015-09-30 15:56:44 +03:00
# include <linux/device.h>
# include <linux/io.h>
# include <linux/module.h>
2015-12-14 12:43:39 +03:00
# include <linux/nvmem-provider.h>
# include <linux/slab.h>
2015-09-30 15:56:44 +03:00
# include <linux/of.h>
2016-09-02 12:14:27 +03:00
# include <linux/of_platform.h>
2015-12-14 12:43:39 +03:00
# include <linux/platform_device.h>
2015-09-30 15:56:44 +03:00
2016-09-02 12:14:27 +03:00
# define RK3288_A_SHIFT 6
# define RK3288_A_MASK 0x3ff
# define RK3288_PGENB BIT(3)
# define RK3288_LOAD BIT(2)
# define RK3288_STROBE BIT(1)
# define RK3288_CSB BIT(0)
# define RK3399_A_SHIFT 16
# define RK3399_A_MASK 0x3ff
# define RK3399_NBYTES 4
# define RK3399_STROBSFTSEL BIT(9)
# define RK3399_RSB BIT(7)
# define RK3399_PD BIT(5)
# define RK3399_PGENB BIT(3)
# define RK3399_LOAD BIT(2)
# define RK3399_STROBE BIT(1)
# define RK3399_CSB BIT(0)
# define REG_EFUSE_CTRL 0x0000
# define REG_EFUSE_DOUT 0x0004
2015-09-30 15:56:44 +03:00
2015-12-14 12:43:39 +03:00
struct rockchip_efuse_chip {
2015-09-30 15:56:44 +03:00
struct device * dev ;
void __iomem * base ;
2015-12-14 12:43:39 +03:00
struct clk * clk ;
2015-09-30 15:56:44 +03:00
} ;
2016-09-02 12:14:27 +03:00
static int rockchip_rk3288_efuse_read ( void * context , unsigned int offset ,
void * val , size_t bytes )
2015-09-30 15:56:44 +03:00
{
2015-12-14 12:43:39 +03:00
struct rockchip_efuse_chip * efuse = context ;
2015-09-30 15:56:44 +03:00
u8 * buf = val ;
int ret ;
2015-12-14 12:43:39 +03:00
ret = clk_prepare_enable ( efuse - > clk ) ;
2015-09-30 15:56:44 +03:00
if ( ret < 0 ) {
2015-12-14 12:43:39 +03:00
dev_err ( efuse - > dev , " failed to prepare/enable efuse clk \n " ) ;
2015-09-30 15:56:44 +03:00
return ret ;
}
2016-09-02 12:14:27 +03:00
writel ( RK3288_LOAD | RK3288_PGENB , efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 15:56:44 +03:00
udelay ( 1 ) ;
2016-04-24 22:28:11 +03:00
while ( bytes - - ) {
2015-12-14 12:43:39 +03:00
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) &
2016-09-02 12:14:27 +03:00
( ~ ( RK3288_A_MASK < < RK3288_A_SHIFT ) ) ,
2015-12-14 12:43:39 +03:00
efuse - > base + REG_EFUSE_CTRL ) ;
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) |
2016-09-02 12:14:27 +03:00
( ( offset + + & RK3288_A_MASK ) < < RK3288_A_SHIFT ) ,
2015-12-14 12:43:39 +03:00
efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 15:56:44 +03:00
udelay ( 1 ) ;
2015-12-14 12:43:39 +03:00
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) |
2016-09-02 12:14:27 +03:00
RK3288_STROBE , efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 15:56:44 +03:00
udelay ( 1 ) ;
2015-12-14 12:43:39 +03:00
* buf + + = readb ( efuse - > base + REG_EFUSE_DOUT ) ;
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) &
2016-09-02 12:14:27 +03:00
( ~ RK3288_STROBE ) , efuse - > base + REG_EFUSE_CTRL ) ;
udelay ( 1 ) ;
}
/* Switch to standby mode */
writel ( RK3288_PGENB | RK3288_CSB , efuse - > base + REG_EFUSE_CTRL ) ;
clk_disable_unprepare ( efuse - > clk ) ;
return 0 ;
}
static int rockchip_rk3399_efuse_read ( void * context , unsigned int offset ,
void * val , size_t bytes )
{
struct rockchip_efuse_chip * efuse = context ;
unsigned int addr_start , addr_end , addr_offset , addr_len ;
u32 out_value ;
u8 * buf ;
int ret , i = 0 ;
ret = clk_prepare_enable ( efuse - > clk ) ;
if ( ret < 0 ) {
dev_err ( efuse - > dev , " failed to prepare/enable efuse clk \n " ) ;
return ret ;
}
addr_start = rounddown ( offset , RK3399_NBYTES ) / RK3399_NBYTES ;
addr_end = roundup ( offset + bytes , RK3399_NBYTES ) / RK3399_NBYTES ;
addr_offset = offset % RK3399_NBYTES ;
addr_len = addr_end - addr_start ;
buf = kzalloc ( sizeof ( * buf ) * addr_len * RK3399_NBYTES , GFP_KERNEL ) ;
if ( ! buf ) {
clk_disable_unprepare ( efuse - > clk ) ;
return - ENOMEM ;
}
writel ( RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB ,
efuse - > base + REG_EFUSE_CTRL ) ;
udelay ( 1 ) ;
while ( addr_len - - ) {
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) | RK3399_STROBE |
( ( addr_start + + & RK3399_A_MASK ) < < RK3399_A_SHIFT ) ,
efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 15:56:44 +03:00
udelay ( 1 ) ;
2016-09-02 12:14:27 +03:00
out_value = readl ( efuse - > base + REG_EFUSE_DOUT ) ;
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) & ( ~ RK3399_STROBE ) ,
efuse - > base + REG_EFUSE_CTRL ) ;
udelay ( 1 ) ;
memcpy ( & buf [ i ] , & out_value , RK3399_NBYTES ) ;
i + = RK3399_NBYTES ;
2015-09-30 15:56:44 +03:00
}
/* Switch to standby mode */
2016-09-02 12:14:27 +03:00
writel ( RK3399_PD | RK3399_CSB , efuse - > base + REG_EFUSE_CTRL ) ;
memcpy ( val , buf + addr_offset , bytes ) ;
kfree ( buf ) ;
2015-09-30 15:56:44 +03:00
2015-12-14 12:43:39 +03:00
clk_disable_unprepare ( efuse - > clk ) ;
2015-09-30 15:56:44 +03:00
return 0 ;
}
static struct nvmem_config econfig = {
. name = " rockchip-efuse " ,
. owner = THIS_MODULE ,
2016-04-24 22:28:11 +03:00
. stride = 1 ,
. word_size = 1 ,
2015-09-30 15:56:44 +03:00
. read_only = true ,
} ;
static const struct of_device_id rockchip_efuse_match [ ] = {
2016-09-02 12:14:27 +03:00
/* deprecated but kept around for dts binding compatibility */
{
. compatible = " rockchip,rockchip-efuse " ,
. data = ( void * ) & rockchip_rk3288_efuse_read ,
} ,
{
. compatible = " rockchip,rk3066a-efuse " ,
. data = ( void * ) & rockchip_rk3288_efuse_read ,
} ,
{
. compatible = " rockchip,rk3188-efuse " ,
. data = ( void * ) & rockchip_rk3288_efuse_read ,
} ,
2017-06-09 12:59:10 +03:00
{
. compatible = " rockchip,rk322x-efuse " ,
. data = ( void * ) & rockchip_rk3288_efuse_read ,
} ,
2016-09-02 12:14:27 +03:00
{
. compatible = " rockchip,rk3288-efuse " ,
. data = ( void * ) & rockchip_rk3288_efuse_read ,
} ,
{
. compatible = " rockchip,rk3399-efuse " ,
. data = ( void * ) & rockchip_rk3399_efuse_read ,
} ,
2015-09-30 15:56:44 +03:00
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , rockchip_efuse_match ) ;
2015-09-30 16:46:06 +03:00
static int rockchip_efuse_probe ( struct platform_device * pdev )
2015-09-30 15:56:44 +03:00
{
struct resource * res ;
struct nvmem_device * nvmem ;
2015-12-14 12:43:39 +03:00
struct rockchip_efuse_chip * efuse ;
2016-09-02 12:14:27 +03:00
const struct of_device_id * match ;
struct device * dev = & pdev - > dev ;
match = of_match_device ( dev - > driver - > of_match_table , dev ) ;
if ( ! match | | ! match - > data ) {
dev_err ( dev , " failed to get match data \n " ) ;
return - EINVAL ;
}
2015-09-30 15:56:44 +03:00
2015-12-14 12:43:39 +03:00
efuse = devm_kzalloc ( & pdev - > dev , sizeof ( struct rockchip_efuse_chip ) ,
GFP_KERNEL ) ;
if ( ! efuse )
return - ENOMEM ;
2015-09-30 15:56:44 +03:00
2015-12-14 12:43:39 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
efuse - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( efuse - > base ) )
return PTR_ERR ( efuse - > base ) ;
2015-09-30 15:56:44 +03:00
2015-12-14 12:43:39 +03:00
efuse - > clk = devm_clk_get ( & pdev - > dev , " pclk_efuse " ) ;
if ( IS_ERR ( efuse - > clk ) )
return PTR_ERR ( efuse - > clk ) ;
2015-09-30 15:56:44 +03:00
2015-12-14 12:43:39 +03:00
efuse - > dev = & pdev - > dev ;
2016-04-24 22:28:11 +03:00
econfig . size = resource_size ( res ) ;
2016-09-02 12:14:27 +03:00
econfig . reg_read = match - > data ;
2016-04-24 22:28:11 +03:00
econfig . priv = efuse ;
2015-12-14 12:43:39 +03:00
econfig . dev = efuse - > dev ;
2015-09-30 15:56:44 +03:00
nvmem = nvmem_register ( & econfig ) ;
if ( IS_ERR ( nvmem ) )
return PTR_ERR ( nvmem ) ;
platform_set_drvdata ( pdev , nvmem ) ;
return 0 ;
}
2015-09-30 16:46:06 +03:00
static int rockchip_efuse_remove ( struct platform_device * pdev )
2015-09-30 15:56:44 +03:00
{
struct nvmem_device * nvmem = platform_get_drvdata ( pdev ) ;
return nvmem_unregister ( nvmem ) ;
}
static struct platform_driver rockchip_efuse_driver = {
. probe = rockchip_efuse_probe ,
. remove = rockchip_efuse_remove ,
. driver = {
. name = " rockchip-efuse " ,
. of_match_table = rockchip_efuse_match ,
} ,
} ;
module_platform_driver ( rockchip_efuse_driver ) ;
MODULE_DESCRIPTION ( " rockchip_efuse driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;