Commit 62ca8034 authored by Shashidhar Hiremath's avatar Shashidhar Hiremath Committed by Chris Ball

mmc: Support of PCI mode in the dw_mmc driver

Support of PCI mode for the dw_mmc driver. This Patch adds the
support for the scenario where the Synopsys Designware IP
is present on the PCI bus. The patch adds the minimal modifications
necessary for the driver to work on PCI platform. Also added separate
files for PCI and PLATFORM modes of operation.
Signed-off-by: default avatarShashidhar Hiremath <shashidharh@vayavyalabs.com>
Acked-by: default avatarJames Hogan <james.hogan@imgtec.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 356ac2cf
...@@ -533,6 +533,31 @@ config MMC_DW_IDMAC ...@@ -533,6 +533,31 @@ config MMC_DW_IDMAC
Designware Mobile Storage IP block. This disables the external DMA Designware Mobile Storage IP block. This disables the external DMA
interface. interface.
config MMC_DW_PLTFM
tristate "Synopsys Designware MCI Support as platform device"
depends on MMC_DW
default y
help
This selects the common helper functions support for Host Controller
Interface based platform driver. Please select this option if the IP
is present as a platform device. This is the common interface for the
Synopsys Designware IP.
If you have a controller with this interface, say Y or M here.
If unsure, say Y.
config MMC_DW_PCI
tristate "Synopsys Designware MCI support on PCI bus"
depends on MMC_DW && PCI
help
This selects the PCI bus for the Synopsys Designware Mobile Storage IP.
Select this option if the IP is present on PCI platform.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SH_MMCIF config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support" tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
......
...@@ -39,6 +39,8 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o ...@@ -39,6 +39,8 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_VUB300) += vub300.o
......
/*
* Synopsys DesignWare Multimedia Card PCI Interface driver
*
* Copyright (C) 2012 Vayavya Labs Pvt. 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, or
* (at your option) any later version.
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/dw_mmc.h>
#include "dw_mmc.h"
#define PCI_BAR_NO 2
#define COMPLETE_BAR 0
#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
/* Defining the Capabilities */
#define DW_MCI_CAPABILITIES (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |\
MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\
MMC_CAP_SDIO_IRQ)
static struct dw_mci_board pci_board_data = {
.num_slots = 1,
.caps = DW_MCI_CAPABILITIES,
.bus_hz = 33 * 1000 * 1000,
.detect_delay_ms = 200,
.fifo_depth = 32,
};
static int __devinit dw_mci_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *entries)
{
struct dw_mci *host;
int ret;
ret = pci_enable_device(pdev);
if (ret)
return ret;
if (pci_request_regions(pdev, "dw_mmc_pci")) {
ret = -ENODEV;
goto err_disable_dev;
}
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
if (!host) {
ret = -ENOMEM;
goto err_release;
}
host->irq = pdev->irq;
host->irq_flags = IRQF_SHARED;
host->dev = pdev->dev;
host->pdata = &pci_board_data;
host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
if (!host->regs) {
ret = -EIO;
goto err_unmap;
}
pci_set_drvdata(pdev, host);
ret = dw_mci_probe(host);
if (ret)
goto err_probe_failed;
return ret;
err_probe_failed:
pci_iounmap(pdev, host->regs);
err_unmap:
kfree(host);
err_release:
pci_release_regions(pdev);
err_disable_dev:
pci_disable_device(pdev);
return ret;
}
static void __devexit dw_mci_pci_remove(struct pci_dev *pdev)
{
struct dw_mci *host = pci_get_drvdata(pdev);
dw_mci_remove(host);
pci_set_drvdata(pdev, NULL);
pci_release_regions(pdev);
pci_iounmap(pdev, host->regs);
kfree(host);
pci_disable_device(pdev);
}
#ifdef CONFIG_PM_SLEEP
static int dw_mci_pci_suspend(struct device *dev)
{
int ret;
struct pci_dev *pdev = to_pci_dev(dev);
struct dw_mci *host = pci_get_drvdata(pdev);
ret = dw_mci_suspend(host);
return ret;
}
static int dw_mci_pci_resume(struct device *dev)
{
int ret;
struct pci_dev *pdev = to_pci_dev(dev);
struct dw_mci *host = pci_get_drvdata(pdev);
ret = dw_mci_resume(host);
return ret;
}
#else
#define dw_mci_pci_suspend NULL
#define dw_mci_pci_resume NULL
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume);
static DEFINE_PCI_DEVICE_TABLE(dw_mci_pci_id) = {
{ PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) },
{}
};
MODULE_DEVICE_TABLE(pci, dw_mci_pci_id);
static struct pci_driver dw_mci_pci_driver = {
.name = "dw_mmc_pci",
.id_table = dw_mci_pci_id,
.probe = dw_mci_pci_probe,
.remove = dw_mci_pci_remove,
.driver = {
.pm = &dw_mci_pci_pmops
},
};
static int __init dw_mci_init(void)
{
return pci_register_driver(&dw_mci_pci_driver);
}
static void __exit dw_mci_exit(void)
{
pci_unregister_driver(&dw_mci_pci_driver);
}
module_init(dw_mci_init);
module_exit(dw_mci_exit);
MODULE_DESCRIPTION("DW Multimedia Card PCI Interface driver");
MODULE_AUTHOR("Shashidhar Hiremath <shashidharh@vayavyalabs.com>");
MODULE_LICENSE("GPL v2");
/*
* Synopsys DesignWare Multimedia Card Interface driver
*
* Copyright (C) 2009 NXP Semiconductors
* Copyright (C) 2009, 2010 Imagination Technologies 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, or
* (at your option) any later version.
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/dw_mmc.h>
#include "dw_mmc.h"
static int dw_mci_pltfm_probe(struct platform_device *pdev)
{
struct dw_mci *host;
struct resource *regs;
int ret;
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
if (!host)
return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
ret = -ENXIO;
goto err_free;
}
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) {
ret = host->irq;
goto err_free;
}
host->dev = pdev->dev;
host->irq_flags = 0;
host->pdata = pdev->dev.platform_data;
ret = -ENOMEM;
host->regs = ioremap(regs->start, resource_size(regs));
if (!host->regs)
goto err_free;
platform_set_drvdata(pdev, host);
ret = dw_mci_probe(host);
if (ret)
goto err_out;
return ret;
err_out:
iounmap(host->regs);
err_free:
kfree(host);
return ret;
}
static int __exit dw_mci_pltfm_remove(struct platform_device *pdev)
{
struct dw_mci *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
dw_mci_remove(host);
iounmap(host->regs);
kfree(host);
return 0;
}
#ifdef CONFIG_PM_SLEEP
/*
* TODO: we should probably disable the clock to the card in the suspend path.
*/
static int dw_mci_pltfm_suspend(struct device *dev)
{
int ret;
struct dw_mci *host = dev_get_drvdata(dev);
ret = dw_mci_suspend(host);
if (ret)
return ret;
return 0;
}
static int dw_mci_pltfm_resume(struct device *dev)
{
int ret;
struct dw_mci *host = dev_get_drvdata(dev);
ret = dw_mci_resume(host);
if (ret)
return ret;
return 0;
}
#else
#define dw_mci_pltfm_suspend NULL
#define dw_mci_pltfm_resume NULL
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
static struct platform_driver dw_mci_pltfm_driver = {
.remove = __exit_p(dw_mci_pltfm_remove),
.driver = {
.name = "dw_mmc",
.pm = &dw_mci_pltfm_pmops,
},
};
static int __init dw_mci_init(void)
{
return platform_driver_probe(&dw_mci_pltfm_driver, dw_mci_pltfm_probe);
}
static void __exit dw_mci_exit(void)
{
platform_driver_unregister(&dw_mci_pltfm_driver);
}
module_init(dw_mci_init);
module_exit(dw_mci_exit);
MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
MODULE_AUTHOR("NXP Semiconductor VietNam");
MODULE_AUTHOR("Imagination Technologies Ltd");
MODULE_LICENSE("GPL v2");
...@@ -268,7 +268,7 @@ static void dw_mci_start_command(struct dw_mci *host, ...@@ -268,7 +268,7 @@ static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags) struct mmc_command *cmd, u32 cmd_flags)
{ {
host->cmd = cmd; host->cmd = cmd;
dev_vdbg(&host->pdev->dev, dev_vdbg(&host->dev,
"start command: ARGR=0x%08x CMDR=0x%08x\n", "start command: ARGR=0x%08x CMDR=0x%08x\n",
cmd->arg, cmd_flags); cmd->arg, cmd_flags);
...@@ -301,7 +301,7 @@ static void dw_mci_dma_cleanup(struct dw_mci *host) ...@@ -301,7 +301,7 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
if (data) if (data)
dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, dma_unmap_sg(&host->dev, data->sg, data->sg_len,
((data->flags & MMC_DATA_WRITE) ((data->flags & MMC_DATA_WRITE)
? DMA_TO_DEVICE : DMA_FROM_DEVICE)); ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
} }
...@@ -326,7 +326,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host) ...@@ -326,7 +326,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
{ {
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
dev_vdbg(&host->pdev->dev, "DMA complete\n"); dev_vdbg(&host->dev, "DMA complete\n");
host->dma_ops->cleanup(host); host->dma_ops->cleanup(host);
...@@ -462,10 +462,10 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) ...@@ -462,10 +462,10 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
else else
direction = DMA_TO_DEVICE; direction = DMA_TO_DEVICE;
sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, sg_len = dma_map_sg(&host->dev, data->sg, data->sg_len,
direction); direction);
dev_vdbg(&host->pdev->dev, dev_vdbg(&host->dev,
"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
sg_len); sg_len);
...@@ -821,12 +821,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) ...@@ -821,12 +821,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
slot = list_entry(host->queue.next, slot = list_entry(host->queue.next,
struct dw_mci_slot, queue_node); struct dw_mci_slot, queue_node);
list_del(&slot->queue_node); list_del(&slot->queue_node);
dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n", dev_vdbg(&host->dev, "list not empty: %s is next\n",
mmc_hostname(slot->mmc)); mmc_hostname(slot->mmc));
host->state = STATE_SENDING_CMD; host->state = STATE_SENDING_CMD;
dw_mci_start_request(host, slot); dw_mci_start_request(host, slot);
} else { } else {
dev_vdbg(&host->pdev->dev, "list empty\n"); dev_vdbg(&host->dev, "list empty\n");
host->state = STATE_IDLE; host->state = STATE_IDLE;
} }
...@@ -965,7 +965,7 @@ static void dw_mci_tasklet_func(unsigned long priv) ...@@ -965,7 +965,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
data->bytes_xfered = 0; data->bytes_xfered = 0;
data->error = -ETIMEDOUT; data->error = -ETIMEDOUT;
} else { } else {
dev_err(&host->pdev->dev, dev_err(&host->dev,
"data FIFO error " "data FIFO error "
"(status=%08x)\n", "(status=%08x)\n",
status); status);
...@@ -1682,7 +1682,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) ...@@ -1682,7 +1682,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
struct mmc_host *mmc; struct mmc_host *mmc;
struct dw_mci_slot *slot; struct dw_mci_slot *slot;
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev); mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->dev);
if (!mmc) if (!mmc)
return -ENOMEM; return -ENOMEM;
...@@ -1794,10 +1794,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) ...@@ -1794,10 +1794,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
static void dw_mci_init_dma(struct dw_mci *host) static void dw_mci_init_dma(struct dw_mci *host)
{ {
/* Alloc memory for sg translation */ /* Alloc memory for sg translation */
host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE, host->sg_cpu = dma_alloc_coherent(&host->dev, PAGE_SIZE,
&host->sg_dma, GFP_KERNEL); &host->sg_dma, GFP_KERNEL);
if (!host->sg_cpu) { if (!host->sg_cpu) {
dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n", dev_err(&host->dev, "%s: could not alloc DMA memory\n",
__func__); __func__);
goto no_dma; goto no_dma;
} }
...@@ -1805,7 +1805,7 @@ static void dw_mci_init_dma(struct dw_mci *host) ...@@ -1805,7 +1805,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
/* Determine which DMA interface to use */ /* Determine which DMA interface to use */
#ifdef CONFIG_MMC_DW_IDMAC #ifdef CONFIG_MMC_DW_IDMAC
host->dma_ops = &dw_mci_idmac_ops; host->dma_ops = &dw_mci_idmac_ops;
dev_info(&host->pdev->dev, "Using internal DMA controller.\n"); dev_info(&host->dev, "Using internal DMA controller.\n");
#endif #endif
if (!host->dma_ops) if (!host->dma_ops)
...@@ -1813,12 +1813,12 @@ static void dw_mci_init_dma(struct dw_mci *host) ...@@ -1813,12 +1813,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
if (host->dma_ops->init) { if (host->dma_ops->init) {
if (host->dma_ops->init(host)) { if (host->dma_ops->init(host)) {
dev_err(&host->pdev->dev, "%s: Unable to initialize " dev_err(&host->dev, "%s: Unable to initialize "
"DMA Controller.\n", __func__); "DMA Controller.\n", __func__);
goto no_dma; goto no_dma;
} }
} else { } else {
dev_err(&host->pdev->dev, "DMA initialization not found.\n"); dev_err(&host->dev, "DMA initialization not found.\n");
goto no_dma; goto no_dma;
} }
...@@ -1826,7 +1826,7 @@ static void dw_mci_init_dma(struct dw_mci *host) ...@@ -1826,7 +1826,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
return; return;
no_dma: no_dma:
dev_info(&host->pdev->dev, "Using PIO mode.\n"); dev_info(&host->dev, "Using PIO mode.\n");
host->use_dma = 0; host->use_dma = 0;
return; return;
} }
...@@ -1852,61 +1852,37 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host) ...@@ -1852,61 +1852,37 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
return false; return false;
} }
static int dw_mci_probe(struct platform_device *pdev) int dw_mci_probe(struct dw_mci *host)
{ {
struct dw_mci *host; int width, i, ret = 0;
struct resource *regs;
struct dw_mci_board *pdata;
int irq, ret, i, width;
u32 fifo_size; u32 fifo_size;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->pdata || !host->pdata->init) {
if (!regs) dev_err(&host->dev,
return -ENXIO;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
if (!host)
return -ENOMEM;
host->pdev = pdev;
host->pdata = pdata = pdev->dev.platform_data;
if (!pdata || !pdata->init) {
dev_err(&pdev->dev,
"Platform data must supply init function\n"); "Platform data must supply init function\n");
ret = -ENODEV; return -ENODEV;
goto err_freehost;
} }
if (!pdata->select_slot && pdata->num_slots > 1) { if (!host->pdata->select_slot && host->pdata->num_slots > 1) {
dev_err(&pdev->dev, dev_err(&host->dev,
"Platform data must supply select_slot function\n"); "Platform data must supply select_slot function\n");
ret = -ENODEV; return -ENODEV;
goto err_freehost;
} }
if (!pdata->bus_hz) { if (!host->pdata->bus_hz) {
dev_err(&pdev->dev, dev_err(&host->dev,
"Platform data must supply bus speed\n"); "Platform data must supply bus speed\n");
ret = -ENODEV; return -ENODEV;
goto err_freehost;
} }
host->bus_hz = pdata->bus_hz; host->bus_hz = host->pdata->bus_hz;
host->quirks = pdata->quirks; host->quirks = host->pdata->quirks;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
INIT_LIST_HEAD(&host->queue); INIT_LIST_HEAD(&host->queue);
ret = -ENOMEM;
host->regs = ioremap(regs->start, resource_size(regs));
if (!host->regs)
goto err_freehost;
host->dma_ops = pdata->dma_ops; host->dma_ops = host->pdata->dma_ops;
dw_mci_init_dma(host); dw_mci_init_dma(host);
/* /*
...@@ -1936,7 +1912,7 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -1936,7 +1912,7 @@ static int dw_mci_probe(struct platform_device *pdev)
} }
/* Reset all blocks */ /* Reset all blocks */
if (!mci_wait_reset(&pdev->dev, host)) { if (!mci_wait_reset(&host->dev, host)) {
ret = -ENODEV; ret = -ENODEV;
goto err_dmaunmap; goto err_dmaunmap;
} }
...@@ -1979,13 +1955,10 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -1979,13 +1955,10 @@ static int dw_mci_probe(struct platform_device *pdev)
if (!dw_mci_card_workqueue) if (!dw_mci_card_workqueue)
goto err_dmaunmap; goto err_dmaunmap;
INIT_WORK(&host->card_work, dw_mci_work_routine_card); INIT_WORK(&host->card_work, dw_mci_work_routine_card);
ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
if (ret) if (ret)
goto err_workqueue; goto err_workqueue;
platform_set_drvdata(pdev, host);
if (host->pdata->num_slots) if (host->pdata->num_slots)
host->num_slots = host->pdata->num_slots; host->num_slots = host->pdata->num_slots;
else else
...@@ -2005,7 +1978,7 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2005,7 +1978,7 @@ static int dw_mci_probe(struct platform_device *pdev)
* Need to check the version-id and set data-offset for DATA register. * Need to check the version-id and set data-offset for DATA register.
*/ */
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID)); host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
dev_info(&pdev->dev, "Version ID is %04x\n", host->verid); dev_info(&host->dev, "Version ID is %04x\n", host->verid);
if (host->verid < DW_MMC_240A) if (host->verid < DW_MMC_240A)
host->data_offset = DATA_OFFSET; host->data_offset = DATA_OFFSET;
...@@ -2022,12 +1995,12 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2022,12 +1995,12 @@ static int dw_mci_probe(struct platform_device *pdev)
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
dev_info(&pdev->dev, "DW MMC controller at irq %d, " dev_info(&host->dev, "DW MMC controller at irq %d, "
"%d bit host data width, " "%d bit host data width, "
"%u deep fifo\n", "%u deep fifo\n",
irq, width, fifo_size); host->irq, width, fifo_size);
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n"); dev_info(&host->dev, "Internal DMAC interrupt fix enabled.\n");
return 0; return 0;
...@@ -2038,7 +2011,7 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2038,7 +2011,7 @@ static int dw_mci_probe(struct platform_device *pdev)
dw_mci_cleanup_slot(host->slot[i], i); dw_mci_cleanup_slot(host->slot[i], i);
i--; i--;
} }
free_irq(irq, host); free_irq(host->irq, host);
err_workqueue: err_workqueue:
destroy_workqueue(dw_mci_card_workqueue); destroy_workqueue(dw_mci_card_workqueue);
...@@ -2046,33 +2019,26 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2046,33 +2019,26 @@ static int dw_mci_probe(struct platform_device *pdev)
err_dmaunmap: err_dmaunmap:
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host); host->dma_ops->exit(host);
dma_free_coherent(&host->pdev->dev, PAGE_SIZE, dma_free_coherent(&host->dev, PAGE_SIZE,
host->sg_cpu, host->sg_dma); host->sg_cpu, host->sg_dma);
iounmap(host->regs);
if (host->vmmc) { if (host->vmmc) {
regulator_disable(host->vmmc); regulator_disable(host->vmmc);
regulator_put(host->vmmc); regulator_put(host->vmmc);
} }
err_freehost:
kfree(host);
return ret; return ret;
} }
EXPORT_SYMBOL(dw_mci_probe);
static int __exit dw_mci_remove(struct platform_device *pdev) void dw_mci_remove(struct dw_mci *host)
{ {
struct dw_mci *host = platform_get_drvdata(pdev);
int i; int i;
mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
platform_set_drvdata(pdev, NULL);
for (i = 0; i < host->num_slots; i++) { for (i = 0; i < host->num_slots; i++) {
dev_dbg(&pdev->dev, "remove slot %d\n", i); dev_dbg(&host->dev, "remove slot %d\n", i);
if (host->slot[i]) if (host->slot[i])
dw_mci_cleanup_slot(host->slot[i], i); dw_mci_cleanup_slot(host->slot[i], i);
} }
...@@ -2081,9 +2047,9 @@ static int __exit dw_mci_remove(struct platform_device *pdev) ...@@ -2081,9 +2047,9 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
mci_writel(host, CLKENA, 0); mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0); mci_writel(host, CLKSRC, 0);
free_irq(platform_get_irq(pdev, 0), host); free_irq(host->irq, host);
destroy_workqueue(dw_mci_card_workqueue); destroy_workqueue(dw_mci_card_workqueue);
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host); host->dma_ops->exit(host);
...@@ -2093,20 +2059,18 @@ static int __exit dw_mci_remove(struct platform_device *pdev) ...@@ -2093,20 +2059,18 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
regulator_put(host->vmmc); regulator_put(host->vmmc);
} }
iounmap(host->regs);
kfree(host);
return 0;
} }
EXPORT_SYMBOL(dw_mci_remove);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/* /*
* TODO: we should probably disable the clock to the card in the suspend path. * TODO: we should probably disable the clock to the card in the suspend path.
*/ */
static int dw_mci_suspend(struct device *dev) int dw_mci_suspend(struct dw_mci *host)
{ {
int i, ret; int i, ret = 0;
struct dw_mci *host = dev_get_drvdata(dev);
for (i = 0; i < host->num_slots; i++) { for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i]; struct dw_mci_slot *slot = host->slot[i];
...@@ -2128,11 +2092,11 @@ static int dw_mci_suspend(struct device *dev) ...@@ -2128,11 +2092,11 @@ static int dw_mci_suspend(struct device *dev)
return 0; return 0;
} }
EXPORT_SYMBOL(dw_mci_suspend);
static int dw_mci_resume(struct device *dev) int dw_mci_resume(struct dw_mci *host)
{ {
int i, ret; int i, ret;
struct dw_mci *host = dev_get_drvdata(dev);
if (host->vmmc) if (host->vmmc)
regulator_enable(host->vmmc); regulator_enable(host->vmmc);
...@@ -2140,7 +2104,7 @@ static int dw_mci_resume(struct device *dev) ...@@ -2140,7 +2104,7 @@ static int dw_mci_resume(struct device *dev)
if (host->dma_ops->init) if (host->dma_ops->init)
host->dma_ops->init(host); host->dma_ops->init(host);
if (!mci_wait_reset(dev, host)) { if (!mci_wait_reset(&host->dev, host)) {
ret = -ENODEV; ret = -ENODEV;
return ret; return ret;
} }
...@@ -2162,32 +2126,19 @@ static int dw_mci_resume(struct device *dev) ...@@ -2162,32 +2126,19 @@ static int dw_mci_resume(struct device *dev)
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
return 0; return 0;
} }
#else EXPORT_SYMBOL(dw_mci_resume);
#define dw_mci_suspend NULL
#define dw_mci_resume NULL
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume);
static struct platform_driver dw_mci_driver = {
.remove = __exit_p(dw_mci_remove),
.driver = {
.name = "dw_mmc",
.pm = &dw_mci_pmops,
},
};
static int __init dw_mci_init(void) static int __init dw_mci_init(void)
{ {
return platform_driver_probe(&dw_mci_driver, dw_mci_probe); printk(KERN_INFO "Synopsys Designware Multimedia Card Interface Driver");
return 0;
} }
static void __exit dw_mci_exit(void) static void __exit dw_mci_exit(void)
{ {
platform_driver_unregister(&dw_mci_driver);
} }
module_init(dw_mci_init); module_init(dw_mci_init);
......
...@@ -175,4 +175,11 @@ ...@@ -175,4 +175,11 @@
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
#endif #endif
extern int dw_mci_probe(struct dw_mci *host);
extern void dw_mci_remove(struct dw_mci *host);
#ifdef CONFIG_PM
extern int dw_mci_suspend(struct dw_mci *host);
extern int dw_mci_resume(struct dw_mci *host);
#endif
#endif /* _DW_MMC_H_ */ #endif /* _DW_MMC_H_ */
...@@ -76,7 +76,7 @@ struct mmc_data; ...@@ -76,7 +76,7 @@ struct mmc_data;
* @num_slots: Number of slots available. * @num_slots: Number of slots available.
* @verid: Denote Version ID. * @verid: Denote Version ID.
* @data_offset: Set the offset of DATA register according to VERID. * @data_offset: Set the offset of DATA register according to VERID.
* @pdev: Platform device associated with the MMC controller. * @dev: Device associated with the MMC controller.
* @pdata: Platform data associated with the MMC controller. * @pdata: Platform data associated with the MMC controller.
* @slot: Slots sharing this MMC controller. * @slot: Slots sharing this MMC controller.
* @fifo_depth: depth of FIFO. * @fifo_depth: depth of FIFO.
...@@ -87,6 +87,8 @@ struct mmc_data; ...@@ -87,6 +87,8 @@ struct mmc_data;
* @push_data: Pointer to FIFO push function. * @push_data: Pointer to FIFO push function.
* @pull_data: Pointer to FIFO pull function. * @pull_data: Pointer to FIFO pull function.
* @quirks: Set of quirks that apply to specific versions of the IP. * @quirks: Set of quirks that apply to specific versions of the IP.
* @irq_flags: The flags to be passed to request_irq.
* @irq: The irq value to be passed to request_irq.
* *
* Locking * Locking
* ======= * =======
...@@ -153,7 +155,7 @@ struct dw_mci { ...@@ -153,7 +155,7 @@ struct dw_mci {
u32 fifoth_val; u32 fifoth_val;
u16 verid; u16 verid;
u16 data_offset; u16 data_offset;
struct platform_device *pdev; struct device dev;
struct dw_mci_board *pdata; struct dw_mci_board *pdata;
struct dw_mci_slot *slot[MAX_MCI_SLOTS]; struct dw_mci_slot *slot[MAX_MCI_SLOTS];
...@@ -174,6 +176,8 @@ struct dw_mci { ...@@ -174,6 +176,8 @@ struct dw_mci {
u32 quirks; u32 quirks;
struct regulator *vmmc; /* Power regulator */ struct regulator *vmmc; /* Power regulator */
unsigned long irq_flags; /* IRQ flags */
unsigned int irq;
}; };
/* DMA ops for Internal/External DMAC interface */ /* DMA ops for Internal/External DMAC interface */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment