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 28 struct 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 57 struct imxdi_data { 58 struct imxdi_regs __iomem *regs; 59 int init_done; 60 }; 61 62 static 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 */ 71 static 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 ) 101 static 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 */ 131 static 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 179 err: 180 return rc; 181 } 182 183 int 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 to_tm(now, tmp); 196 197 err: 198 return rc; 199 } 200 201 int 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 = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, 213 tmp->tm_hour, tmp->tm_min, tmp->tm_sec); 214 /* zero the fractional part first */ 215 rc = DI_WRITE_WAIT(0, dtclr); 216 if (rc == 0) 217 rc = DI_WRITE_WAIT(now, dtcmr); 218 219 err: 220 return rc; 221 } 222 223 void rtc_reset(void) 224 { 225 di_init(); 226 } 227 228 #endif 229