442 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (C) 2022-2023 Microchip Technology Inc.3// PCI1xxxx OTP/EEPROM driver4 5#include <linux/auxiliary_bus.h>6#include <linux/device.h>7#include <linux/iopoll.h>8#include <linux/module.h>9#include <linux/nvmem-provider.h>10 11#include "mchp_pci1xxxx_gp.h"12 13#define AUX_DRIVER_NAME "PCI1xxxxOTPE2P"14#define EEPROM_NAME "pci1xxxx_eeprom"15#define OTP_NAME "pci1xxxx_otp"16 17#define PERI_PF3_SYSTEM_REG_ADDR_BASE 0x200018#define PERI_PF3_SYSTEM_REG_LENGTH 0x400019 20#define EEPROM_SIZE_BYTES 819221#define OTP_SIZE_BYTES 819222 23#define CONFIG_REG_ADDR_BASE 024#define EEPROM_REG_ADDR_BASE 0x0E0025#define OTP_REG_ADDR_BASE 0x100026 27#define MMAP_OTP_OFFSET(x) (OTP_REG_ADDR_BASE + (x))28#define MMAP_EEPROM_OFFSET(x) (EEPROM_REG_ADDR_BASE + (x))29#define MMAP_CFG_OFFSET(x) (CONFIG_REG_ADDR_BASE + (x))30 31#define EEPROM_CMD_REG 0x0032#define EEPROM_DATA_REG 0x0433 34#define EEPROM_CMD_EPC_WRITE (BIT(29) | BIT(28))35#define EEPROM_CMD_EPC_TIMEOUT_BIT BIT(17)36#define EEPROM_CMD_EPC_BUSY_BIT BIT(31)37 38#define STATUS_READ_DELAY_US 139#define STATUS_READ_TIMEOUT_US 2000040 41#define OTP_ADDR_HIGH_OFFSET 0x0442#define OTP_ADDR_LOW_OFFSET 0x0843#define OTP_PRGM_DATA_OFFSET 0x1044#define OTP_PRGM_MODE_OFFSET 0x1445#define OTP_RD_DATA_OFFSET 0x1846#define OTP_FUNC_CMD_OFFSET 0x2047#define OTP_CMD_GO_OFFSET 0x2848#define OTP_PASS_FAIL_OFFSET 0x2C49#define OTP_STATUS_OFFSET 0x3050 51#define OTP_FUNC_RD_BIT BIT(0)52#define OTP_FUNC_PGM_BIT BIT(1)53#define OTP_CMD_GO_BIT BIT(0)54#define OTP_STATUS_BUSY_BIT BIT(0)55#define OTP_PGM_MODE_BYTE_BIT BIT(0)56#define OTP_FAIL_BIT BIT(0)57 58#define OTP_PWR_DN_BIT BIT(0)59#define OTP_PWR_DN_OFFSET 0x0060 61#define CFG_SYS_LOCK_OFFSET 0xA062#define CFG_SYS_LOCK_PF3 BIT(5)63 64#define BYTE_LOW (GENMASK(7, 0))65#define BYTE_HIGH (GENMASK(12, 8))66 67struct pci1xxxx_otp_eeprom_device {68 struct auxiliary_device *pdev;69 void __iomem *reg_base;70 struct nvmem_config nvmem_config_eeprom;71 struct nvmem_device *nvmem_eeprom;72 struct nvmem_config nvmem_config_otp;73 struct nvmem_device *nvmem_otp;74};75 76static int set_sys_lock(struct pci1xxxx_otp_eeprom_device *priv)77{78 void __iomem *sys_lock = priv->reg_base +79 MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);80 u8 data;81 82 writel(CFG_SYS_LOCK_PF3, sys_lock);83 data = readl(sys_lock);84 if (data != CFG_SYS_LOCK_PF3)85 return -EPERM;86 87 return 0;88}89 90static void release_sys_lock(struct pci1xxxx_otp_eeprom_device *priv)91{92 void __iomem *sys_lock = priv->reg_base +93 MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);94 writel(0, sys_lock);95}96 97static bool is_eeprom_responsive(struct pci1xxxx_otp_eeprom_device *priv)98{99 void __iomem *rb = priv->reg_base;100 u32 regval;101 int ret;102 103 writel(EEPROM_CMD_EPC_TIMEOUT_BIT,104 rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));105 writel(EEPROM_CMD_EPC_BUSY_BIT,106 rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));107 108 /* Wait for the EPC_BUSY bit to get cleared or timeout bit to get set*/109 ret = read_poll_timeout(readl, regval, !(regval & EEPROM_CMD_EPC_BUSY_BIT),110 STATUS_READ_DELAY_US, STATUS_READ_TIMEOUT_US,111 true, rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));112 113 /* Return failure if either of software or hardware timeouts happen */114 if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT)))115 return false;116 117 return true;118}119 120static int pci1xxxx_eeprom_read(void *priv_t, unsigned int off,121 void *buf_t, size_t count)122{123 struct pci1xxxx_otp_eeprom_device *priv = priv_t;124 void __iomem *rb = priv->reg_base;125 char *buf = buf_t;126 u32 regval;127 u32 byte;128 int ret;129 130 if (off >= priv->nvmem_config_eeprom.size)131 return -EFAULT;132 133 if ((off + count) > priv->nvmem_config_eeprom.size)134 count = priv->nvmem_config_eeprom.size - off;135 136 ret = set_sys_lock(priv);137 if (ret)138 return ret;139 140 for (byte = 0; byte < count; byte++) {141 writel(EEPROM_CMD_EPC_BUSY_BIT | (off + byte), rb +142 MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));143 144 ret = read_poll_timeout(readl, regval,145 !(regval & EEPROM_CMD_EPC_BUSY_BIT),146 STATUS_READ_DELAY_US,147 STATUS_READ_TIMEOUT_US, true,148 rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));149 if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {150 ret = -EIO;151 goto error;152 }153 154 buf[byte] = readl(rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));155 }156error:157 release_sys_lock(priv);158 return ret;159}160 161static int pci1xxxx_eeprom_write(void *priv_t, unsigned int off,162 void *value_t, size_t count)163{164 struct pci1xxxx_otp_eeprom_device *priv = priv_t;165 void __iomem *rb = priv->reg_base;166 char *value = value_t;167 u32 regval;168 u32 byte;169 int ret;170 171 if (off >= priv->nvmem_config_eeprom.size)172 return -EFAULT;173 174 if ((off + count) > priv->nvmem_config_eeprom.size)175 count = priv->nvmem_config_eeprom.size - off;176 177 ret = set_sys_lock(priv);178 if (ret)179 return ret;180 181 for (byte = 0; byte < count; byte++) {182 writel(*(value + byte), rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));183 regval = EEPROM_CMD_EPC_TIMEOUT_BIT | EEPROM_CMD_EPC_WRITE |184 (off + byte);185 writel(regval, rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));186 writel(EEPROM_CMD_EPC_BUSY_BIT | regval,187 rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));188 189 ret = read_poll_timeout(readl, regval,190 !(regval & EEPROM_CMD_EPC_BUSY_BIT),191 STATUS_READ_DELAY_US,192 STATUS_READ_TIMEOUT_US, true,193 rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));194 if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {195 ret = -EIO;196 goto error;197 }198 }199error:200 release_sys_lock(priv);201 return ret;202}203 204static void otp_device_set_address(struct pci1xxxx_otp_eeprom_device *priv,205 u16 address)206{207 u16 lo, hi;208 209 lo = address & BYTE_LOW;210 hi = (address & BYTE_HIGH) >> 8;211 writew(lo, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_LOW_OFFSET));212 writew(hi, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_HIGH_OFFSET));213}214 215static int pci1xxxx_otp_read(void *priv_t, unsigned int off,216 void *buf_t, size_t count)217{218 struct pci1xxxx_otp_eeprom_device *priv = priv_t;219 void __iomem *rb = priv->reg_base;220 char *buf = buf_t;221 u32 regval;222 u32 byte;223 int ret;224 u8 data;225 226 if (off >= priv->nvmem_config_otp.size)227 return -EFAULT;228 229 if ((off + count) > priv->nvmem_config_otp.size)230 count = priv->nvmem_config_otp.size - off;231 232 ret = set_sys_lock(priv);233 if (ret)234 return ret;235 236 for (byte = 0; byte < count; byte++) {237 otp_device_set_address(priv, (u16)(off + byte));238 data = readl(rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));239 writel(data | OTP_FUNC_RD_BIT,240 rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));241 data = readl(rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));242 writel(data | OTP_CMD_GO_BIT,243 rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));244 245 ret = read_poll_timeout(readl, regval,246 !(regval & OTP_STATUS_BUSY_BIT),247 STATUS_READ_DELAY_US,248 STATUS_READ_TIMEOUT_US, true,249 rb + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));250 251 data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));252 if (ret < 0 || data & OTP_FAIL_BIT) {253 ret = -EIO;254 goto error;255 }256 257 buf[byte] = readl(rb + MMAP_OTP_OFFSET(OTP_RD_DATA_OFFSET));258 }259error:260 release_sys_lock(priv);261 return ret;262}263 264static int pci1xxxx_otp_write(void *priv_t, unsigned int off,265 void *value_t, size_t count)266{267 struct pci1xxxx_otp_eeprom_device *priv = priv_t;268 void __iomem *rb = priv->reg_base;269 char *value = value_t;270 u32 regval;271 u32 byte;272 int ret;273 u8 data;274 275 if (off >= priv->nvmem_config_otp.size)276 return -EFAULT;277 278 if ((off + count) > priv->nvmem_config_otp.size)279 count = priv->nvmem_config_otp.size - off;280 281 ret = set_sys_lock(priv);282 if (ret)283 return ret;284 285 for (byte = 0; byte < count; byte++) {286 otp_device_set_address(priv, (u16)(off + byte));287 288 /*289 * Set OTP_PGM_MODE_BYTE command bit in OTP_PRGM_MODE register290 * to enable Byte programming291 */292 data = readl(rb + MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));293 writel(data | OTP_PGM_MODE_BYTE_BIT,294 rb + MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));295 writel(*(value + byte), rb + MMAP_OTP_OFFSET(OTP_PRGM_DATA_OFFSET));296 data = readl(rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));297 writel(data | OTP_FUNC_PGM_BIT,298 rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));299 data = readl(rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));300 writel(data | OTP_CMD_GO_BIT,301 rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));302 303 ret = read_poll_timeout(readl, regval,304 !(regval & OTP_STATUS_BUSY_BIT),305 STATUS_READ_DELAY_US,306 STATUS_READ_TIMEOUT_US, true,307 rb + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));308 309 data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));310 if (ret < 0 || data & OTP_FAIL_BIT) {311 ret = -EIO;312 goto error;313 }314 }315error:316 release_sys_lock(priv);317 return ret;318}319 320static int pci1xxxx_otp_eeprom_probe(struct auxiliary_device *aux_dev,321 const struct auxiliary_device_id *id)322{323 struct auxiliary_device_wrapper *aux_dev_wrapper;324 struct pci1xxxx_otp_eeprom_device *priv;325 struct gp_aux_data_type *pdata;326 int ret;327 u8 data;328 329 aux_dev_wrapper = container_of(aux_dev, struct auxiliary_device_wrapper,330 aux_dev);331 pdata = &aux_dev_wrapper->gp_aux_data;332 if (!pdata)333 return -EINVAL;334 335 priv = devm_kzalloc(&aux_dev->dev, sizeof(*priv), GFP_KERNEL);336 if (!priv)337 return -ENOMEM;338 339 priv->pdev = aux_dev;340 341 if (!devm_request_mem_region(&aux_dev->dev, pdata->region_start +342 PERI_PF3_SYSTEM_REG_ADDR_BASE,343 PERI_PF3_SYSTEM_REG_LENGTH,344 aux_dev->name))345 return -ENOMEM;346 347 priv->reg_base = devm_ioremap(&aux_dev->dev, pdata->region_start +348 PERI_PF3_SYSTEM_REG_ADDR_BASE,349 PERI_PF3_SYSTEM_REG_LENGTH);350 if (!priv->reg_base)351 return -ENOMEM;352 353 ret = set_sys_lock(priv);354 if (ret)355 return ret;356 357 /* Set OTP_PWR_DN to 0 to make OTP Operational */358 data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));359 writel(data & ~OTP_PWR_DN_BIT,360 priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));361 362 dev_set_drvdata(&aux_dev->dev, priv);363 364 if (is_eeprom_responsive(priv)) {365 priv->nvmem_config_eeprom.type = NVMEM_TYPE_EEPROM;366 priv->nvmem_config_eeprom.name = EEPROM_NAME;367 priv->nvmem_config_eeprom.id = NVMEM_DEVID_AUTO;368 priv->nvmem_config_eeprom.dev = &aux_dev->dev;369 priv->nvmem_config_eeprom.owner = THIS_MODULE;370 priv->nvmem_config_eeprom.reg_read = pci1xxxx_eeprom_read;371 priv->nvmem_config_eeprom.reg_write = pci1xxxx_eeprom_write;372 priv->nvmem_config_eeprom.priv = priv;373 priv->nvmem_config_eeprom.stride = 1;374 priv->nvmem_config_eeprom.word_size = 1;375 priv->nvmem_config_eeprom.size = EEPROM_SIZE_BYTES;376 377 priv->nvmem_eeprom = devm_nvmem_register(&aux_dev->dev,378 &priv->nvmem_config_eeprom);379 if (IS_ERR(priv->nvmem_eeprom))380 return PTR_ERR(priv->nvmem_eeprom);381 }382 383 release_sys_lock(priv);384 385 priv->nvmem_config_otp.type = NVMEM_TYPE_OTP;386 priv->nvmem_config_otp.name = OTP_NAME;387 priv->nvmem_config_otp.id = NVMEM_DEVID_AUTO;388 priv->nvmem_config_otp.dev = &aux_dev->dev;389 priv->nvmem_config_otp.owner = THIS_MODULE;390 priv->nvmem_config_otp.reg_read = pci1xxxx_otp_read;391 priv->nvmem_config_otp.reg_write = pci1xxxx_otp_write;392 priv->nvmem_config_otp.priv = priv;393 priv->nvmem_config_otp.stride = 1;394 priv->nvmem_config_otp.word_size = 1;395 priv->nvmem_config_otp.size = OTP_SIZE_BYTES;396 397 priv->nvmem_otp = devm_nvmem_register(&aux_dev->dev,398 &priv->nvmem_config_otp);399 if (IS_ERR(priv->nvmem_otp))400 return PTR_ERR(priv->nvmem_otp);401 402 return ret;403}404 405static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev)406{407 struct pci1xxxx_otp_eeprom_device *priv;408 void __iomem *sys_lock;409 410 priv = dev_get_drvdata(&aux_dev->dev);411 sys_lock = priv->reg_base + MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);412 writel(CFG_SYS_LOCK_PF3, sys_lock);413 414 /* Shut down OTP */415 writel(OTP_PWR_DN_BIT,416 priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));417 418 writel(0, sys_lock);419}420 421static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = {422 {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"},423 {},424};425MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table);426 427static struct auxiliary_driver pci1xxxx_otp_eeprom_driver = {428 .driver = {429 .name = AUX_DRIVER_NAME,430 },431 .probe = pci1xxxx_otp_eeprom_probe,432 .remove = pci1xxxx_otp_eeprom_remove,433 .id_table = pci1xxxx_otp_eeprom_auxiliary_id_table434};435module_auxiliary_driver(pci1xxxx_otp_eeprom_driver);436 437MODULE_LICENSE("GPL");438MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");439MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>");440MODULE_AUTHOR("Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com>");441MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx OTP EEPROM Programmer");442