diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/interface.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-dev.c | 3 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-puv3.c | 359 | ||||
-rw-r--r-- | drivers/rtc/rtc-vt8500.c | 45 |
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); |