Linux Audio

Check our new training course

Loading...
v5.9
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Freescale MXS On-Chip OTP driver
  4 *
  5 * Copyright (C) 2015 Stefan Wahren <stefan.wahren@i2se.com>
  6 *
  7 * Based on the driver from Huang Shijie and Christoph G. Baumann
 
 
 
 
 
 
 
 
 
 
 
  8 */
  9#include <linux/clk.h>
 10#include <linux/delay.h>
 11#include <linux/device.h>
 12#include <linux/err.h>
 13#include <linux/io.h>
 14#include <linux/module.h>
 15#include <linux/nvmem-provider.h>
 16#include <linux/of_device.h>
 17#include <linux/platform_device.h>
 
 18#include <linux/slab.h>
 19#include <linux/stmp_device.h>
 20
 21/* OCOTP registers and bits */
 22
 23#define BM_OCOTP_CTRL_RD_BANK_OPEN	BIT(12)
 24#define BM_OCOTP_CTRL_ERROR		BIT(9)
 25#define BM_OCOTP_CTRL_BUSY		BIT(8)
 26
 27#define OCOTP_TIMEOUT		10000
 28#define OCOTP_DATA_OFFSET	0x20
 29
 30struct mxs_ocotp {
 31	struct clk *clk;
 32	void __iomem *base;
 33	struct nvmem_device *nvmem;
 34};
 35
 36static int mxs_ocotp_wait(struct mxs_ocotp *otp)
 37{
 38	int timeout = OCOTP_TIMEOUT;
 39	unsigned int status = 0;
 40
 41	while (timeout--) {
 42		status = readl(otp->base);
 43
 44		if (!(status & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)))
 45			break;
 46
 47		cpu_relax();
 48	}
 49
 50	if (status & BM_OCOTP_CTRL_BUSY)
 51		return -EBUSY;
 52	else if (status & BM_OCOTP_CTRL_ERROR)
 53		return -EIO;
 54
 55	return 0;
 56}
 57
 58static int mxs_ocotp_read(void *context, unsigned int offset,
 59			  void *val, size_t bytes)
 60{
 61	struct mxs_ocotp *otp = context;
 
 62	u32 *buf = val;
 63	int ret;
 64
 65	ret = clk_enable(otp->clk);
 66	if (ret)
 67		return ret;
 68
 69	writel(BM_OCOTP_CTRL_ERROR, otp->base + STMP_OFFSET_REG_CLR);
 70
 71	ret = mxs_ocotp_wait(otp);
 72	if (ret)
 73		goto disable_clk;
 74
 75	/* open OCOTP banks for read */
 76	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_SET);
 77
 78	/* approximately wait 33 hclk cycles */
 79	udelay(1);
 80
 81	ret = mxs_ocotp_wait(otp);
 82	if (ret)
 83		goto close_banks;
 84
 85	while (bytes) {
 86		if ((offset < OCOTP_DATA_OFFSET) || (offset % 16)) {
 87			/* fill up non-data register */
 88			*buf++ = 0;
 89		} else {
 90			*buf++ = readl(otp->base + offset);
 91		}
 92
 93		bytes -= 4;
 94		offset += 4;
 
 95	}
 96
 97close_banks:
 98	/* close banks for power saving */
 99	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_CLR);
100
101disable_clk:
102	clk_disable(otp->clk);
103
104	return ret;
105}
106
 
 
 
 
 
 
 
 
 
 
 
