X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fmmc%2Fbcm2835_sdhost.c;h=c4876e81f8cfdf931c594b274ec1673a040ebec3;hb=cd93d625fd751d55c729c78b10f82109d56a5f1d;hp=752c660ea7333c850a4d6fdbd855bb98e533a0e4;hpb=d024236e5a31a2b4b82cbcc98b31b8170fc88d28;p=platform%2Fkernel%2Fu-boot.git diff --git a/drivers/mmc/bcm2835_sdhost.c b/drivers/mmc/bcm2835_sdhost.c index 752c660..c4876e8 100644 --- a/drivers/mmc/bcm2835_sdhost.c +++ b/drivers/mmc/bcm2835_sdhost.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * bcm2835 sdhost driver. * @@ -27,8 +28,6 @@ * sdhci-bcm2708.c by Broadcom * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko * sdhci.c and sdhci-pci.c by Pierre Ossman - * - * SPDX-License-Identifier: GPL-2.0 */ #include #include @@ -37,7 +36,11 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include @@ -164,7 +167,6 @@ struct bcm2835_host { 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; @@ -174,14 +176,7 @@ struct bcm2835_host { 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; @@ -241,17 +236,9 @@ static void bcm2835_reset_internal(struct bcm2835_host *host) 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; + ulong tstart_ms = get_timer(0); while (1) { u32 edm, fsm; @@ -262,21 +249,28 @@ static void bcm2835_wait_transfer_complete(struct bcm2835_host *host) 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; } - /* Error out after 100000 register reads (~1s) */ - if (timediff++ == 100000) { + /* Error out after ~1s */ + ulong tlapse_ms = get_timer(tstart_ms); + if ( tlapse_ms > 1000 /* ms */ ) { + dev_err(host->dev, - "wait_transfer_complete - still waiting after %d retries\n", - timediff); + "wait_transfer_complete - still waiting after %lu ms\n", + tlapse_ms); bcm2835_dumpregs(host); - return; + return -ETIMEDOUT; } } + + return 0; } static int bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read) @@ -323,6 +317,9 @@ 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); @@ -359,9 +356,8 @@ static int bcm2835_transfer_pio(struct bcm2835_host *host) 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 | @@ -380,21 +376,8 @@ static int bcm2835_transfer_pio(struct bcm2835_host *host) 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); @@ -402,14 +385,9 @@ void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_cmd *cmd, 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); } @@ -484,36 +462,6 @@ static int bcm2835_send_command(struct bcm2835_host *host, struct mmc_cmd *cmd, 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; @@ -563,8 +511,6 @@ static int bcm2835_finish_command(struct bcm2835_host *host) /* Processed actual command. */ host->cmd = NULL; - if (host->data && host->data_complete) - ret = bcm2835_transfer_complete(host); return ret; } @@ -609,159 +555,44 @@ static int bcm2835_check_data_error(struct bcm2835_host *host, u32 intmask) 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) @@ -865,8 +696,11 @@ static int bcm2835_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, } /* 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; }