diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 49 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 17 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 24 | ||||
-rw-r--r-- | drivers/rtc/rtc-bfin.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-coh901331.c | 311 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1305.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 3 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1390.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds3234.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ep93xx.c | 14 | ||||
-rw-r--r-- | drivers/rtc/rtc-m41t94.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-max6902.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-mxc.c | 507 | ||||
-rw-r--r-- | drivers/rtc/rtc-omap.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcap.c | 224 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf2123.c | 364 | ||||
-rw-r--r-- | drivers/rtc/rtc-r9701.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-rs5c348.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-stmp3xxx.c | 304 | ||||
-rw-r--r-- | drivers/rtc/rtc-sysfs.c | 14 |
20 files changed, 1819 insertions, 23 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 73771b09fbd3..3c20dae43ce2 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -378,6 +378,15 @@ config RTC_DRV_DS3234 This driver can also be built as a module. If so, the module will be called rtc-ds3234. +config RTC_DRV_PCF2123 + tristate "NXP PCF2123" + help + If you say yes here you get support for the NXP PCF2123 + RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf2123. + endif # SPI_MASTER comment "Platform RTC drivers" @@ -500,6 +509,17 @@ config RTC_DRV_M48T59 This driver can also be built as a module, if so, the module will be called "rtc-m48t59". +config RTC_MXC + tristate "Freescale MXC Real Time Clock" + depends on ARCH_MXC + depends on RTC_CLASS + help + If you say yes here you get support for the Freescale MXC + RTC module. + + This driver can also be built as a module, if so, the module + will be called "rtc-mxc". + config RTC_DRV_BQ4802 tristate "TI BQ4802" help @@ -778,4 +798,33 @@ config RTC_DRV_PS3 This driver can also be built as a module. If so, the module will be called rtc-ps3. +config RTC_DRV_COH901331 + tristate "ST-Ericsson COH 901 331 RTC" + depends on ARCH_U300 + help + If you say Y here you will get access to ST-Ericsson + COH 901 331 RTC clock found in some ST-Ericsson Mobile + Platforms. + + This driver can also be built as a module. If so, the module + will be called "rtc-coh901331". + + +config RTC_DRV_STMP + tristate "Freescale STMP3xxx RTC" + depends on ARCH_STMP3XXX + help + If you say yes here you will get support for the onboard + STMP3xxx RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-stmp3xxx. + +config RTC_DRV_PCAP + tristate "PCAP RTC" + depends on EZX_PCAP + help + If you say Y here you will get support for the RTC found on + the PCAP2 ASIC used on some Motorola phones. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5e152ffe5058..aa3fbd5517a1 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -23,7 +23,9 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o +obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o +obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o @@ -40,24 +42,26 @@ obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o +obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o -obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o -obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o -obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o +obj-$(CONFIG_RTC_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o +obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o +obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o +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_GENERIC) += rtc-generic.o +obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o @@ -69,7 +73,10 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o +obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o +obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o +obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o @@ -78,5 +85,3 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o -obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o -obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index b5bf93706913..bc8bbca9a2e2 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -289,7 +289,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) AT91_RTC_CALEV); ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, "at91_rtc", pdev); if (ret) { printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", @@ -340,7 +340,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) static u32 at91_rtc_imr; -static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) +static int at91_rtc_suspend(struct device *dev) { /* this IRQ is shared with DBGU and other hardware which isn't * necessarily doing PM like we are... @@ -348,7 +348,7 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) at91_rtc_imr = at91_sys_read(AT91_RTC_IMR) & (AT91_RTC_ALARM|AT91_RTC_SECEV); if (at91_rtc_imr) { - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) enable_irq_wake(AT91_ID_SYS); else at91_sys_write(AT91_RTC_IDR, at91_rtc_imr); @@ -356,28 +356,34 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int at91_rtc_resume(struct platform_device *pdev) +static int at91_rtc_resume(struct device *dev) { if (at91_rtc_imr) { - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) disable_irq_wake(AT91_ID_SYS); else at91_sys_write(AT91_RTC_IER, at91_rtc_imr); } return 0; } + +static const struct dev_pm_ops at91_rtc_pm = { + .suspend = at91_rtc_suspend, + .resume = at91_rtc_resume, +}; + +#define at91_rtc_pm_ptr &at91_rtc_pm + #else -#define at91_rtc_suspend NULL -#define at91_rtc_resume NULL +#define at91_rtc_pm_ptr NULL #endif static struct platform_driver at91_rtc_driver = { .remove = __exit_p(at91_rtc_remove), - .suspend = at91_rtc_suspend, - .resume = at91_rtc_resume, .driver = { .name = "at91_rtc", .owner = THIS_MODULE, + .pm = at91_rtc_pm_ptr, }, }; diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index a118eb0f1e67..b11485b9f21c 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -383,7 +383,7 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev) } /* Grab the IRQ and init the hardware */ - ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, pdev->name, dev); + ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, 0, pdev->name, dev); if (unlikely(ret)) goto err_reg; /* sometimes the bootloader touched things, but the write complete was not diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c new file mode 100644 index 000000000000..7fe1fa26c52c --- /dev/null +++ b/drivers/rtc/rtc-coh901331.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2007-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Real Time Clock interface for ST-Ericsson AB COH 901 331 RTC. + * Author: Linus Walleij <linus.walleij@stericsson.com> + * Based on rtc-pl031.c by Deepak Saxena <dsaxena@plexity.net> + * Copyright 2006 (c) MontaVista Software, Inc. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +/* + * Registers in the COH 901 331 + */ +/* Alarm value 32bit (R/W) */ +#define COH901331_ALARM 0x00U +/* Used to set current time 32bit (R/W) */ +#define COH901331_SET_TIME 0x04U +/* Indication if current time is valid 32bit (R/-) */ +#define COH901331_VALID 0x08U +/* Read the current time 32bit (R/-) */ +#define COH901331_CUR_TIME 0x0cU +/* Event register for the "alarm" interrupt */ +#define COH901331_IRQ_EVENT 0x10U +/* Mask register for the "alarm" interrupt */ +#define COH901331_IRQ_MASK 0x14U +/* Force register for the "alarm" interrupt */ +#define COH901331_IRQ_FORCE 0x18U + +/* + * Reference to RTC block clock + * Notice that the frequent clk_enable()/clk_disable() on this + * clock is mainly to be able to turn on/off other clocks in the + * hierarchy as needed, the RTC clock is always on anyway. + */ +struct coh901331_port { + struct rtc_device *rtc; + struct clk *clk; + u32 phybase; + u32 physize; + void __iomem *virtbase; + int irq; +#ifdef CONFIG_PM + u32 irqmaskstore; +#endif +}; + +static irqreturn_t coh901331_interrupt(int irq, void *data) +{ + struct coh901331_port *rtap = data; + + clk_enable(rtap->clk); + /* Ack IRQ */ + writel(1, rtap->virtbase + COH901331_IRQ_EVENT); + clk_disable(rtap->clk); + /* Set alarm flag */ + rtc_update_irq(rtap->rtc, 1, RTC_AF); + + return IRQ_HANDLED; +} + +static int coh901331_read_time(struct device *dev, struct rtc_time *tm) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + /* Check if the time is valid */ + if (readl(rtap->virtbase + COH901331_VALID)) { + rtc_time_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm); + clk_disable(rtap->clk); + return rtc_valid_tm(tm); + } + clk_disable(rtap->clk); + return -EINVAL; +} + +static int coh901331_set_mmss(struct device *dev, unsigned long secs) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + writel(secs, rtap->virtbase + COH901331_SET_TIME); + clk_disable(rtap->clk); + + return 0; +} + +static int coh901331_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + rtc_time_to_tm(readl(rtap->virtbase + COH901331_ALARM), &alarm->time); + alarm->pending = readl(rtap->virtbase + COH901331_IRQ_EVENT) & 1U; + alarm->enabled = readl(rtap->virtbase + COH901331_IRQ_MASK) & 1U; + clk_disable(rtap->clk); + + return 0; +} + +static int coh901331_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + unsigned long time; + + rtc_tm_to_time(&alarm->time, &time); + clk_enable(rtap->clk); + writel(time, rtap->virtbase + COH901331_ALARM); + writel(alarm->enabled, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + + return 0; +} + +static int coh901331_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + if (enabled) + writel(1, rtap->virtbase + COH901331_IRQ_MASK); + else + writel(0, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); +} + +static struct rtc_class_ops coh901331_ops = { + .read_time = coh901331_read_time, + .set_mmss = coh901331_set_mmss, + .read_alarm = coh901331_read_alarm, + .set_alarm = coh901331_set_alarm, + .alarm_irq_enable = coh901331_alarm_irq_enable, +}; + +static int __exit coh901331_remove(struct platform_device *pdev) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + if (rtap) { + free_irq(rtap->irq, rtap); + rtc_device_unregister(rtap->rtc); + clk_put(rtap->clk); + iounmap(rtap->virtbase); + release_mem_region(rtap->phybase, rtap->physize); + platform_set_drvdata(pdev, NULL); + kfree(rtap); + } + + return 0; +} + + +static int __init coh901331_probe(struct platform_device *pdev) +{ + int ret; + struct coh901331_port *rtap; + struct resource *res; + + rtap = kzalloc(sizeof(struct coh901331_port), GFP_KERNEL); + if (!rtap) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto out_no_resource; + } + rtap->phybase = res->start; + rtap->physize = resource_size(res); + + if (request_mem_region(rtap->phybase, rtap->physize, + "rtc-coh901331") == NULL) { + ret = -EBUSY; + goto out_no_memregion; + } + + rtap->virtbase = ioremap(rtap->phybase, rtap->physize); + if (!rtap->virtbase) { + ret = -ENOMEM; + goto out_no_remap; + } + + rtap->irq = platform_get_irq(pdev, 0); + if (request_irq(rtap->irq, coh901331_interrupt, IRQF_DISABLED, + "RTC COH 901 331 Alarm", rtap)) { + ret = -EIO; + goto out_no_irq; + } + + rtap->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(rtap->clk)) { + ret = PTR_ERR(rtap->clk); + dev_err(&pdev->dev, "could not get clock\n"); + goto out_no_clk; + } + + /* We enable/disable the clock only to assure it works */ + ret = clk_enable(rtap->clk); + if (ret) { + dev_err(&pdev->dev, "could not enable clock\n"); + goto out_no_clk_enable; + } + clk_disable(rtap->clk); + + rtap->rtc = rtc_device_register("coh901331", &pdev->dev, &coh901331_ops, + THIS_MODULE); + if (IS_ERR(rtap->rtc)) { + ret = PTR_ERR(rtap->rtc); + goto out_no_rtc; + } + + platform_set_drvdata(pdev, rtap); + + return 0; + + out_no_rtc: + out_no_clk_enable: + clk_put(rtap->clk); + out_no_clk: + free_irq(rtap->irq, rtap); + out_no_irq: + iounmap(rtap->virtbase); + out_no_remap: + platform_set_drvdata(pdev, NULL); + out_no_memregion: + release_mem_region(rtap->phybase, SZ_4K); + out_no_resource: + kfree(rtap); + return ret; +} + +#ifdef CONFIG_PM +static int coh901331_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + /* + * If this RTC alarm will be used for waking the system up, + * don't disable it of course. Else we just disable the alarm + * and await suspension. + */ + if (device_may_wakeup(&pdev->dev)) { + enable_irq_wake(rtap->irq); + } else { + clk_enable(rtap->clk); + rtap->irqmaskstore = readl(rtap->virtbase + COH901331_IRQ_MASK); + writel(0, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + } + return 0; +} + +static int coh901331_resume(struct platform_device *pdev) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtap->irq); + else + clk_enable(rtap->clk); + writel(rtap->irqmaskstore, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + return 0; +} +#else +#define coh901331_suspend NULL +#define coh901331_resume NULL +#endif + +static void coh901331_shutdown(struct platform_device *pdev) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + clk_enable(rtap->clk); + writel(0, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); +} + +static struct platform_driver coh901331_driver = { + .driver = { + .name = "rtc-coh901331", + .owner = THIS_MODULE, + }, + .remove = __exit_p(coh901331_remove), + .suspend = coh901331_suspend, + .resume = coh901331_resume, + .shutdown = coh901331_shutdown, +}; + +static int __init coh901331_init(void) +{ + return platform_driver_probe(&coh901331_driver, coh901331_probe); +} + +static void __exit coh901331_exit(void) +{ + platform_driver_unregister(&coh901331_driver); +} + +module_init(coh901331_init); +module_exit(coh901331_exit); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); +MODULE_DESCRIPTION("ST-Ericsson AB COH 901 331 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 8f410e59d9f5..2736b11a1b1e 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -841,3 +841,4 @@ module_exit(ds1305_exit); MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-ds1305"); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 47a93c022d91..eb99ee4fa0f5 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -896,8 +896,7 @@ read_rtc: return 0; exit_irq: - if (ds1307->rtc) - rtc_device_unregister(ds1307->rtc); + rtc_device_unregister(ds1307->rtc); exit_free: kfree(ds1307); return err; diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index e01b955db077..cdb705057091 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c @@ -189,3 +189,4 @@ module_exit(ds1390_exit); MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver"); MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-ds1390"); diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c index c51589ede5b7..a774ca35b5f7 100644 --- a/drivers/rtc/rtc-ds3234.c +++ b/drivers/rtc/rtc-ds3234.c @@ -188,3 +188,4 @@ module_exit(ds3234_exit); MODULE_DESCRIPTION("DS3234 SPI RTC driver"); MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ds3234"); diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c index 551332e4ed02..9da02d108b73 100644 --- a/drivers/rtc/rtc-ep93xx.c +++ b/drivers/rtc/rtc-ep93xx.c @@ -128,12 +128,16 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) - return -ENXIO; + if (res == NULL) { + err = -ENXIO; + goto fail_free; + } res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) - return -EBUSY; + if (res == NULL) { + err = -EBUSY; + goto fail_free; + } ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res)); if (ep93xx_rtc->mmio_base == NULL) { @@ -169,6 +173,8 @@ fail: pdev->dev.platform_data = NULL; } release_mem_region(res->start, resource_size(res)); +fail_free: + kfree(ep93xx_rtc); return err; } diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c index c3a18c58daf6..c8c97a4169d4 100644 --- a/drivers/rtc/rtc-m41t94.c +++ b/drivers/rtc/rtc-m41t94.c @@ -171,3 +171,4 @@ module_exit(m41t94_exit); MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-m41t94"); diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c index 36a8ea9ed8ba..657403ebd54a 100644 --- a/drivers/rtc/rtc-max6902.c +++ b/drivers/rtc/rtc-max6902.c @@ -175,3 +175,4 @@ module_exit(max6902_exit); MODULE_DESCRIPTION ("max6902 spi RTC driver"); MODULE_AUTHOR ("Raphael Assenat"); MODULE_LICENSE ("GPL"); +MODULE_ALIAS("spi:rtc-max6902"); diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c new file mode 100644 index 000000000000..6bd5072d4eb7 --- /dev/null +++ b/drivers/rtc/rtc-mxc.c @@ -0,0 +1,507 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/io.h> +#include <linux/rtc.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <mach/hardware.h> + +#define RTC_INPUT_CLK_32768HZ (0x00 << 5) +#define RTC_INPUT_CLK_32000HZ (0x01 << 5) +#define RTC_INPUT_CLK_38400HZ (0x02 << 5) + +#define RTC_SW_BIT (1 << 0) +#define RTC_ALM_BIT (1 << 2) +#define RTC_1HZ_BIT (1 << 4) +#define RTC_2HZ_BIT (1 << 7) +#define RTC_SAM0_BIT (1 << 8) +#define RTC_SAM1_BIT (1 << 9) +#define RTC_SAM2_BIT (1 << 10) +#define RTC_SAM3_BIT (1 << 11) +#define RTC_SAM4_BIT (1 << 12) +#define RTC_SAM5_BIT (1 << 13) +#define RTC_SAM6_BIT (1 << 14) +#define RTC_SAM7_BIT (1 << 15) +#define PIT_ALL_ON (RTC_2HZ_BIT | RTC_SAM0_BIT | RTC_SAM1_BIT | \ + RTC_SAM2_BIT | RTC_SAM3_BIT | RTC_SAM4_BIT | \ + RTC_SAM5_BIT | RTC_SAM6_BIT | RTC_SAM7_BIT) + +#define RTC_ENABLE_BIT (1 << 7) + +#define MAX_PIE_NUM 9 +#define MAX_PIE_FREQ 512 +static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = { + { 2, RTC_2HZ_BIT }, + { 4, RTC_SAM0_BIT }, + { 8, RTC_SAM1_BIT }, + { 16, RTC_SAM2_BIT }, + { 32, RTC_SAM3_BIT }, + { 64, RTC_SAM4_BIT }, + { 128, RTC_SAM5_BIT }, + { 256, RTC_SAM6_BIT }, + { MAX_PIE_FREQ, RTC_SAM7_BIT }, +}; + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 /* Periodic interrupt */ +#define RTC_AF 0x20 /* Alarm interrupt */ +#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */ + +#define MXC_RTC_TIME 0 +#define MXC_RTC_ALARM 1 + +#define RTC_HOURMIN 0x00 /* 32bit rtc hour/min counter reg */ +#define RTC_SECOND 0x04 /* 32bit rtc seconds counter reg */ +#define RTC_ALRM_HM 0x08 /* 32bit rtc alarm hour/min reg */ +#define RTC_ALRM_SEC 0x0C /* 32bit rtc alarm seconds reg */ +#define RTC_RTCCTL 0x10 /* 32bit rtc control reg */ +#define RTC_RTCISR 0x14 /* 32bit rtc interrupt status reg */ +#define RTC_RTCIENR 0x18 /* 32bit rtc interrupt enable reg */ +#define RTC_STPWCH 0x1C /* 32bit rtc stopwatch min reg */ +#define RTC_DAYR 0x20 /* 32bit rtc days counter reg */ +#define RTC_DAYALARM 0x24 /* 32bit rtc day alarm reg */ +#define RTC_TEST1 0x28 /* 32bit rtc test reg 1 */ +#define RTC_TEST2 0x2C /* 32bit rtc test reg 2 */ +#define RTC_TEST3 0x30 /* 32bit rtc test reg 3 */ + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + int irq; + struct clk *clk; + unsigned int irqen; + int alrm_sec; + int alrm_min; + int alrm_hour; + int alrm_mday; + struct timespec mxc_rtc_delta; + struct rtc_time g_rtc_alarm; +}; + +/* + * This function is used to obtain the RTC time or the alarm value in + * second. + */ +static u32 get_alarm_or_time(struct device *dev, int time_alarm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 day = 0, hr = 0, min = 0, sec = 0, hr_min = 0; + + switch (time_alarm) { + case MXC_RTC_TIME: + day = readw(ioaddr + RTC_DAYR); + hr_min = readw(ioaddr + RTC_HOURMIN); + sec = readw(ioaddr + RTC_SECOND); + break; + case MXC_RTC_ALARM: + day = readw(ioaddr + RTC_DAYALARM); + hr_min = readw(ioaddr + RTC_ALRM_HM) & 0xffff; + sec = readw(ioaddr + RTC_ALRM_SEC); + break; + } + + hr = hr_min >> 8; + min = hr_min & 0xff; + + return (((day * 24 + hr) * 60) + min) * 60 + sec; +} + +/* + * This function sets the RTC alarm value or the time value. + */ +static void set_alarm_or_time(struct device *dev, int time_alarm, u32 time) +{ + u32 day, hr, min, sec, temp; + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + day = time / 86400; + time -= day * 86400; + + /* time is within a day now */ + hr = time / 3600; + time -= hr * 3600; + + /* time is within an hour now */ + min = time / 60; + sec = time - min * 60; + + temp = (hr << 8) + min; + + switch (time_alarm) { + case MXC_RTC_TIME: + writew(day, ioaddr + RTC_DAYR); + writew(sec, ioaddr + RTC_SECOND); + writew(temp, ioaddr + RTC_HOURMIN); + break; + case MXC_RTC_ALARM: + writew(day, ioaddr + RTC_DAYALARM); + writew(sec, ioaddr + RTC_ALRM_SEC); + writew(temp, ioaddr + RTC_ALRM_HM); + break; + } +} + +/* + * This function updates the RTC alarm registers and then clears all the + * interrupt status bits. + */ +static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm) +{ + struct rtc_time alarm_tm, now_tm; + unsigned long now, time; + int ret; + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + now = get_alarm_or_time(dev, MXC_RTC_TIME); + rtc_time_to_tm(now, &now_tm); + alarm_tm.tm_year = now_tm.tm_year; + alarm_tm.tm_mon = now_tm.tm_mon; + alarm_tm.tm_mday = now_tm.tm_mday; + alarm_tm.tm_hour = alrm->tm_hour; + alarm_tm.tm_min = alrm->tm_min; + alarm_tm.tm_sec = alrm->tm_sec; + rtc_tm_to_time(&now_tm, &now); + rtc_tm_to_time(&alarm_tm, &time); + + if (time < now) { + time += 60 * 60 * 24; + rtc_time_to_tm(time, &alarm_tm); + } + + ret = rtc_tm_to_time(&alarm_tm, &time); + + /* clear all the interrupt status bits */ + writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); + set_alarm_or_time(dev, MXC_RTC_ALARM, time); + + return ret; +} + +/* This function is the RTC interrupt service routine. */ +static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 status; + u32 events = 0; + + spin_lock_irq(&pdata->rtc->irq_lock); + status = readw(ioaddr + RTC_RTCISR) & readw(ioaddr + RTC_RTCIENR); + /* clear interrupt sources */ + writew(status, ioaddr + RTC_RTCISR); + + /* clear alarm interrupt if it has occurred */ + if (status & RTC_ALM_BIT) + status &= ~RTC_ALM_BIT; + + /* update irq data & counter */ + if (status & RTC_ALM_BIT) + events |= (RTC_AF | RTC_IRQF); + + if (status & RTC_1HZ_BIT) + events |= (RTC_UF | RTC_IRQF); + + if (status & PIT_ALL_ON) + events |= (RTC_PF | RTC_IRQF); + + if ((status & RTC_ALM_BIT) && rtc_valid_tm(&pdata->g_rtc_alarm)) + rtc_update_alarm(&pdev->dev, &pdata->g_rtc_alarm); + + rtc_update_irq(pdata->rtc, 1, events); + spin_unlock_irq(&pdata->rtc->irq_lock); + + return IRQ_HANDLED; +} + +/* + * Clear all interrupts and release the IRQ + */ +static void mxc_rtc_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + spin_lock_irq(&pdata->rtc->irq_lock); + + /* Disable all rtc interrupts */ + writew(0, ioaddr + RTC_RTCIENR); + + /* Clear all interrupt status */ + writew(0xffffffff, ioaddr + RTC_RTCISR); + + spin_unlock_irq(&pdata->rtc->irq_lock); +} + +static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit, + unsigned int enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 reg; + + spin_lock_irq(&pdata->rtc->irq_lock); + reg = readw(ioaddr + RTC_RTCIENR); + + if (enabled) + reg |= bit; + else + reg &= ~bit; + + writew(reg, ioaddr + RTC_RTCIENR); + spin_unlock_irq(&pdata->rtc->irq_lock); +} + +static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); + return 0; +} + +static int mxc_rtc_update_irq_enable(struct device *dev, unsigned int enabled) +{ + mxc_rtc_irq_enable(dev, RTC_1HZ_BIT, enabled); + return 0; +} + +/* + * This function reads the current RTC time into tm in Gregorian date. + */ +static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u32 val; + + /* Avoid roll-over from reading the different registers */ + do { + val = get_alarm_or_time(dev, MXC_RTC_TIME); + } while (val != get_alarm_or_time(dev, MXC_RTC_TIME)); + + rtc_time_to_tm(val, tm); + + return 0; +} + +/* + * This function sets the internal RTC time based on tm in Gregorian date. + */ +static int mxc_rtc_set_mmss(struct device *dev, unsigned long time) +{ + /* Avoid roll-over from reading the different registers */ + do { + set_alarm_or_time(dev, MXC_RTC_TIME, time); + } while (time != get_alarm_or_time(dev, MXC_RTC_TIME)); + + return 0; +} + +/* + * This function reads the current alarm value into the passed in 'alrm' + * argument. It updates the alrm's pending field value based on the whether + * an alarm interrupt occurs or not. + */ +static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + rtc_time_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time); + alrm->pending = ((readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT)) ? 1 : 0; + + return 0; +} + +/* + * This function sets the RTC alarm based on passed in alrm. + */ +static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + int ret; + + if (rtc_valid_tm(&alrm->time)) { + if (alrm->time.tm_sec > 59 || + alrm->time.tm_hour > 23 || + alrm->time.tm_min > 59) + return -EINVAL; + + ret = rtc_update_alarm(dev, &alrm->time); + } else { + ret = rtc_valid_tm(&alrm->time); + if (ret) + return ret; + + ret = rtc_update_alarm(dev, &alrm->time); + } + + if (ret) + return ret; + + memcpy(&pdata->g_rtc_alarm, &alrm->time, sizeof(struct rtc_time)); + mxc_rtc_irq_enable(dev, RTC_ALM_BIT, alrm->enabled); + + return 0; +} + +/* RTC layer */ +static struct rtc_class_ops mxc_rtc_ops = { + .release = mxc_rtc_release, + .read_time = mxc_rtc_read_time, + .set_mmss = mxc_rtc_set_mmss, + .read_alarm = mxc_rtc_read_alarm, + .set_alarm = mxc_rtc_set_alarm, + .alarm_irq_enable = mxc_rtc_alarm_irq_enable, + .update_irq_enable = mxc_rtc_update_irq_enable, +}; + +static int __init mxc_rtc_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct resource *res; + struct rtc_device *rtc; + struct rtc_plat_data *pdata = NULL; + u32 reg; + int ret, rate; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->ioaddr = ioremap(res->start, resource_size(res)); + + clk = clk_get(&pdev->dev, "ckil"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rate = clk_get_rate(clk); + clk_put(clk); + + if (rate == 32768) + reg = RTC_INPUT_CLK_32768HZ; + else if (rate == 32000) + reg = RTC_INPUT_CLK_32000HZ; + else if (rate == 38400) + reg = RTC_INPUT_CLK_38400HZ; + else { + dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n", + clk_get_rate(clk)); + ret = -EINVAL; + goto exit_free_pdata; + } + + reg |= RTC_ENABLE_BIT; + writew(reg, (pdata->ioaddr + RTC_RTCCTL)); + if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) { + dev_err(&pdev->dev, "hardware module can't be enabled!\n"); + ret = -EIO; + goto exit_free_pdata; + } + + pdata->clk = clk_get(&pdev->dev, "rtc"); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "unable to get clock!\n"); + ret = PTR_ERR(pdata->clk); + goto exit_free_pdata; + } + + clk_enable(pdata->clk); + + rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto exit_put_clk; + } + + pdata->rtc = rtc; + platform_set_drvdata(pdev, pdata); + + /* Configure and enable the RTC */ + pdata->irq = platform_get_irq(pdev, 0); + + if (pdata->irq >= 0 && + request_irq(pdata->irq, mxc_rtc_interrupt, IRQF_SHARED, + pdev->name, pdev) < 0) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = -1; + } + + return 0; + +exit_put_clk: + clk_put(pdata->clk); + +exit_free_pdata: + kfree(pdata); + + return ret; +} + +static int __exit mxc_rtc_remove(struct platform_device *pdev) +{ + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + rtc_device_unregister(pdata->rtc); + + if (pdata->irq >= 0) + free_irq(pdata->irq, pdev); + + clk_disable(pdata->clk); + clk_put(pdata->clk); + kfree(pdata); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mxc_rtc_driver = { + .driver = { + .name = "mxc_rtc", + .owner = THIS_MODULE, + }, + .remove = __exit_p(mxc_rtc_remove), +}; + +static int __init mxc_rtc_init(void) +{ + return platform_driver_probe(&mxc_rtc_driver, mxc_rtc_probe); +} + +static void __exit mxc_rtc_exit(void) +{ + platform_driver_unregister(&mxc_rtc_driver); +} + +module_init(mxc_rtc_init); +module_exit(mxc_rtc_exit); + +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_DESCRIPTION("RTC driver for Freescale MXC"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index bd1ce8e2bc18..0587d53987fe 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -430,7 +430,7 @@ fail: static int __exit omap_rtc_remove(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev);; + struct rtc_device *rtc = platform_get_drvdata(pdev); device_init_wakeup(&pdev->dev, 0); diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c new file mode 100644 index 000000000000..a99c28992d21 --- /dev/null +++ b/drivers/rtc/rtc-pcap.c @@ -0,0 +1,224 @@ +/* + * pcap rtc code for Motorola EZX phones + * + * Copyright (c) 2008 guiming zhuo <gmzhuo@gmail.com> + * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com> + * + * Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mfd/ezx-pcap.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> + +struct pcap_rtc { + struct pcap_chip *pcap; + struct rtc_device *rtc; +}; + +static irqreturn_t pcap_rtc_irq(int irq, void *_pcap_rtc) +{ + struct pcap_rtc *pcap_rtc = _pcap_rtc; + unsigned long rtc_events; + + if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ)) + rtc_events = RTC_IRQF | RTC_UF; + else if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA)) + rtc_events = RTC_IRQF | RTC_AF; + else + rtc_events = 0; + + rtc_update_irq(pcap_rtc->rtc, 1, rtc_events); + return IRQ_HANDLED; +} + +static int pcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + struct rtc_time *tm = &alrm->time; + unsigned long secs; + u32 tod; /* time of day, seconds since midnight */ + u32 days; /* days since 1/1/1970 */ + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TODA, &tod); + secs = tod & PCAP_RTC_TOD_MASK; + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, &days); + secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; + + rtc_time_to_tm(secs, tm); + + return 0; +} + +static int pcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + struct rtc_time *tm = &alrm->time; + unsigned long secs; + u32 tod, days; + + rtc_tm_to_time(tm, &secs); + + tod = secs % SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TODA, tod); + + days = secs / SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, days); + + return 0; +} + +static int pcap_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + unsigned long secs; + u32 tod, days; + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TOD, &tod); + secs = tod & PCAP_RTC_TOD_MASK; + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAY, &days); + secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; + + rtc_time_to_tm(secs, tm); + + return rtc_valid_tm(tm); +} + +static int pcap_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + u32 tod, days; + + tod = secs % SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TOD, tod); + + days = secs / SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAY, days); + + return 0; +} + +static int pcap_rtc_irq_enable(struct device *dev, int pirq, unsigned int en) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + + if (en) + enable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); + else + disable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); + + return 0; +} + +static int pcap_rtc_alarm_irq_enable(struct device *dev, unsigned int en) +{ + return pcap_rtc_irq_enable(dev, PCAP_IRQ_TODA, en); +} + +static int pcap_rtc_update_irq_enable(struct device *dev, unsigned int en) +{ + return pcap_rtc_irq_enable(dev, PCAP_IRQ_1HZ, en); +} + +static const struct rtc_class_ops pcap_rtc_ops = { + .read_time = pcap_rtc_read_time, + .read_alarm = pcap_rtc_read_alarm, + .set_alarm = pcap_rtc_set_alarm, + .set_mmss = pcap_rtc_set_mmss, + .alarm_irq_enable = pcap_rtc_alarm_irq_enable, + .update_irq_enable = pcap_rtc_update_irq_enable, +}; + +static int __devinit pcap_rtc_probe(struct platform_device *pdev) +{ + struct pcap_rtc *pcap_rtc; + int timer_irq, alarm_irq; + int err = -ENOMEM; + + pcap_rtc = kmalloc(sizeof(struct pcap_rtc), GFP_KERNEL); + if (!pcap_rtc) + return err; + + pcap_rtc->pcap = dev_get_drvdata(pdev->dev.parent); + + pcap_rtc->rtc = rtc_device_register("pcap", &pdev->dev, + &pcap_rtc_ops, THIS_MODULE); + if (IS_ERR(pcap_rtc->rtc)) { + err = PTR_ERR(pcap_rtc->rtc); + goto fail_rtc; + } + + platform_set_drvdata(pdev, pcap_rtc); + + timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ); + alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA); + + err = request_irq(timer_irq, pcap_rtc_irq, 0, "RTC Timer", pcap_rtc); + if (err) + goto fail_timer; + + err = request_irq(alarm_irq, pcap_rtc_irq, 0, "RTC Alarm", pcap_rtc); + if (err) + goto fail_alarm; + + return 0; +fail_alarm: + free_irq(timer_irq, pcap_rtc); +fail_timer: + rtc_device_unregister(pcap_rtc->rtc); +fail_rtc: + kfree(pcap_rtc); + return err; +} + +static int __devexit pcap_rtc_remove(struct platform_device *pdev) +{ + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ), pcap_rtc); + free_irq(pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA), pcap_rtc); + rtc_device_unregister(pcap_rtc->rtc); + kfree(pcap_rtc); + + return 0; +} + +static struct platform_driver pcap_rtc_driver = { + .remove = __devexit_p(pcap_rtc_remove), + .driver = { + .name = "pcap-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init rtc_pcap_init(void) +{ + return platform_driver_probe(&pcap_rtc_driver, pcap_rtc_probe); +} + +static void __exit rtc_pcap_exit(void) +{ + platform_driver_unregister(&pcap_rtc_driver); +} + +module_init(rtc_pcap_init); +module_exit(rtc_pcap_exit); + +MODULE_DESCRIPTION("Motorola pcap rtc driver"); +MODULE_AUTHOR("guiming zhuo <gmzhuo@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c new file mode 100644 index 000000000000..e75df9d50e27 --- /dev/null +++ b/drivers/rtc/rtc-pcf2123.c @@ -0,0 +1,364 @@ +/* + * An SPI driver for the Philips PCF2123 RTC + * Copyright 2009 Cyber Switching, Inc. + * + * Author: Chris Verges <chrisv@cyberswitching.com> + * Maintainers: http://www.cyberswitching.com + * + * based on the RS5C348 driver in this same directory. + * + * Thanks to Christian Pellegrin <chripell@fsfe.org> for + * the sysfs contributions to this driver. + * + * 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. + * + * Please note that the CS is active high, so platform data + * should look something like: + * + * static struct spi_board_info ek_spi_devices[] = { + * ... + * { + * .modalias = "rtc-pcf2123", + * .chip_select = 1, + * .controller_data = (void *)AT91_PIN_PA10, + * .max_speed_hz = 1000 * 1000, + * .mode = SPI_CS_HIGH, + * .bus_num = 0, + * }, + * ... + *}; + * + */ + +#include <linux/bcd.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> + +#define DRV_VERSION "0.6" + +#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */ +#define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */ +#define PCF2123_REG_SC (0x02) /* datetime */ +#define PCF2123_REG_MN (0x03) +#define PCF2123_REG_HR (0x04) +#define PCF2123_REG_DM (0x05) +#define PCF2123_REG_DW (0x06) +#define PCF2123_REG_MO (0x07) +#define PCF2123_REG_YR (0x08) + +#define PCF2123_SUBADDR (1 << 4) +#define PCF2123_WRITE ((0 << 7) | PCF2123_SUBADDR) +#define PCF2123_READ ((1 << 7) | PCF2123_SUBADDR) + +static struct spi_driver pcf2123_driver; + +struct pcf2123_sysfs_reg { + struct device_attribute attr; + char name[2]; +}; + +struct pcf2123_plat_data { + struct rtc_device *rtc; + struct pcf2123_sysfs_reg regs[16]; +}; + +/* + * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select + * is released properly after an SPI write. This function should be + * called after EVERY read/write call over SPI. + */ +static inline void pcf2123_delay_trec(void) +{ + ndelay(30); +} + +static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr, + char *buffer) +{ + struct spi_device *spi = to_spi_device(dev); + struct pcf2123_sysfs_reg *r; + u8 txbuf[1], rxbuf[1]; + unsigned long reg; + int ret; + + r = container_of(attr, struct pcf2123_sysfs_reg, attr); + + if (strict_strtoul(r->name, 16, ®)) + return -EINVAL; + + txbuf[0] = PCF2123_READ | reg; + ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1); + if (ret < 0) + return -EIO; + pcf2123_delay_trec(); + return sprintf(buffer, "0x%x\n", rxbuf[0]); +} + +static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr, + const char *buffer, size_t count) { + struct spi_device *spi = to_spi_device(dev); + struct pcf2123_sysfs_reg *r; + u8 txbuf[2]; + unsigned long reg; + unsigned long val; + + int ret; + + r = container_of(attr, struct pcf2123_sysfs_reg, attr); + + if (strict_strtoul(r->name, 16, ®) + || strict_strtoul(buffer, 10, &val)) + return -EINVAL; + + txbuf[0] = PCF2123_WRITE | reg; + txbuf[1] = val; + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + return -EIO; + pcf2123_delay_trec(); + return count; +} + +static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[1], rxbuf[7]; + int ret; + + txbuf[0] = PCF2123_READ | PCF2123_REG_SC; + ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), + rxbuf, sizeof(rxbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F); + tm->tm_min = bcd2bin(rxbuf[1] & 0x7F); + tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F); + tm->tm_wday = rxbuf[4] & 0x07; + tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(rxbuf[6]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* the clock can give out invalid datetime, but we cannot return + * -EINVAL otherwise hwclock will refuse to set the time on bootup. + */ + if (rtc_valid_tm(tm) < 0) + dev_err(dev, "retrieved date/time is not valid.\n"); + + return 0; +} + +static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[8]; + int ret; + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* Stop the counter first */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x20; + ret = spi_write(spi, txbuf, 2); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* Set the new time */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_SC; + txbuf[1] = bin2bcd(tm->tm_sec & 0x7F); + txbuf[2] = bin2bcd(tm->tm_min & 0x7F); + txbuf[3] = bin2bcd(tm->tm_hour & 0x3F); + txbuf[4] = bin2bcd(tm->tm_mday & 0x3F); + txbuf[5] = tm->tm_wday & 0x07; + txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */ + txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100); + + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* Start the counter */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x00; + ret = spi_write(spi, txbuf, 2); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + return 0; +} + +static const struct rtc_class_ops pcf2123_rtc_ops = { + .read_time = pcf2123_rtc_read_time, + .set_time = pcf2123_rtc_set_time, +}; + +static int __devinit pcf2123_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + struct pcf2123_plat_data *pdata; + u8 txbuf[2], rxbuf[2]; + int ret, i; + + pdata = kzalloc(sizeof(struct pcf2123_plat_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + spi->dev.platform_data = pdata; + + /* Send a software reset command */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x58; + dev_dbg(&spi->dev, "resetting RTC (0x%02X 0x%02X)\n", + txbuf[0], txbuf[1]); + ret = spi_write(spi, txbuf, 2 * sizeof(u8)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + /* Stop the counter */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x20; + dev_dbg(&spi->dev, "stopping RTC (0x%02X 0x%02X)\n", + txbuf[0], txbuf[1]); + ret = spi_write(spi, txbuf, 2 * sizeof(u8)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + /* See if the counter was actually stopped */ + txbuf[0] = PCF2123_READ | PCF2123_REG_CTRL1; + dev_dbg(&spi->dev, "checking for presence of RTC (0x%02X)\n", + txbuf[0]); + ret = spi_write_then_read(spi, txbuf, 1 * sizeof(u8), + rxbuf, 2 * sizeof(u8)); + dev_dbg(&spi->dev, "received data from RTC (0x%02X 0x%02X)\n", + rxbuf[0], rxbuf[1]); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + if (!(rxbuf[0] & 0x20)) { + dev_err(&spi->dev, "chip not found\n"); + goto kfree_exit; + } + + dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n"); + dev_info(&spi->dev, "spiclk %u KHz.\n", + (spi->max_speed_hz + 500) / 1000); + + /* Start the counter */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x00; + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + /* Finalize the initialization */ + rtc = rtc_device_register(pcf2123_driver.driver.name, &spi->dev, + &pcf2123_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&spi->dev, "failed to register.\n"); + ret = PTR_ERR(rtc); + goto kfree_exit; + } + + pdata->rtc = rtc; + + for (i = 0; i < 16; i++) { + sprintf(pdata->regs[i].name, "%1x", i); + pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR; + pdata->regs[i].attr.attr.name = pdata->regs[i].name; + pdata->regs[i].attr.show = pcf2123_show; + pdata->regs[i].attr.store = pcf2123_store; + ret = device_create_file(&spi->dev, &pdata->regs[i].attr); + if (ret) { + dev_err(&spi->dev, "Unable to create sysfs %s\n", + pdata->regs[i].name); + goto sysfs_exit; + } + } + + return 0; + +sysfs_exit: + for (i--; i >= 0; i--) + device_remove_file(&spi->dev, &pdata->regs[i].attr); + +kfree_exit: + kfree(pdata); + spi->dev.platform_data = NULL; + return ret; +} + +static int pcf2123_remove(struct spi_device *spi) +{ + struct pcf2123_plat_data *pdata = spi->dev.platform_data; + int i; + + if (pdata) { + struct rtc_device *rtc = pdata->rtc; + + if (rtc) + rtc_device_unregister(rtc); + for (i = 0; i < 16; i++) + if (pdata->regs[i].name[0]) + device_remove_file(&spi->dev, + &pdata->regs[i].attr); + kfree(pdata); + } + + return 0; +} + +static struct spi_driver pcf2123_driver = { + .driver = { + .name = "rtc-pcf2123", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = pcf2123_probe, + .remove = __devexit_p(pcf2123_remove), +}; + +static int __init pcf2123_init(void) +{ + return spi_register_driver(&pcf2123_driver); +} + +static void __exit pcf2123_exit(void) +{ + spi_unregister_driver(&pcf2123_driver); +} + +MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>"); +MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(pcf2123_init); +module_exit(pcf2123_exit); diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index 42028f233bef..9beba49c3c5b 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c @@ -174,3 +174,4 @@ module_exit(r9701_exit); MODULE_DESCRIPTION("r9701 spi RTC driver"); MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-r9701"); diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c index dd1e2bc7a472..2099037cb3ea 100644 --- a/drivers/rtc/rtc-rs5c348.c +++ b/drivers/rtc/rtc-rs5c348.c @@ -251,3 +251,4 @@ MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("spi:rtc-rs5c348"); diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c new file mode 100644 index 000000000000..d7ce1a5c857d --- /dev/null +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -0,0 +1,304 @@ +/* + * Freescale STMP37XX/STMP378X Real Time Clock driver + * + * Copyright (c) 2007 Sigmatel, Inc. + * Peter Hartley, <peter.hartley@sigmatel.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/rtc.h> + +#include <mach/platform.h> +#include <mach/stmp3xxx.h> +#include <mach/regs-rtc.h> + +struct stmp3xxx_rtc_data { + struct rtc_device *rtc; + unsigned irq_count; + void __iomem *io; + int irq_alarm, irq_1msec; +}; + +static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) +{ + /* + * The datasheet doesn't say which way round the + * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, + * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS + */ + while (__raw_readl(rtc_data->io + HW_RTC_STAT) & + BF(0x80, RTC_STAT_STALE_REGS)) + cpu_relax(); +} + +/* Time read/write */ +static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + stmp3xxx_wait_time(rtc_data); + rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_SECONDS), rtc_tm); + return 0; +} + +static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + __raw_writel(t, rtc_data->io + HW_RTC_SECONDS); + stmp3xxx_wait_time(rtc_data); + return 0; +} + +/* interrupt(s) handler */ +static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); + u32 status; + u32 events = 0; + + status = __raw_readl(rtc_data->io + HW_RTC_CTRL) & + (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ); + + if (status & BM_RTC_CTRL_ALARM_IRQ) { + stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ, + rtc_data->io + HW_RTC_CTRL); + events |= RTC_AF | RTC_IRQF; + } + + if (status & BM_RTC_CTRL_ONEMSEC_IRQ) { + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ, + rtc_data->io + HW_RTC_CTRL); + if (++rtc_data->irq_count % 1000 == 0) { + events |= RTC_UF | RTC_IRQF; + rtc_data->irq_count = 0; + } + } + + if (events) + rtc_update_irq(rtc_data->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + void __iomem *p = rtc_data->io + HW_RTC_PERSISTENT0, + *ctl = rtc_data->io + HW_RTC_CTRL; + + if (enabled) { + stmp3xxx_setl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_setl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + } else { + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + } + return 0; +} + +static int stmp3xxx_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (enabled) + stmp3xxx_setl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + else + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + return 0; +} + +static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_ALARM), &alm->time); + return 0; +} + +static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned long t; + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_tm_to_time(&alm->time, &t); + __raw_writel(t, rtc_data->io + HW_RTC_ALARM); + return 0; +} + +static struct rtc_class_ops stmp3xxx_rtc_ops = { + .alarm_irq_enable = + stmp3xxx_alarm_irq_enable, + .update_irq_enable = + stmp3xxx_update_irq_enable, + .read_time = stmp3xxx_rtc_gettime, + .set_mmss = stmp3xxx_rtc_set_mmss, + .read_alarm = stmp3xxx_rtc_read_alarm, + .set_alarm = stmp3xxx_rtc_set_alarm, +}; + +static int stmp3xxx_rtc_remove(struct platform_device *pdev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); + + if (!rtc_data) + return 0; + + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + free_irq(rtc_data->irq_alarm, &pdev->dev); + free_irq(rtc_data->irq_1msec, &pdev->dev); + rtc_device_unregister(rtc_data->rtc); + iounmap(rtc_data->io); + kfree(rtc_data); + + return 0; +} + +static int stmp3xxx_rtc_probe(struct platform_device *pdev) +{ + struct stmp3xxx_rtc_data *rtc_data; + struct resource *r; + int err; + + rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); + if (!rtc_data) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "failed to get resource\n"); + err = -ENXIO; + goto out_free; + } + + rtc_data->io = ioremap(r->start, resource_size(r)); + if (!rtc_data->io) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -EIO; + goto out_free; + } + + rtc_data->irq_alarm = platform_get_irq(pdev, 0); + rtc_data->irq_1msec = platform_get_irq(pdev, 1); + + if (!(__raw_readl(HW_RTC_STAT + rtc_data->io) & + BM_RTC_STAT_RTC_PRESENT)) { + dev_err(&pdev->dev, "no device onboard\n"); + err = -ENODEV; + goto out_remap; + } + + stmp3xxx_reset_block(rtc_data->io, true); + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + HW_RTC_PERSISTENT0); + rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, + &stmp3xxx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc_data->rtc)) { + err = PTR_ERR(rtc_data->rtc); + goto out_remap; + } + + rtc_data->irq_count = 0; + err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC alarm", &pdev->dev); + if (err) { + dev_err(&pdev->dev, "Cannot claim IRQ%d\n", + rtc_data->irq_alarm); + goto out_irq_alarm; + } + err = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC tick", &pdev->dev); + if (err) { + dev_err(&pdev->dev, "Cannot claim IRQ%d\n", + rtc_data->irq_1msec); + goto out_irq1; + } + + platform_set_drvdata(pdev, rtc_data); + + return 0; + +out_irq1: + free_irq(rtc_data->irq_alarm, &pdev->dev); +out_irq_alarm: + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + rtc_device_unregister(rtc_data->rtc); +out_remap: + iounmap(rtc_data->io); +out_free: + kfree(rtc_data); + return err; +} + +#ifdef CONFIG_PM +static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int stmp3xxx_rtc_resume(struct platform_device *dev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); + + stmp3xxx_reset_block(rtc_data->io, true); + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + HW_RTC_PERSISTENT0); + return 0; +} +#else +#define stmp3xxx_rtc_suspend NULL +#define stmp3xxx_rtc_resume NULL +#endif + +static struct platform_driver stmp3xxx_rtcdrv = { + .probe = stmp3xxx_rtc_probe, + .remove = stmp3xxx_rtc_remove, + .suspend = stmp3xxx_rtc_suspend, + .resume = stmp3xxx_rtc_resume, + .driver = { + .name = "stmp3xxx-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init stmp3xxx_rtc_init(void) +{ + return platform_driver_register(&stmp3xxx_rtcdrv); +} + +static void __exit stmp3xxx_rtc_exit(void) +{ + platform_driver_unregister(&stmp3xxx_rtcdrv); +} + +module_init(stmp3xxx_rtc_init); +module_exit(stmp3xxx_rtc_exit); + +MODULE_DESCRIPTION("STMP3xxx RTC Driver"); +MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 2531ce4c9db0..7dd23a6fc825 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -102,6 +102,19 @@ rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, return n; } +static ssize_t +rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef CONFIG_RTC_HCTOSYS_DEVICE + if (strcmp(dev_name(&to_rtc_device(dev)->dev), + CONFIG_RTC_HCTOSYS_DEVICE) == 0) + return sprintf(buf, "1\n"); + else +#endif + return sprintf(buf, "0\n"); +} + static struct device_attribute rtc_attrs[] = { __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), @@ -109,6 +122,7 @@ static struct device_attribute rtc_attrs[] = { __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq, rtc_sysfs_set_max_user_freq), + __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), { }, }; |