]>
Commit | Line | Data |
---|---|---|
03a69568 Z |
1 | /* |
2 | * Rockchip eFuse Driver | |
3 | * | |
4 | * Copyright (c) 2015 Rockchip Electronics Co. Ltd. | |
5 | * Author: Caesar Wang <[email protected]> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of version 2 of the GNU General Public License as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | */ | |
16 | ||
c37ff3fb CW |
17 | #include <linux/clk.h> |
18 | #include <linux/delay.h> | |
03a69568 Z |
19 | #include <linux/device.h> |
20 | #include <linux/io.h> | |
21 | #include <linux/module.h> | |
c37ff3fb CW |
22 | #include <linux/nvmem-provider.h> |
23 | #include <linux/slab.h> | |
03a69568 | 24 | #include <linux/of.h> |
02baff32 | 25 | #include <linux/of_platform.h> |
c37ff3fb | 26 | #include <linux/platform_device.h> |
03a69568 | 27 | |
02baff32 FX |
28 | #define RK3288_A_SHIFT 6 |
29 | #define RK3288_A_MASK 0x3ff | |
30 | #define RK3288_PGENB BIT(3) | |
31 | #define RK3288_LOAD BIT(2) | |
32 | #define RK3288_STROBE BIT(1) | |
33 | #define RK3288_CSB BIT(0) | |
34 | ||
9a479b08 FX |
35 | #define RK3328_SECURE_SIZES 96 |
36 | #define RK3328_INT_STATUS 0x0018 | |
37 | #define RK3328_DOUT 0x0020 | |
38 | #define RK3328_AUTO_CTRL 0x0024 | |
39 | #define RK3328_INT_FINISH BIT(0) | |
40 | #define RK3328_AUTO_ENB BIT(0) | |
41 | #define RK3328_AUTO_RD BIT(1) | |
42 | ||
02baff32 FX |
43 | #define RK3399_A_SHIFT 16 |
44 | #define RK3399_A_MASK 0x3ff | |
45 | #define RK3399_NBYTES 4 | |
46 | #define RK3399_STROBSFTSEL BIT(9) | |
47 | #define RK3399_RSB BIT(7) | |
48 | #define RK3399_PD BIT(5) | |
49 | #define RK3399_PGENB BIT(3) | |
50 | #define RK3399_LOAD BIT(2) | |
51 | #define RK3399_STROBE BIT(1) | |
52 | #define RK3399_CSB BIT(0) | |
53 | ||
54 | #define REG_EFUSE_CTRL 0x0000 | |
55 | #define REG_EFUSE_DOUT 0x0004 | |
03a69568 | 56 | |
c37ff3fb | 57 | struct rockchip_efuse_chip { |
03a69568 Z |
58 | struct device *dev; |
59 | void __iomem *base; | |
c37ff3fb | 60 | struct clk *clk; |
03a69568 Z |
61 | }; |
62 | ||
02baff32 FX |
63 | static int rockchip_rk3288_efuse_read(void *context, unsigned int offset, |
64 | void *val, size_t bytes) | |
03a69568 | 65 | { |
c37ff3fb | 66 | struct rockchip_efuse_chip *efuse = context; |
03a69568 Z |
67 | u8 *buf = val; |
68 | int ret; | |
69 | ||
c37ff3fb | 70 | ret = clk_prepare_enable(efuse->clk); |
03a69568 | 71 | if (ret < 0) { |
c37ff3fb | 72 | dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); |
03a69568 Z |
73 | return ret; |
74 | } | |
75 | ||
02baff32 | 76 | writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); |
03a69568 | 77 | udelay(1); |
cc907553 | 78 | while (bytes--) { |
c37ff3fb | 79 | writel(readl(efuse->base + REG_EFUSE_CTRL) & |
02baff32 | 80 | (~(RK3288_A_MASK << RK3288_A_SHIFT)), |
c37ff3fb CW |
81 | efuse->base + REG_EFUSE_CTRL); |
82 | writel(readl(efuse->base + REG_EFUSE_CTRL) | | |
02baff32 | 83 | ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), |
c37ff3fb | 84 | efuse->base + REG_EFUSE_CTRL); |
03a69568 | 85 | udelay(1); |
c37ff3fb | 86 | writel(readl(efuse->base + REG_EFUSE_CTRL) | |
02baff32 | 87 | RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); |
03a69568 | 88 | udelay(1); |
c37ff3fb CW |
89 | *buf++ = readb(efuse->base + REG_EFUSE_DOUT); |
90 | writel(readl(efuse->base + REG_EFUSE_CTRL) & | |
02baff32 FX |
91 | (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL); |
92 | udelay(1); | |
93 | } | |
94 | ||
95 | /* Switch to standby mode */ | |
96 | writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL); | |
97 | ||
98 | clk_disable_unprepare(efuse->clk); | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
9a479b08 FX |
103 | static int rockchip_rk3328_efuse_read(void *context, unsigned int offset, |
104 | void *val, size_t bytes) | |
105 | { | |
106 | struct rockchip_efuse_chip *efuse = context; | |
107 | unsigned int addr_start, addr_end, addr_offset, addr_len; | |
108 | u32 out_value, status; | |
109 | u8 *buf; | |
110 | int ret, i = 0; | |
111 | ||
112 | ret = clk_prepare_enable(efuse->clk); | |
113 | if (ret < 0) { | |
114 | dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); | |
115 | return ret; | |
116 | } | |
117 | ||
118 | /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */ | |
119 | offset += RK3328_SECURE_SIZES; | |
120 | addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; | |
121 | addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; | |
122 | addr_offset = offset % RK3399_NBYTES; | |
123 | addr_len = addr_end - addr_start; | |
124 | ||
125 | buf = kzalloc(sizeof(*buf) * addr_len * RK3399_NBYTES, GFP_KERNEL); | |
126 | if (!buf) { | |
127 | ret = -ENOMEM; | |
128 | goto nomem; | |
129 | } | |
130 | ||
131 | while (addr_len--) { | |
132 | writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | | |
133 | ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), | |
134 | efuse->base + RK3328_AUTO_CTRL); | |
135 | udelay(4); | |
136 | status = readl(efuse->base + RK3328_INT_STATUS); | |
137 | if (!(status & RK3328_INT_FINISH)) { | |
138 | ret = -EIO; | |
139 | goto err; | |
140 | } | |
141 | out_value = readl(efuse->base + RK3328_DOUT); | |
142 | writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS); | |
143 | ||
144 | memcpy(&buf[i], &out_value, RK3399_NBYTES); | |
145 | i += RK3399_NBYTES; | |
146 | } | |
147 | ||
148 | memcpy(val, buf + addr_offset, bytes); | |
149 | err: | |
150 | kfree(buf); | |
151 | nomem: | |
152 | clk_disable_unprepare(efuse->clk); | |
153 | ||
154 | return ret; | |
155 | } | |
156 | ||
02baff32 FX |
157 | static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, |
158 | void *val, size_t bytes) | |
159 | { | |
160 | struct rockchip_efuse_chip *efuse = context; | |
161 | unsigned int addr_start, addr_end, addr_offset, addr_len; | |
162 | u32 out_value; | |
163 | u8 *buf; | |
164 | int ret, i = 0; | |
165 | ||
166 | ret = clk_prepare_enable(efuse->clk); | |
167 | if (ret < 0) { | |
168 | dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); | |
169 | return ret; | |
170 | } | |
171 | ||
172 | addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; | |
173 | addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; | |
174 | addr_offset = offset % RK3399_NBYTES; | |
175 | addr_len = addr_end - addr_start; | |
176 | ||
177 | buf = kzalloc(sizeof(*buf) * addr_len * RK3399_NBYTES, GFP_KERNEL); | |
178 | if (!buf) { | |
179 | clk_disable_unprepare(efuse->clk); | |
180 | return -ENOMEM; | |
181 | } | |
182 | ||
183 | writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, | |
184 | efuse->base + REG_EFUSE_CTRL); | |
185 | udelay(1); | |
186 | while (addr_len--) { | |
187 | writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE | | |
188 | ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), | |
189 | efuse->base + REG_EFUSE_CTRL); | |
03a69568 | 190 | udelay(1); |
02baff32 FX |
191 | out_value = readl(efuse->base + REG_EFUSE_DOUT); |
192 | writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE), | |
193 | efuse->base + REG_EFUSE_CTRL); | |
194 | udelay(1); | |
195 | ||
196 | memcpy(&buf[i], &out_value, RK3399_NBYTES); | |
197 | i += RK3399_NBYTES; | |
03a69568 Z |
198 | } |
199 | ||
200 | /* Switch to standby mode */ | |
02baff32 FX |
201 | writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL); |
202 | ||
203 | memcpy(val, buf + addr_offset, bytes); | |
204 | ||
205 | kfree(buf); | |
03a69568 | 206 | |
c37ff3fb | 207 | clk_disable_unprepare(efuse->clk); |
03a69568 Z |
208 | |
209 | return 0; | |
210 | } | |
211 | ||
03a69568 Z |
212 | static struct nvmem_config econfig = { |
213 | .name = "rockchip-efuse", | |
cc907553 SK |
214 | .stride = 1, |
215 | .word_size = 1, | |
03a69568 Z |
216 | .read_only = true, |
217 | }; | |
218 | ||
219 | static const struct of_device_id rockchip_efuse_match[] = { | |
02baff32 FX |
220 | /* deprecated but kept around for dts binding compatibility */ |
221 | { | |
222 | .compatible = "rockchip,rockchip-efuse", | |
223 | .data = (void *)&rockchip_rk3288_efuse_read, | |
224 | }, | |
225 | { | |
226 | .compatible = "rockchip,rk3066a-efuse", | |
227 | .data = (void *)&rockchip_rk3288_efuse_read, | |
228 | }, | |
229 | { | |
230 | .compatible = "rockchip,rk3188-efuse", | |
231 | .data = (void *)&rockchip_rk3288_efuse_read, | |
232 | }, | |
820de1fb | 233 | { |
d6e4bd1b | 234 | .compatible = "rockchip,rk3228-efuse", |
820de1fb FX |
235 | .data = (void *)&rockchip_rk3288_efuse_read, |
236 | }, | |
02baff32 FX |
237 | { |
238 | .compatible = "rockchip,rk3288-efuse", | |
239 | .data = (void *)&rockchip_rk3288_efuse_read, | |
240 | }, | |
7a15cf2a RP |
241 | { |
242 | .compatible = "rockchip,rk3368-efuse", | |
243 | .data = (void *)&rockchip_rk3288_efuse_read, | |
244 | }, | |
9a479b08 FX |
245 | { |
246 | .compatible = "rockchip,rk3328-efuse", | |
247 | .data = (void *)&rockchip_rk3328_efuse_read, | |
248 | }, | |
02baff32 FX |
249 | { |
250 | .compatible = "rockchip,rk3399-efuse", | |
251 | .data = (void *)&rockchip_rk3399_efuse_read, | |
252 | }, | |
03a69568 Z |
253 | { /* sentinel */}, |
254 | }; | |
255 | MODULE_DEVICE_TABLE(of, rockchip_efuse_match); | |
256 | ||
7e532f79 | 257 | static int rockchip_efuse_probe(struct platform_device *pdev) |
03a69568 | 258 | { |
03a69568 Z |
259 | struct resource *res; |
260 | struct nvmem_device *nvmem; | |
c37ff3fb | 261 | struct rockchip_efuse_chip *efuse; |
02baff32 FX |
262 | const struct of_device_id *match; |
263 | struct device *dev = &pdev->dev; | |
264 | ||
265 | match = of_match_device(dev->driver->of_match_table, dev); | |
266 | if (!match || !match->data) { | |
267 | dev_err(dev, "failed to get match data\n"); | |
268 | return -EINVAL; | |
269 | } | |
03a69568 | 270 | |
c37ff3fb CW |
271 | efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), |
272 | GFP_KERNEL); | |
273 | if (!efuse) | |
274 | return -ENOMEM; | |
03a69568 | 275 | |
c37ff3fb CW |
276 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
277 | efuse->base = devm_ioremap_resource(&pdev->dev, res); | |
278 | if (IS_ERR(efuse->base)) | |
279 | return PTR_ERR(efuse->base); | |
03a69568 | 280 | |
c37ff3fb CW |
281 | efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse"); |
282 | if (IS_ERR(efuse->clk)) | |
283 | return PTR_ERR(efuse->clk); | |
03a69568 | 284 | |
c37ff3fb | 285 | efuse->dev = &pdev->dev; |
32277723 FX |
286 | if (of_property_read_u32(dev->of_node, "rockchip,efuse-size", |
287 | &econfig.size)) | |
288 | econfig.size = resource_size(res); | |
02baff32 | 289 | econfig.reg_read = match->data; |
cc907553 | 290 | econfig.priv = efuse; |
c37ff3fb | 291 | econfig.dev = efuse->dev; |
03a69568 Z |
292 | nvmem = nvmem_register(&econfig); |
293 | if (IS_ERR(nvmem)) | |
294 | return PTR_ERR(nvmem); | |
295 | ||
296 | platform_set_drvdata(pdev, nvmem); | |
297 | ||
298 | return 0; | |
299 | } | |
300 | ||
7e532f79 | 301 | static int rockchip_efuse_remove(struct platform_device *pdev) |
03a69568 Z |
302 | { |
303 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | |
304 | ||
305 | return nvmem_unregister(nvmem); | |
306 | } | |
307 | ||
308 | static struct platform_driver rockchip_efuse_driver = { | |
309 | .probe = rockchip_efuse_probe, | |
310 | .remove = rockchip_efuse_remove, | |
311 | .driver = { | |
312 | .name = "rockchip-efuse", | |
313 | .of_match_table = rockchip_efuse_match, | |
314 | }, | |
315 | }; | |
316 | ||
317 | module_platform_driver(rockchip_efuse_driver); | |
318 | MODULE_DESCRIPTION("rockchip_efuse driver"); | |
319 | MODULE_LICENSE("GPL v2"); |