162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * RTC subsystem, dev interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005 Tower Technologies 662306a36Sopenharmony_ci * Author: Alessandro Zummo <a.zummo@towertech.it> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * based on arch/arm/common/rtctime.c 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/compat.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/rtc.h> 1662306a36Sopenharmony_ci#include <linux/sched/signal.h> 1762306a36Sopenharmony_ci#include "rtc-core.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic dev_t rtc_devt; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int rtc_dev_open(struct inode *inode, struct file *file) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct rtc_device *rtc = container_of(inode->i_cdev, 2662306a36Sopenharmony_ci struct rtc_device, char_dev); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) 2962306a36Sopenharmony_ci return -EBUSY; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci file->private_data = rtc; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 3462306a36Sopenharmony_ci rtc->irq_data = 0; 3562306a36Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Routine to poll RTC seconds field for change as often as possible, 4362306a36Sopenharmony_ci * after first RTC_UIE use timer to reduce polling 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistatic void rtc_uie_task(struct work_struct *work) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct rtc_device *rtc = 4862306a36Sopenharmony_ci container_of(work, struct rtc_device, uie_task); 4962306a36Sopenharmony_ci struct rtc_time tm; 5062306a36Sopenharmony_ci int num = 0; 5162306a36Sopenharmony_ci int err; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci err = rtc_read_time(rtc, &tm); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 5662306a36Sopenharmony_ci if (rtc->stop_uie_polling || err) { 5762306a36Sopenharmony_ci rtc->uie_task_active = 0; 5862306a36Sopenharmony_ci } else if (rtc->oldsecs != tm.tm_sec) { 5962306a36Sopenharmony_ci num = (tm.tm_sec + 60 - rtc->oldsecs) % 60; 6062306a36Sopenharmony_ci rtc->oldsecs = tm.tm_sec; 6162306a36Sopenharmony_ci rtc->uie_timer.expires = jiffies + HZ - (HZ / 10); 6262306a36Sopenharmony_ci rtc->uie_timer_active = 1; 6362306a36Sopenharmony_ci rtc->uie_task_active = 0; 6462306a36Sopenharmony_ci add_timer(&rtc->uie_timer); 6562306a36Sopenharmony_ci } else if (schedule_work(&rtc->uie_task) == 0) { 6662306a36Sopenharmony_ci rtc->uie_task_active = 0; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 6962306a36Sopenharmony_ci if (num) 7062306a36Sopenharmony_ci rtc_handle_legacy_irq(rtc, num, RTC_UF); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void rtc_uie_timer(struct timer_list *t) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct rtc_device *rtc = from_timer(rtc, t, uie_timer); 7662306a36Sopenharmony_ci unsigned long flags; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci spin_lock_irqsave(&rtc->irq_lock, flags); 7962306a36Sopenharmony_ci rtc->uie_timer_active = 0; 8062306a36Sopenharmony_ci rtc->uie_task_active = 1; 8162306a36Sopenharmony_ci if ((schedule_work(&rtc->uie_task) == 0)) 8262306a36Sopenharmony_ci rtc->uie_task_active = 0; 8362306a36Sopenharmony_ci spin_unlock_irqrestore(&rtc->irq_lock, flags); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int clear_uie(struct rtc_device *rtc) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 8962306a36Sopenharmony_ci if (rtc->uie_irq_active) { 9062306a36Sopenharmony_ci rtc->stop_uie_polling = 1; 9162306a36Sopenharmony_ci if (rtc->uie_timer_active) { 9262306a36Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 9362306a36Sopenharmony_ci del_timer_sync(&rtc->uie_timer); 9462306a36Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 9562306a36Sopenharmony_ci rtc->uie_timer_active = 0; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci if (rtc->uie_task_active) { 9862306a36Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 9962306a36Sopenharmony_ci flush_work(&rtc->uie_task); 10062306a36Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci rtc->uie_irq_active = 0; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int set_uie(struct rtc_device *rtc) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct rtc_time tm; 11162306a36Sopenharmony_ci int err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci err = rtc_read_time(rtc, &tm); 11462306a36Sopenharmony_ci if (err) 11562306a36Sopenharmony_ci return err; 11662306a36Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 11762306a36Sopenharmony_ci if (!rtc->uie_irq_active) { 11862306a36Sopenharmony_ci rtc->uie_irq_active = 1; 11962306a36Sopenharmony_ci rtc->stop_uie_polling = 0; 12062306a36Sopenharmony_ci rtc->oldsecs = tm.tm_sec; 12162306a36Sopenharmony_ci rtc->uie_task_active = 1; 12262306a36Sopenharmony_ci if (schedule_work(&rtc->uie_task) == 0) 12362306a36Sopenharmony_ci rtc->uie_task_active = 0; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci rtc->irq_data = 0; 12662306a36Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciint rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci if (enabled) 13362306a36Sopenharmony_ci return set_uie(rtc); 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci return clear_uie(rtc); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ciEXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic ssize_t 14262306a36Sopenharmony_cirtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct rtc_device *rtc = file->private_data; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 14762306a36Sopenharmony_ci unsigned long data; 14862306a36Sopenharmony_ci ssize_t ret; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci add_wait_queue(&rtc->irq_queue, &wait); 15462306a36Sopenharmony_ci do { 15562306a36Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 15862306a36Sopenharmony_ci data = rtc->irq_data; 15962306a36Sopenharmony_ci rtc->irq_data = 0; 16062306a36Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (data != 0) { 16362306a36Sopenharmony_ci ret = 0; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 16762306a36Sopenharmony_ci ret = -EAGAIN; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci if (signal_pending(current)) { 17162306a36Sopenharmony_ci ret = -ERESTARTSYS; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci schedule(); 17562306a36Sopenharmony_ci } while (1); 17662306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 17762306a36Sopenharmony_ci remove_wait_queue(&rtc->irq_queue, &wait); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (ret == 0) { 18062306a36Sopenharmony_ci if (sizeof(int) != sizeof(long) && 18162306a36Sopenharmony_ci count == sizeof(unsigned int)) 18262306a36Sopenharmony_ci ret = put_user(data, (unsigned int __user *)buf) ?: 18362306a36Sopenharmony_ci sizeof(unsigned int); 18462306a36Sopenharmony_ci else 18562306a36Sopenharmony_ci ret = put_user(data, (unsigned long __user *)buf) ?: 18662306a36Sopenharmony_ci sizeof(unsigned long); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic __poll_t rtc_dev_poll(struct file *file, poll_table *wait) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct rtc_device *rtc = file->private_data; 19462306a36Sopenharmony_ci unsigned long data; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci poll_wait(file, &rtc->irq_queue, wait); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci data = rtc->irq_data; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return (data != 0) ? (EPOLLIN | EPOLLRDNORM) : 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic long rtc_dev_ioctl(struct file *file, 20462306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int err = 0; 20762306a36Sopenharmony_ci struct rtc_device *rtc = file->private_data; 20862306a36Sopenharmony_ci const struct rtc_class_ops *ops = rtc->ops; 20962306a36Sopenharmony_ci struct rtc_time tm; 21062306a36Sopenharmony_ci struct rtc_wkalrm alarm; 21162306a36Sopenharmony_ci struct rtc_param param; 21262306a36Sopenharmony_ci void __user *uarg = (void __user *)arg; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci err = mutex_lock_interruptible(&rtc->ops_lock); 21562306a36Sopenharmony_ci if (err) 21662306a36Sopenharmony_ci return err; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* check that the calling task has appropriate permissions 21962306a36Sopenharmony_ci * for certain ioctls. doing this check here is useful 22062306a36Sopenharmony_ci * to avoid duplicate code in each driver. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci switch (cmd) { 22362306a36Sopenharmony_ci case RTC_EPOCH_SET: 22462306a36Sopenharmony_ci case RTC_SET_TIME: 22562306a36Sopenharmony_ci case RTC_PARAM_SET: 22662306a36Sopenharmony_ci if (!capable(CAP_SYS_TIME)) 22762306a36Sopenharmony_ci err = -EACCES; 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci case RTC_IRQP_SET: 23162306a36Sopenharmony_ci if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) 23262306a36Sopenharmony_ci err = -EACCES; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci case RTC_PIE_ON: 23662306a36Sopenharmony_ci if (rtc->irq_freq > rtc->max_user_freq && 23762306a36Sopenharmony_ci !capable(CAP_SYS_RESOURCE)) 23862306a36Sopenharmony_ci err = -EACCES; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (err) 24362306a36Sopenharmony_ci goto done; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * Drivers *SHOULD NOT* provide ioctl implementations 24762306a36Sopenharmony_ci * for these requests. Instead, provide methods to 24862306a36Sopenharmony_ci * support the following code, so that the RTC's main 24962306a36Sopenharmony_ci * features are accessible without using ioctls. 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * RTC and alarm times will be in UTC, by preference, 25262306a36Sopenharmony_ci * but dual-booting with MS-Windows implies RTCs must 25362306a36Sopenharmony_ci * use the local wall clock time. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci switch (cmd) { 25762306a36Sopenharmony_ci case RTC_ALM_READ: 25862306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci err = rtc_read_alarm(rtc, &alarm); 26162306a36Sopenharmony_ci if (err < 0) 26262306a36Sopenharmony_ci return err; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (copy_to_user(uarg, &alarm.time, sizeof(tm))) 26562306a36Sopenharmony_ci err = -EFAULT; 26662306a36Sopenharmony_ci return err; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci case RTC_ALM_SET: 26962306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (copy_from_user(&alarm.time, uarg, sizeof(tm))) 27262306a36Sopenharmony_ci return -EFAULT; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci alarm.enabled = 0; 27562306a36Sopenharmony_ci alarm.pending = 0; 27662306a36Sopenharmony_ci alarm.time.tm_wday = -1; 27762306a36Sopenharmony_ci alarm.time.tm_yday = -1; 27862306a36Sopenharmony_ci alarm.time.tm_isdst = -1; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* RTC_ALM_SET alarms may be up to 24 hours in the future. 28162306a36Sopenharmony_ci * Rather than expecting every RTC to implement "don't care" 28262306a36Sopenharmony_ci * for day/month/year fields, just force the alarm to have 28362306a36Sopenharmony_ci * the right values for those fields. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * RTC_WKALM_SET should be used instead. Not only does it 28662306a36Sopenharmony_ci * eliminate the need for a separate RTC_AIE_ON call, it 28762306a36Sopenharmony_ci * doesn't have the "alarm 23:59:59 in the future" race. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * NOTE: some legacy code may have used invalid fields as 29062306a36Sopenharmony_ci * wildcards, exposing hardware "periodic alarm" capabilities. 29162306a36Sopenharmony_ci * Not supported here. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci { 29462306a36Sopenharmony_ci time64_t now, then; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci err = rtc_read_time(rtc, &tm); 29762306a36Sopenharmony_ci if (err < 0) 29862306a36Sopenharmony_ci return err; 29962306a36Sopenharmony_ci now = rtc_tm_to_time64(&tm); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci alarm.time.tm_mday = tm.tm_mday; 30262306a36Sopenharmony_ci alarm.time.tm_mon = tm.tm_mon; 30362306a36Sopenharmony_ci alarm.time.tm_year = tm.tm_year; 30462306a36Sopenharmony_ci err = rtc_valid_tm(&alarm.time); 30562306a36Sopenharmony_ci if (err < 0) 30662306a36Sopenharmony_ci return err; 30762306a36Sopenharmony_ci then = rtc_tm_to_time64(&alarm.time); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* alarm may need to wrap into tomorrow */ 31062306a36Sopenharmony_ci if (then < now) { 31162306a36Sopenharmony_ci rtc_time64_to_tm(now + 24 * 60 * 60, &tm); 31262306a36Sopenharmony_ci alarm.time.tm_mday = tm.tm_mday; 31362306a36Sopenharmony_ci alarm.time.tm_mon = tm.tm_mon; 31462306a36Sopenharmony_ci alarm.time.tm_year = tm.tm_year; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return rtc_set_alarm(rtc, &alarm); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci case RTC_RD_TIME: 32162306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci err = rtc_read_time(rtc, &tm); 32462306a36Sopenharmony_ci if (err < 0) 32562306a36Sopenharmony_ci return err; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (copy_to_user(uarg, &tm, sizeof(tm))) 32862306a36Sopenharmony_ci err = -EFAULT; 32962306a36Sopenharmony_ci return err; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci case RTC_SET_TIME: 33262306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (copy_from_user(&tm, uarg, sizeof(tm))) 33562306a36Sopenharmony_ci return -EFAULT; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return rtc_set_time(rtc, &tm); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci case RTC_PIE_ON: 34062306a36Sopenharmony_ci err = rtc_irq_set_state(rtc, 1); 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci case RTC_PIE_OFF: 34462306a36Sopenharmony_ci err = rtc_irq_set_state(rtc, 0); 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci case RTC_AIE_ON: 34862306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 34962306a36Sopenharmony_ci return rtc_alarm_irq_enable(rtc, 1); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci case RTC_AIE_OFF: 35262306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 35362306a36Sopenharmony_ci return rtc_alarm_irq_enable(rtc, 0); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci case RTC_UIE_ON: 35662306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 35762306a36Sopenharmony_ci return rtc_update_irq_enable(rtc, 1); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci case RTC_UIE_OFF: 36062306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 36162306a36Sopenharmony_ci return rtc_update_irq_enable(rtc, 0); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci case RTC_IRQP_SET: 36462306a36Sopenharmony_ci err = rtc_irq_set_freq(rtc, arg); 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci case RTC_IRQP_READ: 36762306a36Sopenharmony_ci err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci case RTC_WKALM_SET: 37162306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 37262306a36Sopenharmony_ci if (copy_from_user(&alarm, uarg, sizeof(alarm))) 37362306a36Sopenharmony_ci return -EFAULT; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return rtc_set_alarm(rtc, &alarm); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci case RTC_WKALM_RD: 37862306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 37962306a36Sopenharmony_ci err = rtc_read_alarm(rtc, &alarm); 38062306a36Sopenharmony_ci if (err < 0) 38162306a36Sopenharmony_ci return err; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (copy_to_user(uarg, &alarm, sizeof(alarm))) 38462306a36Sopenharmony_ci err = -EFAULT; 38562306a36Sopenharmony_ci return err; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci case RTC_PARAM_GET: 38862306a36Sopenharmony_ci if (copy_from_user(¶m, uarg, sizeof(param))) { 38962306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 39062306a36Sopenharmony_ci return -EFAULT; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci switch(param.param) { 39462306a36Sopenharmony_ci case RTC_PARAM_FEATURES: 39562306a36Sopenharmony_ci if (param.index != 0) 39662306a36Sopenharmony_ci err = -EINVAL; 39762306a36Sopenharmony_ci param.uvalue = rtc->features[0]; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci case RTC_PARAM_CORRECTION: { 40162306a36Sopenharmony_ci long offset; 40262306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 40362306a36Sopenharmony_ci if (param.index != 0) 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci err = rtc_read_offset(rtc, &offset); 40662306a36Sopenharmony_ci mutex_lock(&rtc->ops_lock); 40762306a36Sopenharmony_ci if (err == 0) 40862306a36Sopenharmony_ci param.svalue = offset; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci default: 41262306a36Sopenharmony_ci if (rtc->ops->param_get) 41362306a36Sopenharmony_ci err = rtc->ops->param_get(rtc->dev.parent, ¶m); 41462306a36Sopenharmony_ci else 41562306a36Sopenharmony_ci err = -EINVAL; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!err) 41962306a36Sopenharmony_ci if (copy_to_user(uarg, ¶m, sizeof(param))) 42062306a36Sopenharmony_ci err = -EFAULT; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci case RTC_PARAM_SET: 42562306a36Sopenharmony_ci if (copy_from_user(¶m, uarg, sizeof(param))) { 42662306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 42762306a36Sopenharmony_ci return -EFAULT; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci switch(param.param) { 43162306a36Sopenharmony_ci case RTC_PARAM_FEATURES: 43262306a36Sopenharmony_ci err = -EINVAL; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci case RTC_PARAM_CORRECTION: 43662306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 43762306a36Sopenharmony_ci if (param.index != 0) 43862306a36Sopenharmony_ci return -EINVAL; 43962306a36Sopenharmony_ci return rtc_set_offset(rtc, param.svalue); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci default: 44262306a36Sopenharmony_ci if (rtc->ops->param_set) 44362306a36Sopenharmony_ci err = rtc->ops->param_set(rtc->dev.parent, ¶m); 44462306a36Sopenharmony_ci else 44562306a36Sopenharmony_ci err = -EINVAL; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci /* Finally try the driver's ioctl interface */ 45262306a36Sopenharmony_ci if (ops->ioctl) { 45362306a36Sopenharmony_ci err = ops->ioctl(rtc->dev.parent, cmd, arg); 45462306a36Sopenharmony_ci if (err == -ENOIOCTLCMD) 45562306a36Sopenharmony_ci err = -ENOTTY; 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci err = -ENOTTY; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cidone: 46362306a36Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 46462306a36Sopenharmony_ci return err; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 46862306a36Sopenharmony_ci#define RTC_IRQP_SET32 _IOW('p', 0x0c, __u32) 46962306a36Sopenharmony_ci#define RTC_IRQP_READ32 _IOR('p', 0x0b, __u32) 47062306a36Sopenharmony_ci#define RTC_EPOCH_SET32 _IOW('p', 0x0e, __u32) 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic long rtc_dev_compat_ioctl(struct file *file, 47362306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct rtc_device *rtc = file->private_data; 47662306a36Sopenharmony_ci void __user *uarg = compat_ptr(arg); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci switch (cmd) { 47962306a36Sopenharmony_ci case RTC_IRQP_READ32: 48062306a36Sopenharmony_ci return put_user(rtc->irq_freq, (__u32 __user *)uarg); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci case RTC_IRQP_SET32: 48362306a36Sopenharmony_ci /* arg is a plain integer, not pointer */ 48462306a36Sopenharmony_ci return rtc_dev_ioctl(file, RTC_IRQP_SET, arg); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci case RTC_EPOCH_SET32: 48762306a36Sopenharmony_ci /* arg is a plain integer, not pointer */ 48862306a36Sopenharmony_ci return rtc_dev_ioctl(file, RTC_EPOCH_SET, arg); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return rtc_dev_ioctl(file, cmd, (unsigned long)uarg); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci#endif 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int rtc_dev_fasync(int fd, struct file *file, int on) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct rtc_device *rtc = file->private_data; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return fasync_helper(fd, file, on, &rtc->async_queue); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int rtc_dev_release(struct inode *inode, struct file *file) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct rtc_device *rtc = file->private_data; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* We shut down the repeating IRQs that userspace enabled, 50762306a36Sopenharmony_ci * since nothing is listening to them. 50862306a36Sopenharmony_ci * - Update (UIE) ... currently only managed through ioctls 50962306a36Sopenharmony_ci * - Periodic (PIE) ... also used through rtc_*() interface calls 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * Leave the alarm alone; it may be set to trigger a system wakeup 51262306a36Sopenharmony_ci * later, or be used by kernel code, and is a one-shot event anyway. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Keep ioctl until all drivers are converted */ 51662306a36Sopenharmony_ci rtc_dev_ioctl(file, RTC_UIE_OFF, 0); 51762306a36Sopenharmony_ci rtc_update_irq_enable(rtc, 0); 51862306a36Sopenharmony_ci rtc_irq_set_state(rtc, 0); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic const struct file_operations rtc_dev_fops = { 52562306a36Sopenharmony_ci .owner = THIS_MODULE, 52662306a36Sopenharmony_ci .llseek = no_llseek, 52762306a36Sopenharmony_ci .read = rtc_dev_read, 52862306a36Sopenharmony_ci .poll = rtc_dev_poll, 52962306a36Sopenharmony_ci .unlocked_ioctl = rtc_dev_ioctl, 53062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 53162306a36Sopenharmony_ci .compat_ioctl = rtc_dev_compat_ioctl, 53262306a36Sopenharmony_ci#endif 53362306a36Sopenharmony_ci .open = rtc_dev_open, 53462306a36Sopenharmony_ci .release = rtc_dev_release, 53562306a36Sopenharmony_ci .fasync = rtc_dev_fasync, 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/* insertion/removal hooks */ 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_civoid rtc_dev_prepare(struct rtc_device *rtc) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci if (!rtc_devt) 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (rtc->id >= RTC_DEV_MAX) { 54662306a36Sopenharmony_ci dev_dbg(&rtc->dev, "too many RTC devices\n"); 54762306a36Sopenharmony_ci return; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 55362306a36Sopenharmony_ci INIT_WORK(&rtc->uie_task, rtc_uie_task); 55462306a36Sopenharmony_ci timer_setup(&rtc->uie_timer, rtc_uie_timer, 0); 55562306a36Sopenharmony_ci#endif 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci cdev_init(&rtc->char_dev, &rtc_dev_fops); 55862306a36Sopenharmony_ci rtc->char_dev.owner = rtc->owner; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_civoid __init rtc_dev_init(void) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci int err; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); 56662306a36Sopenharmony_ci if (err < 0) 56762306a36Sopenharmony_ci pr_err("failed to allocate char dev region\n"); 56862306a36Sopenharmony_ci} 569