diff options
Diffstat (limited to 'drivers/rtc/rtc-s3c.c')
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 307 |
1 files changed, 114 insertions, 193 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 16512ec..8f49de7 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -27,14 +27,15 @@ #include <linux/slab.h> #include <mach/hardware.h> -#include <asm/uaccess.h> -#include <asm/io.h> +#include <linux/uaccess.h> +#include <linux/io.h> #include <asm/irq.h> #include <plat/regs-rtc.h> enum s3c_cpu_type { TYPE_S3C2410, TYPE_S3C64XX, + TYPE_EXYNOS, }; /* I have yet to find an S3C implementation with more than one @@ -45,12 +46,9 @@ static struct resource *s3c_rtc_mem; static struct clk *rtc_clk; static void __iomem *s3c_rtc_base; static int s3c_rtc_alarmno = NO_IRQ; -static int s3c_rtc_tickno = NO_IRQ; static bool wake_en; static enum s3c_cpu_type s3c_rtc_cpu_type; -static DEFINE_SPINLOCK(s3c_rtc_pie_lock); - /* IRQ Handlers */ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) @@ -59,24 +57,12 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) + if (s3c_rtc_cpu_type != TYPE_S3C2410) writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); return IRQ_HANDLED; } -static irqreturn_t s3c_rtc_tickirq(int irq, void *id) -{ - struct rtc_device *rdev = id; - - rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); - - if (s3c_rtc_cpu_type == TYPE_S3C64XX) - writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); - - return IRQ_HANDLED; -} - /* Update control registers */ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) { @@ -94,30 +80,6 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) return 0; } -static int s3c_rtc_setfreq(struct device *dev, int freq) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc_dev = platform_get_drvdata(pdev); - unsigned int tmp = 0; - - if (!is_power_of_2(freq)) - return -EINVAL; - - spin_lock_irq(&s3c_rtc_pie_lock); - - if (s3c_rtc_cpu_type == TYPE_S3C2410) { - tmp = readb(s3c_rtc_base + S3C2410_TICNT); - tmp &= S3C2410_TICNT_ENABLE; - } - - tmp |= (rtc_dev->max_user_freq / freq)-1; - - writel(tmp, s3c_rtc_base + S3C2410_TICNT); - spin_unlock_irq(&s3c_rtc_pie_lock); - - return 0; -} - /* Time read/write */ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) @@ -130,7 +92,12 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); rtc_tm->tm_mon = readb(base + S3C2410_RTCMON); - rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); + + if (s3c_rtc_cpu_type == TYPE_EXYNOS) + rtc_tm->tm_year = readw(base + S3C2410_RTCYEAR) & 0x0fff; + else + rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); + rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC); /* the only way to work out wether the system was mid-update @@ -143,18 +110,24 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) goto retry_get_time; } - pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n", - 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, - rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); - rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); - rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); + + if (s3c_rtc_cpu_type == TYPE_EXYNOS) + rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year) + + (rtc_tm->tm_year >> 8) * 100; + else + rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); rtc_tm->tm_year += 100; + + pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n", + 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, + rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); + rtc_tm->tm_mon -= 1; return rtc_valid_tm(rtc_tm); @@ -166,14 +139,16 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) int year = tm->tm_year - 100; pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n", - 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - /* we get around y2k by simply not supporting it */ - if (year < 0 || year >= 100) { - dev_err(dev, "rtc only supports 100 years\n"); - return -EINVAL; + if (!(s3c_rtc_cpu_type == TYPE_EXYNOS + && year >= 100 && year < 1000)) { + dev_err(dev, "rtc can't support %04d year\n", + year + 2000); + return -EINVAL; + } } writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC); @@ -181,7 +156,13 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR); writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE); writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON); - writeb(bin2bcd(year), base + S3C2410_RTCYEAR); + + if (s3c_rtc_cpu_type == TYPE_EXYNOS) { + year = (((year / 100) << 8) + (((year % 100) / 10) << 4) + year % 10); + writew(year, base + S3C2410_RTCYEAR); + } else { + writeb(bin2bcd(year), base + S3C2410_RTCYEAR); + } return 0; } @@ -197,7 +178,13 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR); alm_tm->tm_mon = readb(base + S3C2410_ALMMON); alm_tm->tm_mday = readb(base + S3C2410_ALMDATE); - alm_tm->tm_year = readb(base + S3C2410_ALMYEAR); + + if (s3c_rtc_cpu_type == TYPE_EXYNOS) + alm_tm->tm_year = readw(base + S3C2410_ALMYEAR) & 0xfff; + else + alm_tm->tm_year = readb(base + S3C2410_ALMYEAR); + + alm_tm->tm_year += 100; alm_en = readb(base + S3C2410_RTCALM); @@ -211,37 +198,27 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) /* decode the alarm enable field */ - if (alm_en & S3C2410_RTCALM_SECEN) + if (alrm->enabled) { alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); - else - alm_tm->tm_sec = -1; - - if (alm_en & S3C2410_RTCALM_MINEN) alm_tm->tm_min = bcd2bin(alm_tm->tm_min); - else - alm_tm->tm_min = -1; - - if (alm_en & S3C2410_RTCALM_HOUREN) alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); - else - alm_tm->tm_hour = -1; - - if (alm_en & S3C2410_RTCALM_DAYEN) alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); - else - alm_tm->tm_mday = -1; + alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1; + + if (s3c_rtc_cpu_type == TYPE_EXYNOS) + alm_tm->tm_year = bcd2bin(alm_tm->tm_year) + + (alm_tm->tm_year >> 8) * 100; + else + alm_tm->tm_year = bcd2bin(alm_tm->tm_year); - if (alm_en & S3C2410_RTCALM_MONEN) { - alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); - alm_tm->tm_mon -= 1; } else { + alm_tm->tm_sec = -1; + alm_tm->tm_min = -1; + alm_tm->tm_hour = -1; + alm_tm->tm_mday = -1; alm_tm->tm_mon = -1; - } - - if (alm_en & S3C2410_RTCALM_YEAREN) - alm_tm->tm_year = bcd2bin(alm_tm->tm_year); - else alm_tm->tm_year = -1; + } return 0; } @@ -251,104 +228,56 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) struct rtc_time *tm = &alrm->time; void __iomem *base = s3c_rtc_base; unsigned int alrm_en; + int year = tm->tm_year - 100; pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, - 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + if (year < 0 || year >= 100) { + if (!(s3c_rtc_cpu_type == TYPE_EXYNOS + && year >= 100 && year < 1000)) { + dev_err(dev, "rtc can't support %04d year\n", + 2000 + year); + return -EINVAL; + } + } alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; - writeb(0x00, base + S3C2410_RTCALM); - if (tm->tm_sec < 60 && tm->tm_sec >= 0) { - alrm_en |= S3C2410_RTCALM_SECEN; - writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC); - } + if (alrm->enabled) { + writeb(~S3C2410_RTCALM_ALL, base + S3C2410_RTCALM); - if (tm->tm_min < 60 && tm->tm_min >= 0) { - alrm_en |= S3C2410_RTCALM_MINEN; + writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC); writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN); - } - - if (tm->tm_hour < 24 && tm->tm_hour >= 0) { - alrm_en |= S3C2410_RTCALM_HOUREN; writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); - } - - pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); - - writeb(alrm_en, base + S3C2410_RTCALM); - - s3c_rtc_setaie(dev, alrm->enabled); - - return 0; -} - -static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) -{ - unsigned int ticnt; - - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt = readw(s3c_rtc_base + S3C2410_RTCCON); - ticnt &= S3C64XX_RTCCON_TICEN; - } else { - ticnt = readb(s3c_rtc_base + S3C2410_TICNT); - ticnt &= S3C2410_TICNT_ENABLE; - } - - seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); - return 0; -} - -static int s3c_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(s3c_rtc_alarmno, s3c_rtc_alarmirq, - IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev); - - if (ret) { - dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); - return ret; - } + writeb(bin2bcd(tm->tm_mday), base + S3C2410_ALMDATE); + writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_ALMMON); + + if (s3c_rtc_cpu_type == TYPE_EXYNOS) { + year = (((year / 100) << 8) + (((year % 100) / 10) << 4) + year % 10); + writew(year, base + S3C2410_ALMYEAR); + } else { + writeb(bin2bcd(year), base + S3C2410_ALMYEAR); + } - ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq, - IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev); + writeb(S3C2410_RTCALM_ALL, base + S3C2410_RTCALM); - if (ret) { - dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret); - goto tick_err; + pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); } - return ret; - - tick_err: - free_irq(s3c_rtc_alarmno, rtc_dev); - return ret; -} - -static void s3c_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 */ + if (alrm->enabled != alrm_en) + s3c_rtc_setaie(dev, alrm->enabled); - free_irq(s3c_rtc_alarmno, rtc_dev); - free_irq(s3c_rtc_tickno, rtc_dev); + return 0; } static const struct rtc_class_ops s3c_rtcops = { - .open = s3c_rtc_open, - .release = s3c_rtc_release, .read_time = s3c_rtc_gettime, .set_time = s3c_rtc_settime, .read_alarm = s3c_rtc_getalarm, .set_alarm = s3c_rtc_setalarm, - .proc = s3c_rtc_proc, .alarm_irq_enable = s3c_rtc_setaie, }; @@ -362,7 +291,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) if (!en) { tmp = readw(base + S3C2410_RTCCON); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) + if (s3c_rtc_cpu_type != TYPE_S3C2410) tmp &= ~S3C64XX_RTCCON_TICEN; tmp &= ~S3C2410_RTCCON_RTCEN; writew(tmp, base + S3C2410_RTCCON); @@ -405,6 +334,8 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev) { struct rtc_device *rtc = platform_get_drvdata(dev); + free_irq(s3c_rtc_alarmno, rtc); + platform_set_drvdata(dev, NULL); rtc_device_unregister(rtc); @@ -432,20 +363,13 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) /* find the IRQs */ - s3c_rtc_tickno = platform_get_irq(pdev, 1); - if (s3c_rtc_tickno < 0) { - dev_err(&pdev->dev, "no irq for rtc tick\n"); - return -ENOENT; - } - s3c_rtc_alarmno = platform_get_irq(pdev, 0); if (s3c_rtc_alarmno < 0) { dev_err(&pdev->dev, "no irq for alarm\n"); return -ENOENT; } - pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n", - s3c_rtc_tickno, s3c_rtc_alarmno); + pr_debug("s3c2410_rtc: alarm irq %d\n", s3c_rtc_alarmno); /* get the memory region */ @@ -456,7 +380,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) } s3c_rtc_mem = request_mem_region(res->start, - res->end-res->start+1, + resource_size(res), pdev->name); if (s3c_rtc_mem == NULL) { @@ -465,7 +389,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) goto err_nores; } - s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); + s3c_rtc_base = ioremap(res->start, resource_size(res)); if (s3c_rtc_base == NULL) { dev_err(&pdev->dev, "failed ioremap()\n"); ret = -EINVAL; @@ -491,21 +415,10 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); - /* register RTC and exit */ - - rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, - THIS_MODULE); - - if (IS_ERR(rtc)) { - dev_err(&pdev->dev, "cannot attach rtc\n"); - ret = PTR_ERR(rtc); - goto err_nortc; - } + /* Check RTC Time */ s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; - /* Check RTC Time */ - s3c_rtc_gettime(NULL, &rtc_tm); if (rtc_valid_tm(&rtc_tm)) { @@ -521,17 +434,36 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); } - if (s3c_rtc_cpu_type == TYPE_S3C64XX) + /* register RTC and exit */ + + rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, + THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&pdev->dev, "cannot attach rtc\n"); + ret = PTR_ERR(rtc); + goto err_nortc; + } + + if (s3c_rtc_cpu_type != TYPE_S3C2410) rtc->max_user_freq = 32768; else rtc->max_user_freq = 128; platform_set_drvdata(pdev, rtc); - s3c_rtc_setfreq(&pdev->dev, 1); + ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq, + IRQF_DISABLED, "s3c2410-rtc alarm", rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); + goto err_irq; + } return 0; + err_irq: + rtc_device_unregister(rtc); + err_nortc: s3c_rtc_enable(pdev, 0); clk_disable(rtc_clk); @@ -541,6 +473,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) iounmap(s3c_rtc_base); err_nomap: + release_mem_region(res->start, resource_size(res)); release_resource(s3c_rtc_mem); err_nores: @@ -551,16 +484,8 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) /* RTC Power management control */ -static int ticnt_save, ticnt_en_save; - static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) { - /* save TICNT for anyone using periodic interrupts */ - ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); - ticnt_en_save &= S3C64XX_RTCCON_TICEN; - } s3c_rtc_enable(pdev, 0); if (device_may_wakeup(&pdev->dev) && !wake_en) { @@ -575,14 +500,7 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) static int s3c_rtc_resume(struct platform_device *pdev) { - unsigned int tmp; - s3c_rtc_enable(pdev, 1); - writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); - if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { - tmp = readw(s3c_rtc_base + S3C2410_RTCCON); - writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); - } if (device_may_wakeup(&pdev->dev) && wake_en) { disable_irq_wake(s3c_rtc_alarmno); @@ -603,6 +521,9 @@ static struct platform_device_id s3c_rtc_driver_ids[] = { }, { .name = "s3c64xx-rtc", .driver_data = TYPE_S3C64XX, + }, { + .name = "exynos-rtc", + .driver_data = TYPE_EXYNOS, }, { } }; |