xref: /freebsd/sys/arm64/freescale/imx/imx_ccm.c (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
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
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
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
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
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
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
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
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
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
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