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, ®, 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, ®, 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