[inferno-kirkwood] push by mechiel@ueber.net - lots of improvements. kfs can now mount an fs on the disk and use it.... on 2010-09-05 13:52 GMT

2 views
Skip to first unread message

inferno-...@googlecode.com

unread,
Sep 5, 2010, 9:54:02 AM9/5/10
to inferno-kirk...@googlegroups.com
Revision: 91073684ef
Author: Mechiel Lukkien <mec...@ueber.net>
Date: Sun Sep 5 06:50:48 2010
Log: lots of improvements. kfs can now mount an fs on the disk and use it.

- errors are now propagated to the callers.
- there is now some locking and protection when doing ata commands
and ncq i/o.
- we use the queue depth from ata "identify device" for ncq.
- hotplug works, both connect and disconnect.
- initialisation of the disk is done in the background during
startup.
- for ata commands, we don't poll to learn when BSY is cleared, we
wait for an interrupt for fis "registers dev2host". we also use
normal ata-register-delivered interrupts.

before reaming kfs, you might want to artificially limit the size
of the disk ("data"), otherwise it will take a long time.

there is still a lot to improve.
http://code.google.com/p/inferno-kirkwood/source/detail?r=91073684ef

Modified:
/README
/devsata.c

=======================================
--- /README Wed Aug 25 14:32:43 2010
+++ /README Sun Sep 5 06:50:48 2010
@@ -7,10 +7,10 @@
todo:
- writing kernels to nand (can be done from u-boot, also should be
possible from userland): will implement raw flash device, like flash(3)
but with oob data to be read/written too. then implement a program to
write an image (that executes erase and such).
- better nand support
+- next step for sata support: improve devbs.c and change devsata.c so it
uses it.
- find & fix crash with latest uboot
- l2 cache, does not seem to speed up much. something wrong?
- delay/microdelay calibration
-- better sata support
- perform register file read after register writes to different registers
- lower power. make sure we don't go into sdram self-refresh when we were
just doing something useful.
- more ethernet (fewer interrupts, more checks for idleness when
configuring, phy)
=======================================
--- /devsata.c Thu Sep 2 14:12:17 2010
+++ /devsata.c Sun Sep 5 06:50:48 2010
@@ -1,23 +1,28 @@
/*
first attempt at driver for the sata controller.
-kirkwood has two ports, on the sheevaplug only the second port is in use.
-for now we'll assume ncq "first party dma" read/write commands (very old
sata disks won't work).
+kirkwood has two ports, on the sheevaplug with sata from newit only the
second port is in use.

todo:
-- when doing ata commands, verify ata status if ok.
-- get interrupt when device sends registers. so we can check for BSY
then, or start reading data, etc.
-- error handling & propagating to caller.
+- keep track of supported features of ata drive.
+- non-ncq dma.
+- mark disk as invalid before doing identify. make sure we check that
disk is valid before operating on it.
+
+- better support for ata commands, reading status bits after command.
- better detect ata support of drives
- detect whether packet command is accepted. try if packet commands work.
- read ata/atapi signature in registers after reset?
-- look at cache flushes around dma
-- hotplug, at least handle disconnects
+- look at dcache flushes around dma
- interrupt coalescing
-- general ata commands
-- support: pio,dma,ncq with ata commands. perhaps atapi-mmc & atapi-scsi
too? this chip only does pio with atapi...
-- use generic sd interface (#S; we need partitions)
+- support for general ata commands, e.g. smart, security
+- use generic sd (or bs) interface (#S; we need partitions)
+- lba24-only drives
- abstract for multiple ports (add controller struct to functions)
- in satainit(), only start the disk init, don't wait for it to be ready?
faster booting, seems it takes controller/disk some time to init after
reset.
+
+for the future:
+- power management
+- support for port multiplier
+- support for atapi (mcc scsi), but this chip cannot do dma for packet
commands.
*/

#include "u.h"
@@ -29,7 +34,7 @@

#include "io.h"

-static int satadebug = 1;
+static int satadebug = 0;
#define diprint if(satadebug)iprint
#define dprint if(satadebug)print

@@ -96,12 +101,35 @@
static uchar tags[32];
static int tagnext;
static int tagsinuse;
-static Rendez tagl; /* protected by reqsl */
+static int ntags;
+static Rendez tagr; /* protected by reqsl */

static Rendez reqsr[32]; /* protected by requiring to hold tag */
static volatile ulong reqsdone[32];
static QLock reqsl;

