This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Adding Compact Flash support to IDE driver


This patch add Compact Flash support to the IDE driver. It adds/changes the following:

- It removes the drive "identity" check. The bits that were used in the check are now considered "retired" by the ATA standard, and don't match up with CF disks, or many new hard drives that I tested.

- It adds a configurable "startup delay" parameter. Some cheap CF drives can take a long time to initialize, and on non-PC systems with CF support, the motherboard can boot much quicker than a PC BIOS boot, and thus require a wait for any IDE drive.

- It adds support for embedded systems that use an 8-bit data path to the drive (with CDL option).

- The read and write sector functions were modified for increased performance. They now support transfers of more than one sector at a time (up to 256), and have slightly optimized buffer copy algorithms if the buffers are aligned to 16-bit addresses.

- The default drive names in the CDL were changed to "/dev/disk0" (from "/dev/hda") to agree with the disk names used in the FAT file system test.
Now the filesystem test should work out-of-the-box.


I tested this on:
- Several standard PC's with hard drives.
- A standard PC with an IDE-CF converter.
- A PC/104 board with a CF adapter.
- A proprietary ARM (AT91) board with an 8-bit CF connection.


And it also has a small modification to the FAT fs routine which checks the boot record signature. Microsoft claims that the values 0x28 or 0x29 are acceptable for this value, and I saw both in the IDE tests.


Frank Pagliughi

diff -urN --exclude=CVS --exclude=.svn ecos/packages/devs/disk/ide/current/cdl/ide_disk.cdl ide/packages/devs/disk/ide/current/cdl/ide_disk.cdl
--- ecos/packages/devs/disk/ide/current/cdl/ide_disk.cdl	2004-10-18 05:03:45.000000000 -0400
+++ ide/packages/devs/disk/ide/current/cdl/ide_disk.cdl	2008-07-22 10:45:54.000000000 -0400
@@ -55,7 +55,7 @@
     compile     -library=libextras.a   ide_disk.c
     
     cdl_component CYGVAR_DEVS_DISK_IDE_DISK0 {
-    	display         "Provide disk 0 device"
+        display         "Provide disk 0 device"
         flavor          bool
         default_value   1
         description     "IDE chanel 0:0 disk driver"
@@ -63,12 +63,12 @@
         cdl_option CYGDAT_IO_DISK_IDE_DISK0_NAME {
             display       "Device name for disk 0 device"
             flavor        data
-            default_value {"\"/dev/hda/\""}
+            default_value {"\"/dev/disk0/\""}
         }
     }
     
     cdl_component CYGVAR_DEVS_DISK_IDE_DISK1 {
-     display         "Provide disk 1 device"
+        display         "Provide disk 1 device"
         flavor          bool
         default_value   0
         description     "IDE chanel 0:1 disk driver"
@@ -76,12 +76,12 @@
         cdl_option CYGDAT_IO_DISK_IDE_DISK1_NAME {
             display       "Device name for disk 1 device"
             flavor        data
-            default_value {"\"/dev/hdb/\""}
+            default_value {"\"/dev/disk1/\""}
         }
     }
     
     cdl_component CYGVAR_DEVS_DISK_IDE_DISK2 {
-     display         "Provide disk 2 device"
+        display         "Provide disk 2 device"
         flavor          bool
         default_value   0
         description     "IDE chanel 1:0 disk driver"
@@ -89,12 +89,12 @@
         cdl_option CYGDAT_IO_DISK_IDE_DISK2_NAME {
             display       "Device name for disk 2 device"
             flavor        data
-            default_value {"\"/dev/hdc/\""}
+            default_value {"\"/dev/disk2/\""}
         }
     }
     
     cdl_component CYGVAR_DEVS_DISK_IDE_DISK3 {
-     display         "Provide disk 3 device"
+        display         "Provide disk 3 device"
         flavor          bool
         default_value   0
         description     "IDE chanel 1:1 disk driver"
@@ -102,7 +102,7 @@
         cdl_option CYGDAT_IO_DISK_IDE_DISK3_NAME {
             display       "Device name for disk 3 device"
             flavor        data
-            default_value {"\"/dev/hdd/\""}
+            default_value {"\"/dev/disk3/\""}
         }
     }
 
@@ -112,7 +112,35 @@
         default_value 512
         description "
         This option controls the disk sector size (default=512)"
