xref: /openbsd-src/sys/dev/fdt/bcm2835_mbox.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*     $OpenBSD: bcm2835_mbox.c,v 1.1 2020/04/19 14:51:52 tobhe 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 
52 #include <machine/bus.h>
53 #include <machine/fdt.h>
54 #include <machine/intr.h>
55 
56 #include <dev/ofw/fdt.h>
57 #include <dev/ofw/openfirm.h>
58 
59 #include <dev/ic/bcm2835_mbox.h>
60 #include <dev/ic/bcm2835_vcprop.h>
61 
62 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
63 
64 struct cfdriver bcmmbox_cd = { NULL, "bcmmbox", DV_DULL };
65 
66 struct bcmmbox_softc {
67 	struct device sc_dev;
68 	bus_space_tag_t sc_iot;
69 	bus_space_handle_t sc_ioh;
70 
71 	bus_dma_tag_t	sc_dmat;
72 	bus_dmamap_t	sc_dmamap;
73 
74 	void *sc_ih;
75 
76 	struct mutex sc_lock;
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 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_lock, IPL_NONE);
122 	mtx_init(&sc->sc_intr_lock, IPL_VM);
123 
124 	if (faa->fa_nreg < 1) {
125 		printf(": no registers\n");
126 		return;
127 	}
128 
129 	addr = faa->fa_reg[0].addr;
130 	size = faa->fa_reg[0].size;
131 
132 	sc->sc_dmat = faa->fa_dmat;
133 	sc->sc_iot = faa->fa_iot;
134 	if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) {
135 		printf(": can't map registers\n");
136 		return;
137 	}
138 
139 	if (bus_dmamap_create(sc->sc_dmat, ~0UL, 1, ~0UL, 0,
140 	    BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamap) != 0) {
141 		printf(": unable to create dma map\n");
142 		goto clean_bus_space_map;
143 	}
144 
145 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM, bcmmbox_intr, sc,
146 	    DEVNAME(sc));
147 	if (sc->sc_ih == NULL) {
148 		printf(": failed to establish interrupt\n");
149 		goto clean_dmamap;
150 	}
151 
152 	/* enable interrupt in hardware */
153 	bcmmbox_reg_write(sc, BCMMBOX_CFG, BCMMBOX_CFG_DATA_IRQ_EN);
154 
155 	printf("\n");
156 
157 	bcmmbox_write(BCMMBOX_CHANPM, (
158 	    (1 << VCPROP_POWER_SDCARD) |
159 	    (1 << VCPROP_POWER_UART0) |
160 	    (1 << VCPROP_POWER_USB) |
161 	    (1 << VCPROP_POWER_I2C0) |
162 	    (1 << VCPROP_POWER_I2C1) |
163 	    (1 << VCPROP_POWER_SPI) |
164 	    0) << 4);
165 
166 	return;
167 
168  clean_dmamap:
169 	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
170 
171  clean_bus_space_map:
172 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, size);
173 }
174 
175 uint32_t
176 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr)
177 {
178 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr);
179 }
180 
181 void
182 bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val)
183 {
184 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val);
185 }
186 
187 void
188 bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags)
189 {
190 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags);
191 }
192 
193 int
194 bcmmbox_intr(void *cookie)
195 {
196 	struct bcmmbox_softc *sc = cookie;
197 	int ret;
198 
199 	mtx_enter(&sc->sc_intr_lock);
200 	ret = bcmmbox_intr_helper(sc, 1);
201 	mtx_leave(&sc->sc_intr_lock);
202 
203 	return ret;
204 }
205 
206 int
207 bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast)
208 {
209 	uint32_t mbox, chan, data;
210 	int ret = 0;
211 
212 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
213 
214 	while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) {
215 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
216 
217 		chan = mbox & BCMMBOX_CHANNEL_MASK;
218 		data = mbox & ~BCMMBOX_CHANNEL_MASK;
219 		ret = 1;
220 
221 		if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) {
222 			printf("%s: chan %d overflow\n", DEVNAME(sc), chan);
223 			continue;
224 		}
225 
226 		sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK;
227 
228 		if (broadcast)
229 			wakeup(&sc->sc_chan[chan]);
230 	}
231 
232 	return ret;
233 }
234 
235 void
236 bcmmbox_read(uint8_t chan, uint32_t *data)
237 {
238 	struct bcmmbox_softc *sc = bcmmbox_sc;
239 	uint32_t mbox, rchan, rdata, status;
240 
241 	KASSERT(sc != NULL);
242 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
243 
244 	while (1) {
245 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
246 		status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
247 		if (ISSET(status, BCMMBOX_STATUS_EMPTY))
248 			continue;
249 
250 		mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ);
251 
252 		rchan = mbox & BCMMBOX_CHANNEL_MASK;
253 		rdata = mbox & ~BCMMBOX_CHANNEL_MASK;
254 
255 		if (rchan == chan) {
256 			*data = rdata;
257 			return;
258 		}
259 	}
260 }
261 
262 void
263 bcmmbox_write(uint8_t chan, uint32_t data)
264 {
265 	struct bcmmbox_softc *sc = bcmmbox_sc;
266 	uint32_t rdata;
267 
268 	KASSERT(sc != NULL);
269 	KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK));
270 	KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK));
271 
272 	while (1) {
273 		bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ);
274 		rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS);
275 		if (!ISSET(rdata, BCMMBOX_STATUS_FULL))
276 			break;
277 	}
278 
279 	bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data);
280 	bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE);
281 }
282 
283 int
284 bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res)
285 {
286 	struct bcmmbox_softc *sc = bcmmbox_sc;
287 	bus_dmamap_t map;
288 	int error;
289 
290 	KASSERT(sc != NULL);
291 	if (sc == NULL)
292 		return (ENXIO);
293 
294 	map = sc->sc_dmamap;
295 
296 	error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL,
297 	    BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE);
298 	if (error != 0)
299 		return (error);
300 
301 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
302 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
303 
304 	bcmmbox_write(chan, map->dm_segs[0].ds_addr);
305 	bcmmbox_read(chan, res);
306 
307 	bus_dmamap_sync(sc->sc_dmat, map, 0, len,
308 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
309 
310 	bus_dmamap_unload(sc->sc_dmat, map);
311 
312 	return (0);
313 }
314