1b8cb0864SMarcin Wojtas /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3b8cb0864SMarcin Wojtas * 4b8cb0864SMarcin Wojtas * Copyright (c) 2020 Alstom Group. 5b8cb0864SMarcin Wojtas * Copyright (c) 2020 Semihalf. 6b8cb0864SMarcin Wojtas * 7b8cb0864SMarcin Wojtas * Redistribution and use in source and binary forms, with or without 8b8cb0864SMarcin Wojtas * modification, are permitted provided that the following conditions 9b8cb0864SMarcin Wojtas * are met: 10b8cb0864SMarcin Wojtas * 1. Redistributions of source code must retain the above copyright 11b8cb0864SMarcin Wojtas * notice, this list of conditions and the following disclaimer. 12b8cb0864SMarcin Wojtas * 2. Redistributions in binary form must reproduce the above copyright 13b8cb0864SMarcin Wojtas * notice, this list of conditions and the following disclaimer in the 14b8cb0864SMarcin Wojtas * documentation and/or other materials provided with the distribution. 15b8cb0864SMarcin Wojtas * 16b8cb0864SMarcin Wojtas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17b8cb0864SMarcin Wojtas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18b8cb0864SMarcin Wojtas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19b8cb0864SMarcin Wojtas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20b8cb0864SMarcin Wojtas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21b8cb0864SMarcin Wojtas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22b8cb0864SMarcin Wojtas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23b8cb0864SMarcin Wojtas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24b8cb0864SMarcin Wojtas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25b8cb0864SMarcin Wojtas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26b8cb0864SMarcin Wojtas * SUCH DAMAGE. 27b8cb0864SMarcin Wojtas */ 28b8cb0864SMarcin Wojtas 29b8cb0864SMarcin Wojtas #include <sys/cdefs.h> 30b8cb0864SMarcin Wojtas __FBSDID("$FreeBSD$"); 31b8cb0864SMarcin Wojtas 32b8cb0864SMarcin Wojtas #include <sys/param.h> 33b8cb0864SMarcin Wojtas #include <sys/systm.h> 34b8cb0864SMarcin Wojtas #include <sys/bus.h> 35b8cb0864SMarcin Wojtas #include <sys/endian.h> 36b8cb0864SMarcin Wojtas #include <sys/rman.h> 37b8cb0864SMarcin Wojtas #include <sys/kernel.h> 38b8cb0864SMarcin Wojtas #include <sys/lock.h> 39b8cb0864SMarcin Wojtas #include <sys/module.h> 40b8cb0864SMarcin Wojtas #include <sys/mutex.h> 41b8cb0864SMarcin Wojtas #include <machine/bus.h> 42b8cb0864SMarcin Wojtas 43b8cb0864SMarcin Wojtas #include <dev/fdt/simplebus.h> 44b8cb0864SMarcin Wojtas 45b8cb0864SMarcin Wojtas #include <dev/ofw/ofw_bus.h> 46b8cb0864SMarcin Wojtas #include <dev/ofw/ofw_bus_subr.h> 47b8cb0864SMarcin Wojtas 48b8cb0864SMarcin Wojtas #include <dev/extres/clk/clk_fixed.h> 49b8cb0864SMarcin Wojtas 50b8cb0864SMarcin Wojtas #include <arm64/qoriq/clk/qoriq_clkgen.h> 51b8cb0864SMarcin Wojtas 52b8cb0864SMarcin Wojtas #include "clkdev_if.h" 53b8cb0864SMarcin Wojtas 54b8cb0864SMarcin Wojtas MALLOC_DEFINE(M_QORIQ_CLKGEN, "qoriq_clkgen", "qoriq_clkgen"); 55b8cb0864SMarcin Wojtas 56b8cb0864SMarcin Wojtas static struct resource_spec qoriq_clkgen_spec[] = { 57b8cb0864SMarcin Wojtas { SYS_RES_MEMORY, 0, RF_ACTIVE }, 58b8cb0864SMarcin Wojtas { -1, 0 } 59b8cb0864SMarcin Wojtas }; 60b8cb0864SMarcin Wojtas 61b8cb0864SMarcin Wojtas static const char *qoriq_pll_parents_coreclk[] = { 62b8cb0864SMarcin Wojtas QORIQ_CORECLK_NAME 63b8cb0864SMarcin Wojtas }; 64b8cb0864SMarcin Wojtas 65b8cb0864SMarcin Wojtas static const char *qoriq_pll_parents_sysclk[] = { 66b8cb0864SMarcin Wojtas QORIQ_SYSCLK_NAME 67b8cb0864SMarcin Wojtas }; 68b8cb0864SMarcin Wojtas 69b8cb0864SMarcin Wojtas static int 70b8cb0864SMarcin Wojtas qoriq_clkgen_ofw_mapper(struct clkdom *clkdom, uint32_t ncells, 71b8cb0864SMarcin Wojtas phandle_t *cells, struct clknode **clk) 72b8cb0864SMarcin Wojtas { 73b8cb0864SMarcin Wojtas 74b8cb0864SMarcin Wojtas if (ncells != 2) 75b8cb0864SMarcin Wojtas return (EINVAL); 76b8cb0864SMarcin Wojtas 77b8cb0864SMarcin Wojtas if (cells[0] > 5) 78b8cb0864SMarcin Wojtas return (EINVAL); 79b8cb0864SMarcin Wojtas 80b8cb0864SMarcin Wojtas if (cells[0] == QORIQ_TYPE_SYSCLK || cells[0] == QORIQ_TYPE_CORECLK) 81b8cb0864SMarcin Wojtas if (cells[1] != 0) 82b8cb0864SMarcin Wojtas return (EINVAL); 83b8cb0864SMarcin Wojtas 84b8cb0864SMarcin Wojtas *clk = clknode_find_by_id(clkdom, QORIQ_CLK_ID(cells[0], cells[1])); 85b8cb0864SMarcin Wojtas 86ad86fd01SMichal Meloun if (*clk == NULL) 87b8cb0864SMarcin Wojtas return (EINVAL); 88b8cb0864SMarcin Wojtas 89b8cb0864SMarcin Wojtas return (0); 90b8cb0864SMarcin Wojtas } 91b8cb0864SMarcin Wojtas 92b8cb0864SMarcin Wojtas static int 93b8cb0864SMarcin Wojtas qoriq_clkgen_write_4(device_t dev, bus_addr_t addr, uint32_t val) 94b8cb0864SMarcin Wojtas { 95b8cb0864SMarcin Wojtas struct qoriq_clkgen_softc *sc; 96b8cb0864SMarcin Wojtas 97b8cb0864SMarcin Wojtas sc = device_get_softc(dev); 98b8cb0864SMarcin Wojtas 99b8cb0864SMarcin Wojtas if (sc->flags & QORIQ_LITTLE_ENDIAN) 100b8cb0864SMarcin Wojtas bus_write_4(sc->res, addr, htole32(val)); 101b8cb0864SMarcin Wojtas else 102b8cb0864SMarcin Wojtas bus_write_4(sc->res, addr, htobe32(val)); 103b8cb0864SMarcin Wojtas return (0); 104b8cb0864SMarcin Wojtas } 105b8cb0864SMarcin Wojtas 106b8cb0864SMarcin Wojtas static int 107b8cb0864SMarcin Wojtas qoriq_clkgen_read_4(device_t dev, bus_addr_t addr, uint32_t *val) 108b8cb0864SMarcin Wojtas { 109b8cb0864SMarcin Wojtas struct qoriq_clkgen_softc *sc; 110b8cb0864SMarcin Wojtas 111b8cb0864SMarcin Wojtas sc = device_get_softc(dev); 112b8cb0864SMarcin Wojtas 113b8cb0864SMarcin Wojtas if (sc->flags & QORIQ_LITTLE_ENDIAN) 114b8cb0864SMarcin Wojtas *val = le32toh(bus_read_4(sc->res, addr)); 115b8cb0864SMarcin Wojtas else 116b8cb0864SMarcin Wojtas *val = be32toh(bus_read_4(sc->res, addr)); 117b8cb0864SMarcin Wojtas return (0); 118b8cb0864SMarcin Wojtas } 119b8cb0864SMarcin Wojtas 120b8cb0864SMarcin Wojtas static int 121b8cb0864SMarcin Wojtas qoriq_clkgen_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, 122b8cb0864SMarcin Wojtas uint32_t set) 123b8cb0864SMarcin Wojtas { 124b8cb0864SMarcin Wojtas struct qoriq_clkgen_softc *sc; 125b8cb0864SMarcin Wojtas uint32_t reg; 126b8cb0864SMarcin Wojtas 127b8cb0864SMarcin Wojtas sc = device_get_softc(dev); 128b8cb0864SMarcin Wojtas 129b8cb0864SMarcin Wojtas if (sc->flags & QORIQ_LITTLE_ENDIAN) 130b8cb0864SMarcin Wojtas reg = le32toh(bus_read_4(sc->res, addr)); 131b8cb0864SMarcin Wojtas else 132b8cb0864SMarcin Wojtas reg = be32toh(bus_read_4(sc->res, addr)); 133b8cb0864SMarcin Wojtas 134b8cb0864SMarcin Wojtas reg &= ~clr; 135b8cb0864SMarcin Wojtas reg |= set; 136b8cb0864SMarcin Wojtas 137b8cb0864SMarcin Wojtas if (sc->flags & QORIQ_LITTLE_ENDIAN) 138b8cb0864SMarcin Wojtas bus_write_4(sc->res, addr, htole32(reg)); 139b8cb0864SMarcin Wojtas else 140b8cb0864SMarcin Wojtas bus_write_4(sc->res, addr, htobe32(reg)); 141b8cb0864SMarcin Wojtas 142b8cb0864SMarcin Wojtas return (0); 143b8cb0864SMarcin Wojtas } 144b8cb0864SMarcin Wojtas 145b8cb0864SMarcin Wojtas static void 146b8cb0864SMarcin Wojtas qoriq_clkgen_device_lock(device_t dev) 147b8cb0864SMarcin Wojtas { 148b8cb0864SMarcin Wojtas struct qoriq_clkgen_softc *sc; 149b8cb0864SMarcin Wojtas 150b8cb0864SMarcin Wojtas sc = device_get_softc(dev); 151b8cb0864SMarcin Wojtas mtx_lock(&sc->mtx); 152b8cb0864SMarcin Wojtas } 153b8cb0864SMarcin Wojtas 154b8cb0864SMarcin Wojtas static void 155b8cb0864SMarcin Wojtas qoriq_clkgen_device_unlock(device_t dev) 156b8cb0864SMarcin Wojtas { 157b8cb0864SMarcin Wojtas struct qoriq_clkgen_softc *sc; 158b8cb0864SMarcin Wojtas 159b8cb0864SMarcin Wojtas sc = device_get_softc(dev); 160b8cb0864SMarcin Wojtas mtx_unlock(&sc->mtx); 161b8cb0864SMarcin Wojtas } 162b8cb0864SMarcin Wojtas 163b8cb0864SMarcin Wojtas static device_method_t qoriq_clkgen_methods[] = { 164b8cb0864SMarcin Wojtas DEVMETHOD(clkdev_write_4, qoriq_clkgen_write_4), 165b8cb0864SMarcin Wojtas DEVMETHOD(clkdev_read_4, qoriq_clkgen_read_4), 166b8cb0864SMarcin Wojtas DEVMETHOD(clkdev_modify_4, qoriq_clkgen_modify_4), 167b8cb0864SMarcin Wojtas DEVMETHOD(clkdev_device_lock, qoriq_clkgen_device_lock), 168b8cb0864SMarcin Wojtas DEVMETHOD(clkdev_device_unlock, qoriq_clkgen_device_unlock), 169b8cb0864SMarcin Wojtas 170b8cb0864SMarcin Wojtas DEVMETHOD_END 171b8cb0864SMarcin Wojtas }; 172b8cb0864SMarcin Wojtas 173b8cb0864SMarcin Wojtas DEFINE_CLASS_0(qoriq_clkgen, qoriq_clkgen_driver, qoriq_clkgen_methods, 174b8cb0864SMarcin Wojtas sizeof(struct qoriq_clkgen_softc)); 175b8cb0864SMarcin Wojtas 176b8cb0864SMarcin Wojtas static int 177b8cb0864SMarcin Wojtas qoriq_clkgen_create_sysclk(device_t dev) 178b8cb0864SMarcin Wojtas { 179b8cb0864SMarcin Wojtas struct qoriq_clkgen_softc *sc; 180b8cb0864SMarcin Wojtas struct clk_fixed_def def; 181b8cb0864SMarcin Wojtas const char *clkname; 182b8cb0864SMarcin Wojtas phandle_t node; 183b8cb0864SMarcin Wojtas uint32_t freq; 184b8cb0864SMarcin Wojtas clk_t clock; 185b8cb0864SMarcin Wojtas int rv; 186b8cb0864SMarcin Wojtas 187b8cb0864SMarcin Wojtas sc = device_get_softc(dev); 188b8cb0864SMarcin Wojtas node = ofw_bus_get_node(dev); 189b8cb0864SMarcin Wojtas sc->has_coreclk = false; 190b8cb0864SMarcin Wojtas 191b8cb0864SMarcin Wojtas memset(&def, 0, sizeof(def)); 192b8cb0864SMarcin Wojtas 193b8cb0864SMarcin Wojtas rv = OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)); 194b8cb0864SMarcin Wojtas if (rv > 0) { 195b8cb0864SMarcin Wojtas def.clkdef.name = QORIQ_SYSCLK_NAME; 196b8cb0864SMarcin Wojtas def.clkdef.id = QORIQ_CLK_ID(QORIQ_TYPE_SYSCLK, 0); 197b8cb0864SMarcin Wojtas def.freq = freq; 198b8cb0864SMarcin Wojtas 199b8cb0864SMarcin Wojtas rv = clknode_fixed_register(sc->clkdom, &def); 200b8cb0864SMarcin Wojtas return (rv); 201b8cb0864SMarcin Wojtas } else { 202b8cb0864SMarcin Wojtas /* 203b8cb0864SMarcin Wojtas * As both sysclk and coreclk need to be accessible from 204b8cb0864SMarcin Wojtas * device tree, create internal 1:1 divider nodes. 205b8cb0864SMarcin Wojtas */ 206b8cb0864SMarcin Wojtas def.clkdef.parent_cnt = 1; 207b8cb0864SMarcin Wojtas def.freq = 0; 208b8cb0864SMarcin Wojtas def.mult = 1; 209b8cb0864SMarcin Wojtas def.div = 1; 210b8cb0864SMarcin Wojtas 211b8cb0864SMarcin Wojtas rv = clk_get_by_ofw_name(dev, node, "coreclk", &clock); 212b8cb0864SMarcin Wojtas if (rv == 0) { 213b8cb0864SMarcin Wojtas def.clkdef.name = QORIQ_CORECLK_NAME; 214b8cb0864SMarcin Wojtas clkname = clk_get_name(clock); 215b8cb0864SMarcin Wojtas def.clkdef.parent_names = &clkname; 216b8cb0864SMarcin Wojtas def.clkdef.id = QORIQ_CLK_ID(QORIQ_TYPE_CORECLK, 0); 217b8cb0864SMarcin Wojtas 218b8cb0864SMarcin Wojtas rv = clknode_fixed_register(sc->clkdom, &def); 219b8cb0864SMarcin Wojtas if (rv) 220b8cb0864SMarcin Wojtas return (rv); 221b8cb0864SMarcin Wojtas 222b8cb0864SMarcin Wojtas sc->has_coreclk = true; 223b8cb0864SMarcin Wojtas } 224b8cb0864SMarcin Wojtas 225b8cb0864SMarcin Wojtas rv = clk_get_by_ofw_name(dev, node, "sysclk", &clock); 226b8cb0864SMarcin Wojtas if (rv != 0) { 227b8cb0864SMarcin Wojtas rv = clk_get_by_ofw_index(dev, node, 0, &clock); 228b8cb0864SMarcin Wojtas if (rv != 0) 229b8cb0864SMarcin Wojtas return (rv); 230b8cb0864SMarcin Wojtas } 231b8cb0864SMarcin Wojtas 232b8cb0864SMarcin Wojtas clkname = clk_get_name(clock); 233b8cb0864SMarcin Wojtas def.clkdef.name = QORIQ_SYSCLK_NAME; 234b8cb0864SMarcin Wojtas def.clkdef.id = QORIQ_CLK_ID(QORIQ_TYPE_SYSCLK, 0); 235b8cb0864SMarcin Wojtas def.clkdef.parent_names = &clkname; 236b8cb0864SMarcin Wojtas 237b8cb0864SMarcin Wojtas rv = clknode_fixed_register(sc->clkdom, &def); 238b8cb0864SMarcin Wojtas return (rv); 239b8cb0864SMarcin Wojtas } 240b8cb0864SMarcin Wojtas } 241b8cb0864SMarcin Wojtas 242b8cb0864SMarcin Wojtas int 243b8cb0864SMarcin Wojtas qoriq_clkgen_attach(device_t dev) 244b8cb0864SMarcin Wojtas { 245b8cb0864SMarcin Wojtas struct qoriq_clkgen_softc *sc; 246b8cb0864SMarcin Wojtas int i, error; 247b8cb0864SMarcin Wojtas 248b8cb0864SMarcin Wojtas sc = device_get_softc(dev); 249b8cb0864SMarcin Wojtas sc->dev = dev; 250b8cb0864SMarcin Wojtas 251b8cb0864SMarcin Wojtas if (bus_alloc_resources(dev, qoriq_clkgen_spec, &sc->res) != 0) { 252b8cb0864SMarcin Wojtas device_printf(dev, "Cannot allocate resources.\n"); 253b8cb0864SMarcin Wojtas return (ENXIO); 254b8cb0864SMarcin Wojtas } 255b8cb0864SMarcin Wojtas 256b8cb0864SMarcin Wojtas mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 257b8cb0864SMarcin Wojtas 258b8cb0864SMarcin Wojtas sc->clkdom = clkdom_create(dev); 259b8cb0864SMarcin Wojtas if (sc->clkdom == NULL) 260b8cb0864SMarcin Wojtas panic("Cannot create clock domain.\n"); 261b8cb0864SMarcin Wojtas 262b8cb0864SMarcin Wojtas error = qoriq_clkgen_create_sysclk(dev); 263b8cb0864SMarcin Wojtas if (error != 0) { 264b8cb0864SMarcin Wojtas device_printf(dev, "Cannot create sysclk.\n"); 265b8cb0864SMarcin Wojtas return (error); 266b8cb0864SMarcin Wojtas } 267b8cb0864SMarcin Wojtas 268b8cb0864SMarcin Wojtas sc->pltfrm_pll_def->clkdef.parent_names = qoriq_pll_parents_sysclk; 269b8cb0864SMarcin Wojtas sc->pltfrm_pll_def->clkdef.parent_cnt = 1; 270b8cb0864SMarcin Wojtas error = qoriq_clk_pll_register(sc->clkdom, sc->pltfrm_pll_def); 271b8cb0864SMarcin Wojtas if (error != 0) { 272b8cb0864SMarcin Wojtas device_printf(dev, "Cannot create platform PLL.\n"); 273b8cb0864SMarcin Wojtas return (error); 274b8cb0864SMarcin Wojtas } 275b8cb0864SMarcin Wojtas 276b8cb0864SMarcin Wojtas for (i = 0; i < sc->cga_pll_num; i++) { 277b8cb0864SMarcin Wojtas if (sc->has_coreclk) 278b8cb0864SMarcin Wojtas sc->cga_pll[i]->clkdef.parent_names = qoriq_pll_parents_coreclk; 279b8cb0864SMarcin Wojtas else 280b8cb0864SMarcin Wojtas sc->cga_pll[i]->clkdef.parent_names = qoriq_pll_parents_sysclk; 281b8cb0864SMarcin Wojtas sc->cga_pll[i]->clkdef.parent_cnt = 1; 282b8cb0864SMarcin Wojtas 283b8cb0864SMarcin Wojtas error = qoriq_clk_pll_register(sc->clkdom, sc->cga_pll[i]); 284b8cb0864SMarcin Wojtas if (error != 0) { 285b8cb0864SMarcin Wojtas device_printf(dev, "Cannot create CGA PLLs\n."); 286b8cb0864SMarcin Wojtas return (error); 287b8cb0864SMarcin Wojtas } 288b8cb0864SMarcin Wojtas } 289b8cb0864SMarcin Wojtas 290b8cb0864SMarcin Wojtas /* 291b8cb0864SMarcin Wojtas * Both CMUX and HWACCEL multiplexer nodes can be represented 292b8cb0864SMarcin Wojtas * by using built in clk_mux nodes. 293b8cb0864SMarcin Wojtas */ 294b8cb0864SMarcin Wojtas for (i = 0; i < sc->mux_num; i++) { 295b8cb0864SMarcin Wojtas error = clknode_mux_register(sc->clkdom, sc->mux[i]); 296b8cb0864SMarcin Wojtas if (error != 0) { 297b8cb0864SMarcin Wojtas device_printf(dev, "Cannot create MUX nodes.\n"); 298b8cb0864SMarcin Wojtas return (error); 299b8cb0864SMarcin Wojtas } 300b8cb0864SMarcin Wojtas } 301b8cb0864SMarcin Wojtas 302b8cb0864SMarcin Wojtas if (sc->init_func != NULL) { 303b8cb0864SMarcin Wojtas error = sc->init_func(dev); 304b8cb0864SMarcin Wojtas if (error) { 305b8cb0864SMarcin Wojtas device_printf(dev, "Clock init function failed.\n"); 306b8cb0864SMarcin Wojtas return (error); 307b8cb0864SMarcin Wojtas } 308b8cb0864SMarcin Wojtas } 309b8cb0864SMarcin Wojtas 310b8cb0864SMarcin Wojtas clkdom_set_ofw_mapper(sc->clkdom, qoriq_clkgen_ofw_mapper); 311b8cb0864SMarcin Wojtas 312b8cb0864SMarcin Wojtas if (clkdom_finit(sc->clkdom) != 0) 313b8cb0864SMarcin Wojtas panic("Cannot finalize clock domain initialization.\n"); 314b8cb0864SMarcin Wojtas 315b8cb0864SMarcin Wojtas if (bootverbose) 316b8cb0864SMarcin Wojtas clkdom_dump(sc->clkdom); 317b8cb0864SMarcin Wojtas 318b8cb0864SMarcin Wojtas return (0); 319b8cb0864SMarcin Wojtas } 320