1268896Sbapt/*-
2268896Sbapt * SPDX-License-Identifier: BSD-2-Clause
3268896Sbapt *
4268896Sbapt * Copyright (c) 2021 Alstom Group.
5268896Sbapt * Copyright (c) 2021 Semihalf.
6268896Sbapt *
7268896Sbapt * Redistribution and use in source and binary forms, with or without
8268896Sbapt * modification, are permitted provided that the following conditions
9268896Sbapt * are met:
10268896Sbapt * 1. Redistributions of source code must retain the above copyright
11268896Sbapt *    notice, this list of conditions and the following disclaimer.
12268896Sbapt * 2. Redistributions in binary form must reproduce the above copyright
13268896Sbapt *    notice, this list of conditions and the following disclaimer in the
14268896Sbapt *    documentation and/or other materials provided with the distribution.
15268896Sbapt *
16268896Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17268896Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18268896Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19268896Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20268896Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21268896Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22268896Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23268896Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24268896Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25268896Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26268896Sbapt * SUCH DAMAGE.
27268896Sbapt */
28268896Sbapt
29268896Sbapt#include <sys/param.h>
30268896Sbapt#include <sys/systm.h>
31268896Sbapt#include <sys/bus.h>
32268896Sbapt#include <sys/kernel.h>
33268896Sbapt#include <sys/kobj.h>
34268896Sbapt#include <sys/module.h>
35268896Sbapt#include <sys/malloc.h>
36268896Sbapt#include <sys/rman.h>
37268896Sbapt#include <sys/lock.h>
38268896Sbapt#include <sys/mutex.h>
39268896Sbapt
40268896Sbapt#include <machine/bus.h>
41268896Sbapt#include <machine/cpu.h>
42268896Sbapt
43268896Sbapt#include <dev/clk/clk_div.h>
44268896Sbapt#include <dev/ofw/openfirm.h>
45268896Sbapt#include <dev/ofw/ofw_bus.h>
46268896Sbapt#include <dev/ofw/ofw_bus_subr.h>
47268896Sbapt
48268896Sbapt#include "clkdev_if.h"
49268896Sbapt#include "syscon_if.h"
50268896Sbapt
51268896Sbapt
52268896Sbaptstruct ls1028a_flexspi_clk_softc {
53268896Sbapt	device_t		dev;
54268896Sbapt	struct clkdom		*clkdom;
55268896Sbapt	uint64_t 		reg_offset;
56268896Sbapt	struct syscon		*syscon;
57268896Sbapt	struct clk_div_def	clk_def;
58268896Sbapt	struct mtx		mtx;
59268896Sbapt};
60268896Sbapt
61268896Sbaptstatic struct clk_div_table ls1028a_flexspi_div_tbl[] = {
62268896Sbapt	{ .value = 0, .divider = 1, },
63268896Sbapt	{ .value = 1, .divider = 2, },
64268896Sbapt	{ .value = 2, .divider = 3, },
65268896Sbapt	{ .value = 3, .divider = 4, },
66268896Sbapt	{ .value = 4, .divider = 5, },
67268896Sbapt	{ .value = 5, .divider = 6, },
68268896Sbapt	{ .value = 6, .divider = 7, },
69268896Sbapt	{ .value = 7, .divider = 8, },
70268896Sbapt	{ .value = 11, .divider = 12, },
71268896Sbapt	{ .value = 15, .divider = 16, },
72268896Sbapt	{ .value = 16, .divider = 20, },
73268896Sbapt	{ .value = 17, .divider = 24, },
74268896Sbapt	{ .value = 18, .divider = 28, },
75268896Sbapt	{ .value = 19, .divider = 32, },
76268896Sbapt	{ .value = 20, .divider = 80, },
77268896Sbapt	{}
78268896Sbapt};
79268896Sbaptstatic struct clk_div_table lx2160a_flexspi_div_tbl[] = {
80268896Sbapt	{ .value = 1, .divider = 2, },
81268896Sbapt	{ .value = 3, .divider = 4, },
82268896Sbapt	{ .value = 5, .divider = 6, },
83268896Sbapt	{ .value = 7, .divider = 8, },
84268896Sbapt	{ .value = 11, .divider = 12, },
85268896Sbapt	{ .value = 15, .divider = 16, },
86268896Sbapt	{ .value = 16, .divider = 20, },
87268896Sbapt	{ .value = 17, .divider = 24, },
88268896Sbapt	{ .value = 18, .divider = 28, },
89268896Sbapt	{ .value = 19, .divider = 32, },
90268896Sbapt	{ .value = 20, .divider = 80, },
91268896Sbapt	{}
92268896Sbapt};
93268896Sbapt
94268896Sbaptstatic struct ofw_compat_data compat_data[] = {
95268896Sbapt	{ "fsl,ls1028a-flexspi-clk",	(uintptr_t)ls1028a_flexspi_div_tbl },
96268896Sbapt	{ "fsl,lx2160a-flexspi-clk",	(uintptr_t)lx2160a_flexspi_div_tbl },
97268896Sbapt	{ NULL, 0 }
98268896Sbapt};
99268896Sbapt
100268896Sbaptstatic int
101268896Sbaptls1028a_flexspi_clk_probe(device_t dev)
102268896Sbapt{
103268896Sbapt
104268896Sbapt	if (!ofw_bus_status_okay(dev))
105268896Sbapt		return (ENXIO);
106268896Sbapt
107268896Sbapt	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
108268896Sbapt		device_set_desc(dev, "NXP FlexSPI clock driver");
109268896Sbapt		return (BUS_PROBE_DEFAULT);
110268896Sbapt	}
111268896Sbapt
112268896Sbapt	return (ENXIO);
113268896Sbapt}
114268896Sbapt
115268896Sbaptstatic int
116268896Sbaptls1028a_flexspi_clk_attach(device_t dev)
117268896Sbapt{
118268896Sbapt	struct ls1028a_flexspi_clk_softc *sc;
119268896Sbapt	const char *oclkname = NULL;
120268896Sbapt	const char *pclkname[1];
121268896Sbapt	uint32_t acells;
122268896Sbapt	uint32_t scells;
123268896Sbapt	pcell_t cells[4];
124268896Sbapt	phandle_t node;
125268896Sbapt	uint64_t reg_size;
126268896Sbapt	int ret;
127268896Sbapt	clk_t clk;
128268896Sbapt
129268896Sbapt	sc = device_get_softc(dev);
130268896Sbapt	sc->dev = dev;
131268896Sbapt	node = ofw_bus_get_node(dev);
132268896Sbapt
133268896Sbapt	/* Parse address-cells and size-cells from the parent node as a fallback */
134268896Sbapt	if (OF_getencprop(node, "#address-cells", &acells,
135268896Sbapt	    sizeof(acells)) == -1) {
136268896Sbapt		if (OF_getencprop(OF_parent(node), "#address-cells", &acells,
137268896Sbapt		    sizeof(acells)) == -1) {
138268896Sbapt			acells = 2;
139268896Sbapt		}
140268896Sbapt	}
141268896Sbapt	if (OF_getencprop(node, "#size-cells", &scells,
142268896Sbapt	    sizeof(scells)) == -1) {
143268896Sbapt		if (OF_getencprop(OF_parent(node), "#size-cells", &scells,
144268896Sbapt		    sizeof(scells)) == -1) {
145268896Sbapt			scells = 1;
146268896Sbapt		}
147268896Sbapt	}
148268896Sbapt	ret = OF_getencprop(node, "reg", cells, (acells + scells) * sizeof(pcell_t));
149268896Sbapt	if (ret < 0) {
150268896Sbapt		device_printf(dev, "ERROR: failed to read REG property\n");
151268896Sbapt		return (ENOMEM);
152268896Sbapt	}
153268896Sbapt	sc->reg_offset = (uint64_t)cells[0];
154268896Sbapt	if (acells == 2)
155268896Sbapt		sc->reg_offset = (sc->reg_offset << 32) | (uint64_t)cells[1];
156268896Sbapt	reg_size = (uint64_t)cells[acells];
157268896Sbapt	if (scells == 2)
158268896Sbapt		reg_size = (reg_size << 32) | (uint64_t)cells[acells + 1];
159268896Sbapt
160268896Sbapt	if (reg_size != 4) {
161268896Sbapt		device_printf(dev, "ERROR, expected only single register\n");
162268896Sbapt		return (EINVAL);
163268896Sbapt	}
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
225static int
226ls1028a_flexspi_clk_detach(device_t dev)
227{
228
229	/* Clock detaching is not supported */
230	return (EACCES);
231}
232
233static int
234ls1028a_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
244static int
245ls1028a_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
257static int
258ls1028a_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
270static void
271ls1028a_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
279static void
280ls1028a_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
289static 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
304static DEFINE_CLASS_0(fspi_clk, ls1028a_flexspi_clk_driver, ls1028a_flexspi_clk_methods,
305    sizeof(struct ls1028a_flexspi_clk_softc));
306EARLY_DRIVER_MODULE(ls1028a_flexspi_clk, simple_mfd, ls1028a_flexspi_clk_driver,
307    NULL, NULL, BUS_PASS_TIMER);
308