Writing Modern Linux SDIO Drivers

By Dmitri “Dima” Varsanofiev

Modern Linux kernel finally has a standard SDIO stack, added around the version 2.6.24, written by Peter Ossman, http://drzeus.cx/ .  Except for the source code, there seems to be no reference to the  interfaces.

To some extent the SDIO stack is based on the MMC stack, so the necessary descriptions of the latter are also included.

This documents attempts to fill the gap. Pardon for the dust, as this is a work in progress.

File Locations

The relevant driver sources reside under mmc subdirectory.

·         mmc/host contains the host adapter drivers. sdhci is of particular interest, as it implements the SDIO host interface for the “Secure Digital Host Controller Interface”-compliant chips, including the near-omnipresent Ricoh controllers.

·         mmc/core contains the mmc subsystem code (mmc_core).

·         mmc/card contains the drivers for devices that sit on the MMC/SDIO bus. sdio_uart is a good example of such a driver.

Drivers

sdhci

This is the main SD host controller driver

mmc_core

This driver implements the basic SDIO operations and is used by the sdhci.

mmc_test

This is a memory card test driver that can be converted into a test for other cards.

ricoh_mmc

This driver does practically nothing, and just enables the sdhci to work with Ricoh chips.

MMC_DEBUG

If the MMC_DEBUG is enabled during the compilation of the kernel, all drivers will log a lot of useful debugging information.

Include Files

<linux/mmc/sdio_func.h>

Includes SDIO function-level interface. To be included into the function drivers using SDIO.

<linux/mmc/card.h>

 

<linux/mmc/core.h>

 

<linux/mmc/host.h>

Structure of the SDIO Driver

1.       Initialization:

static const struct sdio_device_id xxx_ids[] = 
{
         { SDIO_DEVICE(0xvendor, 0xproduct)     },
         { /* end: all zeroes */                },
};
 
static struct sdio_driver xxx_driver = 
{
         .probe          = xxx_probe,
         .remove         = xxx_remove,
         .name           = "xxx",
         .id_table       = xxx_ids,
};
 
static int __init xxx_init(void)
{
        ret = sdio_register_driver(&xxx_driver);
}
 

2.       Probe and removal:

// This is internal driver storage. Not necessary, but useful.
struct xxx_port {
  
struct sdio_func        *func
;
};
 
 
static int xxx_probe(struct sdio_func *func,
                           const struct sdio_device_id *id)
{
         struct xxx_port *port;
         port = kzalloc(sizeof(struct xxx_port), GFP_KERNEL);
 
         port->func = func;
         sdio_set_drvdata(func, port);
 
}
 
static void xxx_remove(struct sdio_func *func)
{
 struct xxx_port *port = sdio_get_drvdata(func);
         port->func = NULL;
}

3.       Start operations:

        sdio_claim_host(port->func);
        ret = sdio_enable_func(port->func);
        ret = sdio_claim_irq(port->func, xxx_irq);
        sdio_release_host(port->func);

 

4.       Stop operations:

        sdio_claim_host(port->func);
        sdio_release_irq(port->func);
        sdio_disable_func(port->func);
        sdio_release_host(port->func);

 

5.       Actual I/O (see the API in the sdio_func.h)