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