xref: /openbsd/sys/dev/fdt/bcm2835_mbox.c (revision d415bd75)
1 /*     $OpenBSD: bcm2835_mbox.c,v 1.4 2022/08/27 20:31:45 mglocker Exp $ */
2 
3 /*
4  * Copyright (c) 2020 Tobias Heider <tobhe@openbsd.org>
5  * Copyright (c) 2019 Neil Ashford <ashfordneil0@gmail.com>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*-
21  * Copyright (c) 2012 The NetBSD Foundation, Inc.
22  * All rights reserved.
23  *
24  * This code is derived from software contributed to The NetBSD Foundation
25  * by Nick Hudson
26  *
27  * Redistribution and use in source and binary forms, with or without
28  * modification, are permitted provided that the following conditions
29  * are met:
30  * 1. Redistributions of source code must retain the above copyright
31  *    notice, this list of conditions and the following disclaimer.
32  * 2. Redistributions in binary form must reproduce the above copyright
33  *    notice, this list of conditions and the following disclaimer in the
34  *    documentation and/or other materials provided with the distribution.
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
37  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
38  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
41  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
42  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
43  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
44  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46  * POSSIBILITY OF SUCH DAMAGE.
47  */
48 
49 #include <sys/types.h>
50 #include <sys/systm.h>
51 #include <sys/mutex.h>
52 
53 #include <machine/bus.h>
54 #include <machine/fdt.h>
55 #include <machine/intr.h>
56 
57 #include <dev/ofw/fdt.h>
58 #include <dev/ofw/openfirm.h>
59 
60 #include <dev/ic/bcm2835_mbox.h>
61 #include <dev/ic/bcm2835_vcprop.h>
62 
63 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
64 
65 struct cfdriver bcmmbox_cd = { NULL, "bcmmbox", DV_DULL };
66 
67 struct bcmmbox_softc {
68 	struct device sc_dev;
69 	bus_space_tag_t sc_iot;
70 	bus_space_handle_t sc_ioh;
71 
72 	bus_dma_tag_t	sc_dmat;
73 	bus_dmamap_t	sc_dmamap;
74 
75 	void *sc_ih;
76 
77 	struct mutex sc_intr_lock;
78 	int sc_chan[BCMMBOX_NUM_CHANNELS];
79 	uint32_t sc_mbox[BCMMBOX_NUM_CHANNELS];
80 };
81 
82 static struct bcmmbox_softc *bcmmbox_sc;
83 
84 int bcmmbox_match(struct device *, void *, void *);
85 void bcmmbox_attach(struct device *, struct device *, void *);
86 
87 const struct cfattach bcmmbox_ca = {
88 	sizeof(struct bcmmbox_softc),
89 	bcmmbox_match,
90 	bcmmbox_attach,
91 };
92 
93 uint32_t bcmmbox_reg_read(struct bcmmbox_softc *, int);
94 void bcmmbox_reg_write(struct bcmmbox_softc *, int, uint32_t);
95 void bcmmbox_reg_flush(struct bcmmbox_softc *, int);
96 int bcmmbox_intr(void *);
97 int bcmmbox_intr_helper(struct bcmmbox_softc *, int);
98 
99 int
100 bcmmbox_match(struct device *parent, void *match, void *aux)
101 {
102 	struct fdt_attach_args *faa = aux;
103 
104 	return OF_is_compatible(faa->fa_node, "brcm,bcm2835-mbox");
105 }
106 
107 void
108 bcmmbox_attach(struct device *parent, struct device *self, void *aux)
109 {
110 	struct bcmmbox_softc *sc = (struct bcmmbox_softc *)self;
111 	struct fdt_attach_args *faa = aux;
112 	bus_addr_t addr;
113 	bus_size_t size;
114 
115 	if (bcmmbox_sc) {
116 		printf(": a similar device as already attached\n");
117 		return;
118 	}
119 	bcmmbox_sc = sc;
120 
121 	mtx_init(&sc->sc_intr_lock, IPL_VM);
122 
123 	if (faa->fa_nreg < 1) {
124 		printf(": no registers\n");
125 		return;
126 	}
127 
128 	addr = faa->fa_reg[0].addr;
129 	size = faa->fa_reg[0].size;
130 
131 	sc->sc_dmat = faa->fa_dmat;
132 	sc->sc_iot = faa->fa_iot;
133 	if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) {
134 		printf(": can't map registers\n");
135 		return;
136 	}
137 
138 	if (bus_dmamap_create(sc->sc_dmat, ~0UL, 1, ~0UL, 0,
139 	    BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamap) != 0) {
140 		printf(": unable to create dma map\n");
141 		goto clean_bus_space_map;
142 	}
143 
144 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM, bcmmbox_intr, sc,
145 	    DEVNAME(sc));
146 	if (sc->sc_ih == NULL) {
147 		printf(": failed to establish interrupt\n");
148 		goto clean_dmamap;
149 	}
150 
151 	/* enable interrupt in hardware */
152 	bcmmbox_reg_write(sc, BCMMBOX_CFG, BCMMBOX_CFG_DATA_IRQ_EN);
153 
154 	printf("\n");
155 
156 	bcmmbox_write(BCMMBOX_CHANPM, (
157 	    (1 << VCPROP_POWER_SDCARD) |
158 	    (1 << VCPROP_POWER_UART0) |
159 	    (1 << VCPROP_POWER_USB) |
160 	    (1 << VCPROP_POWER_I2C0) |
161 	    (1 << VCPROP_POWER_I2C1) |
162 	    (1 << VCPROP_POWER_SPI) |
163 	    0) << 4);
164 
165 	return;
166 
167  clean_dmamap:
168 	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
169 
170  clean_bus_space_map:
171 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, size);
172 }
173 
174 uint32_t
175 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr)
176 {
177 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr);
178 }
179 
180 void
181 bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val)
182 {
183 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val);
184 }
185 
186 void
187 bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags)
188 {
189 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags);
190 }
191 
192 int
193 bcmmbox_intr(void *cookie)
194 {
195 	struct bcmmbox_softc *sc = cookie;
196 	int ret;
197 
198 	mtx_enter(&sc->sc_intr_lock);
199 	ret = bcmmbox_intr_helper(sc, 1);
200 	mtx_leave(&sc->sc_intr_lock);
201 
202 	return ret;
203 }
204 
205 int
206 bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast)
207 {
208 	uint32_t mbox, chan, data;
209 	int ret = 0;
210 
211 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
212 
213 	while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) {
214 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
215 
216 		chan = mbox & BCMMBOX_CHANNEL_MASK;
217 		data = mbox & ~BCMMBOX_CHANNEL_MASK;
218 		ret = 1;
219 
220 		if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) {
221 			printf("%s: chan %d overflow\n", DEVNAME(sc), chan);
222 			continue;
223 		}
224 
225 		sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK;
226 
227 		if (broadcast)
228 			wakeup(&sc->sc_chan[chan]);
229 	}
230 
231 	return ret;
232 }
233 
234 void
235 bcmmbox_read(uint8_t chan, uint32_t *data)
236 {
237 	struct bcmmbox_softc *sc = bcmmbox_sc;
238 	uint32_t mbox, rchan, rdata, status;
239 
240 	KASSERT(sc != NULL);
241 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
242 
243 	while (1) {
244 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
245 		status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
246 		if (ISSET(status, BCMMBOX_STATUS_EMPTY))
247 			continue;
248 
249 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
250 
251 		rchan = mbox & BCMMBOX_CHANNEL_MASK;
252 		rdata = mbox & ~BCMMBOX_CHANNEL_MASK;
253 
254 		if (rchan == chan) {
255 			*data = rdata;
256 			return;
257 		}
258 	}
259 }
260 
261 void
262 bcmmbox_write(uint8_t chan, uint32_t data)
263 {
264 	struct bcmmbox_softc *sc = bcmmbox_sc;
265 	uint32_t rdata;
266 
267 	KASSERT(sc != NULL);
268 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
269 	KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK));
270 
271 	while (1) {
272 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
273 		rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
274 		if (!ISSET(rdata, BCMMBOX_STATUS_FULL))
275 			break;
276 	}
277 
278 	bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data);
279 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE);
280 }
281 
282 int
283 bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res)
284 {
285 	struct bcmmbox_softc *sc = bcmmbox_sc;
286 	bus_dmamap_t map;
287 	int error;
288 
289 	KASSERT(sc != NULL);
290 	if (sc == NULL)
291 		return (ENXIO);
292 
293 	map = sc->sc_dmamap;
294 
295 	error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL,
296 	    BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE);
297 	if (error != 0)
298 		return (error);
299 
300 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
301 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
302 
303 	bcmmbox_write(chan, map->dm_segs[0].ds_addr);
304 	bcmmbox_read(chan, res);
305 
306 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
307 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
308 
309 	bus_dmamap_unload(sc->sc_dmat, map);
310 
311 	return (0);
312 }
313