-     }
+    }
+
+    cdl_option CYGDAT_DEVS_DISK_IDE_STARTUP_DELAY {
+        display       "Startup delay (in ms)"
+        flavor        data
+        default_value 0
+        description "
+		The amount of time (in ms) to wait for the IDE drives to 
+		initialize on startup. For hard drives, this can usually
+		be set to zero, but for Compact Flash and other solid
+		state media this could be up to 500ms. If drives are not
+		detected at power-up, try increasing this value.
+		"
+    }
+
+    cdl_option CYGDAT_DEVS_DISK_IDE_8_BIT_DATA_PATH {
+        display       "8-bit data path"
+        flavor        bool
+        default_value false
+        description "
+            This allows the host to communicate with the IDE drives using an 
+            8-bit data, rather than 16-bits. It does so by requesting a \"Set
+            Feature\" on the drive for the 8-bit path. Note that this may
+            be ignored by most modern disk drives, but is supported by Compact
+            Flash drives. This is only used by proprietary boards, and should
+	    be disabled for standard IDE controllers.
+        "
+    }
+
 
     cdl_option CYGSEM_DEVS_DISK_IDE_VMWARE {
         display       "Work with VMware virtual disks"
diff -urN --exclude=CVS --exclude=.svn ecos/packages/devs/disk/ide/current/src/ide_disk.c ide/packages/devs/disk/ide/current/src/ide_disk.c
--- ecos/packages/devs/disk/ide/current/src/ide_disk.c	2006-11-17 13:04:43.000000000 -0500
+++ ide/packages/devs/disk/ide/current/src/ide_disk.c	2008-07-22 10:42:37.000000000 -0400
@@ -48,6 +48,7 @@
 //==========================================================================
 
 #include <pkgconf/devs_disk_ide.h>
+#include <pkgconf/io_disk.h>
 
 #include <cyg/infra/cyg_type.h>
 #include <cyg/infra/cyg_ass.h>
@@ -64,7 +65,9 @@
 
 // ----------------------------------------------------------------------------
 
-//#define DEBUG 1
+#ifdef CYGDBG_IO_DISK_DEBUG
+# define DEBUG 1
+#endif
 
 #ifdef DEBUG
 # define D(fmt,args...) diag_printf(fmt, ## args)
@@ -94,6 +97,8 @@
 IDE_DISK_INSTANCE(3, 1, 1, true, CYGDAT_IO_DISK_IDE_DISK3_NAME);
 #endif
 
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
 // ----------------------------------------------------------------------------
 
 static void
@@ -101,8 +106,7 @@
 {
     int i;
 
-    for (i = 0; i < size; i+=2)
-    {
+    for (i=0; i<size; i+=2) {
         *dest++ = (char)(*src >> 8);
         *dest++ = (char)(*src & 0x00FF);
         src++;
@@ -121,7 +125,9 @@
     } while (status & (IDE_STAT_BSY | IDE_STAT_DRQ));
 }
 
+// ----------------------------------------------------------------------------
 // Wait while the device is busy with the last command
+
 static inline int
 __wait_busy(int ctlr)
 {
@@ -137,6 +143,8 @@
     return 0;   
 }
 
+// ----------------------------------------------------------------------------
+
 static inline int
 __wait_for_drq(int ctlr)
 {
@@ -156,18 +164,20 @@
     return 0;
 }
 
+// ----------------------------------------------------------------------------
 // Return true if any devices attached to controller
+
 static int
 ide_presence_detect(int ctlr)
 {
     cyg_uint8 sel, val;
     int i;
 
-    for (i = 0; i < 2; i++) {
+    for (i = 0; i < HAL_IDE_NUM_CONTROLLERS; i++) {
         sel = (i << 4) | 0xA0;
-        CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000);
+        CYGACC_CALL_IF_DELAY_US(50000U);
         HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, sel);
-        CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000);
+        CYGACC_CALL_IF_DELAY_US(50000U);
         HAL_IDE_READ_UINT8(ctlr, IDE_REG_DEVICE, val);
         if (val == sel) {
 #ifndef CYGSEM_DEVS_DISK_IDE_VMWARE
@@ -180,6 +190,8 @@
     return 0;
 }
 
+// ----------------------------------------------------------------------------
+
 static int
 ide_reset(int ctlr)
 {
@@ -193,14 +205,14 @@
 //
 #ifndef CYGSEM_DEVS_DISK_IDE_VMWARE
     HAL_IDE_WRITE_CONTROL(ctlr, 6);    // polled mode, reset asserted
-    CYGACC_CALL_IF_DELAY_US(5000);
+    CYGACC_CALL_IF_DELAY_US(5000U);
     HAL_IDE_WRITE_CONTROL(ctlr, 2);   // polled mode, reset cleared
-    CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000);
+    CYGACC_CALL_IF_DELAY_US(50000U);
 #endif
 
     // wait 30 seconds max for not busy and drive ready
-    for (delay = 0; delay < 300; ++delay) {
-        CYGACC_CALL_IF_DELAY_US((cyg_uint32)100000);
+    for (delay=0; delay<300; ++delay) {
+        CYGACC_CALL_IF_DELAY_US(100000U);
         HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
             if (!(status & IDE_STAT_BSY)) {
                 if (status & IDE_STAT_DRDY) {
@@ -211,43 +223,75 @@
     return 0;
 }
 
+// ----------------------------------------------------------------------------
+
 static int
 ide_ident(int ctlr, int dev, cyg_uint16 *buf)
 {
     int i;
 
-    if (!__wait_busy(ctlr)) {
+    if (!__wait_busy(ctlr))
          return 0;
-    }
-    
+
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, dev << 4);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0xEC);
-    CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000);
+    CYGACC_CALL_IF_DELAY_US(50000U);
 
-    if (!__wait_for_drq(ctlr)) {
+    if (!__wait_for_drq(ctlr))
          return 0;
-    }
-    
-    for (i = 0; i < (CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16));
-         i++, buf++)
+
+    for (i=0; i<(CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE/sizeof(cyg_uint16)); i++, buf++)
         HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf);
 
     return 1;
 }
 
