|  | // SPDX-License-Identifier: GPL-2.0 | 
|  |  | 
|  | /* | 
|  | * The OCOTP driver for Sunplus	SP7021 | 
|  | * | 
|  | * Copyright (C) 2019 Sunplus Technology Inc., All rights reserved. | 
|  | */ | 
|  |  | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/clk.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/device.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/iopoll.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/nvmem-provider.h> | 
|  | #include <linux/of_device.h> | 
|  | #include <linux/platform_device.h> | 
|  |  | 
|  | /* | 
|  | * OTP memory | 
|  | * Each bank contains 4 words (32 bits). | 
|  | * Bank 0 starts at offset 0 from the base. | 
|  | */ | 
|  |  | 
|  | #define OTP_WORDS_PER_BANK		4 | 
|  | #define OTP_WORD_SIZE			sizeof(u32) | 
|  | #define OTP_BIT_ADDR_OF_BANK		(8 * OTP_WORD_SIZE * OTP_WORDS_PER_BANK) | 
|  | #define QAC628_OTP_NUM_BANKS		8 | 
|  | #define QAC628_OTP_SIZE			(QAC628_OTP_NUM_BANKS * OTP_WORDS_PER_BANK * OTP_WORD_SIZE) | 
|  | #define OTP_READ_TIMEOUT_US		200000 | 
|  |  | 
|  | /* HB_GPIO */ | 
|  | #define ADDRESS_8_DATA			0x20 | 
|  |  | 
|  | /* OTP_RX */ | 
|  | #define OTP_CONTROL_2			0x48 | 
|  | #define OTP_RD_PERIOD			GENMASK(15, 8) | 
|  | #define OTP_RD_PERIOD_MASK		~GENMASK(15, 8) | 
|  | #define CPU_CLOCK			FIELD_PREP(OTP_RD_PERIOD, 30) | 
|  | #define SEL_BAK_KEY2			BIT(5) | 
|  | #define SEL_BAK_KEY2_MASK		~BIT(5) | 
|  | #define SW_TRIM_EN			BIT(4) | 
|  | #define SW_TRIM_EN_MASK			~BIT(4) | 
|  | #define SEL_BAK_KEY			BIT(3) | 
|  | #define SEL_BAK_KEY_MASK		~BIT(3) | 
|  | #define OTP_READ			BIT(2) | 
|  | #define OTP_LOAD_SECURE_DATA		BIT(1) | 
|  | #define OTP_LOAD_SECURE_DATA_MASK	~BIT(1) | 
|  | #define OTP_DO_CRC			BIT(0) | 
|  | #define OTP_DO_CRC_MASK			~BIT(0) | 
|  | #define OTP_STATUS			0x4c | 
|  | #define OTP_READ_DONE			BIT(4) | 
|  | #define OTP_READ_DONE_MASK		~BIT(4) | 
|  | #define OTP_LOAD_SECURE_DONE_MASK	~BIT(2) | 
|  | #define OTP_READ_ADDRESS		0x50 | 
|  |  | 
|  | enum base_type { | 
|  | HB_GPIO, | 
|  | OTPRX, | 
|  | BASEMAX, | 
|  | }; | 
|  |  | 
|  | struct sp_ocotp_priv { | 
|  | struct device *dev; | 
|  | void __iomem *base[BASEMAX]; | 
|  | struct clk *clk; | 
|  | }; | 
|  |  | 
|  | struct sp_ocotp_data { | 
|  | int size; | 
|  | }; | 
|  |  | 
|  | static const struct sp_ocotp_data sp_otp_v0 = { | 
|  | .size = QAC628_OTP_SIZE, | 
|  | }; | 
|  |  | 
|  | static int sp_otp_read_real(struct sp_ocotp_priv *otp, int addr, char *value) | 
|  | { | 
|  | unsigned int addr_data; | 
|  | unsigned int byte_shift; | 
|  | unsigned int status; | 
|  | int ret; | 
|  |  | 
|  | addr_data = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK); | 
|  | addr_data = addr_data / OTP_WORD_SIZE; | 
|  |  | 
|  | byte_shift = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK); | 
|  | byte_shift = byte_shift % OTP_WORD_SIZE; | 
|  |  | 
|  | addr = addr / (OTP_WORD_SIZE * OTP_WORDS_PER_BANK); | 
|  | addr = addr * OTP_BIT_ADDR_OF_BANK; | 
|  |  | 
|  | writel(readl(otp->base[OTPRX] + OTP_STATUS) & OTP_READ_DONE_MASK & | 
|  | OTP_LOAD_SECURE_DONE_MASK, otp->base[OTPRX] + OTP_STATUS); | 
|  | writel(addr, otp->base[OTPRX] + OTP_READ_ADDRESS); | 
|  | writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) | OTP_READ, | 
|  | otp->base[OTPRX] + OTP_CONTROL_2); | 
|  | writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) & SEL_BAK_KEY2_MASK & SW_TRIM_EN_MASK | 
|  | & SEL_BAK_KEY_MASK & OTP_LOAD_SECURE_DATA_MASK & OTP_DO_CRC_MASK, | 
|  | otp->base[OTPRX] + OTP_CONTROL_2); | 
|  | writel((readl(otp->base[OTPRX] + OTP_CONTROL_2) & OTP_RD_PERIOD_MASK) | CPU_CLOCK, | 
|  | otp->base[OTPRX] + OTP_CONTROL_2); | 
|  |  | 
|  | ret = readl_poll_timeout(otp->base[OTPRX] + OTP_STATUS, status, | 
|  | status & OTP_READ_DONE, 10, OTP_READ_TIMEOUT_US); | 
|  |  | 
|  | if (ret < 0) | 
|  | return ret; | 
|  |  | 
|  | *value = (readl(otp->base[HB_GPIO] + ADDRESS_8_DATA + addr_data * OTP_WORD_SIZE) | 
|  | >> (8 * byte_shift)) & 0xff; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int sp_ocotp_read(void *priv, unsigned int offset, void *value, size_t bytes) | 
|  | { | 
|  | struct sp_ocotp_priv *otp = priv; | 
|  | unsigned int addr; | 
|  | char *buf = value; | 
|  | char val[4]; | 
|  | int ret; | 
|  |  | 
|  | ret = clk_enable(otp->clk); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *buf = 0; | 
|  | for (addr = offset; addr < (offset + bytes); addr++) { | 
|  | ret = sp_otp_read_real(otp, addr, val); | 
|  | if (ret < 0) { | 
|  | dev_err(otp->dev, "OTP read fail:%d at %d", ret, addr); | 
|  | goto disable_clk; | 
|  | } | 
|  |  | 
|  | *buf++ = *val; | 
|  | } | 
|  |  | 
|  | disable_clk: | 
|  | clk_disable(otp->clk); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static struct nvmem_config sp_ocotp_nvmem_config = { | 
|  | .name = "sp-ocotp", | 
|  | .read_only = true, | 
|  | .word_size = 1, | 
|  | .size = QAC628_OTP_SIZE, | 
|  | .stride = 1, | 
|  | .reg_read = sp_ocotp_read, | 
|  | .owner = THIS_MODULE, | 
|  | }; | 
|  |  | 
|  | static int sp_ocotp_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device *dev = &pdev->dev; | 
|  | struct nvmem_device *nvmem; | 
|  | struct sp_ocotp_priv *otp; | 
|  | struct resource *res; | 
|  | int ret; | 
|  |  | 
|  | otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL); | 
|  | if (!otp) | 
|  | return -ENOMEM; | 
|  |  | 
|  | otp->dev = dev; | 
|  |  | 
|  | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio"); | 
|  | otp->base[HB_GPIO] = devm_ioremap_resource(dev, res); | 
|  | if (IS_ERR(otp->base[HB_GPIO])) | 
|  | return PTR_ERR(otp->base[HB_GPIO]); | 
|  |  | 
|  | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx"); | 
|  | otp->base[OTPRX] = devm_ioremap_resource(dev, res); | 
|  | if (IS_ERR(otp->base[OTPRX])) | 
|  | return PTR_ERR(otp->base[OTPRX]); | 
|  |  | 
|  | otp->clk = devm_clk_get(&pdev->dev, NULL); | 
|  | if (IS_ERR(otp->clk)) | 
|  | return dev_err_probe(&pdev->dev, PTR_ERR(otp->clk), | 
|  | "devm_clk_get fail\n"); | 
|  |  | 
|  | ret = clk_prepare(otp->clk); | 
|  | if (ret < 0) { | 
|  | dev_err(dev, "failed to prepare clk: %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | sp_ocotp_nvmem_config.priv = otp; | 
|  | sp_ocotp_nvmem_config.dev = dev; | 
|  |  | 
|  | nvmem = devm_nvmem_register(dev, &sp_ocotp_nvmem_config); | 
|  | if (IS_ERR(nvmem)) | 
|  | return dev_err_probe(&pdev->dev, PTR_ERR(nvmem), | 
|  | "register nvmem device fail\n"); | 
|  |  | 
|  | platform_set_drvdata(pdev, nvmem); | 
|  |  | 
|  | dev_dbg(dev, "banks:%d x wpb:%d x wsize:%d = %d", | 
|  | (int)QAC628_OTP_NUM_BANKS, (int)OTP_WORDS_PER_BANK, | 
|  | (int)OTP_WORD_SIZE, (int)QAC628_OTP_SIZE); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id sp_ocotp_dt_ids[] = { | 
|  | { .compatible = "sunplus,sp7021-ocotp", .data = &sp_otp_v0 }, | 
|  | { } | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, sp_ocotp_dt_ids); | 
|  |  | 
|  | static struct platform_driver sp_otp_driver = { | 
|  | .probe     = sp_ocotp_probe, | 
|  | .driver    = { | 
|  | .name           = "sunplus,sp7021-ocotp", | 
|  | .of_match_table = sp_ocotp_dt_ids, | 
|  | } | 
|  | }; | 
|  | module_platform_driver(sp_otp_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Vincent Shih <vincent.sunplus@gmail.com>"); | 
|  | MODULE_DESCRIPTION("Sunplus On-Chip OTP driver"); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  |