162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (C) 2019 Spreadtrum Communications Inc.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/clk.h>
562306a36Sopenharmony_ci#include <linux/delay.h>
662306a36Sopenharmony_ci#include <linux/hwspinlock.h>
762306a36Sopenharmony_ci#include <linux/io.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/nvmem-provider.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define SPRD_EFUSE_ENABLE		0x20
1462306a36Sopenharmony_ci#define SPRD_EFUSE_ERR_FLAG		0x24
1562306a36Sopenharmony_ci#define SPRD_EFUSE_ERR_CLR		0x28
1662306a36Sopenharmony_ci#define SPRD_EFUSE_MAGIC_NUM		0x2c
1762306a36Sopenharmony_ci#define SPRD_EFUSE_FW_CFG		0x50
1862306a36Sopenharmony_ci#define SPRD_EFUSE_PW_SWT		0x54
1962306a36Sopenharmony_ci#define SPRD_EFUSE_MEM(val)		(0x1000 + ((val) << 2))
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define SPRD_EFUSE_VDD_EN		BIT(0)
2262306a36Sopenharmony_ci#define SPRD_EFUSE_AUTO_CHECK_EN	BIT(1)
2362306a36Sopenharmony_ci#define SPRD_EFUSE_DOUBLE_EN		BIT(2)
2462306a36Sopenharmony_ci#define SPRD_EFUSE_MARGIN_RD_EN		BIT(3)
2562306a36Sopenharmony_ci#define SPRD_EFUSE_LOCK_WR_EN		BIT(4)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define SPRD_EFUSE_ERR_CLR_MASK		GENMASK(13, 0)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define SPRD_EFUSE_ENK1_ON		BIT(0)
3062306a36Sopenharmony_ci#define SPRD_EFUSE_ENK2_ON		BIT(1)
3162306a36Sopenharmony_ci#define SPRD_EFUSE_PROG_EN		BIT(2)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define SPRD_EFUSE_MAGIC_NUMBER		0x8810
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* Block width (bytes) definitions */
3662306a36Sopenharmony_ci#define SPRD_EFUSE_BLOCK_WIDTH		4
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * The Spreadtrum AP efuse contains 2 parts: normal efuse and secure efuse,
4062306a36Sopenharmony_ci * and we can only access the normal efuse in kernel. So define the normal
4162306a36Sopenharmony_ci * block offset index and normal block numbers.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci#define SPRD_EFUSE_NORMAL_BLOCK_NUMS	24
4462306a36Sopenharmony_ci#define SPRD_EFUSE_NORMAL_BLOCK_OFFSET	72
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Timeout (ms) for the trylock of hardware spinlocks */
4762306a36Sopenharmony_ci#define SPRD_EFUSE_HWLOCK_TIMEOUT	5000
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * Since different Spreadtrum SoC chip can have different normal block numbers
5162306a36Sopenharmony_ci * and offset. And some SoC can support block double feature, which means
5262306a36Sopenharmony_ci * when reading or writing data to efuse memory, the controller can save double
5362306a36Sopenharmony_ci * data in case one data become incorrect after a long period.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * Thus we should save them in the device data structure.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_cistruct sprd_efuse_variant_data {
5862306a36Sopenharmony_ci	u32 blk_nums;
5962306a36Sopenharmony_ci	u32 blk_offset;
6062306a36Sopenharmony_ci	bool blk_double;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct sprd_efuse {
6462306a36Sopenharmony_ci	struct device *dev;
6562306a36Sopenharmony_ci	struct clk *clk;
6662306a36Sopenharmony_ci	struct hwspinlock *hwlock;
6762306a36Sopenharmony_ci	struct mutex mutex;
6862306a36Sopenharmony_ci	void __iomem *base;
6962306a36Sopenharmony_ci	const struct sprd_efuse_variant_data *data;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic const struct sprd_efuse_variant_data ums312_data = {
7362306a36Sopenharmony_ci	.blk_nums = SPRD_EFUSE_NORMAL_BLOCK_NUMS,
7462306a36Sopenharmony_ci	.blk_offset = SPRD_EFUSE_NORMAL_BLOCK_OFFSET,
7562306a36Sopenharmony_ci	.blk_double = false,
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * On Spreadtrum platform, we have multi-subsystems will access the unique
8062306a36Sopenharmony_ci * efuse controller, so we need one hardware spinlock to synchronize between
8162306a36Sopenharmony_ci * the multiple subsystems.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic int sprd_efuse_lock(struct sprd_efuse *efuse)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	mutex_lock(&efuse->mutex);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ret = hwspin_lock_timeout_raw(efuse->hwlock,
9062306a36Sopenharmony_ci				      SPRD_EFUSE_HWLOCK_TIMEOUT);
9162306a36Sopenharmony_ci	if (ret) {
9262306a36Sopenharmony_ci		dev_err(efuse->dev, "timeout get the hwspinlock\n");
9362306a36Sopenharmony_ci		mutex_unlock(&efuse->mutex);
9462306a36Sopenharmony_ci		return ret;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void sprd_efuse_unlock(struct sprd_efuse *efuse)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	hwspin_unlock_raw(efuse->hwlock);
10362306a36Sopenharmony_ci	mutex_unlock(&efuse->mutex);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void sprd_efuse_set_prog_power(struct sprd_efuse *efuse, bool en)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (en)
11162306a36Sopenharmony_ci		val &= ~SPRD_EFUSE_ENK2_ON;
11262306a36Sopenharmony_ci	else
11362306a36Sopenharmony_ci		val &= ~SPRD_EFUSE_ENK1_ON;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	writel(val, efuse->base + SPRD_EFUSE_PW_SWT);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Open or close efuse power need wait 1000us to make power stable. */
11862306a36Sopenharmony_ci	usleep_range(1000, 1200);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (en)
12162306a36Sopenharmony_ci		val |= SPRD_EFUSE_ENK1_ON;
12262306a36Sopenharmony_ci	else
12362306a36Sopenharmony_ci		val |= SPRD_EFUSE_ENK2_ON;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	writel(val, efuse->base + SPRD_EFUSE_PW_SWT);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* Open or close efuse power need wait 1000us to make power stable. */
12862306a36Sopenharmony_ci	usleep_range(1000, 1200);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void sprd_efuse_set_read_power(struct sprd_efuse *efuse, bool en)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (en)
13662306a36Sopenharmony_ci		val |= SPRD_EFUSE_VDD_EN;
13762306a36Sopenharmony_ci	else
13862306a36Sopenharmony_ci		val &= ~SPRD_EFUSE_VDD_EN;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	writel(val, efuse->base + SPRD_EFUSE_ENABLE);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Open or close efuse power need wait 1000us to make power stable. */
14362306a36Sopenharmony_ci	usleep_range(1000, 1200);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void sprd_efuse_set_prog_lock(struct sprd_efuse *efuse, bool en)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (en)
15162306a36Sopenharmony_ci		val |= SPRD_EFUSE_LOCK_WR_EN;
15262306a36Sopenharmony_ci	else
15362306a36Sopenharmony_ci		val &= ~SPRD_EFUSE_LOCK_WR_EN;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	writel(val, efuse->base + SPRD_EFUSE_ENABLE);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void sprd_efuse_set_auto_check(struct sprd_efuse *efuse, bool en)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (en)
16362306a36Sopenharmony_ci		val |= SPRD_EFUSE_AUTO_CHECK_EN;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		val &= ~SPRD_EFUSE_AUTO_CHECK_EN;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	writel(val, efuse->base + SPRD_EFUSE_ENABLE);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void sprd_efuse_set_data_double(struct sprd_efuse *efuse, bool en)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (en)
17562306a36Sopenharmony_ci		val |= SPRD_EFUSE_DOUBLE_EN;
17662306a36Sopenharmony_ci	else
17762306a36Sopenharmony_ci		val &= ~SPRD_EFUSE_DOUBLE_EN;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	writel(val, efuse->base + SPRD_EFUSE_ENABLE);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void sprd_efuse_set_prog_en(struct sprd_efuse *efuse, bool en)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (en)
18762306a36Sopenharmony_ci		val |= SPRD_EFUSE_PROG_EN;
18862306a36Sopenharmony_ci	else
18962306a36Sopenharmony_ci		val &= ~SPRD_EFUSE_PROG_EN;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	writel(val, efuse->base + SPRD_EFUSE_PW_SWT);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub,
19562306a36Sopenharmony_ci			       bool lock, u32 *data)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	u32 status;
19862306a36Sopenharmony_ci	int ret = 0;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/*
20162306a36Sopenharmony_ci	 * We need set the correct magic number before writing the efuse to
20262306a36Sopenharmony_ci	 * allow programming, and block other programming until we clear the
20362306a36Sopenharmony_ci	 * magic number.
20462306a36Sopenharmony_ci	 */
20562306a36Sopenharmony_ci	writel(SPRD_EFUSE_MAGIC_NUMBER,
20662306a36Sopenharmony_ci	       efuse->base + SPRD_EFUSE_MAGIC_NUM);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/*
20962306a36Sopenharmony_ci	 * Power on the efuse, enable programme and enable double data
21062306a36Sopenharmony_ci	 * if asked.
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci	sprd_efuse_set_prog_power(efuse, true);
21362306a36Sopenharmony_ci	sprd_efuse_set_prog_en(efuse, true);
21462306a36Sopenharmony_ci	sprd_efuse_set_data_double(efuse, doub);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * Enable the auto-check function to validate if the programming is
21862306a36Sopenharmony_ci	 * successful.
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	if (lock)
22162306a36Sopenharmony_ci		sprd_efuse_set_auto_check(efuse, true);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	writel(*data, efuse->base + SPRD_EFUSE_MEM(blk));
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Disable auto-check and data double after programming */
22662306a36Sopenharmony_ci	if (lock)
22762306a36Sopenharmony_ci		sprd_efuse_set_auto_check(efuse, false);
22862306a36Sopenharmony_ci	sprd_efuse_set_data_double(efuse, false);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/*
23162306a36Sopenharmony_ci	 * Check the efuse error status, if the programming is successful,
23262306a36Sopenharmony_ci	 * we should lock this efuse block to avoid programming again.
23362306a36Sopenharmony_ci	 */
23462306a36Sopenharmony_ci	status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG);
23562306a36Sopenharmony_ci	if (status) {
23662306a36Sopenharmony_ci		dev_err(efuse->dev,
23762306a36Sopenharmony_ci			"write error status %u of block %d\n", status, blk);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		writel(SPRD_EFUSE_ERR_CLR_MASK,
24062306a36Sopenharmony_ci		       efuse->base + SPRD_EFUSE_ERR_CLR);
24162306a36Sopenharmony_ci		ret = -EBUSY;
24262306a36Sopenharmony_ci	} else if (lock) {
24362306a36Sopenharmony_ci		sprd_efuse_set_prog_lock(efuse, lock);
24462306a36Sopenharmony_ci		writel(0, efuse->base + SPRD_EFUSE_MEM(blk));
24562306a36Sopenharmony_ci		sprd_efuse_set_prog_lock(efuse, false);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	sprd_efuse_set_prog_power(efuse, false);
24962306a36Sopenharmony_ci	writel(0, efuse->base + SPRD_EFUSE_MAGIC_NUM);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return ret;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int sprd_efuse_raw_read(struct sprd_efuse *efuse, int blk, u32 *val,
25562306a36Sopenharmony_ci			       bool doub)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	u32 status;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/*
26062306a36Sopenharmony_ci	 * Need power on the efuse before reading data from efuse, and will
26162306a36Sopenharmony_ci	 * power off the efuse after reading process.
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	sprd_efuse_set_read_power(efuse, true);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Enable double data if asked */
26662306a36Sopenharmony_ci	sprd_efuse_set_data_double(efuse, doub);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* Start to read data from efuse block */
26962306a36Sopenharmony_ci	*val = readl(efuse->base + SPRD_EFUSE_MEM(blk));
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Disable double data */
27262306a36Sopenharmony_ci	sprd_efuse_set_data_double(efuse, false);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Power off the efuse */
27562306a36Sopenharmony_ci	sprd_efuse_set_read_power(efuse, false);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/*
27862306a36Sopenharmony_ci	 * Check the efuse error status and clear them if there are some
27962306a36Sopenharmony_ci	 * errors occurred.
28062306a36Sopenharmony_ci	 */
28162306a36Sopenharmony_ci	status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG);
28262306a36Sopenharmony_ci	if (status) {
28362306a36Sopenharmony_ci		dev_err(efuse->dev,
28462306a36Sopenharmony_ci			"read error status %d of block %d\n", status, blk);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		writel(SPRD_EFUSE_ERR_CLR_MASK,
28762306a36Sopenharmony_ci		       efuse->base + SPRD_EFUSE_ERR_CLR);
28862306a36Sopenharmony_ci		return -EBUSY;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return 0;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int sprd_efuse_read(void *context, u32 offset, void *val, size_t bytes)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct sprd_efuse *efuse = context;
29762306a36Sopenharmony_ci	bool blk_double = efuse->data->blk_double;
29862306a36Sopenharmony_ci	u32 index = offset / SPRD_EFUSE_BLOCK_WIDTH + efuse->data->blk_offset;
29962306a36Sopenharmony_ci	u32 blk_offset = (offset % SPRD_EFUSE_BLOCK_WIDTH) * BITS_PER_BYTE;
30062306a36Sopenharmony_ci	u32 data;
30162306a36Sopenharmony_ci	int ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ret = sprd_efuse_lock(efuse);
30462306a36Sopenharmony_ci	if (ret)
30562306a36Sopenharmony_ci		return ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	ret = clk_prepare_enable(efuse->clk);
30862306a36Sopenharmony_ci	if (ret)
30962306a36Sopenharmony_ci		goto unlock;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = sprd_efuse_raw_read(efuse, index, &data, blk_double);
31262306a36Sopenharmony_ci	if (!ret) {
31362306a36Sopenharmony_ci		data >>= blk_offset;
31462306a36Sopenharmony_ci		memcpy(val, &data, bytes);
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	clk_disable_unprepare(efuse->clk);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ciunlock:
32062306a36Sopenharmony_ci	sprd_efuse_unlock(efuse);
32162306a36Sopenharmony_ci	return ret;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic int sprd_efuse_write(void *context, u32 offset, void *val, size_t bytes)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct sprd_efuse *efuse = context;
32762306a36Sopenharmony_ci	bool blk_double = efuse->data->blk_double;
32862306a36Sopenharmony_ci	bool lock;
32962306a36Sopenharmony_ci	int ret;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	ret = sprd_efuse_lock(efuse);
33262306a36Sopenharmony_ci	if (ret)
33362306a36Sopenharmony_ci		return ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	ret = clk_prepare_enable(efuse->clk);
33662306a36Sopenharmony_ci	if (ret)
33762306a36Sopenharmony_ci		goto unlock;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/*
34062306a36Sopenharmony_ci	 * If the writing bytes are equal with the block width, which means the
34162306a36Sopenharmony_ci	 * whole block will be programmed. For this case, we should not allow
34262306a36Sopenharmony_ci	 * this block to be programmed again by locking this block.
34362306a36Sopenharmony_ci	 *
34462306a36Sopenharmony_ci	 * If the block was programmed partially, we should allow this block to
34562306a36Sopenharmony_ci	 * be programmed again.
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	if (bytes < SPRD_EFUSE_BLOCK_WIDTH)
34862306a36Sopenharmony_ci		lock = false;
34962306a36Sopenharmony_ci	else
35062306a36Sopenharmony_ci		lock = true;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	ret = sprd_efuse_raw_prog(efuse, offset, blk_double, lock, val);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	clk_disable_unprepare(efuse->clk);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ciunlock:
35762306a36Sopenharmony_ci	sprd_efuse_unlock(efuse);
35862306a36Sopenharmony_ci	return ret;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int sprd_efuse_probe(struct platform_device *pdev)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
36462306a36Sopenharmony_ci	struct nvmem_device *nvmem;
36562306a36Sopenharmony_ci	struct nvmem_config econfig = { };
36662306a36Sopenharmony_ci	struct sprd_efuse *efuse;
36762306a36Sopenharmony_ci	const struct sprd_efuse_variant_data *pdata;
36862306a36Sopenharmony_ci	int ret;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	pdata = of_device_get_match_data(&pdev->dev);
37162306a36Sopenharmony_ci	if (!pdata) {
37262306a36Sopenharmony_ci		dev_err(&pdev->dev, "No matching driver data found\n");
37362306a36Sopenharmony_ci		return -EINVAL;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);
37762306a36Sopenharmony_ci	if (!efuse)
37862306a36Sopenharmony_ci		return -ENOMEM;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	efuse->base = devm_platform_ioremap_resource(pdev, 0);
38162306a36Sopenharmony_ci	if (IS_ERR(efuse->base))
38262306a36Sopenharmony_ci		return PTR_ERR(efuse->base);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ret = of_hwspin_lock_get_id(np, 0);
38562306a36Sopenharmony_ci	if (ret < 0) {
38662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get hwlock id\n");
38762306a36Sopenharmony_ci		return ret;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	efuse->hwlock = devm_hwspin_lock_request_specific(&pdev->dev, ret);
39162306a36Sopenharmony_ci	if (!efuse->hwlock) {
39262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to request hwlock\n");
39362306a36Sopenharmony_ci		return -ENXIO;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	efuse->clk = devm_clk_get(&pdev->dev, "enable");
39762306a36Sopenharmony_ci	if (IS_ERR(efuse->clk)) {
39862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get enable clock\n");
39962306a36Sopenharmony_ci		return PTR_ERR(efuse->clk);
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	mutex_init(&efuse->mutex);
40362306a36Sopenharmony_ci	efuse->dev = &pdev->dev;
40462306a36Sopenharmony_ci	efuse->data = pdata;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	econfig.stride = 1;
40762306a36Sopenharmony_ci	econfig.word_size = 1;
40862306a36Sopenharmony_ci	econfig.read_only = false;
40962306a36Sopenharmony_ci	econfig.name = "sprd-efuse";
41062306a36Sopenharmony_ci	econfig.size = efuse->data->blk_nums * SPRD_EFUSE_BLOCK_WIDTH;
41162306a36Sopenharmony_ci	econfig.reg_read = sprd_efuse_read;
41262306a36Sopenharmony_ci	econfig.reg_write = sprd_efuse_write;
41362306a36Sopenharmony_ci	econfig.priv = efuse;
41462306a36Sopenharmony_ci	econfig.dev = &pdev->dev;
41562306a36Sopenharmony_ci	nvmem = devm_nvmem_register(&pdev->dev, &econfig);
41662306a36Sopenharmony_ci	if (IS_ERR(nvmem)) {
41762306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register nvmem\n");
41862306a36Sopenharmony_ci		return PTR_ERR(nvmem);
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic const struct of_device_id sprd_efuse_of_match[] = {
42562306a36Sopenharmony_ci	{ .compatible = "sprd,ums312-efuse", .data = &ums312_data },
42662306a36Sopenharmony_ci	{ }
42762306a36Sopenharmony_ci};
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic struct platform_driver sprd_efuse_driver = {
43062306a36Sopenharmony_ci	.probe = sprd_efuse_probe,
43162306a36Sopenharmony_ci	.driver = {
43262306a36Sopenharmony_ci		.name = "sprd-efuse",
43362306a36Sopenharmony_ci		.of_match_table = sprd_efuse_of_match,
43462306a36Sopenharmony_ci	},
43562306a36Sopenharmony_ci};
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cimodule_platform_driver(sprd_efuse_driver);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ciMODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>");
44062306a36Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum AP efuse driver");
44162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
442