blob: 985ce93acf71ed803844c43c86c14baee201fc44 [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 *
9 * See file CREDITS for list of people who contributed to this
10 * project.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 * MA 02111-1307 USA
26 */
27
28/*
29 * Date & Time support for Freescale i.MX DryIce RTC
30 */
31
32#include <common.h>
33#include <command.h>
34#include <linux/compat.h>
35#include <rtc.h>
36
37#if defined(CONFIG_CMD_DATE)
38
39#include <asm/io.h>
40#include <asm/arch/imx-regs.h>
41
42/* DryIce Register Definitions */
43
44struct imxdi_regs {
45 u32 dtcmr; /* Time Counter MSB Reg */
46 u32 dtclr; /* Time Counter LSB Reg */
47 u32 dcamr; /* Clock Alarm MSB Reg */
48 u32 dcalr; /* Clock Alarm LSB Reg */
49 u32 dcr; /* Control Reg */
50 u32 dsr; /* Status Reg */
51 u32 dier; /* Interrupt Enable Reg */
52};
53
54#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
55
56#define DCR_TCE (1 << 3) /* Time Counter Enable */
57
58#define DSR_WBF (1 << 10) /* Write Busy Flag */
59#define DSR_WNF (1 << 9) /* Write Next Flag */
60#define DSR_WCF (1 << 8) /* Write Complete Flag */
61#define DSR_WEF (1 << 7) /* Write Error Flag */
62#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
63#define DSR_NVF (1 << 1) /* Non-Valid Flag */
64#define DSR_SVF (1 << 0) /* Security Violation Flag */
65
66#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
67#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
68#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
69#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
70
71/* Driver Private Data */
72
73struct imxdi_data {
74 struct imxdi_regs __iomem *regs;
75 int init_done;
76};
77
78static struct imxdi_data data;
79
80/*
81 * This function attempts to clear the dryice write-error flag.
82 *
83 * A dryice write error is similar to a bus fault and should not occur in
84 * normal operation. Clearing the flag requires another write, so the root
85 * cause of the problem may need to be fixed before the flag can be cleared.
86 */
87static void clear_write_error(void)
88{
89 int cnt;
90
91 puts("### Warning: RTC - Register write error!\n");
92
93 /* clear the write error flag */
94 __raw_writel(DSR_WEF, &data.regs->dsr);
95
96 /* wait for it to take effect */
97 for (cnt = 0; cnt < 1000; cnt++) {
98 if ((__raw_readl(&data.regs->dsr) & DSR_WEF) == 0)
99 return;
100 udelay(10);
101 }
102 puts("### Error: RTC - Cannot clear write-error flag!\n");
103}
104
105/*
106 * Write a dryice register and wait until it completes.
107 *
108 * Use interrupt flags to determine when the write has completed.
109 */
110#define DI_WRITE_WAIT(val, reg) \
111( \
112 /* do the register write */ \
113 __raw_writel((val), &data.regs->reg), \
114 \
115 di_write_wait((val), #reg) \
116)
117static int di_write_wait(u32 val, const char *reg)
118{
119 int cnt;
120 int ret = 0;
121 int rc = 0;
122
123 /* wait for the write to finish */
124 for (cnt = 0; cnt < 100; cnt++) {
125 if ((__raw_readl(&data.regs->dsr) & (DSR_WCF | DSR_WEF)) != 0) {
126 ret = 1;
127 break;
128 }
129 udelay(10);
130 }
131 if (ret == 0)
132 printf("### Warning: RTC - Write-wait timeout "
133 "val = 0x%.8x reg = %s\n", val, reg);
134
135 /* check for write error */
136 if (__raw_readl(&data.regs->dsr) & DSR_WEF) {
137 clear_write_error();
138 rc = -1;
139 }
140
141 return rc;
142}
143
144/*
145 * Initialize dryice hardware
146 */
147static int di_init(void)
148{
149 int rc = 0;
150
151 data.regs = (struct imxdi_regs __iomem *)IMX_DRYICE_BASE;
152
153 /* mask all interrupts */
154 __raw_writel(0, &data.regs->dier);
155
156 /* put dryice into valid state */
157 if (__raw_readl(&data.regs->dsr) & DSR_NVF) {
158 rc = DI_WRITE_WAIT(DSR_NVF | DSR_SVF, dsr);
159 if (rc)
160 goto err;
161 }
162
163 /* initialize alarm */
164 rc = DI_WRITE_WAIT(DCAMR_UNSET, dcamr);
165 if (rc)
166 goto err;
167 rc = DI_WRITE_WAIT(0, dcalr);
168 if (rc)
169 goto err;
170
171 /* clear alarm flag */
172 if (__raw_readl(&data.regs->dsr) & DSR_CAF) {
173 rc = DI_WRITE_WAIT(DSR_CAF, dsr);
174 if (rc)
175 goto err;
176 }
177
178 /* the timer won't count if it has never been written to */
179 if (__raw_readl(&data.regs->dtcmr) == 0) {
180 rc = DI_WRITE_WAIT(0, dtcmr);
181 if (rc)
182 goto err;
183 }
184
185 /* start keeping time */
186 if (!(__raw_readl(&data.regs->dcr) & DCR_TCE)) {
187 rc = DI_WRITE_WAIT(__raw_readl(&data.regs->dcr) | DCR_TCE, dcr);
188 if (rc)
189 goto err;
190 }
191
192 data.init_done = 1;
193 return 0;
194
195err:
196 return rc;
197}
198
199int rtc_get(struct rtc_time *tmp)
200{
201 unsigned long now;
202 int rc = 0;
203
204 if (!data.init_done) {
205 rc = di_init();
206 if (rc)
207 goto err;
208 }
209
210 now = __raw_readl(&data.regs->dtcmr);
211 to_tm(now, tmp);
212
213err:
214 return rc;
215}
216
217int rtc_set(struct rtc_time *tmp)
218{
219 unsigned long now;
220 int rc;
221
222 if (!data.init_done) {
223 rc = di_init();
224 if (rc)
225 goto err;
226 }
227
228 now = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
229 tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
230 /* zero the fractional part first */
231 rc = DI_WRITE_WAIT(0, dtclr);
232 if (rc == 0)
233 rc = DI_WRITE_WAIT(now, dtcmr);
234
235err:
236 return rc;
237}
238
239void rtc_reset(void)
240{
241 di_init();
242}
243
244#endif