xref: /plan9/sys/src/9/pc/sdata.c (revision 1a8a6b0d994d08a990732c4fe0c170c664ab1e2c)
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 
124de34a7eSDavid du Colombier #define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
134de34a7eSDavid du Colombier #define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
144de34a7eSDavid du Colombier 
157dd7cddfSDavid du Colombier extern SDifc sdataifc;
167dd7cddfSDavid du Colombier 
177dd7cddfSDavid du Colombier enum {
18503d3e0aSDavid du Colombier 	DbgCONFIG	= 0x0001,	/* detected drive config info */
19503d3e0aSDavid du Colombier 	DbgIDENTIFY	= 0x0002,	/* detected drive identify info */
20503d3e0aSDavid du Colombier 	DbgSTATE	= 0x0004,	/* dump state on panic */
21503d3e0aSDavid du Colombier 	DbgPROBE	= 0x0008,	/* trace device probing */
22503d3e0aSDavid du Colombier 	DbgDEBUG	= 0x0080,	/* the current problem... */
23503d3e0aSDavid du Colombier 	DbgINL		= 0x0100,	/* That Inil20+ message we hate */
24503d3e0aSDavid du Colombier 	Dbg48BIT	= 0x0200,	/* 48-bit LBA */
25503d3e0aSDavid du Colombier 	DbgBsy		= 0x0400,	/* interrupt but Bsy (shared IRQ) */
267dd7cddfSDavid du Colombier };
273ff48bf5SDavid du Colombier #define DEBUG		(DbgDEBUG|DbgSTATE)
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier enum {					/* I/O ports */
307dd7cddfSDavid du Colombier 	Data		= 0,
317dd7cddfSDavid du Colombier 	Error		= 1,		/* (read) */
327dd7cddfSDavid du Colombier 	Features	= 1,		/* (write) */
336a081dcdSDavid du Colombier 	Count		= 2,		/* sector count<7-0>, sector count<15-8> */
347dd7cddfSDavid du Colombier 	Ir		= 2,		/* interrupt reason (PACKET) */
356a081dcdSDavid du Colombier 	Sector		= 3,		/* sector number */
366a081dcdSDavid du Colombier 	Lbalo		= 3,		/* LBA<7-0>, LBA<31-24> */
376a081dcdSDavid du Colombier 	Cyllo		= 4,		/* cylinder low */
387dd7cddfSDavid du Colombier 	Bytelo		= 4,		/* byte count low (PACKET) */
396a081dcdSDavid du Colombier 	Lbamid		= 4,		/* LBA<15-8>, LBA<39-32> */
406a081dcdSDavid du Colombier 	Cylhi		= 5,		/* cylinder high */
417dd7cddfSDavid du Colombier 	Bytehi		= 5,		/* byte count hi (PACKET) */
426a081dcdSDavid du Colombier 	Lbahi		= 5,		/* LBA<23-16>, LBA<47-40> */
436a5dc222SDavid du Colombier 	Dh		= 6,		/* Device/Head, LBA<27-24> */
447dd7cddfSDavid du Colombier 	Status		= 7,		/* (read) */
457dd7cddfSDavid du Colombier 	Command		= 7,		/* (write) */
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier 	As		= 2,		/* Alternate Status (read) */
487dd7cddfSDavid du Colombier 	Dc		= 2,		/* Device Control (write) */
497dd7cddfSDavid du Colombier };
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier enum {					/* Error */
527dd7cddfSDavid du Colombier 	Med		= 0x01,		/* Media error */
537dd7cddfSDavid du Colombier 	Ili		= 0x01,		/* command set specific (PACKET) */
547dd7cddfSDavid du Colombier 	Nm		= 0x02,		/* No Media */
557dd7cddfSDavid du Colombier 	Eom		= 0x02,		/* command set specific (PACKET) */
567dd7cddfSDavid du Colombier 	Abrt		= 0x04,		/* Aborted command */
577dd7cddfSDavid du Colombier 	Mcr		= 0x08,		/* Media Change Request */
587dd7cddfSDavid du Colombier 	Idnf		= 0x10,		/* no user-accessible address */
597dd7cddfSDavid du Colombier 	Mc		= 0x20,		/* Media Change */
607dd7cddfSDavid du Colombier 	Unc		= 0x40,		/* Uncorrectable data error */
617dd7cddfSDavid du Colombier 	Wp		= 0x40,		/* Write Protect */
627dd7cddfSDavid du Colombier 	Icrc		= 0x80,		/* Interface CRC error */
637dd7cddfSDavid du Colombier };
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier enum {					/* Features */
667dd7cddfSDavid du Colombier 	Dma		= 0x01,		/* data transfer via DMA (PACKET) */
677dd7cddfSDavid du Colombier 	Ovl		= 0x02,		/* command overlapped (PACKET) */
687dd7cddfSDavid du Colombier };
697dd7cddfSDavid du Colombier 
707dd7cddfSDavid du Colombier enum {					/* Interrupt Reason */
717dd7cddfSDavid du Colombier 	Cd		= 0x01,		/* Command/Data */
727dd7cddfSDavid du Colombier 	Io		= 0x02,		/* I/O direction */
737dd7cddfSDavid du Colombier 	Rel		= 0x04,		/* Bus Release */
747dd7cddfSDavid du Colombier };
757dd7cddfSDavid du Colombier 
767dd7cddfSDavid du Colombier enum {					/* Device/Head */
777dd7cddfSDavid du Colombier 	Dev0		= 0xA0,		/* Master */
787dd7cddfSDavid du Colombier 	Dev1		= 0xB0,		/* Slave */
797dd7cddfSDavid du Colombier 	Lba		= 0x40,		/* LBA mode */
806a081dcdSDavid du Colombier };
816a081dcdSDavid du Colombier 
827dd7cddfSDavid du Colombier enum {					/* Status, Alternate Status */
837dd7cddfSDavid du Colombier 	Err		= 0x01,		/* Error */
847dd7cddfSDavid du Colombier 	Chk		= 0x01,		/* Check error (PACKET) */
857dd7cddfSDavid du Colombier 	Drq		= 0x08,		/* Data Request */
867dd7cddfSDavid du Colombier 	Dsc		= 0x10,		/* Device Seek Complete */
877dd7cddfSDavid du Colombier 	Serv		= 0x10,		/* Service */
887dd7cddfSDavid du Colombier 	Df		= 0x20,		/* Device Fault */
897dd7cddfSDavid du Colombier 	Dmrd		= 0x20,		/* DMA ready (PACKET) */
907dd7cddfSDavid du Colombier 	Drdy		= 0x40,		/* Device Ready */
917dd7cddfSDavid du Colombier 	Bsy		= 0x80,		/* Busy */
927dd7cddfSDavid du Colombier };
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier enum {					/* Command */
957dd7cddfSDavid du Colombier 	Cnop		= 0x00,		/* NOP */
967dd7cddfSDavid du Colombier 	Cdr		= 0x08,		/* Device Reset */
977dd7cddfSDavid du Colombier 	Crs		= 0x20,		/* Read Sectors */
986a081dcdSDavid du Colombier 	Crs48		= 0x24,		/* Read Sectors Ext */
996a081dcdSDavid du Colombier 	Crd48		= 0x25,		/* Read w/ DMA Ext */
1006a081dcdSDavid du Colombier 	Crdq48		= 0x26,		/* Read w/ DMA Queued Ext */
1016a081dcdSDavid du Colombier 	Crsm48		= 0x29,		/* Read Multiple Ext */
1027dd7cddfSDavid du Colombier 	Cws		= 0x30,		/* Write Sectors */
1036a081dcdSDavid du Colombier 	Cws48		= 0x34,		/* Write Sectors Ext */
1046a081dcdSDavid du Colombier 	Cwd48		= 0x35,		/* Write w/ DMA Ext */
1056a081dcdSDavid du Colombier 	Cwdq48		= 0x36,		/* Write w/ DMA Queued Ext */
1066a081dcdSDavid du Colombier 	Cwsm48		= 0x39,		/* Write Multiple Ext */
1077dd7cddfSDavid du Colombier 	Cedd		= 0x90,		/* Execute Device Diagnostics */
1087dd7cddfSDavid du Colombier 	Cpkt		= 0xA0,		/* Packet */
1097dd7cddfSDavid du Colombier 	Cidpkt		= 0xA1,		/* Identify Packet Device */
1107dd7cddfSDavid du Colombier 	Crsm		= 0xC4,		/* Read Multiple */
1117dd7cddfSDavid du Colombier 	Cwsm		= 0xC5,		/* Write Multiple */
1127dd7cddfSDavid du Colombier 	Csm		= 0xC6,		/* Set Multiple */
1137dd7cddfSDavid du Colombier 	Crdq		= 0xC7,		/* Read DMA queued */
1147dd7cddfSDavid du Colombier 	Crd		= 0xC8,		/* Read DMA */
1157dd7cddfSDavid du Colombier 	Cwd		= 0xCA,		/* Write DMA */
1167dd7cddfSDavid du Colombier 	Cwdq		= 0xCC,		/* Write DMA queued */
11780ee5cbfSDavid du Colombier 	Cstandby	= 0xE2,		/* Standby */
1187dd7cddfSDavid du Colombier 	Cid		= 0xEC,		/* Identify Device */
1197dd7cddfSDavid du Colombier 	Csf		= 0xEF,		/* Set Features */
1207dd7cddfSDavid du Colombier };
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier enum {					/* Device Control */
1237dd7cddfSDavid du Colombier 	Nien		= 0x02,		/* (not) Interrupt Enable */
1247dd7cddfSDavid du Colombier 	Srst		= 0x04,		/* Software Reset */
1256a081dcdSDavid du Colombier 	Hob		= 0x80,		/* High Order Bit [sic] */
1267dd7cddfSDavid du Colombier };
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier enum {					/* PCI Configuration Registers */
1297dd7cddfSDavid du Colombier 	Bmiba		= 0x20,		/* Bus Master Interface Base Address */
1307dd7cddfSDavid du Colombier 	Idetim		= 0x40,		/* IE Timing */
1317dd7cddfSDavid du Colombier 	Sidetim		= 0x44,		/* Slave IE Timing */
1327dd7cddfSDavid du Colombier 	Udmactl		= 0x48,		/* Ultra DMA/33 Control */
1337dd7cddfSDavid du Colombier 	Udmatim		= 0x4A,		/* Ultra DMA/33 Timing */
1347dd7cddfSDavid du Colombier };
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier enum {					/* Bus Master IDE I/O Ports */
1377dd7cddfSDavid du Colombier 	Bmicx		= 0,		/* Command */
1387dd7cddfSDavid du Colombier 	Bmisx		= 2,		/* Status */
1397dd7cddfSDavid du Colombier 	Bmidtpx		= 4,		/* Descriptor Table Pointer */
1407dd7cddfSDavid du Colombier };
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier enum {					/* Bmicx */
1437dd7cddfSDavid du Colombier 	Ssbm		= 0x01,		/* Start/Stop Bus Master */
1447dd7cddfSDavid du Colombier 	Rwcon		= 0x08,		/* Read/Write Control */
1457dd7cddfSDavid du Colombier };
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier enum {					/* Bmisx */
1487dd7cddfSDavid du Colombier 	Bmidea		= 0x01,		/* Bus Master IDE Active */
1497dd7cddfSDavid du Colombier 	Idedmae		= 0x02,		/* IDE DMA Error  (R/WC) */
1507dd7cddfSDavid du Colombier 	Ideints		= 0x04,		/* IDE Interrupt Status (R/WC) */
1517dd7cddfSDavid du Colombier 	Dma0cap		= 0x20,		/* Drive 0 DMA Capable */
1527dd7cddfSDavid du Colombier 	Dma1cap		= 0x40,		/* Drive 0 DMA Capable */
1537dd7cddfSDavid du Colombier };
1547dd7cddfSDavid du Colombier enum {					/* Physical Region Descriptor */
1552839d78eSDavid du Colombier 	PrdEOT		= 0x80000000,	/* End of Transfer */
1567dd7cddfSDavid du Colombier };
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier enum {					/* offsets into the identify info. */
1597dd7cddfSDavid du Colombier 	Iconfig		= 0,		/* general configuration */
1607dd7cddfSDavid du Colombier 	Ilcyl		= 1,		/* logical cylinders */
1617dd7cddfSDavid du Colombier 	Ilhead		= 3,		/* logical heads */
1627dd7cddfSDavid du Colombier 	Ilsec		= 6,		/* logical sectors per logical track */
1637dd7cddfSDavid du Colombier 	Iserial		= 10,		/* serial number */
1647dd7cddfSDavid du Colombier 	Ifirmware	= 23,		/* firmware revision */
1657dd7cddfSDavid du Colombier 	Imodel		= 27,		/* model number */
1667dd7cddfSDavid du Colombier 	Imaxrwm		= 47,		/* max. read/write multiple sectors */
1677dd7cddfSDavid du Colombier 	Icapabilities	= 49,		/* capabilities */
1687dd7cddfSDavid du Colombier 	Istandby	= 50,		/* device specific standby timer */
1697dd7cddfSDavid du Colombier 	Ipiomode	= 51,		/* PIO data transfer mode number */
1707dd7cddfSDavid du Colombier 	Ivalid		= 53,
1717dd7cddfSDavid du Colombier 	Iccyl		= 54,		/* cylinders if (valid&0x01) */
1727dd7cddfSDavid du Colombier 	Ichead		= 55,		/* heads if (valid&0x01) */
1737dd7cddfSDavid du Colombier 	Icsec		= 56,		/* sectors if (valid&0x01) */
1747dd7cddfSDavid du Colombier 	Iccap		= 57,		/* capacity if (valid&0x01) */
1757dd7cddfSDavid du Colombier 	Irwm		= 59,		/* read/write multiple */
1766a081dcdSDavid du Colombier 	Ilba		= 60,		/* LBA size */
1777dd7cddfSDavid du Colombier 	Imwdma		= 63,		/* multiword DMA mode */
1787dd7cddfSDavid du Colombier 	Iapiomode	= 64,		/* advanced PIO modes supported */
1797dd7cddfSDavid du Colombier 	Iminmwdma	= 65,		/* min. multiword DMA cycle time */
1807dd7cddfSDavid du Colombier 	Irecmwdma	= 66,		/* rec. multiword DMA cycle time */
1817dd7cddfSDavid du Colombier 	Iminpio		= 67,		/* min. PIO cycle w/o flow control */
1827dd7cddfSDavid du Colombier 	Iminiordy	= 68,		/* min. PIO cycle with IORDY */
1837dd7cddfSDavid du Colombier 	Ipcktbr		= 71,		/* time from PACKET to bus release */
1847dd7cddfSDavid du Colombier 	Iserbsy		= 72,		/* time from SERVICE to !Bsy */
1857dd7cddfSDavid du Colombier 	Iqdepth		= 75,		/* max. queue depth */
1867dd7cddfSDavid du Colombier 	Imajor		= 80,		/* major version number */
1877dd7cddfSDavid du Colombier 	Iminor		= 81,		/* minor version number */
18880ee5cbfSDavid du Colombier 	Icsfs		= 82,		/* command set/feature supported */
18980ee5cbfSDavid du Colombier 	Icsfe		= 85,		/* command set/feature enabled */
1907dd7cddfSDavid du Colombier 	Iudma		= 88,		/* ultra DMA mode */
1917dd7cddfSDavid du Colombier 	Ierase		= 89,		/* time for security erase */
1927dd7cddfSDavid du Colombier 	Ieerase		= 90,		/* time for enhanced security erase */
1937dd7cddfSDavid du Colombier 	Ipower		= 91,		/* current advanced power management */
1946a081dcdSDavid du Colombier 	Ilba48		= 100,		/* 48-bit LBA size (64 bits in 100-103) */
1957dd7cddfSDavid du Colombier 	Irmsn		= 127,		/* removable status notification */
1966a081dcdSDavid du Colombier 	Isecstat	= 128,		/* security status */
1976a081dcdSDavid du Colombier 	Icfapwr		= 160,		/* CFA power mode */
1986a081dcdSDavid du Colombier 	Imediaserial	= 176,		/* current media serial number */
1996a081dcdSDavid du Colombier 	Icksum		= 255,		/* checksum */
2006a081dcdSDavid du Colombier };
2016a081dcdSDavid du Colombier 
2026a081dcdSDavid du Colombier enum {					/* bit masks for config identify info */
2036a081dcdSDavid du Colombier 	Mpktsz		= 0x0003,	/* packet command size */
2046a081dcdSDavid du Colombier 	Mincomplete	= 0x0004,	/* incomplete information */
2056a081dcdSDavid du Colombier 	Mdrq		= 0x0060,	/* DRQ type */
2066a081dcdSDavid du Colombier 	Mrmdev		= 0x0080,	/* device is removable */
2076a081dcdSDavid du Colombier 	Mtype		= 0x1F00,	/* device type */
2086a081dcdSDavid du Colombier 	Mproto		= 0x8000,	/* command protocol */
2096a081dcdSDavid du Colombier };
2106a081dcdSDavid du Colombier 
2116a081dcdSDavid du Colombier enum {					/* bit masks for capabilities identify info */
2126a081dcdSDavid du Colombier 	Mdma		= 0x0100,	/* DMA supported */
2136a081dcdSDavid du Colombier 	Mlba		= 0x0200,	/* LBA supported */
2146a081dcdSDavid du Colombier 	Mnoiordy	= 0x0400,	/* IORDY may be disabled */
2156a081dcdSDavid du Colombier 	Miordy		= 0x0800,	/* IORDY supported */
2166a081dcdSDavid du Colombier 	Msoftrst	= 0x1000,	/* needs soft reset when Bsy */
2176a081dcdSDavid du Colombier 	Mstdby		= 0x2000,	/* standby supported */
2186a081dcdSDavid du Colombier 	Mqueueing	= 0x4000,	/* queueing overlap supported */
2196a081dcdSDavid du Colombier 	Midma		= 0x8000,	/* interleaved DMA supported */
2206a081dcdSDavid du Colombier };
2216a081dcdSDavid du Colombier 
2226a081dcdSDavid du Colombier enum {					/* bit masks for supported/enabled features */
2236a081dcdSDavid du Colombier 	Msmart		= 0x0001,
2246a081dcdSDavid du Colombier 	Msecurity	= 0x0002,
2256a081dcdSDavid du Colombier 	Mrmmedia	= 0x0004,
2266a081dcdSDavid du Colombier 	Mpwrmgmt	= 0x0008,
2276a081dcdSDavid du Colombier 	Mpkt		= 0x0010,
2286a081dcdSDavid du Colombier 	Mwcache		= 0x0020,
2296a081dcdSDavid du Colombier 	Mlookahead	= 0x0040,
2306a081dcdSDavid du Colombier 	Mrelirq		= 0x0080,
2316a081dcdSDavid du Colombier 	Msvcirq		= 0x0100,
2326a081dcdSDavid du Colombier 	Mreset		= 0x0200,
2336a081dcdSDavid du Colombier 	Mprotected	= 0x0400,
2346a081dcdSDavid du Colombier 	Mwbuf		= 0x1000,
2356a081dcdSDavid du Colombier 	Mrbuf		= 0x2000,
2366a081dcdSDavid du Colombier 	Mnop		= 0x4000,
2376a081dcdSDavid du Colombier 	Mmicrocode	= 0x0001,
2386a081dcdSDavid du Colombier 	Mqueued		= 0x0002,
2396a081dcdSDavid du Colombier 	Mcfa		= 0x0004,
2406a081dcdSDavid du Colombier 	Mapm		= 0x0008,
2416a081dcdSDavid du Colombier 	Mnotify		= 0x0010,
2426a081dcdSDavid du Colombier 	Mstandby	= 0x0020,
2436a081dcdSDavid du Colombier 	Mspinup		= 0x0040,
2446a081dcdSDavid du Colombier 	Mmaxsec		= 0x0100,
2456a081dcdSDavid du Colombier 	Mautoacoustic	= 0x0200,
2466a081dcdSDavid du Colombier 	Maddr48		= 0x0400,
2476a081dcdSDavid du Colombier 	Mdevconfov	= 0x0800,
2486a081dcdSDavid du Colombier 	Mflush		= 0x1000,
2496a081dcdSDavid du Colombier 	Mflush48	= 0x2000,
2506a081dcdSDavid du Colombier 	Msmarterror	= 0x0001,
2516a081dcdSDavid du Colombier 	Msmartselftest	= 0x0002,
2526a081dcdSDavid du Colombier 	Mmserial	= 0x0004,
2536a081dcdSDavid du Colombier 	Mmpassthru	= 0x0008,
2546a081dcdSDavid du Colombier 	Mlogging	= 0x0020,
2557dd7cddfSDavid du Colombier };
2567dd7cddfSDavid du Colombier 
2577dd7cddfSDavid du Colombier typedef struct Ctlr Ctlr;
2587dd7cddfSDavid du Colombier typedef struct Drive Drive;
2597dd7cddfSDavid du Colombier 
2604de34a7eSDavid du Colombier typedef struct Prd {			/* Physical Region Descriptor */
2617dd7cddfSDavid du Colombier 	ulong	pa;			/* Physical Base Address */
2627dd7cddfSDavid du Colombier 	int	count;
2637dd7cddfSDavid du Colombier } Prd;
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier enum {
2664de34a7eSDavid du Colombier 	BMspan		= 64*1024,	/* must be power of 2 <= 64*1024 */
2674de34a7eSDavid du Colombier 
2684de34a7eSDavid du Colombier 	Nprd		= SDmaxio/BMspan+2,
2697dd7cddfSDavid du Colombier };
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier typedef struct Ctlr {
2727dd7cddfSDavid du Colombier 	int	cmdport;
2737dd7cddfSDavid du Colombier 	int	ctlport;
2747dd7cddfSDavid du Colombier 	int	irq;
2757dd7cddfSDavid du Colombier 	int	tbdf;
2767dd7cddfSDavid du Colombier 	int	bmiba;			/* bus master interface base address */
2774de34a7eSDavid du Colombier 	int	maxio;			/* sector count transfer maximum */
2784de34a7eSDavid du Colombier 	int	span;			/* don't span this boundary with dma */
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier 	Pcidev*	pcidev;
2817dd7cddfSDavid du Colombier 	void	(*ienable)(Ctlr*);
2829a747e4fSDavid du Colombier 	void	(*idisable)(Ctlr*);
2837dd7cddfSDavid du Colombier 	SDev*	sdev;
2847dd7cddfSDavid du Colombier 
2857dd7cddfSDavid du Colombier 	Drive*	drive[2];
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier 	Prd*	prdt;			/* physical region descriptor table */
2887dd7cddfSDavid du Colombier 
2897dd7cddfSDavid du Colombier 	QLock;				/* current command */
2907dd7cddfSDavid du Colombier 	Drive*	curdrive;
2917dd7cddfSDavid du Colombier 	int	command;		/* last command issued (debugging) */
2927dd7cddfSDavid du Colombier 	Rendez;
2937dd7cddfSDavid du Colombier 	int	done;
2947dd7cddfSDavid du Colombier 
2957dd7cddfSDavid du Colombier 	Lock;				/* register access */
2967dd7cddfSDavid du Colombier } Ctlr;
2977dd7cddfSDavid du Colombier 
2987dd7cddfSDavid du Colombier typedef struct Drive {
2997dd7cddfSDavid du Colombier 	Ctlr*	ctlr;
3007dd7cddfSDavid du Colombier 
3017dd7cddfSDavid du Colombier 	int	dev;
3027dd7cddfSDavid du Colombier 	ushort	info[256];
3037dd7cddfSDavid du Colombier 	int	c;			/* cylinder */
3047dd7cddfSDavid du Colombier 	int	h;			/* head */
3057dd7cddfSDavid du Colombier 	int	s;			/* sector */
3065ea8af7bSDavid du Colombier 	vlong	sectors;		/* total */
3077dd7cddfSDavid du Colombier 	int	secsize;		/* sector size */
3087dd7cddfSDavid du Colombier 
3097dd7cddfSDavid du Colombier 	int	dma;			/* DMA R/W possible */
3107dd7cddfSDavid du Colombier 	int	dmactl;
3117dd7cddfSDavid du Colombier 	int	rwm;			/* read/write multiple possible */
3127dd7cddfSDavid du Colombier 	int	rwmctl;
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier 	int	pkt;			/* PACKET device, length of pktcmd */
3157dd7cddfSDavid du Colombier 	uchar	pktcmd[16];
3167dd7cddfSDavid du Colombier 	int	pktdma;			/* this PACKET command using dma */
3177dd7cddfSDavid du Colombier 
3187dd7cddfSDavid du Colombier 	uchar	sense[18];
3197dd7cddfSDavid du Colombier 	uchar	inquiry[48];
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier 	QLock;				/* drive access */
3227dd7cddfSDavid du Colombier 	int	command;		/* current command */
3237dd7cddfSDavid du Colombier 	int	write;
3247dd7cddfSDavid du Colombier 	uchar*	data;
3257dd7cddfSDavid du Colombier 	int	dlen;
3267dd7cddfSDavid du Colombier 	uchar*	limit;
3277dd7cddfSDavid du Colombier 	int	count;			/* sectors */
3287dd7cddfSDavid du Colombier 	int	block;			/* R/W bytes per block */
3297dd7cddfSDavid du Colombier 	int	status;
3307dd7cddfSDavid du Colombier 	int	error;
3316a081dcdSDavid du Colombier 	int	flags;			/* internal flags */
3327dd7cddfSDavid du Colombier } Drive;
3337dd7cddfSDavid du Colombier 
33459f4c0c1SDavid du Colombier enum {					/* internal flags */
33559f4c0c1SDavid du Colombier 	Lba48		= 0x1,		/* LBA48 mode */
33659f4c0c1SDavid du Colombier 	Lba48always	= 0x2,		/* ... */
33759f4c0c1SDavid du Colombier };
33891157df7SDavid du Colombier enum {
33991157df7SDavid du Colombier 	Last28		= (1<<28) - 1 - 1, /* all-ones mask is not addressible */
34091157df7SDavid du Colombier };
34159f4c0c1SDavid du Colombier 
3427dd7cddfSDavid du Colombier static void
3437dd7cddfSDavid du Colombier pc87415ienable(Ctlr* ctlr)
3447dd7cddfSDavid du Colombier {
3457dd7cddfSDavid du Colombier 	Pcidev *p;
3467dd7cddfSDavid du Colombier 	int x;
3477dd7cddfSDavid du Colombier 
3487dd7cddfSDavid du Colombier 	p = ctlr->pcidev;
3497dd7cddfSDavid du Colombier 	if(p == nil)
3507dd7cddfSDavid du Colombier 		return;
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 	x = pcicfgr32(p, 0x40);
3537dd7cddfSDavid du Colombier 	if(ctlr->cmdport == p->mem[0].bar)
3547dd7cddfSDavid du Colombier 		x &= ~0x00000100;
3557dd7cddfSDavid du Colombier 	else
3567dd7cddfSDavid du Colombier 		x &= ~0x00000200;
3577dd7cddfSDavid du Colombier 	pcicfgw32(p, 0x40, x);
3587dd7cddfSDavid du Colombier }
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier static void
3616a081dcdSDavid du Colombier atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count)
3627dd7cddfSDavid du Colombier {
3637dd7cddfSDavid du Colombier 	Prd *prd;
3647dd7cddfSDavid du Colombier 	Pcidev *p;
3657dd7cddfSDavid du Colombier 	Ctlr *ctlr;
3667dd7cddfSDavid du Colombier 	int i, bmiba;
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 	if(!(DEBUG & DbgSTATE)){
3697dd7cddfSDavid du Colombier 		USED(drive, cmd, lba, count);
3707dd7cddfSDavid du Colombier 		return;
3717dd7cddfSDavid du Colombier 	}
3727dd7cddfSDavid du Colombier 
3737dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
37491157df7SDavid du Colombier 	print("sdata: command %2.2uX\n", ctlr->command);
3757dd7cddfSDavid du Colombier 	print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n",
3767dd7cddfSDavid du Colombier 		drive->data, drive->limit, drive->dlen,
3777dd7cddfSDavid du Colombier 		drive->status, drive->error);
3787dd7cddfSDavid du Colombier 	if(cmd != nil){
3796a081dcdSDavid du Colombier 		print("lba %d -> %lld, count %d -> %d (%d)\n",
3807dd7cddfSDavid du Colombier 			(cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba,
3817dd7cddfSDavid du Colombier 			(cmd[7]<<8)|cmd[8], count, drive->count);
3827dd7cddfSDavid du Colombier 	}
3837dd7cddfSDavid du Colombier 	if(!(inb(ctlr->ctlport+As) & Bsy)){
3847dd7cddfSDavid du Colombier 		for(i = 1; i < 7; i++)
3857dd7cddfSDavid du Colombier 			print(" 0x%2.2uX", inb(ctlr->cmdport+i));
3867dd7cddfSDavid du Colombier 		print(" 0x%2.2uX\n", inb(ctlr->ctlport+As));
3877dd7cddfSDavid du Colombier 	}
3887dd7cddfSDavid du Colombier 	if(drive->command == Cwd || drive->command == Crd){
3897dd7cddfSDavid du Colombier 		bmiba = ctlr->bmiba;
3907dd7cddfSDavid du Colombier 		prd = ctlr->prdt;
3917dd7cddfSDavid du Colombier 		print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n",
3927dd7cddfSDavid du Colombier 			inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd);
3937dd7cddfSDavid du Colombier 		for(;;){
3947dd7cddfSDavid du Colombier 			print("pa 0x%8.8luX count %8.8uX\n",
3957dd7cddfSDavid du Colombier 				prd->pa, prd->count);
3967dd7cddfSDavid du Colombier 			if(prd->count & PrdEOT)
3977dd7cddfSDavid du Colombier 				break;
3987dd7cddfSDavid du Colombier 			prd++;
3997dd7cddfSDavid du Colombier 		}
4007dd7cddfSDavid du Colombier 	}
4017dd7cddfSDavid du Colombier 	if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){
4027dd7cddfSDavid du Colombier 		p = ctlr->pcidev;
4037dd7cddfSDavid du Colombier 		print("0x40: %4.4uX 0x42: %4.4uX",
4047dd7cddfSDavid du Colombier 			pcicfgr16(p, 0x40), pcicfgr16(p, 0x42));
4057dd7cddfSDavid du Colombier 		print("0x48: %2.2uX\n", pcicfgr8(p, 0x48));
4067dd7cddfSDavid du Colombier 		print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A));
4077dd7cddfSDavid du Colombier 	}
4087dd7cddfSDavid du Colombier }
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier static int
4117dd7cddfSDavid du Colombier atadebug(int cmdport, int ctlport, char* fmt, ...)
4127dd7cddfSDavid du Colombier {
4137dd7cddfSDavid du Colombier 	int i, n;
4147dd7cddfSDavid du Colombier 	va_list arg;
4157dd7cddfSDavid du Colombier 	char buf[PRINTSIZE];
4167dd7cddfSDavid du Colombier 
4177dd7cddfSDavid du Colombier 	if(!(DEBUG & DbgPROBE)){
4187dd7cddfSDavid du Colombier 		USED(cmdport, ctlport, fmt);
4197dd7cddfSDavid du Colombier 		return 0;
4207dd7cddfSDavid du Colombier 	}
4217dd7cddfSDavid du Colombier 
4227dd7cddfSDavid du Colombier 	va_start(arg, fmt);
4239a747e4fSDavid du Colombier 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
4247dd7cddfSDavid du Colombier 	va_end(arg);
4257dd7cddfSDavid du Colombier 
4267dd7cddfSDavid du Colombier 	if(cmdport){
4277dd7cddfSDavid du Colombier 		if(buf[n-1] == '\n')
4287dd7cddfSDavid du Colombier 			n--;
4297dd7cddfSDavid du Colombier 		n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:",
4307dd7cddfSDavid du Colombier 			cmdport);
4317dd7cddfSDavid du Colombier 		for(i = Features; i < Command; i++)
4327dd7cddfSDavid du Colombier 			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
4337dd7cddfSDavid du Colombier 				inb(cmdport+i));
4347dd7cddfSDavid du Colombier 		if(ctlport)
4357dd7cddfSDavid du Colombier 			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
4367dd7cddfSDavid du Colombier 				inb(ctlport+As));
4377dd7cddfSDavid du Colombier 		n += snprint(buf+n, PRINTSIZE-n, "\n");
4387dd7cddfSDavid du Colombier 	}
4397dd7cddfSDavid du Colombier 	putstrn(buf, n);
4407dd7cddfSDavid du Colombier 
4417dd7cddfSDavid du Colombier 	return n;
4427dd7cddfSDavid du Colombier }
4437dd7cddfSDavid du Colombier 
4447dd7cddfSDavid du Colombier static int
4457dd7cddfSDavid du Colombier ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro)
4467dd7cddfSDavid du Colombier {
4477dd7cddfSDavid du Colombier 	int as;
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 	atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX",
4507dd7cddfSDavid du Colombier 		dev, reset, ready);
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier 	for(;;){
4537dd7cddfSDavid du Colombier 		/*
4547dd7cddfSDavid du Colombier 		 * Wait for the controller to become not busy and
4557dd7cddfSDavid du Colombier 		 * possibly for a status bit to become true (usually
4567dd7cddfSDavid du Colombier 		 * Drdy). Must change to the appropriate device
4577dd7cddfSDavid du Colombier 		 * register set if necessary before testing for ready.
4587dd7cddfSDavid du Colombier 		 * Always run through the loop at least once so it
4597dd7cddfSDavid du Colombier 		 * can be used as a test for !Bsy.
4607dd7cddfSDavid du Colombier 		 */
4617dd7cddfSDavid du Colombier 		as = inb(ctlport+As);
4629a747e4fSDavid du Colombier 		if(as & reset){
4639a747e4fSDavid du Colombier 			/* nothing to do */
4649a747e4fSDavid du Colombier 		}
4657dd7cddfSDavid du Colombier 		else if(dev){
4667dd7cddfSDavid du Colombier 			outb(cmdport+Dh, dev);
4677dd7cddfSDavid du Colombier 			dev = 0;
4687dd7cddfSDavid du Colombier 		}
4697dd7cddfSDavid du Colombier 		else if(ready == 0 || (as & ready)){
4707dd7cddfSDavid du Colombier 			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
4717dd7cddfSDavid du Colombier 			return as;
4727dd7cddfSDavid du Colombier 		}
4737dd7cddfSDavid du Colombier 
4747dd7cddfSDavid du Colombier 		if(micro-- <= 0){
4757dd7cddfSDavid du Colombier 			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
4767dd7cddfSDavid du Colombier 			break;
4777dd7cddfSDavid du Colombier 		}
4787dd7cddfSDavid du Colombier 		microdelay(1);
4797dd7cddfSDavid du Colombier 	}
4807dd7cddfSDavid du Colombier 	atadebug(cmdport, ctlport, "ataready: timeout");
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier 	return -1;
4837dd7cddfSDavid du Colombier }
4847dd7cddfSDavid du Colombier 
4856a081dcdSDavid du Colombier /*
4867dd7cddfSDavid du Colombier static int
48780ee5cbfSDavid du Colombier atacsf(Drive* drive, vlong csf, int supported)
4887dd7cddfSDavid du Colombier {
48980ee5cbfSDavid du Colombier 	ushort *info;
4907dd7cddfSDavid du Colombier 	int cmdset, i, x;
4917dd7cddfSDavid du Colombier 
49280ee5cbfSDavid du Colombier 	if(supported)
49380ee5cbfSDavid du Colombier 		info = &drive->info[Icsfs];
49480ee5cbfSDavid du Colombier 	else
49580ee5cbfSDavid du Colombier 		info = &drive->info[Icsfe];
49680ee5cbfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	for(i = 0; i < 3; i++){
4987dd7cddfSDavid du Colombier 		x = (csf>>(16*i)) & 0xFFFF;
4997dd7cddfSDavid du Colombier 		if(x == 0)
5007dd7cddfSDavid du Colombier 			continue;
50180ee5cbfSDavid du Colombier 		cmdset = info[i];
5027dd7cddfSDavid du Colombier 		if(cmdset == 0 || cmdset == 0xFFFF)
5037dd7cddfSDavid du Colombier 			return 0;
5047dd7cddfSDavid du Colombier 		return cmdset & x;
5057dd7cddfSDavid du Colombier 	}
5067dd7cddfSDavid du Colombier 
5077dd7cddfSDavid du Colombier 	return 0;
5087dd7cddfSDavid du Colombier }
5096a081dcdSDavid du Colombier */
5107dd7cddfSDavid du Colombier 
5117dd7cddfSDavid du Colombier static int
51280ee5cbfSDavid du Colombier atadone(void* arg)
5137dd7cddfSDavid du Colombier {
51480ee5cbfSDavid du Colombier 	return ((Ctlr*)arg)->done;
5157dd7cddfSDavid du Colombier }
5167dd7cddfSDavid du Colombier 
5177dd7cddfSDavid du Colombier static int
5187dd7cddfSDavid du Colombier atarwmmode(Drive* drive, int cmdport, int ctlport, int dev)
5197dd7cddfSDavid du Colombier {
5207dd7cddfSDavid du Colombier 	int as, maxrwm, rwm;
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier 	maxrwm = (drive->info[Imaxrwm] & 0xFF);
5237dd7cddfSDavid du Colombier 	if(maxrwm == 0)
5247dd7cddfSDavid du Colombier 		return 0;
5257dd7cddfSDavid du Colombier 
5267dd7cddfSDavid du Colombier 	/*
5277dd7cddfSDavid du Colombier 	 * Sometimes drives come up with the current count set
5287dd7cddfSDavid du Colombier 	 * to 0; if so, set a suitable value, otherwise believe
5297dd7cddfSDavid du Colombier 	 * the value in Irwm if the 0x100 bit is set.
5307dd7cddfSDavid du Colombier 	 */
5317dd7cddfSDavid du Colombier 	if(drive->info[Irwm] & 0x100)
5327dd7cddfSDavid du Colombier 		rwm = (drive->info[Irwm] & 0xFF);
5337dd7cddfSDavid du Colombier 	else
5347dd7cddfSDavid du Colombier 		rwm = 0;
5357dd7cddfSDavid du Colombier 	if(rwm == 0)
5367dd7cddfSDavid du Colombier 		rwm = maxrwm;
5377dd7cddfSDavid du Colombier 	if(rwm > 16)
5387dd7cddfSDavid du Colombier 		rwm = 16;
5397dd7cddfSDavid du Colombier 	if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0)
5407dd7cddfSDavid du Colombier 		return 0;
5417dd7cddfSDavid du Colombier 	outb(cmdport+Count, rwm);
5427dd7cddfSDavid du Colombier 	outb(cmdport+Command, Csm);
5437dd7cddfSDavid du Colombier 	microdelay(1);
5447dd7cddfSDavid du Colombier 	as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000);
5457dd7cddfSDavid du Colombier 	inb(cmdport+Status);
5467dd7cddfSDavid du Colombier 	if(as < 0 || (as & (Df|Err)))
5477dd7cddfSDavid du Colombier 		return 0;
5487dd7cddfSDavid du Colombier 
5497dd7cddfSDavid du Colombier 	drive->rwm = rwm;
5507dd7cddfSDavid du Colombier 
5517dd7cddfSDavid du Colombier 	return rwm;
5527dd7cddfSDavid du Colombier }
5537dd7cddfSDavid du Colombier 
5547dd7cddfSDavid du Colombier static int
5557dd7cddfSDavid du Colombier atadmamode(Drive* drive)
5567dd7cddfSDavid du Colombier {
5577dd7cddfSDavid du Colombier 	int dma;
5587dd7cddfSDavid du Colombier 
5597dd7cddfSDavid du Colombier 	/*
5607dd7cddfSDavid du Colombier 	 * Check if any DMA mode enabled.
5617dd7cddfSDavid du Colombier 	 * Assumes the BIOS has picked and enabled the best.
5627dd7cddfSDavid du Colombier 	 * This is completely passive at the moment, no attempt is
5637dd7cddfSDavid du Colombier 	 * made to ensure the hardware is correctly set up.
5647dd7cddfSDavid du Colombier 	 */
5657dd7cddfSDavid du Colombier 	dma = drive->info[Imwdma] & 0x0707;
5667dd7cddfSDavid du Colombier 	drive->dma = (dma>>8) & dma;
5677dd7cddfSDavid du Colombier 	if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){
568503d3e0aSDavid du Colombier 		dma = drive->info[Iudma] & 0x7F7F;
5697dd7cddfSDavid du Colombier 		drive->dma = (dma>>8) & dma;
5707dd7cddfSDavid du Colombier 		if(drive->dma)
5717dd7cddfSDavid du Colombier 			drive->dma |= 'U'<<16;
5727dd7cddfSDavid du Colombier 	}
5737dd7cddfSDavid du Colombier 
5747dd7cddfSDavid du Colombier 	return dma;
5757dd7cddfSDavid du Colombier }
5767dd7cddfSDavid du Colombier 
5777dd7cddfSDavid du Colombier static int
5787dd7cddfSDavid du Colombier ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info)
5797dd7cddfSDavid du Colombier {
5807dd7cddfSDavid du Colombier 	int as, command, drdy;
5817dd7cddfSDavid du Colombier 
5827dd7cddfSDavid du Colombier 	if(pkt){
5837dd7cddfSDavid du Colombier 		command = Cidpkt;
5847dd7cddfSDavid du Colombier 		drdy = 0;
5857dd7cddfSDavid du Colombier 	}
5867dd7cddfSDavid du Colombier 	else{
5877dd7cddfSDavid du Colombier 		command = Cid;
5887dd7cddfSDavid du Colombier 		drdy = Drdy;
5897dd7cddfSDavid du Colombier 	}
5907dd7cddfSDavid du Colombier 	as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
5917dd7cddfSDavid du Colombier 	if(as < 0)
5927dd7cddfSDavid du Colombier 		return as;
5937dd7cddfSDavid du Colombier 	outb(cmdport+Command, command);
5947dd7cddfSDavid du Colombier 	microdelay(1);
5957dd7cddfSDavid du Colombier 
59659cc4ca5SDavid du Colombier 	as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
5977dd7cddfSDavid du Colombier 	if(as < 0)
5987dd7cddfSDavid du Colombier 		return -1;
5997dd7cddfSDavid du Colombier 	if(as & Err)
6007dd7cddfSDavid du Colombier 		return as;
6017dd7cddfSDavid du Colombier 
6027dd7cddfSDavid du Colombier 	memset(info, 0, 512);
6037dd7cddfSDavid du Colombier 	inss(cmdport+Data, info, 256);
6047dd7cddfSDavid du Colombier 	inb(cmdport+Status);
6057dd7cddfSDavid du Colombier 
6067dd7cddfSDavid du Colombier 	if(DEBUG & DbgIDENTIFY){
6077dd7cddfSDavid du Colombier 		int i;
6087dd7cddfSDavid du Colombier 		ushort *sp;
6097dd7cddfSDavid du Colombier 
6107dd7cddfSDavid du Colombier 		sp = (ushort*)info;
6117dd7cddfSDavid du Colombier 		for(i = 0; i < 256; i++){
6127dd7cddfSDavid du Colombier 			if(i && (i%16) == 0)
6137dd7cddfSDavid du Colombier 				print("\n");
6147dd7cddfSDavid du Colombier 			print(" %4.4uX", *sp);
6157dd7cddfSDavid du Colombier 			sp++;
6167dd7cddfSDavid du Colombier 		}
6177dd7cddfSDavid du Colombier 		print("\n");
6187dd7cddfSDavid du Colombier 	}
6197dd7cddfSDavid du Colombier 
6207dd7cddfSDavid du Colombier 	return 0;
6217dd7cddfSDavid du Colombier }
6227dd7cddfSDavid du Colombier 
6237dd7cddfSDavid du Colombier static Drive*
6247dd7cddfSDavid du Colombier atadrive(int cmdport, int ctlport, int dev)
6257dd7cddfSDavid du Colombier {
6267dd7cddfSDavid du Colombier 	Drive *drive;
6277dd7cddfSDavid du Colombier 	int as, i, pkt;
6287dd7cddfSDavid du Colombier 	uchar buf[512], *p;
6299a747e4fSDavid du Colombier 	ushort iconfig, *sp;
6307dd7cddfSDavid du Colombier 
6317dd7cddfSDavid du Colombier 	atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
6327dd7cddfSDavid du Colombier 	pkt = 1;
6337dd7cddfSDavid du Colombier retry:
6347dd7cddfSDavid du Colombier 	as = ataidentify(cmdport, ctlport, dev, pkt, buf);
6357dd7cddfSDavid du Colombier 	if(as < 0)
6367dd7cddfSDavid du Colombier 		return nil;
6377dd7cddfSDavid du Colombier 	if(as & Err){
6387dd7cddfSDavid du Colombier 		if(pkt == 0)
6397dd7cddfSDavid du Colombier 			return nil;
6407dd7cddfSDavid du Colombier 		pkt = 0;
6417dd7cddfSDavid du Colombier 		goto retry;
6427dd7cddfSDavid du Colombier 	}
6437dd7cddfSDavid du Colombier 
6447dd7cddfSDavid du Colombier 	if((drive = malloc(sizeof(Drive))) == nil)
6457dd7cddfSDavid du Colombier 		return nil;
6467dd7cddfSDavid du Colombier 	drive->dev = dev;
6477dd7cddfSDavid du Colombier 	memmove(drive->info, buf, sizeof(drive->info));
6487dd7cddfSDavid du Colombier 	drive->sense[0] = 0x70;
6497dd7cddfSDavid du Colombier 	drive->sense[7] = sizeof(drive->sense)-7;
6507dd7cddfSDavid du Colombier 
6517dd7cddfSDavid du Colombier 	drive->inquiry[2] = 2;
6527dd7cddfSDavid du Colombier 	drive->inquiry[3] = 2;
6537dd7cddfSDavid du Colombier 	drive->inquiry[4] = sizeof(drive->inquiry)-4;
6547dd7cddfSDavid du Colombier 	p = &drive->inquiry[8];
6557dd7cddfSDavid du Colombier 	sp = &drive->info[Imodel];
6567dd7cddfSDavid du Colombier 	for(i = 0; i < 20; i++){
6577dd7cddfSDavid du Colombier 		*p++ = *sp>>8;
6587dd7cddfSDavid du Colombier 		*p++ = *sp++;
6597dd7cddfSDavid du Colombier 	}
6607dd7cddfSDavid du Colombier 
6617dd7cddfSDavid du Colombier 	drive->secsize = 512;
6629a747e4fSDavid du Colombier 
6639a747e4fSDavid du Colombier 	/*
6649a747e4fSDavid du Colombier 	 * Beware the CompactFlash Association feature set.
6659a747e4fSDavid du Colombier 	 * Now, why this value in Iconfig just walks all over the bit
6669a747e4fSDavid du Colombier 	 * definitions used in the other parts of the ATA/ATAPI standards
6679a747e4fSDavid du Colombier 	 * is a mystery and a sign of true stupidity on someone's part.
6689a747e4fSDavid du Colombier 	 * Anyway, the standard says if this value is 0x848A then it's
6699a747e4fSDavid du Colombier 	 * CompactFlash and it's NOT a packet device.
6709a747e4fSDavid du Colombier 	 */
6719a747e4fSDavid du Colombier 	iconfig = drive->info[Iconfig];
6729a747e4fSDavid du Colombier 	if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){
6739a747e4fSDavid du Colombier 		if(iconfig & 0x01)
6747dd7cddfSDavid du Colombier 			drive->pkt = 16;
6757dd7cddfSDavid du Colombier 		else
6767dd7cddfSDavid du Colombier 			drive->pkt = 12;
6777dd7cddfSDavid du Colombier 	}
6787dd7cddfSDavid du Colombier 	else{
6797dd7cddfSDavid du Colombier 		if(drive->info[Ivalid] & 0x0001){
6807dd7cddfSDavid du Colombier 			drive->c = drive->info[Iccyl];
6817dd7cddfSDavid du Colombier 			drive->h = drive->info[Ichead];
6827dd7cddfSDavid du Colombier 			drive->s = drive->info[Icsec];
68359f4c0c1SDavid du Colombier 		}
68459f4c0c1SDavid du Colombier 		else{
6853ff48bf5SDavid du Colombier 			drive->c = drive->info[Ilcyl];
6863ff48bf5SDavid du Colombier 			drive->h = drive->info[Ilhead];
6873ff48bf5SDavid du Colombier 			drive->s = drive->info[Ilsec];
6883ff48bf5SDavid du Colombier 		}
6896a081dcdSDavid du Colombier 		if(drive->info[Icapabilities] & Mlba){
6906a081dcdSDavid du Colombier 			if(drive->info[Icsfs+1] & Maddr48){
6916a081dcdSDavid du Colombier 				drive->sectors = drive->info[Ilba48]
6925ea8af7bSDavid du Colombier 					| (drive->info[Ilba48+1]<<16)
6935ea8af7bSDavid du Colombier 					| ((vlong)drive->info[Ilba48+2]<<32);
6946a081dcdSDavid du Colombier 				drive->flags |= Lba48;
69559f4c0c1SDavid du Colombier 			}
69659f4c0c1SDavid du Colombier 			else{
6976a081dcdSDavid du Colombier 				drive->sectors = (drive->info[Ilba+1]<<16)
6986a081dcdSDavid du Colombier 					 |drive->info[Ilba];
6997dd7cddfSDavid du Colombier 			}
7006a081dcdSDavid du Colombier 			drive->dev |= Lba;
70159f4c0c1SDavid du Colombier 		}
70259f4c0c1SDavid du Colombier 		else
7037dd7cddfSDavid du Colombier 			drive->sectors = drive->c*drive->h*drive->s;
7047dd7cddfSDavid du Colombier 		atarwmmode(drive, cmdport, ctlport, dev);
7057dd7cddfSDavid du Colombier 	}
7067dd7cddfSDavid du Colombier 	atadmamode(drive);
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier 	if(DEBUG & DbgCONFIG){
70959cc4ca5SDavid du Colombier 		print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
710ca2418feSDavid du Colombier 			dev, cmdport, iconfig, drive->info[Icapabilities]);
71114414594SDavid du Colombier 		print(" mwdma %4.4uX", drive->info[Imwdma]);
7127dd7cddfSDavid du Colombier 		if(drive->info[Ivalid] & 0x04)
7137dd7cddfSDavid du Colombier 			print(" udma %4.4uX", drive->info[Iudma]);
71459f4c0c1SDavid du Colombier 		print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm);
7156a081dcdSDavid du Colombier 		if(drive->flags&Lba48)
71659f4c0c1SDavid du Colombier 			print("\tLLBA sectors %lld", drive->sectors);
71759f4c0c1SDavid du Colombier 		print("\n");
7187dd7cddfSDavid du Colombier 	}
7197dd7cddfSDavid du Colombier 
7207dd7cddfSDavid du Colombier 	return drive;
7217dd7cddfSDavid du Colombier }
7227dd7cddfSDavid du Colombier 
7237dd7cddfSDavid du Colombier static void
7247dd7cddfSDavid du Colombier atasrst(int ctlport)
7257dd7cddfSDavid du Colombier {
7267dd7cddfSDavid du Colombier 	/*
7277dd7cddfSDavid du Colombier 	 * Srst is a big stick and may cause problems if further
7287dd7cddfSDavid du Colombier 	 * commands are tried before the drives become ready again.
7297dd7cddfSDavid du Colombier 	 * Also, there will be problems here if overlapped commands
7307dd7cddfSDavid du Colombier 	 * are ever supported.
7317dd7cddfSDavid du Colombier 	 */
7327dd7cddfSDavid du Colombier 	microdelay(5);
7337dd7cddfSDavid du Colombier 	outb(ctlport+Dc, Srst);
7347dd7cddfSDavid du Colombier 	microdelay(5);
7357dd7cddfSDavid du Colombier 	outb(ctlport+Dc, 0);
7367dd7cddfSDavid du Colombier 	microdelay(2*1000);
7377dd7cddfSDavid du Colombier }
7387dd7cddfSDavid du Colombier 
7397dd7cddfSDavid du Colombier static SDev*
7407dd7cddfSDavid du Colombier ataprobe(int cmdport, int ctlport, int irq)
7417dd7cddfSDavid du Colombier {
7427dd7cddfSDavid du Colombier 	Ctlr* ctlr;
7437dd7cddfSDavid du Colombier 	SDev *sdev;
7447dd7cddfSDavid du Colombier 	Drive *drive;
7457dd7cddfSDavid du Colombier 	int dev, error, rhi, rlo;
7464de34a7eSDavid du Colombier 	static int nonlegacy = 'C';
7477dd7cddfSDavid du Colombier 
7489a747e4fSDavid du Colombier 	if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {
7499a747e4fSDavid du Colombier 		print("ataprobe: Cannot allocate %X\n", cmdport);
7507dd7cddfSDavid du Colombier 		return nil;
7519a747e4fSDavid du Colombier 	}
7527dd7cddfSDavid du Colombier 	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
7539a747e4fSDavid du Colombier 		print("ataprobe: Cannot allocate %X\n", ctlport + As);
7547dd7cddfSDavid du Colombier 		iofree(cmdport);
7557dd7cddfSDavid du Colombier 		return nil;
7567dd7cddfSDavid du Colombier 	}
7577dd7cddfSDavid du Colombier 
7587dd7cddfSDavid du Colombier 	/*
7597dd7cddfSDavid du Colombier 	 * Try to detect a floating bus.
7607dd7cddfSDavid du Colombier 	 * Bsy should be cleared. If not, see if the cylinder registers
7617dd7cddfSDavid du Colombier 	 * are read/write capable.
7627dd7cddfSDavid du Colombier 	 * If the master fails, try the slave to catch slave-only
7637dd7cddfSDavid du Colombier 	 * configurations.
7647dd7cddfSDavid du Colombier 	 * There's no need to restore the tested registers as they will
7657dd7cddfSDavid du Colombier 	 * be reset on any detected drives by the Cedd command.
7667dd7cddfSDavid du Colombier 	 * All this indicates is that there is at least one drive on the
7677dd7cddfSDavid du Colombier 	 * controller; when the non-existent drive is selected in a
7687dd7cddfSDavid du Colombier 	 * single-drive configuration the registers of the existing drive
7697dd7cddfSDavid du Colombier 	 * are often seen, only command execution fails.
7707dd7cddfSDavid du Colombier 	 */
7717dd7cddfSDavid du Colombier 	dev = Dev0;
7727dd7cddfSDavid du Colombier 	if(inb(ctlport+As) & Bsy){
7737dd7cddfSDavid du Colombier 		outb(cmdport+Dh, dev);
7747dd7cddfSDavid du Colombier 		microdelay(1);
7757dd7cddfSDavid du Colombier trydev1:
7767dd7cddfSDavid du Colombier 		atadebug(cmdport, ctlport, "ataprobe bsy");
7777dd7cddfSDavid du Colombier 		outb(cmdport+Cyllo, 0xAA);
7787dd7cddfSDavid du Colombier 		outb(cmdport+Cylhi, 0x55);
7797dd7cddfSDavid du Colombier 		outb(cmdport+Sector, 0xFF);
7807dd7cddfSDavid du Colombier 		rlo = inb(cmdport+Cyllo);
7817dd7cddfSDavid du Colombier 		rhi = inb(cmdport+Cylhi);
7827dd7cddfSDavid du Colombier 		if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){
7837dd7cddfSDavid du Colombier 			if(dev == Dev1){
7847dd7cddfSDavid du Colombier release:
7857dd7cddfSDavid du Colombier 				iofree(cmdport);
7867dd7cddfSDavid du Colombier 				iofree(ctlport+As);
7877dd7cddfSDavid du Colombier 				return nil;
7887dd7cddfSDavid du Colombier 			}
7897dd7cddfSDavid du Colombier 			dev = Dev1;
7907dd7cddfSDavid du Colombier 			if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)
7917dd7cddfSDavid du Colombier 				goto trydev1;
7927dd7cddfSDavid du Colombier 		}
7937dd7cddfSDavid du Colombier 	}
7947dd7cddfSDavid du Colombier 
7957dd7cddfSDavid du Colombier 	/*
7967dd7cddfSDavid du Colombier 	 * Disable interrupts on any detected controllers.
7977dd7cddfSDavid du Colombier 	 */
7987dd7cddfSDavid du Colombier 	outb(ctlport+Dc, Nien);
7997dd7cddfSDavid du Colombier tryedd1:
8007dd7cddfSDavid du Colombier 	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){
8017dd7cddfSDavid du Colombier 		/*
8027dd7cddfSDavid du Colombier 		 * There's something there, but it didn't come up clean,
8037dd7cddfSDavid du Colombier 		 * so try hitting it with a big stick. The timing here is
8047dd7cddfSDavid du Colombier 		 * wrong but this is a last-ditch effort and it sometimes
8057dd7cddfSDavid du Colombier 		 * gets some marginal hardware back online.
8067dd7cddfSDavid du Colombier 		 */
8077dd7cddfSDavid du Colombier 		atasrst(ctlport);
8087dd7cddfSDavid du Colombier 		if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0)
8097dd7cddfSDavid du Colombier 			goto release;
8107dd7cddfSDavid du Colombier 	}
8117dd7cddfSDavid du Colombier 
8127dd7cddfSDavid du Colombier 	/*
8137dd7cddfSDavid du Colombier 	 * Can only get here if controller is not busy.
8147dd7cddfSDavid du Colombier 	 * If there are drives Bsy will be set within 400nS,
8157dd7cddfSDavid du Colombier 	 * must wait 2mS before testing Status.
8167dd7cddfSDavid du Colombier 	 * Wait for the command to complete (6 seconds max).
8177dd7cddfSDavid du Colombier 	 */
8187dd7cddfSDavid du Colombier 	outb(cmdport+Command, Cedd);
8197dd7cddfSDavid du Colombier 	delay(2);
8207dd7cddfSDavid du Colombier 	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)
8217dd7cddfSDavid du Colombier 		goto release;
8227dd7cddfSDavid du Colombier 
8237dd7cddfSDavid du Colombier 	/*
8247dd7cddfSDavid du Colombier 	 * If bit 0 of the error register is set then the selected drive
8257dd7cddfSDavid du Colombier 	 * exists. This is enough to detect single-drive configurations.
8267dd7cddfSDavid du Colombier 	 * However, if the master exists there is no way short of executing
8277dd7cddfSDavid du Colombier 	 * a command to determine if a slave is present.
8287dd7cddfSDavid du Colombier 	 * It appears possible to get here testing Dev0 although it doesn't
8297dd7cddfSDavid du Colombier 	 * exist and the EDD won't take, so try again with Dev1.
8307dd7cddfSDavid du Colombier 	 */
8317dd7cddfSDavid du Colombier 	error = inb(cmdport+Error);
8327dd7cddfSDavid du Colombier 	atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);
8337dd7cddfSDavid du Colombier 	if((error & ~0x80) != 0x01){
8347dd7cddfSDavid du Colombier 		if(dev == Dev1)
8357dd7cddfSDavid du Colombier 			goto release;
8367dd7cddfSDavid du Colombier 		dev = Dev1;
8377dd7cddfSDavid du Colombier 		goto tryedd1;
8387dd7cddfSDavid du Colombier 	}
8397dd7cddfSDavid du Colombier 
8407dd7cddfSDavid du Colombier 	/*
8417dd7cddfSDavid du Colombier 	 * At least one drive is known to exist, try to
8427dd7cddfSDavid du Colombier 	 * identify it. If that fails, don't bother checking
8437dd7cddfSDavid du Colombier 	 * any further.
8447dd7cddfSDavid du Colombier 	 * If the one drive found is Dev0 and the EDD command
8457dd7cddfSDavid du Colombier 	 * didn't indicate Dev1 doesn't exist, check for it.
8467dd7cddfSDavid du Colombier 	 */
8477dd7cddfSDavid du Colombier 	if((drive = atadrive(cmdport, ctlport, dev)) == nil)
8487dd7cddfSDavid du Colombier 		goto release;
8497dd7cddfSDavid du Colombier 	if((ctlr = malloc(sizeof(Ctlr))) == nil){
8507dd7cddfSDavid du Colombier 		free(drive);
8517dd7cddfSDavid du Colombier 		goto release;
8527dd7cddfSDavid du Colombier 	}
8539a747e4fSDavid du Colombier 	memset(ctlr, 0, sizeof(Ctlr));
8547dd7cddfSDavid du Colombier 	if((sdev = malloc(sizeof(SDev))) == nil){
8557dd7cddfSDavid du Colombier 		free(ctlr);
8567dd7cddfSDavid du Colombier 		free(drive);
8577dd7cddfSDavid du Colombier 		goto release;
8587dd7cddfSDavid du Colombier 	}
8599a747e4fSDavid du Colombier 	memset(sdev, 0, sizeof(SDev));
8607dd7cddfSDavid du Colombier 	drive->ctlr = ctlr;
8617dd7cddfSDavid du Colombier 	if(dev == Dev0){
8627dd7cddfSDavid du Colombier 		ctlr->drive[0] = drive;
8637dd7cddfSDavid du Colombier 		if(!(error & 0x80)){
8647dd7cddfSDavid du Colombier 			/*
8657dd7cddfSDavid du Colombier 			 * Always leave Dh pointing to a valid drive,
8667dd7cddfSDavid du Colombier 			 * otherwise a subsequent call to ataready on
8677dd7cddfSDavid du Colombier 			 * this controller may try to test a bogus Status.
8687dd7cddfSDavid du Colombier 			 * Ataprobe is the only place possibly invalid
8697dd7cddfSDavid du Colombier 			 * drives should be selected.
8707dd7cddfSDavid du Colombier 			 */
8717dd7cddfSDavid du Colombier 			drive = atadrive(cmdport, ctlport, Dev1);
8727dd7cddfSDavid du Colombier 			if(drive != nil){
8737dd7cddfSDavid du Colombier 				drive->ctlr = ctlr;
8747dd7cddfSDavid du Colombier 				ctlr->drive[1] = drive;
8757dd7cddfSDavid du Colombier 			}
8767dd7cddfSDavid du Colombier 			else{
8777dd7cddfSDavid du Colombier 				outb(cmdport+Dh, Dev0);
8787dd7cddfSDavid du Colombier 				microdelay(1);
8797dd7cddfSDavid du Colombier 			}
8807dd7cddfSDavid du Colombier 		}
8817dd7cddfSDavid du Colombier 	}
8827dd7cddfSDavid du Colombier 	else
8837dd7cddfSDavid du Colombier 		ctlr->drive[1] = drive;
8847dd7cddfSDavid du Colombier 
8857dd7cddfSDavid du Colombier 	ctlr->cmdport = cmdport;
8867dd7cddfSDavid du Colombier 	ctlr->ctlport = ctlport;
8877dd7cddfSDavid du Colombier 	ctlr->irq = irq;
8887dd7cddfSDavid du Colombier 	ctlr->tbdf = BUSUNKNOWN;
8897dd7cddfSDavid du Colombier 	ctlr->command = Cedd;		/* debugging */
8907dd7cddfSDavid du Colombier 
8914de34a7eSDavid du Colombier 	switch(cmdport){
8924de34a7eSDavid du Colombier 	default:
8934de34a7eSDavid du Colombier 		sdev->idno = nonlegacy;
8944de34a7eSDavid du Colombier 		break;
8954de34a7eSDavid du Colombier 	case 0x1F0:
8964de34a7eSDavid du Colombier 		sdev->idno = 'C';
8974de34a7eSDavid du Colombier 		nonlegacy = 'E';
8984de34a7eSDavid du Colombier 		break;
8994de34a7eSDavid du Colombier 	case 0x170:
9004de34a7eSDavid du Colombier 		sdev->idno = 'D';
9014de34a7eSDavid du Colombier 		nonlegacy = 'E';
9024de34a7eSDavid du Colombier 		break;
9034de34a7eSDavid du Colombier 	}
9047dd7cddfSDavid du Colombier 	sdev->ifc = &sdataifc;
9057dd7cddfSDavid du Colombier 	sdev->ctlr = ctlr;
9067dd7cddfSDavid du Colombier 	sdev->nunit = 2;
9077dd7cddfSDavid du Colombier 	ctlr->sdev = sdev;
9087dd7cddfSDavid du Colombier 
9097dd7cddfSDavid du Colombier 	return sdev;
9107dd7cddfSDavid du Colombier }
9117dd7cddfSDavid du Colombier 
9129a747e4fSDavid du Colombier static void
9139a747e4fSDavid du Colombier ataclear(SDev *sdev)
9149a747e4fSDavid du Colombier {
9159a747e4fSDavid du Colombier 	Ctlr* ctlr;
9169a747e4fSDavid du Colombier 
9179a747e4fSDavid du Colombier 	ctlr = sdev->ctlr;
9189a747e4fSDavid du Colombier 	iofree(ctlr->cmdport);
9199a747e4fSDavid du Colombier 	iofree(ctlr->ctlport + As);
9209a747e4fSDavid du Colombier 
9219a747e4fSDavid du Colombier 	if (ctlr->drive[0])
9229a747e4fSDavid du Colombier 		free(ctlr->drive[0]);
9239a747e4fSDavid du Colombier 	if (ctlr->drive[1])
9249a747e4fSDavid du Colombier 		free(ctlr->drive[1]);
9259a747e4fSDavid du Colombier 	if (sdev->name)
9269a747e4fSDavid du Colombier 		free(sdev->name);
9279a747e4fSDavid du Colombier 	if (sdev->unitflg)
9289a747e4fSDavid du Colombier 		free(sdev->unitflg);
9299a747e4fSDavid du Colombier 	if (sdev->unit)
9309a747e4fSDavid du Colombier 		free(sdev->unit);
9319a747e4fSDavid du Colombier 	free(ctlr);
9329a747e4fSDavid du Colombier 	free(sdev);
9339a747e4fSDavid du Colombier }
9349a747e4fSDavid du Colombier 
9359a747e4fSDavid du Colombier static char *
9369a747e4fSDavid du Colombier atastat(SDev *sdev, char *p, char *e)
9379a747e4fSDavid du Colombier {
9389a747e4fSDavid du Colombier 	Ctlr *ctlr = sdev->ctlr;
9399a747e4fSDavid du Colombier 
9409a747e4fSDavid du Colombier 	return seprint(p, e, "%s ata port %X ctl %X irq %d\n",
9419a747e4fSDavid du Colombier 		    	       sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);
9429a747e4fSDavid du Colombier }
9439a747e4fSDavid du Colombier 
9449a747e4fSDavid du Colombier static SDev*
9459a747e4fSDavid du Colombier ataprobew(DevConf *cf)
9469a747e4fSDavid du Colombier {
9474de34a7eSDavid du Colombier 	char *p;
9484de34a7eSDavid du Colombier 	ISAConf isa;
9494de34a7eSDavid du Colombier 
9509a747e4fSDavid du Colombier 	if (cf->nports != 2)
9519a747e4fSDavid du Colombier 		error(Ebadarg);
9529a747e4fSDavid du Colombier 
9534de34a7eSDavid du Colombier 	memset(&isa, 0, sizeof isa);
9544de34a7eSDavid du Colombier 	isa.port = cf->ports[0].port;
9554de34a7eSDavid du Colombier 	isa.irq = cf->intnum;
9564de34a7eSDavid du Colombier 	if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0)
9574de34a7eSDavid du Colombier 		error("cannot find controller");
9584de34a7eSDavid du Colombier 
9593ff48bf5SDavid du Colombier 	return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);
9609a747e4fSDavid du Colombier }
9619a747e4fSDavid du Colombier 
9624de34a7eSDavid du Colombier /*
9634de34a7eSDavid du Colombier  * These are duplicated with sdsetsense, etc., in devsd.c, but
9644de34a7eSDavid du Colombier  * those assume that the disk is not SCSI while in fact here
9654de34a7eSDavid du Colombier  * ata drives are not SCSI but ATAPI ones kind of are.
9664de34a7eSDavid du Colombier  */
9677dd7cddfSDavid du Colombier static int
9687dd7cddfSDavid du Colombier atasetsense(Drive* drive, int status, int key, int asc, int ascq)
9697dd7cddfSDavid du Colombier {
9707dd7cddfSDavid du Colombier 	drive->sense[2] = key;
9717dd7cddfSDavid du Colombier 	drive->sense[12] = asc;
9727dd7cddfSDavid du Colombier 	drive->sense[13] = ascq;
9737dd7cddfSDavid du Colombier 
9747dd7cddfSDavid du Colombier 	return status;
9757dd7cddfSDavid du Colombier }
9767dd7cddfSDavid du Colombier 
9777dd7cddfSDavid du Colombier static int
9787dd7cddfSDavid du Colombier atamodesense(Drive* drive, uchar* cmd)
9797dd7cddfSDavid du Colombier {
9807dd7cddfSDavid du Colombier 	int len;
9817dd7cddfSDavid du Colombier 
9827dd7cddfSDavid du Colombier 	/*
9837dd7cddfSDavid du Colombier 	 * Fake a vendor-specific request with page code 0,
9847dd7cddfSDavid du Colombier 	 * return the drive info.
9857dd7cddfSDavid du Colombier 	 */
9867dd7cddfSDavid du Colombier 	if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
9877dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
9887dd7cddfSDavid du Colombier 	len = (cmd[7]<<8)|cmd[8];
9897dd7cddfSDavid du Colombier 	if(len == 0)
9907dd7cddfSDavid du Colombier 		return SDok;
9917dd7cddfSDavid du Colombier 	if(len < 8+sizeof(drive->info))
9927dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);
9937dd7cddfSDavid du Colombier 	if(drive->data == nil || drive->dlen < len)
9947dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
9957dd7cddfSDavid du Colombier 	memset(drive->data, 0, 8);
9967dd7cddfSDavid du Colombier 	drive->data[0] = sizeof(drive->info)>>8;
9977dd7cddfSDavid du Colombier 	drive->data[1] = sizeof(drive->info);
9987dd7cddfSDavid du Colombier 	memmove(drive->data+8, drive->info, sizeof(drive->info));
9997dd7cddfSDavid du Colombier 	drive->data += 8+sizeof(drive->info);
10007dd7cddfSDavid du Colombier 
10017dd7cddfSDavid du Colombier 	return SDok;
10027dd7cddfSDavid du Colombier }
10037dd7cddfSDavid du Colombier 
10044de34a7eSDavid du Colombier static int
10054de34a7eSDavid du Colombier atastandby(Drive* drive, int period)
10064de34a7eSDavid du Colombier {
10074de34a7eSDavid du Colombier 	Ctlr* ctlr;
10084de34a7eSDavid du Colombier 	int cmdport, done;
10094de34a7eSDavid du Colombier 
10104de34a7eSDavid du Colombier 	ctlr = drive->ctlr;
10114de34a7eSDavid du Colombier 	drive->command = Cstandby;
10124de34a7eSDavid du Colombier 	qlock(ctlr);
10134de34a7eSDavid du Colombier 
10144de34a7eSDavid du Colombier 	cmdport = ctlr->cmdport;
10154de34a7eSDavid du Colombier 	ilock(ctlr);
10164de34a7eSDavid du Colombier 	outb(cmdport+Count, period);
10174de34a7eSDavid du Colombier 	outb(cmdport+Dh, drive->dev);
10184de34a7eSDavid du Colombier 	ctlr->done = 0;
10194de34a7eSDavid du Colombier 	ctlr->curdrive = drive;
10204de34a7eSDavid du Colombier 	ctlr->command = Cstandby;	/* debugging */
10214de34a7eSDavid du Colombier 	outb(cmdport+Command, Cstandby);
10224de34a7eSDavid du Colombier 	iunlock(ctlr);
10234de34a7eSDavid du Colombier 
10244de34a7eSDavid du Colombier 	while(waserror())
10254de34a7eSDavid du Colombier 		;
10264de34a7eSDavid du Colombier 	tsleep(ctlr, atadone, ctlr, 60*1000);
10274de34a7eSDavid du Colombier 	poperror();
10284de34a7eSDavid du Colombier 
10294de34a7eSDavid du Colombier 	done = ctlr->done;
10304de34a7eSDavid du Colombier 	qunlock(ctlr);
10314de34a7eSDavid du Colombier 
10324de34a7eSDavid du Colombier 	if(!done || (drive->status & Err))
10334de34a7eSDavid du Colombier 		return atasetsense(drive, SDcheck, 4, 8, drive->error);
10344de34a7eSDavid du Colombier 	return SDok;
10354de34a7eSDavid du Colombier }
10364de34a7eSDavid du Colombier 
10377dd7cddfSDavid du Colombier static void
10387dd7cddfSDavid du Colombier atanop(Drive* drive, int subcommand)
10397dd7cddfSDavid du Colombier {
10407dd7cddfSDavid du Colombier 	Ctlr* ctlr;
10417dd7cddfSDavid du Colombier 	int as, cmdport, ctlport, timeo;
10427dd7cddfSDavid du Colombier 
10437dd7cddfSDavid du Colombier 	/*
10447dd7cddfSDavid du Colombier 	 * Attempt to abort a command by using NOP.
10457dd7cddfSDavid du Colombier 	 * In response, the drive is supposed to set Abrt
10467dd7cddfSDavid du Colombier 	 * in the Error register, set (Drdy|Err) in Status
10477dd7cddfSDavid du Colombier 	 * and clear Bsy when done. However, some drives
10487dd7cddfSDavid du Colombier 	 * (e.g. ATAPI Zip) just go Bsy then clear Status
10497dd7cddfSDavid du Colombier 	 * when done, hence the timeout loop only on Bsy
10507dd7cddfSDavid du Colombier 	 * and the forced setting of drive->error.
10517dd7cddfSDavid du Colombier 	 */
10527dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
10537dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
10547dd7cddfSDavid du Colombier 	outb(cmdport+Features, subcommand);
10557dd7cddfSDavid du Colombier 	outb(cmdport+Dh, drive->dev);
10567dd7cddfSDavid du Colombier 	ctlr->command = Cnop;		/* debugging */
10577dd7cddfSDavid du Colombier 	outb(cmdport+Command, Cnop);
10587dd7cddfSDavid du Colombier 
10597dd7cddfSDavid du Colombier 	microdelay(1);
10607dd7cddfSDavid du Colombier 	ctlport = ctlr->ctlport;
10617dd7cddfSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
10627dd7cddfSDavid du Colombier 		as = inb(ctlport+As);
10637dd7cddfSDavid du Colombier 		if(!(as & Bsy))
10647dd7cddfSDavid du Colombier 			break;
10657dd7cddfSDavid du Colombier 		microdelay(1);
10667dd7cddfSDavid du Colombier 	}
10677dd7cddfSDavid du Colombier 	drive->error |= Abrt;
10687dd7cddfSDavid du Colombier }
10697dd7cddfSDavid du Colombier 
10707dd7cddfSDavid du Colombier static void
10717dd7cddfSDavid du Colombier ataabort(Drive* drive, int dolock)
10727dd7cddfSDavid du Colombier {
10737dd7cddfSDavid du Colombier 	/*
10747dd7cddfSDavid du Colombier 	 * If NOP is available (packet commands) use it otherwise
10757dd7cddfSDavid du Colombier 	 * must try a software reset.
10767dd7cddfSDavid du Colombier 	 */
10777dd7cddfSDavid du Colombier 	if(dolock)
10787dd7cddfSDavid du Colombier 		ilock(drive->ctlr);
10796a081dcdSDavid du Colombier 	if(drive->info[Icsfs] & Mnop)
10807dd7cddfSDavid du Colombier 		atanop(drive, 0);
10817dd7cddfSDavid du Colombier 	else{
10827dd7cddfSDavid du Colombier 		atasrst(drive->ctlr->ctlport);
10837dd7cddfSDavid du Colombier 		drive->error |= Abrt;
10847dd7cddfSDavid du Colombier 	}
10857dd7cddfSDavid du Colombier 	if(dolock)
10867dd7cddfSDavid du Colombier 		iunlock(drive->ctlr);
10877dd7cddfSDavid du Colombier }
10887dd7cddfSDavid du Colombier 
10897dd7cddfSDavid du Colombier static int
10907dd7cddfSDavid du Colombier atadmasetup(Drive* drive, int len)
10917dd7cddfSDavid du Colombier {
10927dd7cddfSDavid du Colombier 	Prd *prd;
10937dd7cddfSDavid du Colombier 	ulong pa;
10947dd7cddfSDavid du Colombier 	Ctlr *ctlr;
10954de34a7eSDavid du Colombier 	int bmiba, bmisx, count, i, span;
10967dd7cddfSDavid du Colombier 
10974de34a7eSDavid du Colombier 	ctlr = drive->ctlr;
10987dd7cddfSDavid du Colombier 	pa = PCIWADDR(drive->data);
10997dd7cddfSDavid du Colombier 	if(pa & 0x03)
11007dd7cddfSDavid du Colombier 		return -1;
11017dd7cddfSDavid du Colombier 
11027dd7cddfSDavid du Colombier 	/*
11037dd7cddfSDavid du Colombier 	 * Sometimes drives identify themselves as being DMA capable
11047dd7cddfSDavid du Colombier 	 * although they are not on a busmastering controller.
11057dd7cddfSDavid du Colombier 	 */
11064de34a7eSDavid du Colombier 	prd = ctlr->prdt;
11077dd7cddfSDavid du Colombier 	if(prd == nil){
11087dd7cddfSDavid du Colombier 		drive->dmactl = 0;
11096de6ce84SDavid du Colombier 		print("disabling dma: not on a busmastering controller\n");
11107dd7cddfSDavid du Colombier 		return -1;
11117dd7cddfSDavid du Colombier 	}
11127dd7cddfSDavid du Colombier 
11134de34a7eSDavid du Colombier 	for(i = 0; len && i < Nprd; i++){
11147dd7cddfSDavid du Colombier 		prd->pa = pa;
11154de34a7eSDavid du Colombier 		span = ROUNDUP(pa, ctlr->span);
11164de34a7eSDavid du Colombier 		if(span == pa)
11174de34a7eSDavid du Colombier 			span += ctlr->span;
11184de34a7eSDavid du Colombier 		count = span - pa;
11197dd7cddfSDavid du Colombier 		if(count >= len){
11204de34a7eSDavid du Colombier 			prd->count = PrdEOT|len;
11217dd7cddfSDavid du Colombier 			break;
11227dd7cddfSDavid du Colombier 		}
11237dd7cddfSDavid du Colombier 		prd->count = count;
11247dd7cddfSDavid du Colombier 		len -= count;
11257dd7cddfSDavid du Colombier 		pa += count;
11267dd7cddfSDavid du Colombier 		prd++;
11277dd7cddfSDavid du Colombier 	}
11284de34a7eSDavid du Colombier 	if(i == Nprd)
11294de34a7eSDavid du Colombier 		(prd-1)->count |= PrdEOT;
11307dd7cddfSDavid du Colombier 
11317dd7cddfSDavid du Colombier 	bmiba = ctlr->bmiba;
11327dd7cddfSDavid du Colombier 	outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt));
11337dd7cddfSDavid du Colombier 	if(drive->write)
11347dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, 0);
11357dd7cddfSDavid du Colombier 	else
11367dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, Rwcon);
11377dd7cddfSDavid du Colombier 	bmisx = inb(bmiba+Bmisx);
11387dd7cddfSDavid du Colombier 	outb(bmiba+Bmisx, bmisx|Ideints|Idedmae);
11397dd7cddfSDavid du Colombier 
11407dd7cddfSDavid du Colombier 	return 0;
11417dd7cddfSDavid du Colombier }
11427dd7cddfSDavid du Colombier 
11437dd7cddfSDavid du Colombier static void
11447dd7cddfSDavid du Colombier atadmastart(Ctlr* ctlr, int write)
11457dd7cddfSDavid du Colombier {
11467dd7cddfSDavid du Colombier 	if(write)
11477dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, Ssbm);
11487dd7cddfSDavid du Colombier 	else
11497dd7cddfSDavid du Colombier 		outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm);
11507dd7cddfSDavid du Colombier }
11517dd7cddfSDavid du Colombier 
11527dd7cddfSDavid du Colombier static int
11537dd7cddfSDavid du Colombier atadmastop(Ctlr* ctlr)
11547dd7cddfSDavid du Colombier {
11557dd7cddfSDavid du Colombier 	int bmiba;
11567dd7cddfSDavid du Colombier 
11577dd7cddfSDavid du Colombier 	bmiba = ctlr->bmiba;
11587dd7cddfSDavid du Colombier 	outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);
11597dd7cddfSDavid du Colombier 
11607dd7cddfSDavid du Colombier 	return inb(bmiba+Bmisx);
11617dd7cddfSDavid du Colombier }
11627dd7cddfSDavid du Colombier 
11637dd7cddfSDavid du Colombier static void
11647dd7cddfSDavid du Colombier atadmainterrupt(Drive* drive, int count)
11657dd7cddfSDavid du Colombier {
11667dd7cddfSDavid du Colombier 	Ctlr* ctlr;
11677dd7cddfSDavid du Colombier 	int bmiba, bmisx;
11687dd7cddfSDavid du Colombier 
11697dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
11707dd7cddfSDavid du Colombier 	bmiba = ctlr->bmiba;
11717dd7cddfSDavid du Colombier 	bmisx = inb(bmiba+Bmisx);
11727dd7cddfSDavid du Colombier 	switch(bmisx & (Ideints|Idedmae|Bmidea)){
11737dd7cddfSDavid du Colombier 	case Bmidea:
11747dd7cddfSDavid du Colombier 		/*
11757dd7cddfSDavid du Colombier 		 * Data transfer still in progress, nothing to do
11767dd7cddfSDavid du Colombier 		 * (this should never happen).
11777dd7cddfSDavid du Colombier 		 */
11787dd7cddfSDavid du Colombier 		return;
11797dd7cddfSDavid du Colombier 
11807dd7cddfSDavid du Colombier 	case Ideints:
11817dd7cddfSDavid du Colombier 	case Ideints|Bmidea:
11827dd7cddfSDavid du Colombier 		/*
11837dd7cddfSDavid du Colombier 		 * Normal termination, tidy up.
11847dd7cddfSDavid du Colombier 		 */
11857dd7cddfSDavid du Colombier 		drive->data += count;
11867dd7cddfSDavid du Colombier 		break;
11877dd7cddfSDavid du Colombier 
11887dd7cddfSDavid du Colombier 	default:
11897dd7cddfSDavid du Colombier 		/*
11907dd7cddfSDavid du Colombier 		 * What's left are error conditions (memory transfer
11917dd7cddfSDavid du Colombier 		 * problem) and the device is not done but the PRD is
11927dd7cddfSDavid du Colombier 		 * exhausted. For both cases must somehow tell the
11937dd7cddfSDavid du Colombier 		 * drive to abort.
11947dd7cddfSDavid du Colombier 		 */
11957dd7cddfSDavid du Colombier 		ataabort(drive, 0);
11967dd7cddfSDavid du Colombier 		break;
11977dd7cddfSDavid du Colombier 	}
11987dd7cddfSDavid du Colombier 	atadmastop(ctlr);
11997dd7cddfSDavid du Colombier 	ctlr->done = 1;
12007dd7cddfSDavid du Colombier }
12017dd7cddfSDavid du Colombier 
12027dd7cddfSDavid du Colombier static void
12037dd7cddfSDavid du Colombier atapktinterrupt(Drive* drive)
12047dd7cddfSDavid du Colombier {
12057dd7cddfSDavid du Colombier 	Ctlr* ctlr;
12067dd7cddfSDavid du Colombier 	int cmdport, len;
12077dd7cddfSDavid du Colombier 
12087dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
12097dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
12107dd7cddfSDavid du Colombier 	switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){
12117dd7cddfSDavid du Colombier 	case Cd:
12127dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->pktcmd, drive->pkt/2);
12137dd7cddfSDavid du Colombier 		break;
12147dd7cddfSDavid du Colombier 
12157dd7cddfSDavid du Colombier 	case 0:
12167dd7cddfSDavid du Colombier 		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
12177dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit){
12187dd7cddfSDavid du Colombier 			atanop(drive, 0);
12197dd7cddfSDavid du Colombier 			break;
12207dd7cddfSDavid du Colombier 		}
12217dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->data, len/2);
12227dd7cddfSDavid du Colombier 		drive->data += len;
12237dd7cddfSDavid du Colombier 		break;
12247dd7cddfSDavid du Colombier 
12257dd7cddfSDavid du Colombier 	case Io:
12267dd7cddfSDavid du Colombier 		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
12277dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit){
12287dd7cddfSDavid du Colombier 			atanop(drive, 0);
12297dd7cddfSDavid du Colombier 			break;
12307dd7cddfSDavid du Colombier 		}
12317dd7cddfSDavid du Colombier 		inss(cmdport+Data, drive->data, len/2);
12327dd7cddfSDavid du Colombier 		drive->data += len;
12337dd7cddfSDavid du Colombier 		break;
12347dd7cddfSDavid du Colombier 
12357dd7cddfSDavid du Colombier 	case Io|Cd:
12367dd7cddfSDavid du Colombier 		if(drive->pktdma)
12377dd7cddfSDavid du Colombier 			atadmainterrupt(drive, drive->dlen);
12387dd7cddfSDavid du Colombier 		else
12397dd7cddfSDavid du Colombier 			ctlr->done = 1;
12407dd7cddfSDavid du Colombier 		break;
12417dd7cddfSDavid du Colombier 	}
12427dd7cddfSDavid du Colombier }
12437dd7cddfSDavid du Colombier 
12447dd7cddfSDavid du Colombier static int
12457dd7cddfSDavid du Colombier atapktio(Drive* drive, uchar* cmd, int clen)
12467dd7cddfSDavid du Colombier {
12477dd7cddfSDavid du Colombier 	Ctlr *ctlr;
12487dd7cddfSDavid du Colombier 	int as, cmdport, ctlport, len, r, timeo;
12497dd7cddfSDavid du Colombier 
12507dd7cddfSDavid du Colombier 	if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)
12517dd7cddfSDavid du Colombier 		return atamodesense(drive, cmd);
12527dd7cddfSDavid du Colombier 
12537dd7cddfSDavid du Colombier 	r = SDok;
12547dd7cddfSDavid du Colombier 
12557dd7cddfSDavid du Colombier 	drive->command = Cpkt;
12567dd7cddfSDavid du Colombier 	memmove(drive->pktcmd, cmd, clen);
12577dd7cddfSDavid du Colombier 	memset(drive->pktcmd+clen, 0, drive->pkt-clen);
12587dd7cddfSDavid du Colombier 	drive->limit = drive->data+drive->dlen;
12597dd7cddfSDavid du Colombier 
12607dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
12617dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
12627dd7cddfSDavid du Colombier 	ctlport = ctlr->ctlport;
12637dd7cddfSDavid du Colombier 
12647dd7cddfSDavid du Colombier 	qlock(ctlr);
12657dd7cddfSDavid du Colombier 
1266b8a11165SDavid du Colombier 	as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, Drdy, 107*1000);
126732078d07SDavid du Colombier 	/* used to test as&Chk as failure too, but some CD readers use that for media change */
126832078d07SDavid du Colombier 	if(as < 0){
12697dd7cddfSDavid du Colombier 		qunlock(ctlr);
12707dd7cddfSDavid du Colombier 		return -1;
12717dd7cddfSDavid du Colombier 	}
12727dd7cddfSDavid du Colombier 
12737dd7cddfSDavid du Colombier 	ilock(ctlr);
12747dd7cddfSDavid du Colombier 	if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))
12757dd7cddfSDavid du Colombier 		drive->pktdma = Dma;
12767dd7cddfSDavid du Colombier 	else
12777dd7cddfSDavid du Colombier 		drive->pktdma = 0;
12787dd7cddfSDavid du Colombier 
12797dd7cddfSDavid du Colombier 	outb(cmdport+Features, drive->pktdma);
12807dd7cddfSDavid du Colombier 	outb(cmdport+Count, 0);
12817dd7cddfSDavid du Colombier 	outb(cmdport+Sector, 0);
12827dd7cddfSDavid du Colombier 	len = 16*drive->secsize;
12837dd7cddfSDavid du Colombier 	outb(cmdport+Bytelo, len);
12847dd7cddfSDavid du Colombier 	outb(cmdport+Bytehi, len>>8);
12857dd7cddfSDavid du Colombier 	outb(cmdport+Dh, drive->dev);
12867dd7cddfSDavid du Colombier 	ctlr->done = 0;
12877dd7cddfSDavid du Colombier 	ctlr->curdrive = drive;
12887dd7cddfSDavid du Colombier 	ctlr->command = Cpkt;		/* debugging */
12897dd7cddfSDavid du Colombier 	if(drive->pktdma)
12907dd7cddfSDavid du Colombier 		atadmastart(ctlr, drive->write);
12917dd7cddfSDavid du Colombier 	outb(cmdport+Command, Cpkt);
12927dd7cddfSDavid du Colombier 
12936a081dcdSDavid du Colombier 	if((drive->info[Iconfig] & Mdrq) != 0x0020){
12947dd7cddfSDavid du Colombier 		microdelay(1);
12957dd7cddfSDavid du Colombier 		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
1296a22b0629SDavid du Colombier 		if(as < 0 || (as & (Bsy|Chk))){
1297a22b0629SDavid du Colombier 			drive->status = as<0 ? 0 : as;
1298a22b0629SDavid du Colombier 			ctlr->curdrive = nil;
1299a22b0629SDavid du Colombier 			ctlr->done = 1;
13007dd7cddfSDavid du Colombier 			r = SDtimeout;
1301a22b0629SDavid du Colombier 		}else
13027dd7cddfSDavid du Colombier 			atapktinterrupt(drive);
13037dd7cddfSDavid du Colombier 	}
13047dd7cddfSDavid du Colombier 	iunlock(ctlr);
13057dd7cddfSDavid du Colombier 
13067dd7cddfSDavid du Colombier 	while(waserror())
13077dd7cddfSDavid du Colombier 		;
13087dd7cddfSDavid du Colombier 	if(!drive->pktdma)
130980ee5cbfSDavid du Colombier 		sleep(ctlr, atadone, ctlr);
13107dd7cddfSDavid du Colombier 	else for(timeo = 0; !ctlr->done; timeo++){
131180ee5cbfSDavid du Colombier 		tsleep(ctlr, atadone, ctlr, 1000);
13127dd7cddfSDavid du Colombier 		if(ctlr->done)
13137dd7cddfSDavid du Colombier 			break;
13147dd7cddfSDavid du Colombier 		ilock(ctlr);
13157dd7cddfSDavid du Colombier 		atadmainterrupt(drive, 0);
13164de34a7eSDavid du Colombier 		if(!drive->error && timeo > 20){
13177dd7cddfSDavid du Colombier 			ataabort(drive, 0);
13187dd7cddfSDavid du Colombier 			atadmastop(ctlr);
13197dd7cddfSDavid du Colombier 			drive->dmactl = 0;
13207dd7cddfSDavid du Colombier 			drive->error |= Abrt;
13217dd7cddfSDavid du Colombier 		}
13227dd7cddfSDavid du Colombier 		if(drive->error){
13237dd7cddfSDavid du Colombier 			drive->status |= Chk;
13247dd7cddfSDavid du Colombier 			ctlr->curdrive = nil;
13257dd7cddfSDavid du Colombier 		}
13267dd7cddfSDavid du Colombier 		iunlock(ctlr);
13277dd7cddfSDavid du Colombier 	}
13287dd7cddfSDavid du Colombier 	poperror();
13297dd7cddfSDavid du Colombier 
13307dd7cddfSDavid du Colombier 	qunlock(ctlr);
13317dd7cddfSDavid du Colombier 
13327dd7cddfSDavid du Colombier 	if(drive->status & Chk)
13337dd7cddfSDavid du Colombier 		r = SDcheck;
13347dd7cddfSDavid du Colombier 
13357dd7cddfSDavid du Colombier 	return r;
13367dd7cddfSDavid du Colombier }
13377dd7cddfSDavid du Colombier 
13386a081dcdSDavid du Colombier static uchar cmd48[256] = {
13396a081dcdSDavid du Colombier 	[Crs]	Crs48,
13406a081dcdSDavid du Colombier 	[Crd]	Crd48,
13416a081dcdSDavid du Colombier 	[Crdq]	Crdq48,
13426a081dcdSDavid du Colombier 	[Crsm]	Crsm48,
13436a081dcdSDavid du Colombier 	[Cws]	Cws48,
13446a081dcdSDavid du Colombier 	[Cwd]	Cwd48,
13456a081dcdSDavid du Colombier 	[Cwdq]	Cwdq48,
13466a081dcdSDavid du Colombier 	[Cwsm]	Cwsm48,
13476a081dcdSDavid du Colombier };
13486a081dcdSDavid du Colombier 
13497dd7cddfSDavid du Colombier static int
135091157df7SDavid du Colombier atageniostart(Drive* drive, uvlong lba)
13517dd7cddfSDavid du Colombier {
13527dd7cddfSDavid du Colombier 	Ctlr *ctlr;
13536a081dcdSDavid du Colombier 	uchar cmd;
13545ea8af7bSDavid du Colombier 	int as, c, cmdport, ctlport, h, len, s, use48;
13557dd7cddfSDavid du Colombier 
13565ea8af7bSDavid du Colombier 	use48 = 0;
135791157df7SDavid du Colombier 	if((drive->flags&Lba48always) || lba > Last28 || drive->count > 256){
13586a081dcdSDavid du Colombier 		if(!(drive->flags & Lba48))
13595ea8af7bSDavid du Colombier 			return -1;
13605ea8af7bSDavid du Colombier 		use48 = 1;
13615ea8af7bSDavid du Colombier 		c = h = s = 0;
136259f4c0c1SDavid du Colombier 	}
136359f4c0c1SDavid du Colombier 	else if(drive->dev & Lba){
13647dd7cddfSDavid du Colombier 		c = (lba>>8) & 0xFFFF;
13657dd7cddfSDavid du Colombier 		h = (lba>>24) & 0x0F;
13667dd7cddfSDavid du Colombier 		s = lba & 0xFF;
136759f4c0c1SDavid du Colombier 	}
136859f4c0c1SDavid du Colombier 	else{
13697dd7cddfSDavid du Colombier 		c = lba/(drive->s*drive->h);
13707dd7cddfSDavid du Colombier 		h = ((lba/drive->s) % drive->h);
13717dd7cddfSDavid du Colombier 		s = (lba % drive->s) + 1;
13727dd7cddfSDavid du Colombier 	}
13737dd7cddfSDavid du Colombier 
13747dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
13757dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
13767dd7cddfSDavid du Colombier 	ctlport = ctlr->ctlport;
1377b8a11165SDavid du Colombier 	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, Drdy, 101*1000) < 0)
13787dd7cddfSDavid du Colombier 		return -1;
13797dd7cddfSDavid du Colombier 
13807dd7cddfSDavid du Colombier 	ilock(ctlr);
13817dd7cddfSDavid du Colombier 	if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){
13827dd7cddfSDavid du Colombier 		if(drive->write)
13837dd7cddfSDavid du Colombier 			drive->command = Cwd;
13847dd7cddfSDavid du Colombier 		else
13857dd7cddfSDavid du Colombier 			drive->command = Crd;
13867dd7cddfSDavid du Colombier 	}
13877dd7cddfSDavid du Colombier 	else if(drive->rwmctl){
13887dd7cddfSDavid du Colombier 		drive->block = drive->rwm*drive->secsize;
13897dd7cddfSDavid du Colombier 		if(drive->write)
13907dd7cddfSDavid du Colombier 			drive->command = Cwsm;
13917dd7cddfSDavid du Colombier 		else
13927dd7cddfSDavid du Colombier 			drive->command = Crsm;
13937dd7cddfSDavid du Colombier 	}
13947dd7cddfSDavid du Colombier 	else{
13957dd7cddfSDavid du Colombier 		drive->block = drive->secsize;
13967dd7cddfSDavid du Colombier 		if(drive->write)
13977dd7cddfSDavid du Colombier 			drive->command = Cws;
13987dd7cddfSDavid du Colombier 		else
13997dd7cddfSDavid du Colombier 			drive->command = Crs;
14007dd7cddfSDavid du Colombier 	}
14017dd7cddfSDavid du Colombier 	drive->limit = drive->data + drive->count*drive->secsize;
14026a081dcdSDavid du Colombier 	cmd = drive->command;
14035ea8af7bSDavid du Colombier 	if(use48){
140491157df7SDavid du Colombier 		outb(cmdport+Count, drive->count>>8);
140591157df7SDavid du Colombier 		outb(cmdport+Count, drive->count);
140691157df7SDavid du Colombier 		outb(cmdport+Lbalo, lba>>24);
140791157df7SDavid du Colombier 		outb(cmdport+Lbalo, lba);
140891157df7SDavid du Colombier 		outb(cmdport+Lbamid, lba>>32);
140991157df7SDavid du Colombier 		outb(cmdport+Lbamid, lba>>8);
141091157df7SDavid du Colombier 		outb(cmdport+Lbahi, lba>>40);
141191157df7SDavid du Colombier 		outb(cmdport+Lbahi, lba>>16);
14126a081dcdSDavid du Colombier 		outb(cmdport+Dh, drive->dev|Lba);
14136a081dcdSDavid du Colombier 		cmd = cmd48[cmd];
14145ea8af7bSDavid du Colombier 
14156a081dcdSDavid du Colombier 		if(DEBUG & Dbg48BIT)
14166a081dcdSDavid du Colombier 			print("using 48-bit commands\n");
141759f4c0c1SDavid du Colombier 	}
141859f4c0c1SDavid du Colombier 	else{
14197dd7cddfSDavid du Colombier 		outb(cmdport+Count, drive->count);
14207dd7cddfSDavid du Colombier 		outb(cmdport+Sector, s);
14217dd7cddfSDavid du Colombier 		outb(cmdport+Cyllo, c);
14227dd7cddfSDavid du Colombier 		outb(cmdport+Cylhi, c>>8);
14236a081dcdSDavid du Colombier 		outb(cmdport+Dh, drive->dev|h);
14245ea8af7bSDavid du Colombier 	}
14257dd7cddfSDavid du Colombier 	ctlr->done = 0;
14267dd7cddfSDavid du Colombier 	ctlr->curdrive = drive;
14277dd7cddfSDavid du Colombier 	ctlr->command = drive->command;	/* debugging */
14286a081dcdSDavid du Colombier 	outb(cmdport+Command, cmd);
14297dd7cddfSDavid du Colombier 
14307dd7cddfSDavid du Colombier 	switch(drive->command){
14317dd7cddfSDavid du Colombier 	case Cws:
14327dd7cddfSDavid du Colombier 	case Cwsm:
14337dd7cddfSDavid du Colombier 		microdelay(1);
14344de34a7eSDavid du Colombier 		/* 10*1000 for flash ide drives - maybe detect them? */
14354de34a7eSDavid du Colombier 		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*1000);
14367dd7cddfSDavid du Colombier 		if(as < 0 || (as & Err)){
14377dd7cddfSDavid du Colombier 			iunlock(ctlr);
14387dd7cddfSDavid du Colombier 			return -1;
14397dd7cddfSDavid du Colombier 		}
14407dd7cddfSDavid du Colombier 		len = drive->block;
14417dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
14427dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
14437dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->data, len/2);
14447dd7cddfSDavid du Colombier 		break;
14457dd7cddfSDavid du Colombier 
14467dd7cddfSDavid du Colombier 	case Crd:
14477dd7cddfSDavid du Colombier 	case Cwd:
14487dd7cddfSDavid du Colombier 		atadmastart(ctlr, drive->write);
14497dd7cddfSDavid du Colombier 		break;
14507dd7cddfSDavid du Colombier 	}
14517dd7cddfSDavid du Colombier 	iunlock(ctlr);
14527dd7cddfSDavid du Colombier 
14537dd7cddfSDavid du Colombier 	return 0;
14547dd7cddfSDavid du Colombier }
14557dd7cddfSDavid du Colombier 
14567dd7cddfSDavid du Colombier static int
14577dd7cddfSDavid du Colombier atagenioretry(Drive* drive)
14587dd7cddfSDavid du Colombier {
14596de6ce84SDavid du Colombier 	if(drive->dmactl){
14607dd7cddfSDavid du Colombier 		drive->dmactl = 0;
14616de6ce84SDavid du Colombier 		print("atagenioretry: disabling dma\n");
1462ef9eff0bSDavid du Colombier 	}
1463ef9eff0bSDavid du Colombier 	else if(drive->rwmctl)
14647dd7cddfSDavid du Colombier 		drive->rwmctl = 0;
14657dd7cddfSDavid du Colombier 	else
14667dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 4, 8, drive->error);
14677dd7cddfSDavid du Colombier 
14687dd7cddfSDavid du Colombier 	return SDretry;
14697dd7cddfSDavid du Colombier }
14707dd7cddfSDavid du Colombier 
14717dd7cddfSDavid du Colombier static int
147291157df7SDavid du Colombier atagenio(Drive* drive, uchar* cmd, int clen)
14737dd7cddfSDavid du Colombier {
14747dd7cddfSDavid du Colombier 	uchar *p;
14757dd7cddfSDavid du Colombier 	Ctlr *ctlr;
14766a081dcdSDavid du Colombier 	vlong lba, len;
14774de34a7eSDavid du Colombier 	int count, maxio;
14787dd7cddfSDavid du Colombier 
14797dd7cddfSDavid du Colombier 	/*
14807dd7cddfSDavid du Colombier 	 * Map SCSI commands into ATA commands for discs.
14817dd7cddfSDavid du Colombier 	 * Fail any command with a LUN except INQUIRY which
14827dd7cddfSDavid du Colombier 	 * will return 'logical unit not supported'.
14837dd7cddfSDavid du Colombier 	 */
14847dd7cddfSDavid du Colombier 	if((cmd[1]>>5) && cmd[0] != 0x12)
14857dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x25, 0);
14867dd7cddfSDavid du Colombier 
14877dd7cddfSDavid du Colombier 	switch(cmd[0]){
14887dd7cddfSDavid du Colombier 	default:
14897dd7cddfSDavid du Colombier 		return atasetsense(drive, SDcheck, 0x05, 0x20, 0);
14907dd7cddfSDavid du Colombier 
14917dd7cddfSDavid du Colombier 	case 0x00:			/* test unit ready */
14927dd7cddfSDavid du Colombier 		return SDok;
14937dd7cddfSDavid du Colombier 
14947dd7cddfSDavid du Colombier 	case 0x03:			/* request sense */
14957dd7cddfSDavid du Colombier 		if(cmd[4] < sizeof(drive->sense))
14967dd7cddfSDavid du Colombier 			len = cmd[4];
14977dd7cddfSDavid du Colombier 		else
14987dd7cddfSDavid du Colombier 			len = sizeof(drive->sense);
14997dd7cddfSDavid du Colombier 		if(drive->data && drive->dlen >= len){
15007dd7cddfSDavid du Colombier 			memmove(drive->data, drive->sense, len);
15017dd7cddfSDavid du Colombier 			drive->data += len;
15027dd7cddfSDavid du Colombier 		}
15037dd7cddfSDavid du Colombier 		return SDok;
15047dd7cddfSDavid du Colombier 
15057dd7cddfSDavid du Colombier 	case 0x12:			/* inquiry */
15067dd7cddfSDavid du Colombier 		if(cmd[4] < sizeof(drive->inquiry))
15077dd7cddfSDavid du Colombier 			len = cmd[4];
15087dd7cddfSDavid du Colombier 		else
15097dd7cddfSDavid du Colombier 			len = sizeof(drive->inquiry);
15107dd7cddfSDavid du Colombier 		if(drive->data && drive->dlen >= len){
15117dd7cddfSDavid du Colombier 			memmove(drive->data, drive->inquiry, len);
15127dd7cddfSDavid du Colombier 			drive->data += len;
15137dd7cddfSDavid du Colombier 		}
15147dd7cddfSDavid du Colombier 		return SDok;
15157dd7cddfSDavid du Colombier 
15167dd7cddfSDavid du Colombier 	case 0x1B:			/* start/stop unit */
15177dd7cddfSDavid du Colombier 		/*
15187dd7cddfSDavid du Colombier 		 * NOP for now, can use the power management feature
15197dd7cddfSDavid du Colombier 		 * set later.
15207dd7cddfSDavid du Colombier 		 */
15217dd7cddfSDavid du Colombier 		return SDok;
15227dd7cddfSDavid du Colombier 
15237dd7cddfSDavid du Colombier 	case 0x25:			/* read capacity */
15247dd7cddfSDavid du Colombier 		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
15257dd7cddfSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
15267dd7cddfSDavid du Colombier 		if(drive->data == nil || drive->dlen < 8)
15277dd7cddfSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
15287dd7cddfSDavid du Colombier 		/*
15297dd7cddfSDavid du Colombier 		 * Read capacity returns the LBA of the last sector.
15307dd7cddfSDavid du Colombier 		 */
15317dd7cddfSDavid du Colombier 		len = drive->sectors-1;
15327dd7cddfSDavid du Colombier 		p = drive->data;
15337dd7cddfSDavid du Colombier 		*p++ = len>>24;
15347dd7cddfSDavid du Colombier 		*p++ = len>>16;
15357dd7cddfSDavid du Colombier 		*p++ = len>>8;
15367dd7cddfSDavid du Colombier 		*p++ = len;
15377dd7cddfSDavid du Colombier 		len = drive->secsize;
15387dd7cddfSDavid du Colombier 		*p++ = len>>24;
15397dd7cddfSDavid du Colombier 		*p++ = len>>16;
15407dd7cddfSDavid du Colombier 		*p++ = len>>8;
15417dd7cddfSDavid du Colombier 		*p = len;
15427dd7cddfSDavid du Colombier 		drive->data += 8;
15437dd7cddfSDavid du Colombier 		return SDok;
15447dd7cddfSDavid du Colombier 
15456a081dcdSDavid du Colombier 	case 0x9E:			/* long read capacity */
15466a081dcdSDavid du Colombier 		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
15476a081dcdSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
15486a081dcdSDavid du Colombier 		if(drive->data == nil || drive->dlen < 8)
15496a081dcdSDavid du Colombier 			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
15506a081dcdSDavid du Colombier 		/*
15516a081dcdSDavid du Colombier 		 * Read capacity returns the LBA of the last sector.
15526a081dcdSDavid du Colombier 		 */
15536a081dcdSDavid du Colombier 		len = drive->sectors-1;
15546a081dcdSDavid du Colombier 		p = drive->data;
15556a081dcdSDavid du Colombier 		*p++ = len>>56;
15566a081dcdSDavid du Colombier 		*p++ = len>>48;
15576a081dcdSDavid du Colombier 		*p++ = len>>40;
15586a081dcdSDavid du Colombier 		*p++ = len>>32;
15596a081dcdSDavid du Colombier 		*p++ = len>>24;
15606a081dcdSDavid du Colombier 		*p++ = len>>16;
15616a081dcdSDavid du Colombier 		*p++ = len>>8;
15626a081dcdSDavid du Colombier 		*p++ = len;
15636a081dcdSDavid du Colombier 		len = drive->secsize;
15646a081dcdSDavid du Colombier 		*p++ = len>>24;
15656a081dcdSDavid du Colombier 		*p++ = len>>16;
15666a081dcdSDavid du Colombier 		*p++ = len>>8;
15676a081dcdSDavid du Colombier 		*p = len;
15684de34a7eSDavid du Colombier 		drive->data += 12;
15696a081dcdSDavid du Colombier 		return SDok;
15706a081dcdSDavid du Colombier 
15717dd7cddfSDavid du Colombier 	case 0x28:			/* read */
157291157df7SDavid du Colombier 	case 0x88:
157391157df7SDavid du Colombier 	case 0x2a:			/* write */
157491157df7SDavid du Colombier 	case 0x8a:
15757dd7cddfSDavid du Colombier 		break;
15767dd7cddfSDavid du Colombier 
15777dd7cddfSDavid du Colombier 	case 0x5A:
15787dd7cddfSDavid du Colombier 		return atamodesense(drive, cmd);
15797dd7cddfSDavid du Colombier 	}
15807dd7cddfSDavid du Colombier 
15817dd7cddfSDavid du Colombier 	ctlr = drive->ctlr;
158291157df7SDavid du Colombier 	if(clen == 16){
158391157df7SDavid du Colombier 		/* ata commands only go to 48-bit lba */
158491157df7SDavid du Colombier 		if(cmd[2] || cmd[3])
158591157df7SDavid du Colombier 			return atasetsense(drive, SDcheck, 3, 0xc, 2);
158691157df7SDavid du Colombier 		lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32;
158791157df7SDavid du Colombier 		lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9];
158891157df7SDavid du Colombier 		count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
158991157df7SDavid du Colombier 	}else{
159091157df7SDavid du Colombier 		lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
159191157df7SDavid du Colombier 		count = cmd[7]<<8 | cmd[8];
159291157df7SDavid du Colombier 	}
15937dd7cddfSDavid du Colombier 	if(drive->data == nil)
15947dd7cddfSDavid du Colombier 		return SDok;
15957dd7cddfSDavid du Colombier 	if(drive->dlen < count*drive->secsize)
15967dd7cddfSDavid du Colombier 		count = drive->dlen/drive->secsize;
15977dd7cddfSDavid du Colombier 	qlock(ctlr);
15984de34a7eSDavid du Colombier 	if(ctlr->maxio)
15994de34a7eSDavid du Colombier 		maxio = ctlr->maxio;
16004de34a7eSDavid du Colombier 	else if(drive->flags & Lba48)
16014de34a7eSDavid du Colombier 		maxio = 65536;
16024de34a7eSDavid du Colombier 	else
16034de34a7eSDavid du Colombier 		maxio = 256;
16047dd7cddfSDavid du Colombier 	while(count){
16054de34a7eSDavid du Colombier 		if(count > maxio)
16064de34a7eSDavid du Colombier 			drive->count = maxio;
16077dd7cddfSDavid du Colombier 		else
16087dd7cddfSDavid du Colombier 			drive->count = count;
16097dd7cddfSDavid du Colombier 		if(atageniostart(drive, lba)){
16107dd7cddfSDavid du Colombier 			ilock(ctlr);
16117dd7cddfSDavid du Colombier 			atanop(drive, 0);
16127dd7cddfSDavid du Colombier 			iunlock(ctlr);
16137dd7cddfSDavid du Colombier 			qunlock(ctlr);
16147dd7cddfSDavid du Colombier 			return atagenioretry(drive);
16157dd7cddfSDavid du Colombier 		}
16167dd7cddfSDavid du Colombier 
16177dd7cddfSDavid du Colombier 		while(waserror())
16187dd7cddfSDavid du Colombier 			;
16194de34a7eSDavid du Colombier 		tsleep(ctlr, atadone, ctlr, 60*1000);
16207dd7cddfSDavid du Colombier 		poperror();
16217dd7cddfSDavid du Colombier 		if(!ctlr->done){
16227dd7cddfSDavid du Colombier 			/*
16237dd7cddfSDavid du Colombier 			 * What should the above timeout be? In
16247dd7cddfSDavid du Colombier 			 * standby and sleep modes it could take as
16257dd7cddfSDavid du Colombier 			 * long as 30 seconds for a drive to respond.
16267dd7cddfSDavid du Colombier 			 * Very hard to get out of this cleanly.
16277dd7cddfSDavid du Colombier 			 */
16287dd7cddfSDavid du Colombier 			atadumpstate(drive, cmd, lba, count);
16297dd7cddfSDavid du Colombier 			ataabort(drive, 1);
163080ee5cbfSDavid du Colombier 			qunlock(ctlr);
16317dd7cddfSDavid du Colombier 			return atagenioretry(drive);
16327dd7cddfSDavid du Colombier 		}
16337dd7cddfSDavid du Colombier 
16347dd7cddfSDavid du Colombier 		if(drive->status & Err){
16357dd7cddfSDavid du Colombier 			qunlock(ctlr);
16367dd7cddfSDavid du Colombier 			return atasetsense(drive, SDcheck, 4, 8, drive->error);
16377dd7cddfSDavid du Colombier 		}
16387dd7cddfSDavid du Colombier 		count -= drive->count;
16397dd7cddfSDavid du Colombier 		lba += drive->count;
16407dd7cddfSDavid du Colombier 	}
16417dd7cddfSDavid du Colombier 	qunlock(ctlr);
16427dd7cddfSDavid du Colombier 
16437dd7cddfSDavid du Colombier 	return SDok;
16447dd7cddfSDavid du Colombier }
16457dd7cddfSDavid du Colombier 
16467dd7cddfSDavid du Colombier static int
16477dd7cddfSDavid du Colombier atario(SDreq* r)
16487dd7cddfSDavid du Colombier {
16497dd7cddfSDavid du Colombier 	Ctlr *ctlr;
16507dd7cddfSDavid du Colombier 	Drive *drive;
16517dd7cddfSDavid du Colombier 	SDunit *unit;
16527dd7cddfSDavid du Colombier 	uchar cmd10[10], *cmdp, *p;
16537dd7cddfSDavid du Colombier 	int clen, reqstatus, status;
16547dd7cddfSDavid du Colombier 
16557dd7cddfSDavid du Colombier 	unit = r->unit;
16567dd7cddfSDavid du Colombier 	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
16577dd7cddfSDavid du Colombier 		r->status = SDtimeout;
16587dd7cddfSDavid du Colombier 		return SDtimeout;
16597dd7cddfSDavid du Colombier 	}
16607dd7cddfSDavid du Colombier 	drive = ctlr->drive[unit->subno];
16617dd7cddfSDavid du Colombier 
16627dd7cddfSDavid du Colombier 	/*
16637dd7cddfSDavid du Colombier 	 * Most SCSI commands can be passed unchanged except for
16647dd7cddfSDavid du Colombier 	 * the padding on the end. The few which require munging
16657dd7cddfSDavid du Colombier 	 * are not used internally. Mode select/sense(6) could be
16667dd7cddfSDavid du Colombier 	 * converted to the 10-byte form but it's not worth the
16677dd7cddfSDavid du Colombier 	 * effort. Read/write(6) are easy.
16687dd7cddfSDavid du Colombier 	 */
16697dd7cddfSDavid du Colombier 	switch(r->cmd[0]){
16707dd7cddfSDavid du Colombier 	case 0x08:			/* read */
16717dd7cddfSDavid du Colombier 	case 0x0A:			/* write */
16727dd7cddfSDavid du Colombier 		cmdp = cmd10;
16737dd7cddfSDavid du Colombier 		memset(cmdp, 0, sizeof(cmd10));
16747dd7cddfSDavid du Colombier 		cmdp[0] = r->cmd[0]|0x20;
16757dd7cddfSDavid du Colombier 		cmdp[1] = r->cmd[1] & 0xE0;
16767dd7cddfSDavid du Colombier 		cmdp[5] = r->cmd[3];
16777dd7cddfSDavid du Colombier 		cmdp[4] = r->cmd[2];
16787dd7cddfSDavid du Colombier 		cmdp[3] = r->cmd[1] & 0x0F;
16797dd7cddfSDavid du Colombier 		cmdp[8] = r->cmd[4];
16807dd7cddfSDavid du Colombier 		clen = sizeof(cmd10);
16817dd7cddfSDavid du Colombier 		break;
16827dd7cddfSDavid du Colombier 
16837dd7cddfSDavid du Colombier 	default:
16847dd7cddfSDavid du Colombier 		cmdp = r->cmd;
16857dd7cddfSDavid du Colombier 		clen = r->clen;
16867dd7cddfSDavid du Colombier 		break;
16877dd7cddfSDavid du Colombier 	}
16887dd7cddfSDavid du Colombier 
16897dd7cddfSDavid du Colombier 	qlock(drive);
16907dd7cddfSDavid du Colombier retry:
16917dd7cddfSDavid du Colombier 	drive->write = r->write;
16927dd7cddfSDavid du Colombier 	drive->data = r->data;
16937dd7cddfSDavid du Colombier 	drive->dlen = r->dlen;
16947dd7cddfSDavid du Colombier 
16957dd7cddfSDavid du Colombier 	drive->status = 0;
16967dd7cddfSDavid du Colombier 	drive->error = 0;
16977dd7cddfSDavid du Colombier 	if(drive->pkt)
16987dd7cddfSDavid du Colombier 		status = atapktio(drive, cmdp, clen);
16997dd7cddfSDavid du Colombier 	else
17007dd7cddfSDavid du Colombier 		status = atagenio(drive, cmdp, clen);
17017dd7cddfSDavid du Colombier 	if(status == SDretry){
17027dd7cddfSDavid du Colombier 		if(DbgDEBUG)
17037dd7cddfSDavid du Colombier 			print("%s: retry: dma %8.8uX rwm %4.4uX\n",
17047dd7cddfSDavid du Colombier 				unit->name, drive->dmactl, drive->rwmctl);
17057dd7cddfSDavid du Colombier 		goto retry;
17067dd7cddfSDavid du Colombier 	}
17077dd7cddfSDavid du Colombier 	if(status == SDok){
17087dd7cddfSDavid du Colombier 		atasetsense(drive, SDok, 0, 0, 0);
17097dd7cddfSDavid du Colombier 		if(drive->data){
17107dd7cddfSDavid du Colombier 			p = r->data;
17117dd7cddfSDavid du Colombier 			r->rlen = drive->data - p;
17127dd7cddfSDavid du Colombier 		}
17137dd7cddfSDavid du Colombier 		else
17147dd7cddfSDavid du Colombier 			r->rlen = 0;
17157dd7cddfSDavid du Colombier 	}
17167dd7cddfSDavid du Colombier 	else if(status == SDcheck && !(r->flags & SDnosense)){
17177dd7cddfSDavid du Colombier 		drive->write = 0;
17187dd7cddfSDavid du Colombier 		memset(cmd10, 0, sizeof(cmd10));
17197dd7cddfSDavid du Colombier 		cmd10[0] = 0x03;
17207dd7cddfSDavid du Colombier 		cmd10[1] = r->lun<<5;
17217dd7cddfSDavid du Colombier 		cmd10[4] = sizeof(r->sense)-1;
17227dd7cddfSDavid du Colombier 		drive->data = r->sense;
17237dd7cddfSDavid du Colombier 		drive->dlen = sizeof(r->sense)-1;
17247dd7cddfSDavid du Colombier 		drive->status = 0;
17257dd7cddfSDavid du Colombier 		drive->error = 0;
17267dd7cddfSDavid du Colombier 		if(drive->pkt)
17277dd7cddfSDavid du Colombier 			reqstatus = atapktio(drive, cmd10, 6);
17287dd7cddfSDavid du Colombier 		else
17297dd7cddfSDavid du Colombier 			reqstatus = atagenio(drive, cmd10, 6);
17307dd7cddfSDavid du Colombier 		if(reqstatus == SDok){
17317dd7cddfSDavid du Colombier 			r->flags |= SDvalidsense;
17327dd7cddfSDavid du Colombier 			atasetsense(drive, SDok, 0, 0, 0);
17337dd7cddfSDavid du Colombier 		}
17347dd7cddfSDavid du Colombier 	}
17357dd7cddfSDavid du Colombier 	qunlock(drive);
17367dd7cddfSDavid du Colombier 	r->status = status;
17377dd7cddfSDavid du Colombier 	if(status != SDok)
17387dd7cddfSDavid du Colombier 		return status;
17397dd7cddfSDavid du Colombier 
17407dd7cddfSDavid du Colombier 	/*
17417dd7cddfSDavid du Colombier 	 * Fix up any results.
17427dd7cddfSDavid du Colombier 	 * Many ATAPI CD-ROMs ignore the LUN field completely and
17437dd7cddfSDavid du Colombier 	 * return valid INQUIRY data. Patch the response to indicate
17447dd7cddfSDavid du Colombier 	 * 'logical unit not supported' if the LUN is non-zero.
17457dd7cddfSDavid du Colombier 	 */
17467dd7cddfSDavid du Colombier 	switch(cmdp[0]){
17477dd7cddfSDavid du Colombier 	case 0x12:			/* inquiry */
17487dd7cddfSDavid du Colombier 		if((p = r->data) == nil)
17497dd7cddfSDavid du Colombier 			break;
17507dd7cddfSDavid du Colombier 		if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05))
17517dd7cddfSDavid du Colombier 			p[0] = 0x7F;
17527dd7cddfSDavid du Colombier 		/*FALLTHROUGH*/
17537dd7cddfSDavid du Colombier 	default:
17547dd7cddfSDavid du Colombier 		break;
17557dd7cddfSDavid du Colombier 	}
17567dd7cddfSDavid du Colombier 
17577dd7cddfSDavid du Colombier 	return SDok;
17587dd7cddfSDavid du Colombier }
17597dd7cddfSDavid du Colombier 
17607dd7cddfSDavid du Colombier static void
17617dd7cddfSDavid du Colombier atainterrupt(Ureg*, void* arg)
17627dd7cddfSDavid du Colombier {
17637dd7cddfSDavid du Colombier 	Ctlr *ctlr;
17647dd7cddfSDavid du Colombier 	Drive *drive;
17657dd7cddfSDavid du Colombier 	int cmdport, len, status;
17667dd7cddfSDavid du Colombier 
17677dd7cddfSDavid du Colombier 	ctlr = arg;
17687dd7cddfSDavid du Colombier 
17697dd7cddfSDavid du Colombier 	ilock(ctlr);
17707dd7cddfSDavid du Colombier 	if(inb(ctlr->ctlport+As) & Bsy){
17717dd7cddfSDavid du Colombier 		iunlock(ctlr);
1772503d3e0aSDavid du Colombier 		if(DEBUG & DbgBsy)
17737dd7cddfSDavid du Colombier 			print("IBsy+");
17747dd7cddfSDavid du Colombier 		return;
17757dd7cddfSDavid du Colombier 	}
17767dd7cddfSDavid du Colombier 	cmdport = ctlr->cmdport;
17777dd7cddfSDavid du Colombier 	status = inb(cmdport+Status);
17787dd7cddfSDavid du Colombier 	if((drive = ctlr->curdrive) == nil){
17797dd7cddfSDavid du Colombier 		iunlock(ctlr);
17803ff48bf5SDavid du Colombier 		if((DEBUG & DbgINL) && ctlr->command != Cedd)
17819a747e4fSDavid du Colombier 			print("Inil%2.2uX+", ctlr->command);
17827dd7cddfSDavid du Colombier 		return;
17837dd7cddfSDavid du Colombier 	}
17847dd7cddfSDavid du Colombier 
17857dd7cddfSDavid du Colombier 	if(status & Err)
17867dd7cddfSDavid du Colombier 		drive->error = inb(cmdport+Error);
17877dd7cddfSDavid du Colombier 	else switch(drive->command){
17887dd7cddfSDavid du Colombier 	default:
17897dd7cddfSDavid du Colombier 		drive->error = Abrt;
17907dd7cddfSDavid du Colombier 		break;
17917dd7cddfSDavid du Colombier 
17927dd7cddfSDavid du Colombier 	case Crs:
17937dd7cddfSDavid du Colombier 	case Crsm:
17947dd7cddfSDavid du Colombier 		if(!(status & Drq)){
17957dd7cddfSDavid du Colombier 			drive->error = Abrt;
17967dd7cddfSDavid du Colombier 			break;
17977dd7cddfSDavid du Colombier 		}
17987dd7cddfSDavid du Colombier 		len = drive->block;
17997dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
18007dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
18017dd7cddfSDavid du Colombier 		inss(cmdport+Data, drive->data, len/2);
18027dd7cddfSDavid du Colombier 		drive->data += len;
18037dd7cddfSDavid du Colombier 		if(drive->data >= drive->limit)
18047dd7cddfSDavid du Colombier 			ctlr->done = 1;
18057dd7cddfSDavid du Colombier 		break;
18067dd7cddfSDavid du Colombier 
18077dd7cddfSDavid du Colombier 	case Cws:
18087dd7cddfSDavid du Colombier 	case Cwsm:
18097dd7cddfSDavid du Colombier 		len = drive->block;
18107dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
18117dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
18127dd7cddfSDavid du Colombier 		drive->data += len;
18137dd7cddfSDavid du Colombier 		if(drive->data >= drive->limit){
18147dd7cddfSDavid du Colombier 			ctlr->done = 1;
18157dd7cddfSDavid du Colombier 			break;
18167dd7cddfSDavid du Colombier 		}
18177dd7cddfSDavid du Colombier 		if(!(status & Drq)){
18187dd7cddfSDavid du Colombier 			drive->error = Abrt;
18197dd7cddfSDavid du Colombier 			break;
18207dd7cddfSDavid du Colombier 		}
18217dd7cddfSDavid du Colombier 		len = drive->block;
18227dd7cddfSDavid du Colombier 		if(drive->data+len > drive->limit)
18237dd7cddfSDavid du Colombier 			len = drive->limit-drive->data;
18247dd7cddfSDavid du Colombier 		outss(cmdport+Data, drive->data, len/2);
18257dd7cddfSDavid du Colombier 		break;
18267dd7cddfSDavid du Colombier 
18277dd7cddfSDavid du Colombier 	case Cpkt:
18287dd7cddfSDavid du Colombier 		atapktinterrupt(drive);
18297dd7cddfSDavid du Colombier 		break;
18307dd7cddfSDavid du Colombier 
18317dd7cddfSDavid du Colombier 	case Crd:
18327dd7cddfSDavid du Colombier 	case Cwd:
18337dd7cddfSDavid du Colombier 		atadmainterrupt(drive, drive->count*drive->secsize);
18347dd7cddfSDavid du Colombier 		break;
183580ee5cbfSDavid du Colombier 
183680ee5cbfSDavid du Colombier 	case Cstandby:
183780ee5cbfSDavid du Colombier 		ctlr->done = 1;
183880ee5cbfSDavid du Colombier 		break;
18397dd7cddfSDavid du Colombier 	}
18407dd7cddfSDavid du Colombier 	iunlock(ctlr);
18417dd7cddfSDavid du Colombier 
18427dd7cddfSDavid du Colombier 	if(drive->error){
18437dd7cddfSDavid du Colombier 		status |= Err;
18447dd7cddfSDavid du Colombier 		ctlr->done = 1;
18457dd7cddfSDavid du Colombier 	}
18467dd7cddfSDavid du Colombier 
18477dd7cddfSDavid du Colombier 	if(ctlr->done){
18487dd7cddfSDavid du Colombier 		ctlr->curdrive = nil;
18497dd7cddfSDavid du Colombier 		drive->status = status;
18507dd7cddfSDavid du Colombier 		wakeup(ctlr);
18517dd7cddfSDavid du Colombier 	}
18527dd7cddfSDavid du Colombier }
18537dd7cddfSDavid du Colombier 
18547dd7cddfSDavid du Colombier static SDev*
18557dd7cddfSDavid du Colombier atapnp(void)
18567dd7cddfSDavid du Colombier {
18577dd7cddfSDavid du Colombier 	Ctlr *ctlr;
18587dd7cddfSDavid du Colombier 	Pcidev *p;
18597dd7cddfSDavid du Colombier 	SDev *legacy[2], *sdev, *head, *tail;
18604de34a7eSDavid du Colombier 	int channel, ispc87415, maxio, pi, r, span;
18617dd7cddfSDavid du Colombier 
18627dd7cddfSDavid du Colombier 	legacy[0] = legacy[1] = head = tail = nil;
18637dd7cddfSDavid du Colombier 	if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){
18647dd7cddfSDavid du Colombier 		head = tail = sdev;
18657dd7cddfSDavid du Colombier 		legacy[0] = sdev;
18667dd7cddfSDavid du Colombier 	}
18677dd7cddfSDavid du Colombier 	if(sdev = ataprobe(0x170, 0x374, IrqATA1)){
18687dd7cddfSDavid du Colombier 		if(head != nil)
18697dd7cddfSDavid du Colombier 			tail->next = sdev;
18707dd7cddfSDavid du Colombier 		else
18717dd7cddfSDavid du Colombier 			head = sdev;
18727dd7cddfSDavid du Colombier 		tail = sdev;
18737dd7cddfSDavid du Colombier 		legacy[1] = sdev;
18747dd7cddfSDavid du Colombier 	}
18757dd7cddfSDavid du Colombier 
18767dd7cddfSDavid du Colombier 	p = nil;
18777dd7cddfSDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
18787dd7cddfSDavid du Colombier 		/*
18797dd7cddfSDavid du Colombier 		 * Look for devices with the correct class and sub-class
18807dd7cddfSDavid du Colombier 		 * code and known device and vendor ID; add native-mode
18817dd7cddfSDavid du Colombier 		 * channels to the list to be probed, save info for the
18827dd7cddfSDavid du Colombier 		 * compatibility mode channels.
18837dd7cddfSDavid du Colombier 		 * Note that the legacy devices should not be considered
18847dd7cddfSDavid du Colombier 		 * PCI devices by the interrupt controller.
18857dd7cddfSDavid du Colombier 		 * For both native and legacy, save info for busmastering
18867dd7cddfSDavid du Colombier 		 * if capable.
18877dd7cddfSDavid du Colombier 		 * Promise Ultra ATA/66 (PDC20262) appears to
18887dd7cddfSDavid du Colombier 		 * 1) give a sub-class of 'other mass storage controller'
18897dd7cddfSDavid du Colombier 		 *    instead of 'IDE controller', regardless of whether it's
18907dd7cddfSDavid du Colombier 		 *    the only controller or not;
18917dd7cddfSDavid du Colombier 		 * 2) put 0 in the programming interface byte (probably
18927dd7cddfSDavid du Colombier 		 *    as a consequence of 1) above).
1893503d3e0aSDavid du Colombier 		 * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
18947dd7cddfSDavid du Colombier 		 */
189559c21d95SDavid du Colombier 		if(p->ccrb != 0x01)
189659c21d95SDavid du Colombier 			continue;
189759c21d95SDavid du Colombier 		if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80)
18987dd7cddfSDavid du Colombier 			continue;
18997dd7cddfSDavid du Colombier 		pi = p->ccrp;
19007dd7cddfSDavid du Colombier 		ispc87415 = 0;
19014de34a7eSDavid du Colombier 		maxio = 0;
19024de34a7eSDavid du Colombier 		span = BMspan;
19037dd7cddfSDavid du Colombier 
19047dd7cddfSDavid du Colombier 		switch((p->did<<16)|p->vid){
19057dd7cddfSDavid du Colombier 		default:
19067dd7cddfSDavid du Colombier 			continue;
19077dd7cddfSDavid du Colombier 
19087dd7cddfSDavid du Colombier 		case (0x0002<<16)|0x100B:	/* NS PC87415 */
19097dd7cddfSDavid du Colombier 			/*
19107dd7cddfSDavid du Colombier 			 * Disable interrupts on both channels until
19117dd7cddfSDavid du Colombier 			 * after they are probed for drives.
19127dd7cddfSDavid du Colombier 			 * This must be called before interrupts are
19137dd7cddfSDavid du Colombier 			 * enabled because the IRQ may be shared.
19147dd7cddfSDavid du Colombier 			 */
19157dd7cddfSDavid du Colombier 			ispc87415 = 1;
19167dd7cddfSDavid du Colombier 			pcicfgw32(p, 0x40, 0x00000300);
19177dd7cddfSDavid du Colombier 			break;
19187dd7cddfSDavid du Colombier 		case (0x1000<<16)|0x1042:	/* PC-Tech RZ1000 */
19197dd7cddfSDavid du Colombier 			/*
19207dd7cddfSDavid du Colombier 			 * Turn off prefetch. Overkill, but cheap.
19217dd7cddfSDavid du Colombier 			 */
19227dd7cddfSDavid du Colombier 			r = pcicfgr32(p, 0x40);
19237dd7cddfSDavid du Colombier 			r &= ~0x2000;
19247dd7cddfSDavid du Colombier 			pcicfgw32(p, 0x40, r);
19257dd7cddfSDavid du Colombier 			break;
19267dd7cddfSDavid du Colombier 		case (0x4D38<<16)|0x105A:	/* Promise PDC20262 */
1927b7b24591SDavid du Colombier 		case (0x4D30<<16)|0x105A:	/* Promise PDC202xx */
19286a081dcdSDavid du Colombier 		case (0x4D68<<16)|0x105A:	/* Promise PDC20268 */
19294c5c8adbSDavid du Colombier 		case (0x4D69<<16)|0x105A:	/* Promise Ultra/133 TX2 */
1930ca2418feSDavid du Colombier 		case (0x3373<<16)|0x105A:	/* Promise 20378 RAID */
1931503d3e0aSDavid du Colombier 		case (0x3149<<16)|0x1106:	/* VIA VT8237 SATA/RAID */
19324de34a7eSDavid du Colombier 		case (0x3112<<16)|0x1095:	/* SiI 3112 SATA/RAID */
19334de34a7eSDavid du Colombier 			maxio = 15;
19344de34a7eSDavid du Colombier 			span = 8*1024;
19354de34a7eSDavid du Colombier 			/*FALLTHROUGH*/
1936d1be6b08SDavid du Colombier 		case (0x0680<<16)|0x1095:	/* SiI 0680/680A PATA133 ATAPI/RAID */
19374de34a7eSDavid du Colombier 		case (0x3114<<16)|0x1095:	/* SiI 3114 SATA/RAID */
1938fb7f0c93SDavid du Colombier 			pi = 0x85;
1939fb7f0c93SDavid du Colombier 			break;
19403806af99SDavid du Colombier 		case (0x0004<<16)|0x1103:	/* HighPoint HPT366 */
19417dd7cddfSDavid du Colombier 			pi = 0x85;
1942fb7f0c93SDavid du Colombier 			/*
1943fb7f0c93SDavid du Colombier 			 * Turn off fast interrupt prediction.
1944fb7f0c93SDavid du Colombier 			 */
1945fb7f0c93SDavid du Colombier 			if((r = pcicfgr8(p, 0x51)) & 0x80)
1946fb7f0c93SDavid du Colombier 				pcicfgw8(p, 0x51, r & ~0x80);
1947fb7f0c93SDavid du Colombier 			if((r = pcicfgr8(p, 0x55)) & 0x80)
1948fb7f0c93SDavid du Colombier 				pcicfgw8(p, 0x55, r & ~0x80);
19497dd7cddfSDavid du Colombier 			break;
19507dd7cddfSDavid du Colombier 		case (0x0640<<16)|0x1095:	/* CMD 640B */
19517dd7cddfSDavid du Colombier 			/*
19527dd7cddfSDavid du Colombier 			 * Bugfix code here...
19537dd7cddfSDavid du Colombier 			 */
19547dd7cddfSDavid du Colombier 			break;
1955f92e8f2eSDavid du Colombier 		case (0x7441<<16)|0x1022:	/* AMD 768 */
1956f92e8f2eSDavid du Colombier 			/*
1957f92e8f2eSDavid du Colombier 			 * Set:
1958f92e8f2eSDavid du Colombier 			 *	0x41	prefetch, postwrite;
1959f92e8f2eSDavid du Colombier 			 *	0x43	FIFO configuration 1/2 and 1/2;
1960f92e8f2eSDavid du Colombier 			 *	0x44	status register read retry;
1961f92e8f2eSDavid du Colombier 			 *	0x46	DMA read and end of sector flush.
1962f92e8f2eSDavid du Colombier 			 */
1963f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x41);
1964f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x41, r|0xF0);
1965f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x43);
1966f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
1967f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x44);
1968f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x44, r|0x08);
1969f92e8f2eSDavid du Colombier 			r = pcicfgr8(p, 0x46);
1970f92e8f2eSDavid du Colombier 			pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
19714c5c8adbSDavid du Colombier 			/*FALLTHROUGH*/
1972dfda52d8SDavid du Colombier 		case (0x7401<<16)|0x1022:	/* AMD 755 Cobra */
1973dfda52d8SDavid du Colombier 		case (0x7409<<16)|0x1022:	/* AMD 756 Viper */
1974dfda52d8SDavid du Colombier 		case (0x7410<<16)|0x1022:	/* AMD 766 Viper Plus */
19756f314b92SDavid du Colombier 		case (0x7469<<16)|0x1022:	/* AMD 3111 */
19764c5c8adbSDavid du Colombier 			/*
19774c5c8adbSDavid du Colombier 			 * This can probably be lumped in with the 768 above.
19784c5c8adbSDavid du Colombier 			 */
19794c5c8adbSDavid du Colombier 			/*FALLTHROUGH*/
19806a5dc222SDavid du Colombier 		case (0x209A<<16)|0x1022:	/* AMD CS5536 */
1981ab3dc52fSDavid du Colombier 		case (0x01BC<<16)|0x10DE:	/* nVidia nForce1 */
1982ab3dc52fSDavid du Colombier 		case (0x0065<<16)|0x10DE:	/* nVidia nForce2 */
1983ab3dc52fSDavid du Colombier 		case (0x0085<<16)|0x10DE:	/* nVidia nForce2 MCP */
1984b8a11165SDavid du Colombier 		case (0x00E3<<16)|0x10DE:	/* nVidia nForce2 250 SATA */
19854c5c8adbSDavid du Colombier 		case (0x00D5<<16)|0x10DE:	/* nVidia nForce3 */
1986ab3dc52fSDavid du Colombier 		case (0x00E5<<16)|0x10DE:	/* nVidia nForce3 Pro */
1987b8a11165SDavid du Colombier 		case (0x00EE<<16)|0x10DE:	/* nVidia nForce3 250 SATA */
1988ab3dc52fSDavid du Colombier 		case (0x0035<<16)|0x10DE:	/* nVidia nForce3 MCP */
1989ab3dc52fSDavid du Colombier 		case (0x0053<<16)|0x10DE:	/* nVidia nForce4 */
19901066d6deSDavid du Colombier 		case (0x0054<<16)|0x10DE:	/* nVidia nForce4 SATA */
19911066d6deSDavid du Colombier 		case (0x0055<<16)|0x10DE:	/* nVidia nForce4 SATA */
19920aa8dc3cSDavid du Colombier 		case (0x0266<<16)|0x10DE:	/* nVidia nForce4 430 SATA */
1993edc15dd6SDavid du Colombier 		case (0x0267<<16)|0x10DE:	/* nVidia nForce 55 MCP SATA */
1994edc15dd6SDavid du Colombier 		case (0x03EC<<16)|0x10DE:	/* nVidia nForce 61 MCP SATA */
1995edc15dd6SDavid du Colombier 		case (0x0448<<16)|0x10DE:	/* nVidia nForce 65 MCP SATA */
1996edc15dd6SDavid du Colombier 		case (0x0560<<16)|0x10DE:	/* nVidia nForce 69 MCP SATA */
19974c5c8adbSDavid du Colombier 			/*
19984c5c8adbSDavid du Colombier 			 * Ditto, although it may have a different base
19994c5c8adbSDavid du Colombier 			 * address for the registers (0x50?).
20004c5c8adbSDavid du Colombier 			 */
2001ff1040beSDavid du Colombier 			/*FALLTHROUGH*/
2002cb8c047aSDavid du Colombier 		case (0x4376<<16)|0x1002:	/* ATI SB400 PATA */
2003cb8c047aSDavid du Colombier 		case (0x4379<<16)|0x1002:	/* ATI SB400 SATA */
2004cb8c047aSDavid du Colombier 		case (0x437a<<16)|0x1002:	/* ATI SB400 SATA */
2005f92e8f2eSDavid du Colombier 			break;
20062839d78eSDavid du Colombier 		case (0x0211<<16)|0x1166:	/* ServerWorks IB6566 */
20072839d78eSDavid du Colombier 			{
20082839d78eSDavid du Colombier 				Pcidev *sb;
20092839d78eSDavid du Colombier 
20102839d78eSDavid du Colombier 				sb = pcimatch(nil, 0x1166, 0x0200);
20112839d78eSDavid du Colombier 				if(sb == nil)
20122839d78eSDavid du Colombier 					break;
20132839d78eSDavid du Colombier 				r = pcicfgr32(sb, 0x64);
20142839d78eSDavid du Colombier 				r &= ~0x2000;
20152839d78eSDavid du Colombier 				pcicfgw32(sb, 0x64, r);
20162839d78eSDavid du Colombier 			}
20174de34a7eSDavid du Colombier 			span = 32*1024;
20182839d78eSDavid du Colombier 			break;
201991157df7SDavid du Colombier 		case (0x0502<<17)|0x100B:	/* NS SC1100/SCx200 */
20201066d6deSDavid du Colombier 		case (0x5229<<16)|0x10B9:	/* ALi M1543 */
20211066d6deSDavid du Colombier 		case (0x5288<<16)|0x10B9:	/* ALi M5288 SATA */
202259f4c0c1SDavid du Colombier 		case (0x5513<<16)|0x1039:	/* SiS 962 */
20237dd7cddfSDavid du Colombier 		case (0x0646<<16)|0x1095:	/* CMD 646 */
20247dd7cddfSDavid du Colombier 		case (0x0571<<16)|0x1106:	/* VIA 82C686 */
2025cb8c047aSDavid du Colombier 		case (0x2363<<16)|0x197b:	/* JMicron SATA */
20267dd7cddfSDavid du Colombier 		case (0x1230<<16)|0x8086:	/* 82371FB (PIIX) */
20277dd7cddfSDavid du Colombier 		case (0x7010<<16)|0x8086:	/* 82371SB (PIIX3) */
20287dd7cddfSDavid du Colombier 		case (0x7111<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */
2029ef9eff0bSDavid du Colombier 		case (0x2411<<16)|0x8086:	/* 82801AA (ICH) */
2030ef9eff0bSDavid du Colombier 		case (0x2421<<16)|0x8086:	/* 82801AB (ICH0) */
2031ef9eff0bSDavid du Colombier 		case (0x244A<<16)|0x8086:	/* 82801BA (ICH2, Mobile) */
2032ef9eff0bSDavid du Colombier 		case (0x244B<<16)|0x8086:	/* 82801BA (ICH2, High-End) */
2033ef9eff0bSDavid du Colombier 		case (0x248A<<16)|0x8086:	/* 82801CA (ICH3, Mobile) */
2034ef9eff0bSDavid du Colombier 		case (0x248B<<16)|0x8086:	/* 82801CA (ICH3, High-End) */
2035e288d156SDavid du Colombier 		case (0x24CA<<16)|0x8086:	/* 82801DBM (ICH4, Mobile) */
2036ef9eff0bSDavid du Colombier 		case (0x24CB<<16)|0x8086:	/* 82801DB (ICH4, High-End) */
2037f92e8f2eSDavid du Colombier 		case (0x24DB<<16)|0x8086:	/* 82801EB (ICH5) */
20381c9e5a6cSDavid du Colombier 		case (0x25A3<<16)|0x8086:	/* 6300ESB (E7210) */
2039*1a8a6b0dSDavid du Colombier 		case (0x2653<<16)|0x8086:	/* 82801FBM (ICH6M) */
20407c881178SDavid du Colombier 		case (0x266F<<16)|0x8086:	/* 82801FB (ICH6) */
2041055c7668SDavid du Colombier 		case (0x27DF<<16)|0x8086:	/* 82801G SATA (ICH7) */
2042055c7668SDavid du Colombier 		case (0x27C0<<16)|0x8086:	/* 82801GB SATA AHCI (ICH7) */
20437ddf765aSDavid du Colombier //		case (0x27C4<<16)|0x8086:	/* 82801GBM SATA (ICH7) */
2044e06f534bSDavid du Colombier 		case (0x27C5<<16)|0x8086:	/* 82801GBM SATA AHCI (ICH7) */
2045b897e69aSDavid du Colombier 		case (0x2920<<16)|0x8086:	/* 82801(IB)/IR/IH/IO SATA IDE (ICH9) */
204691157df7SDavid du Colombier 		case (0x3a20<<16)|0x8086:	/* 82801JI (ICH10) */
204791157df7SDavid du Colombier 		case (0x3a26<<16)|0x8086:	/* 82801JI (ICH10) */
20487dd7cddfSDavid du Colombier 			break;
20497dd7cddfSDavid du Colombier 		}
20507dd7cddfSDavid du Colombier 
20517dd7cddfSDavid du Colombier 		for(channel = 0; channel < 2; channel++){
20527dd7cddfSDavid du Colombier 			if(pi & (1<<(2*channel))){
20537dd7cddfSDavid du Colombier 				sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
20547dd7cddfSDavid du Colombier 						p->mem[1+2*channel].bar & ~0x01,
20557dd7cddfSDavid du Colombier 						p->intl);
20567dd7cddfSDavid du Colombier 				if(sdev == nil)
20577dd7cddfSDavid du Colombier 					continue;
20587dd7cddfSDavid du Colombier 
20597dd7cddfSDavid du Colombier 				ctlr = sdev->ctlr;
20609a747e4fSDavid du Colombier 				if(ispc87415) {
20617dd7cddfSDavid du Colombier 					ctlr->ienable = pc87415ienable;
20629a747e4fSDavid du Colombier 					print("pc87415disable: not yet implemented\n");
20639a747e4fSDavid du Colombier 				}
20647dd7cddfSDavid du Colombier 
20657dd7cddfSDavid du Colombier 				if(head != nil)
20667dd7cddfSDavid du Colombier 					tail->next = sdev;
20677dd7cddfSDavid du Colombier 				else
20687dd7cddfSDavid du Colombier 					head = sdev;
20697dd7cddfSDavid du Colombier 				tail = sdev;
20707dd7cddfSDavid du Colombier 				ctlr->tbdf = p->tbdf;
20717dd7cddfSDavid du Colombier 			}
20727dd7cddfSDavid du Colombier 			else if((sdev = legacy[channel]) == nil)
20737dd7cddfSDavid du Colombier 				continue;
20747dd7cddfSDavid du Colombier 			else
20757dd7cddfSDavid du Colombier 				ctlr = sdev->ctlr;
20767dd7cddfSDavid du Colombier 
20777dd7cddfSDavid du Colombier 			ctlr->pcidev = p;
20784de34a7eSDavid du Colombier 			ctlr->maxio = maxio;
20794de34a7eSDavid du Colombier 			ctlr->span = span;
20807dd7cddfSDavid du Colombier 			if(!(pi & 0x80))
20817dd7cddfSDavid du Colombier 				continue;
20827dd7cddfSDavid du Colombier 			ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8;
20837dd7cddfSDavid du Colombier 		}
20847dd7cddfSDavid du Colombier 	}
20857dd7cddfSDavid du Colombier 
20869a747e4fSDavid du Colombier if(0){
20879a747e4fSDavid du Colombier 	int port;
20889a747e4fSDavid du Colombier 	ISAConf isa;
20899a747e4fSDavid du Colombier 
20909a747e4fSDavid du Colombier 	/*
20919a747e4fSDavid du Colombier 	 * Hack for PCMCIA drives.
20929a747e4fSDavid du Colombier 	 * This will be tidied once we figure out how the whole
20939a747e4fSDavid du Colombier 	 * removeable device thing is going to work.
20949a747e4fSDavid du Colombier 	 */
20959a747e4fSDavid du Colombier 	memset(&isa, 0, sizeof(isa));
20969a747e4fSDavid du Colombier 	isa.port = 0x180;		/* change this for your machine */
20979a747e4fSDavid du Colombier 	isa.irq = 11;			/* change this for your machine */
20989a747e4fSDavid du Colombier 
20999a747e4fSDavid du Colombier 	port = isa.port+0x0C;
21009a747e4fSDavid du Colombier 	channel = pcmspecial("MK2001MPL", &isa);
21019a747e4fSDavid du Colombier 	if(channel == -1)
21029a747e4fSDavid du Colombier 		channel = pcmspecial("SunDisk", &isa);
21039a747e4fSDavid du Colombier 	if(channel == -1){
21049a747e4fSDavid du Colombier 		isa.irq = 10;
21059a747e4fSDavid du Colombier 		channel = pcmspecial("CF", &isa);
21069a747e4fSDavid du Colombier 	}
21079a747e4fSDavid du Colombier 	if(channel == -1){
21089a747e4fSDavid du Colombier 		isa.irq = 10;
21099a747e4fSDavid du Colombier 		channel = pcmspecial("OLYMPUS", &isa);
21109a747e4fSDavid du Colombier 	}
21119a747e4fSDavid du Colombier 	if(channel == -1){
21129a747e4fSDavid du Colombier 		port = isa.port+0x204;
21139a747e4fSDavid du Colombier 		channel = pcmspecial("ATA/ATAPI", &isa);
21149a747e4fSDavid du Colombier 	}
21159a747e4fSDavid du Colombier 	if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){
21169a747e4fSDavid du Colombier 		if(head != nil)
21179a747e4fSDavid du Colombier 			tail->next = sdev;
21189a747e4fSDavid du Colombier 		else
21199a747e4fSDavid du Colombier 			head = sdev;
21209a747e4fSDavid du Colombier 	}
21219a747e4fSDavid du Colombier }
21227dd7cddfSDavid du Colombier 	return head;
21237dd7cddfSDavid du Colombier }
21247dd7cddfSDavid du Colombier 
21257dd7cddfSDavid du Colombier static SDev*
21267dd7cddfSDavid du Colombier atalegacy(int port, int irq)
21277dd7cddfSDavid du Colombier {
21287dd7cddfSDavid du Colombier 	return ataprobe(port, port+0x204, irq);
21297dd7cddfSDavid du Colombier }
21307dd7cddfSDavid du Colombier 
21317dd7cddfSDavid du Colombier static int
21327dd7cddfSDavid du Colombier ataenable(SDev* sdev)
21337dd7cddfSDavid du Colombier {
21347dd7cddfSDavid du Colombier 	Ctlr *ctlr;
21359a747e4fSDavid du Colombier 	char name[32];
21367dd7cddfSDavid du Colombier 
21377dd7cddfSDavid du Colombier 	ctlr = sdev->ctlr;
21387dd7cddfSDavid du Colombier 
21397dd7cddfSDavid du Colombier 	if(ctlr->bmiba){
21409a747e4fSDavid du Colombier #define ALIGN	(4 * 1024)
21417dd7cddfSDavid du Colombier 		if(ctlr->pcidev != nil)
21427dd7cddfSDavid du Colombier 			pcisetbme(ctlr->pcidev);
21432839d78eSDavid du Colombier 		ctlr->prdt = mallocalign(Nprd*sizeof(Prd), 4, 0, 4*1024);
21447dd7cddfSDavid du Colombier 	}
21459a747e4fSDavid du Colombier 	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
21467dd7cddfSDavid du Colombier 	intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
21477dd7cddfSDavid du Colombier 	outb(ctlr->ctlport+Dc, 0);
21487dd7cddfSDavid du Colombier 	if(ctlr->ienable)
21497dd7cddfSDavid du Colombier 		ctlr->ienable(ctlr);
21507dd7cddfSDavid du Colombier 
21517dd7cddfSDavid du Colombier 	return 1;
21527dd7cddfSDavid du Colombier }
21537dd7cddfSDavid du Colombier 
21547dd7cddfSDavid du Colombier static int
21559a747e4fSDavid du Colombier atadisable(SDev *sdev)
21569a747e4fSDavid du Colombier {
21579a747e4fSDavid du Colombier 	Ctlr *ctlr;
21589a747e4fSDavid du Colombier 	char name[32];
21599a747e4fSDavid du Colombier 
21609a747e4fSDavid du Colombier 	ctlr = sdev->ctlr;
21619a747e4fSDavid du Colombier 	outb(ctlr->ctlport+Dc, Nien);		/* disable interrupts */
21629a747e4fSDavid du Colombier 	if (ctlr->idisable)
21639a747e4fSDavid du Colombier 		ctlr->idisable(ctlr);
21649a747e4fSDavid du Colombier 	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
21659a747e4fSDavid du Colombier 	intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
21669a747e4fSDavid du Colombier 	if (ctlr->bmiba) {
21679a747e4fSDavid du Colombier 		if (ctlr->pcidev)
21689a747e4fSDavid du Colombier 			pciclrbme(ctlr->pcidev);
21692839d78eSDavid du Colombier 		free(ctlr->prdt);
21709a747e4fSDavid du Colombier 	}
21719a747e4fSDavid du Colombier 	return 0;
21729a747e4fSDavid du Colombier }
21739a747e4fSDavid du Colombier 
21749a747e4fSDavid du Colombier static int
21757dd7cddfSDavid du Colombier atarctl(SDunit* unit, char* p, int l)
21767dd7cddfSDavid du Colombier {
21777dd7cddfSDavid du Colombier 	int n;
21787dd7cddfSDavid du Colombier 	Ctlr *ctlr;
21797dd7cddfSDavid du Colombier 	Drive *drive;
21807dd7cddfSDavid du Colombier 
21817dd7cddfSDavid du Colombier 	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
21827dd7cddfSDavid du Colombier 		return 0;
21837dd7cddfSDavid du Colombier 	drive = ctlr->drive[unit->subno];
21847dd7cddfSDavid du Colombier 
21857dd7cddfSDavid du Colombier 	qlock(drive);
21867dd7cddfSDavid du Colombier 	n = snprint(p, l, "config %4.4uX capabilities %4.4uX",
21877dd7cddfSDavid du Colombier 		drive->info[Iconfig], drive->info[Icapabilities]);
21887dd7cddfSDavid du Colombier 	if(drive->dma)
21897dd7cddfSDavid du Colombier 		n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX",
21907dd7cddfSDavid du Colombier 			drive->dma, drive->dmactl);
21917dd7cddfSDavid du Colombier 	if(drive->rwm)
21927dd7cddfSDavid du Colombier 		n += snprint(p+n, l-n, " rwm %ud rwmctl %ud",
21937dd7cddfSDavid du Colombier 			drive->rwm, drive->rwmctl);
21946a081dcdSDavid du Colombier 	if(drive->flags&Lba48)
21956a081dcdSDavid du Colombier 		n += snprint(p+n, l-n, " lba48always %s",
21966a081dcdSDavid du Colombier 			(drive->flags&Lba48always) ? "on" : "off");
21977dd7cddfSDavid du Colombier 	n += snprint(p+n, l-n, "\n");
21986a081dcdSDavid du Colombier 	if(drive->sectors){
21996a081dcdSDavid du Colombier 		n += snprint(p+n, l-n, "geometry %lld %d",
22006a081dcdSDavid du Colombier 			drive->sectors, drive->secsize);
22017dd7cddfSDavid du Colombier 		if(drive->pkt == 0)
22027dd7cddfSDavid du Colombier 			n += snprint(p+n, l-n, " %d %d %d",
22037dd7cddfSDavid du Colombier 				drive->c, drive->h, drive->s);
22047dd7cddfSDavid du Colombier 		n += snprint(p+n, l-n, "\n");
22057dd7cddfSDavid du Colombier 	}
22067dd7cddfSDavid du Colombier 	qunlock(drive);
22077dd7cddfSDavid du Colombier 
22087dd7cddfSDavid du Colombier 	return n;
22097dd7cddfSDavid du Colombier }
22107dd7cddfSDavid du Colombier 
22117dd7cddfSDavid du Colombier static int
22127dd7cddfSDavid du Colombier atawctl(SDunit* unit, Cmdbuf* cb)
22137dd7cddfSDavid du Colombier {
221480ee5cbfSDavid du Colombier 	int period;
22157dd7cddfSDavid du Colombier 	Ctlr *ctlr;
22167dd7cddfSDavid du Colombier 	Drive *drive;
22177dd7cddfSDavid du Colombier 
22187dd7cddfSDavid du Colombier 	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
22197dd7cddfSDavid du Colombier 		return 0;
22207dd7cddfSDavid du Colombier 	drive = ctlr->drive[unit->subno];
22217dd7cddfSDavid du Colombier 
22227dd7cddfSDavid du Colombier 	qlock(drive);
22237dd7cddfSDavid du Colombier 	if(waserror()){
22247dd7cddfSDavid du Colombier 		qunlock(drive);
22257dd7cddfSDavid du Colombier 		nexterror();
22267dd7cddfSDavid du Colombier 	}
22277dd7cddfSDavid du Colombier 
22287dd7cddfSDavid du Colombier 	/*
22297dd7cddfSDavid du Colombier 	 * Dma and rwm control is passive at the moment,
22307dd7cddfSDavid du Colombier 	 * i.e. it is assumed that the hardware is set up
22317dd7cddfSDavid du Colombier 	 * correctly already either by the BIOS or when
22327dd7cddfSDavid du Colombier 	 * the drive was initially identified.
22337dd7cddfSDavid du Colombier 	 */
22347dd7cddfSDavid du Colombier 	if(strcmp(cb->f[0], "dma") == 0){
22357dd7cddfSDavid du Colombier 		if(cb->nf != 2 || drive->dma == 0)
22367dd7cddfSDavid du Colombier 			error(Ebadctl);
22377dd7cddfSDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
22387dd7cddfSDavid du Colombier 			drive->dmactl = drive->dma;
22397dd7cddfSDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
22407dd7cddfSDavid du Colombier 			drive->dmactl = 0;
22417dd7cddfSDavid du Colombier 		else
22427dd7cddfSDavid du Colombier 			error(Ebadctl);
22437dd7cddfSDavid du Colombier 	}
22447dd7cddfSDavid du Colombier 	else if(strcmp(cb->f[0], "rwm") == 0){
22457dd7cddfSDavid du Colombier 		if(cb->nf != 2 || drive->rwm == 0)
22467dd7cddfSDavid du Colombier 			error(Ebadctl);
22477dd7cddfSDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
22487dd7cddfSDavid du Colombier 			drive->rwmctl = drive->rwm;
22497dd7cddfSDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
22507dd7cddfSDavid du Colombier 			drive->rwmctl = 0;
22517dd7cddfSDavid du Colombier 		else
22527dd7cddfSDavid du Colombier 			error(Ebadctl);
22537dd7cddfSDavid du Colombier 	}
225480ee5cbfSDavid du Colombier 	else if(strcmp(cb->f[0], "standby") == 0){
225580ee5cbfSDavid du Colombier 		switch(cb->nf){
225680ee5cbfSDavid du Colombier 		default:
225780ee5cbfSDavid du Colombier 			error(Ebadctl);
225880ee5cbfSDavid du Colombier 		case 2:
225980ee5cbfSDavid du Colombier 			period = strtol(cb->f[1], 0, 0);
226080ee5cbfSDavid du Colombier 			if(period && (period < 30 || period > 240*5))
226180ee5cbfSDavid du Colombier 				error(Ebadctl);
226280ee5cbfSDavid du Colombier 			period /= 5;
226380ee5cbfSDavid du Colombier 			break;
226480ee5cbfSDavid du Colombier 		}
226580ee5cbfSDavid du Colombier 		if(atastandby(drive, period) != SDok)
226680ee5cbfSDavid du Colombier 			error(Ebadctl);
226780ee5cbfSDavid du Colombier 	}
22686a081dcdSDavid du Colombier 	else if(strcmp(cb->f[0], "lba48always") == 0){
22696a081dcdSDavid du Colombier 		if(cb->nf != 2 || !(drive->flags&Lba48))
22706a081dcdSDavid du Colombier 			error(Ebadctl);
22716a081dcdSDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
22726a081dcdSDavid du Colombier 			drive->flags |= Lba48always;
22736a081dcdSDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
22746a081dcdSDavid du Colombier 			drive->flags &= ~Lba48always;
22756a081dcdSDavid du Colombier 		else
22766a081dcdSDavid du Colombier 			error(Ebadctl);
22776a081dcdSDavid du Colombier 	}
22787dd7cddfSDavid du Colombier 	else
22797dd7cddfSDavid du Colombier 		error(Ebadctl);
22807dd7cddfSDavid du Colombier 	qunlock(drive);
22817dd7cddfSDavid du Colombier 	poperror();
22827dd7cddfSDavid du Colombier 
22837dd7cddfSDavid du Colombier 	return 0;
22847dd7cddfSDavid du Colombier }
22857dd7cddfSDavid du Colombier 
22867dd7cddfSDavid du Colombier SDifc sdataifc = {
22877dd7cddfSDavid du Colombier 	"ata",				/* name */
22887dd7cddfSDavid du Colombier 
22897dd7cddfSDavid du Colombier 	atapnp,				/* pnp */
22907dd7cddfSDavid du Colombier 	atalegacy,			/* legacy */
22917dd7cddfSDavid du Colombier 	ataenable,			/* enable */
22929a747e4fSDavid du Colombier 	atadisable,			/* disable */
22937dd7cddfSDavid du Colombier 
22947dd7cddfSDavid du Colombier 	scsiverify,			/* verify */
22957dd7cddfSDavid du Colombier 	scsionline,			/* online */
22967dd7cddfSDavid du Colombier 	atario,				/* rio */
22977dd7cddfSDavid du Colombier 	atarctl,			/* rctl */
22987dd7cddfSDavid du Colombier 	atawctl,			/* wctl */
22997dd7cddfSDavid du Colombier 
23007dd7cddfSDavid du Colombier 	scsibio,			/* bio */
23019a747e4fSDavid du Colombier 	ataprobew,			/* probe */
23029a747e4fSDavid du Colombier 	ataclear,			/* clear */
2303d649fdd7SDavid du Colombier 	atastat,			/* rtopctl */
2304d649fdd7SDavid du Colombier 	nil,				/* wtopctl */
23057dd7cddfSDavid du Colombier };
2306