diff options
Diffstat (limited to 'drivers/mmc')
85 files changed, 11142 insertions, 4135 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1a3163f1407..452782bffeb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -415,8 +415,7 @@ static int ioctl_do_sanitize(struct mmc_card *card)  {  	int err; -	if (!(mmc_can_sanitize(card) && -	      (card->host->caps2 & MMC_CAP2_SANITIZE))) { +	if (!mmc_can_sanitize(card)) {  			pr_warn("%s: %s - SANITIZE is not supported\n",  				mmc_hostname(card->host), __func__);  			err = -EOPNOTSUPP; @@ -722,19 +721,6 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)  	return result;  } -static int send_stop(struct mmc_card *card, u32 *status) -{ -	struct mmc_command cmd = {0}; -	int err; - -	cmd.opcode = MMC_STOP_TRANSMISSION; -	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; -	err = mmc_wait_for_cmd(card->host, &cmd, 5); -	if (err == 0) -		*status = cmd.resp[0]; -	return err; -} -  static int get_card_status(struct mmc_card *card, u32 *status, int retries)  {  	struct mmc_command cmd = {0}; @@ -750,6 +736,99 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries)  	return err;  } +static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, +		bool hw_busy_detect, struct request *req, int *gen_err) +{ +	unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); +	int err = 0; +	u32 status; + +	do { +		err = get_card_status(card, &status, 5); +		if (err) { +			pr_err("%s: error %d requesting status\n", +			       req->rq_disk->disk_name, err); +			return err; +		} + +		if (status & R1_ERROR) { +			pr_err("%s: %s: error sending status cmd, status %#x\n", +				req->rq_disk->disk_name, __func__, status); +			*gen_err = 1; +		} + +		/* We may rely on the host hw to handle busy detection.*/ +		if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && +			hw_busy_detect) +			break; + +		/* +		 * Timeout if the device never becomes ready for data and never +		 * leaves the program state. +		 */ +		if (time_after(jiffies, timeout)) { +			pr_err("%s: Card stuck in programming state! %s %s\n", +				mmc_hostname(card->host), +				req->rq_disk->disk_name, __func__); +			return -ETIMEDOUT; +		} + +		/* +		 * Some cards mishandle the status bits, +		 * so make sure to check both the busy +		 * indication and the card state. +		 */ +	} while (!(status & R1_READY_FOR_DATA) || +		 (R1_CURRENT_STATE(status) == R1_STATE_PRG)); + +	return err; +} + +static int send_stop(struct mmc_card *card, unsigned int timeout_ms, +		struct request *req, int *gen_err, u32 *stop_status) +{ +	struct mmc_host *host = card->host; +	struct mmc_command cmd = {0}; +	int err; +	bool use_r1b_resp = rq_data_dir(req) == WRITE; + +	/* +	 * Normally we use R1B responses for WRITE, but in cases where the host +	 * has specified a max_busy_timeout we need to validate it. A failure +	 * means we need to prevent the host from doing hw busy detection, which +	 * is done by converting to a R1 response instead. +	 */ +	if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout)) +		use_r1b_resp = false; + +	cmd.opcode = MMC_STOP_TRANSMISSION; +	if (use_r1b_resp) { +		cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; +		cmd.busy_timeout = timeout_ms; +	} else { +		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; +	} + +	err = mmc_wait_for_cmd(host, &cmd, 5); +	if (err) +		return err; + +	*stop_status = cmd.resp[0]; + +	/* No need to check card status in case of READ. */ +	if (rq_data_dir(req) == READ) +		return 0; + +	if (!mmc_host_is_spi(host) && +		(*stop_status & R1_ERROR)) { +		pr_err("%s: %s: general error sending stop command, resp %#x\n", +			req->rq_disk->disk_name, __func__, *stop_status); +		*gen_err = 1; +	} + +	return card_busy_detect(card, timeout_ms, use_r1b_resp, req, gen_err); +} +  #define ERR_NOMEDIUM	3  #define ERR_RETRY	2  #define ERR_ABORT	1 @@ -866,26 +945,21 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,  	 */  	if (R1_CURRENT_STATE(status) == R1_STATE_DATA ||  	    R1_CURRENT_STATE(status) == R1_STATE_RCV) { -		err = send_stop(card, &stop_status); -		if (err) +		err = send_stop(card, +			DIV_ROUND_UP(brq->data.timeout_ns, 1000000), +			req, gen_err, &stop_status); +		if (err) {  			pr_err("%s: error %d sending stop command\n",  			       req->rq_disk->disk_name, err); - -		/* -		 * If the stop cmd also timed out, the card is probably -		 * not present, so abort.  Other errors are bad news too. -		 */ -		if (err) +			/* +			 * If the stop cmd also timed out, the card is probably +			 * not present, so abort. Other errors are bad news too. +			 */  			return ERR_ABORT; +		} +  		if (stop_status & R1_CARD_ECC_FAILED)  			*ecc_err = 1; -		if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) -			if (stop_status & R1_ERROR) { -				pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n", -				       req->rq_disk->disk_name, __func__, -				       stop_status); -				*gen_err = 1; -			}  	}  	/* Check for set block count errors */ @@ -1157,8 +1231,7 @@ static int mmc_blk_err_check(struct mmc_card *card,  	 * program mode, which we have to wait for it to complete.  	 */  	if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { -		u32 status; -		unsigned long timeout; +		int err;  		/* Check stop command response */  		if (brq->stop.resp[0] & R1_ERROR) { @@ -1168,39 +1241,10 @@ static int mmc_blk_err_check(struct mmc_card *card,  			gen_err = 1;  		} -		timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS); -		do { -			int err = get_card_status(card, &status, 5); -			if (err) { -				pr_err("%s: error %d requesting status\n", -				       req->rq_disk->disk_name, err); -				return MMC_BLK_CMD_ERR; -			} - -			if (status & R1_ERROR) { -				pr_err("%s: %s: general error sending status command, card status %#x\n", -				       req->rq_disk->disk_name, __func__, -				       status); -				gen_err = 1; -			} - -			/* Timeout if the device never becomes ready for data -			 * and never leaves the program state. -			 */ -			if (time_after(jiffies, timeout)) { -				pr_err("%s: Card stuck in programming state!"\ -					" %s %s\n", mmc_hostname(card->host), -					req->rq_disk->disk_name, __func__); - -				return MMC_BLK_CMD_ERR; -			} -			/* -			 * Some cards mishandle the status bits, -			 * so make sure to check both the busy -			 * indication and the card state. -			 */ -		} while (!(status & R1_READY_FOR_DATA) || -			 (R1_CURRENT_STATE(status) == R1_STATE_PRG)); +		err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, false, req, +					&gen_err); +		if (err) +			return MMC_BLK_CMD_ERR;  	}  	/* if general error occurs, retry the write operation. */ @@ -1335,7 +1379,6 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,  	brq->data.blksz = 512;  	brq->stop.opcode = MMC_STOP_TRANSMISSION;  	brq->stop.arg = 0; -	brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;  	brq->data.blocks = blk_rq_sectors(req);  	/* @@ -1378,9 +1421,15 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,  	if (rq_data_dir(req) == READ) {  		brq->cmd.opcode = readcmd;  		brq->data.flags |= MMC_DATA_READ; +		if (brq->mrq.stop) +			brq->stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | +					MMC_CMD_AC;  	} else {  		brq->cmd.opcode = writecmd;  		brq->data.flags |= MMC_DATA_WRITE; +		if (brq->mrq.stop) +			brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | +					MMC_CMD_AC;  	}  	if (do_rel_wr) @@ -1959,6 +2008,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  	struct mmc_card *card = md->queue.card;  	struct mmc_host *host = card->host;  	unsigned long flags; +	unsigned int cmd_flags = req ? req->cmd_flags : 0;  	if (req && !mq->mqrq_prev->req)  		/* claim host only for the first request */ @@ -1974,7 +2024,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  	}  	mq->flags &= ~MMC_QUEUE_NEW_REQUEST; -	if (req && req->cmd_flags & REQ_DISCARD) { +	if (cmd_flags & REQ_DISCARD) {  		/* complete ongoing async transfer before issuing discard */  		if (card->host->areq)  			mmc_blk_issue_rw_rq(mq, NULL); @@ -1983,7 +2033,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  			ret = mmc_blk_issue_secdiscard_rq(mq, req);  		else  			ret = mmc_blk_issue_discard_rq(mq, req); -	} else if (req && req->cmd_flags & REQ_FLUSH) { +	} else if (cmd_flags & REQ_FLUSH) {  		/* complete ongoing async transfer before issuing flush */  		if (card->host->areq)  			mmc_blk_issue_rw_rq(mq, NULL); @@ -1999,7 +2049,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  out:  	if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || -	     (req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK))) +	     (cmd_flags & MMC_REQ_SPECIAL_MASK))  		/*  		 * Release host when there are no more requests  		 * and after special request(discard, flush) is done. @@ -2448,7 +2498,6 @@ static int _mmc_blk_suspend(struct mmc_card *card)  	struct mmc_blk_data *md = mmc_get_drvdata(card);  	if (md) { -		pm_runtime_get_sync(&card->dev);  		mmc_queue_suspend(&md->queue);  		list_for_each_entry(part_md, &md->part, part) {  			mmc_queue_suspend(&part_md->queue); @@ -2483,7 +2532,6 @@ static int mmc_blk_resume(struct mmc_card *card)  		list_for_each_entry(part_md, &md->part, part) {  			mmc_queue_resume(&part_md->queue);  		} -		pm_runtime_put(&card->dev);  	}  	return 0;  } diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index fa9632eb63f..3e049c13429 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -15,6 +15,7 @@  #include <linux/freezer.h>  #include <linux/kthread.h>  #include <linux/scatterlist.h> +#include <linux/dma-mapping.h>  #include <linux/mmc/card.h>  #include <linux/mmc/host.h> @@ -196,7 +197,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,  	struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];  	if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) -		limit = *mmc_dev(host)->dma_mask; +		limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT;  	mq->card = card;  	mq->queue = blk_init_queue(mmc_request_fn, lock); diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 269d072ef55..9ebee72d9c3 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -2,21 +2,6 @@  # MMC core configuration  # -config MMC_UNSAFE_RESUME -	bool "Assume MMC/SD cards are non-removable (DANGEROUS)" -	help -	  If you say Y here, the MMC layer will assume that all cards -	  stayed in their respective slots during the suspend. The -	  normal behaviour is to remove them at suspend and -	  redetecting them at resume. Breaking this assumption will -	  in most cases result in data corruption. - -	  This option is usually just for embedded systems which use -	  a MMC/SD card for rootfs. Most people should say N here. - -	  This option sets a default which can be overridden by the -	  module parameter "removable=0" or "removable=1". -  config MMC_CLKGATE  	bool "MMC host clock gating"  	help diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 704bf66f587..d2dbf02022b 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -27,7 +27,7 @@  #define to_mmc_driver(d)	container_of(d, struct mmc_driver, drv) -static ssize_t mmc_type_show(struct device *dev, +static ssize_t type_show(struct device *dev,  	struct device_attribute *attr, char *buf)  {  	struct mmc_card *card = mmc_dev_to_card(dev); @@ -45,11 +45,13 @@ static ssize_t mmc_type_show(struct device *dev,  		return -EFAULT;  	}  } +static DEVICE_ATTR_RO(type); -static struct device_attribute mmc_dev_attrs[] = { -	__ATTR(type, S_IRUGO, mmc_type_show, NULL), -	__ATTR_NULL, +static struct attribute *mmc_dev_attrs[] = { +	&dev_attr_type.attr, +	NULL,  }; +ATTRIBUTE_GROUPS(mmc_dev);  /*   * This currently matches any MMC driver to any MMC card - drivers @@ -183,24 +185,16 @@ static int mmc_runtime_suspend(struct device *dev)  {  	struct mmc_card *card = mmc_dev_to_card(dev);  	struct mmc_host *host = card->host; -	int ret = 0; -	if (host->bus_ops->runtime_suspend) -		ret = host->bus_ops->runtime_suspend(host); - -	return ret; +	return host->bus_ops->runtime_suspend(host);  }  static int mmc_runtime_resume(struct device *dev)  {  	struct mmc_card *card = mmc_dev_to_card(dev);  	struct mmc_host *host = card->host; -	int ret = 0; -	if (host->bus_ops->runtime_resume) -		ret = host->bus_ops->runtime_resume(host); - -	return ret; +	return host->bus_ops->runtime_resume(host);  }  static int mmc_runtime_idle(struct device *dev) @@ -218,7 +212,7 @@ static const struct dev_pm_ops mmc_bus_pm_ops = {  static struct bus_type mmc_bus_type = {  	.name		= "mmc", -	.dev_attrs	= mmc_dev_attrs, +	.dev_groups	= mmc_dev_groups,  	.match		= mmc_bus_match,  	.uevent		= mmc_bus_uevent,  	.probe		= mmc_bus_probe, @@ -340,23 +334,24 @@ int mmc_add_card(struct mmc_card *card)  		break;  	} -	if (mmc_sd_card_uhs(card) && +	if (mmc_card_uhs(card) &&  		(card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))  		uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];  	if (mmc_host_is_spi(card->host)) {  		pr_info("%s: new %s%s%s card on SPI\n",  			mmc_hostname(card->host), -			mmc_card_highspeed(card) ? "high speed " : "", -			mmc_card_ddr_mode(card) ? "DDR " : "", +			mmc_card_hs(card) ? "high speed " : "", +			mmc_card_ddr52(card) ? "DDR " : "",  			type);  	} else {  		pr_info("%s: new %s%s%s%s%s card at address %04x\n",  			mmc_hostname(card->host),  			mmc_card_uhs(card) ? "ultra high speed " : -			(mmc_card_highspeed(card) ? "high speed " : ""), +			(mmc_card_hs(card) ? "high speed " : ""), +			mmc_card_hs400(card) ? "HS400 " :  			(mmc_card_hs200(card) ? "HS200 " : ""), -			mmc_card_ddr_mode(card) ? "DDR " : "", +			mmc_card_ddr52(card) ? "DDR " : "",  			uhs_bus_speed_mode, type, card->rca);  	} diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index bf18b6bfce4..7dc0c85fdb6 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -23,6 +23,7 @@  #include <linux/log2.h>  #include <linux/regulator/consumer.h>  #include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h>  #include <linux/suspend.h>  #include <linux/fault-inject.h>  #include <linux/random.h> @@ -33,6 +34,7 @@  #include <linux/mmc/host.h>  #include <linux/mmc/mmc.h>  #include <linux/mmc/sd.h> +#include <linux/mmc/slot-gpio.h>  #include "core.h"  #include "bus.h" @@ -64,23 +66,6 @@ bool use_spi_crc = 1;  module_param(use_spi_crc, bool, 0);  /* - * We normally treat cards as removed during suspend if they are not - * known to be on a non-removable bus, to avoid the risk of writing - * back data to a different card after resume.  Allow this to be - * overridden if necessary. - */ -#ifdef CONFIG_MMC_UNSAFE_RESUME -bool mmc_assume_removable; -#else -bool mmc_assume_removable = 1; -#endif -EXPORT_SYMBOL(mmc_assume_removable); -module_param_named(removable, mmc_assume_removable, bool, 0644); -MODULE_PARM_DESC( -	removable, -	"MMC/SD cards are removable and may be removed during suspend"); - -/*   * Internal function. Schedule delayed work in the MMC work queue.   */  static int mmc_schedule_delayed_work(struct delayed_work *work, @@ -301,7 +286,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)  	}  	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -			EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal); +			EXT_CSD_BKOPS_START, 1, timeout, +			use_busy_signal, true, false);  	if (err) {  		pr_warn("%s: Error %d starting bkops\n",  			mmc_hostname(card->host), err); @@ -814,6 +800,10 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)  			data->timeout_ns = limit_us * 1000;  			data->timeout_clks = 0;  		} + +		/* assign limit value if invalid */ +		if (timeout_us == 0) +			data->timeout_ns = limit_us * 1000;  	}  	/* @@ -918,31 +908,6 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)  EXPORT_SYMBOL(__mmc_claim_host);  /** - *	mmc_try_claim_host - try exclusively to claim a host - *	@host: mmc host to claim - * - *	Returns %1 if the host is claimed, %0 otherwise. - */ -int mmc_try_claim_host(struct mmc_host *host) -{ -	int claimed_host = 0; -	unsigned long flags; - -	spin_lock_irqsave(&host->lock, flags); -	if (!host->claimed || host->claimer == current) { -		host->claimed = 1; -		host->claimer = current; -		host->claim_cnt += 1; -		claimed_host = 1; -	} -	spin_unlock_irqrestore(&host->lock, flags); -	if (host->ops->enable && claimed_host && host->claim_cnt == 1) -		host->ops->enable(host); -	return claimed_host; -} -EXPORT_SYMBOL(mmc_try_claim_host); - -/**   *	mmc_release_host - release a host   *	@host: mmc host to release   * @@ -1349,31 +1314,38 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,  }  EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr); +#endif /* CONFIG_REGULATOR */ +  int mmc_regulator_get_supply(struct mmc_host *mmc)  {  	struct device *dev = mmc_dev(mmc); -	struct regulator *supply;  	int ret; -	supply = devm_regulator_get(dev, "vmmc"); -	mmc->supply.vmmc = supply; +	mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");  	mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc"); -	if (IS_ERR(supply)) -		return PTR_ERR(supply); +	if (IS_ERR(mmc->supply.vmmc)) { +		if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER) +			return -EPROBE_DEFER; +		dev_info(dev, "No vmmc regulator found\n"); +	} else { +		ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc); +		if (ret > 0) +			mmc->ocr_avail = ret; +		else +			dev_warn(dev, "Failed getting OCR mask: %d\n", ret); +	} -	ret = mmc_regulator_get_ocrmask(supply); -	if (ret > 0) -		mmc->ocr_avail = ret; -	else -		dev_warn(mmc_dev(mmc), "Failed getting OCR mask: %d\n", ret); +	if (IS_ERR(mmc->supply.vqmmc)) { +		if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER) +			return -EPROBE_DEFER; +		dev_info(dev, "No vqmmc regulator found\n"); +	}  	return 0;  }  EXPORT_SYMBOL_GPL(mmc_regulator_get_supply); -#endif /* CONFIG_REGULATOR */ -  /*   * Mask off any voltages we don't support and select   * the lowest voltage @@ -1382,22 +1354,31 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)  {  	int bit; -	ocr &= host->ocr_avail; +	/* +	 * Sanity check the voltages that the card claims to +	 * support. +	 */ +	if (ocr & 0x7F) { +		dev_warn(mmc_dev(host), +		"card claims to support voltages below defined range\n"); +		ocr &= ~0x7F; +	} -	bit = ffs(ocr); -	if (bit) { -		bit -= 1; +	ocr &= host->ocr_avail; +	if (!ocr) { +		dev_warn(mmc_dev(host), "no support for card's volts\n"); +		return 0; +	} +	if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) { +		bit = ffs(ocr) - 1;  		ocr &= 3 << bit; - -		mmc_host_clk_hold(host); -		host->ios.vdd = bit; -		mmc_set_ios(host); -		mmc_host_clk_release(host); +		mmc_power_cycle(host, ocr);  	} else { -		pr_warning("%s: host doesn't support card's voltages\n", -				mmc_hostname(host)); -		ocr = 0; +		bit = fls(ocr) - 1; +		ocr &= 3 << bit; +		if (bit != host->ios.vdd) +			dev_warn(mmc_dev(host), "exceeding card's volts\n");  	}  	return ocr; @@ -1422,7 +1403,7 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)  } -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)  {  	struct mmc_command cmd = {0};  	int err = 0; @@ -1504,7 +1485,7 @@ power_cycle:  	if (err) {  		pr_debug("%s: Signal voltage switch failed, "  			"power cycling card\n", mmc_hostname(host)); -		mmc_power_cycle(host); +		mmc_power_cycle(host, ocr);  	}  	mmc_host_clk_release(host); @@ -1545,22 +1526,14 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)   * If a host does all the power sequencing itself, ignore the   * initial MMC_POWER_UP stage.   */ -void mmc_power_up(struct mmc_host *host) +void mmc_power_up(struct mmc_host *host, u32 ocr)  { -	int bit; -  	if (host->ios.power_mode == MMC_POWER_ON)  		return;  	mmc_host_clk_hold(host); -	/* If ocr is set, we use it */ -	if (host->ocr) -		bit = ffs(host->ocr) - 1; -	else -		bit = fls(host->ocr_avail) - 1; - -	host->ios.vdd = bit; +	host->ios.vdd = fls(ocr) - 1;  	if (mmc_host_is_spi(host))  		host->ios.chip_select = MMC_CS_HIGH;  	else @@ -1571,8 +1544,13 @@ void mmc_power_up(struct mmc_host *host)  	host->ios.timing = MMC_TIMING_LEGACY;  	mmc_set_ios(host); -	/* Set signal voltage to 3.3V */ -	__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); +	/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ +	if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0) +		dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n"); +	else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180) == 0) +		dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n"); +	else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120) == 0) +		dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");  	/*  	 * This delay should be sufficient to allow the power supply @@ -1604,13 +1582,6 @@ void mmc_power_off(struct mmc_host *host)  	host->ios.clock = 0;  	host->ios.vdd = 0; - -	/* -	 * Reset ocr mask to be the highest possible voltage supported for -	 * this mmc host. This value will be used at next power up. -	 */ -	host->ocr = 1 << (fls(host->ocr_avail) - 1); -  	if (!mmc_host_is_spi(host)) {  		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;  		host->ios.chip_select = MMC_CS_DONTCARE; @@ -1630,12 +1601,12 @@ void mmc_power_off(struct mmc_host *host)  	mmc_host_clk_release(host);  } -void mmc_power_cycle(struct mmc_host *host) +void mmc_power_cycle(struct mmc_host *host, u32 ocr)  {  	mmc_power_off(host);  	/* Wait at least 1 ms according to SD spec */  	mmc_delay(1); -	mmc_power_up(host); +	mmc_power_up(host, ocr);  }  /* @@ -1723,6 +1694,28 @@ void mmc_detach_bus(struct mmc_host *host)  	mmc_bus_put(host);  } +static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, +				bool cd_irq) +{ +#ifdef CONFIG_MMC_DEBUG +	unsigned long flags; +	spin_lock_irqsave(&host->lock, flags); +	WARN_ON(host->removed); +	spin_unlock_irqrestore(&host->lock, flags); +#endif + +	/* +	 * If the device is configured as wakeup, we prevent a new sleep for +	 * 5 s to give provision for user space to consume the event. +	 */ +	if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) && +		device_can_wakeup(mmc_dev(host))) +		pm_wakeup_event(mmc_dev(host), 5000); + +	host->detect_change = 1; +	mmc_schedule_delayed_work(&host->detect, delay); +} +  /**   *	mmc_detect_change - process change of state on a MMC socket   *	@host: host which changed state. @@ -1735,16 +1728,8 @@ void mmc_detach_bus(struct mmc_host *host)   */  void mmc_detect_change(struct mmc_host *host, unsigned long delay)  { -#ifdef CONFIG_MMC_DEBUG -	unsigned long flags; -	spin_lock_irqsave(&host->lock, flags); -	WARN_ON(host->removed); -	spin_unlock_irqrestore(&host->lock, flags); -#endif -	host->detect_change = 1; -	mmc_schedule_delayed_work(&host->detect, delay); +	_mmc_detect_change(host, delay, true);  } -  EXPORT_SYMBOL(mmc_detect_change);  void mmc_init_erase(struct mmc_card *card) @@ -1966,7 +1951,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,  	cmd.opcode = MMC_ERASE;  	cmd.arg = arg;  	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; -	cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty); +	cmd.busy_timeout = mmc_erase_timeout(card, arg, qty);  	err = mmc_wait_for_cmd(card->host, &cmd, 0);  	if (err) {  		pr_err("mmc_erase: erase error %d, status %#x\n", @@ -2153,7 +2138,7 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,  		y = 0;  		for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {  			timeout = mmc_erase_timeout(card, arg, qty + x); -			if (timeout > host->max_discard_to) +			if (timeout > host->max_busy_timeout)  				break;  			if (timeout < last_timeout)  				break; @@ -2185,7 +2170,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)  	struct mmc_host *host = card->host;  	unsigned int max_discard, max_trim; -	if (!host->max_discard_to) +	if (!host->max_busy_timeout)  		return UINT_MAX;  	/* @@ -2205,7 +2190,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)  		max_discard = 0;  	}  	pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n", -		 mmc_hostname(host), max_discard, host->max_discard_to); +		 mmc_hostname(host), max_discard, host->max_busy_timeout);  	return max_discard;  }  EXPORT_SYMBOL(mmc_calc_max_discard); @@ -2214,7 +2199,7 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)  {  	struct mmc_command cmd = {0}; -	if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card)) +	if (mmc_card_blockaddr(card) || mmc_card_ddr52(card))  		return 0;  	cmd.opcode = MMC_SET_BLOCKLEN; @@ -2264,9 +2249,6 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)  {  	struct mmc_card *card = host->card; -	if (!host->bus_ops->power_restore) -		return -EOPNOTSUPP; -  	if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)  		return -EOPNOTSUPP; @@ -2297,7 +2279,6 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)  		}  	} -	host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR);  	if (mmc_host_is_spi(host)) {  		host->ios.chip_select = MMC_CS_HIGH;  		host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; @@ -2334,7 +2315,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)  	pr_info("%s: %s: trying to init card at %u Hz\n",  		mmc_hostname(host), __func__, host->f_init);  #endif -	mmc_power_up(host); +	mmc_power_up(host, host->ocr_avail);  	/*  	 * Some eMMCs (with VCCQ always on) may not be reset after power up, so @@ -2368,7 +2349,7 @@ int _mmc_detect_card_removed(struct mmc_host *host)  {  	int ret; -	if ((host->caps & MMC_CAP_NONREMOVABLE) || !host->bus_ops->alive) +	if (host->caps & MMC_CAP_NONREMOVABLE)  		return 0;  	if (!host->card || mmc_card_removed(host->card)) @@ -2423,7 +2404,7 @@ int mmc_detect_card_removed(struct mmc_host *host)  			 * rescan handle the card removal.  			 */  			cancel_delayed_work(&host->detect); -			mmc_detect_change(host, 0); +			_mmc_detect_change(host, 0, false);  		}  	} @@ -2437,6 +2418,11 @@ void mmc_rescan(struct work_struct *work)  		container_of(work, struct mmc_host, detect.work);  	int i; +	if (host->trigger_card_event && host->ops->card_event) { +		host->ops->card_event(host); +		host->trigger_card_event = false; +	} +  	if (host->rescan_disable)  		return; @@ -2451,7 +2437,7 @@ void mmc_rescan(struct work_struct *work)  	 * if there is a _removable_ card registered, check whether it is  	 * still present  	 */ -	if (host->bus_ops && host->bus_ops->detect && !host->bus_dead +	if (host->bus_ops && !host->bus_dead  	    && !(host->caps & MMC_CAP_NONREMOVABLE))  		host->bus_ops->detect(host); @@ -2476,7 +2462,8 @@ void mmc_rescan(struct work_struct *work)  	 */  	mmc_bus_put(host); -	if (host->ops->get_cd && host->ops->get_cd(host) == 0) { +	if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd && +			host->ops->get_cd(host) == 0) {  		mmc_claim_host(host);  		mmc_power_off(host);  		mmc_release_host(host); @@ -2504,8 +2491,9 @@ void mmc_start_host(struct mmc_host *host)  	if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)  		mmc_power_off(host);  	else -		mmc_power_up(host); -	mmc_detect_change(host, 0); +		mmc_power_up(host, host->ocr_avail); +	mmc_gpiod_request_cd_irq(host); +	_mmc_detect_change(host, 0, false);  }  void mmc_stop_host(struct mmc_host *host) @@ -2516,6 +2504,8 @@ void mmc_stop_host(struct mmc_host *host)  	host->removed = 1;  	spin_unlock_irqrestore(&host->lock, flags);  #endif +	if (host->slot.cd_irq >= 0) +		disable_irq(host->slot.cd_irq);  	host->rescan_disable = 1;  	cancel_delayed_work_sync(&host->detect); @@ -2552,7 +2542,7 @@ int mmc_power_save_host(struct mmc_host *host)  	mmc_bus_get(host); -	if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { +	if (!host->bus_ops || host->bus_dead) {  		mmc_bus_put(host);  		return -EINVAL;  	} @@ -2578,12 +2568,12 @@ int mmc_power_restore_host(struct mmc_host *host)  	mmc_bus_get(host); -	if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { +	if (!host->bus_ops || host->bus_dead) {  		mmc_bus_put(host);  		return -EINVAL;  	} -	mmc_power_up(host); +	mmc_power_up(host, host->card->ocr);  	ret = host->bus_ops->power_restore(host);  	mmc_bus_put(host); @@ -2597,12 +2587,8 @@ EXPORT_SYMBOL(mmc_power_restore_host);   */  int mmc_flush_cache(struct mmc_card *card)  { -	struct mmc_host *host = card->host;  	int err = 0; -	if (!(host->caps2 & MMC_CAP2_CACHE_CTRL)) -		return err; -  	if (mmc_card_mmc(card) &&  			(card->ext_csd.cache_size > 0) &&  			(card->ext_csd.cache_ctrl & 1)) { @@ -2617,68 +2603,8 @@ int mmc_flush_cache(struct mmc_card *card)  }  EXPORT_SYMBOL(mmc_flush_cache); -/* - * Turn the cache ON/OFF. - * Turning the cache OFF shall trigger flushing of the data - * to the non-volatile storage. - * This function should be called with host claimed - */ -int mmc_cache_ctrl(struct mmc_host *host, u8 enable) -{ -	struct mmc_card *card = host->card; -	unsigned int timeout; -	int err = 0; - -	if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) || -			mmc_card_is_removable(host)) -		return err; - -	if (card && mmc_card_mmc(card) && -			(card->ext_csd.cache_size > 0)) { -		enable = !!enable; - -		if (card->ext_csd.cache_ctrl ^ enable) { -			timeout = enable ? card->ext_csd.generic_cmd6_time : 0; -			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -					EXT_CSD_CACHE_CTRL, enable, timeout); -			if (err) -				pr_err("%s: cache %s error %d\n", -						mmc_hostname(card->host), -						enable ? "on" : "off", -						err); -			else -				card->ext_csd.cache_ctrl = enable; -		} -	} - -	return err; -} -EXPORT_SYMBOL(mmc_cache_ctrl); -  #ifdef CONFIG_PM -/** - *	mmc_suspend_host - suspend a host - *	@host: mmc host - */ -int mmc_suspend_host(struct mmc_host *host) -{ -	/* This function is deprecated */ -	return 0; -} -EXPORT_SYMBOL(mmc_suspend_host); - -/** - *	mmc_resume_host - resume a previously suspended host - *	@host: mmc host - */ -int mmc_resume_host(struct mmc_host *host) -{ -	/* This function is deprecated */ -	return 0; -} -EXPORT_SYMBOL(mmc_resume_host); -  /* Do the card removal on suspend if card is assumed removeable   * Do that in pm notifier while userspace isn't yet frozen, so we will be able     to sync the card. @@ -2705,7 +2631,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,  		/* Validate prerequisites for suspend */  		if (host->bus_ops->pre_suspend)  			err = host->bus_ops->pre_suspend(host); -		if (!err && host->bus_ops->suspend) +		if (!err)  			break;  		/* Calling bus_ops->remove() with a claimed host can deadlock */ @@ -2724,7 +2650,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,  		spin_lock_irqsave(&host->lock, flags);  		host->rescan_disable = 0;  		spin_unlock_irqrestore(&host->lock, flags); -		mmc_detect_change(host, 0); +		_mmc_detect_change(host, 0, false);  	} diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 5345d156493..443a584660f 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -42,13 +42,13 @@ void mmc_set_ungated(struct mmc_host *host);  void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);  void mmc_set_bus_width(struct mmc_host *host, unsigned int width);  u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);  int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);  void mmc_set_timing(struct mmc_host *host, unsigned int timing);  void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); -void mmc_power_up(struct mmc_host *host); +void mmc_power_up(struct mmc_host *host, u32 ocr);  void mmc_power_off(struct mmc_host *host); -void mmc_power_cycle(struct mmc_host *host); +void mmc_power_cycle(struct mmc_host *host, u32 ocr);  static inline void mmc_delay(unsigned int ms)  { diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 54829c0ed00..91eb1622324 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -135,8 +135,14 @@ static int mmc_ios_show(struct seq_file *s, void *data)  	case MMC_TIMING_UHS_DDR50:  		str = "sd uhs DDR50";  		break; +	case MMC_TIMING_MMC_DDR52: +		str = "mmc DDR52"; +		break;  	case MMC_TIMING_MMC_HS200: -		str = "mmc high-speed SDR200"; +		str = "mmc HS200"; +		break; +	case MMC_TIMING_MMC_HS400: +		str = "mmc HS400";  		break;  	default:  		str = "invalid"; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 49bc403e31f..95cceae9694 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -337,7 +337,7 @@ int mmc_of_parse(struct mmc_host *host)  		break;  	default:  		dev_err(host->parent, -			"Invalid \"bus-width\" value %ud!\n", bus_width); +			"Invalid \"bus-width\" value %u!\n", bus_width);  		return -EINVAL;  	} @@ -419,6 +419,16 @@ int mmc_of_parse(struct mmc_host *host)  		host->caps |= MMC_CAP_SD_HIGHSPEED;  	if (of_find_property(np, "cap-mmc-highspeed", &len))  		host->caps |= MMC_CAP_MMC_HIGHSPEED; +	if (of_find_property(np, "sd-uhs-sdr12", &len)) +		host->caps |= MMC_CAP_UHS_SDR12; +	if (of_find_property(np, "sd-uhs-sdr25", &len)) +		host->caps |= MMC_CAP_UHS_SDR25; +	if (of_find_property(np, "sd-uhs-sdr50", &len)) +		host->caps |= MMC_CAP_UHS_SDR50; +	if (of_find_property(np, "sd-uhs-sdr104", &len)) +		host->caps |= MMC_CAP_UHS_SDR104; +	if (of_find_property(np, "sd-uhs-ddr50", &len)) +		host->caps |= MMC_CAP_UHS_DDR50;  	if (of_find_property(np, "cap-power-off-card", &len))  		host->caps |= MMC_CAP_POWER_OFF_CARD;  	if (of_find_property(np, "cap-sdio-irq", &len)) @@ -429,6 +439,18 @@ int mmc_of_parse(struct mmc_host *host)  		host->pm_caps |= MMC_PM_KEEP_POWER;  	if (of_find_property(np, "enable-sdio-wakeup", &len))  		host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; +	if (of_find_property(np, "mmc-ddr-1_8v", &len)) +		host->caps |= MMC_CAP_1_8V_DDR; +	if (of_find_property(np, "mmc-ddr-1_2v", &len)) +		host->caps |= MMC_CAP_1_2V_DDR; +	if (of_find_property(np, "mmc-hs200-1_8v", &len)) +		host->caps2 |= MMC_CAP2_HS200_1_8V_SDR; +	if (of_find_property(np, "mmc-hs200-1_2v", &len)) +		host->caps2 |= MMC_CAP2_HS200_1_2V_SDR; +	if (of_find_property(np, "mmc-hs400-1_8v", &len)) +		host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR; +	if (of_find_property(np, "mmc-hs400-1_2v", &len)) +		host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;  	return 0; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6d02012a1d0..793c6f7ddb0 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -13,6 +13,7 @@  #include <linux/err.h>  #include <linux/slab.h>  #include <linux/stat.h> +#include <linux/pm_runtime.h>  #include <linux/mmc/host.h>  #include <linux/mmc/card.h> @@ -239,31 +240,62 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)  static void mmc_select_card_type(struct mmc_card *card)  {  	struct mmc_host *host = card->host; -	u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; +	u8 card_type = card->ext_csd.raw_card_type;  	u32 caps = host->caps, caps2 = host->caps2; -	unsigned int hs_max_dtr = 0; +	unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; +	unsigned int avail_type = 0; -	if (card_type & EXT_CSD_CARD_TYPE_26) +	if (caps & MMC_CAP_MMC_HIGHSPEED && +	    card_type & EXT_CSD_CARD_TYPE_HS_26) {  		hs_max_dtr = MMC_HIGH_26_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_HS_26; +	}  	if (caps & MMC_CAP_MMC_HIGHSPEED && -			card_type & EXT_CSD_CARD_TYPE_52) +	    card_type & EXT_CSD_CARD_TYPE_HS_52) {  		hs_max_dtr = MMC_HIGH_52_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_HS_52; +	} + +	if (caps & MMC_CAP_1_8V_DDR && +	    card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) { +		hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; +	} -	if ((caps & MMC_CAP_1_8V_DDR && -			card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) || -	    (caps & MMC_CAP_1_2V_DDR && -			card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)) +	if (caps & MMC_CAP_1_2V_DDR && +	    card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) {  		hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V; +	} + +	if (caps2 & MMC_CAP2_HS200_1_8V_SDR && +	    card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) { +		hs200_max_dtr = MMC_HS200_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V; +	} + +	if (caps2 & MMC_CAP2_HS200_1_2V_SDR && +	    card_type & EXT_CSD_CARD_TYPE_HS200_1_2V) { +		hs200_max_dtr = MMC_HS200_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V; +	} -	if ((caps2 & MMC_CAP2_HS200_1_8V_SDR && -			card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) || -	    (caps2 & MMC_CAP2_HS200_1_2V_SDR && -			card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)) -		hs_max_dtr = MMC_HS200_MAX_DTR; +	if (caps2 & MMC_CAP2_HS400_1_8V && +	    card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) { +		hs200_max_dtr = MMC_HS200_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_HS400_1_8V; +	} + +	if (caps2 & MMC_CAP2_HS400_1_2V && +	    card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) { +		hs200_max_dtr = MMC_HS200_MAX_DTR; +		avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V; +	}  	card->ext_csd.hs_max_dtr = hs_max_dtr; -	card->ext_csd.card_type = card_type; +	card->ext_csd.hs200_max_dtr = hs200_max_dtr; +	card->mmc_avail_type = avail_type;  }  /* @@ -479,6 +511,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)  			ext_csd[EXT_CSD_PWR_CL_DDR_52_195];  		card->ext_csd.raw_pwr_cl_ddr_52_360 =  			ext_csd[EXT_CSD_PWR_CL_DDR_52_360]; +		card->ext_csd.raw_pwr_cl_ddr_200_360 = +			ext_csd[EXT_CSD_PWR_CL_DDR_200_360];  	}  	if (card->ext_csd.rev >= 5) { @@ -645,7 +679,10 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)  		(card->ext_csd.raw_pwr_cl_ddr_52_195 ==  			bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&  		(card->ext_csd.raw_pwr_cl_ddr_52_360 == -			bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360])); +			bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) && +		(card->ext_csd.raw_pwr_cl_ddr_200_360 == +			bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360])); +  	if (err)  		err = -EINVAL; @@ -693,18 +730,10 @@ static struct attribute *mmc_std_attrs[] = {  	&dev_attr_rel_sectors.attr,  	NULL,  }; - -static struct attribute_group mmc_std_attr_group = { -	.attrs = mmc_std_attrs, -}; - -static const struct attribute_group *mmc_attr_groups[] = { -	&mmc_std_attr_group, -	NULL, -}; +ATTRIBUTE_GROUPS(mmc_std);  static struct device_type mmc_type = { -	.groups = mmc_attr_groups, +	.groups = mmc_std_groups,  };  /* @@ -713,17 +742,13 @@ static struct device_type mmc_type = {   * extended CSD register, select it by executing the   * mmc_switch command.   */ -static int mmc_select_powerclass(struct mmc_card *card, -		unsigned int bus_width) +static int __mmc_select_powerclass(struct mmc_card *card, +				   unsigned int bus_width)  { -	int err = 0; +	struct mmc_host *host = card->host; +	struct mmc_ext_csd *ext_csd = &card->ext_csd;  	unsigned int pwrclass_val = 0; -	struct mmc_host *host; - -	BUG_ON(!card); - -	host = card->host; -	BUG_ON(!host); +	int err = 0;  	/* Power class selection is supported for versions >= 4.0 */  	if (card->csd.mmca_vsn < CSD_SPEC_VER_4) @@ -735,14 +760,14 @@ static int mmc_select_powerclass(struct mmc_card *card,  	switch (1 << host->ios.vdd) {  	case MMC_VDD_165_195: -		if (host->ios.clock <= 26000000) -			pwrclass_val = card->ext_csd.raw_pwr_cl_26_195; -		else if	(host->ios.clock <= 52000000) +		if (host->ios.clock <= MMC_HIGH_26_MAX_DTR) +			pwrclass_val = ext_csd->raw_pwr_cl_26_195; +		else if (host->ios.clock <= MMC_HIGH_52_MAX_DTR)  			pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? -				card->ext_csd.raw_pwr_cl_52_195 : -				card->ext_csd.raw_pwr_cl_ddr_52_195; -		else if (host->ios.clock <= 200000000) -			pwrclass_val = card->ext_csd.raw_pwr_cl_200_195; +				ext_csd->raw_pwr_cl_52_195 : +				ext_csd->raw_pwr_cl_ddr_52_195; +		else if (host->ios.clock <= MMC_HS200_MAX_DTR) +			pwrclass_val = ext_csd->raw_pwr_cl_200_195;  		break;  	case MMC_VDD_27_28:  	case MMC_VDD_28_29: @@ -753,14 +778,16 @@ static int mmc_select_powerclass(struct mmc_card *card,  	case MMC_VDD_33_34:  	case MMC_VDD_34_35:  	case MMC_VDD_35_36: -		if (host->ios.clock <= 26000000) -			pwrclass_val = card->ext_csd.raw_pwr_cl_26_360; -		else if	(host->ios.clock <= 52000000) +		if (host->ios.clock <= MMC_HIGH_26_MAX_DTR) +			pwrclass_val = ext_csd->raw_pwr_cl_26_360; +		else if (host->ios.clock <= MMC_HIGH_52_MAX_DTR)  			pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? -				card->ext_csd.raw_pwr_cl_52_360 : -				card->ext_csd.raw_pwr_cl_ddr_52_360; -		else if (host->ios.clock <= 200000000) -			pwrclass_val = card->ext_csd.raw_pwr_cl_200_360; +				ext_csd->raw_pwr_cl_52_360 : +				ext_csd->raw_pwr_cl_ddr_52_360; +		else if (host->ios.clock <= MMC_HS200_MAX_DTR) +			pwrclass_val = (bus_width == EXT_CSD_DDR_BUS_WIDTH_8) ? +				ext_csd->raw_pwr_cl_ddr_200_360 : +				ext_csd->raw_pwr_cl_200_360;  		break;  	default:  		pr_warning("%s: Voltage range not supported " @@ -786,40 +813,79 @@ static int mmc_select_powerclass(struct mmc_card *card,  	return err;  } +static int mmc_select_powerclass(struct mmc_card *card) +{ +	struct mmc_host *host = card->host; +	u32 bus_width, ext_csd_bits; +	int err, ddr; + +	/* Power class selection is supported for versions >= 4.0 */ +	if (card->csd.mmca_vsn < CSD_SPEC_VER_4) +		return 0; + +	bus_width = host->ios.bus_width; +	/* Power class values are defined only for 4/8 bit bus */ +	if (bus_width == MMC_BUS_WIDTH_1) +		return 0; + +	ddr = card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52; +	if (ddr) +		ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? +			EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; +	else +		ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? +			EXT_CSD_BUS_WIDTH_8 :  EXT_CSD_BUS_WIDTH_4; + +	err = __mmc_select_powerclass(card, ext_csd_bits); +	if (err) +		pr_warn("%s: power class selection to bus width %d ddr %d failed\n", +			mmc_hostname(host), 1 << bus_width, ddr); + +	return err; +} +  /* - * Selects the desired buswidth and switch to the HS200 mode - * if bus width set without error + * Set the bus speed for the selected speed mode.   */ -static int mmc_select_hs200(struct mmc_card *card) +static void mmc_set_bus_speed(struct mmc_card *card) +{ +	unsigned int max_dtr = (unsigned int)-1; + +	if ((mmc_card_hs200(card) || mmc_card_hs400(card)) && +	     max_dtr > card->ext_csd.hs200_max_dtr) +		max_dtr = card->ext_csd.hs200_max_dtr; +	else if (mmc_card_hs(card) && max_dtr > card->ext_csd.hs_max_dtr) +		max_dtr = card->ext_csd.hs_max_dtr; +	else if (max_dtr > card->csd.max_dtr) +		max_dtr = card->csd.max_dtr; + +	mmc_set_clock(card->host, max_dtr); +} + +/* + * Select the bus width amoung 4-bit and 8-bit(SDR). + * If the bus width is changed successfully, return the selected width value. + * Zero is returned instead of error value if the wide width is not supported. + */ +static int mmc_select_bus_width(struct mmc_card *card)  { -	int idx, err = -EINVAL; -	struct mmc_host *host;  	static unsigned ext_csd_bits[] = { -		EXT_CSD_BUS_WIDTH_4,  		EXT_CSD_BUS_WIDTH_8, +		EXT_CSD_BUS_WIDTH_4,  	};  	static unsigned bus_widths[] = { -		MMC_BUS_WIDTH_4,  		MMC_BUS_WIDTH_8, +		MMC_BUS_WIDTH_4,  	}; +	struct mmc_host *host = card->host; +	unsigned idx, bus_width = 0; +	int err = 0; -	BUG_ON(!card); - -	host = card->host; - -	if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && -			host->caps2 & MMC_CAP2_HS200_1_2V_SDR) -		err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); - -	if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V && -			host->caps2 & MMC_CAP2_HS200_1_8V_SDR) -		err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); - -	/* If fails try again during next card power cycle */ -	if (err) -		goto err; +	if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && +	    !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) +		return 0; -	idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0; +	idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 0 : 1;  	/*  	 * Unlike SD, MMC cards dont have a configuration register to notify @@ -827,8 +893,7 @@ static int mmc_select_hs200(struct mmc_card *card)  	 * the supported bus width or compare the ext csd values of current  	 * bus width and ext csd values of 1 bit mode read earlier.  	 */ -	for (; idx >= 0; idx--) { - +	for (; idx < ARRAY_SIZE(bus_widths); idx++) {  		/*  		 * Host is capable of 8bit transfer, then switch  		 * the device to work in 8bit transfer mode. If the @@ -843,25 +908,266 @@ static int mmc_select_hs200(struct mmc_card *card)  		if (err)  			continue; -		mmc_set_bus_width(card->host, bus_widths[idx]); +		bus_width = bus_widths[idx]; +		mmc_set_bus_width(host, bus_width); +		/* +		 * If controller can't handle bus width test, +		 * compare ext_csd previously read in 1 bit mode +		 * against ext_csd at new bus width +		 */  		if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) -			err = mmc_compare_ext_csds(card, bus_widths[idx]); +			err = mmc_compare_ext_csds(card, bus_width);  		else -			err = mmc_bus_test(card, bus_widths[idx]); -		if (!err) +			err = mmc_bus_test(card, bus_width); + +		if (!err) { +			err = bus_width;  			break; +		} else { +			pr_warn("%s: switch to bus width %d failed\n", +				mmc_hostname(host), ext_csd_bits[idx]); +		}  	} -	/* switch to HS200 mode if bus width set successfully */ +	return err; +} + +/* + * Switch to the high-speed mode + */ +static int mmc_select_hs(struct mmc_card *card) +{ +	int err; + +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, +			   card->ext_csd.generic_cmd6_time, +			   true, true, true);  	if (!err) -		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -				 EXT_CSD_HS_TIMING, 2, 0); +		mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + +	return err; +} + +/* + * Activate wide bus and DDR if supported. + */ +static int mmc_select_hs_ddr(struct mmc_card *card) +{ +	struct mmc_host *host = card->host; +	u32 bus_width, ext_csd_bits; +	int err = 0; + +	if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52)) +		return 0; + +	bus_width = host->ios.bus_width; +	if (bus_width == MMC_BUS_WIDTH_1) +		return 0; + +	ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? +		EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; + +	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +			EXT_CSD_BUS_WIDTH, +			ext_csd_bits, +			card->ext_csd.generic_cmd6_time); +	if (err) { +		pr_warn("%s: switch to bus width %d ddr failed\n", +			mmc_hostname(host), 1 << bus_width); +		return err; +	} + +	/* +	 * eMMC cards can support 3.3V to 1.2V i/o (vccq) +	 * signaling. +	 * +	 * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. +	 * +	 * 1.8V vccq at 3.3V core voltage (vcc) is not required +	 * in the JEDEC spec for DDR. +	 * +	 * Do not force change in vccq since we are obviously +	 * working and no change to vccq is needed. +	 * +	 * WARNING: eMMC rules are NOT the same as SD DDR +	 */ +	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { +		err = __mmc_set_signal_voltage(host, +				MMC_SIGNAL_VOLTAGE_120); +		if (err) +			return err; +	} + +	mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + +	return err; +} + +static int mmc_select_hs400(struct mmc_card *card) +{ +	struct mmc_host *host = card->host; +	int err = 0; + +	/* +	 * HS400 mode requires 8-bit bus width +	 */ +	if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 && +	      host->ios.bus_width == MMC_BUS_WIDTH_8)) +		return 0; + +	/* +	 * Before switching to dual data rate operation for HS400, +	 * it is required to convert from HS200 mode to HS mode. +	 */ +	mmc_set_timing(card->host, MMC_TIMING_MMC_HS); +	mmc_set_bus_speed(card); + +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, +			   card->ext_csd.generic_cmd6_time, +			   true, true, true); +	if (err) { +		pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n", +			mmc_hostname(host), err); +		return err; +	} + +	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +			 EXT_CSD_BUS_WIDTH, +			 EXT_CSD_DDR_BUS_WIDTH_8, +			 card->ext_csd.generic_cmd6_time); +	if (err) { +		pr_warn("%s: switch to bus width for hs400 failed, err:%d\n", +			mmc_hostname(host), err); +		return err; +	} + +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, +			   card->ext_csd.generic_cmd6_time, +			   true, true, true); +	if (err) { +		pr_warn("%s: switch to hs400 failed, err:%d\n", +			 mmc_hostname(host), err); +		return err; +	} + +	mmc_set_timing(host, MMC_TIMING_MMC_HS400); +	mmc_set_bus_speed(card); + +	return 0; +} + +/* + * For device supporting HS200 mode, the following sequence + * should be done before executing the tuning process. + * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported) + * 2. switch to HS200 mode + * 3. set the clock to > 52Mhz and <=200MHz + */ +static int mmc_select_hs200(struct mmc_card *card) +{ +	struct mmc_host *host = card->host; +	int err = -EINVAL; + +	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) +		err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); + +	if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V) +		err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + +	/* If fails try again during next card power cycle */ +	if (err) +		goto err; + +	/* +	 * Set the bus width(4 or 8) with host's support and +	 * switch to HS200 mode if bus width is set successfully. +	 */ +	err = mmc_select_bus_width(card); +	if (!IS_ERR_VALUE(err)) { +		err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +				   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, +				   card->ext_csd.generic_cmd6_time, +				   true, true, true); +		if (!err) +			mmc_set_timing(host, MMC_TIMING_MMC_HS200); +	}  err:  	return err;  }  /* + * Activate High Speed or HS200 mode if supported. + */ +static int mmc_select_timing(struct mmc_card *card) +{ +	int err = 0; + +	if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && +	     card->ext_csd.hs_max_dtr == 0)) +		goto bus_speed; + +	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) +		err = mmc_select_hs200(card); +	else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) +		err = mmc_select_hs(card); + +	if (err && err != -EBADMSG) +		return err; + +	if (err) { +		pr_warn("%s: switch to %s failed\n", +			mmc_card_hs(card) ? "high-speed" : +			(mmc_card_hs200(card) ? "hs200" : ""), +			mmc_hostname(card->host)); +		err = 0; +	} + +bus_speed: +	/* +	 * Set the bus speed to the selected bus timing. +	 * If timing is not selected, backward compatible is the default. +	 */ +	mmc_set_bus_speed(card); +	return err; +} + +/* + * Execute tuning sequence to seek the proper bus operating + * conditions for HS200 and HS400, which sends CMD21 to the device. + */ +static int mmc_hs200_tuning(struct mmc_card *card) +{ +	struct mmc_host *host = card->host; +	int err = 0; + +	/* +	 * Timing should be adjusted to the HS400 target +	 * operation frequency for tuning process +	 */ +	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 && +	    host->ios.bus_width == MMC_BUS_WIDTH_8) +		if (host->ops->prepare_hs400_tuning) +			host->ops->prepare_hs400_tuning(host, &host->ios); + +	if (host->ops->execute_tuning) { +		mmc_host_clk_hold(host); +		err = host->ops->execute_tuning(host, +				MMC_SEND_TUNING_BLOCK_HS200); +		mmc_host_clk_release(host); + +		if (err) +			pr_warn("%s: tuning execution failed\n", +				mmc_hostname(host)); +	} + +	return err; +} + +/*   * Handle the detection and initialisation of a card.   *   * In the case of a resume, "oldcard" will contain the card @@ -871,9 +1177,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  	struct mmc_card *oldcard)  {  	struct mmc_card *card; -	int err, ddr = 0; +	int err;  	u32 cid[4]; -	unsigned int max_dtr;  	u32 rocr;  	u8 *ext_csd = NULL; @@ -934,6 +1239,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  			goto err;  		} +		card->ocr = ocr;  		card->type = MMC_TYPE_MMC;  		card->rca = 1;  		memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); @@ -1064,209 +1370,34 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  	}  	/* -	 * Activate high speed (if supported) -	 */ -	if (card->ext_csd.hs_max_dtr != 0) { -		err = 0; -		if (card->ext_csd.hs_max_dtr > 52000000 && -		    host->caps2 & MMC_CAP2_HS200) -			err = mmc_select_hs200(card); -		else if	(host->caps & MMC_CAP_MMC_HIGHSPEED) -			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -					 EXT_CSD_HS_TIMING, 1, -					 card->ext_csd.generic_cmd6_time); - -		if (err && err != -EBADMSG) -			goto free_card; - -		if (err) { -			pr_warning("%s: switch to highspeed failed\n", -			       mmc_hostname(card->host)); -			err = 0; -		} else { -			if (card->ext_csd.hs_max_dtr > 52000000 && -			    host->caps2 & MMC_CAP2_HS200) { -				mmc_card_set_hs200(card); -				mmc_set_timing(card->host, -					       MMC_TIMING_MMC_HS200); -			} else { -				mmc_card_set_highspeed(card); -				mmc_set_timing(card->host, MMC_TIMING_MMC_HS); -			} -		} -	} - -	/* -	 * Compute bus speed. -	 */ -	max_dtr = (unsigned int)-1; - -	if (mmc_card_highspeed(card) || mmc_card_hs200(card)) { -		if (max_dtr > card->ext_csd.hs_max_dtr) -			max_dtr = card->ext_csd.hs_max_dtr; -		if (mmc_card_highspeed(card) && (max_dtr > 52000000)) -			max_dtr = 52000000; -	} else if (max_dtr > card->csd.max_dtr) { -		max_dtr = card->csd.max_dtr; -	} - -	mmc_set_clock(host, max_dtr); - -	/* -	 * Indicate DDR mode (if supported). +	 * Select timing interface  	 */ -	if (mmc_card_highspeed(card)) { -		if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) -			&& ((host->caps & (MMC_CAP_1_8V_DDR | -			     MMC_CAP_UHS_DDR50)) -				== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) -				ddr = MMC_1_8V_DDR_MODE; -		else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) -			&& ((host->caps & (MMC_CAP_1_2V_DDR | -			     MMC_CAP_UHS_DDR50)) -				== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) -				ddr = MMC_1_2V_DDR_MODE; -	} +	err = mmc_select_timing(card); +	if (err) +		goto free_card; -	/* -	 * Indicate HS200 SDR mode (if supported). -	 */  	if (mmc_card_hs200(card)) { -		u32 ext_csd_bits; -		u32 bus_width = card->host->ios.bus_width; - -		/* -		 * For devices supporting HS200 mode, the bus width has -		 * to be set before executing the tuning function. If -		 * set before tuning, then device will respond with CRC -		 * errors for responses on CMD line. So for HS200 the -		 * sequence will be -		 * 1. set bus width 4bit / 8 bit (1 bit not supported) -		 * 2. switch to HS200 mode -		 * 3. set the clock to > 52Mhz <=200MHz and -		 * 4. execute tuning for HS200 -		 */ -		if ((host->caps2 & MMC_CAP2_HS200) && -		    card->host->ops->execute_tuning) { -			mmc_host_clk_hold(card->host); -			err = card->host->ops->execute_tuning(card->host, -				MMC_SEND_TUNING_BLOCK_HS200); -			mmc_host_clk_release(card->host); -		} -		if (err) { -			pr_warning("%s: tuning execution failed\n", -				   mmc_hostname(card->host)); +		err = mmc_hs200_tuning(card); +		if (err)  			goto err; -		} -		ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? -				EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; -		err = mmc_select_powerclass(card, ext_csd_bits); +		err = mmc_select_hs400(card);  		if (err) -			pr_warning("%s: power class selection to bus width %d" -				   " failed\n", mmc_hostname(card->host), -				   1 << bus_width); +			goto err; +	} else if (mmc_card_hs(card)) { +		/* Select the desired bus width optionally */ +		err = mmc_select_bus_width(card); +		if (!IS_ERR_VALUE(err)) { +			err = mmc_select_hs_ddr(card); +			if (err) +				goto err; +		}  	}  	/* -	 * Activate wide bus and DDR (if supported). +	 * Choose the power class with selected bus interface  	 */ -	if (!mmc_card_hs200(card) && -	    (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && -	    (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { -		static unsigned ext_csd_bits[][2] = { -			{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, -			{ EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, -			{ EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, -		}; -		static unsigned bus_widths[] = { -			MMC_BUS_WIDTH_8, -			MMC_BUS_WIDTH_4, -			MMC_BUS_WIDTH_1 -		}; -		unsigned idx, bus_width = 0; - -		if (host->caps & MMC_CAP_8_BIT_DATA) -			idx = 0; -		else -			idx = 1; -		for (; idx < ARRAY_SIZE(bus_widths); idx++) { -			bus_width = bus_widths[idx]; -			if (bus_width == MMC_BUS_WIDTH_1) -				ddr = 0; /* no DDR for 1-bit width */ -			err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); -			if (err) -				pr_warning("%s: power class selection to " -					   "bus width %d failed\n", -					   mmc_hostname(card->host), -					   1 << bus_width); - -			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -					 EXT_CSD_BUS_WIDTH, -					 ext_csd_bits[idx][0], -					 card->ext_csd.generic_cmd6_time); -			if (!err) { -				mmc_set_bus_width(card->host, bus_width); - -				/* -				 * If controller can't handle bus width test, -				 * compare ext_csd previously read in 1 bit mode -				 * against ext_csd at new bus width -				 */ -				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) -					err = mmc_compare_ext_csds(card, -						bus_width); -				else -					err = mmc_bus_test(card, bus_width); -				if (!err) -					break; -			} -		} - -		if (!err && ddr) { -			err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); -			if (err) -				pr_warning("%s: power class selection to " -					   "bus width %d ddr %d failed\n", -					   mmc_hostname(card->host), -					   1 << bus_width, ddr); - -			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -					 EXT_CSD_BUS_WIDTH, -					 ext_csd_bits[idx][1], -					 card->ext_csd.generic_cmd6_time); -		} -		if (err) { -			pr_warning("%s: switch to bus width %d ddr %d " -				"failed\n", mmc_hostname(card->host), -				1 << bus_width, ddr); -			goto free_card; -		} else if (ddr) { -			/* -			 * eMMC cards can support 3.3V to 1.2V i/o (vccq) -			 * signaling. -			 * -			 * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. -			 * -			 * 1.8V vccq at 3.3V core voltage (vcc) is not required -			 * in the JEDEC spec for DDR. -			 * -			 * Do not force change in vccq since we are obviously -			 * working and no change to vccq is needed. -			 * -			 * WARNING: eMMC rules are NOT the same as SD DDR -			 */ -			if (ddr == MMC_1_2V_DDR_MODE) { -				err = __mmc_set_signal_voltage(host, -					MMC_SIGNAL_VOLTAGE_120); -				if (err) -					goto err; -			} -			mmc_card_set_ddr_mode(card); -			mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); -			mmc_set_bus_width(card->host, bus_width); -		} -	} +	mmc_select_powerclass(card);  	/*  	 * Enable HPI feature (if supported) @@ -1289,8 +1420,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  	 * If cache size is higher than 0, this indicates  	 * the existence of cache and it can be turned on.  	 */ -	if ((host->caps2 & MMC_CAP2_CACHE_CTRL) && -			card->ext_csd.cache_size > 0) { +	if (card->ext_csd.cache_size > 0) {  		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,  				EXT_CSD_CACHE_CTRL, 1,  				card->ext_csd.generic_cmd6_time); @@ -1358,11 +1488,9 @@ static int mmc_sleep(struct mmc_host *host)  {  	struct mmc_command cmd = {0};  	struct mmc_card *card = host->card; +	unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);  	int err; -	if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) -		return 0; -  	err = mmc_deselect_cards(host);  	if (err)  		return err; @@ -1371,7 +1499,19 @@ static int mmc_sleep(struct mmc_host *host)  	cmd.arg = card->rca << 16;  	cmd.arg |= 1 << 15; -	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; +	/* +	 * If the max_busy_timeout of the host is specified, validate it against +	 * the sleep cmd timeout. A failure means we need to prevent the host +	 * from doing hw busy detection, which is done by converting to a R1 +	 * response instead of a R1B. +	 */ +	if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout)) { +		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; +	} else { +		cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; +		cmd.busy_timeout = timeout_ms; +	} +  	err = mmc_wait_for_cmd(host, &cmd, 0);  	if (err)  		return err; @@ -1382,8 +1522,8 @@ static int mmc_sleep(struct mmc_host *host)  	 * SEND_STATUS command to poll the status because that command (and most  	 * others) is invalid while the card sleeps.  	 */ -	if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) -		mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); +	if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) +		mmc_delay(timeout_ms);  	return err;  } @@ -1404,9 +1544,9 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)  	if (notify_type == EXT_CSD_POWER_OFF_LONG)  		timeout = card->ext_csd.power_off_longtime; -	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -			 EXT_CSD_POWER_OFF_NOTIFICATION, -			 notify_type, timeout); +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +			EXT_CSD_POWER_OFF_NOTIFICATION, +			notify_type, timeout, true, false, false);  	if (err)  		pr_err("%s: Power Off Notification timed out, %u\n",  		       mmc_hostname(card->host), timeout); @@ -1477,13 +1617,16 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)  	mmc_claim_host(host); +	if (mmc_card_suspended(host->card)) +		goto out; +  	if (mmc_card_doing_bkops(host->card)) {  		err = mmc_stop_bkops(host->card);  		if (err)  			goto out;  	} -	err = mmc_cache_ctrl(host, 0); +	err = mmc_flush_cache(host->card);  	if (err)  		goto out; @@ -1494,53 +1637,94 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)  		err = mmc_sleep(host);  	else if (!mmc_host_is_spi(host))  		err = mmc_deselect_cards(host); -	host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); -	if (!err) +	if (!err) {  		mmc_power_off(host); +		mmc_card_set_suspended(host->card); +	}  out:  	mmc_release_host(host);  	return err;  }  /* - * Suspend callback from host. + * Suspend callback   */  static int mmc_suspend(struct mmc_host *host)  { -	return _mmc_suspend(host, true); -} +	int err; -/* - * Shutdown callback - */ -static int mmc_shutdown(struct mmc_host *host) -{ -	return _mmc_suspend(host, false); +	err = _mmc_suspend(host, true); +	if (!err) { +		pm_runtime_disable(&host->card->dev); +		pm_runtime_set_suspended(&host->card->dev); +	} + +	return err;  }  /* - * Resume callback from host. - *   * This function tries to determine if the same card is still present   * and, if so, restore all state to it.   */ -static int mmc_resume(struct mmc_host *host) +static int _mmc_resume(struct mmc_host *host)  { -	int err; +	int err = 0;  	BUG_ON(!host);  	BUG_ON(!host->card);  	mmc_claim_host(host); -	mmc_power_up(host); -	mmc_select_voltage(host, host->ocr); -	err = mmc_init_card(host, host->ocr, host->card); + +	if (!mmc_card_suspended(host->card)) +		goto out; + +	mmc_power_up(host, host->card->ocr); +	err = mmc_init_card(host, host->card->ocr, host->card); +	mmc_card_clr_suspended(host->card); + +out:  	mmc_release_host(host); +	return err; +} + +/* + * Shutdown callback + */ +static int mmc_shutdown(struct mmc_host *host) +{ +	int err = 0; + +	/* +	 * In a specific case for poweroff notify, we need to resume the card +	 * before we can shutdown it properly. +	 */ +	if (mmc_can_poweroff_notify(host->card) && +		!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) +		err = _mmc_resume(host); + +	if (!err) +		err = _mmc_suspend(host, false);  	return err;  } +/* + * Callback for resume. + */ +static int mmc_resume(struct mmc_host *host) +{ +	int err = 0; + +	if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) { +		err = _mmc_resume(host); +		pm_runtime_set_active(&host->card->dev); +		pm_runtime_mark_last_busy(&host->card->dev); +	} +	pm_runtime_enable(&host->card->dev); + +	return err; +}  /*   * Callback for runtime_suspend. @@ -1552,18 +1736,11 @@ static int mmc_runtime_suspend(struct mmc_host *host)  	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))  		return 0; -	mmc_claim_host(host); - -	err = mmc_suspend(host); -	if (err) { +	err = _mmc_suspend(host, true); +	if (err)  		pr_err("%s: error %d doing aggessive suspend\n",  			mmc_hostname(host), err); -		goto out; -	} -	mmc_power_off(host); -out: -	mmc_release_host(host);  	return err;  } @@ -1574,18 +1751,14 @@ static int mmc_runtime_resume(struct mmc_host *host)  {  	int err; -	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) +	if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))  		return 0; -	mmc_claim_host(host); - -	mmc_power_up(host); -	err = mmc_resume(host); +	err = _mmc_resume(host);  	if (err)  		pr_err("%s: error %d doing aggessive resume\n",  			mmc_hostname(host), err); -	mmc_release_host(host);  	return 0;  } @@ -1593,9 +1766,8 @@ static int mmc_power_restore(struct mmc_host *host)  {  	int ret; -	host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);  	mmc_claim_host(host); -	ret = mmc_init_card(host, host->ocr, host->card); +	ret = mmc_init_card(host, host->card->ocr, host->card);  	mmc_release_host(host);  	return ret; @@ -1604,16 +1776,6 @@ static int mmc_power_restore(struct mmc_host *host)  static const struct mmc_bus_ops mmc_ops = {  	.remove = mmc_remove,  	.detect = mmc_detect, -	.suspend = NULL, -	.resume = NULL, -	.power_restore = mmc_power_restore, -	.alive = mmc_alive, -	.shutdown = mmc_shutdown, -}; - -static const struct mmc_bus_ops mmc_ops_unsafe = { -	.remove = mmc_remove, -	.detect = mmc_detect,  	.suspend = mmc_suspend,  	.resume = mmc_resume,  	.runtime_suspend = mmc_runtime_suspend, @@ -1623,24 +1785,13 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {  	.shutdown = mmc_shutdown,  }; -static void mmc_attach_bus_ops(struct mmc_host *host) -{ -	const struct mmc_bus_ops *bus_ops; - -	if (!mmc_card_is_removable(host)) -		bus_ops = &mmc_ops_unsafe; -	else -		bus_ops = &mmc_ops; -	mmc_attach_bus(host, bus_ops); -} -  /*   * Starting point for MMC card init.   */  int mmc_attach_mmc(struct mmc_host *host)  {  	int err; -	u32 ocr; +	u32 ocr, rocr;  	BUG_ON(!host);  	WARN_ON(!host->claimed); @@ -1653,7 +1804,7 @@ int mmc_attach_mmc(struct mmc_host *host)  	if (err)  		return err; -	mmc_attach_bus_ops(host); +	mmc_attach_bus(host, &mmc_ops);  	if (host->ocr_avail_mmc)  		host->ocr_avail = host->ocr_avail_mmc; @@ -1666,23 +1817,12 @@ int mmc_attach_mmc(struct mmc_host *host)  			goto err;  	} -	/* -	 * Sanity check the voltages that the card claims to -	 * support. -	 */ -	if (ocr & 0x7F) { -		pr_warning("%s: card claims to support voltages " -		       "below the defined range. These will be ignored.\n", -		       mmc_hostname(host)); -		ocr &= ~0x7F; -	} - -	host->ocr = mmc_select_voltage(host, ocr); +	rocr = mmc_select_voltage(host, ocr);  	/*  	 * Can we support the voltage of the card?  	 */ -	if (!host->ocr) { +	if (!rocr) {  		err = -EINVAL;  		goto err;  	} @@ -1690,7 +1830,7 @@ int mmc_attach_mmc(struct mmc_host *host)  	/*  	 * Detect and init the card.  	 */ -	err = mmc_init_card(host, host->ocr, NULL); +	err = mmc_init_card(host, rocr, NULL);  	if (err)  		goto err; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index ef183483d5b..f51b5ba3bbe 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -23,6 +23,40 @@  #define MMC_OPS_TIMEOUT_MS	(10 * 60 * 1000) /* 10 minute timeout */ +static inline int __mmc_send_status(struct mmc_card *card, u32 *status, +				    bool ignore_crc) +{ +	int err; +	struct mmc_command cmd = {0}; + +	BUG_ON(!card); +	BUG_ON(!card->host); + +	cmd.opcode = MMC_SEND_STATUS; +	if (!mmc_host_is_spi(card->host)) +		cmd.arg = card->rca << 16; +	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; +	if (ignore_crc) +		cmd.flags &= ~MMC_RSP_CRC; + +	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +	if (err) +		return err; + +	/* NOTE: callers are required to understand the difference +	 * between "native" and SPI format status words! +	 */ +	if (status) +		*status = cmd.resp[0]; + +	return 0; +} + +int mmc_send_status(struct mmc_card *card, u32 *status) +{ +	return __mmc_send_status(card, status, false); +} +  static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)  {  	int err; @@ -370,19 +404,31 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)   *	@timeout_ms: timeout (ms) for operation performed by register write,   *                   timeout of zero implies maximum possible timeout   *	@use_busy_signal: use the busy signal as response type + *	@send_status: send status cmd to poll for busy + *	@ignore_crc: ignore CRC errors when sending status cmd to poll for busy   *   *	Modifies the EXT_CSD register for selected card.   */  int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, -	       unsigned int timeout_ms, bool use_busy_signal) +		unsigned int timeout_ms, bool use_busy_signal, bool send_status, +		bool ignore_crc)  { +	struct mmc_host *host = card->host;  	int err;  	struct mmc_command cmd = {0};  	unsigned long timeout; -	u32 status; +	u32 status = 0; +	bool use_r1b_resp = use_busy_signal; -	BUG_ON(!card); -	BUG_ON(!card->host); +	/* +	 * If the cmd timeout and the max_busy_timeout of the host are both +	 * specified, let's validate them. A failure means we need to prevent +	 * the host from doing hw busy detection, which is done by converting +	 * to a R1 response instead of a R1B. +	 */ +	if (timeout_ms && host->max_busy_timeout && +		(timeout_ms > host->max_busy_timeout)) +		use_r1b_resp = false;  	cmd.opcode = MMC_SWITCH;  	cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | @@ -390,17 +436,21 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,  		  (value << 8) |  		  set;  	cmd.flags = MMC_CMD_AC; -	if (use_busy_signal) +	if (use_r1b_resp) {  		cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B; -	else +		/* +		 * A busy_timeout of zero means the host can decide to use +		 * whatever value it finds suitable. +		 */ +		cmd.busy_timeout = timeout_ms; +	} else {  		cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; +	} - -	cmd.cmd_timeout_ms = timeout_ms;  	if (index == EXT_CSD_SANITIZE_START)  		cmd.sanitize_busy = true; -	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);  	if (err)  		return err; @@ -408,32 +458,55 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,  	if (!use_busy_signal)  		return 0; -	/* Must check status to be sure of no errors */ -	timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); +	/* +	 * CRC errors shall only be ignored in cases were CMD13 is used to poll +	 * to detect busy completion. +	 */ +	if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) +		ignore_crc = false; + +	/* We have an unspecified cmd timeout, use the fallback value. */ +	if (!timeout_ms) +		timeout_ms = MMC_OPS_TIMEOUT_MS; + +	/* Must check status to be sure of no errors. */ +	timeout = jiffies + msecs_to_jiffies(timeout_ms);  	do { -		err = mmc_send_status(card, &status); -		if (err) -			return err; -		if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) +		if (send_status) { +			err = __mmc_send_status(card, &status, ignore_crc); +			if (err) +				return err; +		} +		if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)  			break; -		if (mmc_host_is_spi(card->host)) +		if (mmc_host_is_spi(host))  			break; +		/* +		 * We are not allowed to issue a status command and the host +		 * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only +		 * rely on waiting for the stated timeout to be sufficient. +		 */ +		if (!send_status) { +			mmc_delay(timeout_ms); +			return 0; +		} +  		/* Timeout if the device never leaves the program state. */  		if (time_after(jiffies, timeout)) {  			pr_err("%s: Card stuck in programming state! %s\n", -				mmc_hostname(card->host), __func__); +				mmc_hostname(host), __func__);  			return -ETIMEDOUT;  		}  	} while (R1_CURRENT_STATE(status) == R1_STATE_PRG); -	if (mmc_host_is_spi(card->host)) { +	if (mmc_host_is_spi(host)) {  		if (status & R1_SPI_ILLEGAL_COMMAND)  			return -EBADMSG;  	} else {  		if (status & 0xFDFFA000) -			pr_warning("%s: unexpected status %#x after " -			       "switch", mmc_hostname(card->host), status); +			pr_warn("%s: unexpected status %#x after switch\n", +				mmc_hostname(host), status);  		if (status & R1_SWITCH_ERROR)  			return -EBADMSG;  	} @@ -445,36 +518,11 @@ EXPORT_SYMBOL_GPL(__mmc_switch);  int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,  		unsigned int timeout_ms)  { -	return __mmc_switch(card, set, index, value, timeout_ms, true); +	return __mmc_switch(card, set, index, value, timeout_ms, true, true, +				false);  }  EXPORT_SYMBOL_GPL(mmc_switch); -int mmc_send_status(struct mmc_card *card, u32 *status) -{ -	int err; -	struct mmc_command cmd = {0}; - -	BUG_ON(!card); -	BUG_ON(!card->host); - -	cmd.opcode = MMC_SEND_STATUS; -	if (!mmc_host_is_spi(card->host)) -		cmd.arg = card->rca << 16; -	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - -	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); -	if (err) -		return err; - -	/* NOTE: callers are required to understand the difference -	 * between "native" and SPI format status words! -	 */ -	if (status) -		*status = cmd.resp[0]; - -	return 0; -} -  static int  mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,  		  u8 len) diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index 06ee1aeaace..6c36fccaa1e 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -13,6 +13,7 @@  #include <linux/kernel.h>  #include <linux/export.h>  #include <linux/mmc/card.h> +#include <linux/mmc/sdio_ids.h>  #ifndef SDIO_VENDOR_ID_TI  #define SDIO_VENDOR_ID_TI		0x0097 @@ -30,6 +31,10 @@  #define SDIO_DEVICE_ID_STE_CW1200	0x2280  #endif +#ifndef SDIO_DEVICE_ID_MARVELL_8797_F0 +#define SDIO_DEVICE_ID_MARVELL_8797_F0	0x9128 +#endif +  /*   * This hook just adds a quirk for all sdio devices   */ @@ -58,6 +63,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = {  	SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,  		   add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512), +	SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0, +		   add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING), +  	END_FIXUP  }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5e8823dc3ef..0c44510bf71 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -11,8 +11,10 @@   */  #include <linux/err.h> +#include <linux/sizes.h>  #include <linux/slab.h>  #include <linux/stat.h> +#include <linux/pm_runtime.h>  #include <linux/mmc/host.h>  #include <linux/mmc/card.h> @@ -44,6 +46,13 @@ static const unsigned int tacc_mant[] = {  	35,	40,	45,	50,	55,	60,	70,	80,  }; +static const unsigned int sd_au_size[] = { +	0,		SZ_16K / 512,		SZ_32K / 512,	SZ_64K / 512, +	SZ_128K / 512,	SZ_256K / 512,		SZ_512K / 512,	SZ_1M / 512, +	SZ_2M / 512,	SZ_4M / 512,		SZ_8M / 512,	(SZ_8M + SZ_4M) / 512, +	SZ_16M / 512,	(SZ_16M + SZ_8M) / 512,	SZ_32M / 512,	SZ_64M / 512, +}; +  #define UNSTUFF_BITS(resp,start,size)					\  	({								\  		const int __size = size;				\ @@ -215,7 +224,7 @@ static int mmc_decode_scr(struct mmc_card *card)  static int mmc_read_ssr(struct mmc_card *card)  {  	unsigned int au, es, et, eo; -	int err, i, max_au; +	int err, i;  	u32 *ssr;  	if (!(card->csd.cmdclass & CCC_APP_SPEC)) { @@ -239,26 +248,25 @@ static int mmc_read_ssr(struct mmc_card *card)  	for (i = 0; i < 16; i++)  		ssr[i] = be32_to_cpu(ssr[i]); -	/* SD3.0 increases max AU size to 64MB (0xF) from 4MB (0x9) */ -	max_au = card->scr.sda_spec3 ? 0xF : 0x9; -  	/*  	 * UNSTUFF_BITS only works with four u32s so we have to offset the  	 * bitfield positions accordingly.  	 */  	au = UNSTUFF_BITS(ssr, 428 - 384, 4); -	if (au > 0 && au <= max_au) { -		card->ssr.au = 1 << (au + 4); -		es = UNSTUFF_BITS(ssr, 408 - 384, 16); -		et = UNSTUFF_BITS(ssr, 402 - 384, 6); -		eo = UNSTUFF_BITS(ssr, 400 - 384, 2); -		if (es && et) { -			card->ssr.erase_timeout = (et * 1000) / es; -			card->ssr.erase_offset = eo * 1000; +	if (au) { +		if (au <= 9 || card->scr.sda_spec3) { +			card->ssr.au = sd_au_size[au]; +			es = UNSTUFF_BITS(ssr, 408 - 384, 16); +			et = UNSTUFF_BITS(ssr, 402 - 384, 6); +			if (es && et) { +				eo = UNSTUFF_BITS(ssr, 400 - 384, 2); +				card->ssr.erase_timeout = (et * 1000) / es; +				card->ssr.erase_offset = eo * 1000; +			} +		} else { +			pr_warning("%s: SD Status: Invalid Allocation Unit size.\n", +				   mmc_hostname(card->host));  		} -	} else { -		pr_warning("%s: SD Status: Invalid Allocation Unit " -			"size.\n", mmc_hostname(card->host));  	}  out:  	kfree(ssr); @@ -699,18 +707,10 @@ static struct attribute *sd_std_attrs[] = {  	&dev_attr_serial.attr,  	NULL,  }; - -static struct attribute_group sd_std_attr_group = { -	.attrs = sd_std_attrs, -}; - -static const struct attribute_group *sd_attr_groups[] = { -	&sd_std_attr_group, -	NULL, -}; +ATTRIBUTE_GROUPS(sd_std);  struct device_type sd_type = { -	.groups = sd_attr_groups, +	.groups = sd_std_groups,  };  /* @@ -721,6 +721,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)  	int err;  	u32 max_current;  	int retries = 10; +	u32 pocr = ocr;  try_again:  	if (!retries) { @@ -773,7 +774,8 @@ try_again:  	 */  	if (!mmc_host_is_spi(host) && rocr &&  	   ((*rocr & 0x41000000) == 0x41000000)) { -		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); +		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, +					pocr);  		if (err == -EAGAIN) {  			retries--;  			goto try_again; @@ -885,7 +887,7 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)  {  	unsigned max_dtr = (unsigned int)-1; -	if (mmc_card_highspeed(card)) { +	if (mmc_card_hs(card)) {  		if (max_dtr > card->sw_caps.hs_max_dtr)  			max_dtr = card->sw_caps.hs_max_dtr;  	} else if (max_dtr > card->csd.max_dtr) { @@ -895,12 +897,6 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)  	return max_dtr;  } -void mmc_sd_go_highspeed(struct mmc_card *card) -{ -	mmc_card_set_highspeed(card); -	mmc_set_timing(card->host, MMC_TIMING_SD_HS); -} -  /*   * Handle the detection and initialisation of a card.   * @@ -935,6 +931,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,  		if (IS_ERR(card))  			return PTR_ERR(card); +		card->ocr = ocr;  		card->type = MMC_TYPE_SD;  		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));  	} @@ -974,16 +971,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,  		err = mmc_sd_init_uhs_card(card);  		if (err)  			goto free_card; - -		/* Card is an ultra-high-speed card */ -		mmc_card_set_uhs(card);  	} else {  		/*  		 * Attempt to change to high-speed (if supported)  		 */  		err = mmc_sd_switch_hs(card);  		if (err > 0) -			mmc_sd_go_highspeed(card); +			mmc_set_timing(card->host, MMC_TIMING_SD_HS);  		else if (err)  			goto free_card; @@ -1064,10 +1058,7 @@ static void mmc_sd_detect(struct mmc_host *host)  	}  } -/* - * Suspend callback from host. - */ -static int mmc_sd_suspend(struct mmc_host *host) +static int _mmc_sd_suspend(struct mmc_host *host)  {  	int err = 0; @@ -1075,34 +1066,77 @@ static int mmc_sd_suspend(struct mmc_host *host)  	BUG_ON(!host->card);  	mmc_claim_host(host); + +	if (mmc_card_suspended(host->card)) +		goto out; +  	if (!mmc_host_is_spi(host))  		err = mmc_deselect_cards(host); -	host->card->state &= ~MMC_STATE_HIGHSPEED; -	if (!err) + +	if (!err) {  		mmc_power_off(host); +		mmc_card_set_suspended(host->card); +	} + +out:  	mmc_release_host(host); +	return err; +} + +/* + * Callback for suspend + */ +static int mmc_sd_suspend(struct mmc_host *host) +{ +	int err; + +	err = _mmc_sd_suspend(host); +	if (!err) { +		pm_runtime_disable(&host->card->dev); +		pm_runtime_set_suspended(&host->card->dev); +	}  	return err;  }  /* - * Resume callback from host. - *   * This function tries to determine if the same card is still present   * and, if so, restore all state to it.   */ -static int mmc_sd_resume(struct mmc_host *host) +static int _mmc_sd_resume(struct mmc_host *host)  { -	int err; +	int err = 0;  	BUG_ON(!host);  	BUG_ON(!host->card);  	mmc_claim_host(host); -	mmc_power_up(host); -	mmc_select_voltage(host, host->ocr); -	err = mmc_sd_init_card(host, host->ocr, host->card); + +	if (!mmc_card_suspended(host->card)) +		goto out; + +	mmc_power_up(host, host->card->ocr); +	err = mmc_sd_init_card(host, host->card->ocr, host->card); +	mmc_card_clr_suspended(host->card); + +out:  	mmc_release_host(host); +	return err; +} + +/* + * Callback for resume + */ +static int mmc_sd_resume(struct mmc_host *host) +{ +	int err = 0; + +	if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) { +		err = _mmc_sd_resume(host); +		pm_runtime_set_active(&host->card->dev); +		pm_runtime_mark_last_busy(&host->card->dev); +	} +	pm_runtime_enable(&host->card->dev);  	return err;  } @@ -1117,18 +1151,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host)  	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))  		return 0; -	mmc_claim_host(host); - -	err = mmc_sd_suspend(host); -	if (err) { +	err = _mmc_sd_suspend(host); +	if (err)  		pr_err("%s: error %d doing aggessive suspend\n",  			mmc_hostname(host), err); -		goto out; -	} -	mmc_power_off(host); -out: -	mmc_release_host(host);  	return err;  } @@ -1139,18 +1166,14 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)  {  	int err; -	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) +	if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))  		return 0; -	mmc_claim_host(host); - -	mmc_power_up(host); -	err = mmc_sd_resume(host); +	err = _mmc_sd_resume(host);  	if (err)  		pr_err("%s: error %d doing aggessive resume\n",  			mmc_hostname(host), err); -	mmc_release_host(host);  	return 0;  } @@ -1158,9 +1181,8 @@ static int mmc_sd_power_restore(struct mmc_host *host)  {  	int ret; -	host->card->state &= ~MMC_STATE_HIGHSPEED;  	mmc_claim_host(host); -	ret = mmc_sd_init_card(host, host->ocr, host->card); +	ret = mmc_sd_init_card(host, host->card->ocr, host->card);  	mmc_release_host(host);  	return ret; @@ -1169,16 +1191,6 @@ static int mmc_sd_power_restore(struct mmc_host *host)  static const struct mmc_bus_ops mmc_sd_ops = {  	.remove = mmc_sd_remove,  	.detect = mmc_sd_detect, -	.suspend = NULL, -	.resume = NULL, -	.power_restore = mmc_sd_power_restore, -	.alive = mmc_sd_alive, -	.shutdown = mmc_sd_suspend, -}; - -static const struct mmc_bus_ops mmc_sd_ops_unsafe = { -	.remove = mmc_sd_remove, -	.detect = mmc_sd_detect,  	.runtime_suspend = mmc_sd_runtime_suspend,  	.runtime_resume = mmc_sd_runtime_resume,  	.suspend = mmc_sd_suspend, @@ -1188,24 +1200,13 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {  	.shutdown = mmc_sd_suspend,  }; -static void mmc_sd_attach_bus_ops(struct mmc_host *host) -{ -	const struct mmc_bus_ops *bus_ops; - -	if (!mmc_card_is_removable(host)) -		bus_ops = &mmc_sd_ops_unsafe; -	else -		bus_ops = &mmc_sd_ops; -	mmc_attach_bus(host, bus_ops); -} -  /*   * Starting point for SD card init.   */  int mmc_attach_sd(struct mmc_host *host)  {  	int err; -	u32 ocr; +	u32 ocr, rocr;  	BUG_ON(!host);  	WARN_ON(!host->claimed); @@ -1214,7 +1215,7 @@ int mmc_attach_sd(struct mmc_host *host)  	if (err)  		return err; -	mmc_sd_attach_bus_ops(host); +	mmc_attach_bus(host, &mmc_sd_ops);  	if (host->ocr_avail_sd)  		host->ocr_avail = host->ocr_avail_sd; @@ -1229,31 +1230,12 @@ int mmc_attach_sd(struct mmc_host *host)  			goto err;  	} -	/* -	 * Sanity check the voltages that the card claims to -	 * support. -	 */ -	if (ocr & 0x7F) { -		pr_warning("%s: card claims to support voltages " -		       "below the defined range. These will be ignored.\n", -		       mmc_hostname(host)); -		ocr &= ~0x7F; -	} - -	if ((ocr & MMC_VDD_165_195) && -	    !(host->ocr_avail_sd & MMC_VDD_165_195)) { -		pr_warning("%s: SD card claims to support the " -		       "incompletely defined 'low voltage range'. This " -		       "will be ignored.\n", mmc_hostname(host)); -		ocr &= ~MMC_VDD_165_195; -	} - -	host->ocr = mmc_select_voltage(host, ocr); +	rocr = mmc_select_voltage(host, ocr);  	/*  	 * Can we support the voltage(s) of the card(s)?  	 */ -	if (!host->ocr) { +	if (!rocr) {  		err = -EINVAL;  		goto err;  	} @@ -1261,7 +1243,7 @@ int mmc_attach_sd(struct mmc_host *host)  	/*  	 * Detect and init the card.  	 */ -	err = mmc_sd_init_card(host, host->ocr, NULL); +	err = mmc_sd_init_card(host, rocr, NULL);  	if (err)  		goto err; diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h index 4b34b24f3f7..aab824a9a7f 100644 --- a/drivers/mmc/core/sd.h +++ b/drivers/mmc/core/sd.h @@ -12,6 +12,5 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,  	bool reinit);  unsigned mmc_sd_get_max_clock(struct mmc_card *card);  int mmc_sd_switch_hs(struct mmc_card *card); -void mmc_sd_go_highspeed(struct mmc_card *card);  #endif diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 80d89cff730..e636d9e99e4 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -363,7 +363,7 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)  {  	unsigned max_dtr; -	if (mmc_card_highspeed(card)) { +	if (mmc_card_hs(card)) {  		/*  		 * The SDIO specification doesn't mention how  		 * the CIS transfer speed register relates to @@ -593,23 +593,28 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,  	struct mmc_card *card;  	int err;  	int retries = 10; +	u32 rocr = 0; +	u32 ocr_card = ocr;  	BUG_ON(!host);  	WARN_ON(!host->claimed); +	/* to query card if 1.8V signalling is supported */ +	if (mmc_host_uhs(host)) +		ocr |= R4_18V_PRESENT; +  try_again:  	if (!retries) {  		pr_warning("%s: Skipping voltage switch\n",  				mmc_hostname(host));  		ocr &= ~R4_18V_PRESENT; -		host->ocr &= ~R4_18V_PRESENT;  	}  	/*  	 * Inform the card of the voltage  	 */  	if (!powered_resume) { -		err = mmc_send_io_op_cond(host, host->ocr, &ocr); +		err = mmc_send_io_op_cond(host, ocr, &rocr);  		if (err)  			goto err;  	} @@ -632,8 +637,8 @@ try_again:  		goto err;  	} -	if ((ocr & R4_MEMORY_PRESENT) && -	    mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) { +	if ((rocr & R4_MEMORY_PRESENT) && +	    mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {  		card->type = MMC_TYPE_SD_COMBO;  		if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || @@ -663,8 +668,9 @@ try_again:  	 * systems that claim 1.8v signalling in fact do not support  	 * it.  	 */ -	if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { -		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); +	if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) { +		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, +					ocr);  		if (err == -EAGAIN) {  			sdio_reset(host);  			mmc_go_idle(host); @@ -674,12 +680,10 @@ try_again:  			goto try_again;  		} else if (err) {  			ocr &= ~R4_18V_PRESENT; -			host->ocr &= ~R4_18V_PRESENT;  		}  		err = 0;  	} else {  		ocr &= ~R4_18V_PRESENT; -		host->ocr &= ~R4_18V_PRESENT;  	}  	/* @@ -729,7 +733,6 @@ try_again:  		mmc_set_clock(host, card->cis.max_dtr);  		if (card->cccr.high_speed) { -			mmc_card_set_highspeed(card);  			mmc_set_timing(card->host, MMC_TIMING_SD_HS);  		} @@ -759,6 +762,7 @@ try_again:  		card = oldcard;  	} +	card->ocr = ocr_card;  	mmc_fixup_device(card, NULL);  	if (card->type == MMC_TYPE_SD_COMBO) { @@ -787,16 +791,13 @@ try_again:  		err = mmc_sdio_init_uhs_card(card);  		if (err)  			goto remove; - -		/* Card is an ultra-high-speed card */ -		mmc_card_set_uhs(card);  	} else {  		/*  		 * Switch to high-speed (if supported).  		 */  		err = sdio_enable_hs(card);  		if (err > 0) -			mmc_sd_go_highspeed(card); +			mmc_set_timing(card->host, MMC_TIMING_SD_HS);  		else if (err)  			goto remove; @@ -938,40 +939,21 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host)   */  static int mmc_sdio_suspend(struct mmc_host *host)  { -	int i, err = 0; - -	for (i = 0; i < host->card->sdio_funcs; i++) { -		struct sdio_func *func = host->card->sdio_func[i]; -		if (func && sdio_func_present(func) && func->dev.driver) { -			const struct dev_pm_ops *pmops = func->dev.driver->pm; -			err = pmops->suspend(&func->dev); -			if (err) -				break; -		} -	} -	while (err && --i >= 0) { -		struct sdio_func *func = host->card->sdio_func[i]; -		if (func && sdio_func_present(func) && func->dev.driver) { -			const struct dev_pm_ops *pmops = func->dev.driver->pm; -			pmops->resume(&func->dev); -		} -	} - -	if (!err && mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { +	if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {  		mmc_claim_host(host);  		sdio_disable_wide(host->card);  		mmc_release_host(host);  	} -	if (!err && !mmc_card_keep_power(host)) +	if (!mmc_card_keep_power(host))  		mmc_power_off(host); -	return err; +	return 0;  }  static int mmc_sdio_resume(struct mmc_host *host)  { -	int i, err = 0; +	int err = 0;  	BUG_ON(!host);  	BUG_ON(!host->card); @@ -981,8 +963,7 @@ static int mmc_sdio_resume(struct mmc_host *host)  	/* Restore power if needed */  	if (!mmc_card_keep_power(host)) { -		mmc_power_up(host); -		mmc_select_voltage(host, host->ocr); +		mmc_power_up(host, host->card->ocr);  		/*  		 * Tell runtime PM core we just powered up the card,  		 * since it still believes the card is powered off. @@ -1000,7 +981,7 @@ static int mmc_sdio_resume(struct mmc_host *host)  	if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {  		sdio_reset(host);  		mmc_go_idle(host); -		err = mmc_sdio_init_card(host, host->ocr, host->card, +		err = mmc_sdio_init_card(host, host->card->ocr, host->card,  					mmc_card_keep_power(host));  	} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {  		/* We may have switched to 1-bit mode during suspend */ @@ -1015,24 +996,6 @@ static int mmc_sdio_resume(struct mmc_host *host)  		wake_up_process(host->sdio_irq_thread);  	mmc_release_host(host); -	/* -	 * If the card looked to be the same as before suspending, then -	 * we proceed to resume all card functions.  If one of them returns -	 * an error then we simply return that error to the core and the -	 * card will be redetected as new.  It is the responsibility of -	 * the function driver to perform further tests with the extra -	 * knowledge it has of the card to confirm the card is indeed the -	 * same as before suspending (same MAC address for network cards, -	 * etc.) and return an error otherwise. -	 */ -	for (i = 0; !err && i < host->card->sdio_funcs; i++) { -		struct sdio_func *func = host->card->sdio_func[i]; -		if (func && sdio_func_present(func) && func->dev.driver) { -			const struct dev_pm_ops *pmops = func->dev.driver->pm; -			err = pmops->resume(&func->dev); -		} -	} -  	host->pm_flags &= ~MMC_PM_KEEP_POWER;  	return err;  } @@ -1040,7 +1003,6 @@ static int mmc_sdio_resume(struct mmc_host *host)  static int mmc_sdio_power_restore(struct mmc_host *host)  {  	int ret; -	u32 ocr;  	BUG_ON(!host);  	BUG_ON(!host->card); @@ -1062,32 +1024,17 @@ static int mmc_sdio_power_restore(struct mmc_host *host)  	 * for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and  	 * harmless in other situations.  	 * -	 * With these steps taken, mmc_select_voltage() is also required to -	 * restore the correct voltage setting of the card.  	 */  	sdio_reset(host);  	mmc_go_idle(host);  	mmc_send_if_cond(host, host->ocr_avail); -	ret = mmc_send_io_op_cond(host, 0, &ocr); +	ret = mmc_send_io_op_cond(host, 0, NULL);  	if (ret)  		goto out; -	if (host->ocr_avail_sdio) -		host->ocr_avail = host->ocr_avail_sdio; - -	host->ocr = mmc_select_voltage(host, ocr & ~0x7F); -	if (!host->ocr) { -		ret = -EINVAL; -		goto out; -	} - -	if (mmc_host_uhs(host)) -		/* to query card if 1.8V signalling is supported */ -		host->ocr |= R4_18V_PRESENT; - -	ret = mmc_sdio_init_card(host, host->ocr, host->card, +	ret = mmc_sdio_init_card(host, host->card->ocr, host->card,  				mmc_card_keep_power(host));  	if (!ret && host->sdio_irqs)  		mmc_signal_sdio_irq(host); @@ -1108,7 +1055,7 @@ static int mmc_sdio_runtime_suspend(struct mmc_host *host)  static int mmc_sdio_runtime_resume(struct mmc_host *host)  {  	/* Restore power and re-initialize. */ -	mmc_power_up(host); +	mmc_power_up(host, host->card->ocr);  	return mmc_sdio_power_restore(host);  } @@ -1131,7 +1078,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {  int mmc_attach_sdio(struct mmc_host *host)  {  	int err, i, funcs; -	u32 ocr; +	u32 ocr, rocr;  	struct mmc_card *card;  	BUG_ON(!host); @@ -1145,23 +1092,13 @@ int mmc_attach_sdio(struct mmc_host *host)  	if (host->ocr_avail_sdio)  		host->ocr_avail = host->ocr_avail_sdio; -	/* -	 * Sanity check the voltages that the card claims to -	 * support. -	 */ -	if (ocr & 0x7F) { -		pr_warning("%s: card claims to support voltages " -		       "below the defined range. These will be ignored.\n", -		       mmc_hostname(host)); -		ocr &= ~0x7F; -	} -	host->ocr = mmc_select_voltage(host, ocr); +	rocr = mmc_select_voltage(host, ocr);  	/*  	 * Can we support the voltage(s) of the card(s)?  	 */ -	if (!host->ocr) { +	if (!rocr) {  		err = -EINVAL;  		goto err;  	} @@ -1169,22 +1106,10 @@ int mmc_attach_sdio(struct mmc_host *host)  	/*  	 * Detect and init the card.  	 */ -	if (mmc_host_uhs(host)) -		/* to query card if 1.8V signalling is supported */ -		host->ocr |= R4_18V_PRESENT; +	err = mmc_sdio_init_card(host, rocr, NULL, 0); +	if (err) +		goto err; -	err = mmc_sdio_init_card(host, host->ocr, NULL, 0); -	if (err) { -		if (err == -EAGAIN) { -			/* -			 * Retry initialization with S18R set to 0. -			 */ -			host->ocr &= ~R4_18V_PRESENT; -			err = mmc_sdio_init_card(host, host->ocr, NULL, 0); -		} -		if (err) -			goto err; -	}  	card = host->card;  	/* diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 6d67492a924..4fa8fef9147 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -34,7 +34,8 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf)				\  									\  	func = dev_to_sdio_func (dev);					\  	return sprintf (buf, format_string, func->field);		\ -} +}									\ +static DEVICE_ATTR_RO(field)  sdio_config_attr(class, "0x%02x\n");  sdio_config_attr(vendor, "0x%04x\n"); @@ -47,14 +48,16 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,  	return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",  			func->class, func->vendor, func->device);  } - -static struct device_attribute sdio_dev_attrs[] = { -	__ATTR_RO(class), -	__ATTR_RO(vendor), -	__ATTR_RO(device), -	__ATTR_RO(modalias), -	__ATTR_NULL, +static DEVICE_ATTR_RO(modalias); + +static struct attribute *sdio_dev_attrs[] = { +	&dev_attr_class.attr, +	&dev_attr_vendor.attr, +	&dev_attr_device.attr, +	&dev_attr_modalias.attr, +	NULL,  }; +ATTRIBUTE_GROUPS(sdio_dev);  static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,  	const struct sdio_device_id *id) @@ -194,20 +197,8 @@ static int sdio_bus_remove(struct device *dev)  #ifdef CONFIG_PM -#ifdef CONFIG_PM_SLEEP -static int pm_no_operation(struct device *dev) -{ -	/* -	 * Prevent the PM core from calling SDIO device drivers' suspend -	 * callback routines, which it is not supposed to do, by using this -	 * empty function as the bus type suspend callaback for SDIO. -	 */ -	return 0; -} -#endif -  static const struct dev_pm_ops sdio_bus_pm_ops = { -	SET_SYSTEM_SLEEP_PM_OPS(pm_no_operation, pm_no_operation) +	SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)  	SET_RUNTIME_PM_OPS(  		pm_generic_runtime_suspend,  		pm_generic_runtime_resume, @@ -225,7 +216,7 @@ static const struct dev_pm_ops sdio_bus_pm_ops = {  static struct bus_type sdio_bus_type = {  	.name		= "sdio", -	.dev_attrs	= sdio_dev_attrs, +	.dev_groups	= sdio_dev_groups,  	.match		= sdio_bus_match,  	.uevent		= sdio_bus_uevent,  	.probe		= sdio_bus_probe, @@ -305,8 +296,7 @@ static void sdio_acpi_set_handle(struct sdio_func *func)  	struct mmc_host *host = func->card->host;  	u64 addr = (host->slotno << 16) | func->num; -	ACPI_HANDLE_SET(&func->dev, -			acpi_get_child(ACPI_HANDLE(host->parent), addr)); +	acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr);  }  #else  static inline void sdio_acpi_set_handle(struct sdio_func *func) {} diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 3d8ceb4084d..5cc13c8d35b 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -53,6 +53,17 @@ static int process_sdio_pending_irqs(struct mmc_host *host)  		return ret;  	} +	if (pending && mmc_card_broken_irq_polling(card) && +	    !(host->caps & MMC_CAP_SDIO_IRQ)) { +		unsigned char dummy; + +		/* A fake interrupt could be created when we poll SDIO_CCCR_INTx +		 * register with a Marvell SD8797 card. A dummy CMD52 read to +		 * function 0 register 0xff can avoid this. +		 */ +		mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy); +	} +  	count = 0;  	for (i = 1; i <= 7; i++) {  		if (pending & (1 << i)) { @@ -79,6 +90,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host)  	return ret;  } +void sdio_run_irqs(struct mmc_host *host) +{ +	mmc_claim_host(host); +	host->sdio_irq_pending = true; +	process_sdio_pending_irqs(host); +	mmc_release_host(host); +} +EXPORT_SYMBOL_GPL(sdio_run_irqs); +  static int sdio_irq_thread(void *_host)  {  	struct mmc_host *host = _host; @@ -178,14 +198,20 @@ static int sdio_card_irq_get(struct mmc_card *card)  	WARN_ON(!host->claimed);  	if (!host->sdio_irqs++) { -		atomic_set(&host->sdio_irq_thread_abort, 0); -		host->sdio_irq_thread = -			kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", -				mmc_hostname(host)); -		if (IS_ERR(host->sdio_irq_thread)) { -			int err = PTR_ERR(host->sdio_irq_thread); -			host->sdio_irqs--; -			return err; +		if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { +			atomic_set(&host->sdio_irq_thread_abort, 0); +			host->sdio_irq_thread = +				kthread_run(sdio_irq_thread, host, +					    "ksdioirqd/%s", mmc_hostname(host)); +			if (IS_ERR(host->sdio_irq_thread)) { +				int err = PTR_ERR(host->sdio_irq_thread); +				host->sdio_irqs--; +				return err; +			} +		} else { +			mmc_host_clk_hold(host); +			host->ops->enable_sdio_irq(host, 1); +			mmc_host_clk_release(host);  		}  	} @@ -200,8 +226,14 @@ static int sdio_card_irq_put(struct mmc_card *card)  	BUG_ON(host->sdio_irqs < 1);  	if (!--host->sdio_irqs) { -		atomic_set(&host->sdio_irq_thread_abort, 1); -		kthread_stop(host->sdio_irq_thread); +		if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { +			atomic_set(&host->sdio_irq_thread_abort, 1); +			kthread_stop(host->sdio_irq_thread); +		} else { +			mmc_host_clk_hold(host); +			host->ops->enable_sdio_irq(host, 0); +			mmc_host_clk_release(host); +		}  	}  	return 0; diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 46596b71a32..5f89cb83d5f 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -10,6 +10,7 @@  #include <linux/err.h>  #include <linux/gpio.h> +#include <linux/gpio/consumer.h>  #include <linux/interrupt.h>  #include <linux/jiffies.h>  #include <linux/mmc/host.h> @@ -18,8 +19,10 @@  #include <linux/slab.h>  struct mmc_gpio { -	int ro_gpio; -	int cd_gpio; +	struct gpio_desc *ro_gpio; +	struct gpio_desc *cd_gpio; +	bool override_ro_active_level; +	bool override_cd_active_level;  	char *ro_label;  	char cd_label[0];  }; @@ -29,9 +32,7 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)  	/* Schedule a card detection after a debounce timeout */  	struct mmc_host *host = dev_id; -	if (host->ops->card_event) -		host->ops->card_event(host); - +	host->trigger_card_event = true;  	mmc_detect_change(host, msecs_to_jiffies(200));  	return IRQ_HANDLED; @@ -57,8 +58,6 @@ static int mmc_gpio_alloc(struct mmc_host *host)  			ctx->ro_label = ctx->cd_label + len;  			snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));  			snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); -			ctx->cd_gpio = -EINVAL; -			ctx->ro_gpio = -EINVAL;  			host->slot.handler_priv = ctx;  		}  	} @@ -72,11 +71,14 @@ int mmc_gpio_get_ro(struct mmc_host *host)  {  	struct mmc_gpio *ctx = host->slot.handler_priv; -	if (!ctx || !gpio_is_valid(ctx->ro_gpio)) +	if (!ctx || !ctx->ro_gpio)  		return -ENOSYS; -	return !gpio_get_value_cansleep(ctx->ro_gpio) ^ -		!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); +	if (ctx->override_ro_active_level) +		return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^ +			!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); + +	return gpiod_get_value_cansleep(ctx->ro_gpio);  }  EXPORT_SYMBOL(mmc_gpio_get_ro); @@ -84,11 +86,14 @@ int mmc_gpio_get_cd(struct mmc_host *host)  {  	struct mmc_gpio *ctx = host->slot.handler_priv; -	if (!ctx || !gpio_is_valid(ctx->cd_gpio)) +	if (!ctx || !ctx->cd_gpio)  		return -ENOSYS; -	return !gpio_get_value_cansleep(ctx->cd_gpio) ^ -		!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); +	if (ctx->override_cd_active_level) +		return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^ +			!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); + +	return gpiod_get_value_cansleep(ctx->cd_gpio);  }  EXPORT_SYMBOL(mmc_gpio_get_cd); @@ -125,12 +130,47 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)  	if (ret < 0)  		return ret; -	ctx->ro_gpio = gpio; +	ctx->override_ro_active_level = true; +	ctx->ro_gpio = gpio_to_desc(gpio);  	return 0;  }  EXPORT_SYMBOL(mmc_gpio_request_ro); +void mmc_gpiod_request_cd_irq(struct mmc_host *host) +{ +	struct mmc_gpio *ctx = host->slot.handler_priv; +	int ret, irq; + +	if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio) +		return; + +	irq = gpiod_to_irq(ctx->cd_gpio); + +	/* +	 * Even if gpiod_to_irq() returns a valid IRQ number, the platform might +	 * still prefer to poll, e.g., because that IRQ number is already used +	 * by another unit and cannot be shared. +	 */ +	if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) +		irq = -EINVAL; + +	if (irq >= 0) { +		ret = devm_request_threaded_irq(&host->class_dev, irq, +			NULL, mmc_gpio_cd_irqt, +			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +			ctx->cd_label, host); +		if (ret < 0) +			irq = ret; +	} + +	host->slot.cd_irq = irq; + +	if (irq < 0) +		host->caps |= MMC_CAP_NEEDS_POLL; +} +EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); +  /**   * mmc_gpio_request_cd - request a gpio for card-detection   * @host: mmc host @@ -154,7 +194,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,  			unsigned int debounce)  {  	struct mmc_gpio *ctx; -	int irq = gpio_to_irq(gpio);  	int ret;  	ret = mmc_gpio_alloc(host); @@ -179,29 +218,10 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,  			return ret;  	} -	/* -	 * Even if gpio_to_irq() returns a valid IRQ number, the platform might -	 * still prefer to poll, e.g., because that IRQ number is already used -	 * by another unit and cannot be shared. -	 */ -	if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) -		irq = -EINVAL; - -	if (irq >= 0) { -		ret = devm_request_threaded_irq(&host->class_dev, irq, -			NULL, mmc_gpio_cd_irqt, -			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, -			ctx->cd_label, host); -		if (ret < 0) -			irq = ret; -	} - -	host->slot.cd_irq = irq; - -	if (irq < 0) -		host->caps |= MMC_CAP_NEEDS_POLL; +	ctx->override_cd_active_level = true; +	ctx->cd_gpio = gpio_to_desc(gpio); -	ctx->cd_gpio = gpio; +	mmc_gpiod_request_cd_irq(host);  	return 0;  } @@ -219,11 +239,11 @@ void mmc_gpio_free_ro(struct mmc_host *host)  	struct mmc_gpio *ctx = host->slot.handler_priv;  	int gpio; -	if (!ctx || !gpio_is_valid(ctx->ro_gpio)) +	if (!ctx || !ctx->ro_gpio)  		return; -	gpio = ctx->ro_gpio; -	ctx->ro_gpio = -EINVAL; +	gpio = desc_to_gpio(ctx->ro_gpio); +	ctx->ro_gpio = NULL;  	devm_gpio_free(&host->class_dev, gpio);  } @@ -241,7 +261,7 @@ void mmc_gpio_free_cd(struct mmc_host *host)  	struct mmc_gpio *ctx = host->slot.handler_priv;  	int gpio; -	if (!ctx || !gpio_is_valid(ctx->cd_gpio)) +	if (!ctx || !ctx->cd_gpio)  		return;  	if (host->slot.cd_irq >= 0) { @@ -249,9 +269,87 @@ void mmc_gpio_free_cd(struct mmc_host *host)  		host->slot.cd_irq = -EINVAL;  	} -	gpio = ctx->cd_gpio; -	ctx->cd_gpio = -EINVAL; +	gpio = desc_to_gpio(ctx->cd_gpio); +	ctx->cd_gpio = NULL;  	devm_gpio_free(&host->class_dev, gpio);  }  EXPORT_SYMBOL(mmc_gpio_free_cd); + +/** + * mmc_gpiod_request_cd - request a gpio descriptor for card-detection + * @host: mmc host + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * @override_active_level: ignore %GPIO_ACTIVE_LOW flag + * @debounce: debounce time in microseconds + * + * Use this function in place of mmc_gpio_request_cd() to use the GPIO + * descriptor API.  Note that it is paired with mmc_gpiod_free_cd() not + * mmc_gpio_free_cd().  Note also that it must be called prior to mmc_add_host() + * otherwise the caller must also call mmc_gpiod_request_cd_irq(). + * + * Returns zero on success, else an error. + */ +int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, +			 unsigned int idx, bool override_active_level, +			 unsigned int debounce) +{ +	struct mmc_gpio *ctx; +	struct gpio_desc *desc; +	int ret; + +	ret = mmc_gpio_alloc(host); +	if (ret < 0) +		return ret; + +	ctx = host->slot.handler_priv; + +	if (!con_id) +		con_id = ctx->cd_label; + +	desc = devm_gpiod_get_index(host->parent, con_id, idx); +	if (IS_ERR(desc)) +		return PTR_ERR(desc); + +	ret = gpiod_direction_input(desc); +	if (ret < 0) +		return ret; + +	if (debounce) { +		ret = gpiod_set_debounce(desc, debounce); +		if (ret < 0) +			return ret; +	} + +	ctx->override_cd_active_level = override_active_level; +	ctx->cd_gpio = desc; + +	return 0; +} +EXPORT_SYMBOL(mmc_gpiod_request_cd); + +/** + * mmc_gpiod_free_cd - free the card-detection gpio descriptor + * @host: mmc host + * + * It's provided only for cases that client drivers need to manually free + * up the card-detection gpio requested by mmc_gpiod_request_cd(). + */ +void mmc_gpiod_free_cd(struct mmc_host *host) +{ +	struct mmc_gpio *ctx = host->slot.handler_priv; + +	if (!ctx || !ctx->cd_gpio) +		return; + +	if (host->slot.cd_irq >= 0) { +		devm_free_irq(&host->class_dev, host->slot.cd_irq, host); +		host->slot.cd_irq = -EINVAL; +	} + +	devm_gpiod_put(&host->class_dev, ctx->cd_gpio); + +	ctx->cd_gpio = NULL; +} +EXPORT_SYMBOL(mmc_gpiod_free_cd); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 7fc5099e44b..a5652548230 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM  	  If unsure, say N. +config MMC_SDHCI_OF_ARASAN +	tristate "SDHCI OF support for the Arasan SDHCI controllers" +	depends on MMC_SDHCI_PLTFM +	depends on OF +	help +	  This selects the Arasan Secure Digital Host Controller Interface +	  (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. + +	  If you have a controller with this interface, say Y or M here. + +	  If unsure, say N. +  config MMC_SDHCI_OF_ESDHC  	tristate "SDHCI OF support for the Freescale eSDHC controller"  	depends on MMC_SDHCI_PLTFM @@ -156,7 +168,7 @@ config MMC_SDHCI_ESDHC_IMX  config MMC_SDHCI_DOVE  	tristate "SDHCI support on Marvell's Dove SoC" -	depends on ARCH_DOVE +	depends on ARCH_DOVE || MACH_DOVE  	depends on MMC_SDHCI_PLTFM  	select MMC_SDHCI_IO_ACCESSORS  	help @@ -204,8 +216,7 @@ config MMC_SDHCI_SIRF  config MMC_SDHCI_PXAV3  	tristate "Marvell MMP2 SD Host Controller support (PXAV3)"  	depends on CLKDEV_LOOKUP -	select MMC_SDHCI -	select MMC_SDHCI_PLTFM +	depends on MMC_SDHCI_PLTFM  	default CPU_MMP2  	help  	  This selects the Marvell(R) PXAV3 SD Host Controller. @@ -217,8 +228,7 @@ config MMC_SDHCI_PXAV3  config MMC_SDHCI_PXAV2  	tristate "Marvell PXA9XX SD Host Controller support (PXAV2)"  	depends on CLKDEV_LOOKUP -	select MMC_SDHCI -	select MMC_SDHCI_PLTFM +	depends on MMC_SDHCI_PLTFM  	default CPU_PXA910  	help  	  This selects the Marvell(R) PXAV2 SD Host Controller. @@ -251,8 +261,8 @@ config MMC_SDHCI_S3C_DMA  config MMC_SDHCI_BCM_KONA  	tristate "SDHCI support on Broadcom KONA platform" -	depends on ARCH_BCM -	select MMC_SDHCI_PLTFM +	depends on ARCH_BCM_MOBILE +	depends on MMC_SDHCI_PLTFM  	help  	  This selects the Broadcom Kona Secure Digital Host Controller  	  Interface(SDHCI) support. @@ -271,10 +281,19 @@ config MMC_SDHCI_BCM2835  	  If unsure, say N. +config MMC_MOXART +	tristate "MOXART SD/MMC Host Controller support" +	depends on ARCH_MOXART && MMC +	help +	  This selects support for the MOXART SD/MMC Host Controller. +	  MOXA provides one multi-functional card reader which can +	  be found on some embedded hardware such as UC-7112-LX. +	  If you have a controller with this interface, say Y here. +  config MMC_OMAP  	tristate "TI OMAP Multimedia Card Interface support"  	depends on ARCH_OMAP -	select TPS65010 if MACH_OMAP_H2 +	depends on TPS65010 || !MACH_OMAP_H2  	help  	  This selects the TI OMAP Multimedia card Interface.  	  If you have an OMAP board with a Multimedia Card slot, @@ -322,9 +341,22 @@ config MMC_ATMELMCI  	  If unsure, say N. +config MMC_SDHCI_MSM +	tristate "Qualcomm SDHCI Controller Support" +	depends on ARCH_QCOM +	depends on MMC_SDHCI_PLTFM +	help +	  This selects the Secure Digital Host Controller Interface (SDHCI) +	  support present in Qualcomm SOCs. The controller supports +	  SD/MMC/SDIO devices. + +	  If you have a controller with this interface, say Y or M here. + +	  If unsure, say N. +  config MMC_MSM  	tristate "Qualcomm SDCC Controller Support" -	depends on MMC && ARCH_MSM +	depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)  	help  	  This provides support for the SD/MMC cell found in the  	  MSM and QSD SOCs from Qualcomm. The controller also has @@ -479,7 +511,8 @@ config MMC_TMIO  config MMC_SDHI  	tristate "SH-Mobile SDHI SD/SDIO controller support" -	depends on SUPERH || ARCH_SHMOBILE +	depends on SUPERH || ARM +	depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST  	select MMC_TMIO_CORE  	help  	  This provides support for the SDHI SD/SDIO controller found in @@ -567,13 +600,15 @@ config MMC_DW_EXYNOS  	  Synopsys DesignWare Memory Card Interface driver. Select this option  	  for platforms based on Exynos4 and Exynos5 SoC's. -config MMC_DW_SOCFPGA -	tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface" -	depends on MMC_DW && MFD_SYSCON +config MMC_DW_K3 +	tristate "K3 specific extensions for Synopsys DW Memory Card Interface" +	depends on MMC_DW  	select MMC_DW_PLTFM +	select MMC_DW_IDMAC  	help -	  This selects support for Altera SoCFPGA specific extensions to the -	  Synopsys DesignWare Memory Card Interface driver. +	  This selects support for Hisilicon K3 SoC specific extensions to the +	  Synopsys DesignWare Memory Card Interface driver. Select this option +	  for platforms based on Hisilicon K3 SoC's.  config MMC_DW_PCI  	tristate "Synopsys Designware MCI support on PCI bus" @@ -588,7 +623,8 @@ config MMC_DW_PCI  config MMC_SH_MMCIF  	tristate "SuperH Internal MMCIF support" -	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) +	depends on MMC_BLOCK +	depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST  	help  	  This selects the MMC Host Interface controller (MMCIF). @@ -659,9 +695,29 @@ config MMC_WMT  	  To compile this driver as a module, choose M here: the  	  module will be called wmt-sdmmc. +config MMC_USDHI6ROL0 +	tristate "Renesas USDHI6ROL0 SD/SDIO Host Controller support" +	help +	  This selects support for the Renesas USDHI6ROL0 SD/SDIO +	  Host Controller +  config MMC_REALTEK_PCI  	tristate "Realtek PCI-E SD/MMC Card Interface Driver"  	depends on MFD_RTSX_PCI  	help  	  Say Y here to include driver code to support SD/MMC card interface  	  of Realtek PCI-E card reader + +config MMC_REALTEK_USB +	tristate "Realtek USB SD/MMC Card Interface Driver" +	depends on MFD_RTSX_USB +	help +	  Say Y here to include driver code to support SD/MMC card interface +	  of Realtek RTS5129/39 series card reader + +config MMC_SUNXI +	tristate "Allwinner sunxi SD/MMC Host Controller support" +	depends on ARCH_SUNXI +	help +	  This selects support for the SD/MMC Host Controller on +	  Allwinner sunxi SoCs. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index c41d0c36450..7f81ddf1dd2 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS)		+= mxs-mmc.o  obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o  obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o  obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI))	+= sdhci-pci-data.o +obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI))	+= sdhci-pci-o2micro.o  obj-$(CONFIG_MMC_SDHCI_ACPI)	+= sdhci-acpi.o  obj-$(CONFIG_MMC_SDHCI_PXAV3)	+= sdhci-pxav3.o  obj-$(CONFIG_MMC_SDHCI_PXAV2)	+= sdhci-pxav2.o @@ -42,25 +43,31 @@ obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o  obj-$(CONFIG_MMC_DW)		+= dw_mmc.o  obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o  obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o -obj-$(CONFIG_MMC_DW_SOCFPGA)	+= dw_mmc-socfpga.o +obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o  obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o  obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o  obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o  obj-$(CONFIG_MMC_VUB300)	+= vub300.o  obj-$(CONFIG_MMC_USHC)		+= ushc.o  obj-$(CONFIG_MMC_WMT)		+= wmt-sdmmc.o +obj-$(CONFIG_MMC_MOXART)	+= moxart-mmc.o +obj-$(CONFIG_MMC_SUNXI)		+= sunxi-mmc.o +obj-$(CONFIG_MMC_USDHI6ROL0)	+= usdhi6rol0.o  obj-$(CONFIG_MMC_REALTEK_PCI)	+= rtsx_pci_sdmmc.o +obj-$(CONFIG_MMC_REALTEK_USB)	+= rtsx_usb_sdmmc.o  obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o  obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o  obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o  obj-$(CONFIG_MMC_SDHCI_DOVE)		+= sdhci-dove.o  obj-$(CONFIG_MMC_SDHCI_TEGRA)		+= sdhci-tegra.o +obj-$(CONFIG_MMC_SDHCI_OF_ARASAN)	+= sdhci-of-arasan.o  obj-$(CONFIG_MMC_SDHCI_OF_ESDHC)	+= sdhci-of-esdhc.o  obj-$(CONFIG_MMC_SDHCI_OF_HLWD)		+= sdhci-of-hlwd.o  obj-$(CONFIG_MMC_SDHCI_BCM_KONA)	+= sdhci-bcm-kona.o  obj-$(CONFIG_MMC_SDHCI_BCM2835)		+= sdhci-bcm2835.o +obj-$(CONFIG_MMC_SDHCI_MSM)		+= sdhci-msm.o  ifeq ($(CONFIG_CB710_DEBUG),y)  	CFLAGS-cb710-mmc	+= -DDEBUG diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 69e438ee043..bb585d94090 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -37,6 +37,7 @@  #include <linux/atmel-mci.h>  #include <linux/atmel_pdc.h> +#include <asm/cacheflush.h>  #include <asm/io.h>  #include <asm/unaligned.h> @@ -255,7 +256,6 @@ struct atmel_mci_slot {  #define ATMCI_CARD_PRESENT	0  #define ATMCI_CARD_NEED_INIT	1  #define ATMCI_SHUTDOWN		2 -#define ATMCI_SUSPENDED		3  	int			detect_pin;  	int			wp_pin; @@ -589,6 +589,13 @@ static void atmci_timeout_timer(unsigned long data)  	if (host->mrq->cmd->data) {  		host->mrq->cmd->data->error = -ETIMEDOUT;  		host->data = NULL; +		/* +		 * With some SDIO modules, sometimes DMA transfer hangs. If +		 * stop_transfer() is not called then the DMA request is not +		 * removed, following ones are queued and never computed. +		 */ +		if (host->state == STATE_DATA_XFER) +			host->stop_transfer(host);  	} else {  		host->mrq->cmd->error = -ETIMEDOUT;  		host->cmd = NULL; @@ -814,16 +821,9 @@ static void atmci_pdc_complete(struct atmel_mci *host)  	atmci_pdc_cleanup(host); -	/* -	 * If the card was removed, data will be NULL. No point trying -	 * to send the stop command or waiting for NBUSY in this case. -	 */ -	if (host->data) { -		dev_dbg(&host->pdev->dev, -		        "(%s) set pending xfer complete\n", __func__); -		atmci_set_pending(host, EVENT_XFER_COMPLETE); -		tasklet_schedule(&host->tasklet); -	} +	dev_dbg(&host->pdev->dev, "(%s) set pending xfer complete\n", __func__); +	atmci_set_pending(host, EVENT_XFER_COMPLETE); +	tasklet_schedule(&host->tasklet);  }  static void atmci_dma_cleanup(struct atmel_mci *host) @@ -1186,11 +1186,22 @@ static void atmci_start_request(struct atmel_mci *host,  	iflags |= ATMCI_CMDRDY;  	cmd = mrq->cmd;  	cmdflags = atmci_prepare_command(slot->mmc, cmd); -	atmci_send_command(host, cmd, cmdflags); + +	/* +	 * DMA transfer should be started before sending the command to avoid +	 * unexpected errors especially for read operations in SDIO mode. +	 * Unfortunately, in PDC mode, command has to be sent before starting +	 * the transfer. +	 */ +	if (host->submit_data != &atmci_submit_data_dma) +		atmci_send_command(host, cmd, cmdflags);  	if (data)  		host->submit_data(host, data); +	if (host->submit_data == &atmci_submit_data_dma) +		atmci_send_command(host, cmd, cmdflags); +  	if (mrq->stop) {  		host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);  		host->stop_cmdr |= ATMCI_CMDR_STOP_XFER; @@ -1385,8 +1396,14 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		clk_unprepare(host->mck);  	switch (ios->power_mode) { +	case MMC_POWER_OFF: +		if (!IS_ERR(mmc->supply.vmmc)) +			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); +		break;  	case MMC_POWER_UP:  		set_bit(ATMCI_CARD_NEED_INIT, &slot->flags); +		if (!IS_ERR(mmc->supply.vmmc)) +			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);  		break;  	default:  		/* @@ -1803,12 +1820,14 @@ static void atmci_tasklet_func(unsigned long priv)  			if (unlikely(status)) {  				host->stop_transfer(host);  				host->data = NULL; -				if (status & ATMCI_DTOE) { -					data->error = -ETIMEDOUT; -				} else if (status & ATMCI_DCRCE) { -					data->error = -EILSEQ; -				} else { -					data->error = -EIO; +				if (data) { +					if (status & ATMCI_DTOE) { +						data->error = -ETIMEDOUT; +					} else if (status & ATMCI_DCRCE) { +						data->error = -EILSEQ; +					} else { +						data->error = -EIO; +					}  				}  			} @@ -2196,6 +2215,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,  	}  	host->slot[id] = slot; +	mmc_regulator_get_supply(mmc);  	mmc_add_host(mmc);  	if (gpio_is_valid(slot->detect_pin)) { @@ -2520,70 +2540,10 @@ static int __exit atmci_remove(struct platform_device *pdev)  	return 0;  } -#ifdef CONFIG_PM_SLEEP -static int atmci_suspend(struct device *dev) -{ -	struct atmel_mci *host = dev_get_drvdata(dev); -	int i; - -	 for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { -		struct atmel_mci_slot *slot = host->slot[i]; -		int ret; - -		if (!slot) -			continue; -		ret = mmc_suspend_host(slot->mmc); -		if (ret < 0) { -			while (--i >= 0) { -				slot = host->slot[i]; -				if (slot -				&& test_bit(ATMCI_SUSPENDED, &slot->flags)) { -					mmc_resume_host(host->slot[i]->mmc); -					clear_bit(ATMCI_SUSPENDED, &slot->flags); -				} -			} -			return ret; -		} else { -			set_bit(ATMCI_SUSPENDED, &slot->flags); -		} -	} - -	return 0; -} - -static int atmci_resume(struct device *dev) -{ -	struct atmel_mci *host = dev_get_drvdata(dev); -	int i; -	int ret = 0; - -	for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { -		struct atmel_mci_slot *slot = host->slot[i]; -		int err; - -		slot = host->slot[i]; -		if (!slot) -			continue; -		if (!test_bit(ATMCI_SUSPENDED, &slot->flags)) -			continue; -		err = mmc_resume_host(slot->mmc); -		if (err < 0) -			ret = err; -		else -			clear_bit(ATMCI_SUSPENDED, &slot->flags); -	} - -	return ret; -} -#endif - -static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume); -  static struct platform_driver atmci_driver = {  	.remove		= __exit_p(atmci_remove),  	.driver		= {  		.name		= "atmel_mci", -		.pm		= &atmci_pm,  		.of_match_table	= of_match_ptr(atmci_dt_ids),  	},  }; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index df9becdd2e9..f5443a6c491 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1157,11 +1157,6 @@ static int au1xmmc_remove(struct platform_device *pdev)  static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)  {  	struct au1xmmc_host *host = platform_get_drvdata(pdev); -	int ret; - -	ret = mmc_suspend_host(host->mmc); -	if (ret) -		return ret;  	au_writel(0, HOST_CONFIG2(host));  	au_writel(0, HOST_CONFIG(host)); @@ -1178,7 +1173,7 @@ static int au1xmmc_resume(struct platform_device *pdev)  	au1xmmc_reset_controller(host); -	return mmc_resume_host(host->mmc); +	return 0;  }  #else  #define au1xmmc_suspend NULL diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 94fae2f1baa..2b7f37e82ca 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -391,6 +391,7 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		/* Disable 4 bit SDIO */  		cfg &= ~SD4E;  	} +	bfin_write_SDH_CFG(cfg);  	host->power_mode = ios->power_mode;  #ifndef RSI_BLKSZ @@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		cfg &= ~SD_CMD_OD;  # endif -  	if (ios->power_mode != MMC_POWER_OFF)  		cfg |= PWR_ON;  	else @@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		clk_ctl |= CLK_E;  		host->clk_div = clk_div;  		bfin_write_SDH_CLK_CTL(clk_ctl); -  	} else  		sdh_stop_clock(host); @@ -640,21 +639,15 @@ static int sdh_remove(struct platform_device *pdev)  #ifdef CONFIG_PM  static int sdh_suspend(struct platform_device *dev, pm_message_t state)  { -	struct mmc_host *mmc = platform_get_drvdata(dev);  	struct bfin_sd_host *drv_data = get_sdh_data(dev); -	int ret = 0; - -	if (mmc) -		ret = mmc_suspend_host(mmc);  	peripheral_free_list(drv_data->pin_req); -	return ret; +	return 0;  }  static int sdh_resume(struct platform_device *dev)  { -	struct mmc_host *mmc = platform_get_drvdata(dev);  	struct bfin_sd_host *drv_data = get_sdh_data(dev);  	int ret = 0; @@ -665,10 +658,6 @@ static int sdh_resume(struct platform_device *dev)  	}  	sdh_reset(); - -	if (mmc) -		ret = mmc_resume_host(mmc); -  	return ret;  }  #else diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 9d6e2b84440..1087b4c79cd 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -667,12 +667,6 @@ static const struct mmc_host_ops cb710_mmc_host = {  static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)  {  	struct cb710_slot *slot = cb710_pdev_to_slot(pdev); -	struct mmc_host *mmc = cb710_slot_to_mmc(slot); -	int err; - -	err = mmc_suspend_host(mmc); -	if (err) -		return err;  	cb710_mmc_enable_irq(slot, 0, ~0);  	return 0; @@ -681,11 +675,9 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)  static int cb710_mmc_resume(struct platform_device *pdev)  {  	struct cb710_slot *slot = cb710_pdev_to_slot(pdev); -	struct mmc_host *mmc = cb710_slot_to_mmc(slot);  	cb710_mmc_enable_irq(slot, 0, ~0); - -	return mmc_resume_host(mmc); +	return 0;  }  #endif /* CONFIG_PM */ diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index e9fa87df909..5d4c5e0fba2 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -193,7 +193,6 @@ struct mmc_davinci_host {  #define DAVINCI_MMC_DATADIR_READ	1  #define DAVINCI_MMC_DATADIR_WRITE	2  	unsigned char data_dir; -	unsigned char suspended;  	/* buffer is used during PIO of one scatterlist segment, and  	 * is updated along with buffer_bytes_left.  bytes_left applies @@ -1193,7 +1192,7 @@ static struct davinci_mmc_config  	struct device_node *np;  	struct davinci_mmc_config *pdata = pdev->dev.platform_data;  	const struct of_device_id *match = -		of_match_device(of_match_ptr(davinci_mmc_dt_ids), &pdev->dev); +		of_match_device(davinci_mmc_dt_ids, &pdev->dev);  	u32 data;  	np = pdev->dev.of_node; @@ -1435,38 +1434,23 @@ static int davinci_mmcsd_suspend(struct device *dev)  {  	struct platform_device *pdev = to_platform_device(dev);  	struct mmc_davinci_host *host = platform_get_drvdata(pdev); -	int ret; -	ret = mmc_suspend_host(host->mmc); -	if (!ret) { -		writel(0, host->base + DAVINCI_MMCIM); -		mmc_davinci_reset_ctrl(host, 1); -		clk_disable(host->clk); -		host->suspended = 1; -	} else { -		host->suspended = 0; -	} +	writel(0, host->base + DAVINCI_MMCIM); +	mmc_davinci_reset_ctrl(host, 1); +	clk_disable(host->clk); -	return ret; +	return 0;  }  static int davinci_mmcsd_resume(struct device *dev)  {  	struct platform_device *pdev = to_platform_device(dev);  	struct mmc_davinci_host *host = platform_get_drvdata(pdev); -	int ret; - -	if (!host->suspended) -		return 0;  	clk_enable(host->clk); -  	mmc_davinci_reset_ctrl(host, 0); -	ret = mmc_resume_host(host->mmc); -	if (!ret) -		host->suspended = 0; -	return ret; +	return 0;  }  static const struct dev_pm_ops davinci_mmcsd_pm = { @@ -1484,7 +1468,7 @@ static struct platform_driver davinci_mmcsd_driver = {  		.name	= "davinci_mmc",  		.owner	= THIS_MODULE,  		.pm	= davinci_mmcsd_pm_ops, -		.of_match_table = of_match_ptr(davinci_mmc_dt_ids), +		.of_match_table = davinci_mmc_dt_ids,  	},  	.remove		= __exit_p(davinci_mmcsd_remove),  	.id_table	= davinci_mmc_devtype, diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 6a1fa2110a0..0fbc53ac7ea 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -14,8 +14,10 @@  #include <linux/clk.h>  #include <linux/mmc/host.h>  #include <linux/mmc/dw_mmc.h> +#include <linux/mmc/mmc.h>  #include <linux/of.h>  #include <linux/of_gpio.h> +#include <linux/slab.h>  #include "dw_mmc.h"  #include "dw_mmc-pltfm.h" @@ -30,16 +32,39 @@  #define SDMMC_CLKSEL_TIMING(x, y, z)	(SDMMC_CLKSEL_CCLK_SAMPLE(x) |	\  					SDMMC_CLKSEL_CCLK_DRIVE(y) |	\  					SDMMC_CLKSEL_CCLK_DIVIDER(z)) +#define SDMMC_CLKSEL_WAKEUP_INT		BIT(11)  #define EXYNOS4210_FIXED_CIU_CLK_DIV	2  #define EXYNOS4412_FIXED_CIU_CLK_DIV	4 +/* Block number in eMMC */ +#define DWMCI_BLOCK_NUM		0xFFFFFFFF + +#define SDMMC_EMMCP_BASE	0x1000 +#define SDMMC_MPSECURITY	(SDMMC_EMMCP_BASE + 0x0010) +#define SDMMC_MPSBEGIN0		(SDMMC_EMMCP_BASE + 0x0200) +#define SDMMC_MPSEND0		(SDMMC_EMMCP_BASE + 0x0204) +#define SDMMC_MPSCTRL0		(SDMMC_EMMCP_BASE + 0x020C) + +/* SMU control bits */ +#define DWMCI_MPSCTRL_SECURE_READ_BIT		BIT(7) +#define DWMCI_MPSCTRL_SECURE_WRITE_BIT		BIT(6) +#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT	BIT(5) +#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT	BIT(4) +#define DWMCI_MPSCTRL_USE_FUSE_KEY		BIT(3) +#define DWMCI_MPSCTRL_ECB_MODE			BIT(2) +#define DWMCI_MPSCTRL_ENCRYPTION		BIT(1) +#define DWMCI_MPSCTRL_VALID			BIT(0) + +#define EXYNOS_CCLKIN_MIN	50000000	/* unit: HZ */ +  /* Variations in Exynos specific dw-mshc controller */  enum dw_mci_exynos_type {  	DW_MCI_TYPE_EXYNOS4210,  	DW_MCI_TYPE_EXYNOS4412,  	DW_MCI_TYPE_EXYNOS5250,  	DW_MCI_TYPE_EXYNOS5420, +	DW_MCI_TYPE_EXYNOS5420_SMU,  };  /* Exynos implementation specific driver private data */ @@ -48,6 +73,7 @@ struct dw_mci_exynos_priv_data {  	u8				ciu_div;  	u32				sdr_timing;  	u32				ddr_timing; +	u32				cur_speed;  };  static struct dw_mci_exynos_compatible { @@ -66,44 +92,80 @@ static struct dw_mci_exynos_compatible {  	}, {  		.compatible	= "samsung,exynos5420-dw-mshc",  		.ctrl_type	= DW_MCI_TYPE_EXYNOS5420, +	}, { +		.compatible	= "samsung,exynos5420-dw-mshc-smu", +		.ctrl_type	= DW_MCI_TYPE_EXYNOS5420_SMU,  	},  };  static int dw_mci_exynos_priv_init(struct dw_mci *host)  { -	struct dw_mci_exynos_priv_data *priv; -	int idx; - -	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); -	if (!priv) { -		dev_err(host->dev, "mem alloc failed for private data\n"); -		return -ENOMEM; -	} +	struct dw_mci_exynos_priv_data *priv = host->priv; -	for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { -		if (of_device_is_compatible(host->dev->of_node, -					exynos_compat[idx].compatible)) -			priv->ctrl_type = exynos_compat[idx].ctrl_type; +	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) { +		mci_writel(host, MPSBEGIN0, 0); +		mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); +		mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | +			   DWMCI_MPSCTRL_NON_SECURE_READ_BIT | +			   DWMCI_MPSCTRL_VALID | +			   DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);  	} -	host->priv = priv;  	return 0;  }  static int dw_mci_exynos_setup_clock(struct dw_mci *host)  {  	struct dw_mci_exynos_priv_data *priv = host->priv; +	unsigned long rate = clk_get_rate(host->ciu_clk); -	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 || -		priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420) -		host->bus_hz /= (priv->ciu_div + 1); -	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) -		host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV; -	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) -		host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV; +	host->bus_hz = rate / (priv->ciu_div + 1); +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_exynos_suspend(struct device *dev) +{ +	struct dw_mci *host = dev_get_drvdata(dev); + +	return dw_mci_suspend(host); +} + +static int dw_mci_exynos_resume(struct device *dev) +{ +	struct dw_mci *host = dev_get_drvdata(dev); + +	dw_mci_exynos_priv_init(host); +	return dw_mci_resume(host); +} + +/** + * dw_mci_exynos_resume_noirq - Exynos-specific resume code + * + * On exynos5420 there is a silicon errata that will sometimes leave the + * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate + * that it fired and we can clear it by writing a 1 back.  Clear it to prevent + * interrupts from going off constantly. + * + * We run this code on all exynos variants because it doesn't hurt. + */ + +static int dw_mci_exynos_resume_noirq(struct device *dev) +{ +	struct dw_mci *host = dev_get_drvdata(dev); +	u32 clksel; + +	clksel = mci_readl(host, CLKSEL); +	if (clksel & SDMMC_CLKSEL_WAKEUP_INT) +		mci_writel(host, CLKSEL, clksel);  	return 0;  } +#else +#define dw_mci_exynos_suspend		NULL +#define dw_mci_exynos_resume		NULL +#define dw_mci_exynos_resume_noirq	NULL +#endif /* CONFIG_PM_SLEEP */  static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)  { @@ -121,23 +183,68 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)  static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)  {  	struct dw_mci_exynos_priv_data *priv = host->priv; +	unsigned int wanted = ios->clock; +	unsigned long actual; +	u8 div = priv->ciu_div + 1; -	if (ios->timing == MMC_TIMING_UHS_DDR50) +	if (ios->timing == MMC_TIMING_MMC_DDR52) {  		mci_writel(host, CLKSEL, priv->ddr_timing); -	else +		/* Should be double rate for DDR mode */ +		if (ios->bus_width == MMC_BUS_WIDTH_8) +			wanted <<= 1; +	} else {  		mci_writel(host, CLKSEL, priv->sdr_timing); +	} + +	/* Don't care if wanted clock is zero */ +	if (!wanted) +		return; + +	/* Guaranteed minimum frequency for cclkin */ +	if (wanted < EXYNOS_CCLKIN_MIN) +		wanted = EXYNOS_CCLKIN_MIN; + +	if (wanted != priv->cur_speed) { +		int ret = clk_set_rate(host->ciu_clk, wanted * div); +		if (ret) +			dev_warn(host->dev, +				"failed to set clk-rate %u error: %d\n", +				 wanted * div, ret); +		actual = clk_get_rate(host->ciu_clk); +		host->bus_hz = actual / div; +		priv->cur_speed = wanted; +		host->current_speed = 0; +	}  }  static int dw_mci_exynos_parse_dt(struct dw_mci *host)  { -	struct dw_mci_exynos_priv_data *priv = host->priv; +	struct dw_mci_exynos_priv_data *priv;  	struct device_node *np = host->dev->of_node;  	u32 timing[2];  	u32 div = 0; +	int idx;  	int ret; -	of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); -	priv->ciu_div = div; +	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); +	if (!priv) { +		dev_err(host->dev, "mem alloc failed for private data\n"); +		return -ENOMEM; +	} + +	for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { +		if (of_device_is_compatible(np, exynos_compat[idx].compatible)) +			priv->ctrl_type = exynos_compat[idx].ctrl_type; +	} + +	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) +		priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1; +	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) +		priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1; +	else { +		of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); +		priv->ciu_div = div; +	}  	ret = of_property_read_u32_array(np,  			"samsung,dw-mshc-sdr-timing", timing, 2); @@ -152,13 +259,134 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)  		return ret;  	priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); +	host->priv = priv;  	return 0;  } +static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) +{ +	return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); +} + +static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) +{ +	u32 clksel; +	clksel = mci_readl(host, CLKSEL); +	clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); +	mci_writel(host, CLKSEL, clksel); +} + +static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) +{ +	u32 clksel; +	u8 sample; + +	clksel = mci_readl(host, CLKSEL); +	sample = (clksel + 1) & 0x7; +	clksel = (clksel & ~0x7) | sample; +	mci_writel(host, CLKSEL, clksel); +	return sample; +} + +static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) +{ +	const u8 iter = 8; +	u8 __c; +	s8 i, loc = -1; + +	for (i = 0; i < iter; i++) { +		__c = ror8(candiates, i); +		if ((__c & 0xc7) == 0xc7) { +			loc = i; +			goto out; +		} +	} + +	for (i = 0; i < iter; i++) { +		__c = ror8(candiates, i); +		if ((__c & 0x83) == 0x83) { +			loc = i; +			goto out; +		} +	} + +out: +	return loc; +} + +static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, +					struct dw_mci_tuning_data *tuning_data) +{ +	struct dw_mci *host = slot->host; +	struct mmc_host *mmc = slot->mmc; +	const u8 *blk_pattern = tuning_data->blk_pattern; +	u8 *blk_test; +	unsigned int blksz = tuning_data->blksz; +	u8 start_smpl, smpl, candiates = 0; +	s8 found = -1; +	int ret = 0; + +	blk_test = kmalloc(blksz, GFP_KERNEL); +	if (!blk_test) +		return -ENOMEM; + +	start_smpl = dw_mci_exynos_get_clksmpl(host); + +	do { +		struct mmc_request mrq = {NULL}; +		struct mmc_command cmd = {0}; +		struct mmc_command stop = {0}; +		struct mmc_data data = {0}; +		struct scatterlist sg; + +		cmd.opcode = opcode; +		cmd.arg = 0; +		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + +		stop.opcode = MMC_STOP_TRANSMISSION; +		stop.arg = 0; +		stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + +		data.blksz = blksz; +		data.blocks = 1; +		data.flags = MMC_DATA_READ; +		data.sg = &sg; +		data.sg_len = 1; + +		sg_init_one(&sg, blk_test, blksz); +		mrq.cmd = &cmd; +		mrq.stop = &stop; +		mrq.data = &data; +		host->mrq = &mrq; + +		mci_writel(host, TMOUT, ~0); +		smpl = dw_mci_exynos_move_next_clksmpl(host); + +		mmc_wait_for_req(mmc, &mrq); + +		if (!cmd.error && !data.error) { +			if (!memcmp(blk_pattern, blk_test, blksz)) +				candiates |= (1 << smpl); +		} else { +			dev_dbg(host->dev, +				"Tuning error: cmd.error:%d, data.error:%d\n", +				cmd.error, data.error); +		} +	} while (start_smpl != smpl); + +	found = dw_mci_exynos_get_best_clksmpl(candiates); +	if (found >= 0) +		dw_mci_exynos_set_clksmpl(host, found); +	else +		ret = -EIO; + +	kfree(blk_test); +	return ret; +} +  /* Common capabilities of Exynos4/Exynos5 SoC */  static unsigned long exynos_dwmmc_caps[4] = { -	MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR | -		MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23, +	MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,  	MMC_CAP_CMD23,  	MMC_CAP_CMD23,  	MMC_CAP_CMD23, @@ -171,6 +399,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {  	.prepare_command	= dw_mci_exynos_prepare_command,  	.set_ios		= dw_mci_exynos_set_ios,  	.parse_dt		= dw_mci_exynos_parse_dt, +	.execute_tuning		= dw_mci_exynos_execute_tuning,  };  static const struct of_device_id dw_mci_exynos_match[] = { @@ -180,6 +409,8 @@ static const struct of_device_id dw_mci_exynos_match[] = {  			.data = &exynos_drv_data, },  	{ .compatible = "samsung,exynos5420-dw-mshc",  			.data = &exynos_drv_data, }, +	{ .compatible = "samsung,exynos5420-dw-mshc-smu", +			.data = &exynos_drv_data, },  	{},  };  MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); @@ -194,13 +425,20 @@ static int dw_mci_exynos_probe(struct platform_device *pdev)  	return dw_mci_pltfm_register(pdev, drv_data);  } +static const struct dev_pm_ops dw_mci_exynos_pmops = { +	SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume) +	.resume_noirq = dw_mci_exynos_resume_noirq, +	.thaw_noirq = dw_mci_exynos_resume_noirq, +	.restore_noirq = dw_mci_exynos_resume_noirq, +}; +  static struct platform_driver dw_mci_exynos_pltfm_driver = {  	.probe		= dw_mci_exynos_probe,  	.remove		= __exit_p(dw_mci_pltfm_remove),  	.driver		= {  		.name		= "dwmmc_exynos",  		.of_match_table	= dw_mci_exynos_match, -		.pm		= &dw_mci_pltfm_pmops, +		.pm		= &dw_mci_exynos_pmops,  	},  }; diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c new file mode 100644 index 00000000000..650f9cc3f7a --- /dev/null +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/mmc/host.h> +#include <linux/mmc/dw_mmc.h> +#include <linux/of_address.h> + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ +	int ret; + +	ret = clk_set_rate(host->ciu_clk, ios->clock); +	if (ret) +		dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock); + +	host->bus_hz = clk_get_rate(host->ciu_clk); +} + +static const struct dw_mci_drv_data k3_drv_data = { +	.set_ios		= dw_mci_k3_set_ios, +}; + +static const struct of_device_id dw_mci_k3_match[] = { +	{ .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, }, +	{}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_k3_match); + +static int dw_mci_k3_probe(struct platform_device *pdev) +{ +	const struct dw_mci_drv_data *drv_data; +	const struct of_device_id *match; + +	match = of_match_node(dw_mci_k3_match, pdev->dev.of_node); +	drv_data = match->data; + +	return dw_mci_pltfm_register(pdev, drv_data); +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_k3_suspend(struct device *dev) +{ +	struct dw_mci *host = dev_get_drvdata(dev); +	int ret; + +	ret = dw_mci_suspend(host); +	if (!ret) +		clk_disable_unprepare(host->ciu_clk); + +	return ret; +} + +static int dw_mci_k3_resume(struct device *dev) +{ +	struct dw_mci *host = dev_get_drvdata(dev); +	int ret; + +	ret = clk_prepare_enable(host->ciu_clk); +	if (ret) { +		dev_err(host->dev, "failed to enable ciu clock\n"); +		return ret; +	} + +	return dw_mci_resume(host); +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume); + +static struct platform_driver dw_mci_k3_pltfm_driver = { +	.probe		= dw_mci_k3_probe, +	.remove		= dw_mci_pltfm_remove, +	.driver		= { +		.name		= "dwmmc_k3", +		.of_match_table	= dw_mci_k3_match, +		.pm		= &dw_mci_k3_pmops, +	}, +}; + +module_platform_driver(dw_mci_k3_pltfm_driver); + +MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dwmmc-k3"); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 20897529ea5..d4a47a9f558 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -25,13 +25,17 @@  #include "dw_mmc.h"  #include "dw_mmc-pltfm.h" -static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr) +static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr)  {  	*cmdr |= SDMMC_CMD_USE_HOLD_REG;  }  static const struct dw_mci_drv_data rockchip_drv_data = { -	.prepare_command	= dw_mci_rockchip_prepare_command, +	.prepare_command	= dw_mci_pltfm_prepare_command, +}; + +static const struct dw_mci_drv_data socfpga_drv_data = { +	.prepare_command	= dw_mci_pltfm_prepare_command,  };  int dw_mci_pltfm_register(struct platform_device *pdev, @@ -39,7 +43,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,  {  	struct dw_mci *host;  	struct resource	*regs; -	int ret;  	host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);  	if (!host) @@ -59,12 +62,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,  	if (IS_ERR(host->regs))  		return PTR_ERR(host->regs); -	if (drv_data && drv_data->init) { -		ret = drv_data->init(host); -		if (ret) -			return ret; -	} -  	platform_set_drvdata(pdev, host);  	return dw_mci_probe(host);  } @@ -99,6 +96,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {  	{ .compatible = "snps,dw-mshc", },  	{ .compatible = "rockchip,rk2928-dw-mshc",  		.data = &rockchip_drv_data }, +	{ .compatible = "altr,socfpga-dw-mshc", +		.data = &socfpga_drv_data },  	{},  };  MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); @@ -130,7 +129,7 @@ static struct platform_driver dw_mci_pltfm_driver = {  	.remove		= dw_mci_pltfm_remove,  	.driver		= {  		.name		= "dw_mmc", -		.of_match_table	= of_match_ptr(dw_mci_pltfm_match), +		.of_match_table	= dw_mci_pltfm_match,  		.pm		= &dw_mci_pltfm_pmops,  	},  }; diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c deleted file mode 100644 index 14b5961a851..00000000000 --- a/drivers/mmc/host/dw_mmc-socfpga.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Altera SoCFPGA Specific Extensions for Synopsys DW Multimedia Card Interface - * driver - * - *  Copyright (C) 2012, Samsung Electronics Co., Ltd. - *  Copyright (C) 2013 Altera Corporation - * - * 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. - * - * Taken from dw_mmc-exynos.c - */ -#include <linux/clk.h> -#include <linux/mfd/syscon.h> -#include <linux/mmc/host.h> -#include <linux/mmc/dw_mmc.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/regmap.h> - -#include "dw_mmc.h" -#include "dw_mmc-pltfm.h" - -#define SYSMGR_SDMMCGRP_CTRL_OFFSET		0x108 -#define DRV_CLK_PHASE_SHIFT_SEL_MASK	0x7 -#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel)          \ -	((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) - -/* SOCFPGA implementation specific driver private data */ -struct dw_mci_socfpga_priv_data { -	u8	ciu_div; /* card interface unit divisor */ -	u32	hs_timing; /* bitmask for CIU clock phase shift */ -	struct regmap   *sysreg; /* regmap for system manager register */ -}; - -static int dw_mci_socfpga_priv_init(struct dw_mci *host) -{ -	struct dw_mci_socfpga_priv_data *priv; - -	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); -	if (!priv) { -		dev_err(host->dev, "mem alloc failed for private data\n"); -		return -ENOMEM; -	} - -	priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); -	if (IS_ERR(priv->sysreg)) { -		dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n"); -		return PTR_ERR(priv->sysreg); -	} -	host->priv = priv; - -	return 0; -} - -static int dw_mci_socfpga_setup_clock(struct dw_mci *host) -{ -	struct dw_mci_socfpga_priv_data *priv = host->priv; - -	clk_disable_unprepare(host->ciu_clk); -	regmap_write(priv->sysreg, SYSMGR_SDMMCGRP_CTRL_OFFSET, -		priv->hs_timing); -	clk_prepare_enable(host->ciu_clk); - -	host->bus_hz /= (priv->ciu_div + 1); -	return 0; -} - -static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr) -{ -	struct dw_mci_socfpga_priv_data *priv = host->priv; - -	if (priv->hs_timing & DRV_CLK_PHASE_SHIFT_SEL_MASK) -		*cmdr |= SDMMC_CMD_USE_HOLD_REG; -} - -static int dw_mci_socfpga_parse_dt(struct dw_mci *host) -{ -	struct dw_mci_socfpga_priv_data *priv = host->priv; -	struct device_node *np = host->dev->of_node; -	u32 timing[2]; -	u32 div = 0; -	int ret; - -	ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div); -	if (ret) -		dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1"); -	priv->ciu_div = div; - -	ret = of_property_read_u32_array(np, -			"altr,dw-mshc-sdr-timing", timing, 2); -	if (ret) -		return ret; - -	priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]); -	return 0; -} - -static const struct dw_mci_drv_data socfpga_drv_data = { -	.init			= dw_mci_socfpga_priv_init, -	.setup_clock		= dw_mci_socfpga_setup_clock, -	.prepare_command	= dw_mci_socfpga_prepare_command, -	.parse_dt		= dw_mci_socfpga_parse_dt, -}; - -static const struct of_device_id dw_mci_socfpga_match[] = { -	{ .compatible = "altr,socfpga-dw-mshc", -			.data = &socfpga_drv_data, }, -	{}, -}; -MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match); - -int dw_mci_socfpga_probe(struct platform_device *pdev) -{ -	const struct dw_mci_drv_data *drv_data; -	const struct of_device_id *match; - -	match = of_match_node(dw_mci_socfpga_match, pdev->dev.of_node); -	drv_data = match->data; -	return dw_mci_pltfm_register(pdev, drv_data); -} - -static struct platform_driver dw_mci_socfpga_pltfm_driver = { -	.probe		= dw_mci_socfpga_probe, -	.remove		= __exit_p(dw_mci_pltfm_remove), -	.driver		= { -		.name		= "dwmmc_socfpga", -		.of_match_table	= of_match_ptr(dw_mci_socfpga_match), -		.pm		= &dw_mci_pltfm_pmops, -	}, -}; - -module_platform_driver(dw_mci_socfpga_pltfm_driver); - -MODULE_DESCRIPTION("Altera SOCFPGA Specific DW-MSHC Driver Extension"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:dwmmc-socfpga"); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 018f365e5ae..1ac227c603b 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,12 +29,14 @@  #include <linux/irq.h>  #include <linux/mmc/host.h>  #include <linux/mmc/mmc.h> +#include <linux/mmc/sdio.h>  #include <linux/mmc/dw_mmc.h>  #include <linux/bitops.h>  #include <linux/regulator/consumer.h>  #include <linux/workqueue.h>  #include <linux/of.h>  #include <linux/of_gpio.h> +#include <linux/mmc/slot-gpio.h>  #include "dw_mmc.h" @@ -50,6 +52,9 @@  #define DW_MCI_RECV_STATUS	2  #define DW_MCI_DMA_THRESHOLD	16 +#define DW_MCI_FREQ_MAX	200000000	/* unit: HZ */ +#define DW_MCI_FREQ_MIN	400000		/* unit: HZ */ +  #ifdef CONFIG_MMC_DW_IDMAC  #define IDMAC_INT_CLR		(SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \  				 SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ @@ -76,42 +81,39 @@ struct idmac_desc {  };  #endif /* CONFIG_MMC_DW_IDMAC */ -/** - * struct dw_mci_slot - MMC slot state - * @mmc: The mmc_host representing this slot. - * @host: The MMC controller this slot is using. - * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) - * @wp_gpio: If gpio_is_valid() we'll use this to read write protect. - * @ctype: Card type for this slot. - * @mrq: mmc_request currently being processed or waiting to be - *	processed, or NULL when the slot is idle. - * @queue_node: List node for placing this node in the @queue list of - *	&struct dw_mci. - * @clock: Clock rate configured by set_ios(). Protected by host->lock. - * @flags: Random state bits associated with the slot. - * @id: Number of this slot. - * @last_detect_state: Most recently observed card detect state. - */ -struct dw_mci_slot { -	struct mmc_host		*mmc; -	struct dw_mci		*host; - -	int			quirks; -	int			wp_gpio; - -	u32			ctype; - -	struct mmc_request	*mrq; -	struct list_head	queue_node; +static const u8 tuning_blk_pattern_4bit[] = { +	0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, +	0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, +	0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, +	0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, +	0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, +	0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, +	0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, +	0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; -	unsigned int		clock; -	unsigned long		flags; -#define DW_MMC_CARD_PRESENT	0 -#define DW_MMC_CARD_NEED_INIT	1 -	int			id; -	int			last_detect_state; +static const u8 tuning_blk_pattern_8bit[] = { +	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, +	0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, +	0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, +	0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, +	0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, +	0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, +	0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, +	0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, +	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, +	0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, +	0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, +	0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, +	0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, +	0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, +	0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, +	0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,  }; +static inline bool dw_mci_fifo_reset(struct dw_mci *host); +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host); +  #if defined(CONFIG_DEBUG_FS)  static int dw_mci_req_show(struct seq_file *s, void *v)  { @@ -233,12 +235,6 @@ err:  }  #endif /* defined(CONFIG_DEBUG_FS) */ -static void dw_mci_set_timeout(struct dw_mci *host) -{ -	/* timeout (maximum) */ -	mci_writel(host, TMOUT, 0xffffffff); -} -  static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)  {  	struct mmc_data	*data; @@ -249,9 +245,13 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)  	cmdr = cmd->opcode; -	if (cmdr == MMC_STOP_TRANSMISSION) +	if (cmd->opcode == MMC_STOP_TRANSMISSION || +	    cmd->opcode == MMC_GO_IDLE_STATE || +	    cmd->opcode == MMC_GO_INACTIVE_STATE || +	    (cmd->opcode == SD_IO_RW_DIRECT && +	     ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT))  		cmdr |= SDMMC_CMD_STOP; -	else +	else if (cmd->opcode != MMC_SEND_STATUS && cmd->data)  		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;  	if (cmd->flags & MMC_RSP_PRESENT) { @@ -279,6 +279,40 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)  	return cmdr;  } +static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) +{ +	struct mmc_command *stop; +	u32 cmdr; + +	if (!cmd->data) +		return 0; + +	stop = &host->stop_abort; +	cmdr = cmd->opcode; +	memset(stop, 0, sizeof(struct mmc_command)); + +	if (cmdr == MMC_READ_SINGLE_BLOCK || +	    cmdr == MMC_READ_MULTIPLE_BLOCK || +	    cmdr == MMC_WRITE_BLOCK || +	    cmdr == MMC_WRITE_MULTIPLE_BLOCK) { +		stop->opcode = MMC_STOP_TRANSMISSION; +		stop->arg = 0; +		stop->flags = MMC_RSP_R1B | MMC_CMD_AC; +	} else if (cmdr == SD_IO_RW_EXTENDED) { +		stop->opcode = SD_IO_RW_DIRECT; +		stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) | +			     ((cmd->arg >> 28) & 0x7); +		stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; +	} else { +		return 0; +	} + +	cmdr = stop->opcode | SDMMC_CMD_STOP | +		SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; + +	return cmdr; +} +  static void dw_mci_start_command(struct dw_mci *host,  				 struct mmc_command *cmd, u32 cmd_flags)  { @@ -293,9 +327,10 @@ static void dw_mci_start_command(struct dw_mci *host,  	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);  } -static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) +static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)  { -	dw_mci_start_command(host, data->stop, host->stop_cmdr); +	struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; +	dw_mci_start_command(host, stop, host->stop_cmdr);  }  /* DMA interface functions */ @@ -304,10 +339,10 @@ static void dw_mci_stop_dma(struct dw_mci *host)  	if (host->using_dma) {  		host->dma_ops->stop(host);  		host->dma_ops->cleanup(host); -	} else { -		/* Data transfer was stopped by the interrupt handler */ -		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);  	} + +	/* Data transfer was stopped by the interrupt handler */ +	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);  }  static int dw_mci_get_dma_dir(struct mmc_data *data) @@ -331,6 +366,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)  				     dw_mci_get_dma_dir(data));  } +static void dw_mci_idmac_reset(struct dw_mci *host) +{ +	u32 bmod = mci_readl(host, BMOD); +	/* Software reset of DMA */ +	bmod |= SDMMC_IDMAC_SWRESET; +	mci_writel(host, BMOD, bmod); +} +  static void dw_mci_idmac_stop_dma(struct dw_mci *host)  {  	u32 temp; @@ -344,6 +387,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host)  	/* Stop the IDMAC running */  	temp = mci_readl(host, BMOD);  	temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB); +	temp |= SDMMC_IDMAC_SWRESET;  	mci_writel(host, BMOD, temp);  } @@ -435,7 +479,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)  	p->des3 = host->sg_dma;  	p->des0 = IDMAC_DES0_ER; -	mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET); +	dw_mci_idmac_reset(host);  	/* Mask out interrupts - get Tx & Rx complete only */  	mci_writel(host, IDSTS, IDMAC_INT_CLR); @@ -532,6 +576,78 @@ static void dw_mci_post_req(struct mmc_host *mmc,  	data->host_cookie = 0;  } +static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) +{ +#ifdef CONFIG_MMC_DW_IDMAC +	unsigned int blksz = data->blksz; +	const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256}; +	u32 fifo_width = 1 << host->data_shift; +	u32 blksz_depth = blksz / fifo_width, fifoth_val; +	u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; +	int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1; + +	tx_wmark = (host->fifo_depth) / 2; +	tx_wmark_invers = host->fifo_depth - tx_wmark; + +	/* +	 * MSIZE is '1', +	 * if blksz is not a multiple of the FIFO width +	 */ +	if (blksz % fifo_width) { +		msize = 0; +		rx_wmark = 1; +		goto done; +	} + +	do { +		if (!((blksz_depth % mszs[idx]) || +		     (tx_wmark_invers % mszs[idx]))) { +			msize = idx; +			rx_wmark = mszs[idx] - 1; +			break; +		} +	} while (--idx > 0); +	/* +	 * If idx is '0', it won't be tried +	 * Thus, initial values are uesed +	 */ +done: +	fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark); +	mci_writel(host, FIFOTH, fifoth_val); +#endif +} + +static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) +{ +	unsigned int blksz = data->blksz; +	u32 blksz_depth, fifo_depth; +	u16 thld_size; + +	WARN_ON(!(data->flags & MMC_DATA_READ)); + +	if (host->timing != MMC_TIMING_MMC_HS200 && +	    host->timing != MMC_TIMING_UHS_SDR104) +		goto disable; + +	blksz_depth = blksz / (1 << host->data_shift); +	fifo_depth = host->fifo_depth; + +	if (blksz_depth > fifo_depth) +		goto disable; + +	/* +	 * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz' +	 * If (blksz_depth) <  (fifo_depth >> 1), should be thld_size = blksz +	 * Currently just choose blksz. +	 */ +	thld_size = blksz; +	mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1)); +	return; + +disable: +	mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0)); +} +  static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)  {  	int sg_len; @@ -556,6 +672,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)  		 (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,  		 sg_len); +	/* +	 * Decide the MSIZE and RX/TX Watermark. +	 * If current block size is same with previous size, +	 * no need to update fifoth. +	 */ +	if (host->prev_blksz != data->blksz) +		dw_mci_adjust_fifoth(host, data); +  	/* Enable the DMA interface */  	temp = mci_readl(host, CTRL);  	temp |= SDMMC_CTRL_DMA_ENABLE; @@ -581,10 +705,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)  	host->sg = NULL;  	host->data = data; -	if (data->flags & MMC_DATA_READ) +	if (data->flags & MMC_DATA_READ) {  		host->dir_status = DW_MCI_RECV_STATUS; -	else +		dw_mci_ctrl_rd_thld(host, data); +	} else {  		host->dir_status = DW_MCI_SEND_STATUS; +	}  	if (dw_mci_submit_data_dma(host, data)) {  		int flags = SG_MITER_ATOMIC; @@ -606,6 +732,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)  		temp = mci_readl(host, CTRL);  		temp &= ~SDMMC_CTRL_DMA_ENABLE;  		mci_writel(host, CTRL, temp); + +		/* +		 * Use the initial fifoth_val for PIO mode. +		 * If next issued data may be transfered by DMA mode, +		 * prev_blksz should be invalidated. +		 */ +		mci_writel(host, FIFOTH, host->fifoth_val); +		host->prev_blksz = 0; +	} else { +		/* +		 * Keep the current block size. +		 * It will be used to decide whether to update +		 * fifoth register next time. +		 */ +		host->prev_blksz = data->blksz;  	}  } @@ -632,24 +773,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)  static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)  {  	struct dw_mci *host = slot->host; +	unsigned int clock = slot->clock;  	u32 div;  	u32 clk_en_a; -	if (slot->clock != host->current_speed || force_clkinit) { -		div = host->bus_hz / slot->clock; -		if (host->bus_hz % slot->clock && host->bus_hz > slot->clock) +	if (!clock) { +		mci_writel(host, CLKENA, 0); +		mci_send_cmd(slot, +			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); +	} else if (clock != host->current_speed || force_clkinit) { +		div = host->bus_hz / clock; +		if (host->bus_hz % clock && host->bus_hz > clock)  			/*  			 * move the + 1 after the divide to prevent  			 * over-clocking the card.  			 */  			div += 1; -		div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0; +		div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; -		dev_info(&slot->mmc->class_dev, -			 "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ" -			 " div = %d)\n", slot->id, host->bus_hz, slot->clock, -			 div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); +		if ((clock << div) != slot->__clk_old || force_clkinit) +			dev_info(&slot->mmc->class_dev, +				 "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", +				 slot->id, host->bus_hz, clock, +				 div ? ((host->bus_hz / div) >> 1) : +				 host->bus_hz, div);  		/* disable clock */  		mci_writel(host, CLKENA, 0); @@ -676,9 +824,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)  		mci_send_cmd(slot,  			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); -		host->current_speed = slot->clock; +		/* keep the clock with reflecting clock dividor */ +		slot->__clk_old = clock << div;  	} +	host->current_speed = clock; +  	/* Set the current slot bus width */  	mci_writel(host, CTYPE, (slot->ctype << slot->id));  } @@ -692,19 +843,19 @@ static void __dw_mci_start_request(struct dw_mci *host,  	u32 cmdflags;  	mrq = slot->mrq; -	if (host->pdata->select_slot) -		host->pdata->select_slot(slot->id);  	host->cur_slot = slot;  	host->mrq = mrq;  	host->pending_events = 0;  	host->completed_events = 0; +	host->cmd_status = 0;  	host->data_status = 0; +	host->dir_status = 0;  	data = cmd->data;  	if (data) { -		dw_mci_set_timeout(host); +		mci_writel(host, TMOUT, 0xFFFFFFFF);  		mci_writel(host, BYTCNT, data->blksz*data->blocks);  		mci_writel(host, BLKSIZ, data->blksz);  	} @@ -724,6 +875,8 @@ static void __dw_mci_start_request(struct dw_mci *host,  	if (mrq->stop)  		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); +	else +		host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);  }  static void dw_mci_start_request(struct dw_mci *host, @@ -800,20 +953,19 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	regs = mci_readl(slot->host, UHS_REG);  	/* DDR mode set */ -	if (ios->timing == MMC_TIMING_UHS_DDR50) +	if (ios->timing == MMC_TIMING_MMC_DDR52)  		regs |= ((0x1 << slot->id) << 16);  	else  		regs &= ~((0x1 << slot->id) << 16);  	mci_writel(slot->host, UHS_REG, regs); +	slot->host->timing = ios->timing; -	if (ios->clock) { -		/* -		 * Use mirror of ios->clock to prevent race with mmc -		 * core ios update when finding the minimum. -		 */ -		slot->clock = ios->clock; -	} +	/* +	 * Use mirror of ios->clock to prevent race with mmc +	 * core ios update when finding the minimum. +	 */ +	slot->clock = ios->clock;  	if (drv_data && drv_data->set_ios)  		drv_data->set_ios(slot->host, ios); @@ -824,17 +976,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	switch (ios->power_mode) {  	case MMC_POWER_UP:  		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); -		/* Power up slot */ -		if (slot->host->pdata->setpower) -			slot->host->pdata->setpower(slot->id, mmc->ocr_avail);  		regs = mci_readl(slot->host, PWREN);  		regs |= (1 << slot->id);  		mci_writel(slot->host, PWREN, regs);  		break;  	case MMC_POWER_OFF: -		/* Power down slot */ -		if (slot->host->pdata->setpower) -			slot->host->pdata->setpower(slot->id, 0);  		regs = mci_readl(slot->host, PWREN);  		regs &= ~(1 << slot->id);  		mci_writel(slot->host, PWREN, regs); @@ -848,15 +994,13 @@ static int dw_mci_get_ro(struct mmc_host *mmc)  {  	int read_only;  	struct dw_mci_slot *slot = mmc_priv(mmc); -	struct dw_mci_board *brd = slot->host->pdata; +	int gpio_ro = mmc_gpio_get_ro(mmc);  	/* Use platform get_ro function, else try on board write protect */  	if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT)  		read_only = 0; -	else if (brd->get_ro) -		read_only = brd->get_ro(slot->id); -	else if (gpio_is_valid(slot->wp_gpio)) -		read_only = gpio_get_value(slot->wp_gpio); +	else if (!IS_ERR_VALUE(gpio_ro)) +		read_only = gpio_ro;  	else  		read_only =  			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; @@ -872,20 +1016,27 @@ static int dw_mci_get_cd(struct mmc_host *mmc)  	int present;  	struct dw_mci_slot *slot = mmc_priv(mmc);  	struct dw_mci_board *brd = slot->host->pdata; +	struct dw_mci *host = slot->host; +	int gpio_cd = mmc_gpio_get_cd(mmc);  	/* Use platform get_cd function, else try onboard card detect */  	if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)  		present = 1; -	else if (brd->get_cd) -		present = !brd->get_cd(slot->id); +	else if (!IS_ERR_VALUE(gpio_cd)) +		present = gpio_cd;  	else  		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))  			== 0 ? 1 : 0; -	if (present) +	spin_lock_bh(&host->lock); +	if (present) { +		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);  		dev_dbg(&mmc->class_dev, "card is present\n"); -	else +	} else { +		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);  		dev_dbg(&mmc->class_dev, "card is not present\n"); +	} +	spin_unlock_bh(&host->lock);  	return present;  } @@ -939,6 +1090,38 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)  	}  } +static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ +	struct dw_mci_slot *slot = mmc_priv(mmc); +	struct dw_mci *host = slot->host; +	const struct dw_mci_drv_data *drv_data = host->drv_data; +	struct dw_mci_tuning_data tuning_data; +	int err = -ENOSYS; + +	if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { +		if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { +			tuning_data.blk_pattern = tuning_blk_pattern_8bit; +			tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); +		} else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { +			tuning_data.blk_pattern = tuning_blk_pattern_4bit; +			tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); +		} else { +			return -EINVAL; +		} +	} else if (opcode == MMC_SEND_TUNING_BLOCK) { +		tuning_data.blk_pattern = tuning_blk_pattern_4bit; +		tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); +	} else { +		dev_err(host->dev, +			"Undefined command(%d) for tuning\n", opcode); +		return -EINVAL; +	} + +	if (drv_data && drv_data->execute_tuning) +		err = drv_data->execute_tuning(slot, opcode, &tuning_data); +	return err; +} +  static const struct mmc_host_ops dw_mci_ops = {  	.request		= dw_mci_request,  	.pre_req		= dw_mci_pre_req, @@ -947,6 +1130,7 @@ static const struct mmc_host_ops dw_mci_ops = {  	.get_ro			= dw_mci_get_ro,  	.get_cd			= dw_mci_get_cd,  	.enable_sdio_irq	= dw_mci_enable_sdio_irq, +	.execute_tuning		= dw_mci_execute_tuning,  };  static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -978,7 +1162,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)  	spin_lock(&host->lock);  } -static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) +static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)  {  	u32 status = host->cmd_status; @@ -1012,12 +1196,52 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd  		/* newer ip versions need a delay between retries */  		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)  			mdelay(20); +	} -		if (cmd->data) { -			dw_mci_stop_dma(host); -			host->data = NULL; +	return cmd->error; +} + +static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) +{ +	u32 status = host->data_status; + +	if (status & DW_MCI_DATA_ERROR_FLAGS) { +		if (status & SDMMC_INT_DRTO) { +			data->error = -ETIMEDOUT; +		} else if (status & SDMMC_INT_DCRC) { +			data->error = -EILSEQ; +		} else if (status & SDMMC_INT_EBE) { +			if (host->dir_status == +				DW_MCI_SEND_STATUS) { +				/* +				 * No data CRC status was returned. +				 * The number of bytes transferred +				 * will be exaggerated in PIO mode. +				 */ +				data->bytes_xfered = 0; +				data->error = -ETIMEDOUT; +			} else if (host->dir_status == +					DW_MCI_RECV_STATUS) { +				data->error = -EIO; +			} +		} else { +			/* SDMMC_INT_SBE is included */ +			data->error = -EIO;  		} + +		dev_dbg(host->dev, "data error, status 0x%08x\n", status); + +		/* +		 * After an error, there may be data lingering +		 * in the FIFO +		 */ +		dw_mci_fifo_reset(host); +	} else { +		data->bytes_xfered = data->blocks * data->blksz; +		data->error = 0;  	} + +	return data->error;  }  static void dw_mci_tasklet_func(unsigned long priv) @@ -1025,14 +1249,16 @@ static void dw_mci_tasklet_func(unsigned long priv)  	struct dw_mci *host = (struct dw_mci *)priv;  	struct mmc_data	*data;  	struct mmc_command *cmd; +	struct mmc_request *mrq;  	enum dw_mci_state state;  	enum dw_mci_state prev_state; -	u32 status, ctrl; +	unsigned int err;  	spin_lock(&host->lock);  	state = host->state;  	data = host->data; +	mrq = host->mrq;  	do {  		prev_state = state; @@ -1049,16 +1275,23 @@ static void dw_mci_tasklet_func(unsigned long priv)  			cmd = host->cmd;  			host->cmd = NULL;  			set_bit(EVENT_CMD_COMPLETE, &host->completed_events); -			dw_mci_command_complete(host, cmd); -			if (cmd == host->mrq->sbc && !cmd->error) { +			err = dw_mci_command_complete(host, cmd); +			if (cmd == mrq->sbc && !err) {  				prev_state = state = STATE_SENDING_CMD;  				__dw_mci_start_request(host, host->cur_slot, -						       host->mrq->cmd); +						       mrq->cmd);  				goto unlock;  			} -			if (!host->mrq->data || cmd->error) { -				dw_mci_request_end(host, host->mrq); +			if (cmd->data && err) { +				dw_mci_stop_dma(host); +				send_stop_abort(host, data); +				state = STATE_SENDING_STOP; +				break; +			} + +			if (!cmd->data || err) { +				dw_mci_request_end(host, mrq);  				goto unlock;  			} @@ -1069,8 +1302,7 @@ static void dw_mci_tasklet_func(unsigned long priv)  			if (test_and_clear_bit(EVENT_DATA_ERROR,  					       &host->pending_events)) {  				dw_mci_stop_dma(host); -				if (data->stop) -					send_stop_cmd(host, data); +				send_stop_abort(host, data);  				state = STATE_DATA_ERROR;  				break;  			} @@ -1090,60 +1322,27 @@ static void dw_mci_tasklet_func(unsigned long priv)  			host->data = NULL;  			set_bit(EVENT_DATA_COMPLETE, &host->completed_events); -			status = host->data_status; - -			if (status & DW_MCI_DATA_ERROR_FLAGS) { -				if (status & SDMMC_INT_DRTO) { -					data->error = -ETIMEDOUT; -				} else if (status & SDMMC_INT_DCRC) { -					data->error = -EILSEQ; -				} else if (status & SDMMC_INT_EBE && -					   host->dir_status == -							DW_MCI_SEND_STATUS) { -					/* -					 * No data CRC status was returned. -					 * The number of bytes transferred will -					 * be exaggerated in PIO mode. -					 */ -					data->bytes_xfered = 0; -					data->error = -ETIMEDOUT; -				} else { -					dev_err(host->dev, -						"data FIFO error " -						"(status=%08x)\n", -						status); -					data->error = -EIO; -				} -				/* -				 * After an error, there may be data lingering -				 * in the FIFO, so reset it - doing so -				 * generates a block interrupt, hence setting -				 * the scatter-gather pointer to NULL. -				 */ -				sg_miter_stop(&host->sg_miter); -				host->sg = NULL; -				ctrl = mci_readl(host, CTRL); -				ctrl |= SDMMC_CTRL_FIFO_RESET; -				mci_writel(host, CTRL, ctrl); -			} else { -				data->bytes_xfered = data->blocks * data->blksz; -				data->error = 0; -			} +			err = dw_mci_data_complete(host, data); -			if (!data->stop) { -				dw_mci_request_end(host, host->mrq); -				goto unlock; -			} +			if (!err) { +				if (!data->stop || mrq->sbc) { +					if (mrq->sbc && data->stop) +						data->stop->error = 0; +					dw_mci_request_end(host, mrq); +					goto unlock; +				} -			if (host->mrq->sbc && !data->error) { -				data->stop->error = 0; -				dw_mci_request_end(host, host->mrq); -				goto unlock; +				/* stop command for open-ended transfer*/ +				if (data->stop) +					send_stop_abort(host, data);  			} +			/* +			 * If err has non-zero, +			 * stop-abort command has been already issued. +			 */  			prev_state = state = STATE_SENDING_STOP; -			if (!data->error) -				send_stop_cmd(host, data); +  			/* fall through */  		case STATE_SENDING_STOP: @@ -1151,9 +1350,19 @@ static void dw_mci_tasklet_func(unsigned long priv)  						&host->pending_events))  				break; +			/* CMD error in data command */ +			if (mrq->cmd->error && mrq->data) +				dw_mci_fifo_reset(host); +  			host->cmd = NULL; -			dw_mci_command_complete(host, host->mrq->stop); -			dw_mci_request_end(host, host->mrq); +			host->data = NULL; + +			if (mrq->stop) +				dw_mci_command_complete(host, mrq->stop); +			else +				host->cmd_status = 0; + +			dw_mci_request_end(host, mrq);  			goto unlock;  		case STATE_DATA_ERROR: @@ -1697,7 +1906,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)  		struct mmc_host *mmc = slot->mmc;  		struct mmc_request *mrq;  		int present; -		u32 ctrl;  		present = dw_mci_get_cd(mmc);  		while (present != slot->last_detect_state) { @@ -1709,10 +1917,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)  			/* Card change detected */  			slot->last_detect_state = present; -			/* Mark card as present if applicable */ -			if (present != 0) -				set_bit(DW_MMC_CARD_PRESENT, &slot->flags); -  			/* Clean up queue if present */  			mrq = slot->mrq;  			if (mrq) { @@ -1736,11 +1940,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)  					case STATE_DATA_ERROR:  						if (mrq->data->error == -EINPROGRESS)  							mrq->data->error = -ENOMEDIUM; -						if (!mrq->stop) -							break;  						/* fall through */  					case STATE_SENDING_STOP: -						mrq->stop->error = -ENOMEDIUM; +						if (mrq->stop) +							mrq->stop->error = -ENOMEDIUM;  						break;  					} @@ -1761,25 +1964,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)  			/* Power down slot */  			if (present == 0) { -				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - -				/* -				 * Clear down the FIFO - doing so generates a -				 * block interrupt, hence setting the -				 * scatter-gather pointer to NULL. -				 */ -				sg_miter_stop(&host->sg_miter); -				host->sg = NULL; - -				ctrl = mci_readl(host, CTRL); -				ctrl |= SDMMC_CTRL_FIFO_RESET; -				mci_writel(host, CTRL, ctrl); - +				/* Clear down the FIFO */ +				dw_mci_fifo_reset(host);  #ifdef CONFIG_MMC_DW_IDMAC -				ctrl = mci_readl(host, BMOD); -				/* Software reset of DMA */ -				ctrl |= SDMMC_IDMAC_SWRESET; -				mci_writel(host, BMOD, ctrl); +				dw_mci_idmac_reset(host);  #endif  			} @@ -1838,61 +2026,15 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)  	return quirks;  } - -/* find out bus-width for a given slot */ -static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) -{ -	struct device_node *np = dw_mci_of_find_slot_node(dev, slot); -	u32 bus_wd = 1; - -	if (!np) -		return 1; - -	if (of_property_read_u32(np, "bus-width", &bus_wd)) -		dev_err(dev, "bus-width property not found, assuming width" -			       " as 1\n"); -	return bus_wd; -} - -/* find the write protect gpio for a given slot; or -1 if none specified */ -static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) -{ -	struct device_node *np = dw_mci_of_find_slot_node(dev, slot); -	int gpio; - -	if (!np) -		return -EINVAL; - -	gpio = of_get_named_gpio(np, "wp-gpios", 0); - -	/* Having a missing entry is valid; return silently */ -	if (!gpio_is_valid(gpio)) -		return -EINVAL; - -	if (devm_gpio_request(dev, gpio, "dw-mci-wp")) { -		dev_warn(dev, "gpio [%d] request failed\n", gpio); -		return -EINVAL; -	} - -	return gpio; -}  #else /* CONFIG_OF */  static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)  {  	return 0;  } -static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) -{ -	return 1; -}  static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)  {  	return NULL;  } -static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) -{ -	return -EINVAL; -}  #endif /* CONFIG_OF */  static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) @@ -1901,7 +2043,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)  	struct dw_mci_slot *slot;  	const struct dw_mci_drv_data *drv_data = host->drv_data;  	int ctrl_id, ret; -	u8 bus_width; +	u32 freq[2];  	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);  	if (!mmc) @@ -1916,20 +2058,16 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)  	slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);  	mmc->ops = &dw_mci_ops; -	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); -	mmc->f_max = host->bus_hz; - -	if (host->pdata->get_ocr) -		mmc->ocr_avail = host->pdata->get_ocr(id); -	else -		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; +	if (of_property_read_u32_array(host->dev->of_node, +				       "clock-freq-min-max", freq, 2)) { +		mmc->f_min = DW_MCI_FREQ_MIN; +		mmc->f_max = DW_MCI_FREQ_MAX; +	} else { +		mmc->f_min = freq[0]; +		mmc->f_max = freq[1]; +	} -	/* -	 * Start with slot power disabled, it will be enabled when a card -	 * is detected. -	 */ -	if (host->pdata->setpower) -		host->pdata->setpower(id, 0); +	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;  	if (host->pdata->caps)  		mmc->caps = host->pdata->caps; @@ -1950,22 +2088,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)  	if (host->pdata->caps2)  		mmc->caps2 = host->pdata->caps2; -	if (host->pdata->get_bus_wd) -		bus_width = host->pdata->get_bus_wd(slot->id); -	else if (host->dev->of_node) -		bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id); -	else -		bus_width = 1; - -	switch (bus_width) { -	case 8: -		mmc->caps |= MMC_CAP_8_BIT_DATA; -	case 4: -		mmc->caps |= MMC_CAP_4_BIT_DATA; -	} - -	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) -		mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; +	mmc_of_parse(mmc);  	if (host->pdata->blk_settings) {  		mmc->max_segs = host->pdata->blk_settings->max_segs; @@ -1995,8 +2118,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)  	else  		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); -	slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id); -  	ret = mmc_add_host(mmc);  	if (ret)  		goto err_setup_bus; @@ -2008,12 +2129,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)  	/* Card initially undetected */  	slot->last_detect_state = 0; -	/* -	 * Card may have been plugged in prior to boot so we -	 * need to run the detect tasklet -	 */ -	queue_work(host->card_workqueue, &host->card_work); -  	return 0;  err_setup_bus: @@ -2023,10 +2138,6 @@ err_setup_bus:  static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)  { -	/* Shutdown detect IRQ */ -	if (slot->host->pdata->exit) -		slot->host->pdata->exit(id); -  	/* Debugfs stuff is cleaned up by mmc core */  	mmc_remove_host(slot->mmc);  	slot->host->slot[id] = NULL; @@ -2074,36 +2185,57 @@ no_dma:  	return;  } -static bool mci_wait_reset(struct device *dev, struct dw_mci *host) +static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)  {  	unsigned long timeout = jiffies + msecs_to_jiffies(500); -	unsigned int ctrl; +	u32 ctrl; -	mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | -				SDMMC_CTRL_DMA_RESET)); +	ctrl = mci_readl(host, CTRL); +	ctrl |= reset; +	mci_writel(host, CTRL, ctrl);  	/* wait till resets clear */  	do {  		ctrl = mci_readl(host, CTRL); -		if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | -			      SDMMC_CTRL_DMA_RESET))) +		if (!(ctrl & reset))  			return true;  	} while (time_before(jiffies, timeout)); -	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); +	dev_err(host->dev, +		"Timeout resetting block (ctrl reset %#x)\n", +		ctrl & reset);  	return false;  } +static inline bool dw_mci_fifo_reset(struct dw_mci *host) +{ +	/* +	 * Reseting generates a block interrupt, hence setting +	 * the scatter-gather pointer to NULL. +	 */ +	if (host->sg) { +		sg_miter_stop(&host->sg_miter); +		host->sg = NULL; +	} + +	return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET); +} + +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host) +{ +	return dw_mci_ctrl_reset(host, +				 SDMMC_CTRL_FIFO_RESET | +				 SDMMC_CTRL_RESET | +				 SDMMC_CTRL_DMA_RESET); +} +  #ifdef CONFIG_OF  static struct dw_mci_of_quirks {  	char *quirk;  	int id;  } of_quirks[] = {  	{ -		.quirk	= "supports-highspeed", -		.id	= DW_MCI_QUIRK_HIGHSPEED, -	}, {  		.quirk	= "broken-cd",  		.id	= DW_MCI_QUIRK_BROKEN_CARD_DETECTION,  	}, @@ -2152,11 +2284,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)  			return ERR_PTR(ret);  	} -	if (of_find_property(np, "keep-power-in-suspend", NULL)) -		pdata->pm_caps |= MMC_PM_KEEP_POWER; - -	if (of_find_property(np, "enable-sdio-wakeup", NULL)) -		pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; +	if (of_find_property(np, "supports-highspeed", NULL)) +		pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;  	return pdata;  } @@ -2183,9 +2312,9 @@ int dw_mci_probe(struct dw_mci *host)  		}  	} -	if (!host->pdata->select_slot && host->pdata->num_slots > 1) { +	if (host->pdata->num_slots > 1) {  		dev_err(host->dev, -			"Platform data must supply select_slot function\n"); +			"Platform data must supply num_slots.\n");  		return -ENODEV;  	} @@ -2215,12 +2344,28 @@ int dw_mci_probe(struct dw_mci *host)  			ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);  			if (ret)  				dev_warn(host->dev, -					 "Unable to set bus rate to %ul\n", +					 "Unable to set bus rate to %uHz\n",  					 host->pdata->bus_hz);  		}  		host->bus_hz = clk_get_rate(host->ciu_clk);  	} +	if (!host->bus_hz) { +		dev_err(host->dev, +			"Platform data must supply bus speed\n"); +		ret = -ENODEV; +		goto err_clk_ciu; +	} + +	if (drv_data && drv_data->init) { +		ret = drv_data->init(host); +		if (ret) { +			dev_err(host->dev, +				"implementation specific init failed\n"); +			goto err_clk_ciu; +		} +	} +  	if (drv_data && drv_data->setup_clock) {  		ret = drv_data->setup_clock(host);  		if (ret) { @@ -2248,13 +2393,6 @@ int dw_mci_probe(struct dw_mci *host)  		}  	} -	if (!host->bus_hz) { -		dev_err(host->dev, -			"Platform data must supply bus speed\n"); -		ret = -ENODEV; -		goto err_regulator; -	} -  	host->quirks = host->pdata->quirks;  	spin_lock_init(&host->lock); @@ -2287,7 +2425,7 @@ int dw_mci_probe(struct dw_mci *host)  	}  	/* Reset all blocks */ -	if (!mci_wait_reset(host->dev, host)) +	if (!dw_mci_ctrl_all_reset(host))  		return -ENODEV;  	host->dma_ops = host->pdata->dma_ops; @@ -2317,8 +2455,8 @@ int dw_mci_probe(struct dw_mci *host)  		fifo_size = host->pdata->fifo_depth;  	}  	host->fifo_depth = fifo_size; -	host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) | -			((fifo_size/2) << 0)); +	host->fifoth_val = +		SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);  	mci_writel(host, FIFOTH, host->fifoth_val);  	/* disable clock to CIU */ @@ -2339,7 +2477,7 @@ int dw_mci_probe(struct dw_mci *host)  	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);  	host->card_workqueue = alloc_workqueue("dw-mci-card", -			WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1); +			WQ_MEM_RECLAIM, 1);  	if (!host->card_workqueue) {  		ret = -ENOMEM;  		goto err_dmaunmap; @@ -2398,8 +2536,6 @@ err_workqueue:  err_dmaunmap:  	if (host->use_dma && host->dma_ops->exit)  		host->dma_ops->exit(host); - -err_regulator:  	if (host->vmmc)  		regulator_disable(host->vmmc); @@ -2456,23 +2592,6 @@ EXPORT_SYMBOL(dw_mci_remove);   */  int dw_mci_suspend(struct dw_mci *host)  { -	int i, ret = 0; - -	for (i = 0; i < host->num_slots; i++) { -		struct dw_mci_slot *slot = host->slot[i]; -		if (!slot) -			continue; -		ret = mmc_suspend_host(slot->mmc); -		if (ret < 0) { -			while (--i >= 0) { -				slot = host->slot[i]; -				if (slot) -					mmc_resume_host(host->slot[i]->mmc); -			} -			return ret; -		} -	} -  	if (host->vmmc)  		regulator_disable(host->vmmc); @@ -2493,7 +2612,7 @@ int dw_mci_resume(struct dw_mci *host)  		}  	} -	if (!mci_wait_reset(host->dev, host)) { +	if (!dw_mci_ctrl_all_reset(host)) {  		ret = -ENODEV;  		return ret;  	} @@ -2501,8 +2620,15 @@ int dw_mci_resume(struct dw_mci *host)  	if (host->use_dma && host->dma_ops->init)  		host->dma_ops->init(host); -	/* Restore the old value at FIFOTH register */ +	/* +	 * Restore the initial value at FIFOTH register +	 * And Invalidate the prev_blksz with zero +	 */  	mci_writel(host, FIFOTH, host->fifoth_val); +	host->prev_blksz = 0; + +	/* Put in max timeout */ +	mci_writel(host, TMOUT, 0xFFFFFFFF);  	mci_writel(host, RINTSTS, 0xFFFFFFFF);  	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | @@ -2518,10 +2644,6 @@ int dw_mci_resume(struct dw_mci *host)  			dw_mci_set_ios(slot->mmc, &slot->mmc->ios);  			dw_mci_setup_bus(slot, true);  		} - -		ret = mmc_resume_host(host->slot[i]->mmc); -		if (ret < 0) -			return ret;  	}  	return 0;  } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 81b29941c5b..738fa241d05 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -53,6 +53,7 @@  #define SDMMC_IDINTEN		0x090  #define SDMMC_DSCADDR		0x094  #define SDMMC_BUFADDR		0x098 +#define SDMMC_CDTHRCTL		0x100  #define SDMMC_DATA(x)		(x)  /* @@ -128,6 +129,10 @@  #define SDMMC_CMD_INDX(n)		((n) & 0x1F)  /* Status register defines */  #define SDMMC_GET_FCNT(x)		(((x)>>17) & 0x1FFF) +/* FIFOTH register defines */ +#define SDMMC_SET_FIFOTH(m, r, t)	(((m) & 0x7) << 28 | \ +					 ((r) & 0xFFF) << 16 | \ +					 ((t) & 0xFFF))  /* Internal DMAC interrupt defines */  #define SDMMC_IDMAC_INT_AI		BIT(9)  #define SDMMC_IDMAC_INT_NI		BIT(8) @@ -142,6 +147,8 @@  #define SDMMC_IDMAC_SWRESET		BIT(0)  /* Version ID register define */  #define SDMMC_GET_VERID(x)		((x) & 0xFFFF) +/* Card read threshold */ +#define SDMMC_SET_RD_THLD(v, x)		(((v) & 0x1FFF) << 16 | (x))  /* Register access macros */  #define mci_readl(dev, reg)			\ @@ -178,12 +185,56 @@  extern int dw_mci_probe(struct dw_mci *host);  extern void dw_mci_remove(struct dw_mci *host); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  extern int dw_mci_suspend(struct dw_mci *host);  extern int dw_mci_resume(struct dw_mci *host);  #endif  /** + * struct dw_mci_slot - MMC slot state + * @mmc: The mmc_host representing this slot. + * @host: The MMC controller this slot is using. + * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) + * @ctype: Card type for this slot. + * @mrq: mmc_request currently being processed or waiting to be + *	processed, or NULL when the slot is idle. + * @queue_node: List node for placing this node in the @queue list of + *	&struct dw_mci. + * @clock: Clock rate configured by set_ios(). Protected by host->lock. + * @__clk_old: The last updated clock with reflecting clock divider. + *	Keeping track of this helps us to avoid spamming the console + *	with CONFIG_MMC_CLKGATE. + * @flags: Random state bits associated with the slot. + * @id: Number of this slot. + * @last_detect_state: Most recently observed card detect state. + */ +struct dw_mci_slot { +	struct mmc_host		*mmc; +	struct dw_mci		*host; + +	int			quirks; + +	u32			ctype; + +	struct mmc_request	*mrq; +	struct list_head	queue_node; + +	unsigned int		clock; +	unsigned int		__clk_old; + +	unsigned long		flags; +#define DW_MMC_CARD_PRESENT	0 +#define DW_MMC_CARD_NEED_INIT	1 +	int			id; +	int			last_detect_state; +}; + +struct dw_mci_tuning_data { +	const u8 *blk_pattern; +	unsigned int blksz; +}; + +/**   * dw_mci driver data - dw-mshc implementation specific driver data.   * @caps: mmc subsystem specified capabilities of the controller(s).   * @init: early implementation specific initialization. @@ -191,6 +242,7 @@ extern int dw_mci_resume(struct dw_mci *host);   * @prepare_command: handle CMD register extensions.   * @set_ios: handle bus specific extensions.   * @parse_dt: parse implementation specific device tree properties. + * @execute_tuning: implementation specific tuning procedure.   *   * Provide controller implementation specific extensions. The usage of this   * data structure is fully optional and usage of each member in this structure @@ -203,5 +255,7 @@ struct dw_mci_drv_data {  	void		(*prepare_command)(struct dw_mci *host, u32 *cmdr);  	void		(*set_ios)(struct dw_mci *host, struct mmc_ios *ios);  	int		(*parse_dt)(struct dw_mci *host); +	int		(*execute_tuning)(struct dw_mci_slot *slot, u32 opcode, +					struct dw_mci_tuning_data *tuning_data);  };  #endif /* _DW_MMC_H_ */ diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 66516339e3a..537d6c7a5ae 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -515,10 +515,13 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)  		jz4740_mmc_send_command(host, req->stop); -		timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_PRG_DONE); -		if (timeout) { -			host->state = JZ4740_MMC_STATE_DONE; -			break; +		if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) { +			timeout = jz4740_mmc_poll_irq(host, +						      JZ_MMC_IRQ_PRG_DONE); +			if (timeout) { +				host->state = JZ4740_MMC_STATE_DONE; +				break; +			}  		}  	case JZ4740_MMC_STATE_DONE:  		break; @@ -880,8 +883,6 @@ static int jz4740_mmc_suspend(struct device *dev)  {  	struct jz4740_mmc_host *host = dev_get_drvdata(dev); -	mmc_suspend_host(host->mmc); -  	jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host));  	return 0; @@ -893,8 +894,6 @@ static int jz4740_mmc_resume(struct device *dev)  	jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); -	mmc_resume_host(host->mmc); -  	return 0;  } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 0a87e569134..cc8d4a6099c 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -448,7 +448,6 @@ mmc_spi_command_send(struct mmc_spi_host *host,  {  	struct scratch		*data = host->data;  	u8			*cp = data->status; -	u32			arg = cmd->arg;  	int			status;  	struct spi_transfer	*t; @@ -465,14 +464,12 @@ mmc_spi_command_send(struct mmc_spi_host *host,  	 * We init the whole buffer to all-ones, which is what we need  	 * to write while we're reading (later) response data.  	 */ -	memset(cp++, 0xff, sizeof(data->status)); +	memset(cp, 0xff, sizeof(data->status)); -	*cp++ = 0x40 | cmd->opcode; -	*cp++ = (u8)(arg >> 24); -	*cp++ = (u8)(arg >> 16); -	*cp++ = (u8)(arg >> 8); -	*cp++ = (u8)arg; -	*cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01; +	cp[1] = 0x40 | cmd->opcode; +	put_unaligned_be32(cmd->arg, cp+2); +	cp[6] = crc7_be(0, cp+1, 5) | 0x01; +	cp += 7;  	/* Then, read up to 13 bytes (while writing all-ones):  	 *  - N(CR) (== 1..8) bytes of all-ones @@ -711,10 +708,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,  	 * so we have to cope with this situation and check the response  	 * bit-by-bit. Arggh!!!  	 */ -	pattern  = scratch->status[0] << 24; -	pattern |= scratch->status[1] << 16; -	pattern |= scratch->status[2] << 8; -	pattern |= scratch->status[3]; +	pattern = get_unaligned_be32(scratch->status);  	/* First 3 bit of pattern are undefined */  	pattern |= 0xE0000000; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c3785edc0e9..7ad463e9741 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -13,6 +13,7 @@  #include <linux/init.h>  #include <linux/ioport.h>  #include <linux/device.h> +#include <linux/io.h>  #include <linux/interrupt.h>  #include <linux/kernel.h>  #include <linux/slab.h> @@ -23,6 +24,7 @@  #include <linux/mmc/pm.h>  #include <linux/mmc/host.h>  #include <linux/mmc/card.h> +#include <linux/mmc/slot-gpio.h>  #include <linux/amba/bus.h>  #include <linux/clk.h>  #include <linux/scatterlist.h> @@ -62,6 +64,7 @@ static unsigned int fmax = 515633;   * @signal_direction: input/out direction of bus signals can be indicated   * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock   * @busy_detect: true if busy detection on dat0 is supported + * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply   */  struct variant_data {  	unsigned int		clkreg; @@ -76,6 +79,7 @@ struct variant_data {  	bool			signal_direction;  	bool			pwrreg_clkgate;  	bool			busy_detect; +	bool			pwrreg_nopower;  };  static struct variant_data variant_arm = { @@ -109,6 +113,7 @@ static struct variant_data variant_u300 = {  	.pwrreg_powerup		= MCI_PWR_ON,  	.signal_direction	= true,  	.pwrreg_clkgate		= true, +	.pwrreg_nopower		= true,  };  static struct variant_data variant_nomadik = { @@ -121,6 +126,7 @@ static struct variant_data variant_nomadik = {  	.pwrreg_powerup		= MCI_PWR_ON,  	.signal_direction	= true,  	.pwrreg_clkgate		= true, +	.pwrreg_nopower		= true,  };  static struct variant_data variant_ux500 = { @@ -135,6 +141,7 @@ static struct variant_data variant_ux500 = {  	.signal_direction	= true,  	.pwrreg_clkgate		= true,  	.busy_detect		= true, +	.pwrreg_nopower		= true,  };  static struct variant_data variant_ux500v2 = { @@ -150,6 +157,7 @@ static struct variant_data variant_ux500v2 = {  	.signal_direction	= true,  	.pwrreg_clkgate		= true,  	.busy_detect		= true, +	.pwrreg_nopower		= true,  };  static int mmci_card_busy(struct mmc_host *mmc) @@ -189,6 +197,21 @@ static int mmci_validate_data(struct mmci_host *host,  	return 0;  } +static void mmci_reg_delay(struct mmci_host *host) +{ +	/* +	 * According to the spec, at least three feedback clock cycles +	 * of max 52 MHz must pass between two writes to the MMCICLOCK reg. +	 * Three MCLK clock cycles must pass between two MMCIPOWER reg writes. +	 * Worst delay time during card init is at 100 kHz => 30 us. +	 * Worst delay time when up and running is at 25 MHz => 120 ns. +	 */ +	if (host->cclk < 25000000) +		udelay(30); +	else +		ndelay(120); +} +  /*   * This must be called with host->lock held   */ @@ -278,7 +301,8 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)  	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)  		clk |= MCI_ST_8BIT_BUS; -	if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) +	if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || +	    host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)  		clk |= MCI_ST_UX500_NEG_EDGE;  	mmci_write_clkreg(host, clk); @@ -343,7 +367,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)  #ifdef CONFIG_DMA_ENGINE  static void mmci_dma_setup(struct mmci_host *host)  { -	struct mmci_platform_data *plat = host->plat;  	const char *rxname, *txname;  	dma_cap_mask_t mask; @@ -357,25 +380,6 @@ static void mmci_dma_setup(struct mmci_host *host)  	dma_cap_zero(mask);  	dma_cap_set(DMA_SLAVE, mask); -	if (plat && plat->dma_filter) { -		if (!host->dma_rx_channel && plat->dma_rx_param) { -			host->dma_rx_channel = dma_request_channel(mask, -							   plat->dma_filter, -							   plat->dma_rx_param); -			/* E.g if no DMA hardware is present */ -			if (!host->dma_rx_channel) -				dev_err(mmc_dev(host->mmc), "no RX DMA channel\n"); -		} - -		if (!host->dma_tx_channel && plat->dma_tx_param) { -			host->dma_tx_channel = dma_request_channel(mask, -							   plat->dma_filter, -							   plat->dma_tx_param); -			if (!host->dma_tx_channel) -				dev_warn(mmc_dev(host->mmc), "no TX DMA channel\n"); -		} -	} -  	/*  	 * If only an RX channel is specified, the driver will  	 * attempt to use it bidirectionally, however if it is @@ -423,11 +427,9 @@ static void mmci_dma_setup(struct mmci_host *host)   */  static inline void mmci_dma_release(struct mmci_host *host)  { -	struct mmci_platform_data *plat = host->plat; -  	if (host->dma_rx_channel)  		dma_release_channel(host->dma_rx_channel); -	if (host->dma_tx_channel && plat->dma_tx_param) +	if (host->dma_tx_channel)  		dma_release_channel(host->dma_tx_channel);  	host->dma_rx_channel = host->dma_tx_channel = NULL;  } @@ -763,7 +765,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)  			mmci_write_clkreg(host, clk);  		} -	if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) +	if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || +	    host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)  		datactrl |= MCI_ST_DPSM_DDRMODE;  	/* @@ -900,6 +903,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,  {  	void __iomem *base = host->base;  	bool sbc = (cmd == host->mrq->sbc); +	bool busy_resp = host->variant->busy_detect && +			(cmd->flags & MMC_RSP_BUSY); + +	/* Check if we need to wait for busy completion. */ +	if (host->busy_status && (status & MCI_ST_CARDBUSY)) +		return; + +	/* Enable busy completion if needed and supported. */ +	if (!host->busy_status && busy_resp && +		!(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && +		(readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) { +		writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND, +			base + MMCIMASK0); +		host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND); +		return; +	} + +	/* At busy completion, mask the IRQ and complete the request. */ +	if (host->busy_status) { +		writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND, +			base + MMCIMASK0); +		host->busy_status = 0; +	}  	host->cmd = NULL; @@ -1118,20 +1144,30 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)  			status &= ~MCI_IRQ1MASK;  		} +		/* +		 * We intentionally clear the MCI_ST_CARDBUSY IRQ here (if it's +		 * enabled) since the HW seems to be triggering the IRQ on both +		 * edges while monitoring DAT0 for busy completion. +		 */  		status &= readl(host->base + MMCIMASK0);  		writel(status, host->base + MMCICLEAR);  		dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); +		cmd = host->cmd; +		if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT| +			MCI_CMDSENT|MCI_CMDRESPEND) && cmd) +			mmci_cmd_irq(host, cmd, status); +  		data = host->data;  		if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|  			      MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|  			      MCI_DATABLOCKEND) && data)  			mmci_data_irq(host, data, status); -		cmd = host->cmd; -		if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd) -			mmci_cmd_irq(host, cmd, status); +		/* Don't poll for busy completion in irq context. */ +		if (host->busy_status) +			status &= ~MCI_ST_CARDBUSY;  		ret = 1;  	} while (status); @@ -1231,7 +1267,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		 * indicating signal direction for the signals in  		 * the SD/MMC bus and feedback-clock usage.  		 */ -		pwr |= host->plat->sigdir; +		pwr |= host->pwr_reg_add;  		if (ios->bus_width == MMC_BUS_WIDTH_4)  			pwr &= ~MCI_ST_DATA74DIREN; @@ -1264,6 +1300,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	mmci_set_clkreg(host, ios->clock);  	mmci_write_pwrreg(host, pwr); +	mmci_reg_delay(host);  	spin_unlock_irqrestore(&host->lock, flags); @@ -1271,35 +1308,18 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	pm_runtime_put_autosuspend(mmc_dev(mmc));  } -static int mmci_get_ro(struct mmc_host *mmc) -{ -	struct mmci_host *host = mmc_priv(mmc); - -	if (host->gpio_wp == -ENOSYS) -		return -ENOSYS; - -	return gpio_get_value_cansleep(host->gpio_wp); -} -  static int mmci_get_cd(struct mmc_host *mmc)  {  	struct mmci_host *host = mmc_priv(mmc);  	struct mmci_platform_data *plat = host->plat; -	unsigned int status; +	unsigned int status = mmc_gpio_get_cd(mmc); -	if (host->gpio_cd == -ENOSYS) { +	if (status == -ENOSYS) {  		if (!plat->status)  			return 1; /* Assume always present */  		status = plat->status(mmc_dev(host->mmc)); -	} else -		status = !!gpio_get_value_cansleep(host->gpio_cd) -			^ plat->cd_invert; - -	/* -	 * Use positive logic throughout - status is zero for no card, -	 * non-zero for card inserted. -	 */ +	}  	return status;  } @@ -1336,70 +1356,44 @@ static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)  	return ret;  } -static irqreturn_t mmci_cd_irq(int irq, void *dev_id) -{ -	struct mmci_host *host = dev_id; - -	mmc_detect_change(host->mmc, msecs_to_jiffies(500)); - -	return IRQ_HANDLED; -} -  static struct mmc_host_ops mmci_ops = {  	.request	= mmci_request,  	.pre_req	= mmci_pre_request,  	.post_req	= mmci_post_request,  	.set_ios	= mmci_set_ios, -	.get_ro		= mmci_get_ro, +	.get_ro		= mmc_gpio_get_ro,  	.get_cd		= mmci_get_cd,  	.start_signal_voltage_switch = mmci_sig_volt_switch,  }; -#ifdef CONFIG_OF -static void mmci_dt_populate_generic_pdata(struct device_node *np, -					struct mmci_platform_data *pdata) +static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)  { -	int bus_width = 0; - -	pdata->gpio_wp = of_get_named_gpio(np, "wp-gpios", 0); -	pdata->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0); +	struct mmci_host *host = mmc_priv(mmc); +	int ret = mmc_of_parse(mmc); -	if (of_get_property(np, "cd-inverted", NULL)) -		pdata->cd_invert = true; -	else -		pdata->cd_invert = false; +	if (ret) +		return ret; -	of_property_read_u32(np, "max-frequency", &pdata->f_max); -	if (!pdata->f_max) -		pr_warn("%s has no 'max-frequency' property\n", np->full_name); +	if (of_get_property(np, "st,sig-dir-dat0", NULL)) +		host->pwr_reg_add |= MCI_ST_DATA0DIREN; +	if (of_get_property(np, "st,sig-dir-dat2", NULL)) +		host->pwr_reg_add |= MCI_ST_DATA2DIREN; +	if (of_get_property(np, "st,sig-dir-dat31", NULL)) +		host->pwr_reg_add |= MCI_ST_DATA31DIREN; +	if (of_get_property(np, "st,sig-dir-dat74", NULL)) +		host->pwr_reg_add |= MCI_ST_DATA74DIREN; +	if (of_get_property(np, "st,sig-dir-cmd", NULL)) +		host->pwr_reg_add |= MCI_ST_CMDDIREN; +	if (of_get_property(np, "st,sig-pin-fbclk", NULL)) +		host->pwr_reg_add |= MCI_ST_FBCLKEN;  	if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL)) -		pdata->capabilities |= MMC_CAP_MMC_HIGHSPEED; +		mmc->caps |= MMC_CAP_MMC_HIGHSPEED;  	if (of_get_property(np, "mmc-cap-sd-highspeed", NULL)) -		pdata->capabilities |= MMC_CAP_SD_HIGHSPEED; +		mmc->caps |= MMC_CAP_SD_HIGHSPEED; -	of_property_read_u32(np, "bus-width", &bus_width); -	switch (bus_width) { -	case 0 : -		/* No bus-width supplied. */ -		break; -	case 4 : -		pdata->capabilities |= MMC_CAP_4_BIT_DATA; -		break; -	case 8 : -		pdata->capabilities |= MMC_CAP_8_BIT_DATA; -		break; -	default : -		pr_warn("%s: Unsupported bus width\n", np->full_name); -	} -} -#else -static void mmci_dt_populate_generic_pdata(struct device_node *np, -					struct mmci_platform_data *pdata) -{ -	return; +	return 0;  } -#endif  static int mmci_probe(struct amba_device *dev,  	const struct amba_id *id) @@ -1423,26 +1417,17 @@ static int mmci_probe(struct amba_device *dev,  			return -ENOMEM;  	} -	if (np) -		mmci_dt_populate_generic_pdata(np, plat); +	mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); +	if (!mmc) +		return -ENOMEM; -	ret = amba_request_regions(dev, DRIVER_NAME); +	ret = mmci_of_parse(np, mmc);  	if (ret) -		goto out; - -	mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); -	if (!mmc) { -		ret = -ENOMEM; -		goto rel_regions; -	} +		goto host_free;  	host = mmc_priv(mmc);  	host->mmc = mmc; -	host->gpio_wp = -ENOSYS; -	host->gpio_cd = -ENOSYS; -	host->gpio_cd_irq = -1; -  	host->hw_designer = amba_manf(dev);  	host->hw_revision = amba_rev(dev);  	dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer); @@ -1474,19 +1459,14 @@ static int mmci_probe(struct amba_device *dev,  		dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n",  			host->mclk);  	} +  	host->phybase = dev->res.start; -	host->base = ioremap(dev->res.start, resource_size(&dev->res)); -	if (!host->base) { -		ret = -ENOMEM; +	host->base = devm_ioremap_resource(&dev->dev, &dev->res); +	if (IS_ERR(host->base)) { +		ret = PTR_ERR(host->base);  		goto clk_disable;  	} -	if (variant->busy_detect) { -		mmci_ops.card_busy = mmci_card_busy; -		mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE); -	} - -	mmc->ops = &mmci_ops;  	/*  	 * The ARM and ST versions of the block have slightly different  	 * clock divider equations which means that the minimum divider @@ -1497,36 +1477,17 @@ static int mmci_probe(struct amba_device *dev,  	else  		mmc->f_min = DIV_ROUND_UP(host->mclk, 512);  	/* -	 * If the platform data supplies a maximum operating -	 * frequency, this takes precedence. Else, we fall back -	 * to using the module parameter, which has a (low) -	 * default value in case it is not specified. Either -	 * value must not exceed the clock rate into the block, -	 * of course. +	 * If no maximum operating frequency is supplied, fall back to use +	 * the module parameter, which has a (low) default value in case it +	 * is not specified. Either value must not exceed the clock rate into +	 * the block, of course.  	 */ -	if (plat->f_max) -		mmc->f_max = min(host->mclk, plat->f_max); +	if (mmc->f_max) +		mmc->f_max = min(host->mclk, mmc->f_max);  	else  		mmc->f_max = min(host->mclk, fmax);  	dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); -	host->pinctrl = devm_pinctrl_get(&dev->dev); -	if (IS_ERR(host->pinctrl)) { -		ret = PTR_ERR(host->pinctrl); -		goto clk_disable; -	} - -	host->pins_default = pinctrl_lookup_state(host->pinctrl, -			PINCTRL_STATE_DEFAULT); - -	/* enable pins to be muxed in and configured */ -	if (!IS_ERR(host->pins_default)) { -		ret = pinctrl_select_state(host->pinctrl, host->pins_default); -		if (ret) -			dev_warn(&dev->dev, "could not set default pins\n"); -	} else -		dev_warn(&dev->dev, "could not get default pinstate\n"); -  	/* Get regulators and the supported OCR mask */  	mmc_regulator_get_supply(mmc);  	if (!mmc->ocr_avail) @@ -1534,11 +1495,27 @@ static int mmci_probe(struct amba_device *dev,  	else if (plat->ocr_mask)  		dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); -	mmc->caps = plat->capabilities; -	mmc->caps2 = plat->capabilities2; +	/* DT takes precedence over platform data. */ +	if (!np) { +		if (!plat->cd_invert) +			mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; +		mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; +	} + +	/* We support these capabilities. */ +	mmc->caps |= MMC_CAP_CMD23; + +	if (variant->busy_detect) { +		mmci_ops.card_busy = mmci_card_busy; +		mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE); +		mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; +		mmc->max_busy_timeout = 0; +	} + +	mmc->ops = &mmci_ops;  	/* We support these PM capabilities. */ -	mmc->pm_caps = MMC_PM_KEEP_POWER; +	mmc->pm_caps |= MMC_PM_KEEP_POWER;  	/*  	 * We can do SGIO @@ -1575,62 +1552,30 @@ static int mmci_probe(struct amba_device *dev,  	writel(0, host->base + MMCIMASK1);  	writel(0xfff, host->base + MMCICLEAR); -	if (plat->gpio_cd == -EPROBE_DEFER) { -		ret = -EPROBE_DEFER; -		goto err_gpio_cd; -	} -	if (gpio_is_valid(plat->gpio_cd)) { -		ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)"); -		if (ret == 0) -			ret = gpio_direction_input(plat->gpio_cd); -		if (ret == 0) -			host->gpio_cd = plat->gpio_cd; -		else if (ret != -ENOSYS) -			goto err_gpio_cd; - -		/* -		 * A gpio pin that will detect cards when inserted and removed -		 * will most likely want to trigger on the edges if it is -		 * 0 when ejected and 1 when inserted (or mutatis mutandis -		 * for the inverted case) so we request triggers on both -		 * edges. -		 */ -		ret = request_any_context_irq(gpio_to_irq(plat->gpio_cd), -				mmci_cd_irq, -				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, -				DRIVER_NAME " (cd)", host); -		if (ret >= 0) -			host->gpio_cd_irq = gpio_to_irq(plat->gpio_cd); -	} -	if (plat->gpio_wp == -EPROBE_DEFER) { -		ret = -EPROBE_DEFER; -		goto err_gpio_wp; +	/* If DT, cd/wp gpios must be supplied through it. */ +	if (!np && gpio_is_valid(plat->gpio_cd)) { +		ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0); +		if (ret) +			goto clk_disable;  	} -	if (gpio_is_valid(plat->gpio_wp)) { -		ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)"); -		if (ret == 0) -			ret = gpio_direction_input(plat->gpio_wp); -		if (ret == 0) -			host->gpio_wp = plat->gpio_wp; -		else if (ret != -ENOSYS) -			goto err_gpio_wp; +	if (!np && gpio_is_valid(plat->gpio_wp)) { +		ret = mmc_gpio_request_ro(mmc, plat->gpio_wp); +		if (ret) +			goto clk_disable;  	} -	if ((host->plat->status || host->gpio_cd != -ENOSYS) -	    && host->gpio_cd_irq < 0) -		mmc->caps |= MMC_CAP_NEEDS_POLL; - -	ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); +	ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED, +			DRIVER_NAME " (cmd)", host);  	if (ret) -		goto unmap; +		goto clk_disable;  	if (!dev->irq[1])  		host->singleirq = true;  	else { -		ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, -				  DRIVER_NAME " (pio)", host); +		ret = devm_request_irq(&dev->dev, dev->irq[1], mmci_pio_irq, +				IRQF_SHARED, DRIVER_NAME " (pio)", host);  		if (ret) -			goto irq0_free; +			goto clk_disable;  	}  	writel(MCI_IRQENABLE, host->base + MMCIMASK0); @@ -1652,25 +1597,10 @@ static int mmci_probe(struct amba_device *dev,  	return 0; - irq0_free: -	free_irq(dev->irq[0], host); - unmap: -	if (host->gpio_wp != -ENOSYS) -		gpio_free(host->gpio_wp); - err_gpio_wp: -	if (host->gpio_cd_irq >= 0) -		free_irq(host->gpio_cd_irq, host); -	if (host->gpio_cd != -ENOSYS) -		gpio_free(host->gpio_cd); - err_gpio_cd: -	iounmap(host->base);   clk_disable:  	clk_disable_unprepare(host->clk);   host_free:  	mmc_free_host(mmc); - rel_regions: -	amba_release_regions(dev); - out:  	return ret;  } @@ -1678,8 +1608,6 @@ static int mmci_remove(struct amba_device *dev)  {  	struct mmc_host *mmc = amba_get_drvdata(dev); -	amba_set_drvdata(dev, NULL); -  	if (mmc) {  		struct mmci_host *host = mmc_priv(mmc); @@ -1698,68 +1626,48 @@ static int mmci_remove(struct amba_device *dev)  		writel(0, host->base + MMCIDATACTRL);  		mmci_dma_release(host); -		free_irq(dev->irq[0], host); -		if (!host->singleirq) -			free_irq(dev->irq[1], host); - -		if (host->gpio_wp != -ENOSYS) -			gpio_free(host->gpio_wp); -		if (host->gpio_cd_irq >= 0) -			free_irq(host->gpio_cd_irq, host); -		if (host->gpio_cd != -ENOSYS) -			gpio_free(host->gpio_cd); - -		iounmap(host->base);  		clk_disable_unprepare(host->clk); -  		mmc_free_host(mmc); - -		amba_release_regions(dev);  	}  	return 0;  } -#ifdef CONFIG_SUSPEND -static int mmci_suspend(struct device *dev) +#ifdef CONFIG_PM +static void mmci_save(struct mmci_host *host)  { -	struct amba_device *adev = to_amba_device(dev); -	struct mmc_host *mmc = amba_get_drvdata(adev); -	int ret = 0; +	unsigned long flags; -	if (mmc) { -		struct mmci_host *host = mmc_priv(mmc); +	spin_lock_irqsave(&host->lock, flags); -		ret = mmc_suspend_host(mmc); -		if (ret == 0) { -			pm_runtime_get_sync(dev); -			writel(0, host->base + MMCIMASK0); -		} +	writel(0, host->base + MMCIMASK0); +	if (host->variant->pwrreg_nopower) { +		writel(0, host->base + MMCIDATACTRL); +		writel(0, host->base + MMCIPOWER); +		writel(0, host->base + MMCICLOCK);  	} +	mmci_reg_delay(host); -	return ret; +	spin_unlock_irqrestore(&host->lock, flags);  } -static int mmci_resume(struct device *dev) +static void mmci_restore(struct mmci_host *host)  { -	struct amba_device *adev = to_amba_device(dev); -	struct mmc_host *mmc = amba_get_drvdata(adev); -	int ret = 0; - -	if (mmc) { -		struct mmci_host *host = mmc_priv(mmc); +	unsigned long flags; -		writel(MCI_IRQENABLE, host->base + MMCIMASK0); -		pm_runtime_put(dev); +	spin_lock_irqsave(&host->lock, flags); -		ret = mmc_resume_host(mmc); +	if (host->variant->pwrreg_nopower) { +		writel(host->clk_reg, host->base + MMCICLOCK); +		writel(host->datactrl_reg, host->base + MMCIDATACTRL); +		writel(host->pwr_reg, host->base + MMCIPOWER);  	} +	writel(MCI_IRQENABLE, host->base + MMCIMASK0); +	mmci_reg_delay(host); -	return ret; +	spin_unlock_irqrestore(&host->lock, flags);  } -#endif -#ifdef CONFIG_PM_RUNTIME  static int mmci_runtime_suspend(struct device *dev)  {  	struct amba_device *adev = to_amba_device(dev); @@ -1767,6 +1675,8 @@ static int mmci_runtime_suspend(struct device *dev)  	if (mmc) {  		struct mmci_host *host = mmc_priv(mmc); +		pinctrl_pm_select_sleep_state(dev); +		mmci_save(host);  		clk_disable_unprepare(host->clk);  	} @@ -1781,6 +1691,8 @@ static int mmci_runtime_resume(struct device *dev)  	if (mmc) {  		struct mmci_host *host = mmc_priv(mmc);  		clk_prepare_enable(host->clk); +		mmci_restore(host); +		pinctrl_pm_select_default_state(dev);  	}  	return 0; @@ -1788,8 +1700,9 @@ static int mmci_runtime_resume(struct device *dev)  #endif  static const struct dev_pm_ops mmci_dev_pm_ops = { -	SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume) -	SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL) +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, +				pm_runtime_force_resume) +	SET_PM_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL)  };  static struct amba_id mmci_ids[] = { diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 69080fab637..347d942d740 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -13,6 +13,16 @@  #define MCI_PWR_ON		0x03  #define MCI_OD			(1 << 6)  #define MCI_ROD			(1 << 7) +/* + * The ST Micro version does not have ROD and reuse the voltage registers for + * direction settings. + */ +#define MCI_ST_DATA2DIREN	(1 << 2) +#define MCI_ST_CMDDIREN		(1 << 3) +#define MCI_ST_DATA0DIREN	(1 << 4) +#define MCI_ST_DATA31DIREN	(1 << 5) +#define MCI_ST_FBCLKEN		(1 << 7) +#define MCI_ST_DATA74DIREN	(1 << 8)  #define MMCICLOCK		0x004  #define MCI_CLK_ENABLE		(1 << 8) @@ -38,10 +48,11 @@  #define MCI_CPSM_INTERRUPT	(1 << 8)  #define MCI_CPSM_PENDING	(1 << 9)  #define MCI_CPSM_ENABLE		(1 << 10) -#define MCI_SDIO_SUSP		(1 << 11) -#define MCI_ENCMD_COMPL		(1 << 12) -#define MCI_NIEN		(1 << 13) -#define MCI_CE_ATACMD		(1 << 14) +/* Argument flag extenstions in the ST Micro versions */ +#define MCI_ST_SDIO_SUSP	(1 << 11) +#define MCI_ST_ENCMD_COMPL	(1 << 12) +#define MCI_ST_NIEN		(1 << 13) +#define MCI_ST_CE_ATACMD	(1 << 14)  #define MMCIRESPCMD		0x010  #define MMCIRESPONSE0		0x014 @@ -139,6 +150,7 @@  /* Extended status bits for the ST Micro variants */  #define MCI_ST_SDIOITMASK	(1 << 22)  #define MCI_ST_CEATAENDMASK	(1 << 23) +#define MCI_ST_BUSYEND		(1 << 24)  #define MMCIMASK1		0x040  #define MMCIFIFOCNT		0x048 @@ -174,9 +186,6 @@ struct mmci_host {  	struct mmc_data		*data;  	struct mmc_host		*mmc;  	struct clk		*clk; -	int			gpio_cd; -	int			gpio_wp; -	int			gpio_cd_irq;  	bool			singleirq;  	spinlock_t		lock; @@ -184,8 +193,10 @@ struct mmci_host {  	unsigned int		mclk;  	unsigned int		cclk;  	u32			pwr_reg; +	u32			pwr_reg_add;  	u32			clk_reg;  	u32			datactrl_reg; +	u32			busy_status;  	bool			vqmmc_enabled;  	struct mmci_platform_data *plat;  	struct variant_data	*variant; @@ -200,10 +211,6 @@ struct mmci_host {  	struct sg_mapping_iter	sg_miter;  	unsigned int		size; -	/* pinctrl handles */ -	struct pinctrl		*pinctrl; -	struct pinctrl_state	*pins_default; -  #ifdef CONFIG_DMA_ENGINE  	/* DMA stuff */  	struct dma_chan		*dma_current; diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c new file mode 100644 index 00000000000..74924a04026 --- /dev/null +++ b/drivers/mmc/host/moxart-mmc.c @@ -0,0 +1,730 @@ +/* + * MOXA ART MMC host driver. + * + * Copyright (C) 2014 Jonas Jensen + * + * Jonas Jensen <jonas.jensen@gmail.com> + * + * Based on code from + * Moxa Technologies Co., Ltd. <www.moxa.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2.  This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sd.h> +#include <linux/sched.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/clk.h> +#include <linux/bitops.h> +#include <linux/of_dma.h> +#include <linux/spinlock.h> + +#define REG_COMMAND		0 +#define REG_ARGUMENT		4 +#define REG_RESPONSE0		8 +#define REG_RESPONSE1		12 +#define REG_RESPONSE2		16 +#define REG_RESPONSE3		20 +#define REG_RESPONSE_COMMAND	24 +#define REG_DATA_CONTROL	28 +#define REG_DATA_TIMER		32 +#define REG_DATA_LENGTH		36 +#define REG_STATUS		40 +#define REG_CLEAR		44 +#define REG_INTERRUPT_MASK	48 +#define REG_POWER_CONTROL	52 +#define REG_CLOCK_CONTROL	56 +#define REG_BUS_WIDTH		60 +#define REG_DATA_WINDOW		64 +#define REG_FEATURE		68 +#define REG_REVISION		72 + +/* REG_COMMAND */ +#define CMD_SDC_RESET		BIT(10) +#define CMD_EN			BIT(9) +#define CMD_APP_CMD		BIT(8) +#define CMD_LONG_RSP		BIT(7) +#define CMD_NEED_RSP		BIT(6) +#define CMD_IDX_MASK		0x3f + +/* REG_RESPONSE_COMMAND */ +#define RSP_CMD_APP		BIT(6) +#define RSP_CMD_IDX_MASK	0x3f + +/* REG_DATA_CONTROL */ +#define DCR_DATA_FIFO_RESET     BIT(8) +#define DCR_DATA_THRES          BIT(7) +#define DCR_DATA_EN		BIT(6) +#define DCR_DMA_EN		BIT(5) +#define DCR_DATA_WRITE		BIT(4) +#define DCR_BLK_SIZE		0x0f + +/* REG_DATA_LENGTH */ +#define DATA_LEN_MASK		0xffffff + +/* REG_STATUS */ +#define WRITE_PROT		BIT(12) +#define CARD_DETECT		BIT(11) +/* 1-10 below can be sent to either registers, interrupt or clear. */ +#define CARD_CHANGE		BIT(10) +#define FIFO_ORUN		BIT(9) +#define FIFO_URUN		BIT(8) +#define DATA_END		BIT(7) +#define CMD_SENT		BIT(6) +#define DATA_CRC_OK		BIT(5) +#define RSP_CRC_OK		BIT(4) +#define DATA_TIMEOUT		BIT(3) +#define RSP_TIMEOUT		BIT(2) +#define DATA_CRC_FAIL		BIT(1) +#define RSP_CRC_FAIL		BIT(0) + +#define MASK_RSP		(RSP_TIMEOUT | RSP_CRC_FAIL | \ +				 RSP_CRC_OK  | CARD_DETECT  | CMD_SENT) + +#define MASK_DATA		(DATA_CRC_OK   | DATA_END | \ +				 DATA_CRC_FAIL | DATA_TIMEOUT) + +#define MASK_INTR_PIO		(FIFO_URUN | FIFO_ORUN | CARD_CHANGE) + +/* REG_POWER_CONTROL */ +#define SD_POWER_ON		BIT(4) +#define SD_POWER_MASK		0x0f + +/* REG_CLOCK_CONTROL */ +#define CLK_HISPD		BIT(9) +#define CLK_OFF			BIT(8) +#define CLK_SD			BIT(7) +#define CLK_DIV_MASK		0x7f + +/* REG_BUS_WIDTH */ +#define BUS_WIDTH_8		BIT(2) +#define BUS_WIDTH_4		BIT(1) +#define BUS_WIDTH_1		BIT(0) + +#define MMC_VDD_360		23 +#define MIN_POWER		(MMC_VDD_360 - SD_POWER_MASK) +#define MAX_RETRIES		500000 + +struct moxart_host { +	spinlock_t			lock; + +	void __iomem			*base; + +	phys_addr_t			reg_phys; + +	struct dma_chan			*dma_chan_tx; +	struct dma_chan                 *dma_chan_rx; +	struct dma_async_tx_descriptor	*tx_desc; +	struct mmc_host			*mmc; +	struct mmc_request		*mrq; +	struct scatterlist		*cur_sg; +	struct completion		dma_complete; +	struct completion		pio_complete; + +	u32				num_sg; +	u32				data_remain; +	u32				data_len; +	u32				fifo_width; +	u32				timeout; +	u32				rate; + +	long				sysclk; + +	bool				have_dma; +	bool				is_removed; +}; + +static inline void moxart_init_sg(struct moxart_host *host, +				  struct mmc_data *data) +{ +	host->cur_sg = data->sg; +	host->num_sg = data->sg_len; +	host->data_remain = host->cur_sg->length; + +	if (host->data_remain > host->data_len) +		host->data_remain = host->data_len; +} + +static inline int moxart_next_sg(struct moxart_host *host) +{ +	int remain; +	struct mmc_data *data = host->mrq->cmd->data; + +	host->cur_sg++; +	host->num_sg--; + +	if (host->num_sg > 0) { +		host->data_remain = host->cur_sg->length; +		remain = host->data_len - data->bytes_xfered; +		if (remain > 0 && remain < host->data_remain) +			host->data_remain = remain; +	} + +	return host->num_sg; +} + +static int moxart_wait_for_status(struct moxart_host *host, +				  u32 mask, u32 *status) +{ +	int ret = -ETIMEDOUT; +	u32 i; + +	for (i = 0; i < MAX_RETRIES; i++) { +		*status = readl(host->base + REG_STATUS); +		if (!(*status & mask)) { +			udelay(5); +			continue; +		} +		writel(*status & mask, host->base + REG_CLEAR); +		ret = 0; +		break; +	} + +	if (ret) +		dev_err(mmc_dev(host->mmc), "timed out waiting for status\n"); + +	return ret; +} + + +static void moxart_send_command(struct moxart_host *host, +	struct mmc_command *cmd) +{ +	u32 status, cmdctrl; + +	writel(RSP_TIMEOUT  | RSP_CRC_OK | +	       RSP_CRC_FAIL | CMD_SENT, host->base + REG_CLEAR); +	writel(cmd->arg, host->base + REG_ARGUMENT); + +	cmdctrl = cmd->opcode & CMD_IDX_MASK; +	if (cmdctrl == SD_APP_SET_BUS_WIDTH    || cmdctrl == SD_APP_OP_COND   || +	    cmdctrl == SD_APP_SEND_SCR         || cmdctrl == SD_APP_SD_STATUS || +	    cmdctrl == SD_APP_SEND_NUM_WR_BLKS) +		cmdctrl |= CMD_APP_CMD; + +	if (cmd->flags & MMC_RSP_PRESENT) +		cmdctrl |= CMD_NEED_RSP; + +	if (cmd->flags & MMC_RSP_136) +		cmdctrl |= CMD_LONG_RSP; + +	writel(cmdctrl | CMD_EN, host->base + REG_COMMAND); + +	if (moxart_wait_for_status(host, MASK_RSP, &status) == -ETIMEDOUT) +		cmd->error = -ETIMEDOUT; + +	if (status & RSP_TIMEOUT) { +		cmd->error = -ETIMEDOUT; +		return; +	} +	if (status & RSP_CRC_FAIL) { +		cmd->error = -EIO; +		return; +	} +	if (status & RSP_CRC_OK) { +		if (cmd->flags & MMC_RSP_136) { +			cmd->resp[3] = readl(host->base + REG_RESPONSE0); +			cmd->resp[2] = readl(host->base + REG_RESPONSE1); +			cmd->resp[1] = readl(host->base + REG_RESPONSE2); +			cmd->resp[0] = readl(host->base + REG_RESPONSE3); +		} else { +			cmd->resp[0] = readl(host->base + REG_RESPONSE0); +		} +	} +} + +static void moxart_dma_complete(void *param) +{ +	struct moxart_host *host = param; + +	complete(&host->dma_complete); +} + +static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host) +{ +	u32 len, dir_data, dir_slave; +	unsigned long dma_time; +	struct dma_async_tx_descriptor *desc = NULL; +	struct dma_chan *dma_chan; + +	if (host->data_len == data->bytes_xfered) +		return; + +	if (data->flags & MMC_DATA_WRITE) { +		dma_chan = host->dma_chan_tx; +		dir_data = DMA_TO_DEVICE; +		dir_slave = DMA_MEM_TO_DEV; +	} else { +		dma_chan = host->dma_chan_rx; +		dir_data = DMA_FROM_DEVICE; +		dir_slave = DMA_DEV_TO_MEM; +	} + +	len = dma_map_sg(dma_chan->device->dev, data->sg, +			 data->sg_len, dir_data); + +	if (len > 0) { +		desc = dmaengine_prep_slave_sg(dma_chan, data->sg, +					       len, dir_slave, +					       DMA_PREP_INTERRUPT | +					       DMA_CTRL_ACK); +	} else { +		dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n"); +	} + +	if (desc) { +		host->tx_desc = desc; +		desc->callback = moxart_dma_complete; +		desc->callback_param = host; +		dmaengine_submit(desc); +		dma_async_issue_pending(dma_chan); +	} + +	data->bytes_xfered += host->data_remain; + +	dma_time = wait_for_completion_interruptible_timeout( +		   &host->dma_complete, host->timeout); + +	dma_unmap_sg(dma_chan->device->dev, +		     data->sg, data->sg_len, +		     dir_data); +} + + +static void moxart_transfer_pio(struct moxart_host *host) +{ +	struct mmc_data *data = host->mrq->cmd->data; +	u32 *sgp, len = 0, remain, status; + +	if (host->data_len == data->bytes_xfered) +		return; + +	sgp = sg_virt(host->cur_sg); +	remain = host->data_remain; + +	if (data->flags & MMC_DATA_WRITE) { +		while (remain > 0) { +			if (moxart_wait_for_status(host, FIFO_URUN, &status) +			     == -ETIMEDOUT) { +				data->error = -ETIMEDOUT; +				complete(&host->pio_complete); +				return; +			} +			for (len = 0; len < remain && len < host->fifo_width;) { +				iowrite32(*sgp, host->base + REG_DATA_WINDOW); +				sgp++; +				len += 4; +			} +			remain -= len; +		} + +	} else { +		while (remain > 0) { +			if (moxart_wait_for_status(host, FIFO_ORUN, &status) +			    == -ETIMEDOUT) { +				data->error = -ETIMEDOUT; +				complete(&host->pio_complete); +				return; +			} +			for (len = 0; len < remain && len < host->fifo_width;) { +				/* SCR data must be read in big endian. */ +				if (data->mrq->cmd->opcode == SD_APP_SEND_SCR) +					*sgp = ioread32be(host->base + +							  REG_DATA_WINDOW); +				else +					*sgp = ioread32(host->base + +							REG_DATA_WINDOW); +				sgp++; +				len += 4; +			} +			remain -= len; +		} +	} + +	data->bytes_xfered += host->data_remain - remain; +	host->data_remain = remain; + +	if (host->data_len != data->bytes_xfered) +		moxart_next_sg(host); +	else +		complete(&host->pio_complete); +} + +static void moxart_prepare_data(struct moxart_host *host) +{ +	struct mmc_data *data = host->mrq->cmd->data; +	u32 datactrl; +	int blksz_bits; + +	if (!data) +		return; + +	host->data_len = data->blocks * data->blksz; +	blksz_bits = ffs(data->blksz) - 1; +	BUG_ON(1 << blksz_bits != data->blksz); + +	moxart_init_sg(host, data); + +	datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE); + +	if (data->flags & MMC_DATA_WRITE) +		datactrl |= DCR_DATA_WRITE; + +	if ((host->data_len > host->fifo_width) && host->have_dma) +		datactrl |= DCR_DMA_EN; + +	writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL); +	writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR); +	writel(host->rate, host->base + REG_DATA_TIMER); +	writel(host->data_len, host->base + REG_DATA_LENGTH); +	writel(datactrl, host->base + REG_DATA_CONTROL); +} + +static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct moxart_host *host = mmc_priv(mmc); +	unsigned long pio_time, flags; +	u32 status; + +	spin_lock_irqsave(&host->lock, flags); + +	init_completion(&host->dma_complete); +	init_completion(&host->pio_complete); + +	host->mrq = mrq; + +	if (readl(host->base + REG_STATUS) & CARD_DETECT) { +		mrq->cmd->error = -ETIMEDOUT; +		goto request_done; +	} + +	moxart_prepare_data(host); +	moxart_send_command(host, host->mrq->cmd); + +	if (mrq->cmd->data) { +		if ((host->data_len > host->fifo_width) && host->have_dma) { + +			writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK); + +			spin_unlock_irqrestore(&host->lock, flags); + +			moxart_transfer_dma(mrq->cmd->data, host); + +			spin_lock_irqsave(&host->lock, flags); +		} else { + +			writel(MASK_INTR_PIO, host->base + REG_INTERRUPT_MASK); + +			spin_unlock_irqrestore(&host->lock, flags); + +			/* PIO transfers start from interrupt. */ +			pio_time = wait_for_completion_interruptible_timeout( +				   &host->pio_complete, host->timeout); + +			spin_lock_irqsave(&host->lock, flags); +		} + +		if (host->is_removed) { +			dev_err(mmc_dev(host->mmc), "card removed\n"); +			mrq->cmd->error = -ETIMEDOUT; +			goto request_done; +		} + +		if (moxart_wait_for_status(host, MASK_DATA, &status) +		    == -ETIMEDOUT) { +			mrq->cmd->data->error = -ETIMEDOUT; +			goto request_done; +		} + +		if (status & DATA_CRC_FAIL) +			mrq->cmd->data->error = -ETIMEDOUT; + +		if (mrq->cmd->data->stop) +			moxart_send_command(host, mrq->cmd->data->stop); +	} + +request_done: +	spin_unlock_irqrestore(&host->lock, flags); +	mmc_request_done(host->mmc, mrq); +} + +static irqreturn_t moxart_irq(int irq, void *devid) +{ +	struct moxart_host *host = (struct moxart_host *)devid; +	u32 status; +	unsigned long flags; + +	spin_lock_irqsave(&host->lock, flags); + +	status = readl(host->base + REG_STATUS); +	if (status & CARD_CHANGE) { +		host->is_removed = status & CARD_DETECT; +		if (host->is_removed && host->have_dma) { +			dmaengine_terminate_all(host->dma_chan_tx); +			dmaengine_terminate_all(host->dma_chan_rx); +		} +		host->mrq = NULL; +		writel(MASK_INTR_PIO, host->base + REG_CLEAR); +		writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK); +		mmc_detect_change(host->mmc, 0); +	} +	if (status & (FIFO_ORUN | FIFO_URUN) && host->mrq) +		moxart_transfer_pio(host); + +	spin_unlock_irqrestore(&host->lock, flags); + +	return IRQ_HANDLED; +} + +static void moxart_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct moxart_host *host = mmc_priv(mmc); +	unsigned long flags; +	u8 power, div; +	u32 ctrl; + +	spin_lock_irqsave(&host->lock, flags); + +	if (ios->clock) { +		for (div = 0; div < CLK_DIV_MASK; ++div) { +			if (ios->clock >= host->sysclk / (2 * (div + 1))) +				break; +		} +		ctrl = CLK_SD | div; +		host->rate = host->sysclk / (2 * (div + 1)); +		if (host->rate > host->sysclk) +			ctrl |= CLK_HISPD; +		writel(ctrl, host->base + REG_CLOCK_CONTROL); +	} + +	if (ios->power_mode == MMC_POWER_OFF) { +		writel(readl(host->base + REG_POWER_CONTROL) & ~SD_POWER_ON, +		       host->base + REG_POWER_CONTROL); +	} else { +		if (ios->vdd < MIN_POWER) +			power = 0; +		else +			power = ios->vdd - MIN_POWER; + +		writel(SD_POWER_ON | (u32) power, +		       host->base + REG_POWER_CONTROL); +	} + +	switch (ios->bus_width) { +	case MMC_BUS_WIDTH_4: +		writel(BUS_WIDTH_4, host->base + REG_BUS_WIDTH); +		break; +	case MMC_BUS_WIDTH_8: +		writel(BUS_WIDTH_8, host->base + REG_BUS_WIDTH); +		break; +	default: +		writel(BUS_WIDTH_1, host->base + REG_BUS_WIDTH); +		break; +	} + +	spin_unlock_irqrestore(&host->lock, flags); +} + + +static int moxart_get_ro(struct mmc_host *mmc) +{ +	struct moxart_host *host = mmc_priv(mmc); + +	return !!(readl(host->base + REG_STATUS) & WRITE_PROT); +} + +static struct mmc_host_ops moxart_ops = { +	.request = moxart_request, +	.set_ios = moxart_set_ios, +	.get_ro = moxart_get_ro, +}; + +static int moxart_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct device_node *node = dev->of_node; +	struct resource res_mmc; +	struct mmc_host *mmc; +	struct moxart_host *host = NULL; +	struct dma_slave_config cfg; +	struct clk *clk; +	void __iomem *reg_mmc; +	dma_cap_mask_t mask; +	int irq, ret; +	u32 i; + +	mmc = mmc_alloc_host(sizeof(struct moxart_host), dev); +	if (!mmc) { +		dev_err(dev, "mmc_alloc_host failed\n"); +		ret = -ENOMEM; +		goto out; +	} + +	ret = of_address_to_resource(node, 0, &res_mmc); +	if (ret) { +		dev_err(dev, "of_address_to_resource failed\n"); +		goto out; +	} + +	irq = irq_of_parse_and_map(node, 0); +	if (irq <= 0) { +		dev_err(dev, "irq_of_parse_and_map failed\n"); +		ret = -EINVAL; +		goto out; +	} + +	clk = of_clk_get(node, 0); +	if (IS_ERR(clk)) { +		dev_err(dev, "of_clk_get failed\n"); +		ret = PTR_ERR(clk); +		goto out; +	} + +	reg_mmc = devm_ioremap_resource(dev, &res_mmc); +	if (IS_ERR(reg_mmc)) { +		ret = PTR_ERR(reg_mmc); +		goto out; +	} + +	mmc_of_parse(mmc); + +	dma_cap_zero(mask); +	dma_cap_set(DMA_SLAVE, mask); + +	host = mmc_priv(mmc); +	host->mmc = mmc; +	host->base = reg_mmc; +	host->reg_phys = res_mmc.start; +	host->timeout = msecs_to_jiffies(1000); +	host->sysclk = clk_get_rate(clk); +	host->fifo_width = readl(host->base + REG_FEATURE) << 2; +	host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); +	host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); + +	spin_lock_init(&host->lock); + +	mmc->ops = &moxart_ops; +	mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2); +	mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2); +	mmc->ocr_avail = 0xffff00;	/* Support 2.0v - 3.6v power. */ + +	if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { +		dev_dbg(dev, "PIO mode transfer enabled\n"); +		host->have_dma = false; +	} else { +		dev_dbg(dev, "DMA channels found (%p,%p)\n", +			 host->dma_chan_tx, host->dma_chan_rx); +		host->have_dma = true; + +		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + +		cfg.direction = DMA_MEM_TO_DEV; +		cfg.src_addr = 0; +		cfg.dst_addr = host->reg_phys + REG_DATA_WINDOW; +		dmaengine_slave_config(host->dma_chan_tx, &cfg); + +		cfg.direction = DMA_DEV_TO_MEM; +		cfg.src_addr = host->reg_phys + REG_DATA_WINDOW; +		cfg.dst_addr = 0; +		dmaengine_slave_config(host->dma_chan_rx, &cfg); +	} + +	switch ((readl(host->base + REG_BUS_WIDTH) >> 3) & 3) { +	case 1: +		mmc->caps |= MMC_CAP_4_BIT_DATA; +		break; +	case 2: +		mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; +		break; +	default: +		break; +	} + +	writel(0, host->base + REG_INTERRUPT_MASK); + +	writel(CMD_SDC_RESET, host->base + REG_COMMAND); +	for (i = 0; i < MAX_RETRIES; i++) { +		if (!(readl(host->base + REG_COMMAND) & CMD_SDC_RESET)) +			break; +		udelay(5); +	} + +	ret = devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host); +	if (ret) +		goto out; + +	dev_set_drvdata(dev, mmc); +	mmc_add_host(mmc); + +	dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width); + +	return 0; + +out: +	if (mmc) +		mmc_free_host(mmc); +	return ret; +} + +static int moxart_remove(struct platform_device *pdev) +{ +	struct mmc_host *mmc = dev_get_drvdata(&pdev->dev); +	struct moxart_host *host = mmc_priv(mmc); + +	dev_set_drvdata(&pdev->dev, NULL); + +	if (mmc) { +		if (!IS_ERR(host->dma_chan_tx)) +			dma_release_channel(host->dma_chan_tx); +		if (!IS_ERR(host->dma_chan_rx)) +			dma_release_channel(host->dma_chan_rx); +		mmc_remove_host(mmc); +		mmc_free_host(mmc); + +		writel(0, host->base + REG_INTERRUPT_MASK); +		writel(0, host->base + REG_POWER_CONTROL); +		writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF, +		       host->base + REG_CLOCK_CONTROL); +	} + +	kfree(host); + +	return 0; +} + +static const struct of_device_id moxart_mmc_match[] = { +	{ .compatible = "moxa,moxart-mmc" }, +	{ .compatible = "faraday,ftsdc010" }, +	{ } +}; + +static struct platform_driver moxart_mmc_driver = { +	.probe      = moxart_probe, +	.remove     = moxart_remove, +	.driver     = { +		.name		= "mmc-moxart", +		.owner		= THIS_MODULE, +		.of_match_table	= moxart_mmc_match, +	}, +}; +module_platform_driver(moxart_mmc_driver); + +MODULE_ALIAS("platform:mmc-moxart"); +MODULE_DESCRIPTION("MOXA ART MMC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>"); diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b900de4e7e9..9405ecdaf6c 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1416,28 +1416,10 @@ ioremap_free:  }  #ifdef CONFIG_PM -#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ -static void -do_resume_work(struct work_struct *work) -{ -	struct msmsdcc_host *host = -		container_of(work, struct msmsdcc_host, resume_task); -	struct mmc_host	*mmc = host->mmc; - -	if (mmc) { -		mmc_resume_host(mmc); -		if (host->stat_irq) -			enable_irq(host->stat_irq); -	} -} -#endif - -  static int  msmsdcc_suspend(struct platform_device *dev, pm_message_t state)  {  	struct mmc_host *mmc = mmc_get_drvdata(dev); -	int rc = 0;  	if (mmc) {  		struct msmsdcc_host *host = mmc_priv(mmc); @@ -1445,14 +1427,11 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)  		if (host->stat_irq)  			disable_irq(host->stat_irq); -		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) -			rc = mmc_suspend_host(mmc); -		if (!rc) -			msmsdcc_writel(host, 0, MMCIMASK0); +		msmsdcc_writel(host, 0, MMCIMASK0);  		if (host->clks_on)  			msmsdcc_disable_clocks(host, 0);  	} -	return rc; +	return 0;  }  static int @@ -1467,8 +1446,6 @@ msmsdcc_resume(struct platform_device *dev)  		msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); -		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) -			mmc_resume_host(mmc);  		if (host->stat_irq)  			enable_irq(host->stat_irq);  #if BUSCLK_PWRSAVE diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 06c5b0b28eb..6b4c5ad3b39 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -79,11 +79,11 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)  		unsigned long t = jiffies + HZ;  		unsigned int hw_state,  count = 0;  		do { +			hw_state = mvsd_read(MVSD_HW_STATE);  			if (time_after(jiffies, t)) {  				dev_warn(host->dev, "FIFO_EMPTY bit missing\n");  				break;  			} -			hw_state = mvsd_read(MVSD_HW_STATE);  			count++;  		} while (!(hw_state & (1 << 13)));  		dev_dbg(host->dev, "*** wait for FIFO_EMPTY bit " @@ -354,6 +354,20 @@ static irqreturn_t mvsd_irq(int irq, void *dev)  		intr_status, mvsd_read(MVSD_NOR_INTR_EN),  		mvsd_read(MVSD_HW_STATE)); +	/* +	 * It looks like, SDIO IP can issue one late, spurious irq +	 * although all irqs should be disabled. To work around this, +	 * bail out early, if we didn't expect any irqs to occur. +	 */ +	if (!mvsd_read(MVSD_NOR_INTR_EN) && !mvsd_read(MVSD_ERR_INTR_EN)) { +		dev_dbg(host->dev, "spurious irq detected intr 0x%04x intr_en 0x%04x erri 0x%04x erri_en 0x%04x\n", +			mvsd_read(MVSD_NOR_INTR_STATUS), +			mvsd_read(MVSD_NOR_INTR_EN), +			mvsd_read(MVSD_ERR_INTR_STATUS), +			mvsd_read(MVSD_ERR_INTR_EN)); +		return IRQ_HANDLED; +	} +  	spin_lock(&host->lock);  	/* PIO handling, if needed. Messy business... */ @@ -655,7 +669,7 @@ static const struct mmc_host_ops mvsd_ops = {  	.enable_sdio_irq	= mvsd_enable_sdio_irq,  }; -static void __init +static void  mv_conf_mbus_windows(struct mvsd_host *host,  		     const struct mbus_dram_target_info *dram)  { @@ -677,7 +691,7 @@ mv_conf_mbus_windows(struct mvsd_host *host,  	}  } -static int __init mvsd_probe(struct platform_device *pdev) +static int mvsd_probe(struct platform_device *pdev)  {  	struct device_node *np = pdev->dev.of_node;  	struct mmc_host *mmc = NULL; @@ -775,9 +789,9 @@ static int __init mvsd_probe(struct platform_device *pdev)  	spin_lock_init(&host->lock); -	host->base = devm_request_and_ioremap(&pdev->dev, r); -	if (!host->base) { -		ret = -ENOMEM; +	host->base = devm_ioremap_resource(&pdev->dev, r); +	if (IS_ERR(host->base)) { +		ret = PTR_ERR(host->base);  		goto out;  	} @@ -801,10 +815,10 @@ static int __init mvsd_probe(struct platform_device *pdev)  		goto out;  	if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) -		dev_notice(&pdev->dev, "using GPIO for card detection\n"); +		dev_dbg(&pdev->dev, "using GPIO for card detection\n");  	else -		dev_notice(&pdev->dev, -			   "lacking card detect (fall back to polling)\n"); +		dev_dbg(&pdev->dev, "lacking card detect (fall back to polling)\n"); +  	return 0;  out: @@ -819,7 +833,7 @@ out:  	return ret;  } -static int __exit mvsd_remove(struct platform_device *pdev) +static int mvsd_remove(struct platform_device *pdev)  {  	struct mmc_host *mmc = platform_get_drvdata(pdev); @@ -838,33 +852,6 @@ static int __exit mvsd_remove(struct platform_device *pdev)  	return 0;  } -#ifdef CONFIG_PM -static int mvsd_suspend(struct platform_device *dev, pm_message_t state) -{ -	struct mmc_host *mmc = platform_get_drvdata(dev); -	int ret = 0; - -	if (mmc) -		ret = mmc_suspend_host(mmc); - -	return ret; -} - -static int mvsd_resume(struct platform_device *dev) -{ -	struct mmc_host *mmc = platform_get_drvdata(dev); -	int ret = 0; - -	if (mmc) -		ret = mmc_resume_host(mmc); - -	return ret; -} -#else -#define mvsd_suspend	NULL -#define mvsd_resume	NULL -#endif -  static const struct of_device_id mvsdio_dt_ids[] = {  	{ .compatible = "marvell,orion-sdio" },  	{ /* sentinel */ } @@ -872,16 +859,15 @@ static const struct of_device_id mvsdio_dt_ids[] = {  MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);  static struct platform_driver mvsd_driver = { -	.remove		= __exit_p(mvsd_remove), -	.suspend	= mvsd_suspend, -	.resume		= mvsd_resume, +	.probe		= mvsd_probe, +	.remove		= mvsd_remove,  	.driver		= {  		.name	= DRIVER_NAME,  		.of_match_table = mvsdio_dt_ids,  	},  }; -module_platform_driver_probe(mvsd_driver, mvsd_probe); +module_platform_driver(mvsd_driver);  /* maximum card clock frequency (default 50MHz) */  module_param(maxfreq, int, 0); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index c174c6a0d22..ed1cb93c378 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -124,9 +124,8 @@ enum mxcmci_type {  struct mxcmci_host {  	struct mmc_host		*mmc; -	struct resource		*res;  	void __iomem		*base; -	int			irq; +	dma_addr_t		phys_base;  	int			detect_irq;  	struct dma_chan		*dma;  	struct dma_async_tx_descriptor *desc; @@ -154,8 +153,6 @@ struct mxcmci_host {  	struct work_struct	datawork;  	spinlock_t		lock; -	struct regulator	*vcc; -  	int			burstlen;  	int			dmareq;  	struct dma_slave_config dma_slave_config; @@ -241,37 +238,15 @@ static inline void mxcmci_writew(struct mxcmci_host *host, u16 val, int reg)  static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); -static inline void mxcmci_init_ocr(struct mxcmci_host *host) -{ -	host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc"); - -	if (IS_ERR(host->vcc)) { -		host->vcc = NULL; -	} else { -		host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc); -		if (host->pdata && host->pdata->ocr_avail) -			dev_warn(mmc_dev(host->mmc), -				"pdata->ocr_avail will not be used\n"); -	} - -	if (host->vcc == NULL) { -		/* fall-back to platform data */ -		if (host->pdata && host->pdata->ocr_avail) -			host->mmc->ocr_avail = host->pdata->ocr_avail; -		else -			host->mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; -	} -} - -static inline void mxcmci_set_power(struct mxcmci_host *host, -				    unsigned char power_mode, -				    unsigned int vdd) +static void mxcmci_set_power(struct mxcmci_host *host, unsigned int vdd)  { -	if (host->vcc) { -		if (power_mode == MMC_POWER_UP) -			mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); -		else if (power_mode == MMC_POWER_OFF) -			mmc_regulator_set_ocr(host->mmc, host->vcc, 0); +	if (!IS_ERR(host->mmc->supply.vmmc)) { +		if (host->power_mode == MMC_POWER_UP) +			mmc_regulator_set_ocr(host->mmc, +					      host->mmc->supply.vmmc, vdd); +		else if (host->power_mode == MMC_POWER_OFF) +			mmc_regulator_set_ocr(host->mmc, +					      host->mmc->supply.vmmc, 0);  	}  	if (host->pdata && host->pdata->setpower) @@ -299,7 +274,6 @@ static void mxcmci_softreset(struct mxcmci_host *host)  	mxcmci_writew(host, 0xff, MMC_REG_RES_TO);  } -static int mxcmci_setup_dma(struct mmc_host *mmc);  #if IS_ENABLED(CONFIG_PPC_MPC512x)  static inline void buffer_swap32(u32 *buf, int len) @@ -868,8 +842,8 @@ static int mxcmci_setup_dma(struct mmc_host *mmc)  	struct mxcmci_host *host = mmc_priv(mmc);  	struct dma_slave_config *config = &host->dma_slave_config; -	config->dst_addr = host->res->start + MMC_REG_BUFFER_ACCESS; -	config->src_addr = host->res->start + MMC_REG_BUFFER_ACCESS; +	config->dst_addr = host->phys_base + MMC_REG_BUFFER_ACCESS; +	config->src_addr = host->phys_base + MMC_REG_BUFFER_ACCESS;  	config->dst_addr_width = 4;  	config->src_addr_width = 4;  	config->dst_maxburst = host->burstlen; @@ -911,8 +885,8 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;  	if (host->power_mode != ios->power_mode) { -		mxcmci_set_power(host, ios->power_mode, ios->vdd);  		host->power_mode = ios->power_mode; +		mxcmci_set_power(host, ios->vdd);  		if (ios->power_mode == MMC_POWER_ON)  			host->cmdat |= CMD_DAT_CONT_INIT; @@ -1040,8 +1014,8 @@ static const struct mmc_host_ops mxcmci_ops = {  static int mxcmci_probe(struct platform_device *pdev)  {  	struct mmc_host *mmc; -	struct mxcmci_host *host = NULL; -	struct resource *iores, *r; +	struct mxcmci_host *host; +	struct resource *res;  	int ret = 0, irq;  	bool dat3_card_detect = false;  	dma_cap_mask_t mask; @@ -1052,21 +1026,25 @@ static int mxcmci_probe(struct platform_device *pdev)  	of_id = of_match_device(mxcmci_of_match, &pdev->dev); -	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	irq = platform_get_irq(pdev, 0); -	if (!iores || irq < 0) +	if (irq < 0)  		return -EINVAL; -	r = request_mem_region(iores->start, resource_size(iores), pdev->name); -	if (!r) -		return -EBUSY; +	mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); +	if (!mmc) +		return -ENOMEM; -	mmc = mmc_alloc_host(sizeof(struct mxcmci_host), &pdev->dev); -	if (!mmc) { -		ret = -ENOMEM; -		goto out_release_mem; +	host = mmc_priv(mmc); + +	host->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(host->base)) { +		ret = PTR_ERR(host->base); +		goto out_free;  	} +	host->phys_base = res->start; +  	ret = mmc_of_parse(mmc);  	if (ret)  		goto out_free; @@ -1084,13 +1062,6 @@ static int mxcmci_probe(struct platform_device *pdev)  	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;  	mmc->max_seg_size = mmc->max_req_size; -	host = mmc_priv(mmc); -	host->base = ioremap(r->start, resource_size(r)); -	if (!host->base) { -		ret = -ENOMEM; -		goto out_free; -	} -  	if (of_id) {  		const struct platform_device_id *id_entry = of_id->data;  		host->devtype = id_entry->driver_data; @@ -1112,7 +1083,14 @@ static int mxcmci_probe(struct platform_device *pdev)  			&& !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))  		dat3_card_detect = true; -	mxcmci_init_ocr(host); +	ret = mmc_regulator_get_supply(mmc); +	if (ret) { +		if (pdata && ret != -EPROBE_DEFER) +			mmc->ocr_avail = pdata->ocr_avail ? : +				MMC_VDD_32_33 | MMC_VDD_33_34; +		else +			goto out_free; +	}  	if (dat3_card_detect)  		host->default_irq_mask = @@ -1120,19 +1098,16 @@ static int mxcmci_probe(struct platform_device *pdev)  	else  		host->default_irq_mask = 0; -	host->res = r; -	host->irq = irq; -  	host->clk_ipg = devm_clk_get(&pdev->dev, "ipg");  	if (IS_ERR(host->clk_ipg)) {  		ret = PTR_ERR(host->clk_ipg); -		goto out_iounmap; +		goto out_free;  	}  	host->clk_per = devm_clk_get(&pdev->dev, "per");  	if (IS_ERR(host->clk_per)) {  		ret = PTR_ERR(host->clk_per); -		goto out_iounmap; +		goto out_free;  	}  	clk_prepare_enable(host->clk_per); @@ -1159,9 +1134,9 @@ static int mxcmci_probe(struct platform_device *pdev)  	if (!host->pdata) {  		host->dma = dma_request_slave_channel(&pdev->dev, "rx-tx");  	} else { -		r = platform_get_resource(pdev, IORESOURCE_DMA, 0); -		if (r) { -			host->dmareq = r->start; +		res = platform_get_resource(pdev, IORESOURCE_DMA, 0); +		if (res) { +			host->dmareq = res->start;  			host->dma_data.peripheral_type = IMX_DMATYPE_SDHC;  			host->dma_data.priority = DMA_PRIO_LOW;  			host->dma_data.dma_request = host->dmareq; @@ -1178,7 +1153,8 @@ static int mxcmci_probe(struct platform_device *pdev)  	INIT_WORK(&host->datawork, mxcmci_datawork); -	ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host); +	ret = devm_request_irq(&pdev->dev, irq, mxcmci_irq, 0, +			       dev_name(&pdev->dev), host);  	if (ret)  		goto out_free_dma; @@ -1188,7 +1164,7 @@ static int mxcmci_probe(struct platform_device *pdev)  		ret = host->pdata->init(&pdev->dev, mxcmci_detect_irq,  				host->mmc);  		if (ret) -			goto out_free_irq; +			goto out_free_dma;  	}  	init_timer(&host->watchdog); @@ -1199,20 +1175,17 @@ static int mxcmci_probe(struct platform_device *pdev)  	return 0; -out_free_irq: -	free_irq(host->irq, host);  out_free_dma:  	if (host->dma)  		dma_release_channel(host->dma); +  out_clk_put:  	clk_disable_unprepare(host->clk_per);  	clk_disable_unprepare(host->clk_ipg); -out_iounmap: -	iounmap(host->base); +  out_free:  	mmc_free_host(mmc); -out_release_mem: -	release_mem_region(iores->start, resource_size(iores)); +  	return ret;  } @@ -1223,62 +1196,41 @@ static int mxcmci_remove(struct platform_device *pdev)  	mmc_remove_host(mmc); -	if (host->vcc) -		regulator_put(host->vcc); -  	if (host->pdata && host->pdata->exit)  		host->pdata->exit(&pdev->dev, mmc); -	free_irq(host->irq, host); -	iounmap(host->base); -  	if (host->dma)  		dma_release_channel(host->dma);  	clk_disable_unprepare(host->clk_per);  	clk_disable_unprepare(host->clk_ipg); -	release_mem_region(host->res->start, resource_size(host->res)); -  	mmc_free_host(mmc);  	return 0;  } -#ifdef CONFIG_PM -static int mxcmci_suspend(struct device *dev) +static int __maybe_unused mxcmci_suspend(struct device *dev)  {  	struct mmc_host *mmc = dev_get_drvdata(dev);  	struct mxcmci_host *host = mmc_priv(mmc); -	int ret = 0; -	if (mmc) -		ret = mmc_suspend_host(mmc);  	clk_disable_unprepare(host->clk_per);  	clk_disable_unprepare(host->clk_ipg); - -	return ret; +	return 0;  } -static int mxcmci_resume(struct device *dev) +static int __maybe_unused mxcmci_resume(struct device *dev)  {  	struct mmc_host *mmc = dev_get_drvdata(dev);  	struct mxcmci_host *host = mmc_priv(mmc); -	int ret = 0;  	clk_prepare_enable(host->clk_per);  	clk_prepare_enable(host->clk_ipg); -	if (mmc) -		ret = mmc_resume_host(mmc); - -	return ret; +	return 0;  } -static const struct dev_pm_ops mxcmci_pm_ops = { -	.suspend	= mxcmci_suspend, -	.resume		= mxcmci_resume, -}; -#endif +static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);  static struct platform_driver mxcmci_driver = {  	.probe		= mxcmci_probe, @@ -1287,9 +1239,7 @@ static struct platform_driver mxcmci_driver = {  	.driver		= {  		.name		= DRIVER_NAME,  		.owner		= THIS_MODULE, -#ifdef CONFIG_PM  		.pm	= &mxcmci_pm_ops, -#endif  		.of_match_table	= mxcmci_of_match,  	}  }; diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index e1fa3ef735e..babfea03ba8 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -38,6 +38,7 @@  #include <linux/mmc/host.h>  #include <linux/mmc/mmc.h>  #include <linux/mmc/sdio.h> +#include <linux/mmc/slot-gpio.h>  #include <linux/gpio.h>  #include <linux/regulator/consumer.h>  #include <linux/module.h> @@ -69,37 +70,29 @@ struct mxs_mmc_host {  	unsigned char			bus_width;  	spinlock_t			lock;  	int				sdio_irq_en; -	int				wp_gpio; -	bool				wp_inverted; -	bool				cd_inverted;  	bool				broken_cd; -	bool				non_removable;  }; -static int mxs_mmc_get_ro(struct mmc_host *mmc) +static int mxs_mmc_get_cd(struct mmc_host *mmc)  {  	struct mxs_mmc_host *host = mmc_priv(mmc); -	int ret; +	struct mxs_ssp *ssp = &host->ssp; +	int present, ret; -	if (!gpio_is_valid(host->wp_gpio)) -		return -EINVAL; +	if (host->broken_cd) +		return -ENOSYS; -	ret = gpio_get_value(host->wp_gpio); +	ret = mmc_gpio_get_cd(mmc); +	if (ret >= 0) +		return ret; -	if (host->wp_inverted) -		ret = !ret; +	present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) & +			BM_SSP_STATUS_CARD_DETECT); -	return ret; -} +	if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) +		present = !present; -static int mxs_mmc_get_cd(struct mmc_host *mmc) -{ -	struct mxs_mmc_host *host = mmc_priv(mmc); -	struct mxs_ssp *ssp = &host->ssp; - -	return host->non_removable || host->broken_cd || -		!(readl(ssp->base + HW_SSP_STATUS(ssp)) & -		  BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted; +	return present;  }  static int mxs_mmc_reset(struct mxs_mmc_host *host) @@ -549,7 +542,7 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)  static const struct mmc_host_ops mxs_mmc_ops = {  	.request = mxs_mmc_request, -	.get_ro = mxs_mmc_get_ro, +	.get_ro = mmc_gpio_get_ro,  	.get_cd = mxs_mmc_get_cd,  	.set_ios = mxs_mmc_set_ios,  	.enable_sdio_irq = mxs_mmc_enable_sdio_irq, @@ -585,9 +578,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)  	struct resource *iores;  	int ret = 0, irq_err;  	struct regulator *reg_vmmc; -	enum of_gpio_flags flags;  	struct mxs_ssp *ssp; -	u32 bus_width = 0;  	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	irq_err = platform_get_irq(pdev, 0); @@ -648,23 +639,15 @@ static int mxs_mmc_probe(struct platform_device *pdev)  	mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |  		    MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL; -	of_property_read_u32(np, "bus-width", &bus_width); -	if (bus_width == 4) -		mmc->caps |= MMC_CAP_4_BIT_DATA; -	else if (bus_width == 8) -		mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;  	host->broken_cd = of_property_read_bool(np, "broken-cd"); -	host->non_removable = of_property_read_bool(np, "non-removable"); -	if (host->non_removable) -		mmc->caps |= MMC_CAP_NONREMOVABLE; -	host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); -	if (flags & OF_GPIO_ACTIVE_LOW) -		host->wp_inverted = 1; - -	host->cd_inverted = of_property_read_bool(np, "cd-inverted");  	mmc->f_min = 400000;  	mmc->f_max = 288000000; + +	ret = mmc_of_parse(mmc); +	if (ret) +		goto out_clk_disable; +  	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;  	mmc->max_segs = 52; @@ -724,13 +707,9 @@ static int mxs_mmc_suspend(struct device *dev)  	struct mmc_host *mmc = dev_get_drvdata(dev);  	struct mxs_mmc_host *host = mmc_priv(mmc);  	struct mxs_ssp *ssp = &host->ssp; -	int ret = 0; - -	ret = mmc_suspend_host(mmc);  	clk_disable_unprepare(ssp->clk); - -	return ret; +	return 0;  }  static int mxs_mmc_resume(struct device *dev) @@ -738,13 +717,9 @@ static int mxs_mmc_resume(struct device *dev)  	struct mmc_host *mmc = dev_get_drvdata(dev);  	struct mxs_mmc_host *host = mmc_priv(mmc);  	struct mxs_ssp *ssp = &host->ssp; -	int ret = 0;  	clk_prepare_enable(ssp->clk); - -	ret = mmc_resume_host(mmc); - -	return ret; +	return 0;  }  static const struct dev_pm_ops mxs_mmc_pm_ops = { diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index b94f38ec2a8..81974ecdfcb 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -22,9 +22,11 @@  #include <linux/delay.h>  #include <linux/spinlock.h>  #include <linux/timer.h> +#include <linux/of.h>  #include <linux/omap-dma.h>  #include <linux/mmc/host.h>  #include <linux/mmc/card.h> +#include <linux/mmc/mmc.h>  #include <linux/clk.h>  #include <linux/scatterlist.h>  #include <linux/slab.h> @@ -90,17 +92,6 @@  #define OMAP_MMC_CMDTYPE_AC	2  #define OMAP_MMC_CMDTYPE_ADTC	3 -#define OMAP_DMA_MMC_TX		21 -#define OMAP_DMA_MMC_RX		22 -#define OMAP_DMA_MMC2_TX	54 -#define OMAP_DMA_MMC2_RX	55 - -#define OMAP24XX_DMA_MMC2_TX	47 -#define OMAP24XX_DMA_MMC2_RX	48 -#define OMAP24XX_DMA_MMC1_TX	61 -#define OMAP24XX_DMA_MMC1_RX	62 - -  #define DRIVER_NAME "mmci-omap"  /* Specifies how often in millisecs to poll for card status changes @@ -128,7 +119,6 @@ struct mmc_omap_slot {  struct mmc_omap_host {  	int			initialized; -	int			suspended;  	struct mmc_request *	mrq;  	struct mmc_command *	cmd;  	struct mmc_data *	data; @@ -141,7 +131,6 @@ struct mmc_omap_host {  	u32			dma_rx_burst;  	struct dma_chan		*dma_tx;  	u32			dma_tx_burst; -	struct resource		*mem_res;  	void __iomem		*virt_base;  	unsigned int		phys_base;  	int			irq; @@ -164,7 +153,6 @@ struct mmc_omap_host {  	u32			total_bytes_left;  	unsigned		features; -	unsigned		use_dma:1;  	unsigned		brs_received:1, dma_done:1;  	unsigned		dma_in_use:1;  	spinlock_t		dma_lock; @@ -189,7 +177,7 @@ static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)  	unsigned long tick_ns;  	if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) { -		tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq; +		tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, slot->fclk_freq);  		ndelay(8 * tick_ns);  	}  } @@ -349,6 +337,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)  	u32 cmdreg;  	u32 resptype;  	u32 cmdtype; +	u16 irq_mask;  	host->cmd = cmd; @@ -401,12 +390,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)  	OMAP_MMC_WRITE(host, CTO, 200);  	OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);  	OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); -	OMAP_MMC_WRITE(host, IE, -		       OMAP_MMC_STAT_A_EMPTY    | OMAP_MMC_STAT_A_FULL    | -		       OMAP_MMC_STAT_CMD_CRC    | OMAP_MMC_STAT_CMD_TOUT  | -		       OMAP_MMC_STAT_DATA_CRC   | OMAP_MMC_STAT_DATA_TOUT | -		       OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR  | -		       OMAP_MMC_STAT_END_OF_DATA); +	irq_mask = OMAP_MMC_STAT_A_EMPTY    | OMAP_MMC_STAT_A_FULL    | +		   OMAP_MMC_STAT_CMD_CRC    | OMAP_MMC_STAT_CMD_TOUT  | +		   OMAP_MMC_STAT_DATA_CRC   | OMAP_MMC_STAT_DATA_TOUT | +		   OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR  | +		   OMAP_MMC_STAT_END_OF_DATA; +	if (cmd->opcode == MMC_ERASE) +		irq_mask &= ~OMAP_MMC_STAT_DATA_TOUT; +	OMAP_MMC_WRITE(host, IE, irq_mask);  	OMAP_MMC_WRITE(host, CMD, cmdreg);  } @@ -444,7 +435,7 @@ static void mmc_omap_send_stop_work(struct work_struct *work)  	struct mmc_data *data = host->stop_data;  	unsigned long tick_ns; -	tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq; +	tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, slot->fclk_freq);  	ndelay(8*tick_ns);  	mmc_omap_start_command(host, data->stop); @@ -486,7 +477,7 @@ mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)  	u16 stat = 0;  	/* Sending abort takes 80 clocks. Have some extra and round up */ -	timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq; +	timeout = DIV_ROUND_UP(120 * USEC_PER_SEC, slot->fclk_freq);  	restarts = 0;  	while (restarts < maxloops) {  		OMAP_MMC_WRITE(host, STAT, 0xFFFF); @@ -686,8 +677,8 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)  	if (n > host->buffer_bytes_left)  		n = host->buffer_bytes_left; -	nwords = n / 2; -	nwords += n & 1; /* handle odd number of bytes to transfer */ +	/* Round up to handle odd number of bytes to transfer */ +	nwords = DIV_ROUND_UP(n, 2);  	host->buffer_bytes_left -= n;  	host->total_bytes_left -= n; @@ -956,7 +947,7 @@ static void  mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)  {  	struct mmc_data *data = req->data; -	int i, use_dma, block_size; +	int i, use_dma = 1, block_size;  	unsigned sg_len;  	host->data = data; @@ -981,13 +972,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)  	sg_len = (data->blocks == 1) ? 1 : data->sg_len;  	/* Only do DMA for entire blocks */ -	use_dma = host->use_dma; -	if (use_dma) { -		for (i = 0; i < sg_len; i++) { -			if ((data->sg[i].length % block_size) != 0) { -				use_dma = 0; -				break; -			} +	for (i = 0; i < sg_len; i++) { +		if ((data->sg[i].length % block_size) != 0) { +			use_dma = 0; +			break;  		}  	} @@ -1250,7 +1238,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)  	mmc->caps = 0;  	if (host->pdata->slots[id].wires >= 4) -		mmc->caps |= MMC_CAP_4_BIT_DATA; +		mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;  	mmc->ops = &mmc_omap_ops;  	mmc->f_min = 400000; @@ -1273,6 +1261,13 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)  	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;  	mmc->max_seg_size = mmc->max_req_size; +	if (slot->pdata->get_cover_state != NULL) { +		setup_timer(&slot->cover_timer, mmc_omap_cover_timer, +			    (unsigned long)slot); +		tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler, +			     (unsigned long)slot); +	} +  	r = mmc_add_host(mmc);  	if (r < 0)  		goto err_remove_host; @@ -1289,11 +1284,6 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)  					&dev_attr_cover_switch);  		if (r < 0)  			goto err_remove_slot_name; - -		setup_timer(&slot->cover_timer, mmc_omap_cover_timer, -			    (unsigned long)slot); -		tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler, -			     (unsigned long)slot);  		tasklet_schedule(&slot->cover_tasklet);  	} @@ -1331,7 +1321,7 @@ static int mmc_omap_probe(struct platform_device *pdev)  	struct mmc_omap_host *host = NULL;  	struct resource *res;  	dma_cap_mask_t mask; -	unsigned sig; +	unsigned sig = 0;  	int i, ret = 0;  	int irq; @@ -1341,24 +1331,22 @@ static int mmc_omap_probe(struct platform_device *pdev)  	}  	if (pdata->nr_slots == 0) {  		dev_err(&pdev->dev, "no slots\n"); -		return -ENXIO; +		return -EPROBE_DEFER;  	} -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	host = devm_kzalloc(&pdev->dev, sizeof(struct mmc_omap_host), +			    GFP_KERNEL); +	if (host == NULL) +		return -ENOMEM; +  	irq = platform_get_irq(pdev, 0); -	if (res == NULL || irq < 0) +	if (irq < 0)  		return -ENXIO; -	res = request_mem_region(res->start, resource_size(res), -				 pdev->name); -	if (res == NULL) -		return -EBUSY; - -	host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL); -	if (host == NULL) { -		ret = -ENOMEM; -		goto err_free_mem_region; -	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	host->virt_base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(host->virt_base)) +		return PTR_ERR(host->virt_base);  	INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);  	INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work); @@ -1380,20 +1368,11 @@ static int mmc_omap_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, host);  	host->id = pdev->id; -	host->mem_res = res;  	host->irq = irq; -	host->use_dma = 1; -	host->irq = irq; -	host->phys_base = host->mem_res->start; -	host->virt_base = ioremap(res->start, resource_size(res)); -	if (!host->virt_base) -		goto err_ioremap; - +	host->phys_base = res->start;  	host->iclk = clk_get(&pdev->dev, "ick"); -	if (IS_ERR(host->iclk)) { -		ret = PTR_ERR(host->iclk); -		goto err_free_mmc_host; -	} +	if (IS_ERR(host->iclk)) +		return PTR_ERR(host->iclk);  	clk_enable(host->iclk);  	host->fclk = clk_get(&pdev->dev, "fck"); @@ -1408,19 +1387,20 @@ static int mmc_omap_probe(struct platform_device *pdev)  	host->dma_tx_burst = -1;  	host->dma_rx_burst = -1; -	if (mmc_omap2()) -		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX; -	else -		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX; -	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); +	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); +	if (res) +		sig = res->start; +	host->dma_tx = dma_request_slave_channel_compat(mask, +				omap_dma_filter_fn, &sig, &pdev->dev, "tx");  	if (!host->dma_tx)  		dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",  			sig); -	if (mmc_omap2()) -		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX; -	else -		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX; -	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); + +	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); +	if (res) +		sig = res->start; +	host->dma_rx = dma_request_slave_channel_compat(mask, +				omap_dma_filter_fn, &sig, &pdev->dev, "rx");  	if (!host->dma_rx)  		dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",  			sig); @@ -1470,12 +1450,6 @@ err_free_dma:  err_free_iclk:  	clk_disable(host->iclk);  	clk_put(host->iclk); -err_free_mmc_host: -	iounmap(host->virt_base); -err_ioremap: -	kfree(host); -err_free_mem_region: -	release_mem_region(res->start, resource_size(res));  	return ret;  } @@ -1503,74 +1477,25 @@ static int mmc_omap_remove(struct platform_device *pdev)  	if (host->dma_rx)  		dma_release_channel(host->dma_rx); -	iounmap(host->virt_base); -	release_mem_region(pdev->resource[0].start, -			   pdev->resource[0].end - pdev->resource[0].start + 1);  	destroy_workqueue(host->mmc_omap_wq); -	kfree(host); -  	return 0;  } -#ifdef CONFIG_PM -static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) -{ -	int i, ret = 0; -	struct mmc_omap_host *host = platform_get_drvdata(pdev); - -	if (host == NULL || host->suspended) -		return 0; - -	for (i = 0; i < host->nr_slots; i++) { -		struct mmc_omap_slot *slot; - -		slot = host->slots[i]; -		ret = mmc_suspend_host(slot->mmc); -		if (ret < 0) { -			while (--i >= 0) { -				slot = host->slots[i]; -				mmc_resume_host(slot->mmc); -			} -			return ret; -		} -	} -	host->suspended = 1; -	return 0; -} - -static int mmc_omap_resume(struct platform_device *pdev) -{ -	int i, ret = 0; -	struct mmc_omap_host *host = platform_get_drvdata(pdev); - -	if (host == NULL || !host->suspended) -		return 0; - -	for (i = 0; i < host->nr_slots; i++) { -		struct mmc_omap_slot *slot; -		slot = host->slots[i]; -		ret = mmc_resume_host(slot->mmc); -		if (ret < 0) -			return ret; - -		host->suspended = 0; -	} -	return 0; -} -#else -#define mmc_omap_suspend	NULL -#define mmc_omap_resume		NULL +#if IS_BUILTIN(CONFIG_OF) +static const struct of_device_id mmc_omap_match[] = { +	{ .compatible = "ti,omap2420-mmc", }, +	{ }, +};  #endif  static struct platform_driver mmc_omap_driver = {  	.probe		= mmc_omap_probe,  	.remove		= mmc_omap_remove, -	.suspend	= mmc_omap_suspend, -	.resume		= mmc_omap_resume,  	.driver		= {  		.name	= DRIVER_NAME,  		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(mmc_omap_match),  	},  }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 6ac63df645c..6b7b7558592 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -31,7 +31,7 @@  #include <linux/of.h>  #include <linux/of_gpio.h>  #include <linux/of_device.h> -#include <linux/omap-dma.h> +#include <linux/omap-dmaengine.h>  #include <linux/mmc/host.h>  #include <linux/mmc/core.h>  #include <linux/mmc/mmc.h> @@ -45,6 +45,7 @@  /* OMAP HSMMC Host Controller Registers */  #define OMAP_HSMMC_SYSSTATUS	0x0014  #define OMAP_HSMMC_CON		0x002C +#define OMAP_HSMMC_SDMASA	0x0100  #define OMAP_HSMMC_BLK		0x0104  #define OMAP_HSMMC_ARG		0x0108  #define OMAP_HSMMC_CMD		0x010C @@ -58,6 +59,7 @@  #define OMAP_HSMMC_STAT		0x0130  #define OMAP_HSMMC_IE		0x0134  #define OMAP_HSMMC_ISE		0x0138 +#define OMAP_HSMMC_AC12		0x013C  #define OMAP_HSMMC_CAPA		0x0140  #define VS18			(1 << 26) @@ -75,11 +77,13 @@  #define ICE			0x1  #define ICS			0x2  #define CEN			(1 << 2) +#define CLKD_MAX		0x3FF		/* max clock divisor: 1023 */  #define CLKD_MASK		0x0000FFC0  #define CLKD_SHIFT		6  #define DTO_MASK		0x000F0000  #define DTO_SHIFT		16  #define INIT_STREAM		(1 << 1) +#define ACEN_ACMD23		(2 << 2)  #define DP_SELECT		(1 << 21)  #define DDIR			(1 << 4)  #define DMAE			0x1 @@ -96,7 +100,6 @@  #define SRC			(1 << 25)  #define SRD			(1 << 26)  #define SOFTRESET		(1 << 1) -#define RESETDONE		(1 << 0)  /* Interrupt masks for IE and ISE register */  #define CC_EN			(1 << 0) @@ -111,19 +114,33 @@  #define DTO_EN			(1 << 20)  #define DCRC_EN			(1 << 21)  #define DEB_EN			(1 << 22) +#define ACE_EN			(1 << 24)  #define CERR_EN			(1 << 28)  #define BADA_EN			(1 << 29) -#define INT_EN_MASK		(BADA_EN | CERR_EN | DEB_EN | DCRC_EN |\ +#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\  		DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \  		BRR_EN | BWR_EN | TC_EN | CC_EN) +#define CNI	(1 << 7) +#define ACIE	(1 << 4) +#define ACEB	(1 << 3) +#define ACCE	(1 << 2) +#define ACTO	(1 << 1) +#define ACNE	(1 << 0) +  #define MMC_AUTOSUSPEND_DELAY	100 -#define MMC_TIMEOUT_MS		20 +#define MMC_TIMEOUT_MS		20		/* 20 mSec */ +#define MMC_TIMEOUT_US		20000		/* 20000 micro Sec */  #define OMAP_MMC_MIN_CLOCK	400000  #define OMAP_MMC_MAX_CLOCK	52000000  #define DRIVER_NAME		"omap_hsmmc" +#define VDD_1V8			1800000		/* 180000 uV */ +#define VDD_3V0			3000000		/* 300000 uV */ +#define VDD_165_195		(ffs(MMC_VDD_165_195) - 1) + +#define AUTO_CMD23		(1 << 1)	/* Auto CMD23 support */  /*   * One controller can have multiple slots, like on some omap boards using   * omap.c controller driver. Luckily this is not currently done on any known @@ -162,7 +179,8 @@ struct omap_hsmmc_host {  	 */  	struct	regulator	*vcc;  	struct	regulator	*vcc_aux; -	int			pbias_disable; +	struct	regulator	*pbias; +	bool			pbias_enabled;  	void	__iomem		*base;  	resource_size_t		mapbase;  	spinlock_t		irq_lock; /* Prevent races with irq handler */ @@ -171,6 +189,10 @@ struct omap_hsmmc_host {  	unsigned char		bus_mode;  	unsigned char		power_mode;  	int			suspended; +	u32			con; +	u32			hctl; +	u32			sysctl; +	u32			capa;  	int			irq;  	int			use_dma, dma_ch;  	struct dma_chan		*tx_chan; @@ -182,11 +204,19 @@ struct omap_hsmmc_host {  	int			reqs_blocked;  	int			use_reg;  	int			req_in_progress; +	unsigned long		clk_rate; +	unsigned int		flags;  	struct omap_hsmmc_next	next_data; -  	struct	omap_mmc_platform_data	*pdata;  }; +struct omap_mmc_of_data { +	u32 reg_offset; +	u8 controller_flags; +}; + +static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host); +  static int omap_hsmmc_card_detect(struct device *dev, int slot)  {  	struct omap_hsmmc_host *host = dev_get_drvdata(dev); @@ -256,17 +286,19 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,  	 */  	if (!host->vcc)  		return 0; -	/* -	 * With DT, never turn OFF the regulator for MMC1. This is because -	 * the pbias cell programming support is still missing when -	 * booting with Device tree -	 */ -	if (host->pbias_disable && !vdd) -		return 0;  	if (mmc_slot(host).before_set_reg)  		mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); +	if (host->pbias) { +		if (host->pbias_enabled == 1) { +			ret = regulator_disable(host->pbias); +			if (!ret) +				host->pbias_enabled = 0; +		} +		regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0); +	} +  	/*  	 * Assume Vcc regulator is used only to power the card ... OMAP  	 * VDDS is used to power the pins, optionally with a transceiver to @@ -281,11 +313,12 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,  	 * chips/cards need an interface voltage rail too.  	 */  	if (power_on) { -		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); +		if (host->vcc) +			ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);  		/* Enable interface voltage rail, if needed */  		if (ret == 0 && host->vcc_aux) {  			ret = regulator_enable(host->vcc_aux); -			if (ret < 0) +			if (ret < 0 && host->vcc)  				ret = mmc_regulator_set_ocr(host->mmc,  							host->vcc, 0);  		} @@ -293,16 +326,34 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,  		/* Shut down the rail */  		if (host->vcc_aux)  			ret = regulator_disable(host->vcc_aux); -		if (!ret) { +		if (host->vcc) {  			/* Then proceed to shut down the local regulator */  			ret = mmc_regulator_set_ocr(host->mmc,  						host->vcc, 0);  		}  	} +	if (host->pbias) { +		if (vdd <= VDD_165_195) +			ret = regulator_set_voltage(host->pbias, VDD_1V8, +								VDD_1V8); +		else +			ret = regulator_set_voltage(host->pbias, VDD_3V0, +								VDD_3V0); +		if (ret < 0) +			goto error_set_power; + +		if (host->pbias_enabled == 0) { +			ret = regulator_enable(host->pbias); +			if (!ret) +				host->pbias_enabled = 1; +		} +	} +  	if (mmc_slot(host).after_set_reg)  		mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); +error_set_power:  	return ret;  } @@ -311,12 +362,12 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)  	struct regulator *reg;  	int ocr_value = 0; -	reg = regulator_get(host->dev, "vmmc"); +	reg = devm_regulator_get(host->dev, "vmmc");  	if (IS_ERR(reg)) { -		dev_err(host->dev, "vmmc regulator missing\n"); +		dev_err(host->dev, "unable to get vmmc regulator %ld\n", +			PTR_ERR(reg));  		return PTR_ERR(reg);  	} else { -		mmc_slot(host).set_power = omap_hsmmc_set_power;  		host->vcc = reg;  		ocr_value = mmc_regulator_get_ocrmask(reg);  		if (!mmc_slot(host).ocr_mask) { @@ -329,31 +380,29 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)  				return -EINVAL;  			}  		} +	} +	mmc_slot(host).set_power = omap_hsmmc_set_power; -		/* Allow an aux regulator */ -		reg = regulator_get(host->dev, "vmmc_aux"); -		host->vcc_aux = IS_ERR(reg) ? NULL : reg; +	/* Allow an aux regulator */ +	reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); +	host->vcc_aux = IS_ERR(reg) ? NULL : reg; -		/* For eMMC do not power off when not in sleep state */ -		if (mmc_slot(host).no_regulator_off_init) -			return 0; -		/* -		* UGLY HACK:  workaround regulator framework bugs. -		* When the bootloader leaves a supply active, it's -		* initialized with zero usecount ... and we can't -		* disable it without first enabling it.  Until the -		* framework is fixed, we need a workaround like this -		* (which is safe for MMC, but not in general). -		*/ -		if (regulator_is_enabled(host->vcc) > 0 || -		    (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { -			int vdd = ffs(mmc_slot(host).ocr_mask) - 1; +	reg = devm_regulator_get_optional(host->dev, "pbias"); +	host->pbias = IS_ERR(reg) ? NULL : reg; -			mmc_slot(host).set_power(host->dev, host->slot_id, -						 1, vdd); -			mmc_slot(host).set_power(host->dev, host->slot_id, -						 0, 0); -		} +	/* For eMMC do not power off when not in sleep state */ +	if (mmc_slot(host).no_regulator_off_init) +		return 0; +	/* +	 * To disable boot_on regulator, enable regulator +	 * to increase usecount and then disable it. +	 */ +	if ((host->vcc && regulator_is_enabled(host->vcc) > 0) || +	    (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { +		int vdd = ffs(mmc_slot(host).ocr_mask) - 1; + +		mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); +		mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);  	}  	return 0; @@ -361,8 +410,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)  static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)  { -	regulator_put(host->vcc); -	regulator_put(host->vcc_aux);  	mmc_slot(host).set_power = NULL;  } @@ -493,8 +540,8 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)  	if (ios->clock) {  		dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock); -		if (dsor > 250) -			dsor = 250; +		if (dsor > CLKD_MAX) +			dsor = CLKD_MAX;  	}  	return dsor; @@ -535,7 +582,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)  	 *	- MMC/SD clock coming out of controller > 25MHz  	 */  	if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && -	    (ios->timing != MMC_TIMING_UHS_DDR50) && +	    (ios->timing != MMC_TIMING_MMC_DDR52) &&  	    ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) {  		regval = OMAP_HSMMC_READ(host->base, HCTL);  		if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) @@ -555,7 +602,7 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)  	u32 con;  	con = OMAP_HSMMC_READ(host->base, CON); -	if (ios->timing == MMC_TIMING_UHS_DDR50) +	if (ios->timing == MMC_TIMING_MMC_DDR52)  		con |= DDR;	/* configure in DDR mode */  	else  		con &= ~DDR; @@ -597,24 +644,16 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)  static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)  {  	struct mmc_ios *ios = &host->mmc->ios; -	struct omap_mmc_platform_data *pdata = host->pdata; -	int context_loss = 0;  	u32 hctl, capa;  	unsigned long timeout; -	if (pdata->get_context_loss_count) { -		context_loss = pdata->get_context_loss_count(host->dev); -		if (context_loss < 0) -			return 1; -	} - -	dev_dbg(mmc_dev(host->mmc), "context was %slost\n", -		context_loss == host->context_loss ? "not " : ""); -	if (host->context_loss == context_loss) -		return 1; +	if (host->con == OMAP_HSMMC_READ(host->base, CON) && +	    host->hctl == OMAP_HSMMC_READ(host->base, HCTL) && +	    host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) && +	    host->capa == OMAP_HSMMC_READ(host->base, CAPA)) +		return 0; -	if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) -		return 1; +	host->context_loss++;  	if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {  		if (host->power_mode != MMC_POWER_OFF && @@ -655,9 +694,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)  	omap_hsmmc_set_bus_mode(host);  out: -	host->context_loss = context_loss; - -	dev_dbg(mmc_dev(host->mmc), "context is restored\n"); +	dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n", +		host->context_loss);  	return 0;  } @@ -666,15 +704,10 @@ out:   */  static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)  { -	struct omap_mmc_platform_data *pdata = host->pdata; -	int context_loss; - -	if (pdata->get_context_loss_count) { -		context_loss = pdata->get_context_loss_count(host->dev); -		if (context_loss < 0) -			return; -		host->context_loss = context_loss; -	} +	host->con =  OMAP_HSMMC_READ(host->base, CON); +	host->hctl = OMAP_HSMMC_READ(host->base, HCTL); +	host->sysctl =  OMAP_HSMMC_READ(host->base, SYSCTL); +	host->capa = OMAP_HSMMC_READ(host->base, CAPA);  }  #else @@ -793,6 +826,11 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,  	cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); +	if ((host->flags & AUTO_CMD23) && mmc_op_multi(cmd->opcode) && +	    host->mrq->sbc) { +		cmdreg |= ACEN_ACMD23; +		OMAP_HSMMC_WRITE(host->base, SDMASA, host->mrq->sbc->arg); +	}  	if (data) {  		cmdreg |= DP_SELECT | MSBS | BCE;  		if (data->flags & MMC_DATA_READ) @@ -870,11 +908,10 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)  	else  		data->bytes_xfered = 0; -	if (!data->stop) { +	if (data->stop && (data->error || !host->mrq->sbc)) +		omap_hsmmc_start_command(host, data->stop, NULL); +	else  		omap_hsmmc_request_done(host, data->mrq); -		return; -	} -	omap_hsmmc_start_command(host, data->stop, NULL);  }  /* @@ -883,6 +920,15 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)  static void  omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)  { +	if (host->mrq->sbc && (host->cmd == host->mrq->sbc) && +	    !host->mrq->sbc->error && !(host->flags & AUTO_CMD23)) { +		host->cmd = NULL; +		omap_hsmmc_start_dma_transfer(host); +		omap_hsmmc_start_command(host, host->mrq->cmd, +						host->mrq->data); +		return; +	} +  	host->cmd = NULL;  	if (cmd->flags & MMC_RSP_PRESENT) { @@ -898,7 +944,7 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)  		}  	}  	if ((host->data == NULL && !host->response_busy) || cmd->error) -		omap_hsmmc_request_done(host, cmd->mrq); +		omap_hsmmc_request_done(host, host->mrq);  }  /* @@ -975,8 +1021,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,  						   unsigned long bit)  {  	unsigned long i = 0; -	unsigned long limit = (loops_per_jiffy * -				msecs_to_jiffies(MMC_TIMEOUT_MS)); +	unsigned long limit = MMC_TIMEOUT_US;  	OMAP_HSMMC_WRITE(host->base, SYSCTL,  			 OMAP_HSMMC_READ(host->base, SYSCTL) | bit); @@ -988,13 +1033,13 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,  	if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {  		while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))  					&& (i++ < limit)) -			cpu_relax(); +			udelay(1);  	}  	i = 0;  	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&  		(i++ < limit)) -		cpu_relax(); +		udelay(1);  	if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit)  		dev_err(mmc_dev(host->mmc), @@ -1022,6 +1067,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)  {  	struct mmc_data *data;  	int end_cmd = 0, end_trans = 0; +	int error = 0;  	data = host->data;  	dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); @@ -1036,6 +1082,20 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)  		else if (status & (CCRC_EN | DCRC_EN))  			hsmmc_command_incomplete(host, -EILSEQ, end_cmd); +		if (status & ACE_EN) { +			u32 ac12; +			ac12 = OMAP_HSMMC_READ(host->base, AC12); +			if (!(ac12 & ACNE) && host->mrq->sbc) { +				end_cmd = 1; +				if (ac12 & ACTO) +					error =  -ETIMEDOUT; +				else if (ac12 & (ACCE | ACEB | ACIE)) +					error = -EILSEQ; +				host->mrq->sbc->error = error; +				hsmmc_command_incomplete(host, error, end_cmd); +			} +			dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12); +		}  		if (host->data || host->response_busy) {  			end_trans = !end_cmd;  			host->response_busy = 0; @@ -1178,9 +1238,6 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)  	struct omap_mmc_slot_data *slot = &mmc_slot(host);  	int carddetect; -	if (host->suspended) -		return IRQ_HANDLED; -  	sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");  	if (slot->card_detect) @@ -1246,8 +1303,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,  	}  	/* Check if next job is already prepared */ -	if (next || -	    (!next && data->host_cookie != host->next_data.cookie)) { +	if (next || data->host_cookie != host->next_data.cookie) {  		dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,  				     omap_hsmmc_get_dma_dir(host, data)); @@ -1272,7 +1328,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,  /*   * Routine to configure and start DMA for the MMC card   */ -static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, +static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host,  					struct mmc_request *req)  {  	struct dma_slave_config cfg; @@ -1331,8 +1387,6 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,  	host->dma_ch = 1; -	dma_async_issue_pending(chan); -  	return 0;  } @@ -1348,7 +1402,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host,  	if (clkd == 0)  		clkd = 1; -	cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd); +	cycle_ns = 1000000000 / (host->clk_rate / clkd);  	timeout = timeout_ns / cycle_ns;  	timeout += timeout_clks;  	if (timeout) { @@ -1373,6 +1427,21 @@ static void set_data_timeout(struct omap_hsmmc_host *host,  	OMAP_HSMMC_WRITE(host->base, SYSCTL, reg);  } +static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host) +{ +	struct mmc_request *req = host->mrq; +	struct dma_chan *chan; + +	if (!req->data) +		return; +	OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) +				| (req->data->blocks << 16)); +	set_data_timeout(host, req->data->timeout_ns, +				req->data->timeout_clks); +	chan = omap_hsmmc_get_dma_chan(host, req->data); +	dma_async_issue_pending(chan); +} +  /*   * Configure block length for MMC/SD cards and initiate the transfer.   */ @@ -1393,12 +1462,8 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)  		return 0;  	} -	OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) -					| (req->data->blocks << 16)); -	set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks); -  	if (host->use_dma) { -		ret = omap_hsmmc_start_dma_transfer(host, req); +		ret = omap_hsmmc_setup_dma_transfer(host, req);  		if (ret != 0) {  			dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");  			return ret; @@ -1472,6 +1537,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)  		host->reqs_blocked = 0;  	WARN_ON(host->mrq != NULL);  	host->mrq = req; +	host->clk_rate = clk_get_rate(host->fclk);  	err = omap_hsmmc_prepare_data(host, req);  	if (err) {  		req->cmd->error = err; @@ -1481,7 +1547,12 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)  		mmc_request_done(mmc, req);  		return;  	} +	if (req->sbc && !(host->flags & AUTO_CMD23)) { +		omap_hsmmc_start_command(host, req->sbc, NULL); +		return; +	} +	omap_hsmmc_start_dma_transfer(host);  	omap_hsmmc_start_command(host, req->cmd, req->data);  } @@ -1519,13 +1590,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		 * of external transceiver; but they all handle 1.8V.  		 */  		if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && -			(ios->vdd == DUAL_VOLT_OCR_BIT) && -			/* -			 * With pbias cell programming missing, this -			 * can't be allowed on MMC1 when booting with device -			 * tree. -			 */ -			!host->pbias_disable) { +			(ios->vdd == DUAL_VOLT_OCR_BIT)) {  				/*  				 * The mmc_select_voltage fn of the core does  				 * not seem to set the power_mode to @@ -1635,18 +1700,9 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)  {  	struct mmc_host *mmc = s->private;  	struct omap_hsmmc_host *host = mmc_priv(mmc); -	int context_loss = 0; - -	if (host->pdata->get_context_loss_count) -		context_loss = host->pdata->get_context_loss_count(host->dev); -	seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n", -			mmc->index, host->context_loss, context_loss); - -	if (host->suspended) { -		seq_printf(s, "host suspended, can't read registers\n"); -		return 0; -	} +	seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n", +			mmc->index, host->context_loss);  	pm_runtime_get_sync(host->dev); @@ -1697,18 +1753,29 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)  #endif  #ifdef CONFIG_OF -static u16 omap4_reg_offset = 0x100; +static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = { +	/* See 35xx errata 2.1.1.128 in SPRZ278F */ +	.controller_flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ, +}; + +static const struct omap_mmc_of_data omap4_mmc_of_data = { +	.reg_offset = 0x100, +};  static const struct of_device_id omap_mmc_of_match[] = {  	{  		.compatible = "ti,omap2-hsmmc",  	},  	{ +		.compatible = "ti,omap3-pre-es3-hsmmc", +		.data = &omap3_pre_es3_mmc_of_data, +	}, +	{  		.compatible = "ti,omap3-hsmmc",  	},  	{  		.compatible = "ti,omap4-hsmmc", -		.data = &omap4_reg_offset, +		.data = &omap4_mmc_of_data,  	},  	{},  }; @@ -1728,7 +1795,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);  	if (!pdata) -		return NULL; /* out of memory */ +		return ERR_PTR(-ENOMEM); /* out of memory */  	if (of_find_property(np, "ti,dual-volt", NULL))  		pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; @@ -1757,13 +1824,19 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)  	if (of_find_property(np, "ti,needs-special-hs-handling", NULL))  		pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT; +	if (of_find_property(np, "keep-power-in-suspend", NULL)) +		pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER; + +	if (of_find_property(np, "enable-sdio-wakeup", NULL)) +		pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ; +  	return pdata;  }  #else  static inline struct omap_mmc_platform_data  			*of_get_hsmmc_pdata(struct device *dev)  { -	return NULL; +	return ERR_PTR(-EINVAL);  }  #endif @@ -1778,6 +1851,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  	dma_cap_mask_t mask;  	unsigned tx_req, rx_req;  	struct pinctrl *pinctrl; +	const struct omap_mmc_of_data *data; +	void __iomem *base;  	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);  	if (match) { @@ -1787,8 +1862,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  			return PTR_ERR(pdata);  		if (match->data) { -			const u16 *offsetp = match->data; -			pdata->reg_offset = *offsetp; +			data = match->data; +			pdata->reg_offset = data->reg_offset; +			pdata->controller_flags |= data->controller_flags;  		}  	} @@ -1807,9 +1883,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  	if (res == NULL || irq < 0)  		return -ENXIO; -	res = request_mem_region(res->start, resource_size(res), pdev->name); -	if (res == NULL) -		return -EBUSY; +	base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(base)) +		return PTR_ERR(base);  	ret = omap_hsmmc_gpio_init(pdata);  	if (ret) @@ -1830,21 +1906,15 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  	host->irq	= irq;  	host->slot_id	= 0;  	host->mapbase	= res->start + pdata->reg_offset; -	host->base	= ioremap(host->mapbase, SZ_4K); +	host->base	= base + pdata->reg_offset;  	host->power_mode = MMC_POWER_OFF;  	host->next_data.cookie = 1; +	host->pbias_enabled = 0;  	platform_set_drvdata(pdev, host);  	mmc->ops	= &omap_hsmmc_ops; -	/* -	 * If regulator_disable can only put vcc_aux to sleep then there is -	 * no off state. -	 */ -	if (mmc_slot(host).vcc_aux_disable_is_sleep) -		mmc_slot(host).no_off = 1; -  	mmc->f_min = OMAP_MMC_MIN_CLOCK;  	if (pdata->max_freq > 0) @@ -1854,7 +1924,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  	spin_lock_init(&host->irq_lock); -	host->fclk = clk_get(&pdev->dev, "fck"); +	host->fclk = devm_clk_get(&pdev->dev, "fck");  	if (IS_ERR(host->fclk)) {  		ret = PTR_ERR(host->fclk);  		host->fclk = NULL; @@ -1873,11 +1943,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  	omap_hsmmc_context_save(host); -	/* This can be removed once we support PBIAS with DT */ -	if (host->dev->of_node && host->mapbase == 0x4809c000) -		host->pbias_disable = 1; - -	host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); +	host->dbclk = devm_clk_get(&pdev->dev, "mmchsdb_fck");  	/*  	 * MMC can still work without debounce clock.  	 */ @@ -1885,7 +1951,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  		host->dbclk = NULL;  	} else if (clk_prepare_enable(host->dbclk) != 0) {  		dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n"); -		clk_put(host->dbclk);  		host->dbclk = NULL;  	} @@ -1954,7 +2019,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  	}  	/* Request IRQ for MMC operations */ -	ret = request_irq(host->irq, omap_hsmmc_irq, 0, +	ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,  			mmc_hostname(mmc), host);  	if (ret) {  		dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); @@ -1965,7 +2030,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  		if (pdata->init(&pdev->dev) != 0) {  			dev_err(mmc_dev(host->mmc),  				"Unable to configure MMC IRQs\n"); -			goto err_irq_cd_init; +			goto err_irq;  		}  	} @@ -1980,9 +2045,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  	/* Request IRQ for card detect */  	if ((mmc_slot(host).card_detect_irq)) { -		ret = request_threaded_irq(mmc_slot(host).card_detect_irq, -					   NULL, -					   omap_hsmmc_detect, +		ret = devm_request_threaded_irq(&pdev->dev, +						mmc_slot(host).card_detect_irq, +						NULL, omap_hsmmc_detect,  					   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,  					   mmc_hostname(mmc), host);  		if (ret) { @@ -2025,15 +2090,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev)  err_slot_name:  	mmc_remove_host(mmc); -	free_irq(mmc_slot(host).card_detect_irq, host);  err_irq_cd:  	if (host->use_reg)  		omap_hsmmc_reg_put(host);  err_reg:  	if (host->pdata->cleanup)  		host->pdata->cleanup(&pdev->dev); -err_irq_cd_init: -	free_irq(host->irq, host);  err_irq:  	if (host->tx_chan)  		dma_release_channel(host->tx_chan); @@ -2041,27 +2103,19 @@ err_irq:  		dma_release_channel(host->rx_chan);  	pm_runtime_put_sync(host->dev);  	pm_runtime_disable(host->dev); -	clk_put(host->fclk); -	if (host->dbclk) { +	if (host->dbclk)  		clk_disable_unprepare(host->dbclk); -		clk_put(host->dbclk); -	}  err1: -	iounmap(host->base);  	mmc_free_host(mmc);  err_alloc:  	omap_hsmmc_gpio_free(pdata);  err: -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (res) -		release_mem_region(res->start, resource_size(res));  	return ret;  }  static int omap_hsmmc_remove(struct platform_device *pdev)  {  	struct omap_hsmmc_host *host = platform_get_drvdata(pdev); -	struct resource *res;  	pm_runtime_get_sync(host->dev);  	mmc_remove_host(host->mmc); @@ -2069,9 +2123,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)  		omap_hsmmc_reg_put(host);  	if (host->pdata->cleanup)  		host->pdata->cleanup(&pdev->dev); -	free_irq(host->irq, host); -	if (mmc_slot(host).card_detect_irq) -		free_irq(mmc_slot(host).card_detect_irq, host);  	if (host->tx_chan)  		dma_release_channel(host->tx_chan); @@ -2080,20 +2131,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev)  	pm_runtime_put_sync(host->dev);  	pm_runtime_disable(host->dev); -	clk_put(host->fclk); -	if (host->dbclk) { +	if (host->dbclk)  		clk_disable_unprepare(host->dbclk); -		clk_put(host->dbclk); -	}  	omap_hsmmc_gpio_free(host->pdata); -	iounmap(host->base);  	mmc_free_host(host->mmc); -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (res) -		release_mem_region(res->start, resource_size(res)); -  	return 0;  } @@ -2119,23 +2162,12 @@ static void omap_hsmmc_complete(struct device *dev)  static int omap_hsmmc_suspend(struct device *dev)  { -	int ret = 0;  	struct omap_hsmmc_host *host = dev_get_drvdata(dev);  	if (!host)  		return 0; -	if (host && host->suspended) -		return 0; -  	pm_runtime_get_sync(host->dev); -	host->suspended = 1; -	ret = mmc_suspend_host(host->mmc); - -	if (ret) { -		host->suspended = 0; -		goto err; -	}  	if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {  		omap_hsmmc_disable_irq(host); @@ -2145,23 +2177,19 @@ static int omap_hsmmc_suspend(struct device *dev)  	if (host->dbclk)  		clk_disable_unprepare(host->dbclk); -err: +  	pm_runtime_put_sync(host->dev); -	return ret; +	return 0;  }  /* Routine to resume the MMC device */  static int omap_hsmmc_resume(struct device *dev)  { -	int ret = 0;  	struct omap_hsmmc_host *host = dev_get_drvdata(dev);  	if (!host)  		return 0; -	if (host && !host->suspended) -		return 0; -  	pm_runtime_get_sync(host->dev);  	if (host->dbclk) @@ -2172,16 +2200,9 @@ static int omap_hsmmc_resume(struct device *dev)  	omap_hsmmc_protect_card(host); -	/* Notify the core to resume the host */ -	ret = mmc_resume_host(host->mmc); -	if (ret == 0) -		host->suspended = 0; -  	pm_runtime_mark_last_busy(host->dev);  	pm_runtime_put_autosuspend(host->dev); - -	return ret; - +	return 0;  }  #else diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1956a3df7cf..32fe11323f3 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -880,35 +880,6 @@ static int pxamci_remove(struct platform_device *pdev)  	return 0;  } -#ifdef CONFIG_PM -static int pxamci_suspend(struct device *dev) -{ -	struct mmc_host *mmc = dev_get_drvdata(dev); -	int ret = 0; - -	if (mmc) -		ret = mmc_suspend_host(mmc); - -	return ret; -} - -static int pxamci_resume(struct device *dev) -{ -	struct mmc_host *mmc = dev_get_drvdata(dev); -	int ret = 0; - -	if (mmc) -		ret = mmc_resume_host(mmc); - -	return ret; -} - -static const struct dev_pm_ops pxamci_pm_ops = { -	.suspend	= pxamci_suspend, -	.resume		= pxamci_resume, -}; -#endif -  static struct platform_driver pxamci_driver = {  	.probe		= pxamci_probe,  	.remove		= pxamci_remove, @@ -916,9 +887,6 @@ static struct platform_driver pxamci_driver = {  		.name	= DRIVER_NAME,  		.owner	= THIS_MODULE,  		.of_match_table = of_match_ptr(pxa_mmc_dt_ids), -#ifdef CONFIG_PM -		.pm	= &pxamci_pm_ops, -#endif  	},  }; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 375a880e0c5..0d519649b57 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -31,16 +31,6 @@  #include <linux/mfd/rtsx_pci.h>  #include <asm/unaligned.h> -/* SD Tuning Data Structure - * Record continuous timing phase path - */ -struct timing_phase_path { -	int start; -	int end; -	int mid; -	int len; -}; -  struct realtek_pci_sdmmc {  	struct platform_device	*pdev;  	struct rtsx_pcr		*pcr; @@ -246,6 +236,9 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,  	case MMC_RSP_R1:  		rsp_type = SD_RSP_TYPE_R1;  		break; +	case MMC_RSP_R1 & ~MMC_RSP_CRC: +		rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; +		break;  	case MMC_RSP_R1B:  		rsp_type = SD_RSP_TYPE_R1b;  		break; @@ -364,7 +357,7 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)  	struct mmc_host *mmc = host->mmc;  	struct mmc_card *card = mmc->card;  	struct mmc_data *data = mrq->data; -	int uhs = mmc_sd_card_uhs(card); +	int uhs = mmc_card_uhs(card);  	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;  	u8 cfg2, trans_mode;  	int err; @@ -511,85 +504,47 @@ static int sd_change_phase(struct realtek_pci_sdmmc *host,  	return 0;  } -static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map) +static inline u32 test_phase_bit(u32 phase_map, unsigned int bit)  { -	struct timing_phase_path path[MAX_PHASE + 1]; -	int i, j, cont_path_cnt; -	int new_block, max_len, final_path_idx; -	u8 final_phase = 0xFF; +	bit %= RTSX_PHASE_MAX; +	return phase_map & (1 << bit); +} -	/* Parse phase_map, take it as a bit-ring */ -	cont_path_cnt = 0; -	new_block = 1; -	j = 0; -	for (i = 0; i < MAX_PHASE + 1; i++) { -		if (phase_map & (1 << i)) { -			if (new_block) { -				new_block = 0; -				j = cont_path_cnt++; -				path[j].start = i; -				path[j].end = i; -			} else { -				path[j].end = i; -			} -		} else { -			new_block = 1; -			if (cont_path_cnt) { -				/* Calculate path length and middle point */ -				int idx = cont_path_cnt - 1; -				path[idx].len = -					path[idx].end - path[idx].start + 1; -				path[idx].mid = -					path[idx].start + path[idx].len / 2; -			} -		} -	} +static int sd_get_phase_len(u32 phase_map, unsigned int start_bit) +{ +	int i; -	if (cont_path_cnt == 0) { -		dev_dbg(sdmmc_dev(host), "No continuous phase path\n"); -		goto finish; -	} else { -		/* Calculate last continuous path length and middle point */ -		int idx = cont_path_cnt - 1; -		path[idx].len = path[idx].end - path[idx].start + 1; -		path[idx].mid = path[idx].start + path[idx].len / 2; +	for (i = 0; i < RTSX_PHASE_MAX; i++) { +		if (test_phase_bit(phase_map, start_bit + i) == 0) +			return i;  	} +	return RTSX_PHASE_MAX; +} -	/* Connect the first and last continuous paths if they are adjacent */ -	if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) { -		/* Using negative index */ -		path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1; -		path[0].len += path[cont_path_cnt - 1].len; -		path[0].mid = path[0].start + path[0].len / 2; -		/* Convert negative middle point index to positive one */ -		if (path[0].mid < 0) -			path[0].mid += MAX_PHASE + 1; -		cont_path_cnt--; +static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map) +{ +	int start = 0, len = 0; +	int start_final = 0, len_final = 0; +	u8 final_phase = 0xFF; + +	if (phase_map == 0) { +		dev_err(sdmmc_dev(host), "phase error: [map:%x]\n", phase_map); +		return final_phase;  	} -	/* Choose the longest continuous phase path */ -	max_len = 0; -	final_phase = 0; -	final_path_idx = 0; -	for (i = 0; i < cont_path_cnt; i++) { -		if (path[i].len > max_len) { -			max_len = path[i].len; -			final_phase = (u8)path[i].mid; -			final_path_idx = i; +	while (start < RTSX_PHASE_MAX) { +		len = sd_get_phase_len(phase_map, start); +		if (len_final < len) { +			start_final = start; +			len_final = len;  		} - -		dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n", -				i, path[i].start); -		dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n", -				i, path[i].end); -		dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n", -				i, path[i].len); -		dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n", -				i, path[i].mid); +		start += len ? len : 1;  	} -finish: -	dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase); +	final_phase = (start_final + len_final / 2) % RTSX_PHASE_MAX; +	dev_dbg(sdmmc_dev(host), "phase: [map:%x] [maxlen:%d] [final:%d]\n", +		phase_map, len_final, final_phase); +  	return final_phase;  } @@ -635,7 +590,7 @@ static int sd_tuning_phase(struct realtek_pci_sdmmc *host,  	int err, i;  	u32 raw_phase_map = 0; -	for (i = MAX_PHASE; i >= 0; i--) { +	for (i = 0; i < RTSX_PHASE_MAX; i++) {  		err = sd_tuning_rx_cmd(host, opcode, (u8)i);  		if (err == 0)  			raw_phase_map |= 1 << i; @@ -864,6 +819,7 @@ static int sd_set_timing(struct realtek_pci_sdmmc *host, unsigned char timing)  		rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);  		break; +	case MMC_TIMING_MMC_DDR52:  	case MMC_TIMING_UHS_DDR50:  		rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,  				0x0C | SD_ASYNC_FIFO_NOT_RST, @@ -944,6 +900,7 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		host->vpclk = true;  		host->double_clk = false;  		break; +	case MMC_TIMING_MMC_DDR52:  	case MMC_TIMING_UHS_DDR50:  	case MMC_TIMING_UHS_SDR25:  		host->ssc_depth = RTSX_SSC_DEPTH_1M; @@ -1197,37 +1154,6 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = {  	.execute_tuning = sdmmc_execute_tuning,  }; -#ifdef CONFIG_PM -static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev, -		pm_message_t state) -{ -	struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); -	struct mmc_host *mmc = host->mmc; -	int err; - -	dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); - -	err = mmc_suspend_host(mmc); -	if (err) -		return err; - -	return 0; -} - -static int rtsx_pci_sdmmc_resume(struct platform_device *pdev) -{ -	struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); -	struct mmc_host *mmc = host->mmc; - -	dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); - -	return mmc_resume_host(mmc); -} -#else /* CONFIG_PM */ -#define rtsx_pci_sdmmc_suspend NULL -#define rtsx_pci_sdmmc_resume NULL -#endif /* CONFIG_PM */ -  static void init_extra_caps(struct realtek_pci_sdmmc *host)  {  	struct mmc_host *mmc = host->mmc; @@ -1328,7 +1254,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)  	pcr->slots[RTSX_SD_CARD].p_dev = NULL;  	pcr->slots[RTSX_SD_CARD].card_event = NULL;  	mmc = host->mmc; -	host->eject = true;  	mutex_lock(&host->host_mutex);  	if (host->mrq) { @@ -1346,6 +1271,8 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)  	mutex_unlock(&host->host_mutex);  	mmc_remove_host(mmc); +	host->eject = true; +  	mmc_free_host(mmc);  	dev_dbg(&(pdev->dev), @@ -1367,8 +1294,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = {  	.probe		= rtsx_pci_sdmmc_drv_probe,  	.remove		= rtsx_pci_sdmmc_drv_remove,  	.id_table       = rtsx_pci_sdmmc_ids, -	.suspend	= rtsx_pci_sdmmc_suspend, -	.resume		= rtsx_pci_sdmmc_resume,  	.driver		= {  		.owner	= THIS_MODULE,  		.name	= DRV_NAME_RTSX_PCI_SDMMC, diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c new file mode 100644 index 00000000000..5d3766e792f --- /dev/null +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -0,0 +1,1456 @@ +/* Realtek USB SD/MMC Card Interface driver + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. 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. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Author: + *   Roger Tseng <rogerable@realtek.com> + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/usb.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/card.h> +#include <linux/scatterlist.h> +#include <linux/pm_runtime.h> + +#include <linux/mfd/rtsx_usb.h> +#include <asm/unaligned.h> + +#if defined(CONFIG_LEDS_CLASS) || (defined(CONFIG_LEDS_CLASS_MODULE) && \ +		defined(CONFIG_MMC_REALTEK_USB_MODULE)) +#include <linux/leds.h> +#include <linux/workqueue.h> +#define RTSX_USB_USE_LEDS_CLASS +#endif + +struct rtsx_usb_sdmmc { +	struct platform_device	*pdev; +	struct rtsx_ucr	*ucr; +	struct mmc_host		*mmc; +	struct mmc_request	*mrq; + +	struct mutex		host_mutex; + +	u8			ssc_depth; +	unsigned int		clock; +	bool			vpclk; +	bool			double_clk; +	bool			host_removal; +	bool			card_exist; +	bool			initial_mode; +	bool			ddr_mode; + +	unsigned char		power_mode; + +#ifdef RTSX_USB_USE_LEDS_CLASS +	struct led_classdev	led; +	char			led_name[32]; +	struct work_struct	led_work; +#endif +}; + +static inline struct device *sdmmc_dev(struct rtsx_usb_sdmmc *host) +{ +	return &(host->pdev->dev); +} + +static inline void sd_clear_error(struct rtsx_usb_sdmmc *host) +{ +	struct rtsx_ucr *ucr = host->ucr; +	rtsx_usb_ep0_write_register(ucr, CARD_STOP, +				  SD_STOP | SD_CLR_ERR, +				  SD_STOP | SD_CLR_ERR); + +	rtsx_usb_clear_dma_err(ucr); +	rtsx_usb_clear_fsm_err(ucr); +} + +#ifdef DEBUG +static void sd_print_debug_regs(struct rtsx_usb_sdmmc *host) +{ +	struct rtsx_ucr *ucr = host->ucr; +	u8 val = 0; + +	rtsx_usb_ep0_read_register(ucr, SD_STAT1, &val); +	dev_dbg(sdmmc_dev(host), "SD_STAT1: 0x%x\n", val); +	rtsx_usb_ep0_read_register(ucr, SD_STAT2, &val); +	dev_dbg(sdmmc_dev(host), "SD_STAT2: 0x%x\n", val); +	rtsx_usb_ep0_read_register(ucr, SD_BUS_STAT, &val); +	dev_dbg(sdmmc_dev(host), "SD_BUS_STAT: 0x%x\n", val); +} +#else +#define sd_print_debug_regs(host) +#endif /* DEBUG */ + +static int sd_read_data(struct rtsx_usb_sdmmc *host, struct mmc_command *cmd, +	       u16 byte_cnt, u8 *buf, int buf_len, int timeout) +{ +	struct rtsx_ucr *ucr = host->ucr; +	int err; +	u8 trans_mode; + +	if (!buf) +		buf_len = 0; + +	rtsx_usb_init_cmd(ucr); +	if (cmd != NULL) { +		dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__ +				, cmd->opcode); +		if (cmd->opcode == MMC_SEND_TUNING_BLOCK) +			trans_mode = SD_TM_AUTO_TUNING; +		else +			trans_mode = SD_TM_NORMAL_READ; + +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD0, 0xFF, (u8)(cmd->opcode) | 0x40); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD1, 0xFF, (u8)(cmd->arg >> 24)); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD2, 0xFF, (u8)(cmd->arg >> 16)); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD3, 0xFF, (u8)(cmd->arg >> 8)); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD4, 0xFF, (u8)cmd->arg); +	} else { +		trans_mode = SD_TM_AUTO_READ_3; +	} + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_H, +			0xFF, (u8)(byte_cnt >> 8)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF, +			SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | +			SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); +	if (trans_mode != SD_TM_AUTO_TUNING) +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER, +			0xFF, trans_mode | SD_TRANSFER_START); +	rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER, +			SD_TRANSFER_END, SD_TRANSFER_END); + +	if (cmd != NULL) { +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD1, 0, 0); +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD2, 0, 0); +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD3, 0, 0); +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD4, 0, 0); +	} + +	err = rtsx_usb_send_cmd(ucr, MODE_CR, timeout); +	if (err) { +		dev_dbg(sdmmc_dev(host), +			"rtsx_usb_send_cmd failed (err = %d)\n", err); +		return err; +	} + +	err = rtsx_usb_get_rsp(ucr, !cmd ? 1 : 5, timeout); +	if (err || (ucr->rsp_buf[0] & SD_TRANSFER_ERR)) { +		sd_print_debug_regs(host); + +		if (!err) { +			dev_dbg(sdmmc_dev(host), +				"Transfer failed (SD_TRANSFER = %02x)\n", +				ucr->rsp_buf[0]); +			err = -EIO; +		} else { +			dev_dbg(sdmmc_dev(host), +				"rtsx_usb_get_rsp failed (err = %d)\n", err); +		} + +		return err; +	} + +	if (cmd != NULL) { +		cmd->resp[0] = get_unaligned_be32(ucr->rsp_buf + 1); +		dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n", +				cmd->resp[0]); +	} + +	if (buf && buf_len) { +		/* 2-byte aligned part */ +		err = rtsx_usb_read_ppbuf(ucr, buf, byte_cnt - (byte_cnt % 2)); +		if (err) { +			dev_dbg(sdmmc_dev(host), +				"rtsx_usb_read_ppbuf failed (err = %d)\n", err); +			return err; +		} + +		/* unaligned byte */ +		if (byte_cnt % 2) +			return rtsx_usb_read_register(ucr, +					PPBUF_BASE2 + byte_cnt, +					buf + byte_cnt - 1); +	} + +	return 0; +} + +static int sd_write_data(struct rtsx_usb_sdmmc *host, struct mmc_command *cmd, +		u16 byte_cnt, u8 *buf, int buf_len, int timeout) +{ +	struct rtsx_ucr *ucr = host->ucr; +	int err; +	u8 trans_mode; + +	if (!buf) +		buf_len = 0; + +	if (buf && buf_len) { +		err = rtsx_usb_write_ppbuf(ucr, buf, buf_len); +		if (err) { +			dev_dbg(sdmmc_dev(host), +				"rtsx_usb_write_ppbuf failed (err = %d)\n", +				err); +			return err; +		} +	} + +	trans_mode = (cmd != NULL) ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3; +	rtsx_usb_init_cmd(ucr); + +	if (cmd != NULL) { +		dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, +				cmd->opcode); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD0, 0xFF, (u8)(cmd->opcode) | 0x40); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD1, 0xFF, (u8)(cmd->arg >> 24)); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD2, 0xFF, (u8)(cmd->arg >> 16)); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD3, 0xFF, (u8)(cmd->arg >> 8)); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CMD4, 0xFF, (u8)cmd->arg); +	} + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_H, +			0xFF, (u8)(byte_cnt >> 8)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF, +		SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | +		SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +			CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, +			trans_mode | SD_TRANSFER_START); +	rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER, +			SD_TRANSFER_END, SD_TRANSFER_END); + +	if (cmd != NULL) { +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD1, 0, 0); +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD2, 0, 0); +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD3, 0, 0); +		rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD4, 0, 0); +	} + +	err = rtsx_usb_send_cmd(ucr, MODE_CR, timeout); +	if (err) { +		dev_dbg(sdmmc_dev(host), +			"rtsx_usb_send_cmd failed (err = %d)\n", err); +		return err; +	} + +	err = rtsx_usb_get_rsp(ucr, !cmd ? 1 : 5, timeout); +	if (err) { +		sd_print_debug_regs(host); +		dev_dbg(sdmmc_dev(host), +			"rtsx_usb_get_rsp failed (err = %d)\n", err); +		return err; +	} + +	if (cmd != NULL) { +		cmd->resp[0] = get_unaligned_be32(ucr->rsp_buf + 1); +		dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n", +				cmd->resp[0]); +	} + +	return 0; +} + +static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host, +		struct mmc_command *cmd) +{ +	struct rtsx_ucr *ucr = host->ucr; +	u8 cmd_idx = (u8)cmd->opcode; +	u32 arg = cmd->arg; +	int err = 0; +	int timeout = 100; +	int i; +	u8 *ptr; +	int stat_idx = 0; +	int len = 2; +	u8 rsp_type; + +	dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", +			__func__, cmd_idx, arg); + +	/* Response type: +	 * R0 +	 * R1, R5, R6, R7 +	 * R1b +	 * R2 +	 * R3, R4 +	 */ +	switch (mmc_resp_type(cmd)) { +	case MMC_RSP_NONE: +		rsp_type = SD_RSP_TYPE_R0; +		break; +	case MMC_RSP_R1: +		rsp_type = SD_RSP_TYPE_R1; +		break; +	case MMC_RSP_R1 & ~MMC_RSP_CRC: +		rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; +		break; +	case MMC_RSP_R1B: +		rsp_type = SD_RSP_TYPE_R1b; +		break; +	case MMC_RSP_R2: +		rsp_type = SD_RSP_TYPE_R2; +		break; +	case MMC_RSP_R3: +		rsp_type = SD_RSP_TYPE_R3; +		break; +	default: +		dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n"); +		err = -EINVAL; +		goto out; +	} + +	if (rsp_type == SD_RSP_TYPE_R1b) +		timeout = 3000; + +	if (cmd->opcode == SD_SWITCH_VOLTAGE) { +		err = rtsx_usb_write_register(ucr, SD_BUS_STAT, +				SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, +				SD_CLK_TOGGLE_EN); +		if (err) +			goto out; +	} + +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, +			0x01, PINGPONG_BUFFER); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER, +			0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START); +	rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER, +		     SD_TRANSFER_END | SD_STAT_IDLE, +		     SD_TRANSFER_END | SD_STAT_IDLE); + +	if (rsp_type == SD_RSP_TYPE_R2) { +		/* Read data from ping-pong buffer */ +		for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++) +			rtsx_usb_add_cmd(ucr, READ_REG_CMD, (u16)i, 0, 0); +		stat_idx = 16; +	} else if (rsp_type != SD_RSP_TYPE_R0) { +		/* Read data from SD_CMDx registers */ +		for (i = SD_CMD0; i <= SD_CMD4; i++) +			rtsx_usb_add_cmd(ucr, READ_REG_CMD, (u16)i, 0, 0); +		stat_idx = 5; +	} +	len += stat_idx; + +	rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_STAT1, 0, 0); + +	err = rtsx_usb_send_cmd(ucr, MODE_CR, 100); +	if (err) { +		dev_dbg(sdmmc_dev(host), +			"rtsx_usb_send_cmd error (err = %d)\n", err); +		goto out; +	} + +	err = rtsx_usb_get_rsp(ucr, len, timeout); +	if (err || (ucr->rsp_buf[0] & SD_TRANSFER_ERR)) { +		sd_print_debug_regs(host); +		sd_clear_error(host); + +		if (!err) { +			dev_dbg(sdmmc_dev(host), +				"Transfer failed (SD_TRANSFER = %02x)\n", +					ucr->rsp_buf[0]); +			err = -EIO; +		} else { +			dev_dbg(sdmmc_dev(host), +				"rtsx_usb_get_rsp failed (err = %d)\n", err); +		} + +		goto out; +	} + +	if (rsp_type == SD_RSP_TYPE_R0) { +		err = 0; +		goto out; +	} + +	/* Skip result of CHECK_REG_CMD */ +	ptr = ucr->rsp_buf + 1; + +	/* Check (Start,Transmission) bit of Response */ +	if ((ptr[0] & 0xC0) != 0) { +		err = -EILSEQ; +		dev_dbg(sdmmc_dev(host), "Invalid response bit\n"); +		goto out; +	} + +	/* Check CRC7 */ +	if (!(rsp_type & SD_NO_CHECK_CRC7)) { +		if (ptr[stat_idx] & SD_CRC7_ERR) { +			err = -EILSEQ; +			dev_dbg(sdmmc_dev(host), "CRC7 error\n"); +			goto out; +		} +	} + +	if (rsp_type == SD_RSP_TYPE_R2) { +		for (i = 0; i < 4; i++) { +			cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4); +			dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n", +					i, cmd->resp[i]); +		} +	} else { +		cmd->resp[0] = get_unaligned_be32(ptr + 1); +		dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n", +				cmd->resp[0]); +	} + +out: +	cmd->error = err; +} + +static int sd_rw_multi(struct rtsx_usb_sdmmc *host, struct mmc_request *mrq) +{ +	struct rtsx_ucr *ucr = host->ucr; +	struct mmc_data *data = mrq->data; +	int read = (data->flags & MMC_DATA_READ) ? 1 : 0; +	u8 cfg2, trans_mode; +	int err; +	u8 flag; +	size_t data_len = data->blksz * data->blocks; +	unsigned int pipe; + +	if (read) { +		dev_dbg(sdmmc_dev(host), "%s: read %zu bytes\n", +				__func__, data_len); +		cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | +			SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; +		trans_mode = SD_TM_AUTO_READ_3; +	} else { +		dev_dbg(sdmmc_dev(host), "%s: write %zu bytes\n", +				__func__, data_len); +		cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | +			SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; +		trans_mode = SD_TM_AUTO_WRITE_3; +	} + +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_L, +			0xFF, (u8)data->blocks); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_H, +			0xFF, (u8)(data->blocks >> 8)); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, +			0x01, RING_BUFFER); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3, +			0xFF, (u8)(data_len >> 24)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2, +			0xFF, (u8)(data_len >> 16)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1, +			0xFF, (u8)(data_len >> 8)); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0, +			0xFF, (u8)data_len); +	if (read) { +		flag = MODE_CDIR; +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL, +				0x03 | DMA_PACK_SIZE_MASK, +				DMA_DIR_FROM_CARD | DMA_EN | DMA_512); +	} else { +		flag = MODE_CDOR; +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL, +				0x03 | DMA_PACK_SIZE_MASK, +				DMA_DIR_TO_CARD | DMA_EN | DMA_512); +	} + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, +			trans_mode | SD_TRANSFER_START); +	rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER, +			SD_TRANSFER_END, SD_TRANSFER_END); + +	err = rtsx_usb_send_cmd(ucr, flag, 100); +	if (err) +		return err; + +	if (read) +		pipe = usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN); +	else +		pipe = usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT); + +	err = rtsx_usb_transfer_data(ucr, pipe, data->sg, data_len, +			data->sg_len,  NULL, 10000); +	if (err) { +		dev_dbg(sdmmc_dev(host), "rtsx_usb_transfer_data error %d\n" +				, err); +		sd_clear_error(host); +		return err; +	} + +	return rtsx_usb_get_rsp(ucr, 1, 2000); +} + +static inline void sd_enable_initial_mode(struct rtsx_usb_sdmmc *host) +{ +	rtsx_usb_write_register(host->ucr, SD_CFG1, +			SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_128); +} + +static inline void sd_disable_initial_mode(struct rtsx_usb_sdmmc *host) +{ +	rtsx_usb_write_register(host->ucr, SD_CFG1, +			SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_0); +} + +static void sd_normal_rw(struct rtsx_usb_sdmmc *host, +		struct mmc_request *mrq) +{ +	struct mmc_command *cmd = mrq->cmd; +	struct mmc_data *data = mrq->data; +	u8 *buf; + +	buf = kzalloc(data->blksz, GFP_NOIO); +	if (!buf) { +		cmd->error = -ENOMEM; +		return; +	} + +	if (data->flags & MMC_DATA_READ) { +		if (host->initial_mode) +			sd_disable_initial_mode(host); + +		cmd->error = sd_read_data(host, cmd, (u16)data->blksz, buf, +				data->blksz, 200); + +		if (host->initial_mode) +			sd_enable_initial_mode(host); + +		sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz); +	} else { +		sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz); + +		cmd->error = sd_write_data(host, cmd, (u16)data->blksz, buf, +				data->blksz, 200); +	} + +	kfree(buf); +} + +static int sd_change_phase(struct rtsx_usb_sdmmc *host, u8 sample_point, int tx) +{ +	struct rtsx_ucr *ucr = host->ucr; +	int err; + +	dev_dbg(sdmmc_dev(host), "%s: %s sample_point = %d\n", +			__func__, tx ? "TX" : "RX", sample_point); + +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); + +	if (tx) +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, +				0x0F, sample_point); +	else +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK1_CTL, +				0x0F, sample_point); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, +			PHASE_NOT_RESET, PHASE_NOT_RESET); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, 0); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_RST, 0); + +	err = rtsx_usb_send_cmd(ucr, MODE_C, 100); +	if (err) +		return err; + +	return 0; +} + +static inline u32 get_phase_point(u32 phase_map, unsigned int idx) +{ +	idx &= MAX_PHASE; +	return phase_map & (1 << idx); +} + +static int get_phase_len(u32 phase_map, unsigned int idx) +{ +	int i; + +	for (i = 0; i < MAX_PHASE + 1; i++) { +		if (get_phase_point(phase_map, idx + i) == 0) +			return i; +	} +	return MAX_PHASE + 1; +} + +static u8 sd_search_final_phase(struct rtsx_usb_sdmmc *host, u32 phase_map) +{ +	int start = 0, len = 0; +	int start_final = 0, len_final = 0; +	u8 final_phase = 0xFF; + +	if (phase_map == 0) { +		dev_dbg(sdmmc_dev(host), "Phase: [map:%x]\n", phase_map); +		return final_phase; +	} + +	while (start < MAX_PHASE + 1) { +		len = get_phase_len(phase_map, start); +		if (len_final < len) { +			start_final = start; +			len_final = len; +		} +		start += len ? len : 1; +	} + +	final_phase = (start_final + len_final / 2) & MAX_PHASE; +	dev_dbg(sdmmc_dev(host), "Phase: [map:%x] [maxlen:%d] [final:%d]\n", +		phase_map, len_final, final_phase); + +	return final_phase; +} + +static void sd_wait_data_idle(struct rtsx_usb_sdmmc *host) +{ +	int err, i; +	u8 val = 0; + +	for (i = 0; i < 100; i++) { +		err = rtsx_usb_ep0_read_register(host->ucr, +				SD_DATA_STATE, &val); +		if (val & SD_DATA_IDLE) +			return; + +		usleep_range(100, 1000); +	} +} + +static int sd_tuning_rx_cmd(struct rtsx_usb_sdmmc *host, +		u8 opcode, u8 sample_point) +{ +	int err; +	struct mmc_command cmd = {0}; + +	err = sd_change_phase(host, sample_point, 0); +	if (err) +		return err; + +	cmd.opcode = MMC_SEND_TUNING_BLOCK; +	err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100); +	if (err) { +		/* Wait till SD DATA IDLE */ +		sd_wait_data_idle(host); +		sd_clear_error(host); +		return err; +	} + +	return 0; +} + +static void sd_tuning_phase(struct rtsx_usb_sdmmc *host, +		u8 opcode, u16 *phase_map) +{ +	int err, i; +	u16 raw_phase_map = 0; + +	for (i = MAX_PHASE; i >= 0; i--) { +		err = sd_tuning_rx_cmd(host, opcode, (u8)i); +		if (!err) +			raw_phase_map |= 1 << i; +	} + +	if (phase_map) +		*phase_map = raw_phase_map; +} + +static int sd_tuning_rx(struct rtsx_usb_sdmmc *host, u8 opcode) +{ +	int err, i; +	u16 raw_phase_map[RX_TUNING_CNT] = {0}, phase_map; +	u8 final_phase; + +	/* setting fixed default TX phase */ +	err = sd_change_phase(host, 0x01, 1); +	if (err) { +		dev_dbg(sdmmc_dev(host), "TX phase setting failed\n"); +		return err; +	} + +	/* tuning RX phase */ +	for (i = 0; i < RX_TUNING_CNT; i++) { +		sd_tuning_phase(host, opcode, &(raw_phase_map[i])); + +		if (raw_phase_map[i] == 0) +			break; +	} + +	phase_map = 0xFFFF; +	for (i = 0; i < RX_TUNING_CNT; i++) { +		dev_dbg(sdmmc_dev(host), "RX raw_phase_map[%d] = 0x%04x\n", +				i, raw_phase_map[i]); +		phase_map &= raw_phase_map[i]; +	} +	dev_dbg(sdmmc_dev(host), "RX phase_map = 0x%04x\n", phase_map); + +	if (phase_map) { +		final_phase = sd_search_final_phase(host, phase_map); +		if (final_phase == 0xFF) +			return -EINVAL; + +		err = sd_change_phase(host, final_phase, 0); +		if (err) +			return err; +	} else { +		return -EINVAL; +	} + +	return 0; +} + +static int sdmmc_get_ro(struct mmc_host *mmc) +{ +	struct rtsx_usb_sdmmc *host = mmc_priv(mmc); +	struct rtsx_ucr *ucr = host->ucr; +	int err; +	u16 val; + +	if (host->host_removal) +		return -ENOMEDIUM; + +	mutex_lock(&ucr->dev_mutex); + +	/* Check SD card detect */ +	err = rtsx_usb_get_card_status(ucr, &val); + +	mutex_unlock(&ucr->dev_mutex); + + +	/* Treat failed detection as non-ro */ +	if (err) +		return 0; + +	if (val & SD_WP) +		return 1; + +	return 0; +} + +static int sdmmc_get_cd(struct mmc_host *mmc) +{ +	struct rtsx_usb_sdmmc *host = mmc_priv(mmc); +	struct rtsx_ucr *ucr = host->ucr; +	int err; +	u16 val; + +	if (host->host_removal) +		return -ENOMEDIUM; + +	mutex_lock(&ucr->dev_mutex); + +	/* Check SD card detect */ +	err = rtsx_usb_get_card_status(ucr, &val); + +	mutex_unlock(&ucr->dev_mutex); + +	/* Treat failed detection as non-exist */ +	if (err) +		goto no_card; + +	if (val & SD_CD) { +		host->card_exist = true; +		return 1; +	} + +no_card: +	host->card_exist = false; +	return 0; +} + +static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct rtsx_usb_sdmmc *host = mmc_priv(mmc); +	struct rtsx_ucr *ucr = host->ucr; +	struct mmc_command *cmd = mrq->cmd; +	struct mmc_data *data = mrq->data; +	unsigned int data_size = 0; + +	dev_dbg(sdmmc_dev(host), "%s\n", __func__); + +	if (host->host_removal) { +		cmd->error = -ENOMEDIUM; +		goto finish; +	} + +	if ((!host->card_exist)) { +		cmd->error = -ENOMEDIUM; +		goto finish_detect_card; +	} + +	/* +	 * Reject SDIO CMDs to speed up card identification +	 * since unsupported +	 */ +	if (cmd->opcode == SD_IO_SEND_OP_COND || +	    cmd->opcode == SD_IO_RW_DIRECT || +	    cmd->opcode == SD_IO_RW_EXTENDED) { +		cmd->error = -EINVAL; +		goto finish; +	} + +	mutex_lock(&ucr->dev_mutex); + +	mutex_lock(&host->host_mutex); +	host->mrq = mrq; +	mutex_unlock(&host->host_mutex); + +	if (mrq->data) +		data_size = data->blocks * data->blksz; + +	if (!data_size) { +		sd_send_cmd_get_rsp(host, cmd); +	} else if ((!(data_size % 512) && cmd->opcode != MMC_SEND_EXT_CSD) || +		   mmc_op_multi(cmd->opcode)) { +		sd_send_cmd_get_rsp(host, cmd); + +		if (!cmd->error) { +			sd_rw_multi(host, mrq); + +			if (mmc_op_multi(cmd->opcode) && mrq->stop) { +				sd_send_cmd_get_rsp(host, mrq->stop); +				rtsx_usb_write_register(ucr, MC_FIFO_CTL, +						FIFO_FLUSH, FIFO_FLUSH); +			} +		} +	} else { +		sd_normal_rw(host, mrq); +	} + +	if (mrq->data) { +		if (cmd->error || data->error) +			data->bytes_xfered = 0; +		else +			data->bytes_xfered = data->blocks * data->blksz; +	} + +	mutex_unlock(&ucr->dev_mutex); + +finish_detect_card: +	if (cmd->error) { +		/* +		 * detect card when fail to update card existence state and +		 * speed up card removal when retry +		 */ +		sdmmc_get_cd(mmc); +		dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); +	} + +finish: +	mutex_lock(&host->host_mutex); +	host->mrq = NULL; +	mutex_unlock(&host->host_mutex); + +	mmc_request_done(mmc, mrq); +} + +static int sd_set_bus_width(struct rtsx_usb_sdmmc *host, +		unsigned char bus_width) +{ +	int err = 0; +	u8 width[] = { +		[MMC_BUS_WIDTH_1] = SD_BUS_WIDTH_1BIT, +		[MMC_BUS_WIDTH_4] = SD_BUS_WIDTH_4BIT, +		[MMC_BUS_WIDTH_8] = SD_BUS_WIDTH_8BIT, +	}; + +	if (bus_width <= MMC_BUS_WIDTH_8) +		err = rtsx_usb_write_register(host->ucr, SD_CFG1, +				0x03, width[bus_width]); + +	return err; +} + +static int sd_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr) +{ +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5); + +	return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int sd_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr) +{ +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59); + +	return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int sd_pull_ctl_enable_lqfp48(struct rtsx_ucr *ucr) +{ +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0xAA); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0xAA); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0xA9); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5); + +	return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int sd_pull_ctl_enable_qfn24(struct rtsx_ucr *ucr) +{ +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0xA5); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x9A); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0xA5); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x9A); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x65); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x5A); + +	return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int sd_power_on(struct rtsx_usb_sdmmc *host) +{ +	struct rtsx_ucr *ucr = host->ucr; +	int err; + +	dev_dbg(sdmmc_dev(host), "%s\n", __func__); +	rtsx_usb_init_cmd(ucr); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE, +			CARD_SHARE_MASK, CARD_SHARE_SD); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, +			SD_CLK_EN, SD_CLK_EN); +	err = rtsx_usb_send_cmd(ucr, MODE_C, 100); +	if (err) +		return err; + +	if (CHECK_PKG(ucr, LQFP48)) +		err = sd_pull_ctl_enable_lqfp48(ucr); +	else +		err = sd_pull_ctl_enable_qfn24(ucr); +	if (err) +		return err; + +	err = rtsx_usb_write_register(ucr, CARD_PWR_CTL, +			POWER_MASK, PARTIAL_POWER_ON); +	if (err) +		return err; + +	usleep_range(800, 1000); + +	rtsx_usb_init_cmd(ucr); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, +			POWER_MASK|LDO3318_PWR_MASK, POWER_ON|LDO_ON); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, +			SD_OUTPUT_EN, SD_OUTPUT_EN); + +	return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int sd_power_off(struct rtsx_usb_sdmmc *host) +{ +	struct rtsx_ucr *ucr = host->ucr; +	int err; + +	dev_dbg(sdmmc_dev(host), "%s\n", __func__); +	rtsx_usb_init_cmd(ucr); + +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, 0); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, SD_OUTPUT_EN, 0); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, +			POWER_MASK, POWER_OFF); +	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, +			POWER_MASK|LDO3318_PWR_MASK, POWER_OFF|LDO_SUSPEND); + +	err = rtsx_usb_send_cmd(ucr, MODE_C, 100); +	if (err) +		return err; + +	if (CHECK_PKG(ucr, LQFP48)) +			return sd_pull_ctl_disable_lqfp48(ucr); +	return sd_pull_ctl_disable_qfn24(ucr); +} + +static int sd_set_power_mode(struct rtsx_usb_sdmmc *host, +		unsigned char power_mode) +{ +	int err; + +	if (power_mode != MMC_POWER_OFF) +		power_mode = MMC_POWER_ON; + +	if (power_mode == host->power_mode) +		return 0; + +	if (power_mode == MMC_POWER_OFF) { +		err = sd_power_off(host); +		pm_runtime_put(sdmmc_dev(host)); +	} else { +		pm_runtime_get_sync(sdmmc_dev(host)); +		err = sd_power_on(host); +	} + +	if (!err) +		host->power_mode = power_mode; + +	return err; +} + +static int sd_set_timing(struct rtsx_usb_sdmmc *host, +		unsigned char timing, bool *ddr_mode) +{ +	struct rtsx_ucr *ucr = host->ucr; +	int err; + +	*ddr_mode = false; + +	rtsx_usb_init_cmd(ucr); + +	switch (timing) { +	case MMC_TIMING_UHS_SDR104: +	case MMC_TIMING_UHS_SDR50: +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1, +				0x0C | SD_ASYNC_FIFO_RST, +				SD_30_MODE | SD_ASYNC_FIFO_RST); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, +				CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); +		break; + +	case MMC_TIMING_UHS_DDR50: +		*ddr_mode = true; + +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1, +				0x0C | SD_ASYNC_FIFO_RST, +				SD_DDR_MODE | SD_ASYNC_FIFO_RST); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, +				CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PUSH_POINT_CTL, +				DDR_VAR_TX_CMD_DAT, DDR_VAR_TX_CMD_DAT); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, +				DDR_VAR_RX_DAT | DDR_VAR_RX_CMD, +				DDR_VAR_RX_DAT | DDR_VAR_RX_CMD); +		break; + +	case MMC_TIMING_MMC_HS: +	case MMC_TIMING_SD_HS: +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1, +				0x0C, SD_20_MODE); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, +				CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PUSH_POINT_CTL, +				SD20_TX_SEL_MASK, SD20_TX_14_AHEAD); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, +				SD20_RX_SEL_MASK, SD20_RX_14_DELAY); +		break; + +	default: +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_CFG1, 0x0C, SD_20_MODE); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, +				CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, +				SD_PUSH_POINT_CTL, 0xFF, 0); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, +				SD20_RX_SEL_MASK, SD20_RX_POS_EDGE); +		break; +	} + +	err = rtsx_usb_send_cmd(ucr, MODE_C, 100); + +	return err; +} + +static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct rtsx_usb_sdmmc *host = mmc_priv(mmc); +	struct rtsx_ucr *ucr = host->ucr; + +	dev_dbg(sdmmc_dev(host), "%s\n", __func__); +	mutex_lock(&ucr->dev_mutex); + +	if (rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD)) { +		mutex_unlock(&ucr->dev_mutex); +		return; +	} + +	sd_set_power_mode(host, ios->power_mode); +	sd_set_bus_width(host, ios->bus_width); +	sd_set_timing(host, ios->timing, &host->ddr_mode); + +	host->vpclk = false; +	host->double_clk = true; + +	switch (ios->timing) { +	case MMC_TIMING_UHS_SDR104: +	case MMC_TIMING_UHS_SDR50: +		host->ssc_depth = SSC_DEPTH_2M; +		host->vpclk = true; +		host->double_clk = false; +		break; +	case MMC_TIMING_UHS_DDR50: +	case MMC_TIMING_UHS_SDR25: +		host->ssc_depth = SSC_DEPTH_1M; +		break; +	default: +		host->ssc_depth = SSC_DEPTH_512K; +		break; +	} + +	host->initial_mode = (ios->clock <= 1000000) ? true : false; +	host->clock = ios->clock; + +	rtsx_usb_switch_clock(host->ucr, host->clock, host->ssc_depth, +			host->initial_mode, host->double_clk, host->vpclk); + +	mutex_unlock(&ucr->dev_mutex); +	dev_dbg(sdmmc_dev(host), "%s end\n", __func__); +} + +static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct rtsx_usb_sdmmc *host = mmc_priv(mmc); +	struct rtsx_ucr *ucr = host->ucr; +	int err = 0; + +	dev_dbg(sdmmc_dev(host), "%s: signal_voltage = %d\n", +			__func__, ios->signal_voltage); + +	if (host->host_removal) +		return -ENOMEDIUM; + +	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_120) +		return -EPERM; + +	mutex_lock(&ucr->dev_mutex); + +	err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD); +	if (err) { +		mutex_unlock(&ucr->dev_mutex); +		return err; +	} + +	/* Let mmc core do the busy checking, simply stop the forced-toggle +	 * clock(while issuing CMD11) and switch voltage. +	 */ +	rtsx_usb_init_cmd(ucr); + +	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PAD_CTL, +				SD_IO_USING_1V8, SD_IO_USING_3V3); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, +				TUNE_SD18_MASK, TUNE_SD18_3V3); +	} else { +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BUS_STAT, +				SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, +				SD_CLK_FORCE_STOP); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PAD_CTL, +				SD_IO_USING_1V8, SD_IO_USING_1V8); +		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, +				TUNE_SD18_MASK, TUNE_SD18_1V8); +	} + +	err = rtsx_usb_send_cmd(ucr, MODE_C, 100); +	mutex_unlock(&ucr->dev_mutex); + +	return err; +} + +static int sdmmc_card_busy(struct mmc_host *mmc) +{ +	struct rtsx_usb_sdmmc *host = mmc_priv(mmc); +	struct rtsx_ucr *ucr = host->ucr; +	int err; +	u8 stat; +	u8 mask = SD_DAT3_STATUS | SD_DAT2_STATUS | SD_DAT1_STATUS +		| SD_DAT0_STATUS; + +	dev_dbg(sdmmc_dev(host), "%s\n", __func__); + +	mutex_lock(&ucr->dev_mutex); + +	err = rtsx_usb_write_register(ucr, SD_BUS_STAT, +			SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, +			SD_CLK_TOGGLE_EN); +	if (err) +		goto out; + +	mdelay(1); + +	err = rtsx_usb_read_register(ucr, SD_BUS_STAT, &stat); +	if (err) +		goto out; + +	err = rtsx_usb_write_register(ucr, SD_BUS_STAT, +			SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); +out: +	mutex_unlock(&ucr->dev_mutex); + +	if (err) +		return err; + +	/* check if any pin between dat[0:3] is low */ +	if ((stat & mask) != mask) +		return 1; +	else +		return 0; +} + +static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ +	struct rtsx_usb_sdmmc *host = mmc_priv(mmc); +	struct rtsx_ucr *ucr = host->ucr; +	int err = 0; + +	if (host->host_removal) +		return -ENOMEDIUM; + +	mutex_lock(&ucr->dev_mutex); + +	if (!host->ddr_mode) +		err = sd_tuning_rx(host, MMC_SEND_TUNING_BLOCK); + +	mutex_unlock(&ucr->dev_mutex); + +	return err; +} + +static const struct mmc_host_ops rtsx_usb_sdmmc_ops = { +	.request = sdmmc_request, +	.set_ios = sdmmc_set_ios, +	.get_ro = sdmmc_get_ro, +	.get_cd = sdmmc_get_cd, +	.start_signal_voltage_switch = sdmmc_switch_voltage, +	.card_busy = sdmmc_card_busy, +	.execute_tuning = sdmmc_execute_tuning, +}; + +#ifdef RTSX_USB_USE_LEDS_CLASS +static void rtsx_usb_led_control(struct led_classdev *led, +	enum led_brightness brightness) +{ +	struct rtsx_usb_sdmmc *host = container_of(led, +			struct rtsx_usb_sdmmc, led); + +	if (host->host_removal) +		return; + +	host->led.brightness = brightness; +	schedule_work(&host->led_work); +} + +static void rtsx_usb_update_led(struct work_struct *work) +{ +	struct rtsx_usb_sdmmc *host = +		container_of(work, struct rtsx_usb_sdmmc, led_work); +	struct rtsx_ucr *ucr = host->ucr; + +	mutex_lock(&ucr->dev_mutex); + +	if (host->led.brightness == LED_OFF) +		rtsx_usb_turn_off_led(ucr); +	else +		rtsx_usb_turn_on_led(ucr); + +	mutex_unlock(&ucr->dev_mutex); +} +#endif + +static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host) +{ +	struct mmc_host *mmc = host->mmc; + +	mmc->f_min = 250000; +	mmc->f_max = 208000000; +	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; +	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | +		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | +		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | +		MMC_CAP_NEEDS_POLL; + +	mmc->max_current_330 = 400; +	mmc->max_current_180 = 800; +	mmc->ops = &rtsx_usb_sdmmc_ops; +	mmc->max_segs = 256; +	mmc->max_seg_size = 65536; +	mmc->max_blk_size = 512; +	mmc->max_blk_count = 65535; +	mmc->max_req_size = 524288; + +	host->power_mode = MMC_POWER_OFF; +} + +static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) +{ +	struct mmc_host *mmc; +	struct rtsx_usb_sdmmc *host; +	struct rtsx_ucr *ucr; +#ifdef RTSX_USB_USE_LEDS_CLASS +	int err; +#endif + +	ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent)); +	if (!ucr) +		return -ENXIO; + +	dev_dbg(&(pdev->dev), ": Realtek USB SD/MMC controller found\n"); + +	mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); +	if (!mmc) +		return -ENOMEM; + +	host = mmc_priv(mmc); +	host->ucr = ucr; +	host->mmc = mmc; +	host->pdev = pdev; +	platform_set_drvdata(pdev, host); + +	mutex_init(&host->host_mutex); +	rtsx_usb_init_host(host); +	pm_runtime_enable(&pdev->dev); + +#ifdef RTSX_USB_USE_LEDS_CLASS +	snprintf(host->led_name, sizeof(host->led_name), +		"%s::", mmc_hostname(mmc)); +	host->led.name = host->led_name; +	host->led.brightness = LED_OFF; +	host->led.default_trigger = mmc_hostname(mmc); +	host->led.brightness_set = rtsx_usb_led_control; + +	err = led_classdev_register(mmc_dev(mmc), &host->led); +	if (err) +		dev_err(&(pdev->dev), +				"Failed to register LED device: %d\n", err); +	INIT_WORK(&host->led_work, rtsx_usb_update_led); + +#endif +	mmc_add_host(mmc); + +	return 0; +} + +static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev) +{ +	struct rtsx_usb_sdmmc *host = platform_get_drvdata(pdev); +	struct mmc_host *mmc; + +	if (!host) +		return 0; + +	mmc = host->mmc; +	host->host_removal = true; + +	mutex_lock(&host->host_mutex); +	if (host->mrq) { +		dev_dbg(&(pdev->dev), +			"%s: Controller removed during transfer\n", +			mmc_hostname(mmc)); +		host->mrq->cmd->error = -ENOMEDIUM; +		if (host->mrq->stop) +			host->mrq->stop->error = -ENOMEDIUM; +		mmc_request_done(mmc, host->mrq); +	} +	mutex_unlock(&host->host_mutex); + +	mmc_remove_host(mmc); + +#ifdef RTSX_USB_USE_LEDS_CLASS +	cancel_work_sync(&host->led_work); +	led_classdev_unregister(&host->led); +#endif + +	mmc_free_host(mmc); +	pm_runtime_disable(&pdev->dev); +	platform_set_drvdata(pdev, NULL); + +	dev_dbg(&(pdev->dev), +		": Realtek USB SD/MMC module has been removed\n"); + +	return 0; +} + +static struct platform_device_id rtsx_usb_sdmmc_ids[] = { +	{ +		.name = "rtsx_usb_sdmmc", +	}, { +		/* sentinel */ +	} +}; +MODULE_DEVICE_TABLE(platform, rtsx_usb_sdmmc_ids); + +static struct platform_driver rtsx_usb_sdmmc_driver = { +	.probe		= rtsx_usb_sdmmc_drv_probe, +	.remove		= rtsx_usb_sdmmc_drv_remove, +	.id_table       = rtsx_usb_sdmmc_ids, +	.driver		= { +		.owner	= THIS_MODULE, +		.name	= "rtsx_usb_sdmmc", +	}, +}; +module_platform_driver(rtsx_usb_sdmmc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>"); +MODULE_DESCRIPTION("Realtek USB SD/MMC Card Host Driver"); diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 8d6794cdf89..f23782683a7 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -23,7 +23,9 @@  #include <linux/irq.h>  #include <linux/io.h> +#include <plat/gpio-cfg.h>  #include <mach/dma.h> +#include <mach/gpio-samsung.h>  #include <linux/platform_data/mmc-s3cmci.h> @@ -1949,39 +1951,10 @@ static struct platform_device_id s3cmci_driver_ids[] = {  MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids); - -#ifdef CONFIG_PM - -static int s3cmci_suspend(struct device *dev) -{ -	struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); - -	return mmc_suspend_host(mmc); -} - -static int s3cmci_resume(struct device *dev) -{ -	struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); - -	return mmc_resume_host(mmc); -} - -static const struct dev_pm_ops s3cmci_pm = { -	.suspend	= s3cmci_suspend, -	.resume		= s3cmci_resume, -}; - -#define s3cmci_pm_ops &s3cmci_pm -#else /* CONFIG_PM */ -#define s3cmci_pm_ops NULL -#endif /* CONFIG_PM */ - -  static struct platform_driver s3cmci_driver = {  	.driver	= {  		.name	= "s3c-sdi",  		.owner	= THIS_MODULE, -		.pm	= s3cmci_pm_ops,  	},  	.id_table	= s3cmci_driver_ids,  	.probe		= s3cmci_probe, diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index cdd4ce0d7c9..8ce3c28cb76 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -31,23 +31,23 @@  #include <linux/bitops.h>  #include <linux/types.h>  #include <linux/err.h> -#include <linux/gpio.h>  #include <linux/interrupt.h>  #include <linux/acpi.h> -#include <linux/acpi_gpio.h>  #include <linux/pm.h>  #include <linux/pm_runtime.h>  #include <linux/delay.h>  #include <linux/mmc/host.h>  #include <linux/mmc/pm.h> +#include <linux/mmc/slot-gpio.h>  #include <linux/mmc/sdhci.h>  #include "sdhci.h"  enum { -	SDHCI_ACPI_SD_CD	= BIT(0), -	SDHCI_ACPI_RUNTIME_PM	= BIT(1), +	SDHCI_ACPI_SD_CD		= BIT(0), +	SDHCI_ACPI_RUNTIME_PM		= BIT(1), +	SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL	= BIT(2),  };  struct sdhci_acpi_chip { @@ -102,11 +102,19 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)  }  static const struct sdhci_ops sdhci_acpi_ops_dflt = { +	.set_clock = sdhci_set_clock,  	.enable_dma = sdhci_acpi_enable_dma, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  static const struct sdhci_ops sdhci_acpi_ops_int = { +	.set_clock = sdhci_set_clock,  	.enable_dma = sdhci_acpi_enable_dma, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  	.hw_reset   = sdhci_acpi_int_hw_reset,  }; @@ -122,6 +130,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {  };  static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { +	.quirks  = SDHCI_QUIRK_BROKEN_CARD_DETECTION,  	.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,  	.caps    = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,  	.flags   = SDHCI_ACPI_RUNTIME_PM, @@ -129,7 +138,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {  };  static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { -	.flags   = SDHCI_ACPI_SD_CD | SDHCI_ACPI_RUNTIME_PM, +	.flags   = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL | +		   SDHCI_ACPI_RUNTIME_PM,  	.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,  }; @@ -142,16 +152,20 @@ struct sdhci_acpi_uid_slot {  static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {  	{ "80860F14" , "1" , &sdhci_acpi_slot_int_emmc },  	{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd   }, +	{ "80860F16" , NULL, &sdhci_acpi_slot_int_sd   },  	{ "INT33BB"  , "2" , &sdhci_acpi_slot_int_sdio },  	{ "INT33C6"  , NULL, &sdhci_acpi_slot_int_sdio }, +	{ "INT3436"  , NULL, &sdhci_acpi_slot_int_sdio },  	{ "PNP0D40"  },  	{ },  };  static const struct acpi_device_id sdhci_acpi_ids[] = {  	{ "80860F14" }, +	{ "80860F16" },  	{ "INT33BB"  },  	{ "INT33C6"  }, +	{ "INT3436"  },  	{ "PNP0D40"  },  	{ },  }; @@ -191,59 +205,6 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,  	return slot;  } -#ifdef CONFIG_PM_RUNTIME - -static irqreturn_t sdhci_acpi_sd_cd(int irq, void *dev_id) -{ -	mmc_detect_change(dev_id, msecs_to_jiffies(200)); -	return IRQ_HANDLED; -} - -static int sdhci_acpi_add_own_cd(struct device *dev, int gpio, -				 struct mmc_host *mmc) -{ -	unsigned long flags; -	int err, irq; - -	if (gpio < 0) { -		err = gpio; -		goto out; -	} - -	err = devm_gpio_request_one(dev, gpio, GPIOF_DIR_IN, "sd_cd"); -	if (err) -		goto out; - -	irq = gpio_to_irq(gpio); -	if (irq < 0) { -		err = irq; -		goto out_free; -	} - -	flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; -	err = devm_request_irq(dev, irq, sdhci_acpi_sd_cd, flags, "sd_cd", mmc); -	if (err) -		goto out_free; - -	return 0; - -out_free: -	devm_gpio_free(dev, gpio); -out: -	dev_warn(dev, "failed to setup card detect wake up\n"); -	return err; -} - -#else - -static int sdhci_acpi_add_own_cd(struct device *dev, int gpio, -				 struct mmc_host *mmc) -{ -	return 0; -} - -#endif -  static int sdhci_acpi_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -254,7 +215,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)  	struct resource *iomem;  	resource_size_t len;  	const char *hid; -	int err, gpio; +	int err;  	if (acpi_bus_get_device(handle, &device))  		return -ENODEV; @@ -279,8 +240,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)  	if (IS_ERR(host))  		return PTR_ERR(host); -	gpio = acpi_get_gpio_by_index(dev, 0, NULL); -  	c = sdhci_priv(host);  	c->host = host;  	c->slot = sdhci_acpi_get_slot(handle, hid); @@ -310,8 +269,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev)  			dma_mask = DMA_BIT_MASK(32);  		} -		dev->dma_mask = &dev->coherent_dma_mask; -		dev->coherent_dma_mask = dma_mask; +		err = dma_coerce_mask_and_coherent(dev, dma_mask); +		if (err) +			goto err_free;  	}  	if (c->slot) { @@ -332,15 +292,19 @@ static int sdhci_acpi_probe(struct platform_device *pdev)  	host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; -	err = sdhci_add_host(host); -	if (err) -		goto err_free; -  	if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) { -		if (sdhci_acpi_add_own_cd(dev, gpio, host->mmc)) +		bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL); + +		if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0)) { +			dev_warn(dev, "failed to setup card detect gpio\n");  			c->use_runtime_pm = false; +		}  	} +	err = sdhci_add_host(host); +	if (err) +		goto err_free; +  	if (c->use_runtime_pm) {  		pm_runtime_set_active(dev);  		pm_suspend_ignore_children(dev, 1); diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 85472d3fd37..dd780c315a6 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -54,6 +54,7 @@  struct sdhci_bcm_kona_dev {  	struct mutex	write_lock; /* protect back to back writes */ +	struct clk	*external_clk;  }; @@ -205,9 +206,13 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,  }  static struct sdhci_ops sdhci_bcm_kona_ops = { +	.set_clock = sdhci_set_clock,  	.get_max_clock = sdhci_bcm_kona_get_max_clk,  	.get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,  	.platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  	.card_event = sdhci_bcm_kona_card_event,  }; @@ -257,6 +262,24 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)  		goto err_pltfm_free;  	} +	/* Get and enable the external clock */ +	kona_dev->external_clk = devm_clk_get(dev, NULL); +	if (IS_ERR(kona_dev->external_clk)) { +		dev_err(dev, "Failed to get external clock\n"); +		ret = PTR_ERR(kona_dev->external_clk); +		goto err_pltfm_free; +	} + +	if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) { +		dev_err(dev, "Failed to set rate external clock\n"); +		goto err_pltfm_free; +	} + +	if (clk_prepare_enable(kona_dev->external_clk) != 0) { +		dev_err(dev, "Failed to enable external clock\n"); +		goto err_pltfm_free; +	} +  	dev_dbg(dev, "non-removable=%c\n",  		(host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');  	dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n", @@ -271,7 +294,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)  	ret = sdhci_bcm_kona_sd_reset(host);  	if (ret) -		goto err_pltfm_free; +		goto err_clk_disable;  	sdhci_bcm_kona_sd_init(host); @@ -307,6 +330,9 @@ err_remove_host:  err_reset:  	sdhci_bcm_kona_sd_reset(host); +err_clk_disable: +	clk_disable_unprepare(kona_dev->external_clk); +  err_pltfm_free:  	sdhci_pltfm_free(pdev); @@ -314,19 +340,18 @@ err_pltfm_free:  	return ret;  } -static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev) +static int sdhci_bcm_kona_remove(struct platform_device *pdev)  {  	struct sdhci_host *host = platform_get_drvdata(pdev); -	int dead; -	u32 scratch; +	struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host); +	struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv); +	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); -	dead = 0; -	scratch = readl(host->ioaddr + SDHCI_INT_STATUS); -	if (scratch == (u32)-1) -		dead = 1;  	sdhci_remove_host(host, dead); -	sdhci_free_host(host); +	clk_disable_unprepare(kona_dev->external_clk); + +	sdhci_pltfm_free(pdev);  	return 0;  } diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 36fa2df0466..46af9a439d7 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -131,8 +131,12 @@ static const struct sdhci_ops bcm2835_sdhci_ops = {  	.read_l = bcm2835_sdhci_readl,  	.read_w = bcm2835_sdhci_readw,  	.read_b = bcm2835_sdhci_readb, +	.set_clock = sdhci_set_clock,  	.get_max_clock = sdhci_pltfm_clk_get_max_clock,  	.get_min_clock = bcm2835_sdhci_get_min_clock, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = { @@ -178,13 +182,7 @@ err:  static int bcm2835_sdhci_remove(struct platform_device *pdev)  { -	struct sdhci_host *host = platform_get_drvdata(pdev); -	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - -	sdhci_remove_host(host, dead); -	sdhci_pltfm_free(pdev); - -	return 0; +	return sdhci_pltfm_unregister(pdev);  }  static const struct of_device_id bcm2835_sdhci_of_match[] = { diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index f2cc26633cb..14b74075589 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -30,13 +30,12 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)  	u16 clk;  	unsigned long timeout; -	if (clock == host->clock) -		return; +	host->mmc->actual_clock = 0;  	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);  	if (clock == 0) -		goto out; +		return;  	while (host->max_clk / div > clock) {  		/* @@ -75,13 +74,14 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)  	clk |= SDHCI_CLOCK_CARD_EN;  	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); -out: -	host->clock = clock;  }  static const struct sdhci_ops sdhci_cns3xxx_ops = {  	.get_max_clock	= sdhci_cns3xxx_get_max_clk,  	.set_clock	= sdhci_cns3xxx_set_clock, +	.set_bus_width	= sdhci_set_bus_width, +	.reset          = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { @@ -90,8 +90,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {  		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |  		  SDHCI_QUIRK_INVERTED_WRITE_PROTECT |  		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | -		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | -		  SDHCI_QUIRK_NONSTANDARD_CLOCK, +		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,  };  static int sdhci_cns3xxx_probe(struct platform_device *pdev) diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 8424839660f..e6278ec007d 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -21,28 +21,17 @@  #include <linux/clk.h>  #include <linux/err.h> -#include <linux/gpio.h>  #include <linux/io.h>  #include <linux/mmc/host.h>  #include <linux/module.h>  #include <linux/of.h> -#include <linux/of_gpio.h>  #include "sdhci-pltfm.h"  struct sdhci_dove_priv {  	struct clk *clk; -	int gpio_cd;  }; -static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data) -{ -	struct sdhci_host *host = data; - -	tasklet_schedule(&host->card_tasklet); -	return IRQ_HANDLED; -} -  static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)  {  	u16 ret; @@ -60,8 +49,6 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)  static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)  { -	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -	struct sdhci_dove_priv *priv = pltfm_host->priv;  	u32 ret;  	ret = readl(host->ioaddr + reg); @@ -71,14 +58,6 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)  		/* Mask the support for 3.0V */  		ret &= ~SDHCI_CAN_VDD_300;  		break; -	case SDHCI_PRESENT_STATE: -		if (gpio_is_valid(priv->gpio_cd)) { -			if (gpio_get_value(priv->gpio_cd) == 0) -				ret |= SDHCI_CARD_PRESENT; -			else -				ret &= ~SDHCI_CARD_PRESENT; -		} -		break;  	}  	return ret;  } @@ -86,6 +65,10 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)  static const struct sdhci_ops sdhci_dove_ops = {  	.read_w	= sdhci_dove_readw,  	.read_l	= sdhci_dove_readl, +	.set_clock = sdhci_set_clock, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  static const struct sdhci_pltfm_data sdhci_dove_pdata = { @@ -113,28 +96,9 @@ static int sdhci_dove_probe(struct platform_device *pdev)  	priv->clk = devm_clk_get(&pdev->dev, NULL); -	if (pdev->dev.of_node) { -		priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, -						  "cd-gpios", 0); -	} else { -		priv->gpio_cd = -EINVAL; -	} - -	if (gpio_is_valid(priv->gpio_cd)) { -		ret = gpio_request(priv->gpio_cd, "sdhci-cd"); -		if (ret) { -			dev_err(&pdev->dev, "card detect gpio request failed: %d\n", -				ret); -			return ret; -		} -		gpio_direction_input(priv->gpio_cd); -	} -  	host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0); -	if (IS_ERR(host)) { -		ret = PTR_ERR(host); -		goto err_sdhci_pltfm_init; -	} +	if (IS_ERR(host)) +		return PTR_ERR(host);  	pltfm_host = sdhci_priv(host);  	pltfm_host->priv = priv; @@ -142,39 +106,20 @@ static int sdhci_dove_probe(struct platform_device *pdev)  	if (!IS_ERR(priv->clk))  		clk_prepare_enable(priv->clk); -	sdhci_get_of_property(pdev); +	ret = mmc_of_parse(host->mmc); +	if (ret) +		goto err_sdhci_add;  	ret = sdhci_add_host(host);  	if (ret)  		goto err_sdhci_add; -	/* -	 * We must request the IRQ after sdhci_add_host(), as the tasklet only -	 * gets setup in sdhci_add_host() and we oops. -	 */ -	if (gpio_is_valid(priv->gpio_cd)) { -		ret = request_irq(gpio_to_irq(priv->gpio_cd), -				  sdhci_dove_carddetect_irq, -				  IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, -				  mmc_hostname(host->mmc), host); -		if (ret) { -			dev_err(&pdev->dev, "card detect irq request failed: %d\n", -				ret); -			goto err_request_irq; -		} -	} -  	return 0; -err_request_irq: -	sdhci_remove_host(host, 0);  err_sdhci_add:  	if (!IS_ERR(priv->clk))  		clk_disable_unprepare(priv->clk);  	sdhci_pltfm_free(pdev); -err_sdhci_pltfm_init: -	if (gpio_is_valid(priv->gpio_cd)) -		gpio_free(priv->gpio_cd);  	return ret;  } @@ -186,11 +131,6 @@ static int sdhci_dove_remove(struct platform_device *pdev)  	sdhci_pltfm_unregister(pdev); -	if (gpio_is_valid(priv->gpio_cd)) { -		free_irq(gpio_to_irq(priv->gpio_cd), host); -		gpio_free(priv->gpio_cd); -	} -  	if (!IS_ERR(priv->clk))  		clk_disable_unprepare(priv->clk); @@ -208,7 +148,7 @@ static struct platform_driver sdhci_dove_driver = {  		.name	= "sdhci-dove",  		.owner	= THIS_MODULE,  		.pm	= SDHCI_PLTFM_PMOPS, -		.of_match_table = of_match_ptr(sdhci_dove_of_match_table), +		.of_match_table = sdhci_dove_of_match_table,  	},  	.probe		= sdhci_dove_probe,  	.remove		= sdhci_dove_remove, diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index abc8cf01e6e..ccec0e32590 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -27,6 +27,7 @@  #include <linux/of_gpio.h>  #include <linux/pinctrl/consumer.h>  #include <linux/platform_data/mmc-esdhc-imx.h> +#include <linux/pm_runtime.h>  #include "sdhci-pltfm.h"  #include "sdhci-esdhc.h" @@ -34,11 +35,41 @@  /* VENDOR SPEC register */  #define ESDHC_VENDOR_SPEC		0xc0  #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1) +#define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1) +#define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)  #define ESDHC_WTMK_LVL			0x44  #define ESDHC_MIX_CTRL			0x48 +#define  ESDHC_MIX_CTRL_DDREN		(1 << 3)  #define  ESDHC_MIX_CTRL_AC23EN		(1 << 7) +#define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22) +#define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23) +#define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)  /* Bits 3 and 6 are not SDHCI standard definitions */  #define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7 +/* Tuning bits */ +#define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000 + +/* dll control register */ +#define ESDHC_DLL_CTRL			0x60 +#define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9 +#define ESDHC_DLL_OVERRIDE_EN_SHIFT	8 + +/* tune control register */ +#define ESDHC_TUNE_CTRL_STATUS		0x68 +#define  ESDHC_TUNE_CTRL_STEP		1 +#define  ESDHC_TUNE_CTRL_MIN		0 +#define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1) + +#define ESDHC_TUNING_CTRL		0xcc +#define ESDHC_STD_TUNING_EN		(1 << 24) +/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ +#define ESDHC_TUNING_START_TAP		0x1 + +#define ESDHC_TUNING_BLOCK_PATTERN_LEN	64 + +/* pinctrl state */ +#define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz" +#define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"  /*   * Our interpretation of the SDHCI_HOST_CONTROL register @@ -66,21 +97,60 @@   * As a result, the TC flag is not asserted and SW  received timeout   * exeception. Bit1 of Vendor Spec registor is used to fix it.   */ -#define ESDHC_FLAG_MULTIBLK_NO_INT	(1 << 1) - -enum imx_esdhc_type { -	IMX25_ESDHC, -	IMX35_ESDHC, -	IMX51_ESDHC, -	IMX53_ESDHC, -	IMX6Q_USDHC, +#define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1) +/* + * The flag enables the workaround for ESDHC errata ENGcm07207 which + * affects i.MX25 and i.MX35. + */ +#define ESDHC_FLAG_ENGCM07207		BIT(2) +/* + * The flag tells that the ESDHC controller is an USDHC block that is + * integrated on the i.MX6 series. + */ +#define ESDHC_FLAG_USDHC		BIT(3) +/* The IP supports manual tuning process */ +#define ESDHC_FLAG_MAN_TUNING		BIT(4) +/* The IP supports standard tuning process */ +#define ESDHC_FLAG_STD_TUNING		BIT(5) +/* The IP has SDHCI_CAPABILITIES_1 register */ +#define ESDHC_FLAG_HAVE_CAP1		BIT(6) + +struct esdhc_soc_data { +	u32 flags; +}; + +static struct esdhc_soc_data esdhc_imx25_data = { +	.flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx35_data = { +	.flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx51_data = { +	.flags = 0, +}; + +static struct esdhc_soc_data esdhc_imx53_data = { +	.flags = ESDHC_FLAG_MULTIBLK_NO_INT, +}; + +static struct esdhc_soc_data usdhc_imx6q_data = { +	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING, +}; + +static struct esdhc_soc_data usdhc_imx6sl_data = { +	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING +			| ESDHC_FLAG_HAVE_CAP1,  };  struct pltfm_imx_data { -	int flags;  	u32 scratchpad; -	enum imx_esdhc_type devtype;  	struct pinctrl *pinctrl; +	struct pinctrl_state *pins_default; +	struct pinctrl_state *pins_100mhz; +	struct pinctrl_state *pins_200mhz; +	const struct esdhc_soc_data *socdata;  	struct esdhc_platform_data boarddata;  	struct clk *clk_ipg;  	struct clk *clk_ahb; @@ -90,25 +160,19 @@ struct pltfm_imx_data {  		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */  		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */  	} multiblock_status; - +	u32 is_ddr;  };  static struct platform_device_id imx_esdhc_devtype[] = {  	{  		.name = "sdhci-esdhc-imx25", -		.driver_data = IMX25_ESDHC, +		.driver_data = (kernel_ulong_t) &esdhc_imx25_data,  	}, {  		.name = "sdhci-esdhc-imx35", -		.driver_data = IMX35_ESDHC, +		.driver_data = (kernel_ulong_t) &esdhc_imx35_data,  	}, {  		.name = "sdhci-esdhc-imx51", -		.driver_data = IMX51_ESDHC, -	}, { -		.name = "sdhci-esdhc-imx53", -		.driver_data = IMX53_ESDHC, -	}, { -		.name = "sdhci-usdhc-imx6q", -		.driver_data = IMX6Q_USDHC, +		.driver_data = (kernel_ulong_t) &esdhc_imx51_data,  	}, {  		/* sentinel */  	} @@ -116,38 +180,34 @@ static struct platform_device_id imx_esdhc_devtype[] = {  MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);  static const struct of_device_id imx_esdhc_dt_ids[] = { -	{ .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], }, -	{ .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], }, -	{ .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], }, -	{ .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], }, -	{ .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], }, +	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, }, +	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, }, +	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, }, +	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, }, +	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, +	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },  	{ /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);  static inline int is_imx25_esdhc(struct pltfm_imx_data *data)  { -	return data->devtype == IMX25_ESDHC; -} - -static inline int is_imx35_esdhc(struct pltfm_imx_data *data) -{ -	return data->devtype == IMX35_ESDHC; +	return data->socdata == &esdhc_imx25_data;  } -static inline int is_imx51_esdhc(struct pltfm_imx_data *data) +static inline int is_imx53_esdhc(struct pltfm_imx_data *data)  { -	return data->devtype == IMX51_ESDHC; +	return data->socdata == &esdhc_imx53_data;  } -static inline int is_imx53_esdhc(struct pltfm_imx_data *data) +static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)  { -	return data->devtype == IMX53_ESDHC; +	return data->socdata == &usdhc_imx6q_data;  } -static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) +static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)  { -	return data->devtype == IMX6Q_USDHC; +	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);  }  static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) @@ -164,7 +224,21 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)  	struct pltfm_imx_data *imx_data = pltfm_host->priv;  	u32 val = readl(host->ioaddr + reg); +	if (unlikely(reg == SDHCI_PRESENT_STATE)) { +		u32 fsl_prss = val; +		/* save the least 20 bits */ +		val = fsl_prss & 0x000FFFFF; +		/* move dat[0-3] bits */ +		val |= (fsl_prss & 0x0F000000) >> 4; +		/* move cmd line bit */ +		val |= (fsl_prss & 0x00800000) << 1; +	} +  	if (unlikely(reg == SDHCI_CAPABILITIES)) { +		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */ +		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1) +			val &= 0xffff0000; +  		/* In FSL esdhc IC module, only bit20 is used to indicate the  		 * ADMA2 capability of esdhc, but this bit is messed up on  		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they @@ -178,6 +252,25 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)  		}  	} +	if (unlikely(reg == SDHCI_CAPABILITIES_1)) { +		if (esdhc_is_usdhc(imx_data)) { +			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1) +				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF; +			else +				/* imx6q/dl does not have cap_1 register, fake one */ +				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 +					| SDHCI_SUPPORT_SDR50 +					| SDHCI_USE_SDR50_TUNING; +		} +	} + +	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) { +		val = 0; +		val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT; +		val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT; +		val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT; +	} +  	if (unlikely(reg == SDHCI_INT_STATUS)) {  		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {  			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR; @@ -224,7 +317,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)  		}  	} -	if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) +	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)  				&& (reg == SDHCI_INT_STATUS)  				&& (val & SDHCI_INT_DATA_END))) {  			u32 v; @@ -256,10 +349,12 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)  {  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);  	struct pltfm_imx_data *imx_data = pltfm_host->priv; +	u16 ret = 0; +	u32 val;  	if (unlikely(reg == SDHCI_HOST_VERSION)) {  		reg ^= 2; -		if (is_imx6q_usdhc(imx_data)) { +		if (esdhc_is_usdhc(imx_data)) {  			/*  			 * The usdhc register returns a wrong host version.  			 * Correct it here. @@ -268,6 +363,45 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)  		}  	} +	if (unlikely(reg == SDHCI_HOST_CONTROL2)) { +		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); +		if (val & ESDHC_VENDOR_SPEC_VSELECT) +			ret |= SDHCI_CTRL_VDD_180; + +		if (esdhc_is_usdhc(imx_data)) { +			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) +				val = readl(host->ioaddr + ESDHC_MIX_CTRL); +			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) +				/* the std tuning bits is in ACMD12_ERR for imx6sl */ +				val = readl(host->ioaddr + SDHCI_ACMD12_ERR); +		} + +		if (val & ESDHC_MIX_CTRL_EXE_TUNE) +			ret |= SDHCI_CTRL_EXEC_TUNING; +		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL) +			ret |= SDHCI_CTRL_TUNED_CLK; + +		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + +		return ret; +	} + +	if (unlikely(reg == SDHCI_TRANSFER_MODE)) { +		if (esdhc_is_usdhc(imx_data)) { +			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); +			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK; +			/* Swap AC23 bit */ +			if (m & ESDHC_MIX_CTRL_AC23EN) { +				ret &= ~ESDHC_MIX_CTRL_AC23EN; +				ret |= SDHCI_TRNS_AUTO_CMD23; +			} +		} else { +			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE); +		} + +		return ret; +	} +  	return readw(host->ioaddr + reg);  } @@ -275,10 +409,54 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)  {  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);  	struct pltfm_imx_data *imx_data = pltfm_host->priv; +	u32 new_val = 0;  	switch (reg) { +	case SDHCI_CLOCK_CONTROL: +		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); +		if (val & SDHCI_CLOCK_CARD_EN) +			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; +		else +			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; +			writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); +		return; +	case SDHCI_HOST_CONTROL2: +		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); +		if (val & SDHCI_CTRL_VDD_180) +			new_val |= ESDHC_VENDOR_SPEC_VSELECT; +		else +			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT; +		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); +		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { +			new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); +			if (val & SDHCI_CTRL_TUNED_CLK) +				new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL; +			else +				new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; +			writel(new_val , host->ioaddr + ESDHC_MIX_CTRL); +		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { +			u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); +			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); +			if (val & SDHCI_CTRL_TUNED_CLK) { +				v |= ESDHC_MIX_CTRL_SMPCLK_SEL; +			} else { +				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; +				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; +			} + +			if (val & SDHCI_CTRL_EXEC_TUNING) { +				v |= ESDHC_MIX_CTRL_EXE_TUNE; +				m |= ESDHC_MIX_CTRL_FBCLK_SEL; +			} else { +				v &= ~ESDHC_MIX_CTRL_EXE_TUNE; +			} + +			writel(v, host->ioaddr + SDHCI_ACMD12_ERR); +			writel(m, host->ioaddr + ESDHC_MIX_CTRL); +		} +		return;  	case SDHCI_TRANSFER_MODE: -		if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) +		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)  				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)  				&& (host->cmd->data->blocks > 1)  				&& (host->cmd->data->flags & MMC_DATA_READ)) { @@ -288,7 +466,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)  			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);  		} -		if (is_imx6q_usdhc(imx_data)) { +		if (esdhc_is_usdhc(imx_data)) {  			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);  			/* Swap AC23 bit */  			if (val & SDHCI_TRNS_AUTO_CMD23) { @@ -310,10 +488,10 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)  			val |= SDHCI_CMD_ABORTCMD;  		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) && -		    (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) +		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))  			imx_data->multiblock_status = MULTIBLK_IN_PROCESS; -		if (is_imx6q_usdhc(imx_data)) +		if (esdhc_is_usdhc(imx_data))  			writel(val << 16,  			       host->ioaddr + SDHCI_TRANSFER_MODE);  		else @@ -379,8 +557,13 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)  		 * The reset on usdhc fails to clear MIX_CTRL register.  		 * Do it manually here.  		 */ -		if (is_imx6q_usdhc(imx_data)) -			writel(0, host->ioaddr + ESDHC_MIX_CTRL); +		if (esdhc_is_usdhc(imx_data)) { +			/* the tuning bits should be kept during reset */ +			new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); +			writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK, +					host->ioaddr + ESDHC_MIX_CTRL); +			imx_data->is_ddr = 0; +		}  	}  } @@ -390,27 +573,77 @@ static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)  	struct pltfm_imx_data *imx_data = pltfm_host->priv;  	struct esdhc_platform_data *boarddata = &imx_data->boarddata; -	u32 f_host = clk_get_rate(pltfm_host->clk); - -	if (boarddata->f_max && (boarddata->f_max < f_host)) +	if (boarddata->f_max && (boarddata->f_max < pltfm_host->clock))  		return boarddata->f_max;  	else -		return f_host; +		return pltfm_host->clock;  }  static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)  {  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); -	return clk_get_rate(pltfm_host->clk) / 256 / 16; +	return pltfm_host->clock / 256 / 16;  }  static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,  					 unsigned int clock)  {  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct pltfm_imx_data *imx_data = pltfm_host->priv; +	unsigned int host_clock = pltfm_host->clock; +	int pre_div = 2; +	int div = 1; +	u32 temp, val; + +	if (clock == 0) { +		host->mmc->actual_clock = 0; + +		if (esdhc_is_usdhc(imx_data)) { +			val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); +			writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, +					host->ioaddr + ESDHC_VENDOR_SPEC); +		} +		return; +	} + +	if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr) +		pre_div = 1; + +	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); +	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN +		| ESDHC_CLOCK_MASK); +	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + +	while (host_clock / pre_div / 16 > clock && pre_div < 256) +		pre_div *= 2; + +	while (host_clock / pre_div / div > clock && div < 16) +		div++; + +	host->mmc->actual_clock = host_clock / pre_div / div; +	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", +		clock, host->mmc->actual_clock); -	esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk)); +	if (imx_data->is_ddr) +		pre_div >>= 2; +	else +		pre_div >>= 1; +	div--; + +	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); +	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN +		| (div << ESDHC_DIVIDER_SHIFT) +		| (pre_div << ESDHC_PREDIV_SHIFT)); +	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + +	if (esdhc_is_usdhc(imx_data)) { +		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); +		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, +		host->ioaddr + ESDHC_VENDOR_SPEC); +	} + +	mdelay(1);  }  static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) @@ -432,7 +665,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)  	return -ENOSYS;  } -static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) +static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)  {  	u32 ctrl; @@ -450,11 +683,204 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)  	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,  			SDHCI_HOST_CONTROL); +} + +static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) +{ +	u32 reg; + +	/* FIXME: delay a bit for card to be ready for next tuning due to errors */ +	mdelay(1); + +	/* This is balanced by the runtime put in sdhci_tasklet_finish */ +	pm_runtime_get_sync(host->mmc->parent); +	reg = readl(host->ioaddr + ESDHC_MIX_CTRL); +	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | +			ESDHC_MIX_CTRL_FBCLK_SEL; +	writel(reg, host->ioaddr + ESDHC_MIX_CTRL); +	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); +	dev_dbg(mmc_dev(host->mmc), +		"tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n", +			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS)); +} + +static void esdhc_request_done(struct mmc_request *mrq) +{ +	complete(&mrq->completion); +} + +static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode, +				 struct scatterlist *sg) +{ +	struct mmc_command cmd = {0}; +	struct mmc_request mrq = {NULL}; +	struct mmc_data data = {0}; + +	cmd.opcode = opcode; +	cmd.arg = 0; +	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + +	data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; +	data.blocks = 1; +	data.flags = MMC_DATA_READ; +	data.sg = sg; +	data.sg_len = 1; + +	mrq.cmd = &cmd; +	mrq.cmd->mrq = &mrq; +	mrq.data = &data; +	mrq.data->mrq = &mrq; +	mrq.cmd->data = mrq.data; + +	mrq.done = esdhc_request_done; +	init_completion(&(mrq.completion)); + +	spin_lock_irq(&host->lock); +	host->mrq = &mrq; + +	sdhci_send_command(host, mrq.cmd); + +	spin_unlock_irq(&host->lock); + +	wait_for_completion(&mrq.completion); + +	if (cmd.error) +		return cmd.error; +	if (data.error) +		return data.error;  	return 0;  } -static const struct sdhci_ops sdhci_esdhc_ops = { +static void esdhc_post_tuning(struct sdhci_host *host) +{ +	u32 reg; + +	reg = readl(host->ioaddr + ESDHC_MIX_CTRL); +	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE; +	writel(reg, host->ioaddr + ESDHC_MIX_CTRL); +} + +static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) +{ +	struct scatterlist sg; +	char *tuning_pattern; +	int min, max, avg, ret; + +	tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL); +	if (!tuning_pattern) +		return -ENOMEM; + +	sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN); + +	/* find the mininum delay first which can pass tuning */ +	min = ESDHC_TUNE_CTRL_MIN; +	while (min < ESDHC_TUNE_CTRL_MAX) { +		esdhc_prepare_tuning(host, min); +		if (!esdhc_send_tuning_cmd(host, opcode, &sg)) +			break; +		min += ESDHC_TUNE_CTRL_STEP; +	} + +	/* find the maxinum delay which can not pass tuning */ +	max = min + ESDHC_TUNE_CTRL_STEP; +	while (max < ESDHC_TUNE_CTRL_MAX) { +		esdhc_prepare_tuning(host, max); +		if (esdhc_send_tuning_cmd(host, opcode, &sg)) { +			max -= ESDHC_TUNE_CTRL_STEP; +			break; +		} +		max += ESDHC_TUNE_CTRL_STEP; +	} + +	/* use average delay to get the best timing */ +	avg = (min + max) / 2; +	esdhc_prepare_tuning(host, avg); +	ret = esdhc_send_tuning_cmd(host, opcode, &sg); +	esdhc_post_tuning(host); + +	kfree(tuning_pattern); + +	dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", +		ret ? "failed" : "passed", avg, ret); + +	return ret; +} + +static int esdhc_change_pinstate(struct sdhci_host *host, +						unsigned int uhs) +{ +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct pltfm_imx_data *imx_data = pltfm_host->priv; +	struct pinctrl_state *pinctrl; + +	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs); + +	if (IS_ERR(imx_data->pinctrl) || +		IS_ERR(imx_data->pins_default) || +		IS_ERR(imx_data->pins_100mhz) || +		IS_ERR(imx_data->pins_200mhz)) +		return -EINVAL; + +	switch (uhs) { +	case MMC_TIMING_UHS_SDR50: +		pinctrl = imx_data->pins_100mhz; +		break; +	case MMC_TIMING_UHS_SDR104: +	case MMC_TIMING_MMC_HS200: +		pinctrl = imx_data->pins_200mhz; +		break; +	default: +		/* back to default state for other legacy timing */ +		pinctrl = imx_data->pins_default; +	} + +	return pinctrl_select_state(imx_data->pinctrl, pinctrl); +} + +static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) +{ +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct pltfm_imx_data *imx_data = pltfm_host->priv; +	struct esdhc_platform_data *boarddata = &imx_data->boarddata; + +	switch (timing) { +	case MMC_TIMING_UHS_SDR12: +	case MMC_TIMING_UHS_SDR25: +	case MMC_TIMING_UHS_SDR50: +	case MMC_TIMING_UHS_SDR104: +	case MMC_TIMING_MMC_HS200: +		break; +	case MMC_TIMING_UHS_DDR50: +	case MMC_TIMING_MMC_DDR52: +		writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | +				ESDHC_MIX_CTRL_DDREN, +				host->ioaddr + ESDHC_MIX_CTRL); +		imx_data->is_ddr = 1; +		if (boarddata->delay_line) { +			u32 v; +			v = boarddata->delay_line << +				ESDHC_DLL_OVERRIDE_VAL_SHIFT | +				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); +			if (is_imx53_esdhc(imx_data)) +				v <<= 1; +			writel(v, host->ioaddr + ESDHC_DLL_CTRL); +		} +		break; +	} + +	esdhc_change_pinstate(host, timing); +} + +static void esdhc_reset(struct sdhci_host *host, u8 mask) +{ +	sdhci_reset(host, mask); + +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + +static struct sdhci_ops sdhci_esdhc_ops = {  	.read_l = esdhc_readl_le,  	.read_w = esdhc_readw_le,  	.write_l = esdhc_writel_le, @@ -464,7 +890,9 @@ static const struct sdhci_ops sdhci_esdhc_ops = {  	.get_max_clock = esdhc_pltfm_get_max_clock,  	.get_min_clock = esdhc_pltfm_get_min_clock,  	.get_ro = esdhc_pltfm_get_ro, -	.platform_bus_width = esdhc_pltfm_bus_width, +	.set_bus_width = esdhc_pltfm_set_bus_width, +	.set_uhs_signaling = esdhc_set_uhs_signaling, +	.reset = esdhc_reset,  };  static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { @@ -506,6 +934,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,  	of_property_read_u32(np, "max-frequency", &boarddata->f_max); +	if (of_find_property(np, "no-1-8-v", NULL)) +		boarddata->support_vsel = false; +	else +		boarddata->support_vsel = true; + +	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) +		boarddata->delay_line = 0; +  	return 0;  }  #else @@ -539,9 +975,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)  		goto free_sdhci;  	} -	if (of_id) -		pdev->id_entry = of_id->data; -	imx_data->devtype = pdev->id_entry->driver_data; +	imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *) +						  pdev->id_entry->driver_data;  	pltfm_host->priv = imx_data;  	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); @@ -563,33 +998,50 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)  	}  	pltfm_host->clk = imx_data->clk_per; - +	pltfm_host->clock = clk_get_rate(pltfm_host->clk);  	clk_prepare_enable(imx_data->clk_per);  	clk_prepare_enable(imx_data->clk_ipg);  	clk_prepare_enable(imx_data->clk_ahb); -	imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); +	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);  	if (IS_ERR(imx_data->pinctrl)) {  		err = PTR_ERR(imx_data->pinctrl);  		goto disable_clk;  	} +	imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl, +						PINCTRL_STATE_DEFAULT); +	if (IS_ERR(imx_data->pins_default)) { +		err = PTR_ERR(imx_data->pins_default); +		dev_err(mmc_dev(host->mmc), "could not get default state\n"); +		goto disable_clk; +	} +  	host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; -	if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) +	if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)  		/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */  		host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK  			| SDHCI_QUIRK_BROKEN_ADMA; -	if (is_imx53_esdhc(imx_data)) -		imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT; -  	/*  	 * The imx6q ROM code will change the default watermark level setting  	 * to something insane.  Change it back here.  	 */ -	if (is_imx6q_usdhc(imx_data)) +	if (esdhc_is_usdhc(imx_data)) {  		writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); +		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; +		host->mmc->caps |= MMC_CAP_1_8V_DDR; +	} + +	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) +		sdhci_esdhc_ops.platform_execute_tuning = +					esdhc_executing_tuning; + +	if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) +		writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) | +			ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP, +			host->ioaddr + ESDHC_TUNING_CTRL);  	boarddata = &imx_data->boarddata;  	if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) { @@ -630,7 +1082,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)  		break;  	case ESDHC_CD_PERMANENT: -		host->mmc->caps = MMC_CAP_NONREMOVABLE; +		host->mmc->caps |= MMC_CAP_NONREMOVABLE;  		break;  	case ESDHC_CD_NONE: @@ -650,10 +1102,33 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)  		break;  	} +	/* sdr50 and sdr104 needs work on 1.8v signal voltage */ +	if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) { +		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, +						ESDHC_PINCTRL_STATE_100MHZ); +		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, +						ESDHC_PINCTRL_STATE_200MHZ); +		if (IS_ERR(imx_data->pins_100mhz) || +				IS_ERR(imx_data->pins_200mhz)) { +			dev_warn(mmc_dev(host->mmc), +				"could not get ultra high speed state, work on normal mode\n"); +			/* fall back to not support uhs by specify no 1.8v quirk */ +			host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; +		} +	} else { +		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; +	} +  	err = sdhci_add_host(host);  	if (err)  		goto disable_clk; +	pm_runtime_set_active(&pdev->dev); +	pm_runtime_enable(&pdev->dev); +	pm_runtime_set_autosuspend_delay(&pdev->dev, 50); +	pm_runtime_use_autosuspend(&pdev->dev); +	pm_suspend_ignore_children(&pdev->dev, 1); +  	return 0;  disable_clk: @@ -674,21 +1149,67 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)  	sdhci_remove_host(host, dead); -	clk_disable_unprepare(imx_data->clk_per); -	clk_disable_unprepare(imx_data->clk_ipg); -	clk_disable_unprepare(imx_data->clk_ahb); +	pm_runtime_dont_use_autosuspend(&pdev->dev); +	pm_runtime_disable(&pdev->dev); + +	if (!IS_ENABLED(CONFIG_PM_RUNTIME)) { +		clk_disable_unprepare(imx_data->clk_per); +		clk_disable_unprepare(imx_data->clk_ipg); +		clk_disable_unprepare(imx_data->clk_ahb); +	}  	sdhci_pltfm_free(pdev);  	return 0;  } +#ifdef CONFIG_PM_RUNTIME +static int sdhci_esdhc_runtime_suspend(struct device *dev) +{ +	struct sdhci_host *host = dev_get_drvdata(dev); +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct pltfm_imx_data *imx_data = pltfm_host->priv; +	int ret; + +	ret = sdhci_runtime_suspend_host(host); + +	if (!sdhci_sdio_irq_enabled(host)) { +		clk_disable_unprepare(imx_data->clk_per); +		clk_disable_unprepare(imx_data->clk_ipg); +	} +	clk_disable_unprepare(imx_data->clk_ahb); + +	return ret; +} + +static int sdhci_esdhc_runtime_resume(struct device *dev) +{ +	struct sdhci_host *host = dev_get_drvdata(dev); +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct pltfm_imx_data *imx_data = pltfm_host->priv; + +	if (!sdhci_sdio_irq_enabled(host)) { +		clk_prepare_enable(imx_data->clk_per); +		clk_prepare_enable(imx_data->clk_ipg); +	} +	clk_prepare_enable(imx_data->clk_ahb); + +	return sdhci_runtime_resume_host(host); +} +#endif + +static const struct dev_pm_ops sdhci_esdhc_pmops = { +	SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume) +	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, +				sdhci_esdhc_runtime_resume, NULL) +}; +  static struct platform_driver sdhci_esdhc_imx_driver = {  	.driver		= {  		.name	= "sdhci-esdhc-imx",  		.owner	= THIS_MODULE,  		.of_match_table = imx_esdhc_dt_ids, -		.pm	= SDHCI_PLTFM_PMOPS, +		.pm	= &sdhci_esdhc_pmops,  	},  	.id_table	= imx_esdhc_devtype,  	.probe		= sdhci_esdhc_imx_probe, diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a2a06420e46..3497cfaf683 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -20,10 +20,8 @@  #define ESDHC_DEFAULT_QUIRKS	(SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \  				SDHCI_QUIRK_NO_BUSY_IRQ | \ -				SDHCI_QUIRK_NONSTANDARD_CLOCK | \  				SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ -				SDHCI_QUIRK_PIO_NEEDS_DELAY | \ -				SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) +				SDHCI_QUIRK_PIO_NEEDS_DELAY)  #define ESDHC_SYSTEM_CONTROL	0x2c  #define ESDHC_CLOCK_MASK	0x0000fff0 @@ -49,41 +47,4 @@  #define ESDHC_HOST_CONTROL_RES	0x05 -static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock, -				   unsigned int host_clock) -{ -	int pre_div = 2; -	int div = 1; -	u32 temp; - -	if (clock == 0) -		goto out; - -	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); -	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN -		| ESDHC_CLOCK_MASK); -	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - -	while (host_clock / pre_div / 16 > clock && pre_div < 256) -		pre_div *= 2; - -	while (host_clock / pre_div / div > clock && div < 16) -		div++; - -	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", -		clock, host_clock / pre_div / div); - -	pre_div >>= 1; -	div--; - -	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); -	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN -		| (div << ESDHC_DIVIDER_SHIFT) -		| (pre_div << ESDHC_PREDIV_SHIFT)); -	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -	mdelay(1); -out: -	host->clock = clock; -} -  #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */ diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c new file mode 100644 index 00000000000..40573a58486 --- /dev/null +++ b/drivers/mmc/host/sdhci-msm.c @@ -0,0 +1,622 @@ +/* + * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver + * + * Copyright (c) 2013-2014, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> +#include <linux/mmc/mmc.h> +#include <linux/slab.h> + +#include "sdhci-pltfm.h" + +#define CORE_HC_MODE		0x78 +#define HC_MODE_EN		0x1 +#define CORE_POWER		0x0 +#define CORE_SW_RST		BIT(7) + +#define MAX_PHASES		16 +#define CORE_DLL_LOCK		BIT(7) +#define CORE_DLL_EN		BIT(16) +#define CORE_CDR_EN		BIT(17) +#define CORE_CK_OUT_EN		BIT(18) +#define CORE_CDR_EXT_EN		BIT(19) +#define CORE_DLL_PDN		BIT(29) +#define CORE_DLL_RST		BIT(30) +#define CORE_DLL_CONFIG		0x100 +#define CORE_DLL_STATUS		0x108 + +#define CORE_VENDOR_SPEC	0x10c +#define CORE_CLK_PWRSAVE	BIT(1) + +#define CDR_SELEXT_SHIFT	20 +#define CDR_SELEXT_MASK		(0xf << CDR_SELEXT_SHIFT) +#define CMUX_SHIFT_PHASE_SHIFT	24 +#define CMUX_SHIFT_PHASE_MASK	(7 << CMUX_SHIFT_PHASE_SHIFT) + +static const u32 tuning_block_64[] = { +	0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe, +	0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777, +	0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff, +	0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7 +}; + +static const u32 tuning_block_128[] = { +	0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc, +	0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff, +	0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff, +	0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb, +	0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc, +	0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff, +	0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb, +	0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77 +}; + +struct sdhci_msm_host { +	struct platform_device *pdev; +	void __iomem *core_mem;	/* MSM SDCC mapped address */ +	struct clk *clk;	/* main SD/MMC bus clock */ +	struct clk *pclk;	/* SDHC peripheral bus clock */ +	struct clk *bus_clk;	/* SDHC bus voter clock */ +	struct mmc_host *mmc; +	struct sdhci_pltfm_data sdhci_msm_pdata; +}; + +/* Platform specific tuning */ +static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) +{ +	u32 wait_cnt = 50; +	u8 ck_out_en; +	struct mmc_host *mmc = host->mmc; + +	/* Poll for CK_OUT_EN bit.  max. poll time = 50us */ +	ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & +			CORE_CK_OUT_EN); + +	while (ck_out_en != poll) { +		if (--wait_cnt == 0) { +			dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n", +			       mmc_hostname(mmc), poll); +			return -ETIMEDOUT; +		} +		udelay(1); + +		ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & +				CORE_CK_OUT_EN); +	} + +	return 0; +} + +static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) +{ +	int rc; +	static const u8 grey_coded_phase_table[] = { +		0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4, +		0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8 +	}; +	unsigned long flags; +	u32 config; +	struct mmc_host *mmc = host->mmc; + +	spin_lock_irqsave(&host->lock, flags); + +	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); +	config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN); +	config |= (CORE_CDR_EXT_EN | CORE_DLL_EN); +	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + +	/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ +	rc = msm_dll_poll_ck_out_en(host, 0); +	if (rc) +		goto err_out; + +	/* +	 * Write the selected DLL clock output phase (0 ... 15) +	 * to CDR_SELEXT bit field of DLL_CONFIG register. +	 */ +	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); +	config &= ~CDR_SELEXT_MASK; +	config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT; +	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + +	/* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) +			| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG); + +	/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ +	rc = msm_dll_poll_ck_out_en(host, 1); +	if (rc) +		goto err_out; + +	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); +	config |= CORE_CDR_EN; +	config &= ~CORE_CDR_EXT_EN; +	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); +	goto out; + +err_out: +	dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n", +	       mmc_hostname(mmc), phase); +out: +	spin_unlock_irqrestore(&host->lock, flags); +	return rc; +} + +/* + * Find out the greatest range of consecuitive selected + * DLL clock output phases that can be used as sampling + * setting for SD3.0 UHS-I card read operation (in SDR104 + * timing mode) or for eMMC4.5 card read operation (in HS200 + * timing mode). + * Select the 3/4 of the range and configure the DLL with the + * selected DLL clock output phase. + */ + +static int msm_find_most_appropriate_phase(struct sdhci_host *host, +					   u8 *phase_table, u8 total_phases) +{ +	int ret; +	u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} }; +	u8 phases_per_row[MAX_PHASES] = { 0 }; +	int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0; +	int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0; +	bool phase_0_found = false, phase_15_found = false; +	struct mmc_host *mmc = host->mmc; + +	if (!total_phases || (total_phases > MAX_PHASES)) { +		dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n", +		       mmc_hostname(mmc), total_phases); +		return -EINVAL; +	} + +	for (cnt = 0; cnt < total_phases; cnt++) { +		ranges[row_index][col_index] = phase_table[cnt]; +		phases_per_row[row_index] += 1; +		col_index++; + +		if ((cnt + 1) == total_phases) { +			continue; +		/* check if next phase in phase_table is consecutive or not */ +		} else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) { +			row_index++; +			col_index = 0; +		} +	} + +	if (row_index >= MAX_PHASES) +		return -EINVAL; + +	/* Check if phase-0 is present in first valid window? */ +	if (!ranges[0][0]) { +		phase_0_found = true; +		phase_0_raw_index = 0; +		/* Check if cycle exist between 2 valid windows */ +		for (cnt = 1; cnt <= row_index; cnt++) { +			if (phases_per_row[cnt]) { +				for (i = 0; i < phases_per_row[cnt]; i++) { +					if (ranges[cnt][i] == 15) { +						phase_15_found = true; +						phase_15_raw_index = cnt; +						break; +					} +				} +			} +		} +	} + +	/* If 2 valid windows form cycle then merge them as single window */ +	if (phase_0_found && phase_15_found) { +		/* number of phases in raw where phase 0 is present */ +		u8 phases_0 = phases_per_row[phase_0_raw_index]; +		/* number of phases in raw where phase 15 is present */ +		u8 phases_15 = phases_per_row[phase_15_raw_index]; + +		if (phases_0 + phases_15 >= MAX_PHASES) +			/* +			 * If there are more than 1 phase windows then total +			 * number of phases in both the windows should not be +			 * more than or equal to MAX_PHASES. +			 */ +			return -EINVAL; + +		/* Merge 2 cyclic windows */ +		i = phases_15; +		for (cnt = 0; cnt < phases_0; cnt++) { +			ranges[phase_15_raw_index][i] = +			    ranges[phase_0_raw_index][cnt]; +			if (++i >= MAX_PHASES) +				break; +		} + +		phases_per_row[phase_0_raw_index] = 0; +		phases_per_row[phase_15_raw_index] = phases_15 + phases_0; +	} + +	for (cnt = 0; cnt <= row_index; cnt++) { +		if (phases_per_row[cnt] > curr_max) { +			curr_max = phases_per_row[cnt]; +			selected_row_index = cnt; +		} +	} + +	i = (curr_max * 3) / 4; +	if (i) +		i--; + +	ret = ranges[selected_row_index][i]; + +	if (ret >= MAX_PHASES) { +		ret = -EINVAL; +		dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n", +		       mmc_hostname(mmc), ret); +	} + +	return ret; +} + +static inline void msm_cm_dll_set_freq(struct sdhci_host *host) +{ +	u32 mclk_freq = 0, config; + +	/* Program the MCLK value to MCLK_FREQ bit field */ +	if (host->clock <= 112000000) +		mclk_freq = 0; +	else if (host->clock <= 125000000) +		mclk_freq = 1; +	else if (host->clock <= 137000000) +		mclk_freq = 2; +	else if (host->clock <= 150000000) +		mclk_freq = 3; +	else if (host->clock <= 162000000) +		mclk_freq = 4; +	else if (host->clock <= 175000000) +		mclk_freq = 5; +	else if (host->clock <= 187000000) +		mclk_freq = 6; +	else if (host->clock <= 200000000) +		mclk_freq = 7; + +	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); +	config &= ~CMUX_SHIFT_PHASE_MASK; +	config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT; +	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); +} + +/* Initialize the DLL (Programmable Delay Line) */ +static int msm_init_cm_dll(struct sdhci_host *host) +{ +	struct mmc_host *mmc = host->mmc; +	int wait_cnt = 50; +	unsigned long flags; + +	spin_lock_irqsave(&host->lock, flags); + +	/* +	 * Make sure that clock is always enabled when DLL +	 * tuning is in progress. Keeping PWRSAVE ON may +	 * turn off the clock. +	 */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) +			& ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC); + +	/* Write 1 to DLL_RST bit of DLL_CONFIG register */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) +			| CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG); + +	/* Write 1 to DLL_PDN bit of DLL_CONFIG register */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) +			| CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG); +	msm_cm_dll_set_freq(host); + +	/* Write 0 to DLL_RST bit of DLL_CONFIG register */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) +			& ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG); + +	/* Write 0 to DLL_PDN bit of DLL_CONFIG register */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) +			& ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG); + +	/* Set DLL_EN bit to 1. */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) +			| CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG); + +	/* Set CK_OUT_EN bit to 1. */ +	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) +			| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG); + +	/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ +	while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) & +		 CORE_DLL_LOCK)) { +		/* max. wait for 50us sec for LOCK bit to be set */ +		if (--wait_cnt == 0) { +			dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n", +			       mmc_hostname(mmc)); +			spin_unlock_irqrestore(&host->lock, flags); +			return -ETIMEDOUT; +		} +		udelay(1); +	} + +	spin_unlock_irqrestore(&host->lock, flags); +	return 0; +} + +static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) +{ +	int tuning_seq_cnt = 3; +	u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; +	const u32 *tuning_block_pattern = tuning_block_64; +	int size = sizeof(tuning_block_64);	/* Pattern size in bytes */ +	int rc; +	struct mmc_host *mmc = host->mmc; +	struct mmc_ios ios = host->mmc->ios; + +	/* +	 * Tuning is required for SDR104, HS200 and HS400 cards and +	 * if clock frequency is greater than 100MHz in these modes. +	 */ +	if (host->clock <= 100 * 1000 * 1000 || +	    !((ios.timing == MMC_TIMING_MMC_HS200) || +	      (ios.timing == MMC_TIMING_UHS_SDR104))) +		return 0; + +	if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && +	    (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) { +		tuning_block_pattern = tuning_block_128; +		size = sizeof(tuning_block_128); +	} + +	data_buf = kmalloc(size, GFP_KERNEL); +	if (!data_buf) +		return -ENOMEM; + +retry: +	/* First of all reset the tuning block */ +	rc = msm_init_cm_dll(host); +	if (rc) +		goto out; + +	phase = 0; +	do { +		struct mmc_command cmd = { 0 }; +		struct mmc_data data = { 0 }; +		struct mmc_request mrq = { +			.cmd = &cmd, +			.data = &data +		}; +		struct scatterlist sg; + +		/* Set the phase in delay line hw block */ +		rc = msm_config_cm_dll_phase(host, phase); +		if (rc) +			goto out; + +		cmd.opcode = opcode; +		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + +		data.blksz = size; +		data.blocks = 1; +		data.flags = MMC_DATA_READ; +		data.timeout_ns = NSEC_PER_SEC;	/* 1 second */ + +		data.sg = &sg; +		data.sg_len = 1; +		sg_init_one(&sg, data_buf, size); +		memset(data_buf, 0, size); +		mmc_wait_for_req(mmc, &mrq); + +		if (!cmd.error && !data.error && +		    !memcmp(data_buf, tuning_block_pattern, size)) { +			/* Tuning is successful at this tuning point */ +			tuned_phases[tuned_phase_cnt++] = phase; +			dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", +				 mmc_hostname(mmc), phase); +		} +	} while (++phase < ARRAY_SIZE(tuned_phases)); + +	if (tuned_phase_cnt) { +		rc = msm_find_most_appropriate_phase(host, tuned_phases, +						     tuned_phase_cnt); +		if (rc < 0) +			goto out; +		else +			phase = rc; + +		/* +		 * Finally set the selected phase in delay +		 * line hw block. +		 */ +		rc = msm_config_cm_dll_phase(host, phase); +		if (rc) +			goto out; +		dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", +			 mmc_hostname(mmc), phase); +	} else { +		if (--tuning_seq_cnt) +			goto retry; +		/* Tuning failed */ +		dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", +		       mmc_hostname(mmc)); +		rc = -EIO; +	} + +out: +	kfree(data_buf); +	return rc; +} + +static const struct of_device_id sdhci_msm_dt_match[] = { +	{ .compatible = "qcom,sdhci-msm-v4" }, +	{}, +}; + +MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); + +static struct sdhci_ops sdhci_msm_ops = { +	.platform_execute_tuning = sdhci_msm_execute_tuning, +	.reset = sdhci_reset, +	.set_clock = sdhci_set_clock, +	.set_bus_width = sdhci_set_bus_width, +	.set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static int sdhci_msm_probe(struct platform_device *pdev) +{ +	struct sdhci_host *host; +	struct sdhci_pltfm_host *pltfm_host; +	struct sdhci_msm_host *msm_host; +	struct resource *core_memres; +	int ret; +	u16 host_version; + +	msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); +	if (!msm_host) +		return -ENOMEM; + +	msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops; +	host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0); +	if (IS_ERR(host)) +		return PTR_ERR(host); + +	pltfm_host = sdhci_priv(host); +	pltfm_host->priv = msm_host; +	msm_host->mmc = host->mmc; +	msm_host->pdev = pdev; + +	ret = mmc_of_parse(host->mmc); +	if (ret) +		goto pltfm_free; + +	sdhci_get_of_property(pdev); + +	/* Setup SDCC bus voter clock. */ +	msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); +	if (!IS_ERR(msm_host->bus_clk)) { +		/* Vote for max. clk rate for max. performance */ +		ret = clk_set_rate(msm_host->bus_clk, INT_MAX); +		if (ret) +			goto pltfm_free; +		ret = clk_prepare_enable(msm_host->bus_clk); +		if (ret) +			goto pltfm_free; +	} + +	/* Setup main peripheral bus clock */ +	msm_host->pclk = devm_clk_get(&pdev->dev, "iface"); +	if (IS_ERR(msm_host->pclk)) { +		ret = PTR_ERR(msm_host->pclk); +		dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret); +		goto bus_clk_disable; +	} + +	ret = clk_prepare_enable(msm_host->pclk); +	if (ret) +		goto bus_clk_disable; + +	/* Setup SDC MMC clock */ +	msm_host->clk = devm_clk_get(&pdev->dev, "core"); +	if (IS_ERR(msm_host->clk)) { +		ret = PTR_ERR(msm_host->clk); +		dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret); +		goto pclk_disable; +	} + +	ret = clk_prepare_enable(msm_host->clk); +	if (ret) +		goto pclk_disable; + +	core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); +	msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres); + +	if (IS_ERR(msm_host->core_mem)) { +		dev_err(&pdev->dev, "Failed to remap registers\n"); +		ret = PTR_ERR(msm_host->core_mem); +		goto clk_disable; +	} + +	/* Reset the core and Enable SDHC mode */ +	writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) | +		       CORE_SW_RST, msm_host->core_mem + CORE_POWER); + +	/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ +	usleep_range(1000, 5000); +	if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) { +		dev_err(&pdev->dev, "Stuck in reset\n"); +		ret = -ETIMEDOUT; +		goto clk_disable; +	} + +	/* Set HC_MODE_EN bit in HC_MODE register */ +	writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); + +	host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; +	host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE; + +	host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); +	dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", +		host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> +			       SDHCI_VENDOR_VER_SHIFT)); + +	ret = sdhci_add_host(host); +	if (ret) +		goto clk_disable; + +	return 0; + +clk_disable: +	clk_disable_unprepare(msm_host->clk); +pclk_disable: +	clk_disable_unprepare(msm_host->pclk); +bus_clk_disable: +	if (!IS_ERR(msm_host->bus_clk)) +		clk_disable_unprepare(msm_host->bus_clk); +pltfm_free: +	sdhci_pltfm_free(pdev); +	return ret; +} + +static int sdhci_msm_remove(struct platform_device *pdev) +{ +	struct sdhci_host *host = platform_get_drvdata(pdev); +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct sdhci_msm_host *msm_host = pltfm_host->priv; +	int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == +		    0xffffffff); + +	sdhci_remove_host(host, dead); +	sdhci_pltfm_free(pdev); +	clk_disable_unprepare(msm_host->clk); +	clk_disable_unprepare(msm_host->pclk); +	if (!IS_ERR(msm_host->bus_clk)) +		clk_disable_unprepare(msm_host->bus_clk); +	return 0; +} + +static struct platform_driver sdhci_msm_driver = { +	.probe = sdhci_msm_probe, +	.remove = sdhci_msm_remove, +	.driver = { +		   .name = "sdhci_msm", +		   .owner = THIS_MODULE, +		   .of_match_table = sdhci_msm_dt_match, +	}, +}; + +module_platform_driver(sdhci_msm_driver); + +MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c new file mode 100644 index 00000000000..5bd1092310f --- /dev/null +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -0,0 +1,228 @@ +/* + * Arasan Secure Digital Host Controller Interface. + * Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu> + * Copyright (c) 2012 Wind River Systems, Inc. + * Copyright (C) 2013 Pengutronix e.K. + * Copyright (C) 2013 Xilinx Inc. + * + * Based on sdhci-of-esdhc.c + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Copyright (c) 2009 MontaVista Software, Inc. + * + * Authors: Xiaobo Xie <X.Xie@freescale.com> + *	    Anton Vorontsov <avorontsov@ru.mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include <linux/module.h> +#include "sdhci-pltfm.h" + +#define SDHCI_ARASAN_CLK_CTRL_OFFSET	0x2c + +#define CLK_CTRL_TIMEOUT_SHIFT		16 +#define CLK_CTRL_TIMEOUT_MASK		(0xf << CLK_CTRL_TIMEOUT_SHIFT) +#define CLK_CTRL_TIMEOUT_MIN_EXP	13 + +/** + * struct sdhci_arasan_data + * @clk_ahb:	Pointer to the AHB clock + */ +struct sdhci_arasan_data { +	struct clk	*clk_ahb; +}; + +static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) +{ +	u32 div; +	unsigned long freq; +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + +	div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); +	div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; + +	freq = clk_get_rate(pltfm_host->clk); +	freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); + +	return freq; +} + +static struct sdhci_ops sdhci_arasan_ops = { +	.set_clock = sdhci_set_clock, +	.get_max_clock = sdhci_pltfm_clk_get_max_clock, +	.get_timeout_clock = sdhci_arasan_get_timeout_clock, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static struct sdhci_pltfm_data sdhci_arasan_pdata = { +	.ops = &sdhci_arasan_ops, +}; + +#ifdef CONFIG_PM_SLEEP +/** + * sdhci_arasan_suspend - Suspend method for the driver + * @dev:	Address of the device structure + * Returns 0 on success and error value on error + * + * Put the device in a low power state. + */ +static int sdhci_arasan_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct sdhci_host *host = platform_get_drvdata(pdev); +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; +	int ret; + +	ret = sdhci_suspend_host(host); +	if (ret) +		return ret; + +	clk_disable(pltfm_host->clk); +	clk_disable(sdhci_arasan->clk_ahb); + +	return 0; +} + +/** + * sdhci_arasan_resume - Resume method for the driver + * @dev:	Address of the device structure + * Returns 0 on success and error value on error + * + * Resume operation after suspend + */ +static int sdhci_arasan_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct sdhci_host *host = platform_get_drvdata(pdev); +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; +	int ret; + +	ret = clk_enable(sdhci_arasan->clk_ahb); +	if (ret) { +		dev_err(dev, "Cannot enable AHB clock.\n"); +		return ret; +	} + +	ret = clk_enable(pltfm_host->clk); +	if (ret) { +		dev_err(dev, "Cannot enable SD clock.\n"); +		clk_disable(sdhci_arasan->clk_ahb); +		return ret; +	} + +	return sdhci_resume_host(host); +} +#endif /* ! CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, +			 sdhci_arasan_resume); + +static int sdhci_arasan_probe(struct platform_device *pdev) +{ +	int ret; +	struct clk *clk_xin; +	struct sdhci_host *host; +	struct sdhci_pltfm_host *pltfm_host; +	struct sdhci_arasan_data *sdhci_arasan; + +	sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan), +			GFP_KERNEL); +	if (!sdhci_arasan) +		return -ENOMEM; + +	sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); +	if (IS_ERR(sdhci_arasan->clk_ahb)) { +		dev_err(&pdev->dev, "clk_ahb clock not found.\n"); +		return PTR_ERR(sdhci_arasan->clk_ahb); +	} + +	clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); +	if (IS_ERR(clk_xin)) { +		dev_err(&pdev->dev, "clk_xin clock not found.\n"); +		return PTR_ERR(clk_xin); +	} + +	ret = clk_prepare_enable(sdhci_arasan->clk_ahb); +	if (ret) { +		dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); +		return ret; +	} + +	ret = clk_prepare_enable(clk_xin); +	if (ret) { +		dev_err(&pdev->dev, "Unable to enable SD clock.\n"); +		goto clk_dis_ahb; +	} + +	host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0); +	if (IS_ERR(host)) { +		ret = PTR_ERR(host); +		dev_err(&pdev->dev, "platform init failed (%u)\n", ret); +		goto clk_disable_all; +	} + +	sdhci_get_of_property(pdev); +	pltfm_host = sdhci_priv(host); +	pltfm_host->priv = sdhci_arasan; +	pltfm_host->clk = clk_xin; + +	ret = sdhci_add_host(host); +	if (ret) { +		dev_err(&pdev->dev, "platform register failed (%u)\n", ret); +		goto err_pltfm_free; +	} + +	return 0; + +err_pltfm_free: +	sdhci_pltfm_free(pdev); +clk_disable_all: +	clk_disable_unprepare(clk_xin); +clk_dis_ahb: +	clk_disable_unprepare(sdhci_arasan->clk_ahb); + +	return ret; +} + +static int sdhci_arasan_remove(struct platform_device *pdev) +{ +	struct sdhci_host *host = platform_get_drvdata(pdev); +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; + +	clk_disable_unprepare(pltfm_host->clk); +	clk_disable_unprepare(sdhci_arasan->clk_ahb); + +	return sdhci_pltfm_unregister(pdev); +} + +static const struct of_device_id sdhci_arasan_of_match[] = { +	{ .compatible = "arasan,sdhci-8.9a" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); + +static struct platform_driver sdhci_arasan_driver = { +	.driver = { +		.name = "sdhci-arasan", +		.owner = THIS_MODULE, +		.of_match_table = sdhci_arasan_of_match, +		.pm = &sdhci_arasan_dev_pm_ops, +	}, +	.probe = sdhci_arasan_probe, +	.remove = sdhci_arasan_remove, +}; + +module_platform_driver(sdhci_arasan_driver); + +MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); +MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index e328252ebf2..8be4dcfb49a 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -199,6 +199,15 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)  static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)  { +	int pre_div = 2; +	int div = 1; +	u32 temp; + +	host->mmc->actual_clock = 0; + +	if (clock == 0) +		return; +  	/* Workaround to reduce the clock frequency for p1010 esdhc */  	if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {  		if (clock > 20000000) @@ -207,23 +216,30 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)  			clock -= 5000000;  	} -	/* Set the clock */ -	esdhc_set_clock(host, clock, host->max_clk); -} +	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); +	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN +		| ESDHC_CLOCK_MASK); +	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); -#ifdef CONFIG_PM -static u32 esdhc_proctl; -static void esdhc_of_suspend(struct sdhci_host *host) -{ -	esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); -} +	while (host->max_clk / pre_div / 16 > clock && pre_div < 256) +		pre_div *= 2; -static void esdhc_of_resume(struct sdhci_host *host) -{ -	esdhc_of_enable_dma(host); -	sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); +	while (host->max_clk / pre_div / div > clock && div < 16) +		div++; + +	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", +		clock, host->max_clk / pre_div / div); + +	pre_div >>= 1; +	div--; + +	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); +	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN +		| (div << ESDHC_DIVIDER_SHIFT) +		| (pre_div << ESDHC_PREDIV_SHIFT)); +	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); +	mdelay(1);  } -#endif  static void esdhc_of_platform_init(struct sdhci_host *host)  { @@ -238,7 +254,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host)  		host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;  } -static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) +static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)  {  	u32 ctrl; @@ -258,8 +274,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)  	clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,  			ESDHC_CTRL_BUSWIDTH_MASK, ctrl); - -	return 0;  }  static const struct sdhci_ops sdhci_esdhc_ops = { @@ -274,14 +288,47 @@ static const struct sdhci_ops sdhci_esdhc_ops = {  	.get_max_clock = esdhc_of_get_max_clock,  	.get_min_clock = esdhc_of_get_min_clock,  	.platform_init = esdhc_of_platform_init, -#ifdef CONFIG_PM -	.platform_suspend = esdhc_of_suspend, -	.platform_resume = esdhc_of_resume, -#endif  	.adma_workaround = esdhci_of_adma_workaround, -	.platform_bus_width = esdhc_pltfm_bus_width, +	.set_bus_width = esdhc_pltfm_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  }; +#ifdef CONFIG_PM + +static u32 esdhc_proctl; +static int esdhc_of_suspend(struct device *dev) +{ +	struct sdhci_host *host = dev_get_drvdata(dev); + +	esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + +	return sdhci_suspend_host(host); +} + +static int esdhc_of_resume(struct device *dev) +{ +	struct sdhci_host *host = dev_get_drvdata(dev); +	int ret = sdhci_resume_host(host); + +	if (ret == 0) { +		/* Isn't this already done by sdhci_resume_host() ? --rmk */ +		esdhc_of_enable_dma(host); +		sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); +	} + +	return ret; +} + +static const struct dev_pm_ops esdhc_pmops = { +	.suspend	= esdhc_of_suspend, +	.resume		= esdhc_of_resume, +}; +#define ESDHC_PMOPS (&esdhc_pmops) +#else +#define ESDHC_PMOPS NULL +#endif +  static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {  	/*  	 * card detection could be handled via GPIO @@ -343,7 +390,7 @@ static struct platform_driver sdhci_esdhc_driver = {  		.name = "sdhci-esdhc",  		.owner = THIS_MODULE,  		.of_match_table = sdhci_esdhc_of_match, -		.pm = SDHCI_PLTFM_PMOPS, +		.pm = ESDHC_PMOPS,  	},  	.probe = sdhci_esdhc_probe,  	.remove = sdhci_esdhc_remove, diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 57c514a81ca..b341661369a 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -58,6 +58,10 @@ static const struct sdhci_ops sdhci_hlwd_ops = {  	.write_l = sdhci_hlwd_writel,  	.write_w = sdhci_hlwd_writew,  	.write_b = sdhci_hlwd_writeb, +	.set_clock = sdhci_set_clock, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  static const struct sdhci_pltfm_data sdhci_hlwd_pdata = { diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c new file mode 100644 index 00000000000..5670e381b0c --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2013 BayHub Technology Ltd. + * + * Authors: Peter Guo <peter.guo@bayhubtech.com> + *          Adam Lee <adam.lee@canonical.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/pci.h> + +#include "sdhci.h" +#include "sdhci-pci.h" +#include "sdhci-pci-o2micro.h" + +static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value) +{ +	u32 scratch_32; +	pci_read_config_dword(chip->pdev, +			      O2_SD_PLL_SETTING, &scratch_32); + +	scratch_32 &= 0x0000FFFF; +	scratch_32 |= value; + +	pci_write_config_dword(chip->pdev, +			       O2_SD_PLL_SETTING, scratch_32); +} + +static void o2_pci_led_enable(struct sdhci_pci_chip *chip) +{ +	int ret; +	u32 scratch_32; + +	/* Set led of SD host function enable */ +	ret = pci_read_config_dword(chip->pdev, +				    O2_SD_FUNC_REG0, &scratch_32); +	if (ret) +		return; + +	scratch_32 &= ~O2_SD_FREG0_LEDOFF; +	pci_write_config_dword(chip->pdev, +			       O2_SD_FUNC_REG0, scratch_32); + +	ret = pci_read_config_dword(chip->pdev, +				    O2_SD_TEST_REG, &scratch_32); +	if (ret) +		return; + +	scratch_32 |= O2_SD_LED_ENABLE; +	pci_write_config_dword(chip->pdev, +			       O2_SD_TEST_REG, scratch_32); + +} + +void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) +{ +	u32 scratch_32; +	int ret; +	/* Improve write performance for SD3.0 */ +	ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14)); +	pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32); + +	/* Enable Link abnormal reset generating Reset */ +	ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~((1 << 19) | (1 << 11)); +	scratch_32 |= (1 << 10); +	pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32); + +	/* set card power over current protection */ +	ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32); +	if (ret) +		return; +	scratch_32 |= (1 << 4); +	pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32); + +	/* adjust the output delay for SD mode */ +	pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492); + +	/* Set the output voltage setting of Aux 1.2v LDO */ +	ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~(3 << 12); +	pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32); + +	/* Set Max power supply capability of SD host */ +	ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~(0x01FE); +	scratch_32 |= 0x00CC; +	pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32); +	/* Set DLL Tuning Window */ +	ret = pci_read_config_dword(chip->pdev, +				    O2_SD_TUNING_CTRL, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~(0x000000FF); +	scratch_32 |= 0x00000066; +	pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32); + +	/* Set UHS2 T_EIDLE */ +	ret = pci_read_config_dword(chip->pdev, +				    O2_SD_UHS2_L1_CTRL, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~(0x000000FC); +	scratch_32 |= 0x00000084; +	pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32); + +	/* Set UHS2 Termination */ +	ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~((1 << 21) | (1 << 30)); + +	/* Set RTD3 function disabled */ +	scratch_32 |= ((1 << 29) | (1 << 28)); +	pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32); + +	/* Set L1 Entrance Timer */ +	ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~(0xf0000000); +	scratch_32 |= 0x30000000; +	pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32); + +	ret = pci_read_config_dword(chip->pdev, +				    O2_SD_MISC_CTRL4, &scratch_32); +	if (ret) +		return; +	scratch_32 &= ~(0x000f0000); +	scratch_32 |= 0x00080000; +	pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_fujin2_pci_init); + +int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) +{ +	struct sdhci_pci_chip *chip; +	struct sdhci_host *host; +	u32 reg; + +	chip = slot->chip; +	host = slot->host; +	switch (chip->pdev->device) { +	case PCI_DEVICE_ID_O2_SDS0: +	case PCI_DEVICE_ID_O2_SEABIRD0: +	case PCI_DEVICE_ID_O2_SEABIRD1: +	case PCI_DEVICE_ID_O2_SDS1: +	case PCI_DEVICE_ID_O2_FUJIN2: +		reg = sdhci_readl(host, O2_SD_VENDOR_SETTING); +		if (reg & 0x1) +			host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + +		if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) +			break; +		/* set dll watch dog timer */ +		reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2); +		reg |= (1 << 12); +		sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2); + +		break; +	default: +		break; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe_slot); + +int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) +{ +	int ret; +	u8 scratch; +	u32 scratch_32; + +	switch (chip->pdev->device) { +	case PCI_DEVICE_ID_O2_8220: +	case PCI_DEVICE_ID_O2_8221: +	case PCI_DEVICE_ID_O2_8320: +	case PCI_DEVICE_ID_O2_8321: +		/* This extra setup is required due to broken ADMA. */ +		ret = pci_read_config_byte(chip->pdev, +				O2_SD_LOCK_WP, &scratch); +		if (ret) +			return ret; +		scratch &= 0x7f; +		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + +		/* Set Multi 3 to VCC3V# */ +		pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); + +		/* Disable CLK_REQ# support after media DET */ +		ret = pci_read_config_byte(chip->pdev, +				O2_SD_CLKREQ, &scratch); +		if (ret) +			return ret; +		scratch |= 0x20; +		pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); + +		/* Choose capabilities, enable SDMA.  We have to write 0x01 +		 * to the capabilities register first to unlock it. +		 */ +		ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); +		if (ret) +			return ret; +		scratch |= 0x01; +		pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); +		pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); + +		/* Disable ADMA1/2 */ +		pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); +		pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); + +		/* Disable the infinite transfer mode */ +		ret = pci_read_config_byte(chip->pdev, +				O2_SD_INF_MOD, &scratch); +		if (ret) +			return ret; +		scratch |= 0x08; +		pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); + +		/* Lock WP */ +		ret = pci_read_config_byte(chip->pdev, +				O2_SD_LOCK_WP, &scratch); +		if (ret) +			return ret; +		scratch |= 0x80; +		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); +		break; +	case PCI_DEVICE_ID_O2_SDS0: +	case PCI_DEVICE_ID_O2_SDS1: +	case PCI_DEVICE_ID_O2_FUJIN2: +		/* UnLock WP */ +		ret = pci_read_config_byte(chip->pdev, +				O2_SD_LOCK_WP, &scratch); +		if (ret) +			return ret; + +		scratch &= 0x7f; +		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + +		/* DevId=8520 subId= 0x11 or 0x12  Type Chip support */ +		if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) { +			ret = pci_read_config_dword(chip->pdev, +						    O2_SD_FUNC_REG0, +						    &scratch_32); +			scratch_32 = ((scratch_32 & 0xFF000000) >> 24); + +			/* Check Whether subId is 0x11 or 0x12 */ +			if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) { +				scratch_32 = 0x2c280000; + +				/* Set Base Clock to 208MZ */ +				o2_pci_set_baseclk(chip, scratch_32); +				ret = pci_read_config_dword(chip->pdev, +							    O2_SD_FUNC_REG4, +							    &scratch_32); + +				/* Enable Base Clk setting change */ +				scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; +				pci_write_config_dword(chip->pdev, +						       O2_SD_FUNC_REG4, +						       scratch_32); + +				/* Set Tuning Window to 4 */ +				pci_write_config_byte(chip->pdev, +						      O2_SD_TUNING_CTRL, 0x44); + +				break; +			} +		} + +		/* Enable 8520 led function */ +		o2_pci_led_enable(chip); + +		/* Set timeout CLK */ +		ret = pci_read_config_dword(chip->pdev, +					    O2_SD_CLK_SETTING, &scratch_32); +		if (ret) +			return ret; + +		scratch_32 &= ~(0xFF00); +		scratch_32 |= 0x07E0C800; +		pci_write_config_dword(chip->pdev, +				       O2_SD_CLK_SETTING, scratch_32); + +		ret = pci_read_config_dword(chip->pdev, +					    O2_SD_CLKREQ, &scratch_32); +		if (ret) +			return ret; +		scratch_32 |= 0x3; +		pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32); + +		ret = pci_read_config_dword(chip->pdev, +					    O2_SD_PLL_SETTING, &scratch_32); +		if (ret) +			return ret; + +		scratch_32 &= ~(0x1F3F070E); +		scratch_32 |= 0x18270106; +		pci_write_config_dword(chip->pdev, +				       O2_SD_PLL_SETTING, scratch_32); + +		/* Disable UHS1 funciton */ +		ret = pci_read_config_dword(chip->pdev, +					    O2_SD_CAP_REG2, &scratch_32); +		if (ret) +			return ret; +		scratch_32 &= ~(0xE0); +		pci_write_config_dword(chip->pdev, +				       O2_SD_CAP_REG2, scratch_32); + +		if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) +			sdhci_pci_o2_fujin2_pci_init(chip); + +		/* Lock WP */ +		ret = pci_read_config_byte(chip->pdev, +					   O2_SD_LOCK_WP, &scratch); +		if (ret) +			return ret; +		scratch |= 0x80; +		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); +		break; +	case PCI_DEVICE_ID_O2_SEABIRD0: +	case PCI_DEVICE_ID_O2_SEABIRD1: +		/* UnLock WP */ +		ret = pci_read_config_byte(chip->pdev, +				O2_SD_LOCK_WP, &scratch); +		if (ret) +			return ret; + +		scratch &= 0x7f; +		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + +		ret = pci_read_config_dword(chip->pdev, +					    O2_SD_PLL_SETTING, &scratch_32); + +		if ((scratch_32 & 0xff000000) == 0x01000000) { +			scratch_32 &= 0x0000FFFF; +			scratch_32 |= 0x1F340000; + +			pci_write_config_dword(chip->pdev, +					       O2_SD_PLL_SETTING, scratch_32); +		} else { +			scratch_32 &= 0x0000FFFF; +			scratch_32 |= 0x2c280000; + +			pci_write_config_dword(chip->pdev, +					       O2_SD_PLL_SETTING, scratch_32); + +			ret = pci_read_config_dword(chip->pdev, +						    O2_SD_FUNC_REG4, +						    &scratch_32); +			scratch_32 |= (1 << 22); +			pci_write_config_dword(chip->pdev, +					       O2_SD_FUNC_REG4, scratch_32); +		} + +		/* Set Tuning Windows to 5 */ +		pci_write_config_byte(chip->pdev, +				O2_SD_TUNING_CTRL, 0x55); +		/* Lock WP */ +		ret = pci_read_config_byte(chip->pdev, +					   O2_SD_LOCK_WP, &scratch); +		if (ret) +			return ret; +		scratch |= 0x80; +		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); +		break; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe); + +int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) +{ +	sdhci_pci_o2_probe(chip); +	return 0; +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_resume); diff --git a/drivers/mmc/host/sdhci-pci-o2micro.h b/drivers/mmc/host/sdhci-pci-o2micro.h new file mode 100644 index 00000000000..f7ffc908d9a --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-o2micro.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 BayHub Technology Ltd. + * + * Authors: Peter Guo <peter.guo@bayhubtech.com> + *          Adam Lee <adam.lee@canonical.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __SDHCI_PCI_O2MICRO_H +#define __SDHCI_PCI_O2MICRO_H + +#include "sdhci-pci.h" + +/* + * O2Micro device IDs + */ + +#define PCI_DEVICE_ID_O2_SDS0		0x8420 +#define PCI_DEVICE_ID_O2_SDS1		0x8421 +#define PCI_DEVICE_ID_O2_FUJIN2		0x8520 +#define PCI_DEVICE_ID_O2_SEABIRD0	0x8620 +#define PCI_DEVICE_ID_O2_SEABIRD1	0x8621 + +/* + * O2Micro device registers + */ + +#define O2_SD_MISC_REG5		0x64 +#define O2_SD_LD0_CTRL		0x68 +#define O2_SD_DEV_CTRL		0x88 +#define O2_SD_LOCK_WP		0xD3 +#define O2_SD_TEST_REG		0xD4 +#define O2_SD_FUNC_REG0		0xDC +#define O2_SD_MULTI_VCC3V	0xEE +#define O2_SD_CLKREQ		0xEC +#define O2_SD_CAPS		0xE0 +#define O2_SD_ADMA1		0xE2 +#define O2_SD_ADMA2		0xE7 +#define O2_SD_INF_MOD		0xF1 +#define O2_SD_MISC_CTRL4	0xFC +#define O2_SD_TUNING_CTRL	0x300 +#define O2_SD_PLL_SETTING	0x304 +#define O2_SD_CLK_SETTING	0x328 +#define O2_SD_CAP_REG2		0x330 +#define O2_SD_CAP_REG0		0x334 +#define O2_SD_UHS1_CAP_SETTING	0x33C +#define O2_SD_DELAY_CTRL	0x350 +#define O2_SD_UHS2_L1_CTRL	0x35C +#define O2_SD_FUNC_REG3		0x3E0 +#define O2_SD_FUNC_REG4		0x3E4 +#define O2_SD_LED_ENABLE	BIT(6) +#define O2_SD_FREG0_LEDOFF	BIT(13) +#define O2_SD_FREG4_ENABLE_CLK_SET	BIT(22) + +#define O2_SD_VENDOR_SETTING	0x110 +#define O2_SD_VENDOR_SETTING2	0x1C8 + +extern void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip); + +extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot); + +extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip); + +extern int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip); + +#endif /* __SDHCI_PCI_O2MICRO_H */ diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index d7d6bc8968d..52c42fcc284 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -27,73 +27,8 @@  #include <linux/mmc/sdhci-pci-data.h>  #include "sdhci.h" - -/* - * PCI device IDs - */ -#define PCI_DEVICE_ID_INTEL_PCH_SDIO0	0x8809 -#define PCI_DEVICE_ID_INTEL_PCH_SDIO1	0x880a -#define PCI_DEVICE_ID_INTEL_BYT_EMMC	0x0f14 -#define PCI_DEVICE_ID_INTEL_BYT_SDIO	0x0f15 -#define PCI_DEVICE_ID_INTEL_BYT_SD	0x0f16 -#define PCI_DEVICE_ID_INTEL_BYT_EMMC2	0x0f50 - -/* - * 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; -	unsigned int		quirks2; -	bool			allow_runtime_pm; - -	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 *); -	int			(*resume) (struct sdhci_pci_chip *); -}; - -struct sdhci_pci_slot { -	struct sdhci_pci_chip	*chip; -	struct sdhci_host	*host; -	struct sdhci_pci_data	*data; - -	int			pci_bar; -	int			rst_n_gpio; -	int			cd_gpio; -	int			cd_irq; - -	void (*hw_reset)(struct sdhci_host *host); -}; - -struct sdhci_pci_chip { -	struct pci_dev		*pdev; - -	unsigned int		quirks; -	unsigned int		quirks2; -	bool			allow_runtime_pm; -	const struct sdhci_pci_fixes *fixes; - -	int			num_slots;	/* Slots on controller */ -	struct sdhci_pci_slot	*slots[MAX_SLOTS]; /* Pointers to host slots */ -}; - +#include "sdhci-pci.h" +#include "sdhci-pci-o2micro.h"  /*****************************************************************************\   *                                                                           * @@ -290,6 +225,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {  static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {  	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,  	.allow_runtime_pm = true, +	.own_cd_for_runtime_pm = true,  };  static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { @@ -354,6 +290,30 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {  static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {  	.quirks2	= SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,  	.allow_runtime_pm = true, +	.own_cd_for_runtime_pm = true, +}; + +/* Define Host controllers for Intel Merrifield platform */ +#define INTEL_MRFL_EMMC_0	0 +#define INTEL_MRFL_EMMC_1	1 + +static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) +{ +	if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) && +	    (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1)) +		/* SD support is not ready yet */ +		return -ENODEV; + +	slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | +				 MMC_CAP_1_8V_DDR; + +	return 0; +} + +static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { +	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, +	.quirks2	= SDHCI_QUIRK2_BROKEN_HS200, +	.probe_slot	= intel_mrfl_mmc_probe_slot,  };  /* O2Micro extra registers */ @@ -365,65 +325,6 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {  #define O2_SD_ADMA2		0xE7  #define O2_SD_INF_MOD		0xF1 -static int o2_probe(struct sdhci_pci_chip *chip) -{ -	int ret; -	u8 scratch; - -	switch (chip->pdev->device) { -	case PCI_DEVICE_ID_O2_8220: -	case PCI_DEVICE_ID_O2_8221: -	case PCI_DEVICE_ID_O2_8320: -	case PCI_DEVICE_ID_O2_8321: -		/* This extra setup is required due to broken ADMA. */ -		ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); -		if (ret) -			return ret; -		scratch &= 0x7f; -		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); - -		/* Set Multi 3 to VCC3V# */ -		pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); - -		/* Disable CLK_REQ# support after media DET */ -		ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch); -		if (ret) -			return ret; -		scratch |= 0x20; -		pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); - -		/* Choose capabilities, enable SDMA.  We have to write 0x01 -		 * to the capabilities register first to unlock it. -		 */ -		ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); -		if (ret) -			return ret; -		scratch |= 0x01; -		pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); -		pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); - -		/* Disable ADMA1/2 */ -		pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); -		pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); - -		/* Disable the infinite transfer mode */ -		ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch); -		if (ret) -			return ret; -		scratch |= 0x08; -		pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); - -		/* Lock WP */ -		ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); -		if (ret) -			return ret; -		scratch |= 0x80; -		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); -	} - -	return 0; -} -  static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)  {  	u8 scratch; @@ -614,7 +515,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)  }  static const struct sdhci_pci_fixes sdhci_o2 = { -	.probe		= o2_probe, +	.probe = sdhci_pci_o2_probe, +	.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, +	.probe_slot = sdhci_pci_o2_probe_slot, +	.resume = sdhci_pci_o2_resume,  };  static const struct sdhci_pci_fixes sdhci_jmicron = { @@ -706,6 +610,18 @@ static const struct sdhci_pci_fixes sdhci_via = {  	.probe		= via_probe,  }; +static int rtsx_probe_slot(struct sdhci_pci_slot *slot) +{ +	slot->host->mmc->caps2 |= MMC_CAP2_HS200; +	return 0; +} + +static const struct sdhci_pci_fixes sdhci_rtsx = { +	.quirks2	= SDHCI_QUIRK2_PRESET_VALUE_BROKEN | +			SDHCI_QUIRK2_BROKEN_DDR50, +	.probe_slot	= rtsx_probe_slot, +}; +  static const struct pci_device_id pci_ids[] = {  	{  		.vendor		= PCI_VENDOR_ID_RICOH, @@ -828,6 +744,14 @@ static const struct pci_device_id pci_ids[] = {  	},  	{ +		.vendor		= PCI_VENDOR_ID_REALTEK, +		.device		= 0x5250, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_rtsx, +	}, + +	{  		.vendor		= PCI_VENDOR_ID_INTEL,  		.device		= PCI_DEVICE_ID_INTEL_MRST_SD0,  		.subvendor	= PCI_ANY_ID, @@ -939,6 +863,54 @@ static const struct pci_device_id pci_ids[] = {  		.driver_data	= (kernel_ulong_t)&sdhci_intel_byt_emmc,  	}, + +	{ +		.vendor		= PCI_VENDOR_ID_INTEL, +		.device		= PCI_DEVICE_ID_INTEL_CLV_SDIO0, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_sd, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_INTEL, +		.device		= PCI_DEVICE_ID_INTEL_CLV_SDIO1, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_sdio, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_INTEL, +		.device		= PCI_DEVICE_ID_INTEL_CLV_SDIO2, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_sdio, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_INTEL, +		.device		= PCI_DEVICE_ID_INTEL_CLV_EMMC0, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_emmc, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_INTEL, +		.device		= PCI_DEVICE_ID_INTEL_CLV_EMMC1, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_emmc, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_INTEL, +		.device		= PCI_DEVICE_ID_INTEL_MRFL_MMC, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_intel_mrfl_mmc, +	},  	{  		.vendor		= PCI_VENDOR_ID_O2,  		.device		= PCI_DEVICE_ID_O2_8120, @@ -979,6 +951,46 @@ static const struct pci_device_id pci_ids[] = {  		.driver_data	= (kernel_ulong_t)&sdhci_o2,  	}, +	{ +		.vendor		= PCI_VENDOR_ID_O2, +		.device		= PCI_DEVICE_ID_O2_FUJIN2, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_o2, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_O2, +		.device		= PCI_DEVICE_ID_O2_SDS0, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_o2, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_O2, +		.device		= PCI_DEVICE_ID_O2_SDS1, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_o2, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_O2, +		.device		= PCI_DEVICE_ID_O2_SEABIRD0, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_o2, +	}, + +	{ +		.vendor		= PCI_VENDOR_ID_O2, +		.device		= PCI_DEVICE_ID_O2_SEABIRD1, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.driver_data	= (kernel_ulong_t)&sdhci_o2, +	}, +  	{	/* Generic SD host controller */  		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)  	}, @@ -1019,7 +1031,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)  	return 0;  } -static int sdhci_pci_bus_width(struct sdhci_host *host, int width) +static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width)  {  	u8 ctrl; @@ -1040,8 +1052,6 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width)  	}  	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - -	return 0;  }  static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) @@ -1068,8 +1078,11 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)  }  static const struct sdhci_ops sdhci_pci_ops = { +	.set_clock	= sdhci_set_clock,  	.enable_dma	= sdhci_pci_enable_dma, -	.platform_bus_width	= sdhci_pci_bus_width, +	.set_bus_width	= sdhci_pci_set_bus_width, +	.reset		= sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  	.hw_reset		= sdhci_pci_hw_reset,  }; @@ -1381,6 +1394,15 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(  	sdhci_pci_add_own_cd(slot); +	/* +	 * Check if the chip needs a separate GPIO for card detect to wake up +	 * from runtime suspend.  If it is not there, don't allow runtime PM. +	 * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. +	 */ +	if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && +	    !gpio_is_valid(slot->cd_gpio)) +		chip->allow_runtime_pm = false; +  	return slot;  remove: diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h new file mode 100644 index 00000000000..6d718719659 --- /dev/null +++ b/drivers/mmc/host/sdhci-pci.h @@ -0,0 +1,78 @@ +#ifndef __SDHCI_PCI_H +#define __SDHCI_PCI_H + +/* + * PCI device IDs + */ + +#define PCI_DEVICE_ID_INTEL_PCH_SDIO0	0x8809 +#define PCI_DEVICE_ID_INTEL_PCH_SDIO1	0x880a +#define PCI_DEVICE_ID_INTEL_BYT_EMMC	0x0f14 +#define PCI_DEVICE_ID_INTEL_BYT_SDIO	0x0f15 +#define PCI_DEVICE_ID_INTEL_BYT_SD	0x0f16 +#define PCI_DEVICE_ID_INTEL_BYT_EMMC2	0x0f50 +#define PCI_DEVICE_ID_INTEL_MRFL_MMC	0x1190 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO0	0x08f9 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO1	0x08fa +#define PCI_DEVICE_ID_INTEL_CLV_SDIO2	0x08fb +#define PCI_DEVICE_ID_INTEL_CLV_EMMC0	0x08e5 +#define PCI_DEVICE_ID_INTEL_CLV_EMMC1	0x08e6 + +/* + * 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; +	unsigned int		quirks2; +	bool			allow_runtime_pm; +	bool			own_cd_for_runtime_pm; + +	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 *); +	int			(*resume) (struct sdhci_pci_chip *); +}; + +struct sdhci_pci_slot { +	struct sdhci_pci_chip	*chip; +	struct sdhci_host	*host; +	struct sdhci_pci_data	*data; + +	int			pci_bar; +	int			rst_n_gpio; +	int			cd_gpio; +	int			cd_irq; + +	void (*hw_reset)(struct sdhci_host *host); +}; + +struct sdhci_pci_chip { +	struct pci_dev		*pdev; + +	unsigned int		quirks; +	unsigned int		quirks2; +	bool			allow_runtime_pm; +	const struct sdhci_pci_fixes *fixes; + +	int			num_slots;	/* Slots on controller */ +	struct sdhci_pci_slot	*slots[MAX_SLOTS]; /* Pointers to host slots */ +}; + +#endif /* __SDHCI_PCI_H */ diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index e2065a44dff..7e834fb78f4 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -45,6 +45,10 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)  EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);  static const struct sdhci_ops sdhci_pltfm_ops = { +	.set_clock = sdhci_set_clock, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  #ifdef CONFIG_OF @@ -237,19 +241,21 @@ int sdhci_pltfm_unregister(struct platform_device *pdev)  EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);  #ifdef CONFIG_PM -static int sdhci_pltfm_suspend(struct device *dev) +int sdhci_pltfm_suspend(struct device *dev)  {  	struct sdhci_host *host = dev_get_drvdata(dev);  	return sdhci_suspend_host(host);  } +EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend); -static int sdhci_pltfm_resume(struct device *dev) +int sdhci_pltfm_resume(struct device *dev)  {  	struct sdhci_host *host = dev_get_drvdata(dev);  	return sdhci_resume_host(host);  } +EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);  const struct dev_pm_ops sdhci_pltfm_pmops = {  	.suspend	= sdhci_pltfm_suspend, diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index e15ced79f7e..04bc2481e5c 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -111,6 +111,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)  }  #ifdef CONFIG_PM +extern int sdhci_pltfm_suspend(struct device *dev); +extern int sdhci_pltfm_resume(struct device *dev);  extern const struct dev_pm_ops sdhci_pltfm_pmops;  #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)  #else diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index d51e061ec57..3c0f3c0a1cc 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -51,11 +51,13 @@  #define MMC_CARD		0x1000  #define MMC_WIDTH		0x0100 -static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) +static void pxav2_reset(struct sdhci_host *host, u8 mask)  {  	struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));  	struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; +	sdhci_reset(host, mask); +  	if (mask == SDHCI_RESET_ALL) {  		u16 tmp = 0; @@ -88,7 +90,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)  	}  } -static int pxav2_mmc_set_width(struct sdhci_host *host, int width) +static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)  {  	u8 ctrl;  	u16 tmp; @@ -107,14 +109,14 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width)  	}  	writew(tmp, host->ioaddr + SD_CE_ATA_2);  	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); - -	return 0;  }  static const struct sdhci_ops pxav2_sdhci_ops = { +	.set_clock     = sdhci_set_clock,  	.get_max_clock = sdhci_pltfm_clk_get_max_clock, -	.platform_reset_exit = pxav2_set_private_registers, -	.platform_bus_width = pxav2_mmc_set_width, +	.set_bus_width = pxav2_mmc_set_bus_width, +	.reset         = pxav2_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 793dacd3b84..f4f12894756 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -34,6 +34,7 @@  #include <linux/of_gpio.h>  #include <linux/pm.h>  #include <linux/pm_runtime.h> +#include <linux/mbus.h>  #include "sdhci.h"  #include "sdhci-pltfm.h" @@ -57,11 +58,67 @@  #define SDCE_MISC_INT		(1<<2)  #define SDCE_MISC_INT_EN	(1<<1) -static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask) +/* + * These registers are relative to the second register region, for the + * MBus bridge. + */ +#define SDHCI_WINDOW_CTRL(i)	(0x80 + ((i) << 3)) +#define SDHCI_WINDOW_BASE(i)	(0x84 + ((i) << 3)) +#define SDHCI_MAX_WIN_NUM	8 + +static int mv_conf_mbus_windows(struct platform_device *pdev, +				const struct mbus_dram_target_info *dram) +{ +	int i; +	void __iomem *regs; +	struct resource *res; + +	if (!dram) { +		dev_err(&pdev->dev, "no mbus dram info\n"); +		return -EINVAL; +	} + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +	if (!res) { +		dev_err(&pdev->dev, "cannot get mbus registers\n"); +		return -EINVAL; +	} + +	regs = ioremap(res->start, resource_size(res)); +	if (!regs) { +		dev_err(&pdev->dev, "cannot map mbus registers\n"); +		return -ENOMEM; +	} + +	for (i = 0; i < SDHCI_MAX_WIN_NUM; i++) { +		writel(0, regs + SDHCI_WINDOW_CTRL(i)); +		writel(0, regs + SDHCI_WINDOW_BASE(i)); +	} + +	for (i = 0; i < dram->num_cs; i++) { +		const struct mbus_dram_window *cs = dram->cs + i; + +		/* Write size, attributes and target id to control register */ +		writel(((cs->size - 1) & 0xffff0000) | +			(cs->mbus_attr << 8) | +			(dram->mbus_dram_target_id << 4) | 1, +			regs + SDHCI_WINDOW_CTRL(i)); +		/* Write base address to base register */ +		writel(cs->base, regs + SDHCI_WINDOW_BASE(i)); +	} + +	iounmap(regs); + +	return 0; +} + +static void pxav3_reset(struct sdhci_host *host, u8 mask)  {  	struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));  	struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; +	sdhci_reset(host, mask); +  	if (mask == SDHCI_RESET_ALL) {  		/*  		 * tune timing of read data/command when crc error happen @@ -129,7 +186,7 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)  	pxa->power_mode = power_mode;  } -static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) +static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)  {  	u16 ctrl_2; @@ -163,15 +220,16 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)  	dev_dbg(mmc_dev(host->mmc),  		"%s uhs = %d, ctrl_2 = %04X\n",  		__func__, uhs, ctrl_2); - -	return 0;  }  static const struct sdhci_ops pxav3_sdhci_ops = { -	.platform_reset_exit = pxav3_set_private_registers, +	.set_clock = sdhci_set_clock,  	.set_uhs_signaling = pxav3_set_uhs_signaling,  	.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,  	.get_max_clock = sdhci_pltfm_clk_get_max_clock, +	.set_bus_width = sdhci_set_bus_width, +	.reset = pxav3_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  static struct sdhci_pltfm_data sdhci_pxav3_pdata = { @@ -187,6 +245,9 @@ static const struct of_device_id sdhci_pxav3_of_match[] = {  	{  		.compatible = "mrvl,pxav3-mmc",  	}, +	{ +		.compatible = "marvell,armada-380-sdhci", +	},  	{},  };  MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match); @@ -219,6 +280,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)  	struct sdhci_pltfm_host *pltfm_host;  	struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;  	struct device *dev = &pdev->dev; +	struct device_node *np = pdev->dev.of_node;  	struct sdhci_host *host = NULL;  	struct sdhci_pxa *pxa = NULL;  	const struct of_device_id *match; @@ -235,6 +297,14 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)  		kfree(pxa);  		return PTR_ERR(host);  	} + +	if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { +		ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); +		if (ret < 0) +			goto err_mbus_win; +	} + +  	pltfm_host = sdhci_priv(host);  	pltfm_host->priv = pxa; @@ -321,6 +391,7 @@ err_add_host:  	clk_disable_unprepare(clk);  	clk_put(clk);  err_clk_get: +err_mbus_win:  	sdhci_pltfm_free(pdev);  	kfree(pxa);  	return ret; diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 6debda95215..fa5954a0544 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -33,9 +33,6 @@  #define MAX_BUS_CLK	(4) -/* Number of gpio's used is max data bus width + command and clock lines */ -#define NUM_GPIOS(x)	(x + 2) -  /**   * struct sdhci_s3c - S3C SDHCI instance   * @host: The SDHCI host created @@ -51,12 +48,15 @@ struct sdhci_s3c {  	struct platform_device	*pdev;  	struct resource		*ioarea;  	struct s3c_sdhci_platdata *pdata; -	unsigned int		cur_clk; +	int			cur_clk;  	int			ext_cd_irq;  	int			ext_cd_gpio;  	struct clk		*clk_io;  	struct clk		*clk_bus[MAX_BUS_CLK]; +	unsigned long		clk_rates[MAX_BUS_CLK]; + +	bool			no_divider;  };  /** @@ -69,6 +69,7 @@ struct sdhci_s3c {   */  struct sdhci_s3c_drv_data {  	unsigned int	sdhci_quirks; +	bool		no_divider;  };  static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) @@ -77,32 +78,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)  }  /** - * get_curclk - convert ctrl2 register to clock source number - * @ctrl2: Control2 register value. - */ -static u32 get_curclk(u32 ctrl2) -{ -	ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK; -	ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; - -	return ctrl2; -} - -static void sdhci_s3c_check_sclk(struct sdhci_host *host) -{ -	struct sdhci_s3c *ourhost = to_s3c(host); -	u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2); - -	if (get_curclk(tmp) != ourhost->cur_clk) { -		dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n"); - -		tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; -		tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; -		writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2); -	} -} - -/**   * sdhci_s3c_get_max_clk - callback to get maximum clock frequency.   * @host: The SDHCI host instance.   * @@ -111,20 +86,11 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)  static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); -	struct clk *busclk; -	unsigned int rate, max; -	int clk; - -	/* note, a reset will reset the clock source */ - -	sdhci_s3c_check_sclk(host); - -	for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) { -		busclk = ourhost->clk_bus[clk]; -		if (!busclk) -			continue; +	unsigned long rate, max = 0; +	int src; -		rate = clk_get_rate(busclk); +	for (src = 0; src < MAX_BUS_CLK; src++) { +		rate = ourhost->clk_rates[src];  		if (rate > max)  			max = rate;  	} @@ -144,31 +110,38 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,  {  	unsigned long rate;  	struct clk *clksrc = ourhost->clk_bus[src]; -	int div; +	int shift; -	if (!clksrc) +	if (IS_ERR(clksrc))  		return UINT_MAX;  	/*  	 * If controller uses a non-standard clock division, find the best clock  	 * speed possible with selected clock source and skip the division.  	 */ -	if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { +	if (ourhost->no_divider) {  		rate = clk_round_rate(clksrc, wanted);  		return wanted - rate;  	} -	rate = clk_get_rate(clksrc); +	rate = ourhost->clk_rates[src]; -	for (div = 1; div < 256; div *= 2) { -		if ((rate / div) <= wanted) +	for (shift = 0; shift <= 8; ++shift) { +		if ((rate >> shift) <= wanted)  			break;  	} +	if (shift > 8) { +		dev_dbg(&ourhost->pdev->dev, +			"clk %d: rate %ld, min rate %lu > wanted %u\n", +			src, rate, rate / 256, wanted); +		return UINT_MAX; +	} +  	dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n", -		src, rate, wanted, rate / div); +		src, rate, wanted, rate >> shift); -	return wanted - (rate / div); +	return wanted - (rate >> shift);  }  /** @@ -188,9 +161,13 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  	int src;  	u32 ctrl; +	host->mmc->actual_clock = 0; +  	/* don't bother if the clock is going off. */ -	if (clock == 0) +	if (clock == 0) { +		sdhci_set_clock(host, clock);  		return; +	}  	for (src = 0; src < MAX_BUS_CLK; src++) {  		delta = sdhci_s3c_consider_clock(ourhost, src, clock); @@ -209,20 +186,22 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  		struct clk *clk = ourhost->clk_bus[best_src];  		clk_prepare_enable(clk); -		clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); - -		/* turn clock off to card before changing clock source */ -		writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); +		if (ourhost->cur_clk >= 0) +			clk_disable_unprepare( +					ourhost->clk_bus[ourhost->cur_clk]);  		ourhost->cur_clk = best_src; -		host->max_clk = clk_get_rate(clk); - -		ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); -		ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; -		ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; -		writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2); +		host->max_clk = ourhost->clk_rates[best_src];  	} +	/* turn clock off to card before changing clock source */ +	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + +	ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); +	ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; +	ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; +	writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2); +  	/* reprogram default hardware configuration */  	writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,  		host->ioaddr + S3C64XX_SDHCI_CONTROL4); @@ -240,6 +219,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  	if (clock < 25 * 1000000)  		ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2);  	writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3); + +	sdhci_set_clock(host, clock);  }  /** @@ -254,17 +235,17 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); -	unsigned int delta, min = UINT_MAX; +	unsigned long rate, min = ULONG_MAX;  	int src;  	for (src = 0; src < MAX_BUS_CLK; src++) { -		delta = sdhci_s3c_consider_clock(ourhost, src, 0); -		if (delta == UINT_MAX) +		rate = ourhost->clk_rates[src] / 256; +		if (!rate)  			continue; -		/* delta is a negative value in this case */ -		if (-delta < min) -			min = -delta; +		if (rate < min) +			min = rate;  	} +  	return min;  } @@ -272,20 +253,44 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)  static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); +	unsigned long rate, max = 0; +	int src; -	return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX); +	for (src = 0; src < MAX_BUS_CLK; src++) { +		struct clk *clk; + +		clk = ourhost->clk_bus[src]; +		if (IS_ERR(clk)) +			continue; + +		rate = clk_round_rate(clk, ULONG_MAX); +		if (rate > max) +			max = rate; +	} + +	return max;  }  /* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */  static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); +	unsigned long rate, min = ULONG_MAX; +	int src; -	/* -	 * initial clock can be in the frequency range of -	 * 100KHz-400KHz, so we set it as max value. -	 */ -	return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000); +	for (src = 0; src < MAX_BUS_CLK; src++) { +		struct clk *clk; + +		clk = ourhost->clk_bus[src]; +		if (IS_ERR(clk)) +			continue; + +		rate = clk_round_rate(clk, 0); +		if (rate < min) +			min = rate; +	} + +	return min;  }  /* sdhci_cmu_set_clock - callback on clock change.*/ @@ -296,10 +301,11 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)  	unsigned long timeout;  	u16 clk = 0; +	host->mmc->actual_clock = 0; +  	/* If the clock is going off, set to 0 at clock control register */  	if (clock == 0) {  		sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); -		host->clock = clock;  		return;  	} @@ -307,8 +313,6 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)  	clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); -	host->clock = clock; -  	clk = SDHCI_CLOCK_INT_EN;  	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); @@ -330,14 +334,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)  }  /** - * sdhci_s3c_platform_bus_width - support 8bit buswidth + * sdhci_s3c_set_bus_width - support 8bit buswidth   * @host: The SDHCI host being queried   * @width: MMC_BUS_WIDTH_ macro for the bus width being requested   *   * We have 8-bit width support but is not a v3 controller.   * So we add platform_bus_width() and support 8bit width.   */ -static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) +static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width)  {  	u8 ctrl; @@ -359,93 +363,23 @@ static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width)  	}  	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - -	return 0;  }  static struct sdhci_ops sdhci_s3c_ops = {  	.get_max_clock		= sdhci_s3c_get_max_clk,  	.set_clock		= sdhci_s3c_set_clock,  	.get_min_clock		= sdhci_s3c_get_min_clock, -	.platform_bus_width	= sdhci_s3c_platform_bus_width, +	.set_bus_width		= sdhci_s3c_set_bus_width, +	.reset			= sdhci_reset, +	.set_uhs_signaling	= sdhci_set_uhs_signaling,  }; -static void sdhci_s3c_notify_change(struct platform_device *dev, int state) -{ -	struct sdhci_host *host = platform_get_drvdata(dev); -#ifdef CONFIG_PM_RUNTIME -	struct sdhci_s3c *sc = sdhci_priv(host); -#endif -	unsigned long flags; - -	if (host) { -		spin_lock_irqsave(&host->lock, flags); -		if (state) { -			dev_dbg(&dev->dev, "card inserted.\n"); -#ifdef CONFIG_PM_RUNTIME -			clk_prepare_enable(sc->clk_io); -#endif -			host->flags &= ~SDHCI_DEVICE_DEAD; -			host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; -		} else { -			dev_dbg(&dev->dev, "card removed.\n"); -			host->flags |= SDHCI_DEVICE_DEAD; -			host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; -#ifdef CONFIG_PM_RUNTIME -			clk_disable_unprepare(sc->clk_io); -#endif -		} -		tasklet_schedule(&host->card_tasklet); -		spin_unlock_irqrestore(&host->lock, flags); -	} -} - -static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) -{ -	struct sdhci_s3c *sc = dev_id; -	int status = gpio_get_value(sc->ext_cd_gpio); -	if (sc->pdata->ext_cd_gpio_invert) -		status = !status; -	sdhci_s3c_notify_change(sc->pdev, status); -	return IRQ_HANDLED; -} - -static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) -{ -	struct s3c_sdhci_platdata *pdata = sc->pdata; -	struct device *dev = &sc->pdev->dev; - -	if (devm_gpio_request(dev, pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { -		sc->ext_cd_gpio = pdata->ext_cd_gpio; -		sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); -		if (sc->ext_cd_irq && -		    request_threaded_irq(sc->ext_cd_irq, NULL, -					 sdhci_s3c_gpio_card_detect_thread, -					 IRQF_TRIGGER_RISING | -					 IRQF_TRIGGER_FALLING | -					 IRQF_ONESHOT, -					 dev_name(dev), sc) == 0) { -			int status = gpio_get_value(sc->ext_cd_gpio); -			if (pdata->ext_cd_gpio_invert) -				status = !status; -			sdhci_s3c_notify_change(sc->pdev, status); -		} else { -			dev_warn(dev, "cannot request irq for card detect\n"); -			sc->ext_cd_irq = 0; -		} -	} else { -		dev_err(dev, "cannot request gpio for card detect\n"); -	} -} -  #ifdef CONFIG_OF  static int sdhci_s3c_parse_dt(struct device *dev,  		struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)  {  	struct device_node *node = dev->of_node; -	struct sdhci_s3c *ourhost = to_s3c(host);  	u32 max_width; -	int gpio;  	/* if the bus-width property is not specified, assume width as 1 */  	if (of_property_read_u32(node, "bus-width", &max_width)) @@ -463,18 +397,8 @@ static int sdhci_s3c_parse_dt(struct device *dev,  		return 0;  	} -	gpio = of_get_named_gpio(node, "cd-gpios", 0); -	if (gpio_is_valid(gpio)) { -		pdata->cd_type = S3C_SDHCI_CD_GPIO; -		pdata->ext_cd_gpio = gpio; -		ourhost->ext_cd_gpio = -1; -		if (of_get_property(node, "cd-inverted", NULL)) -			pdata->ext_cd_gpio_invert = 1; +	if (of_get_named_gpio(node, "cd-gpios", 0))  		return 0; -	} else if (gpio != -ENOENT) { -		dev_err(dev, "invalid card detect gpio specified\n"); -		return -EINVAL; -	}  	/* assuming internal card detect that will be configured by pinctrl */  	pdata->cd_type = S3C_SDHCI_CD_INTERNAL; @@ -552,6 +476,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	sc->host = host;  	sc->pdev = pdev;  	sc->pdata = pdata; +	sc->cur_clk = -1;  	platform_set_drvdata(pdev, host); @@ -566,25 +491,18 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	clk_prepare_enable(sc->clk_io);  	for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { -		struct clk *clk;  		char name[14];  		snprintf(name, 14, "mmc_busclk.%d", ptr); -		clk = devm_clk_get(dev, name); -		if (IS_ERR(clk)) +		sc->clk_bus[ptr] = devm_clk_get(dev, name); +		if (IS_ERR(sc->clk_bus[ptr]))  			continue;  		clks++; -		sc->clk_bus[ptr] = clk; - -		/* -		 * save current clock index to know which clock bus -		 * is used later in overriding functions. -		 */ -		sc->cur_clk = ptr; +		sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);  		dev_info(dev, "clock source %d: %s (%ld Hz)\n", -			 ptr, name, clk_get_rate(clk)); +				ptr, name, sc->clk_rates[ptr]);  	}  	if (clks == 0) { @@ -593,10 +511,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  		goto err_no_busclks;  	} -#ifndef CONFIG_PM_RUNTIME -	clk_prepare_enable(sc->clk_bus[sc->cur_clk]); -#endif -  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	host->ioaddr = devm_ioremap_resource(&pdev->dev, res);  	if (IS_ERR(host->ioaddr)) { @@ -617,8 +531,10 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	/* Setup quirks for the controller */  	host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;  	host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; -	if (drv_data) +	if (drv_data) {  		host->quirks |= drv_data->sdhci_quirks; +		sc->no_divider = drv_data->no_divider; +	}  #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -667,7 +583,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	 * If controller does not have internal clock divider,  	 * we can use overriding functions instead of default.  	 */ -	if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { +	if (sc->no_divider) {  		sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;  		sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;  		sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; @@ -685,6 +601,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	pm_runtime_use_autosuspend(&pdev->dev);  	pm_suspend_ignore_children(&pdev->dev, 1); +	mmc_of_parse(host->mmc); +  	ret = sdhci_add_host(host);  	if (ret) {  		dev_err(dev, "sdhci_add_host() failed\n"); @@ -693,15 +611,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  		goto err_req_regs;  	} -	/* The following two methods of card detection might call -	   sdhci_s3c_notify_change() immediately, so they can be called -	   only after sdhci_add_host(). Setup errors are ignored. */ -	if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) -		pdata->ext_cd_init(&sdhci_s3c_notify_change); -	if (pdata->cd_type == S3C_SDHCI_CD_GPIO && -	    gpio_is_valid(pdata->ext_cd_gpio)) -		sdhci_s3c_setup_card_detect_gpio(sc); -  #ifdef CONFIG_PM_RUNTIME  	if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)  		clk_disable_unprepare(sc->clk_io); @@ -709,10 +618,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	return 0;   err_req_regs: -#ifndef CONFIG_PM_RUNTIME -	clk_disable_unprepare(sc->clk_bus[sc->cur_clk]); -#endif -   err_no_busclks:  	clk_disable_unprepare(sc->clk_io); @@ -726,16 +631,12 @@ static int sdhci_s3c_remove(struct platform_device *pdev)  {  	struct sdhci_host *host =  platform_get_drvdata(pdev);  	struct sdhci_s3c *sc = sdhci_priv(host); -	struct s3c_sdhci_platdata *pdata = sc->pdata; - -	if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) -		pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);  	if (sc->ext_cd_irq)  		free_irq(sc->ext_cd_irq, sc);  #ifdef CONFIG_PM_RUNTIME -	if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) +	if (sc->pdata->cd_type != S3C_SDHCI_CD_INTERNAL)  		clk_prepare_enable(sc->clk_io);  #endif  	sdhci_remove_host(host, 1); @@ -743,9 +644,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)  	pm_runtime_dont_use_autosuspend(&pdev->dev);  	pm_runtime_disable(&pdev->dev); -#ifndef CONFIG_PM_RUNTIME -	clk_disable_unprepare(sc->clk_bus[sc->cur_clk]); -#endif  	clk_disable_unprepare(sc->clk_io);  	sdhci_free_host(host); @@ -779,7 +677,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)  	ret = sdhci_runtime_suspend_host(host); -	clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); +	if (ourhost->cur_clk >= 0) +		clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);  	clk_disable_unprepare(busclk);  	return ret;  } @@ -792,7 +691,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)  	int ret;  	clk_prepare_enable(busclk); -	clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]); +	if (ourhost->cur_clk >= 0) +		clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);  	ret = sdhci_runtime_resume_host(host);  	return ret;  } @@ -813,7 +713,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = {  #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)  static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { -	.sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, +	.no_divider = true,  };  #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)  #else diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 696122c1b46..17004531d08 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -28,7 +28,11 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)  }  static struct sdhci_ops sdhci_sirf_ops = { +	.set_clock = sdhci_set_clock,  	.get_max_clock	= sdhci_sirf_get_max_clk, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  };  static struct sdhci_pltfm_data sdhci_sirf_pdata = { diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 2dba9f8d176..9d535c7336e 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -27,6 +27,7 @@  #include <linux/slab.h>  #include <linux/mmc/host.h>  #include <linux/mmc/sdhci-spear.h> +#include <linux/mmc/slot-gpio.h>  #include <linux/io.h>  #include "sdhci.h" @@ -37,39 +38,12 @@ struct spear_sdhci {  /* sdhci ops */  static const struct sdhci_ops sdhci_pltfm_ops = { -	/* Nothing to do for now. */ +	.set_clock = sdhci_set_clock, +	.set_bus_width = sdhci_set_bus_width, +	.reset = sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling,  }; -/* gpio card detection interrupt handler */ -static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id) -{ -	struct platform_device *pdev = dev_id; -	struct sdhci_host *host = platform_get_drvdata(pdev); -	struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); -	unsigned long gpio_irq_type; -	int val; - -	val = gpio_get_value(sdhci->data->card_int_gpio); - -	/* val == 1 -> card removed, val == 0 -> card inserted */ -	/* if card removed - set irq for low level, else vice versa */ -	gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH; -	irq_set_irq_type(irq, gpio_irq_type); - -	if (sdhci->data->card_power_gpio >= 0) { -		if (!sdhci->data->power_always_enb) { -			/* if card inserted, give power, otherwise remove it */ -			val = sdhci->data->power_active_high ? !val : val ; -			gpio_set_value(sdhci->data->card_power_gpio, val); -		} -	} - -	/* inform sdhci driver about card insertion/removal */ -	tasklet_schedule(&host->card_tasklet); - -	return IRQ_HANDLED; -} -  #ifdef CONFIG_OF  static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)  { @@ -84,14 +58,12 @@ static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pde  	/* If pdata is required */  	if (cd_gpio != -1) {  		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); -		if (!pdata) { +		if (!pdata)  			dev_err(&pdev->dev, "DT: kzalloc failed\n"); -			return ERR_PTR(-ENOMEM); -		} +		else +			pdata->card_int_gpio = cd_gpio;  	} -	pdata->card_int_gpio = cd_gpio; -  	return pdata;  }  #else @@ -107,41 +79,44 @@ static int sdhci_probe(struct platform_device *pdev)  	struct sdhci_host *host;  	struct resource *iomem;  	struct spear_sdhci *sdhci; +	struct device *dev;  	int ret; -	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!iomem) { -		ret = -ENOMEM; -		dev_dbg(&pdev->dev, "memory resource not defined\n"); +	dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev; +	host = sdhci_alloc_host(dev, sizeof(*sdhci)); +	if (IS_ERR(host)) { +		ret = PTR_ERR(host); +		dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");  		goto err;  	} -	if (!devm_request_mem_region(&pdev->dev, iomem->start, -				resource_size(iomem), "spear-sdhci")) { -		ret = -EBUSY; -		dev_dbg(&pdev->dev, "cannot request region\n"); -		goto err; +	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem); +	if (IS_ERR(host->ioaddr)) { +		ret = PTR_ERR(host->ioaddr); +		dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret); +		goto err_host;  	} -	sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL); -	if (!sdhci) { -		ret = -ENOMEM; -		dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); -		goto err; -	} +	host->hw_name = "sdhci"; +	host->ops = &sdhci_pltfm_ops; +	host->irq = platform_get_irq(pdev, 0); +	host->quirks = SDHCI_QUIRK_BROKEN_ADMA; + +	sdhci = sdhci_priv(host);  	/* clk enable */ -	sdhci->clk = clk_get(&pdev->dev, NULL); +	sdhci->clk = devm_clk_get(&pdev->dev, NULL);  	if (IS_ERR(sdhci->clk)) {  		ret = PTR_ERR(sdhci->clk);  		dev_dbg(&pdev->dev, "Error getting clock\n"); -		goto err; +		goto err_host;  	}  	ret = clk_prepare_enable(sdhci->clk);  	if (ret) {  		dev_dbg(&pdev->dev, "Error enabling clock\n"); -		goto put_clk; +		goto err_host;  	}  	ret = clk_set_rate(sdhci->clk, 50000000); @@ -153,118 +128,42 @@ static int sdhci_probe(struct platform_device *pdev)  		sdhci->data = sdhci_probe_config_dt(pdev);  		if (IS_ERR(sdhci->data)) {  			dev_err(&pdev->dev, "DT: Failed to get pdata\n"); -			return -ENODEV; +			goto disable_clk;  		}  	} else {  		sdhci->data = dev_get_platdata(&pdev->dev);  	} -	pdev->dev.platform_data = sdhci; - -	if (pdev->dev.parent) -		host = sdhci_alloc_host(pdev->dev.parent, 0); -	else -		host = sdhci_alloc_host(&pdev->dev, 0); - -	if (IS_ERR(host)) { -		ret = PTR_ERR(host); -		dev_dbg(&pdev->dev, "error allocating host\n"); -		goto disable_clk; -	} - -	host->hw_name = "sdhci"; -	host->ops = &sdhci_pltfm_ops; -	host->irq = platform_get_irq(pdev, 0); -	host->quirks = SDHCI_QUIRK_BROKEN_ADMA; - -	host->ioaddr = devm_ioremap(&pdev->dev, iomem->start, -			resource_size(iomem)); -	if (!host->ioaddr) { -		ret = -ENOMEM; -		dev_dbg(&pdev->dev, "failed to remap registers\n"); -		goto free_host; +	/* +	 * It is optional to use GPIOs for sdhci card detection. If +	 * sdhci->data is NULL, then use original sdhci lines otherwise +	 * GPIO lines. We use the built-in GPIO support for this. +	 */ +	if (sdhci->data && sdhci->data->card_int_gpio >= 0) { +		ret = mmc_gpio_request_cd(host->mmc, +					  sdhci->data->card_int_gpio, 0); +		if (ret < 0) { +			dev_dbg(&pdev->dev, +				"failed to request card-detect gpio%d\n", +				sdhci->data->card_int_gpio); +			goto disable_clk; +		}  	}  	ret = sdhci_add_host(host);  	if (ret) {  		dev_dbg(&pdev->dev, "error adding host\n"); -		goto free_host; +		goto disable_clk;  	}  	platform_set_drvdata(pdev, host); -	/* -	 * It is optional to use GPIOs for sdhci Power control & sdhci card -	 * interrupt detection. If sdhci->data is NULL, then use original sdhci -	 * lines otherwise GPIO lines. -	 * If GPIO is selected for power control, then power should be disabled -	 * after card removal and should be enabled when card insertion -	 * interrupt occurs -	 */ -	if (!sdhci->data) -		return 0; - -	if (sdhci->data->card_power_gpio >= 0) { -		int val = 0; - -		ret = devm_gpio_request(&pdev->dev, -				sdhci->data->card_power_gpio, "sdhci"); -		if (ret < 0) { -			dev_dbg(&pdev->dev, "gpio request fail: %d\n", -					sdhci->data->card_power_gpio); -			goto set_drvdata; -		} - -		if (sdhci->data->power_always_enb) -			val = sdhci->data->power_active_high; -		else -			val = !sdhci->data->power_active_high; - -		ret = gpio_direction_output(sdhci->data->card_power_gpio, val); -		if (ret) { -			dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", -					sdhci->data->card_power_gpio); -			goto set_drvdata; -		} -	} - -	if (sdhci->data->card_int_gpio >= 0) { -		ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio, -				"sdhci"); -		if (ret < 0) { -			dev_dbg(&pdev->dev, "gpio request fail: %d\n", -					sdhci->data->card_int_gpio); -			goto set_drvdata; -		} - -		ret = gpio_direction_input(sdhci->data->card_int_gpio); -		if (ret) { -			dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", -					sdhci->data->card_int_gpio); -			goto set_drvdata; -		} -		ret = devm_request_irq(&pdev->dev, -				gpio_to_irq(sdhci->data->card_int_gpio), -				sdhci_gpio_irq, IRQF_TRIGGER_LOW, -				mmc_hostname(host->mmc), pdev); -		if (ret) { -			dev_dbg(&pdev->dev, "gpio request irq fail: %d\n", -					sdhci->data->card_int_gpio); -			goto set_drvdata; -		} - -	} -  	return 0; -set_drvdata: -	sdhci_remove_host(host, 1); -free_host: -	sdhci_free_host(host);  disable_clk:  	clk_disable_unprepare(sdhci->clk); -put_clk: -	clk_put(sdhci->clk); +err_host: +	sdhci_free_host(host);  err:  	dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);  	return ret; @@ -273,7 +172,7 @@ err:  static int sdhci_remove(struct platform_device *pdev)  {  	struct sdhci_host *host = platform_get_drvdata(pdev); -	struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); +	struct spear_sdhci *sdhci = sdhci_priv(host);  	int dead = 0;  	u32 scratch; @@ -282,9 +181,8 @@ static int sdhci_remove(struct platform_device *pdev)  		dead = 1;  	sdhci_remove_host(host, dead); -	sdhci_free_host(host);  	clk_disable_unprepare(sdhci->clk); -	clk_put(sdhci->clk); +	sdhci_free_host(host);  	return 0;  } @@ -293,7 +191,7 @@ static int sdhci_remove(struct platform_device *pdev)  static int sdhci_suspend(struct device *dev)  {  	struct sdhci_host *host = dev_get_drvdata(dev); -	struct spear_sdhci *sdhci = dev_get_platdata(dev); +	struct spear_sdhci *sdhci = sdhci_priv(host);  	int ret;  	ret = sdhci_suspend_host(host); @@ -306,7 +204,7 @@ static int sdhci_suspend(struct device *dev)  static int sdhci_resume(struct device *dev)  {  	struct sdhci_host *host = dev_get_drvdata(dev); -	struct spear_sdhci *sdhci = dev_get_platdata(dev); +	struct spear_sdhci *sdhci = sdhci_priv(host);  	int ret;  	ret = clk_enable(sdhci->clk); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 5b7b2eba8a5..d93a063a36f 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -32,11 +32,17 @@  /* Tegra SDHOST controller vendor register definitions */  #define SDHCI_TEGRA_VENDOR_MISC_CTRL		0x120 +#define SDHCI_MISC_CTRL_ENABLE_SDR104		0x8 +#define SDHCI_MISC_CTRL_ENABLE_SDR50		0x10  #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300	0x20 +#define SDHCI_MISC_CTRL_ENABLE_DDR50		0x200  #define NVQUIRK_FORCE_SDHCI_SPEC_200	BIT(0)  #define NVQUIRK_ENABLE_BLOCK_GAP_DET	BIT(1)  #define NVQUIRK_ENABLE_SDHCI_SPEC_300	BIT(2) +#define NVQUIRK_DISABLE_SDR50		BIT(3) +#define NVQUIRK_DISABLE_SDR104		BIT(4) +#define NVQUIRK_DISABLE_DDR50		BIT(5)  struct sdhci_tegra_soc_data {  	const struct sdhci_pltfm_data *pdata; @@ -48,19 +54,6 @@ struct sdhci_tegra {  	int power_gpio;  }; -static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) -{ -	u32 val; - -	if (unlikely(reg == SDHCI_PRESENT_STATE)) { -		/* Use wp_gpio here instead? */ -		val = readl(host->ioaddr + reg); -		return val | SDHCI_WRITE_PROTECT; -	} - -	return readl(host->ioaddr + reg); -} -  static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)  {  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -108,26 +101,33 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)  	return mmc_gpio_get_ro(host->mmc);  } -static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) +static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)  {  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);  	struct sdhci_tegra *tegra_host = pltfm_host->priv;  	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; +	u32 misc_ctrl; + +	sdhci_reset(host, mask);  	if (!(mask & SDHCI_RESET_ALL))  		return; +	misc_ctrl = sdhci_readw(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);  	/* Erratum: Enable SDHCI spec v3.00 support */ -	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) { -		u32 misc_ctrl; - -		misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); +	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)  		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; -		sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); -	} +	/* Don't advertise UHS modes which aren't supported yet */ +	if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR50) +		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50; +	if (soc_data->nvquirks & NVQUIRK_DISABLE_DDR50) +		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50; +	if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104) +		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; +	sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);  } -static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) +static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)  {  	u32 ctrl; @@ -144,23 +144,25 @@ static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)  			ctrl &= ~SDHCI_CTRL_4BITBUS;  	}  	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); -	return 0;  }  static const struct sdhci_ops tegra_sdhci_ops = {  	.get_ro     = tegra_sdhci_get_ro, -	.read_l     = tegra_sdhci_readl,  	.read_w     = tegra_sdhci_readw,  	.write_l    = tegra_sdhci_writel, -	.platform_bus_width = tegra_sdhci_buswidth, -	.platform_reset_exit = tegra_sdhci_reset_exit, +	.set_clock  = sdhci_set_clock, +	.set_bus_width = tegra_sdhci_set_bus_width, +	.reset      = tegra_sdhci_reset, +	.set_uhs_signaling = sdhci_set_uhs_signaling, +	.get_max_clock = sdhci_pltfm_clk_get_max_clock,  };  static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {  	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |  		  SDHCI_QUIRK_SINGLE_POWER_WRITE |  		  SDHCI_QUIRK_NO_HISPD_BIT | -		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, +		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | +		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,  	.ops  = &tegra_sdhci_ops,  }; @@ -175,13 +177,16 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {  		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |  		  SDHCI_QUIRK_SINGLE_POWER_WRITE |  		  SDHCI_QUIRK_NO_HISPD_BIT | -		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, +		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | +		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,  	.ops  = &tegra_sdhci_ops,  };  static struct sdhci_tegra_soc_data soc_data_tegra30 = {  	.pdata = &sdhci_tegra30_pdata, -	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300, +	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | +		    NVQUIRK_DISABLE_SDR50 | +		    NVQUIRK_DISABLE_SDR104,  };  static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { @@ -189,15 +194,20 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {  		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |  		  SDHCI_QUIRK_SINGLE_POWER_WRITE |  		  SDHCI_QUIRK_NO_HISPD_BIT | -		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, +		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | +		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,  	.ops  = &tegra_sdhci_ops,  };  static struct sdhci_tegra_soc_data soc_data_tegra114 = {  	.pdata = &sdhci_tegra114_pdata, +	.nvquirks = NVQUIRK_DISABLE_SDR50 | +		    NVQUIRK_DISABLE_DDR50 | +		    NVQUIRK_DISABLE_SDR104,  };  static const struct of_device_id sdhci_tegra_dt_match[] = { +	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },  	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },  	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },  	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 7a7fb4f0d5a..47055f3f01b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -44,12 +44,13 @@  #define MAX_TUNING_LOOP 40 +#define ADMA_SIZE	((128 * 2 + 1) * 4) +  static unsigned int debug_quirks = 0;  static unsigned int debug_quirks2;  static void sdhci_finish_data(struct sdhci_host *); -static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);  static void sdhci_finish_command(struct sdhci_host *);  static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);  static void sdhci_tuning_timer(unsigned long data); @@ -132,43 +133,26 @@ static void sdhci_dumpregs(struct sdhci_host *host)   *                                                                           *  \*****************************************************************************/ -static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) -{ -	u32 ier; - -	ier = sdhci_readl(host, SDHCI_INT_ENABLE); -	ier &= ~clear; -	ier |= set; -	sdhci_writel(host, ier, SDHCI_INT_ENABLE); -	sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); -} - -static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs) -{ -	sdhci_clear_set_irqs(host, 0, irqs); -} - -static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs) -{ -	sdhci_clear_set_irqs(host, irqs, 0); -} -  static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)  { -	u32 present, irqs; +	u32 present;  	if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||  	    (host->mmc->caps & MMC_CAP_NONREMOVABLE))  		return; -	present = sdhci_readl(host, SDHCI_PRESENT_STATE) & -			      SDHCI_CARD_PRESENT; -	irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; +	if (enable) { +		present = sdhci_readl(host, SDHCI_PRESENT_STATE) & +				      SDHCI_CARD_PRESENT; -	if (enable) -		sdhci_unmask_irqs(host, irqs); -	else -		sdhci_mask_irqs(host, irqs); +		host->ier |= present ? SDHCI_INT_CARD_REMOVE : +				       SDHCI_INT_CARD_INSERT; +	} else { +		host->ier &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); +	} + +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);  }  static void sdhci_enable_card_detection(struct sdhci_host *host) @@ -181,22 +165,9 @@ static void sdhci_disable_card_detection(struct sdhci_host *host)  	sdhci_set_card_detection(host, false);  } -static void sdhci_reset(struct sdhci_host *host, u8 mask) +void sdhci_reset(struct sdhci_host *host, u8 mask)  {  	unsigned long timeout; -	u32 uninitialized_var(ier); - -	if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { -		if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & -			SDHCI_CARD_PRESENT)) -			return; -	} - -	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) -		ier = sdhci_readl(host, SDHCI_INT_ENABLE); - -	if (host->ops->platform_reset_enter) -		host->ops->platform_reset_enter(host, mask);  	sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); @@ -221,16 +192,27 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)  		timeout--;  		mdelay(1);  	} +} +EXPORT_SYMBOL_GPL(sdhci_reset); -	if (host->ops->platform_reset_exit) -		host->ops->platform_reset_exit(host, mask); +static void sdhci_do_reset(struct sdhci_host *host, u8 mask) +{ +	if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { +		if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & +			SDHCI_CARD_PRESENT)) +			return; +	} -	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) -		sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); +	host->ops->reset(host, mask); -	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { -		if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) -			host->ops->enable_dma(host); +	if (mask & SDHCI_RESET_ALL) { +		if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { +			if (host->ops->enable_dma) +				host->ops->enable_dma(host); +		} + +		/* Resetting the controller clears many */ +		host->preset_enabled = false;  	}  } @@ -239,15 +221,18 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);  static void sdhci_init(struct sdhci_host *host, int soft)  {  	if (soft) -		sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); +		sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);  	else -		sdhci_reset(host, SDHCI_RESET_ALL); +		sdhci_do_reset(host, SDHCI_RESET_ALL); + +	host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | +		    SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | +		    SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | +		    SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | +		    SDHCI_INT_RESPONSE; -	sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, -		SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | -		SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | -		SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | -		SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE); +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);  	if (soft) {  		/* force clock reconfiguration */ @@ -503,11 +488,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,  	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(mmc_dev(host->mmc), host->align_addr)) @@ -568,7 +548,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,  		 * If this triggers then we have a calculation bug  		 * somewhere. :/  		 */ -		WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4); +		WARN_ON((desc - host->adma_desc) > ADMA_SIZE);  	}  	if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { @@ -596,17 +576,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,  			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(mmc_dev(host->mmc), host->adma_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); @@ -624,19 +595,25 @@ static void sdhci_adma_table_post(struct sdhci_host *host,  	u8 *align;  	char *buffer;  	unsigned long flags; +	bool has_unaligned;  	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) { +	/* Do a quick scan of the SG list for any unaligned mappings */ +	has_unaligned = false; +	for_each_sg(data->sg, sg, host->sg_count, i) +		if (sg_dma_address(sg) & 3) { +			has_unaligned = true; +			break; +		} + +	if (has_unaligned && data->flags & MMC_DATA_READ) {  		dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,  			data->sg_len, direction); @@ -676,12 +653,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)  		return 0xE;  	/* Unspecified timeout, assume max */ -	if (!data && !cmd->cmd_timeout_ms) +	if (!data && !cmd->busy_timeout)  		return 0xE;  	/* timeout in us */  	if (!data) -		target_timeout = cmd->cmd_timeout_ms * 1000; +		target_timeout = cmd->busy_timeout * 1000;  	else {  		target_timeout = data->timeout_ns / 1000;  		if (host->clock) @@ -722,9 +699,12 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)  	u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;  	if (host->flags & SDHCI_REQ_USE_DMA) -		sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); +		host->ier = (host->ier & ~pio_irqs) | dma_irqs;  	else -		sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); +		host->ier = (host->ier & ~dma_irqs) | pio_irqs; + +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);  }  static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) @@ -899,8 +879,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,  	u16 mode;  	struct mmc_data *data = cmd->data; -	if (data == NULL) +	if (data == NULL) { +		/* clear Auto CMD settings for no data CMDs */ +		mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); +		sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | +				SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);  		return; +	}  	WARN_ON(!host->data); @@ -972,8 +957,8 @@ static void sdhci_finish_data(struct sdhci_host *host)  		 * upon error conditions.  		 */  		if (data->error) { -			sdhci_reset(host, SDHCI_RESET_CMD); -			sdhci_reset(host, SDHCI_RESET_DATA); +			sdhci_do_reset(host, SDHCI_RESET_CMD); +			sdhci_do_reset(host, SDHCI_RESET_DATA);  		}  		sdhci_send_command(host, data->stop); @@ -981,7 +966,7 @@ static void sdhci_finish_data(struct sdhci_host *host)  		tasklet_schedule(&host->finish_tasklet);  } -static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) +void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)  {  	int flags;  	u32 mask; @@ -1014,7 +999,12 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)  		mdelay(1);  	} -	mod_timer(&host->timer, jiffies + 10 * HZ); +	timeout = jiffies; +	if (!cmd->data && cmd->busy_timeout > 9000) +		timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; +	else +		timeout += 10 * HZ; +	mod_timer(&host->timer, timeout);  	host->cmd = cmd; @@ -1053,6 +1043,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)  	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);  } +EXPORT_SYMBOL_GPL(sdhci_send_command);  static void sdhci_finish_command(struct sdhci_host *host)  { @@ -1097,24 +1088,23 @@ static void sdhci_finish_command(struct sdhci_host *host)  static u16 sdhci_get_preset_value(struct sdhci_host *host)  { -	u16 ctrl, preset = 0; - -	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +	u16 preset = 0; -	switch (ctrl & SDHCI_CTRL_UHS_MASK) { -	case SDHCI_CTRL_UHS_SDR12: +	switch (host->timing) { +	case MMC_TIMING_UHS_SDR12:  		preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12);  		break; -	case SDHCI_CTRL_UHS_SDR25: +	case MMC_TIMING_UHS_SDR25:  		preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25);  		break; -	case SDHCI_CTRL_UHS_SDR50: +	case MMC_TIMING_UHS_SDR50:  		preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50);  		break; -	case SDHCI_CTRL_UHS_SDR104: +	case MMC_TIMING_UHS_SDR104: +	case MMC_TIMING_MMC_HS200:  		preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104);  		break; -	case SDHCI_CTRL_UHS_DDR50: +	case MMC_TIMING_UHS_DDR50:  		preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);  		break;  	default: @@ -1126,32 +1116,22 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)  	return preset;  } -static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)  {  	int div = 0; /* Initialized for compiler warning */  	int real_div = div, clk_mul = 1;  	u16 clk = 0;  	unsigned long timeout; -	if (clock && clock == host->clock) -		return; -  	host->mmc->actual_clock = 0; -	if (host->ops->set_clock) { -		host->ops->set_clock(host, clock); -		if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) -			return; -	} -  	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);  	if (clock == 0) -		goto out; +		return;  	if (host->version >= SDHCI_SPEC_300) { -		if (sdhci_readw(host, SDHCI_HOST_CONTROL2) & -			SDHCI_CTRL_PRESET_VAL_ENABLE) { +		if (host->preset_enabled) {  			u16 pre_val;  			clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); @@ -1237,26 +1217,16 @@ clock_set:  	clk |= SDHCI_CLOCK_CARD_EN;  	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - -out: -	host->clock = clock; -} - -static inline void sdhci_update_clock(struct sdhci_host *host) -{ -	unsigned int clock; - -	clock = host->clock; -	host->clock = 0; -	sdhci_set_clock(host, clock);  } +EXPORT_SYMBOL_GPL(sdhci_set_clock); -static int sdhci_set_power(struct sdhci_host *host, unsigned short power) +static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, +			    unsigned short vdd)  {  	u8 pwr = 0; -	if (power != (unsigned short)-1) { -		switch (1 << power) { +	if (mode != MMC_POWER_OFF) { +		switch (1 << vdd) {  		case MMC_VDD_165_195:  			pwr = SDHCI_POWER_180;  			break; @@ -1274,7 +1244,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)  	}  	if (host->pwr == pwr) -		return -1; +		return;  	host->pwr = pwr; @@ -1282,38 +1252,43 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)  		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);  		if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)  			sdhci_runtime_pm_bus_off(host); -		return 0; -	} - -	/* -	 * 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->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) -		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); +		vdd = 0; +	} else { +		/* +		 * 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->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) +			sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); -	/* -	 * 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->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) -		sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); +		/* +		 * 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->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) +			sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); -	pwr |= SDHCI_POWER_ON; +		pwr |= SDHCI_POWER_ON; -	sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); +		sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); -	if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) -		sdhci_runtime_pm_bus_on(host); +		if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) +			sdhci_runtime_pm_bus_on(host); -	/* -	 * Some controllers need an extra 10ms delay of 10ms before they -	 * can apply clock after applying power -	 */ -	if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) -		mdelay(10); +		/* +		 * Some controllers need an extra 10ms delay of 10ms before +		 * they can apply clock after applying power +		 */ +		if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) +			mdelay(10); +	} -	return power; +	if (host->vmmc) { +		spin_unlock_irq(&host->lock); +		mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd); +		spin_lock_irq(&host->lock); +	}  }  /*****************************************************************************\ @@ -1391,6 +1366,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)  					mmc->card->type == MMC_TYPE_MMC ?  					MMC_SEND_TUNING_BLOCK_HS200 :  					MMC_SEND_TUNING_BLOCK; + +				/* Here we need to set the host->mrq to NULL, +				 * in case the pending finish_tasklet +				 * finishes it incorrectly. +				 */ +				host->mrq = NULL; +  				spin_unlock_irqrestore(&host->lock, flags);  				sdhci_execute_tuning(mmc, tuning_opcode);  				spin_lock_irqsave(&host->lock, flags); @@ -1410,10 +1392,53 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)  	spin_unlock_irqrestore(&host->lock, flags);  } +void sdhci_set_bus_width(struct sdhci_host *host, int width) +{ +	u8 ctrl; + +	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); +	if (width == MMC_BUS_WIDTH_8) { +		ctrl &= ~SDHCI_CTRL_4BITBUS; +		if (host->version >= SDHCI_SPEC_300) +			ctrl |= SDHCI_CTRL_8BITBUS; +	} else { +		if (host->version >= SDHCI_SPEC_300) +			ctrl &= ~SDHCI_CTRL_8BITBUS; +		if (width == MMC_BUS_WIDTH_4) +			ctrl |= SDHCI_CTRL_4BITBUS; +		else +			ctrl &= ~SDHCI_CTRL_4BITBUS; +	} +	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} +EXPORT_SYMBOL_GPL(sdhci_set_bus_width); + +void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) +{ +	u16 ctrl_2; + +	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); +	/* Select Bus Speed Mode for host */ +	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; +	if ((timing == MMC_TIMING_MMC_HS200) || +	    (timing == MMC_TIMING_UHS_SDR104)) +		ctrl_2 |= SDHCI_CTRL_UHS_SDR104; +	else if (timing == MMC_TIMING_UHS_SDR12) +		ctrl_2 |= SDHCI_CTRL_UHS_SDR12; +	else if (timing == MMC_TIMING_UHS_SDR25) +		ctrl_2 |= SDHCI_CTRL_UHS_SDR25; +	else if (timing == MMC_TIMING_UHS_SDR50) +		ctrl_2 |= SDHCI_CTRL_UHS_SDR50; +	else if ((timing == MMC_TIMING_UHS_DDR50) || +		 (timing == MMC_TIMING_MMC_DDR52)) +		ctrl_2 |= SDHCI_CTRL_UHS_DDR50; +	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} +EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); +  static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)  {  	unsigned long flags; -	int vdd_bit = -1;  	u8 ctrl;  	spin_lock_irqsave(&host->lock, flags); @@ -1435,48 +1460,21 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)  	}  	if (host->version >= SDHCI_SPEC_300 && -		(ios->power_mode == MMC_POWER_UP)) +		(ios->power_mode == MMC_POWER_UP) && +		!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))  		sdhci_enable_preset_value(host, false); -	sdhci_set_clock(host, ios->clock); - -	if (ios->power_mode == MMC_POWER_OFF) -		vdd_bit = sdhci_set_power(host, -1); -	else -		vdd_bit = sdhci_set_power(host, ios->vdd); - -	if (host->vmmc && vdd_bit != -1) { -		spin_unlock_irqrestore(&host->lock, flags); -		mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); -		spin_lock_irqsave(&host->lock, flags); +	if (!ios->clock || ios->clock != host->clock) { +		host->ops->set_clock(host, ios->clock); +		host->clock = ios->clock;  	} +	sdhci_set_power(host, ios->power_mode, ios->vdd); +  	if (host->ops->platform_send_init_74_clocks)  		host->ops->platform_send_init_74_clocks(host, ios->power_mode); -	/* -	 * If your platform has 8-bit width support but is not a v3 controller, -	 * or if it requires special setup code, you should implement that in -	 * platform_bus_width(). -	 */ -	if (host->ops->platform_bus_width) { -		host->ops->platform_bus_width(host, ios->bus_width); -	} else { -		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); -		if (ios->bus_width == MMC_BUS_WIDTH_8) { -			ctrl &= ~SDHCI_CTRL_4BITBUS; -			if (host->version >= SDHCI_SPEC_300) -				ctrl |= SDHCI_CTRL_8BITBUS; -		} else { -			if (host->version >= SDHCI_SPEC_300) -				ctrl &= ~SDHCI_CTRL_8BITBUS; -			if (ios->bus_width == MMC_BUS_WIDTH_4) -				ctrl |= SDHCI_CTRL_4BITBUS; -			else -				ctrl &= ~SDHCI_CTRL_4BITBUS; -		} -		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); -	} +	host->ops->set_bus_width(host, ios->bus_width);  	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); @@ -1492,19 +1490,20 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)  		/* In case of UHS-I modes, set High Speed Enable */  		if ((ios->timing == MMC_TIMING_MMC_HS200) || +		    (ios->timing == MMC_TIMING_MMC_DDR52) ||  		    (ios->timing == MMC_TIMING_UHS_SDR50) ||  		    (ios->timing == MMC_TIMING_UHS_SDR104) ||  		    (ios->timing == MMC_TIMING_UHS_DDR50) ||  		    (ios->timing == MMC_TIMING_UHS_SDR25))  			ctrl |= SDHCI_CTRL_HISPD; -		ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); -		if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) { +		if (!host->preset_enabled) {  			sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);  			/*  			 * We only need to set Driver Strength if the  			 * preset value enable is not set.  			 */ +			ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);  			ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;  			if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)  				ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; @@ -1528,7 +1527,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)  			sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);  			/* Re-enable SD Clock */ -			sdhci_update_clock(host); +			host->ops->set_clock(host, host->clock);  		} @@ -1537,25 +1536,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)  		clk &= ~SDHCI_CLOCK_CARD_EN;  		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); -		if (host->ops->set_uhs_signaling) -			host->ops->set_uhs_signaling(host, ios->timing); -		else { -			ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); -			/* Select Bus Speed Mode for host */ -			ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; -			if ((ios->timing == MMC_TIMING_MMC_HS200) || -			    (ios->timing == MMC_TIMING_UHS_SDR104)) -				ctrl_2 |= SDHCI_CTRL_UHS_SDR104; -			else if (ios->timing == MMC_TIMING_UHS_SDR12) -				ctrl_2 |= SDHCI_CTRL_UHS_SDR12; -			else if (ios->timing == MMC_TIMING_UHS_SDR25) -				ctrl_2 |= SDHCI_CTRL_UHS_SDR25; -			else if (ios->timing == MMC_TIMING_UHS_SDR50) -				ctrl_2 |= SDHCI_CTRL_UHS_SDR50; -			else if (ios->timing == MMC_TIMING_UHS_DDR50) -				ctrl_2 |= SDHCI_CTRL_UHS_DDR50; -			sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); -		} +		host->ops->set_uhs_signaling(host, ios->timing); +		host->timing = ios->timing;  		if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) &&  				((ios->timing == MMC_TIMING_UHS_SDR12) || @@ -1572,7 +1554,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)  		}  		/* Re-enable SD Clock */ -		sdhci_update_clock(host); +		host->ops->set_clock(host, host->clock);  	} else  		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -1582,7 +1564,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)  	 * it on each ios seems to solve the problem.  	 */  	if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) -		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); +		sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);  	mmiowb();  	spin_unlock_irqrestore(&host->lock, flags); @@ -1691,24 +1673,16 @@ static int sdhci_get_ro(struct mmc_host *mmc)  static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)  { -	if (host->flags & SDHCI_DEVICE_DEAD) -		goto out; - -	if (enable) -		host->flags |= SDHCI_SDIO_IRQ_ENABLED; -	else -		host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; - -	/* SDIO IRQ will be enabled as appropriate in runtime resume */ -	if (host->runtime_suspended) -		goto out; +	if (!(host->flags & SDHCI_DEVICE_DEAD)) { +		if (enable) +			host->ier |= SDHCI_INT_CARD_INT; +		else +			host->ier &= ~SDHCI_INT_CARD_INT; -	if (enable) -		sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); -	else -		sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); -out: -	mmiowb(); +		sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +		sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +		mmiowb(); +	}  }  static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1716,9 +1690,18 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)  	struct sdhci_host *host = mmc_priv(mmc);  	unsigned long flags; +	sdhci_runtime_pm_get(host); +  	spin_lock_irqsave(&host->lock, flags); +	if (enable) +		host->flags |= SDHCI_SDIO_IRQ_ENABLED; +	else +		host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; +  	sdhci_enable_sdio_irq_nolock(host, enable);  	spin_unlock_irqrestore(&host->lock, flags); + +	sdhci_runtime_pm_put(host);  }  static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, @@ -1837,21 +1820,14 @@ static int sdhci_card_busy(struct mmc_host *mmc)  static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)  { -	struct sdhci_host *host; +	struct sdhci_host *host = mmc_priv(mmc);  	u16 ctrl; -	u32 ier;  	int tuning_loop_counter = MAX_TUNING_LOOP; -	unsigned long timeout;  	int err = 0; -	bool requires_tuning_nonuhs = false; - -	host = mmc_priv(mmc); +	unsigned long flags;  	sdhci_runtime_pm_get(host); -	disable_irq(host->irq); -	spin_lock(&host->lock); - -	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +	spin_lock_irqsave(&host->lock, flags);  	/*  	 * The Host Controller needs tuning only in case of SDR104 mode @@ -1860,21 +1836,32 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)  	 * If the Host Controller supports the HS200 mode then the  	 * tuning function has to be executed.  	 */ -	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && -	    (host->flags & SDHCI_SDR50_NEEDS_TUNING || -	     host->flags & SDHCI_SDR104_NEEDS_TUNING)) -		requires_tuning_nonuhs = true; - -	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || -	    requires_tuning_nonuhs) -		ctrl |= SDHCI_CTRL_EXEC_TUNING; -	else { -		spin_unlock(&host->lock); -		enable_irq(host->irq); +	switch (host->timing) { +	case MMC_TIMING_MMC_HS200: +	case MMC_TIMING_UHS_SDR104: +		break; + +	case MMC_TIMING_UHS_SDR50: +		if (host->flags & SDHCI_SDR50_NEEDS_TUNING || +		    host->flags & SDHCI_SDR104_NEEDS_TUNING) +			break; +		/* FALLTHROUGH */ + +	default: +		spin_unlock_irqrestore(&host->lock, flags);  		sdhci_runtime_pm_put(host);  		return 0;  	} +	if (host->ops->platform_execute_tuning) { +		spin_unlock_irqrestore(&host->lock, flags); +		err = host->ops->platform_execute_tuning(host, opcode); +		sdhci_runtime_pm_put(host); +		return err; +	} + +	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +	ctrl |= SDHCI_CTRL_EXEC_TUNING;  	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);  	/* @@ -1887,21 +1874,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)  	 * to make sure we don't hit a controller bug, we _only_  	 * enable Buffer Read Ready interrupt here.  	 */ -	ier = sdhci_readl(host, SDHCI_INT_ENABLE); -	sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL); +	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); +	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);  	/*  	 * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number  	 * of loops reaches 40 times or a timeout of 150ms occurs.  	 */ -	timeout = 150;  	do {  		struct mmc_command cmd = {0};  		struct mmc_request mrq = {NULL}; -		if (!tuning_loop_counter && !timeout) -			break; -  		cmd.opcode = opcode;  		cmd.arg = 0;  		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -1909,6 +1892,9 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)  		cmd.data = NULL;  		cmd.error = 0; +		if (tuning_loop_counter-- == 0) +			break; +  		mrq.cmd = &cmd;  		host->mrq = &mrq; @@ -1942,15 +1928,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)  		host->cmd = NULL;  		host->mrq = NULL; -		spin_unlock(&host->lock); -		enable_irq(host->irq); - +		spin_unlock_irqrestore(&host->lock, flags);  		/* Wait for Buffer Read Ready interrupt */  		wait_event_interruptible_timeout(host->buf_ready_int,  					(host->tuning_done == 1),  					msecs_to_jiffies(50)); -		disable_irq(host->irq); -		spin_lock(&host->lock); +		spin_lock_irqsave(&host->lock, flags);  		if (!host->tuning_done) {  			pr_info(DRIVER_NAME ": Timeout waiting for " @@ -1969,25 +1952,25 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)  		host->tuning_done = 0;  		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); -		tuning_loop_counter--; -		timeout--; -		mdelay(1); + +		/* eMMC spec does not require a delay between tuning cycles */ +		if (opcode == MMC_SEND_TUNING_BLOCK) +			mdelay(1);  	} while (ctrl & SDHCI_CTRL_EXEC_TUNING);  	/*  	 * The Host Driver has exhausted the maximum number of loops allowed,  	 * so use fixed sampling frequency.  	 */ -	if (!tuning_loop_counter || !timeout) { +	if (tuning_loop_counter < 0) {  		ctrl &= ~SDHCI_CTRL_TUNED_CLK;  		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); -	} else { -		if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { -			pr_info(DRIVER_NAME ": Tuning procedure" -				" failed, falling back to fixed sampling" -				" clock\n"); -			err = -EIO; -		} +	} +	if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { +		pr_info(DRIVER_NAME ": Tuning procedure" +			" failed, falling back to fixed sampling" +			" clock\n"); +		err = -EIO;  	}  out: @@ -2004,12 +1987,11 @@ out:  			host->tuning_count * HZ);  		/* Tuning mode 1 limits the maximum data length to 4MB */  		mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size; -	} else { +	} else if (host->flags & SDHCI_USING_RETUNING_TIMER) {  		host->flags &= ~SDHCI_NEEDS_RETUNING;  		/* Reload the new initial value for timer */ -		if (host->tuning_mode == SDHCI_TUNING_MODE_1) -			mod_timer(&host->tuning_timer, jiffies + -				host->tuning_count * HZ); +		mod_timer(&host->tuning_timer, jiffies + +			  host->tuning_count * HZ);  	}  	/* @@ -2023,9 +2005,9 @@ out:  	if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))  		err = 0; -	sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); -	spin_unlock(&host->lock); -	enable_irq(host->irq); +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +	spin_unlock_irqrestore(&host->lock, flags);  	sdhci_runtime_pm_put(host);  	return err; @@ -2034,26 +2016,30 @@ out:  static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)  { -	u16 ctrl; -  	/* Host Controller v3.00 defines preset value registers */  	if (host->version < SDHCI_SPEC_300)  		return; -	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); -  	/*  	 * We only enable or disable Preset Value if they are not already  	 * enabled or disabled respectively. Otherwise, we bail out.  	 */ -	if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { -		ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; -		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); -		host->flags |= SDHCI_PV_ENABLED; -	} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { -		ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; +	if (host->preset_enabled != enable) { +		u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + +		if (enable) +			ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; +		else +			ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; +  		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); -		host->flags &= ~SDHCI_PV_ENABLED; + +		if (enable) +			host->flags |= SDHCI_PV_ENABLED; +		else +			host->flags &= ~SDHCI_PV_ENABLED; + +		host->preset_enabled = enable;  	}  } @@ -2075,8 +2061,8 @@ static void sdhci_card_event(struct mmc_host *mmc)  		pr_err("%s: Resetting controller.\n",  			mmc_hostname(host->mmc)); -		sdhci_reset(host, SDHCI_RESET_CMD); -		sdhci_reset(host, SDHCI_RESET_DATA); +		sdhci_do_reset(host, SDHCI_RESET_CMD); +		sdhci_do_reset(host, SDHCI_RESET_DATA);  		host->mrq->cmd->error = -ENOMEDIUM;  		tasklet_schedule(&host->finish_tasklet); @@ -2104,15 +2090,6 @@ static const struct mmc_host_ops sdhci_ops = {   *                                                                           *  \*****************************************************************************/ -static void sdhci_tasklet_card(unsigned long param) -{ -	struct sdhci_host *host = (struct sdhci_host*)param; - -	sdhci_card_event(host->mmc); - -	mmc_detect_change(host->mmc, msecs_to_jiffies(200)); -} -  static void sdhci_tasklet_finish(unsigned long param)  {  	struct sdhci_host *host; @@ -2149,12 +2126,12 @@ static void sdhci_tasklet_finish(unsigned long param)  		/* Some controllers need this kick or reset won't work here */  		if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)  			/* This is to force an update */ -			sdhci_update_clock(host); +			host->ops->set_clock(host, host->clock);  		/* Spec says we should do both at the same time, but Ricoh  		   controllers do not like that. */ -		sdhci_reset(host, SDHCI_RESET_CMD); -		sdhci_reset(host, SDHCI_RESET_DATA); +		sdhci_do_reset(host, SDHCI_RESET_CMD); +		sdhci_do_reset(host, SDHCI_RESET_DATA);  	}  	host->mrq = NULL; @@ -2404,95 +2381,94 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)  static irqreturn_t sdhci_irq(int irq, void *dev_id)  { -	irqreturn_t result; +	irqreturn_t result = IRQ_NONE;  	struct sdhci_host *host = dev_id; -	u32 intmask, unexpected = 0; -	int cardint = 0, max_loops = 16; +	u32 intmask, mask, unexpected = 0; +	int max_loops = 16;  	spin_lock(&host->lock); -	if (host->runtime_suspended) { +	if (host->runtime_suspended && !sdhci_sdio_irq_enabled(host)) {  		spin_unlock(&host->lock); -		pr_warning("%s: got irq while runtime suspended\n", -		       mmc_hostname(host->mmc)); -		return IRQ_HANDLED; +		return IRQ_NONE;  	}  	intmask = sdhci_readl(host, SDHCI_INT_STATUS); -  	if (!intmask || intmask == 0xffffffff) {  		result = IRQ_NONE;  		goto out;  	} -again: -	DBG("*** %s got interrupt: 0x%08x\n", -		mmc_hostname(host->mmc), intmask); - -	if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { -		u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & -			      SDHCI_CARD_PRESENT; - -		/* -		 * There is a observation on i.mx esdhc.  INSERT bit will be -		 * immediately set again when it gets cleared, if a card is -		 * inserted.  We have to mask the irq to prevent interrupt -		 * storm which will freeze the system.  And the REMOVE gets -		 * the same situation. -		 * -		 * More testing are needed here to ensure it works for other -		 * platforms though. -		 */ -		sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT : -						SDHCI_INT_CARD_REMOVE); -		sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE : -						  SDHCI_INT_CARD_INSERT); - -		sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | -			     SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); -		intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); -		tasklet_schedule(&host->card_tasklet); -	} +	do { +		/* Clear selected interrupts. */ +		mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | +				  SDHCI_INT_BUS_POWER); +		sdhci_writel(host, mask, SDHCI_INT_STATUS); -	if (intmask & SDHCI_INT_CMD_MASK) { -		sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, -			SDHCI_INT_STATUS); -		sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); -	} +		DBG("*** %s got interrupt: 0x%08x\n", +			mmc_hostname(host->mmc), intmask); -	if (intmask & SDHCI_INT_DATA_MASK) { -		sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK, -			SDHCI_INT_STATUS); -		sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); -	} +		if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { +			u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & +				      SDHCI_CARD_PRESENT; -	intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); +			/* +			 * There is a observation on i.mx esdhc.  INSERT +			 * bit will be immediately set again when it gets +			 * cleared, if a card is inserted.  We have to mask +			 * the irq to prevent interrupt storm which will +			 * freeze the system.  And the REMOVE gets the +			 * same situation. +			 * +			 * More testing are needed here to ensure it works +			 * for other platforms though. +			 */ +			host->ier &= ~(SDHCI_INT_CARD_INSERT | +				       SDHCI_INT_CARD_REMOVE); +			host->ier |= present ? SDHCI_INT_CARD_REMOVE : +					       SDHCI_INT_CARD_INSERT; +			sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +			sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + +			sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | +				     SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); + +			host->thread_isr |= intmask & (SDHCI_INT_CARD_INSERT | +						       SDHCI_INT_CARD_REMOVE); +			result = IRQ_WAKE_THREAD; +		} -	intmask &= ~SDHCI_INT_ERROR; +		if (intmask & SDHCI_INT_CMD_MASK) +			sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); -	if (intmask & SDHCI_INT_BUS_POWER) { -		pr_err("%s: Card is consuming too much power!\n", -			mmc_hostname(host->mmc)); -		sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS); -	} +		if (intmask & SDHCI_INT_DATA_MASK) +			sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); -	intmask &= ~SDHCI_INT_BUS_POWER; +		if (intmask & SDHCI_INT_BUS_POWER) +			pr_err("%s: Card is consuming too much power!\n", +				mmc_hostname(host->mmc)); -	if (intmask & SDHCI_INT_CARD_INT) -		cardint = 1; +		if (intmask & SDHCI_INT_CARD_INT) { +			sdhci_enable_sdio_irq_nolock(host, false); +			host->thread_isr |= SDHCI_INT_CARD_INT; +			result = IRQ_WAKE_THREAD; +		} -	intmask &= ~SDHCI_INT_CARD_INT; +		intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | +			     SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | +			     SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | +			     SDHCI_INT_CARD_INT); -	if (intmask) { -		unexpected |= intmask; -		sdhci_writel(host, intmask, SDHCI_INT_STATUS); -	} +		if (intmask) { +			unexpected |= intmask; +			sdhci_writel(host, intmask, SDHCI_INT_STATUS); +		} -	result = IRQ_HANDLED; +		if (result == IRQ_NONE) +			result = IRQ_HANDLED; -	intmask = sdhci_readl(host, SDHCI_INT_STATUS); -	if (intmask && --max_loops) -		goto again; +		intmask = sdhci_readl(host, SDHCI_INT_STATUS); +	} while (intmask && --max_loops);  out:  	spin_unlock(&host->lock); @@ -2501,15 +2477,38 @@ out:  			   mmc_hostname(host->mmc), unexpected);  		sdhci_dumpregs(host);  	} -	/* -	 * We have to delay this as it calls back into the driver. -	 */ -	if (cardint) -		mmc_signal_sdio_irq(host->mmc);  	return result;  } +static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) +{ +	struct sdhci_host *host = dev_id; +	unsigned long flags; +	u32 isr; + +	spin_lock_irqsave(&host->lock, flags); +	isr = host->thread_isr; +	host->thread_isr = 0; +	spin_unlock_irqrestore(&host->lock, flags); + +	if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { +		sdhci_card_event(host->mmc); +		mmc_detect_change(host->mmc, msecs_to_jiffies(200)); +	} + +	if (isr & SDHCI_INT_CARD_INT) { +		sdio_run_irqs(host->mmc); + +		spin_lock_irqsave(&host->lock, flags); +		if (host->flags & SDHCI_SDIO_IRQ_ENABLED) +			sdhci_enable_sdio_irq_nolock(host, true); +		spin_unlock_irqrestore(&host->lock, flags); +	} + +	return isr ? IRQ_HANDLED : IRQ_NONE; +} +  /*****************************************************************************\   *                                                                           *   * Suspend/resume                                                            * @@ -2546,11 +2545,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);  int sdhci_suspend_host(struct sdhci_host *host)  { -	int ret; - -	if (host->ops->platform_suspend) -		host->ops->platform_suspend(host); -  	sdhci_disable_card_detection(host);  	/* Disable tuning since we are suspending */ @@ -2559,34 +2553,23 @@ int sdhci_suspend_host(struct sdhci_host *host)  		host->flags &= ~SDHCI_NEEDS_RETUNING;  	} -	ret = mmc_suspend_host(host->mmc); -	if (ret) { -		if (host->flags & SDHCI_USING_RETUNING_TIMER) { -			host->flags |= SDHCI_NEEDS_RETUNING; -			mod_timer(&host->tuning_timer, jiffies + -					host->tuning_count * HZ); -		} - -		sdhci_enable_card_detection(host); - -		return ret; -	} -  	if (!device_may_wakeup(mmc_dev(host->mmc))) { -		sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); +		host->ier = 0; +		sdhci_writel(host, 0, SDHCI_INT_ENABLE); +		sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);  		free_irq(host->irq, host);  	} else {  		sdhci_enable_irq_wakeups(host);  		enable_irq_wake(host->irq);  	} -	return ret; +	return 0;  }  EXPORT_SYMBOL_GPL(sdhci_suspend_host);  int sdhci_resume_host(struct sdhci_host *host)  { -	int ret; +	int ret = 0;  	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {  		if (host->ops->enable_dma) @@ -2594,8 +2577,9 @@ int sdhci_resume_host(struct sdhci_host *host)  	}  	if (!device_may_wakeup(mmc_dev(host->mmc))) { -		ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, -				  mmc_hostname(host->mmc), host); +		ret = request_threaded_irq(host->irq, sdhci_irq, +					   sdhci_thread_irq, IRQF_SHARED, +					   mmc_hostname(host->mmc), host);  		if (ret)  			return ret;  	} else { @@ -2615,12 +2599,8 @@ int sdhci_resume_host(struct sdhci_host *host)  		mmiowb();  	} -	ret = mmc_resume_host(host->mmc);  	sdhci_enable_card_detection(host); -	if (host->ops->platform_resume) -		host->ops->platform_resume(host); -  	/* Set the re-tuning expiration flag */  	if (host->flags & SDHCI_USING_RETUNING_TIMER)  		host->flags |= SDHCI_NEEDS_RETUNING; @@ -2672,10 +2652,12 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)  	}  	spin_lock_irqsave(&host->lock, flags); -	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); +	host->ier &= SDHCI_INT_CARD_INT; +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);  	spin_unlock_irqrestore(&host->lock, flags); -	synchronize_irq(host->irq); +	synchronize_hardirq(host->irq);  	spin_lock_irqsave(&host->lock, flags);  	host->runtime_suspended = true; @@ -2719,7 +2701,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)  	host->runtime_suspended = false;  	/* Enable SDIO IRQ */ -	if ((host->flags & SDHCI_SDIO_IRQ_ENABLED)) +	if (host->flags & SDHCI_SDIO_IRQ_ENABLED)  		sdhci_enable_sdio_irq_nolock(host, true);  	/* Enable Card Detection */ @@ -2778,7 +2760,7 @@ int sdhci_add_host(struct sdhci_host *host)  	if (debug_quirks2)  		host->quirks2 = debug_quirks2; -	sdhci_reset(host, SDHCI_RESET_ALL); +	sdhci_do_reset(host, SDHCI_RESET_ALL);  	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);  	host->version = (host->version & SDHCI_SPEC_VER_MASK) @@ -2838,15 +2820,29 @@ int sdhci_add_host(struct sdhci_host *host)  		 * (128) and potentially one alignment transfer for  		 * each of those entries.  		 */ -		host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL); +		host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc), +						     ADMA_SIZE, &host->adma_addr, +						     GFP_KERNEL);  		host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);  		if (!host->adma_desc || !host->align_buffer) { -			kfree(host->adma_desc); +			dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, +					  host->adma_desc, host->adma_addr);  			kfree(host->align_buffer);  			pr_warning("%s: Unable to allocate ADMA "  				"buffers. Falling back to standard DMA.\n",  				mmc_hostname(mmc));  			host->flags &= ~SDHCI_USE_ADMA; +			host->adma_desc = NULL; +			host->align_buffer = NULL; +		} else if (host->adma_addr & 3) { +			pr_warning("%s: unable to allocate aligned ADMA descriptor\n", +				   mmc_hostname(mmc)); +			host->flags &= ~SDHCI_USE_ADMA; +			dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, +					  host->adma_desc, host->adma_addr); +			kfree(host->align_buffer); +			host->adma_desc = NULL; +			host->align_buffer = NULL;  		}  	} @@ -2928,9 +2924,10 @@ int sdhci_add_host(struct sdhci_host *host)  	if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)  		host->timeout_clk = mmc->f_max / 1000; -	mmc->max_discard_to = (1 << 27) / host->timeout_clk; +	mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;  	mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; +	mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;  	if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)  		host->flags |= SDHCI_AUTO_CMD12; @@ -3002,11 +2999,13 @@ int sdhci_add_host(struct sdhci_host *host)  		/* SD3.0: SDR104 is supported so (for eMMC) the caps2  		 * field can be promoted to support HS200.  		 */ -		mmc->caps2 |= MMC_CAP2_HS200; +		if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) +			mmc->caps2 |= MMC_CAP2_HS200;  	} else if (caps[1] & SDHCI_SUPPORT_SDR50)  		mmc->caps |= MMC_CAP_UHS_SDR50; -	if (caps[1] & SDHCI_SUPPORT_DDR50) +	if ((caps[1] & SDHCI_SUPPORT_DDR50) && +		!(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))  		mmc->caps |= MMC_CAP_UHS_DDR50;  	/* Does the host need tuning for SDR50? */ @@ -3200,8 +3199,6 @@ int sdhci_add_host(struct sdhci_host *host)  	/*  	 * Init tasklets.  	 */ -	tasklet_init(&host->card_tasklet, -		sdhci_tasklet_card, (unsigned long)host);  	tasklet_init(&host->finish_tasklet,  		sdhci_tasklet_finish, (unsigned long)host); @@ -3218,8 +3215,8 @@ int sdhci_add_host(struct sdhci_host *host)  	sdhci_init(host, 0); -	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, -		mmc_hostname(mmc), host); +	ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, +				   IRQF_SHARED,	mmc_hostname(mmc), host);  	if (ret) {  		pr_err("%s: Failed to request IRQ %d: %d\n",  		       mmc_hostname(mmc), host->irq, ret); @@ -3261,12 +3258,12 @@ int sdhci_add_host(struct sdhci_host *host)  #ifdef SDHCI_USE_LEDS_CLASS  reset: -	sdhci_reset(host, SDHCI_RESET_ALL); -	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); +	sdhci_do_reset(host, SDHCI_RESET_ALL); +	sdhci_writel(host, 0, SDHCI_INT_ENABLE); +	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);  	free_irq(host->irq, host);  #endif  untasklet: -	tasklet_kill(&host->card_tasklet);  	tasklet_kill(&host->finish_tasklet);  	return ret; @@ -3303,14 +3300,14 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)  #endif  	if (!dead) -		sdhci_reset(host, SDHCI_RESET_ALL); +		sdhci_do_reset(host, SDHCI_RESET_ALL); -	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); +	sdhci_writel(host, 0, SDHCI_INT_ENABLE); +	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);  	free_irq(host->irq, host);  	del_timer_sync(&host->timer); -	tasklet_kill(&host->card_tasklet);  	tasklet_kill(&host->finish_tasklet);  	if (host->vmmc) { @@ -3323,7 +3320,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)  		regulator_put(host->vqmmc);  	} -	kfree(host->adma_desc); +	if (host->adma_desc) +		dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, +				  host->adma_desc, host->adma_addr);  	kfree(host->align_buffer);  	host->adma_desc = NULL; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b037f188fe4..4a5cd5e3fa3 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -281,17 +281,14 @@ struct sdhci_ops {  	unsigned int	(*get_max_clock)(struct sdhci_host *host);  	unsigned int	(*get_min_clock)(struct sdhci_host *host);  	unsigned int	(*get_timeout_clock)(struct sdhci_host *host); -	int		(*platform_bus_width)(struct sdhci_host *host, -					       int width); +	void		(*set_bus_width)(struct sdhci_host *host, int width);  	void (*platform_send_init_74_clocks)(struct sdhci_host *host,  					     u8 power_mode);  	unsigned int    (*get_ro)(struct sdhci_host *host); -	void	(*platform_reset_enter)(struct sdhci_host *host, u8 mask); -	void	(*platform_reset_exit)(struct sdhci_host *host, u8 mask); -	int	(*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); +	void		(*reset)(struct sdhci_host *host, u8 mask); +	int	(*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); +	void	(*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);  	void	(*hw_reset)(struct sdhci_host *host); -	void	(*platform_suspend)(struct sdhci_host *host); -	void	(*platform_resume)(struct sdhci_host *host);  	void    (*adma_workaround)(struct sdhci_host *host, u32 intmask);  	void	(*platform_init)(struct sdhci_host *host);  	void    (*card_event)(struct sdhci_host *host); @@ -393,6 +390,18 @@ static inline void *sdhci_priv(struct sdhci_host *host)  extern void sdhci_card_detect(struct sdhci_host *host);  extern int sdhci_add_host(struct sdhci_host *host);  extern void sdhci_remove_host(struct sdhci_host *host, int dead); +extern void sdhci_send_command(struct sdhci_host *host, +				struct mmc_command *cmd); + +static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) +{ +	return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); +} + +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); +void sdhci_set_bus_width(struct sdhci_host *host, int width); +void sdhci_reset(struct sdhci_host *host, u8 mask); +void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);  #ifdef CONFIG_PM  extern int sdhci_suspend_host(struct sdhci_host *host); diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 50adbd155f3..b7e30577531 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -516,9 +516,7 @@ static void sdricoh_pcmcia_detach(struct pcmcia_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);  	return 0;  } @@ -527,7 +525,6 @@ 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 diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 36629a024aa..656fbba4c42 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -381,73 +381,75 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)  		desc, cookie);  } -static void sh_mmcif_request_dma(struct sh_mmcif_host *host, -				 struct sh_mmcif_plat_data *pdata) +static struct dma_chan * +sh_mmcif_request_dma_one(struct sh_mmcif_host *host, +			 struct sh_mmcif_plat_data *pdata, +			 enum dma_transfer_direction direction)  { -	struct resource *res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);  	struct dma_slave_config cfg; +	struct dma_chan *chan; +	unsigned int slave_id; +	struct resource *res;  	dma_cap_mask_t mask;  	int ret; -	host->dma_active = false; - -	if (pdata) { -		if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) -			return; -	} else if (!host->pd->dev.of_node) { -		return; -	} - -	/* We can only either use DMA for both Tx and Rx or not use it at all */  	dma_cap_zero(mask);  	dma_cap_set(DMA_SLAVE, mask); -	host->chan_tx = dma_request_slave_channel_compat(mask, shdma_chan_filter, -				pdata ? (void *)pdata->slave_id_tx : NULL, -				&host->pd->dev, "tx"); -	dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, -		host->chan_tx); +	if (pdata) +		slave_id = direction == DMA_MEM_TO_DEV +			 ? pdata->slave_id_tx : pdata->slave_id_rx; +	else +		slave_id = 0; + +	chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, +				(void *)(unsigned long)slave_id, &host->pd->dev, +				direction == DMA_MEM_TO_DEV ? "tx" : "rx"); -	if (!host->chan_tx) -		return; +	dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__, +		direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan); + +	if (!chan) +		return NULL; + +	res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);  	/* In the OF case the driver will get the slave ID from the DT */ -	if (pdata) -		cfg.slave_id = pdata->slave_id_tx; -	cfg.direction = DMA_MEM_TO_DEV; +	cfg.slave_id = slave_id; +	cfg.direction = direction;  	cfg.dst_addr = res->start + MMCIF_CE_DATA;  	cfg.src_addr = 0; -	ret = dmaengine_slave_config(host->chan_tx, &cfg); -	if (ret < 0) -		goto ecfgtx; +	ret = dmaengine_slave_config(chan, &cfg); +	if (ret < 0) { +		dma_release_channel(chan); +		return NULL; +	} -	host->chan_rx = dma_request_slave_channel_compat(mask, shdma_chan_filter, -				pdata ? (void *)pdata->slave_id_rx : NULL, -				&host->pd->dev, "rx"); -	dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, -		host->chan_rx); +	return chan; +} -	if (!host->chan_rx) -		goto erqrx; +static void sh_mmcif_request_dma(struct sh_mmcif_host *host, +				 struct sh_mmcif_plat_data *pdata) +{ +	host->dma_active = false; -	if (pdata) -		cfg.slave_id = pdata->slave_id_rx; -	cfg.direction = DMA_DEV_TO_MEM; -	cfg.dst_addr = 0; -	cfg.src_addr = res->start + MMCIF_CE_DATA; -	ret = dmaengine_slave_config(host->chan_rx, &cfg); -	if (ret < 0) -		goto ecfgrx; +	if (pdata) { +		if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) +			return; +	} else if (!host->pd->dev.of_node) { +		return; +	} -	return; +	/* We can only either use DMA for both Tx and Rx or not use it at all */ +	host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV); +	if (!host->chan_tx) +		return; -ecfgrx: -	dma_release_channel(host->chan_rx); -	host->chan_rx = NULL; -erqrx: -ecfgtx: -	dma_release_channel(host->chan_tx); -	host->chan_tx = NULL; +	host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM); +	if (!host->chan_rx) { +		dma_release_channel(host->chan_tx); +		host->chan_tx = NULL; +	}  }  static void sh_mmcif_release_dma(struct sh_mmcif_host *host) @@ -801,12 +803,13 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,  			break;  		}  		switch (host->timing) { -		case MMC_TIMING_UHS_DDR50: +		case MMC_TIMING_MMC_DDR52:  			/*  			 * MMC core will only set this timing, if the host -			 * advertises the MMC_CAP_UHS_DDR50 capability. MMCIF -			 * implementations with this capability, e.g. sh73a0, -			 * will have to set it in their platform data. +			 * advertises the MMC_CAP_1_8V_DDR/MMC_CAP_1_2V_DDR +			 * capability. MMCIF implementations with this +			 * capability, e.g. sh73a0, will have to set it +			 * in their platform data.  			 */  			tmp |= CMD_SET_DARS;  			break; @@ -964,7 +967,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)  static int sh_mmcif_clk_update(struct sh_mmcif_host *host)  { -	int ret = clk_enable(host->hclk); +	int ret = clk_prepare_enable(host->hclk);  	if (!ret) {  		host->clk = clk_get_rate(host->hclk); @@ -1018,7 +1021,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		}  		if (host->power) {  			pm_runtime_put_sync(&host->pd->dev); -			clk_disable(host->hclk); +			clk_disable_unprepare(host->hclk);  			host->power = false;  			if (ios->power_mode == MMC_POWER_OFF)  				sh_mmcif_set_power(host, ios); @@ -1466,7 +1469,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)  	mutex_init(&host->thread_lock); -	clk_disable(host->hclk); +	clk_disable_unprepare(host->hclk);  	ret = mmc_add_host(mmc);  	if (ret < 0)  		goto emmcaddh; @@ -1487,7 +1490,7 @@ ereqirq1:  ereqirq0:  	pm_runtime_suspend(&pdev->dev);  eresume: -	clk_disable(host->hclk); +	clk_disable_unprepare(host->hclk);  eclkupdate:  	clk_put(host->hclk);  eclkget: @@ -1505,7 +1508,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)  	int irq[2];  	host->dying = true; -	clk_enable(host->hclk); +	clk_prepare_enable(host->hclk);  	pm_runtime_get_sync(&pdev->dev);  	dev_pm_qos_hide_latency_limit(&pdev->dev); @@ -1530,7 +1533,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)  	if (irq[1] >= 0)  		free_irq(irq[1], host); -	clk_disable(host->hclk); +	clk_disable_unprepare(host->hclk);  	mmc_free_host(host->mmc);  	pm_runtime_put_sync(&pdev->dev);  	pm_runtime_disable(&pdev->dev); @@ -1538,28 +1541,21 @@ static int sh_mmcif_remove(struct platform_device *pdev)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int sh_mmcif_suspend(struct device *dev)  {  	struct sh_mmcif_host *host = dev_get_drvdata(dev); -	int ret = mmc_suspend_host(host->mmc); -	if (!ret) -		sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); +	sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); -	return ret; +	return 0;  }  static int sh_mmcif_resume(struct device *dev)  { -	struct sh_mmcif_host *host = dev_get_drvdata(dev); - -	return mmc_resume_host(host->mmc); +	return 0;  } -#else -#define sh_mmcif_suspend	NULL -#define sh_mmcif_resume		NULL -#endif	/* CONFIG_PM */ +#endif  static const struct of_device_id mmcif_of_match[] = {  	{ .compatible = "renesas,sh-mmcif" }, @@ -1568,8 +1564,7 @@ static const struct of_device_id mmcif_of_match[] = {  MODULE_DEVICE_TABLE(of, mmcif_of_match);  static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { -	.suspend = sh_mmcif_suspend, -	.resume = sh_mmcif_resume, +	SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)  };  static struct platform_driver sh_mmcif_driver = { diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 87ed3fb5149..91058dabd11 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -33,8 +33,12 @@  #include "tmio_mmc.h" +#define EXT_ACC           0xe4 +  struct sh_mobile_sdhi_of_data {  	unsigned long tmio_flags; +	unsigned long capabilities; +	unsigned long capabilities2;  };  static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = { @@ -43,6 +47,31 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {  	},  }; +static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = { +	.tmio_flags	= TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, +	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, +}; + +static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { +	.tmio_flags	= TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, +	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, +	.capabilities2	= MMC_CAP2_NO_MULTI_READ, +}; + +static const struct of_device_id sh_mobile_sdhi_of_match[] = { +	{ .compatible = "renesas,sdhi-shmobile" }, +	{ .compatible = "renesas,sdhi-sh7372" }, +	{ .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], }, +	{ .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], }, +	{ .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], }, +	{ .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, }, +	{ .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, }, +	{ .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, }, +	{ .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, }, +	{}, +}; +MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); +  struct sh_mobile_sdhi {  	struct clk *clk;  	struct tmio_mmc_data mmc_data; @@ -54,7 +83,7 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int  	struct mmc_host *mmc = platform_get_drvdata(pdev);  	struct tmio_mmc_host *host = mmc_priv(mmc);  	struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); -	int ret = clk_enable(priv->clk); +	int ret = clk_prepare_enable(priv->clk);  	if (ret < 0)  		return ret; @@ -67,7 +96,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)  	struct mmc_host *mmc = platform_get_drvdata(pdev);  	struct tmio_mmc_host *host = mmc_priv(mmc);  	struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); -	clk_disable(priv->clk); +	clk_disable_unprepare(priv->clk);  }  static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) @@ -112,19 +141,6 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = {  	.cd_wakeup = sh_mobile_sdhi_cd_wakeup,  }; -static const struct of_device_id sh_mobile_sdhi_of_match[] = { -	{ .compatible = "renesas,shmobile-sdhi" }, -	{ .compatible = "renesas,sh7372-sdhi" }, -	{ .compatible = "renesas,sh73a0-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], }, -	{ .compatible = "renesas,r8a73a4-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], }, -	{ .compatible = "renesas,r8a7740-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], }, -	{ .compatible = "renesas,r8a7778-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], }, -	{ .compatible = "renesas,r8a7779-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], }, -	{ .compatible = "renesas,r8a7790-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], }, -	{}, -}; -MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); -  static int sh_mobile_sdhi_probe(struct platform_device *pdev)  {  	const struct of_device_id *of_id = @@ -133,9 +149,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)  	struct tmio_mmc_data *mmc_data;  	struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;  	struct tmio_mmc_host *host; +	struct resource *res;  	int irq, ret, i = 0;  	bool multiplexed_isr = true;  	struct tmio_mmc_dma *dma_priv; +	u16 ver; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) +		return -EINVAL;  	priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);  	if (priv == NULL) { @@ -204,13 +226,26 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)  	if (of_id && of_id->data) {  		const struct sh_mobile_sdhi_of_data *of_data = of_id->data;  		mmc_data->flags |= of_data->tmio_flags; +		mmc_data->capabilities |= of_data->capabilities; +		mmc_data->capabilities2 |= of_data->capabilities2;  	} +	/* SD control register space size is 0x100, 0x200 for bus_shift=1 */ +	mmc_data->bus_shift = resource_size(res) >> 9; +  	ret = tmio_mmc_host_probe(&host, pdev, mmc_data);  	if (ret < 0)  		goto eprobe;  	/* +	 * FIXME: +	 * this Workaround can be more clever method +	 */ +	ver = sd_ctrl_read16(host, CTL_VERSION); +	if (ver == 0xCB0D) +		sd_ctrl_write16(host, EXT_ACC, 1); + +	/*  	 * Allow one or more specific (named) ISRs or  	 * one or more multiplexed (un-named) ISRs.  	 */ @@ -297,10 +332,10 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)  }  static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { -	.suspend = tmio_mmc_host_suspend, -	.resume = tmio_mmc_host_resume, -	.runtime_suspend = tmio_mmc_host_runtime_suspend, -	.runtime_resume = tmio_mmc_host_runtime_resume, +	SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume) +	SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, +			tmio_mmc_host_runtime_resume, +			NULL)  };  static struct platform_driver sh_mobile_sdhi_driver = { diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c new file mode 100644 index 00000000000..024f67c98cd --- /dev/null +++ b/drivers/mmc/host/sunxi-mmc.c @@ -0,0 +1,1049 @@ +/* + * Driver for sunxi SD/MMC host controllers + * (C) Copyright 2007-2011 Reuuimlla Technology Co., Ltd. + * (C) Copyright 2007-2011 Aaron Maoye <leafy.myeh@reuuimllatech.com> + * (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch> + * (C) Copyright 2013-2014 David Lanzend�rfer <david.lanzendoerfer@o2s.ch> + * (C) Copyright 2013-2014 Hans de Goede <hdegoede@redhat.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <linux/clk.h> +#include <linux/clk-private.h> +#include <linux/clk/sunxi.h> + +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/reset.h> + +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/core.h> +#include <linux/mmc/card.h> +#include <linux/mmc/slot-gpio.h> + +/* register offset definitions */ +#define SDXC_REG_GCTRL	(0x00) /* SMC Global Control Register */ +#define SDXC_REG_CLKCR	(0x04) /* SMC Clock Control Register */ +#define SDXC_REG_TMOUT	(0x08) /* SMC Time Out Register */ +#define SDXC_REG_WIDTH	(0x0C) /* SMC Bus Width Register */ +#define SDXC_REG_BLKSZ	(0x10) /* SMC Block Size Register */ +#define SDXC_REG_BCNTR	(0x14) /* SMC Byte Count Register */ +#define SDXC_REG_CMDR	(0x18) /* SMC Command Register */ +#define SDXC_REG_CARG	(0x1C) /* SMC Argument Register */ +#define SDXC_REG_RESP0	(0x20) /* SMC Response Register 0 */ +#define SDXC_REG_RESP1	(0x24) /* SMC Response Register 1 */ +#define SDXC_REG_RESP2	(0x28) /* SMC Response Register 2 */ +#define SDXC_REG_RESP3	(0x2C) /* SMC Response Register 3 */ +#define SDXC_REG_IMASK	(0x30) /* SMC Interrupt Mask Register */ +#define SDXC_REG_MISTA	(0x34) /* SMC Masked Interrupt Status Register */ +#define SDXC_REG_RINTR	(0x38) /* SMC Raw Interrupt Status Register */ +#define SDXC_REG_STAS	(0x3C) /* SMC Status Register */ +#define SDXC_REG_FTRGL	(0x40) /* SMC FIFO Threshold Watermark Registe */ +#define SDXC_REG_FUNS	(0x44) /* SMC Function Select Register */ +#define SDXC_REG_CBCR	(0x48) /* SMC CIU Byte Count Register */ +#define SDXC_REG_BBCR	(0x4C) /* SMC BIU Byte Count Register */ +#define SDXC_REG_DBGC	(0x50) /* SMC Debug Enable Register */ +#define SDXC_REG_HWRST	(0x78) /* SMC Card Hardware Reset for Register */ +#define SDXC_REG_DMAC	(0x80) /* SMC IDMAC Control Register */ +#define SDXC_REG_DLBA	(0x84) /* SMC IDMAC Descriptor List Base Addre */ +#define SDXC_REG_IDST	(0x88) /* SMC IDMAC Status Register */ +#define SDXC_REG_IDIE	(0x8C) /* SMC IDMAC Interrupt Enable Register */ +#define SDXC_REG_CHDA	(0x90) +#define SDXC_REG_CBDA	(0x94) + +#define mmc_readl(host, reg) \ +	readl((host)->reg_base + SDXC_##reg) +#define mmc_writel(host, reg, value) \ +	writel((value), (host)->reg_base + SDXC_##reg) + +/* global control register bits */ +#define SDXC_SOFT_RESET			BIT(0) +#define SDXC_FIFO_RESET			BIT(1) +#define SDXC_DMA_RESET			BIT(2) +#define SDXC_INTERRUPT_ENABLE_BIT	BIT(4) +#define SDXC_DMA_ENABLE_BIT		BIT(5) +#define SDXC_DEBOUNCE_ENABLE_BIT	BIT(8) +#define SDXC_POSEDGE_LATCH_DATA		BIT(9) +#define SDXC_DDR_MODE			BIT(10) +#define SDXC_MEMORY_ACCESS_DONE		BIT(29) +#define SDXC_ACCESS_DONE_DIRECT		BIT(30) +#define SDXC_ACCESS_BY_AHB		BIT(31) +#define SDXC_ACCESS_BY_DMA		(0 << 31) +#define SDXC_HARDWARE_RESET \ +	(SDXC_SOFT_RESET | SDXC_FIFO_RESET | SDXC_DMA_RESET) + +/* clock control bits */ +#define SDXC_CARD_CLOCK_ON		BIT(16) +#define SDXC_LOW_POWER_ON		BIT(17) + +/* bus width */ +#define SDXC_WIDTH1			0 +#define SDXC_WIDTH4			1 +#define SDXC_WIDTH8			2 + +/* smc command bits */ +#define SDXC_RESP_EXPIRE		BIT(6) +#define SDXC_LONG_RESPONSE		BIT(7) +#define SDXC_CHECK_RESPONSE_CRC		BIT(8) +#define SDXC_DATA_EXPIRE		BIT(9) +#define SDXC_WRITE			BIT(10) +#define SDXC_SEQUENCE_MODE		BIT(11) +#define SDXC_SEND_AUTO_STOP		BIT(12) +#define SDXC_WAIT_PRE_OVER		BIT(13) +#define SDXC_STOP_ABORT_CMD		BIT(14) +#define SDXC_SEND_INIT_SEQUENCE		BIT(15) +#define SDXC_UPCLK_ONLY			BIT(21) +#define SDXC_READ_CEATA_DEV		BIT(22) +#define SDXC_CCS_EXPIRE			BIT(23) +#define SDXC_ENABLE_BIT_BOOT		BIT(24) +#define SDXC_ALT_BOOT_OPTIONS		BIT(25) +#define SDXC_BOOT_ACK_EXPIRE		BIT(26) +#define SDXC_BOOT_ABORT			BIT(27) +#define SDXC_VOLTAGE_SWITCH	        BIT(28) +#define SDXC_USE_HOLD_REGISTER	        BIT(29) +#define SDXC_START			BIT(31) + +/* interrupt bits */ +#define SDXC_RESP_ERROR			BIT(1) +#define SDXC_COMMAND_DONE		BIT(2) +#define SDXC_DATA_OVER			BIT(3) +#define SDXC_TX_DATA_REQUEST		BIT(4) +#define SDXC_RX_DATA_REQUEST		BIT(5) +#define SDXC_RESP_CRC_ERROR		BIT(6) +#define SDXC_DATA_CRC_ERROR		BIT(7) +#define SDXC_RESP_TIMEOUT		BIT(8) +#define SDXC_DATA_TIMEOUT		BIT(9) +#define SDXC_VOLTAGE_CHANGE_DONE	BIT(10) +#define SDXC_FIFO_RUN_ERROR		BIT(11) +#define SDXC_HARD_WARE_LOCKED		BIT(12) +#define SDXC_START_BIT_ERROR		BIT(13) +#define SDXC_AUTO_COMMAND_DONE		BIT(14) +#define SDXC_END_BIT_ERROR		BIT(15) +#define SDXC_SDIO_INTERRUPT		BIT(16) +#define SDXC_CARD_INSERT		BIT(30) +#define SDXC_CARD_REMOVE		BIT(31) +#define SDXC_INTERRUPT_ERROR_BIT \ +	(SDXC_RESP_ERROR | SDXC_RESP_CRC_ERROR | SDXC_DATA_CRC_ERROR | \ +	 SDXC_RESP_TIMEOUT | SDXC_DATA_TIMEOUT | SDXC_FIFO_RUN_ERROR | \ +	 SDXC_HARD_WARE_LOCKED | SDXC_START_BIT_ERROR | SDXC_END_BIT_ERROR) +#define SDXC_INTERRUPT_DONE_BIT \ +	(SDXC_AUTO_COMMAND_DONE | SDXC_DATA_OVER | \ +	 SDXC_COMMAND_DONE | SDXC_VOLTAGE_CHANGE_DONE) + +/* status */ +#define SDXC_RXWL_FLAG			BIT(0) +#define SDXC_TXWL_FLAG			BIT(1) +#define SDXC_FIFO_EMPTY			BIT(2) +#define SDXC_FIFO_FULL			BIT(3) +#define SDXC_CARD_PRESENT		BIT(8) +#define SDXC_CARD_DATA_BUSY		BIT(9) +#define SDXC_DATA_FSM_BUSY		BIT(10) +#define SDXC_DMA_REQUEST		BIT(31) +#define SDXC_FIFO_SIZE			16 + +/* Function select */ +#define SDXC_CEATA_ON			(0xceaa << 16) +#define SDXC_SEND_IRQ_RESPONSE		BIT(0) +#define SDXC_SDIO_READ_WAIT		BIT(1) +#define SDXC_ABORT_READ_DATA		BIT(2) +#define SDXC_SEND_CCSD			BIT(8) +#define SDXC_SEND_AUTO_STOPCCSD		BIT(9) +#define SDXC_CEATA_DEV_IRQ_ENABLE	BIT(10) + +/* IDMA controller bus mod bit field */ +#define SDXC_IDMAC_SOFT_RESET		BIT(0) +#define SDXC_IDMAC_FIX_BURST		BIT(1) +#define SDXC_IDMAC_IDMA_ON		BIT(7) +#define SDXC_IDMAC_REFETCH_DES		BIT(31) + +/* IDMA status bit field */ +#define SDXC_IDMAC_TRANSMIT_INTERRUPT		BIT(0) +#define SDXC_IDMAC_RECEIVE_INTERRUPT		BIT(1) +#define SDXC_IDMAC_FATAL_BUS_ERROR		BIT(2) +#define SDXC_IDMAC_DESTINATION_INVALID		BIT(4) +#define SDXC_IDMAC_CARD_ERROR_SUM		BIT(5) +#define SDXC_IDMAC_NORMAL_INTERRUPT_SUM		BIT(8) +#define SDXC_IDMAC_ABNORMAL_INTERRUPT_SUM	BIT(9) +#define SDXC_IDMAC_HOST_ABORT_INTERRUPT		BIT(10) +#define SDXC_IDMAC_IDLE				(0 << 13) +#define SDXC_IDMAC_SUSPEND			(1 << 13) +#define SDXC_IDMAC_DESC_READ			(2 << 13) +#define SDXC_IDMAC_DESC_CHECK			(3 << 13) +#define SDXC_IDMAC_READ_REQUEST_WAIT		(4 << 13) +#define SDXC_IDMAC_WRITE_REQUEST_WAIT		(5 << 13) +#define SDXC_IDMAC_READ				(6 << 13) +#define SDXC_IDMAC_WRITE			(7 << 13) +#define SDXC_IDMAC_DESC_CLOSE			(8 << 13) + +/* +* If the idma-des-size-bits of property is ie 13, bufsize bits are: +*  Bits  0-12: buf1 size +*  Bits 13-25: buf2 size +*  Bits 26-31: not used +* Since we only ever set buf1 size, we can simply store it directly. +*/ +#define SDXC_IDMAC_DES0_DIC	BIT(1)  /* disable interrupt on completion */ +#define SDXC_IDMAC_DES0_LD	BIT(2)  /* last descriptor */ +#define SDXC_IDMAC_DES0_FD	BIT(3)  /* first descriptor */ +#define SDXC_IDMAC_DES0_CH	BIT(4)  /* chain mode */ +#define SDXC_IDMAC_DES0_ER	BIT(5)  /* end of ring */ +#define SDXC_IDMAC_DES0_CES	BIT(30) /* card error summary */ +#define SDXC_IDMAC_DES0_OWN	BIT(31) /* 1-idma owns it, 0-host owns it */ + +struct sunxi_idma_des { +	u32	config; +	u32	buf_size; +	u32	buf_addr_ptr1; +	u32	buf_addr_ptr2; +}; + +struct sunxi_mmc_host { +	struct mmc_host	*mmc; +	struct reset_control *reset; + +	/* IO mapping base */ +	void __iomem	*reg_base; + +	/* clock management */ +	struct clk	*clk_ahb; +	struct clk	*clk_mmc; + +	/* irq */ +	spinlock_t	lock; +	int		irq; +	u32		int_sum; +	u32		sdio_imask; + +	/* dma */ +	u32		idma_des_size_bits; +	dma_addr_t	sg_dma; +	void		*sg_cpu; +	bool		wait_dma; + +	struct mmc_request *mrq; +	struct mmc_request *manual_stop_mrq; +	int		ferror; +}; + +static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host) +{ +	unsigned long expire = jiffies + msecs_to_jiffies(250); +	u32 rval; + +	mmc_writel(host, REG_CMDR, SDXC_HARDWARE_RESET); +	do { +		rval = mmc_readl(host, REG_GCTRL); +	} while (time_before(jiffies, expire) && (rval & SDXC_HARDWARE_RESET)); + +	if (rval & SDXC_HARDWARE_RESET) { +		dev_err(mmc_dev(host->mmc), "fatal err reset timeout\n"); +		return -EIO; +	} + +	return 0; +} + +static int sunxi_mmc_init_host(struct mmc_host *mmc) +{ +	u32 rval; +	struct sunxi_mmc_host *host = mmc_priv(mmc); + +	if (sunxi_mmc_reset_host(host)) +		return -EIO; + +	mmc_writel(host, REG_FTRGL, 0x20070008); +	mmc_writel(host, REG_TMOUT, 0xffffffff); +	mmc_writel(host, REG_IMASK, host->sdio_imask); +	mmc_writel(host, REG_RINTR, 0xffffffff); +	mmc_writel(host, REG_DBGC, 0xdeb); +	mmc_writel(host, REG_FUNS, SDXC_CEATA_ON); +	mmc_writel(host, REG_DLBA, host->sg_dma); + +	rval = mmc_readl(host, REG_GCTRL); +	rval |= SDXC_INTERRUPT_ENABLE_BIT; +	rval &= ~SDXC_ACCESS_DONE_DIRECT; +	mmc_writel(host, REG_GCTRL, rval); + +	return 0; +} + +static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, +				    struct mmc_data *data) +{ +	struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu; +	struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma; +	int i, max_len = (1 << host->idma_des_size_bits); + +	for (i = 0; i < data->sg_len; i++) { +		pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN | +				 SDXC_IDMAC_DES0_DIC; + +		if (data->sg[i].length == max_len) +			pdes[i].buf_size = 0; /* 0 == max_len */ +		else +			pdes[i].buf_size = data->sg[i].length; + +		pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]); +		pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1]; +	} + +	pdes[0].config |= SDXC_IDMAC_DES0_FD; +	pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD; + +	/* +	 * Avoid the io-store starting the idmac hitting io-mem before the +	 * descriptors hit the main-mem. +	 */ +	wmb(); +} + +static enum dma_data_direction sunxi_mmc_get_dma_dir(struct mmc_data *data) +{ +	if (data->flags & MMC_DATA_WRITE) +		return DMA_TO_DEVICE; +	else +		return DMA_FROM_DEVICE; +} + +static int sunxi_mmc_map_dma(struct sunxi_mmc_host *host, +			     struct mmc_data *data) +{ +	u32 i, dma_len; +	struct scatterlist *sg; + +	dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, +			     sunxi_mmc_get_dma_dir(data)); +	if (dma_len == 0) { +		dev_err(mmc_dev(host->mmc), "dma_map_sg failed\n"); +		return -ENOMEM; +	} + +	for_each_sg(data->sg, sg, data->sg_len, i) { +		if (sg->offset & 3 || sg->length & 3) { +			dev_err(mmc_dev(host->mmc), +				"unaligned scatterlist: os %x length %d\n", +				sg->offset, sg->length); +			return -EINVAL; +		} +	} + +	return 0; +} + +static void sunxi_mmc_start_dma(struct sunxi_mmc_host *host, +				struct mmc_data *data) +{ +	u32 rval; + +	sunxi_mmc_init_idma_des(host, data); + +	rval = mmc_readl(host, REG_GCTRL); +	rval |= SDXC_DMA_ENABLE_BIT; +	mmc_writel(host, REG_GCTRL, rval); +	rval |= SDXC_DMA_RESET; +	mmc_writel(host, REG_GCTRL, rval); + +	mmc_writel(host, REG_DMAC, SDXC_IDMAC_SOFT_RESET); + +	if (!(data->flags & MMC_DATA_WRITE)) +		mmc_writel(host, REG_IDIE, SDXC_IDMAC_RECEIVE_INTERRUPT); + +	mmc_writel(host, REG_DMAC, +		   SDXC_IDMAC_FIX_BURST | SDXC_IDMAC_IDMA_ON); +} + +static void sunxi_mmc_send_manual_stop(struct sunxi_mmc_host *host, +				       struct mmc_request *req) +{ +	u32 arg, cmd_val, ri; +	unsigned long expire = jiffies + msecs_to_jiffies(1000); + +	cmd_val = SDXC_START | SDXC_RESP_EXPIRE | +		  SDXC_STOP_ABORT_CMD | SDXC_CHECK_RESPONSE_CRC; + +	if (req->cmd->opcode == SD_IO_RW_EXTENDED) { +		cmd_val |= SD_IO_RW_DIRECT; +		arg = (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) | +		      ((req->cmd->arg >> 28) & 0x7); +	} else { +		cmd_val |= MMC_STOP_TRANSMISSION; +		arg = 0; +	} + +	mmc_writel(host, REG_CARG, arg); +	mmc_writel(host, REG_CMDR, cmd_val); + +	do { +		ri = mmc_readl(host, REG_RINTR); +	} while (!(ri & (SDXC_COMMAND_DONE | SDXC_INTERRUPT_ERROR_BIT)) && +		 time_before(jiffies, expire)); + +	if (!(ri & SDXC_COMMAND_DONE) || (ri & SDXC_INTERRUPT_ERROR_BIT)) { +		dev_err(mmc_dev(host->mmc), "send stop command failed\n"); +		if (req->stop) +			req->stop->resp[0] = -ETIMEDOUT; +	} else { +		if (req->stop) +			req->stop->resp[0] = mmc_readl(host, REG_RESP0); +	} + +	mmc_writel(host, REG_RINTR, 0xffff); +} + +static void sunxi_mmc_dump_errinfo(struct sunxi_mmc_host *host) +{ +	struct mmc_command *cmd = host->mrq->cmd; +	struct mmc_data *data = host->mrq->data; + +	/* For some cmds timeout is normal with sd/mmc cards */ +	if ((host->int_sum & SDXC_INTERRUPT_ERROR_BIT) == +		SDXC_RESP_TIMEOUT && (cmd->opcode == SD_IO_SEND_OP_COND || +				      cmd->opcode == SD_IO_RW_DIRECT)) +		return; + +	dev_err(mmc_dev(host->mmc), +		"smc %d err, cmd %d,%s%s%s%s%s%s%s%s%s%s !!\n", +		host->mmc->index, cmd->opcode, +		data ? (data->flags & MMC_DATA_WRITE ? " WR" : " RD") : "", +		host->int_sum & SDXC_RESP_ERROR     ? " RE"     : "", +		host->int_sum & SDXC_RESP_CRC_ERROR  ? " RCE"    : "", +		host->int_sum & SDXC_DATA_CRC_ERROR  ? " DCE"    : "", +		host->int_sum & SDXC_RESP_TIMEOUT ? " RTO"    : "", +		host->int_sum & SDXC_DATA_TIMEOUT ? " DTO"    : "", +		host->int_sum & SDXC_FIFO_RUN_ERROR  ? " FE"     : "", +		host->int_sum & SDXC_HARD_WARE_LOCKED ? " HL"     : "", +		host->int_sum & SDXC_START_BIT_ERROR ? " SBE"    : "", +		host->int_sum & SDXC_END_BIT_ERROR   ? " EBE"    : "" +		); +} + +/* Called in interrupt context! */ +static irqreturn_t sunxi_mmc_finalize_request(struct sunxi_mmc_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	struct mmc_data *data = mrq->data; +	u32 rval; + +	mmc_writel(host, REG_IMASK, host->sdio_imask); +	mmc_writel(host, REG_IDIE, 0); + +	if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT) { +		sunxi_mmc_dump_errinfo(host); +		mrq->cmd->error = -ETIMEDOUT; + +		if (data) { +			data->error = -ETIMEDOUT; +			host->manual_stop_mrq = mrq; +		} + +		if (mrq->stop) +			mrq->stop->error = -ETIMEDOUT; +	} else { +		if (mrq->cmd->flags & MMC_RSP_136) { +			mrq->cmd->resp[0] = mmc_readl(host, REG_RESP3); +			mrq->cmd->resp[1] = mmc_readl(host, REG_RESP2); +			mrq->cmd->resp[2] = mmc_readl(host, REG_RESP1); +			mrq->cmd->resp[3] = mmc_readl(host, REG_RESP0); +		} else { +			mrq->cmd->resp[0] = mmc_readl(host, REG_RESP0); +		} + +		if (data) +			data->bytes_xfered = data->blocks * data->blksz; +	} + +	if (data) { +		mmc_writel(host, REG_IDST, 0x337); +		mmc_writel(host, REG_DMAC, 0); +		rval = mmc_readl(host, REG_GCTRL); +		rval |= SDXC_DMA_RESET; +		mmc_writel(host, REG_GCTRL, rval); +		rval &= ~SDXC_DMA_ENABLE_BIT; +		mmc_writel(host, REG_GCTRL, rval); +		rval |= SDXC_FIFO_RESET; +		mmc_writel(host, REG_GCTRL, rval); +		dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, +				     sunxi_mmc_get_dma_dir(data)); +	} + +	mmc_writel(host, REG_RINTR, 0xffff); + +	host->mrq = NULL; +	host->int_sum = 0; +	host->wait_dma = false; + +	return host->manual_stop_mrq ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +static irqreturn_t sunxi_mmc_irq(int irq, void *dev_id) +{ +	struct sunxi_mmc_host *host = dev_id; +	struct mmc_request *mrq; +	u32 msk_int, idma_int; +	bool finalize = false; +	bool sdio_int = false; +	irqreturn_t ret = IRQ_HANDLED; + +	spin_lock(&host->lock); + +	idma_int  = mmc_readl(host, REG_IDST); +	msk_int   = mmc_readl(host, REG_MISTA); + +	dev_dbg(mmc_dev(host->mmc), "irq: rq %p mi %08x idi %08x\n", +		host->mrq, msk_int, idma_int); + +	mrq = host->mrq; +	if (mrq) { +		if (idma_int & SDXC_IDMAC_RECEIVE_INTERRUPT) +			host->wait_dma = false; + +		host->int_sum |= msk_int; + +		/* Wait for COMMAND_DONE on RESPONSE_TIMEOUT before finalize */ +		if ((host->int_sum & SDXC_RESP_TIMEOUT) && +				!(host->int_sum & SDXC_COMMAND_DONE)) +			mmc_writel(host, REG_IMASK, +				   host->sdio_imask | SDXC_COMMAND_DONE); +		/* Don't wait for dma on error */ +		else if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT) +			finalize = true; +		else if ((host->int_sum & SDXC_INTERRUPT_DONE_BIT) && +				!host->wait_dma) +			finalize = true; +	} + +	if (msk_int & SDXC_SDIO_INTERRUPT) +		sdio_int = true; + +	mmc_writel(host, REG_RINTR, msk_int); +	mmc_writel(host, REG_IDST, idma_int); + +	if (finalize) +		ret = sunxi_mmc_finalize_request(host); + +	spin_unlock(&host->lock); + +	if (finalize && ret == IRQ_HANDLED) +		mmc_request_done(host->mmc, mrq); + +	if (sdio_int) +		mmc_signal_sdio_irq(host->mmc); + +	return ret; +} + +static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id) +{ +	struct sunxi_mmc_host *host = dev_id; +	struct mmc_request *mrq; +	unsigned long iflags; + +	spin_lock_irqsave(&host->lock, iflags); +	mrq = host->manual_stop_mrq; +	spin_unlock_irqrestore(&host->lock, iflags); + +	if (!mrq) { +		dev_err(mmc_dev(host->mmc), "no request for manual stop\n"); +		return IRQ_HANDLED; +	} + +	dev_err(mmc_dev(host->mmc), "data error, sending stop command\n"); +	sunxi_mmc_send_manual_stop(host, mrq); + +	spin_lock_irqsave(&host->lock, iflags); +	host->manual_stop_mrq = NULL; +	spin_unlock_irqrestore(&host->lock, iflags); + +	mmc_request_done(host->mmc, mrq); + +	return IRQ_HANDLED; +} + +static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) +{ +	unsigned long expire = jiffies + msecs_to_jiffies(250); +	u32 rval; + +	rval = mmc_readl(host, REG_CLKCR); +	rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON); + +	if (oclk_en) +		rval |= SDXC_CARD_CLOCK_ON; + +	mmc_writel(host, REG_CLKCR, rval); + +	rval = SDXC_START | SDXC_UPCLK_ONLY | SDXC_WAIT_PRE_OVER; +	mmc_writel(host, REG_CMDR, rval); + +	do { +		rval = mmc_readl(host, REG_CMDR); +	} while (time_before(jiffies, expire) && (rval & SDXC_START)); + +	/* clear irq status bits set by the command */ +	mmc_writel(host, REG_RINTR, +		   mmc_readl(host, REG_RINTR) & ~SDXC_SDIO_INTERRUPT); + +	if (rval & SDXC_START) { +		dev_err(mmc_dev(host->mmc), "fatal err update clk timeout\n"); +		return -EIO; +	} + +	return 0; +} + +static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, +				  struct mmc_ios *ios) +{ +	u32 rate, oclk_dly, rval, sclk_dly, src_clk; +	int ret; + +	rate = clk_round_rate(host->clk_mmc, ios->clock); +	dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %d\n", +		ios->clock, rate); + +	/* setting clock rate */ +	ret = clk_set_rate(host->clk_mmc, rate); +	if (ret) { +		dev_err(mmc_dev(host->mmc), "error setting clk to %d: %d\n", +			rate, ret); +		return ret; +	} + +	ret = sunxi_mmc_oclk_onoff(host, 0); +	if (ret) +		return ret; + +	/* clear internal divider */ +	rval = mmc_readl(host, REG_CLKCR); +	rval &= ~0xff; +	mmc_writel(host, REG_CLKCR, rval); + +	/* determine delays */ +	if (rate <= 400000) { +		oclk_dly = 0; +		sclk_dly = 7; +	} else if (rate <= 25000000) { +		oclk_dly = 0; +		sclk_dly = 5; +	} else if (rate <= 50000000) { +		if (ios->timing == MMC_TIMING_UHS_DDR50) { +			oclk_dly = 2; +			sclk_dly = 4; +		} else { +			oclk_dly = 3; +			sclk_dly = 5; +		} +	} else { +		/* rate > 50000000 */ +		oclk_dly = 2; +		sclk_dly = 4; +	} + +	src_clk = clk_get_rate(clk_get_parent(host->clk_mmc)); +	if (src_clk >= 300000000 && src_clk <= 400000000) { +		if (oclk_dly) +			oclk_dly--; +		if (sclk_dly) +			sclk_dly--; +	} + +	clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly); + +	return sunxi_mmc_oclk_onoff(host, 1); +} + +static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct sunxi_mmc_host *host = mmc_priv(mmc); +	u32 rval; + +	/* Set the power state */ +	switch (ios->power_mode) { +	case MMC_POWER_ON: +		break; + +	case MMC_POWER_UP: +		mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + +		host->ferror = sunxi_mmc_init_host(mmc); +		if (host->ferror) +			return; + +		dev_dbg(mmc_dev(mmc), "power on!\n"); +		break; + +	case MMC_POWER_OFF: +		dev_dbg(mmc_dev(mmc), "power off!\n"); +		sunxi_mmc_reset_host(host); +		mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); +		break; +	} + +	/* set bus width */ +	switch (ios->bus_width) { +	case MMC_BUS_WIDTH_1: +		mmc_writel(host, REG_WIDTH, SDXC_WIDTH1); +		break; +	case MMC_BUS_WIDTH_4: +		mmc_writel(host, REG_WIDTH, SDXC_WIDTH4); +		break; +	case MMC_BUS_WIDTH_8: +		mmc_writel(host, REG_WIDTH, SDXC_WIDTH8); +		break; +	} + +	/* set ddr mode */ +	rval = mmc_readl(host, REG_GCTRL); +	if (ios->timing == MMC_TIMING_UHS_DDR50) +		rval |= SDXC_DDR_MODE; +	else +		rval &= ~SDXC_DDR_MODE; +	mmc_writel(host, REG_GCTRL, rval); + +	/* set up clock */ +	if (ios->clock && ios->power_mode) { +		host->ferror = sunxi_mmc_clk_set_rate(host, ios); +		/* Android code had a usleep_range(50000, 55000); here */ +	} +} + +static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ +	struct sunxi_mmc_host *host = mmc_priv(mmc); +	unsigned long flags; +	u32 imask; + +	spin_lock_irqsave(&host->lock, flags); + +	imask = mmc_readl(host, REG_IMASK); +	if (enable) { +		host->sdio_imask = SDXC_SDIO_INTERRUPT; +		imask |= SDXC_SDIO_INTERRUPT; +	} else { +		host->sdio_imask = 0; +		imask &= ~SDXC_SDIO_INTERRUPT; +	} +	mmc_writel(host, REG_IMASK, imask); +	spin_unlock_irqrestore(&host->lock, flags); +} + +static void sunxi_mmc_hw_reset(struct mmc_host *mmc) +{ +	struct sunxi_mmc_host *host = mmc_priv(mmc); +	mmc_writel(host, REG_HWRST, 0); +	udelay(10); +	mmc_writel(host, REG_HWRST, 1); +	udelay(300); +} + +static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct sunxi_mmc_host *host = mmc_priv(mmc); +	struct mmc_command *cmd = mrq->cmd; +	struct mmc_data *data = mrq->data; +	unsigned long iflags; +	u32 imask = SDXC_INTERRUPT_ERROR_BIT; +	u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f); +	int ret; + +	/* Check for set_ios errors (should never happen) */ +	if (host->ferror) { +		mrq->cmd->error = host->ferror; +		mmc_request_done(mmc, mrq); +		return; +	} + +	if (data) { +		ret = sunxi_mmc_map_dma(host, data); +		if (ret < 0) { +			dev_err(mmc_dev(mmc), "map DMA failed\n"); +			cmd->error = ret; +			data->error = ret; +			mmc_request_done(mmc, mrq); +			return; +		} +	} + +	if (cmd->opcode == MMC_GO_IDLE_STATE) { +		cmd_val |= SDXC_SEND_INIT_SEQUENCE; +		imask |= SDXC_COMMAND_DONE; +	} + +	if (cmd->flags & MMC_RSP_PRESENT) { +		cmd_val |= SDXC_RESP_EXPIRE; +		if (cmd->flags & MMC_RSP_136) +			cmd_val |= SDXC_LONG_RESPONSE; +		if (cmd->flags & MMC_RSP_CRC) +			cmd_val |= SDXC_CHECK_RESPONSE_CRC; + +		if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) { +			cmd_val |= SDXC_DATA_EXPIRE | SDXC_WAIT_PRE_OVER; +			if (cmd->data->flags & MMC_DATA_STREAM) { +				imask |= SDXC_AUTO_COMMAND_DONE; +				cmd_val |= SDXC_SEQUENCE_MODE | +					   SDXC_SEND_AUTO_STOP; +			} + +			if (cmd->data->stop) { +				imask |= SDXC_AUTO_COMMAND_DONE; +				cmd_val |= SDXC_SEND_AUTO_STOP; +			} else { +				imask |= SDXC_DATA_OVER; +			} + +			if (cmd->data->flags & MMC_DATA_WRITE) +				cmd_val |= SDXC_WRITE; +			else +				host->wait_dma = true; +		} else { +			imask |= SDXC_COMMAND_DONE; +		} +	} else { +		imask |= SDXC_COMMAND_DONE; +	} + +	dev_dbg(mmc_dev(mmc), "cmd %d(%08x) arg %x ie 0x%08x len %d\n", +		cmd_val & 0x3f, cmd_val, cmd->arg, imask, +		mrq->data ? mrq->data->blksz * mrq->data->blocks : 0); + +	spin_lock_irqsave(&host->lock, iflags); + +	if (host->mrq || host->manual_stop_mrq) { +		spin_unlock_irqrestore(&host->lock, iflags); + +		if (data) +			dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, +				     sunxi_mmc_get_dma_dir(data)); + +		dev_err(mmc_dev(mmc), "request already pending\n"); +		mrq->cmd->error = -EBUSY; +		mmc_request_done(mmc, mrq); +		return; +	} + +	if (data) { +		mmc_writel(host, REG_BLKSZ, data->blksz); +		mmc_writel(host, REG_BCNTR, data->blksz * data->blocks); +		sunxi_mmc_start_dma(host, data); +	} + +	host->mrq = mrq; +	mmc_writel(host, REG_IMASK, host->sdio_imask | imask); +	mmc_writel(host, REG_CARG, cmd->arg); +	mmc_writel(host, REG_CMDR, cmd_val); + +	spin_unlock_irqrestore(&host->lock, iflags); +} + +static const struct of_device_id sunxi_mmc_of_match[] = { +	{ .compatible = "allwinner,sun4i-a10-mmc", }, +	{ .compatible = "allwinner,sun5i-a13-mmc", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); + +static struct mmc_host_ops sunxi_mmc_ops = { +	.request	 = sunxi_mmc_request, +	.set_ios	 = sunxi_mmc_set_ios, +	.get_ro		 = mmc_gpio_get_ro, +	.get_cd		 = mmc_gpio_get_cd, +	.enable_sdio_irq = sunxi_mmc_enable_sdio_irq, +	.hw_reset	 = sunxi_mmc_hw_reset, +}; + +static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, +				      struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	int ret; + +	if (of_device_is_compatible(np, "allwinner,sun4i-a10-mmc")) +		host->idma_des_size_bits = 13; +	else +		host->idma_des_size_bits = 16; + +	ret = mmc_regulator_get_supply(host->mmc); +	if (ret) { +		if (ret != -EPROBE_DEFER) +			dev_err(&pdev->dev, "Could not get vmmc supply\n"); +		return ret; +	} + +	host->reg_base = devm_ioremap_resource(&pdev->dev, +			      platform_get_resource(pdev, IORESOURCE_MEM, 0)); +	if (IS_ERR(host->reg_base)) +		return PTR_ERR(host->reg_base); + +	host->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); +	if (IS_ERR(host->clk_ahb)) { +		dev_err(&pdev->dev, "Could not get ahb clock\n"); +		return PTR_ERR(host->clk_ahb); +	} + +	host->clk_mmc = devm_clk_get(&pdev->dev, "mmc"); +	if (IS_ERR(host->clk_mmc)) { +		dev_err(&pdev->dev, "Could not get mmc clock\n"); +		return PTR_ERR(host->clk_mmc); +	} + +	host->reset = devm_reset_control_get(&pdev->dev, "ahb"); + +	ret = clk_prepare_enable(host->clk_ahb); +	if (ret) { +		dev_err(&pdev->dev, "Enable ahb clk err %d\n", ret); +		return ret; +	} + +	ret = clk_prepare_enable(host->clk_mmc); +	if (ret) { +		dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret); +		goto error_disable_clk_ahb; +	} + +	if (!IS_ERR(host->reset)) { +		ret = reset_control_deassert(host->reset); +		if (ret) { +			dev_err(&pdev->dev, "reset err %d\n", ret); +			goto error_disable_clk_mmc; +		} +	} + +	/* +	 * Sometimes the controller asserts the irq on boot for some reason, +	 * make sure the controller is in a sane state before enabling irqs. +	 */ +	ret = sunxi_mmc_reset_host(host); +	if (ret) +		goto error_assert_reset; + +	host->irq = platform_get_irq(pdev, 0); +	return devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq, +			sunxi_mmc_handle_manual_stop, 0, "sunxi-mmc", host); + +error_assert_reset: +	if (!IS_ERR(host->reset)) +		reset_control_assert(host->reset); +error_disable_clk_mmc: +	clk_disable_unprepare(host->clk_mmc); +error_disable_clk_ahb: +	clk_disable_unprepare(host->clk_ahb); +	return ret; +} + +static int sunxi_mmc_probe(struct platform_device *pdev) +{ +	struct sunxi_mmc_host *host; +	struct mmc_host *mmc; +	int ret; + +	mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev); +	if (!mmc) { +		dev_err(&pdev->dev, "mmc alloc host failed\n"); +		return -ENOMEM; +	} + +	host = mmc_priv(mmc); +	host->mmc = mmc; +	spin_lock_init(&host->lock); + +	ret = sunxi_mmc_resource_request(host, pdev); +	if (ret) +		goto error_free_host; + +	host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, +					  &host->sg_dma, GFP_KERNEL); +	if (!host->sg_cpu) { +		dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n"); +		ret = -ENOMEM; +		goto error_free_host; +	} + +	mmc->ops		= &sunxi_mmc_ops; +	mmc->max_blk_count	= 8192; +	mmc->max_blk_size	= 4096; +	mmc->max_segs		= PAGE_SIZE / sizeof(struct sunxi_idma_des); +	mmc->max_seg_size	= (1 << host->idma_des_size_bits); +	mmc->max_req_size	= mmc->max_seg_size * mmc->max_segs; +	/* 400kHz ~ 50MHz */ +	mmc->f_min		=   400000; +	mmc->f_max		= 50000000; +	mmc->caps	       |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; + +	ret = mmc_of_parse(mmc); +	if (ret) +		goto error_free_dma; + +	ret = mmc_add_host(mmc); +	if (ret) +		goto error_free_dma; + +	dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq); +	platform_set_drvdata(pdev, mmc); +	return 0; + +error_free_dma: +	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); +error_free_host: +	mmc_free_host(mmc); +	return ret; +} + +static int sunxi_mmc_remove(struct platform_device *pdev) +{ +	struct mmc_host	*mmc = platform_get_drvdata(pdev); +	struct sunxi_mmc_host *host = mmc_priv(mmc); + +	mmc_remove_host(mmc); +	disable_irq(host->irq); +	sunxi_mmc_reset_host(host); + +	if (!IS_ERR(host->reset)) +		reset_control_assert(host->reset); + +	clk_disable_unprepare(host->clk_mmc); +	clk_disable_unprepare(host->clk_ahb); + +	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); +	mmc_free_host(mmc); + +	return 0; +} + +static struct platform_driver sunxi_mmc_driver = { +	.driver = { +		.name	= "sunxi-mmc", +		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(sunxi_mmc_of_match), +	}, +	.probe		= sunxi_mmc_probe, +	.remove		= sunxi_mmc_remove, +}; +module_platform_driver(sunxi_mmc_driver); + +MODULE_DESCRIPTION("Allwinner's SD/MMC Card Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("David Lanzend�rfer <david.lanzendoerfer@o2s.ch>"); +MODULE_ALIAS("platform:sunxi-mmc"); diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 43d962829f8..d1760ebcac0 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -1030,7 +1030,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)  static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state)  { -	return mmc_suspend_host(tifm_get_drvdata(sock)); +	return 0;  }  static int tifm_sd_resume(struct tifm_dev *sock) @@ -1044,8 +1044,6 @@ static int tifm_sd_resume(struct tifm_dev *sock)  	if (rc)  		host->eject = 1; -	else -		rc = mmc_resume_host(mmc);  	return rc;  } diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 8860d4d2bc2..cfad844730d 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -23,38 +23,37 @@  #include "tmio_mmc.h" -#ifdef CONFIG_PM -static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tmio_mmc_suspend(struct device *dev)  { -	const struct mfd_cell *cell = mfd_get_cell(dev); +	struct platform_device *pdev = to_platform_device(dev); +	const struct mfd_cell *cell = mfd_get_cell(pdev);  	int ret; -	ret = tmio_mmc_host_suspend(&dev->dev); +	ret = tmio_mmc_host_suspend(dev);  	/* Tell MFD core it can disable us now.*/  	if (!ret && cell->disable) -		cell->disable(dev); +		cell->disable(pdev);  	return ret;  } -static int tmio_mmc_resume(struct platform_device *dev) +static int tmio_mmc_resume(struct device *dev)  { -	const struct mfd_cell *cell = mfd_get_cell(dev); +	struct platform_device *pdev = to_platform_device(dev); +	const struct mfd_cell *cell = mfd_get_cell(pdev);  	int ret = 0;  	/* Tell the MFD core we are ready to be enabled */  	if (cell->resume) -		ret = cell->resume(dev); +		ret = cell->resume(pdev);  	if (!ret) -		ret = tmio_mmc_host_resume(&dev->dev); +		ret = tmio_mmc_host_resume(dev);  	return ret;  } -#else -#define tmio_mmc_suspend NULL -#define tmio_mmc_resume NULL  #endif  static int tmio_mmc_probe(struct platform_device *pdev) @@ -62,6 +61,7 @@ static int tmio_mmc_probe(struct platform_device *pdev)  	const struct mfd_cell *cell = mfd_get_cell(pdev);  	struct tmio_mmc_data *pdata;  	struct tmio_mmc_host *host; +	struct resource *res;  	int ret = -EINVAL, irq;  	if (pdev->num_resources != 2) @@ -84,6 +84,14 @@ static int tmio_mmc_probe(struct platform_device *pdev)  			goto out;  	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) +		return -EINVAL; + +	/* SD control register space size is 0x200, 0x400 for bus_shift=1 */ +	pdata->bus_shift = resource_size(res) >> 10; +	pdata->flags |= TMIO_MMC_HAVE_HIGH_REG; +  	ret = tmio_mmc_host_probe(&host, pdev, pdata);  	if (ret)  		goto cell_disable; @@ -125,15 +133,18 @@ static int tmio_mmc_remove(struct platform_device *pdev)  /* ------------------- device registration ----------------------- */ +static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { +	SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume) +}; +  static struct platform_driver tmio_mmc_driver = {  	.driver = {  		.name = "tmio-mmc",  		.owner = THIS_MODULE, +		.pm = &tmio_mmc_dev_pm_ops,  	},  	.probe = tmio_mmc_probe,  	.remove = tmio_mmc_remove, -	.suspend = tmio_mmc_suspend, -	.resume = tmio_mmc_resume,  };  module_platform_driver(tmio_mmc_driver); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 86fd21e0009..100ffe0b2fa 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -58,7 +58,6 @@ enum tmio_mmc_power {  struct tmio_mmc_host {  	void __iomem *ctl; -	unsigned long bus_shift;  	struct mmc_command      *cmd;  	struct mmc_request      *mrq;  	struct mmc_data         *data; @@ -163,32 +162,31 @@ static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host)  }  #endif -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  int tmio_mmc_host_suspend(struct device *dev);  int tmio_mmc_host_resume(struct device *dev); -#else -#define tmio_mmc_host_suspend NULL -#define tmio_mmc_host_resume NULL  #endif +#ifdef CONFIG_PM_RUNTIME  int tmio_mmc_host_runtime_suspend(struct device *dev);  int tmio_mmc_host_runtime_resume(struct device *dev); +#endif  static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)  { -	return readw(host->ctl + (addr << host->bus_shift)); +	return readw(host->ctl + (addr << host->pdata->bus_shift));  }  static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,  		u16 *buf, int count)  { -	readsw(host->ctl + (addr << host->bus_shift), buf, count); +	readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count);  }  static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)  { -	return readw(host->ctl + (addr << host->bus_shift)) | -	       readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16; +	return readw(host->ctl + (addr << host->pdata->bus_shift)) | +	       readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16;  }  static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val) @@ -198,19 +196,19 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val  	 */  	if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))  		return; -	writew(val, host->ctl + (addr << host->bus_shift)); +	writew(val, host->ctl + (addr << host->pdata->bus_shift));  }  static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,  		u16 *buf, int count)  { -	writesw(host->ctl + (addr << host->bus_shift), buf, count); +	writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count);  }  static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)  { -	writew(val, host->ctl + (addr << host->bus_shift)); -	writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); +	writew(val, host->ctl + (addr << host->pdata->bus_shift)); +	writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift));  } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 65edb4a6245..03e7b280cb4 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -293,7 +293,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat  		if (pdata->dma->chan_priv_tx)  			cfg.slave_id = pdata->dma->slave_id_tx;  		cfg.direction = DMA_MEM_TO_DEV; -		cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); +		cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);  		cfg.src_addr = 0;  		ret = dmaengine_slave_config(host->chan_tx, &cfg);  		if (ret < 0) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b3802256f95..faf0924e71c 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -161,10 +161,8 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)  static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)  { -	struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); -  	/* implicit BUG_ON(!res) */ -	if (resource_size(res) > 0x100) { +	if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {  		sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);  		msleep(10);  	} @@ -176,14 +174,12 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)  static void tmio_mmc_clk_start(struct tmio_mmc_host *host)  { -	struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); -  	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |  		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));  	msleep(10);  	/* implicit BUG_ON(!res) */ -	if (resource_size(res) > 0x100) { +	if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {  		sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);  		msleep(10);  	} @@ -191,16 +187,14 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)  static void tmio_mmc_reset(struct tmio_mmc_host *host)  { -	struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); -  	/* FIXME - should we set stop clock reg here */  	sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);  	/* implicit BUG_ON(!res) */ -	if (resource_size(res) > 0x100) +	if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)  		sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);  	msleep(10);  	sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); -	if (resource_size(res) > 0x100) +	if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)  		sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);  	msleep(10);  } @@ -944,17 +938,25 @@ static const struct mmc_host_ops tmio_mmc_ops = {  	.enable_sdio_irq = tmio_mmc_enable_sdio_irq,  }; -static void tmio_mmc_init_ocr(struct tmio_mmc_host *host) +static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)  {  	struct tmio_mmc_data *pdata = host->pdata;  	struct mmc_host *mmc = host->mmc;  	mmc_regulator_get_supply(mmc); +	/* use ocr_mask if no regulator */ +	if (!mmc->ocr_avail) +		mmc->ocr_avail =  pdata->ocr_mask; + +	/* +	 * try again. +	 * There is possibility that regulator has not been probed +	 */  	if (!mmc->ocr_avail) -		mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34; -	else if (pdata->ocr_mask) -		dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); +		return -EPROBE_DEFER; + +	return 0;  }  static void tmio_mmc_of_parse(struct platform_device *pdev, @@ -1005,8 +1007,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,  	_host->set_pwr = pdata->set_pwr;  	_host->set_clk_div = pdata->set_clk_div; -	/* SD control register space size is 0x200, 0x400 for bus_shift=1 */ -	_host->bus_shift = resource_size(res_ctl) >> 10; +	ret = tmio_mmc_init_ocr(_host); +	if (ret < 0) +		goto host_free;  	_host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));  	if (!_host->ctl) { @@ -1016,14 +1019,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,  	mmc->ops = &tmio_mmc_ops;  	mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities; -	mmc->caps2 = pdata->capabilities2; +	mmc->caps2 |= pdata->capabilities2;  	mmc->max_segs = 32;  	mmc->max_blk_size = 512;  	mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *  		mmc->max_segs;  	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;  	mmc->max_seg_size = mmc->max_req_size; -	tmio_mmc_init_ocr(_host);  	_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||  				  mmc->caps & MMC_CAP_NEEDS_POLL || @@ -1140,17 +1142,14 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)  }  EXPORT_SYMBOL(tmio_mmc_host_remove); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  int tmio_mmc_host_suspend(struct device *dev)  {  	struct mmc_host *mmc = dev_get_drvdata(dev);  	struct tmio_mmc_host *host = mmc_priv(mmc); -	int ret = mmc_suspend_host(mmc); - -	if (!ret) -		tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); -	return ret; +	tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); +	return 0;  }  EXPORT_SYMBOL(tmio_mmc_host_suspend); @@ -1163,12 +1162,12 @@ int tmio_mmc_host_resume(struct device *dev)  	/* The MMC core will perform the complete set up */  	host->resuming = true; -	return mmc_resume_host(mmc); +	return 0;  }  EXPORT_SYMBOL(tmio_mmc_host_resume); +#endif -#endif	/* CONFIG_PM */ - +#ifdef CONFIG_PM_RUNTIME  int tmio_mmc_host_runtime_suspend(struct device *dev)  {  	return 0; @@ -1185,5 +1184,6 @@ int tmio_mmc_host_runtime_resume(struct device *dev)  	return 0;  }  EXPORT_SYMBOL(tmio_mmc_host_runtime_resume); +#endif  MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c new file mode 100644 index 00000000000..f0a39eb049a --- /dev/null +++ b/drivers/mmc/host/usdhi6rol0.c @@ -0,0 +1,1847 @@ +/* + * Copyright (C) 2013-2014 Renesas Electronics Europe Ltd. + * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/highmem.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/log2.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/sdio.h> +#include <linux/module.h> +#include <linux/pagemap.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> +#include <linux/string.h> +#include <linux/time.h> +#include <linux/virtio.h> +#include <linux/workqueue.h> + +#define USDHI6_SD_CMD		0x0000 +#define USDHI6_SD_PORT_SEL	0x0004 +#define USDHI6_SD_ARG		0x0008 +#define USDHI6_SD_STOP		0x0010 +#define USDHI6_SD_SECCNT	0x0014 +#define USDHI6_SD_RSP10		0x0018 +#define USDHI6_SD_RSP32		0x0020 +#define USDHI6_SD_RSP54		0x0028 +#define USDHI6_SD_RSP76		0x0030 +#define USDHI6_SD_INFO1		0x0038 +#define USDHI6_SD_INFO2		0x003c +#define USDHI6_SD_INFO1_MASK	0x0040 +#define USDHI6_SD_INFO2_MASK	0x0044 +#define USDHI6_SD_CLK_CTRL	0x0048 +#define USDHI6_SD_SIZE		0x004c +#define USDHI6_SD_OPTION	0x0050 +#define USDHI6_SD_ERR_STS1	0x0058 +#define USDHI6_SD_ERR_STS2	0x005c +#define USDHI6_SD_BUF0		0x0060 +#define USDHI6_SDIO_MODE	0x0068 +#define USDHI6_SDIO_INFO1	0x006c +#define USDHI6_SDIO_INFO1_MASK	0x0070 +#define USDHI6_CC_EXT_MODE	0x01b0 +#define USDHI6_SOFT_RST		0x01c0 +#define USDHI6_VERSION		0x01c4 +#define USDHI6_HOST_MODE	0x01c8 +#define USDHI6_SDIF_MODE	0x01cc + +#define USDHI6_SD_CMD_APP		0x0040 +#define USDHI6_SD_CMD_MODE_RSP_AUTO	0x0000 +#define USDHI6_SD_CMD_MODE_RSP_NONE	0x0300 +#define USDHI6_SD_CMD_MODE_RSP_R1	0x0400	/* Also R5, R6, R7 */ +#define USDHI6_SD_CMD_MODE_RSP_R1B	0x0500	/* R1b */ +#define USDHI6_SD_CMD_MODE_RSP_R2	0x0600 +#define USDHI6_SD_CMD_MODE_RSP_R3	0x0700	/* Also R4 */ +#define USDHI6_SD_CMD_DATA		0x0800 +#define USDHI6_SD_CMD_READ		0x1000 +#define USDHI6_SD_CMD_MULTI		0x2000 +#define USDHI6_SD_CMD_CMD12_AUTO_OFF	0x4000 + +#define USDHI6_CC_EXT_MODE_SDRW		BIT(1) + +#define USDHI6_SD_INFO1_RSP_END		BIT(0) +#define USDHI6_SD_INFO1_ACCESS_END	BIT(2) +#define USDHI6_SD_INFO1_CARD_OUT	BIT(3) +#define USDHI6_SD_INFO1_CARD_IN		BIT(4) +#define USDHI6_SD_INFO1_CD		BIT(5) +#define USDHI6_SD_INFO1_WP		BIT(7) +#define USDHI6_SD_INFO1_D3_CARD_OUT	BIT(8) +#define USDHI6_SD_INFO1_D3_CARD_IN	BIT(9) + +#define USDHI6_SD_INFO2_CMD_ERR		BIT(0) +#define USDHI6_SD_INFO2_CRC_ERR		BIT(1) +#define USDHI6_SD_INFO2_END_ERR		BIT(2) +#define USDHI6_SD_INFO2_TOUT		BIT(3) +#define USDHI6_SD_INFO2_IWA_ERR		BIT(4) +#define USDHI6_SD_INFO2_IRA_ERR		BIT(5) +#define USDHI6_SD_INFO2_RSP_TOUT	BIT(6) +#define USDHI6_SD_INFO2_SDDAT0		BIT(7) +#define USDHI6_SD_INFO2_BRE		BIT(8) +#define USDHI6_SD_INFO2_BWE		BIT(9) +#define USDHI6_SD_INFO2_SCLKDIVEN	BIT(13) +#define USDHI6_SD_INFO2_CBSY		BIT(14) +#define USDHI6_SD_INFO2_ILA		BIT(15) + +#define USDHI6_SD_INFO1_CARD_INSERT (USDHI6_SD_INFO1_CARD_IN | USDHI6_SD_INFO1_D3_CARD_IN) +#define USDHI6_SD_INFO1_CARD_EJECT (USDHI6_SD_INFO1_CARD_OUT | USDHI6_SD_INFO1_D3_CARD_OUT) +#define USDHI6_SD_INFO1_CARD (USDHI6_SD_INFO1_CARD_INSERT | USDHI6_SD_INFO1_CARD_EJECT) +#define USDHI6_SD_INFO1_CARD_CD (USDHI6_SD_INFO1_CARD_IN | USDHI6_SD_INFO1_CARD_OUT) + +#define USDHI6_SD_INFO2_ERR	(USDHI6_SD_INFO2_CMD_ERR |	\ +	USDHI6_SD_INFO2_CRC_ERR | USDHI6_SD_INFO2_END_ERR |	\ +	USDHI6_SD_INFO2_TOUT | USDHI6_SD_INFO2_IWA_ERR |	\ +	USDHI6_SD_INFO2_IRA_ERR | USDHI6_SD_INFO2_RSP_TOUT |	\ +	USDHI6_SD_INFO2_ILA) + +#define USDHI6_SD_INFO1_IRQ	(USDHI6_SD_INFO1_RSP_END | USDHI6_SD_INFO1_ACCESS_END | \ +				 USDHI6_SD_INFO1_CARD) + +#define USDHI6_SD_INFO2_IRQ	(USDHI6_SD_INFO2_ERR | USDHI6_SD_INFO2_BRE | \ +				 USDHI6_SD_INFO2_BWE | 0x0800 | USDHI6_SD_INFO2_ILA) + +#define USDHI6_SD_CLK_CTRL_SCLKEN	BIT(8) + +#define USDHI6_SD_STOP_STP		BIT(0) +#define USDHI6_SD_STOP_SEC		BIT(8) + +#define USDHI6_SDIO_INFO1_IOIRQ		BIT(0) +#define USDHI6_SDIO_INFO1_EXPUB52	BIT(14) +#define USDHI6_SDIO_INFO1_EXWT		BIT(15) + +#define USDHI6_SD_ERR_STS1_CRC_NO_ERROR	BIT(13) + +#define USDHI6_SOFT_RST_RESERVED	(BIT(1) | BIT(2)) +#define USDHI6_SOFT_RST_RESET		BIT(0) + +#define USDHI6_SD_OPTION_TIMEOUT_SHIFT	4 +#define USDHI6_SD_OPTION_TIMEOUT_MASK	(0xf << USDHI6_SD_OPTION_TIMEOUT_SHIFT) +#define USDHI6_SD_OPTION_WIDTH_1	BIT(15) + +#define USDHI6_SD_PORT_SEL_PORTS_SHIFT	8 + +#define USDHI6_SD_CLK_CTRL_DIV_MASK	0xff + +#define USDHI6_SDIO_INFO1_IRQ	(USDHI6_SDIO_INFO1_IOIRQ | 3 | \ +				 USDHI6_SDIO_INFO1_EXPUB52 | USDHI6_SDIO_INFO1_EXWT) + +#define USDHI6_MIN_DMA 64 + +enum usdhi6_wait_for { +	USDHI6_WAIT_FOR_REQUEST, +	USDHI6_WAIT_FOR_CMD, +	USDHI6_WAIT_FOR_MREAD, +	USDHI6_WAIT_FOR_MWRITE, +	USDHI6_WAIT_FOR_READ, +	USDHI6_WAIT_FOR_WRITE, +	USDHI6_WAIT_FOR_DATA_END, +	USDHI6_WAIT_FOR_STOP, +	USDHI6_WAIT_FOR_DMA, +}; + +struct usdhi6_page { +	struct page *page; +	void *mapped;		/* mapped page */ +}; + +struct usdhi6_host { +	struct mmc_host *mmc; +	struct mmc_request *mrq; +	void __iomem *base; +	struct clk *clk; + +	/* SG memory handling */ + +	/* Common for multiple and single block requests */ +	struct usdhi6_page pg;	/* current page from an SG */ +	void *blk_page;		/* either a mapped page, or the bounce buffer */ +	size_t offset;		/* offset within a page, including sg->offset */ + +	/* Blocks, crossing a page boundary */ +	size_t head_len; +	struct usdhi6_page head_pg; + +	/* A bounce buffer for unaligned blocks or blocks, crossing a page boundary */ +	struct scatterlist bounce_sg; +	u8 bounce_buf[512]; + +	/* Multiple block requests only */ +	struct scatterlist *sg;	/* current SG segment */ +	int page_idx;		/* page index within an SG segment */ + +	enum usdhi6_wait_for wait; +	u32 status_mask; +	u32 status2_mask; +	u32 sdio_mask; +	u32 io_error; +	u32 irq_status; +	unsigned long imclk; +	unsigned long rate; +	bool app_cmd; + +	/* Timeout handling */ +	struct delayed_work timeout_work; +	unsigned long timeout; + +	/* DMA support */ +	struct dma_chan *chan_rx; +	struct dma_chan *chan_tx; +	bool dma_active; +}; + +/*			I/O primitives					*/ + +static void usdhi6_write(struct usdhi6_host *host, u32 reg, u32 data) +{ +	iowrite32(data, host->base + reg); +	dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, +		host->base, reg, data); +} + +static void usdhi6_write16(struct usdhi6_host *host, u32 reg, u16 data) +{ +	iowrite16(data, host->base + reg); +	dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, +		host->base, reg, data); +} + +static u32 usdhi6_read(struct usdhi6_host *host, u32 reg) +{ +	u32 data = ioread32(host->base + reg); +	dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, +		host->base, reg, data); +	return data; +} + +static u16 usdhi6_read16(struct usdhi6_host *host, u32 reg) +{ +	u16 data = ioread16(host->base + reg); +	dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, +		host->base, reg, data); +	return data; +} + +static void usdhi6_irq_enable(struct usdhi6_host *host, u32 info1, u32 info2) +{ +	host->status_mask = USDHI6_SD_INFO1_IRQ & ~info1; +	host->status2_mask = USDHI6_SD_INFO2_IRQ & ~info2; +	usdhi6_write(host, USDHI6_SD_INFO1_MASK, host->status_mask); +	usdhi6_write(host, USDHI6_SD_INFO2_MASK, host->status2_mask); +} + +static void usdhi6_wait_for_resp(struct usdhi6_host *host) +{ +	usdhi6_irq_enable(host, USDHI6_SD_INFO1_RSP_END | +			  USDHI6_SD_INFO1_ACCESS_END | USDHI6_SD_INFO1_CARD_CD, +			  USDHI6_SD_INFO2_ERR); +} + +static void usdhi6_wait_for_brwe(struct usdhi6_host *host, bool read) +{ +	usdhi6_irq_enable(host, USDHI6_SD_INFO1_ACCESS_END | +			  USDHI6_SD_INFO1_CARD_CD, USDHI6_SD_INFO2_ERR | +			  (read ? USDHI6_SD_INFO2_BRE : USDHI6_SD_INFO2_BWE)); +} + +static void usdhi6_only_cd(struct usdhi6_host *host) +{ +	/* Mask all except card hotplug */ +	usdhi6_irq_enable(host, USDHI6_SD_INFO1_CARD_CD, 0); +} + +static void usdhi6_mask_all(struct usdhi6_host *host) +{ +	usdhi6_irq_enable(host, 0, 0); +} + +static int usdhi6_error_code(struct usdhi6_host *host) +{ +	u32 err; + +	usdhi6_write(host, USDHI6_SD_STOP, USDHI6_SD_STOP_STP); + +	if (host->io_error & +	    (USDHI6_SD_INFO2_RSP_TOUT | USDHI6_SD_INFO2_TOUT)) { +		u32 rsp54 = usdhi6_read(host, USDHI6_SD_RSP54); +		int opc = host->mrq ? host->mrq->cmd->opcode : -1; + +		err = usdhi6_read(host, USDHI6_SD_ERR_STS2); +		/* Response timeout is often normal, don't spam the log */ +		if (host->wait == USDHI6_WAIT_FOR_CMD) +			dev_dbg(mmc_dev(host->mmc), +				"T-out sts 0x%x, resp 0x%x, state %u, CMD%d\n", +				err, rsp54, host->wait, opc); +		else +			dev_warn(mmc_dev(host->mmc), +				 "T-out sts 0x%x, resp 0x%x, state %u, CMD%d\n", +				 err, rsp54, host->wait, opc); +		return -ETIMEDOUT; +	} + +	err = usdhi6_read(host, USDHI6_SD_ERR_STS1); +	if (err != USDHI6_SD_ERR_STS1_CRC_NO_ERROR) +		dev_warn(mmc_dev(host->mmc), "Err sts 0x%x, state %u, CMD%d\n", +			 err, host->wait, host->mrq ? host->mrq->cmd->opcode : -1); +	if (host->io_error & USDHI6_SD_INFO2_ILA) +		return -EILSEQ; + +	return -EIO; +} + +/*			Scatter-Gather management			*/ + +/* + * In PIO mode we have to map each page separately, using kmap(). That way + * adjacent pages are mapped to non-adjacent virtual addresses. That's why we + * have to use a bounce buffer for blocks, crossing page boundaries. Such blocks + * have been observed with an SDIO WiFi card (b43 driver). + */ +static void usdhi6_blk_bounce(struct usdhi6_host *host, +			      struct scatterlist *sg) +{ +	struct mmc_data *data = host->mrq->data; +	size_t blk_head = host->head_len; + +	dev_dbg(mmc_dev(host->mmc), "%s(): CMD%u of %u SG: %ux%u @ 0x%x\n", +		__func__, host->mrq->cmd->opcode, data->sg_len, +		data->blksz, data->blocks, sg->offset); + +	host->head_pg.page	= host->pg.page; +	host->head_pg.mapped	= host->pg.mapped; +	host->pg.page		= nth_page(host->pg.page, 1); +	host->pg.mapped		= kmap(host->pg.page); + +	host->blk_page = host->bounce_buf; +	host->offset = 0; + +	if (data->flags & MMC_DATA_READ) +		return; + +	memcpy(host->bounce_buf, host->head_pg.mapped + PAGE_SIZE - blk_head, +	       blk_head); +	memcpy(host->bounce_buf + blk_head, host->pg.mapped, +	       data->blksz - blk_head); +} + +/* Only called for multiple block IO */ +static void usdhi6_sg_prep(struct usdhi6_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	struct mmc_data *data = mrq->data; + +	usdhi6_write(host, USDHI6_SD_SECCNT, data->blocks); + +	host->sg = data->sg; +	/* TODO: if we always map, this is redundant */ +	host->offset = host->sg->offset; +} + +/* Map the first page in an SG segment: common for multiple and single block IO */ +static void *usdhi6_sg_map(struct usdhi6_host *host) +{ +	struct mmc_data *data = host->mrq->data; +	struct scatterlist *sg = data->sg_len > 1 ? host->sg : data->sg; +	size_t head = PAGE_SIZE - sg->offset; +	size_t blk_head = head % data->blksz; + +	WARN(host->pg.page, "%p not properly unmapped!\n", host->pg.page); +	if (WARN(sg_dma_len(sg) % data->blksz, +		 "SG size %u isn't a multiple of block size %u\n", +		 sg_dma_len(sg), data->blksz)) +		return NULL; + +	host->pg.page = sg_page(sg); +	host->pg.mapped = kmap(host->pg.page); +	host->offset = sg->offset; + +	/* +	 * Block size must be a power of 2 for multi-block transfers, +	 * therefore blk_head is equal for all pages in this SG +	 */ +	host->head_len = blk_head; + +	if (head < data->blksz) +		/* +		 * The first block in the SG crosses a page boundary. +		 * Max blksz = 512, so blocks can only span 2 pages +		 */ +		usdhi6_blk_bounce(host, sg); +	else +		host->blk_page = host->pg.mapped; + +	dev_dbg(mmc_dev(host->mmc), "Mapped %p (%lx) at %p + %u for CMD%u @ 0x%p\n", +		host->pg.page, page_to_pfn(host->pg.page), host->pg.mapped, +		sg->offset, host->mrq->cmd->opcode, host->mrq); + +	return host->blk_page + host->offset; +} + +/* Unmap the current page: common for multiple and single block IO */ +static void usdhi6_sg_unmap(struct usdhi6_host *host, bool force) +{ +	struct mmc_data *data = host->mrq->data; +	struct page *page = host->head_pg.page; + +	if (page) { +		/* Previous block was cross-page boundary */ +		struct scatterlist *sg = data->sg_len > 1 ? +			host->sg : data->sg; +		size_t blk_head = host->head_len; + +		if (!data->error && data->flags & MMC_DATA_READ) { +			memcpy(host->head_pg.mapped + PAGE_SIZE - blk_head, +			       host->bounce_buf, blk_head); +			memcpy(host->pg.mapped, host->bounce_buf + blk_head, +			       data->blksz - blk_head); +		} + +		flush_dcache_page(page); +		kunmap(page); + +		host->head_pg.page = NULL; + +		if (!force && sg_dma_len(sg) + sg->offset > +		    (host->page_idx << PAGE_SHIFT) + data->blksz - blk_head) +			/* More blocks in this SG, don't unmap the next page */ +			return; +	} + +	page = host->pg.page; +	if (!page) +		return; + +	flush_dcache_page(page); +	kunmap(page); + +	host->pg.page = NULL; +} + +/* Called from MMC_WRITE_MULTIPLE_BLOCK or MMC_READ_MULTIPLE_BLOCK */ +static void usdhi6_sg_advance(struct usdhi6_host *host) +{ +	struct mmc_data *data = host->mrq->data; +	size_t done, total; + +	/* New offset: set at the end of the previous block */ +	if (host->head_pg.page) { +		/* Finished a cross-page block, jump to the new page */ +		host->page_idx++; +		host->offset = data->blksz - host->head_len; +		host->blk_page = host->pg.mapped; +		usdhi6_sg_unmap(host, false); +	} else { +		host->offset += data->blksz; +		/* The completed block didn't cross a page boundary */ +		if (host->offset == PAGE_SIZE) { +			/* If required, we'll map the page below */ +			host->offset = 0; +			host->page_idx++; +		} +	} + +	/* +	 * Now host->blk_page + host->offset point at the end of our last block +	 * and host->page_idx is the index of the page, in which our new block +	 * is located, if any +	 */ + +	done = (host->page_idx << PAGE_SHIFT) + host->offset; +	total = host->sg->offset + sg_dma_len(host->sg); + +	dev_dbg(mmc_dev(host->mmc), "%s(): %zu of %zu @ %zu\n", __func__, +		done, total, host->offset); + +	if (done < total && host->offset) { +		/* More blocks in this page */ +		if (host->offset + data->blksz > PAGE_SIZE) +			/* We approached at a block, that spans 2 pages */ +			usdhi6_blk_bounce(host, host->sg); + +		return; +	} + +	/* Finished current page or an SG segment */ +	usdhi6_sg_unmap(host, false); + +	if (done == total) { +		/* +		 * End of an SG segment or the complete SG: jump to the next +		 * segment, we'll map it later in usdhi6_blk_read() or +		 * usdhi6_blk_write() +		 */ +		struct scatterlist *next = sg_next(host->sg); + +		host->page_idx = 0; + +		if (!next) +			host->wait = USDHI6_WAIT_FOR_DATA_END; +		host->sg = next; + +		if (WARN(next && sg_dma_len(next) % data->blksz, +			 "SG size %u isn't a multiple of block size %u\n", +			 sg_dma_len(next), data->blksz)) +			data->error = -EINVAL; + +		return; +	} + +	/* We cannot get here after crossing a page border */ + +	/* Next page in the same SG */ +	host->pg.page = nth_page(sg_page(host->sg), host->page_idx); +	host->pg.mapped = kmap(host->pg.page); +	host->blk_page = host->pg.mapped; + +	dev_dbg(mmc_dev(host->mmc), "Mapped %p (%lx) at %p for CMD%u @ 0x%p\n", +		host->pg.page, page_to_pfn(host->pg.page), host->pg.mapped, +		host->mrq->cmd->opcode, host->mrq); +} + +/*			DMA handling					*/ + +static void usdhi6_dma_release(struct usdhi6_host *host) +{ +	host->dma_active = false; +	if (host->chan_tx) { +		struct dma_chan *chan = host->chan_tx; +		host->chan_tx = NULL; +		dma_release_channel(chan); +	} +	if (host->chan_rx) { +		struct dma_chan *chan = host->chan_rx; +		host->chan_rx = NULL; +		dma_release_channel(chan); +	} +} + +static void usdhi6_dma_stop_unmap(struct usdhi6_host *host) +{ +	struct mmc_data *data = host->mrq->data; + +	if (!host->dma_active) +		return; + +	usdhi6_write(host, USDHI6_CC_EXT_MODE, 0); +	host->dma_active = false; + +	if (data->flags & MMC_DATA_READ) +		dma_unmap_sg(host->chan_rx->device->dev, data->sg, +			     data->sg_len, DMA_FROM_DEVICE); +	else +		dma_unmap_sg(host->chan_tx->device->dev, data->sg, +			     data->sg_len, DMA_TO_DEVICE); +} + +static void usdhi6_dma_complete(void *arg) +{ +	struct usdhi6_host *host = arg; +	struct mmc_request *mrq = host->mrq; + +	if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion for %p!\n", +		 dev_name(mmc_dev(host->mmc)), mrq)) +		return; + +	dev_dbg(mmc_dev(host->mmc), "%s(): CMD%u DMA completed\n", __func__, +		mrq->cmd->opcode); + +	usdhi6_dma_stop_unmap(host); +	usdhi6_wait_for_brwe(host, mrq->data->flags & MMC_DATA_READ); +} + +static int usdhi6_dma_setup(struct usdhi6_host *host, struct dma_chan *chan, +			    enum dma_transfer_direction dir) +{ +	struct mmc_data *data = host->mrq->data; +	struct scatterlist *sg = data->sg; +	struct dma_async_tx_descriptor *desc = NULL; +	dma_cookie_t cookie = -EINVAL; +	enum dma_data_direction data_dir; +	int ret; + +	switch (dir) { +	case DMA_MEM_TO_DEV: +		data_dir = DMA_TO_DEVICE; +		break; +	case DMA_DEV_TO_MEM: +		data_dir = DMA_FROM_DEVICE; +		break; +	default: +		return -EINVAL; +	} + +	ret = dma_map_sg(chan->device->dev, sg, data->sg_len, data_dir); +	if (ret > 0) { +		host->dma_active = true; +		desc = dmaengine_prep_slave_sg(chan, sg, ret, dir, +					DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +	} + +	if (desc) { +		desc->callback = usdhi6_dma_complete; +		desc->callback_param = host; +		cookie = dmaengine_submit(desc); +	} + +	dev_dbg(mmc_dev(host->mmc), "%s(): mapped %d -> %d, cookie %d @ %p\n", +		__func__, data->sg_len, ret, cookie, desc); + +	if (cookie < 0) { +		/* DMA failed, fall back to PIO */ +		if (ret >= 0) +			ret = cookie; +		usdhi6_dma_release(host); +		dev_warn(mmc_dev(host->mmc), +			 "DMA failed: %d, falling back to PIO\n", ret); +	} + +	return cookie; +} + +static int usdhi6_dma_start(struct usdhi6_host *host) +{ +	if (!host->chan_rx || !host->chan_tx) +		return -ENODEV; + +	if (host->mrq->data->flags & MMC_DATA_READ) +		return usdhi6_dma_setup(host, host->chan_rx, DMA_DEV_TO_MEM); + +	return usdhi6_dma_setup(host, host->chan_tx, DMA_MEM_TO_DEV); +} + +static void usdhi6_dma_kill(struct usdhi6_host *host) +{ +	struct mmc_data *data = host->mrq->data; + +	dev_dbg(mmc_dev(host->mmc), "%s(): SG of %u: %ux%u\n", +		__func__, data->sg_len, data->blocks, data->blksz); +	/* Abort DMA */ +	if (data->flags & MMC_DATA_READ) +		dmaengine_terminate_all(host->chan_rx); +	else +		dmaengine_terminate_all(host->chan_tx); +} + +static void usdhi6_dma_check_error(struct usdhi6_host *host) +{ +	struct mmc_data *data = host->mrq->data; + +	dev_dbg(mmc_dev(host->mmc), "%s(): IO error %d, status 0x%x\n", +		__func__, host->io_error, usdhi6_read(host, USDHI6_SD_INFO1)); + +	if (host->io_error) { +		data->error = usdhi6_error_code(host); +		data->bytes_xfered = 0; +		usdhi6_dma_kill(host); +		usdhi6_dma_release(host); +		dev_warn(mmc_dev(host->mmc), +			 "DMA failed: %d, falling back to PIO\n", data->error); +		return; +	} + +	/* +	 * The datasheet tells us to check a response from the card, whereas +	 * responses only come after the command phase, not after the data +	 * phase. Let's check anyway. +	 */ +	if (host->irq_status & USDHI6_SD_INFO1_RSP_END) +		dev_warn(mmc_dev(host->mmc), "Unexpected response received!\n"); +} + +static void usdhi6_dma_kick(struct usdhi6_host *host) +{ +	if (host->mrq->data->flags & MMC_DATA_READ) +		dma_async_issue_pending(host->chan_rx); +	else +		dma_async_issue_pending(host->chan_tx); +} + +static void usdhi6_dma_request(struct usdhi6_host *host, phys_addr_t start) +{ +	struct dma_slave_config cfg = { +		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, +		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, +	}; +	int ret; + +	host->chan_tx = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); +	dev_dbg(mmc_dev(host->mmc), "%s: TX: got channel %p\n", __func__, +		host->chan_tx); + +	if (!host->chan_tx) +		return; + +	cfg.direction = DMA_MEM_TO_DEV; +	cfg.dst_addr = start + USDHI6_SD_BUF0; +	cfg.dst_maxburst = 128;	/* 128 words * 4 bytes = 512 bytes */ +	cfg.src_addr = 0; +	ret = dmaengine_slave_config(host->chan_tx, &cfg); +	if (ret < 0) +		goto e_release_tx; + +	host->chan_rx = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); +	dev_dbg(mmc_dev(host->mmc), "%s: RX: got channel %p\n", __func__, +		host->chan_rx); + +	if (!host->chan_rx) +		goto e_release_tx; + +	cfg.direction = DMA_DEV_TO_MEM; +	cfg.src_addr = cfg.dst_addr; +	cfg.src_maxburst = 128;	/* 128 words * 4 bytes = 512 bytes */ +	cfg.dst_addr = 0; +	ret = dmaengine_slave_config(host->chan_rx, &cfg); +	if (ret < 0) +		goto e_release_rx; + +	return; + +e_release_rx: +	dma_release_channel(host->chan_rx); +	host->chan_rx = NULL; +e_release_tx: +	dma_release_channel(host->chan_tx); +	host->chan_tx = NULL; +} + +/*			API helpers					*/ + +static void usdhi6_clk_set(struct usdhi6_host *host, struct mmc_ios *ios) +{ +	unsigned long rate = ios->clock; +	u32 val; +	unsigned int i; + +	for (i = 1000; i; i--) { +		if (usdhi6_read(host, USDHI6_SD_INFO2) & USDHI6_SD_INFO2_SCLKDIVEN) +			break; +		usleep_range(10, 100); +	} + +	if (!i) { +		dev_err(mmc_dev(host->mmc), "SD bus busy, clock set aborted\n"); +		return; +	} + +	val = usdhi6_read(host, USDHI6_SD_CLK_CTRL) & ~USDHI6_SD_CLK_CTRL_DIV_MASK; + +	if (rate) { +		unsigned long new_rate; + +		if (host->imclk <= rate) { +			if (ios->timing != MMC_TIMING_UHS_DDR50) { +				/* Cannot have 1-to-1 clock in DDR mode */ +				new_rate = host->imclk; +				val |= 0xff; +			} else { +				new_rate = host->imclk / 2; +			} +		} else { +			unsigned long div = +				roundup_pow_of_two(DIV_ROUND_UP(host->imclk, rate)); +			val |= div >> 2; +			new_rate = host->imclk / div; +		} + +		if (host->rate == new_rate) +			return; + +		host->rate = new_rate; + +		dev_dbg(mmc_dev(host->mmc), "target %lu, div %u, set %lu\n", +			rate, (val & 0xff) << 2, new_rate); +	} + +	/* +	 * if old or new rate is equal to input rate, have to switch the clock +	 * off before changing and on after +	 */ +	if (host->imclk == rate || host->imclk == host->rate || !rate) +		usdhi6_write(host, USDHI6_SD_CLK_CTRL, +			     val & ~USDHI6_SD_CLK_CTRL_SCLKEN); + +	if (!rate) { +		host->rate = 0; +		return; +	} + +	usdhi6_write(host, USDHI6_SD_CLK_CTRL, val); + +	if (host->imclk == rate || host->imclk == host->rate || +	    !(val & USDHI6_SD_CLK_CTRL_SCLKEN)) +		usdhi6_write(host, USDHI6_SD_CLK_CTRL, +			     val | USDHI6_SD_CLK_CTRL_SCLKEN); +} + +static void usdhi6_set_power(struct usdhi6_host *host, struct mmc_ios *ios) +{ +	struct mmc_host *mmc = host->mmc; + +	if (!IS_ERR(mmc->supply.vmmc)) +		/* Errors ignored... */ +		mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, +				      ios->power_mode ? ios->vdd : 0); +} + +static int usdhi6_reset(struct usdhi6_host *host) +{ +	int i; + +	usdhi6_write(host, USDHI6_SOFT_RST, USDHI6_SOFT_RST_RESERVED); +	cpu_relax(); +	usdhi6_write(host, USDHI6_SOFT_RST, USDHI6_SOFT_RST_RESERVED | USDHI6_SOFT_RST_RESET); +	for (i = 1000; i; i--) +		if (usdhi6_read(host, USDHI6_SOFT_RST) & USDHI6_SOFT_RST_RESET) +			break; + +	return i ? 0 : -ETIMEDOUT; +} + +static void usdhi6_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ +	struct usdhi6_host *host = mmc_priv(mmc); +	u32 option, mode; +	int ret; + +	dev_dbg(mmc_dev(mmc), "%uHz, OCR: %u, power %u, bus-width %u, timing %u\n", +		ios->clock, ios->vdd, ios->power_mode, ios->bus_width, ios->timing); + +	switch (ios->power_mode) { +	case MMC_POWER_OFF: +		usdhi6_set_power(host, ios); +		usdhi6_only_cd(host); +		break; +	case MMC_POWER_UP: +		/* +		 * We only also touch USDHI6_SD_OPTION from .request(), which +		 * cannot race with MMC_POWER_UP +		 */ +		ret = usdhi6_reset(host); +		if (ret < 0) { +			dev_err(mmc_dev(mmc), "Cannot reset the interface!\n"); +		} else { +			usdhi6_set_power(host, ios); +			usdhi6_only_cd(host); +		} +		break; +	case MMC_POWER_ON: +		option = usdhi6_read(host, USDHI6_SD_OPTION); +		/* +		 * The eMMC standard only allows 4 or 8 bits in the DDR mode, +		 * the same probably holds for SD cards. We check here anyway, +		 * since the datasheet explicitly requires 4 bits for DDR. +		 */ +		if (ios->bus_width == MMC_BUS_WIDTH_1) { +			if (ios->timing == MMC_TIMING_UHS_DDR50) +				dev_err(mmc_dev(mmc), +					"4 bits are required for DDR\n"); +			option |= USDHI6_SD_OPTION_WIDTH_1; +			mode = 0; +		} else { +			option &= ~USDHI6_SD_OPTION_WIDTH_1; +			mode = ios->timing == MMC_TIMING_UHS_DDR50; +		} +		usdhi6_write(host, USDHI6_SD_OPTION, option); +		usdhi6_write(host, USDHI6_SDIF_MODE, mode); +		break; +	} + +	if (host->rate != ios->clock) +		usdhi6_clk_set(host, ios); +} + +/* This is data timeout. Response timeout is fixed to 640 clock cycles */ +static void usdhi6_timeout_set(struct usdhi6_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	u32 val; +	unsigned long ticks; + +	if (!mrq->data) +		ticks = host->rate / 1000 * mrq->cmd->busy_timeout; +	else +		ticks = host->rate / 1000000 * (mrq->data->timeout_ns / 1000) + +			mrq->data->timeout_clks; + +	if (!ticks || ticks > 1 << 27) +		/* Max timeout */ +		val = 14; +	else if (ticks < 1 << 13) +		/* Min timeout */ +		val = 0; +	else +		val = order_base_2(ticks) - 13; + +	dev_dbg(mmc_dev(host->mmc), "Set %s timeout %lu ticks @ %lu Hz\n", +		mrq->data ? "data" : "cmd", ticks, host->rate); + +	/* Timeout Counter mask: 0xf0 */ +	usdhi6_write(host, USDHI6_SD_OPTION, (val << USDHI6_SD_OPTION_TIMEOUT_SHIFT) | +		     (usdhi6_read(host, USDHI6_SD_OPTION) & ~USDHI6_SD_OPTION_TIMEOUT_MASK)); +} + +static void usdhi6_request_done(struct usdhi6_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	struct mmc_data *data = mrq->data; + +	if (WARN(host->pg.page || host->head_pg.page, +		 "Page %p or %p not unmapped: wait %u, CMD%d(%c) @ +0x%zx %ux%u in SG%u!\n", +		 host->pg.page, host->head_pg.page, host->wait, mrq->cmd->opcode, +		 data ? (data->flags & MMC_DATA_READ ? 'R' : 'W') : '-', +		 data ? host->offset : 0, data ? data->blocks : 0, +		 data ? data->blksz : 0, data ? data->sg_len : 0)) +		usdhi6_sg_unmap(host, true); + +	if (mrq->cmd->error || +	    (data && data->error) || +	    (mrq->stop && mrq->stop->error)) +		dev_dbg(mmc_dev(host->mmc), "%s(CMD%d: %ux%u): err %d %d %d\n", +			__func__, mrq->cmd->opcode, data ? data->blocks : 0, +			data ? data->blksz : 0, +			mrq->cmd->error, +			data ? data->error : 1, +			mrq->stop ? mrq->stop->error : 1); + +	/* Disable DMA */ +	usdhi6_write(host, USDHI6_CC_EXT_MODE, 0); +	host->wait = USDHI6_WAIT_FOR_REQUEST; +	host->mrq = NULL; + +	mmc_request_done(host->mmc, mrq); +} + +static int usdhi6_cmd_flags(struct usdhi6_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	struct mmc_command *cmd = mrq->cmd; +	u16 opc = cmd->opcode; + +	if (host->app_cmd) { +		host->app_cmd = false; +		opc |= USDHI6_SD_CMD_APP; +	} + +	if (mrq->data) { +		opc |= USDHI6_SD_CMD_DATA; + +		if (mrq->data->flags & MMC_DATA_READ) +			opc |= USDHI6_SD_CMD_READ; + +		if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || +		    cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || +		    (cmd->opcode == SD_IO_RW_EXTENDED && +		     mrq->data->blocks > 1)) { +			opc |= USDHI6_SD_CMD_MULTI; +			if (!mrq->stop) +				opc |= USDHI6_SD_CMD_CMD12_AUTO_OFF; +		} + +		switch (mmc_resp_type(cmd)) { +		case MMC_RSP_NONE: +			opc |= USDHI6_SD_CMD_MODE_RSP_NONE; +			break; +		case MMC_RSP_R1: +			opc |= USDHI6_SD_CMD_MODE_RSP_R1; +			break; +		case MMC_RSP_R1B: +			opc |= USDHI6_SD_CMD_MODE_RSP_R1B; +			break; +		case MMC_RSP_R2: +			opc |= USDHI6_SD_CMD_MODE_RSP_R2; +			break; +		case MMC_RSP_R3: +			opc |= USDHI6_SD_CMD_MODE_RSP_R3; +			break; +		default: +			dev_warn(mmc_dev(host->mmc), +				 "Unknown response type %d\n", +				 mmc_resp_type(cmd)); +			return -EINVAL; +		} +	} + +	return opc; +} + +static int usdhi6_rq_start(struct usdhi6_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	struct mmc_command *cmd = mrq->cmd; +	struct mmc_data *data = mrq->data; +	int opc = usdhi6_cmd_flags(host); +	int i; + +	if (opc < 0) +		return opc; + +	for (i = 1000; i; i--) { +		if (!(usdhi6_read(host, USDHI6_SD_INFO2) & USDHI6_SD_INFO2_CBSY)) +			break; +		usleep_range(10, 100); +	} + +	if (!i) { +		dev_dbg(mmc_dev(host->mmc), "Command active, request aborted\n"); +		return -EAGAIN; +	} + +	if (data) { +		bool use_dma; +		int ret = 0; + +		host->page_idx = 0; + +		if (cmd->opcode == SD_IO_RW_EXTENDED && data->blocks > 1) { +			switch (data->blksz) { +			case 512: +				break; +			case 32: +			case 64: +			case 128: +			case 256: +				if (mrq->stop) +					ret = -EINVAL; +				break; +			default: +				ret = -EINVAL; +			} +		} else if ((cmd->opcode == MMC_READ_MULTIPLE_BLOCK || +			    cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) && +			   data->blksz != 512) { +			ret = -EINVAL; +		} + +		if (ret < 0) { +			dev_warn(mmc_dev(host->mmc), "%s(): %u blocks of %u bytes\n", +				 __func__, data->blocks, data->blksz); +			return -EINVAL; +		} + +		if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || +		    cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || +		    (cmd->opcode == SD_IO_RW_EXTENDED && +		     data->blocks > 1)) +			usdhi6_sg_prep(host); + +		usdhi6_write(host, USDHI6_SD_SIZE, data->blksz); + +		if ((data->blksz >= USDHI6_MIN_DMA || +		     data->blocks > 1) && +		    (data->blksz % 4 || +		     data->sg->offset % 4)) +			dev_dbg(mmc_dev(host->mmc), +				"Bad SG of %u: %ux%u @ %u\n", data->sg_len, +				data->blksz, data->blocks, data->sg->offset); + +		/* Enable DMA for USDHI6_MIN_DMA bytes or more */ +		use_dma = data->blksz >= USDHI6_MIN_DMA && +			!(data->blksz % 4) && +			usdhi6_dma_start(host) >= DMA_MIN_COOKIE; + +		if (use_dma) +			usdhi6_write(host, USDHI6_CC_EXT_MODE, USDHI6_CC_EXT_MODE_SDRW); + +		dev_dbg(mmc_dev(host->mmc), +			"%s(): request opcode %u, %u blocks of %u bytes in %u segments, %s %s @+0x%x%s\n", +			__func__, cmd->opcode, data->blocks, data->blksz, +			data->sg_len, use_dma ? "DMA" : "PIO", +			data->flags & MMC_DATA_READ ? "read" : "write", +			data->sg->offset, mrq->stop ? " + stop" : ""); +	} else { +		dev_dbg(mmc_dev(host->mmc), "%s(): request opcode %u\n", +			__func__, cmd->opcode); +	} + +	/* We have to get a command completion interrupt with DMA too */ +	usdhi6_wait_for_resp(host); + +	host->wait = USDHI6_WAIT_FOR_CMD; +	schedule_delayed_work(&host->timeout_work, host->timeout); + +	/* SEC bit is required to enable block counting by the core */ +	usdhi6_write(host, USDHI6_SD_STOP, +		     data && data->blocks > 1 ? USDHI6_SD_STOP_SEC : 0); +	usdhi6_write(host, USDHI6_SD_ARG, cmd->arg); + +	/* Kick command execution */ +	usdhi6_write(host, USDHI6_SD_CMD, opc); + +	return 0; +} + +static void usdhi6_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ +	struct usdhi6_host *host = mmc_priv(mmc); +	int ret; + +	cancel_delayed_work_sync(&host->timeout_work); + +	host->mrq = mrq; +	host->sg = NULL; + +	usdhi6_timeout_set(host); +	ret = usdhi6_rq_start(host); +	if (ret < 0) { +		mrq->cmd->error = ret; +		usdhi6_request_done(host); +	} +} + +static int usdhi6_get_cd(struct mmc_host *mmc) +{ +	struct usdhi6_host *host = mmc_priv(mmc); +	/* Read is atomic, no need to lock */ +	u32 status = usdhi6_read(host, USDHI6_SD_INFO1) & USDHI6_SD_INFO1_CD; + +/* + *	level	status.CD	CD_ACTIVE_HIGH	card present + *	1	0		0		0 + *	1	0		1		1 + *	0	1		0		1 + *	0	1		1		0 + */ +	return !status ^ !(mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); +} + +static int usdhi6_get_ro(struct mmc_host *mmc) +{ +	struct usdhi6_host *host = mmc_priv(mmc); +	/* No locking as above */ +	u32 status = usdhi6_read(host, USDHI6_SD_INFO1) & USDHI6_SD_INFO1_WP; + +/* + *	level	status.WP	RO_ACTIVE_HIGH	card read-only + *	1	0		0		0 + *	1	0		1		1 + *	0	1		0		1 + *	0	1		1		0 + */ +	return !status ^ !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); +} + +static void usdhi6_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ +	struct usdhi6_host *host = mmc_priv(mmc); + +	dev_dbg(mmc_dev(mmc), "%s(): %sable\n", __func__, enable ? "en" : "dis"); + +	if (enable) { +		host->sdio_mask = USDHI6_SDIO_INFO1_IRQ & ~USDHI6_SDIO_INFO1_IOIRQ; +		usdhi6_write(host, USDHI6_SDIO_INFO1_MASK, host->sdio_mask); +		usdhi6_write(host, USDHI6_SDIO_MODE, 1); +	} else { +		usdhi6_write(host, USDHI6_SDIO_MODE, 0); +		usdhi6_write(host, USDHI6_SDIO_INFO1_MASK, USDHI6_SDIO_INFO1_IRQ); +		host->sdio_mask = USDHI6_SDIO_INFO1_IRQ; +	} +} + +static struct mmc_host_ops usdhi6_ops = { +	.request	= usdhi6_request, +	.set_ios	= usdhi6_set_ios, +	.get_cd		= usdhi6_get_cd, +	.get_ro		= usdhi6_get_ro, +	.enable_sdio_irq = usdhi6_enable_sdio_irq, +}; + +/*			State machine handlers				*/ + +static void usdhi6_resp_cmd12(struct usdhi6_host *host) +{ +	struct mmc_command *cmd = host->mrq->stop; +	cmd->resp[0] = usdhi6_read(host, USDHI6_SD_RSP10); +} + +static void usdhi6_resp_read(struct usdhi6_host *host) +{ +	struct mmc_command *cmd = host->mrq->cmd; +	u32 *rsp = cmd->resp, tmp = 0; +	int i; + +/* + * RSP10	39-8 + * RSP32	71-40 + * RSP54	103-72 + * RSP76	127-104 + * R2-type response: + * resp[0]	= r[127..96] + * resp[1]	= r[95..64] + * resp[2]	= r[63..32] + * resp[3]	= r[31..0] + * Other responses: + * resp[0]	= r[39..8] + */ + +	if (mmc_resp_type(cmd) == MMC_RSP_NONE) +		return; + +	if (!(host->irq_status & USDHI6_SD_INFO1_RSP_END)) { +		dev_err(mmc_dev(host->mmc), +			"CMD%d: response expected but is missing!\n", cmd->opcode); +		return; +	} + +	if (mmc_resp_type(cmd) & MMC_RSP_136) +		for (i = 0; i < 4; i++) { +			if (i) +				rsp[3 - i] = tmp >> 24; +			tmp = usdhi6_read(host, USDHI6_SD_RSP10 + i * 8); +			rsp[3 - i] |= tmp << 8; +		} +	else if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || +		 cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) +		/* Read RSP54 to avoid conflict with auto CMD12 */ +		rsp[0] = usdhi6_read(host, USDHI6_SD_RSP54); +	else +		rsp[0] = usdhi6_read(host, USDHI6_SD_RSP10); + +	dev_dbg(mmc_dev(host->mmc), "Response 0x%x\n", rsp[0]); +} + +static int usdhi6_blk_read(struct usdhi6_host *host) +{ +	struct mmc_data *data = host->mrq->data; +	u32 *p; +	int i, rest; + +	if (host->io_error) { +		data->error = usdhi6_error_code(host); +		goto error; +	} + +	if (host->pg.page) { +		p = host->blk_page + host->offset; +	} else { +		p = usdhi6_sg_map(host); +		if (!p) { +			data->error = -ENOMEM; +			goto error; +		} +	} + +	for (i = 0; i < data->blksz / 4; i++, p++) +		*p = usdhi6_read(host, USDHI6_SD_BUF0); + +	rest = data->blksz % 4; +	for (i = 0; i < (rest + 1) / 2; i++) { +		u16 d = usdhi6_read16(host, USDHI6_SD_BUF0); +		((u8 *)p)[2 * i] = ((u8 *)&d)[0]; +		if (rest > 1 && !i) +			((u8 *)p)[2 * i + 1] = ((u8 *)&d)[1]; +	} + +	return 0; + +error: +	dev_dbg(mmc_dev(host->mmc), "%s(): %d\n", __func__, data->error); +	host->wait = USDHI6_WAIT_FOR_REQUEST; +	return data->error; +} + +static int usdhi6_blk_write(struct usdhi6_host *host) +{ +	struct mmc_data *data = host->mrq->data; +	u32 *p; +	int i, rest; + +	if (host->io_error) { +		data->error = usdhi6_error_code(host); +		goto error; +	} + +	if (host->pg.page) { +		p = host->blk_page + host->offset; +	} else { +		p = usdhi6_sg_map(host); +		if (!p) { +			data->error = -ENOMEM; +			goto error; +		} +	} + +	for (i = 0; i < data->blksz / 4; i++, p++) +		usdhi6_write(host, USDHI6_SD_BUF0, *p); + +	rest = data->blksz % 4; +	for (i = 0; i < (rest + 1) / 2; i++) { +		u16 d; +		((u8 *)&d)[0] = ((u8 *)p)[2 * i]; +		if (rest > 1 && !i) +			((u8 *)&d)[1] = ((u8 *)p)[2 * i + 1]; +		else +			((u8 *)&d)[1] = 0; +		usdhi6_write16(host, USDHI6_SD_BUF0, d); +	} + +	return 0; + +error: +	dev_dbg(mmc_dev(host->mmc), "%s(): %d\n", __func__, data->error); +	host->wait = USDHI6_WAIT_FOR_REQUEST; +	return data->error; +} + +static int usdhi6_stop_cmd(struct usdhi6_host *host) +{ +	struct mmc_request *mrq = host->mrq; + +	switch (mrq->cmd->opcode) { +	case MMC_READ_MULTIPLE_BLOCK: +	case MMC_WRITE_MULTIPLE_BLOCK: +		if (mrq->stop->opcode == MMC_STOP_TRANSMISSION) { +			host->wait = USDHI6_WAIT_FOR_STOP; +			return 0; +		} +		/* Unsupported STOP command */ +	default: +		dev_err(mmc_dev(host->mmc), +			"unsupported stop CMD%d for CMD%d\n", +			mrq->stop->opcode, mrq->cmd->opcode); +		mrq->stop->error = -EOPNOTSUPP; +	} + +	return -EOPNOTSUPP; +} + +static bool usdhi6_end_cmd(struct usdhi6_host *host) +{ +	struct mmc_request *mrq = host->mrq; +	struct mmc_command *cmd = mrq->cmd; + +	if (host->io_error) { +		cmd->error = usdhi6_error_code(host); +		return false; +	} + +	usdhi6_resp_read(host); + +	if (!mrq->data) +		return false; + +	if (host->dma_active) { +		usdhi6_dma_kick(host); +		if (!mrq->stop) +			host->wait = USDHI6_WAIT_FOR_DMA; +		else if (usdhi6_stop_cmd(host) < 0) +			return false; +	} else if (mrq->data->flags & MMC_DATA_READ) { +		if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || +		    (cmd->opcode == SD_IO_RW_EXTENDED && +		     mrq->data->blocks > 1)) +			host->wait = USDHI6_WAIT_FOR_MREAD; +		else +			host->wait = USDHI6_WAIT_FOR_READ; +	} else { +		if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || +		    (cmd->opcode == SD_IO_RW_EXTENDED && +		     mrq->data->blocks > 1)) +			host->wait = USDHI6_WAIT_FOR_MWRITE; +		else +			host->wait = USDHI6_WAIT_FOR_WRITE; +	} + +	return true; +} + +static bool usdhi6_read_block(struct usdhi6_host *host) +{ +	/* ACCESS_END IRQ is already unmasked */ +	int ret = usdhi6_blk_read(host); + +	/* +	 * Have to force unmapping both pages: the single block could have been +	 * cross-page, in which case for single-block IO host->page_idx == 0. +	 * So, if we don't force, the second page won't be unmapped. +	 */ +	usdhi6_sg_unmap(host, true); + +	if (ret < 0) +		return false; + +	host->wait = USDHI6_WAIT_FOR_DATA_END; +	return true; +} + +static bool usdhi6_mread_block(struct usdhi6_host *host) +{ +	int ret = usdhi6_blk_read(host); + +	if (ret < 0) +		return false; + +	usdhi6_sg_advance(host); + +	return !host->mrq->data->error && +		(host->wait != USDHI6_WAIT_FOR_DATA_END || !host->mrq->stop); +} + +static bool usdhi6_write_block(struct usdhi6_host *host) +{ +	int ret = usdhi6_blk_write(host); + +	/* See comment in usdhi6_read_block() */ +	usdhi6_sg_unmap(host, true); + +	if (ret < 0) +		return false; + +	host->wait = USDHI6_WAIT_FOR_DATA_END; +	return true; +} + +static bool usdhi6_mwrite_block(struct usdhi6_host *host) +{ +	int ret = usdhi6_blk_write(host); + +	if (ret < 0) +		return false; + +	usdhi6_sg_advance(host); + +	return !host->mrq->data->error && +		(host->wait != USDHI6_WAIT_FOR_DATA_END || !host->mrq->stop); +} + +/*			Interrupt & timeout handlers			*/ + +static irqreturn_t usdhi6_sd_bh(int irq, void *dev_id) +{ +	struct usdhi6_host *host = dev_id; +	struct mmc_request *mrq; +	struct mmc_command *cmd; +	struct mmc_data *data; +	bool io_wait = false; + +	cancel_delayed_work_sync(&host->timeout_work); + +	mrq = host->mrq; +	if (!mrq) +		return IRQ_HANDLED; + +	cmd = mrq->cmd; +	data = mrq->data; + +	switch (host->wait) { +	case USDHI6_WAIT_FOR_REQUEST: +		/* We're too late, the timeout has already kicked in */ +		return IRQ_HANDLED; +	case USDHI6_WAIT_FOR_CMD: +		/* Wait for data? */ +		io_wait = usdhi6_end_cmd(host); +		break; +	case USDHI6_WAIT_FOR_MREAD: +		/* Wait for more data? */ +		io_wait = usdhi6_mread_block(host); +		break; +	case USDHI6_WAIT_FOR_READ: +		/* Wait for data end? */ +		io_wait = usdhi6_read_block(host); +		break; +	case USDHI6_WAIT_FOR_MWRITE: +		/* Wait data to write? */ +		io_wait = usdhi6_mwrite_block(host); +		break; +	case USDHI6_WAIT_FOR_WRITE: +		/* Wait for data end? */ +		io_wait = usdhi6_write_block(host); +		break; +	case USDHI6_WAIT_FOR_DMA: +		usdhi6_dma_check_error(host); +		break; +	case USDHI6_WAIT_FOR_STOP: +		usdhi6_write(host, USDHI6_SD_STOP, 0); +		if (host->io_error) { +			int ret = usdhi6_error_code(host); +			if (mrq->stop) +				mrq->stop->error = ret; +			else +				mrq->data->error = ret; +			dev_warn(mmc_dev(host->mmc), "%s(): %d\n", __func__, ret); +			break; +		} +		usdhi6_resp_cmd12(host); +		mrq->stop->error = 0; +		break; +	case USDHI6_WAIT_FOR_DATA_END: +		if (host->io_error) { +			mrq->data->error = usdhi6_error_code(host); +			dev_warn(mmc_dev(host->mmc), "%s(): %d\n", __func__, +				 mrq->data->error); +		} +		break; +	default: +		cmd->error = -EFAULT; +		dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait); +		usdhi6_request_done(host); +		return IRQ_HANDLED; +	} + +	if (io_wait) { +		schedule_delayed_work(&host->timeout_work, host->timeout); +		/* Wait for more data or ACCESS_END */ +		if (!host->dma_active) +			usdhi6_wait_for_brwe(host, mrq->data->flags & MMC_DATA_READ); +		return IRQ_HANDLED; +	} + +	if (!cmd->error) { +		if (data) { +			if (!data->error) { +				if (host->wait != USDHI6_WAIT_FOR_STOP && +				    host->mrq->stop && +				    !host->mrq->stop->error && +				    !usdhi6_stop_cmd(host)) { +					/* Sending STOP */ +					usdhi6_wait_for_resp(host); + +					schedule_delayed_work(&host->timeout_work, +							      host->timeout); + +					return IRQ_HANDLED; +				} + +				data->bytes_xfered = data->blocks * data->blksz; +			} else { +				/* Data error: might need to unmap the last page */ +				dev_warn(mmc_dev(host->mmc), "%s(): data error %d\n", +					 __func__, data->error); +				usdhi6_sg_unmap(host, true); +			} +		} else if (cmd->opcode == MMC_APP_CMD) { +			host->app_cmd = true; +		} +	} + +	usdhi6_request_done(host); + +	return IRQ_HANDLED; +} + +static irqreturn_t usdhi6_sd(int irq, void *dev_id) +{ +	struct usdhi6_host *host = dev_id; +	u16 status, status2, error; + +	status = usdhi6_read(host, USDHI6_SD_INFO1) & ~host->status_mask & +		~USDHI6_SD_INFO1_CARD; +	status2 = usdhi6_read(host, USDHI6_SD_INFO2) & ~host->status2_mask; + +	usdhi6_only_cd(host); + +	dev_dbg(mmc_dev(host->mmc), +		"IRQ status = 0x%08x, status2 = 0x%08x\n", status, status2); + +	if (!status && !status2) +		return IRQ_NONE; + +	error = status2 & USDHI6_SD_INFO2_ERR; + +	/* Ack / clear interrupts */ +	if (USDHI6_SD_INFO1_IRQ & status) +		usdhi6_write(host, USDHI6_SD_INFO1, +			     0xffff & ~(USDHI6_SD_INFO1_IRQ & status)); + +	if (USDHI6_SD_INFO2_IRQ & status2) { +		if (error) +			/* In error cases BWE and BRE aren't cleared automatically */ +			status2 |= USDHI6_SD_INFO2_BWE | USDHI6_SD_INFO2_BRE; + +		usdhi6_write(host, USDHI6_SD_INFO2, +			     0xffff & ~(USDHI6_SD_INFO2_IRQ & status2)); +	} + +	host->io_error = error; +	host->irq_status = status; + +	if (error) { +		/* Don't pollute the log with unsupported command timeouts */ +		if (host->wait != USDHI6_WAIT_FOR_CMD || +		    error != USDHI6_SD_INFO2_RSP_TOUT) +			dev_warn(mmc_dev(host->mmc), +				 "%s(): INFO2 error bits 0x%08x\n", +				 __func__, error); +		else +			dev_dbg(mmc_dev(host->mmc), +				"%s(): INFO2 error bits 0x%08x\n", +				__func__, error); +	} + +	return IRQ_WAKE_THREAD; +} + +static irqreturn_t usdhi6_sdio(int irq, void *dev_id) +{ +	struct usdhi6_host *host = dev_id; +	u32 status = usdhi6_read(host, USDHI6_SDIO_INFO1) & ~host->sdio_mask; + +	dev_dbg(mmc_dev(host->mmc), "%s(): status 0x%x\n", __func__, status); + +	if (!status) +		return IRQ_NONE; + +	usdhi6_write(host, USDHI6_SDIO_INFO1, ~status); + +	mmc_signal_sdio_irq(host->mmc); + +	return IRQ_HANDLED; +} + +static irqreturn_t usdhi6_cd(int irq, void *dev_id) +{ +	struct usdhi6_host *host = dev_id; +	struct mmc_host *mmc = host->mmc; +	u16 status; + +	/* We're only interested in hotplug events here */ +	status = usdhi6_read(host, USDHI6_SD_INFO1) & ~host->status_mask & +		USDHI6_SD_INFO1_CARD; + +	if (!status) +		return IRQ_NONE; + +	/* Ack */ +	usdhi6_write(host, USDHI6_SD_INFO1, !status); + +	if (!work_pending(&mmc->detect.work) && +	    (((status & USDHI6_SD_INFO1_CARD_INSERT) && +	      !mmc->card) || +	     ((status & USDHI6_SD_INFO1_CARD_EJECT) && +	      mmc->card))) +		mmc_detect_change(mmc, msecs_to_jiffies(100)); + +	return IRQ_HANDLED; +} + +/* + * Actually this should not be needed, if the built-in timeout works reliably in + * the both PIO cases and DMA never fails. But if DMA does fail, a timeout + * handler might be the only way to catch the error. + */ +static void usdhi6_timeout_work(struct work_struct *work) +{ +	struct delayed_work *d = container_of(work, struct delayed_work, work); +	struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work); +	struct mmc_request *mrq = host->mrq; +	struct mmc_data *data = mrq ? mrq->data : NULL; + +	dev_warn(mmc_dev(host->mmc), +		 "%s timeout wait %u CMD%d: IRQ 0x%08x:0x%08x, last IRQ 0x%08x\n", +		 host->dma_active ? "DMA" : "PIO", +		 host->wait, mrq ? mrq->cmd->opcode : -1, +		 usdhi6_read(host, USDHI6_SD_INFO1), +		 usdhi6_read(host, USDHI6_SD_INFO2), host->irq_status); + +	if (host->dma_active) { +		usdhi6_dma_kill(host); +		usdhi6_dma_stop_unmap(host); +	} + +	switch (host->wait) { +	default: +		dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait); +		/* mrq can be NULL in this actually impossible case */ +	case USDHI6_WAIT_FOR_CMD: +		usdhi6_error_code(host); +		if (mrq) +			mrq->cmd->error = -ETIMEDOUT; +		break; +	case USDHI6_WAIT_FOR_STOP: +		usdhi6_error_code(host); +		mrq->stop->error = -ETIMEDOUT; +		break; +	case USDHI6_WAIT_FOR_DMA: +	case USDHI6_WAIT_FOR_MREAD: +	case USDHI6_WAIT_FOR_MWRITE: +	case USDHI6_WAIT_FOR_READ: +	case USDHI6_WAIT_FOR_WRITE: +		dev_dbg(mmc_dev(host->mmc), +			"%c: page #%u @ +0x%zx %ux%u in SG%u. Current SG %u bytes @ %u\n", +			data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx, +			host->offset, data->blocks, data->blksz, data->sg_len, +			sg_dma_len(host->sg), host->sg->offset); +		usdhi6_sg_unmap(host, true); +		/* +		 * If USDHI6_WAIT_FOR_DATA_END times out, we have already unmapped +		 * the page +		 */ +	case USDHI6_WAIT_FOR_DATA_END: +		usdhi6_error_code(host); +		data->error = -ETIMEDOUT; +	} + +	if (mrq) +		usdhi6_request_done(host); +} + +/*			 Probe / release				*/ + +static const struct of_device_id usdhi6_of_match[] = { +	{.compatible = "renesas,usdhi6rol0"}, +	{} +}; +MODULE_DEVICE_TABLE(of, usdhi6_of_match); + +static int usdhi6_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct mmc_host *mmc; +	struct usdhi6_host *host; +	struct resource *res; +	int irq_cd, irq_sd, irq_sdio; +	u32 version; +	int ret; + +	if (!dev->of_node) +		return -ENODEV; + +	irq_cd = platform_get_irq_byname(pdev, "card detect"); +	irq_sd = platform_get_irq_byname(pdev, "data"); +	irq_sdio = platform_get_irq_byname(pdev, "SDIO"); +	if (irq_sd < 0 || irq_sdio < 0) +		return -ENODEV; + +	mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev); +	if (!mmc) +		return -ENOMEM; + +	ret = mmc_of_parse(mmc); +	if (ret < 0) +		goto e_free_mmc; + +	mmc_regulator_get_supply(mmc); + +	host		= mmc_priv(mmc); +	host->mmc	= mmc; +	host->wait	= USDHI6_WAIT_FOR_REQUEST; +	host->timeout	= msecs_to_jiffies(4000); + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	host->base = devm_ioremap_resource(dev, res); +	if (IS_ERR(host->base)) { +		ret = PTR_ERR(host->base); +		goto e_free_mmc; +	} + +	host->clk = devm_clk_get(dev, NULL); +	if (IS_ERR(host->clk)) +		goto e_free_mmc; + +	host->imclk = clk_get_rate(host->clk); + +	ret = clk_prepare_enable(host->clk); +	if (ret < 0) +		goto e_free_mmc; + +	version = usdhi6_read(host, USDHI6_VERSION); +	if ((version & 0xfff) != 0xa0d) { +		dev_err(dev, "Version not recognized %x\n", version); +		goto e_clk_off; +	} + +	dev_info(dev, "A USDHI6ROL0 SD host detected with %d ports\n", +		 usdhi6_read(host, USDHI6_SD_PORT_SEL) >> USDHI6_SD_PORT_SEL_PORTS_SHIFT); + +	usdhi6_mask_all(host); + +	if (irq_cd >= 0) { +		ret = devm_request_irq(dev, irq_cd, usdhi6_cd, 0, +				       dev_name(dev), host); +		if (ret < 0) +			goto e_clk_off; +	} else { +		mmc->caps |= MMC_CAP_NEEDS_POLL; +	} + +	ret = devm_request_threaded_irq(dev, irq_sd, usdhi6_sd, usdhi6_sd_bh, 0, +			       dev_name(dev), host); +	if (ret < 0) +		goto e_clk_off; + +	ret = devm_request_irq(dev, irq_sdio, usdhi6_sdio, 0, +			       dev_name(dev), host); +	if (ret < 0) +		goto e_clk_off; + +	INIT_DELAYED_WORK(&host->timeout_work, usdhi6_timeout_work); + +	usdhi6_dma_request(host, res->start); + +	mmc->ops = &usdhi6_ops; +	mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | +		MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | MMC_CAP_SDIO_IRQ; +	/* Set .max_segs to some random number. Feel free to adjust. */ +	mmc->max_segs = 32; +	mmc->max_blk_size = 512; +	mmc->max_req_size = PAGE_CACHE_SIZE * mmc->max_segs; +	mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size; +	/* +	 * Setting .max_seg_size to 1 page would simplify our page-mapping code, +	 * But OTOH, having large segments makes DMA more efficient. We could +	 * check, whether we managed to get DMA and fall back to 1 page +	 * segments, but if we do manage to obtain DMA and then it fails at +	 * run-time and we fall back to PIO, we will continue getting large +	 * segments. So, we wouldn't be able to get rid of the code anyway. +	 */ +	mmc->max_seg_size = mmc->max_req_size; +	if (!mmc->f_max) +		mmc->f_max = host->imclk; +	mmc->f_min = host->imclk / 512; + +	platform_set_drvdata(pdev, host); + +	ret = mmc_add_host(mmc); +	if (ret < 0) +		goto e_clk_off; + +	return 0; + +e_clk_off: +	clk_disable_unprepare(host->clk); +e_free_mmc: +	mmc_free_host(mmc); + +	return ret; +} + +static int usdhi6_remove(struct platform_device *pdev) +{ +	struct usdhi6_host *host = platform_get_drvdata(pdev); + +	mmc_remove_host(host->mmc); + +	usdhi6_mask_all(host); +	cancel_delayed_work_sync(&host->timeout_work); +	usdhi6_dma_release(host); +	clk_disable_unprepare(host->clk); +	mmc_free_host(host->mmc); + +	return 0; +} + +static struct platform_driver usdhi6_driver = { +	.probe		= usdhi6_probe, +	.remove		= usdhi6_remove, +	.driver		= { +		.name	= "usdhi6rol0", +		.owner	= THIS_MODULE, +		.of_match_table = usdhi6_of_match, +	}, +}; + +module_platform_driver(usdhi6_driver); + +MODULE_DESCRIPTION("Renesas usdhi6rol0 SD/SDIO host driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:usdhi6rol0"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index c0105a2e269..d2c386f09d6 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -504,7 +504,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id  		ret = -ENOMEM;  		goto err;  	} -	ushc->csw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); +	ushc->csw = kzalloc(sizeof(struct ushc_csw), GFP_KERNEL);  	if (ushc->csw == NULL) {  		ret = -ENOMEM;  		goto err; diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 4f84586c6e9..63fac78b3d4 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1269,21 +1269,18 @@ static void via_init_sdc_pm(struct via_crdr_mmc_host *host)  static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state)  {  	struct via_crdr_mmc_host *host; -	int ret = 0;  	host = pci_get_drvdata(pcidev);  	via_save_pcictrlreg(host);  	via_save_sdcreg(host); -	ret = mmc_suspend_host(host->mmc); -  	pci_save_state(pcidev);  	pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0);  	pci_disable_device(pcidev);  	pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); -	return ret; +	return 0;  }  static int via_sd_resume(struct pci_dev *pcidev) @@ -1316,8 +1313,6 @@ static int via_sd_resume(struct pci_dev *pcidev)  	via_restore_pcictrlreg(sdhost);  	via_init_sdc_pm(sdhost); -	ret = mmc_resume_host(sdhost->mmc); -  	return ret;  } diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index e9028ad05ff..4262296c12f 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2392,26 +2392,12 @@ static void vub300_disconnect(struct usb_interface *interface)  #ifdef CONFIG_PM  static int vub300_suspend(struct usb_interface *intf, pm_message_t message)  { -	struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); -	if (!vub300 || !vub300->mmc) { -		return 0; -	} else { -		struct mmc_host *mmc = vub300->mmc; -		mmc_suspend_host(mmc); -		return 0; -	} +	return 0;  }  static int vub300_resume(struct usb_interface *intf)  { -	struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); -	if (!vub300 || !vub300->mmc) { -		return 0; -	} else { -		struct mmc_host *mmc = vub300->mmc; -		mmc_resume_host(mmc); -		return 0; -	} +	return 0;  }  #else  #define vub300_suspend NULL diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index e954b775887..1defd5ed323 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1814,28 +1814,11 @@ static void wbsd_pnp_remove(struct pnp_dev *dev)  #ifdef CONFIG_PM -static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) -{ -	BUG_ON(host == NULL); - -	return mmc_suspend_host(host->mmc); -} - -static int wbsd_resume(struct wbsd_host *host) -{ -	BUG_ON(host == NULL); - -	wbsd_init_device(host); - -	return mmc_resume_host(host->mmc); -} -  static int wbsd_platform_suspend(struct platform_device *dev,  				 pm_message_t state)  {  	struct mmc_host *mmc = platform_get_drvdata(dev);  	struct wbsd_host *host; -	int ret;  	if (mmc == NULL)  		return 0; @@ -1844,12 +1827,7 @@ static int wbsd_platform_suspend(struct platform_device *dev,  	host = mmc_priv(mmc); -	ret = wbsd_suspend(host, state); -	if (ret) -		return ret; -  	wbsd_chip_poweroff(host); -  	return 0;  } @@ -1872,7 +1850,8 @@ static int wbsd_platform_resume(struct platform_device *dev)  	 */  	mdelay(5); -	return wbsd_resume(host); +	wbsd_init_device(host); +	return 0;  }  #ifdef CONFIG_PNP @@ -1880,16 +1859,12 @@ static int wbsd_platform_resume(struct platform_device *dev)  static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)  {  	struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); -	struct wbsd_host *host;  	if (mmc == NULL)  		return 0;  	DBGF("Suspending...\n"); - -	host = mmc_priv(mmc); - -	return wbsd_suspend(host, state); +	return 0;  }  static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) @@ -1922,7 +1897,8 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)  	 */  	mdelay(5); -	return wbsd_resume(host); +	wbsd_init_device(host); +	return 0;  }  #endif /* CONFIG_PNP */ diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 34231d5168f..282891a8e45 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -212,28 +212,14 @@ struct wmt_mci_priv {  static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable)  { -	u32 reg_tmp; -	if (enable) { -		if (priv->power_inverted) { -			reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); -			writeb(reg_tmp | BM_SD_OFF, -			       priv->sdmmc_base + SDMMC_BUSMODE); -		} else { -			reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); -			writeb(reg_tmp & (~BM_SD_OFF), -			       priv->sdmmc_base + SDMMC_BUSMODE); -		} -	} else { -		if (priv->power_inverted) { -			reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); -			writeb(reg_tmp & (~BM_SD_OFF), -			       priv->sdmmc_base + SDMMC_BUSMODE); -		} else { -			reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); -			writeb(reg_tmp | BM_SD_OFF, -			       priv->sdmmc_base + SDMMC_BUSMODE); -		} -	} +	u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + +	if (enable ^ priv->power_inverted) +		reg_tmp &= ~BM_SD_OFF; +	else +		reg_tmp |= BM_SD_OFF; + +	writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE);  }  static void wmt_mci_read_response(struct mmc_host *mmc) @@ -771,7 +757,7 @@ static int wmt_mci_probe(struct platform_device *pdev)  	struct device_node *np = pdev->dev.of_node;  	const struct of_device_id *of_id =  		of_match_device(wmt_mci_dt_ids, &pdev->dev); -	const struct wmt_mci_caps *wmt_caps = of_id->data; +	const struct wmt_mci_caps *wmt_caps;  	int ret;  	int regular_irq, dma_irq; @@ -780,6 +766,8 @@ static int wmt_mci_probe(struct platform_device *pdev)  		return -EFAULT;  	} +	wmt_caps = of_id->data; +  	if (!np) {  		dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n");  		return -EFAULT; @@ -852,7 +840,7 @@ static int wmt_mci_probe(struct platform_device *pdev)  	priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev,  						   mmc->max_blk_count * 16,  						   &priv->dma_desc_device_addr, -						   208); +						   GFP_KERNEL);  	if (!priv->dma_desc_buffer) {  		dev_err(&pdev->dev, "DMA alloc fail\n");  		ret = -EPERM; @@ -939,28 +927,23 @@ static int wmt_mci_suspend(struct device *dev)  	struct platform_device *pdev = to_platform_device(dev);  	struct mmc_host *mmc = platform_get_drvdata(pdev);  	struct wmt_mci_priv *priv; -	int ret;  	if (!mmc)  		return 0;  	priv = mmc_priv(mmc); -	ret = mmc_suspend_host(mmc); - -	if (!ret) { -		reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); -		writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + -		       SDMMC_BUSMODE); +	reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); +	writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + +	       SDMMC_BUSMODE); -		reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); -		writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); +	reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); +	writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); -		writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); -		writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); +	writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); +	writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); -		clk_disable(priv->clk_sdmmc); -	} -	return ret; +	clk_disable(priv->clk_sdmmc); +	return 0;  }  static int wmt_mci_resume(struct device *dev) @@ -969,7 +952,6 @@ static int wmt_mci_resume(struct device *dev)  	struct platform_device *pdev = to_platform_device(dev);  	struct mmc_host *mmc = platform_get_drvdata(pdev);  	struct wmt_mci_priv *priv; -	int ret = 0;  	if (mmc) {  		priv = mmc_priv(mmc); @@ -987,10 +969,9 @@ static int wmt_mci_resume(struct device *dev)  		writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base +  		       SDMMC_INTMASK0); -		ret = mmc_resume_host(mmc);  	} -	return ret; +	return 0;  }  static const struct dev_pm_ops wmt_mci_pm = {  | 