+static volatile ulong atadone; /* whether ata interrupt has occurred */
+static Rendez atadoner;
+
+static volatile ulong ataregs; /* registers received from device */
+static Rendez ataregsr;
+
+/* for reqsdone and atadone */
+enum {
+ Rok,
+ Rtimeout,
+ Rfail,
+};
+static char *donemsgs[] = {
+"ok",
+"timeout",
+"error",
+};
+
+static struct {
+ ulong serrorintrs;
+} stats;
+
static Req *reqs;
static Resp *resps;
static Prd *prds;
@@ -109,12 +137,21 @@
static int respnext;
static Disk disk;

+static Lock startil; /* for access to startr & start, ilock */
+static Rendez startr; /* for kproc satastart to sleep on */
+static volatile ulong start; /* how to start, see below */
+enum {
+ StartReset = 1<<0,
+ StartIdentify = 1<<1,
+};
+
+
enum {
/* edma config */
ECFGncq = 1<<5, /* use ncq */
ECFGqueue = 1<<9, /* use ata queue dma commands */

- /* edma interrup error cause */
+ /* edma interrupt error cause */
Edeverr = 1<<2, /* device error */
Edevdis = 1<<3, /* device disconnect */
Edevcon = 1<<4, /* device connected */
@@ -123,15 +160,27 @@
Etransint = 1<<8, /* edma transport layer interrupt */
Eiordy = 1<<12, /* edma iordy timeout */

- Elinkmask = 0xf, /* link rx/tx control/data errors: */
+ Erxlinkmask = 0xf, /* link rx control/data errors */
Erxctlshift = 13,
Erxdatashift = 17,
+ Erxlinksatacrc = 1<<0, /* sata crc error */
+ Erxlinkfifo = 1<<1, /* internal fifo error */
+ Erxlinkresetsync= 1<<2, /* link layer reset by SYNC from device */
+ Erxlinkother = 1<<3, /* other link ctl errors */
+
+ Etxlinkmask = 0x1f, /* link tx control/data errors */
Etxctlshift = 21,
Etxdatashift = 26,
- Esatacrc = 1<<0, /* sata crc error */
- Efifo = 1<<1, /* internal fifo error */
- Eresetsync = 1<<2, /* link layer reset by SYNC from device */
- Eotherrxctl = 1<<3, /* other link ctl errors */
+ Etxlinksatacrc = 1<<0, /* sata crc error */
+ Etxlinkfifo = 1<<1, /* internal fifo error */
+ Etxlinkresetsync= 1<<2, /* link layer reset by SYNC from device */
+ Etxlinkdmat = 1<<3, /* link layer accepts DMAT from device */
+ Etxcollision = 1<<4, /* collision with receive */
+
+ Elinkmask = Erxlinkmask<<Erxctlshift | Erxlinkmask<<Erxdatashift |
Etxlinkmask<<Etxctlshift | Etxlinkmask<<Etxdatashift,
+ Elinkerrmask = Erxlinkmask<<Erxctlshift | Erxlinkmask<<Erxdatashift |
Etxlinkmask<<Etxdatashift,
+
+ Etransport = 1<<31, /* transport protocol error */

/* edma command */
EdmaEnable = 1<<0, /* enable edma */
@@ -145,7 +194,7 @@
Cacheempty = 1<<6, /* edma cache empty */
EdmaIdle = 1<<7, /* edma idle (but disk can have command pending) */

- /* host controller interrupt */
+ /* host controller main interrupt */
Sata0err = 1<<0,
Sata0done = 1<<1,
Sata1err = 1<<2,
@@ -154,6 +203,13 @@
Sata1dmadone = 1<<5,
Satacoaldone = 1<<8,

+ /* host controller interrupt */
+ Idma0done = 1<<0, /* new crpb in out queue */
+ Idma1done = 1<<1,
+ Iintrcoalesc = 1<<4,
+ Idevintr0 = 1<<8, /* ata interrupt when edma disabled */
+ Idevintr1 = 1<<9,
+
/* interface cfg */
SSC = 1<<6, /* SSC enable */
Gen2 = 1<<7, /* gen2 enable */
@@ -216,14 +272,13 @@


enum {
- Qdir, Qctlr, Qctl, Qdata, Qtest,
+ Qdir, Qctlr, Qctl, Qdata,
};
static Dirtab satadir[] = {
".", {Qdir,0,QTDIR}, 0, 0555,
"sd01", {Qctlr,0,QTDIR}, 0, 0555,
"ctl", {Qctl,0,0}, 0, 0660,
"data", {Qdata,0,0}, 0, 0660,
- "test", {Qtest,0,0}, 0, 0666, /* to be removed */
};


@@ -233,48 +288,159 @@
{
return ((ulong)p[0]<<8) | ((ulong)p[1]<<0);
}
-
+
+static int
+isdone(void *p)
+{
+ ulong *v = p;
+ return *v != Rtimeout;
+}
+
+static int
+notzero(void *p)
+{
+ ulong *v = p;
+ return *v != 0;
+}
+
+static int
+tagfree(void *)
+{
+ return tagsinuse < ntags;
+}
+
+static int
+tagsidle(void*)
+{
+ return tagsinuse == 0;
+}
+
+static long
+min(long a, long b)
+{
+ return (a < b) ? a : b;
+}
+
+static void
+satakick(ulong v)
+{
+ ilock(&startil);
+ diprint("satakick, v %#lux\n", v);
+ start = v;
+ wakeup(&startr);
+ iunlock(&startil);
+}
+
+static void
+sataabort(void)
+{
+ int i;
+
+ for(i = 0; i < nelem(reqsr); i++) {
+ reqsdone[i] = Rfail;
+ wakeup(&reqsr[i]);
+ }
+ atadone = Rfail;
+ wakeup(&atadoner);
+
+ /* xxx disable edma? */
+}
+
+/*
+ * hc main intr & enable register cause interrupts.
+ * main intr is read-only, the bits must be cleared in:
+ * - hc intr
+ * - edma error intr & enable
+ * - serror & intr enable
+ * - fis intr & enable
+ */
static void
sataintr(Ureg*, void*)
{
SatahcReg *hr = SATAHCREG;
SataReg *sr = SATA1REG;
- ulong v, tag;
- static int count = 0;
+ ulong v, e, tag;
ulong in, out;
-
+
v = hr->intrmain;
diprint("intr %#lux, main %#lux\n", hr->intr, v);
if(v & Sata1err) {
diprint("intre %#lux\n", sr->edma.intre);
diprint("m 1err\n");
+
+ e = sr->edma.intre;
+ if(e & (Edevdis | Eiordy | Elinkerrmask | Etransport)) {
+ /* unrecoverable error. need ata reset to anything in future. */
+ sataabort();
+ /* xxx send hotplug disconnect event? */
+ satakick(StartReset);
+ } else if(e & Edeverr) {
+ /* device to host fis, or set device bits fis received with ERR set.
during edma. */
+ /* xxx propagate error, how to recover? */
+ diprint("Edeverr\n");
+ sataabort();
+ }
+ if(e & Edevcon) {
+ /* device connected, hotplug */
+ diprint("Edevcon\n");
+ satakick(StartIdentify);
+ }
+ if(e & Eserror) {
+ /* Serror set */
+ diprint("Eserror, %08lux\n", sr->ifc.serror);
+ sr->ifc.serror = ~0UL;
+ stats.serrorintrs++;
+ }
+ if(e & Eselfdis) {
+ /* edma disabled itself */
+ /* xxx how to recover? at least stop all activity and return error. */
+ iprint("Eselfdis\n");
+ sataabort();
+ satakick(StartReset);
+ }
+ if(e & Etransint) {
+ /* fis interrupt */
+ sr->ifc.fisintr = 0;
+ ataregs = 1;
+ wakeup(&ataregsr);
+ }
+
+ sr->edma.intre = 0;
}
if(v & Sata1done) {
- diprint("m 1done\n");
- }
- hr->intr = 0;
- sr->edma.intre = 0;
- intrclear(Irqlo, IRQ0sata);
-
- diprint("reqin %#lux reqout %#lux respin %#lux respout %#lux\n",
sr->edma.reqin, sr->edma.reqout, sr->edma.respin, sr->edma.respout);
-
- dcinv(resps, 32*sizeof resps[0]);
- in = (sr->edma.respin & MASK(8))/sizeof (Resp);
- out = (sr->edma.respout & MASK(8))/sizeof (Resp);
- for(;;) {
- if(in == out)
- break;
-
- /* determine which request is done, wakeup its caller. */
- tag = resps[out].idflags & MASK(5);
-
- /* xxx check for error? */
-
- reqsdone[tag] = 1;
- wakeup(&reqsr[tag]);
- out = (out+1)%32;
- sr->edma.respout = (ulong)&resps[out];
- }
+ if(hr->intr & Idma1done) {
+ diprint("m 1dmadone\n");
+
+ hr->intr = ~Idma1done;
+
+ dcinv(resps, 32*sizeof resps[0]);
+ in = (sr->edma.respin & MASK(8))/sizeof (Resp);
+ out = (sr->edma.respout & MASK(8))/sizeof (Resp);
+ for(;;) {
+ if(in == out)
+ break;
+
+ /* determine which request is done, wakeup its caller. */
+ tag = resps[out].idflags & MASK(5);
+ /* xxx check for error in idflags? we now handle error through
Edeverr, and make all i/o fail... */
+
+ reqsdone[tag] = Rok;
+ wakeup(&reqsr[tag]);
+ out = (out+1)%32;
+ sr->edma.respout = (ulong)&resps[out];
+ }
+ }
+ if(hr->intr & Idevintr1) {
+ diprint("m 1ataintr\n");
+ /* reading status clears the interrupt */
+ regreadl(&ATA1REG->status);
+ hr->intr = ~Idevintr1;
+ atadone = Rok;
+ wakeup(&atadoner);
+ }
+ }
+
+ intrclear(Irqlo, IRQ0sata);
}

