xref: /openbsd-src/sys/dev/fdt/bcm2835_mbox.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*     $OpenBSD: bcm2835_mbox.c,v 1.2 2021/05/30 15:05:33 visa 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_lock;
78 	struct mutex sc_intr_lock;
79 	int sc_chan[BCMMBOX_NUM_CHANNELS];
80 	uint32_t sc_mbox[BCMMBOX_NUM_CHANNELS];
81 };
82 
83 static struct bcmmbox_softc *bcmmbox_sc;
84 
85 int bcmmbox_match(struct device *, void *, void *);
86 void bcmmbox_attach(struct device *, struct device *, void *);
87 
88 struct cfattach bcmmbox_ca = {
89 	sizeof(struct bcmmbox_softc),
90 	bcmmbox_match,
91 	bcmmbox_attach,
92 };
93 
94 uint32_t bcmmbox_reg_read(struct bcmmbox_softc *, int);
95 void bcmmbox_reg_write(struct bcmmbox_softc *, int, uint32_t);
96 void bcmmbox_reg_flush(struct bcmmbox_softc *, int);
97 int bcmmbox_intr(void *);
98 int bcmmbox_intr_helper(struct bcmmbox_softc *, int);
99 
100 int
101 bcmmbox_match(struct device *parent, void *match, void *aux)
102 {
103 	struct fdt_attach_args *faa = aux;
104 
105 	return OF_is_compatible(faa->fa_node, "brcm,bcm2835-mbox");
106 }
107 
108 void
109 bcmmbox_attach(struct device *parent, struct device *self, void *aux)
110 {
111 	struct bcmmbox_softc *sc = (struct bcmmbox_softc *)self;
112 	struct fdt_attach_args *faa = aux;
113 	bus_addr_t addr;
114 	bus_size_t size;
115 
116 	if (bcmmbox_sc) {
117 		printf(": a similar device as already attached\n");
118 		return;
119 	}
120 	bcmmbox_sc = sc;
121 
122 	mtx_init(&sc->sc_lock, IPL_NONE);
123 	mtx_init(&sc->sc_intr_lock, IPL_VM);
124 
125 	if (faa->fa_nreg < 1) {
126 		printf(": no registers\n");
127 		return;
128 	}
129 
130 	addr = faa->fa_reg[0].addr;
131 	size = faa->fa_reg[0].size;
132 
133 	sc->sc_dmat = faa->fa_dmat;
134 	sc->sc_iot = faa->fa_iot;
135 	if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) {
136 		printf(": can't map registers\n");
137 		return;
138 	}
139 
140 	if (bus_dmamap_create(sc->sc_dmat, ~0UL, 1, ~0UL, 0,
141 	    BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamap) != 0) {
142 		printf(": unable to create dma map\n");
143 		goto clean_bus_space_map;
144 	}
145 
146 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM, bcmmbox_intr, sc,
147 	    DEVNAME(sc));
148 	if (sc->sc_ih == NULL) {
149 		printf(": failed to establish interrupt\n");
150 		goto clean_dmamap;
151 	}
152 
153 	/* enable interrupt in hardware */
154 	bcmmbox_reg_write(sc, BCMMBOX_CFG, BCMMBOX_CFG_DATA_IRQ_EN);
155 
156 	printf("\n");
157 
158 	bcmmbox_write(BCMMBOX_CHANPM, (
159 	    (1 << VCPROP_POWER_SDCARD) |
160 	    (1 << VCPROP_POWER_UART0) |
161 	    (1 << VCPROP_POWER_USB) |
162 	    (1 << VCPROP_POWER_I2C0) |
163 	    (1 << VCPROP_POWER_I2C1) |
164 	    (1 << VCPROP_POWER_SPI) |
165 	    0) << 4);
166 
167 	return;
168 
169  clean_dmamap:
170 	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
171 
172  clean_bus_space_map:
173 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, size);
174 }
175 
176 uint32_t
177 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr)
178 {
179 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr);
180 }
181 
182 void
183 bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val)
184 {
185 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val);
186 }
187 
188 void
189 bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags)
190 {
191 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags);
192 }
193 
194 int
195 bcmmbox_intr(void *cookie)
196 {
197 	struct bcmmbox_softc *sc = cookie;
198 	int ret;
199 
200 	mtx_enter(&sc->sc_intr_lock);
201 	ret = bcmmbox_intr_helper(sc, 1);
202 	mtx_leave(&sc->sc_intr_lock);
203 
204 	return ret;
205 }
206 
207 int
208 bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast)
209 {
210 	uint32_t mbox, chan, data;
211 	int ret = 0;
212 
213 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
214 
215 	while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) {
216 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
217 
218 		chan = mbox & BCMMBOX_CHANNEL_MASK;
219 		data = mbox & ~BCMMBOX_CHANNEL_MASK;
220 		ret = 1;
221 
222 		if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) {
223 			printf("%s: chan %d overflow\n", DEVNAME(sc), chan);
224 			continue;
225 		}
226 
227 		sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK;
228 
229 		if (broadcast)
230 			wakeup(&sc->sc_chan[chan]);
231 	}
232 
233 	return ret;
234 }
235 
236 void
237 bcmmbox_read(uint8_t chan, uint32_t *data)
238 {
239 	struct bcmmbox_softc *sc = bcmmbox_sc;
240 	uint32_t mbox, rchan, rdata, status;
241 
242 	KASSERT(sc != NULL);
243 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
244 
245 	while (1) {
246 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
247 		status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
248 		if (ISSET(status, BCMMBOX_STATUS_EMPTY))
249 			continue;
250 
251 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
252 
253 		rchan = mbox & BCMMBOX_CHANNEL_MASK;
254 		rdata = mbox & ~BCMMBOX_CHANNEL_MASK;
255 
256 		if (rchan == chan) {
257 			*data = rdata;
258 			return;
259 		}
260 	}
261 }
262 
263 void
264 bcmmbox_write(uint8_t chan, uint32_t data)
265 {
266 	struct bcmmbox_softc *sc = bcmmbox_sc;
267 	uint32_t rdata;
268 
269 	KASSERT(sc != NULL);
270 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
271 	KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK));
272 
273 	while (1) {
274 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
275 		rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
276 		if (!ISSET(rdata, BCMMBOX_STATUS_FULL))
277 			break;
278 	}
279 
280 	bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data);
281 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE);
282 }
283 
284 int
285 bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res)
286 {
287 	struct bcmmbox_softc *sc = bcmmbox_sc;
288 	bus_dmamap_t map;
289 	int error;
290 
291 	KASSERT(sc != NULL);
292 	if (sc == NULL)
293 		return (ENXIO);
294 
295 	map = sc->sc_dmamap;
296 
297 	error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL,
298 	    BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE);
299 	if (error != 0)
300 		return (error);
301 
302 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
303 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
304 
305 	bcmmbox_write(chan, map->dm_segs[0].ds_addr);
306 	bcmmbox_read(chan, res);
307 
308 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
309 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
310 
311 	bus_dmamap_unload(sc->sc_dmat, map);
312 
313 	return (0);
314 }
315