* SDIO card support in Linux
@ 2006-08-31 15:04 madhu chikkature
2006-08-31 19:53 ` Pierre Ossman
0 siblings, 1 reply; 9+ messages in thread
From: madhu chikkature @ 2006-08-31 15:04 UTC (permalink / raw)
To: linux-kernel
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 have done some work using our own hardware platform runing ARM
Linux. My hardware platform can support MMC/SD/SDIO cards.
>From the SDIO specification, i understand that we need to add support
for the following commonds in the MMC core to support SDIO cards.
IO_SEND_OP_COND -- CMD5 --- This command is very much simillar to CMD1 of MMC.
CMD3 of MMC can be reused during the discover cards phase, except that
the card will respond back with the RCA.
IO_RW_DIRECT -- CMD52
IO_RW_EXTENDED -- CMD53
The above two are data transfer commands. CMD52 does not use data
lines. This command can be used to read/write 1 byte of data on the
CMD line.
CMD53 is equavalent to CMD17/CMD18/CMD24/CMD25 of MMC to read/write
data on data lines.
The SDIO spec only exposes a few set of registers called CARD COMMON
CONTROL REGISTERS which are common to all types of SDIO cards.
Functionality specific SDIO card registers are left to the vendor to describe.
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?
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.
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.
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.
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.
Does this provide a basic support on which SDIO support can be worked
on? or does community have any other idea?
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?
Please let me know views on this.
Regards,
Madhu
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
2006-08-31 15:04 SDIO card support in Linux madhu chikkature
@ 2006-08-31 19:53 ` Pierre Ossman
2006-09-01 15:48 ` Ian Stirling
2006-09-05 23:51 ` madhu chikkature
0 siblings, 2 replies; 9+ messages in thread
From: Pierre Ossman @ 2006-08-31 19:53 UTC (permalink / raw)
To: madhu chikkature; +Cc: linux-kernel
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
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
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
1 sibling, 1 reply; 9+ messages in thread
From: Ian Stirling @ 2006-09-01 15:48 UTC (permalink / raw)
To: Pierre Ossman; +Cc: madhu chikkature, linux-kernel
Pierre Ossman 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.
<snip>
>> 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.
Is this true in SD-1 bit mode, or SPI?
I see nothing on a quick read-through of the abbreviated SDIO spec
precluding this.
Of course, it'd mean wire-or'd interrupt lines.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
2006-09-01 15:48 ` Ian Stirling
@ 2006-09-01 15:53 ` Pierre Ossman
0 siblings, 0 replies; 9+ messages in thread
From: Pierre Ossman @ 2006-09-01 15:53 UTC (permalink / raw)
To: Ian Stirling; +Cc: madhu chikkature, linux-kernel
Ian Stirling wrote:
>>
>> 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.
>
> Is this true in SD-1 bit mode, or SPI?
Yes, no. :)
> I see nothing on a quick read-through of the abbreviated SDIO spec
> precluding this.
> Of course, it'd mean wire-or'd interrupt lines.
>
The problem is that there is no "king-of-the-hill" mechanism in the SDIO
init sequence (like there is in SD mem). The protocol assumes that the
desired card always receives the command.
Rgds
Pierre
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
2006-08-31 19:53 ` Pierre Ossman
2006-09-01 15:48 ` Ian Stirling
@ 2006-09-05 23:51 ` madhu chikkature
2006-09-06 5:20 ` Pierre Ossman
2006-09-14 12:01 ` Bhavani
1 sibling, 2 replies; 9+ messages in thread
From: madhu chikkature @ 2006-09-05 23:51 UTC (permalink / raw)
To: Pierre Ossman; +Cc: linux-kernel
[-- 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
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
2006-09-05 23:51 ` madhu chikkature
@ 2006-09-06 5:20 ` Pierre Ossman
2006-09-06 23:22 ` madhu chikkature
2006-09-14 12:01 ` Bhavani
1 sibling, 1 reply; 9+ messages in thread
From: Pierre Ossman @ 2006-09-06 5:20 UTC (permalink / raw)
To: madhu chikkature; +Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1029 bytes --]
madhu chikkature wrote:
> 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.
>
Your design is a bit lacking yes as it doesn't properly reuse the
structures in place. Have a look at the version I'm working on instead.
Rgds
Pierre
[-- Attachment #2: sdio-init.patch --]
[-- Type: text/x-patch, Size: 24886 bytes --]
[MMC] SDIO init support
Modify the MMC detection routine to identify and initialise SDIO cards.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
---
drivers/mmc/Makefile | 2
drivers/mmc/mmc.c | 304 +++++++++++++++++++++++++++++++--------
drivers/mmc/mmc.h | 4 +
drivers/mmc/mmc_block.c | 6 -
drivers/mmc/mmc_sysfs.c | 18 ++
drivers/mmc/sdio.c | 90 ++++++++++++
include/linux/mmc/card.h | 26 +++
include/linux/mmc/host.h | 4 -
include/linux/mmc/ids.h | 14 ++
include/linux/mmc/mmc.h | 2
include/linux/mmc/protocol.h | 19 ++
include/linux/mmc/sdio.h | 11 +
include/linux/mod_devicetable.h | 9 +
13 files changed, 433 insertions(+), 76 deletions(-)
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index d2957e3..4847ebe 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -24,7 +24,7 @@ obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o
-mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
+mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o sdio.o
ifeq ($(CONFIG_MMC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index df47d00..4680459 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
- * SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved.
+ * SD/SDIO support Copyright (C) 2005-2006 Pierre Ossman, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -23,6 +23,7 @@ #include <linux/scatterlist.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
+#include <linux/mmc/ids.h>
#include "mmc.h"
@@ -367,7 +368,7 @@ static int mmc_select_card(struct mmc_ho
* we only need to change if it supports the
* wider version.
*/
- if (mmc_card_sd(card) &&
+ if (mmc_card_sdmem(card) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
struct mmc_command cmd;
cmd.opcode = SD_APP_SET_BUS_WIDTH;
@@ -466,6 +467,7 @@ static void mmc_decode_cid(struct mmc_ca
memset(&card->cid, 0, sizeof(struct mmc_cid));
if (mmc_card_sd(card)) {
+ BUG_ON(!mmc_card_sdmem(card));
/*
* SD doesn't currently have a version field so we will
* have to assume we can parse this.
@@ -542,6 +544,8 @@ static void mmc_decode_csd(struct mmc_ca
u32 *resp = card->raw_csd;
if (mmc_card_sd(card)) {
+ BUG_ON(!mmc_card_sdmem(card));
+
csd_struct = UNSTUFF_BITS(resp, 126, 2);
if (csd_struct != 0) {
printk("%s: unrecognised CSD structure version %d\n",
@@ -618,7 +622,7 @@ static void mmc_decode_scr(struct mmc_ca
unsigned int scr_struct;
u32 resp[4];
- BUG_ON(!mmc_card_sd(card));
+ BUG_ON(!mmc_card_sdmem(card));
resp[3] = card->raw_scr[1];
resp[2] = card->raw_scr[0];
@@ -653,28 +657,46 @@ static struct mmc_card *mmc_find_card(st
* Allocate a new MMC card, and assign a unique RCA.
*/
static struct mmc_card *
-mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
+mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca,
+ unsigned int funcs)
{
struct mmc_card *card, *c;
- unsigned int rca = *frca;
+ unsigned int rca, i;
card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);
mmc_init_card(card, host);
- memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid));
+
+ card->functions = kmalloc(sizeof(struct mmc_function) * funcs, GFP_KERNEL);
+ if (!card->functions) {
+ mmc_remove_card(card);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ card->n_functions = funcs;
+
+ for (i = 0;i < card->n_functions;i++)
+ mmc_init_function(&card->functions[i], card);
+
+ if (raw_cid)
+ memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid));
+
+ if (frca) {
+ rca = *frca;
again:
- list_for_each_entry(c, &host->cards, node)
- if (c->rca == rca) {
- rca++;
- goto again;
- }
+ list_for_each_entry(c, &host->cards, node)
+ if (c->rca == rca) {
+ rca++;
+ goto again;
+ }
- card->rca = rca;
+ card->rca = rca;
- *frca = rca;
+ *frca = rca;
+ }
return card;
}
@@ -803,6 +825,50 @@ static int mmc_send_app_op_cond(struct m
return err;
}
+static int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+ struct mmc_command cmd;
+ int i, err = 0;
+
+ cmd.opcode = SD_IO_SEND_OP_COND;
+ cmd.arg = ocr & 0x00FFFF00;
+ cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR;
+
+ 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;
+
+ err = MMC_ERR_TIMEOUT;
+
+ mmc_delay(10);
+ }
+
+ if (rocr)
+ *rocr = cmd.resp[0];
+
+ return err;
+}
+
+static void mmc_send_rca(struct mmc_card *card)
+{
+ struct mmc_command cmd;
+ unsigned int err;
+
+ cmd.opcode = SD_SEND_RELATIVE_ADDR;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES);
+ if (err != MMC_ERR_NONE)
+ mmc_card_set_dead(card);
+
+ card->rca = cmd.resp[0] >> 16;
+}
+
/*
* Discover cards by requesting their CID. If this command
* times out, it is not an error; there are no further cards
@@ -834,31 +900,13 @@ static void mmc_discover_cards(struct mm
break;
}
- card = mmc_find_card(host, cmd.resp);
- if (!card) {
- card = mmc_alloc_card(host, cmd.resp, &first_rca);
- if (IS_ERR(card)) {
- err = PTR_ERR(card);
- break;
- }
- list_add(&card->node, &host->cards);
- }
-
- card->state &= ~MMC_STATE_DEAD;
-
if (host->mode == MMC_MODE_SD) {
- mmc_card_set_sd(card);
+ BUG_ON(list_empty(&host->cards));
+ card = mmc_list_to_card(host->cards.next);
- cmd.opcode = SD_SEND_RELATIVE_ADDR;
- cmd.arg = 0;
- cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
-
- err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
- if (err != MMC_ERR_NONE)
- mmc_card_set_dead(card);
- else {
- card->rca = cmd.resp[0] >> 16;
+ mmc_send_rca(card);
+ if (!mmc_card_dead(card)) {
if (!host->ops->get_ro) {
printk(KERN_WARNING "%s: host does not "
"support reading read-only "
@@ -870,6 +918,21 @@ static void mmc_discover_cards(struct mm
}
}
} else {
+ card = mmc_find_card(host, cmd.resp);
+ if (!card) {
+ card = mmc_alloc_card(host, cmd.resp,
+ &first_rca, 1);
+ if (IS_ERR(card)) {
+ err = PTR_ERR(card);
+ break;
+ }
+ list_add(&card->node, &host->cards);
+ card->functions[0].vendor = MMC_VENDOR_STORAGE;
+ card->functions[0].device = MMC_DEVICE_MMC_STORAGE;
+ }
+
+ card->state &= ~MMC_STATE_DEAD;
+
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
@@ -879,6 +942,13 @@ static void mmc_discover_cards(struct mm
mmc_card_set_dead(card);
}
}
+
+ if (host->mode == MMC_MODE_SD) {
+ BUG_ON(list_empty(&host->cards));
+ card = mmc_list_to_card(&host->cards);
+ if (!card->rca)
+ mmc_card_set_dead(card);
+ }
}
static void mmc_read_csds(struct mmc_host *host)
@@ -924,7 +994,7 @@ static void mmc_read_scrs(struct mmc_hos
list_for_each_entry(card, &host->cards, node) {
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
continue;
- if (!mmc_card_sd(card))
+ if (!mmc_card_sdmem(card))
continue;
err = mmc_select_card(host, card);
@@ -1004,6 +1074,9 @@ static unsigned int mmc_calculate_clock(
if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr)
max_dtr = card->csd.max_dtr;
+ if (max_dtr < 400000)
+ max_dtr = 400000;
+
pr_debug("%s: selected %d.%03dMHz transfer rate\n",
mmc_hostname(host),
max_dtr / 1000000, (max_dtr / 1000) % 1000);
@@ -1042,41 +1115,150 @@ static void mmc_check_cards(struct mmc_h
}
}
-static void mmc_setup(struct mmc_host *host)
+static int mmc_initial_detect(struct mmc_host *host)
{
- if (host->ios.power_mode != MMC_POWER_ON) {
- int err;
- u32 ocr;
+ int err;
+ u32 ocr;
+ int is_sdio;
+
+ host->mode = MMC_MODE_SD;
+ is_sdio = 0;
+
+ mmc_power_up(host);
+ mmc_idle_cards(host);
+
+ err = mmc_send_io_op_cond(host, 0, &ocr);
+
+ if (err == MMC_ERR_NONE) {
+ u32 memocr;
- host->mode = MMC_MODE_SD;
+ is_sdio = 1;
- mmc_power_up(host);
- mmc_idle_cards(host);
+ memocr = ~0;
+ /* Combo card */
+ if (ocr & SDIO_MEM_PRES) {
+ err = mmc_send_app_op_cond(host, 0, &memocr);
+ if (err != MMC_ERR_NONE) {
+ printk(KERN_ERR "%s: error requesting "
+ "memory OCR: %d\n",
+ mmc_hostname(host), err);
+ return -EIO;
+ }
+ }
+
+ if (memocr & ocr)
+ ocr = (ocr & 0xFF000000) | (memocr & ocr);
+ else {
+ printk(KERN_WARNING "%s: memory and IO OCR do "
+ "not overlap. choosing IO.\n",
+ mmc_hostname(host));
+ ocr &= ~SDIO_MEM_PRES;
+ }
+ } else
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) {
+ host->mode = MMC_MODE_MMC;
+
+ err = mmc_send_op_cond(host, 0, &ocr);
+ if (err != MMC_ERR_NONE)
+ return -ENODEV;
+ }
+
+ host->ocr = mmc_select_voltage(host, ocr);
+ if (host->ocr == 0)
+ return -ENODEV;
+
+ /*
+ * Since we're changing the OCR value, we seem to
+ * need to tell some cards to go back to the idle
+ * state. We wait 1ms to give cards time to
+ * respond.
+ */
+ mmc_idle_cards(host);
+
+ /*
+ * SD (star) topology assumes that exactly one card is always
+ * present, so do the setup here.
+ */
+ if (host->mode == MMC_MODE_SD) {
+ struct mmc_card *card;
+ unsigned int funcs;
+
+ if (is_sdio) {
+ funcs = (ocr >> 28) & 0x7;
+ if (ocr & SDIO_MEM_PRES)
+ funcs++;
+ } else
+ funcs = 1;
+
+ card = mmc_alloc_card(host, NULL, NULL, funcs);
+ if (IS_ERR(card))
+ return -ENOMEM;
+
+ if (is_sdio)
+ mmc_card_set_sdio(card);
+ if (!is_sdio || (ocr & SDIO_MEM_PRES))
+ mmc_card_set_sdmem(card);
+
+ BUG_ON(!list_empty(&host->cards));
+ list_add(&card->node, &host->cards);
+
+ if (mmc_card_sdmem(card)) {
+ card->functions[0].vendor = MMC_VENDOR_STORAGE;
+ card->functions[0].device = MMC_DEVICE_SD_STORAGE;
+ }
+
/*
- * If we fail to detect any SD cards then try
- * searching for MMC cards.
+ * For combo cards, voltage and RCA cover both parts.
*/
- if (err != MMC_ERR_NONE) {
- host->mode = MMC_MODE_MMC;
+ if (mmc_card_sdmem(card)) {
+ mmc_send_app_op_cond(host, host->ocr, NULL);
- err = mmc_send_op_cond(host, 0, &ocr);
- if (err != MMC_ERR_NONE)
- return;
+ mmc_discover_cards(host);
+ } else {
+ mmc_send_io_op_cond(host, host->ocr, NULL);
+
+ mmc_send_rca(card);
+ }
+
+ /*
+ * Ok, now switch to push-pull mode.
+ */
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ mmc_set_ios(host);
+
+ if (mmc_card_sdmem(card)) {
+ mmc_read_csds(host);
+ mmc_read_scrs(host);
}
- host->ocr = mmc_select_voltage(host, ocr);
+ if (mmc_card_sdio(card) && !mmc_card_dead(card)) {
+ mmc_select_card(host, card);
+ sdio_discover_functions(card);
+ }
+ }
+ return 0;
+}
+
+static void mmc_setup(struct mmc_host *host)
+{
+ if (host->ios.power_mode != MMC_POWER_ON) {
+ if (mmc_initial_detect(host))
+ return;
+ if (host->mode == MMC_MODE_SD)
+ return;
+ } else if (host->mode == MMC_MODE_SD) {
/*
- * Since we're changing the OCR value, we seem to
- * need to tell some cards to go back to the idle
- * state. We wait 1ms to give cards time to
- * respond.
+ * In star mode there will be no additions to a running bus.
*/
- if (host->ocr)
- mmc_idle_cards(host);
+ return;
} else {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.clock = host->f_min;
@@ -1105,10 +1287,7 @@ 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)
- mmc_send_app_op_cond(host, host->ocr, NULL);
- else
- mmc_send_op_cond(host, host->ocr, NULL);
+ mmc_send_op_cond(host, host->ocr, NULL);
mmc_discover_cards(host);
@@ -1119,9 +1298,6 @@ static void mmc_setup(struct mmc_host *h
mmc_set_ios(host);
mmc_read_csds(host);
-
- if (host->mode == MMC_MODE_SD)
- mmc_read_scrs(host);
}
diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h
index 97bae00..abcf631 100644
--- a/drivers/mmc/mmc.h
+++ b/drivers/mmc/mmc.h
@@ -14,8 +14,12 @@ void mmc_init_card(struct mmc_card *card
int mmc_register_card(struct mmc_card *card);
void mmc_remove_card(struct mmc_card *card);
+void mmc_init_function(struct mmc_function *func, struct mmc_card *card);
+
struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev);
int mmc_add_host_sysfs(struct mmc_host *host);
void mmc_remove_host_sysfs(struct mmc_host *host);
void mmc_free_host_sysfs(struct mmc_host *host);
+
+void sdio_discover_functions(struct mmc_card *card);
#endif
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index fedd375..419c1a3 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -263,7 +263,7 @@ static int mmc_blk_issue_rq(struct mmc_q
/*
* SD cards use a 100 multiplier and has a upper limit
*/
- if (mmc_card_sd(card)) {
+ if (mmc_card_sdmem(card)) {
unsigned int limit_us, timeout_us;
brq.data.timeout_ns *= 10;
@@ -288,7 +288,7 @@ static int mmc_blk_issue_rq(struct mmc_q
* Only SD cards support querying the number of successfully
* written sectors.
*/
- if ((rq_data_dir(req) == WRITE) && !mmc_card_sd(card))
+ if ((rq_data_dir(req) == WRITE) && !mmc_card_sdmem(card))
brq.data.blocks = 1;
if (rq_data_dir(req) == READ) {
@@ -376,7 +376,7 @@ #endif
* If this is an SD card and we're writing, we can first mark the
* known good sectors as ok.
*/
- if ((rq_data_dir(req) == WRITE) && mmc_card_sd(card)) {
+ if ((rq_data_dir(req) == WRITE) && mmc_card_sdmem(card)) {
u32 blocks;
unsigned int bytes;
diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c
index a2a35fd..e35b316 100644
--- a/drivers/mmc/mmc_sysfs.c
+++ b/drivers/mmc/mmc_sysfs.c
@@ -65,6 +65,9 @@ static void mmc_release_card(struct devi
{
struct mmc_card *card = dev_to_mmc_card(dev);
+ if (card->functions)
+ kfree(card->functions);
+
kfree(card);
}
@@ -215,7 +218,7 @@ int mmc_register_card(struct mmc_card *c
ret = device_add(&card->dev);
if (ret == 0) {
- if (mmc_card_sd(card)) {
+ if (mmc_card_sdmem(card)) {
ret = device_create_file(&card->dev, &mmc_dev_attr_scr);
if (ret)
device_del(&card->dev);
@@ -231,7 +234,7 @@ int mmc_register_card(struct mmc_card *c
void mmc_remove_card(struct mmc_card *card)
{
if (mmc_card_present(card)) {
- if (mmc_card_sd(card))
+ if (mmc_card_sdmem(card))
device_remove_file(&card->dev, &mmc_dev_attr_scr);
device_del(&card->dev);
@@ -241,6 +244,17 @@ void mmc_remove_card(struct mmc_card *ca
}
+/*
+ * Internal function. Initialise a MMC card function structure.
+ */
+void mmc_init_function(struct mmc_function *func, struct mmc_card *card)
+{
+ memset(func, 0, sizeof(struct mmc_function));
+ func->card = card;
+ func->number = (unsigned int)-1;
+}
+
+
static void mmc_host_classdev_release(struct class_device *dev)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
diff --git a/drivers/mmc/sdio.c b/drivers/mmc/sdio.c
new file mode 100644
index 0000000..8f73378
--- /dev/null
+++ b/drivers/mmc/sdio.c
@@ -0,0 +1,90 @@
+/*
+ * linux/drivers/mmc/sdio.c
+ *
+ * Copyright (C) 2006 Pierre Ossman, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/protocol.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/ids.h>
+
+#define SDIO_ADDR_MASK 0x1FFFF
+
+static unsigned char __sdio_readb(const struct mmc_card *card,
+ unsigned int func, unsigned int addr)
+{
+ struct mmc_command cmd;
+ int err;
+
+ BUG_ON(addr & ~SDIO_ADDR_MASK);
+ BUG_ON(func > 7);
+
+ cmd.opcode = SD_IO_RW_DIRECT;
+ cmd.arg = (func << 28) || (addr << 9);
+ cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err != MMC_ERR_NONE)
+ return 0xFF;
+
+ if (cmd.resp[0] & (R5_ERROR | R5_FUNCTION_NUMBER | R5_OUT_OF_RANGE))
+ return 0xFF;
+
+ return cmd.resp[0] & 0xFF;
+}
+
+static void __sdio_writeb(const struct mmc_card *card, unsigned int func,
+ unsigned int addr, unsigned char data)
+{
+ struct mmc_command cmd;
+
+ BUG_ON(addr & ~SDIO_ADDR_MASK);
+ BUG_ON(func > 7);
+
+ cmd.opcode = SD_IO_RW_DIRECT;
+ cmd.arg = (1<<31) || (func << 28) || (addr << 9) || data;
+ cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ mmc_wait_for_cmd(card->host, &cmd, 0);
+}
+
+unsigned char sdio_readb(const struct mmc_function *func, unsigned int addr)
+{
+ return __sdio_readb(func->card, func->number, addr);
+}
+
+void sdio_writeb(const struct mmc_function *func,
+ unsigned int addr, unsigned char data)
+{
+ return __sdio_writeb(func->card, func->number, addr, data);
+}
+
+EXPORT_SYMBOL_GPL(sdio_readb);
+EXPORT_SYMBOL_GPL(sdio_writeb);
+
+void sdio_discover_functions(struct mmc_card *card)
+{
+ int i;
+ struct mmc_function *func;
+ unsigned int cur_func, last_func;
+
+ func = card->functions;
+ cur_func = 1;
+ last_func = card->n_functions;
+
+ /* Memory card function isn't a real SDIO function */
+ if (func->vendor == MMC_VENDOR_STORAGE) {
+ func++;
+ last_func--;
+ }
+
+ printk(KERN_INFO "SDIO: First byte: 0x%02x\n",
+ (int)__sdio_readb(card, 0, 0));
+/* while (cur_func <= last_func) {
+ }*/
+}
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 991a373..948c39d 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -46,6 +46,15 @@ #define SD_SCR_BUS_WIDTH_1 (1<<0)
#define SD_SCR_BUS_WIDTH_4 (1<<2)
};
+struct mmc_card;
+
+struct mmc_function {
+ struct mmc_card *card; /* the card this func belongs to */
+ unsigned int number; /* function number in card */
+ unsigned int vendor; /* vendor id */
+ unsigned int device; /* device id */
+};
+
struct mmc_host;
/*
@@ -60,27 +69,36 @@ struct mmc_card {
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */
#define MMC_STATE_BAD (1<<2) /* unrecognised device */
-#define MMC_STATE_SDCARD (1<<3) /* is an SD card */
-#define MMC_STATE_READONLY (1<<4) /* card is read-only */
+#define MMC_STATE_READONLY (1<<3) /* card is read-only */
+#define MMC_STATE_SDMEM (1<<4) /* is an SD memory card */
+#define MMC_STATE_SDIO (1<<5) /* is an SDIO card */
+ unsigned int type; /* type of card */
u32 raw_cid[4]; /* raw card CID */
u32 raw_csd[4]; /* raw card CSD */
u32 raw_scr[2]; /* raw card SCR */
struct mmc_cid cid; /* card identification */
struct mmc_csd csd; /* card specific */
struct sd_scr scr; /* extra SD information */
+ unsigned int n_functions; /* number of card subfunctions */
+ struct mmc_function *functions; /* function data */
};
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD)
#define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD)
-#define mmc_card_sd(c) ((c)->state & MMC_STATE_SDCARD)
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
+#define mmc_card_sdmem(c) ((c)->state & MMC_STATE_SDMEM)
+#define mmc_card_sdio(c) ((c)->state & MMC_STATE_SDIO)
+
+#define mmc_card_sd(c) (mmc_card_sdmem(c) || mmc_card_sdio(c))
+#define mmc_card_mmc(c) (!mmc_card_sd(c))
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD)
#define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD)
-#define mmc_card_set_sd(c) ((c)->state |= MMC_STATE_SDCARD)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
+#define mmc_card_set_sdmem(c) ((c)->state |= MMC_STATE_SDMEM)
+#define mmc_card_set_sdio(c) ((c)->state |= MMC_STATE_SDIO)
#define mmc_card_name(c) ((c)->cid.prod_name)
#define mmc_card_id(c) ((c)->dev.bus_id)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f383b24..5608f22 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -100,8 +100,8 @@ #define MMC_CAP_4_BIT_DATA (1 << 0) /* C
u32 ocr; /* the current OCR setting */
unsigned int mode; /* current card mode of host */
-#define MMC_MODE_MMC 0
-#define MMC_MODE_SD 1
+#define MMC_MODE_MMC 0 /* bus topology */
+#define MMC_MODE_SD 1 /* star topology */
struct list_head cards; /* devices attached to this host */
diff --git a/include/linux/mmc/ids.h b/include/linux/mmc/ids.h
new file mode 100644
index 0000000..71268c0
--- /dev/null
+++ b/include/linux/mmc/ids.h
@@ -0,0 +1,14 @@
+/*
+ * linux/include/linux/mmc/card.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Vendor and device ids
+ */
+
+/* Fake ids for storage cards */
+#define MMC_VENDOR_STORAGE 0xFFFFFFFE
+#define MMC_DEVICE_MMC_STORAGE 0x00000000
+#define MMC_DEVICE_SD_STORAGE 0x00000001
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 03a14a3..2d7f554 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -42,6 +42,8 @@ #define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_
#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_PRESENT)
+#define MMC_RSP_R4 (MMC_RSP_PRESENT)
+#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC)
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
diff --git a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h
index 08dec8d..c596ba6 100644
--- a/include/linux/mmc/protocol.h
+++ b/include/linux/mmc/protocol.h
@@ -81,6 +81,12 @@ #define MMC_GEN_CMD 56 /*
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
+ /* class ? */
+#define SD_IO_SEND_OP_COND 5 /* bcr [23:0] OCR R4 */
+
+ /* class 9 */
+#define SD_IO_RW_DIRECT 52 /* ac <Complex> R5 */
+
/* Application commands */
#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
@@ -126,6 +132,17 @@ #define R1_CURRENT_STATE(x) ((x & 0x
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_APP_CMD (1 << 5) /* sr, c */
+/*
+ SDIO status in R5
+ */
+
+#define R5_COM_CRC_ERROR (1 << 15) /* er, b */
+#define R5_ILLEGAL_COMMAND (1 << 15) /* er, b */
+#define R5_IO_CURRENT_STATE(x) ((x & 0x00003000) >> 12) /* s, b (2 bits) */
+#define R5_ERROR (1 << 11) /* erx, c */
+#define R5_FUNCTION_NUMBER (1 << 9) /* er, c */
+#define R5_OUT_OF_RANGE (1 << 8) /* er, c */
+
/* These are unpacked versions of the actual responses */
struct _mmc_csd {
@@ -196,6 +213,8 @@ #define MMC_VDD_34_35 0x00400000 /* VDD
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
+#define SDIO_MEM_PRES 0x08000000 /* SDIO/Mem combo card */
+
/*
* Card Command Classes (CCC)
*/
diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h
new file mode 100644
index 0000000..9d1d561
--- /dev/null
+++ b/include/linux/mmc/sdio.h
@@ -0,0 +1,11 @@
+/*
+ * linux/include/linux/mmc/sdio.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+unsigned char sdio_readb(const struct mmc_function *func, unsigned int addr);
+void sdio_writeb(const struct mmc_function *func,
+ unsigned int addr, unsigned char data);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index f697770..f16fbb6 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -297,4 +297,13 @@ struct input_device_id {
kernel_ulong_t driver_info;
};
+/* MMC */
+#define MMC_ANY_ID (~0)
+
+struct mmc_device_id {
+ __u32 vendor, device; /* Vendor and device ID or MMC_ANY_ID*/
+ __u32 class; /* SDIO class id */
+ kernel_ulong_t driver_data; /* Data private to the driver */
+};
+
#endif /* LINUX_MOD_DEVICETABLE_H */
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
2006-09-06 5:20 ` Pierre Ossman
@ 2006-09-06 23:22 ` madhu chikkature
2006-09-07 14:54 ` Pierre Ossman
0 siblings, 1 reply; 9+ messages in thread
From: madhu chikkature @ 2006-09-06 23:22 UTC (permalink / raw)
To: Pierre Ossman; +Cc: linux-kernel
Hi Pierre,
What is the kernel version this patch is taken againest?
Regards,
Madhu
On 9/6/06, Pierre Ossman <drzeus-list@drzeus.cx> wrote:
> madhu chikkature wrote:
> > 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.
> >
>
> Your design is a bit lacking yes as it doesn't properly reuse the
> structures in place. Have a look at the version I'm working on instead.
>
> Rgds
> Pierre
>
>
>
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
2006-09-06 23:22 ` madhu chikkature
@ 2006-09-07 14:54 ` Pierre Ossman
0 siblings, 0 replies; 9+ messages in thread
From: Pierre Ossman @ 2006-09-07 14:54 UTC (permalink / raw)
To: madhu chikkature; +Cc: linux-kernel
madhu chikkature wrote:
> Hi Pierre,
>
> What is the kernel version this patch is taken againest?
It's against the git version (plus some of my pending patches, which is
why I said you should ignore the chunk problems in mmc_block.c)
Rgds
Pierre
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: SDIO card support in Linux
2006-09-05 23:51 ` madhu chikkature
2006-09-06 5:20 ` Pierre Ossman
@ 2006-09-14 12:01 ` Bhavani
1 sibling, 0 replies; 9+ messages in thread
From: Bhavani @ 2006-09-14 12:01 UTC (permalink / raw)
To: linux-kernel
madhu chikkature <crmadhu210 <at> gmail.com> writes:
>
> 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
Hi Madhu,
I hve omap2420,Tsunami board n i'm trying to bring up stack for sdio card.
Once i give IO_RW_EXTENDED the system gets hanged. I find it bit difficult to
fix it. Is there anything i'hve to change in host driver code. Please help me.
Thanks,
Bhavani
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2006-09-15 4:25 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-08-31 15:04 SDIO card support in Linux 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
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox