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(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 195 memset(&eft, 0, sizeof(eft)); 196 memset(&alm, 0, sizeof(alm)); 197 memset(&cap, 0, sizeof(cap)); 198 199 efi.get_time(&eft, &cap); 200 efi.get_wakeup_time(&enabled, &pending, &alm); 201 202 seq_printf(seq, 203 "Time\t\t: %u:%u:%u.%09u\n" 204 "Date\t\t: %u-%u-%u\n" 205 "Daylight\t: %u\n", 206 eft.hour, eft.minute, eft.second, eft.nanosecond, 207 eft.year, eft.month, eft.day, 208 eft.daylight); 209 210 if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) 211 seq_puts(seq, "Timezone\t: unspecified\n"); 212 else 213 /* XXX fixme: convert to string? */ 214 seq_printf(seq, "Timezone\t: %u\n", eft.timezone); 215 216 seq_printf(seq, 217 "Alarm Time\t: %u:%u:%u.%09u\n" 218 "Alarm Date\t: %u-%u-%u\n" 219 "Alarm Daylight\t: %u\n" 220 "Enabled\t\t: %s\n" 221 "Pending\t\t: %s\n", 222 alm.hour, alm.minute, alm.second, alm.nanosecond, 223 alm.year, alm.month, alm.day, 224 alm.daylight, 225 enabled == 1 ? "yes" : "no", 226 pending == 1 ? "yes" : "no"); 227 228 if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) 229 seq_puts(seq, "Timezone\t: unspecified\n"); 230 else 231 /* XXX fixme: convert to string? */ 232 seq_printf(seq, "Timezone\t: %u\n", alm.timezone); 233 234 /* 235 * now prints the capabilities 236 */ 237 seq_printf(seq, 238 "Resolution\t: %u\n" 239 "Accuracy\t: %u\n" 240 "SetstoZero\t: %u\n", 241 cap.resolution, cap.accuracy, cap.sets_to_zero); 242 243 return 0; 244 } 245 246 static const struct rtc_class_ops efi_rtc_ops = { 247 .read_time = efi_read_time, 248 .set_time = efi_set_time, 249 .read_alarm = efi_read_alarm, 250 .set_alarm = efi_set_alarm, 251 .proc = efi_procfs, 252 }; 253 254 static int __init efi_rtc_probe(struct platform_device *dev) 255 { 256 struct rtc_device *rtc; 257 efi_time_t eft; 258 efi_time_cap_t cap; 259 260 /* First check if the RTC is usable */ 261 if (efi.get_time(&eft, &cap) != EFI_SUCCESS) 262 return -ENODEV; 263 264 rtc = devm_rtc_device_register(&dev->dev, "rtc-efi", &efi_rtc_ops, 265 THIS_MODULE); 266 if (IS_ERR(rtc)) 267 return PTR_ERR(rtc); 268 269 rtc->uie_unsupported = 1; 270 platform_set_drvdata(dev, rtc); 271 272 return 0; 273 } 274 275 static struct platform_driver efi_rtc_driver = { 276 .driver = { 277 .name = "rtc-efi", 278 }, 279 }; 280 281 module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); 282 283 MODULE_AUTHOR("dann frazier <dannf@dannf.org>"); 284 MODULE_LICENSE("GPL"); 285 MODULE_DESCRIPTION("EFI RTC driver"); 286 MODULE_ALIAS("platform:rtc-efi"); 287