brintos

brintos / linux-shallow public Read only

0
0
Text · 12.2 KiB · a2ed477 Raw
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