arm:mmc driver refactor.

Generalize the usage of mmc_send_cmd function to allow
it to transfer data and remove direct invocations to
mmchs_send_cmd.

Change-Id: Iabb9a7d3f4ec57536407b369531ded2b8b649cce
This commit is contained in:
Kees Jongenburger 2013-06-13 14:53:59 +02:00 committed by Kees Jongenburger
parent 2e3046757a
commit 718114d9b0

View File

@ -3,6 +3,7 @@
#include <minix/com.h> #include <minix/com.h>
#include <minix/vm.h> #include <minix/vm.h>
#include <minix/spin.h> #include <minix/spin.h>
#include <minix/mmio.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/time.h> #include <sys/time.h>
@ -41,6 +42,7 @@ static int hook_id = 1;
#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
*/ */
@ -50,37 +52,6 @@ static struct mmclog log = {
.log_func = default_log .log_func = default_log
}; };
#define REG(x)(*((volatile uint32_t *)(x)))
#define BIT(x)(0x1 << x)
/* Write a uint32_t value to a memory address. */
static inline void
write32(uint32_t address, uint32_t value)
{
REG(address) = value;
}
/* Read an uint32_t from a memory address */
static inline uint32_t
read32(uint32_t address)
{
return REG(address);
}
/* Set a 32 bits value depending on a mask */
static inline void
set32(uint32_t address, uint32_t mask, uint32_t value)
{
uint32_t val;
val = read32(address);
/* clear the bits */
val &= ~(mask);
/* apply the value using the mask */
val |= (value & mask);
write32(address, val);
}
static uint32_t base_address; static uint32_t base_address;
/* /*
@ -150,7 +121,7 @@ mmchs_init(uint32_t instance)
value |= MMCHS_SD_SYSCONFIG_ENAWAKEUP_EN; value |= MMCHS_SD_SYSCONFIG_ENAWAKEUP_EN;
/* Smart-idle */ /* Smart-idle */
value |= MMCHS_SD_SYSCONFIG_SIDLEMODE_IDLE; value |= MMCHS_SD_SYSCONFIG_SIDLEMODE_IDLE;
/* Booth the interface and functional can be switched off */ /* Both the interface and functional can be switched off */
value |= MMCHS_SD_SYSCONFIG_CLOCKACTIVITY_OFF; value |= MMCHS_SD_SYSCONFIG_CLOCKACTIVITY_OFF;
/* Go into wake-up mode when possible */ /* Go into wake-up mode when possible */
value |= MMCHS_SD_SYSCONFIG_STANDBYMODE_WAKEUP_INTERNAL; value |= MMCHS_SD_SYSCONFIG_STANDBYMODE_WAKEUP_INTERNAL;
@ -233,7 +204,7 @@ mmchs_init(uint32_t instance)
* 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, 0x0fffffffu);
/* clean 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,
0xffffffffu); 0xffffffffu);
@ -317,7 +288,8 @@ intr_wait(int mask)
int ipc_status; int ipc_status;
int ticks = SANE_TIMEOUT * sys_hz() / 1000000; int ticks = SANE_TIMEOUT * sys_hz() / 1000000;
if (ticks <= 0) ticks =1; if (ticks <= 0)
ticks = 1;
while (1) { while (1) {
int rr; int rr;
sys_setalarm(ticks, 0); sys_setalarm(ticks, 0);
@ -340,7 +312,9 @@ intr_wait(int mask)
} else if (v & (1 << 15)) { } else if (v & (1 << 15)) {
return 1; /* error */ return 1; /* error */
} else { } else {
mmc_log_debug(&log, "unexpected HW interrupt 0x%08x mask 0X%08x\n", v, mask); mmc_log_debug(&log,
"unexpected HW interrupt 0x%08x mask 0X%08x\n",
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");
@ -374,13 +348,17 @@ intr_wait(int mask)
counter++; counter++;
v = read32(base_address + MMCHS_SD_STAT); v = read32(base_address + MMCHS_SD_STAT);
if (spin_check(&spin) == FALSE) { if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "Timeout waiting for interrupt (%d) value 0x%08x mask 0x%08x\n",counter, v,mask); mmc_log_warn(&log,
"Timeout waiting for interrupt (%d) value 0x%08x mask 0x%08x\n",
counter, v, mask);
return 1; return 1;
} }
if (v & mask) { if (v & mask) {
return 0; return 0;
} else if (v & 0xFF00) { } else if (v & 0xFF00) {
mmc_log_debug(&log, "unexpected HW interrupt (%d) 0x%08x mask 0x%08x\n", v, mask); mmc_log_debug(&log,
"unexpected HW interrupt (%d) 0x%08x mask 0x%08x\n",
v, mask);
return 1; return 1;
} }
} }
@ -448,6 +426,8 @@ mmc_send_cmd(struct mmc_command *c)
/* convert the command to a hsmmc command */ /* convert the command to a hsmmc command */
int ret; int ret;
uint32_t cmd, arg; uint32_t cmd, arg;
uint32_t count;
uint32_t value;
cmd = MMCHS_SD_CMD_INDX_CMD(c->cmd); cmd = MMCHS_SD_CMD_INDX_CMD(c->cmd);
arg = c->args; arg = c->args;
@ -468,6 +448,45 @@ mmc_send_cmd(struct mmc_command *c)
return 1; return 1;
} }
/* read single block */
if (c->cmd == MMC_READ_BLOCK_SINGLE) {
cmd |= MMCHS_SD_CMD_DP_DATA; /* Command with data transfer */
cmd |= MMCHS_SD_CMD_MSBS_SINGLE; /* single block */
cmd |= MMCHS_SD_CMD_DDIR_READ; /* read data from card */
}
/* write single block */
if (c->cmd == MMC_WRITE_BLOCK_SINGLE) {
cmd |= MMCHS_SD_CMD_DP_DATA; /* Command with data transfer */
cmd |= MMCHS_SD_CMD_MSBS_SINGLE; /* single block */
cmd |= MMCHS_SD_CMD_DDIR_WRITE; /* write to the card */
}
/* check we are in a sane state */
if ((read32(base_address + MMCHS_SD_STAT) & 0xffffu)) {
mmc_log_warn(&log, "%s, interrupt already raised stat %08x\n",
__FUNCTION__, read32(base_address + MMCHS_SD_STAT));
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_CC_ENABLE_CLEAR);
}
if (cmd & MMCHS_SD_CMD_DP_DATA) {
if (cmd & MMCHS_SD_CMD_DDIR_READ) {
/* if we are going to read enable the buffer ready
* interrupt */
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BRR_ENABLE,
MMCHS_SD_IE_BRR_ENABLE_ENABLE);
} else {
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_ENABLE);
}
}
set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512);
ret = mmchs_send_cmd(cmd, arg); ret = mmchs_send_cmd(cmd, arg);
/* copy response into cmd->resp */ /* copy response into cmd->resp */
@ -488,14 +507,120 @@ 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;
} }
static struct mmc_command command; int
mmc_send_app_cmd(struct sd_card_regs *card, struct mmc_command *c)
{
struct mmc_command command;
command.cmd = MMC_APP_CMD;
command.resp_type = RESP_LEN_48;
command.args = MMC_ARG_RCA(card->rca);
if (mmc_send_cmd(&command)) {
return 1;
}
return mmc_send_cmd(c);
}
int int
card_goto_idle_state() card_goto_idle_state()
{ {
struct mmc_command command;
command.cmd = MMC_GO_IDLE_STATE; command.cmd = MMC_GO_IDLE_STATE;
command.resp_type = NO_RESPONSE; command.resp_type = NO_RESPONSE;
command.args = 0x00; command.args = 0x00;
@ -509,12 +634,16 @@ card_goto_idle_state()
int int
card_identification() card_identification()
{ {
struct mmc_command command;
command.cmd = MMC_SEND_EXT_CSD; command.cmd = MMC_SEND_EXT_CSD;
command.resp_type = RESP_LEN_48; command.resp_type = RESP_LEN_48;
command.args = MMCHS_SD_ARG_CMD8_VHS | MMCHS_SD_ARG_CMD8_CHECK_PATTERN; command.args = MMCHS_SD_ARG_CMD8_VHS | MMCHS_SD_ARG_CMD8_CHECK_PATTERN;
if (mmc_send_cmd(&command)) { if (mmc_send_cmd(&command)) {
// We currently only support 2.0, /* We currently only support 2.0, and 1.0 won't respond to
* this request */
mmc_log_warn(&log, "%s, non SDHC card inserted\n",
__FUNCTION__);
return 1; return 1;
} }
@ -530,6 +659,7 @@ card_identification()
int int
card_query_voltage_and_type(struct sd_card_regs *card) card_query_voltage_and_type(struct sd_card_regs *card)
{ {
struct mmc_command command;
spin_t spin; spin_t spin;
command.cmd = MMC_APP_CMD; command.cmd = MMC_APP_CMD;
command.resp_type = RESP_LEN_48; command.resp_type = RESP_LEN_48;
@ -594,7 +724,7 @@ card_query_voltage_and_type(struct sd_card_regs *card)
int int
card_identify(struct sd_card_regs *card) card_identify(struct sd_card_regs *card)
{ {
struct mmc_command command;
/* Send cmd 2 (all_send_cid) and expect 136 bits response */ /* Send cmd 2 (all_send_cid) and expect 136 bits response */
command.cmd = MMC_ALL_SEND_CID; command.cmd = MMC_ALL_SEND_CID;
command.resp_type = RESP_LEN_136; command.resp_type = RESP_LEN_136;
@ -629,6 +759,9 @@ card_identify(struct sd_card_regs *card)
int int
card_csd(struct sd_card_regs *card) card_csd(struct sd_card_regs *card)
{ {
/* Read the Card Specific Data register */
struct mmc_command command;
/* send_csd -> r2 response */ /* send_csd -> r2 response */
command.cmd = MMC_SEND_CSD; command.cmd = MMC_SEND_CSD;
command.resp_type = RESP_LEN_136; command.resp_type = RESP_LEN_136;
@ -657,6 +790,7 @@ card_csd(struct sd_card_regs *card)
int int
select_card(struct sd_card_regs *card) select_card(struct sd_card_regs *card)
{ {
struct mmc_command command;
command.cmd = MMC_SELECT_CARD; command.cmd = MMC_SELECT_CARD;
command.resp_type = RESP_LEN_48_CHK_BUSY; command.resp_type = RESP_LEN_48_CHK_BUSY;
@ -672,60 +806,19 @@ int
read_single_block(struct sd_card_regs *card, read_single_block(struct sd_card_regs *card,
uint32_t blknr, unsigned char *buf) uint32_t blknr, unsigned char *buf)
{ {
uint32_t count; struct mmc_command command;
uint32_t value;
count = 0; command.cmd = MMC_READ_BLOCK_SINGLE;
command.args = blknr;
command.resp_type = RESP_LEN_48;
command.data = buf;
command.data_len = 512;
set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BRR_ENABLE, if (mmc_send_cmd(&command)) {
MMCHS_SD_IE_BRR_ENABLE_ENABLE);
set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512);
/* read single block */
if (mmchs_send_cmd(MMCHS_SD_CMD_INDX_CMD(MMC_READ_BLOCK_SINGLE)
| MMCHS_SD_CMD_DP_DATA /* Command with data transfer */
| MMCHS_SD_CMD_RSP_TYPE_48B /* type (R1) */
| MMCHS_SD_CMD_MSBS_SINGLE /* single block */
| MMCHS_SD_CMD_DDIR_READ /* read data from card */
, blknr)) {
mmc_log_warn(&log, "Error sending command\n"); mmc_log_warn(&log, "Error sending command\n");
return 1; return 1;
} }
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 < 512; count += 4) {
value = read32(base_address + MMCHS_SD_DATA);
buf[count] = *((char *) &value);
buf[count + 1] = *((char *) &value + 1);
buf[count + 2] = *((char *) &value + 2);
buf[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);
return 0; return 0;
} }
@ -733,74 +826,22 @@ int
write_single_block(struct sd_card_regs *card, write_single_block(struct sd_card_regs *card,
uint32_t blknr, unsigned char *buf) uint32_t blknr, unsigned char *buf)
{ {
uint32_t count; struct mmc_command command;
uint32_t value;
if ((read32(base_address + MMCHS_SD_STAT) & 0xffffu)) {
mmc_log_warn(&log, "%s, interrupt already raised stat %08x\n",
__FUNCTION__, read32(base_address + MMCHS_SD_STAT));
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_CC_ENABLE_CLEAR);
// return 1;
}
set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_ENABLE);
count = 0;
// set32(base_address + MMCHS_SD_IE, 0xfff , 0xfff);
set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512); set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512);
command.cmd = MMC_WRITE_BLOCK_SINGLE;
command.args = blknr;
command.resp_type = RESP_LEN_48;
command.data = buf;
command.data_len = 512;
/* write single block */ /* write single block */
if (mmchs_send_cmd(MMCHS_SD_CMD_INDX_CMD(MMC_WRITE_BLOCK_SINGLE) if (mmc_send_cmd(&command)) {
| MMCHS_SD_CMD_DP_DATA /* Command with data transfer */
| MMCHS_SD_CMD_RSP_TYPE_48B /* type (R1b) */
| MMCHS_SD_CMD_MSBS_SINGLE /* single block */
| MMCHS_SD_CMD_DDIR_WRITE /* write to the card */
, blknr)) {
mmc_log_warn(&log, "Write single block command failed\n"); mmc_log_warn(&log, "Write single block command failed\n");
mmc_log_trace(&log, "STAT=(0x%08x)\n",
read32(base_address + MMCHS_SD_STAT));
return 1; return 1;
} }
/* 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) = buf[count];
*((char *) &value + 1) = buf[count + 1];
*((char *) &value + 2) = buf[count + 2];
*((char *) &value + 3) = buf[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 0; return 0;
} }