linux-block.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ulf Hansson <ulf.hansson@linaro.org>
To: linux-mmc@vger.kernel.org, Ulf Hansson <ulf.hansson@linaro.org>,
	Adrian Hunter <adrian.hunter@intel.com>
Cc: Linus Walleij <linus.walleij@linaro.org>,
	Wolfram Sang <wsa+renesas@sang-engineering.com>,
	Shawn Lin <shawn.lin@rock-chips.com>,
	Avri Altman <avri.altman@wdc.com>,
	Masami Hiramatsu <masami.hiramatsu@linaro.org>,
	linux-block@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 11/11] mmc: core: Add support for Power Off Notification for SD cards
Date: Tue,  4 May 2021 18:12:22 +0200	[thread overview]
Message-ID: <20210504161222.101536-12-ulf.hansson@linaro.org> (raw)
In-Reply-To: <20210504161222.101536-1-ulf.hansson@linaro.org>

Rather than only deselecting the SD card via a CMD7, before we cut power to
it at system suspend, at runtime suspend or at shutdown, let's add support
for a graceful power off sequence via enabling the SD Power Off
Notification feature.

Note that, the Power Off Notification feature was added in the SD spec
v4.x, which is several years ago. However, it's still a bit unclear how
often the SD card vendors decides to implement support for it. To validate
these changes a Sandisk Extreme PRO A2 64GB has been used, which seems to
work nicely.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/mmc/core/sd.c  | 136 ++++++++++++++++++++++++++++++++++++++++-
 include/linux/mmc/sd.h |   1 +
 2 files changed, 134 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 702d7c1a0aec..760aa86bd54d 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -66,6 +66,13 @@ static const unsigned int sd_au_size[] = {
 		__res & __mask;						\
 	})
 
+#define SD_POWEROFF_NOTIFY_TIMEOUT_MS 2000
+
+struct sd_busy_data {
+	struct mmc_card *card;
+	u8 *reg_buf;
+};
+
 /*
  * Given the decoded CSD structure, decode the raw CID to our CID structure.
  */
@@ -996,6 +1003,66 @@ static bool mmc_sd_card_using_v18(struct mmc_card *card)
 	       (SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50);
 }
 
+static int sd_write_ext_reg(struct mmc_card *card, u8 fno, u8 page, u16 offset,
+			    u8 reg_data)
+{
+	struct mmc_host *host = card->host;
+	struct mmc_request mrq = {};
+	struct mmc_command cmd = {};
+	struct mmc_data data = {};
+	struct scatterlist sg;
+	u8 *reg_buf;
+
+	reg_buf = kzalloc(512, GFP_KERNEL);
+	if (!reg_buf)
+		return -ENOMEM;
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+
+	/*
+	 * Arguments of CMD49:
+	 * [31:31] MIO (0 = memory).
+	 * [30:27] FNO (function number).
+	 * [26:26] MW - mask write mode (0 = disable).
+	 * [25:18] page number.
+	 * [17:9] offset address.
+	 * [8:0] length (0 = 1 byte).
+	 */
+	cmd.arg = fno << 27 | page << 18 | offset << 9;
+
+	/* The first byte in the buffer is the data to be written. */
+	reg_buf[0] = reg_data;
+
+	data.flags = MMC_DATA_WRITE;
+	data.blksz = 512;
+	data.blocks = 1;
+	data.sg = &sg;
+	data.sg_len = 1;
+	sg_init_one(&sg, reg_buf, 512);
+
+	cmd.opcode = SD_WRITE_EXTR_SINGLE;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	mmc_set_data_timeout(&data, card);
+	mmc_wait_for_req(host, &mrq);
+
+	kfree(reg_buf);
+
+	/*
+	 * Note that, the SD card is allowed to signal busy on DAT0 up to 1s
+	 * after the CMD49. Although, let's leave this to be managed by the
+	 * caller.
+	 */
+
+	if (cmd.error)
+		return cmd.error;
+	if (data.error)
+		return data.error;
+
+	return 0;
+}
+
 static int sd_read_ext_reg(struct mmc_card *card, u8 fno, u8 page,
 			   u16 offset, u16 len, u8 *reg_buf)
 {
@@ -1446,21 +1513,84 @@ static void mmc_sd_detect(struct mmc_host *host)
 	}
 }
 
