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