109690015SKumaravel Thiagarajan // SPDX-License-Identifier: GPL-2.0
209690015SKumaravel Thiagarajan // Copyright (C) 2022-2023 Microchip Technology Inc.
309690015SKumaravel Thiagarajan // PCI1xxxx OTP/EEPROM driver
409690015SKumaravel Thiagarajan
509690015SKumaravel Thiagarajan #include <linux/auxiliary_bus.h>
609690015SKumaravel Thiagarajan #include <linux/device.h>
709690015SKumaravel Thiagarajan #include <linux/iopoll.h>
809690015SKumaravel Thiagarajan #include <linux/module.h>
909690015SKumaravel Thiagarajan #include <linux/nvmem-provider.h>
1009690015SKumaravel Thiagarajan
1109690015SKumaravel Thiagarajan #include "mchp_pci1xxxx_gp.h"
1209690015SKumaravel Thiagarajan
1309690015SKumaravel Thiagarajan #define AUX_DRIVER_NAME "PCI1xxxxOTPE2P"
149ab54653SKumaravel Thiagarajan #define EEPROM_NAME "pci1xxxx_eeprom"
1509690015SKumaravel Thiagarajan #define OTP_NAME "pci1xxxx_otp"
1609690015SKumaravel Thiagarajan
1709690015SKumaravel Thiagarajan #define PERI_PF3_SYSTEM_REG_ADDR_BASE 0x2000
1809690015SKumaravel Thiagarajan #define PERI_PF3_SYSTEM_REG_LENGTH 0x4000
1909690015SKumaravel Thiagarajan
209ab54653SKumaravel Thiagarajan #define EEPROM_SIZE_BYTES 8192
2109690015SKumaravel Thiagarajan #define OTP_SIZE_BYTES 8192
2209690015SKumaravel Thiagarajan
2309690015SKumaravel Thiagarajan #define CONFIG_REG_ADDR_BASE 0
249ab54653SKumaravel Thiagarajan #define EEPROM_REG_ADDR_BASE 0x0E00
2509690015SKumaravel Thiagarajan #define OTP_REG_ADDR_BASE 0x1000
2609690015SKumaravel Thiagarajan
2709690015SKumaravel Thiagarajan #define MMAP_OTP_OFFSET(x) (OTP_REG_ADDR_BASE + (x))
289ab54653SKumaravel Thiagarajan #define MMAP_EEPROM_OFFSET(x) (EEPROM_REG_ADDR_BASE + (x))
2909690015SKumaravel Thiagarajan #define MMAP_CFG_OFFSET(x) (CONFIG_REG_ADDR_BASE + (x))
3009690015SKumaravel Thiagarajan
319ab54653SKumaravel Thiagarajan #define EEPROM_CMD_REG 0x00
329ab54653SKumaravel Thiagarajan #define EEPROM_DATA_REG 0x04
339ab54653SKumaravel Thiagarajan
349ab54653SKumaravel Thiagarajan #define EEPROM_CMD_EPC_WRITE (BIT(29) | BIT(28))
359ab54653SKumaravel Thiagarajan #define EEPROM_CMD_EPC_TIMEOUT_BIT BIT(17)
369ab54653SKumaravel Thiagarajan #define EEPROM_CMD_EPC_BUSY_BIT BIT(31)
379ab54653SKumaravel Thiagarajan
3809690015SKumaravel Thiagarajan #define STATUS_READ_DELAY_US 1
3909690015SKumaravel Thiagarajan #define STATUS_READ_TIMEOUT_US 20000
4009690015SKumaravel Thiagarajan
4109690015SKumaravel Thiagarajan #define OTP_ADDR_HIGH_OFFSET 0x04
4209690015SKumaravel Thiagarajan #define OTP_ADDR_LOW_OFFSET 0x08
4309690015SKumaravel Thiagarajan #define OTP_PRGM_DATA_OFFSET 0x10
4409690015SKumaravel Thiagarajan #define OTP_PRGM_MODE_OFFSET 0x14
4509690015SKumaravel Thiagarajan #define OTP_RD_DATA_OFFSET 0x18
4609690015SKumaravel Thiagarajan #define OTP_FUNC_CMD_OFFSET 0x20
4709690015SKumaravel Thiagarajan #define OTP_CMD_GO_OFFSET 0x28
4809690015SKumaravel Thiagarajan #define OTP_PASS_FAIL_OFFSET 0x2C
4909690015SKumaravel Thiagarajan #define OTP_STATUS_OFFSET 0x30
5009690015SKumaravel Thiagarajan
5109690015SKumaravel Thiagarajan #define OTP_FUNC_RD_BIT BIT(0)
5209690015SKumaravel Thiagarajan #define OTP_FUNC_PGM_BIT BIT(1)
5309690015SKumaravel Thiagarajan #define OTP_CMD_GO_BIT BIT(0)
5409690015SKumaravel Thiagarajan #define OTP_STATUS_BUSY_BIT BIT(0)
5509690015SKumaravel Thiagarajan #define OTP_PGM_MODE_BYTE_BIT BIT(0)
5609690015SKumaravel Thiagarajan #define OTP_FAIL_BIT BIT(0)
5709690015SKumaravel Thiagarajan
5809690015SKumaravel Thiagarajan #define OTP_PWR_DN_BIT BIT(0)
5909690015SKumaravel Thiagarajan #define OTP_PWR_DN_OFFSET 0x00
6009690015SKumaravel Thiagarajan
6109690015SKumaravel Thiagarajan #define CFG_SYS_LOCK_OFFSET 0xA0
6209690015SKumaravel Thiagarajan #define CFG_SYS_LOCK_PF3 BIT(5)
6309690015SKumaravel Thiagarajan
6409690015SKumaravel Thiagarajan #define BYTE_LOW (GENMASK(7, 0))
6509690015SKumaravel Thiagarajan #define BYTE_HIGH (GENMASK(12, 8))
6609690015SKumaravel Thiagarajan
6709690015SKumaravel Thiagarajan struct pci1xxxx_otp_eeprom_device {
6809690015SKumaravel Thiagarajan struct auxiliary_device *pdev;
6909690015SKumaravel Thiagarajan void __iomem *reg_base;
709ab54653SKumaravel Thiagarajan struct nvmem_config nvmem_config_eeprom;
719ab54653SKumaravel Thiagarajan struct nvmem_device *nvmem_eeprom;
7209690015SKumaravel Thiagarajan struct nvmem_config nvmem_config_otp;
7309690015SKumaravel Thiagarajan struct nvmem_device *nvmem_otp;
7409690015SKumaravel Thiagarajan };
7509690015SKumaravel Thiagarajan
set_sys_lock(struct pci1xxxx_otp_eeprom_device * priv)7609690015SKumaravel Thiagarajan static int set_sys_lock(struct pci1xxxx_otp_eeprom_device *priv)
7709690015SKumaravel Thiagarajan {
7809690015SKumaravel Thiagarajan void __iomem *sys_lock = priv->reg_base +
7909690015SKumaravel Thiagarajan MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
8009690015SKumaravel Thiagarajan u8 data;
8109690015SKumaravel Thiagarajan
8209690015SKumaravel Thiagarajan writel(CFG_SYS_LOCK_PF3, sys_lock);
8309690015SKumaravel Thiagarajan data = readl(sys_lock);
8409690015SKumaravel Thiagarajan if (data != CFG_SYS_LOCK_PF3)
8509690015SKumaravel Thiagarajan return -EPERM;
8609690015SKumaravel Thiagarajan
8709690015SKumaravel Thiagarajan return 0;
8809690015SKumaravel Thiagarajan }
8909690015SKumaravel Thiagarajan
release_sys_lock(struct pci1xxxx_otp_eeprom_device * priv)9009690015SKumaravel Thiagarajan static void release_sys_lock(struct pci1xxxx_otp_eeprom_device *priv)
9109690015SKumaravel Thiagarajan {
9209690015SKumaravel Thiagarajan void __iomem *sys_lock = priv->reg_base +
9309690015SKumaravel Thiagarajan MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
9409690015SKumaravel Thiagarajan writel(0, sys_lock);
9509690015SKumaravel Thiagarajan }
9609690015SKumaravel Thiagarajan
is_eeprom_responsive(struct pci1xxxx_otp_eeprom_device * priv)979ab54653SKumaravel Thiagarajan static bool is_eeprom_responsive(struct pci1xxxx_otp_eeprom_device *priv)
989ab54653SKumaravel Thiagarajan {
999ab54653SKumaravel Thiagarajan void __iomem *rb = priv->reg_base;
1009ab54653SKumaravel Thiagarajan u32 regval;
1019ab54653SKumaravel Thiagarajan int ret;
1029ab54653SKumaravel Thiagarajan
1039ab54653SKumaravel Thiagarajan writel(EEPROM_CMD_EPC_TIMEOUT_BIT,
1049ab54653SKumaravel Thiagarajan rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1059ab54653SKumaravel Thiagarajan writel(EEPROM_CMD_EPC_BUSY_BIT,
1069ab54653SKumaravel Thiagarajan rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1079ab54653SKumaravel Thiagarajan
1089ab54653SKumaravel Thiagarajan /* Wait for the EPC_BUSY bit to get cleared or timeout bit to get set*/
1099ab54653SKumaravel Thiagarajan ret = read_poll_timeout(readl, regval, !(regval & EEPROM_CMD_EPC_BUSY_BIT),
1109ab54653SKumaravel Thiagarajan STATUS_READ_DELAY_US, STATUS_READ_TIMEOUT_US,
1119ab54653SKumaravel Thiagarajan true, rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1129ab54653SKumaravel Thiagarajan
1139ab54653SKumaravel Thiagarajan /* Return failure if either of software or hardware timeouts happen */
1149ab54653SKumaravel Thiagarajan if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT)))
1159ab54653SKumaravel Thiagarajan return false;
1169ab54653SKumaravel Thiagarajan
1179ab54653SKumaravel Thiagarajan return true;
1189ab54653SKumaravel Thiagarajan }
1199ab54653SKumaravel Thiagarajan
pci1xxxx_eeprom_read(void * priv_t,unsigned int off,void * buf_t,size_t count)1209ab54653SKumaravel Thiagarajan static int pci1xxxx_eeprom_read(void *priv_t, unsigned int off,
1219ab54653SKumaravel Thiagarajan void *buf_t, size_t count)
1229ab54653SKumaravel Thiagarajan {
1239ab54653SKumaravel Thiagarajan struct pci1xxxx_otp_eeprom_device *priv = priv_t;
1249ab54653SKumaravel Thiagarajan void __iomem *rb = priv->reg_base;
1259ab54653SKumaravel Thiagarajan char *buf = buf_t;
1269ab54653SKumaravel Thiagarajan u32 regval;
1279ab54653SKumaravel Thiagarajan u32 byte;
1289ab54653SKumaravel Thiagarajan int ret;
1299ab54653SKumaravel Thiagarajan
1309ab54653SKumaravel Thiagarajan if (off >= priv->nvmem_config_eeprom.size)
1319ab54653SKumaravel Thiagarajan return -EFAULT;
1329ab54653SKumaravel Thiagarajan
1339ab54653SKumaravel Thiagarajan if ((off + count) > priv->nvmem_config_eeprom.size)
1349ab54653SKumaravel Thiagarajan count = priv->nvmem_config_eeprom.size - off;
1359ab54653SKumaravel Thiagarajan
1369ab54653SKumaravel Thiagarajan ret = set_sys_lock(priv);
1379ab54653SKumaravel Thiagarajan if (ret)
1389ab54653SKumaravel Thiagarajan return ret;
1399ab54653SKumaravel Thiagarajan
1409ab54653SKumaravel Thiagarajan for (byte = 0; byte < count; byte++) {
1419ab54653SKumaravel Thiagarajan writel(EEPROM_CMD_EPC_BUSY_BIT | (off + byte), rb +
1429ab54653SKumaravel Thiagarajan MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1439ab54653SKumaravel Thiagarajan
1449ab54653SKumaravel Thiagarajan ret = read_poll_timeout(readl, regval,
1459ab54653SKumaravel Thiagarajan !(regval & EEPROM_CMD_EPC_BUSY_BIT),
1469ab54653SKumaravel Thiagarajan STATUS_READ_DELAY_US,
1479ab54653SKumaravel Thiagarajan STATUS_READ_TIMEOUT_US, true,
1489ab54653SKumaravel Thiagarajan rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1499ab54653SKumaravel Thiagarajan if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {
1509ab54653SKumaravel Thiagarajan ret = -EIO;
1519ab54653SKumaravel Thiagarajan goto error;
1529ab54653SKumaravel Thiagarajan }
1539ab54653SKumaravel Thiagarajan
1549ab54653SKumaravel Thiagarajan buf[byte] = readl(rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));
1559ab54653SKumaravel Thiagarajan }
1569ab54653SKumaravel Thiagarajan error:
1579ab54653SKumaravel Thiagarajan release_sys_lock(priv);
1589ab54653SKumaravel Thiagarajan return ret;
1599ab54653SKumaravel Thiagarajan }
1609ab54653SKumaravel Thiagarajan
pci1xxxx_eeprom_write(void * priv_t,unsigned int off,void * value_t,size_t count)1619ab54653SKumaravel Thiagarajan static int pci1xxxx_eeprom_write(void *priv_t, unsigned int off,
1629ab54653SKumaravel Thiagarajan void *value_t, size_t count)
1639ab54653SKumaravel Thiagarajan {
1649ab54653SKumaravel Thiagarajan struct pci1xxxx_otp_eeprom_device *priv = priv_t;
1659ab54653SKumaravel Thiagarajan void __iomem *rb = priv->reg_base;
1669ab54653SKumaravel Thiagarajan char *value = value_t;
1679ab54653SKumaravel Thiagarajan u32 regval;
1689ab54653SKumaravel Thiagarajan u32 byte;
1699ab54653SKumaravel Thiagarajan int ret;
1709ab54653SKumaravel Thiagarajan
1719ab54653SKumaravel Thiagarajan if (off >= priv->nvmem_config_eeprom.size)
1729ab54653SKumaravel Thiagarajan return -EFAULT;
1739ab54653SKumaravel Thiagarajan
1749ab54653SKumaravel Thiagarajan if ((off + count) > priv->nvmem_config_eeprom.size)
1759ab54653SKumaravel Thiagarajan count = priv->nvmem_config_eeprom.size - off;
1769ab54653SKumaravel Thiagarajan
1779ab54653SKumaravel Thiagarajan ret = set_sys_lock(priv);
1789ab54653SKumaravel Thiagarajan if (ret)
1799ab54653SKumaravel Thiagarajan return ret;
1809ab54653SKumaravel Thiagarajan
1819ab54653SKumaravel Thiagarajan for (byte = 0; byte < count; byte++) {
1829ab54653SKumaravel Thiagarajan writel(*(value + byte), rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));
1839ab54653SKumaravel Thiagarajan regval = EEPROM_CMD_EPC_TIMEOUT_BIT | EEPROM_CMD_EPC_WRITE |
1849ab54653SKumaravel Thiagarajan (off + byte);
1859ab54653SKumaravel Thiagarajan writel(regval, rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1869ab54653SKumaravel Thiagarajan writel(EEPROM_CMD_EPC_BUSY_BIT | regval,
1879ab54653SKumaravel Thiagarajan rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1889ab54653SKumaravel Thiagarajan
1899ab54653SKumaravel Thiagarajan ret = read_poll_timeout(readl, regval,
1909ab54653SKumaravel Thiagarajan !(regval & EEPROM_CMD_EPC_BUSY_BIT),
1919ab54653SKumaravel Thiagarajan STATUS_READ_DELAY_US,
1929ab54653SKumaravel Thiagarajan STATUS_READ_TIMEOUT_US, true,
1939ab54653SKumaravel Thiagarajan rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
1949ab54653SKumaravel Thiagarajan if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {
1959ab54653SKumaravel Thiagarajan ret = -EIO;
1969ab54653SKumaravel Thiagarajan goto error;
1979ab54653SKumaravel Thiagarajan }
1989ab54653SKumaravel Thiagarajan }
1999ab54653SKumaravel Thiagarajan error:
2009ab54653SKumaravel Thiagarajan release_sys_lock(priv);
2019ab54653SKumaravel Thiagarajan return ret;
2029ab54653SKumaravel Thiagarajan }
2039ab54653SKumaravel Thiagarajan
otp_device_set_address(struct pci1xxxx_otp_eeprom_device * priv,u16 address)20409690015SKumaravel Thiagarajan static void otp_device_set_address(struct pci1xxxx_otp_eeprom_device *priv,
20509690015SKumaravel Thiagarajan u16 address)
20609690015SKumaravel Thiagarajan {
20709690015SKumaravel Thiagarajan u16 lo, hi;
20809690015SKumaravel Thiagarajan
20909690015SKumaravel Thiagarajan lo = address & BYTE_LOW;
21009690015SKumaravel Thiagarajan hi = (address & BYTE_HIGH) >> 8;
21109690015SKumaravel Thiagarajan writew(lo, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_LOW_OFFSET));
21209690015SKumaravel Thiagarajan writew(hi, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_HIGH_OFFSET));
21309690015SKumaravel Thiagarajan }
21409690015SKumaravel Thiagarajan
pci1xxxx_otp_read(void * priv_t,unsigned int off,void * buf_t,size_t count)21509690015SKumaravel Thiagarajan static int pci1xxxx_otp_read(void *priv_t, unsigned int off,
21609690015SKumaravel Thiagarajan void *buf_t, size_t count)
21709690015SKumaravel Thiagarajan {
21809690015SKumaravel Thiagarajan struct pci1xxxx_otp_eeprom_device *priv = priv_t;
21909690015SKumaravel Thiagarajan void __iomem *rb = priv->reg_base;
22009690015SKumaravel Thiagarajan char *buf = buf_t;
22109690015SKumaravel Thiagarajan u32 regval;
22209690015SKumaravel Thiagarajan u32 byte;
22309690015SKumaravel Thiagarajan int ret;
22409690015SKumaravel Thiagarajan u8 data;
22509690015SKumaravel Thiagarajan
22609690015SKumaravel Thiagarajan if (off >= priv->nvmem_config_otp.size)
22709690015SKumaravel Thiagarajan return -EFAULT;
22809690015SKumaravel Thiagarajan
22909690015SKumaravel Thiagarajan if ((off + count) > priv->nvmem_config_otp.size)
23009690015SKumaravel Thiagarajan count = priv->nvmem_config_otp.size - off;
23109690015SKumaravel Thiagarajan
23209690015SKumaravel Thiagarajan ret = set_sys_lock(priv);
23309690015SKumaravel Thiagarajan if (ret)
23409690015SKumaravel Thiagarajan return ret;
23509690015SKumaravel Thiagarajan
23609690015SKumaravel Thiagarajan for (byte = 0; byte < count; byte++) {
23709690015SKumaravel Thiagarajan otp_device_set_address(priv, (u16)(off + byte));
23809690015SKumaravel Thiagarajan data = readl(rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
23909690015SKumaravel Thiagarajan writel(data | OTP_FUNC_RD_BIT,
24009690015SKumaravel Thiagarajan rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
24109690015SKumaravel Thiagarajan data = readl(rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
24209690015SKumaravel Thiagarajan writel(data | OTP_CMD_GO_BIT,
24309690015SKumaravel Thiagarajan rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
24409690015SKumaravel Thiagarajan
24509690015SKumaravel Thiagarajan ret = read_poll_timeout(readl, regval,
24609690015SKumaravel Thiagarajan !(regval & OTP_STATUS_BUSY_BIT),
24709690015SKumaravel Thiagarajan STATUS_READ_DELAY_US,
24809690015SKumaravel Thiagarajan STATUS_READ_TIMEOUT_US, true,
24909690015SKumaravel Thiagarajan rb + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));
25009690015SKumaravel Thiagarajan
25109690015SKumaravel Thiagarajan data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));
25209690015SKumaravel Thiagarajan if (ret < 0 || data & OTP_FAIL_BIT) {
25309690015SKumaravel Thiagarajan ret = -EIO;
25409690015SKumaravel Thiagarajan goto error;
25509690015SKumaravel Thiagarajan }
25609690015SKumaravel Thiagarajan
25709690015SKumaravel Thiagarajan buf[byte] = readl(rb + MMAP_OTP_OFFSET(OTP_RD_DATA_OFFSET));
25809690015SKumaravel Thiagarajan }
25909690015SKumaravel Thiagarajan error:
26009690015SKumaravel Thiagarajan release_sys_lock(priv);
26109690015SKumaravel Thiagarajan return ret;
26209690015SKumaravel Thiagarajan }
26309690015SKumaravel Thiagarajan
pci1xxxx_otp_write(void * priv_t,unsigned int off,void * value_t,size_t count)26409690015SKumaravel Thiagarajan static int pci1xxxx_otp_write(void *priv_t, unsigned int off,
26509690015SKumaravel Thiagarajan void *value_t, size_t count)
26609690015SKumaravel Thiagarajan {
26709690015SKumaravel Thiagarajan struct pci1xxxx_otp_eeprom_device *priv = priv_t;
26809690015SKumaravel Thiagarajan void __iomem *rb = priv->reg_base;
26909690015SKumaravel Thiagarajan char *value = value_t;
27009690015SKumaravel Thiagarajan u32 regval;
27109690015SKumaravel Thiagarajan u32 byte;
27209690015SKumaravel Thiagarajan int ret;
27309690015SKumaravel Thiagarajan u8 data;
27409690015SKumaravel Thiagarajan
27509690015SKumaravel Thiagarajan if (off >= priv->nvmem_config_otp.size)
27609690015SKumaravel Thiagarajan return -EFAULT;
27709690015SKumaravel Thiagarajan
27809690015SKumaravel Thiagarajan if ((off + count) > priv->nvmem_config_otp.size)
27909690015SKumaravel Thiagarajan count = priv->nvmem_config_otp.size - off;
28009690015SKumaravel Thiagarajan
28109690015SKumaravel Thiagarajan ret = set_sys_lock(priv);
28209690015SKumaravel Thiagarajan if (ret)
28309690015SKumaravel Thiagarajan return ret;
28409690015SKumaravel Thiagarajan
28509690015SKumaravel Thiagarajan for (byte = 0; byte < count; byte++) {
28609690015SKumaravel Thiagarajan otp_device_set_address(priv, (u16)(off + byte));
28709690015SKumaravel Thiagarajan
28809690015SKumaravel Thiagarajan /*
28909690015SKumaravel Thiagarajan * Set OTP_PGM_MODE_BYTE command bit in OTP_PRGM_MODE register
29009690015SKumaravel Thiagarajan * to enable Byte programming
29109690015SKumaravel Thiagarajan */
29209690015SKumaravel Thiagarajan data = readl(rb + MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));
29309690015SKumaravel Thiagarajan writel(data | OTP_PGM_MODE_BYTE_BIT,
29409690015SKumaravel Thiagarajan rb + MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));
29509690015SKumaravel Thiagarajan writel(*(value + byte), rb + MMAP_OTP_OFFSET(OTP_PRGM_DATA_OFFSET));
29609690015SKumaravel Thiagarajan data = readl(rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
29709690015SKumaravel Thiagarajan writel(data | OTP_FUNC_PGM_BIT,
29809690015SKumaravel Thiagarajan rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
29909690015SKumaravel Thiagarajan data = readl(rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
30009690015SKumaravel Thiagarajan writel(data | OTP_CMD_GO_BIT,
30109690015SKumaravel Thiagarajan rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
30209690015SKumaravel Thiagarajan
30309690015SKumaravel Thiagarajan ret = read_poll_timeout(readl, regval,
30409690015SKumaravel Thiagarajan !(regval & OTP_STATUS_BUSY_BIT),
30509690015SKumaravel Thiagarajan STATUS_READ_DELAY_US,
30609690015SKumaravel Thiagarajan STATUS_READ_TIMEOUT_US, true,
30709690015SKumaravel Thiagarajan rb + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));
30809690015SKumaravel Thiagarajan
30909690015SKumaravel Thiagarajan data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));
31009690015SKumaravel Thiagarajan if (ret < 0 || data & OTP_FAIL_BIT) {
31109690015SKumaravel Thiagarajan ret = -EIO;
31209690015SKumaravel Thiagarajan goto error;
31309690015SKumaravel Thiagarajan }
31409690015SKumaravel Thiagarajan }
31509690015SKumaravel Thiagarajan error:
31609690015SKumaravel Thiagarajan release_sys_lock(priv);
31709690015SKumaravel Thiagarajan return ret;
31809690015SKumaravel Thiagarajan }
31909690015SKumaravel Thiagarajan
pci1xxxx_otp_eeprom_probe(struct auxiliary_device * aux_dev,const struct auxiliary_device_id * id)32009690015SKumaravel Thiagarajan static int pci1xxxx_otp_eeprom_probe(struct auxiliary_device *aux_dev,
32109690015SKumaravel Thiagarajan const struct auxiliary_device_id *id)
32209690015SKumaravel Thiagarajan {
32309690015SKumaravel Thiagarajan struct auxiliary_device_wrapper *aux_dev_wrapper;
32409690015SKumaravel Thiagarajan struct pci1xxxx_otp_eeprom_device *priv;
32509690015SKumaravel Thiagarajan struct gp_aux_data_type *pdata;
32609690015SKumaravel Thiagarajan int ret;
32709690015SKumaravel Thiagarajan u8 data;
32809690015SKumaravel Thiagarajan
32909690015SKumaravel Thiagarajan aux_dev_wrapper = container_of(aux_dev, struct auxiliary_device_wrapper,
33009690015SKumaravel Thiagarajan aux_dev);
33109690015SKumaravel Thiagarajan pdata = &aux_dev_wrapper->gp_aux_data;
33209690015SKumaravel Thiagarajan if (!pdata)
33309690015SKumaravel Thiagarajan return -EINVAL;
33409690015SKumaravel Thiagarajan
33509690015SKumaravel Thiagarajan priv = devm_kzalloc(&aux_dev->dev, sizeof(*priv), GFP_KERNEL);
33609690015SKumaravel Thiagarajan if (!priv)
33709690015SKumaravel Thiagarajan return -ENOMEM;
33809690015SKumaravel Thiagarajan
33909690015SKumaravel Thiagarajan priv->pdev = aux_dev;
34009690015SKumaravel Thiagarajan
34109690015SKumaravel Thiagarajan if (!devm_request_mem_region(&aux_dev->dev, pdata->region_start +
34209690015SKumaravel Thiagarajan PERI_PF3_SYSTEM_REG_ADDR_BASE,
34309690015SKumaravel Thiagarajan PERI_PF3_SYSTEM_REG_LENGTH,
34409690015SKumaravel Thiagarajan aux_dev->name))
34509690015SKumaravel Thiagarajan return -ENOMEM;
34609690015SKumaravel Thiagarajan
34709690015SKumaravel Thiagarajan priv->reg_base = devm_ioremap(&aux_dev->dev, pdata->region_start +
34809690015SKumaravel Thiagarajan PERI_PF3_SYSTEM_REG_ADDR_BASE,
34909690015SKumaravel Thiagarajan PERI_PF3_SYSTEM_REG_LENGTH);
35009690015SKumaravel Thiagarajan if (!priv->reg_base)
35109690015SKumaravel Thiagarajan return -ENOMEM;
35209690015SKumaravel Thiagarajan
35309690015SKumaravel Thiagarajan ret = set_sys_lock(priv);
35409690015SKumaravel Thiagarajan if (ret)
35509690015SKumaravel Thiagarajan return ret;
35609690015SKumaravel Thiagarajan
35709690015SKumaravel Thiagarajan /* Set OTP_PWR_DN to 0 to make OTP Operational */
35809690015SKumaravel Thiagarajan data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
35909690015SKumaravel Thiagarajan writel(data & ~OTP_PWR_DN_BIT,
36009690015SKumaravel Thiagarajan priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
36109690015SKumaravel Thiagarajan
36209690015SKumaravel Thiagarajan dev_set_drvdata(&aux_dev->dev, priv);
36309690015SKumaravel Thiagarajan
3649ab54653SKumaravel Thiagarajan if (is_eeprom_responsive(priv)) {
3659ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.type = NVMEM_TYPE_EEPROM;
3669ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.name = EEPROM_NAME;
36778df4218SHeiko Thiery priv->nvmem_config_eeprom.id = NVMEM_DEVID_AUTO;
3689ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.dev = &aux_dev->dev;
3699ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.owner = THIS_MODULE;
3709ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.reg_read = pci1xxxx_eeprom_read;
3719ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.reg_write = pci1xxxx_eeprom_write;
3729ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.priv = priv;
3739ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.stride = 1;
3749ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.word_size = 1;
3759ab54653SKumaravel Thiagarajan priv->nvmem_config_eeprom.size = EEPROM_SIZE_BYTES;
3769ab54653SKumaravel Thiagarajan
3779ab54653SKumaravel Thiagarajan priv->nvmem_eeprom = devm_nvmem_register(&aux_dev->dev,
3789ab54653SKumaravel Thiagarajan &priv->nvmem_config_eeprom);
3795a652fe5SDan Carpenter if (IS_ERR(priv->nvmem_eeprom))
3805a652fe5SDan Carpenter return PTR_ERR(priv->nvmem_eeprom);
3819ab54653SKumaravel Thiagarajan }
3829ab54653SKumaravel Thiagarajan
38309690015SKumaravel Thiagarajan release_sys_lock(priv);
38409690015SKumaravel Thiagarajan
38509690015SKumaravel Thiagarajan priv->nvmem_config_otp.type = NVMEM_TYPE_OTP;
38609690015SKumaravel Thiagarajan priv->nvmem_config_otp.name = OTP_NAME;
387*245bee12SHeiko Thiery priv->nvmem_config_otp.id = NVMEM_DEVID_AUTO;
38809690015SKumaravel Thiagarajan priv->nvmem_config_otp.dev = &aux_dev->dev;
38909690015SKumaravel Thiagarajan priv->nvmem_config_otp.owner = THIS_MODULE;
39009690015SKumaravel Thiagarajan priv->nvmem_config_otp.reg_read = pci1xxxx_otp_read;
39109690015SKumaravel Thiagarajan priv->nvmem_config_otp.reg_write = pci1xxxx_otp_write;
39209690015SKumaravel Thiagarajan priv->nvmem_config_otp.priv = priv;
39309690015SKumaravel Thiagarajan priv->nvmem_config_otp.stride = 1;
39409690015SKumaravel Thiagarajan priv->nvmem_config_otp.word_size = 1;
39509690015SKumaravel Thiagarajan priv->nvmem_config_otp.size = OTP_SIZE_BYTES;
39609690015SKumaravel Thiagarajan
39709690015SKumaravel Thiagarajan priv->nvmem_otp = devm_nvmem_register(&aux_dev->dev,
39809690015SKumaravel Thiagarajan &priv->nvmem_config_otp);
3995a652fe5SDan Carpenter if (IS_ERR(priv->nvmem_otp))
4005a652fe5SDan Carpenter return PTR_ERR(priv->nvmem_otp);
40109690015SKumaravel Thiagarajan
40209690015SKumaravel Thiagarajan return ret;
40309690015SKumaravel Thiagarajan }
40409690015SKumaravel Thiagarajan
pci1xxxx_otp_eeprom_remove(struct auxiliary_device * aux_dev)40509690015SKumaravel Thiagarajan static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev)
40609690015SKumaravel Thiagarajan {
40709690015SKumaravel Thiagarajan struct pci1xxxx_otp_eeprom_device *priv;
40809690015SKumaravel Thiagarajan void __iomem *sys_lock;
40909690015SKumaravel Thiagarajan
41009690015SKumaravel Thiagarajan priv = dev_get_drvdata(&aux_dev->dev);
41109690015SKumaravel Thiagarajan sys_lock = priv->reg_base + MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
41209690015SKumaravel Thiagarajan writel(CFG_SYS_LOCK_PF3, sys_lock);
41309690015SKumaravel Thiagarajan
41409690015SKumaravel Thiagarajan /* Shut down OTP */
41509690015SKumaravel Thiagarajan writel(OTP_PWR_DN_BIT,
41609690015SKumaravel Thiagarajan priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
41709690015SKumaravel Thiagarajan
41809690015SKumaravel Thiagarajan writel(0, sys_lock);
41909690015SKumaravel Thiagarajan }
42009690015SKumaravel Thiagarajan
42109690015SKumaravel Thiagarajan static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = {
42209690015SKumaravel Thiagarajan {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"},
42309690015SKumaravel Thiagarajan {},
42409690015SKumaravel Thiagarajan };
42509690015SKumaravel Thiagarajan MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table);
42609690015SKumaravel Thiagarajan
42709690015SKumaravel Thiagarajan static struct auxiliary_driver pci1xxxx_otp_eeprom_driver = {
42809690015SKumaravel Thiagarajan .driver = {
42909690015SKumaravel Thiagarajan .name = AUX_DRIVER_NAME,
43009690015SKumaravel Thiagarajan },
43109690015SKumaravel Thiagarajan .probe = pci1xxxx_otp_eeprom_probe,
43209690015SKumaravel Thiagarajan .remove = pci1xxxx_otp_eeprom_remove,
43309690015SKumaravel Thiagarajan .id_table = pci1xxxx_otp_eeprom_auxiliary_id_table
43409690015SKumaravel Thiagarajan };
43509690015SKumaravel Thiagarajan module_auxiliary_driver(pci1xxxx_otp_eeprom_driver);
43609690015SKumaravel Thiagarajan
43709690015SKumaravel Thiagarajan MODULE_LICENSE("GPL");
43809690015SKumaravel Thiagarajan MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
43909690015SKumaravel Thiagarajan MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>");
44009690015SKumaravel Thiagarajan MODULE_AUTHOR("Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com>");
44109690015SKumaravel Thiagarajan MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx OTP EEPROM Programmer");
442