]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/dma.c
kernel: do all fp state fork from procfork() (like pc kernel)
[plan9front.git] / sys / src / 9 / bcm / dma.c
1 /*
2  * bcm2835 dma controller
3  *
4  * simplest to use only channels 0-6
5  *      channels 7-14 have reduced functionality
6  *      channel 15 is at a weird address
7  *      channels 0 and 15 have an "external 128 bit 8 word read FIFO"
8  *        for memory to memory transfers
9  *
10  * Experiments show that only channels 2-5,11-12 work with mmc
11  */
12
13 #include "u.h"
14 #include "../port/lib.h"
15 #include "../port/error.h"
16 #include "mem.h"
17 #include "dat.h"
18 #include "fns.h"
19 #include "io.h"
20
21 #define DMAREGS (VIRTIO+0x7000)
22
23 #define DBG     if(Dbg)
24
25 enum {
26         Nchan           = 7,            /* number of dma channels */
27         Regsize         = 0x100,        /* size of regs for each chan */
28         Cbalign         = 32,           /* control block byte alignment */
29         Dbg             = 0,
30         
31         /* registers for each dma controller */
32         Cs              = 0x00>>2,
33         Conblkad        = 0x04>>2,
34         Ti              = 0x08>>2,
35         Sourcead        = 0x0c>>2,
36         Destad          = 0x10>>2,
37         Txfrlen         = 0x14>>2,
38         Stride          = 0x18>>2,
39         Nextconbk       = 0x1c>>2,
40         Debug           = 0x20>>2,
41
42         /* collective registers */
43         Intstatus       = 0xfe0>>2,
44         Enable          = 0xff0>>2,
45
46         /* Cs */
47         Reset           = 1<<31,
48         Abort           = 1<<30,
49         Error           = 1<<8,
50         Waitwrite       = 1<<6,
51         Waitdreq        = 1<<5,
52         Paused          = 1<<4,
53         Dreq            = 1<<3,
54         Int             = 1<<2,
55         End             = 1<<1,
56         Active          = 1<<0,
57
58         /* Ti */
59         Permapshift= 16,
60         Srcignore       = 1<<11,
61         Srcdreq         = 1<<10,
62         Srcwidth128     = 1<<9,
63         Srcinc          = 1<<8,
64         Destignore      = 1<<7,
65         Destdreq        = 1<<6,
66         Destwidth128    = 1<<5,
67         Destinc         = 1<<4,
68         Waitresp        = 1<<3,
69         Tdmode          = 1<<1,
70         Inten           = 1<<0,
71
72         /* Debug */
73         Lite            = 1<<28,
74         Clrerrors       = 7<<0,
75 };
76
77 typedef struct Ctlr Ctlr;
78 typedef struct Cb Cb;
79
80 struct Ctlr {
81         u32int  *regs;
82         Cb      *cb;
83         Rendez  r;
84         int     dmadone;
85 };
86
87 struct Cb {
88         u32int  ti;
89         u32int  sourcead;
90         u32int  destad;
91         u32int  txfrlen;
92         u32int  stride;
93         u32int  nextconbk;
94         u32int  reserved[2];
95 };
96
97 static Ctlr dma[Nchan];
98 static u32int *dmaregs = (u32int*)DMAREGS;
99
100 static void
101 dump(char *msg, uchar *p, int n)
102 {
103         print("%s", msg);
104         while(n-- > 0)
105                 print(" %2.2x", *p++);
106         print("\n");
107 }
108
109 static void
110 dumpdregs(char *msg, u32int *r)
111 {
112         int i;
113
114         print("%s: %#p =", msg, r);
115         for(i = 0; i < 9; i++)
116                 print(" %8.8uX", r[i]);
117         print("\n");
118 }
119
120 static int
121 dmadone(void *a)
122 {
123         return ((Ctlr*)a)->dmadone;
124 }
125
126 static void
127 dmainterrupt(Ureg*, void *a)
128 {
129         Ctlr *ctlr;
130
131         ctlr = a;
132         ctlr->regs[Cs] = Int;
133         ctlr->dmadone = 1;
134         wakeup(&ctlr->r);
135 }
136
137 void
138 dmastart(int chan, int dev, int dir, void *src, void *dst, int len)
139 {
140         Ctlr *ctlr;
141         Cb *cb;
142         int ti;
143
144         ctlr = &dma[chan];
145         if(ctlr->regs == nil){
146                 ctlr->regs = (u32int*)(DMAREGS + chan*Regsize);
147                 ctlr->cb = xspanalloc(sizeof(Cb), Cbalign, 0);
148                 assert(ctlr->cb != nil);
149                 dmaregs[Enable] |= 1 << chan;
150                 ctlr->regs[Cs] = Reset;
151                 while(ctlr->regs[Cs] & Reset)
152                         ;
153                 intrenable(IRQDMA(chan), dmainterrupt, ctlr, 0, "dma");
154         }
155         cb = ctlr->cb;
156         ti = 0;
157         switch(dir){
158         case DmaD2M:
159                 cachedwbinvse(dst, len);
160                 ti = Srcdreq | Destinc;
161                 cb->sourcead = DMAIO(src);
162                 cb->destad = DMAADDR(dst);
163                 break;
164         case DmaM2D:
165                 cachedwbse(src, len);
166                 ti = Destdreq | Srcinc;
167                 cb->sourcead = DMAADDR(src);
168                 cb->destad = DMAIO(dst);
169                 break;
170         case DmaM2M:
171                 cachedwbse(src, len);
172                 cachedwbinvse(dst, len);
173                 ti = Srcinc | Destinc;
174                 cb->sourcead = DMAADDR(src);
175                 cb->destad = DMAADDR(dst);
176                 break;
177         }
178         cb->ti = ti | dev << Permapshift | Inten;
179         cb->txfrlen = len;
180         cb->stride = 0;
181         cb->nextconbk = 0;
182         cachedwbse(cb, sizeof(Cb));
183         ctlr->regs[Cs] = 0;
184         microdelay(1);
185         ctlr->regs[Conblkad] = DMAADDR(cb);
186         DBG print("dma start: %ux %ux %ux %ux %ux %ux\n",
187                 cb->ti, cb->sourcead, cb->destad, cb->txfrlen,
188                 cb->stride, cb->nextconbk);
189         DBG print("intstatus %ux\n", dmaregs[Intstatus]);
190         dmaregs[Intstatus] = 0;
191         ctlr->regs[Cs] = Int;
192         microdelay(1);
193         coherence();
194         DBG dumpdregs("before Active", ctlr->regs);
195         ctlr->regs[Cs] = Active;
196         DBG dumpdregs("after Active", ctlr->regs);
197 }
198
199 int
200 dmawait(int chan)
201 {
202         Ctlr *ctlr;
203         u32int *r;
204         int s;
205
206         ctlr = &dma[chan];
207         tsleep(&ctlr->r, dmadone, ctlr, 3000);
208         ctlr->dmadone = 0;
209         r = ctlr->regs;
210         DBG dumpdregs("after sleep", r);
211         s = r[Cs];
212         if((s & (Active|End|Error)) != End){
213                 print("dma chan %d %s Cs %ux Debug %ux\n", chan,
214                         (s&End)? "error" : "timeout", s, r[Debug]);
215                 r[Cs] = Reset;
216                 r[Debug] = Clrerrors;
217                 return -1;
218         }
219         r[Cs] = Int|End;
220         return 0;
221 }