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