📄 sdmv50xx.c
字号:
}else{ *cmd++ = CMD(ARlba0, srb->lba[0]); *cmd++ = CMD(ARlba1, srb->lba[1]); *cmd++ = CMD(ARlba2, srb->lba[2]); *cmd++ = CMD(ARdev, srb->lba[3] | 0xE0); } *cmd++ = CMD(ARcmd, srb->cmd) | (1<<15); USED(cmd);}static voidstartsrb(Drive *d, Srb *srb){ int i; Edma *edma; Prd *prd; Tx *tx; if(d->nsrb >= nelem(d->srb)){ srb->next = nil; if(d->srbhead) d->srbtail->next = srb; else d->srbhead = srb; d->srbtail = srb; return; } d->nsrb++; for(i=0; i<nelem(d->srb); i++) if(d->srb[i] == nil) break; if(i == nelem(d->srb)) panic("sdmv50xx: no free srbs"); d->srb[i] = srb; edma = d->edma; tx = (Tx*)KADDR(edma->txi); tx->flag = (i<<1) | (srb->req == SRBread); prd = KADDR(tx->prdpa); prd->pa = PADDR(srb->data); prd->count = srb->count; prd->flag = PRDeot; atarequest(tx->regs, srb, d->flag&Dext); coherence(); edma->txi = advance(edma->txi, 5);}static voidcompletesrb(Drive *d){ Edma *edma; Rx *rx; Srb *srb; edma = d->edma; if((edma->ctl & eEnEDMA) == 0) return; while((edma->rxo & (0x1F<<3)) != (edma->rxi & (0x1F<<3))){ rx = (Rx*)KADDR(edma->rxo); if(srb = d->srb[rx->cid]){ d->srb[rx->cid] = nil; d->nsrb--; if(rx->cDevSts & (ATAerr|ATAdf)) srb->flag |= SFerror; srb->flag |= SFdone; srb->sta = rx->cDevSts; wakeup(srb); }else iprint("srb missing\n"); edma->rxo = advance(edma->rxo, 3); if(srb = d->srbhead){ d->srbhead = srb->next; startsrb(d, srb); } }} static voidabortallsrb(Drive *d){ int i; Srb *srb; for(i=0; i<nelem(d->srb); i++){ if(srb = d->srb[i]){ d->srb[i] = nil; d->nsrb--; srb->flag |= SFerror|SFdone; wakeup(srb); } } while(srb = d->srbhead){ d->srbhead = srb->next; srb->flag |= SFerror|SFdone; wakeup(srb); } }static intsrbdone(void *v){ Srb *srb; srb = v; return srb->flag & SFdone;}/* * Interrupts */static voidmv50interrupt(Ureg*, void *a){ int i; ulong cause; Ctlr *ctlr; Drive *drive; ctlr = a; ilock(ctlr); cause = *(ulong*)(ctlr->mmio + 0x1D60); DPRINT("sd%c: mv50interrupt: 0x%lux\n", ctlr->sdev->idno, cause); for(i=0; i<ctlr->ndrive; i++){ if(cause & (3<<(i*2+i/4))){ drive = &ctlr->drive[i]; ilock(drive); updatedrive(drive, drive->edma->iec); while(ctlr->chip[i/4].arb->ic & (0x0101 << (i%4))){ ctlr->chip[i/4].arb->ic = ~(0x101 << (i%4)); completesrb(drive); } iunlock(drive); } } iunlock(ctlr);}/* * Device discovery */static SDev*mv50pnp(void){ int i, nunit; uchar *base; ulong io; void *mem; Ctlr *ctlr; Pcidev *p; SDev *head, *tail, *sdev; DPRINT("mv50pnp\n"); p = nil; head = nil; tail = nil; while((p = pcimatch(p, 0x11AB, 0)) != nil){ switch(p->did){ case 0x5041: nunit = 4; break; case 0x5081: nunit = 8; break; default: continue; } if((sdev = malloc(sizeof(SDev))) == nil) continue; if((ctlr = malloc(sizeof(Ctlr))) == nil){ free(sdev); continue; } io = p->mem[0].bar & ~0x0F; mem = vmap(io, p->mem[0].size); if(mem == 0){ print("sdmv50xx: address 0x%luX in use\n", io); free(sdev); free(ctlr); continue; } sdev->ifc = &sdmv50xxifc; sdev->ctlr = ctlr; sdev->nunit = nunit; sdev->idno = 'E'; ctlr->sdev = sdev; ctlr->irq = p->intl; ctlr->tbdf = p->tbdf; ctlr->pcidev = p; ctlr->mmio = mem; ctlr->nchip = (nunit+3)/4; ctlr->ndrive = nunit; for(i=0; i<ctlr->nchip; i++){ base = ctlr->mmio+0x20000+0x10000*i; ctlr->chip[i].arb = (Arb*)base; ctlr->chip[i].edma = (Edma*)(base + 0x2000); } if(head) tail->next = sdev; else head = sdev; tail = sdev; } return head;}/* * Enable the controller. Each disk has its own interrupt mask, * and those get enabled as the disks are brought online. */static intmv50enable(SDev *sdev){ char name[32]; Ctlr *ctlr; DPRINT("sd%c: enable\n", sdev->idno); ctlr = sdev->ctlr; snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name); intrenable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name); return 1;}/* * Disable the controller. */static intmv50disable(SDev *sdev){ char name[32]; int i; Ctlr *ctlr; Drive *drive; DPRINT("sd%c: disable\n", sdev->idno); ctlr = sdev->ctlr; ilock(ctlr); for(i=0; i<ctlr->sdev->nunit; i++){ drive = &ctlr->drive[i]; ilock(drive); disabledrive(drive); iunlock(drive); } iunlock(ctlr); snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name); intrdisable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name); return 0;}/* * Clean up all disk structures. Already disabled. * Could keep count of number of allocated controllers * and free the srblist when it drops to zero. */static voidmv50clear(SDev *sdev){ int i; Ctlr *ctlr; Drive *d; DPRINT("sd%c: clear\n", sdev->idno); ctlr = sdev->ctlr; for(i=0; i<ctlr->ndrive; i++){ d = &ctlr->drive[i]; free(d->tx); free(d->rx); free(d->prd); } free(ctlr);}/* * Check that there is a disk or at least a hot swap bay in the drive. */static intmv50verify(SDunit *unit){ Ctlr *ctlr; Drive *drive; DPRINT("%s: verify\n", unit->name); /* * First access of unit. */ ctlr = unit->dev->ctlr; drive = &ctlr->drive[unit->subno]; ilock(ctlr); ilock(drive); if(!configdrive(ctlr, drive, unit) || !enabledrive(drive)){ iunlock(drive); iunlock(ctlr); return 0; } /* * Need to reset the drive before the first call to * identifydrive, or else the satawait in setudma will * freeze the machine when accessing edma->cmdstat. * I do not understand this. -rsc */ updatedrive(drive, eDevDis); iunlock(drive); iunlock(ctlr); return 1;}/* * Check whether the disk is online. */static intmv50online(SDunit *unit){ Ctlr *ctlr; Drive *drive; ctlr = unit->dev->ctlr; drive = &ctlr->drive[unit->subno]; ilock(drive); if(drive->state == Dready){ unit->sectors = drive->sectors; unit->secsize = 512; iunlock(drive); return 1; } DPRINT("%s: online %s\n", unit->name, diskstates[drive->state]); if(drive->state == Dnew){ identifydrive(drive); if(drive->state == Dready){ unit->sectors = drive->sectors; unit->secsize = 512; iunlock(drive); return 2; /* media changed */ } } iunlock(drive); return 0;}/* * Register dumps */typedef struct Regs Regs;struct Regs{ ulong offset; char *name;};static Regs regsctlr[] ={ 0x0C28, "pci serr# mask", 0x1D40, "pci err addr low", 0x1D44, "pci err addr hi", 0x1D48, "pci err attr", 0x1D50, "pci err cmd", 0x1D58, "pci intr cause", 0x1D5C, "pci mask cause", 0x1D60, "device micr", 0x1D64, "device mimr",};static Regs regsarb[] ={ 0x0004, "arb rqop", 0x0008, "arb rqip", 0x000C, "arb ict", 0x0010, "arb itt", 0x0014, "arb ic", 0x0018, "arb btc", 0x001C, "arb bts", 0x0020, "arb bpc",};static Regs regsbridge[] ={ 0x0000, "bridge status", 0x0004, "bridge serror", 0x0008, "bridge sctrl", 0x000C, "bridge phyctrl", 0x003C, "bridge ctrl", 0x0074, "bridge phymode",};static Regs regsedma[] ={ 0x0000, "edma config", 0x0004, "edma timer", 0x0008, "edma iec", 0x000C, "edma iem", 0x0010, "edma txbasehi", 0x0014, "edma txi", 0x0018, "edma txo", 0x001C, "edma rxbasehi", 0x0020, "edma rxi", 0x0024, "edma rxo", 0x0028, "edma c", 0x002C, "edma tc", 0x0030, "edma status", 0x0034, "edma iordyto",/* 0x0100, "edma pio", 0x0104, "edma err", 0x0108, "edma sectors", 0x010C, "edma lba0", 0x0110, "edma lba1", 0x0114, "edma lba2", 0x0118, "edma lba3", 0x011C, "edma cmdstat", 0x0120, "edma altstat",*/};static char*rdregs(char *p, char *e, void *base, Regs *r, int n, char *prefix){ int i; for(i=0; i<n; i++) p = seprint(p, e, "%s%s%-19s %.8ux\n", prefix ? prefix : "", prefix ? ": " : "", r[i].name, *(u32int*)((uchar*)base+r[i].offset)); return p;}static char*rdinfo(char *p, char *e, ushort *info){ int i; p = seprint(p, e, "info"); for(i=0; i<256; i++){ p = seprint(p, e, "%s%.4ux%s", i%8==0 ? "\t" : "", info[i], i%8==7 ? "\n" : ""); } return p;}static intmv50rctl(SDunit *unit, char *p, int l){ char *e, *op; Ctlr *ctlr; Drive *drive; if((ctlr = unit->dev->ctlr) == nil) return 0; drive = &ctlr->drive[unit->subno]; e = p+l; op = p; if(drive->state == Dready){ p = seprint(p, e, "model %s\n", drive->model); p = seprint(p, e, "serial %s\n", drive->serial); p = seprint(p, e, "firmware %s\n", drive->firmware); }else p = seprint(p, e, "no disk present\n"); p = seprint(p, e, "geometry %llud 512\n", drive->sectors); p = rdinfo(p, e, drive->info); p = rdregs(p, e, drive->chip->arb, regsarb, nelem(regsarb), nil); p = rdregs(p, e, drive->bridge, regsbridge, nelem(regsbridge), nil); p = rdregs(p, e, drive->edma, regsedma, nelem(regsedma), nil); return p-op;}static intmv50wctl(SDunit *unit, Cmdbuf *cb){ Ctlr *ctlr; Drive *drive; USED(unit); if(strcmp(cb->f[0], "reset") == 0){ ctlr = unit->dev->ctlr; drive = &ctlr->drive[unit->subno]; ilock(drive); updatedrive(drive, eDevDis); iunlock(drive); return 0; } cmderror(cb, Ebadctl); return -1;}static char*mv50rtopctl(SDev *sdev, char *p, char *e){ char name[10]; Ctlr *ctlr; ctlr = sdev->ctlr; if(ctlr == nil) return p; snprint(name, sizeof name, "sd%c", sdev->idno); p = rdregs(p, e, ctlr->mmio, regsctlr, nelem(regsctlr), name); /* info for first disk */ p = rdregs(p, e, ctlr->chip[0].arb, regsarb, nelem(regsarb), name); p = rdregs(p, e, &ctlr->chip[0].arb->bridge[0], regsbridge, nelem(regsbridge), name); p = rdregs(p, e, &ctlr->chip[0].edma[0], regsedma, nelem(regsedma), name); return p;}static intmv50rio(SDreq *r){ int count, max, n, status; uchar *cmd, *data; uvlong lba; Ctlr *ctlr; Drive *drive; SDunit *unit; Srb *srb; unit = r->unit; ctlr = unit->dev->ctlr; drive = &ctlr->drive[unit->subno]; cmd = r->cmd; if((status = sdfakescsi(r, drive->info, sizeof drive->info)) != SDnostatus){ /* XXX check for SDcheck here */ r->status = status; return status; } switch(cmd[0]){ case 0x28: /* read */ case 0x2A: /* write */ break; default: print("sdmv50xx: bad cmd 0x%.2ux\n", cmd[0]); r->status = SDcheck; return SDcheck; } lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; count = (cmd[7]<<8)|cmd[8]; if(r->data == nil) return SDok; if(r->dlen < count*unit->secsize) count = r->dlen/unit->secsize; /* * Could arrange here to have an Srb always outstanding: * * lsrb = nil; * while(count > 0 || lsrb != nil){ * srb = nil; * if(count > 0){ * srb = issue next srb; * } * if(lsrb){ * sleep on lsrb and handle it * } * } * * On the disks I tried, this didn't help. If anything, * it's a little slower. -rsc */ data = r->data; while(count > 0){ /* * Max is 128 sectors (64kB) because prd->count is 16 bits. */ max = 128; n = count; if(n > max) n = max; srb = srbrw(cmd[0]==0x28 ? SRBread : SRBwrite, drive, data, n, lba); ilock(drive); startsrb(drive, srb); iunlock(drive); /* * Cannot let user interrupt the DMA. */ while(waserror()) ; tsleep(srb, srbdone, srb, 60*1000); poperror(); if(!(srb->flag & SFdone)){ ilock(drive); if(!(srb->flag & SFdone)){ /* * DMA didn't finish but we have to let go of * the data buffer. Reset the drive to (try to) keep it * from using the buffer after we're gone. */ iprint("%s: i/o timeout\n", unit->name); updatedrive(drive, eDevDis); enabledrive(drive); freesrb(srb); iunlock(drive); error("i/o timeout"); } iunlock(drive); } if(srb->flag & SFerror){ freesrb(srb); error("i/o error"); } freesrb(srb); count -= n; lba += n; data += n*unit->secsize; } r->rlen = data - (uchar*)r->data; return SDok; }SDifc sdmv50xxifc = { "mv50xx", /* name */ mv50pnp, /* pnp */ nil, /* legacy */ mv50enable, /* enable */ mv50disable, /* disable */ mv50verify, /* verify */ mv50online, /* online */ mv50rio, /* rio */ mv50rctl, /* rctl */ mv50wctl, /* wctl */ scsibio, /* bio */ nil, /* probe */ mv50clear, /* clear */ mv50rtopctl, /* rtopctl */};/* * The original driver on which this one is based came with the * following notice: * * Copyright 2005 * Coraid, Inc. * * This software is provided `as-is,' without any express or implied * warranty. In no event will the author be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product documentation * would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -