1*dde8b58dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 2*dde8b58dSJohannes Berg /* 3*dde8b58dSJohannes Berg * Copyright (C) 2020 Intel Corporation 4*dde8b58dSJohannes Berg * Author: Johannes Berg <johannes@sipsolutions.net> 5*dde8b58dSJohannes Berg */ 6*dde8b58dSJohannes Berg #include <linux/platform_device.h> 7*dde8b58dSJohannes Berg #include <linux/time-internal.h> 8*dde8b58dSJohannes Berg #include <linux/suspend.h> 9*dde8b58dSJohannes Berg #include <linux/err.h> 10*dde8b58dSJohannes Berg #include <linux/rtc.h> 11*dde8b58dSJohannes Berg #include <kern_util.h> 12*dde8b58dSJohannes Berg #include <irq_kern.h> 13*dde8b58dSJohannes Berg #include <os.h> 14*dde8b58dSJohannes Berg #include "rtc.h" 15*dde8b58dSJohannes Berg 16*dde8b58dSJohannes Berg static time64_t uml_rtc_alarm_time; 17*dde8b58dSJohannes Berg static bool uml_rtc_alarm_enabled; 18*dde8b58dSJohannes Berg static struct rtc_device *uml_rtc; 19*dde8b58dSJohannes Berg static int uml_rtc_irq_fd, uml_rtc_irq; 20*dde8b58dSJohannes Berg 21*dde8b58dSJohannes Berg #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT 22*dde8b58dSJohannes Berg 23*dde8b58dSJohannes Berg static void uml_rtc_time_travel_alarm(struct time_travel_event *ev) 24*dde8b58dSJohannes Berg { 25*dde8b58dSJohannes Berg uml_rtc_send_timetravel_alarm(); 26*dde8b58dSJohannes Berg } 27*dde8b58dSJohannes Berg 28*dde8b58dSJohannes Berg static struct time_travel_event uml_rtc_alarm_event = { 29*dde8b58dSJohannes Berg .fn = uml_rtc_time_travel_alarm, 30*dde8b58dSJohannes Berg }; 31*dde8b58dSJohannes Berg #endif 32*dde8b58dSJohannes Berg 33*dde8b58dSJohannes Berg static int uml_rtc_read_time(struct device *dev, struct rtc_time *tm) 34*dde8b58dSJohannes Berg { 35*dde8b58dSJohannes Berg struct timespec64 ts; 36*dde8b58dSJohannes Berg 37*dde8b58dSJohannes Berg /* Use this to get correct time in time-travel mode */ 38*dde8b58dSJohannes Berg read_persistent_clock64(&ts); 39*dde8b58dSJohannes Berg rtc_time64_to_tm(timespec64_to_ktime(ts) / NSEC_PER_SEC, tm); 40*dde8b58dSJohannes Berg 41*dde8b58dSJohannes Berg return 0; 42*dde8b58dSJohannes Berg } 43*dde8b58dSJohannes Berg 44*dde8b58dSJohannes Berg static int uml_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 45*dde8b58dSJohannes Berg { 46*dde8b58dSJohannes Berg rtc_time64_to_tm(uml_rtc_alarm_time, &alrm->time); 47*dde8b58dSJohannes Berg alrm->enabled = uml_rtc_alarm_enabled; 48*dde8b58dSJohannes Berg 49*dde8b58dSJohannes Berg return 0; 50*dde8b58dSJohannes Berg } 51*dde8b58dSJohannes Berg 52*dde8b58dSJohannes Berg static int uml_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 53*dde8b58dSJohannes Berg { 54*dde8b58dSJohannes Berg unsigned long long secs; 55*dde8b58dSJohannes Berg 56*dde8b58dSJohannes Berg if (!enable && !uml_rtc_alarm_enabled) 57*dde8b58dSJohannes Berg return 0; 58*dde8b58dSJohannes Berg 59*dde8b58dSJohannes Berg uml_rtc_alarm_enabled = enable; 60*dde8b58dSJohannes Berg 61*dde8b58dSJohannes Berg secs = uml_rtc_alarm_time - ktime_get_real_seconds(); 62*dde8b58dSJohannes Berg 63*dde8b58dSJohannes Berg if (time_travel_mode == TT_MODE_OFF) { 64*dde8b58dSJohannes Berg if (!enable) { 65*dde8b58dSJohannes Berg uml_rtc_disable_alarm(); 66*dde8b58dSJohannes Berg return 0; 67*dde8b58dSJohannes Berg } 68*dde8b58dSJohannes Berg 69*dde8b58dSJohannes Berg /* enable or update */ 70*dde8b58dSJohannes Berg return uml_rtc_enable_alarm(secs); 71*dde8b58dSJohannes Berg } else { 72*dde8b58dSJohannes Berg time_travel_del_event(¨_rtc_alarm_event); 73*dde8b58dSJohannes Berg 74*dde8b58dSJohannes Berg if (enable) 75*dde8b58dSJohannes Berg time_travel_add_event_rel(¨_rtc_alarm_event, 76*dde8b58dSJohannes Berg secs * NSEC_PER_SEC); 77*dde8b58dSJohannes Berg } 78*dde8b58dSJohannes Berg 79*dde8b58dSJohannes Berg return 0; 80*dde8b58dSJohannes Berg } 81*dde8b58dSJohannes Berg 82*dde8b58dSJohannes Berg static int uml_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 83*dde8b58dSJohannes Berg { 84*dde8b58dSJohannes Berg uml_rtc_alarm_irq_enable(dev, 0); 85*dde8b58dSJohannes Berg uml_rtc_alarm_time = rtc_tm_to_time64(&alrm->time); 86*dde8b58dSJohannes Berg uml_rtc_alarm_irq_enable(dev, alrm->enabled); 87*dde8b58dSJohannes Berg 88*dde8b58dSJohannes Berg return 0; 89*dde8b58dSJohannes Berg } 90*dde8b58dSJohannes Berg 91*dde8b58dSJohannes Berg static const struct rtc_class_ops uml_rtc_ops = { 92*dde8b58dSJohannes Berg .read_time = uml_rtc_read_time, 93*dde8b58dSJohannes Berg .read_alarm = uml_rtc_read_alarm, 94*dde8b58dSJohannes Berg .alarm_irq_enable = uml_rtc_alarm_irq_enable, 95*dde8b58dSJohannes Berg .set_alarm = uml_rtc_set_alarm, 96*dde8b58dSJohannes Berg }; 97*dde8b58dSJohannes Berg 98*dde8b58dSJohannes Berg static irqreturn_t uml_rtc_interrupt(int irq, void *data) 99*dde8b58dSJohannes Berg { 100*dde8b58dSJohannes Berg unsigned long long c = 0; 101*dde8b58dSJohannes Berg 102*dde8b58dSJohannes Berg /* alarm triggered, it's now off */ 103*dde8b58dSJohannes Berg uml_rtc_alarm_enabled = false; 104*dde8b58dSJohannes Berg 105*dde8b58dSJohannes Berg os_read_file(uml_rtc_irq_fd, &c, sizeof(c)); 106*dde8b58dSJohannes Berg WARN_ON(c == 0); 107*dde8b58dSJohannes Berg 108*dde8b58dSJohannes Berg pm_system_wakeup(); 109*dde8b58dSJohannes Berg rtc_update_irq(uml_rtc, 1, RTC_IRQF | RTC_AF); 110*dde8b58dSJohannes Berg 111*dde8b58dSJohannes Berg return IRQ_HANDLED; 112*dde8b58dSJohannes Berg } 113*dde8b58dSJohannes Berg 114*dde8b58dSJohannes Berg static int uml_rtc_setup(void) 115*dde8b58dSJohannes Berg { 116*dde8b58dSJohannes Berg int err; 117*dde8b58dSJohannes Berg 118*dde8b58dSJohannes Berg err = uml_rtc_start(time_travel_mode != TT_MODE_OFF); 119*dde8b58dSJohannes Berg if (WARN(err < 0, "err = %d\n", err)) 120*dde8b58dSJohannes Berg return err; 121*dde8b58dSJohannes Berg 122*dde8b58dSJohannes Berg uml_rtc_irq_fd = err; 123*dde8b58dSJohannes Berg 124*dde8b58dSJohannes Berg err = um_request_irq(UM_IRQ_ALLOC, uml_rtc_irq_fd, IRQ_READ, 125*dde8b58dSJohannes Berg uml_rtc_interrupt, 0, "rtc", NULL); 126*dde8b58dSJohannes Berg if (err < 0) { 127*dde8b58dSJohannes Berg uml_rtc_stop(time_travel_mode != TT_MODE_OFF); 128*dde8b58dSJohannes Berg return err; 129*dde8b58dSJohannes Berg } 130*dde8b58dSJohannes Berg 131*dde8b58dSJohannes Berg irq_set_irq_wake(err, 1); 132*dde8b58dSJohannes Berg 133*dde8b58dSJohannes Berg uml_rtc_irq = err; 134*dde8b58dSJohannes Berg return 0; 135*dde8b58dSJohannes Berg } 136*dde8b58dSJohannes Berg 137*dde8b58dSJohannes Berg static void uml_rtc_cleanup(void) 138*dde8b58dSJohannes Berg { 139*dde8b58dSJohannes Berg um_free_irq(uml_rtc_irq, NULL); 140*dde8b58dSJohannes Berg uml_rtc_stop(time_travel_mode != TT_MODE_OFF); 141*dde8b58dSJohannes Berg } 142*dde8b58dSJohannes Berg 143*dde8b58dSJohannes Berg static int uml_rtc_probe(struct platform_device *pdev) 144*dde8b58dSJohannes Berg { 145*dde8b58dSJohannes Berg int err; 146*dde8b58dSJohannes Berg 147*dde8b58dSJohannes Berg err = uml_rtc_setup(); 148*dde8b58dSJohannes Berg if (err) 149*dde8b58dSJohannes Berg return err; 150*dde8b58dSJohannes Berg 151*dde8b58dSJohannes Berg uml_rtc = devm_rtc_allocate_device(&pdev->dev); 152*dde8b58dSJohannes Berg if (IS_ERR(uml_rtc)) { 153*dde8b58dSJohannes Berg err = PTR_ERR(uml_rtc); 154*dde8b58dSJohannes Berg goto cleanup; 155*dde8b58dSJohannes Berg } 156*dde8b58dSJohannes Berg 157*dde8b58dSJohannes Berg uml_rtc->ops = ¨_rtc_ops; 158*dde8b58dSJohannes Berg 159*dde8b58dSJohannes Berg device_init_wakeup(&pdev->dev, 1); 160*dde8b58dSJohannes Berg 161*dde8b58dSJohannes Berg err = devm_rtc_register_device(uml_rtc); 162*dde8b58dSJohannes Berg if (err) 163*dde8b58dSJohannes Berg goto cleanup; 164*dde8b58dSJohannes Berg 165*dde8b58dSJohannes Berg return 0; 166*dde8b58dSJohannes Berg cleanup: 167*dde8b58dSJohannes Berg uml_rtc_cleanup(); 168*dde8b58dSJohannes Berg return err; 169*dde8b58dSJohannes Berg } 170*dde8b58dSJohannes Berg 171*dde8b58dSJohannes Berg static int uml_rtc_remove(struct platform_device *pdev) 172*dde8b58dSJohannes Berg { 173*dde8b58dSJohannes Berg device_init_wakeup(&pdev->dev, 0); 174*dde8b58dSJohannes Berg uml_rtc_cleanup(); 175*dde8b58dSJohannes Berg return 0; 176*dde8b58dSJohannes Berg } 177*dde8b58dSJohannes Berg 178*dde8b58dSJohannes Berg static struct platform_driver uml_rtc_driver = { 179*dde8b58dSJohannes Berg .probe = uml_rtc_probe, 180*dde8b58dSJohannes Berg .remove = uml_rtc_remove, 181*dde8b58dSJohannes Berg .driver = { 182*dde8b58dSJohannes Berg .name = "uml-rtc", 183*dde8b58dSJohannes Berg }, 184*dde8b58dSJohannes Berg }; 185*dde8b58dSJohannes Berg 186*dde8b58dSJohannes Berg static int __init uml_rtc_init(void) 187*dde8b58dSJohannes Berg { 188*dde8b58dSJohannes Berg struct platform_device *pdev; 189*dde8b58dSJohannes Berg int err; 190*dde8b58dSJohannes Berg 191*dde8b58dSJohannes Berg err = platform_driver_register(¨_rtc_driver); 192*dde8b58dSJohannes Berg if (err) 193*dde8b58dSJohannes Berg return err; 194*dde8b58dSJohannes Berg 195*dde8b58dSJohannes Berg pdev = platform_device_alloc("uml-rtc", 0); 196*dde8b58dSJohannes Berg if (!pdev) { 197*dde8b58dSJohannes Berg err = -ENOMEM; 198*dde8b58dSJohannes Berg goto unregister; 199*dde8b58dSJohannes Berg } 200*dde8b58dSJohannes Berg 201*dde8b58dSJohannes Berg err = platform_device_add(pdev); 202*dde8b58dSJohannes Berg if (err) 203*dde8b58dSJohannes Berg goto unregister; 204*dde8b58dSJohannes Berg return 0; 205*dde8b58dSJohannes Berg 206*dde8b58dSJohannes Berg unregister: 207*dde8b58dSJohannes Berg platform_device_put(pdev); 208*dde8b58dSJohannes Berg platform_driver_unregister(¨_rtc_driver); 209*dde8b58dSJohannes Berg return err; 210*dde8b58dSJohannes Berg } 211*dde8b58dSJohannes Berg device_initcall(uml_rtc_init); 212