]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/rockchip/clk/rk_cru.c
MFV r349454:
[FreeBSD/FreeBSD.git] / sys / arm64 / rockchip / clk / rk_cru.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
5  * All rights reserved.
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 ``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
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 /*
32  * RockChip Clock and Reset Unit
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/rman.h>
42 #include <sys/kernel.h>
43 #include <sys/lock.h>
44 #include <sys/module.h>
45 #include <sys/mutex.h>
46 #include <machine/bus.h>
47
48 #include <dev/fdt/simplebus.h>
49
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52
53 #include <dev/extres/clk/clk.h>
54 #include <dev/extres/clk/clk_gate.h>
55 #include <dev/extres/hwreset/hwreset.h>
56
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>
62
63 #include "clkdev_if.h"
64 #include "hwreset_if.h"
65
66 static struct resource_spec rk_cru_spec[] = {
67         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
68         { -1, 0 }
69 };
70
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))
73
74 void    rk3328_cru_register_clocks(struct rk_cru_softc *sc);
75
76 static int
77 rk_cru_write_4(device_t dev, bus_addr_t addr, uint32_t val)
78 {
79         struct rk_cru_softc *sc;
80
81         sc = device_get_softc(dev);
82         CCU_WRITE4(sc, addr, val);
83         return (0);
84 }
85
86 static int
87 rk_cru_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
88 {
89         struct rk_cru_softc *sc;
90
91         sc = device_get_softc(dev);
92
93         *val = CCU_READ4(sc, addr);
94         return (0);
95 }
96
97 static int
98 rk_cru_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
99 {
100         struct rk_cru_softc *sc;
101         uint32_t reg;
102
103         sc = device_get_softc(dev);
104
105         reg = CCU_READ4(sc, addr);
106         reg &= ~clr;
107         reg |= set;
108         CCU_WRITE4(sc, addr, reg);
109
110         return (0);
111 }
112
113 static int
114 rk_cru_reset_assert(device_t dev, intptr_t id, bool reset)
115 {
116         struct rk_cru_softc *sc;
117         uint32_t val;
118
119         sc = device_get_softc(dev);
120
121         if (id >= sc->nresets || sc->resets[id].offset == 0)
122                 return (0);
123
124         mtx_lock(&sc->mtx);
125         val = CCU_READ4(sc, sc->resets[id].offset);
126         if (reset)
127                 val &= ~(1 << sc->resets[id].shift);
128         else
129                 val |= 1 << sc->resets[id].shift;
130         CCU_WRITE4(sc, sc->resets[id].offset, val);
131         mtx_unlock(&sc->mtx);
132
133         return (0);
134 }
135
136 static int
137 rk_cru_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
138 {
139         struct rk_cru_softc *sc;
140         uint32_t val;
141
142         sc = device_get_softc(dev);
143
144         if (id >= sc->nresets || sc->resets[id].offset == 0)
145                 return (0);
146
147         mtx_lock(&sc->mtx);
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);
151
152         return (0);
153 }
154
155 static void
156 rk_cru_device_lock(device_t dev)
157 {
158         struct rk_cru_softc *sc;
159
160         sc = device_get_softc(dev);
161         mtx_lock(&sc->mtx);
162 }
163
164 static void
165 rk_cru_device_unlock(device_t dev)
166 {
167         struct rk_cru_softc *sc;
168
169         sc = device_get_softc(dev);
170         mtx_unlock(&sc->mtx);
171 }
172
173 static int
174 rk_cru_register_gates(struct rk_cru_softc *sc)
175 {
176         struct rk_clk_gate_def def;
177         int i;
178
179         for (i = 0; i < sc->ngates; i++) {
180                 if (sc->gates[i].name == NULL)
181                         continue;
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;
189                 def.mask = 1;
190                 def.on_value = 0;
191                 def.off_value = 1;
192                 rk_clk_gate_register(sc->clkdom, &def);
193         }
194
195         return (0);
196 }
197
198 int
199 rk_cru_attach(device_t dev)
200 {
201         struct rk_cru_softc *sc;
202         phandle_t node;
203         int     i;
204
205         sc = device_get_softc(dev);
206         sc->dev = dev;
207
208         node = ofw_bus_get_node(dev);
209
210         if (bus_alloc_resources(dev, rk_cru_spec, &sc->res) != 0) {
211                 device_printf(dev, "cannot allocate resources for device\n");
212                 return (ENXIO);
213         }
214
215         mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
216
217         sc->clkdom = clkdom_create(dev);
218         if (sc->clkdom == NULL)
219                 panic("Cannot create clkdom\n");
220
221         for (i = 0; i < sc->nclks; i++) {
222                 switch (sc->clks[i].type) {
223                 case RK_CLK_UNDEFINED:
224                         break;
225                 case RK3328_CLK_PLL:
226                         rk3328_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll);
227                         break;
228                 case RK3399_CLK_PLL:
229                         rk3399_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll);
230                         break;
231                 case RK_CLK_COMPOSITE:
232                         rk_clk_composite_register(sc->clkdom,
233                             sc->clks[i].clk.composite);
234                         break;
235                 case RK_CLK_MUX:
236                         rk_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux);
237                         break;
238                 case RK_CLK_ARMCLK:
239                         rk_clk_armclk_register(sc->clkdom, sc->clks[i].clk.armclk);
240                         break;
241                 default:
242                         device_printf(dev, "Unknown clock type\n");
243                         return (ENXIO);
244                         break;
245                 }
246         }
247         if (sc->gates)
248                 rk_cru_register_gates(sc);
249
250         if (clkdom_finit(sc->clkdom) != 0)
251                 panic("cannot finalize clkdom initialization\n");
252
253         if (bootverbose)
254                 clkdom_dump(sc->clkdom);
255
256         clk_set_assigned(dev, node);
257
258         /* If we have resets, register our self as a reset provider */
259         if (sc->resets)
260                 hwreset_register_ofw_provider(dev);
261
262         return (0);
263 }
264
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),
272
273         /* Reset interface */
274         DEVMETHOD(hwreset_assert,       rk_cru_reset_assert),
275         DEVMETHOD(hwreset_is_asserted,  rk_cru_reset_is_asserted),
276
277         DEVMETHOD_END
278 };
279
280 DEFINE_CLASS_0(rk_cru, rk_cru_driver, rk_cru_methods,
281     sizeof(struct rk_cru_softc));