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