1 /* $NetBSD: sunxi_gates.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: sunxi_gates.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/cpu.h> 35 #include <sys/device.h> 36 #include <sys/kmem.h> 37 38 #include <dev/fdt/fdtvar.h> 39 40 #include <dev/clk/clk_backend.h> 41 42 #define GATE_REG(index) (((index) / 32) * 4) 43 #define GATE_MASK(index) __BIT((index) % 32) 44 45 static const struct device_compatible_entry compat_data[] = { 46 { .compat = "allwinner,sun4i-a10-gates-clk" }, 47 { .compat = "allwinner,sun9i-a80-apbs-gates-clk" }, 48 DEVICE_COMPAT_EOL 49 }; 50 51 struct sunxi_gate { 52 struct clk base; 53 u_int index; 54 55 TAILQ_ENTRY(sunxi_gate) gates; 56 }; 57 58 struct sunxi_gates_softc { 59 device_t sc_dev; 60 bus_space_tag_t sc_bst; 61 bus_space_handle_t sc_bsh; 62 int sc_phandle; 63 64 struct clk_domain sc_clkdom; 65 TAILQ_HEAD(, sunxi_gate) sc_gates; 66 }; 67 68 #define GATE_READ(sc, reg) \ 69 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 70 #define GATE_WRITE(sc, reg, val) \ 71 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 72 73 static struct clk * 74 sunxi_gates_clock_decode(device_t dev, int cc_phandle, const void *data, 75 size_t len) 76 { 77 struct sunxi_gates_softc * const sc = device_private(dev); 78 struct sunxi_gate *gate; 79 80 if (len != 4) 81 return NULL; 82 83 const u_int index = be32dec(data); 84 85 TAILQ_FOREACH(gate, &sc->sc_gates, gates) 86 if (gate->index == index) 87 return &gate->base; 88 89 return NULL; 90 } 91 92 static const struct fdtbus_clock_controller_func sunxi_gates_fdtclock_funcs = { 93 .decode = sunxi_gates_clock_decode, 94 }; 95 96 static struct clk * 97 sunxi_gates_clock_get(void *priv, const char *name) 98 { 99 struct sunxi_gates_softc * const sc = priv; 100 struct sunxi_gate *gate; 101 102 TAILQ_FOREACH(gate, &sc->sc_gates, gates) 103 if (strcmp(gate->base.name, name) == 0) 104 return &gate->base; 105 106 return NULL; 107 } 108 109 static void 110 sunxi_gates_clock_put(void *priv, struct clk *clk) 111 { 112 } 113 114 static u_int 115 sunxi_gates_clock_get_rate(void *priv, struct clk *clkp) 116 { 117 struct sunxi_gates_softc * const sc = priv; 118 struct sunxi_gate *gate = (struct sunxi_gate *)clkp; 119 struct clk *clkp_parent; 120 121 clkp_parent = clk_get_parent(clkp); 122 if (clkp_parent == NULL) 123 return 0; 124 125 const bus_size_t gate_reg = GATE_REG(gate->index); 126 const uint32_t gate_mask = GATE_MASK(gate->index); 127 128 if ((GATE_READ(sc, gate_reg) & gate_mask) == 0) 129 return 0; 130 131 return clk_get_rate(clkp_parent); 132 } 133 134 static int 135 sunxi_gates_clock_enable(void *priv, struct clk *clkp) 136 { 137 struct sunxi_gates_softc * const sc = priv; 138 struct sunxi_gate *gate = (struct sunxi_gate *)clkp; 139 uint32_t val; 140 141 const bus_size_t gate_reg = GATE_REG(gate->index); 142 const uint32_t gate_mask = GATE_MASK(gate->index); 143 144 val = GATE_READ(sc, gate_reg); 145 val |= gate_mask; 146 GATE_WRITE(sc, gate_reg, val); 147 148 return 0; 149 } 150 151 static int 152 sunxi_gates_clock_disable(void *priv, struct clk *clkp) 153 { 154 struct sunxi_gates_softc * const sc = priv; 155 struct sunxi_gate *gate = (struct sunxi_gate *)clkp; 156 uint32_t val; 157 158 const bus_size_t gate_reg = GATE_REG(gate->index); 159 const uint32_t gate_mask = GATE_MASK(gate->index); 160 161 val = GATE_READ(sc, gate_reg); 162 val &= ~gate_mask; 163 GATE_WRITE(sc, gate_reg, val); 164 165 return 0; 166 } 167 168 static struct clk * 169 sunxi_gates_clock_get_parent(void *priv, struct clk *clkp) 170 { 171 struct sunxi_gates_softc * const sc = priv; 172 173 return fdtbus_clock_get_index(sc->sc_phandle, 0); 174 } 175 176 static const struct clk_funcs sunxi_gates_clock_funcs = { 177 .get = sunxi_gates_clock_get, 178 .put = sunxi_gates_clock_put, 179 .get_rate = sunxi_gates_clock_get_rate, 180 .enable = sunxi_gates_clock_enable, 181 .disable = sunxi_gates_clock_disable, 182 .get_parent = sunxi_gates_clock_get_parent, 183 }; 184 185 static void 186 sunxi_gates_print(struct sunxi_gates_softc *sc) 187 { 188 struct sunxi_gate *gate; 189 struct clk *clkp_parent; 190 191 TAILQ_FOREACH(gate, &sc->sc_gates, gates) { 192 clkp_parent = clk_get_parent(&gate->base); 193 194 aprint_debug_dev(sc->sc_dev, 195 "%3d %-12s %2s %-12s %-7s ", 196 gate->index, 197 gate->base.name, 198 clkp_parent ? "<-" : "", 199 clkp_parent ? clkp_parent->name : "", 200 "gate"); 201 aprint_debug("%10d Hz\n", clk_get_rate(&gate->base)); 202 } 203 } 204 205 static int 206 sunxi_gates_match(device_t parent, cfdata_t cf, void *aux) 207 { 208 struct fdt_attach_args * const faa = aux; 209 210 return of_compatible_match(faa->faa_phandle, compat_data); 211 } 212 213 static void 214 sunxi_gates_attach(device_t parent, device_t self, void *aux) 215 { 216 struct sunxi_gates_softc * const sc = device_private(self); 217 struct fdt_attach_args * const faa = aux; 218 const int phandle = faa->faa_phandle; 219 struct sunxi_gate *gate; 220 const u_int *indices; 221 bus_addr_t addr; 222 bus_size_t size; 223 int len, i; 224 225 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 226 aprint_error(": couldn't get registers\n"); 227 return; 228 } 229 230 sc->sc_dev = self; 231 sc->sc_phandle = phandle; 232 sc->sc_bst = faa->faa_bst; 233 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 234 aprint_error(": couldn't map registers\n"); 235 return; 236 } 237 TAILQ_INIT(&sc->sc_gates); 238 239 aprint_naive("\n"); 240 aprint_normal("\n"); 241 242 sc->sc_clkdom.funcs = &sunxi_gates_clock_funcs; 243 sc->sc_clkdom.priv = sc; 244 245 indices = fdtbus_get_prop(phandle, "clock-indices", &len); 246 if (indices == NULL) { 247 aprint_error_dev(self, "no clock-indices property\n"); 248 return; 249 } 250 251 for (i = 0; 252 len >= sizeof(u_int); 253 len -= sizeof(u_int), i++, indices++) { 254 const u_int index = be32dec(indices); 255 const char *name = fdtbus_get_string_index(phandle, 256 "clock-output-names", i); 257 258 if (name == NULL) { 259 aprint_error_dev(self, "no name for clk index %d\n", 260 index); 261 continue; 262 } 263 264 gate = kmem_zalloc(sizeof(*gate), KM_SLEEP); 265 gate->base.domain = &sc->sc_clkdom; 266 gate->base.name = name; 267 gate->index = index; 268 269 TAILQ_INSERT_TAIL(&sc->sc_gates, gate, gates); 270 } 271 272 fdtbus_register_clock_controller(sc->sc_dev, phandle, 273 &sunxi_gates_fdtclock_funcs); 274 275 sunxi_gates_print(sc); 276 } 277 278 CFATTACH_DECL_NEW(sunxi_gates, sizeof(struct sunxi_gates_softc), 279 sunxi_gates_match, sunxi_gates_attach, NULL, NULL); 280