xref: /plan9/sys/src/9/pc/sdata.c (revision 12009bff671a91993ae58f16dab833e809f4a6f3)
17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "io.h"
77dd7cddfSDavid du Colombier #include "ureg.h"
87dd7cddfSDavid du Colombier #include "../port/error.h"
97dd7cddfSDavid du Colombier 
1080ee5cbfSDavid du Colombier #include "../port/sd.h"
117dd7cddfSDavid du Colombier 
127dd7cddfSDavid du Colombier extern SDifc sdataifc;
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier enum {
15503d3e0aSDavid du Colombier 	DbgCONFIG	= 0x0001,	/* detected drive config info */
16503d3e0aSDavid du Colombier 	DbgIDENTIFY	= 0x0002,	/* detected drive identify info */
17503d3e0aSDavid du Colombier 	DbgSTATE	= 0x0004,	/* dump state on panic */
18503d3e0aSDavid du Colombier 	DbgPROBE	= 0x0008,	/* trace device probing */
19503d3e0aSDavid du Colombier 	DbgDEBUG	= 0x0080,	/* the current problem... */
20503d3e0aSDavid du Colombier 	DbgINL		= 0x0100,	/* That Inil20+ message we hate */
21503d3e0aSDavid du Colombier 	Dbg48BIT	= 0x0200,	/* 48-bit LBA */
22503d3e0aSDavid du Colombier 	DbgBsy		= 0x0400,	/* interrupt but Bsy (shared IRQ) */
237dd7cddfSDavid du Colombier };
243ff48bf5SDavid du Colombier #define DEBUG		(DbgDEBUG|DbgSTATE)
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier enum {					/* I/O ports */
277dd7cddfSDavid du Colombier 	Data		= 0,
287dd7cddfSDavid du Colombier 	Error		= 1,		/* (read) */
297dd7cddfSDavid du Colombier 	Features	= 1,		/* (write) */
306a081dcdSDavid du Colombier 	Count		= 2,		/* sector count<7-0>, sector count<15-8> */
317dd7cddfSDavid du Colombier 	Ir		= 2,		/* interrupt reason (PACKET) */
326a081dcdSDavid du Colombier 	Sector		= 3,		/* sector number */
336a081dcdSDavid du Colombier 	Lbalo		= 3,		/* LBA<7-0>, LBA<31-24> */
346a081dcdSDavid du Colombier 	Cyllo		= 4,		/* cylinder low */
357dd7cddfSDavid du Colombier 	Bytelo		= 4,		/* byte count low (PACKET) */
366a081dcdSDavid du Colombier 	Lbamid		= 4,		/* LBA<15-8>, LBA<39-32> */
376a081dcdSDavid du Colombier 	Cylhi		= 5,		/* cylinder high */
387dd7cddfSDavid du Colombier 	Bytehi		= 5,		/* byte count hi (PACKET) */
396a081dcdSDavid du Colombier 	Lbahi		= 5,		/* LBA<23-16>, LBA<47-40> */
406a5dc222SDavid du Colombier 	Dh		= 6,		/* Device/Head, LBA<27-24> */
417dd7cddfSDavid du Colombier 	Status		= 7,		/* (read) */
427dd7cddfSDavid du Colombier 	Command		= 7,		/* (write) */
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier 	As		= 2,		/* Alternate Status (read) */
457dd7cddfSDavid du Colombier 	Dc		= 2,		/* Device Control (write) */
467dd7cddfSDavid du Colombier };
477dd7cddfSDavid du Colombier 
487dd7cddfSDavid du Colombier enum {					/* Error */
497dd7cddfSDavid du Colombier 	Med		= 0x01,		/* Media error */
507dd7cddfSDavid du Colombier 	Ili		= 0x01,		/* command set specific (PACKET) */
517dd7cddfSDavid du Colombier 	Nm		= 0x02,		/* No Media */
527dd7cddfSDavid du Colombier 	Eom		= 0x02,		/* command set specific (PACKET) */
537dd7cddfSDavid du Colombier 	Abrt		= 0x04,		/* Aborted command */
547dd7cddfSDavid du Colombier 	Mcr		= 0x08,		/* Media Change Request */
557dd7cddfSDavid du Colombier 	Idnf		= 0x10,		/* no user-accessible address */
567dd7cddfSDavid du Colombier 	Mc		= 0x20,		/* Media Change */
577dd7cddfSDavid du Colombier 	Unc		= 0x40,		/* Uncorrectable data error */
587dd7cddfSDavid du Colombier 	Wp		= 0x40,		/* Write Protect */
597dd7cddfSDavid du Colombier 	Icrc		= 0x80,		/* Interface CRC error */
607dd7cddfSDavid du Colombier };
617dd7cddfSDavid du Colombier 
627dd7cddfSDavid du Colombier enum {					/* Features */
637dd7cddfSDavid du Colombier 	Dma		= 0x01,		/* data transfer via DMA (PACKET) */
647dd7cddfSDavid du Colombier 	Ovl		= 0x02,		/* command overlapped (PACKET) */
657dd7cddfSDavid du Colombier };
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier enum {					/* Interrupt Reason */
687dd7cddfSDavid du Colombier 	Cd		= 0x01,		/* Command/Data */
692f2115d0SDavid du Colombier 	Io		= 0x02,		/* I/O direction: read */
707dd7cddfSDavid du Colombier 	Rel		= 0x04,		/* Bus Release */
717dd7cddfSDavid du Colombier };
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier enum {					/* Device/Head */
747dd7cddfSDavid du Colombier 	Dev0		= 0xA0,		/* Master */
757dd7cddfSDavid du Colombier 	Dev1		= 0xB0,		/* Slave */
767dd7cddfSDavid du Colombier 	Lba		= 0x40,		/* LBA mode */
776a081dcdSDavid du Colombier };
786a081dcdSDavid du Colombier 
797dd7cddfSDavid du Colombier enum {					/* Status, Alternate Status */
807dd7cddfSDavid du Colombier 	Err		= 0x01,		/* Error */
817dd7cddfSDavid du Colombier 	Chk		= 0x01,		/* Check error (PACKET) */
827dd7cddfSDavid du Colombier 	Drq		= 0x08,		/* Data Request */
837dd7cddfSDavid du Colombier 	Dsc		= 0x10,		/* Device Seek Complete */
847dd7cddfSDavid du Colombier 	Serv		= 0x10,		/* Service */
857dd7cddfSDavid du Colombier 	Df		= 0x20,		/* Device Fault */
867dd7cddfSDavid du Colombier 	Dmrd		= 0x20,		/* DMA ready (PACKET) */
877dd7cddfSDavid du Colombier 	Drdy		= 0x40,		/* Device Ready */
887dd7cddfSDavid du Colombier 	Bsy		= 0x80,		/* Busy */
897dd7cddfSDavid du Colombier };
907dd7cddfSDavid du Colombier 
917dd7cddfSDavid du Colombier enum {					/* Command */
927dd7cddfSDavid du Colombier 	Cnop		= 0x00,		/* NOP */
937dd7cddfSDavid du Colombier 	Cdr		= 0x08,		/* Device Reset */
947dd7cddfSDavid du Colombier 	Crs		= 0x20,		/* Read Sectors */
956a081dcdSDavid du Colombier 	Crs48		= 0x24,		/* Read Sectors Ext */
966a081dcdSDavid du Colombier 	Crd48		= 0x25,		/* Read w/ DMA Ext */
976a081dcdSDavid du Colombier 	Crdq48		= 0x26,		/* Read w/ DMA Queued Ext */
986a081dcdSDavid du Colombier 	Crsm48		= 0x29,		/* Read Multiple Ext */
997dd7cddfSDavid du Colombier 	Cws		= 0x30,		/* Write Sectors */
1006a081dcdSDavid du Colombier 	Cws48		= 0x34,		/* Write Sectors Ext */
1016a081dcdSDavid du Colombier 	Cwd48		= 0x35,		/* Write w/ DMA Ext */
1026a081dcdSDavid du Colombier 	Cwdq48		= 0x36,		/* Write w/ DMA Queued Ext */
1036a081dcdSDavid du Colombier 	Cwsm48		= 0x39,		/* Write Multiple Ext */
1047dd7cddfSDavid du Colombier 	Cedd		= 0x90,		/* Execute Device Diagnostics */
1057dd7cddfSDavid du Colombier 	Cpkt		= 0xA0,		/* Packet */
1067dd7cddfSDavid du Colombier 	Cidpkt		= 0xA1,		/* Identify Packet Device */
1077dd7cddfSDavid du Colombier 	Crsm		= 0xC4,		/* Read Multiple */
1087dd7cddfSDavid du Colombier 	Cwsm		= 0xC5,		/* Write Multiple */
1097dd7cddfSDavid du Colombier 	Csm		= 0xC6,		/* Set Multiple */
1107dd7cddfSDavid du Colombier 	Crdq		= 0xC7,		/* Read DMA queued */
1117dd7cddfSDavid du Colombier 	Crd		= 0xC8,		/* Read DMA */
1127dd7cddfSDavid du Colombier 	Cwd		= 0xCA,		/* Write DMA */
1137dd7cddfSDavid du Colombier 	Cwdq		= 0xCC,		/* Write DMA queued */
11480ee5cbfSDavid du Colombier 	Cstandby	= 0xE2,		/* Standby */
1157dd7cddfSDavid du Colombier 	Cid		= 0xEC,		/* Identify Device */
1167dd7cddfSDavid du Colombier 	Csf		= 0xEF,		/* Set Features */
1177dd7cddfSDavid du Colombier };
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier enum {					/* Device Control */
1207dd7cddfSDavid du Colombier 	Nien		= 0x02,		/* (not) Interrupt Enable */
1217dd7cddfSDavid du Colombier 	Srst		= 0x04,		/* Software Reset */
1226a081dcdSDavid du Colombier 	Hob		= 0x80,		/* High Order Bit [sic] */
1237dd7cddfSDavid du Colombier };
1247dd7cddfSDavid du Colombier 
1257dd7cddfSDavid du Colombier enum {					/* PCI Configuration Registers */
1267dd7cddfSDavid du Colombier 	Bmiba		= 0x20,		/* Bus Master Interface Base Address */
1277dd7cddfSDavid du Colombier 	Idetim		= 0x40,		/* IE Timing */
1287dd7cddfSDavid du Colombier 	Sidetim		= 0x44,		/* Slave IE Timing */
1297dd7cddfSDavid du Colombier 	Udmactl		= 0x48,		/* Ultra DMA/33 Control */
1307dd7cddfSDavid du Colombier 	Udmatim		= 0x4A,		/* Ultra DMA/33 Timing */
1317dd7cddfSDavid du Colombier };
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier enum {					/* Bus Master IDE I/O Ports */
1347dd7cddfSDavid du Colombier 	Bmicx		= 0,		/* Command */
1357dd7cddfSDavid du Colombier 	Bmisx		= 2,		/* Status */
1367dd7cddfSDavid du Colombier 	Bmidtpx		= 4,		/* Descriptor Table Pointer */
1377dd7cddfSDavid du Colombier };
1387dd7cddfSDavid du Colombier 
1397dd7cddfSDavid du Colombier enum {					/* Bmicx */
1407dd7cddfSDavid du Colombier 	Ssbm		= 0x01,		/* Start/Stop Bus Master */
1417dd7cddfSDavid du Colombier 	Rwcon		= 0x08,		/* Read/Write Control */
1427dd7cddfSDavid du Colombier };
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier enum {					/* Bmisx */
1457dd7cddfSDavid du Colombier 	Bmidea		= 0x01,		/* Bus Master IDE Active */
1467dd7cddfSDavid du Colombier 	Idedmae		= 0x02,		/* IDE DMA Error  (R/WC) */
1477dd7cddfSDavid du Colombier 	Ideints		= 0x04,		/* IDE Interrupt Status (R/WC) */
1487dd7cddfSDavid du Colombier 	Dma0cap		= 0x20,		/* Drive 0 DMA Capable */
1497dd7cddfSDavid du Colombier 	Dma1cap		= 0x40,		/* Drive 0 DMA Capable */
1507dd7cddfSDavid du Colombier };
1517dd7cddfSDavid du Colombier enum {					/* Physical Region Descriptor */
1522839d78eSDavid du Colombier 	PrdEOT		= 0x80000000,	/* End of Transfer */
1537dd7cddfSDavid du Colombier };
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier enum {					/* offsets into the identify info. */
1567dd7cddfSDavid du Colombier 	Iconfig		= 0,		/* general configuration */
1577dd7cddfSDavid du Colombier 	Ilcyl		= 1,		/* logical cylinders */
1587dd7cddfSDavid du Colombier 	Ilhead		= 3,		/* logical heads */
1597dd7cddfSDavid du Colombier 	Ilsec		= 6,		/* logical sectors per logical track */
1607dd7cddfSDavid du Colombier 	Iserial		= 10,		/* serial number */
1617dd7cddfSDavid du Colombier 	Ifirmware	= 23,		/* firmware revision */
1627dd7cddfSDavid du Colombier 	Imodel		= 27,		/* model number */
1637dd7cddfSDavid du Colombier 	Imaxrwm		= 47,		/* max. read/write multiple sectors */
1647dd7cddfSDavid du Colombier 	Icapabilities	= 49,		/* capabilities */
1657dd7cddfSDavid du Colombier 	Istandby	= 50,		/* device specific standby timer */
1667dd7cddfSDavid du Colombier 	Ipiomode	= 51,		/* PIO data transfer mode number */
1677dd7cddfSDavid du Colombier 	Ivalid		= 53,
1687dd7cddfSDavid du Colombier 	Iccyl		= 54,		/* cylinders if (valid&0x01) */
1697dd7cddfSDavid du Colombier 	Ichead		= 55,		/* heads if (valid&0x01) */
1707dd7cddfSDavid du Colombier 	Icsec		= 56,		/* sectors if (valid&0x01) */
1717dd7cddfSDavid du Colombier 	Iccap		= 57,		/* capacity if (valid&0x01) */
1727dd7cddfSDavid du Colombier 	Irwm		= 59,		/* read/write multiple */
1736a081dcdSDavid du Colombier 	Ilba		= 60,		/* LBA size */
1747dd7cddfSDavid du Colombier 	Imwdma		= 63,		/* multiword DMA mode */
1757dd7cddfSDavid du Colombier 	Iapiomode	= 64,		/* advanced PIO modes supported */
1767dd7cddfSDavid du Colombier 	Iminmwdma	= 65,		/* min. multiword DMA cycle time */
1777dd7cddfSDavid du Colombier 	Irecmwdma	= 66,		/* rec. multiword DMA cycle time */
1787dd7cddfSDavid du Colombier 	Iminpio		= 67,		/* min. PIO cycle w/o flow control */
1797dd7cddfSDavid du Colombier 	Iminiordy	= 68,		/* min. PIO cycle with IORDY */
1807dd7cddfSDavid du Colombier 	Ipcktbr		= 71,		/* time from PACKET to bus release */
1817dd7cddfSDavid du Colombier 	Iserbsy		= 72,		/* time from SERVICE to !Bsy */
1827dd7cddfSDavid du Colombier 	Iqdepth		= 75,		/* max. queue depth */
1837dd7cddfSDavid du Colombier 	Imajor		= 80,		/* major version number */
1847dd7cddfSDavid du Colombier 	Iminor		= 81,		/* minor version number */
18580ee5cbfSDavid du Colombier 	Icsfs		= 82,		/* command set/feature supported */
18680ee5cbfSDavid du Colombier 	Icsfe		= 85,		/* command set/feature enabled */
1877dd7cddfSDavid du Colombier 	Iudma		= 88,		/* ultra DMA mode */
1887dd7cddfSDavid du Colombier 	Ierase		= 89,		/* time for security erase */
1897dd7cddfSDavid du Colombier 	Ieerase		= 90,		/* time for enhanced security erase */
1907dd7cddfSDavid du Colombier 	Ipower		= 91,		/* current advanced power management */
1916a081dcdSDavid du Colombier 	Ilba48		= 100,		/* 48-bit LBA size (64 bits in 100-103) */
1927dd7cddfSDavid du Colombier 	Irmsn		= 127,		/* removable status notification */
1936a081dcdSDavid du Colombier 	Isecstat	= 128,		/* security status */
1946a081dcdSDavid du Colombier 	Icfapwr		= 160,		/* CFA power mode */
1956a081dcdSDavid du Colombier 	Imediaserial	= 176,		/* current media serial number */
1966a081dcdSDavid du Colombier 	Icksum		= 255,		/* checksum */
1976a081dcdSDavid du Colombier };
1986a081dcdSDavid du Colombier 
1996a081dcdSDavid du Colombier enum {					/* bit masks for config identify info */
2006a081dcdSDavid du Colombier 	Mpktsz		= 0x0003,	/* packet command size */
2016a081dcdSDavid du Colombier 	Mincomplete	= 0x0004,	/* incomplete information */
2026a081dcdSDavid du Colombier 	Mdrq		= 0x0060,	/* DRQ type */
2036a081dcdSDavid du Colombier 	Mrmdev		= 0x0080,	/* device is removable */
2046a081dcdSDavid du Colombier 	Mtype		= 0x1F00,	/* device type */
2056a081dcdSDavid du Colombier 	Mproto		= 0x8000,	/* command protocol */
2066a081dcdSDavid du Colombier };
2076a081dcdSDavid du Colombier 
2086a081dcdSDavid du Colombier enum {					/* bit masks for capabilities identify info */
2096a081dcdSDavid du Colombier 	Mdma		= 0x0100,	/* DMA supported */
2106a081dcdSDavid du Colombier 	Mlba		= 0x0200,	/* LBA supported */
2116a081dcdSDavid du Colombier 	Mnoiordy	= 0x0400,	/* IORDY may be disabled */
2126a081dcdSDavid du Colombier 	Miordy		= 0x0800,	/* IORDY supported */
2136a081dcdSDavid du Colombier 	Msoftrst	= 0x1000,	/* needs soft reset when Bsy */
2146a081dcdSDavid du Colombier 	Mstdby		= 0x2000,	/* standby supported */
2156a081dcdSDavid du Colombier 	Mqueueing	= 0x4000,	/* queueing overlap supported */
2166a081dcdSDavid du Colombier 	Midma		= 0x8000,	/* interleaved DMA supported */
2176a081dcdSDavid du Colombier };
2186a081dcdSDavid du Colombier 
2196a081dcdSDavid du Colombier enum {					/* bit masks for supported/enabled features */
2206a081dcdSDavid du Colombier 	Msmart		= 0x0001,
2216a081dcdSDavid du Colombier 	Msecurity	= 0x0002,
2226a081dcdSDavid du Colombier 	Mrmmedia	= 0x0004,
2236a081dcdSDavid du Colombier 	Mpwrmgmt	= 0x0008,
2246a081dcdSDavid du Colombier 	Mpkt		= 0x0010,
2256a081dcdSDavid du Colombier 	Mwcache		= 0x0020,
2266a081dcdSDavid du Colombier 	Mlookahead	= 0x0040,
2276a081dcdSDavid du Colombier 	Mrelirq		= 0x0080,
2286a081dcdSDavid du Colombier 	Msvcirq		= 0x0100,
2296a081dcdSDavid du Colombier 	Mreset		= 0x0200,
2306a081dcdSDavid du Colombier 	Mprotected	= 0x0400,
2316a081dcdSDavid du Colombier 	Mwbuf		= 0x1000,
2326a081dcdSDavid du Colombier 	Mrbuf		= 0x2000,
2336a081dcdSDavid du Colombier 	Mnop		= 0x4000,
2346a081dcdSDavid du Colombier 	Mmicrocode	= 0x0001,
2356a081dcdSDavid du Colombier 	Mqueued		= 0x0002,
2366a081dcdSDavid du Colombier 	Mcfa		= 0x0004,
2376a081dcdSDavid du Colombier 	Mapm		= 0x0008,
2386a081dcdSDavid du Colombier 	Mnotify		= 0x0010,
2396a081dcdSDavid du Colombier 	Mstandby	= 0x0020,
2406a081dcdSDavid du Colombier 	Mspinup		= 0x0040,
2416a081dcdSDavid du Colombier 	Mmaxsec		= 0x0100,
2426a081dcdSDavid du Colombier 	Mautoacoustic	= 0x0200,
2436a081dcdSDavid du Colombier 	Maddr48		= 0x0400,
2446a081dcdSDavid du Colombier 	Mdevconfov	= 0x0800,
2456a081dcdSDavid du Colombier 	Mflush		= 0x1000,
2466a081dcdSDavid du Colombier 	Mflush48	= 0x2000,
2476a081dcdSDavid du Colombier 	Msmarterror	= 0x0001,
2486a081dcdSDavid du Colombier 	Msmartselftest	= 0x0002,
2496a081dcdSDavid du Colombier 	Mmserial	= 0x0004,
2506a081dcdSDavid du Colombier 	Mmpassthru	= 0x0008,
2516a081dcdSDavid du Colombier 	Mlogging	= 0x0020,
2527dd7cddfSDavid du Colombier };
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier typedef struct Ctlr Ctlr;
2557dd7cddfSDavid du Colombier typedef struct Drive Drive;
2567dd7cddfSDavid du Colombier 
2574de34a7eSDavid du Colombier typedef struct Prd {			/* Physical Region Descriptor */
2587dd7cddfSDavid du Colombier 	ulong	pa;			/* Physical Base Address */
2597dd7cddfSDavid du Colombier 	int	count;
2607dd7cddfSDavid du Colombier } Prd;
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier enum {
2634de34a7eSDavid du Colombier 	BMspan		= 64*1024,	/* must be power of 2 <= 64*1024 */
2644de34a7eSDavid du Colombier 
2654de34a7eSDavid du Colombier 	Nprd		= SDmaxio/BMspan+2,
2667dd7cddfSDavid du Colombier };
2677dd7cddfSDavid du Colombier 
2687dd7cddfSDavid du Colombier typedef struct Ctlr {
2697dd7cddfSDavid du Colombier 	int	cmdport;
2707dd7cddfSDavid du Colombier 	int	ctlport;
2717dd7cddfSDavid du Colombier 	int	irq;
2727dd7cddfSDavid du Colombier 	int	tbdf;
2737dd7cddfSDavid du Colombier 	int	bmiba;			/* bus master interface base address */
2744de34a7eSDavid du Colombier 	int	maxio;			/* sector count transfer maximum */
2754de34a7eSDavid du Colombier 	int	span;			/* don't span this boundary with dma */
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier 	Pcidev*	pcidev;
2787dd7cddfSDavid du Colombier 	void	(*ienable)(Ctlr*);
2799a747e4fSDavid du Colombier 	void	(*idisable)(Ctlr*);
2807dd7cddfSDavid du Colombier 	SDev*	sdev;
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	Drive*	drive[2];
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier 	Prd*	prdt;			/* physical region descriptor table */
2852f2115d0SDavid du Colombier 	void	(*irqack)(Ctlr*);	/* call to extinguish ICH intrs */
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier 	QLock;				/* current command */
2887dd7cddfSDavid du Colombier 	Drive*	curdrive;
2897dd7cddfSDavid du Colombier 	int	command;		/* last command issued (debugging) */
2907dd7cddfSDavid du Colombier 	Rendez;
2917dd7cddfSDavid du Colombier 	int	done;
2927dd7cddfSDavid du Colombier 
2932f2115d0SDavid du Colombier 	/* interrupt counts */
2942f2115d0SDavid du Colombier 	ulong	intnil;			/* no drive */
2952f2115d0SDavid du Colombier 	ulong	intbusy;		/* controller still busy */
2962f2115d0SDavid du Colombier 	ulong	intok;			/* normal */
2972f2115d0SDavid du Colombier 
2987dd7cddfSDavid du Colombier 	Lock;				/* register access */
2997dd7cddfSDavid du Colombier } Ctlr;
3007dd7cddfSDavid du Colombier 
3017dd7cddfSDavid du Colombier typedef struct Drive {
3027dd7cddfSDavid du Colombier 	Ctlr*	ctlr;
3037dd7cddfSDavid du Colombier 
3047dd7cddfSDavid du Colombier 	int	dev;
3057dd7cddfSDavid du Colombier 	ushort	info[256];
3067dd7cddfSDavid du Colombier 	int	c;			/* cylinder */
3077dd7cddfSDavid du Colombier 	int	h;			/* head */
3087dd7cddfSDavid du Colombier 	int	s;			/* sector */
3095ea8af7bSDavid du Colombier 	vlong	sectors;		/* total */
3107dd7cddfSDavid du Colombier 	int	secsize;		/* sector size */
3117dd7cddfSDavid du Colombier 
3127dd7cddfSDavid du Colombier 	int	dma;			/* DMA R/W possible */
3137dd7cddfSDavid du Colombier 	int	dmactl;
3147dd7cddfSDavid du Colombier 	int	rwm;			/* read/write multiple possible */
3157dd7cddfSDavid du Colombier 	int	rwmctl;
3167dd7cddfSDavid du Colombier 
3177dd7cddfSDavid du Colombier 	int	pkt;			/* PACKET device, length of pktcmd */
3187dd7cddfSDavid du Colombier 	uchar	pktcmd[16];
3197dd7cddfSDavid du Colombier 	int	pktdma;			/* this PACKET command using dma */
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier 	uchar	sense[18];
3227dd7cddfSDavid du Colombier 	uchar	inquiry[48];
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier 	QLock;				/* drive access */
3257dd7cddfSDavid du Colombier 	int	command;		/* current command */
3267dd7cddfSDavid du Colombier 	int	write;
3277dd7cddfSDavid du Colombier 	uchar*	data;
3287dd7cddfSDavid du Colombier 	int	dlen;
3297dd7cddfSDavid du Colombier 	uchar*	limit;
3307dd7cddfSDavid du Colombier 	int	count;			/* sectors */
3317dd7cddfSDavid du Colombier 	int	block;			/* R/W bytes per block */
3327dd7cddfSDavid du Colombier 	int	status;
3337dd7cddfSDavid du Colombier 	int	error;
3346a081dcdSDavid du Colombier 	int	flags;			/* internal flags */
3352f2115d0SDavid du Colombier 
3362f2115d0SDavid du Colombier 	/* interrupt counts */
3372f2115d0SDavid du Colombier 	ulong	intcmd;			/* commands */
3382f2115d0SDavid du Colombier 	ulong	intrd;			/* reads */
3392f2115d0SDavid du Colombier 	ulong	intwr;			/* writes */
3407dd7cddfSDavid du Colombier } Drive;
3417dd7cddfSDavid du Colombier 
34259f4c0c1SDavid du Colombier enum {					/* internal flags */
34359f4c0c1SDavid du Colombier 	Lba48		= 0x1,		/* LBA48 mode */
34459f4c0c1SDavid du Colombier 	Lba48always	= 0x2,		/* ... */
34559f4c0c1SDavid du Colombier };
34691157df7SDavid du Colombier enum {
34791157df7SDavid du Colombier 	Last28		= (1<<28) - 1 - 1, /* all-ones mask is not addressible */
34891157df7SDavid du Colombier };
34959f4c0c1SDavid du Colombier 
3507dd7cddfSDavid du Colombier static void
pc87415ienable(Ctlr * ctlr)3517dd7cddfSDavid du Colombier pc87415ienable(Ctlr* ctlr)
3527dd7cddfSDavid du Colombier {
3537dd7cddfSDavid du Colombier 	Pcidev *p;
3547dd7cddfSDavid du Colombier 	int x;
3557dd7cddfSDavid du Colombier 
3567dd7cddfSDavid du Colombier 	p = ctlr->pcidev;
3577dd7cddfSDavid du Colombier 	if(p == nil)
3587dd7cddfSDavid du Colombier 		return;
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier 	x = pcicfgr32(p, 0x40);
3617dd7cddfSDavid du Colombier 	if(ctlr->cmdport == p->mem[0].bar)
3627dd7cddfSDavid du Colombier 		x &= ~0x00000100;
3637dd7cddfSDavid du Colombier 	else
3647dd7cddfSDavid du Colombier 		x &= ~0x00000200;
3657dd7cddfSDavid du Colombier 	pcicfgw32(p, 0x40, x);
3667dd7cddfSDavid du Colombier }
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier static void
atadumpstate(Drive * drive,uchar * cmd,vlong lba,int count)3696a081dcdSDavid du Colombier atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count)
3707dd7cddfSDavid du Colombier {
3717dd7cddfSDavid du Colombier 	Prd *prd;
3727dd7cddfSDavid du Colombier 	Pcidev *p;
3737dd7cddfSDavid du Colombier 	Ctlr *ctlr;
3747dd7cddfSDavid du Colombier 	int i, bmiba;
3757dd7cddfSDavid du Colombier 
3767dd7cddfSDavid du Colombier 	if(!(DEBUG & DbgSTATE)){
3777dd7cddfSDavid du Colombier 		USED(drive, cmd, lba, count);
3787dd7cddfSDavid du Colombier 		return;
3797dd7cddfSDavid du Colombier 	}
3807dd7cddfSDavid du Colombier 
3817dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
38291157df7SDavid du Colombier 	print("sdata: command %2.2uX\n", ctlr->command);
3837dd7cddfSDavid du Colombier 	print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n",
3847dd7cddfSDavid du Colombier 		drive->data, drive->limit, drive->dlen,
3857dd7cddfSDavid du Colombier 		drive->status, drive->error);
3867dd7cddfSDavid du Colombier 	if(cmd != nil){
3876a081dcdSDavid du Colombier 		print("lba %d -> %lld, count %d -> %d (%d)\n",
3887dd7cddfSDavid du Colombier 			(cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba,
3897dd7cddfSDavid du Colombier 			(cmd[7]<<8)|cmd[8], count, drive->count);
3907dd7cddfSDavid du Colombier 	}
3917dd7cddfSDavid du Colombier 	if(!(inb(ctlr->ctlport+As) & Bsy)){
3927dd7cddfSDavid du Colombier 		for(i = 1; i < 7; i++)
3937dd7cddfSDavid du Colombier 			print(" 0x%2.2uX", inb(ctlr->cmdport+i));
3947dd7cddfSDavid du Colombier 		print(" 0x%2.2uX\n", inb(ctlr->ctlport+As));
3957dd7cddfSDavid du Colombier 	}
3967dd7cddfSDavid du Colombier 	if(drive->command == Cwd || drive->command == Crd){
3977dd7cddfSDavid du Colombier 		bmiba = ctlr->bmiba;
3987dd7cddfSDavid du Colombier 		prd = ctlr->prdt;
3997dd7cddfSDavid du Colombier 		print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n",
4007dd7cddfSDavid du Colombier 			inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd);
4017dd7cddfSDavid du Colombier 		for(;;){
4027dd7cddfSDavid du Colombier 			print("pa 0x%8.8luX count %8.8uX\n",
4037dd7cddfSDavid du Colombier 				prd->pa, prd->count);
4047dd7cddfSDavid du Colombier 			if(prd->count & PrdEOT)
4057dd7cddfSDavid du Colombier 				break;
4067dd7cddfSDavid du Colombier 			prd++;
4077dd7cddfSDavid du Colombier 		}
4087dd7cddfSDavid du Colombier 	}
4097dd7cddfSDavid du Colombier 	if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){
4107dd7cddfSDavid du Colombier 		p = ctlr->pcidev;
4117dd7cddfSDavid du Colombier 		print("0x40: %4.4uX 0x42: %4.4uX",
4127dd7cddfSDavid du Colombier 			pcicfgr16(p, 0x40), pcicfgr16(p, 0x42));
4137dd7cddfSDavid du Colombier 		print("0x48: %2.2uX\n", pcicfgr8(p, 0x48));
4147dd7cddfSDavid du Colombier 		print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A));
4157dd7cddfSDavid du Colombier 	}
4167dd7cddfSDavid du Colombier }
4177dd7cddfSDavid du Colombier 
4187dd7cddfSDavid du Colombier static int
atadebug(int cmdport,int ctlport,char * fmt,...)4197dd7cddfSDavid du Colombier atadebug(int cmdport, int ctlport, char* fmt, ...)
4207dd7cddfSDavid du Colombier {
4217dd7cddfSDavid du Colombier 	int i, n;
4227dd7cddfSDavid du Colombier 	va_list arg;
4237dd7cddfSDavid du Colombier 	char buf[PRINTSIZE];
4247dd7cddfSDavid du Colombier 
4257dd7cddfSDavid du Colombier 	if(!(DEBUG & DbgPROBE)){
4267dd7cddfSDavid du Colombier 		USED(cmdport, ctlport, fmt);
4277dd7cddfSDavid du Colombier 		return 0;
4287dd7cddfSDavid du Colombier 	}
4297dd7cddfSDavid du Colombier 
4307dd7cddfSDavid du Colombier 	va_start(arg, fmt);
4319a747e4fSDavid du Colombier 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
4327dd7cddfSDavid du Colombier 	va_end(arg);
4337dd7cddfSDavid du Colombier 
4347dd7cddfSDavid du Colombier 	if(cmdport){
4357dd7cddfSDavid du Colombier 		if(buf[n-1] == '\n')
4367dd7cddfSDavid du Colombier 			n--;
4377dd7cddfSDavid du Colombier 		n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:",
4387dd7cddfSDavid du Colombier 			cmdport);
4397dd7cddfSDavid du Colombier 		for(i = Features; i < Command; i++)
4407dd7cddfSDavid du Colombier 			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
4417dd7cddfSDavid du Colombier 				inb(cmdport+i));
4427dd7cddfSDavid du Colombier 		if(ctlport)
4437dd7cddfSDavid du Colombier 			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
4447dd7cddfSDavid du Colombier 				inb(ctlport+As));
4457dd7cddfSDavid du Colombier 		n += snprint(buf+n, PRINTSIZE-n, "\n");
4467dd7cddfSDavid du Colombier 	}
4477dd7cddfSDavid du Colombier 	putstrn(buf, n);
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 	return n;
4507dd7cddfSDavid du Colombier }
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier static int
ataready(int cmdport,int ctlport,int dev,int reset,int ready,int micro)4537dd7cddfSDavid du Colombier ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro)
4547dd7cddfSDavid du Colombier {
4557dd7cddfSDavid du Colombier 	int as;
4567dd7cddfSDavid du Colombier 
4577dd7cddfSDavid du Colombier 	atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX",
4587dd7cddfSDavid du Colombier 		dev, reset, ready);
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier 	for(;;){
4617dd7cddfSDavid du Colombier 		/*
4627dd7cddfSDavid du Colombier 		 * Wait for the controller to become not busy and
4637dd7cddfSDavid du Colombier 		 * possibly for a status bit to become true (usually
4647dd7cddfSDavid du Colombier 		 * Drdy). Must change to the appropriate device
4657dd7cddfSDavid du Colombier 		 * register set if necessary before testing for ready.
4667dd7cddfSDavid du Colombier 		 * Always run through the loop at least once so it
4677dd7cddfSDavid du Colombier 		 * can be used as a test for !Bsy.
4687dd7cddfSDavid du Colombier 		 */
4697dd7cddfSDavid du Colombier 		as = inb(ctlport+As);
4709a747e4fSDavid du Colombier 		if(as & reset){
4719a747e4fSDavid du Colombier 			/* nothing to do */
4729a747e4fSDavid du Colombier 		}
4737dd7cddfSDavid du Colombier 		else if(dev){
4747dd7cddfSDavid du Colombier 			outb(cmdport+Dh, dev);
4757dd7cddfSDavid du Colombier 			dev = 0;
4767dd7cddfSDavid du Colombier 		}
4777dd7cddfSDavid du Colombier 		else if(ready == 0 || (as & ready)){
4787dd7cddfSDavid du Colombier 			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
4797dd7cddfSDavid du Colombier 			return as;
4807dd7cddfSDavid du Colombier 		}
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier 		if(micro-- <= 0){
4837dd7cddfSDavid du Colombier 			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
4847dd7cddfSDavid du Colombier 			break;
4857dd7cddfSDavid du Colombier 		}
4867dd7cddfSDavid du Colombier 		microdelay(1);
4877dd7cddfSDavid du Colombier 	}
4887dd7cddfSDavid du Colombier 	atadebug(cmdport, ctlport, "ataready: timeout");
4897dd7cddfSDavid du Colombier 
4907dd7cddfSDavid du Colombier 	return -1;
4917dd7cddfSDavid du Colombier }
4927dd7cddfSDavid du Colombier 
4936a081dcdSDavid du Colombier /*
4947dd7cddfSDavid du Colombier static int
49580ee5cbfSDavid du Colombier atacsf(Drive* drive, vlong csf, int supported)
4967dd7cddfSDavid du Colombier {
49780ee5cbfSDavid du Colombier 	ushort *info;
4987dd7cddfSDavid du Colombier 	int cmdset, i, x;
4997dd7cddfSDavid du Colombier 
50080ee5cbfSDavid du Colombier 	if(supported)
50180ee5cbfSDavid du Colombier 		info = &drive->info[Icsfs];
50280ee5cbfSDavid du Colombier 	else
50380ee5cbfSDavid du Colombier 		info = &drive->info[Icsfe];
50480ee5cbfSDavid du Colombier 
5057dd7cddfSDavid du Colombier 	for(i = 0; i < 3; i++){
5067dd7cddfSDavid du Colombier 		x = (csf>>(16*i)) & 0xFFFF;
5077dd7cddfSDavid du Colombier 		if(x == 0)
5087dd7cddfSDavid du Colombier 			continue;
50980ee5cbfSDavid du Colombier 		cmdset = info[i];
5107dd7cddfSDavid du Colombier 		if(cmdset == 0 || cmdset == 0xFFFF)
5117dd7cddfSDavid du Colombier 			return 0;
5127dd7cddfSDavid du Colombier 		return cmdset & x;
5137dd7cddfSDavid du Colombier 	}
5147dd7cddfSDavid du Colombier 
5157dd7cddfSDavid du Colombier 	return 0;
5167dd7cddfSDavid du Colombier }
5176a081dcdSDavid du Colombier */
5187dd7cddfSDavid du Colombier 
5197dd7cddfSDavid du Colombier static int
atadone(void * arg)52080ee5cbfSDavid du Colombier atadone(void* arg)
5217dd7cddfSDavid du Colombier {
52280ee5cbfSDavid du Colombier 	return ((Ctlr*)arg)->done;
5237dd7cddfSDavid du Colombier }
5247dd7cddfSDavid du Colombier 
5257dd7cddfSDavid du Colombier static int
atarwmmode(Drive * drive,int cmdport,int ctlport,int dev)5267dd7cddfSDavid du Colombier atarwmmode(Drive* drive, int cmdport, int ctlport, int dev)
5277dd7cddfSDavid du Colombier {
5287dd7cddfSDavid du Colombier 	int as, maxrwm, rwm;
5297dd7cddfSDavid du Colombier 
5307dd7cddfSDavid du Colombier 	maxrwm = (drive->info[Imaxrwm] & 0xFF);
5317dd7cddfSDavid du Colombier 	if(maxrwm == 0)
5327dd7cddfSDavid du Colombier 		return 0;
5337dd7cddfSDavid du Colombier 
5347dd7cddfSDavid du Colombier 	/*
5357dd7cddfSDavid du Colombier 	 * Sometimes drives come up with the current count set
5367dd7cddfSDavid du Colombier 	 * to 0; if so, set a suitable value, otherwise believe
5377dd7cddfSDavid du Colombier 	 * the value in Irwm if the 0x100 bit is set.
5387dd7cddfSDavid du Colombier 	 */
5397dd7cddfSDavid du Colombier 	if(drive->info[Irwm] & 0x100)
5407dd7cddfSDavid du Colombier 		rwm = (drive->info[Irwm] & 0xFF);
5417dd7cddfSDavid du Colombier 	else
5427dd7cddfSDavid du Colombier 		rwm = 0;
5437dd7cddfSDavid du Colombier 	if(rwm == 0)
5447dd7cddfSDavid du Colombier 		rwm = maxrwm;
5457dd7cddfSDavid du Colombier 	if(rwm > 16)
5467dd7cddfSDavid du Colombier 		rwm = 16;
5477dd7cddfSDavid du Colombier 	if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0)
5487dd7cddfSDavid du Colombier 		return 0;
5497dd7cddfSDavid du Colombier 	outb(cmdport+Count, rwm);
5507dd7cddfSDavid du Colombier 	outb(cmdport+Command, Csm);
5517dd7cddfSDavid du Colombier 	microdelay(1);
5527dd7cddfSDavid du Colombier 	as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000);
5537dd7cddfSDavid du Colombier 	inb(cmdport+Status);
5547dd7cddfSDavid du Colombier 	if(as < 0 || (as & (Df|Err)))
5557dd7cddfSDavid du Colombier 		return 0;
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	drive->rwm = rwm;
5587dd7cddfSDavid du Colombier 
5597dd7cddfSDavid du Colombier 	return rwm;
5607dd7cddfSDavid du Colombier }
5617dd7cddfSDavid du Colombier 
5627dd7cddfSDavid du Colombier static int
atadmamode(Drive * drive)5637dd7cddfSDavid du Colombier atadmamode(Drive* drive)
5647dd7cddfSDavid du Colombier {
5657dd7cddfSDavid du Colombier 	int dma;
5667dd7cddfSDavid du Colombier 
5677dd7cddfSDavid du Colombier 	/*
5687dd7cddfSDavid du Colombier 	 * Check if any DMA mode enabled.
5697dd7cddfSDavid du Colombier 	 * Assumes the BIOS has picked and enabled the best.
5707dd7cddfSDavid du Colombier 	 * This is completely passive at the moment, no attempt is
5717dd7cddfSDavid du Colombier 	 * made to ensure the hardware is correctly set up.
5727dd7cddfSDavid du Colombier 	 */
5737dd7cddfSDavid du Colombier 	dma = drive->info[Imwdma] & 0x0707;
5747dd7cddfSDavid du Colombier 	drive->dma = (dma>>8) & dma;
5757dd7cddfSDavid du Colombier 	if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){
576503d3e0aSDavid du Colombier 		dma = drive->info[Iudma] & 0x7F7F;
5777dd7cddfSDavid du Colombier 		drive->dma = (dma>>8) & dma;
5787dd7cddfSDavid du Colombier 		if(drive->dma)
5797dd7cddfSDavid du Colombier 			drive->dma |= 'U'<<16;
5807dd7cddfSDavid du Colombier 	}
5817dd7cddfSDavid du Colombier 
5827dd7cddfSDavid du Colombier 	return dma;
5837dd7cddfSDavid du Colombier }
5847dd7cddfSDavid du Colombier 
5857dd7cddfSDavid du Colombier static int
ataidentify(int cmdport,int ctlport,int dev,int pkt,void * info)5867dd7cddfSDavid du Colombier ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info)
5877dd7cddfSDavid du Colombier {
5887dd7cddfSDavid du Colombier 	int as, command, drdy;
5897dd7cddfSDavid du Colombier 
5907dd7cddfSDavid du Colombier 	if(pkt){
5917dd7cddfSDavid du Colombier 		command = Cidpkt;
5927dd7cddfSDavid du Colombier 		drdy = 0;
5937dd7cddfSDavid du Colombier 	}
5947dd7cddfSDavid du Colombier 	else{
5957dd7cddfSDavid du Colombier 		command = Cid;
5967dd7cddfSDavid du Colombier 		drdy = Drdy;
5977dd7cddfSDavid du Colombier 	}
5987dd7cddfSDavid du Colombier 	as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
5997dd7cddfSDavid du Colombier 	if(as < 0)
6007dd7cddfSDavid du Colombier 		return as;
6017dd7cddfSDavid du Colombier 	outb(cmdport+Command, command);
6027dd7cddfSDavid du Colombier 	microdelay(1);
6037dd7cddfSDavid du Colombier 
60459cc4ca5SDavid du Colombier 	as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
6057dd7cddfSDavid du Colombier 	if(as < 0)
6067dd7cddfSDavid du Colombier 		return -1;
6077dd7cddfSDavid du Colombier 	if(as & Err)
6087dd7cddfSDavid du Colombier 		return as;
6097dd7cddfSDavid du Colombier 
6107dd7cddfSDavid du Colombier 	memset(info, 0, 512);
6117dd7cddfSDavid du Colombier 	inss(cmdport+Data, info, 256);
6127dd7cddfSDavid du Colombier 	inb(cmdport+Status);
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier 	if(DEBUG & DbgIDENTIFY){
6157dd7cddfSDavid du Colombier 		int i;
6167dd7cddfSDavid du Colombier 		ushort *sp;
6177dd7cddfSDavid du Colombier 
6187dd7cddfSDavid du Colombier 		sp = (ushort*)info;
6197dd7cddfSDavid du Colombier 		for(i = 0; i < 256; i++){
6207dd7cddfSDavid du Colombier 			if(i && (i%16) == 0)
6217dd7cddfSDavid du Colombier 				print("\n");
6227dd7cddfSDavid du Colombier 			print(" %4.4uX", *sp);
6237dd7cddfSDavid du Colombier 			sp++;
6247dd7cddfSDavid du Colombier 		}
6257dd7cddfSDavid du Colombier 		print("\n");
6267dd7cddfSDavid du Colombier 	}
6277dd7cddfSDavid du Colombier 
6287dd7cddfSDavid du Colombier 	return 0;
6297dd7cddfSDavid du Colombier }
6307dd7cddfSDavid du Colombier 
6317dd7cddfSDavid du Colombier static Drive*
atadrive(int cmdport,int ctlport,int dev)6327dd7cddfSDavid du Colombier atadrive(int cmdport, int ctlport, int dev)
6337dd7cddfSDavid du Colombier {
6347dd7cddfSDavid du Colombier 	Drive *drive;
6357dd7cddfSDavid du Colombier 	int as, i, pkt;
6367dd7cddfSDavid du Colombier 	uchar buf[512], *p;
6379a747e4fSDavid du Colombier 	ushort iconfig, *sp;
6387dd7cddfSDavid du Colombier 
6397dd7cddfSDavid du Colombier 	atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
6407dd7cddfSDavid du Colombier 	pkt = 1;
6417dd7cddfSDavid du Colombier retry:
6427dd7cddfSDavid du Colombier 	as = ataidentify(cmdport, ctlport, dev, pkt, buf);
6437dd7cddfSDavid du Colombier 	if(as < 0)
6447dd7cddfSDavid du Colombier 		return nil;
6457dd7cddfSDavid du Colombier 	if(as & Err){
6467dd7cddfSDavid du Colombier 		if(pkt == 0)
6477dd7cddfSDavid du Colombier 			return nil;
6487dd7cddfSDavid du Colombier 		pkt = 0;
6497dd7cddfSDavid du Colombier 		goto retry;
6507dd7cddfSDavid du Colombier 	}
6517dd7cddfSDavid du Colombier 
6527dd7cddfSDavid du Colombier 	if((drive = malloc(sizeof(Drive))) == nil)
6537dd7cddfSDavid du Colombier 		return nil;
6547dd7cddfSDavid du Colombier 	drive->dev = dev;
6557dd7cddfSDavid du Colombier 	memmove(drive->info, buf, sizeof(drive->info));
6567dd7cddfSDavid du Colombier 	drive->sense[0] = 0x70;
6577dd7cddfSDavid du Colombier 	drive->sense[7] = sizeof(drive->sense)-7;
6587dd7cddfSDavid du Colombier 
6597dd7cddfSDavid du Colombier 	drive->inquiry[2] = 2;
6607dd7cddfSDavid du Colombier 	drive->inquiry[3] = 2;
6617dd7cddfSDavid du Colombier 	drive->inquiry[4] = sizeof(drive->inquiry)-4;
6627dd7cddfSDavid du Colombier 	p = &drive->inquiry[8];
6637dd7cddfSDavid du Colombier 	sp = &drive->info[Imodel];
6647dd7cddfSDavid du Colombier 	for(i = 0; i < 20; i++){
6657dd7cddfSDavid du Colombier 		*p++ = *sp>>8;
6667dd7cddfSDavid du Colombier 		*p++ = *sp++;
6677dd7cddfSDavid du Colombier 	}
6687dd7cddfSDavid du Colombier 
6697dd7cddfSDavid du Colombier 	drive->secsize = 512;
6709a747e4fSDavid du Colombier 
6719a747e4fSDavid du Colombier 	/*
6729a747e4fSDavid du Colombier 	 * Beware the CompactFlash Association feature set.
6739a747e4fSDavid du Colombier 	 * Now, why this value in Iconfig just walks all over the bit
6749a747e4fSDavid du Colombier 	 * definitions used in the other parts of the ATA/ATAPI standards
6759a747e4fSDavid du Colombier 	 * is a mystery and a sign of true stupidity on someone's part.
6769a747e4fSDavid du Colombier 	 * Anyway, the standard says if this value is 0x848A then it's
6779a747e4fSDavid du Colombier 	 * CompactFlash and it's NOT a packet device.
6789a747e4fSDavid du Colombier 	 */
6799a747e4fSDavid du Colombier 	iconfig = drive->info[Iconfig];
6809a747e4fSDavid du Colombier 	if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){
6819a747e4fSDavid du Colombier 		if(iconfig & 0x01)
6827dd7cddfSDavid du Colombier 			drive->pkt = 16;
6837dd7cddfSDavid du Colombier 		else
6847dd7cddfSDavid du Colombier 			drive->pkt = 12;
6857dd7cddfSDavid du Colombier 	}
6867dd7cddfSDavid du Colombier 	else{
6877dd7cddfSDavid du Colombier 		if(drive->info[Ivalid] & 0x0001){
6887dd7cddfSDavid du Colombier 			drive->c = drive->info[Iccyl];
6897dd7cddfSDavid du Colombier 			drive->h = drive->info[Ichead];
6907dd7cddfSDavid du Colombier 			drive->s = drive->info[Icsec];
69159f4c0c1SDavid du Colombier 		}
69259f4c0c1SDavid du Colombier 		else{
6933ff48bf5SDavid du Colombier 			drive->c = drive->info[Ilcyl];
6943ff48bf5SDavid du Colombier 			drive->h = drive->info[Ilhead];
6953ff48bf5SDavid du Colombier 			drive->s = drive->info[Ilsec];
6963ff48bf5SDavid du Colombier 		}
6976a081dcdSDavid du Colombier 		if(drive->info[Icapabilities] & Mlba){
6986a081dcdSDavid du Colombier 			if(drive->info[Icsfs+1] & Maddr48){
6996a081dcdSDavid du Colombier 				drive->sectors = drive->info[Ilba48]
7005ea8af7bSDavid du Colombier 					| (drive->info[Ilba48+1]<<16)
7015ea8af7bSDavid du Colombier 					| ((vlong)drive->info[Ilba48+2]<<32);
7026a081dcdSDavid du Colombier 				drive->flags |= Lba48;
70359f4c0c1SDavid du Colombier 			}
70459f4c0c1SDavid du Colombier 			else{
7056a081dcdSDavid du Colombier 				drive->sectors = (drive->info[Ilba+1]<<16)
7066a081dcdSDavid du Colombier 					 |drive->info[Ilba];
7077dd7cddfSDavid du Colombier 			}
7086a081dcdSDavid du Colombier 			drive->dev |= Lba;
70959f4c0c1SDavid du Colombier 		}
71059f4c0c1SDavid du Colombier 		else
7117dd7cddfSDavid du Colombier 			drive->sectors = drive->c*drive->h*drive->s;
7127dd7cddfSDavid du Colombier 		atarwmmode(drive, cmdport, ctlport, dev);
7137dd7cddfSDavid du Colombier 	}
7147dd7cddfSDavid du Colombier 	atadmamode(drive);
7157dd7cddfSDavid du Colombier 
7167dd7cddfSDavid du Colombier 	if(DEBUG & DbgCONFIG){
71759cc4ca5SDavid du Colombier 		print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
718ca2418feSDavid du Colombier 			dev, cmdport, iconfig, drive->info[Icapabilities]);
71914414594SDavid du Colombier 		print(" mwdma %4.4uX", drive->info[Imwdma]);
7207dd7cddfSDavid du Colombier 		if(drive->info[Ivalid] & 0x04)
7217dd7cddfSDavid du Colombier 			print(" udma %4.4uX", drive->info[Iudma]);
72259f4c0c1SDavid du Colombier 		print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm);
7236a081dcdSDavid du Colombier 		if(drive->flags&Lba48)
72459f4c0c1SDavid du Colombier 			print("\tLLBA sectors %lld", drive->sectors);
72559f4c0c1SDavid du Colombier 		print("\n");
7267dd7cddfSDavid du Colombier 	}
7277dd7cddfSDavid du Colombier 
7287dd7cddfSDavid du Colombier 	return drive;
7297dd7cddfSDavid du Colombier }
7307dd7cddfSDavid du Colombier 
7317dd7cddfSDavid du Colombier static void
atasrst(int ctlport)7327dd7cddfSDavid du Colombier atasrst(int ctlport)
7337dd7cddfSDavid du Colombier {
7347dd7cddfSDavid du Colombier 	/*
7357dd7cddfSDavid du Colombier 	 * Srst is a big stick and may cause problems if further
7367dd7cddfSDavid du Colombier 	 * commands are tried before the drives become ready again.
7377dd7cddfSDavid du Colombier 	 * Also, there will be problems here if overlapped commands
7387dd7cddfSDavid du Colombier 	 * are ever supported.
7397dd7cddfSDavid du Colombier 	 */
7407dd7cddfSDavid du Colombier 	microdelay(5);
7417dd7cddfSDavid du Colombier 	outb(ctlport+Dc, Srst);
7427dd7cddfSDavid du Colombier 	microdelay(5);
7437dd7cddfSDavid du Colombier 	outb(ctlport+Dc, 0);
7447dd7cddfSDavid du Colombier 	microdelay(2*1000);
7457dd7cddfSDavid du Colombier }
7467dd7cddfSDavid du Colombier 
7477dd7cddfSDavid du Colombier static SDev*
ataprobe(int cmdport,int ctlport,int irq)7487dd7cddfSDavid du Colombier ataprobe(int cmdport, int ctlport, int irq)
7497dd7cddfSDavid du Colombier {
7507dd7cddfSDavid du Colombier 	Ctlr* ctlr;
7517dd7cddfSDavid du Colombier 	SDev *sdev;
7527dd7cddfSDavid du Colombier 	Drive *drive;
7537dd7cddfSDavid du Colombier 	int dev, error, rhi, rlo;
7544de34a7eSDavid du Colombier 	static int nonlegacy = 'C';
7557dd7cddfSDavid du Colombier 
756*27522402SDavid du Colombier 	if(cmdport == 0) {
757*27522402SDavid du Colombier 		print("ataprobe: cmdport is 0\n");
758*27522402SDavid du Colombier 		return nil;
759*27522402SDavid du Colombier 	}
7609a747e4fSDavid du Colombier 	if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {
7619a747e4fSDavid du Colombier 		print("ataprobe: Cannot allocate %X\n", cmdport);
7627dd7cddfSDavid du Colombier 		return nil;
7639a747e4fSDavid du Colombier 	}
7647dd7cddfSDavid du Colombier 	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
7659a747e4fSDavid du Colombier 		print("ataprobe: Cannot allocate %X\n", ctlport + As);
7667dd7cddfSDavid du Colombier 		iofree(cmdport);
7677dd7cddfSDavid du Colombier 		return nil;
7687dd7cddfSDavid du Colombier 	}
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier 	/*
7717dd7cddfSDavid du Colombier 	 * Try to detect a floating bus.
7727dd7cddfSDavid du Colombier 	 * Bsy should be cleared. If not, see if the cylinder registers
7737dd7cddfSDavid du Colombier 	 * are read/write capable.
7747dd7cddfSDavid du Colombier 	 * If the master fails, try the slave to catch slave-only
7757dd7cddfSDavid du Colombier 	 * configurations.
7767dd7cddfSDavid du Colombier 	 * There's no need to restore the tested registers as they will
7777dd7cddfSDavid du Colombier 	 * be reset on any detected drives by the Cedd command.
7787dd7cddfSDavid du Colombier 	 * All this indicates is that there is at least one drive on the
7797dd7cddfSDavid du Colombier 	 * controller; when the non-existent drive is selected in a
7807dd7cddfSDavid du Colombier 	 * single-drive configuration the registers of the existing drive
7817dd7cddfSDavid du Colombier 	 * are often seen, only command execution fails.
7827dd7cddfSDavid du Colombier 	 */
7837dd7cddfSDavid du Colombier 	dev = Dev0;
7847dd7cddfSDavid du Colombier 	if(inb(ctlport+As) & Bsy){
7857dd7cddfSDavid du Colombier 		outb(cmdport+Dh, dev);
7867dd7cddfSDavid du Colombier 		microdelay(1);
7877dd7cddfSDavid du Colombier trydev1:
7887dd7cddfSDavid du Colombier 		atadebug(cmdport, ctlport, "ataprobe bsy");
7897dd7cddfSDavid du Colombier 		outb(cmdport+Cyllo, 0xAA);
7907dd7cddfSDavid du Colombier 		outb(cmdport+Cylhi, 0x55);
7917dd7cddfSDavid du Colombier 		outb(cmdport+Sector, 0xFF);
7927dd7cddfSDavid du Colombier 		rlo = inb(cmdport+Cyllo);
7937dd7cddfSDavid du Colombier 		rhi = inb(cmdport+Cylhi);
7947dd7cddfSDavid du Colombier 		if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){
7957dd7cddfSDavid du Colombier 			if(dev == Dev1){
7967dd7cddfSDavid du Colombier release:
7977dd7cddfSDavid du Colombier 				iofree(cmdport);
7987dd7cddfSDavid du Colombier 				iofree(ctlport+As);
7997dd7cddfSDavid du Colombier 				return nil;
8007dd7cddfSDavid du Colombier 			}
8017dd7cddfSDavid du Colombier 			dev = Dev1;
8027dd7cddfSDavid du Colombier 			if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)
8037dd7cddfSDavid du Colombier 				goto trydev1;
8047dd7cddfSDavid du Colombier 		}
8057dd7cddfSDavid du Colombier 	}
8067dd7cddfSDavid du Colombier 
8077dd7cddfSDavid du Colombier 	/*
8087dd7cddfSDavid du Colombier 	 * Disable interrupts on any detected controllers.
8097dd7cddfSDavid du Colombier 	 */
8107dd7cddfSDavid du Colombier 	outb(ctlport+Dc, Nien);
8117dd7cddfSDavid du Colombier tryedd1:
8127dd7cddfSDavid du Colombier 	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){
8137dd7cddfSDavid du Colombier 		/*
8147dd7cddfSDavid du Colombier 		 * There's something there, but it didn't come up clean,
8157dd7cddfSDavid du Colombier 		 * so try hitting it with a big stick. The timing here is
8167dd7cddfSDavid du Colombier 		 * wrong but this is a last-ditch effort and it sometimes
8177dd7cddfSDavid du Colombier 		 * gets some marginal hardware back online.
8187dd7cddfSDavid du Colombier 		 */
8197dd7cddfSDavid du Colombier 		atasrst(ctlport);
8207dd7cddfSDavid du Colombier 		if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0)
8217dd7cddfSDavid du Colombier 			goto release;
8227dd7cddfSDavid du Colombier 	}
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier 	/*
8257dd7cddfSDavid du Colombier 	 * Can only get here if controller is not busy.
8267dd7cddfSDavid du Colombier 	 * If there are drives Bsy will be set within 400nS,
8277dd7cddfSDavid du Colombier 	 * must wait 2mS before testing Status.
8287dd7cddfSDavid du Colombier 	 * Wait for the command to complete (6 seconds max).
8297dd7cddfSDavid du Colombier 	 */
8307dd7cddfSDavid du Colombier 	outb(cmdport+Command, Cedd);
8317dd7cddfSDavid du Colombier 	delay(2);
8327dd7cddfSDavid du Colombier 	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)
8337dd7cddfSDavid du Colombier 		goto release;
8347dd7cddfSDavid du Colombier 
8357dd7cddfSDavid du Colombier 	/*
8367dd7cddfSDavid du Colombier 	 * If bit 0 of the error register is set then the selected drive
8377dd7cddfSDavid du Colombier 	 * exists. This is enough to detect single-drive configurations.
8387dd7cddfSDavid du Colombier 	 * However, if the master exists there is no way short of executing
8397dd7cddfSDavid du Colombier 	 * a command to determine if a slave is present.
8407dd7cddfSDavid du Colombier 	 * It appears possible to get here testing Dev0 although it doesn't
8417dd7cddfSDavid du Colombier 	 * exist and the EDD won't take, so try again with Dev1.
8427dd7cddfSDavid du Colombier 	 */
8437dd7cddfSDavid du Colombier 	error = inb(cmdport+Error);
8447dd7cddfSDavid du Colombier 	atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);
8457dd7cddfSDavid du Colombier 	if((error & ~0x80) != 0x01){
8467dd7cddfSDavid du Colombier 		if(dev == Dev1)
8477dd7cddfSDavid du Colombier 			goto release;
8487dd7cddfSDavid du Colombier 		dev = Dev1;
8497dd7cddfSDavid du Colombier 		goto tryedd1;
8507dd7cddfSDavid du Colombier 	}
8517dd7cddfSDavid du Colombier 
8527dd7cddfSDavid du Colombier 	/*
8537dd7cddfSDavid du Colombier 	 * At least one drive is known to exist, try to
8547dd7cddfSDavid du Colombier 	 * identify it. If that fails, don't bother checking
8557dd7cddfSDavid du Colombier 	 * any further.
8567dd7cddfSDavid du Colombier 	 * If the one drive found is Dev0 and the EDD command
8577dd7cddfSDavid du Colombier 	 * didn't indicate Dev1 doesn't exist, check for it.
8587dd7cddfSDavid du Colombier 	 */
8597dd7cddfSDavid du Colombier 	if((drive = atadrive(cmdport, ctlport, dev)) == nil)
8607dd7cddfSDavid du Colombier 		goto release;
8617dd7cddfSDavid du Colombier 	if((ctlr = malloc(sizeof(Ctlr))) == nil){
8627dd7cddfSDavid du Colombier 		free(drive);
8637dd7cddfSDavid du Colombier 		goto release;
8647dd7cddfSDavid du Colombier 	}
8659a747e4fSDavid du Colombier 	memset(ctlr, 0, sizeof(Ctlr));
8667dd7cddfSDavid du Colombier 	if((sdev = malloc(sizeof(SDev))) == nil){
8677dd7cddfSDavid du Colombier 		free(ctlr);
8687dd7cddfSDavid du Colombier 		free(drive);
8697dd7cddfSDavid du Colombier 		goto release;
8707dd7cddfSDavid du Colombier 	}
8719a747e4fSDavid du Colombier 	memset(sdev, 0, sizeof(SDev));
8727dd7cddfSDavid du Colombier 	drive->ctlr = ctlr;
8737dd7cddfSDavid du Colombier 	if(dev == Dev0){
8747dd7cddfSDavid du Colombier 		ctlr->drive[0] = drive;
8757dd7cddfSDavid du Colombier 		if(!(error & 0x80)){
8767dd7cddfSDavid du Colombier 			/*
8777dd7cddfSDavid du Colombier 			 * Always leave Dh pointing to a valid drive,
8787dd7cddfSDavid du Colombier 			 * otherwise a subsequent call to ataready on
8797dd7cddfSDavid du Colombier 			 * this controller may try to test a bogus Status.
8807dd7cddfSDavid du Colombier 			 * Ataprobe is the only place possibly invalid
8817dd7cddfSDavid du Colombier 			 * drives should be selected.
8827dd7cddfSDavid du Colombier 			 */
8837dd7cddfSDavid du Colombier 			drive = atadrive(cmdport, ctlport, Dev1);
8847dd7cddfSDavid du Colombier 			if(drive != nil){
8857dd7cddfSDavid du Colombier 				drive->ctlr = ctlr;
8867dd7cddfSDavid du Colombier 				ctlr->drive[1] = drive;
8877dd7cddfSDavid du Colombier 			}
8887dd7cddfSDavid du Colombier 			else{
8897dd7cddfSDavid du Colombier 				outb(cmdport+Dh, Dev0);
8907dd7cddfSDavid du Colombier 				microdelay(1);
8917dd7cddfSDavid du Colombier 			}
8927dd7cddfSDavid du Colombier 		}
8937dd7cddfSDavid du Colombier 	}
8947dd7cddfSDavid du Colombier 	else
8957dd7cddfSDavid du Colombier 		ctlr->drive[1] = drive;
8967dd7cddfSDavid du Colombier 
8977dd7cddfSDavid du Colombier 	ctlr->cmdport = cmdport;
8987dd7cddfSDavid du Colombier 	ctlr->ctlport = ctlport;
8997dd7cddfSDavid du Colombier 	ctlr->irq = irq;
9007dd7cddfSDavid du Colombier 	ctlr->tbdf = BUSUNKNOWN;
9017dd7cddfSDavid du Colombier 	ctlr->command = Cedd;		/* debugging */
9027dd7cddfSDavid du Colombier 
9034de34a7eSDavid du Colombier 	switch(cmdport){
9044de34a7eSDavid du Colombier 	default:
9054de34a7eSDavid du Colombier 		sdev->idno = nonlegacy;
9064de34a7eSDavid du Colombier 		break;
9074de34a7eSDavid du Colombier 	case 0x1F0:
9084de34a7eSDavid du Colombier 		sdev->idno = 'C';
9094de34a7eSDavid du Colombier 		nonlegacy = 'E';
9104de34a7eSDavid du Colombier 		break;
9114de34a7eSDavid du Colombier 	case 0x170:
9124de34a7eSDavid du Colombier 		sdev->idno = 'D';
9134de34a7eSDavid du Colombier 		nonlegacy = 'E';
9144de34a7eSDavid du Colombier 		break;
9154de34a7eSDavid du Colombier 	}
9167dd7cddfSDavid du Colombier 	sdev->ifc = &sdataifc;
9177dd7cddfSDavid du Colombier 	sdev->ctlr = ctlr;
9187dd7cddfSDavid du Colombier 	sdev->nunit = 2;
9197dd7cddfSDavid du Colombier 	ctlr->sdev = sdev;
9207dd7cddfSDavid du Colombier 
9217dd7cddfSDavid du Colombier 	return sdev;
9227dd7cddfSDavid du Colombier }
9237dd7cddfSDavid du Colombier 
9249a747e4fSDavid du Colombier static void
ataclear(SDev * sdev)9259a747e4fSDavid du Colombier ataclear(SDev *sdev)
9269a747e4fSDavid du Colombier {
9279a747e4fSDavid du Colombier 	Ctlr* ctlr;
9289a747e4fSDavid du Colombier 
9299a747e4fSDavid du Colombier 	ctlr = sdev->ctlr;
9309a747e4fSDavid du Colombier 	iofree(ctlr->cmdport);
9319a747e4fSDavid du Colombier 	iofree(ctlr->ctlport + As);
9329a747e4fSDavid du Colombier 
9339a747e4fSDavid du Colombier 	if (ctlr->drive[0])
9349a747e4fSDavid du Colombier 		free(ctlr->drive[0]);
9359a747e4fSDavid du Colombier 	if (ctlr->drive[1])
9369a747e4fSDavid du Colombier 		free(ctlr->drive[1]);
9379a747e4fSDavid du Colombier 	if (sdev->name)
9389a747e4fSDavid du Colombier 		free(sdev->name);
9399a747e4fSDavid du Colombier 	if (sdev->unitflg)
9409a747e4fSDavid du Colombier 		free(sdev->unitflg);
9419a747e4fSDavid du Colombier 	if (sdev->unit)
9429a747e4fSDavid du Colombier 		free(sdev->unit);
9439a747e4fSDavid du Colombier 	free(ctlr);
9449a747e4fSDavid du Colombier 	free(sdev);
9459a747e4fSDavid du Colombier }
9469a747e4fSDavid du Colombier 
9479a747e4fSDavid du Colombier static char *
atastat(SDev * sdev,char * p,char * e)9489a747e4fSDavid du Colombier atastat(SDev *sdev, char *p, char *e)
9499a747e4fSDavid du Colombier {
9509a747e4fSDavid du Colombier 	Ctlr *ctlr = sdev->ctlr;
9519a747e4fSDavid du Colombier 
9522f2115d0SDavid du Colombier 	return seprint(p, e, "%s ata port %X ctl %X irq %d "
9532f2115d0SDavid du Colombier 		"intr-ok %lud intr-busy %lud intr-nil-drive %lud\n",
9542f2115d0SDavid du Colombier 		sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq,
9552f2115d0SDavid du Colombier 		ctlr->intok, ctlr->intbusy, ctlr->intnil);
9569a747e4fSDavid du Colombier }
9579a747e4fSDavid du Colombier 
9589a747e4fSDavid du Colombier static SDev*
ataprobew(DevConf * cf)9599a747e4fSDavid du Colombier ataprobew(DevConf *cf)
9609a747e4fSDavid du Colombier {
9614de34a7eSDavid du Colombier 	char *p;
9624de34a7eSDavid du Colombier 	ISAConf isa;
9634de34a7eSDavid du Colombier 
9649a747e4fSDavid du Colombier 	if (cf->nports != 2)
9659a747e4fSDavid du Colombier 		error(Ebadarg);
9669a747e4fSDavid du Colombier 
9674de34a7eSDavid du Colombier 	memset(&isa, 0, sizeof isa);
9684de34a7eSDavid du Colombier 	isa.port = cf->ports[0].port;
9694de34a7eSDavid du Colombier 	isa.irq = cf->intnum;
9704de34a7eSDavid du Colombier 	if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0)
9714de34a7eSDavid du Colombier 		error("cannot find controller");
9724de34a7eSDavid du Colombier 
9733ff48bf5SDavid du Colombier 	return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);
9749a747e4fSDavid du Colombier }
9759a747e4fSDavid du Colombier 
9764de34a7eSDavid du Colombier /*
9774de34a7eSDavid du Colombier  * These are duplicated with sdsetsense, etc., in devsd.c, but
9784de34a7eSDavid du Colombier  * those assume that the disk is not SCSI while in fact here
9794de34a7eSDavid du Colombier  * ata drives are not SCSI but ATAPI ones kind of are.
9804de34a7eSDavid du Colombier  */
9817dd7cddfSDavid du Colombier static int
atasetsense(Drive * drive,int status,int key,int asc,int ascq)9827dd7cddfSDavid du Colombier atasetsense(Drive* drive, int status, int key, int asc, int ascq)
9837dd7cddfSDavid du Colombier {
9847dd7cddfSDavid du Colombier 	drive->sense[2] = key;
9857dd7cddfSDavid du Colombier 	drive->sense[12] = asc;
9867dd7cddfSDavid du Colombier 	drive->sense[13] = ascq;
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier 	return status;
9897dd7cddfSDavid du Colombier }
9907dd7cddfSDavid du Colombier 
9917dd7cddfSDavid du Colombier static int
atamodesense(Drive * drive,uchar * cmd)9927dd7cddfSDavid du Colombier atamodesense(Drive* drive, uchar* cmd)
9937dd7cddfSDavid du Colombier {
9947dd7cddfSDavid du Colombier 	int len;
9957dd7cddfSDavid du Colombier 
9967dd7cddfSDavid du Colombier 	/*
9977dd7cddfSDavid du Colombier 	 * Fake a vendor-specific request with page code 0,
9987dd7cddfSDavid du Colombier 	 * return the drive info.
9997dd7cddfSDavid du Colombier 	 */
10007dd7cddfSDavid du Colombier 	if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
10017dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
10027dd7cddfSDavid du Colombier 	len = (cmd[7]<<8)|cmd[8];
10037dd7cddfSDavid du Colombier 	if(len == 0)
10047dd7cddfSDavid du Colombier 		return SDok;
10057dd7cddfSDavid du Colombier 	if(len < 8+sizeof(drive->info))
10067dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);
10077dd7cddfSDavid du Colombier 	if(drive->data == nil || drive->dlen < len)
10087dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
10097dd7cddfSDavid du Colombier 	memset(drive->data, 0, 8);
10107dd7cddfSDavid du Colombier 	drive->data[0] = sizeof(drive->info)>>8;
10117dd7cddfSDavid du Colombier 	drive->data[1] = sizeof(drive->info);
10127dd7cddfSDavid du Colombier 	memmove(drive->data+8, drive->info, sizeof(drive->info));
10137dd7cddfSDavid du Colombier 	drive->data += 8+sizeof(drive->info);
10147dd7cddfSDavid du Colombier 
10157dd7cddfSDavid du Colombier 	return SDok;
10167dd7cddfSDavid du Colombier }
10177dd7cddfSDavid du Colombier 
10184de34a7eSDavid du Colombier static int
atastandby(Drive * drive,int period)10194de34a7eSDavid du Colombier atastandby(Drive* drive, int period)
10204de34a7eSDavid du Colombier {
10214de34a7eSDavid du Colombier 	Ctlr* ctlr;
10224de34a7eSDavid du Colombier 	int cmdport, done;
10234de34a7eSDavid du Colombier 
10244de34a7eSDavid du Colombier 	ctlr = drive->ctlr;
10254de34a7eSDavid du Colombier 	drive->command = Cstandby;
10264de34a7eSDavid du Colombier 	qlock(ctlr);
10274de34a7eSDavid du Colombier 
10284de34a7eSDavid du Colombier 	cmdport = ctlr->cmdport;
10294de34a7eSDavid du Colombier 	ilock(ctlr);
10304de34a7eSDavid du Colombier 	outb(cmdport+Count, period);
10314de34a7eSDavid du Colombier 	outb(cmdport+Dh, drive->dev);
10324de34a7eSDavid du Colombier 	ctlr->done = 0;
10334de34a7eSDavid du Colombier 	ctlr->curdrive = drive;
10344de34a7eSDavid du Colombier 	ctlr->command = Cstandby;	/* debugging */
10354de34a7eSDavid du Colombier 	outb(cmdport+Command, Cstandby);
10364de34a7eSDavid du Colombier 	iunlock(ctlr);
10374de34a7eSDavid du Colombier 
10384de34a7eSDavid du Colombier 	while(waserror())
10394de34a7eSDavid du Colombier 		;
10404de34a7eSDavid du Colombier 	tsleep(ctlr, atadone, ctlr, 60*1000);
10414de34a7eSDavid du Colombier 	poperror();
10424de34a7eSDavid du Colombier 
10434de34a7eSDavid du Colombier 	done = ctlr->done;
10444de34a7eSDavid du Colombier 	qunlock(ctlr);
10454de34a7eSDavid du Colombier 
10464de34a7eSDavid du Colombier 	if(!done || (drive->status & Err))
10474de34a7eSDavid du Colombier 		return atasetsense(drive, SDcheck, 4, 8, drive->error);
10484de34a7eSDavid du Colombier 	return SDok;
10494de34a7eSDavid du Colombier }
10504de34a7eSDavid du Colombier 
10517dd7cddfSDavid du Colombier static void
atanop(Drive * drive,int subcommand)10527dd7cddfSDavid du Colombier atanop(Drive* drive, int subcommand)
10537dd7cddfSDavid du Colombier {
10547dd7cddfSDavid du Colombier 	Ctlr* ctlr;
10557dd7cddfSDavid du Colombier 	int as, cmdport, ctlport, timeo;
10567dd7cddfSDavid du Colombier 
10577dd7cddfSDavid du Colombier 	/*
10587dd7cddfSDavid du Colombier 	 * Attempt to abort a command by using NOP.
10597dd7cddfSDavid du Colombier 	 * In response, the drive is supposed to set Abrt
10607dd7cddfSDavid du Colombier 	 * in the Error register, set (Drdy|Err) in Status
10617dd7cddfSDavid du Colombier 	 * and clear Bsy when done. However, some drives
10627dd7cddfSDavid du Colombier 	 * (e.g. ATAPI Zip) just go Bsy then clear Status
10637dd7cddfSDavid du Colombier 	 * when done, hence the timeout loop only on Bsy
10647dd7cddfSDavid du Colombier 	 * and the forced setting of drive->error.
10657dd7cddfSDavid du Colombier 	 */
10667dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
10677dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
10687dd7cddfSDavid du Colombier 	outb(cmdport+Features, subcommand);
10697dd7cddfSDavid du Colombier 	outb(cmdport+Dh, drive->dev);
10707dd7cddfSDavid du Colombier 	ctlr->command = Cnop;		/* debugging */
10717dd7cddfSDavid du Colombier 	outb(cmdport+Command, Cnop);
10727dd7cddfSDavid du Colombier 
10737dd7cddfSDavid du Colombier 	microdelay(1);
10747dd7cddfSDavid du Colombier 	ctlport = ctlr->ctlport;
10757dd7cddfSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
10767dd7cddfSDavid du Colombier 		as = inb(ctlport+As);
10777dd7cddfSDavid du Colombier 		if(!(as & Bsy))
10787dd7cddfSDavid du Colombier 			break;
10797dd7cddfSDavid du Colombier 		microdelay(1);
10807dd7cddfSDavid du Colombier 	}
10817dd7cddfSDavid du Colombier 	drive->error |= Abrt;
10827dd7cddfSDavid du Colombier }
10837dd7cddfSDavid du Colombier 
10847dd7cddfSDavid du Colombier static void
ataabort(Drive * drive,int dolock)10857dd7cddfSDavid du Colombier ataabort(Drive* drive, int dolock)
10867dd7cddfSDavid du Colombier {
10877dd7cddfSDavid du Colombier 	/*
10887dd7cddfSDavid du Colombier 	 * If NOP is available (packet commands) use it otherwise
10897dd7cddfSDavid du Colombier 	 * must try a software reset.
10907dd7cddfSDavid du Colombier 	 */
10917dd7cddfSDavid du Colombier 	if(dolock)
10927dd7cddfSDavid du Colombier 		ilock(drive->ctlr);
10936a081dcdSDavid du Colombier 	if(drive->info[Icsfs] & Mnop)
10947dd7cddfSDavid du Colombier 		atanop(drive, 0);
10957dd7cddfSDavid du Colombier 	else{
10967dd7cddfSDavid du Colombier 		atasrst(drive->ctlr->ctlport);
10977dd7cddfSDavid du Colombier 		drive->error |= Abrt;
10987dd7cddfSDavid du Colombier 	}
10997dd7cddfSDavid du Colombier 	if(dolock)
11007dd7cddfSDavid du Colombier 		iunlock(drive->ctlr);
11017dd7cddfSDavid du Colombier }
11027dd7cddfSDavid du Colombier 
11037dd7cddfSDavid du Colombier static int
atadmasetup(Drive * drive,int len)11047dd7cddfSDavid du Colombier atadmasetup(Drive* drive, int len)
11057dd7cddfSDavid du Colombier {
11067dd7cddfSDavid du Colombier 	Prd *prd;
11077dd7cddfSDavid du Colombier 	ulong pa;
11087dd7cddfSDavid du Colombier 	Ctlr *ctlr;
11094de34a7eSDavid du Colombier 	int bmiba, bmisx, count, i, span;
11107dd7cddfSDavid du Colombier 
11114de34a7eSDavid du Colombier 	ctlr = drive->ctlr;
11127dd7cddfSDavid du Colombier 	pa = PCIWADDR(drive->data);
11137dd7cddfSDavid du Colombier 	if(pa & 0x03)
11147dd7cddfSDavid du Colombier 		return -1;
11157dd7cddfSDavid du Colombier 
11167dd7cddfSDavid du Colombier 	/*
11177dd7cddfSDavid du Colombier 	 * Sometimes drives identify themselves as being DMA capable
11187dd7cddfSDavid du Colombier 	 * although they are not on a busmastering controller.
11197dd7cddfSDavid du Colombier 	 */
11204de34a7eSDavid du Colombier 	prd = ctlr->prdt;
11217dd7cddfSDavid du Colombier 	if(prd == nil){
11227dd7cddfSDavid du Colombier 		drive->dmactl = 0;
11236de6ce84SDavid du Colombier 		print("disabling dma: not on a busmastering controller\n");
11247dd7cddfSDavid du Colombier 		return -1;
11257dd7cddfSDavid du Colombier 	}
11267dd7cddfSDavid du Colombier 
11274de34a7eSDavid du Colombier 	for(i = 0; len && i < Nprd; i++){
11287dd7cddfSDavid du Colombier 		prd->pa = pa;
11294de34a7eSDavid du Colombier 		span = ROUNDUP(pa, ctlr->span);
11304de34a7eSDavid du Colombier 		if(span == pa)
11314de34a7eSDavid du Colombier 			span += ctlr->span;
11324de34a7eSDavid du Colombier 		count = span - pa;
11337dd7cddfSDavid du Colombier 		if(count >= len){
11344de34a7eSDavid du Colombier 			prd->count = PrdEOT|len;
11357dd7cddfSDavid du Colombier 			break;
11367dd7cddfSDavid du Colombier 		}
11377dd7cddfSDavid du Colombier 		prd->count = count;
11387dd7cddfSDavid du Colombier 		len -= count;
11397dd7cddfSDavid du Colombier 		pa += count;
11407dd7cddfSDavid du Colombier 		prd++;
11417dd7cddfSDavid du Colombier 	}
11424de34a7eSDavid du Colombier 	if(i == Nprd)
11434de34a7eSDavid du Colombier 		(prd-1)->count |= PrdEOT;
11447dd7cddfSDavid du Colombier 
11457dd7cddfSDavid du Colombier 	bmiba = ctlr->bmiba;
11467dd7cddfSDavid du Colombier 	outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt));
11477dd7cddfSDavid du Colombier 	if(drive->write)
11487dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, 0);
11497dd7cddfSDavid du Colombier 	else
11507dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, Rwcon);
11517dd7cddfSDavid du Colombier 	bmisx = inb(bmiba+Bmisx);
11527dd7cddfSDavid du Colombier 	outb(bmiba+Bmisx, bmisx|Ideints|Idedmae);
11537dd7cddfSDavid du Colombier 
11547dd7cddfSDavid du Colombier 	return 0;
11557dd7cddfSDavid du Colombier }
11567dd7cddfSDavid du Colombier 
11577dd7cddfSDavid du Colombier static void
atadmastart(Ctlr * ctlr,int write)11587dd7cddfSDavid du Colombier atadmastart(Ctlr* ctlr, int write)
11597dd7cddfSDavid du Colombier {
11607dd7cddfSDavid du Colombier 	if(write)
11617dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, Ssbm);
11627dd7cddfSDavid du Colombier 	else
11637dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm);
11647dd7cddfSDavid du Colombier }
11657dd7cddfSDavid du Colombier 
11667dd7cddfSDavid du Colombier static int
atadmastop(Ctlr * ctlr)11677dd7cddfSDavid du Colombier atadmastop(Ctlr* ctlr)
11687dd7cddfSDavid du Colombier {
11697dd7cddfSDavid du Colombier 	int bmiba;
11707dd7cddfSDavid du Colombier 
11717dd7cddfSDavid du Colombier 	bmiba = ctlr->bmiba;
11727dd7cddfSDavid du Colombier 	outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);
11737dd7cddfSDavid du Colombier 
11747dd7cddfSDavid du Colombier 	return inb(bmiba+Bmisx);
11757dd7cddfSDavid du Colombier }
11767dd7cddfSDavid du Colombier 
11777dd7cddfSDavid du Colombier static void
atadmainterrupt(Drive * drive,int count)11787dd7cddfSDavid du Colombier atadmainterrupt(Drive* drive, int count)
11797dd7cddfSDavid du Colombier {
11807dd7cddfSDavid du Colombier 	Ctlr* ctlr;
11817dd7cddfSDavid du Colombier 	int bmiba, bmisx;
11827dd7cddfSDavid du Colombier 
11837dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
11847dd7cddfSDavid du Colombier 	bmiba = ctlr->bmiba;
11857dd7cddfSDavid du Colombier 	bmisx = inb(bmiba+Bmisx);
11867dd7cddfSDavid du Colombier 	switch(bmisx & (Ideints|Idedmae|Bmidea)){
11877dd7cddfSDavid du Colombier 	case Bmidea:
11887dd7cddfSDavid du Colombier 		/*
11897dd7cddfSDavid du Colombier 		 * Data transfer still in progress, nothing to do
11907dd7cddfSDavid du Colombier 		 * (this should never happen).
11917dd7cddfSDavid du Colombier 		 */
11927dd7cddfSDavid du Colombier 		return;
11937dd7cddfSDavid du Colombier 
11947dd7cddfSDavid du Colombier 	case Ideints:
11957dd7cddfSDavid du Colombier 	case Ideints|Bmidea:
11967dd7cddfSDavid du Colombier 		/*
11977dd7cddfSDavid du Colombier 		 * Normal termination, tidy up.
11987dd7cddfSDavid du Colombier 		 */
11997dd7cddfSDavid du Colombier 		drive->data += count;
12007dd7cddfSDavid du Colombier 		break;
12017dd7cddfSDavid du Colombier 
12027dd7cddfSDavid du Colombier 	default:
12037dd7cddfSDavid du Colombier 		/*
12047dd7cddfSDavid du Colombier 		 * What's left are error conditions (memory transfer
12057dd7cddfSDavid du Colombier 		 * problem) and the device is not done but the PRD is
12067dd7cddfSDavid du Colombier 		 * exhausted. For both cases must somehow tell the
12077dd7cddfSDavid du Colombier 		 * drive to abort.
12087dd7cddfSDavid du Colombier 		 */
12097dd7cddfSDavid du Colombier 		ataabort(drive, 0);
12107dd7cddfSDavid du Colombier 		break;
12117dd7cddfSDavid du Colombier 	}
12127dd7cddfSDavid du Colombier 	atadmastop(ctlr);
12137dd7cddfSDavid du Colombier 	ctlr->done = 1;
12147dd7cddfSDavid du Colombier }
12157dd7cddfSDavid du Colombier 
12167dd7cddfSDavid du Colombier static void
atapktinterrupt(Drive * drive)12177dd7cddfSDavid du Colombier atapktinterrupt(Drive* drive)
12187dd7cddfSDavid du Colombier {
12197dd7cddfSDavid du Colombier 	Ctlr* ctlr;
12202f2115d0SDavid du Colombier 	int cmdport, len, sts;
12217dd7cddfSDavid du Colombier 
12227dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
12237dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
12242f2115d0SDavid du Colombier 	sts = inb(cmdport+Ir) & (/*Rel|*/ Io|Cd);
12252f2115d0SDavid du Colombier 	/* a default case is impossible since all cases are enumerated */
12262f2115d0SDavid du Colombier 	switch(sts){
12272f2115d0SDavid du Colombier 	case Cd:			/* write cmd */
12287dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->pktcmd, drive->pkt/2);
12297dd7cddfSDavid du Colombier 		break;
12307dd7cddfSDavid du Colombier 
12312f2115d0SDavid du Colombier 	case 0:				/* write data */
12327dd7cddfSDavid du Colombier 		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
12337dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit){
12347dd7cddfSDavid du Colombier 			atanop(drive, 0);
12357dd7cddfSDavid du Colombier 			break;
12367dd7cddfSDavid du Colombier 		}
12377dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->data, len/2);
12387dd7cddfSDavid du Colombier 		drive->data += len;
12397dd7cddfSDavid du Colombier 		break;
12407dd7cddfSDavid du Colombier 
12412f2115d0SDavid du Colombier 	case Io:			/* read data */
12427dd7cddfSDavid du Colombier 		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
12437dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit){
12447dd7cddfSDavid du Colombier 			atanop(drive, 0);
12457dd7cddfSDavid du Colombier 			break;
12467dd7cddfSDavid du Colombier 		}
12477dd7cddfSDavid du Colombier 		inss(cmdport+Data, drive->data, len/2);
12487dd7cddfSDavid du Colombier 		drive->data += len;
12497dd7cddfSDavid du Colombier 		break;
12507dd7cddfSDavid du Colombier 
12512f2115d0SDavid du Colombier 	case Io|Cd:			/* read cmd */
12527dd7cddfSDavid du Colombier 		if(drive->pktdma)
12537dd7cddfSDavid du Colombier 			atadmainterrupt(drive, drive->dlen);
12547dd7cddfSDavid du Colombier 		else
12557dd7cddfSDavid du Colombier 			ctlr->done = 1;
12567dd7cddfSDavid du Colombier 		break;
12577dd7cddfSDavid du Colombier 	}
12582f2115d0SDavid du Colombier 	if(sts & Cd)
12592f2115d0SDavid du Colombier 		drive->intcmd++;
12602f2115d0SDavid du Colombier 	if(sts & Io)
12612f2115d0SDavid du Colombier 		drive->intrd++;
12622f2115d0SDavid du Colombier 	else
12632f2115d0SDavid du Colombier 		drive->intwr++;
12647dd7cddfSDavid du Colombier }
12657dd7cddfSDavid du Colombier 
12667dd7cddfSDavid du Colombier static int
atapktio(Drive * drive,uchar * cmd,int clen)12677dd7cddfSDavid du Colombier atapktio(Drive* drive, uchar* cmd, int clen)
12687dd7cddfSDavid du Colombier {
12697dd7cddfSDavid du Colombier 	Ctlr *ctlr;
12707dd7cddfSDavid du Colombier 	int as, cmdport, ctlport, len, r, timeo;
12717dd7cddfSDavid du Colombier 
12727dd7cddfSDavid du Colombier 	if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)
12737dd7cddfSDavid du Colombier 		return atamodesense(drive, cmd);
12747dd7cddfSDavid du Colombier 
12757dd7cddfSDavid du Colombier 	r = SDok;
12767dd7cddfSDavid du Colombier 
12777dd7cddfSDavid du Colombier 	drive->command = Cpkt;
12787dd7cddfSDavid du Colombier 	memmove(drive->pktcmd, cmd, clen);
12797dd7cddfSDavid du Colombier 	memset(drive->pktcmd+clen, 0, drive->pkt-clen);
12807dd7cddfSDavid du Colombier 	drive->limit = drive->data+drive->dlen;
12817dd7cddfSDavid du Colombier 
12827dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
12837dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
12847dd7cddfSDavid du Colombier 	ctlport = ctlr->ctlport;
12857dd7cddfSDavid du Colombier 
12867dd7cddfSDavid du Colombier 	qlock(ctlr);
12877dd7cddfSDavid du Colombier 
1288b8a11165SDavid du Colombier 	as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, Drdy, 107*1000);
128932078d07SDavid du Colombier 	/* used to test as&Chk as failure too, but some CD readers use that for media change */
129032078d07SDavid du Colombier 	if(as < 0){
12917dd7cddfSDavid du Colombier 		qunlock(ctlr);
12927dd7cddfSDavid du Colombier 		return -1;
12937dd7cddfSDavid du Colombier 	}
12947dd7cddfSDavid du Colombier 
12957dd7cddfSDavid du Colombier 	ilock(ctlr);
12967dd7cddfSDavid du Colombier 	if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))
12977dd7cddfSDavid du Colombier 		drive->pktdma = Dma;
12987dd7cddfSDavid du Colombier 	else
12997dd7cddfSDavid du Colombier 		drive->pktdma = 0;
13007dd7cddfSDavid du Colombier 
13017dd7cddfSDavid du Colombier 	outb(cmdport+Features, drive->pktdma);
13027dd7cddfSDavid du Colombier 	outb(cmdport+Count, 0);
13037dd7cddfSDavid du Colombier 	outb(cmdport+Sector, 0);
13047dd7cddfSDavid du Colombier 	len = 16*drive->secsize;
13057dd7cddfSDavid du Colombier 	outb(cmdport+Bytelo, len);
13067dd7cddfSDavid du Colombier 	outb(cmdport+Bytehi, len>>8);
13077dd7cddfSDavid du Colombier 	outb(cmdport+Dh, drive->dev);
13087dd7cddfSDavid du Colombier 	ctlr->done = 0;
13097dd7cddfSDavid du Colombier 	ctlr->curdrive = drive;
13107dd7cddfSDavid du Colombier 	ctlr->command = Cpkt;		/* debugging */
13117dd7cddfSDavid du Colombier 	if(drive->pktdma)
13127dd7cddfSDavid du Colombier 		atadmastart(ctlr, drive->write);
13137dd7cddfSDavid du Colombier 	outb(cmdport+Command, Cpkt);
13147dd7cddfSDavid du Colombier 
13156a081dcdSDavid du Colombier 	if((drive->info[Iconfig] & Mdrq) != 0x0020){
13167dd7cddfSDavid du Colombier 		microdelay(1);
13177dd7cddfSDavid du Colombier 		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
1318a22b0629SDavid du Colombier 		if(as < 0 || (as & (Bsy|Chk))){
1319a22b0629SDavid du Colombier 			drive->status = as<0 ? 0 : as;
1320a22b0629SDavid du Colombier 			ctlr->curdrive = nil;
1321a22b0629SDavid du Colombier 			ctlr->done = 1;
13227dd7cddfSDavid du Colombier 			r = SDtimeout;
1323a22b0629SDavid du Colombier 		}else
13247dd7cddfSDavid du Colombier 			atapktinterrupt(drive);
13257dd7cddfSDavid du Colombier 	}
13267dd7cddfSDavid du Colombier 	iunlock(ctlr);
13277dd7cddfSDavid du Colombier 
13287dd7cddfSDavid du Colombier 	while(waserror())
13297dd7cddfSDavid du Colombier 		;
13307dd7cddfSDavid du Colombier 	if(!drive->pktdma)
133180ee5cbfSDavid du Colombier 		sleep(ctlr, atadone, ctlr);
13327dd7cddfSDavid du Colombier 	else for(timeo = 0; !ctlr->done; timeo++){
133380ee5cbfSDavid du Colombier 		tsleep(ctlr, atadone, ctlr, 1000);
13347dd7cddfSDavid du Colombier 		if(ctlr->done)
13357dd7cddfSDavid du Colombier 			break;
13367dd7cddfSDavid du Colombier 		ilock(ctlr);
13377dd7cddfSDavid du Colombier 		atadmainterrupt(drive, 0);
13384de34a7eSDavid du Colombier 		if(!drive->error && timeo > 20){
13397dd7cddfSDavid du Colombier 			ataabort(drive, 0);
13407dd7cddfSDavid du Colombier 			atadmastop(ctlr);
13417dd7cddfSDavid du Colombier 			drive->dmactl = 0;
13427dd7cddfSDavid du Colombier 			drive->error |= Abrt;
13437dd7cddfSDavid du Colombier 		}
13447dd7cddfSDavid du Colombier 		if(drive->error){
13457dd7cddfSDavid du Colombier 			drive->status |= Chk;
13467dd7cddfSDavid du Colombier 			ctlr->curdrive = nil;
13477dd7cddfSDavid du Colombier 		}
13487dd7cddfSDavid du Colombier 		iunlock(ctlr);
13497dd7cddfSDavid du Colombier 	}
13507dd7cddfSDavid du Colombier 	poperror();
13517dd7cddfSDavid du Colombier 
13527dd7cddfSDavid du Colombier 	qunlock(ctlr);
13537dd7cddfSDavid du Colombier 
13547dd7cddfSDavid du Colombier 	if(drive->status & Chk)
13557dd7cddfSDavid du Colombier 		r = SDcheck;
13567dd7cddfSDavid du Colombier 
13577dd7cddfSDavid du Colombier 	return r;
13587dd7cddfSDavid du Colombier }
13597dd7cddfSDavid du Colombier 
13606a081dcdSDavid du Colombier static uchar cmd48[256] = {
13616a081dcdSDavid du Colombier 	[Crs]	Crs48,
13626a081dcdSDavid du Colombier 	[Crd]	Crd48,
13636a081dcdSDavid du Colombier 	[Crdq]	Crdq48,
13646a081dcdSDavid du Colombier 	[Crsm]	Crsm48,
13656a081dcdSDavid du Colombier 	[Cws]	Cws48,
13666a081dcdSDavid du Colombier 	[Cwd]	Cwd48,
13676a081dcdSDavid du Colombier 	[Cwdq]	Cwdq48,
13686a081dcdSDavid du Colombier 	[Cwsm]	Cwsm48,
13696a081dcdSDavid du Colombier };
13706a081dcdSDavid du Colombier 
13717dd7cddfSDavid du Colombier static int
atageniostart(Drive * drive,uvlong lba)137291157df7SDavid du Colombier atageniostart(Drive* drive, uvlong lba)
13737dd7cddfSDavid du Colombier {
13747dd7cddfSDavid du Colombier 	Ctlr *ctlr;
13756a081dcdSDavid du Colombier 	uchar cmd;
13765ea8af7bSDavid du Colombier 	int as, c, cmdport, ctlport, h, len, s, use48;
13777dd7cddfSDavid du Colombier 
13785ea8af7bSDavid du Colombier 	use48 = 0;
137991157df7SDavid du Colombier 	if((drive->flags&Lba48always) || lba > Last28 || drive->count > 256){
13806a081dcdSDavid du Colombier 		if(!(drive->flags & Lba48))
13815ea8af7bSDavid du Colombier 			return -1;
13825ea8af7bSDavid du Colombier 		use48 = 1;
13835ea8af7bSDavid du Colombier 		c = h = s = 0;
138459f4c0c1SDavid du Colombier 	}
138559f4c0c1SDavid du Colombier 	else if(drive->dev & Lba){
13867dd7cddfSDavid du Colombier 		c = (lba>>8) & 0xFFFF;
13877dd7cddfSDavid du Colombier 		h = (lba>>24) & 0x0F;
13887dd7cddfSDavid du Colombier 		s = lba & 0xFF;
138959f4c0c1SDavid du Colombier 	}
139059f4c0c1SDavid du Colombier 	else{
13917dd7cddfSDavid du Colombier 		c = lba/(drive->s*drive->h);
13927dd7cddfSDavid du Colombier 		h = ((lba/drive->s) % drive->h);
13937dd7cddfSDavid du Colombier 		s = (lba % drive->s) + 1;
13947dd7cddfSDavid du Colombier 	}
13957dd7cddfSDavid du Colombier 
13967dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
13977dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
13987dd7cddfSDavid du Colombier 	ctlport = ctlr->ctlport;
1399b8a11165SDavid du Colombier 	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, Drdy, 101*1000) < 0)
14007dd7cddfSDavid du Colombier 		return -1;
14017dd7cddfSDavid du Colombier 
14027dd7cddfSDavid du Colombier 	ilock(ctlr);
14037dd7cddfSDavid du Colombier 	if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){
14047dd7cddfSDavid du Colombier 		if(drive->write)
14057dd7cddfSDavid du Colombier 			drive->command = Cwd;
14067dd7cddfSDavid du Colombier 		else
14077dd7cddfSDavid du Colombier 			drive->command = Crd;
14087dd7cddfSDavid du Colombier 	}
14097dd7cddfSDavid du Colombier 	else if(drive->rwmctl){
14107dd7cddfSDavid du Colombier 		drive->block = drive->rwm*drive->secsize;
14117dd7cddfSDavid du Colombier 		if(drive->write)
14127dd7cddfSDavid du Colombier 			drive->command = Cwsm;
14137dd7cddfSDavid du Colombier 		else
14147dd7cddfSDavid du Colombier 			drive->command = Crsm;
14157dd7cddfSDavid du Colombier 	}
14167dd7cddfSDavid du Colombier 	else{
14177dd7cddfSDavid du Colombier 		drive->block = drive->secsize;
14187dd7cddfSDavid du Colombier 		if(drive->write)
14197dd7cddfSDavid du Colombier 			drive->command = Cws;
14207dd7cddfSDavid du Colombier 		else
14217dd7cddfSDavid du Colombier 			drive->command = Crs;
14227dd7cddfSDavid du Colombier 	}
14237dd7cddfSDavid du Colombier 	drive->limit = drive->data + drive->count*drive->secsize;
14246a081dcdSDavid du Colombier 	cmd = drive->command;
14255ea8af7bSDavid du Colombier 	if(use48){
142691157df7SDavid du Colombier 		outb(cmdport+Count, drive->count>>8);
142791157df7SDavid du Colombier 		outb(cmdport+Count, drive->count);
142891157df7SDavid du Colombier 		outb(cmdport+Lbalo, lba>>24);
142991157df7SDavid du Colombier 		outb(cmdport+Lbalo, lba);
143091157df7SDavid du Colombier 		outb(cmdport+Lbamid, lba>>32);
143191157df7SDavid du Colombier 		outb(cmdport+Lbamid, lba>>8);
143291157df7SDavid du Colombier 		outb(cmdport+Lbahi, lba>>40);
143391157df7SDavid du Colombier 		outb(cmdport+Lbahi, lba>>16);
14346a081dcdSDavid du Colombier 		outb(cmdport+Dh, drive->dev|Lba);
14356a081dcdSDavid du Colombier 		cmd = cmd48[cmd];
14365ea8af7bSDavid du Colombier 
14376a081dcdSDavid du Colombier 		if(DEBUG & Dbg48BIT)
14386a081dcdSDavid du Colombier 			print("using 48-bit commands\n");
143959f4c0c1SDavid du Colombier 	}
144059f4c0c1SDavid du Colombier 	else{
14417dd7cddfSDavid du Colombier 		outb(cmdport+Count, drive->count);
14427dd7cddfSDavid du Colombier 		outb(cmdport+Sector, s);
14437dd7cddfSDavid du Colombier 		outb(cmdport+Cyllo, c);
14447dd7cddfSDavid du Colombier 		outb(cmdport+Cylhi, c>>8);
14456a081dcdSDavid du Colombier 		outb(cmdport+Dh, drive->dev|h);
14465ea8af7bSDavid du Colombier 	}
14477dd7cddfSDavid du Colombier 	ctlr->done = 0;
14487dd7cddfSDavid du Colombier 	ctlr->curdrive = drive;
14497dd7cddfSDavid du Colombier 	ctlr->command = drive->command;	/* debugging */
14506a081dcdSDavid du Colombier 	outb(cmdport+Command, cmd);
14517dd7cddfSDavid du Colombier 
14527dd7cddfSDavid du Colombier 	switch(drive->command){
14537dd7cddfSDavid du Colombier 	case Cws:
14547dd7cddfSDavid du Colombier 	case Cwsm:
14557dd7cddfSDavid du Colombier 		microdelay(1);
14564de34a7eSDavid du Colombier 		/* 10*1000 for flash ide drives - maybe detect them? */
14574de34a7eSDavid du Colombier 		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*1000);
14587dd7cddfSDavid du Colombier 		if(as < 0 || (as & Err)){
14597dd7cddfSDavid du Colombier 			iunlock(ctlr);
14607dd7cddfSDavid du Colombier 			return -1;
14617dd7cddfSDavid du Colombier 		}
14627dd7cddfSDavid du Colombier 		len = drive->block;
14637dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
14647dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
14657dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->data, len/2);
14667dd7cddfSDavid du Colombier 		break;
14677dd7cddfSDavid du Colombier 
14687dd7cddfSDavid du Colombier 	case Crd:
14697dd7cddfSDavid du Colombier 	case Cwd:
14707dd7cddfSDavid du Colombier 		atadmastart(ctlr, drive->write);
14717dd7cddfSDavid du Colombier 		break;
14727dd7cddfSDavid du Colombier 	}
14737dd7cddfSDavid du Colombier 	iunlock(ctlr);
14747dd7cddfSDavid du Colombier 
14757dd7cddfSDavid du Colombier 	return 0;
14767dd7cddfSDavid du Colombier }
14777dd7cddfSDavid du Colombier 
14787dd7cddfSDavid du Colombier static int
atagenioretry(Drive * drive)14797dd7cddfSDavid du Colombier atagenioretry(Drive* drive)
14807dd7cddfSDavid du Colombier {
14816de6ce84SDavid du Colombier 	if(drive->dmactl){
14827dd7cddfSDavid du Colombier 		drive->dmactl = 0;
14836de6ce84SDavid du Colombier 		print("atagenioretry: disabling dma\n");
1484ef9eff0bSDavid du Colombier 	}
1485ef9eff0bSDavid du Colombier 	else if(drive->rwmctl)
14867dd7cddfSDavid du Colombier 		drive->rwmctl = 0;
14877dd7cddfSDavid du Colombier 	else
14887dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 4, 8, drive->error);
14897dd7cddfSDavid du Colombier 
14907dd7cddfSDavid du Colombier 	return SDretry;
14917dd7cddfSDavid du Colombier }
14927dd7cddfSDavid du Colombier 
14937dd7cddfSDavid du Colombier static int
atagenio(Drive * drive,uchar * cmd,int clen)149491157df7SDavid du Colombier atagenio(Drive* drive, uchar* cmd, int clen)
14957dd7cddfSDavid du Colombier {
14967dd7cddfSDavid du Colombier 	uchar *p;
14977dd7cddfSDavid du Colombier 	Ctlr *ctlr;
14986a081dcdSDavid du Colombier 	vlong lba, len;
14994de34a7eSDavid du Colombier 	int count, maxio;
15007dd7cddfSDavid du Colombier 
15017dd7cddfSDavid du Colombier 	/*
15027dd7cddfSDavid du Colombier 	 * Map SCSI commands into ATA commands for discs.
15037dd7cddfSDavid du Colombier 	 * Fail any command with a LUN except INQUIRY which
15047dd7cddfSDavid du Colombier 	 * will return 'logical unit not supported'.
15057dd7cddfSDavid du Colombier 	 */
15067dd7cddfSDavid du Colombier 	if((cmd[1]>>5) && cmd[0] != 0x12)
15077dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x25, 0);
15087dd7cddfSDavid du Colombier 
15097dd7cddfSDavid du Colombier 	switch(cmd[0]){
15107dd7cddfSDavid du Colombier 	default:
15117dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x20, 0);
15127dd7cddfSDavid du Colombier 
15137dd7cddfSDavid du Colombier 	case 0x00:			/* test unit ready */
15147dd7cddfSDavid du Colombier 		return SDok;
15157dd7cddfSDavid du Colombier 
15167dd7cddfSDavid du Colombier 	case 0x03:			/* request sense */
15177dd7cddfSDavid du Colombier 		if(cmd[4] < sizeof(drive->sense))
15187dd7cddfSDavid du Colombier 			len = cmd[4];
15197dd7cddfSDavid du Colombier 		else
15207dd7cddfSDavid du Colombier 			len = sizeof(drive->sense);
15217dd7cddfSDavid du Colombier 		if(drive->data && drive->dlen >= len){
15227dd7cddfSDavid du Colombier 			memmove(drive->data, drive->sense, len);
15237dd7cddfSDavid du Colombier 			drive->data += len;
15247dd7cddfSDavid du Colombier 		}
15257dd7cddfSDavid du Colombier 		return SDok;
15267dd7cddfSDavid du Colombier 
15277dd7cddfSDavid du Colombier 	case 0x12:			/* inquiry */
15287dd7cddfSDavid du Colombier 		if(cmd[4] < sizeof(drive->inquiry))
15297dd7cddfSDavid du Colombier 			len = cmd[4];
15307dd7cddfSDavid du Colombier 		else
15317dd7cddfSDavid du Colombier 			len = sizeof(drive->inquiry);
15327dd7cddfSDavid du Colombier 		if(drive->data && drive->dlen >= len){
15337dd7cddfSDavid du Colombier 			memmove(drive->data, drive->inquiry, len);
15347dd7cddfSDavid du Colombier 			drive->data += len;
15357dd7cddfSDavid du Colombier 		}
15367dd7cddfSDavid du Colombier 		return SDok;
15377dd7cddfSDavid du Colombier 
15387dd7cddfSDavid du Colombier 	case 0x1B:			/* start/stop unit */
15397dd7cddfSDavid du Colombier 		/*
15407dd7cddfSDavid du Colombier 		 * NOP for now, can use the power management feature
15417dd7cddfSDavid du Colombier 		 * set later.
15427dd7cddfSDavid du Colombier 		 */
15437dd7cddfSDavid du Colombier 		return SDok;
15447dd7cddfSDavid du Colombier 
15457dd7cddfSDavid du Colombier 	case 0x25:			/* read capacity */
15467dd7cddfSDavid du Colombier 		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
15477dd7cddfSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
15487dd7cddfSDavid du Colombier 		if(drive->data == nil || drive->dlen < 8)
15497dd7cddfSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
15507dd7cddfSDavid du Colombier 		/*
15517dd7cddfSDavid du Colombier 		 * Read capacity returns the LBA of the last sector.
15527dd7cddfSDavid du Colombier 		 */
15537dd7cddfSDavid du Colombier 		len = drive->sectors-1;
15547dd7cddfSDavid du Colombier 		p = drive->data;
15557dd7cddfSDavid du Colombier 		*p++ = len>>24;
15567dd7cddfSDavid du Colombier 		*p++ = len>>16;
15577dd7cddfSDavid du Colombier 		*p++ = len>>8;
15587dd7cddfSDavid du Colombier 		*p++ = len;
15597dd7cddfSDavid du Colombier 		len = drive->secsize;
15607dd7cddfSDavid du Colombier 		*p++ = len>>24;
15617dd7cddfSDavid du Colombier 		*p++ = len>>16;
15627dd7cddfSDavid du Colombier 		*p++ = len>>8;
15637dd7cddfSDavid du Colombier 		*p = len;
15647dd7cddfSDavid du Colombier 		drive->data += 8;
15657dd7cddfSDavid du Colombier 		return SDok;
15667dd7cddfSDavid du Colombier 
15676a081dcdSDavid du Colombier 	case 0x9E:			/* long read capacity */
15686a081dcdSDavid du Colombier 		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
15696a081dcdSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
15706a081dcdSDavid du Colombier 		if(drive->data == nil || drive->dlen < 8)
15716a081dcdSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
15726a081dcdSDavid du Colombier 		/*
15736a081dcdSDavid du Colombier 		 * Read capacity returns the LBA of the last sector.
15746a081dcdSDavid du Colombier 		 */
15756a081dcdSDavid du Colombier 		len = drive->sectors-1;
15766a081dcdSDavid du Colombier 		p = drive->data;
15776a081dcdSDavid du Colombier 		*p++ = len>>56;
15786a081dcdSDavid du Colombier 		*p++ = len>>48;
15796a081dcdSDavid du Colombier 		*p++ = len>>40;
15806a081dcdSDavid du Colombier 		*p++ = len>>32;
15816a081dcdSDavid du Colombier 		*p++ = len>>24;
15826a081dcdSDavid du Colombier 		*p++ = len>>16;
15836a081dcdSDavid du Colombier 		*p++ = len>>8;
15846a081dcdSDavid du Colombier 		*p++ = len;
15856a081dcdSDavid du Colombier 		len = drive->secsize;
15866a081dcdSDavid du Colombier 		*p++ = len>>24;
15876a081dcdSDavid du Colombier 		*p++ = len>>16;
15886a081dcdSDavid du Colombier 		*p++ = len>>8;
15896a081dcdSDavid du Colombier 		*p = len;
15904de34a7eSDavid du Colombier 		drive->data += 12;
15916a081dcdSDavid du Colombier 		return SDok;
15926a081dcdSDavid du Colombier 
1593*27522402SDavid du Colombier 	case 0x28:			/* read (10) */
1594*27522402SDavid du Colombier 	case 0x88:			/* long read (16) */
1595*27522402SDavid du Colombier 	case 0x2a:			/* write (10) */
1596*27522402SDavid du Colombier 	case 0x8a:			/* long write (16) */
1597*27522402SDavid du Colombier 	case 0x2e:			/* write and verify (10) */
15987dd7cddfSDavid du Colombier 		break;
15997dd7cddfSDavid du Colombier 
16007dd7cddfSDavid du Colombier 	case 0x5A:
16017dd7cddfSDavid du Colombier 		return atamodesense(drive, cmd);
16027dd7cddfSDavid du Colombier 	}
16037dd7cddfSDavid du Colombier 
16047dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
160591157df7SDavid du Colombier 	if(clen == 16){
160691157df7SDavid du Colombier 		/* ata commands only go to 48-bit lba */
160791157df7SDavid du Colombier 		if(cmd[2] || cmd[3])
160891157df7SDavid du Colombier 			return atasetsense(drive, SDcheck, 3, 0xc, 2);
160991157df7SDavid du Colombier 		lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32;
161091157df7SDavid du Colombier 		lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9];
161191157df7SDavid du Colombier 		count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
161291157df7SDavid du Colombier 	}else{
161391157df7SDavid du Colombier 		lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
161491157df7SDavid du Colombier 		count = cmd[7]<<8 | cmd[8];
161591157df7SDavid du Colombier 	}
16167dd7cddfSDavid du Colombier 	if(drive->data == nil)
16177dd7cddfSDavid du Colombier 		return SDok;
16187dd7cddfSDavid du Colombier 	if(drive->dlen < count*drive->secsize)
16197dd7cddfSDavid du Colombier 		count = drive->dlen/drive->secsize;
16207dd7cddfSDavid du Colombier 	qlock(ctlr);
16214de34a7eSDavid du Colombier 	if(ctlr->maxio)
16224de34a7eSDavid du Colombier 		maxio = ctlr->maxio;
16234de34a7eSDavid du Colombier 	else if(drive->flags & Lba48)
16244de34a7eSDavid du Colombier 		maxio = 65536;
16254de34a7eSDavid du Colombier 	else
16264de34a7eSDavid du Colombier 		maxio = 256;
16277dd7cddfSDavid du Colombier 	while(count){
16284de34a7eSDavid du Colombier 		if(count > maxio)
16294de34a7eSDavid du Colombier 			drive->count = maxio;
16307dd7cddfSDavid du Colombier 		else
16317dd7cddfSDavid du Colombier 			drive->count = count;
16327dd7cddfSDavid du Colombier 		if(atageniostart(drive, lba)){
16337dd7cddfSDavid du Colombier 			ilock(ctlr);
16347dd7cddfSDavid du Colombier 			atanop(drive, 0);
16357dd7cddfSDavid du Colombier 			iunlock(ctlr);
16367dd7cddfSDavid du Colombier 			qunlock(ctlr);
16377dd7cddfSDavid du Colombier 			return atagenioretry(drive);
16387dd7cddfSDavid du Colombier 		}
16397dd7cddfSDavid du Colombier 
16407dd7cddfSDavid du Colombier 		while(waserror())
16417dd7cddfSDavid du Colombier 			;
16424de34a7eSDavid du Colombier 		tsleep(ctlr, atadone, ctlr, 60*1000);
16437dd7cddfSDavid du Colombier 		poperror();
16447dd7cddfSDavid du Colombier 		if(!ctlr->done){
16457dd7cddfSDavid du Colombier 			/*
16467dd7cddfSDavid du Colombier 			 * What should the above timeout be? In
16477dd7cddfSDavid du Colombier 			 * standby and sleep modes it could take as
16487dd7cddfSDavid du Colombier 			 * long as 30 seconds for a drive to respond.
16497dd7cddfSDavid du Colombier 			 * Very hard to get out of this cleanly.
16507dd7cddfSDavid du Colombier 			 */
16517dd7cddfSDavid du Colombier 			atadumpstate(drive, cmd, lba, count);
16527dd7cddfSDavid du Colombier 			ataabort(drive, 1);
165380ee5cbfSDavid du Colombier 			qunlock(ctlr);
16547dd7cddfSDavid du Colombier 			return atagenioretry(drive);
16557dd7cddfSDavid du Colombier 		}
16567dd7cddfSDavid du Colombier 
16577dd7cddfSDavid du Colombier 		if(drive->status & Err){
16587dd7cddfSDavid du Colombier 			qunlock(ctlr);
16597dd7cddfSDavid du Colombier 			return atasetsense(drive, SDcheck, 4, 8, drive->error);
16607dd7cddfSDavid du Colombier 		}
16617dd7cddfSDavid du Colombier 		count -= drive->count;
16627dd7cddfSDavid du Colombier 		lba += drive->count;
16637dd7cddfSDavid du Colombier 	}
16647dd7cddfSDavid du Colombier 	qunlock(ctlr);
16657dd7cddfSDavid du Colombier 
16667dd7cddfSDavid du Colombier 	return SDok;
16677dd7cddfSDavid du Colombier }
16687dd7cddfSDavid du Colombier 
16697dd7cddfSDavid du Colombier static int
atario(SDreq * r)16707dd7cddfSDavid du Colombier atario(SDreq* r)
16717dd7cddfSDavid du Colombier {
16727dd7cddfSDavid du Colombier 	Ctlr *ctlr;
16737dd7cddfSDavid du Colombier 	Drive *drive;
16747dd7cddfSDavid du Colombier 	SDunit *unit;
16757dd7cddfSDavid du Colombier 	uchar cmd10[10], *cmdp, *p;
16767dd7cddfSDavid du Colombier 	int clen, reqstatus, status;
16777dd7cddfSDavid du Colombier 
16787dd7cddfSDavid du Colombier 	unit = r->unit;
16797dd7cddfSDavid du Colombier 	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
16807dd7cddfSDavid du Colombier 		r->status = SDtimeout;
16817dd7cddfSDavid du Colombier 		return SDtimeout;
16827dd7cddfSDavid du Colombier 	}
16837dd7cddfSDavid du Colombier 	drive = ctlr->drive[unit->subno];
16847dd7cddfSDavid du Colombier 
16857dd7cddfSDavid du Colombier 	/*
16867dd7cddfSDavid du Colombier 	 * Most SCSI commands can be passed unchanged except for
16877dd7cddfSDavid du Colombier 	 * the padding on the end. The few which require munging
16887dd7cddfSDavid du Colombier 	 * are not used internally. Mode select/sense(6) could be
16897dd7cddfSDavid du Colombier 	 * converted to the 10-byte form but it's not worth the
16907dd7cddfSDavid du Colombier 	 * effort. Read/write(6) are easy.
16917dd7cddfSDavid du Colombier 	 */
16927dd7cddfSDavid du Colombier 	switch(r->cmd[0]){
16937dd7cddfSDavid du Colombier 	case 0x08:			/* read */
16947dd7cddfSDavid du Colombier 	case 0x0A:			/* write */
16957dd7cddfSDavid du Colombier 		cmdp = cmd10;
16967dd7cddfSDavid du Colombier 		memset(cmdp, 0, sizeof(cmd10));
16977dd7cddfSDavid du Colombier 		cmdp[0] = r->cmd[0]|0x20;
16987dd7cddfSDavid du Colombier 		cmdp[1] = r->cmd[1] & 0xE0;
16997dd7cddfSDavid du Colombier 		cmdp[5] = r->cmd[3];
17007dd7cddfSDavid du Colombier 		cmdp[4] = r->cmd[2];
17017dd7cddfSDavid du Colombier 		cmdp[3] = r->cmd[1] & 0x0F;
17027dd7cddfSDavid du Colombier 		cmdp[8] = r->cmd[4];
17037dd7cddfSDavid du Colombier 		clen = sizeof(cmd10);
17047dd7cddfSDavid du Colombier 		break;
17057dd7cddfSDavid du Colombier 
17067dd7cddfSDavid du Colombier 	default:
17077dd7cddfSDavid du Colombier 		cmdp = r->cmd;
17087dd7cddfSDavid du Colombier 		clen = r->clen;
17097dd7cddfSDavid du Colombier 		break;
17107dd7cddfSDavid du Colombier 	}
17117dd7cddfSDavid du Colombier 
17127dd7cddfSDavid du Colombier 	qlock(drive);
17137dd7cddfSDavid du Colombier retry:
17147dd7cddfSDavid du Colombier 	drive->write = r->write;
17157dd7cddfSDavid du Colombier 	drive->data = r->data;
17167dd7cddfSDavid du Colombier 	drive->dlen = r->dlen;
17177dd7cddfSDavid du Colombier 
17187dd7cddfSDavid du Colombier 	drive->status = 0;
17197dd7cddfSDavid du Colombier 	drive->error = 0;
17207dd7cddfSDavid du Colombier 	if(drive->pkt)
17217dd7cddfSDavid du Colombier 		status = atapktio(drive, cmdp, clen);
17227dd7cddfSDavid du Colombier 	else
17237dd7cddfSDavid du Colombier 		status = atagenio(drive, cmdp, clen);
17247dd7cddfSDavid du Colombier 	if(status == SDretry){
17257dd7cddfSDavid du Colombier 		if(DbgDEBUG)
17267dd7cddfSDavid du Colombier 			print("%s: retry: dma %8.8uX rwm %4.4uX\n",
17277dd7cddfSDavid du Colombier 				unit->name, drive->dmactl, drive->rwmctl);
17287dd7cddfSDavid du Colombier 		goto retry;
17297dd7cddfSDavid du Colombier 	}
17307dd7cddfSDavid du Colombier 	if(status == SDok){
17317dd7cddfSDavid du Colombier 		atasetsense(drive, SDok, 0, 0, 0);
17327dd7cddfSDavid du Colombier 		if(drive->data){
17337dd7cddfSDavid du Colombier 			p = r->data;
17347dd7cddfSDavid du Colombier 			r->rlen = drive->data - p;
17357dd7cddfSDavid du Colombier 		}
17367dd7cddfSDavid du Colombier 		else
17377dd7cddfSDavid du Colombier 			r->rlen = 0;
17387dd7cddfSDavid du Colombier 	}
17397dd7cddfSDavid du Colombier 	else if(status == SDcheck && !(r->flags & SDnosense)){
17407dd7cddfSDavid du Colombier 		drive->write = 0;
17417dd7cddfSDavid du Colombier 		memset(cmd10, 0, sizeof(cmd10));
17427dd7cddfSDavid du Colombier 		cmd10[0] = 0x03;
17437dd7cddfSDavid du Colombier 		cmd10[1] = r->lun<<5;
17447dd7cddfSDavid du Colombier 		cmd10[4] = sizeof(r->sense)-1;
17457dd7cddfSDavid du Colombier 		drive->data = r->sense;
17467dd7cddfSDavid du Colombier 		drive->dlen = sizeof(r->sense)-1;
17477dd7cddfSDavid du Colombier 		drive->status = 0;
17487dd7cddfSDavid du Colombier 		drive->error = 0;
17497dd7cddfSDavid du Colombier 		if(drive->pkt)
17507dd7cddfSDavid du Colombier 			reqstatus = atapktio(drive, cmd10, 6);
17517dd7cddfSDavid du Colombier 		else
17527dd7cddfSDavid du Colombier 			reqstatus = atagenio(drive, cmd10, 6);
17537dd7cddfSDavid du Colombier 		if(reqstatus == SDok){
17547dd7cddfSDavid du Colombier 			r->flags |= SDvalidsense;
17557dd7cddfSDavid du Colombier 			atasetsense(drive, SDok, 0, 0, 0);
17567dd7cddfSDavid du Colombier 		}
17577dd7cddfSDavid du Colombier 	}
17587dd7cddfSDavid du Colombier 	qunlock(drive);
17597dd7cddfSDavid du Colombier 	r->status = status;
17607dd7cddfSDavid du Colombier 	if(status != SDok)
17617dd7cddfSDavid du Colombier 		return status;
17627dd7cddfSDavid du Colombier 
17637dd7cddfSDavid du Colombier 	/*
17647dd7cddfSDavid du Colombier 	 * Fix up any results.
17657dd7cddfSDavid du Colombier 	 * Many ATAPI CD-ROMs ignore the LUN field completely and
17667dd7cddfSDavid du Colombier 	 * return valid INQUIRY data. Patch the response to indicate
17677dd7cddfSDavid du Colombier 	 * 'logical unit not supported' if the LUN is non-zero.
17687dd7cddfSDavid du Colombier 	 */
17697dd7cddfSDavid du Colombier 	switch(cmdp[0]){
17707dd7cddfSDavid du Colombier 	case 0x12:			/* inquiry */
17717dd7cddfSDavid du Colombier 		if((p = r->data) == nil)
17727dd7cddfSDavid du Colombier 			break;
17737dd7cddfSDavid du Colombier 		if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05))
17747dd7cddfSDavid du Colombier 			p[0] = 0x7F;
17757dd7cddfSDavid du Colombier 		/*FALLTHROUGH*/
17767dd7cddfSDavid du Colombier 	default:
17777dd7cddfSDavid du Colombier 		break;
17787dd7cddfSDavid du Colombier 	}
17797dd7cddfSDavid du Colombier 
17807dd7cddfSDavid du Colombier 	return SDok;
17817dd7cddfSDavid du Colombier }
17827dd7cddfSDavid du Colombier 
17832f2115d0SDavid du Colombier /* interrupt ack hack for intel ich controllers */
17842f2115d0SDavid du Colombier static void
ichirqack(Ctlr * ctlr)17852f2115d0SDavid du Colombier ichirqack(Ctlr *ctlr)
17862f2115d0SDavid du Colombier {
17872f2115d0SDavid du Colombier 	int bmiba;
17882f2115d0SDavid du Colombier 
17892f2115d0SDavid du Colombier 	bmiba = ctlr->bmiba;
17902f2115d0SDavid du Colombier 	if(bmiba)
17912f2115d0SDavid du Colombier 		outb(bmiba+Bmisx, inb(bmiba+Bmisx));
17922f2115d0SDavid du Colombier }
17932f2115d0SDavid du Colombier 
17947dd7cddfSDavid du Colombier static void
atainterrupt(Ureg *,void * arg)17957dd7cddfSDavid du Colombier atainterrupt(Ureg*, void* arg)
17967dd7cddfSDavid du Colombier {
17977dd7cddfSDavid du Colombier 	Ctlr *ctlr;
17987dd7cddfSDavid du Colombier 	Drive *drive;
17997dd7cddfSDavid du Colombier 	int cmdport, len, status;
18007dd7cddfSDavid du Colombier 
18017dd7cddfSDavid du Colombier 	ctlr = arg;
18027dd7cddfSDavid du Colombier 
18037dd7cddfSDavid du Colombier 	ilock(ctlr);
18047dd7cddfSDavid du Colombier 	if(inb(ctlr->ctlport+As) & Bsy){
18052f2115d0SDavid du Colombier 		ctlr->intbusy++;
18067dd7cddfSDavid du Colombier 		iunlock(ctlr);
1807503d3e0aSDavid du Colombier 		if(DEBUG & DbgBsy)
18087dd7cddfSDavid du Colombier 			print("IBsy+");
18097dd7cddfSDavid du Colombier 		return;
18107dd7cddfSDavid du Colombier 	}
18117dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
18127dd7cddfSDavid du Colombier 	status = inb(cmdport+Status);
18137dd7cddfSDavid du Colombier 	if((drive = ctlr->curdrive) == nil){
18142f2115d0SDavid du Colombier 		ctlr->intnil++;
18152f2115d0SDavid du Colombier 		if(ctlr->irqack != nil)
18162f2115d0SDavid du Colombier 			ctlr->irqack(ctlr);
18177dd7cddfSDavid du Colombier 		iunlock(ctlr);
18183ff48bf5SDavid du Colombier 		if((DEBUG & DbgINL) && ctlr->command != Cedd)
18199a747e4fSDavid du Colombier 			print("Inil%2.2uX+", ctlr->command);
18207dd7cddfSDavid du Colombier 		return;
18217dd7cddfSDavid du Colombier 	}
18227dd7cddfSDavid du Colombier 
18232f2115d0SDavid du Colombier 	ctlr->intok++;
18242f2115d0SDavid du Colombier 
18257dd7cddfSDavid du Colombier 	if(status & Err)
18267dd7cddfSDavid du Colombier 		drive->error = inb(cmdport+Error);
18277dd7cddfSDavid du Colombier 	else switch(drive->command){
18287dd7cddfSDavid du Colombier 	default:
18297dd7cddfSDavid du Colombier 		drive->error = Abrt;
18307dd7cddfSDavid du Colombier 		break;
18317dd7cddfSDavid du Colombier 
18327dd7cddfSDavid du Colombier 	case Crs:
18337dd7cddfSDavid du Colombier 	case Crsm:
18342f2115d0SDavid du Colombier 		drive->intrd++;
18357dd7cddfSDavid du Colombier 		if(!(status & Drq)){
18367dd7cddfSDavid du Colombier 			drive->error = Abrt;
18377dd7cddfSDavid du Colombier 			break;
18387dd7cddfSDavid du Colombier 		}
18397dd7cddfSDavid du Colombier 		len = drive->block;
18407dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
18417dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
18427dd7cddfSDavid du Colombier 		inss(cmdport+Data, drive->data, len/2);
18437dd7cddfSDavid du Colombier 		drive->data += len;
18447dd7cddfSDavid du Colombier 		if(drive->data >= drive->limit)
18457dd7cddfSDavid du Colombier 			ctlr->done = 1;
18467dd7cddfSDavid du Colombier 		break;
18477dd7cddfSDavid du Colombier 
18487dd7cddfSDavid du Colombier 	case Cws:
18497dd7cddfSDavid du Colombier 	case Cwsm:
18502f2115d0SDavid du Colombier 		drive->intwr++;
18517dd7cddfSDavid du Colombier 		len = drive->block;
18527dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
18537dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
18547dd7cddfSDavid du Colombier 		drive->data += len;
18557dd7cddfSDavid du Colombier 		if(drive->data >= drive->limit){
18567dd7cddfSDavid du Colombier 			ctlr->done = 1;
18577dd7cddfSDavid du Colombier 			break;
18587dd7cddfSDavid du Colombier 		}
18597dd7cddfSDavid du Colombier 		if(!(status & Drq)){
18607dd7cddfSDavid du Colombier 			drive->error = Abrt;
18617dd7cddfSDavid du Colombier 			break;
18627dd7cddfSDavid du Colombier 		}
18637dd7cddfSDavid du Colombier 		len = drive->block;
18647dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
18657dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
18667dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->data, len/2);
18677dd7cddfSDavid du Colombier 		break;
18687dd7cddfSDavid du Colombier 
18697dd7cddfSDavid du Colombier 	case Cpkt:
18707dd7cddfSDavid du Colombier 		atapktinterrupt(drive);
18717dd7cddfSDavid du Colombier 		break;
18727dd7cddfSDavid du Colombier 
18737dd7cddfSDavid du Colombier 	case Crd:
18742f2115d0SDavid du Colombier 		drive->intrd++;
18752f2115d0SDavid du Colombier 		/* fall through */
18767dd7cddfSDavid du Colombier 	case Cwd:
18772f2115d0SDavid du Colombier 		if (drive->command == Cwd)
18782f2115d0SDavid du Colombier 			drive->intwr++;
18797dd7cddfSDavid du Colombier 		atadmainterrupt(drive, drive->count*drive->secsize);
18807dd7cddfSDavid du Colombier 		break;
188180ee5cbfSDavid du Colombier 
188280ee5cbfSDavid du Colombier 	case Cstandby:
188380ee5cbfSDavid du Colombier 		ctlr->done = 1;
188480ee5cbfSDavid du Colombier 		break;
18857dd7cddfSDavid du Colombier 	}
18862f2115d0SDavid du Colombier 	if(ctlr->irqack != nil)
18872f2115d0SDavid du Colombier 		ctlr->irqack(ctlr);
18887dd7cddfSDavid du Colombier 	iunlock(ctlr);
18897dd7cddfSDavid du Colombier 
18907dd7cddfSDavid du Colombier 	if(drive->error){
18917dd7cddfSDavid du Colombier 		status |= Err;
18927dd7cddfSDavid du Colombier 		ctlr->done = 1;
18937dd7cddfSDavid du Colombier 	}
18947dd7cddfSDavid du Colombier 
18957dd7cddfSDavid du Colombier 	if(ctlr->done){
18967dd7cddfSDavid du Colombier 		ctlr->curdrive = nil;
18977dd7cddfSDavid du Colombier 		drive->status = status;
18987dd7cddfSDavid du Colombier 		wakeup(ctlr);
18997dd7cddfSDavid du Colombier 	}
19007dd7cddfSDavid du Colombier }
19017dd7cddfSDavid du Colombier 
19027dd7cddfSDavid du Colombier static SDev*
atapnp(void)19037dd7cddfSDavid du Colombier atapnp(void)
19047dd7cddfSDavid du Colombier {
19057dd7cddfSDavid du Colombier 	Ctlr *ctlr;
19067dd7cddfSDavid du Colombier 	Pcidev *p;
19077dd7cddfSDavid du Colombier 	SDev *legacy[2], *sdev, *head, *tail;
19084de34a7eSDavid du Colombier 	int channel, ispc87415, maxio, pi, r, span;
19092f2115d0SDavid du Colombier 	void (*irqack)(Ctlr*);
19107dd7cddfSDavid du Colombier 
19112f2115d0SDavid du Colombier 	irqack = nil;
19127dd7cddfSDavid du Colombier 	legacy[0] = legacy[1] = head = tail = nil;
19137dd7cddfSDavid du Colombier 	if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){
19147dd7cddfSDavid du Colombier 		head = tail = sdev;
19157dd7cddfSDavid du Colombier 		legacy[0] = sdev;
19167dd7cddfSDavid du Colombier 	}
19177dd7cddfSDavid du Colombier 	if(sdev = ataprobe(0x170, 0x374, IrqATA1)){
19187dd7cddfSDavid du Colombier 		if(head != nil)
19197dd7cddfSDavid du Colombier 			tail->next = sdev;
19207dd7cddfSDavid du Colombier 		else
19217dd7cddfSDavid du Colombier 			head = sdev;
19227dd7cddfSDavid du Colombier 		tail = sdev;
19237dd7cddfSDavid du Colombier 		legacy[1] = sdev;
19247dd7cddfSDavid du Colombier 	}
19257dd7cddfSDavid du Colombier 
19267dd7cddfSDavid du Colombier 	p = nil;
19277dd7cddfSDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
19287dd7cddfSDavid du Colombier 		/*
19297dd7cddfSDavid du Colombier 		 * Look for devices with the correct class and sub-class
19307dd7cddfSDavid du Colombier 		 * code and known device and vendor ID; add native-mode
19317dd7cddfSDavid du Colombier 		 * channels to the list to be probed, save info for the
19327dd7cddfSDavid du Colombier 		 * compatibility mode channels.
19337dd7cddfSDavid du Colombier 		 * Note that the legacy devices should not be considered
19347dd7cddfSDavid du Colombier 		 * PCI devices by the interrupt controller.
19357dd7cddfSDavid du Colombier 		 * For both native and legacy, save info for busmastering
19367dd7cddfSDavid du Colombier 		 * if capable.
19377dd7cddfSDavid du Colombier 		 * Promise Ultra ATA/66 (PDC20262) appears to
19387dd7cddfSDavid du Colombier 		 * 1) give a sub-class of 'other mass storage controller'
19397dd7cddfSDavid du Colombier 		 *    instead of 'IDE controller', regardless of whether it's
19407dd7cddfSDavid du Colombier 		 *    the only controller or not;
19417dd7cddfSDavid du Colombier 		 * 2) put 0 in the programming interface byte (probably
19427dd7cddfSDavid du Colombier 		 *    as a consequence of 1) above).
1943503d3e0aSDavid du Colombier 		 * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
19447dd7cddfSDavid du Colombier 		 */
194559c21d95SDavid du Colombier 		if(p->ccrb != 0x01)
194659c21d95SDavid du Colombier 			continue;
194759c21d95SDavid du Colombier 		if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80)
19487dd7cddfSDavid du Colombier 			continue;
19497dd7cddfSDavid du Colombier 		pi = p->ccrp;
19507dd7cddfSDavid du Colombier 		ispc87415 = 0;
19514de34a7eSDavid du Colombier 		maxio = 0;
19524de34a7eSDavid du Colombier 		span = BMspan;
19537dd7cddfSDavid du Colombier 
19547dd7cddfSDavid du Colombier 		switch((p->did<<16)|p->vid){
19557dd7cddfSDavid du Colombier 		default:
19567dd7cddfSDavid du Colombier 			continue;
19577dd7cddfSDavid du Colombier 
19587dd7cddfSDavid du Colombier 		case (0x0002<<16)|0x100B:	/* NS PC87415 */
19597dd7cddfSDavid du Colombier 			/*
19607dd7cddfSDavid du Colombier 			 * Disable interrupts on both channels until
19617dd7cddfSDavid du Colombier 			 * after they are probed for drives.
19627dd7cddfSDavid du Colombier 			 * This must be called before interrupts are
19637dd7cddfSDavid du Colombier 			 * enabled because the IRQ may be shared.
19647dd7cddfSDavid du Colombier 			 */
19657dd7cddfSDavid du Colombier 			ispc87415 = 1;
19667dd7cddfSDavid du Colombier 			pcicfgw32(p, 0x40, 0x00000300);
19677dd7cddfSDavid du Colombier 			break;
19687dd7cddfSDavid du Colombier 		case (0x1000<<16)|0x1042:	/* PC-Tech RZ1000 */
19697dd7cddfSDavid du Colombier 			/*
19707dd7cddfSDavid du Colombier 			 * Turn off prefetch. Overkill, but cheap.
19717dd7cddfSDavid du Colombier 			 */
19727dd7cddfSDavid du Colombier 			r = pcicfgr32(p, 0x40);
19737dd7cddfSDavid du Colombier 			r &= ~0x2000;
19747dd7cddfSDavid du Colombier 			pcicfgw32(p, 0x40, r);
19757dd7cddfSDavid du Colombier 			break;
1976175630faSDavid du Colombier 		case (0x4379<<16)|0x1002:	/* ATI SB400 SATA*/
1977175630faSDavid du Colombier 		case (0x437a<<16)|0x1002:	/* ATI SB400 SATA */
197823173ec1SDavid du Colombier 		case (0x439c<<16)|0x1002:	/* ATI 439c SATA*/
197923173ec1SDavid du Colombier 		case (0x3373<<16)|0x105A:	/* Promise 20378 RAID */
1980b7b24591SDavid du Colombier 		case (0x4D30<<16)|0x105A:	/* Promise PDC202xx */
198123173ec1SDavid du Colombier 		case (0x4D38<<16)|0x105A:	/* Promise PDC20262 */
19826a081dcdSDavid du Colombier 		case (0x4D68<<16)|0x105A:	/* Promise PDC20268 */
19834c5c8adbSDavid du Colombier 		case (0x4D69<<16)|0x105A:	/* Promise Ultra/133 TX2 */
19844de34a7eSDavid du Colombier 		case (0x3112<<16)|0x1095:	/* SiI 3112 SATA/RAID */
198523173ec1SDavid du Colombier 		case (0x3149<<16)|0x1106:	/* VIA VT8237 SATA/RAID */
19864de34a7eSDavid du Colombier 			maxio = 15;
19874de34a7eSDavid du Colombier 			span = 8*1024;
19884de34a7eSDavid du Colombier 			/*FALLTHROUGH*/
1989d1be6b08SDavid du Colombier 		case (0x0680<<16)|0x1095:	/* SiI 0680/680A PATA133 ATAPI/RAID */
19904de34a7eSDavid du Colombier 		case (0x3114<<16)|0x1095:	/* SiI 3114 SATA/RAID */
1991fb7f0c93SDavid du Colombier 			pi = 0x85;
1992fb7f0c93SDavid du Colombier 			break;
19933806af99SDavid du Colombier 		case (0x0004<<16)|0x1103:	/* HighPoint HPT366 */
19947dd7cddfSDavid du Colombier 			pi = 0x85;
1995fb7f0c93SDavid du Colombier 			/*
1996fb7f0c93SDavid du Colombier 			 * Turn off fast interrupt prediction.
1997fb7f0c93SDavid du Colombier 			 */
1998fb7f0c93SDavid du Colombier 			if((r = pcicfgr8(p, 0x51)) & 0x80)
1999fb7f0c93SDavid du Colombier 				pcicfgw8(p, 0x51, r & ~0x80);
2000fb7f0c93SDavid du Colombier 			if((r = pcicfgr8(p, 0x55)) & 0x80)
2001fb7f0c93SDavid du Colombier 				pcicfgw8(p, 0x55, r & ~0x80);
20027dd7cddfSDavid du Colombier 			break;
20037dd7cddfSDavid du Colombier 		case (0x0640<<16)|0x1095:	/* CMD 640B */
20047dd7cddfSDavid du Colombier 			/*
20057dd7cddfSDavid du Colombier 			 * Bugfix code here...
20067dd7cddfSDavid du Colombier 			 */
20077dd7cddfSDavid du Colombier 			break;
2008f92e8f2eSDavid du Colombier 		case (0x7441<<16)|0x1022:	/* AMD 768 */
2009f92e8f2eSDavid du Colombier 			/*
2010f92e8f2eSDavid du Colombier 			 * Set:
2011f92e8f2eSDavid du Colombier 			 *	0x41	prefetch, postwrite;
2012f92e8f2eSDavid du Colombier 			 *	0x43	FIFO configuration 1/2 and 1/2;
2013f92e8f2eSDavid du Colombier 			 *	0x44	status register read retry;
2014f92e8f2eSDavid du Colombier 			 *	0x46	DMA read and end of sector flush.
2015f92e8f2eSDavid du Colombier 			 */
2016f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x41);
2017f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x41, r|0xF0);
2018f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x43);
2019f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
2020f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x44);
2021f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x44, r|0x08);
2022f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x46);
2023f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
20244c5c8adbSDavid du Colombier 			/*FALLTHROUGH*/
2025dfda52d8SDavid du Colombier 		case (0x7401<<16)|0x1022:	/* AMD 755 Cobra */
2026dfda52d8SDavid du Colombier 		case (0x7409<<16)|0x1022:	/* AMD 756 Viper */
2027dfda52d8SDavid du Colombier 		case (0x7410<<16)|0x1022:	/* AMD 766 Viper Plus */
20286f314b92SDavid du Colombier 		case (0x7469<<16)|0x1022:	/* AMD 3111 */
20294c5c8adbSDavid du Colombier 			/*
20304c5c8adbSDavid du Colombier 			 * This can probably be lumped in with the 768 above.
20314c5c8adbSDavid du Colombier 			 */
20324c5c8adbSDavid du Colombier 			/*FALLTHROUGH*/
20336a5dc222SDavid du Colombier 		case (0x209A<<16)|0x1022:	/* AMD CS5536 */
2034ab3dc52fSDavid du Colombier 		case (0x01BC<<16)|0x10DE:	/* nVidia nForce1 */
2035ab3dc52fSDavid du Colombier 		case (0x0065<<16)|0x10DE:	/* nVidia nForce2 */
2036ab3dc52fSDavid du Colombier 		case (0x0085<<16)|0x10DE:	/* nVidia nForce2 MCP */
2037b8a11165SDavid du Colombier 		case (0x00E3<<16)|0x10DE:	/* nVidia nForce2 250 SATA */
20384c5c8adbSDavid du Colombier 		case (0x00D5<<16)|0x10DE:	/* nVidia nForce3 */
2039ab3dc52fSDavid du Colombier 		case (0x00E5<<16)|0x10DE:	/* nVidia nForce3 Pro */
2040b8a11165SDavid du Colombier 		case (0x00EE<<16)|0x10DE:	/* nVidia nForce3 250 SATA */
2041ab3dc52fSDavid du Colombier 		case (0x0035<<16)|0x10DE:	/* nVidia nForce3 MCP */
2042ab3dc52fSDavid du Colombier 		case (0x0053<<16)|0x10DE:	/* nVidia nForce4 */
20431066d6deSDavid du Colombier 		case (0x0054<<16)|0x10DE:	/* nVidia nForce4 SATA */
20441066d6deSDavid du Colombier 		case (0x0055<<16)|0x10DE:	/* nVidia nForce4 SATA */
20450aa8dc3cSDavid du Colombier 		case (0x0266<<16)|0x10DE:	/* nVidia nForce4 430 SATA */
2046edc15dd6SDavid du Colombier 		case (0x0267<<16)|0x10DE:	/* nVidia nForce 55 MCP SATA */
2047edc15dd6SDavid du Colombier 		case (0x03EC<<16)|0x10DE:	/* nVidia nForce 61 MCP SATA */
2048edc15dd6SDavid du Colombier 		case (0x0448<<16)|0x10DE:	/* nVidia nForce 65 MCP SATA */
2049edc15dd6SDavid du Colombier 		case (0x0560<<16)|0x10DE:	/* nVidia nForce 69 MCP SATA */
20504c5c8adbSDavid du Colombier 			/*
20514c5c8adbSDavid du Colombier 			 * Ditto, although it may have a different base
20524c5c8adbSDavid du Colombier 			 * address for the registers (0x50?).
20534c5c8adbSDavid du Colombier 			 */
2054ff1040beSDavid du Colombier 			/*FALLTHROUGH*/
2055175630faSDavid du Colombier 		case (0x4376<<16)|0x1002:	/* ATI SB400 PATA */
20563be656c8SDavid du Colombier 		case (0x438c<<16)|0x1002:	/* ATI SB600 PATA */
2057f92e8f2eSDavid du Colombier 			break;
20582839d78eSDavid du Colombier 		case (0x0211<<16)|0x1166:	/* ServerWorks IB6566 */
20592839d78eSDavid du Colombier 			{
20602839d78eSDavid du Colombier 				Pcidev *sb;
20612839d78eSDavid du Colombier 
20622839d78eSDavid du Colombier 				sb = pcimatch(nil, 0x1166, 0x0200);
20632839d78eSDavid du Colombier 				if(sb == nil)
20642839d78eSDavid du Colombier 					break;
20652839d78eSDavid du Colombier 				r = pcicfgr32(sb, 0x64);
20662839d78eSDavid du Colombier 				r &= ~0x2000;
20672839d78eSDavid du Colombier 				pcicfgw32(sb, 0x64, r);
20682839d78eSDavid du Colombier 			}
20694de34a7eSDavid du Colombier 			span = 32*1024;
20702839d78eSDavid du Colombier 			break;
207191157df7SDavid du Colombier 		case (0x0502<<17)|0x100B:	/* NS SC1100/SCx200 */
20721066d6deSDavid du Colombier 		case (0x5229<<16)|0x10B9:	/* ALi M1543 */
20731066d6deSDavid du Colombier 		case (0x5288<<16)|0x10B9:	/* ALi M5288 SATA */
207459f4c0c1SDavid du Colombier 		case (0x5513<<16)|0x1039:	/* SiS 962 */
20757dd7cddfSDavid du Colombier 		case (0x0646<<16)|0x1095:	/* CMD 646 */
20767dd7cddfSDavid du Colombier 		case (0x0571<<16)|0x1106:	/* VIA 82C686 */
2077cb8c047aSDavid du Colombier 		case (0x2363<<16)|0x197b:	/* JMicron SATA */
20782f2115d0SDavid du Colombier 			break;	/* TODO: verify that this should be here; wasn't in original patch */
20797dd7cddfSDavid du Colombier 		case (0x1230<<16)|0x8086:	/* 82371FB (PIIX) */
20807dd7cddfSDavid du Colombier 		case (0x7010<<16)|0x8086:	/* 82371SB (PIIX3) */
20817dd7cddfSDavid du Colombier 		case (0x7111<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */
2082ef9eff0bSDavid du Colombier 		case (0x2411<<16)|0x8086:	/* 82801AA (ICH) */
2083ef9eff0bSDavid du Colombier 		case (0x2421<<16)|0x8086:	/* 82801AB (ICH0) */
2084ef9eff0bSDavid du Colombier 		case (0x244A<<16)|0x8086:	/* 82801BA (ICH2, Mobile) */
2085ef9eff0bSDavid du Colombier 		case (0x244B<<16)|0x8086:	/* 82801BA (ICH2, High-End) */
2086ef9eff0bSDavid du Colombier 		case (0x248A<<16)|0x8086:	/* 82801CA (ICH3, Mobile) */
2087ef9eff0bSDavid du Colombier 		case (0x248B<<16)|0x8086:	/* 82801CA (ICH3, High-End) */
2088e288d156SDavid du Colombier 		case (0x24CA<<16)|0x8086:	/* 82801DBM (ICH4, Mobile) */
2089ef9eff0bSDavid du Colombier 		case (0x24CB<<16)|0x8086:	/* 82801DB (ICH4, High-End) */
20903be656c8SDavid du Colombier 		case (0x24D1<<16)|0x8086:	/* 82801EB/ER (ICH5 High-End) */
2091f92e8f2eSDavid du Colombier 		case (0x24DB<<16)|0x8086:	/* 82801EB (ICH5) */
20921c9e5a6cSDavid du Colombier 		case (0x25A3<<16)|0x8086:	/* 6300ESB (E7210) */
20931a8a6b0dSDavid du Colombier 		case (0x2653<<16)|0x8086:	/* 82801FBM (ICH6M) */
20947c881178SDavid du Colombier 		case (0x266F<<16)|0x8086:	/* 82801FB (ICH6) */
2095055c7668SDavid du Colombier 		case (0x27DF<<16)|0x8086:	/* 82801G SATA (ICH7) */
2096055c7668SDavid du Colombier 		case (0x27C0<<16)|0x8086:	/* 82801GB SATA AHCI (ICH7) */
20977ddf765aSDavid du Colombier //		case (0x27C4<<16)|0x8086:	/* 82801GBM SATA (ICH7) */
2098e06f534bSDavid du Colombier 		case (0x27C5<<16)|0x8086:	/* 82801GBM SATA AHCI (ICH7) */
2099b897e69aSDavid du Colombier 		case (0x2920<<16)|0x8086:	/* 82801(IB)/IR/IH/IO SATA IDE (ICH9) */
210091157df7SDavid du Colombier 		case (0x3a20<<16)|0x8086:	/* 82801JI (ICH10) */
210191157df7SDavid du Colombier 		case (0x3a26<<16)|0x8086:	/* 82801JI (ICH10) */
21022f2115d0SDavid du Colombier 			irqack = ichirqack;
21037dd7cddfSDavid du Colombier 			break;
21047dd7cddfSDavid du Colombier 		}
21057dd7cddfSDavid du Colombier 
21067dd7cddfSDavid du Colombier 		for(channel = 0; channel < 2; channel++){
21077dd7cddfSDavid du Colombier 			if(pi & (1<<(2*channel))){
21087dd7cddfSDavid du Colombier 				sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
21097dd7cddfSDavid du Colombier 						p->mem[1+2*channel].bar & ~0x01,
21107dd7cddfSDavid du Colombier 						p->intl);
21117dd7cddfSDavid du Colombier 				if(sdev == nil)
21127dd7cddfSDavid du Colombier 					continue;
21137dd7cddfSDavid du Colombier 
21147dd7cddfSDavid du Colombier 				ctlr = sdev->ctlr;
21159a747e4fSDavid du Colombier 				if(ispc87415) {
21167dd7cddfSDavid du Colombier 					ctlr->ienable = pc87415ienable;
21179a747e4fSDavid du Colombier 					print("pc87415disable: not yet implemented\n");
21189a747e4fSDavid du Colombier 				}
21197dd7cddfSDavid du Colombier 
21207dd7cddfSDavid du Colombier 				if(head != nil)
21217dd7cddfSDavid du Colombier 					tail->next = sdev;
21227dd7cddfSDavid du Colombier 				else
21237dd7cddfSDavid du Colombier 					head = sdev;
21247dd7cddfSDavid du Colombier 				tail = sdev;
21257dd7cddfSDavid du Colombier 				ctlr->tbdf = p->tbdf;
21267dd7cddfSDavid du Colombier 			}
21277dd7cddfSDavid du Colombier 			else if((sdev = legacy[channel]) == nil)
21287dd7cddfSDavid du Colombier 				continue;
21297dd7cddfSDavid du Colombier 			else
21307dd7cddfSDavid du Colombier 				ctlr = sdev->ctlr;
21317dd7cddfSDavid du Colombier 
21327dd7cddfSDavid du Colombier 			ctlr->pcidev = p;
21334de34a7eSDavid du Colombier 			ctlr->maxio = maxio;
21344de34a7eSDavid du Colombier 			ctlr->span = span;
21352f2115d0SDavid du Colombier 			ctlr->irqack = irqack;
21367dd7cddfSDavid du Colombier 			if(!(pi & 0x80))
21377dd7cddfSDavid du Colombier 				continue;
21387dd7cddfSDavid du Colombier 			ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8;
21397dd7cddfSDavid du Colombier 		}
21407dd7cddfSDavid du Colombier 	}
21417dd7cddfSDavid du Colombier 
21429a747e4fSDavid du Colombier if(0){
21439a747e4fSDavid du Colombier 	int port;
21449a747e4fSDavid du Colombier 	ISAConf isa;
21459a747e4fSDavid du Colombier 
21469a747e4fSDavid du Colombier 	/*
21479a747e4fSDavid du Colombier 	 * Hack for PCMCIA drives.
21489a747e4fSDavid du Colombier 	 * This will be tidied once we figure out how the whole
21499a747e4fSDavid du Colombier 	 * removeable device thing is going to work.
21509a747e4fSDavid du Colombier 	 */
21519a747e4fSDavid du Colombier 	memset(&isa, 0, sizeof(isa));
21529a747e4fSDavid du Colombier 	isa.port = 0x180;		/* change this for your machine */
21539a747e4fSDavid du Colombier 	isa.irq = 11;			/* change this for your machine */
21549a747e4fSDavid du Colombier 
21559a747e4fSDavid du Colombier 	port = isa.port+0x0C;
21569a747e4fSDavid du Colombier 	channel = pcmspecial("MK2001MPL", &isa);
21579a747e4fSDavid du Colombier 	if(channel == -1)
21589a747e4fSDavid du Colombier 		channel = pcmspecial("SunDisk", &isa);
21599a747e4fSDavid du Colombier 	if(channel == -1){
21609a747e4fSDavid du Colombier 		isa.irq = 10;
21619a747e4fSDavid du Colombier 		channel = pcmspecial("CF", &isa);
21629a747e4fSDavid du Colombier 	}
21639a747e4fSDavid du Colombier 	if(channel == -1){
21649a747e4fSDavid du Colombier 		isa.irq = 10;
21659a747e4fSDavid du Colombier 		channel = pcmspecial("OLYMPUS", &isa);
21669a747e4fSDavid du Colombier 	}
21679a747e4fSDavid du Colombier 	if(channel == -1){
21689a747e4fSDavid du Colombier 		port = isa.port+0x204;
21699a747e4fSDavid du Colombier 		channel = pcmspecial("ATA/ATAPI", &isa);
21709a747e4fSDavid du Colombier 	}
21719a747e4fSDavid du Colombier 	if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){
21729a747e4fSDavid du Colombier 		if(head != nil)
21739a747e4fSDavid du Colombier 			tail->next = sdev;
21749a747e4fSDavid du Colombier 		else
21759a747e4fSDavid du Colombier 			head = sdev;
21769a747e4fSDavid du Colombier 	}
21779a747e4fSDavid du Colombier }
21787dd7cddfSDavid du Colombier 	return head;
21797dd7cddfSDavid du Colombier }
21807dd7cddfSDavid du Colombier 
21817dd7cddfSDavid du Colombier static SDev*
atalegacy(int port,int irq)21827dd7cddfSDavid du Colombier atalegacy(int port, int irq)
21837dd7cddfSDavid du Colombier {
21847dd7cddfSDavid du Colombier 	return ataprobe(port, port+0x204, irq);
21857dd7cddfSDavid du Colombier }
21867dd7cddfSDavid du Colombier 
21877dd7cddfSDavid du Colombier static int
ataenable(SDev * sdev)21887dd7cddfSDavid du Colombier ataenable(SDev* sdev)
21897dd7cddfSDavid du Colombier {
21907dd7cddfSDavid du Colombier 	Ctlr *ctlr;
21919a747e4fSDavid du Colombier 	char name[32];
21927dd7cddfSDavid du Colombier 
21937dd7cddfSDavid du Colombier 	ctlr = sdev->ctlr;
21947dd7cddfSDavid du Colombier 
21957dd7cddfSDavid du Colombier 	if(ctlr->bmiba){
21969a747e4fSDavid du Colombier #define ALIGN	(4 * 1024)
21977dd7cddfSDavid du Colombier 		if(ctlr->pcidev != nil)
21987dd7cddfSDavid du Colombier 			pcisetbme(ctlr->pcidev);
21992839d78eSDavid du Colombier 		ctlr->prdt = mallocalign(Nprd*sizeof(Prd), 4, 0, 4*1024);
2200aa72973aSDavid du Colombier 		if(ctlr->prdt == nil)
2201aa72973aSDavid du Colombier 			error(Enomem);
22027dd7cddfSDavid du Colombier 	}
22039a747e4fSDavid du Colombier 	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
22047dd7cddfSDavid du Colombier 	intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
22057dd7cddfSDavid du Colombier 	outb(ctlr->ctlport+Dc, 0);
22067dd7cddfSDavid du Colombier 	if(ctlr->ienable)
22077dd7cddfSDavid du Colombier 		ctlr->ienable(ctlr);
22087dd7cddfSDavid du Colombier 
22097dd7cddfSDavid du Colombier 	return 1;
22107dd7cddfSDavid du Colombier }
22117dd7cddfSDavid du Colombier 
22127dd7cddfSDavid du Colombier static int
atadisable(SDev * sdev)22139a747e4fSDavid du Colombier atadisable(SDev *sdev)
22149a747e4fSDavid du Colombier {
22159a747e4fSDavid du Colombier 	Ctlr *ctlr;
22169a747e4fSDavid du Colombier 	char name[32];
22179a747e4fSDavid du Colombier 
22189a747e4fSDavid du Colombier 	ctlr = sdev->ctlr;
22199a747e4fSDavid du Colombier 	outb(ctlr->ctlport+Dc, Nien);		/* disable interrupts */
22209a747e4fSDavid du Colombier 	if (ctlr->idisable)
22219a747e4fSDavid du Colombier 		ctlr->idisable(ctlr);
22229a747e4fSDavid du Colombier 	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
22239a747e4fSDavid du Colombier 	intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
22249a747e4fSDavid du Colombier 	if (ctlr->bmiba) {
22259a747e4fSDavid du Colombier 		if (ctlr->pcidev)
22269a747e4fSDavid du Colombier 			pciclrbme(ctlr->pcidev);
22272839d78eSDavid du Colombier 		free(ctlr->prdt);
22289a747e4fSDavid du Colombier 	}
22299a747e4fSDavid du Colombier 	return 0;
22309a747e4fSDavid du Colombier }
22319a747e4fSDavid du Colombier 
22329a747e4fSDavid du Colombier static int
atarctl(SDunit * unit,char * p,int l)22337dd7cddfSDavid du Colombier atarctl(SDunit* unit, char* p, int l)
22347dd7cddfSDavid du Colombier {
22357dd7cddfSDavid du Colombier 	int n;
22367dd7cddfSDavid du Colombier 	Ctlr *ctlr;
22377dd7cddfSDavid du Colombier 	Drive *drive;
22387dd7cddfSDavid du Colombier 
22397dd7cddfSDavid du Colombier 	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
22407dd7cddfSDavid du Colombier 		return 0;
22417dd7cddfSDavid du Colombier 	drive = ctlr->drive[unit->subno];
22427dd7cddfSDavid du Colombier 
22437dd7cddfSDavid du Colombier 	qlock(drive);
22447dd7cddfSDavid du Colombier 	n = snprint(p, l, "config %4.4uX capabilities %4.4uX",
22457dd7cddfSDavid du Colombier 		drive->info[Iconfig], drive->info[Icapabilities]);
22467dd7cddfSDavid du Colombier 	if(drive->dma)
22477dd7cddfSDavid du Colombier 		n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX",
22487dd7cddfSDavid du Colombier 			drive->dma, drive->dmactl);
22497dd7cddfSDavid du Colombier 	if(drive->rwm)
22507dd7cddfSDavid du Colombier 		n += snprint(p+n, l-n, " rwm %ud rwmctl %ud",
22517dd7cddfSDavid du Colombier 			drive->rwm, drive->rwmctl);
22526a081dcdSDavid du Colombier 	if(drive->flags&Lba48)
22536a081dcdSDavid du Colombier 		n += snprint(p+n, l-n, " lba48always %s",
22546a081dcdSDavid du Colombier 			(drive->flags&Lba48always) ? "on" : "off");
22557dd7cddfSDavid du Colombier 	n += snprint(p+n, l-n, "\n");
22562f2115d0SDavid du Colombier 	n += snprint(p+n, l-n, "interrupts read %lud write %lud cmds %lud\n",
22572f2115d0SDavid du Colombier 		drive->intrd, drive->intwr, drive->intcmd);
22586a081dcdSDavid du Colombier 	if(drive->sectors){
22596a081dcdSDavid du Colombier 		n += snprint(p+n, l-n, "geometry %lld %d",
22606a081dcdSDavid du Colombier 			drive->sectors, drive->secsize);
22617dd7cddfSDavid du Colombier 		if(drive->pkt == 0)
22627dd7cddfSDavid du Colombier 			n += snprint(p+n, l-n, " %d %d %d",
22637dd7cddfSDavid du Colombier 				drive->c, drive->h, drive->s);
22647dd7cddfSDavid du Colombier 		n += snprint(p+n, l-n, "\n");
22657dd7cddfSDavid du Colombier 	}
22667dd7cddfSDavid du Colombier 	qunlock(drive);
22677dd7cddfSDavid du Colombier 
22687dd7cddfSDavid du Colombier 	return n;
22697dd7cddfSDavid du Colombier }
22707dd7cddfSDavid du Colombier 
22717dd7cddfSDavid du Colombier static int
atawctl(SDunit * unit,Cmdbuf * cb)22727dd7cddfSDavid du Colombier atawctl(SDunit* unit, Cmdbuf* cb)
22737dd7cddfSDavid du Colombier {
227480ee5cbfSDavid du Colombier 	int period;
22757dd7cddfSDavid du Colombier 	Ctlr *ctlr;
22767dd7cddfSDavid du Colombier 	Drive *drive;
22777dd7cddfSDavid du Colombier 
22787dd7cddfSDavid du Colombier 	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
22797dd7cddfSDavid du Colombier 		return 0;
22807dd7cddfSDavid du Colombier 	drive = ctlr->drive[unit->subno];
22817dd7cddfSDavid du Colombier 
22827dd7cddfSDavid du Colombier 	qlock(drive);
22837dd7cddfSDavid du Colombier 	if(waserror()){
22847dd7cddfSDavid du Colombier 		qunlock(drive);
22857dd7cddfSDavid du Colombier 		nexterror();
22867dd7cddfSDavid du Colombier 	}
22877dd7cddfSDavid du Colombier 
22887dd7cddfSDavid du Colombier 	/*
22897dd7cddfSDavid du Colombier 	 * Dma and rwm control is passive at the moment,
22907dd7cddfSDavid du Colombier 	 * i.e. it is assumed that the hardware is set up
22917dd7cddfSDavid du Colombier 	 * correctly already either by the BIOS or when
22927dd7cddfSDavid du Colombier 	 * the drive was initially identified.
22937dd7cddfSDavid du Colombier 	 */
22947dd7cddfSDavid du Colombier 	if(strcmp(cb->f[0], "dma") == 0){
22957dd7cddfSDavid du Colombier 		if(cb->nf != 2 || drive->dma == 0)
22967dd7cddfSDavid du Colombier 			error(Ebadctl);
22977dd7cddfSDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
22987dd7cddfSDavid du Colombier 			drive->dmactl = drive->dma;
22997dd7cddfSDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
23007dd7cddfSDavid du Colombier 			drive->dmactl = 0;
23017dd7cddfSDavid du Colombier 		else
23027dd7cddfSDavid du Colombier 			error(Ebadctl);
23037dd7cddfSDavid du Colombier 	}
23047dd7cddfSDavid du Colombier 	else if(strcmp(cb->f[0], "rwm") == 0){
23057dd7cddfSDavid du Colombier 		if(cb->nf != 2 || drive->rwm == 0)
23067dd7cddfSDavid du Colombier 			error(Ebadctl);
23077dd7cddfSDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
23087dd7cddfSDavid du Colombier 			drive->rwmctl = drive->rwm;
23097dd7cddfSDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
23107dd7cddfSDavid du Colombier 			drive->rwmctl = 0;
23117dd7cddfSDavid du Colombier 		else
23127dd7cddfSDavid du Colombier 			error(Ebadctl);
23137dd7cddfSDavid du Colombier 	}
231480ee5cbfSDavid du Colombier 	else if(strcmp(cb->f[0], "standby") == 0){
231580ee5cbfSDavid du Colombier 		switch(cb->nf){
231680ee5cbfSDavid du Colombier 		default:
231780ee5cbfSDavid du Colombier 			error(Ebadctl);
231880ee5cbfSDavid du Colombier 		case 2:
231980ee5cbfSDavid du Colombier 			period = strtol(cb->f[1], 0, 0);
232080ee5cbfSDavid du Colombier 			if(period && (period < 30 || period > 240*5))
232180ee5cbfSDavid du Colombier 				error(Ebadctl);
232280ee5cbfSDavid du Colombier 			period /= 5;
232380ee5cbfSDavid du Colombier 			break;
232480ee5cbfSDavid du Colombier 		}
232580ee5cbfSDavid du Colombier 		if(atastandby(drive, period) != SDok)
232680ee5cbfSDavid du Colombier 			error(Ebadctl);
232780ee5cbfSDavid du Colombier 	}
23286a081dcdSDavid du Colombier 	else if(strcmp(cb->f[0], "lba48always") == 0){
23296a081dcdSDavid du Colombier 		if(cb->nf != 2 || !(drive->flags&Lba48))
23306a081dcdSDavid du Colombier 			error(Ebadctl);
23316a081dcdSDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
23326a081dcdSDavid du Colombier 			drive->flags |= Lba48always;
23336a081dcdSDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
23346a081dcdSDavid du Colombier 			drive->flags &= ~Lba48always;
23356a081dcdSDavid du Colombier 		else
23366a081dcdSDavid du Colombier 			error(Ebadctl);
23376a081dcdSDavid du Colombier 	}
23387dd7cddfSDavid du Colombier 	else
23397dd7cddfSDavid du Colombier 		error(Ebadctl);
23407dd7cddfSDavid du Colombier 	qunlock(drive);
23417dd7cddfSDavid du Colombier 	poperror();
23427dd7cddfSDavid du Colombier 
23437dd7cddfSDavid du Colombier 	return 0;
23447dd7cddfSDavid du Colombier }
23457dd7cddfSDavid du Colombier 
23467dd7cddfSDavid du Colombier SDifc sdataifc = {
23477dd7cddfSDavid du Colombier 	"ata",				/* name */
23487dd7cddfSDavid du Colombier 
23497dd7cddfSDavid du Colombier 	atapnp,				/* pnp */
23507dd7cddfSDavid du Colombier 	atalegacy,			/* legacy */
23517dd7cddfSDavid du Colombier 	ataenable,			/* enable */
23529a747e4fSDavid du Colombier 	atadisable,			/* disable */
23537dd7cddfSDavid du Colombier 
23547dd7cddfSDavid du Colombier 	scsiverify,			/* verify */
23557dd7cddfSDavid du Colombier 	scsionline,			/* online */
23567dd7cddfSDavid du Colombier 	atario,				/* rio */
23577dd7cddfSDavid du Colombier 	atarctl,			/* rctl */
23587dd7cddfSDavid du Colombier 	atawctl,			/* wctl */
23597dd7cddfSDavid du Colombier 
23607dd7cddfSDavid du Colombier 	scsibio,			/* bio */
23619a747e4fSDavid du Colombier 	ataprobew,			/* probe */
23629a747e4fSDavid du Colombier 	ataclear,			/* clear */
2363d649fdd7SDavid du Colombier 	atastat,			/* rtopctl */
2364d649fdd7SDavid du Colombier 	nil,				/* wtopctl */
23657dd7cddfSDavid du Colombier };
2366