107static struct nvmem_config ocotp_config = {
108	.name = "mxs-ocotp",
109	.stride = 16,
110	.word_size = 4,
111	.reg_read = mxs_ocotp_read,
 
 
 
 
 
 
 
 
 
 
 
112};
113
114struct mxs_data {
115	int size;
 
116};
117
118static const struct mxs_data imx23_data = {
119	.size = 0x220,
 
 
 
120};
121
122static const struct mxs_data imx28_data = {
123	.size = 0x2a0,
 
 
 
124};
125
126static const struct of_device_id mxs_ocotp_match[] = {
127	{ .compatible = "fsl,imx23-ocotp", .data = &imx23_data },
128	{ .compatible = "fsl,imx28-ocotp", .data = &imx28_data },
129	{ /* sentinel */},
130};
131MODULE_DEVICE_TABLE(of, mxs_ocotp_match);
132
133static void mxs_ocotp_action(void *data)
134{
135	clk_unprepare(data);
136}
137
138static int mxs_ocotp_probe(struct platform_device *pdev)
139{
140	struct device *dev = &pdev->dev;
141	const struct mxs_data *data;
142	struct mxs_ocotp *otp;
 
143	const struct of_device_id *match;
 
 
144	int ret;
145
146	match = of_match_device(dev->driver->of_match_table, dev);
147	if (!match || !match->data)
148		return -EINVAL;
149
150	otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
151	if (!otp)
152		return -ENOMEM;
153
154	otp->base = devm_platform_ioremap_resource(pdev, 0);
 
155	if (IS_ERR(otp->base))
156		return PTR_ERR(otp->base);
157
158	otp->clk = devm_clk_get(&pdev->dev, NULL);
159	if (IS_ERR(otp->clk))
160		return PTR_ERR(otp->clk);
161
162	ret = clk_prepare(otp->clk);
163	if (ret < 0) {
164		dev_err(dev, "failed to prepare clk: %d\n", ret);
165		return ret;
166	}
167
168	ret = devm_add_action_or_reset(&pdev->dev, mxs_ocotp_action, otp->clk);
169	if (ret)
170		return ret;
171
172	data = match->data;
 
 
 
 
 
173
174	ocotp_config.size = data->size;
175	ocotp_config.priv = otp;
176	ocotp_config.dev = dev;
177	otp->nvmem = devm_nvmem_register(dev, &ocotp_config);
178	if (IS_ERR(otp->nvmem))
179		return PTR_ERR(otp->nvmem);
 
 
180
181	platform_set_drvdata(pdev, otp);
182
183	return 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184}
185
186static struct platform_driver mxs_ocotp_driver = {
187	.probe = mxs_ocotp_probe,
 
188	.driver = {
189		.name = "mxs-ocotp",
190		.of_match_table = mxs_ocotp_match,
191	},
192};
193
194module_platform_driver(mxs_ocotp_driver);
195MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net");
196MODULE_DESCRIPTION("driver for OCOTP in i.MX23/i.MX28");
197MODULE_LICENSE("GPL v2");
v4.6
 
  1/*
  2 * Freescale MXS On-Chip OTP driver
  3 *
  4 * Copyright (C) 2015 Stefan Wahren <stefan.wahren@i2se.com>
  5 *
  6 * Based on the driver from Huang Shijie and Christoph G. Baumann
  7 *
  8 * This program is free software; you can redistribute it and/or modify
  9 * it under the terms of the GNU General Public License as published by
 10 * the Free Software Foundation; either version 2 of the License, or
 11 * (at your option) any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 */
 19#include <linux/clk.h>
 20#include <linux/delay.h>
 21#include <linux/device.h>
 22#include <linux/err.h>
 23#include <linux/io.h>
 24#include <linux/module.h>
 25#include <linux/nvmem-provider.h>
 26#include <linux/of_device.h>
 27#include <linux/platform_device.h>
 28#include <linux/regmap.h>
 29#include <linux/slab.h>
 30#include <linux/stmp_device.h>
 31
 32/* OCOTP registers and bits */
 33
 34#define BM_OCOTP_CTRL_RD_BANK_OPEN	BIT(12)
 35#define BM_OCOTP_CTRL_ERROR		BIT(9)
 36#define BM_OCOTP_CTRL_BUSY		BIT(8)
 37
 38#define OCOTP_TIMEOUT		10000
 39#define OCOTP_DATA_OFFSET	0x20
 40
 41struct mxs_ocotp {
 42	struct clk *clk;
 43	void __iomem *base;
 44	struct nvmem_device *nvmem;
 45};
 46
 47static int mxs_ocotp_wait(struct mxs_ocotp *otp)
 48{
 49	int timeout = OCOTP_TIMEOUT;
 50	unsigned int status = 0;
 51
 52	while (timeout--) {
 53		status = readl(otp->base);
 54
 55		if (!(status & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)))
 56			break;
 57
 58		cpu_relax();
 59	}
 60
 61	if (status & BM_OCOTP_CTRL_BUSY)
 62		return -EBUSY;
 63	else if (status & BM_OCOTP_CTRL_ERROR)
 64		return -EIO;
 65
 66	return 0;
 67}
 68
 69static int mxs_ocotp_read(void *context, const void *reg, size_t reg_size,
 70			  void *val, size_t val_size)
 71{
 72	struct mxs_ocotp *otp = context;
 73	unsigned int offset = *(u32 *)reg;
 74	u32 *buf = val;
 75	int ret;
 76
 77	ret = clk_enable(otp->clk);
 78	if (ret)
 79		return ret;
 80
 81	writel(BM_OCOTP_CTRL_ERROR, otp->base + STMP_OFFSET_REG_CLR);
 82
 83	ret = mxs_ocotp_wait(otp);
 84	if (ret)
 85		goto disable_clk;
 86
 87	/* open OCOTP banks for read */
 88	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_SET);
 89
 90	/* approximately wait 33 hclk cycles */
 91	udelay(1);
 92
 93	ret = mxs_ocotp_wait(otp);
 94	if (ret)
 95		goto close_banks;
 96
 97	while (val_size >= reg_size) {
 98		if ((offset < OCOTP_DATA_OFFSET) || (offset % 16)) {
 99			/* fill up non-data register */
100			*buf = 0;
101		} else {
102			*buf = readl(otp->base + offset);
103		}
104
105		buf++;
106		val_size -= reg_size;
107		offset += reg_size;
108	}
109
110close_banks:
111	/* close banks for power saving */
112	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_CLR);
113
114disable_clk:
115	clk_disable(otp->clk);
116
117	return ret;
118}
119
120static int mxs_ocotp_write(void *context, const void *data, size_t count)
121{
122	/* We don't want to support writing */
123	return 0;
124}
125
126static bool mxs_ocotp_writeable_reg(struct device *dev, unsigned int reg)
127{
128	return false;
129}
130
131static struct nvmem_config ocotp_config = {
132	.name = "mxs-ocotp",
133	.owner = THIS_MODULE,
134};
135
136static const struct regmap_range imx23_ranges[] = {
137	regmap_reg_range(OCOTP_DATA_OFFSET, 0x210),
138};
139
140static const struct regmap_access_table imx23_access = {
141	.yes_ranges = imx23_ranges,
142	.n_yes_ranges = ARRAY_SIZE(imx23_ranges),
143};
144
145static const struct regmap_range imx28_ranges[] = {
146	regmap_reg_range(OCOTP_DATA_OFFSET, 0x290),
147};
148
149static const struct regmap_access_table imx28_access = {
150	.yes_ranges = imx28_ranges,
151	.n_yes_ranges = ARRAY_SIZE(imx28_ranges),
152};
153
154static struct regmap_bus mxs_ocotp_bus = {
155	.read = mxs_ocotp_read,
156	.write = mxs_ocotp_write, /* make regmap_init() happy */
157	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
158	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
159};
160
161static struct regmap_config mxs_ocotp_config = {
162	.reg_bits = 32,
163	.val_bits = 32,
164	.reg_stride = 16,
165	.writeable_reg = mxs_ocotp_writeable_reg,
166};
167
168static const struct of_device_id mxs_ocotp_match[] = {
169	{ .compatible = "fsl,imx23-ocotp", .data = &imx23_access },
170	{ .compatible = "fsl,imx28-ocotp", .data = &imx28_access },
171	{ /* sentinel */},
172};
173MODULE_DEVICE_TABLE(of, mxs_ocotp_match);
174
 
 
 
 
 
