brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · 56fc19f Raw
223 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/iopoll.h>4#include <linux/module.h>5#include <linux/nvmem-provider.h>6#include <linux/of.h>7#include <linux/platform_device.h>8 9#define OTP_OTP_PWR_DN(t)			(t + 0x00)10#define OTP_OTP_PWR_DN_OTP_PWRDN_N		BIT(0)11#define OTP_OTP_ADDR_HI(t)			(t + 0x04)12#define OTP_OTP_ADDR_LO(t)			(t + 0x08)13#define OTP_OTP_PRGM_DATA(t)			(t + 0x10)14#define OTP_OTP_PRGM_MODE(t)			(t + 0x14)15#define OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE	BIT(0)16#define OTP_OTP_RD_DATA(t)			(t + 0x18)17#define OTP_OTP_FUNC_CMD(t)			(t + 0x20)18#define OTP_OTP_FUNC_CMD_OTP_PROGRAM		BIT(1)19#define OTP_OTP_FUNC_CMD_OTP_READ		BIT(0)20#define OTP_OTP_CMD_GO(t)			(t + 0x28)21#define OTP_OTP_CMD_GO_OTP_GO			BIT(0)22#define OTP_OTP_PASS_FAIL(t)			(t + 0x2c)23#define OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED	BIT(3)24#define OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED	BIT(2)25#define OTP_OTP_PASS_FAIL_OTP_FAIL		BIT(0)26#define OTP_OTP_STATUS(t)			(t + 0x30)27#define OTP_OTP_STATUS_OTP_CPUMPEN		BIT(1)28#define OTP_OTP_STATUS_OTP_BUSY			BIT(0)29 30#define OTP_MEM_SIZE 819231#define OTP_SLEEP_US 1032#define OTP_TIMEOUT_US 50000033 34struct lan9662_otp {35	struct device *dev;36	void __iomem *base;37};38 39static int lan9662_otp_wait_flag_clear(void __iomem *reg, u32 flag)40{41	u32 val;42 43	return readl_poll_timeout(reg, val, !(val & flag),44				  OTP_SLEEP_US, OTP_TIMEOUT_US);45}46 47static int lan9662_otp_power(struct lan9662_otp *otp, bool up)48{49	void __iomem *pwrdn = OTP_OTP_PWR_DN(otp->base);50 51	if (up) {52		writel(readl(pwrdn) & ~OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn);53		if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base),54						OTP_OTP_STATUS_OTP_CPUMPEN))55			return -ETIMEDOUT;56	} else {57		writel(readl(pwrdn) | OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn);58	}59 60	return 0;61}62 63static int lan9662_otp_execute(struct lan9662_otp *otp)64{65	if (lan9662_otp_wait_flag_clear(OTP_OTP_CMD_GO(otp->base),66					OTP_OTP_CMD_GO_OTP_GO))67		return -ETIMEDOUT;68 69	if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base),70					OTP_OTP_STATUS_OTP_BUSY))71		return -ETIMEDOUT;72 73	return 0;74}75 76static void lan9662_otp_set_address(struct lan9662_otp *otp, u32 offset)77{78	writel(0xff & (offset >> 8), OTP_OTP_ADDR_HI(otp->base));79	writel(0xff & offset, OTP_OTP_ADDR_LO(otp->base));80}81 82static int lan9662_otp_read_byte(struct lan9662_otp *otp, u32 offset, u8 *dst)83{84	u32 pass;85	int rc;86 87	lan9662_otp_set_address(otp, offset);88	writel(OTP_OTP_FUNC_CMD_OTP_READ, OTP_OTP_FUNC_CMD(otp->base));89	writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base));90	rc = lan9662_otp_execute(otp);91	if (!rc) {92		pass = readl(OTP_OTP_PASS_FAIL(otp->base));93		if (pass & OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED)94			return -EACCES;95		*dst = (u8) readl(OTP_OTP_RD_DATA(otp->base));96	}97	return rc;98}99 100static int lan9662_otp_write_byte(struct lan9662_otp *otp, u32 offset, u8 data)101{102	u32 pass;103	int rc;104 105	lan9662_otp_set_address(otp, offset);106	writel(OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE, OTP_OTP_PRGM_MODE(otp->base));107	writel(data, OTP_OTP_PRGM_DATA(otp->base));108	writel(OTP_OTP_FUNC_CMD_OTP_PROGRAM, OTP_OTP_FUNC_CMD(otp->base));109	writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base));110 111	rc = lan9662_otp_execute(otp);112	if (!rc) {113		pass = readl(OTP_OTP_PASS_FAIL(otp->base));114		if (pass & OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED)115			return -EACCES;116		if (pass & OTP_OTP_PASS_FAIL_OTP_FAIL)117			return -EIO;118	}119	return rc;120}121 122static int lan9662_otp_read(void *context, unsigned int offset,123			    void *_val, size_t bytes)124{125	struct lan9662_otp *otp = context;126	u8 *val = _val;127	uint8_t data;128	int i, rc = 0;129 130	lan9662_otp_power(otp, true);131	for (i = 0; i < bytes; i++) {132		rc = lan9662_otp_read_byte(otp, offset + i, &data);133		if (rc < 0)134			break;135		*val++ = data;136	}137	lan9662_otp_power(otp, false);138 139	return rc;140}141 142static int lan9662_otp_write(void *context, unsigned int offset,143			     void *_val, size_t bytes)144{145	struct lan9662_otp *otp = context;146	u8 *val = _val;147	u8 data, newdata;148	int i, rc = 0;149 150	lan9662_otp_power(otp, true);151	for (i = 0; i < bytes; i++) {152		/* Skip zero bytes */153		if (val[i]) {154			rc = lan9662_otp_read_byte(otp, offset + i, &data);155			if (rc < 0)156				break;157 158			newdata = data | val[i];159			if (newdata == data)160				continue;161 162			rc = lan9662_otp_write_byte(otp, offset + i,163						      newdata);164			if (rc < 0)165				break;166		}167	}168	lan9662_otp_power(otp, false);169 170	return rc;171}172 173static struct nvmem_config otp_config = {174	.name = "lan9662-otp",175	.stride = 1,176	.word_size = 1,177	.reg_read = lan9662_otp_read,178	.reg_write = lan9662_otp_write,179	.size = OTP_MEM_SIZE,180};181 182static int lan9662_otp_probe(struct platform_device *pdev)183{184	struct device *dev = &pdev->dev;185	struct nvmem_device *nvmem;186	struct lan9662_otp *otp;187 188	otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL);189	if (!otp)190		return -ENOMEM;191 192	otp->dev = dev;193	otp->base = devm_platform_ioremap_resource(pdev, 0);194	if (IS_ERR(otp->base))195		return PTR_ERR(otp->base);196 197	otp_config.priv = otp;198	otp_config.dev = dev;199 200	nvmem = devm_nvmem_register(dev, &otp_config);201 202	return PTR_ERR_OR_ZERO(nvmem);203}204 205static const struct of_device_id lan9662_otp_match[] = {206	{ .compatible = "microchip,lan9662-otpc", },207	{ },208};209MODULE_DEVICE_TABLE(of, lan9662_otp_match);210 211static struct platform_driver lan9662_otp_driver = {212	.probe = lan9662_otp_probe,213	.driver = {214		.name = "lan9662-otp",215		.of_match_table = lan9662_otp_match,216	},217};218module_platform_driver(lan9662_otp_driver);219 220MODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>");221MODULE_DESCRIPTION("lan9662 OTP driver");222MODULE_LICENSE("GPL");223