diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/mmc/core/Makefile | 3 | ||||
| -rw-r--r-- | drivers/mmc/core/bus.c | 8 | ||||
| -rw-r--r-- | drivers/mmc/core/core.c | 40 | ||||
| -rw-r--r-- | drivers/mmc/core/sdio.c | 176 | ||||
| -rw-r--r-- | drivers/mmc/core/sdio_ops.c | 49 | ||||
| -rw-r--r-- | drivers/mmc/core/sdio_ops.h | 18 | 
6 files changed, 281 insertions, 13 deletions
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 3fdd08c7f14..2fa5ebbc170 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,6 @@ endif  obj-$(CONFIG_MMC)		+= mmc_core.o  mmc_core-y			:= core.o sysfs.o bus.o host.o \ -				   mmc.o mmc_ops.o sd.o sd_ops.o +				   mmc.o mmc_ops.o sd.o sd_ops.o \ +				   sdio.o sdio_ops.o diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 817a79462b3..87a6070522f 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -34,6 +34,8 @@ static ssize_t mmc_type_show(struct device *dev,  		return sprintf(buf, "MMC\n");  	case MMC_TYPE_SD:  		return sprintf(buf, "SD\n"); +	case MMC_TYPE_SDIO: +		return sprintf(buf, "SDIO\n");  	default:  		return -EFAULT;  	} @@ -76,6 +78,9 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,  	case MMC_TYPE_SD:  		add_env("MMC_TYPE=%s", "SD");  		break; +	case MMC_TYPE_SDIO: +		add_env("MMC_TYPE=%s", "SDIO"); +		break;  	}  	add_env("MMC_NAME=%s", mmc_card_name(card)); @@ -221,6 +226,9 @@ int mmc_add_card(struct mmc_card *card)  		if (mmc_card_blockaddr(card))  			type = "SDHC";  		break; +	case MMC_TYPE_SDIO: +		type = "SDIO"; +		break;  	default:  		type = "?";  		break; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 51e611f2f33..092fa906ab8 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -32,9 +32,11 @@  #include "mmc_ops.h"  #include "sd_ops.h" +#include "sdio_ops.h"  extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);  extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); +extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);  static struct workqueue_struct *workqueue; @@ -595,24 +597,38 @@ void mmc_rescan(struct work_struct *work)  		mmc_send_if_cond(host, host->ocr_avail); +		/* +		 * First we search for SDIO... +		 */ +		err = mmc_send_io_op_cond(host, 0, &ocr); +		if (!err) { +			if (mmc_attach_sdio(host, ocr)) +				mmc_power_off(host); +			return; +		} + +		/* +		 * ...then normal SD... +		 */  		err = mmc_send_app_op_cond(host, 0, &ocr);  		if (!err) {  			if (mmc_attach_sd(host, ocr))  				mmc_power_off(host); -		} else { -			/* -			 * If we fail to detect any SD cards then try -			 * searching for MMC cards. -			 */ -			err = mmc_send_op_cond(host, 0, &ocr); -			if (!err) { -				if (mmc_attach_mmc(host, ocr)) -					mmc_power_off(host); -			} else { +			return; +		} + +		/* +		 * ...and finally MMC. +		 */ +		err = mmc_send_op_cond(host, 0, &ocr); +		if (!err) { +			if (mmc_attach_mmc(host, ocr))  				mmc_power_off(host); -				mmc_release_host(host); -			} +			return;  		} + +		mmc_release_host(host); +		mmc_power_off(host);  	} else {  		if (host->bus_ops->detect && !host->bus_dead)  			host->bus_ops->detect(host); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c new file mode 100644 index 00000000000..ac0dd68df8e --- /dev/null +++ b/drivers/mmc/core/sdio.c @@ -0,0 +1,176 @@ +/* + *  linux/drivers/mmc/sdio.c + * + *  Copyright 2006-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/err.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/card.h> + +#include "core.h" +#include "bus.h" +#include "mmc_ops.h" +#include "sd_ops.h" +#include "sdio_ops.h" + +/* + * Host is being removed. Free up the current card. + */ +static void mmc_sdio_remove(struct mmc_host *host) +{ +	BUG_ON(!host); +	BUG_ON(!host->card); + +	mmc_remove_card(host->card); +	host->card = NULL; +} + +/* + * Card detection callback from host. + */ +static void mmc_sdio_detect(struct mmc_host *host) +{ +	int err; + +	BUG_ON(!host); +	BUG_ON(!host->card); + +	mmc_claim_host(host); + +	/* +	 * Just check if our card has been removed. +	 */ +	err = mmc_select_card(host->card); + +	mmc_release_host(host); + +	if (err) { +		mmc_sdio_remove(host); + +		mmc_claim_host(host); +		mmc_detach_bus(host); +		mmc_release_host(host); +	} +} + + +static const struct mmc_bus_ops mmc_sdio_ops = { +	.remove = mmc_sdio_remove, +	.detect = mmc_sdio_detect, +}; + + +/* + * Starting point for SDIO card init. + */ +int mmc_attach_sdio(struct mmc_host *host, u32 ocr) +{ +	int err; +	int funcs; +	struct mmc_card *card; + +	BUG_ON(!host); +	BUG_ON(!host->claimed); + +	mmc_attach_bus(host, &mmc_sdio_ops); + +	/* +	 * Sanity check the voltages that the card claims to +	 * support. +	 */ +	if (ocr & 0x7F) { +		printk(KERN_WARNING "%s: card claims to support voltages " +		       "below the defined range. These will be ignored.\n", +		       mmc_hostname(host)); +		ocr &= ~0x7F; +	} + +	if (ocr & MMC_VDD_165_195) { +		printk(KERN_WARNING "%s: SDIO card claims to support the " +		       "incompletely defined 'low voltage range'. This " +		       "will be ignored.\n", mmc_hostname(host)); +		ocr &= ~MMC_VDD_165_195; +	} + +	host->ocr = mmc_select_voltage(host, ocr); + +	/* +	 * Can we support the voltage(s) of the card(s)? +	 */ +	if (!host->ocr) { +		err = -EINVAL; +		goto err; +	} + +	/* +	 * Inform the card of the voltage +	 */ +	err = mmc_send_io_op_cond(host, host->ocr, &ocr); +	if (err) +		goto err; + +	/* +	 * The number of functions on the card is encoded inside +	 * the ocr. +	 */ +	funcs = (ocr & 0x70000000) >> 28; + +	/* +	 * Allocate card structure. +	 */ +	card = mmc_alloc_card(host); +	if (IS_ERR(card)) { +		err = PTR_ERR(card); +		goto err; +	} + +	card->type = MMC_TYPE_SDIO; + +	/* +	 * Set card RCA. +	 */ +	err = mmc_send_relative_addr(host, &card->rca); +	if (err) +		goto free_card; + +	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + +	/* +	 * Select card, as all following commands rely on that. +	 */ +	err = mmc_select_card(card); +	if (err) +		goto free_card; + +	host->card = card; + +	mmc_release_host(host); + +	err = mmc_add_card(host->card); +	if (err) +		goto reclaim_host; + +	return 0; + +reclaim_host: +	mmc_claim_host(host); +free_card: +	mmc_remove_card(card); +	host->card = NULL; +err: +	mmc_detach_bus(host); +	mmc_release_host(host); + +	printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", +		mmc_hostname(host), err); + +	return err; +} + diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c new file mode 100644 index 00000000000..d6f9f9d8517 --- /dev/null +++ b/drivers/mmc/core/sdio_ops.c @@ -0,0 +1,49 @@ +/* + *  linux/drivers/mmc/sdio_ops.c + * + *  Copyright 2006-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/mmc.h> +#include <linux/mmc/sdio.h> + +#include "core.h" + +int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ +	struct mmc_command cmd; +	int i, err = 0; + +	BUG_ON(!host); + +	memset(&cmd, 0, sizeof(struct mmc_command)); + +	cmd.opcode = SD_IO_SEND_OP_COND; +	cmd.arg = ocr; +	cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR; + +	for (i = 100; i; i--) { +		err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +		if (err) +			break; + +		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) +			break; + +		err = -ETIMEDOUT; + +		mmc_delay(10); +	} + +	if (rocr) +		*rocr = cmd.resp[0]; + +	return err; +} + diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h new file mode 100644 index 00000000000..d8c982976f1 --- /dev/null +++ b/drivers/mmc/core/sdio_ops.h @@ -0,0 +1,18 @@ +/* + *  linux/drivers/mmc/sdio_ops.c + * + *  Copyright 2006-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_OPS_H +#define _MMC_SDIO_OPS_H + +int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); + +#endif +  |