xref: /linux/drivers/nvmem/bcm-ocotp.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
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