162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/iopoll.h> 462306a36Sopenharmony_ci#include <linux/module.h> 562306a36Sopenharmony_ci#include <linux/nvmem-provider.h> 662306a36Sopenharmony_ci#include <linux/of.h> 762306a36Sopenharmony_ci#include <linux/platform_device.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define OTP_OTP_PWR_DN(t) (t + 0x00) 1062306a36Sopenharmony_ci#define OTP_OTP_PWR_DN_OTP_PWRDN_N BIT(0) 1162306a36Sopenharmony_ci#define OTP_OTP_ADDR_HI(t) (t + 0x04) 1262306a36Sopenharmony_ci#define OTP_OTP_ADDR_LO(t) (t + 0x08) 1362306a36Sopenharmony_ci#define OTP_OTP_PRGM_DATA(t) (t + 0x10) 1462306a36Sopenharmony_ci#define OTP_OTP_PRGM_MODE(t) (t + 0x14) 1562306a36Sopenharmony_ci#define OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE BIT(0) 1662306a36Sopenharmony_ci#define OTP_OTP_RD_DATA(t) (t + 0x18) 1762306a36Sopenharmony_ci#define OTP_OTP_FUNC_CMD(t) (t + 0x20) 1862306a36Sopenharmony_ci#define OTP_OTP_FUNC_CMD_OTP_PROGRAM BIT(1) 1962306a36Sopenharmony_ci#define OTP_OTP_FUNC_CMD_OTP_READ BIT(0) 2062306a36Sopenharmony_ci#define OTP_OTP_CMD_GO(t) (t + 0x28) 2162306a36Sopenharmony_ci#define OTP_OTP_CMD_GO_OTP_GO BIT(0) 2262306a36Sopenharmony_ci#define OTP_OTP_PASS_FAIL(t) (t + 0x2c) 2362306a36Sopenharmony_ci#define OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED BIT(3) 2462306a36Sopenharmony_ci#define OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED BIT(2) 2562306a36Sopenharmony_ci#define OTP_OTP_PASS_FAIL_OTP_FAIL BIT(0) 2662306a36Sopenharmony_ci#define OTP_OTP_STATUS(t) (t + 0x30) 2762306a36Sopenharmony_ci#define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1) 2862306a36Sopenharmony_ci#define OTP_OTP_STATUS_OTP_BUSY BIT(0) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define OTP_MEM_SIZE 8192 3162306a36Sopenharmony_ci#define OTP_SLEEP_US 10 3262306a36Sopenharmony_ci#define OTP_TIMEOUT_US 500000 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct lan9662_otp { 3562306a36Sopenharmony_ci struct device *dev; 3662306a36Sopenharmony_ci void __iomem *base; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int lan9662_otp_wait_flag_clear(void __iomem *reg, u32 flag) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci u32 val; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return readl_poll_timeout(reg, val, !(val & flag), 4462306a36Sopenharmony_ci OTP_SLEEP_US, OTP_TIMEOUT_US); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int lan9662_otp_power(struct lan9662_otp *otp, bool up) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci void __iomem *pwrdn = OTP_OTP_PWR_DN(otp->base); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (up) { 5262306a36Sopenharmony_ci writel(readl(pwrdn) & ~OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn); 5362306a36Sopenharmony_ci if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base), 5462306a36Sopenharmony_ci OTP_OTP_STATUS_OTP_CPUMPEN)) 5562306a36Sopenharmony_ci return -ETIMEDOUT; 5662306a36Sopenharmony_ci } else { 5762306a36Sopenharmony_ci writel(readl(pwrdn) | OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int lan9662_otp_execute(struct lan9662_otp *otp) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci if (lan9662_otp_wait_flag_clear(OTP_OTP_CMD_GO(otp->base), 6662306a36Sopenharmony_ci OTP_OTP_CMD_GO_OTP_GO)) 6762306a36Sopenharmony_ci return -ETIMEDOUT; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base), 7062306a36Sopenharmony_ci OTP_OTP_STATUS_OTP_BUSY)) 7162306a36Sopenharmony_ci return -ETIMEDOUT; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void lan9662_otp_set_address(struct lan9662_otp *otp, u32 offset) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci writel(0xff & (offset >> 8), OTP_OTP_ADDR_HI(otp->base)); 7962306a36Sopenharmony_ci writel(0xff & offset, OTP_OTP_ADDR_LO(otp->base)); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int lan9662_otp_read_byte(struct lan9662_otp *otp, u32 offset, u8 *dst) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci u32 pass; 8562306a36Sopenharmony_ci int rc; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci lan9662_otp_set_address(otp, offset); 8862306a36Sopenharmony_ci writel(OTP_OTP_FUNC_CMD_OTP_READ, OTP_OTP_FUNC_CMD(otp->base)); 8962306a36Sopenharmony_ci writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base)); 9062306a36Sopenharmony_ci rc = lan9662_otp_execute(otp); 9162306a36Sopenharmony_ci if (!rc) { 9262306a36Sopenharmony_ci pass = readl(OTP_OTP_PASS_FAIL(otp->base)); 9362306a36Sopenharmony_ci if (pass & OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED) 9462306a36Sopenharmony_ci return -EACCES; 9562306a36Sopenharmony_ci *dst = (u8) readl(OTP_OTP_RD_DATA(otp->base)); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci return rc; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int lan9662_otp_write_byte(struct lan9662_otp *otp, u32 offset, u8 data) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci u32 pass; 10362306a36Sopenharmony_ci int rc; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci lan9662_otp_set_address(otp, offset); 10662306a36Sopenharmony_ci writel(OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE, OTP_OTP_PRGM_MODE(otp->base)); 10762306a36Sopenharmony_ci writel(data, OTP_OTP_PRGM_DATA(otp->base)); 10862306a36Sopenharmony_ci writel(OTP_OTP_FUNC_CMD_OTP_PROGRAM, OTP_OTP_FUNC_CMD(otp->base)); 10962306a36Sopenharmony_ci writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base)); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci rc = lan9662_otp_execute(otp); 11262306a36Sopenharmony_ci if (!rc) { 11362306a36Sopenharmony_ci pass = readl(OTP_OTP_PASS_FAIL(otp->base)); 11462306a36Sopenharmony_ci if (pass & OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED) 11562306a36Sopenharmony_ci return -EACCES; 11662306a36Sopenharmony_ci if (pass & OTP_OTP_PASS_FAIL_OTP_FAIL) 11762306a36Sopenharmony_ci return -EIO; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return rc; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int lan9662_otp_read(void *context, unsigned int offset, 12362306a36Sopenharmony_ci void *_val, size_t bytes) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct lan9662_otp *otp = context; 12662306a36Sopenharmony_ci u8 *val = _val; 12762306a36Sopenharmony_ci uint8_t data; 12862306a36Sopenharmony_ci int i, rc = 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci lan9662_otp_power(otp, true); 13162306a36Sopenharmony_ci for (i = 0; i < bytes; i++) { 13262306a36Sopenharmony_ci rc = lan9662_otp_read_byte(otp, offset + i, &data); 13362306a36Sopenharmony_ci if (rc < 0) 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci *val++ = data; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci lan9662_otp_power(otp, false); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return rc; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int lan9662_otp_write(void *context, unsigned int offset, 14362306a36Sopenharmony_ci void *_val, size_t bytes) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct lan9662_otp *otp = context; 14662306a36Sopenharmony_ci u8 *val = _val; 14762306a36Sopenharmony_ci u8 data, newdata; 14862306a36Sopenharmony_ci int i, rc = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci lan9662_otp_power(otp, true); 15162306a36Sopenharmony_ci for (i = 0; i < bytes; i++) { 15262306a36Sopenharmony_ci /* Skip zero bytes */ 15362306a36Sopenharmony_ci if (val[i]) { 15462306a36Sopenharmony_ci rc = lan9662_otp_read_byte(otp, offset + i, &data); 15562306a36Sopenharmony_ci if (rc < 0) 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci newdata = data | val[i]; 15962306a36Sopenharmony_ci if (newdata == data) 16062306a36Sopenharmony_ci continue; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci rc = lan9662_otp_write_byte(otp, offset + i, 16362306a36Sopenharmony_ci newdata); 16462306a36Sopenharmony_ci if (rc < 0) 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci lan9662_otp_power(otp, false); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return rc; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic struct nvmem_config otp_config = { 17462306a36Sopenharmony_ci .name = "lan9662-otp", 17562306a36Sopenharmony_ci .stride = 1, 17662306a36Sopenharmony_ci .word_size = 1, 17762306a36Sopenharmony_ci .reg_read = lan9662_otp_read, 17862306a36Sopenharmony_ci .reg_write = lan9662_otp_write, 17962306a36Sopenharmony_ci .size = OTP_MEM_SIZE, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int lan9662_otp_probe(struct platform_device *pdev) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 18562306a36Sopenharmony_ci struct nvmem_device *nvmem; 18662306a36Sopenharmony_ci struct lan9662_otp *otp; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL); 18962306a36Sopenharmony_ci if (!otp) 19062306a36Sopenharmony_ci return -ENOMEM; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci otp->dev = dev; 19362306a36Sopenharmony_ci otp->base = devm_platform_ioremap_resource(pdev, 0); 19462306a36Sopenharmony_ci if (IS_ERR(otp->base)) 19562306a36Sopenharmony_ci return PTR_ERR(otp->base); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci otp_config.priv = otp; 19862306a36Sopenharmony_ci otp_config.dev = dev; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci nvmem = devm_nvmem_register(dev, &otp_config); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(nvmem); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic const struct of_device_id lan9662_otp_match[] = { 20662306a36Sopenharmony_ci { .compatible = "microchip,lan9662-otpc", }, 20762306a36Sopenharmony_ci { }, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, lan9662_otp_match); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic struct platform_driver lan9662_otp_driver = { 21262306a36Sopenharmony_ci .probe = lan9662_otp_probe, 21362306a36Sopenharmony_ci .driver = { 21462306a36Sopenharmony_ci .name = "lan9662-otp", 21562306a36Sopenharmony_ci .of_match_table = lan9662_otp_match, 21662306a36Sopenharmony_ci }, 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_cimodule_platform_driver(lan9662_otp_driver); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciMODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>"); 22162306a36Sopenharmony_ciMODULE_DESCRIPTION("lan9662 OTP driver"); 22262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 223