1297627Sjmcneill/*-
2297627Sjmcneill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3297627Sjmcneill * All rights reserved.
4297627Sjmcneill *
5297627Sjmcneill * Redistribution and use in source and binary forms, with or without
6297627Sjmcneill * modification, are permitted provided that the following conditions
7297627Sjmcneill * are met:
8297627Sjmcneill * 1. Redistributions of source code must retain the above copyright
9297627Sjmcneill *    notice, this list of conditions and the following disclaimer.
10297627Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
11297627Sjmcneill *    notice, this list of conditions and the following disclaimer in the
12297627Sjmcneill *    documentation and/or other materials provided with the distribution.
13297627Sjmcneill *
14297627Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15297627Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16297627Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17297627Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18297627Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19297627Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20297627Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21297627Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22297627Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23297627Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24297627Sjmcneill * SUCH DAMAGE.
25297627Sjmcneill *
26297627Sjmcneill * $FreeBSD: stable/11/sys/arm/allwinner/clk/aw_debeclk.c 308324 2016-11-05 04:17:32Z mmel $
27297627Sjmcneill */
28297627Sjmcneill
29297627Sjmcneill/*
30297627Sjmcneill * Allwinner display backend clocks
31297627Sjmcneill */
32297627Sjmcneill
33297627Sjmcneill#include <sys/cdefs.h>
34297627Sjmcneill__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_debeclk.c 308324 2016-11-05 04:17:32Z mmel $");
35297627Sjmcneill
36297627Sjmcneill#include <sys/param.h>
37297627Sjmcneill#include <sys/systm.h>
38297627Sjmcneill#include <sys/bus.h>
39297627Sjmcneill#include <sys/rman.h>
40297627Sjmcneill#include <sys/kernel.h>
41297627Sjmcneill#include <sys/module.h>
42297627Sjmcneill#include <machine/bus.h>
43297627Sjmcneill
44297627Sjmcneill#include <dev/ofw/ofw_bus.h>
45297627Sjmcneill#include <dev/ofw/ofw_bus_subr.h>
46297627Sjmcneill#include <dev/ofw/ofw_subr.h>
47297627Sjmcneill
48297627Sjmcneill#include <dev/extres/clk/clk.h>
49297627Sjmcneill#include <dev/extres/hwreset/hwreset.h>
50297627Sjmcneill
51297627Sjmcneill#include "clkdev_if.h"
52297627Sjmcneill#include "hwreset_if.h"
53297627Sjmcneill
54297627Sjmcneill#define	SCLK_GATING		(1 << 31)
55297627Sjmcneill#define	BE_RST			(1 << 30)
56297627Sjmcneill#define	CLK_SRC_SEL		(0x3 << 24)
57297627Sjmcneill#define	CLK_SRC_SEL_SHIFT	24
58297627Sjmcneill#define	CLK_SRC_SEL_MAX		2
59297627Sjmcneill#define	CLK_SRC_SEL_PLL3	0
60297627Sjmcneill#define	CLK_SRC_SEL_PLL7	1
61297627Sjmcneill#define	CLK_SRC_SEL_PLL5	2
62297627Sjmcneill#define	CLK_RATIO_M		(0xf << 0)
63297627Sjmcneill#define	CLK_RATIO_M_SHIFT	0
64297627Sjmcneill#define	CLK_RATIO_M_MAX		0xf
65297627Sjmcneill
66297627Sjmcneillstatic struct ofw_compat_data compat_data[] = {
67297627Sjmcneill	{ "allwinner,sun4i-a10-de-be-clk",	1 },
68297627Sjmcneill	{ NULL, 0 }
69297627Sjmcneill};
70297627Sjmcneill
71297627Sjmcneillstruct aw_debeclk_softc {
72297627Sjmcneill	device_t	clkdev;
73297627Sjmcneill	bus_addr_t	reg;
74297627Sjmcneill};
75297627Sjmcneill
76297627Sjmcneill#define	DEBECLK_READ(sc, val)	CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
77297627Sjmcneill#define	DEBECLK_WRITE(sc, val)	CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
78297627Sjmcneill#define	DEBECLK_MODIFY(sc, clr, set)	\
79297627Sjmcneill	CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set))
80297627Sjmcneill#define	DEVICE_LOCK(sc)		CLKDEV_DEVICE_LOCK((sc)->clkdev)
81297627Sjmcneill#define	DEVICE_UNLOCK(sc)	CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
82297627Sjmcneill
83297627Sjmcneillstatic int
84297627Sjmcneillaw_debeclk_hwreset_assert(device_t dev, intptr_t id, bool value)
85297627Sjmcneill{
86297627Sjmcneill	struct aw_debeclk_softc *sc;
87297627Sjmcneill	int error;
88297627Sjmcneill
89297627Sjmcneill	sc = device_get_softc(dev);
90297627Sjmcneill
91297627Sjmcneill	DEVICE_LOCK(sc);
92297627Sjmcneill	error = DEBECLK_MODIFY(sc, BE_RST, value ? 0 : BE_RST);
93297627Sjmcneill	DEVICE_UNLOCK(sc);
94297627Sjmcneill
95297627Sjmcneill	return (error);
96297627Sjmcneill}
97297627Sjmcneill
98297627Sjmcneillstatic int
99297627Sjmcneillaw_debeclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value)
100297627Sjmcneill{
101297627Sjmcneill	struct aw_debeclk_softc *sc;
102297627Sjmcneill	uint32_t val;
103297627Sjmcneill	int error;
104297627Sjmcneill
105297627Sjmcneill	sc = device_get_softc(dev);
106297627Sjmcneill
107297627Sjmcneill	DEVICE_LOCK(sc);
108297627Sjmcneill	error = DEBECLK_READ(sc, &val);
109297627Sjmcneill	DEVICE_UNLOCK(sc);
110297627Sjmcneill
111297627Sjmcneill	if (error)
112297627Sjmcneill		return (error);
113297627Sjmcneill
114297627Sjmcneill	*value = (val & BE_RST) != 0 ? false : true;
115297627Sjmcneill
116297627Sjmcneill	return (0);
117297627Sjmcneill}
118297627Sjmcneill
119297627Sjmcneillstatic int
120297627Sjmcneillaw_debeclk_init(struct clknode *clk, device_t dev)
121297627Sjmcneill{
122297627Sjmcneill	struct aw_debeclk_softc *sc;
123297627Sjmcneill	uint32_t val, index;
124297627Sjmcneill
125297627Sjmcneill	sc = clknode_get_softc(clk);
126297627Sjmcneill
127297627Sjmcneill	/* Set BE source to PLL5 (DDR external peripheral clock) */
128297627Sjmcneill	index = CLK_SRC_SEL_PLL5;
129297627Sjmcneill
130297627Sjmcneill	DEVICE_LOCK(sc);
131297627Sjmcneill	DEBECLK_READ(sc, &val);
132297627Sjmcneill	val &= ~CLK_SRC_SEL;
133297627Sjmcneill	val |= (index << CLK_SRC_SEL_SHIFT);
134297627Sjmcneill	DEBECLK_WRITE(sc, val);
135297627Sjmcneill	DEVICE_UNLOCK(sc);
136297627Sjmcneill
137297627Sjmcneill	clknode_init_parent_idx(clk, index);
138297627Sjmcneill	return (0);
139297627Sjmcneill}
140297627Sjmcneill
141297627Sjmcneillstatic int
142297627Sjmcneillaw_debeclk_set_mux(struct clknode *clk, int index)
143297627Sjmcneill{
144297627Sjmcneill	struct aw_debeclk_softc *sc;
145297627Sjmcneill	uint32_t val;
146297627Sjmcneill
147297627Sjmcneill	sc = clknode_get_softc(clk);
148297627Sjmcneill
149297627Sjmcneill	if (index < 0 || index > CLK_SRC_SEL_MAX)
150297627Sjmcneill		return (ERANGE);
151297627Sjmcneill
152297627Sjmcneill	DEVICE_LOCK(sc);
153297627Sjmcneill	DEBECLK_READ(sc, &val);
154297627Sjmcneill	val &= ~CLK_SRC_SEL;
155297627Sjmcneill	val |= (index << CLK_SRC_SEL_SHIFT);
156297627Sjmcneill	DEBECLK_WRITE(sc, val);
157297627Sjmcneill	DEVICE_UNLOCK(sc);
158297627Sjmcneill
159297627Sjmcneill	return (0);
160297627Sjmcneill}
161297627Sjmcneill
162297627Sjmcneillstatic int
163297627Sjmcneillaw_debeclk_set_gate(struct clknode *clk, bool enable)
164297627Sjmcneill{
165297627Sjmcneill	struct aw_debeclk_softc *sc;
166297627Sjmcneill	uint32_t val;
167297627Sjmcneill
168297627Sjmcneill	sc = clknode_get_softc(clk);
169297627Sjmcneill
170297627Sjmcneill	DEVICE_LOCK(sc);
171297627Sjmcneill	DEBECLK_READ(sc, &val);
172297627Sjmcneill	if (enable)
173297627Sjmcneill		val |= SCLK_GATING;
174297627Sjmcneill	else
175297627Sjmcneill		val &= ~SCLK_GATING;
176297627Sjmcneill	DEBECLK_WRITE(sc, val);
177297627Sjmcneill	DEVICE_UNLOCK(sc);
178297627Sjmcneill
179297627Sjmcneill	return (0);
180297627Sjmcneill}
181297627Sjmcneill
182297627Sjmcneillstatic int
183297627Sjmcneillaw_debeclk_recalc_freq(struct clknode *clk, uint64_t *freq)
184297627Sjmcneill{
185297627Sjmcneill	struct aw_debeclk_softc *sc;
186297627Sjmcneill	uint32_t val, m;
187297627Sjmcneill
188297627Sjmcneill	sc = clknode_get_softc(clk);
189297627Sjmcneill
190297627Sjmcneill	DEVICE_LOCK(sc);
191297627Sjmcneill	DEBECLK_READ(sc, &val);
192297627Sjmcneill	DEVICE_UNLOCK(sc);
193297627Sjmcneill
194297627Sjmcneill	m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1;
195297627Sjmcneill
196297627Sjmcneill	*freq = *freq / m;
197297627Sjmcneill
198297627Sjmcneill	return (0);
199297627Sjmcneill}
200297627Sjmcneill
201297627Sjmcneillstatic int
202297627Sjmcneillaw_debeclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
203297627Sjmcneill    int flags, int *stop)
204297627Sjmcneill{
205297627Sjmcneill	struct aw_debeclk_softc *sc;
206297627Sjmcneill	uint32_t val, m;
207297627Sjmcneill
208297627Sjmcneill	sc = clknode_get_softc(clk);
209297627Sjmcneill
210297627Sjmcneill	m = howmany(fin, *fout) - 1;
211297627Sjmcneill
212297627Sjmcneill	DEVICE_LOCK(sc);
213297627Sjmcneill	DEBECLK_READ(sc, &val);
214297627Sjmcneill	val &= ~CLK_RATIO_M;
215297627Sjmcneill	val |= (m << CLK_RATIO_M_SHIFT);
216297627Sjmcneill	DEBECLK_WRITE(sc, val);
217297627Sjmcneill	DEVICE_UNLOCK(sc);
218297627Sjmcneill
219297627Sjmcneill	*fout = fin / (m + 1);
220297627Sjmcneill	*stop = 1;
221297627Sjmcneill
222297627Sjmcneill	return (0);
223297627Sjmcneill}
224297627Sjmcneill
225297627Sjmcneillstatic clknode_method_t aw_debeclk_clknode_methods[] = {
226297627Sjmcneill	/* Device interface */
227297627Sjmcneill	CLKNODEMETHOD(clknode_init,		aw_debeclk_init),
228297627Sjmcneill	CLKNODEMETHOD(clknode_set_gate,		aw_debeclk_set_gate),
229297627Sjmcneill	CLKNODEMETHOD(clknode_set_mux,		aw_debeclk_set_mux),
230297627Sjmcneill	CLKNODEMETHOD(clknode_recalc_freq,	aw_debeclk_recalc_freq),
231297627Sjmcneill	CLKNODEMETHOD(clknode_set_freq,		aw_debeclk_set_freq),
232297627Sjmcneill	CLKNODEMETHOD_END
233297627Sjmcneill};
234297627SjmcneillDEFINE_CLASS_1(aw_debeclk_clknode, aw_debeclk_clknode_class,
235297627Sjmcneill    aw_debeclk_clknode_methods, sizeof(struct aw_debeclk_softc), clknode_class);
236297627Sjmcneill
237297627Sjmcneillstatic int
238297627Sjmcneillaw_debeclk_probe(device_t dev)
239297627Sjmcneill{
240297627Sjmcneill	if (!ofw_bus_status_okay(dev))
241297627Sjmcneill		return (ENXIO);
242297627Sjmcneill
243297627Sjmcneill	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
244297627Sjmcneill		return (ENXIO);
245297627Sjmcneill
246297627Sjmcneill	device_set_desc(dev, "Allwinner Display Engine Backend Clock");
247297627Sjmcneill	return (BUS_PROBE_DEFAULT);
248297627Sjmcneill}
249297627Sjmcneill
250297627Sjmcneillstatic int
251297627Sjmcneillaw_debeclk_attach(device_t dev)
252297627Sjmcneill{
253297627Sjmcneill	struct clknode_init_def def;
254297627Sjmcneill	struct aw_debeclk_softc *sc, *clk_sc;
255297627Sjmcneill	struct clkdom *clkdom;
256297627Sjmcneill	struct clknode *clk;
257297627Sjmcneill	clk_t clk_parent;
258297627Sjmcneill	bus_size_t psize;
259297627Sjmcneill	phandle_t node;
260297627Sjmcneill	int error, ncells, i;
261297627Sjmcneill
262297627Sjmcneill	sc = device_get_softc(dev);
263297627Sjmcneill	sc->clkdev = device_get_parent(dev);
264297627Sjmcneill	node = ofw_bus_get_node(dev);
265297627Sjmcneill
266297627Sjmcneill	if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) {
267297627Sjmcneill		device_printf(dev, "cannot parse 'reg' property\n");
268297627Sjmcneill		return (ENXIO);
269297627Sjmcneill	}
270297627Sjmcneill
271297627Sjmcneill	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
272297627Sjmcneill	    "#clock-cells", &ncells);
273297627Sjmcneill	if (error != 0) {
274297627Sjmcneill		device_printf(dev, "cannot get clock count\n");
275297627Sjmcneill		return (error);
276297627Sjmcneill	}
277297627Sjmcneill
278297627Sjmcneill	clkdom = clkdom_create(dev);
279297627Sjmcneill
280297627Sjmcneill	memset(&def, 0, sizeof(def));
281297627Sjmcneill	error = clk_parse_ofw_clk_name(dev, node, &def.name);
282297627Sjmcneill	if (error != 0) {
283297627Sjmcneill		device_printf(dev, "cannot parse clock name\n");
284297627Sjmcneill		error = ENXIO;
285297627Sjmcneill		goto fail;
286297627Sjmcneill	}
287297627Sjmcneill	def.id = 1;
288297627Sjmcneill	def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
289297627Sjmcneill	for (i = 0; i < ncells; i++) {
290308324Smmel		error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
291297627Sjmcneill		if (error != 0) {
292297627Sjmcneill			device_printf(dev, "cannot get clock %d\n", i);
293297627Sjmcneill			goto fail;
294297627Sjmcneill		}
295297627Sjmcneill		def.parent_names[i] = clk_get_name(clk_parent);
296297627Sjmcneill		clk_release(clk_parent);
297297627Sjmcneill	}
298297627Sjmcneill	def.parent_cnt = ncells;
299297627Sjmcneill
300297627Sjmcneill	clk = clknode_create(clkdom, &aw_debeclk_clknode_class, &def);
301297627Sjmcneill	if (clk == NULL) {
302297627Sjmcneill		device_printf(dev, "cannot create clknode\n");
303297627Sjmcneill		error = ENXIO;
304297627Sjmcneill		goto fail;
305297627Sjmcneill	}
306297627Sjmcneill
307297627Sjmcneill	clk_sc = clknode_get_softc(clk);
308297627Sjmcneill	clk_sc->reg = sc->reg;
309297627Sjmcneill	clk_sc->clkdev = device_get_parent(dev);
310297627Sjmcneill
311297627Sjmcneill	clknode_register(clkdom, clk);
312297627Sjmcneill
313297627Sjmcneill	if (clkdom_finit(clkdom) != 0) {
314297627Sjmcneill		device_printf(dev, "cannot finalize clkdom initialization\n");
315297627Sjmcneill		error = ENXIO;
316297627Sjmcneill		goto fail;
317297627Sjmcneill	}
318297627Sjmcneill
319297627Sjmcneill	if (bootverbose)
320297627Sjmcneill		clkdom_dump(clkdom);
321297627Sjmcneill
322297627Sjmcneill	hwreset_register_ofw_provider(dev);
323297627Sjmcneill
324297627Sjmcneill	return (0);
325297627Sjmcneill
326297627Sjmcneillfail:
327297627Sjmcneill	return (error);
328297627Sjmcneill}
329297627Sjmcneill
330297627Sjmcneillstatic device_method_t aw_debeclk_methods[] = {
331297627Sjmcneill	/* Device interface */
332297627Sjmcneill	DEVMETHOD(device_probe,		aw_debeclk_probe),
333297627Sjmcneill	DEVMETHOD(device_attach,	aw_debeclk_attach),
334297627Sjmcneill
335297627Sjmcneill	/* Reset interface */
336297627Sjmcneill	DEVMETHOD(hwreset_assert,	aw_debeclk_hwreset_assert),
337297627Sjmcneill	DEVMETHOD(hwreset_is_asserted,	aw_debeclk_hwreset_is_asserted),
338297627Sjmcneill
339297627Sjmcneill	DEVMETHOD_END
340297627Sjmcneill};
341297627Sjmcneill
342297627Sjmcneillstatic driver_t aw_debeclk_driver = {
343297627Sjmcneill	"aw_debeclk",
344297627Sjmcneill	aw_debeclk_methods,
345297627Sjmcneill	sizeof(struct aw_debeclk_softc)
346297627Sjmcneill};
347297627Sjmcneill
348297627Sjmcneillstatic devclass_t aw_debeclk_devclass;
349297627Sjmcneill
350297627SjmcneillEARLY_DRIVER_MODULE(aw_debeclk, simplebus, aw_debeclk_driver,
351297627Sjmcneill    aw_debeclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
352