1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * rtc-efi: RTC Class Driver for EFI-based systems 4 * 5 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. 6 * 7 * Author: dann frazier <dannf@dannf.org> 8 * Based on efirtc.c by Stephane Eranian 9 */ 10 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/stringify.h> 16 #include <linux/time.h> 17 #include <linux/platform_device.h> 18 #include <linux/rtc.h> 19 #include <linux/efi.h> 20 21 #define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT) 22 23 /* 24 * returns day of the year [0-365] 25 */ 26 static inline int 27 compute_yday(efi_time_t *eft) 28 { 29 /* efi_time_t.month is in the [1-12] so, we need -1 */ 30 return rtc_year_days(eft->day, eft->month - 1, eft->year); 31 } 32 33 /* 34 * returns day of the week [0-6] 0=Sunday 35 */ 36 static int 37 compute_wday(efi_time_t *eft, int yday) 38 { 39 int ndays = eft->year * (365 % 7) 40 + (eft->year - 1) / 4 41 - (eft->year - 1) / 100 42 + (eft->year - 1) / 400 43 + yday; 44 45 /* 46 * 1/1/0000 may or may not have been a Sunday (if it ever existed at 47 * all) but assuming it was makes this calculation work correctly. 48 */ 49 return ndays % 7; 50 } 51 52 static void 53 convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) 54 { 55 eft->year = wtime->tm_year + 1900; 56 eft->month = wtime->tm_mon + 1; 57 eft->day = wtime->tm_mday; 58 eft->hour = wtime->tm_hour; 59 eft->minute = wtime->tm_min; 60 eft->second = wtime->tm_sec; 61 eft->nanosecond = 0; 62 eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0; 63 eft->timezone = EFI_UNSPECIFIED_TIMEZONE; 64 } 65 66 static bool 67 convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) 68 { 69 memset(wtime, 0, sizeof(*wtime)); 70 71 if (eft->second >= 60) 72 return false; 73 wtime->tm_sec = eft->second; 74 75 if (eft->minute >= 60) 76 return false; 77 wtime->tm_min = eft->minute; 78 79 if (eft->hour >= 24) 80 return false; 81 wtime->tm_hour = eft->hour; 82 83 if (!eft->day || eft->day > 31) 84 return false; 85 wtime->tm_mday = eft->day; 86 87 if (!eft->month || eft->month > 12) 88 return false; 89 wtime->tm_mon = eft->month - 1; 90 91 if (eft->year < 1900 || eft->year > 9999) 92 return false; 93 wtime->tm_year = eft->year - 1900; 94 95 /* day in the year [1-365]*/ 96 wtime->tm_yday = compute_yday(eft); 97 98 /* day of the week [0-6], Sunday=0 */ 99 wtime->tm_wday = compute_wday(eft, wtime->tm_yday); 100 101 switch (eft->daylight & EFI_ISDST) { 102 case EFI_ISDST: 103 wtime->tm_isdst = 1; 104 break; 105 case EFI_TIME_ADJUST_DAYLIGHT: 106 wtime->tm_isdst = 0; 107 break; 108 default: 109 wtime->tm_isdst = -1; 110 } 111 112 return true; 113 } 114 115 static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) 116 { 117 efi_time_t eft; 118 efi_status_t status; 119 120 /* 121 * As of EFI v1.10, this call always returns an unsupported status 122 */ 123 status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled, 124 (efi_bool_t *)&wkalrm->pending, &eft); 125 126 if (status != EFI_SUCCESS) 127 return -EINVAL; 128 129 if (!convert_from_efi_time(&eft, &wkalrm->time)) 130 return -EIO; 131 132 return rtc_valid_tm(&wkalrm->time); 133 } 134 135 static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) 136 { 137 efi_time_t eft; 138 efi_status_t status; 139 140 convert_to_efi_time(&wkalrm->time, &eft); 141 142 /* 143 * XXX Fixme: 144 * As of EFI 0.92 with the firmware I have on my 145 * machine this call does not seem to work quite 146 * right 147 * 148 * As of v1.10, this call always returns an unsupported status 149 */ 150 status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft); 151 152 dev_warn(dev, "write status is %d\n", (int)status); 153 154 return status == EFI_SUCCESS ? 0 : -EINVAL; 155 } 156 157 static int efi_read_time(struct device *dev, struct rtc_time *tm) 158 { 159 efi_status_t status; 160 efi_time_t eft; 161 efi_time_cap_t cap; 162 163 status = efi.get_time(&eft, &cap); 164 165 if (status != EFI_SUCCESS) { 166 /* should never happen */ 167 dev_err_once(dev, "can't read time\n"); 168 return -EINVAL; 169 } 170 171 if (!convert_from_efi_time(&eft, tm)) 172 return -EIO; 173 174 return 0; 175 } 176 177 static int efi_set_time(struct device *dev, struct rtc_time *tm) 178 { 179 efi_status_t status; 180 efi_time_t eft; 181 182 convert_to_efi_time(tm, &eft); 183 184 status = efi.set_time(&eft); 185 186 return status == EFI_SUCCESS ? 0 : -EINVAL; 187 } 188 189 static int efi_procfs(struct device *dev, struct seq_file *seq) 190 { 191 efi_time_t eft, alm; 192 efi_time_cap_t cap; 193 efi_bool_t enabled, pending; 194 struct rtc_device *rtc = dev_get_drvdata(dev); 195 196 memset(&eft, 0, sizeof(eft)); 197 memset(&alm, 0, sizeof(alm)); 198 memset(&cap, 0, sizeof(cap)); 199 200 efi.get_time(&eft, &cap); 201 efi.get_wakeup_time(&enabled, &pending, &alm); 202 203 seq_printf(seq, 204 "Time\t\t: %u:%u:%u.%09u\n" 205 "Date\t\t: %u-%u-%u\n" 206 "Daylight\t: %u\n", 207 eft.hour, eft.minute, eft.second, eft.nanosecond, 208 eft.year, eft.month, eft.day, 209 eft.daylight); 210 211 if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) 212 seq_puts(seq, "Timezone\t: unspecified\n"); 213 else 214 /* XXX fixme: convert to string? */ 215 seq_printf(seq, "Timezone\t: %u\n", eft.timezone); 216 217 if (test_bit(RTC_FEATURE_ALARM, rtc->features)) { 218 seq_printf(seq, 219 "Alarm Time\t: %u:%u:%u.%09u\n" 220 "Alarm Date\t: %u-%u-%u\n" 221 "Alarm Daylight\t: %u\n" 222 "Enabled\t\t: %s\n" 223 "Pending\t\t: %s\n", 224 alm.hour, alm.minute, alm.second, alm.nanosecond, 225 alm.year, alm.month, alm.day, 226 alm.daylight, 227 enabled == 1 ? "yes" : "no", 228 pending == 1 ? "yes" : "no"); 229 230 if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) 231 seq_puts(seq, "Timezone\t: unspecified\n"); 232 else 233 /* XXX fixme: convert to string? */ 234 seq_printf(seq, "Timezone\t: %u\n", alm.timezone); 235 } 236 237 /* 238 * now prints the capabilities 239 */ 240 seq_printf(seq, 241 "Resolution\t: %u\n" 242 "Accuracy\t: %u\n" 243 "SetstoZero\t: %u\n", 244 cap.resolution, cap.accuracy, cap.sets_to_zero); 245 246 return 0; 247 } 248 249 static const struct rtc_class_ops efi_rtc_ops = { 250 .read_time = efi_read_time, 251 .set_time = efi_set_time, 252 .read_alarm = efi_read_alarm, 253 .set_alarm = efi_set_alarm, 254 .proc = efi_procfs, 255 }; 256 257 static int __init efi_rtc_probe(struct platform_device *dev) 258 { 259 struct rtc_device *rtc; 260 efi_time_t eft; 261 efi_time_cap_t cap; 262 263 /* First check if the RTC is usable */ 264 if (efi.get_time(&eft, &cap) != EFI_SUCCESS) 265 return -ENODEV; 266 267 rtc = devm_rtc_allocate_device(&dev->dev); 268 if (IS_ERR(rtc)) 269 return PTR_ERR(rtc); 270 271 platform_set_drvdata(dev, rtc); 272 273 rtc->ops = &efi_rtc_ops; 274 clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); 275 if (efi_rt_services_supported(EFI_RT_SUPPORTED_WAKEUP_SERVICES)) 276 set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features); 277 else 278 clear_bit(RTC_FEATURE_ALARM, rtc->features); 279 280 device_init_wakeup(&dev->dev, true); 281 282 return devm_rtc_register_device(rtc); 283 } 284 285 static struct platform_driver efi_rtc_driver = { 286 .driver = { 287 .name = "rtc-efi", 288 }, 289 }; 290 291 module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); 292 293 MODULE_AUTHOR("dann frazier <dannf@dannf.org>"); 294 MODULE_LICENSE("GPL"); 295 MODULE_DESCRIPTION("EFI RTC driver"); 296 MODULE_ALIAS("platform:rtc-efi"); 297