linux/drivers/rtc/rtc-pic32.c
<<
>>
Prefs
   1/*
   2 * PIC32 RTC driver
   3 *
   4 * Joshua Henderson <joshua.henderson@microchip.com>
   5 * Copyright (C) 2016 Microchip Technology Inc.  All rights reserved.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful, but WITHOUT
  13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15 * more details.
  16 */
  17#include <linux/init.h>
  18#include <linux/module.h>
  19#include <linux/of.h>
  20#include <linux/platform_device.h>
  21#include <linux/io.h>
  22#include <linux/slab.h>
  23#include <linux/clk.h>
  24#include <linux/rtc.h>
  25#include <linux/bcd.h>
  26
  27#include <asm/mach-pic32/pic32.h>
  28
  29#define PIC32_RTCCON            0x00
  30#define PIC32_RTCCON_ON         BIT(15)
  31#define PIC32_RTCCON_SIDL       BIT(13)
  32#define PIC32_RTCCON_RTCCLKSEL  (3 << 9)
  33#define PIC32_RTCCON_RTCCLKON   BIT(6)
  34#define PIC32_RTCCON_RTCWREN    BIT(3)
  35#define PIC32_RTCCON_RTCSYNC    BIT(2)
  36#define PIC32_RTCCON_HALFSEC    BIT(1)
  37#define PIC32_RTCCON_RTCOE      BIT(0)
  38
  39#define PIC32_RTCALRM           0x10
  40#define PIC32_RTCALRM_ALRMEN    BIT(15)
  41#define PIC32_RTCALRM_CHIME     BIT(14)
  42#define PIC32_RTCALRM_PIV       BIT(13)
  43#define PIC32_RTCALRM_ALARMSYNC BIT(12)
  44#define PIC32_RTCALRM_AMASK     0x0F00
  45#define PIC32_RTCALRM_ARPT      0xFF
  46
  47#define PIC32_RTCHOUR           0x23
  48#define PIC32_RTCMIN            0x22
  49#define PIC32_RTCSEC            0x21
  50#define PIC32_RTCYEAR           0x33
  51#define PIC32_RTCMON            0x32
  52#define PIC32_RTCDAY            0x31
  53
  54#define PIC32_ALRMTIME          0x40
  55#define PIC32_ALRMDATE          0x50
  56
  57#define PIC32_ALRMHOUR          0x43
  58#define PIC32_ALRMMIN           0x42
  59#define PIC32_ALRMSEC           0x41
  60#define PIC32_ALRMYEAR          0x53
  61#define PIC32_ALRMMON           0x52
  62#define PIC32_ALRMDAY           0x51
  63
  64struct pic32_rtc_dev {
  65        struct rtc_device       *rtc;
  66        void __iomem            *reg_base;
  67        struct clk              *clk;
  68        spinlock_t              alarm_lock;
  69        int                     alarm_irq;
  70        bool                    alarm_clk_enabled;
  71};
  72
  73static void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata,
  74                                       bool enable)
  75{
  76        unsigned long flags;
  77
  78        spin_lock_irqsave(&pdata->alarm_lock, flags);
  79        if (enable) {
  80                if (!pdata->alarm_clk_enabled) {
  81                        clk_enable(pdata->clk);
  82                        pdata->alarm_clk_enabled = true;
  83                }
  84        } else {
  85                if (pdata->alarm_clk_enabled) {
  86                        clk_disable(pdata->clk);
  87                        pdata->alarm_clk_enabled = false;
  88                }
  89        }
  90        spin_unlock_irqrestore(&pdata->alarm_lock, flags);
  91}
  92
  93static irqreturn_t pic32_rtc_alarmirq(int irq, void *id)
  94{
  95        struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id;
  96
  97        clk_enable(pdata->clk);
  98        rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);
  99        clk_disable(pdata->clk);
 100
 101        pic32_rtc_alarm_clk_enable(pdata, false);
 102
 103        return IRQ_HANDLED;
 104}
 105
 106static int pic32_rtc_setaie(struct device *dev, unsigned int enabled)
 107{
 108        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
 109        void __iomem *base = pdata->reg_base;
 110
 111        clk_enable(pdata->clk);
 112
 113        writel(PIC32_RTCALRM_ALRMEN,
 114               base + (enabled ? PIC32_SET(PIC32_RTCALRM) :
 115                       PIC32_CLR(PIC32_RTCALRM)));
 116
 117        clk_disable(pdata->clk);
 118
 119        pic32_rtc_alarm_clk_enable(pdata, enabled);
 120
 121        return 0;
 122}
 123
 124static int pic32_rtc_setfreq(struct device *dev, int freq)
 125{
 126        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
 127        void __iomem *base = pdata->reg_base;
 128
 129        clk_enable(pdata->clk);
 130
 131        writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM));
 132        writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM));
 133        writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM));
 134
 135        clk_disable(pdata->clk);
 136
 137        return 0;
 138}
 139
 140static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
 141{
 142        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
 143        void __iomem *base = pdata->reg_base;
 144        unsigned int tries = 0;
 145
 146        clk_enable(pdata->clk);
 147
 148        do {
 149                rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR);
 150                rtc_tm->tm_min = readb(base + PIC32_RTCMIN);
 151                rtc_tm->tm_mon  = readb(base + PIC32_RTCMON);
 152                rtc_tm->tm_mday = readb(base + PIC32_RTCDAY);
 153                rtc_tm->tm_year = readb(base + PIC32_RTCYEAR);
 154                rtc_tm->tm_sec  = readb(base + PIC32_RTCSEC);
 155
 156                /*
 157                 * The only way to work out whether the system was mid-update
 158                 * when we read it is to check the second counter, and if it
 159                 * is zero, then we re-try the entire read.
 160                 */
 161                tries += 1;
 162        } while (rtc_tm->tm_sec == 0 && tries < 2);
 163
 164        rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
 165        rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
 166        rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
 167        rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
 168        rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon) - 1;
 169        rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
 170
 171        rtc_tm->tm_year += 100;
 172
 173        dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
 174                1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
 175                rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
 176
 177        clk_disable(pdata->clk);
 178        return 0;
 179}
 180
 181static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm)
 182{
 183        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
 184        void __iomem *base = pdata->reg_base;
 185        int year = tm->tm_year - 100;
 186
 187        dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
 188                1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
 189                tm->tm_hour, tm->tm_min, tm->tm_sec);
 190
 191        if (year < 0 || year >= 100) {
 192                dev_err(dev, "rtc only supports 100 years\n");
 193                return -EINVAL;
 194        }
 195
 196        clk_enable(pdata->clk);
 197        writeb(bin2bcd(tm->tm_sec),  base + PIC32_RTCSEC);
 198        writeb(bin2bcd(tm->tm_min),  base + PIC32_RTCMIN);
 199        writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR);
 200        writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY);
 201        writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON);
 202        writeb(bin2bcd(year), base + PIC32_RTCYEAR);
 203        clk_disable(pdata->clk);
 204
 205        return 0;
 206}
 207
 208static int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
 209{
 210        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
 211        struct rtc_time *alm_tm = &alrm->time;
 212        void __iomem *base = pdata->reg_base;
 213        unsigned int alm_en;
 214
 215        clk_enable(pdata->clk);
 216        alm_tm->tm_sec  = readb(base + PIC32_ALRMSEC);
 217        alm_tm->tm_min  = readb(base + PIC32_ALRMMIN);
 218        alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR);
 219        alm_tm->tm_mon  = readb(base + PIC32_ALRMMON);
 220        alm_tm->tm_mday = readb(base + PIC32_ALRMDAY);
 221        alm_tm->tm_year = readb(base + PIC32_ALRMYEAR);
 222
 223        alm_en = readb(base + PIC32_RTCALRM);
 224
 225        alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0;
 226
 227        dev_dbg(dev, "getalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
 228                alm_en,
 229                1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
 230                alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
 231
 232        alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
 233        alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
 234        alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
 235        alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
 236        alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1;
 237        alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
 238
 239        clk_disable(pdata->clk);
 240        return 0;
 241}
 242
 243static int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 244{
 245        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
 246        struct rtc_time *tm = &alrm->time;
 247        void __iomem *base = pdata->reg_base;
 248
 249        clk_enable(pdata->clk);
 250        dev_dbg(dev, "setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
 251                alrm->enabled,
 252                1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
 253                tm->tm_hour, tm->tm_min, tm->tm_sec);
 254
 255        writel(0x00, base + PIC32_ALRMTIME);
 256        writel(0x00, base + PIC32_ALRMDATE);
 257
 258        pic32_rtc_setaie(dev, alrm->enabled);
 259
 260        clk_disable(pdata->clk);
 261        return 0;
 262}
 263
 264static int pic32_rtc_proc(struct device *dev, struct seq_file *seq)
 265{
 266        struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
 267        void __iomem *base = pdata->reg_base;
 268        unsigned int repeat;
 269
 270        clk_enable(pdata->clk);
 271
 272        repeat = readw(base + PIC32_RTCALRM);
 273        repeat &= PIC32_RTCALRM_ARPT;
 274        seq_printf(seq, "periodic_IRQ\t: %s\n", repeat  ? "yes" : "no");
 275
 276        clk_disable(pdata->clk);
 277        return 0;
 278}
 279
 280static const struct rtc_class_ops pic32_rtcops = {
 281        .read_time        = pic32_rtc_gettime,
 282        .set_time         = pic32_rtc_settime,
 283        .read_alarm       = pic32_rtc_getalarm,
 284        .set_alarm        = pic32_rtc_setalarm,
 285        .proc             = pic32_rtc_proc,
 286        .alarm_irq_enable = pic32_rtc_setaie,
 287};
 288
 289static void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en)
 290{
 291        void __iomem *base = pdata->reg_base;
 292
 293        if (!base)
 294                return;
 295
 296        clk_enable(pdata->clk);
 297        if (!en) {
 298                writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON));
 299        } else {
 300                pic32_syskey_unlock();
 301
 302                writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON));
 303                writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON));
 304
 305                if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON))
 306                        writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON));
 307        }
 308        clk_disable(pdata->clk);
 309}
 310
 311static int pic32_rtc_remove(struct platform_device *pdev)
 312{
 313        struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev);
 314
 315        pic32_rtc_setaie(&pdev->dev, 0);
 316        clk_unprepare(pdata->clk);
 317        pdata->clk = NULL;
 318
 319        return 0;
 320}
 321
 322static int pic32_rtc_probe(struct platform_device *pdev)
 323{
 324        struct pic32_rtc_dev *pdata;
 325        struct resource *res;
 326        int ret;
 327
 328        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
 329        if (!pdata)
 330                return -ENOMEM;
 331
 332        platform_set_drvdata(pdev, pdata);
 333
 334        pdata->alarm_irq = platform_get_irq(pdev, 0);
 335        if (pdata->alarm_irq < 0) {
 336                dev_err(&pdev->dev, "no irq for alarm\n");
 337                return pdata->alarm_irq;
 338        }
 339
 340        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 341        pdata->reg_base = devm_ioremap_resource(&pdev->dev, res);
 342        if (IS_ERR(pdata->reg_base))
 343                return PTR_ERR(pdata->reg_base);
 344
 345        pdata->clk = devm_clk_get(&pdev->dev, NULL);
 346        if (IS_ERR(pdata->clk)) {
 347                dev_err(&pdev->dev, "failed to find rtc clock source\n");
 348                ret = PTR_ERR(pdata->clk);
 349                pdata->clk = NULL;
 350                return ret;
 351        }
 352
 353        spin_lock_init(&pdata->alarm_lock);
 354
 355        clk_prepare_enable(pdata->clk);
 356
 357        pic32_rtc_enable(pdata, 1);
 358
 359        device_init_wakeup(&pdev->dev, 1);
 360
 361        pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
 362                                                 &pic32_rtcops,
 363                                                 THIS_MODULE);
 364        if (IS_ERR(pdata->rtc)) {
 365                ret = PTR_ERR(pdata->rtc);
 366                goto err_nortc;
 367        }
 368
 369        pdata->rtc->max_user_freq = 128;
 370
 371        pic32_rtc_setfreq(&pdev->dev, 1);
 372        ret = devm_request_irq(&pdev->dev, pdata->alarm_irq,
 373                               pic32_rtc_alarmirq, 0,
 374                               dev_name(&pdev->dev), pdata);
 375        if (ret) {
 376                dev_err(&pdev->dev,
 377                        "IRQ %d error %d\n", pdata->alarm_irq, ret);
 378                goto err_nortc;
 379        }
 380
 381        clk_disable(pdata->clk);
 382
 383        return 0;
 384
 385err_nortc:
 386        pic32_rtc_enable(pdata, 0);
 387        clk_disable_unprepare(pdata->clk);
 388
 389        return ret;
 390}
 391
 392static const struct of_device_id pic32_rtc_dt_ids[] = {
 393        { .compatible = "microchip,pic32mzda-rtc" },
 394        { /* sentinel */ }
 395};
 396MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids);
 397
 398static struct platform_driver pic32_rtc_driver = {
 399        .probe          = pic32_rtc_probe,
 400        .remove         = pic32_rtc_remove,
 401        .driver         = {
 402                .name   = "pic32-rtc",
 403                .of_match_table = of_match_ptr(pic32_rtc_dt_ids),
 404        },
 405};
 406module_platform_driver(pic32_rtc_driver);
 407
 408MODULE_DESCRIPTION("Microchip PIC32 RTC Driver");
 409MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");
 410MODULE_LICENSE("GPL");
 411