]> git.ipfire.org Git - people/ms/u-boot.git/blob - drivers/rtc/imxdi.c
dm: rtc: Rename to_tm() to rtc_to_tm() and add error code
[people/ms/u-boot.git] / drivers / rtc / imxdi.c
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 rtc_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