Blame view

drivers/nvmem/mxs-ocotp.c 4.03 KB
c942fddf8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
c01e9a11a   Stefan Wahren   nvmem: add driver...
2
3
4
5
6
7
  /*
   * Freescale MXS On-Chip OTP driver
   *
   * Copyright (C) 2015 Stefan Wahren <stefan.wahren@i2se.com>
   *
   * Based on the driver from Huang Shijie and Christoph G. Baumann
c01e9a11a   Stefan Wahren   nvmem: add driver...
8
9
10
11
12
13
14
15
16
17
   */
  #include <linux/clk.h>
  #include <linux/delay.h>
  #include <linux/device.h>
  #include <linux/err.h>
  #include <linux/io.h>
  #include <linux/module.h>
  #include <linux/nvmem-provider.h>
  #include <linux/of_device.h>
  #include <linux/platform_device.h>
c01e9a11a   Stefan Wahren   nvmem: add driver...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  #include <linux/slab.h>
  #include <linux/stmp_device.h>
  
  /* OCOTP registers and bits */
  
  #define BM_OCOTP_CTRL_RD_BANK_OPEN	BIT(12)
  #define BM_OCOTP_CTRL_ERROR		BIT(9)
  #define BM_OCOTP_CTRL_BUSY		BIT(8)
  
  #define OCOTP_TIMEOUT		10000
  #define OCOTP_DATA_OFFSET	0x20
  
  struct mxs_ocotp {
  	struct clk *clk;
  	void __iomem *base;
  	struct nvmem_device *nvmem;
  };
  
  static int mxs_ocotp_wait(struct mxs_ocotp *otp)
  {
  	int timeout = OCOTP_TIMEOUT;
  	unsigned int status = 0;
  
  	while (timeout--) {
  		status = readl(otp->base);
  
  		if (!(status & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)))
  			break;
  
  		cpu_relax();
  	}
  
  	if (status & BM_OCOTP_CTRL_BUSY)
  		return -EBUSY;
  	else if (status & BM_OCOTP_CTRL_ERROR)
  		return -EIO;
  
  	return 0;
  }
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
57
58
  static int mxs_ocotp_read(void *context, unsigned int offset,
  			  void *val, size_t bytes)
c01e9a11a   Stefan Wahren   nvmem: add driver...
59
60
  {
  	struct mxs_ocotp *otp = context;
c01e9a11a   Stefan Wahren   nvmem: add driver...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  	u32 *buf = val;
  	int ret;
  
  	ret = clk_enable(otp->clk);
  	if (ret)
  		return ret;
  
  	writel(BM_OCOTP_CTRL_ERROR, otp->base + STMP_OFFSET_REG_CLR);
  
  	ret = mxs_ocotp_wait(otp);
  	if (ret)
  		goto disable_clk;
  
  	/* open OCOTP banks for read */
  	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_SET);
  
  	/* approximately wait 33 hclk cycles */
  	udelay(1);
  
  	ret = mxs_ocotp_wait(otp);
  	if (ret)
  		goto close_banks;
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
83
  	while (bytes) {
c01e9a11a   Stefan Wahren   nvmem: add driver...
84
85
  		if ((offset < OCOTP_DATA_OFFSET) || (offset % 16)) {
  			/* fill up non-data register */
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
86
  			*buf++ = 0;
c01e9a11a   Stefan Wahren   nvmem: add driver...
87
  		} else {
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
88
  			*buf++ = readl(otp->base + offset);
c01e9a11a   Stefan Wahren   nvmem: add driver...
89
  		}
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
90
91
  		bytes -= 4;
  		offset += 4;
c01e9a11a   Stefan Wahren   nvmem: add driver...
92
93
94
95
96
97
98
99
100
101
102
  	}
  
  close_banks:
  	/* close banks for power saving */
  	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_CLR);
  
  disable_clk:
  	clk_disable(otp->clk);
  
  	return ret;
  }
