aw_ahbclk.c revision 308324
1/*-
2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 308324 2016-11-05 04:17:32Z mmel $
27 */
28
29/*
30 * Allwinner AHB clock
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 308324 2016-11-05 04:17:32Z mmel $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/bus.h>
39#include <sys/rman.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <machine/bus.h>
43
44#include <dev/ofw/ofw_bus.h>
45#include <dev/ofw/ofw_bus_subr.h>
46#include <dev/ofw/ofw_subr.h>
47
48#include <dev/extres/clk/clk.h>
49
50#include "clkdev_if.h"
51
52#define	A10_AHB_CLK_DIV_RATIO		(0x3 << 4)
53#define	A10_AHB_CLK_DIV_RATIO_SHIFT	4
54
55#define	A13_AHB_CLK_SRC_SEL		(0x3 << 6)
56#define	A13_AHB_CLK_SRC_SEL_MAX		3
57#define	A13_AHB_CLK_SRC_SEL_SHIFT	6
58
59#define	A31_AHB1_PRE_DIV		(0x3 << 6)
60#define	A31_AHB1_PRE_DIV_SHIFT		6
61#define	A31_AHB1_CLK_SRC_SEL		(0x3 << 12)
62#define	A31_AHB1_CLK_SRC_SEL_PLL6	3
63#define	A31_AHB1_CLK_SRC_SEL_MAX	3
64#define	A31_AHB1_CLK_SRC_SEL_SHIFT	12
65
66#define	A83T_AHB1_CLK_SRC_SEL		(0x3 << 12)
67#define	A83T_AHB1_CLK_SRC_SEL_ISPLL(x)	((x) & 0x2)
68#define	A83T_AHB1_CLK_SRC_SEL_MAX	3
69#define	A83T_AHB1_CLK_SRC_SEL_SHIFT	12
70#define	A83T_AHB1_PRE_DIV		(0x3 << 6)
71#define	A83T_AHB1_PRE_DIV_SHIFT		6
72#define	A83T_AHB1_CLK_DIV_RATIO		(0x3 << 4)
73#define	A83T_AHB1_CLK_DIV_RATIO_SHIFT	4
74
75#define	H3_AHB2_CLK_CFG			(0x3 << 0)
76#define	H3_AHB2_CLK_CFG_SHIFT		0
77#define	H3_AHB2_CLK_CFG_AHB1		0
78#define	H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2	1
79#define	H3_AHB2_CLK_CFG_MAX		1
80
81enum aw_ahbclk_type {
82	AW_A10_AHB = 1,
83	AW_A13_AHB,
84	AW_A31_AHB1,
85	AW_A83T_AHB1,
86	AW_H3_AHB2,
87};
88
89static struct ofw_compat_data compat_data[] = {
90	{ "allwinner,sun4i-a10-ahb-clk",	AW_A10_AHB },
91	{ "allwinner,sun5i-a13-ahb-clk",	AW_A13_AHB },
92	{ "allwinner,sun6i-a31-ahb1-clk",	AW_A31_AHB1 },
93	{ "allwinner,sun8i-a83t-ahb1-clk",	AW_A83T_AHB1 },
94	{ "allwinner,sun8i-h3-ahb2-clk",	AW_H3_AHB2 },
95	{ NULL, 0 }
96};
97
98struct aw_ahbclk_sc {
99	device_t		clkdev;
100	bus_addr_t		reg;
101	enum aw_ahbclk_type	type;
102};
103
104#define	AHBCLK_READ(sc, val)	CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
105#define	AHBCLK_WRITE(sc, val)	CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
106#define	DEVICE_LOCK(sc)		CLKDEV_DEVICE_LOCK((sc)->clkdev)
107#define	DEVICE_UNLOCK(sc)	CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
108
109static int
110aw_ahbclk_init(struct clknode *clk, device_t dev)
111{
112	struct aw_ahbclk_sc *sc;
113	uint32_t val, index;
114
115	sc = clknode_get_softc(clk);
116
117	switch (sc->type) {
118	case AW_A10_AHB:
119		index = 0;
120		break;
121	case AW_A13_AHB:
122		DEVICE_LOCK(sc);
123		AHBCLK_READ(sc, &val);
124		DEVICE_UNLOCK(sc);
125		index = (val & A13_AHB_CLK_SRC_SEL) >>
126		    A13_AHB_CLK_SRC_SEL_SHIFT;
127		break;
128	case AW_A31_AHB1:
129		DEVICE_LOCK(sc);
130		AHBCLK_READ(sc, &val);
131		DEVICE_UNLOCK(sc);
132		index = (val & A31_AHB1_CLK_SRC_SEL) >>
133		    A31_AHB1_CLK_SRC_SEL_SHIFT;
134		break;
135	case AW_A83T_AHB1:
136		DEVICE_LOCK(sc);
137		AHBCLK_READ(sc, &val);
138		DEVICE_UNLOCK(sc);
139		index = (val & A83T_AHB1_CLK_SRC_SEL) >>
140		    A83T_AHB1_CLK_SRC_SEL_SHIFT;
141		break;
142	case AW_H3_AHB2:
143		DEVICE_LOCK(sc);
144		AHBCLK_READ(sc, &val);
145		DEVICE_UNLOCK(sc);
146		index = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT;
147		break;
148	default:
149		return (ENXIO);
150	}
151
152	clknode_init_parent_idx(clk, index);
153	return (0);
154}
155
156static int
157aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
158{
159	struct aw_ahbclk_sc *sc;
160	uint32_t val, src_sel, div, pre_div;
161
162	sc = clknode_get_softc(clk);
163
164	DEVICE_LOCK(sc);
165	AHBCLK_READ(sc, &val);
166	DEVICE_UNLOCK(sc);
167
168	switch (sc->type) {
169	case AW_A31_AHB1:
170		div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
171		    A10_AHB_CLK_DIV_RATIO_SHIFT);
172		src_sel = (val & A31_AHB1_CLK_SRC_SEL) >>
173		    A31_AHB1_CLK_SRC_SEL_SHIFT;
174		if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6)
175			pre_div = ((val & A31_AHB1_PRE_DIV) >>
176			    A31_AHB1_PRE_DIV_SHIFT) + 1;
177		else
178			pre_div = 1;
179		break;
180	case AW_A83T_AHB1:
181		div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >>
182		    A83T_AHB1_CLK_DIV_RATIO_SHIFT);
183		src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >>
184		    A83T_AHB1_CLK_SRC_SEL_SHIFT;
185		if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel))
186			pre_div = ((val & A83T_AHB1_PRE_DIV) >>
187			    A83T_AHB1_PRE_DIV_SHIFT) + 1;
188		else
189			pre_div = 1;
190		break;
191	case AW_H3_AHB2:
192		src_sel = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT;
193		if (src_sel == H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2)
194			div = 2;
195		else
196			div = 1;
197		pre_div = 1;
198		break;
199	default:
200		div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
201		    A10_AHB_CLK_DIV_RATIO_SHIFT);
202		pre_div = 1;
203		break;
204	}
205
206	*freq = *freq / pre_div / div;
207
208	return (0);
209}
210
211static int
212aw_ahbclk_set_mux(struct clknode *clk, int index)
213{
214	struct aw_ahbclk_sc *sc;
215	uint32_t val;
216
217	sc = clknode_get_softc(clk);
218
219	switch (sc->type) {
220	case AW_A10_AHB:
221		if (index != 0)
222			return (ERANGE);
223		break;
224	case AW_A13_AHB:
225		if (index < 0 || index > A13_AHB_CLK_SRC_SEL_MAX)
226			return (ERANGE);
227		DEVICE_LOCK(sc);
228		AHBCLK_READ(sc, &val);
229		val &= ~A13_AHB_CLK_SRC_SEL;
230		val |= (index << A13_AHB_CLK_SRC_SEL_SHIFT);
231		AHBCLK_WRITE(sc, val);
232		DEVICE_UNLOCK(sc);
233		break;
234	case AW_A83T_AHB1:
235		if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX)
236			return (ERANGE);
237		DEVICE_LOCK(sc);
238		AHBCLK_READ(sc, &val);
239		val &= ~A83T_AHB1_CLK_SRC_SEL;
240		val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT);
241		AHBCLK_WRITE(sc, val);
242		DEVICE_UNLOCK(sc);
243		break;
244	case AW_H3_AHB2:
245		if (index < 0 || index > H3_AHB2_CLK_CFG)
246			return (ERANGE);
247		DEVICE_LOCK(sc);
248		AHBCLK_READ(sc, &val);
249		val &= ~H3_AHB2_CLK_CFG;
250		val |= (index << H3_AHB2_CLK_CFG_SHIFT);
251		AHBCLK_WRITE(sc, val);
252		DEVICE_UNLOCK(sc);
253		break;
254	default:
255		return (ENXIO);
256	}
257
258	return (0);
259}
260
261static clknode_method_t aw_ahbclk_clknode_methods[] = {
262	/* Device interface */
263	CLKNODEMETHOD(clknode_init,		aw_ahbclk_init),
264	CLKNODEMETHOD(clknode_recalc_freq,	aw_ahbclk_recalc_freq),
265	CLKNODEMETHOD(clknode_set_mux,		aw_ahbclk_set_mux),
266	CLKNODEMETHOD_END
267};
268DEFINE_CLASS_1(aw_ahbclk_clknode, aw_ahbclk_clknode_class,
269    aw_ahbclk_clknode_methods, sizeof(struct aw_ahbclk_sc), clknode_class);
270
271static int
272aw_ahbclk_probe(device_t dev)
273{
274	if (!ofw_bus_status_okay(dev))
275		return (ENXIO);
276
277	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
278		return (ENXIO);
279
280	device_set_desc(dev, "Allwinner AHB Clock");
281	return (BUS_PROBE_DEFAULT);
282}
283
284static int
285aw_ahbclk_attach(device_t dev)
286{
287	struct clknode_init_def def;
288	struct aw_ahbclk_sc *sc;
289	struct clkdom *clkdom;
290	struct clknode *clk;
291	clk_t clk_parent;
292	bus_addr_t paddr;
293	bus_size_t psize;
294	phandle_t node;
295	int error, ncells, i;
296
297	node = ofw_bus_get_node(dev);
298
299	if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
300		device_printf(dev, "cannot parse 'reg' property\n");
301		return (ENXIO);
302	}
303
304	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
305	    "#clock-cells", &ncells);
306	if (error != 0) {
307		device_printf(dev, "cannot get clock count\n");
308		return (error);
309	}
310
311	clkdom = clkdom_create(dev);
312
313	memset(&def, 0, sizeof(def));
314	def.id = 1;
315	def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP,
316	    M_WAITOK);
317	for (i = 0; i < ncells; i++) {
318		error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
319		if (error != 0) {
320			device_printf(dev, "cannot get clock %d\n", i);
321			goto fail;
322		}
323		def.parent_names[i] = clk_get_name(clk_parent);
324		clk_release(clk_parent);
325	}
326	def.parent_cnt = ncells;
327
328	error = clk_parse_ofw_clk_name(dev, node, &def.name);
329	if (error != 0) {
330		device_printf(dev, "cannot parse clock name\n");
331		error = ENXIO;
332		goto fail;
333	}
334
335	clk = clknode_create(clkdom, &aw_ahbclk_clknode_class, &def);
336	if (clk == NULL) {
337		device_printf(dev, "cannot create clknode\n");
338		error = ENXIO;
339		goto fail;
340	}
341	sc = clknode_get_softc(clk);
342	sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
343	sc->reg = paddr;
344	sc->clkdev = device_get_parent(dev);
345
346	clknode_register(clkdom, clk);
347
348	if (clkdom_finit(clkdom) != 0) {
349		device_printf(dev, "cannot finalize clkdom initialization\n");
350		error = ENXIO;
351		goto fail;
352	}
353
354	if (bootverbose)
355		clkdom_dump(clkdom);
356
357	return (0);
358
359fail:
360	return (error);
361}
362
363static device_method_t aw_ahbclk_methods[] = {
364	/* Device interface */
365	DEVMETHOD(device_probe,		aw_ahbclk_probe),
366	DEVMETHOD(device_attach,	aw_ahbclk_attach),
367
368	DEVMETHOD_END
369};
370
371static driver_t aw_ahbclk_driver = {
372	"aw_ahbclk",
373	aw_ahbclk_methods,
374	0
375};
376
377static devclass_t aw_ahbclk_devclass;
378
379EARLY_DRIVER_MODULE(aw_ahbclk, simplebus, aw_ahbclk_driver,
380    aw_ahbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
381