common: Drop linux/bitops.h from common header
[platform/kernel/u-boot.git] / drivers / mmc / bcm2835_sdhost.c
index 752c660..c4876e8 100644 (file)
@@ -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 <clk.h>
 #include <common.h>
 #include <asm/arch/msg.h>
 #include <asm/arch/mbox.h>
 #include <asm/unaligned.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/compat.h>
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/sizes.h>
@@ -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;
 }