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