c01e9a11a   Stefan Wahren   nvmem: add driver...
103
104
  static struct nvmem_config ocotp_config = {
  	.name = "mxs-ocotp",
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
105
106
  	.stride = 16,
  	.word_size = 4,
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
107
  	.reg_read = mxs_ocotp_read,
c01e9a11a   Stefan Wahren   nvmem: add driver...
108
  };
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
109
110
  struct mxs_data {
  	int size;
c01e9a11a   Stefan Wahren   nvmem: add driver...
111
  };
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
112
113
  static const struct mxs_data imx23_data = {
  	.size = 0x220,
c01e9a11a   Stefan Wahren   nvmem: add driver...
114
  };
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
115
116
  static const struct mxs_data imx28_data = {
  	.size = 0x2a0,
c01e9a11a   Stefan Wahren   nvmem: add driver...
117
118
119
  };
  
  static const struct of_device_id mxs_ocotp_match[] = {
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
120
121
  	{ .compatible = "fsl,imx23-ocotp", .data = &imx23_data },
  	{ .compatible = "fsl,imx28-ocotp", .data = &imx28_data },
c01e9a11a   Stefan Wahren   nvmem: add driver...
122
123
124
  	{ /* sentinel */},
  };
  MODULE_DEVICE_TABLE(of, mxs_ocotp_match);
bbde5709e   Anson Huang   nvmem: mxs-ocotp:...
125
126
127
128
  static void mxs_ocotp_action(void *data)
  {
  	clk_unprepare(data);
  }
c01e9a11a   Stefan Wahren   nvmem: add driver...
129
130
131
  static int mxs_ocotp_probe(struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
132
  	const struct mxs_data *data;
c01e9a11a   Stefan Wahren   nvmem: add driver...
133
  	struct mxs_ocotp *otp;
c01e9a11a   Stefan Wahren   nvmem: add driver...
134
  	const struct of_device_id *match;
c01e9a11a   Stefan Wahren   nvmem: add driver...
135
136
137
138
139
140
141
142
143
  	int ret;
  
  	match = of_match_device(dev->driver->of_match_table, dev);
  	if (!match || !match->data)
  		return -EINVAL;
  
  	otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
  	if (!otp)
  		return -ENOMEM;
794a1e229   Anson Huang   nvmem: mxs-ocotp:...
144
  	otp->base = devm_platform_ioremap_resource(pdev, 0);
c01e9a11a   Stefan Wahren   nvmem: add driver...
145
146
147
148
149
150
151
152
153
154
155
156
157
  	if (IS_ERR(otp->base))
  		return PTR_ERR(otp->base);
  
  	otp->clk = devm_clk_get(&pdev->dev, NULL);
  	if (IS_ERR(otp->clk))
  		return PTR_ERR(otp->clk);
  
  	ret = clk_prepare(otp->clk);
  	if (ret < 0) {
  		dev_err(dev, "failed to prepare clk: %d
  ", ret);
  		return ret;
  	}
bbde5709e   Anson Huang   nvmem: mxs-ocotp:...
158
159
160
  	ret = devm_add_action_or_reset(&pdev->dev, mxs_ocotp_action, otp->clk);
  	if (ret)
  		return ret;
7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
161
  	data = match->data;
c01e9a11a   Stefan Wahren   nvmem: add driver...
162

7d8867d71   Srinivas Kandagatla   nvmem: mxs-ocotp:...
163
164
  	ocotp_config.size = data->size;
  	ocotp_config.priv = otp;
c01e9a11a   Stefan Wahren   nvmem: add driver...
165
  	ocotp_config.dev = dev;
7d9f9f24f   Bartosz Golaszewski   nvmem: mxs-ocotp:...
166
  	otp->nvmem = devm_nvmem_register(dev, &ocotp_config);
bbde5709e   Anson Huang   nvmem: mxs-ocotp:...
167
168
  	if (IS_ERR(otp->nvmem))
  		return PTR_ERR(otp->nvmem);
c01e9a11a   Stefan Wahren   nvmem: add driver...
169
170
171
172
  
  	platform_set_drvdata(pdev, otp);
  
  	return 0;
c01e9a11a   Stefan Wahren   nvmem: add driver...
173
174
175
176
  }
  
  static struct platform_driver mxs_ocotp_driver = {
  	.probe = mxs_ocotp_probe,
c01e9a11a   Stefan Wahren   nvmem: add driver...
177
178
179
180
181
182
183
  	.driver = {
  		.name = "mxs-ocotp",
  		.of_match_table = mxs_ocotp_match,
  	},
  };
  
  module_platform_driver(mxs_ocotp_driver);
5fb812293   Stefan Wahren   nvmem: mxs-ocotp:...
184
  MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net");
c01e9a11a   Stefan Wahren   nvmem: add driver...
185
186
  MODULE_DESCRIPTION("driver for OCOTP in i.MX23/i.MX28");
  MODULE_LICENSE("GPL v2");