xref: /openbsd/sys/arch/octeon/dev/octrtc.c (revision 0701a158)
1 /*	$OpenBSD: octrtc.c,v 1.15 2022/10/12 13:39:50 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2013, 2014 Paul Irofti.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/proc.h>
23 
24 #include <dev/clock_subr.h>
25 
26 #include <machine/bus.h>
27 #include <machine/autoconf.h>
28 #include <machine/octeonvar.h>
29 
30 #ifdef OCTRTC_DEBUG
31 #define DPRINTF(x)	printf x
32 #else
33 #define DPRINTF(x)
34 #endif
35 
36 #define MIO_TWS_SW_TWSI		0x0001180000001000ULL
37 #define MIO_TWS_SW_TWSI_EXT	0x0001180000001018ULL
38 #define OCTRTC_REG	0x68
39 
40 struct octrtc_softc {
41 	struct device			sc_dev;
42 	struct todr_chip_handle		sc_todr;
43 };
44 
45 struct cfdriver octrtc_cd = {
46 	NULL, "octrtc", DV_DULL
47 };
48 
49 int	octrtc_match(struct device *, void *, void *);
50 void	octrtc_attach(struct device *, struct device *, void *);
51 
52 int	octrtc_gettime(struct todr_chip_handle *, struct timeval *);
53 int	octrtc_read(uint8_t *, char);
54 
55 int	octrtc_settime(struct todr_chip_handle *, struct timeval *);
56 int	octrtc_write(uint8_t);
57 
58 
59 const struct cfattach octrtc_ca = {
60 	sizeof(struct octrtc_softc), octrtc_match, octrtc_attach,
61 };
62 
63 
64 union mio_tws_sw_twsi_reg {
65 	uint64_t reg;
66 	struct cvmx_mio_twsx_sw_twsi_s {
67 		uint64_t v:1;		/* Valid bit */
68 		uint64_t slonly:1;	/* Slave Only Mode */
69 		uint64_t eia:1;		/* Extended Internal Address */
70 		uint64_t op:4;		/* Opcode field */
71 		uint64_t r:1;		/* Read bit or result */
72 		uint64_t sovr:1;	/* Size Override */
73 		uint64_t size:3;	/* Size in bytes */
74 		uint64_t scr:2;		/* Scratch, unused */
75 		uint64_t a:10;		/* Address field */
76 		uint64_t ia:5;		/* Internal Address */
77 		uint64_t eop_ia:3;	/* Extra opcode */
78 		uint64_t d:32;		/* Data Field */
79 	} field;
80 };
81 
82 
83 static const enum octeon_board no_rtc_boards[] = {
84 	BOARD_UBIQUITI_E100,
85 	BOARD_UBIQUITI_E120,
86 	BOARD_UBIQUITI_E200,
87 	BOARD_UBIQUITI_E220,
88 	BOARD_UBIQUITI_E300,
89 	BOARD_UBIQUITI_E1000,
90 	BOARD_RHINOLABS_UTM8,
91 };
92 
93 int
octrtc_match(struct device * parent,void * match,void * aux)94 octrtc_match(struct device *parent, void *match, void *aux)
95 {
96 	struct mainbus_attach_args *maa = aux;
97 	struct cfdata *cf = match;
98 	int i;
99 
100 	if (strcmp(maa->maa_name, cf->cf_driver->cd_name) != 0)
101 		return 0;
102 	for (i = 0; i < nitems(no_rtc_boards); i++)
103 		if (octeon_board == no_rtc_boards[i])
104 			return 0;
105 	return 1;
106 }
107 
108 void
octrtc_attach(struct device * parent,struct device * self,void * aux)109 octrtc_attach(struct device *parent, struct device *self, void *aux)
110 {
111 	struct octrtc_softc *sc = (struct octrtc_softc *)self;
112 
113 	sc->sc_todr.cookie = sc;
114 	sc->sc_todr.todr_gettime = octrtc_gettime;
115 	sc->sc_todr.todr_settime = octrtc_settime;
116 	sc->sc_todr.todr_quality = 0;
117 	todr_attach(&sc->sc_todr);
118 
119 	printf(": DS1337\n");
120 }
121 
122 int
octrtc_gettime(struct todr_chip_handle * handle,struct timeval * tv)123 octrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
124 {
125 	struct clock_ymdhms dt;
126 	uint8_t tod[8];
127 	uint8_t check;
128 	int i, rc;
129 
130 	int nretries = 2;
131 
132 	DPRINTF(("\nTOD: "));
133 	while (nretries--) {
134 		rc = octrtc_read(&tod[0], 1);	/* ia read */
135 		if (rc) {
136 			DPRINTF(("octrtc_read(0) failed %d", rc));
137 			return EIO;
138 		}
139 
140 		for (i = 1; i < 8; i++) {
141 			rc = octrtc_read(&tod[i], 0);	/* current address */
142 			if (rc) {
143 				DPRINTF(("octrtc_read(%d) failed %d", i, rc));
144 				return EIO;
145 			}
146 			DPRINTF(("%#X ", tod[i]));
147 		}
148 
149 		/* Check against time-wrap */
150 		rc = octrtc_read(&check, 1);	/* ia read */
151 		if (rc) {
152 			DPRINTF(("octrtc_read(check) failed %d", rc));
153 			return EIO;
154 		}
155 		if ((check & 0xf) == (tod[0] & 0xf))
156 			break;
157 	}
158 	DPRINTF(("\n"));
159 
160 	DPRINTF(("Time: %d %d %d (%d) %02d:%02d:%02d\n",
161 	    ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]),	/* year */
162 	    FROMBCD(tod[5] & 0x1f),	/* month */
163 	    FROMBCD(tod[4] & 0x3f),	/* day */
164 	    (tod[3] & 0x7),		/* day of the week */
165 	    FROMBCD(tod[2] & 0x3f),	/* hour */
166 	    FROMBCD(tod[1] & 0x7f),	/* minute */
167 	    FROMBCD(tod[0] & 0x7f)));	/* second */
168 
169 	dt.dt_year = ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]);
170 	dt.dt_mon = FROMBCD(tod[5] & 0x1f);
171 	dt.dt_day = FROMBCD(tod[4] & 0x3f);
172 	dt.dt_hour = FROMBCD(tod[2] & 0x3f);
173 	if ((tod[2] & 0x40) && (tod[2] & 0x20))	/* adjust AM/PM format */
174 		dt.dt_hour = (dt.dt_hour + 12) % 24;
175 	dt.dt_min = FROMBCD(tod[1] & 0x7f);
176 	dt.dt_sec = FROMBCD(tod[0] & 0x7f);
177 
178 	if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
179 	    dt.dt_day > 31 || dt.dt_day == 0 ||
180 	    dt.dt_mon > 12 || dt.dt_mon == 0 ||
181 	    dt.dt_year < POSIX_BASE_YEAR)
182 		return EINVAL;
183 
184 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
185 	tv->tv_usec = 0;
186 	return 0;
187 }
188 
189 int
octrtc_read(uint8_t * data,char ia)190 octrtc_read(uint8_t *data, char ia)
191 {
192 	int nretries = 5;
193 	union mio_tws_sw_twsi_reg twsi;
194 
195 again:
196 	twsi.reg = 0;
197 	twsi.field.v = 1;
198 	twsi.field.r = 1;
199 	twsi.field.sovr = 1;
200 	twsi.field.a = OCTRTC_REG;
201 	if (ia) {
202 		twsi.field.op = 1;
203 	}
204 
205 	octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg);
206 	/* The 1st bit is cleared when the operation is complete */
207 	do {
208 		delay(1000);
209 		twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI);
210 	} while (twsi.field.v);
211 	DPRINTF(("%#llX ", twsi.reg));
212 
213 	/*
214 	 * The data field is in the upper 32 bits and we're only
215 	 * interested in the first byte.
216 	 */
217 	*data = twsi.field.d & 0xff;
218 
219 	/* 8th bit is the read result: 1 = success, 0 = failure */
220 	if (twsi.field.r == 0) {
221 		/*
222 		 * Lost arbitration : 0x38, 0x68, 0xB0, 0x78
223 		 * Core busy as slave: 0x80, 0x88, 0xA0, 0xA8, 0xB8, 0xC0, 0xC8
224 		 */
225 		if (*data == 0x38 || *data == 0x68 || *data == 0xB0 ||
226 		    *data == 0x78 || *data == 0x80 || *data == 0x88 ||
227 		    *data == 0xA0 || *data == 0xA8 || *data == 0xB8 ||
228 		    *data == 0xC0 || *data == 0xC8)
229 			if (nretries--)
230 				goto again;
231 		return EIO;
232 	}
233 
234 	return 0;
235 }
236 
237 int
octrtc_settime(struct todr_chip_handle * handle,struct timeval * tv)238 octrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
239 {
240 	struct clock_ymdhms dt;
241 	int nretries = 2;
242 	int rc, i;
243 	uint8_t tod[8];
244 	uint8_t check;
245 
246 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
247 
248 	DPRINTF(("settime: %d %d %d (%d) %02d:%02d:%02d\n",
249 	    dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_wday,
250 	    dt.dt_hour, dt.dt_min, dt.dt_sec));
251 
252 	tod[0] = TOBCD(dt.dt_sec);
253 	tod[1] = TOBCD(dt.dt_min);
254 	tod[2] = TOBCD(dt.dt_hour);
255 	tod[3] = TOBCD(dt.dt_wday + 1);
256 	tod[4] = TOBCD(dt.dt_day);
257 	tod[5] = TOBCD(dt.dt_mon);
258 	if (dt.dt_year >= 2000)
259 		tod[5] |= 0x80;
260 	tod[6] = TOBCD(dt.dt_year % 100);
261 
262 	while (nretries--) {
263 		for (i = 0; i < 7; i++) {
264 			rc = octrtc_write(tod[i]);
265 			if (rc) {
266 				DPRINTF(("octrtc_write(%d) failed %d", i, rc));
267 				return EIO;
268 			}
269 		}
270 
271 		rc = octrtc_read(&check, 1);
272 		if (rc) {
273 			DPRINTF(("octrtc_read(check) failed %d", rc));
274 			return EIO;
275 		}
276 
277 		if ((check & 0xf) == (tod[0] & 0xf))
278 			break;
279 	}
280 
281 	return 0;
282 }
283 
284 int
octrtc_write(uint8_t data)285 octrtc_write(uint8_t data)
286 {
287 	union mio_tws_sw_twsi_reg twsi;
288 	int npoll = 128;
289 
290 	twsi.reg = 0;
291 	twsi.field.v = 1;
292 	twsi.field.sovr = 1;
293 	twsi.field.op = 1;
294 	twsi.field.a = OCTRTC_REG;
295 	twsi.field.d = data & 0xffffffff;
296 
297 	octeon_xkphys_write_8(MIO_TWS_SW_TWSI_EXT, 0);
298 	octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg);
299 	do {
300 		delay(1000);
301 		twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI);
302 	} while (twsi.field.v);
303 
304 	/* Try to read back */
305 	while (npoll-- && octrtc_read(&data, 0));
306 
307 	return npoll ? 0 : EIO;
308 }
309