|
@@ -129,6 +129,324 @@ struct imxdi_dev {
|
|
|
struct work_struct work;
|
|
|
};
|
|
|
|
|
|
+/* 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)
|
|
|
+{
|
|
|
+ /* do the register write */
|
|
|
+ writel(val, imxdi->ioaddr + reg);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * now it takes four 32,768 kHz clock cycles to take
|
|
|
+ * the change into effect = 122 us
|
|
|
+ */
|
|
|
+ usleep_range(130, 200);
|
|
|
+}
|
|
|
+
|
|
|
+static void di_report_tamper_info(struct imxdi_dev *imxdi, u32 dsr)
|
|
|
+{
|
|
|
+ u32 dtcr;
|
|
|
+
|
|
|
+ dtcr = readl(imxdi->ioaddr + DTCR);
|
|
|
+
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "DryIce tamper event detected\n");
|
|
|
+ /* the following flags force a transition into the "FAILURE STATE" */
|
|
|
+ if (dsr & DSR_VTD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%sVoltage Tamper Event\n",
|
|
|
+ dtcr & DTCR_VTE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_CTD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%s32768 Hz Clock Tamper Event\n",
|
|
|
+ dtcr & DTCR_CTE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_TTD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%sTemperature Tamper Event\n",
|
|
|
+ dtcr & DTCR_TTE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_SAD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev,
|
|
|
+ "%sSecure Controller Alarm Event\n",
|
|
|
+ dtcr & DTCR_SAIE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_EBD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%sExternal Boot Tamper Event\n",
|
|
|
+ dtcr & DTCR_EBE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_ETAD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper A Event\n",
|
|
|
+ dtcr & DTCR_ETAE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_ETBD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper B Event\n",
|
|
|
+ dtcr & DTCR_ETBE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_WTD)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%sWire-mesh Tamper Event\n",
|
|
|
+ dtcr & DTCR_WTE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_MCO)
|
|
|
+ dev_emerg(&imxdi->pdev->dev,
|
|
|
+ "%sMonotonic-counter Overflow Event\n",
|
|
|
+ dtcr & DTCR_MOE ? "" : "Spurious ");
|
|
|
+
|
|
|
+ if (dsr & DSR_TCO)
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "%sTimer-counter Overflow Event\n",
|
|
|
+ dtcr & DTCR_TOE ? "" : "Spurious ");
|
|
|
+}
|
|
|
+
|
|
|
+static void di_what_is_to_be_done(struct imxdi_dev *imxdi,
|
|
|
+ const char *power_supply)
|
|
|
+{
|
|
|
+ dev_emerg(&imxdi->pdev->dev, "Please cycle the %s power supply in order to get the DryIce/RTC unit working again\n",
|
|
|
+ power_supply);
|
|
|
+}
|
|
|
+
|
|
|
+static int di_handle_failure_state(struct imxdi_dev *imxdi, u32 dsr)
|
|
|
+{
|
|
|
+ u32 dcr;
|
|
|
+
|
|
|
+ dev_dbg(&imxdi->pdev->dev, "DSR register reports: %08X\n", dsr);
|
|
|
+
|
|
|
+ /* report the cause */
|
|
|
+ di_report_tamper_info(imxdi, dsr);
|
|
|
+
|
|
|
+ dcr = readl(imxdi->ioaddr + DCR);
|
|
|
+
|
|
|
+ if (dcr & DCR_FSHL) {
|
|
|
+ /* we are out of luck */
|
|
|
+ di_what_is_to_be_done(imxdi, "battery");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * with the next SYSTEM POR we will transit from the "FAILURE STATE"
|
|
|
+ * into the "NON-VALID STATE" + "FAILURE STATE"
|
|
|
+ */
|
|
|
+ di_what_is_to_be_done(imxdi, "main");
|
|
|
+
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
+static int di_handle_valid_state(struct imxdi_dev *imxdi, u32 dsr)
|
|
|
+{
|
|
|
+ /* initialize alarm */
|
|
|
+ di_write_busy_wait(imxdi, DCAMR_UNSET, DCAMR);
|
|
|
+ di_write_busy_wait(imxdi, 0, DCALR);
|
|
|
+
|
|
|
+ /* clear alarm flag */
|
|
|
+ if (dsr & DSR_CAF)
|
|
|
+ di_write_busy_wait(imxdi, DSR_CAF, DSR);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int di_handle_invalid_state(struct imxdi_dev *imxdi, u32 dsr)
|
|
|
+{
|
|
|
+ u32 dcr, sec;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * lets disable all sources which can force the DryIce unit into
|
|
|
+ * the "FAILURE STATE" for now
|
|
|
+ */
|
|
|
+ di_write_busy_wait(imxdi, 0x00000000, DTCR);
|
|
|
+ /* and lets protect them at runtime from any change */
|
|
|
+ di_write_busy_wait(imxdi, DCR_TDCSL, DCR);
|
|
|
+
|
|
|
+ sec = readl(imxdi->ioaddr + DTCMR);
|
|
|
+ if (sec != 0)
|
|
|
+ dev_warn(&imxdi->pdev->dev,
|
|
|
+ "The security violation has happend at %u seconds\n",
|
|
|
+ sec);
|
|
|
+ /*
|
|
|
+ * the timer cannot be set/modified if
|
|
|
+ * - the TCHL or TCSL bit is set in DCR
|
|
|
+ */
|
|
|
+ dcr = readl(imxdi->ioaddr + DCR);
|
|
|
+ if (!(dcr & DCR_TCE)) {
|
|
|
+ if (dcr & DCR_TCHL) {
|
|
|
+ /* we are out of luck */
|
|
|
+ di_what_is_to_be_done(imxdi, "battery");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+ if (dcr & DCR_TCSL) {
|
|
|
+ di_what_is_to_be_done(imxdi, "main");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * - the timer counter stops/is stopped if
|
|
|
+ * - its overflow flag is set (TCO in DSR)
|
|
|
+ * -> clear overflow bit to make it count again
|
|
|
+ * - NVF is set in DSR
|
|
|
+ * -> clear non-valid bit to make it count again
|
|
|
+ * - its TCE (DCR) is cleared
|
|
|
+ * -> set TCE to make it count
|
|
|
+ * - it was never set before
|
|
|
+ * -> write a time into it (required again if the NVF was set)
|
|
|
+ */
|
|
|
+ /* state handled */
|
|
|
+ di_write_busy_wait(imxdi, DSR_NVF, DSR);
|
|
|
+ /* clear overflow flag */
|
|
|
+ di_write_busy_wait(imxdi, DSR_TCO, DSR);
|
|
|
+ /* enable the counter */
|
|
|
+ di_write_busy_wait(imxdi, dcr | DCR_TCE, DCR);
|
|
|
+ /* set and trigger it to make it count */
|
|
|
+ di_write_busy_wait(imxdi, sec, DTCMR);
|
|
|
+
|
|
|
+ /* now prepare for the valid state */
|
|
|
+ return di_handle_valid_state(imxdi, __raw_readl(imxdi->ioaddr + DSR));
|
|
|
+}
|
|
|
+
|
|
|
+static int di_handle_invalid_and_failure_state(struct imxdi_dev *imxdi, u32 dsr)
|
|
|
+{
|
|
|
+ u32 dcr;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * now we must first remove the tamper sources in order to get the
|
|
|
+ * device out of the "FAILURE STATE"
|
|
|
+ * To disable any of the following sources we need to modify the DTCR
|
|
|
+ */
|
|
|
+ if (dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD |
|
|
|
+ DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)) {
|
|
|
+ dcr = __raw_readl(imxdi->ioaddr + DCR);
|
|
|
+ if (dcr & DCR_TDCHL) {
|
|
|
+ /*
|
|
|
+ * the tamper register is locked. We cannot disable the
|
|
|
+ * tamper detection. The TDCHL can only be reset by a
|
|
|
+ * DRYICE POR, but we cannot force a DRYICE POR in
|
|
|
+ * softwere because we are still in "FAILURE STATE".
|
|
|
+ * We need a DRYICE POR via battery power cycling....
|
|
|
+ */
|
|
|
+ /*
|
|
|
+ * out of luck!
|
|
|
+ * we cannot disable them without a DRYICE POR
|
|
|
+ */
|
|
|
+ di_what_is_to_be_done(imxdi, "battery");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+ if (dcr & DCR_TDCSL) {
|
|
|
+ /* a soft lock can be removed by a SYSTEM POR */
|
|
|
+ di_what_is_to_be_done(imxdi, "main");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* disable all sources */
|
|
|
+ di_write_busy_wait(imxdi, 0x00000000, DTCR);
|
|
|
+
|
|
|
+ /* clear the status bits now */
|
|
|
+ di_write_busy_wait(imxdi, dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD |
|
|
|
+ DSR_EBD | DSR_SAD | DSR_TTD | DSR_CTD | DSR_VTD |
|
|
|
+ DSR_MCO | DSR_TCO), DSR);
|
|
|
+
|
|
|
+ dsr = readl(imxdi->ioaddr + DSR);
|
|
|
+ if ((dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF |
|
|
|
+ DSR_WCF | DSR_WEF)) != 0)
|
|
|
+ dev_warn(&imxdi->pdev->dev,
|
|
|
+ "There are still some sources of pain in DSR: %08x!\n",
|
|
|
+ dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF |
|
|
|
+ DSR_WCF | DSR_WEF));
|
|
|
+
|
|
|
+ /*
|
|
|
+ * now we are trying to clear the "Security-violation flag" to
|
|
|
+ * get the DryIce out of this state
|
|
|
+ */
|
|
|
+ di_write_busy_wait(imxdi, DSR_SVF, DSR);
|
|
|
+
|
|
|
+ /* success? */
|
|
|
+ dsr = readl(imxdi->ioaddr + DSR);
|
|
|
+ if (dsr & DSR_SVF) {
|
|
|
+ dev_crit(&imxdi->pdev->dev,
|
|
|
+ "Cannot clear the security violation flag. We are ending up in an endless loop!\n");
|
|
|
+ /* last resort */
|
|
|
+ di_what_is_to_be_done(imxdi, "battery");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * now we have left the "FAILURE STATE" and ending up in the
|
|
|
+ * "NON-VALID STATE" time to recover everything
|
|
|
+ */
|
|
|
+ return di_handle_invalid_state(imxdi, dsr);
|
|
|
+}
|
|
|
+
|
|
|
+static int di_handle_state(struct imxdi_dev *imxdi)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ u32 dsr;
|
|
|
+
|
|
|
+ dsr = readl(imxdi->ioaddr + DSR);
|
|
|
+
|
|
|
+ switch (dsr & (DSR_NVF | DSR_SVF)) {
|
|
|
+ case DSR_NVF:
|
|
|
+ dev_warn(&imxdi->pdev->dev, "Invalid stated unit detected\n");
|
|
|
+ rc = di_handle_invalid_state(imxdi, dsr);
|
|
|
+ break;
|
|
|
+ case DSR_SVF:
|
|
|
+ dev_warn(&imxdi->pdev->dev, "Failure stated unit detected\n");
|
|
|
+ rc = di_handle_failure_state(imxdi, dsr);
|
|
|
+ break;
|
|
|
+ case DSR_NVF | DSR_SVF:
|
|
|
+ dev_warn(&imxdi->pdev->dev,
|
|
|
+ "Failure+Invalid stated unit detected\n");
|
|
|
+ rc = di_handle_invalid_and_failure_state(imxdi, dsr);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ dev_notice(&imxdi->pdev->dev, "Unlocked unit detected\n");
|
|
|
+ rc = di_handle_valid_state(imxdi, dsr);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* enable a dryice interrupt
|
|
|
*/
|
|
@@ -137,8 +455,8 @@ static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
|
|
|
unsigned long flags;
|
|
|
|
|
|
spin_lock_irqsave(&imxdi->irq_lock, flags);
|
|
|
- __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
|
|
|
- imxdi->ioaddr + DIER);
|
|
|
+ writel(readl(imxdi->ioaddr + DIER) | intr,
|
|
|
+ imxdi->ioaddr + DIER);
|
|
|
spin_unlock_irqrestore(&imxdi->irq_lock, flags);
|
|
|
}
|
|
|
|
|
@@ -150,8 +468,8 @@ static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
|
|
|
unsigned long flags;
|
|
|
|
|
|
spin_lock_irqsave(&imxdi->irq_lock, flags);
|
|
|
- __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
|
|
|
- imxdi->ioaddr + DIER);
|
|
|
+ writel(readl(imxdi->ioaddr + DIER) & ~intr,
|
|
|
+ imxdi->ioaddr + DIER);
|
|
|
spin_unlock_irqrestore(&imxdi->irq_lock, flags);
|
|
|
}
|
|
|
|
|
@@ -169,11 +487,11 @@ static void clear_write_error(struct imxdi_dev *imxdi)
|
|
|
dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
|
|
|
|
|
|
/* clear the write error flag */
|
|
|
- __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
|
|
|
+ writel(DSR_WEF, imxdi->ioaddr + DSR);
|
|
|
|
|
|
/* wait for it to take effect */
|
|
|
for (cnt = 0; cnt < 1000; cnt++) {
|
|
|
- if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
|
|
|
+ if ((readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
|
|
|
return;
|
|
|
udelay(10);
|
|
|
}
|
|
@@ -201,7 +519,7 @@ static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
|
|
|
imxdi->dsr = 0;
|
|
|
|
|
|
/* do the register write */
|
|
|
- __raw_writel(val, imxdi->ioaddr + reg);
|
|
|
+ writel(val, imxdi->ioaddr + reg);
|
|
|
|
|
|
/* wait for the write to finish */
|
|
|
ret = wait_event_interruptible_timeout(imxdi->write_wait,
|
|
@@ -235,7 +553,7 @@ static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|
|
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
|
|
|
unsigned long now;
|
|
|
|
|
|
- now = __raw_readl(imxdi->ioaddr + DTCMR);
|
|
|
+ now = readl(imxdi->ioaddr + DTCMR);
|
|
|
rtc_time_to_tm(now, tm);
|
|
|
|
|
|
return 0;
|
|
@@ -248,14 +566,35 @@ static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|
|
static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
|
|
|
{
|
|
|
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
|
|
|
+ u32 dcr, dsr;
|
|
|
int rc;
|
|
|
|
|
|
+ dcr = readl(imxdi->ioaddr + DCR);
|
|
|
+ dsr = readl(imxdi->ioaddr + DSR);
|
|
|
+
|
|
|
+ if (!(dcr & DCR_TCE) || (dsr & DSR_SVF)) {
|
|
|
+ if (dcr & DCR_TCHL) {
|
|
|
+ /* we are even more out of luck */
|
|
|
+ di_what_is_to_be_done(imxdi, "battery");
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+ if ((dcr & DCR_TCSL) || (dsr & DSR_SVF)) {
|
|
|
+ /* we are out of luck for now */
|
|
|
+ di_what_is_to_be_done(imxdi, "main");
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* zero the fractional part first */
|
|
|
rc = di_write_wait(imxdi, 0, DTCLR);
|
|
|
- if (rc == 0)
|
|
|
- rc = di_write_wait(imxdi, secs, DTCMR);
|
|
|
+ if (rc != 0)
|
|
|
+ return rc;
|
|
|
|
|
|
- return rc;
|
|
|
+ rc = di_write_wait(imxdi, secs, DTCMR);
|
|
|
+ if (rc != 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ return di_write_wait(imxdi, readl(imxdi->ioaddr + DCR) | DCR_TCE, DCR);
|
|
|
}
|
|
|
|
|
|
static int dryice_rtc_alarm_irq_enable(struct device *dev,
|
|
@@ -280,17 +619,17 @@ static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
|
|
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
|
|
|
u32 dcamr;
|
|
|
|
|
|
- dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
|
|
|
+ dcamr = readl(imxdi->ioaddr + DCAMR);
|
|
|
rtc_time_to_tm(dcamr, &alarm->time);
|
|
|
|
|
|
/* alarm is enabled if the interrupt is enabled */
|
|
|
- alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
|
|
|
+ alarm->enabled = (readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
|
|
|
|
|
|
/* don't allow the DSR read to mess up DSR_WCF */
|
|
|
mutex_lock(&imxdi->write_mutex);
|
|
|
|
|
|
/* alarm is pending if the alarm flag is set */
|
|
|
- alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
|
|
|
+ alarm->pending = (readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
|
|
|
|
|
|
mutex_unlock(&imxdi->write_mutex);
|
|
|
|
|
@@ -312,7 +651,7 @@ static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
|
|
return rc;
|
|
|
|
|
|
/* don't allow setting alarm in the past */
|
|
|
- now = __raw_readl(imxdi->ioaddr + DTCMR);
|
|
|
+ now = readl(imxdi->ioaddr + DTCMR);
|
|
|
if (alarm_time < now)
|
|
|
return -EINVAL;
|
|
|
|
|
@@ -346,7 +685,26 @@ static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
|
|
|
u32 dsr, dier;
|
|
|
irqreturn_t rc = IRQ_NONE;
|
|
|
|
|
|
- dier = __raw_readl(imxdi->ioaddr + DIER);
|
|
|
+ dier = readl(imxdi->ioaddr + DIER);
|
|
|
+ dsr = readl(imxdi->ioaddr + DSR);
|
|
|
+
|
|
|
+ /* handle the security violation event */
|
|
|
+ if (dier & DIER_SVIE) {
|
|
|
+ if (dsr & DSR_SVF) {
|
|
|
+ /*
|
|
|
+ * Disable the interrupt when this kind of event has
|
|
|
+ * happened.
|
|
|
+ * There cannot be more than one event of this type,
|
|
|
+ * because it needs a complex state change
|
|
|
+ * including a main power cycle to get again out of
|
|
|
+ * this state.
|
|
|
+ */
|
|
|
+ di_int_disable(imxdi, DIER_SVIE);
|
|
|
+ /* report the violation */
|
|
|
+ di_report_tamper_info(imxdi, dsr);
|
|
|
+ rc = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/* handle write complete and write error cases */
|
|
|
if (dier & DIER_WCIE) {
|
|
@@ -357,7 +715,6 @@ static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
|
|
|
return rc;
|
|
|
|
|
|
/* DSR_WCF clears itself on DSR read */
|
|
|
- dsr = __raw_readl(imxdi->ioaddr + DSR);
|
|
|
if (dsr & (DSR_WCF | DSR_WEF)) {
|
|
|
/* mask the interrupt */
|
|
|
di_int_disable(imxdi, DIER_WCIE);
|
|
@@ -373,7 +730,6 @@ static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
|
|
|
/* handle the alarm case */
|
|
|
if (dier & DIER_CAIE) {
|
|
|
/* DSR_WCF clears itself on DSR read */
|
|
|
- dsr = __raw_readl(imxdi->ioaddr + DSR);
|
|
|
if (dsr & DSR_CAF) {
|
|
|
/* mask the interrupt */
|
|
|
di_int_disable(imxdi, DIER_CAIE);
|
|
@@ -446,7 +802,11 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
|
|
|
*/
|
|
|
|
|
|
/* mask all interrupts */
|
|
|
- __raw_writel(0, imxdi->ioaddr + DIER);
|
|
|
+ writel(0, imxdi->ioaddr + DIER);
|
|
|
+
|
|
|
+ rc = di_handle_state(imxdi);
|
|
|
+ if (rc != 0)
|
|
|
+ goto err;
|
|
|
|
|
|
rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
|
|
|
IRQF_SHARED, pdev->name, imxdi);
|
|
@@ -455,44 +815,6 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
- /* put dryice into valid state */
|
|
|
- if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
|
|
|
- rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
|
|
|
- if (rc)
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- /* initialize alarm */
|
|
|
- rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
|
|
|
- if (rc)
|
|
|
- goto err;
|
|
|
- rc = di_write_wait(imxdi, 0, DCALR);
|
|
|
- if (rc)
|
|
|
- goto err;
|
|
|
-
|
|
|
- /* clear alarm flag */
|
|
|
- if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
|
|
|
- rc = di_write_wait(imxdi, DSR_CAF, DSR);
|
|
|
- if (rc)
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- /* the timer won't count if it has never been written to */
|
|
|
- if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
|
|
|
- rc = di_write_wait(imxdi, 0, DTCMR);
|
|
|
- if (rc)
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- /* start keeping time */
|
|
|
- if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
|
|
|
- rc = di_write_wait(imxdi,
|
|
|
- __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
|
|
|
- DCR);
|
|
|
- if (rc)
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
platform_set_drvdata(pdev, imxdi);
|
|
|
imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
|
|
&dryice_rtc_ops, THIS_MODULE);
|
|
@@ -516,7 +838,7 @@ static int __exit dryice_rtc_remove(struct platform_device *pdev)
|
|
|
flush_work(&imxdi->work);
|
|
|
|
|
|
/* mask all interrupts */
|
|
|
- __raw_writel(0, imxdi->ioaddr + DIER);
|
|
|
+ writel(0, imxdi->ioaddr + DIER);
|
|
|
|
|
|
clk_disable_unprepare(imxdi->clk);
|
|
|
|