From 36e14f5fdfdf7cec8887b7ff69cd9bb5051ecf62 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 31 Dec 2018 11:36:16 +0100 Subject: rtc: rename core files Rename core files so there is a clearer separation between the RTC core and the RTC drivers. Signed-off-by: Alexandre Belloni --- drivers/rtc/Makefile | 8 +- drivers/rtc/dev.c | 483 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/lib.c | 148 +++++++++++++++ drivers/rtc/proc.c | 95 ++++++++++ drivers/rtc/rtc-dev.c | 483 ------------------------------------------------ drivers/rtc/rtc-lib.c | 148 --------------- drivers/rtc/rtc-proc.c | 95 ---------- drivers/rtc/rtc-sysfs.c | 358 ----------------------------------- drivers/rtc/sysfs.c | 358 +++++++++++++++++++++++++++++++++++ 9 files changed, 1088 insertions(+), 1088 deletions(-) create mode 100644 drivers/rtc/dev.c create mode 100644 drivers/rtc/lib.c create mode 100644 drivers/rtc/proc.c delete mode 100644 drivers/rtc/rtc-dev.c delete mode 100644 drivers/rtc/rtc-lib.c delete mode 100644 drivers/rtc/rtc-proc.c delete mode 100644 drivers/rtc/rtc-sysfs.c create mode 100644 drivers/rtc/sysfs.c diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index f97c05ef99db..df022d820bee 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -5,7 +5,7 @@ ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG -obj-$(CONFIG_RTC_LIB) += rtc-lib.o +obj-$(CONFIG_RTC_LIB) += lib.o obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o obj-$(CONFIG_RTC_SYSTOHC) += systohc.o obj-$(CONFIG_RTC_CLASS) += rtc-core.o @@ -17,9 +17,9 @@ rtc-core-y += rtc-efi-platform.o endif rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o -rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o -rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o -rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o +rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o +rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o +rtc-core-$(CONFIG_RTC_INTF_SYSFS) += sysfs.o # Keep the list ordered. diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c new file mode 100644 index 000000000000..43d962a9c210 --- /dev/null +++ b/drivers/rtc/dev.c @@ -0,0 +1,483 @@ +/* + * RTC subsystem, dev interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + * + * 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "rtc-core.h" + +static dev_t rtc_devt; + +#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ + +static int rtc_dev_open(struct inode *inode, struct file *file) +{ + struct rtc_device *rtc = container_of(inode->i_cdev, + struct rtc_device, char_dev); + + if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) + return -EBUSY; + + file->private_data = rtc; + + spin_lock_irq(&rtc->irq_lock); + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + + return 0; +} + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL +/* + * Routine to poll RTC seconds field for change as often as possible, + * after first RTC_UIE use timer to reduce polling + */ +static void rtc_uie_task(struct work_struct *work) +{ + struct rtc_device *rtc = + container_of(work, struct rtc_device, uie_task); + struct rtc_time tm; + int num = 0; + int err; + + err = rtc_read_time(rtc, &tm); + + spin_lock_irq(&rtc->irq_lock); + if (rtc->stop_uie_polling || err) { + rtc->uie_task_active = 0; + } else if (rtc->oldsecs != tm.tm_sec) { + num = (tm.tm_sec + 60 - rtc->oldsecs) % 60; + rtc->oldsecs = tm.tm_sec; + rtc->uie_timer.expires = jiffies + HZ - (HZ/10); + rtc->uie_timer_active = 1; + rtc->uie_task_active = 0; + add_timer(&rtc->uie_timer); + } else if (schedule_work(&rtc->uie_task) == 0) { + rtc->uie_task_active = 0; + } + spin_unlock_irq(&rtc->irq_lock); + if (num) + rtc_handle_legacy_irq(rtc, num, RTC_UF); +} +static void rtc_uie_timer(struct timer_list *t) +{ + struct rtc_device *rtc = from_timer(rtc, t, uie_timer); + unsigned long flags; + + spin_lock_irqsave(&rtc->irq_lock, flags); + rtc->uie_timer_active = 0; + rtc->uie_task_active = 1; + if ((schedule_work(&rtc->uie_task) == 0)) + rtc->uie_task_active = 0; + spin_unlock_irqrestore(&rtc->irq_lock, flags); +} + +static int clear_uie(struct rtc_device *rtc) +{ + spin_lock_irq(&rtc->irq_lock); + if (rtc->uie_irq_active) { + rtc->stop_uie_polling = 1; + if (rtc->uie_timer_active) { + spin_unlock_irq(&rtc->irq_lock); + del_timer_sync(&rtc->uie_timer); + spin_lock_irq(&rtc->irq_lock); + rtc->uie_timer_active = 0; + } + if (rtc->uie_task_active) { + spin_unlock_irq(&rtc->irq_lock); + flush_scheduled_work(); + spin_lock_irq(&rtc->irq_lock); + } + rtc->uie_irq_active = 0; + } + spin_unlock_irq(&rtc->irq_lock); + return 0; +} + +static int set_uie(struct rtc_device *rtc) +{ + struct rtc_time tm; + int err; + + err = rtc_read_time(rtc, &tm); + if (err) + return err; + spin_lock_irq(&rtc->irq_lock); + if (!rtc->uie_irq_active) { + rtc->uie_irq_active = 1; + rtc->stop_uie_polling = 0; + rtc->oldsecs = tm.tm_sec; + rtc->uie_task_active = 1; + if (schedule_work(&rtc->uie_task) == 0) + rtc->uie_task_active = 0; + } + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + return 0; +} + +int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) +{ + if (enabled) + return set_uie(rtc); + else + return clear_uie(rtc); +} +EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); + +#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ + +static ssize_t +rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct rtc_device *rtc = file->private_data; + + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t ret; + + if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc->irq_queue, &wait); + do { + __set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irq(&rtc->irq_lock); + data = rtc->irq_data; + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + + if (data != 0) { + ret = 0; + break; + } + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + schedule(); + } while (1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc->irq_queue, &wait); + + if (ret == 0) { + /* Check for any data updates */ + if (rtc->ops->read_callback) + data = rtc->ops->read_callback(rtc->dev.parent, + data); + + if (sizeof(int) != sizeof(long) && + count == sizeof(unsigned int)) + ret = put_user(data, (unsigned int __user *)buf) ?: + sizeof(unsigned int); + else + ret = put_user(data, (unsigned long __user *)buf) ?: + sizeof(unsigned long); + } + return ret; +} + +static __poll_t rtc_dev_poll(struct file *file, poll_table *wait) +{ + struct rtc_device *rtc = file->private_data; + unsigned long data; + + poll_wait(file, &rtc->irq_queue, wait); + + data = rtc->irq_data; + + return (data != 0) ? (EPOLLIN | EPOLLRDNORM) : 0; +} + +static long rtc_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct rtc_device *rtc = file->private_data; + const struct rtc_class_ops *ops = rtc->ops; + struct rtc_time tm; + struct rtc_wkalrm alarm; + void __user *uarg = (void __user *) arg; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + /* check that the calling task has appropriate permissions + * for certain ioctls. doing this check here is useful + * to avoid duplicate code in each driver. + */ + switch (cmd) { + case RTC_EPOCH_SET: + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + err = -EACCES; + break; + + case RTC_IRQP_SET: + if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) + err = -EACCES; + break; + + case RTC_PIE_ON: + if (rtc->irq_freq > rtc->max_user_freq && + !capable(CAP_SYS_RESOURCE)) + err = -EACCES; + break; + } + + if (err) + goto done; + + /* + * Drivers *SHOULD NOT* provide ioctl implementations + * for these requests. Instead, provide methods to + * support the following code, so that the RTC's main + * features are accessible without using ioctls. + * + * RTC and alarm times will be in UTC, by preference, + * but dual-booting with MS-Windows implies RTCs must + * use the local wall clock time. + */ + + switch (cmd) { + case RTC_ALM_READ: + mutex_unlock(&rtc->ops_lock); + + err = rtc_read_alarm(rtc, &alarm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &alarm.time, sizeof(tm))) + err = -EFAULT; + return err; + + case RTC_ALM_SET: + mutex_unlock(&rtc->ops_lock); + + if (copy_from_user(&alarm.time, uarg, sizeof(tm))) + return -EFAULT; + + alarm.enabled = 0; + alarm.pending = 0; + alarm.time.tm_wday = -1; + alarm.time.tm_yday = -1; + alarm.time.tm_isdst = -1; + + /* RTC_ALM_SET alarms may be up to 24 hours in the future. + * Rather than expecting every RTC to implement "don't care" + * for day/month/year fields, just force the alarm to have + * the right values for those fields. + * + * RTC_WKALM_SET should be used instead. Not only does it + * eliminate the need for a separate RTC_AIE_ON call, it + * doesn't have the "alarm 23:59:59 in the future" race. + * + * NOTE: some legacy code may have used invalid fields as + * wildcards, exposing hardware "periodic alarm" capabilities. + * Not supported here. + */ + { + time64_t now, then; + + err = rtc_read_time(rtc, &tm); + if (err < 0) + return err; + now = rtc_tm_to_time64(&tm); + + alarm.time.tm_mday = tm.tm_mday; + alarm.time.tm_mon = tm.tm_mon; + alarm.time.tm_year = tm.tm_year; + err = rtc_valid_tm(&alarm.time); + if (err < 0) + return err; + then = rtc_tm_to_time64(&alarm.time); + + /* alarm may need to wrap into tomorrow */ + if (then < now) { + rtc_time64_to_tm(now + 24 * 60 * 60, &tm); + alarm.time.tm_mday = tm.tm_mday; + alarm.time.tm_mon = tm.tm_mon; + alarm.time.tm_year = tm.tm_year; + } + } + + return rtc_set_alarm(rtc, &alarm); + + case RTC_RD_TIME: + mutex_unlock(&rtc->ops_lock); + + err = rtc_read_time(rtc, &tm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &tm, sizeof(tm))) + err = -EFAULT; + return err; + + case RTC_SET_TIME: + mutex_unlock(&rtc->ops_lock); + + if (copy_from_user(&tm, uarg, sizeof(tm))) + return -EFAULT; + + return rtc_set_time(rtc, &tm); + + case RTC_PIE_ON: + err = rtc_irq_set_state(rtc, 1); + break; + + case RTC_PIE_OFF: + err = rtc_irq_set_state(rtc, 0); + break; + + case RTC_AIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 1); + + case RTC_AIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 0); + + case RTC_UIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 1); + + case RTC_UIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 0); + + case RTC_IRQP_SET: + err = rtc_irq_set_freq(rtc, arg); + break; + + case RTC_IRQP_READ: + err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); + break; + + case RTC_WKALM_SET: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&alarm, uarg, sizeof(alarm))) + return -EFAULT; + + return rtc_set_alarm(rtc, &alarm); + + case RTC_WKALM_RD: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_alarm(rtc, &alarm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &alarm, sizeof(alarm))) + err = -EFAULT; + return err; + + default: + /* Finally try the driver's ioctl interface */ + if (ops->ioctl) { + err = ops->ioctl(rtc->dev.parent, cmd, arg); + if (err == -ENOIOCTLCMD) + err = -ENOTTY; + } else + err = -ENOTTY; + break; + } + +done: + mutex_unlock(&rtc->ops_lock); + return err; +} + +static int rtc_dev_fasync(int fd, struct file *file, int on) +{ + struct rtc_device *rtc = file->private_data; + return fasync_helper(fd, file, on, &rtc->async_queue); +} + +static int rtc_dev_release(struct inode *inode, struct file *file) +{ + struct rtc_device *rtc = file->private_data; + + /* We shut down the repeating IRQs that userspace enabled, + * since nothing is listening to them. + * - Update (UIE) ... currently only managed through ioctls + * - Periodic (PIE) ... also used through rtc_*() interface calls + * + * Leave the alarm alone; it may be set to trigger a system wakeup + * later, or be used by kernel code, and is a one-shot event anyway. + */ + + /* Keep ioctl until all drivers are converted */ + rtc_dev_ioctl(file, RTC_UIE_OFF, 0); + rtc_update_irq_enable(rtc, 0); + rtc_irq_set_state(rtc, 0); + + clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); + return 0; +} + +static const struct file_operations rtc_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = rtc_dev_read, + .poll = rtc_dev_poll, + .unlocked_ioctl = rtc_dev_ioctl, + .open = rtc_dev_open, + .release = rtc_dev_release, + .fasync = rtc_dev_fasync, +}; + +/* insertion/removal hooks */ + +void rtc_dev_prepare(struct rtc_device *rtc) +{ + if (!rtc_devt) + return; + + if (rtc->id >= RTC_DEV_MAX) { + dev_dbg(&rtc->dev, "too many RTC devices\n"); + return; + } + + rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + INIT_WORK(&rtc->uie_task, rtc_uie_task); + timer_setup(&rtc->uie_timer, rtc_uie_timer, 0); +#endif + + cdev_init(&rtc->char_dev, &rtc_dev_fops); + rtc->char_dev.owner = rtc->owner; +} + +void __init rtc_dev_init(void) +{ + int err; + + err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); + if (err < 0) + pr_err("failed to allocate char dev region\n"); +} + +void __exit rtc_dev_exit(void) +{ + if (rtc_devt) + unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); +} diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c new file mode 100644 index 000000000000..ef160da84220 --- /dev/null +++ b/drivers/rtc/lib.c @@ -0,0 +1,148 @@ +/* + * rtc and date/time utility functions + * + * Copyright (C) 2005-06 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c and other bits + * + * 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 +#include + +static const unsigned char rtc_days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static const unsigned short rtc_ydays[2][13] = { + /* Normal years */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) + +/* + * The number of days in the month. + */ +int rtc_month_days(unsigned int month, unsigned int year) +{ + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); +} +EXPORT_SYMBOL(rtc_month_days); + +/* + * The number of days since January 1. (0 to 365) + */ +int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) +{ + return rtc_ydays[is_leap_year(year)][month] + day-1; +} +EXPORT_SYMBOL(rtc_year_days); + + +/* + * rtc_time64_to_tm - Converts time64_t to rtc_time. + * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. + */ +void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) +{ + unsigned int month, year, secs; + int days; + + /* time must be positive */ + days = div_s64_rem(time, 86400, &secs); + + /* day of the week, 1970-01-01 was a Thursday */ + tm->tm_wday = (days + 4) % 7; + + year = 1970 + days / 365; + days -= (year - 1970) * 365 + + LEAPS_THRU_END_OF(year - 1) + - LEAPS_THRU_END_OF(1970 - 1); + while (days < 0) { + year -= 1; + days += 365 + is_leap_year(year); + } + tm->tm_year = year - 1900; + tm->tm_yday = days + 1; + + for (month = 0; month < 11; month++) { + int newdays; + + newdays = days - rtc_month_days(month, year); + if (newdays < 0) + break; + days = newdays; + } + tm->tm_mon = month; + tm->tm_mday = days + 1; + + 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_time64_to_tm); + +/* + * Does the rtc_time represent a valid date/time? + */ +int rtc_valid_tm(struct rtc_time *tm) +{ + if (tm->tm_year < 70 + || ((unsigned)tm->tm_mon) >= 12 + || tm->tm_mday < 1 + || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) + || ((unsigned)tm->tm_hour) >= 24 + || ((unsigned)tm->tm_min) >= 60 + || ((unsigned)tm->tm_sec) >= 60) + return -EINVAL; + + return 0; +} +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. + */ +time64_t rtc_tm_to_time64(struct rtc_time *tm) +{ + return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +} +EXPORT_SYMBOL(rtc_tm_to_time64); + +/* + * Convert rtc_time to ktime + */ +ktime_t rtc_tm_to_ktime(struct rtc_time tm) +{ + return ktime_set(rtc_tm_to_time64(&tm), 0); +} +EXPORT_SYMBOL_GPL(rtc_tm_to_ktime); + +/* + * Convert ktime to rtc_time + */ +struct rtc_time rtc_ktime_to_tm(ktime_t kt) +{ + struct timespec64 ts; + struct rtc_time ret; + + ts = ktime_to_timespec64(kt); + /* Round up any ns */ + if (ts.tv_nsec) + ts.tv_sec++; + rtc_time64_to_tm(ts.tv_sec, &ret); + return ret; +} +EXPORT_SYMBOL_GPL(rtc_ktime_to_tm); diff --git a/drivers/rtc/proc.c b/drivers/rtc/proc.c new file mode 100644 index 000000000000..4d74e4f4ff30 --- /dev/null +++ b/drivers/rtc/proc.c @@ -0,0 +1,95 @@ +/* + * RTC subsystem, proc interface + * + * Copyright (C) 2005-06 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + * + * 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 +#include +#include +#include + +#include "rtc-core.h" + +#define NAME_SIZE 10 + +#if defined(CONFIG_RTC_HCTOSYS_DEVICE) +static bool is_rtc_hctosys(struct rtc_device *rtc) +{ + int size; + char name[NAME_SIZE]; + + size = scnprintf(name, NAME_SIZE, "rtc%d", rtc->id); + if (size > NAME_SIZE) + return false; + + return !strncmp(name, CONFIG_RTC_HCTOSYS_DEVICE, NAME_SIZE); +} +#else +static bool is_rtc_hctosys(struct rtc_device *rtc) +{ + return (rtc->id == 0); +} +#endif + +static int rtc_proc_show(struct seq_file *seq, void *offset) +{ + int err; + struct rtc_device *rtc = seq->private; + const struct rtc_class_ops *ops = rtc->ops; + struct rtc_wkalrm alrm; + struct rtc_time tm; + + err = rtc_read_time(rtc, &tm); + if (err == 0) { + seq_printf(seq, + "rtc_time\t: %ptRt\n" + "rtc_date\t: %ptRd\n", + &tm, &tm); + } + + err = rtc_read_alarm(rtc, &alrm); + if (err == 0) { + seq_printf(seq, "alrm_time\t: %ptRt\n", &alrm.time); + seq_printf(seq, "alrm_date\t: %ptRd\n", &alrm.time); + seq_printf(seq, "alarm_IRQ\t: %s\n", + alrm.enabled ? "yes" : "no"); + seq_printf(seq, "alrm_pending\t: %s\n", + alrm.pending ? "yes" : "no"); + seq_printf(seq, "update IRQ enabled\t: %s\n", + (rtc->uie_rtctimer.enabled) ? "yes" : "no"); + seq_printf(seq, "periodic IRQ enabled\t: %s\n", + (rtc->pie_enabled) ? "yes" : "no"); + seq_printf(seq, "periodic IRQ frequency\t: %d\n", + rtc->irq_freq); + seq_printf(seq, "max user IRQ frequency\t: %d\n", + rtc->max_user_freq); + } + + seq_printf(seq, "24hr\t\t: yes\n"); + + if (ops->proc) + ops->proc(rtc->dev.parent, seq); + + return 0; +} + +void rtc_proc_add_device(struct rtc_device *rtc) +{ + if (is_rtc_hctosys(rtc)) + proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show, + rtc); +} + +void rtc_proc_del_device(struct rtc_device *rtc) +{ + if (is_rtc_hctosys(rtc)) + remove_proc_entry("driver/rtc", NULL); +} diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c deleted file mode 100644 index 43d962a9c210..000000000000 --- a/drivers/rtc/rtc-dev.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * RTC subsystem, dev interface - * - * Copyright (C) 2005 Tower Technologies - * Author: Alessandro Zummo - * - * based on arch/arm/common/rtctime.c - * - * 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. -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include "rtc-core.h" - -static dev_t rtc_devt; - -#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ - -static int rtc_dev_open(struct inode *inode, struct file *file) -{ - struct rtc_device *rtc = container_of(inode->i_cdev, - struct rtc_device, char_dev); - - if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) - return -EBUSY; - - file->private_data = rtc; - - spin_lock_irq(&rtc->irq_lock); - rtc->irq_data = 0; - spin_unlock_irq(&rtc->irq_lock); - - return 0; -} - -#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL -/* - * Routine to poll RTC seconds field for change as often as possible, - * after first RTC_UIE use timer to reduce polling - */ -static void rtc_uie_task(struct work_struct *work) -{ - struct rtc_device *rtc = - container_of(work, struct rtc_device, uie_task); - struct rtc_time tm; - int num = 0; - int err; - - err = rtc_read_time(rtc, &tm); - - spin_lock_irq(&rtc->irq_lock); - if (rtc->stop_uie_polling || err) { - rtc->uie_task_active = 0; - } else if (rtc->oldsecs != tm.tm_sec) { - num = (tm.tm_sec + 60 - rtc->oldsecs) % 60; - rtc->oldsecs = tm.tm_sec; - rtc->uie_timer.expires = jiffies + HZ - (HZ/10); - rtc->uie_timer_active = 1; - rtc->uie_task_active = 0; - add_timer(&rtc->uie_timer); - } else if (schedule_work(&rtc->uie_task) == 0) { - rtc->uie_task_active = 0; - } - spin_unlock_irq(&rtc->irq_lock); - if (num) - rtc_handle_legacy_irq(rtc, num, RTC_UF); -} -static void rtc_uie_timer(struct timer_list *t) -{ - struct rtc_device *rtc = from_timer(rtc, t, uie_timer); - unsigned long flags; - - spin_lock_irqsave(&rtc->irq_lock, flags); - rtc->uie_timer_active = 0; - rtc->uie_task_active = 1; - if ((schedule_work(&rtc->uie_task) == 0)) - rtc->uie_task_active = 0; - spin_unlock_irqrestore(&rtc->irq_lock, flags); -} - -static int clear_uie(struct rtc_device *rtc) -{ - spin_lock_irq(&rtc->irq_lock); - if (rtc->uie_irq_active) { - rtc->stop_uie_polling = 1; - if (rtc->uie_timer_active) { - spin_unlock_irq(&rtc->irq_lock); - del_timer_sync(&rtc->uie_timer); - spin_lock_irq(&rtc->irq_lock); - rtc->uie_timer_active = 0; - } - if (rtc->uie_task_active) { - spin_unlock_irq(&rtc->irq_lock); - flush_scheduled_work(); - spin_lock_irq(&rtc->irq_lock); - } - rtc->uie_irq_active = 0; - } - spin_unlock_irq(&rtc->irq_lock); - return 0; -} - -static int set_uie(struct rtc_device *rtc) -{ - struct rtc_time tm; - int err; - - err = rtc_read_time(rtc, &tm); - if (err) - return err; - spin_lock_irq(&rtc->irq_lock); - if (!rtc->uie_irq_active) { - rtc->uie_irq_active = 1; - rtc->stop_uie_polling = 0; - rtc->oldsecs = tm.tm_sec; - rtc->uie_task_active = 1; - if (schedule_work(&rtc->uie_task) == 0) - rtc->uie_task_active = 0; - } - rtc->irq_data = 0; - spin_unlock_irq(&rtc->irq_lock); - return 0; -} - -int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) -{ - if (enabled) - return set_uie(rtc); - else - return clear_uie(rtc); -} -EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); - -#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ - -static ssize_t -rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct rtc_device *rtc = file->private_data; - - DECLARE_WAITQUEUE(wait, current); - unsigned long data; - ssize_t ret; - - if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) - return -EINVAL; - - add_wait_queue(&rtc->irq_queue, &wait); - do { - __set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irq(&rtc->irq_lock); - data = rtc->irq_data; - rtc->irq_data = 0; - spin_unlock_irq(&rtc->irq_lock); - - if (data != 0) { - ret = 0; - break; - } - if (file->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - schedule(); - } while (1); - set_current_state(TASK_RUNNING); - remove_wait_queue(&rtc->irq_queue, &wait); - - if (ret == 0) { - /* Check for any data updates */ - if (rtc->ops->read_callback) - data = rtc->ops->read_callback(rtc->dev.parent, - data); - - if (sizeof(int) != sizeof(long) && - count == sizeof(unsigned int)) - ret = put_user(data, (unsigned int __user *)buf) ?: - sizeof(unsigned int); - else - ret = put_user(data, (unsigned long __user *)buf) ?: - sizeof(unsigned long); - } - return ret; -} - -static __poll_t rtc_dev_poll(struct file *file, poll_table *wait) -{ - struct rtc_device *rtc = file->private_data; - unsigned long data; - - poll_wait(file, &rtc->irq_queue, wait); - - data = rtc->irq_data; - - return (data != 0) ? (EPOLLIN | EPOLLRDNORM) : 0; -} - -static long rtc_dev_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int err = 0; - struct rtc_device *rtc = file->private_data; - const struct rtc_class_ops *ops = rtc->ops; - struct rtc_time tm; - struct rtc_wkalrm alarm; - void __user *uarg = (void __user *) arg; - - err = mutex_lock_interruptible(&rtc->ops_lock); - if (err) - return err; - - /* check that the calling task has appropriate permissions - * for certain ioctls. doing this check here is useful - * to avoid duplicate code in each driver. - */ - switch (cmd) { - case RTC_EPOCH_SET: - case RTC_SET_TIME: - if (!capable(CAP_SYS_TIME)) - err = -EACCES; - break; - - case RTC_IRQP_SET: - if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) - err = -EACCES; - break; - - case RTC_PIE_ON: - if (rtc->irq_freq > rtc->max_user_freq && - !capable(CAP_SYS_RESOURCE)) - err = -EACCES; - break; - } - - if (err) - goto done; - - /* - * Drivers *SHOULD NOT* provide ioctl implementations - * for these requests. Instead, provide methods to - * support the following code, so that the RTC's main - * features are accessible without using ioctls. - * - * RTC and alarm times will be in UTC, by preference, - * but dual-booting with MS-Windows implies RTCs must - * use the local wall clock time. - */ - - switch (cmd) { - case RTC_ALM_READ: - mutex_unlock(&rtc->ops_lock); - - err = rtc_read_alarm(rtc, &alarm); - if (err < 0) - return err; - - if (copy_to_user(uarg, &alarm.time, sizeof(tm))) - err = -EFAULT; - return err; - - case RTC_ALM_SET: - mutex_unlock(&rtc->ops_lock); - - if (copy_from_user(&alarm.time, uarg, sizeof(tm))) - return -EFAULT; - - alarm.enabled = 0; - alarm.pending = 0; - alarm.time.tm_wday = -1; - alarm.time.tm_yday = -1; - alarm.time.tm_isdst = -1; - - /* RTC_ALM_SET alarms may be up to 24 hours in the future. - * Rather than expecting every RTC to implement "don't care" - * for day/month/year fields, just force the alarm to have - * the right values for those fields. - * - * RTC_WKALM_SET should be used instead. Not only does it - * eliminate the need for a separate RTC_AIE_ON call, it - * doesn't have the "alarm 23:59:59 in the future" race. - * - * NOTE: some legacy code may have used invalid fields as - * wildcards, exposing hardware "periodic alarm" capabilities. - * Not supported here. - */ - { - time64_t now, then; - - err = rtc_read_time(rtc, &tm); - if (err < 0) - return err; - now = rtc_tm_to_time64(&tm); - - alarm.time.tm_mday = tm.tm_mday; - alarm.time.tm_mon = tm.tm_mon; - alarm.time.tm_year = tm.tm_year; - err = rtc_valid_tm(&alarm.time); - if (err < 0) - return err; - then = rtc_tm_to_time64(&alarm.time); - - /* alarm may need to wrap into tomorrow */ - if (then < now) { - rtc_time64_to_tm(now + 24 * 60 * 60, &tm); - alarm.time.tm_mday = tm.tm_mday; - alarm.time.tm_mon = tm.tm_mon; - alarm.time.tm_year = tm.tm_year; - } - } - - return rtc_set_alarm(rtc, &alarm); - - case RTC_RD_TIME: - mutex_unlock(&rtc->ops_lock); - - err = rtc_read_time(rtc, &tm); - if (err < 0) - return err; - - if (copy_to_user(uarg, &tm, sizeof(tm))) - err = -EFAULT; - return err; - - case RTC_SET_TIME: - mutex_unlock(&rtc->ops_lock); - - if (copy_from_user(&tm, uarg, sizeof(tm))) - return -EFAULT; - - return rtc_set_time(rtc, &tm); - - case RTC_PIE_ON: - err = rtc_irq_set_state(rtc, 1); - break; - - case RTC_PIE_OFF: - err = rtc_irq_set_state(rtc, 0); - break; - - case RTC_AIE_ON: - mutex_unlock(&rtc->ops_lock); - return rtc_alarm_irq_enable(rtc, 1); - - case RTC_AIE_OFF: - mutex_unlock(&rtc->ops_lock); - return rtc_alarm_irq_enable(rtc, 0); - - case RTC_UIE_ON: - mutex_unlock(&rtc->ops_lock); - return rtc_update_irq_enable(rtc, 1); - - case RTC_UIE_OFF: - mutex_unlock(&rtc->ops_lock); - return rtc_update_irq_enable(rtc, 0); - - case RTC_IRQP_SET: - err = rtc_irq_set_freq(rtc, arg); - break; - - case RTC_IRQP_READ: - err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); - break; - - case RTC_WKALM_SET: - mutex_unlock(&rtc->ops_lock); - if (copy_from_user(&alarm, uarg, sizeof(alarm))) - return -EFAULT; - - return rtc_set_alarm(rtc, &alarm); - - case RTC_WKALM_RD: - mutex_unlock(&rtc->ops_lock); - err = rtc_read_alarm(rtc, &alarm); - if (err < 0) - return err; - - if (copy_to_user(uarg, &alarm, sizeof(alarm))) - err = -EFAULT; - return err; - - default: - /* Finally try the driver's ioctl interface */ - if (ops->ioctl) { - err = ops->ioctl(rtc->dev.parent, cmd, arg); - if (err == -ENOIOCTLCMD) - err = -ENOTTY; - } else - err = -ENOTTY; - break; - } - -done: - mutex_unlock(&rtc->ops_lock); - return err; -} - -static int rtc_dev_fasync(int fd, struct file *file, int on) -{ - struct rtc_device *rtc = file->private_data; - return fasync_helper(fd, file, on, &rtc->async_queue); -} - -static int rtc_dev_release(struct inode *inode, struct file *file) -{ - struct rtc_device *rtc = file->private_data; - - /* We shut down the repeating IRQs that userspace enabled, - * since nothing is listening to them. - * - Update (UIE) ... currently only managed through ioctls - * - Periodic (PIE) ... also used through rtc_*() interface calls - * - * Leave the alarm alone; it may be set to trigger a system wakeup - * later, or be used by kernel code, and is a one-shot event anyway. - */ - - /* Keep ioctl until all drivers are converted */ - rtc_dev_ioctl(file, RTC_UIE_OFF, 0); - rtc_update_irq_enable(rtc, 0); - rtc_irq_set_state(rtc, 0); - - clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); - return 0; -} - -static const struct file_operations rtc_dev_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = rtc_dev_read, - .poll = rtc_dev_poll, - .unlocked_ioctl = rtc_dev_ioctl, - .open = rtc_dev_open, - .release = rtc_dev_release, - .fasync = rtc_dev_fasync, -}; - -/* insertion/removal hooks */ - -void rtc_dev_prepare(struct rtc_device *rtc) -{ - if (!rtc_devt) - return; - - if (rtc->id >= RTC_DEV_MAX) { - dev_dbg(&rtc->dev, "too many RTC devices\n"); - return; - } - - rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); - -#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL - INIT_WORK(&rtc->uie_task, rtc_uie_task); - timer_setup(&rtc->uie_timer, rtc_uie_timer, 0); -#endif - - cdev_init(&rtc->char_dev, &rtc_dev_fops); - rtc->char_dev.owner = rtc->owner; -} - -void __init rtc_dev_init(void) -{ - int err; - - err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); - if (err < 0) - pr_err("failed to allocate char dev region\n"); -} - -void __exit rtc_dev_exit(void) -{ - if (rtc_devt) - unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); -} diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c deleted file mode 100644 index ef160da84220..000000000000 --- a/drivers/rtc/rtc-lib.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * rtc and date/time utility functions - * - * Copyright (C) 2005-06 Tower Technologies - * Author: Alessandro Zummo - * - * based on arch/arm/common/rtctime.c and other bits - * - * 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 -#include - -static const unsigned char rtc_days_in_month[] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -static const unsigned short rtc_ydays[2][13] = { - /* Normal years */ - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, - /* Leap years */ - { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } -}; - -#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) - -/* - * The number of days in the month. - */ -int rtc_month_days(unsigned int month, unsigned int year) -{ - return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); -} -EXPORT_SYMBOL(rtc_month_days); - -/* - * The number of days since January 1. (0 to 365) - */ -int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) -{ - return rtc_ydays[is_leap_year(year)][month] + day-1; -} -EXPORT_SYMBOL(rtc_year_days); - - -/* - * rtc_time64_to_tm - Converts time64_t to rtc_time. - * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. - */ -void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) -{ - unsigned int month, year, secs; - int days; - - /* time must be positive */ - days = div_s64_rem(time, 86400, &secs); - - /* day of the week, 1970-01-01 was a Thursday */ - tm->tm_wday = (days + 4) % 7; - - year = 1970 + days / 365; - days -= (year - 1970) * 365 - + LEAPS_THRU_END_OF(year - 1) - - LEAPS_THRU_END_OF(1970 - 1); - while (days < 0) { - year -= 1; - days += 365 + is_leap_year(year); - } - tm->tm_year = year - 1900; - tm->tm_yday = days + 1; - - for (month = 0; month < 11; month++) { - int newdays; - - newdays = days - rtc_month_days(month, year); - if (newdays < 0) - break; - days = newdays; - } - tm->tm_mon = month; - tm->tm_mday = days + 1; - - 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_time64_to_tm); - -/* - * Does the rtc_time represent a valid date/time? - */ -int rtc_valid_tm(struct rtc_time *tm) -{ - if (tm->tm_year < 70 - || ((unsigned)tm->tm_mon) >= 12 - || tm->tm_mday < 1 - || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) - || ((unsigned)tm->tm_hour) >= 24 - || ((unsigned)tm->tm_min) >= 60 - || ((unsigned)tm->tm_sec) >= 60) - return -EINVAL; - - return 0; -} -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. - */ -time64_t rtc_tm_to_time64(struct rtc_time *tm) -{ - return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); -} -EXPORT_SYMBOL(rtc_tm_to_time64); - -/* - * Convert rtc_time to ktime - */ -ktime_t rtc_tm_to_ktime(struct rtc_time tm) -{ - return ktime_set(rtc_tm_to_time64(&tm), 0); -} -EXPORT_SYMBOL_GPL(rtc_tm_to_ktime); - -/* - * Convert ktime to rtc_time - */ -struct rtc_time rtc_ktime_to_tm(ktime_t kt) -{ - struct timespec64 ts; - struct rtc_time ret; - - ts = ktime_to_timespec64(kt); - /* Round up any ns */ - if (ts.tv_nsec) - ts.tv_sec++; - rtc_time64_to_tm(ts.tv_sec, &ret); - return ret; -} -EXPORT_SYMBOL_GPL(rtc_ktime_to_tm); diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c deleted file mode 100644 index 4d74e4f4ff30..000000000000 --- a/drivers/rtc/rtc-proc.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * RTC subsystem, proc interface - * - * Copyright (C) 2005-06 Tower Technologies - * Author: Alessandro Zummo - * - * based on arch/arm/common/rtctime.c - * - * 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 -#include -#include -#include - -#include "rtc-core.h" - -#define NAME_SIZE 10 - -#if defined(CONFIG_RTC_HCTOSYS_DEVICE) -static bool is_rtc_hctosys(struct rtc_device *rtc) -{ - int size; - char name[NAME_SIZE]; - - size = scnprintf(name, NAME_SIZE, "rtc%d", rtc->id); - if (size > NAME_SIZE) - return false; - - return !strncmp(name, CONFIG_RTC_HCTOSYS_DEVICE, NAME_SIZE); -} -#else -static bool is_rtc_hctosys(struct rtc_device *rtc) -{ - return (rtc->id == 0); -} -#endif - -static int rtc_proc_show(struct seq_file *seq, void *offset) -{ - int err; - struct rtc_device *rtc = seq->private; - const struct rtc_class_ops *ops = rtc->ops; - struct rtc_wkalrm alrm; - struct rtc_time tm; - - err = rtc_read_time(rtc, &tm); - if (err == 0) { - seq_printf(seq, - "rtc_time\t: %ptRt\n" - "rtc_date\t: %ptRd\n", - &tm, &tm); - } - - err = rtc_read_alarm(rtc, &alrm); - if (err == 0) { - seq_printf(seq, "alrm_time\t: %ptRt\n", &alrm.time); - seq_printf(seq, "alrm_date\t: %ptRd\n", &alrm.time); - seq_printf(seq, "alarm_IRQ\t: %s\n", - alrm.enabled ? "yes" : "no"); - seq_printf(seq, "alrm_pending\t: %s\n", - alrm.pending ? "yes" : "no"); - seq_printf(seq, "update IRQ enabled\t: %s\n", - (rtc->uie_rtctimer.enabled) ? "yes" : "no"); - seq_printf(seq, "periodic IRQ enabled\t: %s\n", - (rtc->pie_enabled) ? "yes" : "no"); - seq_printf(seq, "periodic IRQ frequency\t: %d\n", - rtc->irq_freq); - seq_printf(seq, "max user IRQ frequency\t: %d\n", - rtc->max_user_freq); - } - - seq_printf(seq, "24hr\t\t: yes\n"); - - if (ops->proc) - ops->proc(rtc->dev.parent, seq); - - return 0; -} - -void rtc_proc_add_device(struct rtc_device *rtc) -{ - if (is_rtc_hctosys(rtc)) - proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show, - rtc); -} - -void rtc_proc_del_device(struct rtc_device *rtc) -{ - if (is_rtc_hctosys(rtc)) - remove_proc_entry("driver/rtc", NULL); -} diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c deleted file mode 100644 index a8f22ee726bb..000000000000 --- a/drivers/rtc/rtc-sysfs.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * RTC subsystem, sysfs interface - * - * Copyright (C) 2005 Tower Technologies - * Author: Alessandro Zummo - * - * 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 -#include - -#include "rtc-core.h" - - -/* device attributes */ - -/* - * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's - * ideally UTC. However, PCs that also boot to MS-Windows normally use - * the local time and change to match daylight savings time. That affects - * attributes including date, time, since_epoch, and wakealarm. - */ - -static ssize_t -name_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent), - dev_name(dev->parent)); -} -static DEVICE_ATTR_RO(name); - -static ssize_t -date_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ssize_t retval; - struct rtc_time tm; - - retval = rtc_read_time(to_rtc_device(dev), &tm); - if (retval) - return retval; - - return sprintf(buf, "%ptRd\n", &tm); -} -static DEVICE_ATTR_RO(date); - -static ssize_t -time_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ssize_t retval; - struct rtc_time tm; - - retval = rtc_read_time(to_rtc_device(dev), &tm); - if (retval) - return retval; - - return sprintf(buf, "%ptRt\n", &tm); -} -static DEVICE_ATTR_RO(time); - -static ssize_t -since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ssize_t retval; - struct rtc_time tm; - - retval = rtc_read_time(to_rtc_device(dev), &tm); - if (retval == 0) { - time64_t time; - - time = rtc_tm_to_time64(&tm); - retval = sprintf(buf, "%lld\n", time); - } - - return retval; -} -static DEVICE_ATTR_RO(since_epoch); - -static ssize_t -max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq); -} - -static ssize_t -max_user_freq_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t n) -{ - struct rtc_device *rtc = to_rtc_device(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 0, &val); - if (err) - return err; - - if (val >= 4096 || val == 0) - return -EINVAL; - - rtc->max_user_freq = (int)val; - - return n; -} -static DEVICE_ATTR_RW(max_user_freq); - -/** - * rtc_sysfs_show_hctosys - indicate if the given RTC set the system time - * - * Returns 1 if the system clock was set by this RTC at the last - * boot or resume event. - */ -static ssize_t -hctosys_show(struct device *dev, struct device_attribute *attr, char *buf) -{ -#ifdef CONFIG_RTC_HCTOSYS_DEVICE - if (rtc_hctosys_ret == 0 && - strcmp(dev_name(&to_rtc_device(dev)->dev), - CONFIG_RTC_HCTOSYS_DEVICE) == 0) - return sprintf(buf, "1\n"); - else -#endif - return sprintf(buf, "0\n"); -} -static DEVICE_ATTR_RO(hctosys); - -static ssize_t -wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ssize_t retval; - time64_t alarm; - struct rtc_wkalrm alm; - - /* Don't show disabled alarms. For uniformity, RTC alarms are - * conceptually one-shot, even though some common RTCs (on PCs) - * don't actually work that way. - * - * NOTE: RTC implementations where the alarm doesn't match an - * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC - * alarms after they trigger, to ensure one-shot semantics. - */ - retval = rtc_read_alarm(to_rtc_device(dev), &alm); - if (retval == 0 && alm.enabled) { - alarm = rtc_tm_to_time64(&alm.time); - retval = sprintf(buf, "%lld\n", alarm); - } - - return retval; -} - -static ssize_t -wakealarm_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t n) -{ - ssize_t retval; - time64_t now, alarm; - time64_t push = 0; - struct rtc_wkalrm alm; - struct rtc_device *rtc = to_rtc_device(dev); - const char *buf_ptr; - int adjust = 0; - - /* Only request alarms that trigger in the future. Disable them - * by writing another time, e.g. 0 meaning Jan 1 1970 UTC. - */ - retval = rtc_read_time(rtc, &alm.time); - if (retval < 0) - return retval; - now = rtc_tm_to_time64(&alm.time); - - buf_ptr = buf; - if (*buf_ptr == '+') { - buf_ptr++; - if (*buf_ptr == '=') { - buf_ptr++; - push = 1; - } else - adjust = 1; - } - retval = kstrtos64(buf_ptr, 0, &alarm); - if (retval) - return retval; - if (adjust) { - alarm += now; - } - if (alarm > now || push) { - /* Avoid accidentally clobbering active alarms; we can't - * entirely prevent that here, without even the minimal - * locking from the /dev/rtcN api. - */ - retval = rtc_read_alarm(rtc, &alm); - if (retval < 0) - return retval; - if (alm.enabled) { - if (push) { - push = rtc_tm_to_time64(&alm.time); - alarm += push; - } else - return -EBUSY; - } else if (push) - return -EINVAL; - alm.enabled = 1; - } else { - alm.enabled = 0; - - /* Provide a valid future alarm time. Linux isn't EFI, - * this time won't be ignored when disabling the alarm. - */ - alarm = now + 300; - } - rtc_time64_to_tm(alarm, &alm.time); - - retval = rtc_set_alarm(rtc, &alm); - return (retval < 0) ? retval : n; -} -static DEVICE_ATTR_RW(wakealarm); - -static ssize_t -offset_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - ssize_t retval; - long offset; - - retval = rtc_read_offset(to_rtc_device(dev), &offset); - if (retval == 0) - retval = sprintf(buf, "%ld\n", offset); - - return retval; -} - -static ssize_t -offset_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t n) -{ - ssize_t retval; - long offset; - - retval = kstrtol(buf, 10, &offset); - if (retval == 0) - retval = rtc_set_offset(to_rtc_device(dev), offset); - - return (retval < 0) ? retval : n; -} -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, - &dev_attr_time.attr, - &dev_attr_since_epoch.attr, - &dev_attr_max_user_freq.attr, - &dev_attr_hctosys.attr, - &dev_attr_wakealarm.attr, - &dev_attr_offset.attr, - &dev_attr_range.attr, - NULL, -}; - -/* The reason to trigger an alarm with no process watching it (via sysfs) - * is its side effect: waking from a system state like suspend-to-RAM or - * suspend-to-disk. So: no attribute unless that side effect is possible. - * (Userspace may disable that mechanism later.) - */ -static bool rtc_does_wakealarm(struct rtc_device *rtc) -{ - if (!device_can_wakeup(rtc->dev.parent)) - return false; - - return rtc->ops->set_alarm != NULL; -} - -static umode_t rtc_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct rtc_device *rtc = to_rtc_device(dev); - umode_t mode = attr->mode; - - if (attr == &dev_attr_wakealarm.attr) { - if (!rtc_does_wakealarm(rtc)) - mode = 0; - } 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; -} - -static struct attribute_group rtc_attr_group = { - .is_visible = rtc_attr_is_visible, - .attrs = rtc_attrs, -}; - -static const struct attribute_group *rtc_attr_groups[] = { - &rtc_attr_group, - NULL -}; - -const struct attribute_group **rtc_get_dev_attribute_groups(void) -{ - return rtc_attr_groups; -} - -int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps) -{ - size_t old_cnt = 0, add_cnt = 0, new_cnt; - const struct attribute_group **groups, **old; - - if (rtc->registered) - return -EINVAL; - if (!grps) - return -EINVAL; - - groups = rtc->dev.groups; - if (groups) - for (; *groups; groups++) - old_cnt++; - - for (groups = grps; *groups; groups++) - add_cnt++; - - new_cnt = old_cnt + add_cnt + 1; - groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL); - if (!groups) - return -ENOMEM; - memcpy(groups, rtc->dev.groups, old_cnt * sizeof(*groups)); - memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups)); - groups[old_cnt + add_cnt] = NULL; - - old = rtc->dev.groups; - rtc->dev.groups = groups; - if (old && old != rtc_attr_groups) - devm_kfree(&rtc->dev, old); - - return 0; -} -EXPORT_SYMBOL(rtc_add_groups); - -int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp) -{ - const struct attribute_group *groups[] = { grp, NULL }; - - return rtc_add_groups(rtc, groups); -} -EXPORT_SYMBOL(rtc_add_group); diff --git a/drivers/rtc/sysfs.c b/drivers/rtc/sysfs.c new file mode 100644 index 000000000000..a8f22ee726bb --- /dev/null +++ b/drivers/rtc/sysfs.c @@ -0,0 +1,358 @@ +/* + * RTC subsystem, sysfs interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 +#include + +#include "rtc-core.h" + + +/* device attributes */ + +/* + * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's + * ideally UTC. However, PCs that also boot to MS-Windows normally use + * the local time and change to match daylight savings time. That affects + * attributes including date, time, since_epoch, and wakealarm. + */ + +static ssize_t +name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent), + dev_name(dev->parent)); +} +static DEVICE_ATTR_RO(name); + +static ssize_t +date_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(to_rtc_device(dev), &tm); + if (retval) + return retval; + + return sprintf(buf, "%ptRd\n", &tm); +} +static DEVICE_ATTR_RO(date); + +static ssize_t +time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(to_rtc_device(dev), &tm); + if (retval) + return retval; + + return sprintf(buf, "%ptRt\n", &tm); +} +static DEVICE_ATTR_RO(time); + +static ssize_t +since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(to_rtc_device(dev), &tm); + if (retval == 0) { + time64_t time; + + time = rtc_tm_to_time64(&tm); + retval = sprintf(buf, "%lld\n", time); + } + + return retval; +} +static DEVICE_ATTR_RO(since_epoch); + +static ssize_t +max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq); +} + +static ssize_t +max_user_freq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct rtc_device *rtc = to_rtc_device(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + if (val >= 4096 || val == 0) + return -EINVAL; + + rtc->max_user_freq = (int)val; + + return n; +} +static DEVICE_ATTR_RW(max_user_freq); + +/** + * rtc_sysfs_show_hctosys - indicate if the given RTC set the system time + * + * Returns 1 if the system clock was set by this RTC at the last + * boot or resume event. + */ +static ssize_t +hctosys_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +#ifdef CONFIG_RTC_HCTOSYS_DEVICE + if (rtc_hctosys_ret == 0 && + strcmp(dev_name(&to_rtc_device(dev)->dev), + CONFIG_RTC_HCTOSYS_DEVICE) == 0) + return sprintf(buf, "1\n"); + else +#endif + return sprintf(buf, "0\n"); +} +static DEVICE_ATTR_RO(hctosys); + +static ssize_t +wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + time64_t alarm; + struct rtc_wkalrm alm; + + /* Don't show disabled alarms. For uniformity, RTC alarms are + * conceptually one-shot, even though some common RTCs (on PCs) + * don't actually work that way. + * + * NOTE: RTC implementations where the alarm doesn't match an + * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC + * alarms after they trigger, to ensure one-shot semantics. + */ + retval = rtc_read_alarm(to_rtc_device(dev), &alm); + if (retval == 0 && alm.enabled) { + alarm = rtc_tm_to_time64(&alm.time); + retval = sprintf(buf, "%lld\n", alarm); + } + + return retval; +} + +static ssize_t +wakealarm_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + ssize_t retval; + time64_t now, alarm; + time64_t push = 0; + struct rtc_wkalrm alm; + struct rtc_device *rtc = to_rtc_device(dev); + const char *buf_ptr; + int adjust = 0; + + /* Only request alarms that trigger in the future. Disable them + * by writing another time, e.g. 0 meaning Jan 1 1970 UTC. + */ + retval = rtc_read_time(rtc, &alm.time); + if (retval < 0) + return retval; + now = rtc_tm_to_time64(&alm.time); + + buf_ptr = buf; + if (*buf_ptr == '+') { + buf_ptr++; + if (*buf_ptr == '=') { + buf_ptr++; + push = 1; + } else + adjust = 1; + } + retval = kstrtos64(buf_ptr, 0, &alarm); + if (retval) + return retval; + if (adjust) { + alarm += now; + } + if (alarm > now || push) { + /* Avoid accidentally clobbering active alarms; we can't + * entirely prevent that here, without even the minimal + * locking from the /dev/rtcN api. + */ + retval = rtc_read_alarm(rtc, &alm); + if (retval < 0) + return retval; + if (alm.enabled) { + if (push) { + push = rtc_tm_to_time64(&alm.time); + alarm += push; + } else + return -EBUSY; + } else if (push) + return -EINVAL; + alm.enabled = 1; + } else { + alm.enabled = 0; + + /* Provide a valid future alarm time. Linux isn't EFI, + * this time won't be ignored when disabling the alarm. + */ + alarm = now + 300; + } + rtc_time64_to_tm(alarm, &alm.time); + + retval = rtc_set_alarm(rtc, &alm); + return (retval < 0) ? retval : n; +} +static DEVICE_ATTR_RW(wakealarm); + +static ssize_t +offset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + long offset; + + retval = rtc_read_offset(to_rtc_device(dev), &offset); + if (retval == 0) + retval = sprintf(buf, "%ld\n", offset); + + return retval; +} + +static ssize_t +offset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + ssize_t retval; + long offset; + + retval = kstrtol(buf, 10, &offset); + if (retval == 0) + retval = rtc_set_offset(to_rtc_device(dev), offset); + + return (retval < 0) ? retval : n; +} +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, + &dev_attr_time.attr, + &dev_attr_since_epoch.attr, + &dev_attr_max_user_freq.attr, + &dev_attr_hctosys.attr, + &dev_attr_wakealarm.attr, + &dev_attr_offset.attr, + &dev_attr_range.attr, + NULL, +}; + +/* The reason to trigger an alarm with no process watching it (via sysfs) + * is its side effect: waking from a system state like suspend-to-RAM or + * suspend-to-disk. So: no attribute unless that side effect is possible. + * (Userspace may disable that mechanism later.) + */ +static bool rtc_does_wakealarm(struct rtc_device *rtc) +{ + if (!device_can_wakeup(rtc->dev.parent)) + return false; + + return rtc->ops->set_alarm != NULL; +} + +static umode_t rtc_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct rtc_device *rtc = to_rtc_device(dev); + umode_t mode = attr->mode; + + if (attr == &dev_attr_wakealarm.attr) { + if (!rtc_does_wakealarm(rtc)) + mode = 0; + } 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; +} + +static struct attribute_group rtc_attr_group = { + .is_visible = rtc_attr_is_visible, + .attrs = rtc_attrs, +}; + +static const struct attribute_group *rtc_attr_groups[] = { + &rtc_attr_group, + NULL +}; + +const struct attribute_group **rtc_get_dev_attribute_groups(void) +{ + return rtc_attr_groups; +} + +int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps) +{ + size_t old_cnt = 0, add_cnt = 0, new_cnt; + const struct attribute_group **groups, **old; + + if (rtc->registered) + return -EINVAL; + if (!grps) + return -EINVAL; + + groups = rtc->dev.groups; + if (groups) + for (; *groups; groups++) + old_cnt++; + + for (groups = grps; *groups; groups++) + add_cnt++; + + new_cnt = old_cnt + add_cnt + 1; + groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL); + if (!groups) + return -ENOMEM; + memcpy(groups, rtc->dev.groups, old_cnt * sizeof(*groups)); + memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups)); + groups[old_cnt + add_cnt] = NULL; + + old = rtc->dev.groups; + rtc->dev.groups = groups; + if (old && old != rtc_attr_groups) + devm_kfree(&rtc->dev, old); + + return 0; +} +EXPORT_SYMBOL(rtc_add_groups); + +int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp) +{ + const struct attribute_group *groups[] = { grp, NULL }; + + return rtc_add_groups(rtc, groups); +} +EXPORT_SYMBOL(rtc_add_group); -- cgit