mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-13 18:17:39 -04:00
Cortex-M processors don't have an MMU, but can still have caches that need software management, so on those platforms we don't want to include the MMU related functions. While here, remove an outdated section of a comment referring to deprecated cache maintenance functions which no longer exist. Change-Id: I6f0fe694560bdee25ed7c69a846bf46e3e544cb1
1313 lines
40 KiB
C
1313 lines
40 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2007 Dave Chapman
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
//#define LOGF_ENABLE
|
|
|
|
#include "config.h"
|
|
#include "storage.h"
|
|
#include "timer.h"
|
|
#include "kernel.h"
|
|
#include "string.h"
|
|
#include "power.h"
|
|
#include "panic.h"
|
|
#include "mmcdefs-target.h"
|
|
#include "s5l87xx.h"
|
|
#include "led.h"
|
|
#include "debug.h"
|
|
#include "panic.h"
|
|
#include "fs_defines.h"
|
|
#include "logf.h"
|
|
|
|
#ifndef ATA_RETRIES
|
|
#define ATA_RETRIES 3
|
|
#endif
|
|
|
|
#define CMD_READ_SECTORS 0x20
|
|
#define CMD_READ_DMA_EXT 0x25
|
|
#define CMD_READ_MULTIPLE_EXT 0x29
|
|
#define CMD_WRITE_SECTORS 0x30
|
|
#define CMD_WRITE_DMA_EXT 0x35
|
|
#define CMD_WRITE_MULTIPLE_EXT 0x39
|
|
#ifdef HAVE_ATA_SMART
|
|
#define CMD_SMART 0xB0
|
|
#endif
|
|
#define CMD_READ_MULTIPLE 0xC4
|
|
#define CMD_WRITE_MULTIPLE 0xC5
|
|
#define CMD_READ_DMA 0xC8
|
|
#define CMD_WRITE_DMA 0xCA
|
|
#define CMD_STANDBY_IMMEDIATE 0xE0
|
|
#define CMD_FLUSH_CACHE 0xE7
|
|
#define CMD_FLUSH_CACHE_EXT 0xEA
|
|
#define CMD_IDENTIFY 0xEC
|
|
#define CMD_SET_FEATURES 0xEF
|
|
|
|
#define CEATA_POWERUP_TIMEOUT 20000000
|
|
#define CEATA_COMMAND_TIMEOUT 1000000
|
|
#define CEATA_DAT_NONBUSY_TIMEOUT 5000000
|
|
#define CEATA_MMC_RCA 1
|
|
|
|
/** static, private data **/
|
|
static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR;
|
|
static uint16_t identify_info[ATA_IDENTIFY_WORDS] STORAGE_ALIGN_ATTR;
|
|
static bool ceata;
|
|
static bool ata_lba48;
|
|
static bool ata_dma;
|
|
static uint64_t ata_total_sectors;
|
|
static uint32_t log_sector_size;
|
|
static struct mutex ata_mutex;
|
|
static struct semaphore ata_wakeup;
|
|
static long ata_last_activity_value = -1;
|
|
static long ata_sleep_timeout = 7 * HZ;
|
|
static bool ata_powered;
|
|
static bool canflush = true;
|
|
static struct semaphore mmc_wakeup;
|
|
static struct semaphore mmc_comp_wakeup;
|
|
static int spinup_time = 0;
|
|
#ifdef HAVE_ATA_DMA
|
|
static int dma_mode = 0;
|
|
static uint32_t ata_dma_flags;
|
|
#endif
|
|
|
|
static const int ata_retries = ATA_RETRIES;
|
|
static const bool ata_error_srst = true;
|
|
|
|
static int ata_reset(void);
|
|
|
|
static uint16_t ata_read_cbr(uint32_t volatile* reg)
|
|
{
|
|
while (!(ATA_PIO_READY & 2));
|
|
volatile uint32_t dummy __attribute__((unused)) = *reg;
|
|
while (!(ATA_PIO_READY & 1));
|
|
return ATA_PIO_RDATA;
|
|
}
|
|
|
|
static void ata_write_cbr(uint32_t volatile* reg, uint16_t data)
|
|
{
|
|
while (!(ATA_PIO_READY & 2));
|
|
*reg = data;
|
|
}
|
|
|
|
static int ata_wait_for_not_bsy(long timeout)
|
|
{
|
|
long startusec = USEC_TIMER;
|
|
while (true)
|
|
{
|
|
uint8_t csd = ata_read_cbr(&ATA_PIO_CSD);
|
|
if (!(csd & BIT(7)))
|
|
return 0;
|
|
if (TIMEOUT_EXPIRED(startusec, timeout))
|
|
RET_ERR(0);
|
|
yield();
|
|
}
|
|
}
|
|
|
|
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);
|
|
yield();
|
|
}
|
|
}
|
|
|
|
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);
|
|
yield();
|
|
}
|
|
}
|
|
|
|
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 mmc_dsta_check_command_success(bool disable_crc)
|
|
{
|
|
int rc = 0;
|
|
uint32_t dsta = SDCI_DSTA;
|
|
if (dsta & SDCI_DSTA_RESTOUTE)
|
|
rc |= 1;
|
|
if (dsta & SDCI_DSTA_RESENDE)
|
|
rc |= 2;
|
|
if (dsta & SDCI_DSTA_RESINDE)
|
|
rc |= 4;
|
|
if (!disable_crc)
|
|
if (dsta & SDCI_DSTA_RESCRCE)
|
|
rc |= 8;
|
|
if (rc)
|
|
RET_ERR(rc);
|
|
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_get_card_status(uint32_t* result)
|
|
{
|
|
return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SEND_STATUS)
|
|
| SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1
|
|
| SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR,
|
|
MMC_CMD_SEND_STATUS_RCA(CEATA_MMC_RCA), result, CEATA_COMMAND_TIMEOUT);
|
|
}
|
|
|
|
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 mmc_fastio_write(uint32_t addr, uint32_t data)
|
|
{
|
|
return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_FAST_IO)
|
|
| SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R4
|
|
| SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR,
|
|
MMC_CMD_FAST_IO_RCA(CEATA_MMC_RCA) | MMC_CMD_FAST_IO_DIRECTION_WRITE
|
|
| MMC_CMD_FAST_IO_ADDRESS(addr) | MMC_CMD_FAST_IO_DATA(data),
|
|
NULL, CEATA_COMMAND_TIMEOUT);
|
|
}
|
|
|
|
static int mmc_fastio_read(uint32_t addr, uint32_t* data)
|
|
{
|
|
return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_FAST_IO)
|
|
| SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R4
|
|
| SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR,
|
|
MMC_CMD_FAST_IO_RCA(CEATA_MMC_RCA) | MMC_CMD_FAST_IO_DIRECTION_READ
|
|
| MMC_CMD_FAST_IO_ADDRESS(addr), data, CEATA_COMMAND_TIMEOUT);
|
|
}
|
|
|
|
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 mmc_dsta_check_data_success(void)
|
|
{
|
|
int rc = 0;
|
|
uint32_t dsta = SDCI_DSTA;
|
|
if (dsta & (SDCI_DSTA_WR_DATCRCE | SDCI_DSTA_RD_DATCRCE))
|
|
{
|
|
if (dsta & SDCI_DSTA_WR_DATCRCE)
|
|
rc |= 1;
|
|
if (dsta & SDCI_DSTA_RD_DATCRCE)
|
|
rc |= 2;
|
|
if ((dsta & SDCI_DSTA_WR_CRC_STATUS_MASK) == SDCI_DSTA_WR_CRC_STATUS_TXERR)
|
|
rc |= 4;
|
|
else if ((dsta & SDCI_DSTA_WR_CRC_STATUS_MASK) == SDCI_DSTA_WR_CRC_STATUS_CARDERR)
|
|
rc |= 8;
|
|
}
|
|
if (dsta & (SDCI_DSTA_RD_DATENDE0 | SDCI_DSTA_RD_DATENDE1 | SDCI_DSTA_RD_DATENDE2
|
|
| SDCI_DSTA_RD_DATENDE3 | SDCI_DSTA_RD_DATENDE4 | SDCI_DSTA_RD_DATENDE5
|
|
| SDCI_DSTA_RD_DATENDE6 | SDCI_DSTA_RD_DATENDE7))
|
|
rc |= 16;
|
|
if (rc)
|
|
RET_ERR(rc);
|
|
return 0;
|
|
}
|
|
|
|
static void mmc_discard_irq(void)
|
|
{
|
|
SDCI_IRQ = SDCI_IRQ_DAT_DONE_INT | SDCI_IRQ_MASK_MASK_IOCARD_IRQ_INT
|
|
| SDCI_IRQ_MASK_MASK_READ_WAIT_INT;
|
|
semaphore_wait(&mmc_wakeup, 0);
|
|
}
|
|
|
|
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 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 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 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_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_cancel_command(void)
|
|
{
|
|
GPIOCMD = 0x9000e;
|
|
udelay(1);
|
|
GPIOCMD = 0x9000f;
|
|
udelay(1);
|
|
GPIOCMD = 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_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), 3, 0);
|
|
if (write)
|
|
SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX;
|
|
if (semaphore_wait(&mmc_wakeup, timeout) == OBJ_WAIT_TIMEDOUT)
|
|
{
|
|
PASS_RC(ceata_cancel_command(), 3, 1);
|
|
RET_ERR(2);
|
|
}
|
|
PASS_RC(mmc_dsta_check_data_success(), 3, 3);
|
|
if (semaphore_wait(&mmc_comp_wakeup, timeout) == OBJ_WAIT_TIMEDOUT)
|
|
{
|
|
PASS_RC(ceata_cancel_command(), 3, 4);
|
|
RET_ERR(5);
|
|
}
|
|
PASS_RC(ceata_check_error(), 3, 6);
|
|
return 0;
|
|
}
|
|
|
|
static int ata_identify(uint16_t* buf)
|
|
{
|
|
int i;
|
|
if (ceata)
|
|
{
|
|
memset(ceata_taskfile, 0, 16);
|
|
ceata_taskfile[0xf] = CMD_IDENTIFY;
|
|
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
|
|
{
|
|
PASS_RC(ata_wait_for_not_bsy(10000000), 1, 0);
|
|
ata_write_cbr(&ATA_PIO_DVR, 0);
|
|
ata_write_cbr(&ATA_PIO_CSD, CMD_IDENTIFY);
|
|
PASS_RC(ata_wait_for_start_of_transfer(10000000), 1, 1);
|
|
for (i = 0; i < ATA_IDENTIFY_WORDS; i++)
|
|
buf[i] = ata_read_cbr(&ATA_PIO_DTR);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ata_set_active(void)
|
|
{
|
|
ata_last_activity_value = current_tick;
|
|
}
|
|
|
|
bool ata_disk_is_active(void)
|
|
{
|
|
return ata_powered;
|
|
}
|
|
|
|
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] = CMD_SET_FEATURES;
|
|
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, CMD_SET_FEATURES);
|
|
PASS_RC(ata_wait_for_rdy(2000000), 2, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_ATA_DMA
|
|
static int udmatimes[ATA_MAX_UDMA + 1] = {
|
|
0x4071152,
|
|
0x2050d52,
|
|
#if ATA_MAX_UDMA >= 2
|
|
0x2030a52,
|
|
#endif
|
|
#if ATA_MAX_UDMA >= 3
|
|
0x1020a52,
|
|
#endif
|
|
#if ATA_MAX_UDMA >= 4
|
|
0x1010a52,
|
|
#endif
|
|
};
|
|
static int mwdmatimes[ATA_MAX_MWDMA + 1] = {
|
|
0x1c175,
|
|
0x7083,
|
|
0x5072,
|
|
};
|
|
|
|
static int ata_get_best_mode(unsigned short identword, int max, int modetype)
|
|
{
|
|
unsigned short testbit = BIT_N(max);
|
|
|
|
while (1) {
|
|
if (identword & testbit)
|
|
return max | modetype;
|
|
testbit >>= 1;
|
|
if (!testbit)
|
|
return 0;
|
|
max--;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* ATA_UDMA_TIME register is documented on s3c6400 datasheet, information
|
|
* included in s5l8700 datasheet is wrong or not valid for s5l8702.
|
|
*
|
|
* On Classic (boosted):
|
|
* HClk = 108 MHz. -> T = ~9.26 ns.
|
|
*
|
|
* Configured values (in nanoseconds):
|
|
*
|
|
* UDMA ATA_UDMA_TIME tACK tRP tSS tDVS tDVH Tcyc WR(MB/s)
|
|
* 0 0x4071152 27.8 166.7 55.6 74.1 46.3 120.4 16.6
|
|
* 1 0x2050d52 27.8 129.6 55.6 55.6 27.8 83.4 24
|
|
* 2 0x2030a52 27.8 101.8 55.6 37 27.8 64.8 30.9
|
|
* 3 0x1020a52 27.8 101.8 55.6 27.8 18.5 46.3 43.2
|
|
* 4 0x1010a52 27.8 101.8 55.6 18.5 18.5 37 54
|
|
*
|
|
* Tcyc = tDVS+tDVH
|
|
* WR[bytes/s] = 1/Tcyc[s] * 2[bytes]
|
|
*/
|
|
static int ata_power_up(void)
|
|
{
|
|
logf("ata POWERUP %ld", current_tick);
|
|
|
|
ata_set_active();
|
|
ide_power_enable(true);
|
|
long spinup_start = current_tick;
|
|
if (ceata) {
|
|
ata_lba48 = true;
|
|
ata_dma = true;
|
|
#ifdef HAVE_ATA_DMA
|
|
dma_mode = 0xff; /* Canary */
|
|
#endif
|
|
PCON(8) = 0x33333333;
|
|
PCON(9) = 0x00000033;
|
|
PCON(11) |= 0xf;
|
|
ATA_UNKNOWN = 0;
|
|
ATA_CONTROL = 0;
|
|
PWRCON(0) &= ~(1 << 9);
|
|
SDCI_RESET = 0xa5;
|
|
sleep(HZ / 100);
|
|
GPIOUNK380 = 0;
|
|
PUNK8 = 0xff;
|
|
SDCI_CTRL = SDCI_CTRL_SDCIEN | SDCI_CTRL_CLK_SEL_SDCLK
|
|
| SDCI_CTRL_BIT_8 | SDCI_CTRL_BIT_14;
|
|
SDCI_CLKDIV = SDCI_CDIV_CLKDIV(260);
|
|
GPIOCMD = 0xb000f;
|
|
SDCI_IRQ_MASK = SDCI_IRQ_MASK_MASK_DAT_DONE_INT | SDCI_IRQ_MASK_MASK_IOCARD_IRQ_INT;
|
|
PASS_RC(mmc_init(), 3, 0);
|
|
SDCI_CLKDIV = SDCI_CDIV_CLKDIV(4);
|
|
sleep(HZ / 100);
|
|
PASS_RC(ceata_init(8), 3, 1);
|
|
PASS_RC(ata_identify(identify_info), 3, 2);
|
|
} 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);
|
|
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;
|
|
ATA_CFG = BIT(6);
|
|
while (!(ATA_PIO_READY & BIT(1))) yield();
|
|
|
|
PASS_RC(ata_identify(identify_info), 3, 3);
|
|
|
|
uint32_t piotime = 0x11f3; /* PIO0-2? */
|
|
if (identify_info[53] & BIT(1)) /* Word 64..70 valid */
|
|
{
|
|
if (identify_info[64] & BIT(1))
|
|
piotime = 0x2072; /* PIO mode 4 */
|
|
else if (identify_info[64] & BIT(0))
|
|
piotime = 0x7083; /* PIO mode 3 */
|
|
}
|
|
ATA_PIO_TIME = piotime;
|
|
|
|
uint32_t param = 0;
|
|
#ifdef HAVE_ATA_DMA
|
|
ata_dma_flags = 0;
|
|
if ((identify_info[53] & BIT(2)) && (identify_info[88] & BITRANGE(0, 4))) /* Any UDMA */
|
|
{
|
|
int max_udma = ATA_MAX_UDMA;
|
|
#if ATA_MAX_UDMA > 2
|
|
if (!identify_info[76] && !(identify_info[93] & BIT(13))) /* w93b13 is only valid for PATA, w76 is 0 PATA */
|
|
max_udma = 2;
|
|
#endif
|
|
param = ata_get_best_mode(identify_info[88], max_udma, 0x40);
|
|
ATA_UDMA_TIME = udmatimes[param & 0xf];
|
|
ata_dma_flags = BIT(2) | BIT(3) | BIT(9) | BIT(10);
|
|
}
|
|
if (!param && identify_info[63] & BITRANGE(0, 2)) /* Fall back to any MWDMA */
|
|
{
|
|
param = ata_get_best_mode(identify_info[63], ATA_MAX_MWDMA, 0x20);
|
|
ATA_MDMA_TIME = mwdmatimes[param & 0xf];
|
|
ata_dma_flags = BIT(3) | BIT(10);
|
|
}
|
|
dma_mode = param;
|
|
#endif /* HAVE_ATA_DMA */
|
|
ata_dma = param ? true : false;
|
|
PASS_RC(ata_set_feature(0x03, param), 3, 4); /* Transfer mode */
|
|
|
|
/* SET_FEATURE only supported on PATA, not CE-ATA */
|
|
if (identify_info[82] & BIT(5))
|
|
PASS_RC(ata_set_feature(0x02, 0), 3, 5); /* Enable volatile write cache */
|
|
if (identify_info[82] & BIT(6))
|
|
PASS_RC(ata_set_feature(0xaa, 0), 3, 6); /* Enable read lookahead */
|
|
if (identify_info[83] & BIT(3))
|
|
PASS_RC(ata_set_feature(0x05, 0x80), 3, 7); /* Enable lowest power mode w/o standby */
|
|
if (identify_info[83] & BIT(9))
|
|
PASS_RC(ata_set_feature(0x42, 0x80), 3, 8); /* Enable lowest noise mode */
|
|
|
|
PASS_RC(ata_identify(identify_info), 3, 9); /* Finally, re-read identify info */
|
|
}
|
|
|
|
spinup_time = current_tick - spinup_start;
|
|
|
|
ata_total_sectors = (identify_info[61] << 16) | identify_info[60];
|
|
if ( identify_info[83] & BIT(10) && ata_total_sectors == 0x0FFFFFFF)
|
|
{
|
|
ata_total_sectors = ((uint64_t)identify_info[103] << 48) |
|
|
((uint64_t)identify_info[102] << 32) |
|
|
((uint64_t)identify_info[101] << 16) |
|
|
identify_info[100];
|
|
ata_lba48 = true;
|
|
} else {
|
|
ata_lba48 = false;
|
|
}
|
|
|
|
ata_powered = true;
|
|
ata_set_active();
|
|
return 0;
|
|
}
|
|
|
|
static void ata_power_down(void)
|
|
{
|
|
if (!ata_powered)
|
|
return;
|
|
|
|
logf("ata POWERDOWN %ld", current_tick);
|
|
|
|
PCON(7) = 0;
|
|
PCON(8) = 0;
|
|
PCON(9) = 0;
|
|
PCON(10) &= ~0xffff;
|
|
PCON(11) &= ~0xf;
|
|
ide_power_enable(false);
|
|
ata_powered = false;
|
|
}
|
|
|
|
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 >> 8;
|
|
ceata_taskfile[0x3] = sector >> 24;
|
|
ceata_taskfile[0x4] = sector >> 32;
|
|
ceata_taskfile[0x5] = sector >> 40;
|
|
ceata_taskfile[0xa] = cnt;
|
|
ceata_taskfile[0xb] = sector;
|
|
ceata_taskfile[0xc] = sector >> 8;
|
|
ceata_taskfile[0xd] = sector >> 16;
|
|
ceata_taskfile[0xf] = write ? CMD_WRITE_DMA_EXT : CMD_READ_DMA_EXT;
|
|
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, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 2, 2);
|
|
}
|
|
else
|
|
{
|
|
PASS_RC(ata_wait_for_rdy(100000), 2, 0);
|
|
ata_write_cbr(&ATA_PIO_DVR, 0);
|
|
if (ata_lba48)
|
|
{
|
|
ata_write_cbr(&ATA_PIO_SCR, (cnt >> 8) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_SCR, (cnt) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LHR, (sector >> 40) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LMR, (sector >> 32) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LLR, (sector >> 24) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LHR, (sector >> 16) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LMR, (sector >> 8) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LLR, (sector) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_DVR, BIT(6));
|
|
if (write)
|
|
ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_WRITE_DMA_EXT : CMD_WRITE_MULTIPLE_EXT);
|
|
else
|
|
ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_READ_DMA_EXT : CMD_READ_MULTIPLE_EXT);
|
|
}
|
|
else
|
|
{
|
|
ata_write_cbr(&ATA_PIO_SCR, (cnt) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LHR, (sector >> 16) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LMR, (sector >> 8) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_LLR, (sector) & 0xff);
|
|
ata_write_cbr(&ATA_PIO_DVR, BIT(6) | ((sector >> 24) & 0xf)); /* LBA28, mask off upper 4 bits of 32-bit sector address */
|
|
if (write)
|
|
ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_WRITE_DMA : CMD_WRITE_SECTORS);
|
|
else
|
|
ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_READ_DMA : CMD_READ_MULTIPLE);
|
|
}
|
|
#ifdef HAVE_ATA_DMA
|
|
if (ata_dma)
|
|
{
|
|
PASS_RC(ata_wait_for_start_of_transfer(500000), 2, 1);
|
|
if (write)
|
|
{
|
|
ATA_SBUF_START = buffer;
|
|
ATA_SBUF_SIZE = log_sector_size * cnt;
|
|
ATA_CFG |= BIT(4);
|
|
}
|
|
else
|
|
{
|
|
ATA_TBUF_START = buffer;
|
|
ATA_TBUF_SIZE = log_sector_size * cnt;
|
|
ATA_CFG &= ~BIT(4);
|
|
}
|
|
ATA_XFR_NUM = log_sector_size * cnt - 1;
|
|
ATA_CFG |= ata_dma_flags;
|
|
ATA_CFG &= ~(BIT(7) | BIT(8));
|
|
semaphore_wait(&ata_wakeup, 0);
|
|
ATA_IRQ = BITRANGE(0, 4);
|
|
ATA_IRQ_MASK = BIT(0);
|
|
ATA_COMMAND = BIT(0);
|
|
if (semaphore_wait(&ata_wakeup, 500000 * HZ / 1000000) == OBJ_WAIT_TIMEDOUT)
|
|
{
|
|
ATA_COMMAND = BIT(1);
|
|
ATA_CFG &= ~(BITRANGE(2, 3) | BIT(12));
|
|
RET_ERR(2);
|
|
}
|
|
ATA_COMMAND = BIT(1);
|
|
ATA_CFG &= ~(BITRANGE(2, 3) | BIT(12));
|
|
}
|
|
else
|
|
#endif // HAVE_ATA_DMA
|
|
{
|
|
while (cnt--)
|
|
{
|
|
uint16_t i;
|
|
PASS_RC(ata_wait_for_start_of_transfer(500000), 2, 1);
|
|
if (write)
|
|
for (i = 0; i < log_sector_size/2; i++)
|
|
ata_write_cbr(&ATA_PIO_DTR, ((uint16_t*)buffer)[i]);
|
|
else
|
|
for (i = 0; i < log_sector_size/2; i++)
|
|
((uint16_t*)buffer)[i] = ata_read_cbr(&ATA_PIO_DTR);
|
|
buffer += log_sector_size;
|
|
}
|
|
}
|
|
PASS_RC(ata_wait_for_end_of_transfer(100000), 2, 3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ata_rw_chunk(uint64_t sector, uint32_t cnt, void* buffer, bool write)
|
|
{
|
|
led(true);
|
|
int rc = ata_rw_chunk_internal(sector, cnt, buffer, write);
|
|
led(false);
|
|
return rc;
|
|
}
|
|
|
|
static int ata_transfer_sectors(uint64_t sector, uint32_t count, void* buffer, bool write)
|
|
{
|
|
if (!ata_powered)
|
|
ata_power_up();
|
|
if (sector + count > ata_total_sectors)
|
|
RET_ERR(0);
|
|
ata_set_active();
|
|
if (ata_dma && write)
|
|
commit_dcache();
|
|
else if (ata_dma)
|
|
commit_discard_dcache();
|
|
if (!ceata)
|
|
ATA_COMMAND = BIT(1);
|
|
|
|
logf("ata XFER (%d) %d @ %llu", write, count, sector);
|
|
|
|
while (count)
|
|
{
|
|
uint32_t cnt = MIN(ata_lba48 ? 65536 : 256, count);
|
|
int rc = -1;
|
|
rc = ata_rw_chunk(sector, cnt, buffer, write);
|
|
if (rc && ata_error_srst)
|
|
ata_reset();
|
|
if (rc && ata_retries)
|
|
{
|
|
void* buf = buffer;
|
|
uint64_t sect;
|
|
for (sect = sector; sect < sector + cnt; sect++)
|
|
{
|
|
rc = -1;
|
|
int tries = ata_retries;
|
|
while (tries-- && rc)
|
|
{
|
|
rc = ata_rw_chunk(sect, 1, buf, write);
|
|
if (rc && ata_error_srst)
|
|
ata_reset();
|
|
}
|
|
if (rc)
|
|
break;
|
|
buf += log_sector_size;
|
|
}
|
|
}
|
|
PASS_RC(rc, 1, 1);
|
|
buffer += log_sector_size * cnt;
|
|
sector += cnt;
|
|
count -= cnt;
|
|
}
|
|
ata_set_active();
|
|
return 0;
|
|
}
|
|
|
|
/* API Functions */
|
|
int ata_soft_reset(void)
|
|
{
|
|
int rc;
|
|
mutex_lock(&ata_mutex);
|
|
if (!ata_powered)
|
|
PASS_RC(ata_power_up(), 1, 0);
|
|
ata_set_active();
|
|
if (ceata)
|
|
{
|
|
rc = ceata_soft_reset();
|
|
}
|
|
else
|
|
{
|
|
ata_write_cbr(&ATA_PIO_DAD, BIT(1) | BIT(2));
|
|
udelay(10);
|
|
ata_write_cbr(&ATA_PIO_DAD, 0);
|
|
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))
|
|
{
|
|
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);
|
|
return rc;
|
|
}
|
|
|
|
#include "ata-common.c"
|
|
|
|
#ifndef MAX_PHYS_SECTOR_SIZE
|
|
int ata_read_sectors(IF_MD(int drive,) sector_t start, int incount,
|
|
void* inbuf)
|
|
{
|
|
#ifdef HAVE_MULTIDRIVE
|
|
(void)drive; /* unused for now */
|
|
#endif
|
|
|
|
mutex_lock(&ata_mutex);
|
|
int rc = ata_transfer_sectors(start, incount, inbuf, false);
|
|
mutex_unlock(&ata_mutex);
|
|
return rc;
|
|
}
|
|
|
|
int ata_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
|
const void* outbuf)
|
|
{
|
|
#ifdef HAVE_MULTIDRIVE
|
|
(void)drive; /* unused for now */
|
|
#endif
|
|
|
|
mutex_lock(&ata_mutex);
|
|
int rc = ata_transfer_sectors(start, count, (void*)((uint32_t)outbuf), true);
|
|
mutex_unlock(&ata_mutex);
|
|
return rc;
|
|
}
|
|
#endif /* ndef MAX_PHYS_SECTOR_SIZE */
|
|
|
|
void ata_spindown(int seconds)
|
|
{
|
|
ata_sleep_timeout = seconds * HZ;
|
|
}
|
|
|
|
static void ata_flush_cache(void)
|
|
{
|
|
uint8_t cmd;
|
|
|
|
logf("ata FLUSH CACHE %ld", current_tick);
|
|
|
|
if (ceata) {
|
|
memset(ceata_taskfile, 0, 16);
|
|
ceata_taskfile[0xf] = CMD_FLUSH_CACHE_EXT; /* CE-ATA only supports EXT */
|
|
ceata_wait_idle();
|
|
ceata_write_multiple_register(0, ceata_taskfile, 16);
|
|
ceata_wait_idle();
|
|
} else {
|
|
if (!canflush) {
|
|
return;
|
|
} else if (ata_lba48 && identify_info[83] & BIT(13)) {
|
|
cmd = CMD_FLUSH_CACHE_EXT; /* Flag, optional, ATA-6 and up, for use with LBA48 devices. Mandatory for CE-ATA */
|
|
} else if (identify_info[83] & BIT(12)) {
|
|
cmd = CMD_FLUSH_CACHE; /* Flag, mandatory, ATA-6 and up */
|
|
} else if (identify_info[80] >= BIT(5)) { /* Use >= instead of '&' because bits lower than the latest standard we support don't have to be set */
|
|
cmd = CMD_FLUSH_CACHE; /* No flag, mandatory, ATA-5 (Optional for ATA-4) */
|
|
} else {
|
|
/* If neither command is supported then don't issue it. */
|
|
canflush = 0;
|
|
return;
|
|
}
|
|
|
|
ata_wait_for_rdy(1000000);
|
|
ata_write_cbr(&ATA_PIO_DVR, 0);
|
|
ata_write_cbr(&ATA_PIO_CSD, cmd);
|
|
ata_wait_for_rdy(1000000);
|
|
}
|
|
}
|
|
|
|
int ata_flush(void)
|
|
{
|
|
if (ata_powered) {
|
|
mutex_lock(&ata_mutex);
|
|
ata_flush_cache();
|
|
mutex_unlock(&ata_mutex);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ata_sleepnow(void)
|
|
{
|
|
mutex_lock(&ata_mutex);
|
|
|
|
ata_flush_cache();
|
|
|
|
if (ata_disk_can_sleep()) {
|
|
logf("ata SLEEP %ld", current_tick);
|
|
|
|
if (ceata) {
|
|
memset(ceata_taskfile, 0, 16);
|
|
ceata_taskfile[0xf] = CMD_STANDBY_IMMEDIATE;
|
|
ceata_wait_idle();
|
|
ceata_write_multiple_register(0, ceata_taskfile, 16);
|
|
ceata_wait_idle();
|
|
sleep(HZ);
|
|
PWRCON(0) |= (1 << 9);
|
|
} else {
|
|
ata_wait_for_rdy(1000000);
|
|
ata_write_cbr(&ATA_PIO_DVR, 0);
|
|
ata_write_cbr(&ATA_PIO_CSD, CMD_STANDBY_IMMEDIATE);
|
|
ata_wait_for_rdy(1000000);
|
|
sleep(HZ / 30);
|
|
ATA_CONTROL = 0;
|
|
while (!(ATA_CONTROL & BIT(1)))
|
|
yield();
|
|
PWRCON(0) |= (1 << 5);
|
|
}
|
|
}
|
|
|
|
if (ata_disk_can_sleep() || canflush)
|
|
ata_power_down(); // XXX add a powerdown delay similar to main ATA driver?
|
|
|
|
mutex_unlock(&ata_mutex);
|
|
}
|
|
|
|
void ata_spin(void)
|
|
{
|
|
ata_set_active();
|
|
}
|
|
|
|
#ifdef STORAGE_GET_INFO
|
|
void ata_get_info(IF_MD(int drive,) struct storage_info *info)
|
|
{
|
|
info->sector_size = log_sector_size;
|
|
info->num_sectors = ata_total_sectors;
|
|
info->vendor = "Apple";
|
|
info->product = "iPod Classic";
|
|
info->revision = "1.0";
|
|
}
|
|
#endif
|
|
|
|
long ata_last_disk_activity(void)
|
|
{
|
|
return ata_last_activity_value;
|
|
}
|
|
|
|
int ata_init(void)
|
|
{
|
|
mutex_init(&ata_mutex);
|
|
semaphore_init(&ata_wakeup, 1, 0);
|
|
semaphore_init(&mmc_wakeup, 1, 0);
|
|
semaphore_init(&mmc_comp_wakeup, 1, 0);
|
|
ceata = PDAT(11) & BIT(1);
|
|
ata_powered = false;
|
|
ata_total_sectors = 0;
|
|
|
|
/* get identify_info */
|
|
mutex_lock(&ata_mutex);
|
|
int rc = ata_power_up(); /* Includes identify() call */
|
|
mutex_unlock(&ata_mutex);
|
|
if (IS_ERR(rc))
|
|
return rc;
|
|
|
|
/* Logical sector size */
|
|
if ((identify_info[106] & 0xd000) == 0x5000) /* B14, B12 */
|
|
log_sector_size = (identify_info[117] | (identify_info[118] << 16)) * 2;
|
|
else
|
|
log_sector_size = 512;
|
|
|
|
#ifdef MAX_PHYS_SECTOR_SIZE
|
|
rc = ata_get_phys_sector_mult();
|
|
if (IS_ERR(rc))
|
|
return rc;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_ATA_SMART
|
|
static int ata_smart(uint16_t* buf)
|
|
{
|
|
if (!ata_powered) PASS_RC(ata_power_up(), 3, 0);
|
|
if (ceata)
|
|
{
|
|
memset(ceata_taskfile, 0, 16);
|
|
ceata_taskfile[0xc] = 0x4f;
|
|
ceata_taskfile[0xd] = 0xc2;
|
|
ceata_taskfile[0xe] = BIT(6);
|
|
ceata_taskfile[0xf] = CMD_SMART;
|
|
PASS_RC(ceata_wait_idle(), 3, 1);
|
|
if (((uint8_t*)identify_info)[54] != 'A') /* Model != aAmsung */
|
|
{
|
|
ceata_taskfile[0x9] = 0xd8; /* SMART enable operations */
|
|
PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 2);
|
|
PASS_RC(ceata_check_error(), 3, 3);
|
|
}
|
|
ceata_taskfile[0x9] = 0xd0; /* SMART read data */
|
|
PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 4);
|
|
PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 3, 5);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
PASS_RC(ata_wait_for_not_bsy(10000000), 3, 6);
|
|
ata_write_cbr(&ATA_PIO_FED, 0xd0);
|
|
ata_write_cbr(&ATA_PIO_LMR, 0x4f);
|
|
ata_write_cbr(&ATA_PIO_LHR, 0xc2);
|
|
ata_write_cbr(&ATA_PIO_DVR, BIT(6));
|
|
ata_write_cbr(&ATA_PIO_CSD, CMD_SMART);
|
|
PASS_RC(ata_wait_for_start_of_transfer(10000000), 3, 7);
|
|
for (i = 0; i < 0x100; i++)
|
|
buf[i] = ata_read_cbr(&ATA_PIO_DTR);
|
|
}
|
|
ata_set_active();
|
|
return 0;
|
|
}
|
|
|
|
int ata_read_smart(struct ata_smart_values* smart_data)
|
|
{
|
|
mutex_lock(&ata_mutex);
|
|
int rc = ata_smart((uint16_t*)smart_data);
|
|
mutex_unlock(&ata_mutex);
|
|
return rc;
|
|
}
|
|
#endif /* HAVE_ATA_SMART */
|
|
|
|
#ifdef CONFIG_STORAGE_MULTI
|
|
static int ata_num_drives(int first_drive)
|
|
{
|
|
/* We don't care which logical drive number(s) we have been assigned */
|
|
(void)first_drive;
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
unsigned short* ata_get_identify(void)
|
|
{
|
|
return identify_info;
|
|
}
|
|
|
|
int ata_spinup_time(void)
|
|
{
|
|
return spinup_time;
|
|
}
|
|
|
|
#ifdef HAVE_ATA_DMA
|
|
int ata_get_dma_mode(void)
|
|
{
|
|
return dma_mode;
|
|
}
|
|
#endif
|
|
|
|
void INT_ATA(void)
|
|
{
|
|
uint32_t ata_irq = ATA_IRQ;
|
|
ATA_IRQ = ata_irq;
|
|
if (ata_irq & ATA_IRQ_MASK)
|
|
semaphore_release(&ata_wakeup);
|
|
ATA_IRQ_MASK = 0;
|
|
}
|
|
|
|
void INT_MMC(void)
|
|
{
|
|
uint32_t irq = SDCI_IRQ;
|
|
if (irq & SDCI_IRQ_DAT_DONE_INT)
|
|
semaphore_release(&mmc_wakeup);
|
|
if (irq & SDCI_IRQ_IOCARD_IRQ_INT)
|
|
semaphore_release(&mmc_comp_wakeup);
|
|
SDCI_IRQ = irq;
|
|
}
|
|
|
|
int ata_event(long id, intptr_t data)
|
|
{
|
|
int rc = 0;
|
|
|
|
/* GCC does a lousy job culling unreachable cases in the default handler
|
|
if statements are in a switch statement, so we'll do it this way. Only
|
|
the first case is frequently hit anyway. */
|
|
if (LIKELY(id == Q_STORAGE_TICK))
|
|
{
|
|
if (!ata_powered ||
|
|
TIME_BEFORE(current_tick, ata_last_activity_value + ata_sleep_timeout))
|
|
{
|
|
STG_EVENT_ASSERT_ACTIVE(STORAGE_ATA);
|
|
}
|
|
}
|
|
else if (id == Q_STORAGE_SLEEPNOW)
|
|
{
|
|
ata_sleepnow();
|
|
}
|
|
else if (id == Q_STORAGE_SLEEP)
|
|
{
|
|
ata_last_activity_value = current_tick - ata_sleep_timeout + HZ / 5;
|
|
}
|
|
else if (id == SYS_USB_CONNECTED)
|
|
{
|
|
STG_EVENT_ASSERT_ACTIVE(STORAGE_ATA);
|
|
}
|
|
else
|
|
{
|
|
rc = storage_event_default_handler(id, data, ata_last_activity_value,
|
|
STORAGE_ATA);
|
|
}
|
|
|
|
return rc;
|
|
}
|