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 }; 33859f4c0c1SDavid du Colombier 3397dd7cddfSDavid du Colombier static void 3407dd7cddfSDavid du Colombier pc87415ienable(Ctlr* ctlr) 3417dd7cddfSDavid du Colombier { 3427dd7cddfSDavid du Colombier Pcidev *p; 3437dd7cddfSDavid du Colombier int x; 3447dd7cddfSDavid du Colombier 3457dd7cddfSDavid du Colombier p = ctlr->pcidev; 3467dd7cddfSDavid du Colombier if(p == nil) 3477dd7cddfSDavid du Colombier return; 3487dd7cddfSDavid du Colombier 3497dd7cddfSDavid du Colombier x = pcicfgr32(p, 0x40); 3507dd7cddfSDavid du Colombier if(ctlr->cmdport == p->mem[0].bar) 3517dd7cddfSDavid du Colombier x &= ~0x00000100; 3527dd7cddfSDavid du Colombier else 3537dd7cddfSDavid du Colombier x &= ~0x00000200; 3547dd7cddfSDavid du Colombier pcicfgw32(p, 0x40, x); 3557dd7cddfSDavid du Colombier } 3567dd7cddfSDavid du Colombier 3577dd7cddfSDavid du Colombier static void 3586a081dcdSDavid du Colombier atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count) 3597dd7cddfSDavid du Colombier { 3607dd7cddfSDavid du Colombier Prd *prd; 3617dd7cddfSDavid du Colombier Pcidev *p; 3627dd7cddfSDavid du Colombier Ctlr *ctlr; 3637dd7cddfSDavid du Colombier int i, bmiba; 3647dd7cddfSDavid du Colombier 3657dd7cddfSDavid du Colombier if(!(DEBUG & DbgSTATE)){ 3667dd7cddfSDavid du Colombier USED(drive, cmd, lba, count); 3677dd7cddfSDavid du Colombier return; 3687dd7cddfSDavid du Colombier } 3697dd7cddfSDavid du Colombier 3707dd7cddfSDavid du Colombier ctlr = drive->ctlr; 3717dd7cddfSDavid du Colombier print("command %2.2uX\n", ctlr->command); 3727dd7cddfSDavid du Colombier print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n", 3737dd7cddfSDavid du Colombier drive->data, drive->limit, drive->dlen, 3747dd7cddfSDavid du Colombier drive->status, drive->error); 3757dd7cddfSDavid du Colombier if(cmd != nil){ 3766a081dcdSDavid du Colombier print("lba %d -> %lld, count %d -> %d (%d)\n", 3777dd7cddfSDavid du Colombier (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba, 3787dd7cddfSDavid du Colombier (cmd[7]<<8)|cmd[8], count, drive->count); 3797dd7cddfSDavid du Colombier } 3807dd7cddfSDavid du Colombier if(!(inb(ctlr->ctlport+As) & Bsy)){ 3817dd7cddfSDavid du Colombier for(i = 1; i < 7; i++) 3827dd7cddfSDavid du Colombier print(" 0x%2.2uX", inb(ctlr->cmdport+i)); 3837dd7cddfSDavid du Colombier print(" 0x%2.2uX\n", inb(ctlr->ctlport+As)); 3847dd7cddfSDavid du Colombier } 3857dd7cddfSDavid du Colombier if(drive->command == Cwd || drive->command == Crd){ 3867dd7cddfSDavid du Colombier bmiba = ctlr->bmiba; 3877dd7cddfSDavid du Colombier prd = ctlr->prdt; 3887dd7cddfSDavid du Colombier print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n", 3897dd7cddfSDavid du Colombier inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd); 3907dd7cddfSDavid du Colombier for(;;){ 3917dd7cddfSDavid du Colombier print("pa 0x%8.8luX count %8.8uX\n", 3927dd7cddfSDavid du Colombier prd->pa, prd->count); 3937dd7cddfSDavid du Colombier if(prd->count & PrdEOT) 3947dd7cddfSDavid du Colombier break; 3957dd7cddfSDavid du Colombier prd++; 3967dd7cddfSDavid du Colombier } 3977dd7cddfSDavid du Colombier } 3987dd7cddfSDavid du Colombier if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){ 3997dd7cddfSDavid du Colombier p = ctlr->pcidev; 4007dd7cddfSDavid du Colombier print("0x40: %4.4uX 0x42: %4.4uX", 4017dd7cddfSDavid du Colombier pcicfgr16(p, 0x40), pcicfgr16(p, 0x42)); 4027dd7cddfSDavid du Colombier print("0x48: %2.2uX\n", pcicfgr8(p, 0x48)); 4037dd7cddfSDavid du Colombier print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A)); 4047dd7cddfSDavid du Colombier } 4057dd7cddfSDavid du Colombier } 4067dd7cddfSDavid du Colombier 4077dd7cddfSDavid du Colombier static int 4087dd7cddfSDavid du Colombier atadebug(int cmdport, int ctlport, char* fmt, ...) 4097dd7cddfSDavid du Colombier { 4107dd7cddfSDavid du Colombier int i, n; 4117dd7cddfSDavid du Colombier va_list arg; 4127dd7cddfSDavid du Colombier char buf[PRINTSIZE]; 4137dd7cddfSDavid du Colombier 4147dd7cddfSDavid du Colombier if(!(DEBUG & DbgPROBE)){ 4157dd7cddfSDavid du Colombier USED(cmdport, ctlport, fmt); 4167dd7cddfSDavid du Colombier return 0; 4177dd7cddfSDavid du Colombier } 4187dd7cddfSDavid du Colombier 4197dd7cddfSDavid du Colombier va_start(arg, fmt); 4209a747e4fSDavid du Colombier n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; 4217dd7cddfSDavid du Colombier va_end(arg); 4227dd7cddfSDavid du Colombier 4237dd7cddfSDavid du Colombier if(cmdport){ 4247dd7cddfSDavid du Colombier if(buf[n-1] == '\n') 4257dd7cddfSDavid du Colombier n--; 4267dd7cddfSDavid du Colombier n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:", 4277dd7cddfSDavid du Colombier cmdport); 4287dd7cddfSDavid du Colombier for(i = Features; i < Command; i++) 4297dd7cddfSDavid du Colombier n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", 4307dd7cddfSDavid du Colombier inb(cmdport+i)); 4317dd7cddfSDavid du Colombier if(ctlport) 4327dd7cddfSDavid du Colombier n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", 4337dd7cddfSDavid du Colombier inb(ctlport+As)); 4347dd7cddfSDavid du Colombier n += snprint(buf+n, PRINTSIZE-n, "\n"); 4357dd7cddfSDavid du Colombier } 4367dd7cddfSDavid du Colombier putstrn(buf, n); 4377dd7cddfSDavid du Colombier 4387dd7cddfSDavid du Colombier return n; 4397dd7cddfSDavid du Colombier } 4407dd7cddfSDavid du Colombier 4417dd7cddfSDavid du Colombier static int 4427dd7cddfSDavid du Colombier ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro) 4437dd7cddfSDavid du Colombier { 4447dd7cddfSDavid du Colombier int as; 4457dd7cddfSDavid du Colombier 4467dd7cddfSDavid du Colombier atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX", 4477dd7cddfSDavid du Colombier dev, reset, ready); 4487dd7cddfSDavid du Colombier 4497dd7cddfSDavid du Colombier for(;;){ 4507dd7cddfSDavid du Colombier /* 4517dd7cddfSDavid du Colombier * Wait for the controller to become not busy and 4527dd7cddfSDavid du Colombier * possibly for a status bit to become true (usually 4537dd7cddfSDavid du Colombier * Drdy). Must change to the appropriate device 4547dd7cddfSDavid du Colombier * register set if necessary before testing for ready. 4557dd7cddfSDavid du Colombier * Always run through the loop at least once so it 4567dd7cddfSDavid du Colombier * can be used as a test for !Bsy. 4577dd7cddfSDavid du Colombier */ 4587dd7cddfSDavid du Colombier as = inb(ctlport+As); 4599a747e4fSDavid du Colombier if(as & reset){ 4609a747e4fSDavid du Colombier /* nothing to do */ 4619a747e4fSDavid du Colombier } 4627dd7cddfSDavid du Colombier else if(dev){ 4637dd7cddfSDavid du Colombier outb(cmdport+Dh, dev); 4647dd7cddfSDavid du Colombier dev = 0; 4657dd7cddfSDavid du Colombier } 4667dd7cddfSDavid du Colombier else if(ready == 0 || (as & ready)){ 4677dd7cddfSDavid du Colombier atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); 4687dd7cddfSDavid du Colombier return as; 4697dd7cddfSDavid du Colombier } 4707dd7cddfSDavid du Colombier 4717dd7cddfSDavid du Colombier if(micro-- <= 0){ 4727dd7cddfSDavid du Colombier atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); 4737dd7cddfSDavid du Colombier break; 4747dd7cddfSDavid du Colombier } 4757dd7cddfSDavid du Colombier microdelay(1); 4767dd7cddfSDavid du Colombier } 4777dd7cddfSDavid du Colombier atadebug(cmdport, ctlport, "ataready: timeout"); 4787dd7cddfSDavid du Colombier 4797dd7cddfSDavid du Colombier return -1; 4807dd7cddfSDavid du Colombier } 4817dd7cddfSDavid du Colombier 4826a081dcdSDavid du Colombier /* 4837dd7cddfSDavid du Colombier static int 48480ee5cbfSDavid du Colombier atacsf(Drive* drive, vlong csf, int supported) 4857dd7cddfSDavid du Colombier { 48680ee5cbfSDavid du Colombier ushort *info; 4877dd7cddfSDavid du Colombier int cmdset, i, x; 4887dd7cddfSDavid du Colombier 48980ee5cbfSDavid du Colombier if(supported) 49080ee5cbfSDavid du Colombier info = &drive->info[Icsfs]; 49180ee5cbfSDavid du Colombier else 49280ee5cbfSDavid du Colombier info = &drive->info[Icsfe]; 49380ee5cbfSDavid du Colombier 4947dd7cddfSDavid du Colombier for(i = 0; i < 3; i++){ 4957dd7cddfSDavid du Colombier x = (csf>>(16*i)) & 0xFFFF; 4967dd7cddfSDavid du Colombier if(x == 0) 4977dd7cddfSDavid du Colombier continue; 49880ee5cbfSDavid du Colombier cmdset = info[i]; 4997dd7cddfSDavid du Colombier if(cmdset == 0 || cmdset == 0xFFFF) 5007dd7cddfSDavid du Colombier return 0; 5017dd7cddfSDavid du Colombier return cmdset & x; 5027dd7cddfSDavid du Colombier } 5037dd7cddfSDavid du Colombier 5047dd7cddfSDavid du Colombier return 0; 5057dd7cddfSDavid du Colombier } 5066a081dcdSDavid du Colombier */ 5077dd7cddfSDavid du Colombier 5087dd7cddfSDavid du Colombier static int 50980ee5cbfSDavid du Colombier atadone(void* arg) 5107dd7cddfSDavid du Colombier { 51180ee5cbfSDavid du Colombier return ((Ctlr*)arg)->done; 5127dd7cddfSDavid du Colombier } 5137dd7cddfSDavid du Colombier 5147dd7cddfSDavid du Colombier static int 5157dd7cddfSDavid du Colombier atarwmmode(Drive* drive, int cmdport, int ctlport, int dev) 5167dd7cddfSDavid du Colombier { 5177dd7cddfSDavid du Colombier int as, maxrwm, rwm; 5187dd7cddfSDavid du Colombier 5197dd7cddfSDavid du Colombier maxrwm = (drive->info[Imaxrwm] & 0xFF); 5207dd7cddfSDavid du Colombier if(maxrwm == 0) 5217dd7cddfSDavid du Colombier return 0; 5227dd7cddfSDavid du Colombier 5237dd7cddfSDavid du Colombier /* 5247dd7cddfSDavid du Colombier * Sometimes drives come up with the current count set 5257dd7cddfSDavid du Colombier * to 0; if so, set a suitable value, otherwise believe 5267dd7cddfSDavid du Colombier * the value in Irwm if the 0x100 bit is set. 5277dd7cddfSDavid du Colombier */ 5287dd7cddfSDavid du Colombier if(drive->info[Irwm] & 0x100) 5297dd7cddfSDavid du Colombier rwm = (drive->info[Irwm] & 0xFF); 5307dd7cddfSDavid du Colombier else 5317dd7cddfSDavid du Colombier rwm = 0; 5327dd7cddfSDavid du Colombier if(rwm == 0) 5337dd7cddfSDavid du Colombier rwm = maxrwm; 5347dd7cddfSDavid du Colombier if(rwm > 16) 5357dd7cddfSDavid du Colombier rwm = 16; 5367dd7cddfSDavid du Colombier if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0) 5377dd7cddfSDavid du Colombier return 0; 5387dd7cddfSDavid du Colombier outb(cmdport+Count, rwm); 5397dd7cddfSDavid du Colombier outb(cmdport+Command, Csm); 5407dd7cddfSDavid du Colombier microdelay(1); 5417dd7cddfSDavid du Colombier as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000); 5427dd7cddfSDavid du Colombier inb(cmdport+Status); 5437dd7cddfSDavid du Colombier if(as < 0 || (as & (Df|Err))) 5447dd7cddfSDavid du Colombier return 0; 5457dd7cddfSDavid du Colombier 5467dd7cddfSDavid du Colombier drive->rwm = rwm; 5477dd7cddfSDavid du Colombier 5487dd7cddfSDavid du Colombier return rwm; 5497dd7cddfSDavid du Colombier } 5507dd7cddfSDavid du Colombier 5517dd7cddfSDavid du Colombier static int 5527dd7cddfSDavid du Colombier atadmamode(Drive* drive) 5537dd7cddfSDavid du Colombier { 5547dd7cddfSDavid du Colombier int dma; 5557dd7cddfSDavid du Colombier 5567dd7cddfSDavid du Colombier /* 5577dd7cddfSDavid du Colombier * Check if any DMA mode enabled. 5587dd7cddfSDavid du Colombier * Assumes the BIOS has picked and enabled the best. 5597dd7cddfSDavid du Colombier * This is completely passive at the moment, no attempt is 5607dd7cddfSDavid du Colombier * made to ensure the hardware is correctly set up. 5617dd7cddfSDavid du Colombier */ 5627dd7cddfSDavid du Colombier dma = drive->info[Imwdma] & 0x0707; 5637dd7cddfSDavid du Colombier drive->dma = (dma>>8) & dma; 5647dd7cddfSDavid du Colombier if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){ 565503d3e0aSDavid du Colombier dma = drive->info[Iudma] & 0x7F7F; 5667dd7cddfSDavid du Colombier drive->dma = (dma>>8) & dma; 5677dd7cddfSDavid du Colombier if(drive->dma) 5687dd7cddfSDavid du Colombier drive->dma |= 'U'<<16; 5697dd7cddfSDavid du Colombier } 5707dd7cddfSDavid du Colombier 5717dd7cddfSDavid du Colombier return dma; 5727dd7cddfSDavid du Colombier } 5737dd7cddfSDavid du Colombier 5747dd7cddfSDavid du Colombier static int 5757dd7cddfSDavid du Colombier ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) 5767dd7cddfSDavid du Colombier { 5777dd7cddfSDavid du Colombier int as, command, drdy; 5787dd7cddfSDavid du Colombier 5797dd7cddfSDavid du Colombier if(pkt){ 5807dd7cddfSDavid du Colombier command = Cidpkt; 5817dd7cddfSDavid du Colombier drdy = 0; 5827dd7cddfSDavid du Colombier } 5837dd7cddfSDavid du Colombier else{ 5847dd7cddfSDavid du Colombier command = Cid; 5857dd7cddfSDavid du Colombier drdy = Drdy; 5867dd7cddfSDavid du Colombier } 5877dd7cddfSDavid du Colombier as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000); 5887dd7cddfSDavid du Colombier if(as < 0) 5897dd7cddfSDavid du Colombier return as; 5907dd7cddfSDavid du Colombier outb(cmdport+Command, command); 5917dd7cddfSDavid du Colombier microdelay(1); 5927dd7cddfSDavid du Colombier 59359cc4ca5SDavid du Colombier as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000); 5947dd7cddfSDavid du Colombier if(as < 0) 5957dd7cddfSDavid du Colombier return -1; 5967dd7cddfSDavid du Colombier if(as & Err) 5977dd7cddfSDavid du Colombier return as; 5987dd7cddfSDavid du Colombier 5997dd7cddfSDavid du Colombier memset(info, 0, 512); 6007dd7cddfSDavid du Colombier inss(cmdport+Data, info, 256); 6017dd7cddfSDavid du Colombier inb(cmdport+Status); 6027dd7cddfSDavid du Colombier 6037dd7cddfSDavid du Colombier if(DEBUG & DbgIDENTIFY){ 6047dd7cddfSDavid du Colombier int i; 6057dd7cddfSDavid du Colombier ushort *sp; 6067dd7cddfSDavid du Colombier 6077dd7cddfSDavid du Colombier sp = (ushort*)info; 6087dd7cddfSDavid du Colombier for(i = 0; i < 256; i++){ 6097dd7cddfSDavid du Colombier if(i && (i%16) == 0) 6107dd7cddfSDavid du Colombier print("\n"); 6117dd7cddfSDavid du Colombier print(" %4.4uX", *sp); 6127dd7cddfSDavid du Colombier sp++; 6137dd7cddfSDavid du Colombier } 6147dd7cddfSDavid du Colombier print("\n"); 6157dd7cddfSDavid du Colombier } 6167dd7cddfSDavid du Colombier 6177dd7cddfSDavid du Colombier return 0; 6187dd7cddfSDavid du Colombier } 6197dd7cddfSDavid du Colombier 6207dd7cddfSDavid du Colombier static Drive* 6217dd7cddfSDavid du Colombier atadrive(int cmdport, int ctlport, int dev) 6227dd7cddfSDavid du Colombier { 6237dd7cddfSDavid du Colombier Drive *drive; 6247dd7cddfSDavid du Colombier int as, i, pkt; 6257dd7cddfSDavid du Colombier uchar buf[512], *p; 6269a747e4fSDavid du Colombier ushort iconfig, *sp; 6277dd7cddfSDavid du Colombier 6287dd7cddfSDavid du Colombier atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev); 6297dd7cddfSDavid du Colombier pkt = 1; 6307dd7cddfSDavid du Colombier retry: 6317dd7cddfSDavid du Colombier as = ataidentify(cmdport, ctlport, dev, pkt, buf); 6327dd7cddfSDavid du Colombier if(as < 0) 6337dd7cddfSDavid du Colombier return nil; 6347dd7cddfSDavid du Colombier if(as & Err){ 6357dd7cddfSDavid du Colombier if(pkt == 0) 6367dd7cddfSDavid du Colombier return nil; 6377dd7cddfSDavid du Colombier pkt = 0; 6387dd7cddfSDavid du Colombier goto retry; 6397dd7cddfSDavid du Colombier } 6407dd7cddfSDavid du Colombier 6417dd7cddfSDavid du Colombier if((drive = malloc(sizeof(Drive))) == nil) 6427dd7cddfSDavid du Colombier return nil; 6437dd7cddfSDavid du Colombier drive->dev = dev; 6447dd7cddfSDavid du Colombier memmove(drive->info, buf, sizeof(drive->info)); 6457dd7cddfSDavid du Colombier drive->sense[0] = 0x70; 6467dd7cddfSDavid du Colombier drive->sense[7] = sizeof(drive->sense)-7; 6477dd7cddfSDavid du Colombier 6487dd7cddfSDavid du Colombier drive->inquiry[2] = 2; 6497dd7cddfSDavid du Colombier drive->inquiry[3] = 2; 6507dd7cddfSDavid du Colombier drive->inquiry[4] = sizeof(drive->inquiry)-4; 6517dd7cddfSDavid du Colombier p = &drive->inquiry[8]; 6527dd7cddfSDavid du Colombier sp = &drive->info[Imodel]; 6537dd7cddfSDavid du Colombier for(i = 0; i < 20; i++){ 6547dd7cddfSDavid du Colombier *p++ = *sp>>8; 6557dd7cddfSDavid du Colombier *p++ = *sp++; 6567dd7cddfSDavid du Colombier } 6577dd7cddfSDavid du Colombier 6587dd7cddfSDavid du Colombier drive->secsize = 512; 6599a747e4fSDavid du Colombier 6609a747e4fSDavid du Colombier /* 6619a747e4fSDavid du Colombier * Beware the CompactFlash Association feature set. 6629a747e4fSDavid du Colombier * Now, why this value in Iconfig just walks all over the bit 6639a747e4fSDavid du Colombier * definitions used in the other parts of the ATA/ATAPI standards 6649a747e4fSDavid du Colombier * is a mystery and a sign of true stupidity on someone's part. 6659a747e4fSDavid du Colombier * Anyway, the standard says if this value is 0x848A then it's 6669a747e4fSDavid du Colombier * CompactFlash and it's NOT a packet device. 6679a747e4fSDavid du Colombier */ 6689a747e4fSDavid du Colombier iconfig = drive->info[Iconfig]; 6699a747e4fSDavid du Colombier if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){ 6709a747e4fSDavid du Colombier if(iconfig & 0x01) 6717dd7cddfSDavid du Colombier drive->pkt = 16; 6727dd7cddfSDavid du Colombier else 6737dd7cddfSDavid du Colombier drive->pkt = 12; 6747dd7cddfSDavid du Colombier } 6757dd7cddfSDavid du Colombier else{ 6767dd7cddfSDavid du Colombier if(drive->info[Ivalid] & 0x0001){ 6777dd7cddfSDavid du Colombier drive->c = drive->info[Iccyl]; 6787dd7cddfSDavid du Colombier drive->h = drive->info[Ichead]; 6797dd7cddfSDavid du Colombier drive->s = drive->info[Icsec]; 68059f4c0c1SDavid du Colombier } 68159f4c0c1SDavid du Colombier else{ 6823ff48bf5SDavid du Colombier drive->c = drive->info[Ilcyl]; 6833ff48bf5SDavid du Colombier drive->h = drive->info[Ilhead]; 6843ff48bf5SDavid du Colombier drive->s = drive->info[Ilsec]; 6853ff48bf5SDavid du Colombier } 6866a081dcdSDavid du Colombier if(drive->info[Icapabilities] & Mlba){ 6876a081dcdSDavid du Colombier if(drive->info[Icsfs+1] & Maddr48){ 6886a081dcdSDavid du Colombier drive->sectors = drive->info[Ilba48] 6895ea8af7bSDavid du Colombier | (drive->info[Ilba48+1]<<16) 6905ea8af7bSDavid du Colombier | ((vlong)drive->info[Ilba48+2]<<32); 6916a081dcdSDavid du Colombier drive->flags |= Lba48; 69259f4c0c1SDavid du Colombier } 69359f4c0c1SDavid du Colombier else{ 6946a081dcdSDavid du Colombier drive->sectors = (drive->info[Ilba+1]<<16) 6956a081dcdSDavid du Colombier |drive->info[Ilba]; 6967dd7cddfSDavid du Colombier } 6976a081dcdSDavid du Colombier drive->dev |= Lba; 69859f4c0c1SDavid du Colombier } 69959f4c0c1SDavid du Colombier else 7007dd7cddfSDavid du Colombier drive->sectors = drive->c*drive->h*drive->s; 7017dd7cddfSDavid du Colombier atarwmmode(drive, cmdport, ctlport, dev); 7027dd7cddfSDavid du Colombier } 7037dd7cddfSDavid du Colombier atadmamode(drive); 7047dd7cddfSDavid du Colombier 7057dd7cddfSDavid du Colombier if(DEBUG & DbgCONFIG){ 70659cc4ca5SDavid du Colombier print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", 707ca2418feSDavid du Colombier dev, cmdport, iconfig, drive->info[Icapabilities]); 70814414594SDavid du Colombier print(" mwdma %4.4uX", drive->info[Imwdma]); 7097dd7cddfSDavid du Colombier if(drive->info[Ivalid] & 0x04) 7107dd7cddfSDavid du Colombier print(" udma %4.4uX", drive->info[Iudma]); 71159f4c0c1SDavid du Colombier print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm); 7126a081dcdSDavid du Colombier if(drive->flags&Lba48) 71359f4c0c1SDavid du Colombier print("\tLLBA sectors %lld", drive->sectors); 71459f4c0c1SDavid du Colombier print("\n"); 7157dd7cddfSDavid du Colombier } 7167dd7cddfSDavid du Colombier 7177dd7cddfSDavid du Colombier return drive; 7187dd7cddfSDavid du Colombier } 7197dd7cddfSDavid du Colombier 7207dd7cddfSDavid du Colombier static void 7217dd7cddfSDavid du Colombier atasrst(int ctlport) 7227dd7cddfSDavid du Colombier { 7237dd7cddfSDavid du Colombier /* 7247dd7cddfSDavid du Colombier * Srst is a big stick and may cause problems if further 7257dd7cddfSDavid du Colombier * commands are tried before the drives become ready again. 7267dd7cddfSDavid du Colombier * Also, there will be problems here if overlapped commands 7277dd7cddfSDavid du Colombier * are ever supported. 7287dd7cddfSDavid du Colombier */ 7297dd7cddfSDavid du Colombier microdelay(5); 7307dd7cddfSDavid du Colombier outb(ctlport+Dc, Srst); 7317dd7cddfSDavid du Colombier microdelay(5); 7327dd7cddfSDavid du Colombier outb(ctlport+Dc, 0); 7337dd7cddfSDavid du Colombier microdelay(2*1000); 7347dd7cddfSDavid du Colombier } 7357dd7cddfSDavid du Colombier 7367dd7cddfSDavid du Colombier static SDev* 7377dd7cddfSDavid du Colombier ataprobe(int cmdport, int ctlport, int irq) 7387dd7cddfSDavid du Colombier { 7397dd7cddfSDavid du Colombier Ctlr* ctlr; 7407dd7cddfSDavid du Colombier SDev *sdev; 7417dd7cddfSDavid du Colombier Drive *drive; 7427dd7cddfSDavid du Colombier int dev, error, rhi, rlo; 7434de34a7eSDavid du Colombier static int nonlegacy = 'C'; 7447dd7cddfSDavid du Colombier 7459a747e4fSDavid du Colombier if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { 7469a747e4fSDavid du Colombier print("ataprobe: Cannot allocate %X\n", cmdport); 7477dd7cddfSDavid du Colombier return nil; 7489a747e4fSDavid du Colombier } 7497dd7cddfSDavid du Colombier if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){ 7509a747e4fSDavid du Colombier print("ataprobe: Cannot allocate %X\n", ctlport + As); 7517dd7cddfSDavid du Colombier iofree(cmdport); 7527dd7cddfSDavid du Colombier return nil; 7537dd7cddfSDavid du Colombier } 7547dd7cddfSDavid du Colombier 7557dd7cddfSDavid du Colombier /* 7567dd7cddfSDavid du Colombier * Try to detect a floating bus. 7577dd7cddfSDavid du Colombier * Bsy should be cleared. If not, see if the cylinder registers 7587dd7cddfSDavid du Colombier * are read/write capable. 7597dd7cddfSDavid du Colombier * If the master fails, try the slave to catch slave-only 7607dd7cddfSDavid du Colombier * configurations. 7617dd7cddfSDavid du Colombier * There's no need to restore the tested registers as they will 7627dd7cddfSDavid du Colombier * be reset on any detected drives by the Cedd command. 7637dd7cddfSDavid du Colombier * All this indicates is that there is at least one drive on the 7647dd7cddfSDavid du Colombier * controller; when the non-existent drive is selected in a 7657dd7cddfSDavid du Colombier * single-drive configuration the registers of the existing drive 7667dd7cddfSDavid du Colombier * are often seen, only command execution fails. 7677dd7cddfSDavid du Colombier */ 7687dd7cddfSDavid du Colombier dev = Dev0; 7697dd7cddfSDavid du Colombier if(inb(ctlport+As) & Bsy){ 7707dd7cddfSDavid du Colombier outb(cmdport+Dh, dev); 7717dd7cddfSDavid du Colombier microdelay(1); 7727dd7cddfSDavid du Colombier trydev1: 7737dd7cddfSDavid du Colombier atadebug(cmdport, ctlport, "ataprobe bsy"); 7747dd7cddfSDavid du Colombier outb(cmdport+Cyllo, 0xAA); 7757dd7cddfSDavid du Colombier outb(cmdport+Cylhi, 0x55); 7767dd7cddfSDavid du Colombier outb(cmdport+Sector, 0xFF); 7777dd7cddfSDavid du Colombier rlo = inb(cmdport+Cyllo); 7787dd7cddfSDavid du Colombier rhi = inb(cmdport+Cylhi); 7797dd7cddfSDavid du Colombier if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){ 7807dd7cddfSDavid du Colombier if(dev == Dev1){ 7817dd7cddfSDavid du Colombier release: 7827dd7cddfSDavid du Colombier iofree(cmdport); 7837dd7cddfSDavid du Colombier iofree(ctlport+As); 7847dd7cddfSDavid du Colombier return nil; 7857dd7cddfSDavid du Colombier } 7867dd7cddfSDavid du Colombier dev = Dev1; 7877dd7cddfSDavid du Colombier if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0) 7887dd7cddfSDavid du Colombier goto trydev1; 7897dd7cddfSDavid du Colombier } 7907dd7cddfSDavid du Colombier } 7917dd7cddfSDavid du Colombier 7927dd7cddfSDavid du Colombier /* 7937dd7cddfSDavid du Colombier * Disable interrupts on any detected controllers. 7947dd7cddfSDavid du Colombier */ 7957dd7cddfSDavid du Colombier outb(ctlport+Dc, Nien); 7967dd7cddfSDavid du Colombier tryedd1: 7977dd7cddfSDavid du Colombier if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){ 7987dd7cddfSDavid du Colombier /* 7997dd7cddfSDavid du Colombier * There's something there, but it didn't come up clean, 8007dd7cddfSDavid du Colombier * so try hitting it with a big stick. The timing here is 8017dd7cddfSDavid du Colombier * wrong but this is a last-ditch effort and it sometimes 8027dd7cddfSDavid du Colombier * gets some marginal hardware back online. 8037dd7cddfSDavid du Colombier */ 8047dd7cddfSDavid du Colombier atasrst(ctlport); 8057dd7cddfSDavid du Colombier if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0) 8067dd7cddfSDavid du Colombier goto release; 8077dd7cddfSDavid du Colombier } 8087dd7cddfSDavid du Colombier 8097dd7cddfSDavid du Colombier /* 8107dd7cddfSDavid du Colombier * Can only get here if controller is not busy. 8117dd7cddfSDavid du Colombier * If there are drives Bsy will be set within 400nS, 8127dd7cddfSDavid du Colombier * must wait 2mS before testing Status. 8137dd7cddfSDavid du Colombier * Wait for the command to complete (6 seconds max). 8147dd7cddfSDavid du Colombier */ 8157dd7cddfSDavid du Colombier outb(cmdport+Command, Cedd); 8167dd7cddfSDavid du Colombier delay(2); 8177dd7cddfSDavid du Colombier if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0) 8187dd7cddfSDavid du Colombier goto release; 8197dd7cddfSDavid du Colombier 8207dd7cddfSDavid du Colombier /* 8217dd7cddfSDavid du Colombier * If bit 0 of the error register is set then the selected drive 8227dd7cddfSDavid du Colombier * exists. This is enough to detect single-drive configurations. 8237dd7cddfSDavid du Colombier * However, if the master exists there is no way short of executing 8247dd7cddfSDavid du Colombier * a command to determine if a slave is present. 8257dd7cddfSDavid du Colombier * It appears possible to get here testing Dev0 although it doesn't 8267dd7cddfSDavid du Colombier * exist and the EDD won't take, so try again with Dev1. 8277dd7cddfSDavid du Colombier */ 8287dd7cddfSDavid du Colombier error = inb(cmdport+Error); 8297dd7cddfSDavid du Colombier atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev); 8307dd7cddfSDavid du Colombier if((error & ~0x80) != 0x01){ 8317dd7cddfSDavid du Colombier if(dev == Dev1) 8327dd7cddfSDavid du Colombier goto release; 8337dd7cddfSDavid du Colombier dev = Dev1; 8347dd7cddfSDavid du Colombier goto tryedd1; 8357dd7cddfSDavid du Colombier } 8367dd7cddfSDavid du Colombier 8377dd7cddfSDavid du Colombier /* 8387dd7cddfSDavid du Colombier * At least one drive is known to exist, try to 8397dd7cddfSDavid du Colombier * identify it. If that fails, don't bother checking 8407dd7cddfSDavid du Colombier * any further. 8417dd7cddfSDavid du Colombier * If the one drive found is Dev0 and the EDD command 8427dd7cddfSDavid du Colombier * didn't indicate Dev1 doesn't exist, check for it. 8437dd7cddfSDavid du Colombier */ 8447dd7cddfSDavid du Colombier if((drive = atadrive(cmdport, ctlport, dev)) == nil) 8457dd7cddfSDavid du Colombier goto release; 8467dd7cddfSDavid du Colombier if((ctlr = malloc(sizeof(Ctlr))) == nil){ 8477dd7cddfSDavid du Colombier free(drive); 8487dd7cddfSDavid du Colombier goto release; 8497dd7cddfSDavid du Colombier } 8509a747e4fSDavid du Colombier memset(ctlr, 0, sizeof(Ctlr)); 8517dd7cddfSDavid du Colombier if((sdev = malloc(sizeof(SDev))) == nil){ 8527dd7cddfSDavid du Colombier free(ctlr); 8537dd7cddfSDavid du Colombier free(drive); 8547dd7cddfSDavid du Colombier goto release; 8557dd7cddfSDavid du Colombier } 8569a747e4fSDavid du Colombier memset(sdev, 0, sizeof(SDev)); 8577dd7cddfSDavid du Colombier drive->ctlr = ctlr; 8587dd7cddfSDavid du Colombier if(dev == Dev0){ 8597dd7cddfSDavid du Colombier ctlr->drive[0] = drive; 8607dd7cddfSDavid du Colombier if(!(error & 0x80)){ 8617dd7cddfSDavid du Colombier /* 8627dd7cddfSDavid du Colombier * Always leave Dh pointing to a valid drive, 8637dd7cddfSDavid du Colombier * otherwise a subsequent call to ataready on 8647dd7cddfSDavid du Colombier * this controller may try to test a bogus Status. 8657dd7cddfSDavid du Colombier * Ataprobe is the only place possibly invalid 8667dd7cddfSDavid du Colombier * drives should be selected. 8677dd7cddfSDavid du Colombier */ 8687dd7cddfSDavid du Colombier drive = atadrive(cmdport, ctlport, Dev1); 8697dd7cddfSDavid du Colombier if(drive != nil){ 8707dd7cddfSDavid du Colombier drive->ctlr = ctlr; 8717dd7cddfSDavid du Colombier ctlr->drive[1] = drive; 8727dd7cddfSDavid du Colombier } 8737dd7cddfSDavid du Colombier else{ 8747dd7cddfSDavid du Colombier outb(cmdport+Dh, Dev0); 8757dd7cddfSDavid du Colombier microdelay(1); 8767dd7cddfSDavid du Colombier } 8777dd7cddfSDavid du Colombier } 8787dd7cddfSDavid du Colombier } 8797dd7cddfSDavid du Colombier else 8807dd7cddfSDavid du Colombier ctlr->drive[1] = drive; 8817dd7cddfSDavid du Colombier 8827dd7cddfSDavid du Colombier ctlr->cmdport = cmdport; 8837dd7cddfSDavid du Colombier ctlr->ctlport = ctlport; 8847dd7cddfSDavid du Colombier ctlr->irq = irq; 8857dd7cddfSDavid du Colombier ctlr->tbdf = BUSUNKNOWN; 8867dd7cddfSDavid du Colombier ctlr->command = Cedd; /* debugging */ 8877dd7cddfSDavid du Colombier 8884de34a7eSDavid du Colombier switch(cmdport){ 8894de34a7eSDavid du Colombier default: 8904de34a7eSDavid du Colombier sdev->idno = nonlegacy; 8914de34a7eSDavid du Colombier break; 8924de34a7eSDavid du Colombier case 0x1F0: 8934de34a7eSDavid du Colombier sdev->idno = 'C'; 8944de34a7eSDavid du Colombier nonlegacy = 'E'; 8954de34a7eSDavid du Colombier break; 8964de34a7eSDavid du Colombier case 0x170: 8974de34a7eSDavid du Colombier sdev->idno = 'D'; 8984de34a7eSDavid du Colombier nonlegacy = 'E'; 8994de34a7eSDavid du Colombier break; 9004de34a7eSDavid du Colombier } 9017dd7cddfSDavid du Colombier sdev->ifc = &sdataifc; 9027dd7cddfSDavid du Colombier sdev->ctlr = ctlr; 9037dd7cddfSDavid du Colombier sdev->nunit = 2; 9047dd7cddfSDavid du Colombier ctlr->sdev = sdev; 9057dd7cddfSDavid du Colombier 9067dd7cddfSDavid du Colombier return sdev; 9077dd7cddfSDavid du Colombier } 9087dd7cddfSDavid du Colombier 9099a747e4fSDavid du Colombier static void 9109a747e4fSDavid du Colombier ataclear(SDev *sdev) 9119a747e4fSDavid du Colombier { 9129a747e4fSDavid du Colombier Ctlr* ctlr; 9139a747e4fSDavid du Colombier 9149a747e4fSDavid du Colombier ctlr = sdev->ctlr; 9159a747e4fSDavid du Colombier iofree(ctlr->cmdport); 9169a747e4fSDavid du Colombier iofree(ctlr->ctlport + As); 9179a747e4fSDavid du Colombier 9189a747e4fSDavid du Colombier if (ctlr->drive[0]) 9199a747e4fSDavid du Colombier free(ctlr->drive[0]); 9209a747e4fSDavid du Colombier if (ctlr->drive[1]) 9219a747e4fSDavid du Colombier free(ctlr->drive[1]); 9229a747e4fSDavid du Colombier if (sdev->name) 9239a747e4fSDavid du Colombier free(sdev->name); 9249a747e4fSDavid du Colombier if (sdev->unitflg) 9259a747e4fSDavid du Colombier free(sdev->unitflg); 9269a747e4fSDavid du Colombier if (sdev->unit) 9279a747e4fSDavid du Colombier free(sdev->unit); 9289a747e4fSDavid du Colombier free(ctlr); 9299a747e4fSDavid du Colombier free(sdev); 9309a747e4fSDavid du Colombier } 9319a747e4fSDavid du Colombier 9329a747e4fSDavid du Colombier static char * 9339a747e4fSDavid du Colombier atastat(SDev *sdev, char *p, char *e) 9349a747e4fSDavid du Colombier { 9359a747e4fSDavid du Colombier Ctlr *ctlr = sdev->ctlr; 9369a747e4fSDavid du Colombier 9379a747e4fSDavid du Colombier return seprint(p, e, "%s ata port %X ctl %X irq %d\n", 9389a747e4fSDavid du Colombier sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq); 9399a747e4fSDavid du Colombier } 9409a747e4fSDavid du Colombier 9419a747e4fSDavid du Colombier static SDev* 9429a747e4fSDavid du Colombier ataprobew(DevConf *cf) 9439a747e4fSDavid du Colombier { 9444de34a7eSDavid du Colombier char *p; 9454de34a7eSDavid du Colombier ISAConf isa; 9464de34a7eSDavid du Colombier 9479a747e4fSDavid du Colombier if (cf->nports != 2) 9489a747e4fSDavid du Colombier error(Ebadarg); 9499a747e4fSDavid du Colombier 9504de34a7eSDavid du Colombier memset(&isa, 0, sizeof isa); 9514de34a7eSDavid du Colombier isa.port = cf->ports[0].port; 9524de34a7eSDavid du Colombier isa.irq = cf->intnum; 9534de34a7eSDavid du Colombier if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0) 9544de34a7eSDavid du Colombier error("cannot find controller"); 9554de34a7eSDavid du Colombier 9563ff48bf5SDavid du Colombier return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum); 9579a747e4fSDavid du Colombier } 9589a747e4fSDavid du Colombier 9594de34a7eSDavid du Colombier /* 9604de34a7eSDavid du Colombier * These are duplicated with sdsetsense, etc., in devsd.c, but 9614de34a7eSDavid du Colombier * those assume that the disk is not SCSI while in fact here 9624de34a7eSDavid du Colombier * ata drives are not SCSI but ATAPI ones kind of are. 9634de34a7eSDavid du Colombier */ 9647dd7cddfSDavid du Colombier static int 9657dd7cddfSDavid du Colombier atasetsense(Drive* drive, int status, int key, int asc, int ascq) 9667dd7cddfSDavid du Colombier { 9677dd7cddfSDavid du Colombier drive->sense[2] = key; 9687dd7cddfSDavid du Colombier drive->sense[12] = asc; 9697dd7cddfSDavid du Colombier drive->sense[13] = ascq; 9707dd7cddfSDavid du Colombier 9717dd7cddfSDavid du Colombier return status; 9727dd7cddfSDavid du Colombier } 9737dd7cddfSDavid du Colombier 9747dd7cddfSDavid du Colombier static int 9757dd7cddfSDavid du Colombier atamodesense(Drive* drive, uchar* cmd) 9767dd7cddfSDavid du Colombier { 9777dd7cddfSDavid du Colombier int len; 9787dd7cddfSDavid du Colombier 9797dd7cddfSDavid du Colombier /* 9807dd7cddfSDavid du Colombier * Fake a vendor-specific request with page code 0, 9817dd7cddfSDavid du Colombier * return the drive info. 9827dd7cddfSDavid du Colombier */ 9837dd7cddfSDavid du Colombier if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) 9847dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x24, 0); 9857dd7cddfSDavid du Colombier len = (cmd[7]<<8)|cmd[8]; 9867dd7cddfSDavid du Colombier if(len == 0) 9877dd7cddfSDavid du Colombier return SDok; 9887dd7cddfSDavid du Colombier if(len < 8+sizeof(drive->info)) 9897dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x1A, 0); 9907dd7cddfSDavid du Colombier if(drive->data == nil || drive->dlen < len) 9917dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x20, 1); 9927dd7cddfSDavid du Colombier memset(drive->data, 0, 8); 9937dd7cddfSDavid du Colombier drive->data[0] = sizeof(drive->info)>>8; 9947dd7cddfSDavid du Colombier drive->data[1] = sizeof(drive->info); 9957dd7cddfSDavid du Colombier memmove(drive->data+8, drive->info, sizeof(drive->info)); 9967dd7cddfSDavid du Colombier drive->data += 8+sizeof(drive->info); 9977dd7cddfSDavid du Colombier 9987dd7cddfSDavid du Colombier return SDok; 9997dd7cddfSDavid du Colombier } 10007dd7cddfSDavid du Colombier 10014de34a7eSDavid du Colombier static int 10024de34a7eSDavid du Colombier atastandby(Drive* drive, int period) 10034de34a7eSDavid du Colombier { 10044de34a7eSDavid du Colombier Ctlr* ctlr; 10054de34a7eSDavid du Colombier int cmdport, done; 10064de34a7eSDavid du Colombier 10074de34a7eSDavid du Colombier ctlr = drive->ctlr; 10084de34a7eSDavid du Colombier drive->command = Cstandby; 10094de34a7eSDavid du Colombier qlock(ctlr); 10104de34a7eSDavid du Colombier 10114de34a7eSDavid du Colombier cmdport = ctlr->cmdport; 10124de34a7eSDavid du Colombier ilock(ctlr); 10134de34a7eSDavid du Colombier outb(cmdport+Count, period); 10144de34a7eSDavid du Colombier outb(cmdport+Dh, drive->dev); 10154de34a7eSDavid du Colombier ctlr->done = 0; 10164de34a7eSDavid du Colombier ctlr->curdrive = drive; 10174de34a7eSDavid du Colombier ctlr->command = Cstandby; /* debugging */ 10184de34a7eSDavid du Colombier outb(cmdport+Command, Cstandby); 10194de34a7eSDavid du Colombier iunlock(ctlr); 10204de34a7eSDavid du Colombier 10214de34a7eSDavid du Colombier while(waserror()) 10224de34a7eSDavid du Colombier ; 10234de34a7eSDavid du Colombier tsleep(ctlr, atadone, ctlr, 60*1000); 10244de34a7eSDavid du Colombier poperror(); 10254de34a7eSDavid du Colombier 10264de34a7eSDavid du Colombier done = ctlr->done; 10274de34a7eSDavid du Colombier qunlock(ctlr); 10284de34a7eSDavid du Colombier 10294de34a7eSDavid du Colombier if(!done || (drive->status & Err)) 10304de34a7eSDavid du Colombier return atasetsense(drive, SDcheck, 4, 8, drive->error); 10314de34a7eSDavid du Colombier return SDok; 10324de34a7eSDavid du Colombier } 10334de34a7eSDavid du Colombier 10347dd7cddfSDavid du Colombier static void 10357dd7cddfSDavid du Colombier atanop(Drive* drive, int subcommand) 10367dd7cddfSDavid du Colombier { 10377dd7cddfSDavid du Colombier Ctlr* ctlr; 10387dd7cddfSDavid du Colombier int as, cmdport, ctlport, timeo; 10397dd7cddfSDavid du Colombier 10407dd7cddfSDavid du Colombier /* 10417dd7cddfSDavid du Colombier * Attempt to abort a command by using NOP. 10427dd7cddfSDavid du Colombier * In response, the drive is supposed to set Abrt 10437dd7cddfSDavid du Colombier * in the Error register, set (Drdy|Err) in Status 10447dd7cddfSDavid du Colombier * and clear Bsy when done. However, some drives 10457dd7cddfSDavid du Colombier * (e.g. ATAPI Zip) just go Bsy then clear Status 10467dd7cddfSDavid du Colombier * when done, hence the timeout loop only on Bsy 10477dd7cddfSDavid du Colombier * and the forced setting of drive->error. 10487dd7cddfSDavid du Colombier */ 10497dd7cddfSDavid du Colombier ctlr = drive->ctlr; 10507dd7cddfSDavid du Colombier cmdport = ctlr->cmdport; 10517dd7cddfSDavid du Colombier outb(cmdport+Features, subcommand); 10527dd7cddfSDavid du Colombier outb(cmdport+Dh, drive->dev); 10537dd7cddfSDavid du Colombier ctlr->command = Cnop; /* debugging */ 10547dd7cddfSDavid du Colombier outb(cmdport+Command, Cnop); 10557dd7cddfSDavid du Colombier 10567dd7cddfSDavid du Colombier microdelay(1); 10577dd7cddfSDavid du Colombier ctlport = ctlr->ctlport; 10587dd7cddfSDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){ 10597dd7cddfSDavid du Colombier as = inb(ctlport+As); 10607dd7cddfSDavid du Colombier if(!(as & Bsy)) 10617dd7cddfSDavid du Colombier break; 10627dd7cddfSDavid du Colombier microdelay(1); 10637dd7cddfSDavid du Colombier } 10647dd7cddfSDavid du Colombier drive->error |= Abrt; 10657dd7cddfSDavid du Colombier } 10667dd7cddfSDavid du Colombier 10677dd7cddfSDavid du Colombier static void 10687dd7cddfSDavid du Colombier ataabort(Drive* drive, int dolock) 10697dd7cddfSDavid du Colombier { 10707dd7cddfSDavid du Colombier /* 10717dd7cddfSDavid du Colombier * If NOP is available (packet commands) use it otherwise 10727dd7cddfSDavid du Colombier * must try a software reset. 10737dd7cddfSDavid du Colombier */ 10747dd7cddfSDavid du Colombier if(dolock) 10757dd7cddfSDavid du Colombier ilock(drive->ctlr); 10766a081dcdSDavid du Colombier if(drive->info[Icsfs] & Mnop) 10777dd7cddfSDavid du Colombier atanop(drive, 0); 10787dd7cddfSDavid du Colombier else{ 10797dd7cddfSDavid du Colombier atasrst(drive->ctlr->ctlport); 10807dd7cddfSDavid du Colombier drive->error |= Abrt; 10817dd7cddfSDavid du Colombier } 10827dd7cddfSDavid du Colombier if(dolock) 10837dd7cddfSDavid du Colombier iunlock(drive->ctlr); 10847dd7cddfSDavid du Colombier } 10857dd7cddfSDavid du Colombier 10867dd7cddfSDavid du Colombier static int 10877dd7cddfSDavid du Colombier atadmasetup(Drive* drive, int len) 10887dd7cddfSDavid du Colombier { 10897dd7cddfSDavid du Colombier Prd *prd; 10907dd7cddfSDavid du Colombier ulong pa; 10917dd7cddfSDavid du Colombier Ctlr *ctlr; 10924de34a7eSDavid du Colombier int bmiba, bmisx, count, i, span; 10937dd7cddfSDavid du Colombier 10944de34a7eSDavid du Colombier ctlr = drive->ctlr; 10957dd7cddfSDavid du Colombier pa = PCIWADDR(drive->data); 10967dd7cddfSDavid du Colombier if(pa & 0x03) 10977dd7cddfSDavid du Colombier return -1; 10987dd7cddfSDavid du Colombier 10997dd7cddfSDavid du Colombier /* 11007dd7cddfSDavid du Colombier * Sometimes drives identify themselves as being DMA capable 11017dd7cddfSDavid du Colombier * although they are not on a busmastering controller. 11027dd7cddfSDavid du Colombier */ 11034de34a7eSDavid du Colombier prd = ctlr->prdt; 11047dd7cddfSDavid du Colombier if(prd == nil){ 11057dd7cddfSDavid du Colombier drive->dmactl = 0; 11066de6ce84SDavid du Colombier print("disabling dma: not on a busmastering controller\n"); 11077dd7cddfSDavid du Colombier return -1; 11087dd7cddfSDavid du Colombier } 11097dd7cddfSDavid du Colombier 11104de34a7eSDavid du Colombier for(i = 0; len && i < Nprd; i++){ 11117dd7cddfSDavid du Colombier prd->pa = pa; 11124de34a7eSDavid du Colombier span = ROUNDUP(pa, ctlr->span); 11134de34a7eSDavid du Colombier if(span == pa) 11144de34a7eSDavid du Colombier span += ctlr->span; 11154de34a7eSDavid du Colombier count = span - pa; 11167dd7cddfSDavid du Colombier if(count >= len){ 11174de34a7eSDavid du Colombier prd->count = PrdEOT|len; 11187dd7cddfSDavid du Colombier break; 11197dd7cddfSDavid du Colombier } 11207dd7cddfSDavid du Colombier prd->count = count; 11217dd7cddfSDavid du Colombier len -= count; 11227dd7cddfSDavid du Colombier pa += count; 11237dd7cddfSDavid du Colombier prd++; 11247dd7cddfSDavid du Colombier } 11254de34a7eSDavid du Colombier if(i == Nprd) 11264de34a7eSDavid du Colombier (prd-1)->count |= PrdEOT; 11277dd7cddfSDavid du Colombier 11287dd7cddfSDavid du Colombier bmiba = ctlr->bmiba; 11297dd7cddfSDavid du Colombier outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt)); 11307dd7cddfSDavid du Colombier if(drive->write) 11317dd7cddfSDavid du Colombier outb(ctlr->bmiba+Bmicx, 0); 11327dd7cddfSDavid du Colombier else 11337dd7cddfSDavid du Colombier outb(ctlr->bmiba+Bmicx, Rwcon); 11347dd7cddfSDavid du Colombier bmisx = inb(bmiba+Bmisx); 11357dd7cddfSDavid du Colombier outb(bmiba+Bmisx, bmisx|Ideints|Idedmae); 11367dd7cddfSDavid du Colombier 11377dd7cddfSDavid du Colombier return 0; 11387dd7cddfSDavid du Colombier } 11397dd7cddfSDavid du Colombier 11407dd7cddfSDavid du Colombier static void 11417dd7cddfSDavid du Colombier atadmastart(Ctlr* ctlr, int write) 11427dd7cddfSDavid du Colombier { 11437dd7cddfSDavid du Colombier if(write) 11447dd7cddfSDavid du Colombier outb(ctlr->bmiba+Bmicx, Ssbm); 11457dd7cddfSDavid du Colombier else 11467dd7cddfSDavid du Colombier outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm); 11477dd7cddfSDavid du Colombier } 11487dd7cddfSDavid du Colombier 11497dd7cddfSDavid du Colombier static int 11507dd7cddfSDavid du Colombier atadmastop(Ctlr* ctlr) 11517dd7cddfSDavid du Colombier { 11527dd7cddfSDavid du Colombier int bmiba; 11537dd7cddfSDavid du Colombier 11547dd7cddfSDavid du Colombier bmiba = ctlr->bmiba; 11557dd7cddfSDavid du Colombier outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm); 11567dd7cddfSDavid du Colombier 11577dd7cddfSDavid du Colombier return inb(bmiba+Bmisx); 11587dd7cddfSDavid du Colombier } 11597dd7cddfSDavid du Colombier 11607dd7cddfSDavid du Colombier static void 11617dd7cddfSDavid du Colombier atadmainterrupt(Drive* drive, int count) 11627dd7cddfSDavid du Colombier { 11637dd7cddfSDavid du Colombier Ctlr* ctlr; 11647dd7cddfSDavid du Colombier int bmiba, bmisx; 11657dd7cddfSDavid du Colombier 11667dd7cddfSDavid du Colombier ctlr = drive->ctlr; 11677dd7cddfSDavid du Colombier bmiba = ctlr->bmiba; 11687dd7cddfSDavid du Colombier bmisx = inb(bmiba+Bmisx); 11697dd7cddfSDavid du Colombier switch(bmisx & (Ideints|Idedmae|Bmidea)){ 11707dd7cddfSDavid du Colombier case Bmidea: 11717dd7cddfSDavid du Colombier /* 11727dd7cddfSDavid du Colombier * Data transfer still in progress, nothing to do 11737dd7cddfSDavid du Colombier * (this should never happen). 11747dd7cddfSDavid du Colombier */ 11757dd7cddfSDavid du Colombier return; 11767dd7cddfSDavid du Colombier 11777dd7cddfSDavid du Colombier case Ideints: 11787dd7cddfSDavid du Colombier case Ideints|Bmidea: 11797dd7cddfSDavid du Colombier /* 11807dd7cddfSDavid du Colombier * Normal termination, tidy up. 11817dd7cddfSDavid du Colombier */ 11827dd7cddfSDavid du Colombier drive->data += count; 11837dd7cddfSDavid du Colombier break; 11847dd7cddfSDavid du Colombier 11857dd7cddfSDavid du Colombier default: 11867dd7cddfSDavid du Colombier /* 11877dd7cddfSDavid du Colombier * What's left are error conditions (memory transfer 11887dd7cddfSDavid du Colombier * problem) and the device is not done but the PRD is 11897dd7cddfSDavid du Colombier * exhausted. For both cases must somehow tell the 11907dd7cddfSDavid du Colombier * drive to abort. 11917dd7cddfSDavid du Colombier */ 11927dd7cddfSDavid du Colombier ataabort(drive, 0); 11937dd7cddfSDavid du Colombier break; 11947dd7cddfSDavid du Colombier } 11957dd7cddfSDavid du Colombier atadmastop(ctlr); 11967dd7cddfSDavid du Colombier ctlr->done = 1; 11977dd7cddfSDavid du Colombier } 11987dd7cddfSDavid du Colombier 11997dd7cddfSDavid du Colombier static void 12007dd7cddfSDavid du Colombier atapktinterrupt(Drive* drive) 12017dd7cddfSDavid du Colombier { 12027dd7cddfSDavid du Colombier Ctlr* ctlr; 12037dd7cddfSDavid du Colombier int cmdport, len; 12047dd7cddfSDavid du Colombier 12057dd7cddfSDavid du Colombier ctlr = drive->ctlr; 12067dd7cddfSDavid du Colombier cmdport = ctlr->cmdport; 12077dd7cddfSDavid du Colombier switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){ 12087dd7cddfSDavid du Colombier case Cd: 12097dd7cddfSDavid du Colombier outss(cmdport+Data, drive->pktcmd, drive->pkt/2); 12107dd7cddfSDavid du Colombier break; 12117dd7cddfSDavid du Colombier 12127dd7cddfSDavid du Colombier case 0: 12137dd7cddfSDavid du Colombier len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); 12147dd7cddfSDavid du Colombier if(drive->data+len > drive->limit){ 12157dd7cddfSDavid du Colombier atanop(drive, 0); 12167dd7cddfSDavid du Colombier break; 12177dd7cddfSDavid du Colombier } 12187dd7cddfSDavid du Colombier outss(cmdport+Data, drive->data, len/2); 12197dd7cddfSDavid du Colombier drive->data += len; 12207dd7cddfSDavid du Colombier break; 12217dd7cddfSDavid du Colombier 12227dd7cddfSDavid du Colombier case Io: 12237dd7cddfSDavid du Colombier len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); 12247dd7cddfSDavid du Colombier if(drive->data+len > drive->limit){ 12257dd7cddfSDavid du Colombier atanop(drive, 0); 12267dd7cddfSDavid du Colombier break; 12277dd7cddfSDavid du Colombier } 12287dd7cddfSDavid du Colombier inss(cmdport+Data, drive->data, len/2); 12297dd7cddfSDavid du Colombier drive->data += len; 12307dd7cddfSDavid du Colombier break; 12317dd7cddfSDavid du Colombier 12327dd7cddfSDavid du Colombier case Io|Cd: 12337dd7cddfSDavid du Colombier if(drive->pktdma) 12347dd7cddfSDavid du Colombier atadmainterrupt(drive, drive->dlen); 12357dd7cddfSDavid du Colombier else 12367dd7cddfSDavid du Colombier ctlr->done = 1; 12377dd7cddfSDavid du Colombier break; 12387dd7cddfSDavid du Colombier } 12397dd7cddfSDavid du Colombier } 12407dd7cddfSDavid du Colombier 12417dd7cddfSDavid du Colombier static int 12427dd7cddfSDavid du Colombier atapktio(Drive* drive, uchar* cmd, int clen) 12437dd7cddfSDavid du Colombier { 12447dd7cddfSDavid du Colombier Ctlr *ctlr; 12457dd7cddfSDavid du Colombier int as, cmdport, ctlport, len, r, timeo; 12467dd7cddfSDavid du Colombier 12477dd7cddfSDavid du Colombier if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0) 12487dd7cddfSDavid du Colombier return atamodesense(drive, cmd); 12497dd7cddfSDavid du Colombier 12507dd7cddfSDavid du Colombier r = SDok; 12517dd7cddfSDavid du Colombier 12527dd7cddfSDavid du Colombier drive->command = Cpkt; 12537dd7cddfSDavid du Colombier memmove(drive->pktcmd, cmd, clen); 12547dd7cddfSDavid du Colombier memset(drive->pktcmd+clen, 0, drive->pkt-clen); 12557dd7cddfSDavid du Colombier drive->limit = drive->data+drive->dlen; 12567dd7cddfSDavid du Colombier 12577dd7cddfSDavid du Colombier ctlr = drive->ctlr; 12587dd7cddfSDavid du Colombier cmdport = ctlr->cmdport; 12597dd7cddfSDavid du Colombier ctlport = ctlr->ctlport; 12607dd7cddfSDavid du Colombier 12617dd7cddfSDavid du Colombier qlock(ctlr); 12627dd7cddfSDavid du Colombier 126343a33802SDavid du Colombier as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000); 126432078d07SDavid du Colombier /* used to test as&Chk as failure too, but some CD readers use that for media change */ 126532078d07SDavid du Colombier if(as < 0){ 12667dd7cddfSDavid du Colombier qunlock(ctlr); 12677dd7cddfSDavid du Colombier return -1; 12687dd7cddfSDavid du Colombier } 12697dd7cddfSDavid du Colombier 12707dd7cddfSDavid du Colombier ilock(ctlr); 12717dd7cddfSDavid du Colombier if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen)) 12727dd7cddfSDavid du Colombier drive->pktdma = Dma; 12737dd7cddfSDavid du Colombier else 12747dd7cddfSDavid du Colombier drive->pktdma = 0; 12757dd7cddfSDavid du Colombier 12767dd7cddfSDavid du Colombier outb(cmdport+Features, drive->pktdma); 12777dd7cddfSDavid du Colombier outb(cmdport+Count, 0); 12787dd7cddfSDavid du Colombier outb(cmdport+Sector, 0); 12797dd7cddfSDavid du Colombier len = 16*drive->secsize; 12807dd7cddfSDavid du Colombier outb(cmdport+Bytelo, len); 12817dd7cddfSDavid du Colombier outb(cmdport+Bytehi, len>>8); 12827dd7cddfSDavid du Colombier outb(cmdport+Dh, drive->dev); 12837dd7cddfSDavid du Colombier ctlr->done = 0; 12847dd7cddfSDavid du Colombier ctlr->curdrive = drive; 12857dd7cddfSDavid du Colombier ctlr->command = Cpkt; /* debugging */ 12867dd7cddfSDavid du Colombier if(drive->pktdma) 12877dd7cddfSDavid du Colombier atadmastart(ctlr, drive->write); 12887dd7cddfSDavid du Colombier outb(cmdport+Command, Cpkt); 12897dd7cddfSDavid du Colombier 12906a081dcdSDavid du Colombier if((drive->info[Iconfig] & Mdrq) != 0x0020){ 12917dd7cddfSDavid du Colombier microdelay(1); 12927dd7cddfSDavid du Colombier as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000); 1293a22b0629SDavid du Colombier if(as < 0 || (as & (Bsy|Chk))){ 1294a22b0629SDavid du Colombier drive->status = as<0 ? 0 : as; 1295a22b0629SDavid du Colombier ctlr->curdrive = nil; 1296a22b0629SDavid du Colombier ctlr->done = 1; 12977dd7cddfSDavid du Colombier r = SDtimeout; 1298a22b0629SDavid du Colombier }else 12997dd7cddfSDavid du Colombier atapktinterrupt(drive); 13007dd7cddfSDavid du Colombier } 13017dd7cddfSDavid du Colombier iunlock(ctlr); 13027dd7cddfSDavid du Colombier 13037dd7cddfSDavid du Colombier while(waserror()) 13047dd7cddfSDavid du Colombier ; 13057dd7cddfSDavid du Colombier if(!drive->pktdma) 130680ee5cbfSDavid du Colombier sleep(ctlr, atadone, ctlr); 13077dd7cddfSDavid du Colombier else for(timeo = 0; !ctlr->done; timeo++){ 130880ee5cbfSDavid du Colombier tsleep(ctlr, atadone, ctlr, 1000); 13097dd7cddfSDavid du Colombier if(ctlr->done) 13107dd7cddfSDavid du Colombier break; 13117dd7cddfSDavid du Colombier ilock(ctlr); 13127dd7cddfSDavid du Colombier atadmainterrupt(drive, 0); 13134de34a7eSDavid du Colombier if(!drive->error && timeo > 20){ 13147dd7cddfSDavid du Colombier ataabort(drive, 0); 13157dd7cddfSDavid du Colombier atadmastop(ctlr); 13167dd7cddfSDavid du Colombier drive->dmactl = 0; 13177dd7cddfSDavid du Colombier drive->error |= Abrt; 13187dd7cddfSDavid du Colombier } 13197dd7cddfSDavid du Colombier if(drive->error){ 13207dd7cddfSDavid du Colombier drive->status |= Chk; 13217dd7cddfSDavid du Colombier ctlr->curdrive = nil; 13227dd7cddfSDavid du Colombier } 13237dd7cddfSDavid du Colombier iunlock(ctlr); 13247dd7cddfSDavid du Colombier } 13257dd7cddfSDavid du Colombier poperror(); 13267dd7cddfSDavid du Colombier 13277dd7cddfSDavid du Colombier qunlock(ctlr); 13287dd7cddfSDavid du Colombier 13297dd7cddfSDavid du Colombier if(drive->status & Chk) 13307dd7cddfSDavid du Colombier r = SDcheck; 13317dd7cddfSDavid du Colombier 13327dd7cddfSDavid du Colombier return r; 13337dd7cddfSDavid du Colombier } 13347dd7cddfSDavid du Colombier 13356a081dcdSDavid du Colombier static uchar cmd48[256] = { 13366a081dcdSDavid du Colombier [Crs] Crs48, 13376a081dcdSDavid du Colombier [Crd] Crd48, 13386a081dcdSDavid du Colombier [Crdq] Crdq48, 13396a081dcdSDavid du Colombier [Crsm] Crsm48, 13406a081dcdSDavid du Colombier [Cws] Cws48, 13416a081dcdSDavid du Colombier [Cwd] Cwd48, 13426a081dcdSDavid du Colombier [Cwdq] Cwdq48, 13436a081dcdSDavid du Colombier [Cwsm] Cwsm48, 13446a081dcdSDavid du Colombier }; 13456a081dcdSDavid du Colombier 13467dd7cddfSDavid du Colombier static int 13476a081dcdSDavid du Colombier atageniostart(Drive* drive, vlong lba) 13487dd7cddfSDavid du Colombier { 13497dd7cddfSDavid du Colombier Ctlr *ctlr; 13506a081dcdSDavid du Colombier uchar cmd; 13515ea8af7bSDavid du Colombier int as, c, cmdport, ctlport, h, len, s, use48; 13527dd7cddfSDavid du Colombier 13535ea8af7bSDavid du Colombier use48 = 0; 13546a081dcdSDavid du Colombier if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){ 13556a081dcdSDavid du Colombier if(!(drive->flags & Lba48)) 13565ea8af7bSDavid du Colombier return -1; 13575ea8af7bSDavid du Colombier use48 = 1; 13585ea8af7bSDavid du Colombier c = h = s = 0; 135959f4c0c1SDavid du Colombier } 136059f4c0c1SDavid du Colombier else if(drive->dev & Lba){ 13617dd7cddfSDavid du Colombier c = (lba>>8) & 0xFFFF; 13627dd7cddfSDavid du Colombier h = (lba>>24) & 0x0F; 13637dd7cddfSDavid du Colombier s = lba & 0xFF; 136459f4c0c1SDavid du Colombier } 136559f4c0c1SDavid du Colombier else{ 13667dd7cddfSDavid du Colombier c = lba/(drive->s*drive->h); 13677dd7cddfSDavid du Colombier h = ((lba/drive->s) % drive->h); 13687dd7cddfSDavid du Colombier s = (lba % drive->s) + 1; 13697dd7cddfSDavid du Colombier } 13707dd7cddfSDavid du Colombier 13717dd7cddfSDavid du Colombier ctlr = drive->ctlr; 13727dd7cddfSDavid du Colombier cmdport = ctlr->cmdport; 13737dd7cddfSDavid du Colombier ctlport = ctlr->ctlport; 13747dd7cddfSDavid du Colombier if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0) 13757dd7cddfSDavid du Colombier return -1; 13767dd7cddfSDavid du Colombier 13777dd7cddfSDavid du Colombier ilock(ctlr); 13787dd7cddfSDavid du Colombier if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){ 13797dd7cddfSDavid du Colombier if(drive->write) 13807dd7cddfSDavid du Colombier drive->command = Cwd; 13817dd7cddfSDavid du Colombier else 13827dd7cddfSDavid du Colombier drive->command = Crd; 13837dd7cddfSDavid du Colombier } 13847dd7cddfSDavid du Colombier else if(drive->rwmctl){ 13857dd7cddfSDavid du Colombier drive->block = drive->rwm*drive->secsize; 13867dd7cddfSDavid du Colombier if(drive->write) 13877dd7cddfSDavid du Colombier drive->command = Cwsm; 13887dd7cddfSDavid du Colombier else 13897dd7cddfSDavid du Colombier drive->command = Crsm; 13907dd7cddfSDavid du Colombier } 13917dd7cddfSDavid du Colombier else{ 13927dd7cddfSDavid du Colombier drive->block = drive->secsize; 13937dd7cddfSDavid du Colombier if(drive->write) 13947dd7cddfSDavid du Colombier drive->command = Cws; 13957dd7cddfSDavid du Colombier else 13967dd7cddfSDavid du Colombier drive->command = Crs; 13977dd7cddfSDavid du Colombier } 13987dd7cddfSDavid du Colombier drive->limit = drive->data + drive->count*drive->secsize; 13996a081dcdSDavid du Colombier cmd = drive->command; 14005ea8af7bSDavid du Colombier if(use48){ 14016a081dcdSDavid du Colombier outb(cmdport+Count, (drive->count>>8) & 0xFF); 14026a081dcdSDavid du Colombier outb(cmdport+Count, drive->count & 0XFF); 14036a081dcdSDavid du Colombier outb(cmdport+Lbalo, (lba>>24) & 0xFF); 14046a081dcdSDavid du Colombier outb(cmdport+Lbalo, lba & 0xFF); 14056a081dcdSDavid du Colombier outb(cmdport+Lbamid, (lba>>32) & 0xFF); 14066a081dcdSDavid du Colombier outb(cmdport+Lbamid, (lba>>8) & 0xFF); 14076a081dcdSDavid du Colombier outb(cmdport+Lbahi, (lba>>40) & 0xFF); 14086a081dcdSDavid du Colombier outb(cmdport+Lbahi, (lba>>16) & 0xFF); 14096a081dcdSDavid du Colombier outb(cmdport+Dh, drive->dev|Lba); 14106a081dcdSDavid du Colombier cmd = cmd48[cmd]; 14115ea8af7bSDavid du Colombier 14126a081dcdSDavid du Colombier if(DEBUG & Dbg48BIT) 14136a081dcdSDavid du Colombier print("using 48-bit commands\n"); 141459f4c0c1SDavid du Colombier } 141559f4c0c1SDavid du Colombier else{ 14167dd7cddfSDavid du Colombier outb(cmdport+Count, drive->count); 14177dd7cddfSDavid du Colombier outb(cmdport+Sector, s); 14187dd7cddfSDavid du Colombier outb(cmdport+Cyllo, c); 14197dd7cddfSDavid du Colombier outb(cmdport+Cylhi, c>>8); 14206a081dcdSDavid du Colombier outb(cmdport+Dh, drive->dev|h); 14215ea8af7bSDavid du Colombier } 14227dd7cddfSDavid du Colombier ctlr->done = 0; 14237dd7cddfSDavid du Colombier ctlr->curdrive = drive; 14247dd7cddfSDavid du Colombier ctlr->command = drive->command; /* debugging */ 14256a081dcdSDavid du Colombier outb(cmdport+Command, cmd); 14267dd7cddfSDavid du Colombier 14277dd7cddfSDavid du Colombier switch(drive->command){ 14287dd7cddfSDavid du Colombier case Cws: 14297dd7cddfSDavid du Colombier case Cwsm: 14307dd7cddfSDavid du Colombier microdelay(1); 14314de34a7eSDavid du Colombier /* 10*1000 for flash ide drives - maybe detect them? */ 14324de34a7eSDavid du Colombier as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*1000); 14337dd7cddfSDavid du Colombier if(as < 0 || (as & Err)){ 14347dd7cddfSDavid du Colombier iunlock(ctlr); 14357dd7cddfSDavid du Colombier return -1; 14367dd7cddfSDavid du Colombier } 14377dd7cddfSDavid du Colombier len = drive->block; 14387dd7cddfSDavid du Colombier if(drive->data+len > drive->limit) 14397dd7cddfSDavid du Colombier len = drive->limit-drive->data; 14407dd7cddfSDavid du Colombier outss(cmdport+Data, drive->data, len/2); 14417dd7cddfSDavid du Colombier break; 14427dd7cddfSDavid du Colombier 14437dd7cddfSDavid du Colombier case Crd: 14447dd7cddfSDavid du Colombier case Cwd: 14457dd7cddfSDavid du Colombier atadmastart(ctlr, drive->write); 14467dd7cddfSDavid du Colombier break; 14477dd7cddfSDavid du Colombier } 14487dd7cddfSDavid du Colombier iunlock(ctlr); 14497dd7cddfSDavid du Colombier 14507dd7cddfSDavid du Colombier return 0; 14517dd7cddfSDavid du Colombier } 14527dd7cddfSDavid du Colombier 14537dd7cddfSDavid du Colombier static int 14547dd7cddfSDavid du Colombier atagenioretry(Drive* drive) 14557dd7cddfSDavid du Colombier { 14566de6ce84SDavid du Colombier if(drive->dmactl){ 14577dd7cddfSDavid du Colombier drive->dmactl = 0; 14586de6ce84SDavid du Colombier print("atagenioretry: disabling dma\n"); 1459ef9eff0bSDavid du Colombier } 1460ef9eff0bSDavid du Colombier else if(drive->rwmctl) 14617dd7cddfSDavid du Colombier drive->rwmctl = 0; 14627dd7cddfSDavid du Colombier else 14637dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 4, 8, drive->error); 14647dd7cddfSDavid du Colombier 14657dd7cddfSDavid du Colombier return SDretry; 14667dd7cddfSDavid du Colombier } 14677dd7cddfSDavid du Colombier 14687dd7cddfSDavid du Colombier static int 14697dd7cddfSDavid du Colombier atagenio(Drive* drive, uchar* cmd, int) 14707dd7cddfSDavid du Colombier { 14717dd7cddfSDavid du Colombier uchar *p; 14727dd7cddfSDavid du Colombier Ctlr *ctlr; 14736a081dcdSDavid du Colombier vlong lba, len; 14744de34a7eSDavid du Colombier int count, maxio; 14757dd7cddfSDavid du Colombier 14767dd7cddfSDavid du Colombier /* 14777dd7cddfSDavid du Colombier * Map SCSI commands into ATA commands for discs. 14787dd7cddfSDavid du Colombier * Fail any command with a LUN except INQUIRY which 14797dd7cddfSDavid du Colombier * will return 'logical unit not supported'. 14807dd7cddfSDavid du Colombier */ 14817dd7cddfSDavid du Colombier if((cmd[1]>>5) && cmd[0] != 0x12) 14827dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x25, 0); 14837dd7cddfSDavid du Colombier 14847dd7cddfSDavid du Colombier switch(cmd[0]){ 14857dd7cddfSDavid du Colombier default: 14867dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x20, 0); 14877dd7cddfSDavid du Colombier 14887dd7cddfSDavid du Colombier case 0x00: /* test unit ready */ 14897dd7cddfSDavid du Colombier return SDok; 14907dd7cddfSDavid du Colombier 14917dd7cddfSDavid du Colombier case 0x03: /* request sense */ 14927dd7cddfSDavid du Colombier if(cmd[4] < sizeof(drive->sense)) 14937dd7cddfSDavid du Colombier len = cmd[4]; 14947dd7cddfSDavid du Colombier else 14957dd7cddfSDavid du Colombier len = sizeof(drive->sense); 14967dd7cddfSDavid du Colombier if(drive->data && drive->dlen >= len){ 14977dd7cddfSDavid du Colombier memmove(drive->data, drive->sense, len); 14987dd7cddfSDavid du Colombier drive->data += len; 14997dd7cddfSDavid du Colombier } 15007dd7cddfSDavid du Colombier return SDok; 15017dd7cddfSDavid du Colombier 15027dd7cddfSDavid du Colombier case 0x12: /* inquiry */ 15037dd7cddfSDavid du Colombier if(cmd[4] < sizeof(drive->inquiry)) 15047dd7cddfSDavid du Colombier len = cmd[4]; 15057dd7cddfSDavid du Colombier else 15067dd7cddfSDavid du Colombier len = sizeof(drive->inquiry); 15077dd7cddfSDavid du Colombier if(drive->data && drive->dlen >= len){ 15087dd7cddfSDavid du Colombier memmove(drive->data, drive->inquiry, len); 15097dd7cddfSDavid du Colombier drive->data += len; 15107dd7cddfSDavid du Colombier } 15117dd7cddfSDavid du Colombier return SDok; 15127dd7cddfSDavid du Colombier 15137dd7cddfSDavid du Colombier case 0x1B: /* start/stop unit */ 15147dd7cddfSDavid du Colombier /* 15157dd7cddfSDavid du Colombier * NOP for now, can use the power management feature 15167dd7cddfSDavid du Colombier * set later. 15177dd7cddfSDavid du Colombier */ 15187dd7cddfSDavid du Colombier return SDok; 15197dd7cddfSDavid du Colombier 15207dd7cddfSDavid du Colombier case 0x25: /* read capacity */ 15217dd7cddfSDavid du Colombier if((cmd[1] & 0x01) || cmd[2] || cmd[3]) 15227dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x24, 0); 15237dd7cddfSDavid du Colombier if(drive->data == nil || drive->dlen < 8) 15247dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x20, 1); 15257dd7cddfSDavid du Colombier /* 15267dd7cddfSDavid du Colombier * Read capacity returns the LBA of the last sector. 15277dd7cddfSDavid du Colombier */ 15287dd7cddfSDavid du Colombier len = drive->sectors-1; 15297dd7cddfSDavid du Colombier p = drive->data; 15307dd7cddfSDavid du Colombier *p++ = len>>24; 15317dd7cddfSDavid du Colombier *p++ = len>>16; 15327dd7cddfSDavid du Colombier *p++ = len>>8; 15337dd7cddfSDavid du Colombier *p++ = len; 15347dd7cddfSDavid du Colombier len = drive->secsize; 15357dd7cddfSDavid du Colombier *p++ = len>>24; 15367dd7cddfSDavid du Colombier *p++ = len>>16; 15377dd7cddfSDavid du Colombier *p++ = len>>8; 15387dd7cddfSDavid du Colombier *p = len; 15397dd7cddfSDavid du Colombier drive->data += 8; 15407dd7cddfSDavid du Colombier return SDok; 15417dd7cddfSDavid du Colombier 15426a081dcdSDavid du Colombier case 0x9E: /* long read capacity */ 15436a081dcdSDavid du Colombier if((cmd[1] & 0x01) || cmd[2] || cmd[3]) 15446a081dcdSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x24, 0); 15456a081dcdSDavid du Colombier if(drive->data == nil || drive->dlen < 8) 15466a081dcdSDavid du Colombier return atasetsense(drive, SDcheck, 0x05, 0x20, 1); 15476a081dcdSDavid du Colombier /* 15486a081dcdSDavid du Colombier * Read capacity returns the LBA of the last sector. 15496a081dcdSDavid du Colombier */ 15506a081dcdSDavid du Colombier len = drive->sectors-1; 15516a081dcdSDavid du Colombier p = drive->data; 15526a081dcdSDavid du Colombier *p++ = len>>56; 15536a081dcdSDavid du Colombier *p++ = len>>48; 15546a081dcdSDavid du Colombier *p++ = len>>40; 15556a081dcdSDavid du Colombier *p++ = len>>32; 15566a081dcdSDavid du Colombier *p++ = len>>24; 15576a081dcdSDavid du Colombier *p++ = len>>16; 15586a081dcdSDavid du Colombier *p++ = len>>8; 15596a081dcdSDavid du Colombier *p++ = len; 15606a081dcdSDavid du Colombier len = drive->secsize; 15616a081dcdSDavid du Colombier *p++ = len>>24; 15626a081dcdSDavid du Colombier *p++ = len>>16; 15636a081dcdSDavid du Colombier *p++ = len>>8; 15646a081dcdSDavid du Colombier *p = len; 15654de34a7eSDavid du Colombier drive->data += 12; 15666a081dcdSDavid du Colombier return SDok; 15676a081dcdSDavid du Colombier 15687dd7cddfSDavid du Colombier case 0x28: /* read */ 15697dd7cddfSDavid du Colombier case 0x2A: /* write */ 15707dd7cddfSDavid du Colombier break; 15717dd7cddfSDavid du Colombier 15727dd7cddfSDavid du Colombier case 0x5A: 15737dd7cddfSDavid du Colombier return atamodesense(drive, cmd); 15747dd7cddfSDavid du Colombier } 15757dd7cddfSDavid du Colombier 15767dd7cddfSDavid du Colombier ctlr = drive->ctlr; 15777dd7cddfSDavid du Colombier lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; 15787dd7cddfSDavid du Colombier count = (cmd[7]<<8)|cmd[8]; 15797dd7cddfSDavid du Colombier if(drive->data == nil) 15807dd7cddfSDavid du Colombier return SDok; 15817dd7cddfSDavid du Colombier if(drive->dlen < count*drive->secsize) 15827dd7cddfSDavid du Colombier count = drive->dlen/drive->secsize; 15837dd7cddfSDavid du Colombier qlock(ctlr); 15844de34a7eSDavid du Colombier if(ctlr->maxio) 15854de34a7eSDavid du Colombier maxio = ctlr->maxio; 15864de34a7eSDavid du Colombier else if(drive->flags & Lba48) 15874de34a7eSDavid du Colombier maxio = 65536; 15884de34a7eSDavid du Colombier else 15894de34a7eSDavid du Colombier maxio = 256; 15907dd7cddfSDavid du Colombier while(count){ 15914de34a7eSDavid du Colombier if(count > maxio) 15924de34a7eSDavid du Colombier drive->count = maxio; 15937dd7cddfSDavid du Colombier else 15947dd7cddfSDavid du Colombier drive->count = count; 15957dd7cddfSDavid du Colombier if(atageniostart(drive, lba)){ 15967dd7cddfSDavid du Colombier ilock(ctlr); 15977dd7cddfSDavid du Colombier atanop(drive, 0); 15987dd7cddfSDavid du Colombier iunlock(ctlr); 15997dd7cddfSDavid du Colombier qunlock(ctlr); 16007dd7cddfSDavid du Colombier return atagenioretry(drive); 16017dd7cddfSDavid du Colombier } 16027dd7cddfSDavid du Colombier 16037dd7cddfSDavid du Colombier while(waserror()) 16047dd7cddfSDavid du Colombier ; 16054de34a7eSDavid du Colombier tsleep(ctlr, atadone, ctlr, 60*1000); 16067dd7cddfSDavid du Colombier poperror(); 16077dd7cddfSDavid du Colombier if(!ctlr->done){ 16087dd7cddfSDavid du Colombier /* 16097dd7cddfSDavid du Colombier * What should the above timeout be? In 16107dd7cddfSDavid du Colombier * standby and sleep modes it could take as 16117dd7cddfSDavid du Colombier * long as 30 seconds for a drive to respond. 16127dd7cddfSDavid du Colombier * Very hard to get out of this cleanly. 16137dd7cddfSDavid du Colombier */ 16147dd7cddfSDavid du Colombier atadumpstate(drive, cmd, lba, count); 16157dd7cddfSDavid du Colombier ataabort(drive, 1); 161680ee5cbfSDavid du Colombier qunlock(ctlr); 16177dd7cddfSDavid du Colombier return atagenioretry(drive); 16187dd7cddfSDavid du Colombier } 16197dd7cddfSDavid du Colombier 16207dd7cddfSDavid du Colombier if(drive->status & Err){ 16217dd7cddfSDavid du Colombier qunlock(ctlr); 16227dd7cddfSDavid du Colombier return atasetsense(drive, SDcheck, 4, 8, drive->error); 16237dd7cddfSDavid du Colombier } 16247dd7cddfSDavid du Colombier count -= drive->count; 16257dd7cddfSDavid du Colombier lba += drive->count; 16267dd7cddfSDavid du Colombier } 16277dd7cddfSDavid du Colombier qunlock(ctlr); 16287dd7cddfSDavid du Colombier 16297dd7cddfSDavid du Colombier return SDok; 16307dd7cddfSDavid du Colombier } 16317dd7cddfSDavid du Colombier 16327dd7cddfSDavid du Colombier static int 16337dd7cddfSDavid du Colombier atario(SDreq* r) 16347dd7cddfSDavid du Colombier { 16357dd7cddfSDavid du Colombier Ctlr *ctlr; 16367dd7cddfSDavid du Colombier Drive *drive; 16377dd7cddfSDavid du Colombier SDunit *unit; 16387dd7cddfSDavid du Colombier uchar cmd10[10], *cmdp, *p; 16397dd7cddfSDavid du Colombier int clen, reqstatus, status; 16407dd7cddfSDavid du Colombier 16417dd7cddfSDavid du Colombier unit = r->unit; 16427dd7cddfSDavid du Colombier if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){ 16437dd7cddfSDavid du Colombier r->status = SDtimeout; 16447dd7cddfSDavid du Colombier return SDtimeout; 16457dd7cddfSDavid du Colombier } 16467dd7cddfSDavid du Colombier drive = ctlr->drive[unit->subno]; 16477dd7cddfSDavid du Colombier 16487dd7cddfSDavid du Colombier /* 16497dd7cddfSDavid du Colombier * Most SCSI commands can be passed unchanged except for 16507dd7cddfSDavid du Colombier * the padding on the end. The few which require munging 16517dd7cddfSDavid du Colombier * are not used internally. Mode select/sense(6) could be 16527dd7cddfSDavid du Colombier * converted to the 10-byte form but it's not worth the 16537dd7cddfSDavid du Colombier * effort. Read/write(6) are easy. 16547dd7cddfSDavid du Colombier */ 16557dd7cddfSDavid du Colombier switch(r->cmd[0]){ 16567dd7cddfSDavid du Colombier case 0x08: /* read */ 16577dd7cddfSDavid du Colombier case 0x0A: /* write */ 16587dd7cddfSDavid du Colombier cmdp = cmd10; 16597dd7cddfSDavid du Colombier memset(cmdp, 0, sizeof(cmd10)); 16607dd7cddfSDavid du Colombier cmdp[0] = r->cmd[0]|0x20; 16617dd7cddfSDavid du Colombier cmdp[1] = r->cmd[1] & 0xE0; 16627dd7cddfSDavid du Colombier cmdp[5] = r->cmd[3]; 16637dd7cddfSDavid du Colombier cmdp[4] = r->cmd[2]; 16647dd7cddfSDavid du Colombier cmdp[3] = r->cmd[1] & 0x0F; 16657dd7cddfSDavid du Colombier cmdp[8] = r->cmd[4]; 16667dd7cddfSDavid du Colombier clen = sizeof(cmd10); 16677dd7cddfSDavid du Colombier break; 16687dd7cddfSDavid du Colombier 16697dd7cddfSDavid du Colombier default: 16707dd7cddfSDavid du Colombier cmdp = r->cmd; 16717dd7cddfSDavid du Colombier clen = r->clen; 16727dd7cddfSDavid du Colombier break; 16737dd7cddfSDavid du Colombier } 16747dd7cddfSDavid du Colombier 16757dd7cddfSDavid du Colombier qlock(drive); 16767dd7cddfSDavid du Colombier retry: 16777dd7cddfSDavid du Colombier drive->write = r->write; 16787dd7cddfSDavid du Colombier drive->data = r->data; 16797dd7cddfSDavid du Colombier drive->dlen = r->dlen; 16807dd7cddfSDavid du Colombier 16817dd7cddfSDavid du Colombier drive->status = 0; 16827dd7cddfSDavid du Colombier drive->error = 0; 16837dd7cddfSDavid du Colombier if(drive->pkt) 16847dd7cddfSDavid du Colombier status = atapktio(drive, cmdp, clen); 16857dd7cddfSDavid du Colombier else 16867dd7cddfSDavid du Colombier status = atagenio(drive, cmdp, clen); 16877dd7cddfSDavid du Colombier if(status == SDretry){ 16887dd7cddfSDavid du Colombier if(DbgDEBUG) 16897dd7cddfSDavid du Colombier print("%s: retry: dma %8.8uX rwm %4.4uX\n", 16907dd7cddfSDavid du Colombier unit->name, drive->dmactl, drive->rwmctl); 16917dd7cddfSDavid du Colombier goto retry; 16927dd7cddfSDavid du Colombier } 16937dd7cddfSDavid du Colombier if(status == SDok){ 16947dd7cddfSDavid du Colombier atasetsense(drive, SDok, 0, 0, 0); 16957dd7cddfSDavid du Colombier if(drive->data){ 16967dd7cddfSDavid du Colombier p = r->data; 16977dd7cddfSDavid du Colombier r->rlen = drive->data - p; 16987dd7cddfSDavid du Colombier } 16997dd7cddfSDavid du Colombier else 17007dd7cddfSDavid du Colombier r->rlen = 0; 17017dd7cddfSDavid du Colombier } 17027dd7cddfSDavid du Colombier else if(status == SDcheck && !(r->flags & SDnosense)){ 17037dd7cddfSDavid du Colombier drive->write = 0; 17047dd7cddfSDavid du Colombier memset(cmd10, 0, sizeof(cmd10)); 17057dd7cddfSDavid du Colombier cmd10[0] = 0x03; 17067dd7cddfSDavid du Colombier cmd10[1] = r->lun<<5; 17077dd7cddfSDavid du Colombier cmd10[4] = sizeof(r->sense)-1; 17087dd7cddfSDavid du Colombier drive->data = r->sense; 17097dd7cddfSDavid du Colombier drive->dlen = sizeof(r->sense)-1; 17107dd7cddfSDavid du Colombier drive->status = 0; 17117dd7cddfSDavid du Colombier drive->error = 0; 17127dd7cddfSDavid du Colombier if(drive->pkt) 17137dd7cddfSDavid du Colombier reqstatus = atapktio(drive, cmd10, 6); 17147dd7cddfSDavid du Colombier else 17157dd7cddfSDavid du Colombier reqstatus = atagenio(drive, cmd10, 6); 17167dd7cddfSDavid du Colombier if(reqstatus == SDok){ 17177dd7cddfSDavid du Colombier r->flags |= SDvalidsense; 17187dd7cddfSDavid du Colombier atasetsense(drive, SDok, 0, 0, 0); 17197dd7cddfSDavid du Colombier } 17207dd7cddfSDavid du Colombier } 17217dd7cddfSDavid du Colombier qunlock(drive); 17227dd7cddfSDavid du Colombier r->status = status; 17237dd7cddfSDavid du Colombier if(status != SDok) 17247dd7cddfSDavid du Colombier return status; 17257dd7cddfSDavid du Colombier 17267dd7cddfSDavid du Colombier /* 17277dd7cddfSDavid du Colombier * Fix up any results. 17287dd7cddfSDavid du Colombier * Many ATAPI CD-ROMs ignore the LUN field completely and 17297dd7cddfSDavid du Colombier * return valid INQUIRY data. Patch the response to indicate 17307dd7cddfSDavid du Colombier * 'logical unit not supported' if the LUN is non-zero. 17317dd7cddfSDavid du Colombier */ 17327dd7cddfSDavid du Colombier switch(cmdp[0]){ 17337dd7cddfSDavid du Colombier case 0x12: /* inquiry */ 17347dd7cddfSDavid du Colombier if((p = r->data) == nil) 17357dd7cddfSDavid du Colombier break; 17367dd7cddfSDavid du Colombier if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05)) 17377dd7cddfSDavid du Colombier p[0] = 0x7F; 17387dd7cddfSDavid du Colombier /*FALLTHROUGH*/ 17397dd7cddfSDavid du Colombier default: 17407dd7cddfSDavid du Colombier break; 17417dd7cddfSDavid du Colombier } 17427dd7cddfSDavid du Colombier 17437dd7cddfSDavid du Colombier return SDok; 17447dd7cddfSDavid du Colombier } 17457dd7cddfSDavid du Colombier 17467dd7cddfSDavid du Colombier static void 17477dd7cddfSDavid du Colombier atainterrupt(Ureg*, void* arg) 17487dd7cddfSDavid du Colombier { 17497dd7cddfSDavid du Colombier Ctlr *ctlr; 17507dd7cddfSDavid du Colombier Drive *drive; 17517dd7cddfSDavid du Colombier int cmdport, len, status; 17527dd7cddfSDavid du Colombier 17537dd7cddfSDavid du Colombier ctlr = arg; 17547dd7cddfSDavid du Colombier 17557dd7cddfSDavid du Colombier ilock(ctlr); 17567dd7cddfSDavid du Colombier if(inb(ctlr->ctlport+As) & Bsy){ 17577dd7cddfSDavid du Colombier iunlock(ctlr); 1758503d3e0aSDavid du Colombier if(DEBUG & DbgBsy) 17597dd7cddfSDavid du Colombier print("IBsy+"); 17607dd7cddfSDavid du Colombier return; 17617dd7cddfSDavid du Colombier } 17627dd7cddfSDavid du Colombier cmdport = ctlr->cmdport; 17637dd7cddfSDavid du Colombier status = inb(cmdport+Status); 17647dd7cddfSDavid du Colombier if((drive = ctlr->curdrive) == nil){ 17657dd7cddfSDavid du Colombier iunlock(ctlr); 17663ff48bf5SDavid du Colombier if((DEBUG & DbgINL) && ctlr->command != Cedd) 17679a747e4fSDavid du Colombier print("Inil%2.2uX+", ctlr->command); 17687dd7cddfSDavid du Colombier return; 17697dd7cddfSDavid du Colombier } 17707dd7cddfSDavid du Colombier 17717dd7cddfSDavid du Colombier if(status & Err) 17727dd7cddfSDavid du Colombier drive->error = inb(cmdport+Error); 17737dd7cddfSDavid du Colombier else switch(drive->command){ 17747dd7cddfSDavid du Colombier default: 17757dd7cddfSDavid du Colombier drive->error = Abrt; 17767dd7cddfSDavid du Colombier break; 17777dd7cddfSDavid du Colombier 17787dd7cddfSDavid du Colombier case Crs: 17797dd7cddfSDavid du Colombier case Crsm: 17807dd7cddfSDavid du Colombier if(!(status & Drq)){ 17817dd7cddfSDavid du Colombier drive->error = Abrt; 17827dd7cddfSDavid du Colombier break; 17837dd7cddfSDavid du Colombier } 17847dd7cddfSDavid du Colombier len = drive->block; 17857dd7cddfSDavid du Colombier if(drive->data+len > drive->limit) 17867dd7cddfSDavid du Colombier len = drive->limit-drive->data; 17877dd7cddfSDavid du Colombier inss(cmdport+Data, drive->data, len/2); 17887dd7cddfSDavid du Colombier drive->data += len; 17897dd7cddfSDavid du Colombier if(drive->data >= drive->limit) 17907dd7cddfSDavid du Colombier ctlr->done = 1; 17917dd7cddfSDavid du Colombier break; 17927dd7cddfSDavid du Colombier 17937dd7cddfSDavid du Colombier case Cws: 17947dd7cddfSDavid du Colombier case Cwsm: 17957dd7cddfSDavid du Colombier len = drive->block; 17967dd7cddfSDavid du Colombier if(drive->data+len > drive->limit) 17977dd7cddfSDavid du Colombier len = drive->limit-drive->data; 17987dd7cddfSDavid du Colombier drive->data += len; 17997dd7cddfSDavid du Colombier if(drive->data >= drive->limit){ 18007dd7cddfSDavid du Colombier ctlr->done = 1; 18017dd7cddfSDavid du Colombier break; 18027dd7cddfSDavid du Colombier } 18037dd7cddfSDavid du Colombier if(!(status & Drq)){ 18047dd7cddfSDavid du Colombier drive->error = Abrt; 18057dd7cddfSDavid du Colombier break; 18067dd7cddfSDavid du Colombier } 18077dd7cddfSDavid du Colombier len = drive->block; 18087dd7cddfSDavid du Colombier if(drive->data+len > drive->limit) 18097dd7cddfSDavid du Colombier len = drive->limit-drive->data; 18107dd7cddfSDavid du Colombier outss(cmdport+Data, drive->data, len/2); 18117dd7cddfSDavid du Colombier break; 18127dd7cddfSDavid du Colombier 18137dd7cddfSDavid du Colombier case Cpkt: 18147dd7cddfSDavid du Colombier atapktinterrupt(drive); 18157dd7cddfSDavid du Colombier break; 18167dd7cddfSDavid du Colombier 18177dd7cddfSDavid du Colombier case Crd: 18187dd7cddfSDavid du Colombier case Cwd: 18197dd7cddfSDavid du Colombier atadmainterrupt(drive, drive->count*drive->secsize); 18207dd7cddfSDavid du Colombier break; 182180ee5cbfSDavid du Colombier 182280ee5cbfSDavid du Colombier case Cstandby: 182380ee5cbfSDavid du Colombier ctlr->done = 1; 182480ee5cbfSDavid du Colombier break; 18257dd7cddfSDavid du Colombier } 18267dd7cddfSDavid du Colombier iunlock(ctlr); 18277dd7cddfSDavid du Colombier 18287dd7cddfSDavid du Colombier if(drive->error){ 18297dd7cddfSDavid du Colombier status |= Err; 18307dd7cddfSDavid du Colombier ctlr->done = 1; 18317dd7cddfSDavid du Colombier } 18327dd7cddfSDavid du Colombier 18337dd7cddfSDavid du Colombier if(ctlr->done){ 18347dd7cddfSDavid du Colombier ctlr->curdrive = nil; 18357dd7cddfSDavid du Colombier drive->status = status; 18367dd7cddfSDavid du Colombier wakeup(ctlr); 18377dd7cddfSDavid du Colombier } 18387dd7cddfSDavid du Colombier } 18397dd7cddfSDavid du Colombier 18407dd7cddfSDavid du Colombier static SDev* 18417dd7cddfSDavid du Colombier atapnp(void) 18427dd7cddfSDavid du Colombier { 18437dd7cddfSDavid du Colombier Ctlr *ctlr; 18447dd7cddfSDavid du Colombier Pcidev *p; 18457dd7cddfSDavid du Colombier SDev *legacy[2], *sdev, *head, *tail; 18464de34a7eSDavid du Colombier int channel, ispc87415, maxio, pi, r, span; 18477dd7cddfSDavid du Colombier 18487dd7cddfSDavid du Colombier legacy[0] = legacy[1] = head = tail = nil; 18497dd7cddfSDavid du Colombier if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){ 18507dd7cddfSDavid du Colombier head = tail = sdev; 18517dd7cddfSDavid du Colombier legacy[0] = sdev; 18527dd7cddfSDavid du Colombier } 18537dd7cddfSDavid du Colombier if(sdev = ataprobe(0x170, 0x374, IrqATA1)){ 18547dd7cddfSDavid du Colombier if(head != nil) 18557dd7cddfSDavid du Colombier tail->next = sdev; 18567dd7cddfSDavid du Colombier else 18577dd7cddfSDavid du Colombier head = sdev; 18587dd7cddfSDavid du Colombier tail = sdev; 18597dd7cddfSDavid du Colombier legacy[1] = sdev; 18607dd7cddfSDavid du Colombier } 18617dd7cddfSDavid du Colombier 18627dd7cddfSDavid du Colombier p = nil; 18637dd7cddfSDavid du Colombier while(p = pcimatch(p, 0, 0)){ 18647dd7cddfSDavid du Colombier /* 18657dd7cddfSDavid du Colombier * Look for devices with the correct class and sub-class 18667dd7cddfSDavid du Colombier * code and known device and vendor ID; add native-mode 18677dd7cddfSDavid du Colombier * channels to the list to be probed, save info for the 18687dd7cddfSDavid du Colombier * compatibility mode channels. 18697dd7cddfSDavid du Colombier * Note that the legacy devices should not be considered 18707dd7cddfSDavid du Colombier * PCI devices by the interrupt controller. 18717dd7cddfSDavid du Colombier * For both native and legacy, save info for busmastering 18727dd7cddfSDavid du Colombier * if capable. 18737dd7cddfSDavid du Colombier * Promise Ultra ATA/66 (PDC20262) appears to 18747dd7cddfSDavid du Colombier * 1) give a sub-class of 'other mass storage controller' 18757dd7cddfSDavid du Colombier * instead of 'IDE controller', regardless of whether it's 18767dd7cddfSDavid du Colombier * the only controller or not; 18777dd7cddfSDavid du Colombier * 2) put 0 in the programming interface byte (probably 18787dd7cddfSDavid du Colombier * as a consequence of 1) above). 1879503d3e0aSDavid du Colombier * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237. 18807dd7cddfSDavid du Colombier */ 188159c21d95SDavid du Colombier if(p->ccrb != 0x01) 188259c21d95SDavid du Colombier continue; 188359c21d95SDavid du Colombier if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80) 18847dd7cddfSDavid du Colombier continue; 18857dd7cddfSDavid du Colombier pi = p->ccrp; 18867dd7cddfSDavid du Colombier ispc87415 = 0; 18874de34a7eSDavid du Colombier maxio = 0; 18884de34a7eSDavid du Colombier span = BMspan; 18897dd7cddfSDavid du Colombier 18907dd7cddfSDavid du Colombier switch((p->did<<16)|p->vid){ 18917dd7cddfSDavid du Colombier default: 18927dd7cddfSDavid du Colombier continue; 18937dd7cddfSDavid du Colombier 18947dd7cddfSDavid du Colombier case (0x0002<<16)|0x100B: /* NS PC87415 */ 18957dd7cddfSDavid du Colombier /* 18967dd7cddfSDavid du Colombier * Disable interrupts on both channels until 18977dd7cddfSDavid du Colombier * after they are probed for drives. 18987dd7cddfSDavid du Colombier * This must be called before interrupts are 18997dd7cddfSDavid du Colombier * enabled because the IRQ may be shared. 19007dd7cddfSDavid du Colombier */ 19017dd7cddfSDavid du Colombier ispc87415 = 1; 19027dd7cddfSDavid du Colombier pcicfgw32(p, 0x40, 0x00000300); 19037dd7cddfSDavid du Colombier break; 19047dd7cddfSDavid du Colombier case (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */ 19057dd7cddfSDavid du Colombier /* 19067dd7cddfSDavid du Colombier * Turn off prefetch. Overkill, but cheap. 19077dd7cddfSDavid du Colombier */ 19087dd7cddfSDavid du Colombier r = pcicfgr32(p, 0x40); 19097dd7cddfSDavid du Colombier r &= ~0x2000; 19107dd7cddfSDavid du Colombier pcicfgw32(p, 0x40, r); 19117dd7cddfSDavid du Colombier break; 19127dd7cddfSDavid du Colombier case (0x4D38<<16)|0x105A: /* Promise PDC20262 */ 1913b7b24591SDavid du Colombier case (0x4D30<<16)|0x105A: /* Promise PDC202xx */ 19146a081dcdSDavid du Colombier case (0x4D68<<16)|0x105A: /* Promise PDC20268 */ 19154c5c8adbSDavid du Colombier case (0x4D69<<16)|0x105A: /* Promise Ultra/133 TX2 */ 1916ca2418feSDavid du Colombier case (0x3373<<16)|0x105A: /* Promise 20378 RAID */ 1917503d3e0aSDavid du Colombier case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */ 191839571b0aSDavid du Colombier case (0x4379<<16)|0x1002: /* ATI 4379 SATA*/ 19194de34a7eSDavid du Colombier case (0x3112<<16)|0x1095: /* SiI 3112 SATA/RAID */ 19204de34a7eSDavid du Colombier maxio = 15; 19214de34a7eSDavid du Colombier span = 8*1024; 19224de34a7eSDavid du Colombier /*FALLTHROUGH*/ 19234de34a7eSDavid du Colombier case (0x3114<<16)|0x1095: /* SiI 3114 SATA/RAID */ 1924fb7f0c93SDavid du Colombier pi = 0x85; 1925fb7f0c93SDavid du Colombier break; 19263806af99SDavid du Colombier case (0x0004<<16)|0x1103: /* HighPoint HPT366 */ 19277dd7cddfSDavid du Colombier pi = 0x85; 1928fb7f0c93SDavid du Colombier /* 1929fb7f0c93SDavid du Colombier * Turn off fast interrupt prediction. 1930fb7f0c93SDavid du Colombier */ 1931fb7f0c93SDavid du Colombier if((r = pcicfgr8(p, 0x51)) & 0x80) 1932fb7f0c93SDavid du Colombier pcicfgw8(p, 0x51, r & ~0x80); 1933fb7f0c93SDavid du Colombier if((r = pcicfgr8(p, 0x55)) & 0x80) 1934fb7f0c93SDavid du Colombier pcicfgw8(p, 0x55, r & ~0x80); 19357dd7cddfSDavid du Colombier break; 19367dd7cddfSDavid du Colombier case (0x0640<<16)|0x1095: /* CMD 640B */ 19377dd7cddfSDavid du Colombier /* 19387dd7cddfSDavid du Colombier * Bugfix code here... 19397dd7cddfSDavid du Colombier */ 19407dd7cddfSDavid du Colombier break; 1941f92e8f2eSDavid du Colombier case (0x7441<<16)|0x1022: /* AMD 768 */ 1942f92e8f2eSDavid du Colombier /* 1943f92e8f2eSDavid du Colombier * Set: 1944f92e8f2eSDavid du Colombier * 0x41 prefetch, postwrite; 1945f92e8f2eSDavid du Colombier * 0x43 FIFO configuration 1/2 and 1/2; 1946f92e8f2eSDavid du Colombier * 0x44 status register read retry; 1947f92e8f2eSDavid du Colombier * 0x46 DMA read and end of sector flush. 1948f92e8f2eSDavid du Colombier */ 1949f92e8f2eSDavid du Colombier r = pcicfgr8(p, 0x41); 1950f92e8f2eSDavid du Colombier pcicfgw8(p, 0x41, r|0xF0); 1951f92e8f2eSDavid du Colombier r = pcicfgr8(p, 0x43); 1952f92e8f2eSDavid du Colombier pcicfgw8(p, 0x43, (r & 0x90)|0x2A); 1953f92e8f2eSDavid du Colombier r = pcicfgr8(p, 0x44); 1954f92e8f2eSDavid du Colombier pcicfgw8(p, 0x44, r|0x08); 1955f92e8f2eSDavid du Colombier r = pcicfgr8(p, 0x46); 1956f92e8f2eSDavid du Colombier pcicfgw8(p, 0x46, (r & 0x0C)|0xF0); 19574c5c8adbSDavid du Colombier /*FALLTHROUGH*/ 1958*dfda52d8SDavid du Colombier case (0x7401<<16)|0x1022: /* AMD 755 Cobra */ 1959*dfda52d8SDavid du Colombier case (0x7409<<16)|0x1022: /* AMD 756 Viper */ 1960*dfda52d8SDavid du Colombier case (0x7410<<16)|0x1022: /* AMD 766 Viper Plus */ 19616f314b92SDavid du Colombier case (0x7469<<16)|0x1022: /* AMD 3111 */ 19624c5c8adbSDavid du Colombier /* 19634c5c8adbSDavid du Colombier * This can probably be lumped in with the 768 above. 19644c5c8adbSDavid du Colombier */ 19654c5c8adbSDavid du Colombier /*FALLTHROUGH*/ 19666a5dc222SDavid du Colombier case (0x209A<<16)|0x1022: /* AMD CS5536 */ 1967ab3dc52fSDavid du Colombier case (0x01BC<<16)|0x10DE: /* nVidia nForce1 */ 1968ab3dc52fSDavid du Colombier case (0x0065<<16)|0x10DE: /* nVidia nForce2 */ 1969ab3dc52fSDavid du Colombier case (0x0085<<16)|0x10DE: /* nVidia nForce2 MCP */ 19704c5c8adbSDavid du Colombier case (0x00D5<<16)|0x10DE: /* nVidia nForce3 */ 1971ab3dc52fSDavid du Colombier case (0x00E5<<16)|0x10DE: /* nVidia nForce3 Pro */ 1972ab3dc52fSDavid du Colombier case (0x0035<<16)|0x10DE: /* nVidia nForce3 MCP */ 1973ab3dc52fSDavid du Colombier case (0x0053<<16)|0x10DE: /* nVidia nForce4 */ 19741066d6deSDavid du Colombier case (0x0054<<16)|0x10DE: /* nVidia nForce4 SATA */ 19751066d6deSDavid du Colombier case (0x0055<<16)|0x10DE: /* nVidia nForce4 SATA */ 19760aa8dc3cSDavid du Colombier case (0x0266<<16)|0x10DE: /* nVidia nForce4 430 SATA */ 1977edc15dd6SDavid du Colombier case (0x0267<<16)|0x10DE: /* nVidia nForce 55 MCP SATA */ 1978edc15dd6SDavid du Colombier case (0x03EC<<16)|0x10DE: /* nVidia nForce 61 MCP SATA */ 1979edc15dd6SDavid du Colombier case (0x0448<<16)|0x10DE: /* nVidia nForce 65 MCP SATA */ 1980edc15dd6SDavid du Colombier case (0x0560<<16)|0x10DE: /* nVidia nForce 69 MCP SATA */ 19814c5c8adbSDavid du Colombier /* 19824c5c8adbSDavid du Colombier * Ditto, although it may have a different base 19834c5c8adbSDavid du Colombier * address for the registers (0x50?). 19844c5c8adbSDavid du Colombier */ 1985ff1040beSDavid du Colombier /*FALLTHROUGH*/ 198639571b0aSDavid du Colombier case (0x1002<<16)|0x4372: /* ATI SB400 */ 1987ff1040beSDavid du Colombier case (0x4376<<16)|0x1002: /* ATI Radeon Xpress 200M */ 1988f92e8f2eSDavid du Colombier break; 19892839d78eSDavid du Colombier case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */ 19902839d78eSDavid du Colombier { 19912839d78eSDavid du Colombier Pcidev *sb; 19922839d78eSDavid du Colombier 19932839d78eSDavid du Colombier sb = pcimatch(nil, 0x1166, 0x0200); 19942839d78eSDavid du Colombier if(sb == nil) 19952839d78eSDavid du Colombier break; 19962839d78eSDavid du Colombier r = pcicfgr32(sb, 0x64); 19972839d78eSDavid du Colombier r &= ~0x2000; 19982839d78eSDavid du Colombier pcicfgw32(sb, 0x64, r); 19992839d78eSDavid du Colombier } 20004de34a7eSDavid du Colombier span = 32*1024; 20012839d78eSDavid du Colombier break; 20021066d6deSDavid du Colombier case (0x5229<<16)|0x10B9: /* ALi M1543 */ 20031066d6deSDavid du Colombier case (0x5288<<16)|0x10B9: /* ALi M5288 SATA */ 20041066d6deSDavid du Colombier /*FALLTHROUGH*/ 200559f4c0c1SDavid du Colombier case (0x5513<<16)|0x1039: /* SiS 962 */ 20067dd7cddfSDavid du Colombier case (0x0646<<16)|0x1095: /* CMD 646 */ 20077dd7cddfSDavid du Colombier case (0x0571<<16)|0x1106: /* VIA 82C686 */ 20087dd7cddfSDavid du Colombier case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */ 20097dd7cddfSDavid du Colombier case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */ 20107dd7cddfSDavid du Colombier case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */ 2011ef9eff0bSDavid du Colombier case (0x2411<<16)|0x8086: /* 82801AA (ICH) */ 2012ef9eff0bSDavid du Colombier case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */ 2013ef9eff0bSDavid du Colombier case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */ 2014ef9eff0bSDavid du Colombier case (0x244B<<16)|0x8086: /* 82801BA (ICH2, High-End) */ 2015ef9eff0bSDavid du Colombier case (0x248A<<16)|0x8086: /* 82801CA (ICH3, Mobile) */ 2016ef9eff0bSDavid du Colombier case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */ 2017e288d156SDavid du Colombier case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */ 2018ef9eff0bSDavid du Colombier case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */ 2019f92e8f2eSDavid du Colombier case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */ 20207c881178SDavid du Colombier case (0x266F<<16)|0x8086: /* 82801FB (ICH6) */ 20212c7f2648SDavid du Colombier case (0x27C4<<16)|0x8086: /* 82801GBM SATA (ICH7) */ 2022e06f534bSDavid du Colombier case (0x27C5<<16)|0x8086: /* 82801GBM SATA AHCI (ICH7) */ 20237dd7cddfSDavid du Colombier break; 20247dd7cddfSDavid du Colombier } 20257dd7cddfSDavid du Colombier 20267dd7cddfSDavid du Colombier for(channel = 0; channel < 2; channel++){ 20277dd7cddfSDavid du Colombier if(pi & (1<<(2*channel))){ 20287dd7cddfSDavid du Colombier sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01, 20297dd7cddfSDavid du Colombier p->mem[1+2*channel].bar & ~0x01, 20307dd7cddfSDavid du Colombier p->intl); 20317dd7cddfSDavid du Colombier if(sdev == nil) 20327dd7cddfSDavid du Colombier continue; 20337dd7cddfSDavid du Colombier 20347dd7cddfSDavid du Colombier ctlr = sdev->ctlr; 20359a747e4fSDavid du Colombier if(ispc87415) { 20367dd7cddfSDavid du Colombier ctlr->ienable = pc87415ienable; 20379a747e4fSDavid du Colombier print("pc87415disable: not yet implemented\n"); 20389a747e4fSDavid du Colombier } 20397dd7cddfSDavid du Colombier 20407dd7cddfSDavid du Colombier if(head != nil) 20417dd7cddfSDavid du Colombier tail->next = sdev; 20427dd7cddfSDavid du Colombier else 20437dd7cddfSDavid du Colombier head = sdev; 20447dd7cddfSDavid du Colombier tail = sdev; 20457dd7cddfSDavid du Colombier ctlr->tbdf = p->tbdf; 20467dd7cddfSDavid du Colombier } 20477dd7cddfSDavid du Colombier else if((sdev = legacy[channel]) == nil) 20487dd7cddfSDavid du Colombier continue; 20497dd7cddfSDavid du Colombier else 20507dd7cddfSDavid du Colombier ctlr = sdev->ctlr; 20517dd7cddfSDavid du Colombier 20527dd7cddfSDavid du Colombier ctlr->pcidev = p; 20534de34a7eSDavid du Colombier ctlr->maxio = maxio; 20544de34a7eSDavid du Colombier ctlr->span = span; 20557dd7cddfSDavid du Colombier if(!(pi & 0x80)) 20567dd7cddfSDavid du Colombier continue; 20577dd7cddfSDavid du Colombier ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8; 20587dd7cddfSDavid du Colombier } 20597dd7cddfSDavid du Colombier } 20607dd7cddfSDavid du Colombier 20619a747e4fSDavid du Colombier if(0){ 20629a747e4fSDavid du Colombier int port; 20639a747e4fSDavid du Colombier ISAConf isa; 20649a747e4fSDavid du Colombier 20659a747e4fSDavid du Colombier /* 20669a747e4fSDavid du Colombier * Hack for PCMCIA drives. 20679a747e4fSDavid du Colombier * This will be tidied once we figure out how the whole 20689a747e4fSDavid du Colombier * removeable device thing is going to work. 20699a747e4fSDavid du Colombier */ 20709a747e4fSDavid du Colombier memset(&isa, 0, sizeof(isa)); 20719a747e4fSDavid du Colombier isa.port = 0x180; /* change this for your machine */ 20729a747e4fSDavid du Colombier isa.irq = 11; /* change this for your machine */ 20739a747e4fSDavid du Colombier 20749a747e4fSDavid du Colombier port = isa.port+0x0C; 20759a747e4fSDavid du Colombier channel = pcmspecial("MK2001MPL", &isa); 20769a747e4fSDavid du Colombier if(channel == -1) 20779a747e4fSDavid du Colombier channel = pcmspecial("SunDisk", &isa); 20789a747e4fSDavid du Colombier if(channel == -1){ 20799a747e4fSDavid du Colombier isa.irq = 10; 20809a747e4fSDavid du Colombier channel = pcmspecial("CF", &isa); 20819a747e4fSDavid du Colombier } 20829a747e4fSDavid du Colombier if(channel == -1){ 20839a747e4fSDavid du Colombier isa.irq = 10; 20849a747e4fSDavid du Colombier channel = pcmspecial("OLYMPUS", &isa); 20859a747e4fSDavid du Colombier } 20869a747e4fSDavid du Colombier if(channel == -1){ 20879a747e4fSDavid du Colombier port = isa.port+0x204; 20889a747e4fSDavid du Colombier channel = pcmspecial("ATA/ATAPI", &isa); 20899a747e4fSDavid du Colombier } 20909a747e4fSDavid du Colombier if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){ 20919a747e4fSDavid du Colombier if(head != nil) 20929a747e4fSDavid du Colombier tail->next = sdev; 20939a747e4fSDavid du Colombier else 20949a747e4fSDavid du Colombier head = sdev; 20959a747e4fSDavid du Colombier } 20969a747e4fSDavid du Colombier } 20977dd7cddfSDavid du Colombier return head; 20987dd7cddfSDavid du Colombier } 20997dd7cddfSDavid du Colombier 21007dd7cddfSDavid du Colombier static SDev* 21017dd7cddfSDavid du Colombier atalegacy(int port, int irq) 21027dd7cddfSDavid du Colombier { 21037dd7cddfSDavid du Colombier return ataprobe(port, port+0x204, irq); 21047dd7cddfSDavid du Colombier } 21057dd7cddfSDavid du Colombier 21067dd7cddfSDavid du Colombier static int 21077dd7cddfSDavid du Colombier ataenable(SDev* sdev) 21087dd7cddfSDavid du Colombier { 21097dd7cddfSDavid du Colombier Ctlr *ctlr; 21109a747e4fSDavid du Colombier char name[32]; 21117dd7cddfSDavid du Colombier 21127dd7cddfSDavid du Colombier ctlr = sdev->ctlr; 21137dd7cddfSDavid du Colombier 21147dd7cddfSDavid du Colombier if(ctlr->bmiba){ 21159a747e4fSDavid du Colombier #define ALIGN (4 * 1024) 21167dd7cddfSDavid du Colombier if(ctlr->pcidev != nil) 21177dd7cddfSDavid du Colombier pcisetbme(ctlr->pcidev); 21182839d78eSDavid du Colombier ctlr->prdt = mallocalign(Nprd*sizeof(Prd), 4, 0, 4*1024); 21197dd7cddfSDavid du Colombier } 21209a747e4fSDavid du Colombier snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); 21217dd7cddfSDavid du Colombier intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); 21227dd7cddfSDavid du Colombier outb(ctlr->ctlport+Dc, 0); 21237dd7cddfSDavid du Colombier if(ctlr->ienable) 21247dd7cddfSDavid du Colombier ctlr->ienable(ctlr); 21257dd7cddfSDavid du Colombier 21267dd7cddfSDavid du Colombier return 1; 21277dd7cddfSDavid du Colombier } 21287dd7cddfSDavid du Colombier 21297dd7cddfSDavid du Colombier static int 21309a747e4fSDavid du Colombier atadisable(SDev *sdev) 21319a747e4fSDavid du Colombier { 21329a747e4fSDavid du Colombier Ctlr *ctlr; 21339a747e4fSDavid du Colombier char name[32]; 21349a747e4fSDavid du Colombier 21359a747e4fSDavid du Colombier ctlr = sdev->ctlr; 21369a747e4fSDavid du Colombier outb(ctlr->ctlport+Dc, Nien); /* disable interrupts */ 21379a747e4fSDavid du Colombier if (ctlr->idisable) 21389a747e4fSDavid du Colombier ctlr->idisable(ctlr); 21399a747e4fSDavid du Colombier snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); 21409a747e4fSDavid du Colombier intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); 21419a747e4fSDavid du Colombier if (ctlr->bmiba) { 21429a747e4fSDavid du Colombier if (ctlr->pcidev) 21439a747e4fSDavid du Colombier pciclrbme(ctlr->pcidev); 21442839d78eSDavid du Colombier free(ctlr->prdt); 21459a747e4fSDavid du Colombier } 21469a747e4fSDavid du Colombier return 0; 21479a747e4fSDavid du Colombier } 21489a747e4fSDavid du Colombier 21499a747e4fSDavid du Colombier static int 21507dd7cddfSDavid du Colombier atarctl(SDunit* unit, char* p, int l) 21517dd7cddfSDavid du Colombier { 21527dd7cddfSDavid du Colombier int n; 21537dd7cddfSDavid du Colombier Ctlr *ctlr; 21547dd7cddfSDavid du Colombier Drive *drive; 21557dd7cddfSDavid du Colombier 21567dd7cddfSDavid du Colombier if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) 21577dd7cddfSDavid du Colombier return 0; 21587dd7cddfSDavid du Colombier drive = ctlr->drive[unit->subno]; 21597dd7cddfSDavid du Colombier 21607dd7cddfSDavid du Colombier qlock(drive); 21617dd7cddfSDavid du Colombier n = snprint(p, l, "config %4.4uX capabilities %4.4uX", 21627dd7cddfSDavid du Colombier drive->info[Iconfig], drive->info[Icapabilities]); 21637dd7cddfSDavid du Colombier if(drive->dma) 21647dd7cddfSDavid du Colombier n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX", 21657dd7cddfSDavid du Colombier drive->dma, drive->dmactl); 21667dd7cddfSDavid du Colombier if(drive->rwm) 21677dd7cddfSDavid du Colombier n += snprint(p+n, l-n, " rwm %ud rwmctl %ud", 21687dd7cddfSDavid du Colombier drive->rwm, drive->rwmctl); 21696a081dcdSDavid du Colombier if(drive->flags&Lba48) 21706a081dcdSDavid du Colombier n += snprint(p+n, l-n, " lba48always %s", 21716a081dcdSDavid du Colombier (drive->flags&Lba48always) ? "on" : "off"); 21727dd7cddfSDavid du Colombier n += snprint(p+n, l-n, "\n"); 21736a081dcdSDavid du Colombier if(drive->sectors){ 21746a081dcdSDavid du Colombier n += snprint(p+n, l-n, "geometry %lld %d", 21756a081dcdSDavid du Colombier drive->sectors, drive->secsize); 21767dd7cddfSDavid du Colombier if(drive->pkt == 0) 21777dd7cddfSDavid du Colombier n += snprint(p+n, l-n, " %d %d %d", 21787dd7cddfSDavid du Colombier drive->c, drive->h, drive->s); 21797dd7cddfSDavid du Colombier n += snprint(p+n, l-n, "\n"); 21807dd7cddfSDavid du Colombier } 21817dd7cddfSDavid du Colombier qunlock(drive); 21827dd7cddfSDavid du Colombier 21837dd7cddfSDavid du Colombier return n; 21847dd7cddfSDavid du Colombier } 21857dd7cddfSDavid du Colombier 21867dd7cddfSDavid du Colombier static int 21877dd7cddfSDavid du Colombier atawctl(SDunit* unit, Cmdbuf* cb) 21887dd7cddfSDavid du Colombier { 218980ee5cbfSDavid du Colombier int period; 21907dd7cddfSDavid du Colombier Ctlr *ctlr; 21917dd7cddfSDavid du Colombier Drive *drive; 21927dd7cddfSDavid du Colombier 21937dd7cddfSDavid du Colombier if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) 21947dd7cddfSDavid du Colombier return 0; 21957dd7cddfSDavid du Colombier drive = ctlr->drive[unit->subno]; 21967dd7cddfSDavid du Colombier 21977dd7cddfSDavid du Colombier qlock(drive); 21987dd7cddfSDavid du Colombier if(waserror()){ 21997dd7cddfSDavid du Colombier qunlock(drive); 22007dd7cddfSDavid du Colombier nexterror(); 22017dd7cddfSDavid du Colombier } 22027dd7cddfSDavid du Colombier 22037dd7cddfSDavid du Colombier /* 22047dd7cddfSDavid du Colombier * Dma and rwm control is passive at the moment, 22057dd7cddfSDavid du Colombier * i.e. it is assumed that the hardware is set up 22067dd7cddfSDavid du Colombier * correctly already either by the BIOS or when 22077dd7cddfSDavid du Colombier * the drive was initially identified. 22087dd7cddfSDavid du Colombier */ 22097dd7cddfSDavid du Colombier if(strcmp(cb->f[0], "dma") == 0){ 22107dd7cddfSDavid du Colombier if(cb->nf != 2 || drive->dma == 0) 22117dd7cddfSDavid du Colombier error(Ebadctl); 22127dd7cddfSDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 22137dd7cddfSDavid du Colombier drive->dmactl = drive->dma; 22147dd7cddfSDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 22157dd7cddfSDavid du Colombier drive->dmactl = 0; 22167dd7cddfSDavid du Colombier else 22177dd7cddfSDavid du Colombier error(Ebadctl); 22187dd7cddfSDavid du Colombier } 22197dd7cddfSDavid du Colombier else if(strcmp(cb->f[0], "rwm") == 0){ 22207dd7cddfSDavid du Colombier if(cb->nf != 2 || drive->rwm == 0) 22217dd7cddfSDavid du Colombier error(Ebadctl); 22227dd7cddfSDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 22237dd7cddfSDavid du Colombier drive->rwmctl = drive->rwm; 22247dd7cddfSDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 22257dd7cddfSDavid du Colombier drive->rwmctl = 0; 22267dd7cddfSDavid du Colombier else 22277dd7cddfSDavid du Colombier error(Ebadctl); 22287dd7cddfSDavid du Colombier } 222980ee5cbfSDavid du Colombier else if(strcmp(cb->f[0], "standby") == 0){ 223080ee5cbfSDavid du Colombier switch(cb->nf){ 223180ee5cbfSDavid du Colombier default: 223280ee5cbfSDavid du Colombier error(Ebadctl); 223380ee5cbfSDavid du Colombier case 2: 223480ee5cbfSDavid du Colombier period = strtol(cb->f[1], 0, 0); 223580ee5cbfSDavid du Colombier if(period && (period < 30 || period > 240*5)) 223680ee5cbfSDavid du Colombier error(Ebadctl); 223780ee5cbfSDavid du Colombier period /= 5; 223880ee5cbfSDavid du Colombier break; 223980ee5cbfSDavid du Colombier } 224080ee5cbfSDavid du Colombier if(atastandby(drive, period) != SDok) 224180ee5cbfSDavid du Colombier error(Ebadctl); 224280ee5cbfSDavid du Colombier } 22436a081dcdSDavid du Colombier else if(strcmp(cb->f[0], "lba48always") == 0){ 22446a081dcdSDavid du Colombier if(cb->nf != 2 || !(drive->flags&Lba48)) 22456a081dcdSDavid du Colombier error(Ebadctl); 22466a081dcdSDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 22476a081dcdSDavid du Colombier drive->flags |= Lba48always; 22486a081dcdSDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 22496a081dcdSDavid du Colombier drive->flags &= ~Lba48always; 22506a081dcdSDavid du Colombier else 22516a081dcdSDavid du Colombier error(Ebadctl); 22526a081dcdSDavid du Colombier } 22537dd7cddfSDavid du Colombier else 22547dd7cddfSDavid du Colombier error(Ebadctl); 22557dd7cddfSDavid du Colombier qunlock(drive); 22567dd7cddfSDavid du Colombier poperror(); 22577dd7cddfSDavid du Colombier 22587dd7cddfSDavid du Colombier return 0; 22597dd7cddfSDavid du Colombier } 22607dd7cddfSDavid du Colombier 22617dd7cddfSDavid du Colombier SDifc sdataifc = { 22627dd7cddfSDavid du Colombier "ata", /* name */ 22637dd7cddfSDavid du Colombier 22647dd7cddfSDavid du Colombier atapnp, /* pnp */ 22657dd7cddfSDavid du Colombier atalegacy, /* legacy */ 22667dd7cddfSDavid du Colombier ataenable, /* enable */ 22679a747e4fSDavid du Colombier atadisable, /* disable */ 22687dd7cddfSDavid du Colombier 22697dd7cddfSDavid du Colombier scsiverify, /* verify */ 22707dd7cddfSDavid du Colombier scsionline, /* online */ 22717dd7cddfSDavid du Colombier atario, /* rio */ 22727dd7cddfSDavid du Colombier atarctl, /* rctl */ 22737dd7cddfSDavid du Colombier atawctl, /* wctl */ 22747dd7cddfSDavid du Colombier 22757dd7cddfSDavid du Colombier scsibio, /* bio */ 22769a747e4fSDavid du Colombier ataprobew, /* probe */ 22779a747e4fSDavid du Colombier ataclear, /* clear */ 2278d649fdd7SDavid du Colombier atastat, /* rtopctl */ 2279d649fdd7SDavid du Colombier nil, /* wtopctl */ 22807dd7cddfSDavid du Colombier }; 2281