diff options
author | Fred Fan <r01011@freescale.com> | 2010-01-26 20:24:08 +0800 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2010-12-17 12:09:42 -0500 |
commit | ba41cc7112352f27f465c49f03df6dce92451685 (patch) | |
tree | 003f7ea45279f6e381ba18edb1e6273266ecfa0f /drivers/rtc | |
parent | 9c04f92efdb58c643a054f3135b01bf96bbfc806 (diff) |
ENGR00120475-1 i.MX28 rtc support
Copy driver from i.mx233 and rename to rtc-mxs
Signed-off-by: Fred.fan <r01011@freescale.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-mxs.c | 290 |
3 files changed, 300 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9b6c16bf0351..09492700cddf 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -755,6 +755,15 @@ config RTC_DRV_STMP3XXX This driver can also be build as a module. If so, the module will be called rtc-stmp3xxx. +config RTC_DRV_MXS + tristate "Freescale MXS series SoC RTC" + depends on ARCH_MXS && RTC_CLASS + help + Say Y here to get support for the real-time clock peripheral + on Freescale MXS series SoCs + This driver can also be build as a module. If so, the module + will be called rtc-mxs. + config RTC_DRV_SUN4V bool "SUN4V Hypervisor RTC" depends on SPARC64 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index e327ad2d188e..91da97eca589 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -83,3 +83,4 @@ obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o obj-$(CONFIG_RTC_MC13892) += rtc-mc13892.o obj-$(CONFIG_RTC_DRV_STMP3XXX) += rtc-stmp3xxx.o +obj-$(CONFIG_RTC_DRV_MXS) += rtc-mxs.o diff --git a/drivers/rtc/rtc-mxs.c b/drivers/rtc/rtc-mxs.c new file mode 100644 index 000000000000..bad08ff5d82b --- /dev/null +++ b/drivers/rtc/rtc-mxs.c @@ -0,0 +1,290 @@ +/* + * Freescale STMP37XX/STMP378X Real Time Clock driver + * + * Copyright (c) 2007 Sigmatel, Inc. + * Peter Hartley, <peter.hartley@sigmatel.com> + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * 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/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/io.h> +#include <linux/irq.h> +#include <linux/uaccess.h> + +#include <mach/stmp3xxx.h> +#include <mach/hardware.h> +#include <mach/platform.h> +#include <mach/irqs.h> +#include <mach/regs-rtc.h> + +struct stmp3xxx_rtc_data { + struct rtc_device *rtc; + unsigned irq_count; +}; + +/* Time read/write */ +static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + while (__raw_readl(REGS_RTC_BASE + HW_RTC_STAT) & BF(0x80, RTC_STAT_STALE_REGS)) + cpu_relax(); + + rtc_time_to_tm(__raw_readl(REGS_RTC_BASE + HW_RTC_SECONDS), rtc_tm); + return 0; +} + +static int stmp3xxx_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) +{ + unsigned long t; + int rc = rtc_tm_to_time(rtc_tm, &t); + + if (rc == 0) { + __raw_writel(t, REGS_RTC_BASE + HW_RTC_SECONDS); + + /* 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(REGS_RTC_BASE + HW_RTC_STAT) & BF(0x80, RTC_STAT_NEW_REGS)) + cpu_relax(); + } + return rc; +} + +static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = to_platform_device(dev_id); + struct stmp3xxx_rtc_data *data = platform_get_drvdata(pdev); + u32 status; + u32 events = 0; + + status = __raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) & + (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ); + if (status & BM_RTC_CTRL_ALARM_IRQ) { + __raw_writel(BM_RTC_CTRL_ALARM_IRQ, + REGS_RTC_BASE + HW_RTC_CTRL_CLR); + events |= RTC_AF | RTC_IRQF; + } + if (status & BM_RTC_CTRL_ONEMSEC_IRQ) { + __raw_writel(BM_RTC_CTRL_ONEMSEC_IRQ, + REGS_RTC_BASE + HW_RTC_CTRL_CLR); + if (++data->irq_count % 1000 == 0) { + events |= RTC_UF | RTC_IRQF; + data->irq_count = 0; + } + } + + if (events) + rtc_update_irq(data->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int stmp3xxx_rtc_open(struct device *dev) +{ + int r; + + r = request_irq(IRQ_RTC_ALARM, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC alarm", dev); + if (r) { + dev_err(dev, "Cannot claim IRQ%d\n", IRQ_RTC_ALARM); + goto fail_1; + } + r = request_irq(IRQ_RTC_1MSEC, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC tick", dev); + if (r) { + dev_err(dev, "Cannot claim IRQ%d\n", IRQ_RTC_1MSEC); + goto fail_2; + } + + return 0; +fail_2: + free_irq(IRQ_RTC_ALARM, dev); +fail_1: + return r; +} + +static void stmp3xxx_rtc_release(struct device *dev) +{ + __raw_writel(BM_RTC_CTRL_ALARM_IRQ_EN | BM_RTC_CTRL_ONEMSEC_IRQ_EN, + REGS_RTC_BASE + HW_RTC_CTRL_CLR); + free_irq(IRQ_RTC_ALARM, dev); + free_irq(IRQ_RTC_1MSEC, dev); +} + +static int stmp3xxx_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct stmp3xxx_rtc_data *data = dev_get_drvdata(dev); + + switch (cmd) { + case RTC_AIE_OFF: + __raw_writel(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, + REGS_RTC_BASE + HW_RTC_PERSISTENT0_CLR); + __raw_writel(BM_RTC_CTRL_ALARM_IRQ_EN, + REGS_RTC_BASE + HW_RTC_CTRL_CLR); + break; + case RTC_AIE_ON: + __raw_writel(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, + REGS_RTC_BASE + HW_RTC_PERSISTENT0_SET); + + __raw_writel(BM_RTC_CTRL_ALARM_IRQ_EN, + REGS_RTC_BASE + HW_RTC_CTRL_SET); + break; + case RTC_UIE_ON: + data->irq_count = 0; + __raw_writel(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + REGS_RTC_BASE + HW_RTC_CTRL_SET); + break; + case RTC_UIE_OFF: + __raw_writel(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + REGS_RTC_BASE + HW_RTC_CTRL_CLR); + break; + default: + return -ENOIOCTLCMD; + } + + return 0; +} +static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u32 t = __raw_readl(REGS_RTC_BASE + HW_RTC_ALARM); + + rtc_time_to_tm(t, &alm->time); + + return 0; +} + +static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned long t; + + rtc_tm_to_time(&alm->time, &t); + __raw_writel(t, REGS_RTC_BASE + HW_RTC_ALARM); + return 0; +} + +static struct rtc_class_ops stmp3xxx_rtc_ops = { + .open = stmp3xxx_rtc_open, + .release = stmp3xxx_rtc_release, + .ioctl = stmp3xxx_rtc_ioctl, + .read_time = stmp3xxx_rtc_gettime, + .set_time = stmp3xxx_rtc_settime, + .read_alarm = stmp3xxx_rtc_read_alarm, + .set_alarm = stmp3xxx_rtc_set_alarm, +}; + +static int stmp3xxx_rtc_remove(struct platform_device *dev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); + + if (rtc_data) { + rtc_device_unregister(rtc_data->rtc); + kfree(rtc_data); + } + + return 0; +} + +static int stmp3xxx_rtc_probe(struct platform_device *pdev) +{ + u32 hwversion = __raw_readl(REGS_RTC_BASE + HW_RTC_VERSION); + u32 rtc_stat = __raw_readl(REGS_RTC_BASE + HW_RTC_STAT); + struct stmp3xxx_rtc_data *rtc_data = kzalloc(sizeof *rtc_data, + GFP_KERNEL); + + if ((rtc_stat & BM_RTC_STAT_RTC_PRESENT) == 0) + return -ENODEV; + if (!rtc_data) + return -ENOMEM; + + stmp3xxx_reset_block(REGS_RTC_BASE, 1); + + __raw_writel(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + REGS_RTC_BASE + HW_RTC_PERSISTENT0_CLR); + + printk(KERN_INFO "STMP3xxx RTC driver v1.0 hardware v%u.%u.%u\n", + (hwversion >> 24), + (hwversion >> 16) & 0xFF, + hwversion & 0xFFFF); + + rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, + &stmp3xxx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc_data->rtc)) { + kfree(rtc_data); + return PTR_ERR(rtc_data->rtc); + } + + platform_set_drvdata(pdev, rtc_data); + + return 0; +} + +#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) +{ + stmp3xxx_reset_block(REGS_RTC_BASE, 1); + __raw_writel(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + REGS_RTC_BASE + HW_RTC_PERSISTENT0_CLR); + 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 <dimka@embeddedalley.com>"); +MODULE_LICENSE("GPL"); |