RE: [PATCH] mmc: add the MMC/SD/SDIO Arasan host controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> -----Original Message-----
> From: Giuseppe CAVALLARO [mailto:peppe.cavallaro@xxxxxx]
> Sent: Wednesday, July 28, 2010 8:39 AM
> To: linux-mmc@xxxxxxxxxxxxxxx
> Cc: Peppe CAVALLARO
> Subject: [PATCH] mmc: add the MMC/SD/SDIO Arasan host controller
>
> This patch adds the MMC/SD/SDIO Arasan host controller.
> Advanced DMA, Single DMA and PIO modes are supported.
> The former is the default.
> This has been tested on the 7108/06 STM platforms.
>
> Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>

Hello!

Just a few notes, I hope useful for reviewing the driver:
- its configuration depends on ST40 CPU because I have only
  tested it on STM platforms (ST40 based). At any rate, the driver
  builds fine on x86 machine against the latest Kernel from git.
- I've added the linux/mmc/arasan_plat.h for the driver's
  platform information and the drivers/mmc/host/arasan.h
  header file. I've voluntarily preferred to split the two headers
  avoiding to include the header comes from ../../../driver/mmc/host
  within our include/linux/stm/platform.h.
  I don't know if it's the best solution and if you like it.
  I did the same for the stmmac driver in net-2.6, in the past.
- No problems while running the mmc_test.ko.

Welcome advice and feedback.

Best Regards,
Giuseppe

