diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-16 15:11:07 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-16 15:17:52 -0700 | 
| commit | 8a0ca91e1db5de5eb5b18cfa919d52ff8be375af (patch) | |
| tree | bd3a1564940d27ae7f6229089db1283ff2a636c8 /drivers | |
| parent | 9c1be0c4712fe760d8969427ef91107e9c062d91 (diff) | |
| parent | c43d8636971c39da993e94082fd65bfff421618e (diff) | |
| download | olio-linux-3.10-8a0ca91e1db5de5eb5b18cfa919d52ff8be375af.tar.xz olio-linux-3.10-8a0ca91e1db5de5eb5b18cfa919d52ff8be375af.zip  | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (68 commits)
  sdio_uart: Fix SDIO break control to now return success or an error
  mmc: host driver for Ricoh Bay1Controllers
  sdio: sdio_io.c Fix sparse warnings
  sdio: fix the use of hard coded timeout value.
  mmc: OLPC: update vdd/powerup quirk comment
  mmc: fix spares errors of sdhci.c
  mmc: remove multiwrite capability
  wbsd: fix bad dma_addr_t conversion
  atmel-mci: Driver for Atmel on-chip MMC controllers
  mmc: fix sdio_io sparse errors
  mmc: wbsd.c fix shadowing of 'dma' variable
  MMC: S3C24XX: Refuse incorrectly aligned transfers
  MMC: S3C24XX: Add maintainer entry
  MMC: S3C24XX: Update error debugging.
  MMC: S3C24XX: Add media presence test to request handling.
  MMC: S3C24XX: Fix use of msecs where jiffies are needed
  MMC: S3C24XX: Add MODULE_ALIAS() entries for the platform devices
  MMC: S3C24XX: Fix s3c2410_dma_request() return code check.
  MMC: S3C24XX: Allow card-detect on non-IRQ capable pin
  MMC: S3C24XX: Ensure host->mrq->data is valid
  ...
