diff options
author | Juergen Borleis <jbe@pengutronix.de> | 2015-04-27 09:59:49 -0400 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2015-06-19 14:03:21 -0400 |
commit | c7e9bbe022c7bee57d9e14b42a7c3732da8db558 (patch) | |
tree | 7b81b55261e6ef1212646f6657262d2e762f7596 /drivers/rtc | |
parent | 3ba3fab765beecd599d8e8e00dc2ed4306518dfd (diff) |
rtc: imxdi: add the unit recovery code
This code is required to recover the unit from a security violation.
Hopefully this code can recover the unit from a hardware related invalid
state as well.
Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
Signed-off-by: Robert Schwebel <rsc@pengutronix.de>
[rsc: got NDA clearance from Freescale]
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-imxdi.c | 317 |
1 files changed, 279 insertions, 38 deletions
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index 63ca52b2029b..3d7f0390170e 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c | |||
@@ -173,6 +173,281 @@ struct imxdi_dev { | |||
173 | */ | 173 | */ |
174 | 174 | ||
175 | /* | 175 | /* |
176 | * Do a write into the unit without interrupt support. | ||
177 | * We do not need to check the WEF here, because the only reason this kind of | ||
178 | * write error can happen is if we write to the unit twice within the 122 us | ||
179 | * interval. This cannot happen, since we are using this function only while | ||
180 | * setting up the unit. | ||
181 | */ | ||
182 | static void di_write_busy_wait(const struct imxdi_dev *imxdi, u32 val, | ||
183 | unsigned reg) | ||
184 | { | ||
185 | /* do the register write */ | ||
186 | writel(val, imxdi->ioaddr + reg); | ||
187 | |||
188 | /* | ||
189 | * now it takes four 32,768 kHz clock cycles to take | ||
190 | * the change into effect = 122 us | ||
191 | */ | ||
192 | usleep_range(130, 200); | ||
193 | } | ||
194 | |||
195 | static void di_report_tamper_info(struct imxdi_dev *imxdi, u32 dsr) | ||
196 | { | ||
197 | u32 dtcr; | ||
198 | |||
199 | dtcr = readl(imxdi->ioaddr + DTCR); | ||
200 | |||
201 | dev_emerg(&imxdi->pdev->dev, "DryIce tamper event detected\n"); | ||
202 | /* the following flags force a transition into the "FAILURE STATE" */ | ||
203 | if (dsr & DSR_VTD) | ||
204 | dev_emerg(&imxdi->pdev->dev, "%sVoltage Tamper Event\n", | ||
205 | dtcr & DTCR_VTE ? "" : "Spurious "); | ||
206 | |||
207 | if (dsr & DSR_CTD) | ||
208 | dev_emerg(&imxdi->pdev->dev, "%s32768 Hz Clock Tamper Event\n", | ||
209 | dtcr & DTCR_CTE ? "" : "Spurious "); | ||
210 | |||
211 | if (dsr & DSR_TTD) | ||
212 | dev_emerg(&imxdi->pdev->dev, "%sTemperature Tamper Event\n", | ||
213 | dtcr & DTCR_TTE ? "" : "Spurious "); | ||
214 | |||
215 | if (dsr & DSR_SAD) | ||
216 | dev_emerg(&imxdi->pdev->dev, | ||
217 | "%sSecure Controller Alarm Event\n", | ||
218 | dtcr & DTCR_SAIE ? "" : "Spurious "); | ||
219 | |||
220 | if (dsr & DSR_EBD) | ||
221 | dev_emerg(&imxdi->pdev->dev, "%sExternal Boot Tamper Event\n", | ||
222 | dtcr & DTCR_EBE ? "" : "Spurious "); | ||
223 | |||
224 | if (dsr & DSR_ETAD) | ||
225 | dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper A Event\n", | ||
226 | dtcr & DTCR_ETAE ? "" : "Spurious "); | ||
227 | |||
228 | if (dsr & DSR_ETBD) | ||
229 | dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper B Event\n", | ||
230 | dtcr & DTCR_ETBE ? "" : "Spurious "); | ||
231 | |||
232 | if (dsr & DSR_WTD) | ||
233 | dev_emerg(&imxdi->pdev->dev, "%sWire-mesh Tamper Event\n", | ||
234 | dtcr & DTCR_WTE ? "" : "Spurious "); | ||
235 | |||
236 | if (dsr & DSR_MCO) | ||
237 | dev_emerg(&imxdi->pdev->dev, | ||
238 | "%sMonotonic-counter Overflow Event\n", | ||
239 | dtcr & DTCR_MOE ? "" : "Spurious "); | ||
240 | |||
241 | if (dsr & DSR_TCO) | ||
242 | dev_emerg(&imxdi->pdev->dev, "%sTimer-counter Overflow Event\n", | ||
243 | dtcr & DTCR_TOE ? "" : "Spurious "); | ||
244 | } | ||
245 | |||
246 | static void di_what_is_to_be_done(struct imxdi_dev *imxdi, | ||
247 | const char *power_supply) | ||
248 | { | ||
249 | dev_emerg(&imxdi->pdev->dev, "Please cycle the %s power supply in order to get the DryIce/RTC unit working again\n", | ||
250 | power_supply); | ||
251 | } | ||
252 | |||
253 | static int di_handle_failure_state(struct imxdi_dev *imxdi, u32 dsr) | ||
254 | { | ||
255 | u32 dcr; | ||
256 | |||
257 | dev_dbg(&imxdi->pdev->dev, "DSR register reports: %08X\n", dsr); | ||
258 | |||
259 | /* report the cause */ | ||
260 | di_report_tamper_info(imxdi, dsr); | ||
261 | |||
262 | dcr = readl(imxdi->ioaddr + DCR); | ||
263 | |||
264 | if (dcr & DCR_FSHL) { | ||
265 | /* we are out of luck */ | ||
266 | di_what_is_to_be_done(imxdi, "battery"); | ||
267 | return -ENODEV; | ||
268 | } | ||
269 | /* | ||
270 | * with the next SYSTEM POR we will transit from the "FAILURE STATE" | ||
271 | * into the "NON-VALID STATE" + "FAILURE STATE" | ||
272 | */ | ||
273 | di_what_is_to_be_done(imxdi, "main"); | ||
274 | |||
275 | return -ENODEV; | ||
276 | } | ||
277 | |||
278 | static int di_handle_valid_state(struct imxdi_dev *imxdi, u32 dsr) | ||
279 | { | ||
280 | /* initialize alarm */ | ||
281 | di_write_busy_wait(imxdi, DCAMR_UNSET, DCAMR); | ||
282 | di_write_busy_wait(imxdi, 0, DCALR); | ||
283 | |||
284 | /* clear alarm flag */ | ||
285 | if (dsr & DSR_CAF) | ||
286 | di_write_busy_wait(imxdi, DSR_CAF, DSR); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int di_handle_invalid_state(struct imxdi_dev *imxdi, u32 dsr) | ||
292 | { | ||
293 | u32 dcr, sec; | ||
294 | |||
295 | /* | ||
296 | * lets disable all sources which can force the DryIce unit into | ||
297 | * the "FAILURE STATE" for now | ||
298 | */ | ||
299 | di_write_busy_wait(imxdi, 0x00000000, DTCR); | ||
300 | /* and lets protect them at runtime from any change */ | ||
301 | di_write_busy_wait(imxdi, DCR_TDCSL, DCR); | ||
302 | |||
303 | sec = readl(imxdi->ioaddr + DTCMR); | ||
304 | if (sec != 0) | ||
305 | dev_warn(&imxdi->pdev->dev, | ||
306 | "The security violation has happend at %u seconds\n", | ||
307 | sec); | ||
308 | /* | ||
309 | * the timer cannot be set/modified if | ||
310 | * - the TCHL or TCSL bit is set in DCR | ||
311 | */ | ||
312 | dcr = readl(imxdi->ioaddr + DCR); | ||
313 | if (!(dcr & DCR_TCE)) { | ||
314 | if (dcr & DCR_TCHL) { | ||
315 | /* we are out of luck */ | ||
316 | di_what_is_to_be_done(imxdi, "battery"); | ||
317 | return -ENODEV; | ||
318 | } | ||
319 | if (dcr & DCR_TCSL) { | ||
320 | di_what_is_to_be_done(imxdi, "main"); | ||
321 | return -ENODEV; | ||
322 | } | ||
323 | } | ||
324 | /* | ||
325 | * - the timer counter stops/is stopped if | ||
326 | * - its overflow flag is set (TCO in DSR) | ||
327 | * -> clear overflow bit to make it count again | ||
328 | * - NVF is set in DSR | ||
329 | * -> clear non-valid bit to make it count again | ||
330 | * - its TCE (DCR) is cleared | ||
331 | * -> set TCE to make it count | ||
332 | * - it was never set before | ||
333 | * -> write a time into it (required again if the NVF was set) | ||
334 | */ | ||
335 | /* state handled */ | ||
336 | di_write_busy_wait(imxdi, DSR_NVF, DSR); | ||
337 | /* clear overflow flag */ | ||
338 | di_write_busy_wait(imxdi, DSR_TCO, DSR); | ||
339 | /* enable the counter */ | ||
340 | di_write_busy_wait(imxdi, dcr | DCR_TCE, DCR); | ||
341 | /* set and trigger it to make it count */ | ||
342 | di_write_busy_wait(imxdi, sec, DTCMR); | ||
343 | |||
344 | /* now prepare for the valid state */ | ||
345 | return di_handle_valid_state(imxdi, __raw_readl(imxdi->ioaddr + DSR)); | ||
346 | } | ||
347 | |||
348 | static int di_handle_invalid_and_failure_state(struct imxdi_dev *imxdi, u32 dsr) | ||
349 | { | ||
350 | u32 dcr; | ||
351 | |||
352 | /* | ||
353 | * now we must first remove the tamper sources in order to get the | ||
354 | * device out of the "FAILURE STATE" | ||
355 | * To disable any of the following sources we need to modify the DTCR | ||
356 | */ | ||
357 | if (dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD | | ||
358 | DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)) { | ||
359 | dcr = __raw_readl(imxdi->ioaddr + DCR); | ||
360 | if (dcr & DCR_TDCHL) { | ||
361 | /* | ||
362 | * the tamper register is locked. We cannot disable the | ||
363 | * tamper detection. The TDCHL can only be reset by a | ||
364 | * DRYICE POR, but we cannot force a DRYICE POR in | ||
365 | * softwere because we are still in "FAILURE STATE". | ||
366 | * We need a DRYICE POR via battery power cycling.... | ||
367 | */ | ||
368 | /* | ||
369 | * out of luck! | ||
370 | * we cannot disable them without a DRYICE POR | ||
371 | */ | ||
372 | di_what_is_to_be_done(imxdi, "battery"); | ||
373 | return -ENODEV; | ||
374 | } | ||
375 | if (dcr & DCR_TDCSL) { | ||
376 | /* a soft lock can be removed by a SYSTEM POR */ | ||
377 | di_what_is_to_be_done(imxdi, "main"); | ||
378 | return -ENODEV; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | /* disable all sources */ | ||
383 | di_write_busy_wait(imxdi, 0x00000000, DTCR); | ||
384 | |||
385 | /* clear the status bits now */ | ||
386 | di_write_busy_wait(imxdi, dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | | ||
387 | DSR_EBD | DSR_SAD | DSR_TTD | DSR_CTD | DSR_VTD | | ||
388 | DSR_MCO | DSR_TCO), DSR); | ||
389 | |||
390 | dsr = readl(imxdi->ioaddr + DSR); | ||
391 | if ((dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF | | ||
392 | DSR_WCF | DSR_WEF)) != 0) | ||
393 | dev_warn(&imxdi->pdev->dev, | ||
394 | "There are still some sources of pain in DSR: %08x!\n", | ||
395 | dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF | | ||
396 | DSR_WCF | DSR_WEF)); | ||
397 | |||
398 | /* | ||
399 | * now we are trying to clear the "Security-violation flag" to | ||
400 | * get the DryIce out of this state | ||
401 | */ | ||
402 | di_write_busy_wait(imxdi, DSR_SVF, DSR); | ||
403 | |||
404 | /* success? */ | ||
405 | dsr = readl(imxdi->ioaddr + DSR); | ||
406 | if (dsr & DSR_SVF) { | ||
407 | dev_crit(&imxdi->pdev->dev, | ||
408 | "Cannot clear the security violation flag. We are ending up in an endless loop!\n"); | ||
409 | /* last resort */ | ||
410 | di_what_is_to_be_done(imxdi, "battery"); | ||
411 | return -ENODEV; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * now we have left the "FAILURE STATE" and ending up in the | ||
416 | * "NON-VALID STATE" time to recover everything | ||
417 | */ | ||
418 | return di_handle_invalid_state(imxdi, dsr); | ||
419 | } | ||
420 | |||
421 | static int di_handle_state(struct imxdi_dev *imxdi) | ||
422 | { | ||
423 | int rc; | ||
424 | u32 dsr; | ||
425 | |||
426 | dsr = readl(imxdi->ioaddr + DSR); | ||
427 | |||
428 | switch (dsr & (DSR_NVF | DSR_SVF)) { | ||
429 | case DSR_NVF: | ||
430 | dev_warn(&imxdi->pdev->dev, "Invalid stated unit detected\n"); | ||
431 | rc = di_handle_invalid_state(imxdi, dsr); | ||
432 | break; | ||
433 | case DSR_SVF: | ||
434 | dev_warn(&imxdi->pdev->dev, "Failure stated unit detected\n"); | ||
435 | rc = di_handle_failure_state(imxdi, dsr); | ||
436 | break; | ||
437 | case DSR_NVF | DSR_SVF: | ||
438 | dev_warn(&imxdi->pdev->dev, | ||
439 | "Failure+Invalid stated unit detected\n"); | ||
440 | rc = di_handle_invalid_and_failure_state(imxdi, dsr); | ||
441 | break; | ||
442 | default: | ||
443 | dev_notice(&imxdi->pdev->dev, "Unlocked unit detected\n"); | ||
444 | rc = di_handle_valid_state(imxdi, dsr); | ||
445 | } | ||
446 | |||
447 | return rc; | ||
448 | } | ||
449 | |||
450 | /* | ||
176 | * enable a dryice interrupt | 451 | * enable a dryice interrupt |
177 | */ | 452 | */ |
178 | static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) | 453 | static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) |
@@ -491,6 +766,10 @@ static int __init dryice_rtc_probe(struct platform_device *pdev) | |||
491 | /* mask all interrupts */ | 766 | /* mask all interrupts */ |
492 | writel(0, imxdi->ioaddr + DIER); | 767 | writel(0, imxdi->ioaddr + DIER); |
493 | 768 | ||
769 | rc = di_handle_state(imxdi); | ||
770 | if (rc != 0) | ||
771 | goto err; | ||
772 | |||
494 | rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, | 773 | rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, |
495 | IRQF_SHARED, pdev->name, imxdi); | 774 | IRQF_SHARED, pdev->name, imxdi); |
496 | if (rc) { | 775 | if (rc) { |
@@ -498,44 +777,6 @@ static int __init dryice_rtc_probe(struct platform_device *pdev) | |||
498 | goto err; | 777 | goto err; |
499 | } | 778 | } |
500 | 779 | ||
501 | /* put dryice into valid state */ | ||
502 | if (readl(imxdi->ioaddr + DSR) & DSR_NVF) { | ||
503 | rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR); | ||
504 | if (rc) | ||
505 | goto err; | ||
506 | } | ||
507 | |||
508 | /* initialize alarm */ | ||
509 | rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR); | ||
510 | if (rc) | ||
511 | goto err; | ||
512 | rc = di_write_wait(imxdi, 0, DCALR); | ||
513 | if (rc) | ||
514 | goto err; | ||
515 | |||
516 | /* clear alarm flag */ | ||
517 | if (readl(imxdi->ioaddr + DSR) & DSR_CAF) { | ||
518 | rc = di_write_wait(imxdi, DSR_CAF, DSR); | ||
519 | if (rc) | ||
520 | goto err; | ||
521 | } | ||
522 | |||
523 | /* the timer won't count if it has never been written to */ | ||
524 | if (readl(imxdi->ioaddr + DTCMR) == 0) { | ||
525 | rc = di_write_wait(imxdi, 0, DTCMR); | ||
526 | if (rc) | ||
527 | goto err; | ||
528 | } | ||
529 | |||
530 | /* start keeping time */ | ||
531 | if (!(readl(imxdi->ioaddr + DCR) & DCR_TCE)) { | ||
532 | rc = di_write_wait(imxdi, | ||
533 | readl(imxdi->ioaddr + DCR) | DCR_TCE, | ||
534 | DCR); | ||
535 | if (rc) | ||
536 | goto err; | ||
537 | } | ||
538 | |||
539 | platform_set_drvdata(pdev, imxdi); | 780 | platform_set_drvdata(pdev, imxdi); |
540 | imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, | 781 | imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, |
541 | &dryice_rtc_ops, THIS_MODULE); | 782 | &dryice_rtc_ops, THIS_MODULE); |