arm:mmc fixes

When we send MMC commads that contain data the controller provide no
description of the order of events and we need to be able to send data
from and to the controller "when needed". Changed the code to react
on buffer read and buffer write ready based on interrupts.

We also increase driver process priority.  Without special attribute
drivers will have the same priority as other userland processes this.
Posix test 1 was taking so much resources that it triggered alarms
in the MMC driver.

Change-Id: I60c9140bf0e45b74be6475054564d4e1bd89f21e
This commit is contained in:
Kees Jongenburger 2013-06-18 21:39:04 +02:00 committed by Kees Jongenburger
parent 8e7c0604bd
commit ac16328d6d
4 changed files with 165 additions and 133 deletions

View File

@ -43,7 +43,7 @@ struct sd_card_regs
uint32_t rca; /* Relative card address */ uint32_t rca; /* Relative card address */
uint32_t dsr; /* Driver stage register */ uint32_t dsr; /* Driver stage register */
uint32_t csd[4]; /* Card specific data */ uint32_t csd[4]; /* Card specific data */
uint32_t scr[2]; /* SD configuration */ uint32_t scr[512/4]; /* SD configuration */
uint32_t ocr; /* Operation conditions */ uint32_t ocr; /* Operation conditions */
uint32_t ssr[5]; /* SD Status */ uint32_t ssr[5]; /* SD Status */
uint32_t csr; /* Card status */ uint32_t csr; /* Card status */

View File

