Hi Gerd, first of all here my wishlist for the next round of this driver: * compile test the series with ARM and ARM64 * add me in CC for all patches of the series * run checkpatch.pl before submission * apply the patch series to your cgit repo > Gerd Hoffmann <kraxel@xxxxxxxxxx> hat am 3. Februar 2017 um 13:11 geschrieben: > > > From: Eric Anholt <eric@xxxxxxxxxx> > > The 2835 has two SD controllers: The Arasan sdhci controller (supported > by the iproc driver) and a custom sdhost controller. This patch adds a > driver for the latter. > > The sdhci controller supports both sdcard and sdio. The sdhost > controller supports the sdcard only, but has better performance. Also Sorry, for the confusion. I was wrong. According to the registers the SDHOST should also support SDIO. It's a feature we could implement later. > note that the rpi3 has sdio wifi, so driving the sdcard with the sdhost > controller allows to use the sdhci controller for wifi support. > > The configuration is done by devicetree via pin muxing. Both SD > controller are available on the same pins (2 pin groups = pin 22 to 27 + > pin 48 to 53). So it's possible to use both SD controllers at the same > time with different pin groups. > > The code was originally written by Phil Elwell in the downstream > Rasbperry Pi tree, and I did a major cleanup on it (+319, -707 lines > out of the original 2055) for inclusion. I think it would be helpful to known the downstream commit, because this commit [1] doesn't seem to be included. [1] - https://github.com/raspberrypi/linux/commit/ea4b1c5c2ddbb6caba43ab9b0103542a4ca7e1f0 I've found and fixed a lot of issues in this version and i think it would be better if i send you the patches for squashing them all together. Here is the preview for my patch series: Stefan Wahren (15): mmc: bcm2835: Add missing include for threaded irq mmc: bcm2835: Remove CMD_DALLY_US mmc: bcm2835: Fix pio_timeout handling mmc: bcm2835: Remove unnecessary return in bcm2835_data_irq mmc: bcm2835: Handle error cases during probe mmc: bcm2835: Print clk_max as decimal mmc: bcm2835: Downrate message in case of PIO fallback mmc: bcm2835: Don't unveil the data pointer mmc: bcm2835: remove unused host members mmc: bcm2835: Avoid unnecessary linebreaks mmc: bcm2835: Add leading zero to register dumps mmc: bcm2835: Align struct members with tabs mmc: bcm2835: Rearrange bcm2835_finish_request() mmc: bcm2835: Rearrange bcm2835_dma_complete_work() mmc: bcm2835: Rename Kconfig switch I'll send it after my tests. In the following review i will mention only the issues which aren't fixed in my patch series. > diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c > new file mode 100644 > index 0000000..5ea8bd6 > --- /dev/null > +++ b/drivers/mmc/host/bcm2835.c > @@ -0,0 +1,1521 @@ > +/* > + * bcm2835 sdhost driver. > + * > + * The 2835 has two SD controllers: The Arasan sdhci controller > + * (supported by the iproc driver) and a custom sdhost controller > + * (supported by this driver). > + * > + * The sdhci controller supports both sdcard and sdio. The sdhost > + * controller supports the sdcard only, but has better performance. > + * Also note that the rpi3 has sdio wifi, so driving the sdcard with > + * the sdhost controller allows to use the sdhci controller for wifi > + * support. > + * > + * The configuration is done by devicetree via pin muxing. Both > + * SD controller are available on the same pins (2 pin groups = pin 22 > + * to 27 + pin 48 to 53). So it's possible to use both SD controllers > + * at the same time with different pin groups. > + * > + * Author: Phil Elwell <phil@xxxxxxxxxxxxxxx> > + * Copyright (C) 2015-2016 Raspberry Pi (Trading) Ltd. > + * > + * Based on > + * mmc-bcm2835.c by Gellert Weisz > + * which is, in turn, based on > + * sdhci-bcm2708.c by Broadcom > + * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko > + * sdhci.c and sdhci-pci.c by Pierre Ossman > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/device.h> > +#include <linux/dmaengine.h> > +#include <linux/dma-mapping.h> > +#include <linux/err.h> > +#include <linux/highmem.h> > +#include <linux/io.h> > +#include <linux/iopoll.h> > +#include <linux/module.h> > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <linux/platform_device.h> > +#include <linux/scatterlist.h> > +#include <linux/time.h> > +#include <linux/workqueue.h> > + > +#include <linux/mmc/host.h> > +#include <linux/mmc/mmc.h> > +#include <linux/mmc/sd.h> > + > +#define SDCMD 0x00 /* Command to SD card - 16 R/W */ > +#define SDARG 0x04 /* Argument to SD card - 32 R/W */ > +#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */ > +#define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */ > +#define SDRSP0 0x10 /* SD card response (31:0) - 32 R */ > +#define SDRSP1 0x14 /* SD card response (63:32) - 32 R */ > +#define SDRSP2 0x18 /* SD card response (95:64) - 32 R */ > +#define SDRSP3 0x1c /* SD card response (127:96) - 32 R */ > +#define SDHSTS 0x20 /* SD host status - 11 R */ > +#define SDVDD 0x30 /* SD card power control - 1 R/W */ > +#define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */ > +#define SDHCFG 0x38 /* Host configuration - 2 R/W */ > +#define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */ > +#define SDDATA 0x40 /* Data to/from SD card - 32 R/W */ > +#define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */ > + > +#define SDCMD_NEW_FLAG 0x8000 > +#define SDCMD_FAIL_FLAG 0x4000 > +#define SDCMD_BUSYWAIT 0x800 > +#define SDCMD_NO_RESPONSE 0x400 > +#define SDCMD_LONG_RESPONSE 0x200 > +#define SDCMD_WRITE_CMD 0x80 > +#define SDCMD_READ_CMD 0x40 > +#define SDCMD_CMD_MASK 0x3f > + > +#define SDCDIV_MAX_CDIV 0x7ff > + > +#define SDHSTS_BUSY_IRPT 0x400 > +#define SDHSTS_BLOCK_IRPT 0x200 > +#define SDHSTS_SDIO_IRPT 0x100 > +#define SDHSTS_REW_TIME_OUT 0x80 > +#define SDHSTS_CMD_TIME_OUT 0x40 > +#define SDHSTS_CRC16_ERROR 0x20 > +#define SDHSTS_CRC7_ERROR 0x10 > +#define SDHSTS_FIFO_ERROR 0x08 > +/* Reserved */ > +/* Reserved */ > +#define SDHSTS_DATA_FLAG 0x01 > + > +#define SDHSTS_TRANSFER_ERROR_MASK (SDHSTS_CRC7_ERROR | \ > + SDHSTS_CRC16_ERROR | \ > + SDHSTS_REW_TIME_OUT | \ > + SDHSTS_FIFO_ERROR) > + > +#define SDHSTS_ERROR_MASK (SDHSTS_CMD_TIME_OUT | \ > + SDHSTS_TRANSFER_ERROR_MASK) > + > +#define SDHCFG_BUSY_IRPT_EN BIT(10) > +#define SDHCFG_BLOCK_IRPT_EN BIT(8) > +#define SDHCFG_SDIO_IRPT_EN BIT(5) > +#define SDHCFG_DATA_IRPT_EN BIT(4) > +#define SDHCFG_SLOW_CARD BIT(3) > +#define SDHCFG_WIDE_EXT_BUS BIT(2) > +#define SDHCFG_WIDE_INT_BUS BIT(1) > +#define SDHCFG_REL_CMD_LINE BIT(0) > + > +#define SDVDD_POWER_OFF 0 > +#define SDVDD_POWER_ON 1 > + > +#define SDEDM_FORCE_DATA_MODE BIT(19) > +#define SDEDM_CLOCK_PULSE BIT(20) > +#define SDEDM_BYPASS BIT(21) > + > +#define SDEDM_WRITE_THRESHOLD_SHIFT 9 > +#define SDEDM_READ_THRESHOLD_SHIFT 14 > +#define SDEDM_THRESHOLD_MASK 0x1f > + > +#define SDEDM_FSM_MASK 0xf > +#define SDEDM_FSM_IDENTMODE 0x0 > +#define SDEDM_FSM_DATAMODE 0x1 > +#define SDEDM_FSM_READDATA 0x2 > +#define SDEDM_FSM_WRITEDATA 0x3 > +#define SDEDM_FSM_READWAIT 0x4 > +#define SDEDM_FSM_READCRC 0x5 > +#define SDEDM_FSM_WRITECRC 0x6 > +#define SDEDM_FSM_WRITEWAIT1 0x7 > +#define SDEDM_FSM_POWERDOWN 0x8 > +#define SDEDM_FSM_POWERUP 0x9 > +#define SDEDM_FSM_WRITESTART1 0xa > +#define SDEDM_FSM_WRITESTART2 0xb > +#define SDEDM_FSM_GENPULSES 0xc > +#define SDEDM_FSM_WRITEWAIT2 0xd > +#define SDEDM_FSM_STARTPOWDOWN 0xf > + > +#define SDDATA_FIFO_WORDS 16 > + > +#define FIFO_READ_THRESHOLD 4 > +#define FIFO_WRITE_THRESHOLD 4 > +#define SDDATA_FIFO_PIO_BURST 8 > +#define CMD_DALLY_US 1 > + > +struct bcm2835_host { > + spinlock_t lock; > + struct mutex mutex; Would be nice to have a comment for the lock and mutex. > + > + void __iomem *ioaddr; > + u32 phys_addr; > + > + struct mmc_host *mmc; > + struct platform_device *pdev; > + > + u32 pio_timeout; /* In jiffies */ > + int clock; /* Current clock speed */ > + unsigned int max_clk; /* Max possible freq */ > + struct work_struct dma_work; > + struct delayed_work timeout_work; /* Timer for timeouts */ > + struct sg_mapping_iter sg_miter; /* SG state for PIO */ > + unsigned int blocks; /* remaining PIO blocks */ > + int irq; /* Device IRQ */ > + > + u32 ns_per_fifo_word; > + > + /* cached registers */ > + u32 hcfg; > + u32 cdiv; > + > + struct mmc_request *mrq; /* Current request */ > + struct mmc_command *cmd; /* Current command */ > + struct mmc_data *data; /* Current data request */ > + bool data_complete:1;/* Data finished before cmd */ > + bool flush_fifo:1; /* Drain the fifo when finish */ > + bool use_busy:1; /* Wait for busy interrupt */ > + bool use_sbc:1; /* Send CMD23 */ It would be nice to get the rid off use_busy and use_sbc. Do you see any chance for use_busy? > + > + /* for threaded irq handler */ > + bool irq_block; > + bool irq_busy; > + bool irq_data; > + > + /* DMA part */ > + struct dma_chan *dma_chan_rx; > + struct dma_chan *dma_chan_tx; > + struct dma_chan *dma_chan; > + struct dma_async_tx_descriptor *dma_desc; > + u32 dma_dir; > + u32 drain_words; > + struct page *drain_page; > + u32 drain_offset; > + bool use_dma; > + > + int max_delay; /* maximum length of time spent waiting */ > + u32 pio_limit; /* Maximum block count for PIO (0 = DMA) */ > +}; > + > +static void bcm2835_dumpcmd(struct bcm2835_host *host, > + struct mmc_command *cmd, > + const char *label) > +{ > + struct device *dev = &host->pdev->dev; > + > + if (!cmd) > + return; > + > + dev_dbg(dev, "%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n", > + (cmd == host->cmd) ? '>' : ' ', > + label, cmd->opcode, cmd->arg, cmd->flags, > + cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], > + cmd->error); > +} > + > +static void bcm2835_dumpregs(struct bcm2835_host *host) > +{ > + struct mmc_request *mrq = host->mrq; > + struct device *dev = &host->pdev->dev; > + > + if (mrq) { > + bcm2835_dumpcmd(host, mrq->sbc, "sbc"); > + bcm2835_dumpcmd(host, mrq->cmd, "cmd"); > + if (mrq->data) { > + dev_dbg(dev, "data blocks %x blksz %x - err %d\n", > + mrq->data->blocks, > + mrq->data->blksz, > + mrq->data->error); > + } > + bcm2835_dumpcmd(host, mrq->stop, "stop"); > + } > + > + dev_dbg(dev, "=========== REGISTER DUMP ===========\n"); > + dev_dbg(dev, "SDCMD 0x%08x\n", readl(host->ioaddr + SDCMD)); > + dev_dbg(dev, "SDARG 0x%08x\n", readl(host->ioaddr + SDARG)); > + dev_dbg(dev, "SDTOUT 0x%08x\n", readl(host->ioaddr + SDTOUT)); > + dev_dbg(dev, "SDCDIV 0x%08x\n", readl(host->ioaddr + SDCDIV)); > + dev_dbg(dev, "SDRSP0 0x%08x\n", readl(host->ioaddr + SDRSP0)); > + dev_dbg(dev, "SDRSP1 0x%08x\n", readl(host->ioaddr + SDRSP1)); > + dev_dbg(dev, "SDRSP2 0x%08x\n", readl(host->ioaddr + SDRSP2)); > + dev_dbg(dev, "SDRSP3 0x%08x\n", readl(host->ioaddr + SDRSP3)); > + dev_dbg(dev, "SDHSTS 0x%08x\n", readl(host->ioaddr + SDHSTS)); > + dev_dbg(dev, "SDVDD 0x%08x\n", readl(host->ioaddr + SDVDD)); > + dev_dbg(dev, "SDEDM 0x%08x\n", readl(host->ioaddr + SDEDM)); > + dev_dbg(dev, "SDHCFG 0x%08x\n", readl(host->ioaddr + SDHCFG)); > + dev_dbg(dev, "SDHBCT 0x%08x\n", readl(host->ioaddr + SDHBCT)); > + dev_dbg(dev, "SDHBLC 0x%08x\n", readl(host->ioaddr + SDHBLC)); > + dev_dbg(dev, "===========================================\n"); > +} > + > +static void bcm2835_reset_internal(struct bcm2835_host *host) > +{ > + u32 temp; > + > + writel(SDVDD_POWER_OFF, host->ioaddr + SDVDD); > + writel(0, host->ioaddr + SDCMD); > + writel(0, host->ioaddr + SDARG); > + writel(0xf00000, host->ioaddr + SDTOUT); > + writel(0, host->ioaddr + SDCDIV); > + writel(0x7f8, host->ioaddr + SDHSTS); /* Write 1s to clear */ Any chance to eliminate the bit magic for SDTOUT and SDHSTS? > + writel(0, host->ioaddr + SDHCFG); > + writel(0, host->ioaddr + SDHBCT); > + writel(0, host->ioaddr + SDHBLC); > + > + /* Limit fifo usage due to silicon bug */ > + temp = readl(host->ioaddr + SDEDM); > + temp &= ~((SDEDM_THRESHOLD_MASK << SDEDM_READ_THRESHOLD_SHIFT) | > + (SDEDM_THRESHOLD_MASK << SDEDM_WRITE_THRESHOLD_SHIFT)); > + temp |= (FIFO_READ_THRESHOLD << SDEDM_READ_THRESHOLD_SHIFT) | > + (FIFO_WRITE_THRESHOLD << SDEDM_WRITE_THRESHOLD_SHIFT); > + writel(temp, host->ioaddr + SDEDM); > + msleep(20); > + writel(SDVDD_POWER_ON, host->ioaddr + SDVDD); > + msleep(20); > + host->clock = 0; > + writel(host->hcfg, host->ioaddr + SDHCFG); > + writel(host->cdiv, host->ioaddr + SDCDIV); > +} > + > ... > + > +static void bcm2835_finish_data(struct bcm2835_host *host) > +{ > + struct device *dev = &host->pdev->dev; > + struct mmc_data *data; > + > + data = host->data; > + > + dev_dbg(dev, "finish_data(error %d, stop %d, sbc %d)\n", > + data->error, data->stop ? 1 : 0, > + host->mrq->sbc ? 1 : 0); > + > + host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN); > + writel(host->hcfg, host->ioaddr + SDHCFG); > + > + data->bytes_xfered = data->error ? 0 : (data->blksz * data->blocks); > + > + 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 %x\n", > + readl(host->ioaddr + SDHSTS)); > + } else { > + bcm2835_transfer_complete(host); > + } > +} > + > +static void bcm2835_finish_command(struct bcm2835_host *host) > +{ > + struct device *dev = &host->pdev->dev; > + struct mmc_command *cmd = host->cmd; > + u32 sdcmd; > + > + dev_dbg(dev, "finish_command(%x)\n", readl(host->ioaddr + SDCMD)); > + > + sdcmd = bcm2835_read_wait_sdcmd(host, 100, true); > + > + /* Check for errors */ > + if (sdcmd & SDCMD_NEW_FLAG) { > + dev_err(dev, "command never completed.\n"); > + bcm2835_dumpregs(host); > + host->cmd->error = -EIO; > + bcm2835_finish_request(host); > + return; > + } else if (sdcmd & SDCMD_FAIL_FLAG) { > + u32 sdhsts = readl(host->ioaddr + SDHSTS); > + > + /* Clear the errors */ > + writel(SDHSTS_ERROR_MASK, host->ioaddr + SDHSTS); > + > + if (!(sdhsts & SDHSTS_CRC7_ERROR) || > + (host->cmd->opcode != MMC_SEND_OP_COND)) { > + if (sdhsts & SDHSTS_CMD_TIME_OUT) { > + host->cmd->error = -ETIMEDOUT; > + } else { > + dev_err(dev, "unexpected command %d error\n", > + host->cmd->opcode); > + bcm2835_dumpregs(host); > + host->cmd->error = -EILSEQ; > + } > + bcm2835_finish_request(host); > + return; > + } > + } > + > + if (cmd->flags & MMC_RSP_PRESENT) { > + if (cmd->flags & MMC_RSP_136) { > + int i; > + > + for (i = 0; i < 4; i++) { > + cmd->resp[3 - i] = > + readl(host->ioaddr + SDRSP0 + i * 4); > + } > + > + dev_dbg(dev, "finish_command %08x %08x %08x %08x\n", > + cmd->resp[0], cmd->resp[1], > + cmd->resp[2], cmd->resp[3]); > + } else { > + cmd->resp[0] = readl(host->ioaddr + SDRSP0); > + dev_dbg(dev, "finish_command %08x\n", cmd->resp[0]); > + } > + } > + > + if (cmd == host->mrq->sbc) { > + /* Finished CMD23, now send actual command. */ > + host->cmd = NULL; > + if (bcm2835_send_command(host, host->mrq->cmd)) { > + if (host->data && host->dma_desc) > + /* DMA transfer starts now, PIO starts > + * after irq > + */ > + bcm2835_start_dma(host); > + > + if (!host->use_busy) > + bcm2835_finish_command(host); Recursion? > + } > + } else if (cmd == host->mrq->stop) { > + /* Finished CMD12 */ > + bcm2835_finish_request(host); > + } else { > + /* Processed actual command. */ > + host->cmd = NULL; > + if (!host->data) > + bcm2835_finish_request(host); > + else if (host->data_complete) > + bcm2835_transfer_complete(host); bcm2835_finish_command() calls bcm2835_transfer_complete() bcm2835_transfer_complete() calls bcm2835_finish_command() This should be avoided. > ... > +int bcm2835_add_host(struct bcm2835_host *host) > +{ > + struct mmc_host *mmc = host->mmc; > + struct device *dev = &host->pdev->dev; > + struct dma_slave_config cfg; > + char pio_limit_string[20]; > + int ret; > + > + bcm2835_reset_internal(host); > + > + mmc->f_max = host->max_clk; > + mmc->f_min = host->max_clk / SDCDIV_MAX_CDIV; > + > + mmc->max_busy_timeout = ~0 / (mmc->f_max / 1000); > + > + dev_dbg(dev, "f_max %d, f_min %d, max_busy_timeout %d\n", > + mmc->f_max, mmc->f_min, mmc->max_busy_timeout); > + > + /* host controller capabilities */ > + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | > + MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE | > + MMC_CAP_CMD23; > + > + spin_lock_init(&host->lock); > + mutex_init(&host->mutex); > + > + if (IS_ERR_OR_NULL(host->dma_chan_tx) || > + IS_ERR_OR_NULL(host->dma_chan_rx)) { > + dev_err(dev, "unable to initialise DMA channels. Falling back to PIO\n"); > + host->use_dma = false; > + } else { > + host->use_dma = true; > + > + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; > + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; > + cfg.slave_id = 13; /* DREQ channel */ Is this the same value which is also defined in the DT? Thanks Stefan -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html