mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-08 20:55:17 -05:00
jz4760: SD driver enhancements:
* Check to see if clock is [not] running prior to [en|dis]abling it * Stop clock _prior_ to resetting controller * Stop clock after transaction is completed, not before initiating it * Use controller's low power mode (disables clocks when idle) * Fix, and enable, interrupt-driven DMA transfers * Fixes for full interrupt-driven operation (WIP, still broken) Change-Id: I723ffa6450fc85f97898c8a8b3e538ae31c4858e
This commit is contained in:
parent
1b31101fdd
commit
0aa2197d93
1 changed files with 91 additions and 42 deletions
|
|
@ -29,10 +29,12 @@
|
|||
#include "logf.h"
|
||||
#include "storage.h"
|
||||
#include "string.h"
|
||||
#include "panic.h"
|
||||
|
||||
#define SD_INTERRUPT 0
|
||||
#define SD_INTERRUPT 0 // COMPLETELY BROKEN!
|
||||
#define SD_DMA_ENABLE 1
|
||||
#define SD_DMA_INTERRUPT 0
|
||||
#define SD_DMA_INTERRUPT 1 // HANGS RANDOMLY!
|
||||
#define SD_AUTO_CLOCK 1
|
||||
|
||||
#if NUM_DRIVES > 2
|
||||
#error "JZ4760 SD driver supports NUM_DRIVES <= 2 only"
|
||||
|
|
@ -267,6 +269,14 @@ static inline int jz_sd_stop_clock(const int drive)
|
|||
register int timeout = 1000;
|
||||
|
||||
//DEBUG("stop MMC clock");
|
||||
#if SD_AUTO_CLOCK
|
||||
REG_MSC_LPM(drive) = 0; /* disable auto clock stop */
|
||||
#endif
|
||||
|
||||
/* only stop if necessary */
|
||||
if (!(REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_CLK_EN))
|
||||
return SD_NO_ERROR;
|
||||
|
||||
REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_STOP;
|
||||
|
||||
while (timeout && (REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_CLK_EN))
|
||||
|
|
@ -279,6 +289,7 @@ static inline int jz_sd_stop_clock(const int drive)
|
|||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
//DEBUG("clock off time is %d microsec", timeout);
|
||||
return SD_NO_ERROR;
|
||||
}
|
||||
|
|
@ -286,7 +297,11 @@ static inline int jz_sd_stop_clock(const int drive)
|
|||
/* Start the MMC clock and operation */
|
||||
static inline int jz_sd_start_clock(const int drive)
|
||||
{
|
||||
REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP;
|
||||
int reg = MSC_STRPCL_START_OP;
|
||||
#if !SD_AUTO_CLOCK
|
||||
reg |= (REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_CLK_EN) ? 0 : MSC_STRPCL_CLOCK_CONTROL_START;
|
||||
#endif
|
||||
REG_MSC_STRPCL(MSC_CHN(drive)) = reg;
|
||||
return SD_NO_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -588,91 +603,88 @@ void DMA_CALLBACK(DMA_SD_RX_CHANNEL0)(void)
|
|||
{
|
||||
if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_1)) & DMAC_DCCSR_AR)
|
||||
{
|
||||
logf("SD RX DMA address error");
|
||||
REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_1)) &= ~DMAC_DCCSR_AR;
|
||||
panicf("SD RX DMA address error");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_1)) & DMAC_DCCSR_HLT)
|
||||
{
|
||||
logf("SD RX DMA halt");
|
||||
REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_1)) &= ~DMAC_DCCSR_HLT;
|
||||
panicf("SD RX DMA halt");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_1)) & DMAC_DCCSR_TT)
|
||||
{
|
||||
REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_1)) &= ~DMAC_DCCSR_TT;
|
||||
//sd_rx_dma_callback();
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_1]);
|
||||
}
|
||||
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_1]);
|
||||
}
|
||||
|
||||
void DMA_CALLBACK(DMA_SD_RX_CHANNEL1)(void)
|
||||
{
|
||||
if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_2)) & DMAC_DCCSR_AR)
|
||||
{
|
||||
logf("SD RX DMA address error");
|
||||
REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_2)) &= ~DMAC_DCCSR_AR;
|
||||
panicf("SD RX DMA address error");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_2)) & DMAC_DCCSR_HLT)
|
||||
{
|
||||
logf("SD RX DMA halt");
|
||||
REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_2)) &= ~DMAC_DCCSR_HLT;
|
||||
panicf("SD RX DMA halt");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_2)) & DMAC_DCCSR_TT)
|
||||
{
|
||||
REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL(SD_SLOT_2)) &= ~DMAC_DCCSR_TT;
|
||||
//sd_rx_dma_callback();
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
}
|
||||
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
}
|
||||
|
||||
void DMA_CALLBACK(DMA_SD_TX_CHANNEL0)(void)
|
||||
{
|
||||
if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_1)) & DMAC_DCCSR_AR)
|
||||
{
|
||||
logf("SD TX DMA address error: %x, %x, %x", var1, var2, var3);
|
||||
REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_1)) &= ~DMAC_DCCSR_AR;
|
||||
panicf("SD TX DMA address error");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_1)) & DMAC_DCCSR_HLT)
|
||||
{
|
||||
logf("SD TX DMA halt");
|
||||
REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_1)) &= ~DMAC_DCCSR_HLT;
|
||||
panicf("SD TX DMA halt");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_1)) & DMAC_DCCSR_TT)
|
||||
{
|
||||
REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_1)) &= ~DMAC_DCCSR_TT;
|
||||
//sd_tx_dma_callback();
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_1]);
|
||||
}
|
||||
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_1]);
|
||||
}
|
||||
|
||||
void DMA_CALLBACK(DMA_SD_TX_CHANNEL1)(void)
|
||||
{
|
||||
if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_2)) & DMAC_DCCSR_AR)
|
||||
{
|
||||
logf("SD TX DMA address error: %x, %x, %x", var1, var2, var3);
|
||||
REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_2)) &= ~DMAC_DCCSR_AR;
|
||||
panicf("SD TX DMA address error");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_2)) & DMAC_DCCSR_HLT)
|
||||
{
|
||||
logf("SD TX DMA halt");
|
||||
REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_2)) &= ~DMAC_DCCSR_HLT;
|
||||
panicf("SD TX DMA halt");
|
||||
}
|
||||
|
||||
if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_2)) & DMAC_DCCSR_TT)
|
||||
{
|
||||
REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL(SD_SLOT_2)) &= ~DMAC_DCCSR_TT;
|
||||
//sd_tx_dma_callback();
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
}
|
||||
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
}
|
||||
#endif /* SD_DMA_INTERRUPT */
|
||||
#endif /* SD_DMA_ENABLE */
|
||||
|
|
@ -715,8 +727,6 @@ static void jz_sd_set_clock(const int drive, unsigned int rate)
|
|||
{
|
||||
int clkrt;
|
||||
|
||||
jz_sd_stop_clock(drive);
|
||||
|
||||
/* select clock source from CPM */
|
||||
cpm_select_msc_clk();
|
||||
|
||||
|
|
@ -747,28 +757,27 @@ static int jz_sd_exec_cmd(const int drive, struct sd_request *request)
|
|||
/* On reset, 1-bit bus width */
|
||||
use_4bit[drive] = 0;
|
||||
|
||||
/* On reset, stop SD clock */
|
||||
jz_sd_stop_clock(drive);
|
||||
|
||||
/* Reset MMC/SD controller */
|
||||
__msc_reset(MSC_CHN(drive));
|
||||
|
||||
/* On reset, drop SD clock down */
|
||||
/* Drop SD clock down to lowest speed */
|
||||
jz_sd_set_clock(drive, MMC_CLOCK_SLOW);
|
||||
|
||||
/* On reset, stop SD clock */
|
||||
jz_sd_stop_clock(drive);
|
||||
#if SD_AUTO_CLOCK
|
||||
/* Re-enable clocks */
|
||||
REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_START;
|
||||
REG_MSC_LPM(drive) = MSC_SET_LPM;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* stop clock */
|
||||
jz_sd_stop_clock(drive);
|
||||
|
||||
/* mask all interrupts and clear status */
|
||||
SD_IRQ_MASK(MSC_CHN(drive));
|
||||
|
||||
/* open interrupt */
|
||||
#if SD_INTERRUPT
|
||||
REG_MSC_IMASK(MSC_CHN(drive)) = ~(MSC_IMASK_DATA_TRAN_DONE | MSC_IMASK_PRG_DONE);
|
||||
#else
|
||||
REG_MSC_IMASK(MSC_CHN(drive)) = ~(MSC_IMASK_END_CMD_RES | MSC_IMASK_DATA_TRAN_DONE | MSC_IMASK_PRG_DONE);
|
||||
#endif
|
||||
|
||||
/* Set command type and events */
|
||||
switch (request->cmd)
|
||||
|
|
@ -926,8 +935,8 @@ static int jz_sd_exec_cmd(const int drive, struct sd_request *request)
|
|||
return SD_ERROR_TIMEOUT;
|
||||
yield();
|
||||
}
|
||||
#endif
|
||||
REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_END_CMD_RES; /* clear flag */
|
||||
#endif
|
||||
|
||||
/* Check for status */
|
||||
retval = jz_sd_check_status(drive, request);
|
||||
|
|
@ -964,8 +973,8 @@ static int jz_sd_exec_cmd(const int drive, struct sd_request *request)
|
|||
/* Wait for Data Done */
|
||||
while (!(REG_MSC_IREG(MSC_CHN(drive)) & MSC_IREG_DATA_TRAN_DONE))
|
||||
yield();
|
||||
#endif
|
||||
REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_DATA_TRAN_DONE; /* clear status */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Wait for Prog Done event */
|
||||
|
|
@ -976,11 +985,14 @@ static int jz_sd_exec_cmd(const int drive, struct sd_request *request)
|
|||
#else
|
||||
while (!(REG_MSC_IREG(MSC_CHN(drive)) & MSC_IREG_PRG_DONE))
|
||||
yield();
|
||||
#endif
|
||||
REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_PRG_DONE; /* clear status */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Command completed */
|
||||
#if !SD_AUTO_CLOCK
|
||||
jz_sd_stop_clock(drive); /* stop SD clock */
|
||||
#endif
|
||||
|
||||
return SD_NO_ERROR; /* return successfully */
|
||||
}
|
||||
|
|
@ -996,18 +1008,45 @@ static int jz_sd_chkcard(const int drive)
|
|||
return (__gpio_get_pin((drive == SD_SLOT_1) ? PIN_SD1_CD : PIN_SD2_CD) == 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/* MSC interrupt handler */
|
||||
void MSC(void)
|
||||
{
|
||||
/* MSC interrupt handlers */
|
||||
#if SD_INTERRUPT
|
||||
if (REG_MSC_IREG(MSC_CHN(SD_SLOT_1)) & MSC_IREG_DATA_TRAN_DONE)
|
||||
void MSC2(void) /* SD_SLOT_1 */
|
||||
{
|
||||
logf("MSC2 interrupt");
|
||||
|
||||
if (REG_MSC_IREG(MSC_CHN(SD_SLOT_1)) & MSC_IREG_END_CMD_RES) {
|
||||
REG_MSC_IREG(MSC_CHN(SD_SLOT_1)) = MSC_IREG_END_CMD_RES; /* clear flag */
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_1]);
|
||||
else if (REG_MSC_IREG(MSC_CHN(SD_SLOT_2)) & MSC_IREG_DATA_TRAN_DONE)
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
#endif
|
||||
logf("MSC interrupt");
|
||||
}
|
||||
if (REG_MSC_IREG(MSC_CHN(SD_SLOT_1)) & MSC_IREG_PRG_DONE) {
|
||||
REG_MSC_IREG(MSC_CHN(SD_SLOT_1)) = MSC_IREG_PRG_DONE; /* clear flag */
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_1]);
|
||||
}
|
||||
if (REG_MSC_IREG(MSC_CHN(SD_SLOT_1)) & MSC_IREG_DATA_TRAN_DONE) {
|
||||
REG_MSC_IREG(MSC_CHN(SD_SLOT_1)) = MSC_IREG_DATA_TRAN_DONE; /* clear flag */
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* MSC interrupt handlers */
|
||||
void MSC1(void) /* SD_SLOT_2 */
|
||||
{
|
||||
logf("MSC1 interrupt");
|
||||
if (REG_MSC_IREG(MSC_CHN(SD_SLOT_2)) & MSC_IREG_END_CMD_RES) {
|
||||
REG_MSC_IREG(MSC_CHN(SD_SLOT_2)) = MSC_IREG_END_CMD_RES; /* clear flag */
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
}
|
||||
if (REG_MSC_IREG(MSC_CHN(SD_SLOT_2)) & MSC_IREG_PRG_DONE) {
|
||||
REG_MSC_IREG(MSC_CHN(SD_SLOT_2)) = MSC_IREG_PRG_DONE; /* clear flag */
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
}
|
||||
if (REG_MSC_IREG(MSC_CHN(SD_SLOT_2)) & MSC_IREG_DATA_TRAN_DONE) {
|
||||
REG_MSC_IREG(MSC_CHN(SD_SLOT_2)) = MSC_IREG_DATA_TRAN_DONE; /* clear flag */
|
||||
semaphore_release(&sd_wakeup[SD_SLOT_2]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_HOTSWAP
|
||||
static void sd_gpio_setup_irq(const int drive, bool inserted)
|
||||
{
|
||||
|
|
@ -1038,7 +1077,12 @@ static void jz_sd_hardware_init(const int drive)
|
|||
#endif
|
||||
__msc_reset(MSC_CHN(drive)); /* reset mmc/sd controller */
|
||||
SD_IRQ_MASK(MSC_CHN(drive)); /* mask all IRQs */
|
||||
#if SD_AUTO_CLOCK
|
||||
REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_START; /* Enable clocks */
|
||||
REG_MSC_LPM(drive) = MSC_SET_LPM; /* enable auto clock stop */
|
||||
#else
|
||||
jz_sd_stop_clock(drive); /* stop SD clock */
|
||||
#endif
|
||||
}
|
||||
|
||||
static void sd_send_cmd(const int drive, struct sd_request *request, int cmd, unsigned int arg,
|
||||
|
|
@ -1315,6 +1359,11 @@ int sd_init(void)
|
|||
mutex_init(&sd_mtx[SD_SLOT_1]);
|
||||
mutex_init(&sd_mtx[SD_SLOT_2]);
|
||||
|
||||
#if SD_INTERRUPT
|
||||
system_enable_irq(IRQ_MSC2);
|
||||
system_enable_irq(IRQ_MSC1);
|
||||
#endif
|
||||
|
||||
#if SD_DMA_ENABLE && SD_DMA_INTERRUPT
|
||||
system_enable_irq(DMA_IRQ(DMA_SD_RX_CHANNEL(SD_SLOT_1)));
|
||||
system_enable_irq(DMA_IRQ(DMA_SD_RX_CHANNEL(SD_SLOT_2)));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue