xref: /src/sys/dev/iicbus/rtc/hym8563.c (revision 6b77d34f449128e6591131ec3d1822e2df3d8d8a) !
1 /*-
2  * Copyright (c) 2017 Hiroki Mori.  All rights reserved.
3  * Copyright (c) 2017 Ian Lepore.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * This code base on isl12xx.c
26  */
27 
28 #include <sys/cdefs.h>
29 /*
30  * Driver for realtime clock HAOYU HYM8563
31  */
32 
33 #include "opt_platform.h"
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/clock.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/module.h>
42 
43 #if defined(FDT) && !defined(__powerpc64__)
44 #include <dev/clk/clk.h>
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47 #endif
48 
49 #include <dev/iicbus/iiconf.h>
50 #include <dev/iicbus/iicbus.h>
51 
52 #include "clock_if.h"
53 #include "iicbus_if.h"
54 
55 /* Registers */
56 #define	HYM8563_CTRL1		0x00
57 #define	 HYM8563_CTRL1_TEST		(1 << 7)
58 #define	 HYM8563_CTRL1_STOP		(1 << 5)
59 #define	 HYM8563_CTRL1_TESTC		(1 << 3)
60 
61 #define	HYM8563_CTRL2		0x01
62 #define	 HYM8563_CTRL2_TI_TP		(1 << 4)
63 #define	 HYM8563_CTRL2_AF		(1 << 3)
64 #define	 HYM8563_CTRL2_TF		(1 << 2)
65 #define	 HYM8563_CTRL2_AIE		(1 << 1)
66 #define	 HYM8563_CTRL2_TIE		(1 << 0)
67 
68 #define	HYM8563_SEC		0x02	/* plus battery low bit */
69 #define	 HYM8563_SEC_VL			(1 << 7)
70 
71 #define	HYM8563_MIN		0x03
72 #define	HYM8563_HOUR		0x04
73 #define	HYM8563_DAY		0x05
74 #define	HYM8563_WEEKDAY		0x06
75 #define	HYM8563_MONTH		0x07	/* plus 1 bit for century */
76 #define	 HYM8563_MONTH_CENTURY		(1 << 7)
77 #define	HYM8563_YEAR		0x08
78 
79 #define	HYM8563_CLKOUT		0x0D
80 #define	 HYM8563_CLKOUT_ENABLE	(1 << 7)
81 #define	 HYM8563_CLKOUT_32768	0
82 #define	 HYM8563_CLKOUT_1024	1
83 #define	 HYM8563_CLKOUT_32	2
84 #define	 HYM8563_CLKOUT_1	3
85 #define	 HYM8563_CLKOUT_MASK	3
86 
87 struct hym8563_softc {
88 	device_t			dev;
89 	struct intr_config_hook		init_hook;
90 };
91 
92 #if defined(FDT) && !defined(__powerpc64__)
93 /* Clock class and method */
94 struct hym8563_clk_sc {
95 	device_t		base_dev;
96 };
97 
98 
99 static struct ofw_compat_data compat_data[] = {
100 	{"haoyu,hym8563", 1},
101 	{NULL,           0},
102 };
103 #endif
104 
105 
106 static inline int
hym8563_read_buf(device_t dev,uint8_t reg,uint8_t * buf,uint16_t buflen)107 hym8563_read_buf(device_t dev, uint8_t reg, uint8_t *buf, uint16_t buflen)
108 {
109 
110 	return (iicdev_readfrom(dev, reg, buf, buflen, IIC_WAIT));
111 }
112 
113 static inline int
hym8563_write_buf(device_t dev,uint8_t reg,uint8_t * buf,uint16_t buflen)114 hym8563_write_buf(device_t dev, uint8_t reg, uint8_t *buf,  uint16_t buflen)
115 {
116 
117 	return (iicdev_writeto(dev, reg, buf, buflen, IIC_WAIT));
118 }
119 
120 static inline int
hym8563_read_1(device_t dev,uint8_t reg,uint8_t * data)121 hym8563_read_1(device_t dev, uint8_t reg, uint8_t *data)
122 {
123 
124 	return (iicdev_readfrom(dev, reg, data, 1, IIC_WAIT));
125 }
126 
127 static inline int
hym8563_write_1(device_t dev,uint8_t reg,uint8_t val)128 hym8563_write_1(device_t dev, uint8_t reg, uint8_t val)
129 {
130 
131 	return (iicdev_writeto(dev, reg, &val, 1, IIC_WAIT));
132 }
133 
134 #if defined(FDT) && !defined(__powerpc64__)
135 static int
hym8563_clk_set_gate(struct clknode * clk,bool enable)136 hym8563_clk_set_gate(struct clknode *clk, bool enable)
137 {
138 	struct hym8563_clk_sc *sc;
139 	uint8_t val;
140 	int rv;
141 
142 	sc = clknode_get_softc(clk);
143 
144 	rv = hym8563_read_1(sc->base_dev, HYM8563_CLKOUT, &val);
145 	if (rv != 0) {
146 		device_printf(sc->base_dev,
147 		    "Cannot read CLKOUT registers: %d\n", rv);
148 		return (rv);
149 	}
150 	if (enable)
151 		val |= HYM8563_CLKOUT_ENABLE;
152 	else
153 		val &= ~HYM8563_CLKOUT_ENABLE;
154 	hym8563_write_1(sc->base_dev, HYM8563_CLKOUT, val);
155 	if (rv != 0) {
156 		device_printf(sc->base_dev,
157 		    "Cannot write CLKOUT registers: %d\n", rv);
158 		return (rv);
159 	}
160 	return (0);
161 }
162 
163 static int
hym8563_clk_recalc(struct clknode * clk,uint64_t * freq)164 hym8563_clk_recalc(struct clknode *clk, uint64_t *freq)
165 {
166 	struct hym8563_clk_sc *sc;
167 	uint8_t val;
168 	int rv;
169 
170 	sc = clknode_get_softc(clk);
171 
172 	rv = hym8563_read_1(sc->base_dev, HYM8563_CLKOUT, &val);
173 	if (rv != 0) {
174 		device_printf(sc->base_dev,
175 		    "Cannot read CLKOUT registers: %d\n", rv);
176 		return (rv);
177 	}
178 
179 	switch (val & HYM8563_CLKOUT_MASK) {
180 	case HYM8563_CLKOUT_32768:
181 		*freq = 32768;
182 		break;
183 	case HYM8563_CLKOUT_1024:
184 		*freq = 1024;
185 		break;
186 	case HYM8563_CLKOUT_32:
187 		*freq = 32;
188 		break;
189 	case HYM8563_CLKOUT_1:
190 		*freq = 1;
191 		break;
192 	default:
193 		return (EINVAL);
194 	}
195 	return (0);
196 }
197 static int
hym8563_clk_set(struct clknode * clk,uint64_t fparent,uint64_t * fout,int flags,int * stop)198 hym8563_clk_set(struct clknode *clk, uint64_t fparent, uint64_t *fout,
199     int flags, int *stop)
200 {
201 	struct hym8563_clk_sc *sc;
202 	uint8_t val, tmp;
203 	int rv;
204 
205 	sc = clknode_get_softc(clk);
206 
207 	switch (*fout) {
208 	case 32768:
209 		tmp = HYM8563_CLKOUT_32768;
210 		break;
211 	case 1024:
212 		tmp = HYM8563_CLKOUT_1024;
213 		break;
214 	case 32:
215 		tmp = HYM8563_CLKOUT_32;
216 		break;
217 	case 1:
218 		tmp = HYM8563_CLKOUT_1;
219 		break;
220 	default:
221 		*stop = 1;
222 		return (EINVAL);
223 	}
224 
225 	rv = hym8563_read_1(sc->base_dev, HYM8563_CLKOUT, &val);
226 	if (rv != 0) {
227 		device_printf(sc->base_dev,
228 		    "Cannot read CLKOUT registers: %d\n", rv);
229 		return (rv);
230 	}
231 
232 	val &= ~HYM8563_CLKOUT_MASK;
233 	val |= tmp;
234 	rv = hym8563_write_1(sc->base_dev, HYM8563_CLKOUT, val);
235 	if (rv != 0) {
236 		device_printf(sc->base_dev,
237 		    "Cannot write CLKOUT registers: %d\n", rv);
238 		return (rv);
239 	}
240 
241 	return (0);
242 }
243 
244 static clknode_method_t hym8563_clk_clknode_methods[] = {
245 	CLKNODEMETHOD(clknode_recalc_freq,	hym8563_clk_recalc),
246 	CLKNODEMETHOD(clknode_set_freq,		hym8563_clk_set),
247 	CLKNODEMETHOD(clknode_set_gate,		hym8563_clk_set_gate),
248 	CLKNODEMETHOD_END
249 };
250 
251 DEFINE_CLASS_1(hym8563_clk_clknode, hym8563_clk_clknode_class,
252     hym8563_clk_clknode_methods, sizeof(struct hym8563_clk_sc),
253     clknode_class);
254 
255 
256 static int
hym8563_attach_clocks(struct hym8563_softc * sc)257 hym8563_attach_clocks(struct hym8563_softc *sc)
258 {
259 	struct clkdom *clkdom;
260 	struct clknode_init_def clkidef;
261 	struct clknode *clk;
262 	struct hym8563_clk_sc *clksc;
263 	const char **clknames;
264 	phandle_t node;
265 	int nclks, rv;
266 
267 	node = ofw_bus_get_node(sc->dev);
268 
269 	/* clock-output-names are optional. Could use them for clkidef.name. */
270 	nclks = ofw_bus_string_list_to_array(node, "clock-output-names",
271 	    &clknames);
272 
273 	clkdom = clkdom_create(sc->dev);
274 
275 	memset(&clkidef, 0, sizeof(clkidef));
276 	clkidef.id = 1;
277 	clkidef.name = (nclks == 1) ? clknames[0] : "hym8563-clkout";
278 	clk = clknode_create(clkdom, &hym8563_clk_clknode_class, &clkidef);
279 	if (clk == NULL) {
280 		device_printf(sc->dev, "Cannot create '%s'.\n", clkidef.name);
281 		return (ENXIO);
282 	}
283 	clksc = clknode_get_softc(clk);
284 	clksc->base_dev = sc->dev;
285 	clknode_register(clkdom, clk);
286 
287 	rv = clkdom_finit(clkdom);
288 	if (rv != 0) {
289 		device_printf(sc->dev, "Cannot finalize clkdom initialization: "
290 		    "%d\n", rv);
291 		return (ENXIO);
292 	}
293 
294 	if (bootverbose)
295 		clkdom_dump(clkdom);
296 
297 	return (0);
298 }
299 #endif
300 
301 static int
hym8563_gettime(device_t dev,struct timespec * ts)302 hym8563_gettime(device_t dev, struct timespec *ts)
303 {
304 	struct hym8563_softc	*sc;
305 	struct bcd_clocktime	 bct;
306 	uint8_t			 buf[7];
307 	int 			 rv;
308 
309 	sc = device_get_softc(dev);
310 
311 	/* Read all RTC data */
312 	rv = hym8563_read_buf(sc->dev, HYM8563_SEC, buf, sizeof(buf));
313 	if (rv != 0) {
314 		device_printf(sc->dev, "Cannot read time registers: %d\n", rv);
315 		return (rv);
316 	}
317 
318 	/* Check for low voltage flag */
319 	if (buf[0] & HYM8563_SEC_VL)
320 	{
321 		device_printf(sc->dev,
322 		    "WARNING: RTC battery failed; time is invalid\n");
323 		return (EINVAL);
324 	}
325 
326 	bzero(&bct, sizeof(bct));
327 	bct.sec  = buf[0] & 0x7F;
328 	bct.min  = buf[1] & 0x7F;
329 	bct.hour = buf[2] & 0x3f;
330 	bct.day  = buf[3] & 0x3f;
331 	/* buf[4] is  weekday */
332 	bct.mon  = buf[5] & 0x1f;
333 	bct.year = buf[6] & 0xff;
334 	if (buf[5] & HYM8563_MONTH_CENTURY)
335 		bct.year += 0x100;
336 
337 	clock_dbgprint_bcd(sc->dev, CLOCK_DBG_READ, &bct);
338 	return (clock_bcd_to_ts(&bct, ts, false));
339 }
340 
341 static int
hym8563_settime(device_t dev,struct timespec * ts)342 hym8563_settime(device_t dev, struct timespec *ts)
343 {
344 	struct hym8563_softc	*sc;
345 	struct bcd_clocktime 	 bct;
346 	uint8_t			 buf[7];
347 	int 			 rv;
348 
349 	sc = device_get_softc(dev);
350 	ts->tv_sec -= utc_offset();
351 	clock_ts_to_bcd(ts, &bct, false);
352 	clock_dbgprint_bcd(sc->dev, CLOCK_DBG_WRITE, &bct);
353 
354 	buf[0] = bct.sec;	/* Also clear VL flag */
355 	buf[1] = bct.min;
356 	buf[2] = bct.hour;
357 	buf[3] = bct.day;
358 	buf[4] = bct.dow;
359 	buf[5] = bct.mon;
360 	buf[6] = bct.year & 0xFF;
361 	if (bct.year > 0x99)
362 		buf[5] |= HYM8563_MONTH_CENTURY;
363 
364 	/* Stop RTC */
365 	rv = hym8563_write_1(sc->dev, HYM8563_CTRL1, HYM8563_CTRL1_STOP);
366 	if (rv != 0) {
367 		device_printf(sc->dev, "Cannot write CTRL1 register: %d\n", rv);
368 		return (rv);
369 	}
370 
371 	/* Write all RTC data */
372 	rv = hym8563_write_buf(sc->dev, HYM8563_SEC, buf, sizeof(buf));
373 	if (rv != 0) {
374 		device_printf(sc->dev, "Cannot write time registers: %d\n", rv);
375 		return (rv);
376 	}
377 	return (rv);
378 
379 	/* Start RTC again */
380 	rv = hym8563_write_1(sc->dev, HYM8563_CTRL1, 0);
381 	if (rv != 0) {
382 		device_printf(sc->dev, "Cannot write CTRL1 register: %d\n", rv);
383 		return (rv);
384 	}
385 
386 	return (0);
387 }
388 
389 static void
hym8563_init(void * arg)390 hym8563_init(void *arg)
391 {
392 	struct hym8563_softc *sc;
393 	uint8_t reg;
394 	int rv;
395 
396 	sc = (struct hym8563_softc*)arg;
397 	config_intrhook_disestablish(&sc->init_hook);
398 
399 	/* Clear CTL1 register (stop and test bits) */
400 	rv = hym8563_write_1(sc->dev, HYM8563_CTRL1, 0);
401 	if (rv != 0) {
402 		device_printf(sc->dev, "Cannot init CTRL1 register: %d\n", rv);
403 		return;
404 	}
405 
406 	/* Disable interrupts and alarms */
407 	rv = hym8563_read_1(sc->dev, HYM8563_CTRL2, &reg);
408 	if (rv != 0) {
409 		device_printf(sc->dev, "Cannot read CTRL2 register: %d\n", rv);
410 		return;
411 	}
412 	rv &= ~HYM8563_CTRL2_TI_TP;
413 	rv &= ~HYM8563_CTRL2_AF;
414 	rv &= ~HYM8563_CTRL2_TF;
415 	rv = hym8563_write_1(sc->dev, HYM8563_CTRL2, 0);
416 	if (rv != 0) {
417 		device_printf(sc->dev, "Cannot write CTRL2 register: %d\n", rv);
418 		return;
419 	}
420 
421 	/*
422 	 * Register as a system realtime clock.
423 	 */
424 	clock_register_flags(sc->dev, 1000000, 0);
425 	clock_schedule(sc->dev, 1);
426 	return;
427 }
428 
429 static int
hym8563_probe(device_t dev)430 hym8563_probe(device_t dev)
431 {
432 
433 #if defined(FDT) && !defined(__powerpc64__)
434 	if (!ofw_bus_status_okay(dev))
435 		return (ENXIO);
436 
437 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
438 		device_set_desc(dev, "HYM8694 RTC");
439 		return (BUS_PROBE_DEFAULT);
440 	}
441 #endif
442 	return (ENXIO);
443 }
444 
445 static int
hym8563_attach(device_t dev)446 hym8563_attach(device_t dev)
447 {
448 	struct hym8563_softc *sc;
449 
450 	sc = device_get_softc(dev);
451 	sc->dev = dev;
452 
453 #if defined(FDT) && !defined(__powerpc64__)
454 	if (hym8563_attach_clocks(sc) != 0)
455 		return(ENXIO);
456 #endif
457 
458 	/*
459 	 * Chip init must wait until interrupts are enabled.  Often i2c access
460 	 * works only when the interrupts are available.
461 	 */
462 	sc->init_hook.ich_func = hym8563_init;
463 	sc->init_hook.ich_arg = sc;
464 	if (config_intrhook_establish(&sc->init_hook) != 0)
465 		return (ENOMEM);
466 
467 	return (0);
468 }
469 
470 static int
hym8563_detach(device_t dev)471 hym8563_detach(device_t dev)
472 {
473 
474 	clock_unregister(dev);
475 	return (0);
476 }
477 
478 static device_method_t hym8563_methods[] = {
479         /* device_if methods */
480 	DEVMETHOD(device_probe,		hym8563_probe),
481 	DEVMETHOD(device_attach,	hym8563_attach),
482 	DEVMETHOD(device_detach,	hym8563_detach),
483 
484         /* clock_if methods */
485 	DEVMETHOD(clock_gettime,	hym8563_gettime),
486 	DEVMETHOD(clock_settime,	hym8563_settime),
487 
488 	DEVMETHOD_END,
489 };
490 
491 static DEFINE_CLASS_0(hym8563_rtc, hym8563_driver, hym8563_methods,
492     sizeof(struct hym8563_softc));
493 EARLY_DRIVER_MODULE(hym8563, iicbus, hym8563_driver, NULL, NULL,
494     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
495 MODULE_VERSION(hym8563, 1);
496 MODULE_DEPEND(hym8563, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
497 #if defined(FDT) && !defined(__powerpc64__)
498 IICBUS_FDT_PNP_INFO(compat_data);
499 #endif