xref: /linux/drivers/nvmem/bcm-ocotp.c (revision c3bdd5e65185f46150b3bac103b3854040487857)
19d59c6e8SJonathan Richardson /*
29d59c6e8SJonathan Richardson  * Copyright (C) 2016 Broadcom
39d59c6e8SJonathan Richardson  *
49d59c6e8SJonathan Richardson  * This program is free software; you can redistribute it and/or
59d59c6e8SJonathan Richardson  * modify it under the terms of the GNU General Public License as
69d59c6e8SJonathan Richardson  * published by the Free Software Foundation version 2.
79d59c6e8SJonathan Richardson  *
89d59c6e8SJonathan Richardson  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
99d59c6e8SJonathan Richardson  * kind, whether express or implied; without even the implied warranty
109d59c6e8SJonathan Richardson  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
119d59c6e8SJonathan Richardson  * GNU General Public License for more details.
129d59c6e8SJonathan Richardson  */
139d59c6e8SJonathan Richardson 
14*16941555SSrinath Mannam #include <linux/acpi.h>
159d59c6e8SJonathan Richardson #include <linux/delay.h>
169d59c6e8SJonathan Richardson #include <linux/device.h>
179d59c6e8SJonathan Richardson #include <linux/io.h>
189d59c6e8SJonathan Richardson #include <linux/module.h>
199d59c6e8SJonathan Richardson #include <linux/nvmem-provider.h>
209d59c6e8SJonathan Richardson #include <linux/of.h>
21*16941555SSrinath Mannam #include <linux/of_device.h>
229d59c6e8SJonathan Richardson #include <linux/platform_device.h>
239d59c6e8SJonathan Richardson 
249d59c6e8SJonathan Richardson /*
259d59c6e8SJonathan Richardson  * # of tries for OTP Status. The time to execute a command varies. The slowest
269d59c6e8SJonathan Richardson  * commands are writes which also vary based on the # of bits turned on. Writing
279d59c6e8SJonathan Richardson  * 0xffffffff takes ~3800 us.
289d59c6e8SJonathan Richardson  */
299d59c6e8SJonathan Richardson #define OTPC_RETRIES                 5000
309d59c6e8SJonathan Richardson 
319d59c6e8SJonathan Richardson /* Sequence to enable OTP program */
329d59c6e8SJonathan Richardson #define OTPC_PROG_EN_SEQ             { 0xf, 0x4, 0x8, 0xd }
339d59c6e8SJonathan Richardson 
349d59c6e8SJonathan Richardson /* OTPC Commands */
359d59c6e8SJonathan Richardson #define OTPC_CMD_READ                0x0
369d59c6e8SJonathan Richardson #define OTPC_CMD_OTP_PROG_ENABLE     0x2
379d59c6e8SJonathan Richardson #define OTPC_CMD_OTP_PROG_DISABLE    0x3
38e827756dSOza Pawandeep #define OTPC_CMD_PROGRAM             0x8
399d59c6e8SJonathan Richardson 
409d59c6e8SJonathan Richardson /* OTPC Status Bits */
419d59c6e8SJonathan Richardson #define OTPC_STAT_CMD_DONE           BIT(1)
429d59c6e8SJonathan Richardson #define OTPC_STAT_PROG_OK            BIT(2)
439d59c6e8SJonathan Richardson 
449d59c6e8SJonathan Richardson /* OTPC register definition */
459d59c6e8SJonathan Richardson #define OTPC_MODE_REG_OFFSET         0x0
469d59c6e8SJonathan Richardson #define OTPC_MODE_REG_OTPC_MODE      0
479d59c6e8SJonathan Richardson #define OTPC_COMMAND_OFFSET          0x4
489d59c6e8SJonathan Richardson #define OTPC_COMMAND_COMMAND_WIDTH   6
499d59c6e8SJonathan Richardson #define OTPC_CMD_START_OFFSET        0x8
509d59c6e8SJonathan Richardson #define OTPC_CMD_START_START         0
519d59c6e8SJonathan Richardson #define OTPC_CPU_STATUS_OFFSET       0xc
529d59c6e8SJonathan Richardson #define OTPC_CPUADDR_REG_OFFSET      0x28
539d59c6e8SJonathan Richardson #define OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH 16
549d59c6e8SJonathan Richardson #define OTPC_CPU_WRITE_REG_OFFSET    0x2c
559d59c6e8SJonathan Richardson 
569d59c6e8SJonathan Richardson #define OTPC_CMD_MASK  (BIT(OTPC_COMMAND_COMMAND_WIDTH) - 1)
579d59c6e8SJonathan Richardson #define OTPC_ADDR_MASK (BIT(OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH) - 1)
589d59c6e8SJonathan Richardson 
599d59c6e8SJonathan Richardson 
609d59c6e8SJonathan Richardson struct otpc_map {
619d59c6e8SJonathan Richardson 	/* in words. */
629d59c6e8SJonathan Richardson 	u32 otpc_row_size;
639d59c6e8SJonathan Richardson 	/* 128 bit row / 4 words support. */
649d59c6e8SJonathan Richardson 	u16 data_r_offset[4];
659d59c6e8SJonathan Richardson 	/* 128 bit row / 4 words support. */
669d59c6e8SJonathan Richardson 	u16 data_w_offset[4];
679d59c6e8SJonathan Richardson };
689d59c6e8SJonathan Richardson 
699d59c6e8SJonathan Richardson static struct otpc_map otp_map = {
709d59c6e8SJonathan Richardson 	.otpc_row_size = 1,
719d59c6e8SJonathan Richardson 	.data_r_offset = {0x10},
729d59c6e8SJonathan Richardson 	.data_w_offset = {0x2c},
739d59c6e8SJonathan Richardson };
749d59c6e8SJonathan Richardson 
759d59c6e8SJonathan Richardson static struct otpc_map otp_map_v2 = {
769d59c6e8SJonathan Richardson 	.otpc_row_size = 2,
779d59c6e8SJonathan Richardson 	.data_r_offset = {0x10, 0x5c},
789d59c6e8SJonathan Richardson 	.data_w_offset = {0x2c, 0x64},
799d59c6e8SJonathan Richardson };
809d59c6e8SJonathan Richardson 
819d59c6e8SJonathan Richardson struct otpc_priv {
829d59c6e8SJonathan Richardson 	struct device *dev;
839d59c6e8SJonathan Richardson 	void __iomem *base;
84*16941555SSrinath Mannam 	const struct otpc_map *map;
859d59c6e8SJonathan Richardson 	struct nvmem_config *config;
869d59c6e8SJonathan Richardson };
879d59c6e8SJonathan Richardson 
889d59c6e8SJonathan Richardson static inline void set_command(void __iomem *base, u32 command)
899d59c6e8SJonathan Richardson {
909d59c6e8SJonathan Richardson 	writel(command & OTPC_CMD_MASK, base + OTPC_COMMAND_OFFSET);
919d59c6e8SJonathan Richardson }
929d59c6e8SJonathan Richardson 
939d59c6e8SJonathan Richardson static inline void set_cpu_address(void __iomem *base, u32 addr)
949d59c6e8SJonathan Richardson {
959d59c6e8SJonathan Richardson 	writel(addr & OTPC_ADDR_MASK, base + OTPC_CPUADDR_REG_OFFSET);
969d59c6e8SJonathan Richardson }
979d59c6e8SJonathan Richardson 
989d59c6e8SJonathan Richardson static inline void set_start_bit(void __iomem *base)
999d59c6e8SJonathan Richardson {
1009d59c6e8SJonathan Richardson 	writel(1 << OTPC_CMD_START_START, base + OTPC_CMD_START_OFFSET);
1019d59c6e8SJonathan Richardson }
1029d59c6e8SJonathan Richardson 
1039d59c6e8SJonathan Richardson static inline void reset_start_bit(void __iomem *base)
1049d59c6e8SJonathan Richardson {
1059d59c6e8SJonathan Richardson 	writel(0, base + OTPC_CMD_START_OFFSET);
1069d59c6e8SJonathan Richardson }
1079d59c6e8SJonathan Richardson 
1089d59c6e8SJonathan Richardson static inline void write_cpu_data(void __iomem *base, u32 value)
1099d59c6e8SJonathan Richardson {
1109d59c6e8SJonathan Richardson 	writel(value, base + OTPC_CPU_WRITE_REG_OFFSET);
1119d59c6e8SJonathan Richardson }
1129d59c6e8SJonathan Richardson 
1139d59c6e8SJonathan Richardson static int poll_cpu_status(void __iomem *base, u32 value)
1149d59c6e8SJonathan Richardson {
1159d59c6e8SJonathan Richardson 	u32 status;
1169d59c6e8SJonathan Richardson 	u32 retries;
1179d59c6e8SJonathan Richardson 
1189d59c6e8SJonathan Richardson 	for (retries = 0; retries < OTPC_RETRIES; retries++) {
1199d59c6e8SJonathan Richardson 		status = readl(base + OTPC_CPU_STATUS_OFFSET);
1209d59c6e8SJonathan Richardson 		if (status & value)
1219d59c6e8SJonathan Richardson 			break;
1229d59c6e8SJonathan Richardson 		udelay(1);
1239d59c6e8SJonathan Richardson 	}
1249d59c6e8SJonathan Richardson 	if (retries == OTPC_RETRIES)
1259d59c6e8SJonathan Richardson 		return -EAGAIN;
1269d59c6e8SJonathan Richardson 
1279d59c6e8SJonathan Richardson 	return 0;
1289d59c6e8SJonathan Richardson }
1299d59c6e8SJonathan Richardson 
1309d59c6e8SJonathan Richardson static int enable_ocotp_program(void __iomem *base)
1319d59c6e8SJonathan Richardson {
1329d59c6e8SJonathan Richardson 	static const u32 vals[] = OTPC_PROG_EN_SEQ;
1339d59c6e8SJonathan Richardson 	int i;
1349d59c6e8SJonathan Richardson 	int ret;
1359d59c6e8SJonathan Richardson 
1369d59c6e8SJonathan Richardson 	/* Write the magic sequence to enable programming */
1379d59c6e8SJonathan Richardson 	set_command(base, OTPC_CMD_OTP_PROG_ENABLE);
1389d59c6e8SJonathan Richardson 	for (i = 0; i < ARRAY_SIZE(vals); i++) {
1399d59c6e8SJonathan Richardson 		write_cpu_data(base, vals[i]);
1409d59c6e8SJonathan Richardson 		set_start_bit(base);
1419d59c6e8SJonathan Richardson 		ret = poll_cpu_status(base, OTPC_STAT_CMD_DONE);
1429d59c6e8SJonathan Richardson 		reset_start_bit(base);
1439d59c6e8SJonathan Richardson 		if (ret)
1449d59c6e8SJonathan Richardson 			return ret;
1459d59c6e8SJonathan Richardson 	}
1469d59c6e8SJonathan Richardson 
1479d59c6e8SJonathan Richardson 	return poll_cpu_status(base, OTPC_STAT_PROG_OK);
1489d59c6e8SJonathan Richardson }
1499d59c6e8SJonathan Richardson 
1509d59c6e8SJonathan Richardson static int disable_ocotp_program(void __iomem *base)
1519d59c6e8SJonathan Richardson {
1529d59c6e8SJonathan Richardson 	int ret;
1539d59c6e8SJonathan Richardson 
1549d59c6e8SJonathan Richardson 	set_command(base, OTPC_CMD_OTP_PROG_DISABLE);
1559d59c6e8SJonathan Richardson 	set_start_bit(base);
1569d59c6e8SJonathan Richardson 	ret = poll_cpu_status(base, OTPC_STAT_PROG_OK);
1579d59c6e8SJonathan Richardson 	reset_start_bit(base);
1589d59c6e8SJonathan Richardson 
1599d59c6e8SJonathan Richardson 	return ret;
1609d59c6e8SJonathan Richardson }
1619d59c6e8SJonathan Richardson 
1629d59c6e8SJonathan Richardson static int bcm_otpc_read(void *context, unsigned int offset, void *val,
1639d59c6e8SJonathan Richardson 	size_t bytes)
1649d59c6e8SJonathan Richardson {
1659d59c6e8SJonathan Richardson 	struct otpc_priv *priv = context;
1669d59c6e8SJonathan Richardson 	u32 *buf = val;
1679d59c6e8SJonathan Richardson 	u32 bytes_read;
1689d59c6e8SJonathan Richardson 	u32 address = offset / priv->config->word_size;
1699d59c6e8SJonathan Richardson 	int i, ret;
1709d59c6e8SJonathan Richardson 
1719d59c6e8SJonathan Richardson 	for (bytes_read = 0; bytes_read < bytes;) {
1729d59c6e8SJonathan Richardson 		set_command(priv->base, OTPC_CMD_READ);
1739d59c6e8SJonathan Richardson 		set_cpu_address(priv->base, address++);
1749d59c6e8SJonathan Richardson 		set_start_bit(priv->base);
1759d59c6e8SJonathan Richardson 		ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
1769d59c6e8SJonathan Richardson 		if (ret) {
1779d59c6e8SJonathan Richardson 			dev_err(priv->dev, "otp read error: 0x%x", ret);
1789d59c6e8SJonathan Richardson 			return -EIO;
1799d59c6e8SJonathan Richardson 		}
1809d59c6e8SJonathan Richardson 
1819d59c6e8SJonathan Richardson 		for (i = 0; i < priv->map->otpc_row_size; i++) {
1829d59c6e8SJonathan Richardson 			*buf++ = readl(priv->base +
1839d59c6e8SJonathan Richardson 					priv->map->data_r_offset[i]);
1849d59c6e8SJonathan Richardson 			bytes_read += sizeof(*buf);
1859d59c6e8SJonathan Richardson 		}
1869d59c6e8SJonathan Richardson 
1879d59c6e8SJonathan Richardson 		reset_start_bit(priv->base);
1889d59c6e8SJonathan Richardson 	}
1899d59c6e8SJonathan Richardson 
1909d59c6e8SJonathan Richardson 	return 0;
1919d59c6e8SJonathan Richardson }
1929d59c6e8SJonathan Richardson 
1939d59c6e8SJonathan Richardson static int bcm_otpc_write(void *context, unsigned int offset, void *val,
1949d59c6e8SJonathan Richardson 	size_t bytes)
1959d59c6e8SJonathan Richardson {
1969d59c6e8SJonathan Richardson 	struct otpc_priv *priv = context;
1979d59c6e8SJonathan Richardson 	u32 *buf = val;
1989d59c6e8SJonathan Richardson 	u32 bytes_written;
1999d59c6e8SJonathan Richardson 	u32 address = offset / priv->config->word_size;
2009d59c6e8SJonathan Richardson 	int i, ret;
2019d59c6e8SJonathan Richardson 
2029d59c6e8SJonathan Richardson 	if (offset % priv->config->word_size)
2039d59c6e8SJonathan Richardson 		return -EINVAL;
2049d59c6e8SJonathan Richardson 
2059d59c6e8SJonathan Richardson 	ret = enable_ocotp_program(priv->base);
2069d59c6e8SJonathan Richardson 	if (ret)
2079d59c6e8SJonathan Richardson 		return -EIO;
2089d59c6e8SJonathan Richardson 
2099d59c6e8SJonathan Richardson 	for (bytes_written = 0; bytes_written < bytes;) {
2109d59c6e8SJonathan Richardson 		set_command(priv->base, OTPC_CMD_PROGRAM);
2119d59c6e8SJonathan Richardson 		set_cpu_address(priv->base, address++);
2129d59c6e8SJonathan Richardson 		for (i = 0; i < priv->map->otpc_row_size; i++) {
213e827756dSOza Pawandeep 			writel(*buf, priv->base + priv->map->data_w_offset[i]);
2149d59c6e8SJonathan Richardson 			buf++;
2159d59c6e8SJonathan Richardson 			bytes_written += sizeof(*buf);
2169d59c6e8SJonathan Richardson 		}
2179d59c6e8SJonathan Richardson 		set_start_bit(priv->base);
2189d59c6e8SJonathan Richardson 		ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
2199d59c6e8SJonathan Richardson 		reset_start_bit(priv->base);
2209d59c6e8SJonathan Richardson 		if (ret) {
2219d59c6e8SJonathan Richardson 			dev_err(priv->dev, "otp write error: 0x%x", ret);
2229d59c6e8SJonathan Richardson 			return -EIO;
2239d59c6e8SJonathan Richardson 		}
2249d59c6e8SJonathan Richardson 	}
2259d59c6e8SJonathan Richardson 
2269d59c6e8SJonathan Richardson 	disable_ocotp_program(priv->base);
2279d59c6e8SJonathan Richardson 
2289d59c6e8SJonathan Richardson 	return 0;
2299d59c6e8SJonathan Richardson }
2309d59c6e8SJonathan Richardson 
2319d59c6e8SJonathan Richardson static struct nvmem_config bcm_otpc_nvmem_config = {
2329d59c6e8SJonathan Richardson 	.name = "bcm-ocotp",
2339d59c6e8SJonathan Richardson 	.read_only = false,
2349d59c6e8SJonathan Richardson 	.word_size = 4,
2359d59c6e8SJonathan Richardson 	.stride = 4,
2369d59c6e8SJonathan Richardson 	.reg_read = bcm_otpc_read,
2379d59c6e8SJonathan Richardson 	.reg_write = bcm_otpc_write,
2389d59c6e8SJonathan Richardson };
2399d59c6e8SJonathan Richardson 
2409d59c6e8SJonathan Richardson static const struct of_device_id bcm_otpc_dt_ids[] = {
241*16941555SSrinath Mannam 	{ .compatible = "brcm,ocotp", .data = &otp_map },
242*16941555SSrinath Mannam 	{ .compatible = "brcm,ocotp-v2", .data = &otp_map_v2 },
2439d59c6e8SJonathan Richardson 	{ },
2449d59c6e8SJonathan Richardson };
2459d59c6e8SJonathan Richardson MODULE_DEVICE_TABLE(of, bcm_otpc_dt_ids);
2469d59c6e8SJonathan Richardson 
247*16941555SSrinath Mannam static const struct acpi_device_id bcm_otpc_acpi_ids[] = {
248*16941555SSrinath Mannam 	{ .id = "BRCM0700", .driver_data = (kernel_ulong_t)&otp_map },
249*16941555SSrinath Mannam 	{ .id = "BRCM0701", .driver_data = (kernel_ulong_t)&otp_map_v2 },
250*16941555SSrinath Mannam 	{ /* sentinel */ }
251*16941555SSrinath Mannam };
252*16941555SSrinath Mannam MODULE_DEVICE_TABLE(acpi, bcm_otpc_acpi_ids);
253*16941555SSrinath Mannam 
2549d59c6e8SJonathan Richardson static int bcm_otpc_probe(struct platform_device *pdev)
2559d59c6e8SJonathan Richardson {
2569d59c6e8SJonathan Richardson 	struct device *dev = &pdev->dev;
2579d59c6e8SJonathan Richardson 	struct resource *res;
2589d59c6e8SJonathan Richardson 	struct otpc_priv *priv;
2599d59c6e8SJonathan Richardson 	struct nvmem_device *nvmem;
2609d59c6e8SJonathan Richardson 	int err;
2619d59c6e8SJonathan Richardson 	u32 num_words;
2629d59c6e8SJonathan Richardson 
2639d59c6e8SJonathan Richardson 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2649d59c6e8SJonathan Richardson 	if (!priv)
2659d59c6e8SJonathan Richardson 		return -ENOMEM;
2669d59c6e8SJonathan Richardson 
267*16941555SSrinath Mannam 	priv->map = device_get_match_data(dev);
268*16941555SSrinath Mannam 	if (!priv->map)
269*16941555SSrinath Mannam 		return -ENODEV;
2709d59c6e8SJonathan Richardson 
2719d59c6e8SJonathan Richardson 	/* Get OTP base address register. */
2729d59c6e8SJonathan Richardson 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2739d59c6e8SJonathan Richardson 	priv->base = devm_ioremap_resource(dev, res);
2749d59c6e8SJonathan Richardson 	if (IS_ERR(priv->base)) {
2759d59c6e8SJonathan Richardson 		dev_err(dev, "unable to map I/O memory\n");
2769d59c6e8SJonathan Richardson 		return PTR_ERR(priv->base);
2779d59c6e8SJonathan Richardson 	}
2789d59c6e8SJonathan Richardson 
2799d59c6e8SJonathan Richardson 	/* Enable CPU access to OTPC. */
2809d59c6e8SJonathan Richardson 	writel(readl(priv->base + OTPC_MODE_REG_OFFSET) |
2819d59c6e8SJonathan Richardson 		BIT(OTPC_MODE_REG_OTPC_MODE),
2829d59c6e8SJonathan Richardson 		priv->base + OTPC_MODE_REG_OFFSET);
2839d59c6e8SJonathan Richardson 	reset_start_bit(priv->base);
2849d59c6e8SJonathan Richardson 
2859d59c6e8SJonathan Richardson 	/* Read size of memory in words. */
286*16941555SSrinath Mannam 	err = device_property_read_u32(dev, "brcm,ocotp-size", &num_words);
2879d59c6e8SJonathan Richardson 	if (err) {
2889d59c6e8SJonathan Richardson 		dev_err(dev, "size parameter not specified\n");
2899d59c6e8SJonathan Richardson 		return -EINVAL;
2909d59c6e8SJonathan Richardson 	} else if (num_words == 0) {
2919d59c6e8SJonathan Richardson 		dev_err(dev, "size must be > 0\n");
2929d59c6e8SJonathan Richardson 		return -EINVAL;
2939d59c6e8SJonathan Richardson 	}
2949d59c6e8SJonathan Richardson 
2959d59c6e8SJonathan Richardson 	bcm_otpc_nvmem_config.size = 4 * num_words;
2969d59c6e8SJonathan Richardson 	bcm_otpc_nvmem_config.dev = dev;
2979d59c6e8SJonathan Richardson 	bcm_otpc_nvmem_config.priv = priv;
2989d59c6e8SJonathan Richardson 
299*16941555SSrinath Mannam 	if (priv->map == &otp_map_v2) {
3009d59c6e8SJonathan Richardson 		bcm_otpc_nvmem_config.word_size = 8;
3019d59c6e8SJonathan Richardson 		bcm_otpc_nvmem_config.stride = 8;
3029d59c6e8SJonathan Richardson 	}
3039d59c6e8SJonathan Richardson 
3049d59c6e8SJonathan Richardson 	priv->config = &bcm_otpc_nvmem_config;
3059d59c6e8SJonathan Richardson 
306b2236dbdSAndrey Smirnov 	nvmem = devm_nvmem_register(dev, &bcm_otpc_nvmem_config);
3079d59c6e8SJonathan Richardson 	if (IS_ERR(nvmem)) {
3089d59c6e8SJonathan Richardson 		dev_err(dev, "error registering nvmem config\n");
3099d59c6e8SJonathan Richardson 		return PTR_ERR(nvmem);
3109d59c6e8SJonathan Richardson 	}
3119d59c6e8SJonathan Richardson 
3129d59c6e8SJonathan Richardson 	return 0;
3139d59c6e8SJonathan Richardson }
3149d59c6e8SJonathan Richardson 
3159d59c6e8SJonathan Richardson static struct platform_driver bcm_otpc_driver = {
3169d59c6e8SJonathan Richardson 	.probe	= bcm_otpc_probe,
3179d59c6e8SJonathan Richardson 	.driver = {
3189d59c6e8SJonathan Richardson 		.name	= "brcm-otpc",
3199d59c6e8SJonathan Richardson 		.of_match_table = bcm_otpc_dt_ids,
320*16941555SSrinath Mannam 		.acpi_match_table = ACPI_PTR(bcm_otpc_acpi_ids),
3219d59c6e8SJonathan Richardson 	},
3229d59c6e8SJonathan Richardson };
3239d59c6e8SJonathan Richardson module_platform_driver(bcm_otpc_driver);
3249d59c6e8SJonathan Richardson 
3259d59c6e8SJonathan Richardson MODULE_DESCRIPTION("Broadcom OTPC driver");
3269d59c6e8SJonathan Richardson MODULE_LICENSE("GPL v2");
327