Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

# ata.c

Go to the documentation of this file.

    00001 /*! \file ata.c \brief IDE-ATA hard disk interface driver. */
    00002 //*****************************************************************************
    00003 //
    00004 // File Name    : 'ata.c'
    00005 // Title        : IDE-ATA interface driver for hard disks
    00006 // Author       : Pascal Stang
    00007 // Date         : 11/22/2000
    00008 // Revised      : 4/2/2004
    00009 // Version      : 0.1
    00010 // Target MCU   : any
    00011 // Editor Tabs  : 4
    00012 //
    00013 // NOTE: This code is currently below version 1.0, and therefore is considered
    00014 // to be lacking in some functionality or documentation, or may not be fully
    00015 // tested.  Nonetheless, you can expect most functions to work.
    00016 //
    00017 // This code is distributed under the GNU Public License
    00018 //      which can be found at http://www.gnu.org/licenses/gpl.txt
    00019 //
    00020 //*****************************************************************************
    00021
    00022 #include "global.h"
    00023 #include "timer.h"
    00024 #include "rprintf.h"
    00025 #include "debug.h"
    00026
    00027 #include "atadev.h"
    00028 #include "ata.h"
    00029
    00030 // global variables
    00031 unsigned char AtaBuffer[0x200];
    00032
    00033 // functions
    00034 void ataInit(DiskInfo_t* disk, DevBlock_t ataif, unsigned char driveno)
    00035 {
    00036     // set drive interface
    00037     disk->ataif = ataif;
    00038     // set drive number (0=master, 1=slave)
    00039     disk->driveno = driveno;
    00040     // initialize interface
    00041     disk->ataif.Init();
    00042 }
    00043
    00044 void ataDriveInit(DiskInfo_t* disk)
    00045 {
    00046     u08 i;
    00047     //unsigned char* buffer = (unsigned char*)SECTOR_BUFFER_ADDR;
    00048     unsigned char* buffer = AtaBuffer;
    00049
    00050 //  rprintfProgStrM("Init      :"); rprintfu32((unsigned int)disk->ataif.Init); rprintfCRLF();
    00051 //  rprintfProgStrM("ReadReg   :"); rprintfu32((unsigned int)disk->ataif.ReadReg); rprintfCRLF();
    00052 //  rprintfProgStrM("WriteReg  :"); rprintfu32((unsigned int)disk->ataif.WriteReg); rprintfCRLF();
    00053 //  rprintfProgStrM("ReadBlock :"); rprintfu32((unsigned int)disk->ataif.ReadBlock); rprintfCRLF();
    00054 //  rprintfProgStrM("WriteBlock:"); rprintfu32((unsigned int)disk->ataif.WriteBlock); rprintfCRLF();
    00055
    00056     // read drive identity
    00057     rprintfProgStrM("\r\nScanning IDE interface...\r\n");
    00058     // Wait for drive to be ready
    00059     ataStatusWait(disk, ATA_SR_BSY, ATA_SR_BSY);
    00060     // issue identify command
    00061     disk->ataif.WriteReg(ATA_REG_CMDSTATUS1, 0xEC);
    00062     // wait for drive to request data transfer
    00063     ataStatusWait(disk, ATA_SR_DRQ, ATA_SR_DRQ);
    00064     timerPause(20);
    00065     // read in the data
    00066     disk->ataif.ReadBlock(buffer, 512);
    00067
    00068     // set local drive info parameters
    00069     disk->cylinders =       *( ((unsigned short*) buffer) + ATA_IDENT_CYLINDERS );
    00070     disk->heads =           *( ((unsigned short*) buffer) + ATA_IDENT_HEADS );
    00071     disk->sectors =         *( ((unsigned short*) buffer) + ATA_IDENT_SECTORS );
    00072     disk->LBAsupport =      *( ((unsigned short*) buffer) + ATA_IDENT_FIELDVALID );
    00073     disk->sizeinsectors =   *( ((unsigned long*) (buffer + ATA_IDENT_LBASECTORS*2)) );
    00074
    00075     // copy model string
    00076     for(i=0; i<40; i+=2)
    00077     {
    00078         // correct for byte order
    00079         disk->model[i  ] = buffer[(ATA_IDENT_MODEL*2) + i + 1];
    00080         disk->model[i+1] = buffer[(ATA_IDENT_MODEL*2) + i    ];
    00081     }
    00082     // terminate string
    00083     disk->model[40] = 0;
    00084
    00085     // process and print info
    00086     if(disk->LBAsupport)
    00087     {
    00088         // LBA support
    00089         rprintf("Drive 0: %dMB ", disk->sizeinsectors/(1000000/512) );
    00090         rprintfProgStrM("LBA mode -- MODEL: ");
    00091     }
    00092     else
    00093     {
    00094         // CHS, no LBA support
    00095         // calculate drive size
    00096         disk->sizeinsectors = (unsigned long) disk->cylinders*disk->heads*disk->sectors;
    00097         rprintf("Drive 0: %dMB ", disk->sizeinsectors/(1000000/512) );
    00098         rprintf("CHS mode C=%d H=%d S=%d -- MODEL: ", disk->cylinders, disk->heads, disk->sectors );
    00099     }
    00100     // print model information
    00101     rprintfStr(disk->model); rprintfCRLF();
    00102
    00103     // initialize local disk parameters
    00104     //ataDriveInfo.cylinders = ATA_DISKPARM_CLYS;
    00105     //ataDriveInfo.heads = ATA_DISKPARM_HEADS;
    00106     //ataDriveInfo.sectors = ATA_DISKPARM_SECTORS;
    00107 }
    00108
    00109 void ataDiskErr(DiskInfo_t* disk)
    00110 {
    00111     unsigned char b;
    00112
    00113     b = disk->ataif.ReadReg(ATA_REG_ERROR);
    00114     rprintfProgStrM("ATA Error: ");
    00115     rprintfu08(b);
    00116     rprintfCRLF();
    00117 }
    00118
    00119 void ataSetDrivePowerMode(DiskInfo_t* disk, u08 mode, u08 timeout)
    00120 {
    00121     // select drive
    00122     ataDriveSelect(disk);
    00123     // Wait for drive to be ready
    00124     ataStatusWait(disk, ATA_SR_BSY, ATA_SR_BSY);
    00125
    00126     // set mode
    00127     switch(mode)
    00128     {
    00129     case ATA_DISKMODE_SPINDOWN:
    00130         disk->ataif.WriteReg(ATA_REG_CMDSTATUS1, ATA_CMD_SPINDOWN);
    00131         break;
    00132     case ATA_DISKMODE_SPINUP:
    00133         disk->ataif.WriteReg(ATA_REG_CMDSTATUS1, ATA_CMD_SPINUP);
    00134         break;
    00135     case ATA_DISKMODE_SETTIMEOUT:
    00136         disk->ataif.WriteReg(ATA_REG_SECCOUNT, timeout);
    00137         disk->ataif.WriteReg(ATA_REG_CMDSTATUS1, ATA_CMD_IDLE_5SU);
    00138         break;
    00139     case ATA_DISKMODE_SLEEP:
    00140         disk->ataif.WriteReg(ATA_REG_CMDSTATUS1, ATA_CMD_SLEEP);
    00141         break;
    00142     default:
    00143         break;
    00144     }
    00145 }
    00146
    00147 u08 ataStatusWait(DiskInfo_t* disk, u08 mask, u08 waitStatus)
    00148 {
    00149     register u08 status;
    00150
    00151     delay(100);
    00152
    00153     // wait for desired status
    00154     while( ((status = disk->ataif.ReadReg(ATA_REG_CMDSTATUS1)) & mask) == waitStatus );
    00155
    00156     return status;
    00157 }
    00158
    00159
    00160 unsigned char ataReadSectorsCHS(    DiskInfo_t* disk,
    00161                                     unsigned char Head,
    00162                                     unsigned int Track,
    00163                                     unsigned char Sector,
    00164                                     unsigned int numsectors,
    00165                                     unsigned char *Buffer)
    00166 {
    00167     unsigned char temp;
    00168
    00169     // Wait for drive to be ready
    00170     temp = ataStatusWait(disk, ATA_SR_BSY, ATA_SR_BSY);
    00171
    00172     // Prepare parameters...
    00173     disk->ataif.WriteReg(ATA_REG_HDDEVSEL, 0xA0+(disk->driveno ? 0x10:00)+Head); // CHS mode/Drive/Head
    00174     disk->ataif.WriteReg(ATA_REG_CYLHI, Track>>8);          // MSB of track
    00175     disk->ataif.WriteReg(ATA_REG_CYLLO, Track);             // LSB of track
    00176     disk->ataif.WriteReg(ATA_REG_STARTSEC, Sector);     // sector
    00177     disk->ataif.WriteReg(ATA_REG_SECCOUNT, numsectors); // # of sectors
    00178
    00179     // Issue read sector command...
    00180     disk->ataif.WriteReg(ATA_REG_CMDSTATUS1, 0x21);
    00181
    00182     // Wait for drive to be ready
    00183     temp = ataStatusWait(disk, ATA_SR_BSY, ATA_SR_BSY);
    00184
    00185     if (temp & ATA_SR_ERR)
    00186     {
    00187         rprintfProgStrM("RD ERR\r\n");
    00188         return 1;
    00189     }
    00190
    00191     // Wait for drive to request data transfer
    00192     ataStatusWait(disk, ATA_SR_DRQ, 0);
    00193
    00194     // read data from drive
    00195     disk->ataif.ReadBlock(Buffer, 512*numsectors);
    00196
    00197     // Return the error bit from the status register...
    00198     temp = disk->ataif.ReadReg(ATA_REG_CMDSTATUS1); // read status register
    00199
    00200     return (temp & ATA_SR_ERR) ? 1:0;
    00201 }
    00202
    00203
    00204 unsigned char ataWriteSectorsCHS(   DiskInfo_t* disk,
    00205                                     unsigned char Head,
    00206                                     unsigned int Track,
    00207                                     unsigned char Sector,
    00208                                     unsigned int numsectors,
    00209                                     unsigned char *Buffer)
    00210 {
    00211     unsigned char temp;
    00212
    00213     // Wait for drive to be ready
    00214     temp = ataStatusWait(disk, ATA_SR_BSY, ATA_SR_BSY);
    00215
    00216     // Prepare parameters...
    00217     disk->ataif.WriteReg(ATA_REG_HDDEVSEL, 0xA0+(disk->driveno ? 0x10:00)+Head); // CHS mode/Drive/Head
    00218     disk->ataif.WriteReg(ATA_REG_CYLHI, Track>>8);          // MSB of track
    00219     disk->ataif.WriteReg(ATA_REG_CYLLO, Track);             // LSB of track
    00220     disk->ataif.WriteReg(ATA_REG_STARTSEC, Sector);     // sector
    00221     disk->ataif.WriteReg(ATA_REG_SECCOUNT, numsectors); // # of sectors
    00222
    00223     // Issue write sector command
    00224     disk->ataif.WriteReg(ATA_REG_CMDSTATUS1, 0x31);
    00225
    00226     //delay(100);
    00227
    00228     // Wait for drive to request data transfer
    00229     ataStatusWait(disk, ATA_SR_DRQ, 0);
    00230
    00231     // write data to drive
    00232     disk->ataif.WriteBlock(Buffer, 512*numsectors);
    00233
    00234     // Wait for drive to finish write
    00235     temp = ataStatusWait(disk, ATA_SR_BSY, ATA_SR_BSY);
    00236
    00237     // check for errors
    00238     if (temp & ATA_SR_ERR)
    00239     {
    00240         rprintfProgStrM("WR ERR\r\n");
    00241         return 1;
    00242     }
    00243
    00244     // Return the error bit from the status register...
    00245     return (temp & ATA_SR_ERR) ? 1:0;
    00246 }
    00247
    00248 unsigned char ataReadSectorsLBA(    DiskInfo_t* disk,
    00249                                     unsigned long lba,
    00250                                     unsigned int numsectors,
    00251                                     unsigned char *Buffer)
    00252 {
    00253     unsigned int cyl, head, sect;
    00254     unsigned char temp;
    00255
    00256 #ifdef DEBUG_ATA
    00257     rprintfProgStrM("ATA LBA read ");
    00258     rprintfu32(lba); rprintfChar(' ');
    00259     rprintfu16(numsectors); rprintfChar(' ');
    00260     rprintfu16((unsigned int)Buffer);
    00261     rprintfCRLF();
    00262 #endif
    00263
    00264     sect = (int) ( lba & 0x000000ffL );
    00265     lba = lba >> 8;
    00266     cyl = (int) ( lba & 0x0000ffff );
    00267     lba = lba >> 16;
    00268     head = ( (int) ( lba & 0x0fL ) ) | ATA_HEAD_USE_LBA;
    00269
    00270     temp = ataReadSectorsCHS( disk, head, cyl, sect, numsectors, Buffer );
    00271
    00272     if(temp)
    00273         ataDiskErr(disk);
    00274     return temp;
    00275 }
    00276
    00277 unsigned char ataWriteSectorsLBA(   DiskInfo_t* disk,
    00278                                     unsigned long lba,
    00279                                     unsigned int numsectors,
    00280                                     unsigned char *Buffer)
    00281 {
    00282     unsigned int cyl, head, sect;
    00283     unsigned char temp;
    00284
    00285 #ifdef DEBUG_ATA
    00286     rprintfProgStrM("ATA LBA write ");
    00287     rprintfu32(lba); rprintfChar(' ');
    00288     rprintfu16(numsectors); rprintfChar(' ');
    00289     rprintfu16((unsigned int)Buffer);
    00290     rprintfCRLF();
    00291 #endif
    00292
    00293     sect = (int) ( lba & 0x000000ffL );
    00294     lba = lba >> 8;
    00295     cyl = (int) ( lba & 0x0000ffff );
    00296     lba = lba >> 16;
    00297     head = ( (int) ( lba & 0x0fL ) ) | ATA_HEAD_USE_LBA;
    00298
    00299     temp = ataWriteSectorsCHS( disk, head, cyl, sect, numsectors, Buffer );
    00300
    00301     if(temp)
    00302         ataDiskErr(disk);
    00303     return temp;
    00304 }
    00305
    00306
    00307 unsigned char ataReadSectors(   DiskInfo_t* disk,
    00308                                 unsigned long lba,
    00309                                 unsigned int numsectors,
    00310                                 unsigned char *Buffer)
    00311 {
    00312     unsigned int cyl, head, sect;
    00313     unsigned char temp;
    00314
    00315     // check if drive supports native LBA mode
    00316     if(disk->LBAsupport)
    00317     {
    00318         // drive supports using native LBA
    00319         temp = ataReadSectorsLBA(disk, lba, numsectors, Buffer);
    00320     }
    00321     else
    00322     {
    00323         // drive required CHS access
    00324         #ifdef DEBUG_ATA
    00325             // do this defore destroying lba
    00326             rprintfProgStrM("ATA LBA for CHS read: ");
    00327             rprintfProgStrM("LBA="); rprintfu32(lba); rprintfChar(' ');
    00328         #endif
    00329
    00330         // convert LBA to pseudo CHS
    00331         // remember to offset the sector count by one
    00332         sect = (u08) (lba % disk->sectors)+1;
    00333         lba = lba / disk->sectors;
    00334         head = (u08) (lba % disk->heads);
    00335         lba = lba / disk->heads;
    00336         cyl = (u16) lba;
    00337
    00338         #ifdef DEBUG_ATA
    00339             rprintfProgStrM("C:H:S=");
    00340             rprintfu16(cyl); rprintfChar(':');
    00341             rprintfu08(head); rprintfChar(':');
    00342             rprintfu08(sect); rprintfCRLF();
    00343         #endif
    00344
    00345         temp = ataReadSectorsCHS( disk, head, cyl, sect, numsectors, Buffer );
    00346     }
    00347
    00348     if(temp)
    00349         ataDiskErr(disk);
    00350     return temp;
    00351 }
    00352
    00353
    00354 unsigned char ataWriteSectors(  DiskInfo_t* disk,
    00355                                 unsigned long lba,
    00356                                 unsigned int numsectors,
    00357                                 unsigned char *Buffer)
    00358 {
    00359     unsigned int cyl, head, sect;
    00360     unsigned char temp;
    00361
    00362     // check if drive supports native LBA mode
    00363     if(disk->LBAsupport)
    00364     {
    00365         // drive supports using native LBA
    00366         temp = ataWriteSectorsLBA(disk, lba, numsectors, Buffer);
    00367     }
    00368     else
    00369     {
    00370         // drive required CHS access
    00371         #ifdef DEBUG_ATA
    00372             // do this defore destroying lba
    00373             rprintfProgStrM("ATA LBA for CHS write: ");
    00374             rprintfProgStrM("LBA="); rprintfu32(lba); rprintfChar(' ');
    00375         #endif
    00376
    00377         // convert LBA to pseudo CHS
    00378         // remember to offset the sector count by one
    00379         sect = (u08) (lba % disk->sectors)+1;
    00380         lba = lba / disk->sectors;
    00381         head = (u08) (lba % disk->heads);
    00382         lba = lba / disk->heads;
    00383         cyl = (u16) lba;
    00384
    00385         #ifdef DEBUG_ATA
    00386             rprintfProgStrM("C:H:S=");
    00387             rprintfu16(cyl); rprintfChar(' ');
    00388             rprintfu08(head); rprintfChar(' ');
    00389             rprintfu08(sect); rprintfCRLF();
    00390         #endif
    00391
    00392         temp = ataWriteSectorsCHS( disk, head, cyl, sect, numsectors, Buffer );
    00393     }
    00394
    00395     if(temp)
    00396         ataDiskErr(disk);
    00397     return temp;
    00398 }
    00399
    00400 void ataDriveSelect(DiskInfo_t* disk)
    00401 {
    00402     disk->ataif.WriteReg(ATA_REG_HDDEVSEL, 0xA0+(disk->driveno ? 0x10:00)); // Drive selection
    00403 }
    00404
    00405 void ataShowRegisters(DiskInfo_t* disk)
    00406 {
    00407     disk->ataif.WriteReg(ATA_REG_HDDEVSEL, 0xA0 + (disk->driveno ? 0x10:0x00)); // Select drive
    00408
    00409     rprintfProgStrM("R0: DATA     = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_DATA     ));     rprintfProgStrM(" \r\n");
    00410     rprintfProgStrM("R1: ERROR    = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_ERROR    ));     rprintfProgStrM(" \r\n");
    00411     rprintfProgStrM("R2: SECT CNT = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_SECCOUNT));      rprintfProgStrM(" \r\n");
    00412     rprintfProgStrM("R3: SECT NUM = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_STARTSEC));      rprintfProgStrM(" \r\n");
    00413     rprintfProgStrM("R4: CYL LOW  = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_CYLLO    ));     rprintfProgStrM(" \r\n");
    00414     rprintfProgStrM("R5: CYL HIGH = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_CYLHI    ));     rprintfProgStrM(" \r\n");
    00415     rprintfProgStrM("R6: HEAD/DEV = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_HDDEVSEL));      rprintfProgStrM(" \r\n");
    00416     rprintfProgStrM("R7: CMD/STA  = 0x");   rprintfu08(disk->ataif.ReadReg(ATA_REG_CMDSTATUS1));    rprintfProgStrM("\r\n");
    00417 }
    00418
    00419 unsigned char ataSWReset(DiskInfo_t* disk)
    00420 {
    00421     disk->ataif.WriteReg(ATA_REG_HDDEVSEL, 0x06);   // SRST and nIEN bits
    00422     delay(10);  // 10uS delay
    00423     disk->ataif.WriteReg(ATA_REG_HDDEVSEL, 0x02);   // nIEN bits
    00424     delay(10);  // 10uS delay
    00425
    00426     while( (disk->ataif.ReadReg(ATA_REG_CMDSTATUS1) & 0xC0) != 0x40 ); // Wait for DRDY and not BSY
    00427
    00428     return disk->ataif.ReadReg(ATA_REG_CMDSTATUS1) + disk->ataif.ReadReg(ATA_REG_ERROR);
    00429 }
    00430
    00431 /*
    00432 unsigned char ATA_Idle(unsigned char Drive)
    00433 {
    00434
    00435   WriteBYTE(CMD, 6, 0xA0 + (Drive ? 0x10:0x00)); // Select drive
    00436   WriteBYTE(CMD,7, 0xE1);
    00437
    00438   while ((ReadBYTE(CMD,7) & 0xC0)!=0x40); // Wait for DRDY & NOT BUSY
    00439
    00440   // Return the error register...
    00441   return ReadBYTE(CMD, 1);
    00442 }
    00443 */
    00444 //----------------------------------------------------------------------------
    00445 // Set drive mode (STANDBY, IDLE)
    00446 //----------------------------------------------------------------------------
    00447 /*#define STANDBY 0
    00448 #define IDLE    1
    00449 #define SLEEP   2
    00450 */
    00451
    00452 /*
    00453 unsigned char SetMode(unsigned char DriveNo, unsigned char Mode, unsigned char PwrDown)
    00454 {
    00455   WriteBYTE(CMD, 6, 0xA0 + (DriveNo ? 0x10:0x00)); // Select drive
    00456   WriteBYTE(CMD, 2, (PwrDown ? 0x01:0x00)); // Enable automatic power down
    00457   switch (Mode)
    00458   {
    00459     case STANDBY: WriteBYTE(CMD,7, 0xE2); break;
    00460     case IDLE:    WriteBYTE(CMD,7, 0xE3); break;
    00461     // NOTE: To recover from sleep, either issue a soft or hardware reset !
    00462     // (But not on all drives, f.ex seagate ST3655A it's not nessecary to reset
    00463     // but only to go in Idle mode, But on a Conner CFA170A it's nessecary with
    00464     // a reset)
    00465     case SLEEP:   WriteBYTE(CMD,7, 0xE6); break;
    00466   }
    00467   Timer10mSec=10000;
    00468   while ((ReadBYTE(CMD,7) & 0xC0)!=0x40 && Timer10mSec); // Wait for DRDY & NOT BUSY
    00469   if (Timer10mSec==0) return 0xFF;                       //   or timeout
    00470
    00471   // Return the error register...
    00472   return ReadBYTE(CMD, 1);
    00473 }
    00474
    00475 */