linux/drivers/rtc/rtc-imxdi.c

// 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();