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