rtc: rename core files
authorAlexandre Belloni <alexandre.belloni@bootlin.com>
Mon, 31 Dec 2018 10:36:16 +0000 (11:36 +0100)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Mon, 31 Dec 2018 10:36:16 +0000 (11:36 +0100)
Rename core files so there is a clearer separation between the RTC core and
the RTC drivers.

Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/rtc/Makefile
drivers/rtc/dev.c [new file with mode: 0644]
drivers/rtc/lib.c [new file with mode: 0644]
drivers/rtc/proc.c [new file with mode: 0644]
drivers/rtc/rtc-dev.c [deleted file]
drivers/rtc/rtc-lib.c [deleted file]
drivers/rtc/rtc-proc.c [deleted file]
drivers/rtc/rtc-sysfs.c [deleted file]
drivers/rtc/sysfs.c [new file with mode: 0644]

index f97c05ef99dbe9ecc0465bc634f457920ca7bb22..df022d820bee1abae550cec9aacab668e46f5f76 100644 (file)
@@ -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 (file)
index 0000000..43d962a
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * 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);
+}
diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c
new file mode 100644 (file)
index 0000000..ef160da
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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);
diff --git a/drivers/rtc/proc.c b/drivers/rtc/proc.c
new file mode 100644 (file)
index 0000000..4d74e4f
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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);
+}
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
deleted file mode 100644 (file)
index 43d962a..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * 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);
-}
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
deleted file mode 100644 (file)
index ef160da..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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);
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c
deleted file mode 100644 (file)
index 4d74e4f..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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);
-}
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
deleted file mode 100644 (file)
index a8f22ee..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * 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);
diff --git a/drivers/rtc/sysfs.c b/drivers/rtc/sysfs.c
new file mode 100644 (file)
index 0000000..a8f22ee
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * 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);