xref: /freebsd/sys/arm64/qoriq/clk/ls1028a_flexspi_clk.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021 Alstom Group.
5  * Copyright (c) 2021 Semihalf.
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 AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, 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 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/kernel.h>
33 #include <sys/kobj.h>
34 #include <sys/module.h>
35 #include <sys/malloc.h>
36 #include <sys/rman.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39 
40 #include <machine/bus.h>
41 #include <machine/cpu.h>
42 
43 #include <dev/extres/clk/clk_div.h>
44 #include <dev/ofw/openfirm.h>
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47 
48 #include "clkdev_if.h"
49 #include "syscon_if.h"
50 
51 
52 struct ls1028a_flexspi_clk_softc {
53 	device_t		dev;
54 	struct clkdom		*clkdom;
55 	uint64_t 		reg_offset;
56 	struct syscon		*syscon;
57 	struct clk_div_def	clk_def;
58 	struct mtx		mtx;
59 };
60 
61 static struct clk_div_table ls1028a_flexspi_div_tbl[] = {
62 	{ .value = 0, .divider = 1, },
63 	{ .value = 1, .divider = 2, },
64 	{ .value = 2, .divider = 3, },
65 	{ .value = 3, .divider = 4, },
66 	{ .value = 4, .divider = 5, },
67 	{ .value = 5, .divider = 6, },
68 	{ .value = 6, .divider = 7, },
69 	{ .value = 7, .divider = 8, },
70 	{ .value = 11, .divider = 12, },
71 	{ .value = 15, .divider = 16, },
72 	{ .value = 16, .divider = 20, },
73 	{ .value = 17, .divider = 24, },
74 	{ .value = 18, .divider = 28, },
75 	{ .value = 19, .divider = 32, },
76 	{ .value = 20, .divider = 80, },
77 	{}
78 };
79 static struct clk_div_table lx2160a_flexspi_div_tbl[] = {
80 	{ .value = 1, .divider = 2, },
81 	{ .value = 3, .divider = 4, },
82 	{ .value = 5, .divider = 6, },
83 	{ .value = 7, .divider = 8, },
84 	{ .value = 11, .divider = 12, },
85 	{ .value = 15, .divider = 16, },
86 	{ .value = 16, .divider = 20, },
87 	{ .value = 17, .divider = 24, },
88 	{ .value = 18, .divider = 28, },
89 	{ .value = 19, .divider = 32, },
90 	{ .value = 20, .divider = 80, },
91 	{}
92 };
93 
94 static struct ofw_compat_data compat_data[] = {
95 	{ "fsl,ls1028a-flexspi-clk",	(uintptr_t)ls1028a_flexspi_div_tbl },
96 	{ "fsl,lx2160a-flexspi-clk",	(uintptr_t)lx2160a_flexspi_div_tbl },
97 	{ NULL, 0 }
98 };
99 
100 static int
101 ls1028a_flexspi_clk_probe(device_t dev)
102 {
103 
104 	if (!ofw_bus_status_okay(dev))
105 		return (ENXIO);
106 
107 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
108 		device_set_desc(dev, "NXP FlexSPI clock driver");
109 		return (BUS_PROBE_DEFAULT);
110 	}
111 
112 	return (ENXIO);
113 }
114 
115 static int
116 ls1028a_flexspi_clk_attach(device_t dev)
117 {
118 	struct ls1028a_flexspi_clk_softc *sc;
119 	const char *oclkname = NULL;
120 	const char *pclkname[1];
121 	uint32_t acells;
122 	uint32_t scells;
123 	pcell_t cells[4];
124 	phandle_t node;
125 	uint64_t reg_size;
126 	int ret;
127 	clk_t clk;
128 
129 	sc = device_get_softc(dev);
130 	sc->dev = dev;
131 	node = ofw_bus_get_node(dev);
132 
133 	/* Parse address-cells and size-cells from the parent node as a fallback */
134 	if (OF_getencprop(node, "#address-cells", &acells,
135 	    sizeof(acells)) == -1) {
136 		if (OF_getencprop(OF_parent(node), "#address-cells", &acells,
137 		    sizeof(acells)) == -1) {
138 			acells = 2;
139 		}
140 	}
141 	if (OF_getencprop(node, "#size-cells", &scells,
142 	    sizeof(scells)) == -1) {
143 		if (OF_getencprop(OF_parent(node), "#size-cells", &scells,
144 		    sizeof(scells)) == -1) {
145 			scells = 1;
146 		}
147 	}
148 	ret = OF_getencprop(node, "reg", cells, (acells + scells) * sizeof(pcell_t));
149 	if (ret < 0) {
150 		device_printf(dev, "ERROR: failed to read REG property\n");
151 		return (ENOMEM);
152 	}
153 	sc->reg_offset = (uint64_t)cells[0];
154 	if (acells == 2)
155 		sc->reg_offset = (sc->reg_offset << 32) | (uint64_t)cells[1];
156 	reg_size = (uint64_t)cells[acells];
157 	if (scells == 2)
158 		reg_size = (reg_size << 32) | (uint64_t)cells[acells + 1];
159 
160 	if (reg_size != 4) {
161 		device_printf(dev, "ERROR, expected only single register\n");
162 		return (EINVAL);
163 	}
164 	if (sc->reg_offset >> 32UL) {
165 		device_printf(dev, "ERROR, only 32-bit address offset is supported\n");
166 		return (EINVAL);
167 	}
168 
169 	/* Get syscon handle */
170 	ret = SYSCON_GET_HANDLE(dev, &sc->syscon);
171 	if ((ret != 0) || (sc->syscon == NULL)) {
172 		device_printf(dev, "ERROR: failed to get syscon\n");
173 		return (EFAULT);
174 	}
175 
176 	/* Initialize access mutex */
177 	mtx_init(&sc->mtx, "FSL clock mtx", NULL, MTX_DEF);
178 
179 	/* Get clock names */
180 	ret = clk_get_by_ofw_index(dev, node, 0, &clk);
181 	if (ret) {
182 		device_printf(dev, "ERROR: failed to get parent clock\n");
183 		return (EINVAL);
184 	}
185 	pclkname[0] = clk_get_name(clk);
186 	ret = clk_parse_ofw_clk_name(dev, node, &oclkname);
187 	if (ret) {
188 		device_printf(dev, "ERROR: failed to get output clock name\n");
189 		return (EINVAL);
190 	}
191 
192 #ifdef DEBUG
193 	device_printf(dev, "INFO: pclkname %s, oclkname %s\n", pclkname[0], oclkname);
194 #endif
195 
196 	/* Fixup CLK structure */
197 	sc->clk_def.clkdef.name = oclkname;
198 	sc->clk_def.clkdef.parent_names = (const char **)pclkname;
199 	sc->clk_def.offset = (uint32_t)sc->reg_offset;
200 	sc->clk_def.clkdef.id = 1;
201 	sc->clk_def.clkdef.parent_cnt = 1;
202 	sc->clk_def.clkdef.flags =  0;
203 	sc->clk_def.div_flags = CLK_DIV_WITH_TABLE;
204 	sc->clk_def.i_shift = 0;
205 	sc->clk_def.i_width = 5;
206 	sc->clk_def.div_table = (struct clk_div_table*)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
207 
208 	/* Create clock */
209 	sc->clkdom = clkdom_create(dev);
210 	if (sc->clkdom == NULL)
211 		panic("clkdom == NULL");
212 	ret = clknode_div_register(sc->clkdom, &sc->clk_def);
213 	if (ret) {
214 		device_printf(dev, "ERROR: unable to register clock\n");
215 		return (EINVAL);
216 	}
217 	clkdom_finit(sc->clkdom);
218 
219 	if (bootverbose)
220 		clkdom_dump(sc->clkdom);
221 
222 	return (0);
223 }
224 
225 static int
226 ls1028a_flexspi_clk_detach(device_t dev)
227 {
228 
229 	/* Clock detaching is not supported */
230 	return (EACCES);
231 }
232 
233 static int
234 ls1028a_flexspi_clk_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
235 {
236 	struct ls1028a_flexspi_clk_softc *sc;
237 	sc = device_get_softc(dev);
238 
239 	*val = SYSCON_READ_4(sc->syscon, addr);
240 
241 	return (0);
242 }
243 
244 static int
245 ls1028a_flexspi_clk_write_4(device_t dev, bus_addr_t addr, uint32_t val)
246 {
247 	struct ls1028a_flexspi_clk_softc *sc;
248 	int ret;
249 
250 	sc = device_get_softc(dev);
251 
252 	ret = SYSCON_WRITE_4(sc->syscon, addr, val);
253 
254 	return (ret);
255 }
256 
257 static int
258 ls1028a_flexspi_clk_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
259 {
260 	struct ls1028a_flexspi_clk_softc *sc;
261 	int ret;
262 
263 	sc = device_get_softc(dev);
264 
265 	ret = SYSCON_MODIFY_4(sc->syscon, addr, clr, set);
266 
267 	return (ret);
268 }
269 
270 static void
271 ls1028a_flexspi_clk_device_lock(device_t dev)
272 {
273 	struct ls1028a_flexspi_clk_softc *sc;
274 	sc = device_get_softc(dev);
275 
276 	mtx_lock(&sc->mtx);
277 }
278 
279 static void
280 ls1028a_flexspi_clk_device_unlock(device_t dev)
281 {
282 	struct ls1028a_flexspi_clk_softc *sc;
283 
284 	sc = device_get_softc(dev);
285 
286 	mtx_unlock(&sc->mtx);
287 }
288 
289 static device_method_t ls1028a_flexspi_clk_methods[] = {
290 	/* Device interface */
291 	DEVMETHOD(device_probe,		ls1028a_flexspi_clk_probe),
292 	DEVMETHOD(device_attach,	ls1028a_flexspi_clk_attach),
293 	DEVMETHOD(device_detach,	ls1028a_flexspi_clk_detach),
294 
295 	DEVMETHOD(clkdev_read_4,	ls1028a_flexspi_clk_read_4),
296 	DEVMETHOD(clkdev_write_4,	ls1028a_flexspi_clk_write_4),
297 	DEVMETHOD(clkdev_modify_4,	ls1028a_flexspi_clk_modify_4),
298 	DEVMETHOD(clkdev_device_lock,	ls1028a_flexspi_clk_device_lock),
299 	DEVMETHOD(clkdev_device_unlock,	ls1028a_flexspi_clk_device_unlock),
300 
301 	DEVMETHOD_END
302 };
303 
304 static DEFINE_CLASS_0(fspi_clk, ls1028a_flexspi_clk_driver, ls1028a_flexspi_clk_methods,
305     sizeof(struct ls1028a_flexspi_clk_softc));
306 EARLY_DRIVER_MODULE(ls1028a_flexspi_clk, simple_mfd, ls1028a_flexspi_clk_driver,
307     NULL, NULL, BUS_PASS_TIMER);
308