1 /* $OpenBSD: imxrtc.c,v 1.1 2018/06/16 14:11:35 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2018 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 22 #include <machine/intr.h> 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/fdt.h> 28 #include <dev/ofw/ofw_misc.h> 29 30 #include <dev/clock_subr.h> 31 32 extern todr_chip_handle_t todr_handle; 33 34 /* Registers. */ 35 #define LPCR 0x38 36 #define LPCR_SRTC_ENV (1 << 0) 37 #define LPSR 0x4c 38 #define LPSRTCMR 0x50 39 #define LPSRTCLR 0x54 40 41 #define HREAD4(sc, reg) \ 42 (regmap_read_4((sc)->sc_rm, (reg))) 43 #define HWRITE4(sc, reg, val) \ 44 regmap_write_4((sc)->sc_rm, (reg), (val)) 45 46 struct imxrtc_softc { 47 struct device sc_dev; 48 struct regmap *sc_rm; 49 50 struct todr_chip_handle sc_todr; 51 }; 52 53 int imxrtc_match(struct device *, void *, void *); 54 void imxrtc_attach(struct device *, struct device *, void *); 55 56 struct cfattach imxrtc_ca = { 57 sizeof (struct imxrtc_softc), imxrtc_match, imxrtc_attach 58 }; 59 60 struct cfdriver imxrtc_cd = { 61 NULL, "imxrtc", DV_DULL 62 }; 63 64 int imxrtc_gettime(struct todr_chip_handle *, struct timeval *); 65 int imxrtc_settime(struct todr_chip_handle *, struct timeval *); 66 67 int 68 imxrtc_match(struct device *parent, void *match, void *aux) 69 { 70 struct fdt_attach_args *faa = aux; 71 72 return OF_is_compatible(faa->fa_node, "fsl,sec-v4.0-mon-rtc-lp"); 73 } 74 75 void 76 imxrtc_attach(struct device *parent, struct device *self, void *aux) 77 { 78 struct imxrtc_softc *sc = (struct imxrtc_softc *)self; 79 struct fdt_attach_args *faa = aux; 80 uint32_t regmap; 81 82 regmap = OF_getpropint(faa->fa_node, "regmap", 0); 83 sc->sc_rm = regmap_byphandle(regmap); 84 if (sc->sc_rm == NULL) { 85 printf(": no registers\n"); 86 return; 87 } 88 89 printf("\n"); 90 91 sc->sc_todr.cookie = sc; 92 sc->sc_todr.todr_gettime = imxrtc_gettime; 93 sc->sc_todr.todr_settime = imxrtc_settime; 94 todr_handle = &sc->sc_todr; 95 } 96 97 int 98 imxrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 99 { 100 struct imxrtc_softc *sc = handle->cookie; 101 uint64_t mr, lr, srtc, srtc2; 102 uint32_t cr; 103 int retries; 104 int s; 105 106 cr = HREAD4(sc, LPCR); 107 if ((cr & LPCR_SRTC_ENV) == 0) 108 return EINVAL; 109 110 /* 111 * Read counters until we read back the same values twice. 112 * This shouldn't take more than two attempts; throw in an 113 * extra round just in case. 114 */ 115 s = splhigh(); 116 mr = HREAD4(sc, LPSRTCMR); 117 lr = HREAD4(sc, LPSRTCLR); 118 srtc = (mr << 32) | lr; 119 for (retries = 3; retries > 0; retries--) { 120 mr = HREAD4(sc, LPSRTCMR); 121 lr = HREAD4(sc, LPSRTCLR); 122 srtc2 = (mr << 32) | lr; 123 if (srtc == srtc2) 124 break; 125 srtc = srtc2; 126 } 127 splx(s); 128 if (retries == 0) 129 return EIO; 130 131 tv->tv_sec = srtc / 32768; 132 tv->tv_usec = ((srtc % 32768) * 1000000U) / 32768U; 133 return 0; 134 } 135 136 int 137 imxrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 138 { 139 struct imxrtc_softc *sc = handle->cookie; 140 uint64_t srtc; 141 uint32_t cr; 142 int timeout; 143 144 /* Disable RTC. */ 145 cr = HREAD4(sc, LPCR); 146 cr &= ~LPCR_SRTC_ENV; 147 HWRITE4(sc, LPCR, cr); 148 for (timeout = 1000000; timeout > 0; timeout--) { 149 if ((HREAD4(sc, LPCR) & LPCR_SRTC_ENV) == 0) 150 break; 151 } 152 153 srtc = tv->tv_sec * 32768 + (tv->tv_usec * 32768U / 1000000U); 154 HWRITE4(sc, LPSRTCMR, srtc >> 32); 155 HWRITE4(sc, LPSRTCLR, srtc & 0xffffffff); 156 157 /* Enable RTC. */ 158 cr |= LPCR_SRTC_ENV; 159 HWRITE4(sc, LPCR, cr); 160 for (timeout = 1000000; timeout > 0; timeout--) { 161 if (HREAD4(sc, LPCR) & LPCR_SRTC_ENV) 162 break; 163 } 164 165 return 0; 166 } 167