@ -41,7 +41,7 @@ static int hook_id = 1;
#endif #endif
#endif #endif
#define SANE_TIMEOUT 500000 /* 500 MS */ #define SANE_TIMEOUT 500000 /* 500 ms */
/* /*
* Define a structure to be used for logging * Define a structure to be used for logging
@ -181,7 +181,6 @@ mmchs_init(uint32_t instance)
spin_init(&spin, SANE_TIMEOUT); spin_init(&spin, SANE_TIMEOUT);
while ((read32(base_address + MMCHS_SD_SYSCTL) & MMCHS_SD_SYSCTL_ICS) while ((read32(base_address + MMCHS_SD_SYSCTL) & MMCHS_SD_SYSCTL_ICS)
!= MMCHS_SD_SYSCTL_ICS_STABLE) { != MMCHS_SD_SYSCTL_ICS_STABLE) {
if (spin_check(&spin) == FALSE) { if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "clock not stable\n"); mmc_log_warn(&log, "clock not stable\n");
return 1; return 1;
@ -202,7 +201,7 @@ mmchs_init(uint32_t instance)
/* enable error interrupts */ /* enable error interrupts */
/* NOTE: We are currently skipping the BADA interrupt it does get /* NOTE: We are currently skipping the BADA interrupt it does get
* raised for unknown reasons */ * raised for unknown reasons */
set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_ERROR_MASK, 0x0fffffffu); set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_ERROR_MASK, 0xffffffffu);
/* clear the error interrupts */ /* clear the error interrupts */
set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK, set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK,
@ -261,10 +260,80 @@ mmchs_init(uint32_t instance)
return 0; return 0;
} }
void
intr_deassert(int mask)
{
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
mmc_log_warn(&log, "%s, error stat %08x\n", __FUNCTION__,
read32(base_address + MMCHS_SD_STAT));
set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK,
0xffffffffu);
} else {
write32(base_address + MMCHS_SD_STAT, mask);
}
}
/* pointer to the data to transfer used in bwr and brr */
unsigned char *io_data;
int io_len;
void
handle_bwr()
{
/* handle buffer write ready interrupts. These happen in a non
* predictable way (eg. we send a request but don't know if we are
* first doing to get a request completed before we are allowed to
* send the data to the harware or not */
uint32_t value;
uint32_t count;
assert(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BWE_EN);
for (count = 0; count < io_len; count += 4) {
while (!(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BWE_EN)) {
mmc_log_trace(&log,
"Error expected Buffer to be write enabled(%d)\n",
count);
}
*((char *) &value) = io_data[count];
*((char *) &value + 1) = io_data[count + 1];
*((char *) &value + 2) = io_data[count + 2];
*((char *) &value + 3) = io_data[count + 3];
write32(base_address + MMCHS_SD_DATA, value);
}
intr_deassert(MMCHS_SD_IE_BWR_ENABLE);
/* expect buffer to be write enabled */
}
void
handle_brr()
{
/* handle buffer read ready interrupts. genrally these happen afther
* the data is read from the sd card. */
uint32_t value;
uint32_t count;
/* Problem BRE should be true */
assert(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BRE_EN);
for (count = 0; count < io_len; count += 4) {
value = read32(base_address + MMCHS_SD_DATA);
io_data[count] = *((char *) &value);
io_data[count + 1] = *((char *) &value + 1);
io_data[count + 2] = *((char *) &value + 2);
io_data[count + 3] = *((char *) &value + 3);
}
/* clear bbr interrupt */
intr_deassert(MMCHS_SD_IE_BRR_ENABLE_ENABLE);
}
static void static void
mmchs_hw_intr(unsigned int irqs) mmchs_hw_intr(unsigned int irqs)
{ {
mmc_log_warn(&log, "Hardware interrupt left over\n"); mmc_log_warn(&log, "Hardware interrupt left over (0x%08lx)\n",
read32(base_address + MMCHS_SD_STAT));
#ifdef USE_INTR #ifdef USE_INTR
if (sys_irqenable(&hook_id) != OK) if (sys_irqenable(&hook_id) != OK)
@ -300,27 +369,46 @@ intr_wait(int mask)
switch (_ENDPOINT_P(m.m_source)) { switch (_ENDPOINT_P(m.m_source)) {
case CLOCK: case CLOCK:
/* Timeout. */ /* Timeout. */
// w_timeout(); /* a.o. set w_status */
mmc_log_warn(&log, "TIMEOUT\n"); mmc_log_warn(&log, "TIMEOUT\n");
return 1; return 1;
break; break;
case HARDWARE: case HARDWARE:
v = read32(base_address + MMCHS_SD_STAT); while ((v =
if (v & mask) { read32(base_address +
sys_setalarm(0, 0); MMCHS_SD_STAT)) != 0) {
return 0; if (v & MMCHS_SD_IE_BWR_ENABLE) {
} else if (v & (1 << 15)) { handle_bwr();
return 1; /* error */ continue;
} else { }
mmc_log_debug(&log, if (v & MMCHS_SD_IE_BRR_ENABLE) {
handle_brr();
continue;
}
if (v & mask) {
/* this is the normal return
* path, the mask given
* matches the pending
* interrupt. canel the alarm
* and return */
sys_setalarm(0, 0);
return 0;
} else if (v & (1 << 15)) {
return 1; /* error */
}
mmc_log_warn(&log,
"unexpected HW interrupt 0x%08x mask 0X%08x\n", "unexpected HW interrupt 0x%08x mask 0X%08x\n",
v, mask); v, mask);
if (sys_irqenable(&hook_id) != OK) if (sys_irqenable(&hook_id) != OK)
printf printf
("Failed to re-enable irqenable irq\n"); ("Failed to re-enable irqenable irq\n");
continue;
// return 1;
} }
/* if we end up here re-enable interrupts for the next round */
if (sys_irqenable(&hook_id) != OK)
printf
("Failed to re-enable irqenable irq\n");
break;
default: default:
/* /*
* unhandled message. queue it and * unhandled message. queue it and
@ -353,6 +441,14 @@ intr_wait(int mask)
counter, v, mask); counter, v, mask);
return 1; return 1;
} }
if (v & MMCHS_SD_IE_BWR_ENABLE) {
handle_bwr();
continue;
}
if (v & MMCHS_SD_IE_BRR_ENABLE) {
handle_brr();
continue;
}
if (v & mask) { if (v & mask) {
return 0; return 0;
} else if (v & 0xFF00) { } else if (v & 0xFF00) {
@ -366,18 +462,7 @@ intr_wait(int mask)
#endif /* USE_INTR */ #endif /* USE_INTR */
} }
void static int bbw_passed;
intr_assert(int mask)
{
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
mmc_log_debug(&log, "%s, error stat %08x\n", __FUNCTION__,
read32(base_address + MMCHS_SD_STAT));
set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK,
0xffffffffu);
} else {
write32(base_address + MMCHS_SD_STAT, mask);
}
}
int int
mmchs_send_cmd(uint32_t command, uint32_t arg) mmchs_send_cmd(uint32_t command, uint32_t arg)
@ -385,17 +470,20 @@ mmchs_send_cmd(uint32_t command, uint32_t arg)
/* Read current interrupt status and fail it an interrupt is already /* Read current interrupt status and fail it an interrupt is already
* asserted */ * asserted */
assert(read32(base_address + MMCHS_SD_STAT) == 0);
/* Set arguments */ /* Set arguments */
write32(base_address + MMCHS_SD_ARG, arg); write32(base_address + MMCHS_SD_ARG, arg);
/* Set command */ /* Set command */
set32(base_address + MMCHS_SD_CMD, MMCHS_SD_CMD_MASK, command); set32(base_address + MMCHS_SD_CMD, MMCHS_SD_CMD_MASK, command);
if (intr_wait(MMCHS_SD_STAT_CC | MMCHS_SD_IE_TC_ENABLE_CLEAR)) { if (intr_wait(MMCHS_SD_STAT_CC)) {
intr_assert(MMCHS_SD_STAT_CC); uint32_t v = read32(base_address + MMCHS_SD_STAT);
mmc_log_warn(&log, "Failure waiting for interrupt\n"); intr_deassert(MMCHS_SD_STAT_CC);
mmc_log_warn(&log, "Failure waiting for interrupt 0x%lx\n", v);
return 1; return 1;
} }
intr_deassert(MMCHS_SD_STAT_CC);
if ((command & MMCHS_SD_CMD_RSP_TYPE) == if ((command & MMCHS_SD_CMD_RSP_TYPE) ==
MMCHS_SD_CMD_RSP_TYPE_48B_BUSY) { MMCHS_SD_CMD_RSP_TYPE_48B_BUSY) {
@ -406,16 +494,8 @@ mmchs_send_cmd(uint32_t command, uint32_t arg)
& MMCHS_SD_IE_TC_ENABLE_ENABLE) == 0) { & MMCHS_SD_IE_TC_ENABLE_ENABLE) == 0) {
mmc_log_warn(&log, "TC should be raised\n"); mmc_log_warn(&log, "TC should be raised\n");
} }
write32(base_address + MMCHS_SD_STAT, intr_deassert(MMCHS_SD_STAT_TC);
MMCHS_SD_IE_TC_ENABLE_CLEAR);
if (intr_wait(MMCHS_SD_STAT_CC | MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
intr_assert(MMCHS_SD_STAT_CC);
mmc_log_warn(&log, "Failure waiting for clear\n");
return 1;
}
} }
intr_assert(MMCHS_SD_STAT_CC);
return 0; return 0;
} }
@ -449,7 +529,7 @@ mmc_send_cmd(struct mmc_command *c)
} }
/* read single block */ /* read single block */
if (c->cmd == MMC_READ_BLOCK_SINGLE) { if ((c->cmd == MMC_READ_BLOCK_SINGLE) || (c->cmd == SD_APP_SEND_SCR)) {
cmd |= MMCHS_SD_CMD_DP_DATA; /* Command with data transfer */ cmd |= MMCHS_SD_CMD_DP_DATA; /* Command with data transfer */
cmd |= MMCHS_SD_CMD_MSBS_SINGLE; /* single block */ cmd |= MMCHS_SD_CMD_MSBS_SINGLE; /* single block */
cmd |= MMCHS_SD_CMD_DDIR_READ; /* read data from card */ cmd |= MMCHS_SD_CMD_DDIR_READ; /* read data from card */
@ -478,10 +558,14 @@ mmc_send_cmd(struct mmc_command *c)
set32(base_address + MMCHS_SD_IE, set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BRR_ENABLE, MMCHS_SD_IE_BRR_ENABLE,
MMCHS_SD_IE_BRR_ENABLE_ENABLE); MMCHS_SD_IE_BRR_ENABLE_ENABLE);
io_data = c->data;
io_len = c->data_len;
} else { } else {
set32(base_address + MMCHS_SD_IE, set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BWR_ENABLE, MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_ENABLE); MMCHS_SD_IE_BWR_ENABLE_ENABLE);
io_data = c->data;
io_len = c->data_len;
} }
} }
@ -489,6 +573,42 @@ mmc_send_cmd(struct mmc_command *c)
ret = mmchs_send_cmd(cmd, arg); ret = mmchs_send_cmd(cmd, arg);
if (cmd & MMCHS_SD_CMD_DP_DATA) {
count = 0;
assert(c->data_len);
if (cmd & MMCHS_SD_CMD_DDIR_READ) {
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_ENABLE)) {
intr_deassert(MMCHS_SD_IE_TC_ENABLE_ENABLE);
mmc_log_warn(&log,
"(Read) Timeout waiting for interrupt\n");
return 1;
}
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_TC_ENABLE_CLEAR);
/* disable the bbr interrupt */
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BRR_ENABLE,
MMCHS_SD_IE_BRR_ENABLE_DISABLE);
} else {
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_ENABLE)) {
intr_deassert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
mmc_log_warn(&log,
"(Write) Timeout waiting for transfer complete\n");
return 1;
}
intr_deassert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_DISABLE);
}
}
/* copy response into cmd->resp */ /* copy response into cmd->resp */
switch (c->resp_type) { switch (c->resp_type) {
case RESP_LEN_48_CHK_BUSY: case RESP_LEN_48_CHK_BUSY:
@ -507,98 +627,6 @@ mmc_send_cmd(struct mmc_command *c)
return 1; return 1;
} }
if (cmd & MMCHS_SD_CMD_DP_DATA) {
count = 0;
assert(c->data_len);
if (cmd & MMCHS_SD_CMD_DDIR_READ) {
if (intr_wait(MMCHS_SD_IE_BRR_ENABLE_ENABLE)) {
intr_assert(MMCHS_SD_IE_BRR_ENABLE_ENABLE);
mmc_log_warn(&log,
"Timeout waiting for interrupt\n");
return 1;
}
if (!(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BRE_EN))
{
mmc_log_warn(&log,
"Problem BRE should be true\n");
return 1; /* We are not allowed to read
* data from the data buffer */
}
for (count = 0; count < c->data_len; count += 4) {
value = read32(base_address + MMCHS_SD_DATA);
c->data[count] = *((char *) &value);
c->data[count + 1] = *((char *) &value + 1);
c->data[count + 2] = *((char *) &value + 2);
c->data[count + 3] = *((char *) &value + 3);
}
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_ENABLE)) {
intr_assert(MMCHS_SD_IE_TC_ENABLE_ENABLE);
mmc_log_warn(&log,
"Timeout waiting for interrupt\n");
return 1;
}
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_TC_ENABLE_CLEAR);
/* clear and disable the bbr interrupt */
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_BRR_ENABLE_CLEAR);
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BRR_ENABLE,
MMCHS_SD_IE_BRR_ENABLE_DISABLE);
} else {
/* Wait for the MMCHS_SD_IE_BWR_ENABLE interrupt */
if (intr_wait(MMCHS_SD_IE_BWR_ENABLE)) {
intr_assert(MMCHS_SD_IE_BWR_ENABLE);
mmc_log_warn(&log, "WFI failed\n");
return 1;
}
/* clear the interrupt directly */
intr_assert(MMCHS_SD_IE_BWR_ENABLE);
if (!(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BWE_EN))
{
mmc_log_warn(&log,
"Error expected Buffer to be write enabled\n");
return 1; /* not ready to write data */
}
for (count = 0; count < 512; count += 4) {
while (!(read32(base_address +
MMCHS_SD_PSTATE) &
MMCHS_SD_PSTATE_BWE_EN)) {
mmc_log_trace(&log,
"Error expected Buffer to be write enabled(%d)\n",
count);
}
*((char *) &value) = c->data[count];
*((char *) &value + 1) = c->data[count + 1];
*((char *) &value + 2) = c->data[count + 2];
*((char *) &value + 3) = c->data[count + 3];
write32(base_address + MMCHS_SD_DATA, value);
}
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
intr_assert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
mmc_log_warn(&log,
"(Write) Timeout waiting for transfer complete\n");
return 1;
}
intr_assert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_DISABLE);
}
}
return ret; return ret;
} }