> ---
>  drivers/mmc/host/Kconfig        |    7 +
>  drivers/mmc/host/Makefile       |    1 +
>  drivers/mmc/host/arasan.c       | 1354
> +++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/arasan.h       |  237 +++++++
>  include/linux/mmc/arasan_plat.h |   49 ++
>  5 files changed, 1648 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/mmc/host/arasan.c
>  create mode 100644 drivers/mmc/host/arasan.h
>  create mode 100644 include/linux/mmc/arasan_plat.h
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index f06d06e..f403f1d 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -432,3 +432,10 @@ config MMC_SH_MMCIF
>         This selects the MMC Host Interface controler (MMCIF).
>
>         This driver supports MMCIF in sh7724/sh7757/sh7372.
> +
> +config MMC_ARASAN
> +     tristate "Arasan MMC/SD/SDIO host driver"
> +     depends on CPU_SUBTYPE_ST40
> +     help
> +       This selects the Arasan MMC/SD/SDIO host controller integrated
> +       in the STMicroelectronics platforms (stx7108 and stx7106).
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index e30c2ee..8337f15 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_MMC_CB710)     += cb710-mmc.o
>  obj-$(CONFIG_MMC_VIA_SDMMC)  += via-sdmmc.o
>  obj-$(CONFIG_SDH_BFIN)               += bfin_sdh.o
>  obj-$(CONFIG_MMC_SH_MMCIF)   += sh_mmcif.o
> +obj-$(CONFIG_MMC_ARASAN)     += arasan.o
>
>  obj-$(CONFIG_MMC_SDHCI_OF)   += sdhci-of.o
>  sdhci-of-y                           := sdhci-of-core.o
> diff --git a/drivers/mmc/host/arasan.c b/drivers/mmc/host/arasan.c
> new file mode 100644
> index 0000000..7dc49ae
> --- /dev/null
> +++ b/drivers/mmc/host/arasan.c
> @@ -0,0 +1,1354 @@
> +/*
> + * Arasan MMC/SD/SDIO driver
> + *
> + *  This is the driver for the Arasan MMC/SD/SDIO host controller
> + *  integrated in the STMicroelectronics platforms
> + *
> + * Author: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>
> + * Copyright (C) 2010 STMicroelectronics Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/mbus.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/scatterlist.h>
> +#include <linux/irq.h>
> +#include <linux/highmem.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/arasan_plat.h>
> +
> +#include <asm/sizes.h>
> +#include <asm/unaligned.h>
> +
> +#include "arasan.h"
> +
> +/* To enable more debug information. */
> +#undef ARASAN_DEBUG
> +/*#define ARASAN_DEBUG*/
> +#ifdef ARASAN_DEBUG
> +#define DBG(fmt, args...)  pr_info(fmt, ## args)
> +#else
> +#define DBG(fmt, args...)  do { } while (0)
> +#endif
> +
> +static int maxfreq = ARASAN_CLOCKRATE_MAX;
> +module_param(maxfreq, int, S_IRUGO);
> +MODULE_PARM_DESC(maxfreq, "Maximum card clock frequency (default 50MHz)");
> +
> +static unsigned int adma = 1;
> +module_param(adma, int, S_IRUGO);
> +MODULE_PARM_DESC(adma, "Disable/Enable the Advanced DMA mode");
> +
> +static unsigned int led;
> +module_param(led, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(led, "Enable|Disable LED");
> +
> +static unsigned int pio;
> +module_param(pio, int, S_IRUGO);
> +MODULE_PARM_DESC(pio, "PIO mode (no DMA)");
> +
> +struct arasan_cap {
> +     unsigned int timer_freq;
> +     unsigned int timer_unit;
> +     unsigned int base_clk_sd;
> +     unsigned int max_blk_len;
> +     unsigned int adma2;
> +     unsigned int high_speed;
> +     unsigned int sdma;
> +     unsigned int suspend;
> +     unsigned int voltage33;
> +     unsigned int voltage30;
> +     unsigned int voltage18;
> +     unsigned int int_mode;
> +     unsigned int spi;
> +     unsigned int spi_block;
> +};
> +
> +struct arasan_host {
> +     void __iomem *base;
> +     struct mmc_request *mrq;
> +     unsigned int intr_en;
> +     u8 ctrl;
> +     unsigned int sg_frags;
> +     struct timer_list timer;
> +     struct mmc_host *mmc;
> +     struct device *dev;
> +     struct resource *res;
> +     int irq;
> +     struct arasan_cap cap;
> +     u8 vdd;
> +     unsigned int freq;
> +     unsigned int status;
> +     unsigned int adma;
> +     unsigned int use_pio;
> +     u16 pio_blksz;
> +     u32 pio_blocks;
> +     u32 *pio_blkbuf;
> +     spinlock_t lock;
> +     struct tasklet_struct card_tasklet;
> +     u8 *adma_desc;
> +     dma_addr_t adma_addr;
> +     int need_poll;
> +};
> +
> +static inline void arsan_sw_reset(struct arasan_host *host, unsigned int
> flag)
> +{
> +     /* After completing the reset, wait the HC clears these bits */
> +     if (likely(flag == reset_all)) {
> +             writeb(ARSAN_RESET_ALL, host->base + ARASAN_SW_RESET);
> +             do { } while ((readb(host->base + ARASAN_SW_RESET)) &
> +                      ARSAN_RESET_ALL);
> +     } else if (flag == reset_cmd_line) {
> +             writeb(ARSAN_RESET_CMD_LINE, host->base + ARASAN_SW_RESET);
> +             do { } while ((readb(host->base + ARASAN_SW_RESET)) &
> +                      ARSAN_RESET_CMD_LINE);
> +
> +     } else if (flag == reset_dat_line) {
> +             writeb(ARSAN_RESET_DAT_LINE, host->base + ARASAN_SW_RESET);
> +             do { } while ((readb(host->base + ARASAN_SW_RESET)) &
> +                      ARSAN_RESET_DAT_LINE);
> +     }
> +}
> +
> +static inline void arsan_hc_version(struct arasan_host *host)
> +{
> +     u16 version;
> +
> +     version = readw(host->base + ARASAN_HOST_VERSION);
> +     pr_debug("Arasan MMC/SDIO:\n\tHC Vendor Version Number: %d\n",
> +              (version >> 8));
> +     pr_debug("\tHC SPEC Version Number: %d\n", (version & 0x00ff));
> +}
> +
> +static void arasan_capabilities(struct arasan_host *host)
> +{
> +     unsigned int cap;
> +     unsigned int max_blk_len;
> +
> +     cap = readl(host->base + ARASAN_CAPABILITIES);
> +
> +     pr_debug("\tArasan capabilities: 0x%x\n", cap);
> +
> +     host->cap.timer_freq = cap & 0x3f;
> +     host->cap.timer_unit = (cap >> 7) & 0x1;
> +
> +     pr_debug("\tTimeout Clock Freq: %d %s\n", host->cap.timer_freq,
> +              host->cap.timer_unit ? "MHz" : "KHz");
> +
> +     host->cap.base_clk_sd = (cap >> 8) & 0x3f;
> +     pr_debug("\tBase Clock Freq for SD: %d MHz\n", host-
> >cap.base_clk_sd);
> +
> +     max_blk_len = (cap >> 16) & 0x3;
> +     switch (max_blk_len) {
> +     case 0:
> +             host->cap.max_blk_len = 512;
> +             break;
> +     case 1:
> +             host->cap.max_blk_len = 1024;
> +             break;
> +     case 2:
> +             host->cap.max_blk_len = 2048;
> +             break;
> +     case 3:
> +             host->cap.max_blk_len = 4096;
> +             break;
> +     default:
> +             break;
> +     }
> +     pr_debug("\tMax Block size: %d bytes\n", host->cap.max_blk_len);
> +
> +     host->cap.adma2 = (cap >> 19) & 0x1;
> +     host->cap.high_speed = (cap >> 21) & 0x1;
> +     host->cap.sdma = (cap >> 22) & 0x1;
> +
> +     pr_debug("\tadma2 %s, high speed %s, sdma %s\n",
> +              host->cap.adma2 ? "Yes" : "Not",
> +              host->cap.high_speed ? "Yes" : "Not",
> +              host->cap.sdma ? "Yes" : "Not");
> +
> +     host->cap.suspend = (cap >> 23) & 0x1;
> +     pr_debug("\tsuspend/resume %s suported\n",
> +              host->cap.adma2 ? "is" : "Not");
> +
> +     /* Disable adma user option if cap not supported. */
> +     if (!host->cap.adma2)
> +             adma = 0;
> +
> +     host->cap.voltage33 = (cap >> 24) & 0x1;
> +     host->cap.voltage30 = (cap >> 25) & 0x1;
> +     host->cap.voltage18 = (cap >> 26) & 0x1;
> +     host->cap.int_mode = (cap >> 27) & 0x1;
> +     host->cap.spi = (cap >> 29) & 0x1;
> +     host->cap.spi_block = (cap >> 30) & 0x1;
> +
> +     if (host->cap.voltage33)
> +             pr_debug("\t3.3V voltage suported\n");
> +     if (host->cap.voltage30)
> +             pr_debug("\t3.0V voltage suported\n");
> +     if (host->cap.voltage18)
> +             pr_debug("\t1.8V voltage suported\n");
> +
> +     if (host->cap.int_mode)
> +             pr_debug("\tInterrupt Mode supported\n");
> +     if (host->cap.spi)
> +             pr_debug("\tSPI Mode supported\n");
> +     if (host->cap.spi_block)
> +             pr_debug("\tSPI Block Mode supported\n");
> +}
> +
> +static void arasan_ctrl_led(struct arasan_host *host, unsigned int flag)
> +{
> +     if (led) {
> +             u8 ctrl_reg = readb(host->base + ARASAN_HOST_CTRL);
> +
> +             if (flag)
> +                     ctrl_reg |= ARASAN_HOST_CTRL_LED;
> +             else
> +                     ctrl_reg &= ~ARASAN_HOST_CTRL_LED;
> +
> +             host->ctrl = ctrl_reg;
> +             writeb(host->ctrl, host->base + ARASAN_HOST_CTRL);
> +     }
> +}
> +
> +static inline void arasan_set_interrupts(struct arasan_host *host)
> +{
> +     host->intr_en = ARASAN_IRQ_DEFAULT_MASK;
> +     writel(host->intr_en, host->base + ARASAN_NORMAL_INT_STATUS_EN);
> +     writel(host->intr_en, host->base + ARASAN_NORMAL_INT_SIGN_EN);
> +}
> +
> +static inline void arasan_clear_interrupts(struct arasan_host *host)
> +{
> +     writel(0, host->base + ARASAN_NORMAL_INT_STATUS_EN);
> +     writel(0, host->base + ARASAN_ERR_INT_STATUS_EN);
> +     writel(0, host->base + ARASAN_NORMAL_INT_SIGN_EN);
> +}
> +
> +static void arasan_power_set(struct arasan_host *host, unsigned int pwr,
> u8 vdd)
> +{
> +     u8 pwr_reg;
> +
> +     pwr_reg = readb(host->base + ARASAN_PWR_CTRL);
> +
> +     host->vdd = (1 << vdd);
> +
> +     if (pwr) {
> +             pwr_reg &= 0xf1;
> +
> +             if ((host->vdd & MMC_VDD_165_195) && host->cap.voltage18)
> +                     pwr_reg |= ARASAN_PWR_BUS_VOLTAGE_18;
> +             else if ((host->vdd & MMC_VDD_29_30) && host->cap.voltage30)
> +                     pwr_reg |= ARASAN_PWR_BUS_VOLTAGE_30;
> +             else if ((host->vdd & MMC_VDD_32_33) && host->cap.voltage33)
> +                     pwr_reg |= ARASAN_PWR_BUS_VOLTAGE_33;
> +
> +             pwr_reg |= ARASAN_PWR_CTRL_UP;
> +     } else
> +             pwr_reg &= ~ARASAN_PWR_CTRL_UP;
> +
> +     DBG("%s: pwr_reg 0x%x, host->vdd = 0x%x\n", __func__, pwr_reg,
> +         host->vdd);
> +     writeb(pwr_reg, host->base + ARASAN_PWR_CTRL);
> +}
> +
> +static int arasan_test_card(struct arasan_host *host)
> +{
> +     unsigned int ret = 0;
> +     u32 present = readl(host->base + ARASAN_PRESENT_STATE);
> +     if (likely(!(present & ARASAN_PRESENT_STATE_CARD_PRESENT)))
> +             ret = -1;
> +
> +#ifdef ARASAN_DEBUG
> +     if (present & ARASAN_PRESENT_STATE_CARD_STABLE)
> +             pr_info("\tcard stable...");
> +     if (!(present & ARASAN_PRESENT_STATE_WR_EN))
> +             pr_info("\tcard Write protected...");
> +     if (present & ARASAN_PRESENT_STATE_BUFFER_RD_EN)
> +             pr_info("\tPIO Read Enable...");
> +     if (present & ARASAN_PRESENT_STATE_BUFFER_WR_EN)
> +             pr_info("\tPIO Write Enable...");
> +     if (present & ARASAN_PRESENT_STATE_RD_ACTIVE)
> +             pr_info("\tRead Xfer data...");
> +     if (present & ARASAN_PRESENT_STATE_WR_ACTIVE)
> +             pr_info("\tWrite Xfer data...");
> +     if (present & ARASAN_PRESENT_STATE_DAT_ACTIVE)
> +             pr_info("\tDAT line active...");
> +#endif
> +     return ret;
> +}
> +static void arasan_set_clock(struct arasan_host *host, unsigned int freq)
> +{
> +     u16 clock = 0;
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&host->lock, flags);
> +
> +     if ((host->freq != freq) && (freq)) {
> +             u16 divisor;
> +
> +             /* Ensure clock is off before making any changes */
> +             writew(clock, host->base + ARASAN_CLOCK_CTRL);
> +
> +             /* core checks if this is a good freq < max_freq */
> +             host->freq = freq;
> +
> +             DBG("%s:\n\tnew freq %d", __func__, host->freq);
> +
> +             /* Work out divisor for specified clock frequency */
> +             for (divisor = 1; divisor <= 256; divisor *= 2)
> +                     /* Find first divisor producing a frequency less
> +                      * than or equal to MHz */
> +                     if ((maxfreq / divisor) <= freq)
> +                             break;
> +
> +             DBG("\tdivisor %d", divisor);
> +             /* Set the clock divisor and enable the internal clock */
> +             clock = divisor << (ARASAN_CLOCK_CTRL_SDCLK_SHIFT);
> +             clock &= ARASAN_CLOCK_CTRL_SDCLK_MASK;
> +             clock |= ARASAN_CLOCK_CTRL_ICLK_ENABLE;
> +             writew(clock, host->base + ARASAN_CLOCK_CTRL);
> +
> +             /* Busy wait for the clock to become stable */
> +             do { } while (((readw(host->base + ARASAN_CLOCK_CTRL)) &
> +                       ARASAN_CLOCK_CTRL_ICLK_STABLE) == 0);
> +
> +             /* Enable the SD clock */
> +             clock |= ARASAN_CLOCK_CTRL_SDCLK_ENABLE;
> +             writew(clock, host->base + ARASAN_CLOCK_CTRL);
> +
> +             DBG("\tclk ctrl reg. [0x%x]\n",
> +                 (unsigned int)readw(host->base + ARASAN_CLOCK_CTRL));
> +     }
> +
> +     spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> +/* Read the response from the card */
> +static void arasan_get_resp(struct mmc_command *cmd, struct arasan_host
> *host)
> +{
> +     unsigned int i;
> +     unsigned int resp[4];
> +
> +     for (i = 0; i < 4; i++)
> +             resp[i] = readl(host->base + ARASAN_RSP(i));
> +
> +     if (cmd->flags & MMC_RSP_136) {
> +             cmd->resp[3] = (resp[0] << 8);
> +             cmd->resp[2] = (resp[0] >> 24) | (resp[1] << 8);
> +             cmd->resp[1] = (resp[1] >> 24) | (resp[2] << 8);
> +             cmd->resp[0] = (resp[2] >> 24) | (resp[3] << 8);
> +     } else {
> +             cmd->resp[0] = resp[0];
> +             cmd->resp[1] = resp[1];
> +     }
> +
> +     DBG("%s: resp length %s\n-(CMD%u):\n %08x %08x %08x %08x\n"
> +         "-RAW reg:\n %08x %08x %08x %08x\n",
> +         __func__, (cmd->flags & MMC_RSP_136) ? "136" : "48", cmd->opcode,
> +         cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3],
> +         resp[0], resp[1], resp[2], resp[3]);
> +}
> +
> +static void arasan_read_block_pio(struct arasan_host *host)
> +{
> +     unsigned long flags;
> +     u16 blksz;
> +
> +     DBG("\tPIO reading\n");
> +
> +     local_irq_save(flags);
> +
> +     for (blksz = host->pio_blksz; blksz > 0; blksz -= 4) {
> +             *host->pio_blkbuf =
> +                 readl(host->base + ARASAN_BUFF);
> +             host->pio_blkbuf++;
> +     }
> +
> +     local_irq_restore(flags);
> +}
> +
> +static void arasan_write_block_pio(struct arasan_host *host)
> +{
> +     unsigned long flags;
> +     u16 blksz;
> +
> +     DBG("\tPIO writing\n");
> +     local_irq_save(flags);
> +
> +     for (blksz = host->pio_blksz; blksz > 0; blksz -= 4) {
> +             writel(*host->pio_blkbuf,
> +                    host->base + ARASAN_BUFF);
> +             host->pio_blkbuf++;
> +     }
> +
> +     local_irq_restore(flags);
> +}
> +
> +static void arasan_data_pio(struct arasan_host *host)
> +{
> +     if (host->pio_blocks == 0)
> +             return;
> +
> +     if (host->status == STATE_DATA_READ) {
> +             while (readl(host->base + ARASAN_PRESENT_STATE) &
> +                          ARASAN_PRESENT_STATE_BUFFER_RD_EN) {
> +
> +                     arasan_read_block_pio(host);
> +
> +                     host->pio_blocks--;
> +                     if (host->pio_blocks == 0)
> +                             break;
> +             }
> +
> +     } else {
> +             while (readl(host->base + ARASAN_PRESENT_STATE) &
> +                          ARASAN_PRESENT_STATE_BUFFER_WR_EN) {
> +
> +                     arasan_write_block_pio(host);
> +
> +                     host->pio_blocks--;
> +                     if (host->pio_blocks == 0)
> +                             break;
> +             }
> +     }
> +     DBG("\tPIO transfer complete.\n");
> +}
> +
> +static void arasan_start_cmd(struct arasan_host *host, struct mmc_command
> *cmd)
> +{
> +     u16 cmdreg = 0;
> +
> +     /* Command Request */
> +     cmdreg = ARASAN_CMD_INDEX(cmd->opcode);
> +     DBG("%s: cmd type %04x,  CMD%d\n", __func__,
> +         mmc_resp_type(cmd), cmd->opcode);
> +
> +     if (cmd->flags & MMC_RSP_BUSY) {
> +             cmdreg |= ARASAN_CMD_RSP_48BUSY;
> +             DBG("\tResponse length 48 check Busy.\n");
> +     } else if (cmd->flags & MMC_RSP_136) {
> +             cmdreg |= ARASAN_CMD_RSP_136;
> +             DBG("\tResponse length 136\n");
> +     } else if (cmd->flags & MMC_RSP_PRESENT) {
> +             cmdreg |= ARASAN_CMD_RSP_48;
> +             DBG("\tResponse length 48\n");
> +     } else {
> +             cmdreg |= ARASAN_CMD_RSP_NONE;
> +             DBG("\tNo Response\n");
> +     }
> +
> +     if (cmd->flags & MMC_RSP_CRC) {
> +             cmdreg |= ARASAN_CMD_CHECK_CMDCRC;
> +             DBG("\tCheck the CRC field in the response\n");
> +     }
> +     if (cmd->flags & MMC_RSP_OPCODE) {
> +             cmdreg |= ARASAN_CMD_INDX_CHECK;
> +             DBG("\tCheck the Index field in the response\n");
> +     }
> +
> +     /* Wait until the CMD line is not in use */
> +     do { } while ((readl(host->base + ARASAN_PRESENT_STATE)) &
> +              ARASAN_PRESENT_STATE_CMD_INHIBIT);
> +
> +     /* Set the argument register */
> +     writel(cmd->arg, host->base + ARASAN_ARG);
> +
> +     /* Data present and must be transferred */
> +     if (likely(host->mrq->data)) {
> +             cmdreg |= ARASAN_CMD_DATA_PRESENT;
> +             if (cmd->flags & MMC_RSP_BUSY)
> +                     /* Wait for data inhibit */
> +                     do { } while ((readl(host->base +
> +                                     ARASAN_PRESENT_STATE)) &
> +                              ARASAN_PRESENT_STATE_DAT_INHIBIT);
> +     }
> +
> +     /* Write the Command */
> +     writew(cmdreg, host->base + ARASAN_CMD);
> +
> +     DBG("\tcmd: 0x%x cmd reg: 0x%x - cmd->arg 0x%x, reg 0x%x\n",
> +         cmdreg, readw(host->base + ARASAN_CMD), cmd->arg,
> +         readl(host->base + ARASAN_ARG));
> +}
> +
> +#ifdef ARASAN_DEBUG
> +static void arasan_adma_error(struct arasan_host *host)
> +{
> +     u8 status = readb(host->base + ARASAN_ADMA_ERR_STATUS);
> +
> +     if (status & ARASAN_ADMA_ERROR_LENGTH)
> +             pr_err("-ADMA Length Mismatch Error...");
> +
> +     if (status & ARASAN_ADMA_ERROR_ST_TFR)
> +             pr_err("-Transfer Data Error desc: ");
> +     else if (status & ARASAN_ADMA_ERROR_ST_FDS)
> +             pr_err("-Fetch Data Error desc: ");
> +     else if (status & ARASAN_ADMA_ERROR_ST_STOP)
> +             pr_err("-Stop DMA Data Error desc: ");
> +
> +     pr_err("0x%x", readl(host->base + ARASAN_ADMA_ADDRESS));
> +}
> +
> +static void arasan_adma_dump_desc(u8 *desc)
> +{
> +     __le32 *dma;
> +     __le16 *len;
> +     u8 attr;
> +
> +     pr_info("\tDescriptors:");
> +
> +     while (1) {
> +             dma = (__le32 *) (desc + 4);
> +             len = (__le16 *) (desc + 2);
> +             attr = *desc;
> +
> +             pr_info("\t\t%p: Buff 0x%08x, len %d, Attr 0x%02x\n",
> +                     desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr);
> +
> +             desc += 8;
> +
> +             if (attr & 2)   /* END of descriptor */
> +                     break;
> +     }
> +}
> +#else
> +static void arasan_adma_error(struct arasan_host *host)
> +{
> +}
> +
> +static void arasan_adma_dump_desc(u8 *desc)
> +{
> +}
> +#endif
> +
> +static int arasan_init_sg(struct arasan_host *host)
> +{
> +
> +     host->adma_desc = kmalloc((ARASAN_DMA_DESC_NUM * 2 + 1) * 4,
> +                               GFP_KERNEL);
> +
> +     if (unlikely(host->adma_desc == NULL))
> +             return -ENOMEM;
> +
> +     return 0;
> +}
> +
> +static void arasan_adma_table_pre(struct arasan_host *host,
> +                               struct mmc_data *data)
> +{
> +     int direction, i;
> +     u8 *desc;
> +     struct scatterlist *sg;
> +     int len;
> +     dma_addr_t addr;
> +
> +     if (host->status == STATE_DATA_READ)
> +             direction = DMA_FROM_DEVICE;
> +     else
> +             direction = DMA_TO_DEVICE;
> +
> +     DBG("\t%s: sg entries %d\n", __func__, data->sg_len);
> +
> +     host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg,
> +                                 data->sg_len, direction);
> +     desc = host->adma_desc;
> +
> +     for_each_sg(data->sg, sg, host->sg_frags, i) {
> +             addr = sg_dma_address(sg);
> +             len = sg_dma_len(sg);
> +
> +             DBG("\t\tFrag %d: addr 0x%x, len %d\n", i, addr, len);
> +
> +             /* Preparing the descriptor */
> +             desc[7] = (addr >> 24) & 0xff;
> +             desc[6] = (addr >> 16) & 0xff;
> +             desc[5] = (addr >> 8) & 0xff;
> +             desc[4] = (addr >> 0) & 0xff;
> +
> +             desc[3] = (len >> 8) & 0xff;
> +             desc[2] = (len >> 0) & 0xff;
> +
> +             desc[1] = 0x00;
> +             desc[0] = 0x21;
> +
> +             desc += 8;
> +     }
> +     desc -= 8;
> +     desc[0] = 0x23;
> +
> +     arasan_adma_dump_desc(host->adma_desc);
> +
> +     host->adma_addr = dma_map_single(mmc_dev(host->mmc),
> +                                      host->adma_desc,
> +                                      (ARASAN_DMA_DESC_NUM * 2 + 1) * 4,
> +                                      DMA_TO_DEVICE);
> +
> +     writel(host->adma_addr, host->base + ARASAN_ADMA_ADDRESS);
> +}
> +
> +static void arasan_adma_table_post(struct arasan_host *host,
> +                                struct mmc_data *data)
> +{
> +     int direction;
> +
> +     if (host->status == STATE_DATA_READ)
> +             direction = DMA_FROM_DEVICE;
> +     else
> +             direction = DMA_TO_DEVICE;
> +
> +     DBG("\t%s\n", __func__);
> +
> +     dma_unmap_single(mmc_dev(host->mmc), host->adma_addr,
> +                      (ARASAN_DMA_DESC_NUM * 2 + 1) * 4, DMA_TO_DEVICE);
> +
> +     dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction);
> +}
> +
> +static int arasan_setup_data(struct arasan_host *host)
> +{
> +     u16 blksz;
> +     u16 xfer = 0;
> +     struct mmc_data *data = host->mrq->data;
> +
> +     DBG("%s:\n\t%s mode, data dir: %s; Buff=0x%08x,"
> +         "blocks=%d, blksz=%d\n", __func__, host->use_pio ? "PIO" : "DMA",
> +         (data->flags & MMC_DATA_READ) ? "read" : "write",
> +         (unsigned int)sg_virt(data->sg), data->blocks, data->blksz);
> +
> +     /* Transfer Direction */
> +     if (data->flags & MMC_DATA_READ) {
> +             xfer |= ARASAN_XFER_DATA_DIR;
> +             host->status = STATE_DATA_READ;
> +     } else {
> +             xfer &= ~ARASAN_XFER_DATA_DIR;
> +             host->status = STATE_DATA_WRITE;
> +     }
> +
> +     xfer |= ARASAN_XFER_BLK_COUNT_EN;
> +
> +     if (data->blocks > 1)
> +             xfer |= ARASAN_XFER_MULTI_BLK | ARASAN_XFER_AUTOCMD12;
> +
> +     /* Set the block size register */
> +     blksz = ARASAN_BLOCK_SIZE_SDMA_512KB;
> +     blksz |= (data->blksz & ARASAN_BLOCK_SIZE_TRANSFER);
> +     blksz |= (data->blksz & 0x1000) ? ARASAN_BLOCK_SIZE_SDMA_8KB : 0;
> +
> +     writew(blksz, host->base + ARASAN_BLK_SIZE);
> +
> +     /* Set the block count register */
> +     writew(data->blocks, host->base + ARASAN_BLK_COUNT);
> +
> +     /* PIO mode is used when 'pio' var is set by the user or no
> +      * sdma is available from HC caps. */
> +     if (unlikely(host->use_pio || (host->cap.sdma == 0))) {
> +             host->pio_blksz = data->blksz;
> +             host->pio_blocks = data->blocks;
> +             host->pio_blkbuf = sg_virt(data->sg);
> +     } else {
> +             dma_addr_t phys_addr;
> +
> +             /* Enable DMA */
> +             xfer |= ARASAN_XFER_DMA_EN;
> +
> +             /* Scatter list init */
> +             host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg,
> +                                         data->sg_len,
> +                                         (host->status & STATE_DATA_READ) ?
> +                                         DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +
> +             phys_addr = sg_dma_address(data->sg);
> +
> +             if (likely(host->adma)) {
> +                     /* Set the host control register dma bits for adma
> +                      * if supported and enabled by user. */
> +                     host->ctrl |= ARASAN_HOST_CTRL_ADMA2_32;
> +
> +                     /* Prepare ADMA table */
> +                     arasan_adma_table_pre(host, data);
> +             } else {
> +                     /* SDMA Mode selected (default mode) */
> +                     host->ctrl &= ~ARASAN_HOST_CTRL_ADMA2_64;
> +
> +                     writel((unsigned int)phys_addr,
> +                            host->base + ARASAN_SDMA_SYS_ADDR);
> +             }
> +             writeb(host->ctrl, host->base + ARASAN_HOST_CTRL);
> +
> +     }
> +     /* Set the data transfer mode register */
> +     writew(xfer, host->base + ARASAN_XFER_MODE);
> +
> +     DBG("\tHC Reg [xfer 0x%x] [blksz 0x%x] [blkcount 0x%x] [CRTL
> 0x%x]\n",
> +         readw(host->base + ARASAN_XFER_MODE),
> +         readw(host->base + ARASAN_BLK_SIZE),
> +         readw(host->base + ARASAN_BLK_COUNT),
> +         readb(host->base + ARASAN_HOST_CTRL));
> +
> +     return 0;
> +}
> +
> +static void arasan_finish_data(struct arasan_host *host)
> +{
> +     struct mmc_data *data = host->mrq->data;
> +
> +     DBG("\t%s\n", __func__);
> +
> +     if (unlikely(host->pio_blkbuf)) {
> +             host->pio_blksz = 0;
> +             host->pio_blocks = 0;
> +             host->pio_blkbuf = NULL;
> +     } else {
> +             if (likely(host->adma)) {
> +                     arasan_adma_table_post(host, data);
> +             } else {
> +                     dma_unmap_sg(mmc_dev(host->mmc), data->sg,
> +                                  host->sg_frags,
> +                                  (host->status & STATE_DATA_READ) ?
> +                                  DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +             }
> +     }
> +
> +     data->bytes_xfered = data->blocks * data->blksz;
> +     host->status = STATE_CMD;
> +}
> +
> +static int arasan_finish_cmd(unsigned int err_status, unsigned int status,
> +                          unsigned int opcode)
> +{
> +     int ret = 0;
> +
> +     if (unlikely(err_status)) {
> +             if (err_status & ARASAN_CMD_TIMEOUT) {
> +                     DBG("\tcmd_timeout...\n");
> +                     ret = -ETIMEDOUT;
> +             }
> +             if (err_status & ARASAN_CMD_CRC_ERROR) {
> +                     DBG("\tcmd_crc_error...\n");
> +                     ret = -EILSEQ;
> +             }
> +             if (err_status & ARASAN_CMD_END_BIT_ERROR) {
> +                     DBG("\tcmd_end_bit_error...\n");
> +                     ret = -EILSEQ;
> +             }
> +             if (err_status & ARASAN_CMD_INDEX_ERROR) {
> +                     DBG("\tcmd_index_error...\n");
> +                     ret = -EILSEQ;
> +             }
> +     }
> +     if (likely(status & ARASAN_N_CMD_COMPLETE))
> +             DBG("\tCommand (CMD%u) Completed irq...\n", opcode);
> +
> +     return ret;
> +}
> +
> +/* Enable/Disable Normal and Error interrupts */
> +static void aranan_enable_sdio_irq(struct mmc_host *mmc, int enable)
> +{
> +     unsigned long flags;
> +     struct arasan_host *host = mmc_priv(mmc);
> +
> +     DBG("%s: %s CARD_IRQ\n", __func__, enable ? "enable" : "disable");
> +
> +     spin_lock_irqsave(&host->lock, flags);
> +     if (enable)
> +             host->intr_en = ARASAN_IRQ_DEFAULT_MASK;
> +     else
> +             host->intr_en = 0;
> +
> +     writel(host->intr_en, host->base + ARASAN_NORMAL_INT_STATUS_EN);
> +     spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> +static void arasan_timeout_timer(unsigned long data)
> +{
> +     struct arasan_host *host = (struct arasan_host *)data;
> +     struct mmc_request *mrq;
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&host->lock, flags);
> +
> +     if ((host->mrq) && (host->status == STATE_CMD)) {
> +             mrq = host->mrq;
> +
> +             pr_debug("%s: Timeout waiting for hardware interrupt.\n",
> +                      mmc_hostname(host->mmc));
> +
> +             writel(0xffffffff, host->base + ARASAN_NORMAL_INT_STATUS);
> +             aranan_enable_sdio_irq(host->mmc, 1);
> +
> +             if (mrq->data) {
> +                     arasan_finish_data(host);
> +                     arsan_sw_reset(host, reset_dat_line);
> +                     mrq->data->error = -ETIMEDOUT;
> +             }
> +             if (likely(mrq->cmd)) {
> +                     mrq->cmd->error = -ETIMEDOUT;
> +                     arsan_sw_reset(host, reset_cmd_line);
> +                     arasan_get_resp(mrq->cmd, host);
> +             }
> +             arasan_ctrl_led(host, 0);
> +             host->mrq = NULL;
> +             mmc_request_done(host->mmc, mrq);
> +     }
> +     spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> +/* Process requests from the MMC layer */
> +static void arasan_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> +     struct arasan_host *host = mmc_priv(mmc);
> +     struct mmc_command *cmd = mrq->cmd;
> +     unsigned long flags;
> +
> +     BUG_ON(host->mrq != NULL);
> +
> +     spin_lock_irqsave(&host->lock, flags);
> +
> +     DBG(">>> araran_request:\n");
> +     /* Check that there is a card in the slot */
> +     if (unlikely(arasan_test_card(host) < 0)) {
> +             DBG("%s: Error: No card present...\n", mmc_hostname(host-
> >mmc));
> +
> +             mrq->cmd->error = -ENOMEDIUM;
> +             mmc_request_done(mmc, mrq);
> +             spin_unlock_irqrestore(&host->lock, flags);
> +             return;
> +     }
> +
> +     host->mrq = mrq;
> +
> +     host->status = STATE_CMD;
> +     if (likely(mrq->data))
> +             arasan_setup_data(host);
> +
> +     /* Turn-on/off the LED when send/complete a cmd */
> +     arasan_ctrl_led(host, 1);
> +
> +     arasan_start_cmd(host, cmd);
> +
> +     mod_timer(&host->timer, jiffies + 5 * HZ);
> +
> +     DBG("<<< araran_request done!\n");
> +     spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> +static int arasan_get_ro(struct mmc_host *mmc)
> +{
> +     struct arasan_host *host = mmc_priv(mmc);
> +
> +     u32 ro = readl(host->base + ARASAN_PRESENT_STATE);
> +     if (!(ro & ARASAN_PRESENT_STATE_WR_EN))
> +             return 1;
> +
> +     return 0;
> +}
> +
> +/* I/O bus settings (MMC clock/power ...) */
> +static void arasan_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> +     struct arasan_host *host = mmc_priv(mmc);
> +     u8 ctrl_reg = readb(host->base + ARASAN_HOST_CTRL);
> +
> +     DBG("%s: pwr %d, clk %d, vdd %d, bus_width %d, timing %d\n",
> +         __func__, ios->power_mode, ios->clock, ios->vdd, ios->bus_width,
> +         ios->timing);
> +
> +     /* Set the power supply mode */
> +     if (ios->power_mode == MMC_POWER_OFF)
> +             arasan_power_set(host, 0, ios->vdd);
> +     else
> +             arasan_power_set(host, 1, ios->vdd);
> +
> +     /* Timing (high speed supported?) */
> +     if ((ios->timing == MMC_TIMING_MMC_HS ||
> +          ios->timing == MMC_TIMING_SD_HS) && host->cap.high_speed)
> +             ctrl_reg |= ARASAN_HOST_CTRL_HIGH_SPEED;
> +
> +     /* Clear the current bus width configuration */
> +     ctrl_reg &= ~ARASAN_HOST_CTRL_SD_MASK;
> +
> +     /* Set SD bus bit mode */
> +     switch (ios->bus_width) {
> +     case MMC_BUS_WIDTH_8:
> +             ctrl_reg |= ARASAN_HOST_CTRL_SD8;
> +             break;
> +     case MMC_BUS_WIDTH_4:
> +             ctrl_reg |= ARASAN_HOST_CTRL_SD;
> +             break;
> +     }
> +
> +     /* Default to maximum timeout */
> +     writeb(0x0e, host->base + ARASAN_TIMEOUT_CTRL);
> +
> +     /* Disable Card Interrupt in Host in case we change
> +      * the Bus Width. */
> +     aranan_enable_sdio_irq(host->mmc, 0);
> +
> +     host->ctrl = ctrl_reg;
> +     writeb(host->ctrl, host->base + ARASAN_HOST_CTRL);
> +
> +     aranan_enable_sdio_irq(host->mmc, 1);
> +
> +     /* Set clock */
> +     arasan_set_clock(host, ios->clock);
> +}
> +
> +/* Tasklet for Card-detection */
> +static void arasan_tasklet_card(unsigned long data)
> +{
> +     unsigned long flags;
> +     struct arasan_host *host = (struct arasan_host *)data;
> +
> +     spin_lock_irqsave(&host->lock, flags);
> +
> +     if (likely((readl(host->base + ARASAN_PRESENT_STATE) &
> +                 ARASAN_PRESENT_STATE_CARD_PRESENT))) {
> +             if (host->mrq) {
> +                     pr_err("%s: Card removed during transfer!\n",
> +                            mmc_hostname(host->mmc));
> +                     /* Reset cmd and dat lines */
> +                     arsan_sw_reset(host, reset_cmd_line);
> +                     arsan_sw_reset(host, reset_dat_line);
> +
> +                     if (likely(host->mrq->cmd)) {
> +                             host->mrq->cmd->error = -ENOMEDIUM;
> +                             mmc_request_done(host->mmc, host->mrq);
> +                     }
> +             }
> +     }
> +
> +     spin_unlock_irqrestore(&host->lock, flags);
> +
> +     if (likely(host->mmc))
> +             mmc_detect_change(host->mmc, msecs_to_jiffies(200));
> +}
> +
> +static irqreturn_t arasan_irq(int irq, void *dev)
> +{
> +     struct arasan_host *host = dev;
> +     unsigned int status, err_status, handled = 0;
> +     struct mmc_command *cmd = NULL;
> +     struct mmc_data *data = NULL;
> +
> +     spin_lock(&host->lock);
> +
> +     /* Interrupt Status */
> +     status = readl(host->base + ARASAN_NORMAL_INT_STATUS);
> +     err_status = (status >> 16) & 0xffff;
> +
> +     DBG("%s: Normal IRQ status  0x%x, Error status 0x%x\n",
> +         __func__, status & 0xffff, err_status);
> +
> +     if ((!host->need_poll) &&
> +         ((status & ARASAN_N_CARD_REMOVAL) ||
> +                 (status & ARASAN_N_CARD_INS)))
> +                     tasklet_schedule(&host->card_tasklet);
> +
> +     if (unlikely(!host->mrq))
> +             goto out;
> +
> +     cmd = host->mrq->cmd;
> +     data = host->mrq->data;
> +
> +     cmd->error = 0;
> +     /* Check for any CMD interrupts */
> +     if (likely(status & ARASAN_INT_CMD_MASK)) {
> +
> +             cmd->error = arasan_finish_cmd(err_status, status, cmd-
> >opcode);
> +             if (cmd->error)
> +                     arsan_sw_reset(host, reset_cmd_line);
> +
> +             if ((host->status == STATE_CMD) || cmd->error) {
> +                     arasan_get_resp(cmd, host);
> +
> +                     handled = 1;
> +             }
> +     }
> +
> +     /* Check for any data interrupts */
> +     if (likely((status & ARASAN_INT_DATA_MASK)) && data) {
> +             data->error = 0;
> +             if (unlikely(err_status)) {
> +                     if (err_status & ARASAN_DATA_TIMEOUT_ERROR) {
> +                             DBG("\tdata_timeout_error...\n");
> +                             data->error = -ETIMEDOUT;
> +                     }
> +                     if (err_status & ARASAN_DATA_CRC_ERROR) {
> +                             DBG("\tdata_crc_error...\n");
> +                             data->error = -EILSEQ;
> +                     }
> +                     if (err_status & ARASAN_DATA_END_ERROR) {
> +                             DBG("\tdata_end_error...\n");
> +                             data->error = -EILSEQ;
> +                     }
> +                     if (err_status & ARASAN_AUTO_CMD12_ERROR) {
> +                             unsigned int err_cmd12 =
> +                                 readw(host->base + ARASAN_CMD12_ERR_STATUS);
> +
> +                             DBG("\tc12err 0x%04x\n", err_cmd12);
> +
> +                             if (err_cmd12 & ARASAN_AUTOCMD12_ERR_NOTEXE)
> +                                     data->stop->error = -ENOEXEC;
> +
> +                             if ((err_cmd12 & ARASAN_AUTOCMD12_ERR_TIMEOUT)
> +                                 && !(err_cmd12 & ARASAN_AUTOCMD12_ERR_CRC))
> +                                     /* Timeout Error */
> +                                     data->stop->error = -ETIMEDOUT;
> +                             else if (!(err_cmd12 &
> +                                        ARASAN_AUTOCMD12_ERR_TIMEOUT)
> +                                      && (err_cmd12 &
> +                                          ARASAN_AUTOCMD12_ERR_CRC))
> +                                     /* CRC Error */
> +                                     data->stop->error = -EILSEQ;
> +                             else if ((err_cmd12 &
> +                                       ARASAN_AUTOCMD12_ERR_TIMEOUT)
> +                                      && (err_cmd12 &
> +                                          ARASAN_AUTOCMD12_ERR_CRC))
> +                                     DBG("\tCMD line Conflict\n");
> +                     }
> +                     arsan_sw_reset(host, reset_dat_line);
> +                     handled = 1;
> +             } else {
> +                     if (likely(((status & ARASAN_N_BUFF_READ) ||
> +                                 status & ARASAN_N_BUFF_WRITE))) {
> +                             DBG("\tData R/W interrupts...\n");
> +                             arasan_data_pio(host);
> +                     }
> +
> +                     if (likely(status & ARASAN_N_DMA_IRQ))
> +                             DBG("\tDMA interrupts...\n");
> +
> +                     if (likely(status & ARASAN_N_TRANS_COMPLETE)) {
> +                             DBG("\tData XFER completed interrupts...\n");
> +                             arasan_finish_data(host);
> +                             if (data->stop) {
> +                                     u32 opcode = data->stop->opcode;
> +                                     data->stop->error =
> +                                         arasan_finish_cmd(err_status,
> +                                                           status, opcode);
> +                                     arasan_get_resp(data->stop, host);
> +                             }
> +                             handled = 1;
> +                     }
> +             }
> +     }
> +     if (err_status & ARASAN_ADMA_ERROR) {
> +             DBG("\tADMA Error...\n");
> +             arasan_adma_error(host);
> +             cmd->error = -EIO;
> +     }
> +     if (err_status & ARASAN_CURRENT_LIMIT_ERROR) {
> +             DBG("\tPower Fail...\n");
> +             cmd->error = -EIO;
> +     }
> +
> +     if (likely(host->mrq && handled)) {
> +             struct mmc_request *mrq = host->mrq;
> +
> +             arasan_ctrl_led(host, 0);
> +
> +             del_timer(&host->timer);
> +
> +             host->mrq = NULL;
> +             DBG("\tcalling mmc_request_done...\n");
> +             mmc_request_done(host->mmc, mrq);
> +     }
> +out:
> +     DBG("\tclear status and exit...\n");
> +     writel(status, host->base + ARASAN_NORMAL_INT_STATUS);
> +
> +     spin_unlock(&host->lock);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static void arasan_setup_hc(struct arasan_host *host)
> +{
> +     /* Clear all the interrupts before resetting */
> +     arasan_clear_interrupts(host);
> +
> +     /* Reset All and get the HC version */
> +     arsan_sw_reset(host, reset_all);
> +
> +     /* Print HC version and SPEC */
> +     arsan_hc_version(host);
> +
> +     /* Set capabilities and print theri info */
> +     arasan_capabilities(host);
> +
> +     /* Enable interrupts */
> +     arasan_set_interrupts(host);
> +}
> +
> +static const struct mmc_host_ops arasan_ops = {
> +     .request = arasan_request,
> +     .get_ro = arasan_get_ro,
> +     .set_ios = arasan_set_ios,
> +     .enable_sdio_irq = aranan_enable_sdio_irq,
> +};
> +
> +static int __init arasan_probe(struct platform_device *pdev)
> +{
> +     struct mmc_host *mmc = NULL;
> +     struct arasan_host *host = NULL;
> +     const struct arasan_platform_data *arasan_data;
> +     struct resource *r;
> +     int ret, irq;
> +
> +     r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +     irq = platform_get_irq_byname(pdev, "mmcirq");
> +
> +     arasan_data = pdev->dev.platform_data;
> +
> +     if (!r || irq < 0 || !arasan_data)
> +             return -ENXIO;
> +
> +     r = request_mem_region(r->start, resource_size(r), pdev->name);
> +     if (!r) {
> +             pr_err("%s: ERROR: memory allocation failed\n", __func__);
> +             return -EBUSY;
> +             goto out;
> +     }
> +     /* Allocate the mmc_host with private data size */
> +     mmc = mmc_alloc_host(sizeof(struct arasan_host), &pdev->dev);
> +     if (!mmc) {
> +             pr_err("%s: ERROR: mmc_alloc_host failed\n", __func__);
> +             ret = -ENOMEM;
> +             goto out;
> +     }
> +
> +     /* Verify resource from the platform */
> +     ret = arasan_claim_resource(pdev);
> +     if (ret < 0)
> +             goto out;
> +
> +     host = mmc_priv(mmc);
> +     host->mmc = mmc;
> +     host->dev = &pdev->dev;
> +     host->res = r;
> +
> +     host->need_poll = arasan_data->need_poll;
> +     if (host->need_poll) {
> +             mmc->caps |= MMC_CAP_NEEDS_POLL;
> +             DBG("\tHC needs polling to detect the card...");
> +     } else
> +             /* no set the MMC_CAP_NEEDS_POLL in cap */
> +             tasklet_init(&host->card_tasklet, arasan_tasklet_card,
> +                          (unsigned long)host);
> +
> +     host->base = ioremap(r->start, resource_size(r));
> +     if (!host->base) {
> +             pr_err("%s: ERROR: memory mapping failed\n", __func__);
> +             ret = -ENOMEM;
> +             goto out;
> +     }
> +
> +     ret =
> +         request_irq(irq, arasan_irq, IRQF_SHARED, ARASAN_DRIVER_NAME,
> host);
> +     if (ret) {
> +             pr_err("%s: cannot assign irq %d\n", __func__, irq);
> +             goto out;
> +     } else
> +             host->irq = irq;
> +
> +     spin_lock_init(&host->lock);
> +
> +     /* Setup the Host Controller according to its capabilities */
> +     arasan_setup_hc(host);
> +
> +     mmc->ops = &arasan_ops;
> +
> +     if (host->cap.voltage33)
> +             mmc->ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
> +     if (host->cap.voltage30)
> +             mmc->ocr_avail |= MMC_VDD_29_30;
> +     if (host->cap.voltage18)
> +             mmc->ocr_avail |= MMC_VDD_165_195;
> +
> +     mmc->caps = MMC_CAP_SDIO_IRQ;
> +     mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
> +
> +     if (host->cap.high_speed)
> +             mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
> +
> +     host->freq = host->cap.timer_freq * 1000000;
> +     host->use_pio = pio;
> +     mmc->f_max = maxfreq;
> +     mmc->f_min = mmc->f_max / 256;
> +
> +     /*
> +      * Maximum block size. This is specified in the capabilities
> register.
> +      */
> +     mmc->max_blk_size = host->cap.max_blk_len;
> +     mmc->max_blk_count = 65535;
> +
> +     mmc->max_hw_segs = 1;
> +     mmc->max_phys_segs = 128;
> +     mmc->max_seg_size = 65535;
> +     mmc->max_req_size = 524288;
> +
> +     /* Passing the "pio" option, we force the driver to not
> +      * use any DMA engines. */
> +     if (unlikely(host->use_pio)) {
> +             adma = 0;
> +             pr_debug("\tPIO mode\n");
> +     } else {
> +             if (likely(adma)) {
> +                     /* Turn-on the ADMA if supported by the HW
> +                      * or Fall back to SDMA in case of failures */
> +                     pr_debug("\tADMA mode\n");
> +                     ret = arasan_init_sg(host);
> +                     if (unlikely(ret)) {
> +                             pr_warning("\tSG init failed (disable ADMA)\n");
> +                             adma = 0;
> +                     } else
> +                             /* Set the Maximum number of segments
> +                              * becasue we can do scatter/gathering in ADMA
> +                              * mode. */
> +                             mmc->max_hw_segs = 128;
> +             } else
> +                     pr_debug("\tSDMA mode\n");
> +     }
> +     host->adma = adma;
> +
> +     platform_set_drvdata(pdev, mmc);
> +     ret = mmc_add_host(mmc);
> +     if (ret)
> +             goto out;
> +
> +     setup_timer(&host->timer, arasan_timeout_timer, (unsigned long)host);
> +
> +     pr_info("%s: driver initialized... IRQ: %d, Base addr 0x%x\n",
> +             mmc_hostname(mmc), irq, (unsigned int)host->base);
> +
> +#ifdef ARASAN_DEBUG
> +     led = 1;
> +#endif
> +     return 0;
> +out:
> +     if (host) {
> +             if (host->irq)
> +                     free_irq(host->irq, host);
> +             if (host->base)
> +                     iounmap(host->base);
> +     }
> +     if (r)
> +             release_resource(r);
> +     if (mmc)
> +             mmc_free_host(mmc);
> +
> +     return ret;
> +}
> +
> +static int __exit arasan_remove(struct platform_device *pdev)
> +{
> +     struct mmc_host *mmc = platform_get_drvdata(pdev);
> +
> +     if (mmc) {
> +             struct arasan_host *host = mmc_priv(mmc);
> +
> +             arasan_clear_interrupts(host);
> +             if (!host->need_poll)
> +                     tasklet_kill(&host->card_tasklet);
> +             mmc_remove_host(mmc);
> +             free_irq(host->irq, host);
> +             arasan_power_set(host, 0, -1);
> +             iounmap(host->base);
> +             if (likely(host->adma))
> +                     kfree(host->adma_desc);
> +             release_resource(host->res);
> +             mmc_free_host(mmc);
> +     }
> +     platform_set_drvdata(pdev, NULL);
> +     return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int arasan_suspend(struct platform_device *dev, pm_message_t state)
> +{
> +     struct mmc_host *mmc = platform_get_drvdata(dev);
> +     struct arasan_host *host = mmc_priv(mmc);
> +     int ret = 0;
> +
> +     if (mmc && host->cap.suspend)
> +             ret = mmc_suspend_host(mmc);
> +
> +     return ret;
> +}
> +
> +static int arasan_resume(struct platform_device *dev)
> +{
> +     struct mmc_host *mmc = platform_get_drvdata(dev);
> +     struct arasan_host *host = mmc_priv(mmc);
> +     int ret = 0;
> +
> +     if (mmc && host->cap.suspend)
> +             ret = mmc_resume_host(mmc);
> +
> +     return ret;
> +}
> +#endif
> +
> +static struct platform_driver arasan_driver = {
> +     .remove = __exit_p(arasan_remove),
> +#ifdef CONFIG_PM
> +     .suspend = arasan_suspend,
> +     .resume = arasan_resume,
> +#endif
> +     .driver = {
> +                .name = ARASAN_DRIVER_NAME,
> +                },
> +};
> +
> +static int __init arasan_init(void)
> +{
> +     return platform_driver_probe(&arasan_driver, arasan_probe);
> +}
> +
> +static void __exit arasan_exit(void)
> +{
> +     platform_driver_unregister(&arasan_driver);
> +}
> +
> +#ifndef MODULE
> +static int __init arasan_cmdline_opt(char *str)
> +{
> +     char *opt;
> +
> +     if (!str || !*str)
> +             return -EINVAL;
> +
> +     while ((opt = strsep(&str, ",")) != NULL) {
> +             if (!strncmp(opt, "maxfreq:", 8))
> +                     strict_strtoul(opt + 8, 0, (unsigned long *)&maxfreq);
> +             else if (!strncmp(opt, "adma:", 5))
> +                     strict_strtoul(opt + 5, 0, (unsigned long *)&adma);
> +             else if (!strncmp(opt, "led:", 4))
> +                     strict_strtoul(opt + 4, 0, (unsigned long *)&led);
> +             else if (!strncmp(opt, "pio:", 4))
> +                     strict_strtoul(opt + 4, 0, (unsigned long *)&pio);
> +     }
> +     return 0;
> +}
> +
> +__setup("arasanmmc=", arasan_cmdline_opt);
> +#endif
> +
> +module_init(arasan_init);
> +module_exit(arasan_exit);
> +
> +MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>");
> +MODULE_DESCRIPTION("Arasan MMC/SD/SDIO Host Controller driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mmc/host/arasan.h b/drivers/mmc/host/arasan.h
> new file mode 100644
> index 0000000..56a4f8b
> --- /dev/null
> +++ b/drivers/mmc/host/arasan.h
> @@ -0,0 +1,237 @@
> +/*
> + * Author: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>
> + *
> + * Copyright (C) 2010  STMicroelectronics Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __ARASAN_H
> +#define __ARASAN_H
> +
> +#define ARASAN_CLOCKRATE_MAX 50000000
> +#define ARASAN_DRIVER_NAME   "arasan"
> +#define ARASAN_DMA_DESC_NUM  128
> +
> +/*
> + * Register offsets
> + */
> +#define ARASAN_SDMA_SYS_ADDR         0x000
> +#define ARASAN_BLK_SIZE                      0x004
> +#define ARASAN_BLK_COUNT             0x006
> +#define ARASAN_ARG                   0x008
> +#define ARASAN_XFER_MODE             0x00c
> +#define ARASAN_CMD                   0x00e
> +#define ARASAN_RSP(i)                        (0x010 + ((i)<<2))
> +#define ARASAN_RSP0                  0x010
> +#define ARASAN_RSP1                  0x012
> +#define ARASAN_RSP2                  0x014
> +#define ARASAN_RSP3                  0x016
> +#define ARASAN_RSP4                  0x018
> +#define ARASAN_RSP5                  0x01a
> +#define ARASAN_RSP6                  0x01c
> +#define ARASAN_RSP7                  0x01e
> +#define ARASAN_BUFF                  0x020
> +#define ARASAN_PRESENT_STATE         0x024
> +#define ARASAN_HOST_CTRL             0x028
> +#define ARASAN_PWR_CTRL                      0x029
> +#define ARASAN_GAP_CTRL                      0x02a
> +#define ARASAN_GAP_WAKEUP            0x02b
> +#define ARASAN_CLOCK_CTRL            0x02c
> +#define ARASAN_TIMEOUT_CTRL          0x02e
> +#define ARASAN_SW_RESET                      0x02f
> +
> +#define ARASAN_NORMAL_INT_STATUS     0x030
> +#define ARASAN_ERR_INT_STATUS                0x032
> +#define ARASAN_NORMAL_INT_STATUS_EN  0x034
> +#define ARASAN_ERR_INT_STATUS_EN     0x036
> +#define ARASAN_NORMAL_INT_SIGN_EN    0x038
> +#define ARASAN_ERR_INT_SIGN_EN               0x03a
> +
> +#define ARASAN_CMD12_ERR_STATUS              0x03c
> +
> +#define ARASAN_CAPABILITIES          0x040
> +
> +#define ARASAN_ADMA_ERR_STATUS               0x054
> +#define ARASAN_ADMA_ADDRESS          0x058
> +
> +#define ARASAN_SPI_INT_SUPPORT               0x0f0
> +#define ARASAN_HOST_VERSION          0x0fe
> +
> +/* Error Interrupt Status Register */
> +#define ARASAN_CMD_TIMEOUT           (1 << 0)
> +#define ARASAN_CMD_CRC_ERROR         (1 << 1)
> +#define ARASAN_CMD_END_BIT_ERROR     (1 << 2)
> +#define ARASAN_CMD_INDEX_ERROR               (1 << 3)
> +#define ARASAN_DATA_TIMEOUT_ERROR    (1 << 4)
> +#define ARASAN_DATA_CRC_ERROR                (1 << 5)
> +#define ARASAN_DATA_END_ERROR                (1 << 6)
> +#define ARASAN_CURRENT_LIMIT_ERROR   (1 << 7)
> +#define ARASAN_AUTO_CMD12_ERROR              (1 << 8)
> +#define ARASAN_ADMA_ERROR            (1 << 9)
> +#define ARASAN_TARGET_RESP_ERROR     (1 << 12)
> +#define ARASAN_CEATA_ERROR           (1 << 13)
> +
> +/* Error Interrupt Status ENABLE reg. (0- Masked, 1: Enabled) */
> +#define ARASAN_E_EN_CMD_TIMEOUT              (1 << 0)
> +#define ARASAN_E_EN_CMD_CRC_ERROR    (1 << 1)
> +#define ARASAN_E_EN_CMD_END_BIT_ERROR        (1 << 2)
> +#define ARASAN_E_EN_CMD_INDEX_ERROR  (1 << 3)
> +#define ARASAN_E_EN_DATA_TIMEOUT_ERROR       (1 << 4)
> +#define ARASAN_E_EN_DATA_CRC_ERROR   (1 << 5)
> +#define ARASAN_E_EN_DATA_END_ERROR   (1 << 6)
> +#define ARASAN_E_EN_CURRENT_LIMIT_ERROR      (1 << 7)
> +#define ARASAN_E_EN_AUTO_CMD12_ERROR (1 << 8)
> +#define ARASAN_E_EN_ADMA_ERROR               (1 << 9)
> +#define ARASAN_E_EN_TARGET_RESP_ERROR        (1 << 12)
> +#define ARASAN_E_EN_CEATA_ERROR              (1 << 13)
> +
> +/* Normal Interrupt Status Register */
> +#define ARASAN_N_CMD_COMPLETE                (1 << 0)
> +#define ARASAN_N_TRANS_COMPLETE              (1 << 1)
> +#define ARASAN_N_BLK_GAP_EVENT               (1 << 2)
> +#define ARASAN_N_DMA_IRQ             (1 << 3)
> +#define ARASAN_N_BUFF_WRITE          (1 << 4)
> +#define ARASAN_N_BUFF_READ           (1 << 5)
> +#define ARASAN_N_CARD_INS            (1 << 6)
> +#define ARASAN_N_CARD_REMOVAL                (1 << 7)
> +#define ARASAN_N_CARD_IRQ            (1 << 8)
> +#define ARASAN_N_ERROR_IRQ           (1 << 15)
> +
> +/* Normal Interrupt Status ENABLE reg. (0- Masked, 1: Enabled) */
> +#define ARASAN_N_EN_CMD_COMPLETE     (1 << 0)
> +#define ARASAN_N_EN_TRANS_COMPL              (1 << 1)
> +#define ARASAN_N_EN_BLOCK_GAP                (1 << 2)
> +#define ARASAN_N_EN_DMA_IRQ          (1 << 3)
> +#define ARASAN_N_EN_BUFF_WRITE               (1 << 4)
> +#define ARASAN_N_EN_BUFF_READ                (1 << 5)
> +#define ARASAN_N_EN_CARD_INS         (1 << 6)
> +#define ARASAN_N_EN_CARD_REM         (1 << 7)
> +#define ARASAN_N_EN_CARD_IRQ         (1 << 8)
> +
> +/* Default Enable Normal/Error interrupt mask */
> +#define ARASAN_IRQ_DEFAULT_MASK              0x02ff00fb
> +
> +/* Mask normal and error fields */
> +#define ARASAN_INT_DATA_MASK         0x0070003a
> +#define ARASAN_INT_CMD_MASK          0x000f0001
> +
> +/* Command Register */
> +#define ARASAN_CMD_RSP_NONE          (0 << 0)
> +#define ARASAN_CMD_RSP_136           (1 << 0)
> +#define ARASAN_CMD_RSP_48            (2 << 0)
> +#define ARASAN_CMD_RSP_48BUSY                (3 << 0)
> +#define ARASAN_CMD_CHECK_CMDCRC              (1 << 3)
> +#define ARASAN_CMD_INDX_CHECK                (1 << 4)
> +#define ARASAN_CMD_DATA_PRESENT              (1 << 5)
> +#define ARASAN_COMMAD_TYPE_NORM              (0 << 6)
> +#define ARASAN_COMMAD_TYPE_SUSP              (1 << 6)
> +#define ARASAN_COMMAD_TYPE_RESU              (2 << 6)
> +#define ARASAN_COMMAD_TYPE_ABOR              (3 << 6)
> +#define ARASAN_CMD_INDEX(x)          ((x) << 8)
> +
> +/* Transfer Mode Register */
> +#define ARASAN_XFER_DMA_EN           (1 << 0)
> +#define ARASAN_XFER_BLK_COUNT_EN     (1 << 1)
> +#define ARASAN_XFER_AUTOCMD12                (1 << 2)        /* 1: Enable */
> +#define ARASAN_XFER_DATA_DIR         (1 << 4)        /* 0: Write, 1: Read */
> +#define ARASAN_XFER_MULTI_BLK                (1 << 5)        /* 0: Single 1: Multi
> */
> +#define ARASAN_XFER_SPI_MODE         (1 << 7)        /* 1: SPI 0: SD Mode */
> +
> +enum xfer_dat_cmd_status {
> +     STATE_CMD = 0,
> +     STATE_DATA_WRITE = 1,
> +     STATE_DATA_READ = 2,
> +     STATE_DATA_STOP = 2,
> +};
> +
> +/* Software Reset */
> +#define ARSAN_RESET_ALL                      0x1
> +#define ARSAN_RESET_CMD_LINE         0x2
> +#define ARSAN_RESET_DAT_LINE         0x4
> +
> +enum sw_reset_cmd {
> +     reset_all = 0,
> +     reset_cmd_line = 1,
> +     reset_dat_line = 2,
> +};
> +
> +/* Host Control Register */
> +#define ARASAN_HOST_CTRL_LED         (1 << 0)
> +#define ARASAN_HOST_CTRL_SD          (1 << 1)        /* 1: 4 bit mode */
> +#define ARASAN_HOST_CTRL_HIGH_SPEED  (1 << 2)
> +#define ARASAN_HOST_CTRL_SDMA_SEL    (0 << 3)
> +#define ARASAN_HOST_CTRL_ADMA1               (1 << 3)
> +#define ARASAN_HOST_CTRL_ADMA2_32    (2 << 3)
> +#define ARASAN_HOST_CTRL_ADMA2_64    (3 << 3)
> +#define ARASAN_HOST_CTRL_SD8         (1 << 5)
> +#define ARASAN_HOST_CTRL_CARD_LEV_TEST       (1 << 6)
> +#define ARASAN_HOST_CTRL_CARD_SIG_TEST       (1 << 7)
> +
> +#define ARASAN_HOST_CTRL_SD_MASK     0x22
> +
> +/* Clock Control Register */
> +#define ARASAN_CLOCK_CTRL_SDCLK_MASK 0xff00
> +#define ARASAN_CLOCK_CTRL_SDCLK_SHIFT        7
> +#define ARASAN_CLOCK_CTRL_SDCLK_256  0x8000
> +#define ARASAN_CLOCK_CTRL_SDCLK_128  0x4000
> +#define ARASAN_CLOCK_CTRL_SDCLK_64   0x2000
> +#define ARASAN_CLOCK_CTRL_SDCLK_32   0x1000
> +#define ARASAN_CLOCK_CTRL_SDCLK_16   0x0800
> +#define ARASAN_CLOCK_CTRL_SDCLK_8    0x0400
> +#define ARASAN_CLOCK_CTRL_SDCLK_4    0x0200
> +#define ARASAN_CLOCK_CTRL_SDCLK_2    0x0100
> +#define ARASAN_CLOCK_CTRL_SDCLK_1    0x0000
> +#define ARASAN_CLOCK_CTRL_SDCLK_ENABLE       (1 << 2)
> +#define ARASAN_CLOCK_CTRL_ICLK_STABLE        (1 << 1)
> +#define ARASAN_CLOCK_CTRL_ICLK_ENABLE        (1 << 0)
> +
> +/* Power Control Register */
> +#define ARASAN_PWR_CTRL_UP           (1 << 0)        /* 1: Power-On */
> +#define ARASAN_PWR_BUS_VOLTAGE_33    (7 << 1)
> +#define ARASAN_PWR_BUS_VOLTAGE_30    (6 << 1)
> +#define ARASAN_PWR_BUS_VOLTAGE_18    (5 << 1)
> +
> +/* CMD12 error status bits */
> +#define ARASAN_AUTOCMD12_ERR_NOTEXE  (1 << 0)
> +#define ARASAN_AUTOCMD12_ERR_TIMEOUT (1 << 1)
> +#define ARASAN_AUTOCMD12_ERR_CRC     (1 << 2)
> +#define ARASAN_AUTOCMD12_ERR_ENDBIT  (1 << 3)
> +#define ARASAN_AUTOCMD12_ERR_INDEX   (1 << 4)
> +#define ARASAN_AUTOCMD12_ERR_NOT_ISSUED      (1 << 7)
> +
> +/* Present State Register */
> +#define ARASAN_PRESENT_STATE_DAT7_4       0x1e000000
> +#define ARASAN_PRESENT_STATE_CMD_LINE     0x01000000
> +#define ARASAN_PRESENT_STATE_DAT3_0       0x00f00000
> +#define ARASAN_PRESENT_STATE_WR_EN        0x00080000
> +#define ARASAN_PRESENT_STATE_CARD_DETECT  0x00040000
> +#define ARASAN_PRESENT_STATE_CARD_STABLE  0x00020000
> +#define ARASAN_PRESENT_STATE_CARD_PRESENT 0x00010000
> +#define ARASAN_PRESENT_STATE_BUFFER_RD_EN 0x00000800
> +#define ARASAN_PRESENT_STATE_BUFFER_WR_EN 0x00000400
> +#define ARASAN_PRESENT_STATE_RD_ACTIVE    0x00000200
> +#define ARASAN_PRESENT_STATE_WR_ACTIVE    0x00000100
> +#define ARASAN_PRESENT_STATE_DAT_ACTIVE   0x00000004
> +#define ARASAN_PRESENT_STATE_DAT_INHIBIT  0x00000002
> +#define ARASAN_PRESENT_STATE_CMD_INHIBIT  0x00000001
> +
> +/* Block size register defines */
> +#define ARASAN_BLOCK_SIZE_SDMA_512KB 0x7000
> +#define ARASAN_BLOCK_SIZE_SDMA_256KB 0x6000
> +#define ARASAN_BLOCK_SIZE_SDMA_128KB 0x5000
> +#define ARASAN_BLOCK_SIZE_SDMA_64KB  0x4000
> +#define ARASAN_BLOCK_SIZE_SDMA_32KB  0x3000
> +#define ARASAN_BLOCK_SIZE_SDMA_16KB  0x2000
> +#define ARASAN_BLOCK_SIZE_SDMA_8KB   0x1000
> +#define ARASAN_BLOCK_SIZE_SDMA_4KB   0x0000
> +#define ARASAN_BLOCK_SIZE_TRANSFER   0x0fff
> +
> +/* ADMA Error Status Register */
> +#define ARASAN_ADMA_ERROR_LENGTH          0x04
> +#define ARASAN_ADMA_ERROR_ST_TFR          0x03
> +#define ARASAN_ADMA_ERROR_ST_FDS          0x01
> +#define ARASAN_ADMA_ERROR_ST_STOP         0x00
> +#endif
> diff --git a/include/linux/mmc/arasan_plat.h
> b/include/linux/mmc/arasan_plat.h
> new file mode 100644
> index 0000000..9e16287
> --- /dev/null
> +++ b/include/linux/mmc/arasan_plat.h
> @@ -0,0 +1,49 @@
> +/*
> + * Author: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>
> + *
> + * include/linux/mmc/arsan_plat.h
> + *
> + * platform data for the Arasan MMC/SD/SDI HC driver
> + *
> + * Copyright (C) 2010  STMicroelectronics Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License.
> + *
> + */
> +
> +#ifndef __ARASAN_PLAT_H__
> +#define __ARASAN_PLAT_H__
> +
> +struct arasan_platform_data {
> +     unsigned int need_poll;
> +#ifdef CONFIG_STM_DRIVERS
> +     struct stm_pad_config *pad_config;
> +#endif
> +};
> +
> +/* ARASAN Resource configuration */
> +#ifdef CONFIG_STM_DRIVERS
> +#include <linux/stm/platform.h>
> +#include <linux/stm/pad.h>
> +static inline int arasan_claim_resource(struct platform_device *pdev)
> +{
> +     int ret = 0;
> +     struct arasan_platform_data *plat_dat = pdev->dev.platform_data;
> +
> +     /* Pad routing setup required on STM platforms */
> +     if (!devm_stm_pad_claim(&pdev->dev, plat_dat->pad_config,
> +                             dev_name(&pdev->dev))) {
> +             pr_err("%s: Failed to request pads!\n", __func__);
> +             ret = -ENODEV;
> +     }
> +     return ret;
> +}
> +#else
> +static inline int arasan_claim_resource(struct platform_device *pdev)
> +{
> +     return 0;
> +}
> +#endif
> +#endif
> --
> 1.5.5.6

--
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


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux