static int ceata_write_multiple_register(uint32_t addr, void* dest, uint32_t size) { uint32_t i; if (size > 0x10) RET_ERR(0); mmc_discard_irq(); SDCI_DMASIZE = size; SDCI_DMACOUNT = 0; SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_REG) | SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_CMD_RD_WR | SDCI_CMD_RES_BUSY | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, MMC_CMD_CEATA_RW_MULTIPLE_REG_DIRECTION_WRITE | MMC_CMD_CEATA_RW_MULTIPLE_REG_ADDRESS(addr & 0xfc) | MMC_CMD_CEATA_RW_MULTIPLE_REG_COUNT(size & 0xfc), NULL, CEATA_COMMAND_TIMEOUT), 3, 1); SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX; for (i = 0; i < size / 4; i++) SDCI_DATA = ((uint32_t*)dest)[i]; long startusec = USEC_TIMER; if (semaphore_wait(&mmc_wakeup, CEATA_COMMAND_TIMEOUT * HZ / 1000000) == OBJ_WAIT_TIMEDOUT) RET_ERR(2); while ((SDCI_STATE & SDCI_STATE_DAT_STATE_MASK) != SDCI_STATE_DAT_STATE_IDLE) { if (TIMEOUT_EXPIRED(startusec, CEATA_COMMAND_TIMEOUT)) RET_ERR(3); yield(); } PASS_RC(mmc_dsta_check_data_success(), 3, 4); return 0; }
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); return 0; }
static int ceata_check_error(void) { uint32_t status, error; PASS_RC(mmc_fastio_read(0xf, &status), 2, 0); if (status & 1) { PASS_RC(mmc_fastio_read(0x9, &error), 2, 1); RET_ERR((error << 2) | 2); } return 0; }
static int ceata_cancel_command(void) { *((uint32_t volatile*)0x3cf00200) = 0x9000e; udelay(1); *((uint32_t volatile*)0x3cf00200) = 0x9000f; udelay(1); *((uint32_t volatile*)0x3cf00200) = 0x90003; udelay(1); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_STOP_TRANSMISSION) | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_BUSY | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, 0, NULL, CEATA_COMMAND_TIMEOUT), 1, 0); PASS_RC(ceata_wait_idle(), 1, 1); return 0; }
static int ceata_soft_reset(void) { PASS_RC(mmc_fastio_write(6, 4), 2, 0); sleep(HZ / 100); PASS_RC(mmc_fastio_write(6, 0), 2, 1); sleep(HZ / 100); long startusec = USEC_TIMER; uint32_t status; do { PASS_RC(mmc_fastio_read(0xf, &status), 2, 2); if (TIMEOUT_EXPIRED(startusec, CEATA_POWERUP_TIMEOUT)) RET_ERR(3); sleep(HZ / 100); } while (status & 0x80); return 0; }
static int ata_wait_for_end_of_transfer(long timeout) { PASS_RC(ata_wait_for_not_bsy(timeout), 2, 0); uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); if (dad & BIT(0)) RET_ERR(1); if ((dad & (BIT(3) | BITRANGE(5, 7))) == BIT(6)) return 0; RET_ERR(2); }
static int ceata_rw_multiple_block(bool write, void* buf, uint32_t count, long timeout) { mmc_discard_irq(); uint32_t responsetype; uint32_t cmdtype; uint32_t direction; if (write) { cmdtype = SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_CMD_RD_WR; responsetype = SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_BUSY; direction = MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_DIRECTION_WRITE; } else { cmdtype = SDCI_CMD_CMD_TYPE_ADTC; responsetype = SDCI_CMD_RES_TYPE_R1; direction = MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_DIRECTION_READ; } SDCI_DMASIZE = 0x200; SDCI_DMAADDR = buf; SDCI_DMACOUNT = count; SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; commit_discard_dcache(); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_BLOCK) | 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); if (write) SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX; if (semaphore_wait(&mmc_wakeup, timeout) == OBJ_WAIT_TIMEDOUT) { PASS_RC(ceata_cancel_command(), 4, 1); RET_ERR(2); } PASS_RC(mmc_dsta_check_data_success(), 4, 3); if (semaphore_wait(&mmc_comp_wakeup, timeout) == OBJ_WAIT_TIMEDOUT) { PASS_RC(ceata_cancel_command(), 4, 4); RET_ERR(4); } PASS_RC(ceata_check_error(), 4, 5); return 0; }
static int ata_wait_for_rdy(long timeout) { long startusec = USEC_TIMER; PASS_RC(ata_wait_for_not_bsy(timeout), 1, 0); while (true) { uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); if (dad & BIT(6)) return 0; if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(1); } }
static int ata_rw_chunk_internal(uint64_t sector, uint32_t cnt, void* buffer, bool write) { if (ceata) { memset(ceata_taskfile, 0, 16); ceata_taskfile[0x2] = cnt >> 5; ceata_taskfile[0x3] = sector >> 21; ceata_taskfile[0x4] = sector >> 29; ceata_taskfile[0x5] = sector >> 37; ceata_taskfile[0xa] = cnt << 3; ceata_taskfile[0xb] = sector << 3; ceata_taskfile[0xc] = sector >> 5; ceata_taskfile[0xd] = sector >> 13; ceata_taskfile[0xf] = write ? 0x35 : 0x25; PASS_RC(ceata_wait_idle(), 2, 0); PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); PASS_RC(ceata_rw_multiple_block(write, buffer, cnt << 3, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 2, 2); } else {
static int ceata_wait_idle(void) { long startusec = USEC_TIMER; while (true) { uint32_t status; PASS_RC(mmc_fastio_read(0xf, &status), 1, 0); if (!(status & 0x88)) return 0; if (TIMEOUT_EXPIRED(startusec, CEATA_DAT_NONBUSY_TIMEOUT)) RET_ERR(1); sleep(HZ / 20); } }
static int ceata_read_multiple_register(uint32_t addr, void* dest, uint32_t size) { if (size > 0x10) RET_ERR(0); mmc_discard_irq(); SDCI_DMASIZE = size; SDCI_DMACOUNT = 1; SDCI_DMAADDR = dest; SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; commit_discard_dcache(); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_REG) | SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, MMC_CMD_CEATA_RW_MULTIPLE_REG_DIRECTION_READ | MMC_CMD_CEATA_RW_MULTIPLE_REG_ADDRESS(addr & 0xfc) | MMC_CMD_CEATA_RW_MULTIPLE_REG_COUNT(size & 0xfc), NULL, CEATA_COMMAND_TIMEOUT), 2, 1); if (semaphore_wait(&mmc_wakeup, CEATA_COMMAND_TIMEOUT * HZ / 1000000) == OBJ_WAIT_TIMEDOUT) RET_ERR(2); PASS_RC(mmc_dsta_check_data_success(), 2, 3); return 0; }
static int ata_wait_for_start_of_transfer(long timeout) { long startusec = USEC_TIMER; PASS_RC(ata_wait_for_not_bsy(timeout), 2, 0); while (true) { uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); if (dad & BIT(0)) RET_ERR(1); if ((dad & (BIT(7) | BIT(3))) == BIT(3)) return 0; if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(2); } }
static int ata_set_feature(uint32_t feature, uint32_t param) { 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 ceata_init(int buswidth) { uint32_t result; PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SWITCH) | SDCI_CMD_RES_BUSY | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, MMC_CMD_SWITCH_ACCESS_WRITE_BYTE | MMC_CMD_SWITCH_INDEX(MMC_CMD_SWITCH_FIELD_HS_TIMING) | MMC_CMD_SWITCH_VALUE(MMC_CMD_SWITCH_FIELD_HS_TIMING_HIGH_SPEED), &result, CEATA_COMMAND_TIMEOUT), 3, 0); if (result & MMC_STATUS_SWITCH_ERROR) RET_ERR(1); if (buswidth > 1) { int setting; if (buswidth == 4) setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_4BIT; else if (buswidth == 8) setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_8BIT; else setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_1BIT; PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SWITCH) | SDCI_CMD_RES_BUSY | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, MMC_CMD_SWITCH_ACCESS_WRITE_BYTE | MMC_CMD_SWITCH_INDEX(MMC_CMD_SWITCH_FIELD_BUS_WIDTH) | MMC_CMD_SWITCH_VALUE(setting), &result, CEATA_COMMAND_TIMEOUT), 3, 2); if (result & MMC_STATUS_SWITCH_ERROR) RET_ERR(3); if (buswidth == 4) SDCI_CTRL = (SDCI_CTRL & ~SDCI_CTRL_BUS_WIDTH_MASK) | SDCI_CTRL_BUS_WIDTH_4BIT; else if (buswidth == 8) SDCI_CTRL = (SDCI_CTRL & ~SDCI_CTRL_BUS_WIDTH_MASK) | SDCI_CTRL_BUS_WIDTH_8BIT; } PASS_RC(ceata_soft_reset(), 3, 4); PASS_RC(ceata_read_multiple_register(0, ceata_taskfile, 0x10), 3, 5); if (ceata_taskfile[0xc] != 0xce || ceata_taskfile[0xd] != 0xaa) RET_ERR(6); PASS_RC(mmc_fastio_write(6, 0), 3, 7); return 0; }
static int ata_identify(uint16_t* buf) { int i; if (ceata) { memset(ceata_taskfile, 0, 16); ceata_taskfile[0xf] = 0xec; PASS_RC(ceata_wait_idle(), 2, 0); PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 2, 2); } else { uint32_t old = ATA_CFG; ATA_CFG |= BIT(6); PASS_RC(ata_wait_for_not_bsy(10000000), 1, 0); ata_write_cbr(&ATA_PIO_DVR, 0); ata_write_cbr(&ATA_PIO_CSD, 0xec); PASS_RC(ata_wait_for_start_of_transfer(10000000), 1, 1); for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR); ATA_CFG = old; } return 0; }
static bool mmc_send_command(uint32_t cmd, uint32_t arg, uint32_t* result, int timeout) { long starttime = USEC_TIMER; while ((SDCI_STATE & SDCI_STATE_CMD_STATE_MASK) != SDCI_STATE_CMD_STATE_CMD_IDLE) { if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(0); yield(); } SDCI_STAC = SDCI_STAC_CLR_CMDEND | SDCI_STAC_CLR_BIT_3 | SDCI_STAC_CLR_RESEND | SDCI_STAC_CLR_DATEND | SDCI_STAC_CLR_DAT_CRCEND | SDCI_STAC_CLR_CRC_STAEND | SDCI_STAC_CLR_RESTOUTE | SDCI_STAC_CLR_RESENDE | SDCI_STAC_CLR_RESINDE | SDCI_STAC_CLR_RESCRCE | SDCI_STAC_CLR_WR_DATCRCE | SDCI_STAC_CLR_RD_DATCRCE | SDCI_STAC_CLR_RD_DATENDE0 | SDCI_STAC_CLR_RD_DATENDE1 | SDCI_STAC_CLR_RD_DATENDE2 | SDCI_STAC_CLR_RD_DATENDE3 | SDCI_STAC_CLR_RD_DATENDE4 | SDCI_STAC_CLR_RD_DATENDE5 | SDCI_STAC_CLR_RD_DATENDE6 | SDCI_STAC_CLR_RD_DATENDE7; SDCI_ARGU = arg; SDCI_CMD = cmd; if (!(SDCI_DSTA & SDCI_DSTA_CMDRDY)) RET_ERR(1); SDCI_CMD = cmd | SDCI_CMD_CMDSTR; long sleepbase = USEC_TIMER; while (TIMEOUT_EXPIRED(sleepbase, 1000)) yield(); while (!(SDCI_DSTA & SDCI_DSTA_CMDEND)) { if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(2); yield(); } if ((cmd & SDCI_CMD_RES_TYPE_MASK) != SDCI_CMD_RES_TYPE_NONE) { while (!(SDCI_DSTA & SDCI_DSTA_RESEND)) { if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(3); yield(); } if (cmd & SDCI_CMD_RES_BUSY) while (SDCI_DSTA & SDCI_DSTA_DAT_BUSY) { if (TIMEOUT_EXPIRED(starttime, CEATA_DAT_NONBUSY_TIMEOUT)) RET_ERR(4); yield(); } } bool nocrc = (cmd & SDCI_CMD_RES_SIZE_MASK) == SDCI_CMD_RES_SIZE_136; PASS_RC(mmc_dsta_check_command_success(nocrc), 3, 5); if (result) *result = SDCI_RESP0; return 0; }
static int mmc_init(void) { sleep(HZ / 10); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_GO_IDLE_STATE) | SDCI_CMD_CMD_TYPE_BC | SDCI_CMD_RES_TYPE_NONE | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NID, 0, NULL, CEATA_COMMAND_TIMEOUT), 3, 0); long startusec = USEC_TIMER; uint32_t result; do { if (TIMEOUT_EXPIRED(startusec, CEATA_POWERUP_TIMEOUT)) RET_ERR(1); sleep(HZ / 100); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SEND_OP_COND) | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R3 | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NID, MMC_CMD_SEND_OP_COND_OCR(MMC_OCR_270_360), NULL, CEATA_COMMAND_TIMEOUT), 3, 2); result = SDCI_RESP0; } while (!(result & MMC_OCR_POWER_UP_DONE)); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_ALL_SEND_CID) | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R2 | SDCI_CMD_RES_SIZE_136 | SDCI_CMD_NCR_NID_NID, 0, NULL, CEATA_COMMAND_TIMEOUT), 3, 3); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SET_RELATIVE_ADDR) | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, MMC_CMD_SET_RELATIVE_ADDR_RCA(CEATA_MMC_RCA), NULL, CEATA_COMMAND_TIMEOUT), 3, 4); PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SELECT_CARD) | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, MMC_CMD_SELECT_CARD_RCA(CEATA_MMC_RCA), NULL, CEATA_COMMAND_TIMEOUT), 3, 5); PASS_RC(mmc_get_card_status(&result), 3, 6); if ((result & MMC_STATUS_CURRENT_STATE_MASK) != MMC_STATUS_CURRENT_STATE_TRAN) RET_ERR(7); 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) { PWRCON(0) &= ~(1 << 9); SDCI_RESET = 0xa5; sleep(HZ / 100); *((uint32_t volatile*)0x3cf00380) = 0; *((uint32_t volatile*)0x3cf0010c) = 0xff; SDCI_CTRL = SDCI_CTRL_SDCIEN | SDCI_CTRL_CLK_SEL_SDCLK | SDCI_CTRL_BIT_8 | SDCI_CTRL_BIT_14; 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); 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); dma_mode = 0x44; } else { PWRCON(0) &= ~(1 << 5); ATA_CFG = BIT(0); sleep(HZ / 100); ATA_CFG = 0; sleep(HZ / 100); ATA_SWRST = BIT(0); sleep(HZ / 100); ATA_SWRST = 0; sleep(HZ / 10); ATA_CONTROL = BIT(0); sleep(HZ / 5); ATA_PIO_TIME = 0x191f7; 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); uint32_t piotime = 0x11f3; uint32_t mdmatime = 0x1c175; uint32_t udmatime = 0x5071152; uint32_t param = 0; ata_dma_flags = 0; ata_lba48 = ata_identify_data[83] & BIT(10) ? true : false; if (ata_identify_data[53] & BIT(1)) { if (ata_identify_data[64] & BIT(1)) piotime = 0x2072; else if (ata_identify_data[64] & BIT(0)) piotime = 0x7083; } if (ata_identify_data[63] & BIT(2)) { mdmatime = 0x5072; param = 0x22; } else if (ata_identify_data[63] & BIT(1)) { mdmatime = 0x7083; param = 0x21; } if (ata_identify_data[63] & BITRANGE(0, 2)) { ata_dma_flags = BIT(3) | BIT(10); param |= 0x20; } if (ata_identify_data[53] & BIT(2)) { if (ata_identify_data[88] & BIT(4)) { udmatime = 0x2010a52; param = 0x44; } else if (ata_identify_data[88] & BIT(3)) { udmatime = 0x2020a52; param = 0x43; } else if (ata_identify_data[88] & BIT(2)) { udmatime = 0x3030a52; param = 0x42; } else if (ata_identify_data[88] & BIT(1)) { udmatime = 0x3050a52; param = 0x41; } if (ata_identify_data[88] & BITRANGE(0, 4)) { ata_dma_flags = BIT(2) | BIT(3) | BIT(9) | BIT(10); param |= 0x40; } } 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); ATA_PIO_TIME = piotime; ATA_MDMA_TIME = mdmatime; ATA_UDMA_TIME = udmatime; } spinup_time = current_tick - spinup_start; if (ata_lba48) ata_total_sectors = ata_identify_data[100] | (((uint64_t)ata_identify_data[101]) << 16) | (((uint64_t)ata_identify_data[102]) << 32) | (((uint64_t)ata_identify_data[103]) << 48); else ata_total_sectors = ata_identify_data[60] | (((uint32_t)ata_identify_data[61]) << 16); ata_total_sectors >>= 3; ata_powered = true; ata_set_active(); return 0; }