X-Git-Url: http://plrg.eecs.uci.edu/git/?p=firefly-linux-kernel-4.4.55.git;a=blobdiff_plain;f=drivers%2Fnvmem%2Frockchip-efuse.c;h=c117d071d96772ac6838184867dd4f6b8749fff2;hp=f552134242223959301a419a78fc08b0c65ec207;hb=28ce472032d4f09f1aa7a8171f40ffea850a2652;hpb=c6de7f1754bd474019c60d6f076fa3f704e46b78 diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index f55213424222..c117d071d967 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -14,148 +14,357 @@ * more details. */ -#include -#include -#include -#include +#include +#include #include #include #include -#include +#include +#include #include -#include +#include +#include +#include + +#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 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 RK3366_A_SHIFT 6 +#define RK3366_A_MASK 0x3ff +#define RK3366_RDEN BIT(2) +#define RK3366_AEN BIT(1) -#define REG_EFUSE_CTRL 0x0000 -#define REG_EFUSE_DOUT 0x0004 +#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) -struct rockchip_efuse_context { +#define REG_EFUSE_CTRL 0x0000 +#define REG_EFUSE_DOUT 0x0004 + +struct rockchip_efuse_chip { struct device *dev; void __iomem *base; - struct clk *efuse_clk; + struct clk *clk; + phys_addr_t phys; }; -static int rockchip_efuse_write(void *context, const void *data, size_t count) +static int rockchip_rk3288_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct rockchip_efuse_chip *efuse = context; + u8 *buf = val; + int ret; + + ret = clk_prepare_enable(efuse->clk); + if (ret < 0) { + dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); + return ret; + } + + writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); + udelay(1); + while (bytes--) { + writel(readl(efuse->base + REG_EFUSE_CTRL) & + (~(RK3288_A_MASK << RK3288_A_SHIFT)), + efuse->base + REG_EFUSE_CTRL); + writel(readl(efuse->base + REG_EFUSE_CTRL) | + ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), + efuse->base + REG_EFUSE_CTRL); + udelay(1); + writel(readl(efuse->base + REG_EFUSE_CTRL) | + RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); + udelay(1); + *buf++ = readb(efuse->base + REG_EFUSE_DOUT); + writel(readl(efuse->base + REG_EFUSE_CTRL) & + (~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_rk3288_efuse_secure_read(void *context, + unsigned int offset, + void *val, size_t bytes) { - /* Nothing TBD, Read-Only */ + struct rockchip_efuse_chip *efuse = context; + u8 *buf = val; + u32 wr_val; + int ret; + + ret = clk_prepare_enable(efuse->clk); + if (ret < 0) { + dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); + return ret; + } + + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, + RK3288_LOAD | RK3288_PGENB); + udelay(1); + while (bytes--) { + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & + (~(RK3288_A_MASK << RK3288_A_SHIFT)); + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | + ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT); + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + udelay(1); + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | + RK3288_STROBE; + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + udelay(1); + *buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT); + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & + (~RK3288_STROBE); + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + udelay(1); + } + + /* Switch to standby mode */ + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, + RK3288_PGENB | RK3288_CSB); + + clk_disable_unprepare(efuse->clk); + return 0; } -static int rockchip_efuse_read(void *context, - const void *reg, size_t reg_size, - void *val, size_t val_size) +static int rockchip_rk3366_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) { - unsigned int offset = *(u32 *)reg; - struct rockchip_efuse_context *_context = context; - void __iomem *base = _context->base; - struct clk *clk = _context->efuse_clk; + struct rockchip_efuse_chip *efuse = context; u8 *buf = val; int ret; - ret = clk_prepare_enable(clk); + ret = clk_prepare_enable(efuse->clk); if (ret < 0) { - dev_err(_context->dev, "failed to prepare/enable efuse clk\n"); + dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); return ret; } - writel(EFUSE_LOAD | EFUSE_PGENB, base + REG_EFUSE_CTRL); + writel(RK3366_RDEN, efuse->base + REG_EFUSE_CTRL); udelay(1); - while (val_size) { - writel(readl(base + REG_EFUSE_CTRL) & - (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), - base + REG_EFUSE_CTRL); - writel(readl(base + REG_EFUSE_CTRL) | - ((offset & EFUSE_A_MASK) << EFUSE_A_SHIFT), - base + REG_EFUSE_CTRL); + while (bytes--) { + writel(readl(efuse->base + REG_EFUSE_CTRL) & + (~(RK3366_A_MASK << RK3366_A_SHIFT)), + efuse->base + REG_EFUSE_CTRL); + writel(readl(efuse->base + REG_EFUSE_CTRL) | + ((offset++ & RK3366_A_MASK) << RK3366_A_SHIFT), + efuse->base + REG_EFUSE_CTRL); udelay(1); - writel(readl(base + REG_EFUSE_CTRL) | - EFUSE_STROBE, base + REG_EFUSE_CTRL); + writel(readl(efuse->base + REG_EFUSE_CTRL) | + RK3366_AEN, efuse->base + REG_EFUSE_CTRL); udelay(1); - *buf++ = readb(base + REG_EFUSE_DOUT); - writel(readl(base + REG_EFUSE_CTRL) & - (~EFUSE_STROBE), base + REG_EFUSE_CTRL); + *buf++ = readb(efuse->base + REG_EFUSE_DOUT); + writel(readl(efuse->base + REG_EFUSE_CTRL) & + (~RK3366_AEN), efuse->base + REG_EFUSE_CTRL); udelay(1); + } + + writel(readl(efuse->base + REG_EFUSE_CTRL) & + (~RK3366_RDEN), efuse->base + REG_EFUSE_CTRL); + + clk_disable_unprepare(efuse->clk); + + return 0; +} + +static int rockchip_rk3368_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct rockchip_efuse_chip *efuse = context; + u8 *buf = val; + u32 wr_val; + int ret; + + ret = clk_prepare_enable(efuse->clk); + if (ret < 0) { + dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); + return ret; + } - val_size -= 1; - offset += 1; + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, + RK3288_LOAD | RK3288_PGENB); + udelay(1); + while (bytes--) { + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & + (~(RK3288_A_MASK << RK3288_A_SHIFT)); + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | + ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT); + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + udelay(1); + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | + RK3288_STROBE; + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + udelay(1); + *buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT); + wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & + (~RK3288_STROBE); + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); + udelay(1); } /* Switch to standby mode */ - writel(EFUSE_PGENB | EFUSE_CSB, base + REG_EFUSE_CTRL); + sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, + RK3288_PGENB | RK3288_CSB); - clk_disable_unprepare(clk); + clk_disable_unprepare(efuse->clk); 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, -}; +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; -static struct regmap_config rockchip_efuse_regmap_config = { - .reg_bits = 32, - .reg_stride = 1, - .val_bits = 8, -}; + 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); + udelay(1); + 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; + } + + /* Switch to standby mode */ + writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL); + + memcpy(val, buf + addr_offset, bytes); + + kfree(buf); + + clk_disable_unprepare(efuse->clk); + + return 0; +} static struct nvmem_config econfig = { .name = "rockchip-efuse", .owner = THIS_MODULE, + .stride = 1, + .word_size = 1, .read_only = true, }; static const struct of_device_id rockchip_efuse_match[] = { - { .compatible = "rockchip,rockchip-efuse",}, + /* 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, + }, + { + .compatible = "rockchip,rk3288-efuse", + .data = (void *)&rockchip_rk3288_efuse_read, + }, + { + .compatible = "rockchip,rk3288-secure-efuse", + .data = (void *)&rockchip_rk3288_efuse_secure_read, + }, + { + .compatible = "rockchip,rk3366-efuse", + .data = (void *)&rockchip_rk3366_efuse_read, + }, + { + .compatible = "rockchip,rk3368-efuse", + .data = (void *)&rockchip_rk3368_efuse_read, + }, + { + .compatible = "rockchip,rk3399-efuse", + .data = (void *)&rockchip_rk3399_efuse_read, + }, { /* sentinel */}, }; MODULE_DEVICE_TABLE(of, rockchip_efuse_match); -static int rockchip_efuse_probe(struct platform_device *pdev) +static int __init rockchip_efuse_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; - struct regmap *regmap; - void __iomem *base; - struct clk *clk; - struct rockchip_efuse_context *context; + struct rockchip_efuse_chip *efuse; + const struct of_device_id *match; + struct device *dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - context = devm_kzalloc(dev, sizeof(struct rockchip_efuse_context), - GFP_KERNEL); - if (IS_ERR(context)) - return PTR_ERR(context); - - clk = devm_clk_get(dev, "pclk_efuse"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - context->dev = dev; - context->base = base; - context->efuse_clk = clk; - - rockchip_efuse_regmap_config.max_register = resource_size(res) - 1; - - regmap = devm_regmap_init(dev, &rockchip_efuse_bus, - context, &rockchip_efuse_regmap_config); - if (IS_ERR(regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(regmap); + 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; } - econfig.dev = dev; + + efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), + GFP_KERNEL); + if (!efuse) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + efuse->phys = res->start; + efuse->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(efuse->base)) + return PTR_ERR(efuse->base); + + efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse"); + if (IS_ERR(efuse->clk)) + return PTR_ERR(efuse->clk); + + efuse->dev = &pdev->dev; + econfig.size = resource_size(res); + econfig.reg_read = match->data; + econfig.priv = efuse; + econfig.dev = efuse->dev; nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); @@ -173,7 +382,6 @@ static int rockchip_efuse_remove(struct platform_device *pdev) } static struct platform_driver rockchip_efuse_driver = { - .probe = rockchip_efuse_probe, .remove = rockchip_efuse_remove, .driver = { .name = "rockchip-efuse", @@ -181,6 +389,13 @@ static struct platform_driver rockchip_efuse_driver = { }, }; -module_platform_driver(rockchip_efuse_driver); +static int __init rockchip_efuse_module_init(void) +{ + return platform_driver_probe(&rockchip_efuse_driver, + rockchip_efuse_probe); +} + +subsys_initcall(rockchip_efuse_module_init); + MODULE_DESCRIPTION("rockchip_efuse driver"); MODULE_LICENSE("GPL v2");