]>
Commit | Line | Data |
---|---|---|
ebd6ca9a BT |
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 | * | |
1a459660 | 9 | * SPDX-License-Identifier: GPL-2.0+ |
ebd6ca9a BT |
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); | |
9f9276c3 | 195 | rtc_to_tm(now, tmp); |
ebd6ca9a BT |
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 | ||
71420983 | 212 | now = rtc_mktime(tmp); |
ebd6ca9a BT |
213 | /* zero the fractional part first */ |
214 | rc = DI_WRITE_WAIT(0, dtclr); | |
215 | if (rc == 0) | |
216 | rc = DI_WRITE_WAIT(now, dtcmr); | |
217 | ||
218 | err: | |
219 | return rc; | |
220 | } | |
221 | ||
222 | void rtc_reset(void) | |
223 | { | |
224 | di_init(); | |
225 | } | |
226 | ||
227 | #endif |