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_mmcclk.c 309756 2016-12-09 20:07:01Z manu $
27297627Sjmcneill */
28297627Sjmcneill
29297627Sjmcneill/*
30297627Sjmcneill * Allwinner MMC clocks
31297627Sjmcneill */
32297627Sjmcneill
33297627Sjmcneill#include <sys/cdefs.h>
34297627Sjmcneill__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_mmcclk.c 309756 2016-12-09 20:07:01Z manu $");
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_mux.h>
49297627Sjmcneill#include <dev/extres/clk/clk_gate.h>
50297627Sjmcneill
51297627Sjmcneill#include "clkdev_if.h"
52297627Sjmcneill
53297627Sjmcneill#define	SCLK_GATING			(1 << 31)
54297627Sjmcneill#define	CLK_SRC_SEL			(0x3 << 24)
55297627Sjmcneill#define	CLK_SRC_SEL_SHIFT		24
56297627Sjmcneill#define	CLK_SRC_SEL_MAX			0x3
57297627Sjmcneill#define	CLK_SRC_SEL_OSC24M		0
58297627Sjmcneill#define	CLK_SRC_SEL_PLL6		1
59297627Sjmcneill#define	CLK_PHASE_CTR			(0x7 << 20)
60297627Sjmcneill#define	CLK_PHASE_CTR_SHIFT		20
61297627Sjmcneill#define	CLK_RATIO_N			(0x3 << 16)
62297627Sjmcneill#define	CLK_RATIO_N_SHIFT		16
63297627Sjmcneill#define	CLK_RATIO_N_MAX			0x3
64297627Sjmcneill#define	OUTPUT_CLK_PHASE_CTR		(0x7 << 8)
65297627Sjmcneill#define	OUTPUT_CLK_PHASE_CTR_SHIFT	8
66297627Sjmcneill#define	CLK_RATIO_M			(0xf << 0)
67297627Sjmcneill#define	CLK_RATIO_M_SHIFT		0
68297627Sjmcneill#define	CLK_RATIO_M_MAX			0xf
69297627Sjmcneill
70297627Sjmcneillstatic struct ofw_compat_data compat_data[] = {
71297627Sjmcneill	{ "allwinner,sun4i-a10-mmc-clk",	1 },
72297627Sjmcneill	{ NULL, 0 }
73297627Sjmcneill};
74297627Sjmcneill
75297627Sjmcneillstruct aw_mmcclk_sc {
76297627Sjmcneill	device_t	clkdev;
77297627Sjmcneill	bus_addr_t	reg;
78297627Sjmcneill};
79297627Sjmcneill
80297627Sjmcneill#define	MODCLK_READ(sc, val)	CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
81297627Sjmcneill#define	MODCLK_WRITE(sc, val)	CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
82297627Sjmcneill#define	DEVICE_LOCK(sc)		CLKDEV_DEVICE_LOCK((sc)->clkdev)
83297627Sjmcneill#define	DEVICE_UNLOCK(sc)	CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
84297627Sjmcneill
85297627Sjmcneillstatic int
86297627Sjmcneillaw_mmcclk_init(struct clknode *clk, device_t dev)
87297627Sjmcneill{
88297627Sjmcneill	struct aw_mmcclk_sc *sc;
89297627Sjmcneill	uint32_t val, index;
90297627Sjmcneill
91297627Sjmcneill	sc = clknode_get_softc(clk);
92297627Sjmcneill
93297627Sjmcneill	DEVICE_LOCK(sc);
94297627Sjmcneill	MODCLK_READ(sc, &val);
95297627Sjmcneill	DEVICE_UNLOCK(sc);
96297627Sjmcneill
97297627Sjmcneill	index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT;
98297627Sjmcneill
99297627Sjmcneill	clknode_init_parent_idx(clk, index);
100297627Sjmcneill	return (0);
101297627Sjmcneill}
102297627Sjmcneill
103297627Sjmcneillstatic int
104297627Sjmcneillaw_mmcclk_set_mux(struct clknode *clk, int index)
105297627Sjmcneill{
106297627Sjmcneill	struct aw_mmcclk_sc *sc;
107297627Sjmcneill	uint32_t val;
108297627Sjmcneill
109297627Sjmcneill	sc = clknode_get_softc(clk);
110297627Sjmcneill
111297627Sjmcneill	if (index < 0 || index > CLK_SRC_SEL_MAX)
112297627Sjmcneill		return (ERANGE);
113297627Sjmcneill
114297627Sjmcneill	DEVICE_LOCK(sc);
115297627Sjmcneill	MODCLK_READ(sc, &val);
116297627Sjmcneill	val &= ~CLK_SRC_SEL;
117297627Sjmcneill	val |= (index << CLK_SRC_SEL_SHIFT);
118297627Sjmcneill	MODCLK_WRITE(sc, val);
119297627Sjmcneill	DEVICE_UNLOCK(sc);
120297627Sjmcneill
121297627Sjmcneill	return (0);
122297627Sjmcneill}
123297627Sjmcneill
124297627Sjmcneillstatic int
125297627Sjmcneillaw_mmcclk_set_gate(struct clknode *clk, bool enable)
126297627Sjmcneill{
127297627Sjmcneill	struct aw_mmcclk_sc *sc;
128297627Sjmcneill	uint32_t val;
129297627Sjmcneill
130297627Sjmcneill	sc = clknode_get_softc(clk);
131297627Sjmcneill
132297627Sjmcneill	DEVICE_LOCK(sc);
133297627Sjmcneill	MODCLK_READ(sc, &val);
134297627Sjmcneill	if (enable)
135297627Sjmcneill		val |= SCLK_GATING;
136297627Sjmcneill	else
137297627Sjmcneill		val &= ~SCLK_GATING;
138297627Sjmcneill	MODCLK_WRITE(sc, val);
139297627Sjmcneill	DEVICE_UNLOCK(sc);
140297627Sjmcneill
141297627Sjmcneill	return (0);
142297627Sjmcneill}
143297627Sjmcneill
144297627Sjmcneillstatic int
145297627Sjmcneillaw_mmcclk_recalc_freq(struct clknode *clk, uint64_t *freq)
146297627Sjmcneill{
147297627Sjmcneill	struct aw_mmcclk_sc *sc;
148297627Sjmcneill	uint32_t val, m, n;
149297627Sjmcneill
150297627Sjmcneill	sc = clknode_get_softc(clk);
151297627Sjmcneill
152297627Sjmcneill	DEVICE_LOCK(sc);
153297627Sjmcneill	MODCLK_READ(sc, &val);
154297627Sjmcneill	DEVICE_UNLOCK(sc);
155297627Sjmcneill
156297627Sjmcneill	n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT);
157297627Sjmcneill	m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1;
158297627Sjmcneill
159297627Sjmcneill	*freq = *freq / n / m;
160297627Sjmcneill
161297627Sjmcneill	return (0);
162297627Sjmcneill}
163297627Sjmcneill
164297627Sjmcneillstatic int
165297627Sjmcneillaw_mmcclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
166297627Sjmcneill    int flags, int *stop)
167297627Sjmcneill{
168297627Sjmcneill	struct aw_mmcclk_sc *sc;
169297627Sjmcneill	uint32_t val, m, n, phase, ophase;
170297627Sjmcneill	int parent_idx, error;
171297627Sjmcneill
172297627Sjmcneill	sc = clknode_get_softc(clk);
173297627Sjmcneill
174297627Sjmcneill	/* XXX
175297627Sjmcneill	 * The ophase/phase values should be set by the MMC driver, but
176297627Sjmcneill	 * there is currently no way to do this with the clk API
177297627Sjmcneill	 */
178297627Sjmcneill	if (*fout <= 400000) {
179297627Sjmcneill		parent_idx = CLK_SRC_SEL_OSC24M;
180297627Sjmcneill		ophase = 0;
181297627Sjmcneill		phase = 0;
182297627Sjmcneill		n = 2;
183297627Sjmcneill	} else if (*fout <= 25000000) {
184297627Sjmcneill		parent_idx = CLK_SRC_SEL_PLL6;
185297627Sjmcneill		ophase = 0;
186297627Sjmcneill		phase = 5;
187297627Sjmcneill		n = 2;
188309756Smanu	} else if (*fout <= 52000000) {
189297627Sjmcneill		parent_idx = CLK_SRC_SEL_PLL6;
190297627Sjmcneill		ophase = 3;
191297627Sjmcneill		phase = 5;
192297627Sjmcneill		n = 0;
193297627Sjmcneill	} else
194297627Sjmcneill		return (ERANGE);
195297627Sjmcneill
196297627Sjmcneill	/* Switch parent clock, if necessary */
197297627Sjmcneill	if (parent_idx != clknode_get_parent_idx(clk)) {
198297627Sjmcneill		error = clknode_set_parent_by_idx(clk, parent_idx);
199297627Sjmcneill		if (error != 0)
200297627Sjmcneill			return (error);
201297627Sjmcneill
202297627Sjmcneill		/* Fetch new input frequency */
203297627Sjmcneill		error = clknode_get_freq(clknode_get_parent(clk), &fin);
204297627Sjmcneill		if (error != 0)
205297627Sjmcneill			return (error);
206297627Sjmcneill	}
207297627Sjmcneill
208297627Sjmcneill	m = ((fin / (1 << n)) / *fout) - 1;
209297627Sjmcneill
210297627Sjmcneill	DEVICE_LOCK(sc);
211297627Sjmcneill	MODCLK_READ(sc, &val);
212297627Sjmcneill	val &= ~(CLK_RATIO_N | CLK_RATIO_M | CLK_PHASE_CTR |
213297627Sjmcneill	    OUTPUT_CLK_PHASE_CTR);
214297627Sjmcneill	val |= (n << CLK_RATIO_N_SHIFT);
215297627Sjmcneill	val |= (m << CLK_RATIO_M_SHIFT);
216297627Sjmcneill	val |= (phase << CLK_PHASE_CTR_SHIFT);
217297627Sjmcneill	val |= (ophase << OUTPUT_CLK_PHASE_CTR_SHIFT);
218297627Sjmcneill	MODCLK_WRITE(sc, val);
219297627Sjmcneill	DEVICE_UNLOCK(sc);
220297627Sjmcneill
221297627Sjmcneill	*fout = fin / (1 << n) / (m + 1);
222297627Sjmcneill	*stop = 1;
223297627Sjmcneill
224297627Sjmcneill	return (0);
225297627Sjmcneill}
226297627Sjmcneill
227297627Sjmcneillstatic clknode_method_t aw_mmcclk_clknode_methods[] = {
228297627Sjmcneill	/* Device interface */
229297627Sjmcneill	CLKNODEMETHOD(clknode_init,		aw_mmcclk_init),
230297627Sjmcneill	CLKNODEMETHOD(clknode_set_gate,		aw_mmcclk_set_gate),
231297627Sjmcneill	CLKNODEMETHOD(clknode_set_mux,		aw_mmcclk_set_mux),
232297627Sjmcneill	CLKNODEMETHOD(clknode_recalc_freq,	aw_mmcclk_recalc_freq),
233297627Sjmcneill	CLKNODEMETHOD(clknode_set_freq,		aw_mmcclk_set_freq),
234297627Sjmcneill	CLKNODEMETHOD_END
235297627Sjmcneill};
236297627SjmcneillDEFINE_CLASS_1(aw_mmcclk_clknode, aw_mmcclk_clknode_class,
237297627Sjmcneill    aw_mmcclk_clknode_methods, sizeof(struct aw_mmcclk_sc), clknode_class);
238297627Sjmcneill
239297627Sjmcneillstatic int
240297627Sjmcneillaw_mmcclk_probe(device_t dev)
241297627Sjmcneill{
242297627Sjmcneill	if (!ofw_bus_status_okay(dev))
243297627Sjmcneill		return (ENXIO);
244297627Sjmcneill
245297627Sjmcneill	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
246297627Sjmcneill		return (ENXIO);
247297627Sjmcneill
248297627Sjmcneill	device_set_desc(dev, "Allwinner MMC Clock");
249297627Sjmcneill	return (BUS_PROBE_DEFAULT);
250297627Sjmcneill}
251297627Sjmcneill
252297627Sjmcneillstatic int
253297627Sjmcneillaw_mmcclk_attach(device_t dev)
254297627Sjmcneill{
255297627Sjmcneill	struct clknode_init_def def;
256297627Sjmcneill	struct aw_mmcclk_sc *sc;
257297627Sjmcneill	struct clkdom *clkdom;
258297627Sjmcneill	struct clknode *clk;
259297627Sjmcneill	const char **names;
260297627Sjmcneill	uint32_t *indices;
261297627Sjmcneill	clk_t clk_parent;
262297627Sjmcneill	bus_addr_t paddr;
263297627Sjmcneill	bus_size_t psize;
264297627Sjmcneill	phandle_t node;
265297627Sjmcneill	int error, nout, ncells, i;
266297627Sjmcneill
267297627Sjmcneill	node = ofw_bus_get_node(dev);
268297627Sjmcneill
269297627Sjmcneill	if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
270297627Sjmcneill		device_printf(dev, "cannot parse 'reg' property\n");
271297627Sjmcneill		return (ENXIO);
272297627Sjmcneill	}
273297627Sjmcneill
274297627Sjmcneill	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
275297627Sjmcneill	    "#clock-cells", &ncells);
276297627Sjmcneill	if (error != 0 || ncells == 0) {
277297627Sjmcneill		device_printf(dev, "couldn't find parent clocks\n");
278297627Sjmcneill		return (ENXIO);
279297627Sjmcneill	}
280297627Sjmcneill
281297627Sjmcneill	clkdom = clkdom_create(dev);
282297627Sjmcneill
283297627Sjmcneill	nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
284297627Sjmcneill	if (nout == 0) {
285297627Sjmcneill		device_printf(dev, "no output clocks found\n");
286297627Sjmcneill		error = ENXIO;
287297627Sjmcneill		goto fail;
288297627Sjmcneill	}
289297627Sjmcneill
290297627Sjmcneill	memset(&def, 0, sizeof(def));
291297627Sjmcneill	def.name = names[0];
292297627Sjmcneill	def.id = 0;
293297627Sjmcneill	def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
294297627Sjmcneill	for (i = 0; i < ncells; i++) {
295308324Smmel		error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
296297627Sjmcneill		if (error != 0) {
297297627Sjmcneill			device_printf(dev, "cannot get clock %d\n", i);
298297627Sjmcneill			goto fail;
299297627Sjmcneill		}
300297627Sjmcneill		def.parent_names[i] = clk_get_name(clk_parent);
301297627Sjmcneill		clk_release(clk_parent);
302297627Sjmcneill	}
303297627Sjmcneill	def.parent_cnt = ncells;
304297627Sjmcneill	def.flags = CLK_NODE_GLITCH_FREE;
305297627Sjmcneill
306297627Sjmcneill	clk = clknode_create(clkdom, &aw_mmcclk_clknode_class, &def);
307297627Sjmcneill	if (clk == NULL) {
308297627Sjmcneill		device_printf(dev, "cannot create clknode\n");
309297627Sjmcneill		error = ENXIO;
310297627Sjmcneill		goto fail;
311297627Sjmcneill	}
312297627Sjmcneill
313297627Sjmcneill	sc = clknode_get_softc(clk);
314297627Sjmcneill	sc->reg = paddr;
315297627Sjmcneill	sc->clkdev = device_get_parent(dev);
316297627Sjmcneill
317297627Sjmcneill	clknode_register(clkdom, clk);
318297627Sjmcneill
319297627Sjmcneill	if (clkdom_finit(clkdom) != 0) {
320297627Sjmcneill		device_printf(dev, "cannot finalize clkdom initialization\n");
321297627Sjmcneill		error = ENXIO;
322297627Sjmcneill		goto fail;
323297627Sjmcneill	}
324297627Sjmcneill
325297627Sjmcneill	if (bootverbose)
326297627Sjmcneill		clkdom_dump(clkdom);
327297627Sjmcneill
328297627Sjmcneill	return (0);
329297627Sjmcneill
330297627Sjmcneillfail:
331297627Sjmcneill	return (error);
332297627Sjmcneill}
333297627Sjmcneill
334297627Sjmcneillstatic device_method_t aw_mmcclk_methods[] = {
335297627Sjmcneill	/* Device interface */
336297627Sjmcneill	DEVMETHOD(device_probe,		aw_mmcclk_probe),
337297627Sjmcneill	DEVMETHOD(device_attach,	aw_mmcclk_attach),
338297627Sjmcneill
339297627Sjmcneill	DEVMETHOD_END
340297627Sjmcneill};
341297627Sjmcneill
342297627Sjmcneillstatic driver_t aw_mmcclk_driver = {
343297627Sjmcneill	"aw_mmcclk",
344297627Sjmcneill	aw_mmcclk_methods,
345297627Sjmcneill	0
346297627Sjmcneill};
347297627Sjmcneill
348297627Sjmcneillstatic devclass_t aw_mmcclk_devclass;
349297627Sjmcneill
350297627SjmcneillEARLY_DRIVER_MODULE(aw_mmcclk, simplebus, aw_mmcclk_driver,
351297627Sjmcneill    aw_mmcclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
352