static void
@@ -305,23 +471,35 @@
}
}

+static void
+atawait(void)
+{
+ AtaReg *a = ATA1REG;
+
+ while(a->status & Absy) {
+ ataregs = 0;
+ sleep(&ataregsr, notzero, &ataregs);
+ }
+}
+
+/* properly deal with commands that do (not) generate interrupt, and do
(not) read/write data. */
enum {
Nodata, Host2dev, Dev2host,
};
-static ulong
-atacmd(uchar cmd, uchar feat, uchar sectors, ulong lba, uchar dev, int
dir, uchar *data)
-{
- SatahcReg *hr = SATAHCREG;
- SataReg *sr = SATA1REG;
+static void
+atacmd(uchar cmd, uchar feat, uchar sectors, ulong lba, uchar dev, int
dir, uchar *data, int ms)
+{
AtaReg *a = ATA1REG;
ulong v;
+ char *msg;

/* xxx sleep until edma is disabled or edma is idle (edma status, bit 7
(EDMAIdle). then disable edma. */
-
- /* xxx don't blindly reset registers later */
- hr->intrmainena &= ~(Sata1err|Sata1done|Sata1dmadone);
-
- diprint("ata, status %#lux\n", a->status);
+ /* xxx assert that edma is disabled */
+
+ dprint("ata, status %#lux\n", a->status);
+ atawait();
+
+ atadone = Rtimeout;
a->feat = feat;
a->sectors = sectors;
a->lbalow = (lba>>0) & 0xff;
@@ -329,17 +507,22 @@
a->lbahigh = (lba>>16) & 0xff;
a->dev = dev;
a->cmd = cmd;
- delay(100);
+ if(ms > 0) {
+ tsleep(&atadoner, isdone, &atadone, ms);
+ if(atadone != Rok) {
+ msg = donemsgs[atadone];
+ dprint("%s\n", msg);
+ error(msg);
+ }
+ } else {
+ sleep(&atadoner, isdone, &atadone);
+ }
v = a->status;
-if(satadebug) {
- diprint("result, status %#lux\n", v);
- if(v & Aerr) diprint(" err\n");
- if(v & Adrq) diprint(" drq\n");
- if(v & Adf) diprint(" df\n");
- if(v & Adrdy) diprint(" drdy\n");
- if(v & Absy) diprint(" bsy\n");
-}
- /* xxx check for & propagate errors */
+
+ if(v & Aerr)
+ error("ata command failed");
+ if(v & Adf)
+ error("device fault");

switch(dir) {
case Nodata:
@@ -351,10 +534,37 @@
pioget(data);
break;
}
-
- hr->intrmainena |= Sata1err|Sata1done|Sata1dmadone;
- hr->intr = 0;
- return v;
+}
+
+static int
+atacheck(ulong statusmask, ulong status)
+{
+ if((ATA1REG->status & statusmask) != status)
+ return -1;
+ return 0;
+}
+
+/* claim the sata controller. must be called before doing ata commands,
outside of edma. */
+static void
+sataclaim(void)
+{
+ SataReg *sr = SATA1REG;
+
+ qlock(&reqsl);
+ do {
+ sleep(&tagr, tagsidle, nil);
+ } while(tagsinuse > 0);
+ SATA1REG->edma.cmd |= EdmaAbort;
+ sr->ifc.fisintrena |= 1<<0;
+}
+
+static void
+sataunclaim(void)
+{
+ SataReg *sr = SATA1REG;
+
+ sr->ifc.fisintrena &= ~(1<<0);
+ qunlock(&reqsl);
}

/* strip spaces in string. at least western digital returns space-padded
strings for "identify device". */
@@ -436,7 +646,7 @@
};


