From patchwork Wed May 23 20:24:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 136677 Delivered-To: patch@linaro.org Received: by 2002:a2e:9706:0:0:0:0:0 with SMTP id r6-v6csp1338541lji; Wed, 23 May 2018 13:24:59 -0700 (PDT) X-Google-Smtp-Source: AB8JxZqid7AfzUnJZVoXwkxPEVg6kZMze7wOcwGuBebFRJs/yyYArV0ZZp4ammoqwryPQR5O9Tjf X-Received: by 2002:a50:b1b5:: with SMTP id m50-v6mr8717508edd.66.1527107099669; Wed, 23 May 2018 13:24:59 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527107099; cv=none; d=google.com; s=arc-20160816; b=JzOFoKdG5OpNOAFxhrSpX1J0tx+Aa716ndmlTq671ZUdHQLRnPhWADTlF18uYImXkd 95vEiWhGLRpkYUfOhozyFL+Hht1sXxLvnba/pC559FA4hJPz4kptmnczmzaIsz225CFz r2MYLlPV397MmdXMxiFBbrc1MEh3/wTOQ8E9QEPONVHguUj7pTPtjMokXVOchI9obCuq WCsrbrXcmTAPTgSewEGHhzgy4MDSUrY9MFGsXor1fDIVemOWnbJ4BEpA8JjgYM3dQ7gr IB6putK5MJQ8AE57pkntGffGMAPUmEw2CFDvq4jyaqliLglwz3nSwB+OImSrfDAEFDiY ZHrQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:message-id:date:to:from :arc-authentication-results; bh=ybTliOWfJPs0O8mjGnlpY2RkUAIiLMdrdKJzKmstBco=; b=UzDp6ny9r6hJaOL94IYsD0FldFfYmEAhayWa9NCGo8lv/lQSbkBNJLgoORXTSGMkdz Ft14qhXh8RVLNpwHXL6uvgpZaPIKS6WDU7M7ssDnwg+nFd3qE8U6OAprmBhKMdY9gaxy yUDgB0MgMPmrEab9641BQqh0eAPVgHzgTcgIo033BIxHpvULIBlzPzh7594P+2JsQYqn YtRpJzXKauiD56m83+sEJT02UPqiB8T+y/Eh2o7xdeaF5sRBhOhPhQlsjEnX60+lqK0E aVvGpqNOp7/hWkAE+ZT65jEQJYHsCUn8fJ4ove5KQNCWyaR42b7BZbeS23DJb4SSuUDe +W/w== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Return-Path: Received: from lists.denx.de (dione.denx.de. [81.169.180.215]) by mx.google.com with ESMTP id w46-v6si3818610edd.72.2018.05.23.13.24.59; Wed, 23 May 2018 13:24:59 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) client-ip=81.169.180.215; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by lists.denx.de (Postfix, from userid 105) id 2481BC21E18; Wed, 23 May 2018 20:24:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=RCVD_IN_DNSWL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id AEC66C21C29; Wed, 23 May 2018 20:24:54 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 2E523C21C29; Wed, 23 May 2018 20:24:53 +0000 (UTC) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) by lists.denx.de (Postfix) with ESMTPS id B1BA6C21C29 for ; Wed, 23 May 2018 20:24:52 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 3131FAE3A; Wed, 23 May 2018 20:24:52 +0000 (UTC) From: Alexander Graf To: u-boot@lists.denx.de Date: Wed, 23 May 2018 22:24:51 +0200 Message-Id: <20180523202451.68781-1-agraf@suse.de> X-Mailer: git-send-email 2.12.3 Subject: [U-Boot] [PATCH] mmc: Unirqify bcm2835_sdhost and fix writes X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 Signed-off-by: Alexander Graf --- 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 96428333b0..1ce019af57 100644 --- a/drivers/mmc/bcm2835_sdhost.c +++ b/drivers/mmc/bcm2835_sdhost.c @@ -163,7 +163,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; @@ -173,14 +172,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; @@ -240,17 +232,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; @@ -261,7 +245,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; @@ -273,9 +260,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) @@ -322,6 +311,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); @@ -358,9 +350,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 | @@ -379,21 +370,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); @@ -401,14 +379,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); } @@ -483,36 +456,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; @@ -562,8 +505,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; } @@ -608,159 +549,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) @@ -864,8 +690,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; }