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