diff options
Diffstat (limited to 'drivers/nvmem')
-rw-r--r-- | drivers/nvmem/Kconfig | 11 | ||||
-rw-r--r-- | drivers/nvmem/Makefile | 3 | ||||
-rw-r--r-- | drivers/nvmem/core.c | 2 | ||||
-rw-r--r-- | drivers/nvmem/imx-ocotp.c | 7 | ||||
-rw-r--r-- | drivers/nvmem/lpc18xx_eeprom.c | 1 | ||||
-rw-r--r-- | drivers/nvmem/mtk-efuse.c | 1 | ||||
-rw-r--r-- | drivers/nvmem/qfprom.c | 1 | ||||
-rw-r--r-- | drivers/nvmem/rave-sp-eeprom.c | 6 | ||||
-rw-r--r-- | drivers/nvmem/sc27xx-efuse.c | 264 | ||||
-rw-r--r-- | drivers/nvmem/uniphier-efuse.c | 1 |
10 files changed, 293 insertions, 4 deletions
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 54a3c298247b..0a7a470ee859 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -181,4 +181,15 @@ config RAVE_SP_EEPROM help Say y here to enable Rave SP EEPROM support. +config SC27XX_EFUSE + tristate "Spreadtrum SC27XX eFuse Support" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + depends on HAS_IOMEM + help + This is a simple driver to dump specified values of Spreadtrum + SC27XX PMICs from eFuse. + + This driver can also be built as a module. If so, the module + will be called nvmem-sc27xx-efuse. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 27e96a8efd1c..4e8c61628f1a 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -39,4 +39,5 @@ obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o nvmem_snvs_lpgpr-y := snvs_lpgpr.o obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o - +obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o +nvmem-sc27xx-efuse-y := sc27xx-efuse.o diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 514d1dfc5630..aa1657831b70 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -31,7 +31,6 @@ struct nvmem_device { struct device dev; int stride; int word_size; - int ncells; int id; int users; size_t size; @@ -389,7 +388,6 @@ int nvmem_add_cells(struct nvmem_device *nvmem, nvmem_cell_add(cells[i]); } - nvmem->ncells = ncells; /* remove tmp array */ kfree(cells); diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 60816c856dd6..afb429a417fe 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -409,6 +409,12 @@ static const struct ocotp_params imx6sl_params = { .set_timing = imx_ocotp_set_imx6_timing, }; +static const struct ocotp_params imx6sll_params = { + .nregs = 128, + .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, +}; + static const struct ocotp_params imx6sx_params = { .nregs = 128, .bank_address_words = 0, @@ -433,6 +439,7 @@ static const struct of_device_id imx_ocotp_dt_ids[] = { { .compatible = "fsl,imx6sx-ocotp", .data = &imx6sx_params }, { .compatible = "fsl,imx6ul-ocotp", .data = &imx6ul_params }, { .compatible = "fsl,imx7d-ocotp", .data = &imx7d_params }, + { .compatible = "fsl,imx6sll-ocotp", .data = &imx6sll_params }, { }, }; MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c index b1af966206a6..a9534a6e8636 100644 --- a/drivers/nvmem/lpc18xx_eeprom.c +++ b/drivers/nvmem/lpc18xx_eeprom.c @@ -14,6 +14,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/nvmem-provider.h> #include <linux/platform_device.h> #include <linux/reset.h> diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index e66adf17a747..58c998b2e3bc 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -14,6 +14,7 @@ #include <linux/device.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/io.h> #include <linux/nvmem-provider.h> #include <linux/platform_device.h> diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index 4f650baad983..fbb1f1df6fc7 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -13,6 +13,7 @@ #include <linux/device.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/io.h> #include <linux/nvmem-provider.h> #include <linux/platform_device.h> diff --git a/drivers/nvmem/rave-sp-eeprom.c b/drivers/nvmem/rave-sp-eeprom.c index 50aeea6ec6cc..66699d44f73d 100644 --- a/drivers/nvmem/rave-sp-eeprom.c +++ b/drivers/nvmem/rave-sp-eeprom.c @@ -35,6 +35,7 @@ enum rave_sp_eeprom_header_size { RAVE_SP_EEPROM_HEADER_SMALL = 4U, RAVE_SP_EEPROM_HEADER_BIG = 5U, }; +#define RAVE_SP_EEPROM_HEADER_MAX RAVE_SP_EEPROM_HEADER_BIG #define RAVE_SP_EEPROM_PAGE_SIZE 32U @@ -97,9 +98,12 @@ static int rave_sp_eeprom_io(struct rave_sp_eeprom *eeprom, const unsigned int rsp_size = is_write ? sizeof(*page) - sizeof(page->data) : sizeof(*page); unsigned int offset = 0; - u8 cmd[cmd_size]; + u8 cmd[RAVE_SP_EEPROM_HEADER_MAX + sizeof(page->data)]; int ret; + if (WARN_ON(cmd_size > sizeof(cmd))) + return -EINVAL; + cmd[offset++] = eeprom->address; cmd[offset++] = 0; cmd[offset++] = type; diff --git a/drivers/nvmem/sc27xx-efuse.c b/drivers/nvmem/sc27xx-efuse.c new file mode 100644 index 000000000000..33185d8d82cf --- /dev/null +++ b/drivers/nvmem/sc27xx-efuse.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Spreadtrum Communications Inc. + +#include <linux/hwspinlock.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/nvmem-provider.h> + +/* PMIC global registers definition */ +#define SC27XX_MODULE_EN 0xc08 +#define SC27XX_EFUSE_EN BIT(6) + +/* Efuse controller registers definition */ +#define SC27XX_EFUSE_GLB_CTRL 0x0 +#define SC27XX_EFUSE_DATA_RD 0x4 +#define SC27XX_EFUSE_DATA_WR 0x8 +#define SC27XX_EFUSE_BLOCK_INDEX 0xc +#define SC27XX_EFUSE_MODE_CTRL 0x10 +#define SC27XX_EFUSE_STATUS 0x14 +#define SC27XX_EFUSE_WR_TIMING_CTRL 0x20 +#define SC27XX_EFUSE_RD_TIMING_CTRL 0x24 +#define SC27XX_EFUSE_EFUSE_DEB_CTRL 0x28 + +/* Mask definition for SC27XX_EFUSE_BLOCK_INDEX register */ +#define SC27XX_EFUSE_BLOCK_MASK GENMASK(4, 0) + +/* Bits definitions for SC27XX_EFUSE_MODE_CTRL register */ +#define SC27XX_EFUSE_PG_START BIT(0) +#define SC27XX_EFUSE_RD_START BIT(1) +#define SC27XX_EFUSE_CLR_RDDONE BIT(2) + +/* Bits definitions for SC27XX_EFUSE_STATUS register */ +#define SC27XX_EFUSE_PGM_BUSY BIT(0) +#define SC27XX_EFUSE_READ_BUSY BIT(1) +#define SC27XX_EFUSE_STANDBY BIT(2) +#define SC27XX_EFUSE_GLOBAL_PROT BIT(3) +#define SC27XX_EFUSE_RD_DONE BIT(4) + +/* Block number and block width (bytes) definitions */ +#define SC27XX_EFUSE_BLOCK_MAX 32 +#define SC27XX_EFUSE_BLOCK_WIDTH 2 + +/* Timeout (ms) for the trylock of hardware spinlocks */ +#define SC27XX_EFUSE_HWLOCK_TIMEOUT 5000 + +/* Timeout (us) of polling the status */ +#define SC27XX_EFUSE_POLL_TIMEOUT 3000000 +#define SC27XX_EFUSE_POLL_DELAY_US 10000 + +struct sc27xx_efuse { + struct device *dev; + struct regmap *regmap; + struct hwspinlock *hwlock; + struct mutex mutex; + u32 base; +}; + +/* + * 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 sc27xx_efuse_lock(struct sc27xx_efuse *efuse) +{ + int ret; + + mutex_lock(&efuse->mutex); + + ret = hwspin_lock_timeout_raw(efuse->hwlock, + SC27XX_EFUSE_HWLOCK_TIMEOUT); + if (ret) { + dev_err(efuse->dev, "timeout to get the hwspinlock\n"); + mutex_unlock(&efuse->mutex); + return ret; + } + + return 0; +} + +static void sc27xx_efuse_unlock(struct sc27xx_efuse *efuse) +{ + hwspin_unlock_raw(efuse->hwlock); + mutex_unlock(&efuse->mutex); +} + +static int sc27xx_efuse_poll_status(struct sc27xx_efuse *efuse, u32 bits) +{ + int ret; + u32 val; + + ret = regmap_read_poll_timeout(efuse->regmap, + efuse->base + SC27XX_EFUSE_STATUS, + val, (val & bits), + SC27XX_EFUSE_POLL_DELAY_US, + SC27XX_EFUSE_POLL_TIMEOUT); + if (ret) { + dev_err(efuse->dev, "timeout to update the efuse status\n"); + return ret; + } + + return 0; +} + +static int sc27xx_efuse_read(void *context, u32 offset, void *val, size_t bytes) +{ + struct sc27xx_efuse *efuse = context; + u32 buf; + int ret; + + if (offset > SC27XX_EFUSE_BLOCK_MAX || bytes > SC27XX_EFUSE_BLOCK_WIDTH) + return -EINVAL; + + ret = sc27xx_efuse_lock(efuse); + if (ret) + return ret; + + /* Enable the efuse controller. */ + ret = regmap_update_bits(efuse->regmap, SC27XX_MODULE_EN, + SC27XX_EFUSE_EN, SC27XX_EFUSE_EN); + if (ret) + goto unlock_efuse; + + /* + * Before reading, we should ensure the efuse controller is in + * standby state. + */ + ret = sc27xx_efuse_poll_status(efuse, SC27XX_EFUSE_STANDBY); + if (ret) + goto disable_efuse; + + /* Set the block address to be read. */ + ret = regmap_write(efuse->regmap, + efuse->base + SC27XX_EFUSE_BLOCK_INDEX, + offset & SC27XX_EFUSE_BLOCK_MASK); + if (ret) + goto disable_efuse; + + /* Start reading process from efuse memory. */ + ret = regmap_update_bits(efuse->regmap, + efuse->base + SC27XX_EFUSE_MODE_CTRL, + SC27XX_EFUSE_RD_START, + SC27XX_EFUSE_RD_START); + if (ret) + goto disable_efuse; + + /* + * Polling the read done status to make sure the reading process + * is completed, that means the data can be read out now. + */ + ret = sc27xx_efuse_poll_status(efuse, SC27XX_EFUSE_RD_DONE); + if (ret) + goto disable_efuse; + + /* Read data from efuse memory. */ + ret = regmap_read(efuse->regmap, efuse->base + SC27XX_EFUSE_DATA_RD, + &buf); + if (ret) + goto disable_efuse; + + /* Clear the read done flag. */ + ret = regmap_update_bits(efuse->regmap, + efuse->base + SC27XX_EFUSE_MODE_CTRL, + SC27XX_EFUSE_CLR_RDDONE, + SC27XX_EFUSE_CLR_RDDONE); + +disable_efuse: + /* Disable the efuse controller after reading. */ + regmap_update_bits(efuse->regmap, SC27XX_MODULE_EN, SC27XX_EFUSE_EN, 0); +unlock_efuse: + sc27xx_efuse_unlock(efuse); + + if (!ret) + memcpy(val, &buf, bytes); + + return ret; +} + +static int sc27xx_efuse_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct nvmem_config econfig = { }; + struct nvmem_device *nvmem; + struct sc27xx_efuse *efuse; + int ret; + + efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); + if (!efuse) + return -ENOMEM; + + efuse->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!efuse->regmap) { + dev_err(&pdev->dev, "failed to get efuse regmap\n"); + return -ENODEV; + } + + ret = of_property_read_u32(np, "reg", &efuse->base); + if (ret) { + dev_err(&pdev->dev, "failed to get efuse base address\n"); + return ret; + } + + ret = of_hwspin_lock_get_id(np, 0); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get hwspinlock id\n"); + return ret; + } + + efuse->hwlock = hwspin_lock_request_specific(ret); + if (!efuse->hwlock) { + dev_err(&pdev->dev, "failed to request hwspinlock\n"); + return -ENXIO; + } + + mutex_init(&efuse->mutex); + efuse->dev = &pdev->dev; + platform_set_drvdata(pdev, efuse); + + econfig.stride = 1; + econfig.word_size = 1; + econfig.read_only = true; + econfig.name = "sc27xx-efuse"; + econfig.size = SC27XX_EFUSE_BLOCK_MAX * SC27XX_EFUSE_BLOCK_WIDTH; + econfig.reg_read = sc27xx_efuse_read; + 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 config\n"); + hwspin_lock_free(efuse->hwlock); + return PTR_ERR(nvmem); + } + + return 0; +} + +static int sc27xx_efuse_remove(struct platform_device *pdev) +{ + struct sc27xx_efuse *efuse = platform_get_drvdata(pdev); + + hwspin_lock_free(efuse->hwlock); + return 0; +} + +static const struct of_device_id sc27xx_efuse_of_match[] = { + { .compatible = "sprd,sc2731-efuse" }, + { } +}; + +static struct platform_driver sc27xx_efuse_driver = { + .probe = sc27xx_efuse_probe, + .remove = sc27xx_efuse_remove, + .driver = { + .name = "sc27xx-efuse", + .of_match_table = sc27xx_efuse_of_match, + }, +}; + +module_platform_driver(sc27xx_efuse_driver); + +MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>"); +MODULE_DESCRIPTION("Spreadtrum SC27xx efuse driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/uniphier-efuse.c b/drivers/nvmem/uniphier-efuse.c index 271f0b2ff86a..286910336ef6 100644 --- a/drivers/nvmem/uniphier-efuse.c +++ b/drivers/nvmem/uniphier-efuse.c @@ -16,6 +16,7 @@ #include <linux/device.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/nvmem-provider.h> #include <linux/platform_device.h> |