-static int
+static void
identify(void)
{
uchar c;
@@ -445,15 +655,15 @@
ushort w;
Atadev dev;

- atacmd(0xec, 0, 0, 0, 0, Dev2host, buf);
+ atacmd(0xec, 0, 0, 0, 0, Dev2host, buf, 60*1000);
+ if(atacheck(Absy|Adrdy|Adf|Adrq|Aerr, Adrdy) < 0)
+ error("identify failed");

c = 0;
for(i = 0; i < 512; i++)
c += buf[i];
- if(c != 0) {
- dprint("check byte for 'identify device' response invalid\n");
- return -1;
- }
+ if(c != 0)
+ error("check byte for 'identify device' response invalid");

memmove(disk.serial, buf+10*2, sizeof disk.serial-1);
memmove(disk.firmware, buf+23*2, sizeof disk.firmware-1);
@@ -467,10 +677,13 @@
disk.sectors |= (uvlong)g16(buf+102*2)<<32;

w = g16(buf+49*2);
- if((w & Fcapdma) == 0 || (w & Fcaplba) == 0) {
- dprint("disk does not support dma and/or lba\n");
- return -1;
- }
+ if((w & Fcapdma) == 0 || (w & Fcaplba) == 0)
+ error("disk does not support dma and/or lba");
+
+ w = g16(buf+75*2);
+ ntags = 1 + (w&MASK(5));
+ for(i = 0; i < ntags; i++)
+ tags[i] = i;

dev.major = g16(buf+80*2);
dev.minor = g16(buf+81*2);
@@ -484,10 +697,8 @@
if((dev.cmdset[3+2] & Fvalidmask) != Fvalid)
dev.cmdset[3+2] = 0;

- if((dev.cmdset[3+1] & Feat1Addr48) == 0) {
- dprint("disk does not have lba48 enabled\n");
- return -1;
- }
+ if((dev.cmdset[3+1] & Feat1Addr48) == 0)
+ error("disk does not have lba48 enabled");

dev.sectorflags = g16(buf+106*2);
dev.sectorsize = 512;
@@ -533,7 +744,7 @@
dev.cmdset[0] & ~dev.cmdset[3+0],
dev.cmdset[1] & ~dev.cmdset[3+1],
dev.cmdset[2] & ~dev.cmdset[3+2]);
- dprint("wwn %llux\n", dev.wwn);
+ dprint("wwn %016llux\n", dev.wwn);
dprint("nvcache cap:%s%s%s\n",
(dev.nvcachecap & NvcacheEnabled) ? " NvcacheEnabled" : "",
(dev.nvcachecap & NvcachePMEnabled) ? " NvcachePMEnabled" : "",
@@ -542,15 +753,28 @@
dprint("rpm %hud\n", dev.rpm);
}
satadir[Qdata].length = disk.sectors*512;
+ //xxx satadir[Qdata].length = 2048000*512;
disk.valid = 1;
-
- return 0;
}

