| // SPDX-License-Identifier: GPL-2.0 | 
 | // Copyright (C) 2019 Spreadtrum Communications Inc. | 
 |  | 
 | #include <linux/clk.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/hwspinlock.h> | 
 | #include <linux/io.h> | 
 | #include <linux/module.h> | 
 | #include <linux/nvmem-provider.h> | 
 | #include <linux/of_device.h> | 
 | #include <linux/platform_device.h> | 
 |  | 
 | #define SPRD_EFUSE_ENABLE		0x20 | 
 | #define SPRD_EFUSE_ERR_FLAG		0x24 | 
 | #define SPRD_EFUSE_ERR_CLR		0x28 | 
 | #define SPRD_EFUSE_MAGIC_NUM		0x2c | 
 | #define SPRD_EFUSE_FW_CFG		0x50 | 
 | #define SPRD_EFUSE_PW_SWT		0x54 | 
 | #define SPRD_EFUSE_MEM(val)		(0x1000 + ((val) << 2)) | 
 |  | 
 | #define SPRD_EFUSE_VDD_EN		BIT(0) | 
 | #define SPRD_EFUSE_AUTO_CHECK_EN	BIT(1) | 
 | #define SPRD_EFUSE_DOUBLE_EN		BIT(2) | 
 | #define SPRD_EFUSE_MARGIN_RD_EN		BIT(3) | 
 | #define SPRD_EFUSE_LOCK_WR_EN		BIT(4) | 
 |  | 
 | #define SPRD_EFUSE_ERR_CLR_MASK		GENMASK(13, 0) | 
 |  | 
 | #define SPRD_EFUSE_ENK1_ON		BIT(0) | 
 | #define SPRD_EFUSE_ENK2_ON		BIT(1) | 
 | #define SPRD_EFUSE_PROG_EN		BIT(2) | 
 |  | 
 | #define SPRD_EFUSE_MAGIC_NUMBER		0x8810 | 
 |  | 
 | /* Block width (bytes) definitions */ | 
 | #define SPRD_EFUSE_BLOCK_WIDTH		4 | 
 |  | 
 | /* | 
 |  * The Spreadtrum AP efuse contains 2 parts: normal efuse and secure efuse, | 
 |  * and we can only access the normal efuse in kernel. So define the normal | 
 |  * block offset index and normal block numbers. | 
 |  */ | 
 | #define SPRD_EFUSE_NORMAL_BLOCK_NUMS	24 | 
 | #define SPRD_EFUSE_NORMAL_BLOCK_OFFSET	72 | 
 |  | 
 | /* Timeout (ms) for the trylock of hardware spinlocks */ | 
 | #define SPRD_EFUSE_HWLOCK_TIMEOUT	5000 | 
 |  | 
 | /* | 
 |  * Since different Spreadtrum SoC chip can have different normal block numbers | 
 |  * and offset. And some SoC can support block double feature, which means | 
 |  * when reading or writing data to efuse memory, the controller can save double | 
 |  * data in case one data become incorrect after a long period. | 
 |  * | 
 |  * Thus we should save them in the device data structure. | 
 |  */ | 
 | struct sprd_efuse_variant_data { | 
 | 	u32 blk_nums; | 
 | 	u32 blk_offset; | 
 | 	bool blk_double; | 
 | }; | 
 |  | 
 | struct sprd_efuse { | 
 | 	struct device *dev; | 
 | 	struct clk *clk; | 
 | 	struct hwspinlock *hwlock; | 
 | 	struct mutex mutex; | 
 | 	void __iomem *base; | 
 | 	const struct sprd_efuse_variant_data *data; | 
 | }; | 
 |  | 
 | static const struct sprd_efuse_variant_data ums312_data = { | 
 | 	.blk_nums = SPRD_EFUSE_NORMAL_BLOCK_NUMS, | 
 | 	.blk_offset = SPRD_EFUSE_NORMAL_BLOCK_OFFSET, | 
 | 	.blk_double = false, | 
 | }; | 
 |  | 
 | /* | 
 |  * On Spreadtrum platform, we have multi-subsystems will access the unique | 
 |  * efuse controller, so we need one hardware spinlock to synchronize between | 
 |  * the multiple subsystems. | 
 |  */ | 
 | static int sprd_efuse_lock(struct sprd_efuse *efuse) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	mutex_lock(&efuse->mutex); | 
 |  | 
 | 	ret = hwspin_lock_timeout_raw(efuse->hwlock, | 
 | 				      SPRD_EFUSE_HWLOCK_TIMEOUT); | 
 | 	if (ret) { | 
 | 		dev_err(efuse->dev, "timeout get the hwspinlock\n"); | 
 | 		mutex_unlock(&efuse->mutex); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void sprd_efuse_unlock(struct sprd_efuse *efuse) | 
 | { | 
 | 	hwspin_unlock_raw(efuse->hwlock); | 
 | 	mutex_unlock(&efuse->mutex); | 
 | } | 
 |  | 
 | static void sprd_efuse_set_prog_power(struct sprd_efuse *efuse, bool en) | 
 | { | 
 | 	u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT); | 
 |  | 
 | 	if (en) | 
 | 		val &= ~SPRD_EFUSE_ENK2_ON; | 
 | 	else | 
 | 		val &= ~SPRD_EFUSE_ENK1_ON; | 
 |  | 
 | 	writel(val, efuse->base + SPRD_EFUSE_PW_SWT); | 
 |  | 
 | 	/* Open or close efuse power need wait 1000us to make power stable. */ | 
 | 	usleep_range(1000, 1200); | 
 |  | 
 | 	if (en) | 
 | 		val |= SPRD_EFUSE_ENK1_ON; | 
 | 	else | 
 | 		val |= SPRD_EFUSE_ENK2_ON; | 
 |  | 
 | 	writel(val, efuse->base + SPRD_EFUSE_PW_SWT); | 
 |  | 
 | 	/* Open or close efuse power need wait 1000us to make power stable. */ | 
 | 	usleep_range(1000, 1200); | 
 | } | 
 |  | 
 | static void sprd_efuse_set_read_power(struct sprd_efuse *efuse, bool en) | 
 | { | 
 | 	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); | 
 |  | 
 | 	if (en) | 
 | 		val |= SPRD_EFUSE_VDD_EN; | 
 | 	else | 
 | 		val &= ~SPRD_EFUSE_VDD_EN; | 
 |  | 
 | 	writel(val, efuse->base + SPRD_EFUSE_ENABLE); | 
 |  | 
 | 	/* Open or close efuse power need wait 1000us to make power stable. */ | 
 | 	usleep_range(1000, 1200); | 
 | } | 
 |  | 
 | static void sprd_efuse_set_prog_lock(struct sprd_efuse *efuse, bool en) | 
 | { | 
 | 	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); | 
 |  | 
 | 	if (en) | 
 | 		val |= SPRD_EFUSE_LOCK_WR_EN; | 
 | 	else | 
 | 		val &= ~SPRD_EFUSE_LOCK_WR_EN; | 
 |  | 
 | 	writel(val, efuse->base + SPRD_EFUSE_ENABLE); | 
 | } | 
 |  | 
 | static void sprd_efuse_set_auto_check(struct sprd_efuse *efuse, bool en) | 
 | { | 
 | 	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); | 
 |  | 
 | 	if (en) | 
 | 		val |= SPRD_EFUSE_AUTO_CHECK_EN; | 
 | 	else | 
 | 		val &= ~SPRD_EFUSE_AUTO_CHECK_EN; | 
 |  | 
 | 	writel(val, efuse->base + SPRD_EFUSE_ENABLE); | 
 | } | 
 |  | 
 | static void sprd_efuse_set_data_double(struct sprd_efuse *efuse, bool en) | 
 | { | 
 | 	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); | 
 |  | 
 | 	if (en) | 
 | 		val |= SPRD_EFUSE_DOUBLE_EN; | 
 | 	else | 
 | 		val &= ~SPRD_EFUSE_DOUBLE_EN; | 
 |  | 
 | 	writel(val, efuse->base + SPRD_EFUSE_ENABLE); | 
 | } | 
 |  | 
 | static void sprd_efuse_set_prog_en(struct sprd_efuse *efuse, bool en) | 
 | { | 
 | 	u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT); | 
 |  | 
 | 	if (en) | 
 | 		val |= SPRD_EFUSE_PROG_EN; | 
 | 	else | 
 | 		val &= ~SPRD_EFUSE_PROG_EN; | 
 |  | 
 | 	writel(val, efuse->base + SPRD_EFUSE_PW_SWT); | 
 | } | 
 |  | 
 | static int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub, | 
 | 			       bool lock, u32 *data) | 
 | { | 
 | 	u32 status; | 
 | 	int ret = 0; | 
 |  | 
 | 	/* | 
 | 	 * We need set the correct magic number before writing the efuse to | 
 | 	 * allow programming, and block other programming until we clear the | 
 | 	 * magic number. | 
 | 	 */ | 
 | 	writel(SPRD_EFUSE_MAGIC_NUMBER, | 
 | 	       efuse->base + SPRD_EFUSE_MAGIC_NUM); | 
 |  | 
 | 	/* | 
 | 	 * Power on the efuse, enable programme and enable double data | 
 | 	 * if asked. | 
 | 	 */ | 
 | 	sprd_efuse_set_prog_power(efuse, true); | 
 | 	sprd_efuse_set_prog_en(efuse, true); | 
 | 	sprd_efuse_set_data_double(efuse, doub); | 
 |  | 
 | 	/* | 
 | 	 * Enable the auto-check function to validate if the programming is | 
 | 	 * successful. | 
 | 	 */ | 
 | 	if (lock) | 
 | 		sprd_efuse_set_auto_check(efuse, true); | 
 |  | 
 | 	writel(*data, efuse->base + SPRD_EFUSE_MEM(blk)); | 
 |  | 
 | 	/* Disable auto-check and data double after programming */ | 
 | 	if (lock) | 
 | 		sprd_efuse_set_auto_check(efuse, false); | 
 | 	sprd_efuse_set_data_double(efuse, false); | 
 |  | 
 | 	/* | 
 | 	 * Check the efuse error status, if the programming is successful, | 
 | 	 * we should lock this efuse block to avoid programming again. | 
 | 	 */ | 
 | 	status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG); | 
 | 	if (status) { | 
 | 		dev_err(efuse->dev, | 
 | 			"write error status %u of block %d\n", status, blk); | 
 |  | 
 | 		writel(SPRD_EFUSE_ERR_CLR_MASK, | 
 | 		       efuse->base + SPRD_EFUSE_ERR_CLR); | 
 | 		ret = -EBUSY; | 
 | 	} else if (lock) { | 
 | 		sprd_efuse_set_prog_lock(efuse, lock); | 
 | 		writel(0, efuse->base + SPRD_EFUSE_MEM(blk)); | 
 | 		sprd_efuse_set_prog_lock(efuse, false); | 
 | 	} | 
 |  | 
 | 	sprd_efuse_set_prog_power(efuse, false); | 
 | 	writel(0, efuse->base + SPRD_EFUSE_MAGIC_NUM); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int sprd_efuse_raw_read(struct sprd_efuse *efuse, int blk, u32 *val, | 
 | 			       bool doub) | 
 | { | 
 | 	u32 status; | 
 |  | 
 | 	/* | 
 | 	 * Need power on the efuse before reading data from efuse, and will | 
 | 	 * power off the efuse after reading process. | 
 | 	 */ | 
 | 	sprd_efuse_set_read_power(efuse, true); | 
 |  | 
 | 	/* Enable double data if asked */ | 
 | 	sprd_efuse_set_data_double(efuse, doub); | 
 |  | 
 | 	/* Start to read data from efuse block */ | 
 | 	*val = readl(efuse->base + SPRD_EFUSE_MEM(blk)); | 
 |  | 
 | 	/* Disable double data */ | 
 | 	sprd_efuse_set_data_double(efuse, false); | 
 |  | 
 | 	/* Power off the efuse */ | 
 | 	sprd_efuse_set_read_power(efuse, false); | 
 |  | 
 | 	/* | 
 | 	 * Check the efuse error status and clear them if there are some | 
 | 	 * errors occurred. | 
 | 	 */ | 
 | 	status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG); | 
 | 	if (status) { | 
 | 		dev_err(efuse->dev, | 
 | 			"read error status %d of block %d\n", status, blk); | 
 |  | 
 | 		writel(SPRD_EFUSE_ERR_CLR_MASK, | 
 | 		       efuse->base + SPRD_EFUSE_ERR_CLR); | 
 | 		return -EBUSY; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int sprd_efuse_read(void *context, u32 offset, void *val, size_t bytes) | 
 | { | 
 | 	struct sprd_efuse *efuse = context; | 
 | 	bool blk_double = efuse->data->blk_double; | 
 | 	u32 index = offset / SPRD_EFUSE_BLOCK_WIDTH + efuse->data->blk_offset; | 
 | 	u32 blk_offset = (offset % SPRD_EFUSE_BLOCK_WIDTH) * BITS_PER_BYTE; | 
 | 	u32 data; | 
 | 	int ret; | 
 |  | 
 | 	ret = sprd_efuse_lock(efuse); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	ret = clk_prepare_enable(efuse->clk); | 
 | 	if (ret) | 
 | 		goto unlock; | 
 |  | 
 | 	ret = sprd_efuse_raw_read(efuse, index, &data, blk_double); | 
 | 	if (!ret) { | 
 | 		data >>= blk_offset; | 
 | 		memcpy(val, &data, bytes); | 
 | 	} | 
 |  | 
 | 	clk_disable_unprepare(efuse->clk); | 
 |  | 
 | unlock: | 
 | 	sprd_efuse_unlock(efuse); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int sprd_efuse_write(void *context, u32 offset, void *val, size_t bytes) | 
 | { | 
 | 	struct sprd_efuse *efuse = context; | 
 | 	bool blk_double = efuse->data->blk_double; | 
 | 	bool lock; | 
 | 	int ret; | 
 |  | 
 | 	ret = sprd_efuse_lock(efuse); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	ret = clk_prepare_enable(efuse->clk); | 
 | 	if (ret) | 
 | 		goto unlock; | 
 |  | 
 | 	/* | 
 | 	 * If the writing bytes are equal with the block width, which means the | 
 | 	 * whole block will be programmed. For this case, we should not allow | 
 | 	 * this block to be programmed again by locking this block. | 
 | 	 * | 
 | 	 * If the block was programmed partially, we should allow this block to | 
 | 	 * be programmed again. | 
 | 	 */ | 
 | 	if (bytes < SPRD_EFUSE_BLOCK_WIDTH) | 
 | 		lock = false; | 
 | 	else | 
 | 		lock = true; | 
 |  | 
 | 	ret = sprd_efuse_raw_prog(efuse, offset, blk_double, lock, val); | 
 |  | 
 | 	clk_disable_unprepare(efuse->clk); | 
 |  | 
 | unlock: | 
 | 	sprd_efuse_unlock(efuse); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int sprd_efuse_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct device_node *np = pdev->dev.of_node; | 
 | 	struct nvmem_device *nvmem; | 
 | 	struct nvmem_config econfig = { }; | 
 | 	struct sprd_efuse *efuse; | 
 | 	const struct sprd_efuse_variant_data *pdata; | 
 | 	int ret; | 
 |  | 
 | 	pdata = of_device_get_match_data(&pdev->dev); | 
 | 	if (!pdata) { | 
 | 		dev_err(&pdev->dev, "No matching driver data found\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); | 
 | 	if (!efuse) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	efuse->base = devm_platform_ioremap_resource(pdev, 0); | 
 | 	if (IS_ERR(efuse->base)) | 
 | 		return PTR_ERR(efuse->base); | 
 |  | 
 | 	ret = of_hwspin_lock_get_id(np, 0); | 
 | 	if (ret < 0) { | 
 | 		dev_err(&pdev->dev, "failed to get hwlock id\n"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	efuse->hwlock = devm_hwspin_lock_request_specific(&pdev->dev, ret); | 
 | 	if (!efuse->hwlock) { | 
 | 		dev_err(&pdev->dev, "failed to request hwlock\n"); | 
 | 		return -ENXIO; | 
 | 	} | 
 |  | 
 | 	efuse->clk = devm_clk_get(&pdev->dev, "enable"); | 
 | 	if (IS_ERR(efuse->clk)) { | 
 | 		dev_err(&pdev->dev, "failed to get enable clock\n"); | 
 | 		return PTR_ERR(efuse->clk); | 
 | 	} | 
 |  | 
 | 	mutex_init(&efuse->mutex); | 
 | 	efuse->dev = &pdev->dev; | 
 | 	efuse->data = pdata; | 
 |  | 
 | 	econfig.stride = 1; | 
 | 	econfig.word_size = 1; | 
 | 	econfig.read_only = false; | 
 | 	econfig.name = "sprd-efuse"; | 
 | 	econfig.size = efuse->data->blk_nums * SPRD_EFUSE_BLOCK_WIDTH; | 
 | 	econfig.reg_read = sprd_efuse_read; | 
 | 	econfig.reg_write = sprd_efuse_write; | 
 | 	econfig.priv = efuse; | 
 | 	econfig.dev = &pdev->dev; | 
 | 	nvmem = devm_nvmem_register(&pdev->dev, &econfig); | 
 | 	if (IS_ERR(nvmem)) { | 
 | 		dev_err(&pdev->dev, "failed to register nvmem\n"); | 
 | 		return PTR_ERR(nvmem); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct of_device_id sprd_efuse_of_match[] = { | 
 | 	{ .compatible = "sprd,ums312-efuse", .data = &ums312_data }, | 
 | 	{ } | 
 | }; | 
 |  | 
 | static struct platform_driver sprd_efuse_driver = { | 
 | 	.probe = sprd_efuse_probe, | 
 | 	.driver = { | 
 | 		.name = "sprd-efuse", | 
 | 		.of_match_table = sprd_efuse_of_match, | 
 | 	}, | 
 | }; | 
 |  | 
 | module_platform_driver(sprd_efuse_driver); | 
 |  | 
 | MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>"); | 
 | MODULE_DESCRIPTION("Spreadtrum AP efuse driver"); | 
 | MODULE_LICENSE("GPL v2"); |