2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 * RockChip Clock and Reset Unit
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/systm.h>
42 #include <sys/kernel.h>
44 #include <sys/module.h>
45 #include <sys/mutex.h>
46 #include <machine/bus.h>
48 #include <dev/fdt/simplebus.h>
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
53 #include <dev/extres/clk/clk.h>
54 #include <dev/extres/clk/clk_gate.h>
55 #include <dev/extres/hwreset/hwreset.h>
57 #include <arm64/rockchip/clk/rk_clk_composite.h>
58 #include <arm64/rockchip/clk/rk_clk_gate.h>
59 #include <arm64/rockchip/clk/rk_clk_mux.h>
60 #include <arm64/rockchip/clk/rk_clk_pll.h>
61 #include <arm64/rockchip/clk/rk_cru.h>
63 #include "clkdev_if.h"
64 #include "hwreset_if.h"
66 static struct resource_spec rk_cru_spec[] = {
67 { SYS_RES_MEMORY, 0, RF_ACTIVE },
71 #define CCU_READ4(sc, reg) bus_read_4((sc)->res, (reg))
72 #define CCU_WRITE4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
74 void rk3328_cru_register_clocks(struct rk_cru_softc *sc);
77 rk_cru_write_4(device_t dev, bus_addr_t addr, uint32_t val)
79 struct rk_cru_softc *sc;
81 sc = device_get_softc(dev);
82 CCU_WRITE4(sc, addr, val);
87 rk_cru_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
89 struct rk_cru_softc *sc;
91 sc = device_get_softc(dev);
93 *val = CCU_READ4(sc, addr);
98 rk_cru_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
100 struct rk_cru_softc *sc;
103 sc = device_get_softc(dev);
105 reg = CCU_READ4(sc, addr);
108 CCU_WRITE4(sc, addr, reg);
114 rk_cru_reset_assert(device_t dev, intptr_t id, bool reset)
116 struct rk_cru_softc *sc;
119 sc = device_get_softc(dev);
121 if (id >= sc->nresets || sc->resets[id].offset == 0)
125 val = CCU_READ4(sc, sc->resets[id].offset);
127 val &= ~(1 << sc->resets[id].shift);
129 val |= 1 << sc->resets[id].shift;
130 CCU_WRITE4(sc, sc->resets[id].offset, val);
131 mtx_unlock(&sc->mtx);
137 rk_cru_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
139 struct rk_cru_softc *sc;
142 sc = device_get_softc(dev);
144 if (id >= sc->nresets || sc->resets[id].offset == 0)
148 val = CCU_READ4(sc, sc->resets[id].offset);
149 *reset = (val & (1 << sc->resets[id].shift)) != 0 ? false : true;
150 mtx_unlock(&sc->mtx);
156 rk_cru_device_lock(device_t dev)
158 struct rk_cru_softc *sc;
160 sc = device_get_softc(dev);
165 rk_cru_device_unlock(device_t dev)
167 struct rk_cru_softc *sc;
169 sc = device_get_softc(dev);
170 mtx_unlock(&sc->mtx);
174 rk_cru_register_gates(struct rk_cru_softc *sc)
176 struct rk_clk_gate_def def;
179 for (i = 0; i < sc->ngates; i++) {
180 if (sc->gates[i].name == NULL)
182 memset(&def, 0, sizeof(def));
183 def.clkdef.id = sc->gates[i].id;
184 def.clkdef.name = sc->gates[i].name;
185 def.clkdef.parent_names = &sc->gates[i].parent_name;
186 def.clkdef.parent_cnt = 1;
187 def.offset = sc->gates[i].offset;
188 def.shift = sc->gates[i].shift;
192 rk_clk_gate_register(sc->clkdom, &def);
199 rk_cru_attach(device_t dev)
201 struct rk_cru_softc *sc;
205 sc = device_get_softc(dev);
208 node = ofw_bus_get_node(dev);
210 if (bus_alloc_resources(dev, rk_cru_spec, &sc->res) != 0) {
211 device_printf(dev, "cannot allocate resources for device\n");
215 mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
217 sc->clkdom = clkdom_create(dev);
218 if (sc->clkdom == NULL)
219 panic("Cannot create clkdom\n");
221 for (i = 0; i < sc->nclks; i++) {
222 switch (sc->clks[i].type) {
223 case RK_CLK_UNDEFINED:
226 rk3328_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll);
229 rk3399_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll);
231 case RK_CLK_COMPOSITE:
232 rk_clk_composite_register(sc->clkdom,
233 sc->clks[i].clk.composite);
236 rk_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux);
239 rk_clk_armclk_register(sc->clkdom, sc->clks[i].clk.armclk);
242 device_printf(dev, "Unknown clock type\n");
248 rk_cru_register_gates(sc);
250 if (clkdom_finit(sc->clkdom) != 0)
251 panic("cannot finalize clkdom initialization\n");
254 clkdom_dump(sc->clkdom);
256 clk_set_assigned(dev, node);
258 /* If we have resets, register our self as a reset provider */
260 hwreset_register_ofw_provider(dev);
265 static device_method_t rk_cru_methods[] = {
266 /* clkdev interface */
267 DEVMETHOD(clkdev_write_4, rk_cru_write_4),
268 DEVMETHOD(clkdev_read_4, rk_cru_read_4),
269 DEVMETHOD(clkdev_modify_4, rk_cru_modify_4),
270 DEVMETHOD(clkdev_device_lock, rk_cru_device_lock),
271 DEVMETHOD(clkdev_device_unlock, rk_cru_device_unlock),
273 /* Reset interface */
274 DEVMETHOD(hwreset_assert, rk_cru_reset_assert),
275 DEVMETHOD(hwreset_is_asserted, rk_cru_reset_is_asserted),
280 DEFINE_CLASS_0(rk_cru, rk_cru_driver, rk_cru_methods,
281 sizeof(struct rk_cru_softc));