This is the mail archive of the
ecos-patches@sourceware.org
mailing list for the eCos project.
Adding Compact Flash support to IDE driver
- From: Frank Pagliughi <fpagliughi at mindspring dot com>
- To: ecos-patches at sourceware dot org
- Date: Wed, 23 Jul 2008 16:55:26 -0400
- Subject: Adding Compact Flash support to IDE driver
- Domainkey-signature: a=rsa-sha1; q=dns; c=nofws; s=dk20050327; d=mindspring.com; b=W++wlganAYDV7B9XyQntv7UD/N9504ePGYRgeecYQ0OmcnRsfdVRvxPfaa7KbUda; h=Received:Message-ID:Date:From:User-Agent:MIME-Version:To:Subject:Content-Type:X-ELNK-Trace:X-Originating-IP;
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;