diff mbox series

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

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

Commit Message

(Exiting) Baolin Wang June 25, 2018, 6:31 a.m. UTC
This patch adds the binding documentation for Spreadtrum SC27XX series
PMICs efuse controller device.

Signed-off-by: Baolin Wang <baolin.wang@linaro.org>

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

-- 
1.7.9.5

--
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

Comments

(Exiting) Baolin Wang July 9, 2018, 9:26 a.m. UTC | #1
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
Srinivas Kandagatla July 9, 2018, 9:41 a.m. UTC | #2
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 mbox series

Patch

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";
+	};