summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sparmann <theseven@gmx.net>2014-07-18 23:15:21 +0200
committerMarcin Bukat <marcin.bukat@gmail.com>2014-09-22 10:16:35 +0200
commit582035c5cbd4f04588615d2f9b91b2f97c80f949 (patch)
tree0cb45355d380df40acd7739e462b877b8ce5f202
parent3e131abe27840bf7dd43ea729b4b91636cee33db (diff)
iPod Classic: Fix several ATA driver bugs.
This improves compatibility with various HDD and CF/SD card mods. It should also reduce power consumption while the drive is powered down. Change-Id: I4b22c59b5d9ae2daea2ec5892e348e7e1934ca3e Reviewed-on: http://gerrit.rockbox.org/897 Tested: Franklin Wei <frankhwei536@gmail.com> Tested: Nial Shui <nialv7@gmail.com> Tested: Michael Sparmann <theseven@gmx.net> Reviewed-by: Marcin Bukat <marcin.bukat@gmail.com>
-rwxr-xr-x[-rw-r--r--]firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c159
1 files changed, 108 insertions, 51 deletions
diff --git a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c
index 0fd74787d1..955a33a595 100644..100755
--- a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c
+++ b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c
@@ -78,18 +78,31 @@ uint64_t ata_virtual_sectors;
uint32_t ata_last_offset;
uint64_t ata_last_phys;
+static int ata_reset(void);
+static void ata_power_down(void);
int ata_rw_sectors_internal(uint64_t sector, uint32_t count,
void* buffer, bool write);
int ata_bbt_read_sectors(uint32_t sector, uint32_t count, void* buffer)
{
- if (ata_last_phys != sector - 1 && ata_last_phys > sector - 64) ata_soft_reset();
+ if (ata_last_phys != sector - 1 && ata_last_phys > sector - 64) ata_reset();
int rc = ata_rw_sectors_internal(sector, count, buffer, false);
if (rc) rc = ata_rw_sectors_internal(sector, count, buffer, false);
+ if (rc)
+ {
+ ata_reset();
+ rc = ata_rw_sectors_internal(sector, count, buffer, false);
+ }
+ if (rc)
+ {
+ ata_power_down();
+ sleep(HZ * 10);
+ rc = ata_rw_sectors_internal(sector, count, buffer, false);
+ }
ata_last_phys = sector + count - 1;
ata_last_offset = 0;
if (IS_ERR(rc))
- panicf("ATA: Error %08X while reading BBT (sector %d, count %d)\n",
+ panicf("ATA: Error %08X while reading BBT (sector %d, count %d)",
(unsigned int)rc, (unsigned int)sector, (unsigned int)count);
return rc;
}
@@ -486,20 +499,20 @@ static int ceata_rw_multiple_block(bool write, void* buf, uint32_t count, long t
| SDCI_CMD_CMD_TYPE_ADTC | cmdtype | responsetype
| SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR,
direction | MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_COUNT(count),
- NULL, CEATA_COMMAND_TIMEOUT), 4, 0);
+ NULL, CEATA_COMMAND_TIMEOUT), 3, 0);
if (write) SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX;
if (semaphore_wait(&mmc_wakeup, timeout) == OBJ_WAIT_TIMEDOUT)
{
- PASS_RC(ceata_cancel_command(), 4, 1);
+ PASS_RC(ceata_cancel_command(), 3, 1);
RET_ERR(2);
}
- PASS_RC(mmc_dsta_check_data_success(), 4, 3);
+ PASS_RC(mmc_dsta_check_data_success(), 3, 3);
if (semaphore_wait(&mmc_comp_wakeup, timeout) == OBJ_WAIT_TIMEDOUT)
{
- PASS_RC(ceata_cancel_command(), 4, 4);
- RET_ERR(4);
+ PASS_RC(ceata_cancel_command(), 3, 4);
+ RET_ERR(5);
}
- PASS_RC(ceata_check_error(), 4, 5);
+ PASS_RC(ceata_check_error(), 3, 6);
return 0;
}
@@ -540,23 +553,42 @@ bool ata_disk_is_active(void)
static int ata_set_feature(uint32_t feature, uint32_t param)
{
- PASS_RC(ata_wait_for_rdy(500000), 1, 0);
- ata_write_cbr(&ATA_PIO_DVR, 0);
- ata_write_cbr(&ATA_PIO_FED, 3);
- ata_write_cbr(&ATA_PIO_SCR, param);
- ata_write_cbr(&ATA_PIO_CSD, feature);
- PASS_RC(ata_wait_for_rdy(500000), 1, 1);
+ if (ceata)
+ {
+ memset(ceata_taskfile, 0, 16);
+ ceata_taskfile[0x1] = feature;
+ ceata_taskfile[0x2] = param;
+ ceata_taskfile[0xf] = 0xef;
+ PASS_RC(ceata_wait_idle(), 2, 0);
+ PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1);
+ PASS_RC(ceata_wait_idle(), 2, 2);
+ }
+ else
+ {
+ PASS_RC(ata_wait_for_rdy(2000000), 2, 0);
+ ata_write_cbr(&ATA_PIO_DVR, 0);
+ ata_write_cbr(&ATA_PIO_FED, feature);
+ ata_write_cbr(&ATA_PIO_SCR, param);
+ ata_write_cbr(&ATA_PIO_CSD, 0xef);
+ PASS_RC(ata_wait_for_rdy(2000000), 2, 1);
+ }
return 0;
}
static int ata_power_up(void)
{
ata_set_active();
- if (ata_powered) return 0;
ide_power_enable(true);
long spinup_start = current_tick;
if (ceata)
{
+ ata_lba48 = true;
+ ata_dma = true;
+ PCON(8) = 0x33333333;
+ PCON(9) = 0x00000033;
+ PCON(11) |= 0xf;
+ *((uint32_t volatile*)0x38a00000) = 0;
+ *((uint32_t volatile*)0x38700000) = 0;
PWRCON(0) &= ~(1 << 9);
SDCI_RESET = 0xa5;
sleep(HZ / 100);
@@ -567,15 +599,19 @@ static int ata_power_up(void)
SDCI_CDIV = SDCI_CDIV_CLKDIV(260);
*((uint32_t volatile*)0x3cf00200) = 0xb000f;
SDCI_IRQ_MASK = SDCI_IRQ_MASK_MASK_DAT_DONE_INT | SDCI_IRQ_MASK_MASK_IOCARD_IRQ_INT;
- PASS_RC(mmc_init(), 2, 0);
+ PASS_RC(mmc_init(), 3, 0);
SDCI_CDIV = SDCI_CDIV_CLKDIV(4);
sleep(HZ / 100);
- PASS_RC(ceata_init(8), 2, 1);
- PASS_RC(ata_identify(ata_identify_data), 2, 2);
+ PASS_RC(ceata_init(8), 3, 1);
+ PASS_RC(ata_identify(ata_identify_data), 3, 2);
dma_mode = 0x44;
}
else
{
+ PCON(7) = 0x44444444;
+ PCON(8) = 0x44444444;
+ PCON(9) = 0x44444444;
+ PCON(10) = (PCON(10) & ~0xffff) | 0x4444;
PWRCON(0) &= ~(1 << 5);
ATA_CFG = BIT(0);
sleep(HZ / 100);
@@ -591,7 +627,7 @@ static int ata_power_up(void)
ATA_PIO_LHR = 0;
if (!ata_swap) ATA_CFG = BIT(6);
while (!(ATA_PIO_READY & BIT(1))) yield();
- PASS_RC(ata_identify(ata_identify_data), 2, 0);
+ PASS_RC(ata_identify(ata_identify_data), 3, 3);
uint32_t piotime = 0x11f3;
uint32_t mdmatime = 0x1c175;
uint32_t udmatime = 0x5071152;
@@ -648,14 +684,15 @@ static int ata_power_up(void)
}
ata_dma = param ? true : false;
dma_mode = param;
- PASS_RC(ata_set_feature(0xef, param), 2, 1);
- if (ata_identify_data[82] & BIT(5)) PASS_RC(ata_set_feature(0x02, 0), 2, 2);
- if (ata_identify_data[82] & BIT(6)) PASS_RC(ata_set_feature(0x55, 0), 2, 3);
+ PASS_RC(ata_set_feature(0x03, param), 3, 4);
ATA_PIO_TIME = piotime;
ATA_MDMA_TIME = mdmatime;
ATA_UDMA_TIME = udmatime;
}
spinup_time = current_tick - spinup_start;
+ if (ata_identify_data[82] & BIT(5))
+ PASS_RC(ata_set_feature(ata_bbt ? 0x82 : 0x02, 0), 3, 5);
+ if (ata_identify_data[82] & BIT(6)) PASS_RC(ata_set_feature(0xaa, 0), 3, 6);
if (ata_lba48)
ata_total_sectors = ata_identify_data[100]
| (((uint64_t)ata_identify_data[101]) << 16)
@@ -678,6 +715,7 @@ static void ata_power_down(void)
ceata_taskfile[0xf] = 0xe0;
ceata_wait_idle();
ceata_write_multiple_register(0, ceata_taskfile, 16);
+ ceata_wait_idle();
sleep(HZ);
PWRCON(0) |= (1 << 9);
}
@@ -692,6 +730,11 @@ static void ata_power_down(void)
while (!(ATA_CONTROL & BIT(1))) yield();
PWRCON(0) |= (1 << 5);
}
+ PCON(7) = 0;
+ PCON(8) = 0;
+ PCON(9) = 0;
+ PCON(10) &= ~0xffff;
+ PCON(11) &= ~0xf;
ide_power_enable(false);
}
@@ -886,7 +929,7 @@ static int ata_rw_sectors(uint64_t sector, uint32_t count, void* buffer, bool wr
uint32_t cnt;
PASS_RC(ata_bbt_translate(sector, count, &phys, &cnt), 0, 0);
uint32_t offset = phys - sector;
- if (offset != ata_last_offset && phys - ata_last_phys < 64) ata_soft_reset();
+ if (offset != ata_last_offset && phys - ata_last_phys < 64) ata_reset();
ata_last_offset = offset;
ata_last_phys = phys + cnt;
PASS_RC(ata_rw_sectors_internal(phys, cnt, buffer, write), 0, 0);
@@ -912,7 +955,7 @@ int ata_rw_sectors_internal(uint64_t sector, uint32_t count, void* buffer, bool
uint32_t cnt = MIN(ata_lba48 ? 8192 : 32, count);
int rc = -1;
rc = ata_rw_chunk(sector, cnt, buffer, write);
- if (rc && ata_error_srst) ata_soft_reset();
+ if (rc && ata_error_srst) ata_reset();
if (rc && ata_retries)
{
void* buf = buffer;
@@ -924,7 +967,7 @@ int ata_rw_sectors_internal(uint64_t sector, uint32_t count, void* buffer, bool
while (tries-- && rc)
{
rc = ata_rw_chunk(sect, 1, buf, write);
- if (rc && ata_error_srst) ata_soft_reset();
+ if (rc && ata_error_srst) ata_reset();
}
if (rc) break;
buf += SECTOR_SIZE;
@@ -959,7 +1002,7 @@ int ata_soft_reset(void)
{
int rc;
mutex_lock(&ata_mutex);
- if (!ata_powered) ata_power_up();
+ if (!ata_powered) PASS_RC(ata_power_up(), 1, 0);
ata_set_active();
if (ceata) rc = ceata_soft_reset();
else
@@ -967,13 +1010,42 @@ int ata_soft_reset(void)
ata_write_cbr(&ATA_PIO_DAD, BIT(1) | BIT(2));
udelay(10);
ata_write_cbr(&ATA_PIO_DAD, 0);
- rc = ata_wait_for_rdy(20000000);
+ rc = ata_wait_for_rdy(3000000);
}
+ ata_set_active();
+ mutex_unlock(&ata_mutex);
+ PASS_RC(rc, 1, 1);
+ return 0;
+}
+
+int ata_hard_reset(void)
+{
+ mutex_lock(&ata_mutex);
+ PASS_RC(ata_power_up(), 0, 0);
+ ata_set_active();
+ mutex_unlock(&ata_mutex);
+ return 0;
+}
+
+static int ata_reset(void)
+{
+ int rc;
+ mutex_lock(&ata_mutex);
+ if (!ata_powered) PASS_RC(ata_power_up(), 2, 0);
+ ata_set_active();
+ rc = ata_soft_reset();
if (IS_ERR(rc))
{
- ata_power_down();
- sleep(HZ * 3);
- ata_power_up();
+ rc = ata_hard_reset();
+ if (IS_ERR(rc))
+ {
+ rc = ERR_RC((rc << 2) | 1);
+ ata_power_down();
+ sleep(HZ * 3);
+ int rc2 = ata_power_up();
+ if (IS_ERR(rc2)) rc = ERR_RC((rc << 2) | 2);
+ }
+ else rc = 1;
}
ata_set_active();
mutex_unlock(&ata_mutex);
@@ -1052,11 +1124,11 @@ void ata_bbt_disable(void)
mutex_unlock(&ata_mutex);
}
-void ata_bbt_reload(void)
+int ata_bbt_reload(void)
{
mutex_lock(&ata_mutex);
ata_bbt_disable();
- ata_power_up();
+ PASS_RC(ata_power_up(), 1, 0);
uint32_t* buf = (uint32_t*)(ata_bbt_buf + sizeof(ata_bbt_buf) - SECTOR_SIZE);
if (buf)
{
@@ -1064,6 +1136,7 @@ void ata_bbt_reload(void)
ata_virtual_sectors = ata_total_sectors;
else if (!memcmp(buf, "emBIbbth", 8))
{
+ if (ata_identify_data[82] & BIT(5)) PASS_RC(ata_set_feature(0x02, 0), 1, 1);
ata_virtual_sectors = (((uint64_t)buf[0x1fd]) << 32) | buf[0x1fc];
uint32_t count = buf[0x1ff];
if (count > ATA_BBT_PAGES / 64)
@@ -1089,6 +1162,7 @@ void ata_bbt_reload(void)
}
else ata_virtual_sectors = ata_total_sectors;
mutex_unlock(&ata_mutex);
+ return 0;
}
#endif
@@ -1099,29 +1173,11 @@ int ata_init(void)
semaphore_init(&mmc_wakeup, 1, 0);
semaphore_init(&mmc_comp_wakeup, 1, 0);
ceata = PDAT(11) & BIT(1);
- if (ceata)
- {
- ata_lba48 = true;
- ata_dma = true;
- PCON(8) = 0x33333333;
- PCON(9) = (PCON(9) & ~0xff) | 0x33;
- PCON(11) |= 0xf;
- *((uint32_t volatile*)0x38a00000) = 0;
- *((uint32_t volatile*)0x38700000) = 0;
- }
- else
- {
- PCON(7) = 0x44444444;
- PCON(8) = 0x44444444;
- PCON(9) = 0x44444444;
- PCON(10) = (PCON(10) & ~0xffff) | 0x4444;
- }
ata_swap = false;
ata_powered = false;
ata_total_sectors = 0;
- ata_power_up();
#ifdef ATA_HAVE_BBT
- ata_bbt_reload();
+ PASS_RC(ata_bbt_reload(), 0, 0);
#endif
/* HDD data endianness check:
@@ -1191,3 +1247,4 @@ void INT_MMC(void)
if (irq & SDCI_IRQ_IOCARD_IRQ_INT) semaphore_release(&mmc_comp_wakeup);
SDCI_IRQ = irq;
}
+