summaryrefslogtreecommitdiffstats
path: root/drivers/mci/mmci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mci/mmci.c')
-rw-r--r--drivers/mci/mmci.c690
1 files changed, 690 insertions, 0 deletions
diff --git a/drivers/mci/mmci.c b/drivers/mci/mmci.c
new file mode 100644
index 0000000000..66ca450206
--- /dev/null
+++ b/drivers/mci/mmci.c
@@ -0,0 +1,690 @@
+/*
+ * ARM PrimeCell MultiMedia Card Interface - PL180
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ulf Hansson <ulf.hansson@stericsson.com>
+ * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com>
+ * Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org>
+ *
+ * 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, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <init.h>
+#include <mci.h>
+#include <io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <errno.h>
+#include <malloc.h>
+#include <mmci.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/mmci.h>
+
+#include "mmci.h"
+
+#define DRIVER_NAME "mmci-pl18x"
+
+static unsigned long fmax = 515633;
+
+/**
+ * struct variant_data - MMCI variant-specific quirks
+ * @clkreg: default value for MCICLOCK register
+ * @clkreg_enable: enable value for MMCICLOCK register
+ * @datalength_bits: number of bits in the MMCIDATALENGTH register
+ * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
+ * is asserted (likewise for RX)
+ * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
+ * is asserted (likewise for RX)
+ * @sdio: variant supports SDIO
+ * @st_clkdiv: true if using a ST-specific clock divider algorithm
+ * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
+ * @pwrreg_powerup: power up value for MMCIPOWER register
+ * @signal_direction: input/out direction of bus signals can be indicated
+ */
+struct variant_data {
+ unsigned int clkreg;
+ unsigned int clkreg_enable;
+ unsigned int datalength_bits;
+ unsigned int fifosize;
+ unsigned int fifohalfsize;
+ bool sdio;
+ bool st_clkdiv;
+ bool blksz_datactrl16;
+ u32 pwrreg_powerup;
+ bool signal_direction;
+};
+
+static struct variant_data variant_arm = {
+ .fifosize = 16 * 4,
+ .fifohalfsize = 8 * 4,
+ .datalength_bits = 16,
+ .pwrreg_powerup = MCI_PWR_UP,
+};
+
+static struct variant_data variant_arm_extended_fifo = {
+ .fifosize = 128 * 4,
+ .fifohalfsize = 64 * 4,
+ .datalength_bits = 16,
+ .pwrreg_powerup = MCI_PWR_UP,
+};
+
+static struct variant_data variant_ux500 = {
+ .fifosize = 30 * 4,
+ .fifohalfsize = 8 * 4,
+ .clkreg = MCI_CLK_ENABLE,
+ .clkreg_enable = MCI_ST_UX500_HWFCEN,
+ .datalength_bits = 24,
+ .sdio = true,
+ .st_clkdiv = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .signal_direction = true,
+};
+
+static struct variant_data variant_ux500v2 = {
+ .fifosize = 30 * 4,
+ .fifohalfsize = 8 * 4,
+ .clkreg = MCI_CLK_ENABLE,
+ .clkreg_enable = MCI_ST_UX500_HWFCEN,
+ .datalength_bits = 24,
+ .sdio = true,
+ .st_clkdiv = true,
+ .blksz_datactrl16 = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .signal_direction = true,
+};
+
+struct mmci_host {
+ struct mci_host mci;
+ void __iomem *base;
+ struct device_d *hw_dev;
+ struct mmci_platform_data *plat;
+ struct clk *clk;
+ unsigned long mclk;
+
+ int hw_revision;
+ int hw_designer;
+ struct variant_data *variant;
+};
+
+#define to_mci_host(mci) container_of(mci, struct mmci_host, mci)
+
+static inline u32 mmci_readl(struct mmci_host *host, u32 offset)
+{
+ return readl(host->base + offset);
+}
+
+static inline void mmci_writel(struct mmci_host *host, u32 offset,
+ u32 value)
+{
+ writel(value, host->base + offset);
+}
+
+static int wait_for_command_end(struct mci_host *mci, struct mci_cmd *cmd)
+{
+ u32 hoststatus, statusmask;
+ struct mmci_host *host = to_mci_host(mci);
+
+ statusmask = MCI_CMDTIMEOUT | MCI_CMDCRCFAIL;
+ if ((cmd->resp_type & MMC_RSP_PRESENT))
+ statusmask |= MCI_CMDRESPEND;
+ else
+ statusmask |= MCI_CMDSENT;
+
+ do
+ hoststatus = mmci_readl(host, MMCISTATUS) & statusmask;
+ while (!hoststatus);
+
+ dev_dbg(host->hw_dev, "SDI_ICR <= 0x%08X\n", statusmask);
+ dev_dbg(host->hw_dev, "status <= 0x%08X\n", hoststatus);
+ mmci_writel(host, MMCICLEAR, statusmask);
+ if (hoststatus & MCI_CMDTIMEOUT) {
+ dev_dbg(host->hw_dev, "CMD%d time out\n", cmd->cmdidx);
+ return -ETIMEDOUT;
+ } else if ((hoststatus & MCI_CMDCRCFAIL) &&
+ (cmd->resp_type & MMC_RSP_CRC)) {
+ dev_err(host->hw_dev, "CMD%d CRC error\n", cmd->cmdidx);
+ return -EILSEQ;
+ }
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ cmd->response[0] = mmci_readl(host, MMCIRESPONSE0);
+ cmd->response[1] = mmci_readl(host, MMCIRESPONSE1);
+ cmd->response[2] = mmci_readl(host, MMCIRESPONSE2);
+ cmd->response[3] = mmci_readl(host, MMCIRESPONSE3);
+ dev_dbg(host->hw_dev, "CMD%d response[0]:0x%08X, response[1]:0x%08X, "
+ "response[2]:0x%08X, response[3]:0x%08X\n",
+ cmd->cmdidx, cmd->response[0], cmd->response[1],
+ cmd->response[2], cmd->response[3]);
+ }
+
+ return 0;
+}
+
+/* send command to the mmc card and wait for results */
+static int do_command(struct mci_host *mci, struct mci_cmd *cmd)
+{
+ int result;
+ u32 sdi_cmd = 0;
+ struct mmci_host *host = to_mci_host(mci);
+
+ dev_dbg(host->hw_dev, "Request to do CMD%d\n", cmd->cmdidx);
+
+ sdi_cmd = ((cmd->cmdidx & MCI_CMDINDEXMASK) | MCI_CPSM_ENABLE);
+
+ if (cmd->resp_type) {
+ sdi_cmd |= MCI_CPSM_RESPONSE;
+ if (cmd->resp_type & MMC_RSP_136)
+ sdi_cmd |= MCI_CPSM_LONGRSP;
+ }
+
+ dev_dbg(host->hw_dev, "SDI_ARG <= 0x%08X\n", cmd->cmdarg);
+ mmci_writel(host, MMCIARGUMENT, (u32)cmd->cmdarg);
+ udelay(COMMAND_REG_DELAY);
+ dev_dbg(host->hw_dev, "SDI_CMD <= 0x%08X\n", sdi_cmd);
+
+ mmci_writel(host, MMCICOMMAND, sdi_cmd);
+ result = wait_for_command_end(mci, cmd);
+
+ /* After CMD3 open drain is switched off and push pull is used. */
+ if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) {
+ u32 sdi_pwr = mmci_readl(host, MMCIPOWER) & ~MCI_OD;
+ mmci_writel(host, MMCIPOWER, sdi_pwr);
+ }
+
+ return result;
+}
+
+static u64 mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int host_remain)
+{
+ void __iomem *base = host->base;
+ char *ptr = buffer;
+ u32 status;
+ struct variant_data *variant = host->variant;
+
+ do {
+ int count = readl(base + MMCIFIFOCNT) << 2;
+
+ if (count > host_remain)
+ count = host_remain;
+
+ if (count > variant->fifosize)
+ count = variant->fifosize;
+
+ if (count <= 0)
+ break;
+
+ /*
+ * SDIO especially may want to send something that is
+ * not divisible by 4 (as opposed to card sectors
+ * etc). Therefore make sure to always read the last bytes
+ * while only doing full 32-bit reads towards the FIFO.
+ */
+ if (unlikely(count & 0x3)) {
+ if (count < 4) {
+ unsigned char buf[4];
+ readsl(base + MMCIFIFO, buf, 1);
+ memcpy(ptr, buf, count);
+ } else {
+ readsl(base + MMCIFIFO, ptr, count >> 2);
+ count &= ~0x3;
+ }
+ } else {
+ readsl(base + MMCIFIFO, ptr, count >> 2);
+ }
+
+ ptr += count;
+ host_remain -= count;
+
+ if (host_remain == 0)
+ break;
+
+ status = readl(base + MMCISTATUS);
+ } while (status & MCI_RXDATAAVLBL);
+
+ return ptr - buffer;
+}
+
+static int read_bytes(struct mci_host *mci, char *dest, unsigned int blkcount, unsigned int blksize)
+{
+ unsigned int xfercount = blkcount * blksize;
+ struct mmci_host *host = to_mci_host(mci);
+ u32 status, status_err;
+ int len;
+
+ dev_dbg(host->hw_dev, "read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
+
+ do {
+ mmci_writel(host, MMCIDATACTRL, mmci_readl(host, MMCIDATACTRL));
+ len = mmci_pio_read(host, dest, xfercount);
+ xfercount -= len;
+ dest += len;
+ status = mmci_readl(host, MMCISTATUS);
+ status_err = status & (MCI_CMDCRCFAIL | MCI_DATATIMEOUT |
+ MCI_RXOVERRUN);
+ } while(xfercount && !status_err);
+
+ status_err = status &
+ (MCI_CMDCRCFAIL | MCI_DATATIMEOUT | MCI_DATABLOCKEND |
+ MCI_RXOVERRUN);
+
+ while (!status_err) {
+ status = mmci_readl(host, MMCISTATUS);
+ status_err = status &
+ (MCI_CMDCRCFAIL | MCI_DATATIMEOUT | MCI_DATABLOCKEND |
+ MCI_RXOVERRUN);
+ }
+
+ if (status & MCI_DATATIMEOUT) {
+ dev_err(host->hw_dev, "Read data timed out, xfercount: %u, status: 0x%08X\n",
+ xfercount, status);
+ return -ETIMEDOUT;
+ } else if (status & MCI_CMDCRCFAIL) {
+ dev_err(host->hw_dev, "Read data bytes CRC error: 0x%x\n", status);
+ return -EILSEQ;
+ } else if (status & MCI_RXOVERRUN) {
+ dev_err(host->hw_dev, "Read data RX overflow error\n");
+ return -EIO;
+ }
+
+ mmci_writel(host, MMCICLEAR, MCI_ICR_MASK);
+
+ if (xfercount) {
+ dev_err(host->hw_dev, "Read data error, xfercount: %u\n", xfercount);
+ return -ENOBUFS;
+ }
+
+ return 0;
+}
+
+static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
+{
+ struct variant_data *variant = host->variant;
+ void __iomem *base = host->base;
+ char *ptr = buffer;
+
+ do {
+ unsigned int count, maxcnt;
+
+ maxcnt = status & MCI_TXFIFOEMPTY ?
+ variant->fifosize : variant->fifohalfsize;
+ count = min(remain, maxcnt);
+
+ /*
+ * SDIO especially may want to send something that is
+ * not divisible by 4 (as opposed to card sectors
+ * etc), and the FIFO only accept full 32-bit writes.
+ * So compensate by adding +3 on the count, a single
+ * byte become a 32bit write, 7 bytes will be two
+ * 32bit writes etc.
+ */
+ writesl(base + MMCIFIFO, ptr, (count + 3) >> 2);
+
+ ptr += count;
+ remain -= count;
+
+ if (remain == 0)
+ break;
+
+ status = readl(base + MMCISTATUS);
+ } while (status & MCI_TXFIFOHALFEMPTY);
+
+ return ptr - buffer;
+}
+
+static int write_bytes(struct mci_host *mci, char *dest, unsigned int blkcount, unsigned int blksize)
+{
+ unsigned int xfercount = blkcount * blksize;
+ struct mmci_host *host = to_mci_host(mci);
+ u32 status, status_err;
+ int len;
+
+ dev_dbg(host->hw_dev, "write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
+
+ status = mmci_readl(host, MMCISTATUS);
+ status_err = status & (MCI_CMDCRCFAIL | MCI_DATATIMEOUT);
+
+ do {
+ len = mmci_pio_write(host, dest, xfercount, status);
+ xfercount -= len;
+ dest += len;
+
+ status = mmci_readl(host, MMCISTATUS);
+ status_err = status & (MCI_CMDCRCFAIL | MCI_DATATIMEOUT);
+ } while (!status_err && xfercount);
+
+ status_err = status &
+ (MCI_CMDCRCFAIL | MCI_DATATIMEOUT | MCI_DATABLOCKEND);
+ while (!status_err) {
+ status = mmci_readl(host, MMCISTATUS);
+ status_err = status &
+ (MCI_CMDCRCFAIL | MCI_DATATIMEOUT | MCI_DATABLOCKEND);
+ }
+
+ if (status & MCI_DATATIMEOUT) {
+ dev_err(host->hw_dev, "Write data timed out, xfercount:%u,status:0x%08X\n",
+ xfercount, status);
+ return -ETIMEDOUT;
+ } else if (status & MCI_CMDCRCFAIL) {
+ dev_err(host->hw_dev, "Write data CRC error\n");
+ return -EILSEQ;
+ }
+
+ mmci_writel(host, MMCICLEAR, MCI_ICR_MASK);
+
+ if (xfercount) {
+ dev_err(host->hw_dev, "Write data error, xfercount:%u", xfercount);
+ return -ENOBUFS;
+ }
+
+ return 0;
+}
+
+static int do_data_transfer(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+ int error = -ETIMEDOUT;
+ struct mmci_host *host = to_mci_host(mci);
+ u32 data_ctrl;
+ u32 data_len = (u32) (data->blocks * data->blocksize);
+
+ if (host->variant->blksz_datactrl16) {
+ data_ctrl = data->blocksize << 16;
+ } else {
+ u32 blksz_bits;
+
+ blksz_bits = ffs(data->blocksize) - 1;
+ data_ctrl = blksz_bits << 4;
+ }
+ data_ctrl |= MCI_DPSM_ENABLE;
+
+ if (data_ctrl & MCI_ST_DPSM_DDRMODE)
+ dev_dbg(host->hw_dev, "MCI_ST_DPSM_DDRMODE\n");
+
+ mmci_writel(host, MMCIDATATIMER, MCI_DTIMER_DEFAULT);
+ mmci_writel(host, MMCIDATALENGTH, data_len);
+ udelay(DATA_REG_DELAY);
+
+ error = do_command(mci, cmd);
+ if (error)
+ return error;
+
+ if (data->flags & MMC_DATA_READ)
+ data_ctrl |= MCI_DPSM_DIRECTION;
+
+ mmci_writel(host, MMCIDATACTRL ,data_ctrl);
+
+ if (data->flags & MMC_DATA_READ)
+ error = read_bytes(mci, data->dest, data->blocks,
+ data->blocksize);
+ else if (data->flags & MMC_DATA_WRITE)
+ error = write_bytes(mci, (char *)data->src, data->blocks,
+ data->blocksize);
+
+ return error;
+}
+
+static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+ int result;
+
+ if (data)
+ result = do_data_transfer(mci, cmd, data);
+ else
+ result = do_command(mci, cmd);
+
+ return result;
+}
+
+/* MMC uses open drain drivers in the enumeration phase */
+static int mci_reset(struct mci_host *mci, struct device_d *mci_dev)
+{
+ struct mmci_host *host = to_mci_host(mci);
+ struct variant_data *variant = host->variant;
+
+ u32 pwr = variant->pwrreg_powerup;
+
+ if (variant->signal_direction) {
+ /*
+ * The ST Micro variant has some additional bits
+ * indicating signal direction for the signals in
+ * the SD/MMC bus and feedback-clock usage.
+ */
+ pwr |= host->plat->sigdir;
+ }
+
+ if (host->hw_designer != AMBA_VENDOR_ST) {
+ pwr |= MCI_ROD;
+ } else {
+ /*
+ * The ST Micro variant use the ROD bit for something
+ * else and only has OD (Open Drain).
+ */
+ pwr |= MCI_OD;
+ }
+
+ mmci_writel(host, MMCIPOWER, pwr);
+ return 0;
+}
+
+static void mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct mmci_host *host = to_mci_host(mci);
+ u32 sdi_clkcr;
+
+ sdi_clkcr = mmci_readl(host, MMCICLOCK);
+
+ /* Ramp up the clock rate */
+ if (mci->clock) {
+ u32 clkdiv = 0;
+ u32 tmp_clock;
+
+ dev_dbg(host->hw_dev, "setting clock and bus width in the host:");
+ if (mci->clock >= mci->f_max) {
+ clkdiv = 0;
+ mci->clock = mci->f_max;
+ } else {
+ clkdiv = (host->mclk / mci->clock) - 2;
+ }
+ tmp_clock = host->mclk / (clkdiv + 2);
+ while (tmp_clock > mci->clock) {
+ clkdiv++;
+ tmp_clock = host->mclk / (clkdiv + 2);
+ }
+ if (clkdiv > MCI_CLK_CLKDIV_MASK)
+ clkdiv = MCI_CLK_CLKDIV_MASK;
+ tmp_clock = host->mclk / (clkdiv + 2);
+ mci->clock = tmp_clock;
+ sdi_clkcr &= ~(MCI_CLK_CLKDIV_MASK);
+ sdi_clkcr |= clkdiv;
+ }
+
+ /* Set the bus width */
+ if (mci->bus_width) {
+ u32 buswidth = 0;
+
+ switch (mci->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ buswidth |= MCI_1BIT_BUS;
+ break;
+ case MMC_BUS_WIDTH_4:
+ buswidth |= MCI_4BIT_BUS;
+ break;
+ case MMC_BUS_WIDTH_8:
+ buswidth |= MCI_ST_8BIT_BUS;
+ break;
+ default:
+ dev_err(host->hw_dev, "Invalid bus width (%d)\n", mci->bus_width);
+ break;
+ }
+ sdi_clkcr &= ~(MCI_xBIT_BUS_MASK);
+ sdi_clkcr |= buswidth;
+ }
+
+ mmci_writel(host, MMCICLOCK, sdi_clkcr);
+ udelay(CLK_CHANGE_DELAY);
+}
+
+static int mmci_probe(struct amba_device *dev, const struct amba_id *id)
+{
+ struct device_d *hw_dev = &dev->dev;
+ struct mmci_platform_data *plat = hw_dev->platform_data;
+ struct variant_data *variant = id->data;
+ u32 sdi_u32;
+ struct mmci_host *host;
+ struct clk *clk;
+ int ret;
+
+ if (!plat) {
+ dev_err(hw_dev, "missing platform data\n");
+ return -EINVAL;
+ }
+
+ host = xzalloc(sizeof(*host));
+
+ host->base = amba_get_mem_region(dev);
+ host->mci.send_cmd = mci_request;
+ host->mci.set_ios = mci_set_ios;
+ host->mci.init = mci_reset;
+ host->hw_dev = host->mci.hw_dev = hw_dev;
+
+ clk = clk_get(hw_dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto host_free;
+ }
+
+ ret = clk_enable(clk);
+ if (ret)
+ goto host_free;
+
+ host->hw_designer = amba_manf(dev);
+ host->hw_revision = amba_rev(dev);
+
+ dev_dbg(hw_dev, "hw_designer = 0x%x\n", host->hw_designer);
+ dev_dbg(hw_dev, "hw_revision = 0x%x\n", host->hw_revision);
+
+ host->variant = variant;
+ host->plat = plat;
+
+ mmci_writel(host, MMCIPOWER, plat->sigdir | variant->pwrreg_powerup);
+
+ mmci_writel(host, MMCICLOCK,
+ plat->clkdiv_init | variant->clkreg_enable | variant->clkreg);
+ udelay(CLK_CHANGE_DELAY);
+
+ /* Disable mmc interrupts */
+ sdi_u32 = mmci_readl(host, MMCIMASK0) & ~MCI_MASK0_MASK;
+ mmci_writel(host, MMCIMASK0, sdi_u32);
+
+ host->mclk = clk_get_rate(clk);
+
+ /*
+ * According to the spec, mclk is max 100 MHz,
+ * so we try to adjust the clock down to this,
+ * (if possible).
+ */
+ if (host->mclk > 100000000) {
+ ret = clk_set_rate(clk, 100000000);
+ if (ret < 0)
+ goto clk_disable;
+ host->mclk = clk_get_rate(clk);
+ dev_dbg(hw_dev, "eventual mclk rate: %lu Hz\n", host->mclk);
+ }
+
+ /*
+ * The ARM and ST versions of the block have slightly different
+ * clock divider equations which means that the minimum divider
+ * differs too.
+ */
+ if (variant->st_clkdiv)
+ host->mci.f_min = DIV_ROUND_UP(host->mclk, 257);
+ else
+ host->mci.f_min = DIV_ROUND_UP(host->mclk, 512);
+ /*
+ * If the platform data supplies a maximum operating
+ * frequency, this takes precedence. Else, we fall back
+ * to using the module parameter, which has a (low)
+ * default value in case it is not specified. Either
+ * value must not exceed the clock rate into the block,
+ * of course.
+ */
+ if (plat->f_max)
+ host->mci.f_max = min(host->mclk, plat->f_max);
+ else
+ host->mci.f_max = min(host->mclk, fmax);
+ dev_dbg(hw_dev, "clocking block at %u Hz\n", host->mci.f_max);
+
+ host->mci.max_req_size = (1 << variant->datalength_bits) - 1;
+
+ host->mci.host_caps = plat->capabilities;
+ host->mci.voltages = plat->ocr_mask;
+
+ mci_register(&host->mci);
+
+ return 0;
+
+clk_disable:
+ clk_disable(clk);
+host_free:
+ free(host);
+ return ret;
+}
+
+static struct amba_id mmci_ids[] = {
+ {
+ .id = 0x00041180,
+ .mask = 0xff0fffff,
+ .data = &variant_arm,
+ },
+ {
+ .id = 0x01041180,
+ .mask = 0xff0fffff,
+ .data = &variant_arm_extended_fifo,
+ },
+ {
+ .id = 0x00041181,
+ .mask = 0x000fffff,
+ .data = &variant_arm,
+ },
+ /* ST Micro variants */
+ {
+ .id = 0x00480180,
+ .mask = 0xf0ffffff,
+ .data = &variant_ux500,
+ },
+ {
+ .id = 0x10480180,
+ .mask = 0xf0ffffff,
+ .data = &variant_ux500v2,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver mmci_driver = {
+ .drv = {
+ .name = DRIVER_NAME,
+ },
+ .probe = mmci_probe,
+ .id_table = mmci_ids,
+};
+
+static int mmci_init(void)
+{
+ amba_driver_register(&mmci_driver);
+ return 0;
+}
+device_initcall(mmci_init);