1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2017,2018 Emmanuel Vadot <manu@freebsd.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * Allwinner Clock Control Unit 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/rman.h> 36 #include <sys/kernel.h> 37 #include <sys/lock.h> 38 #include <sys/module.h> 39 #include <sys/mutex.h> 40 #include <machine/bus.h> 41 42 #include <dev/fdt/simplebus.h> 43 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include <dev/clk/clk.h> 48 #include <dev/clk/clk_gate.h> 49 50 #include <dev/hwreset/hwreset.h> 51 52 #include <dev/clk/allwinner/aw_ccung.h> 53 #include <dev/clk/allwinner/aw_clk.h> 54 55 #ifdef __aarch64__ 56 #include "opt_soc.h" 57 #endif 58 59 #include "clkdev_if.h" 60 #include "hwreset_if.h" 61 62 #if 0 63 #define dprintf(format, arg...) device_printf(dev, "%s: " format, __func__, arg) 64 #else 65 #define dprintf(format, arg...) 66 #endif 67 68 static struct resource_spec aw_ccung_spec[] = { 69 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 70 { -1, 0 } 71 }; 72 73 #define CCU_READ4(sc, reg) bus_read_4((sc)->res, (reg)) 74 #define CCU_WRITE4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 75 76 static int 77 aw_ccung_write_4(device_t dev, bus_addr_t addr, uint32_t val) 78 { 79 struct aw_ccung_softc *sc; 80 81 sc = device_get_softc(dev); 82 dprintf("offset=%lx write %x\n", addr, val); 83 CCU_WRITE4(sc, addr, val); 84 return (0); 85 } 86 87 static int 88 aw_ccung_read_4(device_t dev, bus_addr_t addr, uint32_t *val) 89 { 90 struct aw_ccung_softc *sc; 91 92 sc = device_get_softc(dev); 93 94 *val = CCU_READ4(sc, addr); 95 dprintf("offset=%lx Read %x\n", addr, *val); 96 return (0); 97 } 98 99 static int 100 aw_ccung_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) 101 { 102 struct aw_ccung_softc *sc; 103 uint32_t reg; 104 105 sc = device_get_softc(dev); 106 107 dprintf("offset=%lx clr: %x set: %x\n", addr, clr, set); 108 reg = CCU_READ4(sc, addr); 109 reg &= ~clr; 110 reg |= set; 111 CCU_WRITE4(sc, addr, reg); 112 113 return (0); 114 } 115 116 static int 117 aw_ccung_reset_assert(device_t dev, intptr_t id, bool reset) 118 { 119 struct aw_ccung_softc *sc; 120 uint32_t val; 121 122 sc = device_get_softc(dev); 123 124 dprintf("%sassert reset id %ld\n", reset ? "" : "De", id); 125 if (id >= sc->nresets || sc->resets[id].offset == 0) 126 return (0); 127 128 mtx_lock(&sc->mtx); 129 val = CCU_READ4(sc, sc->resets[id].offset); 130 dprintf("offset=%x Read %x\n", sc->resets[id].offset, val); 131 if (reset) 132 val &= ~(1 << sc->resets[id].shift); 133 else 134 val |= 1 << sc->resets[id].shift; 135 dprintf("offset=%x Write %x\n", sc->resets[id].offset, val); 136 CCU_WRITE4(sc, sc->resets[id].offset, val); 137 mtx_unlock(&sc->mtx); 138 139 return (0); 140 } 141 142 static int 143 aw_ccung_reset_is_asserted(device_t dev, intptr_t id, bool *reset) 144 { 145 struct aw_ccung_softc *sc; 146 uint32_t val; 147 148 sc = device_get_softc(dev); 149 150 if (id >= sc->nresets || sc->resets[id].offset == 0) 151 return (0); 152 153 mtx_lock(&sc->mtx); 154 val = CCU_READ4(sc, sc->resets[id].offset); 155 dprintf("offset=%x Read %x\n", sc->resets[id].offset, val); 156 *reset = (val & (1 << sc->resets[id].shift)) != 0 ? false : true; 157 mtx_unlock(&sc->mtx); 158 159 return (0); 160 } 161 162 static void 163 aw_ccung_device_lock(device_t dev) 164 { 165 struct aw_ccung_softc *sc; 166 167 sc = device_get_softc(dev); 168 mtx_lock(&sc->mtx); 169 } 170 171 static void 172 aw_ccung_device_unlock(device_t dev) 173 { 174 struct aw_ccung_softc *sc; 175 176 sc = device_get_softc(dev); 177 mtx_unlock(&sc->mtx); 178 } 179 180 static int 181 aw_ccung_register_gates(struct aw_ccung_softc *sc) 182 { 183 struct clk_gate_def def; 184 int i; 185 186 for (i = 0; i < sc->ngates; i++) { 187 if (sc->gates[i].name == NULL) 188 continue; 189 memset(&def, 0, sizeof(def)); 190 def.clkdef.id = i; 191 def.clkdef.name = sc->gates[i].name; 192 def.clkdef.parent_names = &sc->gates[i].parent_name; 193 def.clkdef.parent_cnt = 1; 194 def.offset = sc->gates[i].offset; 195 def.shift = sc->gates[i].shift; 196 def.mask = 1; 197 def.on_value = 1; 198 def.off_value = 0; 199 clknode_gate_register(sc->clkdom, &def); 200 } 201 202 return (0); 203 } 204 205 static void 206 aw_ccung_init_clocks(struct aw_ccung_softc *sc) 207 { 208 struct clknode *clknode; 209 int i, error; 210 211 for (i = 0; i < sc->n_clk_init; i++) { 212 clknode = clknode_find_by_name(sc->clk_init[i].name); 213 if (clknode == NULL) { 214 device_printf(sc->dev, "Cannot find clock %s\n", 215 sc->clk_init[i].name); 216 continue; 217 } 218 219 if (sc->clk_init[i].parent_name != NULL) { 220 if (bootverbose) 221 device_printf(sc->dev, "Setting %s as parent for %s\n", 222 sc->clk_init[i].parent_name, 223 sc->clk_init[i].name); 224 error = clknode_set_parent_by_name(clknode, 225 sc->clk_init[i].parent_name); 226 if (error != 0) { 227 device_printf(sc->dev, 228 "Cannot set parent to %s for %s\n", 229 sc->clk_init[i].parent_name, 230 sc->clk_init[i].name); 231 continue; 232 } 233 } 234 if (sc->clk_init[i].default_freq != 0) { 235 if (bootverbose) 236 device_printf(sc->dev, 237 "Setting freq %ju for %s\n", 238 sc->clk_init[i].default_freq, 239 sc->clk_init[i].name); 240 error = clknode_set_freq(clknode, 241 sc->clk_init[i].default_freq, 0 , 0); 242 if (error != 0) { 243 device_printf(sc->dev, 244 "Cannot set frequency for %s to %ju\n", 245 sc->clk_init[i].name, 246 sc->clk_init[i].default_freq); 247 continue; 248 } 249 } 250 if (sc->clk_init[i].enable) { 251 error = clknode_enable(clknode); 252 if (error != 0) { 253 device_printf(sc->dev, 254 "Cannot enable %s\n", 255 sc->clk_init[i].name); 256 continue; 257 } 258 } 259 } 260 } 261 262 int 263 aw_ccung_attach(device_t dev) 264 { 265 struct aw_ccung_softc *sc; 266 int i; 267 268 sc = device_get_softc(dev); 269 sc->dev = dev; 270 271 if (bus_alloc_resources(dev, aw_ccung_spec, &sc->res) != 0) { 272 device_printf(dev, "cannot allocate resources for device\n"); 273 return (ENXIO); 274 } 275 276 mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 277 278 sc->clkdom = clkdom_create(dev); 279 if (sc->clkdom == NULL) 280 panic("Cannot create clkdom\n"); 281 282 for (i = 0; i < sc->nclks; i++) { 283 switch (sc->clks[i].type) { 284 case AW_CLK_UNDEFINED: 285 break; 286 case AW_CLK_MUX: 287 clknode_mux_register(sc->clkdom, sc->clks[i].clk.mux); 288 break; 289 case AW_CLK_DIV: 290 clknode_div_register(sc->clkdom, sc->clks[i].clk.div); 291 break; 292 case AW_CLK_FIXED: 293 clknode_fixed_register(sc->clkdom, 294 sc->clks[i].clk.fixed); 295 break; 296 case AW_CLK_NKMP: 297 aw_clk_nkmp_register(sc->clkdom, sc->clks[i].clk.nkmp); 298 break; 299 case AW_CLK_NM: 300 aw_clk_nm_register(sc->clkdom, sc->clks[i].clk.nm); 301 break; 302 case AW_CLK_M: 303 aw_clk_m_register(sc->clkdom, sc->clks[i].clk.m); 304 break; 305 case AW_CLK_PREDIV_MUX: 306 aw_clk_prediv_mux_register(sc->clkdom, 307 sc->clks[i].clk.prediv_mux); 308 break; 309 case AW_CLK_FRAC: 310 aw_clk_frac_register(sc->clkdom, sc->clks[i].clk.frac); 311 break; 312 case AW_CLK_MIPI: 313 aw_clk_mipi_register(sc->clkdom, sc->clks[i].clk.mipi); 314 break; 315 case AW_CLK_NP: 316 aw_clk_np_register(sc->clkdom, sc->clks[i].clk.np); 317 break; 318 case AW_CLK_NMM: 319 aw_clk_nmm_register(sc->clkdom, sc->clks[i].clk.nmm); 320 break; 321 } 322 } 323 324 if (sc->gates) 325 aw_ccung_register_gates(sc); 326 if (clkdom_finit(sc->clkdom) != 0) 327 panic("cannot finalize clkdom initialization\n"); 328 329 clkdom_xlock(sc->clkdom); 330 aw_ccung_init_clocks(sc); 331 clkdom_unlock(sc->clkdom); 332 333 if (bootverbose) 334 clkdom_dump(sc->clkdom); 335 336 /* If we have resets, register our self as a reset provider */ 337 if (sc->resets) 338 hwreset_register_ofw_provider(dev); 339 340 return (0); 341 } 342 343 static device_method_t aw_ccung_methods[] = { 344 /* clkdev interface */ 345 DEVMETHOD(clkdev_write_4, aw_ccung_write_4), 346 DEVMETHOD(clkdev_read_4, aw_ccung_read_4), 347 DEVMETHOD(clkdev_modify_4, aw_ccung_modify_4), 348 DEVMETHOD(clkdev_device_lock, aw_ccung_device_lock), 349 DEVMETHOD(clkdev_device_unlock, aw_ccung_device_unlock), 350 351 /* Reset interface */ 352 DEVMETHOD(hwreset_assert, aw_ccung_reset_assert), 353 DEVMETHOD(hwreset_is_asserted, aw_ccung_reset_is_asserted), 354 355 DEVMETHOD_END 356 }; 357 358 DEFINE_CLASS_0(aw_ccung, aw_ccung_driver, aw_ccung_methods, 359 sizeof(struct aw_ccung_softc)); 360