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