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 * See file CREDITS for list of people who contributed to this
  10 * project.
  11 *
  12 * This program is free software; you can redistribute it and/or
  13 * modify it under the terms of the GNU General Public License as
  14 * published by the Free Software Foundation; either version 2 of
  15 * the License, or (at your option) any later version.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  25 * MA 02111-1307 USA
  26 */
  27
  28/*
  29 * Date & Time support for Freescale i.MX DryIce RTC
  30 */
  31
  32#include <common.h>
  33#include <command.h>
  34#include <linux/compat.h>
  35#include <rtc.h>
  36
  37#if defined(CONFIG_CMD_DATE)
  38
  39#include <asm/io.h>
  40#include <asm/arch/imx-regs.h>
  41
  42/* DryIce Register Definitions */
  43
  44struct imxdi_regs {
  45        u32 dtcmr;                      /* Time Counter MSB Reg */
  46        u32 dtclr;                      /* Time Counter LSB Reg */
  47        u32 dcamr;                      /* Clock Alarm MSB Reg */
  48        u32 dcalr;                      /* Clock Alarm LSB Reg */
  49        u32 dcr;                        /* Control Reg */
  50        u32 dsr;                        /* Status Reg */
  51        u32 dier;                       /* Interrupt Enable Reg */
  52};
  53
  54#define DCAMR_UNSET     0xFFFFFFFF      /* doomsday - 1 sec */
  55
  56#define DCR_TCE         (1 << 3)        /* Time Counter Enable */
  57
  58#define DSR_WBF         (1 << 10)       /* Write Busy Flag */
  59#define DSR_WNF         (1 << 9)        /* Write Next Flag */
  60#define DSR_WCF         (1 << 8)        /* Write Complete Flag */
  61#define DSR_WEF         (1 << 7)        /* Write Error Flag */
  62#define DSR_CAF         (1 << 4)        /* Clock Alarm Flag */
  63#define DSR_NVF         (1 << 1)        /* Non-Valid Flag */
  64#define DSR_SVF         (1 << 0)        /* Security Violation Flag */
  65
  66#define DIER_WNIE       (1 << 9)        /* Write Next Interrupt Enable */
  67#define DIER_WCIE       (1 << 8)        /* Write Complete Interrupt Enable */
  68#define DIER_WEIE       (1 << 7)        /* Write Error Interrupt Enable */
  69#define DIER_CAIE       (1 << 4)        /* Clock Alarm Interrupt Enable */
  70
  71/* Driver Private Data */
  72
  73struct imxdi_data {
  74        struct imxdi_regs __iomem       *regs;
  75        int                             init_done;
  76};
  77
  78static struct imxdi_data data;
  79
  80/*
  81 * This function attempts to clear the dryice write-error flag.
  82 *
  83 * A dryice write error is similar to a bus fault and should not occur in
  84 * normal operation.  Clearing the flag requires another write, so the root
  85 * cause of the problem may need to be fixed before the flag can be cleared.
  86 */
  87static void clear_write_error(void)
  88{
  89        int cnt;
  90
  91        puts("### Warning: RTC - Register write error!\n");
  92
  93        /* clear the write error flag */
  94        __raw_writel(DSR_WEF, &data.regs->dsr);
  95
  96        /* wait for it to take effect */
  97        for (cnt = 0; cnt < 1000; cnt++) {
  98                if ((__raw_readl(&data.regs->dsr) & DSR_WEF) == 0)
  99                        return;
 100                udelay(10);
 101        }
 102        puts("### Error: RTC - Cannot clear write-error flag!\n");
 103}
 104
 105/*
 106 * Write a dryice register and wait until it completes.
 107 *
 108 * Use interrupt flags to determine when the write has completed.
 109 */
 110#define DI_WRITE_WAIT(val, reg)                                         \
 111(                                                                       \
 112        /* do the register write */                                     \
 113        __raw_writel((val), &data.regs->reg),                           \
 114                                                                        \
 115        di_write_wait((val), #reg)                                      \
 116)
 117static int di_write_wait(u32 val, const char *reg)
 118{
 119        int cnt;
 120        int ret = 0;
 121        int rc = 0;
 122
 123        /* wait for the write to finish */
 124        for (cnt = 0; cnt < 100; cnt++) {
 125                if ((__raw_readl(&data.regs->dsr) & (DSR_WCF | DSR_WEF)) != 0) {
 126                        ret = 1;
 127                        break;
 128                }
 129                udelay(10);
 130        }
 131        if (ret == 0)
 132                printf("### Warning: RTC - Write-wait timeout "
 133                                "val = 0x%.8x reg = %s\n", val, reg);
 134
 135        /* check for write error */
 136        if (__raw_readl(&data.regs->dsr) & DSR_WEF) {
 137                clear_write_error();
 138                rc = -1;
 139        }
 140
 141        return rc;
 142}
 143
 144/*
 145 * Initialize dryice hardware
 146 */
 147static int di_init(void)
 148{
 149        int rc = 0;
 150
 151        data.regs = (struct imxdi_regs __iomem *)IMX_DRYICE_BASE;
 152
 153        /* mask all interrupts */
 154        __raw_writel(0, &data.regs->dier);
 155
 156        /* put dryice into valid state */
 157        if (__raw_readl(&data.regs->dsr) & DSR_NVF) {
 158                rc = DI_WRITE_WAIT(DSR_NVF | DSR_SVF, dsr);
 159                if (rc)
 160                        goto err;
 161        }
 162
 163        /* initialize alarm */
 164        rc = DI_WRITE_WAIT(DCAMR_UNSET, dcamr);
 165        if (rc)
 166                goto err;
 167        rc = DI_WRITE_WAIT(0, dcalr);
 168        if (rc)
 169                goto err;
 170
 171        /* clear alarm flag */
 172        if (__raw_readl(&data.regs->dsr) & DSR_CAF) {
 173                rc = DI_WRITE_WAIT(DSR_CAF, dsr);
 174                if (rc)
 175                        goto err;
 176        }
 177
 178        /* the timer won't count if it has never been written to */
 179        if (__raw_readl(&data.regs->dtcmr) == 0) {
 180                rc = DI_WRITE_WAIT(0, dtcmr);
 181                if (rc)
 182                        goto err;
 183        }
 184
 185        /* start keeping time */
 186        if (!(__raw_readl(&data.regs->dcr) & DCR_TCE)) {
 187                rc = DI_WRITE_WAIT(__raw_readl(&data.regs->dcr) | DCR_TCE, dcr);
 188                if (rc)
 189                        goto err;
 190        }
 191
 192        data.init_done = 1;
 193        return 0;
 194
 195err:
 196        return rc;
 197}
 198
 199int rtc_get(struct rtc_time *tmp)
 200{
 201        unsigned long now;
 202        int rc = 0;
 203
 204        if (!data.init_done) {
 205                rc = di_init();
 206                if (rc)
 207                        goto err;
 208        }
 209
 210        now = __raw_readl(&data.regs->dtcmr);
 211        to_tm(now, tmp);
 212
 213err:
 214        return rc;
 215}
 216
 217int rtc_set(struct rtc_time *tmp)
 218{
 219        unsigned long now;
 220        int rc;
 221
 222        if (!data.init_done) {
 223                rc = di_init();
 224                if (rc)
 225                        goto err;
 226        }
 227
 228        now = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
 229                     tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
 230        /* zero the fractional part first */
 231        rc = DI_WRITE_WAIT(0, dtclr);
 232        if (rc == 0)
 233                rc = DI_WRITE_WAIT(now, dtcmr);
 234
 235err:
 236        return rc;
 237}
 238
 239void rtc_reset(void)
 240{
 241        di_init();
 242}
 243
 244#endif
 245