linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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).