Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1204312 > unrolled thread
| Started by | Sanchayan Maity <maitysanchayan@gmail.com> |
|---|---|
| First post | 2015-08-10 16:20 +0200 |
| Last post | 2015-08-10 16:20 +0200 |
| Articles | 7 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH v8 0/4] Implement OCOTP driver for Vybrid using NVMEM Sanchayan Maity <maitysanchayan@gmail.com> - 2015-08-10 16:20 +0200
[PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support Sanchayan Maity <maitysanchayan@gmail.com> - 2015-08-10 16:20 +0200
Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support Srinivas Kandagatla <srinivas.kandagatla@linaro.org> - 2015-08-10 17:00 +0200
Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support Srinivas Kandagatla <srinivas.kandagatla@linaro.org> - 2015-08-12 12:50 +0200
Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support maitysanchayan@gmail.com - 2015-08-12 13:40 +0200
Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support Srinivas Kandagatla <srinivas.kandagatla@linaro.org> - 2015-08-12 13:50 +0200
[PATCH v8 1/4] clk: clk-vf610: Add clock for Vybrid OCOTP controller Sanchayan Maity <maitysanchayan@gmail.com> - 2015-08-10 16:20 +0200
| From | Sanchayan Maity <maitysanchayan@gmail.com> |
|---|---|
| Date | 2015-08-10 16:20 +0200 |
| Subject | [PATCH v8 0/4] Implement OCOTP driver for Vybrid using NVMEM |
| Message-ID | <pVW5I-iZ-3@gated-at.bofh.it> |
Hello, This patchset is based on top of v9 of Srinivas's NVMEM framework patches. Tested using v9 NVMEM patches which got merged after applying them on shawn's tree for-next branch along with Stefan's NAND driver patchset. Sample output on Colibri VF50 root@colibri-vf:/sys/bus/nvmem/devices/ocotp0# hexdump nvmem 0000000 0000 0000 0000 0000 0000 0000 0000 0000 * 0000410 72a6 df64 0000 0000 0000 0000 0000 0000 0000420 11d4 2c14 0000 0000 0000 0000 0000 0000 0000430 0000 0000 0000 0000 0000 0000 0000 0000 * 0000450 0280 0000 0000 0000 0000 0000 0000 0000 0000460 0000 0000 0000 0000 0000 0000 0000 0000 * 0000880 8f01 0000 0000 0000 0000 0000 0000 0000 0000890 0000 0000 0000 0000 0000 0000 0000 0000 * 00008c0 0000 1000 0000 0000 0000 0000 0000 0000 00008d0 3202 0800 0000 0000 0000 0000 0000 0000 00008e0 0000 e100 0000 0000 0000 0000 0000 0000 00008f0 0000 0000 0000 0000 0000 0000 0000 0000 * 0000c80 bada bada 0000 0000 0000 0000 0000 0000 * 0000cc0 0000 0000 0000 0000 0000 0000 0000 0000 * 0000cf0 The driver has changed quite a bit from the first version relying on of_platform_populate in mach file, to using SoC driver under drivers/soc and finally to NVMEM. Feedback and comments most welcome. Version 7 patches can be found here https://lkml.org/lkml/2015/8/6/440 Version 6 RFC patches can be found here http://lkml.iu.edu/hypermail/linux/kernel/1506.2/05123.html Version 5 of the patchset can be found here http://lkml.iu.edu/hypermail/linux/kernel/1506.0/03787.html Version 4 of the patchset can be found here https://lkml.org/lkml/2015/5/26/199 Version 3 of the patchset can be found here http://www.spinics.net/lists/arm-kernel/msg420847.html Version 2 of the patchset can be found here http://www.spinics.net/lists/devicetree/msg80654.html Version 1 of the patchset can be found here http://www.spinics.net/lists/devicetree/msg80257.html The RFC version can be found here https://lkml.org/lkml/2015/5/11/13 Changes since v7: 1. Add COMPILE_TEST to Kconfig 2. Use GENMASK and BIT macros where applicable 3. Fix a code alignment issue 4. Get the max_register value for regmap config using resource_size() 5. Also add copyright info as the driver logic is based off on ocotp code in barebox 6. Add missing info related to clock in DT binding doc Changes since v6: 1. Use the v9 of NVMEM framework patchset 2. Add a few comments 3. Initialise buffer address not part of the fuse map to 0 instead of only handling buffer locations with valid fuse addresses. Changes since v5: Use NVMEM framework by Srinivas and Maxime Changes since v4: 1. Use devm_* family of functions and use a struct to get rid of global variables (suggested by Joachim Eastwood) 2. Make Kconfig govern the compilation with tristate, instead of earlier bool. Paul Bolle raised a valid point that perhaps this should have been built in with the bool, however I had not taken into consideration generic distro kernels and it makes sense to have this tristated. (comments from Paul Bolle and Andreas Farber) Changes since v3: Instead of using the syscon_regmap_lookup_by_compatible function use a phandle in the device tree along with offsets specified in this phandle node and then read the offset along with the device node in the driver for reading from the required region. Changes since v2: Implement the SoC bus code as a driver in drivers/soc by registering with fsl,mscm-cpucfg as per Arnd's feedback Changes since v1: Sort the headers in alphabetical order Changes since RFC: Use a DT entry for the ROM area while specifying it as syscon. Thanks & Regards, Sanchayan Maity. Sanchayan Maity (4): clk: clk-vf610: Add clock for Vybrid OCOTP controller ARM: dts: vfxxx: Add OCOTP node drivers: nvmem: Add Vybrid OCOTP support nvmem: Add DT binding documentation for Vybrid OCOTP driver .../devicetree/bindings/nvmem/vf610-ocotp.txt | 21 ++ arch/arm/boot/dts/vfxxx.dtsi | 9 + drivers/clk/imx/clk-vf610.c | 1 + drivers/nvmem/Kconfig | 10 + drivers/nvmem/Makefile | 2 + drivers/nvmem/vf610-ocotp.c | 301 +++++++++++++++++++++ include/dt-bindings/clock/vf610-clock.h | 3 +- 7 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/nvmem/vf610-ocotp.txt create mode 100644 drivers/nvmem/vf610-ocotp.c -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | Sanchayan Maity <maitysanchayan@gmail.com> |
|---|---|
| Date | 2015-08-10 16:20 +0200 |
| Subject | [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support |
| Message-ID | <pVW5I-iZ-17@gated-at.bofh.it> |
| In reply to | #1204312 |
The patch adds support for the On Chip One Time Programmable Peripheral
(OCOTP) on the Vybrid platform.
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
---
drivers/nvmem/Kconfig | 10 ++
drivers/nvmem/Makefile | 2 +
drivers/nvmem/vf610-ocotp.c | 301 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 313 insertions(+)
create mode 100644 drivers/nvmem/vf610-ocotp.c
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 0b33014..7af4c1d 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -47,4 +47,14 @@ config NVMEM_IMX_OCOTP
This driver can also be built as a module. If so, the module
will be called nvmem-imx-ocotp.
+config NVMEM_VF610_OCOTP
+ tristate "VF610_SoCs OCOTP support"
+ depends on SOC_VF610 || COMPILE_TEST
+ help
+ This is a driver for the 'OCOTP' peripheral available on Vybrid
+ devices like VF5xx and VF6xx.
+
+ This driver can also be built as a module. If so, the module will
+ be called nvmem-vf610-ocotp.
+
endif
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index b512d77..8a1eea8 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -12,3 +12,5 @@ obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
nvmem_sunxi_sid-y := sunxi_sid.o
obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
nvmem-imx-ocotp-y := imx-ocotp.o
+obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
+nvmem-vf610-ocotp-y := vf610-ocotp.o
diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c
new file mode 100644
index 0000000..9e18156
--- /dev/null
+++ b/drivers/nvmem/vf610-ocotp.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2015 Toradex AG.
+ *
+ * Author: Sanchayan Maity <sanchayan.maity@toradex.com>
+ *
+ * Based on the barebox ocotp driver,
+ * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>
+ * Orex Computed Radiography
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* OCOTP Register Offsets */
+#define OCOTP_CTRL_REG 0x00
+#define OCOTP_CTRL_SET 0x04
+#define OCOTP_CTRL_CLR 0x08
+#define OCOTP_TIMING 0x10
+#define OCOTP_DATA 0x20
+#define OCOTP_READ_CTRL_REG 0x30
+#define OCOTP_READ_FUSE_DATA 0x40
+
+/* OCOTP Register bits and masks */
+#define OCOTP_CTRL_WR_UNLOCK 16
+#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
+#define OCOTP_CTRL_WR_UNLOCK_MASK GENMASK(31, 16)
+#define OCOTP_CTRL_ADDR 0
+#define OCOTP_CTRL_ADDR_MASK GENMASK(6, 0)
+#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10)
+#define OCOTP_CTRL_ERROR BIT(9)
+#define OCOTP_CTRL_BUSY BIT(8)
+
+#define OCOTP_TIMING_STROBE_READ 16
+#define OCOTP_TIMING_STROBE_READ_MASK GENMASK(21, 16)
+#define OCOTP_TIMING_RELAX 12
+#define OCOTP_TIMING_RELAX_MASK GENMASK(15, 12)
+#define OCOTP_TIMING_STROBE_PROG 0
+#define OCOTP_TIMING_STROBE_PROG_MASK GENMASK(11, 0)
+
+#define OCOTP_READ_CTRL_READ_FUSE 0x1
+
+#define VF610_OCOTP_TIMEOUT 100000
+
+#define BF(value, field) (((value) << field) & field##_MASK)
+
+#define DEF_RELAX 20
+
+static const int base_to_fuse_addr_mappings[][2] = {
+ {0x400, 0x00},
+ {0x410, 0x01},
+ {0x420, 0x02},
+ {0x450, 0x05},
+ {0x4F0, 0x0F},
+ {0x600, 0x20},
+ {0x610, 0x21},
+ {0x620, 0x22},
+ {0x630, 0x23},
+ {0x640, 0x24},
+ {0x650, 0x25},
+ {0x660, 0x26},
+ {0x670, 0x27},
+ {0x6F0, 0x2F},
+ {0x880, 0x38},
+ {0x890, 0x39},
+ {0x8A0, 0x3A},
+ {0x8B0, 0x3B},
+ {0x8C0, 0x3C},
+ {0x8D0, 0x3D},
+ {0x8E0, 0x3E},
+ {0x8F0, 0x3F},
+ {0xC80, 0x78},
+ {0xC90, 0x79},
+ {0xCA0, 0x7A},
+ {0xCB0, 0x7B},
+ {0xCC0, 0x7C},
+ {0xCD0, 0x7D},
+ {0xCE0, 0x7E},
+ {0xCF0, 0x7F},
+};
+
+struct vf610_ocotp {
+ void __iomem *base;
+ struct clk *clk;
+ struct device *dev;
+ struct nvmem_device *nvmem;
+ int timing;
+};
+
+static int vf610_ocotp_wait_busy(void __iomem *base)
+{
+ int timeout = VF610_OCOTP_TIMEOUT;
+
+ while ((readl(base) & OCOTP_CTRL_BUSY) && --timeout)
+ udelay(10);
+
+ if (!timeout) {
+ writel(OCOTP_CTRL_ERROR, base + OCOTP_CTRL_CLR);
+ return -ETIMEDOUT;
+ }
+
+ udelay(10);
+
+ return 0;
+}
+
+static int vf610_ocotp_calculate_timing(struct vf610_ocotp *ocotp_dev)
+{
+ u32 clk_rate;
+ u32 relax, strobe_read, strobe_prog;
+ u32 timing;
+
+ clk_rate = clk_get_rate(ocotp_dev->clk);
+
+ /* Refer section OTP read/write timing parameters in TRM */
+ relax = clk_rate / (1000000000 / DEF_RELAX) - 1;
+ strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1;
+ strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1;
+
+ timing = BF(relax, OCOTP_TIMING_RELAX);
+ timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ);
+ timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG);
+
+ return timing;
+}
+
+static int vf610_get_fuse_address(int base_addr_offset)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(base_to_fuse_addr_mappings); i++) {
+ if (base_to_fuse_addr_mappings[i][0] == base_addr_offset)
+ return base_to_fuse_addr_mappings[i][1];
+ }
+
+ return -EINVAL;
+}
+
+static int vf610_ocotp_write(void *context, const void *data, size_t count)
+{
+ return 0;
+}
+
+static int vf610_ocotp_read(void *context,
+ const void *off, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct vf610_ocotp *ocotp = context;
+ unsigned int offset = *(u32 *)off;
+ u32 reg, *buf = val;
+ int fuse_addr;
+ int ret;
+
+ while (val_size > 0) {
+ fuse_addr = vf610_get_fuse_address(offset);
+ if (fuse_addr > 0) {
+ writel(ocotp->timing, ocotp->base + OCOTP_TIMING);
+ ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
+ if (ret)
+ return ret;
+
+ reg = readl(ocotp->base + OCOTP_CTRL_REG);
+ reg &= ~OCOTP_CTRL_ADDR_MASK;
+ reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
+ reg |= BF(fuse_addr, OCOTP_CTRL_ADDR);
+ writel(reg, ocotp->base + OCOTP_CTRL_REG);
+
+ writel(OCOTP_READ_CTRL_READ_FUSE,
+ ocotp->base + OCOTP_READ_CTRL_REG);
+ ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
+ if (ret)
+ return ret;
+
+ if (readl(ocotp->base) & OCOTP_CTRL_ERROR) {
+ dev_dbg(ocotp->dev, "Error reading from fuse address %x\n",
+ fuse_addr);
+ writel(OCOTP_CTRL_ERROR, ocotp->base + OCOTP_CTRL_CLR);
+ }
+
+ /*
+ * In case of error, we do not abort and expect to read
+ * 0xBADABADA as mentioned by the TRM. We just read this
+ * value and return.
+ */
+ *buf = readl(ocotp->base + OCOTP_READ_FUSE_DATA);
+ } else {
+ *buf = 0;
+ }
+
+ buf++;
+ val_size--;
+ offset += reg_size;
+ }
+
+ return 0;
+}
+
+static struct regmap_bus vf610_ocotp_bus = {
+ .read = vf610_ocotp_read,
+ .write = vf610_ocotp_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
+static struct regmap_config ocotp_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static struct nvmem_config ocotp_config = {
+ .name = "ocotp",
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id ocotp_of_match[] = {
+ { .compatible = "fsl,vf610-ocotp", },
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, ocotp_of_match);
+
+static int vf610_ocotp_remove(struct platform_device *pdev)
+{
+ struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev);
+
+ return nvmem_unregister(ocotp_dev->nvmem);
+}
+
+static int vf610_ocotp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct regmap *regmap;
+ struct vf610_ocotp *ocotp_dev;
+
+ ocotp_dev = devm_kzalloc(&pdev->dev,
+ sizeof(struct vf610_ocotp), GFP_KERNEL);
+ if (!ocotp_dev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ocotp_dev->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ocotp_dev->base))
+ return PTR_ERR(ocotp_dev->base);
+
+ ocotp_dev->clk = devm_clk_get(dev, "ocotp");
+ if (IS_ERR(ocotp_dev->clk)) {
+ dev_err(dev, "failed getting clock, err = %ld\n",
+ PTR_ERR(ocotp_dev->clk));
+ return PTR_ERR(ocotp_dev->clk);
+ }
+
+ ocotp_regmap_config.max_register = resource_size(res);
+ regmap = devm_regmap_init(dev,
+ &vf610_ocotp_bus, ocotp_dev, &ocotp_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(regmap);
+ }
+ ocotp_config.dev = dev;
+
+ ocotp_dev->nvmem = nvmem_register(&ocotp_config);
+ if (IS_ERR(ocotp_dev->nvmem))
+ return PTR_ERR(ocotp_dev->nvmem);
+
+ ocotp_dev->dev = dev;
+ platform_set_drvdata(pdev, ocotp_dev);
+
+ ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
+
+ return 0;
+}
+
+static struct platform_driver vf610_ocotp_driver = {
+ .probe = vf610_ocotp_probe,
+ .remove = vf610_ocotp_remove,
+ .driver = {
+ .name = "vf610-ocotp",
+ .of_match_table = ocotp_of_match,
+ },
+};
+module_platform_driver(vf610_ocotp_driver);
+MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
+MODULE_DESCRIPTION("Vybrid OCOTP driver");
+MODULE_LICENSE("GPL v2");
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> |
|---|---|
| Date | 2015-08-10 17:00 +0200 |
| Subject | Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support |
| Message-ID | <pVWIq-12B-3@gated-at.bofh.it> |
| In reply to | #1204313 |
Hi Sanchayan,
On 10/08/15 15:11, Sanchayan Maity wrote:
> The patch adds support for the On Chip One Time Programmable Peripheral
> (OCOTP) on the Vybrid platform.
>
> Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
> ---
> drivers/nvmem/Kconfig | 10 ++
> drivers/nvmem/Makefile | 2 +
> drivers/nvmem/vf610-ocotp.c | 301 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 313 insertions(+)
> create mode 100644 drivers/nvmem/vf610-ocotp.c
>
Patch looks good to me,
Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
--srini
> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
> index 0b33014..7af4c1d 100644
> --- a/drivers/nvmem/Kconfig
> +++ b/drivers/nvmem/Kconfig
> @@ -47,4 +47,14 @@ config NVMEM_IMX_OCOTP
> This driver can also be built as a module. If so, the module
> will be called nvmem-imx-ocotp.
>
> +config NVMEM_VF610_OCOTP
> + tristate "VF610_SoCs OCOTP support"
> + depends on SOC_VF610 || COMPILE_TEST
> + help
> + This is a driver for the 'OCOTP' peripheral available on Vybrid
> + devices like VF5xx and VF6xx.
> +
> + This driver can also be built as a module. If so, the module will
> + be called nvmem-vf610-ocotp.
> +
> endif
> diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
> index b512d77..8a1eea8 100644
> --- a/drivers/nvmem/Makefile
> +++ b/drivers/nvmem/Makefile
> @@ -12,3 +12,5 @@ obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
> nvmem_sunxi_sid-y := sunxi_sid.o
> obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
> nvmem-imx-ocotp-y := imx-ocotp.o
> +obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
> +nvmem-vf610-ocotp-y := vf610-ocotp.o
> diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c
> new file mode 100644
> index 0000000..9e18156
> --- /dev/null
> +++ b/drivers/nvmem/vf610-ocotp.c
> @@ -0,0 +1,301 @@
> +/*
> + * Copyright (C) 2015 Toradex AG.
> + *
> + * Author: Sanchayan Maity <sanchayan.maity@toradex.com>
> + *
> + * Based on the barebox ocotp driver,
> + * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>
> + * Orex Computed Radiography
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +/* OCOTP Register Offsets */
> +#define OCOTP_CTRL_REG 0x00
> +#define OCOTP_CTRL_SET 0x04
> +#define OCOTP_CTRL_CLR 0x08
> +#define OCOTP_TIMING 0x10
> +#define OCOTP_DATA 0x20
> +#define OCOTP_READ_CTRL_REG 0x30
> +#define OCOTP_READ_FUSE_DATA 0x40
> +
> +/* OCOTP Register bits and masks */
> +#define OCOTP_CTRL_WR_UNLOCK 16
> +#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
> +#define OCOTP_CTRL_WR_UNLOCK_MASK GENMASK(31, 16)
> +#define OCOTP_CTRL_ADDR 0
> +#define OCOTP_CTRL_ADDR_MASK GENMASK(6, 0)
> +#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10)
> +#define OCOTP_CTRL_ERROR BIT(9)
> +#define OCOTP_CTRL_BUSY BIT(8)
> +
> +#define OCOTP_TIMING_STROBE_READ 16
> +#define OCOTP_TIMING_STROBE_READ_MASK GENMASK(21, 16)
> +#define OCOTP_TIMING_RELAX 12
> +#define OCOTP_TIMING_RELAX_MASK GENMASK(15, 12)
> +#define OCOTP_TIMING_STROBE_PROG 0
> +#define OCOTP_TIMING_STROBE_PROG_MASK GENMASK(11, 0)
> +
> +#define OCOTP_READ_CTRL_READ_FUSE 0x1
> +
> +#define VF610_OCOTP_TIMEOUT 100000
> +
> +#define BF(value, field) (((value) << field) & field##_MASK)
> +
> +#define DEF_RELAX 20
> +
> +static const int base_to_fuse_addr_mappings[][2] = {
> + {0x400, 0x00},
> + {0x410, 0x01},
> + {0x420, 0x02},
> + {0x450, 0x05},
> + {0x4F0, 0x0F},
> + {0x600, 0x20},
> + {0x610, 0x21},
> + {0x620, 0x22},
> + {0x630, 0x23},
> + {0x640, 0x24},
> + {0x650, 0x25},
> + {0x660, 0x26},
> + {0x670, 0x27},
> + {0x6F0, 0x2F},
> + {0x880, 0x38},
> + {0x890, 0x39},
> + {0x8A0, 0x3A},
> + {0x8B0, 0x3B},
> + {0x8C0, 0x3C},
> + {0x8D0, 0x3D},
> + {0x8E0, 0x3E},
> + {0x8F0, 0x3F},
> + {0xC80, 0x78},
> + {0xC90, 0x79},
> + {0xCA0, 0x7A},
> + {0xCB0, 0x7B},
> + {0xCC0, 0x7C},
> + {0xCD0, 0x7D},
> + {0xCE0, 0x7E},
> + {0xCF0, 0x7F},
> +};
> +
> +struct vf610_ocotp {
> + void __iomem *base;
> + struct clk *clk;
> + struct device *dev;
> + struct nvmem_device *nvmem;
> + int timing;
> +};
> +
> +static int vf610_ocotp_wait_busy(void __iomem *base)
> +{
> + int timeout = VF610_OCOTP_TIMEOUT;
> +
> + while ((readl(base) & OCOTP_CTRL_BUSY) && --timeout)
> + udelay(10);
> +
> + if (!timeout) {
> + writel(OCOTP_CTRL_ERROR, base + OCOTP_CTRL_CLR);
> + return -ETIMEDOUT;
> + }
> +
> + udelay(10);
> +
> + return 0;
> +}
> +
> +static int vf610_ocotp_calculate_timing(struct vf610_ocotp *ocotp_dev)
> +{
> + u32 clk_rate;
> + u32 relax, strobe_read, strobe_prog;
> + u32 timing;
> +
> + clk_rate = clk_get_rate(ocotp_dev->clk);
> +
> + /* Refer section OTP read/write timing parameters in TRM */
> + relax = clk_rate / (1000000000 / DEF_RELAX) - 1;
> + strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1;
> + strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1;
> +
> + timing = BF(relax, OCOTP_TIMING_RELAX);
> + timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ);
> + timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG);
> +
> + return timing;
> +}
> +
> +static int vf610_get_fuse_address(int base_addr_offset)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(base_to_fuse_addr_mappings); i++) {
> + if (base_to_fuse_addr_mappings[i][0] == base_addr_offset)
> + return base_to_fuse_addr_mappings[i][1];
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int vf610_ocotp_write(void *context, const void *data, size_t count)
> +{
> + return 0;
> +}
> +
> +static int vf610_ocotp_read(void *context,
> + const void *off, size_t reg_size,
> + void *val, size_t val_size)
> +{
> + struct vf610_ocotp *ocotp = context;
> + unsigned int offset = *(u32 *)off;
> + u32 reg, *buf = val;
> + int fuse_addr;
> + int ret;
> +
> + while (val_size > 0) {
> + fuse_addr = vf610_get_fuse_address(offset);
> + if (fuse_addr > 0) {
> + writel(ocotp->timing, ocotp->base + OCOTP_TIMING);
> + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
> + if (ret)
> + return ret;
> +
> + reg = readl(ocotp->base + OCOTP_CTRL_REG);
> + reg &= ~OCOTP_CTRL_ADDR_MASK;
> + reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
> + reg |= BF(fuse_addr, OCOTP_CTRL_ADDR);
> + writel(reg, ocotp->base + OCOTP_CTRL_REG);
> +
> + writel(OCOTP_READ_CTRL_READ_FUSE,
> + ocotp->base + OCOTP_READ_CTRL_REG);
> + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
> + if (ret)
> + return ret;
> +
> + if (readl(ocotp->base) & OCOTP_CTRL_ERROR) {
> + dev_dbg(ocotp->dev, "Error reading from fuse address %x\n",
> + fuse_addr);
> + writel(OCOTP_CTRL_ERROR, ocotp->base + OCOTP_CTRL_CLR);
> + }
> +
> + /*
> + * In case of error, we do not abort and expect to read
> + * 0xBADABADA as mentioned by the TRM. We just read this
> + * value and return.
> + */
> + *buf = readl(ocotp->base + OCOTP_READ_FUSE_DATA);
> + } else {
> + *buf = 0;
> + }
> +
> + buf++;
> + val_size--;
> + offset += reg_size;
> + }
> +
> + return 0;
> +}
> +
> +static struct regmap_bus vf610_ocotp_bus = {
> + .read = vf610_ocotp_read,
> + .write = vf610_ocotp_write,
> + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
> + .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
> +};
> +
> +static struct regmap_config ocotp_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> +};
> +
> +static struct nvmem_config ocotp_config = {
> + .name = "ocotp",
> + .owner = THIS_MODULE,
> +};
> +
> +static const struct of_device_id ocotp_of_match[] = {
> + { .compatible = "fsl,vf610-ocotp", },
> + {/* sentinel */},
> +};
> +MODULE_DEVICE_TABLE(of, ocotp_of_match);
> +
> +static int vf610_ocotp_remove(struct platform_device *pdev)
> +{
> + struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev);
> +
> + return nvmem_unregister(ocotp_dev->nvmem);
> +}
> +
> +static int vf610_ocotp_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + struct regmap *regmap;
> + struct vf610_ocotp *ocotp_dev;
> +
> + ocotp_dev = devm_kzalloc(&pdev->dev,
> + sizeof(struct vf610_ocotp), GFP_KERNEL);
> + if (!ocotp_dev)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ocotp_dev->base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ocotp_dev->base))
> + return PTR_ERR(ocotp_dev->base);
> +
> + ocotp_dev->clk = devm_clk_get(dev, "ocotp");
> + if (IS_ERR(ocotp_dev->clk)) {
> + dev_err(dev, "failed getting clock, err = %ld\n",
> + PTR_ERR(ocotp_dev->clk));
> + return PTR_ERR(ocotp_dev->clk);
> + }
> +
> + ocotp_regmap_config.max_register = resource_size(res);
> + regmap = devm_regmap_init(dev,
> + &vf610_ocotp_bus, ocotp_dev, &ocotp_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(dev, "regmap init failed\n");
> + return PTR_ERR(regmap);
> + }
> + ocotp_config.dev = dev;
> +
> + ocotp_dev->nvmem = nvmem_register(&ocotp_config);
> + if (IS_ERR(ocotp_dev->nvmem))
> + return PTR_ERR(ocotp_dev->nvmem);
> +
> + ocotp_dev->dev = dev;
> + platform_set_drvdata(pdev, ocotp_dev);
> +
> + ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver vf610_ocotp_driver = {
> + .probe = vf610_ocotp_probe,
> + .remove = vf610_ocotp_remove,
> + .driver = {
> + .name = "vf610-ocotp",
> + .of_match_table = ocotp_of_match,
> + },
> +};
> +module_platform_driver(vf610_ocotp_driver);
> +MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
> +MODULE_DESCRIPTION("Vybrid OCOTP driver");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> |
|---|---|
| Date | 2015-08-12 12:50 +0200 |
| Subject | Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support |
| Message-ID | <pWBLA-1Xg-15@gated-at.bofh.it> |
| In reply to | #1204313 |
Hi Sanchayan,
Please run checkpatch before you send the patch next time.
Look at Documentation/SubmittingPatches for more details.
WARNING: line over 80 characters
#225: FILE: drivers/nvmem/vf610-ocotp.c:174:
+ ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
WARNING: line over 80 characters
#237: FILE: drivers/nvmem/vf610-ocotp.c:186:
+ ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
WARNING: line over 80 characters
#244: FILE: drivers/nvmem/vf610-ocotp.c:193:
+ writel(OCOTP_CTRL_ERROR, ocotp->base + OCOTP_CTRL_CLR);
On 10/08/15 15:11, Sanchayan Maity wrote:
> The patch adds support for the On Chip One Time Programmable Peripheral
> (OCOTP) on the Vybrid platform.
>
> Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
> ---
> drivers/nvmem/Kconfig | 10 ++
> drivers/nvmem/Makefile | 2 +
> drivers/nvmem/vf610-ocotp.c | 301 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 313 insertions(+)
> create mode 100644 drivers/nvmem/vf610-ocotp.c
>
> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
> index 0b33014..7af4c1d 100644
> --- a/drivers/nvmem/Kconfig
> +++ b/drivers/nvmem/Kconfig
> @@ -47,4 +47,14 @@ config NVMEM_IMX_OCOTP
> This driver can also be built as a module. If so, the module
> will be called nvmem-imx-ocotp.
>
Also please can you rebase it on top of char-misc-next?, This patch
would not apply as it is because of the above context.
> +config NVMEM_VF610_OCOTP
> + tristate "VF610_SoCs OCOTP support"
> + depends on SOC_VF610 || COMPILE_TEST
> + help
> + This is a driver for the 'OCOTP' peripheral available on Vybrid
> + devices like VF5xx and VF6xx.
> +
> + This driver can also be built as a module. If so, the module will
> + be called nvmem-vf610-ocotp.
> +
> endif
> diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
> index b512d77..8a1eea8 100644
> --- a/drivers/nvmem/Makefile
> +++ b/drivers/nvmem/Makefile
> @@ -12,3 +12,5 @@ obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
> nvmem_sunxi_sid-y := sunxi_sid.o
> obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
> nvmem-imx-ocotp-y := imx-ocotp.o
> +obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
> +nvmem-vf610-ocotp-y := vf610-ocotp.o
> diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c
> new file mode 100644
> index 0000000..9e18156
> --- /dev/null
> +++ b/drivers/nvmem/vf610-ocotp.c
> @@ -0,0 +1,301 @@
> +/*
> + * Copyright (C) 2015 Toradex AG.
> + *
> + * Author: Sanchayan Maity <sanchayan.maity@toradex.com>
> + *
> + * Based on the barebox ocotp driver,
> + * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>
> + * Orex Computed Radiography
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +/* OCOTP Register Offsets */
> +#define OCOTP_CTRL_REG 0x00
> +#define OCOTP_CTRL_SET 0x04
> +#define OCOTP_CTRL_CLR 0x08
> +#define OCOTP_TIMING 0x10
> +#define OCOTP_DATA 0x20
> +#define OCOTP_READ_CTRL_REG 0x30
> +#define OCOTP_READ_FUSE_DATA 0x40
> +
> +/* OCOTP Register bits and masks */
> +#define OCOTP_CTRL_WR_UNLOCK 16
> +#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
> +#define OCOTP_CTRL_WR_UNLOCK_MASK GENMASK(31, 16)
> +#define OCOTP_CTRL_ADDR 0
> +#define OCOTP_CTRL_ADDR_MASK GENMASK(6, 0)
> +#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10)
> +#define OCOTP_CTRL_ERROR BIT(9)
> +#define OCOTP_CTRL_BUSY BIT(8)
> +
> +#define OCOTP_TIMING_STROBE_READ 16
> +#define OCOTP_TIMING_STROBE_READ_MASK GENMASK(21, 16)
> +#define OCOTP_TIMING_RELAX 12
> +#define OCOTP_TIMING_RELAX_MASK GENMASK(15, 12)
> +#define OCOTP_TIMING_STROBE_PROG 0
> +#define OCOTP_TIMING_STROBE_PROG_MASK GENMASK(11, 0)
> +
> +#define OCOTP_READ_CTRL_READ_FUSE 0x1
> +
> +#define VF610_OCOTP_TIMEOUT 100000
> +
> +#define BF(value, field) (((value) << field) & field##_MASK)
> +
> +#define DEF_RELAX 20
> +
> +static const int base_to_fuse_addr_mappings[][2] = {
> + {0x400, 0x00},
> + {0x410, 0x01},
> + {0x420, 0x02},
> + {0x450, 0x05},
> + {0x4F0, 0x0F},
> + {0x600, 0x20},
> + {0x610, 0x21},
> + {0x620, 0x22},
> + {0x630, 0x23},
> + {0x640, 0x24},
> + {0x650, 0x25},
> + {0x660, 0x26},
> + {0x670, 0x27},
> + {0x6F0, 0x2F},
> + {0x880, 0x38},
> + {0x890, 0x39},
> + {0x8A0, 0x3A},
> + {0x8B0, 0x3B},
> + {0x8C0, 0x3C},
> + {0x8D0, 0x3D},
> + {0x8E0, 0x3E},
> + {0x8F0, 0x3F},
> + {0xC80, 0x78},
> + {0xC90, 0x79},
> + {0xCA0, 0x7A},
> + {0xCB0, 0x7B},
> + {0xCC0, 0x7C},
> + {0xCD0, 0x7D},
> + {0xCE0, 0x7E},
> + {0xCF0, 0x7F},
> +};
> +
> +struct vf610_ocotp {
> + void __iomem *base;
> + struct clk *clk;
> + struct device *dev;
> + struct nvmem_device *nvmem;
> + int timing;
> +};
> +
> +static int vf610_ocotp_wait_busy(void __iomem *base)
> +{
> + int timeout = VF610_OCOTP_TIMEOUT;
> +
> + while ((readl(base) & OCOTP_CTRL_BUSY) && --timeout)
> + udelay(10);
> +
> + if (!timeout) {
> + writel(OCOTP_CTRL_ERROR, base + OCOTP_CTRL_CLR);
> + return -ETIMEDOUT;
> + }
> +
> + udelay(10);
> +
> + return 0;
> +}
> +
> +static int vf610_ocotp_calculate_timing(struct vf610_ocotp *ocotp_dev)
> +{
> + u32 clk_rate;
> + u32 relax, strobe_read, strobe_prog;
> + u32 timing;
> +
> + clk_rate = clk_get_rate(ocotp_dev->clk);
> +
> + /* Refer section OTP read/write timing parameters in TRM */
> + relax = clk_rate / (1000000000 / DEF_RELAX) - 1;
> + strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1;
> + strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1;
> +
> + timing = BF(relax, OCOTP_TIMING_RELAX);
> + timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ);
> + timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG);
> +
> + return timing;
> +}
> +
> +static int vf610_get_fuse_address(int base_addr_offset)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(base_to_fuse_addr_mappings); i++) {
> + if (base_to_fuse_addr_mappings[i][0] == base_addr_offset)
> + return base_to_fuse_addr_mappings[i][1];
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int vf610_ocotp_write(void *context, const void *data, size_t count)
> +{
> + return 0;
> +}
> +
> +static int vf610_ocotp_read(void *context,
> + const void *off, size_t reg_size,
> + void *val, size_t val_size)
> +{
> + struct vf610_ocotp *ocotp = context;
> + unsigned int offset = *(u32 *)off;
> + u32 reg, *buf = val;
> + int fuse_addr;
> + int ret;
> +
> + while (val_size > 0) {
> + fuse_addr = vf610_get_fuse_address(offset);
> + if (fuse_addr > 0) {
> + writel(ocotp->timing, ocotp->base + OCOTP_TIMING);
> + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
> + if (ret)
> + return ret;
> +
> + reg = readl(ocotp->base + OCOTP_CTRL_REG);
> + reg &= ~OCOTP_CTRL_ADDR_MASK;
> + reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
> + reg |= BF(fuse_addr, OCOTP_CTRL_ADDR);
> + writel(reg, ocotp->base + OCOTP_CTRL_REG);
> +
> + writel(OCOTP_READ_CTRL_READ_FUSE,
> + ocotp->base + OCOTP_READ_CTRL_REG);
> + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
> + if (ret)
> + return ret;
> +
> + if (readl(ocotp->base) & OCOTP_CTRL_ERROR) {
> + dev_dbg(ocotp->dev, "Error reading from fuse address %x\n",
> + fuse_addr);
> + writel(OCOTP_CTRL_ERROR, ocotp->base + OCOTP_CTRL_CLR);
> + }
> +
> + /*
> + * In case of error, we do not abort and expect to read
> + * 0xBADABADA as mentioned by the TRM. We just read this
> + * value and return.
> + */
> + *buf = readl(ocotp->base + OCOTP_READ_FUSE_DATA);
> + } else {
> + *buf = 0;
> + }
> +
> + buf++;
> + val_size--;
> + offset += reg_size;
> + }
> +
> + return 0;
> +}
> +
> +static struct regmap_bus vf610_ocotp_bus = {
> + .read = vf610_ocotp_read,
> + .write = vf610_ocotp_write,
> + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
> + .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
> +};
> +
> +static struct regmap_config ocotp_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> +};
> +
> +static struct nvmem_config ocotp_config = {
> + .name = "ocotp",
> + .owner = THIS_MODULE,
> +};
> +
> +static const struct of_device_id ocotp_of_match[] = {
> + { .compatible = "fsl,vf610-ocotp", },
> + {/* sentinel */},
> +};
> +MODULE_DEVICE_TABLE(of, ocotp_of_match);
> +
> +static int vf610_ocotp_remove(struct platform_device *pdev)
> +{
> + struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev);
> +
> + return nvmem_unregister(ocotp_dev->nvmem);
> +}
> +
> +static int vf610_ocotp_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + struct regmap *regmap;
> + struct vf610_ocotp *ocotp_dev;
> +
> + ocotp_dev = devm_kzalloc(&pdev->dev,
> + sizeof(struct vf610_ocotp), GFP_KERNEL);
> + if (!ocotp_dev)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ocotp_dev->base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ocotp_dev->base))
> + return PTR_ERR(ocotp_dev->base);
> +
> + ocotp_dev->clk = devm_clk_get(dev, "ocotp");
> + if (IS_ERR(ocotp_dev->clk)) {
> + dev_err(dev, "failed getting clock, err = %ld\n",
> + PTR_ERR(ocotp_dev->clk));
> + return PTR_ERR(ocotp_dev->clk);
> + }
> +
> + ocotp_regmap_config.max_register = resource_size(res);
> + regmap = devm_regmap_init(dev,
> + &vf610_ocotp_bus, ocotp_dev, &ocotp_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(dev, "regmap init failed\n");
> + return PTR_ERR(regmap);
> + }
> + ocotp_config.dev = dev;
> +
> + ocotp_dev->nvmem = nvmem_register(&ocotp_config);
> + if (IS_ERR(ocotp_dev->nvmem))
> + return PTR_ERR(ocotp_dev->nvmem);
> +
> + ocotp_dev->dev = dev;
> + platform_set_drvdata(pdev, ocotp_dev);
> +
> + ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver vf610_ocotp_driver = {
> + .probe = vf610_ocotp_probe,
> + .remove = vf610_ocotp_remove,
> + .driver = {
> + .name = "vf610-ocotp",
> + .of_match_table = ocotp_of_match,
> + },
> +};
> +module_platform_driver(vf610_ocotp_driver);
> +MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
> +MODULE_DESCRIPTION("Vybrid OCOTP driver");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | maitysanchayan@gmail.com |
|---|---|
| Date | 2015-08-12 13:40 +0200 |
| Subject | Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support |
| Message-ID | <pWCxY-36M-5@gated-at.bofh.it> |
| In reply to | #1205937 |
Hello,
On 15-08-12 11:41:55, Srinivas Kandagatla wrote:
> Hi Sanchayan,
>
> Please run checkpatch before you send the patch next time.
> Look at Documentation/SubmittingPatches for more details.
>
> WARNING: line over 80 characters
> #225: FILE: drivers/nvmem/vf610-ocotp.c:174:
> + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
>
> WARNING: line over 80 characters
> #237: FILE: drivers/nvmem/vf610-ocotp.c:186:
> + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
>
> WARNING: line over 80 characters
> #244: FILE: drivers/nvmem/vf610-ocotp.c:193:
> + writel(OCOTP_CTRL_ERROR, ocotp->base + OCOTP_CTRL_CLR);
>
I had. However splitting these lines seemed odd. Will split and fix.
>
> On 10/08/15 15:11, Sanchayan Maity wrote:
> > The patch adds support for the On Chip One Time Programmable Peripheral
> > (OCOTP) on the Vybrid platform.
> >
> > Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
> > ---
> > drivers/nvmem/Kconfig | 10 ++
> > drivers/nvmem/Makefile | 2 +
> > drivers/nvmem/vf610-ocotp.c | 301 ++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 313 insertions(+)
> > create mode 100644 drivers/nvmem/vf610-ocotp.c
> >
> > diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
> > index 0b33014..7af4c1d 100644
> > --- a/drivers/nvmem/Kconfig
> > +++ b/drivers/nvmem/Kconfig
> > @@ -47,4 +47,14 @@ config NVMEM_IMX_OCOTP
> > This driver can also be built as a module. If so, the module
> > will be called nvmem-imx-ocotp.
> >
>
> Also please can you rebase it on top of char-misc-next?, This patch
> would not apply as it is because of the above context.
Sure. Will rebase and send.
Thanks.
Regards,
Sanchayan.
>
>
> > +config NVMEM_VF610_OCOTP
> > + tristate "VF610_SoCs OCOTP support"
> > + depends on SOC_VF610 || COMPILE_TEST
> > + help
> > + This is a driver for the 'OCOTP' peripheral available on Vybrid
> > + devices like VF5xx and VF6xx.
> > +
> > + This driver can also be built as a module. If so, the module will
> > + be called nvmem-vf610-ocotp.
> > +
> > endif
> > diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
> > index b512d77..8a1eea8 100644
> > --- a/drivers/nvmem/Makefile
> > +++ b/drivers/nvmem/Makefile
> > @@ -12,3 +12,5 @@ obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
> > nvmem_sunxi_sid-y := sunxi_sid.o
> > obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
> > nvmem-imx-ocotp-y := imx-ocotp.o
> > +obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
> > +nvmem-vf610-ocotp-y := vf610-ocotp.o
> > diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c
> > new file mode 100644
> > index 0000000..9e18156
> > --- /dev/null
> > +++ b/drivers/nvmem/vf610-ocotp.c
> > @@ -0,0 +1,301 @@
> > +/*
> > + * Copyright (C) 2015 Toradex AG.
> > + *
> > + * Author: Sanchayan Maity <sanchayan.maity@toradex.com>
> > + *
> > + * Based on the barebox ocotp driver,
> > + * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>
> > + * Orex Computed Radiography
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 and
> > + * only version 2 as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/nvmem-provider.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/slab.h>
> > +
> > +/* OCOTP Register Offsets */
> > +#define OCOTP_CTRL_REG 0x00
> > +#define OCOTP_CTRL_SET 0x04
> > +#define OCOTP_CTRL_CLR 0x08
> > +#define OCOTP_TIMING 0x10
> > +#define OCOTP_DATA 0x20
> > +#define OCOTP_READ_CTRL_REG 0x30
> > +#define OCOTP_READ_FUSE_DATA 0x40
> > +
> > +/* OCOTP Register bits and masks */
> > +#define OCOTP_CTRL_WR_UNLOCK 16
> > +#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
> > +#define OCOTP_CTRL_WR_UNLOCK_MASK GENMASK(31, 16)
> > +#define OCOTP_CTRL_ADDR 0
> > +#define OCOTP_CTRL_ADDR_MASK GENMASK(6, 0)
> > +#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10)
> > +#define OCOTP_CTRL_ERROR BIT(9)
> > +#define OCOTP_CTRL_BUSY BIT(8)
> > +
> > +#define OCOTP_TIMING_STROBE_READ 16
> > +#define OCOTP_TIMING_STROBE_READ_MASK GENMASK(21, 16)
> > +#define OCOTP_TIMING_RELAX 12
> > +#define OCOTP_TIMING_RELAX_MASK GENMASK(15, 12)
> > +#define OCOTP_TIMING_STROBE_PROG 0
> > +#define OCOTP_TIMING_STROBE_PROG_MASK GENMASK(11, 0)
> > +
> > +#define OCOTP_READ_CTRL_READ_FUSE 0x1
> > +
> > +#define VF610_OCOTP_TIMEOUT 100000
> > +
> > +#define BF(value, field) (((value) << field) & field##_MASK)
> > +
> > +#define DEF_RELAX 20
> > +
> > +static const int base_to_fuse_addr_mappings[][2] = {
> > + {0x400, 0x00},
> > + {0x410, 0x01},
> > + {0x420, 0x02},
> > + {0x450, 0x05},
> > + {0x4F0, 0x0F},
> > + {0x600, 0x20},
> > + {0x610, 0x21},
> > + {0x620, 0x22},
> > + {0x630, 0x23},
> > + {0x640, 0x24},
> > + {0x650, 0x25},
> > + {0x660, 0x26},
> > + {0x670, 0x27},
> > + {0x6F0, 0x2F},
> > + {0x880, 0x38},
> > + {0x890, 0x39},
> > + {0x8A0, 0x3A},
> > + {0x8B0, 0x3B},
> > + {0x8C0, 0x3C},
> > + {0x8D0, 0x3D},
> > + {0x8E0, 0x3E},
> > + {0x8F0, 0x3F},
> > + {0xC80, 0x78},
> > + {0xC90, 0x79},
> > + {0xCA0, 0x7A},
> > + {0xCB0, 0x7B},
> > + {0xCC0, 0x7C},
> > + {0xCD0, 0x7D},
> > + {0xCE0, 0x7E},
> > + {0xCF0, 0x7F},
> > +};
> > +
> > +struct vf610_ocotp {
> > + void __iomem *base;
> > + struct clk *clk;
> > + struct device *dev;
> > + struct nvmem_device *nvmem;
> > + int timing;
> > +};
> > +
> > +static int vf610_ocotp_wait_busy(void __iomem *base)
> > +{
> > + int timeout = VF610_OCOTP_TIMEOUT;
> > +
> > + while ((readl(base) & OCOTP_CTRL_BUSY) && --timeout)
> > + udelay(10);
> > +
> > + if (!timeout) {
> > + writel(OCOTP_CTRL_ERROR, base + OCOTP_CTRL_CLR);
> > + return -ETIMEDOUT;
> > + }
> > +
> > + udelay(10);
> > +
> > + return 0;
> > +}
> > +
> > +static int vf610_ocotp_calculate_timing(struct vf610_ocotp *ocotp_dev)
> > +{
> > + u32 clk_rate;
> > + u32 relax, strobe_read, strobe_prog;
> > + u32 timing;
> > +
> > + clk_rate = clk_get_rate(ocotp_dev->clk);
> > +
> > + /* Refer section OTP read/write timing parameters in TRM */
> > + relax = clk_rate / (1000000000 / DEF_RELAX) - 1;
> > + strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1;
> > + strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1;
> > +
> > + timing = BF(relax, OCOTP_TIMING_RELAX);
> > + timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ);
> > + timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG);
> > +
> > + return timing;
> > +}
> > +
> > +static int vf610_get_fuse_address(int base_addr_offset)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(base_to_fuse_addr_mappings); i++) {
> > + if (base_to_fuse_addr_mappings[i][0] == base_addr_offset)
> > + return base_to_fuse_addr_mappings[i][1];
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int vf610_ocotp_write(void *context, const void *data, size_t count)
> > +{
> > + return 0;
> > +}
> > +
> > +static int vf610_ocotp_read(void *context,
> > + const void *off, size_t reg_size,
> > + void *val, size_t val_size)
> > +{
> > + struct vf610_ocotp *ocotp = context;
> > + unsigned int offset = *(u32 *)off;
> > + u32 reg, *buf = val;
> > + int fuse_addr;
> > + int ret;
> > +
> > + while (val_size > 0) {
> > + fuse_addr = vf610_get_fuse_address(offset);
> > + if (fuse_addr > 0) {
> > + writel(ocotp->timing, ocotp->base + OCOTP_TIMING);
> > + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
> > + if (ret)
> > + return ret;
> > +
> > + reg = readl(ocotp->base + OCOTP_CTRL_REG);
> > + reg &= ~OCOTP_CTRL_ADDR_MASK;
> > + reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
> > + reg |= BF(fuse_addr, OCOTP_CTRL_ADDR);
> > + writel(reg, ocotp->base + OCOTP_CTRL_REG);
> > +
> > + writel(OCOTP_READ_CTRL_READ_FUSE,
> > + ocotp->base + OCOTP_READ_CTRL_REG);
> > + ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG);
> > + if (ret)
> > + return ret;
> > +
> > + if (readl(ocotp->base) & OCOTP_CTRL_ERROR) {
> > + dev_dbg(ocotp->dev, "Error reading from fuse address %x\n",
> > + fuse_addr);
> > + writel(OCOTP_CTRL_ERROR, ocotp->base + OCOTP_CTRL_CLR);
> > + }
> > +
> > + /*
> > + * In case of error, we do not abort and expect to read
> > + * 0xBADABADA as mentioned by the TRM. We just read this
> > + * value and return.
> > + */
> > + *buf = readl(ocotp->base + OCOTP_READ_FUSE_DATA);
> > + } else {
> > + *buf = 0;
> > + }
> > +
> > + buf++;
> > + val_size--;
> > + offset += reg_size;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static struct regmap_bus vf610_ocotp_bus = {
> > + .read = vf610_ocotp_read,
> > + .write = vf610_ocotp_write,
> > + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
> > + .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
> > +};
> > +
> > +static struct regmap_config ocotp_regmap_config = {
> > + .reg_bits = 32,
> > + .val_bits = 32,
> > + .reg_stride = 4,
> > +};
> > +
> > +static struct nvmem_config ocotp_config = {
> > + .name = "ocotp",
> > + .owner = THIS_MODULE,
> > +};
> > +
> > +static const struct of_device_id ocotp_of_match[] = {
> > + { .compatible = "fsl,vf610-ocotp", },
> > + {/* sentinel */},
> > +};
> > +MODULE_DEVICE_TABLE(of, ocotp_of_match);
> > +
> > +static int vf610_ocotp_remove(struct platform_device *pdev)
> > +{
> > + struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev);
> > +
> > + return nvmem_unregister(ocotp_dev->nvmem);
> > +}
> > +
> > +static int vf610_ocotp_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct resource *res;
> > + struct regmap *regmap;
> > + struct vf610_ocotp *ocotp_dev;
> > +
> > + ocotp_dev = devm_kzalloc(&pdev->dev,
> > + sizeof(struct vf610_ocotp), GFP_KERNEL);
> > + if (!ocotp_dev)
> > + return -ENOMEM;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + ocotp_dev->base = devm_ioremap_resource(dev, res);
> > + if (IS_ERR(ocotp_dev->base))
> > + return PTR_ERR(ocotp_dev->base);
> > +
> > + ocotp_dev->clk = devm_clk_get(dev, "ocotp");
> > + if (IS_ERR(ocotp_dev->clk)) {
> > + dev_err(dev, "failed getting clock, err = %ld\n",
> > + PTR_ERR(ocotp_dev->clk));
> > + return PTR_ERR(ocotp_dev->clk);
> > + }
> > +
> > + ocotp_regmap_config.max_register = resource_size(res);
> > + regmap = devm_regmap_init(dev,
> > + &vf610_ocotp_bus, ocotp_dev, &ocotp_regmap_config);
> > + if (IS_ERR(regmap)) {
> > + dev_err(dev, "regmap init failed\n");
> > + return PTR_ERR(regmap);
> > + }
> > + ocotp_config.dev = dev;
> > +
> > + ocotp_dev->nvmem = nvmem_register(&ocotp_config);
> > + if (IS_ERR(ocotp_dev->nvmem))
> > + return PTR_ERR(ocotp_dev->nvmem);
> > +
> > + ocotp_dev->dev = dev;
> > + platform_set_drvdata(pdev, ocotp_dev);
> > +
> > + ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver vf610_ocotp_driver = {
> > + .probe = vf610_ocotp_probe,
> > + .remove = vf610_ocotp_remove,
> > + .driver = {
> > + .name = "vf610-ocotp",
> > + .of_match_table = ocotp_of_match,
> > + },
> > +};
> > +module_platform_driver(vf610_ocotp_driver);
> > +MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
> > +MODULE_DESCRIPTION("Vybrid OCOTP driver");
> > +MODULE_LICENSE("GPL v2");
> >
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> |
|---|---|
| Date | 2015-08-12 13:50 +0200 |
| Subject | Re: [PATCH v8 3/4] drivers: nvmem: Add Vybrid OCOTP support |
| Message-ID | <pWCHE-3ic-7@gated-at.bofh.it> |
| In reply to | #1205977 |
On 12/08/15 12:32, maitysanchayan@gmail.com wrote: > Hello, > > On 15-08-12 11:41:55, Srinivas Kandagatla wrote: >> >Hi Sanchayan, >> > >> >Please run checkpatch before you send the patch next time. >> >Look at Documentation/SubmittingPatches for more details. >> > >> >WARNING: line over 80 characters >> >#225: FILE: drivers/nvmem/vf610-ocotp.c:174: >> >+ ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG); >> > >> >WARNING: line over 80 characters >> >#237: FILE: drivers/nvmem/vf610-ocotp.c:186: >> >+ ret = vf610_ocotp_wait_busy(ocotp->base + OCOTP_CTRL_REG); >> > >> >WARNING: line over 80 characters >> >#244: FILE: drivers/nvmem/vf610-ocotp.c:193: >> >+ writel(OCOTP_CTRL_ERROR, ocotp->base + OCOTP_CTRL_CLR); >> > > I had. However splitting these lines seemed odd. Will split and fix. > Just get the base to a local variable, which should make it look neat and this will avoid de-referencing the octop in the all the time in loop. --srini -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Sanchayan Maity <maitysanchayan@gmail.com> |
|---|---|
| Date | 2015-08-10 16:20 +0200 |
| Subject | [PATCH v8 1/4] clk: clk-vf610: Add clock for Vybrid OCOTP controller |
| Message-ID | <pVW5J-iZ-33@gated-at.bofh.it> |
| In reply to | #1204312 |
Add clock support for Vybrid On-Chip One Time Programmable
(OCOTP) controller.
While the OCOTP block does not require explicit clock gating,
for programming the OCOTP timing register the clock rate of
ipg clock is required for timing calculations related to fuse
and shadow register read sequence. We explicitly specify the
ipg clock for OCOTP as a result.
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
---
drivers/clk/imx/clk-vf610.c | 1 +
include/dt-bindings/clock/vf610-clock.h | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index bff45ea..d1b1c95 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -387,6 +387,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_SNVS] = imx_clk_gate2("snvs-rtc", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(7));
clk[VF610_CLK_DAP] = imx_clk_gate("dap", "platform_bus", CCM_CCSR, 24);
+ clk[VF610_CLK_OCOTP] = imx_clk_gate("ocotp", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(5));
imx_check_clocks(clk, ARRAY_SIZE(clk));
diff --git a/include/dt-bindings/clock/vf610-clock.h b/include/dt-bindings/clock/vf610-clock.h
index d197634..56c16aa 100644
--- a/include/dt-bindings/clock/vf610-clock.h
+++ b/include/dt-bindings/clock/vf610-clock.h
@@ -194,6 +194,7 @@
#define VF610_PLL7_BYPASS 181
#define VF610_CLK_SNVS 182
#define VF610_CLK_DAP 183
-#define VF610_CLK_END 184
+#define VF610_CLK_OCOTP 184
+#define VF610_CLK_END 185
#endif /* __DT_BINDINGS_CLOCK_VF610_H */
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web