Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 217cf955 authored by Dov Levenglick's avatar Dov Levenglick
Browse files

mmc: add support for bkops



Add support for manual and auto bkops for
legacy (not command-queue) mode.

Change-Id: I1333e1d4330393e446ba48a9474c83295744c050
Signed-off-by: default avatarDov Levenglick <dovl@codeaurora.org>
parent d9889219
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@

#include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
@@ -3280,7 +3281,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
	unsigned long flags;
	unsigned int cmd_flags = req ? req->cmd_flags : 0;

	if (req && !mq->mqrq_prev->req)
	if (req && !mq->mqrq_prev->req) {
		/* claim host only for the first request */
		mmc_get_card(card);

@@ -3288,6 +3289,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
	if (mmc_bus_needs_resume(card->host))
		mmc_resume_bus(card->host);
#endif
		if (mmc_card_doing_bkops(host->card)) {
			ret = mmc_stop_bkops(host->card);
			if (ret)
				goto out;
		}
	}

	ret = mmc_blk_part_switch(card, md);
	if (ret) {
@@ -3811,7 +3818,14 @@ static int mmc_blk_probe(struct mmc_card *card)
			goto out;
	}

	pm_runtime_set_autosuspend_delay(&card->dev, 3000);
	pm_runtime_set_autosuspend_delay(&card->dev, MMC_AUTOSUSPEND_DELAY_MS);
	/*
	 * If there is a runtime_idle function, it should take care of
	 * suspending the card
	 */
	if (card->host->bus_ops->runtime_idle)
		pm_runtime_dont_use_autosuspend(&card->dev);
	else
		pm_runtime_use_autosuspend(&card->dev);

	/*
+11 −1
Original line number Diff line number Diff line
@@ -205,10 +205,20 @@ static int mmc_runtime_resume(struct device *dev)

	return host->bus_ops->runtime_resume(host);
}

static int mmc_runtime_idle(struct device *dev)
{
	struct mmc_card *card = mmc_dev_to_card(dev);
	struct mmc_host *host = card->host;

	return host->bus_ops->runtime_idle(host);
}

#endif /* !CONFIG_PM_RUNTIME */

static const struct dev_pm_ops mmc_bus_pm_ops = {
	SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, NULL)
	SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume,
			mmc_runtime_idle)
	SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
};

+114 −40
Original line number Diff line number Diff line
@@ -865,24 +865,71 @@ static void mmc_start_cmdq_request(struct mmc_host *host,
}

/**
 *	mmc_start_bkops - start BKOPS for supported cards
 *	mmc_set_auto_bkops - set auto BKOPS for supported cards
 *	@card: MMC card to start BKOPS
 *	@form_exception: A flag to indicate if this function was
 *			 called due to an exception raised by the card
 *	@enable: enable/disable flag
 *
 *	Start background operations whenever requested.
 *	When the urgent BKOPS bit is set in a R1 command response
 *	then background operations should be started immediately.
 *	Configure the card to run automatic BKOPS.
 *
 *	Should be called when host is claimed.
*/
void mmc_start_bkops(struct mmc_card *card, bool from_exception)
int mmc_set_auto_bkops(struct mmc_card *card, bool enable)
{
	int ret = 0;
	u8 bkops_en;

	BUG_ON(!card);
	enable = !!enable;

	if (unlikely(!mmc_card_support_auto_bkops(card))) {
		pr_err("%s: %s: card doesn't support auto bkops\n",
				mmc_hostname(card->host), __func__);
		return -EPERM;
	}

	if (enable) {
		if (mmc_card_doing_auto_bkops(card))
			goto out;
		bkops_en = card->ext_csd.bkops_en | EXT_CSD_BKOPS_AUTO_EN;
	} else {
		if (!mmc_card_doing_auto_bkops(card))
			goto out;
		bkops_en = card->ext_csd.bkops_en & ~EXT_CSD_BKOPS_AUTO_EN;
	}

	ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN,
			bkops_en, 0);
	if (ret) {
		pr_err("%s: %s: error in setting auto bkops to %d (%d)\n",
			mmc_hostname(card->host), __func__, enable, ret);
	} else {
		if (enable)
			mmc_card_set_auto_bkops(card);
		else
			mmc_card_clr_auto_bkops(card);
		card->ext_csd.bkops_en = bkops_en;
	}
out:
	return ret;
}
EXPORT_SYMBOL(mmc_set_auto_bkops);

