📄 sdmv50xx.c
字号:
/* * Marvell 88SX[56]0[48][01] Serial ATA (SATA) driver * * See MV-S101357-00 Rev B Marvell PCI/PCI-X to 8-Port/4-Port * SATA Host Controller, ATA-5 ANSI NCITS 340-2000. * * This is a heavily-modified version of a driver written by Coraid, Inc. * The original copyright notice appears at the end of this file. */#ifdef FS#include "all.h"#include "io.h"#include "mem.h"#include "sd.h"#include "compat.h"#else#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/sd.h"#endifenum { DEBUGPR = 0, IDEBUG = 0, /* old stuff carried forward */ NCtlr= 8, NCtlrdrv= 8, NDrive= NCtlr*NCtlrdrv, Maxxfer= 16*1024, /* maximum transfer size/cmd */ Read = 0, Write, Drvmagic = 0xcafebabeUL, Ctlrmagic = 0xfeedfaceUL,};#define DPRINT if(DEBUGPR)print#define IDPRINT if(IDEBUG)printenum { SrbRing = 32, /* Addresses of ATA register */ ARcmd = 027, ARdev = 026, ARerr = 021, ARfea = 021, ARlba2 = 025, ARlba1 = 024, ARlba0 = 023, ARseccnt = 022, ARstat = 027, ATAerr = (1<<0), ATAdrq = (1<<3), ATAdf = (1<<5), ATAdrdy = (1<<6), ATAbusy = (1<<7), ATAabort = (1<<2), ATAeIEN = (1<<1), ATAsrst = (1<<2), ATAhob = (1<<7), SFdone = (1<<0), SFerror = (1<<1), SRBident = 0, SRBread, SRBwrite, SRBsmart, SRBnodata = 0, SRBdatain, SRBdataout, RQread = 1, /* data coming IN from device */ PRDeot = (1<<15), /* EDMA interrupt error cause register */ ePrtDataErr = (1<<0), ePrtPRDErr = (1<<1), eDevErr = (1<<2), eDevDis = (1<<3), eDevCon = (1<<4), eOverrun = (1<<5), eUnderrun = (1<<6), eSelfDis = (1<<8), ePrtCRQBErr = (1<<9), ePrtCRPBErr = (1<<10), ePrtIntErr = (1<<11), eIORdyErr = (1<<12), /* EDMA Command Register */ eEnEDMA = (1<<0), eDsEDMA = (1<<1), eAtaRst = (1<<2), /* Interrupt mask for errors we care about */ IEM = (eDevDis | eDevCon | eSelfDis), /* drive states */ Dnull = 0, Dnew, Dident, Dready, Derror, Dmissing, Dunconfig, /* drive flags */ Dext = (1<<0), /* use ext commands */ Dpio = (1<<1), /* doing pio */ Dwanted = (1<<2), /* someone wants an srb entry */ Dedma = (1<<3), /* device in edma mode */ Dpiowant = (1<<4), /* some wants to use the pio mode */};static char* diskstates[] ={ "null", "new", "ident", "ready", "error", "missing", "unconfigured",};extern SDifc sdmv50xxifc;typedef struct Arb Arb;typedef struct Bridge Bridge;typedef struct Chip Chip;typedef struct Ctlr Ctlr;typedef struct Drive Drive;typedef struct Edma Edma;typedef struct Prd Prd;typedef struct Rx Rx;typedef struct Srb Srb;typedef struct Tx Tx;struct Chip /* pointers to per-Chip mmio */{ Arb *arb; Edma *edma; /* array of 4 */};struct Drive /* a single disk */{ Lock; Ctlr *ctlr; SDunit *unit;// int subno; char name[10]; ulong magic; Bridge *bridge; Edma *edma; Chip *chip; int chipx; int state; int flag; uvlong sectors; char serial[20+1]; char firmware[8+1]; char model[40+1]; ushort info[256]; Srb *srb[SrbRing-1]; int nsrb; Prd *prd; Tx *tx; Rx *rx; Srb *srbhead; Srb *srbtail; /* added for file server */ /* for ata* routines */ int online; Devsize offset; int driveno; /* ctlr*NCtlrdrv + unit */ /* * old stuff carried forward. it's in Drive not Ctlr to maximise * possible concurrency. */ uchar buf[RBUFSIZE];};struct Ctlr /* a single PCI card */{ Lock; int irq; int tbdf; ulong magic; int enabled; SDev *sdev; Pcidev *pcidev; uchar *mmio; Chip chip[2]; int nchip; Drive drive[NCtlrdrv]; int ndrive; Target target[NTarget]; /* contains filters for stats */ /* old stuff carried forward */ QLock idelock; /* make seek & i/o atomic in ide* routines */};struct Srb /* request buffer */{ Lock; Rendez; Srb *next; Drive *drive; uvlong blockno; int count; int req; int flag; uchar *data; uchar cmd; uchar lba[6]; uchar sectors; int sta; int err;};/* * Memory-mapped I/O registers in many forms. */struct Bridge /* memory-mapped per-Drive registers */{ ulong status; ulong serror; ulong sctrl; ulong phyctrl; char fill1[0x2c]; ulong ctrl; char fill2[0x34]; ulong phymode; char fill3[0x88]; /* pad to 0x100 in length */};struct Arb /* memory-mapped per-Chip registers */{ ulong fill0; ulong rqop; /* request queue out-pointer */ ulong rqip; /* response queue in pointer */ ulong ict; /* inerrupt caolescing threshold */ ulong itt; /* interrupt timer threshold */ ulong ic; /* interrupt cause */ ulong btc; /* bridges test control */ ulong bts; /* bridges test status */ ulong bpc; /* bridges pin configuration */ char fill1[0xdc]; Bridge bridge[4];};struct Edma /* memory-mapped per-Drive DMA-related registers */{ ulong config; /* configuration register */ ulong timer; ulong iec; /* interrupt error cause */ ulong iem; /* interrupt error mask */ ulong txbasehi; /* request queue base address high */ ulong txi; /* request queue in pointer */ ulong txo; /* request queue out pointer */ ulong rxbasehi; /* response queue base address high */ ulong rxi; /* response queue in pointer */ ulong rxo; /* response queue out pointer */ ulong ctl; /* command register */ ulong testctl; /* test control */ ulong status; ulong iordyto; /* IORDY timeout */ char fill[0xc8]; ushort pio; /* data register */ char pad0[2]; uchar err; /* features and error */ char pad1[3]; uchar seccnt; /* sector count */ char pad2[3]; uchar lba0; char pad3[3]; uchar lba1; char pad4[3]; uchar lba2; char pad5[3]; uchar lba3; char pad6[3]; uchar cmdstat; /* cmd/status */ char pad7[3]; uchar altstat; /* alternate status */ char fill2[0x1edc]; /* pad to 0x2000 bytes */};/* * Memory structures shared with card. */struct Prd /* physical region descriptor */{ ulong pa; /* byte address of physical memory */ ushort count; /* byte count (bit0 must be 0) */ ushort flag; ulong zero; /* high long of 64 bit address */ ulong reserved;};struct Tx /* command request block */{ ulong prdpa; /* physical region descriptor table structures */ ulong zero; /* must be zero (high long of prd address) */ ushort flag; /* control flags */ ushort regs[11];};struct Rx /* command response block */{ ushort cid; /* cID of response */ uchar cEdmaSts; /* EDMA status */ uchar cDevSts; /* status from disk */ ulong ts; /* time stamp */};/* file-server-specific data */static Ctlr *mvsatactlr[NCtlr];static SDev *sdevs[NCtlr];static Drive *mvsatadrive[NDrive];static SDunit *sdunits[NDrive];static Drive *mvsatadriveprobe(int driveno);static void statsinit(void);/* * Little-endian parsing for drive data. */static ushortlhgets(void *p){ uchar *a = p; return ((ushort) a[1] << 8) | a[0];}static ulonglhgetl(void *p){ uchar *a = p; return ((ulong) lhgets(a+2) << 16) | lhgets(a);}static uvlonglhgetv(void *p){ uchar *a = p; return ((uvlong) lhgetl(a+4) << 32) | lhgetl(a);}static voididmove(char *p, ushort *a, int n){ char *op; int i; op = p; for(i=0; i<n/2; i++){ *p++ = a[i]>>8; *p++ = a[i]; } while(p>op && *--p == ' ') *p = 0;}/* * Request buffers. */static struct{ Lock; Srb *freechain; int nalloc;} srblist;static Srb*allocsrb(void){ Srb *p; ilock(&srblist); if((p = srblist.freechain) == nil){ srblist.nalloc++; iunlock(&srblist); p = smalloc(sizeof *p); }else{ srblist.freechain = p->next; iunlock(&srblist); } return p;}static voidfreesrb(Srb *p){ ilock(&srblist); p->next = srblist.freechain; srblist.freechain = p; iunlock(&srblist);}/* * Wait for a byte to be a particular value. */static intsatawait(uchar *p, uchar mask, uchar v, int ms){ int i;// DPRINT("satawait %p %#x %#x %d...", p, mask, v, ms);// DPRINT("!%#x...", *p); for(i=0; i<ms && (*p & mask) != v; i++){ if(i%1000 == 0) DPRINT("!%#x", *p); microdelay(1000); } return (*p & mask) == v;}/* * Drive initialization */static intconfigdrive(Ctlr *ctlr, Drive *d, SDunit *unit){ int i; ulong *r; DPRINT("%s: configdrive\n", unit->name); d->unit = unit; d->ctlr = ctlr; d->chipx = unit->subno%4; d->chip = &ctlr->chip[unit->subno/4]; d->bridge = &d->chip->arb->bridge[d->chipx]; d->edma = &d->chip->edma[d->chipx]; if (d->driveno < 0) panic("mv50xx: configdrive: unset driveno\n"); sdunits[d->driveno] = unit; if(d->tx == nil){ d->tx = mallocalign(32*sizeof(Tx), 1024, 0, 0); d->rx = mallocalign(32*sizeof(Rx), 256, 0, 0); d->prd = mallocalign(32*sizeof(Prd), 32, 0, 0); if(d->tx == nil || d->rx == nil || d->prd == nil){ iprint("%s: out of memory allocating ring buffers\n", unit->name); free(d->tx); d->tx = nil; free(d->rx); d->rx = nil; free(d->prd); d->prd = nil; d->state = Dunconfig; return 0; } for(i=0; i<32; i++) d->tx[i].prdpa = PADDR(&d->prd[i]); coherence(); } /* leave disk interrupts turned off until we use it ... */ d->edma->iem = 0; /* ... but enable them on the controller */ r = (ulong*)(d->ctlr->mmio + 0x1D64); if(d->unit->subno < 4) *r |= 3 << (d->chipx*2); else *r |= 3 << (d->chipx*2+9); return 1;}static intenabledrive(Drive *d){ Edma *edma; DPRINT("%s: enabledrive\n", d->unit->name); if((d->bridge->status & 0xF) != 0x3){ /* Det */ DPRINT("%s: not present\n", d->unit->name); d->state = Dmissing; return 0; } edma = d->edma; if(satawait(&edma->cmdstat, ATAbusy, 0, 10*1000) == 0){ print("%s: busy timeout\n", d->unit->name); d->state = Dmissing; return 0; } edma->iec = 0; d->chip->arb->ic &= ~(0x101 << d->chipx); edma->config = 0x11F; edma->txi = PADDR(d->tx); edma->txo = (ulong)d->tx & 0x3E0; edma->rxi = (ulong)d->rx & 0xF8; edma->rxo = PADDR(d->rx); edma->ctl |= 1; /* enable dma */ DPRINT("%s: enable interrupts\n", d->unit->name); if(d->bridge->status = 0x113) d->state = Dnew; d->edma->iem = IEM; return 1;}static voiddisabledrive(Drive *d){ int i; ulong *r; DPRINT("%s: disabledrive\n", d->unit->name); if(d->tx == nil) /* never enabled */ return; d->edma->ctl = 0; d->edma->iem = 0; r = (ulong*)(d->ctlr->mmio + 0x1D64); i = d->chipx; if(d->chipx < 4) *r &= ~(3 << (i*2)); else *r |= ~(3 << (i*2+9));}static intsetudmamode(Drive *d, uchar mode){ Edma *edma; DPRINT("%s: setudmamode %d\n", d->unit->name, mode); edma = d->edma; if (edma == nil) { print("setudamode(m%d): zero d->edma\m", d->driveno); return 0; } if(satawait(&edma->cmdstat, ATAerr|ATAdrq|ATAdf|ATAdrdy|ATAbusy, ATAdrdy, 15*1000) == 0){ iprint("%s: cmdstat 0x%.2ux ready timeout\n", d->unit->name, edma->cmdstat); return 0; } edma->altstat = ATAeIEN; edma->err = 3; edma->seccnt = 0x40 | mode; edma->cmdstat = 0xEF; microdelay(1); if(satawait(&edma->cmdstat, ATAbusy, 0, 15*1000) == 0){ iprint("%s: cmdstat 0x%.2ux busy timeout\n", d->unit->name, edma->cmdstat); return 0; } return 1;}static voididentifydrive(Drive *d){ int i; ushort *id; Edma *edma; SDunit *unit; DPRINT("%s: identifydrive\n", d->unit->name); if(setudmamode(d, 5) == 0) /* do all SATA support 5? */ goto Error; id = d->info; memset(d->info, 0, sizeof d->info); edma = d->edma; if(satawait(&edma->cmdstat, 0xE9, 0x40, 15*1000) == 0) goto Error; edma->altstat = ATAeIEN; /* no interrupts */ edma->cmdstat = 0xEC; microdelay(1); if(satawait(&edma->cmdstat, ATAbusy, 0, 15*1000) == 0) goto Error; for(i=0; i<256; i++) id[i] = edma->pio; if(edma->cmdstat & (ATAerr|ATAdf)) goto Error; i = lhgets(id+83) | lhgets(id+86); if(i & (1<<10)){ d->flag |= Dext; d->sectors = lhgetv(id+100); }else{ d->flag &= ~Dext; d->sectors = lhgetl(id+60); } idmove(d->serial, id+10, 20); idmove(d->firmware, id+23, 8); idmove(d->model, id+27, 40); unit = d->unit; memset(unit->inquiry, 0, sizeof unit->inquiry); unit->inquiry[2] = 2; unit->inquiry[3] = 2; unit->inquiry[4] = sizeof(unit->inquiry)-4; idmove((char*)unit->inquiry+8, id+27, 40); if(enabledrive(d)) { d->state = Dready; print("mvsata: m%d: LLBA %lld sectors\n", d->driveno, (Wideoff)d->sectors); } else d->state = Derror; return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -