aw_apbclk.c revision 297627
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: head/sys/arm/allwinner/clk/aw_apbclk.c 297627 2016-04-06 23:11:03Z jmcneill $
27297627Sjmcneill */
28297627Sjmcneill
29297627Sjmcneill/*
30297627Sjmcneill * Allwinner APB clock
31297627Sjmcneill */
32297627Sjmcneill
33297627Sjmcneill#include <sys/cdefs.h>
34297627Sjmcneill__FBSDID("$FreeBSD: head/sys/arm/allwinner/clk/aw_apbclk.c 297627 2016-04-06 23:11:03Z jmcneill $");
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
50297627Sjmcneill#include "clkdev_if.h"
51297627Sjmcneill
52297627Sjmcneill#define	APB0_CLK_RATIO		(0x3 << 8)
53297627Sjmcneill#define	APB0_CLK_RATIO_SHIFT	8
54297627Sjmcneill#define	APB1_CLK_SRC_SEL	(0x3 << 24)
55297627Sjmcneill#define	APB1_CLK_SRC_SEL_SHIFT	24
56297627Sjmcneill#define	APB1_CLK_SRC_SEL_MAX	0x3
57297627Sjmcneill#define	APB1_CLK_RAT_N		(0x3 << 16)
58297627Sjmcneill#define	APB1_CLK_RAT_N_SHIFT	16
59297627Sjmcneill#define	APB1_CLK_RAT_M		(0x1f << 0)
60297627Sjmcneill#define	APB1_CLK_RAT_M_SHIFT	0
61297627Sjmcneill
62297627Sjmcneillenum aw_apbclk_type {
63297627Sjmcneill	AW_A10_APB0 = 1,
64297627Sjmcneill	AW_A10_APB1,
65297627Sjmcneill};
66297627Sjmcneill
67297627Sjmcneillstatic struct ofw_compat_data compat_data[] = {
68297627Sjmcneill	{ "allwinner,sun4i-a10-apb0-clk",	AW_A10_APB0 },
69297627Sjmcneill	{ "allwinner,sun4i-a10-apb1-clk",	AW_A10_APB1 },
70297627Sjmcneill	{ NULL, 0 }
71297627Sjmcneill};
72297627Sjmcneill
73297627Sjmcneillstruct aw_apbclk_sc {
74297627Sjmcneill	device_t		clkdev;
75297627Sjmcneill	bus_addr_t		reg;
76297627Sjmcneill	enum aw_apbclk_type	type;
77297627Sjmcneill};
78297627Sjmcneill
79297627Sjmcneill#define	APBCLK_READ(sc, val)	CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
80297627Sjmcneill#define	APBCLK_WRITE(sc, val)	CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
81297627Sjmcneill#define	DEVICE_LOCK(sc)		CLKDEV_DEVICE_LOCK((sc)->clkdev)
82297627Sjmcneill#define	DEVICE_UNLOCK(sc)	CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
83297627Sjmcneill
84297627Sjmcneillstatic int
85297627Sjmcneillaw_apbclk_init(struct clknode *clk, device_t dev)
86297627Sjmcneill{
87297627Sjmcneill	struct aw_apbclk_sc *sc;
88297627Sjmcneill	uint32_t val, index;
89297627Sjmcneill
90297627Sjmcneill	sc = clknode_get_softc(clk);
91297627Sjmcneill
92297627Sjmcneill	switch (sc->type) {
93297627Sjmcneill	case AW_A10_APB0:
94297627Sjmcneill		index = 0;
95297627Sjmcneill		break;
96297627Sjmcneill	case AW_A10_APB1:
97297627Sjmcneill		DEVICE_LOCK(sc);
98297627Sjmcneill		APBCLK_READ(sc, &val);
99297627Sjmcneill		DEVICE_UNLOCK(sc);
100297627Sjmcneill		index = (val & APB1_CLK_SRC_SEL) >> APB1_CLK_SRC_SEL_SHIFT;
101297627Sjmcneill		break;
102297627Sjmcneill	default:
103297627Sjmcneill		return (ENXIO);
104297627Sjmcneill	}
105297627Sjmcneill
106297627Sjmcneill	clknode_init_parent_idx(clk, index);
107297627Sjmcneill	return (0);
108297627Sjmcneill}
109297627Sjmcneill
110297627Sjmcneillstatic int
111297627Sjmcneillaw_apbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
112297627Sjmcneill{
113297627Sjmcneill	struct aw_apbclk_sc *sc;
114297627Sjmcneill	uint32_t val, div, m, n;
115297627Sjmcneill
116297627Sjmcneill	sc = clknode_get_softc(clk);
117297627Sjmcneill
118297627Sjmcneill	DEVICE_LOCK(sc);
119297627Sjmcneill	APBCLK_READ(sc, &val);
120297627Sjmcneill	DEVICE_UNLOCK(sc);
121297627Sjmcneill
122297627Sjmcneill	switch (sc->type) {
123297627Sjmcneill	case AW_A10_APB0:
124297627Sjmcneill		div = 1 << ((val & APB0_CLK_RATIO) >> APB0_CLK_RATIO_SHIFT);
125297627Sjmcneill		if (div == 1)
126297627Sjmcneill			div = 2;
127297627Sjmcneill		*freq = *freq / div;
128297627Sjmcneill		break;
129297627Sjmcneill	case AW_A10_APB1:
130297627Sjmcneill		n = 1 << ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_N_SHIFT);
131297627Sjmcneill		m = ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_M_SHIFT) + 1;
132297627Sjmcneill		*freq = *freq / n / m;
133297627Sjmcneill		break;
134297627Sjmcneill	default:
135297627Sjmcneill		return (ENXIO);
136297627Sjmcneill	}
137297627Sjmcneill
138297627Sjmcneill	return (0);
139297627Sjmcneill}
140297627Sjmcneill
141297627Sjmcneillstatic int
142297627Sjmcneillaw_apbclk_set_mux(struct clknode *clk, int index)
143297627Sjmcneill{
144297627Sjmcneill	struct aw_apbclk_sc *sc;
145297627Sjmcneill	uint32_t val;
146297627Sjmcneill
147297627Sjmcneill	sc = clknode_get_softc(clk);
148297627Sjmcneill
149297627Sjmcneill	if (sc->type != AW_A10_APB1)
150297627Sjmcneill		return (ENXIO);
151297627Sjmcneill
152297627Sjmcneill	if (index < 0 || index > APB1_CLK_SRC_SEL_MAX)
153297627Sjmcneill		return (ERANGE);
154297627Sjmcneill
155297627Sjmcneill	DEVICE_LOCK(sc);
156297627Sjmcneill	APBCLK_READ(sc, &val);
157297627Sjmcneill	val &= ~APB1_CLK_SRC_SEL;
158297627Sjmcneill	val |= (index << APB1_CLK_SRC_SEL_SHIFT);
159297627Sjmcneill	APBCLK_WRITE(sc, val);
160297627Sjmcneill	DEVICE_UNLOCK(sc);
161297627Sjmcneill
162297627Sjmcneill	return (0);
163297627Sjmcneill}
164297627Sjmcneill
165297627Sjmcneillstatic clknode_method_t aw_apbclk_clknode_methods[] = {
166297627Sjmcneill	/* Device interface */
167297627Sjmcneill	CLKNODEMETHOD(clknode_init,		aw_apbclk_init),
168297627Sjmcneill	CLKNODEMETHOD(clknode_recalc_freq,	aw_apbclk_recalc_freq),
169297627Sjmcneill	CLKNODEMETHOD(clknode_set_mux,		aw_apbclk_set_mux),
170297627Sjmcneill	CLKNODEMETHOD_END
171297627Sjmcneill};
172297627SjmcneillDEFINE_CLASS_1(aw_apbclk_clknode, aw_apbclk_clknode_class,
173297627Sjmcneill    aw_apbclk_clknode_methods, sizeof(struct aw_apbclk_sc), clknode_class);
174297627Sjmcneill
175297627Sjmcneillstatic int
176297627Sjmcneillaw_apbclk_probe(device_t dev)
177297627Sjmcneill{
178297627Sjmcneill	if (!ofw_bus_status_okay(dev))
179297627Sjmcneill		return (ENXIO);
180297627Sjmcneill
181297627Sjmcneill	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
182297627Sjmcneill		return (ENXIO);
183297627Sjmcneill
184297627Sjmcneill	device_set_desc(dev, "Allwinner APB Clock");
185297627Sjmcneill	return (BUS_PROBE_DEFAULT);
186297627Sjmcneill}
187297627Sjmcneill
188297627Sjmcneillstatic int
189297627Sjmcneillaw_apbclk_attach(device_t dev)
190297627Sjmcneill{
191297627Sjmcneill	struct clknode_init_def def;
192297627Sjmcneill	struct aw_apbclk_sc *sc;
193297627Sjmcneill	struct clkdom *clkdom;
194297627Sjmcneill	struct clknode *clk;
195297627Sjmcneill	clk_t clk_parent;
196297627Sjmcneill	bus_addr_t paddr;
197297627Sjmcneill	bus_size_t psize;
198297627Sjmcneill	phandle_t node;
199297627Sjmcneill	int error, ncells, i;
200297627Sjmcneill
201297627Sjmcneill	node = ofw_bus_get_node(dev);
202297627Sjmcneill
203297627Sjmcneill	if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
204297627Sjmcneill		device_printf(dev, "cannot parse 'reg' property\n");
205297627Sjmcneill		return (ENXIO);
206297627Sjmcneill	}
207297627Sjmcneill
208297627Sjmcneill	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
209297627Sjmcneill	    "#clock-cells", &ncells);
210297627Sjmcneill	if (error != 0) {
211297627Sjmcneill		device_printf(dev, "cannot get clock count\n");
212297627Sjmcneill		return (error);
213297627Sjmcneill	}
214297627Sjmcneill
215297627Sjmcneill	clkdom = clkdom_create(dev);
216297627Sjmcneill
217297627Sjmcneill	memset(&def, 0, sizeof(def));
218297627Sjmcneill	error = clk_parse_ofw_clk_name(dev, node, &def.name);
219297627Sjmcneill	if (error != 0) {
220297627Sjmcneill		device_printf(dev, "cannot parse clock name\n");
221297627Sjmcneill		error = ENXIO;
222297627Sjmcneill		goto fail;
223297627Sjmcneill	}
224297627Sjmcneill	def.id = 1;
225297627Sjmcneill	def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
226297627Sjmcneill	for (i = 0; i < ncells; i++) {
227297627Sjmcneill		error = clk_get_by_ofw_index(dev, i, &clk_parent);
228297627Sjmcneill		if (error != 0) {
229297627Sjmcneill			device_printf(dev, "cannot get clock %d\n", i);
230297627Sjmcneill			goto fail;
231297627Sjmcneill		}
232297627Sjmcneill		def.parent_names[i] = clk_get_name(clk_parent);
233297627Sjmcneill		clk_release(clk_parent);
234297627Sjmcneill	}
235297627Sjmcneill	def.parent_cnt = ncells;
236297627Sjmcneill
237297627Sjmcneill	clk = clknode_create(clkdom, &aw_apbclk_clknode_class, &def);
238297627Sjmcneill	if (clk == NULL) {
239297627Sjmcneill		device_printf(dev, "cannot create clknode\n");
240297627Sjmcneill		error = ENXIO;
241297627Sjmcneill		goto fail;
242297627Sjmcneill	}
243297627Sjmcneill
244297627Sjmcneill	sc = clknode_get_softc(clk);
245297627Sjmcneill	sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
246297627Sjmcneill	sc->reg = paddr;
247297627Sjmcneill	sc->clkdev = device_get_parent(dev);
248297627Sjmcneill
249297627Sjmcneill	clknode_register(clkdom, clk);
250297627Sjmcneill
251297627Sjmcneill	if (clkdom_finit(clkdom) != 0) {
252297627Sjmcneill		device_printf(dev, "cannot finalize clkdom initialization\n");
253297627Sjmcneill		error = ENXIO;
254297627Sjmcneill		goto fail;
255297627Sjmcneill	}
256297627Sjmcneill
257297627Sjmcneill	if (bootverbose)
258297627Sjmcneill		clkdom_dump(clkdom);
259297627Sjmcneill
260297627Sjmcneill	return (0);
261297627Sjmcneill
262297627Sjmcneillfail:
263297627Sjmcneill	return (error);
264297627Sjmcneill}
265297627Sjmcneill
266297627Sjmcneillstatic device_method_t aw_apbclk_methods[] = {
267297627Sjmcneill	/* Device interface */
268297627Sjmcneill	DEVMETHOD(device_probe,		aw_apbclk_probe),
269297627Sjmcneill	DEVMETHOD(device_attach,	aw_apbclk_attach),
270297627Sjmcneill
271297627Sjmcneill	DEVMETHOD_END
272297627Sjmcneill};
273297627Sjmcneill
274297627Sjmcneillstatic driver_t aw_apbclk_driver = {
275297627Sjmcneill	"aw_apbclk",
276297627Sjmcneill	aw_apbclk_methods,
277297627Sjmcneill	0
278297627Sjmcneill};
279297627Sjmcneill
280297627Sjmcneillstatic devclass_t aw_apbclk_devclass;
281297627Sjmcneill
282297627SjmcneillEARLY_DRIVER_MODULE(aw_apbclk, simplebus, aw_apbclk_driver,
283297627Sjmcneill    aw_apbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
284