static void
flush(void)
{
- atacmd(0xea, 0, 0, 0, 0, Nodata, nil);
+ AtaReg *a = ATA1REG;
+
+ sataclaim();
+ if(waserror()) {
+ sataunclaim();
+ nexterror();
+ }
+
+ atacmd(0xea, 0, 0, 0, 0, Nodata, nil, 0);
+ /* xxx should log the lba48 sector that failed and perhaps try to flush
the rest? */
+ if(atacheck(Absy|Adrdy|Adf|Adrq|Aerr, Adrdy) < 0)
+ error("flush cache ext failed");
+
+ poperror();
+ sataunclaim();
}

static void
@@ -599,13 +823,17 @@
sr->ifc.serrintrena = 0;
sr->ifc.fisintrena = 0;

+ /* disable & abort edma, bdma */
+ sr->edma.cmd = (sr->edma.cmd & ~EdmaEnable) | EdmaAbort;
+
/* clear interrupts */
+ hr->intr = ~0UL;
sr->edma.intre = 0;
+ sr->ifc.serror = ~0UL;
+ sr->ifc.fisintr = 0;
+
/* xxx more */

- /* disable & abort edma, bdma */
- sr->edma.cmd = (sr->edma.cmd & ~EdmaEnable) | EdmaAbort;
-
/* xxx should set full register? */
sr->ifc.ifccfg &= ~Physhutdown;

@@ -619,6 +847,13 @@
hr->intrtime = 0; /* number of clocks before asserting interrupt (disable
coalescing) */
hr->intr = 0; /* clear */

