uboot/drivers/rtc/imxdi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2009-2012 ADVANSEE
   4 * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
   5 *
   6 * Based on the Linux rtc-imxdi.c driver, which is:
   7 * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
   8 * Copyright 2010 Orex Computed Radiography
   9 */
  10
  11/*
  12 * Date & Time support for Freescale i.MX DryIce RTC
  13 */
  14
  15#include <common.h>
  16#include <command.h>
  17#include <linux/compat.h>
  18#include <rtc.h>
  19#include <linux/delay.h>
  20
  21#include <asm/io.h>
  22#include <asm/arch/imx-regs.h>
  23
  24/* DryIce Register Definitions */
  25
  26struct imxdi_regs {
  27        u32 dtcmr;                      /* Time Counter MSB Reg */
  28        u32 dtclr;                      /* Time Counter LSB Reg */
  29        u32 dcamr;                      /* Clock Alarm MSB Reg */
  30        u32 dcalr;                      /* Clock Alarm LSB Reg */
  31        u32 dcr;                        /* Control Reg */
  32        u32 dsr;                        /* Status Reg */
  33        u32 dier;                       /* Interrupt Enable Reg */
  34};
  35
  36#define DCAMR_UNSET     0xFFFFFFFF      /* doomsday - 1 sec */
  37
  38#define DCR_TCE         (1 << 3)        /* Time Counter Enable */
  39
  40#define DSR_WBF         (1 << 10)       /* Write Busy Flag */
  41#define DSR_WNF         (1 << 9)        /* Write Next Flag */
  42#define DSR_WCF         (1 << 8)        /* Write Complete Flag */
  43#define DSR_WEF         (1 << 7)        /* Write Error Flag */
  44#define DSR_CAF         (1 << 4)        /* Clock Alarm Flag */
  45#define DSR_NVF         (1 << 1)        /* Non-Valid Flag */
  46#define DSR_SVF         (1 << 0)        /* Security Violation Flag */
  47
  48#define DIER_WNIE       (1 << 9)        /* Write Next Interrupt Enable */
  49#define DIER_WCIE       (1 << 8)        /* Write Complete Interrupt Enable */
  50#define DIER_WEIE       (1 << 7)        /* Write Error Interrupt Enable */
  51#define DIER_CAIE       (1 << 4)        /* Clock Alarm Interrupt Enable */
  52
  53/* Driver Private Data */
  54
  55struct imxdi_data {
  56        struct imxdi_regs __iomem       *regs;
  57        int                             init_done;
  58};
  59
  60static struct imxdi_data data;
  61
  62/*
  63 * This function attempts to clear the dryice write-error flag.
  64 *
  65 * A dryice write error is similar to a bus fault and should not occur in
  66 * normal operation.  Clearing the flag requires another write, so the root
  67 * cause of the problem may need to be fixed before the flag can be cleared.
  68 */
  69static void clear_write_error(void)
  70{
  71        int cnt;
  72
  73        puts("### Warning: RTC - Register write error!\n");
  74
  75        /* clear the write error flag */
  76        __raw_writel(DSR_WEF, &data.regs->dsr);
  77
  78        /* wait for it to take effect */
  79        for (cnt = 0; cnt < 1000; cnt++) {
  80                if ((__raw_readl(&data.regs->dsr) & DSR_WEF) == 0)
  81                        return;
  82                udelay(10);
  83        }
  84        puts("### Error: RTC - Cannot clear write-error flag!\n");
  85}
  86
  87/*
  88 * Write a dryice register and wait until it completes.
  89 *
  90 * Use interrupt flags to determine when the write has completed.
  91 */
  92#define DI_WRITE_WAIT(val, reg)                                         \
  93(                                                                       \
  94        /* do the register write */                                     \
  95        __raw_writel((val), &data.regs->reg),                           \
  96                                                                        \
  97        di_write_wait((val), #reg)                                      \
  98)
  99static int di_write_wait(u32 val, const char *reg)
 100{
 101        int cnt;
 102        int ret = 0;
 103        int rc = 0;
 104
 105        /* wait for the write to finish */
 106        for (cnt = 0; cnt < 100; cnt++) {
 107                if ((__raw_readl(&data.regs->dsr) & (DSR_WCF | DSR_WEF)) != 0) {
 108                        ret = 1;
 109                        break;
 110                }
 111                udelay(10);
 112        }
 113        if (ret == 0)
 114                printf("### Warning: RTC - Write-wait timeout "
 115                                "val = 0x%.8x reg = %s\n", val, reg);
 116
 117        /* check for write error */
 118        if (__raw_readl(&data.regs->dsr) & DSR_WEF) {
 119                clear_write_error();
 120                rc = -1;
 121        }
 122
 123        return rc;
 124}
 125
 126/*
 127 * Initialize dryice hardware
 128 */
 129static int di_init(void)
 130{
 131        int rc = 0;
 132
 133        data.regs = (struct imxdi_regs __iomem *)IMX_DRYICE_BASE;
 134
 135        /* mask all interrupts */
 136        __raw_writel(0, &data.regs->dier);
 137
 138        /* put dryice into valid state */
 139        if (__raw_readl(&data.regs->dsr) & DSR_NVF) {
 140                rc = DI_WRITE_WAIT(DSR_NVF | DSR_SVF, dsr);
 141                if (rc)
 142                        goto err;
 143        }
 144
 145        /* initialize alarm */
 146        rc = DI_WRITE_WAIT(DCAMR_UNSET, dcamr);
 147        if (rc)
 148                goto err;
 149        rc = DI_WRITE_WAIT(0, dcalr);
 150        if (rc)
 151                goto err;
 152
 153        /* clear alarm flag */
 154        if (__raw_readl(&data.regs->dsr) & DSR_CAF) {
 155                rc = DI_WRITE_WAIT(DSR_CAF, dsr);
 156                if (rc)
 157                        goto err;
 158        }
 159
 160        /* the timer won't count if it has never been written to */
 161        if (__raw_readl(&data.regs->dtcmr) == 0) {
 162                rc = DI_WRITE_WAIT(0, dtcmr);
 163                if (rc)
 164                        goto err;
 165        }
 166
 167        /* start keeping time */
 168        if (!(__raw_readl(&data.regs->dcr) & DCR_TCE)) {
 169                rc = DI_WRITE_WAIT(__raw_readl(&data.regs->dcr) | DCR_TCE, dcr);
 170                if (rc)
 171                        goto err;
 172        }
 173
 174        data.init_done = 1;
 175        return 0;
 176
 177err:
 178        return rc;
 179}
 180
 181int rtc_get(struct rtc_time *tmp)
 182{
 183        unsigned long now;
 184        int rc = 0;
 185
 186        if (!data.init_done) {
 187                rc = di_init();
 188                if (rc)
 189                        goto err;
 190        }
 191
 192        now = __raw_readl(&data.regs->dtcmr);
 193        rtc_to_tm(now, tmp);
 194
 195err:
 196        return rc;
 197}
 198
 199int rtc_set(struct rtc_time *tmp)
 200{
 201        unsigned long now;
 202        int rc;
 203
 204        if (!data.init_done) {
 205                rc = di_init();
 206                if (rc)
 207                        goto err;
 208        }
 209
 210        now = rtc_mktime(tmp);
 211        /* zero the fractional part first */
 212        rc = DI_WRITE_WAIT(0, dtclr);
 213        if (rc == 0)
 214                rc = DI_WRITE_WAIT(now, dtcmr);
 215
 216err:
 217        return rc;
 218}
 219
 220void rtc_reset(void)
 221{
 222        di_init();
 223}
 224