Thursday, October 11, 2012

Design Tip - SDIO Support in Linux kernel

<!--[if gte mso 9]> ashim ashim 2 4 2012-10-11T06:22:00Z 2012-10-11T06:28:00Z 2 803 4582 38 10 5375 14.00 <![endif]
-->
Earlier there was no support of SDIO in Linux mainline kernel. Developers had to rely on proprietary stacks. Starting from 2.6.24 version SDIO support is part of mainline kernel. The mainline kernel SDIO stack is very different from other proprietary stacks. It hides all the details of SDIO protocol in well-designed API. In other implementations SDIO function drivers need to handle details of SDIO protocol. Mainline kernel implementation provides simple readX /writeX kind of routines as available for PCI. In fact its model is largely based on PCI.
SDIO device IRQ handler is called in process-context hence all SDIO I/O API can sleep. Support for asynchronous transfers doesn’t exist.

API for SDIO function drivers
sdio_register_driver
Registers SDIO function driver to the SDIO sub-system. Driver specifies probe, remove routines and deiceID table.

sdio_unregister_driver
Un-Registers SDIO function driver from the SDIO sub-system. 

sdio_claim_irq
Claims and activates the IRQ for the given SDIO function. The provided handler will be called when interrupt is asserted.

sdio_release_irq
Releases and de-activates the IRQ for the given SDIO function.

sdio_claim_host
Exclusively claims a bus for a certain SDIO function. It provides locking mechanism. This lock must be held before doing any I/O operation for the function.

sdio_release_host
Release a bus for a certain SDIO function.

sdio_enable_func
Enables a SDIO function for usage. SDIO function driver’s first need to call this function after the SDIO device is successfully probed.

sdio_disable_func
Disables a SDIO function. Drivers call it when SDIO device is removed.

sdio_set_block_size
Sets the block size of an SDIO function. Though SDIO stack automatically sets block size, drivers can alter it.

sdio_max_byte_size
Returns the maximum byte mode transfer size.

sdio_align_size
Pads a transfer size to a more optimal value. Sometimes due to limitations of DMA controller transfers need padding. This function facilitates that.

sdio_readb
Reads a single byte from a SDIO function. This uses direct transfer mode of SDIO protocol.

sdio_writeb
Write a single byte to a SDIO function. This uses direct transfer mode(CMD52) of SDIO protocol.

sdio_memcpy_fromio
Reads a chunk of memory from a SDIO function. This uses extended transfer mode(CMD53) of SDIO protocol. If count is multiple of block size, it uses block transfer. If count is not multiple of block size, it uses byte transfer for the last transfer.

sdio_memcpy_toio
Writes a chunk of memory to a SDIO function. This uses extended transfer mode of SDIO protocol. If count is multiple of block size, it uses block transfer. If count is not multiple of block size, it uses byte transfer for the last transfer.

sdio_readsb
Reads from a FIFO on a SDIO function. This uses extended transfer mode of SDIO protocol. If count is multiple of block size, it uses block transfer. If count is not multiple of block size, it uses byte transfer for the last transfer.

sdio_writesb
Writes to a FIFO of a SDIO function. This uses extended transfer mode of SDIO protocol. If count is multiple of block size, it uses block transfer. If count is not multiple of block size, it uses byte transfer for the last transfer.

sdio_readw
Reads 16 bit integer from a SDIO function. This uses extended byte transfer mode of SDIO protocol.

sdio_writew
Writes 16 bit integer to a SDIO function. This uses extended byte transfer mode of SDIO protocol.

sdio_readl
Reads 32 bit integer from a SDIO function. This uses extended byte transfer mode of SDIO protocol.

sdio_writel
Writes 32 bit integer to a SDIO function. This uses extended byte transfer mode of SDIO protocol.

Writing SDIO function driver
  1. In module_init of our driver, register probe and remove routines for SDIO function device using sdio_register_driver. Device ID table is also specified in this step.
  2. Whenever the device is inserted in SDIO socket, based on vendorIDdeviceID pair, SDIO sub-system calls our probe routine.
    1. In probe routine, allocate the private data for our function driver.
    2. Enable the SDIO function using sdio_enable_func
    3. Register IRQ handler using sdio_claim_irq
    4. If required set the block size using sdio_set_block_size
    5. SDIO IRQ handler is called from dedicated kernel thread for all the SDIO function drivers. So it is advisable not to do very large I/O operations from SDIO IRQ handler. Large I/O operation can be differed to work-queue. Create work-queue as our bottom half handler mechanism.
  3. Based on device architecture, device can signal data read FIFO full or write FIFO empty through interrupt. SDIO sub-system calls our IRQ handler.
Find cause of interrupt and submit SDIO I/O operation work to the workqueue. Inside work-queue function any of the following API can be used to perform read/write.
<![if !supportLineBreakNewLine]>
<![endif]>
sdio_readb/sdio_writeb
sdio_readw/sdio_writew
sdio_readl/sdio_writel
sdio_readsb/sdio_writesb
sdio_memcpy_fromio/sdio_memcpy_toio
  1. In module_exit of our driver unregister our probe and remove methods using sdio_unregister_driver. This calls our remove routine.
    1. In remove routine, wait for pending transfers and then release IRQ handler using sdio_release_irq
    2. Disable SDIO function using sdio_disable_func
    3. Free private data for our driver.
Remove routine is also called when device is hot-unplugged from SDIO bus. In this case cleanup is tricky and 100% cleanup may not be possible.
-->

No comments:

Post a Comment