linux/drivers/rtc/rtc-imx-sc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2018 NXP.
   4 */
   5
   6#include <dt-bindings/firmware/imx/rsrc.h>
   7#include <linux/arm-smccc.h>
   8#include <linux/firmware/imx/sci.h>
   9#include <linux/module.h>
  10#include <linux/of.h>
  11#include <linux/platform_device.h>
  12#include <linux/rtc.h>
  13
  14#define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970       9
  15#define IMX_SC_TIMER_FUNC_SET_RTC_ALARM         8
  16#define IMX_SC_TIMER_FUNC_SET_RTC_TIME          6
  17
  18#define IMX_SIP_SRTC                    0xC2000002
  19#define IMX_SIP_SRTC_SET_TIME           0x0
  20
  21#define SC_IRQ_GROUP_RTC    2
  22#define SC_IRQ_RTC          1
  23
  24static struct imx_sc_ipc *rtc_ipc_handle;
  25static struct rtc_device *imx_sc_rtc;
  26
  27struct imx_sc_msg_timer_get_rtc_time {
  28        struct imx_sc_rpc_msg hdr;
  29        u32 time;
  30} __packed;
  31
  32struct imx_sc_msg_timer_rtc_set_alarm {
  33        struct imx_sc_rpc_msg hdr;
  34        u16 year;
  35        u8 mon;
  36        u8 day;
  37        u8 hour;
  38        u8 min;
  39        u8 sec;
  40} __packed;
  41
  42static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm)
  43{
  44        struct imx_sc_msg_timer_get_rtc_time msg;
  45        struct imx_sc_rpc_msg *hdr = &msg.hdr;
  46        int ret;
  47
  48        hdr->ver = IMX_SC_RPC_VERSION;
  49        hdr->svc = IMX_SC_RPC_SVC_TIMER;
  50        hdr->func = IMX_SC_TIMER_FUNC_GET_RTC_SEC1970;
  51        hdr->size = 1;
  52
  53        ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true);
  54        if (ret) {
  55                dev_err(dev, "read rtc time failed, ret %d\n", ret);
  56                return ret;
  57        }
  58
  59        rtc_time64_to_tm(msg.time, tm);
  60
  61        return 0;
  62}
  63
  64static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm)
  65{
  66        struct arm_smccc_res res;
  67
  68        /* pack 2 time parameters into 1 register, 16 bits for each */
  69        arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME,
  70                      ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1),
  71                      (tm->tm_mday << 16) | tm->tm_hour,
  72                      (tm->tm_min << 16) | tm->tm_sec,
  73                      0, 0, 0, &res);
  74
  75        return res.a0;
  76}
  77
  78static int imx_sc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
  79{
  80        return imx_scu_irq_group_enable(SC_IRQ_GROUP_RTC, SC_IRQ_RTC, enable);
  81}
  82
  83static int imx_sc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  84{
  85        /*
  86         * SCU firmware does NOT provide read alarm API, but .read_alarm
  87         * callback is required by RTC framework to support alarm function,
  88         * so just return here.
  89         */
  90        return 0;
  91}
  92
  93static int imx_sc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  94{
  95        struct imx_sc_msg_timer_rtc_set_alarm msg;
  96        struct imx_sc_rpc_msg *hdr = &msg.hdr;
  97        int ret;
  98        struct rtc_time *alrm_tm = &alrm->time;
  99
 100        hdr->ver = IMX_SC_RPC_VERSION;
 101        hdr->svc = IMX_SC_RPC_SVC_TIMER;
 102        hdr->func = IMX_SC_TIMER_FUNC_SET_RTC_ALARM;
 103        hdr->size = 3;
 104
 105        msg.year = alrm_tm->tm_year + 1900;
 106        msg.mon = alrm_tm->tm_mon + 1;
 107        msg.day = alrm_tm->tm_mday;
 108        msg.hour = alrm_tm->tm_hour;
 109        msg.min = alrm_tm->tm_min;
 110        msg.sec = alrm_tm->tm_sec;
 111
 112        ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true);
 113        if (ret) {
 114                dev_err(dev, "set rtc alarm failed, ret %d\n", ret);
 115                return ret;
 116        }
 117
 118        ret = imx_sc_rtc_alarm_irq_enable(dev, alrm->enabled);
 119        if (ret) {
 120                dev_err(dev, "enable rtc alarm failed, ret %d\n", ret);
 121                return ret;
 122        }
 123
 124        return 0;
 125}
 126
 127static const struct rtc_class_ops imx_sc_rtc_ops = {
 128        .read_time = imx_sc_rtc_read_time,
 129        .set_time = imx_sc_rtc_set_time,
 130        .read_alarm = imx_sc_rtc_read_alarm,
 131        .set_alarm = imx_sc_rtc_set_alarm,
 132        .alarm_irq_enable = imx_sc_rtc_alarm_irq_enable,
 133};
 134
 135static int imx_sc_rtc_alarm_notify(struct notifier_block *nb,
 136                                        unsigned long event, void *group)
 137{
 138        /* ignore non-rtc irq */
 139        if (!((event & SC_IRQ_RTC) && (*(u8 *)group == SC_IRQ_GROUP_RTC)))
 140                return 0;
 141
 142        rtc_update_irq(imx_sc_rtc, 1, RTC_IRQF | RTC_AF);
 143
 144        return 0;
 145}
 146
 147static struct notifier_block imx_sc_rtc_alarm_sc_notifier = {
 148        .notifier_call = imx_sc_rtc_alarm_notify,
 149};
 150
 151static int imx_sc_rtc_probe(struct platform_device *pdev)
 152{
 153        int ret;
 154
 155        ret = imx_scu_get_handle(&rtc_ipc_handle);
 156        if (ret)
 157                return ret;
 158
 159        device_init_wakeup(&pdev->dev, true);
 160
 161        imx_sc_rtc = devm_rtc_allocate_device(&pdev->dev);
 162        if (IS_ERR(imx_sc_rtc))
 163                return PTR_ERR(imx_sc_rtc);
 164
 165        imx_sc_rtc->ops = &imx_sc_rtc_ops;
 166        imx_sc_rtc->range_min = 0;
 167        imx_sc_rtc->range_max = U32_MAX;
 168
 169        ret = rtc_register_device(imx_sc_rtc);
 170        if (ret) {
 171                dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
 172                return ret;
 173        }
 174
 175        imx_scu_irq_register_notifier(&imx_sc_rtc_alarm_sc_notifier);
 176
 177        return 0;
 178}
 179
 180static const struct of_device_id imx_sc_dt_ids[] = {
 181        { .compatible = "fsl,imx8qxp-sc-rtc", },
 182        {}
 183};
 184MODULE_DEVICE_TABLE(of, imx_sc_dt_ids);
 185
 186static struct platform_driver imx_sc_rtc_driver = {
 187        .driver = {
 188                .name   = "imx-sc-rtc",
 189                .of_match_table = imx_sc_dt_ids,
 190        },
 191        .probe          = imx_sc_rtc_probe,
 192};
 193module_platform_driver(imx_sc_rtc_driver);
 194
 195MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
 196MODULE_DESCRIPTION("NXP i.MX System Controller RTC Driver");
 197MODULE_LICENSE("GPL");
 198