/**
 *	mmc_check_bkops - check BKOPS for supported cards
 *	@card: MMC card to check BKOPS
 *
 *	Read the BKOPS status in order to determine whether the
 *	card requires bkops to be started.
*/
void mmc_check_bkops(struct mmc_card *card)
{
	int err;
	int timeout;
	bool use_busy_signal;

	BUG_ON(!card);

	if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
	if (unlikely(!mmc_card_configured_manual_bkops(card)))
		return;
	if (mmc_card_doing_bkops(card) || mmc_card_doing_auto_bkops(card))
		return;

	err = mmc_read_bkops_status(card);
@@ -892,42 +939,44 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
		return;
	}

	if (!card->ext_csd.raw_bkops_status)
	if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2)
		return;

	card->bkops.needs_manual = true;
}
EXPORT_SYMBOL(mmc_check_bkops);

/**
 *	mmc_start_manual_bkops - start BKOPS for supported cards
 *	@card: MMC card to start BKOPS
 *
 *	Send START_BKOPS to the card.
*/
void mmc_start_manual_bkops(struct mmc_card *card)
{
	int err;

	BUG_ON(!card);

	if (unlikely(!mmc_card_configured_manual_bkops(card)))
		return;

	if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
	    from_exception)
	if (mmc_card_doing_bkops(card))
		return;

	mmc_claim_host(card->host);
	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
		timeout = MMC_BKOPS_MAX_TIMEOUT;
		use_busy_signal = true;
	} else {
		timeout = 0;
		use_busy_signal = false;
	}

	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
			EXT_CSD_BKOPS_START, 1, timeout,
			use_busy_signal, true, false);
	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_START,
				1, 0, false, true, false);
	if (err) {
		pr_warn("%s: Error %d starting bkops\n",
		pr_err("%s: Error %d starting manual bkops\n",
				mmc_hostname(card->host), err);
		goto out;
	}

	/*
	 * For urgent bkops status (LEVEL_2 and more)
	 * bkops executed synchronously, otherwise
	 * the operation is in progress
	 */
	if (!use_busy_signal)
	} else {
		mmc_card_set_doing_bkops(card);
out:
		card->bkops.needs_manual = false;
	}
	mmc_release_host(card->host);
}
EXPORT_SYMBOL(mmc_start_bkops);
EXPORT_SYMBOL(mmc_start_manual_bkops);

/*
 * mmc_wait_data_done() - done callback for data request
@@ -1280,7 +1329,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
		    ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
		     (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
		    (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))
			mmc_start_bkops(host->card, true);
			mmc_check_bkops(host->card);
	}

	if (!err && areq) {
@@ -1437,6 +1486,11 @@ int mmc_stop_bkops(struct mmc_card *card)
	int err = 0;

	BUG_ON(!card);
	if (unlikely(!mmc_card_configured_manual_bkops(card)))
		goto out;
	if (!mmc_card_doing_bkops(card))
		goto out;

	err = mmc_interrupt_hpi(card);

	/*
@@ -1447,7 +1501,7 @@ int mmc_stop_bkops(struct mmc_card *card)
		mmc_card_clr_doing_bkops(card);
		err = 0;
	}

out:
	return err;
}
EXPORT_SYMBOL(mmc_stop_bkops);
@@ -1705,6 +1759,19 @@ void mmc_get_card(struct mmc_card *card)
}
EXPORT_SYMBOL(mmc_get_card);

/*
 * This is a helper function, which drops the runtime
 * pm reference for the card device.
 */
static void __mmc_put_card(struct mmc_card *card)
{
	/* In case of runtime_idle, it will handle the suspend */
	if (card->host->bus_ops->runtime_idle)
		pm_runtime_put(&card->dev);
	else
		pm_runtime_put_autosuspend(&card->dev);
}

