aw_ahbclk.c revision 309771
11638Srgrimes/*-
21638Srgrimes * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
31638Srgrimes * All rights reserved.
41638Srgrimes *
51638Srgrimes * Redistribution and use in source and binary forms, with or without
61638Srgrimes * modification, are permitted provided that the following conditions
71638Srgrimes * are met:
81638Srgrimes * 1. Redistributions of source code must retain the above copyright
91638Srgrimes *    notice, this list of conditions and the following disclaimer.
101638Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111638Srgrimes *    notice, this list of conditions and the following disclaimer in the
12263142Seadler *    documentation and/or other materials provided with the distribution.
131638Srgrimes *
141638Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
151638Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
161638Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171638Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
181638Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
191638Srgrimes * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
201638Srgrimes * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
211638Srgrimes * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
221638Srgrimes * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231638Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241638Srgrimes * SUCH DAMAGE.
251638Srgrimes *
261638Srgrimes * $FreeBSD: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 309771 2016-12-09 21:17:40Z manu $
271638Srgrimes */
281638Srgrimes
291638Srgrimes/*
301638Srgrimes * Allwinner AHB clock
311638Srgrimes */
321638Srgrimes
331638Srgrimes#include <sys/cdefs.h>
341638Srgrimes__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 309771 2016-12-09 21:17:40Z manu $");
351638Srgrimes
361638Srgrimes#include <sys/param.h>
371638Srgrimes#include <sys/systm.h>
381638Srgrimes#include <sys/bus.h>
391638Srgrimes#include <sys/rman.h>
401638Srgrimes#include <sys/kernel.h>
411638Srgrimes#include <sys/module.h>
421638Srgrimes#include <machine/bus.h>
431638Srgrimes
441638Srgrimes#include <dev/ofw/ofw_bus.h>
451638Srgrimes#include <dev/ofw/ofw_bus_subr.h>
461638Srgrimes#include <dev/ofw/ofw_subr.h>
471638Srgrimes
481638Srgrimes#include <dev/extres/clk/clk.h>
491638Srgrimes
501638Srgrimes#include "clkdev_if.h"
511638Srgrimes
521638Srgrimes#define	A10_AHB_CLK_DIV_RATIO		(0x3 << 4)
531638Srgrimes#define	A10_AHB_CLK_DIV_RATIO_SHIFT	4
541638Srgrimes
551638Srgrimes#define	A13_AHB_CLK_SRC_SEL		(0x3 << 6)
561638Srgrimes#define	A13_AHB_CLK_SRC_SEL_MAX		3
571638Srgrimes#define	A13_AHB_CLK_SRC_SEL_SHIFT	6
581638Srgrimes
591638Srgrimes#define	A31_AHB1_PRE_DIV		(0x3 << 6)
601638Srgrimes#define	A31_AHB1_PRE_DIV_SHIFT		6
611638Srgrimes#define	A31_AHB1_CLK_SRC_SEL		(0x3 << 12)
621638Srgrimes#define	A31_AHB1_CLK_SRC_SEL_PLL6	3
631638Srgrimes#define	A31_AHB1_CLK_SRC_SEL_MAX	3
641638Srgrimes#define	A31_AHB1_CLK_SRC_SEL_SHIFT	12
651638Srgrimes
661638Srgrimes#define	A83T_AHB1_CLK_SRC_SEL		(0x3 << 12)
671638Srgrimes#define	A83T_AHB1_CLK_SRC_SEL_ISPLL(x)	((x) & 0x2)
681638Srgrimes#define	A83T_AHB1_CLK_SRC_SEL_MAX	3
691638Srgrimes#define	A83T_AHB1_CLK_SRC_SEL_SHIFT	12
701638Srgrimes#define	A83T_AHB1_PRE_DIV		(0x3 << 6)
711638Srgrimes#define	A83T_AHB1_PRE_DIV_SHIFT		6
721638Srgrimes#define	A83T_AHB1_CLK_DIV_RATIO		(0x3 << 4)
731638Srgrimes#define	A83T_AHB1_CLK_DIV_RATIO_SHIFT	4
741638Srgrimes
751638Srgrimes#define	H3_AHB2_CLK_CFG			(0x3 << 0)
761638Srgrimes#define	H3_AHB2_CLK_CFG_SHIFT		0
771638Srgrimes#define	H3_AHB2_CLK_CFG_AHB1		0
781638Srgrimes#define	H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2	1
791638Srgrimes#define	H3_AHB2_CLK_CFG_MAX		1
801638Srgrimes
811638Srgrimesenum aw_ahbclk_type {
821638Srgrimes	AW_A10_AHB = 1,
831638Srgrimes	AW_A13_AHB,
841638Srgrimes	AW_A31_AHB1,
851638Srgrimes	AW_A83T_AHB1,
861638Srgrimes	AW_H3_AHB2,
871638Srgrimes};
881638Srgrimes
891638Srgrimesstatic struct ofw_compat_data compat_data[] = {
901638Srgrimes	{ "allwinner,sun4i-a10-ahb-clk",	AW_A10_AHB },
911638Srgrimes	{ "allwinner,sun5i-a13-ahb-clk",	AW_A13_AHB },
921638Srgrimes	{ "allwinner,sun6i-a31-ahb1-clk",	AW_A31_AHB1 },
931638Srgrimes	{ "allwinner,sun8i-a83t-ahb1-clk",	AW_A83T_AHB1 },
941638Srgrimes	{ "allwinner,sun8i-h3-ahb2-clk",	AW_H3_AHB2 },
951638Srgrimes	{ NULL, 0 }
961638Srgrimes};
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		/* Set source to PLL_PERIPH/2 */
144		index = H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2;
145		DEVICE_LOCK(sc);
146		AHBCLK_READ(sc, &val);
147		val &= ~H3_AHB2_CLK_CFG;
148		val |= (index << H3_AHB2_CLK_CFG_SHIFT);
149		AHBCLK_WRITE(sc, val);
150		DEVICE_UNLOCK(sc);
151		break;
152	default:
153		return (ENXIO);
154	}
155
156	clknode_init_parent_idx(clk, index);
157	return (0);
158}
159
160static int
161aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
162{
163	struct aw_ahbclk_sc *sc;
164	uint32_t val, src_sel, div, pre_div;
165
166	sc = clknode_get_softc(clk);
167
168	DEVICE_LOCK(sc);
169	AHBCLK_READ(sc, &val);
170	DEVICE_UNLOCK(sc);
171
172	switch (sc->type) {
173	case AW_A31_AHB1:
174		div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
175		    A10_AHB_CLK_DIV_RATIO_SHIFT);
176		src_sel = (val & A31_AHB1_CLK_SRC_SEL) >>
177		    A31_AHB1_CLK_SRC_SEL_SHIFT;
178		if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6)
179			pre_div = ((val & A31_AHB1_PRE_DIV) >>
180			    A31_AHB1_PRE_DIV_SHIFT) + 1;
181		else
182			pre_div = 1;
183		break;
184	case AW_A83T_AHB1:
185		div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >>
186		    A83T_AHB1_CLK_DIV_RATIO_SHIFT);
187		src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >>
188		    A83T_AHB1_CLK_SRC_SEL_SHIFT;
189		if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel))
190			pre_div = ((val & A83T_AHB1_PRE_DIV) >>
191			    A83T_AHB1_PRE_DIV_SHIFT) + 1;
192		else
193			pre_div = 1;
194		break;
195	case AW_H3_AHB2:
196		div = pre_div = 1;
197		break;
198	default:
199		div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
200		    A10_AHB_CLK_DIV_RATIO_SHIFT);
201		pre_div = 1;
202		break;
203	}
204
205	*freq = *freq / pre_div / div;
206
207	return (0);
208}
209
210static int
211aw_ahbclk_set_mux(struct clknode *clk, int index)
212{
213	struct aw_ahbclk_sc *sc;
214	uint32_t val;
215
216	sc = clknode_get_softc(clk);
217
218	switch (sc->type) {
219	case AW_A10_AHB:
220		if (index != 0)
221			return (ERANGE);
222		break;
223	case AW_A13_AHB:
224		if (index < 0 || index > A13_AHB_CLK_SRC_SEL_MAX)
225			return (ERANGE);
226		DEVICE_LOCK(sc);
227		AHBCLK_READ(sc, &val);
228		val &= ~A13_AHB_CLK_SRC_SEL;
229		val |= (index << A13_AHB_CLK_SRC_SEL_SHIFT);
230		AHBCLK_WRITE(sc, val);
231		DEVICE_UNLOCK(sc);
232		break;
233	case AW_A83T_AHB1:
234		if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX)
235			return (ERANGE);
236		DEVICE_LOCK(sc);
237		AHBCLK_READ(sc, &val);
238		val &= ~A83T_AHB1_CLK_SRC_SEL;
239		val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT);
240		AHBCLK_WRITE(sc, val);
241		DEVICE_UNLOCK(sc);
242		break;
243	case AW_H3_AHB2:
244		if (index < 0 || index > H3_AHB2_CLK_CFG)
245			return (ERANGE);
246		DEVICE_LOCK(sc);
247		AHBCLK_READ(sc, &val);
248		val &= ~H3_AHB2_CLK_CFG;
249		val |= (index << H3_AHB2_CLK_CFG_SHIFT);
250		AHBCLK_WRITE(sc, val);
251		DEVICE_UNLOCK(sc);
252		break;
253	default:
254		return (ENXIO);
255	}
256
257	return (0);
258}
259
260static clknode_method_t aw_ahbclk_clknode_methods[] = {
261	/* Device interface */
262	CLKNODEMETHOD(clknode_init,		aw_ahbclk_init),
263	CLKNODEMETHOD(clknode_recalc_freq,	aw_ahbclk_recalc_freq),
264	CLKNODEMETHOD(clknode_set_mux,		aw_ahbclk_set_mux),
265	CLKNODEMETHOD_END
266};
267DEFINE_CLASS_1(aw_ahbclk_clknode, aw_ahbclk_clknode_class,
268    aw_ahbclk_clknode_methods, sizeof(struct aw_ahbclk_sc), clknode_class);
269
270static int
271aw_ahbclk_probe(device_t dev)
272{
273	if (!ofw_bus_status_okay(dev))
274		return (ENXIO);
275
276	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
277		return (ENXIO);
278
279	device_set_desc(dev, "Allwinner AHB Clock");
280	return (BUS_PROBE_DEFAULT);
281}
282
283static int
284aw_ahbclk_attach(device_t dev)
285{
286	struct clknode_init_def def;
287	struct aw_ahbclk_sc *sc;
288	struct clkdom *clkdom;
289	struct clknode *clk;
290	clk_t clk_parent;
291	bus_addr_t paddr;
292	bus_size_t psize;
293	phandle_t node;
294	int error, ncells, i;
295
296	node = ofw_bus_get_node(dev);
297
298	if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
299		device_printf(dev, "cannot parse 'reg' property\n");
300		return (ENXIO);
301	}
302
303	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
304	    "#clock-cells", &ncells);
305	if (error != 0) {
306		device_printf(dev, "cannot get clock count\n");
307		return (error);
308	}
309
310	clkdom = clkdom_create(dev);
311
312	memset(&def, 0, sizeof(def));
313	def.id = 1;
314	def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP,
315	    M_WAITOK);
316	for (i = 0; i < ncells; i++) {
317		error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
318		if (error != 0) {
319			device_printf(dev, "cannot get clock %d\n", i);
320			goto fail;
321		}
322		def.parent_names[i] = clk_get_name(clk_parent);
323		clk_release(clk_parent);
324	}
325	def.parent_cnt = ncells;
326
327	error = clk_parse_ofw_clk_name(dev, node, &def.name);
328	if (error != 0) {
329		device_printf(dev, "cannot parse clock name\n");
330		error = ENXIO;
331		goto fail;
332	}
333
334	clk = clknode_create(clkdom, &aw_ahbclk_clknode_class, &def);
335	if (clk == NULL) {
336		device_printf(dev, "cannot create clknode\n");
337		error = ENXIO;
338		goto fail;
339	}
340	sc = clknode_get_softc(clk);
341	sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
342	sc->reg = paddr;
343	sc->clkdev = device_get_parent(dev);
344
345	clknode_register(clkdom, clk);
346
347	if (clkdom_finit(clkdom) != 0) {
348		device_printf(dev, "cannot finalize clkdom initialization\n");
349		error = ENXIO;
350		goto fail;
351	}
352
353	error = clk_set_assigned(dev, node);
354	if (error != 0 && error != ENOENT) {
355		device_printf(dev, "cannot set assigned parents: %d\n", error);
356		goto fail;
357	}
358
359	if (bootverbose)
360		clkdom_dump(clkdom);
361
362	return (0);
363
364fail:
365	return (error);
366}
367
368static device_method_t aw_ahbclk_methods[] = {
369	/* Device interface */
370	DEVMETHOD(device_probe,		aw_ahbclk_probe),
371	DEVMETHOD(device_attach,	aw_ahbclk_attach),
372
373	DEVMETHOD_END
374};
375
376static driver_t aw_ahbclk_driver = {
377	"aw_ahbclk",
378	aw_ahbclk_methods,
379	0
380};
381
382static devclass_t aw_ahbclk_devclass;
383
384EARLY_DRIVER_MODULE(aw_ahbclk, simplebus, aw_ahbclk_driver,
385    aw_ahbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
386