xref: /openbmc/u-boot/drivers/rtc/imxdi.c (revision a9a274c1)
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 	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
229