From 29a1f599c0cc37004f92ba455d1ccda3db0b6a94 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Thu, 14 Dec 2017 13:31:43 +0800 Subject: rtc: Add tracepoints for RTC system It will be more helpful to add some tracepoints to track RTC actions when debugging RTC driver. Below sample is that we set/read the RTC time, then set 2 alarms, so we can see the trace logs: set/read RTC time: kworker/0:1-67 [000] 21.814245: rtc_set_time: UTC (1510301580) (0) kworker/0:1-67 [000] 21.814312: rtc_read_time: UTC (1510301580) (0) set the first alarm timer: kworker/0:1-67 [000] 21.829238: rtc_timer_enqueue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0 kworker/0:1-67 [000] 22.018279: rtc_set_alarm: UTC (1510301700) (0) set the second alarm timer: kworker/0:1-67 [000] 22.230284: rtc_timer_enqueue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0 the first alarm timer was expired: kworker/0:1-67 [000] 145.155584: rtc_timer_dequeue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0 kworker/0:1-67 [000] 145.155593: rtc_timer_fired: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0 kworker/0:1-67 [000] 145.172504: rtc_set_alarm: UTC (1510301820) (0) the second alarm timer was expired: kworker/0:1-67 [000] 269.102353: rtc_timer_dequeue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0 kworker/0:1-67 [000] 269.102360: rtc_timer_fired: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0 disable alarm irq: kworker/0:1-67 [000] 269.102469: rtc_alarm_irq_enable: disable RTC alarm IRQ (0) Signed-off-by: Baolin Wang Signed-off-by: Alexandre Belloni --- include/trace/events/rtc.h | 206 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 include/trace/events/rtc.h (limited to 'include') diff --git a/include/trace/events/rtc.h b/include/trace/events/rtc.h new file mode 100644 index 000000000000..621333f1c890 --- /dev/null +++ b/include/trace/events/rtc.h @@ -0,0 +1,206 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rtc + +#if !defined(_TRACE_RTC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_RTC_H + +#include +#include + +DECLARE_EVENT_CLASS(rtc_time_alarm_class, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err), + + TP_STRUCT__entry( + __field(time64_t, secs) + __field(int, err) + ), + + TP_fast_assign( + __entry->secs = secs; + __entry->err = err; + ), + + TP_printk("UTC (%lld) (%d)", + __entry->secs, __entry->err + ) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_set_time, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_read_time, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_set_alarm, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_read_alarm, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +TRACE_EVENT(rtc_irq_set_freq, + + TP_PROTO(int freq, int err), + + TP_ARGS(freq, err), + + TP_STRUCT__entry( + __field(int, freq) + __field(int, err) + ), + + TP_fast_assign( + __entry->freq = freq; + __entry->err = err; + ), + + TP_printk("set RTC periodic IRQ frequency:%u (%d)", + __entry->freq, __entry->err + ) +); + +TRACE_EVENT(rtc_irq_set_state, + + TP_PROTO(int enabled, int err), + + TP_ARGS(enabled, err), + + TP_STRUCT__entry( + __field(int, enabled) + __field(int, err) + ), + + TP_fast_assign( + __entry->enabled = enabled; + __entry->err = err; + ), + + TP_printk("%s RTC 2^N Hz periodic IRQs (%d)", + __entry->enabled ? "enable" : "disable", + __entry->err + ) +); + +TRACE_EVENT(rtc_alarm_irq_enable, + + TP_PROTO(unsigned int enabled, int err), + + TP_ARGS(enabled, err), + + TP_STRUCT__entry( + __field(unsigned int, enabled) + __field(int, err) + ), + + TP_fast_assign( + __entry->enabled = enabled; + __entry->err = err; + ), + + TP_printk("%s RTC alarm IRQ (%d)", + __entry->enabled ? "enable" : "disable", + __entry->err + ) +); + +DECLARE_EVENT_CLASS(rtc_offset_class, + + TP_PROTO(long offset, int err), + + TP_ARGS(offset, err), + + TP_STRUCT__entry( + __field(long, offset) + __field(int, err) + ), + + TP_fast_assign( + __entry->offset = offset; + __entry->err = err; + ), + + TP_printk("RTC offset: %ld (%d)", + __entry->offset, __entry->err + ) +); + +DEFINE_EVENT(rtc_offset_class, rtc_set_offset, + + TP_PROTO(long offset, int err), + + TP_ARGS(offset, err) +); + +DEFINE_EVENT(rtc_offset_class, rtc_read_offset, + + TP_PROTO(long offset, int err), + + TP_ARGS(offset, err) +); + +DECLARE_EVENT_CLASS(rtc_timer_class, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer), + + TP_STRUCT__entry( + __field(struct rtc_timer *, timer) + __field(ktime_t, expires) + __field(ktime_t, period) + ), + + TP_fast_assign( + __entry->timer = timer; + __entry->expires = timer->node.expires; + __entry->period = timer->period; + ), + + TP_printk("RTC timer:(%p) expires:%lld period:%lld", + __entry->timer, __entry->expires, __entry->period + ) +); + +DEFINE_EVENT(rtc_timer_class, rtc_timer_enqueue, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer) +); + +DEFINE_EVENT(rtc_timer_class, rtc_timer_dequeue, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer) +); + +DEFINE_EVENT(rtc_timer_class, rtc_timer_fired, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer) +); + +#endif /* _TRACE_RTC_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From fd5cd21d995e67f87b3eb4adf938be85fe83ef4b Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 12 Feb 2018 23:47:19 +0100 Subject: rtc: export rtc_nvmem_register() to drivers Export rtc_nvmem_register() so it can be called from drivers instead of only the core. Signed-off-by: Alexandre Belloni --- drivers/rtc/nvmem.c | 3 +-- drivers/rtc/rtc-core.h | 13 ------------- include/linux/rtc.h | 13 +++++++++++++ 3 files changed, 14 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c index eb8c622cfcf4..17ec4c8d0fad 100644 --- a/drivers/rtc/nvmem.c +++ b/drivers/rtc/nvmem.c @@ -14,8 +14,6 @@ #include #include -#include "rtc-core.h" - /* * Deprecated ABI compatibility, this should be removed at some point */ @@ -105,6 +103,7 @@ int rtc_nvmem_register(struct rtc_device *rtc, return 0; } +EXPORT_SYMBOL_GPL(rtc_nvmem_register); void rtc_nvmem_unregister(struct rtc_device *rtc) { diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h index 05a67837fd76..0abf98983e13 100644 --- a/drivers/rtc/rtc-core.h +++ b/drivers/rtc/rtc-core.h @@ -46,16 +46,3 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void) return NULL; } #endif - -#ifdef CONFIG_RTC_NVMEM -int rtc_nvmem_register(struct rtc_device *rtc, - struct nvmem_config *nvmem_config); -void rtc_nvmem_unregister(struct rtc_device *rtc); -#else -static inline int rtc_nvmem_register(struct rtc_device *rtc, - struct nvmem_config *nvmem_config) -{ - return -ENODEV; -} -static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {} -#endif diff --git a/include/linux/rtc.h b/include/linux/rtc.h index fc6c90b57be0..fbc92fff7c2e 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -271,4 +271,17 @@ extern int rtc_hctosys_ret; #define rtc_hctosys_ret -ENODEV #endif +#ifdef CONFIG_RTC_NVMEM +int rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config); +void rtc_nvmem_unregister(struct rtc_device *rtc); +#else +static inline int rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config) +{ + return -ENODEV; +} +static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {} +#endif + #endif /* _LINUX_RTC_H_ */ -- cgit v1.2.3 From 0391df74a608e4e65c29ddf80e704edfa8f8ef25 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 12 Feb 2018 23:47:34 +0100 Subject: rtc: remove nvmem_config Because nvmem_config is only used and copied at nvmem registration, remove it from struct rtc_device. All the rtc drivers using nvmem are now calling rtc_nvmem_register directly. Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 2 -- include/linux/rtc.h | 1 - 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 0cab397f6e37..5a5ab4fa14f9 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -454,8 +454,6 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc) rtc_proc_add_device(rtc); - rtc_nvmem_register(rtc, rtc->nvmem_config); - rtc->registered = true; dev_info(rtc->dev.parent, "registered as %s\n", dev_name(&rtc->dev)); diff --git a/include/linux/rtc.h b/include/linux/rtc.h index fbc92fff7c2e..37b041f72f8d 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -145,7 +145,6 @@ struct rtc_device { bool registered; - struct nvmem_config *nvmem_config; struct nvmem_device *nvmem; /* Old ABI support */ bool nvram_old_abi; -- cgit v1.2.3 From 9e7002a70e4294a093b3cacf2346af33aeefd265 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 6 Feb 2018 23:12:26 +0100 Subject: char: rtc: remove unused rtc_control() API Since commit 34ce71a96dcb ("ALSA: timer: remove legacy rtctimer"), the rtc_register/rtc_control/rtc_unregister API is unused. As it is highly unlikely to be needed again, remove it. Acked-by: Greg Kroah-Hartman Acked-by: Arnd Bergmann Signed-off-by: Alexandre Belloni --- drivers/char/rtc.c | 83 ----------------------------------------------------- include/linux/rtc.h | 4 --- 2 files changed, 87 deletions(-) (limited to 'include') diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 0c858d027bf3..57dc546628b5 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -809,89 +809,6 @@ static __poll_t rtc_poll(struct file *file, poll_table *wait) } #endif -int rtc_register(rtc_task_t *task) -{ -#ifndef RTC_IRQ - return -EIO; -#else - if (task == NULL || task->func == NULL) - return -EINVAL; - spin_lock_irq(&rtc_lock); - if (rtc_status & RTC_IS_OPEN) { - spin_unlock_irq(&rtc_lock); - return -EBUSY; - } - spin_lock(&rtc_task_lock); - if (rtc_callback) { - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return -EBUSY; - } - rtc_status |= RTC_IS_OPEN; - rtc_callback = task; - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return 0; -#endif -} -EXPORT_SYMBOL(rtc_register); - -int rtc_unregister(rtc_task_t *task) -{ -#ifndef RTC_IRQ - return -EIO; -#else - unsigned char tmp; - - spin_lock_irq(&rtc_lock); - spin_lock(&rtc_task_lock); - if (rtc_callback != task) { - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return -ENXIO; - } - rtc_callback = NULL; - - /* disable controls */ - if (!hpet_mask_rtc_irq_bit(RTC_PIE | RTC_AIE | RTC_UIE)) { - tmp = CMOS_READ(RTC_CONTROL); - tmp &= ~RTC_PIE; - tmp &= ~RTC_AIE; - tmp &= ~RTC_UIE; - CMOS_WRITE(tmp, RTC_CONTROL); - CMOS_READ(RTC_INTR_FLAGS); - } - if (rtc_status & RTC_TIMER_ON) { - rtc_status &= ~RTC_TIMER_ON; - del_timer(&rtc_irq_timer); - } - rtc_status &= ~RTC_IS_OPEN; - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return 0; -#endif -} -EXPORT_SYMBOL(rtc_unregister); - -int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) -{ -#ifndef RTC_IRQ - return -EIO; -#else - unsigned long flags; - if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET) - return -EINVAL; - spin_lock_irqsave(&rtc_task_lock, flags); - if (rtc_callback != task) { - spin_unlock_irqrestore(&rtc_task_lock, flags); - return -ENXIO; - } - spin_unlock_irqrestore(&rtc_task_lock, flags); - return rtc_do_ioctl(cmd, arg, 1); -#endif -} -EXPORT_SYMBOL(rtc_control); - /* * The various file operations we support. */ diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 37b041f72f8d..3b65b201169c 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -211,10 +211,6 @@ void rtc_aie_update_irq(void *private); void rtc_uie_update_irq(void *private); enum hrtimer_restart rtc_pie_update_irq(struct hrtimer *timer); -int rtc_register(rtc_task_t *task); -int rtc_unregister(rtc_task_t *task); -int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); - void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data); int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer, ktime_t expires, ktime_t period); -- cgit v1.2.3 From 71db049e7355f31604e2c04b6cabb71d02bd487d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sat, 17 Feb 2018 14:58:40 +0100 Subject: rtc: Add RTC range Add a way for drivers to inform the core of the supported date/time range. The core can then check whether the date/time or alarm is in the range before calling ->set_time, ->set_mmss or ->set_alarm. It returns -ERANGE when the time is out of range. Signed-off-by: Alexandre Belloni --- Documentation/ABI/testing/sysfs-class-rtc | 8 ++++++++ drivers/rtc/interface.c | 14 ++++++++++++++ drivers/rtc/rtc-sysfs.c | 12 ++++++++++++ include/linux/rtc.h | 3 +++ 4 files changed, 37 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-rtc b/Documentation/ABI/testing/sysfs-class-rtc index 792a38300336..95984289a4ee 100644 --- a/Documentation/ABI/testing/sysfs-class-rtc +++ b/Documentation/ABI/testing/sysfs-class-rtc @@ -43,6 +43,14 @@ Contact: linux-rtc@vger.kernel.org Description: (RO) The name of the RTC corresponding to this sysfs directory +What: /sys/class/rtc/rtcX/range +Date: January 2018 +KernelVersion: 4.16 +Contact: linux-rtc@vger.kernel.org +Description: + Valid time range for the RTC, as seconds from epoch, formatted + as [min, max] + What: /sys/class/rtc/rtcX/since_epoch Date: March 2006 KernelVersion: 2.6.17 diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 7e253be19ba7..c068daebeec2 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -70,6 +70,13 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) if (err != 0) return err; + if (rtc->range_min != rtc->range_max) { + time64_t time = rtc_tm_to_time64(tm); + + if (time < rtc->range_min || time > rtc->range_max) + return -ERANGE; + } + err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; @@ -374,6 +381,13 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) if (err != 0) return err; + if (rtc->range_min != rtc->range_max) { + time64_t time = rtc_tm_to_time64(&alarm->time); + + if (time < rtc->range_min || time > rtc->range_max) + return -ERANGE; + } + err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 92ff2edb86a6..454da38c6012 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -248,6 +248,14 @@ offset_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(offset); +static ssize_t +range_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min, + to_rtc_device(dev)->range_max); +} +static DEVICE_ATTR_RO(range); + static struct attribute *rtc_attrs[] = { &dev_attr_name.attr, &dev_attr_date.attr, @@ -257,6 +265,7 @@ static struct attribute *rtc_attrs[] = { &dev_attr_hctosys.attr, &dev_attr_wakealarm.attr, &dev_attr_offset.attr, + &dev_attr_range.attr, NULL, }; @@ -286,6 +295,9 @@ static umode_t rtc_attr_is_visible(struct kobject *kobj, } else if (attr == &dev_attr_offset.attr) { if (!rtc->ops->set_offset) mode = 0; + } else if (attr == &dev_attr_range.attr) { + if (!(rtc->range_max - rtc->range_min)) + mode = 0; } return mode; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 3b65b201169c..c78528c394e5 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -150,6 +150,9 @@ struct rtc_device { bool nvram_old_abi; struct bin_attribute *nvram; + time64_t range_min; + timeu64_t range_max; + #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL struct work_struct uie_task; struct timer_list uie_timer; -- cgit v1.2.3 From 989515647e783221f9737ed1cf519573d26ce99b Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 8 Jan 2018 14:04:50 +0800 Subject: rtc: Add one offset seconds to expand RTC range From our investigation for all RTC drivers, 1 driver will be expired before year 2017, 7 drivers will be expired before year 2038, 23 drivers will be expired before year 2069, 72 drivers will be expired before 2100 and 104 drivers will be expired before 2106. Especially for these early expired drivers, we need to expand the RTC range to make the RTC can still work after the expired year. So we can expand the RTC range by adding one offset to the time when reading from hardware, and subtracting it when writing back. For example, if you have an RTC that can do 100 years, and currently is configured to be based in Jan 1 1970, so it can represents times from 1970 to 2069. Then if you change the start year from 1970 to 2000, which means it can represents times from 2000 to 2099. By adding or subtracting the offset produced by moving the wrap point, all times between 1970 and 1999 from RTC hardware could get interpreted as times from 2070 to 2099, but the interpretation of dates between 2000 and 2069 would not change. Signed-off-by: Baolin Wang Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/interface.c | 59 ++++++++++++++++++++++++++++++++++++++++- include/linux/rtc.h | 3 +++ 3 files changed, 131 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 5a5ab4fa14f9..d37588f08055 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -211,6 +211,73 @@ static int rtc_device_get_id(struct device *dev) return id; } +static void rtc_device_get_offset(struct rtc_device *rtc) +{ + time64_t range_secs; + u32 start_year; + int ret; + + /* + * If RTC driver did not implement the range of RTC hardware device, + * then we can not expand the RTC range by adding or subtracting one + * offset. + */ + if (rtc->range_min == rtc->range_max) + return; + + ret = device_property_read_u32(rtc->dev.parent, "start-year", + &start_year); + if (!ret) { + rtc->start_secs = mktime64(start_year, 1, 1, 0, 0, 0); + rtc->set_start_time = true; + } + + /* + * If user did not implement the start time for RTC driver, then no + * need to expand the RTC range. + */ + if (!rtc->set_start_time) + return; + + range_secs = rtc->range_max - rtc->range_min + 1; + + /* + * If the start_secs is larger than the maximum seconds (rtc->range_max) + * supported by RTC hardware or the maximum seconds of new expanded + * range (start_secs + rtc->range_max - rtc->range_min) is less than + * rtc->range_min, which means the minimum seconds (rtc->range_min) of + * RTC hardware will be mapped to start_secs by adding one offset, so + * the offset seconds calculation formula should be: + * rtc->offset_secs = rtc->start_secs - rtc->range_min; + * + * If the start_secs is larger than the minimum seconds (rtc->range_min) + * supported by RTC hardware, then there is one region is overlapped + * between the original RTC hardware range and the new expanded range, + * and this overlapped region do not need to be mapped into the new + * expanded range due to it is valid for RTC device. So the minimum + * seconds of RTC hardware (rtc->range_min) should be mapped to + * rtc->range_max + 1, then the offset seconds formula should be: + * rtc->offset_secs = rtc->range_max - rtc->range_min + 1; + * + * If the start_secs is less than the minimum seconds (rtc->range_min), + * which is similar to case 2. So the start_secs should be mapped to + * start_secs + rtc->range_max - rtc->range_min + 1, then the + * offset seconds formula should be: + * rtc->offset_secs = -(rtc->range_max - rtc->range_min + 1); + * + * Otherwise the offset seconds should be 0. + */ + if (rtc->start_secs > rtc->range_max || + rtc->start_secs + range_secs - 1 < rtc->range_min) + rtc->offset_secs = rtc->start_secs - rtc->range_min; + else if (rtc->start_secs > rtc->range_min) + rtc->offset_secs = range_secs; + else if (rtc->start_secs < rtc->range_min) + rtc->offset_secs = -range_secs; + else + rtc->offset_secs = 0; +} + /** * rtc_device_register - register w/ RTC class * @dev: the device to register @@ -247,6 +314,8 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, dev_set_name(&rtc->dev, "rtc%d", id); + rtc_device_get_offset(rtc); + /* Check to see if there is an ALARM already set in hw */ err = __rtc_read_alarm(rtc, &alrm); @@ -436,6 +505,7 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc) return -EINVAL; rtc->owner = owner; + rtc_device_get_offset(rtc); /* Check to see if there is an ALARM already set in hw */ err = __rtc_read_alarm(rtc, &alrm); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 25aebc5b448d..7cbdc9228dd5 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -23,12 +23,61 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); +static void rtc_add_offset(struct rtc_device *rtc, struct rtc_time *tm) +{ + time64_t secs; + + if (!rtc->offset_secs) + return; + + secs = rtc_tm_to_time64(tm); + + /* + * Since the reading time values from RTC device are always in the RTC + * original valid range, but we need to skip the overlapped region + * between expanded range and original range, which is no need to add + * the offset. + */ + if ((rtc->start_secs > rtc->range_min && secs >= rtc->start_secs) || + (rtc->start_secs < rtc->range_min && + secs <= (rtc->start_secs + rtc->range_max - rtc->range_min))) + return; + + rtc_time64_to_tm(secs + rtc->offset_secs, tm); +} + +static void rtc_subtract_offset(struct rtc_device *rtc, struct rtc_time *tm) +{ + time64_t secs; + + if (!rtc->offset_secs) + return; + + secs = rtc_tm_to_time64(tm); + + /* + * If the setting time values are in the valid range of RTC hardware + * device, then no need to subtract the offset when setting time to RTC + * device. Otherwise we need to subtract the offset to make the time + * values are valid for RTC hardware device. + */ + if (secs >= rtc->range_min && secs <= rtc->range_max) + return; + + rtc_time64_to_tm(secs - rtc->offset_secs, tm); +} + static int rtc_valid_range(struct rtc_device *rtc, struct rtc_time *tm) { if (rtc->range_min != rtc->range_max) { time64_t time = rtc_tm_to_time64(tm); + time64_t range_min = rtc->set_start_time ? rtc->start_secs : + rtc->range_min; + time64_t range_max = rtc->set_start_time ? + (rtc->start_secs + rtc->range_max - rtc->range_min) : + rtc->range_max; - if (time < rtc->range_min || time > rtc->range_max) + if (time < range_min || time > range_max) return -ERANGE; } @@ -51,6 +100,8 @@ static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) return err; } + rtc_add_offset(rtc, tm); + err = rtc_valid_tm(tm); if (err < 0) dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n"); @@ -86,6 +137,8 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) if (err) return err; + rtc_subtract_offset(rtc, tm); + err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; @@ -355,6 +408,8 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) err = rtc_valid_tm(&alarm->time); if (err) return err; + + rtc_subtract_offset(rtc, &alarm->time); scheduled = rtc_tm_to_time64(&alarm->time); /* Make sure we're not setting alarms in the past */ @@ -406,6 +461,8 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) err = rtc_timer_enqueue(rtc, &rtc->aie_timer); mutex_unlock(&rtc->ops_lock); + + rtc_add_offset(rtc, &alarm->time); return err; } EXPORT_SYMBOL_GPL(rtc_set_alarm); diff --git a/include/linux/rtc.h b/include/linux/rtc.h index c78528c394e5..82a3038f16ab 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -152,6 +152,9 @@ struct rtc_device { time64_t range_min; timeu64_t range_max; + time64_t start_secs; + time64_t offset_secs; + bool set_start_time; #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL struct work_struct uie_task; -- cgit v1.2.3 From 83bbc5ac63326433755592829caf02920b3d8dc0 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 8 Mar 2018 00:13:52 +0100 Subject: rtc: Add useful timestamp definitions Add commonly used timestamps for range definition. Signed-off-by: Alexandre Belloni --- include/linux/rtc.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 82a3038f16ab..4c007f69082f 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -169,6 +169,11 @@ struct rtc_device { }; #define to_rtc_device(d) container_of(d, struct rtc_device, dev) +/* useful timestamps */ +#define RTC_TIMESTAMP_BEGIN_1900 -2208989361LL /* 1900-01-01 00:00:00 */ +#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */ +#define RTC_TIMESTAMP_END_2099 4102444799LL /* 2099-12-31 23:59:59 */ + extern struct rtc_device *rtc_device_register(const char *name, struct device *dev, const struct rtc_class_ops *ops, -- cgit v1.2.3 From 4a681243cc2d2cea98c6b5e57224f3bcb08bce6c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Sat, 10 Mar 2018 00:27:15 -0600 Subject: rtc: s5m: Move enum from rtc.h to rtc-s5m.c Move this enum to rtc-s5m.c once it is meaningless to others drivers [1]. [1] https://marc.info/?l=linux-rtc&m=152060068925948&w=2 Suggested-by: Krzysztof Kozlowski Signed-off-by: Gustavo A. R. Silva Reviewed-by: Krzysztof Kozlowski Acked-by: Lee Jones Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s5m.c | 11 +++++++++++ include/linux/mfd/samsung/rtc.h | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index 6deae10c14ac..4c363deb79fd 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -38,6 +38,17 @@ */ #define UDR_READ_RETRY_CNT 5 +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, +}; + /* * Registers used by the driver which are different between chipsets. * diff --git a/include/linux/mfd/samsung/rtc.h b/include/linux/mfd/samsung/rtc.h index 48c3c5be7eb1..9ed2871ea335 100644 --- a/include/linux/mfd/samsung/rtc.h +++ b/include/linux/mfd/samsung/rtc.h @@ -141,15 +141,4 @@ enum s2mps_rtc_reg { #define WTSR_ENABLE_SHIFT 6 #define WTSR_ENABLE_MASK (1 << WTSR_ENABLE_SHIFT) -enum { - RTC_SEC = 0, - RTC_MIN, - RTC_HOUR, - RTC_WEEKDAY, - RTC_DATE, - RTC_MONTH, - RTC_YEAR1, - RTC_YEAR2, -}; - #endif /* __LINUX_MFD_SEC_RTC_H */ -- cgit v1.2.3