xref: /openbsd/sys/dev/fdt/rkpmic.c (revision 3bef86f7)
1 /*	$OpenBSD: rkpmic.c,v 1.13 2023/04/10 04:21:20 jsg Exp $	*/
2 /*
3  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 
23 #include <dev/ofw/openfirm.h>
24 #include <dev/ofw/ofw_regulator.h>
25 #include <dev/ofw/fdt.h>
26 
27 #include <dev/i2c/i2cvar.h>
28 
29 #include <dev/clock_subr.h>
30 
31 #define RK80X_SECONDS		0x00
32 #define RK80X_MINUTES		0x01
33 #define RK80X_HOURS		0x02
34 #define RK80X_DAYS		0x03
35 #define RK80X_MONTHS		0x04
36 #define RK80X_YEARS		0x05
37 #define RK80X_WEEKS		0x06
38 #define RK80X_NRTC_REGS	7
39 
40 #define RK805_RTC_CTRL		0x10
41 #define RK808_RTC_CTRL		0x10
42 #define RK809_RTC_CTRL		0x0d
43 #define  RK80X_RTC_CTRL_STOP_RTC	0x01
44 
45 #define RK805_RTC_STATUS	0x11
46 #define RK808_RTC_STATUS	0x11
47 #define RK809_RTC_STATUS	0x0e
48 #define  RK80X_RTC_STATUS_POWER_UP	0x80
49 
50 struct rkpmic_vsel_range {
51 	uint32_t base, delta;
52 	uint8_t vsel_min, vsel_max;
53 };
54 
55 struct rkpmic_regdata {
56 	const char *name;
57 	uint8_t reg, mask;
58 	const struct rkpmic_vsel_range *vsel_range;
59 };
60 
61 /*
62  * Used by RK805 for BUCK1, BUCK2
63  *  0-59:	0.7125V-1.45V, step=12.5mV
64  *  60-62:	1.8V-2.2V, step=200mV
65  *  63:		2.3V
66  */
67 const struct rkpmic_vsel_range rk805_vsel_range1[] = {
68 	{ 712500, 12500, 0, 59 },
69 	{ 1800000, 200000, 60, 62 },
70 	{ 2300000, 0, 63, 63 },
71 	{}
72 };
73 
74 /*
75  * Used by RK805 for BUCK4
76  *  0-27:	0.8V-3.5V, step=100mV
77  */
78 const struct rkpmic_vsel_range rk805_vsel_range2[] = {
79 	{ 800000, 100000, 0, 27 },
80 	{}
81 };
82 
83 /*
84  * Used by RK805 for LDO1-3
85  *  0-26:	0.8V-3.4V, step=100mV
86  */
87 const struct rkpmic_vsel_range rk805_vsel_range3[] = {
88 	{ 800000, 100000, 0, 26 },
89 	{}
90 };
91 
92 const struct rkpmic_regdata rk805_regdata[] = {
93 	{ "DCDC_REG1", 0x2f, 0x3f, rk805_vsel_range1 },
94 	{ "DCDC_REG2", 0x33, 0x3f, rk805_vsel_range1 },
95 	{ "DCDC_REG4", 0x38, 0x1f, rk805_vsel_range2 },
96 	{ "LDO_REG1", 0x3b, 0x1f, rk805_vsel_range3 },
97 	{ "LDO_REG2", 0x3d, 0x1f, rk805_vsel_range3 },
98 	{ "LDO_REG3", 0x3f, 0x1f, rk805_vsel_range3 },
99 	{ }
100 };
101 
102 /*
103  * Used by RK808 for BUCK1 & BUCK2
104  *  0-63:	0.7125V-1.5V, step=12.5mV
105  */
106 const struct rkpmic_vsel_range rk808_vsel_range1[] = {
107 	{ 712500, 12500, 0, 63 },
108 	{}
109 };
110 
111 /*
112  * Used by RK808 for BUCK4
113  *  0-15:	1.8V-3.3V,step=100mV
114  */
115 const struct rkpmic_vsel_range rk808_vsel_range2[] = {
116 	{ 1800000, 100000, 0, 15 },
117 	{}
118 };
119 
120 /*
121  * Used by RK808 for LDO1-2, 4-5, 8
122  *  0-16:	1.8V-3.4V, step=100mV
123  */
124 const struct rkpmic_vsel_range rk808_vsel_range3[] = {
125 	{ 1800000, 100000, 0, 16 },
126 	{}
127 };
128 
129 /*
130  * Used by RK808 for LDO3
131  *   0-12:	0.8V~2.0V, step=100mV
132  *   13:	2.2V
133  *   15:	2.5V
134  */
135 const struct rkpmic_vsel_range rk808_vsel_range4[] = {
136 	{ 800000, 100000, 0, 12 },
137 	{ 2200000, 0, 13, 13 },
138 	{ 2500000, 0, 15, 15 },
139 	{}
140 };
141 
142 /*
143  * Used by RK808 for LDO6-7
144  *  0-17:	0.8V-2.5V,step=100mV
145  */
146 const struct rkpmic_vsel_range rk808_vsel_range5[] = {
147 	{ 800000, 100000, 0, 17 },
148 	{}
149 };
150 
151 const struct rkpmic_regdata rk808_regdata[] = {
152 	{ "DCDC_REG1", 0x2f, 0x3f, rk808_vsel_range1 },
153 	{ "DCDC_REG2", 0x33, 0x3f, rk808_vsel_range1 },
154 	{ "DCDC_REG4", 0x38, 0x0f, rk808_vsel_range2 },
155 	{ "LDO_REG1", 0x3b, 0x1f, rk808_vsel_range3 },
156 	{ "LDO_REG2", 0x3d, 0x1f, rk808_vsel_range3 },
157 	{ "LDO_REG3", 0x3f, 0x0f, rk808_vsel_range4 },
158 	{ "LDO_REG4", 0x41, 0x1f, rk808_vsel_range3 },
159 	{ "LDO_REG5", 0x43, 0x1f, rk808_vsel_range3 },
160 	{ "LDO_REG6", 0x45, 0x1f, rk808_vsel_range5 },
161 	{ "LDO_REG7", 0x47, 0x1f, rk808_vsel_range5 },
162 	{ "LDO_REG8", 0x49, 0x1f, rk808_vsel_range3 },
163 	{ }
164 };
165 
166 /*
167  * Used by RK809 for BUCK1-3
168  *  0-80:	0.5V-1.5V,step=12.5mV
169  *  81-89:	1.6V-2.4V,step=100mV
170  */
171 const struct rkpmic_vsel_range rk809_vsel_range1[] = {
172 	{ 500000, 12500, 0, 80 },
173 	{ 1600000, 100000, 81, 89 },
174 	{}
175 };
176 
177 /*
178  * Used by RK809 for BUCK4
179  *  0-80:	0.5V-1.5V,step=12.5mV
180  *  81-99:	1.6V-3.4V,step=100mV
181  */
182 const struct rkpmic_vsel_range rk809_vsel_range2[] = {
183 	{ 500000, 12500, 0, 80 },
184 	{ 1600000, 100000, 81, 99 },
185 	{}
186 };
187 
188 /*
189  * Used by RK809 for BUCK5
190  *  0:		1.5V
191  *  1-3:	1.8V-2.2V,step=200mV
192  *  4-5:	2.8V-3.0V,step=200mV
193  *  6-7:	3.3V-3.6V,step=300mV
194  */
195 const struct rkpmic_vsel_range rk809_vsel_range3[] = {
196 	{ 1500000, 0, 0, 0 },
197 	{ 1800000, 200000, 1, 3 },
198 	{ 2800000, 200000, 4, 5 },
199 	{ 3300000, 300000, 6, 7 },
200 	{}
201 };
202 
203 /*
204  * Used by RK809 for LDO1-7
205  *  0-112: 0.6V-3.4V,step=25mV
206  */
207 const struct rkpmic_vsel_range rk809_vsel_range4[] = {
208 	{ 600000, 25000, 0, 112 },
209 	{}
210 };
211 
212 const struct rkpmic_regdata rk809_regdata[] = {
213 	{ "DCDC_REG1", 0xbb, 0x7f, rk809_vsel_range1 },
214 	{ "DCDC_REG2", 0xbe, 0x7f, rk809_vsel_range1 },
215 	{ "DCDC_REG3", 0xc1, 0x7f, rk809_vsel_range1 },
216 	{ "DCDC_REG4", 0xc4, 0x7f, rk809_vsel_range2 },
217 	{ "DCDC_REG5", 0xde, 0x0f, rk809_vsel_range3},
218 	{ "LDO_REG1", 0xcc, 0x7f, rk809_vsel_range4 },
219 	{ "LDO_REG2", 0xce, 0x7f, rk809_vsel_range4 },
220 	{ "LDO_REG3", 0xd0, 0x7f, rk809_vsel_range4 },
221 	{ "LDO_REG4", 0xd2, 0x7f, rk809_vsel_range4 },
222 	{ "LDO_REG5", 0xd4, 0x7f, rk809_vsel_range4 },
223 	{ "LDO_REG6", 0xd6, 0x7f, rk809_vsel_range4 },
224 	{ "LDO_REG7", 0xd8, 0x7f, rk809_vsel_range4 },
225 	{ "LDO_REG8", 0xda, 0x7f, rk809_vsel_range4 },
226 	{ "LDO_REG9", 0xdc, 0x7f, rk809_vsel_range4 },
227 	{ }
228 };
229 
230 /*
231  * Used by RK817 for BOOST
232  *  0-7: 4.7V-5.4V,step=100mV
233  */
234 const struct rkpmic_vsel_range rk817_boost_range[] = {
235 	{ 4700000, 100000, 0, 7 },
236 	{}
237 };
238 
239 const struct rkpmic_regdata rk817_regdata[] = {
240 	{ "DCDC_REG1", 0xbb, 0x7f, rk809_vsel_range1 },
241 	{ "DCDC_REG2", 0xbe, 0x7f, rk809_vsel_range1 },
242 	{ "DCDC_REG3", 0xc1, 0x7f, rk809_vsel_range1 },
243 	{ "DCDC_REG4", 0xc4, 0x7f, rk809_vsel_range2 },
244 	{ "LDO_REG1", 0xcc, 0x7f, rk809_vsel_range4 },
245 	{ "LDO_REG2", 0xce, 0x7f, rk809_vsel_range4 },
246 	{ "LDO_REG3", 0xd0, 0x7f, rk809_vsel_range4 },
247 	{ "LDO_REG4", 0xd2, 0x7f, rk809_vsel_range4 },
248 	{ "LDO_REG5", 0xd4, 0x7f, rk809_vsel_range4 },
249 	{ "LDO_REG6", 0xd6, 0x7f, rk809_vsel_range4 },
250 	{ "LDO_REG7", 0xd8, 0x7f, rk809_vsel_range4 },
251 	{ "LDO_REG8", 0xda, 0x7f, rk809_vsel_range4 },
252 	{ "LDO_REG9", 0xdc, 0x7f, rk809_vsel_range4 },
253 	{ "BOOST", 0xde, 0x07, rk817_boost_range },
254 	{ }
255 };
256 
257 struct rkpmic_softc {
258 	struct device sc_dev;
259 	i2c_tag_t sc_tag;
260 	i2c_addr_t sc_addr;
261 
262 	int sc_rtc_ctrl_reg, sc_rtc_status_reg;
263 	struct todr_chip_handle sc_todr;
264 	const struct rkpmic_regdata *sc_regdata;
265 };
266 
267 int	rkpmic_match(struct device *, void *, void *);
268 void	rkpmic_attach(struct device *, struct device *, void *);
269 
270 const struct cfattach rkpmic_ca = {
271 	sizeof(struct rkpmic_softc), rkpmic_match, rkpmic_attach
272 };
273 
274 struct cfdriver rkpmic_cd = {
275 	NULL, "rkpmic", DV_DULL
276 };
277 
278 void	rkpmic_attach_regulator(struct rkpmic_softc *, int);
279 uint8_t	rkpmic_reg_read(struct rkpmic_softc *, int);
280 void	rkpmic_reg_write(struct rkpmic_softc *, int, uint8_t);
281 int	rkpmic_clock_read(struct rkpmic_softc *, struct clock_ymdhms *);
282 int	rkpmic_clock_write(struct rkpmic_softc *, struct clock_ymdhms *);
283 int	rkpmic_gettime(struct todr_chip_handle *, struct timeval *);
284 int	rkpmic_settime(struct todr_chip_handle *, struct timeval *);
285 
286 int
287 rkpmic_match(struct device *parent, void *match, void *aux)
288 {
289 	struct i2c_attach_args *ia = aux;
290 
291 	return (strcmp(ia->ia_name, "rockchip,rk805") == 0 ||
292 	    strcmp(ia->ia_name, "rockchip,rk808") == 0 ||
293 	    strcmp(ia->ia_name, "rockchip,rk809") == 0 ||
294 	    strcmp(ia->ia_name, "rockchip,rk817") == 0);
295 }
296 
297 void
298 rkpmic_attach(struct device *parent, struct device *self, void *aux)
299 {
300 	struct rkpmic_softc *sc = (struct rkpmic_softc *)self;
301 	struct i2c_attach_args *ia = aux;
302 	int node = *(int *)ia->ia_cookie;
303 	const char *chip;
304 
305 	sc->sc_tag = ia->ia_tag;
306 	sc->sc_addr = ia->ia_addr;
307 
308 	sc->sc_todr.cookie = sc;
309 	sc->sc_todr.todr_gettime = rkpmic_gettime;
310 	sc->sc_todr.todr_settime = rkpmic_settime;
311 	sc->sc_todr.todr_quality = 0;
312 	todr_attach(&sc->sc_todr);
313 
314 	if (OF_is_compatible(node, "rockchip,rk805")) {
315 		chip = "RK805";
316 		sc->sc_rtc_ctrl_reg = RK805_RTC_CTRL;
317 		sc->sc_rtc_status_reg = RK805_RTC_STATUS;
318 		sc->sc_regdata = rk805_regdata;
319 	} else if (OF_is_compatible(node, "rockchip,rk808")) {
320 		chip = "RK808";
321 		sc->sc_rtc_ctrl_reg = RK808_RTC_CTRL;
322 		sc->sc_rtc_status_reg = RK808_RTC_STATUS;
323 		sc->sc_regdata = rk808_regdata;
324 	} else if (OF_is_compatible(node, "rockchip,rk809")) {
325 		chip = "RK809";
326 		sc->sc_rtc_ctrl_reg = RK809_RTC_CTRL;
327 		sc->sc_rtc_status_reg = RK809_RTC_STATUS;
328 		sc->sc_regdata = rk809_regdata;
329 	} else {
330 		chip = "RK817";
331 		sc->sc_rtc_ctrl_reg = RK809_RTC_CTRL;
332 		sc->sc_rtc_status_reg = RK809_RTC_STATUS;
333 		sc->sc_regdata = rk817_regdata;
334 	}
335 	printf(": %s\n", chip);
336 
337 	node = OF_getnodebyname(node, "regulators");
338 	if (node == 0)
339 		return;
340 	for (node = OF_child(node); node; node = OF_peer(node))
341 		rkpmic_attach_regulator(sc, node);
342 }
343 
344 struct rkpmic_regulator {
345 	struct rkpmic_softc *rr_sc;
346 
347 	uint8_t rr_reg, rr_mask;
348 	const struct rkpmic_vsel_range *rr_vsel_range;
349 
350 	struct regulator_device rr_rd;
351 };
352 
353 uint32_t rkpmic_get_voltage(void *);
354 int	rkpmic_set_voltage(void *, uint32_t);
355 
356 void
357 rkpmic_attach_regulator(struct rkpmic_softc *sc, int node)
358 {
359 	struct rkpmic_regulator *rr;
360 	char name[32];
361 	int i;
362 
363 	name[0] = 0;
364 	OF_getprop(node, "name", name, sizeof(name));
365 	name[sizeof(name) - 1] = 0;
366 	for (i = 0; sc->sc_regdata[i].name; i++) {
367 		if (strcmp(sc->sc_regdata[i].name, name) == 0)
368 			break;
369 	}
370 	if (sc->sc_regdata[i].name == NULL)
371 		return;
372 
373 	rr = malloc(sizeof(*rr), M_DEVBUF, M_WAITOK | M_ZERO);
374 	rr->rr_sc = sc;
375 
376 	rr->rr_reg = sc->sc_regdata[i].reg;
377 	rr->rr_mask = sc->sc_regdata[i].mask;
378 	rr->rr_vsel_range = sc->sc_regdata[i].vsel_range;
379 
380 	rr->rr_rd.rd_node = node;
381 	rr->rr_rd.rd_cookie = rr;
382 	rr->rr_rd.rd_get_voltage = rkpmic_get_voltage;
383 	rr->rr_rd.rd_set_voltage = rkpmic_set_voltage;
384 	regulator_register(&rr->rr_rd);
385 }
386 
387 uint32_t
388 rkpmic_get_voltage(void *cookie)
389 {
390 	struct rkpmic_regulator *rr = cookie;
391 	const struct rkpmic_vsel_range *vsel_range = rr->rr_vsel_range;
392 	uint8_t vsel;
393 	uint32_t ret = 0;
394 
395 	vsel = rkpmic_reg_read(rr->rr_sc, rr->rr_reg) & rr->rr_mask;
396 
397 	while (vsel_range->base) {
398 		ret = vsel_range->base;
399 		if (vsel >= vsel_range->vsel_min &&
400 		    vsel <= vsel_range->vsel_max) {
401 			ret += (vsel - vsel_range->vsel_min) *
402 			    vsel_range->delta;
403 			break;
404 		} else
405 			ret += (vsel_range->vsel_max - vsel_range->vsel_min) *
406 			    vsel_range->delta;
407 		vsel_range++;
408 
409 	}
410 
411 	return ret;
412 }
413 
414 int
415 rkpmic_set_voltage(void *cookie, uint32_t voltage)
416 {
417 	struct rkpmic_regulator *rr = cookie;
418 	const struct rkpmic_vsel_range *vsel_range = rr->rr_vsel_range;
419 	uint32_t vmin, vmax, volt;
420 	uint8_t reg, vsel;
421 
422 	while (vsel_range->base) {
423 		vmin = vsel_range->base;
424 		vmax = vmin + (vsel_range->vsel_max - vsel_range->vsel_min) *
425 		    vsel_range->delta;
426 		if (voltage < vmin)
427 			return EINVAL;
428 		if (voltage <= vmax) {
429 			vsel = vsel_range->vsel_min;
430 			volt = vsel_range->base;
431 			while (vsel <= vsel_range->vsel_max) {
432 				if (volt == voltage)
433 					break;
434 				else {
435 					vsel++;
436 					volt += vsel_range->delta;
437 				}
438 			}
439 			if (volt != voltage)
440 				return EINVAL;
441 			break;
442 		}
443 		vsel_range++;
444 	}
445 
446 	if (vsel_range->base == 0)
447 		return EINVAL;
448 
449 	reg = rkpmic_reg_read(rr->rr_sc, rr->rr_reg);
450 	reg &= ~rr->rr_mask;
451 	reg |= vsel;
452 	rkpmic_reg_write(rr->rr_sc, rr->rr_reg, reg);
453 
454 	return 0;
455 }
456 
457 int
458 rkpmic_gettime(struct todr_chip_handle *handle, struct timeval *tv)
459 {
460 	struct rkpmic_softc *sc = handle->cookie;
461 	struct clock_ymdhms dt;
462 	time_t secs;
463 	int error;
464 
465 	error = rkpmic_clock_read(sc, &dt);
466 	if (error)
467 		return error;
468 
469 	if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
470 	    dt.dt_day > 31 || dt.dt_day == 0 ||
471 	    dt.dt_mon > 12 || dt.dt_mon == 0 ||
472 	    dt.dt_year < POSIX_BASE_YEAR)
473 		return EINVAL;
474 
475 	/*
476 	 * The RTC thinks November has 31 days.  Match what Linux does
477 	 * and undo the damage by considering the calendars to be in
478 	 * sync on January 1st 2016.
479 	 */
480 	secs = clock_ymdhms_to_secs(&dt);
481 	secs += (dt.dt_year - 2016 + (dt.dt_mon == 12 ? 1 : 0)) * 86400;
482 
483 	tv->tv_sec = secs;
484 	tv->tv_usec = 0;
485 	return 0;
486 }
487 
488 int
489 rkpmic_settime(struct todr_chip_handle *handle, struct timeval *tv)
490 {
491 	struct rkpmic_softc *sc = handle->cookie;
492 	struct clock_ymdhms dt;
493 	time_t secs;
494 
495 	/*
496 	 * Take care of the November 31st braindamage here as well.
497 	 * Don't try to be clever, just do the conversion in two
498 	 * steps, first taking care of November 31 in previous years,
499 	 * and then taking care of days in December of the current
500 	 * year.  December 1st turns into November 31st!
501 	 */
502 	secs = tv->tv_sec;
503 	clock_secs_to_ymdhms(secs, &dt);
504 	secs -= (dt.dt_year - 2016) * 86400;
505 	clock_secs_to_ymdhms(secs, &dt);
506 	if (dt.dt_mon == 12) {
507 		dt.dt_day--;
508 		if (dt.dt_day == 0) {
509 			dt.dt_mon = 11;
510 			dt.dt_day = 31;
511 		}
512 	}
513 
514 	return rkpmic_clock_write(sc, &dt);
515 }
516 
517 uint8_t
518 rkpmic_reg_read(struct rkpmic_softc *sc, int reg)
519 {
520 	uint8_t cmd = reg;
521 	uint8_t val;
522 	int error;
523 
524 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
525 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
526 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
527 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
528 
529 	if (error) {
530 		printf("%s: can't read register 0x%02x\n",
531 		    sc->sc_dev.dv_xname, reg);
532 		val = 0xff;
533 	}
534 
535 	return val;
536 }
537 
538 void
539 rkpmic_reg_write(struct rkpmic_softc *sc, int reg, uint8_t val)
540 {
541 	uint8_t cmd = reg;
542 	int error;
543 
544 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
545 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
546 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
547 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
548 
549 	if (error) {
550 		printf("%s: can't write register 0x%02x\n",
551 		    sc->sc_dev.dv_xname, reg);
552 	}
553 }
554 
555 int
556 rkpmic_clock_read(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
557 {
558 	uint8_t regs[RK80X_NRTC_REGS];
559 	uint8_t cmd = RK80X_SECONDS;
560 	uint8_t status;
561 	int error;
562 
563 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
564 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
565 	    &cmd, sizeof(cmd), regs, RK80X_NRTC_REGS, I2C_F_POLL);
566 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
567 
568 	if (error) {
569 		printf("%s: can't read RTC\n", sc->sc_dev.dv_xname);
570 		return error;
571 	}
572 
573 	/*
574 	 * Convert the RK80x's register values into something useable.
575 	 */
576 	dt->dt_sec = FROMBCD(regs[0]);
577 	dt->dt_min = FROMBCD(regs[1]);
578 	dt->dt_hour = FROMBCD(regs[2]);
579 	dt->dt_day = FROMBCD(regs[3]);
580 	dt->dt_mon = FROMBCD(regs[4]);
581 	dt->dt_year = FROMBCD(regs[5]) + 2000;
582 
583 	/* Consider the time to be invalid if the POWER_UP bit is set. */
584 	status = rkpmic_reg_read(sc, sc->sc_rtc_status_reg);
585 	if (status & RK80X_RTC_STATUS_POWER_UP)
586 		return EINVAL;
587 
588 	return 0;
589 }
590 
591 int
592 rkpmic_clock_write(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
593 {
594 	uint8_t regs[RK80X_NRTC_REGS];
595 	uint8_t cmd = RK80X_SECONDS;
596 	int error;
597 
598 	/*
599 	 * Convert our time representation into something the RK80x
600 	 * can understand.
601 	 */
602 	regs[0] = TOBCD(dt->dt_sec);
603 	regs[1] = TOBCD(dt->dt_min);
604 	regs[2] = TOBCD(dt->dt_hour);
605 	regs[3] = TOBCD(dt->dt_day);
606 	regs[4] = TOBCD(dt->dt_mon);
607 	regs[5] = TOBCD(dt->dt_year - 2000);
608 	regs[6] = TOBCD(dt->dt_wday);
609 
610 	/* Stop RTC such that we can write to it. */
611 	rkpmic_reg_write(sc, sc->sc_rtc_ctrl_reg, RK80X_RTC_CTRL_STOP_RTC);
612 
613 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
614 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
615 	    &cmd, sizeof(cmd), regs, RK80X_NRTC_REGS, I2C_F_POLL);
616 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
617 
618 	/* Restart RTC. */
619 	rkpmic_reg_write(sc, sc->sc_rtc_ctrl_reg, 0);
620 
621 	if (error) {
622 		printf("%s: can't write RTC\n", sc->sc_dev.dv_xname);
623 		return error;
624 	}
625 
626 	/* Clear POWER_UP bit to indicate the time is now valid. */
627 	rkpmic_reg_write(sc, sc->sc_rtc_status_reg, RK80X_RTC_STATUS_POWER_UP);
628 
629 	return 0;
630 }
631