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