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
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.
--- /dev/null
+/*
+ * RTC subsystem, dev interface
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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 <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/sched/signal.h>
+#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);
+}
--- /dev/null
+/*
+ * rtc and date/time utility functions
+ *
+ * Copyright (C) 2005-06 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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 <linux/export.h>
+#include <linux/rtc.h>
+
+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);
--- /dev/null
+/*
+ * RTC subsystem, proc interface
+ *
+ * Copyright (C) 2005-06 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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 <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#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);
+}
+++ /dev/null
-/*
- * RTC subsystem, dev interface
- *
- * Copyright (C) 2005 Tower Technologies
- * Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * 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 <linux/module.h>
-#include <linux/rtc.h>
-#include <linux/sched/signal.h>
-#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);
-}
+++ /dev/null
-/*
- * rtc and date/time utility functions
- *
- * Copyright (C) 2005-06 Tower Technologies
- * Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * 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 <linux/export.h>
-#include <linux/rtc.h>
-
-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);
+++ /dev/null
-/*
- * RTC subsystem, proc interface
- *
- * Copyright (C) 2005-06 Tower Technologies
- * Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * 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 <linux/module.h>
-#include <linux/rtc.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-
-#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);
-}
+++ /dev/null
-/*
- * RTC subsystem, sysfs interface
- *
- * Copyright (C) 2005 Tower Technologies
- * Author: Alessandro Zummo <a.zummo@towertech.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/rtc.h>
-
-#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);
--- /dev/null
+/*
+ * RTC subsystem, sysfs interface
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#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);