123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- // SPDX-License-Identifier: GPL-2.0
- // Copyright (C) 2019 Spreadtrum Communications Inc.
- #include <linux/clk.h>
- #include <linux/delay.h>
- #include <linux/hwspinlock.h>
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/nvmem-provider.h>
- #include <linux/of_device.h>
- #include <linux/platform_device.h>
- #define SPRD_EFUSE_ENABLE 0x20
- #define SPRD_EFUSE_ERR_FLAG 0x24
- #define SPRD_EFUSE_ERR_CLR 0x28
- #define SPRD_EFUSE_MAGIC_NUM 0x2c
- #define SPRD_EFUSE_FW_CFG 0x50
- #define SPRD_EFUSE_PW_SWT 0x54
- #define SPRD_EFUSE_MEM(val) (0x1000 + ((val) << 2))
- #define SPRD_EFUSE_VDD_EN BIT(0)
- #define SPRD_EFUSE_AUTO_CHECK_EN BIT(1)
- #define SPRD_EFUSE_DOUBLE_EN BIT(2)
- #define SPRD_EFUSE_MARGIN_RD_EN BIT(3)
- #define SPRD_EFUSE_LOCK_WR_EN BIT(4)
- #define SPRD_EFUSE_ERR_CLR_MASK GENMASK(13, 0)
- #define SPRD_EFUSE_ENK1_ON BIT(0)
- #define SPRD_EFUSE_ENK2_ON BIT(1)
- #define SPRD_EFUSE_PROG_EN BIT(2)
- #define SPRD_EFUSE_MAGIC_NUMBER 0x8810
- /* Block width (bytes) definitions */
- #define SPRD_EFUSE_BLOCK_WIDTH 4
- /*
- * The Spreadtrum AP efuse contains 2 parts: normal efuse and secure efuse,
- * and we can only access the normal efuse in kernel. So define the normal
- * block offset index and normal block numbers.
- */
- #define SPRD_EFUSE_NORMAL_BLOCK_NUMS 24
- #define SPRD_EFUSE_NORMAL_BLOCK_OFFSET 72
- /* Timeout (ms) for the trylock of hardware spinlocks */
- #define SPRD_EFUSE_HWLOCK_TIMEOUT 5000
- /*
- * Since different Spreadtrum SoC chip can have different normal block numbers
- * and offset. And some SoC can support block double feature, which means
- * when reading or writing data to efuse memory, the controller can save double
- * data in case one data become incorrect after a long period.
- *
- * Thus we should save them in the device data structure.
- */
- struct sprd_efuse_variant_data {
- u32 blk_nums;
- u32 blk_offset;
- bool blk_double;
- };
- struct sprd_efuse {
- struct device *dev;
- struct clk *clk;
- struct hwspinlock *hwlock;
- struct mutex mutex;
- void __iomem *base;
- const struct sprd_efuse_variant_data *data;
- };
- static const struct sprd_efuse_variant_data ums312_data = {
- .blk_nums = SPRD_EFUSE_NORMAL_BLOCK_NUMS,
- .blk_offset = SPRD_EFUSE_NORMAL_BLOCK_OFFSET,
- .blk_double = false,
- };
- /*
- * On Spreadtrum platform, we have multi-subsystems will access the unique
- * efuse controller, so we need one hardware spinlock to synchronize between
- * the multiple subsystems.
- */
- static int sprd_efuse_lock(struct sprd_efuse *efuse)
- {
- int ret;
- mutex_lock(&efuse->mutex);
- ret = hwspin_lock_timeout_raw(efuse->hwlock,
- SPRD_EFUSE_HWLOCK_TIMEOUT);
- if (ret) {
- dev_err(efuse->dev, "timeout get the hwspinlock\n");
- mutex_unlock(&efuse->mutex);
- return ret;
- }
- return 0;
- }
- static void sprd_efuse_unlock(struct sprd_efuse *efuse)
- {
- hwspin_unlock_raw(efuse->hwlock);
- mutex_unlock(&efuse->mutex);
- }
- static void sprd_efuse_set_prog_power(struct sprd_efuse *efuse, bool en)
- {
- u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT);
- if (en)
- val &= ~SPRD_EFUSE_ENK2_ON;
- else
- val &= ~SPRD_EFUSE_ENK1_ON;
- writel(val, efuse->base + SPRD_EFUSE_PW_SWT);
- /* Open or close efuse power need wait 1000us to make power stable. */
- usleep_range(1000, 1200);
- if (en)
- val |= SPRD_EFUSE_ENK1_ON;
- else
- val |= SPRD_EFUSE_ENK2_ON;
- writel(val, efuse->base + SPRD_EFUSE_PW_SWT);
- /* Open or close efuse power need wait 1000us to make power stable. */
- usleep_range(1000, 1200);
- }
- static void sprd_efuse_set_read_power(struct sprd_efuse *efuse, bool en)
- {
- u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
- if (en)
- val |= SPRD_EFUSE_VDD_EN;
- else
- val &= ~SPRD_EFUSE_VDD_EN;
- writel(val, efuse->base + SPRD_EFUSE_ENABLE);
- /* Open or close efuse power need wait 1000us to make power stable. */
- usleep_range(1000, 1200);
- }
- static void sprd_efuse_set_prog_lock(struct sprd_efuse *efuse, bool en)
- {
- u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
- if (en)
- val |= SPRD_EFUSE_LOCK_WR_EN;
- else
- val &= ~SPRD_EFUSE_LOCK_WR_EN;
- writel(val, efuse->base + SPRD_EFUSE_ENABLE);
- }
- static void sprd_efuse_set_auto_check(struct sprd_efuse *efuse, bool en)
- {
- u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
- if (en)
- val |= SPRD_EFUSE_AUTO_CHECK_EN;
- else
- val &= ~SPRD_EFUSE_AUTO_CHECK_EN;
- writel(val, efuse->base + SPRD_EFUSE_ENABLE);
- }
- static void sprd_efuse_set_data_double(struct sprd_efuse *efuse, bool en)
- {
- u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
- if (en)
- val |= SPRD_EFUSE_DOUBLE_EN;
- else
- val &= ~SPRD_EFUSE_DOUBLE_EN;
- writel(val, efuse->base + SPRD_EFUSE_ENABLE);
- }
- static void sprd_efuse_set_prog_en(struct sprd_efuse *efuse, bool en)
- {
- u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT);
- if (en)
- val |= SPRD_EFUSE_PROG_EN;
- else
- val &= ~SPRD_EFUSE_PROG_EN;
- writel(val, efuse->base + SPRD_EFUSE_PW_SWT);
- }
- static int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub,
- bool lock, u32 *data)
- {
- u32 status;
- int ret = 0;
- /*
- * We need set the correct magic number before writing the efuse to
- * allow programming, and block other programming until we clear the
- * magic number.
- */
- writel(SPRD_EFUSE_MAGIC_NUMBER,
- efuse->base + SPRD_EFUSE_MAGIC_NUM);
- /*
- * Power on the efuse, enable programme and enable double data
- * if asked.
- */
- sprd_efuse_set_prog_power(efuse, true);
- sprd_efuse_set_prog_en(efuse, true);
- sprd_efuse_set_data_double(efuse, doub);
- /*
- * Enable the auto-check function to validate if the programming is
- * successful.
- */
- if (lock)
- sprd_efuse_set_auto_check(efuse, true);
- writel(*data, efuse->base + SPRD_EFUSE_MEM(blk));
- /* Disable auto-check and data double after programming */
- if (lock)
- sprd_efuse_set_auto_check(efuse, false);
- sprd_efuse_set_data_double(efuse, false);
- /*
- * Check the efuse error status, if the programming is successful,
- * we should lock this efuse block to avoid programming again.
- */
- status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG);
- if (status) {
- dev_err(efuse->dev,
- "write error status %u of block %d\n", status, blk);
- writel(SPRD_EFUSE_ERR_CLR_MASK,
- efuse->base + SPRD_EFUSE_ERR_CLR);
- ret = -EBUSY;
- } else if (lock) {
- sprd_efuse_set_prog_lock(efuse, lock);
- writel(0, efuse->base + SPRD_EFUSE_MEM(blk));
- sprd_efuse_set_prog_lock(efuse, false);
- }
- sprd_efuse_set_prog_power(efuse, false);
- writel(0, efuse->base + SPRD_EFUSE_MAGIC_NUM);
- return ret;
- }
- static int sprd_efuse_raw_read(struct sprd_efuse *efuse, int blk, u32 *val,
- bool doub)
- {
- u32 status;
- /*
- * Need power on the efuse before reading data from efuse, and will
- * power off the efuse after reading process.
- */
- sprd_efuse_set_read_power(efuse, true);
- /* Enable double data if asked */
- sprd_efuse_set_data_double(efuse, doub);
- /* Start to read data from efuse block */
- *val = readl(efuse->base + SPRD_EFUSE_MEM(blk));
- /* Disable double data */
- sprd_efuse_set_data_double(efuse, false);
- /* Power off the efuse */
- sprd_efuse_set_read_power(efuse, false);
- /*
- * Check the efuse error status and clear them if there are some
- * errors occurred.
- */
- status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG);
- if (status) {
- dev_err(efuse->dev,
- "read error status %d of block %d\n", status, blk);
- writel(SPRD_EFUSE_ERR_CLR_MASK,
- efuse->base + SPRD_EFUSE_ERR_CLR);
- return -EBUSY;
- }
- return 0;
- }
- static int sprd_efuse_read(void *context, u32 offset, void *val, size_t bytes)
- {
- struct sprd_efuse *efuse = context;
- bool blk_double = efuse->data->blk_double;
- u32 index = offset / SPRD_EFUSE_BLOCK_WIDTH + efuse->data->blk_offset;
- u32 blk_offset = (offset % SPRD_EFUSE_BLOCK_WIDTH) * BITS_PER_BYTE;
- u32 data;
- int ret;
- ret = sprd_efuse_lock(efuse);
- if (ret)
- return ret;
- ret = clk_prepare_enable(efuse->clk);
- if (ret)
- goto unlock;
- ret = sprd_efuse_raw_read(efuse, index, &data, blk_double);
- if (!ret) {
- data >>= blk_offset;
- memcpy(val, &data, bytes);
- }
- clk_disable_unprepare(efuse->clk);
- unlock:
- sprd_efuse_unlock(efuse);
- return ret;
- }
- static int sprd_efuse_write(void *context, u32 offset, void *val, size_t bytes)
- {
- struct sprd_efuse *efuse = context;
- bool blk_double = efuse->data->blk_double;
- bool lock;
- int ret;
- ret = sprd_efuse_lock(efuse);
- if (ret)
- return ret;
- ret = clk_prepare_enable(efuse->clk);
- if (ret)
- goto unlock;
- /*
- * If the writing bytes are equal with the block width, which means the
- * whole block will be programmed. For this case, we should not allow
- * this block to be programmed again by locking this block.
- *
- * If the block was programmed partially, we should allow this block to
- * be programmed again.
- */
- if (bytes < SPRD_EFUSE_BLOCK_WIDTH)
- lock = false;
- else
- lock = true;
- ret = sprd_efuse_raw_prog(efuse, offset, blk_double, lock, val);
- clk_disable_unprepare(efuse->clk);
- unlock:
- sprd_efuse_unlock(efuse);
- return ret;
- }
- static int sprd_efuse_probe(struct platform_device *pdev)
- {
- struct device_node *np = pdev->dev.of_node;
- struct nvmem_device *nvmem;
- struct nvmem_config econfig = { };
- struct sprd_efuse *efuse;
- const struct sprd_efuse_variant_data *pdata;
- int ret;
- pdata = of_device_get_match_data(&pdev->dev);
- if (!pdata) {
- dev_err(&pdev->dev, "No matching driver data found\n");
- return -EINVAL;
- }
- efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);
- if (!efuse)
- return -ENOMEM;
- efuse->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(efuse->base))
- return PTR_ERR(efuse->base);
- ret = of_hwspin_lock_get_id(np, 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to get hwlock id\n");
- return ret;
- }
- efuse->hwlock = devm_hwspin_lock_request_specific(&pdev->dev, ret);
- if (!efuse->hwlock) {
- dev_err(&pdev->dev, "failed to request hwlock\n");
- return -ENXIO;
- }
- efuse->clk = devm_clk_get(&pdev->dev, "enable");
- if (IS_ERR(efuse->clk)) {
- dev_err(&pdev->dev, "failed to get enable clock\n");
- return PTR_ERR(efuse->clk);
- }
- mutex_init(&efuse->mutex);
- efuse->dev = &pdev->dev;
- efuse->data = pdata;
- econfig.stride = 1;
- econfig.word_size = 1;
- econfig.read_only = false;
- econfig.name = "sprd-efuse";
- econfig.size = efuse->data->blk_nums * SPRD_EFUSE_BLOCK_WIDTH;
- econfig.reg_read = sprd_efuse_read;
- econfig.reg_write = sprd_efuse_write;
- econfig.priv = efuse;
- econfig.dev = &pdev->dev;
- nvmem = devm_nvmem_register(&pdev->dev, &econfig);
- if (IS_ERR(nvmem)) {
- dev_err(&pdev->dev, "failed to register nvmem\n");
- return PTR_ERR(nvmem);
- }
- return 0;
- }
- static const struct of_device_id sprd_efuse_of_match[] = {
- { .compatible = "sprd,ums312-efuse", .data = &ums312_data },
- { }
- };
- static struct platform_driver sprd_efuse_driver = {
- .probe = sprd_efuse_probe,
- .driver = {
- .name = "sprd-efuse",
- .of_match_table = sprd_efuse_of_match,
- },
- };
- module_platform_driver(sprd_efuse_driver);
- MODULE_AUTHOR("Freeman Liu <[email protected]>");
- MODULE_DESCRIPTION("Spreadtrum AP efuse driver");
- MODULE_LICENSE("GPL v2");
|