xref: /openbsd/sys/dev/fdt/bcm2835_mbox.c (revision 4c14fa96)
1 /*     $OpenBSD: bcm2835_mbox.c,v 1.5 2024/05/28 09:19:04 claudio 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
bcmmbox_match(struct device * parent,void * match,void * aux)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
bcmmbox_attach(struct device * parent,struct device * self,void * aux)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 	return;
157 
158  clean_dmamap:
159 	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
160 
161  clean_bus_space_map:
162 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, size);
163 }
164 
165 uint32_t
bcmmbox_reg_read(struct bcmmbox_softc * sc,int addr)166 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr)
167 {
168 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr);
169 }
170 
171 void
bcmmbox_reg_write(struct bcmmbox_softc * sc,int addr,uint32_t val)172 bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val)
173 {
174 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val);
175 }
176 
177 void
bcmmbox_reg_flush(struct bcmmbox_softc * sc,int flags)178 bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags)
179 {
180 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags);
181 }
182 
183 int
bcmmbox_intr(void * cookie)184 bcmmbox_intr(void *cookie)
185 {
186 	struct bcmmbox_softc *sc = cookie;
187 	int ret;
188 
189 	mtx_enter(&sc->sc_intr_lock);
190 	ret = bcmmbox_intr_helper(sc, 1);
191 	mtx_leave(&sc->sc_intr_lock);
192 
193 	return ret;
194 }
195 
196 int
bcmmbox_intr_helper(struct bcmmbox_softc * sc,int broadcast)197 bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast)
198 {
199 	uint32_t mbox, chan, data;
200 	int ret = 0;
201 
202 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
203 
204 	while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) {
205 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
206 
207 		chan = mbox & BCMMBOX_CHANNEL_MASK;
208 		data = mbox & ~BCMMBOX_CHANNEL_MASK;
209 		ret = 1;
210 
211 		if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) {
212 			printf("%s: chan %d overflow\n", DEVNAME(sc), chan);
213 			continue;
214 		}
215 
216 		sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK;
217 
218 		if (broadcast)
219 			wakeup(&sc->sc_chan[chan]);
220 	}
221 
222 	return ret;
223 }
224 
225 void
bcmmbox_read(uint8_t chan,uint32_t * data)226 bcmmbox_read(uint8_t chan, uint32_t *data)
227 {
228 	struct bcmmbox_softc *sc = bcmmbox_sc;
229 	uint32_t mbox, rchan, rdata, status;
230 
231 	KASSERT(sc != NULL);
232 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
233 
234 	while (1) {
235 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
236 		status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
237 		if (ISSET(status, BCMMBOX_STATUS_EMPTY))
238 			continue;
239 
240 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
241 
242 		rchan = mbox & BCMMBOX_CHANNEL_MASK;
243 		rdata = mbox & ~BCMMBOX_CHANNEL_MASK;
244 
245 		if (rchan == chan) {
246 			*data = rdata;
247 			return;
248 		}
249 	}
250 }
251 
252 void
bcmmbox_write(uint8_t chan,uint32_t data)253 bcmmbox_write(uint8_t chan, uint32_t data)
254 {
255 	struct bcmmbox_softc *sc = bcmmbox_sc;
256 	uint32_t rdata;
257 
258 	KASSERT(sc != NULL);
259 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
260 	KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK));
261 
262 	while (1) {
263 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
264 		rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
265 		if (!ISSET(rdata, BCMMBOX_STATUS_FULL))
266 			break;
267 	}
268 
269 	bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data);
270 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE);
271 }
272 
273 int
bcmmbox_post(uint8_t chan,void * buf,size_t len,uint32_t * res)274 bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res)
275 {
276 	struct bcmmbox_softc *sc = bcmmbox_sc;
277 	bus_dmamap_t map;
278 	int error;
279 
280 	KASSERT(sc != NULL);
281 	if (sc == NULL)
282 		return (ENXIO);
283 
284 	map = sc->sc_dmamap;
285 
286 	error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL,
287 	    BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE);
288 	if (error != 0)
289 		return (error);
290 
291 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
292 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
293 
294 	bcmmbox_write(chan, map->dm_segs[0].ds_addr);
295 	bcmmbox_read(chan, res);
296 
297 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
298 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
299 
300 	bus_dmamap_unload(sc->sc_dmat, map);
301 
302 	return (0);
303 }
304