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_time(struct device *dev, struct rtc_time *tm) 116 { 117 efi_status_t status; 118 efi_time_t eft; 119 efi_time_cap_t cap; 120 121 status = efi.get_time(&eft, &cap); 122 123 if (status != EFI_SUCCESS) { 124 /* should never happen */ 125 dev_err_once(dev, "can't read time\n"); 126 return -EINVAL; 127 } 128 129 if (!convert_from_efi_time(&eft, tm)) 130 return -EIO; 131 132 return 0; 133 } 134 135 static int efi_set_time(struct device *dev, struct rtc_time *tm) 136 { 137 efi_status_t status; 138 efi_time_t eft; 139 140 convert_to_efi_time(tm, &eft); 141 142 status = efi.set_time(&eft); 143 144 return status == EFI_SUCCESS ? 0 : -EINVAL; 145 } 146 147 static int efi_procfs(struct device *dev, struct seq_file *seq) 148 { 149 efi_time_t eft; 150 efi_time_cap_t cap; 151 152 memset(&eft, 0, sizeof(eft)); 153 memset(&cap, 0, sizeof(cap)); 154 155 efi.get_time(&eft, &cap); 156 157 seq_printf(seq, 158 "Time\t\t: %u:%u:%u.%09u\n" 159 "Date\t\t: %u-%u-%u\n" 160 "Daylight\t: %u\n", 161 eft.hour, eft.minute, eft.second, eft.nanosecond, 162 eft.year, eft.month, eft.day, 163 eft.daylight); 164 165 if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) 166 seq_puts(seq, "Timezone\t: unspecified\n"); 167 else 168 /* XXX fixme: convert to string? */ 169 seq_printf(seq, "Timezone\t: %u\n", eft.timezone); 170 171 /* 172 * now prints the capabilities 173 */ 174 seq_printf(seq, 175 "Resolution\t: %u\n" 176 "Accuracy\t: %u\n" 177 "SetstoZero\t: %u\n", 178 cap.resolution, cap.accuracy, cap.sets_to_zero); 179 180 return 0; 181 } 182 183 static const struct rtc_class_ops efi_rtc_ops = { 184 .read_time = efi_read_time, 185 .set_time = efi_set_time, 186 .proc = efi_procfs, 187 }; 188 189 static int __init efi_rtc_probe(struct platform_device *dev) 190 { 191 struct rtc_device *rtc; 192 efi_time_t eft; 193 efi_time_cap_t cap; 194 195 /* First check if the RTC is usable */ 196 if (efi.get_time(&eft, &cap) != EFI_SUCCESS) 197 return -ENODEV; 198 199 rtc = devm_rtc_allocate_device(&dev->dev); 200 if (IS_ERR(rtc)) 201 return PTR_ERR(rtc); 202 203 platform_set_drvdata(dev, rtc); 204 205 rtc->ops = &efi_rtc_ops; 206 clear_bit(RTC_FEATURE_ALARM, rtc->features); 207 208 device_init_wakeup(&dev->dev, true); 209 210 return devm_rtc_register_device(rtc); 211 } 212 213 static struct platform_driver efi_rtc_driver = { 214 .driver = { 215 .name = "rtc-efi", 216 }, 217 }; 218 219 module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); 220 221 MODULE_AUTHOR("dann frazier <dannf@dannf.org>"); 222 MODULE_LICENSE("GPL"); 223 MODULE_DESCRIPTION("EFI RTC driver"); 224 MODULE_ALIAS("platform:rtc-efi"); 225