2015-09-30 13:56:44 +01: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 09:43:39 +00:00
# include <linux/clk.h>
# include <linux/delay.h>
2015-09-30 13:56:44 +01:00
# include <linux/device.h>
# include <linux/io.h>
# include <linux/module.h>
2015-12-14 09:43:39 +00:00
# include <linux/nvmem-provider.h>
# include <linux/slab.h>
2015-09-30 13:56:44 +01:00
# include <linux/of.h>
2015-12-14 09:43:39 +00:00
# include <linux/platform_device.h>
# include <linux/regmap.h>
2015-09-30 13:56:44 +01:00
# define EFUSE_A_SHIFT 6
# define EFUSE_A_MASK 0x3ff
# define EFUSE_PGENB BIT(3)
# define EFUSE_LOAD BIT(2)
# define EFUSE_STROBE BIT(1)
# define EFUSE_CSB BIT(0)
# define REG_EFUSE_CTRL 0x0000
# define REG_EFUSE_DOUT 0x0004
2015-12-14 09:43:39 +00:00
struct rockchip_efuse_chip {
2015-09-30 13:56:44 +01:00
struct device * dev ;
void __iomem * base ;
2015-12-14 09:43:39 +00:00
struct clk * clk ;
2015-09-30 13:56:44 +01:00
} ;
static int rockchip_efuse_write ( void * context , const void * data , size_t count )
{
/* Nothing TBD, Read-Only */
return 0 ;
}
static int rockchip_efuse_read ( void * context ,
const void * reg , size_t reg_size ,
void * val , size_t val_size )
{
unsigned int offset = * ( u32 * ) reg ;
2015-12-14 09:43:39 +00:00
struct rockchip_efuse_chip * efuse = context ;
2015-09-30 13:56:44 +01:00
u8 * buf = val ;
int ret ;
2015-12-14 09:43:39 +00:00
ret = clk_prepare_enable ( efuse - > clk ) ;
2015-09-30 13:56:44 +01:00
if ( ret < 0 ) {
2015-12-14 09:43:39 +00:00
dev_err ( efuse - > dev , " failed to prepare/enable efuse clk \n " ) ;
2015-09-30 13:56:44 +01:00
return ret ;
}
2015-12-14 09:43:39 +00:00
writel ( EFUSE_LOAD | EFUSE_PGENB , efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 13:56:44 +01:00
udelay ( 1 ) ;
while ( val_size ) {
2015-12-14 09:43:39 +00:00
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) &
2015-09-30 13:56:44 +01:00
( ~ ( EFUSE_A_MASK < < EFUSE_A_SHIFT ) ) ,
2015-12-14 09:43:39 +00:00
efuse - > base + REG_EFUSE_CTRL ) ;
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) |
2015-09-30 13:56:44 +01:00
( ( offset & EFUSE_A_MASK ) < < EFUSE_A_SHIFT ) ,
2015-12-14 09:43:39 +00:00
efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 13:56:44 +01:00
udelay ( 1 ) ;
2015-12-14 09:43:39 +00:00
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) |
EFUSE_STROBE , efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 13:56:44 +01:00
udelay ( 1 ) ;
2015-12-14 09:43:39 +00:00
* buf + + = readb ( efuse - > base + REG_EFUSE_DOUT ) ;
writel ( readl ( efuse - > base + REG_EFUSE_CTRL ) &
( ~ EFUSE_STROBE ) , efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 13:56:44 +01:00
udelay ( 1 ) ;
val_size - = 1 ;
offset + = 1 ;
}
/* Switch to standby mode */
2015-12-14 09:43:39 +00:00
writel ( EFUSE_PGENB | EFUSE_CSB , efuse - > base + REG_EFUSE_CTRL ) ;
2015-09-30 13:56:44 +01:00
2015-12-14 09:43:39 +00:00
clk_disable_unprepare ( efuse - > clk ) ;
2015-09-30 13:56:44 +01:00
return 0 ;
}
static struct regmap_bus rockchip_efuse_bus = {
. read = rockchip_efuse_read ,
. write = rockchip_efuse_write ,
. reg_format_endian_default = REGMAP_ENDIAN_NATIVE ,
. val_format_endian_default = REGMAP_ENDIAN_NATIVE ,
} ;
2015-09-30 21:46:06 +08:00
static struct regmap_config rockchip_efuse_regmap_config = {
2015-09-30 13:56:44 +01:00
. reg_bits = 32 ,
. reg_stride = 1 ,
. val_bits = 8 ,
} ;
static struct nvmem_config econfig = {
. name = " rockchip-efuse " ,
. owner = THIS_MODULE ,
. read_only = true ,
} ;
static const struct of_device_id rockchip_efuse_match [ ] = {
2015-12-14 09:43:39 +00:00
{ . compatible = " rockchip,rockchip-efuse " , } ,
2015-09-30 13:56:44 +01:00
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , rockchip_efuse_match ) ;
2015-09-30 21:46:06 +08:00
static int rockchip_efuse_probe ( struct platform_device * pdev )
2015-09-30 13:56:44 +01:00
{
struct resource * res ;
struct nvmem_device * nvmem ;
struct regmap * regmap ;
2015-12-14 09:43:39 +00:00
struct rockchip_efuse_chip * efuse ;
2015-09-30 13:56:44 +01:00
2015-12-14 09:43:39 +00:00
efuse = devm_kzalloc ( & pdev - > dev , sizeof ( struct rockchip_efuse_chip ) ,
GFP_KERNEL ) ;
if ( ! efuse )
return - ENOMEM ;
2015-09-30 13:56:44 +01:00
2015-12-14 09:43:39 +00: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 13:56:44 +01:00
2015-12-14 09:43:39 +00:00
efuse - > clk = devm_clk_get ( & pdev - > dev , " pclk_efuse " ) ;
if ( IS_ERR ( efuse - > clk ) )
return PTR_ERR ( efuse - > clk ) ;
2015-09-30 13:56:44 +01:00
2015-12-14 09:43:39 +00:00
efuse - > dev = & pdev - > dev ;
2015-09-30 13:56:44 +01:00
rockchip_efuse_regmap_config . max_register = resource_size ( res ) - 1 ;
2015-12-14 09:43:39 +00:00
regmap = devm_regmap_init ( efuse - > dev , & rockchip_efuse_bus ,
efuse , & rockchip_efuse_regmap_config ) ;
2015-09-30 13:56:44 +01:00
if ( IS_ERR ( regmap ) ) {
2015-12-14 09:43:39 +00:00
dev_err ( efuse - > dev , " regmap init failed \n " ) ;
2015-09-30 13:56:44 +01:00
return PTR_ERR ( regmap ) ;
}
2015-12-14 09:43:39 +00:00
econfig . dev = efuse - > dev ;
2015-09-30 13:56:44 +01:00
nvmem = nvmem_register ( & econfig ) ;
if ( IS_ERR ( nvmem ) )
return PTR_ERR ( nvmem ) ;
platform_set_drvdata ( pdev , nvmem ) ;
return 0 ;
}
2015-09-30 21:46:06 +08:00
static int rockchip_efuse_remove ( struct platform_device * pdev )
2015-09-30 13:56:44 +01: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 " ) ;