M bitsy/devflash.c => bitsy/devflash.c +504 -74
@@ 6,18 6,6 @@
#include "io.h"
#include "../port/error.h"
-/* flash partitions */
-typedef struct FlashPart FlashPart;
-struct FlashPart
-{
- QLock;
- char name[NAMELEN];
- ulong start; /* byte offsets */
- ulong end;
-};
-
-static ulong *flash = (ulong*)FLASHZERO;
-
/*
* on the bitsy, all 32 bit accesses to flash are mapped to two 16 bit
* accesses, one to the low half of the chip and the other to the high
@@ 27,66 15,117 @@ static ulong *flash = (ulong*)FLASHZERO;
* sectors for each request erase request.
*/
-/*
- * common flash memory interface
- */
-struct CFIid
-{
- ulong q;
- ulong r;
- ulong y;
- ulong cmd_set;
- ulong vendor_alg;
- ulong ext_alg_addr[2];
- ulong alt_cmd_set;
- ulong alt_vendor_alg;
- ulong alt_ext_ald_addr[2];
-
+#define mirror(x) (((x)<<16)|(x))
+
+/* this defines a contiguous set of erase blocks of one size */
+typedef struct FlashRegion FlashRegion;
+struct FlashRegion
+{
+ ulong addr; /* start of region */
+ ulong end; /* end of region + 1 */
+ ulong n; /* number of blocks */
+ ulong size; /* size of each block */
};
-struct CFIsys
-{
- ulong vcc_min; /* 100 mv */
- ulong vcc_max;
- ulong vpp_min;
- ulong vpp_max;
- ulong word_wr_to; /* 2**n µs */
- ulong buf_wr_to; /* 2**n µs */
- ulong block_erase_to; /* 2**n ms */
- ulong chip_erase_to; /* 2**n ms */
- ulong max_word_wr_to; /* 2**n µs */
- ulong max_buf_wr_to; /* 2**n µs */
- ulong max_block_erase_to; /* 2**n ms */
- ulong max_chip_erase_to; /* 2**n ms */
+/* this defines a particular access algorithm */
+typedef struct FlashAlg FlashAlg;
+struct FlashAlg
+{
+ int id;
+ char *name;
+ void (*identify)(void); /* identify device */
+ void (*erase)(ulong); /* erase a region */
+ void (*write)(void*, long, ulong); /* write a region */
};
-struct CFIgeom
+static void ise_id(void);
+static void ise_erase(ulong);
+static void ise_write(void*, long, ulong);
+
+static void afs_id(void);
+static void afs_erase(ulong);
+static void afs_write(void*, long, ulong);
+
+FlashAlg falg[] =
{
- ulong size; /* 2**n bytes */
- ulong dev_code; /* ??? */
- ulong max_multi; /* max bytes in a multibyte write */
- ulong nregion; /* number of erase regions */
- ulong region[1]; /* erase region info */
+ { 1, "Intel/Sharp Extended", ise_id, ise_erase, ise_write },
+ { 2, "AMD/Fujitsu Standard", afs_id, afs_erase, afs_write },
};
-#define mirror(x) (((x)<<16)|(x))
+struct
+{
+ RWlock;
+ ulong *p;
+ ushort algid; /* access algorithm */
+ FlashAlg *alg;
+ ushort manid; /* manufacturer id */
+ ushort devid; /* device id */
+ ulong size; /* size in bytes */
+ int wbsize; /* size of write buffer */
+ ulong nr; /* number of regions */
+ uchar bootprotect;
+ FlashRegion r[32];
+ ulong *wb; /* staging area for write buffer */
+} flash;
-void
-cfiquery(void)
+/*
+ * common flash interface
+ */
+static uchar
+cfigetc(int off)
{
- struct CFIid *id;
+ uchar rv;
- flash[0x55] = mirror(0x98);
- id = (struct CFIid*)&flash[0x10];
- if(id.q != 'q' || id.r != 'r' || id.y != 'y')
- print("CFI not supported by flash\n");
-
- flash[0x55] = mirror(0xFF);
+ flash.p[0x55] = mirror(0x98);
+ rv = flash.p[off];
+ flash.p[0x55] = mirror(0xFF);
+ return rv;
}
-void
-cfigeom(void)
+static ushort
+cfigets(int off)
+{
+ return (cfigetc(off+1)<<8)|cfigetc(off);
+}
+
+static ulong
+cfigetl(int off)
+{
+ return (cfigetc(off+3)<<24)|(cfigetc(off+2)<<16)|
+ (cfigetc(off+1)<<8)|cfigetc(off);
+}
+
+static void
+cfiquery(void)
{
+ uchar q, r, y;
+ ulong x, addr;
+
+ q = cfigetc(0x10);
+ r = cfigetc(0x11);
+ y = cfigetc(0x12);
+ if(q != 'Q' || r != 'R' || y != 'Y'){
+ print("cfi query failed: %ux %ux %ux\n", q, r, y);
+ return;
+ }
+ flash.algid = cfigetc(0x13);
+ flash.size = 1<<(cfigetc(0x27)+1);
+ flash.wbsize = 1<<(cfigetc(0x2a)+1);
+ flash.nr = cfigetc(0x2c);
+ if(flash.nr > nelem(flash.r)){
+ print("cfi reports > %d regions\n", nelem(flash.r));
+ flash.nr = nelem(flash.r);
+ }
+ addr = 0;
+ for(q = 0; q < flash.nr; q++){
+ x = cfigetl(q+0x2d);
+ flash.r[q].size = 2*256*(x>>16);
+ flash.r[q].n = (x&0xffff)+1;
+ flash.r[q].addr = addr;
+ addr += flash.r[q].size*flash.r[q].n;
+ flash.r[q].end = addr;
+ }
+ flash.wb = malloc(flash.wbsize);
}
/*
@@ 95,30 134,35 @@ cfigeom(void)
enum
{
- Qf0=1,
- Qf1,
- Qf2,
- Qf3,
+ Qfctl=1,
+ Qfdata,
};
Dirtab flashdir[]={
- "f0", { Qf0, 0 }, 0, 0664,
- "f1", { Qf1, 0 }, 0, 0664,
- "f2", { Qf2, 0 }, 0, 0664,
- "f3", { Qf3, 0 }, 0, 0664,
+ "flashctl", { Qfctl, 0 }, 0, 0664,
+ "flashdata", { Qfdata, 0 }, 0, 0660,
};
void
flashinit(void)
{
+ int i;
+
+ flash.p = (ulong*)FLASHZERO;
cfiquery();
- cfigeom();
+ for(i = 0; i < nelem(falg); i++)
+ if(flash.algid == falg[i].id){
+ flash.alg = &falg[i];
+ (*flash.alg->identify)();
+ break;
+ }
+ flash.bootprotect = 1;
}
static Chan*
flashattach(char* spec)
{
- return devattach('r', spec);
+ return devattach('F', spec);
}
static int
@@ 150,16 194,185 @@ flashclose(Chan*)
static long
flashread(Chan* c, void* a, long n, vlong off)
{
- USED(c, a, off);
- error("UUO");
+ char *buf, *p, *e;
+ int i;
+
+ if(c->qid.path&CHDIR)
+ return devdirread(c, a, n, flashdir, nelem(flashdir), devgen);
+ switch(c->qid.path){
+ default:
+ error(Eperm);
+ case Qfctl:
+ buf = smalloc(1024);
+ e = buf + 1024;
+ p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9lux\n", flash.size,
+ flash.wbsize, flash.manid, flash.devid);
+ for(i = 0; i < flash.nr; i++)
+ p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", flash.r[i].addr,
+ flash.r[i].n, flash.r[i].size);
+ n = readstr(off, a, n, buf);
+ free(buf);
+ break;
+ case Qfdata:
+ if(!iseve())
+ error(Eperm);
+ if(off >= flash.size)
+ return 0;
+ if(off + n > flash.size)
+ n = flash.size - off;
+ rlock(&flash);
+ if(waserror()){
+ runlock(&flash);
+ nexterror();
+ }
+ memmove(a, ((uchar*)FLASHZERO)+off, n);
+ runlock(&flash);
+ poperror();
+ break;
+ }
+ return n;
+}
+
+static void
+bootprotect(ulong addr)
+{
+ FlashRegion *r;
+
+ if(flash.bootprotect == 0)
+ return;
+ if(flash.nr == 0)
+ error("writing over boot loader disallowed");
+ r = flash.r;
+ if(addr >= r->addr && addr < r->addr + r->size)
+ error("writing over boot loader disallowed");
+}
+
+ulong
+blockstart(ulong addr)
+{
+ FlashRegion *r, *e;
+ ulong x;
+
+ r = flash.r;
+ for(e = &flash.r[flash.nr]; r < e; r++)
+ if(addr >= r->addr && addr < r->end){
+ x = addr - r->addr;
+ x /= r->size;
+ return r->addr + x*r->size;
+ }
+
+ return (ulong)-1;
+}
+
+ulong
+blockend(ulong addr)
+{
+ FlashRegion *r, *e;
+ ulong x;
+
+ r = flash.r;
+ for(e = &flash.r[flash.nr]; r < e; r++)
+ if(addr >= r->addr && addr < r->end){
+ x = addr - r->addr;
+ x /= r->size;
+ return r->addr + (x+1)*r->size;
+ }
+
+ return (ulong)-1;
+}
+
+static long
+flashctlwrite(char *p, long n)
+{
+ Cmdbuf *cmd;
+ ulong addr;
+
+ cmd = parsecmd(p, n);
+ wlock(&flash);
+ if(waserror()){
+ wunlock(&flash);
+ nexterror();
+ }
+ if(strcmp(cmd->f[0], "erase") == 0){
+ if(cmd->nf != 2)
+ error(Ebadarg);
+ addr = atoi(cmd->f[1]);
+ if(addr != blockstart(addr))
+ error("erase must be a block boundary");
+ bootprotect(addr);
+ (*flash.alg->erase)(addr);
+ } else if(strcmp(cmd->f[0], "protectboot") == 0){
+ if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0)
+ flash.bootprotect = 1;
+ else
+ flash.bootprotect = 0;
+ } else
+ error(Ebadarg);
+ poperror();
+ wunlock(&flash);
+ free(cmd);
+
+ return n;
+}
+
+static long
+flashdatawrite(uchar *p, long n, long off)
+{
+ uchar *end;
+ int m;
+ long ooff = off;
+ uchar *op = p;
+
+ if((off & 0x3) || (n & 0x3))
+ error("only quad writes");
+ if(off >= flash.size || off+n > flash.size || n <= 0)
+ error(Ebadarg);
+
+ wlock(&flash);
+ if(waserror()){
+ wunlock(&flash);
+ nexterror();
+ }
+
+ /* make sure we're not writing the boot sector */
+ bootprotect(off);
+
+ /* (*flash.alg->write) can't cross blocks */
+ for(end = p + n; p < end; p += m){
+ m = blockend(off) - off;
+ if(m > end - p)
+ m = end - p;
+ (*flash.alg->write)(p, m, off);
+ off += m;
+ }
+
+ /* make sure write succeeded */
+ if(memcmp(op, &flash.p[ooff>>2], n) != 0)
+ error("written bytes don't match");
+
+ wunlock(&flash);
+ poperror();
+
return n;
}
static long
-flashwrite(Chan* c, void* a, long n, vlong)
+flashwrite(Chan* c, void* a, long n, vlong off)
{
- USED(c, a, off);
- error("UUO");
+ if(c->qid.path & CHDIR)
+ error(Eperm);
+
+ if(!iseve())
+ error(Eperm);
+
+ switch(c->qid.path){
+ default:
+ panic("flashwrite");
+ case Qfctl:
+ return flashctlwrite(a, n);
+ case Qfdata:
+ return flashdatawrite(a, n, off);
+ }
return n;
}
@@ 183,3 396,220 @@ Dev flashdevtab = {
devremove,
devwstat,
};
+
+
+/* intel/sharp extended command set */
+static void
+ise_reset(void)
+{
+ flash.p[0x55] = mirror(0xff); /* reset */
+}
+static void
+ise_id(void)
+{
+ ise_reset();
+ flash.p[0x555] = mirror(0x90); /* uncover vendor info */
+ flash.manid = flash.p[00];
+ flash.devid = flash.p[01];
+ ise_reset();
+}
+static void
+ise_clearerror(void)
+{
+ flash.p[0x100] = mirror(0x50);
+
+}
+static void
+ise_error(int bank, ulong status)
+{
+ char err[ERRLEN];
+
+ if(status & (1<<3)){
+ sprint(err, "flash%d: low prog voltage", bank);
+ error(err);
+ }
+ if(status & (1<<1)){
+ sprint(err, "flash%d: block locked", bank);
+ error(err);
+ }
+ if(status & (1<<5)){
+ sprint(err, "flash%d: i/o error", bank);
+ error(err);
+ }
+}
+static void
+ise_erase(ulong addr)
+{
+ ulong start;
+ ulong x;
+
+ addr >>= 2; /* convert to ulong offset */
+
+ flashprogpower(1);
+ flash.p[addr] = mirror(0x20);
+ flash.p[addr] = mirror(0xd0);
+ start = m->ticks;
+ do {
+ x = flash.p[addr];
+ if((x & mirror(1<<7)) == mirror(1<<7))
+ break;
+ } while(TK2MS(m->ticks-start) < 1500);
+ flashprogpower(0);
+
+ ise_clearerror();
+ ise_error(0, x);
+ ise_error(1, x>>16);
+
+ ise_reset();
+}
+/*
+ * flash writing goes about 16 times faster if we use
+ * the write buffer. We fill the write buffer and then
+ * issue the write request. After the write request,
+ * subsequent reads will yield the status register or,
+ * since error bits are sticky, another write buffer can
+ * be filled and written.
+ *
+ * On timeout, we issue a read status register request so
+ * that the status register can be read no matter how we
+ * exit.
+ */
+static int
+ise_wbwrite(ulong *p, int n, ulong off)
+{
+ ulong start;
+ int i;
+
+ /* copy out of user space to avoid faults later */
+ memmove(flash.wb, p, n*4);
+ p = flash.wb;
+
+ /* put flash into write buffer mode */
+ start = m->ticks;
+ for(;;) {
+ /* request write buffer mode */
+ flash.p[off] = mirror(0xe8);
+
+ /* look at extended status reg for status */
+ if((flash.p[off] & mirror(1<<7)) == mirror(1<<7))
+ break;
+
+ /* didn't work, keep trying for 2 secs */
+ if(TK2MS(m->ticks-start) > 2000){
+ /* set up to read status */
+ flash.p[off] = mirror(0x70);
+ return -1;
+ }
+ }
+
+ /* fill write buffer */
+ flash.p[off] = mirror(n-1);
+ for(i = 0; i < n; i++)
+ flash.p[off+i] = *p++;
+
+ /* program from buffer */
+ flash.p[off] = mirror(0xd0);
+
+ /* subsequent reads will return status about the write */
+
+ return n;
+}
+static void
+ise_write(void *a, long n, ulong off)
+{
+ ulong *p, *end;
+ int i, wbsize;
+ ulong x, start, ooff;
+
+ /* everything in terms of ulongs */
+ wbsize = flash.wbsize>>2;
+ off >>= 2;
+ n >>= 2;
+ p = a;
+ ooff = off;
+
+ /* first see if write will succeed */
+ for(i = 0; i < n; i++)
+ if((p[i] & flash.p[off+i]) != p[i])
+ error("flash needs erase");
+
+ if(waserror()){
+ ise_reset();
+ flashprogpower(0);
+ nexterror();
+ }
+ flashprogpower(1);
+
+ /*
+ * use the first write to reach
+ * a write buffer boundary. the intel maunal
+ * says writes startng at wb boundaries
+ * maximize speed.
+ */
+ i = wbsize - (off & (wbsize-1));
+ for(end = p + n; p < end;){
+ if(i > end - p)
+ i = end - p;
+
+ if(ise_wbwrite(p, i, off) != i)
+ break;
+
+ off += i;
+ p += i;
+ i = wbsize;
+ }
+
+ /* wait till the programming is done */
+ start = m->ticks;
+ do {
+ x = flash.p[ooff];
+ if((x & mirror(1<<7)) == mirror(1<<7))
+ break;
+ } while(TK2MS(m->ticks-start) < 1000);
+
+ ise_clearerror();
+ ise_error(0, x);
+ ise_error(1, x>>16);
+
+ ise_reset();
+ flashprogpower(0);
+ poperror();
+}
+
+/* amd/fujitsu standard command set
+ * I don't have an amd chipset to work with
+ * so I'm loathe to write this yet. If someone
+ * else does, please send it to me and I'll
+ * incorporate it -- presotto@bell-labs.com
+ */
+static void
+afs_reset(void)
+{
+ flash.p[0x55] = mirror(0xf0); /* reset */
+}
+static void
+afs_id(void)
+{
+ afs_reset();
+ flash.p[0x55] = mirror(0xf0); /* reset */
+ flash.p[0x555] = mirror(0xaa); /* query vendor block */
+ flash.p[0x2aa] = mirror(0x55);
+ flash.p[0x555] = mirror(0x90);
+ flash.manid = flash.p[00];
+ afs_reset();
+ flash.p[0x555] = mirror(0xaa); /* query vendor block */
+ flash.p[0x2aa] = mirror(0x55);
+ flash.p[0x555] = mirror(0x90);
+ flash.devid = flash.p[01];
+ afs_reset();
+}
+static void
+afs_erase(ulong)
+{
+ error("amd/fujistsu erase not implemented");
+}
+static void
+afs_write(void*, long, ulong)
+{
+ error("amd/fujistsu write not implemented");
+}
M bitsy/devuda1341.c => bitsy/devuda1341.c +185 -95
@@ 41,14 41,14 @@ static int debug = 1;
*/
/*
- * L3 setup and hold times (expressed in us)
+ * L3 setup and hold times (expressed in µs)
*/
#define L3_DataSetupTime 1 /* 190 ns */
#define L3_DataHoldTime 1 /* 30 ns */
#define L3_ModeSetupTime 1 /* 190 ns */
#define L3_ModeHoldTime 1 /* 190 ns */
-#define L3_ClockHighTime 100 /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
-#define L3_ClockLowTime 100 /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
+#define L3_ClockHighTime 10 /* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
+#define L3_ClockLowTime 10 /* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
#define L3_HaltTime 1 /* 190 ns */
/* UDA 1341 Registers */
@@ 76,7 76,7 @@ enum {
#define UDA1341_DATA0 0
#define UDA1341_DATA1 1
#define UDA1341_STATUS 2
-#define UDA1341_L3Addr 5
+#define UDA1341_L3Addr 0x14
typedef struct AQueue AQueue;
typedef struct Buf Buf;
@@ 90,7 90,7 @@ enum
Qstatus,
Fmono = 1,
- Fin = 2,
+ Fin = 2,
Fout = 4,
Aclosed = 0,
@@ 98,11 98,7 @@ enum
Awrite,
Vaudio = 0,
- Vsynth,
- Vcd,
- Vline,
Vmic,
- Vspeaker,
Vtreb,
Vbass,
Vspeed,
@@ 164,6 160,7 @@ static struct
ulong totaldma;
ulong idledma;
ulong faildma;
+ ulong samedma;
} iostats;
static struct
@@ 174,15 171,11 @@ static struct
int irval;
} volumes[] =
{
-[Vaudio] "audio", Fout, 50, 50,
-[Vsynth] "synth", Fin|Fout, 0, 0,
-[Vcd] "cd", Fin|Fout, 0, 0,
-[Vline] "line", Fin|Fout, 0, 0,
-[Vmic] "mic", Fin|Fout|Fmono, 0, 0,
-[Vspeaker] "speaker", Fout|Fmono, 0, 0,
+[Vaudio] "audio", Fout|Fmono, 50, 50,
+[Vmic] "mic", Fin, 0, 0,
-[Vtreb] "treb", Fout, 50, 50,
-[Vbass] "bass", Fout, 50, 50,
+[Vtreb] "treb", Fout|Fmono, 50, 50,
+[Vbass] "bass", Fout|Fmono, 50, 50,
[Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed,
0
@@ 420,19 413,24 @@ audioinit(void)
/* do nothing */
}
+uchar status0 = 0x22;
+uchar status1 = 0x80;
+uchar data00 = 0x00; /* volume control, bits 0 – 5 */
+uchar data01 = 0x40;
+uchar data02 = 0x90;
+ushort data0e2 = 0xf2c2;
+ushort data0e4 = 0xf3c4;
+ushort data0e6 = 0xe3c6;
+
static void
-audioenable(void)
+enable(void)
{
- uchar data[2];
+ ushort data;
L3_init();
/* Setup the uarts */
- ppcregs->assignment &= ~(1<<18);
-
- gpioregs->altfunc &= ~(GPIO_SSP_TXD_o | GPIO_SSP_RXD_i | GPIO_SSP_SCLK_o | GPIO_SSP_SFRM_o);
- gpioregs->altfunc |= (GPIO_SSP_CLK_i);
- gpioregs->direction &= ~(GPIO_SSP_CLK_i);
+ ppcregs->assignment &= ~(1<<18);
sspregs->control0 = 0;
sspregs->control0 = 0x031f; /* 16 bits, TI frames, serial clock rate 3 */
@@ 445,79 443,159 @@ audioenable(void)
audiomute(0);
/* external clock configured for 44100 samples/sec */
- gpioregs->direction |= (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o);
-/* This is purportedly the wrong way round: 0 should be set, 1 cleared */
- gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
-// gpioregs->clear = GPIO_CLK_SET1_o;
+ gpioregs->set = GPIO_CLK_SET0_o;
+ gpioregs->clear = GPIO_CLK_SET1_o;
/* Wait for the UDA1341 to wake up */
delay(100);
/* Reset the chip */
- data[0] = 2<<UdaStatusSC | 1<<UdaStatusIF | 1<<UdaStatusRST;
- L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 );
- gpioregs->set = EGPIO_codec_reset;
+ data = status0 | 1<<UdaStatusRST;
+ L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&data, 1 );
gpioregs->clear = EGPIO_codec_reset;
+ gpioregs->set = EGPIO_codec_reset;
/* write uda 1341 status[0] */
- data[0] &= ~(1<<UdaStatusRST); /* clear reset */
- L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 );
+ L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status0, 1 );
+ L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1);
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e2, 2);
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e4, 2 );
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e6, 2 );
- /* write uda 1341 status[1] */
- data[0] = 0x80 | 0x3<<UdaStatusPC | 1<<UdaStatusIGS | 1<<UdaStatusOGS;
- L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1);
+ if (debug) {
+ print("uchar status0 = 0x%2.2ux\n", status0);
+ print("uchar status1 = 0x%2.2ux\n", status1);
+ print("uchar data02 = 0x%2.2ux\n", data02);
+ print("ushort data0e2 = 0x%4.4ux\n", data0e2);
+ print("ushort data0e4 = 0x%4.4ux\n", data0e4);
+ print("ushort data0e6 = 0x%4.4ux\n", data0e6);
+ print("#A: audio enabled\n");
+ print("\tsspregs->control0 = 0x%lux\n", sspregs->control0);
+ print("\tsspregs->control1 = 0x%lux\n", sspregs->control1);
+ }
+}
- /* write uda 1341 data0[0] (volume) */
- data[0] = 15 & 0x3f; /* 6 bits, others must be 0 */
- L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1);
+static void
+resetlevel(void)
+{
+ int i;
- /* write uda 1341 data0[2] (mode switch) */
- data[0] = 0x80 | 3; /* mode = 3 */
- L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1);
+ for(i=0; volumes[i].name; i++) {
+ audio.lovol[i] = volumes[i].ilval;
+ audio.rovol[i] = volumes[i].irval;
+ audio.livol[i] = volumes[i].ilval;
+ audio.rivol[i] = volumes[i].irval;
+ }
+}
- /* set mixer mode and level */
- data[0] = 0xc0 | 2 /* ext address */;
- data[1] = 0xe0 | 2 | 4 << 2; /* mixer mode and mic level */
- L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2);
+static void
+mxvolume(void) {
+ int *left, *right;
- /* set agc control and input amplifier gain */
- data[0] = 0xc0 | 4 /* ext address */;
- data[1] = 0xe0 | 1<<4 | 3; /* AGC control and input ampl gain */
- L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 );
+ if(audio.amode & Aread){
+ left = audio.livol;
+ right = audio.rivol;
+ }
+ if(audio.amode & Awrite){
+ left = audio.lovol;
+ right = audio.rovol;
+ data00 &= ~0x3f;
+ data00 |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data00, 1);
+ if (debug) print("uchar data00 = 0x%2.2ux (audio out)\n", data00);
+ if (left[Vtreb]+right[Vtreb] <= 100
+ && left[Vbass]+right[Vbass] <= 100) {
+ /* settings neutral */
+ data02 &= ~0x03;
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1);
+ if (debug) print("uchar data02 = 0x%2.2ux (mode flat)\n", data02);
+ } else {
+ data02 |= 0x03;
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1);
+ if (debug) print("uchar data02 = 0x%2.2ux (mode boost)\n", data02);
+ data01 |= ~0x3f;
+ data01 |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
+ data01 |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data01, 1);
+ if (debug) print("uchar data01 = 0x%2.2ux (bass&treb)\n", data01);
+ }
+ }
- /* set agc time constant and output level */
- data[0] = 0xc0 | 6 /* ext address */;
- data[1] = 0xe0 | 0<<2 | 3; /* agc time constant and output level */
- L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 );
+}
+static void
+outenable(void) {
+ /* turn on DAC, set output gain switch */
+ status1 |= 0x41;
+ L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
+ /* set volume */
+ data00 |= 0xf;
+ L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data00, 1);
if (debug) {
- print("#A: audio enabled\n");
- print("\tsspregs->control0 = 0x%lux\n", sspregs->control0);
- print("\tsspregs->control1 = 0x%lux\n", sspregs->control1);
+ print("uchar status1 = 0x%2.2ux\n", status1);
+ print("uchar data00 = 0x%2.2ux\n", data00);
}
}
static void
-sendaudio(IOstate *b) {
+outdisable(void) {
+ /* turn off DAC, clear output gain switch */
+ status1 &= ~0x41;
+ L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
+ if (debug) {
+ print("uchar status1 = 0x%2.2ux\n", status1);
+ }
+}
+
+static void
+inenable(void) {
+ /* turn on ADC, set input gain switch */
+ status1 |= 0x22;
+ L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
+ if (debug) {
+ print("uchar status1 = 0x%2.2ux\n", status1);
+ }
+}
+
+static void
+indisable(void) {
+ /* turn off ADC, clear input gain switch */
+ status1 &= ~0x22;
+ L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
+ if (debug) {
+ print("uchar status1 = 0x%2.2ux\n", status1);
+ }
+}
+
+static void
+sendaudio(IOstate *s) {
/* interrupt routine calls this too */
int n;
if (debug > 1) print("#A: sendaudio\n");
- ilock(&b->ilock);
- while (b->next != b->filling) {
- assert(b->next->nbytes);
- if ((n = dmastart(b->dma, b->next->virt, b->next->nbytes)) == 0) {
+ ilock(&s->ilock);
+ while (s->next != s->filling) {
+ assert(s->next->nbytes);
+ if ((n = dmastart(s->dma, s->next->virt, s->next->nbytes)) == 0) {
iostats.faildma++;
break;
}
iostats.totaldma++;
- if (n == 1) iostats.idledma++;
- if (debug > 1) print("#A: dmastart @%p\n", b->next);
- b->next->nbytes = 0;
- b->next++;
- if (b->next == &b->buf[Nbuf])
- b->next = &b->buf[0];
+ switch (n) {
+ case 1:
+ iostats.idledma++;
+ break;
+ case 3:
+ iostats.faildma++;
+ break;
+ }
+ if (debug > 1) print("#A: dmastart @%p\n", s->next);
+ s->next->nbytes = 0;
+ s->next++;
+ if (s->next == &s->buf[Nbuf])
+ s->next = &s->buf[0];
}
- iunlock(&b->ilock);
+ iunlock(&s->ilock);
}
static void
@@ 557,6 635,7 @@ audiostat(Chan *c, char *db)
static Chan*
audioopen(Chan *c, int omode)
{
+ IOstate *s;
switch(c->qid.path & ~CHDIR) {
default:
@@ 580,29 659,33 @@ audioopen(Chan *c, int omode)
qunlock(&audio);
error(Einuse);
}
- audioenable();
+ enable();
memset(&iostats, 0, sizeof(iostats));
if (omode & Aread) {
+ inenable();
+ s = &audio.i;
/* read */
audio.amode |= Aread;
if(audio.i.bufinit == 0)
bufinit(&audio.i);
setempty(&audio.i);
- audio.i.chan = c;
- audio.i.dma = dmaalloc(0, 0, 8, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o);
+ s->chan = c;
+ s->dma = dmaalloc(0, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o);
}
if (omode & 0x2) {
+ outenable();
+ s = &audio.o;
/* write */
// amplifierpower(1);
// audiomute(0);
audio.amode |= Awrite;
- if(audio.o.bufinit == 0)
- bufinit(&audio.o);
- setempty(&audio.o);
- audio.o.chan = c;
- audio.o.dma = dmaalloc(0, 0, 8, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)&audio.o);
+ if(s->bufinit == 0)
+ bufinit(s);
+ setempty(s);
+ s->chan = c;
+ s->dma = dmaalloc(0, 0, 4, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)s);
}
-// mxvolume();
+ mxvolume();
qunlock(&audio);
if (debug) print("#A: open done\n");
break;
@@ 618,6 701,7 @@ audioopen(Chan *c, int omode)
static void
audioclose(Chan *c)
{
+ IOstate *s;
switch(c->qid.path & ~CHDIR) {
default:
@@ 633,45 717,51 @@ audioclose(Chan *c)
if (debug > 1) print("#A: close\n");
if(c->flag & COPEN) {
qlock(&audio);
- qlock(&audio.o);
if(waserror()){
- qunlock(&audio.o);
qunlock(&audio);
nexterror();
}
if (audio.o.chan == c) {
+ s = &audio.o;
+ qlock(s);
+ if(waserror()){
+ qunlock(s);
+ nexterror();
+ }
/* closing the write end */
audio.amode &= ~Awrite;
- if (audio.o.filling->nbytes) {
- audio.o.filling++;
- if (audio.o.filling == &audio.o.buf[Nbuf])
- audio.o.filling = &audio.o.buf[0];
- sendaudio(&audio.o);
+ if (s->filling->nbytes) {
+ /* send remaining partial buffer */
+ s->filling++;
+ if (s->filling == &s->buf[Nbuf])
+ s->filling = &s->buf[0];
+ sendaudio(s);
}
- delay(2000);
- // dmawait(audio.o.dma);
- if (!dmaidle(audio.o.dma))
- print("dma still busy\n");
+ dmawait(s->dma);
+ outdisable();
amplifierpower(0);
- setempty(&audio.o);
- dmafree(audio.o.dma);
+ setempty(s);
+ dmafree(s->dma);
+ qunlock(s);
+ poperror();
}
if (audio.i.chan == c) {
/* closing the read end */
audio.amode &= ~Aread;
+ indisable();
setempty(&audio.i);
}
if (audio.amode == 0) {
/* turn audio off */
audiopower(0);
}
- poperror();
- qunlock(&audio.o);
qunlock(&audio);
+ poperror();
print("total dmas: %lud\n", iostats.totaldma);
print("dmas while idle: %lud\n", iostats.idledma);
print("dmas while busy: %lud\n", iostats.faildma);
+ print("out of order dma: %lud\n", iostats.samedma);
}
break;
}
@@ 807,8 897,8 @@ audiowrite(Chan *c, void *vp, long n, vlong)
}
if(strcmp(field[i], "reset") == 0) {
-// resetlevel();
-// mxvolume();
+ resetlevel();
+ mxvolume();
goto cont0;
}
if(strcmp(field[i], "in") == 0) {
M bitsy/main.c => bitsy/main.c +2 -1
@@ 376,7 376,8 @@ gpioinit(void)
gpioregs->falling = 0;
gpioregs->altfunc |=
GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o
- |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o;
+ |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o
+ |GPIO_SSP_CLK_i;
egpioreg = mapspecial(EGPIOREGS, 4);
}
M bitsy/sa1110dma.c => bitsy/sa1110dma.c +19 -23
@@ 41,7 41,6 @@ enum {
};
typedef struct DMAchan {
- Lock;
int allocated;
Rendez r;
void (*intr)(void*, ulong);
@@ 71,7 70,7 @@ dmainit(void) {
int i;
/* map the lcd regs into the kernel's virtual space */
- dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));;
+ dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));
if (debug) print("dma: dmaalloc registers 0x%ux mapped at 0x%p\n",
DMAREGS, dmaregs);
for (i = 0; i < NDMA; i++) {
@@ 110,6 109,8 @@ dmaalloc(int rd, int bigendian, int burstsize, int datumsize, int device, ulong
void
dmafree(int i) {
+ dmaregs[i].dcsr_clr = 0xff;
+ dmaregs[i].ddar = 0;
dma.chan[i].allocated = 0;
dma.chan[i].intr = nil;
}
@@ 117,35 118,38 @@ dmafree(int i) {
ulong
dmastart(int chan, void *addr, int count) {
ulong status, n;
+ static int last;
- ilock(&dma.chan[chan]);
+ /* If this gets called from interrupt routines, make sure ilocks are used */
status = dmaregs[chan].dcsr_rd;
if (debug > 1)
iprint("dma: dmastart 0x%lux\n", status);
if ((status & (1<<STRTA|1<<STRTB|1<<RUN)) == (1<<STRTA|1<<STRTB|1<<RUN)) {
- iunlock(&dma.chan[chan]);
return 0;
}
cachewbregion((ulong)addr, count);
n = 1;
if ((status & (1<<BIU | 1<<STRTB)) == (1<<BIU | 1<<STRTB) ||
(status & (1<<BIU | 1<<STRTA)) == 0) {
- assert((status & 1<<STRTA) == 0);
- if (status & 1<<RUN)
- n = 2;
+ if (status & 1<<STRTA)
+ iprint("writing busy dma entry 0x%lux\n", status);
+ if (status & 1<<STRTB)
+ n = (last == 1)?2:3;
+ last = 2;
dmaregs[chan].dstrtA = addr;
dmaregs[chan].dxcntA = count-1;
dmaregs[chan].dcsr_set = 1<<RUN | 1<<IE | 1<<STRTA;
} else {
- assert((status & 1<<STRTB) == 0);
- if (status & 1<<RUN)
- n = 2;
+ if (status & 1<<STRTB)
+ iprint("writing busy dma entry 0x%lux\n", status);
+ if (status & 1<<STRTA)
+ n = (last == 2)?2:3;
+ last = 1;
dmaregs[chan].dstrtB = addr;
dmaregs[chan].dxcntB = count-1;
dmaregs[chan].dcsr_set = 1<<RUN | 1<<IE | 1<<STRTB;
}
- iunlock(&dma.chan[chan]);
return n;
}
@@ 154,7 158,8 @@ dmaidle(int chan) {
ulong status;
status = dmaregs[chan].dcsr_rd;
- return (status & (1<<STRTA|1<<STRTB|1<<RUN)) == 0;
+ if (debug > 1) print("dmaidle: 0x%lux\n", status);
+ return (status & (1<<STRTA|1<<STRTB)) == 0;
}
static int
@@ 162,7 167,7 @@ _dmaidle(void* chan) {
ulong status;
status = dmaregs[(int)chan].dcsr_rd;
- return (status & (1<<STRTA|1<<STRTB|1<<RUN)) == 0;
+ return (status & (1<<STRTA|1<<STRTB)) == 0;
}
void
@@ 182,7 187,6 @@ dmaintr(Ureg*, void *x)
ulong dcsr, donebit;
i = regs - dmaregs;
- ilock(&dma.chan[i]);
dcsr = regs->dcsr_rd;
if (debug > 1)
iprint("dma: interrupt channel %d, status 0x%lux\n", i, dcsr);
@@ 191,19 195,11 @@ dmaintr(Ureg*, void *x)
donebit = 1<<((dcsr&1<<BIU)?DONEA:DONEB);
if (dcsr & donebit) {
regs->dcsr_clr = donebit;
- /* try the other bit as well */
- donebit ^= 1<<DONEA|1<<DONEB;
- if (dcsr & donebit) {
- regs->dcsr_clr = donebit;
- }
- iunlock(&dma.chan[i]);
if (dma.chan[i].intr) {
- dcsr &= 1<<STRTA | 1<<STRTB | 1<<RUN;
- (*dma.chan[i].intr)(dma.chan[i].param, dcsr == 0);
+ (*dma.chan[i].intr)(dma.chan[i].param, dcsr & (1<<DONEA|1<<DONEB));
}
wakeup(&dma.chan[i].r);
return;
}
iprint("spurious DMA interrupt, channel %d, status 0x%lux\n", i, dcsr);
- iunlock(&dma.chan[i]);
}