* [PATCH 00/21] Series short description
@ 2006-06-21 14:23 Pierre Ossman
2006-06-21 14:25 ` [PATCH 01/21] [MMC] Check SDHCI base clock Pierre Ossman
` (21 more replies)
0 siblings, 22 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:23 UTC (permalink / raw)
To: rmk+lkml; +Cc: linux-kernel
The SD Association decided to release a specification for the SDHCI
controllers. This set of patches updates the current driver, which is
based on reverse engineering, to comply with the official specification.
It has been tested for quite some time by people on the sdhci-devel list.
Although it doesn't solve all known issues, it doesn't cause any new ones.
Rgds
Pierre
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 01/21] [MMC] Check SDHCI base clock
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
@ 2006-06-21 14:25 ` Pierre Ossman
2006-06-21 14:25 ` [PATCH 02/21] [MMC] Print device id Pierre Ossman
` (20 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:25 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 15 +++++++++++----
1 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 6bfcdbc..681cbb8 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -1030,7 +1030,14 @@ static int __devinit sdhci_probe_slot(st
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(st
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 @@ #endif
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);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 02/21] [MMC] Print device id
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
2006-06-21 14:25 ` [PATCH 01/21] [MMC] Check SDHCI base clock Pierre Ossman
@ 2006-06-21 14:25 ` Pierre Ossman
2006-06-21 14:25 ` [PATCH 03/21] [MMC] Support for multiple voltages Pierre Ossman
` (19 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:25 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 9 +++++++--
1 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 681cbb8..efbece3 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -1151,13 +1151,18 @@ static int __devinit sdhci_probe(struct
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)
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 03/21] [MMC] Support for multiple voltages
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
2006-06-21 14:25 ` [PATCH 01/21] [MMC] Check SDHCI base clock Pierre Ossman
2006-06-21 14:25 ` [PATCH 02/21] [MMC] Print device id Pierre Ossman
@ 2006-06-21 14:25 ` Pierre Ossman
2006-06-21 14:25 ` [PATCH 04/21] [MMC] Fix timeout loops in sdhci Pierre Ossman
` (18 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:25 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
The sdhci controllers can support up to three voltage levels. Detect which
and report back to the MMC layer.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
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 efbece3..a6238b2 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_hos
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(st
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 3b270ef..aed4abd 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -67,6 +67,10 @@ #define SDHCI_CTRL_LED 0x01
#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 @@ #define SDHCI_ACMD12_ERR 0x3C
/* 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 @@ #define SDHCI_USE_DMA (1<<0)
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 */
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 04/21] [MMC] Fix timeout loops in sdhci
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (2 preceding siblings ...)
2006-06-21 14:25 ` [PATCH 03/21] [MMC] Support for multiple voltages Pierre Ossman
@ 2006-06-21 14:25 ` Pierre Ossman
2006-06-21 14:25 ` [PATCH 05/21] [MMC] Fix sdhci reset timeout Pierre Ossman
` (17 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:25 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 29 ++++++++++++++++-------------
1 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index a6238b2..b68ca02 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -371,17 +371,17 @@ static void sdhci_finish_data(struct sdh
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 sd
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
{
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
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);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 05/21] [MMC] Fix sdhci reset timeout
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (3 preceding siblings ...)
2006-06-21 14:25 ` [PATCH 04/21] [MMC] Fix timeout loops in sdhci Pierre Ossman
@ 2006-06-21 14:25 ` Pierre Ossman
2006-06-21 14:25 ` [PATCH 06/21] [MMC] Proper timeout handling Pierre Ossman
` (16 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:25 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 21 +++++++++++++++++----
1 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index b68ca02..d904435 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -94,12 +94,27 @@ static void sdhci_dumpregs(struct sdhci_
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_hos
*/
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);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 06/21] [MMC] Proper timeout handling
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (4 preceding siblings ...)
2006-06-21 14:25 ` [PATCH 05/21] [MMC] Fix sdhci reset timeout Pierre Ossman
@ 2006-06-21 14:25 ` Pierre Ossman
2006-06-21 14:25 ` [PATCH 07/21] [MMC] Correct register order Pierre Ossman
` (15 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:25 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
Use the give timeout clock and calculate a proper timeout instead of using
the maximum at all times.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
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 d904435..feadc2d 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -128,9 +128,6 @@ static void sdhci_init(struct sdhci_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 sd
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 sd
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(st
}
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 aed4abd..a8f4521 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -125,6 +125,9 @@ #define SDHCI_ACMD12_ERR 0x3C
/* 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 */
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 07/21] [MMC] Correct register order
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (5 preceding siblings ...)
2006-06-21 14:25 ` [PATCH 06/21] [MMC] Proper timeout handling Pierre Ossman
@ 2006-06-21 14:25 ` Pierre Ossman
2006-06-23 16:02 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 08/21] [MMC] Fix interrupt handling Pierre Ossman
` (14 subsequent siblings)
21 siblings, 1 reply; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:25 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
The sdhci specification states that some registers must be written to in a
specific order.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 44 +++++++++++++++++++++++++++-----------------
1 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index feadc2d..52e4c6e 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -270,16 +270,13 @@ static void sdhci_transfer_pio(struct sd
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",
1 << data->blksz_bits, data->blocks, data->flags);
@@ -317,19 +314,6 @@ static void sdhci_prepare_data(struct sd
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(1 << data->blksz_bits, 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 sd
host->offset = 0;
host->remain = host->cur_sg->length;
}
+
+ writew(1 << data->blksz_bits, 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 sd
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",
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 08/21] [MMC] Fix interrupt handling
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (6 preceding siblings ...)
2006-06-21 14:25 ` [PATCH 07/21] [MMC] Correct register order Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 09/21] [MMC] Fix sdhci PIO routines Pierre Ossman
` (13 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 80 +++++++++++++++------------------------------------
1 files changed, 24 insertions(+), 56 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 52e4c6e..b7a9ac2 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -124,7 +124,12 @@ static void sdhci_init(struct sdhci_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(stru
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 sdh
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
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, vo
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);
+ }
- if (intmask & SDHCI_INT_CMD_MASK) {
- sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
+ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+ if (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;
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 09/21] [MMC] Fix sdhci PIO routines
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (7 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 08/21] [MMC] Fix interrupt handling Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 10/21] [MMC] Avoid sdhci DMA boundaries Pierre Ossman
` (12 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 158 ++++++++++++++++++++++++++++++++-------------------
drivers/mmc/sdhci.h | 6 +-
2 files changed, 101 insertions(+), 63 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index b7a9ac2..eb6aee4 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 <linux/delay.h>
#include <linux/highmem.h>
#include <linux/pci.h>
@@ -128,7 +122,7 @@ static void sdhci_init(struct sdhci_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,96 @@ static inline int sdhci_next_sg(struct s
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 = 1 << host->data->blksz_bits;
+ 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 (blksize) {
+ if (chunk_remain == 0) {
+ data = readl(host->ioaddr + SDHCI_BUFFER);
+ chunk_remain = min(blksize, 4);
+ }
- 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);
+ size = min(host->size, host->remain);
+ size = min(size, chunk_remain);
- sdhci_kunmap_sg(host);
+ chunk_remain -= size;
+ blksize -= size;
+ host->offset += size;
+ host->remain -= size;
+ host->size -= size;
+ while (size) {
+ *buffer = data & 0xFF;
+ buffer++;
+ data >>= 8;
+ size--;
+ }
- host->data->error = MMC_ERR_FAILED;
- sdhci_finish_data(host);
- return;
+ if (host->remain == 0) {
+ sdhci_kunmap_sg(host);
+ if (sdhci_next_sg(host) == 0) {
+ BUG_ON(blksize != 0);
+ return;
+ }
+ buffer = sdhci_kmap_sg(host);
}
+ }
- if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask))
- continue;
+ sdhci_kunmap_sg(host);
+}
- size = min(host->size, host->remain);
+static void sdhci_write_block_pio(struct sdhci_host *host)
+{
+ int blksize, chunk_remain;
+ u32 data;
+ char *buffer;
+ int bytes, size;
- 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;
- }
+ DBG("PIO writing\n");
+
+ blksize = 1 << host->data->blksz_bits;
+ 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);
- buffer += size;
+ chunk_remain -= size;
+ blksize -= size;
host->offset += size;
host->remain -= size;
-
- bytes += 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) {
- DBG("PIO transfer: %d bytes\n", bytes);
+ BUG_ON(blksize != 0);
return;
}
buffer = sdhci_kmap_sg(host);
@@ -269,8 +280,35 @@ static void sdhci_transfer_pio(struct sd
}
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: %d bytes\n", bytes);
+ 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_
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 a8f4521..f8df28f 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -95,8 +95,8 @@ #define SDHCI_SIGNAL_ENABLE 0x38
#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_ERROR_MASK 0xFFFF8000
#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)
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 10/21] [MMC] Avoid sdhci DMA boundaries
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (8 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 09/21] [MMC] Fix sdhci PIO routines Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-23 16:03 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 11/21] [MMC] Test for invalid block size Pierre Ossman
` (11 subsequent siblings)
21 siblings, 1 reply; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
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 eb6aee4..57200e4 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -326,6 +326,9 @@ static void sdhci_prepare_data(struct sd
DBG("tsac %d ms nsac %d clk\n",
data->timeout_ns / 1000000, data->timeout_clks);
+ /* Sanity checks */
+ BUG_ON((1 << data->blksz_bits) * 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 sd
host->remain = host->cur_sg->length;
}
- writew(1 << data->blksz_bits, host->ioaddr + SDHCI_BLOCK_SIZE);
+ /* We do not handle DMA boundaries, so set it to max (512 KiB) */
+ writew(SDHCI_MAKE_BLKSZ(7, 1 << data->blksz_bits),
+ host->ioaddr + SDHCI_BLOCK_SIZE);
writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT);
}
@@ -1188,10 +1193,10 @@ static int __devinit sdhci_probe_slot(st
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 f8df28f..8ed2a89 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -23,6 +23,7 @@ #define PCI_SLOT_INFO_FIRST_BAR_MASK 0x
#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
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 11/21] [MMC] Test for invalid block size
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (9 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 10/21] [MMC] Avoid sdhci DMA boundaries Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 12/21] [MMC] Check only relevant inhibit bits Pierre Ossman
` (10 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
The controller has an upper limit on the block size. Make sure we do not
cross it.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 11 +++++++++++
drivers/mmc/sdhci.h | 3 +++
2 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 57200e4..919d60f 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -328,6 +328,8 @@ static void sdhci_prepare_data(struct sd
/* Sanity checks */
BUG_ON((1 << data->blksz_bits) * data->blocks > 524288);
+ BUG_ON((1 << data->blksz_bits) > 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(st
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 8ed2a89..b1aa3ac 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -131,6 +131,8 @@ #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_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 @@ #define SDHCI_USE_DMA (1<<0)
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 */
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 12/21] [MMC] Check only relevant inhibit bits
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (10 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 11/21] [MMC] Test for invalid block size Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 13/21] [MMC] Check controller version Pierre Ossman
` (9 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
Conform to the sdhci specification as to which inhibit bits should be
checked at different times.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 16 +++++++++++++---
1 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 919d60f..41addde 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -465,6 +465,7 @@ static void sdhci_finish_data(struct sdh
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 sd
/* 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;
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 13/21] [MMC] Check controller version
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (11 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 12/21] [MMC] Check only relevant inhibit bits Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 14/21] [MMC] Reset sdhci controller early Pierre Ossman
` (8 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
Check the interface version of the controller and bail out if it's an
unknown version.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 11 +++++++++++
drivers/mmc/sdhci.h | 4 ++++
2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 41addde..0c19e27 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -1073,6 +1073,7 @@ #endif /* CONFIG_PM */
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(st
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 b1aa3ac..758cf1c 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -149,6 +149,10 @@ #define SDHCI_MAX_CURRENT 0x48
#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;
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 14/21] [MMC] Reset sdhci controller early
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (12 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 13/21] [MMC] Check controller version Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 15/21] [MMC] More DMA capabilities tests Pierre Ossman
` (7 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
The specification states that the capabilities register might need a reset
to get correct values after boot up.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 0c19e27..7b55691 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -1132,6 +1132,8 @@ static int __devinit sdhci_probe_slot(st
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) {
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 15/21] [MMC] More DMA capabilities tests
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (13 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 14/21] [MMC] Reset sdhci controller early Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 16/21] [MMC] Support controller specific quirks Pierre Ossman
` (6 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
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 <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 30 +++++++++++++++++++++++++++++-
drivers/mmc/sdhci.h | 4 ++++
2 files changed, 33 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 7b55691..04d79a2 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -28,6 +28,9 @@ #define BUGMAIL "<sdhci-devel@list.drzeu
#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(st
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(st
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 <drzeus@drzeus.cx>");
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 758cf1c..8111fa3 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
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 16/21] [MMC] Support controller specific quirks
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (14 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 15/21] [MMC] More DMA capabilities tests Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 17/21] [MMC] Version bump sdhci Pierre Ossman
` (5 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
As some specific controllers will have bugs, we need a way to map special
behaviour to certain hardware.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 7 +++++++
drivers/mmc/sdhci.h | 2 ++
2 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 04d79a2..1e08acc 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -30,6 +30,7 @@ #define DBG(f, x...) \
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
}
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 <drzeus@drzeus.cx>");
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 8111fa3..f245334 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -204,6 +204,8 @@ #define SDHCI_USE_DMA (1<<0)
struct sdhci_chip {
struct pci_dev *pdev;
+ unsigned long quirks;
+
int num_slots; /* Slots on controller */
struct sdhci_host *hosts[0]; /* Pointers to hosts */
};
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 17/21] [MMC] Version bump sdhci
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (15 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 16/21] [MMC] Support controller specific quirks Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 18/21] [PCI] Add SDHCI controller ids Pierre Ossman
` (4 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
New version number for sdhci driver.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 1e08acc..1a12427 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -21,7 +21,7 @@ #include <asm/scatterlist.h>
#include "sdhci.h"
#define DRIVER_NAME "sdhci"
-#define DRIVER_VERSION "0.11"
+#define DRIVER_VERSION "0.12"
#define BUGMAIL "<sdhci-devel@list.drzeus.cx>"
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 18/21] [PCI] Add SDHCI controller ids
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (16 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 17/21] [MMC] Version bump sdhci Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 19/21] [MMC] Quirk for broken reset Pierre Ossman
` (3 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
Add ids for SDHCI controllers so that they can be identified for quirks.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
include/linux/pci_ids.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index bcfe9d4..6ef38b4 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -725,6 +725,7 @@ #define PCI_VENDOR_ID_TI 0x104c
#define PCI_DEVICE_ID_TI_TVP4020 0x3d07
#define PCI_DEVICE_ID_TI_4450 0x8011
#define PCI_DEVICE_ID_TI_XX21_XX11 0x8031
+#define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034
#define PCI_DEVICE_ID_TI_X515 0x8036
#define PCI_DEVICE_ID_TI_1130 0xac12
#define PCI_DEVICE_ID_TI_1031 0xac13
@@ -1422,6 +1423,7 @@ #define PCI_DEVICE_ID_RICOH_RL5C466 0x04
#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475
#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476
#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478
+#define PCI_DEVICE_ID_RICOH_R5C822 0x0822
#define PCI_VENDOR_ID_DLINK 0x1186
#define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 19/21] [MMC] Quirk for broken reset
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (17 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 18/21] [PCI] Add SDHCI controller ids Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 20/21] [MMC] Force DMA on some controllers Pierre Ossman
` (2 subsequent siblings)
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
Some controllers fail to complete a reset unless you touch the clock
register first.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 29 +++++++++++++++++++++++++++--
1 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 1a12427..617a588 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -32,9 +32,21 @@ static unsigned int debug_nodma = 0;
static unsigned int debug_forcedma = 0;
static unsigned int debug_quirks = 0;
+#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
+
static const struct pci_device_id pci_ids[] __devinitdata = {
- /* handle any SD host controller */
- {PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)},
+ {
+ .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,
+ },
+
+ { /* Generic SD host controller */
+ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
+ },
+
{ /* end: all zeroes */ },
};
@@ -808,6 +820,19 @@ static void sdhci_tasklet_finish(unsigne
if ((mrq->cmd->error != MMC_ERR_NONE) ||
(mrq->data && ((mrq->data->error != MMC_ERR_NONE) ||
(mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) {
+
+ /* Some controllers need this kick or reset won't work here */
+ if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+ unsigned int clock;
+
+ /* This is to force an update */
+ clock = host->clock;
+ host->clock = 0;
+ sdhci_set_clock(host, clock);
+ }
+
+ /* Spec says we should do both at the same time, but Ricoh
+ controllers do not like that. */
sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA);
}
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 20/21] [MMC] Force DMA on some controllers
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (18 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 19/21] [MMC] Quirk for broken reset Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 21/21] [MMC] Remove duplicate error message Pierre Ossman
2006-06-27 19:06 ` [PATCH 00/21] Series short description Pierre Ossman
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
Some controllers incorrectly report that the cannot do DMA. Forcefully
enable it for those that we know it works fine on.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 24 ++++++++++++++++++++++--
1 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 617a588..60d838c 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -33,6 +33,7 @@ static unsigned int debug_forcedma = 0;
static unsigned int debug_quirks = 0;
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
+#define SDHCI_QUIRK_FORCE_DMA (1<<1)
static const struct pci_device_id pci_ids[] __devinitdata = {
{
@@ -40,7 +41,24 @@ static const struct pci_device_id pci_id
.device = PCI_DEVICE_ID_RICOH_R5C822,
.subvendor = PCI_VENDOR_ID_IBM,
.subdevice = PCI_ANY_ID,
- .driver_data = SDHCI_QUIRK_CLOCK_BEFORE_RESET,
+ .driver_data = SDHCI_QUIRK_CLOCK_BEFORE_RESET |
+ SDHCI_QUIRK_FORCE_DMA,
+ },
+
+ {
+ .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,
},
{ /* Generic SD host controller */
@@ -1190,7 +1208,9 @@ static int __devinit sdhci_probe_slot(st
else if (debug_forcedma) {
DBG("DMA forced on\n");
host->flags |= SDHCI_USE_DMA;
- } else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA)
+ } else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
+ 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");
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 21/21] [MMC] Remove duplicate error message
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (19 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 20/21] [MMC] Force DMA on some controllers Pierre Ossman
@ 2006-06-21 14:26 ` Pierre Ossman
2006-06-27 19:06 ` [PATCH 00/21] Series short description Pierre Ossman
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-21 14:26 UTC (permalink / raw)
To: rmk+lkml; +Cc: Pierre Ossman, linux-kernel
When there is remaining blocks untransferred, we get two error messages
saying almost the same thing. Make sure at most one is shown.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 60d838c..8110ce0 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -470,9 +470,7 @@ static void sdhci_finish_data(struct sdh
"though there were blocks left. Please report this "
"to " BUGMAIL ".\n", mmc_hostname(host->mmc));
data->error = MMC_ERR_FAILED;
- }
-
- if (host->size != 0) {
+ } else if (host->size != 0) {
printk(KERN_ERR "%s: %d bytes were left untransferred. "
"Please report this to " BUGMAIL ".\n",
mmc_hostname(host->mmc), host->size);
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH 07/21] [MMC] Correct register order
2006-06-21 14:25 ` [PATCH 07/21] [MMC] Correct register order Pierre Ossman
@ 2006-06-23 16:02 ` Pierre Ossman
0 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-23 16:02 UTC (permalink / raw)
To: rmk+lkml; +Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 47 bytes --]
New version to fix conflicts by recent merge.
[-- Attachment #2: sdhci-reg-order.patch --]
[-- Type: text/x-patch, Size: 2494 bytes --]
[MMC] Correct register order
The sdhci specification states that some registers must be written to in a
specific order.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/sdhci.c | 44 +++++++++++++++++++++++++++-----------------
1 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 877226e..4457881 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -270,16 +270,13 @@ static void sdhci_transfer_pio(struct sd
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 sd
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 sd
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 sd
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",
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH 10/21] [MMC] Avoid sdhci DMA boundaries
2006-06-21 14:26 ` [PATCH 10/21] [MMC] Avoid sdhci DMA boundaries Pierre Ossman
@ 2006-06-23 16:03 ` Pierre Ossman
0 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-23 16:03 UTC (permalink / raw)
To: rmk+lkml; +Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 47 bytes --]
New version to fix conflicts by recent merge.
[-- Attachment #2: sdhci-dma-bounds.patch --]
[-- Type: text/x-patch, Size: 2215 bytes --]
[MMC] 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 <drzeus@drzeus.cx>
---
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 2636cf5..f8c02ee 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -326,6 +326,9 @@ static void sdhci_prepare_data(struct sd
DBG("tsac %d ms nsac %d clk\n",
data->timeout_ns / 1000000, data->timeout_clks);
+ /* Sanity checks */
+ BUG_ON((1 << data->blksz_bits) * 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 sd
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(st
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 f8df28f..8ed2a89 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -23,6 +23,7 @@ #define PCI_SLOT_INFO_FIRST_BAR_MASK 0x
#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
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH 00/21] Series short description
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
` (20 preceding siblings ...)
2006-06-21 14:26 ` [PATCH 21/21] [MMC] Remove duplicate error message Pierre Ossman
@ 2006-06-27 19:06 ` Pierre Ossman
21 siblings, 0 replies; 25+ messages in thread
From: Pierre Ossman @ 2006-06-27 19:06 UTC (permalink / raw)
To: Andrew Morton; +Cc: rmk+lkml, linux-kernel
Andrew, could you have a look and this and queue it in -mm (so that it
can be included in 2.6.18)? Russell is busy with other responsibilities
and I don't want to miss this merge window.
Pierre Ossman wrote:
> The SD Association decided to release a specification for the SDHCI
> controllers. This set of patches updates the current driver, which is
> based on reverse engineering, to comply with the official specification.
>
> It has been tested for quite some time by people on the sdhci-devel list.
> Although it doesn't solve all known issues, it doesn't cause any new ones.
Rgds
Pierre
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2006-06-27 19:06 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-06-21 14:23 [PATCH 00/21] Series short description Pierre Ossman
2006-06-21 14:25 ` [PATCH 01/21] [MMC] Check SDHCI base clock Pierre Ossman
2006-06-21 14:25 ` [PATCH 02/21] [MMC] Print device id Pierre Ossman
2006-06-21 14:25 ` [PATCH 03/21] [MMC] Support for multiple voltages Pierre Ossman
2006-06-21 14:25 ` [PATCH 04/21] [MMC] Fix timeout loops in sdhci Pierre Ossman
2006-06-21 14:25 ` [PATCH 05/21] [MMC] Fix sdhci reset timeout Pierre Ossman
2006-06-21 14:25 ` [PATCH 06/21] [MMC] Proper timeout handling Pierre Ossman
2006-06-21 14:25 ` [PATCH 07/21] [MMC] Correct register order Pierre Ossman
2006-06-23 16:02 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 08/21] [MMC] Fix interrupt handling Pierre Ossman
2006-06-21 14:26 ` [PATCH 09/21] [MMC] Fix sdhci PIO routines Pierre Ossman
2006-06-21 14:26 ` [PATCH 10/21] [MMC] Avoid sdhci DMA boundaries Pierre Ossman
2006-06-23 16:03 ` Pierre Ossman
2006-06-21 14:26 ` [PATCH 11/21] [MMC] Test for invalid block size Pierre Ossman
2006-06-21 14:26 ` [PATCH 12/21] [MMC] Check only relevant inhibit bits Pierre Ossman
2006-06-21 14:26 ` [PATCH 13/21] [MMC] Check controller version Pierre Ossman
2006-06-21 14:26 ` [PATCH 14/21] [MMC] Reset sdhci controller early Pierre Ossman
2006-06-21 14:26 ` [PATCH 15/21] [MMC] More DMA capabilities tests Pierre Ossman
2006-06-21 14:26 ` [PATCH 16/21] [MMC] Support controller specific quirks Pierre Ossman
2006-06-21 14:26 ` [PATCH 17/21] [MMC] Version bump sdhci Pierre Ossman
2006-06-21 14:26 ` [PATCH 18/21] [PCI] Add SDHCI controller ids Pierre Ossman
2006-06-21 14:26 ` [PATCH 19/21] [MMC] Quirk for broken reset Pierre Ossman
2006-06-21 14:26 ` [PATCH 20/21] [MMC] Force DMA on some controllers Pierre Ossman
2006-06-21 14:26 ` [PATCH 21/21] [MMC] Remove duplicate error message Pierre Ossman
2006-06-27 19:06 ` [PATCH 00/21] Series short description Pierre Ossman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).