xref: /openbsd-src/sys/dev/fdt/qcrtc.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1*0f9e9ec2Sjsg /*	$OpenBSD: qcrtc.c,v 1.4 2024/05/13 01:15:50 jsg Exp $	*/
2ebbbade5Spatrick /*
3ebbbade5Spatrick  * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se>
4ebbbade5Spatrick  *
5ebbbade5Spatrick  * Permission to use, copy, modify, and distribute this software for any
6ebbbade5Spatrick  * purpose with or without fee is hereby granted, provided that the above
7ebbbade5Spatrick  * copyright notice and this permission notice appear in all copies.
8ebbbade5Spatrick  *
9ebbbade5Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ebbbade5Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ebbbade5Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ebbbade5Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ebbbade5Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ebbbade5Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ebbbade5Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ebbbade5Spatrick  */
17ebbbade5Spatrick 
18ebbbade5Spatrick #include <sys/param.h>
19ebbbade5Spatrick #include <sys/systm.h>
20ebbbade5Spatrick #include <sys/device.h>
21ebbbade5Spatrick 
22ebbbade5Spatrick #include <machine/bus.h>
23ebbbade5Spatrick #include <machine/fdt.h>
24ebbbade5Spatrick 
25ebbbade5Spatrick #include <dev/fdt/spmivar.h>
26ebbbade5Spatrick 
27ebbbade5Spatrick #include <dev/ofw/openfirm.h>
2841e484b3Spatrick #include <dev/ofw/ofw_misc.h>
29ebbbade5Spatrick #include <dev/ofw/fdt.h>
30ebbbade5Spatrick 
31ebbbade5Spatrick #include <dev/clock_subr.h>
32ebbbade5Spatrick 
33ebbbade5Spatrick /* Registers. */
34ebbbade5Spatrick #define RTC_WRITE		0x40
35ebbbade5Spatrick #define RTC_CTRL		0x46
36ebbbade5Spatrick #define  RTC_CTRL_EN			(1U << 7)
37ebbbade5Spatrick #define RTC_READ		0x48
38ebbbade5Spatrick 
39ebbbade5Spatrick struct qcrtc_softc {
40ebbbade5Spatrick 	struct device		sc_dev;
41ebbbade5Spatrick 	int			sc_node;
42ebbbade5Spatrick 	spmi_tag_t		sc_tag;
43ebbbade5Spatrick 	int8_t			sc_sid;
44ebbbade5Spatrick 	uint16_t		sc_addr;
45ebbbade5Spatrick 
46ebbbade5Spatrick 	struct todr_chip_handle sc_todr;
47ebbbade5Spatrick };
48ebbbade5Spatrick 
49ebbbade5Spatrick int qcrtc_match(struct device *, void *, void *);
50ebbbade5Spatrick void qcrtc_attach(struct device *, struct device *, void *);
51ebbbade5Spatrick 
52ebbbade5Spatrick const struct cfattach	qcrtc_ca = {
53ebbbade5Spatrick 	sizeof (struct qcrtc_softc), qcrtc_match, qcrtc_attach
54ebbbade5Spatrick };
55ebbbade5Spatrick 
56ebbbade5Spatrick struct cfdriver qcrtc_cd = {
57ebbbade5Spatrick 	NULL, "qcrtc", DV_DULL
58ebbbade5Spatrick };
59ebbbade5Spatrick 
60ebbbade5Spatrick int	qcrtc_gettime(struct todr_chip_handle *, struct timeval *);
61ebbbade5Spatrick int	qcrtc_settime(struct todr_chip_handle *, struct timeval *);
62ebbbade5Spatrick 
635172d7d1Spatrick extern int qcscm_uefi_rtc_get(uint32_t *);
645172d7d1Spatrick extern int qcscm_uefi_rtc_set(uint32_t);
655172d7d1Spatrick 
66ebbbade5Spatrick int
qcrtc_match(struct device * parent,void * match,void * aux)67ebbbade5Spatrick qcrtc_match(struct device *parent, void *match, void *aux)
68ebbbade5Spatrick {
69ebbbade5Spatrick 	struct spmi_attach_args *saa = aux;
70ebbbade5Spatrick 
71ebbbade5Spatrick 	return OF_is_compatible(saa->sa_node, "qcom,pmk8350-rtc");
72ebbbade5Spatrick }
73ebbbade5Spatrick 
74ebbbade5Spatrick void
qcrtc_attach(struct device * parent,struct device * self,void * aux)75ebbbade5Spatrick qcrtc_attach(struct device *parent, struct device *self, void *aux)
76ebbbade5Spatrick {
77ebbbade5Spatrick 	struct qcrtc_softc *sc = (struct qcrtc_softc *)self;
78ebbbade5Spatrick 	struct spmi_attach_args *saa = aux;
79ebbbade5Spatrick 	uint32_t reg[2];
80ebbbade5Spatrick 
81ebbbade5Spatrick 	if (OF_getpropintarray(saa->sa_node, "reg",
82ebbbade5Spatrick 	    reg, sizeof(reg)) != sizeof(reg)) {
83ebbbade5Spatrick 		printf(": can't find registers\n");
84ebbbade5Spatrick 		return;
85ebbbade5Spatrick 	}
86ebbbade5Spatrick 
87ebbbade5Spatrick 	sc->sc_node = saa->sa_node;
88ebbbade5Spatrick 	sc->sc_tag = saa->sa_tag;
89ebbbade5Spatrick 	sc->sc_sid = saa->sa_sid;
90ebbbade5Spatrick 	sc->sc_addr = reg[0];
91ebbbade5Spatrick 
92ebbbade5Spatrick 	printf("\n");
93ebbbade5Spatrick 
94ebbbade5Spatrick 	sc->sc_todr.cookie = sc;
95ebbbade5Spatrick 	sc->sc_todr.todr_gettime = qcrtc_gettime;
96ebbbade5Spatrick 	sc->sc_todr.todr_settime = qcrtc_settime;
97ebbbade5Spatrick 	sc->sc_todr.todr_quality = 0;
98ebbbade5Spatrick 	todr_attach(&sc->sc_todr);
99ebbbade5Spatrick }
100ebbbade5Spatrick 
101ebbbade5Spatrick int
qcrtc_gettime(struct todr_chip_handle * handle,struct timeval * tv)102ebbbade5Spatrick qcrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
103ebbbade5Spatrick {
104ebbbade5Spatrick 	struct qcrtc_softc *sc = handle->cookie;
1055172d7d1Spatrick 	uint32_t reg, off;
1065172d7d1Spatrick 	int error;
1075172d7d1Spatrick 
1085172d7d1Spatrick 	/* Read current counting RTC value. */
1095172d7d1Spatrick 	error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
1105172d7d1Spatrick 	    sc->sc_addr + RTC_READ, &reg, sizeof(reg));
1115172d7d1Spatrick 	if (error) {
1125172d7d1Spatrick 		printf("%s: error reading RTC\n", sc->sc_dev.dv_xname);
1135172d7d1Spatrick 		return error;
1145172d7d1Spatrick 	}
1155172d7d1Spatrick 
11641e484b3Spatrick 	/* Retrieve RTC offset from either NVRAM or UEFI. */
11741e484b3Spatrick 	error = nvmem_read_cell(sc->sc_node, "offset", &off, sizeof(off));
11841e484b3Spatrick 	if (error == ENXIO || (!error && off == 0))
11941e484b3Spatrick 		error = qcscm_uefi_rtc_get(&off);
12041e484b3Spatrick 	if (error)
12141e484b3Spatrick 		return error;
1225172d7d1Spatrick 
12341e484b3Spatrick 	tv->tv_sec = off + reg;
1245172d7d1Spatrick 	tv->tv_usec = 0;
1255172d7d1Spatrick 	return 0;
1265172d7d1Spatrick }
1275172d7d1Spatrick 
1285172d7d1Spatrick int
qcrtc_settime(struct todr_chip_handle * handle,struct timeval * tv)1295172d7d1Spatrick qcrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
1305172d7d1Spatrick {
1315172d7d1Spatrick 	struct qcrtc_softc *sc = handle->cookie;
1325172d7d1Spatrick 	uint32_t reg, off;
133ebbbade5Spatrick 	int error;
134ebbbade5Spatrick 
135ebbbade5Spatrick 	error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
136ebbbade5Spatrick 	    sc->sc_addr + RTC_READ, &reg, sizeof(reg));
137ebbbade5Spatrick 	if (error) {
138ebbbade5Spatrick 		printf("%s: error reading RTC\n", sc->sc_dev.dv_xname);
139ebbbade5Spatrick 		return error;
140ebbbade5Spatrick 	}
141ebbbade5Spatrick 
14241e484b3Spatrick 	/* Store RTC offset in either NVRAM or UEFI. */
14341e484b3Spatrick 	off = tv->tv_sec - reg;
14441e484b3Spatrick 	error = nvmem_write_cell(sc->sc_node, "offset", &off, sizeof(off));
14541e484b3Spatrick 	if (error == ENXIO)
14641e484b3Spatrick 		error = qcscm_uefi_rtc_set(off);
147ebbbade5Spatrick 
14841e484b3Spatrick 	return error;
149ebbbade5Spatrick }
150