+// ----------------------------------------------------------------------------
+// Requests the disk to use an 8-bit data path. This is probably ignored by
+// most modern drives, but is supported by compact flash devices.
+
+#ifdef CYGDAT_DEVS_DISK_IDE_8_BIT_DATA_PATH
 static int
-ide_read_sector(int ctlr, int dev, cyg_uint32 start, 
-                cyg_uint8 *buf, cyg_uint32 len)
+ide_8bit_mode(int ctlr, int dev, cyg_bool on)
 {
-    int j, c;
-    cyg_uint16 p;
-    cyg_uint8 * b=buf;
+    cyg_uint8 stat;
 
-    if(!__wait_busy(ctlr)) {
+    if (!__wait_busy(ctlr))
          return 0;
-    }
     
-    HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, 1);    // count =1
+    HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, dev << 4);
+    HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_FEATURES, 0x01);
+    HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND,  0xEF);
+
+    if (!__wait_busy(ctlr))
+        return 0;
+
+    HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, stat);
+    return (stat & 1) ? 0 : 1;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// Reads a group of contiguous sectors from the drive.
+// It can read up to 256 sectors.
+
+static int
+ide_read_sector(int ctlr, int dev, cyg_uint32 start, 
+                cyg_uint8 *buf, cyg_uint32 len)
+{
+    int i, nword;
+    cyg_uint8 lenb;
+    cyg_uint16 w;
+
+    if (len==0 || !__wait_busy(ctlr))
+       return 0;
+
+    len = MIN(len, 256);
+    lenb = (len == 256) ? 0 : ((cyg_uint8) len);
+
+    nword = len * CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16);
+
+    HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, lenb);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, start & 0xff);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, (start >>  8) & 0xff);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI,  (start >> 16) & 0xff);
@@ -257,52 +301,68 @@
 
     if (!__wait_for_drq(ctlr))
         return 0;
-    // 
-    // It would be fine if all buffers were word aligned,
-    // but who knows
-    //
-    for (j = 0, c=0 ; j < (CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16));
-         j++) {
-        HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, p);
-        if (c++<(len*512)) *b++=p&0xff;
-        if (c++<(len*512)) *b++=(p>>8)&0xff;
+
+    if ((int) buf & 1) {
+        // Unaligned buffer, so split each word manually
+        for (i=0; i<nword; i++) {
+            HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, w);
+            *buf++ = w & 0xff;
+            *buf++ = (w>>8) & 0xff;
+        }
     }
-    return 1;
+    else {
+        cyg_uint16* wbuf = (cyg_uint16*) buf;
+        for (i=0; i<nword; i++, wbuf++)
+            HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *wbuf);
+    }
+    return (int) len;
 }
 
+// ----------------------------------------------------------------------------
+// Writes a group of contiguous sectors to the drive.
+// It can write up to 256 sectors.
+
 static int
 ide_write_sector(int ctlr, int dev, cyg_uint32 start, 
-                cyg_uint8 *buf, cyg_uint32 len)
+                 cyg_uint8 *buf, cyg_uint32 len)
 {
-    int j, c;
-    cyg_uint16 p;
-    cyg_uint8 * b=buf;
+    int i, nword;
+    cyg_uint8 lenb;
+    cyg_uint16 w;
 
-    if(!__wait_busy(ctlr)) {
-         return 0;
-    }
-    
-    HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, 1);    // count =1
+    if (len==0 || !__wait_busy(ctlr))
+       return 0;
+
+    len = MIN(len, 256);
+    lenb = (len == 256) ? 0 : ((cyg_uint8) len);
+
+    nword = len * CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16);
+
+    HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, lenb);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, start & 0xff);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, (start >>  8) & 0xff);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI,  (start >> 16) & 0xff);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE,
-          ((start >> 24) & 0xf) | (dev << 4) | 0x40);
+                            ((start >> 24) & 0xf) | (dev << 4) | 0x40);
     HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0x30);
 
     if (!__wait_for_drq(ctlr))
         return 0;
