xref: /plan9/sys/src/9/pc/sdmv50xx.c (revision ea58ad6fbee60d5a3fca57ac646881779dd8f0ea)
14de34a7eSDavid du Colombier /*
257195852SDavid du Colombier  * Marvell 88SX[56]0[48][01] fileserver Serial ATA (SATA) driver
357195852SDavid du Colombier  *
457195852SDavid du Colombier  * See MV-S101357-00 Rev B Marvell PCI/PCI-X to 8-Port/4-Port
557195852SDavid du Colombier  * SATA Host Controller, ATA-5 ANSI NCITS 340-2000.
657195852SDavid du Colombier  *
757195852SDavid du Colombier  * This is a heavily-modified version (by Coraid) of a heavily-modified
857195852SDavid du Colombier  * version (from The Labs) of a driver written by Coraid, Inc.
94de34a7eSDavid du Colombier  * The original copyright notice appears at the end of this file.
104de34a7eSDavid du Colombier  */
114de34a7eSDavid du Colombier 
124de34a7eSDavid du Colombier #include "u.h"
134de34a7eSDavid du Colombier #include "../port/lib.h"
144de34a7eSDavid du Colombier #include "mem.h"
154de34a7eSDavid du Colombier #include "dat.h"
164de34a7eSDavid du Colombier #include "fns.h"
174de34a7eSDavid du Colombier #include "io.h"
184de34a7eSDavid du Colombier #include "../port/error.h"
194de34a7eSDavid du Colombier 
204de34a7eSDavid du Colombier #include "../port/sd.h"
214de34a7eSDavid du Colombier 
2257195852SDavid du Colombier #define dprint if(!0){}else iprint
2357195852SDavid du Colombier #define idprint if(!0){}else iprint
2457195852SDavid du Colombier #define ioprint if(!0){}else iprint
2557195852SDavid du Colombier 
2657195852SDavid du Colombier enum {
2757195852SDavid du Colombier 	NCtlr		= 4,
2857195852SDavid du Colombier 	NCtlrdrv	= 8,
2957195852SDavid du Colombier 	NDrive		= NCtlr*NCtlrdrv,
3057195852SDavid du Colombier 
3157195852SDavid du Colombier 	Read 		= 0,
3257195852SDavid du Colombier 	Write,
33*ea58ad6fSDavid du Colombier 
34*ea58ad6fSDavid du Colombier 	Coraiddebug	= 0,
3557195852SDavid du Colombier };
364de34a7eSDavid du Colombier 
374de34a7eSDavid du Colombier enum {
384de34a7eSDavid du Colombier 	SrbRing = 32,
394de34a7eSDavid du Colombier 
404de34a7eSDavid du Colombier 	/* Addresses of ATA register */
414de34a7eSDavid du Colombier 	ARcmd		= 027,
424de34a7eSDavid du Colombier 	ARdev		= 026,
434de34a7eSDavid du Colombier 	ARerr		= 021,
444de34a7eSDavid du Colombier 	ARfea		= 021,
454de34a7eSDavid du Colombier 	ARlba2		= 025,
464de34a7eSDavid du Colombier 	ARlba1		= 024,
474de34a7eSDavid du Colombier 	ARlba0		= 023,
484de34a7eSDavid du Colombier 	ARseccnt	= 022,
494de34a7eSDavid du Colombier 	ARstat		= 027,
504de34a7eSDavid du Colombier 
514de34a7eSDavid du Colombier 	ATAerr		= (1<<0),
524de34a7eSDavid du Colombier 	ATAdrq		= (1<<3),
534de34a7eSDavid du Colombier 	ATAdf 		= (1<<5),
544de34a7eSDavid du Colombier 	ATAdrdy 	= (1<<6),
554de34a7eSDavid du Colombier 	ATAbusy 	= (1<<7),
564de34a7eSDavid du Colombier 	ATAabort	= (1<<2),
5757195852SDavid du Colombier 	ATAobs		= (1<<1 | 1<<2 | 1<<4),
584de34a7eSDavid du Colombier 	ATAeIEN		= (1<<1),
594de34a7eSDavid du Colombier 	ATAsrst		= (1<<2),
604de34a7eSDavid du Colombier 	ATAhob		= (1<<7),
6157195852SDavid du Colombier 	ATAbad		= (ATAbusy|ATAdf|ATAdrq|ATAerr),
624de34a7eSDavid du Colombier 
634de34a7eSDavid du Colombier 	SFdone 		= (1<<0),
644de34a7eSDavid du Colombier 	SFerror 	= (1<<1),
654de34a7eSDavid du Colombier 
664de34a7eSDavid du Colombier 	SRBident 	= 0,
674de34a7eSDavid du Colombier 	SRBread,
684de34a7eSDavid du Colombier 	SRBwrite,
694de34a7eSDavid du Colombier 	SRBsmart,
704de34a7eSDavid du Colombier 
714de34a7eSDavid du Colombier 	SRBnodata = 0,
724de34a7eSDavid du Colombier 	SRBdatain,
734de34a7eSDavid du Colombier 	SRBdataout,
744de34a7eSDavid du Colombier 
754de34a7eSDavid du Colombier 	RQread		= 1,		/* data coming IN from device */
764de34a7eSDavid du Colombier 
774de34a7eSDavid du Colombier 	PRDeot		= (1<<15),
784de34a7eSDavid du Colombier 
794de34a7eSDavid du Colombier 	/* EDMA interrupt error cause register */
804de34a7eSDavid du Colombier 
814de34a7eSDavid du Colombier 	ePrtDataErr	= (1<<0),
824de34a7eSDavid du Colombier 	ePrtPRDErr	= (1<<1),
834de34a7eSDavid du Colombier 	eDevErr		= (1<<2),
844de34a7eSDavid du Colombier 	eDevDis		= (1<<3),
854de34a7eSDavid du Colombier 	eDevCon		= (1<<4),
864de34a7eSDavid du Colombier 	eOverrun	= (1<<5),
874de34a7eSDavid du Colombier 	eUnderrun	= (1<<6),
884de34a7eSDavid du Colombier 	eSelfDis	= (1<<8),
894de34a7eSDavid du Colombier 	ePrtCRQBErr	= (1<<9),
904de34a7eSDavid du Colombier 	ePrtCRPBErr	= (1<<10),
914de34a7eSDavid du Colombier 	ePrtIntErr	= (1<<11),
924de34a7eSDavid du Colombier 	eIORdyErr	= (1<<12),
934de34a7eSDavid du Colombier 
9441dd6b47SDavid du Colombier 	/* flags for sata 2 version */
9557195852SDavid du Colombier 	eSelfDis2	= (1<<7),
9657195852SDavid du Colombier 	SerrInt		= (1<<5),
9757195852SDavid du Colombier 
984de34a7eSDavid du Colombier 	/* EDMA Command Register */
994de34a7eSDavid du Colombier 
1004de34a7eSDavid du Colombier 	eEnEDMA	= (1<<0),
1014de34a7eSDavid du Colombier 	eDsEDMA 	= (1<<1),
1024de34a7eSDavid du Colombier 	eAtaRst 	= (1<<2),
1034de34a7eSDavid du Colombier 
1044de34a7eSDavid du Colombier 	/* Interrupt mask for errors we care about */
1054de34a7eSDavid du Colombier 	IEM		= (eDevDis | eDevCon | eSelfDis),
10657195852SDavid du Colombier 	IEM2		= (eDevDis | eDevCon | eSelfDis2),
1074de34a7eSDavid du Colombier 
10857195852SDavid du Colombier 	/* drive states */
1094de34a7eSDavid du Colombier 	Dnull 		= 0,
1104de34a7eSDavid du Colombier 	Dnew,
1114de34a7eSDavid du Colombier 	Dready,
1124de34a7eSDavid du Colombier 	Derror,
1134de34a7eSDavid du Colombier 	Dmissing,
11457195852SDavid du Colombier 	Dreset,
11557195852SDavid du Colombier 	Dlast,
1164de34a7eSDavid du Colombier 
11757195852SDavid du Colombier 	/* drive flags */
1184de34a7eSDavid du Colombier 	Dext	 	= (1<<0),	/* use ext commands */
1194de34a7eSDavid du Colombier 	Dpio		= (1<<1),	/* doing pio */
1204de34a7eSDavid du Colombier 	Dwanted		= (1<<2),	/* someone wants an srb entry */
1214de34a7eSDavid du Colombier 	Dedma		= (1<<3),	/* device in edma mode */
1224de34a7eSDavid du Colombier 	Dpiowant	= (1<<4),	/* some wants to use the pio mode */
12357195852SDavid du Colombier 
12441dd6b47SDavid du Colombier 	/* phyerrata magic crap */
12557195852SDavid du Colombier 	Mpreamp	= 0x7e0,
12657195852SDavid du Colombier 	Dpreamp	= 0x720,
12757195852SDavid du Colombier 
12857195852SDavid du Colombier 	REV60X1B2	= 0x7,
12957195852SDavid du Colombier 	REV60X1C0	= 0x9,
13057195852SDavid du Colombier 
1314de34a7eSDavid du Colombier };
1324de34a7eSDavid du Colombier 
13357195852SDavid du Colombier static char* diskstates[Dlast] = {
1344de34a7eSDavid du Colombier 	"null",
1354de34a7eSDavid du Colombier 	"new",
1364de34a7eSDavid du Colombier 	"ready",
1374de34a7eSDavid du Colombier 	"error",
1384de34a7eSDavid du Colombier 	"missing",
13957195852SDavid du Colombier 	"reset",
1404de34a7eSDavid du Colombier };
1414de34a7eSDavid du Colombier 
1424de34a7eSDavid du Colombier extern SDifc sdmv50xxifc;
1434de34a7eSDavid du Colombier 
1444de34a7eSDavid du Colombier typedef struct Arb Arb;
1454de34a7eSDavid du Colombier typedef struct Bridge Bridge;
1464de34a7eSDavid du Colombier typedef struct Chip Chip;
1474de34a7eSDavid du Colombier typedef struct Ctlr Ctlr;
1484de34a7eSDavid du Colombier typedef struct Drive Drive;
1494de34a7eSDavid du Colombier typedef struct Edma Edma;
1504de34a7eSDavid du Colombier typedef struct Prd Prd;
1514de34a7eSDavid du Colombier typedef struct Rx Rx;
1524de34a7eSDavid du Colombier typedef struct Srb Srb;
1534de34a7eSDavid du Colombier typedef struct Tx Tx;
1544de34a7eSDavid du Colombier 
15541dd6b47SDavid du Colombier /*
15641dd6b47SDavid du Colombier  * there are 4 drives per chip.  thus an 8-port
15741dd6b47SDavid du Colombier  * card has two chips.
15841dd6b47SDavid du Colombier  */
15957195852SDavid du Colombier struct Chip
1604de34a7eSDavid du Colombier {
1614de34a7eSDavid du Colombier 	Arb	*arb;
16257195852SDavid du Colombier 	Edma	*edma;
1634de34a7eSDavid du Colombier };
1644de34a7eSDavid du Colombier 
16557195852SDavid du Colombier enum {
16657195852SDavid du Colombier 	DMautoneg,
16757195852SDavid du Colombier 	DMsatai,
16857195852SDavid du Colombier 	DMsataii,
16957195852SDavid du Colombier };
17057195852SDavid du Colombier 
17157195852SDavid du Colombier struct Drive
1724de34a7eSDavid du Colombier {
1734de34a7eSDavid du Colombier 	Lock;
1744de34a7eSDavid du Colombier 
1754de34a7eSDavid du Colombier 	Ctlr	*ctlr;
1764de34a7eSDavid du Colombier 	SDunit	*unit;
1774de34a7eSDavid du Colombier 	char	name[10];
17857195852SDavid du Colombier 	ulong	magic;
1794de34a7eSDavid du Colombier 
1804de34a7eSDavid du Colombier 	Bridge	*bridge;
1814de34a7eSDavid du Colombier 	Edma	*edma;
1824de34a7eSDavid du Colombier 	Chip	*chip;
1834de34a7eSDavid du Colombier 	int	chipx;
1844de34a7eSDavid du Colombier 
18557195852SDavid du Colombier 	int	mediachange;
1864de34a7eSDavid du Colombier 	int	state;
1874de34a7eSDavid du Colombier 	int	flag;
1884de34a7eSDavid du Colombier 	uvlong	sectors;
18941dd6b47SDavid du Colombier 	ulong	pm2;		/* phymode 2 init state */
19041dd6b47SDavid du Colombier 	ulong	intick;		/* check for hung western digital drives. */
19157195852SDavid du Colombier 	int	wait;
19241dd6b47SDavid du Colombier 	int	mode;		/* DMautoneg, satai or sataii. */
1934de34a7eSDavid du Colombier 
1944de34a7eSDavid du Colombier 	char	serial[20+1];
1954de34a7eSDavid du Colombier 	char	firmware[8+1];
1964de34a7eSDavid du Colombier 	char	model[40+1];
1974de34a7eSDavid du Colombier 
1984de34a7eSDavid du Colombier 	ushort	info[256];
1994de34a7eSDavid du Colombier 
2004de34a7eSDavid du Colombier 	Srb	*srb[SrbRing-1];
2014de34a7eSDavid du Colombier 	int	nsrb;
2024de34a7eSDavid du Colombier 	Prd	*prd;
2034de34a7eSDavid du Colombier 	Tx	*tx;
2044de34a7eSDavid du Colombier 	Rx	*rx;
2054de34a7eSDavid du Colombier 
2064de34a7eSDavid du Colombier 	Srb	*srbhead;
2074de34a7eSDavid du Colombier 	Srb	*srbtail;
20841dd6b47SDavid du Colombier 	int	driveno;	/* ctlr*NCtlrdrv + unit */
2094de34a7eSDavid du Colombier };
2104de34a7eSDavid du Colombier 
21157195852SDavid du Colombier struct Ctlr
2124de34a7eSDavid du Colombier {
2134de34a7eSDavid du Colombier 	Lock;
2144de34a7eSDavid du Colombier 
2154de34a7eSDavid du Colombier 	int	irq;
2164de34a7eSDavid du Colombier 	int	tbdf;
21757195852SDavid du Colombier 	int	rid;
21857195852SDavid du Colombier 	ulong	magic;
21957195852SDavid du Colombier 	int	enabled;
22057195852SDavid du Colombier 	int	type;
2214de34a7eSDavid du Colombier 	SDev	*sdev;
2224de34a7eSDavid du Colombier 	Pcidev	*pcidev;
2234de34a7eSDavid du Colombier 
2244de34a7eSDavid du Colombier 	uchar	*mmio;
22557195852SDavid du Colombier 	ulong	*lmmio;
2264de34a7eSDavid du Colombier 	Chip	chip[2];
2274de34a7eSDavid du Colombier 	int	nchip;
22857195852SDavid du Colombier 	Drive	drive[NCtlrdrv];
2294de34a7eSDavid du Colombier 	int	ndrive;
2304de34a7eSDavid du Colombier };
2314de34a7eSDavid du Colombier 
2324de34a7eSDavid du Colombier struct Srb			/* request buffer */
2334de34a7eSDavid du Colombier {
2344de34a7eSDavid du Colombier 	Lock;
2354de34a7eSDavid du Colombier 	Rendez;
2364de34a7eSDavid du Colombier 	Srb	*next;
2374de34a7eSDavid du Colombier 
2384de34a7eSDavid du Colombier 	Drive	*drive;
2394de34a7eSDavid du Colombier 	uvlong	blockno;
2404de34a7eSDavid du Colombier 	int	count;
2414de34a7eSDavid du Colombier 	int	req;
2424de34a7eSDavid du Colombier 	int	flag;
2434de34a7eSDavid du Colombier 	uchar	*data;
2444de34a7eSDavid du Colombier 
2454de34a7eSDavid du Colombier 	uchar	cmd;
2464de34a7eSDavid du Colombier 	uchar	lba[6];
2474de34a7eSDavid du Colombier 	uchar	sectors;
2484de34a7eSDavid du Colombier 	int	sta;
2494de34a7eSDavid du Colombier 	int	err;
2504de34a7eSDavid du Colombier };
2514de34a7eSDavid du Colombier 
2524de34a7eSDavid du Colombier /*
2534de34a7eSDavid du Colombier  * Memory-mapped I/O registers in many forms.
2544de34a7eSDavid du Colombier  */
2554de34a7eSDavid du Colombier struct Bridge			/* memory-mapped per-Drive registers */
2564de34a7eSDavid du Colombier {
2574de34a7eSDavid du Colombier 	ulong	status;
2584de34a7eSDavid du Colombier 	ulong	serror;
2594de34a7eSDavid du Colombier 	ulong	sctrl;
2604de34a7eSDavid du Colombier 	ulong	phyctrl;
26157195852SDavid du Colombier 	ulong	phymode3;
26257195852SDavid du Colombier 	ulong	phymode4;
26357195852SDavid du Colombier 	uchar	fill0[0x14];
26457195852SDavid du Colombier 	ulong	phymode1;
26557195852SDavid du Colombier 	ulong	phymode2;
26657195852SDavid du Colombier 	char	fill1[8];
2674de34a7eSDavid du Colombier 	ulong	ctrl;
2684de34a7eSDavid du Colombier 	char	fill2[0x34];
2694de34a7eSDavid du Colombier 	ulong	phymode;
27057195852SDavid du Colombier 	char	fill3[0x88];
27141dd6b47SDavid du Colombier };				/* length must be 0x100 */
2724de34a7eSDavid du Colombier 
2734de34a7eSDavid du Colombier struct Arb			/* memory-mapped per-Chip registers */
2744de34a7eSDavid du Colombier {
27557195852SDavid du Colombier 	ulong	config;		/* satahc configuration register (sata2 only) */
2764de34a7eSDavid du Colombier 	ulong	rqop;		/* request queue out-pointer */
2774de34a7eSDavid du Colombier 	ulong	rqip;		/* response queue in pointer */
2784de34a7eSDavid du Colombier 	ulong	ict;		/* inerrupt caolescing threshold */
2794de34a7eSDavid du Colombier 	ulong	itt;		/* interrupt timer threshold */
2804de34a7eSDavid du Colombier 	ulong	ic;		/* interrupt cause */
2814de34a7eSDavid du Colombier 	ulong	btc;		/* bridges test control */
2824de34a7eSDavid du Colombier 	ulong	bts;		/* bridges test status */
2834de34a7eSDavid du Colombier 	ulong	bpc;		/* bridges pin configuration */
2844de34a7eSDavid du Colombier 	char	fill1[0xdc];
2854de34a7eSDavid du Colombier 	Bridge	bridge[4];
2864de34a7eSDavid du Colombier };
2874de34a7eSDavid du Colombier 
2884de34a7eSDavid du Colombier struct Edma			/* memory-mapped per-Drive DMA-related registers */
2894de34a7eSDavid du Colombier {
2904de34a7eSDavid du Colombier 	ulong	config;		/* configuration register */
2914de34a7eSDavid du Colombier 	ulong	timer;
2924de34a7eSDavid du Colombier 	ulong	iec;		/* interrupt error cause */
2934de34a7eSDavid du Colombier 	ulong	iem;		/* interrupt error mask */
2944de34a7eSDavid du Colombier 
2954de34a7eSDavid du Colombier 	ulong	txbasehi;		/* request queue base address high */
2964de34a7eSDavid du Colombier 	ulong	txi;		/* request queue in pointer */
2974de34a7eSDavid du Colombier 	ulong	txo;		/* request queue out pointer */
2984de34a7eSDavid du Colombier 
2994de34a7eSDavid du Colombier 	ulong	rxbasehi;		/* response queue base address high */
3004de34a7eSDavid du Colombier 	ulong	rxi;		/* response queue in pointer */
3014de34a7eSDavid du Colombier 	ulong	rxo;		/* response queue out pointer */
3024de34a7eSDavid du Colombier 
3034de34a7eSDavid du Colombier 	ulong	ctl;		/* command register */
3044de34a7eSDavid du Colombier 	ulong	testctl;		/* test control */
3054de34a7eSDavid du Colombier 	ulong	status;
3064de34a7eSDavid du Colombier 	ulong	iordyto;		/* IORDY timeout */
30757195852SDavid du Colombier 	char	fill[0x18];
30857195852SDavid du Colombier 	ulong	sataconfig;	/* sata 2 */
30957195852SDavid du Colombier 	char	fill[0xac];
3104de34a7eSDavid du Colombier 	ushort	pio;		/* data register */
3114de34a7eSDavid du Colombier 	char	pad0[2];
3124de34a7eSDavid du Colombier 	uchar	err;		/* features and error */
3134de34a7eSDavid du Colombier 	char	pad1[3];
3144de34a7eSDavid du Colombier 	uchar	seccnt;		/* sector count */
3154de34a7eSDavid du Colombier 	char	pad2[3];
3164de34a7eSDavid du Colombier 	uchar	lba0;
3174de34a7eSDavid du Colombier 	char	pad3[3];
3184de34a7eSDavid du Colombier 	uchar	lba1;
3194de34a7eSDavid du Colombier 	char	pad4[3];
3204de34a7eSDavid du Colombier 	uchar	lba2;
3214de34a7eSDavid du Colombier 	char	pad5[3];
3224de34a7eSDavid du Colombier 	uchar	lba3;
3234de34a7eSDavid du Colombier 	char	pad6[3];
3244de34a7eSDavid du Colombier 	uchar	cmdstat;		/* cmd/status */
3254de34a7eSDavid du Colombier 	char	pad7[3];
3264de34a7eSDavid du Colombier 	uchar	altstat;		/* alternate status */
32757195852SDavid du Colombier 	uchar	fill2[0x1df];
32857195852SDavid du Colombier 	Bridge	port;
32957195852SDavid du Colombier 	char	fill3[0x1c00];	/* pad to 0x2000 bytes */
3304de34a7eSDavid du Colombier };
3314de34a7eSDavid du Colombier 
3324de34a7eSDavid du Colombier /*
3334de34a7eSDavid du Colombier  * Memory structures shared with card.
3344de34a7eSDavid du Colombier  */
3354de34a7eSDavid du Colombier struct Prd			/* physical region descriptor */
3364de34a7eSDavid du Colombier {
3374de34a7eSDavid du Colombier 	ulong	pa;		/* byte address of physical memory */
3384de34a7eSDavid du Colombier 	ushort	count;		/* byte count (bit0 must be 0) */
3394de34a7eSDavid du Colombier 	ushort	flag;
3404de34a7eSDavid du Colombier 	ulong	zero;		/* high long of 64 bit address */
3414de34a7eSDavid du Colombier 	ulong	reserved;
3424de34a7eSDavid du Colombier };
3434de34a7eSDavid du Colombier 
3444de34a7eSDavid du Colombier struct Tx				/* command request block */
3454de34a7eSDavid du Colombier {
3464de34a7eSDavid du Colombier 	ulong	prdpa;		/* physical region descriptor table structures */
3474de34a7eSDavid du Colombier 	ulong	zero;		/* must be zero (high long of prd address) */
3484de34a7eSDavid du Colombier 	ushort	flag;		/* control flags */
3494de34a7eSDavid du Colombier 	ushort	regs[11];
3504de34a7eSDavid du Colombier };
3514de34a7eSDavid du Colombier 
3524de34a7eSDavid du Colombier struct Rx				/* command response block */
3534de34a7eSDavid du Colombier {
3544de34a7eSDavid du Colombier 	ushort	cid;		/* cID of response */
3554de34a7eSDavid du Colombier 	uchar	cEdmaSts;	/* EDMA status */
3564de34a7eSDavid du Colombier 	uchar	cDevSts;		/* status from disk */
3574de34a7eSDavid du Colombier 	ulong	ts;		/* time stamp */
3584de34a7eSDavid du Colombier };
3594de34a7eSDavid du Colombier 
36057195852SDavid du Colombier static Drive 	*mvsatadrive[NDrive];
36157195852SDavid du Colombier static int	nmvsatadrive;
36257195852SDavid du Colombier 
3634de34a7eSDavid du Colombier /*
3644de34a7eSDavid du Colombier  * Little-endian parsing for drive data.
3654de34a7eSDavid du Colombier  */
3664de34a7eSDavid du Colombier static ushort
lhgets(void * p)3674de34a7eSDavid du Colombier lhgets(void *p)
3684de34a7eSDavid du Colombier {
3694de34a7eSDavid du Colombier 	uchar *a = p;
3704de34a7eSDavid du Colombier 	return ((ushort) a[1] << 8) | a[0];
3714de34a7eSDavid du Colombier }
3724de34a7eSDavid du Colombier 
3734de34a7eSDavid du Colombier static ulong
lhgetl(void * p)3744de34a7eSDavid du Colombier lhgetl(void *p)
3754de34a7eSDavid du Colombier {
3764de34a7eSDavid du Colombier 	uchar *a = p;
3774de34a7eSDavid du Colombier 	return ((ulong) lhgets(a+2) << 16) | lhgets(a);
3784de34a7eSDavid du Colombier }
3794de34a7eSDavid du Colombier 
3804de34a7eSDavid du Colombier static uvlong
lhgetv(void * p)3814de34a7eSDavid du Colombier lhgetv(void *p)
3824de34a7eSDavid du Colombier {
3834de34a7eSDavid du Colombier 	uchar *a = p;
3844de34a7eSDavid du Colombier 	return ((uvlong) lhgetl(a+4) << 32) | lhgetl(a);
3854de34a7eSDavid du Colombier }
3864de34a7eSDavid du Colombier 
3874de34a7eSDavid du Colombier static void
idmove(char * p,ushort * a,int n)3884de34a7eSDavid du Colombier idmove(char *p, ushort *a, int n)
3894de34a7eSDavid du Colombier {
3904de34a7eSDavid du Colombier 	char *op;
3914de34a7eSDavid du Colombier 	int i;
3924de34a7eSDavid du Colombier 
3934de34a7eSDavid du Colombier 	op = p;
3944de34a7eSDavid du Colombier 	for(i=0; i<n/2; i++){
3954de34a7eSDavid du Colombier 		*p++ = a[i]>>8;
3964de34a7eSDavid du Colombier 		*p++ = a[i];
3974de34a7eSDavid du Colombier 	}
3984de34a7eSDavid du Colombier 	while(p>op && *--p == ' ')
3994de34a7eSDavid du Colombier 		*p = 0;
4004de34a7eSDavid du Colombier }
4014de34a7eSDavid du Colombier 
4024de34a7eSDavid du Colombier /*
4034de34a7eSDavid du Colombier  * Request buffers.
4044de34a7eSDavid du Colombier  */
4054de34a7eSDavid du Colombier struct
4064de34a7eSDavid du Colombier {
4074de34a7eSDavid du Colombier 	Lock;
4084de34a7eSDavid du Colombier 	Srb *freechain;
4094de34a7eSDavid du Colombier 	int nalloc;
4104de34a7eSDavid du Colombier } srblist;
4114de34a7eSDavid du Colombier 
4124de34a7eSDavid du Colombier static Srb*
allocsrb(void)4134de34a7eSDavid du Colombier allocsrb(void)
4144de34a7eSDavid du Colombier {
4154de34a7eSDavid du Colombier 	Srb *p;
4164de34a7eSDavid du Colombier 
4174de34a7eSDavid du Colombier 	ilock(&srblist);
4184de34a7eSDavid du Colombier 	if((p = srblist.freechain) == nil){
4194de34a7eSDavid du Colombier 		srblist.nalloc++;
4204de34a7eSDavid du Colombier 		iunlock(&srblist);
4214de34a7eSDavid du Colombier 		p = smalloc(sizeof *p);
4224de34a7eSDavid du Colombier 	}else{
4234de34a7eSDavid du Colombier 		srblist.freechain = p->next;
4244de34a7eSDavid du Colombier 		iunlock(&srblist);
4254de34a7eSDavid du Colombier 	}
4264de34a7eSDavid du Colombier 	return p;
4274de34a7eSDavid du Colombier }
4284de34a7eSDavid du Colombier 
4294de34a7eSDavid du Colombier static void
freesrb(Srb * p)4304de34a7eSDavid du Colombier freesrb(Srb *p)
4314de34a7eSDavid du Colombier {
4324de34a7eSDavid du Colombier 	ilock(&srblist);
4334de34a7eSDavid du Colombier 	p->next = srblist.freechain;
4344de34a7eSDavid du Colombier 	srblist.freechain = p;
4354de34a7eSDavid du Colombier 	iunlock(&srblist);
4364de34a7eSDavid du Colombier }
4374de34a7eSDavid du Colombier 
4384de34a7eSDavid du Colombier /*
4394de34a7eSDavid du Colombier  * Wait for a byte to be a particular value.
4404de34a7eSDavid du Colombier  */
4414de34a7eSDavid du Colombier static int
satawait(uchar * p,uchar mask,uchar v,int ms)4424de34a7eSDavid du Colombier satawait(uchar *p, uchar mask, uchar v, int ms)
4434de34a7eSDavid du Colombier {
4444de34a7eSDavid du Colombier 	int i;
4454de34a7eSDavid du Colombier 
44657195852SDavid du Colombier 	for(i=0; i<ms && (*p & mask) != v; i++)
4474de34a7eSDavid du Colombier 		microdelay(1000);
4484de34a7eSDavid du Colombier 	return (*p & mask) == v;
4494de34a7eSDavid du Colombier }
4504de34a7eSDavid du Colombier 
4514de34a7eSDavid du Colombier /*
4524de34a7eSDavid du Colombier  * Drive initialization
4534de34a7eSDavid du Colombier  */
45441dd6b47SDavid du Colombier /* unmask in the pci registers err done */
45557195852SDavid du Colombier static void
unmask(ulong * mmio,int port,int coal)45657195852SDavid du Colombier unmask(ulong *mmio, int port, int coal)
45757195852SDavid du Colombier {
45857195852SDavid du Colombier 	port &= 7;
45957195852SDavid du Colombier 	if(coal)
46057195852SDavid du Colombier 		coal = 1;
46157195852SDavid du Colombier 	if (port < 4)
46257195852SDavid du Colombier 		mmio[0x1d64/4] |= (3 << (((port&3)*2)) | (coal<<8));
46357195852SDavid du Colombier 	else
46457195852SDavid du Colombier 		mmio[0x1d64/4] |= (3 << (((port&3)*2+9)) | (coal<<17));
46557195852SDavid du Colombier }
46657195852SDavid du Colombier 
46757195852SDavid du Colombier static void
mask(ulong * mmio,int port,int coal)46857195852SDavid du Colombier mask(ulong *mmio, int port, int coal)
46957195852SDavid du Colombier {
47057195852SDavid du Colombier 	port &= 7;
47157195852SDavid du Colombier 	if(coal)
47257195852SDavid du Colombier 		coal = 1;
47357195852SDavid du Colombier 	if (port < 4)
47457195852SDavid du Colombier 		mmio[0x1d64/4] &= ~(3 << (((port&3)*2)) | (coal<<8));
47557195852SDavid du Colombier 	else
47657195852SDavid du Colombier 		mmio[0x1d64/4] &= ~(3 << (((port&3)*2+9)) | (coal<<17));
47757195852SDavid du Colombier }
47857195852SDavid du Colombier 
47957195852SDavid du Colombier /* I give up, marvell.  You win. */
48057195852SDavid du Colombier static void
phyerrata(Drive * d)48157195852SDavid du Colombier phyerrata(Drive *d)
48257195852SDavid du Colombier {
48357195852SDavid du Colombier 	ulong n, m;
48457195852SDavid du Colombier 	enum { BadAutoCal = 0xf << 26, };
48557195852SDavid du Colombier 
48657195852SDavid du Colombier 	if (d->ctlr->type == 1)
48757195852SDavid du Colombier 		return;
48857195852SDavid du Colombier 	microdelay(200);
48957195852SDavid du Colombier 	n = d->bridge->phymode2;
49057195852SDavid du Colombier 	while ((n & BadAutoCal) == BadAutoCal) {
49157195852SDavid du Colombier 		dprint("%s: badautocal\n", d->unit->name);
49257195852SDavid du Colombier 		n &= ~(1<<16);
49357195852SDavid du Colombier 		n |= (1<<31);
49457195852SDavid du Colombier 		d->bridge->phymode2 = n;
49557195852SDavid du Colombier 		microdelay(200);
49657195852SDavid du Colombier 		d->bridge->phymode2 &= ~((1<<16) | (1<<31));
49757195852SDavid du Colombier 		microdelay(200);
49857195852SDavid du Colombier 		n = d->bridge->phymode2;
49957195852SDavid du Colombier 	}
50057195852SDavid du Colombier 	n &= ~(1<<31);
50157195852SDavid du Colombier 	d->bridge->phymode2 = n;
50257195852SDavid du Colombier 	microdelay(200);
50357195852SDavid du Colombier 
50457195852SDavid du Colombier 	/* abra cadabra!  (random magic) */
50557195852SDavid du Colombier 	m = d->bridge->phymode3;
50657195852SDavid du Colombier 	m &= ~0x7f800000;
50757195852SDavid du Colombier 	m |= 0x2a800000;
50857195852SDavid du Colombier 	d->bridge->phymode3 = m;
50957195852SDavid du Colombier 
51057195852SDavid du Colombier 	/* fix phy mode 4 */
51157195852SDavid du Colombier 	m = d->bridge->phymode3;
51257195852SDavid du Colombier 	n = d->bridge->phymode4;
51357195852SDavid du Colombier 	n &= ~(1<<1);
51457195852SDavid du Colombier 	n |= 1;
51557195852SDavid du Colombier 	switch(d->ctlr->rid){
51657195852SDavid du Colombier 	case REV60X1B2:
51757195852SDavid du Colombier 	default:
51857195852SDavid du Colombier 		d->bridge->phymode4 = n;
51957195852SDavid du Colombier 		d->bridge->phymode3 = m;
52057195852SDavid du Colombier 		break;
52157195852SDavid du Colombier 	case REV60X1C0:
52257195852SDavid du Colombier 		d->bridge->phymode4 = n;
52357195852SDavid du Colombier 		break;
52457195852SDavid du Colombier 	}
52557195852SDavid du Colombier 
52657195852SDavid du Colombier 	/* revert values of pre-emphasis and signal amps to the saved ones */
52757195852SDavid du Colombier 	n = d->bridge->phymode2;
52857195852SDavid du Colombier 	n &= ~Mpreamp;
52957195852SDavid du Colombier 	n |= d->pm2;
53057195852SDavid du Colombier 	n &= ~(1<<16);
53157195852SDavid du Colombier 	d->bridge->phymode2 = n;
53257195852SDavid du Colombier }
53357195852SDavid du Colombier 
53457195852SDavid du Colombier static void
edmacleanout(Drive * d)53557195852SDavid du Colombier edmacleanout(Drive *d)
5364de34a7eSDavid du Colombier {
5374de34a7eSDavid du Colombier 	int i;
53857195852SDavid du Colombier 	Srb *srb;
5394de34a7eSDavid du Colombier 
54057195852SDavid du Colombier 	for(i=0; i<nelem(d->srb); i++){
54157195852SDavid du Colombier 		if(srb = d->srb[i]){
54257195852SDavid du Colombier 			d->srb[i] = nil;
54357195852SDavid du Colombier 			d->nsrb--;
54457195852SDavid du Colombier 			srb->flag |= SFerror|SFdone;
54557195852SDavid du Colombier 			wakeup(srb);
5464de34a7eSDavid du Colombier 		}
54757195852SDavid du Colombier 	}
54857195852SDavid du Colombier 	while(srb = d->srbhead){
54957195852SDavid du Colombier 		d->srbhead = srb->next;
55057195852SDavid du Colombier 		srb->flag |= SFerror|SFdone;
55157195852SDavid du Colombier 		wakeup(srb);
55257195852SDavid du Colombier 	}
55357195852SDavid du Colombier }
55457195852SDavid du Colombier 
55557195852SDavid du Colombier static void
resetdisk(Drive * d)55657195852SDavid du Colombier resetdisk(Drive *d)
55757195852SDavid du Colombier {
55857195852SDavid du Colombier 	ulong n;
55957195852SDavid du Colombier 
56057195852SDavid du Colombier 	d->sectors = 0;
56157195852SDavid du Colombier 	d->unit->sectors = 0;
56257195852SDavid du Colombier 	if (d->ctlr->type == 2) {
56341dd6b47SDavid du Colombier 		/*
56441dd6b47SDavid du Colombier 		 * without bit 8 we can boot without disks, but
56541dd6b47SDavid du Colombier 		 * inserted disks will never appear.  :-X
56641dd6b47SDavid du Colombier 		 */
56757195852SDavid du Colombier 		n = d->edma->sataconfig;
56857195852SDavid du Colombier 		n &= 0xff;
56957195852SDavid du Colombier 		n |= 0x9b1100;
57057195852SDavid du Colombier 		d->edma->sataconfig = n;
57141dd6b47SDavid du Colombier 		n = d->edma->sataconfig;	/* flush */
57257195852SDavid du Colombier 		USED(n);
57357195852SDavid du Colombier 	}
57457195852SDavid du Colombier 	d->edma->ctl = eDsEDMA;
57557195852SDavid du Colombier 	microdelay(1);
57657195852SDavid du Colombier 	d->edma->ctl = eAtaRst;
57757195852SDavid du Colombier 	microdelay(25);
57857195852SDavid du Colombier 	d->edma->ctl = 0;
57957195852SDavid du Colombier 	if (satawait((uchar *)&d->edma->ctl, eEnEDMA, 0, 3*1000) == 0)
58057195852SDavid du Colombier 		print("%s: eEnEDMA never cleared on reset\n", d->unit->name);
58157195852SDavid du Colombier 	edmacleanout(d);
58257195852SDavid du Colombier 	phyerrata(d);
58357195852SDavid du Colombier 	d->bridge->sctrl = 0x301 | (d->mode << 4);
58457195852SDavid du Colombier 	d->state = Dmissing;
58557195852SDavid du Colombier }
58657195852SDavid du Colombier 
58757195852SDavid du Colombier static void
edmainit(Drive * d)58857195852SDavid du Colombier edmainit(Drive *d)
58957195852SDavid du Colombier {
59057195852SDavid du Colombier 	int i;
59157195852SDavid du Colombier 
59257195852SDavid du Colombier 	if(d->tx != nil)
59357195852SDavid du Colombier 		return;
59457195852SDavid du Colombier 
59557195852SDavid du Colombier 	d->tx = xspanalloc(32*sizeof(Tx), 1024, 0);
59657195852SDavid du Colombier 	d->rx = xspanalloc(32*sizeof(Rx), 256, 0);
59757195852SDavid du Colombier 	d->prd = xspanalloc(32*sizeof(Prd), 32, 0);
5984de34a7eSDavid du Colombier 	for(i = 0; i < 32; i++)
5994de34a7eSDavid du Colombier 		d->tx[i].prdpa = PADDR(&d->prd[i]);
6004de34a7eSDavid du Colombier 	coherence();
6014de34a7eSDavid du Colombier }
6024de34a7eSDavid du Colombier 
60357195852SDavid du Colombier static int
configdrive(Ctlr * ctlr,Drive * d,SDunit * unit)60457195852SDavid du Colombier configdrive(Ctlr *ctlr, Drive *d, SDunit *unit)
60557195852SDavid du Colombier {
60657195852SDavid du Colombier 	dprint("%s: configdrive\n", unit->name);
60757195852SDavid du Colombier 	if(d->driveno < 0)
60857195852SDavid du Colombier 		panic("mv50xx: configdrive: unset driveno\n");
60957195852SDavid du Colombier 	d->unit = unit;
61057195852SDavid du Colombier 	edmainit(d);
61157195852SDavid du Colombier 	d->mode = DMsatai;
61257195852SDavid du Colombier 	if(d->ctlr->type == 1){
61357195852SDavid du Colombier 		d->edma->iem = IEM;
61457195852SDavid du Colombier 		d->bridge = &d->chip->arb->bridge[d->chipx];
61557195852SDavid du Colombier 	}else{
61657195852SDavid du Colombier 		d->edma->iem = IEM2;
61757195852SDavid du Colombier 		d->bridge = &d->chip->edma[d->chipx].port;
61857195852SDavid du Colombier 		d->edma->iem = ~(1<<6);
61957195852SDavid du Colombier 		d->pm2 = Dpreamp;
62057195852SDavid du Colombier 		if(d->ctlr->lmmio[0x180d8/4] & 1)
62157195852SDavid du Colombier 			d->pm2 = d->bridge->phymode2 & Mpreamp;
62257195852SDavid du Colombier 	}
62357195852SDavid du Colombier 	resetdisk(d);
62457195852SDavid du Colombier 	unmask(ctlr->lmmio, d->driveno, 0);
62557195852SDavid du Colombier 	delay(100);
62657195852SDavid du Colombier 	if(d->bridge->status){
62757195852SDavid du Colombier 		dprint("%s: configdrive: found drive %lx\n", unit->name, d->bridge->status);
62857195852SDavid du Colombier 		return 0;
62957195852SDavid du Colombier 	}
63057195852SDavid du Colombier 	return -1;
6314de34a7eSDavid du Colombier }
6324de34a7eSDavid du Colombier 
6334de34a7eSDavid du Colombier static int
enabledrive(Drive * d)6344de34a7eSDavid du Colombier enabledrive(Drive *d)
6354de34a7eSDavid du Colombier {
6364de34a7eSDavid du Colombier 	Edma *edma;
6374de34a7eSDavid du Colombier 
63857195852SDavid du Colombier 	dprint("%s: enabledrive..", d->unit->name);
6394de34a7eSDavid du Colombier 
64057195852SDavid du Colombier 	if((d->bridge->status & 0xf) != 3){
64157195852SDavid du Colombier 		dprint("%s: not present\n", d->unit->name);
6424de34a7eSDavid du Colombier 		d->state = Dmissing;
64357195852SDavid du Colombier 		return -1;
6444de34a7eSDavid du Colombier 	}
6454de34a7eSDavid du Colombier 	edma = d->edma;
64657195852SDavid du Colombier 	if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){
64757195852SDavid du Colombier 		dprint("%s: busy timeout\n", d->unit->name);
6484de34a7eSDavid du Colombier 		d->state = Dmissing;
64957195852SDavid du Colombier 		return -1;
6504de34a7eSDavid du Colombier 	}
6514de34a7eSDavid du Colombier 	edma->iec = 0;
6524de34a7eSDavid du Colombier 	d->chip->arb->ic &= ~(0x101 << d->chipx);
65357195852SDavid du Colombier 	edma->config = 0x51f;
65457195852SDavid du Colombier 	if (d->ctlr->type == 2)
65557195852SDavid du Colombier 		edma->config |= 7<<11;
6564de34a7eSDavid du Colombier 	edma->txi = PADDR(d->tx);
65757195852SDavid du Colombier 	edma->txo = (ulong)d->tx & 0x3e0;
65857195852SDavid du Colombier 	edma->rxi = (ulong)d->rx & 0xf8;
6594de34a7eSDavid du Colombier 	edma->rxo = PADDR(d->rx);
6604de34a7eSDavid du Colombier 	edma->ctl |= 1;		/* enable dma */
6614de34a7eSDavid du Colombier 
66257195852SDavid du Colombier 	if(d->bridge->status = 0x113){
66357195852SDavid du Colombier 		dprint("%s: new\n", d->unit->name);
6644de34a7eSDavid du Colombier 		d->state = Dnew;
66557195852SDavid du Colombier 	}else
66657195852SDavid du Colombier 		print("%s: status not forced (should be okay)\n", d->unit->name);
66757195852SDavid du Colombier 	return 0;
6684de34a7eSDavid du Colombier }
6694de34a7eSDavid du Colombier 
6704de34a7eSDavid du Colombier static void
disabledrive(Drive * d)6714de34a7eSDavid du Colombier disabledrive(Drive *d)
6724de34a7eSDavid du Colombier {
6734de34a7eSDavid du Colombier 	int i;
6744de34a7eSDavid du Colombier 	ulong *r;
6754de34a7eSDavid du Colombier 
67657195852SDavid du Colombier 	dprint("%s: disabledrive\n", d->unit->name);
6774de34a7eSDavid du Colombier 
6784de34a7eSDavid du Colombier 	if(d->tx == nil)	/* never enabled */
6794de34a7eSDavid du Colombier 		return;
6804de34a7eSDavid du Colombier 
6814de34a7eSDavid du Colombier 	d->edma->ctl = 0;
6824de34a7eSDavid du Colombier 	d->edma->iem = 0;
6834de34a7eSDavid du Colombier 
68457195852SDavid du Colombier 	r = (ulong*)(d->ctlr->mmio + 0x1d64);
6854de34a7eSDavid du Colombier 	i = d->chipx;
6864de34a7eSDavid du Colombier 	if(d->chipx < 4)
6874de34a7eSDavid du Colombier 		*r &= ~(3 << (i*2));
6884de34a7eSDavid du Colombier 	else
6894de34a7eSDavid du Colombier 		*r |= ~(3 << (i*2+9));
6904de34a7eSDavid du Colombier }
6914de34a7eSDavid du Colombier 
6924de34a7eSDavid du Colombier static int
setudmamode(Drive * d,uchar mode)6934de34a7eSDavid du Colombier setudmamode(Drive *d, uchar mode)
6944de34a7eSDavid du Colombier {
6954de34a7eSDavid du Colombier 	Edma *edma;
6964de34a7eSDavid du Colombier 
69757195852SDavid du Colombier 	dprint("%s: setudmamode %d\n", d->unit->name, mode);
6984de34a7eSDavid du Colombier 
6994de34a7eSDavid du Colombier 	edma = d->edma;
70057195852SDavid du Colombier 	if (edma == nil) {
70157195852SDavid du Colombier 		iprint("setudamode(m%d): zero d->edma\m", d->driveno);
70257195852SDavid du Colombier 		return 0;
70357195852SDavid du Colombier 	}
70457195852SDavid du Colombier 	if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 9*1000) == 0){
70557195852SDavid du Colombier 		iprint("%s: cmdstat 0x%.2ux ready timeout\n", d->unit->name, edma->cmdstat);
7064de34a7eSDavid du Colombier 		return 0;
7074de34a7eSDavid du Colombier 	}
7084de34a7eSDavid du Colombier 	edma->altstat = ATAeIEN;
7094de34a7eSDavid du Colombier 	edma->err = 3;
7104de34a7eSDavid du Colombier 	edma->seccnt = 0x40 | mode;
71157195852SDavid du Colombier 	edma->cmdstat = 0xef;
7124de34a7eSDavid du Colombier 	microdelay(1);
71357195852SDavid du Colombier 	if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){
71457195852SDavid du Colombier 		iprint("%s: cmdstat 0x%.2ux busy timeout\n", d->unit->name, edma->cmdstat);
7154de34a7eSDavid du Colombier 		return 0;
7164de34a7eSDavid du Colombier 	}
7174de34a7eSDavid du Colombier 	return 1;
7184de34a7eSDavid du Colombier }
7194de34a7eSDavid du Colombier 
72057195852SDavid du Colombier static int
identifydrive(Drive * d)7214de34a7eSDavid du Colombier identifydrive(Drive *d)
7224de34a7eSDavid du Colombier {
7234de34a7eSDavid du Colombier 	int i;
7244de34a7eSDavid du Colombier 	ushort *id;
7254de34a7eSDavid du Colombier 	Edma *edma;
7264de34a7eSDavid du Colombier 	SDunit *unit;
7274de34a7eSDavid du Colombier 
72857195852SDavid du Colombier 	dprint("%s: identifydrive\n", d->unit->name);
7294de34a7eSDavid du Colombier 
7304de34a7eSDavid du Colombier 	if(setudmamode(d, 5) == 0)	/* do all SATA support 5? */
7314de34a7eSDavid du Colombier 		goto Error;
7324de34a7eSDavid du Colombier 
7334de34a7eSDavid du Colombier 	id = d->info;
7344de34a7eSDavid du Colombier 	memset(d->info, 0, sizeof d->info);
7354de34a7eSDavid du Colombier 	edma = d->edma;
73657195852SDavid du Colombier 	if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 5*1000) == 0)
7374de34a7eSDavid du Colombier 		goto Error;
7384de34a7eSDavid du Colombier 
7394de34a7eSDavid du Colombier 	edma->altstat = ATAeIEN;	/* no interrupts */
74057195852SDavid du Colombier 	edma->cmdstat = 0xec;
7414de34a7eSDavid du Colombier 	microdelay(1);
74257195852SDavid du Colombier 	if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0)
7434de34a7eSDavid du Colombier 		goto Error;
7444de34a7eSDavid du Colombier 	for(i = 0; i < 256; i++)
7454de34a7eSDavid du Colombier 		id[i] = edma->pio;
74657195852SDavid du Colombier 	if(edma->cmdstat & ATAbad)
7474de34a7eSDavid du Colombier 		goto Error;
7484de34a7eSDavid du Colombier 	i = lhgets(id+83) | lhgets(id+86);
7494de34a7eSDavid du Colombier 	if(i & (1<<10)){
7504de34a7eSDavid du Colombier 		d->flag |= Dext;
7514de34a7eSDavid du Colombier 		d->sectors = lhgetv(id+100);
7524de34a7eSDavid du Colombier 	}else{
7534de34a7eSDavid du Colombier 		d->flag &= ~Dext;
7544de34a7eSDavid du Colombier 		d->sectors = lhgetl(id+60);
7554de34a7eSDavid du Colombier 	}
7564de34a7eSDavid du Colombier 	idmove(d->serial, id+10, 20);
7574de34a7eSDavid du Colombier 	idmove(d->firmware, id+23, 8);
7584de34a7eSDavid du Colombier 	idmove(d->model, id+27, 40);
7594de34a7eSDavid du Colombier 
7604de34a7eSDavid du Colombier 	unit = d->unit;
7614de34a7eSDavid du Colombier 	memset(unit->inquiry, 0, sizeof unit->inquiry);
7624de34a7eSDavid du Colombier 	unit->inquiry[2] = 2;
7634de34a7eSDavid du Colombier 	unit->inquiry[3] = 2;
7644de34a7eSDavid du Colombier 	unit->inquiry[4] = sizeof(unit->inquiry)-4;
7654de34a7eSDavid du Colombier 	idmove((char*)unit->inquiry+8, id+27, 40);
7664de34a7eSDavid du Colombier 
76757195852SDavid du Colombier 	if(enabledrive(d) == 0) {
7684de34a7eSDavid du Colombier 		d->state = Dready;
76957195852SDavid du Colombier 		d->mediachange = 1;
77057195852SDavid du Colombier 		idprint("%s: LLBA %lld sectors\n", d->unit->name, d->sectors);
77157195852SDavid du Colombier 	} else
7724de34a7eSDavid du Colombier 		d->state = Derror;
77357195852SDavid du Colombier 	if(d->state == Dready)
77457195852SDavid du Colombier 		return 0;
77557195852SDavid du Colombier 	return -1;
7764de34a7eSDavid du Colombier Error:
77757195852SDavid du Colombier 	dprint("error...");
7784de34a7eSDavid du Colombier 	d->state = Derror;
77957195852SDavid du Colombier 	return -1;
7804de34a7eSDavid du Colombier }
7814de34a7eSDavid du Colombier 
78257195852SDavid du Colombier /* p. 163:
78357195852SDavid du Colombier 	M	recovered error
78457195852SDavid du Colombier 	P	protocol error
78557195852SDavid du Colombier 	N	PhyRdy change
78657195852SDavid du Colombier 	W	CommWake
78757195852SDavid du Colombier 	B	8-to-10 encoding error
78857195852SDavid du Colombier 	D	disparity error
78957195852SDavid du Colombier 	C	crc error
79057195852SDavid du Colombier 	H	handshake error
79157195852SDavid du Colombier 	S	link sequence error
79257195852SDavid du Colombier 	T	transport state transition error
79357195852SDavid du Colombier 	F	unrecognized fis type
79457195852SDavid du Colombier 	X	device changed
79557195852SDavid du Colombier */
79657195852SDavid du Colombier 
79757195852SDavid du Colombier static char stab[] = {
79857195852SDavid du Colombier [1]	'M',
79957195852SDavid du Colombier [10]	'P',
80057195852SDavid du Colombier [16]	'N',
80157195852SDavid du Colombier [18]	'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
80257195852SDavid du Colombier };
80357195852SDavid du Colombier static ulong sbad = (7<<20)|(3<<23);
8044de34a7eSDavid du Colombier 
8054de34a7eSDavid du Colombier static void
serrdecode(ulong r,char * s,char * e)80657195852SDavid du Colombier serrdecode(ulong r, char *s, char *e)
80757195852SDavid du Colombier {
80857195852SDavid du Colombier 	int i;
80957195852SDavid du Colombier 
81057195852SDavid du Colombier 	e -= 3;
81157195852SDavid du Colombier 	for(i = 0; i < nelem(stab) && s < e; i++){
81257195852SDavid du Colombier 		if((r&(1<<i)) && stab[i]){
81357195852SDavid du Colombier 			*s++ = stab[i];
81457195852SDavid du Colombier 			if(sbad&(1<<i))
81557195852SDavid du Colombier 				*s++ = '*';
81657195852SDavid du Colombier 		}
81757195852SDavid du Colombier 	}
81857195852SDavid du Colombier 	*s = 0;
81957195852SDavid du Colombier }
82057195852SDavid du Colombier 
82157195852SDavid du Colombier char *iectab[] = {
82257195852SDavid du Colombier 	"ePrtDataErr",
82357195852SDavid du Colombier 	"ePrtPRDErr",
82457195852SDavid du Colombier 	"eDevErr",
82557195852SDavid du Colombier 	"eDevDis",
82657195852SDavid du Colombier 	"eDevCon",
82757195852SDavid du Colombier 	"SerrInt",
82857195852SDavid du Colombier 	"eUnderrun",
82957195852SDavid du Colombier 	"eSelfDis2",
83057195852SDavid du Colombier 	"eSelfDis",
83157195852SDavid du Colombier 	"ePrtCRQBErr",
83257195852SDavid du Colombier 	"ePrtCRPBErr",
83357195852SDavid du Colombier 	"ePrtIntErr",
83457195852SDavid du Colombier 	"eIORdyErr",
83557195852SDavid du Colombier };
83657195852SDavid du Colombier 
83757195852SDavid du Colombier static char*
iecdecode(ulong cause)83857195852SDavid du Colombier iecdecode(ulong cause)
83957195852SDavid du Colombier {
84057195852SDavid du Colombier 	int i;
84157195852SDavid du Colombier 
84257195852SDavid du Colombier 	for(i = 0; i < nelem(iectab); i++)
84357195852SDavid du Colombier 		if(cause&(1<<i))
84457195852SDavid du Colombier 			return iectab[i];
84557195852SDavid du Colombier 	return "";
84657195852SDavid du Colombier }
84757195852SDavid du Colombier 
84857195852SDavid du Colombier enum{
84957195852SDavid du Colombier 	Cerror = ePrtDataErr|ePrtPRDErr|eDevErr|eSelfDis2|ePrtCRPBErr|ePrtIntErr,
85057195852SDavid du Colombier };
85157195852SDavid du Colombier 
85257195852SDavid du Colombier static void
updatedrive(Drive * d)85357195852SDavid du Colombier updatedrive(Drive *d)
8544de34a7eSDavid du Colombier {
8554de34a7eSDavid du Colombier 	int x;
85657195852SDavid du Colombier 	ulong cause;
8574de34a7eSDavid du Colombier 	Edma *edma;
85857195852SDavid du Colombier 	char buf[32+4+1];
8594de34a7eSDavid du Colombier 
8604de34a7eSDavid du Colombier 	edma = d->edma;
86157195852SDavid du Colombier 	if((edma->ctl&eEnEDMA) == 0){
86241dd6b47SDavid du Colombier 		/* FEr SATA#4 40xx */
86357195852SDavid du Colombier 		x = d->edma->cmdstat;
86457195852SDavid du Colombier 		USED(x);
8654de34a7eSDavid du Colombier 	}
86657195852SDavid du Colombier 	cause = edma->iec;
86757195852SDavid du Colombier 	if(cause == 0)
86857195852SDavid du Colombier 		return;
86957195852SDavid du Colombier 	dprint("%s: cause %08ulx [%s]\n", d->unit->name, cause, iecdecode(cause));
87057195852SDavid du Colombier 	if(cause & eDevCon)
8714de34a7eSDavid du Colombier 		d->state = Dnew;
87257195852SDavid du Colombier 	if(cause&eDevDis && d->state == Dready)
87357195852SDavid du Colombier 		iprint("%s: pulled: st=%08ulx\n", d->unit->name, cause);
87457195852SDavid du Colombier 	switch(d->ctlr->type){
87557195852SDavid du Colombier 	case 1:
8764de34a7eSDavid du Colombier 		if(cause&eSelfDis)
8774de34a7eSDavid du Colombier 			d->state = Derror;
87857195852SDavid du Colombier 		break;
87957195852SDavid du Colombier 	case 2:
88057195852SDavid du Colombier 		if(cause&Cerror)
88157195852SDavid du Colombier 			d->state = Derror;
88257195852SDavid du Colombier 		if(cause&SerrInt){
88357195852SDavid du Colombier 			serrdecode(d->bridge->serror, buf, buf+sizeof buf);
88457195852SDavid du Colombier 			dprint("%s: serror %08ulx [%s]\n", d->unit->name, (ulong)d->bridge->serror, buf);
88557195852SDavid du Colombier 			d->bridge->serror = d->bridge->serror;
88657195852SDavid du Colombier 		}
88757195852SDavid du Colombier 	}
88857195852SDavid du Colombier 	edma->iec = ~cause;
8894de34a7eSDavid du Colombier }
8904de34a7eSDavid du Colombier 
8914de34a7eSDavid du Colombier /*
8924de34a7eSDavid du Colombier  * Requests
8934de34a7eSDavid du Colombier  */
8944de34a7eSDavid du Colombier static Srb*
srbrw(int req,Drive * d,uchar * data,uint sectors,uvlong lba)8954de34a7eSDavid du Colombier srbrw(int req, Drive *d, uchar *data, uint sectors, uvlong lba)
8964de34a7eSDavid du Colombier {
8974de34a7eSDavid du Colombier 	int i;
8984de34a7eSDavid du Colombier 	Srb *srb;
8994de34a7eSDavid du Colombier 	static uchar cmd[2][2] = { 0xC8, 0x25, 0xCA, 0x35 };
9004de34a7eSDavid du Colombier 
9014de34a7eSDavid du Colombier 	srb = allocsrb();
9024de34a7eSDavid du Colombier 	srb->req = req;
9034de34a7eSDavid du Colombier 	srb->drive = d;
9044de34a7eSDavid du Colombier 	srb->blockno = lba;
9054de34a7eSDavid du Colombier 	srb->sectors = sectors;
9064de34a7eSDavid du Colombier 	srb->count = sectors*512;
9074de34a7eSDavid du Colombier 	srb->flag = 0;
9084de34a7eSDavid du Colombier 	srb->data = data;
9094de34a7eSDavid du Colombier 
9104de34a7eSDavid du Colombier 	for(i=0; i<6; i++)
9114de34a7eSDavid du Colombier 		srb->lba[i] = lba >> (8*i);
9124de34a7eSDavid du Colombier 	srb->cmd = cmd[srb->req!=SRBread][(d->flag&Dext)!=0];
9134de34a7eSDavid du Colombier 	return srb;
9144de34a7eSDavid du Colombier }
9154de34a7eSDavid du Colombier 
9164de34a7eSDavid du Colombier static uintptr
advance(uintptr pa,int shift)9174de34a7eSDavid du Colombier advance(uintptr pa, int shift)
9184de34a7eSDavid du Colombier {
9194de34a7eSDavid du Colombier 	int n, mask;
9204de34a7eSDavid du Colombier 
9214de34a7eSDavid du Colombier 	mask = 0x1F<<shift;
9224de34a7eSDavid du Colombier 	n = (pa & mask) + (1<<shift);
9234de34a7eSDavid du Colombier 	return (pa & ~mask) | (n & mask);
9244de34a7eSDavid du Colombier }
9254de34a7eSDavid du Colombier 
9264de34a7eSDavid du Colombier #define CMD(r, v) (((r)<<8) | ((v)&0xFF))
9274de34a7eSDavid du Colombier static void
mvsatarequest(ushort * cmd,Srb * srb,int ext)92857195852SDavid du Colombier mvsatarequest(ushort *cmd, Srb *srb, int ext)
9294de34a7eSDavid du Colombier {
9304de34a7eSDavid du Colombier 	*cmd++ = CMD(ARseccnt, 0);
9314de34a7eSDavid du Colombier 	*cmd++ = CMD(ARseccnt, srb->sectors);
9324de34a7eSDavid du Colombier 	*cmd++ = CMD(ARfea, 0);
9334de34a7eSDavid du Colombier 	if(ext){
9344de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba0, srb->lba[3]);
9354de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba0, srb->lba[0]);
9364de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba1, srb->lba[4]);
9374de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba1, srb->lba[1]);
9384de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba2, srb->lba[5]);
9394de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba2, srb->lba[2]);
94057195852SDavid du Colombier 		*cmd++ = CMD(ARdev, 0xe0);
9414de34a7eSDavid du Colombier 	}else{
9424de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba0, srb->lba[0]);
9434de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba1, srb->lba[1]);
9444de34a7eSDavid du Colombier 		*cmd++ = CMD(ARlba2, srb->lba[2]);
94557195852SDavid du Colombier 		*cmd++ = CMD(ARdev, srb->lba[3] | 0xe0);
9464de34a7eSDavid du Colombier 	}
94757195852SDavid du Colombier 	*cmd = CMD(ARcmd, srb->cmd) | (1<<15);
9484de34a7eSDavid du Colombier }
9494de34a7eSDavid du Colombier 
9504de34a7eSDavid du Colombier static void
startsrb(Drive * d,Srb * srb)9514de34a7eSDavid du Colombier startsrb(Drive *d, Srb *srb)
9524de34a7eSDavid du Colombier {
9534de34a7eSDavid du Colombier 	int i;
9544de34a7eSDavid du Colombier 	Edma *edma;
9554de34a7eSDavid du Colombier 	Prd *prd;
9564de34a7eSDavid du Colombier 	Tx *tx;
9574de34a7eSDavid du Colombier 
9584de34a7eSDavid du Colombier 	if(d->nsrb >= nelem(d->srb)){
9594de34a7eSDavid du Colombier 		srb->next = nil;
9604de34a7eSDavid du Colombier 		if(d->srbhead)
9614de34a7eSDavid du Colombier 			d->srbtail->next = srb;
9624de34a7eSDavid du Colombier 		else
9634de34a7eSDavid du Colombier 			d->srbhead = srb;
9644de34a7eSDavid du Colombier 		d->srbtail = srb;
9654de34a7eSDavid du Colombier 		return;
9664de34a7eSDavid du Colombier 	}
9674de34a7eSDavid du Colombier 
9684de34a7eSDavid du Colombier 	d->nsrb++;
9694de34a7eSDavid du Colombier 	for(i=0; i<nelem(d->srb); i++)
9704de34a7eSDavid du Colombier 		if(d->srb[i] == nil)
9714de34a7eSDavid du Colombier 			break;
9724de34a7eSDavid du Colombier 	if(i == nelem(d->srb))
9734de34a7eSDavid du Colombier 		panic("sdmv50xx: no free srbs");
97457195852SDavid du Colombier 	d->intick = MACHP(0)->ticks;
9754de34a7eSDavid du Colombier 	d->srb[i] = srb;
9764de34a7eSDavid du Colombier 	edma = d->edma;
9774de34a7eSDavid du Colombier 	tx = (Tx*)KADDR(edma->txi);
9784de34a7eSDavid du Colombier 	tx->flag = (i<<1) | (srb->req == SRBread);
9794de34a7eSDavid du Colombier 	prd = KADDR(tx->prdpa);
9804de34a7eSDavid du Colombier 	prd->pa = PADDR(srb->data);
9814de34a7eSDavid du Colombier 	prd->count = srb->count;
9824de34a7eSDavid du Colombier 	prd->flag = PRDeot;
98357195852SDavid du Colombier 	mvsatarequest(tx->regs, srb, d->flag&Dext);
9844de34a7eSDavid du Colombier 	coherence();
9854de34a7eSDavid du Colombier 	edma->txi = advance(edma->txi, 5);
98657195852SDavid du Colombier 	d->intick = MACHP(0)->ticks;
9874de34a7eSDavid du Colombier }
9884de34a7eSDavid du Colombier 
98957195852SDavid du Colombier enum{
99057195852SDavid du Colombier 	Rpidx	= 0x1f<<3,
99157195852SDavid du Colombier };
99257195852SDavid du Colombier 
9934de34a7eSDavid du Colombier static void
completesrb(Drive * d)9944de34a7eSDavid du Colombier completesrb(Drive *d)
9954de34a7eSDavid du Colombier {
9964de34a7eSDavid du Colombier 	Edma *edma;
9974de34a7eSDavid du Colombier 	Rx *rx;
9984de34a7eSDavid du Colombier 	Srb *srb;
9994de34a7eSDavid du Colombier 
10004de34a7eSDavid du Colombier 	edma = d->edma;
10014de34a7eSDavid du Colombier 	if((edma->ctl & eEnEDMA) == 0)
10024de34a7eSDavid du Colombier 		return;
10034de34a7eSDavid du Colombier 
100457195852SDavid du Colombier 	while((edma->rxo&Rpidx) != (edma->rxi&Rpidx)){
10054de34a7eSDavid du Colombier 		rx = (Rx*)KADDR(edma->rxo);
10064de34a7eSDavid du Colombier 		if(srb = d->srb[rx->cid]){
10074de34a7eSDavid du Colombier 			d->srb[rx->cid] = nil;
10084de34a7eSDavid du Colombier 			d->nsrb--;
100957195852SDavid du Colombier 			if(rx->cDevSts & ATAbad)
10104de34a7eSDavid du Colombier 				srb->flag |= SFerror;
101157195852SDavid du Colombier 			if (rx->cEdmaSts)
101257195852SDavid du Colombier 				iprint("cEdmaSts: %02ux\n", rx->cEdmaSts);
10134de34a7eSDavid du Colombier 			srb->sta = rx->cDevSts;
101457195852SDavid du Colombier 			srb->flag |= SFdone;
10154de34a7eSDavid du Colombier 			wakeup(srb);
10164de34a7eSDavid du Colombier 		}else
10174de34a7eSDavid du Colombier 			iprint("srb missing\n");
10184de34a7eSDavid du Colombier 		edma->rxo = advance(edma->rxo, 3);
10194de34a7eSDavid du Colombier 		if(srb = d->srbhead){
10204de34a7eSDavid du Colombier 			d->srbhead = srb->next;
10214de34a7eSDavid du Colombier 			startsrb(d, srb);
10224de34a7eSDavid du Colombier 		}
10234de34a7eSDavid du Colombier 	}
10244de34a7eSDavid du Colombier }
10254de34a7eSDavid du Colombier 
10264de34a7eSDavid du Colombier static int
srbdone(void * v)10274de34a7eSDavid du Colombier srbdone(void *v)
10284de34a7eSDavid du Colombier {
10294de34a7eSDavid du Colombier 	Srb *srb;
10304de34a7eSDavid du Colombier 
10314de34a7eSDavid du Colombier 	srb = v;
10324de34a7eSDavid du Colombier 	return srb->flag & SFdone;
10334de34a7eSDavid du Colombier }
10344de34a7eSDavid du Colombier 
10354de34a7eSDavid du Colombier /*
10364de34a7eSDavid du Colombier  * Interrupts
10374de34a7eSDavid du Colombier  */
10384de34a7eSDavid du Colombier static void
mv50interrupt(Ureg *,void * a)10394de34a7eSDavid du Colombier mv50interrupt(Ureg*, void *a)
10404de34a7eSDavid du Colombier {
10414de34a7eSDavid du Colombier 	int i;
10424de34a7eSDavid du Colombier 	ulong cause;
10434de34a7eSDavid du Colombier 	Ctlr *ctlr;
10444de34a7eSDavid du Colombier 	Drive *drive;
10454de34a7eSDavid du Colombier 
10464de34a7eSDavid du Colombier 	ctlr = a;
10474de34a7eSDavid du Colombier 	ilock(ctlr);
104857195852SDavid du Colombier 	cause = ctlr->lmmio[0x1d60/4];
104957195852SDavid du Colombier //	dprint("sd%c: mv50interrupt: 0x%lux\n", ctlr->sdev->idno, cause);
105057195852SDavid du Colombier 	for(i=0; i<ctlr->ndrive; i++)
10514de34a7eSDavid du Colombier 		if(cause & (3<<(i*2+i/4))){
10524de34a7eSDavid du Colombier 			drive = &ctlr->drive[i];
105357195852SDavid du Colombier 			if(drive->edma == 0)
105441dd6b47SDavid du Colombier 				continue;	/* not ready yet. */
10554de34a7eSDavid du Colombier 			ilock(drive);
105657195852SDavid du Colombier 			updatedrive(drive);
10574de34a7eSDavid du Colombier 			while(ctlr->chip[i/4].arb->ic & (0x0101 << (i%4))){
10584de34a7eSDavid du Colombier 				ctlr->chip[i/4].arb->ic = ~(0x101 << (i%4));
10594de34a7eSDavid du Colombier 				completesrb(drive);
10604de34a7eSDavid du Colombier 			}
10614de34a7eSDavid du Colombier 			iunlock(drive);
10624de34a7eSDavid du Colombier 		}
10634de34a7eSDavid du Colombier 	iunlock(ctlr);
10644de34a7eSDavid du Colombier }
10654de34a7eSDavid du Colombier 
106657195852SDavid du Colombier enum{
106757195852SDavid du Colombier 	Nms		= 256,
106857195852SDavid du Colombier 	Midwait		= 16*1024/Nms-1,
106957195852SDavid du Colombier 	Mphywait	= 512/Nms-1,
107057195852SDavid du Colombier };
107157195852SDavid du Colombier 
107257195852SDavid du Colombier static void
westerndigitalhung(Drive * d)107357195852SDavid du Colombier westerndigitalhung(Drive *d)
107457195852SDavid du Colombier {
107557195852SDavid du Colombier 	Edma *e;
107657195852SDavid du Colombier 
107757195852SDavid du Colombier 	e = d->edma;
107857195852SDavid du Colombier 	if(d->srb
107957195852SDavid du Colombier 	&& TK2MS(MACHP(0)->ticks-d->intick) > 5*1000
108057195852SDavid du Colombier 	&& (e->rxo&Rpidx) == (e->rxi&Rpidx)){
108157195852SDavid du Colombier 		dprint("westerndigital drive hung; resetting\n");
108257195852SDavid du Colombier 		d->state = Dreset;
108357195852SDavid du Colombier 	}
108457195852SDavid du Colombier }
108557195852SDavid du Colombier 
108657195852SDavid du Colombier static void
checkdrive(Drive * d,int i)108757195852SDavid du Colombier checkdrive(Drive *d, int i)
108857195852SDavid du Colombier {
108957195852SDavid du Colombier 	static ulong s, olds[NCtlr*NCtlrdrv];
109057195852SDavid du Colombier 	char *name;
109157195852SDavid du Colombier 
109257195852SDavid du Colombier 	ilock(d);
109357195852SDavid du Colombier 	name = d->unit->name;
109457195852SDavid du Colombier 	s = d->bridge->status;
109557195852SDavid du Colombier 	if(s != olds[i]){
109657195852SDavid du Colombier 		dprint("%s: status: %08lx -> %08lx: %s\n", name, olds[i], s, diskstates[d->state]);
109757195852SDavid du Colombier 		olds[i] = s;
109857195852SDavid du Colombier 	}
109941dd6b47SDavid du Colombier 	/* westerndigitalhung(d); */
110057195852SDavid du Colombier 	switch(d->state){
110157195852SDavid du Colombier 	case Dnew:
110257195852SDavid du Colombier 	case Dmissing:
110357195852SDavid du Colombier 		switch(s){
110457195852SDavid du Colombier 		case 0x000:
110557195852SDavid du Colombier 			break;
110657195852SDavid du Colombier 		default:
110757195852SDavid du Colombier 			dprint("%s: unknown state %8lx\n", name, s);
110857195852SDavid du Colombier 		case 0x100:
110957195852SDavid du Colombier 			if(++d->wait&Mphywait)
111057195852SDavid du Colombier 				break;
111157195852SDavid du Colombier 		reset:	d->mode ^= 1;
111257195852SDavid du Colombier 			dprint("%s: reset; new mode %d\n", name, d->mode);
111357195852SDavid du Colombier 			resetdisk(d);
111457195852SDavid du Colombier 			break;
111557195852SDavid du Colombier 		case 0x123:
111657195852SDavid du Colombier 		case 0x113:
111757195852SDavid du Colombier 			s = d->edma->cmdstat;
111857195852SDavid du Colombier 			if(s == 0x7f || (s&~ATAobs) != ATAdrdy){
111957195852SDavid du Colombier 				if((++d->wait&Midwait) == 0)
112057195852SDavid du Colombier 					goto reset;
112157195852SDavid du Colombier 			}else if(identifydrive(d) == -1)
112257195852SDavid du Colombier 				goto reset;
112357195852SDavid du Colombier 		}
112457195852SDavid du Colombier 		break;
112557195852SDavid du Colombier 	case Dready:
112657195852SDavid du Colombier 		if(s != 0)
112757195852SDavid du Colombier 			break;
112841dd6b47SDavid du Colombier 		iprint("%s: pulled: st=%08ulx\n", name, s);	/* never happens */
112957195852SDavid du Colombier 	case Dreset:
113057195852SDavid du Colombier 	case Derror:
113157195852SDavid du Colombier 		dprint("%s reset: mode %d\n", name, d->mode);
113257195852SDavid du Colombier 		resetdisk(d);
113357195852SDavid du Colombier 		break;
113457195852SDavid du Colombier 	}
113557195852SDavid du Colombier 	iunlock(d);
113657195852SDavid du Colombier }
113757195852SDavid du Colombier 
113857195852SDavid du Colombier static void
satakproc(void *)113957195852SDavid du Colombier satakproc(void*)
114057195852SDavid du Colombier {
114157195852SDavid du Colombier 	int i;
114257195852SDavid du Colombier 
114357195852SDavid du Colombier 	while(waserror())
114457195852SDavid du Colombier 		;
114557195852SDavid du Colombier 
114657195852SDavid du Colombier 	for(;;){
114757195852SDavid du Colombier 		tsleep(&up->sleep, return0, 0, Nms);
114857195852SDavid du Colombier 		for(i = 0; i < nmvsatadrive; i++)
114957195852SDavid du Colombier 			checkdrive(mvsatadrive[i], i);
115057195852SDavid du Colombier 	}
115157195852SDavid du Colombier }
115257195852SDavid du Colombier 
11534de34a7eSDavid du Colombier /*
11544de34a7eSDavid du Colombier  * Device discovery
11554de34a7eSDavid du Colombier  */
11564de34a7eSDavid du Colombier static SDev*
mv50pnp(void)11574de34a7eSDavid du Colombier mv50pnp(void)
11584de34a7eSDavid du Colombier {
11594de34a7eSDavid du Colombier 	int i, nunit;
11604de34a7eSDavid du Colombier 	uchar *base;
116157195852SDavid du Colombier 	ulong io, n, *mem;
11624de34a7eSDavid du Colombier 	Ctlr *ctlr;
11634de34a7eSDavid du Colombier 	Pcidev *p;
11644de34a7eSDavid du Colombier 	SDev *head, *tail, *sdev;
116557195852SDavid du Colombier 	Drive *d;
116657195852SDavid du Colombier 	static int ctlrno, done;
11674de34a7eSDavid du Colombier 
116857195852SDavid du Colombier 	dprint("mv50pnp\n");
116957195852SDavid du Colombier 	if(done++)
117057195852SDavid du Colombier 		return nil;
11714de34a7eSDavid du Colombier 
11724de34a7eSDavid du Colombier 	p = nil;
11734de34a7eSDavid du Colombier 	head = nil;
11744de34a7eSDavid du Colombier 	tail = nil;
117557195852SDavid du Colombier 	while((p = pcimatch(p, 0x11ab, 0)) != nil){
11764de34a7eSDavid du Colombier 		switch(p->did){
117757195852SDavid du Colombier 		case 0x5040:
11784de34a7eSDavid du Colombier 		case 0x5041:
117957195852SDavid du Colombier 		case 0x5080:
11804de34a7eSDavid du Colombier 		case 0x5081:
118157195852SDavid du Colombier 		case 0x6041:
118257195852SDavid du Colombier 		case 0x6081:
11834de34a7eSDavid du Colombier 			break;
11844de34a7eSDavid du Colombier 		default:
118557195852SDavid du Colombier 			print("mv50pnp: unknown did %ux ignored\n", (ushort)p->did);
11864de34a7eSDavid du Colombier 			continue;
11874de34a7eSDavid du Colombier 		}
118857195852SDavid du Colombier 		if (ctlrno >= NCtlr) {
118957195852SDavid du Colombier 			print("mv50pnp: too many controllers\n");
119057195852SDavid du Colombier 			break;
119157195852SDavid du Colombier 		}
119257195852SDavid du Colombier 		nunit = (p->did&0xf0) >> 4;
119357195852SDavid du Colombier 		print("Marvell 88SX%ux: %d SATA-%s ports with%s flash\n",
119457195852SDavid du Colombier 			(ushort)p->did, nunit,
119557195852SDavid du Colombier 			((p->did&0xf000)==0x6000? "II": "I"),
119657195852SDavid du Colombier 			(p->did&1? "": "out"));
11974de34a7eSDavid du Colombier 		if((sdev = malloc(sizeof(SDev))) == nil)
11984de34a7eSDavid du Colombier 			continue;
11994de34a7eSDavid du Colombier 		if((ctlr = malloc(sizeof(Ctlr))) == nil){
12004de34a7eSDavid du Colombier 			free(sdev);
12014de34a7eSDavid du Colombier 			continue;
12024de34a7eSDavid du Colombier 		}
120357195852SDavid du Colombier 		memset(sdev, 0, sizeof *sdev);
120457195852SDavid du Colombier 		memset(ctlr, 0, sizeof *ctlr);
120557195852SDavid du Colombier 
12064de34a7eSDavid du Colombier 		io = p->mem[0].bar & ~0x0F;
120757195852SDavid du Colombier 		mem = (ulong*)vmap(io, p->mem[0].size);
12084de34a7eSDavid du Colombier 		if(mem == 0){
12094de34a7eSDavid du Colombier 			print("sdmv50xx: address 0x%luX in use\n", io);
12104de34a7eSDavid du Colombier 			free(sdev);
12114de34a7eSDavid du Colombier 			free(ctlr);
12124de34a7eSDavid du Colombier 			continue;
12134de34a7eSDavid du Colombier 		}
121457195852SDavid du Colombier 		ctlr->rid = p->rid;
121557195852SDavid du Colombier 
121641dd6b47SDavid du Colombier 		/* avert thine eyes!  (what does this do?) */
121757195852SDavid du Colombier 		mem[0x104f0/4] = 0;
121857195852SDavid du Colombier 		ctlr->type = (p->did >> 12) & 3;
121957195852SDavid du Colombier 		if(ctlr->type == 1){
122057195852SDavid du Colombier 			n = mem[0xc00/4];
122157195852SDavid du Colombier 			n &= ~(3<<4);
122257195852SDavid du Colombier 			mem[0xc00/4] = n;
122357195852SDavid du Colombier 		}
122457195852SDavid du Colombier 
12254de34a7eSDavid du Colombier 		sdev->ifc = &sdmv50xxifc;
12264de34a7eSDavid du Colombier 		sdev->ctlr = ctlr;
12274de34a7eSDavid du Colombier 		sdev->nunit = nunit;
12284de34a7eSDavid du Colombier 		sdev->idno = 'E';
12294de34a7eSDavid du Colombier 		ctlr->sdev = sdev;
12304de34a7eSDavid du Colombier 		ctlr->irq = p->intl;
12314de34a7eSDavid du Colombier 		ctlr->tbdf = p->tbdf;
12324de34a7eSDavid du Colombier 		ctlr->pcidev = p;
123357195852SDavid du Colombier 		ctlr->lmmio = mem;
123457195852SDavid du Colombier 		ctlr->mmio = (uchar*)mem;
12354de34a7eSDavid du Colombier 		ctlr->nchip = (nunit+3)/4;
12364de34a7eSDavid du Colombier 		ctlr->ndrive = nunit;
123757195852SDavid du Colombier 		ctlr->enabled = 0;
12384de34a7eSDavid du Colombier 		for(i = 0; i < ctlr->nchip; i++){
12394de34a7eSDavid du Colombier 			base = ctlr->mmio+0x20000+0x10000*i;
12404de34a7eSDavid du Colombier 			ctlr->chip[i].arb = (Arb*)base;
12414de34a7eSDavid du Colombier 			ctlr->chip[i].edma = (Edma*)(base + 0x2000);
12424de34a7eSDavid du Colombier 		}
124357195852SDavid du Colombier 		for (i = 0; i < nunit; i++) {
124457195852SDavid du Colombier 			d = &ctlr->drive[i];
124557195852SDavid du Colombier 			d->sectors = 0;
124657195852SDavid du Colombier 			d->ctlr = ctlr;
124757195852SDavid du Colombier 			d->driveno = ctlrno*NCtlrdrv + i;
124857195852SDavid du Colombier 			d->chipx = i%4;
124957195852SDavid du Colombier 			d->chip = &ctlr->chip[i/4];
125057195852SDavid du Colombier 			d->edma = &d->chip->edma[d->chipx];
125157195852SDavid du Colombier 			mvsatadrive[d->driveno] = d;
125257195852SDavid du Colombier 		}
125357195852SDavid du Colombier 		nmvsatadrive += nunit;
125457195852SDavid du Colombier 		ctlrno++;
12554de34a7eSDavid du Colombier 		if(head)
12564de34a7eSDavid du Colombier 			tail->next = sdev;
12574de34a7eSDavid du Colombier 		else
12584de34a7eSDavid du Colombier 			head = sdev;
12594de34a7eSDavid du Colombier 		tail = sdev;
12604de34a7eSDavid du Colombier 	}
12614de34a7eSDavid du Colombier 	return head;
12624de34a7eSDavid du Colombier }
12634de34a7eSDavid du Colombier 
12644de34a7eSDavid du Colombier /*
12654de34a7eSDavid du Colombier  * Enable the controller.  Each disk has its own interrupt mask,
12664de34a7eSDavid du Colombier  * and those get enabled as the disks are brought online.
12674de34a7eSDavid du Colombier  */
12684de34a7eSDavid du Colombier static int
mv50enable(SDev * sdev)12694de34a7eSDavid du Colombier mv50enable(SDev *sdev)
12704de34a7eSDavid du Colombier {
12714de34a7eSDavid du Colombier 	char name[32];
12724de34a7eSDavid du Colombier 	Ctlr *ctlr;
12734de34a7eSDavid du Colombier 
127457195852SDavid du Colombier 	dprint("sd%c: enable\n", sdev->idno);
12754de34a7eSDavid du Colombier 
12764de34a7eSDavid du Colombier 	ctlr = sdev->ctlr;
127757195852SDavid du Colombier 	if (ctlr->enabled)
127857195852SDavid du Colombier 		return 1;
12794de34a7eSDavid du Colombier 	snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name);
12804de34a7eSDavid du Colombier 	intrenable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name);
128157195852SDavid du Colombier 	ctlr->enabled = 1;
12824de34a7eSDavid du Colombier 	return 1;
12834de34a7eSDavid du Colombier }
12844de34a7eSDavid du Colombier 
12854de34a7eSDavid du Colombier /*
12864de34a7eSDavid du Colombier  * Disable the controller.
12874de34a7eSDavid du Colombier  */
12884de34a7eSDavid du Colombier static int
mv50disable(SDev * sdev)12894de34a7eSDavid du Colombier mv50disable(SDev *sdev)
12904de34a7eSDavid du Colombier {
12914de34a7eSDavid du Colombier 	char name[32];
12924de34a7eSDavid du Colombier 	int i;
12934de34a7eSDavid du Colombier 	Ctlr *ctlr;
12944de34a7eSDavid du Colombier 	Drive *drive;
12954de34a7eSDavid du Colombier 
129657195852SDavid du Colombier 	dprint("sd%c: disable\n", sdev->idno);
12974de34a7eSDavid du Colombier 
12984de34a7eSDavid du Colombier 	ctlr = sdev->ctlr;
12994de34a7eSDavid du Colombier 	ilock(ctlr);
13004de34a7eSDavid du Colombier 	for(i=0; i<ctlr->sdev->nunit; i++){
13014de34a7eSDavid du Colombier 		drive = &ctlr->drive[i];
13024de34a7eSDavid du Colombier 		ilock(drive);
13034de34a7eSDavid du Colombier 		disabledrive(drive);
13044de34a7eSDavid du Colombier 		iunlock(drive);
13054de34a7eSDavid du Colombier 	}
13064de34a7eSDavid du Colombier 	iunlock(ctlr);
13074de34a7eSDavid du Colombier 	snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name);
13084de34a7eSDavid du Colombier 	intrdisable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name);
13094de34a7eSDavid du Colombier 	return 0;
13104de34a7eSDavid du Colombier }
13114de34a7eSDavid du Colombier 
13124de34a7eSDavid du Colombier /*
13134de34a7eSDavid du Colombier  * Clean up all disk structures.  Already disabled.
13144de34a7eSDavid du Colombier  * Could keep count of number of allocated controllers
13154de34a7eSDavid du Colombier  * and free the srblist when it drops to zero.
13164de34a7eSDavid du Colombier  */
13174de34a7eSDavid du Colombier static void
mv50clear(SDev * sdev)13184de34a7eSDavid du Colombier mv50clear(SDev *sdev)
13194de34a7eSDavid du Colombier {
13204de34a7eSDavid du Colombier 	int i;
13214de34a7eSDavid du Colombier 	Ctlr *ctlr;
13224de34a7eSDavid du Colombier 	Drive *d;
13234de34a7eSDavid du Colombier 
132457195852SDavid du Colombier 	dprint("sd%c: clear\n", sdev->idno);
13254de34a7eSDavid du Colombier 
13264de34a7eSDavid du Colombier 	ctlr = sdev->ctlr;
13274de34a7eSDavid du Colombier 	for(i=0; i<ctlr->ndrive; i++){
13284de34a7eSDavid du Colombier 		d = &ctlr->drive[i];
13294de34a7eSDavid du Colombier 		free(d->tx);
13304de34a7eSDavid du Colombier 		free(d->rx);
13314de34a7eSDavid du Colombier 		free(d->prd);
13324de34a7eSDavid du Colombier 	}
13334de34a7eSDavid du Colombier 	free(ctlr);
13344de34a7eSDavid du Colombier }
13354de34a7eSDavid du Colombier 
13364de34a7eSDavid du Colombier /*
13374de34a7eSDavid du Colombier  * Check that there is a disk or at least a hot swap bay in the drive.
13384de34a7eSDavid du Colombier  */
13394de34a7eSDavid du Colombier static int
mv50verify(SDunit * unit)13404de34a7eSDavid du Colombier mv50verify(SDunit *unit)
13414de34a7eSDavid du Colombier {
13424de34a7eSDavid du Colombier 	Ctlr *ctlr;
13434de34a7eSDavid du Colombier 	Drive *drive;
134457195852SDavid du Colombier 	int i;
13454de34a7eSDavid du Colombier 
134657195852SDavid du Colombier 	dprint("%s: verify\n", unit->name);
13474de34a7eSDavid du Colombier 	ctlr = unit->dev->ctlr;
13484de34a7eSDavid du Colombier 	drive = &ctlr->drive[unit->subno];
13494de34a7eSDavid du Colombier 	ilock(ctlr);
13504de34a7eSDavid du Colombier 	ilock(drive);
135157195852SDavid du Colombier 	i = configdrive(ctlr, drive, unit);
13524de34a7eSDavid du Colombier 	iunlock(drive);
13534de34a7eSDavid du Colombier 	iunlock(ctlr);
135457195852SDavid du Colombier 
13554de34a7eSDavid du Colombier 	/*
135657195852SDavid du Colombier 	 * If ctlr->type == 1, then the drives spin up whenever
135757195852SDavid du Colombier 	 * the controller feels like it; if ctlr->type != 1, then
135857195852SDavid du Colombier 	 * they spin up as a result of configdrive.
135957195852SDavid du Colombier 	 *
136057195852SDavid du Colombier 	 * If there is a drive in the slot, give it 1.5s to spin up
136157195852SDavid du Colombier 	 * before returning.  There is a noticeable drag on the
136257195852SDavid du Colombier 	 * power supply when spinning up fifteen drives
136357195852SDavid du Colombier 	 * all at once (like in the Coraid enclosures).
13644de34a7eSDavid du Colombier 	 */
136557195852SDavid du Colombier 	if(ctlr->type != 1 && i == 0){
136657195852SDavid du Colombier 		if(!waserror()){
136757195852SDavid du Colombier 			tsleep(&up->sleep, return0, 0, 1500);
136857195852SDavid du Colombier 			poperror();
136957195852SDavid du Colombier 		}
137057195852SDavid du Colombier 	}
13714de34a7eSDavid du Colombier 	return 1;
13724de34a7eSDavid du Colombier }
13734de34a7eSDavid du Colombier 
13744de34a7eSDavid du Colombier /*
13754de34a7eSDavid du Colombier  * Check whether the disk is online.
13764de34a7eSDavid du Colombier  */
13774de34a7eSDavid du Colombier static int
mv50online(SDunit * unit)13784de34a7eSDavid du Colombier mv50online(SDunit *unit)
13794de34a7eSDavid du Colombier {
13804de34a7eSDavid du Colombier 	Ctlr *ctlr;
138157195852SDavid du Colombier 	Drive *d;
138257195852SDavid du Colombier 	int r, s0;
138357195852SDavid du Colombier 	static int once;
138457195852SDavid du Colombier 
138557195852SDavid du Colombier 	if(once++ == 0)
138657195852SDavid du Colombier 		kproc("mvsata", satakproc, 0);
13874de34a7eSDavid du Colombier 
13884de34a7eSDavid du Colombier 	ctlr = unit->dev->ctlr;
138957195852SDavid du Colombier 	d = &ctlr->drive[unit->subno];
139057195852SDavid du Colombier 	r = 0;
139157195852SDavid du Colombier 	ilock(d);
139257195852SDavid du Colombier 	s0 = d->state;
139357195852SDavid du Colombier 	USED(s0);
139457195852SDavid du Colombier 	if(d->state == Dnew)
139557195852SDavid du Colombier 		identifydrive(d);
139657195852SDavid du Colombier 	if(d->mediachange){
139757195852SDavid du Colombier 		idprint("%s: online: %s -> %s\n", unit->name, diskstates[s0], diskstates[d->state]);
139857195852SDavid du Colombier 		r = 2;
139957195852SDavid du Colombier 		unit->sectors = d->sectors;
14004de34a7eSDavid du Colombier 		unit->secsize = 512;
140157195852SDavid du Colombier 		d->mediachange = 0;
140257195852SDavid du Colombier 	} else if(d->state == Dready)
140357195852SDavid du Colombier 		r = 1;
140457195852SDavid du Colombier 	iunlock(d);
140557195852SDavid du Colombier 	return r;
14064de34a7eSDavid du Colombier }
14074de34a7eSDavid du Colombier 
14084de34a7eSDavid du Colombier /*
14094de34a7eSDavid du Colombier  * Register dumps
14104de34a7eSDavid du Colombier  */
14114de34a7eSDavid du Colombier typedef struct Regs Regs;
14124de34a7eSDavid du Colombier struct Regs
14134de34a7eSDavid du Colombier {
14144de34a7eSDavid du Colombier 	ulong offset;
14154de34a7eSDavid du Colombier 	char *name;
14164de34a7eSDavid du Colombier };
14174de34a7eSDavid du Colombier 
14184de34a7eSDavid du Colombier static Regs regsctlr[] =
14194de34a7eSDavid du Colombier {
14204de34a7eSDavid du Colombier 	0x0C28, "pci serr# mask",
14214de34a7eSDavid du Colombier 	0x1D40, "pci err addr low",
14224de34a7eSDavid du Colombier 	0x1D44, "pci err addr hi",
14234de34a7eSDavid du Colombier 	0x1D48, "pci err attr",
14244de34a7eSDavid du Colombier 	0x1D50, "pci err cmd",
14254de34a7eSDavid du Colombier 	0x1D58, "pci intr cause",
14264de34a7eSDavid du Colombier 	0x1D5C, "pci mask cause",
14274de34a7eSDavid du Colombier 	0x1D60, "device micr",
14284de34a7eSDavid du Colombier 	0x1D64, "device mimr",
14294de34a7eSDavid du Colombier };
14304de34a7eSDavid du Colombier 
14314de34a7eSDavid du Colombier static Regs regsarb[] =
14324de34a7eSDavid du Colombier {
14334de34a7eSDavid du Colombier 	0x0004,	"arb rqop",
14344de34a7eSDavid du Colombier 	0x0008,	"arb rqip",
14354de34a7eSDavid du Colombier 	0x000C,	"arb ict",
14364de34a7eSDavid du Colombier 	0x0010,	"arb itt",
14374de34a7eSDavid du Colombier 	0x0014,	"arb ic",
14384de34a7eSDavid du Colombier 	0x0018,	"arb btc",
14394de34a7eSDavid du Colombier 	0x001C,	"arb bts",
14404de34a7eSDavid du Colombier 	0x0020,	"arb bpc",
14414de34a7eSDavid du Colombier };
14424de34a7eSDavid du Colombier 
14434de34a7eSDavid du Colombier static Regs regsbridge[] =
14444de34a7eSDavid du Colombier {
14454de34a7eSDavid du Colombier 	0x0000,	"bridge status",
14464de34a7eSDavid du Colombier 	0x0004,	"bridge serror",
14474de34a7eSDavid du Colombier 	0x0008,	"bridge sctrl",
14484de34a7eSDavid du Colombier 	0x000C,	"bridge phyctrl",
14494de34a7eSDavid du Colombier 	0x003C,	"bridge ctrl",
14504de34a7eSDavid du Colombier 	0x0074,	"bridge phymode",
14514de34a7eSDavid du Colombier };
14524de34a7eSDavid du Colombier 
14534de34a7eSDavid du Colombier static Regs regsedma[] =
14544de34a7eSDavid du Colombier {
14554de34a7eSDavid du Colombier 	0x0000,	"edma config",
14564de34a7eSDavid du Colombier 	0x0004,	"edma timer",
14574de34a7eSDavid du Colombier 	0x0008,	"edma iec",
14584de34a7eSDavid du Colombier 	0x000C,	"edma iem",
14594de34a7eSDavid du Colombier 	0x0010,	"edma txbasehi",
14604de34a7eSDavid du Colombier 	0x0014,	"edma txi",
14614de34a7eSDavid du Colombier 	0x0018,	"edma txo",
14624de34a7eSDavid du Colombier 	0x001C,	"edma rxbasehi",
14634de34a7eSDavid du Colombier 	0x0020,	"edma rxi",
14644de34a7eSDavid du Colombier 	0x0024,	"edma rxo",
14654de34a7eSDavid du Colombier 	0x0028,	"edma c",
14664de34a7eSDavid du Colombier 	0x002C,	"edma tc",
14674de34a7eSDavid du Colombier 	0x0030,	"edma status",
14684de34a7eSDavid du Colombier 	0x0034,	"edma iordyto",
14694de34a7eSDavid du Colombier /*	0x0100,	"edma pio",
14704de34a7eSDavid du Colombier 	0x0104,	"edma err",
14714de34a7eSDavid du Colombier 	0x0108,	"edma sectors",
14724de34a7eSDavid du Colombier 	0x010C,	"edma lba0",
14734de34a7eSDavid du Colombier 	0x0110,	"edma lba1",
14744de34a7eSDavid du Colombier 	0x0114,	"edma lba2",
14754de34a7eSDavid du Colombier 	0x0118,	"edma lba3",
14764de34a7eSDavid du Colombier 	0x011C,	"edma cmdstat",
14774de34a7eSDavid du Colombier 	0x0120,	"edma altstat",
14784de34a7eSDavid du Colombier */
14794de34a7eSDavid du Colombier };
14804de34a7eSDavid du Colombier 
14814de34a7eSDavid du Colombier static char*
rdregs(char * p,char * e,void * base,Regs * r,int n,char * prefix)14824de34a7eSDavid du Colombier rdregs(char *p, char *e, void *base, Regs *r, int n, char *prefix)
14834de34a7eSDavid du Colombier {
14844de34a7eSDavid du Colombier 	int i;
14854de34a7eSDavid du Colombier 
14864de34a7eSDavid du Colombier 	for(i = 0; i < n; i++)
1487*ea58ad6fSDavid du Colombier 		p = seprint(p, e, "%s%s%-19s %.8lux\n",
14884de34a7eSDavid du Colombier 			prefix? prefix: "", prefix? ": ": "",
148991b330d9SDavid du Colombier 			r[i].name, *(ulong *)((uchar*)base + r[i].offset));
14904de34a7eSDavid du Colombier 	return p;
14914de34a7eSDavid du Colombier }
14924de34a7eSDavid du Colombier 
14934de34a7eSDavid du Colombier static char*
rdinfo(char * p,char * e,ushort * info)14944de34a7eSDavid du Colombier rdinfo(char *p, char *e, ushort *info)
14954de34a7eSDavid du Colombier {
14964de34a7eSDavid du Colombier 	int i;
14974de34a7eSDavid du Colombier 
14984de34a7eSDavid du Colombier 	p = seprint(p, e, "info");
1499*ea58ad6fSDavid du Colombier 	for(i = 0; i < 256; i++)
1500*ea58ad6fSDavid du Colombier 		p = seprint(p, e, "%s%.4ux%s", i%8 == 0? "\t": "", info[i],
15014de34a7eSDavid du Colombier 			i%8 == 7? "\n": "");
15024de34a7eSDavid du Colombier 	return p;
15034de34a7eSDavid du Colombier }
15044de34a7eSDavid du Colombier 
15054de34a7eSDavid du Colombier static int
mv50rctl(SDunit * unit,char * p,int l)15064de34a7eSDavid du Colombier mv50rctl(SDunit *unit, char *p, int l)
15074de34a7eSDavid du Colombier {
15084de34a7eSDavid du Colombier 	char *e, *op;
15094de34a7eSDavid du Colombier 	Ctlr *ctlr;
15104de34a7eSDavid du Colombier 	Drive *drive;
15114de34a7eSDavid du Colombier 
15124de34a7eSDavid du Colombier 	if((ctlr = unit->dev->ctlr) == nil)
15134de34a7eSDavid du Colombier 		return 0;
15144de34a7eSDavid du Colombier 	drive = &ctlr->drive[unit->subno];
15154de34a7eSDavid du Colombier 
15164de34a7eSDavid du Colombier 	e = p+l;
15174de34a7eSDavid du Colombier 	op = p;
15184de34a7eSDavid du Colombier 	if(drive->state == Dready){
15194de34a7eSDavid du Colombier 		p = seprint(p, e, "model    %s\n", drive->model);
15204de34a7eSDavid du Colombier 		p = seprint(p, e, "serial   %s\n", drive->serial);
15214de34a7eSDavid du Colombier 		p = seprint(p, e, "firmware %s\n", drive->firmware);
15224de34a7eSDavid du Colombier 	}else
15234de34a7eSDavid du Colombier 		p = seprint(p, e, "no disk present\n");
15244de34a7eSDavid du Colombier 	p = seprint(p, e, "geometry %llud 512\n", drive->sectors);
15254de34a7eSDavid du Colombier 	p = rdinfo(p, e, drive->info);
15264de34a7eSDavid du Colombier 
15274de34a7eSDavid du Colombier 	p = rdregs(p, e, drive->chip->arb, regsarb, nelem(regsarb), nil);
15284de34a7eSDavid du Colombier 	p = rdregs(p, e, drive->bridge, regsbridge, nelem(regsbridge), nil);
15294de34a7eSDavid du Colombier 	p = rdregs(p, e, drive->edma, regsedma, nelem(regsedma), nil);
15304de34a7eSDavid du Colombier 
15314de34a7eSDavid du Colombier 	return p-op;
15324de34a7eSDavid du Colombier }
15334de34a7eSDavid du Colombier 
15344de34a7eSDavid du Colombier static int
mv50wctl(SDunit * unit,Cmdbuf * cb)15354de34a7eSDavid du Colombier mv50wctl(SDunit *unit, Cmdbuf *cb)
15364de34a7eSDavid du Colombier {
15374de34a7eSDavid du Colombier 	Ctlr *ctlr;
15384de34a7eSDavid du Colombier 	Drive *drive;
15394de34a7eSDavid du Colombier 
15404de34a7eSDavid du Colombier 	USED(unit);
15414de34a7eSDavid du Colombier 	if(strcmp(cb->f[0], "reset") == 0){
15424de34a7eSDavid du Colombier 		ctlr = unit->dev->ctlr;
15434de34a7eSDavid du Colombier 		drive = &ctlr->drive[unit->subno];
15444de34a7eSDavid du Colombier 		ilock(drive);
154557195852SDavid du Colombier 		drive->state = Dreset;
15464de34a7eSDavid du Colombier 		iunlock(drive);
15474de34a7eSDavid du Colombier 		return 0;
15484de34a7eSDavid du Colombier 	}
15494de34a7eSDavid du Colombier 	cmderror(cb, Ebadctl);
15504de34a7eSDavid du Colombier 	return -1;
15514de34a7eSDavid du Colombier }
15524de34a7eSDavid du Colombier 
1553*ea58ad6fSDavid du Colombier /*
1554*ea58ad6fSDavid du Colombier  * sd(3): ``Reading /dev/sdctl yields information about each controller,
1555*ea58ad6fSDavid du Colombier  * one line per controller.''
1556*ea58ad6fSDavid du Colombier  */
15574de34a7eSDavid du Colombier static char*
mv50rtopctl(SDev * sdev,char * p,char * e)15584de34a7eSDavid du Colombier mv50rtopctl(SDev *sdev, char *p, char *e)
15594de34a7eSDavid du Colombier {
15604de34a7eSDavid du Colombier 	char name[10];
15614de34a7eSDavid du Colombier 	Ctlr *ctlr;
15624de34a7eSDavid du Colombier 
15634de34a7eSDavid du Colombier 	ctlr = sdev->ctlr;
15644de34a7eSDavid du Colombier 	if(ctlr == nil)
15654de34a7eSDavid du Colombier 		return p;
15664de34a7eSDavid du Colombier 
15674de34a7eSDavid du Colombier 	snprint(name, sizeof name, "sd%c", sdev->idno);
15684de34a7eSDavid du Colombier 	p = rdregs(p, e, ctlr->mmio, regsctlr, nelem(regsctlr), name);
1569*ea58ad6fSDavid du Colombier 	if (Coraiddebug) {
1570*ea58ad6fSDavid du Colombier 		/* info for first disk.  BUG: this shouldn't be here. */
1571*ea58ad6fSDavid du Colombier 		p = rdregs(p, e, ctlr->chip[0].arb,
1572*ea58ad6fSDavid du Colombier 			regsarb, nelem(regsarb), name);
1573*ea58ad6fSDavid du Colombier 		p = rdregs(p, e, &ctlr->chip[0].arb->bridge[0],
1574*ea58ad6fSDavid du Colombier 			regsbridge, nelem(regsbridge), name);
1575*ea58ad6fSDavid du Colombier 		p = rdregs(p, e, &ctlr->chip[0].edma[0],
1576*ea58ad6fSDavid du Colombier 			regsedma, nelem(regsedma), name);
1577*ea58ad6fSDavid du Colombier 	}
15784de34a7eSDavid du Colombier 	return p;
15794de34a7eSDavid du Colombier }
15804de34a7eSDavid du Colombier 
15814de34a7eSDavid du Colombier static int
waitready(Drive * d)158257195852SDavid du Colombier waitready(Drive *d)
158357195852SDavid du Colombier {
158457195852SDavid du Colombier 	ulong s, i;
158557195852SDavid du Colombier 
158657195852SDavid du Colombier 	for(i = 0; i < 120; i++){
158757195852SDavid du Colombier 		ilock(d);
158857195852SDavid du Colombier 		s = d->bridge->status;
158957195852SDavid du Colombier 		iunlock(d);
159057195852SDavid du Colombier 		if(s == 0)
159157195852SDavid du Colombier 			return SDeio;
159257195852SDavid du Colombier 		if (d->state == Dready)
159357195852SDavid du Colombier 			return SDok;
159457195852SDavid du Colombier 		if ((i+1)%60 == 0){
159557195852SDavid du Colombier 			ilock(d);
159657195852SDavid du Colombier 			resetdisk(d);
159757195852SDavid du Colombier 			iunlock(d);
159857195852SDavid du Colombier 		}
159957195852SDavid du Colombier 		if(!waserror()){
160057195852SDavid du Colombier 			tsleep(&up->sleep, return0, 0, 1000);
160157195852SDavid du Colombier 			poperror();
160257195852SDavid du Colombier 		}
160357195852SDavid du Colombier 	}
160457195852SDavid du Colombier 	print("%s: not responding after 2 minutes\n", d->unit->name);
160557195852SDavid du Colombier 	return SDeio;
160657195852SDavid du Colombier }
160757195852SDavid du Colombier 
160857195852SDavid du Colombier static int
mv50rio(SDreq * r)16094de34a7eSDavid du Colombier mv50rio(SDreq *r)
16104de34a7eSDavid du Colombier {
161157195852SDavid du Colombier 	int count, max, n, status, try, flag;
16124de34a7eSDavid du Colombier 	uchar *cmd, *data;
16134de34a7eSDavid du Colombier 	uvlong lba;
16144de34a7eSDavid du Colombier 	Ctlr *ctlr;
16154de34a7eSDavid du Colombier 	Drive *drive;
16164de34a7eSDavid du Colombier 	SDunit *unit;
16174de34a7eSDavid du Colombier 	Srb *srb;
16184de34a7eSDavid du Colombier 
16194de34a7eSDavid du Colombier 	unit = r->unit;
16204de34a7eSDavid du Colombier 	ctlr = unit->dev->ctlr;
16214de34a7eSDavid du Colombier 	drive = &ctlr->drive[unit->subno];
16224de34a7eSDavid du Colombier 	cmd = r->cmd;
16234de34a7eSDavid du Colombier 
16244de34a7eSDavid du Colombier 	if((status = sdfakescsi(r, drive->info, sizeof drive->info)) != SDnostatus){
16254de34a7eSDavid du Colombier 		/* XXX check for SDcheck here */
16264de34a7eSDavid du Colombier 		r->status = status;
16274de34a7eSDavid du Colombier 		return status;
16284de34a7eSDavid du Colombier 	}
16294de34a7eSDavid du Colombier 
16304de34a7eSDavid du Colombier 	switch(cmd[0]){
16314de34a7eSDavid du Colombier 	case 0x28:	/* read */
16324de34a7eSDavid du Colombier 	case 0x2A:	/* write */
16334de34a7eSDavid du Colombier 		break;
16344de34a7eSDavid du Colombier 	default:
163557195852SDavid du Colombier 		iprint("%s: bad cmd 0x%.2ux\n", drive->unit->name, cmd[0]);
16364de34a7eSDavid du Colombier 		r->status = SDcheck;
16374de34a7eSDavid du Colombier 		return SDcheck;
16384de34a7eSDavid du Colombier 	}
16394de34a7eSDavid du Colombier 
16404de34a7eSDavid du Colombier 	lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
16414de34a7eSDavid du Colombier 	count = (cmd[7]<<8)|cmd[8];
16424de34a7eSDavid du Colombier 	if(r->data == nil)
16434de34a7eSDavid du Colombier 		return SDok;
16444de34a7eSDavid du Colombier 	if(r->dlen < count*unit->secsize)
16454de34a7eSDavid du Colombier 		count = r->dlen/unit->secsize;
16464de34a7eSDavid du Colombier 
164757195852SDavid du Colombier 	try = 0;
164857195852SDavid du Colombier retry:
164957195852SDavid du Colombier 	if(waitready(drive) != SDok)
165057195852SDavid du Colombier 		return SDeio;
16514de34a7eSDavid du Colombier 	/*
16524de34a7eSDavid du Colombier 	 * Could arrange here to have an Srb always outstanding:
16534de34a7eSDavid du Colombier 	 *
16544de34a7eSDavid du Colombier 	 *	lsrb = nil;
16554de34a7eSDavid du Colombier 	 *	while(count > 0 || lsrb != nil){
16564de34a7eSDavid du Colombier 	 *		srb = nil;
16574de34a7eSDavid du Colombier 	 *		if(count > 0){
16584de34a7eSDavid du Colombier 	 *			srb = issue next srb;
16594de34a7eSDavid du Colombier 	 *		}
16604de34a7eSDavid du Colombier 	 *		if(lsrb){
16614de34a7eSDavid du Colombier 	 *			sleep on lsrb and handle it
16624de34a7eSDavid du Colombier 	 *		}
16634de34a7eSDavid du Colombier 	 *	}
16644de34a7eSDavid du Colombier 	 *
16654de34a7eSDavid du Colombier 	 * On the disks I tried, this didn't help.  If anything,
16664de34a7eSDavid du Colombier 	 * it's a little slower.		-rsc
16674de34a7eSDavid du Colombier 	 */
16684de34a7eSDavid du Colombier 	data = r->data;
16694de34a7eSDavid du Colombier 	while(count > 0){
16704de34a7eSDavid du Colombier 		/*
16714de34a7eSDavid du Colombier 		 * Max is 128 sectors (64kB) because prd->count is 16 bits.
16724de34a7eSDavid du Colombier 		 */
16734de34a7eSDavid du Colombier 		max = 128;
16744de34a7eSDavid du Colombier 		n = count;
16754de34a7eSDavid du Colombier 		if(n > max)
16764de34a7eSDavid du Colombier 			n = max;
167757195852SDavid du Colombier 		if((drive->edma->ctl&eEnEDMA) == 0)
167857195852SDavid du Colombier 			goto tryagain;
16794de34a7eSDavid du Colombier 		srb = srbrw(cmd[0]==0x28 ? SRBread : SRBwrite, drive, data, n, lba);
16804de34a7eSDavid du Colombier 		ilock(drive);
16814de34a7eSDavid du Colombier 		startsrb(drive, srb);
16824de34a7eSDavid du Colombier 		iunlock(drive);
16834de34a7eSDavid du Colombier 
168457195852SDavid du Colombier 		/* Don't let user interrupt DMA. */
16854de34a7eSDavid du Colombier 		while(waserror())
16864de34a7eSDavid du Colombier 			;
168757195852SDavid du Colombier 		sleep(srb, srbdone, srb);
16884de34a7eSDavid du Colombier 		poperror();
16894de34a7eSDavid du Colombier 
169057195852SDavid du Colombier 		flag = srb->flag;
16914de34a7eSDavid du Colombier 		freesrb(srb);
169257195852SDavid du Colombier 		if(flag == 0){
169391b330d9SDavid du Colombier tryagain:
169491b330d9SDavid du Colombier 			if(++try == 10){
169557195852SDavid du Colombier 				print("%s: bad disk\n", drive->unit->name);
169657195852SDavid du Colombier 				return SDeio;
16974de34a7eSDavid du Colombier 			}
169857195852SDavid du Colombier 			dprint("%s: retry\n", drive->unit->name);
169957195852SDavid du Colombier 			if(!waserror()){
170057195852SDavid du Colombier 				tsleep(&up->sleep, return0, 0, 1000);
170157195852SDavid du Colombier 				poperror();
17024de34a7eSDavid du Colombier 			}
170357195852SDavid du Colombier 			goto retry;
17044de34a7eSDavid du Colombier 		}
170557195852SDavid du Colombier 		if(flag & SFerror){
170657195852SDavid du Colombier 			print("%s: i/o error\n", drive->unit->name);
170757195852SDavid du Colombier 			return SDeio;
170857195852SDavid du Colombier 		}
17094de34a7eSDavid du Colombier 		count -= n;
17104de34a7eSDavid du Colombier 		lba += n;
17114de34a7eSDavid du Colombier 		data += n*unit->secsize;
17124de34a7eSDavid du Colombier 	}
17134de34a7eSDavid du Colombier 	r->rlen = data - (uchar*)r->data;
17144de34a7eSDavid du Colombier 	return SDok;
17154de34a7eSDavid du Colombier }
17164de34a7eSDavid du Colombier 
17174de34a7eSDavid du Colombier SDifc sdmv50xxifc = {
17184de34a7eSDavid du Colombier 	"mv50xx",			/* name */
17194de34a7eSDavid du Colombier 
17204de34a7eSDavid du Colombier 	mv50pnp,			/* pnp */
17214de34a7eSDavid du Colombier 	nil,				/* legacy */
17224de34a7eSDavid du Colombier 	mv50enable,			/* enable */
17234de34a7eSDavid du Colombier 	mv50disable,			/* disable */
17244de34a7eSDavid du Colombier 
17254de34a7eSDavid du Colombier 	mv50verify,			/* verify */
17264de34a7eSDavid du Colombier 	mv50online,			/* online */
17274de34a7eSDavid du Colombier 	mv50rio,				/* rio */
17284de34a7eSDavid du Colombier 	mv50rctl,			/* rctl */
17294de34a7eSDavid du Colombier 	mv50wctl,			/* wctl */
17304de34a7eSDavid du Colombier 
17314de34a7eSDavid du Colombier 	scsibio,			/* bio */
17324de34a7eSDavid du Colombier 	nil,				/* probe */
17334de34a7eSDavid du Colombier 	mv50clear,			/* clear */
17344de34a7eSDavid du Colombier 	mv50rtopctl,			/* rtopctl */
17354de34a7eSDavid du Colombier };
17364de34a7eSDavid du Colombier 
17374de34a7eSDavid du Colombier /*
17384de34a7eSDavid du Colombier  * The original driver on which this one is based came with the
17394de34a7eSDavid du Colombier  * following notice:
17404de34a7eSDavid du Colombier  *
17414de34a7eSDavid du Colombier  * Copyright 2005
17424de34a7eSDavid du Colombier  * Coraid, Inc.
17434de34a7eSDavid du Colombier  *
17444de34a7eSDavid du Colombier  * This software is provided `as-is,' without any express or implied
17454de34a7eSDavid du Colombier  * warranty.  In no event will the author be held liable for any damages
17464de34a7eSDavid du Colombier  * arising from the use of this software.
17474de34a7eSDavid du Colombier  *
17484de34a7eSDavid du Colombier  * Permission is granted to anyone to use this software for any purpose,
17494de34a7eSDavid du Colombier  * including commercial applications, and to alter it and redistribute it
17504de34a7eSDavid du Colombier  * freely, subject to the following restrictions:
17514de34a7eSDavid du Colombier  *
17524de34a7eSDavid du Colombier  * 1.  The origin of this software must not be misrepresented; you must
17534de34a7eSDavid du Colombier  * not claim that you wrote the original software.  If you use this
17544de34a7eSDavid du Colombier  * software in a product, an acknowledgment in the product documentation
17554de34a7eSDavid du Colombier  * would be appreciated but is not required.
17564de34a7eSDavid du Colombier  *
17574de34a7eSDavid du Colombier  * 2.  Altered source versions must be plainly marked as such, and must
17584de34a7eSDavid du Colombier  * not be misrepresented as being the original software.
17594de34a7eSDavid du Colombier  *
17604de34a7eSDavid du Colombier  * 3.  This notice may not be removed or altered from any source
17614de34a7eSDavid du Colombier  * distribution.
17624de34a7eSDavid du Colombier  */
1763