Manually fixed up bogus executable bits on drivers/mmc/core/sdio_io.c
and include/linux/mmc/sdio_func.h when merging.
Diffstat (limited to 'drivers')
29 files changed, 5889 insertions, 1279 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index f9ad960d7c1..66e5a5487c2 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2,7 +2,7 @@   * Block driver for media (i.e., flash cards)   *   * Copyright 2002 Hewlett-Packard Company - * Copyright 2005-2007 Pierre Ossman + * Copyright 2005-2008 Pierre Ossman   *   * Use consistent with the GNU GPL is permitted,   * provided that this copyright notice is @@ -237,17 +237,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  		if (brq.data.blocks > card->host->max_blk_count)  			brq.data.blocks = card->host->max_blk_count; -		/* -		 * If the host doesn't support multiple block writes, force -		 * block writes to single block. SD cards are excepted from -		 * this rule as they support querying the number of -		 * successfully written sectors. -		 */ -		if (rq_data_dir(req) != READ && -		    !(card->host->caps & MMC_CAP_MULTIWRITE) && -		    !mmc_card_sd(card)) -			brq.data.blocks = 1; -  		if (brq.data.blocks > 1) {  			/* SPI multiblock writes terminate using a special  			 * token, not a STOP_TRANSMISSION request. @@ -296,22 +285,24 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  		mmc_queue_bounce_post(mq); +		/* +		 * Check for errors here, but don't jump to cmd_err +		 * until later as we need to wait for the card to leave +		 * programming mode even when things go wrong. +		 */  		if (brq.cmd.error) {  			printk(KERN_ERR "%s: error %d sending read/write command\n",  			       req->rq_disk->disk_name, brq.cmd.error); -			goto cmd_err;  		}  		if (brq.data.error) {  			printk(KERN_ERR "%s: error %d transferring data\n",  			       req->rq_disk->disk_name, brq.data.error); -			goto cmd_err;  		}  		if (brq.stop.error) {  			printk(KERN_ERR "%s: error %d sending stop command\n",  			       req->rq_disk->disk_name, brq.stop.error); -			goto cmd_err;  		}  		if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { @@ -344,6 +335,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  #endif  		} +		if (brq.cmd.error || brq.data.error || brq.stop.error) +			goto cmd_err; +  		/*  		 * A block was successfully transferred.  		 */ @@ -362,30 +356,32 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)   	 * mark the known good sectors as ok.   	 *  	 * If the card is not SD, we can still ok written sectors -	 * if the controller can do proper error reporting. +	 * as reported by the controller (which might be less than +	 * the real number of written sectors, but never more).  	 *  	 * For reads we just fail the entire chunk as that should  	 * be safe in all cases.  	 */ - 	if (rq_data_dir(req) != READ && mmc_card_sd(card)) { -		u32 blocks; -		unsigned int bytes; +	if (rq_data_dir(req) != READ) { +		if (mmc_card_sd(card)) { +			u32 blocks; +			unsigned int bytes; -		blocks = mmc_sd_num_wr_blocks(card); -		if (blocks != (u32)-1) { -			if (card->csd.write_partial) -				bytes = blocks << md->block_bits; -			else -				bytes = blocks << 9; +			blocks = mmc_sd_num_wr_blocks(card); +			if (blocks != (u32)-1) { +				if (card->csd.write_partial) +					bytes = blocks << md->block_bits; +				else +					bytes = blocks << 9; +				spin_lock_irq(&md->lock); +				ret = __blk_end_request(req, 0, bytes); +				spin_unlock_irq(&md->lock); +			} +		} else {  			spin_lock_irq(&md->lock); -			ret = __blk_end_request(req, 0, bytes); +			ret = __blk_end_request(req, 0, brq.data.bytes_xfered);  			spin_unlock_irq(&md->lock);  		} -	} else if (rq_data_dir(req) != READ && -		   (card->host->caps & MMC_CAP_MULTIWRITE)) { -		spin_lock_irq(&md->lock); -		ret = __blk_end_request(req, 0, brq.data.bytes_xfered); -		spin_unlock_irq(&md->lock);  	}  	mmc_release_host(card->host); diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index ffadee549a4..d6b9b486417 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -1,7 +1,7 @@  /*   *  linux/drivers/mmc/card/mmc_test.c   * - *  Copyright 2007 Pierre Ossman + *  Copyright 2007-2008 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 @@ -26,13 +26,17 @@  struct mmc_test_card {  	struct mmc_card	*card; +	u8		scratch[BUFFER_SIZE];  	u8		*buffer;  };  /*******************************************************************/ -/*  Helper functions                                               */ +/*  General helper functions                                       */  /*******************************************************************/ +/* + * Configure correct block size in card + */  static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)  {  	struct mmc_command cmd; @@ -48,117 +52,61 @@ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)  	return 0;  } -static int __mmc_test_transfer(struct mmc_test_card *test, int write, -	unsigned broken_xfer, u8 *buffer, unsigned addr, -	unsigned blocks, unsigned blksz) +/* + * Fill in the mmc_request structure given a set of transfer parameters. + */ +static void mmc_test_prepare_mrq(struct mmc_test_card *test, +	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len, +	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)  { -	int ret, busy; - -	struct mmc_request mrq; -	struct mmc_command cmd; -	struct mmc_command stop; -	struct mmc_data data; - -	struct scatterlist sg; - -	memset(&mrq, 0, sizeof(struct mmc_request)); - -	mrq.cmd = &cmd; -	mrq.data = &data; - -	memset(&cmd, 0, sizeof(struct mmc_command)); +	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop); -	if (broken_xfer) { -		if (blocks > 1) { -			cmd.opcode = write ? -				MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK; -		} else { -			cmd.opcode = MMC_SEND_STATUS; -		} +	if (blocks > 1) { +		mrq->cmd->opcode = write ? +			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;  	} else { -		if (blocks > 1) { -			cmd.opcode = write ? -				MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK; -		} else { -			cmd.opcode = write ? -				MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK; -		} +		mrq->cmd->opcode = write ? +			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;  	} -	if (broken_xfer && blocks == 1) -		cmd.arg = test->card->rca << 16; -	else -		cmd.arg = addr; -	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; +	mrq->cmd->arg = dev_addr; +	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC; -	memset(&stop, 0, sizeof(struct mmc_command)); - -	if (!broken_xfer && (blocks > 1)) { -		stop.opcode = MMC_STOP_TRANSMISSION; -		stop.arg = 0; -		stop.flags = MMC_RSP_R1B | MMC_CMD_AC; - -		mrq.stop = &stop; +	if (blocks == 1) +		mrq->stop = NULL; +	else { +		mrq->stop->opcode = MMC_STOP_TRANSMISSION; +		mrq->stop->arg = 0; +		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;  	} -	memset(&data, 0, sizeof(struct mmc_data)); - -	data.blksz = blksz; -	data.blocks = blocks; -	data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; -	data.sg = &sg; -	data.sg_len = 1; - -	sg_init_one(&sg, buffer, blocks * blksz); - -	mmc_set_data_timeout(&data, test->card); +	mrq->data->blksz = blksz; +	mrq->data->blocks = blocks; +	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; +	mrq->data->sg = sg; +	mrq->data->sg_len = sg_len; -	mmc_wait_for_req(test->card->host, &mrq); - -	ret = 0; - -	if (broken_xfer) { -		if (!ret && cmd.error) -			ret = cmd.error; -		if (!ret && data.error == 0) -			ret = RESULT_FAIL; -		if (!ret && data.error != -ETIMEDOUT) -			ret = data.error; -		if (!ret && stop.error) -			ret = stop.error; -		if (blocks > 1) { -			if (!ret && data.bytes_xfered > blksz) -				ret = RESULT_FAIL; -		} else { -			if (!ret && data.bytes_xfered > 0) -				ret = RESULT_FAIL; -		} -	} else { -		if (!ret && cmd.error) -			ret = cmd.error; -		if (!ret && data.error) -			ret = data.error; -		if (!ret && stop.error) -			ret = stop.error; -		if (!ret && data.bytes_xfered != blocks * blksz) -			ret = RESULT_FAIL; -	} +	mmc_set_data_timeout(mrq->data, test->card); +} -	if (ret == -EINVAL) -		ret = RESULT_UNSUP_HOST; +/* + * Wait for the card to finish the busy state + */ +static int mmc_test_wait_busy(struct mmc_test_card *test) +{ +	int ret, busy; +	struct mmc_command cmd;  	busy = 0;  	do { -		int ret2; -  		memset(&cmd, 0, sizeof(struct mmc_command));  		cmd.opcode = MMC_SEND_STATUS;  		cmd.arg = test->card->rca << 16;  		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; -		ret2 = mmc_wait_for_cmd(test->card->host, &cmd, 0); -		if (ret2) +		ret = mmc_wait_for_cmd(test->card->host, &cmd, 0); +		if (ret)  			break;  		if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) { @@ -172,14 +120,57 @@ static int __mmc_test_transfer(struct mmc_test_card *test, int write,  	return ret;  } -static int mmc_test_transfer(struct mmc_test_card *test, int write, -	u8 *buffer, unsigned addr, unsigned blocks, unsigned blksz) +/* + * Transfer a single sector of kernel addressable data + */ +static int mmc_test_buffer_transfer(struct mmc_test_card *test, +	u8 *buffer, unsigned addr, unsigned blksz, int write)  { -	return __mmc_test_transfer(test, write, 0, buffer, -			addr, blocks, blksz); +	int ret; + +	struct mmc_request mrq; +	struct mmc_command cmd; +	struct mmc_command stop; +	struct mmc_data data; + +	struct scatterlist sg; + +	memset(&mrq, 0, sizeof(struct mmc_request)); +	memset(&cmd, 0, sizeof(struct mmc_command)); +	memset(&data, 0, sizeof(struct mmc_data)); +	memset(&stop, 0, sizeof(struct mmc_command)); + +	mrq.cmd = &cmd; +	mrq.data = &data; +	mrq.stop = &stop; + +	sg_init_one(&sg, buffer, blksz); + +	mmc_test_prepare_mrq(test, &mrq, &sg, 1, addr, 1, blksz, write); + +	mmc_wait_for_req(test->card->host, &mrq); + +	if (cmd.error) +		return cmd.error; +	if (data.error) +		return data.error; + +	ret = mmc_test_wait_busy(test); +	if (ret) +		return ret; + +	return 0;  } -static int mmc_test_prepare_verify(struct mmc_test_card *test, int write) +/*******************************************************************/ +/*  Test preparation and cleanup                                   */ +/*******************************************************************/ + +/* + * Fill the first couple of sectors of the card with known data + * so that bad reads/writes can be detected + */ +static int __mmc_test_prepare(struct mmc_test_card *test, int write)  {  	int ret, i; @@ -188,15 +179,14 @@ static int mmc_test_prepare_verify(struct mmc_test_card *test, int write)  		return ret;  	if (write) -		memset(test->buffer, 0xDF, BUFFER_SIZE); +		memset(test->buffer, 0xDF, 512);  	else { -		for (i = 0;i < BUFFER_SIZE;i++) +		for (i = 0;i < 512;i++)  			test->buffer[i] = i;  	}  	for (i = 0;i < BUFFER_SIZE / 512;i++) { -		ret = mmc_test_transfer(test, 1, test->buffer + i * 512, -			i * 512, 1, 512); +		ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);  		if (ret)  			return ret;  	} @@ -204,41 +194,218 @@ static int mmc_test_prepare_verify(struct mmc_test_card *test, int write)  	return 0;  } -static int mmc_test_prepare_verify_write(struct mmc_test_card *test) +static int mmc_test_prepare_write(struct mmc_test_card *test) +{ +	return __mmc_test_prepare(test, 1); +} + +static int mmc_test_prepare_read(struct mmc_test_card *test) +{ +	return __mmc_test_prepare(test, 0); +} + +static int mmc_test_cleanup(struct mmc_test_card *test)  { -	return mmc_test_prepare_verify(test, 1); +	int ret, i; + +	ret = mmc_test_set_blksize(test, 512); +	if (ret) +		return ret; + +	memset(test->buffer, 0, 512); + +	for (i = 0;i < BUFFER_SIZE / 512;i++) { +		ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1); +		if (ret) +			return ret; +	} + +	return 0;  } -static int mmc_test_prepare_verify_read(struct mmc_test_card *test) +/*******************************************************************/ +/*  Test execution helpers                                         */ +/*******************************************************************/ + +/* + * Modifies the mmc_request to perform the "short transfer" tests + */ +static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test, +	struct mmc_request *mrq, int write)  { -	return mmc_test_prepare_verify(test, 0); +	BUG_ON(!mrq || !mrq->cmd || !mrq->data); + +	if (mrq->data->blocks > 1) { +		mrq->cmd->opcode = write ? +			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK; +		mrq->stop = NULL; +	} else { +		mrq->cmd->opcode = MMC_SEND_STATUS; +		mrq->cmd->arg = test->card->rca << 16; +	}  } -static int mmc_test_verified_transfer(struct mmc_test_card *test, int write, -	u8 *buffer, unsigned addr, unsigned blocks, unsigned blksz) +/* + * Checks that a normal transfer didn't have any errors + */ +static int mmc_test_check_result(struct mmc_test_card *test, +	struct mmc_request *mrq)  { -	int ret, i, sectors; +	int ret; -	/* -	 * It is assumed that the above preparation has been done. -	 */ +	BUG_ON(!mrq || !mrq->cmd || !mrq->data); + +	ret = 0; -	memset(test->buffer, 0, BUFFER_SIZE); +	if (!ret && mrq->cmd->error) +		ret = mrq->cmd->error; +	if (!ret && mrq->data->error) +		ret = mrq->data->error; +	if (!ret && mrq->stop && mrq->stop->error) +		ret = mrq->stop->error; +	if (!ret && mrq->data->bytes_xfered != +		mrq->data->blocks * mrq->data->blksz) +		ret = RESULT_FAIL; + +	if (ret == -EINVAL) +		ret = RESULT_UNSUP_HOST; + +	return ret; +} + +/* + * Checks that a "short transfer" behaved as expected + */ +static int mmc_test_check_broken_result(struct mmc_test_card *test, +	struct mmc_request *mrq) +{ +	int ret; + +	BUG_ON(!mrq || !mrq->cmd || !mrq->data); + +	ret = 0; + +	if (!ret && mrq->cmd->error) +		ret = mrq->cmd->error; +	if (!ret && mrq->data->error == 0) +		ret = RESULT_FAIL; +	if (!ret && mrq->data->error != -ETIMEDOUT) +		ret = mrq->data->error; +	if (!ret && mrq->stop && mrq->stop->error) +		ret = mrq->stop->error; +	if (mrq->data->blocks > 1) { +		if (!ret && mrq->data->bytes_xfered > mrq->data->blksz) +			ret = RESULT_FAIL; +	} else { +		if (!ret && mrq->data->bytes_xfered > 0) +			ret = RESULT_FAIL; +	} + +	if (ret == -EINVAL) +		ret = RESULT_UNSUP_HOST; + +	return ret; +} + +/* + * Tests a basic transfer with certain parameters + */ +static int mmc_test_simple_transfer(struct mmc_test_card *test, +	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr, +	unsigned blocks, unsigned blksz, int write) +{ +	struct mmc_request mrq; +	struct mmc_command cmd; +	struct mmc_command stop; +	struct mmc_data data; + +	memset(&mrq, 0, sizeof(struct mmc_request)); +	memset(&cmd, 0, sizeof(struct mmc_command)); +	memset(&data, 0, sizeof(struct mmc_data)); +	memset(&stop, 0, sizeof(struct mmc_command)); + +	mrq.cmd = &cmd; +	mrq.data = &data; +	mrq.stop = &stop; + +	mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr, +		blocks, blksz, write); + +	mmc_wait_for_req(test->card->host, &mrq); + +	mmc_test_wait_busy(test); + +	return mmc_test_check_result(test, &mrq); +} + +/* + * Tests a transfer where the card will fail completely or partly + */ +static int mmc_test_broken_transfer(struct mmc_test_card *test, +	unsigned blocks, unsigned blksz, int write) +{ +	struct mmc_request mrq; +	struct mmc_command cmd; +	struct mmc_command stop; +	struct mmc_data data; + +	struct scatterlist sg; + +	memset(&mrq, 0, sizeof(struct mmc_request)); +	memset(&cmd, 0, sizeof(struct mmc_command)); +	memset(&data, 0, sizeof(struct mmc_data)); +	memset(&stop, 0, sizeof(struct mmc_command)); + +	mrq.cmd = &cmd; +	mrq.data = &data; +	mrq.stop = &stop; + +	sg_init_one(&sg, test->buffer, blocks * blksz); + +	mmc_test_prepare_mrq(test, &mrq, &sg, 1, 0, blocks, blksz, write); +	mmc_test_prepare_broken_mrq(test, &mrq, write); + +	mmc_wait_for_req(test->card->host, &mrq); + +	mmc_test_wait_busy(test); + +	return mmc_test_check_broken_result(test, &mrq); +} + +/* + * Does a complete transfer test where data is also validated + * + * Note: mmc_test_prepare() must have been done before this call + */ +static int mmc_test_transfer(struct mmc_test_card *test, +	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr, +	unsigned blocks, unsigned blksz, int write) +{ +	int ret, i; +	unsigned long flags;  	if (write) {  		for (i = 0;i < blocks * blksz;i++) -			buffer[i] = i; +			test->scratch[i] = i; +	} else { +		memset(test->scratch, 0, BUFFER_SIZE);  	} +	local_irq_save(flags); +	sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE); +	local_irq_restore(flags);  	ret = mmc_test_set_blksize(test, blksz);  	if (ret)  		return ret; -	ret = mmc_test_transfer(test, write, buffer, addr, blocks, blksz); +	ret = mmc_test_simple_transfer(test, sg, sg_len, dev_addr, +		blocks, blksz, write);  	if (ret)  		return ret;  	if (write) { +		int sectors; +  		ret = mmc_test_set_blksize(test, 512);  		if (ret)  			return ret; @@ -253,9 +420,9 @@ static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,  		memset(test->buffer, 0, sectors * 512);  		for (i = 0;i < sectors;i++) { -			ret = mmc_test_transfer(test, 0, +			ret = mmc_test_buffer_transfer(test,  				test->buffer + i * 512, -				addr + i * 512, 1, 512); +				dev_addr + i * 512, 512, 0);  			if (ret)  				return ret;  		} @@ -270,8 +437,11 @@ static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,  				return RESULT_FAIL;  		}  	} else { +		local_irq_save(flags); +		sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE); +		local_irq_restore(flags);  		for (i = 0;i < blocks * blksz;i++) { -			if (buffer[i] != (u8)i) +			if (test->scratch[i] != (u8)i)  				return RESULT_FAIL;  		}  	} @@ -279,26 +449,6 @@ static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,  	return 0;  } -static int mmc_test_cleanup_verify(struct mmc_test_card *test) -{ -	int ret, i; - -	ret = mmc_test_set_blksize(test, 512); -	if (ret) -		return ret; - -	memset(test->buffer, 0, BUFFER_SIZE); - -	for (i = 0;i < BUFFER_SIZE / 512;i++) { -		ret = mmc_test_transfer(test, 1, test->buffer + i * 512, -			i * 512, 1, 512); -		if (ret) -			return ret; -	} - -	return 0; -} -  /*******************************************************************/  /*  Tests                                                          */  /*******************************************************************/ @@ -314,12 +464,15 @@ struct mmc_test_case {  static int mmc_test_basic_write(struct mmc_test_card *test)  {  	int ret; +	struct scatterlist sg;  	ret = mmc_test_set_blksize(test, 512);  	if (ret)  		return ret; -	ret = mmc_test_transfer(test, 1, test->buffer, 0, 1, 512); +	sg_init_one(&sg, test->buffer, 512); + +	ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);  	if (ret)  		return ret; @@ -329,12 +482,15 @@ static int mmc_test_basic_write(struct mmc_test_card *test)  static int mmc_test_basic_read(struct mmc_test_card *test)  {  	int ret; +	struct scatterlist sg;  	ret = mmc_test_set_blksize(test, 512);  	if (ret)  		return ret; -	ret = mmc_test_transfer(test, 0, test->buffer, 0, 1, 512); +	sg_init_one(&sg, test->buffer, 512); + +	ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);  	if (ret)  		return ret; @@ -344,8 +500,11 @@ static int mmc_test_basic_read(struct mmc_test_card *test)  static int mmc_test_verify_write(struct mmc_test_card *test)  {  	int ret; +	struct scatterlist sg; + +	sg_init_one(&sg, test->buffer, 512); -	ret = mmc_test_verified_transfer(test, 1, test->buffer, 0, 1, 512); +	ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);  	if (ret)  		return ret; @@ -355,8 +514,11 @@ static int mmc_test_verify_write(struct mmc_test_card *test)  static int mmc_test_verify_read(struct mmc_test_card *test)  {  	int ret; +	struct scatterlist sg; + +	sg_init_one(&sg, test->buffer, 512); -	ret = mmc_test_verified_transfer(test, 0, test->buffer, 0, 1, 512); +	ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);  	if (ret)  		return ret; @@ -367,6 +529,7 @@ static int mmc_test_multi_write(struct mmc_test_card *test)  {  	int ret;  	unsigned int size; +	struct scatterlist sg;  	if (test->card->host->max_blk_count == 1)  		return RESULT_UNSUP_HOST; @@ -379,8 +542,9 @@ static int mmc_test_multi_write(struct mmc_test_card *test)  	if (size < 1024)  		return RESULT_UNSUP_HOST; -	ret = mmc_test_verified_transfer(test, 1, test->buffer, 0, -		size / 512, 512); +	sg_init_one(&sg, test->buffer, size); + +	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);  	if (ret)  		return ret; @@ -391,6 +555,7 @@ static int mmc_test_multi_read(struct mmc_test_card *test)  {  	int ret;  	unsigned int size; +	struct scatterlist sg;  	if (test->card->host->max_blk_count == 1)  		return RESULT_UNSUP_HOST; @@ -403,8 +568,9 @@ static int mmc_test_multi_read(struct mmc_test_card *test)  	if (size < 1024)  		return RESULT_UNSUP_HOST; -	ret = mmc_test_verified_transfer(test, 0, test->buffer, 0, -		size / 512, 512); +	sg_init_one(&sg, test->buffer, size); + +	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);  	if (ret)  		return ret; @@ -414,13 +580,14 @@ static int mmc_test_multi_read(struct mmc_test_card *test)  static int mmc_test_pow2_write(struct mmc_test_card *test)  {  	int ret, i; +	struct scatterlist sg;  	if (!test->card->csd.write_partial)  		return RESULT_UNSUP_CARD;  	for (i = 1; i < 512;i <<= 1) { -		ret = mmc_test_verified_transfer(test, 1, -			test->buffer, 0, 1, i); +		sg_init_one(&sg, test->buffer, i); +		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);  		if (ret)  			return ret;  	} @@ -431,13 +598,14 @@ static int mmc_test_pow2_write(struct mmc_test_card *test)  static int mmc_test_pow2_read(struct mmc_test_card *test)  {  	int ret, i; +	struct scatterlist sg;  	if (!test->card->csd.read_partial)  		return RESULT_UNSUP_CARD;  	for (i = 1; i < 512;i <<= 1) { -		ret = mmc_test_verified_transfer(test, 0, -			test->buffer, 0, 1, i); +		sg_init_one(&sg, test->buffer, i); +		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);  		if (ret)  			return ret;  	} @@ -448,13 +616,14 @@ static int mmc_test_pow2_read(struct mmc_test_card *test)  static int mmc_test_weird_write(struct mmc_test_card *test)  {  	int ret, i; +	struct scatterlist sg;  	if (!test->card->csd.write_partial)  		return RESULT_UNSUP_CARD;  	for (i = 3; i < 512;i += 7) { -		ret = mmc_test_verified_transfer(test, 1, -			test->buffer, 0, 1, i); +		sg_init_one(&sg, test->buffer, i); +		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);  		if (ret)  			return ret;  	} @@ -465,13 +634,14 @@ static int mmc_test_weird_write(struct mmc_test_card *test)  static int mmc_test_weird_read(struct mmc_test_card *test)  {  	int ret, i; +	struct scatterlist sg;  	if (!test->card->csd.read_partial)  		return RESULT_UNSUP_CARD;  	for (i = 3; i < 512;i += 7) { -		ret = mmc_test_verified_transfer(test, 0, -			test->buffer, 0, 1, i); +		sg_init_one(&sg, test->buffer, i); +		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);  		if (ret)  			return ret;  	} @@ -482,10 +652,11 @@ static int mmc_test_weird_read(struct mmc_test_card *test)  static int mmc_test_align_write(struct mmc_test_card *test)  {  	int ret, i; +	struct scatterlist sg;  	for (i = 1;i < 4;i++) { -		ret = mmc_test_verified_transfer(test, 1, test->buffer + i, -			0, 1, 512); +		sg_init_one(&sg, test->buffer + i, 512); +		ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);  		if (ret)  			return ret;  	} @@ -496,10 +667,11 @@ static int mmc_test_align_write(struct mmc_test_card *test)  static int mmc_test_align_read(struct mmc_test_card *test)  {  	int ret, i; +	struct scatterlist sg;  	for (i = 1;i < 4;i++) { -		ret = mmc_test_verified_transfer(test, 0, test->buffer + i, -			0, 1, 512); +		sg_init_one(&sg, test->buffer + i, 512); +		ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);  		if (ret)  			return ret;  	} @@ -511,6 +683,7 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test)  {  	int ret, i;  	unsigned int size; +	struct scatterlist sg;  	if (test->card->host->max_blk_count == 1)  		return RESULT_UNSUP_HOST; @@ -524,8 +697,8 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test)  		return RESULT_UNSUP_HOST;  	for (i = 1;i < 4;i++) { -		ret = mmc_test_verified_transfer(test, 1, test->buffer + i, -			0, size / 512, 512); +		sg_init_one(&sg, test->buffer + i, size); +		ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);  		if (ret)  			return ret;  	} @@ -537,6 +710,7 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test)  {  	int ret, i;  	unsigned int size; +	struct scatterlist sg;  	if (test->card->host->max_blk_count == 1)  		return RESULT_UNSUP_HOST; @@ -550,8 +724,8 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test)  		return RESULT_UNSUP_HOST;  	for (i = 1;i < 4;i++) { -		ret = mmc_test_verified_transfer(test, 0, test->buffer + i, -			0, size / 512, 512); +		sg_init_one(&sg, test->buffer + i, size); +		ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);  		if (ret)  			return ret;  	} @@ -567,7 +741,7 @@ static int mmc_test_xfersize_write(struct mmc_test_card *test)  	if (ret)  		return ret; -	ret = __mmc_test_transfer(test, 1, 1, test->buffer, 0, 1, 512); +	ret = mmc_test_broken_transfer(test, 1, 512, 1);  	if (ret)  		return ret; @@ -582,7 +756,7 @@ static int mmc_test_xfersize_read(struct mmc_test_card *test)  	if (ret)  		return ret; -	ret = __mmc_test_transfer(test, 0, 1, test->buffer, 0, 1, 512); +	ret = mmc_test_broken_transfer(test, 1, 512, 0);  	if (ret)  		return ret; @@ -600,7 +774,7 @@ static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)  	if (ret)  		return ret; -	ret = __mmc_test_transfer(test, 1, 1, test->buffer, 0, 2, 512); +	ret = mmc_test_broken_transfer(test, 2, 512, 1);  	if (ret)  		return ret; @@ -618,7 +792,7 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)  	if (ret)  		return ret; -	ret = __mmc_test_transfer(test, 0, 1, test->buffer, 0, 2, 512); +	ret = mmc_test_broken_transfer(test, 2, 512, 0);  	if (ret)  		return ret; @@ -638,86 +812,86 @@ static const struct mmc_test_case mmc_test_cases[] = {  	{  		.name = "Basic write (with data verification)", -		.prepare = mmc_test_prepare_verify_write, +		.prepare = mmc_test_prepare_write,  		.run = mmc_test_verify_write, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Basic read (with data verification)", -		.prepare = mmc_test_prepare_verify_read, +		.prepare = mmc_test_prepare_read,  		.run = mmc_test_verify_read, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Multi-block write", -		.prepare = mmc_test_prepare_verify_write, +		.prepare = mmc_test_prepare_write,  		.run = mmc_test_multi_write, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Multi-block read", -		.prepare = mmc_test_prepare_verify_read, +		.prepare = mmc_test_prepare_read,  		.run = mmc_test_multi_read, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Power of two block writes", -		.prepare = mmc_test_prepare_verify_write, +		.prepare = mmc_test_prepare_write,  		.run = mmc_test_pow2_write, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Power of two block reads", -		.prepare = mmc_test_prepare_verify_read, +		.prepare = mmc_test_prepare_read,  		.run = mmc_test_pow2_read, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Weird sized block writes", -		.prepare = mmc_test_prepare_verify_write, +		.prepare = mmc_test_prepare_write,  		.run = mmc_test_weird_write, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Weird sized block reads", -		.prepare = mmc_test_prepare_verify_read, +		.prepare = mmc_test_prepare_read,  		.run = mmc_test_weird_read, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Badly aligned write", -		.prepare = mmc_test_prepare_verify_write, +		.prepare = mmc_test_prepare_write,  		.run = mmc_test_align_write, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Badly aligned read", -		.prepare = mmc_test_prepare_verify_read, +		.prepare = mmc_test_prepare_read,  		.run = mmc_test_align_read, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Badly aligned multi-block write", -		.prepare = mmc_test_prepare_verify_write, +		.prepare = mmc_test_prepare_write,  		.run = mmc_test_align_multi_write, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{  		.name = "Badly aligned multi-block read", -		.prepare = mmc_test_prepare_verify_read, +		.prepare = mmc_test_prepare_read,  		.run = mmc_test_align_multi_read, -		.cleanup = mmc_test_cleanup_verify, +		.cleanup = mmc_test_cleanup,  	},  	{ @@ -743,7 +917,7 @@ static const struct mmc_test_case mmc_test_cases[] = {  static struct mutex mmc_test_lock; -static void mmc_test_run(struct mmc_test_card *test) +static void mmc_test_run(struct mmc_test_card *test, int testcase)  {  	int i, ret; @@ -753,6 +927,9 @@ static void mmc_test_run(struct mmc_test_card *test)  	mmc_claim_host(test->card->host);  	for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) { +		if (testcase && ((i + 1) != testcase)) +			continue; +  		printk(KERN_INFO "%s: Test case %d. %s...\n",  			mmc_hostname(test->card->host), i + 1,  			mmc_test_cases[i].name); @@ -824,9 +1001,12 @@ static ssize_t mmc_test_store(struct device *dev,  {  	struct mmc_card *card;  	struct mmc_test_card *test; +	int testcase;  	card = container_of(dev, struct mmc_card, dev); +	testcase = simple_strtol(buf, NULL, 10); +  	test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);  	if (!test)  		return -ENOMEM; @@ -836,7 +1016,7 @@ static ssize_t mmc_test_store(struct device *dev,  	test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);  	if (test->buffer) {  		mutex_lock(&mmc_test_lock); -		mmc_test_run(test); +		mmc_test_run(test, testcase);  		mutex_unlock(&mmc_test_lock);  	} @@ -852,6 +1032,9 @@ static int mmc_test_probe(struct mmc_card *card)  {  	int ret; +	if ((card->type != MMC_TYPE_MMC) && (card->type != MMC_TYPE_SD)) +		return -ENODEV; +  	mutex_init(&mmc_test_lock);  	ret = device_create_file(&card->dev, &dev_attr_test); diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index eeea84c309e..78ad48718ab 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -885,12 +885,14 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t  	sdio_uart_release_func(port);  } -static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state) +static int sdio_uart_break_ctl(struct tty_struct *tty, int break_state)  {  	struct sdio_uart_port *port = tty->driver_data; +	int result; -	if (sdio_uart_claim_func(port) != 0) -		return; +	result = sdio_uart_claim_func(port); +	if (result != 0) +		return result;  	if (break_state == -1)  		port->lcr |= UART_LCR_SBC; @@ -899,6 +901,7 @@ static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state)  	sdio_out(port, UART_LCR, port->lcr);  	sdio_uart_release_func(port); +	return 0;  }  static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 01ced4c5a61..3ee5b8c3b5c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -3,7 +3,7 @@   *   *  Copyright (C) 2003-2004 Russell King, All Rights Reserved.   *  SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. - *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + *  Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.   *  MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.   *   * This program is free software; you can redistribute it and/or modify @@ -295,6 +295,33 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)  EXPORT_SYMBOL(mmc_set_data_timeout);  /** + *	mmc_align_data_size - pads a transfer size to a more optimal value + *	@card: the MMC card associated with the data transfer + *	@sz: original transfer size + * + *	Pads the original data size with a number of extra bytes in + *	order to avoid controller bugs and/or performance hits + *	(e.g. some controllers revert to PIO for certain sizes). + * + *	Returns the improved size, which might be unmodified. + * + *	Note that this function is only relevant when issuing a + *	single scatter gather entry. + */ +unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) +{ +	/* +	 * FIXME: We don't have a system for the controller to tell +	 * the core about its problems yet, so for now we just 32-bit +	 * align the size. +	 */ +	sz = ((sz + 3) / 4) * 4; + +	return sz; +} +EXPORT_SYMBOL(mmc_align_data_size); + +/**   *	__mmc_claim_host - exclusively claim a host   *	@host: mmc host to claim   *	@abort: whether or not the operation should be aborted @@ -638,6 +665,9 @@ void mmc_rescan(struct work_struct *work)  		 */  		mmc_bus_put(host); +		if (host->ops->get_cd && host->ops->get_cd(host) == 0) +			goto out; +  		mmc_claim_host(host);  		mmc_power_up(host); @@ -652,7 +682,7 @@ void mmc_rescan(struct work_struct *work)  		if (!err) {  			if (mmc_attach_sdio(host, ocr))  				mmc_power_off(host); -			return; +			goto out;  		}  		/* @@ -662,7 +692,7 @@ void mmc_rescan(struct work_struct *work)  		if (!err) {  			if (mmc_attach_sd(host, ocr))  				mmc_power_off(host); -			return; +			goto out;  		}  		/* @@ -672,7 +702,7 @@ void mmc_rescan(struct work_struct *work)  		if (!err) {  			if (mmc_attach_mmc(host, ocr))  				mmc_power_off(host); -			return; +			goto out;  		}  		mmc_release_host(host); @@ -683,6 +713,9 @@ void mmc_rescan(struct work_struct *work)  		mmc_bus_put(host);  	} +out: +	if (host->caps & MMC_CAP_NEEDS_POLL) +		mmc_schedule_delayed_work(&host->detect, HZ);  }  void mmc_start_host(struct mmc_host *host) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 3da29eef8f7..fdd7c760be8 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -288,7 +288,7 @@ static struct device_type mmc_type = {  /*   * Handle the detection and initialisation of a card.   * - * In the case of a resume, "curcard" will contain the card + * In the case of a resume, "oldcard" will contain the card   * we're trying to reinitialise.   */  static int mmc_init_card(struct mmc_host *host, u32 ocr, diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 7ef3b15c5e3..26fc098d77c 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -326,7 +326,7 @@ static struct device_type sd_type = {  /*   * Handle the detection and initialisation of a card.   * - * In the case of a resume, "curcard" will contain the card + * In the case of a resume, "oldcard" will contain the card   * we're trying to reinitialise.   */  static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, @@ -494,13 +494,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,  	 * Check if read-only switch is active.  	 */  	if (!oldcard) { -		if (!host->ops->get_ro) { +		if (!host->ops->get_ro || host->ops->get_ro(host) < 0) {  			printk(KERN_WARNING "%s: host does not "  				"support reading read-only "  				"switch. assuming write-enable.\n",  				mmc_hostname(host));  		} else { -			if (host->ops->get_ro(host)) +			if (host->ops->get_ro(host) > 0)  				mmc_card_set_readonly(card);  		}  	} diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index d5e51b1c7b3..956bd767750 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -129,6 +129,12 @@ static int cistpl_funce_func(struct sdio_func *func,  	/* TPLFE_MAX_BLK_SIZE */  	func->max_blksize = buf[12] | (buf[13] << 8); +	/* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */ +	if (vsn > SDIO_SDIO_REV_1_00) +		func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10; +	else +		func->enable_timeout = jiffies_to_msecs(HZ); +  	return 0;  } diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 625b92ce9ce..f61fc2d4cd0 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -1,7 +1,7 @@  /*   *  linux/drivers/mmc/core/sdio_io.c   * - *  Copyright 2007 Pierre Ossman + *  Copyright 2007-2008 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 @@ -76,11 +76,7 @@ int sdio_enable_func(struct sdio_func *func)  	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; +	timeout = jiffies + msecs_to_jiffies(func->enable_timeout);  	while (1) {  		ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®); @@ -167,10 +163,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)  		return -EINVAL;  	if (blksz == 0) { -		blksz = min(min( -			func->max_blksize, -			func->card->host->max_blk_size), -			512u); +		blksz = min(func->max_blksize, func->card->host->max_blk_size); +		blksz = min(blksz, 512u);  	}  	ret = mmc_io_rw_direct(func->card, 1, 0, @@ -186,9 +180,116 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)  	func->cur_blksize = blksz;  	return 0;  } -  EXPORT_SYMBOL_GPL(sdio_set_block_size); +/* + * 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_align_size - pads a transfer size to a more optimal value + *	@func: SDIO function + *	@sz: original transfer size + * + *	Pads the original data size with a number of extra bytes in + *	order to avoid controller bugs and/or performance hits + *	(e.g. some controllers revert to PIO for certain sizes). + * + *	If possible, it will also adjust the size so that it can be + *	handled in just a single request. + * + *	Returns the improved size, which might be unmodified. + */ +unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz) +{ +	unsigned int orig_sz; +	unsigned int blk_sz, byte_sz; +	unsigned chunk_sz; + +	orig_sz = sz; + +	/* +	 * Do a first check with the controller, in case it +	 * wants to increase the size up to a point where it +	 * might need more than one block. +	 */ +	sz = mmc_align_data_size(func->card, sz); + +	/* +	 * If we can still do this with just a byte transfer, then +	 * we're done. +	 */ +	if (sz <= sdio_max_byte_size(func)) +		return sz; + +	if (func->card->cccr.multi_block) { +		/* +		 * Check if the transfer is already block aligned +		 */ +		if ((sz % func->cur_blksize) == 0) +			return sz; + +		/* +		 * Realign it so that it can be done with one request, +		 * and recheck if the controller still likes it. +		 */ +		blk_sz = ((sz + func->cur_blksize - 1) / +			func->cur_blksize) * func->cur_blksize; +		blk_sz = mmc_align_data_size(func->card, blk_sz); + +		/* +		 * This value is only good if it is still just +		 * one request. +		 */ +		if ((blk_sz % func->cur_blksize) == 0) +			return blk_sz; + +		/* +		 * We failed to do one request, but at least try to +		 * pad the remainder properly. +		 */ +		byte_sz = mmc_align_data_size(func->card, +				sz % func->cur_blksize); +		if (byte_sz <= sdio_max_byte_size(func)) { +			blk_sz = sz / func->cur_blksize; +			return blk_sz * func->cur_blksize + byte_sz; +		} +	} else { +		/* +		 * We need multiple requests, so first check that the +		 * controller can handle the chunk size; +		 */ +		chunk_sz = mmc_align_data_size(func->card, +				sdio_max_byte_size(func)); +		if (chunk_sz == sdio_max_byte_size(func)) { +			/* +			 * Fix up the size of the remainder (if any) +			 */ +			byte_sz = orig_sz % chunk_sz; +			if (byte_sz) { +				byte_sz = mmc_align_data_size(func->card, +						byte_sz); +			} + +			return (orig_sz / chunk_sz) * chunk_sz + byte_sz; +		} +	} + +	/* +	 * The controller is simply incapable of transferring the size +	 * we want in decent manner, so just return the original size. +	 */ +	return orig_sz; +} +EXPORT_SYMBOL_GPL(sdio_align_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, @@ -199,14 +300,13 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,  	int ret;  	/* Do the bulk of the transfer using block mode (if supported). */ -	if (func->card->cccr.multi_block) { +	if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {  		/* 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); +		max_blocks = min(func->card->host->max_blk_count, +			func->card->host->max_seg_size / func->cur_blksize); +		max_blocks = min(max_blocks, 511u);  		while (remainder > func->cur_blksize) {  			unsigned blocks; @@ -231,11 +331,7 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,  	/* 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 */ +		size = min(remainder, sdio_max_byte_size(func));  		ret = mmc_io_rw_extended(func->card, write, func->num, addr,  			 incr_addr, buf, 1, size); @@ -260,11 +356,10 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,   *	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) +u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)  {  	int ret; -	unsigned char val; +	u8 val;  	BUG_ON(!func); @@ -293,8 +388,7 @@ EXPORT_SYMBOL_GPL(sdio_readb);   *	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) +void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret)  {  	int ret; @@ -355,7 +449,6 @@ int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,  {  	return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);  } -  EXPORT_SYMBOL_GPL(sdio_readsb);  /** @@ -385,8 +478,7 @@ EXPORT_SYMBOL_GPL(sdio_writesb);   *	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) +u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret)  {  	int ret; @@ -400,7 +492,7 @@ unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,  		return 0xFFFF;  	} -	return le16_to_cpu(*(u16*)func->tmpbuf); +	return le16_to_cpup((__le16 *)func->tmpbuf);  }  EXPORT_SYMBOL_GPL(sdio_readw); @@ -415,12 +507,11 @@ EXPORT_SYMBOL_GPL(sdio_readw);   *	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) +void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret)  {  	int ret; -	*(u16*)func->tmpbuf = cpu_to_le16(b); +	*(__le16 *)func->tmpbuf = cpu_to_le16(b);  	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);  	if (err_ret) @@ -439,8 +530,7 @@ EXPORT_SYMBOL_GPL(sdio_writew);   *	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) +u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret)  {  	int ret; @@ -454,7 +544,7 @@ unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,  		return 0xFFFFFFFF;  	} -	return le32_to_cpu(*(u32*)func->tmpbuf); +	return le32_to_cpup((__le32 *)func->tmpbuf);  }  EXPORT_SYMBOL_GPL(sdio_readl); @@ -469,12 +559,11 @@ EXPORT_SYMBOL_GPL(sdio_readl);   *	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) +void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret)  {  	int ret; -	*(u32*)func->tmpbuf = cpu_to_le32(b); +	*(__le32 *)func->tmpbuf = cpu_to_le32(b);  	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);  	if (err_ret) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index dead61754ad..dc6f2579f85 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -26,18 +26,31 @@ config MMC_PXA  config MMC_SDHCI  	tristate "Secure Digital Host Controller Interface support" -	depends on PCI +	depends on HAS_DMA  	help -	  This select the generic Secure Digital Host Controller Interface. +	  This selects the generic Secure Digital Host Controller Interface.  	  It is used by manufacturers such as Texas Instruments(R), Ricoh(R)  	  and Toshiba(R). Most controllers found in laptops are of this type. + +	  If you have a controller with this interface, say Y or M here. You +	  also need to enable an appropriate bus interface. + +	  If unsure, say N. + +config MMC_SDHCI_PCI +	tristate "SDHCI support on PCI bus" +	depends on MMC_SDHCI && PCI +	help +	  This selects the PCI Secure Digital Host Controller Interface. +	  Most controllers found today are PCI devices. +  	  If you have a controller with this interface, say Y or M here.  	  If unsure, say N.  config MMC_RICOH_MMC  	tristate "Ricoh MMC Controller Disabler  (EXPERIMENTAL)" -	depends on PCI && EXPERIMENTAL && MMC_SDHCI +	depends on MMC_SDHCI_PCI  	help  	  This selects the disabler for the Ricoh MMC Controller. This  	  proprietary controller is unnecessary because the SDHCI driver @@ -91,6 +104,16 @@ config MMC_AT91  	  If unsure, say N. +config MMC_ATMELMCI +	tristate "Atmel Multimedia Card Interface support" +	depends on AVR32 +	help +	  This selects the Atmel Multimedia Card Interface driver. If +	  you have an AT32 (AVR32) platform with a Multimedia Card +	  slot, say Y or M here. + +	  If unsure, say N. +  config MMC_IMX  	tristate "Motorola i.MX Multimedia Card Interface support"  	depends on ARCH_IMX @@ -130,3 +153,24 @@ config MMC_SPI  	  If unsure, or if your system has no SPI master driver, say N. +config MMC_S3C +	tristate "Samsung S3C SD/MMC Card Interface support" +	depends on ARCH_S3C2410 && MMC +	help +	  This selects a driver for the MCI interface found in +          Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs. +	  If you have a board based on one of those and a MMC/SD +	  slot, say Y or M here. + +	  If unsure, say N. + +config MMC_SDRICOH_CS +	tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)" +	depends on EXPERIMENTAL && MMC && PCI && PCMCIA +	help +	  Say Y here if your Notebook reports a Ricoh Bay1Controller PCMCIA +	  card whenever you insert a MMC or SD card into the card slot. + +	  To compile this driver as a module, choose M here: the +	  module will be called sdricoh_cs. + diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 3877c87e6da..db52eebfb50 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -10,11 +10,15 @@ obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o  obj-$(CONFIG_MMC_PXA)		+= pxamci.o  obj-$(CONFIG_MMC_IMX)		+= imxmmc.o  obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o +obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o  obj-$(CONFIG_MMC_RICOH_MMC)	+= ricoh_mmc.o  obj-$(CONFIG_MMC_WBSD)		+= wbsd.o  obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o  obj-$(CONFIG_MMC_OMAP)		+= omap.o  obj-$(CONFIG_MMC_AT91)		+= at91_mci.o +obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o  obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o  obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o +obj-$(CONFIG_MMC_S3C)   	+= s3cmci.o +obj-$(CONFIG_MMC_SDRICOH_CS)	+= sdricoh_cs.o diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 8979ad330a4..f15e2064305 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -125,9 +125,72 @@ struct at91mci_host  	/* Latest in the scatterlist that has been enabled for transfer */  	int transfer_index; + +	/* Timer for timeouts */ +	struct timer_list timer;  };  /* + * Reset the controller and restore most of the state + */ +static void at91_reset_host(struct at91mci_host *host) +{ +	unsigned long flags; +	u32 mr; +	u32 sdcr; +	u32 dtor; +	u32 imr; + +	local_irq_save(flags); +	imr = at91_mci_read(host, AT91_MCI_IMR); + +	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + +	/* save current state */ +	mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; +	sdcr = at91_mci_read(host, AT91_MCI_SDCR); +	dtor = at91_mci_read(host, AT91_MCI_DTOR); + +	/* reset the controller */ +	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); + +	/* restore state */ +	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); +	at91_mci_write(host, AT91_MCI_MR, mr); +	at91_mci_write(host, AT91_MCI_SDCR, sdcr); +	at91_mci_write(host, AT91_MCI_DTOR, dtor); +	at91_mci_write(host, AT91_MCI_IER, imr); + +	/* make sure sdio interrupts will fire */ +	at91_mci_read(host, AT91_MCI_SR); + +	local_irq_restore(flags); +} + +static void at91_timeout_timer(unsigned long data) +{ +	struct at91mci_host *host; + +	host = (struct at91mci_host *)data; + +	if (host->request) { +		dev_err(host->mmc->parent, "Timeout waiting end of packet\n"); + +		if (host->cmd && host->cmd->data) { +			host->cmd->data->error = -ETIMEDOUT; +		} else { +			if (host->cmd) +				host->cmd->error = -ETIMEDOUT; +			else +				host->request->cmd->error = -ETIMEDOUT; +		} + +		at91_reset_host(host); +		mmc_request_done(host->mmc, host->request); +	} +} + +/*   * Copy from sg to a dma block - used for transfers   */  static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) @@ -135,9 +198,14 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data  	unsigned int len, i, size;  	unsigned *dmabuf = host->buffer; -	size = host->total_length; +	size = data->blksz * data->blocks;  	len = data->sg_len; +	/* AT91SAM926[0/3] Data Write Operation and number of bytes erratum */ +	if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) +		if (host->total_length == 12) +			memset(dmabuf, 0, 12); +  	/*  	 * Just loop through all entries. Size might not  	 * be the entire list though so make sure that @@ -159,9 +227,10 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data  			for (index = 0; index < (amount / 4); index++)  				*dmabuf++ = swab32(sgbuffer[index]); -		} -		else +		} else {  			memcpy(dmabuf, sgbuffer, amount); +			dmabuf += amount; +		}  		kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); @@ -233,11 +302,11 @@ static void at91_mci_pre_dma_read(struct at91mci_host *host)  		if (i == 0) {  			at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); -			at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4); +			at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);  		}  		else {  			at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); -			at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4); +			at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);  		}  	} @@ -277,8 +346,6 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)  		dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); -		data->bytes_xfered += sg->length; -  		if (cpu_is_at91rm9200()) {	/* AT91RM9200 errata */  			unsigned int *buffer;  			int index; @@ -294,6 +361,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)  		}  		flush_dcache_page(sg_page(sg)); + +		data->bytes_xfered += sg->length;  	}  	/* Is there another transfer to trigger? */ @@ -334,10 +403,32 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)  		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);  	} else  		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); +} + +/* + * Update bytes tranfered count during a write operation + */ +static void at91_mci_update_bytes_xfered(struct at91mci_host *host) +{ +	struct mmc_data *data; + +	/* always deal with the effective request (and not the current cmd) */ + +	if (host->request->cmd && host->request->cmd->error != 0) +		return; -	data->bytes_xfered = host->total_length; +	if (host->request->data) { +		data = host->request->data; +		if (data->flags & MMC_DATA_WRITE) { +			/* card is in IDLE mode now */ +			pr_debug("-> bytes_xfered %d, total_length = %d\n", +				data->bytes_xfered, host->total_length); +			data->bytes_xfered = data->blksz * data->blocks; +		} +	}  } +  /*Handle after command sent ready*/  static int at91_mci_handle_cmdrdy(struct at91mci_host *host)  { @@ -350,8 +441,7 @@ static int at91_mci_handle_cmdrdy(struct at91mci_host *host)  		} else return 1;  	} else if (host->cmd->data->flags & MMC_DATA_WRITE) {  		/*After sendding multi-block-write command, start DMA transfer*/ -		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE); -		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); +		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE);  		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);  	} @@ -430,11 +520,19 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command  	if (data) { -		if ( data->blksz & 0x3 ) { -			pr_debug("Unsupported block size\n"); -			cmd->error = -EINVAL; -			mmc_request_done(host->mmc, host->request); -			return; +		if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) { +			if (data->blksz & 0x3) { +				pr_debug("Unsupported block size\n"); +				cmd->error = -EINVAL; +				mmc_request_done(host->mmc, host->request); +				return; +			} +			if (data->flags & MMC_DATA_STREAM) { +				pr_debug("Stream commands not supported\n"); +				cmd->error = -EINVAL; +				mmc_request_done(host->mmc, host->request); +				return; +			}  		}  		block_length = data->blksz; @@ -481,8 +579,16 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command  		ier = AT91_MCI_CMDRDY;  	} else {  		/* zero block length and PDC mode */ -		mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; -		at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); +		mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff; +		mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0; +		mr |= (block_length << 16); +		mr |= AT91_MCI_PDCMODE; +		at91_mci_write(host, AT91_MCI_MR, mr); + +		if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261())) +			at91_mci_write(host, AT91_MCI_BLKR, +				AT91_MCI_BLKR_BCNT(blocks) | +				AT91_MCI_BLKR_BLKLEN(block_length));  		/*  		 * Disable the PDC controller @@ -508,6 +614,13 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command  				 * Handle a write  				 */  				host->total_length = block_length * blocks; +				/* +				 * AT91SAM926[0/3] Data Write Operation and +				 * number of bytes erratum +				 */ +				if (cpu_is_at91sam9260 () || cpu_is_at91sam9263()) +					if (host->total_length < 12) +						host->total_length = 12;  				host->buffer = dma_alloc_coherent(NULL,  						host->total_length,  						&host->physical_address, GFP_KERNEL); @@ -517,7 +630,9 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command  				pr_debug("Transmitting %d bytes\n", host->total_length);  				at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); -				at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); +				at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ? +						host->total_length : host->total_length / 4); +  				ier = AT91_MCI_CMDRDY;  			}  		} @@ -552,20 +667,26 @@ static void at91_mci_process_next(struct at91mci_host *host)  	else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {  		host->flags |= FL_SENT_STOP;  		at91_mci_send_command(host, host->request->stop); -	} -	else +	} else { +		del_timer(&host->timer); +		/* the at91rm9200 mci controller hangs after some transfers, +		 * and the workaround is to reset it after each transfer. +		 */ +		if (cpu_is_at91rm9200()) +			at91_reset_host(host);  		mmc_request_done(host->mmc, host->request); +	}  }  /*   * Handle a command that has been completed   */ -static void at91_mci_completed_command(struct at91mci_host *host) +static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status)  {  	struct mmc_command *cmd = host->cmd; -	unsigned int status; +	struct mmc_data *data = cmd->data; -	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); +	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));  	cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));  	cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); @@ -577,25 +698,34 @@ static void at91_mci_completed_command(struct at91mci_host *host)  		host->buffer = NULL;  	} -	status = at91_mci_read(host, AT91_MCI_SR); - -	pr_debug("Status = %08X [%08X %08X %08X %08X]\n", -		 status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); +	pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n", +		 status, at91_mci_read(host, AT91_MCI_SR), +		 cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);  	if (status & AT91_MCI_ERRORS) {  		if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {  			cmd->error = 0;  		}  		else { -			if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) -				cmd->error = -ETIMEDOUT; -			else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) -				cmd->error = -EILSEQ; -			else -				cmd->error = -EIO; +			if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) { +				if (data) { +					if (status & AT91_MCI_DTOE) +						data->error = -ETIMEDOUT; +					else if (status & AT91_MCI_DCRCE) +						data->error = -EILSEQ; +				} +			} else { +				if (status & AT91_MCI_RTOE) +					cmd->error = -ETIMEDOUT; +				else if (status & AT91_MCI_RCRCE) +					cmd->error = -EILSEQ; +				else +					cmd->error = -EIO; +			} -			pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", -				 cmd->error, cmd->opcode, cmd->retries); +			pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n", +				cmd->error, data ? data->error : 0, +				 cmd->opcode, cmd->retries);  		}  	}  	else @@ -613,6 +743,8 @@ static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)  	host->request = mrq;  	host->flags = 0; +	mod_timer(&host->timer, jiffies +  HZ); +  	at91_mci_process_next(host);  } @@ -736,6 +868,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)  		if (int_status & AT91_MCI_NOTBUSY) {  			pr_debug("Card is ready\n"); +			at91_mci_update_bytes_xfered(host);  			completed = 1;  		} @@ -744,9 +877,21 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)  		if (int_status & AT91_MCI_BLKE) {  			pr_debug("Block transfer has ended\n"); -			completed = 1; +			if (host->request->data && host->request->data->blocks > 1) { +				/* multi block write : complete multi write +				 * command and send stop */ +				completed = 1; +			} else { +				at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); +			}  		} +		if (int_status & AT91_MCI_SDIOIRQA) +			mmc_signal_sdio_irq(host->mmc); + +		if (int_status & AT91_MCI_SDIOIRQB) +			mmc_signal_sdio_irq(host->mmc); +  		if (int_status & AT91_MCI_TXRDY)  			pr_debug("Ready to transmit\n"); @@ -761,10 +906,10 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)  	if (completed) {  		pr_debug("Completed command\n"); -		at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); -		at91_mci_completed_command(host); +		at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); +		at91_mci_completed_command(host, int_status);  	} else -		at91_mci_write(host, AT91_MCI_IDR, int_status); +		at91_mci_write(host, AT91_MCI_IDR, int_status & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));  	return IRQ_HANDLED;  } @@ -793,25 +938,33 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host)  static int at91_mci_get_ro(struct mmc_host *mmc)  { -	int read_only = 0;  	struct at91mci_host *host = mmc_priv(mmc); -	if (host->board->wp_pin) { -		read_only = gpio_get_value(host->board->wp_pin); -		printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc), -				(read_only ? "read-only" : "read-write") ); -	} -	else { -		printk(KERN_WARNING "%s: host does not support reading read-only " -				"switch.  Assuming write-enable.\n", mmc_hostname(mmc)); -	} -	return read_only; +	if (host->board->wp_pin) +		return !!gpio_get_value(host->board->wp_pin); +	/* +	 * Board doesn't support read only detection; let the mmc core +	 * decide what to do. +	 */ +	return -ENOSYS; +} + +static void at91_mci_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ +	struct at91mci_host *host = mmc_priv(mmc); + +	pr_debug("%s: sdio_irq %c : %s\n", mmc_hostname(host->mmc), +		host->board->slot_b ? 'B':'A', enable ? "enable" : "disable"); +	at91_mci_write(host, enable ? AT91_MCI_IER : AT91_MCI_IDR, +		host->board->slot_b ? AT91_MCI_SDIOIRQB : AT91_MCI_SDIOIRQA); +  }  static const struct mmc_host_ops at91_mci_ops = {  	.request	= at91_mci_request,  	.set_ios	= at91_mci_set_ios,  	.get_ro		= at91_mci_get_ro, +	.enable_sdio_irq = at91_mci_enable_sdio_irq,  };  /* @@ -842,6 +995,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)  	mmc->f_min = 375000;  	mmc->f_max = 25000000;  	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; +	mmc->caps = MMC_CAP_SDIO_IRQ;  	mmc->max_blk_size = 4095;  	mmc->max_blk_count = mmc->max_req_size; @@ -935,6 +1089,8 @@ static int __init at91_mci_probe(struct platform_device *pdev)  	mmc_add_host(mmc); +	setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host); +  	/*  	 * monitor card insertion/removal if we can  	 */ @@ -995,6 +1151,7 @@ static int __exit at91_mci_remove(struct platform_device *pdev)  	}  	at91_mci_disable(host); +	del_timer_sync(&host->timer);  	mmc_remove_host(mmc);  	free_irq(host->irq, host); diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h new file mode 100644 index 00000000000..a9a5657706c --- /dev/null +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -0,0 +1,91 @@ +/* + * Atmel MultiMedia Card Interface driver + * + * Copyright (C) 2004-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __DRIVERS_MMC_ATMEL_MCI_H__ +#define __DRIVERS_MMC_ATMEL_MCI_H__ + +/* MCI Register Definitions */ +#define MCI_CR			0x0000	/* Control */ +# define MCI_CR_MCIEN		(  1 <<  0)	/* MCI Enable */ +# define MCI_CR_MCIDIS		(  1 <<  1)	/* MCI Disable */ +# define MCI_CR_SWRST		(  1 <<  7)	/* Software Reset */ +#define MCI_MR			0x0004	/* Mode */ +# define MCI_MR_CLKDIV(x)	((x) <<  0)	/* Clock Divider */ +# define MCI_MR_RDPROOF		(  1 << 11)	/* Read Proof */ +# define MCI_MR_WRPROOF		(  1 << 12)	/* Write Proof */ +#define MCI_DTOR		0x0008	/* Data Timeout */ +# define MCI_DTOCYC(x)		((x) <<  0)	/* Data Timeout Cycles */ +# define MCI_DTOMUL(x)		((x) <<  4)	/* Data Timeout Multiplier */ +#define MCI_SDCR		0x000c	/* SD Card / SDIO */ +# define MCI_SDCSEL_SLOT_A	(  0 <<  0)	/* Select SD slot A */ +# define MCI_SDCSEL_SLOT_B	(  1 <<  0)	/* Select SD slot A */ +# define MCI_SDCBUS_1BIT	(  0 <<  7)	/* 1-bit data bus */ +# define MCI_SDCBUS_4BIT	(  1 <<  7)	/* 4-bit data bus */ +#define MCI_ARGR		0x0010	/* Command Argument */ +#define MCI_CMDR		0x0014	/* Command */ +# define MCI_CMDR_CMDNB(x)	((x) <<  0)	/* Command Opcode */ +# define MCI_CMDR_RSPTYP_NONE	(  0 <<  6)	/* No response */ +# define MCI_CMDR_RSPTYP_48BIT	(  1 <<  6)	/* 48-bit response */ +# define MCI_CMDR_RSPTYP_136BIT	(  2 <<  6)	/* 136-bit response */ +# define MCI_CMDR_SPCMD_INIT	(  1 <<  8)	/* Initialization command */ +# define MCI_CMDR_SPCMD_SYNC	(  2 <<  8)	/* Synchronized command */ +# define MCI_CMDR_SPCMD_INT	(  4 <<  8)	/* Interrupt command */ +# define MCI_CMDR_SPCMD_INTRESP	(  5 <<  8)	/* Interrupt response */ +# define MCI_CMDR_OPDCMD	(  1 << 11)	/* Open Drain */ +# define MCI_CMDR_MAXLAT_5CYC	(  0 << 12)	/* Max latency 5 cycles */ +# define MCI_CMDR_MAXLAT_64CYC	(  1 << 12)	/* Max latency 64 cycles */ +# define MCI_CMDR_START_XFER	(  1 << 16)	/* Start data transfer */ +# define MCI_CMDR_STOP_XFER	(  2 << 16)	/* Stop data transfer */ +# define MCI_CMDR_TRDIR_WRITE	(  0 << 18)	/* Write data */ +# define MCI_CMDR_TRDIR_READ	(  1 << 18)	/* Read data */ +# define MCI_CMDR_BLOCK		(  0 << 19)	/* Single-block transfer */ +# define MCI_CMDR_MULTI_BLOCK	(  1 << 19)	/* Multi-block transfer */ +# define MCI_CMDR_STREAM	(  2 << 19)	/* MMC Stream transfer */ +# define MCI_CMDR_SDIO_BYTE	(  4 << 19)	/* SDIO Byte transfer */ +# define MCI_CMDR_SDIO_BLOCK	(  5 << 19)	/* SDIO Block transfer */ +# define MCI_CMDR_SDIO_SUSPEND	(  1 << 24)	/* SDIO Suspend Command */ +# define MCI_CMDR_SDIO_RESUME	(  2 << 24)	/* SDIO Resume Command */ +#define MCI_BLKR		0x0018	/* Block */ +# define MCI_BCNT(x)		((x) <<  0)	/* Data Block Count */ +# define MCI_BLKLEN(x)		((x) << 16)	/* Data Block Length */ +#define MCI_RSPR		0x0020	/* Response 0 */ +#define MCI_RSPR1		0x0024	/* Response 1 */ +#define MCI_RSPR2		0x0028	/* Response 2 */ +#define MCI_RSPR3		0x002c	/* Response 3 */ +#define MCI_RDR			0x0030	/* Receive Data */ +#define MCI_TDR			0x0034	/* Transmit Data */ +#define MCI_SR			0x0040	/* Status */ +#define MCI_IER			0x0044	/* Interrupt Enable */ +#define MCI_IDR			0x0048	/* Interrupt Disable */ +#define MCI_IMR			0x004c	/* Interrupt Mask */ +# define MCI_CMDRDY		(  1 <<   0)	/* Command Ready */ +# define MCI_RXRDY		(  1 <<   1)	/* Receiver Ready */ +# define MCI_TXRDY		(  1 <<   2)	/* Transmitter Ready */ +# define MCI_BLKE		(  1 <<   3)	/* Data Block Ended */ +# define MCI_DTIP		(  1 <<   4)	/* Data Transfer In Progress */ +# define MCI_NOTBUSY		(  1 <<   5)	/* Data Not Busy */ +# define MCI_SDIOIRQA		(  1 <<   8)	/* SDIO IRQ in slot A */ +# define MCI_SDIOIRQB		(  1 <<   9)	/* SDIO IRQ in slot B */ +# define MCI_RINDE		(  1 <<  16)	/* Response Index Error */ +# define MCI_RDIRE		(  1 <<  17)	/* Response Direction Error */ +# define MCI_RCRCE		(  1 <<  18)	/* Response CRC Error */ +# define MCI_RENDE		(  1 <<  19)	/* Response End Bit Error */ +# define MCI_RTOE		(  1 <<  20)	/* Response Time-Out Error */ +# define MCI_DCRCE		(  1 <<  21)	/* Data CRC Error */ +# define MCI_DTOE		(  1 <<  22)	/* Data Time-Out Error */ +# define MCI_OVRE		(  1 <<  30)	/* RX Overrun Error */ +# define MCI_UNRE		(  1 <<  31)	/* TX Underrun Error */ + +/* Register access macros */ +#define mci_readl(port,reg)				\ +	__raw_readl((port)->regs + MCI_##reg) +#define mci_writel(port,reg,value)			\ +	__raw_writel((value), (port)->regs + MCI_##reg) + +#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */ diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c new file mode 100644 index 00000000000..cce873c5a14 --- /dev/null +++ b/drivers/mmc/host/atmel-mci.c @@ -0,0 +1,981 @@ +/* + * Atmel MultiMedia Card Interface driver + * + * Copyright (C) 2004-2008 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/blkdev.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> + +#include <linux/mmc/host.h> + +#include <asm/atmel-mci.h> +#include <asm/io.h> +#include <asm/unaligned.h> + +#include <asm/arch/board.h> +#include <asm/arch/gpio.h> + +#include "atmel-mci-regs.h" + +#define ATMCI_DATA_ERROR_FLAGS	(MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE) + +enum { +	EVENT_CMD_COMPLETE = 0, +	EVENT_DATA_ERROR, +	EVENT_DATA_COMPLETE, +	EVENT_STOP_SENT, +	EVENT_STOP_COMPLETE, +	EVENT_XFER_COMPLETE, +}; + +struct atmel_mci { +	struct mmc_host		*mmc; +	void __iomem		*regs; + +	struct scatterlist	*sg; +	unsigned int		pio_offset; + +	struct mmc_request	*mrq; +	struct mmc_command	*cmd; +	struct mmc_data		*data; + +	u32			cmd_status; +	u32			data_status; +	u32			stop_status; +	u32			stop_cmdr; + +	u32			mode_reg; +	u32			sdc_reg; + +	struct tasklet_struct	tasklet; +	unsigned long		pending_events; +	unsigned long		completed_events; + +	int			present; +	int			detect_pin; +	int			wp_pin; + +	/* For detect pin debouncing */ +	struct timer_list	detect_timer; + +	unsigned long		bus_hz; +	unsigned long		mapbase; +	struct clk		*mck; +	struct platform_device	*pdev; +}; + +#define atmci_is_completed(host, event)				\ +	test_bit(event, &host->completed_events) +#define atmci_test_and_clear_pending(host, event)		\ +	test_and_clear_bit(event, &host->pending_events) +#define atmci_test_and_set_completed(host, event)		\ +	test_and_set_bit(event, &host->completed_events) +#define atmci_set_completed(host, event)			\ +	set_bit(event, &host->completed_events) +#define atmci_set_pending(host, event)				\ +	set_bit(event, &host->pending_events) +#define atmci_clear_pending(host, event)			\ +	clear_bit(event, &host->pending_events) + + +static void atmci_enable(struct atmel_mci *host) +{ +	clk_enable(host->mck); +	mci_writel(host, CR, MCI_CR_MCIEN); +	mci_writel(host, MR, host->mode_reg); +	mci_writel(host, SDCR, host->sdc_reg); +} + +static void atmci_disable(struct atmel_mci *host) +{ +	mci_writel(host, CR, MCI_CR_SWRST); + +	/* Stall until write is complete, then disable the bus clock */ +	mci_readl(host, SR); +	clk_disable(host->mck); +} + +static inline unsigned int ns_to_clocks(struct atmel_mci *host, +					unsigned int ns) +{ +	return (ns * (host->bus_hz / 1000000) + 999) / 1000; +} + +static void atmci_set_timeout(struct atmel_mci *host, +			      struct mmc_data *data) +{ +	static unsigned	dtomul_to_shift[] = { +		0, 4, 7, 8, 10, 12, 16, 20 +	}; +	unsigned	timeout; +	unsigned	dtocyc; +	unsigned	dtomul; + +	timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks; + +	for (dtomul = 0; dtomul < 8; dtomul++) { +		unsigned shift = dtomul_to_shift[dtomul]; +		dtocyc = (timeout + (1 << shift) - 1) >> shift; +		if (dtocyc < 15) +			break; +	} + +	if (dtomul >= 8) { +		dtomul = 7; +		dtocyc = 15; +	} + +	dev_vdbg(&host->mmc->class_dev, "setting timeout to %u cycles\n", +			dtocyc << dtomul_to_shift[dtomul]); +	mci_writel(host, DTOR, (MCI_DTOMUL(dtomul) | MCI_DTOCYC(dtocyc))); +} + +/* + * Return mask with command flags to be enabled for this command. + */ +static u32 atmci_prepare_command(struct mmc_host *mmc, +				 struct mmc_command *cmd) +{ +	struct mmc_data	*data; +	u32		cmdr; + +	cmd->error = -EINPROGRESS; + +	cmdr = MCI_CMDR_CMDNB(cmd->opcode); + +	if (cmd->flags & MMC_RSP_PRESENT) { +		if (cmd->flags & MMC_RSP_136) +			cmdr |= MCI_CMDR_RSPTYP_136BIT; +		else +			cmdr |= MCI_CMDR_RSPTYP_48BIT; +	} + +	/* +	 * This should really be MAXLAT_5 for CMD2 and ACMD41, but +	 * it's too difficult to determine whether this is an ACMD or +	 * not. Better make it 64. +	 */ +	cmdr |= MCI_CMDR_MAXLAT_64CYC; + +	if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN) +		cmdr |= MCI_CMDR_OPDCMD; + +	data = cmd->data; +	if (data) { +		cmdr |= MCI_CMDR_START_XFER; +		if (data->flags & MMC_DATA_STREAM) +			cmdr |= MCI_CMDR_STREAM; +		else if (data->blocks > 1) +			cmdr |= MCI_CMDR_MULTI_BLOCK; +		else +			cmdr |= MCI_CMDR_BLOCK; + +		if (data->flags & MMC_DATA_READ) +			cmdr |= MCI_CMDR_TRDIR_READ; +	} + +	return cmdr; +} + +static void atmci_start_command(struct atmel_mci *host, +				struct mmc_command *cmd, +				u32 cmd_flags) +{ +	/* Must read host->cmd after testing event flags */ +	smp_rmb(); +	WARN_ON(host->cmd); +	host->cmd = cmd; + +	dev_vdbg(&host->mmc->class_dev, +			"start command: ARGR=0x%08x CMDR=0x%08x\n", +			cmd->arg, cmd_flags); + +	mci_writel(host, ARGR, cmd->arg); +	mci_writel(host, CMDR, cmd_flags); +} + +static void send_stop_cmd(struct mmc_host *mmc, struct mmc_data *data) +{ +	struct atmel_mci *host = mmc_priv(mmc); + +	atmci_start_command(host, data->stop, host->stop_cmdr); +	mci_writel(host, IER, MCI_CMDRDY); +} + +static void atmci_request_end(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct atmel_mci *host = mmc_priv(mmc); + +	WARN_ON(host->cmd || host->data); +	host->mrq = NULL; + +	atmci_disable(host); + +	mmc_request_done(mmc, mrq); +} + +/* + * Returns a mask of interrupt flags to be enabled after the whole + * request has been prepared. + */ +static u32 atmci_submit_data(struct mmc_host *mmc, struct mmc_data *data) +{ +	struct atmel_mci	*host = mmc_priv(mmc); +	u32			iflags; + +	data->error = -EINPROGRESS; + +	WARN_ON(host->data); +	host->sg = NULL; +	host->data = data; + +	mci_writel(host, BLKR, MCI_BCNT(data->blocks) +			| MCI_BLKLEN(data->blksz)); +	dev_vdbg(&mmc->class_dev, "BLKR=0x%08x\n", +			MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz)); + +	iflags = ATMCI_DATA_ERROR_FLAGS; +	host->sg = data->sg; +	host->pio_offset = 0; +	if (data->flags & MMC_DATA_READ) +		iflags |= MCI_RXRDY; +	else +		iflags |= MCI_TXRDY; + +	return iflags; +} + +static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct atmel_mci	*host = mmc_priv(mmc); +	struct mmc_data		*data; +	struct mmc_command	*cmd; +	u32			iflags; +	u32			cmdflags = 0; + +	iflags = mci_readl(host, IMR); +	if (iflags) +		dev_warn(&mmc->class_dev, "WARNING: IMR=0x%08x\n", +				mci_readl(host, IMR)); + +	WARN_ON(host->mrq != NULL); + +	/* +	 * We may "know" the card is gone even though there's still an +	 * electrical connection. If so, we really need to communicate +	 * this to the MMC core since there won't be any more +	 * interrupts as the card is completely removed. Otherwise, +	 * the MMC core might believe the card is still there even +	 * though the card was just removed very slowly. +	 */ +	if (!host->present) { +		mrq->cmd->error = -ENOMEDIUM; +		mmc_request_done(mmc, mrq); +		return; +	} + +	host->mrq = mrq; +	host->pending_events = 0; +	host->completed_events = 0; + +	atmci_enable(host); + +	/* We don't support multiple blocks of weird lengths. */ +	data = mrq->data; +	if (data) { +		if (data->blocks > 1 && data->blksz & 3) +			goto fail; +		atmci_set_timeout(host, data); +	} + +	iflags = MCI_CMDRDY; +	cmd = mrq->cmd; +	cmdflags = atmci_prepare_command(mmc, cmd); +	atmci_start_command(host, cmd, cmdflags); + +	if (data) +		iflags |= atmci_submit_data(mmc, data); + +	if (mrq->stop) { +		host->stop_cmdr = atmci_prepare_command(mmc, mrq->stop); +		host->stop_cmdr |= MCI_CMDR_STOP_XFER; +		if (!(data->flags & MMC_DATA_WRITE)) +			host->stop_cmdr |= MCI_CMDR_TRDIR_READ; +		if (data->flags & MMC_DATA_STREAM) +			host->stop_cmdr |= MCI_CMDR_STREAM; +		else +			host->stop_cmdr |= MCI_CMDR_MULTI_BLOCK; +	} + +	/* +	 * We could have enabled interrupts earlier, but I suspect +	 * that would open up a nice can of interesting race +	 * conditions (e.g. command and data complete, but stop not +	 * prepared yet.) +	 */ +	mci_writel(host, IER, iflags); + +	return; + +fail: +	atmci_disable(host); +	host->mrq = NULL; +	mrq->cmd->error = -EINVAL; +	mmc_request_done(mmc, mrq); +} + +static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct atmel_mci	*host = mmc_priv(mmc); + +	if (ios->clock) { +		u32 clkdiv; + +		/* Set clock rate */ +		clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * ios->clock) - 1; +		if (clkdiv > 255) { +			dev_warn(&mmc->class_dev, +				"clock %u too slow; using %lu\n", +				ios->clock, host->bus_hz / (2 * 256)); +			clkdiv = 255; +		} + +		host->mode_reg = MCI_MR_CLKDIV(clkdiv) | MCI_MR_WRPROOF +					| MCI_MR_RDPROOF; +	} + +	switch (ios->bus_width) { +	case MMC_BUS_WIDTH_1: +		host->sdc_reg = 0; +		break; +	case MMC_BUS_WIDTH_4: +		host->sdc_reg = MCI_SDCBUS_4BIT; +		break; +	} + +	switch (ios->power_mode) { +	case MMC_POWER_ON: +		/* Send init sequence (74 clock cycles) */ +		atmci_enable(host); +		mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT); +		while (!(mci_readl(host, SR) & MCI_CMDRDY)) +			cpu_relax(); +		atmci_disable(host); +		break; +	default: +		/* +		 * TODO: None of the currently available AVR32-based +		 * boards allow MMC power to be turned off. Implement +		 * power control when this can be tested properly. +		 */ +		break; +	} +} + +static int atmci_get_ro(struct mmc_host *mmc) +{ +	int			read_only = 0; +	struct atmel_mci	*host = mmc_priv(mmc); + +	if (host->wp_pin >= 0) { +		read_only = gpio_get_value(host->wp_pin); +		dev_dbg(&mmc->class_dev, "card is %s\n", +				read_only ? "read-only" : "read-write"); +	} else { +		dev_dbg(&mmc->class_dev, +			"no pin for checking read-only switch." +			" Assuming write-enable.\n"); +	} + +	return read_only; +} + +static struct mmc_host_ops atmci_ops = { +	.request	= atmci_request, +	.set_ios	= atmci_set_ios, +	.get_ro		= atmci_get_ro, +}; + +static void atmci_command_complete(struct atmel_mci *host, +			struct mmc_command *cmd, u32 status) +{ +	/* Read the response from the card (up to 16 bytes) */ +	cmd->resp[0] = mci_readl(host, RSPR); +	cmd->resp[1] = mci_readl(host, RSPR); +	cmd->resp[2] = mci_readl(host, RSPR); +	cmd->resp[3] = mci_readl(host, RSPR); + +	if (status & MCI_RTOE) +		cmd->error = -ETIMEDOUT; +	else if ((cmd->flags & MMC_RSP_CRC) && (status & MCI_RCRCE)) +		cmd->error = -EILSEQ; +	else if (status & (MCI_RINDE | MCI_RDIRE | MCI_RENDE)) +		cmd->error = -EIO; +	else +		cmd->error = 0; + +	if (cmd->error) { +		dev_dbg(&host->mmc->class_dev, +			"command error: status=0x%08x\n", status); + +		if (cmd->data) { +			host->data = NULL; +			mci_writel(host, IDR, MCI_NOTBUSY +					| MCI_TXRDY | MCI_RXRDY +					| ATMCI_DATA_ERROR_FLAGS); +		} +	} +} + +static void atmci_detect_change(unsigned long data) +{ +	struct atmel_mci *host = (struct atmel_mci *)data; +	struct mmc_request *mrq = host->mrq; +	int present; + +	/* +	 * atmci_remove() sets detect_pin to -1 before freeing the +	 * interrupt. We must not re-enable the interrupt if it has +	 * been freed. +	 */ +	smp_rmb(); +	if (host->detect_pin < 0) +		return; + +	enable_irq(gpio_to_irq(host->detect_pin)); +	present = !gpio_get_value(host->detect_pin); + +	dev_vdbg(&host->pdev->dev, "detect change: %d (was %d)\n", +			present, host->present); + +	if (present != host->present) { +		dev_dbg(&host->mmc->class_dev, "card %s\n", +			present ? "inserted" : "removed"); +		host->present = present; + +		/* Reset controller if card is gone */ +		if (!present) { +			mci_writel(host, CR, MCI_CR_SWRST); +			mci_writel(host, IDR, ~0UL); +			mci_writel(host, CR, MCI_CR_MCIEN); +		} + +		/* Clean up queue if present */ +		if (mrq) { +			/* +			 * Reset controller to terminate any ongoing +			 * commands or data transfers. +			 */ +			mci_writel(host, CR, MCI_CR_SWRST); + +			if (!atmci_is_completed(host, EVENT_CMD_COMPLETE)) +				mrq->cmd->error = -ENOMEDIUM; + +			if (mrq->data && !atmci_is_completed(host, +						EVENT_DATA_COMPLETE)) { +				host->data = NULL; +				mrq->data->error = -ENOMEDIUM; +			} +			if (mrq->stop && !atmci_is_completed(host, +						EVENT_STOP_COMPLETE)) +				mrq->stop->error = -ENOMEDIUM; + +			host->cmd = NULL; +			atmci_request_end(host->mmc, mrq); +		} + +		mmc_detect_change(host->mmc, 0); +	} +} + +static void atmci_tasklet_func(unsigned long priv) +{ +	struct mmc_host		*mmc = (struct mmc_host *)priv; +	struct atmel_mci	*host = mmc_priv(mmc); +	struct mmc_request	*mrq = host->mrq; +	struct mmc_data		*data = host->data; + +	dev_vdbg(&mmc->class_dev, +		"tasklet: pending/completed/mask %lx/%lx/%x\n", +		host->pending_events, host->completed_events, +		mci_readl(host, IMR)); + +	if (atmci_test_and_clear_pending(host, EVENT_CMD_COMPLETE)) { +		/* +		 * host->cmd must be set to NULL before the interrupt +		 * handler sees EVENT_CMD_COMPLETE +		 */ +		host->cmd = NULL; +		smp_wmb(); +		atmci_set_completed(host, EVENT_CMD_COMPLETE); +		atmci_command_complete(host, mrq->cmd, host->cmd_status); + +		if (!mrq->cmd->error && mrq->stop +				&& atmci_is_completed(host, EVENT_XFER_COMPLETE) +				&& !atmci_test_and_set_completed(host, +					EVENT_STOP_SENT)) +			send_stop_cmd(host->mmc, mrq->data); +	} +	if (atmci_test_and_clear_pending(host, EVENT_STOP_COMPLETE)) { +		/* +		 * host->cmd must be set to NULL before the interrupt +		 * handler sees EVENT_STOP_COMPLETE +		 */ +		host->cmd = NULL; +		smp_wmb(); +		atmci_set_completed(host, EVENT_STOP_COMPLETE); +		atmci_command_complete(host, mrq->stop, host->stop_status); +	} +	if (atmci_test_and_clear_pending(host, EVENT_DATA_ERROR)) { +		u32 status = host->data_status; + +		dev_vdbg(&mmc->class_dev, "data error: status=%08x\n", status); + +		atmci_set_completed(host, EVENT_DATA_ERROR); +		atmci_set_completed(host, EVENT_DATA_COMPLETE); + +		if (status & MCI_DTOE) { +			dev_dbg(&mmc->class_dev, +					"data timeout error\n"); +			data->error = -ETIMEDOUT; +		} else if (status & MCI_DCRCE) { +			dev_dbg(&mmc->class_dev, "data CRC error\n"); +			data->error = -EILSEQ; +		} else { +			dev_dbg(&mmc->class_dev, +					"data FIFO error (status=%08x)\n", +					status); +			data->error = -EIO; +		} + +		if (host->present && data->stop +				&& atmci_is_completed(host, EVENT_CMD_COMPLETE) +				&& !atmci_test_and_set_completed( +					host, EVENT_STOP_SENT)) +			send_stop_cmd(host->mmc, data); + +		host->data = NULL; +	} +	if (atmci_test_and_clear_pending(host, EVENT_DATA_COMPLETE)) { +		atmci_set_completed(host, EVENT_DATA_COMPLETE); + +		if (!atmci_is_completed(host, EVENT_DATA_ERROR)) { +			data->bytes_xfered = data->blocks * data->blksz; +			data->error = 0; +		} + +		host->data = NULL; +	} + +	if (host->mrq && !host->cmd && !host->data) +		atmci_request_end(mmc, host->mrq); +} + +static void atmci_read_data_pio(struct atmel_mci *host) +{ +	struct scatterlist	*sg = host->sg; +	void			*buf = sg_virt(sg); +	unsigned int		offset = host->pio_offset; +	struct mmc_data		*data = host->data; +	u32			value; +	u32			status; +	unsigned int		nbytes = 0; + +	do { +		value = mci_readl(host, RDR); +		if (likely(offset + 4 <= sg->length)) { +			put_unaligned(value, (u32 *)(buf + offset)); + +			offset += 4; +			nbytes += 4; + +			if (offset == sg->length) { +				host->sg = sg = sg_next(sg); +				if (!sg) +					goto done; + +				offset = 0; +				buf = sg_virt(sg); +			} +		} else { +			unsigned int remaining = sg->length - offset; +			memcpy(buf + offset, &value, remaining); +			nbytes += remaining; + +			flush_dcache_page(sg_page(sg)); +			host->sg = sg = sg_next(sg); +			if (!sg) +				goto done; + +			offset = 4 - remaining; +			buf = sg_virt(sg); +			memcpy(buf, (u8 *)&value + remaining, offset); +			nbytes += offset; +		} + +		status = mci_readl(host, SR); +		if (status & ATMCI_DATA_ERROR_FLAGS) { +			mci_writel(host, IDR, (MCI_NOTBUSY | MCI_RXRDY +						| ATMCI_DATA_ERROR_FLAGS)); +			host->data_status = status; +			atmci_set_pending(host, EVENT_DATA_ERROR); +			tasklet_schedule(&host->tasklet); +			break; +		} +	} while (status & MCI_RXRDY); + +	host->pio_offset = offset; +	data->bytes_xfered += nbytes; + +	return; + +done: +	mci_writel(host, IDR, MCI_RXRDY); +	mci_writel(host, IER, MCI_NOTBUSY); +	data->bytes_xfered += nbytes; +	atmci_set_completed(host, EVENT_XFER_COMPLETE); +	if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE) +			&& !atmci_test_and_set_completed(host, EVENT_STOP_SENT)) +		send_stop_cmd(host->mmc, data); +} + +static void atmci_write_data_pio(struct atmel_mci *host) +{ +	struct scatterlist	*sg = host->sg; +	void			*buf = sg_virt(sg); +	unsigned int		offset = host->pio_offset; +	struct mmc_data		*data = host->data; +	u32			value; +	u32			status; +	unsigned int		nbytes = 0; + +	do { +		if (likely(offset + 4 <= sg->length)) { +			value = get_unaligned((u32 *)(buf + offset)); +			mci_writel(host, TDR, value); + +			offset += 4; +			nbytes += 4; +			if (offset == sg->length) { +				host->sg = sg = sg_next(sg); +				if (!sg) +					goto done; + +				offset = 0; +				buf = sg_virt(sg); +			} +		} else { +			unsigned int remaining = sg->length - offset; + +			value = 0; +			memcpy(&value, buf + offset, remaining); +			nbytes += remaining; + +			host->sg = sg = sg_next(sg); +			if (!sg) { +				mci_writel(host, TDR, value); +				goto done; +			} + +			offset = 4 - remaining; +			buf = sg_virt(sg); +			memcpy((u8 *)&value + remaining, buf, offset); +			mci_writel(host, TDR, value); +			nbytes += offset; +		} + +		status = mci_readl(host, SR); +		if (status & ATMCI_DATA_ERROR_FLAGS) { +			mci_writel(host, IDR, (MCI_NOTBUSY | MCI_TXRDY +						| ATMCI_DATA_ERROR_FLAGS)); +			host->data_status = status; +			atmci_set_pending(host, EVENT_DATA_ERROR); +			tasklet_schedule(&host->tasklet); +			break; +		} +	} while (status & MCI_TXRDY); + +	host->pio_offset = offset; +	data->bytes_xfered += nbytes; + +	return; + +done: +	mci_writel(host, IDR, MCI_TXRDY); +	mci_writel(host, IER, MCI_NOTBUSY); +	data->bytes_xfered += nbytes; +	atmci_set_completed(host, EVENT_XFER_COMPLETE); +	if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE) +			&& !atmci_test_and_set_completed(host, EVENT_STOP_SENT)) +		send_stop_cmd(host->mmc, data); +} + +static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status) +{ +	struct atmel_mci	*host = mmc_priv(mmc); + +	mci_writel(host, IDR, MCI_CMDRDY); + +	if (atmci_is_completed(host, EVENT_STOP_SENT)) { +		host->stop_status = status; +		atmci_set_pending(host, EVENT_STOP_COMPLETE); +	} else { +		host->cmd_status = status; +		atmci_set_pending(host, EVENT_CMD_COMPLETE); +	} + +	tasklet_schedule(&host->tasklet); +} + +static irqreturn_t atmci_interrupt(int irq, void *dev_id) +{ +	struct mmc_host		*mmc = dev_id; +	struct atmel_mci	*host = mmc_priv(mmc); +	u32			status, mask, pending; +	unsigned int		pass_count = 0; + +	spin_lock(&mmc->lock); + +	do { +		status = mci_readl(host, SR); +		mask = mci_readl(host, IMR); +		pending = status & mask; +		if (!pending) +			break; + +		if (pending & ATMCI_DATA_ERROR_FLAGS) { +			mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS +					| MCI_RXRDY | MCI_TXRDY); +			pending &= mci_readl(host, IMR); +			host->data_status = status; +			atmci_set_pending(host, EVENT_DATA_ERROR); +			tasklet_schedule(&host->tasklet); +		} +		if (pending & MCI_NOTBUSY) { +			mci_writel(host, IDR, (MCI_NOTBUSY +					       | ATMCI_DATA_ERROR_FLAGS)); +			atmci_set_pending(host, EVENT_DATA_COMPLETE); +			tasklet_schedule(&host->tasklet); +		} +		if (pending & MCI_RXRDY) +			atmci_read_data_pio(host); +		if (pending & MCI_TXRDY) +			atmci_write_data_pio(host); + +		if (pending & MCI_CMDRDY) +			atmci_cmd_interrupt(mmc, status); +	} while (pass_count++ < 5); + +	spin_unlock(&mmc->lock); + +	return pass_count ? IRQ_HANDLED : IRQ_NONE; +} + +static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) +{ +	struct mmc_host		*mmc = dev_id; +	struct atmel_mci	*host = mmc_priv(mmc); + +	/* +	 * Disable interrupts until the pin has stabilized and check +	 * the state then. Use mod_timer() since we may be in the +	 * middle of the timer routine when this interrupt triggers. +	 */ +	disable_irq_nosync(irq); +	mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(20)); + +	return IRQ_HANDLED; +} + +static int __init atmci_probe(struct platform_device *pdev) +{ +	struct mci_platform_data	*pdata; +	struct atmel_mci *host; +	struct mmc_host *mmc; +	struct resource *regs; +	int irq; +	int ret; + +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!regs) +		return -ENXIO; +	pdata = pdev->dev.platform_data; +	if (!pdata) +		return -ENXIO; +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) +		return irq; + +	mmc = mmc_alloc_host(sizeof(struct atmel_mci), &pdev->dev); +	if (!mmc) +		return -ENOMEM; + +	host = mmc_priv(mmc); +	host->pdev = pdev; +	host->mmc = mmc; +	host->detect_pin = pdata->detect_pin; +	host->wp_pin = pdata->wp_pin; + +	host->mck = clk_get(&pdev->dev, "mci_clk"); +	if (IS_ERR(host->mck)) { +		ret = PTR_ERR(host->mck); +		goto err_clk_get; +	} + +	ret = -ENOMEM; +	host->regs = ioremap(regs->start, regs->end - regs->start + 1); +	if (!host->regs) +		goto err_ioremap; + +	clk_enable(host->mck); +	mci_writel(host, CR, MCI_CR_SWRST); +	host->bus_hz = clk_get_rate(host->mck); +	clk_disable(host->mck); + +	host->mapbase = regs->start; + +	mmc->ops = &atmci_ops; +	mmc->f_min = (host->bus_hz + 511) / 512; +	mmc->f_max = host->bus_hz / 2; +	mmc->ocr_avail	= MMC_VDD_32_33 | MMC_VDD_33_34; +	mmc->caps |= MMC_CAP_4_BIT_DATA; + +	mmc->max_hw_segs = 64; +	mmc->max_phys_segs = 64; +	mmc->max_req_size = 32768 * 512; +	mmc->max_blk_size = 32768; +	mmc->max_blk_count = 512; + +	tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)mmc); + +	ret = request_irq(irq, atmci_interrupt, 0, pdev->dev.bus_id, mmc); +	if (ret) +		goto err_request_irq; + +	/* Assume card is present if we don't have a detect pin */ +	host->present = 1; +	if (host->detect_pin >= 0) { +		if (gpio_request(host->detect_pin, "mmc_detect")) { +			dev_dbg(&mmc->class_dev, "no detect pin available\n"); +			host->detect_pin = -1; +		} else { +			host->present = !gpio_get_value(host->detect_pin); +		} +	} +	if (host->wp_pin >= 0) { +		if (gpio_request(host->wp_pin, "mmc_wp")) { +			dev_dbg(&mmc->class_dev, "no WP pin available\n"); +			host->wp_pin = -1; +		} +	} + +	platform_set_drvdata(pdev, host); + +	mmc_add_host(mmc); + +	if (host->detect_pin >= 0) { +		setup_timer(&host->detect_timer, atmci_detect_change, +				(unsigned long)host); + +		ret = request_irq(gpio_to_irq(host->detect_pin), +				atmci_detect_interrupt, +				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, +				"mmc-detect", mmc); +		if (ret) { +			dev_dbg(&mmc->class_dev, +				"could not request IRQ %d for detect pin\n", +				gpio_to_irq(host->detect_pin)); +			gpio_free(host->detect_pin); +			host->detect_pin = -1; +		} +	} + +	dev_info(&mmc->class_dev, +			"Atmel MCI controller at 0x%08lx irq %d\n", +			host->mapbase, irq); + +	return 0; + +err_request_irq: +	iounmap(host->regs); +err_ioremap: +	clk_put(host->mck); +err_clk_get: +	mmc_free_host(mmc); +	return ret; +} + +static int __exit atmci_remove(struct platform_device *pdev) +{ +	struct atmel_mci *host = platform_get_drvdata(pdev); + +	platform_set_drvdata(pdev, NULL); + +	if (host) { +		if (host->detect_pin >= 0) { +			int pin = host->detect_pin; + +			/* Make sure the timer doesn't enable the interrupt */ +			host->detect_pin = -1; +			smp_wmb(); + +			free_irq(gpio_to_irq(pin), host->mmc); +			del_timer_sync(&host->detect_timer); +			gpio_free(pin); +		} + +		mmc_remove_host(host->mmc); + +		clk_enable(host->mck); +		mci_writel(host, IDR, ~0UL); +		mci_writel(host, CR, MCI_CR_MCIDIS); +		mci_readl(host, SR); +		clk_disable(host->mck); + +		if (host->wp_pin >= 0) +			gpio_free(host->wp_pin); + +		free_irq(platform_get_irq(pdev, 0), host->mmc); +		iounmap(host->regs); + +		clk_put(host->mck); + +		mmc_free_host(host->mmc); +	} +	return 0; +} + +static struct platform_driver atmci_driver = { +	.remove		= __exit_p(atmci_remove), +	.driver		= { +		.name		= "atmel_mci", +	}, +}; + +static int __init atmci_init(void) +{ +	return platform_driver_probe(&atmci_driver, atmci_probe); +} + +static void __exit atmci_exit(void) +{ +	platform_driver_unregister(&atmci_driver); +} + +module_init(atmci_init); +module_exit(atmci_exit); + +MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver"); +MODULE_AUTHOR("Haavard Skinnemoen <haavard.skinnemoen@atmel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index cc5f7bc546a..3f15eb20489 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -21,7 +21,7 @@   * published by the Free Software Foundation.   */ -/* Why is a timer used to detect insert events? +/* Why don't we use the SD controllers' carddetect feature?   *   * From the AU1100 MMC application guide:   * If the Au1100-based design is intended to support both MultiMediaCards @@ -30,8 +30,6 @@   * In doing so, a MMC card never enters SPI-mode communications,   * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective   * (the low to high transition will not occur). - * - * So we use the timer to check the status manually.   */  #include <linux/module.h> @@ -41,51 +39,110 @@  #include <linux/interrupt.h>  #include <linux/dma-mapping.h>  #include <linux/scatterlist.h> - +#include <linux/leds.h>  #include <linux/mmc/host.h> +  #include <asm/io.h>  #include <asm/mach-au1x00/au1000.h>  #include <asm/mach-au1x00/au1xxx_dbdma.h>  #include <asm/mach-au1x00/au1100_mmc.h> -#include <au1xxx.h> -#include "au1xmmc.h" -  #define DRIVER_NAME "au1xxx-mmc"  /* Set this to enable special debugging macros */ +/* #define DEBUG */  #ifdef DEBUG -#define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) +#define DBG(fmt, idx, args...)	\ +	printk(KERN_DEBUG "au1xmmc(%d): DEBUG: " fmt, idx, ##args)  #else -#define DBG(fmt, idx, args...) +#define DBG(fmt, idx, args...) do {} while (0)  #endif -const struct { +/* Hardware definitions */ +#define AU1XMMC_DESCRIPTOR_COUNT 1 +#define AU1XMMC_DESCRIPTOR_SIZE  2048 + +#define AU1XMMC_OCR (MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \ +		     MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \ +		     MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36) + +/* This gives us a hard value for the stop command that we can write directly + * to the command register. + */ +#define STOP_CMD	\ +	(SD_CMD_RT_1B | SD_CMD_CT_7 | (0xC << SD_CMD_CI_SHIFT) | SD_CMD_GO) + +/* This is the set of interrupts that we configure by default. */ +#define AU1XMMC_INTERRUPTS 				\ +	(SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_RAT |	\ +	 SD_CONFIG_CR | SD_CONFIG_I) + +/* The poll event (looking for insert/remove events runs twice a second. */ +#define AU1XMMC_DETECT_TIMEOUT (HZ/2) + +struct au1xmmc_host { +	struct mmc_host *mmc; +	struct mmc_request *mrq; + +	u32 flags;  	u32 iobase; -	u32 tx_devid, rx_devid; -	u16 bcsrpwr; -	u16 bcsrstatus; -	u16 wpstatus; -} au1xmmc_card_table[] = { -	{ SD0_BASE, DSCR_CMD0_SDMS_TX0, DSCR_CMD0_SDMS_RX0, -	  BCSR_BOARD_SD0PWR, BCSR_INT_SD0INSERT, BCSR_STATUS_SD0WP }, -#ifndef CONFIG_MIPS_DB1200 -	{ SD1_BASE, DSCR_CMD0_SDMS_TX1, DSCR_CMD0_SDMS_RX1, -	  BCSR_BOARD_DS1PWR, BCSR_INT_SD1INSERT, BCSR_STATUS_SD1WP } -#endif +	u32 clock; +	u32 bus_width; +	u32 power_mode; + +	int status; + +	struct { +		int len; +		int dir; +	} dma; + +	struct { +		int index; +		int offset; +		int len; +	} pio; + +	u32 tx_chan; +	u32 rx_chan; + +	int irq; + +	struct tasklet_struct finish_task; +	struct tasklet_struct data_task; +	struct au1xmmc_platform_data *platdata; +	struct platform_device *pdev; +	struct resource *ioarea;  }; -#define AU1XMMC_CONTROLLER_COUNT (ARRAY_SIZE(au1xmmc_card_table)) +/* Status flags used by the host structure */ +#define HOST_F_XMIT	0x0001 +#define HOST_F_RECV	0x0002 +#define HOST_F_DMA	0x0010 +#define HOST_F_ACTIVE	0x0100 +#define HOST_F_STOP	0x1000 -/* This array stores pointers for the hosts (used by the IRQ handler) */ -struct au1xmmc_host *au1xmmc_hosts[AU1XMMC_CONTROLLER_COUNT]; -static int dma = 1; +#define HOST_S_IDLE	0x0001 +#define HOST_S_CMD	0x0002 +#define HOST_S_DATA	0x0003 +#define HOST_S_STOP	0x0004 -#ifdef MODULE -module_param(dma, bool, 0); -MODULE_PARM_DESC(dma, "Use DMA engine for data transfers (0 = disabled)"); -#endif +/* Easy access macros */ +#define HOST_STATUS(h)	((h)->iobase + SD_STATUS) +#define HOST_CONFIG(h)	((h)->iobase + SD_CONFIG) +#define HOST_ENABLE(h)	((h)->iobase + SD_ENABLE) +#define HOST_TXPORT(h)	((h)->iobase + SD_TXPORT) +#define HOST_RXPORT(h)	((h)->iobase + SD_RXPORT) +#define HOST_CMDARG(h)	((h)->iobase + SD_CMDARG) +#define HOST_BLKSIZE(h)	((h)->iobase + SD_BLKSIZE) +#define HOST_CMD(h)	((h)->iobase + SD_CMD) +#define HOST_CONFIG2(h)	((h)->iobase + SD_CONFIG2) +#define HOST_TIMEOUT(h)	((h)->iobase + SD_TIMEOUT) +#define HOST_DEBUG(h)	((h)->iobase + SD_DEBUG) + +#define DMA_CHANNEL(h)	\ +	(((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)  static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask)  { @@ -119,14 +176,13 @@ static inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask)  static inline void SEND_STOP(struct au1xmmc_host *host)  { - -	/* We know the value of CONFIG2, so avoid a read we don't need */ -	u32 mask = SD_CONFIG2_EN; +	u32 config2;  	WARN_ON(host->status != HOST_S_DATA);  	host->status = HOST_S_STOP; -	au_writel(mask | SD_CONFIG2_DF, HOST_CONFIG2(host)); +	config2 = au_readl(HOST_CONFIG2(host)); +	au_writel(config2 | SD_CONFIG2_DF, HOST_CONFIG2(host));  	au_sync();  	/* Send the stop commmand */ @@ -135,35 +191,36 @@ static inline void SEND_STOP(struct au1xmmc_host *host)  static void au1xmmc_set_power(struct au1xmmc_host *host, int state)  { - -	u32 val = au1xmmc_card_table[host->id].bcsrpwr; - -	bcsr->board &= ~val; -	if (state) bcsr->board |= val; - -	au_sync_delay(1); +	if (host->platdata && host->platdata->set_power) +		host->platdata->set_power(host->mmc, state);  } -static inline int au1xmmc_card_inserted(struct au1xmmc_host *host) +static int au1xmmc_card_inserted(struct mmc_host *mmc)  { -	return (bcsr->sig_status & au1xmmc_card_table[host->id].bcsrstatus) -		? 1 : 0; +	struct au1xmmc_host *host = mmc_priv(mmc); + +	if (host->platdata && host->platdata->card_inserted) +		return !!host->platdata->card_inserted(host->mmc); + +	return -ENOSYS;  }  static int au1xmmc_card_readonly(struct mmc_host *mmc)  {  	struct au1xmmc_host *host = mmc_priv(mmc); -	return (bcsr->status & au1xmmc_card_table[host->id].wpstatus) -		? 1 : 0; + +	if (host->platdata && host->platdata->card_readonly) +		return !!host->platdata->card_readonly(mmc); + +	return -ENOSYS;  }  static void au1xmmc_finish_request(struct au1xmmc_host *host)  { -  	struct mmc_request *mrq = host->mrq;  	host->mrq = NULL; -	host->flags &= HOST_F_ACTIVE; +	host->flags &= HOST_F_ACTIVE | HOST_F_DMA;  	host->dma.len = 0;  	host->dma.dir = 0; @@ -174,8 +231,6 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host)  	host->status = HOST_S_IDLE; -	bcsr->disk_leds |= (1 << 8); -  	mmc_request_done(host->mmc, mrq);  } @@ -235,18 +290,14 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,  	au_sync();  	/* Wait for the command to go on the line */ - -	while(1) { -		if (!(au_readl(HOST_CMD(host)) & SD_CMD_GO)) -			break; -	} +	while (au_readl(HOST_CMD(host)) & SD_CMD_GO) +		/* nop */;  	/* Wait for the command to come back */ -  	if (wait) {  		u32 status = au_readl(HOST_STATUS(host)); -		while(!(status & SD_STATUS_CR)) +		while (!(status & SD_STATUS_CR))  			status = au_readl(HOST_STATUS(host));  		/* Clear the CR status */ @@ -260,12 +311,11 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,  static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)  { -  	struct mmc_request *mrq = host->mrq;  	struct mmc_data *data;  	u32 crc; -	WARN_ON(host->status != HOST_S_DATA && host->status != HOST_S_STOP); +	WARN_ON((host->status != HOST_S_DATA) && (host->status != HOST_S_STOP));  	if (host->mrq == NULL)  		return; @@ -276,15 +326,13 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)  		status = au_readl(HOST_STATUS(host));  	/* The transaction is really over when the SD_STATUS_DB bit is clear */ - -	while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB)) +	while ((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))  		status = au_readl(HOST_STATUS(host));  	data->error = 0;  	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);          /* Process any errors */ -  	crc = (status & (SD_STATUS_WC | SD_STATUS_RC));  	if (host->flags & HOST_F_XMIT)  		crc |= ((status & 0x07) == 0x02) ? 0 : 1; @@ -299,16 +347,16 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)  	if (!data->error) {  		if (host->flags & HOST_F_DMA) { +#ifdef CONFIG_SOC_AU1200	/* DBDMA */  			u32 chan = DMA_CHANNEL(host); -			chan_tab_t *c = *((chan_tab_t **) chan); +			chan_tab_t *c = *((chan_tab_t **)chan);  			au1x_dma_chan_t *cp = c->chan_ptr;  			data->bytes_xfered = cp->ddma_bytecnt; -		} -		else +#endif +		} else  			data->bytes_xfered = -				(data->blocks * data->blksz) - -				host->pio.len; +				(data->blocks * data->blksz) - host->pio.len;  	}  	au1xmmc_finish_request(host); @@ -316,7 +364,7 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)  static void au1xmmc_tasklet_data(unsigned long param)  { -	struct au1xmmc_host *host = (struct au1xmmc_host *) param; +	struct au1xmmc_host *host = (struct au1xmmc_host *)param;  	u32 status = au_readl(HOST_STATUS(host));  	au1xmmc_data_complete(host, status); @@ -326,11 +374,10 @@ static void au1xmmc_tasklet_data(unsigned long param)  static void au1xmmc_send_pio(struct au1xmmc_host *host)  { - -	struct mmc_data *data = 0; -	int sg_len, max, count = 0; -	unsigned char *sg_ptr; -	u32 status = 0; +	struct mmc_data *data; +	int sg_len, max, count; +	unsigned char *sg_ptr, val; +	u32 status;  	struct scatterlist *sg;  	data = host->mrq->data; @@ -345,14 +392,12 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)  	/* This is the space left inside the buffer */  	sg_len = data->sg[host->pio.index].length - host->pio.offset; -	/* Check to if we need less then the size of the sg_buffer */ - +	/* Check if we need less than the size of the sg_buffer */  	max = (sg_len > host->pio.len) ? host->pio.len : sg_len; -	if (max > AU1XMMC_MAX_TRANSFER) max = AU1XMMC_MAX_TRANSFER; - -	for(count = 0; count < max; count++ ) { -		unsigned char val; +	if (max > AU1XMMC_MAX_TRANSFER) +		max = AU1XMMC_MAX_TRANSFER; +	for (count = 0; count < max; count++) {  		status = au_readl(HOST_STATUS(host));  		if (!(status & SD_STATUS_TH)) @@ -360,7 +405,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)  		val = *sg_ptr++; -		au_writel((unsigned long) val, HOST_TXPORT(host)); +		au_writel((unsigned long)val, HOST_TXPORT(host));  		au_sync();  	} @@ -384,11 +429,10 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)  static void au1xmmc_receive_pio(struct au1xmmc_host *host)  { - -	struct mmc_data *data = 0; -	int sg_len = 0, max = 0, count = 0; -	unsigned char *sg_ptr = 0; -	u32 status = 0; +	struct mmc_data *data; +	int max, count, sg_len = 0; +	unsigned char *sg_ptr = NULL; +	u32 status, val;  	struct scatterlist *sg;  	data = host->mrq->data; @@ -405,33 +449,33 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)  		/* This is the space left inside the buffer */  		sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; -		/* Check to if we need less then the size of the sg_buffer */ -		if (sg_len < max) max = sg_len; +		/* Check if we need less than the size of the sg_buffer */ +		if (sg_len < max) +			max = sg_len;  	}  	if (max > AU1XMMC_MAX_TRANSFER)  		max = AU1XMMC_MAX_TRANSFER; -	for(count = 0; count < max; count++ ) { -		u32 val; +	for (count = 0; count < max; count++) {  		status = au_readl(HOST_STATUS(host));  		if (!(status & SD_STATUS_NE))  			break;  		if (status & SD_STATUS_RC) { -			DBG("RX CRC Error [%d + %d].\n", host->id, +			DBG("RX CRC Error [%d + %d].\n", host->pdev->id,  					host->pio.len, count);  			break;  		}  		if (status & SD_STATUS_RO) { -			DBG("RX Overrun [%d + %d]\n", host->id, +			DBG("RX Overrun [%d + %d]\n", host->pdev->id,  					host->pio.len, count);  			break;  		}  		else if (status & SD_STATUS_RU) { -			DBG("RX Underrun [%d + %d]\n", host->id, +			DBG("RX Underrun [%d + %d]\n", host->pdev->id,  					host->pio.len,	count);  			break;  		} @@ -439,7 +483,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)  		val = au_readl(HOST_RXPORT(host));  		if (sg_ptr) -			*sg_ptr++ = (unsigned char) (val & 0xFF); +			*sg_ptr++ = (unsigned char)(val & 0xFF);  	}  	host->pio.len -= count; @@ -451,7 +495,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)  	}  	if (host->pio.len == 0) { -		//IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); +		/* IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); */  		IRQ_OFF(host, SD_CONFIG_NE);  		if (host->flags & HOST_F_STOP) @@ -461,17 +505,15 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)  	}  } -/* static void au1xmmc_cmd_complete -   This is called when a command has been completed - grab the response -   and check for errors.  Then start the data transfer if it is indicated. -*/ - +/* This is called when a command has been completed - grab the response + * and check for errors.  Then start the data transfer if it is indicated. + */  static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)  { -  	struct mmc_request *mrq = host->mrq;  	struct mmc_command *cmd; -	int trans; +	u32 r[4]; +	int i, trans;  	if (!host->mrq)  		return; @@ -481,9 +523,6 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)  	if (cmd->flags & MMC_RSP_PRESENT) {  		if (cmd->flags & MMC_RSP_136) { -			u32 r[4]; -			int i; -  			r[0] = au_readl(host->iobase + SD_RESP3);  			r[1] = au_readl(host->iobase + SD_RESP2);  			r[2] = au_readl(host->iobase + SD_RESP1); @@ -491,10 +530,9 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)  			/* The CRC is omitted from the response, so really  			 * we only got 120 bytes, but the engine expects -			 * 128 bits, so we have to shift things up +			 * 128 bits, so we have to shift things up.  			 */ - -			for(i = 0; i < 4; i++) { +			for (i = 0; i < 4; i++) {  				cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8;  				if (i != 3)  					cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24; @@ -505,22 +543,20 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)  			 * our response omits the CRC, our data ends up  			 * being shifted 8 bits to the right.  In this case,  			 * that means that the OSR data starts at bit 31, -			 * so we can just read RESP0 and return that +			 * so we can just read RESP0 and return that.  			 */  			cmd->resp[0] = au_readl(host->iobase + SD_RESP0);  		}  	}          /* Figure out errors */ -  	if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))  		cmd->error = -EILSEQ;  	trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);  	if (!trans || cmd->error) { - -		IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF); +		IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);  		tasklet_schedule(&host->finish_task);  		return;  	} @@ -528,6 +564,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)  	host->status = HOST_S_DATA;  	if (host->flags & HOST_F_DMA) { +#ifdef CONFIG_SOC_AU1200	/* DBDMA */  		u32 channel = DMA_CHANNEL(host);  		/* Start the DMA as soon as the buffer gets something in it */ @@ -540,23 +577,21 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)  		}  		au1xxx_dbdma_start(channel); +#endif  	}  }  static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate)  { -  	unsigned int pbus = get_au1x00_speed();  	unsigned int divisor;  	u32 config;  	/* From databook: -	   divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1 -	*/ - +	 * divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1 +	 */  	pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2);  	pbus /= 2; -  	divisor = ((pbus / rate) / 2) - 1;  	config = au_readl(HOST_CONFIG(host)); @@ -568,15 +603,11 @@ static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate)  	au_sync();  } -static int -au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) +static int au1xmmc_prepare_data(struct au1xmmc_host *host, +				struct mmc_data *data)  { -  	int datalen = data->blocks * data->blksz; -	if (dma != 0) -		host->flags |= HOST_F_DMA; -  	if (data->flags & MMC_DATA_READ)  		host->flags |= HOST_F_RECV;  	else @@ -596,12 +627,13 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)  	au_writel(data->blksz - 1, HOST_BLKSIZE(host));  	if (host->flags & HOST_F_DMA) { +#ifdef CONFIG_SOC_AU1200	/* DBDMA */  		int i;  		u32 channel = DMA_CHANNEL(host);  		au1xxx_dbdma_stop(channel); -		for(i = 0; i < host->dma.len; i++) { +		for (i = 0; i < host->dma.len; i++) {  			u32 ret = 0, flags = DDMA_FLAGS_NOIE;  			struct scatterlist *sg = &data->sg[i];  			int sg_len = sg->length; @@ -611,23 +643,21 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)  			if (i == host->dma.len - 1)  				flags = DDMA_FLAGS_IE; -    			if (host->flags & HOST_F_XMIT){ -      				ret = au1xxx_dbdma_put_source_flags(channel, -					(void *) sg_virt(sg), len, flags); -			} -    			else { -      				ret = au1xxx_dbdma_put_dest_flags(channel, -					(void *) sg_virt(sg), -					len, flags); +			if (host->flags & HOST_F_XMIT) { +				ret = au1xxx_dbdma_put_source_flags(channel, +					(void *)sg_virt(sg), len, flags); +			} else { +				ret = au1xxx_dbdma_put_dest_flags(channel, +					(void *)sg_virt(sg), len, flags);  			} -    			if (!ret) +			if (!ret)  				goto dataerr;  			datalen -= len;  		} -	} -	else { +#endif +	} else {  		host->pio.index = 0;  		host->pio.offset = 0;  		host->pio.len = datalen; @@ -636,25 +666,21 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)  			IRQ_ON(host, SD_CONFIG_TH);  		else  			IRQ_ON(host, SD_CONFIG_NE); -			//IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF); +			/* IRQ_ON(host, SD_CONFIG_RA | SD_CONFIG_RF); */  	}  	return 0; - dataerr: -	dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); +dataerr: +	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, +			host->dma.dir);  	return -ETIMEDOUT;  } -/* static void au1xmmc_request -   This actually starts a command or data transaction -*/ - +/* This actually starts a command or data transaction */  static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)  { -  	struct au1xmmc_host *host = mmc_priv(mmc); -	unsigned int flags = 0;  	int ret = 0;  	WARN_ON(irqs_disabled()); @@ -663,11 +689,15 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)  	host->mrq = mrq;  	host->status = HOST_S_CMD; -	bcsr->disk_leds &= ~(1 << 8); +	/* fail request immediately if no card is present */ +	if (0 == au1xmmc_card_inserted(mmc)) { +		mrq->cmd->error = -ENOMEDIUM; +		au1xmmc_finish_request(host); +		return; +	}  	if (mrq->data) {  		FLUSH_FIFO(host); -		flags = mrq->data->flags;  		ret = au1xmmc_prepare_data(host, mrq->data);  	} @@ -682,7 +712,6 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)  static void au1xmmc_reset_controller(struct au1xmmc_host *host)  { -  	/* Apply the clock */  	au_writel(SD_ENABLE_CE, HOST_ENABLE(host));          au_sync_delay(1); @@ -712,9 +741,10 @@ static void au1xmmc_reset_controller(struct au1xmmc_host *host)  } -static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) +static void au1xmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  {  	struct au1xmmc_host *host = mmc_priv(mmc); +	u32 config2;  	if (ios->power_mode == MMC_POWER_OFF)  		au1xmmc_set_power(host, 0); @@ -726,21 +756,18 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)  		au1xmmc_set_clock(host, ios->clock);  		host->clock = ios->clock;  	} -} - -static void au1xmmc_dma_callback(int irq, void *dev_id) -{ -	struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id; - -	/* Avoid spurious interrupts */ - -	if (!host->mrq) -		return; -	if (host->flags & HOST_F_STOP) -		SEND_STOP(host); - -	tasklet_schedule(&host->data_task); +	config2 = au_readl(HOST_CONFIG2(host)); +	switch (ios->bus_width) { +	case MMC_BUS_WIDTH_4: +		config2 |= SD_CONFIG2_WB; +		break; +	case MMC_BUS_WIDTH_1: +		config2 &= ~SD_CONFIG2_WB; +		break; +	} +	au_writel(config2, HOST_CONFIG2(host)); +	au_sync();  }  #define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT) @@ -749,245 +776,354 @@ static void au1xmmc_dma_callback(int irq, void *dev_id)  static irqreturn_t au1xmmc_irq(int irq, void *dev_id)  { - +	struct au1xmmc_host *host = dev_id;  	u32 status; -	int i, ret = 0; - -	disable_irq(AU1100_SD_IRQ); -	for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { -		struct au1xmmc_host * host = au1xmmc_hosts[i]; -		u32 handled = 1; +	status = au_readl(HOST_STATUS(host)); -		status = au_readl(HOST_STATUS(host)); +	if (!(status & SD_STATUS_I)) +		return IRQ_NONE;	/* not ours */ -		if (host->mrq && (status & STATUS_TIMEOUT)) { -			if (status & SD_STATUS_RAT) -				host->mrq->cmd->error = -ETIMEDOUT; +	if (status & SD_STATUS_SI)	/* SDIO */ +		mmc_signal_sdio_irq(host->mmc); -			else if (status & SD_STATUS_DT) -				host->mrq->data->error = -ETIMEDOUT; +	if (host->mrq && (status & STATUS_TIMEOUT)) { +		if (status & SD_STATUS_RAT) +			host->mrq->cmd->error = -ETIMEDOUT; +		else if (status & SD_STATUS_DT) +			host->mrq->data->error = -ETIMEDOUT; -			/* In PIO mode, interrupts might still be enabled */ -			IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); +		/* In PIO mode, interrupts might still be enabled */ +		IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); -			//IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF); -			tasklet_schedule(&host->finish_task); -		} +		/* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */ +		tasklet_schedule(&host->finish_task); +	}  #if 0 -		else if (status & SD_STATUS_DD) { - -			/* Sometimes we get a DD before a NE in PIO mode */ - -			if (!(host->flags & HOST_F_DMA) && -					(status & SD_STATUS_NE)) -				au1xmmc_receive_pio(host); -			else { -				au1xmmc_data_complete(host, status); -				//tasklet_schedule(&host->data_task); -			} +	else if (status & SD_STATUS_DD) { +		/* Sometimes we get a DD before a NE in PIO mode */ +		if (!(host->flags & HOST_F_DMA) && (status & SD_STATUS_NE)) +			au1xmmc_receive_pio(host); +		else { +			au1xmmc_data_complete(host, status); +			/* tasklet_schedule(&host->data_task); */  		} +	}  #endif -		else if (status & (SD_STATUS_CR)) { -			if (host->status == HOST_S_CMD) -				au1xmmc_cmd_complete(host,status); -		} -		else if (!(host->flags & HOST_F_DMA)) { -			if ((host->flags & HOST_F_XMIT) && -			    (status & STATUS_DATA_OUT)) -				au1xmmc_send_pio(host); -			else if ((host->flags & HOST_F_RECV) && -			    (status & STATUS_DATA_IN)) -				au1xmmc_receive_pio(host); -		} -		else if (status & 0x203FBC70) { -			DBG("Unhandled status %8.8x\n", host->id, status); -			handled = 0; -		} +	else if (status & SD_STATUS_CR) { +		if (host->status == HOST_S_CMD) +			au1xmmc_cmd_complete(host, status); -		au_writel(status, HOST_STATUS(host)); -		au_sync(); +	} else if (!(host->flags & HOST_F_DMA)) { +		if ((host->flags & HOST_F_XMIT) && (status & STATUS_DATA_OUT)) +			au1xmmc_send_pio(host); +		else if ((host->flags & HOST_F_RECV) && (status & STATUS_DATA_IN)) +			au1xmmc_receive_pio(host); -		ret |= handled; +	} else if (status & 0x203F3C70) { +			DBG("Unhandled status %8.8x\n", host->pdev->id, +				status);  	} -	enable_irq(AU1100_SD_IRQ); -	return ret; +	au_writel(status, HOST_STATUS(host)); +	au_sync(); + +	return IRQ_HANDLED;  } -static void au1xmmc_poll_event(unsigned long arg) -{ -	struct au1xmmc_host *host = (struct au1xmmc_host *) arg; +#ifdef CONFIG_SOC_AU1200 +/* 8bit memory DMA device */ +static dbdev_tab_t au1xmmc_mem_dbdev = { +	.dev_id		= DSCR_CMD0_ALWAYS, +	.dev_flags	= DEV_FLAGS_ANYUSE, +	.dev_tsize	= 0, +	.dev_devwidth	= 8, +	.dev_physaddr	= 0x00000000, +	.dev_intlevel	= 0, +	.dev_intpolarity = 0, +}; +static int memid; -	int card = au1xmmc_card_inserted(host); -        int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0; +static void au1xmmc_dbdma_callback(int irq, void *dev_id) +{ +	struct au1xmmc_host *host = (struct au1xmmc_host *)dev_id; -	if (card != controller) { -		host->flags &= ~HOST_F_ACTIVE; -		if (card) host->flags |= HOST_F_ACTIVE; -		mmc_detect_change(host->mmc, 0); -	} +	/* Avoid spurious interrupts */ +	if (!host->mrq) +		return; -	if (host->mrq != NULL) { -		u32 status = au_readl(HOST_STATUS(host)); -		DBG("PENDING - %8.8x\n", host->id, status); -	} +	if (host->flags & HOST_F_STOP) +		SEND_STOP(host); -	mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT); +	tasklet_schedule(&host->data_task);  } -static dbdev_tab_t au1xmmc_mem_dbdev = +static int au1xmmc_dbdma_init(struct au1xmmc_host *host)  { -	DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 8, 0x00000000, 0, 0 -}; +	struct resource *res; +	int txid, rxid; -static void au1xmmc_init_dma(struct au1xmmc_host *host) -{ +	res = platform_get_resource(host->pdev, IORESOURCE_DMA, 0); +	if (!res) +		return -ENODEV; +	txid = res->start; -	u32 rxchan, txchan; +	res = platform_get_resource(host->pdev, IORESOURCE_DMA, 1); +	if (!res) +		return -ENODEV; +	rxid = res->start; -	int txid = au1xmmc_card_table[host->id].tx_devid; -	int rxid = au1xmmc_card_table[host->id].rx_devid; +	if (!memid) +		return -ENODEV; -	/* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride -	   of 8 bits.  And since devices are shared, we need to create -	   our own to avoid freaking out other devices -	*/ +	host->tx_chan = au1xxx_dbdma_chan_alloc(memid, txid, +				au1xmmc_dbdma_callback, (void *)host); +	if (!host->tx_chan) { +		dev_err(&host->pdev->dev, "cannot allocate TX DMA\n"); +		return -ENODEV; +	} + +	host->rx_chan = au1xxx_dbdma_chan_alloc(rxid, memid, +				au1xmmc_dbdma_callback, (void *)host); +	if (!host->rx_chan) { +		dev_err(&host->pdev->dev, "cannot allocate RX DMA\n"); +		au1xxx_dbdma_chan_free(host->tx_chan); +		return -ENODEV; +	} -	int memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); +	au1xxx_dbdma_set_devwidth(host->tx_chan, 8); +	au1xxx_dbdma_set_devwidth(host->rx_chan, 8); -	txchan = au1xxx_dbdma_chan_alloc(memid, txid, -					 au1xmmc_dma_callback, (void *) host); +	au1xxx_dbdma_ring_alloc(host->tx_chan, AU1XMMC_DESCRIPTOR_COUNT); +	au1xxx_dbdma_ring_alloc(host->rx_chan, AU1XMMC_DESCRIPTOR_COUNT); -	rxchan = au1xxx_dbdma_chan_alloc(rxid, memid, -					 au1xmmc_dma_callback, (void *) host); +	/* DBDMA is good to go */ +	host->flags |= HOST_F_DMA; -	au1xxx_dbdma_set_devwidth(txchan, 8); -	au1xxx_dbdma_set_devwidth(rxchan, 8); +	return 0; +} + +static void au1xmmc_dbdma_shutdown(struct au1xmmc_host *host) +{ +	if (host->flags & HOST_F_DMA) { +		host->flags &= ~HOST_F_DMA; +		au1xxx_dbdma_chan_free(host->tx_chan); +		au1xxx_dbdma_chan_free(host->rx_chan); +	} +} +#endif -	au1xxx_dbdma_ring_alloc(txchan, AU1XMMC_DESCRIPTOR_COUNT); -	au1xxx_dbdma_ring_alloc(rxchan, AU1XMMC_DESCRIPTOR_COUNT); +static void au1xmmc_enable_sdio_irq(struct mmc_host *mmc, int en) +{ +	struct au1xmmc_host *host = mmc_priv(mmc); -	host->tx_chan = txchan; -	host->rx_chan = rxchan; +	if (en) +		IRQ_ON(host, SD_CONFIG_SI); +	else +		IRQ_OFF(host, SD_CONFIG_SI);  }  static const struct mmc_host_ops au1xmmc_ops = {  	.request	= au1xmmc_request,  	.set_ios	= au1xmmc_set_ios,  	.get_ro		= au1xmmc_card_readonly, +	.get_cd		= au1xmmc_card_inserted, +	.enable_sdio_irq = au1xmmc_enable_sdio_irq,  };  static int __devinit au1xmmc_probe(struct platform_device *pdev)  { +	struct mmc_host *mmc; +	struct au1xmmc_host *host; +	struct resource *r; +	int ret; -	int i, ret = 0; +	mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); +	if (!mmc) { +		dev_err(&pdev->dev, "no memory for mmc_host\n"); +		ret = -ENOMEM; +		goto out0; +	} -	/* THe interrupt is shared among all controllers */ -	ret = request_irq(AU1100_SD_IRQ, au1xmmc_irq, IRQF_DISABLED, "MMC", 0); +	host = mmc_priv(mmc); +	host->mmc = mmc; +	host->platdata = pdev->dev.platform_data; +	host->pdev = pdev; -	if (ret) { -		printk(DRIVER_NAME "ERROR: Couldn't get int %d: %d\n", -				AU1100_SD_IRQ, ret); -		return -ENXIO; +	ret = -ENODEV; +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!r) { +		dev_err(&pdev->dev, "no mmio defined\n"); +		goto out1;  	} -	disable_irq(AU1100_SD_IRQ); +	host->ioarea = request_mem_region(r->start, r->end - r->start + 1, +					   pdev->name); +	if (!host->ioarea) { +		dev_err(&pdev->dev, "mmio already in use\n"); +		goto out1; +	} -	for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { -		struct mmc_host *mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); -		struct au1xmmc_host *host = 0; +	host->iobase = (unsigned long)ioremap(r->start, 0x3c); +	if (!host->iobase) { +		dev_err(&pdev->dev, "cannot remap mmio\n"); +		goto out2; +	} -		if (!mmc) { -			printk(DRIVER_NAME "ERROR: no mem for host %d\n", i); -			au1xmmc_hosts[i] = 0; -			continue; -		} +	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +	if (!r) { +		dev_err(&pdev->dev, "no IRQ defined\n"); +		goto out3; +	} -		mmc->ops = &au1xmmc_ops; +	host->irq = r->start; +	/* IRQ is shared among both SD controllers */ +	ret = request_irq(host->irq, au1xmmc_irq, IRQF_SHARED, +			  DRIVER_NAME, host); +	if (ret) { +		dev_err(&pdev->dev, "cannot grab IRQ\n"); +		goto out3; +	} -		mmc->f_min =   450000; -		mmc->f_max = 24000000; +	mmc->ops = &au1xmmc_ops; -		mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; -		mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; +	mmc->f_min =   450000; +	mmc->f_max = 24000000; -		mmc->max_blk_size = 2048; -		mmc->max_blk_count = 512; +	mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; +	mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; -		mmc->ocr_avail = AU1XMMC_OCR; +	mmc->max_blk_size = 2048; +	mmc->max_blk_count = 512; -		host = mmc_priv(mmc); -		host->mmc = mmc; +	mmc->ocr_avail = AU1XMMC_OCR; +	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; -		host->id = i; -		host->iobase = au1xmmc_card_table[host->id].iobase; -		host->clock = 0; -		host->power_mode = MMC_POWER_OFF; +	host->status = HOST_S_IDLE; -		host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0; -		host->status = HOST_S_IDLE; +	/* board-specific carddetect setup, if any */ +	if (host->platdata && host->platdata->cd_setup) { +		ret = host->platdata->cd_setup(mmc, 1); +		if (ret) { +			dev_warn(&pdev->dev, "board CD setup failed\n"); +			mmc->caps |= MMC_CAP_NEEDS_POLL; +		} +	} else +		mmc->caps |= MMC_CAP_NEEDS_POLL; -		init_timer(&host->timer); +	tasklet_init(&host->data_task, au1xmmc_tasklet_data, +			(unsigned long)host); -		host->timer.function = au1xmmc_poll_event; -		host->timer.data = (unsigned long) host; -		host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT; +	tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, +			(unsigned long)host); -		tasklet_init(&host->data_task, au1xmmc_tasklet_data, -				(unsigned long) host); +#ifdef CONFIG_SOC_AU1200 +	ret = au1xmmc_dbdma_init(host); +	if (ret) +		printk(KERN_INFO DRIVER_NAME ": DBDMA init failed; using PIO\n"); +#endif -		tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, -				(unsigned long) host); +#ifdef CONFIG_LEDS_CLASS +	if (host->platdata && host->platdata->led) { +		struct led_classdev *led = host->platdata->led; +		led->name = mmc_hostname(mmc); +		led->brightness = LED_OFF; +		led->default_trigger = mmc_hostname(mmc); +		ret = led_classdev_register(mmc_dev(mmc), led); +		if (ret) +			goto out5; +	} +#endif -		spin_lock_init(&host->lock); +	au1xmmc_reset_controller(host); -		if (dma != 0) -			au1xmmc_init_dma(host); +	ret = mmc_add_host(mmc); +	if (ret) { +		dev_err(&pdev->dev, "cannot add mmc host\n"); +		goto out6; +	} -		au1xmmc_reset_controller(host); +	platform_set_drvdata(pdev, mmc); -		mmc_add_host(mmc); -		au1xmmc_hosts[i] = host; +	printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X" +		" (mode=%s)\n", pdev->id, host->iobase, +		host->flags & HOST_F_DMA ? "dma" : "pio"); -		add_timer(&host->timer); +	return 0;	/* all ok */ -		printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X (mode=%s)\n", -		       host->id, host->iobase, dma ? "dma" : "pio"); -	} +out6: +#ifdef CONFIG_LEDS_CLASS +	if (host->platdata && host->platdata->led) +		led_classdev_unregister(host->platdata->led); +out5: +#endif +	au_writel(0, HOST_ENABLE(host)); +	au_writel(0, HOST_CONFIG(host)); +	au_writel(0, HOST_CONFIG2(host)); +	au_sync(); + +#ifdef CONFIG_SOC_AU1200 +	au1xmmc_dbdma_shutdown(host); +#endif -	enable_irq(AU1100_SD_IRQ); +	tasklet_kill(&host->data_task); +	tasklet_kill(&host->finish_task); -	return 0; +	if (host->platdata && host->platdata->cd_setup && +	    !(mmc->caps & MMC_CAP_NEEDS_POLL)) +		host->platdata->cd_setup(mmc, 0); + +	free_irq(host->irq, host); +out3: +	iounmap((void *)host->iobase); +out2: +	release_resource(host->ioarea); +	kfree(host->ioarea); +out1: +	mmc_free_host(mmc); +out0: +	return ret;  }  static int __devexit au1xmmc_remove(struct platform_device *pdev)  { +	struct mmc_host *mmc = platform_get_drvdata(pdev); +	struct au1xmmc_host *host; + +	if (mmc) { +		host  = mmc_priv(mmc); -	int i; +		mmc_remove_host(mmc); -	disable_irq(AU1100_SD_IRQ); +#ifdef CONFIG_LEDS_CLASS +		if (host->platdata && host->platdata->led) +			led_classdev_unregister(host->platdata->led); +#endif -	for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { -		struct au1xmmc_host *host = au1xmmc_hosts[i]; -		if (!host) continue; +		if (host->platdata && host->platdata->cd_setup && +		    !(mmc->caps & MMC_CAP_NEEDS_POLL)) +			host->platdata->cd_setup(mmc, 0); + +		au_writel(0, HOST_ENABLE(host)); +		au_writel(0, HOST_CONFIG(host)); +		au_writel(0, HOST_CONFIG2(host)); +		au_sync();  		tasklet_kill(&host->data_task);  		tasklet_kill(&host->finish_task); -		del_timer_sync(&host->timer); +#ifdef CONFIG_SOC_AU1200 +		au1xmmc_dbdma_shutdown(host); +#endif  		au1xmmc_set_power(host, 0); -		mmc_remove_host(host->mmc); - -		au1xxx_dbdma_chan_free(host->tx_chan); -		au1xxx_dbdma_chan_free(host->rx_chan); +		free_irq(host->irq, host); +		iounmap((void *)host->iobase); +		release_resource(host->ioarea); +		kfree(host->ioarea); -		au_writel(0x0, HOST_ENABLE(host)); -		au_sync(); +		mmc_free_host(mmc);  	} - -	free_irq(AU1100_SD_IRQ, 0);  	return 0;  } @@ -1004,21 +1140,31 @@ static struct platform_driver au1xmmc_driver = {  static int __init au1xmmc_init(void)  { +#ifdef CONFIG_SOC_AU1200 +	/* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride +	 * of 8 bits.  And since devices are shared, we need to create +	 * our own to avoid freaking out other devices. +	 */ +	memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); +	if (!memid) +		printk(KERN_ERR "au1xmmc: cannot add memory dbdma dev\n"); +#endif  	return platform_driver_register(&au1xmmc_driver);  }  static void __exit au1xmmc_exit(void)  { +#ifdef CONFIG_SOC_AU1200 +	if (memid) +		au1xxx_ddma_del_device(memid); +#endif  	platform_driver_unregister(&au1xmmc_driver);  }  module_init(au1xmmc_init);  module_exit(au1xmmc_exit); -#ifdef MODULE  MODULE_AUTHOR("Advanced Micro Devices, Inc");  MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX");  MODULE_LICENSE("GPL");  MODULE_ALIAS("platform:au1xxx-mmc"); -#endif - diff --git a/drivers/mmc/host/au1xmmc.h b/drivers/mmc/host/au1xmmc.h deleted file mode 100644 index 341cbdf0bac..00000000000 --- a/drivers/mmc/host/au1xmmc.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef _AU1XMMC_H_ -#define _AU1XMMC_H_ - -/* Hardware definitions */ - -#define AU1XMMC_DESCRIPTOR_COUNT 1 -#define AU1XMMC_DESCRIPTOR_SIZE  2048 - -#define AU1XMMC_OCR ( MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30  | \ -		      MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33  | \ -		      MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36) - -/* Easy access macros */ - -#define HOST_STATUS(h)	((h)->iobase + SD_STATUS) -#define HOST_CONFIG(h)	((h)->iobase + SD_CONFIG) -#define HOST_ENABLE(h)	((h)->iobase + SD_ENABLE) -#define HOST_TXPORT(h)	((h)->iobase + SD_TXPORT) -#define HOST_RXPORT(h)	((h)->iobase + SD_RXPORT) -#define HOST_CMDARG(h)	((h)->iobase + SD_CMDARG) -#define HOST_BLKSIZE(h)	((h)->iobase + SD_BLKSIZE) -#define HOST_CMD(h)	((h)->iobase + SD_CMD) -#define HOST_CONFIG2(h)	((h)->iobase + SD_CONFIG2) -#define HOST_TIMEOUT(h)	((h)->iobase + SD_TIMEOUT) -#define HOST_DEBUG(h)	((h)->iobase + SD_DEBUG) - -#define DMA_CHANNEL(h) \ -	( ((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan) - -/* This gives us a hard value for the stop command that we can write directly - * to the command register - */ - -#define STOP_CMD (SD_CMD_RT_1B|SD_CMD_CT_7|(0xC << SD_CMD_CI_SHIFT)|SD_CMD_GO) - -/* This is the set of interrupts that we configure by default */ - -#if 0 -#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_DD | \ -		SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I) -#endif - -#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | \ -		SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I) -/* The poll event (looking for insert/remove events runs twice a second */ -#define AU1XMMC_DETECT_TIMEOUT (HZ/2) - -struct au1xmmc_host { -  struct mmc_host *mmc; -  struct mmc_request *mrq; - -  u32 id; - -  u32 flags; -  u32 iobase; -  u32 clock; -  u32 bus_width; -  u32 power_mode; - -  int status; - -   struct { -	   int len; -	   int dir; -  } dma; - -   struct { -	   int index; -	   int offset; -	   int len; -  } pio; - -  u32 tx_chan; -  u32 rx_chan; - -  struct timer_list timer; -  struct tasklet_struct finish_task; -  struct tasklet_struct data_task; - -  spinlock_t lock; -}; - -/* Status flags used by the host structure */ - -#define HOST_F_XMIT   0x0001 -#define HOST_F_RECV   0x0002 -#define HOST_F_DMA    0x0010 -#define HOST_F_ACTIVE 0x0100 -#define HOST_F_STOP   0x1000 - -#define HOST_S_IDLE   0x0001 -#define HOST_S_CMD    0x0002 -#define HOST_S_DATA   0x0003 -#define HOST_S_STOP   0x0004 - -#endif diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index eed211b2ac7..5e880c0f134 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -892,9 +892,12 @@ static int imxmci_get_ro(struct mmc_host *mmc)  	struct imxmci_host *host = mmc_priv(mmc);  	if (host->pdata && host->pdata->get_ro) -		return host->pdata->get_ro(mmc_dev(mmc)); -	/* Host doesn't support read only detection so assume writeable */ -	return 0; +		return !!host->pdata->get_ro(mmc_dev(mmc)); +	/* +	 * Board doesn't support read only detection; let the mmc core +	 * decide what to do. +	 */ +	return -ENOSYS;  } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 35508584ac2..41cc63360e4 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1126,16 +1126,28 @@ static int mmc_spi_get_ro(struct mmc_host *mmc)  	struct mmc_spi_host *host = mmc_priv(mmc);  	if (host->pdata && host->pdata->get_ro) -		return host->pdata->get_ro(mmc->parent); -	/* board doesn't support read only detection; assume writeable */ -	return 0; +		return !!host->pdata->get_ro(mmc->parent); +	/* +	 * Board doesn't support read only detection; let the mmc core +	 * decide what to do. +	 */ +	return -ENOSYS;  } +static int mmc_spi_get_cd(struct mmc_host *mmc) +{ +	struct mmc_spi_host *host = mmc_priv(mmc); + +	if (host->pdata && host->pdata->get_cd) +		return !!host->pdata->get_cd(mmc->parent); +	return -ENOSYS; +}  static const struct mmc_host_ops mmc_spi_ops = {  	.request	= mmc_spi_request,  	.set_ios	= mmc_spi_set_ios,  	.get_ro		= mmc_spi_get_ro, +	.get_cd		= mmc_spi_get_cd,  }; @@ -1240,10 +1252,7 @@ static int mmc_spi_probe(struct spi_device *spi)  	mmc->ops = &mmc_spi_ops;  	mmc->max_blk_size = MMC_SPI_BLOCKSIZE; -	/* As long as we keep track of the number of successfully -	 * transmitted blocks, we're good for multiwrite. -	 */ -	mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE; +	mmc->caps = MMC_CAP_SPI;  	/* SPI doesn't need the lowspeed device identification thing for  	 * MMC or SD cards, since it never comes up in open drain mode. @@ -1319,17 +1328,23 @@ static int mmc_spi_probe(struct spi_device *spi)  			goto fail_glue_init;  	} +	/* pass platform capabilities, if any */ +	if (host->pdata) +		mmc->caps |= host->pdata->caps; +  	status = mmc_add_host(mmc);  	if (status != 0)  		goto fail_add_host; -	dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n", +	dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n",  			mmc->class_dev.bus_id,  			host->dma_dev ? "" : ", no DMA",  			(host->pdata && host->pdata->get_ro)  				? "" : ", no WP",  			(host->pdata && host->pdata->setpower) -				? "" : ", no poweroff"); +				? "" : ", no poweroff", +			(mmc->caps & MMC_CAP_NEEDS_POLL) +				? ", cd polling" : "");  	return 0;  fail_add_host: diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index da5fecad74d..696cf3647ce 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -535,7 +535,6 @@ static int mmci_probe(struct amba_device *dev, void *id)  	mmc->f_min = (host->mclk + 511) / 512;  	mmc->f_max = min(host->mclk, fmax);  	mmc->ocr_avail = plat->ocr_mask; -	mmc->caps = MMC_CAP_MULTIWRITE;  	/*  	 * We can do SGIO diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 549517c3567..dbc26eb6a89 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1317,7 +1317,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)  	host->slots[id] = slot; -	mmc->caps = MMC_CAP_MULTIWRITE; +	mmc->caps = 0;  	if (host->pdata->conf.wire4)  		mmc->caps |= MMC_CAP_4_BIT_DATA; diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index d89475d3698..d39f5973886 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -374,9 +374,12 @@ static int pxamci_get_ro(struct mmc_host *mmc)  	struct pxamci_host *host = mmc_priv(mmc);  	if (host->pdata && host->pdata->get_ro) -		return host->pdata->get_ro(mmc_dev(mmc)); -	/* Host doesn't support read only detection so assume writeable */ -	return 0; +		return !!host->pdata->get_ro(mmc_dev(mmc)); +	/* +	 * Board doesn't support read only detection; let the mmc core +	 * decide what to do. +	 */ +	return -ENOSYS;  }  static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c new file mode 100644 index 00000000000..6a1e4994b72 --- /dev/null +++ b/drivers/mmc/host/s3cmci.c @@ -0,0 +1,1446 @@ +/* + *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver + * + *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/mmc/host.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <asm/dma.h> + +#include <asm/arch/regs-sdi.h> +#include <asm/arch/regs-gpio.h> + +#include <asm/plat-s3c24xx/mci.h> + +#include "s3cmci.h" + +#define DRIVER_NAME "s3c-mci" + +enum dbg_channels { +	dbg_err   = (1 << 0), +	dbg_debug = (1 << 1), +	dbg_info  = (1 << 2), +	dbg_irq   = (1 << 3), +	dbg_sg    = (1 << 4), +	dbg_dma   = (1 << 5), +	dbg_pio   = (1 << 6), +	dbg_fail  = (1 << 7), +	dbg_conf  = (1 << 8), +}; + +static const int dbgmap_err   = dbg_err | dbg_fail; +static const int dbgmap_info  = dbg_info | dbg_conf; +static const int dbgmap_debug = dbg_debug; + +#define dbg(host, channels, args...)		  \ +	do {					  \ +	if (dbgmap_err & channels) 		  \ +		dev_err(&host->pdev->dev, args);  \ +	else if (dbgmap_info & channels)	  \ +		dev_info(&host->pdev->dev, args); \ +	else if (dbgmap_debug & channels)	  \ +		dev_dbg(&host->pdev->dev, args);  \ +	} while (0) + +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) + +static struct s3c2410_dma_client s3cmci_dma_client = { +	.name		= "s3c-mci", +}; + +static void finalize_request(struct s3cmci_host *host); +static void s3cmci_send_request(struct mmc_host *mmc); +static void s3cmci_reset(struct s3cmci_host *host); + +#ifdef CONFIG_MMC_DEBUG + +static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) +{ +	u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize; +	u32 datcon, datcnt, datsta, fsta, imask; + +	con 	= readl(host->base + S3C2410_SDICON); +	pre 	= readl(host->base + S3C2410_SDIPRE); +	cmdarg 	= readl(host->base + S3C2410_SDICMDARG); +	cmdcon 	= readl(host->base + S3C2410_SDICMDCON); +	cmdsta 	= readl(host->base + S3C2410_SDICMDSTAT); +	r0 	= readl(host->base + S3C2410_SDIRSP0); +	r1 	= readl(host->base + S3C2410_SDIRSP1); +	r2 	= readl(host->base + S3C2410_SDIRSP2); +	r3 	= readl(host->base + S3C2410_SDIRSP3); +	timer 	= readl(host->base + S3C2410_SDITIMER); +	bsize 	= readl(host->base + S3C2410_SDIBSIZE); +	datcon 	= readl(host->base + S3C2410_SDIDCON); +	datcnt 	= readl(host->base + S3C2410_SDIDCNT); +	datsta 	= readl(host->base + S3C2410_SDIDSTA); +	fsta 	= readl(host->base + S3C2410_SDIFSTA); +	imask   = readl(host->base + host->sdiimsk); + +	dbg(host, dbg_debug, "%s  CON:[%08x]  PRE:[%08x]  TMR:[%08x]\n", +				prefix, con, pre, timer); + +	dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n", +				prefix, cmdcon, cmdarg, cmdsta); + +	dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]" +			       " DSTA:[%08x] DCNT:[%08x]\n", +				prefix, datcon, fsta, datsta, datcnt); + +	dbg(host, dbg_debug, "%s   R0:[%08x]   R1:[%08x]" +			       "   R2:[%08x]   R3:[%08x]\n", +				prefix, r0, r1, r2, r3); +} + +static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd, +			   int stop) +{ +	snprintf(host->dbgmsg_cmd, 300, +		 "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u", +		 host->ccnt, (stop ? " (STOP)" : ""), +		 cmd->opcode, cmd->arg, cmd->flags, cmd->retries); + +	if (cmd->data) { +		snprintf(host->dbgmsg_dat, 300, +			 "#%u bsize:%u blocks:%u bytes:%u", +			 host->dcnt, cmd->data->blksz, +			 cmd->data->blocks, +			 cmd->data->blocks * cmd->data->blksz); +	} else { +		host->dbgmsg_dat[0] = '\0'; +	} +} + +static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd, +			int fail) +{ +	unsigned int dbglvl = fail ? dbg_fail : dbg_debug; + +	if (!cmd) +		return; + +	if (cmd->error == 0) { +		dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n", +			host->dbgmsg_cmd, cmd->resp[0]); +	} else { +		dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n", +			cmd->error, host->dbgmsg_cmd, host->status); +	} + +	if (!cmd->data) +		return; + +	if (cmd->data->error == 0) { +		dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat); +	} else { +		dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n", +			cmd->data->error, host->dbgmsg_dat, +			readl(host->base + S3C2410_SDIDCNT)); +	} +} +#else +static void dbg_dumpcmd(struct s3cmci_host *host, +			struct mmc_command *cmd, int fail) { } + +static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd, +			   int stop) { } + +static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) { } + +#endif /* CONFIG_MMC_DEBUG */ + +static inline u32 enable_imask(struct s3cmci_host *host, u32 imask) +{ +	u32 newmask; + +	newmask = readl(host->base + host->sdiimsk); +	newmask |= imask; + +	writel(newmask, host->base + host->sdiimsk); + +	return newmask; +} + +static inline u32 disable_imask(struct s3cmci_host *host, u32 imask) +{ +	u32 newmask; + +	newmask = readl(host->base + host->sdiimsk); +	newmask &= ~imask; + +	writel(newmask, host->base + host->sdiimsk); + +	return newmask; +} + +static inline void clear_imask(struct s3cmci_host *host) +{ +	writel(0, host->base + host->sdiimsk); +} + +static inline int get_data_buffer(struct s3cmci_host *host, +				  u32 *words, u32 **pointer) +{ +	struct scatterlist *sg; + +	if (host->pio_active == XFER_NONE) +		return -EINVAL; + +	if ((!host->mrq) || (!host->mrq->data)) +		return -EINVAL; + +	if (host->pio_sgptr >= host->mrq->data->sg_len) { +		dbg(host, dbg_debug, "no more buffers (%i/%i)\n", +		      host->pio_sgptr, host->mrq->data->sg_len); +		return -EBUSY; +	} +	sg = &host->mrq->data->sg[host->pio_sgptr]; + +	*words = sg->length >> 2; +	*pointer = sg_virt(sg); + +	host->pio_sgptr++; + +	dbg(host, dbg_sg, "new buffer (%i/%i)\n", +	    host->pio_sgptr, host->mrq->data->sg_len); + +	return 0; +} + +static inline u32 fifo_count(struct s3cmci_host *host) +{ +	u32 fifostat = readl(host->base + S3C2410_SDIFSTA); + +	fifostat &= S3C2410_SDIFSTA_COUNTMASK; +	return fifostat >> 2; +} + +static inline u32 fifo_free(struct s3cmci_host *host) +{ +	u32 fifostat = readl(host->base + S3C2410_SDIFSTA); + +	fifostat &= S3C2410_SDIFSTA_COUNTMASK; +	return (63 - fifostat) >> 2; +} + +static void do_pio_read(struct s3cmci_host *host) +{ +	int res; +	u32 fifo; +	void __iomem *from_ptr; + +	/* write real prescaler to host, it might be set slow to fix */ +	writel(host->prescaler, host->base + S3C2410_SDIPRE); + +	from_ptr = host->base + host->sdidata; + +	while ((fifo = fifo_count(host))) { +		if (!host->pio_words) { +			res = get_data_buffer(host, &host->pio_words, +					      &host->pio_ptr); +			if (res) { +				host->pio_active = XFER_NONE; +				host->complete_what = COMPLETION_FINALIZE; + +				dbg(host, dbg_pio, "pio_read(): " +				    "complete (no more data).\n"); +				return; +			} + +			dbg(host, dbg_pio, +			    "pio_read(): new target: [%i]@[%p]\n", +			    host->pio_words, host->pio_ptr); +		} + +		dbg(host, dbg_pio, +		    "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", +		    fifo, host->pio_words, +		    readl(host->base + S3C2410_SDIDCNT)); + +		if (fifo > host->pio_words) +			fifo = host->pio_words; + +		host->pio_words -= fifo; +		host->pio_count += fifo; + +		while (fifo--) +			*(host->pio_ptr++) = readl(from_ptr); +	} + +	if (!host->pio_words) { +		res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); +		if (res) { +			dbg(host, dbg_pio, +			    "pio_read(): complete (no more buffers).\n"); +			host->pio_active = XFER_NONE; +			host->complete_what = COMPLETION_FINALIZE; + +			return; +		} +	} + +	enable_imask(host, +		     S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST); +} + +static void do_pio_write(struct s3cmci_host *host) +{ +	void __iomem *to_ptr; +	int res; +	u32 fifo; + +	to_ptr = host->base + host->sdidata; + +	while ((fifo = fifo_free(host))) { +		if (!host->pio_words) { +			res = get_data_buffer(host, &host->pio_words, +							&host->pio_ptr); +			if (res) { +				dbg(host, dbg_pio, +				    "pio_write(): complete (no more data).\n"); +				host->pio_active = XFER_NONE; + +				return; +			} + +			dbg(host, dbg_pio, +			    "pio_write(): new source: [%i]@[%p]\n", +			    host->pio_words, host->pio_ptr); + +		} + +		if (fifo > host->pio_words) +			fifo = host->pio_words; + +		host->pio_words -= fifo; +		host->pio_count += fifo; + +		while (fifo--) +			writel(*(host->pio_ptr++), to_ptr); +	} + +	enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); +} + +static void pio_tasklet(unsigned long data) +{ +	struct s3cmci_host *host = (struct s3cmci_host *) data; + + +	disable_irq(host->irq); + +	if (host->pio_active == XFER_WRITE) +		do_pio_write(host); + +	if (host->pio_active == XFER_READ) +		do_pio_read(host); + +	if (host->complete_what == COMPLETION_FINALIZE) { +		clear_imask(host); +		if (host->pio_active != XFER_NONE) { +			dbg(host, dbg_err, "unfinished %s " +			    "- pio_count:[%u] pio_words:[%u]\n", +			    (host->pio_active == XFER_READ) ? "read" : "write", +			    host->pio_count, host->pio_words); + +			if (host->mrq->data) +				host->mrq->data->error = -EINVAL; +		} + +		finalize_request(host); +	} else +		enable_irq(host->irq); +} + +/* + * ISR for SDI Interface IRQ + * Communication between driver and ISR works as follows: + *   host->mrq 			points to current request + *   host->complete_what	Indicates when the request is considered done + *     COMPLETION_CMDSENT	  when the command was sent + *     COMPLETION_RSPFIN          when a response was received + *     COMPLETION_XFERFINISH	  when the data transfer is finished + *     COMPLETION_XFERFINISH_RSPFIN both of the above. + *   host->complete_request	is the completion-object the driver waits for + * + * 1) Driver sets up host->mrq and host->complete_what + * 2) Driver prepares the transfer + * 3) Driver enables interrupts + * 4) Driver starts transfer + * 5) Driver waits for host->complete_rquest + * 6) ISR checks for request status (errors and success) + * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error + * 7) ISR completes host->complete_request + * 8) ISR disables interrupts + * 9) Driver wakes up and takes care of the request + * + * Note: "->error"-fields are expected to be set to 0 before the request + *       was issued by mmc.c - therefore they are only set, when an error + *       contition comes up + */ + +static irqreturn_t s3cmci_irq(int irq, void *dev_id) +{ +	struct s3cmci_host *host = dev_id; +	struct mmc_command *cmd; +	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk; +	u32 mci_cclear, mci_dclear; +	unsigned long iflags; + +	spin_lock_irqsave(&host->complete_lock, iflags); + +	mci_csta = readl(host->base + S3C2410_SDICMDSTAT); +	mci_dsta = readl(host->base + S3C2410_SDIDSTA); +	mci_dcnt = readl(host->base + S3C2410_SDIDCNT); +	mci_fsta = readl(host->base + S3C2410_SDIFSTA); +	mci_imsk = readl(host->base + host->sdiimsk); +	mci_cclear = 0; +	mci_dclear = 0; + +	if ((host->complete_what == COMPLETION_NONE) || +	    (host->complete_what == COMPLETION_FINALIZE)) { +		host->status = "nothing to complete"; +		clear_imask(host); +		goto irq_out; +	} + +	if (!host->mrq) { +		host->status = "no active mrq"; +		clear_imask(host); +		goto irq_out; +	} + +	cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd; + +	if (!cmd) { +		host->status = "no active cmd"; +		clear_imask(host); +		goto irq_out; +	} + +	if (!host->dodma) { +		if ((host->pio_active == XFER_WRITE) && +		    (mci_fsta & S3C2410_SDIFSTA_TFDET)) { + +			disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); +			tasklet_schedule(&host->pio_tasklet); +			host->status = "pio tx"; +		} + +		if ((host->pio_active == XFER_READ) && +		    (mci_fsta & S3C2410_SDIFSTA_RFDET)) { + +			disable_imask(host, +				      S3C2410_SDIIMSK_RXFIFOHALF | +				      S3C2410_SDIIMSK_RXFIFOLAST); + +			tasklet_schedule(&host->pio_tasklet); +			host->status = "pio rx"; +		} +	} + +	if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) { +		dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n"); +		cmd->error = -ETIMEDOUT; +		host->status = "error: command timeout"; +		goto fail_transfer; +	} + +	if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) { +		if (host->complete_what == COMPLETION_CMDSENT) { +			host->status = "ok: command sent"; +			goto close_transfer; +		} + +		mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT; +	} + +	if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) { +		if (cmd->flags & MMC_RSP_CRC) { +			if (host->mrq->cmd->flags & MMC_RSP_136) { +				dbg(host, dbg_irq, +				    "fixup: ignore CRC fail with long rsp\n"); +			} else { +				/* note, we used to fail the transfer +				 * here, but it seems that this is just +				 * the hardware getting it wrong. +				 * +				 * cmd->error = -EILSEQ; +				 * host->status = "error: bad command crc"; +				 * goto fail_transfer; +				*/ +			} +		} + +		mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL; +	} + +	if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) { +		if (host->complete_what == COMPLETION_RSPFIN) { +			host->status = "ok: command response received"; +			goto close_transfer; +		} + +		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) +			host->complete_what = COMPLETION_XFERFINISH; + +		mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN; +	} + +	/* errors handled after this point are only relevant +	   when a data transfer is in progress */ + +	if (!cmd->data) +		goto clear_status_bits; + +	/* Check for FIFO failure */ +	if (host->is2440) { +		if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) { +			dbg(host, dbg_err, "FIFO failure\n"); +			host->mrq->data->error = -EILSEQ; +			host->status = "error: 2440 fifo failure"; +			goto fail_transfer; +		} +	} else { +		if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) { +			dbg(host, dbg_err, "FIFO failure\n"); +			cmd->data->error = -EILSEQ; +			host->status = "error:  fifo failure"; +			goto fail_transfer; +		} +	} + +	if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) { +		dbg(host, dbg_err, "bad data crc (outgoing)\n"); +		cmd->data->error = -EILSEQ; +		host->status = "error: bad data crc (outgoing)"; +		goto fail_transfer; +	} + +	if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) { +		dbg(host, dbg_err, "bad data crc (incoming)\n"); +		cmd->data->error = -EILSEQ; +		host->status = "error: bad data crc (incoming)"; +		goto fail_transfer; +	} + +	if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) { +		dbg(host, dbg_err, "data timeout\n"); +		cmd->data->error = -ETIMEDOUT; +		host->status = "error: data timeout"; +		goto fail_transfer; +	} + +	if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) { +		if (host->complete_what == COMPLETION_XFERFINISH) { +			host->status = "ok: data transfer completed"; +			goto close_transfer; +		} + +		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) +			host->complete_what = COMPLETION_RSPFIN; + +		mci_dclear |= S3C2410_SDIDSTA_XFERFINISH; +	} + +clear_status_bits: +	writel(mci_cclear, host->base + S3C2410_SDICMDSTAT); +	writel(mci_dclear, host->base + S3C2410_SDIDSTA); + +	goto irq_out; + +fail_transfer: +	host->pio_active = XFER_NONE; + +close_transfer: +	host->complete_what = COMPLETION_FINALIZE; + +	clear_imask(host); +	tasklet_schedule(&host->pio_tasklet); + +	goto irq_out; + +irq_out: +	dbg(host, dbg_irq, +	    "csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n", +	    mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status); + +	spin_unlock_irqrestore(&host->complete_lock, iflags); +	return IRQ_HANDLED; + +} + +/* + * ISR for the CardDetect Pin +*/ + +static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) +{ +	struct s3cmci_host *host = (struct s3cmci_host *)dev_id; + +	dbg(host, dbg_irq, "card detect\n"); + +	mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + +	return IRQ_HANDLED; +} + +void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id, +			      int size, enum s3c2410_dma_buffresult result) +{ +	struct s3cmci_host *host = buf_id; +	unsigned long iflags; +	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt; + +	mci_csta = readl(host->base + S3C2410_SDICMDSTAT); +	mci_dsta = readl(host->base + S3C2410_SDIDSTA); +	mci_fsta = readl(host->base + S3C2410_SDIFSTA); +	mci_dcnt = readl(host->base + S3C2410_SDIDCNT); + +	BUG_ON(!host->mrq); +	BUG_ON(!host->mrq->data); +	BUG_ON(!host->dmatogo); + +	spin_lock_irqsave(&host->complete_lock, iflags); + +	if (result != S3C2410_RES_OK) { +		dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x " +			"fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n", +			mci_csta, mci_dsta, mci_fsta, +			mci_dcnt, result, host->dmatogo); + +		goto fail_request; +	} + +	host->dmatogo--; +	if (host->dmatogo) { +		dbg(host, dbg_dma, "DMA DONE  Size:%i DSTA:[%08x] " +			"DCNT:[%08x] toGo:%u\n", +			size, mci_dsta, mci_dcnt, host->dmatogo); + +		goto out; +	} + +	dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n", +		size, mci_dsta, mci_dcnt); + +	host->complete_what = COMPLETION_FINALIZE; + +out: +	tasklet_schedule(&host->pio_tasklet); +	spin_unlock_irqrestore(&host->complete_lock, iflags); +	return; + +fail_request: +	host->mrq->data->error = -EINVAL; +	host->complete_what = COMPLETION_FINALIZE; +	writel(0, host->base + host->sdiimsk); +	goto out; + +} + +static void finalize_request(struct s3cmci_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; +	int debug_as_failure = 0; + +	if (host->complete_what != COMPLETION_FINALIZE) +		return; + +	if (!mrq) +		return; + +	if (cmd->data && (cmd->error == 0) && +	    (cmd->data->error == 0)) { +		if (host->dodma && (!host->dma_complete)) { +			dbg(host, dbg_dma, "DMA Missing!\n"); +			return; +		} +	} + +	/* Read response from controller. */ +	cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0); +	cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1); +	cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2); +	cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3); + +	writel(host->prescaler, host->base + S3C2410_SDIPRE); + +	if (cmd->error) +		debug_as_failure = 1; + +	if (cmd->data && cmd->data->error) +		debug_as_failure = 1; + +	dbg_dumpcmd(host, cmd, debug_as_failure); + +	/* Cleanup controller */ +	writel(0, host->base + S3C2410_SDICMDARG); +	writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); +	writel(0, host->base + S3C2410_SDICMDCON); +	writel(0, host->base + host->sdiimsk); + +	if (cmd->data && cmd->error) +		cmd->data->error = cmd->error; + +	if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) { +		host->cmd_is_stop = 1; +		s3cmci_send_request(host->mmc); +		return; +	} + +	/* If we have no data transfer we are finished here */ +	if (!mrq->data) +		goto request_done; + +	/* Calulate the amout of bytes transfer if there was no error */ +	if (mrq->data->error == 0) { +		mrq->data->bytes_xfered = +			(mrq->data->blocks * mrq->data->blksz); +	} else { +		mrq->data->bytes_xfered = 0; +	} + +	/* If we had an error while transfering data we flush the +	 * DMA channel and the fifo to clear out any garbage. */ +	if (mrq->data->error != 0) { +		if (host->dodma) +			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); + +		if (host->is2440) { +			/* Clear failure register and reset fifo. */ +			writel(S3C2440_SDIFSTA_FIFORESET | +			       S3C2440_SDIFSTA_FIFOFAIL, +			       host->base + S3C2410_SDIFSTA); +		} else { +			u32 mci_con; + +			/* reset fifo */ +			mci_con = readl(host->base + S3C2410_SDICON); +			mci_con |= S3C2410_SDICON_FIFORESET; + +			writel(mci_con, host->base + S3C2410_SDICON); +		} +	} + +request_done: +	host->complete_what = COMPLETION_NONE; +	host->mrq = NULL; +	mmc_request_done(host->mmc, mrq); +} + + +void s3cmci_dma_setup(struct s3cmci_host *host, enum s3c2410_dmasrc source) +{ +	static enum s3c2410_dmasrc last_source = -1; +	static int setup_ok; + +	if (last_source == source) +		return; + +	last_source = source; + +	s3c2410_dma_devconfig(host->dma, source, 3, +			      host->mem->start + host->sdidata); + +	if (!setup_ok) { +		s3c2410_dma_config(host->dma, 4, +			(S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI)); +		s3c2410_dma_set_buffdone_fn(host->dma, +					    s3cmci_dma_done_callback); +		s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART); +		setup_ok = 1; +	} +} + +static void s3cmci_send_command(struct s3cmci_host *host, +					struct mmc_command *cmd) +{ +	u32 ccon, imsk; + +	imsk  = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT | +		S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT | +		S3C2410_SDIIMSK_RESPONSECRC; + +	enable_imask(host, imsk); + +	if (cmd->data) +		host->complete_what = COMPLETION_XFERFINISH_RSPFIN; +	else if (cmd->flags & MMC_RSP_PRESENT) +		host->complete_what = COMPLETION_RSPFIN; +	else +		host->complete_what = COMPLETION_CMDSENT; + +	writel(cmd->arg, host->base + S3C2410_SDICMDARG); + +	ccon  = cmd->opcode & S3C2410_SDICMDCON_INDEX; +	ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; + +	if (cmd->flags & MMC_RSP_PRESENT) +		ccon |= S3C2410_SDICMDCON_WAITRSP; + +	if (cmd->flags & MMC_RSP_136) +		ccon |= S3C2410_SDICMDCON_LONGRSP; + +	writel(ccon, host->base + S3C2410_SDICMDCON); +} + +static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) +{ +	u32 dcon, imsk, stoptries = 3; + +	/* write DCON register */ + +	if (!data) { +		writel(0, host->base + S3C2410_SDIDCON); +		return 0; +	} + +	if ((data->blksz & 3) != 0) { +		/* We cannot deal with unaligned blocks with more than +		 * one block being transfered. */ + +		if (data->blocks > 1) +			return -EINVAL; + +		/* No support yet for non-word block transfers. */ +		return -EINVAL; +	} + +	while (readl(host->base + S3C2410_SDIDSTA) & +	       (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { + +		dbg(host, dbg_err, +		    "mci_setup_data() transfer stillin progress.\n"); + +		writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); +		s3cmci_reset(host); + +		if ((stoptries--) == 0) { +			dbg_dumpregs(host, "DRF"); +			return -EINVAL; +		} +	} + +	dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; + +	if (host->dodma) +		dcon |= S3C2410_SDIDCON_DMAEN; + +	if (host->bus_width == MMC_BUS_WIDTH_4) +		dcon |= S3C2410_SDIDCON_WIDEBUS; + +	if (!(data->flags & MMC_DATA_STREAM)) +		dcon |= S3C2410_SDIDCON_BLOCKMODE; + +	if (data->flags & MMC_DATA_WRITE) { +		dcon |= S3C2410_SDIDCON_TXAFTERRESP; +		dcon |= S3C2410_SDIDCON_XFER_TXSTART; +	} + +	if (data->flags & MMC_DATA_READ) { +		dcon |= S3C2410_SDIDCON_RXAFTERCMD; +		dcon |= S3C2410_SDIDCON_XFER_RXSTART; +	} + +	if (host->is2440) { +		dcon |= S3C2440_SDIDCON_DS_WORD; +		dcon |= S3C2440_SDIDCON_DATSTART; +	} + +	writel(dcon, host->base + S3C2410_SDIDCON); + +	/* write BSIZE register */ + +	writel(data->blksz, host->base + S3C2410_SDIBSIZE); + +	/* add to IMASK register */ +	imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | +	       S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; + +	enable_imask(host, imsk); + +	/* write TIMER register */ + +	if (host->is2440) { +		writel(0x007FFFFF, host->base + S3C2410_SDITIMER); +	} else { +		writel(0x0000FFFF, host->base + S3C2410_SDITIMER); + +		/* FIX: set slow clock to prevent timeouts on read */ +		if (data->flags & MMC_DATA_READ) +			writel(0xFF, host->base + S3C2410_SDIPRE); +	} + +	return 0; +} + +#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ) + +static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) +{ +	int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; + +	BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); + +	host->pio_sgptr = 0; +	host->pio_words = 0; +	host->pio_count = 0; +	host->pio_active = rw ? XFER_WRITE : XFER_READ; + +	if (rw) { +		do_pio_write(host); +		enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); +	} else { +		enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF +			     | S3C2410_SDIIMSK_RXFIFOLAST); +	} + +	return 0; +} + +static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) +{ +	int dma_len, i; +	int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; + +	BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); + +	s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW); +	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); + +	dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, +			     (rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + +	if (dma_len == 0) +		return -ENOMEM; + +	host->dma_complete = 0; +	host->dmatogo = dma_len; + +	for (i = 0; i < dma_len; i++) { +		int res; + +		dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i, +			sg_dma_address(&data->sg[i]), +			sg_dma_len(&data->sg[i])); + +		res = s3c2410_dma_enqueue(host->dma, (void *) host, +					  sg_dma_address(&data->sg[i]), +					  sg_dma_len(&data->sg[i])); + +		if (res) { +			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); +			return -EBUSY; +		} +	} + +	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START); + +	return 0; +} + +static void s3cmci_send_request(struct mmc_host *mmc) +{ +	struct s3cmci_host *host = mmc_priv(mmc); +	struct mmc_request *mrq = host->mrq; +	struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + +	host->ccnt++; +	prepare_dbgmsg(host, cmd, host->cmd_is_stop); + +	/* Clear command, data and fifo status registers +	   Fifo clear only necessary on 2440, but doesn't hurt on 2410 +	*/ +	writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT); +	writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA); +	writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA); + +	if (cmd->data) { +		int res = s3cmci_setup_data(host, cmd->data); + +		host->dcnt++; + +		if (res) { +			dbg(host, dbg_err, "setup data error %d\n", res); +			cmd->error = res; +			cmd->data->error = res; + +			mmc_request_done(mmc, mrq); +			return; +		} + +		if (host->dodma) +			res = s3cmci_prepare_dma(host, cmd->data); +		else +			res = s3cmci_prepare_pio(host, cmd->data); + +		if (res) { +			dbg(host, dbg_err, "data prepare error %d\n", res); +			cmd->error = res; +			cmd->data->error = res; + +			mmc_request_done(mmc, mrq); +			return; +		} +	} + +	/* Send command */ +	s3cmci_send_command(host, cmd); + +	/* Enable Interrupt */ +	enable_irq(host->irq); +} + +static int s3cmci_card_present(struct s3cmci_host *host) +{ +	struct s3c24xx_mci_pdata *pdata = host->pdata; +	int ret; + +	if (pdata->gpio_detect == 0) +		return -ENOSYS; + +	ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1; +	return ret ^ pdata->detect_invert; +} + +static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct s3cmci_host *host = mmc_priv(mmc); + +	host->status = "mmc request"; +	host->cmd_is_stop = 0; +	host->mrq = mrq; + +	if (s3cmci_card_present(host) == 0) { +		dbg(host, dbg_err, "%s: no medium present\n", __func__); +		host->mrq->cmd->error = -ENOMEDIUM; +		mmc_request_done(mmc, mrq); +	} else +		s3cmci_send_request(mmc); +} + +static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct s3cmci_host *host = mmc_priv(mmc); +	u32 mci_psc, mci_con; + +	/* Set the power state */ + +	mci_con = readl(host->base + S3C2410_SDICON); + +	switch (ios->power_mode) { +	case MMC_POWER_ON: +	case MMC_POWER_UP: +		s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK); +		s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD); +		s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0); +		s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1); +		s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2); +		s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3); + +		if (host->pdata->set_power) +			host->pdata->set_power(ios->power_mode, ios->vdd); + +		if (!host->is2440) +			mci_con |= S3C2410_SDICON_FIFORESET; + +		break; + +	case MMC_POWER_OFF: +	default: +		s3c2410_gpio_setpin(S3C2410_GPE5, 0); +		s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP); + +		if (host->is2440) +			mci_con |= S3C2440_SDICON_SDRESET; + +		if (host->pdata->set_power) +			host->pdata->set_power(ios->power_mode, ios->vdd); + +		break; +	} + +	/* Set clock */ +	for (mci_psc = 0; mci_psc < 255; mci_psc++) { +		host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1)); + +		if (host->real_rate <= ios->clock) +			break; +	} + +	if (mci_psc > 255) +		mci_psc = 255; + +	host->prescaler = mci_psc; +	writel(host->prescaler, host->base + S3C2410_SDIPRE); + +	/* If requested clock is 0, real_rate will be 0, too */ +	if (ios->clock == 0) +		host->real_rate = 0; + +	/* Set CLOCK_ENABLE */ +	if (ios->clock) +		mci_con |= S3C2410_SDICON_CLOCKTYPE; +	else +		mci_con &= ~S3C2410_SDICON_CLOCKTYPE; + +	writel(mci_con, host->base + S3C2410_SDICON); + +	if ((ios->power_mode == MMC_POWER_ON) || +	    (ios->power_mode == MMC_POWER_UP)) { +		dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n", +			host->real_rate/1000, ios->clock/1000); +	} else { +		dbg(host, dbg_conf, "powered down.\n"); +	} + +	host->bus_width = ios->bus_width; +} + +static void s3cmci_reset(struct s3cmci_host *host) +{ +	u32 con = readl(host->base + S3C2410_SDICON); + +	con |= S3C2440_SDICON_SDRESET; +	writel(con, host->base + S3C2410_SDICON); +} + +static int s3cmci_get_ro(struct mmc_host *mmc) +{ +	struct s3cmci_host *host = mmc_priv(mmc); +	struct s3c24xx_mci_pdata *pdata = host->pdata; +	int ret; + +	if (pdata->gpio_wprotect == 0) +		return 0; + +	ret = s3c2410_gpio_getpin(pdata->gpio_wprotect); + +	if (pdata->wprotect_invert) +		ret = !ret; + +	return ret; +} + +static struct mmc_host_ops s3cmci_ops = { +	.request	= s3cmci_request, +	.set_ios	= s3cmci_set_ios, +	.get_ro		= s3cmci_get_ro, +}; + +static struct s3c24xx_mci_pdata s3cmci_def_pdata = { +	/* This is currently here to avoid a number of if (host->pdata) +	 * checks. Any zero fields to ensure reaonable defaults are picked. */ +}; + +static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) +{ +	struct s3cmci_host *host; +	struct mmc_host	*mmc; +	int ret; + +	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); +	if (!mmc) { +		ret = -ENOMEM; +		goto probe_out; +	} + +	host = mmc_priv(mmc); +	host->mmc 	= mmc; +	host->pdev	= pdev; +	host->is2440	= is2440; + +	host->pdata = pdev->dev.platform_data; +	if (!host->pdata) { +		pdev->dev.platform_data = &s3cmci_def_pdata; +		host->pdata = &s3cmci_def_pdata; +	} + +	spin_lock_init(&host->complete_lock); +	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host); + +	if (is2440) { +		host->sdiimsk	= S3C2440_SDIIMSK; +		host->sdidata	= S3C2440_SDIDATA; +		host->clk_div	= 1; +	} else { +		host->sdiimsk	= S3C2410_SDIIMSK; +		host->sdidata	= S3C2410_SDIDATA; +		host->clk_div	= 2; +	} + +	host->dodma		= 0; +	host->complete_what 	= COMPLETION_NONE; +	host->pio_active 	= XFER_NONE; + +	host->dma		= S3CMCI_DMA; + +	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!host->mem) { +		dev_err(&pdev->dev, +			"failed to get io memory region resouce.\n"); + +		ret = -ENOENT; +		goto probe_free_host; +	} + +	host->mem = request_mem_region(host->mem->start, +				       RESSIZE(host->mem), pdev->name); + +	if (!host->mem) { +		dev_err(&pdev->dev, "failed to request io memory region.\n"); +		ret = -ENOENT; +		goto probe_free_host; +	} + +	host->base = ioremap(host->mem->start, RESSIZE(host->mem)); +	if (host->base == 0) { +		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); +		ret = -EINVAL; +		goto probe_free_mem_region; +	} + +	host->irq = platform_get_irq(pdev, 0); +	if (host->irq == 0) { +		dev_err(&pdev->dev, "failed to get interrupt resouce.\n"); +		ret = -EINVAL; +		goto probe_iounmap; +	} + +	if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) { +		dev_err(&pdev->dev, "failed to request mci interrupt.\n"); +		ret = -ENOENT; +		goto probe_iounmap; +	} + +	/* We get spurious interrupts even when we have set the IMSK +	 * register to ignore everything, so use disable_irq() to make +	 * ensure we don't lock the system with un-serviceable requests. */ + +	disable_irq(host->irq); + +	host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect); + +	if (host->irq_cd >= 0) { +		if (request_irq(host->irq_cd, s3cmci_irq_cd, +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +				DRIVER_NAME, host)) { +			dev_err(&pdev->dev, "can't get card detect irq.\n"); +			ret = -ENOENT; +			goto probe_free_irq; +		} +	} else { +		dev_warn(&pdev->dev, "host detect has no irq available\n"); +		s3c2410_gpio_cfgpin(host->pdata->gpio_detect, +				    S3C2410_GPIO_INPUT); +	} + +	if (host->pdata->gpio_wprotect) +		s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect, +				    S3C2410_GPIO_INPUT); + +	if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) { +		dev_err(&pdev->dev, "unable to get DMA channel.\n"); +		ret = -EBUSY; +		goto probe_free_irq_cd; +	} + +	host->clk = clk_get(&pdev->dev, "sdi"); +	if (IS_ERR(host->clk)) { +		dev_err(&pdev->dev, "failed to find clock source.\n"); +		ret = PTR_ERR(host->clk); +		host->clk = NULL; +		goto probe_free_host; +	} + +	ret = clk_enable(host->clk); +	if (ret) { +		dev_err(&pdev->dev, "failed to enable clock source.\n"); +		goto clk_free; +	} + +	host->clk_rate = clk_get_rate(host->clk); + +	mmc->ops 	= &s3cmci_ops; +	mmc->ocr_avail	= MMC_VDD_32_33 | MMC_VDD_33_34; +	mmc->caps	= MMC_CAP_4_BIT_DATA; +	mmc->f_min 	= host->clk_rate / (host->clk_div * 256); +	mmc->f_max 	= host->clk_rate / host->clk_div; + +	if (host->pdata->ocr_avail) +		mmc->ocr_avail = host->pdata->ocr_avail; + +	mmc->max_blk_count	= 4095; +	mmc->max_blk_size	= 4095; +	mmc->max_req_size	= 4095 * 512; +	mmc->max_seg_size	= mmc->max_req_size; + +	mmc->max_phys_segs	= 128; +	mmc->max_hw_segs	= 128; + +	dbg(host, dbg_debug, +	    "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n", +	    (host->is2440?"2440":""), +	    host->base, host->irq, host->irq_cd, host->dma); + +	ret = mmc_add_host(mmc); +	if (ret) { +		dev_err(&pdev->dev, "failed to add mmc host.\n"); +		goto free_dmabuf; +	} + +	platform_set_drvdata(pdev, mmc); +	dev_info(&pdev->dev, "initialisation done.\n"); + +	return 0; + + free_dmabuf: +	clk_disable(host->clk); + + clk_free: +	clk_put(host->clk); + + probe_free_irq_cd: +	if (host->irq_cd >= 0) +		free_irq(host->irq_cd, host); + + probe_free_irq: +	free_irq(host->irq, host); + + probe_iounmap: +	iounmap(host->base); + + probe_free_mem_region: +	release_mem_region(host->mem->start, RESSIZE(host->mem)); + + probe_free_host: +	mmc_free_host(mmc); + probe_out: +	return ret; +} + +static int __devexit s3cmci_remove(struct platform_device *pdev) +{ +	struct mmc_host		*mmc  = platform_get_drvdata(pdev); +	struct s3cmci_host	*host = mmc_priv(mmc); + +	mmc_remove_host(mmc); + +	clk_disable(host->clk); +	clk_put(host->clk); + +	tasklet_disable(&host->pio_tasklet); +	s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client); + +	if (host->irq_cd >= 0) +		free_irq(host->irq_cd, host); +	free_irq(host->irq, host); + +	iounmap(host->base); +	release_mem_region(host->mem->start, RESSIZE(host->mem)); + +	mmc_free_host(mmc); +	return 0; +} + +static int __devinit s3cmci_probe_2410(struct platform_device *dev) +{ +	return s3cmci_probe(dev, 0); +} + +static int __devinit s3cmci_probe_2412(struct platform_device *dev) +{ +	return s3cmci_probe(dev, 1); +} + +static int __devinit s3cmci_probe_2440(struct platform_device *dev) +{ +	return s3cmci_probe(dev, 1); +} + +#ifdef CONFIG_PM + +static int s3cmci_suspend(struct platform_device *dev, pm_message_t state) +{ +	struct mmc_host *mmc = platform_get_drvdata(dev); + +	return  mmc_suspend_host(mmc, state); +} + +static int s3cmci_resume(struct platform_device *dev) +{ +	struct mmc_host *mmc = platform_get_drvdata(dev); + +	return mmc_resume_host(mmc); +} + +#else /* CONFIG_PM */ +#define s3cmci_suspend NULL +#define s3cmci_resume NULL +#endif /* CONFIG_PM */ + + +static struct platform_driver s3cmci_driver_2410 = { +	.driver.name	= "s3c2410-sdi", +	.driver.owner	= THIS_MODULE, +	.probe		= s3cmci_probe_2410, +	.remove		= __devexit_p(s3cmci_remove), +	.suspend	= s3cmci_suspend, +	.resume		= s3cmci_resume, +}; + +static struct platform_driver s3cmci_driver_2412 = { +	.driver.name	= "s3c2412-sdi", +	.driver.owner	= THIS_MODULE, +	.probe		= s3cmci_probe_2412, +	.remove		= __devexit_p(s3cmci_remove), +	.suspend	= s3cmci_suspend, +	.resume		= s3cmci_resume, +}; + +static struct platform_driver s3cmci_driver_2440 = { +	.driver.name	= "s3c2440-sdi", +	.driver.owner	= THIS_MODULE, +	.probe		= s3cmci_probe_2440, +	.remove		= __devexit_p(s3cmci_remove), +	.suspend	= s3cmci_suspend, +	.resume		= s3cmci_resume, +}; + + +static int __init s3cmci_init(void) +{ +	platform_driver_register(&s3cmci_driver_2410); +	platform_driver_register(&s3cmci_driver_2412); +	platform_driver_register(&s3cmci_driver_2440); +	return 0; +} + +static void __exit s3cmci_exit(void) +{ +	platform_driver_unregister(&s3cmci_driver_2410); +	platform_driver_unregister(&s3cmci_driver_2412); +	platform_driver_unregister(&s3cmci_driver_2440); +} + +module_init(s3cmci_init); +module_exit(s3cmci_exit); + +MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>"); +MODULE_ALIAS("platform:s3c2410-sdi"); +MODULE_ALIAS("platform:s3c2412-sdi"); +MODULE_ALIAS("platform:s3c2440-sdi"); diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h new file mode 100644 index 00000000000..37d9c60010c --- /dev/null +++ b/drivers/mmc/host/s3cmci.h @@ -0,0 +1,70 @@ +/* + *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver + * + *  Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* FIXME: DMA Resource management ?! */ +#define S3CMCI_DMA 0 + +enum s3cmci_waitfor { +	COMPLETION_NONE, +	COMPLETION_FINALIZE, +	COMPLETION_CMDSENT, +	COMPLETION_RSPFIN, +	COMPLETION_XFERFINISH, +	COMPLETION_XFERFINISH_RSPFIN, +}; + +struct s3cmci_host { +	struct platform_device	*pdev; +	struct s3c24xx_mci_pdata *pdata; +	struct mmc_host		*mmc; +	struct resource		*mem; +	struct clk		*clk; +	void __iomem		*base; +	int			irq; +	int			irq_cd; +	int			dma; + +	unsigned long		clk_rate; +	unsigned long		clk_div; +	unsigned long		real_rate; +	u8			prescaler; + +	int			is2440; +	unsigned		sdiimsk; +	unsigned		sdidata; +	int			dodma; +	int			dmatogo; + +	struct mmc_request	*mrq; +	int			cmd_is_stop; + +	spinlock_t		complete_lock; +	enum s3cmci_waitfor	complete_what; + +	int			dma_complete; + +	u32			pio_sgptr; +	u32			pio_words; +	u32			pio_count; +	u32			*pio_ptr; +#define XFER_NONE 0 +#define XFER_READ 1 +#define XFER_WRITE 2 +	u32			pio_active; + +	int			bus_width; + +	char 			dbgmsg_cmd[301]; +	char 			dbgmsg_dat[301]; +	char			*status; + +	unsigned int		ccnt, dcnt; +	struct tasklet_struct	pio_tasklet; +}; diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c new file mode 100644 index 00000000000..deb607c52c0 --- /dev/null +++ b/drivers/mmc/host/sdhci-pci.c @@ -0,0 +1,732 @@ +/*  linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus interface + * + *  Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. + * + * 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. + * + * Thanks to the following companies for their support: + * + *     - JMicron (hardware and technical support) + */ + +#include <linux/delay.h> +#include <linux/highmem.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> + +#include <linux/mmc/host.h> + +#include <asm/scatterlist.h> +#include <asm/io.h> + +#include "sdhci.h" + +/* + * PCI registers + */ + +#define PCI_SDHCI_IFPIO			0x00 +#define PCI_SDHCI_IFDMA			0x01 +#define PCI_SDHCI_IFVENDOR		0x02 + +#define PCI_SLOT_INFO			0x40	/* 8 bits */ +#define  PCI_SLOT_INFO_SLOTS(x)		((x >> 4) & 7) +#define  PCI_SLOT_INFO_FIRST_BAR_MASK	0x07 + +#define MAX_SLOTS			8 + +struct sdhci_pci_chip; +struct sdhci_pci_slot; + +struct sdhci_pci_fixes { +	unsigned int		quirks; + +	int			(*probe)(struct sdhci_pci_chip*); + +	int			(*probe_slot)(struct sdhci_pci_slot*); +	void			(*remove_slot)(struct sdhci_pci_slot*, int); + +	int			(*suspend)(struct sdhci_pci_chip*, +					pm_message_t); +	int			(*resume)(struct sdhci_pci_chip*); +}; + +struct sdhci_pci_slot { +	struct sdhci_pci_chip	*chip; +	struct sdhci_host	*host; + +	int			pci_bar; +}; + +struct sdhci_pci_chip { +	struct pci_dev		*pdev; + +	unsigned int		quirks; +	const struct sdhci_pci_fixes *fixes; + +	int			num_slots;	/* Slots on controller */ +	struct sdhci_pci_slot	*slots[MAX_SLOTS]; /* Pointers to host slots */ +}; + + +/*****************************************************************************\ + *                                                                           * + * Hardware specific quirk handling                                          * + *                                                                           * +\*****************************************************************************/ + +static int ricoh_probe(struct sdhci_pci_chip *chip) +{ +	if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) +		chip->quirks |= SDHCI_QUIRK_CLOCK_BEFORE_RESET; + +	if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG) +		chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; + +	return 0; +} + +static const struct sdhci_pci_fixes sdhci_ricoh = { +	.probe		= ricoh_probe, +	.quirks		= SDHCI_QUIRK_32BIT_DMA_ADDR, +}; + +static const struct sdhci_pci_fixes sdhci_ene_712 = { +	.quirks		= SDHCI_QUIRK_SINGLE_POWER_WRITE | +			  SDHCI_QUIRK_BROKEN_DMA, +}; + +static const struct sdhci_pci_fixes sdhci_ene_714 = { +	.quirks		= SDHCI_QUIRK_SINGLE_POWER_WRITE | +			  SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | +			  SDHCI_QUIRK_BROKEN_DMA, +}; + +static const struct sdhci_pci_fixes sdhci_cafe = { +	.quirks		= SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | +			  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, +}; + +static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) +{ +	u8 scratch; +	int ret; + +	ret = pci_read_config_byte(chip->pdev, 0xAE, &scratch); +	if (ret) +		return ret; + +	/* +	 * Turn PMOS on [bit 0], set over current detection to 2.4 V +	 * [bit 1:2] and enable over current debouncing [bit 6]. +	 */ +	if (on) +		scratch |= 0x47; +	else +		scratch &= ~0x47; + +	ret = pci_write_config_byte(chip->pdev, 0xAE, scratch); +	if (ret) +		return ret; + +	return 0; +} + +static int jmicron_probe(struct sdhci_pci_chip *chip) +{ +	int ret; + +	if (chip->pdev->revision == 0) { +		chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | +			  SDHCI_QUIRK_32BIT_DMA_SIZE | +			  SDHCI_QUIRK_32BIT_ADMA_SIZE | +			  SDHCI_QUIRK_RESET_AFTER_REQUEST; +	} + +	/* +	 * JMicron chips can have two interfaces to the same hardware +	 * in order to work around limitations in Microsoft's driver. +	 * We need to make sure we only bind to one of them. +	 * +	 * This code assumes two things: +	 * +	 * 1. The PCI code adds subfunctions in order. +	 * +	 * 2. The MMC interface has a lower subfunction number +	 *    than the SD interface. +	 */ +	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) { +		struct pci_dev *sd_dev; + +		sd_dev = NULL; +		while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, +			PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) { +			if ((PCI_SLOT(chip->pdev->devfn) == +				PCI_SLOT(sd_dev->devfn)) && +				(chip->pdev->bus == sd_dev->bus)) +				break; +		} + +		if (sd_dev) { +			pci_dev_put(sd_dev); +			dev_info(&chip->pdev->dev, "Refusing to bind to " +				"secondary interface.\n"); +			return -ENODEV; +		} +	} + +	/* +	 * JMicron chips need a bit of a nudge to enable the power +	 * output pins. +	 */ +	ret = jmicron_pmos(chip, 1); +	if (ret) { +		dev_err(&chip->pdev->dev, "Failure enabling card power\n"); +		return ret; +	} + +	return 0; +} + +static void jmicron_enable_mmc(struct sdhci_host *host, int on) +{ +	u8 scratch; + +	scratch = readb(host->ioaddr + 0xC0); + +	if (on) +		scratch |= 0x01; +	else +		scratch &= ~0x01; + +	writeb(scratch, host->ioaddr + 0xC0); +} + +static int jmicron_probe_slot(struct sdhci_pci_slot *slot) +{ +	if (slot->chip->pdev->revision == 0) { +		u16 version; + +		version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION); +		version = (version & SDHCI_VENDOR_VER_MASK) >> +			SDHCI_VENDOR_VER_SHIFT; + +		/* +		 * Older versions of the chip have lots of nasty glitches +		 * in the ADMA engine. It's best just to avoid it +		 * completely. +		 */ +		if (version < 0xAC) +			slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; +	} + +	/* +	 * The secondary interface requires a bit set to get the +	 * interrupts. +	 */ +	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) +		jmicron_enable_mmc(slot->host, 1); + +	return 0; +} + +static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead) +{ +	if (dead) +		return; + +	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) +		jmicron_enable_mmc(slot->host, 0); +} + +static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) +{ +	int i; + +	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { +		for (i = 0;i < chip->num_slots;i++) +			jmicron_enable_mmc(chip->slots[i]->host, 0); +	} + +	return 0; +} + +static int jmicron_resume(struct sdhci_pci_chip *chip) +{ +	int ret, i; + +	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { +		for (i = 0;i < chip->num_slots;i++) +			jmicron_enable_mmc(chip->slots[i]->host, 1); +	} + +	ret = jmicron_pmos(chip, 1); +	if (ret) { +		dev_err(&chip->pdev->dev, "Failure enabling card power\n"); +		return ret; +	} + +	return 0; +} + +static const struct sdhci_pci_fixes sdhci_jmicron = { +	.probe		= jmicron_probe, + +	.probe_slot	= jmicron_probe_slot, +	.remove_slot	= jmicron_remove_slot, + +	.suspend	= jmicron_suspend, +	.resume		= jmicron_resume, +}; + +static const struct pci_device_id pci_ids[] __devinitdata = { +	{ +		.vendor		= PCI_VENDOR_ID_RICOH, +		.device		= PCI_DEVICE_ID_RICOH_R5C822, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_ricoh, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_ENE, +		.device		= PCI_DEVICE_ID_ENE_CB712_SD, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_ene_712, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_ENE, +		.device		= PCI_DEVICE_ID_ENE_CB712_SD_2, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_ene_712, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_ENE, +		.device		= PCI_DEVICE_ID_ENE_CB714_SD, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_ene_714, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_ENE, +		.device		= PCI_DEVICE_ID_ENE_CB714_SD_2, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_ene_714, +	}, + +	{ +		.vendor         = PCI_VENDOR_ID_MARVELL, +		.device         = PCI_DEVICE_ID_MARVELL_CAFE_SD, +		.subvendor      = PCI_ANY_ID, +		.subdevice      = PCI_ANY_ID, +		.driver_data    = (kernel_ulong_t)&sdhci_cafe, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_JMICRON, +		.device		= PCI_DEVICE_ID_JMICRON_JMB38X_SD, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_JMICRON, +		.device		= PCI_DEVICE_ID_JMICRON_JMB38X_MMC, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, +	}, + +	{	/* Generic SD host controller */ +		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) +	}, + +	{ /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(pci, pci_ids); + +/*****************************************************************************\ + *                                                                           * + * SDHCI core callbacks                                                      * + *                                                                           * +\*****************************************************************************/ + +static int sdhci_pci_enable_dma(struct sdhci_host *host) +{ +	struct sdhci_pci_slot *slot; +	struct pci_dev *pdev; +	int ret; + +	slot = sdhci_priv(host); +	pdev = slot->chip->pdev; + +	if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) && +		((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && +		(host->flags & SDHCI_USE_DMA)) { +		dev_warn(&pdev->dev, "Will use DMA mode even though HW " +			"doesn't fully claim to support it.\n"); +	} + +	ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); +	if (ret) +		return ret; + +	pci_set_master(pdev); + +	return 0; +} + +static struct sdhci_ops sdhci_pci_ops = { +	.enable_dma	= sdhci_pci_enable_dma, +}; + +/*****************************************************************************\ + *                                                                           * + * Suspend/resume                                                            * + *                                                                           * +\*****************************************************************************/ + +#ifdef CONFIG_PM + +static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) +{ +	struct sdhci_pci_chip *chip; +	struct sdhci_pci_slot *slot; +	int i, ret; + +	chip = pci_get_drvdata(pdev); +	if (!chip) +		return 0; + +	for (i = 0;i < chip->num_slots;i++) { +		slot = chip->slots[i]; +		if (!slot) +			continue; + +		ret = sdhci_suspend_host(slot->host, state); + +		if (ret) { +			for (i--;i >= 0;i--) +				sdhci_resume_host(chip->slots[i]->host); +			return ret; +		} +	} + +	if (chip->fixes && chip->fixes->suspend) { +		ret = chip->fixes->suspend(chip, state); +		if (ret) { +			for (i = chip->num_slots - 1;i >= 0;i--) +				sdhci_resume_host(chip->slots[i]->host); +			return ret; +		} +	} + +	pci_save_state(pdev); +	pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); +	pci_disable_device(pdev); +	pci_set_power_state(pdev, pci_choose_state(pdev, state)); + +	return 0; +} + +static int sdhci_pci_resume (struct pci_dev *pdev) +{ +	struct sdhci_pci_chip *chip; +	struct sdhci_pci_slot *slot; +	int i, ret; + +	chip = pci_get_drvdata(pdev); +	if (!chip) +		return 0; + +	pci_set_power_state(pdev, PCI_D0); +	pci_restore_state(pdev); +	ret = pci_enable_device(pdev); +	if (ret) +		return ret; + +	if (chip->fixes && chip->fixes->resume) { +		ret = chip->fixes->resume(chip); +		if (ret) +			return ret; +	} + +	for (i = 0;i < chip->num_slots;i++) { +		slot = chip->slots[i]; +		if (!slot) +			continue; + +		ret = sdhci_resume_host(slot->host); +		if (ret) +			return ret; +	} + +	return 0; +} + +#else /* CONFIG_PM */ + +#define sdhci_pci_suspend NULL +#define sdhci_pci_resume NULL + +#endif /* CONFIG_PM */ + +/*****************************************************************************\ + *                                                                           * + * Device probing/removal                                                    * + *                                                                           * +\*****************************************************************************/ + +static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( +	struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar) +{ +	struct sdhci_pci_slot *slot; +	struct sdhci_host *host; + +	resource_size_t addr; + +	int ret; + +	if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { +		dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); +		return ERR_PTR(-ENODEV); +	} + +	if (pci_resource_len(pdev, bar) != 0x100) { +		dev_err(&pdev->dev, "Invalid iomem size. You may " +			"experience problems.\n"); +	} + +	if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { +		dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n"); +		return ERR_PTR(-ENODEV); +	} + +	if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { +		dev_err(&pdev->dev, "Unknown interface. Aborting.\n"); +		return ERR_PTR(-ENODEV); +	} + +	host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot)); +	if (IS_ERR(host)) { +		ret = PTR_ERR(host); +		goto unmap; +	} + +	slot = sdhci_priv(host); + +	slot->chip = chip; +	slot->host = host; +	slot->pci_bar = bar; + +	host->hw_name = "PCI"; +	host->ops = &sdhci_pci_ops; +	host->quirks = chip->quirks; + +	host->irq = pdev->irq; + +	ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); +	if (ret) { +		dev_err(&pdev->dev, "cannot request region\n"); +		return ERR_PTR(ret); +	} + +	addr = pci_resource_start(pdev, bar); +	host->ioaddr = ioremap_nocache(addr, pci_resource_len(pdev, bar)); +	if (!host->ioaddr) { +		dev_err(&pdev->dev, "failed to remap registers\n"); +		goto release; +	} + +	if (chip->fixes && chip->fixes->probe_slot) { +		ret = chip->fixes->probe_slot(slot); +		if (ret) +			goto unmap; +	} + +	ret = sdhci_add_host(host); +	if (ret) +		goto remove; + +	return slot; + +remove: +	if (chip->fixes && chip->fixes->remove_slot) +		chip->fixes->remove_slot(slot, 0); + +unmap: +	iounmap(host->ioaddr); + +release: +	pci_release_region(pdev, bar); +	sdhci_free_host(host); + +	return ERR_PTR(ret); +} + +static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) +{ +	int dead; +	u32 scratch; + +	dead = 0; +	scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); +	if (scratch == (u32)-1) +		dead = 1; + +	sdhci_remove_host(slot->host, dead); + +	if (slot->chip->fixes && slot->chip->fixes->remove_slot) +		slot->chip->fixes->remove_slot(slot, dead); + +	pci_release_region(slot->chip->pdev, slot->pci_bar); + +	sdhci_free_host(slot->host); +} + +static int __devinit sdhci_pci_probe(struct pci_dev *pdev, +				     const struct pci_device_id *ent) +{ +	struct sdhci_pci_chip *chip; +	struct sdhci_pci_slot *slot; + +	u8 slots, rev, first_bar; +	int ret, i; + +	BUG_ON(pdev == NULL); +	BUG_ON(ent == NULL); + +	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); + +	dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n", +		 (int)pdev->vendor, (int)pdev->device, (int)rev); + +	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); +	if (ret) +		return ret; + +	slots = PCI_SLOT_INFO_SLOTS(slots) + 1; +	dev_dbg(&pdev->dev, "found %d slot(s)\n", slots); +	if (slots == 0) +		return -ENODEV; + +	BUG_ON(slots > MAX_SLOTS); + +	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); +	if (ret) +		return ret; + +	first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; + +	if (first_bar > 5) { +		dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n"); +		return -ENODEV; +	} + +	ret = pci_enable_device(pdev); +	if (ret) +		return ret; + +	chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL); +	if (!chip) { +		ret = -ENOMEM; +		goto err; +	} + +	chip->pdev = pdev; +	chip->fixes = (const struct sdhci_pci_fixes*)ent->driver_data; +	if (chip->fixes) +		chip->quirks = chip->fixes->quirks; +	chip->num_slots = slots; + +	pci_set_drvdata(pdev, chip); + +	if (chip->fixes && chip->fixes->probe) { +		ret = chip->fixes->probe(chip); +		if (ret) +			goto free; +	} + +	for (i = 0;i < slots;i++) { +		slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i); +		if (IS_ERR(slot)) { +			for (i--;i >= 0;i--) +				sdhci_pci_remove_slot(chip->slots[i]); +			ret = PTR_ERR(slot); +			goto free; +		} + +		chip->slots[i] = slot; +	} + +	return 0; + +free: +	pci_set_drvdata(pdev, NULL); +	kfree(chip); + +err: +	pci_disable_device(pdev); +	return ret; +} + +static void __devexit sdhci_pci_remove(struct pci_dev *pdev) +{ +	int i; +	struct sdhci_pci_chip *chip; + +	chip = pci_get_drvdata(pdev); + +	if (chip) { +		for (i = 0;i < chip->num_slots; i++) +			sdhci_pci_remove_slot(chip->slots[i]); + +		pci_set_drvdata(pdev, NULL); +		kfree(chip); +	} + +	pci_disable_device(pdev); +} + +static struct pci_driver sdhci_driver = { +	.name = 	"sdhci-pci", +	.id_table =	pci_ids, +	.probe = 	sdhci_pci_probe, +	.remove =	__devexit_p(sdhci_pci_remove), +	.suspend =	sdhci_pci_suspend, +	.resume	=	sdhci_pci_resume, +}; + +/*****************************************************************************\ + *                                                                           * + * Driver init/exit                                                          * + *                                                                           * +\*****************************************************************************/ + +static int __init sdhci_drv_init(void) +{ +	return pci_register_driver(&sdhci_driver); +} + +static void __exit sdhci_drv_exit(void) +{ +	pci_unregister_driver(&sdhci_driver); +} + +module_init(sdhci_drv_init); +module_exit(sdhci_drv_exit); + +MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>"); +MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b413aa6c246..17701c3da73 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -15,7 +15,7 @@  #include <linux/delay.h>  #include <linux/highmem.h> -#include <linux/pci.h> +#include <linux/io.h>  #include <linux/dma-mapping.h>  #include <linux/scatterlist.h> @@ -32,135 +32,6 @@  static unsigned int debug_quirks = 0; -/* - * Different quirks to handle when the hardware deviates from a strict - * interpretation of the SDHCI specification. - */ - -/* Controller doesn't honor resets unless we touch the clock register */ -#define SDHCI_QUIRK_CLOCK_BEFORE_RESET			(1<<0) -/* Controller has bad caps bits, but really supports DMA */ -#define SDHCI_QUIRK_FORCE_DMA				(1<<1) -/* Controller doesn't like to be reset when there is no card inserted. */ -#define SDHCI_QUIRK_NO_CARD_NO_RESET			(1<<2) -/* Controller doesn't like clearing the power reg before a change */ -#define SDHCI_QUIRK_SINGLE_POWER_WRITE			(1<<3) -/* Controller has flaky internal state so reset it on each ios change */ -#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS		(1<<4) -/* Controller has an unusable DMA engine */ -#define SDHCI_QUIRK_BROKEN_DMA				(1<<5) -/* Controller can only DMA from 32-bit aligned addresses */ -#define SDHCI_QUIRK_32BIT_DMA_ADDR			(1<<6) -/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ -#define SDHCI_QUIRK_32BIT_DMA_SIZE			(1<<7) -/* Controller needs to be reset after each request to stay stable */ -#define SDHCI_QUIRK_RESET_AFTER_REQUEST			(1<<8) -/* Controller needs voltage and power writes to happen separately */ -#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER		(1<<9) -/* Controller has an off-by-one issue with timeout value */ -#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL		(1<<10) - -static const struct pci_device_id pci_ids[] __devinitdata = { -	{ -		.vendor		= PCI_VENDOR_ID_RICOH, -		.device		= PCI_DEVICE_ID_RICOH_R5C822, -		.subvendor	= PCI_VENDOR_ID_IBM, -		.subdevice	= PCI_ANY_ID, -		.driver_data	= SDHCI_QUIRK_CLOCK_BEFORE_RESET | -				  SDHCI_QUIRK_FORCE_DMA, -	}, - -	{ -		.vendor		= PCI_VENDOR_ID_RICOH, -		.device		= PCI_DEVICE_ID_RICOH_R5C822, -		.subvendor	= PCI_VENDOR_ID_SAMSUNG, -		.subdevice	= PCI_ANY_ID, -		.driver_data	= SDHCI_QUIRK_FORCE_DMA | -				  SDHCI_QUIRK_NO_CARD_NO_RESET, -	}, - -	{ -		.vendor		= PCI_VENDOR_ID_RICOH, -		.device		= PCI_DEVICE_ID_RICOH_R5C822, -		.subvendor	= PCI_ANY_ID, -		.subdevice	= PCI_ANY_ID, -		.driver_data	= SDHCI_QUIRK_FORCE_DMA, -	}, - -	{ -		.vendor		= PCI_VENDOR_ID_TI, -		.device		= PCI_DEVICE_ID_TI_XX21_XX11_SD, -		.subvendor	= PCI_ANY_ID, -		.subdevice	= PCI_ANY_ID, -		.driver_data	= SDHCI_QUIRK_FORCE_DMA, -	}, - -	{ -		.vendor		= PCI_VENDOR_ID_ENE, -		.device		= PCI_DEVICE_ID_ENE_CB712_SD, -		.subvendor	= PCI_ANY_ID, -		.subdevice	= PCI_ANY_ID, -		.driver_data	= SDHCI_QUIRK_SINGLE_POWER_WRITE | -				  SDHCI_QUIRK_BROKEN_DMA, -	}, - -	{ -		.vendor		= PCI_VENDOR_ID_ENE, -		.device		= PCI_DEVICE_ID_ENE_CB712_SD_2, -		.subvendor	= PCI_ANY_ID, -		.subdevice	= PCI_ANY_ID, -		.driver_data	= SDHCI_QUIRK_SINGLE_POWER_WRITE | -				  SDHCI_QUIRK_BROKEN_DMA, -	}, - -	{ -		.vendor         = PCI_VENDOR_ID_ENE, -		.device         = PCI_DEVICE_ID_ENE_CB714_SD, -		.subvendor      = PCI_ANY_ID, -		.subdevice      = PCI_ANY_ID, -		.driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE | -				  SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | -				  SDHCI_QUIRK_BROKEN_DMA, -	}, - -	{ -		.vendor         = PCI_VENDOR_ID_ENE, -		.device         = PCI_DEVICE_ID_ENE_CB714_SD_2, -		.subvendor      = PCI_ANY_ID, -		.subdevice      = PCI_ANY_ID, -		.driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE | -				  SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | -				  SDHCI_QUIRK_BROKEN_DMA, -	}, - -	{ -		.vendor         = PCI_VENDOR_ID_MARVELL, -		.device         = PCI_DEVICE_ID_MARVELL_CAFE_SD, -		.subvendor      = PCI_ANY_ID, -		.subdevice      = PCI_ANY_ID, -		.driver_data    = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | -				  SDHCI_QUIRK_INCR_TIMEOUT_CONTROL, -	}, - -	{ -		.vendor         = PCI_VENDOR_ID_JMICRON, -		.device         = PCI_DEVICE_ID_JMICRON_JMB38X_SD, -		.subvendor      = PCI_ANY_ID, -		.subdevice      = PCI_ANY_ID, -		.driver_data    = SDHCI_QUIRK_32BIT_DMA_ADDR | -				  SDHCI_QUIRK_32BIT_DMA_SIZE | -				  SDHCI_QUIRK_RESET_AFTER_REQUEST, -	}, - -	{	/* Generic SD host controller */ -		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) -	}, - -	{ /* end: all zeroes */ }, -}; - -MODULE_DEVICE_TABLE(pci, pci_ids); -  static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);  static void sdhci_finish_data(struct sdhci_host *); @@ -215,7 +86,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)  {  	unsigned long timeout; -	if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { +	if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {  		if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &  			SDHCI_CARD_PRESENT))  			return; @@ -253,7 +124,8 @@ static void sdhci_init(struct sdhci_host *host)  		SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |  		SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT |  		SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | -		SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; +		SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE | +		SDHCI_INT_ADMA_ERROR;  	writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);  	writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); @@ -443,23 +315,226 @@ static void sdhci_transfer_pio(struct sdhci_host *host)  	DBG("PIO transfer complete.\n");  } -static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) +static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)  { -	u8 count; -	unsigned target_timeout, current_timeout; +	local_irq_save(*flags); +	return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; +} -	WARN_ON(host->data); +static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) +{ +	kunmap_atomic(buffer, KM_BIO_SRC_IRQ); +	local_irq_restore(*flags); +} -	if (data == NULL) -		return; +static int sdhci_adma_table_pre(struct sdhci_host *host, +	struct mmc_data *data) +{ +	int direction; -	/* Sanity checks */ -	BUG_ON(data->blksz * data->blocks > 524288); -	BUG_ON(data->blksz > host->mmc->max_blk_size); -	BUG_ON(data->blocks > 65535); +	u8 *desc; +	u8 *align; +	dma_addr_t addr; +	dma_addr_t align_addr; +	int len, offset; -	host->data = data; -	host->data_early = 0; +	struct scatterlist *sg; +	int i; +	char *buffer; +	unsigned long flags; + +	/* +	 * The spec does not specify endianness of descriptor table. +	 * We currently guess that it is LE. +	 */ + +	if (data->flags & MMC_DATA_READ) +		direction = DMA_FROM_DEVICE; +	else +		direction = DMA_TO_DEVICE; + +	/* +	 * The ADMA descriptor table is mapped further down as we +	 * need to fill it with data first. +	 */ + +	host->align_addr = dma_map_single(mmc_dev(host->mmc), +		host->align_buffer, 128 * 4, direction); +	if (dma_mapping_error(host->align_addr)) +		goto fail; +	BUG_ON(host->align_addr & 0x3); + +	host->sg_count = dma_map_sg(mmc_dev(host->mmc), +		data->sg, data->sg_len, direction); +	if (host->sg_count == 0) +		goto unmap_align; + +	desc = host->adma_desc; +	align = host->align_buffer; + +	align_addr = host->align_addr; + +	for_each_sg(data->sg, sg, host->sg_count, i) { +		addr = sg_dma_address(sg); +		len = sg_dma_len(sg); + +		/* +		 * The SDHCI specification states that ADMA +		 * addresses must be 32-bit aligned. If they +		 * aren't, then we use a bounce buffer for +		 * the (up to three) bytes that screw up the +		 * alignment. +		 */ +		offset = (4 - (addr & 0x3)) & 0x3; +		if (offset) { +			if (data->flags & MMC_DATA_WRITE) { +				buffer = sdhci_kmap_atomic(sg, &flags); +				memcpy(align, buffer, offset); +				sdhci_kunmap_atomic(buffer, &flags); +			} + +			desc[7] = (align_addr >> 24) & 0xff; +			desc[6] = (align_addr >> 16) & 0xff; +			desc[5] = (align_addr >> 8) & 0xff; +			desc[4] = (align_addr >> 0) & 0xff; + +			BUG_ON(offset > 65536); + +			desc[3] = (offset >> 8) & 0xff; +			desc[2] = (offset >> 0) & 0xff; + +			desc[1] = 0x00; +			desc[0] = 0x21; /* tran, valid */ + +			align += 4; +			align_addr += 4; + +			desc += 8; + +			addr += offset; +			len -= offset; +		} + +		desc[7] = (addr >> 24) & 0xff; +		desc[6] = (addr >> 16) & 0xff; +		desc[5] = (addr >> 8) & 0xff; +		desc[4] = (addr >> 0) & 0xff; + +		BUG_ON(len > 65536); + +		desc[3] = (len >> 8) & 0xff; +		desc[2] = (len >> 0) & 0xff; + +		desc[1] = 0x00; +		desc[0] = 0x21; /* tran, valid */ + +		desc += 8; + +		/* +		 * If this triggers then we have a calculation bug +		 * somewhere. :/ +		 */ +		WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4); +	} + +	/* +	 * Add a terminating entry. +	 */ +	desc[7] = 0; +	desc[6] = 0; +	desc[5] = 0; +	desc[4] = 0; + +	desc[3] = 0; +	desc[2] = 0; + +	desc[1] = 0x00; +	desc[0] = 0x03; /* nop, end, valid */ + +	/* +	 * Resync align buffer as we might have changed it. +	 */ +	if (data->flags & MMC_DATA_WRITE) { +		dma_sync_single_for_device(mmc_dev(host->mmc), +			host->align_addr, 128 * 4, direction); +	} + +	host->adma_addr = dma_map_single(mmc_dev(host->mmc), +		host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); +	if (dma_mapping_error(host->align_addr)) +		goto unmap_entries; +	BUG_ON(host->adma_addr & 0x3); + +	return 0; + +unmap_entries: +	dma_unmap_sg(mmc_dev(host->mmc), data->sg, +		data->sg_len, direction); +unmap_align: +	dma_unmap_single(mmc_dev(host->mmc), host->align_addr, +		128 * 4, direction); +fail: +	return -EINVAL; +} + +static void sdhci_adma_table_post(struct sdhci_host *host, +	struct mmc_data *data) +{ +	int direction; + +	struct scatterlist *sg; +	int i, size; +	u8 *align; +	char *buffer; +	unsigned long flags; + +	if (data->flags & MMC_DATA_READ) +		direction = DMA_FROM_DEVICE; +	else +		direction = DMA_TO_DEVICE; + +	dma_unmap_single(mmc_dev(host->mmc), host->adma_addr, +		(128 * 2 + 1) * 4, DMA_TO_DEVICE); + +	dma_unmap_single(mmc_dev(host->mmc), host->align_addr, +		128 * 4, direction); + +	if (data->flags & MMC_DATA_READ) { +		dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, +			data->sg_len, direction); + +		align = host->align_buffer; + +		for_each_sg(data->sg, sg, host->sg_count, i) { +			if (sg_dma_address(sg) & 0x3) { +				size = 4 - (sg_dma_address(sg) & 0x3); + +				buffer = sdhci_kmap_atomic(sg, &flags); +				memcpy(buffer, align, size); +				sdhci_kunmap_atomic(buffer, &flags); + +				align += 4; +			} +		} +	} + +	dma_unmap_sg(mmc_dev(host->mmc), data->sg, +		data->sg_len, direction); +} + +static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) +{ +	u8 count; +	unsigned target_timeout, current_timeout; + +	/* +	 * If the host controller provides us with an incorrect timeout +	 * value, just skip the check and use 0xE.  The hardware may take +	 * longer to time out, but that's much better than having a too-short +	 * timeout value. +	 */ +	if ((host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)) +		return 0xE;  	/* timeout in us */  	target_timeout = data->timeout_ns / 1000 + @@ -484,52 +559,158 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)  			break;  	} -	/* -	 * Compensate for an off-by-one error in the CaFe hardware; otherwise, -	 * a too-small count gives us interrupt timeouts. -	 */ -	if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)) -		count++; -  	if (count >= 0xF) {  		printk(KERN_WARNING "%s: Too large timeout requested!\n",  			mmc_hostname(host->mmc));  		count = 0xE;  	} +	return count; +} + +static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) +{ +	u8 count; +	u8 ctrl; +	int ret; + +	WARN_ON(host->data); + +	if (data == NULL) +		return; + +	/* Sanity checks */ +	BUG_ON(data->blksz * data->blocks > 524288); +	BUG_ON(data->blksz > host->mmc->max_blk_size); +	BUG_ON(data->blocks > 65535); + +	host->data = data; +	host->data_early = 0; + +	count = sdhci_calc_timeout(host, data);  	writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);  	if (host->flags & SDHCI_USE_DMA)  		host->flags |= SDHCI_REQ_USE_DMA; -	if (unlikely((host->flags & SDHCI_REQ_USE_DMA) && -		(host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) && -		((data->blksz * data->blocks) & 0x3))) { -		DBG("Reverting to PIO because of transfer size (%d)\n", -			data->blksz * data->blocks); -		host->flags &= ~SDHCI_REQ_USE_DMA; +	/* +	 * FIXME: This doesn't account for merging when mapping the +	 * scatterlist. +	 */ +	if (host->flags & SDHCI_REQ_USE_DMA) { +		int broken, i; +		struct scatterlist *sg; + +		broken = 0; +		if (host->flags & SDHCI_USE_ADMA) { +			if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE) +				broken = 1; +		} else { +			if (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) +				broken = 1; +		} + +		if (unlikely(broken)) { +			for_each_sg(data->sg, sg, data->sg_len, i) { +				if (sg->length & 0x3) { +					DBG("Reverting to PIO because of " +						"transfer size (%d)\n", +						sg->length); +					host->flags &= ~SDHCI_REQ_USE_DMA; +					break; +				} +			} +		}  	}  	/*  	 * The assumption here being that alignment is the same after  	 * translation to device address space.  	 */ -	if (unlikely((host->flags & SDHCI_REQ_USE_DMA) && -		(host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && -		(data->sg->offset & 0x3))) { -		DBG("Reverting to PIO because of bad alignment\n"); -		host->flags &= ~SDHCI_REQ_USE_DMA; +	if (host->flags & SDHCI_REQ_USE_DMA) { +		int broken, i; +		struct scatterlist *sg; + +		broken = 0; +		if (host->flags & SDHCI_USE_ADMA) { +			/* +			 * As we use 3 byte chunks to work around +			 * alignment problems, we need to check this +			 * quirk. +			 */ +			if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE) +				broken = 1; +		} else { +			if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) +				broken = 1; +		} + +		if (unlikely(broken)) { +			for_each_sg(data->sg, sg, data->sg_len, i) { +				if (sg->offset & 0x3) { +					DBG("Reverting to PIO because of " +						"bad alignment\n"); +					host->flags &= ~SDHCI_REQ_USE_DMA; +					break; +				} +			} +		}  	}  	if (host->flags & SDHCI_REQ_USE_DMA) { -		int count; +		if (host->flags & SDHCI_USE_ADMA) { +			ret = sdhci_adma_table_pre(host, data); +			if (ret) { +				/* +				 * This only happens when someone fed +				 * us an invalid request. +				 */ +				WARN_ON(1); +				host->flags &= ~SDHCI_USE_DMA; +			} else { +				writel(host->adma_addr, +					host->ioaddr + SDHCI_ADMA_ADDRESS); +			} +		} else { +			int sg_cnt; + +			sg_cnt = dma_map_sg(mmc_dev(host->mmc), +					data->sg, data->sg_len, +					(data->flags & MMC_DATA_READ) ? +						DMA_FROM_DEVICE : +						DMA_TO_DEVICE); +			if (sg_cnt == 0) { +				/* +				 * This only happens when someone fed +				 * us an invalid request. +				 */ +				WARN_ON(1); +				host->flags &= ~SDHCI_USE_DMA; +			} else { +				WARN_ON(count != 1); +				writel(sg_dma_address(data->sg), +					host->ioaddr + SDHCI_DMA_ADDRESS); +			} +		} +	} -		count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len, -			(data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); -		BUG_ON(count != 1); +	/* +	 * Always adjust the DMA selection as some controllers +	 * (e.g. JMicron) can't do PIO properly when the selection +	 * is ADMA. +	 */ +	if (host->version >= SDHCI_SPEC_200) { +		ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); +		ctrl &= ~SDHCI_CTRL_DMA_MASK; +		if ((host->flags & SDHCI_REQ_USE_DMA) && +			(host->flags & SDHCI_USE_ADMA)) +			ctrl |= SDHCI_CTRL_ADMA32; +		else +			ctrl |= SDHCI_CTRL_SDMA; +		writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); +	} -		writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS); -	} else { +	if (!(host->flags & SDHCI_REQ_USE_DMA)) {  		host->cur_sg = data->sg;  		host->num_sg = data->sg_len; @@ -567,7 +748,6 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,  static void sdhci_finish_data(struct sdhci_host *host)  {  	struct mmc_data *data; -	u16 blocks;  	BUG_ON(!host->data); @@ -575,25 +755,26 @@ static void sdhci_finish_data(struct sdhci_host *host)  	host->data = NULL;  	if (host->flags & SDHCI_REQ_USE_DMA) { -		pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len, -			(data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); +		if (host->flags & SDHCI_USE_ADMA) +			sdhci_adma_table_post(host, data); +		else { +			dma_unmap_sg(mmc_dev(host->mmc), data->sg, +				data->sg_len, (data->flags & MMC_DATA_READ) ? +					DMA_FROM_DEVICE : DMA_TO_DEVICE); +		}  	}  	/* -	 * Controller doesn't count down when in single block mode. +	 * The specification states that the block count register must +	 * be updated, but it does not specify at what point in the +	 * data flow. That makes the register entirely useless to read +	 * back so we have to assume that nothing made it to the card +	 * in the event of an error.  	 */ -	if (data->blocks == 1) -		blocks = (data->error == 0) ? 0 : 1; +	if (data->error) +		data->bytes_xfered = 0;  	else -		blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT); -	data->bytes_xfered = data->blksz * (data->blocks - blocks); - -	if (!data->error && blocks) { -		printk(KERN_ERR "%s: Controller signalled completion even " -			"though there were blocks left.\n", -			mmc_hostname(host->mmc)); -		data->error = -EIO; -	} +		data->bytes_xfered = data->blksz * data->blocks;  	if (data->stop) {  		/* @@ -775,7 +956,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)  	 * Spec says that we should clear the power reg before setting  	 * a new value. Some controllers don't seem to like this though.  	 */ -	if (!(host->chip->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) +	if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))  		writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);  	pwr = SDHCI_POWER_ON; @@ -797,10 +978,10 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)  	}  	/* -	 * At least the CaFe chip gets confused if we set the voltage +	 * At least the Marvell CaFe chip gets confused if we set the voltage  	 * and set turn on power at the same time, so set the voltage first.  	 */ -	if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) +	if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))  		writeb(pwr & ~SDHCI_POWER_ON,  				host->ioaddr + SDHCI_POWER_CONTROL); @@ -833,7 +1014,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)  	host->mrq = mrq; -	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { +	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT) +		|| (host->flags & SDHCI_DEVICE_DEAD)) {  		host->mrq->cmd->error = -ENOMEDIUM;  		tasklet_schedule(&host->finish_tasklet);  	} else @@ -853,6 +1035,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	spin_lock_irqsave(&host->lock, flags); +	if (host->flags & SDHCI_DEVICE_DEAD) +		goto out; +  	/*  	 * Reset the chip on each power off.  	 * Should clear out any weird states. @@ -888,9 +1073,10 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	 * signalling timeout and CRC errors even on CMD0. Resetting  	 * it on each ios seems to solve the problem.  	 */ -	if(host->chip->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) +	if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)  		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); +out:  	mmiowb();  	spin_unlock_irqrestore(&host->lock, flags);  } @@ -905,7 +1091,10 @@ static int sdhci_get_ro(struct mmc_host *mmc)  	spin_lock_irqsave(&host->lock, flags); -	present = readl(host->ioaddr + SDHCI_PRESENT_STATE); +	if (host->flags & SDHCI_DEVICE_DEAD) +		present = 0; +	else +		present = readl(host->ioaddr + SDHCI_PRESENT_STATE);  	spin_unlock_irqrestore(&host->lock, flags); @@ -922,6 +1111,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)  	spin_lock_irqsave(&host->lock, flags); +	if (host->flags & SDHCI_DEVICE_DEAD) +		goto out; +  	ier = readl(host->ioaddr + SDHCI_INT_ENABLE);  	ier &= ~SDHCI_INT_CARD_INT; @@ -931,6 +1123,7 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)  	writel(ier, host->ioaddr + SDHCI_INT_ENABLE);  	writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); +out:  	mmiowb();  	spin_unlock_irqrestore(&host->lock, flags); @@ -996,13 +1189,14 @@ static void sdhci_tasklet_finish(unsigned long param)  	 * The controller needs a reset of internal state machines  	 * upon error conditions.  	 */ -	if (mrq->cmd->error || -		(mrq->data && (mrq->data->error || -		(mrq->data->stop && mrq->data->stop->error))) || -		(host->chip->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) { +	if (!(host->flags & SDHCI_DEVICE_DEAD) && +		(mrq->cmd->error || +		 (mrq->data && (mrq->data->error || +		  (mrq->data->stop && mrq->data->stop->error))) || +		   (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {  		/* Some controllers need this kick or reset won't work here */ -		if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { +		if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {  			unsigned int clock;  			/* This is to force an update */ @@ -1116,6 +1310,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)  		host->data->error = -ETIMEDOUT;  	else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))  		host->data->error = -EILSEQ; +	else if (intmask & SDHCI_INT_ADMA_ERROR) +		host->data->error = -EIO;  	if (host->data->error)  		sdhci_finish_data(host); @@ -1234,218 +1430,167 @@ out:  #ifdef CONFIG_PM -static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state) +int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)  { -	struct sdhci_chip *chip; -	int i, ret; - -	chip = pci_get_drvdata(pdev); -	if (!chip) -		return 0; - -	DBG("Suspending...\n"); - -	for (i = 0;i < chip->num_slots;i++) { -		if (!chip->hosts[i]) -			continue; -		ret = mmc_suspend_host(chip->hosts[i]->mmc, state); -		if (ret) { -			for (i--;i >= 0;i--) -				mmc_resume_host(chip->hosts[i]->mmc); -			return ret; -		} -	} - -	pci_save_state(pdev); -	pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); +	int ret; -	for (i = 0;i < chip->num_slots;i++) { -		if (!chip->hosts[i]) -			continue; -		free_irq(chip->hosts[i]->irq, chip->hosts[i]); -	} +	ret = mmc_suspend_host(host->mmc, state); +	if (ret) +		return ret; -	pci_disable_device(pdev); -	pci_set_power_state(pdev, pci_choose_state(pdev, state)); +	free_irq(host->irq, host);  	return 0;  } -static int sdhci_resume (struct pci_dev *pdev) -{ -	struct sdhci_chip *chip; -	int i, ret; +EXPORT_SYMBOL_GPL(sdhci_suspend_host); -	chip = pci_get_drvdata(pdev); -	if (!chip) -		return 0; +int sdhci_resume_host(struct sdhci_host *host) +{ +	int ret; -	DBG("Resuming...\n"); +	if (host->flags & SDHCI_USE_DMA) { +		if (host->ops->enable_dma) +			host->ops->enable_dma(host); +	} -	pci_set_power_state(pdev, PCI_D0); -	pci_restore_state(pdev); -	ret = pci_enable_device(pdev); +	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, +			  mmc_hostname(host->mmc), host);  	if (ret)  		return ret; -	for (i = 0;i < chip->num_slots;i++) { -		if (!chip->hosts[i]) -			continue; -		if (chip->hosts[i]->flags & SDHCI_USE_DMA) -			pci_set_master(pdev); -		ret = request_irq(chip->hosts[i]->irq, sdhci_irq, -			IRQF_SHARED, mmc_hostname(chip->hosts[i]->mmc), -			chip->hosts[i]); -		if (ret) -			return ret; -		sdhci_init(chip->hosts[i]); -		mmiowb(); -		ret = mmc_resume_host(chip->hosts[i]->mmc); -		if (ret) -			return ret; -	} +	sdhci_init(host); +	mmiowb(); + +	ret = mmc_resume_host(host->mmc); +	if (ret) +		return ret;  	return 0;  } -#else /* CONFIG_PM */ - -#define sdhci_suspend NULL -#define sdhci_resume NULL +EXPORT_SYMBOL_GPL(sdhci_resume_host);  #endif /* CONFIG_PM */  /*****************************************************************************\   *                                                                           * - * Device probing/removal                                                    * + * Device allocation/registration                                            *   *                                                                           *  \*****************************************************************************/ -static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) +struct sdhci_host *sdhci_alloc_host(struct device *dev, +	size_t priv_size)  { -	int ret; -	unsigned int version; -	struct sdhci_chip *chip;  	struct mmc_host *mmc;  	struct sdhci_host *host; -	u8 first_bar; -	unsigned int caps; - -	chip = pci_get_drvdata(pdev); -	BUG_ON(!chip); - -	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); -	if (ret) -		return ret; - -	first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; - -	if (first_bar > 5) { -		printk(KERN_ERR DRIVER_NAME ": Invalid first BAR. Aborting.\n"); -		return -ENODEV; -	} +	WARN_ON(dev == NULL); -	if (!(pci_resource_flags(pdev, first_bar + slot) & IORESOURCE_MEM)) { -		printk(KERN_ERR DRIVER_NAME ": BAR is not iomem. Aborting.\n"); -		return -ENODEV; -	} - -	if (pci_resource_len(pdev, first_bar + slot) != 0x100) { -		printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. " -			"You may experience problems.\n"); -	} - -	if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { -		printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n"); -		return -ENODEV; -	} - -	if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { -		printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n"); -		return -ENODEV; -	} - -	mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev); +	mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);  	if (!mmc) -		return -ENOMEM; +		return ERR_PTR(-ENOMEM);  	host = mmc_priv(mmc);  	host->mmc = mmc; -	host->chip = chip; -	chip->hosts[slot] = host; +	return host; +} -	host->bar = first_bar + slot; +EXPORT_SYMBOL_GPL(sdhci_alloc_host); -	host->addr = pci_resource_start(pdev, host->bar); -	host->irq = pdev->irq; +int sdhci_add_host(struct sdhci_host *host) +{ +	struct mmc_host *mmc; +	unsigned int caps; +	int ret; -	DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq); +	WARN_ON(host == NULL); +	if (host == NULL) +		return -EINVAL; -	ret = pci_request_region(pdev, host->bar, mmc_hostname(mmc)); -	if (ret) -		goto free; +	mmc = host->mmc; -	host->ioaddr = ioremap_nocache(host->addr, -		pci_resource_len(pdev, host->bar)); -	if (!host->ioaddr) { -		ret = -ENOMEM; -		goto release; -	} +	if (debug_quirks) +		host->quirks = debug_quirks;  	sdhci_reset(host, SDHCI_RESET_ALL); -	version = readw(host->ioaddr + SDHCI_HOST_VERSION); -	version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; -	if (version > 1) { +	host->version = readw(host->ioaddr + SDHCI_HOST_VERSION); +	host->version = (host->version & SDHCI_SPEC_VER_MASK) +				>> SDHCI_SPEC_VER_SHIFT; +	if (host->version > SDHCI_SPEC_200) {  		printk(KERN_ERR "%s: Unknown controller version (%d). "  			"You may experience problems.\n", mmc_hostname(mmc), -			version); +			host->version);  	}  	caps = readl(host->ioaddr + SDHCI_CAPABILITIES); -	if (chip->quirks & SDHCI_QUIRK_FORCE_DMA) +	if (host->quirks & SDHCI_QUIRK_FORCE_DMA)  		host->flags |= SDHCI_USE_DMA;  	else if (!(caps & SDHCI_CAN_DO_DMA))  		DBG("Controller doesn't have DMA capability\n");  	else  		host->flags |= SDHCI_USE_DMA; -	if ((chip->quirks & SDHCI_QUIRK_BROKEN_DMA) && +	if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&  		(host->flags & SDHCI_USE_DMA)) {  		DBG("Disabling DMA as it is marked broken\n");  		host->flags &= ~SDHCI_USE_DMA;  	} -	if (((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && -		(host->flags & SDHCI_USE_DMA)) { -		printk(KERN_WARNING "%s: Will use DMA " -			"mode even though HW doesn't fully " -			"claim to support it.\n", mmc_hostname(mmc)); +	if (host->flags & SDHCI_USE_DMA) { +		if ((host->version >= SDHCI_SPEC_200) && +				(caps & SDHCI_CAN_DO_ADMA2)) +			host->flags |= SDHCI_USE_ADMA; +	} + +	if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) && +		(host->flags & SDHCI_USE_ADMA)) { +		DBG("Disabling ADMA as it is marked broken\n"); +		host->flags &= ~SDHCI_USE_ADMA;  	}  	if (host->flags & SDHCI_USE_DMA) { -		if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { -			printk(KERN_WARNING "%s: No suitable DMA available. " -				"Falling back to PIO.\n", mmc_hostname(mmc)); -			host->flags &= ~SDHCI_USE_DMA; +		if (host->ops->enable_dma) { +			if (host->ops->enable_dma(host)) { +				printk(KERN_WARNING "%s: No suitable DMA " +					"available. Falling back to PIO.\n", +					mmc_hostname(mmc)); +				host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA); +			}  		}  	} -	if (host->flags & SDHCI_USE_DMA) -		pci_set_master(pdev); -	else /* XXX: Hack to get MMC layer to avoid highmem */ -		pdev->dma_mask = 0; +	if (host->flags & SDHCI_USE_ADMA) { +		/* +		 * We need to allocate descriptors for all sg entries +		 * (128) and potentially one alignment transfer for +		 * each of those entries. +		 */ +		host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL); +		host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); +		if (!host->adma_desc || !host->align_buffer) { +			kfree(host->adma_desc); +			kfree(host->align_buffer); +			printk(KERN_WARNING "%s: Unable to allocate ADMA " +				"buffers. Falling back to standard DMA.\n", +				mmc_hostname(mmc)); +			host->flags &= ~SDHCI_USE_ADMA; +		} +	} + +	/* XXX: Hack to get MMC layer to avoid highmem */ +	if (!(host->flags & SDHCI_USE_DMA)) +		mmc_dev(host->mmc)->dma_mask = NULL;  	host->max_clk =  		(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;  	if (host->max_clk == 0) {  		printk(KERN_ERR "%s: Hardware doesn't specify base clock "  			"frequency.\n", mmc_hostname(mmc)); -		ret = -ENODEV; -		goto unmap; +		return -ENODEV;  	}  	host->max_clk *= 1000000; @@ -1454,8 +1599,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)  	if (host->timeout_clk == 0) {  		printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "  			"frequency.\n", mmc_hostname(mmc)); -		ret = -ENODEV; -		goto unmap; +		return -ENODEV;  	}  	if (caps & SDHCI_TIMEOUT_CLK_UNIT)  		host->timeout_clk *= 1000; @@ -1466,7 +1610,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)  	mmc->ops = &sdhci_ops;  	mmc->f_min = host->max_clk / 256;  	mmc->f_max = host->max_clk; -	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SDIO_IRQ; +	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;  	if (caps & SDHCI_CAN_DO_HISPD)  		mmc->caps |= MMC_CAP_SD_HIGHSPEED; @@ -1482,20 +1626,22 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)  	if (mmc->ocr_avail == 0) {  		printk(KERN_ERR "%s: Hardware doesn't report any "  			"support voltages.\n", mmc_hostname(mmc)); -		ret = -ENODEV; -		goto unmap; +		return -ENODEV;  	}  	spin_lock_init(&host->lock);  	/* -	 * Maximum number of segments. Hardware cannot do scatter lists. +	 * Maximum number of segments. Depends on if the hardware +	 * can do scatter/gather or not.  	 */ -	if (host->flags & SDHCI_USE_DMA) +	if (host->flags & SDHCI_USE_ADMA) +		mmc->max_hw_segs = 128; +	else if (host->flags & SDHCI_USE_DMA)  		mmc->max_hw_segs = 1; -	else -		mmc->max_hw_segs = 16; -	mmc->max_phys_segs = 16; +	else /* PIO */ +		mmc->max_hw_segs = 128; +	mmc->max_phys_segs = 128;  	/*  	 * Maximum number of sectors in one transfer. Limited by DMA boundary @@ -1505,9 +1651,13 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)  	/*  	 * Maximum segment size. Could be one segment with the maximum number -	 * of bytes. +	 * of bytes. When doing hardware scatter/gather, each entry cannot +	 * be larger than 64 KiB though.  	 */ -	mmc->max_seg_size = mmc->max_req_size; +	if (host->flags & SDHCI_USE_ADMA) +		mmc->max_seg_size = 65536; +	else +		mmc->max_seg_size = mmc->max_req_size;  	/*  	 * Maximum block size. This varies from controller to controller and @@ -1553,7 +1703,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)  	host->led.default_trigger = mmc_hostname(mmc);  	host->led.brightness_set = sdhci_led_control; -	ret = led_classdev_register(&pdev->dev, &host->led); +	ret = led_classdev_register(mmc_dev(mmc), &host->led);  	if (ret)  		goto reset;  #endif @@ -1562,8 +1712,9 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)  	mmc_add_host(mmc); -	printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", -		mmc_hostname(mmc), host->addr, host->irq, +	printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n", +		mmc_hostname(mmc), host->hw_name, mmc_dev(mmc)->bus_id, +		(host->flags & SDHCI_USE_ADMA)?"A":"",  		(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");  	return 0; @@ -1576,35 +1727,40 @@ reset:  untasklet:  	tasklet_kill(&host->card_tasklet);  	tasklet_kill(&host->finish_tasklet); -unmap: -	iounmap(host->ioaddr); -release: -	pci_release_region(pdev, host->bar); -free: -	mmc_free_host(mmc);  	return ret;  } -static void sdhci_remove_slot(struct pci_dev *pdev, int slot) +EXPORT_SYMBOL_GPL(sdhci_add_host); + +void sdhci_remove_host(struct sdhci_host *host, int dead)  { -	struct sdhci_chip *chip; -	struct mmc_host *mmc; -	struct sdhci_host *host; +	unsigned long flags; -	chip = pci_get_drvdata(pdev); -	host = chip->hosts[slot]; -	mmc = host->mmc; +	if (dead) { +		spin_lock_irqsave(&host->lock, flags); -	chip->hosts[slot] = NULL; +		host->flags |= SDHCI_DEVICE_DEAD; -	mmc_remove_host(mmc); +		if (host->mrq) { +			printk(KERN_ERR "%s: Controller removed during " +				" transfer!\n", mmc_hostname(host->mmc)); + +			host->mrq->cmd->error = -ENOMEDIUM; +			tasklet_schedule(&host->finish_tasklet); +		} + +		spin_unlock_irqrestore(&host->lock, flags); +	} + +	mmc_remove_host(host->mmc);  #ifdef CONFIG_LEDS_CLASS  	led_classdev_unregister(&host->led);  #endif -	sdhci_reset(host, SDHCI_RESET_ALL); +	if (!dead) +		sdhci_reset(host, SDHCI_RESET_ALL);  	free_irq(host->irq, host); @@ -1613,106 +1769,21 @@ static void sdhci_remove_slot(struct pci_dev *pdev, int slot)  	tasklet_kill(&host->card_tasklet);  	tasklet_kill(&host->finish_tasklet); -	iounmap(host->ioaddr); - -	pci_release_region(pdev, host->bar); +	kfree(host->adma_desc); +	kfree(host->align_buffer); -	mmc_free_host(mmc); +	host->adma_desc = NULL; +	host->align_buffer = NULL;  } -static int __devinit sdhci_probe(struct pci_dev *pdev, -	const struct pci_device_id *ent) -{ -	int ret, i; -	u8 slots, rev; -	struct sdhci_chip *chip; - -	BUG_ON(pdev == NULL); -	BUG_ON(ent == NULL); - -	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); - -	printk(KERN_INFO DRIVER_NAME -		": SDHCI controller found at %s [%04x:%04x] (rev %x)\n", -		pci_name(pdev), (int)pdev->vendor, (int)pdev->device, -		(int)rev); - -	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); -	if (ret) -		return ret; - -	slots = PCI_SLOT_INFO_SLOTS(slots) + 1; -	DBG("found %d slot(s)\n", slots); -	if (slots == 0) -		return -ENODEV; - -	ret = pci_enable_device(pdev); -	if (ret) -		return ret; - -	chip = kzalloc(sizeof(struct sdhci_chip) + -		sizeof(struct sdhci_host*) * slots, GFP_KERNEL); -	if (!chip) { -		ret = -ENOMEM; -		goto err; -	} - -	chip->pdev = pdev; -	chip->quirks = ent->driver_data; - -	if (debug_quirks) -		chip->quirks = debug_quirks; - -	chip->num_slots = slots; -	pci_set_drvdata(pdev, chip); +EXPORT_SYMBOL_GPL(sdhci_remove_host); -	for (i = 0;i < slots;i++) { -		ret = sdhci_probe_slot(pdev, i); -		if (ret) { -			for (i--;i >= 0;i--) -				sdhci_remove_slot(pdev, i); -			goto free; -		} -	} - -	return 0; - -free: -	pci_set_drvdata(pdev, NULL); -	kfree(chip); - -err: -	pci_disable_device(pdev); -	return ret; -} - -static void __devexit sdhci_remove(struct pci_dev *pdev) +void sdhci_free_host(struct sdhci_host *host)  { -	int i; -	struct sdhci_chip *chip; - -	chip = pci_get_drvdata(pdev); - -	if (chip) { -		for (i = 0;i < chip->num_slots;i++) -			sdhci_remove_slot(pdev, i); - -		pci_set_drvdata(pdev, NULL); - -		kfree(chip); -	} - -	pci_disable_device(pdev); +	mmc_free_host(host->mmc);  } -static struct pci_driver sdhci_driver = { -	.name = 	DRIVER_NAME, -	.id_table =	pci_ids, -	.probe = 	sdhci_probe, -	.remove =	__devexit_p(sdhci_remove), -	.suspend =	sdhci_suspend, -	.resume	=	sdhci_resume, -}; +EXPORT_SYMBOL_GPL(sdhci_free_host);  /*****************************************************************************\   *                                                                           * @@ -1726,14 +1797,11 @@ static int __init sdhci_drv_init(void)  		": Secure Digital Host Controller Interface driver\n");  	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); -	return pci_register_driver(&sdhci_driver); +	return 0;  }  static void __exit sdhci_drv_exit(void)  { -	DBG("Exiting\n"); - -	pci_unregister_driver(&sdhci_driver);  }  module_init(sdhci_drv_init); @@ -1742,7 +1810,7 @@ module_exit(sdhci_drv_exit);  module_param(debug_quirks, uint, 0444);  MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>"); -MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver"); +MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");  MODULE_LICENSE("GPL");  MODULE_PARM_DESC(debug_quirks, "Force certain quirks."); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 299118de893..5bb35528176 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -10,18 +10,6 @@   */  /* - * PCI registers - */ - -#define PCI_SDHCI_IFPIO			0x00 -#define PCI_SDHCI_IFDMA			0x01 -#define PCI_SDHCI_IFVENDOR		0x02 - -#define PCI_SLOT_INFO			0x40	/* 8 bits */ -#define  PCI_SLOT_INFO_SLOTS(x)		((x >> 4) & 7) -#define  PCI_SLOT_INFO_FIRST_BAR_MASK	0x07 - -/*   * Controller registers   */ @@ -72,6 +60,11 @@  #define  SDHCI_CTRL_LED		0x01  #define  SDHCI_CTRL_4BITBUS	0x02  #define  SDHCI_CTRL_HISPD	0x04 +#define  SDHCI_CTRL_DMA_MASK	0x18 +#define   SDHCI_CTRL_SDMA	0x00 +#define   SDHCI_CTRL_ADMA1	0x08 +#define   SDHCI_CTRL_ADMA32	0x10 +#define   SDHCI_CTRL_ADMA64	0x18  #define SDHCI_POWER_CONTROL	0x29  #define  SDHCI_POWER_ON		0x01 @@ -117,6 +110,7 @@  #define  SDHCI_INT_DATA_END_BIT	0x00400000  #define  SDHCI_INT_BUS_POWER	0x00800000  #define  SDHCI_INT_ACMD12ERR	0x01000000 +#define  SDHCI_INT_ADMA_ERROR	0x02000000  #define  SDHCI_INT_NORMAL_MASK	0x00007FFF  #define  SDHCI_INT_ERROR_MASK	0xFFFF8000 @@ -140,11 +134,14 @@  #define  SDHCI_CLOCK_BASE_SHIFT	8  #define  SDHCI_MAX_BLOCK_MASK	0x00030000  #define  SDHCI_MAX_BLOCK_SHIFT  16 +#define  SDHCI_CAN_DO_ADMA2	0x00080000 +#define  SDHCI_CAN_DO_ADMA1	0x00100000  #define  SDHCI_CAN_DO_HISPD	0x00200000  #define  SDHCI_CAN_DO_DMA	0x00400000  #define  SDHCI_CAN_VDD_330	0x01000000  #define  SDHCI_CAN_VDD_300	0x02000000  #define  SDHCI_CAN_VDD_180	0x04000000 +#define  SDHCI_CAN_64BIT	0x10000000  /* 44-47 reserved for more caps */ @@ -152,7 +149,16 @@  /* 4C-4F reserved for more max current */ -/* 50-FB reserved */ +#define SDHCI_SET_ACMD12_ERROR	0x50 +#define SDHCI_SET_INT_ERROR	0x52 + +#define SDHCI_ADMA_ERROR	0x54 + +/* 55-57 reserved */ + +#define SDHCI_ADMA_ADDRESS	0x58 + +/* 60-FB reserved */  #define SDHCI_SLOT_INT_STATUS	0xFC @@ -161,11 +167,50 @@  #define  SDHCI_VENDOR_VER_SHIFT	8  #define  SDHCI_SPEC_VER_MASK	0x00FF  #define  SDHCI_SPEC_VER_SHIFT	0 +#define   SDHCI_SPEC_100	0 +#define   SDHCI_SPEC_200	1 -struct sdhci_chip; +struct sdhci_ops;  struct sdhci_host { -	struct sdhci_chip	*chip; +	/* Data set by hardware interface driver */ +	const char		*hw_name;	/* Hardware bus name */ + +	unsigned int		quirks;		/* Deviations from spec. */ + +/* Controller doesn't honor resets unless we touch the clock register */ +#define SDHCI_QUIRK_CLOCK_BEFORE_RESET			(1<<0) +/* Controller has bad caps bits, but really supports DMA */ +#define SDHCI_QUIRK_FORCE_DMA				(1<<1) +/* Controller doesn't like to be reset when there is no card inserted. */ +#define SDHCI_QUIRK_NO_CARD_NO_RESET			(1<<2) +/* Controller doesn't like clearing the power reg before a change */ +#define SDHCI_QUIRK_SINGLE_POWER_WRITE			(1<<3) +/* Controller has flaky internal state so reset it on each ios change */ +#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS		(1<<4) +/* Controller has an unusable DMA engine */ +#define SDHCI_QUIRK_BROKEN_DMA				(1<<5) +/* Controller has an unusable ADMA engine */ +#define SDHCI_QUIRK_BROKEN_ADMA				(1<<6) +/* Controller can only DMA from 32-bit aligned addresses */ +#define SDHCI_QUIRK_32BIT_DMA_ADDR			(1<<7) +/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ +#define SDHCI_QUIRK_32BIT_DMA_SIZE			(1<<8) +/* Controller can only ADMA chunks that are a multiple of 32 bits */ +#define SDHCI_QUIRK_32BIT_ADMA_SIZE			(1<<9) +/* Controller needs to be reset after each request to stay stable */ +#define SDHCI_QUIRK_RESET_AFTER_REQUEST			(1<<10) +/* Controller needs voltage and power writes to happen separately */ +#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER		(1<<11) +/* Controller provides an incorrect timeout value for transfers */ +#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL			(1<<12) + +	int			irq;		/* Device IRQ */ +	void __iomem *		ioaddr;		/* Mapped address */ + +	const struct sdhci_ops	*ops;		/* Low level hw interface */ + +	/* Internal data */  	struct mmc_host		*mmc;		/* MMC structure */  #ifdef CONFIG_LEDS_CLASS @@ -176,7 +221,11 @@ struct sdhci_host {  	int			flags;		/* Host attributes */  #define SDHCI_USE_DMA		(1<<0)		/* Host is DMA capable */ -#define SDHCI_REQ_USE_DMA	(1<<1)		/* Use DMA for this req. */ +#define SDHCI_USE_ADMA		(1<<1)		/* Host is ADMA capable */ +#define SDHCI_REQ_USE_DMA	(1<<2)		/* Use DMA for this req. */ +#define SDHCI_DEVICE_DEAD	(1<<3)		/* Device unresponsive */ + +	unsigned int		version;	/* SDHCI spec. version */  	unsigned int		max_clk;	/* Max possible freq (MHz) */  	unsigned int		timeout_clk;	/* Timeout freq (KHz) */ @@ -194,22 +243,41 @@ struct sdhci_host {  	int			offset;		/* Offset into current sg */  	int			remain;		/* Bytes left in current */ -	int			irq;		/* Device IRQ */ -	int			bar;		/* PCI BAR index */ -	unsigned long		addr;		/* Bus address */ -	void __iomem *		ioaddr;		/* Mapped address */ +	int			sg_count;	/* Mapped sg entries */ + +	u8			*adma_desc;	/* ADMA descriptor table */ +	u8			*align_buffer;	/* Bounce buffer */ + +	dma_addr_t		adma_addr;	/* Mapped ADMA descr. table */ +	dma_addr_t		align_addr;	/* Mapped bounce buffer */  	struct tasklet_struct	card_tasklet;	/* Tasklet structures */  	struct tasklet_struct	finish_tasklet;  	struct timer_list	timer;		/* Timer for timeouts */ -}; -struct sdhci_chip { -	struct pci_dev		*pdev; +	unsigned long		private[0] ____cacheline_aligned; +}; -	unsigned long		quirks; -	int			num_slots;	/* Slots on controller */ -	struct sdhci_host	*hosts[0];	/* Pointers to hosts */ +struct sdhci_ops { +	int		(*enable_dma)(struct sdhci_host *host);  }; + + +extern struct sdhci_host *sdhci_alloc_host(struct device *dev, +	size_t priv_size); +extern void sdhci_free_host(struct sdhci_host *host); + +static inline void *sdhci_priv(struct sdhci_host *host) +{ +	return (void *)host->private; +} + +extern int sdhci_add_host(struct sdhci_host *host); +extern void sdhci_remove_host(struct sdhci_host *host, int dead); + +#ifdef CONFIG_PM +extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state); +extern int sdhci_resume_host(struct sdhci_host *host); +#endif diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c new file mode 100644 index 00000000000..f99e9f72162 --- /dev/null +++ b/drivers/mmc/host/sdricoh_cs.c @@ -0,0 +1,575 @@ +/* + *  sdricoh_cs.c - driver for Ricoh Secure Digital Card Readers that can be + *     found on some Ricoh RL5c476 II cardbus bridge + * + *  Copyright (C) 2006 - 2008 Sascha Sommer <saschasommer@freenet.de> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ +#include <linux/delay.h> +#include <linux/highmem.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#include <linux/scatterlist.h> +#include <linux/version.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <linux/io.h> + +#include <linux/mmc/host.h> + +#define DRIVER_NAME "sdricoh_cs" + +static unsigned int switchlocked; + +/* i/o region */ +#define SDRICOH_PCI_REGION 0 +#define SDRICOH_PCI_REGION_SIZE 0x1000 + +/* registers */ +#define R104_VERSION     0x104 +#define R200_CMD         0x200 +#define R204_CMD_ARG     0x204 +#define R208_DATAIO      0x208 +#define R20C_RESP        0x20c +#define R21C_STATUS      0x21c +#define R2E0_INIT        0x2e0 +#define R2E4_STATUS_RESP 0x2e4 +#define R2F0_RESET       0x2f0 +#define R224_MODE        0x224 +#define R226_BLOCKSIZE   0x226 +#define R228_POWER       0x228 +#define R230_DATA        0x230 + +/* flags for the R21C_STATUS register */ +#define STATUS_CMD_FINISHED      0x00000001 +#define STATUS_TRANSFER_FINISHED 0x00000004 +#define STATUS_CARD_INSERTED     0x00000020 +#define STATUS_CARD_LOCKED       0x00000080 +#define STATUS_CMD_TIMEOUT       0x00400000 +#define STATUS_READY_TO_READ     0x01000000 +#define STATUS_READY_TO_WRITE    0x02000000 +#define STATUS_BUSY              0x40000000 + +/* timeouts */ +#define INIT_TIMEOUT      100 +#define CMD_TIMEOUT       100000 +#define TRANSFER_TIMEOUT  100000 +#define BUSY_TIMEOUT      32767 + +/* list of supported pcmcia devices */ +static struct pcmcia_device_id pcmcia_ids[] = { +	/* vendor and device strings followed by their crc32 hashes */ +	PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay1Controller", 0xd9f522ed, +				0xc3901202), +	PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, pcmcia_ids); + +/* mmc privdata */ +struct sdricoh_host { +	struct device *dev; +	struct mmc_host *mmc;	/* MMC structure */ +	unsigned char __iomem *iobase; +	struct pci_dev *pci_dev; +	int app_cmd; +}; + +/***************** register i/o helper functions *****************************/ + +static inline unsigned int sdricoh_readl(struct sdricoh_host *host, +					 unsigned int reg) +{ +	unsigned int value = readl(host->iobase + reg); +	dev_vdbg(host->dev, "rl %x 0x%x\n", reg, value); +	return value; +} + +static inline void sdricoh_writel(struct sdricoh_host *host, unsigned int reg, +				  unsigned int value) +{ +	writel(value, host->iobase + reg); +	dev_vdbg(host->dev, "wl %x 0x%x\n", reg, value); + +} + +static inline unsigned int sdricoh_readw(struct sdricoh_host *host, +					 unsigned int reg) +{ +	unsigned int value = readw(host->iobase + reg); +	dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value); +	return value; +} + +static inline void sdricoh_writew(struct sdricoh_host *host, unsigned int reg, +					 unsigned short value) +{ +	writew(value, host->iobase + reg); +	dev_vdbg(host->dev, "ww %x 0x%x\n", reg, value); +} + +static inline unsigned int sdricoh_readb(struct sdricoh_host *host, +					 unsigned int reg) +{ +	unsigned int value = readb(host->iobase + reg); +	dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value); +	return value; +} + +static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted, +				unsigned int timeout){ +	unsigned int loop; +	unsigned int status = 0; +	struct device *dev = host->dev; +	for (loop = 0; loop < timeout; loop++) { +		status = sdricoh_readl(host, R21C_STATUS); +		sdricoh_writel(host, R2E4_STATUS_RESP, status); +		if (status & wanted) +			break; +	} + +	if (loop == timeout) { +		dev_err(dev, "query_status: timeout waiting for %x\n", wanted); +		return -ETIMEDOUT; +	} + +	/* do not do this check in the loop as some commands fail otherwise */ +	if (status & 0x7F0000) { +		dev_err(dev, "waiting for status bit %x failed\n", wanted); +		return -EINVAL; +	} +	return 0; + +} + +static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode, +			   unsigned int arg) +{ +	unsigned int status; +	int result = 0; +	unsigned int loop = 0; +	/* reset status reg? */ +	sdricoh_writel(host, R21C_STATUS, 0x18); +	/* fill parameters */ +	sdricoh_writel(host, R204_CMD_ARG, arg); +	sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode); +	/* wait for command completion */ +	if (opcode) { +		for (loop = 0; loop < CMD_TIMEOUT; loop++) { +			status = sdricoh_readl(host, R21C_STATUS); +			sdricoh_writel(host, R2E4_STATUS_RESP, status); +			if (status  & STATUS_CMD_FINISHED) +				break; +		} +		/* don't check for timeout in the loop it is not always +		   reset correctly +		*/ +		if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT) +			result = -ETIMEDOUT; + +	} + +	return result; + +} + +static int sdricoh_reset(struct sdricoh_host *host) +{ +	dev_dbg(host->dev, "reset\n"); +	sdricoh_writel(host, R2F0_RESET, 0x10001); +	sdricoh_writel(host, R2E0_INIT, 0x10000); +	if (sdricoh_readl(host, R2E0_INIT) != 0x10000) +		return -EIO; +	sdricoh_writel(host, R2E0_INIT, 0x10007); + +	sdricoh_writel(host, R224_MODE, 0x2000000); +	sdricoh_writel(host, R228_POWER, 0xe0); + + +	/* status register ? */ +	sdricoh_writel(host, R21C_STATUS, 0x18); + +	return 0; +} + +static int sdricoh_blockio(struct sdricoh_host *host, int read, +				u8 *buf, int len) +{ +	int size; +	u32 data = 0; +	/* wait until the data is available */ +	if (read) { +		if (sdricoh_query_status(host, STATUS_READY_TO_READ, +						TRANSFER_TIMEOUT)) +			return -ETIMEDOUT; +		sdricoh_writel(host, R21C_STATUS, 0x18); +		/* read data */ +		while (len) { +			data = sdricoh_readl(host, R230_DATA); +			size = min(len, 4); +			len -= size; +			while (size) { +				*buf = data & 0xFF; +				buf++; +				data >>= 8; +				size--; +			} +		} +	} else { +		if (sdricoh_query_status(host, STATUS_READY_TO_WRITE, +						TRANSFER_TIMEOUT)) +			return -ETIMEDOUT; +		sdricoh_writel(host, R21C_STATUS, 0x18); +		/* write data */ +		while (len) { +			size = min(len, 4); +			len -= size; +			while (size) { +				data >>= 8; +				data |= (u32)*buf << 24; +				buf++; +				size--; +			} +			sdricoh_writel(host, R230_DATA, data); +		} +	} + +	if (len) +		return -EIO; + +	return 0; +} + +static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct sdricoh_host *host = mmc_priv(mmc); +	struct mmc_command *cmd = mrq->cmd; +	struct mmc_data *data = cmd->data; +	struct device *dev = host->dev; +	unsigned char opcode = cmd->opcode; +	int i; + +	dev_dbg(dev, "=============================\n"); +	dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode); + +	sdricoh_writel(host, R21C_STATUS, 0x18); + +	/* MMC_APP_CMDs need some special handling */ +	if (host->app_cmd) { +		opcode |= 64; +		host->app_cmd = 0; +	} else if (opcode == 55) +		host->app_cmd = 1; + +	/* read/write commands seem to require this */ +	if (data) { +		sdricoh_writew(host, R226_BLOCKSIZE, data->blksz); +		sdricoh_writel(host, R208_DATAIO, 0); +	} + +	cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg); + +	/* read response buffer */ +	if (cmd->flags & MMC_RSP_PRESENT) { +		if (cmd->flags & MMC_RSP_136) { +			/* CRC is stripped so we need to do some shifting. */ +			for (i = 0; i < 4; i++) { +				cmd->resp[i] = +				    sdricoh_readl(host, +						  R20C_RESP + (3 - i) * 4) << 8; +				if (i != 3) +					cmd->resp[i] |= +					    sdricoh_readb(host, R20C_RESP + +							  (3 - i) * 4 - 1); +			} +		} else +			cmd->resp[0] = sdricoh_readl(host, R20C_RESP); +	} + +	/* transfer data */ +	if (data && cmd->error == 0) { +		dev_dbg(dev, "transfer: blksz %i blocks %i sg_len %i " +			"sg length %i\n", data->blksz, data->blocks, +			data->sg_len, data->sg->length); + +		/* enter data reading mode */ +		sdricoh_writel(host, R21C_STATUS, 0x837f031e); +		for (i = 0; i < data->blocks; i++) { +			size_t len = data->blksz; +			u8 *buf; +			struct page *page; +			int result; +			page = sg_page(data->sg); + +			buf = kmap(page) + data->sg->offset + (len * i); +			result = +				sdricoh_blockio(host, +					data->flags & MMC_DATA_READ, buf, len); +			kunmap(page); +			flush_dcache_page(page); +			if (result) { +				dev_err(dev, "sdricoh_request: cmd %i " +					"block transfer failed\n", cmd->opcode); +				cmd->error = result; +				break; +			} else +				data->bytes_xfered += len; +		} + +		sdricoh_writel(host, R208_DATAIO, 1); + +		if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED, +					TRANSFER_TIMEOUT)) { +			dev_err(dev, "sdricoh_request: transfer end error\n"); +			cmd->error = -EINVAL; +		} +	} +	/* FIXME check busy flag */ + +	mmc_request_done(mmc, mrq); +	dev_dbg(dev, "=============================\n"); +} + +static void sdricoh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct sdricoh_host *host = mmc_priv(mmc); +	dev_dbg(host->dev, "set_ios\n"); + +	if (ios->power_mode == MMC_POWER_ON) { +		sdricoh_writel(host, R228_POWER, 0xc0e0); + +		if (ios->bus_width == MMC_BUS_WIDTH_4) { +			sdricoh_writel(host, R224_MODE, 0x2000300); +			sdricoh_writel(host, R228_POWER, 0x40e0); +		} else { +			sdricoh_writel(host, R224_MODE, 0x2000340); +		} + +	} else if (ios->power_mode == MMC_POWER_UP) { +		sdricoh_writel(host, R224_MODE, 0x2000320); +		sdricoh_writel(host, R228_POWER, 0xe0); +	} +} + +static int sdricoh_get_ro(struct mmc_host *mmc) +{ +	struct sdricoh_host *host = mmc_priv(mmc); +	unsigned int status; + +	status = sdricoh_readl(host, R21C_STATUS); +	sdricoh_writel(host, R2E4_STATUS_RESP, status); + +	/* some notebooks seem to have the locked flag switched */ +	if (switchlocked) +		return !(status & STATUS_CARD_LOCKED); + +	return (status & STATUS_CARD_LOCKED); +} + +static struct mmc_host_ops sdricoh_ops = { +	.request = sdricoh_request, +	.set_ios = sdricoh_set_ios, +	.get_ro = sdricoh_get_ro, +}; + +/* initialize the control and register it to the mmc framework */ +static int sdricoh_init_mmc(struct pci_dev *pci_dev, +			    struct pcmcia_device *pcmcia_dev) +{ +	int result = 0; +	void __iomem *iobase = NULL; +	struct mmc_host *mmc = NULL; +	struct sdricoh_host *host = NULL; +	struct device *dev = &pcmcia_dev->dev; +	/* map iomem */ +	if (pci_resource_len(pci_dev, SDRICOH_PCI_REGION) != +	    SDRICOH_PCI_REGION_SIZE) { +		dev_dbg(dev, "unexpected pci resource len\n"); +		return -ENODEV; +	} +	iobase = +	    pci_iomap(pci_dev, SDRICOH_PCI_REGION, SDRICOH_PCI_REGION_SIZE); +	if (!iobase) { +		dev_err(dev, "unable to map iobase\n"); +		return -ENODEV; +	} +	/* check version? */ +	if (readl(iobase + R104_VERSION) != 0x4000) { +		dev_dbg(dev, "no supported mmc controller found\n"); +		result = -ENODEV; +		goto err; +	} +	/* allocate privdata */ +	mmc = pcmcia_dev->priv = +	    mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev); +	if (!mmc) { +		dev_err(dev, "mmc_alloc_host failed\n"); +		result = -ENOMEM; +		goto err; +	} +	host = mmc_priv(mmc); + +	host->iobase = iobase; +	host->dev = dev; +	host->pci_dev = pci_dev; + +	mmc->ops = &sdricoh_ops; + +	/* FIXME: frequency and voltage handling is done by the controller +	 */ +	mmc->f_min = 450000; +	mmc->f_max = 24000000; +	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; +	mmc->caps |= MMC_CAP_4_BIT_DATA; + +	mmc->max_seg_size = 1024 * 512; +	mmc->max_blk_size = 512; + +	/* reset the controler */ +	if (sdricoh_reset(host)) { +		dev_dbg(dev, "could not reset\n"); +		result = -EIO; +		goto err; + +	} + +	result = mmc_add_host(mmc); + +	if (!result) { +		dev_dbg(dev, "mmc host registered\n"); +		return 0; +	} + +err: +	if (iobase) +		iounmap(iobase); +	if (mmc) +		mmc_free_host(mmc); + +	return result; +} + +/* search for supported mmc controllers */ +static int sdricoh_pcmcia_probe(struct pcmcia_device *pcmcia_dev) +{ +	struct pci_dev *pci_dev = NULL; + +	dev_info(&pcmcia_dev->dev, "Searching MMC controller for pcmcia device" +		" %s %s ...\n", pcmcia_dev->prod_id[0], pcmcia_dev->prod_id[1]); + +	/* search pci cardbus bridge that contains the mmc controler */ +	/* the io region is already claimed by yenta_socket... */ +	while ((pci_dev = +		pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476, +			       pci_dev))) { +		/* try to init the device */ +		if (!sdricoh_init_mmc(pci_dev, pcmcia_dev)) { +			dev_info(&pcmcia_dev->dev, "MMC controller found\n"); +			return 0; +		} + +	} +	dev_err(&pcmcia_dev->dev, "No MMC controller was found.\n"); +	return -ENODEV; +} + +static void sdricoh_pcmcia_detach(struct pcmcia_device *link) +{ +	struct mmc_host *mmc = link->priv; + +	dev_dbg(&link->dev, "detach\n"); + +	/* remove mmc host */ +	if (mmc) { +		struct sdricoh_host *host = mmc_priv(mmc); +		mmc_remove_host(mmc); +		pci_iounmap(host->pci_dev, host->iobase); +		pci_dev_put(host->pci_dev); +		mmc_free_host(mmc); +	} +	pcmcia_disable_device(link); + +} + +#ifdef CONFIG_PM +static int sdricoh_pcmcia_suspend(struct pcmcia_device *link) +{ +	struct mmc_host *mmc = link->priv; +	dev_dbg(&link->dev, "suspend\n"); +	mmc_suspend_host(mmc, PMSG_SUSPEND); +	return 0; +} + +static int sdricoh_pcmcia_resume(struct pcmcia_device *link) +{ +	struct mmc_host *mmc = link->priv; +	dev_dbg(&link->dev, "resume\n"); +	sdricoh_reset(mmc_priv(mmc)); +	mmc_resume_host(mmc); +	return 0; +} +#else +#define sdricoh_pcmcia_suspend NULL +#define sdricoh_pcmcia_resume NULL +#endif + +static struct pcmcia_driver sdricoh_driver = { +	.drv = { +		.name = DRIVER_NAME, +		}, +	.probe = sdricoh_pcmcia_probe, +	.remove = sdricoh_pcmcia_detach, +	.id_table = pcmcia_ids, +	.suspend = sdricoh_pcmcia_suspend, +	.resume = sdricoh_pcmcia_resume, +}; + +/*****************************************************************************\ + *                                                                           * + * Driver init/exit                                                          * + *                                                                           * +\*****************************************************************************/ + +static int __init sdricoh_drv_init(void) +{ +	return pcmcia_register_driver(&sdricoh_driver); +} + +static void __exit sdricoh_drv_exit(void) +{ +	pcmcia_unregister_driver(&sdricoh_driver); +} + +module_init(sdricoh_drv_init); +module_exit(sdricoh_drv_exit); + +module_param(switchlocked, uint, 0444); + +MODULE_AUTHOR("Sascha Sommer <saschasommer@freenet.de>"); +MODULE_DESCRIPTION("Ricoh PCMCIA Secure Digital Interface driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM_DESC(switchlocked, "Switch the cards locked status." +		"Use this when unlocked cards are shown readonly (default 0)"); diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 1c14a186f00..13844843e8d 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -973,7 +973,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)  	mmc->ops = &tifm_sd_ops;  	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; -	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; +	mmc->caps = MMC_CAP_4_BIT_DATA;  	mmc->f_min = 20000000 / 60;  	mmc->f_max = 24000000; diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index c303e7f57ab..adda3795203 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -68,16 +68,16 @@ static const int unlock_codes[] = { 0x83, 0x87 };  static const int valid_ids[] = {  	0x7112, -	}; +};  #ifdef CONFIG_PNP -static unsigned int nopnp = 0; +static unsigned int param_nopnp = 0;  #else -static const unsigned int nopnp = 1; +static const unsigned int param_nopnp = 1;  #endif -static unsigned int io = 0x248; -static unsigned int irq = 6; -static int dma = 2; +static unsigned int param_io = 0x248; +static unsigned int param_irq = 6; +static int param_dma = 2;  /*   * Basic functions @@ -939,7 +939,7 @@ static int wbsd_get_ro(struct mmc_host *mmc)  	spin_unlock_bh(&host->lock); -	return csr & WBSD_WRPT; +	return !!(csr & WBSD_WRPT);  }  static const struct mmc_host_ops wbsd_ops = { @@ -1219,7 +1219,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)  	mmc->f_min = 375000;  	mmc->f_max = 24000000;  	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; -	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; +	mmc->caps = MMC_CAP_4_BIT_DATA;  	spin_lock_init(&host->lock); @@ -1420,7 +1420,7 @@ kfree:  	dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,  		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); -	host->dma_addr = (dma_addr_t)NULL; +	host->dma_addr = 0;  	kfree(host->dma_buffer);  	host->dma_buffer = NULL; @@ -1445,7 +1445,7 @@ static void wbsd_release_dma(struct wbsd_host *host)  	host->dma = -1;  	host->dma_buffer = NULL; -	host->dma_addr = (dma_addr_t)NULL; +	host->dma_addr = 0;  }  /* @@ -1765,7 +1765,7 @@ static void __devexit wbsd_shutdown(struct device *dev, int pnp)  static int __devinit wbsd_probe(struct platform_device *dev)  {  	/* Use the module parameters for resources */ -	return wbsd_init(&dev->dev, io, irq, dma, 0); +	return wbsd_init(&dev->dev, param_io, param_irq, param_dma, 0);  }  static int __devexit wbsd_remove(struct platform_device *dev) @@ -1979,14 +1979,14 @@ static int __init wbsd_drv_init(void)  #ifdef CONFIG_PNP -	if (!nopnp) { +	if (!param_nopnp) {  		result = pnp_register_driver(&wbsd_pnp_driver);  		if (result < 0)  			return result;  	}  #endif /* CONFIG_PNP */ -	if (nopnp) { +	if (param_nopnp) {  		result = platform_driver_register(&wbsd_driver);  		if (result < 0)  			return result; @@ -2012,12 +2012,12 @@ static void __exit wbsd_drv_exit(void)  {  #ifdef CONFIG_PNP -	if (!nopnp) +	if (!param_nopnp)  		pnp_unregister_driver(&wbsd_pnp_driver);  #endif /* CONFIG_PNP */ -	if (nopnp) { +	if (param_nopnp) {  		platform_device_unregister(wbsd_device);  		platform_driver_unregister(&wbsd_driver); @@ -2029,11 +2029,11 @@ static void __exit wbsd_drv_exit(void)  module_init(wbsd_drv_init);  module_exit(wbsd_drv_exit);  #ifdef CONFIG_PNP -module_param(nopnp, uint, 0444); +module_param_named(nopnp, param_nopnp, uint, 0444);  #endif -module_param(io, uint, 0444); -module_param(irq, uint, 0444); -module_param(dma, int, 0444); +module_param_named(io, param_io, uint, 0444); +module_param_named(irq, param_irq, uint, 0444); +module_param_named(dma, param_dma, int, 0444);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>"); diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 3dd537be87d..b54e2ea8346 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -1,7 +1,7 @@  /*   *  linux/drivers/net/wireless/libertas/if_sdio.c   * - *  Copyright 2007 Pierre Ossman + *  Copyright 2007-2008 Pierre Ossman   *   * Inspired by if_cs.c, Copyright 2007 Holger Schurig   * @@ -266,13 +266,10 @@ static int if_sdio_card_to_host(struct if_sdio_card *card)  	/*  	 * The transfer must be in one transaction or the firmware -	 * goes suicidal. +	 * goes suicidal. There's no way to guarantee that for all +	 * controllers, but we can at least try.  	 */ -	chunk = size; -	if ((chunk > card->func->cur_blksize) || (chunk > 512)) { -		chunk = (chunk + card->func->cur_blksize - 1) / -			card->func->cur_blksize * card->func->cur_blksize; -	} +	chunk = sdio_align_size(card->func, size);  	ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);  	if (ret) @@ -696,13 +693,10 @@ static int if_sdio_host_to_card(struct lbs_private *priv,  	/*  	 * The transfer must be in one transaction or the firmware -	 * goes suicidal. +	 * goes suicidal. There's no way to guarantee that for all +	 * controllers, but we can at least try.  	 */ -	size = nb + 4; -	if ((size > card->func->cur_blksize) || (size > 512)) { -		size = (size + card->func->cur_blksize - 1) / -			card->func->cur_blksize * card->func->cur_blksize; -	} +	size = sdio_align_size(card->func, nb + 4);  	packet = kzalloc(sizeof(struct if_sdio_packet) + size,  			GFP_ATOMIC);  |