// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP Real Time Clock interface for Linux * * Copyright (C) 2003 MontaVista Software, Inc. * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com> * * Copyright (C) 2006 David Brownell (new RTC framework) * Copyright (C) 2014 Johan Hovold <johan@kernel.org>
*/
/* * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock * with century-range alarm matching, driven by the 32kHz clock. * * The main user-visible ways it differs from PC RTCs are by omitting * "don't care" alarm fields and sub-second periodic IRQs, and having * an autoadjust mechanism to calibrate to the true oscillator rate. * * Board-specific wiring options include using split power mode with * RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset), * and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from * low power modes) for OMAP1 boards (OMAP-L138 has this built into * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment.
*/
/* * We rely on the rtc framework to handle locking (rtc->ops_lock), * so the only other requirement is that register accesses which * require BUSY to be clear are made with IRQs locally disabled
*/ staticvoid rtc_wait_not_busy(struct omap_rtc *rtc)
{ int count;
u8 status;
/* BUSY may stay active for 1/32768 second (~30 usec) */ for (count = 0; count < 50; count++) {
status = rtc_read(rtc, OMAP_RTC_STATUS_REG); if (!(status & OMAP_RTC_STATUS_BUSY)) break;
udelay(1);
} /* now we have ~15 usec to read/write various registers */
}
/** * omap_rtc_power_off_program: Set the pmic power off sequence. The RTC * generates pmic_pwr_enable control, which can be used to control an external * PMIC.
*/ int omap_rtc_power_off_program(struct device *dev)
{ struct omap_rtc *rtc = omap_rtc_power_off_rtc; struct rtc_time tm; unsignedlong now; int seconds;
u32 val;
rtc->type->unlock(rtc); /* enable pmic_power_en control */
val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN);
/* set alarm one second from now */
omap_rtc_read_time_raw(rtc, &tm);
seconds = tm.tm_sec;
bcd2tm(&tm);
now = rtc_tm_to_time64(&tm);
rtc_time64_to_tm(now + 1, &tm);
/* * enable ALARM2 interrupt * * NOTE: this fails on AM3352 if rtc_write (writeb) is used
*/
val = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG,
val | OMAP_RTC_INTERRUPTS_IT_ALARM2);
/* Retry in case roll over happened before alarm was armed. */ if (rtc_read(rtc, OMAP_RTC_SECONDS_REG) != seconds) {
val = rtc_read(rtc, OMAP_RTC_STATUS_REG); if (!(val & OMAP_RTC_STATUS_ALARM2)) goto again;
}
/* * omap_rtc_poweroff: RTC-controlled power off * * The RTC can be used to control an external PMIC via the pmic_power_en pin, * which can be configured to transition to OFF on ALARM2 events. * * Notes: * The one-second alarm offset is the shortest offset possible as the alarm * registers must be set before the next timer update and the offset * calculation is too heavy for everything to be done within a single access * period (~15 us). * * Called with local interrupts disabled.
*/ staticvoid omap_rtc_power_off(void)
{ struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc;
u32 val;
omap_rtc_power_off_program(rtc->dev.parent);
/* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */
omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc);
val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG);
val |= OMAP_RTC_PMIC_POWER_EN_EN | OMAP_RTC_PMIC_EXT_WKUP_POL(0) |
OMAP_RTC_PMIC_EXT_WKUP_EN(0);
rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG, val);
omap_rtc_power_off_rtc->type->lock(omap_rtc_power_off_rtc);
/* * Wait for alarm to trigger (within one second) and external PMIC to * power off the system. Add a 500 ms margin for external latencies * (e.g. debounce circuits).
*/
mdelay(1500);
}
/* active low by default */
val |= OMAP_RTC_PMIC_EXT_WKUP_POL(pin);
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
param_val = pinconf_to_config_argument(configs[i]);
switch (param) { case PIN_CONFIG_INPUT_ENABLE: if (param_val)
val |= OMAP_RTC_PMIC_EXT_WKUP_EN(pin); else
val &= ~OMAP_RTC_PMIC_EXT_WKUP_EN(pin); break; case PIN_CONFIG_ACTIVE_HIGH:
val &= ~OMAP_RTC_PMIC_EXT_WKUP_POL(pin); break; default:
dev_err(&rtc->rtc->dev, "Property %u not supported\n",
param); return -ENOTSUPP;
}
}
/* clear old status */
reg = rtc_read(rtc, OMAP_RTC_STATUS_REG);
mask = OMAP_RTC_STATUS_ALARM;
if (rtc->type->has_pmic_mode)
mask |= OMAP_RTC_STATUS_ALARM2;
if (rtc->type->has_power_up_reset) {
mask |= OMAP_RTC_STATUS_POWER_UP; if (reg & OMAP_RTC_STATUS_POWER_UP)
dev_info(&pdev->dev, "RTC power up reset detected\n");
}
if (reg & mask)
rtc_write(rtc, OMAP_RTC_STATUS_REG, reg & mask);
/* On boards with split power, RTC_ON_NOFF won't reset the RTC */
reg = rtc_read(rtc, OMAP_RTC_CTRL_REG); if (reg & OMAP_RTC_CTRL_STOP)
dev_info(&pdev->dev, "already running\n");
/* force to 24 hour mode */
new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP);
new_ctrl |= OMAP_RTC_CTRL_STOP;
/* * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: * * - Device wake-up capability setting should come through chip * init logic. OMAP1 boards should initialize the "wakeup capable" * flag in the platform device if the board is wired right for * being woken up by RTC alarm. For OMAP-L138, this capability * is built into the SoC by the "Deep Sleep" capability. * * - Boards wired so RTC_ON_nOFF is used as the reset signal, * rather than nPWRON_RESET, should forcibly enable split * power mode. (Some chip errata report that RTC_CTRL_SPLIT * is write-only, and always reads as zero...)
*/
if (new_ctrl & OMAP_RTC_CTRL_SPLIT)
dev_info(&pdev->dev, "split power mode\n");
if (reg != new_ctrl)
rtc_write(rtc, OMAP_RTC_CTRL_REG, new_ctrl);
/* * If we have the external clock then switch to it so we can keep * ticking across suspend.
*/ if (rtc->has_ext_clk) {
reg = rtc_read(rtc, OMAP_RTC_OSC_REG);
reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE;
reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC;
rtc_write(rtc, OMAP_RTC_OSC_REG, reg);
}
rtc->type->lock(rtc);
device_init_wakeup(&pdev->dev, true);
rtc->rtc = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(rtc->rtc)) {
ret = PTR_ERR(rtc->rtc); goto err;
}
rtc->type->unlock(rtc); /* * FIXME: the RTC alarm is not currently acting as a wakeup event * source on some platforms, and in fact this enable() call is just * saving a flag that's never used...
*/ if (device_may_wakeup(dev))
enable_irq_wake(rtc->irq_alarm); else
rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0);
rtc->type->lock(rtc);
/* * Keep the ALARM interrupt enabled to allow the system to power up on * alarm events.
*/
rtc->type->unlock(rtc);
mask = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
mask &= OMAP_RTC_INTERRUPTS_IT_ALARM;
rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, mask);
rtc->type->lock(rtc);
}
MODULE_AUTHOR("George G. Davis (and others)");
MODULE_DESCRIPTION("TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx RTC driver");
MODULE_LICENSE("GPL");
Messung V0.5
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.