/*
 * This is a helper function, which releases the host and drops the runtime
 * pm reference for the card device.
@@ -1713,7 +1780,7 @@ void mmc_put_card(struct mmc_card *card)
{
	mmc_release_host(card->host);
	pm_runtime_mark_last_busy(&card->dev);
	pm_runtime_put_autosuspend(&card->dev);
	__mmc_put_card(card);
}
EXPORT_SYMBOL(mmc_put_card);

@@ -1725,6 +1792,13 @@ void mmc_set_ios(struct mmc_host *host)
{
	struct mmc_ios *ios = &host->ios;

	if (unlikely(ios->power_mode == MMC_POWER_OFF &&
		     host->card && mmc_card_doing_auto_bkops(host->card))) {
		pr_err("%s: %s: illegal to disable power to card when running auto bkops\n",
				mmc_hostname(host), __func__);
		return;
	}

	pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
		"width %u timing %u\n",
		 mmc_hostname(host), ios->clock, ios->bus_mode,
+0 −15
Original line number Diff line number Diff line
@@ -15,21 +15,6 @@

#define MMC_CMD_RETRIES        3

struct mmc_bus_ops {
	void (*remove)(struct mmc_host *);
	void (*detect)(struct mmc_host *);
	int (*pre_suspend)(struct mmc_host *);
	int (*suspend)(struct mmc_host *);
	int (*resume)(struct mmc_host *);
	int (*runtime_suspend)(struct mmc_host *);
	int (*runtime_resume)(struct mmc_host *);
	int (*power_save)(struct mmc_host *);
	int (*power_restore)(struct mmc_host *);
	int (*alive)(struct mmc_host *);
	int (*shutdown)(struct mmc_host *);
	int (*change_bus_speed)(struct mmc_host *, unsigned long *);
};

void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
void mmc_detach_bus(struct mmc_host *host);

+51 −3
Original line number Diff line number Diff line
@@ -570,6 +570,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
		 */
		card->ext_csd.out_of_int_time =
			ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
		pr_info("%s: Out-of-interrupt timeout is %d[ms]\n",
				mmc_hostname(card->host),
				card->ext_csd.out_of_int_time);
	}

	if (card->ext_csd.rev >= 5) {
@@ -584,9 +587,10 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
			card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
			card->ext_csd.raw_bkops_status =
				ext_csd[EXT_CSD_BKOPS_STATUS];
			if (!card->ext_csd.bkops_en)
				pr_info("%s: BKOPS_EN bit is not set\n",
					mmc_hostname(card->host));
			pr_info("%s: BKOPS_EN equals 0x%x\n",
					mmc_hostname(card->host),
					card->ext_csd.bkops_en);

		}

		card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
@@ -1925,6 +1929,23 @@ reinit:
		}
	}

	/*
	 * Start auto bkops, if supported.
	 *
	 * Note: This leaves the possibility of having both manual and
	 * auto bkops running in parallel. The runtime implementation
	 * will allow this, but ignore bkops exceptions on the premises
	 * that auto bkops will eventually kick in and the device will
	 * handle bkops without START_BKOPS from the host.
	 */
	if (mmc_card_support_auto_bkops(card)) {
		/*
		 * Ignore the return value of setting auto bkops.
		 * If it failed, will run in backward compatible mode.
		 */
		(void)mmc_set_auto_bkops(card, true);
	}

	return 0;

free_card:
@@ -2110,6 +2131,12 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
			goto out;
	}

	if (mmc_card_doing_auto_bkops(host->card)) {
		err = mmc_set_auto_bkops(host->card, false);
		if (err)
			goto out;
	}

	err = mmc_flush_cache(host->card);
	if (err)
		goto out;
@@ -2299,6 +2326,26 @@ static int mmc_power_restore(struct mmc_host *host)
	return ret;
}

#define NO_AUTOSUSPEND	(-1)
static int mmc_runtime_idle(struct mmc_host *host)
{
	BUG_ON(!host->card);

	if (host->card->bkops.needs_manual)
		mmc_start_manual_bkops(host->card);

	pm_runtime_mark_last_busy(&host->card->dev);
	/*
	 * TODO: consider prolonging suspend when bkops
	 * is running in order to allow a longer time for
	 * bkops to complete
	 * */
	pm_schedule_suspend(&host->card->dev, MMC_AUTOSUSPEND_DELAY_MS);

	/* return negative value in order to avoid autosuspend */
	return NO_AUTOSUSPEND;
}

static const struct mmc_bus_ops mmc_ops = {
	.remove = mmc_remove,
	.detect = mmc_detect,
@@ -2306,6 +2353,7 @@ static const struct mmc_bus_ops mmc_ops = {
	.resume = mmc_resume,
	.runtime_suspend = mmc_runtime_suspend,
	.runtime_resume = mmc_runtime_resume,
	.runtime_idle = mmc_runtime_idle,
	.power_restore = mmc_power_restore,
	.alive = mmc_alive,
	.shutdown = mmc_shutdown,
Loading