diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 30 | ||||
-rw-r--r-- | drivers/rtc/class.c | 30 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91sam9.c | 138 | ||||
-rw-r--r-- | drivers/rtc/rtc-lib.c | 38 |
4 files changed, 162 insertions, 74 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6dd12ddbabc..b682651b530 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1109,36 +1109,42 @@ config RTC_DRV_AT91RM9200 this is powered by the backup power supply. config RTC_DRV_AT91SAM9 - tristate "AT91SAM9x/AT91CAP9 RTT as RTC" - depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40) + tristate "AT91SAM9 RTT as RTC" + depends on ARCH_AT91 + select MFD_SYSCON help - RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT - (Real Time Timer). These timers are powered by the backup power - supply (such as a small coin cell battery), but do not need to - be used as RTCs. - - (On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the - dedicated RTC module and leave the RTT available for other uses.) + Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which + can be used as an RTC thanks to the backup power supply (e.g. a + small coin cell battery) which keeps this block and the GPBR + (General Purpose Backup Registers) block powered when the device + is shutdown. + Some AT91SAM9 SoCs provide a real RTC block, on those ones you'd + probably want to use the real RTC block instead of the "RTT as an + RTC" driver. config RTC_DRV_AT91SAM9_RTT int range 0 1 default 0 - prompt "RTT module Number" if ARCH_AT91SAM9263 depends on RTC_DRV_AT91SAM9 help + This option is only relevant for legacy board support and + won't be used when booting a DT board. + More than one RTT module is available. You can choose which one will be used as an RTC. The default of zero is normally OK to use, though some systems use that for non-RTC purposes. config RTC_DRV_AT91SAM9_GPBR int - range 0 3 if !ARCH_AT91SAM9263 - range 0 15 if ARCH_AT91SAM9263 + range 0 3 default 0 prompt "Backup Register Number" depends on RTC_DRV_AT91SAM9 help + This option is only relevant for legacy board support and + won't be used when booting a DT board. + The RTC driver needs to use one of the General Purpose Backup Registers (GPBRs) as well as the RTT. You can choose which one will be used. The default of zero is normally OK to use, but diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 38e26be705b..472a5adc464 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -45,14 +45,14 @@ int rtc_hctosys_ret = -ENODEV; * system's wall clock; restore it on resume(). */ -static struct timespec old_rtc, old_system, old_delta; +static struct timespec64 old_rtc, old_system, old_delta; static int rtc_suspend(struct device *dev) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - struct timespec delta, delta_delta; + struct timespec64 delta, delta_delta; int err; if (has_persistent_clock()) @@ -68,8 +68,8 @@ static int rtc_suspend(struct device *dev) return 0; } - getnstimeofday(&old_system); - rtc_tm_to_time(&tm, &old_rtc.tv_sec); + getnstimeofday64(&old_system); + old_rtc.tv_sec = rtc_tm_to_time64(&tm); /* @@ -78,8 +78,8 @@ static int rtc_suspend(struct device *dev) * try to compensate so the difference in system time * and rtc time stays close to constant. */ - delta = timespec_sub(old_system, old_rtc); - delta_delta = timespec_sub(delta, old_delta); + delta = timespec64_sub(old_system, old_rtc); + delta_delta = timespec64_sub(delta, old_delta); if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) { /* * if delta_delta is too large, assume time correction @@ -88,7 +88,7 @@ static int rtc_suspend(struct device *dev) old_delta = delta; } else { /* Otherwise try to adjust old_system to compensate */ - old_system = timespec_sub(old_system, delta_delta); + old_system = timespec64_sub(old_system, delta_delta); } return 0; @@ -98,8 +98,8 @@ static int rtc_resume(struct device *dev) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - struct timespec new_system, new_rtc; - struct timespec sleep_time; + struct timespec64 new_system, new_rtc; + struct timespec64 sleep_time; int err; if (has_persistent_clock()) @@ -110,7 +110,7 @@ static int rtc_resume(struct device *dev) return 0; /* snapshot the current rtc and system time at resume */ - getnstimeofday(&new_system); + getnstimeofday64(&new_system); err = rtc_read_time(rtc, &tm); if (err < 0) { pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev)); @@ -121,7 +121,7 @@ static int rtc_resume(struct device *dev) pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev)); return 0; } - rtc_tm_to_time(&tm, &new_rtc.tv_sec); + new_rtc.tv_sec = rtc_tm_to_time64(&tm); new_rtc.tv_nsec = 0; if (new_rtc.tv_sec < old_rtc.tv_sec) { @@ -130,7 +130,7 @@ static int rtc_resume(struct device *dev) } /* calculate the RTC time delta (sleep time)*/ - sleep_time = timespec_sub(new_rtc, old_rtc); + sleep_time = timespec64_sub(new_rtc, old_rtc); /* * Since these RTC suspend/resume handlers are not called @@ -139,11 +139,11 @@ static int rtc_resume(struct device *dev) * so subtract kernel run-time between rtc_suspend to rtc_resume * to keep things accurate. */ - sleep_time = timespec_sub(sleep_time, - timespec_sub(new_system, old_system)); + sleep_time = timespec64_sub(sleep_time, + timespec64_sub(new_system, old_system)); if (sleep_time.tv_sec >= 0) - timekeeping_inject_sleeptime(&sleep_time); + timekeeping_inject_sleeptime64(&sleep_time); rtc_hctosys_ret = 0; return 0; } diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 59637430453..abac38abd38 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -21,10 +21,9 @@ #include <linux/slab.h> #include <linux/platform_data/atmel.h> #include <linux/io.h> - -#include <mach/at91_rtt.h> -#include <mach/cpu.h> -#include <mach/hardware.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/clk.h> /* * This driver uses two configurable hardware resources that live in the @@ -47,6 +46,22 @@ * registers available, likewise usable for more than "RTC" support. */ +#define AT91_RTT_MR 0x00 /* Real-time Mode Register */ +#define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */ +#define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */ +#define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */ +#define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */ + +#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */ +#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */ + +#define AT91_RTT_VR 0x08 /* Real-time Value Register */ +#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */ + +#define AT91_RTT_SR 0x0c /* Real-time Status Register */ +#define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */ +#define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */ + /* * We store ALARM_DISABLED in ALMV to record that no alarm is set. * It's also the reset value for that field. @@ -58,19 +73,30 @@ struct sam9_rtc { void __iomem *rtt; struct rtc_device *rtcdev; u32 imr; - void __iomem *gpbr; + struct regmap *gpbr; + unsigned int gpbr_offset; int irq; + struct clk *sclk; }; #define rtt_readl(rtc, field) \ - __raw_readl((rtc)->rtt + AT91_RTT_ ## field) + readl((rtc)->rtt + AT91_RTT_ ## field) #define rtt_writel(rtc, field, val) \ - __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) + writel((val), (rtc)->rtt + AT91_RTT_ ## field) + +static inline unsigned int gpbr_readl(struct sam9_rtc *rtc) +{ + unsigned int val; + + regmap_read(rtc->gpbr, rtc->gpbr_offset, &val); -#define gpbr_readl(rtc) \ - __raw_readl((rtc)->gpbr) -#define gpbr_writel(rtc, val) \ - __raw_writel((val), (rtc)->gpbr) + return val; +} + +static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val) +{ + regmap_write(rtc->gpbr, rtc->gpbr_offset, val); +} /* * Read current time and date in RTC @@ -287,22 +313,22 @@ static const struct rtc_class_ops at91_rtc_ops = { .alarm_irq_enable = at91_rtc_alarm_irq_enable, }; +static struct regmap_config gpbr_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + /* * Initialize and install RTC driver */ static int at91_rtc_probe(struct platform_device *pdev) { - struct resource *r, *r_gpbr; + struct resource *r; struct sam9_rtc *rtc; int ret, irq; u32 mr; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!r || !r_gpbr) { - dev_err(&pdev->dev, "need 2 ressources\n"); - return -ENODEV; - } + unsigned int sclk_rate; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -321,24 +347,66 @@ static int at91_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, rtc); - rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (!rtc->rtt) { - dev_err(&pdev->dev, "failed to map registers, aborting.\n"); - return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rtc->rtt = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(rtc->rtt)) + return PTR_ERR(rtc->rtt); + + if (!pdev->dev.of_node) { + /* + * TODO: Remove this code chunk when removing non DT board + * support. Remember to remove the gpbr_regmap_config + * variable too. + */ + void __iomem *gpbr; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + gpbr = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(gpbr)) + return PTR_ERR(gpbr); + + rtc->gpbr = regmap_init_mmio(NULL, gpbr, + &gpbr_regmap_config); + } else { + struct of_phandle_args args; + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "atmel,rtt-rtc-time-reg", 1, 0, + &args); + if (ret) + return ret; + + rtc->gpbr = syscon_node_to_regmap(args.np); + rtc->gpbr_offset = args.args[0]; } - rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start, - resource_size(r_gpbr)); - if (!rtc->gpbr) { - dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n"); + if (IS_ERR(rtc->gpbr)) { + dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n"); return -ENOMEM; } + rtc->sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(rtc->sclk)) + return PTR_ERR(rtc->sclk); + + sclk_rate = clk_get_rate(rtc->sclk); + if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) { + dev_err(&pdev->dev, "Invalid slow clock rate\n"); + return -EINVAL; + } + + ret = clk_prepare_enable(rtc->sclk); + if (ret) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return ret; + } + mr = rtt_readl(rtc, MR); /* unless RTT is counting at 1 Hz, re-initialize it */ - if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) { - mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES); + if ((mr & AT91_RTT_RTPRES) != sclk_rate) { + mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES); gpbr_writel(rtc, 0); } @@ -383,6 +451,9 @@ static int at91_rtc_remove(struct platform_device *pdev) /* disable all interrupts */ rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + if (!IS_ERR(rtc->sclk)) + clk_disable_unprepare(rtc->sclk); + return 0; } @@ -440,6 +511,14 @@ static int at91_rtc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); +#ifdef CONFIG_OF +static const struct of_device_id at91_rtc_dt_ids[] = { + { .compatible = "atmel,at91sam9260-rtt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); +#endif + static struct platform_driver at91_rtc_driver = { .probe = at91_rtc_probe, .remove = at91_rtc_remove, @@ -448,6 +527,7 @@ static struct platform_driver at91_rtc_driver = { .name = "rtc-at91sam9", .owner = THIS_MODULE, .pm = &at91_rtc_pm_ops, + .of_match_table = of_match_ptr(at91_rtc_dt_ids), }, }; diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index c4cf0573111..e6bfb9c42a1 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -45,16 +45,20 @@ int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) } EXPORT_SYMBOL(rtc_year_days); + /* + * rtc_time_to_tm64 - Converts time64_t to rtc_time. * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. */ -void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) +void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) { unsigned int month, year; + unsigned long secs; int days; - days = time / 86400; - time -= (unsigned int) days * 86400; + /* time must be positive */ + days = div_s64(time, 86400); + secs = time - (unsigned int) days * 86400; /* day of the week, 1970-01-01 was a Thursday */ tm->tm_wday = (days + 4) % 7; @@ -81,14 +85,14 @@ void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) tm->tm_mon = month; tm->tm_mday = days + 1; - tm->tm_hour = time / 3600; - time -= tm->tm_hour * 3600; - tm->tm_min = time / 60; - tm->tm_sec = time - tm->tm_min * 60; + tm->tm_hour = secs / 3600; + secs -= tm->tm_hour * 3600; + tm->tm_min = secs / 60; + tm->tm_sec = secs - tm->tm_min * 60; tm->tm_isdst = 0; } -EXPORT_SYMBOL(rtc_time_to_tm); +EXPORT_SYMBOL(rtc_time64_to_tm); /* * Does the rtc_time represent a valid date/time? @@ -109,24 +113,22 @@ int rtc_valid_tm(struct rtc_time *tm) EXPORT_SYMBOL(rtc_valid_tm); /* + * rtc_tm_to_time64 - Converts rtc_time to time64_t. * Convert Gregorian date to seconds since 01-01-1970 00:00:00. */ -int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) +time64_t rtc_tm_to_time64(struct rtc_time *tm) { - *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - return 0; } -EXPORT_SYMBOL(rtc_tm_to_time); +EXPORT_SYMBOL(rtc_tm_to_time64); /* * Convert rtc_time to ktime */ ktime_t rtc_tm_to_ktime(struct rtc_time tm) { - time_t time; - rtc_tm_to_time(&tm, &time); - return ktime_set(time, 0); + return ktime_set(rtc_tm_to_time64(&tm), 0); } EXPORT_SYMBOL_GPL(rtc_tm_to_ktime); @@ -135,14 +137,14 @@ EXPORT_SYMBOL_GPL(rtc_tm_to_ktime); */ struct rtc_time rtc_ktime_to_tm(ktime_t kt) { - struct timespec ts; + struct timespec64 ts; struct rtc_time ret; - ts = ktime_to_timespec(kt); + ts = ktime_to_timespec64(kt); /* Round up any ns */ if (ts.tv_nsec) ts.tv_sec++; - rtc_time_to_tm(ts.tv_sec, &ret); + rtc_time64_to_tm(ts.tv_sec, &ret); return ret; } EXPORT_SYMBOL_GPL(rtc_ktime_to_tm); |