+ /* clock ticks to reach 1250ns (as specified for sata). */
+ sr->edma.iordytimeout = 0xbc;
+ if(CLOCKFREQ == 200*1000*1000)
+ sr->edma.iordytimeout = 0xfa;
+ sr->edma.cmddelaythr = 0;
+ // sr->edma.haltcond =
+
/* xxx should set windows correct too */
if(0) {
hr->win[0].ctl = (1<<0) /* enable window */
@@ -643,106 +878,123 @@
}

static void
-satainit(void)
-{
- SatahcReg *hr = SATAHCREG;
+satastartreset(void)
+{
SataReg *sr = SATA1REG;
- AtaReg *ar = ATA1REG;
- int n;
- int i;
-
- diprint("satainit...\n");
-
- tagnext = tagsinuse = 0;
- for(i = 0; i < nelem(tags); i++)
- tags[i] = i;
-
- /* disable interrupts */
- hr->intrmainena = 0;
- sr->edma.intreena = 0;
- sr->ifc.serrintrena = 0;
- sr->ifc.fisintrena = 0;
-
- /* disable & abort edma */
- sr->edma.cmd = (sr->edma.cmd & ~EdmaEnable) | EdmaAbort;
-
- /* clear edma */
- sr->edma.reqbasehi = sr->edma.respbasehi = 0;
- sr->edma.reqin = 0;
- sr->edma.reqout = 0;
- sr->edma.respin = 0;
- sr->edma.respout = (ulong)&resps[0];
-
- diprint("satainit, reqin %#lux, reqout %#lux\n", sr->edma.reqin,
sr->edma.reqout);
-
-if(0) {
- dprint("ata before reset\n");
- atadump();
-}
-
- dprint("sr->ifc.sstatus before edma reset %#lux\n", sr->ifc.sstatus);
+
+ diprint("before ata reset, sstatus %#lux\n", sr->ifc.sstatus);

sr->edma.cmd |= Atareset;
- delay(1);
+ regreadl(&sr->edma.cmd);
sr->edma.cmd &= ~Atareset;
- delay(200);
+ tsleep(&up->sleep, return0, nil, 200); // xxx needed?

/* errata magic, to fix the phy. see uboot code (no docs available). */
- sr->ifc.phym3 = (sr->ifc.phym3 & ~0x78100000) | 0x28000000;
+ sr->ifc.phym3 = (sr->ifc.phym3 & ~0x78100000UL) | 0x28000000UL;
sr->ifc.phym4 = (sr->ifc.phym4 & ~1) | (1<<16);
- sr->ifc.phym9g2 = (sr->ifc.phym9g2 & ~0x400f) | 0x00008; /* tx driver
amplitude */
- sr->ifc.phym9g1 = (sr->ifc.phym9g1 & ~0x400f) | 0x00008; /* tx driver
amplitude */
- delay(100); /* needed? */
+ sr->ifc.phym9g2 = (sr->ifc.phym9g2 & ~0x400fUL) | 0x00008UL; /* tx driver
amplitude */
+ sr->ifc.phym9g1 = (sr->ifc.phym9g1 & ~0x400fUL) | 0x00008UL; /* tx driver
amplitude */
+ tsleep(&up->sleep, return0, nil, 100); /* needed? */
+
+ sr->ifc.serror = ~0UL;
+ sr->ifc.serrintrena = EN|EX;
+
+ /* get Etransint interrupt when fis "registers device to host" comes in,
for ata commands waiting on Absy */
+ sr->ifc.fisintrena = 0;
+ sr->ifc.fiscfg = 1<<0;

diprint("before phy init, sstatus %#lux, serror %#lux\n",
sr->ifc.sstatus, sr->ifc.serror);

sr->ifc.scontrol = CDETcomm|CSPDany|CIPMnopartial|CIPMnoslumber;
regreadl(&sr->ifc.scontrol);
- delay(1);
- dprint("sr->ifc.sstatus after phy reset %#lux\n", sr->ifc.sstatus);
-
sr->ifc.scontrol &= ~CDETcomm;
regreadl(&sr->ifc.scontrol);
- microdelay(20*1000);
-
- /* check phy status */
- n = 0;
- while((sr->ifc.sstatus & SDETmask) != SDETdevphy) {
- if(n++ > 200) {
- dprint("no sata disk attached (sstatus %#lux; serror %#lux), aborting
sata init\n", sr->ifc.sstatus, sr->ifc.serror);
- return;
- }
- delay(1);
- }
-
- diprint("after phy init, have connection, sstatus %#lux, serror %#lux\n",
sr->ifc.sstatus, sr->ifc.serror);
-
- sr->ifc.ifccfg &= ~Ignorebsy;
-
- dprint("sr->ifc.sstatus before ata identify %#lux\n", sr->ifc.sstatus);
-
- /* xxx horrible, should properly wait during command execution, until
device no longer busy */
- i = 0;
+ diprint("after phy reset, sstatus %#lux\n", sr->ifc.sstatus);
+
+ /* we'll get a connected interrupt now if something is connected */
+ /* have to find out if hotplug works for sata 1.x */
+}
+
+static void
+satastartidentify(void)
+{
+ sataclaim();
+ if(waserror()) {
+ sataunclaim();
+ nexterror();
+ }
+ identify();
+ poperror();
+ sataunclaim();
+
+ print("#S/sd01: %q, %lludGiB (%,llud bytes), %s Gb/s\n",
+ disk.model,
+ disk.sectors*512/(1024*1024*1024),
+ disk.sectors*512,
+ (SATA1REG->ifc.sstatus & SSPDgen2) ? "3.0" : "1.5");
+}
+
+static void
+satastart(void*)
+{
+ ulong v;
+
for(;;) {
- if((ar->status & (Absy|Adrq)) == 0)
- break;
- if(++i == 20) {
- dprint("disk not ready\n");
- return;
- }
- tsleep(&up->sleep, return0, nil, 100);
- }
- if(identify() < 0) {
- dprint("no disk\n");
- return;
- }
- dprint("#S/sd01: %q, %lludGB (%,llud bytes), sata-i%s\n", disk.model,
disk.sectors*512/(1024*1024*1024), disk.sectors*512, (SATA1REG->ifc.sstatus
& SSPDgen2) ? "i" : "");
-
- hr->intrmainena = Sata1err|Sata1done|Sata1dmadone;
+ diprint("satastart sleep... start %#lux\n", start);
+ sleep(&startr, notzero, &start);
+ diprint("satastart wakeup... start %#lux\n", start);
+
+ ilock(&startil);
+ v = start;
+ start = 0;
+ iunlock(&startil);
+
+ if(!waserror()) {
+ if(v & StartReset)
+ satastartreset();
+ if(v & StartIdentify)
+ satastartidentify();
+ poperror();
+ }
+ }
+}
+
+static void
+satainit(void)
+{
+ SatahcReg *hr = SATAHCREG;
+ SataReg *sr = SATA1REG;
+
+ diprint("satainit...\n");
+
+ /* one tag by default, for devices without ncq. changed in identify(). */
+ tagnext = tagsinuse = 0;
+ ntags = 1;
+ tags[0] = 0;
+
+ /* disable & abort edma */
+ sr->edma.cmd = (sr->edma.cmd & ~EdmaEnable) | EdmaAbort;
+
+ /* clear edma */
+ sr->edma.reqbasehi = sr->edma.respbasehi = 0;
+ sr->edma.reqin = 0;
+ sr->edma.reqout = 0;
+ sr->edma.respin = 0;
+ sr->edma.respout = (ulong)&resps[0];
+
+ /* clear & enable interrupts, to get "device connected" interrupts among
others */
+ hr->intrmainena = Sata1err|Sata1done;
hr->intr = 0;
sr->edma.intre = 0;
-
- diprint("satainit, before edma, hr->intr %#lux, hr->intrmain %#lux,
sr->edma.intre %#lux\n", hr->intr, hr->intrmain, sr->edma.intre);
+ sr->edma.intreena = ~(0UL | Etxlinkmask<<Etxctlshift);
+ sr->ifc.serror = ~0UL;
+ sr->ifc.serrintrena = EN|EX;
+ sr->ifc.fisintrena = 0;
+ sr->ifc.fiscfg = 0;
+
+ start = 0;
+ kproc("satastart", satastart, nil, 0);
+ satakick(StartReset);
}

static char *dets[] = {"none", "dev", nil, "devphy", "nophy"};
@@ -762,12 +1014,6 @@
{EX, "X"},
};

-/* hc intr */
-enum {
- Dma1done = 1<<1,
- Intrcoalesc = 1<<4,
- Dev1intr = 1<<9,
-};
/* yuck, remove later */
static ulong
satadump(char *dst, long n, vlong off)
@@ -789,10 +1035,10 @@

v = hr->intr;
p = seprint(p, e, "hc intr %#lux\n", v);
- if(v & Dma1done) p = seprint(p, e, " dma1done");
- if(v & Intrcoalesc) p = seprint(p, e, " intrcoalesc");
- if(v & Dev1intr) p = seprint(p, e, " dev1intr");
- v &= ~(Dma1done|Intrcoalesc|Dev1intr);
+ if(v & Idma1done) p = seprint(p, e, " dma1done");
+ if(v & Iintrcoalesc) p = seprint(p, e, " intrcoalesc");
+ if(v & Idevintr1) p = seprint(p, e, " dev1intr");
+ v &= ~(Idma1done|Iintrcoalesc|Idevintr1);
if(v) p = seprint(p, e, " other: %#lux", v);
p = seprint(p, e, "\n");

@@ -941,25 +1187,6 @@
free(buf);
return n;
}
-
-static int
-isdone(void *p)
-{
- ulong *v = p;
- return *v;
-}
-
-static int
-tagfree(void *)
-{
- return tagsinuse < nelem(tags);
-}
-
-static long
-min(long a, long b)
-{
- return (a < b) ? a : b;
-}

