From 582035c5cbd4f04588615d2f9b91b2f97c80f949 Mon Sep 17 00:00:00 2001 From: Michael Sparmann Date: Fri, 18 Jul 2014 23:15:21 +0200 Subject: 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 Tested: Nial Shui Tested: Michael Sparmann Reviewed-by: Marcin Bukat --- .../target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c | 159 ++++++++++++++------- 1 file changed, 108 insertions(+), 51 deletions(-) mode change 100644 => 100755 firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c diff --git a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c old mode 100644 new mode 100755 index 0fd74787d1..955a33a595 --- 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; } + -- cgit v1.2.3