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