// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2010 Orex Computed Radiography */ /* * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block * to implement a Linux RTC. Times and alarms are truncated to seconds. * Since the RTC framework performs API locking via rtc->ops_lock the * only simultaneous accesses we need to deal with is updating DryIce * registers while servicing an alarm. * * Note that reading the DSR (DryIce Status Register) automatically clears * the WCF (Write Complete Flag). All DryIce writes are synchronized to the * LP (Low Power) domain and set the WCF upon completion. Writes to the * DIER (DryIce Interrupt Enable Register) are the only exception. These * occur at normal bus speeds and do not set WCF. Periodic interrupts are * not supported by the hardware. */ #include <linux/io.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_wakeirq.h> #include <linux/rtc.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/of.h> /* DryIce Register Definitions */ #define DTCMR … #define DTCLR … #define DCAMR … #define DCALR … #define DCAMR_UNSET … #define DCR … #define DCR_TDCHL … #define DCR_TDCSL … #define DCR_KSSL … #define DCR_MCHL … #define DCR_MCSL … #define DCR_TCHL … #define DCR_TCSL … #define DCR_FSHL … #define DCR_TCE … #define DCR_MCE … #define DSR … #define DSR_WTD … #define DSR_ETBD … #define DSR_ETAD … #define DSR_EBD … #define DSR_SAD … #define DSR_TTD … #define DSR_CTD … #define DSR_VTD … #define DSR_WBF … #define DSR_WNF … #define DSR_WCF … #define DSR_WEF … #define DSR_CAF … #define DSR_MCO … #define DSR_TCO … #define DSR_NVF … #define DSR_SVF … #define DIER … #define DIER_WNIE … #define DIER_WCIE … #define DIER_WEIE … #define DIER_CAIE … #define DIER_SVIE … #define DMCR … #define DTCR … #define DTCR_MOE … #define DTCR_TOE … #define DTCR_WTE … #define DTCR_ETBE … #define DTCR_ETAE … #define DTCR_EBE … #define DTCR_SAIE … #define DTCR_TTE … #define DTCR_CTE … #define DTCR_VTE … #define DGPR … /** * struct imxdi_dev - private imxdi rtc data * @pdev: pointer to platform dev * @rtc: pointer to rtc struct * @ioaddr: IO registers pointer * @clk: input reference clock * @dsr: copy of the DSR register * @irq_lock: interrupt enable register (DIER) lock * @write_wait: registers write complete queue * @write_mutex: serialize registers write * @work: schedule alarm work */ struct imxdi_dev { … }; /* Some background: * * The DryIce unit is a complex security/tamper monitor device. To be able do * its job in a useful manner it runs a bigger statemachine to bring it into * security/tamper failure state and once again to bring it out of this state. * * This unit can be in one of three states: * * - "NON-VALID STATE" * always after the battery power was removed * - "FAILURE STATE" * if one of the enabled security events has happened * - "VALID STATE" * if the unit works as expected * * Everything stops when the unit enters the failure state including the RTC * counter (to be able to detect the time the security event happened). * * The following events (when enabled) let the DryIce unit enter the failure * state: * * - wire-mesh-tamper detect * - external tamper B detect * - external tamper A detect * - temperature tamper detect * - clock tamper detect * - voltage tamper detect * - RTC counter overflow * - monotonic counter overflow * - external boot * * If we find the DryIce unit in "FAILURE STATE" and the TDCHL cleared, we * can only detect this state. In this case the unit is completely locked and * must force a second "SYSTEM POR" to bring the DryIce into the * "NON-VALID STATE" + "FAILURE STATE" where a recovery is possible. * If the TDCHL is set in the "FAILURE STATE" we are out of luck. In this case * a battery power cycle is required. * * In the "NON-VALID STATE" + "FAILURE STATE" we can clear the "FAILURE STATE" * and recover the DryIce unit. By clearing the "NON-VALID STATE" as the last * task, we bring back this unit into life. */ /* * Do a write into the unit without interrupt support. * We do not need to check the WEF here, because the only reason this kind of * write error can happen is if we write to the unit twice within the 122 us * interval. This cannot happen, since we are using this function only while * setting up the unit. */ static void di_write_busy_wait(const struct imxdi_dev *imxdi, u32 val, unsigned reg) { … } static void di_report_tamper_info(struct imxdi_dev *imxdi, u32 dsr) { … } static void di_what_is_to_be_done(struct imxdi_dev *imxdi, const char *power_supply) { … } static int di_handle_failure_state(struct imxdi_dev *imxdi, u32 dsr) { … } static int di_handle_valid_state(struct imxdi_dev *imxdi, u32 dsr) { … } static int di_handle_invalid_state(struct imxdi_dev *imxdi, u32 dsr) { … } static int di_handle_invalid_and_failure_state(struct imxdi_dev *imxdi, u32 dsr) { … } static int di_handle_state(struct imxdi_dev *imxdi) { … } /* * enable a dryice interrupt */ static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) { … } /* * disable a dryice interrupt */ static void di_int_disable(struct imxdi_dev *imxdi, u32 intr) { … } /* * This function attempts to clear the dryice write-error flag. * * A dryice write error is similar to a bus fault and should not occur in * normal operation. Clearing the flag requires another write, so the root * cause of the problem may need to be fixed before the flag can be cleared. */ static void clear_write_error(struct imxdi_dev *imxdi) { … } /* * Write a dryice register and wait until it completes. * * This function uses interrupts to determine when the * write has completed. */ static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg) { … } /* * read the seconds portion of the current time from the dryice time counter */ static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm) { … } /* * set the seconds portion of dryice time counter and clear the * fractional part. */ static int dryice_rtc_set_time(struct device *dev, struct rtc_time *tm) { … } static int dryice_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { … } /* * read the seconds portion of the alarm register. * the fractional part of the alarm register is always zero. */ static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { … } /* * set the seconds portion of dryice alarm register */ static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { … } static const struct rtc_class_ops dryice_rtc_ops = …; /* * interrupt handler for dryice "normal" and security violation interrupt */ static irqreturn_t dryice_irq(int irq, void *dev_id) { … } /* * post the alarm event from user context so it can sleep * on the write completion. */ static void dryice_work(struct work_struct *work) { … } /* * probe for dryice rtc device */ static int __init dryice_rtc_probe(struct platform_device *pdev) { … } static void __exit dryice_rtc_remove(struct platform_device *pdev) { … } static const struct of_device_id dryice_dt_ids[] = …; MODULE_DEVICE_TABLE(of, dryice_dt_ids); /* * dryice_rtc_remove() lives in .exit.text. For drivers registered via * module_platform_driver_probe() this is ok because they cannot get unbound at * runtime. So mark the driver struct with __refdata to prevent modpost * triggering a section mismatch warning. */ static struct platform_driver dryice_rtc_driver __refdata = …; module_platform_driver_probe(…); MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;