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 44 struct 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 73 struct imxdi_data { 74 struct imxdi_regs __iomem *regs; 75 int init_done; 76 }; 77 78 static 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 */ 87 static 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 ) 117 static 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 */ 147 static 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 195 err: 196 return rc; 197 } 198 199 int 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 213 err: 214 return rc; 215 } 216 217 int 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 235 err: 236 return rc; 237 } 238 239 void rtc_reset(void) 240 { 241 di_init(); 242 } 243 244 #endif 245