summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2007-05-23 17:51:18 +0000
committerJens Arnold <amiconn@rockbox.org>2007-05-23 17:51:18 +0000
commitc5d71aab1677741d445367820f78b0c85a6f4cde (patch)
tree2d547276eaec8dc1bef0133bd01dd54d92ae8694
parentc5302d5101962ea21265a271c2494cacea088fea (diff)
ATA driver: * Support for drives with large physical sectors and no support for partial access in the firmware (i.e. Toshiba MK8010GAH - iPod G5.5/80GB). Sequential writes with a single 512-byte buffer to that disk are really slow, so this is an intermediate solution that allows to adjust the FAT driver and the file system gradually. * Assume multisectors = 16 if the value reported by the drive is invalid (also MK8010GAH).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13480 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/common/disk.c4
-rw-r--r--firmware/drivers/ata.c234
-rw-r--r--firmware/export/config-ipodvideo.h6
3 files changed, 235 insertions, 9 deletions
diff --git a/firmware/common/disk.c b/firmware/common/disk.c
index c58ae3ee86..ebf8103aa0 100644
--- a/firmware/common/disk.c
+++ b/firmware/common/disk.c
@@ -152,10 +152,10 @@ int disk_mount(int drive)
real problem. */
for (i=0; volume != -1 && i<4; i++)
{
-#ifdef MAX_SECTOR_SIZE
+#ifdef MAX_LOG_SECTOR_SIZE
int j;
- for (j = 1; j <= (MAX_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
+ for (j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
{
if (!fat_mount(IF_MV2(volume,) IF_MV2(drive,) pinfo[i].start * j))
{
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 35967d7d78..e9e567761b 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -92,6 +92,18 @@ long last_disk_activity = -1;
static int multisectors; /* number of supported multisectors */
static unsigned short identify_info[SECTOR_SIZE];
+#ifdef MAX_PHYS_SECTOR_SIZE
+struct sector_cache_entry {
+ bool inuse;
+ unsigned long sectornum; /* logical sector */
+ unsigned char data[MAX_PHYS_SECTOR_SIZE];
+};
+/* buffer for reading and writing large physical sectors */
+#define NUMCACHES 2
+static struct sector_cache_entry sector_cache;
+static int phys_sector_mult = 1;
+#endif
+
static int ata_power_on(void);
static int perform_soft_reset(void);
static int set_multiple_mode(int sectors);
@@ -201,10 +213,16 @@ STATICIRAM void copy_read_sectors(unsigned char* buf, int wordcount)
}
#endif /* !ATA_OPTIMIZED_READING */
+#ifdef MAX_PHYS_SECTOR_SIZE
+static int _read_sectors(unsigned long start,
+ int incount,
+ void* inbuf)
+#else
int ata_read_sectors(IF_MV2(int drive,)
unsigned long start,
int incount,
void* inbuf)
+#endif
{
int ret = 0;
long timeout;
@@ -212,10 +230,12 @@ int ata_read_sectors(IF_MV2(int drive,)
void* buf;
long spinup_start;
+#ifndef MAX_PHYS_SECTOR_SIZE
#ifdef HAVE_MULTIVOLUME
(void)drive; /* unused for now */
#endif
spinlock_lock(&ata_mtx);
+#endif
last_disk_activity = current_tick;
spinup_start = current_tick;
@@ -318,9 +338,6 @@ int ata_read_sectors(IF_MV2(int drive,)
/* read the status register exactly once per loop */
status = ATA_STATUS;
- /* if destination address is odd, use byte copying,
- otherwise use word copying */
-
if (count >= multisectors )
sectors = multisectors;
else
@@ -358,7 +375,9 @@ int ata_read_sectors(IF_MV2(int drive,)
}
ata_led(false);
+#ifndef MAX_PHYS_SECTOR_SIZE
spinlock_unlock(&ata_mtx);
+#endif
return ret;
}
@@ -401,22 +420,30 @@ STATICIRAM void copy_write_sectors(const unsigned char* buf, int wordcount)
}
#endif /* !ATA_OPTIMIZED_WRITING */
+#ifdef MAX_PHYS_SECTOR_SIZE
+static int _write_sectors(unsigned long start,
+ int count,
+ const void* buf)
+#else
int ata_write_sectors(IF_MV2(int drive,)
unsigned long start,
int count,
const void* buf)
+#endif
{
int i;
int ret = 0;
long spinup_start;
-#ifdef HAVE_MULTIVOLUME
- (void)drive; /* unused for now */
-#endif
if (start == 0)
panicf("Writing on sector 0\n");
+#ifndef MAX_PHYS_SECTOR_SIZE
+#ifdef HAVE_MULTIVOLUME
+ (void)drive; /* unused for now */
+#endif
spinlock_lock(&ata_mtx);
+#endif
last_disk_activity = current_tick;
spinup_start = current_tick;
@@ -506,11 +533,186 @@ int ata_write_sectors(IF_MV2(int drive,)
ata_led(false);
+#ifndef MAX_PHYS_SECTOR_SIZE
spinlock_unlock(&ata_mtx);
+#endif
return ret;
}
+#ifdef MAX_PHYS_SECTOR_SIZE
+static int cache_sector(unsigned long sector)
+{
+ int rc;
+
+ sector &= ~(phys_sector_mult - 1);
+ /* round down to physical sector boundary */
+
+ /* check whether the sector is already cached */
+ if (sector_cache.inuse && (sector_cache.sectornum == sector))
+ return 0;
+
+ /* not found: read the sector */
+ sector_cache.inuse = false;
+ rc = _read_sectors(sector, phys_sector_mult, sector_cache.data);
+ if (!rc)
+ {
+ sector_cache.sectornum = sector;
+ sector_cache.inuse = true;
+ }
+ return rc;
+}
+
+static inline int flush_current_sector(void)
+{
+ return _write_sectors(sector_cache.sectornum, phys_sector_mult,
+ sector_cache.data);
+}
+
+int ata_read_sectors(IF_MV2(int drive,)
+ unsigned long start,
+ int incount,
+ void* inbuf)
+{
+ int rc = 0;
+ int offset;
+
+#ifdef HAVE_MULTIVOLUME
+ (void)drive; /* unused for now */
+#endif
+ spinlock_lock(&ata_mtx);
+
+ offset = start & (phys_sector_mult - 1);
+
+ if (offset) /* first partial sector */
+ {
+ int partcount = MIN(incount, phys_sector_mult - offset);
+
+ rc = cache_sector(start);
+ if (rc)
+ {
+ rc = rc * 10 - 1;
+ goto error;
+ }
+ memcpy(inbuf, sector_cache.data + offset * SECTOR_SIZE,
+ partcount * SECTOR_SIZE);
+
+ start += partcount;
+ inbuf += partcount * SECTOR_SIZE;
+ incount -= partcount;
+ }
+ if (incount)
+ {
+ offset = incount & (phys_sector_mult - 1);
+ incount -= offset;
+
+ if (incount)
+ {
+ rc = _read_sectors(start, incount, inbuf);
+ if (rc)
+ {
+ rc = rc * 10 - 2;
+ goto error;
+ }
+ start += incount;
+ inbuf += incount * SECTOR_SIZE;
+ }
+ if (offset)
+ {
+ rc = cache_sector(start);
+ if (rc)
+ {
+ rc = rc * 10 - 3;
+ goto error;
+ }
+ memcpy(inbuf, sector_cache.data, offset * SECTOR_SIZE);
+ }
+ }
+
+ error:
+ spinlock_unlock(&ata_mtx);
+
+ return rc;
+}
+
+int ata_write_sectors(IF_MV2(int drive,)
+ unsigned long start,
+ int count,
+ const void* buf)
+{
+ int rc = 0;
+ int offset;
+
+#ifdef HAVE_MULTIVOLUME
+ (void)drive; /* unused for now */
+#endif
+ spinlock_lock(&ata_mtx);
+
+ offset = start & (phys_sector_mult - 1);
+
+ if (offset) /* first partial sector */
+ {
+ int partcount = MIN(count, phys_sector_mult - offset);
+
+ rc = cache_sector(start);
+ if (rc)
+ {
+ rc = rc * 10 - 1;
+ goto error;
+ }
+ memcpy(sector_cache.data + offset * SECTOR_SIZE, buf,
+ partcount * SECTOR_SIZE);
+ rc = flush_current_sector();
+ if (rc)
+ {
+ rc = rc * 10 - 2;
+ goto error;
+ }
+ start += partcount;
+ buf += partcount * SECTOR_SIZE;
+ count -= partcount;
+ }
+ if (count)
+ {
+ offset = count & (phys_sector_mult - 1);
+ count -= offset;
+
+ if (count)
+ {
+ rc = _write_sectors(start, count, buf);
+ if (rc)
+ {
+ rc = rc * 10 - 3;
+ goto error;
+ }
+ start += count;
+ buf += count * SECTOR_SIZE;
+ }
+ if (offset)
+ {
+ rc = cache_sector(start);
+ if (rc)
+ {
+ rc = rc * 10 - 4;
+ goto error;
+ }
+ memcpy(sector_cache.data, buf, offset * SECTOR_SIZE);
+ rc = flush_current_sector();
+ if (rc)
+ {
+ rc = rc * 10 - 5;
+ goto error;
+ }
+ }
+ }
+
+ error:
+ spinlock_unlock(&ata_mtx);
+
+ return rc;
+}
+#endif /* MAX_PHYS_SECTOR_SIZE */
+
static int check_registers(void)
{
#if (CONFIG_CPU == PP5002)
@@ -941,6 +1143,9 @@ int ata_init(void)
ata_device_init();
sleeping = false;
ata_enable(true);
+#ifdef MAX_PHYS_SECTOR_SIZE
+ memset(&sector_cache, 0, sizeof(sector_cache));
+#endif
if ( !initialized ) {
if (!ide_powered()) /* somebody has switched it off */
@@ -966,8 +1171,25 @@ int ata_init(void)
return -40 + rc;
multisectors = identify_info[47] & 0xff;
+ if (multisectors == 0) /* Invalid multisector info, try with 16 */
+ multisectors = 16;
+
DEBUGF("ata: %d sectors per ata request\n",multisectors);
+#ifdef MAX_PHYS_SECTOR_SIZE
+ /* Find out the physical sector size */
+ if((identify_info[106] & 0xe000) == 0x6000)
+ phys_sector_mult = 1 << (identify_info[106] & 0x000f);
+ else
+ phys_sector_mult = 1;
+
+ DEBUGF("ata: %d logical sectors per phys sector", phys_sector_mult);
+
+ if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/SECTOR_SIZE))
+ panicf("Unsupported physical sector size: %d",
+ phys_sector_mult * SECTOR_SIZE);
+#endif
+
#ifdef HAVE_LBA48
if (identify_info[83] & 0x0400 /* 48 bit address support */
&& identify_info[60] == 0xFFFF /* and disk size >= 128 GiB */
diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h
index b2b4ae9856..46411a7784 100644
--- a/firmware/export/config-ipodvideo.h
+++ b/firmware/export/config-ipodvideo.h
@@ -141,7 +141,11 @@
/* define this if the device has larger sectors when accessed via USB */
/* (only relevant in disk.c, fat.c now always supports large virtual sectors) */
-#define MAX_SECTOR_SIZE 2048
+#define MAX_LOG_SECTOR_SIZE 2048
+
+/* define this if the hard drive uses large physical sectors (ATA-7 feature) */
+/* and doesn't handle them in the drive firmware */
+#define MAX_PHYS_SECTOR_SIZE 1024
#define BOOTFILE_EXT "ipod"
#define BOOTFILE "rockbox." BOOTFILE_EXT