diff options
| author | Wim Van Sebroeck <wim@iguana.be> | 2007-05-11 19:03:13 +0000 | 
|---|---|---|
| committer | Wim Van Sebroeck <wim@iguana.be> | 2007-05-11 19:03:13 +0000 | 
| commit | 5c34202b8bf942da411b6599668a76b07449bbfd (patch) | |
| tree | 5719c361321eaddc8e4f1b0c8a7994f0e9a6fdd3 /drivers/mmc/core/sd_ops.c | |
| parent | 0d4804b31f91cfbcff6d62af0bc09a893a1c8ae0 (diff) | |
| parent | 1f8a6b658a943b4f04a1fc7b3a420360202c86cd (diff) | |
Merge /pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/mmc/core/sd_ops.c')
| -rw-r--r-- | drivers/mmc/core/sd_ops.c | 316 | 
1 files changed, 316 insertions, 0 deletions
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c new file mode 100644 index 00000000000..9697ce58110 --- /dev/null +++ b/drivers/mmc/core/sd_ops.c @@ -0,0 +1,316 @@ +/* + *  linux/drivers/mmc/sd_ops.h + * + *  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/types.h> +#include <asm/scatterlist.h> +#include <linux/scatterlist.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/card.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> + +#include "core.h" +#include "sd_ops.h" + +/** + *	mmc_wait_for_app_cmd - start an application command and wait for + 			       completion + *	@host: MMC host to start command + *	@rca: RCA to send MMC_APP_CMD to + *	@cmd: MMC command to start + *	@retries: maximum number of retries + * + *	Sends a MMC_APP_CMD, checks the card response, sends the command + *	in the parameter and waits for it to complete. Return any error + *	that occurred while the command was executing.  Do not attempt to + *	parse the response. + */ +int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, +	struct mmc_command *cmd, int retries) +{ +	struct mmc_request mrq; + +	int i, err; + +	BUG_ON(!cmd); +	BUG_ON(retries < 0); + +	err = MMC_ERR_INVALID; + +	/* +	 * We have to resend MMC_APP_CMD for each attempt so +	 * we cannot use the retries field in mmc_command. +	 */ +	for (i = 0;i <= retries;i++) { +		memset(&mrq, 0, sizeof(struct mmc_request)); + +		err = mmc_app_cmd(host, card); +		if (err != MMC_ERR_NONE) +			continue; + +		memset(&mrq, 0, sizeof(struct mmc_request)); + +		memset(cmd->resp, 0, sizeof(cmd->resp)); +		cmd->retries = 0; + +		mrq.cmd = cmd; +		cmd->data = NULL; + +		mmc_wait_for_req(host, &mrq); + +		err = cmd->error; +		if (cmd->error == MMC_ERR_NONE) +			break; +	} + +	return err; +} + +EXPORT_SYMBOL(mmc_wait_for_app_cmd); + +int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) +{ +	int err; +	struct mmc_command cmd; + +	BUG_ON(!host); +	BUG_ON(card && (card->host != host)); + +	cmd.opcode = MMC_APP_CMD; + +	if (card) { +		cmd.arg = card->rca << 16; +		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; +	} else { +		cmd.arg = 0; +		cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR; +	} + +	err = mmc_wait_for_cmd(host, &cmd, 0); +	if (err != MMC_ERR_NONE) +		return err; + +	/* Check that card supported application commands */ +	if (!(cmd.resp[0] & R1_APP_CMD)) +		return MMC_ERR_FAILED; + +	return MMC_ERR_NONE; +} + +int mmc_app_set_bus_width(struct mmc_card *card, int width) +{ +	int err; +	struct mmc_command cmd; + +	BUG_ON(!card); +	BUG_ON(!card->host); + +	memset(&cmd, 0, sizeof(struct mmc_command)); + +	cmd.opcode = SD_APP_SET_BUS_WIDTH; +	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + +	switch (width) { +	case MMC_BUS_WIDTH_1: +		cmd.arg = SD_BUS_WIDTH_1; +		break; +	case MMC_BUS_WIDTH_4: +		cmd.arg = SD_BUS_WIDTH_4; +		break; +	default: +		return MMC_ERR_INVALID; +	} + +	err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES); +	if (err != MMC_ERR_NONE) +		return err; + +	return MMC_ERR_NONE; +} + +int mmc_send_app_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_APP_OP_COND; +	cmd.arg = ocr; +	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + +	for (i = 100; i; i--) { +		err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); +		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; +} + +int mmc_send_if_cond(struct mmc_host *host, u32 ocr) +{ +	struct mmc_command cmd; +	int err; +	static const u8 test_pattern = 0xAA; + +	/* +	 * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND +	 * before SD_APP_OP_COND. This command will harmlessly fail for +	 * SD 1.0 cards. +	 */ +	cmd.opcode = SD_SEND_IF_COND; +	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; +	cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; + +	err = mmc_wait_for_cmd(host, &cmd, 0); +	if (err != MMC_ERR_NONE) +		return err; + +	if ((cmd.resp[0] & 0xFF) != test_pattern) +		return MMC_ERR_FAILED; + +	return MMC_ERR_NONE; +} + +int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) +{ +	int err; +	struct mmc_command cmd; + +	BUG_ON(!host); +	BUG_ON(!rca); + +	memset(&cmd, 0, sizeof(struct mmc_command)); + +	cmd.opcode = SD_SEND_RELATIVE_ADDR; +	cmd.arg = 0; +	cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + +	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +	if (err != MMC_ERR_NONE) +		return err; + +	*rca = cmd.resp[0] >> 16; + +	return MMC_ERR_NONE; +} + +int mmc_app_send_scr(struct mmc_card *card, u32 *scr) +{ +	int err; +	struct mmc_request mrq; +	struct mmc_command cmd; +	struct mmc_data data; +	struct scatterlist sg; + +	BUG_ON(!card); +	BUG_ON(!card->host); +	BUG_ON(!scr); + +	err = mmc_app_cmd(card->host, card); +	if (err != MMC_ERR_NONE) +		return err; + +	memset(&mrq, 0, sizeof(struct mmc_request)); +	memset(&cmd, 0, sizeof(struct mmc_command)); +	memset(&data, 0, sizeof(struct mmc_data)); + +	mrq.cmd = &cmd; +	mrq.data = &data; + +	cmd.opcode = SD_APP_SEND_SCR; +	cmd.arg = 0; +	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + +	data.blksz = 8; +	data.blocks = 1; +	data.flags = MMC_DATA_READ; +	data.sg = &sg; +	data.sg_len = 1; + +	sg_init_one(&sg, scr, 8); + +	mmc_set_data_timeout(&data, card, 0); + +	mmc_wait_for_req(card->host, &mrq); + +	if (cmd.error != MMC_ERR_NONE) +		return cmd.error; +	if (data.error != MMC_ERR_NONE) +		return data.error; + +	scr[0] = ntohl(scr[0]); +	scr[1] = ntohl(scr[1]); + +	return MMC_ERR_NONE; +} + +int mmc_sd_switch(struct mmc_card *card, int mode, int group, +	u8 value, u8 *resp) +{ +	struct mmc_request mrq; +	struct mmc_command cmd; +	struct mmc_data data; +	struct scatterlist sg; + +	BUG_ON(!card); +	BUG_ON(!card->host); + +	mode = !!mode; +	value &= 0xF; + +	memset(&mrq, 0, sizeof(struct mmc_request)); +	memset(&cmd, 0, sizeof(struct mmc_command)); +	memset(&data, 0, sizeof(struct mmc_data)); + +	mrq.cmd = &cmd; +	mrq.data = &data; + +	cmd.opcode = SD_SWITCH; +	cmd.arg = mode << 31 | 0x00FFFFFF; +	cmd.arg &= ~(0xF << (group * 4)); +	cmd.arg |= value << (group * 4); +	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + +	data.blksz = 64; +	data.blocks = 1; +	data.flags = MMC_DATA_READ; +	data.sg = &sg; +	data.sg_len = 1; + +	sg_init_one(&sg, resp, 64); + +	mmc_set_data_timeout(&data, card, 0); + +	mmc_wait_for_req(card->host, &mrq); + +	if (cmd.error != MMC_ERR_NONE) +		return cmd.error; +	if (data.error != MMC_ERR_NONE) +		return data.error; + +	return MMC_ERR_NONE; +} +  | 