View File

@ -159,6 +159,9 @@
#define MMCHS_SD_STAT_CC (0x1 << 0) /* Command complete status */ #define MMCHS_SD_STAT_CC (0x1 << 0) /* Command complete status */
#define MMCHS_SD_STAT_CC_UNRAISED (0x0 << 0) /* Command not completed */ #define MMCHS_SD_STAT_CC_UNRAISED (0x0 << 0) /* Command not completed */
#define MMCHS_SD_STAT_CC_RAISED (0x1 << 0) /* Command completed */ #define MMCHS_SD_STAT_CC_RAISED (0x1 << 0) /* Command completed */
#define MMCHS_SD_STAT_TC (0x1 << 1) /* Transfer complete status */
#define MMCHS_SD_STAT_TC_UNRAISED (0x0 << 1) /* Transfer not completed */
#define MMCHS_SD_STAT_TC_RAISED (0x1 << 1) /* Transfer completed */
#define MMCHS_SD_IE_ERROR_MASK (0xff << 15 | 0x3 << 24 | 0x03 << 28) #define MMCHS_SD_IE_ERROR_MASK (0xff << 15 | 0x3 << 24 | 0x03 << 28)

View File

@ -557,6 +557,7 @@ service mmc
64 64
83 83
; # IRQs allowed ; # IRQs allowed
priority 4; # priority queue 4
}; };
service fb service fb