Kernel - Reworked PCI API to be cleaner
[tpg/acess2.git] / Modules / Storage / ATA / io.c
index 4193377..368c310 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Disk Input/Output control
  */
-#define DEBUG  1
+#define DEBUG  0
 #include <acess.h>
 #include <modules.h>   // Needed for error codes
 #include <drv_pci.h>
 // === MACROS ===
 #define IO_DELAY()     do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
 
+// === Constants ===
+#define        IDE_PRI_BASE    0x1F0
+#define IDE_PRI_CTRL   0x3F6
+#define        IDE_SEC_BASE    0x170
+#define IDE_SEC_CTRL   0x376
+
+#define        IDE_PRDT_LAST   0x8000
+/**
+ \enum HddControls
+ \brief Commands to be sent to HDD_CMD
+*/
+enum HddControls {
+       HDD_PIO_R28 = 0x20,
+       HDD_PIO_R48 = 0x24,
+       HDD_DMA_R48 = 0x25,
+       HDD_PIO_W28 = 0x30,
+       HDD_PIO_W48 = 0x34,
+       HDD_DMA_W48 = 0x35,
+       HDD_DMA_R28 = 0xC8,
+       HDD_DMA_W28 = 0xCA,
+       HDD_IDENTIFY = 0xEC
+};
+
 // === TYPES ===
 /**
  * \brief PRDT Entry
@@ -42,9 +65,9 @@ typedef struct
        Uint16  ValidExtData;   // 54
        Uint16  Unused5[5];      // 59
        Uint16  SizeOfRWMultiple;       // 60
-       Uint32  Sectors28;      // 62
+       Uint32  Sectors28;      // LBA 28 Sector Count
        Uint16  Unused6[100-62];
-       Uint64  Sectors48;
+       Uint64  Sectors48;      // LBA 48 Sector Count
        Uint16  Unused7[256-104];
 } __attribute__ ((packed))     tIdentify;
 
@@ -60,21 +83,28 @@ void        ATA_IRQHandlerPri(int UNUSED(IRQ));
 void   ATA_IRQHandlerSec(int UNUSED(IRQ));
 // Controller IO
 Uint8  ATA_int_BusMasterReadByte(int Ofs);
+Uint32 ATA_int_BusMasterReadDWord(int Ofs);
 void   ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value);
 void   ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value);
 
 // === GLOBALS ===
-Uint32 gATA_BusMasterBase = 0;
-Uint8  *gATA_BusMasterBasePtr = 0;
+// - BusMaster IO Addresses
+Uint32 gATA_BusMasterBase;     //!< True Address (IO/MMIO)
+Uint8  *gATA_BusMasterBasePtr; //!< Paging Mapped MMIO (If needed)
+// - IRQs
  int   gATA_IRQPri = 14;
  int   gATA_IRQSec = 15;
- int   giaATA_ControllerLock[2] = {0}; //!< Spinlocks for each controller
-Uint8  gATA_Buffers[2][(MAX_DMA_SECTORS+0xFFF)&~0xFFF] __attribute__ ((section(".padata")));
 volatile int   gaATA_IRQs[2] = {0};
+// - Locks to avoid tripping
+tMutex glaATA_ControllerLock[2];
+// - Buffers!
+Uint8  gATA_Buffers[2][(MAX_DMA_SECTORS+0xFFF)&~0xFFF] __attribute__ ((section(".padata")));
+// - PRDTs
 tPRDT_Ent      gATA_PRDTs[2] = {
        {0, 512, IDE_PRDT_LAST},
        {0, 512, IDE_PRDT_LAST}
 };
+tPAddr gaATA_PRDT_PAddrs[2];
 
 // === CODE ===
 /**
@@ -83,14 +113,13 @@ tPRDT_Ent  gATA_PRDTs[2] = {
 int ATA_SetupIO(void)
 {
         int    ent;
-       tPAddr  addr;
 
        ENTER("");
 
        // Get IDE Controller's PCI Entry
        ent = PCI_GetDeviceByClass(0x0101, 0xFFFF, -1);
        LOG("ent = %i", ent);
-       gATA_BusMasterBase = PCI_GetBAR4( ent );
+       gATA_BusMasterBase = PCI_GetBAR(ent, 4);
        if( gATA_BusMasterBase == 0 ) {
                Log_Warning("ATA", "It seems that there is no Bus Master Controller on this machine. Get one");
                // TODO: Use PIO mode instead
@@ -98,6 +127,9 @@ int ATA_SetupIO(void)
                return MODULE_ERR_NOTNEEDED;
        }
        
+       LOG("BAR5 = 0x%x", PCI_GetBAR(ent, 5));
+       LOG("IRQ = %i", PCI_GetIRQ(ent));
+       
        // Map memory
        if( !(gATA_BusMasterBase & 1) )
        {
@@ -121,22 +153,34 @@ int ATA_SetupIO(void)
 
        LOG("gATA_PRDTs = {PBufAddr: 0x%x, PBufAddr: 0x%x}", gATA_PRDTs[0].PBufAddr, gATA_PRDTs[1].PBufAddr);
 
-       addr = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[0] );
-       LOG("addr = 0x%x", addr);
-       ATA_int_BusMasterWriteDWord(4, addr);
-       addr = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[1] );
-       LOG("addr = 0x%x", addr);
-       ATA_int_BusMasterWriteDWord(12, addr);
+       gaATA_PRDT_PAddrs[0] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[0] );
+       LOG("gaATA_PRDT_PAddrs[0] = 0x%x", gaATA_PRDT_PAddrs[0]);
+       ATA_int_BusMasterWriteDWord(4, gaATA_PRDT_PAddrs[0]);
+       
+       gaATA_PRDT_PAddrs[1] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[1] );
+       LOG("gaATA_PRDT_PAddrs[1] = 0x%x", gaATA_PRDT_PAddrs[1]);
+       ATA_int_BusMasterWriteDWord(12, gaATA_PRDT_PAddrs[1]);
 
        // Enable controllers
        outb(IDE_PRI_BASE+1, 1);
        outb(IDE_SEC_BASE+1, 1);
+       
+       // Make sure interrupts are ACKed
+       ATA_int_BusMasterWriteByte(2, 0x4);
+       ATA_int_BusMasterWriteByte(10, 0x4);
 
        // return
        LEAVE('i', MODULE_ERR_OK);
        return MODULE_ERR_OK;
 }
 
+/**
+ * \brief Get the size (in sectors) of a disk
+ * \param Disk Disk to get size of
+ * \return Number of sectors reported
+ * 
+ * Does an ATA IDENTIFY
+ */
 Uint64 ATA_GetDiskSize(int Disk)
 {
        union {
@@ -151,9 +195,9 @@ Uint64 ATA_GetDiskSize(int Disk)
        base = ATA_GetBasePort( Disk );
 
        // Send Disk Selector
-       if(Disk == 1 || Disk == 3)
+       if(Disk & 1)    // Slave
                outb(base+6, 0xB0);
-       else
+       else    // Master
                outb(base+6, 0xA0);
        IO_DELAY();
        
@@ -165,6 +209,7 @@ Uint64 ATA_GetDiskSize(int Disk)
        }
        
        // Check for the controller
+       // - Write to two RW ports and attempt to read back
        outb(base+0x02, 0x66);
        outb(base+0x03, 0xFF);
        if(inb(base+0x02) != 0x66 || inb(base+0x03) != 0xFF) {
@@ -173,8 +218,8 @@ Uint64 ATA_GetDiskSize(int Disk)
                return 0;
        }
 
-       // Send IDENTIFY
-       outb(base+7, 0xEC);
+       // Send ATA IDENTIFY
+       outb(base+7, HDD_IDENTIFY);
        IO_DELAY();
        val = inb(base+7);      // Read status
        LOG("val = 0x%02x", val);
@@ -183,9 +228,15 @@ Uint64 ATA_GetDiskSize(int Disk)
                return 0;       // Disk does not exist
        }
 
-       // Poll until BSY clears and DRQ sets or ERR is set
-       while( ((val & 0x80) || !(val & 0x08)) && !(val & 1))
+       // Poll until BSY clears or ERR is set
+       // TODO: Timeout?
+       while( (val & 0x80) && !(val & 1) )
                val = inb(base+7);
+       LOG("BSY unset (0x%x)", val);
+       // and, wait for DRQ to set
+       while( !(val & 0x08) && !(val & 1))
+               val = inb(base+7);
+       LOG("DRQ set (0x%x)", val);
 
        // Check for an error
        if(val & 1) {
@@ -198,10 +249,14 @@ Uint64 ATA_GetDiskSize(int Disk)
                data.buf[i] = inw(base);
 
        // Return the disk size
-       if(data.identify.Sectors48 != 0)
+       if(data.identify.Sectors48 != 0) {
+               LEAVE('X', data.identify.Sectors48);
                return data.identify.Sectors48;
-       else
+       }
+       else {
+               LEAVE('x', data.identify.Sectors28);
                return data.identify.Sectors28;
+       }
 }
 
 /**
@@ -220,26 +275,33 @@ Uint16 ATA_GetBasePort(int Disk)
 
 /**
  * \fn int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
+ * \return Boolean Failure
  */
 int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
 {
         int    cont = (Disk>>1)&1;     // Controller ID
         int    disk = Disk & 1;
        Uint16  base;
+       Sint64  timeoutTime;
        Uint8   val;
 
        ENTER("iDisk XAddress iCount pBuffer", Disk, Address, Count, Buffer);
 
        // Check if the count is small enough
        if(Count > MAX_DMA_SECTORS) {
-               Warning("Passed too many sectors for a bulk DMA read (%i > %i)",
+               Log_Warning("ATA", "Passed too many sectors for a bulk DMA read (%i > %i)",
                        Count, MAX_DMA_SECTORS);
                LEAVE('i');
-               return 0;
+               return 1;
        }
+       
+       // Hack to make debug hexdump noticable
+       #if 1
+       memset(Buffer, 0xFF, Count*SECTOR_SIZE);
+       #endif
 
        // Get exclusive access to the disk controller
-       LOCK( &giaATA_ControllerLock[ cont ] );
+       Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
 
        // Set Size
        gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
@@ -250,10 +312,24 @@ int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
        // Reset IRQ Flag
        gaATA_IRQs[cont] = 0;
 
+       #if 1
+       if( cont == 0 ) {
+               outb(base+IDE_PRI_CTRL, 4);
+               IO_DELAY();
+               outb(base+IDE_PRI_CTRL, 0);
+       }
+       else {
+               outb(base+IDE_SEC_CTRL, 4);
+               IO_DELAY();
+               outb(base+IDE_SEC_CTRL, 0);
+       }
+       #endif
+
        // Set up transfer
        if( Address > 0x0FFFFFFF )      // Use LBA48
        {
                outb(base+0x6, 0x40 | (disk << 4));
+               IO_DELAY();
                outb(base+0x2, 0 >> 8); // Upper Sector Count
                outb(base+0x3, Address >> 24);  // Low 2 Addr
                outb(base+0x4, Address >> 28);  // Mid 2 Addr
@@ -261,16 +337,23 @@ int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
        }
        else
        {
-               outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); // Magic, Disk, High addr
+               // Magic, Disk, High Address nibble
+               outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F));
+               //outb(base+0x06, 0xA0 | (disk << 4) | ((Address >> 24) & 0x0F));
+               IO_DELAY();
        }
 
-       outb(base+0x01, 0x01);  //?
-       outb(base+0x02, (Uint8) Count);         // Sector Count
-       outb(base+0x03, (Uint8) Address);               // Low Addr
-       outb(base+0x04, (Uint8) (Address >> 8));        // Middle Addr
-       outb(base+0x05, (Uint8) (Address >> 16));       // High Addr
+       //outb(base+0x01, 0x01);        //?
+       outb(base+0x02, Count & 0xFF);          // Sector Count
+       outb(base+0x03, Address & 0xFF);                // Low Addr
+       outb(base+0x04, (Address >> 8) & 0xFF); // Middle Addr
+       outb(base+0x05, (Address >> 16) & 0xFF);        // High Addr
 
        LOG("Starting Transfer");
+       
+       // HACK: Ensure the PRDT is reset
+       ATA_int_BusMasterWriteDWord(cont*8+4, gaATA_PRDT_PAddrs[cont]);
+               
        LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes);
        if( Address > 0x0FFFFFFF )
                outb(base+0x07, HDD_DMA_R48);   // Read Command (LBA48)
@@ -278,28 +361,47 @@ int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
                outb(base+0x07, HDD_DMA_R28);   // Read Command (LBA28)
 
        // Start transfer
-       ATA_int_BusMasterWriteByte( cont << 3, 9 );     // Read and start
+       ATA_int_BusMasterWriteByte( cont * 8, 9 );      // Read and start
 
        // Wait for transfer to complete
-       while( gaATA_IRQs[cont] == 0 )  Threads_Yield();
+       timeoutTime = now() + ATA_TIMEOUT;
+       while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
+       {
+               HALT();
+       }
 
        // Complete Transfer
-       ATA_int_BusMasterWriteByte( cont << 3, 8 );     // Read and stop
+       ATA_int_BusMasterWriteByte( cont * 8, 8 );      // Read and stop
 
        val = inb(base+0x7);
