changelog shortlog tags branches changeset files file revisions raw help

Mercurial > hg > plan9front / annotate sys/src/9/bcm/dma.c

changeset 7249: 3d3af63a444a
parent: bee572b18071
child: 55d93e47a2de
author: cinap_lenrek@felloff.net
date: Sun, 19 May 2019 16:54:50 +0200
permissions: -rw-r--r--
description: bcm, bcm64: fix cache operations for dma and emmc

always clean AND invalidate caches before dma read,
never just invalidate as the buffer might not be
aligned to cache lines...

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