S3C RTC driver: add support for S3C64XX Add support for the S3C64XX SoC to the generic S3C RTC driver. Signed-off-by: Maurus Cuelenaere <mcuelenaere@xxxxxxxxx> --- arch/arm/mach-s3c6400/include/mach/map.h | 1 + arch/arm/plat-s3c/include/plat/regs-rtc.h | 4 + arch/arm/plat-s3c64xx/Makefile | 1 + arch/arm/plat-s3c64xx/dev-rtc.c | 43 ++++++++++++ drivers/rtc/Kconfig | 2 +- drivers/rtc/rtc-s3c.c | 107 +++++++++++++++++++++++------ 6 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 arch/arm/plat-s3c64xx/dev-rtc.c diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index 106ee13..793fb8b 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -42,6 +42,7 @@ #define S3C64XX_PA_FB (0x77100000) #define S3C64XX_PA_USB_HSOTG (0x7C000000) #define S3C64XX_PA_WATCHDOG (0x7E004000) +#define S3C64XX_PA_RTC (0x7E005000) #define S3C64XX_PA_SYSCON (0x7E00F000) #define S3C64XX_PA_AC97 (0x7F001000) #define S3C64XX_PA_IIS0 (0x7F002000) diff --git a/arch/arm/plat-s3c/include/plat/regs-rtc.h b/arch/arm/plat-s3c/include/plat/regs-rtc.h index d5837cf..65c190d 100644 --- a/arch/arm/plat-s3c/include/plat/regs-rtc.h +++ b/arch/arm/plat-s3c/include/plat/regs-rtc.h @@ -20,6 +20,10 @@ #define S3C2410_RTCCON_CLKSEL (1<<1) #define S3C2410_RTCCON_CNTSEL (1<<2) #define S3C2410_RTCCON_CLKRST (1<<3) +#define S3C64XX_RTCCON_TICEN (1<<8) + +#define S3C64XX_RTCCON_TICMSK (0xF<<7) +#define S3C64XX_RTCCON_TICSHT (7) #define S3C2410_TICNT S3C2410_RTCREG(0x44) #define S3C2410_TICNT_ENABLE (1<<7) diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile index b85b435..e66dbd7 100644 --- a/arch/arm/plat-s3c64xx/Makefile +++ b/arch/arm/plat-s3c64xx/Makefile @@ -13,6 +13,7 @@ obj- := # Core files obj-y += dev-uart.o +obj-y += dev-rtc.o obj-y += cpu.o obj-y += irq.o obj-y += irq-eint.o diff --git a/arch/arm/plat-s3c64xx/dev-rtc.c b/arch/arm/plat-s3c64xx/dev-rtc.c new file mode 100644 index 0000000..b9e7a05 --- /dev/null +++ b/arch/arm/plat-s3c64xx/dev-rtc.c @@ -0,0 +1,43 @@ +/* linux/arch/arm/plat-s3c64xx/dev-rtc.c + * + * Copyright 2009 by Maurus Cuelenaere <mcuelenaere@xxxxxxxxx> + * + * 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/string.h> +#include <linux/platform_device.h> + +#include <mach/irqs.h> +#include <mach/map.h> + +#include <plat/devs.h> + +static struct resource s3c_rtc_resource[] = { + [0] = { + .start = S3C64XX_PA_RTC, + .end = S3C64XX_PA_RTC + 0xff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_RTC_ALARM, + .end = IRQ_RTC_ALARM, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_RTC_TIC, + .end = IRQ_RTC_TIC, + .flags = IORESOURCE_IRQ + } +}; + +struct platform_device s3c_device_rtc = { + .name = "s3c64xx-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_rtc_resource), + .resource = s3c_rtc_resource, +}; +EXPORT_SYMBOL(s3c_device_rtc); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8167e9e..b44db44 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -620,7 +620,7 @@ config RTC_DRV_OMAP config RTC_DRV_S3C tristate "Samsung S3C series SoC RTC" - depends on ARCH_S3C2410 + depends on ARCH_S3C2410 || ARCH_S3C64XX help RTC (Realtime Clock) driver for the clock inbuilt into the Samsung S3C24XX series of SoCs. This can provide periodic diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index e0d7b99..798d2dd 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -28,6 +28,11 @@ #include <asm/irq.h> #include <plat/regs-rtc.h> +enum s3c_cpu_type { + TYPE_S3C2410, + TYPE_S3C64XX, +}; + /* I have yet to find an S3C implementation with more than one * of these rtc blocks in */ @@ -36,6 +41,7 @@ static struct resource *s3c_rtc_mem; static void __iomem *s3c_rtc_base; static int s3c_rtc_alarmno = NO_IRQ; static int s3c_rtc_tickno = NO_IRQ; +static enum s3c_cpu_type s3c_rtc_cpu_type; static DEFINE_SPINLOCK(s3c_rtc_pie_lock); @@ -79,12 +85,25 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) pr_debug("%s: pie=%d\n", __func__, enabled); spin_lock_irq(&s3c_rtc_pie_lock); - tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; - if (enabled) - tmp |= S3C2410_TICNT_ENABLE; + if (s3c_rtc_cpu_type == TYPE_S3C64XX) { + tmp = readb(s3c_rtc_base + S3C2410_RTCCON); + tmp &= ~S3C64XX_RTCCON_TICEN; + + if (enabled) + tmp |= S3C64XX_RTCCON_TICEN; + + writeb(tmp, s3c_rtc_base + S3C2410_RTCCON); + } else { + tmp = readb(s3c_rtc_base + S3C2410_TICNT); + tmp &= ~S3C2410_TICNT_ENABLE; + + if (enabled) + tmp |= S3C2410_TICNT_ENABLE; + + writeb(tmp, s3c_rtc_base + S3C2410_TICNT); + } - writeb(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock); return 0; @@ -92,15 +111,21 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) static int s3c_rtc_setfreq(struct device *dev, int freq) { - unsigned int tmp; + 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); - tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; - tmp |= (128 / freq)-1; + 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; writeb(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock); @@ -282,10 +307,17 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) { - unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT); + unsigned int ticnt; - seq_printf(seq, "periodic_IRQ\t: %s\n", - (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); + if (s3c_rtc_cpu_type == TYPE_S3C64XX) { + ticnt = readb(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; } @@ -352,10 +384,16 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) if (!en) { tmp = readb(base + S3C2410_RTCCON); - writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); - - tmp = readb(base + S3C2410_TICNT); - writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT); + if (s3c_rtc_cpu_type == TYPE_S3C64XX) + tmp &= ~S3C64XX_RTCCON_TICEN; + tmp &= ~S3C2410_RTCCON_RTCEN; + writeb(tmp, base + S3C2410_RTCCON); + + if (s3c_rtc_cpu_type == TYPE_S3C2410) { + tmp = readb(base + S3C2410_TICNT); + tmp &= ~S3C2410_TICNT_ENABLE; + writeb(tmp, base + S3C2410_TICNT); + } } else { /* re-enable the device, and check it is ok */ @@ -471,7 +509,12 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) goto err_nortc; } - rtc->max_user_freq = 128; + if (s3c_rtc_cpu_type == TYPE_S3C64XX) + rtc->max_user_freq = 32768; + else + rtc->max_user_freq = 128; + + s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; platform_set_drvdata(pdev, rtc); return 0; @@ -491,20 +534,30 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) /* RTC Power management control */ -static int ticnt_save; +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 = readb(s3c_rtc_base + S3C2410_RTCCON); + ticnt_en_save &= S3C64XX_RTCCON_TICEN; + } s3c_rtc_enable(pdev, 0); return 0; } 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 = readb(s3c_rtc_base + S3C2410_RTCCON); + writeb(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); + } return 0; } #else @@ -512,13 +565,27 @@ static int s3c_rtc_resume(struct platform_device *pdev) #define s3c_rtc_resume NULL #endif -static struct platform_driver s3c2410_rtc_driver = { +static struct platform_device_id s3c_rtc_driver_ids[] = { + { + .name = "s3c2410-rtc", + .driver_data = TYPE_S3C2410, + }, { + .name = "s3c64xx-rtc", + .driver_data = TYPE_S3C64XX, + }, + { } +}; + +MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); + +static struct platform_driver s3c_rtc_driver = { .probe = s3c_rtc_probe, .remove = __devexit_p(s3c_rtc_remove), .suspend = s3c_rtc_suspend, .resume = s3c_rtc_resume, + .id_table = s3c_rtc_driver_ids, .driver = { - .name = "s3c2410-rtc", + .name = "s3c-rtc", .owner = THIS_MODULE, }, }; @@ -528,12 +595,12 @@ static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics static int __init s3c_rtc_init(void) { printk(banner); - return platform_driver_register(&s3c2410_rtc_driver); + return platform_driver_register(&s3c_rtc_driver); } static void __exit s3c_rtc_exit(void) { - platform_driver_unregister(&s3c2410_rtc_driver); + platform_driver_unregister(&s3c_rtc_driver); } module_init(s3c_rtc_init); -- 1.6.6 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html