diff mbox

Siano 10222 sdio stack

Message ID 65188.22766.qm@web110816.mail.gq1.yahoo.com (mailing list archive)
State Superseded
Headers show

Commit Message

Uri Shkolnik Jan. 13, 2009, 1:16 p.m. UTC
# HG changeset patch
# User Uri Shkolnik <uris@siano-ms.com>
# Date 1231841725 -7200
# Node ID 4ba2960232322ae3a1448670bbf2c33a8ecacfda
# Parent  2a546240ebe57391622c731646cf7523d20830dd
sdio: add low level i/o functions for workarounds

From: Pierre Ossman <drzeus@drzeus.cx>

Some shoddy hardware doesn't properly adhere to the register model
of SDIO, but treats the system like a series of transaction. That means
that the drivers must have full control over what goes the bus (and the
core cannot optimise transactions or work around problems in host
controllers).
This commit adds some low level functions that gives SDIO drivers the
ability to send specific register access commands. They should only be
used when the hardware is truly broken though.

Priority: high

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Signed-off-by: Uri Shkolnik <uris@siano-ms.com>




      
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff -r 2a546240ebe5 -r 4ba296023232 linux/drivers/mmc/core/sdio_io.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/mmc/core/sdio_io.c	Tue Jan 13 12:15:25 2009 +0200
@@ -0,0 +1,808 @@ 
+/*
+ *  linux/drivers/mmc/core/sdio_io.c
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "sdio_ops.h"
+
+/**
+ *	sdio_claim_host - exclusively claim a bus for a certain SDIO function
+ *	@func: SDIO function that will be accessed
+ *
+ *	Claim a bus for a set of operations. The SDIO function given
+ *	is used to figure out which bus is relevant.
+ */
+void sdio_claim_host(struct sdio_func *func)
+{
+	BUG_ON(!func);
+	BUG_ON(!func->card);
+
+	mmc_claim_host(func->card->host);
+}
+EXPORT_SYMBOL_GPL(sdio_claim_host);
+
+/**
+ *	sdio_release_host - release a bus for a certain SDIO function
+ *	@func: SDIO function that was accessed
+ *
+ *	Release a bus, allowing others to claim the bus for their
+ *	operations.
+ */
+void sdio_release_host(struct sdio_func *func)
+{
+	BUG_ON(!func);
+	BUG_ON(!func->card);
+
+	mmc_release_host(func->card->host);
+}
+EXPORT_SYMBOL_GPL(sdio_release_host);
+
+/**
+ *	sdio_enable_func - enables a SDIO function for usage
+ *	@func: SDIO function to enable
+ *
+ *	Powers up and activates a SDIO function so that register
+ *	access is possible.
+ */
+int sdio_enable_func(struct sdio_func *func)
+{
+	int ret;
+	unsigned char reg;
+	unsigned long timeout;
+
+	BUG_ON(!func);
+	BUG_ON(!func->card);
+
+	pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
+
+	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
+	if (ret)
+		goto err;
+
+	reg |= 1 << func->num;
+
+	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
+	if (ret)
+		goto err;
+
+	/*
+	 * FIXME: This should timeout based on information in the CIS,
+	 * but we don't have card to parse that yet.
+	 */
+	timeout = jiffies + HZ;
+
+	while (1) {
+		ret = mmc_io_rw_direct(func->card, 0, 0,
+				SDIO_CCCR_IORx, 0, &reg);
+		if (ret)
+			goto err;
+		if (reg & (1 << func->num))
+			break;
+		ret = -ETIME;
+		if (time_after(jiffies, timeout))
+			goto err;
+	}
+
+	pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
+
+	return 0;
+
+err:
+	pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sdio_enable_func);
+
+/**
+ *	sdio_disable_func - disable a SDIO function
+ *	@func: SDIO function to disable
+ *
+ *	Powers down and deactivates a SDIO function. Register access
+ *	to this function will fail until the function is reenabled.
+ */
+int sdio_disable_func(struct sdio_func *func)
+{
+	int ret;
+	unsigned char reg;
+
+	BUG_ON(!func);
+	BUG_ON(!func->card);
+
+	pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
+
+	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
+	if (ret)
+		goto err;
+
+	reg &= ~(1 << func->num);
+
+	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
+	if (ret)
+		goto err;
+
+	pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
+
+	return 0;
+
+err:
+	pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
+	return -EIO;
+}
+EXPORT_SYMBOL_GPL(sdio_disable_func);
+
+/**
+ *	sdio_set_block_size - set the block size of an SDIO function
+ *	@func: SDIO function to change
+ *	@blksz: new block size or 0 to use the default.
+ *
+ *	The default block size is the largest supported by both the function
+ *	and the host, with a maximum of 512 to ensure that arbitrarily sized
+ *	data transfer use the optimal (least) number of commands.
+ *
+ *	A driver may call this to override the default block size set by the
+ *	core. This can be used to set a block size greater than the maximum
+ *	that reported by the card; it is the driver's responsibility to ensure
+ *	it uses a value that the card supports.
+ *
+ *	Returns 0 on success, -EINVAL if the host does not support the
+ *	requested block size, or -EIO (etc.) if one of the resultant FBR block
+ *	size register writes failed.
+ *
+ */
+int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
+{
+	int ret;
+
+	if (blksz > func->card->host->max_blk_size)
+		return -EINVAL;
+
+	if (blksz == 0) {
+		blksz = min(min(
+			func->max_blksize,
+			func->card->host->max_blk_size),
+			512u);
+	}
+
+	ret = mmc_io_rw_direct(func->card, 1, 0,
+		SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
+		blksz & 0xff, NULL);
+	if (ret)
+		return ret;
+	ret = mmc_io_rw_direct(func->card, 1, 0,
+		SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
+		(blksz >> 8) & 0xff, NULL);
+	if (ret)
+		return ret;
+	func->cur_blksize = blksz;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdio_set_block_size);
+
+/* Split an arbitrarily sized data transfer into several
+ * IO_RW_EXTENDED commands. */
+static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
+	unsigned addr, int incr_addr, u8 *buf, unsigned size)
+{
+	unsigned remainder = size;
+	unsigned max_blocks;
+	int ret;
+
+	/* Do the bulk of the transfer using block mode (if supported). */
+	if (func->card->cccr.multi_block) {
+		/* Blocks per command is limited by host count, host transfer
+		 * size (we only use a single sg entry) and the maximum for
+		 * IO_RW_EXTENDED of 511 blocks. */
+		max_blocks = min(min(
+			func->card->host->max_blk_count,
+			func->card->host->max_seg_size / func->cur_blksize),
+			511u);
+
+		while (remainder > func->cur_blksize) {
+			unsigned blocks;
+
+			blocks = remainder / func->cur_blksize;
+			if (blocks > max_blocks)
+				blocks = max_blocks;
+			size = blocks * func->cur_blksize;
+
+			ret = mmc_io_rw_extended(func->card, write,
+				func->num, addr, incr_addr, buf,
+				blocks, func->cur_blksize);
+			if (ret)
+				return ret;
+
+			remainder -= size;
+			buf += size;
+			if (incr_addr)
+				addr += size;
+		}
+	}
+
+	/* Write the remainder using byte mode. */
+	while (remainder > 0) {
+		size = remainder;
+		if (size > func->cur_blksize)
+			size = func->cur_blksize;
+		if (size > 512)
+			size = 512; /* maximum size for byte mode */
+
+		ret = mmc_io_rw_extended(func->card, write, func->num, addr,
+			 incr_addr, buf, 1, size);
+		if (ret)
+			return ret;
+
+		remainder -= size;
+		buf += size;
+		if (incr_addr)
+			addr += size;
+	}
+	return 0;
+}
+
+/**
+ *	sdio_readb - read a single byte from a SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to read
+ *	@err_ret: optional status value from transfer
+ *
+ *	Reads a single byte from the address space of a given SDIO
+ *	function. If there is a problem reading the address, 0xff
+ *	is returned and @err_ret will contain the error code.
+ */
+unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+	unsigned char val;
+
+	BUG_ON(!func);
+
+	if (err_ret)
+		*err_ret = 0;
+
+	ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
+	if (ret) {
+		if (err_ret)
+			*err_ret = ret;
+		return 0xFF;
+	}
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(sdio_readb);
+
+/**
+ *	sdio_writeb - write a single byte to a SDIO function
+ *	@func: SDIO function to access
+ *	@b: byte to write
+ *	@addr: address to write to
+ *	@err_ret: optional status value from transfer
+ *
+ *	Writes a single byte to the address space of a given SDIO
+ *	function. @err_ret will contain the status of the actual
+ *	transfer.
+ */
+void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+
+	BUG_ON(!func);
+
+	ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
+	if (err_ret)
+		*err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_writeb);
+
+/**
+ *	sdio_memcpy_fromio - read a chunk of memory from a SDIO function
+ *	@func: SDIO function to access
+ *	@dst: buffer to store the data
+ *	@addr: address to begin reading from
+ *	@count: number of bytes to read
+ *
+ *	Reads from the address space of a given SDIO function. Return
+ *	value indicates if the transfer succeeded or not.
+ */
+int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
+	unsigned int addr, int count)
+{
+	return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count);
+}
+EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
+
+/**
+ *	sdio_memcpy_toio - write a chunk of memory to a SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to start writing to
+ *	@src: buffer that contains the data to write
+ *	@count: number of bytes to write
+ *
+ *	Writes to the address space of a given SDIO function. Return
+ *	value indicates if the transfer succeeded or not.
+ */
+int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
+	void *src, int count)
+{
+	return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count);
+}
+EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
+
+/**
+ *	sdio_readsb - read from a FIFO on a SDIO function
+ *	@func: SDIO function to access
+ *	@dst: buffer to store the data
+ *	@addr: address of (single byte) FIFO
+ *	@count: number of bytes to read
+ *
+ *	Reads from the specified FIFO of a given SDIO function. Return
+ *	value indicates if the transfer succeeded or not.
+ */
+int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
+	int count)
+{
+	return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
+}
+EXPORT_SYMBOL_GPL(sdio_readsb);
+
+/**
+ *	sdio_writesb - write to a FIFO of a SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address of (single byte) FIFO
+ *	@src: buffer that contains the data to write
+ *	@count: number of bytes to write
+ *
+ *	Writes to the specified FIFO of a given SDIO function. Return
+ *	value indicates if the transfer succeeded or not.
+ */
+int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
+	int count)
+{
+	return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count);
+}
+EXPORT_SYMBOL_GPL(sdio_writesb);
+
+/**
+ *	sdio_readw - read a 16 bit integer from a SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to read
+ *	@err_ret: optional status value from transfer
+ *
+ *	Reads a 16 bit integer from the address space of a given SDIO
+ *	function. If there is a problem reading the address, 0xffff
+ *	is returned and @err_ret will contain the error code.
+ */
+unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+
+	if (err_ret)
+		*err_ret = 0;
+
+	ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2);
+	if (ret) {
+		if (err_ret)
+			*err_ret = ret;
+		return 0xFFFF;
+	}
+
+	return le16_to_cpu(*(u16 *)func->tmpbuf);
+}
+EXPORT_SYMBOL_GPL(sdio_readw);
+
+/**
+ *	sdio_writew - write a 16 bit integer to a SDIO function
+ *	@func: SDIO function to access
+ *	@b: integer to write
+ *	@addr: address to write to
+ *	@err_ret: optional status value from transfer
+ *
+ *	Writes a 16 bit integer to the address space of a given SDIO
+ *	function. @err_ret will contain the status of the actual
+ *	transfer.
+ */
+void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+
+	*(u16 *)func->tmpbuf = cpu_to_le16(b);
+
+	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
+	if (err_ret)
+		*err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_writew);
+
+/**
+ *	sdio_readl - read a 32 bit integer from a SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to read
+ *	@err_ret: optional status value from transfer
+ *
+ *	Reads a 32 bit integer from the address space of a given SDIO
+ *	function. If there is a problem reading the address,
+ *	0xffffffff is returned and @err_ret will contain the error
+ *	code.
+ */
+unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+
+	if (err_ret)
+		*err_ret = 0;
+
+	ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4);
+	if (ret) {
+		if (err_ret)
+			*err_ret = ret;
+		return 0xFFFFFFFF;
+	}
+
+	return le32_to_cpu(*(u32 *)func->tmpbuf);
+}
+EXPORT_SYMBOL_GPL(sdio_readl);
+
+/**
+ *	sdio_writel - write a 32 bit integer to a SDIO function
+ *	@func: SDIO function to access
+ *	@b: integer to write
+ *	@addr: address to write to
+ *	@err_ret: optional status value from transfer
+ *
+ *	Writes a 32 bit integer to the address space of a given SDIO
+ *	function. @err_ret will contain the status of the actual
+ *	transfer.
+ */
+void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+
+	*(u32 *)func->tmpbuf = cpu_to_le32(b);
+
+	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
+	if (err_ret)
+		*err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_writel);
+
+/**
+ *	sdio_f0_readb - read a single byte from SDIO function 0
+ *	@func: an SDIO function of the card
+ *	@addr: address to read
+ *	@err_ret: optional status value from transfer
+ *
+ *	Reads a single byte from the address space of SDIO function 0.
+ *	If there is a problem reading the address, 0xff is returned
+ *	and @err_ret will contain the error code.
+ */
+unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+	unsigned char val;
+
+	BUG_ON(!func);
+
+	if (err_ret)
+		*err_ret = 0;
+
+	ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val);
+	if (ret) {
+		if (err_ret)
+			*err_ret = ret;
+		return 0xFF;
+	}
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(sdio_f0_readb);
+
+/**
+ *	sdio_f0_writeb - write a single byte to SDIO function 0
+ *	@func: an SDIO function of the card
+ *	@b: byte to write
+ *	@addr: address to write to
+ *	@err_ret: optional status value from transfer
+ *
+ *	Writes a single byte to the address space of SDIO function 0.
+ *	@err_ret will contain the status of the actual transfer.
+ *
+ *	Only writes to the vendor specific CCCR registers (0xF0 -
+ *	0xFF) are permiited; @err_ret will be set to -EINVAL for *
+ *	writes outside this range.
+ */
+void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
+	int *err_ret)
+{
+	int ret;
+
+	BUG_ON(!func);
+
+	if (addr < 0xF0 || addr > 0xFF) {
+		if (err_ret)
+			*err_ret = -EINVAL;
+		return;
+	}
+
+	ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
+	if (err_ret)
+		*err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_f0_writeb);
+
+/*
+ * Calculate the maximum byte mode transfer size
+ */
+static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
+{
+	unsigned mval =	min(func->card->host->max_seg_size,
+			    func->card->host->max_blk_size);
+	mval = min(mval, func->max_blksize);
+	return min(mval, 512u); /* maximum size for byte mode */
+}
+
+
+/**
+ *	sdio_read_bytes - low level byte mode transfer from an SDIO function
+ *	@func: SDIO function to access
+ *	@dst: buffer to store the data
+ *	@addr: address to begin reading from
+ *	@bytes: number of bytes to read
+ *
+ *	Performs a byte mode transfer from the address space of the given
+ *	SDIO function. The address is increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_read_bytes(struct sdio_func *func, void *dst,
+	unsigned int addr, int bytes)
+{
+	if (bytes > sdio_max_byte_size(func))
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 0, func->num, addr, 1,
+			dst, 1, bytes);
+}
+EXPORT_SYMBOL_GPL(sdio_read_bytes);
+
+/**
+ *	sdio_read_bytes_noincr - low level byte mode transfer from an SDIO function
+ *	@func: SDIO function to access
+ *	@dst: buffer to store the data
+ *	@addr: address to begin reading from
+ *	@bytes: number of bytes to read
+ *
+ *	Performs a byte mode transfer from the address space of the given
+ *	SDIO function. The address is NOT increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_read_bytes_noincr(struct sdio_func *func, void *dst,
+	unsigned int addr, int bytes)
+{
+	if (bytes > sdio_max_byte_size(func))
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 0, func->num, addr, 0,
+			dst, 1, bytes);
+}
+EXPORT_SYMBOL_GPL(sdio_read_bytes_noincr);
+
+/**
+ *	sdio_read_blocks - low level block mode transfer from an SDIO function
+ *	@func: SDIO function to access
+ *	@dst: buffer to store the data
+ *	@addr: address to begin reading from
+ *	@block: number of blocks to read
+ *
+ *	Performs a block mode transfer from the address space of the given
+ *	SDIO function. The address is increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	The block size needs to be explicitly changed by calling
+ *	sdio_set_block_size().
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_read_blocks(struct sdio_func *func, void *dst,
+	unsigned int addr, int blocks)
+{
+	if (!func->card->cccr.multi_block)
+		return -EINVAL;
+
+	if (blocks > func->card->host->max_blk_count)
+		return -EINVAL;
+	if (blocks > (func->card->host->max_seg_size / func->cur_blksize))
+		return -EINVAL;
+	if (blocks > 511)
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 0, func->num, addr, 1,
+			dst, blocks, func->cur_blksize);
+}
+EXPORT_SYMBOL_GPL(sdio_read_blocks);
+
+/**
+ *	sdio_read_blocks_noincr - low level block mode transfer from an SDIO function
+ *	@func: SDIO function to access
+ *	@dst: buffer to store the data
+ *	@addr: address to begin reading from
+ *	@block: number of blocks to read
+ *
+ *	Performs a block mode transfer from the address space of the given
+ *	SDIO function. The address is NOT increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	The block size needs to be explicitly changed by calling
+ *	sdio_set_block_size().
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_read_blocks_noincr(struct sdio_func *func, void *dst,
+	unsigned int addr, int blocks)
+{
+	if (!func->card->cccr.multi_block)
+		return -EINVAL;
+
+	if (blocks > func->card->host->max_blk_count)
+		return -EINVAL;
+	if (blocks > (func->card->host->max_seg_size / func->cur_blksize))
+		return -EINVAL;
+	if (blocks > 511)
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 0, func->num, addr, 0,
+			dst, blocks, func->cur_blksize);
+}
+EXPORT_SYMBOL_GPL(sdio_read_blocks_noincr);
+
+/**
+ *	sdio_write_bytes - low level byte mode transfer to an SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to start writing to
+ *	@src: buffer that contains the data to write
+ *	@bytes: number of bytes to write
+ *
+ *	Performs a byte mode transfer to the address space of the given
+ *	SDIO function. The address is increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_write_bytes(struct sdio_func *func, unsigned int addr,
+	 void *src, int bytes)
+{
+	if (bytes > sdio_max_byte_size(func))
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 1, func->num, addr, 1,
+			src, 1, bytes);
+}
+EXPORT_SYMBOL_GPL(sdio_write_bytes);
+
+/**
+ *	sdio_write_bytes_noincr - low level byte mode transfer to an SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to start writing to
+ *	@src: buffer that contains the data to write
+ *	@bytes: number of bytes to write
+ *
+ *	Performs a byte mode transfer to the address space of the given
+ *	SDIO function. The address is NOT increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_write_bytes_noincr(struct sdio_func *func, unsigned int addr,
+	void *src, int bytes)
+{
+	if (bytes > sdio_max_byte_size(func))
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 1, func->num, addr, 0,
+			src, 1, bytes);
+}
+EXPORT_SYMBOL_GPL(sdio_write_bytes_noincr);
+
+/**
+ *	sdio_read_blocks - low level block mode transfer to an SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to start writing to
+ *	@src: buffer that contains the data to write
+ *	@block: number of blocks to write
+ *
+ *	Performs a block mode transfer to the address space of the given
+ *	SDIO function. The address is increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	The block size needs to be explicitly changed by calling
+ *	sdio_set_block_size().
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_write_blocks(struct sdio_func *func, unsigned int addr,
+	void *src, int blocks)
+{
+	if (!func->card->cccr.multi_block)
+		return -EINVAL;
+
+	if (blocks > func->card->host->max_blk_count)
+		return -EINVAL;
+	if (blocks > (func->card->host->max_seg_size / func->cur_blksize))
+		return -EINVAL;
+	if (blocks > 511)
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 1, func->num, addr, 1,
+			src, blocks, func->cur_blksize);
+}
+EXPORT_SYMBOL_GPL(sdio_write_blocks);
+
+/**
+ *	sdio_read_blocks_noincr - low level block mode transfer to an SDIO function
+ *	@func: SDIO function to access
+ *	@addr: address to start writing to
+ *	@src: buffer that contains the data to write
+ *	@block: number of blocks to write
+ *
+ *	Performs a block mode transfer to the address space of the given
+ *	SDIO function. The address is NOT increased for each byte. Return
+ *	value indicates if the transfer succeeded or not.
+ *
+ *	The block size needs to be explicitly changed by calling
+ *	sdio_set_block_size().
+ *
+ *	Note: This is a low level function that should only be used as a
+ *	workaround when the hardware has a crappy register abstraction
+ *	that relies on specific SDIO operations.
+ */
+int sdio_write_blocks_noincr(struct sdio_func *func, unsigned int addr,
+	void *src, int blocks)
+{
+	if (!func->card->cccr.multi_block)
+		return -EINVAL;
+
+	if (blocks > func->card->host->max_blk_count)
+		return -EINVAL;
+	if (blocks > (func->card->host->max_seg_size / func->cur_blksize))
+		return -EINVAL;
+	if (blocks > 511)
+		return -EINVAL;
+
+	return mmc_io_rw_extended(func->card, 1, func->num, addr, 0,
+			src, blocks, func->cur_blksize);
+}
+EXPORT_SYMBOL_GPL(sdio_write_blocks_noincr);
+
diff -r 2a546240ebe5 -r 4ba296023232 linux/include/linux/mmc/sdio_func.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/include/linux/mmc/sdio_func.h	Tue Jan 13 12:15:25 2009 +0200
@@ -0,0 +1,182 @@ 
+/*
+ *  include/linux/mmc/sdio_func.h
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef MMC_SDIO_FUNC_H
+#define MMC_SDIO_FUNC_H
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+struct mmc_card;
+struct sdio_func;
+
+typedef void (sdio_irq_handler_t)(struct sdio_func *);
+
+/*
+ * SDIO function CIS tuple (unknown to the core)
+ */
+struct sdio_func_tuple {
+	struct sdio_func_tuple *next;
+	unsigned char code;
+	unsigned char size;
+	unsigned char data[0];
+};
+
+/*
+ * SDIO function devices
+ */
+struct sdio_func {
+	struct mmc_card		*card;		/* the card this
+						* device belongs to
+						*/
+	struct device		dev;		/* the device */
+	sdio_irq_handler_t	*irq_handler;	/* IRQ callback */
+	unsigned int		num;		/* function number */
+
+	unsigned char		class;		/* standard interface class */
+	unsigned short		vendor;		/* vendor id */
+	unsigned short		device;		/* device id */
+
+	unsigned		max_blksize;	/* maximum block size */
+	unsigned		cur_blksize;	/* current block size */
+
+	unsigned int		state;		/* function state */
+#define SDIO_STATE_PRESENT	(1<<0)		/* present in sysfs */
+
+	u8			tmpbuf[4];	/* DMA:able scratch buffer */
+
+	unsigned		num_info;	/* number of info strings */
+	const char		**info;		/* info strings */
+
+	struct sdio_func_tuple *tuples;
+};
+
+#define sdio_func_present(f)	((f)->state & SDIO_STATE_PRESENT)
+
+#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT)
+
+#define sdio_func_id(f)		((f)->dev.bus_id)
+
+#define sdio_get_drvdata(f)	dev_get_drvdata(&(f)->dev)
+#define sdio_set_drvdata(f, d)	dev_set_drvdata(&(f)->dev, d)
+
+/*
+ * SDIO function device driver
+ */
+struct sdio_driver {
+	char *name;
+	const struct sdio_device_id *id_table;
+
+	int (*probe)(struct sdio_func *, const struct sdio_device_id *);
+	void (*remove)(struct sdio_func *);
+
+	struct device_driver drv;
+};
+
+/**
+ * SDIO_DEVICE - macro used to describe a specific SDIO device
+ * @vend: the 16 bit manufacturer code
+ * @dev: the 16 bit function id
+ *
+ * This macro is used to create a struct sdio_device_id that matches a
+ * specific device. The class field will be set to SDIO_ANY_ID.
+ */
+#define SDIO_DEVICE(vend,dev) {	\
+	.class = SDIO_ANY_ID,	\
+	.vendor = (vend),	\
+	.device = (dev) }
+
+/**
+ * SDIO_DEVICE_CLASS - macro used to describe a specific SDIO device class
+ * @dev_class: the 8 bit standard interface code
+ *
+ * This macro is used to create a struct sdio_device_id that matches a
+ * specific standard SDIO function type.  The vendor and device fields will
+ * be set to SDIO_ANY_ID.
+ */
+#define SDIO_DEVICE_CLASS(dev_class) {	\
+	.class = (dev_class),		\
+	.vendor = SDIO_ANY_ID,		\
+	.device = SDIO_ANY_ID }
+
+extern int sdio_register_driver(struct sdio_driver *);
+extern void sdio_unregister_driver(struct sdio_driver *);
+
+/*
+ * SDIO I/O operations
+ */
+extern void sdio_claim_host(struct sdio_func *func);
+extern void sdio_release_host(struct sdio_func *func);
+
+extern int sdio_enable_func(struct sdio_func *func);
+extern int sdio_disable_func(struct sdio_func *func);
+
+extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz);
+
+extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
+extern int sdio_release_irq(struct sdio_func *func);
+
+extern unsigned char sdio_readb(struct sdio_func *func,
+	unsigned int addr, int *err_ret);
+extern unsigned short sdio_readw(struct sdio_func *func,
+	unsigned int addr, int *err_ret);
+extern unsigned long sdio_readl(struct sdio_func *func,
+	unsigned int addr, int *err_ret);
+
+extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
+	unsigned int addr, int count);
+extern int sdio_readsb(struct sdio_func *func, void *dst,
+	unsigned int addr, int count);
+
+extern void sdio_writeb(struct sdio_func *func, unsigned char b,
+	unsigned int addr, int *err_ret);
+extern void sdio_writew(struct sdio_func *func, unsigned short b,
+	unsigned int addr, int *err_ret);
+extern void sdio_writel(struct sdio_func *func, unsigned long b,
+	unsigned int addr, int *err_ret);
+
+extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
+	void *src, int count);
+extern int sdio_writesb(struct sdio_func *func, unsigned int addr,
+	void *src, int count);
+
+extern unsigned char sdio_f0_readb(struct sdio_func *func,
+	unsigned int addr, int *err_ret);
+extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
+	unsigned int addr, int *err_ret);
+
+/*
+ * Low-level I/O functions for hardware that doesn't properly abstract
+ * the register space. Don't use these unless you absolutely have to.
+ */
+
+extern int sdio_read_bytes(struct sdio_func *func, void *dst,
+	unsigned int addr, int bytes);
+extern int sdio_read_bytes_noincr(struct sdio_func *func, void *dst,
+	unsigned int addr, int bytes);
+
+extern int sdio_read_blocks(struct sdio_func *func, void *dst,
+	unsigned int addr, int blocks);
+extern int sdio_read_blocks_noincr(struct sdio_func *func, void *dst,
+	unsigned int addr, int blocks);
+
+extern int sdio_write_bytes(struct sdio_func *func, unsigned int addr,
+	 void *src, int bytes);
+extern int sdio_write_bytes_noincr(struct sdio_func *func, unsigned int addr,
+	void *src, int bytes);
+
+extern int sdio_write_blocks(struct sdio_func *func, unsigned int addr,
+	void *src, int blocks);
+extern int sdio_write_blocks_noincr(struct sdio_func *func, unsigned int addr,
+	void *src, int blocks);
+
+#endif
+
diff -r 2a546240ebe5 -r 4ba296023232 linux/include/linux/mmc/sdio_ids.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/include/linux/mmc/sdio_ids.h	Tue Jan 13 12:15:25 2009 +0200
@@ -0,0 +1,38 @@ 
+/*
+ * SDIO Classes, Interface Types, Manufacturer IDs, etc.
+ */
+
+#ifndef MMC_SDIO_IDS_H
+#define MMC_SDIO_IDS_H
+
+/*
+ * Standard SDIO Function Interfaces
+ */
+
+#define SDIO_CLASS_NONE		0x00	/* Not a SDIO standard interface */
+#define SDIO_CLASS_UART		0x01	/* standard UART interface */
+#define SDIO_CLASS_BT_A		0x02	/* Type-A BlueTooth std interface */
+#define SDIO_CLASS_BT_B		0x03	/* Type-B BlueTooth std interface */
+#define SDIO_CLASS_GPS		0x04	/* GPS standard interface */
+#define SDIO_CLASS_CAMERA	0x05	/* Camera standard interface */
+#define SDIO_CLASS_PHS		0x06	/* PHS standard interface */
+#define SDIO_CLASS_WLAN		0x07	/* WLAN interface */
+#define SDIO_CLASS_ATA		0x08	/* Embedded SDIO-ATA std interface */
+
+/*
+ * Vendors and devices.  Sort key: vendor first, device next.
+ */
+
+#define SDIO_VENDOR_ID_MARVELL			0x02df
+#define SDIO_VENDOR_ID_SIANO			0x039a
+
+#define SDIO_DEVICE_ID_MARVELL_LIBERTAS		0x9103
+#define SDIO_DEVICE_ID_SIANO_STELLAR 		0x5347
+#define SDIO_DEVICE_ID_SIANO_NOVA_A0		0x1100
+#define SDIO_DEVICE_ID_SIANO_NOVA_B0		0x0201
+#define SDIO_DEVICE_ID_SIANO_VEGA_A0		0x0300
+#define SDIO_DEVICE_ID_SIANO_VENICE		0x0301
+
+#endif
+
+