Commit | Line | Data |
---|---|---|
ded1b7fc FG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * STM32 Factory-programmed memory read access driver | |
4 | * | |
5 | * Copyright (C) 2017, STMicroelectronics - All Rights Reserved | |
6 | * Author: Fabrice Gasnier <fabrice.gasnier@st.com> for STMicroelectronics. | |
7 | */ | |
8 | ||
7c1cd8fd | 9 | #include <linux/arm-smccc.h> |
ded1b7fc FG |
10 | #include <linux/io.h> |
11 | #include <linux/module.h> | |
12 | #include <linux/nvmem-provider.h> | |
13 | #include <linux/of_device.h> | |
6a0bc352 PD |
14 | #include <linux/tee_drv.h> |
15 | ||
16 | #include "stm32-bsec-optee-ta.h" | |
ded1b7fc | 17 | |
7c1cd8fd FG |
18 | /* BSEC secure service access from non-secure */ |
19 | #define STM32_SMC_BSEC 0x82001003 | |
20 | #define STM32_SMC_READ_SHADOW 0x01 | |
21 | #define STM32_SMC_PROG_OTP 0x02 | |
22 | #define STM32_SMC_WRITE_SHADOW 0x03 | |
23 | #define STM32_SMC_READ_OTP 0x04 | |
24 | ||
06aac0e1 | 25 | /* shadow registers offset */ |
7c1cd8fd FG |
26 | #define STM32MP15_BSEC_DATA0 0x200 |
27 | ||
7c1cd8fd FG |
28 | struct stm32_romem_cfg { |
29 | int size; | |
fbfc4ca4 | 30 | u8 lower; |
6a0bc352 | 31 | bool ta; |
7c1cd8fd FG |
32 | }; |
33 | ||
ded1b7fc FG |
34 | struct stm32_romem_priv { |
35 | void __iomem *base; | |
36 | struct nvmem_config cfg; | |
fbfc4ca4 | 37 | u8 lower; |
6a0bc352 | 38 | struct tee_context *ctx; |
ded1b7fc FG |
39 | }; |
40 | ||
41 | static int stm32_romem_read(void *context, unsigned int offset, void *buf, | |
42 | size_t bytes) | |
43 | { | |
44 | struct stm32_romem_priv *priv = context; | |
45 | u8 *buf8 = buf; | |
46 | int i; | |
47 | ||
48 | for (i = offset; i < offset + bytes; i++) | |
49 | *buf8++ = readb_relaxed(priv->base + i); | |
50 | ||
51 | return 0; | |
52 | } | |
53 | ||
7c1cd8fd FG |
54 | static int stm32_bsec_smc(u8 op, u32 otp, u32 data, u32 *result) |
55 | { | |
56 | #if IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) | |
57 | struct arm_smccc_res res; | |
58 | ||
59 | arm_smccc_smc(STM32_SMC_BSEC, op, otp, data, 0, 0, 0, 0, &res); | |
60 | if (res.a0) | |
61 | return -EIO; | |
62 | ||
63 | if (result) | |
64 | *result = (u32)res.a1; | |
65 | ||
66 | return 0; | |
67 | #else | |
68 | return -ENXIO; | |
69 | #endif | |
70 | } | |
71 | ||
72 | static int stm32_bsec_read(void *context, unsigned int offset, void *buf, | |
73 | size_t bytes) | |
74 | { | |
75 | struct stm32_romem_priv *priv = context; | |
76 | struct device *dev = priv->cfg.dev; | |
77 | u32 roffset, rbytes, val; | |
78 | u8 *buf8 = buf, *val8 = (u8 *)&val; | |
79 | int i, j = 0, ret, skip_bytes, size; | |
80 | ||
81 | /* Round unaligned access to 32-bits */ | |
82 | roffset = rounddown(offset, 4); | |
83 | skip_bytes = offset & 0x3; | |
84 | rbytes = roundup(bytes + skip_bytes, 4); | |
85 | ||
86 | if (roffset + rbytes > priv->cfg.size) | |
87 | return -EINVAL; | |
88 | ||
89 | for (i = roffset; (i < roffset + rbytes); i += 4) { | |
90 | u32 otp = i >> 2; | |
91 | ||
fbfc4ca4 | 92 | if (otp < priv->lower) { |
7c1cd8fd FG |
93 | /* read lower data from shadow registers */ |
94 | val = readl_relaxed( | |
95 | priv->base + STM32MP15_BSEC_DATA0 + i); | |
96 | } else { | |
97 | ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, otp, 0, | |
98 | &val); | |
99 | if (ret) { | |
100 | dev_err(dev, "Can't read data%d (%d)\n", otp, | |
101 | ret); | |
102 | return ret; | |
103 | } | |
104 | } | |
105 | /* skip first bytes in case of unaligned read */ | |
106 | if (skip_bytes) | |
107 | size = min(bytes, (size_t)(4 - skip_bytes)); | |
108 | else | |
109 | size = min(bytes, (size_t)4); | |
110 | memcpy(&buf8[j], &val8[skip_bytes], size); | |
111 | bytes -= size; | |
112 | j += size; | |
113 | skip_bytes = 0; | |
114 | } | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | static int stm32_bsec_write(void *context, unsigned int offset, void *buf, | |
120 | size_t bytes) | |
121 | { | |
122 | struct stm32_romem_priv *priv = context; | |
123 | struct device *dev = priv->cfg.dev; | |
124 | u32 *buf32 = buf; | |
125 | int ret, i; | |
126 | ||
127 | /* Allow only writing complete 32-bits aligned words */ | |
128 | if ((bytes % 4) || (offset % 4)) | |
129 | return -EINVAL; | |
130 | ||
131 | for (i = offset; i < offset + bytes; i += 4) { | |
132 | ret = stm32_bsec_smc(STM32_SMC_PROG_OTP, i >> 2, *buf32++, | |
133 | NULL); | |
134 | if (ret) { | |
135 | dev_err(dev, "Can't write data%d (%d)\n", i >> 2, ret); | |
136 | return ret; | |
137 | } | |
138 | } | |
139 | ||
d61784e6 PD |
140 | if (offset + bytes >= priv->lower * 4) |
141 | dev_warn(dev, "Update of upper OTPs with ECC protection (word programming, only once)\n"); | |
142 | ||
7c1cd8fd FG |
143 | return 0; |
144 | } | |
145 | ||
6a0bc352 PD |
146 | static int stm32_bsec_pta_read(void *context, unsigned int offset, void *buf, |
147 | size_t bytes) | |
148 | { | |
149 | struct stm32_romem_priv *priv = context; | |
150 | ||
151 | return stm32_bsec_optee_ta_read(priv->ctx, offset, buf, bytes); | |
152 | } | |
153 | ||
154 | static int stm32_bsec_pta_write(void *context, unsigned int offset, void *buf, | |
155 | size_t bytes) | |
156 | { | |
157 | struct stm32_romem_priv *priv = context; | |
158 | ||
159 | return stm32_bsec_optee_ta_write(priv->ctx, priv->lower, offset, buf, bytes); | |
160 | } | |
161 | ||
df2f34ef PD |
162 | static bool stm32_bsec_smc_check(void) |
163 | { | |
164 | u32 val; | |
165 | int ret; | |
166 | ||
167 | /* check that the OP-TEE support the BSEC SMC (legacy mode) */ | |
168 | ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, 0, 0, &val); | |
169 | ||
170 | return !ret; | |
171 | } | |
172 | ||
173 | static bool optee_presence_check(void) | |
174 | { | |
175 | struct device_node *np; | |
176 | bool tee_detected = false; | |
177 | ||
178 | /* check that the OP-TEE node is present and available. */ | |
179 | np = of_find_compatible_node(NULL, NULL, "linaro,optee-tz"); | |
180 | if (np && of_device_is_available(np)) | |
181 | tee_detected = true; | |
182 | of_node_put(np); | |
183 | ||
184 | return tee_detected; | |
185 | } | |
186 | ||
ded1b7fc FG |
187 | static int stm32_romem_probe(struct platform_device *pdev) |
188 | { | |
7c1cd8fd | 189 | const struct stm32_romem_cfg *cfg; |
ded1b7fc FG |
190 | struct device *dev = &pdev->dev; |
191 | struct stm32_romem_priv *priv; | |
192 | struct resource *res; | |
6a0bc352 | 193 | int rc; |
ded1b7fc FG |
194 | |
195 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
196 | if (!priv) | |
197 | return -ENOMEM; | |
198 | ||
199 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
200 | priv->base = devm_ioremap_resource(dev, res); | |
201 | if (IS_ERR(priv->base)) | |
202 | return PTR_ERR(priv->base); | |
203 | ||
204 | priv->cfg.name = "stm32-romem"; | |
ded1b7fc FG |
205 | priv->cfg.word_size = 1; |
206 | priv->cfg.stride = 1; | |
ded1b7fc FG |
207 | priv->cfg.dev = dev; |
208 | priv->cfg.priv = priv; | |
209 | priv->cfg.owner = THIS_MODULE; | |
a3816a7d | 210 | priv->cfg.type = NVMEM_TYPE_OTP; |
ded1b7fc | 211 | |
fbfc4ca4 PD |
212 | priv->lower = 0; |
213 | ||
7c1cd8fd FG |
214 | cfg = (const struct stm32_romem_cfg *) |
215 | of_match_device(dev->driver->of_match_table, dev)->data; | |
216 | if (!cfg) { | |
217 | priv->cfg.read_only = true; | |
218 | priv->cfg.size = resource_size(res); | |
219 | priv->cfg.reg_read = stm32_romem_read; | |
220 | } else { | |
221 | priv->cfg.size = cfg->size; | |
fbfc4ca4 | 222 | priv->lower = cfg->lower; |
df2f34ef | 223 | if (cfg->ta || optee_presence_check()) { |
6a0bc352 | 224 | rc = stm32_bsec_optee_ta_open(&priv->ctx); |
df2f34ef PD |
225 | if (rc) { |
226 | /* wait for OP-TEE client driver to be up and ready */ | |
227 | if (rc == -EPROBE_DEFER) | |
228 | return -EPROBE_DEFER; | |
229 | /* BSEC PTA is required or SMC not supported */ | |
230 | if (cfg->ta || !stm32_bsec_smc_check()) | |
231 | return rc; | |
232 | } | |
6a0bc352 PD |
233 | } |
234 | if (priv->ctx) { | |
235 | rc = devm_add_action_or_reset(dev, stm32_bsec_optee_ta_close, priv->ctx); | |
236 | if (rc) { | |
237 | dev_err(dev, "devm_add_action_or_reset() failed (%d)\n", rc); | |
238 | return rc; | |
239 | } | |
240 | priv->cfg.reg_read = stm32_bsec_pta_read; | |
241 | priv->cfg.reg_write = stm32_bsec_pta_write; | |
242 | } else { | |
243 | priv->cfg.reg_read = stm32_bsec_read; | |
244 | priv->cfg.reg_write = stm32_bsec_write; | |
245 | } | |
7c1cd8fd FG |
246 | } |
247 | ||
ded1b7fc FG |
248 | return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg)); |
249 | } | |
250 | ||
fbfc4ca4 | 251 | /* |
6a0bc352 | 252 | * STM32MP15/13 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits) |
fbfc4ca4 PD |
253 | * => 96 x 32-bits data words |
254 | * - Lower: 1K bits, 2:1 redundancy, incremental bit programming | |
255 | * => 32 (x 32-bits) lower shadow registers = words 0 to 31 | |
256 | * - Upper: 2K bits, ECC protection, word programming only | |
257 | * => 64 (x 32-bits) = words 32 to 95 | |
258 | */ | |
7c1cd8fd | 259 | static const struct stm32_romem_cfg stm32mp15_bsec_cfg = { |
fbfc4ca4 PD |
260 | .size = 384, |
261 | .lower = 32, | |
6a0bc352 PD |
262 | .ta = false, |
263 | }; | |
264 | ||
265 | static const struct stm32_romem_cfg stm32mp13_bsec_cfg = { | |
266 | .size = 384, | |
267 | .lower = 32, | |
268 | .ta = true, | |
7c1cd8fd FG |
269 | }; |
270 | ||
ded1b7fc | 271 | static const struct of_device_id stm32_romem_of_match[] = { |
7c1cd8fd FG |
272 | { .compatible = "st,stm32f4-otp", }, { |
273 | .compatible = "st,stm32mp15-bsec", | |
274 | .data = (void *)&stm32mp15_bsec_cfg, | |
275 | }, { | |
6a0bc352 PD |
276 | .compatible = "st,stm32mp13-bsec", |
277 | .data = (void *)&stm32mp13_bsec_cfg, | |
7c1cd8fd | 278 | }, |
6a0bc352 | 279 | { /* sentinel */ }, |
ded1b7fc FG |
280 | }; |
281 | MODULE_DEVICE_TABLE(of, stm32_romem_of_match); | |
282 | ||
283 | static struct platform_driver stm32_romem_driver = { | |
284 | .probe = stm32_romem_probe, | |
285 | .driver = { | |
286 | .name = "stm32-romem", | |
287 | .of_match_table = of_match_ptr(stm32_romem_of_match), | |
288 | }, | |
289 | }; | |
290 | module_platform_driver(stm32_romem_driver); | |
291 | ||
292 | MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); | |
293 | MODULE_DESCRIPTION("STMicroelectronics STM32 RO-MEM"); | |
294 | MODULE_ALIAS("platform:nvmem-stm32-romem"); | |
295 | MODULE_LICENSE("GPL v2"); |