From: "madhu chikkature" <crmadhu210@gmail.com>
To: "Pierre Ossman" <drzeus-list@drzeus.cx>
Cc: linux-kernel@vger.kernel.org
Subject: Re: SDIO card support in Linux
Date: Tue, 5 Sep 2006 18:51:00 -0500 [thread overview]
Message-ID: <f71aedf40609051651k5d36d4fdkb6020685fc366983@mail.gmail.com> (raw)
In-Reply-To: <44F73E37.6030602@drzeus.cx>
[-- Attachment #1: Type: text/plain, Size: 4991 bytes --]
Hi Pierre,
Here is some piece of code that i wrote for SDIO. I use 2.6.10 kernel
and hence i can not really take a diff between the latest kernel
version. But this is not really a patch. So, You can just comment on
my code. I might later on work on the latest kernel versions based on
your comment.I see that there are more discussions happening. Please
pont to me if you have some code already written.
After your previous mail, i see that i can remove the support for CMD3
seperately for SDIO and do it the SD way. But i am not sure how to
maintain the list of SDIO cards seperately.Also some hardware as our
omap does, can support multiple MMC slots, in such cases one slot can
have SDIO and the other MMC. The core needs to cliam the cards from
different lists. So you may see some not so correct parts in my code.
I am on the Texas Instruments MMC/SD/SDIO controller on the omap2 platform.
Regards,
Madhu
On 8/31/06, Pierre Ossman <drzeus-list@drzeus.cx> wrote:
> madhu chikkature wrote:
> > Hi,
> >
> > This is regarding the discussion going on in the list about the
> > support of SDIO cards in Linux. I read some discussion happening to
> > support SDIO cards using the existing Linux MMC core but I could not
> > figure out what would be the direction the community to support the
> > SDIO cards.
> >
>
> I've been casually working on adding SDIO support to the MMC layer. The
> driver model needs quite a bit of changes, so it's a bit of work before
> we have something working. So far I've only got the basic init up and
> running.
>
> The current version of the patch included (ignore the failed chunks for
> mmc_block.c). Feel free to test away. :)
>
> > I have done some work using our own hardware platform runing ARM
> > Linux. My hardware platform can support MMC/SD/SDIO cards.
> >
>
> Out of curiosity, what controller are you using?
>
> >
> > CMD3 of MMC can be reused during the discover cards phase, except that
> > the card will respond back with the RCA.
> >
>
> This is a SD "feature" and not specific to SDIO, so we have code for
> this already.
>
> >
> > With this, is it a fissible solution to have the MMC core do the
> > initialization part of the card by having the CMD sequence for SDIO
> > card (CMD5 and CMD3) in the mmc_setup sequence and maintain the SDIO
> > card list along with MMC/SD?
> >
>
> SD mandates a star topology (just a single card per bus), so we'll just
> force a single card into the list. SD memory cards can actually work on
> a shared bus, SDIO can not. It's not a big problem in practice though.
>
> > The CMD52 and CMD53 can be implemented with a simple pointer to
> > mmc_data structure(An instance of it for SDIO) to send and receive
> > data. Exporting the functions that implement CMD52 and CMD53 need to
> > be done, so that any card specific driver sitting on the top of the
> > MMC core can call these functions to read/write data from the card and
> > configure the card.
>
> I've started implementing some SDIO equivalents of readX/writeX.
>
> >
> > Couple of issues i faced are, how do we maintain the list of SDIO
> > cards? Right now, i am not adding it to the list of MMC cards. SDIO
> > combo cards need more work.
> >
>
> The driver model isn't designed for SDIO cards, so it needs to be
> changed. The design I'm working on couples "functions" to each card,
> where drivers will bind to these functions instead of the card. Search
> the archives for "MMC driver model" and you should find my LKML post
> about it.
>
> > Second issue is related to how well the data transfer commands can be
> > supported in such a way that the middleware, which does not exist as
> > of today to hook the SDIO cards to specific Linux subsystems based on
> > the type of the SDIO cards detected, for exaple WLAN SDIO card may
> > need to talk to the networking subsystem etc.
>
> It shouldn't be different from PCI, USB or any other bus.
>
> >
> > I am leaving the SDIO generic interrupts to the card specific driver.
> > With this setup and few additions to the MMC controller driver, i
> > could get the SDIO cards to be detected and i am able to read and
> > write data from the SDIO card CCCR registers.In fact the MMC/SD and
> > SDIO cards can co-exist.
> >
>
> We need controller help to do interrupts. It's on my todo-list as it
> requires a bit more indirection than "normal" interrupts.
>
> > Does this provide a basic support on which SDIO support can be worked
> > on? or does community have any other idea?
>
> The basic model changes should come first as they will dictate on how
> the rest of the code must be organised. I'd love to see your code though.
>
> >
> > SD support came in at 2.6.14 times and many people still does not have
> > access to SD specification easily. Is there any such issues related to
> > SDIO as well which might prevent the community from supporting SDIO
> > cards?
> >
>
> SDIO is actually easier as there is a spec for at least the protocol and
> Bluetooth cards.
>
> Rgds
> Pierre
>
>
[-- Attachment #2: mmc-core.patch --]
[-- Type: application/octet-stream, Size: 9137 bytes --]
--- 2.6_kernel/drivers/mmc/mmc.c 2006-09-05 17:48:50.000000000 -0500
+++ 2.6_kernel-sdio/drivers/mmc/mmc.c 2006-09-05 17:57:06.000000000 -0500
@@ -709,6 +709,23 @@ mmc_alloc_card(struct mmc_host *host, u3
}
/*
+ * Allocate a new SDIO card, and assign a RCA.
+ */
+static struct mmc_card *
+sdio_alloc_card(struct mmc_host *host, int io_rca)
+{
+ struct mmc_card *card;
+
+ card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
+ if (!card)
+ return ERR_PTR(-ENOMEM);
+ mmc_init_card(card, host);
+ card->rca = io_rca;
+
+ return card;
+}
+
+/*
* Tell attached cards to go to IDLE state
*/
static void mmc_idle_cards(struct mmc_host *host)
@@ -796,6 +813,121 @@ static int mmc_send_op_cond(struct mmc_h
return err;
}
+static int io_send_op_cond(struct mmc_host *host, u32 ocr, u32 * rocr)
+{
+ struct mmc_command cmd;
+ int i, err = 0;
+
+ cmd.opcode = IO_SEND_OP_COND;
+ cmd.arg = ocr;
+ cmd.flags = MMC_RSP_R4;
+
+ for (i = 100; i; i--) {
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err != MMC_ERR_NONE)
+ break;
+
+ if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+ break;
+ mmc_delay(1);
+ err = MMC_ERR_TIMEOUT;
+
+ mmc_delay(10);
+ }
+
+ if (rocr)
+ *rocr = cmd.resp[0];
+
+ return err;
+}
+
+struct mmc_card *sdiocard;
+static int io_select_card(struct mmc_host *host){
+ struct mmc_command cmd;
+ int err = 0;
+
+ cmd.opcode = MMC_SELECT_CARD;
+ cmd.arg = sdiocard->rca;
+ cmd.flags = MMC_RSP_R1;
+
+ err = mmc_wait_for_cmd(host, &cmd,0);
+ return err;
+}
+
+static int read_cia,sdioresp;
+/*
+* Read/Write one byte of data from an SDIO card register of the given function on the resp line.
+*/
+int io_rw_direct(int rwFlag,int funcNum,int rawFlag, unsigned long regAddr,int value){
+ struct mmc_command cmd;
+ int err = 0,trans_err = 1;
+
+ if (!sdiocard)
+ return trans_err;
+
+ if (sdioresp)
+ return trans_err;
+
+ cmd.opcode = IO_RW_DIRECT;
+ if(rwFlag)
+ cmd.arg = ((rwFlag<<31) | (funcNum << 28) | (rawFlag<<27) | (regAddr << 9) | value);
+ else
+ cmd.arg = ((rwFlag<<31) | (funcNum << 28) | (rawFlag<<27) | (regAddr << 9));
+ cmd.flags = MMC_RSP_R5;
+
+ if (read_cia == 0)
+ mmc_claim_host(sdiocard->host);
+
+ if (sdiocard->host->ios.clock == 0){
+ printk(KERN_ERR"CMD52 when the host is suspended\n");
+ mmc_release_host(sdiocard->host);
+ return trans_err;
+ }
+
+ err = mmc_wait_for_cmd(sdiocard->host, &cmd,0);
+
+ if (err!= MMC_ERR_NONE)
+ return err;
+
+ if (read_cia == 0)
+ mmc_release_host(sdiocard->host);
+
+ if (rwFlag)
+ return err;
+ else
+ return cmd.resp[0] & 0xFF;
+}
+EXPORT_SYMBOL(io_rw_direct);
+/*
+* Read/Write bytec number of bytes from SDIO card registers of a given function on the data lines.
+*/
+int io_rw_extended(struct mmc_request *mrq,int rwFlag,int funcNum,int blkMode,int opcode, unsigned long regAddr, int bytec){
+ struct mmc_command cmd;
+ int err = 0,trans_err = 1;
+
+ if (!sdiocard)
+ return trans_err;
+
+ mrq->cmd = &cmd;
+ cmd.opcode = IO_RW_EXTENDED;
+ cmd.arg = ((rwFlag<<31) | (funcNum << 28) | (blkMode<<27) | (opcode << 26) | (regAddr << 9) | bytec);
+ cmd.flags = MMC_RSP_R5;
+
+ mmc_claim_host(sdiocard->host);
+
+ if (sdiocard->host->ios.clock == 0){
+ printk(KERN_ERR"CMD53 when the host is suspended\n");
+ mmc_release_host(sdiocard->host);
+ return trans_err;
+ }
+
+ err = mmc_wait_for_req(sdiocard->host, mrq);
+
+ mmc_release_host(sdiocard->host);
+
+ return err;
+}
+EXPORT_SYMBOL(io_rw_extended);
static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
@@ -835,11 +967,27 @@ static int mmc_send_app_op_cond(struct m
static void mmc_discover_cards(struct mmc_host *host)
{
struct mmc_card *card;
- unsigned int first_rca = 1, err;
+ unsigned int first_rca = 1,io_rca, err;
while (1) {
struct mmc_command cmd;
+ if (host->mode == MMC_MODE_SDIO){
+ /* Read the rca of an SDIO card.
+ * */
+ cmd.opcode = SDIO_SEND_RELATIVE_ADDR;
+ cmd.arg = 0x0;
+ cmd.flags = MMC_RSP_R6;
+
+ err = mmc_wait_for_cmd(host, &cmd,0);
+
+ if (err == MMC_ERR_NONE){
+ io_rca = cmd.resp[0] & 0xFFFF0000;
+ sdiocard = sdio_alloc_card(host, io_rca);
+ break;
+ }
+ }
+
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = MMC_RSP_R2;
@@ -1052,6 +1200,70 @@ static unsigned int mmc_read_ext_csd(str
return err;
}
+
+/*
+ * Read the CCCR register contents of an I/O card.
+ */
+static void io_read_cia(struct mmc_host *host)
+{
+ int sdio_rev,io_cardType,io_busWidth,io_cardCap;
+
+ read_cia = 1;
+ sdioresp = io_select_card(host);
+
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ if (sdioresp == 0){
+ if (host->caps & MMC_CAP_4_BIT_DATA) {
+ io_cardCap = io_rw_direct(READ,FUNCTION_0,0,CCCR_CARD_CAPACITY,0x0);
+ if (io_cardCap & (1<<6)){
+ host->ios.clock = host->f_min;
+ host->ops->set_ios(host, &host->ios);
+ if (io_cardCap & (1<<7)){
+ io_busWidth = io_rw_direct(READ,FUNCTION_0,0,CCCR_BUS_INTF_CONTROL,0x0);
+ io_busWidth |= 0x2;
+ io_rw_direct(WRITE,FUNCTION_0,0,CCCR_BUS_INTF_CONTROL,io_busWidth);
+ host->ios.bus_width = MMC_BUS_WIDTH_4;
+ }
+ } else {
+ host->ios.clock = 25000000;
+ host->ios.bus_width = MMC_BUS_WIDTH_4;
+ host->ops->set_ios(host, &host->ios);
+ io_busWidth = io_rw_direct(READ,FUNCTION_0,0,CCCR_BUS_INTF_CONTROL,0x0);
+ io_busWidth |= 0x2;
+ io_rw_direct(WRITE,FUNCTION_0,0,CCCR_BUS_INTF_CONTROL,io_busWidth);
+
+ }
+
+ }
+
+ sdio_rev = io_rw_direct(READ,FUNCTION_0,0,CCCR_SDIO_REVISION,0x0);
+ /*
+ * Enable all the functions in the card.
+ */
+ io_rw_direct(WRITE,FUNCTION_0,0,CCCR_IO_ENABLE,0xFF);
+ io_rw_direct(WRITE,FUNCTION_0,0,CCCR_FUNCTION_SELECT,io_rw_direct(READ,FUNCTION_0,0,CCCR_FUNCTION_SELECT,0x0) | 0x1);
+
+ io_cardType = io_rw_direct(READ,FUNCTION_1,0,FBR1,0x0);
+ if (io_cardType == 0x0)
+ printk(KERN_INFO"SDIO WLAN card detected\n");
+ else if (io_cardType == 0x1)
+ printk(KERN_INFO"SDIO UART interface detected\n");
+ else if (io_cardType == 0x2)
+ printk(KERN_INFO"SDIO thin bluetooth interface detected\n");
+ else if (io_cardType == 0x3)
+ printk(KERN_INFO"SDIO complete bluetooth interface detected\n");
+ else if (io_cardType == 0x4)
+ printk(KERN_INFO"SDIO GPS standard interface detected\n");
+ else if (io_cardType == 0x5)
+ printk(KERN_INFO"SDIO Camera standard interface detected\n");
+ else if (io_cardType == 0x6)
+ printk(KERN_INFO"SDIO PHS Radio standard interface detected\n");
+ if (sdio_rev == 0x00)
+ printk(KERN_INFO"SDIO revision 1.00\n");
+
+ }
+ read_cia = 0;
+}
static unsigned int mmc_calculate_clock(struct mmc_host *host)
{
struct mmc_card *card;
@@ -1453,18 +1665,24 @@ static void mmc_setup(struct mmc_host *h
int err;
u32 ocr;
- host->mode = MMC_MODE_SD;
+ host->mode = MMC_MODE_SDIO;
mmc_power_up(host);
mmc_idle_cards(host);
+ err = io_send_op_cond(host, 0, &ocr);
+ /*
+ * * If we fail to detect any SDIO cards then try
+ * * searching for SD cards.
+ */
+ if (err != MMC_ERR_NONE) {
err = mmc_send_app_op_cond(host, 0, &ocr);
/*
* * If we fail to detect any SD cards then try
* * searching for MMC cards.
*/
- if (err != MMC_ERR_NONE){
+ if (err != MMC_ERR_NONE){
host->mode = MMC_MODE_MMC;
err = mmc_send_op_cond(host, 0, &ocr);
if (err != MMC_ERR_NONE)
@@ -1473,6 +1691,7 @@ static void mmc_setup(struct mmc_host *h
else {
host->mode = MMC_MODE_SD;
}
+ }
host->ocr = mmc_select_voltage(host, ocr);
/*
@@ -1511,7 +1730,10 @@ static void mmc_setup(struct mmc_host *h
* all get the idea that they should be ready for CMD2.
* (My SanDisk card seems to need this.)
*/
- if (host->mode == MMC_MODE_SD)
+ if (host->mode == MMC_MODE_SDIO){
+ io_send_op_cond(host, host->ocr, NULL);
+ }
+ else if (host->mode == MMC_MODE_SD)
mmc_send_app_op_cond(host, host->ocr, NULL);
else
mmc_send_op_cond(host, host->ocr, NULL);
@@ -1523,7 +1745,10 @@ static void mmc_setup(struct mmc_host *h
*/
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
host->ops->set_ios(host, &host->ios);
- mmc_read_csds(host);
+ if (host->mode == MMC_MODE_MMC || host->mode == MMC_MODE_SD)
+ mmc_read_csds(host);
+ else
+ io_read_cia(host);
if (host->mode == MMC_MODE_SD)
mmc_read_scrs(host);
@@ -1600,8 +1825,16 @@ static void mmc_rescan(void *data)
* If we discover that there are no cards on the
* bus, turn off the clock and power down.
*/
+ if (host->mode == MMC_MODE_SDIO ){
+ if (sdioresp == 1){
+ kfree(sdiocard);
+ mmc_power_off(host);
+ }
+ }
+ else {
if (list_empty(&host->cards))
mmc_power_off(host);
+ }
}
[-- Attachment #3: mmc-include.patch --]
[-- Type: application/octet-stream, Size: 3806 bytes --]
diff -purN 2.6_kernel/include/linux/mmc/card.h 2.6_kernel-sdio/include/linux/mmc/card.h
--- 2.6_kernel/include/linux/mmc/card.h 2006-09-05 17:53:31.000000000 -0500
+++ 2.6_kernel-sdio/include/linux/mmc/card.h 2006-09-05 17:56:49.000000000 -0500
@@ -150,4 +150,32 @@ static inline int mmc_card_claim_host(st
/* ARG for CMD6 to change bus width.
*/
#define BUS_WIDTH_CHANGE_ARG 0x03B70100
+
+/*
+ * SDIO card CCCR register offsets.
+ */
+#define CCCR_SDIO_REVISION 0x00
+#define CCCR_SD_SPECIFICATION_REVISION 0x01
+#define CCCR_IO_ENABLE 0x02
+#define CCCR_IO_READY 0x03
+#define CCCR_INT_ENABLE 0x04
+#define CCCR_INT_PENDING 0x05
+#define CCCR_IO_ABORT 0x06
+#define CCCR_BUS_INTF_CONTROL 0x07
+#define CCCR_CARD_CAPACITY 0x08
+#define CCCR_COMMON_CIS_PTR1 0x09
+#define CCCR_COMMON_CIS_PTR2 0x0A
+#define CCCR_COMMON_CIS_PTR3 0x0B
+#define CCCR_BUS_SUSPEND 0x0C
+#define CCCR_FUNCTION_SELECT 0x0D
+#define CCCR_EXEC_FLAGS 0x0E
+#define CCCR_READY_FLAGS 0x0F
+#define CCCR_FN0_BLK_SIZE1 0x10
+#define CCCR_FN0_BLK_SIZE2 0x11
+#define FBR1 0x100
+
+#define FUNCTION_0 0
+#define FUNCTION_1 1
+#define READ 0
+#define WRITE 1
#endif
diff -purN 2.6_kernel/include/linux/mmc/host.h 2.6_kernel-sdio/include/linux/mmc/host.h
--- 2.6_kernel/include/linux/mmc/host.h 2006-09-05 17:55:22.000000000 -0500
+++ 2.6_kernel-sdio/include/linux/mmc/host.h 2006-09-05 17:56:38.000000000 -0500
@@ -106,6 +106,7 @@ struct mmc_host {
unsigned int mode; /* current card mode of host */
#define MMC_MODE_MMC 0
#define MMC_MODE_SD 1
+#define MMC_MODE_SDIO 2
struct list_head cards; /* devices attached to this host */
diff -purN 2.6_kernel/include/linux/mmc/mmc.h 2.6_kernel-sdio/include/linux/mmc/mmc.h
--- 2.6_kernel/include/linux/mmc/mmc.h 2006-09-05 17:52:12.000000000 -0500
+++ 2.6_kernel-sdio/include/linux/mmc/mmc.h 2006-09-05 17:56:13.000000000 -0500
@@ -38,6 +38,8 @@ struct mmc_command {
#define MMC_RSP_R1B (MMC_RSP_SHORT|MMC_RSP_CRC|MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_LONG|MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_SHORT)
+#define MMC_RSP_R4 (MMC_RSP_SHORT)
+#define MMC_RSP_R5 (MMC_RSP_SHORT)
#define MMC_RSP_R6 (MMC_RSP_SHORT|MMC_RSP_CRC)
unsigned int retries; /* max number of retries */
@@ -78,9 +80,16 @@ struct mmc_data {
struct scatterlist *sg; /* I/O scatter list */
};
+struct sdio_data {
+ u32 *sdio_buffer_phys;
+ void *sdio_buffer_virt;
+ struct mmc_request *mrq; /* assoicated request */
+};
+
struct mmc_request {
struct mmc_command *cmd;
struct mmc_data *data;
+ struct sdio_data *sdiodata;
struct mmc_command *stop;
void *done_data; /* completion data */
diff -purN 2.6_kernel/include/linux/mmc/protocol.h 2.6_kernel-sdio/include/linux/mmc/protocol.h
--- 2.6_kernel/include/linux/mmc/protocol.h 2006-09-05 17:54:41.000000000 -0500
+++ 2.6_kernel-sdio/include/linux/mmc/protocol.h 2006-09-05 17:56:26.000000000 -0500
@@ -90,6 +90,12 @@
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
#define SD_APP_SEND_SCR 51 /* adtc R1 */
+/* SDIO commands type argument response */
+/* This is basically the same command as for MMC with some quirks. */
+#define SDIO_SEND_RELATIVE_ADDR 3 /* bcr R6 */
+#define IO_SEND_OP_COND 5
+#define IO_RW_DIRECT 52
+#define IO_RW_EXTENDED 53
/*
MMC status in R1
Type
next prev parent reply other threads:[~2006-09-05 23:51 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-08-31 15:04 madhu chikkature
2006-08-31 19:53 ` Pierre Ossman
2006-09-01 15:48 ` Ian Stirling
2006-09-01 15:53 ` Pierre Ossman
2006-09-05 23:51 ` madhu chikkature [this message]
2006-09-06 5:20 ` Pierre Ossman
2006-09-06 23:22 ` madhu chikkature
2006-09-07 14:54 ` Pierre Ossman
2006-09-14 12:01 ` Bhavani
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f71aedf40609051651k5d36d4fdkb6020685fc366983@mail.gmail.com \
--to=crmadhu210@gmail.com \
--cc=drzeus-list@drzeus.cx \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®