int clock; /* Current clock speed */
unsigned int max_clk; /* Max possible freq */
unsigned int blocks; /* remaining PIO blocks */
- int irq; /* Device IRQ */
u32 ns_per_fifo_word;
struct mmc_cmd *cmd; /* Current command */
struct mmc_data *data; /* Current data request */
- bool data_complete:1;/* Data finished before cmd */
bool use_busy:1; /* Wait for busy interrupt */
- bool wait_data_complete:1; /* Wait for data */
-
- /* for threaded irq handler */
- bool irq_block;
- bool irq_busy;
- bool irq_data;
struct udevice *dev;
struct mmc *mmc;
writel(host->cdiv, host->ioaddr + SDCDIV);
}
-static int bcm2835_finish_command(struct bcm2835_host *host);
-
-static void bcm2835_wait_transfer_complete(struct bcm2835_host *host)
+static int bcm2835_wait_transfer_complete(struct bcm2835_host *host)
{
- int timediff;
- u32 alternate_idle;
-
- alternate_idle = (host->data->flags & MMC_DATA_READ) ?
- SDEDM_FSM_READWAIT : SDEDM_FSM_WRITESTART1;
-
- timediff = 0;
+ int timediff = 0;
while (1) {
u32 edm, fsm;
if ((fsm == SDEDM_FSM_IDENTMODE) ||
(fsm == SDEDM_FSM_DATAMODE))
break;
- if (fsm == alternate_idle) {
+
+ if ((fsm == SDEDM_FSM_READWAIT) ||
+ (fsm == SDEDM_FSM_WRITESTART1) ||
+ (fsm == SDEDM_FSM_READDATA)) {
writel(edm | SDEDM_FORCE_DATA_MODE,
host->ioaddr + SDEDM);
break;
"wait_transfer_complete - still waiting after %d retries\n",
timediff);
bcm2835_dumpregs(host);
- return;
+ return -ETIMEDOUT;
}
}
+
+ return 0;
}
static int bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
fsm_state != SDEDM_FSM_READCRC)) ||
(!is_read &&
(fsm_state != SDEDM_FSM_WRITEDATA &&
+ fsm_state != SDEDM_FSM_WRITEWAIT1 &&
+ fsm_state != SDEDM_FSM_WRITEWAIT2 &&
+ fsm_state != SDEDM_FSM_WRITECRC &&
fsm_state != SDEDM_FSM_WRITESTART1 &&
fsm_state != SDEDM_FSM_WRITESTART2))) {
hsts = readl(host->ioaddr + SDHSTS);
is_read = (host->data->flags & MMC_DATA_READ) != 0;
ret = bcm2835_transfer_block_pio(host, is_read);
-
- if (host->wait_data_complete)
- bcm2835_wait_transfer_complete(host);
+ if (ret)
+ return ret;
sdhsts = readl(host->ioaddr + SDHSTS);
if (sdhsts & (SDHSTS_CRC16_ERROR |
return ret;
}
-static void bcm2835_set_transfer_irqs(struct bcm2835_host *host)
-{
- u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN |
- SDHCFG_BUSY_IRPT_EN;
-
- host->hcfg = (host->hcfg & ~all_irqs) |
- SDHCFG_DATA_IRPT_EN |
- SDHCFG_BUSY_IRPT_EN;
-
- writel(host->hcfg, host->ioaddr + SDHCFG);
-}
-
-static
-void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_cmd *cmd,
- struct mmc_data *data)
+static void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_cmd *cmd,
+ struct mmc_data *data)
{
WARN_ON(host->data);
if (!data)
return;
- host->wait_data_complete = cmd->cmdidx != MMC_CMD_READ_MULTIPLE_BLOCK;
- host->data_complete = false;
-
/* Use PIO */
host->blocks = data->blocks;
- bcm2835_set_transfer_irqs(host);
-
writel(data->blocksize, host->ioaddr + SDHBCT);
writel(data->blocks, host->ioaddr + SDHBLC);
}
return 0;
}
-static int bcm2835_transfer_complete(struct bcm2835_host *host)
-{
- int ret = 0;
-
- WARN_ON(!host->data_complete);
-
- host->data = NULL;
-
- return ret;
-}
-
-static void bcm2835_finish_data(struct bcm2835_host *host)
-{
- host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
- writel(host->hcfg, host->ioaddr + SDHCFG);
-
- host->data_complete = true;
-
- if (host->cmd) {
- /* Data managed to finish before the
- * command completed. Make sure we do
- * things in the proper order.
- */
- dev_dbg(dev, "Finished early - HSTS %08x\n",
- readl(host->ioaddr + SDHSTS));
- } else {
- bcm2835_transfer_complete(host);
- }
-}
-
static int bcm2835_finish_command(struct bcm2835_host *host)
{
struct mmc_cmd *cmd = host->cmd;
/* Processed actual command. */
host->cmd = NULL;
- if (host->data && host->data_complete)
- ret = bcm2835_transfer_complete(host);
return ret;
}
return ret;
}
-static void bcm2835_busy_irq(struct bcm2835_host *host)
-{
- if (WARN_ON(!host->cmd)) {
- bcm2835_dumpregs(host);
- return;
- }
-
- if (WARN_ON(!host->use_busy)) {
- bcm2835_dumpregs(host);
- return;
- }
- host->use_busy = false;
-
- bcm2835_finish_command(host);
-}
-
-static void bcm2835_data_irq(struct bcm2835_host *host, u32 intmask)
+static int bcm2835_transmit(struct bcm2835_host *host)
{
+ u32 intmask = readl(host->ioaddr + SDHSTS);
int ret;
- /*
- * There are no dedicated data/space available interrupt
- * status bits, so it is necessary to use the single shared
- * data/space available FIFO status bits. It is therefore not
- * an error to get here when there is no data transfer in
- * progress.
- */
- if (!host->data)
- return;
-
+ /* Check for errors */
ret = bcm2835_check_data_error(host, intmask);
if (ret)
- goto finished;
-
- if (host->data->flags & MMC_DATA_WRITE) {
- /* Use the block interrupt for writes after the first block */
- host->hcfg &= ~(SDHCFG_DATA_IRPT_EN);
- host->hcfg |= SDHCFG_BLOCK_IRPT_EN;
- writel(host->hcfg, host->ioaddr + SDHCFG);
- bcm2835_transfer_pio(host);
- } else {
- bcm2835_transfer_pio(host);
- host->blocks--;
- if ((host->blocks == 0))
- goto finished;
- }
- return;
+ return ret;
-finished:
- host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
- writel(host->hcfg, host->ioaddr + SDHCFG);
-}
-
-static void bcm2835_data_threaded_irq(struct bcm2835_host *host)
-{
- if (!host->data)
- return;
- if ((host->blocks == 0))
- bcm2835_finish_data(host);
-}
-
-static void bcm2835_block_irq(struct bcm2835_host *host)
-{
- if (WARN_ON(!host->data)) {
- bcm2835_dumpregs(host);
- return;
- }
-
- WARN_ON(!host->blocks);
- if ((--host->blocks == 0))
- bcm2835_finish_data(host);
- else
- bcm2835_transfer_pio(host);
-}
+ ret = bcm2835_check_cmd_error(host, intmask);
+ if (ret)
+ return ret;
-static irqreturn_t bcm2835_irq(int irq, void *dev_id)
-{
- irqreturn_t result = IRQ_NONE;
- struct bcm2835_host *host = dev_id;
- u32 intmask;
-
- intmask = readl(host->ioaddr + SDHSTS);
-
- writel(SDHSTS_BUSY_IRPT |
- SDHSTS_BLOCK_IRPT |
- SDHSTS_SDIO_IRPT |
- SDHSTS_DATA_FLAG,
- host->ioaddr + SDHSTS);
-
- if (intmask & SDHSTS_BLOCK_IRPT) {
- bcm2835_check_data_error(host, intmask);
- host->irq_block = true;
- result = IRQ_WAKE_THREAD;
+ /* Handle wait for busy end */
+ if (host->use_busy && (intmask & SDHSTS_BUSY_IRPT)) {
+ writel(SDHSTS_BUSY_IRPT, host->ioaddr + SDHSTS);
+ host->use_busy = false;
+ bcm2835_finish_command(host);
}
- if (intmask & SDHSTS_BUSY_IRPT) {
- if (!bcm2835_check_cmd_error(host, intmask)) {
- host->irq_busy = true;
- result = IRQ_WAKE_THREAD;
- } else {
- result = IRQ_HANDLED;
+ /* Handle PIO data transfer */
+ if (host->data) {
+ ret = bcm2835_transfer_pio(host);
+ if (ret)
+ return ret;
+ host->blocks--;
+ if (host->blocks == 0) {
+ /* Wait for command to complete for real */
+ ret = bcm2835_wait_transfer_complete(host);
+ if (ret)
+ return ret;
+ /* Transfer complete */
+ host->data = NULL;
}
}
- /* There is no true data interrupt status bit, so it is
- * necessary to qualify the data flag with the interrupt
- * enable bit.
- */
- if ((intmask & SDHSTS_DATA_FLAG) &&
- (host->hcfg & SDHCFG_DATA_IRPT_EN)) {
- bcm2835_data_irq(host, intmask);
- host->irq_data = true;
- result = IRQ_WAKE_THREAD;
- }
-
- return result;
-}
-
-static irqreturn_t bcm2835_threaded_irq(int irq, void *dev_id)
-{
- struct bcm2835_host *host = dev_id;
-
- if (host->irq_block) {
- host->irq_block = false;
- bcm2835_block_irq(host);
- }
-
- if (host->irq_busy) {
- host->irq_busy = false;
- bcm2835_busy_irq(host);
- }
-
- if (host->irq_data) {
- host->irq_data = false;
- bcm2835_data_threaded_irq(host);
- }
-
- return IRQ_HANDLED;
-}
-
-static void bcm2835_irq_poll(struct bcm2835_host *host)
-{
- u32 intmask;
-
- while (1) {
- intmask = readl(host->ioaddr + SDHSTS);
- if (intmask & (SDHSTS_BUSY_IRPT | SDHSTS_BLOCK_IRPT |
- SDHSTS_SDIO_IRPT | SDHSTS_DATA_FLAG)) {
- bcm2835_irq(0, host);
- bcm2835_threaded_irq(0, host);
- return;
- }
- }
+ return 0;
}
static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
}
/* Wait for completion of busy signal or data transfer */
- while (host->use_busy || host->data)
- bcm2835_irq_poll(host);
+ while (host->use_busy || host->data) {
+ ret = bcm2835_transmit(host);
+ if (ret)
+ break;
+ }
return ret;
}