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