18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * RTC subsystem, dev interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Tower Technologies 68c2ecf20Sopenharmony_ci * Author: Alessandro Zummo <a.zummo@towertech.it> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * based on arch/arm/common/rtctime.c 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/compat.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/rtc.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 178c2ecf20Sopenharmony_ci#include "rtc-core.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic dev_t rtc_devt; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int rtc_dev_open(struct inode *inode, struct file *file) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct rtc_device *rtc = container_of(inode->i_cdev, 268c2ecf20Sopenharmony_ci struct rtc_device, char_dev); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) 298c2ecf20Sopenharmony_ci return -EBUSY; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci file->private_data = rtc; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 348c2ecf20Sopenharmony_ci rtc->irq_data = 0; 358c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * Routine to poll RTC seconds field for change as often as possible, 438c2ecf20Sopenharmony_ci * after first RTC_UIE use timer to reduce polling 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic void rtc_uie_task(struct work_struct *work) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct rtc_device *rtc = 488c2ecf20Sopenharmony_ci container_of(work, struct rtc_device, uie_task); 498c2ecf20Sopenharmony_ci struct rtc_time tm; 508c2ecf20Sopenharmony_ci int num = 0; 518c2ecf20Sopenharmony_ci int err; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci err = rtc_read_time(rtc, &tm); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 568c2ecf20Sopenharmony_ci if (rtc->stop_uie_polling || err) { 578c2ecf20Sopenharmony_ci rtc->uie_task_active = 0; 588c2ecf20Sopenharmony_ci } else if (rtc->oldsecs != tm.tm_sec) { 598c2ecf20Sopenharmony_ci num = (tm.tm_sec + 60 - rtc->oldsecs) % 60; 608c2ecf20Sopenharmony_ci rtc->oldsecs = tm.tm_sec; 618c2ecf20Sopenharmony_ci rtc->uie_timer.expires = jiffies + HZ - (HZ / 10); 628c2ecf20Sopenharmony_ci rtc->uie_timer_active = 1; 638c2ecf20Sopenharmony_ci rtc->uie_task_active = 0; 648c2ecf20Sopenharmony_ci add_timer(&rtc->uie_timer); 658c2ecf20Sopenharmony_ci } else if (schedule_work(&rtc->uie_task) == 0) { 668c2ecf20Sopenharmony_ci rtc->uie_task_active = 0; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 698c2ecf20Sopenharmony_ci if (num) 708c2ecf20Sopenharmony_ci rtc_handle_legacy_irq(rtc, num, RTC_UF); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void rtc_uie_timer(struct timer_list *t) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct rtc_device *rtc = from_timer(rtc, t, uie_timer); 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc->irq_lock, flags); 798c2ecf20Sopenharmony_ci rtc->uie_timer_active = 0; 808c2ecf20Sopenharmony_ci rtc->uie_task_active = 1; 818c2ecf20Sopenharmony_ci if ((schedule_work(&rtc->uie_task) == 0)) 828c2ecf20Sopenharmony_ci rtc->uie_task_active = 0; 838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc->irq_lock, flags); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int clear_uie(struct rtc_device *rtc) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 898c2ecf20Sopenharmony_ci if (rtc->uie_irq_active) { 908c2ecf20Sopenharmony_ci rtc->stop_uie_polling = 1; 918c2ecf20Sopenharmony_ci if (rtc->uie_timer_active) { 928c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 938c2ecf20Sopenharmony_ci del_timer_sync(&rtc->uie_timer); 948c2ecf20Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 958c2ecf20Sopenharmony_ci rtc->uie_timer_active = 0; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci if (rtc->uie_task_active) { 988c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 998c2ecf20Sopenharmony_ci flush_scheduled_work(); 1008c2ecf20Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci rtc->uie_irq_active = 0; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int set_uie(struct rtc_device *rtc) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct rtc_time tm; 1118c2ecf20Sopenharmony_ci int err; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci err = rtc_read_time(rtc, &tm); 1148c2ecf20Sopenharmony_ci if (err) 1158c2ecf20Sopenharmony_ci return err; 1168c2ecf20Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 1178c2ecf20Sopenharmony_ci if (!rtc->uie_irq_active) { 1188c2ecf20Sopenharmony_ci rtc->uie_irq_active = 1; 1198c2ecf20Sopenharmony_ci rtc->stop_uie_polling = 0; 1208c2ecf20Sopenharmony_ci rtc->oldsecs = tm.tm_sec; 1218c2ecf20Sopenharmony_ci rtc->uie_task_active = 1; 1228c2ecf20Sopenharmony_ci if (schedule_work(&rtc->uie_task) == 0) 1238c2ecf20Sopenharmony_ci rtc->uie_task_active = 0; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci rtc->irq_data = 0; 1268c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciint rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci if (enabled) 1338c2ecf20Sopenharmony_ci return set_uie(rtc); 1348c2ecf20Sopenharmony_ci else 1358c2ecf20Sopenharmony_ci return clear_uie(rtc); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic ssize_t 1428c2ecf20Sopenharmony_cirtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct rtc_device *rtc = file->private_data; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 1478c2ecf20Sopenharmony_ci unsigned long data; 1488c2ecf20Sopenharmony_ci ssize_t ret; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) 1518c2ecf20Sopenharmony_ci return -EINVAL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci add_wait_queue(&rtc->irq_queue, &wait); 1548c2ecf20Sopenharmony_ci do { 1558c2ecf20Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci spin_lock_irq(&rtc->irq_lock); 1588c2ecf20Sopenharmony_ci data = rtc->irq_data; 1598c2ecf20Sopenharmony_ci rtc->irq_data = 0; 1608c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc->irq_lock); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (data != 0) { 1638c2ecf20Sopenharmony_ci ret = 0; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 1678c2ecf20Sopenharmony_ci ret = -EAGAIN; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci if (signal_pending(current)) { 1718c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci schedule(); 1758c2ecf20Sopenharmony_ci } while (1); 1768c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 1778c2ecf20Sopenharmony_ci remove_wait_queue(&rtc->irq_queue, &wait); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (ret == 0) { 1808c2ecf20Sopenharmony_ci if (sizeof(int) != sizeof(long) && 1818c2ecf20Sopenharmony_ci count == sizeof(unsigned int)) 1828c2ecf20Sopenharmony_ci ret = put_user(data, (unsigned int __user *)buf) ?: 1838c2ecf20Sopenharmony_ci sizeof(unsigned int); 1848c2ecf20Sopenharmony_ci else 1858c2ecf20Sopenharmony_ci ret = put_user(data, (unsigned long __user *)buf) ?: 1868c2ecf20Sopenharmony_ci sizeof(unsigned long); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic __poll_t rtc_dev_poll(struct file *file, poll_table *wait) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct rtc_device *rtc = file->private_data; 1948c2ecf20Sopenharmony_ci unsigned long data; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci poll_wait(file, &rtc->irq_queue, wait); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci data = rtc->irq_data; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return (data != 0) ? (EPOLLIN | EPOLLRDNORM) : 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic long rtc_dev_ioctl(struct file *file, 2048c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int err = 0; 2078c2ecf20Sopenharmony_ci struct rtc_device *rtc = file->private_data; 2088c2ecf20Sopenharmony_ci const struct rtc_class_ops *ops = rtc->ops; 2098c2ecf20Sopenharmony_ci struct rtc_time tm; 2108c2ecf20Sopenharmony_ci struct rtc_wkalrm alarm; 2118c2ecf20Sopenharmony_ci void __user *uarg = (void __user *)arg; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci err = mutex_lock_interruptible(&rtc->ops_lock); 2148c2ecf20Sopenharmony_ci if (err) 2158c2ecf20Sopenharmony_ci return err; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* check that the calling task has appropriate permissions 2188c2ecf20Sopenharmony_ci * for certain ioctls. doing this check here is useful 2198c2ecf20Sopenharmony_ci * to avoid duplicate code in each driver. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci switch (cmd) { 2228c2ecf20Sopenharmony_ci case RTC_EPOCH_SET: 2238c2ecf20Sopenharmony_ci case RTC_SET_TIME: 2248c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_TIME)) 2258c2ecf20Sopenharmony_ci err = -EACCES; 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci case RTC_IRQP_SET: 2298c2ecf20Sopenharmony_ci if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) 2308c2ecf20Sopenharmony_ci err = -EACCES; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci case RTC_PIE_ON: 2348c2ecf20Sopenharmony_ci if (rtc->irq_freq > rtc->max_user_freq && 2358c2ecf20Sopenharmony_ci !capable(CAP_SYS_RESOURCE)) 2368c2ecf20Sopenharmony_ci err = -EACCES; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (err) 2418c2ecf20Sopenharmony_ci goto done; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * Drivers *SHOULD NOT* provide ioctl implementations 2458c2ecf20Sopenharmony_ci * for these requests. Instead, provide methods to 2468c2ecf20Sopenharmony_ci * support the following code, so that the RTC's main 2478c2ecf20Sopenharmony_ci * features are accessible without using ioctls. 2488c2ecf20Sopenharmony_ci * 2498c2ecf20Sopenharmony_ci * RTC and alarm times will be in UTC, by preference, 2508c2ecf20Sopenharmony_ci * but dual-booting with MS-Windows implies RTCs must 2518c2ecf20Sopenharmony_ci * use the local wall clock time. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci switch (cmd) { 2558c2ecf20Sopenharmony_ci case RTC_ALM_READ: 2568c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci err = rtc_read_alarm(rtc, &alarm); 2598c2ecf20Sopenharmony_ci if (err < 0) 2608c2ecf20Sopenharmony_ci return err; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (copy_to_user(uarg, &alarm.time, sizeof(tm))) 2638c2ecf20Sopenharmony_ci err = -EFAULT; 2648c2ecf20Sopenharmony_ci return err; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci case RTC_ALM_SET: 2678c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (copy_from_user(&alarm.time, uarg, sizeof(tm))) 2708c2ecf20Sopenharmony_ci return -EFAULT; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci alarm.enabled = 0; 2738c2ecf20Sopenharmony_ci alarm.pending = 0; 2748c2ecf20Sopenharmony_ci alarm.time.tm_wday = -1; 2758c2ecf20Sopenharmony_ci alarm.time.tm_yday = -1; 2768c2ecf20Sopenharmony_ci alarm.time.tm_isdst = -1; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* RTC_ALM_SET alarms may be up to 24 hours in the future. 2798c2ecf20Sopenharmony_ci * Rather than expecting every RTC to implement "don't care" 2808c2ecf20Sopenharmony_ci * for day/month/year fields, just force the alarm to have 2818c2ecf20Sopenharmony_ci * the right values for those fields. 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * RTC_WKALM_SET should be used instead. Not only does it 2848c2ecf20Sopenharmony_ci * eliminate the need for a separate RTC_AIE_ON call, it 2858c2ecf20Sopenharmony_ci * doesn't have the "alarm 23:59:59 in the future" race. 2868c2ecf20Sopenharmony_ci * 2878c2ecf20Sopenharmony_ci * NOTE: some legacy code may have used invalid fields as 2888c2ecf20Sopenharmony_ci * wildcards, exposing hardware "periodic alarm" capabilities. 2898c2ecf20Sopenharmony_ci * Not supported here. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci { 2928c2ecf20Sopenharmony_ci time64_t now, then; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci err = rtc_read_time(rtc, &tm); 2958c2ecf20Sopenharmony_ci if (err < 0) 2968c2ecf20Sopenharmony_ci return err; 2978c2ecf20Sopenharmony_ci now = rtc_tm_to_time64(&tm); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci alarm.time.tm_mday = tm.tm_mday; 3008c2ecf20Sopenharmony_ci alarm.time.tm_mon = tm.tm_mon; 3018c2ecf20Sopenharmony_ci alarm.time.tm_year = tm.tm_year; 3028c2ecf20Sopenharmony_ci err = rtc_valid_tm(&alarm.time); 3038c2ecf20Sopenharmony_ci if (err < 0) 3048c2ecf20Sopenharmony_ci return err; 3058c2ecf20Sopenharmony_ci then = rtc_tm_to_time64(&alarm.time); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* alarm may need to wrap into tomorrow */ 3088c2ecf20Sopenharmony_ci if (then < now) { 3098c2ecf20Sopenharmony_ci rtc_time64_to_tm(now + 24 * 60 * 60, &tm); 3108c2ecf20Sopenharmony_ci alarm.time.tm_mday = tm.tm_mday; 3118c2ecf20Sopenharmony_ci alarm.time.tm_mon = tm.tm_mon; 3128c2ecf20Sopenharmony_ci alarm.time.tm_year = tm.tm_year; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return rtc_set_alarm(rtc, &alarm); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci case RTC_RD_TIME: 3198c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci err = rtc_read_time(rtc, &tm); 3228c2ecf20Sopenharmony_ci if (err < 0) 3238c2ecf20Sopenharmony_ci return err; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (copy_to_user(uarg, &tm, sizeof(tm))) 3268c2ecf20Sopenharmony_ci err = -EFAULT; 3278c2ecf20Sopenharmony_ci return err; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci case RTC_SET_TIME: 3308c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (copy_from_user(&tm, uarg, sizeof(tm))) 3338c2ecf20Sopenharmony_ci return -EFAULT; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return rtc_set_time(rtc, &tm); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci case RTC_PIE_ON: 3388c2ecf20Sopenharmony_ci err = rtc_irq_set_state(rtc, 1); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci case RTC_PIE_OFF: 3428c2ecf20Sopenharmony_ci err = rtc_irq_set_state(rtc, 0); 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci case RTC_AIE_ON: 3468c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3478c2ecf20Sopenharmony_ci return rtc_alarm_irq_enable(rtc, 1); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci case RTC_AIE_OFF: 3508c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3518c2ecf20Sopenharmony_ci return rtc_alarm_irq_enable(rtc, 0); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci case RTC_UIE_ON: 3548c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3558c2ecf20Sopenharmony_ci return rtc_update_irq_enable(rtc, 1); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci case RTC_UIE_OFF: 3588c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3598c2ecf20Sopenharmony_ci return rtc_update_irq_enable(rtc, 0); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci case RTC_IRQP_SET: 3628c2ecf20Sopenharmony_ci err = rtc_irq_set_freq(rtc, arg); 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case RTC_IRQP_READ: 3658c2ecf20Sopenharmony_ci err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci case RTC_WKALM_SET: 3698c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3708c2ecf20Sopenharmony_ci if (copy_from_user(&alarm, uarg, sizeof(alarm))) 3718c2ecf20Sopenharmony_ci return -EFAULT; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return rtc_set_alarm(rtc, &alarm); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci case RTC_WKALM_RD: 3768c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3778c2ecf20Sopenharmony_ci err = rtc_read_alarm(rtc, &alarm); 3788c2ecf20Sopenharmony_ci if (err < 0) 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (copy_to_user(uarg, &alarm, sizeof(alarm))) 3828c2ecf20Sopenharmony_ci err = -EFAULT; 3838c2ecf20Sopenharmony_ci return err; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci default: 3868c2ecf20Sopenharmony_ci /* Finally try the driver's ioctl interface */ 3878c2ecf20Sopenharmony_ci if (ops->ioctl) { 3888c2ecf20Sopenharmony_ci err = ops->ioctl(rtc->dev.parent, cmd, arg); 3898c2ecf20Sopenharmony_ci if (err == -ENOIOCTLCMD) 3908c2ecf20Sopenharmony_ci err = -ENOTTY; 3918c2ecf20Sopenharmony_ci } else { 3928c2ecf20Sopenharmony_ci err = -ENOTTY; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cidone: 3988c2ecf20Sopenharmony_ci mutex_unlock(&rtc->ops_lock); 3998c2ecf20Sopenharmony_ci return err; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 4038c2ecf20Sopenharmony_ci#define RTC_IRQP_SET32 _IOW('p', 0x0c, __u32) 4048c2ecf20Sopenharmony_ci#define RTC_IRQP_READ32 _IOR('p', 0x0b, __u32) 4058c2ecf20Sopenharmony_ci#define RTC_EPOCH_SET32 _IOW('p', 0x0e, __u32) 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic long rtc_dev_compat_ioctl(struct file *file, 4088c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct rtc_device *rtc = file->private_data; 4118c2ecf20Sopenharmony_ci void __user *uarg = compat_ptr(arg); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci switch (cmd) { 4148c2ecf20Sopenharmony_ci case RTC_IRQP_READ32: 4158c2ecf20Sopenharmony_ci return put_user(rtc->irq_freq, (__u32 __user *)uarg); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci case RTC_IRQP_SET32: 4188c2ecf20Sopenharmony_ci /* arg is a plain integer, not pointer */ 4198c2ecf20Sopenharmony_ci return rtc_dev_ioctl(file, RTC_IRQP_SET, arg); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci case RTC_EPOCH_SET32: 4228c2ecf20Sopenharmony_ci /* arg is a plain integer, not pointer */ 4238c2ecf20Sopenharmony_ci return rtc_dev_ioctl(file, RTC_EPOCH_SET, arg); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return rtc_dev_ioctl(file, cmd, (unsigned long)uarg); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci#endif 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int rtc_dev_fasync(int fd, struct file *file, int on) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct rtc_device *rtc = file->private_data; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return fasync_helper(fd, file, on, &rtc->async_queue); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int rtc_dev_release(struct inode *inode, struct file *file) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct rtc_device *rtc = file->private_data; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* We shut down the repeating IRQs that userspace enabled, 4428c2ecf20Sopenharmony_ci * since nothing is listening to them. 4438c2ecf20Sopenharmony_ci * - Update (UIE) ... currently only managed through ioctls 4448c2ecf20Sopenharmony_ci * - Periodic (PIE) ... also used through rtc_*() interface calls 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * Leave the alarm alone; it may be set to trigger a system wakeup 4478c2ecf20Sopenharmony_ci * later, or be used by kernel code, and is a one-shot event anyway. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Keep ioctl until all drivers are converted */ 4518c2ecf20Sopenharmony_ci rtc_dev_ioctl(file, RTC_UIE_OFF, 0); 4528c2ecf20Sopenharmony_ci rtc_update_irq_enable(rtc, 0); 4538c2ecf20Sopenharmony_ci rtc_irq_set_state(rtc, 0); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic const struct file_operations rtc_dev_fops = { 4608c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4618c2ecf20Sopenharmony_ci .llseek = no_llseek, 4628c2ecf20Sopenharmony_ci .read = rtc_dev_read, 4638c2ecf20Sopenharmony_ci .poll = rtc_dev_poll, 4648c2ecf20Sopenharmony_ci .unlocked_ioctl = rtc_dev_ioctl, 4658c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 4668c2ecf20Sopenharmony_ci .compat_ioctl = rtc_dev_compat_ioctl, 4678c2ecf20Sopenharmony_ci#endif 4688c2ecf20Sopenharmony_ci .open = rtc_dev_open, 4698c2ecf20Sopenharmony_ci .release = rtc_dev_release, 4708c2ecf20Sopenharmony_ci .fasync = rtc_dev_fasync, 4718c2ecf20Sopenharmony_ci}; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* insertion/removal hooks */ 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_civoid rtc_dev_prepare(struct rtc_device *rtc) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci if (!rtc_devt) 4788c2ecf20Sopenharmony_ci return; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (rtc->id >= RTC_DEV_MAX) { 4818c2ecf20Sopenharmony_ci dev_dbg(&rtc->dev, "too many RTC devices\n"); 4828c2ecf20Sopenharmony_ci return; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 4888c2ecf20Sopenharmony_ci INIT_WORK(&rtc->uie_task, rtc_uie_task); 4898c2ecf20Sopenharmony_ci timer_setup(&rtc->uie_timer, rtc_uie_timer, 0); 4908c2ecf20Sopenharmony_ci#endif 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci cdev_init(&rtc->char_dev, &rtc_dev_fops); 4938c2ecf20Sopenharmony_ci rtc->char_dev.owner = rtc->owner; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_civoid __init rtc_dev_init(void) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci int err; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); 5018c2ecf20Sopenharmony_ci if (err < 0) 5028c2ecf20Sopenharmony_ci pr_err("failed to allocate char dev region\n"); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_civoid __exit rtc_dev_exit(void) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci if (rtc_devt) 5088c2ecf20Sopenharmony_ci unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); 5098c2ecf20Sopenharmony_ci} 510