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