// 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> */ #include <linux/bcd.h>#include <linux/clk.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/io.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/of.h>#include <linux/pinctrl/pinctrl.h>#include <linux/pinctrl/pinconf.h>#include <linux/pinctrl/pinconf-generic.h>#include <linux/platform_device.h>#include <linux/pm_runtime.h>#include <linux/property.h>#include <linux/rtc.h>#include <linux/rtc/rtc-omap.h> /* * 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. */ /* RTC registers */#define OMAP_RTC_SECONDS_REG 0x00#define OMAP_RTC_MINUTES_REG 0x04#define OMAP_RTC_HOURS_REG 0x08#define OMAP_RTC_DAYS_REG 0x0C#define OMAP_RTC_MONTHS_REG 0x10#define OMAP_RTC_YEARS_REG 0x14#define OMAP_RTC_WEEKS_REG 0x18 #define OMAP_RTC_ALARM_SECONDS_REG 0x20#define OMAP_RTC_ALARM_MINUTES_REG 0x24#define OMAP_RTC_ALARM_HOURS_REG 0x28#define OMAP_RTC_ALARM_DAYS_REG 0x2c#define OMAP_RTC_ALARM_MONTHS_REG 0x30#define OMAP_RTC_ALARM_YEARS_REG 0x34 #define OMAP_RTC_CTRL_REG 0x40#define OMAP_RTC_STATUS_REG 0x44#define OMAP_RTC_INTERRUPTS_REG 0x48 #define OMAP_RTC_COMP_LSB_REG 0x4c#define OMAP_RTC_COMP_MSB_REG 0x50#define OMAP_RTC_OSC_REG 0x54 #define OMAP_RTC_SCRATCH0_REG 0x60#define OMAP_RTC_SCRATCH1_REG 0x64#define OMAP_RTC_SCRATCH2_REG 0x68 #define OMAP_RTC_KICK0_REG 0x6c#define OMAP_RTC_KICK1_REG 0x70 #define OMAP_RTC_IRQWAKEEN 0x7c #define OMAP_RTC_ALARM2_SECONDS_REG 0x80#define OMAP_RTC_ALARM2_MINUTES_REG 0x84#define OMAP_RTC_ALARM2_HOURS_REG 0x88#define OMAP_RTC_ALARM2_DAYS_REG 0x8c#define OMAP_RTC_ALARM2_MONTHS_REG 0x90#define OMAP_RTC_ALARM2_YEARS_REG 0x94 #define OMAP_RTC_PMIC_REG 0x98 /* OMAP_RTC_CTRL_REG bit fields: */#define OMAP_RTC_CTRL_SPLIT BIT(7)#define OMAP_RTC_CTRL_DISABLE BIT(6)#define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5)#define OMAP_RTC_CTRL_TEST BIT(4)#define OMAP_RTC_CTRL_MODE_12_24 BIT(3)#define OMAP_RTC_CTRL_AUTO_COMP BIT(2)#define OMAP_RTC_CTRL_ROUND_30S BIT(1)#define OMAP_RTC_CTRL_STOP BIT(0) /* OMAP_RTC_STATUS_REG bit fields: */#define OMAP_RTC_STATUS_POWER_UP BIT(7)#define OMAP_RTC_STATUS_ALARM2 BIT(7)#define OMAP_RTC_STATUS_ALARM BIT(6)#define OMAP_RTC_STATUS_1D_EVENT BIT(5)#define OMAP_RTC_STATUS_1H_EVENT BIT(4)#define OMAP_RTC_STATUS_1M_EVENT BIT(3)#define OMAP_RTC_STATUS_1S_EVENT BIT(2)#define OMAP_RTC_STATUS_RUN BIT(1)#define OMAP_RTC_STATUS_BUSY BIT(0) /* OMAP_RTC_INTERRUPTS_REG bit fields: */#define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4)#define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3)#define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2) /* OMAP_RTC_OSC_REG bit fields: */#define OMAP_RTC_OSC_32KCLK_EN BIT(6)#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3)#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) /* OMAP_RTC_IRQWAKEEN bit fields: */#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) /* OMAP_RTC_PMIC bit fields: */#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16)#define OMAP_RTC_PMIC_EXT_WKUP_EN(x) BIT(x)#define OMAP_RTC_PMIC_EXT_WKUP_POL(x) BIT(4 + x) /* OMAP_RTC_KICKER values */#define KICK0_VALUE 0x83e70b13#define KICK1_VALUE 0x95a4f1e0 struct omap_rtc; struct omap_rtc_device_type { bool has_32kclk_en; bool has_irqwakeen; bool has_pmic_mode; bool has_power_up_reset; void (*lock)(struct omap_rtc *rtc); void (*unlock)(struct omap_rtc *rtc);}; struct omap_rtc { struct rtc_device *rtc; void __iomem *base; struct clk *clk; int irq_alarm; int irq_timer; u8 interrupts_reg; bool is_pmic_controller; bool has_ext_clk; bool is_suspending; const struct omap_rtc_device_type *type; struct pinctrl_dev *pctldev;}; static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg){ return readb(rtc->base + reg);} static inline u32 rtc_readl(struct omap_rtc *rtc, unsigned int reg){ return readl(rtc->base + reg);} static inline void rtc_write(struct omap_rtc *rtc, unsigned int reg, u8 val){ writeb(val, rtc->base + reg);} static inline void rtc_writel(struct omap_rtc *rtc, unsigned int reg, u32 val){ writel(val, rtc->base + reg);} static void am3352_rtc_unlock(struct omap_rtc *rtc){ rtc_writel(rtc, OMAP_RTC_KICK0_REG, KICK0_VALUE); rtc_writel(rtc, OMAP_RTC_KICK1_REG, KICK1_VALUE);} static void am3352_rtc_lock(struct omap_rtc *rtc){ rtc_writel(rtc, OMAP_RTC_KICK0_REG, 0); rtc_writel(rtc, OMAP_RTC_KICK1_REG, 0);} static void default_rtc_unlock(struct omap_rtc *rtc){} static void default_rtc_lock(struct omap_rtc *rtc){} /* * 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 */static void 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 */} static irqreturn_t rtc_irq(int irq, void *dev_id){ struct omap_rtc *rtc = dev_id; unsigned long events = 0; u8 irq_data; irq_data = rtc_read(rtc, OMAP_RTC_STATUS_REG); /* alarm irq? */ if (irq_data & OMAP_RTC_STATUS_ALARM) { rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM); rtc->type->lock(rtc); events |= RTC_IRQF | RTC_AF; } /* 1/sec periodic/update irq? */ if (irq_data & OMAP_RTC_STATUS_1S_EVENT) events |= RTC_IRQF | RTC_UF; rtc_update_irq(rtc->rtc, 1, events); return IRQ_HANDLED;} static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled){ struct omap_rtc *rtc = dev_get_drvdata(dev); u8 reg, irqwake_reg = 0; local_irq_disable(); rtc_wait_not_busy(rtc); reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); if (rtc->type->has_irqwakeen) irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); if (enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } rtc_wait_not_busy(rtc); rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); if (rtc->type->has_irqwakeen) rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); rtc->type->lock(rtc); local_irq_enable(); return 0;} /* this hardware doesn't support "don't care" alarm fields */static void tm2bcd(struct rtc_time *tm){ tm->tm_sec = bin2bcd(tm->tm_sec); tm->tm_min = bin2bcd(tm->tm_min); tm->tm_hour = bin2bcd(tm->tm_hour); tm->tm_mday = bin2bcd(tm->tm_mday); tm->tm_mon = bin2bcd(tm->tm_mon + 1); tm->tm_year = bin2bcd(tm->tm_year - 100);} static void bcd2tm(struct rtc_time *tm){ tm->tm_sec = bcd2bin(tm->tm_sec); tm->tm_min = bcd2bin(tm->tm_min); tm->tm_hour = bcd2bin(tm->tm_hour); tm->tm_mday = bcd2bin(tm->tm_mday); tm->tm_mon = bcd2bin(tm->tm_mon) - 1; /* epoch == 1900 */ tm->tm_year = bcd2bin(tm->tm_year) + 100;} static void omap_rtc_read_time_raw(struct omap_rtc *rtc, struct rtc_time *tm){ tm->tm_sec = rtc_read(rtc, OMAP_RTC_SECONDS_REG); tm->tm_min = rtc_read(rtc, OMAP_RTC_MINUTES_REG); tm->tm_hour = rtc_read(rtc, OMAP_RTC_HOURS_REG); tm->tm_mday = rtc_read(rtc, OMAP_RTC_DAYS_REG); tm->tm_mon = rtc_read(rtc, OMAP_RTC_MONTHS_REG); tm->tm_year = rtc_read(rtc, OMAP_RTC_YEARS_REG);} static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm){ struct omap_rtc *rtc = dev_get_drvdata(dev); /* we don't report wday/yday/isdst ... */ local_irq_disable(); rtc_wait_not_busy(rtc); omap_rtc_read_time_raw(rtc, tm); local_irq_enable(); bcd2tm(tm); return 0;} static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm){ struct omap_rtc *rtc = dev_get_drvdata(dev); tm2bcd(tm); local_irq_disable(); rtc_wait_not_busy(rtc); rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_YEARS_REG, tm->tm_year); rtc_write(rtc, OMAP_RTC_MONTHS_REG, tm->tm_mon); rtc_write(rtc, OMAP_RTC_DAYS_REG, tm->tm_mday); rtc_write(rtc, OMAP_RTC_HOURS_REG, tm->tm_hour); rtc_write(rtc, OMAP_RTC_MINUTES_REG, tm->tm_min); rtc_write(rtc, OMAP_RTC_SECONDS_REG, tm->tm_sec); rtc->type->lock(rtc); local_irq_enable(); return 0;} static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm){ struct omap_rtc *rtc = dev_get_drvdata(dev); u8 interrupts; local_irq_disable(); rtc_wait_not_busy(rtc); alm->time.tm_sec = rtc_read(rtc, OMAP_RTC_ALARM_SECONDS_REG); alm->time.tm_min = rtc_read(rtc, OMAP_RTC_ALARM_MINUTES_REG); alm->time.tm_hour = rtc_read(rtc, OMAP_RTC_ALARM_HOURS_REG); alm->time.tm_mday = rtc_read(rtc, OMAP_RTC_ALARM_DAYS_REG); alm->time.tm_mon = rtc_read(rtc, OMAP_RTC_ALARM_MONTHS_REG); alm->time.tm_year = rtc_read(rtc, OMAP_RTC_ALARM_YEARS_REG); local_irq_enable(); bcd2tm(&alm->time); interrupts = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); alm->enabled = !!(interrupts & OMAP_RTC_INTERRUPTS_IT_ALARM); return 0;} static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm){ struct omap_rtc *rtc = dev_get_drvdata(dev); u8 reg, irqwake_reg = 0; tm2bcd(&alm->time); local_irq_disable(); rtc_wait_not_busy(rtc); rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_ALARM_YEARS_REG, alm->time.tm_year); rtc_write(rtc, OMAP_RTC_ALARM_MONTHS_REG, alm->time.tm_mon); rtc_write(rtc, OMAP_RTC_ALARM_DAYS_REG, alm->time.tm_mday); rtc_write(rtc, OMAP_RTC_ALARM_HOURS_REG, alm->time.tm_hour); rtc_write(rtc, OMAP_RTC_ALARM_MINUTES_REG, alm->time.tm_min); rtc_write(rtc, OMAP_RTC_ALARM_SECONDS_REG, alm->time.tm_sec); reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); if (rtc->type->has_irqwakeen) irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); if (alm->enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); if (rtc->type->has_irqwakeen) rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); rtc->type->lock(rtc); local_irq_enable(); return 0;} static struct omap_rtc *omap_rtc_power_off_rtc; /** * 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; unsigned long 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); again: /* Clear any existing ALARM2 event */ rtc_writel(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM2); /* 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); tm2bcd(&tm); rtc_wait_not_busy(rtc); rtc_write(rtc, OMAP_RTC_ALARM2_SECONDS_REG, tm.tm_sec); rtc_write(rtc, OMAP_RTC_ALARM2_MINUTES_REG, tm.tm_min); rtc_write(rtc, OMAP_RTC_ALARM2_HOURS_REG, tm.tm_hour); rtc_write(rtc, OMAP_RTC_ALARM2_DAYS_REG, tm.tm_mday); rtc_write(rtc, OMAP_RTC_ALARM2_MONTHS_REG, tm.tm_mon); rtc_write(rtc, OMAP_RTC_ALARM2_YEARS_REG, tm.tm_year); /* * 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; } rtc->type->lock(rtc); return 0;}EXPORT_SYMBOL(omap_rtc_power_off_program); /* * 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. */static void 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).Lines 1–500 of 1031. Load more
// 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> */ #include <linux/bcd.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> #include <linux/rtc.h> #include <linux/rtc/rtc-omap.h> /* * 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. */ /* RTC registers */ #define OMAP_RTC_SECONDS_REG 0x00 #define OMAP_RTC_MINUTES_REG 0x04 #define OMAP_RTC_HOURS_REG 0x08 #define OMAP_RTC_DAYS_REG 0x0C #define OMAP_RTC_MONTHS_REG 0x10 #define OMAP_RTC_YEARS_REG 0x14 #define OMAP_RTC_WEEKS_REG 0x18 #define OMAP_RTC_ALARM_SECONDS_REG 0x20 #define OMAP_RTC_ALARM_MINUTES_REG 0x24 #define OMAP_RTC_ALARM_HOURS_REG 0x28 #define OMAP_RTC_ALARM_DAYS_REG 0x2c #define OMAP_RTC_ALARM_MONTHS_REG 0x30 #define OMAP_RTC_ALARM_YEARS_REG 0x34 #define OMAP_RTC_CTRL_REG 0x40 #define OMAP_RTC_STATUS_REG 0x44 #define OMAP_RTC_INTERRUPTS_REG 0x48 #define OMAP_RTC_COMP_LSB_REG 0x4c #define OMAP_RTC_COMP_MSB_REG 0x50 #define OMAP_RTC_OSC_REG 0x54 #define OMAP_RTC_SCRATCH0_REG 0x60 #define OMAP_RTC_SCRATCH1_REG 0x64 #define OMAP_RTC_SCRATCH2_REG 0x68 #define OMAP_RTC_KICK0_REG 0x6c #define OMAP_RTC_KICK1_REG 0x70 #define OMAP_RTC_IRQWAKEEN 0x7c #define OMAP_RTC_ALARM2_SECONDS_REG 0x80 #define OMAP_RTC_ALARM2_MINUTES_REG 0x84 #define OMAP_RTC_ALARM2_HOURS_REG 0x88 #define OMAP_RTC_ALARM2_DAYS_REG 0x8c #define OMAP_RTC_ALARM2_MONTHS_REG 0x90 #define OMAP_RTC_ALARM2_YEARS_REG 0x94 #define OMAP_RTC_PMIC_REG 0x98 /* OMAP_RTC_CTRL_REG bit fields: */ #define OMAP_RTC_CTRL_SPLIT BIT(7) #define OMAP_RTC_CTRL_DISABLE BIT(6) #define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5) #define OMAP_RTC_CTRL_TEST BIT(4) #define OMAP_RTC_CTRL_MODE_12_24 BIT(3) #define OMAP_RTC_CTRL_AUTO_COMP BIT(2) #define OMAP_RTC_CTRL_ROUND_30S BIT(1) #define OMAP_RTC_CTRL_STOP BIT(0) /* OMAP_RTC_STATUS_REG bit fields: */ #define OMAP_RTC_STATUS_POWER_UP BIT(7) #define OMAP_RTC_STATUS_ALARM2 BIT(7) #define OMAP_RTC_STATUS_ALARM BIT(6) #define OMAP_RTC_STATUS_1D_EVENT BIT(5) #define OMAP_RTC_STATUS_1H_EVENT BIT(4) #define OMAP_RTC_STATUS_1M_EVENT BIT(3) #define OMAP_RTC_STATUS_1S_EVENT BIT(2) #define OMAP_RTC_STATUS_RUN BIT(1) #define OMAP_RTC_STATUS_BUSY BIT(0) /* OMAP_RTC_INTERRUPTS_REG bit fields: */ #define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4) #define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3) #define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2) /* OMAP_RTC_OSC_REG bit fields: */ #define OMAP_RTC_OSC_32KCLK_EN BIT(6) #define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3) #define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) /* OMAP_RTC_IRQWAKEEN bit fields: */ #define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) /* OMAP_RTC_PMIC bit fields: */ #define OMAP_RTC_PMIC_POWER_EN_EN BIT(16) #define OMAP_RTC_PMIC_EXT_WKUP_EN(x) BIT(x) #define OMAP_RTC_PMIC_EXT_WKUP_POL(x) BIT(4 + x) /* OMAP_RTC_KICKER values */ #define KICK0_VALUE 0x83e70b13 #define KICK1_VALUE 0x95a4f1e0 struct omap_rtc; struct omap_rtc_device_type { bool has_32kclk_en; bool has_irqwakeen; bool has_pmic_mode; bool has_power_up_reset; void (*lock)(struct omap_rtc *rtc); void (*unlock)(struct omap_rtc *rtc); }; struct omap_rtc { struct rtc_device *rtc; void __iomem *base; struct clk *clk; int irq_alarm; int irq_timer; u8 interrupts_reg; bool is_pmic_controller; bool has_ext_clk; bool is_suspending; const struct omap_rtc_device_type *type; struct pinctrl_dev *pctldev; }; static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) { return readb(rtc->base + reg); } static inline u32 rtc_readl(struct omap_rtc *rtc, unsigned int reg) { return readl(rtc->base + reg); } static inline void rtc_write(struct omap_rtc *rtc, unsigned int reg, u8 val) { writeb(val, rtc->base + reg); } static inline void rtc_writel(struct omap_rtc *rtc, unsigned int reg, u32 val) { writel(val, rtc->base + reg); } static void am3352_rtc_unlock(struct omap_rtc *rtc) { rtc_writel(rtc, OMAP_RTC_KICK0_REG, KICK0_VALUE); rtc_writel(rtc, OMAP_RTC_KICK1_REG, KICK1_VALUE); } static void am3352_rtc_lock(struct omap_rtc *rtc) { rtc_writel(rtc, OMAP_RTC_KICK0_REG, 0); rtc_writel(rtc, OMAP_RTC_KICK1_REG, 0); } static void default_rtc_unlock(struct omap_rtc *rtc) { } static void default_rtc_lock(struct omap_rtc *rtc) { } /* * 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 */ static void 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 */ } static irqreturn_t rtc_irq(int irq, void *dev_id) { struct omap_rtc *rtc = dev_id; unsigned long events = 0; u8 irq_data; irq_data = rtc_read(rtc, OMAP_RTC_STATUS_REG); /* alarm irq? */ if (irq_data & OMAP_RTC_STATUS_ALARM) { rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM); rtc->type->lock(rtc); events |= RTC_IRQF | RTC_AF; } /* 1/sec periodic/update irq? */ if (irq_data & OMAP_RTC_STATUS_1S_EVENT) events |= RTC_IRQF | RTC_UF; rtc_update_irq(rtc->rtc, 1, events); return IRQ_HANDLED; } static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct omap_rtc *rtc = dev_get_drvdata(dev); u8 reg, irqwake_reg = 0; local_irq_disable(); rtc_wait_not_busy(rtc); reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); if (rtc->type->has_irqwakeen) irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); if (enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } rtc_wait_not_busy(rtc); rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); if (rtc->type->has_irqwakeen) rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); rtc->type->lock(rtc); local_irq_enable(); return 0; } /* this hardware doesn't support "don't care" alarm fields */ static void tm2bcd(struct rtc_time *tm) { tm->tm_sec = bin2bcd(tm->tm_sec); tm->tm_min = bin2bcd(tm->tm_min); tm->tm_hour = bin2bcd(tm->tm_hour); tm->tm_mday = bin2bcd(tm->tm_mday); tm->tm_mon = bin2bcd(tm->tm_mon + 1); tm->tm_year = bin2bcd(tm->tm_year - 100); } static void bcd2tm(struct rtc_time *tm) { tm->tm_sec = bcd2bin(tm->tm_sec); tm->tm_min = bcd2bin(tm->tm_min); tm->tm_hour = bcd2bin(tm->tm_hour); tm->tm_mday = bcd2bin(tm->tm_mday); tm->tm_mon = bcd2bin(tm->tm_mon) - 1; /* epoch == 1900 */ tm->tm_year = bcd2bin(tm->tm_year) + 100; } static void omap_rtc_read_time_raw(struct omap_rtc *rtc, struct rtc_time *tm) { tm->tm_sec = rtc_read(rtc, OMAP_RTC_SECONDS_REG); tm->tm_min = rtc_read(rtc, OMAP_RTC_MINUTES_REG); tm->tm_hour = rtc_read(rtc, OMAP_RTC_HOURS_REG); tm->tm_mday = rtc_read(rtc, OMAP_RTC_DAYS_REG); tm->tm_mon = rtc_read(rtc, OMAP_RTC_MONTHS_REG); tm->tm_year = rtc_read(rtc, OMAP_RTC_YEARS_REG); } static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct omap_rtc *rtc = dev_get_drvdata(dev); /* we don't report wday/yday/isdst ... */ local_irq_disable(); rtc_wait_not_busy(rtc); omap_rtc_read_time_raw(rtc, tm); local_irq_enable(); bcd2tm(tm); return 0; } static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct omap_rtc *rtc = dev_get_drvdata(dev); tm2bcd(tm); local_irq_disable(); rtc_wait_not_busy(rtc); rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_YEARS_REG, tm->tm_year); rtc_write(rtc, OMAP_RTC_MONTHS_REG, tm->tm_mon); rtc_write(rtc, OMAP_RTC_DAYS_REG, tm->tm_mday); rtc_write(rtc, OMAP_RTC_HOURS_REG, tm->tm_hour); rtc_write(rtc, OMAP_RTC_MINUTES_REG, tm->tm_min); rtc_write(rtc, OMAP_RTC_SECONDS_REG, tm->tm_sec); rtc->type->lock(rtc); local_irq_enable(); return 0; } static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { struct omap_rtc *rtc = dev_get_drvdata(dev); u8 interrupts; local_irq_disable(); rtc_wait_not_busy(rtc); alm->time.tm_sec = rtc_read(rtc, OMAP_RTC_ALARM_SECONDS_REG); alm->time.tm_min = rtc_read(rtc, OMAP_RTC_ALARM_MINUTES_REG); alm->time.tm_hour = rtc_read(rtc, OMAP_RTC_ALARM_HOURS_REG); alm->time.tm_mday = rtc_read(rtc, OMAP_RTC_ALARM_DAYS_REG); alm->time.tm_mon = rtc_read(rtc, OMAP_RTC_ALARM_MONTHS_REG); alm->time.tm_year = rtc_read(rtc, OMAP_RTC_ALARM_YEARS_REG); local_irq_enable(); bcd2tm(&alm->time); interrupts = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); alm->enabled = !!(interrupts & OMAP_RTC_INTERRUPTS_IT_ALARM); return 0; } static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { struct omap_rtc *rtc = dev_get_drvdata(dev); u8 reg, irqwake_reg = 0; tm2bcd(&alm->time); local_irq_disable(); rtc_wait_not_busy(rtc); rtc->type->unlock(rtc); rtc_write(rtc, OMAP_RTC_ALARM_YEARS_REG, alm->time.tm_year); rtc_write(rtc, OMAP_RTC_ALARM_MONTHS_REG, alm->time.tm_mon); rtc_write(rtc, OMAP_RTC_ALARM_DAYS_REG, alm->time.tm_mday); rtc_write(rtc, OMAP_RTC_ALARM_HOURS_REG, alm->time.tm_hour); rtc_write(rtc, OMAP_RTC_ALARM_MINUTES_REG, alm->time.tm_min); rtc_write(rtc, OMAP_RTC_ALARM_SECONDS_REG, alm->time.tm_sec); reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); if (rtc->type->has_irqwakeen) irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); if (alm->enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); if (rtc->type->has_irqwakeen) rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); rtc->type->lock(rtc); local_irq_enable(); return 0; } static struct omap_rtc *omap_rtc_power_off_rtc; /** * 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; unsigned long 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); again: /* Clear any existing ALARM2 event */ rtc_writel(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM2); /* 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); tm2bcd(&tm); rtc_wait_not_busy(rtc); rtc_write(rtc, OMAP_RTC_ALARM2_SECONDS_REG, tm.tm_sec); rtc_write(rtc, OMAP_RTC_ALARM2_MINUTES_REG, tm.tm_min); rtc_write(rtc, OMAP_RTC_ALARM2_HOURS_REG, tm.tm_hour); rtc_write(rtc, OMAP_RTC_ALARM2_DAYS_REG, tm.tm_mday); rtc_write(rtc, OMAP_RTC_ALARM2_MONTHS_REG, tm.tm_mon); rtc_write(rtc, OMAP_RTC_ALARM2_YEARS_REG, tm.tm_year); /* * 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; } rtc->type->lock(rtc); return 0; } EXPORT_SYMBOL(omap_rtc_power_off_program); /* * 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. */ static void 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).