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 */

Generated on Mon Nov 6 23:36:58 2006 for Procyon ARMlib by  doxygen 1.4.2