aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc/host/mvsdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/mvsdio.c')
-rw-r--r--drivers/mmc/host/mvsdio.c317
1 files changed, 140 insertions, 177 deletions
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index a5bf60e01af..6b4c5ad3b39 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -19,18 +19,23 @@
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/irq.h>
+#include <linux/clk.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/pinctrl/consumer.h>
#include <asm/sizes.h>
#include <asm/unaligned.h>
-#include <plat/mvsdio.h>
+#include <linux/platform_data/mmc-mvsdio.h>
#include "mvsdio.h"
#define DRIVER_NAME "mvsdio"
-static int maxfreq = MVSD_CLOCKRATE_MAX;
+static int maxfreq;
static int nodma;
struct mvsd_host {
@@ -49,10 +54,7 @@ struct mvsd_host {
struct timer_list timer;
struct mmc_host *mmc;
struct device *dev;
- struct resource *res;
- int irq;
- int gpio_card_detect;
- int gpio_write_protect;
+ struct clk *clk;
};
#define mvsd_write(offs, val) writel(val, iobase + (offs))
@@ -77,11 +79,11 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
unsigned long t = jiffies + HZ;
unsigned int hw_state, count = 0;
do {
+ hw_state = mvsd_read(MVSD_HW_STATE);
if (time_after(jiffies, t)) {
dev_warn(host->dev, "FIFO_EMPTY bit missing\n");
break;
}
- hw_state = mvsd_read(MVSD_HW_STATE);
count++;
} while (!(hw_state & (1 << 13)));
dev_dbg(host->dev, "*** wait for FIFO_EMPTY bit "
@@ -117,10 +119,8 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
host->pio_size = data->blocks * data->blksz;
host->pio_ptr = sg_virt(data->sg);
if (!nodma)
- printk(KERN_DEBUG "%s: fallback to PIO for data "
- "at 0x%p size %d\n",
- mmc_hostname(host->mmc),
- host->pio_ptr, host->pio_size);
+ dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n",
+ host->pio_ptr, host->pio_size);
return 1;
} else {
dma_addr_t phys_addr;
@@ -354,6 +354,20 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
intr_status, mvsd_read(MVSD_NOR_INTR_EN),
mvsd_read(MVSD_HW_STATE));
+ /*
+ * It looks like, SDIO IP can issue one late, spurious irq
+ * although all irqs should be disabled. To work around this,
+ * bail out early, if we didn't expect any irqs to occur.
+ */
+ if (!mvsd_read(MVSD_NOR_INTR_EN) && !mvsd_read(MVSD_ERR_INTR_EN)) {
+ dev_dbg(host->dev, "spurious irq detected intr 0x%04x intr_en 0x%04x erri 0x%04x erri_en 0x%04x\n",
+ mvsd_read(MVSD_NOR_INTR_STATUS),
+ mvsd_read(MVSD_NOR_INTR_EN),
+ mvsd_read(MVSD_ERR_INTR_STATUS),
+ mvsd_read(MVSD_ERR_INTR_EN));
+ return IRQ_HANDLED;
+ }
+
spin_lock(&host->lock);
/* PIO handling, if needed. Messy business... */
@@ -471,8 +485,8 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
if (mrq->data)
err_status = mvsd_finish_data(host, mrq->data, err_status);
if (err_status) {
- printk(KERN_ERR "%s: unhandled error status %#04x\n",
- mmc_hostname(host->mmc), err_status);
+ dev_err(host->dev, "unhandled error status %#04x\n",
+ err_status);
cmd->error = -ENOMSG;
}
@@ -489,9 +503,8 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
if (irq_handled)
return IRQ_HANDLED;
- printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x "
- "pio=%d\n", mmc_hostname(host->mmc), intr_status,
- host->intr_en, host->pio_size);
+ dev_err(host->dev, "unhandled interrupt status=0x%04x en=0x%04x pio=%d\n",
+ intr_status, host->intr_en, host->pio_size);
return IRQ_NONE;
}
@@ -505,13 +518,11 @@ static void mvsd_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags);
mrq = host->mrq;
if (mrq) {
- printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n",
- mmc_hostname(host->mmc));
- printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x "
- "intr_en=0x%04x\n", mmc_hostname(host->mmc),
- mvsd_read(MVSD_HW_STATE),
- mvsd_read(MVSD_NOR_INTR_STATUS),
- mvsd_read(MVSD_NOR_INTR_EN));
+ dev_err(host->dev, "Timeout waiting for hardware interrupt.\n");
+ dev_err(host->dev, "hw_state=0x%04x, intr_status=0x%04x intr_en=0x%04x\n",
+ mvsd_read(MVSD_HW_STATE),
+ mvsd_read(MVSD_NOR_INTR_STATUS),
+ mvsd_read(MVSD_NOR_INTR_EN));
host->mrq = NULL;
@@ -538,13 +549,6 @@ static void mvsd_timeout_timer(unsigned long data)
mmc_request_done(host->mmc, mrq);
}
-static irqreturn_t mvsd_card_detect_irq(int irq, void *dev)
-{
- struct mvsd_host *host = dev;
- mmc_detect_change(host->mmc, msecs_to_jiffies(100));
- return IRQ_HANDLED;
-}
-
static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct mvsd_host *host = mmc_priv(mmc);
@@ -564,20 +568,6 @@ static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
}
-static int mvsd_get_ro(struct mmc_host *mmc)
-{
- struct mvsd_host *host = mmc_priv(mmc);
-
- if (host->gpio_write_protect)
- return gpio_get_value(host->gpio_write_protect);
-
- /*
- * Board doesn't support read only detection; let the mmc core
- * decide what to do.
- */
- return -ENOSYS;
-}
-
static void mvsd_power_up(struct mvsd_host *host)
{
void __iomem *iobase = host->base;
@@ -674,13 +664,14 @@ static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static const struct mmc_host_ops mvsd_ops = {
.request = mvsd_request,
- .get_ro = mvsd_get_ro,
+ .get_ro = mmc_gpio_get_ro,
.set_ios = mvsd_set_ios,
.enable_sdio_irq = mvsd_enable_sdio_irq,
};
-static void __init mv_conf_mbus_windows(struct mvsd_host *host,
- struct mbus_dram_target_info *dram)
+static void
+mv_conf_mbus_windows(struct mvsd_host *host,
+ const struct mbus_dram_target_info *dram)
{
void __iomem *iobase = host->base;
int i;
@@ -691,7 +682,7 @@ static void __init mv_conf_mbus_windows(struct mvsd_host *host,
}
for (i = 0; i < dram->num_cs; i++) {
- struct mbus_dram_window *cs = dram->cs + i;
+ const struct mbus_dram_window *cs = dram->cs + i;
writel(((cs->size - 1) & 0xffff0000) |
(cs->mbus_attr << 8) |
(dram->mbus_dram_target_id << 4) | 1,
@@ -700,24 +691,21 @@ static void __init mv_conf_mbus_windows(struct mvsd_host *host,
}
}
-static int __init mvsd_probe(struct platform_device *pdev)
+static int mvsd_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct mmc_host *mmc = NULL;
struct mvsd_host *host = NULL;
- const struct mvsdio_platform_data *mvsd_data;
+ const struct mbus_dram_target_info *dram;
struct resource *r;
int ret, irq;
+ struct pinctrl *pinctrl;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- mvsd_data = pdev->dev.platform_data;
- if (!r || irq < 0 || !mvsd_data)
+ if (!r || irq < 0)
return -ENXIO;
- r = request_mem_region(r->start, SZ_1K, DRIVER_NAME);
- if (!r)
- return -EBUSY;
-
mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
@@ -727,17 +715,28 @@ static int __init mvsd_probe(struct platform_device *pdev)
host = mmc_priv(mmc);
host->mmc = mmc;
host->dev = &pdev->dev;
- host->res = r;
- host->base_clock = mvsd_data->clock / 2;
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&pdev->dev, "no pins associated\n");
+
+ /*
+ * Some non-DT platforms do not pass a clock, and the clock
+ * frequency is passed through platform_data. On DT platforms,
+ * a clock must always be passed, even if there is no gatable
+ * clock associated to the SDIO interface (it can simply be a
+ * fixed rate clock).
+ */
+ host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(host->clk))
+ clk_prepare_enable(host->clk);
mmc->ops = &mvsd_ops;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
- MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
- mmc->f_max = maxfreq;
+ mmc->f_max = MVSD_CLOCKRATE_MAX;
mmc->max_blk_size = 2048;
mmc->max_blk_count = 65535;
@@ -746,54 +745,67 @@ static int __init mvsd_probe(struct platform_device *pdev)
mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ if (np) {
+ if (IS_ERR(host->clk)) {
+ dev_err(&pdev->dev, "DT platforms must have a clock associated\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ host->base_clock = clk_get_rate(host->clk) / 2;
+ ret = mmc_of_parse(mmc);
+ if (ret < 0)
+ goto out;
+ } else {
+ const struct mvsdio_platform_data *mvsd_data;
+
+ mvsd_data = pdev->dev.platform_data;
+ if (!mvsd_data) {
+ ret = -ENXIO;
+ goto out;
+ }
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+ host->base_clock = mvsd_data->clock / 2;
+ /* GPIO 0 regarded as invalid for backward compatibility */
+ if (mvsd_data->gpio_card_detect &&
+ gpio_is_valid(mvsd_data->gpio_card_detect)) {
+ ret = mmc_gpio_request_cd(mmc,
+ mvsd_data->gpio_card_detect,
+ 0);
+ if (ret)
+ goto out;
+ } else {
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+ }
+
+ if (mvsd_data->gpio_write_protect &&
+ gpio_is_valid(mvsd_data->gpio_write_protect))
+ mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect);
+ }
+
+ if (maxfreq)
+ mmc->f_max = maxfreq;
+
spin_lock_init(&host->lock);
- host->base = ioremap(r->start, SZ_4K);
- if (!host->base) {
- ret = -ENOMEM;
+ host->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(host->base)) {
+ ret = PTR_ERR(host->base);
goto out;
}
/* (Re-)program MBUS remapping windows if we are asked to. */
- if (mvsd_data->dram != NULL)
- mv_conf_mbus_windows(host, mvsd_data->dram);
+ dram = mv_mbus_dram_info();
+ if (dram)
+ mv_conf_mbus_windows(host, dram);
mvsd_power_down(host);
- ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
+ ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host);
if (ret) {
- printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq);
+ dev_err(&pdev->dev, "cannot assign irq %d\n", irq);
goto out;
- } else
- host->irq = irq;
-
- if (mvsd_data->gpio_card_detect) {
- ret = gpio_request(mvsd_data->gpio_card_detect,
- DRIVER_NAME " cd");
- if (ret == 0) {
- gpio_direction_input(mvsd_data->gpio_card_detect);
- irq = gpio_to_irq(mvsd_data->gpio_card_detect);
- ret = request_irq(irq, mvsd_card_detect_irq,
- IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
- DRIVER_NAME " cd", host);
- if (ret == 0)
- host->gpio_card_detect =
- mvsd_data->gpio_card_detect;
- else
- gpio_free(mvsd_data->gpio_card_detect);
- }
- }
- if (!host->gpio_card_detect)
- mmc->caps |= MMC_CAP_NEEDS_POLL;
-
- if (mvsd_data->gpio_write_protect) {
- ret = gpio_request(mvsd_data->gpio_write_protect,
- DRIVER_NAME " wp");
- if (ret == 0) {
- gpio_direction_input(mvsd_data->gpio_write_protect);
- host->gpio_write_protect =
- mvsd_data->gpio_write_protect;
- }
}
setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
@@ -802,109 +814,60 @@ static int __init mvsd_probe(struct platform_device *pdev)
if (ret)
goto out;
- printk(KERN_NOTICE "%s: %s driver initialized, ",
- mmc_hostname(mmc), DRIVER_NAME);
- if (host->gpio_card_detect)
- printk("using GPIO %d for card detection\n",
- host->gpio_card_detect);
+ if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
+ dev_dbg(&pdev->dev, "using GPIO for card detection\n");
else
- printk("lacking card detect (fall back to polling)\n");
+ dev_dbg(&pdev->dev, "lacking card detect (fall back to polling)\n");
+
return 0;
out:
- if (host) {
- if (host->irq)
- free_irq(host->irq, host);
- if (host->gpio_card_detect) {
- free_irq(gpio_to_irq(host->gpio_card_detect), host);
- gpio_free(host->gpio_card_detect);
- }
- if (host->gpio_write_protect)
- gpio_free(host->gpio_write_protect);
- if (host->base)
- iounmap(host->base);
- }
- if (r)
- release_resource(r);
- if (mmc)
+ if (mmc) {
+ mmc_gpio_free_cd(mmc);
+ mmc_gpio_free_ro(mmc);
+ if (!IS_ERR(host->clk))
+ clk_disable_unprepare(host->clk);
mmc_free_host(mmc);
+ }
return ret;
}
-static int __exit mvsd_remove(struct platform_device *pdev)
+static int mvsd_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
- if (mmc) {
- struct mvsd_host *host = mmc_priv(mmc);
-
- if (host->gpio_card_detect) {
- free_irq(gpio_to_irq(host->gpio_card_detect), host);
- gpio_free(host->gpio_card_detect);
- }
- mmc_remove_host(mmc);
- free_irq(host->irq, host);
- if (host->gpio_write_protect)
- gpio_free(host->gpio_write_protect);
- del_timer_sync(&host->timer);
- mvsd_power_down(host);
- iounmap(host->base);
- release_resource(host->res);
- mmc_free_host(mmc);
- }
- platform_set_drvdata(pdev, NULL);
- return 0;
-}
+ struct mvsd_host *host = mmc_priv(mmc);
-#ifdef CONFIG_PM
-static int mvsd_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct mmc_host *mmc = platform_get_drvdata(dev);
- int ret = 0;
+ mmc_gpio_free_cd(mmc);
+ mmc_gpio_free_ro(mmc);
+ mmc_remove_host(mmc);
+ del_timer_sync(&host->timer);
+ mvsd_power_down(host);
- if (mmc)
- ret = mmc_suspend_host(mmc);
+ if (!IS_ERR(host->clk))
+ clk_disable_unprepare(host->clk);
+ mmc_free_host(mmc);
- return ret;
+ return 0;
}
-static int mvsd_resume(struct platform_device *dev)
-{
- struct mmc_host *mmc = platform_get_drvdata(dev);
- int ret = 0;
-
- if (mmc)
- ret = mmc_resume_host(mmc);
-
- return ret;
-}
-#else
-#define mvsd_suspend NULL
-#define mvsd_resume NULL
-#endif
+static const struct of_device_id mvsdio_dt_ids[] = {
+ { .compatible = "marvell,orion-sdio" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
static struct platform_driver mvsd_driver = {
- .remove = __exit_p(mvsd_remove),
- .suspend = mvsd_suspend,
- .resume = mvsd_resume,
+ .probe = mvsd_probe,
+ .remove = mvsd_remove,
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = mvsdio_dt_ids,
},
};
-static int __init mvsd_init(void)
-{
- return platform_driver_probe(&mvsd_driver, mvsd_probe);
-}
-
-static void __exit mvsd_exit(void)
-{
- platform_driver_unregister(&mvsd_driver);
-}
-
-module_init(mvsd_init);
-module_exit(mvsd_exit);
+module_platform_driver(mvsd_driver);
/* maximum card clock frequency (default 50MHz) */
module_param(maxfreq, int, 0);