Message ID | a469ed55cb7c43901eaa8881cdd2a8845616d987.1529908086.git.baolin.wang@linaro.org |
---|---|
State | Accepted |
Commit | 3eb93e0825abf76ddb7960506be71e6f1a63ad17 |
Headers | show |
Series | [v2,1/2] dt-bindings: nvmem: Add Spreadtrum SC27XX efuse controller documentation | expand |
Hi Srinivas, On 25 June 2018 at 14:31, Baolin Wang <baolin.wang@linaro.org> wrote: > From: Freeman Liu <freeman.liu@spreadtrum.com> > > This patch add the efuse driver which is embeded in Spreadtrum SC27XX > series PMICs. The sc27xx efuse contains 32 blocks and each block's > data width is 16 bits. > > Signed-off-by: Freeman Liu <freeman.liu@spreadtrum.com> > Signed-off-by: Baolin Wang <baolin.wang@linaro.org> > --- > Changes since v1: > - Fix one typo. > --- Do you have any comments for this patch set? Thanks. > drivers/nvmem/Kconfig | 11 ++ > drivers/nvmem/Makefile | 3 +- > drivers/nvmem/sc27xx-efuse.c | 263 ++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 276 insertions(+), 1 deletion(-) > create mode 100644 drivers/nvmem/sc27xx-efuse.c > > diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig > index 54a3c29..0a7a470e 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 27e96a8..4e8c616 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/sc27xx-efuse.c b/drivers/nvmem/sc27xx-efuse.c > new file mode 100644 > index 0000000..07c210c > --- /dev/null > +++ b/drivers/nvmem/sc27xx-efuse.c > @@ -0,0 +1,263 @@ > +// 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"); > -- > 1.7.9.5 > -- Baolin Wang Best Regards -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 09/07/18 10:26, Baolin Wang wrote: > Hi Srinivas, > > On 25 June 2018 at 14:31, Baolin Wang<baolin.wang@linaro.org> wrote: >> From: Freeman Liu<freeman.liu@spreadtrum.com> >> >> This patch add the efuse driver which is embeded in Spreadtrum SC27XX >> series PMICs. The sc27xx efuse contains 32 blocks and each block's >> data width is 16 bits. >> >> Signed-off-by: Freeman Liu<freeman.liu@spreadtrum.com> >> Signed-off-by: Baolin Wang<baolin.wang@linaro.org> >> --- >> Changes since v1: >> - Fix one typo. >> --- > Do you have any comments for this patch set? Thanks. Thanks for your patience! These patches look good!, Normally I tend to send nvmem patches to Greg K.H just before rc5! I will queue them by end of this week! Thanks Srini > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/Documentation/devicetree/bindings/nvmem/sc27xx-efuse.txt b/Documentation/devicetree/bindings/nvmem/sc27xx-efuse.txt new file mode 100644 index 0000000..586c082 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/sc27xx-efuse.txt @@ -0,0 +1,52 @@ += Spreadtrum SC27XX PMIC eFuse device tree bindings = + +Required properties: +- compatible: Should be one of the following. + "sprd,sc2720-efuse" + "sprd,sc2721-efuse" + "sprd,sc2723-efuse" + "sprd,sc2730-efuse" + "sprd,sc2731-efuse" +- reg: Specify the address offset of efuse controller. +- hwlocks: Reference to a phandle of a hwlock provider node. + += Data cells = +Are child nodes of eFuse, bindings of which as described in +bindings/nvmem/nvmem.txt + +Example: + + sc2731_pmic: pmic@0 { + compatible = "sprd,sc2731"; + reg = <0>; + spi-max-frequency = <26000000>; + interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + + efuse@380 { + compatible = "sprd,sc2731-efuse"; + reg = <0x380>; + #address-cells = <1>; + #size-cells = <1>; + hwlocks = <&hwlock 12>; + + /* Data cells */ + thermal_calib: calib@10 { + reg = <0x10 0x2>; + }; + }; + }; + += Data consumers = +Are device nodes which consume nvmem data cells. + +Example: + + thermal { + ... + nvmem-cells = <&thermal_calib>; + nvmem-cell-names = "calibration"; + };