summaryrefslogtreecommitdiff
path: root/sys/src/9/pc/etheriwl.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@gmx.de>2013-02-15 22:20:25 +0100
committercinap_lenrek <cinap_lenrek@gmx.de>2013-02-15 22:20:25 +0100
commit9bcf45da9ab26ba8ad5b6f178aefea1714dc31d5 (patch)
treeb35dfedb7aab810798e003c35676a2544e463336 /sys/src/9/pc/etheriwl.c
parente7127522296d64f9ef908ab53f1df86e2e0ba632 (diff)
etheriwl: fix bugs, support for Centrino Ultimate-N 6300
Diffstat (limited to 'sys/src/9/pc/etheriwl.c')
-rw-r--r--sys/src/9/pc/etheriwl.c784
1 files changed, 517 insertions, 267 deletions
diff --git a/sys/src/9/pc/etheriwl.c b/sys/src/9/pc/etheriwl.c
index 94e5f3229..746535b47 100644
--- a/sys/src/9/pc/etheriwl.c
+++ b/sys/src/9/pc/etheriwl.c
@@ -64,7 +64,7 @@ enum {
FhIsr = 0x010, /* second interrupt status */
Reset = 0x020,
-
+
Rev = 0x028, /* hardware revision */
EepromIo = 0x02c, /* EEPROM i/o register */
@@ -292,6 +292,8 @@ struct TXQ
uchar *d;
uchar *c;
+ uint lastcmd;
+
Rendez;
QLock;
};
@@ -314,7 +316,9 @@ struct Ctlr {
int type;
int port;
+ int power;
int active;
+ int broken;
int attached;
u32int ie;
@@ -351,10 +355,21 @@ struct Ctlr {
} rfcfg;
struct {
+ uchar version;
+ uchar type;
+ u16int volt;
+
+ char regdom[4+1];
+
u32int crystal;
} eeprom;
struct {
+ Block *cmd[21];
+ int done;
+ } calib;
+
+ struct {
u32int base;
uchar *s;
} sched;
@@ -388,6 +403,10 @@ static char *fwname[16] = {
[Type6005] "iwn-6005",
};
+static char *qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block);
+static char *flushq(Ctlr *ctlr, uint qid);
+static char *cmd(Ctlr *ctlr, uint code, uchar *data, int size);
+
#define csr32r(c, r) (*((c)->nic+((r)/4)))
#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
@@ -484,6 +503,7 @@ dumpctlr(Ctlr *ctlr)
u32int dump[13];
int i;
+ print("lastcmd: %ud (0x%ux)\n", ctlr->tx[4].lastcmd, ctlr->tx[4].lastcmd);
if(ctlr->fwinfo.errptr == 0){
print("no error pointer\n");
return;
@@ -634,16 +654,87 @@ poweron(Ctlr *ctlr)
prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11));
nicunlock(ctlr);
+
+ ctlr->power = 1;
+
return 0;
}
+static void
+poweroff(Ctlr *ctlr)
+{
+ int i, j;
+
+ csr32w(ctlr, Reset, 1);
+
+ /* Disable interrupts */
+ ctlr->ie = 0;
+ csr32w(ctlr, Imr, 0);
+ csr32w(ctlr, Isr, ~0);
+ csr32w(ctlr, FhIsr, ~0);
+
+ /* Stop scheduler */
+ if(ctlr->type != Type4965)
+ prphwrite(ctlr, SchedTxFact5000, 0);
+ else
+ prphwrite(ctlr, SchedTxFact4965, 0);
+
+ /* Stop TX ring */
+ if(niclock(ctlr) == nil){
+ for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--){
+ csr32w(ctlr, FhTxConfig + i*32, 0);
+ for(j = 0; j < 200; j++){
+ if(csr32r(ctlr, FhTxStatus) & (0x10000<<i))
+ break;
+ delay(10);
+ }
+ }
+ nicunlock(ctlr);
+ }
+
+ /* Stop RX ring */
+ if(niclock(ctlr) == nil){
+ csr32w(ctlr, FhRxConfig, 0);
+ for(j = 0; j < 200; j++){
+ if(csr32r(ctlr, FhRxStatus) & 0x1000000)
+ break;
+ delay(10);
+ }
+ nicunlock(ctlr);
+ }
+
+ /* Disable DMA */
+ if(niclock(ctlr) == nil){
+ prphwrite(ctlr, ApmgClkDis, DmaClkRqt);
+ nicunlock(ctlr);
+ }
+ delay(5);
+
+ /* Stop busmaster DMA activity. */
+ csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<9));
+ for(j = 0; j < 100; j++){
+ if(csr32r(ctlr, Reset) & (1<<8))
+ break;
+ delay(10);
+ }
+
+ /* Reset the entire device. */
+ csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<7));
+ delay(10);
+
+ /* Clear "initialization complete" bit. */
+ csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) & ~InitDone);
+
+ ctlr->power = 0;
+}
+
static int
iwlinit(Ether *edev)
{
Ctlr *ctlr;
char *err;
- uchar b[2];
- uint u;
+ uchar b[4];
+ uint u, caloff, regoff;
ctlr = edev->ctlr;
if((err = handover(ctlr)) != nil)
@@ -660,21 +751,35 @@ iwlinit(Ether *edev)
eepromunlock(ctlr);
goto Err;
}
- if(ctlr->type != Type4965){
- if((err = eepromread(ctlr, b, 2, 0x048)) != nil){
- eepromunlock(ctlr);
- goto Err;
- }
- u = get16(b);
- ctlr->rfcfg.type = u & 3; u >>= 2;
- ctlr->rfcfg.step = u & 3; u >>= 2;
- ctlr->rfcfg.dash = u & 3; u >>= 4;
- ctlr->rfcfg.txantmask = u & 15; u >>= 4;
- ctlr->rfcfg.rxantmask = u & 15;
- if((err = eepromread(ctlr, b, 4, 0x128)) != nil){
- eepromunlock(ctlr);
- goto Err;
- }
+ if((err = eepromread(ctlr, b, 2, 0x048)) != nil){
+ Err2:
+ eepromunlock(ctlr);
+ goto Err;
+ }
+ u = get16(b);
+ ctlr->rfcfg.type = u & 3; u >>= 2;
+ ctlr->rfcfg.step = u & 3; u >>= 2;
+ ctlr->rfcfg.dash = u & 3; u >>= 4;
+ ctlr->rfcfg.txantmask = u & 15; u >>= 4;
+ ctlr->rfcfg.rxantmask = u & 15;
+ if((err = eepromread(ctlr, b, 2, 0x66)) != nil)
+ goto Err2;
+ regoff = get16(b);
+ if((err = eepromread(ctlr, b, 4, regoff+1)) != nil)
+ goto Err2;
+ strncpy(ctlr->eeprom.regdom, (char*)b, 4);
+ ctlr->eeprom.regdom[4] = 0;
+ if((err = eepromread(ctlr, b, 2, 0x67)) != nil)
+ goto Err2;
+ caloff = get16(b);
+ if((err = eepromread(ctlr, b, 4, caloff)) != nil)
+ goto Err2;
+ ctlr->eeprom.version = b[0];
+ ctlr->eeprom.type = b[1];
+ ctlr->eeprom.volt = get16(b+2);
+ if(ctlr->type != Type4965 && ctlr->type != Type5150){
+ if((err = eepromread(ctlr, b, 4, caloff + 0x128)) != nil)
+ goto Err2;
ctlr->eeprom.crystal = get32(b);
}
eepromunlock(ctlr);
@@ -695,14 +800,11 @@ iwlinit(Ether *edev)
}
break;
}
-
- ctlr->ie = 0;
- csr32w(ctlr, Isr, ~0); /* clear pending interrupts */
- csr32w(ctlr, Imr, 0); /* no interrupts for now */
-
+ poweroff(ctlr);
return 0;
Err:
print("iwlinit: %s\n", err);
+ poweroff(ctlr);
return -1;
}
@@ -868,6 +970,315 @@ irqwait(Ctlr *ctlr, u32int mask, int timeout)
return ctlr->wait.r & mask;
}
+static int
+rbplant(Ctlr *ctlr, int i)
+{
+ Block *b;
+
+ b = iallocb(Rbufsize + 256);
+ if(b == nil)
+ return -1;
+ b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
+ memset(b->rp, 0, Rdscsize);
+ ctlr->rx.b[i] = b;
+ ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8;
+ return 0;
+}
+
+static char*
+initring(Ctlr *ctlr)
+{
+ RXQ *rx;
+ TXQ *tx;
+ int i, q;
+
+ rx = &ctlr->rx;
+ if(rx->b == nil)
+ rx->b = malloc(sizeof(Block*) * Nrx);
+ if(rx->p == nil)
+ rx->p = mallocalign(sizeof(u32int) * Nrx, 256, 0, 0);
+ if(rx->s == nil)
+ rx->s = mallocalign(Rstatsize, 16, 0, 0);
+ if(rx->b == nil || rx->p == nil || rx->s == nil)
+ return "no memory for rx ring";
+ memset(ctlr->rx.s, 0, Rstatsize);
+ for(i=0; i<Nrx; i++){
+ rx->p[i] = 0;
+ if(rx->b[i] != nil){
+ freeb(rx->b[i]);
+ rx->b[i] = nil;
+ }
+ if(rbplant(ctlr, i) < 0)
+ return "no memory for rx descriptors";
+ }
+ rx->i = 0;
+
+ if(ctlr->sched.s == nil)
+ ctlr->sched.s = mallocalign(512 * nelem(ctlr->tx) * 2, 1024, 0, 0);
+ if(ctlr->sched.s == nil)
+ return "no memory for sched buffer";
+ memset(ctlr->sched.s, 0, 512 * nelem(ctlr->tx));
+
+ for(q=0; q<nelem(ctlr->tx); q++){
+ tx = &ctlr->tx[q];
+ if(tx->b == nil)
+ tx->b = malloc(sizeof(Block*) * Ntx);
+ if(tx->d == nil)
+ tx->d = mallocalign(Tdscsize * Ntx, 256, 0, 0);
+ if(tx->c == nil)
+ tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0);
+ if(tx->b == nil || tx->d == nil || tx->c == nil)
+ return "no memory for tx ring";
+ memset(tx->d, 0, Tdscsize * Ntx);
+ memset(tx->c, 0, Tcmdsize * Ntx);
+ for(i=0; i<Ntx; i++){
+ if(tx->b[i] != nil){
+ freeb(tx->b[i]);
+ tx->b[i] = nil;
+ }
+ }
+ tx->i = 0;
+ tx->n = 0;
+ tx->lastcmd = 0;
+ }
+
+ if(ctlr->kwpage == nil)
+ ctlr->kwpage = mallocalign(4096, 4096, 0, 0);
+ if(ctlr->kwpage == nil)
+ return "no memory for kwpage";
+ memset(ctlr->kwpage, 0, 4096);
+
+ return nil;
+}
+
+static char*
+reset(Ctlr *ctlr)
+{
+ char *err;
+ int i, q;
+
+ if(ctlr->power)
+ poweroff(ctlr);
+ if((err = initring(ctlr)) != nil)
+ return err;
+ if((err = poweron(ctlr)) != nil)
+ return err;
+
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
+ nicunlock(ctlr);
+
+ csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi);
+
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ if(ctlr->type != Type4965)
+ prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis);
+ if(ctlr->type == Type1000){
+ /*
+ * Select first Switching Voltage Regulator (1.32V) to
+ * solve a stability issue related to noisy DC2DC line
+ * in the silicon of 1000 Series.
+ */
+ prphwrite(ctlr, ApmgDigitalSvr,
+ (prphread(ctlr, ApmgDigitalSvr) & ~(0xf<<5)) | (3<<5));
+ }
+ nicunlock(ctlr);
+
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ csr32w(ctlr, FhRxConfig, 0);
+ csr32w(ctlr, FhRxWptr, 0);
+ csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8);
+ csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4);
+ csr32w(ctlr, FhRxConfig,
+ FhRxConfigEna |
+ FhRxConfigIgnRxfEmpty |
+ FhRxConfigIrqDstHost |
+ FhRxConfigSingleFrame |
+ (Nrxlog << FhRxConfigNrbdShift));
+ csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
+ nicunlock(ctlr);
+
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ if(ctlr->type != Type4965)
+ prphwrite(ctlr, SchedTxFact5000, 0);
+ else
+ prphwrite(ctlr, SchedTxFact4965, 0);
+ csr32w(ctlr, FhKwAddr, PCIWADDR(ctlr->kwpage) >> 4);
+ for(q = (ctlr->type != Type4965) ? 19 : 15; q >= 0; q--)
+ csr32w(ctlr, FhCbbcQueue + q*4, PCIWADDR(ctlr->tx[q].d) >> 8);
+ nicunlock(ctlr);
+
+ for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--)
+ csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna);
+
+ csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
+ csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
+
+ ctlr->broken = 0;
+ ctlr->wait.r = 0;
+ ctlr->wait.w = 0;
+
+ ctlr->ie = Idefmask;
+ csr32w(ctlr, Imr, ctlr->ie);
+ csr32w(ctlr, Isr, ~0);
+
+ if(ctlr->type >= Type6000)
+ csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff);
+
+ return nil;
+}
+
+static char*
+postboot(Ctlr *ctlr)
+{
+ uint ctxoff, ctxlen, dramaddr, txfact;
+ char *err;
+ int i, q;
+
+ if((err = niclock(ctlr)) != nil)
+ return err;
+
+ if(ctlr->type != Type4965){
+ dramaddr = SchedDramAddr5000;
+ ctxoff = SchedCtxOff5000;
+ ctxlen = SchedCtxLen5000;
+ txfact = SchedTxFact5000;
+ } else {
+ dramaddr = SchedDramAddr4965;
+ ctxoff = SchedCtxOff4965;
+ ctxlen = SchedCtxLen4965;
+ txfact = SchedTxFact4965;
+ }
+
+ ctlr->sched.base = prphread(ctlr, SchedSramAddr);
+ for(i=0; i < ctxlen; i += 4)
+ memwrite(ctlr, ctlr->sched.base + ctxoff + i, 0);
+
+ prphwrite(ctlr, dramaddr, PCIWADDR(ctlr->sched.s)>>10);
+
+ csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2);
+
+ if(ctlr->type != Type4965){
+ /* Enable chain mode for all queues, except command queue 4. */
+ prphwrite(ctlr, SchedQChainSel5000, 0xfffef);
+ prphwrite(ctlr, SchedAggrSel5000, 0);
+
+ for(q=0; q<20; q++){
+ prphwrite(ctlr, SchedQueueRdptr5000 + q*4, 0);
+ csr32w(ctlr, HbusTargWptr, q << 8);
+
+ memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 0);
+ /* Set scheduler window size and frame limit. */
+ memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16 | 64);
+ }
+ /* Enable interrupts for all our 20 queues. */
+ prphwrite(ctlr, SchedIntrMask5000, 0xfffff);
+ } else {
+ /* Disable chain mode for all our 16 queues. */
+ prphwrite(ctlr, SchedQChainSel4965, 0);
+
+ for(q=0; q<16; q++) {
+ prphwrite(ctlr, SchedQueueRdptr4965 + q*4, 0);
+ csr32w(ctlr, HbusTargWptr, q << 8);
+
+ /* Set scheduler window size. */
+ memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 64);
+ /* Set scheduler window size and frame limit. */
+ memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16);
+ }
+ /* Enable interrupts for all our 16 queues. */
+ prphwrite(ctlr, SchedIntrMask4965, 0xffff);
+ }
+
+ /* Identify TX FIFO rings (0-7). */
+ prphwrite(ctlr, txfact, 0xff);
+
+ /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
+ for(q=0; q<7; q++){
+ if(ctlr->type != Type4965){
+ static uchar qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
+ prphwrite(ctlr, SchedQueueStatus5000 + q*4, 0x00ff0018 | qid2fifo[q]);
+ } else {
+ static uchar qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
+ prphwrite(ctlr, SchedQueueStatus4965 + q*4, 0x0007fc01 | qid2fifo[q]<<1);
+ }
+ }
+ nicunlock(ctlr);
+
+ if(ctlr->type != Type4965){
+ uchar c[Tcmdsize];
+
+ /* disable wimax coexistance */
+ memset(c, 0, sizeof(c));
+ if((err = cmd(ctlr, 90, c, 4+4*16)) != nil)
+ return err;
+
+ if(ctlr->type != Type5150){
+ /* calibrate crystal */
+ memset(c, 0, sizeof(c));
+ c[0] = 15; /* code */
+ c[1] = 0; /* group */
+ c[2] = 1; /* ngroup */
+ c[3] = 1; /* isvalid */
+ c[4] = ctlr->eeprom.crystal;
+ c[5] = ctlr->eeprom.crystal>>16;
+ if((err = cmd(ctlr, 176, c, 8)) != nil)
+ return err;
+ }
+
+ if(ctlr->calib.done == 0){
+ /* query calibration (init firmware) */
+ memset(c, 0, sizeof(c));
+ put32(c + 0*(5*4) + 0, 0xffffffff);
+ put32(c + 0*(5*4) + 4, 0xffffffff);
+ put32(c + 0*(5*4) + 8, 0xffffffff);
+ put32(c + 2*(5*4) + 0, 0xffffffff);
+ if((err = cmd(ctlr, 101, c, (((2*(5*4))+4)*2)+4)) != nil)
+ return err;
+
+ /* wait to collect calibration records */
+ if(irqwait(ctlr, Ierr, 2000))
+ return "calibration failed";
+
+ if(ctlr->calib.done == 0){
+ print("iwl: no calibration results\n");
+ ctlr->calib.done = 1;
+ }
+ } else {
+ static uchar cmds[] = {8, 9, 11, 17, 16};
+
+ /* send calibration records (runtime firmware) */
+ for(q=0; q<nelem(cmds); q++){
+ Block *b;
+
+ i = cmds[q];
+ if(i == 8 && ctlr->type != Type5150)
+ continue;
+ if(i == 17 && (ctlr->type >= Type6000 || ctlr->type == Type5150))
+ continue;
+ if((b = ctlr->calib.cmd[i]) == nil)
+ continue;
+ b->ref++; /* dont free on command completion */
+ if((err = qcmd(ctlr, 4, 176, nil, 0, b)) != nil)
+ return err;
+ if((err = flushq(ctlr, 4)) != nil)
+ return err;
+ }
+
+ /* set tx antenna config */
+ put32(c, ctlr->rfcfg.txantmask & 7);
+ if((err = cmd(ctlr, 152, c, 4)) != nil)
+ return err;
+ }
+ }
+
+ return nil;
+}
+
static char*
loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size)
{
@@ -902,23 +1313,37 @@ loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size)
}
static char*
-bootfirmware(Ctlr *ctlr)
+boot(Ctlr *ctlr)
{
int i, n, size;
uchar *p, *dma;
FWImage *fw;
char *err;
- dma = nil;
fw = ctlr->fw;
if(fw->boot.text.size == 0){
+ if(ctlr->calib.done == 0){
+ if((err = loadfirmware1(ctlr, 0x00000000, fw->init.text.data, fw->init.text.size)) != nil)
+ return err;
+ if((err = loadfirmware1(ctlr, 0x00800000, fw->init.data.data, fw->init.data.size)) != nil)
+ return err;
+ csr32w(ctlr, Reset, 0);
+ if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
+ return "init firmware boot failed";
+ if((err = postboot(ctlr)) != nil)
+ return err;
+ if((err = reset(ctlr)) != nil)
+ return err;
+ }
if((err = loadfirmware1(ctlr, 0x00000000, fw->main.text.data, fw->main.text.size)) != nil)
return err;
if((err = loadfirmware1(ctlr, 0x00800000, fw->main.data.data, fw->main.data.size)) != nil)
return err;
csr32w(ctlr, Reset, 0);
- goto bootmain;
+ if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
+ return "main firmware boot failed";
+ return postboot(ctlr);
}
size = ROUND(fw->init.data.size, 16) + ROUND(fw->init.text.size, 16);
@@ -967,7 +1392,7 @@ bootfirmware(Ctlr *ctlr)
if(i == 1000){
nicunlock(ctlr);
free(dma);
- return "bootfirmware: bootcode timeout";
+ return "bootcode timeout";
}
prphwrite(ctlr, BsmWrCtrl, 1<<30);
@@ -1000,13 +1425,12 @@ bootfirmware(Ctlr *ctlr)
prphwrite(ctlr, BsmDramTextSize, fw->main.text.size | (1<<31));
nicunlock(ctlr);
-bootmain:
if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
free(dma);
return "main firmware boot failed";
}
free(dma);
- return nil;
+ return postboot(ctlr);
}
static int
@@ -1016,7 +1440,7 @@ txqready(void *arg)
return q->n < Ntx;
}
-static void
+static char*
qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
{
uchar *d, *c;
@@ -1027,7 +1451,7 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
ilock(ctlr);
q = &ctlr->tx[qid];
- while(q->n >= Ntx){
+ while(!ctlr->broken && q->n >= Ntx){
iunlock(ctlr);
qlock(q);
if(!waserror()){
@@ -1037,8 +1461,13 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
qunlock(q);
ilock(ctlr);
}
+ if(ctlr->broken){
+ iunlock(ctlr);
+ return "qcmd: broken";
+ }
q->n++;
+ q->lastcmd = code;
q->b[q->i] = block;
c = q->c + q->i * Tcmdsize;
d = q->d + q->i * Tdscsize;
@@ -1049,7 +1478,8 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
c[2] = q->i;
c[3] = qid;
- memmove(c+4, data, size);
+ if(size > 0)
+ memmove(c+4, data, size);
size += 4;
@@ -1061,11 +1491,8 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
put32(d, PCIWADDR(c)); d += 4;
put16(d, size << 4); d += 2;
if(block != nil){
- size = BLEN(block);
- if(size > Tbufsize)
- size = Tbufsize;
put32(d, PCIWADDR(block->rp)); d += 4;
- put16(d, size << 4);
+ put16(d, BLEN(block) << 4);
}
coherence();
@@ -1074,6 +1501,8 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
csr32w(ctlr, HbusTargWptr, (qid<<8) | q->i);
iunlock(ctlr);
+
+ return nil;
}
static int
@@ -1083,35 +1512,40 @@ txqempty(void *arg)
return q->n == 0;
}
-static void
+static char*
flushq(Ctlr *ctlr, uint qid)
{
TXQ *q;
+ int i;
q = &ctlr->tx[qid];
- while(q->n > 0){
- qlock(q);
+ qlock(q);
+ for(i = 0; i < 200 && !ctlr->broken; i++){
+ if(txqempty(q)){
+ qunlock(q);
+ return nil;
+ }
if(!waserror()){
tsleep(q, txqempty, q, 10);
poperror();
}
- qunlock(q);
}
+ qunlock(q);
+ if(ctlr->broken)
+ return "flushq: broken";
+ return "flushq: timeout";
}
-
-static void
-flushcmd(Ctlr *ctlr)
-{
- flushq(ctlr, 4);
-}
-
-static void
+static char*
cmd(Ctlr *ctlr, uint code, uchar *data, int size)
{
- qcmd(ctlr, 4, code, data, size, nil);
-}
+ char *err;
+ if(0) print("cmd %ud\n", code);
+ if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil)
+ return err;
+ return flushq(ctlr, 4);
+}
static void
setled(Ctlr *ctlr, int which, int on, int off)
@@ -1128,102 +1562,6 @@ setled(Ctlr *ctlr, int which, int on, int off)
cmd(ctlr, 72, c, sizeof(c));
}
-/*
- * initialization which runs after the firmware has been booted up
- */
-static void
-postboot(Ctlr *ctlr)
-{
- uint ctxoff, ctxlen, dramaddr, txfact;
- uchar c[8];
- char *err;
- int i, q;
-
- if((err = niclock(ctlr)) != nil)
- error(err);
-
- if(ctlr->type != Type4965){
- dramaddr = SchedDramAddr5000;
- ctxoff = SchedCtxOff5000;
- ctxlen = SchedCtxLen5000;
- txfact = SchedTxFact5000;
- } else {
- dramaddr = SchedDramAddr4965;
- ctxoff = SchedCtxOff4965;
- ctxlen = SchedCtxLen4965;
- txfact = SchedTxFact4965;
- }
-
- ctlr->sched.base = prphread(ctlr, SchedSramAddr);
- for(i=0; i < ctxlen; i += 4)
- memwrite(ctlr, ctlr->sched.base + ctxoff + i, 0);
-
- prphwrite(ctlr, dramaddr, PCIWADDR(ctlr->sched.s)>>10);
-
- csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2);
-
- if(ctlr->type != Type4965){
- /* Enable chain mode for all queues, except command queue 4. */
- prphwrite(ctlr, SchedQChainSel5000, 0xfffef);
- prphwrite(ctlr, SchedAggrSel5000, 0);
-
- for(q=0; q<20; q++){
- prphwrite(ctlr, SchedQueueRdptr5000 + q*4, 0);
- csr32w(ctlr, HbusTargWptr, q << 8);
-
- memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 0);
- /* Set scheduler window size and frame limit. */
- memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16 | 64);
- }
- /* Enable interrupts for all our 20 queues. */
- prphwrite(ctlr, SchedIntrMask5000, 0xfffff);
- } else {
- /* Disable chain mode for all our 16 queues. */
- prphwrite(ctlr, SchedQChainSel4965, 0);
-
- for(q=0; q<16; q++) {
- prphwrite(ctlr, SchedQueueRdptr4965 + q*4, 0);
- csr32w(ctlr, HbusTargWptr, q << 8);
-
- /* Set scheduler window size. */
- memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 64);
- /* Set scheduler window size and frame limit. */
- memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16);
- }
- /* Enable interrupts for all our 16 queues. */
- prphwrite(ctlr, SchedIntrMask4965, 0xffff);
- }
-
- /* Identify TX FIFO rings (0-7). */
- prphwrite(ctlr, txfact, 0xff);
-
- /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
- for(q=0; q<7; q++){
- if(ctlr->type != Type4965){
- static uchar qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
- prphwrite(ctlr, SchedQueueStatus5000 + q*4, 0x00ff0018 | qid2fifo[q]);
- } else {
- static uchar qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
- prphwrite(ctlr, SchedQueueStatus4965 + q*4, 0x0007fc01 | qid2fifo[q]<<1);
- }
- }
- nicunlock(ctlr);
-
- if(ctlr->type != Type4965){
- if(ctlr->type != Type5150){
- memset(c, 0, sizeof(c));
- c[0] = 15; /* code */
- c[1] = 0; /* grup */
- c[2] = 1; /* ngroup */
- c[3] = 1; /* isvalid */
- put16(c+4, ctlr->eeprom.crystal);
- cmd(ctlr, 176, c, 8);
- }
- put32(c, ctlr->rfcfg.txantmask & 7);
- cmd(ctlr, 152, c, 4);
- }
-}
-
static void
addnode(Ctlr *ctlr, uchar id, uchar *addr)
{
@@ -1267,6 +1605,7 @@ rxon(Ether *edev, Wnode *bss)
uchar c[Tcmdsize], *p;
int filter, flags;
Ctlr *ctlr;
+ char *err;
ctlr = edev->ctlr;
filter = FilterMulticast | FilterBeacon;
@@ -1322,7 +1661,10 @@ rxon(Ether *edev, Wnode *bss)
put16(p, 0); p += 2; /* acquisition */
p += 2; /* reserved */
}
- cmd(ctlr, 16, c, p - c);
+ if((err = cmd(ctlr, 16, c, p - c)) != nil){
+ print("rxon error: %s\n", err);
+ return;
+ }
if(ctlr->bcastnodeid == -1){
ctlr->bcastnodeid = (ctlr->type != Type4965) ? 15 : 31;
@@ -1332,7 +1674,6 @@ rxon(Ether *edev, Wnode *bss)
ctlr->bssnodeid = 0;
addnode(ctlr, ctlr->bssnodeid, bss->bssid);
}
- flushcmd(ctlr);
}
static struct ratetab {
@@ -1384,6 +1725,11 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
ctlr = edev->ctlr;
qlock(ctlr);
+ if(ctlr->attached == 0 || ctlr->broken){
+ qunlock(ctlr);
+ freeb(b);
+ return;
+ }
if(ctlr->prom == 0)
if(wn->aid != ctlr->aid
@@ -1451,21 +1797,6 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
qcmd(ctlr, 0, 28, c, p - c, b);
}
-static int
-rbplant(Ctlr *ctlr, int i)
-{
- Block *b;
-
- b = iallocb(Rbufsize + 256);
- if(b == nil)
- return -1;
- b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
- memset(b->rp, 0, Rdscsize);
- ctlr->rx.b[i] = b;
- ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8;
- return 0;
-}
-
static long
iwlctl(Ether *edev, void *buf, long n)
{
@@ -1579,21 +1910,19 @@ iwlattach(Ether *edev)
FWImage *fw;
Ctlr *ctlr;
char *err;
- RXQ *rx;
- TXQ *tx;
- int i, q;
ctlr = edev->ctlr;
eqlock(ctlr);
if(waserror()){
+ print("#l%d: %s\n", edev->ctlrno, up->errstr);
+ if(ctlr->power)
+ poweroff(ctlr);
qunlock(ctlr);
nexterror();
}
if(ctlr->attached == 0){
- if((csr32r(ctlr, Gpc) & RfKill) == 0){
- print("#l%d: wifi disabled by switch\n", edev->ctlrno);
+ if((csr32r(ctlr, Gpc) & RfKill) == 0)
error("wifi disabled by switch");
- }
if(ctlr->wifi == nil)
ctlr->wifi = wifiattach(edev, transmit);
@@ -1610,104 +1939,10 @@ iwlattach(Ether *edev)
ctlr->fw = fw;
}
- rx = &ctlr->rx;
- rx->i = 0;
- if(rx->b == nil)
- rx->b = malloc(sizeof(Block*) * Nrx);
- if(rx->p == nil)
- rx->p = mallocalign(sizeof(u32int) * Nrx, 256, 0, 0);
- if(rx->s == nil)
- rx->s = mallocalign(Rstatsize, 16, 0, 0);
- if(rx->b == nil || rx->p == nil || rx->s == nil)
- error("no memory for rx ring");
- memset(rx->s, 0, Rstatsize);
- for(i=0; i<Nrx; i++){
- rx->p[i] = 0;
- if(rx->b[i] != nil){
- freeb(rx->b[i]);
- rx->b[i] = nil;
- }
- if(rbplant(ctlr, i) < 0)
- error("no memory for rx descriptors");
- }
-
- for(q=0; q<nelem(ctlr->tx); q++){
- tx = &ctlr->tx[q];
- tx->i = 0;
- tx->n = 0;
- if(tx->b == nil)
- tx->b = malloc(sizeof(Block*) * Ntx);
- if(tx->d == nil)
- tx->d = mallocalign(Tdscsize * Ntx, 256, 0, 0);
- if(tx->c == nil)
- tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0);
- if(tx->b == nil || tx->d == nil || tx->c == nil)
- error("no memory for tx ring");
- memset(tx->d, 0, Tdscsize * Ntx);
- }
-
- if(ctlr->sched.s == nil)
- ctlr->sched.s = mallocalign(512 * nelem(ctlr->tx) * 2, 1024, 0, 0);
- if(ctlr->kwpage == nil)
- ctlr->kwpage = mallocalign(4096, 4096, 0, 0);
-
- if((err = niclock(ctlr)) != nil)
+ if((err = reset(ctlr)) != nil)
error(err);
- prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
- nicunlock(ctlr);
-
- csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi);
-
- if((err = niclock(ctlr)) != nil)
+ if((err = boot(ctlr)) != nil)
error(err);
- prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis);
- nicunlock(ctlr);
-
- if((err = niclock(ctlr)) != nil)
- error(err);
- csr32w(ctlr, FhRxConfig, 0);
- csr32w(ctlr, FhRxWptr, 0);
- csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8);
- csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4);
- csr32w(ctlr, FhRxConfig,
- FhRxConfigEna |
- FhRxConfigIgnRxfEmpty |
- FhRxConfigIrqDstHost |
- FhRxConfigSingleFrame |
- (Nrxlog << FhRxConfigNrbdShift));
- csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
- nicunlock(ctlr);
-
- if((err = niclock(ctlr)) != nil)
- error(err);
-
- if(ctlr->type != Type4965)
- prphwrite(ctlr, SchedTxFact5000, 0);
- else
- prphwrite(ctlr, SchedTxFact4965, 0);
-
- csr32w(ctlr, FhKwAddr, PCIWADDR(ctlr->kwpage) >> 4);
-
- for(q = (ctlr->type != Type4965) ? 19 : 15; q >= 0; q--)
- csr32w(ctlr, FhCbbcQueue + q*4, PCIWADDR(ctlr->tx[q].d) >> 8);
-
- nicunlock(ctlr);
-
- for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--)
- csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna);
-
- csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
- csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
-
- ctlr->ie = Idefmask;
- csr32w(ctlr, Imr, ctlr->ie);
- csr32w(ctlr, Isr, ~0);
-
- if(ctlr->type >= Type6000)
- csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff);
-
- bootfirmware(ctlr);
- postboot(ctlr);
ctlr->bcastnodeid = -1;
ctlr->bssnodeid = -1;
@@ -1735,7 +1970,7 @@ receive(Ctlr *ctlr)
uint hw;
rx = &ctlr->rx;
- if(rx->s == nil || rx->b == nil)
+ if(ctlr->broken || rx->s == nil || rx->b == nil)
return;
for(hw = get16(rx->s) % Nrx; rx->i != hw; rx->i = (rx->i + 1) % Nrx){
uchar type, flags, idx, qid;
@@ -1786,8 +2021,21 @@ receive(Ctlr *ctlr)
case 28: /* tx done */
break;
case 102: /* calibration result (Type5000 only) */
- break;
+ if(len < 4)
+ break;
+ idx = d[0];
+ if(idx >= nelem(ctlr->calib.cmd))
+ break;
+ if(rbplant(ctlr, rx->i) < 0)
+ break;
+ if(ctlr->calib.cmd[idx] != nil)
+ freeb(ctlr->calib.cmd[idx]);
+ b->rp = d;
+ b->wp = d + len;
+ ctlr->calib.cmd[idx] = b;
+ continue;
case 103: /* calibration done (Type5000 only) */
+ ctlr->calib.done = 1;
break;
case 130: /* start scan */
break;
@@ -1857,6 +2105,7 @@ iwlinterrupt(Ureg*, void *arg)
if((isr & (Iswrx | Ifhrx | Irxperiodic)) || (fhisr & Ifhrx))
receive(ctlr);
if(isr & Ierr){
+ ctlr->broken = 1;
iprint("#l%d: fatal firmware error\n", edev->ctlrno);
dumpctlr(ctlr);
}
@@ -1895,6 +2144,7 @@ iwlpci(void)
case 0x4230: /* WiFi Link 4965 */
case 0x4236: /* WiFi Link 5300 AGN */
case 0x4237: /* Wifi Link 5100 AGN */
+ case 0x422b: /* Centrino Ultimate-N 6300 */
break;
}