Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.4:ARM
u-boot-pine64plus.16979
0032-mmc-Unirqify-bcm2835_sdhost-and-fix.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0032-mmc-Unirqify-bcm2835_sdhost-and-fix.patch of Package u-boot-pine64plus.16979
From dd045b9aa42f6965907d26e3f7d5e4ffbf1f0906 Mon Sep 17 00:00:00 2001 From: Alexander Graf <agraf@suse.de> Date: Wed, 23 May 2018 22:24:51 +0200 Subject: [PATCH] mmc: Unirqify bcm2835_sdhost and fix writes The bcm2835 sdhost driver has a problem with "write multiple" commands. It seems to boil down to the fact that the controller dislikes its FIFO to get drained at the end of a block when a write multiple blocks command is in flight. The easy fix is to simply get rid of all the IRQ driven logic and make the driver push as much data into the FIFO as it can. That way we never drain and we never run into the problem. Reported-by: Jan Leonhardt <jan@cyberdesigner.net> Signed-off-by: Alexander Graf <agraf@suse.de> --- drivers/mmc/bcm2835_sdhost.c | 265 +++++++---------------------------- 1 file changed, 47 insertions(+), 218 deletions(-) diff --git a/drivers/mmc/bcm2835_sdhost.c b/drivers/mmc/bcm2835_sdhost.c index bccd182e50..3c9f799948 100644 --- a/drivers/mmc/bcm2835_sdhost.c +++ b/drivers/mmc/bcm2835_sdhost.c @@ -166,7 +166,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; @@ -176,14 +175,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; @@ -243,17 +235,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; + int timediff = 0; while (1) { u32 edm, fsm; @@ -264,7 +248,10 @@ 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; @@ -276,9 +263,11 @@ static void bcm2835_wait_transfer_complete(struct bcm2835_host *host) "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) @@ -325,6 +314,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); @@ -361,9 +353,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 | @@ -382,21 +373,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); @@ -404,14 +382,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); } @@ -486,36 +459,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; @@ -565,8 +508,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; } @@ -611,159 +552,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) @@ -867,8 +693,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; }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor