linux/drivers/rtc/rtc-asm9260.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 Oleksij Rempel <linux@rempel-privat.de>
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/interrupt.h>
   8#include <linux/io.h>
   9#include <linux/module.h>
  10#include <linux/of.h>
  11#include <linux/platform_device.h>
  12#include <linux/rtc.h>
  13
  14/* Miscellaneous registers */
  15/* Interrupt Location Register */
  16#define HW_ILR                  0x00
  17#define BM_RTCALF               BIT(1)
  18#define BM_RTCCIF               BIT(0)
  19
  20/* Clock Control Register */
  21#define HW_CCR                  0x08
  22/* Calibration counter disable */
  23#define BM_CCALOFF              BIT(4)
  24/* Reset internal oscillator divider */
  25#define BM_CTCRST               BIT(1)
  26/* Clock Enable */
  27#define BM_CLKEN                BIT(0)
  28
  29/* Counter Increment Interrupt Register */
  30#define HW_CIIR                 0x0C
  31#define BM_CIIR_IMYEAR          BIT(7)
  32#define BM_CIIR_IMMON           BIT(6)
  33#define BM_CIIR_IMDOY           BIT(5)
  34#define BM_CIIR_IMDOW           BIT(4)
  35#define BM_CIIR_IMDOM           BIT(3)
  36#define BM_CIIR_IMHOUR          BIT(2)
  37#define BM_CIIR_IMMIN           BIT(1)
  38#define BM_CIIR_IMSEC           BIT(0)
  39
  40/* Alarm Mask Register */
  41#define HW_AMR                  0x10
  42#define BM_AMR_IMYEAR           BIT(7)
  43#define BM_AMR_IMMON            BIT(6)
  44#define BM_AMR_IMDOY            BIT(5)
  45#define BM_AMR_IMDOW            BIT(4)
  46#define BM_AMR_IMDOM            BIT(3)
  47#define BM_AMR_IMHOUR           BIT(2)
  48#define BM_AMR_IMMIN            BIT(1)
  49#define BM_AMR_IMSEC            BIT(0)
  50#define BM_AMR_OFF              0xff
  51
  52/* Consolidated time registers */
  53#define HW_CTIME0               0x14
  54#define BM_CTIME0_DOW_S         24
  55#define BM_CTIME0_DOW_M         0x7
  56#define BM_CTIME0_HOUR_S        16
  57#define BM_CTIME0_HOUR_M        0x1f
  58#define BM_CTIME0_MIN_S         8
  59#define BM_CTIME0_MIN_M         0x3f
  60#define BM_CTIME0_SEC_S         0
  61#define BM_CTIME0_SEC_M         0x3f
  62
  63#define HW_CTIME1               0x18
  64#define BM_CTIME1_YEAR_S        16
  65#define BM_CTIME1_YEAR_M        0xfff
  66#define BM_CTIME1_MON_S         8
  67#define BM_CTIME1_MON_M         0xf
  68#define BM_CTIME1_DOM_S         0
  69#define BM_CTIME1_DOM_M         0x1f
  70
  71#define HW_CTIME2               0x1C
  72#define BM_CTIME2_DOY_S         0
  73#define BM_CTIME2_DOY_M         0xfff
  74
  75/* Time counter registers */
  76#define HW_SEC                  0x20
  77#define HW_MIN                  0x24
  78#define HW_HOUR                 0x28
  79#define HW_DOM                  0x2C
  80#define HW_DOW                  0x30
  81#define HW_DOY                  0x34
  82#define HW_MONTH                0x38
  83#define HW_YEAR                 0x3C
  84
  85#define HW_CALIBRATION          0x40
  86#define BM_CALDIR_BACK          BIT(17)
  87#define BM_CALVAL_M             0x1ffff
  88
  89/* General purpose registers */
  90#define HW_GPREG0               0x44
  91#define HW_GPREG1               0x48
  92#define HW_GPREG2               0x4C
  93#define HW_GPREG3               0x50
  94#define HW_GPREG4               0x54
  95
  96/* Alarm register group */
  97#define HW_ALSEC                0x60
  98#define HW_ALMIN                0x64
  99#define HW_ALHOUR               0x68
 100#define HW_ALDOM                0x6C
 101#define HW_ALDOW                0x70
 102#define HW_ALDOY                0x74
 103#define HW_ALMON                0x78
 104#define HW_ALYEAR               0x7C
 105
 106struct asm9260_rtc_priv {
 107        struct device           *dev;
 108        void __iomem            *iobase;
 109        struct rtc_device       *rtc;
 110        struct clk              *clk;
 111};
 112
 113static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id)
 114{
 115        struct asm9260_rtc_priv *priv = dev_id;
 116        u32 isr;
 117        unsigned long events = 0;
 118
 119        rtc_lock(priv->rtc);
 120        isr = ioread32(priv->iobase + HW_CIIR);
 121        if (!isr) {
 122                rtc_unlock(priv->rtc);
 123                return IRQ_NONE;
 124        }
 125
 126        iowrite32(0, priv->iobase + HW_CIIR);
 127        rtc_unlock(priv->rtc);
 128
 129        events |= RTC_AF | RTC_IRQF;
 130
 131        rtc_update_irq(priv->rtc, 1, events);
 132
 133        return IRQ_HANDLED;
 134}
 135
 136static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
 137{
 138        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
 139        u32 ctime0, ctime1, ctime2;
 140
 141        ctime0 = ioread32(priv->iobase + HW_CTIME0);
 142        ctime1 = ioread32(priv->iobase + HW_CTIME1);
 143        ctime2 = ioread32(priv->iobase + HW_CTIME2);
 144
 145        if (ctime1 != ioread32(priv->iobase + HW_CTIME1)) {
 146                /*
 147                 * woops, counter flipped right now. Now we are safe
 148                 * to reread.
 149                 */
 150                ctime0 = ioread32(priv->iobase + HW_CTIME0);
 151                ctime1 = ioread32(priv->iobase + HW_CTIME1);
 152                ctime2 = ioread32(priv->iobase + HW_CTIME2);
 153        }
 154
 155        tm->tm_sec  = (ctime0 >> BM_CTIME0_SEC_S)  & BM_CTIME0_SEC_M;
 156        tm->tm_min  = (ctime0 >> BM_CTIME0_MIN_S)  & BM_CTIME0_MIN_M;
 157        tm->tm_hour = (ctime0 >> BM_CTIME0_HOUR_S) & BM_CTIME0_HOUR_M;
 158        tm->tm_wday = (ctime0 >> BM_CTIME0_DOW_S)  & BM_CTIME0_DOW_M;
 159
 160        tm->tm_mday = (ctime1 >> BM_CTIME1_DOM_S)  & BM_CTIME1_DOM_M;
 161        tm->tm_mon  = (ctime1 >> BM_CTIME1_MON_S)  & BM_CTIME1_MON_M;
 162        tm->tm_year = (ctime1 >> BM_CTIME1_YEAR_S) & BM_CTIME1_YEAR_M;
 163
 164        tm->tm_yday = (ctime2 >> BM_CTIME2_DOY_S)  & BM_CTIME2_DOY_M;
 165
 166        return 0;
 167}
 168
 169static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
 170{
 171        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
 172
 173        /*
 174         * make sure SEC counter will not flip other counter on write time,
 175         * real value will be written at the enf of sequence.
 176         */
 177        iowrite32(0, priv->iobase + HW_SEC);
 178
 179        iowrite32(tm->tm_year, priv->iobase + HW_YEAR);
 180        iowrite32(tm->tm_mon,  priv->iobase + HW_MONTH);
 181        iowrite32(tm->tm_mday, priv->iobase + HW_DOM);
 182        iowrite32(tm->tm_wday, priv->iobase + HW_DOW);
 183        iowrite32(tm->tm_yday, priv->iobase + HW_DOY);
 184        iowrite32(tm->tm_hour, priv->iobase + HW_HOUR);
 185        iowrite32(tm->tm_min,  priv->iobase + HW_MIN);
 186        iowrite32(tm->tm_sec,  priv->iobase + HW_SEC);
 187
 188        return 0;
 189}
 190
 191static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 192{
 193        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
 194
 195        alrm->time.tm_year = ioread32(priv->iobase + HW_ALYEAR);
 196        alrm->time.tm_mon  = ioread32(priv->iobase + HW_ALMON);
 197        alrm->time.tm_mday = ioread32(priv->iobase + HW_ALDOM);
 198        alrm->time.tm_wday = ioread32(priv->iobase + HW_ALDOW);
 199        alrm->time.tm_yday = ioread32(priv->iobase + HW_ALDOY);
 200        alrm->time.tm_hour = ioread32(priv->iobase + HW_ALHOUR);
 201        alrm->time.tm_min  = ioread32(priv->iobase + HW_ALMIN);
 202        alrm->time.tm_sec  = ioread32(priv->iobase + HW_ALSEC);
 203
 204        alrm->enabled = ioread32(priv->iobase + HW_AMR) ? 1 : 0;
 205        alrm->pending = ioread32(priv->iobase + HW_CIIR) ? 1 : 0;
 206
 207        return rtc_valid_tm(&alrm->time);
 208}
 209
 210static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 211{
 212        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
 213
 214        iowrite32(alrm->time.tm_year, priv->iobase + HW_ALYEAR);
 215        iowrite32(alrm->time.tm_mon,  priv->iobase + HW_ALMON);
 216        iowrite32(alrm->time.tm_mday, priv->iobase + HW_ALDOM);
 217        iowrite32(alrm->time.tm_wday, priv->iobase + HW_ALDOW);
 218        iowrite32(alrm->time.tm_yday, priv->iobase + HW_ALDOY);
 219        iowrite32(alrm->time.tm_hour, priv->iobase + HW_ALHOUR);
 220        iowrite32(alrm->time.tm_min,  priv->iobase + HW_ALMIN);
 221        iowrite32(alrm->time.tm_sec,  priv->iobase + HW_ALSEC);
 222
 223        iowrite32(alrm->enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR);
 224
 225        return 0;
 226}
 227
 228static int asm9260_alarm_irq_enable(struct device *dev, unsigned int enabled)
 229{
 230        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
 231
 232        iowrite32(enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR);
 233        return 0;
 234}
 235
 236static const struct rtc_class_ops asm9260_rtc_ops = {
 237        .read_time              = asm9260_rtc_read_time,
 238        .set_time               = asm9260_rtc_set_time,
 239        .read_alarm             = asm9260_rtc_read_alarm,
 240        .set_alarm              = asm9260_rtc_set_alarm,
 241        .alarm_irq_enable       = asm9260_alarm_irq_enable,
 242};
 243
 244static int asm9260_rtc_probe(struct platform_device *pdev)
 245{
 246        struct asm9260_rtc_priv *priv;
 247        struct device *dev = &pdev->dev;
 248        int irq_alarm, ret;
 249        u32 ccr;
 250
 251        priv = devm_kzalloc(dev, sizeof(struct asm9260_rtc_priv), GFP_KERNEL);
 252        if (!priv)
 253                return -ENOMEM;
 254
 255        priv->dev = &pdev->dev;
 256        platform_set_drvdata(pdev, priv);
 257
 258        irq_alarm = platform_get_irq(pdev, 0);
 259        if (irq_alarm < 0)
 260                return irq_alarm;
 261
 262        priv->iobase = devm_platform_ioremap_resource(pdev, 0);
 263        if (IS_ERR(priv->iobase))
 264                return PTR_ERR(priv->iobase);
 265
 266        priv->clk = devm_clk_get(dev, "ahb");
 267        if (IS_ERR(priv->clk))
 268                return PTR_ERR(priv->clk);
 269
 270        ret = clk_prepare_enable(priv->clk);
 271        if (ret) {
 272                dev_err(dev, "Failed to enable clk!\n");
 273                return ret;
 274        }
 275
 276        ccr = ioread32(priv->iobase + HW_CCR);
 277        /* if dev is not enabled, reset it */
 278        if ((ccr & (BM_CLKEN | BM_CTCRST)) != BM_CLKEN) {
 279                iowrite32(BM_CTCRST, priv->iobase + HW_CCR);
 280                ccr = 0;
 281        }
 282
 283        iowrite32(BM_CLKEN | ccr, priv->iobase + HW_CCR);
 284        iowrite32(0, priv->iobase + HW_CIIR);
 285        iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR);
 286
 287        priv->rtc = devm_rtc_device_register(dev, dev_name(dev),
 288                                             &asm9260_rtc_ops, THIS_MODULE);
 289        if (IS_ERR(priv->rtc)) {
 290                ret = PTR_ERR(priv->rtc);
 291                dev_err(dev, "Failed to register RTC device: %d\n", ret);
 292                goto err_return;
 293        }
 294
 295        ret = devm_request_threaded_irq(dev, irq_alarm, NULL,
 296                                        asm9260_rtc_irq, IRQF_ONESHOT,
 297                                        dev_name(dev), priv);
 298        if (ret < 0) {
 299                dev_err(dev, "can't get irq %i, err %d\n",
 300                        irq_alarm, ret);
 301                goto err_return;
 302        }
 303
 304        return 0;
 305
 306err_return:
 307        clk_disable_unprepare(priv->clk);
 308        return ret;
 309}
 310
 311static int asm9260_rtc_remove(struct platform_device *pdev)
 312{
 313        struct asm9260_rtc_priv *priv = platform_get_drvdata(pdev);
 314
 315        /* Disable alarm matching */
 316        iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR);
 317        clk_disable_unprepare(priv->clk);
 318        return 0;
 319}
 320
 321static const struct of_device_id asm9260_dt_ids[] = {
 322        { .compatible = "alphascale,asm9260-rtc", },
 323        {}
 324};
 325MODULE_DEVICE_TABLE(of, asm9260_dt_ids);
 326
 327static struct platform_driver asm9260_rtc_driver = {
 328        .probe          = asm9260_rtc_probe,
 329        .remove         = asm9260_rtc_remove,
 330        .driver         = {
 331                .name   = "asm9260-rtc",
 332                .of_match_table = asm9260_dt_ids,
 333        },
 334};
 335
 336module_platform_driver(asm9260_rtc_driver);
 337
 338MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
 339MODULE_DESCRIPTION("Alphascale asm9260 SoC Realtime Clock Driver (RTC)");
 340MODULE_LICENSE("GPL");
 341