1 /* $OpenBSD: qcrtc.c,v 1.4 2024/05/13 01:15:50 jsg Exp $ */ 2 /* 3 * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> 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 22 #include <machine/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/fdt/spmivar.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/fdt.h> 30 31 #include <dev/clock_subr.h> 32 33 /* Registers. */ 34 #define RTC_WRITE 0x40 35 #define RTC_CTRL 0x46 36 #define RTC_CTRL_EN (1U << 7) 37 #define RTC_READ 0x48 38 39 struct qcrtc_softc { 40 struct device sc_dev; 41 int sc_node; 42 spmi_tag_t sc_tag; 43 int8_t sc_sid; 44 uint16_t sc_addr; 45 46 struct todr_chip_handle sc_todr; 47 }; 48 49 int qcrtc_match(struct device *, void *, void *); 50 void qcrtc_attach(struct device *, struct device *, void *); 51 52 const struct cfattach qcrtc_ca = { 53 sizeof (struct qcrtc_softc), qcrtc_match, qcrtc_attach 54 }; 55 56 struct cfdriver qcrtc_cd = { 57 NULL, "qcrtc", DV_DULL 58 }; 59 60 int qcrtc_gettime(struct todr_chip_handle *, struct timeval *); 61 int qcrtc_settime(struct todr_chip_handle *, struct timeval *); 62 63 extern int qcscm_uefi_rtc_get(uint32_t *); 64 extern int qcscm_uefi_rtc_set(uint32_t); 65 66 int 67 qcrtc_match(struct device *parent, void *match, void *aux) 68 { 69 struct spmi_attach_args *saa = aux; 70 71 return OF_is_compatible(saa->sa_node, "qcom,pmk8350-rtc"); 72 } 73 74 void 75 qcrtc_attach(struct device *parent, struct device *self, void *aux) 76 { 77 struct qcrtc_softc *sc = (struct qcrtc_softc *)self; 78 struct spmi_attach_args *saa = aux; 79 uint32_t reg[2]; 80 81 if (OF_getpropintarray(saa->sa_node, "reg", 82 reg, sizeof(reg)) != sizeof(reg)) { 83 printf(": can't find registers\n"); 84 return; 85 } 86 87 sc->sc_node = saa->sa_node; 88 sc->sc_tag = saa->sa_tag; 89 sc->sc_sid = saa->sa_sid; 90 sc->sc_addr = reg[0]; 91 92 printf("\n"); 93 94 sc->sc_todr.cookie = sc; 95 sc->sc_todr.todr_gettime = qcrtc_gettime; 96 sc->sc_todr.todr_settime = qcrtc_settime; 97 sc->sc_todr.todr_quality = 0; 98 todr_attach(&sc->sc_todr); 99 } 100 101 int 102 qcrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 103 { 104 struct qcrtc_softc *sc = handle->cookie; 105 uint32_t reg, off; 106 int error; 107 108 /* Read current counting RTC value. */ 109 error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL, 110 sc->sc_addr + RTC_READ, ®, sizeof(reg)); 111 if (error) { 112 printf("%s: error reading RTC\n", sc->sc_dev.dv_xname); 113 return error; 114 } 115 116 /* Retrieve RTC offset from either NVRAM or UEFI. */ 117 error = nvmem_read_cell(sc->sc_node, "offset", &off, sizeof(off)); 118 if (error == ENXIO || (!error && off == 0)) 119 error = qcscm_uefi_rtc_get(&off); 120 if (error) 121 return error; 122 123 tv->tv_sec = off + reg; 124 tv->tv_usec = 0; 125 return 0; 126 } 127 128 int 129 qcrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 130 { 131 struct qcrtc_softc *sc = handle->cookie; 132 uint32_t reg, off; 133 int error; 134 135 error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL, 136 sc->sc_addr + RTC_READ, ®, sizeof(reg)); 137 if (error) { 138 printf("%s: error reading RTC\n", sc->sc_dev.dv_xname); 139 return error; 140 } 141 142 /* Store RTC offset in either NVRAM or UEFI. */ 143 off = tv->tv_sec - reg; 144 error = nvmem_write_cell(sc->sc_node, "offset", &off, sizeof(off)); 145 if (error == ENXIO) 146 error = qcscm_uefi_rtc_set(off); 147 148 return error; 149 } 150