diff -drupN a/drivers/rtc/rtc-ingenic.c b/drivers/rtc/rtc-ingenic.c --- a/drivers/rtc/rtc-ingenic.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/rtc/rtc-ingenic.c 2022-06-09 05:02:33.000000000 +0300 @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2015 Ingenic Semiconductor Co., Ltd. + * Author: cli + * + * Real Time Clock interface for Ingenic's SOC, such as X1000, + * and so on. (kernel.4.4) + * + * Base on:rtc-generic: RTC driver using the generic RTC abstraction + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtc-ingenic.h" + +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) +#include +#include +#endif + +struct ingenic_rtc_device { + struct rtc_device *rtc; + struct device *dev; + struct mutex reg_mutex; + bool pmic_vailed; /*power manage ic is vailed*/ + void __iomem *reg_base; + int irq; + uint32_t lprs_pwon_ms; /*long press power ms time on Wakeup pin*/ + uint32_t hr_assert_ms; /*HIBERNATE Reset assert time n*125ms*/ + struct clk *rtc_gate; + int rtc_clk_rate; + unsigned int hwrsr; + struct notifier_block restart_handler; +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) + struct dentry *debugfs; +#endif +#ifdef CONFIG_SUSPEND_TEST + unsigned int sleep_count; + unsigned int os_alarm_time; + unsigned int save_rtccr; +#endif +}; + +static struct ingenic_rtc_device *m_rtc; + +static bool ingenic_rtc_clk_disconnect = false; + +static int ingenic_rtc_read(struct ingenic_rtc_device* rtc, int reg) +{ + return readl(rtc->reg_base + reg); +} + +static int ingenic_rtc_write(struct ingenic_rtc_device* rtc, int reg, int val) +{ + int timeout = 1000000; + + mutex_lock(&rtc->reg_mutex); + + writel(WENR_WENPAT_WRITABLE, rtc->reg_base + RTC_WENR); + + while (!(readl(rtc->reg_base + RTC_WENR) & WENR_WEN) && + --timeout); + if (!timeout) { + dev_warn(rtc->dev, "wait rtc wenr timeout\n"); + mutex_unlock(&rtc->reg_mutex); + return -EIO; + } + timeout = 1000000; + while(!(readl(rtc->reg_base + RTC_RTCCR) & RTCCR_WRDY) && + --timeout); + if (!timeout) { + dev_warn(rtc->dev, "wait rtc write ready timeout 1 %x:%x (rtccr:%x)\n", + reg, val, + readl(rtc->reg_base + RTC_RTCCR)); + mutex_unlock(&rtc->reg_mutex); + return -EIO; + } + + writel(val, rtc->reg_base + reg); + + timeout = 1000000; + while(!(readl(rtc->reg_base + RTC_RTCCR) & RTCCR_WRDY) && + --timeout); + if (!timeout) { + dev_warn(rtc->dev, "wait rtc write ready timeout 2 %x:%x (rtccr:%x)\n", + reg, val, + readl(rtc->reg_base + RTC_RTCCR)); + mutex_unlock(&rtc->reg_mutex); + return -EIO; + } + + mutex_unlock(&rtc->reg_mutex); + return 0; +} + +static int ingenic_rtc_prepare_enable(struct ingenic_rtc_device *rtc) +{ + if (ingenic_rtc_read(rtc, RTC_HSPR) != HSPR_RTCV) { /*rtc power on*/ + unsigned int rtccr, tmp; + int ret; + dev_info(rtc->dev, "usb rtc clk, system power on first time\n"); + + /*Try use rtc clk*/ + rtccr = RTCCR_RTCE; + //writel(rtccr, rtc->reg_base + RTC_RTCCR); + ingenic_rtc_clk_disconnect = false; + ret = ingenic_rtc_write(rtc, RTC_RTCGR, (rtc->rtc_clk_rate - 1) & RTCGR_NC1HZ_MASK); + if (!ret && (ingenic_rtc_read(rtc, RTC_RTCGR) & RTCGR_NC1HZ_MASK) == + (rtc->rtc_clk_rate - 1)) + goto rtc_power_on; + + /*Try use ext clk / 512*/ + dev_info(rtc->dev, "use (extern clk)/512\n"); + rtccr = RTCCR_SELEXC | RTCCR_RTCE; + writel(rtccr, rtc->reg_base + RTC_RTCCR); + rtc->rtc_clk_rate = 24000000/512; + ingenic_rtc_clk_disconnect = true; + ret = ingenic_rtc_write(rtc, RTC_RTCGR, (rtc->rtc_clk_rate - 1) & RTCGR_NC1HZ_MASK); + if (ret || (ingenic_rtc_read(rtc, RTC_RTCGR) & RTCGR_NC1HZ_MASK) != + (rtc->rtc_clk_rate - 1)) + return -ENODEV; +rtc_power_on: + ingenic_rtc_write(rtc, RTC_HWFCR, HWFCR_WAIT_TIME(rtc->lprs_pwon_ms, rtc->rtc_clk_rate)); + ingenic_rtc_write(rtc, RTC_HRCR, HRCR_WAIT_TIME(rtc->hr_assert_ms, rtc->rtc_clk_rate)); + ingenic_rtc_write(rtc, RTC_RTCSR, 0); + ingenic_rtc_write(rtc, RTC_RTCSAR, 0); + tmp = ingenic_rtc_read(rtc, RTC_WKUPPINCR); + tmp &= ~(WKUPPINCR_P_JUD_EN); + ingenic_rtc_write(rtc, RTC_WKUPPINCR, tmp); + ingenic_rtc_write(rtc, RTC_RTCCR, rtccr); + ingenic_rtc_write(rtc, RTC_HSPR, HSPR_RTCV); + } else { + int temp, temp_new; + + temp = HWFCR_WAIT_TIME(rtc->lprs_pwon_ms, rtc->rtc_clk_rate); + if (temp != (ingenic_rtc_read(rtc, RTC_HWFCR) & HWFCR_MASK)) + ingenic_rtc_write(rtc, RTC_HWFCR, temp); + + temp = HRCR_WAIT_TIME(rtc->hr_assert_ms, rtc->rtc_clk_rate); + if (temp != (ingenic_rtc_read(rtc, RTC_HRCR) & HRCR_MASK)) + ingenic_rtc_write(rtc, RTC_HRCR, temp); + + temp = ingenic_rtc_read(rtc, RTC_WKUPPINCR); + temp_new = temp & (~WKUPPINCR_P_JUD_EN); + if (temp_new != temp) + ingenic_rtc_write(rtc, RTC_WKUPPINCR, temp_new); + + rtc->hwrsr = ingenic_rtc_read(rtc, RTC_HWRSR); + } + ingenic_rtc_write(rtc, RTC_HWRSR, 0x0); + ingenic_rtc_write(rtc, RTC_RTCGR, RTCGR_LOCK | ingenic_rtc_read(rtc, RTC_RTCGR)); + ingenic_rtc_write(rtc, RTC_HWCR, HWCR_EALM | EPDET_ENABLE); + + return 0; +} + +static irqreturn_t ingenic_rtc_interrupt_thread_handler(int irq, void *dev_id) +{ + struct ingenic_rtc_device *rtc = dev_id; + unsigned int rtccr; + + mutex_lock(&rtc->rtc->ops_lock); + rtccr = ingenic_rtc_read(rtc, RTC_RTCCR); + rtccr &= ~(RTCCR_1HZ | RTCCR_AF); + ingenic_rtc_write(rtc, RTC_RTCCR, rtccr); + mutex_unlock(&rtc->rtc->ops_lock); + + rtc_update_irq(rtc->rtc, 0/*uncare*/, 0/*uncare*/); + + return IRQ_HANDLED; +} + +static int ingenic_rtc_get_time(struct device *dev, struct rtc_time *tm) +{ + struct ingenic_rtc_device *rtc = dev_get_drvdata(dev); + uint32_t secs, secs2; + int timeout = 10; + + /* If the seconds register is read while it is updated, it can contain a + * bogus value. This can be avoided by making sure that two consecutive + * reads have the same value. + */ + secs = readl(rtc->reg_base + RTC_RTCSR); + secs = readl(rtc->reg_base + RTC_RTCSR); + + while (secs != secs2 && --timeout) { + secs = secs2; + secs2 = readl(rtc->reg_base + RTC_RTCSR); + } + + if (timeout == 0) + return -EIO; + + rtc_time_to_tm(secs, tm); + + return rtc_valid_tm(tm); +} + +static int ingenic_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct ingenic_rtc_device *rtc = dev_get_drvdata(dev); + return ingenic_rtc_write(rtc, RTC_RTCSR, secs); +} + +static int ingenic_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ingenic_rtc_device *rtc = dev_get_drvdata(dev); + unsigned int rtccr_new, rtccr; + + rtccr = ingenic_rtc_read(rtc, RTC_RTCCR); + /*WARN_ON(!(enabled && (rtccr & (RTCCR_1HZIE|RTCCR_AIE))));*/ + + if (!enabled) { + rtccr_new = rtccr; + if (!rtc->rtc->uie_rtctimer.enabled) + rtccr_new &= ~(RTCCR_1HZIE); + rtccr_new &= ~(RTCCR_AIE | RTCCR_AE); + if (rtccr_new != rtccr) + ingenic_rtc_write(rtc, RTC_RTCCR, rtccr_new); + } + pr_debug("=====> %s %d enabled %d, %x\n", __func__, __LINE__, + enabled, + ingenic_rtc_read(rtc, RTC_RTCCR)); + return 0; +} + +static int ingenic_rtc_read_alarm(struct device * dev, struct rtc_wkalrm *alrm) +{ + struct ingenic_rtc_device *rtc = dev_get_drvdata(dev); + uint32_t secs; + uint32_t ctrl; + + secs = ingenic_rtc_read(rtc, RTC_RTCSAR); + ctrl = ingenic_rtc_read(rtc, RTC_RTCCR); + + alrm->enabled = !!(ctrl & RTCCR_AIE); + alrm->pending = !!(ctrl & RTCCR_AF); + + rtc_time_to_tm(secs, &alrm->time); + + return rtc_valid_tm(&alrm->time); +} + +static int ingenic_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ingenic_rtc_device *rtc = dev_get_drvdata(dev); + struct timerqueue_node *next = timerqueue_getnext(&rtc->rtc->timerqueue); + struct rtc_timer *timer; + unsigned long secs; + unsigned int rtccr_new, rtccr; + int ret = 0; + + BUG_ON(!next); + timer = container_of(next, struct rtc_timer, node); + rtc_tm_to_time(&alrm->time, &secs); + + if (&rtc->rtc->uie_rtctimer == timer) { + /*1HZ peroid interrupt*/ + rtccr = rtccr_new = ingenic_rtc_read(rtc, RTC_RTCCR); + rtccr_new &= ~(RTCCR_AIE | RTCCR_AE); + rtccr_new |= RTCCR_1HZIE; + if (rtccr_new != rtccr) + ret = ingenic_rtc_write(rtc, RTC_RTCCR, rtccr_new); + } else { + /*alarm interrupt*/ + ret = ingenic_rtc_write(rtc, RTC_RTCSAR, secs); + if (!ret) { + rtccr = rtccr_new = ingenic_rtc_read(rtc, RTC_RTCCR); + rtccr_new &= ~(RTCCR_1HZIE); + rtccr_new |= RTCCR_AIE | RTCCR_AE; + if (rtccr_new != rtccr) { + ret = ingenic_rtc_write(rtc, RTC_RTCCR, rtccr_new); + } + } + } + pr_debug("=====> %s %d %x:%x:%x\n", __func__, __LINE__, + ingenic_rtc_read(rtc, RTC_RTCSR), + ingenic_rtc_read(rtc, RTC_RTCSAR), + ingenic_rtc_read(rtc, RTC_RTCCR)); + return ret; +} + +static int ingenic_rtc_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct ingenic_rtc_device *rtc = dev_get_drvdata(dev); + + seq_printf(seq, "RTC regulator\t: 0x%08x\n", + ingenic_rtc_read(rtc, RTC_RTCGR)); + seq_printf(seq, "update_IRQ\t: %s\n", + (ingenic_rtc_read(rtc, RTC_RTCCR) & RTCCR_1HZIE) ? "yes" : "no"); + + if (rtc->hwrsr & HWRSR_APD) + seq_printf(seq, "Accident power down\n"); + if (rtc->hwrsr & HWRSR_HR) + seq_printf(seq, "Hibernate Reset: \n"); + if (rtc->hwrsr & HWRSR_PIN) + seq_printf(seq, "\tWakeup Pin wakeup system\n"); + if (rtc->hwrsr & HWRSR_ALM) + seq_printf(seq, "\tAlarm wakeup system\n"); + if (rtc->hwrsr & HWRSR_PPR) + seq_printf(seq, "PAD PIN Reset\n"); + return 0; +} + +static const struct rtc_class_ops ingenic_rtc_ops = { + .read_time = ingenic_rtc_get_time, + .set_mmss = ingenic_rtc_set_mmss, + .alarm_irq_enable = ingenic_rtc_alarm_irq_enable, + .read_alarm = ingenic_rtc_read_alarm, + .set_alarm = ingenic_rtc_set_alarm, + .proc = ingenic_rtc_rtc_proc, +}; + +#define INGENIC_RTC_DEFUALT_PWON_PRESS_MS (2000) +#define INGENIC_RTC_DEFUALT_HIBERNATE_RESET_ASSERT_MS (60) /*copy from old rtc driver*/ + +static void ingenic_rtc_hibernate(void) +{ + struct ingenic_rtc_device *rtc = m_rtc; + + mutex_lock(&rtc->rtc->ops_lock); + ingenic_rtc_write(rtc, RTC_HCR, HCR_PD); + mdelay(2000); + mutex_unlock(&rtc->rtc->ops_lock); + pr_err("RTC hibernate has been run, but extern power not down RTC_HCR(%x)\n", + ingenic_rtc_read(rtc, RTC_HCR)); + while(1); +} + +static int ingenic_rtc_reset_handler(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct ingenic_rtc_device *rtc = m_rtc; + uint32_t rtc_rtcsr,rtc_rtccr; + + /* + * Use setup params "reboot=h" (mode == REBOOT_HARD) , + * enable the hibernate reset function + */ + if (mode != REBOOT_HARD || (cmd && (!strcmp(cmd, REBOOT_CMD_RECOVERY) || + !strcmp(cmd, REBOOT_CMD_SOFTBURN)))) { + pr_debug("%s %d mode %lu cmd %s\n", __func__, __LINE__, mode, cmd ? (char*)cmd : "null"); + return NOTIFY_DONE; + } + pr_info("hibernate reset"); + mutex_lock(&rtc->rtc->ops_lock); + rtc_rtcsr = ingenic_rtc_read(rtc, RTC_RTCSR); + ingenic_rtc_write(rtc, RTC_RTCSAR, rtc_rtcsr + 5); /*5s delay*/ + rtc_rtccr = ingenic_rtc_read(rtc, RTC_RTCCR); + rtc_rtccr &= ~RTCCR_AF; + rtc_rtccr |= RTCCR_AIE | RTCCR_AE; + ingenic_rtc_write(rtc, RTC_RTCCR, rtc_rtccr); + ingenic_rtc_write(rtc, RTC_HWRSR, 0x0); + ingenic_rtc_write(rtc, RTC_HWCR, HWCR_EALM|EPDET_ENABLE); + ingenic_rtc_write(rtc, RTC_HCR, HCR_PD); + while(1) { + mdelay(200); + printk("%s:We should NOT come here.%08x\n", + __func__, ingenic_rtc_read(rtc, RTC_HCR)); + } + mutex_unlock(&rtc->rtc->ops_lock); + return NOTIFY_STOP; +} + +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) +static ssize_t ingenic_rtc_show_regs(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ingenic_rtc_device *rtc = file->private_data; + char *buf; + int len = 0; + ssize_t ret; + +#define REGS_BUFSIZE 1024 + buf = kzalloc(REGS_BUFSIZE, GFP_KERNEL); + if (!buf) + return 0; + len += snprintf(buf + len, REGS_BUFSIZE - len, "rtc register: %p\n", rtc->reg_base); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_RTCCR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_RTCCR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_RTCSR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_RTCSR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_RTCSAR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_RTCSAR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_RTCGR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_RTCGR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_HCR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_HCR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_HWFCR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_HWFCR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_HRCR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_HRCR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_HWCR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_HWCR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_HWRSR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_HWRSR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_HSPR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_HSPR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_WENR : 0x%08x\n", ingenic_rtc_read(rtc, RTC_WENR)); + len += snprintf(buf + len, REGS_BUFSIZE - len, "RTC_WKUPPINCR: 0x%08x\n", ingenic_rtc_read(rtc, RTC_WKUPPINCR)); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); +#undef REGS_BUFSIZE + return ret; +} + +static struct file_operations ingenic_rtc_reg_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = ingenic_rtc_show_regs, + .llseek = default_llseek, +}; +#endif + +static int __init ingenic_rtc_probe(struct platform_device *pdev) +{ + struct ingenic_rtc_device *ingenic_rtc; + struct resource *res; + struct clk *rtc_clk; + struct clk *rtc_gate; + int ret; + + ingenic_rtc = devm_kzalloc(&pdev->dev, sizeof(struct ingenic_rtc_device), GFP_KERNEL); + if (!ingenic_rtc) + return -ENOMEM; + + ingenic_rtc->pmic_vailed = of_property_read_bool(pdev->dev.of_node, + "system-power-controller"); + if (ingenic_rtc->pmic_vailed) { + ret = of_property_read_u32(pdev->dev.of_node, + "power-on-press-ms", + &ingenic_rtc->lprs_pwon_ms); + if (ret) + ingenic_rtc->lprs_pwon_ms = INGENIC_RTC_DEFUALT_PWON_PRESS_MS; + ingenic_rtc->hr_assert_ms = INGENIC_RTC_DEFUALT_HIBERNATE_RESET_ASSERT_MS; + if (!pm_power_off) { + m_rtc = ingenic_rtc; + pm_power_off = ingenic_rtc_hibernate; + } + } + ingenic_rtc->dev = &pdev->dev; + mutex_init(&ingenic_rtc->reg_mutex); + + rtc_gate = clk_get(&pdev->dev, "gate_rtc"); + if (IS_ERR(rtc_gate)) + return PTR_ERR(rtc_gate); + clk_prepare_enable(rtc_gate); + clk_put(rtc_gate); + + rtc_clk = clk_get(&pdev->dev, "rtc"); + if (IS_ERR_OR_NULL(rtc_clk) || + (ingenic_rtc->rtc_clk_rate = clk_get_rate(rtc_clk)) <= 0) { + dev_warn(&pdev->dev, "Impossible: can not find fin_rtc(use 32768)\n"); + ingenic_rtc->rtc_clk_rate = 32768; + } + if (!IS_ERR_OR_NULL(rtc_clk)) + clk_put(rtc_clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + ingenic_rtc->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ingenic_rtc->reg_base)) + return PTR_ERR(ingenic_rtc->reg_base); + + ingenic_rtc->irq = platform_get_irq(pdev, 0); + if (ingenic_rtc->irq < 0) + return ingenic_rtc->irq; + + ret = ingenic_rtc_prepare_enable(ingenic_rtc); + if (ret) + return ret; + + device_init_wakeup(&pdev->dev, true); + platform_set_drvdata(pdev, ingenic_rtc); + + ingenic_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rtc-ingenic", + &ingenic_rtc_ops, THIS_MODULE); + if (IS_ERR(ingenic_rtc->rtc)) + return PTR_ERR(ingenic_rtc->rtc); + + ret = devm_request_threaded_irq(&pdev->dev, ingenic_rtc->irq, NULL, + ingenic_rtc_interrupt_thread_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rtc 1HZ and alarm", + (void *)ingenic_rtc); + if (ret) + return ret; + + ingenic_rtc->restart_handler.notifier_call = ingenic_rtc_reset_handler; + ingenic_rtc->restart_handler.priority = RTC_HIBERNATE_RESET_PROR; + ret = register_restart_handler(&ingenic_rtc->restart_handler); + if (ret) + dev_warn(&pdev->dev, + "cannot register rtc restart\ + handler (err=%d)\n", ret); + + +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) + ingenic_rtc->debugfs = debugfs_create_file("rtc_reg", 0x444, NULL, + ingenic_rtc, &ingenic_rtc_reg_ops); +#endif + return 0; +} + +static void __exit ingenic_rtc_remove(struct platform_device *pdev) +{ +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) + struct ingenic_rtc_device *ingenic_rtc = platform_get_drvdata(pdev); + if (ingenic_rtc->debugfs) + debugfs_remove(ingenic_rtc->debugfs); +#endif +} + +static const struct of_device_id ingenic_rtc_of_match[] = { + { .compatible = "ingenic,rtc", .data = NULL, }, + {}, +}; +MODULE_DEVICE_TABLE(of, ingenic_rtc_of_match); + +#ifdef CONFIG_PM +static int ingenic_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ingenic_rtc_device *rtc = platform_get_drvdata(pdev); +#ifdef CONFIG_SUSPEND_TEST + unsigned int val; + unsigned int test_alarm_time, sr_time; + + val = ingenic_rtc_read(rtc, RTC_RTCCR); + if(val & RTCCR_AE) { + rtc->save_rtccr = val; + rtc->os_alarm_time = ingenic_rtc_read(rtc, RTC_RTCSAR); + } + val |= RTCCR_AIE | RTCCR_AE; + ingenic_rtc_write(rtc, RTC_RTCCR, val); + + sr_time = ingenic_rtc_read(rtc, RTC_RTCSR); + test_alarm_time = sr_time + CONFIG_SUSPEND_ALARM_TIME; + if(rtc->os_alarm_time && rtc->os_alarm_time > sr_time \ + && rtc->os_alarm_time < test_alarm_time) + test_alarm_time = rtc->os_alarm_time; + ingenic_rtc_write(rtc, RTC_RTCSAR, test_alarm_time); + + printk("-------suspend count = %d\n", rtc->sleep_count++); +#endif + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(rtc->irq); + return 0; +} + +static int ingenic_rtc_resume(struct platform_device *pdev) +{ + struct ingenic_rtc_device *rtc = platform_get_drvdata(pdev); +#ifdef CONFIG_SUSPEND_TEST + if(rtc->save_rtccr & RTCCR_AE) { + ingenic_rtc_write(rtc, RTC_RTCSAR, rtc->os_alarm_time); + ingenic_rtc_write(rtc, RTC_RTCCR, rtc->save_rtccr); + rtc->os_alarm_time = 0; + rtc->save_rtccr = 0; + } else { + unsigned int val; + val = ingenic_rtc_read(rtc, RTC_RTCCR); + val &= ~ (RTCCR_AF |RTCCR_AIE | RTCCR_AE); + ingenic_rtc_write(rtc, RTC_RTCCR, val); + } +#endif + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtc->irq); + return 0; +} + +#else +#define ingenic_rtc_suspend NULL +#define ingenic_rtc_resume NULL +#endif + +static struct platform_driver ingenic_rtc_driver = { + .driver = { + .name = "rtc-ingenic", + .of_match_table = ingenic_rtc_of_match, + }, + .remove = __exit_p(ingenic_rtc_remove), + .suspend = ingenic_rtc_suspend, + .resume = ingenic_rtc_resume, +}; +module_platform_driver_probe(ingenic_rtc_driver, ingenic_rtc_probe); + +MODULE_AUTHOR("Cli "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ingenic RTC driver"); +MODULE_ALIAS("platform:rtc-ingenic");