mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-09 13:15:18 -05:00
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>
This commit is contained in:
parent
3e131abe27
commit
582035c5cb
1 changed files with 108 additions and 51 deletions
159
firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c
Normal file → Executable file
159
firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c
Normal file → Executable file
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue