--- zzzz-none-000/linux-3.10.107/drivers/rtc/rtc-omap.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/rtc/rtc-omap.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,10 +1,11 @@ /* - * TI OMAP1 Real Time Clock interface for Linux + * TI OMAP Real Time Clock interface for Linux * * Copyright (C) 2003 MontaVista Software, Inc. * Author: George G. Davis or * * Copyright (C) 2006 David Brownell (new RTC framework) + * Copyright (C) 2014 Johan Hovold * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,11 +24,11 @@ #include #include #include +#include +#include -#include - - -/* The OMAP1 RTC is a year/month/day/hours/minutes/seconds BCD clock +/* + * 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 @@ -41,10 +42,6 @@ * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment. */ -#define DRIVER_NAME "omap_rtc" - -#define OMAP_RTC_BASE 0xfffb4800 - /* RTC registers */ #define OMAP_RTC_SECONDS_REG 0x00 #define OMAP_RTC_MINUTES_REG 0x04 @@ -72,73 +69,154 @@ #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 (1<<7) -#define OMAP_RTC_CTRL_DISABLE (1<<6) -#define OMAP_RTC_CTRL_SET_32_COUNTER (1<<5) -#define OMAP_RTC_CTRL_TEST (1<<4) -#define OMAP_RTC_CTRL_MODE_12_24 (1<<3) -#define OMAP_RTC_CTRL_AUTO_COMP (1<<2) -#define OMAP_RTC_CTRL_ROUND_30S (1<<1) -#define OMAP_RTC_CTRL_STOP (1<<0) +#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 (1<<7) -#define OMAP_RTC_STATUS_ALARM (1<<6) -#define OMAP_RTC_STATUS_1D_EVENT (1<<5) -#define OMAP_RTC_STATUS_1H_EVENT (1<<4) -#define OMAP_RTC_STATUS_1M_EVENT (1<<3) -#define OMAP_RTC_STATUS_1S_EVENT (1<<2) -#define OMAP_RTC_STATUS_RUN (1<<1) -#define OMAP_RTC_STATUS_BUSY (1<<0) +#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_ALARM (1<<3) -#define OMAP_RTC_INTERRUPTS_IT_TIMER (1<<2) +#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) /* OMAP_RTC_KICKER values */ #define KICK0_VALUE 0x83e70b13 #define KICK1_VALUE 0x95a4f1e0 -#define OMAP_RTC_HAS_KICKER 0x1 +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); +}; -static void __iomem *rtc_base; +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; + const struct omap_rtc_device_type *type; +}; -#define rtc_read(addr) readb(rtc_base + (addr)) -#define rtc_write(val, addr) writeb(val, rtc_base + (addr)) +static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) +{ + return readb(rtc->base + reg); +} -#define rtc_writel(val, addr) writel(val, rtc_base + (addr)) +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), +/* + * 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(void) +static void rtc_wait_not_busy(struct omap_rtc *rtc) { - int count = 0; - u8 status; + int count; + u8 status; /* BUSY may stay active for 1/32768 second (~30 usec) */ for (count = 0; count < 50; count++) { - status = rtc_read(OMAP_RTC_STATUS_REG); - if ((status & (u8)OMAP_RTC_STATUS_BUSY) == 0) + 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 *rtc) +static irqreturn_t rtc_irq(int irq, void *dev_id) { - unsigned long events = 0; - u8 irq_data; + struct omap_rtc *rtc = dev_id; + unsigned long events = 0; + u8 irq_data; - irq_data = rtc_read(OMAP_RTC_STATUS_REG); + irq_data = rtc_read(rtc, OMAP_RTC_STATUS_REG); /* alarm irq? */ if (irq_data & OMAP_RTC_STATUS_ALARM) { - rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG); + rtc->type->unlock(rtc); + rtc_write(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM); + rtc->type->lock(rtc); events |= RTC_IRQF | RTC_AF; } @@ -146,24 +224,35 @@ if (irq_data & OMAP_RTC_STATUS_1S_EVENT) events |= RTC_IRQF | RTC_UF; - rtc_update_irq(rtc, 1, events); + rtc_update_irq(rtc->rtc, 1, events); return IRQ_HANDLED; } static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { - u8 reg; + struct omap_rtc *rtc = dev_get_drvdata(dev); + u8 reg, irqwake_reg = 0; local_irq_disable(); - rtc_wait_not_busy(); - reg = rtc_read(OMAP_RTC_INTERRUPTS_REG); - if (enabled) + 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; - else + irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; - rtc_wait_not_busy(); - rtc_write(reg, OMAP_RTC_INTERRUPTS_REG); + 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; @@ -201,39 +290,49 @@ 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(); - - tm->tm_sec = rtc_read(OMAP_RTC_SECONDS_REG); - tm->tm_min = rtc_read(OMAP_RTC_MINUTES_REG); - tm->tm_hour = rtc_read(OMAP_RTC_HOURS_REG); - tm->tm_mday = rtc_read(OMAP_RTC_DAYS_REG); - tm->tm_mon = rtc_read(OMAP_RTC_MONTHS_REG); - tm->tm_year = rtc_read(OMAP_RTC_YEARS_REG); - + 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); + if (tm2bcd(tm) < 0) return -EINVAL; + local_irq_disable(); - rtc_wait_not_busy(); + rtc_wait_not_busy(rtc); - rtc_write(tm->tm_year, OMAP_RTC_YEARS_REG); - rtc_write(tm->tm_mon, OMAP_RTC_MONTHS_REG); - rtc_write(tm->tm_mday, OMAP_RTC_DAYS_REG); - rtc_write(tm->tm_hour, OMAP_RTC_HOURS_REG); - rtc_write(tm->tm_min, OMAP_RTC_MINUTES_REG); - rtc_write(tm->tm_sec, OMAP_RTC_SECONDS_REG); + 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(); @@ -242,54 +341,135 @@ 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_wait_not_busy(rtc); - alm->time.tm_sec = rtc_read(OMAP_RTC_ALARM_SECONDS_REG); - alm->time.tm_min = rtc_read(OMAP_RTC_ALARM_MINUTES_REG); - alm->time.tm_hour = rtc_read(OMAP_RTC_ALARM_HOURS_REG); - alm->time.tm_mday = rtc_read(OMAP_RTC_ALARM_DAYS_REG); - alm->time.tm_mon = rtc_read(OMAP_RTC_ALARM_MONTHS_REG); - alm->time.tm_year = rtc_read(OMAP_RTC_ALARM_YEARS_REG); + 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); - alm->enabled = !!(rtc_read(OMAP_RTC_INTERRUPTS_REG) - & OMAP_RTC_INTERRUPTS_IT_ALARM); + + 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) { - u8 reg; + struct omap_rtc *rtc = dev_get_drvdata(dev); + u8 reg, irqwake_reg = 0; if (tm2bcd(&alm->time) < 0) return -EINVAL; local_irq_disable(); - rtc_wait_not_busy(); + rtc_wait_not_busy(rtc); - rtc_write(alm->time.tm_year, OMAP_RTC_ALARM_YEARS_REG); - rtc_write(alm->time.tm_mon, OMAP_RTC_ALARM_MONTHS_REG); - rtc_write(alm->time.tm_mday, OMAP_RTC_ALARM_DAYS_REG); - rtc_write(alm->time.tm_hour, OMAP_RTC_ALARM_HOURS_REG); - rtc_write(alm->time.tm_min, OMAP_RTC_ALARM_MINUTES_REG); - rtc_write(alm->time.tm_sec, OMAP_RTC_ALARM_SECONDS_REG); + 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); - reg = rtc_read(OMAP_RTC_INTERRUPTS_REG); - if (alm->enabled) + if (alm->enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; - else + irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; - rtc_write(reg, OMAP_RTC_INTERRUPTS_REG); + 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_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 two-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 omap_rtc *rtc = omap_rtc_power_off_rtc; + struct rtc_time tm; + unsigned long now; + 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 two seconds from now */ + omap_rtc_read_time_raw(rtc, &tm); + bcd2tm(&tm); + rtc_tm_to_time(&tm, &now); + rtc_time_to_tm(now + 2, &tm); + + if (tm2bcd(&tm) < 0) { + dev_err(&rtc->rtc->dev, "power off failed\n"); + return; + } + + 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); + rtc->type->lock(rtc); + + /* + * Wait for alarm to trigger (within two seconds) and external PMIC to + * power off the system. Add a 500 ms margin for external latencies + * (e.g. debounce circuits). + */ + mdelay(2500); +} + static struct rtc_class_ops omap_rtc_ops = { .read_time = omap_rtc_read_time, .set_time = omap_rtc_set_time, @@ -298,118 +478,150 @@ .alarm_irq_enable = omap_rtc_alarm_irq_enable, }; -static int omap_rtc_alarm; -static int omap_rtc_timer; +static const struct omap_rtc_device_type omap_rtc_default_type = { + .has_power_up_reset = true, + .lock = default_rtc_lock, + .unlock = default_rtc_unlock, +}; -#define OMAP_RTC_DATA_DA830_IDX 1 +static const struct omap_rtc_device_type omap_rtc_am3352_type = { + .has_32kclk_en = true, + .has_irqwakeen = true, + .has_pmic_mode = true, + .lock = am3352_rtc_lock, + .unlock = am3352_rtc_unlock, +}; -static struct platform_device_id omap_rtc_devtype[] = { +static const struct omap_rtc_device_type omap_rtc_da830_type = { + .lock = am3352_rtc_lock, + .unlock = am3352_rtc_unlock, +}; + +static const struct platform_device_id omap_rtc_id_table[] = { { - .name = DRIVER_NAME, + .name = "omap_rtc", + .driver_data = (kernel_ulong_t)&omap_rtc_default_type, + }, { + .name = "am3352-rtc", + .driver_data = (kernel_ulong_t)&omap_rtc_am3352_type, }, { .name = "da830-rtc", - .driver_data = OMAP_RTC_HAS_KICKER, - }, - {}, + .driver_data = (kernel_ulong_t)&omap_rtc_da830_type, + }, { + /* sentinel */ + } }; -MODULE_DEVICE_TABLE(platform, omap_rtc_devtype); +MODULE_DEVICE_TABLE(platform, omap_rtc_id_table); static const struct of_device_id omap_rtc_of_match[] = { - { .compatible = "ti,da830-rtc", - .data = &omap_rtc_devtype[OMAP_RTC_DATA_DA830_IDX], - }, - {}, + { + .compatible = "ti,am3352-rtc", + .data = &omap_rtc_am3352_type, + }, { + .compatible = "ti,da830-rtc", + .data = &omap_rtc_da830_type, + }, { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(of, omap_rtc_of_match); -static int __init omap_rtc_probe(struct platform_device *pdev) +static int omap_rtc_probe(struct platform_device *pdev) { - struct resource *res; - struct rtc_device *rtc; - u8 reg, new_ctrl; + struct omap_rtc *rtc; + struct resource *res; + u8 reg, mask, new_ctrl; const struct platform_device_id *id_entry; const struct of_device_id *of_id; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; of_id = of_match_device(omap_rtc_of_match, &pdev->dev); - if (of_id) - pdev->id_entry = of_id->data; + if (of_id) { + rtc->type = of_id->data; + rtc->is_pmic_controller = rtc->type->has_pmic_mode && + of_property_read_bool(pdev->dev.of_node, + "system-power-controller"); + } else { + id_entry = platform_get_device_id(pdev); + rtc->type = (void *)id_entry->driver_data; + } - omap_rtc_timer = platform_get_irq(pdev, 0); - if (omap_rtc_timer <= 0) { - pr_debug("%s: no update irq?\n", pdev->name); + rtc->irq_timer = platform_get_irq(pdev, 0); + if (rtc->irq_timer <= 0) return -ENOENT; - } - omap_rtc_alarm = platform_get_irq(pdev, 1); - if (omap_rtc_alarm <= 0) { - pr_debug("%s: no alarm irq?\n", pdev->name); + rtc->irq_alarm = platform_get_irq(pdev, 1); + if (rtc->irq_alarm <= 0) return -ENOENT; - } + + rtc->clk = devm_clk_get(&pdev->dev, "ext-clk"); + if (!IS_ERR(rtc->clk)) + rtc->has_ext_clk = true; + else + rtc->clk = devm_clk_get(&pdev->dev, "int-clk"); + + if (!IS_ERR(rtc->clk)) + clk_prepare_enable(rtc->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rtc_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(rtc_base)) - return PTR_ERR(rtc_base); + rtc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rtc->base)) + return PTR_ERR(rtc->base); + + platform_set_drvdata(pdev, rtc); /* Enable the clock/module so that we can access the registers */ pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - id_entry = platform_get_device_id(pdev); - if (id_entry && (id_entry->driver_data & OMAP_RTC_HAS_KICKER)) { - rtc_writel(KICK0_VALUE, OMAP_RTC_KICK0_REG); - rtc_writel(KICK1_VALUE, OMAP_RTC_KICK1_REG); - } - - rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &omap_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - pr_debug("%s: can't register RTC device, err %ld\n", - pdev->name, PTR_ERR(rtc)); - goto fail0; - } - platform_set_drvdata(pdev, rtc); + rtc->type->unlock(rtc); - /* clear pending irqs, and set 1/second periodic, - * which we'll use instead of update irqs + /* + * disable interrupts + * + * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used */ - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, 0); - /* clear old status */ - reg = rtc_read(OMAP_RTC_STATUS_REG); - if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) { - pr_info("%s: RTC power up reset detected\n", - pdev->name); - rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG); + /* enable RTC functional clock */ + if (rtc->type->has_32kclk_en) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + rtc_writel(rtc, OMAP_RTC_OSC_REG, + reg | OMAP_RTC_OSC_32KCLK_EN); } - if (reg & (u8) OMAP_RTC_STATUS_ALARM) - rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG); - /* handle periodic and alarm irqs */ - if (devm_request_irq(&pdev->dev, omap_rtc_timer, rtc_irq, 0, - dev_name(&rtc->dev), rtc)) { - pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n", - pdev->name, omap_rtc_timer); - goto fail0; - } - if ((omap_rtc_timer != omap_rtc_alarm) && - (devm_request_irq(&pdev->dev, omap_rtc_alarm, rtc_irq, 0, - dev_name(&rtc->dev), rtc))) { - pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n", - pdev->name, omap_rtc_alarm); - goto fail0; + /* 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(OMAP_RTC_CTRL_REG); - if (reg & (u8) OMAP_RTC_CTRL_STOP) - pr_info("%s: already running\n", pdev->name); + 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 = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP); new_ctrl |= OMAP_RTC_CTRL_STOP; - /* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: + /* + * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: * * - Device wake-up capability setting should come through chip * init logic. OMAP1 boards should initialize the "wakeup capable" @@ -423,34 +635,92 @@ * is write-only, and always reads as zero...) */ - if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT) - pr_info("%s: split power mode\n", pdev->name); + if (new_ctrl & OMAP_RTC_CTRL_SPLIT) + dev_info(&pdev->dev, "split power mode\n"); if (reg != new_ctrl) - rtc_write(new_ctrl, OMAP_RTC_CTRL_REG); + 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_writel(rtc, OMAP_RTC_OSC_REG, reg); + } + + rtc->type->lock(rtc); + + device_init_wakeup(&pdev->dev, true); + + rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &omap_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto err; + } + + /* handle periodic and alarm irqs */ + ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0, + dev_name(&rtc->rtc->dev), rtc); + if (ret) + goto err; + + if (rtc->irq_timer != rtc->irq_alarm) { + ret = devm_request_irq(&pdev->dev, rtc->irq_alarm, rtc_irq, 0, + dev_name(&rtc->rtc->dev), rtc); + if (ret) + goto err; + } + + if (rtc->is_pmic_controller) { + if (!pm_power_off) { + omap_rtc_power_off_rtc = rtc; + pm_power_off = omap_rtc_power_off; + } + } return 0; -fail0: - if (id_entry && (id_entry->driver_data & OMAP_RTC_HAS_KICKER)) - rtc_writel(0, OMAP_RTC_KICK0_REG); +err: + device_init_wakeup(&pdev->dev, false); + rtc->type->lock(rtc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - return -EIO; + + return ret; } static int __exit omap_rtc_remove(struct platform_device *pdev) { - const struct platform_device_id *id_entry = - platform_get_device_id(pdev); + struct omap_rtc *rtc = platform_get_drvdata(pdev); + u8 reg; + + if (pm_power_off == omap_rtc_power_off && + omap_rtc_power_off_rtc == rtc) { + pm_power_off = NULL; + omap_rtc_power_off_rtc = NULL; + } device_init_wakeup(&pdev->dev, 0); + if (!IS_ERR(rtc->clk)) + clk_disable_unprepare(rtc->clk); + + rtc->type->unlock(rtc); /* leave rtc running, but disable irqs */ - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); + + if (rtc->has_ext_clk) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_SEL_32KCLK_SRC; + rtc_write(rtc, OMAP_RTC_OSC_REG, reg); + } - if (id_entry && (id_entry->driver_data & OMAP_RTC_HAS_KICKER)) - rtc_writel(0, OMAP_RTC_KICK0_REG); + rtc->type->lock(rtc); /* Disable the clock/module */ pm_runtime_put_sync(&pdev->dev); @@ -460,20 +730,23 @@ } #ifdef CONFIG_PM_SLEEP -static u8 irqstat; - static int omap_rtc_suspend(struct device *dev) { - irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG); + struct omap_rtc *rtc = dev_get_drvdata(dev); + + rtc->interrupts_reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); - /* FIXME the RTC alarm is not currently acting as a wakeup event - * source, and in fact this enable() call is just saving a flag - * that's never used... + 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(omap_rtc_alarm); + enable_irq_wake(rtc->irq_alarm); else - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); + rtc->type->lock(rtc); /* Disable the clock/module */ pm_runtime_put_sync(dev); @@ -483,13 +756,18 @@ static int omap_rtc_resume(struct device *dev) { + struct omap_rtc *rtc = dev_get_drvdata(dev); + /* Enable the clock/module so that we can access the registers */ pm_runtime_get_sync(dev); + rtc->type->unlock(rtc); if (device_may_wakeup(dev)) - disable_irq_wake(omap_rtc_alarm); + disable_irq_wake(rtc->irq_alarm); else - rtc_write(irqstat, OMAP_RTC_INTERRUPTS_REG); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, rtc->interrupts_reg); + rtc->type->lock(rtc); + return 0; } #endif @@ -498,23 +776,34 @@ static void omap_rtc_shutdown(struct platform_device *pdev) { - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + struct omap_rtc *rtc = platform_get_drvdata(pdev); + u8 mask; + + /* + * 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_ALIAS("platform:omap_rtc"); static struct platform_driver omap_rtc_driver = { + .probe = omap_rtc_probe, .remove = __exit_p(omap_rtc_remove), .shutdown = omap_rtc_shutdown, .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, + .name = "omap_rtc", .pm = &omap_rtc_pm_ops, - .of_match_table = of_match_ptr(omap_rtc_of_match), + .of_match_table = omap_rtc_of_match, }, - .id_table = omap_rtc_devtype, + .id_table = omap_rtc_id_table, }; -module_platform_driver_probe(omap_rtc_driver, omap_rtc_probe); +module_platform_driver(omap_rtc_driver); +MODULE_ALIAS("platform:omap_rtc"); MODULE_AUTHOR("George G. Davis (and others)"); MODULE_LICENSE("GPL");