xref: /netbsd-src/sys/arch/arm/broadcom/bcm2835_cm.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /*	$NetBSD: bcm2835_cm.c,v 1.3 2021/01/27 03:10:19 thorpej Exp $ */
2f59d56dbSmlelstv 
3f59d56dbSmlelstv /*-
4f59d56dbSmlelstv  * Copyright (c) 2015 The NetBSD Foundation, Inc.
5f59d56dbSmlelstv  * All rights reserved.
6f59d56dbSmlelstv  *
7f59d56dbSmlelstv  * This code is derived from software contributed to The NetBSD Foundation
8f59d56dbSmlelstv  * by Michael van Elst
9f59d56dbSmlelstv  *
10f59d56dbSmlelstv  * Redistribution and use in source and binary forms, with or without
11f59d56dbSmlelstv  * modification, are permitted provided that the following conditions
12f59d56dbSmlelstv  * are met:
13f59d56dbSmlelstv  * 1. Redistributions of source code must retain the above copyright
14f59d56dbSmlelstv  *    notice, this list of conditions and the following disclaimer.
15f59d56dbSmlelstv  * 2. Redistributions in binary form must reproduce the above copyright
16f59d56dbSmlelstv  *    notice, this list of conditions and the following disclaimer in the
17f59d56dbSmlelstv  *    documentation and/or other materials provided with the distribution.
18f59d56dbSmlelstv  *
19f59d56dbSmlelstv  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20f59d56dbSmlelstv  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21f59d56dbSmlelstv  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22f59d56dbSmlelstv  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23f59d56dbSmlelstv  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24f59d56dbSmlelstv  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25f59d56dbSmlelstv  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26f59d56dbSmlelstv  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27f59d56dbSmlelstv  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28f59d56dbSmlelstv  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29f59d56dbSmlelstv  * POSSIBILITY OF SUCH DAMAGE.
30f59d56dbSmlelstv  */
31f59d56dbSmlelstv 
32f59d56dbSmlelstv /*
33f59d56dbSmlelstv  * Driver for BCM2835 Clock Manager
34f59d56dbSmlelstv  */
35f59d56dbSmlelstv 
36f59d56dbSmlelstv #include <sys/cdefs.h>
37*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: bcm2835_cm.c,v 1.3 2021/01/27 03:10:19 thorpej Exp $");
38f59d56dbSmlelstv 
39f59d56dbSmlelstv #include <sys/param.h>
40f59d56dbSmlelstv #include <sys/systm.h>
41f59d56dbSmlelstv #include <sys/device.h>
42f59d56dbSmlelstv #include <sys/kernel.h>
43f59d56dbSmlelstv #include <sys/bus.h>
44f59d56dbSmlelstv 
45f59d56dbSmlelstv #include <arm/broadcom/bcm2835reg.h>
46f59d56dbSmlelstv #include <arm/broadcom/bcm2835_cm.h>
47f59d56dbSmlelstv 
48ee91b1e5Sskrll #include <dev/fdt/fdtvar.h>
49ee91b1e5Sskrll 
50ee91b1e5Sskrll #include <arm/fdt/arm_fdtvar.h>
51ee91b1e5Sskrll 
52f59d56dbSmlelstv struct bcm2835cm_softc {
53f59d56dbSmlelstv 	device_t		sc_dev;
54f59d56dbSmlelstv 
55f59d56dbSmlelstv 	bus_space_tag_t		sc_iot;
56f59d56dbSmlelstv 	bus_space_handle_t	sc_ioh;
57f59d56dbSmlelstv };
58f59d56dbSmlelstv 
59f59d56dbSmlelstv #define CM_WRITE(sc, reg, val) \
60f59d56dbSmlelstv 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
61f59d56dbSmlelstv #define CM_READ(sc, reg) \
62f59d56dbSmlelstv 	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
63f59d56dbSmlelstv 
64f59d56dbSmlelstv static int bcmcm_match(device_t, cfdata_t, void *);
65f59d56dbSmlelstv static void bcmcm_attach(device_t, device_t, void *);
66f59d56dbSmlelstv static int bcmcm_wait(struct bcm2835cm_softc *, int, int);
67f59d56dbSmlelstv 
68ee91b1e5Sskrll CFATTACH_DECL_NEW(bcmcm_fdt, sizeof(struct bcm2835cm_softc),
69f59d56dbSmlelstv     bcmcm_match, bcmcm_attach, NULL, NULL);
70f59d56dbSmlelstv 
71*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
72*6e54367aSthorpej 	{ .compat = "brcm,bcm2835-cprman" },
73*6e54367aSthorpej 	DEVICE_COMPAT_EOL
74*6e54367aSthorpej };
75*6e54367aSthorpej 
76f59d56dbSmlelstv /* ARGSUSED */
77f59d56dbSmlelstv static int
bcmcm_match(device_t parent,cfdata_t match,void * aux)78f59d56dbSmlelstv bcmcm_match(device_t parent, cfdata_t match, void *aux)
79f59d56dbSmlelstv {
80ee91b1e5Sskrll 	struct fdt_attach_args * const faa = aux;
81f59d56dbSmlelstv 
82*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
83f59d56dbSmlelstv }
84f59d56dbSmlelstv 
85f59d56dbSmlelstv static void
bcmcm_attach(device_t parent,device_t self,void * aux)86f59d56dbSmlelstv bcmcm_attach(device_t parent, device_t self, void *aux)
87f59d56dbSmlelstv {
88f59d56dbSmlelstv 	struct bcm2835cm_softc *sc = device_private(self);
89ee91b1e5Sskrll 	struct fdt_attach_args * const faa = aux;
90f59d56dbSmlelstv 
91f59d56dbSmlelstv 	aprint_naive("\n");
92f59d56dbSmlelstv 	aprint_normal(": CM\n");
93f59d56dbSmlelstv 
94f59d56dbSmlelstv 	sc->sc_dev = self;
95ee91b1e5Sskrll 	sc->sc_iot = faa->faa_bst;
96ee91b1e5Sskrll 	const int phandle = faa->faa_phandle;
97f59d56dbSmlelstv 
98ee91b1e5Sskrll 	bus_addr_t addr;
99ee91b1e5Sskrll 	bus_size_t size;
100ee91b1e5Sskrll 
101ee91b1e5Sskrll 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
102ee91b1e5Sskrll 		aprint_error(": missing 'reg' property\n");
103ee91b1e5Sskrll 		return;
104ee91b1e5Sskrll 	}
105ee91b1e5Sskrll 
106ee91b1e5Sskrll 	if (bus_space_map(faa->faa_bst, addr, size, 0, &sc->sc_ioh)) {
107f59d56dbSmlelstv 		aprint_error_dev(sc->sc_dev, "unable to map device\n");
108ee91b1e5Sskrll 		return;
109f59d56dbSmlelstv 	}
110f59d56dbSmlelstv 
111f59d56dbSmlelstv 	/* Success!  */
112f59d56dbSmlelstv 
113ee91b1e5Sskrll 	return;
114f59d56dbSmlelstv }
115f59d56dbSmlelstv 
116f59d56dbSmlelstv static int
bcmcm_wait(struct bcm2835cm_softc * sc,int ctlreg,int onoff)117f59d56dbSmlelstv bcmcm_wait(struct bcm2835cm_softc *sc, int ctlreg, int onoff)
118f59d56dbSmlelstv {
119f59d56dbSmlelstv 	int i;
120f59d56dbSmlelstv 	uint32_t r;
121f59d56dbSmlelstv 
122f59d56dbSmlelstv 	for (i=0; i<100; ++i) {
123f59d56dbSmlelstv 		r = CM_READ(sc, ctlreg);
124f59d56dbSmlelstv 		if (((r & CM_CTL_BUSY) != 0) == onoff)
125f59d56dbSmlelstv 			break;
126f59d56dbSmlelstv 		delay(10);
127f59d56dbSmlelstv 	}
128f59d56dbSmlelstv 	if (i >= 100) {
129f59d56dbSmlelstv 		device_printf(sc->sc_dev, "busy (addr=%#x)\n", ctlreg);
130f59d56dbSmlelstv 		return EIO;
131f59d56dbSmlelstv 	}
132f59d56dbSmlelstv 
133f59d56dbSmlelstv 	return 0;
134f59d56dbSmlelstv }
135f59d56dbSmlelstv 
136f59d56dbSmlelstv int
bcm_cm_set(enum bcm_cm_clock clk,uint32_t ctl,uint32_t div)137f59d56dbSmlelstv bcm_cm_set(enum bcm_cm_clock clk, uint32_t ctl, uint32_t div)
138f59d56dbSmlelstv {
139f59d56dbSmlelstv 	struct bcm2835cm_softc *sc;
140f59d56dbSmlelstv 	device_t dev;
141f59d56dbSmlelstv 	int ctlreg, divreg;
142f59d56dbSmlelstv 	uint32_t r;
143f59d56dbSmlelstv 
144f59d56dbSmlelstv 	dev = device_find_by_driver_unit("bcmcm", 0);
145f59d56dbSmlelstv 	if (dev == NULL)
146f59d56dbSmlelstv 		return ENXIO;
147f59d56dbSmlelstv 	sc = device_private(dev);
148f59d56dbSmlelstv 
149f59d56dbSmlelstv 	switch (clk) {
150f59d56dbSmlelstv 	case BCM_CM_GP0:
151f59d56dbSmlelstv 		ctlreg = CM_GP0CTL;
152f59d56dbSmlelstv 		divreg = CM_GP0DIV;
153f59d56dbSmlelstv 		break;
154f59d56dbSmlelstv 	case BCM_CM_GP1:
155f59d56dbSmlelstv 		ctlreg = CM_GP1CTL;
156f59d56dbSmlelstv 		divreg = CM_GP1DIV;
157f59d56dbSmlelstv 		break;
158f59d56dbSmlelstv 	case BCM_CM_GP2:
159f59d56dbSmlelstv 		ctlreg = CM_GP2CTL;
160f59d56dbSmlelstv 		divreg = CM_GP2DIV;
161f59d56dbSmlelstv 		break;
162f59d56dbSmlelstv 	case BCM_CM_PCM:
163f59d56dbSmlelstv 		ctlreg = CM_PCMCTL;
164f59d56dbSmlelstv 		divreg = CM_PCMDIV;
165f59d56dbSmlelstv 		break;
166f59d56dbSmlelstv 	case BCM_CM_PWM:
167f59d56dbSmlelstv 		ctlreg = CM_PWMCTL;
168f59d56dbSmlelstv 		divreg = CM_PWMDIV;
169f59d56dbSmlelstv 		break;
170f59d56dbSmlelstv 	default:
171f59d56dbSmlelstv 		return EINVAL;
172f59d56dbSmlelstv 	}
173f59d56dbSmlelstv 
174f59d56dbSmlelstv 	ctl &= ~CM_CTL_PASSWD;
175f59d56dbSmlelstv 	ctl |= __SHIFTIN(CM_PASSWD, CM_CTL_PASSWD);
176f59d56dbSmlelstv 	div &= ~CM_DIV_PASSWD;
177f59d56dbSmlelstv 	div |= __SHIFTIN(CM_PASSWD, CM_DIV_PASSWD);
178f59d56dbSmlelstv 
179ee91b1e5Sskrll 	/*
180ee91b1e5Sskrll 	 * if clock is running, turn it off and wait for
181f59d56dbSmlelstv 	 * the cycle to end
182f59d56dbSmlelstv 	 */
183f59d56dbSmlelstv 	r = CM_READ(sc, ctlreg);
184f59d56dbSmlelstv 	if (r & CM_CTL_ENAB) {
185f59d56dbSmlelstv 		r &= ~CM_CTL_PASSWD;
186f59d56dbSmlelstv 		r |= __SHIFTIN(CM_PASSWD, CM_CTL_PASSWD);
187f59d56dbSmlelstv 		r &= ~CM_CTL_ENAB;
188f59d56dbSmlelstv 		CM_WRITE(sc, ctlreg, r);
189f59d56dbSmlelstv 	}
190f59d56dbSmlelstv 
191f59d56dbSmlelstv 	bcmcm_wait(sc, ctlreg, 0);
192f59d56dbSmlelstv 
193f59d56dbSmlelstv 	/* configure new divider, mode, don't enable */
194f59d56dbSmlelstv 	CM_WRITE(sc, divreg, div);
195f59d56dbSmlelstv 	CM_WRITE(sc, ctlreg, ctl & ~CM_CTL_ENAB);
196f59d56dbSmlelstv 
197f59d56dbSmlelstv 	/* enable it */
198f59d56dbSmlelstv 	if (ctl & CM_CTL_ENAB) {
199f59d56dbSmlelstv 		CM_WRITE(sc, ctlreg, ctl);
200f59d56dbSmlelstv 		return bcmcm_wait(sc, ctlreg, 1);
201f59d56dbSmlelstv 	}
202f59d56dbSmlelstv 
203f59d56dbSmlelstv 	return 0;
204f59d56dbSmlelstv }
205f59d56dbSmlelstv 
206f59d56dbSmlelstv int
bcm_cm_get(enum bcm_cm_clock clk,uint32_t * ctlp,uint32_t * divp)207f59d56dbSmlelstv bcm_cm_get(enum bcm_cm_clock clk, uint32_t *ctlp, uint32_t *divp)
208f59d56dbSmlelstv {
209f59d56dbSmlelstv 	struct bcm2835cm_softc *sc;
210f59d56dbSmlelstv 	device_t dev;
211f59d56dbSmlelstv 	int ctlreg, divreg;
212f59d56dbSmlelstv 
213f59d56dbSmlelstv 	dev = device_find_by_driver_unit("bcmcm", 0);
214f59d56dbSmlelstv 	if (dev == NULL)
215f59d56dbSmlelstv 		return ENXIO;
216f59d56dbSmlelstv 	sc = device_private(dev);
217f59d56dbSmlelstv 
218f59d56dbSmlelstv 	switch (clk) {
219f59d56dbSmlelstv 	case BCM_CM_GP0:
220f59d56dbSmlelstv 		ctlreg = CM_GP0CTL;
221f59d56dbSmlelstv 		divreg = CM_GP0DIV;
222f59d56dbSmlelstv 		break;
223f59d56dbSmlelstv 	case BCM_CM_GP1:
224f59d56dbSmlelstv 		ctlreg = CM_GP1CTL;
225f59d56dbSmlelstv 		divreg = CM_GP1DIV;
226f59d56dbSmlelstv 		break;
227f59d56dbSmlelstv 	case BCM_CM_GP2:
228f59d56dbSmlelstv 		ctlreg = CM_GP2CTL;
229f59d56dbSmlelstv 		divreg = CM_GP2DIV;
230f59d56dbSmlelstv 		break;
231f59d56dbSmlelstv 	case BCM_CM_PCM:
232f59d56dbSmlelstv 		ctlreg = CM_PCMCTL;
233f59d56dbSmlelstv 		divreg = CM_PCMDIV;
234f59d56dbSmlelstv 		break;
235f59d56dbSmlelstv 	case BCM_CM_PWM:
236f59d56dbSmlelstv 		ctlreg = CM_PWMCTL;
237f59d56dbSmlelstv 		divreg = CM_PWMDIV;
238f59d56dbSmlelstv 		break;
239f59d56dbSmlelstv 	default:
240f59d56dbSmlelstv 		return EINVAL;
241f59d56dbSmlelstv 	}
242f59d56dbSmlelstv 
243f59d56dbSmlelstv 	if (ctlp != NULL)
244f59d56dbSmlelstv 		*ctlp = CM_READ(sc, ctlreg);
245f59d56dbSmlelstv 	if (divp != NULL)
246f59d56dbSmlelstv 		*divp = CM_READ(sc, divreg);
247f59d56dbSmlelstv 
248f59d56dbSmlelstv 	return 0;
249f59d56dbSmlelstv }
250