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/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
ls1028a_flexspi_clk_probe(device_t dev)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
ls1028a_flexspi_clk_attach(device_t dev)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
ls1028a_flexspi_clk_detach(device_t dev)226 ls1028a_flexspi_clk_detach(device_t dev)
227 {
228
229 /* Clock detaching is not supported */
230 return (EACCES);
231 }
232
233 static int
ls1028a_flexspi_clk_read_4(device_t dev,bus_addr_t addr,uint32_t * val)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
ls1028a_flexspi_clk_write_4(device_t dev,bus_addr_t addr,uint32_t val)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
ls1028a_flexspi_clk_modify_4(device_t dev,bus_addr_t addr,uint32_t clr,uint32_t set)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
ls1028a_flexspi_clk_device_lock(device_t dev)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
ls1028a_flexspi_clk_device_unlock(device_t dev)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