1 /* $OpenBSD: omclock.c,v 1.2 2022/04/06 18:59:26 naddy Exp $ */ 2 /* 3 * Copyright (c) 2020 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/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/ofw/openfirm.h> 26 #include <dev/ofw/ofw_clock.h> 27 #include <dev/ofw/fdt.h> 28 29 #define IDLEST_MASK (0x3 << 16) 30 #define IDLEST_FUNC (0x0 << 16) 31 #define IDLEST_TRANS (0x1 << 16) 32 #define IDLEST_IDLE (0x2 << 16) 33 #define IDLEST_DISABLED (0x3 << 16) 34 #define MODULEMODE_MASK (0x3 << 0) 35 #define MODULEMODE_DISABLED (0x0 << 0) 36 #define MODULEMODE_ENABLED (0x2 << 0) 37 38 #define HREAD4(sc, reg) \ 39 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 40 #define HWRITE4(sc, reg, val) \ 41 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 42 #define HSET4(sc, reg, bits) \ 43 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 44 #define HCLR4(sc, reg, bits) \ 45 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 46 47 struct omclock_softc { 48 struct device sc_dev; 49 bus_space_tag_t sc_iot; 50 bus_space_handle_t sc_ioh; 51 int sc_node; 52 53 struct clock_device sc_cd; 54 }; 55 56 int omclock_match(struct device *, void *, void *); 57 void omclock_attach(struct device *, struct device *, void *); 58 59 const struct cfattach omclock_ca = { 60 sizeof (struct omclock_softc), omclock_match, omclock_attach 61 }; 62 63 struct cfdriver omclock_cd = { 64 NULL, "omclock", DV_DULL 65 }; 66 67 uint32_t omclock_get_frequency(void *, uint32_t *); 68 int omclock_set_frequency(void *, uint32_t *, uint32_t); 69 void omclock_enable(void *, uint32_t *, int); 70 71 int 72 omclock_match(struct device *parent, void *match, void *aux) 73 { 74 struct fdt_attach_args *faa = aux; 75 76 return OF_is_compatible(faa->fa_node, "ti,clkctrl"); 77 } 78 79 void 80 omclock_attach(struct device *parent, struct device *self, void *aux) 81 { 82 struct omclock_softc *sc = (struct omclock_softc *)self; 83 struct fdt_attach_args *faa = aux; 84 char name[32]; 85 86 if (faa->fa_nreg < 1) { 87 printf(": no registers\n"); 88 return; 89 } 90 91 sc->sc_iot = faa->fa_iot; 92 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 93 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 94 printf(": can't map registers\n"); 95 return; 96 } 97 98 sc->sc_node = faa->fa_node; 99 if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) { 100 name[sizeof(name) - 1] = 0; 101 printf(": \"%s\"", name); 102 } 103 104 printf("\n"); 105 106 sc->sc_cd.cd_node = faa->fa_node; 107 sc->sc_cd.cd_cookie = sc; 108 sc->sc_cd.cd_get_frequency = omclock_get_frequency; 109 sc->sc_cd.cd_set_frequency = omclock_set_frequency; 110 sc->sc_cd.cd_enable = omclock_enable; 111 clock_register(&sc->sc_cd); 112 } 113 114 uint32_t 115 omclock_get_frequency(void *cookie, uint32_t *cells) 116 { 117 printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]); 118 return 0; 119 } 120 121 int 122 omclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 123 { 124 printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]); 125 return -1; 126 } 127 128 void 129 omclock_enable(void *cookie, uint32_t *cells, int on) 130 { 131 struct omclock_softc *sc = cookie; 132 uint32_t base = cells[0]; 133 uint32_t idx = cells[1]; 134 uint32_t reg; 135 int retry; 136 137 reg = HREAD4(sc, base); 138 if (idx == 0) { 139 reg &= ~MODULEMODE_MASK; 140 if (on) 141 reg |= MODULEMODE_ENABLED; 142 else 143 reg |= MODULEMODE_DISABLED; 144 } else { 145 if (on) 146 reg |= (1U << idx); 147 else 148 reg &= ~(1U << idx); 149 } 150 HWRITE4(sc, base, reg); 151 152 if (idx == 0) { 153 retry = 100; 154 while (--retry > 0) { 155 if ((HREAD4(sc, base) & IDLEST_MASK) == IDLEST_FUNC) 156 break; 157 delay(10); 158 } 159 /* XXX Hope for the best if this loop times out. */ 160 } 161 } 162