static void
prdfill(Prd *prd, uchar *buf, long n)
@@ -988,6 +1215,7 @@
static ulong
io(int t, void *buf, long nb, vlong off)
{
+ SatahcReg *hr = SATAHCREG;
SataReg *sr = SATA1REG;
Req *rq;
int i;
@@ -999,6 +1227,7 @@
ulong lbalo, lbahi;
ulong cmds[] = {0x60, 0x61}; /* xxx this is for sata fpdma only, ncq */
Prd *prd;
+ char *msg;

if(disk.valid == 0)
error(Enodisk);
@@ -1021,9 +1250,9 @@

qlock(&reqsl);

- sleep(&tagl, tagfree, nil);
+ sleep(&tagr, tagfree, nil);
tag = tags[tagnext];
- tagnext = (tagnext+1)%nelem(tags);
+ tagnext = (tagnext+1)%ntags;
tagsinuse++;

i = reqnext;
@@ -1061,16 +1290,17 @@
rq->ata[3] = (tag<<3)<<0 | 0<<8; /* sectors current (tag), previous */
dcwbinv(rq, sizeof rq[0]);

- reqsdone[tag] = 0;
+ reqsdone[tag] = Rtimeout;
sr->edma.reqin = (ulong)&reqs[reqnext];
if((sr->edma.cmd & EdmaEnable) == 0) {
- /* clear edma intr error, and satahc interrupt dmaXdone */
+ /* xxx check for bsy in ata status register? */
+
+ sr->edma.intre = 0;
+ hr->intr = ~(Sata1err|Sata1done);

sr->edma.cfg = (sr->edma.cfg & ~ECFGqueue) | ECFGncq;

- sr->ifc.fisintr = ~0;
- sr->ifc.fisintrena = 0;
- sr->ifc.fiscfg = (1<<6)-1;
+ sr->ifc.fisintr = 0;

sr->edma.cmd = EdmaEnable;
regreadl(&sr->edma.cmd);
@@ -1078,18 +1308,17 @@
qunlock(&reqsl);

/* xxx have to return tag on interrupt? */
- tsleep(&reqsr[tag], isdone, &reqsdone[tag], 10*1000);
-
- if(!reqsdone[tag]) {
+ tsleep(&reqsr[tag], isdone, &reqsdone[tag], 60*1000);
+
+ if(reqsdone[tag] != Rok) {
/* xxx should do ata reset, or return the tag back to pool */
- error(Etimeout);
+ msg = donemsgs[reqsdone[tag]];
+ error(msg);
}

- tags[(tagnext-tagsinuse+nelem(tags)) % nelem(tags)] = tag;
+ tags[(tagnext-tagsinuse+ntags) % ntags] = tag;
tagsinuse--;
- wakeup(&tagl);
-
- /* xxx check for error, raise it */
+ wakeup(&tagr);

return ns*512;
}
@@ -1113,32 +1342,56 @@
return n;
}
if(strcmp(cb->f[0], "identify") == 0) {
- if(identify() < 0) {
- error("no disk");
- }
- dprint("#S/sd01: %q, %lludGB (%,llud bytes), sata-i%s\n", disk.model,
disk.sectors*512/(1024*1024*1024), disk.sectors*512, (SATA1REG->ifc.sstatus
& SSPDgen2) ? "i" : "");
+ sataclaim();
+ if(waserror()) {
+ sataunclaim();
+ nexterror();
+ }
+
+ identify();
+
+ poperror();
+ sataunclaim();
+
return n;
}
+ if(strcmp(cb->f[0], "flush") == 0) {
+ flush();
+ return n;
+ }
error("bad ctl");
return -1;
}


static int
-satagen(Chan *c, char*, Dirtab *, int, int i, Dir *dp)
+satagen(Chan *c, char *name, Dirtab *, int, int s, Dir *dp)
{
***The diff for this file has been truncated for email.***

Reply all
Reply to author
Forward
0 new messages