]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/arm/xscale/i80321/i80321_aau.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / arm / xscale / i80321 / i80321_aau.c
1 /*-
2  * Copyright (c) 2005 Olivier Houchard.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD$");
27
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
33 #include <sys/malloc.h>
34 #include <sys/rman.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/proc.h>
38
39 #include <vm/vm.h>
40 #include <vm/pmap.h>
41 #include <vm/vm_map.h>
42 #include <machine/bus.h>
43 #include <machine/cpu.h>
44 #include <machine/md_var.h>
45
46 #include <arm/xscale/i80321/i80321reg.h>
47 #include <arm/xscale/i80321/i80321var.h>
48 #include <arm/xscale/i80321/iq80321reg.h>
49 #include <arm/xscale/i80321/iq80321var.h>
50 #include <arm/xscale/i80321/i80321_intr.h>
51
52 typedef struct i80321_aaudesc_s {
53         vm_paddr_t next_desc;
54         uint32_t sar[4];
55         vm_paddr_t local_addr;
56         vm_size_t count;
57         uint32_t descr_ctrl;
58 } __packed      i80321_aaudesc_t;
59
60 typedef struct i80321_aauring_s {
61         i80321_aaudesc_t *desc;
62         vm_paddr_t phys_addr;
63         bus_dmamap_t map;
64 } i80321_aauring_t;
65
66 #define AAU_RING_SIZE 64
67
68 struct i80321_aau_softc {
69         bus_space_tag_t sc_st;
70         bus_space_handle_t sc_aau_sh;
71         bus_dma_tag_t dmatag;
72         i80321_aauring_t aauring[AAU_RING_SIZE];
73         int flags;
74 #define BUSY    0x1
75         int unit;
76         struct mtx mtx;
77 };
78
79 static int
80 i80321_aau_probe(device_t dev)
81 {
82         device_set_desc(dev, "I80321 AAU");
83         return (0);
84 }
85
86 static struct i80321_aau_softc *aau_softc;
87
88 static void
89 i80321_mapphys(void *arg, bus_dma_segment_t *segs, int nseg, int error)
90 {
91         vm_paddr_t *addr = (vm_paddr_t *)arg;
92
93         *addr = segs->ds_addr;
94 }
95
96 #define AAU_REG_WRITE(softc, reg, val) \
97     bus_space_write_4((softc)->sc_st, (softc)->sc_aau_sh, \
98         (reg), (val))
99 #define AAU_REG_READ(softc, reg) \
100     bus_space_read_4((softc)->sc_st, (softc)->sc_aau_sh, \
101         (reg))
102
103 static int aau_bzero(void *, int, int);
104
105 static int
106 i80321_aau_attach(device_t dev)
107 {
108         struct i80321_aau_softc *softc = device_get_softc(dev);
109         struct i80321_softc *sc = device_get_softc(device_get_parent(dev));
110         struct i80321_aaudesc_s *aaudescs;
111
112         mtx_init(&softc->mtx, "AAU mtx", NULL, MTX_SPIN);
113         softc->sc_st = sc->sc_st;
114         if (bus_space_subregion(softc->sc_st, sc->sc_sh, VERDE_AAU_BASE,
115             VERDE_AAU_SIZE, &softc->sc_aau_sh) != 0)
116                 panic("%s: unable to subregion AAU registers",
117                     device_get_name(dev));
118         if (bus_dma_tag_create(NULL, sizeof(i80321_aaudesc_t), 0,
119             BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
120             AAU_RING_SIZE * sizeof(i80321_aaudesc_t),
121             1, sizeof(i80321_aaudesc_t), BUS_DMA_ALLOCNOW, busdma_lock_mutex,
122             &Giant, &softc->dmatag))
123                 panic("Couldn't create a dma tag");
124         if (bus_dmamem_alloc(softc->dmatag, (void **)&aaudescs,
125             BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &softc->aauring[0].map))
126                 panic("Couldn't alloc dma memory");
127
128         for (int i = 0; i < AAU_RING_SIZE; i++) {
129                 if (i > 0)
130                         if (bus_dmamap_create(softc->dmatag, 0,
131                             &softc->aauring[i].map))
132                                 panic("Couldn't create dma map");
133                 softc->aauring[i].desc = &aaudescs[i];
134                 bus_dmamap_load(softc->dmatag, softc->aauring[i].map,
135                     softc->aauring[i].desc, sizeof(i80321_aaudesc_t),
136                     i80321_mapphys, &softc->aauring[i].phys_addr, 0);
137                 bzero(softc->aauring[i].desc, sizeof(i80321_aaudesc_t));
138         }
139         aau_softc = softc;
140         _arm_bzero = aau_bzero;
141         _min_bzero_size = 1024;
142         return (0);
143 }
144
145 static __inline void
146 test_virt_addr(void *addr, int len)
147 {
148         int to_nextpage;
149
150         while (len > 0) {
151                 *(char *)addr = 0;
152                 to_nextpage = ((vm_offset_t)addr & ~PAGE_MASK) +
153                     PAGE_SIZE - (vm_offset_t)addr;
154                 if (to_nextpage >= len)
155                         break;
156                 len -= to_nextpage;
157                 addr = (void *)((vm_offset_t)addr + to_nextpage);
158         }
159 }
160
161 static int
162 aau_bzero(void *dst, int len, int flags)
163 {
164         struct i80321_aau_softc *sc = aau_softc;
165         i80321_aaudesc_t *desc;
166         int ret;
167         int csr;
168         int descnb = 0;
169         int tmplen = len;
170         int to_nextpagedst;
171         int min_hop;
172         vm_paddr_t pa, tmppa;
173
174         if (!sc)
175                 return (-1);
176         mtx_lock_spin(&sc->mtx);
177         if (sc->flags & BUSY) {
178                 mtx_unlock_spin(&sc->mtx);
179                 return (-1);
180         }
181         sc->flags |= BUSY;
182         mtx_unlock_spin(&sc->mtx);
183         desc = sc->aauring[0].desc;
184         if (flags & IS_PHYSICAL) {
185                 desc->local_addr = (vm_paddr_t)dst;
186                 desc->next_desc = 0;
187                 desc->count = len;
188                 desc->descr_ctrl = 2 << 1 | 1 << 31; /* Fill, enable dest write */
189                 bus_dmamap_sync(sc->dmatag, sc->aauring[0].map,
190                     BUS_DMASYNC_PREWRITE);
191         } else {
192                 test_virt_addr(dst, len);
193                 if ((vm_offset_t)dst & (31))
194                         cpu_dcache_wb_range((vm_offset_t)dst & ~31, 32);
195                 if (((vm_offset_t)dst + len) & 31)
196                         cpu_dcache_wb_range(((vm_offset_t)dst + len) & ~31,
197                             32);
198                 cpu_dcache_inv_range((vm_offset_t)dst, len);
199                 while (tmplen > 0) {
200                         pa = vtophys(dst);
201                         to_nextpagedst = ((vm_offset_t)dst & ~PAGE_MASK) +
202                             PAGE_SIZE - (vm_offset_t)dst;
203                         while (to_nextpagedst < tmplen) {
204                                 tmppa = vtophys((vm_offset_t)dst +
205                                     to_nextpagedst);
206                                 if (tmppa != pa + to_nextpagedst)
207                                         break;
208                                 to_nextpagedst += PAGE_SIZE;
209                         }
210                         min_hop = to_nextpagedst;
211                         if (min_hop < 64) {
212                                 tmplen -= min_hop;
213                                 bzero(dst, min_hop);
214                                 cpu_dcache_wbinv_range((vm_offset_t)dst,
215                                     min_hop);
216
217                                 dst = (void *)((vm_offset_t)dst + min_hop);
218                                 if (tmplen <= 0 && descnb > 0) {
219                                         sc->aauring[descnb - 1].desc->next_desc
220                                             = 0;
221                                         bus_dmamap_sync(sc->dmatag,
222                                             sc->aauring[descnb - 1].map,
223                                             BUS_DMASYNC_PREWRITE);
224                                 }
225                                 continue;
226                         }
227                         desc->local_addr = pa;
228                         desc->count = tmplen > min_hop ? min_hop : tmplen;
229                         desc->descr_ctrl = 2 << 1 | 1 << 31; /* Fill, enable dest write */;
230                         if (min_hop < tmplen) {
231                                 tmplen -= min_hop;
232                                 dst = (void *)((vm_offset_t)dst + min_hop);
233                         } else
234                                 tmplen = 0;
235                         if (descnb + 1 >= AAU_RING_SIZE) {
236                                 mtx_lock_spin(&sc->mtx);
237                                 sc->flags &= ~BUSY;
238                                 mtx_unlock_spin(&sc->mtx);
239                                 return (-1);
240                         }
241                         if (tmplen > 0) {
242                                 desc->next_desc = sc->aauring[descnb + 1].
243                                     phys_addr;
244                                 bus_dmamap_sync(sc->dmatag,
245                                     sc->aauring[descnb].map,
246                                     BUS_DMASYNC_PREWRITE);
247                                 desc = sc->aauring[descnb + 1].desc;
248                                 descnb++;
249                         } else {
250                                 desc->next_desc = 0;
251                                 bus_dmamap_sync(sc->dmatag,
252                                     sc->aauring[descnb].map,
253                                     BUS_DMASYNC_PREWRITE);
254                         }
255                                                                         
256                 }
257
258         }
259         AAU_REG_WRITE(sc, 0x0c /* Descriptor addr */,
260             sc->aauring[0].phys_addr);
261         AAU_REG_WRITE(sc, 0 /* Control register */, 1 << 0/* Start transfer */);
262         while ((csr = AAU_REG_READ(sc, 0x4)) & (1 << 10));
263         /* Wait until it's done. */
264         if (csr & (1 << 5)) /* error */
265                 ret = -1;
266         else
267                 ret = 0;
268         /* Clear the interrupt. */
269         AAU_REG_WRITE(sc, 0x4, csr);
270         /* Stop the AAU. */
271         AAU_REG_WRITE(sc, 0, 0);
272         mtx_lock_spin(&sc->mtx);
273         sc->flags &= ~BUSY;
274         mtx_unlock_spin(&sc->mtx);
275         return (ret);
276 }
277
278 static device_method_t i80321_aau_methods[] = {
279         DEVMETHOD(device_probe, i80321_aau_probe),
280         DEVMETHOD(device_attach, i80321_aau_attach),
281         {0, 0},
282 };
283
284 static driver_t i80321_aau_driver = {
285         "i80321_aau",
286         i80321_aau_methods,
287         sizeof(struct i80321_aau_softc),
288 };
289
290 static devclass_t i80321_aau_devclass;
291
292 DRIVER_MODULE(i80321_aau, iq, i80321_aau_driver, i80321_aau_devclass, 0, 0);