|
@@ -15,7 +15,7 @@
|
|
|
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/highmem.h>
|
|
|
-#include <linux/pci.h>
|
|
|
+#include <linux/io.h>
|
|
|
#include <linux/dma-mapping.h>
|
|
|
#include <linux/scatterlist.h>
|
|
|
|
|
@@ -32,135 +32,6 @@
|
|
|
|
|
|
static unsigned int debug_quirks = 0;
|
|
|
|
|
|
-/*
|
|
|
- * Different quirks to handle when the hardware deviates from a strict
|
|
|
- * interpretation of the SDHCI specification.
|
|
|
- */
|
|
|
-
|
|
|
-/* Controller doesn't honor resets unless we touch the clock register */
|
|
|
-#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
|
|
|
-/* Controller has bad caps bits, but really supports DMA */
|
|
|
-#define SDHCI_QUIRK_FORCE_DMA (1<<1)
|
|
|
-/* Controller doesn't like to be reset when there is no card inserted. */
|
|
|
-#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
|
|
|
-/* Controller doesn't like clearing the power reg before a change */
|
|
|
-#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
|
|
|
-/* Controller has flaky internal state so reset it on each ios change */
|
|
|
-#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
|
|
|
-/* Controller has an unusable DMA engine */
|
|
|
-#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
|
|
|
-/* Controller can only DMA from 32-bit aligned addresses */
|
|
|
-#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<6)
|
|
|
-/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
|
|
|
-#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7)
|
|
|
-/* Controller needs to be reset after each request to stay stable */
|
|
|
-#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8)
|
|
|
-/* Controller needs voltage and power writes to happen separately */
|
|
|
-#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9)
|
|
|
-/* Controller has an off-by-one issue with timeout value */
|
|
|
-#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL (1<<10)
|
|
|
-
|
|
|
-static const struct pci_device_id pci_ids[] __devinitdata = {
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_RICOH,
|
|
|
- .device = PCI_DEVICE_ID_RICOH_R5C822,
|
|
|
- .subvendor = PCI_VENDOR_ID_IBM,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_CLOCK_BEFORE_RESET |
|
|
|
- SDHCI_QUIRK_FORCE_DMA,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_RICOH,
|
|
|
- .device = PCI_DEVICE_ID_RICOH_R5C822,
|
|
|
- .subvendor = PCI_VENDOR_ID_SAMSUNG,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_FORCE_DMA |
|
|
|
- SDHCI_QUIRK_NO_CARD_NO_RESET,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_RICOH,
|
|
|
- .device = PCI_DEVICE_ID_RICOH_R5C822,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_FORCE_DMA,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_TI,
|
|
|
- .device = PCI_DEVICE_ID_TI_XX21_XX11_SD,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_FORCE_DMA,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_ENE,
|
|
|
- .device = PCI_DEVICE_ID_ENE_CB712_SD,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
|
|
- SDHCI_QUIRK_BROKEN_DMA,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_ENE,
|
|
|
- .device = PCI_DEVICE_ID_ENE_CB712_SD_2,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
|
|
- SDHCI_QUIRK_BROKEN_DMA,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_ENE,
|
|
|
- .device = PCI_DEVICE_ID_ENE_CB714_SD,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
|
|
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
|
|
|
- SDHCI_QUIRK_BROKEN_DMA,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_ENE,
|
|
|
- .device = PCI_DEVICE_ID_ENE_CB714_SD_2,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
|
|
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
|
|
|
- SDHCI_QUIRK_BROKEN_DMA,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_MARVELL,
|
|
|
- .device = PCI_DEVICE_ID_MARVELL_CAFE_SD,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
|
|
|
- SDHCI_QUIRK_INCR_TIMEOUT_CONTROL,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- .vendor = PCI_VENDOR_ID_JMICRON,
|
|
|
- .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD,
|
|
|
- .subvendor = PCI_ANY_ID,
|
|
|
- .subdevice = PCI_ANY_ID,
|
|
|
- .driver_data = SDHCI_QUIRK_32BIT_DMA_ADDR |
|
|
|
- SDHCI_QUIRK_32BIT_DMA_SIZE |
|
|
|
- SDHCI_QUIRK_RESET_AFTER_REQUEST,
|
|
|
- },
|
|
|
-
|
|
|
- { /* Generic SD host controller */
|
|
|
- PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
|
|
|
- },
|
|
|
-
|
|
|
- { /* end: all zeroes */ },
|
|
|
-};
|
|
|
-
|
|
|
-MODULE_DEVICE_TABLE(pci, pci_ids);
|
|
|
-
|
|
|
static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
|
|
|
static void sdhci_finish_data(struct sdhci_host *);
|
|
|
|
|
@@ -215,7 +86,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
|
|
|
{
|
|
|
unsigned long timeout;
|
|
|
|
|
|
- if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
|
|
+ if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
|
|
if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
|
|
|
SDHCI_CARD_PRESENT))
|
|
|
return;
|
|
@@ -488,7 +359,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
|
|
|
* Compensate for an off-by-one error in the CaFe hardware; otherwise,
|
|
|
* a too-small count gives us interrupt timeouts.
|
|
|
*/
|
|
|
- if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL))
|
|
|
+ if ((host->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL))
|
|
|
count++;
|
|
|
|
|
|
if (count >= 0xF) {
|
|
@@ -503,7 +374,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
|
|
|
host->flags |= SDHCI_REQ_USE_DMA;
|
|
|
|
|
|
if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
|
|
|
- (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
|
|
|
+ (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
|
|
|
((data->blksz * data->blocks) & 0x3))) {
|
|
|
DBG("Reverting to PIO because of transfer size (%d)\n",
|
|
|
data->blksz * data->blocks);
|
|
@@ -515,7 +386,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
|
|
|
* translation to device address space.
|
|
|
*/
|
|
|
if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
|
|
|
- (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
|
|
|
+ (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
|
|
|
(data->sg->offset & 0x3))) {
|
|
|
DBG("Reverting to PIO because of bad alignment\n");
|
|
|
host->flags &= ~SDHCI_REQ_USE_DMA;
|
|
@@ -524,11 +395,13 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
|
|
|
if (host->flags & SDHCI_REQ_USE_DMA) {
|
|
|
int count;
|
|
|
|
|
|
- count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len,
|
|
|
- (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE);
|
|
|
- BUG_ON(count != 1);
|
|
|
+ count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
+ (data->flags & MMC_DATA_READ) ?
|
|
|
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
|
|
+ WARN_ON(count != 1);
|
|
|
|
|
|
- writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS);
|
|
|
+ writel(sg_dma_address(data->sg),
|
|
|
+ host->ioaddr + SDHCI_DMA_ADDRESS);
|
|
|
} else {
|
|
|
host->cur_sg = data->sg;
|
|
|
host->num_sg = data->sg_len;
|
|
@@ -574,8 +447,9 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|
|
host->data = NULL;
|
|
|
|
|
|
if (host->flags & SDHCI_REQ_USE_DMA) {
|
|
|
- pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len,
|
|
|
- (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE);
|
|
|
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
+ (data->flags & MMC_DATA_READ) ?
|
|
|
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -770,7 +644,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
|
|
* Spec says that we should clear the power reg before setting
|
|
|
* a new value. Some controllers don't seem to like this though.
|
|
|
*/
|
|
|
- if (!(host->chip->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
|
|
|
+ if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
|
|
|
writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
|
|
|
|
|
|
pwr = SDHCI_POWER_ON;
|
|
@@ -795,7 +669,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
|
|
* At least the CaFe chip gets confused if we set the voltage
|
|
|
* and set turn on power at the same time, so set the voltage first.
|
|
|
*/
|
|
|
- if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
|
|
|
+ if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
|
|
|
writeb(pwr & ~SDHCI_POWER_ON,
|
|
|
host->ioaddr + SDHCI_POWER_CONTROL);
|
|
|
|
|
@@ -883,7 +757,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
* signalling timeout and CRC errors even on CMD0. Resetting
|
|
|
* it on each ios seems to solve the problem.
|
|
|
*/
|
|
|
- if(host->chip->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
|
|
+ if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
|
|
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
|
|
|
|
|
mmiowb();
|
|
@@ -994,10 +868,10 @@ static void sdhci_tasklet_finish(unsigned long param)
|
|
|
if (mrq->cmd->error ||
|
|
|
(mrq->data && (mrq->data->error ||
|
|
|
(mrq->data->stop && mrq->data->stop->error))) ||
|
|
|
- (host->chip->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
|
|
|
+ (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
|
|
|
|
|
|
/* Some controllers need this kick or reset won't work here */
|
|
|
- if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
|
|
|
+ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
|
|
|
unsigned int clock;
|
|
|
|
|
|
/* This is to force an update */
|
|
@@ -1229,165 +1103,90 @@ out:
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
|
|
-static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state)
|
|
|
+int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
|
|
|
{
|
|
|
- struct sdhci_chip *chip;
|
|
|
- int i, ret;
|
|
|
-
|
|
|
- chip = pci_get_drvdata(pdev);
|
|
|
- if (!chip)
|
|
|
- return 0;
|
|
|
-
|
|
|
- DBG("Suspending...\n");
|
|
|
-
|
|
|
- for (i = 0;i < chip->num_slots;i++) {
|
|
|
- if (!chip->hosts[i])
|
|
|
- continue;
|
|
|
- ret = mmc_suspend_host(chip->hosts[i]->mmc, state);
|
|
|
- if (ret) {
|
|
|
- for (i--;i >= 0;i--)
|
|
|
- mmc_resume_host(chip->hosts[i]->mmc);
|
|
|
- return ret;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- pci_save_state(pdev);
|
|
|
- pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
|
|
|
+ int ret;
|
|
|
|
|
|
- for (i = 0;i < chip->num_slots;i++) {
|
|
|
- if (!chip->hosts[i])
|
|
|
- continue;
|
|
|
- free_irq(chip->hosts[i]->irq, chip->hosts[i]);
|
|
|
- }
|
|
|
+ ret = mmc_suspend_host(host->mmc, state);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- pci_disable_device(pdev);
|
|
|
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
|
|
+ free_irq(host->irq, host);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int sdhci_resume (struct pci_dev *pdev)
|
|
|
-{
|
|
|
- struct sdhci_chip *chip;
|
|
|
- int i, ret;
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_suspend_host);
|
|
|
|
|
|
- chip = pci_get_drvdata(pdev);
|
|
|
- if (!chip)
|
|
|
- return 0;
|
|
|
+int sdhci_resume_host(struct sdhci_host *host)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
|
|
|
- DBG("Resuming...\n");
|
|
|
+ if (host->flags & SDHCI_USE_DMA) {
|
|
|
+ if (host->ops->enable_dma)
|
|
|
+ host->ops->enable_dma(host);
|
|
|
+ }
|
|
|
|
|
|
- pci_set_power_state(pdev, PCI_D0);
|
|
|
- pci_restore_state(pdev);
|
|
|
- ret = pci_enable_device(pdev);
|
|
|
+ ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
|
|
|
+ mmc_hostname(host->mmc), host);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- for (i = 0;i < chip->num_slots;i++) {
|
|
|
- if (!chip->hosts[i])
|
|
|
- continue;
|
|
|
- if (chip->hosts[i]->flags & SDHCI_USE_DMA)
|
|
|
- pci_set_master(pdev);
|
|
|
- ret = request_irq(chip->hosts[i]->irq, sdhci_irq,
|
|
|
- IRQF_SHARED, mmc_hostname(chip->hosts[i]->mmc),
|
|
|
- chip->hosts[i]);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- sdhci_init(chip->hosts[i]);
|
|
|
- mmiowb();
|
|
|
- ret = mmc_resume_host(chip->hosts[i]->mmc);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ sdhci_init(host);
|
|
|
+ mmiowb();
|
|
|
+
|
|
|
+ ret = mmc_resume_host(host->mmc);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#else /* CONFIG_PM */
|
|
|
-
|
|
|
-#define sdhci_suspend NULL
|
|
|
-#define sdhci_resume NULL
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_resume_host);
|
|
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
|
/*****************************************************************************\
|
|
|
* *
|
|
|
- * Device probing/removal *
|
|
|
+ * Device allocation/registration *
|
|
|
* *
|
|
|
\*****************************************************************************/
|
|
|
|
|
|
-static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
|
|
|
+struct sdhci_host *sdhci_alloc_host(struct device *dev,
|
|
|
+ size_t priv_size)
|
|
|
{
|
|
|
- int ret;
|
|
|
- unsigned int version;
|
|
|
- struct sdhci_chip *chip;
|
|
|
struct mmc_host *mmc;
|
|
|
struct sdhci_host *host;
|
|
|
|
|
|
- u8 first_bar;
|
|
|
- unsigned int caps;
|
|
|
-
|
|
|
- chip = pci_get_drvdata(pdev);
|
|
|
- BUG_ON(!chip);
|
|
|
-
|
|
|
- ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
|
|
|
+ WARN_ON(dev == NULL);
|
|
|
|
|
|
- if (first_bar > 5) {
|
|
|
- printk(KERN_ERR DRIVER_NAME ": Invalid first BAR. Aborting.\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
- if (!(pci_resource_flags(pdev, first_bar + slot) & IORESOURCE_MEM)) {
|
|
|
- printk(KERN_ERR DRIVER_NAME ": BAR is not iomem. Aborting.\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
- if (pci_resource_len(pdev, first_bar + slot) != 0x100) {
|
|
|
- printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. "
|
|
|
- "You may experience problems.\n");
|
|
|
- }
|
|
|
-
|
|
|
- if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
|
|
|
- printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
- if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
|
|
|
- printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
- mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev);
|
|
|
+ mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
|
|
|
if (!mmc)
|
|
|
- return -ENOMEM;
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
host = mmc_priv(mmc);
|
|
|
host->mmc = mmc;
|
|
|
|
|
|
- host->chip = chip;
|
|
|
- chip->hosts[slot] = host;
|
|
|
+ return host;
|
|
|
+}
|
|
|
|
|
|
- host->bar = first_bar + slot;
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_alloc_host);
|
|
|
|
|
|
- host->addr = pci_resource_start(pdev, host->bar);
|
|
|
- host->irq = pdev->irq;
|
|
|
+int sdhci_add_host(struct sdhci_host *host)
|
|
|
+{
|
|
|
+ struct mmc_host *mmc;
|
|
|
+ unsigned int caps;
|
|
|
+ unsigned int version;
|
|
|
+ int ret;
|
|
|
|
|
|
- DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq);
|
|
|
+ WARN_ON(host == NULL);
|
|
|
+ if (host == NULL)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- ret = pci_request_region(pdev, host->bar, mmc_hostname(mmc));
|
|
|
- if (ret)
|
|
|
- goto free;
|
|
|
+ mmc = host->mmc;
|
|
|
|
|
|
- host->ioaddr = ioremap_nocache(host->addr,
|
|
|
- pci_resource_len(pdev, host->bar));
|
|
|
- if (!host->ioaddr) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto release;
|
|
|
- }
|
|
|
+ if (debug_quirks)
|
|
|
+ host->quirks = debug_quirks;
|
|
|
|
|
|
sdhci_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
@@ -1401,46 +1200,40 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
|
|
|
|
|
|
caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
|
|
|
|
|
|
- if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
|
|
|
+ if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
|
|
|
host->flags |= SDHCI_USE_DMA;
|
|
|
else if (!(caps & SDHCI_CAN_DO_DMA))
|
|
|
DBG("Controller doesn't have DMA capability\n");
|
|
|
else
|
|
|
host->flags |= SDHCI_USE_DMA;
|
|
|
|
|
|
- if ((chip->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
|
|
|
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
|
|
|
(host->flags & SDHCI_USE_DMA)) {
|
|
|
DBG("Disabling DMA as it is marked broken\n");
|
|
|
host->flags &= ~SDHCI_USE_DMA;
|
|
|
}
|
|
|
|
|
|
- if (((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
|
|
|
- (host->flags & SDHCI_USE_DMA)) {
|
|
|
- printk(KERN_WARNING "%s: Will use DMA "
|
|
|
- "mode even though HW doesn't fully "
|
|
|
- "claim to support it.\n", mmc_hostname(mmc));
|
|
|
- }
|
|
|
-
|
|
|
if (host->flags & SDHCI_USE_DMA) {
|
|
|
- if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
|
|
|
- printk(KERN_WARNING "%s: No suitable DMA available. "
|
|
|
- "Falling back to PIO.\n", mmc_hostname(mmc));
|
|
|
- host->flags &= ~SDHCI_USE_DMA;
|
|
|
+ if (host->ops->enable_dma) {
|
|
|
+ if (host->ops->enable_dma(host)) {
|
|
|
+ printk(KERN_WARNING "%s: No suitable DMA "
|
|
|
+ "available. Falling back to PIO.\n",
|
|
|
+ mmc_hostname(mmc));
|
|
|
+ host->flags &= ~SDHCI_USE_DMA;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (host->flags & SDHCI_USE_DMA)
|
|
|
- pci_set_master(pdev);
|
|
|
- else /* XXX: Hack to get MMC layer to avoid highmem */
|
|
|
- pdev->dma_mask = 0;
|
|
|
+ /* XXX: Hack to get MMC layer to avoid highmem */
|
|
|
+ if (!(host->flags & SDHCI_USE_DMA))
|
|
|
+ mmc_dev(host->mmc)->dma_mask = 0;
|
|
|
|
|
|
host->max_clk =
|
|
|
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
|
|
|
if (host->max_clk == 0) {
|
|
|
printk(KERN_ERR "%s: Hardware doesn't specify base clock "
|
|
|
"frequency.\n", mmc_hostname(mmc));
|
|
|
- ret = -ENODEV;
|
|
|
- goto unmap;
|
|
|
+ return -ENODEV;
|
|
|
}
|
|
|
host->max_clk *= 1000000;
|
|
|
|
|
@@ -1449,8 +1242,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
|
|
|
if (host->timeout_clk == 0) {
|
|
|
printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
|
|
|
"frequency.\n", mmc_hostname(mmc));
|
|
|
- ret = -ENODEV;
|
|
|
- goto unmap;
|
|
|
+ return -ENODEV;
|
|
|
}
|
|
|
if (caps & SDHCI_TIMEOUT_CLK_UNIT)
|
|
|
host->timeout_clk *= 1000;
|
|
@@ -1477,8 +1269,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
|
|
|
if (mmc->ocr_avail == 0) {
|
|
|
printk(KERN_ERR "%s: Hardware doesn't report any "
|
|
|
"support voltages.\n", mmc_hostname(mmc));
|
|
|
- ret = -ENODEV;
|
|
|
- goto unmap;
|
|
|
+ return -ENODEV;
|
|
|
}
|
|
|
|
|
|
spin_lock_init(&host->lock);
|
|
@@ -1548,7 +1339,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
|
|
|
host->led.default_trigger = mmc_hostname(mmc);
|
|
|
host->led.brightness_set = sdhci_led_control;
|
|
|
|
|
|
- ret = led_classdev_register(&pdev->dev, &host->led);
|
|
|
+ ret = led_classdev_register(mmc_dev(mmc), &host->led);
|
|
|
if (ret)
|
|
|
goto reset;
|
|
|
#endif
|
|
@@ -1557,8 +1348,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
|
|
|
|
|
|
mmc_add_host(mmc);
|
|
|
|
|
|
- printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n",
|
|
|
- mmc_hostname(mmc), host->addr, host->irq,
|
|
|
+ printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n",
|
|
|
+ mmc_hostname(mmc), host->hw_name, mmc_dev(mmc)->bus_id,
|
|
|
(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
|
|
|
|
|
|
return 0;
|
|
@@ -1571,29 +1362,15 @@ reset:
|
|
|
untasklet:
|
|
|
tasklet_kill(&host->card_tasklet);
|
|
|
tasklet_kill(&host->finish_tasklet);
|
|
|
-unmap:
|
|
|
- iounmap(host->ioaddr);
|
|
|
-release:
|
|
|
- pci_release_region(pdev, host->bar);
|
|
|
-free:
|
|
|
- mmc_free_host(mmc);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void sdhci_remove_slot(struct pci_dev *pdev, int slot)
|
|
|
-{
|
|
|
- struct sdhci_chip *chip;
|
|
|
- struct mmc_host *mmc;
|
|
|
- struct sdhci_host *host;
|
|
|
-
|
|
|
- chip = pci_get_drvdata(pdev);
|
|
|
- host = chip->hosts[slot];
|
|
|
- mmc = host->mmc;
|
|
|
-
|
|
|
- chip->hosts[slot] = NULL;
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_add_host);
|
|
|
|
|
|
- mmc_remove_host(mmc);
|
|
|
+void sdhci_remove_host(struct sdhci_host *host)
|
|
|
+{
|
|
|
+ mmc_remove_host(host->mmc);
|
|
|
|
|
|
#ifdef CONFIG_LEDS_CLASS
|
|
|
led_classdev_unregister(&host->led);
|
|
@@ -1607,107 +1384,16 @@ static void sdhci_remove_slot(struct pci_dev *pdev, int slot)
|
|
|
|
|
|
tasklet_kill(&host->card_tasklet);
|
|
|
tasklet_kill(&host->finish_tasklet);
|
|
|
-
|
|
|
- iounmap(host->ioaddr);
|
|
|
-
|
|
|
- pci_release_region(pdev, host->bar);
|
|
|
-
|
|
|
- mmc_free_host(mmc);
|
|
|
}
|
|
|
|
|
|
-static int __devinit sdhci_probe(struct pci_dev *pdev,
|
|
|
- const struct pci_device_id *ent)
|
|
|
-{
|
|
|
- int ret, i;
|
|
|
- u8 slots, rev;
|
|
|
- struct sdhci_chip *chip;
|
|
|
-
|
|
|
- BUG_ON(pdev == NULL);
|
|
|
- BUG_ON(ent == NULL);
|
|
|
-
|
|
|
- pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
|
|
|
-
|
|
|
- printk(KERN_INFO DRIVER_NAME
|
|
|
- ": SDHCI controller found at %s [%04x:%04x] (rev %x)\n",
|
|
|
- pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
|
|
|
- (int)rev);
|
|
|
-
|
|
|
- ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
|
|
|
- DBG("found %d slot(s)\n", slots);
|
|
|
- if (slots == 0)
|
|
|
- return -ENODEV;
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_remove_host);
|
|
|
|
|
|
- ret = pci_enable_device(pdev);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- chip = kzalloc(sizeof(struct sdhci_chip) +
|
|
|
- sizeof(struct sdhci_host*) * slots, GFP_KERNEL);
|
|
|
- if (!chip) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- chip->pdev = pdev;
|
|
|
- chip->quirks = ent->driver_data;
|
|
|
-
|
|
|
- if (debug_quirks)
|
|
|
- chip->quirks = debug_quirks;
|
|
|
-
|
|
|
- chip->num_slots = slots;
|
|
|
- pci_set_drvdata(pdev, chip);
|
|
|
-
|
|
|
- for (i = 0;i < slots;i++) {
|
|
|
- ret = sdhci_probe_slot(pdev, i);
|
|
|
- if (ret) {
|
|
|
- for (i--;i >= 0;i--)
|
|
|
- sdhci_remove_slot(pdev, i);
|
|
|
- goto free;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-free:
|
|
|
- pci_set_drvdata(pdev, NULL);
|
|
|
- kfree(chip);
|
|
|
-
|
|
|
-err:
|
|
|
- pci_disable_device(pdev);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static void __devexit sdhci_remove(struct pci_dev *pdev)
|
|
|
+void sdhci_free_host(struct sdhci_host *host)
|
|
|
{
|
|
|
- int i;
|
|
|
- struct sdhci_chip *chip;
|
|
|
-
|
|
|
- chip = pci_get_drvdata(pdev);
|
|
|
-
|
|
|
- if (chip) {
|
|
|
- for (i = 0;i < chip->num_slots;i++)
|
|
|
- sdhci_remove_slot(pdev, i);
|
|
|
-
|
|
|
- pci_set_drvdata(pdev, NULL);
|
|
|
-
|
|
|
- kfree(chip);
|
|
|
- }
|
|
|
-
|
|
|
- pci_disable_device(pdev);
|
|
|
+ mmc_free_host(host->mmc);
|
|
|
}
|
|
|
|
|
|
-static struct pci_driver sdhci_driver = {
|
|
|
- .name = DRIVER_NAME,
|
|
|
- .id_table = pci_ids,
|
|
|
- .probe = sdhci_probe,
|
|
|
- .remove = __devexit_p(sdhci_remove),
|
|
|
- .suspend = sdhci_suspend,
|
|
|
- .resume = sdhci_resume,
|
|
|
-};
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_free_host);
|
|
|
|
|
|
/*****************************************************************************\
|
|
|
* *
|
|
@@ -1721,14 +1407,11 @@ static int __init sdhci_drv_init(void)
|
|
|
": Secure Digital Host Controller Interface driver\n");
|
|
|
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
|
|
|
|
|
|
- return pci_register_driver(&sdhci_driver);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void __exit sdhci_drv_exit(void)
|
|
|
{
|
|
|
- DBG("Exiting\n");
|
|
|
-
|
|
|
- pci_unregister_driver(&sdhci_driver);
|
|
|
}
|
|
|
|
|
|
module_init(sdhci_drv_init);
|
|
@@ -1737,7 +1420,7 @@ module_exit(sdhci_drv_exit);
|
|
|
module_param(debug_quirks, uint, 0444);
|
|
|
|
|
|
MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
|
|
|
-MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver");
|
|
|
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
|