|
@@ -1,5 +1,7 @@
|
|
|
/*
|
|
|
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
|
|
|
+ * Copyright (C) 2013, Imagination Technologies
|
|
|
+ *
|
|
|
* JZ4740 SD/MMC controller driver
|
|
|
*
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
@@ -13,24 +15,25 @@
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
-#include <linux/mmc/host.h>
|
|
|
-#include <linux/mmc/slot-gpio.h>
|
|
|
+#include <linux/bitops.h>
|
|
|
+#include <linux/clk.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+#include <linux/dmaengine.h>
|
|
|
+#include <linux/dma-mapping.h>
|
|
|
#include <linux/err.h>
|
|
|
+#include <linux/gpio.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
#include <linux/io.h>
|
|
|
#include <linux/irq.h>
|
|
|
-#include <linux/interrupt.h>
|
|
|
+#include <linux/mmc/host.h>
|
|
|
+#include <linux/mmc/slot-gpio.h>
|
|
|
#include <linux/module.h>
|
|
|
+#include <linux/of_device.h>
|
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
-#include <linux/delay.h>
|
|
|
#include <linux/scatterlist.h>
|
|
|
-#include <linux/clk.h>
|
|
|
|
|
|
-#include <linux/bitops.h>
|
|
|
-#include <linux/gpio.h>
|
|
|
#include <asm/cacheflush.h>
|
|
|
-#include <linux/dma-mapping.h>
|
|
|
-#include <linux/dmaengine.h>
|
|
|
|
|
|
#include <asm/mach-jz4740/dma.h>
|
|
|
#include <asm/mach-jz4740/jz4740_mmc.h>
|
|
@@ -51,6 +54,7 @@
|
|
|
#define JZ_REG_MMC_RESP_FIFO 0x34
|
|
|
#define JZ_REG_MMC_RXFIFO 0x38
|
|
|
#define JZ_REG_MMC_TXFIFO 0x3C
|
|
|
+#define JZ_REG_MMC_DMAC 0x44
|
|
|
|
|
|
#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7)
|
|
|
#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6)
|
|
@@ -104,9 +108,17 @@
|
|
|
#define JZ_MMC_IRQ_PRG_DONE BIT(1)
|
|
|
#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0)
|
|
|
|
|
|
+#define JZ_MMC_DMAC_DMA_SEL BIT(1)
|
|
|
+#define JZ_MMC_DMAC_DMA_EN BIT(0)
|
|
|
|
|
|
#define JZ_MMC_CLK_RATE 24000000
|
|
|
|
|
|
+enum jz4740_mmc_version {
|
|
|
+ JZ_MMC_JZ4740,
|
|
|
+ JZ_MMC_JZ4750,
|
|
|
+ JZ_MMC_JZ4780,
|
|
|
+};
|
|
|
+
|
|
|
enum jz4740_mmc_state {
|
|
|
JZ4740_MMC_STATE_READ_RESPONSE,
|
|
|
JZ4740_MMC_STATE_TRANSFER_DATA,
|
|
@@ -125,6 +137,8 @@ struct jz4740_mmc_host {
|
|
|
struct jz4740_mmc_platform_data *pdata;
|
|
|
struct clk *clk;
|
|
|
|
|
|
+ enum jz4740_mmc_version version;
|
|
|
+
|
|
|
int irq;
|
|
|
int card_detect_irq;
|
|
|
|
|
@@ -137,7 +151,7 @@ struct jz4740_mmc_host {
|
|
|
|
|
|
uint32_t cmdat;
|
|
|
|
|
|
- uint16_t irq_mask;
|
|
|
+ uint32_t irq_mask;
|
|
|
|
|
|
spinlock_t lock;
|
|
|
|
|
@@ -159,6 +173,32 @@ struct jz4740_mmc_host {
|
|
|
#define JZ4740_MMC_FIFO_HALF_SIZE 8
|
|
|
};
|
|
|
|
|
|
+static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host,
|
|
|
+ uint32_t val)
|
|
|
+{
|
|
|
+ if (host->version >= JZ_MMC_JZ4750)
|
|
|
+ return writel(val, host->base + JZ_REG_MMC_IMASK);
|
|
|
+ else
|
|
|
+ return writew(val, host->base + JZ_REG_MMC_IMASK);
|
|
|
+}
|
|
|
+
|
|
|
+static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host,
|
|
|
+ uint32_t val)
|
|
|
+{
|
|
|
+ if (host->version >= JZ_MMC_JZ4780)
|
|
|
+ return writel(val, host->base + JZ_REG_MMC_IREG);
|
|
|
+ else
|
|
|
+ return writew(val, host->base + JZ_REG_MMC_IREG);
|
|
|
+}
|
|
|
+
|
|
|
+static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host)
|
|
|
+{
|
|
|
+ if (host->version >= JZ_MMC_JZ4780)
|
|
|
+ return readl(host->base + JZ_REG_MMC_IREG);
|
|
|
+ else
|
|
|
+ return readw(host->base + JZ_REG_MMC_IREG);
|
|
|
+}
|
|
|
+
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
/* DMA infrastructure */
|
|
|
|
|
@@ -173,31 +213,23 @@ static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
|
|
|
|
|
|
static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
|
|
|
{
|
|
|
- dma_cap_mask_t mask;
|
|
|
-
|
|
|
- dma_cap_zero(mask);
|
|
|
- dma_cap_set(DMA_SLAVE, mask);
|
|
|
-
|
|
|
- host->dma_tx = dma_request_channel(mask, NULL, host);
|
|
|
- if (!host->dma_tx) {
|
|
|
+ host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx");
|
|
|
+ if (IS_ERR(host->dma_tx)) {
|
|
|
dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
|
|
|
- return -ENODEV;
|
|
|
+ return PTR_ERR(host->dma_tx);
|
|
|
}
|
|
|
|
|
|
- host->dma_rx = dma_request_channel(mask, NULL, host);
|
|
|
- if (!host->dma_rx) {
|
|
|
+ host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx");
|
|
|
+ if (IS_ERR(host->dma_rx)) {
|
|
|
dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
|
|
|
- goto free_master_write;
|
|
|
+ dma_release_channel(host->dma_tx);
|
|
|
+ return PTR_ERR(host->dma_rx);
|
|
|
}
|
|
|
|
|
|
/* Initialize DMA pre request cookie */
|
|
|
host->next_data.cookie = 1;
|
|
|
|
|
|
return 0;
|
|
|
-
|
|
|
-free_master_write:
|
|
|
- dma_release_channel(host->dma_tx);
|
|
|
- return -ENODEV;
|
|
|
}
|
|
|
|
|
|
static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host,
|
|
@@ -363,7 +395,7 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
|
|
|
else
|
|
|
host->irq_mask |= irq;
|
|
|
|
|
|
- writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK);
|
|
|
+ jz4740_mmc_write_irq_mask(host, host->irq_mask);
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
}
|
|
|
|
|
@@ -415,10 +447,10 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
|
|
|
unsigned int irq)
|
|
|
{
|
|
|
unsigned int timeout = 0x800;
|
|
|
- uint16_t status;
|
|
|
+ uint32_t status;
|
|
|
|
|
|
do {
|
|
|
- status = readw(host->base + JZ_REG_MMC_IREG);
|
|
|
+ status = jz4740_mmc_read_irq_reg(host);
|
|
|
} while (!(status & irq) && --timeout);
|
|
|
|
|
|
if (timeout == 0) {
|
|
@@ -518,7 +550,7 @@ static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host,
|
|
|
void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO;
|
|
|
uint32_t *buf;
|
|
|
uint32_t d;
|
|
|
- uint16_t status;
|
|
|
+ uint32_t status;
|
|
|
size_t i, j;
|
|
|
unsigned int timeout;
|
|
|
|
|
@@ -654,8 +686,25 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
|
|
|
cmdat |= JZ_MMC_CMDAT_DATA_EN;
|
|
|
if (cmd->data->flags & MMC_DATA_WRITE)
|
|
|
cmdat |= JZ_MMC_CMDAT_WRITE;
|
|
|
- if (host->use_dma)
|
|
|
- cmdat |= JZ_MMC_CMDAT_DMA_EN;
|
|
|
+ if (host->use_dma) {
|
|
|
+ /*
|
|
|
+ * The 4780's MMC controller has integrated DMA ability
|
|
|
+ * in addition to being able to use the external DMA
|
|
|
+ * controller. It moves DMA control bits to a separate
|
|
|
+ * register. The DMA_SEL bit chooses the external
|
|
|
+ * controller over the integrated one. Earlier SoCs
|
|
|
+ * can only use the external controller, and have a
|
|
|
+ * single DMA enable bit in CMDAT.
|
|
|
+ */
|
|
|
+ if (host->version >= JZ_MMC_JZ4780) {
|
|
|
+ writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL,
|
|
|
+ host->base + JZ_REG_MMC_DMAC);
|
|
|
+ } else {
|
|
|
+ cmdat |= JZ_MMC_CMDAT_DMA_EN;
|
|
|
+ }
|
|
|
+ } else if (host->version >= JZ_MMC_JZ4780) {
|
|
|
+ writel(0, host->base + JZ_REG_MMC_DMAC);
|
|
|
+ }
|
|
|
|
|
|
writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
|
|
|
writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
|
|
@@ -736,7 +785,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
|
|
|
host->state = JZ4740_MMC_STATE_SEND_STOP;
|
|
|
break;
|
|
|
}
|
|
|
- writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG);
|
|
|
+ jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
|
|
|
|
|
|
case JZ4740_MMC_STATE_SEND_STOP:
|
|
|
if (!req->stop)
|
|
@@ -766,9 +815,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
|
|
{
|
|
|
struct jz4740_mmc_host *host = devid;
|
|
|
struct mmc_command *cmd = host->cmd;
|
|
|
- uint16_t irq_reg, status, tmp;
|
|
|
+ uint32_t irq_reg, status, tmp;
|
|
|
|
|
|
- irq_reg = readw(host->base + JZ_REG_MMC_IREG);
|
|
|
+ status = readl(host->base + JZ_REG_MMC_STATUS);
|
|
|
+ irq_reg = jz4740_mmc_read_irq_reg(host);
|
|
|
|
|
|
tmp = irq_reg;
|
|
|
irq_reg &= ~host->irq_mask;
|
|
@@ -777,10 +827,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
|
|
JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE);
|
|
|
|
|
|
if (tmp != irq_reg)
|
|
|
- writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG);
|
|
|
+ jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg);
|
|
|
|
|
|
if (irq_reg & JZ_MMC_IRQ_SDIO) {
|
|
|
- writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG);
|
|
|
+ jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO);
|
|
|
mmc_signal_sdio_irq(host->mmc);
|
|
|
irq_reg &= ~JZ_MMC_IRQ_SDIO;
|
|
|
}
|
|
@@ -789,8 +839,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
|
|
if (test_and_clear_bit(0, &host->waiting)) {
|
|
|
del_timer(&host->timeout_timer);
|
|
|
|
|
|
- status = readl(host->base + JZ_REG_MMC_STATUS);
|
|
|
-
|
|
|
if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
|
|
|
cmd->error = -ETIMEDOUT;
|
|
|
} else if (status & JZ_MMC_STATUS_CRC_RES_ERR) {
|
|
@@ -803,7 +851,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
|
|
}
|
|
|
|
|
|
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
|
|
|
- writew(irq_reg, host->base + JZ_REG_MMC_IREG);
|
|
|
+ jz4740_mmc_write_irq_reg(host, irq_reg);
|
|
|
|
|
|
return IRQ_WAKE_THREAD;
|
|
|
}
|
|
@@ -818,7 +866,7 @@ static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate)
|
|
|
int real_rate;
|
|
|
|
|
|
jz4740_mmc_clock_disable(host);
|
|
|
- clk_set_rate(host->clk, JZ_MMC_CLK_RATE);
|
|
|
+ clk_set_rate(host->clk, host->mmc->f_max);
|
|
|
|
|
|
real_rate = clk_get_rate(host->clk);
|
|
|
|
|
@@ -837,9 +885,7 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|
|
|
|
|
host->req = req;
|
|
|
|
|
|
- writew(0xffff, host->base + JZ_REG_MMC_IREG);
|
|
|
-
|
|
|
- writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG);
|
|
|
+ jz4740_mmc_write_irq_reg(host, ~0);
|
|
|
jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true);
|
|
|
|
|
|
host->state = JZ4740_MMC_STATE_READ_RESPONSE;
|
|
@@ -857,7 +903,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
switch (ios->power_mode) {
|
|
|
case MMC_POWER_UP:
|
|
|
jz4740_mmc_reset(host);
|
|
|
- if (gpio_is_valid(host->pdata->gpio_power))
|
|
|
+ if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
|
|
|
gpio_set_value(host->pdata->gpio_power,
|
|
|
!host->pdata->power_active_low);
|
|
|
host->cmdat |= JZ_MMC_CMDAT_INIT;
|
|
@@ -866,7 +912,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
case MMC_POWER_ON:
|
|
|
break;
|
|
|
default:
|
|
|
- if (gpio_is_valid(host->pdata->gpio_power))
|
|
|
+ if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
|
|
|
gpio_set_value(host->pdata->gpio_power,
|
|
|
host->pdata->power_active_low);
|
|
|
clk_disable_unprepare(host->clk);
|
|
@@ -926,7 +972,7 @@ static int jz4740_mmc_request_gpio(struct device *dev, int gpio,
|
|
|
static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
|
|
struct platform_device *pdev)
|
|
|
{
|
|
|
- struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
|
|
|
+ struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
|
int ret = 0;
|
|
|
|
|
|
if (!pdata)
|
|
@@ -955,7 +1001,7 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
|
|
|
|
|
static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
|
|
{
|
|
|
- struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
|
|
|
+ struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
|
|
|
|
if (!pdata)
|
|
|
return;
|
|
@@ -964,14 +1010,22 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
|
|
gpio_free(pdata->gpio_power);
|
|
|
}
|
|
|
|
|
|
+static const struct of_device_id jz4740_mmc_of_match[] = {
|
|
|
+ { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 },
|
|
|
+ { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 },
|
|
|
+ {},
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match);
|
|
|
+
|
|
|
static int jz4740_mmc_probe(struct platform_device* pdev)
|
|
|
{
|
|
|
int ret;
|
|
|
struct mmc_host *mmc;
|
|
|
struct jz4740_mmc_host *host;
|
|
|
+ const struct of_device_id *match;
|
|
|
struct jz4740_mmc_platform_data *pdata;
|
|
|
|
|
|
- pdata = pdev->dev.platform_data;
|
|
|
+ pdata = dev_get_platdata(&pdev->dev);
|
|
|
|
|
|
mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
|
|
|
if (!mmc) {
|
|
@@ -982,6 +1036,27 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|
|
host = mmc_priv(mmc);
|
|
|
host->pdata = pdata;
|
|
|
|
|
|
+ match = of_match_device(jz4740_mmc_of_match, &pdev->dev);
|
|
|
+ if (match) {
|
|
|
+ host->version = (enum jz4740_mmc_version)match->data;
|
|
|
+ ret = mmc_of_parse(mmc);
|
|
|
+ if (ret) {
|
|
|
+ if (ret != -EPROBE_DEFER)
|
|
|
+ dev_err(&pdev->dev,
|
|
|
+ "could not parse of data: %d\n", ret);
|
|
|
+ goto err_free_host;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* JZ4740 should be the only one using legacy probe */
|
|
|
+ host->version = JZ_MMC_JZ4740;
|
|
|
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
|
|
|
+ if (!(pdata && pdata->data_1bit))
|
|
|
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
|
|
|
+ ret = jz4740_mmc_request_gpios(mmc, pdev);
|
|
|
+ if (ret)
|
|
|
+ goto err_free_host;
|
|
|
+ }
|
|
|
+
|
|
|
host->irq = platform_get_irq(pdev, 0);
|
|
|
if (host->irq < 0) {
|
|
|
ret = host->irq;
|
|
@@ -1004,16 +1079,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|
|
goto err_free_host;
|
|
|
}
|
|
|
|
|
|
- ret = jz4740_mmc_request_gpios(mmc, pdev);
|
|
|
- if (ret)
|
|
|
- goto err_release_dma;
|
|
|
-
|
|
|
mmc->ops = &jz4740_mmc_ops;
|
|
|
- mmc->f_min = JZ_MMC_CLK_RATE / 128;
|
|
|
- mmc->f_max = JZ_MMC_CLK_RATE;
|
|
|
+ if (!mmc->f_max)
|
|
|
+ mmc->f_max = JZ_MMC_CLK_RATE;
|
|
|
+ mmc->f_min = mmc->f_max / 128;
|
|
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
|
- mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA;
|
|
|
- mmc->caps |= MMC_CAP_SDIO_IRQ;
|
|
|
|
|
|
mmc->max_blk_size = (1 << 10) - 1;
|
|
|
mmc->max_blk_count = (1 << 15) - 1;
|
|
@@ -1025,7 +1095,9 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|
|
host->mmc = mmc;
|
|
|
host->pdev = pdev;
|
|
|
spin_lock_init(&host->lock);
|
|
|
- host->irq_mask = 0xffff;
|
|
|
+ host->irq_mask = ~0;
|
|
|
+
|
|
|
+ jz4740_mmc_reset(host);
|
|
|
|
|
|
ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
|
|
|
dev_name(&pdev->dev), host);
|
|
@@ -1034,20 +1106,20 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|
|
goto err_free_gpios;
|
|
|
}
|
|
|
|
|
|
- jz4740_mmc_reset(host);
|
|
|
jz4740_mmc_clock_disable(host);
|
|
|
timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0);
|
|
|
|
|
|
- host->use_dma = true;
|
|
|
- if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)
|
|
|
- host->use_dma = false;
|
|
|
+ ret = jz4740_mmc_acquire_dma_channels(host);
|
|
|
+ if (ret == -EPROBE_DEFER)
|
|
|
+ goto err_free_irq;
|
|
|
+ host->use_dma = !ret;
|
|
|
|
|
|
platform_set_drvdata(pdev, host);
|
|
|
ret = mmc_add_host(mmc);
|
|
|
|
|
|
if (ret) {
|
|
|
dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret);
|
|
|
- goto err_free_irq;
|
|
|
+ goto err_release_dma;
|
|
|
}
|
|
|
dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
|
|
|
|
|
@@ -1057,13 +1129,13 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err_release_dma:
|
|
|
+ if (host->use_dma)
|
|
|
+ jz4740_mmc_release_dma_channels(host);
|
|
|
err_free_irq:
|
|
|
free_irq(host->irq, host);
|
|
|
err_free_gpios:
|
|
|
jz4740_mmc_free_gpios(pdev);
|
|
|
-err_release_dma:
|
|
|
- if (host->use_dma)
|
|
|
- jz4740_mmc_release_dma_channels(host);
|
|
|
err_free_host:
|
|
|
mmc_free_host(mmc);
|
|
|
|
|
@@ -1116,6 +1188,7 @@ static struct platform_driver jz4740_mmc_driver = {
|
|
|
.remove = jz4740_mmc_remove,
|
|
|
.driver = {
|
|
|
.name = "jz4740-mmc",
|
|
|
+ .of_match_table = of_match_ptr(jz4740_mmc_of_match),
|
|
|
.pm = JZ4740_MMC_PM_OPS,
|
|
|
},
|
|
|
};
|