Re: [PATCH][MMC][6/6] Secure Digital (SD) support : wide bus
From: Pierre Ossman
Date: Sat Mar 05 2005 - 20:59:57 EST
Wide bus support.
This adds 4-bit bus support to the MMC layer. It is designed to
(hopefully) be compatible with a future 4-bit MMC implementation. This
is done by seperating the three different instances of bus width defines:
* Protocol definition: SD_BUS_WIDTH_x
* SCR contents: SD_SCR_BUS_WIDTH_x
* Host mode: MMC_BUS_WIDTH_x
They have the same values atm but drivers should not rely on this.
MMC_BUS_WIDTH_x is not meant to be SD specific.
The MMC layer changes bus width when a card is selected. This is because
the SD spec says that a card is only required to keep a certain bus
width as long as it's selected.
Layers further up do not need to know which mode the host/card is in.
They will only see a change in speed.
Index: linux-sd/include/linux/mmc/host.h
===================================================================
--- linux-sd/include/linux/mmc/host.h (revision 138)
+++ linux-sd/include/linux/mmc/host.h (working copy)
@@ -51,6 +51,11 @@
#define MMC_POWER_OFF 0
#define MMC_POWER_UP 1
#define MMC_POWER_ON 2
+
+ unsigned char bus_width; /* data bus width */
+
+#define MMC_BUS_WIDTH_1 0
+#define MMC_BUS_WIDTH_4 2
};
struct mmc_host_ops {
@@ -69,7 +74,11 @@
unsigned int f_max;
u32 ocr_avail;
char host_name[8];
+
+ unsigned long caps; /* Host capabilities */
+#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */
+
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
Index: linux-sd/include/linux/mmc/protocol.h
===================================================================
--- linux-sd/include/linux/mmc/protocol.h (revision 136)
+++ linux-sd/include/linux/mmc/protocol.h (working copy)
@@ -209,5 +209,12 @@
#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */
#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 */
+
+/*
+ * SD bus widths
+ */
+#define SD_BUS_WIDTH_1 0
+#define SD_BUS_WIDTH_4 2
+
#endif /* MMC_MMC_PROTOCOL_H */
Index: linux-sd/drivers/mmc/mmc.c
===================================================================
--- linux-sd/drivers/mmc/mmc.c (revision 139)
+++ linux-sd/drivers/mmc/mmc.c (working copy)
@@ -335,6 +335,40 @@
if (err != MMC_ERR_NONE)
return err;
+ /*
+ * Default bus width is 1 bit.
+ */
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+
+ /*
+ * We can only change the bus width of the selected
+ * card so therefore we have to put the handling
+ * here.
+ */
+ if (host->caps & MMC_CAP_4_BIT_DATA) {
+ /*
+ * The card is in 1 bit mode by default so
+ * we only need to change if it supports the
+ * wider version.
+ */
+ if (mmc_card_sd(card) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ struct mmc_command cmd;
+ cmd.opcode = SD_APP_SET_BUS_WIDTH;
+ cmd.arg = SD_BUS_WIDTH_4;
+ cmd.flags = MMC_RSP_R1;
+
+ err = mmc_wait_for_app_cmd(host, card->rca, &cmd,
+ CMD_RETRIES);
+ if (err != MMC_ERR_NONE)
+ return err;
+
+ host->ios.bus_width = MMC_BUS_WIDTH_4;
+ }
+ }
+
+ host->ops->set_ios(host, &host->ios);
+
return MMC_ERR_NONE;
}
@@ -644,6 +678,7 @@
host->ios.vdd = bit;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.power_mode = MMC_POWER_UP;
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ops->set_ios(host, &host->ios);
mmc_delay(1);
@@ -661,6 +696,7 @@
host->ios.vdd = 0;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.power_mode = MMC_POWER_OFF;
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ops->set_ios(host, &host->ios);
}