Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Amlogic Meson6, Meson8 and Meson8b eFuse Driver
  4 *
  5 * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
  6 */
  7
  8#include <linux/bitfield.h>
  9#include <linux/bitops.h>
 10#include <linux/clk.h>
 11#include <linux/delay.h>
 12#include <linux/io.h>
 13#include <linux/iopoll.h>
 14#include <linux/module.h>
 15#include <linux/nvmem-provider.h>
 16#include <linux/of.h>
 17#include <linux/of_device.h>
 18#include <linux/platform_device.h>
 19#include <linux/sizes.h>
 20#include <linux/slab.h>
 21
 22#define MESON_MX_EFUSE_CNTL1					0x04
 23#define MESON_MX_EFUSE_CNTL1_PD_ENABLE				BIT(27)
 24#define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY			BIT(26)
 25#define MESON_MX_EFUSE_CNTL1_AUTO_RD_START			BIT(25)
 26#define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE			BIT(24)
 27#define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA			GENMASK(23, 16)
 28#define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY			BIT(14)
 29#define MESON_MX_EFUSE_CNTL1_AUTO_WR_START			BIT(13)
 30#define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE			BIT(12)
 31#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET			BIT(11)
 32#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK			GENMASK(10, 0)
 33
 34#define MESON_MX_EFUSE_CNTL2					0x08
 35
 36#define MESON_MX_EFUSE_CNTL4					0x10
 37#define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE			BIT(10)
 38
 39struct meson_mx_efuse_platform_data {
 40	const char *name;
 41	unsigned int word_size;
 42};
 43
 44struct meson_mx_efuse {
 45	void __iomem *base;
 46	struct clk *core_clk;
 47	struct nvmem_device *nvmem;
 48	struct nvmem_config config;
 49};
 50
 51static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg,
 52				     u32 mask, u32 set)
 53{
 54	u32 data;
 55
 56	data = readl(efuse->base + reg);
 57	data &= ~mask;
 58	data |= (set & mask);
 59
 60	writel(data, efuse->base + reg);
 61}
 62
 63static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse)
 64{
 65	int err;
 66
 67	err = clk_prepare_enable(efuse->core_clk);
 68	if (err)
 69		return err;
 70
 71	/* power up the efuse */
 72	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
 73				 MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0);
 74
 75	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4,
 76				 MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0);
 77
 78	return 0;
 79}
 80
 81static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse)
 82{
 83	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
 84				 MESON_MX_EFUSE_CNTL1_PD_ENABLE,
 85				 MESON_MX_EFUSE_CNTL1_PD_ENABLE);
 86
 87	clk_disable_unprepare(efuse->core_clk);
 88}
 89
 90static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse,
 91				    unsigned int addr, u32 *value)
 92{
 93	int err;
 94	u32 regval;
 95
 96	/* write the address to read */
 97	regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr);
 98	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
 99				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval);
100
101	/* inform the hardware that we changed the address */
102	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
103				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET,
104				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET);
105	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
106				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0);
107
108	/* start the read process */
109	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
110				 MESON_MX_EFUSE_CNTL1_AUTO_RD_START,
111				 MESON_MX_EFUSE_CNTL1_AUTO_RD_START);
112	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
113				 MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0);
114
115	/*
116	 * perform a dummy read to ensure that the HW has the RD_BUSY bit set
117	 * when polling for the status below.
118	 */
119	readl(efuse->base + MESON_MX_EFUSE_CNTL1);
120
121	err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1,
122			regval,
123			(!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)),
124			1, 1000);
125	if (err) {
126		dev_err(efuse->config.dev,
127			"Timeout while reading efuse address %u\n", addr);
128		return err;
129	}
130
131	*value = readl(efuse->base + MESON_MX_EFUSE_CNTL2);
132
133	return 0;
134}
135
136static int meson_mx_efuse_read(void *context, unsigned int offset,
137			       void *buf, size_t bytes)
138{
139	struct meson_mx_efuse *efuse = context;
140	u32 tmp;
141	int err, i, addr;
142
143	err = meson_mx_efuse_hw_enable(efuse);
144	if (err)
145		return err;
146
147	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
148				 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE,
149				 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE);
150
151	for (i = 0; i < bytes; i += efuse->config.word_size) {
152		addr = (offset + i) / efuse->config.word_size;
153
154		err = meson_mx_efuse_read_addr(efuse, addr, &tmp);
155		if (err)
156			break;
157
158		memcpy(buf + i, &tmp,
159		       min_t(size_t, bytes - i, efuse->config.word_size));
160	}
161
162	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
163				 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0);
164
165	meson_mx_efuse_hw_disable(efuse);
166
167	return err;
168}
169
170static const struct meson_mx_efuse_platform_data meson6_efuse_data = {
171	.name = "meson6-efuse",
172	.word_size = 1,
173};
174
175static const struct meson_mx_efuse_platform_data meson8_efuse_data = {
176	.name = "meson8-efuse",
177	.word_size = 4,
178};
179
180static const struct meson_mx_efuse_platform_data meson8b_efuse_data = {
181	.name = "meson8b-efuse",
182	.word_size = 4,
183};
184
185static const struct of_device_id meson_mx_efuse_match[] = {
186	{ .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data },
187	{ .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data },
188	{ .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data },
189	{ /* sentinel */ },
190};
191MODULE_DEVICE_TABLE(of, meson_mx_efuse_match);
192
193static int meson_mx_efuse_probe(struct platform_device *pdev)
194{
195	const struct meson_mx_efuse_platform_data *drvdata;
196	struct meson_mx_efuse *efuse;
197	struct resource *res;
198
199	drvdata = of_device_get_match_data(&pdev->dev);
200	if (!drvdata)
201		return -EINVAL;
202
203	efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);
204	if (!efuse)
205		return -ENOMEM;
206
207	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
208	efuse->base = devm_ioremap_resource(&pdev->dev, res);
209	if (IS_ERR(efuse->base))
210		return PTR_ERR(efuse->base);
211
212	efuse->config.name = drvdata->name;
213	efuse->config.owner = THIS_MODULE;
214	efuse->config.dev = &pdev->dev;
215	efuse->config.priv = efuse;
216	efuse->config.stride = drvdata->word_size;
217	efuse->config.word_size = drvdata->word_size;
218	efuse->config.size = SZ_512;
219	efuse->config.read_only = true;
220	efuse->config.reg_read = meson_mx_efuse_read;
221
222	efuse->core_clk = devm_clk_get(&pdev->dev, "core");
223	if (IS_ERR(efuse->core_clk)) {
224		dev_err(&pdev->dev, "Failed to get core clock\n");
225		return PTR_ERR(efuse->core_clk);
226	}
227
228	efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
229
230	return PTR_ERR_OR_ZERO(efuse->nvmem);
231}
232
233static struct platform_driver meson_mx_efuse_driver = {
234	.probe = meson_mx_efuse_probe,
235	.driver = {
236		.name = "meson-mx-efuse",
237		.of_match_table = meson_mx_efuse_match,
238	},
239};
240
241module_platform_driver(meson_mx_efuse_driver);
242
243MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
244MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver");
245MODULE_LICENSE("GPL v2");