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