+static int sd_can_poweroff_notify(struct mmc_card *card)
+{
+	return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
+}
+
+static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
+{
+	struct sd_busy_data *data = cb_data;
+	struct mmc_card *card = data->card;
+	int err;
+
+	/*
+	 * Read the status register for the power management function. It's at
+	 * one byte offset and is one byte long. The Power Off Notification
+	 * Ready is bit 0.
+	 */
+	err = sd_read_ext_reg(card, card->ext_power.fno, card->ext_power.page,
+			      card->ext_power.offset + 1, 1, data->reg_buf);
+	if (err) {
+		pr_warn("%s: error %d reading status reg of PM func\n",
+			mmc_hostname(card->host), err);
+		return err;
+	}
+
+	*busy = !(data->reg_buf[0] & 0x1);
+	return 0;
+}
+
+static int sd_poweroff_notify(struct mmc_card *card)
+{
+	struct sd_busy_data cb_data;
+	u8 *reg_buf;
+	int err;
+
+	reg_buf = kzalloc(512, GFP_KERNEL);
+	if (!reg_buf)
+		return -ENOMEM;
+
+	/*
+	 * Set the Power Off Notification bit in the power management settings
+	 * register at 2 bytes offset.
+	 */
+	err = sd_write_ext_reg(card, card->ext_power.fno, card->ext_power.page,
+			       card->ext_power.offset + 2, 0x1);
+	if (err) {
+		pr_warn("%s: error %d writing Power Off Notify bit\n",
+			mmc_hostname(card->host), err);
+		goto out;
+	}
+
+	cb_data.card = card;
+	cb_data.reg_buf = reg_buf;
+	err = __mmc_poll_for_busy(card, SD_POWEROFF_NOTIFY_TIMEOUT_MS,
+				  &sd_busy_poweroff_notify_cb, &cb_data);
+
+out:
+	kfree(reg_buf);
+	return err;
+}
+
 static int _mmc_sd_suspend(struct mmc_host *host)
 {
+	struct mmc_card *card = host->card;
 	int err = 0;
 
 	mmc_claim_host(host);
 
-	if (mmc_card_suspended(host->card))
+	if (mmc_card_suspended(card))
 		goto out;
 
-	if (!mmc_host_is_spi(host))
+	if (sd_can_poweroff_notify(card))
+		err = sd_poweroff_notify(card);
+	else if (!mmc_host_is_spi(host))
 		err = mmc_deselect_cards(host);
 
 	if (!err) {
 		mmc_power_off(host);
-		mmc_card_set_suspended(host->card);
+		mmc_card_set_suspended(card);
 	}
 
 out:
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 43bfc5c39ad4..6727576a8755 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -31,6 +31,7 @@
 
   /* class 11 */
 #define SD_READ_EXTR_SINGLE      48   /* adtc [31:0]             R1  */
+#define SD_WRITE_EXTR_SINGLE     49   /* adtc [31:0]             R1  */
 
 /* OCR bit definitions */
 #define SD_OCR_S18R		(1 << 24)    /* 1.8V switching request */
-- 
2.25.1


  parent reply	other threads:[~2021-05-04 16:13 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-04 16:12 [PATCH 00/11] Initital support for new power/perf features for SD cards Ulf Hansson
2021-05-04 16:12 ` [PATCH 01/11] mmc: core: Drop open coding when preparing commands with busy signaling Ulf Hansson
2021-05-06 12:50   ` Linus Walleij
2021-05-07  1:42   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 02/11] mmc: core: Take into account MMC_CAP_NEED_RSP_BUSY for eMMC HPI commands Ulf Hansson
2021-05-06 12:51   ` Linus Walleij
2021-05-07  1:44   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 03/11] mmc: core: Re-structure some code in __mmc_poll_for_busy() Ulf Hansson
2021-05-06 12:52   ` Linus Walleij
2021-05-07  1:48   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 04/11] mmc: core: Extend re-use of __mmc_poll_for_busy() Ulf Hansson
2021-05-06 12:53   ` Linus Walleij
2021-05-07  1:51   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 05/11] mmc: core: Enable eMMC sleep commands to use HW busy polling Ulf Hansson
2021-05-06 12:55   ` Linus Walleij
2021-05-07  1:52   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 06/11] mmc: core: Prepare mmc_send_cxd_data() to be re-used for additional cmds Ulf Hansson
2021-05-06 12:56   ` Linus Walleij
2021-05-06 13:00   ` Linus Walleij
2021-05-07  7:30     ` Ulf Hansson
2021-05-07  1:53   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 07/11] mmc: core: Drop open coding in mmc_sd_switch() Ulf Hansson
2021-05-06 12:57   ` Linus Walleij
2021-05-07  1:54   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 08/11] mmc: core: Parse the SD SCR register for support of CMD48/49 and CMD58/59 Ulf Hansson
2021-05-06 13:01   ` Linus Walleij
2021-05-07  1:58   ` Shawn Lin
2021-05-04 16:12 ` [PATCH 09/11] mmc: core: Read the SD function extension registers for power management Ulf Hansson
2021-05-06 13:04   ` Linus Walleij
2021-05-07  2:06   ` Shawn Lin
2021-05-07  7:27     ` Ulf Hansson
2021-05-07  7:48       ` Shawn Lin
2021-05-07 11:36         ` Ulf Hansson
2021-05-04 16:12 ` [PATCH 10/11] mmc: core: Read performance enhancements registers for SD cards Ulf Hansson
2021-05-06 13:05   ` Linus Walleij
2021-05-04 16:12 ` Ulf Hansson [this message]
2021-05-06 13:07   ` [PATCH 11/11] mmc: core: Add support for Power Off Notification " Linus Walleij
2021-05-07  6:44 ` [PATCH 00/11] Initital support for new power/perf features " Avri Altman
2021-05-07  7:31   ` Ulf Hansson
2021-05-11 10:56 ` Ulf Hansson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210504161222.101536-12-ulf.hansson@linaro.org \
    --to=ulf.hansson@linaro.org \
    --cc=adrian.hunter@intel.com \
    --cc=avri.altman@wdc.com \
    --cc=linus.walleij@linaro.org \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=masami.hiramatsu@linaro.org \
    --cc=shawn.lin@rock-chips.com \
    --cc=wsa+renesas@sang-engineering.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).