Linux Audio

Check our new training course

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