[PATCH v3] mmc: sdhci-moxart: Add MOXA ART SDHCI driver
Mark Rutland
mark.rutland at arm.com
Tue Jul 30 05:08:32 EDT 2013
On Mon, Jul 29, 2013 at 11:49:34AM +0100, Jonas Jensen wrote:
> Add SDHCI driver for MOXA ART SoCs.
>
> Signed-off-by: Jonas Jensen <jonas.jensen at gmail.com>
> ---
>
> Notes:
> Changes since v2:
>
> 1. #include <linux/bitops.h> because BIT() comes from there
> 2. reinsert dev_set_drvdata() in moxart_remove
> 3. remove MODULE_VERSION()
>
> device tree bindings document:
> 4. describe compatible variable "Must be" instead of "Should be".
> 5. change description so it's from the point of view of the device
>
> Applies to next-20130729
>
> .../devicetree/bindings/mmc/moxa,moxart-mmc.txt | 17 +
> drivers/mmc/host/Kconfig | 9 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-moxart.c | 782 +++++++++++++++++++++
> drivers/mmc/host/sdhci-moxart.h | 155 ++++
> 5 files changed, 964 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
> create mode 100644 drivers/mmc/host/sdhci-moxart.c
> create mode 100644 drivers/mmc/host/sdhci-moxart.h
>
> diff --git a/Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt b/Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
> new file mode 100644
> index 0000000..67782ad
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
> @@ -0,0 +1,17 @@
> +MOXA ART SD Host Controller Interface
> +
> +Required properties:
> +
> +- compatible : Must be "moxa,moxart-mmc"
> +- reg : Should contain registers location and length
> +- interrupts : Should contain the interrupt number
> +- clocks : Should contain phandle for the clock feeding the SDHCI controller
> +
> +Example:
> +
> + mmc: mmc at 98e00000 {
> + compatible = "moxa,moxart-mmc";
> + reg = <0x98e00000 0x5C>;
> + interrupts = <5 0>;
> + clocks = <&coreclk>;
> + };
This binding looks sensible, but I'd appreciate someone who understands
MMCs checking that this captures the relevant information, as I don't
know much about MMCs.
Most mmc bindings I see have to describe some gpio pins, pinctrl to mux
the relevant pins on from the SoC, etc. Is this a full description of
the hardware supporting all features?
[...]
> +static int moxart_get_ro(struct mmc_host *mmc)
> +{
> + int ret;
> + struct moxart_host *host = mmc_priv(mmc);
> +
> + (readl(&host->reg->status) & MSD_WRITE_PROT) ? (ret = 1) : (ret = 0);
> + return ret;
This looks a little odd, how about something like:
return !!(readl(&host->reg->status) & SD_WRITE_PROT);
> +}
> +
> +static struct mmc_host_ops moxart_ops = {
> + .request = moxart_request,
> + .set_ios = moxart_set_ios,
> + .get_ro = moxart_get_ro,
> +};
> +
> +static int moxart_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *node = dev->of_node;
> + struct resource res_mmc;
> + struct mmc_host *mmc;
> + struct moxart_host *host = NULL;
> + void __iomem *reg_mmc;
> + dma_cap_mask_t mask;
> + int ret;
> + struct dma_slave_config cfg;
> + unsigned int irq;
> + struct clk *clk;
> + unsigned int dma_chan_rx_req = 1;
> + unsigned int dma_chan_tx_req = 0;
> +
> + mmc = mmc_alloc_host(sizeof(struct moxart_host), dev);
> + if (!mmc) {
> + dev_err(dev, "%s: mmc_alloc_host failed\n", __func__);
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + ret = of_address_to_resource(node, 0, &res_mmc);
> + if (ret) {
> + dev_err(dev, "%s: could not get MMC base resource\n", __func__);
> + goto out;
> + }
> +
> + irq = irq_of_parse_and_map(node, 0);
> +
> + reg_mmc = devm_ioremap_resource(dev, &res_mmc);
> + if (IS_ERR(reg_mmc)) {
> + dev_err(dev, "%s: devm_ioremap_resource res_mmc failed\n",
> + __func__);
> + return PTR_ERR(reg_mmc);
> + }
> +
> + mmc->ops = &moxart_ops;
> +
> + /*
> + * hardware does not support MMC_CAP_SD_HIGHSPEED
> + * CMD6 will timeout and make things not work
> + */
> + mmc->caps = MMC_CAP_4_BIT_DATA;
> +
> + mmc->f_min = 400000;
> + mmc->f_max = 25000000;
> + mmc->ocr_avail = 0xffff00; /* support 2.0v - 3.6v power */
> + mmc->max_segs = 32;
> + mmc->max_blk_size = 512;
> + mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size;
> + mmc->max_seg_size = mmc->max_req_size;
> +
> + host = mmc_priv(mmc);
> + host->mmc = mmc;
> + host->reg = (struct moxart_reg *)reg_mmc;
> + host->reg_phys = res_mmc.start;
> + host->timeout = msecs_to_jiffies(1000);
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_SLAVE, mask);
> +
> + clk = of_clk_get(node, 0);
> + if (IS_ERR(clk)) {
> + dev_err(dev, "%s: of_clk_get failed\n", __func__);
> + return PTR_ERR(clk);
> + }
> + host->sysclk = clk_get_rate(clk);
> +
> + spin_lock_init(&host->lock);
> +
> + /* disable all interrupt */
> + writel(0, &host->reg->interrupt_mask);
> +
> + /* reset chip */
> + writel(MSD_SDC_RST, &host->reg->command);
> +
> + /* wait for reset finished */
> + while (readl(&host->reg->command) & MSD_SDC_RST)
> + udelay(10);
> +
> + host->dma_chan_tx = dma_request_channel(mask, moxart_filter_fn,
> + (void *)&dma_chan_tx_req);
> + host->dma_chan_rx = dma_request_channel(mask, moxart_filter_fn,
> + (void *)&dma_chan_rx_req);
> + dev_dbg(dev, "%s: using 2 DMA channels rx=%p tx=%p\n",
> + __func__, host->dma_chan_rx, host->dma_chan_tx);
> +
> + if (!host->dma_chan_rx || !host->dma_chan_tx) {
> + host->have_dma = false;
> + mmc->max_blk_count = 1;
> + } else {
> + cfg.slave_id = APB_DMA_SD_REQ_NO;
> + cfg.direction = DMA_MEM_TO_DEV;
> + cfg.src_addr = 0;
> + cfg.dst_addr = (unsigned int)host->reg_phys + MSD_DATA_WIN_REG;
> + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> + dmaengine_slave_config(host->dma_chan_tx, &cfg);
> +
> + cfg.slave_id = APB_DMA_SD_REQ_NO;
> + cfg.direction = DMA_DEV_TO_MEM;
> + cfg.src_addr = (unsigned int)host->reg_phys + MSD_DATA_WIN_REG;
> + cfg.dst_addr = 0;
> + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> + dmaengine_slave_config(host->dma_chan_rx, &cfg);
> +
> + host->have_dma = true;
> +
> + /*
> + * there seems to be a max size on transfers so
> + * set max_blk_count low for both DMA and PIO
> + *
> + * sending large chunks result either in timeout
> + * or render the MMC controller unresponsive
> + * (status register 0 on consecutive read retries,
> + * also see comments in moxart_send_command)
> + *
> + * obviously, DMA is quicker and can handle
> + * larger chunks but setting it higher than 16
> + * can still bug the controller
> + */
> + mmc->max_blk_count = 16;
> + }
> +
> + devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host);
> +
> + if (ret)
> + goto out;
Presumably you meant to record the return code of devm_request_irq?
> +
> + dev_set_drvdata(dev, mmc);
> + mmc_add_host(mmc);
> +
> + dev_dbg(dev, "%s: IRQ=%d\n", __func__, irq);
> +
> + return 0;
> +
> +out:
> + if (mmc)
> + mmc_free_host(mmc);
> + return ret;
> +}
[...]
> +struct moxart_reg {
> +
> +#define MSD_SDC_RST BIT(10)
> +#define MSD_CMD_EN BIT(9)
> +#define MSD_APP_CMD BIT(8)
> +#define MSD_LONG_RSP BIT(7)
> +#define MSD_NEED_RSP BIT(6)
> +#define MSD_CMD_IDX_MASK 0x3f
> + unsigned int command;
> +
> + unsigned int argument;
> + unsigned int response0;
> + unsigned int response1;
> + unsigned int response2;
> + unsigned int response3;
> +
> +#define MSD_RSP_CMD_APP BIT(6)
> +#define MSD_RSP_CMD_IDX_MASK 0x3f
> + unsigned int response_command;
> +
> +#define MSD_DATA_EN BIT(6)
> +#define MSD_DMA_EN BIT(5)
> +#define MSD_DATA_WRITE BIT(4)
> +#define MSD_BLK_SIZE_MASK 0x0f
> + unsigned int data_control;
> +
> + unsigned int data_timer;
> +
> +#define MSD_DATA_LEN_MASK 0xffffff
> + unsigned int data_length;
> +
> +#define MSD_WRITE_PROT BIT(12)
> +#define MSD_CARD_DETECT BIT(11)
> +/* 1-10 below can be sent to interrupt or clear register */
> +#define MSD_CARD_CHANGE BIT(10)
> +#define MSD_FIFO_ORUN BIT(9)
> +#define MSD_FIFO_URUN BIT(8)
> +#define MSD_DATA_END BIT(7)
> +#define MSD_CMD_SENT BIT(6)
> +#define MSD_DATA_CRC_OK BIT(5)
> +#define MSD_RSP_CRC_OK BIT(4)
> +#define MSD_DATA_TIMEOUT BIT(3)
> +#define MSD_RSP_TIMEOUT BIT(2)
> +#define MSD_DATA_CRC_FAIL BIT(1)
> +#define MSD_RSP_CRC_FAIL BIT(0)
> + unsigned int status;
> +
> + unsigned int clear;
> + unsigned int interrupt_mask;
> +
> +#define MSD_SD_POWER_ON BIT(4)
> +#define MSD_SD_POWER_MASK 0x0f
> + unsigned int power_control;
> +
> +#define MSD_CLK_DIS BIT(8)
> +#define MSD_CLK_SD BIT(7)
> +#define MSD_CLK_DIV_MASK 0x7f
> + unsigned int clock_control;
> +
> +#define MSD_WIDE_BUS_SUPPORT BIT(3)
> +#define MSD_WIDE_BUS BIT(2) /* bus width is 4 bytes */
> +#define MSD_SINGLE_BUS BIT(0) /* bus width is 1 byte */
> + unsigned int bus_width;
> +
> + unsigned int data_window;
> +
> +#define MSD_CPRM_FUNCTION BIT(8)
> + unsigned int feature;
> +
> + unsigned int revision;
> +};
I see you're using this to handle your register offsets. This isn't
necessarily portable, as you're relying on the size of unsigned int
being 4 bytes, and that elements aren't padded to a larger size. At the
very least unsigned int should be replaced with u32.
Thanks,
Mark.
More information about the linux-arm-kernel
mailing list