-       LOG("Status byte = 0x%02x", val);
-
-       LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes);
-       LOG("Transfer Completed & Acknowledged");
-
-       // Copy to destination buffer
-       memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
-
-       // Release controller lock
-       RELEASE( &giaATA_ControllerLock[ cont ] );
+       LOG("Status byte = 0x%02x, Controller Status = 0x%02x",
+               val, ATA_int_BusMasterReadByte(cont * 8 + 2));
+
+       if( gaATA_IRQs[cont] == 0 ) {
+               
+               #if 1
+               Debug_HexDump("ATA", Buffer, 512);
+               #endif
+               
+               // Release controller lock
+               Mutex_Release( &glaATA_ControllerLock[ cont ] );
+               Log_Warning("ATA",
+                       "Read timeout on disk %i (Reading sector 0x%llx)",
+                       Disk, Address);
+               // Return error
+               LEAVE('i', 1);
+               return 1;
+       }
+       else {
+               LOG("Transfer Completed & Acknowledged");
+               // Copy to destination buffer
+               memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
+               // Release controller lock
+               Mutex_Release( &glaATA_ControllerLock[ cont ] );
 
-       LEAVE('i', 1);
-       return 1;
+               LEAVE('i', 0);
+               return 0;
+       }
 }
 
 /**
@@ -309,18 +411,20 @@ int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
  * \param Address      LBA of first sector
  * \param Count        Number of sectors to write (must be >= \a MAX_DMA_SECTORS)
  * \param Buffer       Source buffer for data
+ * \return Boolean Failure
  */
 int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
 {
         int    cont = (Disk>>1)&1;     // Controller ID
         int    disk = Disk & 1;
        Uint16  base;
+       Sint64  timeoutTime;
 
        // Check if the count is small enough
-       if(Count > MAX_DMA_SECTORS)     return 0;
+       if(Count > MAX_DMA_SECTORS)     return 1;
 
        // Get exclusive access to the disk controller
-       LOCK( &giaATA_ControllerLock[ cont ] );
+       Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
 
        // Set Size
        gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
@@ -328,6 +432,9 @@ int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
        // Get Port Base
        base = ATA_GetBasePort(Disk);
 
+       // Reset IRQ Flag
+       gaATA_IRQs[cont] = 0;
+       
        // Set up transfer
        outb(base+0x01, 0x00);
        if( Address > 0x0FFFFFFF )      // Use LBA48
@@ -340,7 +447,8 @@ int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
        }
        else
        {
-               outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); //Disk,Magic,High addr
+               // Magic, Disk, High Address nibble
+               outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F));
        }
 
        outb(base+0x02, (Uint8) Count);         // Sector Count
@@ -352,9 +460,6 @@ int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
        else
                outb(base+0x07, HDD_DMA_W28);   // Write Command (LBA28)
 
-       // Reset IRQ Flag
-       gaATA_IRQs[cont] = 0;
-
        // Copy to output buffer
        memcpy( gATA_Buffers[cont], Buffer, Count*SECTOR_SIZE );
 
@@ -362,15 +467,25 @@ int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
        ATA_int_BusMasterWriteByte( cont << 3, 1 );     // Write and start
 
        // Wait for transfer to complete
-       while( gaATA_IRQs[cont] == 0 )  Threads_Yield();
+       timeoutTime = now() + ATA_TIMEOUT;
+       while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
+       {
+               HALT();
+       }
 
        // Complete Transfer
        ATA_int_BusMasterWriteByte( cont << 3, 0 );     // Write and stop
 
-       // Release controller lock
-       RELEASE( &giaATA_ControllerLock[ cont ] );
-
-       return 1;
+       // If the IRQ is unset, return error
+       if( gaATA_IRQs[cont] == 0 ) {
+               // Release controller lock
+               Mutex_Release( &glaATA_ControllerLock[ cont ] );
+               return 1;       // Error
+       }
+       else {
+               Mutex_Release( &glaATA_ControllerLock[ cont ] );
+               return 0;
+       }
 }
 
 /**
@@ -420,6 +535,18 @@ Uint8 ATA_int_BusMasterReadByte(int Ofs)
                return *(Uint8*)(gATA_BusMasterBasePtr + Ofs);
 }
 
+/**
+ * \brief Read an 32-bit value from a Bus Master register
+ * \param Ofs  Register offset
+ */
+Uint32 ATA_int_BusMasterReadDWord(int Ofs)
+{
+       if( gATA_BusMasterBase & 1 )
+               return ind( (gATA_BusMasterBase & ~1) + Ofs );
+       else
+               return *(Uint32*)(gATA_BusMasterBasePtr + Ofs);
+}
+
 /**
  * \brief Writes a byte to a Bus Master Register
  * \param Ofs  Register Offset

UCC git Repository :: git.ucc.asn.au