1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Semihalf. 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, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/bus.h> 30 #include <sys/rman.h> 31 #include <sys/kernel.h> 32 #include <sys/lock.h> 33 #include <sys/module.h> 34 #include <sys/mutex.h> 35 36 #include <machine/bus.h> 37 38 #include <dev/fdt/simplebus.h> 39 #include <dev/ofw/ofw_bus.h> 40 #include <dev/ofw/ofw_bus_subr.h> 41 #include <dev/clk/clk.h> 42 #include <dev/clk/clk_gate.h> 43 44 #include "clkdev_if.h" 45 46 #define ARMADA38X_GATECLK_MAXREG 0 47 48 static struct resource_spec armada38x_gateclk_specs[] = { 49 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 50 { -1, 0 } 51 }; 52 53 #define RD4(_sc, addr) bus_read_4(_sc->res, addr) 54 #define WR4(_sc, addr, val) bus_write_4(_sc->res, addr, val) 55 56 struct armada38x_gateclk_softc 57 { 58 struct resource *res; 59 struct clkdom *clkdom; 60 struct mtx mtx; 61 const char* parent; 62 }; 63 64 static struct clk_gate_def gateclk_nodes[] = 65 { 66 { 67 .clkdef = { 68 .name = "gateclk-audio", 69 .id = 0, 70 .parent_cnt = 1, 71 .flags = 0, 72 }, 73 .shift = 0, 74 }, 75 { 76 .clkdef = { 77 .name = "gateclk-eth2", 78 .id = 2, 79 .parent_cnt = 1, 80 .flags = 0, 81 }, 82 .shift = 2, 83 }, 84 { 85 .clkdef = { 86 .name = "gateclk-eth1", 87 .id = 3, 88 .parent_cnt = 1, 89 .flags = 0, 90 }, 91 .shift = 3, 92 }, 93 { 94 .clkdef = { 95 .name = "gateclk-eth0", 96 .id = 4, 97 .parent_cnt = 1, 98 .flags = 0, 99 }, 100 .shift = 4, 101 }, 102 { 103 .clkdef = { 104 .name = "gateclk-mdio", 105 .id = 4, 106 .parent_cnt = 1, 107 .flags = 0, 108 }, 109 .shift = 4, 110 }, 111 { 112 .clkdef = { 113 .name = "gateclk-usb3h0", 114 .id = 9, 115 .parent_cnt = 1, 116 .flags = 0, 117 }, 118 .shift = 9, 119 }, 120 { 121 .clkdef = { 122 .name = "gateclk-usb3h1", 123 .id = 10, 124 .parent_cnt = 1, 125 .flags = 0, 126 }, 127 .shift = 10, 128 }, 129 { 130 .clkdef = { 131 .name = "gateclk-bm", 132 .id = 13, 133 .parent_cnt = 1, 134 .flags = 0, 135 }, 136 .shift = 13, 137 }, 138 { 139 .clkdef = { 140 .name = "gateclk-crypto0z", 141 .id = 14, 142 .parent_cnt = 1, 143 .flags = 0, 144 }, 145 .shift = 14, 146 }, 147 { 148 .clkdef = { 149 .name = "gateclk-sata0", 150 .id = 15, 151 .parent_cnt = 1, 152 .flags = 0, 153 }, 154 .shift = 15, 155 }, 156 { 157 .clkdef = { 158 .name = "gateclk-crypto1z", 159 .id = 16, 160 .parent_cnt = 1, 161 .flags = 0, 162 }, 163 .shift = 16, 164 }, 165 { 166 .clkdef = { 167 .name = "gateclk-sdio", 168 .id = 17, 169 .parent_cnt = 1, 170 .flags = 0, 171 }, 172 .shift = 17, 173 }, 174 { 175 .clkdef = { 176 .name = "gateclk-usb2", 177 .id = 18, 178 .parent_cnt = 1, 179 .flags = 0, 180 }, 181 .shift = 18, 182 }, 183 { 184 .clkdef = { 185 .name = "gateclk-crypto1", 186 .id = 21, 187 .parent_cnt = 1, 188 .flags = 0, 189 }, 190 .shift = 21, 191 }, 192 { 193 .clkdef = { 194 .name = "gateclk-xor0", 195 .id = 22, 196 .parent_cnt = 1, 197 .flags = 0, 198 }, 199 .shift = 22, 200 }, 201 { 202 .clkdef = { 203 .name = "gateclk-crypto0", 204 .id = 23, 205 .parent_cnt = 1, 206 .flags = 0, 207 }, 208 .shift = 23, 209 }, 210 { 211 .clkdef = { 212 .name = "gateclk-xor1", 213 .id = 28, 214 .parent_cnt = 1, 215 .flags = 0, 216 }, 217 .shift = 28, 218 }, 219 { 220 .clkdef = { 221 .name = "gateclk-sata1", 222 .id = 30, 223 .parent_cnt = 1, 224 .flags = 0, 225 }, 226 .shift = 30, 227 } 228 }; 229 230 static int armada38x_gateclk_probe(device_t dev); 231 static int armada38x_gateclk_attach(device_t dev); 232 233 static int 234 armada38x_gateclk_write_4(device_t dev, bus_addr_t addr, uint32_t val) 235 { 236 struct armada38x_gateclk_softc *sc = device_get_softc(dev); 237 238 if (addr > ARMADA38X_GATECLK_MAXREG) 239 return (EINVAL); 240 241 WR4(sc, addr, val); 242 return (0); 243 } 244 245 static int 246 armada38x_gateclk_read_4(device_t dev, bus_addr_t addr, uint32_t *val) 247 { 248 struct armada38x_gateclk_softc *sc = device_get_softc(dev); 249 250 if (addr > ARMADA38X_GATECLK_MAXREG) 251 return (EINVAL); 252 253 *val = RD4(sc, addr); 254 return (0); 255 } 256 257 static int 258 armada38x_gateclk_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, 259 uint32_t set) 260 { 261 struct armada38x_gateclk_softc *sc = device_get_softc(dev); 262 uint32_t reg; 263 264 if (addr > ARMADA38X_GATECLK_MAXREG) 265 return (EINVAL); 266 267 reg = RD4(sc, addr); 268 reg &= ~clr; 269 reg |= set; 270 WR4(sc, addr, reg); 271 272 return (0); 273 } 274 275 static void 276 armada38x_gateclk_device_lock(device_t dev) 277 { 278 struct armada38x_gateclk_softc *sc = device_get_softc(dev); 279 280 mtx_lock(&sc->mtx); 281 } 282 283 static void 284 armada38x_gateclk_device_unlock(device_t dev) 285 { 286 struct armada38x_gateclk_softc *sc = device_get_softc(dev); 287 288 mtx_unlock(&sc->mtx); 289 } 290 291 static device_method_t armada38x_gateclk_methods[] = { 292 DEVMETHOD(device_probe, armada38x_gateclk_probe), 293 DEVMETHOD(device_attach, armada38x_gateclk_attach), 294 295 /* clkdev interface */ 296 DEVMETHOD(clkdev_write_4, armada38x_gateclk_write_4), 297 DEVMETHOD(clkdev_read_4, armada38x_gateclk_read_4), 298 DEVMETHOD(clkdev_modify_4, armada38x_gateclk_modify_4), 299 DEVMETHOD(clkdev_device_lock, armada38x_gateclk_device_lock), 300 DEVMETHOD(clkdev_device_unlock, armada38x_gateclk_device_unlock), 301 302 DEVMETHOD_END 303 }; 304 305 static driver_t armada38x_gateclk_driver = { 306 "armada38x_gateclk", 307 armada38x_gateclk_methods, 308 sizeof(struct armada38x_gateclk_softc), 309 }; 310 311 EARLY_DRIVER_MODULE(armada38x_gateclk, simplebus, armada38x_gateclk_driver, 0, 0, 312 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 1); 313 314 static int 315 armada38x_gateclk_probe(device_t dev) 316 { 317 318 if (!ofw_bus_status_okay(dev)) 319 return (ENXIO); 320 321 if(!ofw_bus_is_compatible(dev, "marvell,armada-380-gating-clock")) 322 return (ENXIO); 323 324 device_set_desc(dev, "ARMADA38X gateclk"); 325 326 return (BUS_PROBE_DEFAULT); 327 } 328 329 static int 330 armada38x_gateclk_attach(device_t dev) 331 { 332 struct armada38x_gateclk_softc *sc; 333 struct clk_gate_def *defp; 334 phandle_t node; 335 int i, error; 336 clk_t clock; 337 338 sc = device_get_softc(dev); 339 node = ofw_bus_get_node(dev); 340 341 if (bus_alloc_resources(dev, armada38x_gateclk_specs, &sc->res) != 0) { 342 device_printf(dev, "Cannot allocate resources.\n"); 343 return (ENXIO); 344 } 345 346 mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 347 348 sc->clkdom = clkdom_create(dev); 349 if (sc->clkdom == NULL) { 350 device_printf(dev, "Cannot create clock domain.\n"); 351 return (ENXIO); 352 } 353 354 error = clk_get_by_ofw_index(dev, node, 0, &clock); 355 if (error > 0) 356 return (error); 357 358 sc->parent = clk_get_name(clock); 359 360 for (i = 0; i < nitems(gateclk_nodes); ++i) { 361 /* Fill clk_gate fields. */ 362 defp = &gateclk_nodes[i]; 363 defp->clkdef.parent_names = &sc->parent; 364 defp->offset = 0; 365 defp->mask = 0x1; 366 defp->on_value = 1; 367 defp->off_value = 0; 368 369 error = clknode_gate_register(sc->clkdom, defp); 370 if (error != 0) { 371 device_printf(dev, "Cannot create gate nodes\n"); 372 return (error); 373 } 374 } 375 376 if (clkdom_finit(sc->clkdom) != 0) 377 panic("Cannot finalize clock domain initialization.\n"); 378 379 return (0); 380 } 381