[PATCH v2 1/2] dt-bindings: nvmem: Add Spreadtrum SC27XX efuse controller documentation

Subsystems: nvmem framework, open firmware and flattened device tree bindings, the rest

STALE3008d REVIEWED: 6 (6M)

1 review trailer (1 from subsystem maintainers).

5 messages, 2 authors, 2018-07-09 · open the first message on its own page

[PATCH v2 1/2] dt-bindings: nvmem: Add Spreadtrum SC27XX efuse controller documentation

From: Baolin Wang <hidden>
Date: 2018-06-25 06:32:17

This patch adds the binding documentation for Spreadtrum SC27XX series
PMICs efuse controller device.

Signed-off-by: Baolin Wang <redacted>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changes since v1:
 - Add reviewed tag from Rob.
---
 .../devicetree/bindings/nvmem/sc27xx-efuse.txt     |   52 ++++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/nvmem/sc27xx-efuse.txt
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";
+	};
-- 
1.7.9.5

[PATCH v2 2/2] nvmem: Add Spreadtrum SC27XX efuse support

From: Baolin Wang <hidden>
Date: 2018-06-25 06:32:24

From: Freeman Liu <redacted>

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 <redacted>
Signed-off-by: Baolin Wang <redacted>
---
Changes since v1:
 - Fix one typo.
---
 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

Re: [PATCH v2 2/2] nvmem: Add Spreadtrum SC27XX efuse support

From: Baolin Wang <hidden>
Date: 2018-07-09 09:26:28

Hi Srinivas,

On 25 June 2018 at 14:31, Baolin Wang [off-list ref] wrote:
From: Freeman Liu <redacted>

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 <redacted>
Signed-off-by: Baolin Wang <redacted>
---
Changes since v1:
 - Fix one typo.
---
Do you have any comments for this patch set? Thanks.
quoted hunk
 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

Re: [PATCH v2 2/2] nvmem: Add Spreadtrum SC27XX efuse support

From: Srinivas Kandagatla <hidden>
Date: 2018-07-09 09:41:18


On 09/07/18 10:26, Baolin Wang wrote:
Hi Srinivas,

On 25 June 2018 at 14:31, Baolin Wang[off-list ref]  wrote:
quoted
From: Freeman Liu<redacted>

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<redacted>
Signed-off-by: Baolin Wang<redacted>
---
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

Re: [PATCH v2 2/2] nvmem: Add Spreadtrum SC27XX efuse support

From: Baolin Wang <hidden>
Date: 2018-07-09 10:30:42

On 9 July 2018 at 17:41, Srinivas Kandagatla
[off-list ref] wrote:

On 09/07/18 10:26, Baolin Wang wrote:
quoted
Hi Srinivas,

On 25 June 2018 at 14:31, Baolin Wang[off-list ref]  wrote:
quoted
From: Freeman Liu<redacted>

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<redacted>
Signed-off-by: Baolin Wang<redacted>
---
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.

-- 
Baolin Wang
Best Regards
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help