12aec85b2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22aec85b2SThomas Gleixner // Copyright (C) 2016 Broadcom
39d59c6e8SJonathan Richardson
416941555SSrinath Mannam #include <linux/acpi.h>
59d59c6e8SJonathan Richardson #include <linux/delay.h>
69d59c6e8SJonathan Richardson #include <linux/device.h>
79d59c6e8SJonathan Richardson #include <linux/io.h>
89d59c6e8SJonathan Richardson #include <linux/module.h>
99d59c6e8SJonathan Richardson #include <linux/nvmem-provider.h>
109d59c6e8SJonathan Richardson #include <linux/of.h>
119d59c6e8SJonathan Richardson #include <linux/platform_device.h>
129d59c6e8SJonathan Richardson
139d59c6e8SJonathan Richardson /*
149d59c6e8SJonathan Richardson * # of tries for OTP Status. The time to execute a command varies. The slowest
159d59c6e8SJonathan Richardson * commands are writes which also vary based on the # of bits turned on. Writing
169d59c6e8SJonathan Richardson * 0xffffffff takes ~3800 us.
179d59c6e8SJonathan Richardson */
189d59c6e8SJonathan Richardson #define OTPC_RETRIES 5000
199d59c6e8SJonathan Richardson
209d59c6e8SJonathan Richardson /* Sequence to enable OTP program */
219d59c6e8SJonathan Richardson #define OTPC_PROG_EN_SEQ { 0xf, 0x4, 0x8, 0xd }
229d59c6e8SJonathan Richardson
239d59c6e8SJonathan Richardson /* OTPC Commands */
249d59c6e8SJonathan Richardson #define OTPC_CMD_READ 0x0
259d59c6e8SJonathan Richardson #define OTPC_CMD_OTP_PROG_ENABLE 0x2
269d59c6e8SJonathan Richardson #define OTPC_CMD_OTP_PROG_DISABLE 0x3
27e827756dSOza Pawandeep #define OTPC_CMD_PROGRAM 0x8
289d59c6e8SJonathan Richardson
299d59c6e8SJonathan Richardson /* OTPC Status Bits */
309d59c6e8SJonathan Richardson #define OTPC_STAT_CMD_DONE BIT(1)
319d59c6e8SJonathan Richardson #define OTPC_STAT_PROG_OK BIT(2)
329d59c6e8SJonathan Richardson
339d59c6e8SJonathan Richardson /* OTPC register definition */
349d59c6e8SJonathan Richardson #define OTPC_MODE_REG_OFFSET 0x0
359d59c6e8SJonathan Richardson #define OTPC_MODE_REG_OTPC_MODE 0
369d59c6e8SJonathan Richardson #define OTPC_COMMAND_OFFSET 0x4
379d59c6e8SJonathan Richardson #define OTPC_COMMAND_COMMAND_WIDTH 6
389d59c6e8SJonathan Richardson #define OTPC_CMD_START_OFFSET 0x8
399d59c6e8SJonathan Richardson #define OTPC_CMD_START_START 0
409d59c6e8SJonathan Richardson #define OTPC_CPU_STATUS_OFFSET 0xc
419d59c6e8SJonathan Richardson #define OTPC_CPUADDR_REG_OFFSET 0x28
429d59c6e8SJonathan Richardson #define OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH 16
439d59c6e8SJonathan Richardson #define OTPC_CPU_WRITE_REG_OFFSET 0x2c
449d59c6e8SJonathan Richardson
459d59c6e8SJonathan Richardson #define OTPC_CMD_MASK (BIT(OTPC_COMMAND_COMMAND_WIDTH) - 1)
469d59c6e8SJonathan Richardson #define OTPC_ADDR_MASK (BIT(OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH) - 1)
479d59c6e8SJonathan Richardson
489d59c6e8SJonathan Richardson
499d59c6e8SJonathan Richardson struct otpc_map {
509d59c6e8SJonathan Richardson /* in words. */
519d59c6e8SJonathan Richardson u32 otpc_row_size;
529d59c6e8SJonathan Richardson /* 128 bit row / 4 words support. */
539d59c6e8SJonathan Richardson u16 data_r_offset[4];
549d59c6e8SJonathan Richardson /* 128 bit row / 4 words support. */
559d59c6e8SJonathan Richardson u16 data_w_offset[4];
569d59c6e8SJonathan Richardson };
579d59c6e8SJonathan Richardson
589d59c6e8SJonathan Richardson static struct otpc_map otp_map = {
599d59c6e8SJonathan Richardson .otpc_row_size = 1,
609d59c6e8SJonathan Richardson .data_r_offset = {0x10},
619d59c6e8SJonathan Richardson .data_w_offset = {0x2c},
629d59c6e8SJonathan Richardson };
639d59c6e8SJonathan Richardson
649d59c6e8SJonathan Richardson static struct otpc_map otp_map_v2 = {
659d59c6e8SJonathan Richardson .otpc_row_size = 2,
669d59c6e8SJonathan Richardson .data_r_offset = {0x10, 0x5c},
679d59c6e8SJonathan Richardson .data_w_offset = {0x2c, 0x64},
689d59c6e8SJonathan Richardson };
699d59c6e8SJonathan Richardson
709d59c6e8SJonathan Richardson struct otpc_priv {
719d59c6e8SJonathan Richardson struct device *dev;
729d59c6e8SJonathan Richardson void __iomem *base;
7316941555SSrinath Mannam const struct otpc_map *map;
749d59c6e8SJonathan Richardson struct nvmem_config *config;
759d59c6e8SJonathan Richardson };
769d59c6e8SJonathan Richardson
set_command(void __iomem * base,u32 command)779d59c6e8SJonathan Richardson static inline void set_command(void __iomem *base, u32 command)
789d59c6e8SJonathan Richardson {
799d59c6e8SJonathan Richardson writel(command & OTPC_CMD_MASK, base + OTPC_COMMAND_OFFSET);
809d59c6e8SJonathan Richardson }
819d59c6e8SJonathan Richardson
set_cpu_address(void __iomem * base,u32 addr)829d59c6e8SJonathan Richardson static inline void set_cpu_address(void __iomem *base, u32 addr)
839d59c6e8SJonathan Richardson {
849d59c6e8SJonathan Richardson writel(addr & OTPC_ADDR_MASK, base + OTPC_CPUADDR_REG_OFFSET);
859d59c6e8SJonathan Richardson }
869d59c6e8SJonathan Richardson
set_start_bit(void __iomem * base)879d59c6e8SJonathan Richardson static inline void set_start_bit(void __iomem *base)
889d59c6e8SJonathan Richardson {
899d59c6e8SJonathan Richardson writel(1 << OTPC_CMD_START_START, base + OTPC_CMD_START_OFFSET);
909d59c6e8SJonathan Richardson }
919d59c6e8SJonathan Richardson
reset_start_bit(void __iomem * base)929d59c6e8SJonathan Richardson static inline void reset_start_bit(void __iomem *base)
939d59c6e8SJonathan Richardson {
949d59c6e8SJonathan Richardson writel(0, base + OTPC_CMD_START_OFFSET);
959d59c6e8SJonathan Richardson }
969d59c6e8SJonathan Richardson
write_cpu_data(void __iomem * base,u32 value)979d59c6e8SJonathan Richardson static inline void write_cpu_data(void __iomem *base, u32 value)
989d59c6e8SJonathan Richardson {
999d59c6e8SJonathan Richardson writel(value, base + OTPC_CPU_WRITE_REG_OFFSET);
1009d59c6e8SJonathan Richardson }
1019d59c6e8SJonathan Richardson
poll_cpu_status(void __iomem * base,u32 value)1029d59c6e8SJonathan Richardson static int poll_cpu_status(void __iomem *base, u32 value)
1039d59c6e8SJonathan Richardson {
1049d59c6e8SJonathan Richardson u32 status;
1059d59c6e8SJonathan Richardson u32 retries;
1069d59c6e8SJonathan Richardson
1079d59c6e8SJonathan Richardson for (retries = 0; retries < OTPC_RETRIES; retries++) {
1089d59c6e8SJonathan Richardson status = readl(base + OTPC_CPU_STATUS_OFFSET);
1099d59c6e8SJonathan Richardson if (status & value)
1109d59c6e8SJonathan Richardson break;
1119d59c6e8SJonathan Richardson udelay(1);
1129d59c6e8SJonathan Richardson }
1139d59c6e8SJonathan Richardson if (retries == OTPC_RETRIES)
1149d59c6e8SJonathan Richardson return -EAGAIN;
1159d59c6e8SJonathan Richardson
1169d59c6e8SJonathan Richardson return 0;
1179d59c6e8SJonathan Richardson }
1189d59c6e8SJonathan Richardson
enable_ocotp_program(void __iomem * base)1199d59c6e8SJonathan Richardson static int enable_ocotp_program(void __iomem *base)
1209d59c6e8SJonathan Richardson {
1219d59c6e8SJonathan Richardson static const u32 vals[] = OTPC_PROG_EN_SEQ;
1229d59c6e8SJonathan Richardson int i;
1239d59c6e8SJonathan Richardson int ret;
1249d59c6e8SJonathan Richardson
1259d59c6e8SJonathan Richardson /* Write the magic sequence to enable programming */
1269d59c6e8SJonathan Richardson set_command(base, OTPC_CMD_OTP_PROG_ENABLE);
1279d59c6e8SJonathan Richardson for (i = 0; i < ARRAY_SIZE(vals); i++) {
1289d59c6e8SJonathan Richardson write_cpu_data(base, vals[i]);
1299d59c6e8SJonathan Richardson set_start_bit(base);
1309d59c6e8SJonathan Richardson ret = poll_cpu_status(base, OTPC_STAT_CMD_DONE);
1319d59c6e8SJonathan Richardson reset_start_bit(base);
1329d59c6e8SJonathan Richardson if (ret)
1339d59c6e8SJonathan Richardson return ret;
1349d59c6e8SJonathan Richardson }
1359d59c6e8SJonathan Richardson
1369d59c6e8SJonathan Richardson return poll_cpu_status(base, OTPC_STAT_PROG_OK);
1379d59c6e8SJonathan Richardson }
1389d59c6e8SJonathan Richardson
disable_ocotp_program(void __iomem * base)1399d59c6e8SJonathan Richardson static int disable_ocotp_program(void __iomem *base)
1409d59c6e8SJonathan Richardson {
1419d59c6e8SJonathan Richardson int ret;
1429d59c6e8SJonathan Richardson
1439d59c6e8SJonathan Richardson set_command(base, OTPC_CMD_OTP_PROG_DISABLE);
1449d59c6e8SJonathan Richardson set_start_bit(base);
1459d59c6e8SJonathan Richardson ret = poll_cpu_status(base, OTPC_STAT_PROG_OK);
1469d59c6e8SJonathan Richardson reset_start_bit(base);
1479d59c6e8SJonathan Richardson
1489d59c6e8SJonathan Richardson return ret;
1499d59c6e8SJonathan Richardson }
1509d59c6e8SJonathan Richardson
bcm_otpc_read(void * context,unsigned int offset,void * val,size_t bytes)1519d59c6e8SJonathan Richardson static int bcm_otpc_read(void *context, unsigned int offset, void *val,
1529d59c6e8SJonathan Richardson size_t bytes)
1539d59c6e8SJonathan Richardson {
1549d59c6e8SJonathan Richardson struct otpc_priv *priv = context;
1559d59c6e8SJonathan Richardson u32 *buf = val;
1569d59c6e8SJonathan Richardson u32 bytes_read;
1579d59c6e8SJonathan Richardson u32 address = offset / priv->config->word_size;
1589d59c6e8SJonathan Richardson int i, ret;
1599d59c6e8SJonathan Richardson
1609d59c6e8SJonathan Richardson for (bytes_read = 0; bytes_read < bytes;) {
1619d59c6e8SJonathan Richardson set_command(priv->base, OTPC_CMD_READ);
1629d59c6e8SJonathan Richardson set_cpu_address(priv->base, address++);
1639d59c6e8SJonathan Richardson set_start_bit(priv->base);
1649d59c6e8SJonathan Richardson ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
1659d59c6e8SJonathan Richardson if (ret) {
1669d59c6e8SJonathan Richardson dev_err(priv->dev, "otp read error: 0x%x", ret);
1679d59c6e8SJonathan Richardson return -EIO;
1689d59c6e8SJonathan Richardson }
1699d59c6e8SJonathan Richardson
1709d59c6e8SJonathan Richardson for (i = 0; i < priv->map->otpc_row_size; i++) {
1719d59c6e8SJonathan Richardson *buf++ = readl(priv->base +
1729d59c6e8SJonathan Richardson priv->map->data_r_offset[i]);
1739d59c6e8SJonathan Richardson bytes_read += sizeof(*buf);
1749d59c6e8SJonathan Richardson }
1759d59c6e8SJonathan Richardson
1769d59c6e8SJonathan Richardson reset_start_bit(priv->base);
1779d59c6e8SJonathan Richardson }
1789d59c6e8SJonathan Richardson
1799d59c6e8SJonathan Richardson return 0;
1809d59c6e8SJonathan Richardson }
1819d59c6e8SJonathan Richardson
bcm_otpc_write(void * context,unsigned int offset,void * val,size_t bytes)1829d59c6e8SJonathan Richardson static int bcm_otpc_write(void *context, unsigned int offset, void *val,
1839d59c6e8SJonathan Richardson size_t bytes)
1849d59c6e8SJonathan Richardson {
1859d59c6e8SJonathan Richardson struct otpc_priv *priv = context;
1869d59c6e8SJonathan Richardson u32 *buf = val;
1879d59c6e8SJonathan Richardson u32 bytes_written;
1889d59c6e8SJonathan Richardson u32 address = offset / priv->config->word_size;
1899d59c6e8SJonathan Richardson int i, ret;
1909d59c6e8SJonathan Richardson
1919d59c6e8SJonathan Richardson if (offset % priv->config->word_size)
1929d59c6e8SJonathan Richardson return -EINVAL;
1939d59c6e8SJonathan Richardson
1949d59c6e8SJonathan Richardson ret = enable_ocotp_program(priv->base);
1959d59c6e8SJonathan Richardson if (ret)
1969d59c6e8SJonathan Richardson return -EIO;
1979d59c6e8SJonathan Richardson
1989d59c6e8SJonathan Richardson for (bytes_written = 0; bytes_written < bytes;) {
1999d59c6e8SJonathan Richardson set_command(priv->base, OTPC_CMD_PROGRAM);
2009d59c6e8SJonathan Richardson set_cpu_address(priv->base, address++);
2019d59c6e8SJonathan Richardson for (i = 0; i < priv->map->otpc_row_size; i++) {
202e827756dSOza Pawandeep writel(*buf, priv->base + priv->map->data_w_offset[i]);
2039d59c6e8SJonathan Richardson buf++;
2049d59c6e8SJonathan Richardson bytes_written += sizeof(*buf);
2059d59c6e8SJonathan Richardson }
2069d59c6e8SJonathan Richardson set_start_bit(priv->base);
2079d59c6e8SJonathan Richardson ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
2089d59c6e8SJonathan Richardson reset_start_bit(priv->base);
2099d59c6e8SJonathan Richardson if (ret) {
2109d59c6e8SJonathan Richardson dev_err(priv->dev, "otp write error: 0x%x", ret);
2119d59c6e8SJonathan Richardson return -EIO;
2129d59c6e8SJonathan Richardson }
2139d59c6e8SJonathan Richardson }
2149d59c6e8SJonathan Richardson
2159d59c6e8SJonathan Richardson disable_ocotp_program(priv->base);
2169d59c6e8SJonathan Richardson
2179d59c6e8SJonathan Richardson return 0;
2189d59c6e8SJonathan Richardson }
2199d59c6e8SJonathan Richardson
2209d59c6e8SJonathan Richardson static struct nvmem_config bcm_otpc_nvmem_config = {
2219d59c6e8SJonathan Richardson .name = "bcm-ocotp",
2229d59c6e8SJonathan Richardson .read_only = false,
2239d59c6e8SJonathan Richardson .word_size = 4,
2249d59c6e8SJonathan Richardson .stride = 4,
2259d59c6e8SJonathan Richardson .reg_read = bcm_otpc_read,
2269d59c6e8SJonathan Richardson .reg_write = bcm_otpc_write,
2279d59c6e8SJonathan Richardson };
2289d59c6e8SJonathan Richardson
2299d59c6e8SJonathan Richardson static const struct of_device_id bcm_otpc_dt_ids[] = {
23016941555SSrinath Mannam { .compatible = "brcm,ocotp", .data = &otp_map },
23116941555SSrinath Mannam { .compatible = "brcm,ocotp-v2", .data = &otp_map_v2 },
2329d59c6e8SJonathan Richardson { },
2339d59c6e8SJonathan Richardson };
2349d59c6e8SJonathan Richardson MODULE_DEVICE_TABLE(of, bcm_otpc_dt_ids);
2359d59c6e8SJonathan Richardson
2366bd0ffeaSKrzysztof Kozlowski static const struct acpi_device_id bcm_otpc_acpi_ids[] __maybe_unused = {
23716941555SSrinath Mannam { .id = "BRCM0700", .driver_data = (kernel_ulong_t)&otp_map },
23816941555SSrinath Mannam { .id = "BRCM0701", .driver_data = (kernel_ulong_t)&otp_map_v2 },
23916941555SSrinath Mannam { /* sentinel */ }
24016941555SSrinath Mannam };
24116941555SSrinath Mannam MODULE_DEVICE_TABLE(acpi, bcm_otpc_acpi_ids);
24216941555SSrinath Mannam
bcm_otpc_probe(struct platform_device * pdev)2439d59c6e8SJonathan Richardson static int bcm_otpc_probe(struct platform_device *pdev)
2449d59c6e8SJonathan Richardson {
2459d59c6e8SJonathan Richardson struct device *dev = &pdev->dev;
2469d59c6e8SJonathan Richardson struct otpc_priv *priv;
2479d59c6e8SJonathan Richardson struct nvmem_device *nvmem;
2489d59c6e8SJonathan Richardson int err;
2499d59c6e8SJonathan Richardson u32 num_words;
2509d59c6e8SJonathan Richardson
2519d59c6e8SJonathan Richardson priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2529d59c6e8SJonathan Richardson if (!priv)
2539d59c6e8SJonathan Richardson return -ENOMEM;
2549d59c6e8SJonathan Richardson
25516941555SSrinath Mannam priv->map = device_get_match_data(dev);
25616941555SSrinath Mannam if (!priv->map)
25716941555SSrinath Mannam return -ENODEV;
2589d59c6e8SJonathan Richardson
2599d59c6e8SJonathan Richardson /* Get OTP base address register. */
260*1dc552faSYang Li priv->base = devm_platform_ioremap_resource(pdev, 0);
2619d59c6e8SJonathan Richardson if (IS_ERR(priv->base)) {
2629d59c6e8SJonathan Richardson dev_err(dev, "unable to map I/O memory\n");
2639d59c6e8SJonathan Richardson return PTR_ERR(priv->base);
2649d59c6e8SJonathan Richardson }
2659d59c6e8SJonathan Richardson
2669d59c6e8SJonathan Richardson /* Enable CPU access to OTPC. */
2679d59c6e8SJonathan Richardson writel(readl(priv->base + OTPC_MODE_REG_OFFSET) |
2689d59c6e8SJonathan Richardson BIT(OTPC_MODE_REG_OTPC_MODE),
2699d59c6e8SJonathan Richardson priv->base + OTPC_MODE_REG_OFFSET);
2709d59c6e8SJonathan Richardson reset_start_bit(priv->base);
2719d59c6e8SJonathan Richardson
2729d59c6e8SJonathan Richardson /* Read size of memory in words. */
27316941555SSrinath Mannam err = device_property_read_u32(dev, "brcm,ocotp-size", &num_words);
2749d59c6e8SJonathan Richardson if (err) {
2759d59c6e8SJonathan Richardson dev_err(dev, "size parameter not specified\n");
2769d59c6e8SJonathan Richardson return -EINVAL;
2779d59c6e8SJonathan Richardson } else if (num_words == 0) {
2789d59c6e8SJonathan Richardson dev_err(dev, "size must be > 0\n");
2799d59c6e8SJonathan Richardson return -EINVAL;
2809d59c6e8SJonathan Richardson }
2819d59c6e8SJonathan Richardson
2829d59c6e8SJonathan Richardson bcm_otpc_nvmem_config.size = 4 * num_words;
2839d59c6e8SJonathan Richardson bcm_otpc_nvmem_config.dev = dev;
2849d59c6e8SJonathan Richardson bcm_otpc_nvmem_config.priv = priv;
2859d59c6e8SJonathan Richardson
28616941555SSrinath Mannam if (priv->map == &otp_map_v2) {
2879d59c6e8SJonathan Richardson bcm_otpc_nvmem_config.word_size = 8;
2889d59c6e8SJonathan Richardson bcm_otpc_nvmem_config.stride = 8;
2899d59c6e8SJonathan Richardson }
2909d59c6e8SJonathan Richardson
2919d59c6e8SJonathan Richardson priv->config = &bcm_otpc_nvmem_config;
2929d59c6e8SJonathan Richardson
293b2236dbdSAndrey Smirnov nvmem = devm_nvmem_register(dev, &bcm_otpc_nvmem_config);
2949d59c6e8SJonathan Richardson if (IS_ERR(nvmem)) {
2959d59c6e8SJonathan Richardson dev_err(dev, "error registering nvmem config\n");
2969d59c6e8SJonathan Richardson return PTR_ERR(nvmem);
2979d59c6e8SJonathan Richardson }
2989d59c6e8SJonathan Richardson
2999d59c6e8SJonathan Richardson return 0;
3009d59c6e8SJonathan Richardson }
3019d59c6e8SJonathan Richardson
3029d59c6e8SJonathan Richardson static struct platform_driver bcm_otpc_driver = {
3039d59c6e8SJonathan Richardson .probe = bcm_otpc_probe,
3049d59c6e8SJonathan Richardson .driver = {
3059d59c6e8SJonathan Richardson .name = "brcm-otpc",
3069d59c6e8SJonathan Richardson .of_match_table = bcm_otpc_dt_ids,
30716941555SSrinath Mannam .acpi_match_table = ACPI_PTR(bcm_otpc_acpi_ids),
3089d59c6e8SJonathan Richardson },
3099d59c6e8SJonathan Richardson };
3109d59c6e8SJonathan Richardson module_platform_driver(bcm_otpc_driver);
3119d59c6e8SJonathan Richardson
3129d59c6e8SJonathan Richardson MODULE_DESCRIPTION("Broadcom OTPC driver");
3139d59c6e8SJonathan Richardson MODULE_LICENSE("GPL v2");
314