175static int mxs_ocotp_probe(struct platform_device *pdev)
176{
177	struct device *dev = &pdev->dev;
 
178	struct mxs_ocotp *otp;
179	struct resource *res;
180	const struct of_device_id *match;
181	struct regmap *regmap;
182	const struct regmap_access_table *access;
183	int ret;
184
185	match = of_match_device(dev->driver->of_match_table, dev);
186	if (!match || !match->data)
187		return -EINVAL;
188
189	otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
190	if (!otp)
191		return -ENOMEM;
192
193	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
194	otp->base = devm_ioremap_resource(dev, res);
195	if (IS_ERR(otp->base))
196		return PTR_ERR(otp->base);
197
198	otp->clk = devm_clk_get(&pdev->dev, NULL);
199	if (IS_ERR(otp->clk))
200		return PTR_ERR(otp->clk);
201
202	ret = clk_prepare(otp->clk);
203	if (ret < 0) {
204		dev_err(dev, "failed to prepare clk: %d\n", ret);
205		return ret;
206	}
207
208	access = match->data;
209	mxs_ocotp_config.rd_table = access;
210	mxs_ocotp_config.max_register = access->yes_ranges[0].range_max;
211
212	regmap = devm_regmap_init(dev, &mxs_ocotp_bus, otp, &mxs_ocotp_config);
213	if (IS_ERR(regmap)) {
214		dev_err(dev, "regmap init failed\n");
215		ret = PTR_ERR(regmap);
216		goto err_clk;
217	}
218
 
 
219	ocotp_config.dev = dev;
220	otp->nvmem = nvmem_register(&ocotp_config);
221	if (IS_ERR(otp->nvmem)) {
222		ret = PTR_ERR(otp->nvmem);
223		goto err_clk;
224	}
225
226	platform_set_drvdata(pdev, otp);
227
228	return 0;
229
230err_clk:
231	clk_unprepare(otp->clk);
232
233	return ret;
234}
235
236static int mxs_ocotp_remove(struct platform_device *pdev)
237{
238	struct mxs_ocotp *otp = platform_get_drvdata(pdev);
239
240	clk_unprepare(otp->clk);
241
242	return nvmem_unregister(otp->nvmem);
243}
244
245static struct platform_driver mxs_ocotp_driver = {
246	.probe = mxs_ocotp_probe,
247	.remove = mxs_ocotp_remove,
248	.driver = {
249		.name = "mxs-ocotp",
250		.of_match_table = mxs_ocotp_match,
251	},
252};
253
254module_platform_driver(mxs_ocotp_driver);
255MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
256MODULE_DESCRIPTION("driver for OCOTP in i.MX23/i.MX28");
257MODULE_LICENSE("GPL v2");