linux/drivers/rtc/rtc-rtd119x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Realtek RTD129x RTC
   4 *
   5 * Copyright (c) 2017 Andreas F\xC3\xA4rber
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/io.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/of_address.h>
  13#include <linux/platform_device.h>
  14#include <linux/rtc.h>
  15#include <linux/spinlock.h>
  16
  17#define RTD_RTCSEC              0x00
  18#define RTD_RTCMIN              0x04
  19#define RTD_RTCHR               0x08
  20#define RTD_RTCDATE1            0x0c
  21#define RTD_RTCDATE2            0x10
  22#define RTD_RTCACR              0x28
  23#define RTD_RTCEN               0x2c
  24#define RTD_RTCCR               0x30
  25
  26#define RTD_RTCSEC_RTCSEC_MASK          0x7f
  27
  28#define RTD_RTCMIN_RTCMIN_MASK          0x3f
  29
  30#define RTD_RTCHR_RTCHR_MASK            0x1f
  31
  32#define RTD_RTCDATE1_RTCDATE1_MASK      0xff
  33
  34#define RTD_RTCDATE2_RTCDATE2_MASK      0x7f
  35
  36#define RTD_RTCACR_RTCPWR               BIT(7)
  37
  38#define RTD_RTCEN_RTCEN_MASK            0xff
  39
  40#define RTD_RTCCR_RTCRST                BIT(6)
  41
  42struct rtd119x_rtc {
  43        void __iomem *base;
  44        struct clk *clk;
  45        struct rtc_device *rtcdev;
  46        unsigned int base_year;
  47};
  48
  49static inline int rtd119x_rtc_days_in_year(int year)
  50{
  51        return 365 + (is_leap_year(year) ? 1 : 0);
  52}
  53
  54static void rtd119x_rtc_reset(struct device *dev)
  55{
  56        struct rtd119x_rtc *data = dev_get_drvdata(dev);
  57        u32 val;
  58
  59        val = readl_relaxed(data->base + RTD_RTCCR);
  60        val |= RTD_RTCCR_RTCRST;
  61        writel_relaxed(val, data->base + RTD_RTCCR);
  62
  63        val &= ~RTD_RTCCR_RTCRST;
  64        writel(val, data->base + RTD_RTCCR);
  65}
  66
  67static void rtd119x_rtc_set_enabled(struct device *dev, bool enable)
  68{
  69        struct rtd119x_rtc *data = dev_get_drvdata(dev);
  70        u32 val;
  71
  72        val = readl_relaxed(data->base + RTD_RTCEN);
  73        if (enable) {
  74                if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a)
  75                        return;
  76                writel_relaxed(0x5a, data->base + RTD_RTCEN);
  77        } else {
  78                writel_relaxed(0, data->base + RTD_RTCEN);
  79        }
  80}
  81
  82static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm)
  83{
  84        struct rtd119x_rtc *data = dev_get_drvdata(dev);
  85        s32 day;
  86        u32 sec;
  87        unsigned int year;
  88        int tries = 0;
  89
  90        while (true) {
  91                tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1;
  92                tm->tm_min  = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK;
  93                tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK;
  94                day  =  readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK;
  95                day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8;
  96                sec  = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1;
  97                tries++;
  98
  99                if (sec == tm->tm_sec)
 100                        break;
 101
 102                if (tries >= 3)
 103                        return -EINVAL;
 104        }
 105        if (tries > 1)
 106                dev_dbg(dev, "%s: needed %i tries\n", __func__, tries);
 107
 108        year = data->base_year;
 109        while (day >= rtd119x_rtc_days_in_year(year)) {
 110                day -= rtd119x_rtc_days_in_year(year);
 111                year++;
 112        }
 113        tm->tm_year = year - 1900;
 114        tm->tm_yday = day;
 115
 116        tm->tm_mon = 0;
 117        while (day >= rtc_month_days(tm->tm_mon, year)) {
 118                day -= rtc_month_days(tm->tm_mon, year);
 119                tm->tm_mon++;
 120        }
 121        tm->tm_mday = day + 1;
 122
 123        return 0;
 124}
 125
 126static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm)
 127{
 128        struct rtd119x_rtc *data = dev_get_drvdata(dev);
 129        unsigned int day;
 130        int i;
 131
 132        if (1900 + tm->tm_year < data->base_year)
 133                return -EINVAL;
 134
 135        day = 0;
 136        for (i = data->base_year; i < 1900 + tm->tm_year; i++)
 137                day += rtd119x_rtc_days_in_year(i);
 138
 139        day += tm->tm_yday;
 140        if (day > 0x7fff)
 141                return -EINVAL;
 142
 143        rtd119x_rtc_set_enabled(dev, false);
 144
 145        writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC);
 146        writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN);
 147        writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR);
 148        writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1);
 149        writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2);
 150
 151        rtd119x_rtc_set_enabled(dev, true);
 152
 153        return 0;
 154}
 155
 156static const struct rtc_class_ops rtd119x_rtc_ops = {
 157        .read_time      = rtd119x_rtc_read_time,
 158        .set_time       = rtd119x_rtc_set_time,
 159};
 160
 161static const struct of_device_id rtd119x_rtc_dt_ids[] = {
 162         { .compatible = "realtek,rtd1295-rtc" },
 163         { }
 164};
 165
 166static int rtd119x_rtc_probe(struct platform_device *pdev)
 167{
 168        struct rtd119x_rtc *data;
 169        u32 val;
 170        int ret;
 171
 172        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 173        if (!data)
 174                return -ENOMEM;
 175
 176        platform_set_drvdata(pdev, data);
 177        data->base_year = 2014;
 178
 179        data->base = devm_platform_ioremap_resource(pdev, 0);
 180        if (IS_ERR(data->base))
 181                return PTR_ERR(data->base);
 182
 183        data->clk = of_clk_get(pdev->dev.of_node, 0);
 184        if (IS_ERR(data->clk))
 185                return PTR_ERR(data->clk);
 186
 187        ret = clk_prepare_enable(data->clk);
 188        if (ret) {
 189                clk_put(data->clk);
 190                return ret;
 191        }
 192
 193        val = readl_relaxed(data->base + RTD_RTCACR);
 194        if (!(val & RTD_RTCACR_RTCPWR)) {
 195                writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR);
 196
 197                rtd119x_rtc_reset(&pdev->dev);
 198
 199                writel_relaxed(0, data->base + RTD_RTCMIN);
 200                writel_relaxed(0, data->base + RTD_RTCHR);
 201                writel_relaxed(0, data->base + RTD_RTCDATE1);
 202                writel_relaxed(0, data->base + RTD_RTCDATE2);
 203        }
 204
 205        rtd119x_rtc_set_enabled(&pdev->dev, true);
 206
 207        data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc",
 208                                                &rtd119x_rtc_ops, THIS_MODULE);
 209        if (IS_ERR(data->rtcdev)) {
 210                dev_err(&pdev->dev, "failed to register rtc device");
 211                clk_disable_unprepare(data->clk);
 212                clk_put(data->clk);
 213                return PTR_ERR(data->rtcdev);
 214        }
 215
 216        return 0;
 217}
 218
 219static int rtd119x_rtc_remove(struct platform_device *pdev)
 220{
 221        struct rtd119x_rtc *data = platform_get_drvdata(pdev);
 222
 223        rtd119x_rtc_set_enabled(&pdev->dev, false);
 224
 225        clk_disable_unprepare(data->clk);
 226        clk_put(data->clk);
 227
 228        return 0;
 229}
 230
 231static struct platform_driver rtd119x_rtc_driver = {
 232        .probe = rtd119x_rtc_probe,
 233        .remove = rtd119x_rtc_remove,
 234        .driver = {
 235                .name = "rtd1295-rtc",
 236                .of_match_table = rtd119x_rtc_dt_ids,
 237        },
 238};
 239builtin_platform_driver(rtd119x_rtc_driver);
 240