blob: e89034dc0a80c5b7b35ccb47f1c24c5236e9fd81 [file] [log] [blame]
Benoît Thébaudeau66b70bb2012-08-08 04:52:47 +00001/*
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 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02009 * SPDX-License-Identifier: GPL-2.0+
Benoît Thébaudeau66b70bb2012-08-08 04:52:47 +000010 */
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
28struct 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
57struct imxdi_data {
58 struct imxdi_regs __iomem *regs;
59 int init_done;
60};
61
62static 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 */
71static 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)
101static 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 */
131static 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
179err:
180 return rc;
181}
182
183int 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);
Simon Glass12994662015-04-20 12:37:18 -0600195 rtc_to_tm(now, tmp);
Benoît Thébaudeau66b70bb2012-08-08 04:52:47 +0000196
197err:
198 return rc;
199}
200
201int 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
219err:
220 return rc;
221}
222
223void rtc_reset(void)
224{
225 di_init();
226}
227
228#endif