linux/drivers/rtc/rtc-opal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * IBM OPAL RTC driver
   4 * Copyright (C) 2014 IBM
   5 */
   6
   7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   8
   9#define DRVNAME         "rtc-opal"
  10
  11#include <linux/module.h>
  12#include <linux/err.h>
  13#include <linux/rtc.h>
  14#include <linux/delay.h>
  15#include <linux/bcd.h>
  16#include <linux/platform_device.h>
  17#include <linux/of.h>
  18#include <asm/opal.h>
  19#include <asm/firmware.h>
  20
  21static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
  22{
  23        tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) +
  24                       bcd2bin((y_m_d >> 16) & 0xff)) - 1900;
  25        tm->tm_mon  = bcd2bin((y_m_d >> 8) & 0xff) - 1;
  26        tm->tm_mday = bcd2bin(y_m_d & 0xff);
  27        tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff);
  28        tm->tm_min  = bcd2bin((h_m_s_ms >> 48) & 0xff);
  29        tm->tm_sec  = bcd2bin((h_m_s_ms >> 40) & 0xff);
  30
  31        tm->tm_wday = -1;
  32}
  33
  34static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms)
  35{
  36        *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
  37        *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
  38        *y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
  39        *y_m_d |= ((u32)bin2bcd(tm->tm_mday));
  40
  41        *h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
  42        *h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
  43        *h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
  44}
  45
  46static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
  47{
  48        s64 rc = OPAL_BUSY;
  49        int retries = 10;
  50        u32 y_m_d;
  51        u64 h_m_s_ms;
  52        __be32 __y_m_d;
  53        __be64 __h_m_s_ms;
  54
  55        while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
  56                rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
  57                if (rc == OPAL_BUSY_EVENT) {
  58                        msleep(OPAL_BUSY_DELAY_MS);
  59                        opal_poll_events(NULL);
  60                } else if (rc == OPAL_BUSY) {
  61                        msleep(OPAL_BUSY_DELAY_MS);
  62                } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
  63                        if (retries--) {
  64                                msleep(10); /* Wait 10ms before retry */
  65                                rc = OPAL_BUSY; /* go around again */
  66                        }
  67                }
  68        }
  69
  70        if (rc != OPAL_SUCCESS)
  71                return -EIO;
  72
  73        y_m_d = be32_to_cpu(__y_m_d);
  74        h_m_s_ms = be64_to_cpu(__h_m_s_ms);
  75        opal_to_tm(y_m_d, h_m_s_ms, tm);
  76
  77        return 0;
  78}
  79
  80static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
  81{
  82        s64 rc = OPAL_BUSY;
  83        int retries = 10;
  84        u32 y_m_d = 0;
  85        u64 h_m_s_ms = 0;
  86
  87        tm_to_opal(tm, &y_m_d, &h_m_s_ms);
  88
  89        while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
  90                rc = opal_rtc_write(y_m_d, h_m_s_ms);
  91                if (rc == OPAL_BUSY_EVENT) {
  92                        msleep(OPAL_BUSY_DELAY_MS);
  93                        opal_poll_events(NULL);
  94                } else if (rc == OPAL_BUSY) {
  95                        msleep(OPAL_BUSY_DELAY_MS);
  96                } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
  97                        if (retries--) {
  98                                msleep(10); /* Wait 10ms before retry */
  99                                rc = OPAL_BUSY; /* go around again */
 100                        }
 101                }
 102        }
 103
 104        return rc == OPAL_SUCCESS ? 0 : -EIO;
 105}
 106
 107/*
 108 * TPO  Timed Power-On
 109 *
 110 * TPO get/set OPAL calls care about the hour and min and to make it consistent
 111 * with the rtc utility time conversion functions, we use the 'u64' to store
 112 * its value and perform bit shift by 32 before use..
 113 */
 114static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
 115{
 116        __be32 __y_m_d, __h_m;
 117        struct opal_msg msg;
 118        int rc, token;
 119        u64 h_m_s_ms;
 120        u32 y_m_d;
 121
 122        token = opal_async_get_token_interruptible();
 123        if (token < 0) {
 124                if (token != -ERESTARTSYS)
 125                        pr_err("Failed to get the async token\n");
 126
 127                return token;
 128        }
 129
 130        rc = opal_tpo_read(token, &__y_m_d, &__h_m);
 131        if (rc != OPAL_ASYNC_COMPLETION) {
 132                rc = -EIO;
 133                goto exit;
 134        }
 135
 136        rc = opal_async_wait_response(token, &msg);
 137        if (rc) {
 138                rc = -EIO;
 139                goto exit;
 140        }
 141
 142        rc = opal_get_async_rc(msg);
 143        if (rc != OPAL_SUCCESS) {
 144                rc = -EIO;
 145                goto exit;
 146        }
 147
 148        y_m_d = be32_to_cpu(__y_m_d);
 149        h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
 150
 151        /* check if no alarm is set */
 152        if (y_m_d == 0 && h_m_s_ms == 0) {
 153                pr_debug("No alarm is set\n");
 154                rc = -ENOENT;
 155                goto exit;
 156        } else {
 157                pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
 158        }
 159
 160        opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
 161
 162exit:
 163        opal_async_release_token(token);
 164        return rc;
 165}
 166
 167/* Set Timed Power-On */
 168static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
 169{
 170        u64 h_m_s_ms = 0;
 171        struct opal_msg msg;
 172        u32 y_m_d = 0;
 173        int token, rc;
 174
 175        /* if alarm is enabled */
 176        if (alarm->enabled) {
 177                tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
 178                pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
 179
 180        } else {
 181                pr_debug("Alarm getting disabled\n");
 182        }
 183
 184        token = opal_async_get_token_interruptible();
 185        if (token < 0) {
 186                if (token != -ERESTARTSYS)
 187                        pr_err("Failed to get the async token\n");
 188
 189                return token;
 190        }
 191
 192        /* TPO, we care about hour and minute */
 193        rc = opal_tpo_write(token, y_m_d,
 194                            (u32)((h_m_s_ms >> 32) & 0xffff0000));
 195        if (rc != OPAL_ASYNC_COMPLETION) {
 196                rc = -EIO;
 197                goto exit;
 198        }
 199
 200        rc = opal_async_wait_response(token, &msg);
 201        if (rc) {
 202                rc = -EIO;
 203                goto exit;
 204        }
 205
 206        rc = opal_get_async_rc(msg);
 207        if (rc != OPAL_SUCCESS)
 208                rc = -EIO;
 209
 210exit:
 211        opal_async_release_token(token);
 212        return rc;
 213}
 214
 215static int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled)
 216{
 217        struct rtc_wkalrm alarm = { .enabled = 0 };
 218
 219        /*
 220         * TPO is automatically enabled when opal_set_tpo_time() is called with
 221         * non-zero rtc-time. We only handle disable case which needs to be
 222         * explicitly told to opal.
 223         */
 224        return enabled ? 0 : opal_set_tpo_time(dev, &alarm);
 225}
 226
 227static struct rtc_class_ops opal_rtc_ops = {
 228        .read_time      = opal_get_rtc_time,
 229        .set_time       = opal_set_rtc_time,
 230};
 231
 232static int opal_rtc_probe(struct platform_device *pdev)
 233{
 234        struct rtc_device *rtc;
 235
 236        if (pdev->dev.of_node &&
 237            (of_property_read_bool(pdev->dev.of_node, "wakeup-source") ||
 238             of_property_read_bool(pdev->dev.of_node, "has-tpo")/* legacy */)) {
 239                device_set_wakeup_capable(&pdev->dev, true);
 240                opal_rtc_ops.read_alarm = opal_get_tpo_time;
 241                opal_rtc_ops.set_alarm = opal_set_tpo_time;
 242                opal_rtc_ops.alarm_irq_enable = opal_tpo_alarm_irq_enable;
 243        }
 244
 245        rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops,
 246                                       THIS_MODULE);
 247        if (IS_ERR(rtc))
 248                return PTR_ERR(rtc);
 249
 250        rtc->uie_unsupported = 1;
 251
 252        return 0;
 253}
 254
 255static const struct of_device_id opal_rtc_match[] = {
 256        {
 257                .compatible     = "ibm,opal-rtc",
 258        },
 259        { }
 260};
 261MODULE_DEVICE_TABLE(of, opal_rtc_match);
 262
 263static const struct platform_device_id opal_rtc_driver_ids[] = {
 264        {
 265                .name           = "opal-rtc",
 266        },
 267        { }
 268};
 269MODULE_DEVICE_TABLE(platform, opal_rtc_driver_ids);
 270
 271static struct platform_driver opal_rtc_driver = {
 272        .probe          = opal_rtc_probe,
 273        .id_table       = opal_rtc_driver_ids,
 274        .driver         = {
 275                .name           = DRVNAME,
 276                .of_match_table = opal_rtc_match,
 277        },
 278};
 279
 280static int __init opal_rtc_init(void)
 281{
 282        if (!firmware_has_feature(FW_FEATURE_OPAL))
 283                return -ENODEV;
 284
 285        return platform_driver_register(&opal_rtc_driver);
 286}
 287
 288static void __exit opal_rtc_exit(void)
 289{
 290        platform_driver_unregister(&opal_rtc_driver);
 291}
 292
 293MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
 294MODULE_DESCRIPTION("IBM OPAL RTC driver");
 295MODULE_LICENSE("GPL");
 296
 297module_init(opal_rtc_init);
 298module_exit(opal_rtc_exit);
 299