root/drivers/rtc/rtc-efi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. compute_yday
  2. compute_wday
  3. convert_to_efi_time
  4. convert_from_efi_time
  5. efi_read_alarm
  6. efi_set_alarm
  7. efi_read_time
  8. efi_set_time
  9. efi_procfs
  10. efi_rtc_probe

   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_ALIAS("platform:rtc-efi");
 284 MODULE_AUTHOR("dann frazier <dannf@dannf.org>");
 285 MODULE_LICENSE("GPL");
 286 MODULE_DESCRIPTION("EFI RTC driver");
 287 MODULE_ALIAS("platform:rtc-efi");

/* [<][>][^][v][top][bottom][index][help] */