aw_ccu.c revision 299688
1139825Simp/*-
2167755Ssam * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3167755Ssam * All rights reserved.
4167755Ssam *
5167755Ssam * Redistribution and use in source and binary forms, with or without
6167755Ssam * modification, are permitted provided that the following conditions
7167755Ssam * are met:
8167755Ssam * 1. Redistributions of source code must retain the above copyright
9167755Ssam *    notice, this list of conditions and the following disclaimer.
10167755Ssam * 2. Redistributions in binary form must reproduce the above copyright
11167755Ssam *    notice, this list of conditions and the following disclaimer in the
12167755Ssam *    documentation and/or other materials provided with the distribution.
13167755Ssam *
14167755Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15167755Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16167755Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17167755Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18167755Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19167755Ssam * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20167755Ssam * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21167755Ssam * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22167755Ssam * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23167755Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24167755Ssam * SUCH DAMAGE.
25167755Ssam *
26167755Ssam * $FreeBSD: head/sys/arm/allwinner/aw_ccu.c 299688 2016-05-13 18:20:54Z manu $
27167755Ssam */
28167755Ssam
29167755Ssam/*
30167755Ssam * Allwinner oscillator clock
31167755Ssam */
32167755Ssam
33167755Ssam#include <sys/cdefs.h>
34167755Ssam__FBSDID("$FreeBSD: head/sys/arm/allwinner/aw_ccu.c 299688 2016-05-13 18:20:54Z manu $");
35167755Ssam
36167755Ssam#include <sys/param.h>
37104476Ssam#include <sys/systm.h>
38104476Ssam#include <sys/bus.h>
39104476Ssam#include <sys/rman.h>
40104476Ssam#include <sys/kernel.h>
41104476Ssam#include <sys/module.h>
42104476Ssam#include <machine/bus.h>
43104476Ssam
44104476Ssam#include <dev/fdt/simplebus.h>
45104476Ssam#include <dev/fdt/fdt_common.h>
46104476Ssam
47104476Ssam#include <dev/ofw/ofw_bus.h>
48104476Ssam#include <dev/ofw/ofw_bus_subr.h>
49104476Ssam
50104476Ssam#include <dev/extres/clk/clk.h>
51104476Ssam
52104476Ssam#include "clkdev_if.h"
53104476Ssam
54104476Ssam#define	CCU_BASE	0x01c20000
55104476Ssam#define	CCU_SIZE	0x400
56116191Sobrien
57108587Ssam#define	PRCM_BASE	0x01f01400
58104476Ssam#define	PRCM_SIZE	0x200
59167755Ssam
60167755Ssam#define	SYSCTRL_BASE	0x01c00000
61104476Ssam#define	SYSCTRL_SIZE	0x34
62104476Ssam
63104476Ssamstruct aw_ccu_softc {
64104476Ssam	struct simplebus_softc	sc;
65104476Ssam	bus_space_tag_t		bst;
66104476Ssam	bus_space_handle_t	ccu_bsh;
67129880Sphk	bus_space_handle_t	prcm_bsh;
68104476Ssam	bus_space_handle_t	sysctrl_bsh;
69104476Ssam	struct mtx		mtx;
70104476Ssam	int			flags;
71199884Sbz};
72104476Ssam
73104476Ssam#define	CLOCK_CCU	(1 << 0)
74167755Ssam#define	CLOCK_PRCM	(1 << 1)
75167755Ssam#define	CLOCK_SYSCTRL	(1 << 2)
76104476Ssam
77104476Ssamstatic struct ofw_compat_data compat_data[] = {
78104628Ssam	{ "allwinner,sun4i-a10",	CLOCK_CCU },
79104476Ssam	{ "allwinner,sun7i-a20",	CLOCK_CCU },
80167755Ssam	{ "allwinner,sun6i-a31",	CLOCK_CCU },
81167755Ssam	{ "allwinner,sun6i-a31s",	CLOCK_CCU },
82167755Ssam	{ "allwinner,sun8i-a83t",	CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL },
83167755Ssam	{ "allwinner,sun8i-h3",		CLOCK_CCU },
84208834Skib	{ NULL, 0 }
85208834Skib};
86208834Skib
87208834Skibstatic int
88199884Sbzaw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr,
89199884Sbz    bus_space_handle_t *pbsh, bus_size_t *poff)
90104476Ssam{
91104476Ssam	if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) &&
92104476Ssam	    (sc->flags & CLOCK_CCU) != 0) {
93104476Ssam		*poff = addr - CCU_BASE;
94104476Ssam		*pbsh = sc->ccu_bsh;
95104476Ssam		return (0);
96104476Ssam	}
97104476Ssam	if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) &&
98167755Ssam	    (sc->flags & CLOCK_PRCM) != 0) {
99167755Ssam		*poff = addr - PRCM_BASE;
100167755Ssam		*pbsh = sc->prcm_bsh;
101167755Ssam		return (0);
102167755Ssam	}
103167755Ssam	if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) &&
104167755Ssam	    (sc->flags & CLOCK_SYSCTRL) != 0) {
105167755Ssam		*poff = addr - SYSCTRL_BASE;
106167755Ssam		*pbsh = sc->sysctrl_bsh;
107167755Ssam		return (0);
108167755Ssam	}
109167755Ssam	return (EINVAL);
110167755Ssam}
111167755Ssam
112167755Ssamstatic int
113167755Ssamaw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val)
114167755Ssam{
115167755Ssam	struct aw_ccu_softc *sc;
116167755Ssam	bus_space_handle_t bsh;
117167755Ssam	bus_size_t reg;
118167755Ssam
119167755Ssam	sc = device_get_softc(dev);
120167755Ssam
121167755Ssam	if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
122167755Ssam		return (EINVAL);
123167755Ssam
124167755Ssam	mtx_assert(&sc->mtx, MA_OWNED);
125104476Ssam	bus_space_write_4(sc->bst, bsh, reg, val);
126104476Ssam
127104476Ssam	return (0);
128104476Ssam}
129104476Ssam
130104476Ssamstatic int
131104476Ssamaw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
132104476Ssam{
133104476Ssam	struct aw_ccu_softc *sc;
134104476Ssam	bus_space_handle_t bsh;
135158827Spjd	bus_size_t reg;
136104476Ssam
137104476Ssam	sc = device_get_softc(dev);
138104476Ssam
139104476Ssam	if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
140104476Ssam		return (EINVAL);
141104476Ssam
142104476Ssam	mtx_assert(&sc->mtx, MA_OWNED);
143104476Ssam	*val = bus_space_read_4(sc->bst, bsh, reg);
144104476Ssam
145104476Ssam	return (0);
146104476Ssam}
147104476Ssam
148104476Ssamstatic int
149104476Ssamaw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
150104476Ssam{
151104476Ssam	struct aw_ccu_softc *sc;
152104476Ssam	bus_space_handle_t bsh;
153104476Ssam	bus_size_t reg;
154104476Ssam	uint32_t val;
155158826Spjd
156104476Ssam	sc = device_get_softc(dev);
157104476Ssam
158104476Ssam	if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
159104476Ssam		return (EINVAL);
160104476Ssam
161104476Ssam	mtx_assert(&sc->mtx, MA_OWNED);
162104476Ssam	val = bus_space_read_4(sc->bst, bsh, reg);
163104476Ssam	val &= ~clr;
164262994Sjmg	val |= set;
165104476Ssam	bus_space_write_4(sc->bst, bsh, reg, val);
166104476Ssam
167262994Sjmg	return (0);
168104476Ssam}
169104476Ssam
170104476Ssamstatic void
171108588Ssamaw_ccu_device_lock(device_t dev)
172108588Ssam{
173108588Ssam	struct aw_ccu_softc *sc;
174108588Ssam
175108588Ssam	sc = device_get_softc(dev);
176158702Spjd	mtx_lock(&sc->mtx);
177167755Ssam}
178108588Ssam
179108587Ssamstatic void
180108587Ssamaw_ccu_device_unlock(device_t dev)
181108587Ssam{
182108587Ssam	struct aw_ccu_softc *sc;
183108587Ssam
184108587Ssam	sc = device_get_softc(dev);
185108587Ssam	mtx_unlock(&sc->mtx);
186108587Ssam}
187108587Ssam
188108587Ssamstatic const struct ofw_compat_data *
189108588Ssamaw_ccu_search_compatible(void)
190104476Ssam{
191104476Ssam	const struct ofw_compat_data *compat;
192108588Ssam	phandle_t root;
193108588Ssam
194115746Ssam	root = OF_finddevice("/");
195115746Ssam	for (compat = compat_data; compat_data->ocd_str != NULL; compat++)
196108588Ssam		if (fdt_is_compatible(root, compat->ocd_str))
197108588Ssam			break;
198108588Ssam
199115746Ssam	return (compat);
200108588Ssam}
201108588Ssam
202108588Ssamstatic int
203115746Ssamaw_ccu_probe(device_t dev)
204108588Ssam{
205104476Ssam	const char *name;
206104476Ssam
207104476Ssam	name = ofw_bus_get_name(dev);
208104476Ssam
209104476Ssam	if (name == NULL || strcmp(name, "clocks") != 0)
210104476Ssam		return (ENXIO);
211108588Ssam
212108588Ssam	if (aw_ccu_search_compatible()->ocd_data == 0)
213108588Ssam		return (ENXIO);
214108588Ssam
215108588Ssam	device_set_desc(dev, "Allwinner Clock Control Unit");
216104476Ssam	return (BUS_PROBE_SPECIFIC);
217104476Ssam}
218104476Ssam
219104476Ssamstatic int
220108588Ssamaw_ccu_attach(device_t dev)
221108588Ssam{
222108588Ssam	struct aw_ccu_softc *sc;
223108588Ssam	phandle_t node, child;
224108588Ssam	device_t cdev;
225104476Ssam	int error;
226172836Sjulian
227108588Ssam	sc = device_get_softc(dev);
228108588Ssam	node = ofw_bus_get_node(dev);
229108588Ssam
230108588Ssam	simplebus_init(dev, node);
231108588Ssam
232108588Ssam	sc->flags = aw_ccu_search_compatible()->ocd_data;
233104476Ssam
234172836Sjulian	/*
235108588Ssam	 * Map registers. The DT doesn't have a "reg" property
236108588Ssam	 * for the /clocks node and child nodes have conflicting "reg"
237108588Ssam	 * properties.
238108588Ssam	 */
239108588Ssam	sc->bst = bus_get_bus_tag(dev);
240108588Ssam	if (sc->flags & CLOCK_CCU) {
241108588Ssam		error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0,
242108588Ssam		    &sc->ccu_bsh);
243108588Ssam		if (error != 0) {
244108588Ssam			device_printf(dev, "couldn't map CCU: %d\n", error);
245104476Ssam			return (error);
246104476Ssam		}
247104476Ssam	}
248108588Ssam	if (sc->flags & CLOCK_PRCM) {
249108588Ssam		error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0,
250108588Ssam		    &sc->prcm_bsh);
251108588Ssam		if (error != 0) {
252108588Ssam			device_printf(dev, "couldn't map PRCM: %d\n", error);
253108588Ssam			return (error);
254108588Ssam		}
255108588Ssam	}
256108588Ssam	if (sc->flags & CLOCK_SYSCTRL) {
257108588Ssam		error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0,
258108588Ssam		    &sc->sysctrl_bsh);
259108588Ssam		if (error != 0) {
260108588Ssam			device_printf(dev, "couldn't map SYSCTRL: %d\n", error);
261108588Ssam			return (error);
262108588Ssam		}
263108588Ssam	}
264108588Ssam
265108588Ssam	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
266108588Ssam
267108588Ssam	/* Attach child devices */
268108588Ssam	for (child = OF_child(node); child > 0; child = OF_peer(child)) {
269108588Ssam		cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL);
270108588Ssam		if (cdev != NULL)
271108588Ssam			device_probe_and_attach(cdev);
272108588Ssam	}
273108588Ssam
274108588Ssam	return (bus_generic_attach(dev));
275108588Ssam}
276108588Ssam
277108588Ssamstatic device_method_t aw_ccu_methods[] = {
278108588Ssam	/* Device interface */
279108588Ssam	DEVMETHOD(device_probe,		aw_ccu_probe),
280108588Ssam	DEVMETHOD(device_attach,	aw_ccu_attach),
281108588Ssam
282108588Ssam	/* clkdev interface */
283108588Ssam	DEVMETHOD(clkdev_write_4,	aw_ccu_write_4),
284108588Ssam	DEVMETHOD(clkdev_read_4,	aw_ccu_read_4),
285108588Ssam	DEVMETHOD(clkdev_modify_4,	aw_ccu_modify_4),
286108588Ssam	DEVMETHOD(clkdev_device_lock,	aw_ccu_device_lock),
287108588Ssam	DEVMETHOD(clkdev_device_unlock,	aw_ccu_device_unlock),
288108588Ssam
289108588Ssam	DEVMETHOD_END
290108588Ssam};
291108588Ssam
292108588SsamDEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods,
293108588Ssam    sizeof(struct aw_ccu_softc), simplebus_driver);
294108588Ssam
295108588Ssamstatic devclass_t aw_ccu_devclass;
296108588Ssam
297108588SsamEARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass,
298108588Ssam    0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
299108588Ssam
300167755SsamMODULE_VERSION(aw_ccu, 1);
301167755Ssam