1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5 * Copyright (c) 2024 The FreeBSD Foundation
6 *
7 * Portions of this software were developed by Tom Jones <thj@freebsd.org>
8 * under sponsorship from the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33
34 /*
35 * Clock Control Module driver for Freescale i.MX 8M SoC family.
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
43 #include <sys/bus.h>
44 #include <sys/rman.h>
45
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48
49 #include <machine/bus.h>
50
51 #include <arm64/freescale/imx/imx_ccm.h>
52 #include <arm64/freescale/imx/clk/imx_clk_gate.h>
53 #include <arm64/freescale/imx/clk/imx_clk_mux.h>
54 #include <arm64/freescale/imx/clk/imx_clk_composite.h>
55 #include <arm64/freescale/imx/clk/imx_clk_sscg_pll.h>
56 #include <arm64/freescale/imx/clk/imx_clk_frac_pll.h>
57
58 #include "clkdev_if.h"
59
60 static inline uint32_t
CCU_READ4(struct imx_ccm_softc * sc,bus_size_t off)61 CCU_READ4(struct imx_ccm_softc *sc, bus_size_t off)
62 {
63
64 return (bus_read_4(sc->mem_res, off));
65 }
66
67 static inline void
CCU_WRITE4(struct imx_ccm_softc * sc,bus_size_t off,uint32_t val)68 CCU_WRITE4(struct imx_ccm_softc *sc, bus_size_t off, uint32_t val)
69 {
70
71 bus_write_4(sc->mem_res, off, val);
72 }
73
74 int
imx_ccm_detach(device_t dev)75 imx_ccm_detach(device_t dev)
76 {
77 struct imx_ccm_softc *sc;
78
79 sc = device_get_softc(dev);
80
81 if (sc->mem_res != NULL)
82 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
83
84 return (0);
85 }
86
87 int
imx_ccm_attach(device_t dev)88 imx_ccm_attach(device_t dev)
89 {
90 struct imx_ccm_softc *sc;
91 int err, rid;
92 phandle_t node;
93 int i;
94
95 sc = device_get_softc(dev);
96 sc->dev = dev;
97 err = 0;
98
99 /* Allocate bus_space resources. */
100 rid = 0;
101 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
102 RF_ACTIVE);
103 if (sc->mem_res == NULL) {
104 device_printf(dev, "Cannot allocate memory resources\n");
105 err = ENXIO;
106 goto out;
107 }
108
109 mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
110
111 sc->clkdom = clkdom_create(dev);
112 if (sc->clkdom == NULL)
113 panic("Cannot create clkdom\n");
114
115 for (i = 0; i < sc->nclks; i++) {
116 switch (sc->clks[i].type) {
117 case IMX_CLK_UNDEFINED:
118 break;
119 case IMX_CLK_LINK:
120 clknode_link_register(sc->clkdom,
121 sc->clks[i].clk.link);
122 break;
123 case IMX_CLK_FIXED:
124 clknode_fixed_register(sc->clkdom,
125 sc->clks[i].clk.fixed);
126 break;
127 case IMX_CLK_MUX:
128 imx_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux);
129 break;
130 case IMX_CLK_GATE:
131 imx_clk_gate_register(sc->clkdom, sc->clks[i].clk.gate);
132 break;
133 case IMX_CLK_COMPOSITE:
134 imx_clk_composite_register(sc->clkdom, sc->clks[i].clk.composite);
135 break;
136 case IMX_CLK_SSCG_PLL:
137 imx_clk_sscg_pll_register(sc->clkdom, sc->clks[i].clk.sscg_pll);
138 break;
139 case IMX_CLK_FRAC_PLL:
140 imx_clk_frac_pll_register(sc->clkdom, sc->clks[i].clk.frac_pll);
141 break;
142 case IMX_CLK_DIV:
143 clknode_div_register(sc->clkdom, sc->clks[i].clk.div);
144 break;
145 default:
146 device_printf(dev, "Unknown clock type %d\n", sc->clks[i].type);
147 return (ENXIO);
148 }
149 }
150
151 if (clkdom_finit(sc->clkdom) != 0)
152 panic("cannot finalize clkdom initialization\n");
153
154 if (bootverbose)
155 clkdom_dump(sc->clkdom);
156
157 node = ofw_bus_get_node(dev);
158 clk_set_assigned(dev, node);
159
160 err = 0;
161
162 out:
163
164 if (err != 0)
165 imx_ccm_detach(dev);
166
167 return (err);
168 }
169
170 static int
imx_ccm_write_4(device_t dev,bus_addr_t addr,uint32_t val)171 imx_ccm_write_4(device_t dev, bus_addr_t addr, uint32_t val)
172 {
173 struct imx_ccm_softc *sc;
174
175 sc = device_get_softc(dev);
176 CCU_WRITE4(sc, addr, val);
177 return (0);
178 }
179
180 static int
imx_ccm_read_4(device_t dev,bus_addr_t addr,uint32_t * val)181 imx_ccm_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
182 {
183 struct imx_ccm_softc *sc;
184
185 sc = device_get_softc(dev);
186
187 *val = CCU_READ4(sc, addr);
188 return (0);
189 }
190
191 static int
imx_ccm_modify_4(device_t dev,bus_addr_t addr,uint32_t clr,uint32_t set)192 imx_ccm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
193 {
194 struct imx_ccm_softc *sc;
195 uint32_t reg;
196
197 sc = device_get_softc(dev);
198
199 reg = CCU_READ4(sc, addr);
200 reg &= ~clr;
201 reg |= set;
202 CCU_WRITE4(sc, addr, reg);
203
204 return (0);
205 }
206
207 static void
imx_ccm_device_lock(device_t dev)208 imx_ccm_device_lock(device_t dev)
209 {
210 struct imx_ccm_softc *sc;
211
212 sc = device_get_softc(dev);
213 mtx_lock(&sc->mtx);
214 }
215
216 static void
imx_ccm_device_unlock(device_t dev)217 imx_ccm_device_unlock(device_t dev)
218 {
219 struct imx_ccm_softc *sc;
220
221 sc = device_get_softc(dev);
222 mtx_unlock(&sc->mtx);
223 }
224
225 static device_method_t imx_ccm_methods[] = {
226 /* clkdev interface */
227 DEVMETHOD(clkdev_write_4, imx_ccm_write_4),
228 DEVMETHOD(clkdev_read_4, imx_ccm_read_4),
229 DEVMETHOD(clkdev_modify_4, imx_ccm_modify_4),
230 DEVMETHOD(clkdev_device_lock, imx_ccm_device_lock),
231 DEVMETHOD(clkdev_device_unlock, imx_ccm_device_unlock),
232
233 DEVMETHOD_END
234 };
235
236 DEFINE_CLASS_0(imx_ccm, imx_ccm_driver, imx_ccm_methods,
237 sizeof(struct imx_ccm_softc));
238