summaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig9
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/interface.c2
-rw-r--r--drivers/rtc/rtc-dev.c3
-rw-r--r--drivers/rtc/rtc-ds1307.c1
-rw-r--r--drivers/rtc/rtc-puv3.c359
-rw-r--r--drivers/rtc/rtc-vt8500.c45
7 files changed, 376 insertions, 44 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index f822e13..ce2aabf 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1051,4 +1051,13 @@ config RTC_DRV_TILE
Enable support for the Linux driver side of the Tilera
hypervisor's real-time clock interface.
+config RTC_DRV_PUV3
+ tristate "PKUnity v3 RTC support"
+ depends on ARCH_PUV3
+ help
+ This enables support for the RTC in the PKUnity-v3 SoCs.
+
+ This drive can also be built as a module. If so, the module
+ will be called rtc-puv3.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 213d725..0ffefe8 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
+obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index ef6316a..df68618 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -318,7 +318,7 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
}
EXPORT_SYMBOL_GPL(rtc_read_alarm);
-int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
struct rtc_time tm;
long now, scheduled;
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index d0e06ed..cace6d3 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -421,7 +421,8 @@ static long rtc_dev_ioctl(struct file *file,
err = ops->ioctl(rtc->dev.parent, cmd, arg);
if (err == -ENOIOCTLCMD)
err = -ENOTTY;
- }
+ } else
+ err = -ENOTTY;
break;
}
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 4724ba3..b2005b4 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -149,6 +149,7 @@ static const struct i2c_device_id ds1307_id[] = {
{ "ds1340", ds_1340 },
{ "ds3231", ds_3231 },
{ "m41t00", m41t00 },
+ { "pt7c4338", ds_1307 },
{ "rx8025", rx_8025 },
{ }
};
diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c
new file mode 100644
index 0000000..46f14b8
--- /dev/null
+++ b/drivers/rtc/rtc-puv3.c
@@ -0,0 +1,359 @@
+/*
+ * RTC driver code specific to PKUnity SoC and UniCore ISA
+ *
+ * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
+ * Copyright (C) 2001-2010 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+
+static struct resource *puv3_rtc_mem;
+
+static int puv3_rtc_alarmno = IRQ_RTCAlarm;
+static int puv3_rtc_tickno = IRQ_RTC;
+
+static DEFINE_SPINLOCK(puv3_rtc_pie_lock);
+
+/* IRQ Handlers */
+static irqreturn_t puv3_rtc_alarmirq(int irq, void *id)
+{
+ struct rtc_device *rdev = id;
+
+ writel(readl(RTC_RTSR) | RTC_RTSR_AL, RTC_RTSR);
+ rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t puv3_rtc_tickirq(int irq, void *id)
+{
+ struct rtc_device *rdev = id;
+
+ writel(readl(RTC_RTSR) | RTC_RTSR_HZ, RTC_RTSR);
+ rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+/* Update control registers */
+static void puv3_rtc_setaie(int to)
+{
+ unsigned int tmp;
+
+ pr_debug("%s: aie=%d\n", __func__, to);
+
+ tmp = readl(RTC_RTSR) & ~RTC_RTSR_ALE;
+
+ if (to)
+ tmp |= RTC_RTSR_ALE;
+
+ writel(tmp, RTC_RTSR);
+}
+
+static int puv3_rtc_setpie(struct device *dev, int enabled)
+{
+ unsigned int tmp;
+
+ pr_debug("%s: pie=%d\n", __func__, enabled);
+
+ spin_lock_irq(&puv3_rtc_pie_lock);
+ tmp = readl(RTC_RTSR) & ~RTC_RTSR_HZE;
+
+ if (enabled)
+ tmp |= RTC_RTSR_HZE;
+
+ writel(tmp, RTC_RTSR);
+ spin_unlock_irq(&puv3_rtc_pie_lock);
+
+ return 0;
+}
+
+/* Time read/write */
+static int puv3_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ rtc_time_to_tm(readl(RTC_RCNR), rtc_tm);
+
+ pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
+ rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+
+ return 0;
+}
+
+static int puv3_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long rtc_count = 0;
+
+ pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ rtc_tm_to_time(tm, &rtc_count);
+ writel(rtc_count, RTC_RCNR);
+
+ return 0;
+}
+
+static int puv3_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *alm_tm = &alrm->time;
+
+ rtc_time_to_tm(readl(RTC_RTAR), alm_tm);
+
+ alrm->enabled = readl(RTC_RTSR) & RTC_RTSR_ALE;
+
+ pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
+ alrm->enabled,
+ alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+ alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+
+ return 0;
+}
+
+static int puv3_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *tm = &alrm->time;
+ unsigned long rtcalarm_count = 0;
+
+ pr_debug("puv3_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
+ alrm->enabled,
+ tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
+ tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
+
+ rtc_tm_to_time(tm, &rtcalarm_count);
+ writel(rtcalarm_count, RTC_RTAR);
+
+ puv3_rtc_setaie(alrm->enabled);
+
+ if (alrm->enabled)
+ enable_irq_wake(puv3_rtc_alarmno);
+ else
+ disable_irq_wake(puv3_rtc_alarmno);
+
+ return 0;
+}
+
+static int puv3_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (readl(RTC_RTSR) & RTC_RTSR_HZE) ? "yes" : "no");
+ return 0;
+}
+
+static int puv3_rtc_open(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = request_irq(puv3_rtc_alarmno, puv3_rtc_alarmirq,
+ IRQF_DISABLED, "pkunity-rtc alarm", rtc_dev);
+
+ if (ret) {
+ dev_err(dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret);
+ return ret;
+ }
+
+ ret = request_irq(puv3_rtc_tickno, puv3_rtc_tickirq,
+ IRQF_DISABLED, "pkunity-rtc tick", rtc_dev);
+
+ if (ret) {
+ dev_err(dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret);
+ goto tick_err;
+ }
+
+ return ret;
+
+ tick_err:
+ free_irq(puv3_rtc_alarmno, rtc_dev);
+ return ret;
+}
+
+static void puv3_rtc_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
+
+ /* do not clear AIE here, it may be needed for wake */
+ puv3_rtc_setpie(dev, 0);
+ free_irq(puv3_rtc_alarmno, rtc_dev);
+ free_irq(puv3_rtc_tickno, rtc_dev);
+}
+
+static const struct rtc_class_ops puv3_rtcops = {
+ .open = puv3_rtc_open,
+ .release = puv3_rtc_release,
+ .read_time = puv3_rtc_gettime,
+ .set_time = puv3_rtc_settime,
+ .read_alarm = puv3_rtc_getalarm,
+ .set_alarm = puv3_rtc_setalarm,
+ .proc = puv3_rtc_proc,
+};
+
+static void puv3_rtc_enable(struct platform_device *pdev, int en)
+{
+ if (!en) {
+ writel(readl(RTC_RTSR) & ~RTC_RTSR_HZE, RTC_RTSR);
+ } else {
+ /* re-enable the device, and check it is ok */
+ if ((readl(RTC_RTSR) & RTC_RTSR_HZE) == 0) {
+ dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
+ writel(readl(RTC_RTSR) | RTC_RTSR_HZE, RTC_RTSR);
+ }
+ }
+}
+
+static int puv3_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ platform_set_drvdata(dev, NULL);
+ rtc_device_unregister(rtc);
+
+ puv3_rtc_setpie(&dev->dev, 0);
+ puv3_rtc_setaie(0);
+
+ release_resource(puv3_rtc_mem);
+ kfree(puv3_rtc_mem);
+
+ return 0;
+}
+
+static int puv3_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ int ret;
+
+ pr_debug("%s: probe=%p\n", __func__, pdev);
+
+ /* find the IRQs */
+ puv3_rtc_tickno = platform_get_irq(pdev, 1);
+ if (puv3_rtc_tickno < 0) {
+ dev_err(&pdev->dev, "no irq for rtc tick\n");
+ return -ENOENT;
+ }
+
+ puv3_rtc_alarmno = platform_get_irq(pdev, 0);
+ if (puv3_rtc_alarmno < 0) {
+ dev_err(&pdev->dev, "no irq for alarm\n");
+ return -ENOENT;
+ }
+
+ pr_debug("PKUnity_rtc: tick irq %d, alarm irq %d\n",
+ puv3_rtc_tickno, puv3_rtc_alarmno);
+
+ /* get the memory region */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region resource\n");
+ return -ENOENT;
+ }
+
+ puv3_rtc_mem = request_mem_region(res->start,
+ res->end-res->start+1,
+ pdev->name);
+
+ if (puv3_rtc_mem == NULL) {
+ dev_err(&pdev->dev, "failed to reserve memory region\n");
+ ret = -ENOENT;
+ goto err_nores;
+ }
+
+ puv3_rtc_enable(pdev, 1);
+
+ /* register RTC and exit */
+ rtc = rtc_device_register("pkunity", &pdev->dev, &puv3_rtcops,
+ THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "cannot attach rtc\n");
+ ret = PTR_ERR(rtc);
+ goto err_nortc;
+ }
+
+ /* platform setup code should have handled this; sigh */
+ if (!device_can_wakeup(&pdev->dev))
+ device_init_wakeup(&pdev->dev, 1);
+
+ platform_set_drvdata(pdev, rtc);
+ return 0;
+
+ err_nortc:
+ puv3_rtc_enable(pdev, 0);
+ release_resource(puv3_rtc_mem);
+
+ err_nores:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+
+static int ticnt_save;
+
+static int puv3_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /* save RTAR for anyone using periodic interrupts */
+ ticnt_save = readl(RTC_RTAR);
+ puv3_rtc_enable(pdev, 0);
+ return 0;
+}
+
+static int puv3_rtc_resume(struct platform_device *pdev)
+{
+ puv3_rtc_enable(pdev, 1);
+ writel(ticnt_save, RTC_RTAR);
+ return 0;
+}
+#else
+#define puv3_rtc_suspend NULL
+#define puv3_rtc_resume NULL
+#endif
+
+static struct platform_driver puv3_rtcdrv = {
+ .probe = puv3_rtc_probe,
+ .remove = __devexit_p(puv3_rtc_remove),
+ .suspend = puv3_rtc_suspend,
+ .resume = puv3_rtc_resume,
+ .driver = {
+ .name = "PKUnity-v3-RTC",
+ .owner = THIS_MODULE,
+ }
+};
+
+static char __initdata banner[] = "PKUnity-v3 RTC, (c) 2009 PKUnity Co.\n";
+
+static int __init puv3_rtc_init(void)
+{
+ printk(banner);
+ return platform_driver_register(&puv3_rtcdrv);
+}
+
+static void __exit puv3_rtc_exit(void)
+{
+ platform_driver_unregister(&puv3_rtcdrv);
+}
+
+module_init(puv3_rtc_init);
+module_exit(puv3_rtc_exit);
+
+MODULE_DESCRIPTION("RTC Driver for the PKUnity v3 chip");
+MODULE_AUTHOR("Hu Dongliang");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
index b8bc862..efd6066 100644
--- a/drivers/rtc/rtc-vt8500.c
+++ b/drivers/rtc/rtc-vt8500.c
@@ -78,7 +78,6 @@ struct vt8500_rtc {
void __iomem *regbase;
struct resource *res;
int irq_alarm;
- int irq_hz;
struct rtc_device *rtc;
spinlock_t lock; /* Protects this structure */
};
@@ -100,10 +99,6 @@ static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
if (isr & 1)
events |= RTC_AF | RTC_IRQF;
- /* Only second/minute interrupts are supported */
- if (isr & 2)
- events |= RTC_UF | RTC_IRQF;
-
rtc_update_irq(vt8500_rtc->rtc, 1, events);
return IRQ_HANDLED;
@@ -199,27 +194,12 @@ static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
return 0;
}
-static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
-{
- struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
- unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
-
- if (enabled)
- tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE;
- else
- tmp &= ~VT8500_RTC_CR_SM_ENABLE;
-
- writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR);
- return 0;
-}
-
static const struct rtc_class_ops vt8500_rtc_ops = {
.read_time = vt8500_rtc_read_time,
.set_time = vt8500_rtc_set_time,
.read_alarm = vt8500_rtc_read_alarm,
.set_alarm = vt8500_rtc_set_alarm,
.alarm_irq_enable = vt8500_alarm_irq_enable,
- .update_irq_enable = vt8500_update_irq_enable,
};
static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
@@ -248,13 +228,6 @@ static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
goto err_free;
}
- vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
- if (vt8500_rtc->irq_hz < 0) {
- dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
- ret = -ENXIO;
- goto err_free;
- }
-
vt8500_rtc->res = request_mem_region(vt8500_rtc->res->start,
resource_size(vt8500_rtc->res),
"vt8500-rtc");
@@ -272,9 +245,8 @@ static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
goto err_release;
}
- /* Enable the second/minute interrupt generation and enable RTC */
- writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
- | VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
+ /* Enable RTC and set it to 24-hour mode */
+ writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H,
vt8500_rtc->regbase + VT8500_RTC_CR);
vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
@@ -286,26 +258,16 @@ static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
goto err_unmap;
}
- ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
- "rtc 1Hz", vt8500_rtc);
- if (ret < 0) {
- dev_err(&pdev->dev, "can't get irq %i, err %d\n",
- vt8500_rtc->irq_hz, ret);
- goto err_unreg;
- }
-
ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
"rtc alarm", vt8500_rtc);
if (ret < 0) {
dev_err(&pdev->dev, "can't get irq %i, err %d\n",
vt8500_rtc->irq_alarm, ret);
- goto err_free_hz;
+ goto err_unreg;
}
return 0;
-err_free_hz:
- free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
err_unreg:
rtc_device_unregister(vt8500_rtc->rtc);
err_unmap:
@@ -323,7 +285,6 @@ static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
- free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
rtc_device_unregister(vt8500_rtc->rtc);
OpenPOWER on IntegriCloud