From 8ef1a14379e105c1419d21e96ffac53202bc0501 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:21 -0700 Subject: [PATCH 01/17] [MMC] sdhci: check SDHCI base clock A base clock value of 0 means that the driver must get the base clock through some other means. As we have no other way of getting it, we must abort. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 8e9100bd57e..0d9c327a84b 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1030,7 +1030,14 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) else /* XXX: Hack to get MMC layer to avoid highmem */ pdev->dma_mask = 0; - host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + 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", host->slot_descr); + ret = -ENODEV; + goto unmap; + } host->max_clk *= 1000000; /* @@ -1078,7 +1085,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ret = request_irq(host->irq, sdhci_irq, SA_SHIRQ, host->slot_descr, host); if (ret) - goto unmap; + goto untasklet; sdhci_init(host); @@ -1097,10 +1104,10 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) return 0; -unmap: +untasklet: tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); - +unmap: iounmap(host->ioaddr); release: pci_release_region(pdev, host->bar); From 51f82bc07a9673d790c2a17de8e3fa8046543f04 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:22 -0700 Subject: [PATCH 02/17] [MMC] sdhci: print device id As sdhci is a generic driver, it is helpful to see some more specific identification of the actual hardware in dmesg. PCI vendor, device and revision is sufficient in most cases. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 0d9c327a84b..405b6158cb6 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1151,13 +1151,18 @@ static int __devinit sdhci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret, i; - u8 slots; + u8 slots, rev; struct sdhci_chip *chip; BUG_ON(pdev == NULL); BUG_ON(ent == NULL); - DBG("found at %s\n", pci_name(pdev)); + 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) From 146ad66eac836c0b976c98f428d73e1f6a75270d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:23 -0700 Subject: [PATCH 03/17] [MMC] sdhci: support for multiple voltages The sdhci controllers can support up to three voltage levels. Detect which and report back to the MMC layer. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 60 ++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/sdhci.h | 10 +++++++- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 405b6158cb6..74912dcac82 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -530,6 +530,46 @@ out: host->clock = clock; } +static void sdhci_set_power(struct sdhci_host *host, unsigned short power) +{ + u8 pwr; + + if (host->power == power) + return; + + writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); + + if (power == (unsigned short)-1) + goto out; + + pwr = SDHCI_POWER_ON; + + switch (power) { + case MMC_VDD_170: + case MMC_VDD_180: + case MMC_VDD_190: + pwr |= SDHCI_POWER_180; + break; + case MMC_VDD_290: + case MMC_VDD_300: + case MMC_VDD_310: + pwr |= SDHCI_POWER_300; + break; + case MMC_VDD_320: + case MMC_VDD_330: + case MMC_VDD_340: + pwr |= SDHCI_POWER_330; + break; + default: + BUG(); + } + + writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); + +out: + host->power = power; +} + /*****************************************************************************\ * * * MMC callbacks * @@ -584,9 +624,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_set_clock(host, ios->clock); if (ios->power_mode == MMC_POWER_OFF) - writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); + sdhci_set_power(host, -1); else - writeb(0xFF, host->ioaddr + SDHCI_POWER_CONTROL); + sdhci_set_power(host, ios->vdd); ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); if (ios->bus_width == MMC_BUS_WIDTH_4) @@ -1046,9 +1086,23 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc->ops = &sdhci_ops; mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; - mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->ocr_avail = 0; + if (caps & SDHCI_CAN_VDD_330) + mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; + else if (caps & SDHCI_CAN_VDD_300) + mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; + else if (caps & SDHCI_CAN_VDD_180) + mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; + + if (mmc->ocr_avail == 0) { + printk(KERN_ERR "%s: Hardware doesn't report any " + "support voltages.\n", host->slot_descr); + ret = -ENODEV; + goto unmap; + } + spin_lock_init(&host->lock); /* diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index 3b270ef486b..aed4abd37bf 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -67,6 +67,10 @@ #define SDHCI_CTRL_4BITBUS 0x02 #define SDHCI_POWER_CONTROL 0x29 +#define SDHCI_POWER_ON 0x01 +#define SDHCI_POWER_180 0x0A +#define SDHCI_POWER_300 0x0C +#define SDHCI_POWER_330 0x0E #define SDHCI_BLOCK_GAP_CONTROL 0x2A @@ -121,9 +125,12 @@ /* 3E-3F reserved */ #define SDHCI_CAPABILITIES 0x40 -#define SDHCI_CAN_DO_DMA 0x00400000 #define SDHCI_CLOCK_BASE_MASK 0x00003F00 #define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_CAN_DO_DMA 0x00400000 +#define SDHCI_CAN_VDD_330 0x01000000 +#define SDHCI_CAN_VDD_300 0x02000000 +#define SDHCI_CAN_VDD_180 0x04000000 /* 44-47 reserved for more caps */ @@ -151,6 +158,7 @@ struct sdhci_host { unsigned int max_clk; /* Max possible freq (MHz) */ unsigned int clock; /* Current clock (MHz) */ + unsigned short power; /* Current voltage */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ From 7cb2c76fa2251474e42d55b75163c9d7ed11741e Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:23 -0700 Subject: [PATCH 04/17] [MMC] sdhci: fix timeout loops in sdhci The current timeout loop assume that jiffies are updated. This might not be the case depending on locks and if the kernel is compiled without preemption. Change the system to use a counter and fixed delays. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 74912dcac82..007e825dcb9 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -371,17 +371,17 @@ static void sdhci_finish_data(struct sdhci_host *host) static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { int flags; - u32 present; - unsigned long max_jiffies; + unsigned long timeout; WARN_ON(host->cmd); DBG("Sending cmd (%x)\n", cmd->opcode); /* Wait max 10 ms */ - max_jiffies = jiffies + (HZ + 99)/100; - do { - if (time_after(jiffies, max_jiffies)) { + timeout = 10; + while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & + (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) { + if (timeout == 0) { printk(KERN_ERR "%s: Controller never released " "inhibit bits. Please report this to " BUGMAIL ".\n", mmc_hostname(host->mmc)); @@ -390,8 +390,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) tasklet_schedule(&host->finish_tasklet); return; } - present = readl(host->ioaddr + SDHCI_PRESENT_STATE); - } while (present & (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)); + timeout--; + mdelay(1); + } mod_timer(&host->timer, jiffies + 10 * HZ); @@ -490,7 +491,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div; u16 clk; - unsigned long max_jiffies; + unsigned long timeout; if (clock == host->clock) return; @@ -511,17 +512,19 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); /* Wait max 10 ms */ - max_jiffies = jiffies + (HZ + 99)/100; - do { - if (time_after(jiffies, max_jiffies)) { + timeout = 10; + while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { printk(KERN_ERR "%s: Internal clock never stabilised. " "Please report this to " BUGMAIL ".\n", mmc_hostname(host->mmc)); sdhci_dumpregs(host); return; } - clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL); - } while (!(clk & SDHCI_CLOCK_INT_STABLE)); + timeout--; + mdelay(1); + } clk |= SDHCI_CLOCK_CARD_EN; writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); From e16514d8d86ecbde18a2a7495cf028861b34c157 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:24 -0700 Subject: [PATCH 05/17] [MMC] sdhci: fix sdhci reset timeout The reset register is automatically cleared when the reset has completed. Hence, we should busy wait and not have a fixed delay. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 007e825dcb9..77b7db27b55 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -94,12 +94,27 @@ static void sdhci_dumpregs(struct sdhci_host *host) static void sdhci_reset(struct sdhci_host *host, u8 mask) { + unsigned long timeout; + writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET); - if (mask & SDHCI_RESET_ALL) { + if (mask & SDHCI_RESET_ALL) host->clock = 0; - mdelay(50); + /* Wait max 100 ms */ + timeout = 100; + + /* hw clears the bit when it's done */ + while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) { + if (timeout == 0) { + printk(KERN_ERR "%s: Reset 0x%x never completed. " + "Please report this to " BUGMAIL ".\n", + mmc_hostname(host->mmc), (int)mask); + sdhci_dumpregs(host); + return; + } + timeout--; + mdelay(1); } } @@ -619,9 +634,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) */ if (ios->power_mode == MMC_POWER_OFF) { writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE); - spin_unlock_irqrestore(&host->lock, flags); sdhci_init(host); - spin_lock_irqsave(&host->lock, flags); } sdhci_set_clock(host, ios->clock); From 1c8cde92fa5c57daa9ff58d970ca6374f8d484a2 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:25 -0700 Subject: [PATCH 06/17] [MMC] sdhci: proper timeout handling Use the give timeout clock and calculate a proper timeout instead of using the maximum at all times. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 47 ++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/sdhci.h | 4 ++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 77b7db27b55..877226e2ffa 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -128,9 +128,6 @@ static void sdhci_init(struct sdhci_host *host) writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); - - /* This is unknown magic. */ - writeb(0xE, host->ioaddr + SDHCI_TIMEOUT_CONTROL); } static void sdhci_activate_led(struct sdhci_host *host) @@ -274,6 +271,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host) static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) { u16 mode; + u8 count; + unsigned target_timeout, current_timeout; WARN_ON(host->data); @@ -287,6 +286,37 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) DBG("tsac %d ms nsac %d clk\n", data->timeout_ns / 1000000, data->timeout_clks); + /* timeout in us */ + target_timeout = data->timeout_ns / 1000 + + data->timeout_clks / host->clock; + + /* + * Figure out needed cycles. + * We do this in steps in order to fit inside a 32 bit int. + * The first step is the minimum timeout, which will have a + * minimum resolution of 6 bits: + * (1) 2^13*1000 > 2^22, + * (2) host->timeout_clk < 2^16 + * => + * (1) / (2) > 2^6 + */ + count = 0; + current_timeout = (1 << 13) * 1000 / host->timeout_clk; + while (current_timeout < target_timeout) { + count++; + current_timeout <<= 1; + if (count >= 0xF) + break; + } + + if (count >= 0xF) { + printk(KERN_WARNING "%s: Too large timeout requested!\n", + mmc_hostname(host->mmc)); + count = 0xE; + } + + writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); + mode = SDHCI_TRNS_BLK_CNT_EN; if (data->blocks > 1) mode |= SDHCI_TRNS_MULTI; @@ -1096,6 +1126,17 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) } host->max_clk *= 1000000; + host->timeout_clk = + (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; + if (host->timeout_clk == 0) { + printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " + "frequency.\n", host->slot_descr); + ret = -ENODEV; + goto unmap; + } + if (caps & SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; + /* * Set host parameters. */ diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index aed4abd37bf..a8f45215ef3 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -125,6 +125,9 @@ /* 3E-3F reserved */ #define SDHCI_CAPABILITIES 0x40 +#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F +#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 #define SDHCI_CLOCK_BASE_MASK 0x00003F00 #define SDHCI_CLOCK_BASE_SHIFT 8 #define SDHCI_CAN_DO_DMA 0x00400000 @@ -156,6 +159,7 @@ struct sdhci_host { #define SDHCI_USE_DMA (1<<0) unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ unsigned int clock; /* Current clock (MHz) */ unsigned short power; /* Current voltage */ From c7fa9963ee6317b54e85b260791d603ea2feb8e3 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:25 -0700 Subject: [PATCH 07/17] [MMC] sdhci: correct register order The sdhci specification states that some registers must be written to in a specific order. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 877226e2ffa..44578815964 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -270,16 +270,13 @@ static void sdhci_transfer_pio(struct sdhci_host *host) static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) { - u16 mode; u8 count; unsigned target_timeout, current_timeout; WARN_ON(host->data); - if (data == NULL) { - writew(0, host->ioaddr + SDHCI_TRANSFER_MODE); + if (data == NULL) return; - } DBG("blksz %04x blks %04x flags %08x\n", data->blksz, data->blocks, data->flags); @@ -317,19 +314,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); - mode = SDHCI_TRNS_BLK_CNT_EN; - if (data->blocks > 1) - mode |= SDHCI_TRNS_MULTI; - if (data->flags & MMC_DATA_READ) - mode |= SDHCI_TRNS_READ; - if (host->flags & SDHCI_USE_DMA) - mode |= SDHCI_TRNS_DMA; - - writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE); - - writew(data->blksz, host->ioaddr + SDHCI_BLOCK_SIZE); - writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT); - if (host->flags & SDHCI_USE_DMA) { int count; @@ -347,6 +331,30 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) host->offset = 0; host->remain = host->cur_sg->length; } + + writew(data->blksz, host->ioaddr + SDHCI_BLOCK_SIZE); + writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT); +} + +static void sdhci_set_transfer_mode(struct sdhci_host *host, + struct mmc_data *data) +{ + u16 mode; + + WARN_ON(host->data); + + if (data == NULL) + return; + + mode = SDHCI_TRNS_BLK_CNT_EN; + if (data->blocks > 1) + mode |= SDHCI_TRNS_MULTI; + if (data->flags & MMC_DATA_READ) + mode |= SDHCI_TRNS_READ; + if (host->flags & SDHCI_USE_DMA) + mode |= SDHCI_TRNS_DMA; + + writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE); } static void sdhci_finish_data(struct sdhci_host *host) @@ -447,6 +455,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT); + sdhci_set_transfer_mode(host, cmd->data); + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { printk(KERN_ERR "%s: Unsupported response type! " "Please report this to " BUGMAIL ".\n", From 3192a28f7d34ea8f1d0fef8ca5bc0314b5b5bb19 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:26 -0700 Subject: [PATCH 08/17] [MMC] sdhci: fix interrupt handling The specification says that interrupts should be cleared before the source is removed. We should also not set unknown bits. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 80 ++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 44578815964..b9aa60aed7f 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -124,7 +124,12 @@ static void sdhci_init(struct sdhci_host *host) sdhci_reset(host, SDHCI_RESET_ALL); - intmask = ~(SDHCI_INT_CARD_INT | SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL); + intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | + SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | + SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | + SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL | + SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); @@ -360,7 +365,6 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, static void sdhci_finish_data(struct sdhci_host *host) { struct mmc_data *data; - u32 intmask; u16 blocks; BUG_ON(!host->data); @@ -371,14 +375,6 @@ static void sdhci_finish_data(struct sdhci_host *host) if (host->flags & SDHCI_USE_DMA) { pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len, (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); - } else { - intmask = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); - intmask &= ~(SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL); - writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); - - intmask = readl(host->ioaddr + SDHCI_INT_ENABLE); - intmask &= ~(SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL); - writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); } /* @@ -512,31 +508,9 @@ static void sdhci_finish_command(struct sdhci_host *host) DBG("Ending cmd (%x)\n", host->cmd->opcode); - if (host->cmd->data) { - u32 intmask; - + if (host->cmd->data) host->data = host->cmd->data; - - if (!(host->flags & SDHCI_USE_DMA)) { - /* - * Don't enable the interrupts until now to make sure we - * get stable handling of the FIFO. - */ - intmask = readl(host->ioaddr + SDHCI_INT_ENABLE); - intmask |= SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL; - writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); - - intmask = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); - intmask |= SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL; - writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); - - /* - * The buffer interrupts are to unreliable so we - * start the transfer immediatly. - */ - sdhci_transfer_pio(host); - } - } else + else tasklet_schedule(&host->finish_tasklet); host->cmd = NULL; @@ -914,50 +888,44 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id, struct pt_regs *regs) DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); - if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) + if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), + host->ioaddr + SDHCI_INT_STATUS); tasklet_schedule(&host->card_tasklet); + } + + intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); if (intmask & SDHCI_INT_CMD_MASK) { - sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); - writel(intmask & SDHCI_INT_CMD_MASK, host->ioaddr + SDHCI_INT_STATUS); + sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); } if (intmask & SDHCI_INT_DATA_MASK) { - sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); - writel(intmask & SDHCI_INT_DATA_MASK, host->ioaddr + SDHCI_INT_STATUS); + sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); } intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); - if (intmask & SDHCI_INT_CARD_INT) { - printk(KERN_ERR "%s: Unexpected card interrupt. Please " - "report this to " BUGMAIL ".\n", - mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - } - if (intmask & SDHCI_INT_BUS_POWER) { - printk(KERN_ERR "%s: Unexpected bus power interrupt. Please " - "report this to " BUGMAIL ".\n", + printk(KERN_ERR "%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); + writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS); } - if (intmask & SDHCI_INT_ACMD12ERR) { - printk(KERN_ERR "%s: Unexpected auto CMD12 error. Please " + intmask &= SDHCI_INT_BUS_POWER; + + if (intmask) { + printk(KERN_ERR "%s: Unexpected interrupt 0x%08x. Please " "report this to " BUGMAIL ".\n", - mmc_hostname(host->mmc)); + mmc_hostname(host->mmc), intmask); sdhci_dumpregs(host); - writew(~0, host->ioaddr + SDHCI_ACMD12_ERR); - } - - if (intmask) writel(intmask, host->ioaddr + SDHCI_INT_STATUS); + } result = IRQ_HANDLED; From a406f5a3b68ee1db2306a2ba1c9b00dbd3505d05 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 2 Jul 2006 16:50:59 +0100 Subject: [PATCH 09/17] [MMC] Fix sdhci PIO routines The sdhci controllers operate with blocks, not bytes. The PIO routines must therefore make sure that the minimum unit transfered is a complete block. Signed-off-by: Pierre Ossman Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 166 +++++++++++++++++++++++++++----------------- drivers/mmc/sdhci.h | 6 +- 2 files changed, 105 insertions(+), 67 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index b9aa60aed7f..8e480140cd2 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -8,12 +8,6 @@ * published by the Free Software Foundation. */ - /* - * Note that PIO transfer is rather crappy atm. The buffer full/empty - * interrupts aren't reliable so we currently transfer the entire buffer - * directly. Patches to solve the problem are welcome. - */ - #include #include #include @@ -128,7 +122,7 @@ static void sdhci_init(struct sdhci_host *host) SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | - SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL | + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); @@ -189,79 +183,46 @@ static inline int sdhci_next_sg(struct sdhci_host* host) return host->num_sg; } -static void sdhci_transfer_pio(struct sdhci_host *host) +static void sdhci_read_block_pio(struct sdhci_host *host) { + int blksize, chunk_remain; + u32 data; char *buffer; - u32 mask; - int bytes, size; - unsigned long max_jiffies; + int size; - BUG_ON(!host->data); + DBG("PIO reading\n"); - if (host->num_sg == 0) - return; - - bytes = 0; - if (host->data->flags & MMC_DATA_READ) - mask = SDHCI_DATA_AVAILABLE; - else - mask = SDHCI_SPACE_AVAILABLE; + blksize = host->data->blksz; + chunk_remain = 0; + data = 0; buffer = sdhci_kmap_sg(host) + host->offset; - /* Transfer shouldn't take more than 5 s */ - max_jiffies = jiffies + HZ * 5; - - while (host->size > 0) { - if (time_after(jiffies, max_jiffies)) { - printk(KERN_ERR "%s: PIO transfer stalled. " - "Please report this to " - BUGMAIL ".\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - - sdhci_kunmap_sg(host); - - host->data->error = MMC_ERR_FAILED; - sdhci_finish_data(host); - return; + while (blksize) { + if (chunk_remain == 0) { + data = readl(host->ioaddr + SDHCI_BUFFER); + chunk_remain = min(blksize, 4); } - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask)) - continue; - size = min(host->size, host->remain); + size = min(size, chunk_remain); - if (size >= 4) { - if (host->data->flags & MMC_DATA_READ) - *(u32*)buffer = readl(host->ioaddr + SDHCI_BUFFER); - else - writel(*(u32*)buffer, host->ioaddr + SDHCI_BUFFER); - size = 4; - } else if (size >= 2) { - if (host->data->flags & MMC_DATA_READ) - *(u16*)buffer = readw(host->ioaddr + SDHCI_BUFFER); - else - writew(*(u16*)buffer, host->ioaddr + SDHCI_BUFFER); - size = 2; - } else { - if (host->data->flags & MMC_DATA_READ) - *(u8*)buffer = readb(host->ioaddr + SDHCI_BUFFER); - else - writeb(*(u8*)buffer, host->ioaddr + SDHCI_BUFFER); - size = 1; - } - - buffer += size; + chunk_remain -= size; + blksize -= size; host->offset += size; host->remain -= size; - - bytes += size; host->size -= size; + while (size) { + *buffer = data & 0xFF; + buffer++; + data >>= 8; + size--; + } if (host->remain == 0) { sdhci_kunmap_sg(host); if (sdhci_next_sg(host) == 0) { - DBG("PIO transfer: %d bytes\n", bytes); + BUG_ON(blksize != 0); return; } buffer = sdhci_kmap_sg(host); @@ -269,8 +230,85 @@ static void sdhci_transfer_pio(struct sdhci_host *host) } sdhci_kunmap_sg(host); +} - DBG("PIO transfer: %d bytes\n", bytes); +static void sdhci_write_block_pio(struct sdhci_host *host) +{ + int blksize, chunk_remain; + u32 data; + char *buffer; + int bytes, size; + + DBG("PIO writing\n"); + + blksize = host->data->blksz; + chunk_remain = 4; + data = 0; + + bytes = 0; + buffer = sdhci_kmap_sg(host) + host->offset; + + while (blksize) { + size = min(host->size, host->remain); + size = min(size, chunk_remain); + + chunk_remain -= size; + blksize -= size; + host->offset += size; + host->remain -= size; + host->size -= size; + while (size) { + data >>= 8; + data |= (u32)*buffer << 24; + buffer++; + size--; + } + + if (chunk_remain == 0) { + writel(data, host->ioaddr + SDHCI_BUFFER); + chunk_remain = min(blksize, 4); + } + + if (host->remain == 0) { + sdhci_kunmap_sg(host); + if (sdhci_next_sg(host) == 0) { + BUG_ON(blksize != 0); + return; + } + buffer = sdhci_kmap_sg(host); + } + } + + sdhci_kunmap_sg(host); +} + +static void sdhci_transfer_pio(struct sdhci_host *host) +{ + u32 mask; + + BUG_ON(!host->data); + + if (host->size == 0) + return; + + if (host->data->flags & MMC_DATA_READ) + mask = SDHCI_DATA_AVAILABLE; + else + mask = SDHCI_SPACE_AVAILABLE; + + while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { + if (host->data->flags & MMC_DATA_READ) + sdhci_read_block_pio(host); + else + sdhci_write_block_pio(host); + + if (host->size == 0) + break; + + BUG_ON(host->num_sg == 0); + } + + DBG("PIO transfer complete.\n"); } static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) @@ -863,7 +901,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (host->data->error != MMC_ERR_NONE) sdhci_finish_data(host); else { - if (intmask & (SDHCI_INT_BUF_FULL | SDHCI_INT_BUF_EMPTY)) + if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) sdhci_transfer_pio(host); if (intmask & SDHCI_INT_DATA_END) diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index a8f45215ef3..f8df28f8d6d 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -95,8 +95,8 @@ #define SDHCI_INT_RESPONSE 0x00000001 #define SDHCI_INT_DATA_END 0x00000002 #define SDHCI_INT_DMA_END 0x00000008 -#define SDHCI_INT_BUF_EMPTY 0x00000010 -#define SDHCI_INT_BUF_FULL 0x00000020 +#define SDHCI_INT_SPACE_AVAIL 0x00000010 +#define SDHCI_INT_DATA_AVAIL 0x00000020 #define SDHCI_INT_CARD_INSERT 0x00000040 #define SDHCI_INT_CARD_REMOVE 0x00000080 #define SDHCI_INT_CARD_INT 0x00000100 @@ -116,7 +116,7 @@ #define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) #define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ - SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL | \ + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ SDHCI_INT_DATA_END_BIT) From bab7696184bbf0ea48d56902bd1f9ac983079ad2 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 2 Jul 2006 16:51:35 +0100 Subject: [PATCH 10/17] [MMC] sdhci: Avoid sdhci DMA boundaries The sdhci controllers will issue an interrupt when a configurable number of bytes have been transfered using DMA. The purpose is to handle multiple, scattered memory pages. Unfortunately, it requires that all transfers are completely aligned to memory pages, which we cannot guarantee. So we just disable the function. Signed-off-by: Pierre Ossman Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 13 +++++++++---- drivers/mmc/sdhci.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 8e480140cd2..95fe0fdac48 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -326,6 +326,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) DBG("tsac %d ms nsac %d clk\n", data->timeout_ns / 1000000, data->timeout_clks); + /* Sanity checks */ + BUG_ON(data->blksz * data->blocks > 524288); + /* timeout in us */ target_timeout = data->timeout_ns / 1000 + data->timeout_clks / host->clock; @@ -375,7 +378,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) host->remain = host->cur_sg->length; } - writew(data->blksz, host->ioaddr + SDHCI_BLOCK_SIZE); + /* We do not handle DMA boundaries, so set it to max (512 KiB) */ + writew(SDHCI_MAKE_BLKSZ(7, data->blksz), + host->ioaddr + SDHCI_BLOCK_SIZE); writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT); } @@ -1188,10 +1193,10 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc->max_phys_segs = 16; /* - * Maximum number of sectors in one transfer. Limited by sector - * count register. + * Maximum number of sectors in one transfer. Limited by DMA boundary + * size (512KiB), which means (512 KiB/512=) 1024 entries. */ - mmc->max_sectors = 0x3FFF; + mmc->max_sectors = 1024; /* * Maximum segment size. Could be one segment with the maximum number diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index f8df28f8d6d..8ed2a8973db 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -23,6 +23,7 @@ #define SDHCI_DMA_ADDRESS 0x00 #define SDHCI_BLOCK_SIZE 0x04 +#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) #define SDHCI_BLOCK_COUNT 0x06 From 1d676e02970d9e511c9b96101501da90954ee265 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 2 Jul 2006 16:52:10 +0100 Subject: [PATCH 11/17] [MMC] sdhci: Test for invalid block size The controller has an upper limit on the block size. Make sure we do not cross it. Signed-off-by: Pierre Ossman Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 11 +++++++++++ drivers/mmc/sdhci.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 95fe0fdac48..302dd5bde75 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -328,6 +328,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) /* Sanity checks */ BUG_ON(data->blksz * data->blocks > 524288); + BUG_ON(data->blksz > host->max_block); + BUG_ON(data->blocks > 65535); /* timeout in us */ target_timeout = data->timeout_ns / 1000 + @@ -1158,6 +1160,15 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) if (caps & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; + host->max_block = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; + if (host->max_block >= 3) { + printk(KERN_ERR "%s: Invalid maximum block size.\n", + host->slot_descr); + ret = -ENODEV; + goto unmap; + } + host->max_block = 512 << host->max_block; + /* * Set host parameters. */ diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index 8ed2a8973db..b1aa3acf090 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -131,6 +131,8 @@ #define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 #define SDHCI_CLOCK_BASE_MASK 0x00003F00 #define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_MAX_BLOCK_MASK 0x00030000 +#define SDHCI_MAX_BLOCK_SHIFT 16 #define SDHCI_CAN_DO_DMA 0x00400000 #define SDHCI_CAN_VDD_330 0x01000000 #define SDHCI_CAN_VDD_300 0x02000000 @@ -161,6 +163,7 @@ struct sdhci_host { unsigned int max_clk; /* Max possible freq (MHz) */ unsigned int timeout_clk; /* Timeout freq (KHz) */ + unsigned int max_block; /* Max block size (bytes) */ unsigned int clock; /* Current clock (MHz) */ unsigned short power; /* Current voltage */ From fd2208d7c72ef5995b730f1e23b082261499e334 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:28 -0700 Subject: [PATCH 12/17] [MMC] sdhci: check only relevant inhibit bits Conform to the sdhci specification as to which inhibit bits should be checked at different times. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 302dd5bde75..5324eae6d72 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -465,6 +465,7 @@ static void sdhci_finish_data(struct sdhci_host *host) static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { int flags; + u32 mask; unsigned long timeout; WARN_ON(host->cmd); @@ -473,11 +474,20 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) /* Wait max 10 ms */ timeout = 10; - while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & - (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) { + + mask = SDHCI_CMD_INHIBIT; + if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) + mask |= SDHCI_DATA_INHIBIT; + + /* We shouldn't wait for data inihibit for stop commands, even + though they might use busy signaling */ + if (host->mrq->data && (cmd == host->mrq->data->stop)) + mask &= ~SDHCI_DATA_INHIBIT; + + while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { if (timeout == 0) { printk(KERN_ERR "%s: Controller never released " - "inhibit bits. Please report this to " + "inhibit bit(s). Please report this to " BUGMAIL ".\n", mmc_hostname(host->mmc)); sdhci_dumpregs(host); cmd->error = MMC_ERR_FAILED; From 4a9655051fb1efa568e53baf5dfb21e33bad6bf6 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:29 -0700 Subject: [PATCH 13/17] [MMC] sdhci: check controller version Check the interface version of the controller and bail out if it's an unknown version. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 11 +++++++++++ drivers/mmc/sdhci.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 5324eae6d72..e37c8149249 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1073,6 +1073,7 @@ static int sdhci_resume (struct pci_dev *pdev) static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) { int ret; + unsigned int version; struct sdhci_chip *chip; struct mmc_host *mmc; struct sdhci_host *host; @@ -1131,6 +1132,16 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) goto release; } + version = readw(host->ioaddr + SDHCI_HOST_VERSION); + version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; + if (version != 0) { + printk(KERN_ERR "%s: Unknown controller version (%d). " + "Cowardly refusing to continue.\n", host->slot_descr, + version); + ret = -ENODEV; + goto unmap; + } + caps = readl(host->ioaddr + SDHCI_CAPABILITIES); if ((caps & SDHCI_CAN_DO_DMA) && ((pdev->class & 0x0000FF) == 0x01)) diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index b1aa3acf090..758cf1c2436 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -149,6 +149,10 @@ #define SDHCI_SLOT_INT_STATUS 0xFC #define SDHCI_HOST_VERSION 0xFE +#define SDHCI_VENDOR_VER_MASK 0xFF00 +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK 0x00FF +#define SDHCI_SPEC_VER_SHIFT 0 struct sdhci_chip; From d96649ed5ace812ffc8d86252d7c663326ca47f8 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:30 -0700 Subject: [PATCH 14/17] [MMC] sdhci: reset sdhci controller early The specification states that the capabilities register might need a reset to get correct values after boot up. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index e37c8149249..06dec744d53 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1132,6 +1132,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) goto release; } + sdhci_reset(host, SDHCI_RESET_ALL); + version = readw(host->ioaddr + SDHCI_HOST_VERSION); version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; if (version != 0) { From 6743527441430586aa82a0dee1b2700a2a974ebc Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:31 -0700 Subject: [PATCH 15/17] [MMC] sdhci: more DMA capabilities tests Properly test for controller interface to see if it's DMA capable. As many controllers are misconfigured in this regard, also add debug parameters to force DMA support either way. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 30 +++++++++++++++++++++++++++++- drivers/mmc/sdhci.h | 4 ++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 06dec744d53..315ab49e4b2 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -28,6 +28,9 @@ #define DBG(f, x...) \ pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) +static unsigned int debug_nodma = 0; +static unsigned int debug_forcedma = 0; + static const struct pci_device_id pci_ids[] __devinitdata = { /* handle any SD host controller */ {PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)}, @@ -1105,6 +1108,16 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) return -ENODEV; } + 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); if (!mmc) return -ENOMEM; @@ -1146,7 +1159,16 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) caps = readl(host->ioaddr + SDHCI_CAPABILITIES); - if ((caps & SDHCI_CAN_DO_DMA) && ((pdev->class & 0x0000FF) == 0x01)) + if (debug_nodma) + DBG("DMA forced off\n"); + else if (debug_forcedma) { + DBG("DMA forced on\n"); + host->flags |= SDHCI_USE_DMA; + } else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) + DBG("Controller doesn't have DMA interface\n"); + else if (!(caps & SDHCI_CAN_DO_DMA)) + DBG("Controller doesn't have DMA capability\n"); + else host->flags |= SDHCI_USE_DMA; if (host->flags & SDHCI_USE_DMA) { @@ -1429,7 +1451,13 @@ static void __exit sdhci_drv_exit(void) module_init(sdhci_drv_init); module_exit(sdhci_drv_exit); +module_param(debug_nodma, uint, 0444); +module_param(debug_forcedma, uint, 0444); + MODULE_AUTHOR("Pierre Ossman "); MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver"); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); + +MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)"); +MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)"); diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index 758cf1c2436..8111fa38c05 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -12,6 +12,10 @@ * PCI registers */ +#define PCI_SDHCI_IFPIO 0x00 +#define PCI_SDHCI_IFDMA 0x01 +#define PCI_SDHCI_IFVENDOR 0x02 + #define PCI_SLOT_INFO 0x40 /* 8 bits */ #define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7) #define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07 From df673b227ce08a7706b30fd2bf6512393d9c3c29 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:31 -0700 Subject: [PATCH 16/17] [MMC] sdhci: support controller specific quirks As some specific controllers will have bugs, we need a way to map special behaviour to certain hardware. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 7 +++++++ drivers/mmc/sdhci.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 315ab49e4b2..27f81e61419 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -30,6 +30,7 @@ static unsigned int debug_nodma = 0; static unsigned int debug_forcedma = 0; +static unsigned int debug_quirks = 0; static const struct pci_device_id pci_ids[] __devinitdata = { /* handle any SD host controller */ @@ -1373,6 +1374,10 @@ static int __devinit sdhci_probe(struct pci_dev *pdev, } chip->pdev = pdev; + chip->quirks = ent->driver_data; + + if (debug_quirks) + chip->quirks = debug_quirks; chip->num_slots = slots; pci_set_drvdata(pdev, chip); @@ -1453,6 +1458,7 @@ module_exit(sdhci_drv_exit); module_param(debug_nodma, uint, 0444); module_param(debug_forcedma, uint, 0444); +module_param(debug_quirks, uint, 0444); MODULE_AUTHOR("Pierre Ossman "); MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver"); @@ -1461,3 +1467,4 @@ MODULE_LICENSE("GPL"); MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)"); MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)"); +MODULE_PARM_DESC(debug_quirks, "Force certain quirks."); diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index 8111fa38c05..f2453343f78 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -204,6 +204,8 @@ struct sdhci_host { struct sdhci_chip { struct pci_dev *pdev; + unsigned long quirks; + int num_slots; /* Slots on controller */ struct sdhci_host *hosts[0]; /* Pointers to hosts */ }; From 2c5f394025df6e54a9c09afda03855f3877b9afa Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 02:22:32 -0700 Subject: [PATCH 17/17] [MMC] sdhci: version bump sdhci New version number for sdhci driver. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/mmc/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 27f81e61419..e192f3c9a43 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -21,7 +21,7 @@ #include "sdhci.h" #define DRIVER_NAME "sdhci" -#define DRIVER_VERSION "0.11" +#define DRIVER_VERSION "0.12" #define BUGMAIL ""