firmware/br-ext-chip-ingenic/board/t40/kernel/patches/00000-drivers_rtc_rtc-ingen...

600 lines
19 KiB
Diff

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 <chen.li@ingenic.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <soc/xburst/reboot.h>
+
+#include "rtc-ingenic.h"
+
+#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#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 <chen.li@ingenic.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Ingenic RTC driver");
+MODULE_ALIAS("platform:rtc-ingenic");