bcm2835_mbox.c revision 1.8 1 /* $NetBSD: bcm2835_mbox.c,v 1.8 2014/10/07 08:30:05 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nick Hudson
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: bcm2835_mbox.c,v 1.8 2014/10/07 08:30:05 skrll Exp $");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/kernel.h>
39 #include <sys/timetc.h>
40 #include <sys/bus.h>
41 #include <sys/mutex.h>
42
43 #include <arm/broadcom/bcm_amba.h>
44 #include <arm/broadcom/bcm2835_mbox.h>
45 #include <arm/broadcom/bcm2835_mboxreg.h>
46 #include <arm/broadcom/bcm2835reg.h>
47
48 struct bcm2835mbox_softc {
49 device_t sc_dev;
50 device_t sc_platdev;
51
52 bus_space_tag_t sc_iot;
53 bus_space_handle_t sc_ioh;
54 bus_dma_tag_t sc_dmat;
55 void *sc_intrh;
56
57 kmutex_t sc_lock;
58 kmutex_t sc_intr_lock;
59 kcondvar_t sc_chan[BCM2835_MBOX_NUMCHANNELS];
60 uint32_t sc_mbox[BCM2835_MBOX_NUMCHANNELS];
61 };
62
63 static struct bcm2835mbox_softc *bcm2835mbox_sc;
64
65 static int bcmmbox_match(device_t, cfdata_t, void *);
66 static void bcmmbox_attach(device_t, device_t, void *);
67 static int bcmmbox_intr1(struct bcm2835mbox_softc *, int);
68 static int bcmmbox_intr(void *);
69
70 CFATTACH_DECL_NEW(bcmmbox, sizeof(struct bcm2835mbox_softc),
71 bcmmbox_match, bcmmbox_attach, NULL, NULL);
72
73 /* ARGSUSED */
74 static int
75 bcmmbox_match(device_t parent, cfdata_t match, void *aux)
76 {
77 struct amba_attach_args *aaa = aux;
78
79 if (strcmp(aaa->aaa_name, "bcmmbox") != 0)
80 return 0;
81
82 return 1;
83 }
84
85 static void
86 bcmmbox_attach(device_t parent, device_t self, void *aux)
87 {
88 struct bcm2835mbox_softc *sc = device_private(self);
89 struct amba_attach_args *aaa = aux;
90 struct bcmmbox_attach_args baa;
91 int i;
92
93 aprint_naive("\n");
94 aprint_normal(": VC mailbox\n");
95
96 sc->sc_dev = self;
97 sc->sc_iot = aaa->aaa_iot;
98 sc->sc_dmat = aaa->aaa_dmat;
99 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
100 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM);
101 for (i = 0; i < BCM2835_MBOX_NUMCHANNELS; ++i)
102 cv_init(&sc->sc_chan[i], "bcmmbox");
103
104 if (bus_space_map(aaa->aaa_iot, aaa->aaa_addr, BCM2835_MBOX_SIZE, 0,
105 &sc->sc_ioh)) {
106 aprint_error_dev(sc->sc_dev, "unable to map device\n");
107 return;
108 }
109
110 sc->sc_intrh = bcm2835_intr_establish(aaa->aaa_intr, IPL_VM,
111 bcmmbox_intr, sc);
112 if (sc->sc_intrh == NULL) {
113 aprint_error_dev(sc->sc_dev, "unable to establish interrupt\n");
114 return;
115 }
116
117 /* enable mbox interrupt */
118 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_MBOX_CFG,
119 BCM2835_MBOX_CFG_DATAIRQEN);
120
121 if (bcm2835mbox_sc == NULL)
122 bcm2835mbox_sc = sc;
123
124 baa.baa_dmat = aaa->aaa_dmat;
125 sc->sc_platdev = config_found_ia(self, "bcmmboxbus", &baa, NULL);
126 }
127
128 static int
129 bcmmbox_intr1(struct bcm2835mbox_softc *sc, int cv)
130 {
131 uint32_t mbox, chan, data;
132 int ret = 0;
133
134 KASSERT(mutex_owned(&sc->sc_intr_lock));
135
136 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCM2835_MBOX_SIZE,
137 BUS_SPACE_BARRIER_READ);
138
139 while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh,
140 BCM2835_MBOX0_STATUS) & BCM2835_MBOX_STATUS_EMPTY) == 0) {
141
142 mbox = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
143 BCM2835_MBOX0_READ);
144
145 chan = BCM2835_MBOX_CHAN(mbox);
146 data = BCM2835_MBOX_DATA(mbox);
147 ret = 1;
148
149 if (BCM2835_MBOX_CHAN(sc->sc_mbox[chan]) != 0) {
150 aprint_error("bcmmbox_intr: chan %d overflow\n",chan);
151 continue;
152 }
153
154 sc->sc_mbox[chan] = data | BCM2835_MBOX_CHANMASK;
155
156 if (cv)
157 cv_broadcast(&sc->sc_chan[chan]);
158 }
159
160 return ret;
161 }
162
163 static int
164 bcmmbox_intr(void *cookie)
165 {
166 struct bcm2835mbox_softc *sc = cookie;
167 int ret;
168
169 mutex_enter(&sc->sc_intr_lock);
170
171 ret = bcmmbox_intr1(sc, 1);
172
173 mutex_exit(&sc->sc_intr_lock);
174
175 return ret;
176 }
177
178 void
179 bcmmbox_read(uint8_t chan, uint32_t *data)
180 {
181 struct bcm2835mbox_softc *sc = bcm2835mbox_sc;
182
183 KASSERT(sc != NULL);
184
185 mutex_enter(&sc->sc_lock);
186
187 mutex_enter(&sc->sc_intr_lock);
188 while (BCM2835_MBOX_CHAN(sc->sc_mbox[chan]) == 0) {
189 if (cold)
190 bcmmbox_intr1(sc, 0);
191 else
192 cv_wait(&sc->sc_chan[chan], &sc->sc_intr_lock);
193 }
194 *data = BCM2835_MBOX_DATA(sc->sc_mbox[chan]);
195 sc->sc_mbox[chan] = 0;
196 mutex_exit(&sc->sc_intr_lock);
197
198 mutex_exit(&sc->sc_lock);
199 }
200
201 void
202 bcmmbox_write(uint8_t chan, uint32_t data)
203 {
204 struct bcm2835mbox_softc *sc = bcm2835mbox_sc;
205
206 KASSERT(sc != NULL);
207 KASSERT(BCM2835_MBOX_CHAN(chan) == chan);
208 KASSERT(BCM2835_MBOX_CHAN(data) == 0);
209
210 mutex_enter(&sc->sc_lock);
211
212 bcm2835_mbox_write(sc->sc_iot, sc->sc_ioh, chan, data);
213
214 mutex_exit(&sc->sc_lock);
215 }
216
217 int
218 bcmmbox_request(uint8_t chan, void *buf, size_t buflen, uint32_t *pres)
219 {
220 struct bcm2835mbox_softc *sc = bcm2835mbox_sc;
221 void *dma_buf;
222 bus_dmamap_t map;
223 bus_dma_segment_t segs[1];
224 int nsegs;
225 int error;
226
227 KASSERT(sc != NULL);
228
229 error = bus_dmamem_alloc(sc->sc_dmat, buflen, 16, 0, segs, 1,
230 &nsegs, BUS_DMA_WAITOK);
231 if (error)
232 return error;
233 error = bus_dmamem_map(sc->sc_dmat, segs, nsegs, buflen, &dma_buf,
234 BUS_DMA_WAITOK);
235 if (error)
236 goto map_failed;
237 error = bus_dmamap_create(sc->sc_dmat, buflen, 1, buflen, 0,
238 BUS_DMA_WAITOK, &map);
239 if (error)
240 goto create_failed;
241 error = bus_dmamap_load(sc->sc_dmat, map, dma_buf, buflen, NULL,
242 BUS_DMA_WAITOK);
243 if (error)
244 goto load_failed;
245
246 memcpy(dma_buf, buf, buflen);
247
248 bus_dmamap_sync(sc->sc_dmat, map, 0, buflen,
249 BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
250 bcmmbox_write(chan, map->dm_segs[0].ds_addr);
251 bcmmbox_read(chan, pres);
252 bus_dmamap_sync(sc->sc_dmat, map, 0, buflen,
253 BUS_DMASYNC_POSTWRITE|BUS_DMASYNC_POSTREAD);
254
255 memcpy(buf, dma_buf, buflen);
256
257 bus_dmamap_unload(sc->sc_dmat, map);
258 load_failed:
259 bus_dmamap_destroy(sc->sc_dmat, map);
260 create_failed:
261 bus_dmamem_unmap(sc->sc_dmat, dma_buf, buflen);
262 map_failed:
263 bus_dmamem_free(sc->sc_dmat, segs, nsegs);
264
265 return error;
266 }
267