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