-    // 
-    // It would be fine if all buffers were word aligned,
-    // but who knows
-    //
-    for (j = 0, c=0 ; j < (CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16));
-         j++) {
-        p = (c++<(len*512)) ? *b++ : 0;
-        p |= (c++<(len*512)) ? (*b++<<8) : 0; 
-        HAL_IDE_WRITE_UINT16(ctlr, IDE_REG_DATA, p);
+
+    if ((int) buf & 1) {
+        // Unaligned buffer, so assemble each word manually
+        for (i=0; i<nword; ++i) {
+            w  = *buf++;
+            w |= (cyg_uint16) (*buf++) << 8;
+            HAL_IDE_WRITE_UINT16(ctlr, IDE_REG_DATA, w);
+        }
     }
-    return 1;
+    else {
+        cyg_uint16* wbuf = (cyg_uint16*) buf;
+        for (i=0; i<nword; ++i)
+            HAL_IDE_WRITE_UINT16(ctlr, IDE_REG_DATA, *wbuf++);
+    }
+    return (int) len;
 }
 
 // ----------------------------------------------------------------------------
@@ -328,6 +388,11 @@
         D("No IDE controller for channel %d:%d\n", info->port, info->chan);
         return false;
     }
+
+#if CYGDAT_DEVS_DISK_IDE_STARTUP_DELAY
+    CYGACC_CALL_IF_DELAY_US(CYGDAT_DEVS_DISK_IDE_STARTUP_DELAY*1000U);
+#endif
+
     if (!ide_present[info->port]) {
         ide_present[info->port]=ide_presence_detect(info->port);
         if (!ide_present[info->port]) {
@@ -342,6 +407,13 @@
             return false;
         }
     }
+
+#ifdef CYGDAT_DEVS_DISK_IDE_8_BIT_DATA_PATH
+    if (!ide_8bit_mode(info->port, info->chan, true)) {
+        D("IDE disk %d:%d failed to enter 8-bit mode\n",
+          info->port, info->chan);
+    }
+#endif
     
     D("IDE %d:%d identify drive\n", info->port, info->chan);
     
@@ -350,9 +422,9 @@
         return false;
     }
 
-    id_strcpy(ident.serial, ide_idData->serial,       20);
+    id_strcpy(ident.serial, ide_idData->serial, 20);
     id_strcpy(ident.firmware_rev, ide_idData->firmware_rev, 8);
-    id_strcpy(ident.model_num, ide_idData->model_num,    40);
+    id_strcpy(ident.model_num, ide_idData->model_num, 40);
     
     ident.cylinders_num  = ide_idData->num_cylinders;
     ident.heads_num = ide_idData->num_heads;
@@ -360,6 +432,8 @@
     ident.lba_sectors_num = ide_idData->lba_total_sectors[1] << 16 | 
                             ide_idData->lba_total_sectors[0];
     ident.phys_block_size = 1;
+
+    // TODO: Should this be CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE?
     ident.max_transfer = 512;
     
     D("\tSerial : %s\n", ident.serial);
@@ -367,13 +441,7 @@
     D("\tModel : %s\n", ident.model_num);
     D("\tC/H/S : %d/%d/%d\n", ident.cylinders_num, 
                               ident.heads_num, ident.sectors_num);
-    D("\tKind : %x\n", (ide_idData->general_conf>>8)&0x1f);
 
-    if (((ide_idData->general_conf>>8)&0x1f)!=2) {
-        diag_printf("IDE device %d:%d is not a hard disk!\n",
-                    info->port, info->chan);
-        return false;
-    }
     if (!(chan->callbacks->disk_init)(tab))
         return false;
 
diff -urN --exclude=CVS --exclude=.svn ecos/packages/fs/fat/current/src/fatfs_supp.c ide/packages/fs/fat/current/src/fatfs_supp.c
--- ecos/packages/fs/fat/current/src/fatfs_supp.c	2008-05-13 13:57:35.000000000 -0400
+++ ide/packages/fs/fat/current/src/fatfs_supp.c	2008-07-23 16:07:02.000000000 -0400
@@ -1868,7 +1868,8 @@
         return err;
 
     // Check some known boot record values
-    if (0x29 != boot_rec.ext_sig       ||
+    if (!(0x29 == boot_rec.ext_sig ||
+		  0x28 == boot_rec.ext_sig)    ||
         0x55 != boot_rec.exe_marker[0] ||
         0xAA != boot_rec.exe_marker[1])
         return EINVAL;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]