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