From patchwork Tue May 12 11:50:51 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Avi Shchislowski X-Patchwork-Id: 6387391 Return-Path: X-Original-To: patchwork-linux-mmc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 8B07EBEEE1 for ; Tue, 12 May 2015 12:05:48 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 09BBA203E3 for ; Tue, 12 May 2015 12:05:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 06A1C20328 for ; Tue, 12 May 2015 12:05:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932587AbbELMFo (ORCPT ); Tue, 12 May 2015 08:05:44 -0400 Received: from mail-bn1on0085.outbound.protection.outlook.com ([157.56.110.85]:48096 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S932312AbbELMFm convert rfc822-to-8bit (ORCPT ); Tue, 12 May 2015 08:05:42 -0400 X-Greylist: delayed 883 seconds by postgrey-1.27 at vger.kernel.org; Tue, 12 May 2015 08:05:42 EDT Received: from BY2PR02CA0096.namprd02.prod.outlook.com (25.163.44.150) by BN3PR0201MB1027.namprd02.prod.outlook.com (25.161.207.149) with Microsoft SMTP Server (TLS) id 15.1.160.19; Tue, 12 May 2015 11:50:57 +0000 Received: from BL2FFO11FD026.protection.gbl (2a01:111:f400:7c09::183) by BY2PR02CA0096.outlook.office365.com (2a01:111:e400:5261::22) with Microsoft SMTP Server (TLS) id 15.1.160.19 via Frontend Transport; Tue, 12 May 2015 11:50:56 +0000 Authentication-Results: spf=pass (sender IP is 74.221.232.162) smtp.mailfrom=sandisk.com; linaro.org; dkim=none (message not signed) header.d=none; Received-SPF: Pass (protection.outlook.com: domain of sandisk.com designates 74.221.232.162 as permitted sender) receiver=protection.outlook.com; client-ip=74.221.232.162; helo=sacsmgep11.sandisk.com; Received: from sacsmgep11.sandisk.com (74.221.232.162) by BL2FFO11FD026.mail.protection.outlook.com (10.173.161.105) with Microsoft SMTP Server id 15.1.160.8 via Frontend Transport; Tue, 12 May 2015 11:50:56 +0000 X-AuditID: ac1c210e-f79256d0000069d9-57-5551e91eb7e6 Received: from SACHUBIP01.sdcorp.global.sandisk.com ( [172.28.1.254]) by sacsmgep11.sandisk.com (Symantec Messaging Gateway) with SMTP id 31.69.27097.E19E1555; Tue, 12 May 2015 04:50:54 -0700 (PDT) Received: from SACMBXIP02.sdcorp.global.sandisk.com ([fe80::6420:780c:fa4c:c1e7]) by SACHUBIP01.sdcorp.global.sandisk.com ([10.181.10.103]) with mapi id 14.03.0224.002; Tue, 12 May 2015 04:50:52 -0700 From: Avi Shchislowski To: Ulf Hansson CC: Alex Lemberg , linux-mmc Subject: [RFC PATCH] mmc: sleep notification Thread-Topic: [RFC PATCH] mmc: sleep notification Thread-Index: AdCMqd6PVfY/UAj8QEiZDH/BmpHRrg== Date: Tue, 12 May 2015 11:50:51 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.181.8.64] MIME-Version: 1.0 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrLLMWRmVeSWpSXmKPExsWyRobxn67cy8BQg02XFS2O/O9ntDi+NtyB yePOtT1sHp83yQUwRXHZpKTmZJalFunbJXBl9O2/yl6wJKviyuoljA2Mn8K6GDk5JARMJL7e mcQIYYtJXLi3ng3EFhI4zijRv4avi5ELyD7HKNF8+RETSIJNwFRi3fcnYLaIgIbEnofnWUFs ZoEQib1v77KA2MICOhL7f59mg6gxlDhwahdQnAPI1pPY9VMNJMwioCqx78ZhZhCbVyBa4nbP HrAxjEA3fD+1hglipLjErSfzmSBuE5BYsuc8M4QtKvHy8T9WkJESAvIS10/bQZTrSCzY/YkN wtaWWLbwNdR4QYmTM5+wTGAUmYVk6iwkLbOQtMxC0rKAkWUVo1hxYnJxbnpqgaGhXnFiXkpm cbZecn7uJkZwJCjy7WDcN8X8EKMAB6MSD++EZ4GhQqyJZcWVuYcYJTiYlUR4t98HCvGmJFZW pRblxxeV5qQWH2KU5mBREueduFImVEggPbEkNTs1tSC1CCbLxMEp1cDIW7hEUPXy8y29B4zF 13ZuPd63VtLyt7R/w+SLNh8Z5KxCVigs/3GrKtI2w6B3+ayW4D+h6f6zi6+scruzlv2BP5v+ xdKcOY++nTiUNPH41MW3NN/qMH/Q1fOq5LZ29wjeWLhHrPKqXddKxfD7Bw9//ptj/Mfky0q2 tlVfrU0W3Zm2r3Yrw4rLSizFGYmGWsxFxYkA6iFYQYACAAA= X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1; BL2FFO11FD026; 1:T1azYy79/g2HC6f3YLNuhScQZQ/nnup1w1LM2UDhuHQB2vN3a6A5A4njOGpoG/Wf2yLiZWFuLqmh3Z6azqIcwAwAe8EUUePGVlySCd7fD+O/pF1L1Kpf0uhcGtmwtVLNdbzuO1/GSzdTRyrPbDaH/g4qzzRDZb1xoT/wAlK4tUgdzOOzihhCT1nFNSB4KNwwrrFGcpInzv1uyIlfgg2d8fL5d8Q4m0fcwktVObCS6qo1OWUDwGoD2rsLFfJOJf6L3HqVaLhKfA8D4rAh8oflgQ== X-Forefront-Antispam-Report: CIP:74.221.232.162; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10009020)(6009001)(438002)(189002)(199003)(77096005)(229853001)(47776003)(102836002)(33656002)(50986999)(16796002)(2930100002)(2920100001)(2900100001)(189998001)(55846006)(5001960100002)(46102003)(54356999)(106466001)(97756001)(110136002)(81156007)(19580395003)(575784001)(86362001)(46406003)(23726002)(19580405001)(50466002)(62966003)(92566002)(2656002)(77156002)(87936001)(7099028)(393754007); DIR:OUT; SFP:1101; SCL:1; SRVR:BN3PR0201MB1027; H:sacsmgep11.sandisk.com; FPR:; SPF:Pass; MLV:ovrnspm; MX:1; A:1; PTR:InfoDomainNonexistent; LANG:en; X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BN3PR0201MB1027; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(5005006)(3002001); SRVR:BN3PR0201MB1027; BCL:0; PCL:0; RULEID:; SRVR:BN3PR0201MB1027; X-Forefront-PRVS: 0574D4712B X-OriginatorOrg: sandisk.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 May 2015 11:50:56.3004 (UTC) X-MS-Exchange-CrossTenant-Id: fcd9ea9c-ae8c-460c-ab3c-3db42d7ac64d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=fcd9ea9c-ae8c-460c-ab3c-3db42d7ac64d; Ip=[74.221.232.162]; Helo=[sacsmgep11.sandisk.com] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN3PR0201MB1027 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Hi Ulf, Following your recommendation, we have moved the Sleep_Notification call from mmc_pm_notify() to mmc_suspend(). As mentioned in MELS discussion, we are still experiencing system crash on Suspend, after making this change. We will appreciate your review and comments on this RFC. This patch is implements the new additional state of Power_Off_Notification - SLEEP_NOTIFICATION. Until now, the implementation of Power_Off_Notification supported only three modes - POWERED_ON (0x01), POWER_OFF_SHORT (0x02) and POWER_OFF_LONG (0x03). As part of eMMC5.0 before moving to Sleep state hosts may set the POWER_OFF_NOTIFICATION byte to SLEEP_NOTIFICATION (0x04). After setting SLEEP_NOTIFICATION, host should wait for the busy line to be de-asserted. The max timeout allowed for busy line de-assertion defined in SLEEP_NOTIFICATION_TIME byte in EXT_CSD [216]. HPI may interrupt the SLEEP_NOTIFICATION operation. In that case POWER_OFF_NOTIFICATION byte will restore to POWERED_ON. Signed-off-by: Alex Lemberg Signed-off-by: Avi Shchislowski diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index eefdf25..9ad296d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2034,9 +2034,26 @@ 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 (unlikely(req && mmc_card_mmc(card) && + (card->ext_csd.power_off_notification == + EXT_CSD_SLEEP_NOTIFICATION))) { + /* restoring the power_off_notification + * field's state to as it was before so + * that the sleep notification will be + * able to resume later + */ + card->ext_csd.power_off_notification = EXT_CSD_POWER_ON; + } + + if (req && !mq->mqrq_prev->req) { /* claim host only for the first request */ mmc_get_card(card); + if (unlikely(req && + mmc_card_doing_sleep_notify(card))) { + mmc_interrupt_hpi(card); + mmc_card_clr_sleep_notify(card); + } + } ret = mmc_blk_part_switch(card, md); if (ret) { diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 8c61ddd..e1520c56 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -298,7 +298,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) BUG_ON(!card); - if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card)) + if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card) || + mmc_card_doing_sleep_notify(card)) return; err = mmc_read_bkops_status(card); @@ -2706,6 +2707,8 @@ int mmc_pm_notify(struct notifier_block *notify_block, err = host->bus_ops->pre_suspend(host); if (!err) break; + if (host->card && host->bus_ops->suspend) + break; /* Calling bus_ops->remove() with a claimed host can deadlock */ host->bus_ops->remove(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index e6f2de7..a3086a0 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -14,6 +14,7 @@ #include #define MMC_CMD_RETRIES 3 +#define MMC_SLEEP_NOTIFY_MAX_TIME 0x17 struct mmc_bus_ops { void (*remove)(struct mmc_host *); @@ -37,6 +38,7 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host, unsigned func_num); void mmc_init_erase(struct mmc_card *card); +int mmc_sleep_notify(struct mmc_card *card); void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a802863..7688923 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -59,6 +59,11 @@ static const unsigned int tacc_mant[] = { __res & __mask; \ }) +#define GET_SLEEP_NOTIFY_TIME(value) \ + (10 * (1 << (unsigned int)(value))) +#define GET_SLEEP_NOTIFY_TIME_MSEC(value) \ + (DIV_ROUND_UP(GET_SLEEP_NOTIFY_TIME(value), 1000)) + /* * Given the decoded CSD structure, decode the raw CID to our CID structure. */ @@ -582,6 +587,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.ffu_capable = (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) && !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1); + card->ext_csd.sleep_notify_time = + ext_csd[EXT_CSD_SLEEP_NOTIFICATION_TIME]; } out: return err; @@ -1529,6 +1536,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, card->ext_csd.hpi_en = 1; } + /* sleep notify enable/disable for eMMC 5.0 and above */ + if ((card->ext_csd.rev >= 7) && card->ext_csd.hpi_en && + (card->host->caps2 & MMC_CAP2_SLEEP_NOTIFY) && + card->ext_csd.sleep_notify_time > 0 && + card->ext_csd.sleep_notify_time <= + MMC_SLEEP_NOTIFY_MAX_TIME) { + card->can_sleep_notify = 1; + } + /* * If cache size is higher than 0, this indicates * the existence of cache and it can be turned on. @@ -1642,6 +1658,33 @@ out_release: return err; } +/* + * check if device is in program state (busy) + */ +static bool mmc_device_prg_state(struct mmc_card *card) +{ + int rc; + u32 status; + bool state; + + mmc_get_card(card); + rc = mmc_send_status(card, &status); + if (rc) { + pr_err("%s: Get card status fail. rc=%d\n", + mmc_hostname(card->host), rc); + state = false; + goto out; + } + + if (R1_CURRENT_STATE(status) == R1_STATE_PRG) + state = true; + else + state = false; +out: + mmc_put_card(card); + return state; +} + static int mmc_can_poweroff_notify(const struct mmc_card *card) { return card && @@ -1653,20 +1696,106 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) { unsigned int timeout = card->ext_csd.generic_cmd6_time; int err; + bool use_busy_signal; /* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */ if (notify_type == EXT_CSD_POWER_OFF_LONG) timeout = card->ext_csd.power_off_longtime; + else if (notify_type == EXT_CSD_SLEEP_NOTIFICATION) { + /* calculate the maximum timeout for the + * switch command when notifying the device + * that it is about to move to sleep */ + timeout = GET_SLEEP_NOTIFY_TIME_MSEC( + card->ext_csd.sleep_notify_time); + } + /* do not wait on busy signal in case of + * Sleep Notification - to let host get + * another requests + */ + use_busy_signal = (notify_type == EXT_CSD_SLEEP_NOTIFICATION) ? + false : true; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_POWER_OFF_NOTIFICATION, - notify_type, timeout, true, false, false); - if (err) + notify_type, timeout, + use_busy_signal, false, false); + if (!err) { + card->ext_csd.power_off_notification = notify_type; + } else { pr_err("%s: Power Off Notification timed out, %u\n", - mmc_hostname(card->host), timeout); + mmc_hostname(card->host), timeout); + } - /* Disable the power off notification after the switch operation. */ - card->ext_csd.power_off_notification = EXT_CSD_NO_POWER_NOTIFICATION; + return err; +} + +int mmc_sleep_notify(struct mmc_card *card) +{ + int err = 0; + bool is_busy = 0; + unsigned long prg_wait = 0; + + if (!card->can_sleep_notify || !mmc_can_poweroff_notify(card)) + return 0; + + if (!mmc_card_doing_sleep_notify(card)) { + mmc_get_card(card); + mmc_card_set_sleep_notify(card); + err = mmc_poweroff_notify(card, + EXT_CSD_SLEEP_NOTIFICATION); + mmc_put_card(card); + if (err) { + pr_err("%s: mmc_poweroff_notify failed with %d\n", + __func__, err); + goto out; + } + + prg_wait = jiffies + + msecs_to_jiffies(GET_SLEEP_NOTIFY_TIME_MSEC( + card->ext_csd.sleep_notify_time)); + } + + /* + * Loop will run until: + * 1. Device is no more in Busy state + * 2. Sleep notification is not interrupted by HPI & IO request + */ + do { + /* added some delay to avoid sending card status too often */ + msleep(20); + err = 0; + /* Stop polling in case sleep notification was HPIed already */ + if (!mmc_card_doing_sleep_notify(card)) { + is_busy = mmc_device_prg_state(card); + if (is_busy) + err = -EBUSY; + break; + } + is_busy = mmc_device_prg_state(card); + if (is_busy && time_after(jiffies, prg_wait)) { + /* + * making sure we are still in busy before + * sending HPI due to timeout error + */ + is_busy = mmc_device_prg_state(card); + if (is_busy) { + if (mmc_card_doing_sleep_notify(card)) { + card->ext_csd.power_off_notification = + EXT_CSD_POWER_ON; + mmc_interrupt_hpi(card); + } + err = -ETIMEDOUT; + break; + } + } + } while (is_busy); + +out: + mmc_card_clr_sleep_notify(card); + if (err) { + pr_err("%s: mmc_poweroff_notify for sleep failed with %d\n", + mmc_hostname(card->host), err); + } return err; } @@ -1745,8 +1874,16 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) goto out; if (mmc_can_poweroff_notify(host->card) && - ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend)) - err = mmc_poweroff_notify(host->card, notify_type); + ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend)) { + if (!host->card->can_sleep_notify || + !mmc_can_poweroff_notify(host->card)) { + err = mmc_poweroff_notify(host->card, notify_type); + } else { + err = mmc_sleep_notify(host->card); + if (err != -ETIMEDOUT) + err = mmc_sleep(host); + } + } else if (mmc_can_sleep(host->card)) err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 19f0175..ed91e6f 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -62,6 +62,7 @@ struct mmc_ext_csd { unsigned int sa_timeout; /* Units: 100ns */ unsigned int generic_cmd6_time; /* Units: 10ms */ unsigned int power_off_longtime; /* Units: ms */ + unsigned int sleep_notify_time; /* Units: us */ u8 power_off_notification; /* state */ unsigned int hs_max_dtr; unsigned int hs200_max_dtr; @@ -262,6 +263,7 @@ struct mmc_card { #define MMC_CARD_REMOVED (1<<4) /* card has been removed */ #define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */ #define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */ +#define MMC_STATE_SLEEP_NOTIFY (1<<7) /* card in sleep notify */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -309,6 +311,7 @@ struct mmc_card { struct dentry *debugfs_root; struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ unsigned int nr_parts; + u8 can_sleep_notify; /* sleep_notify on/off */ }; /* @@ -427,6 +430,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) #define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED) +#define mmc_card_doing_sleep_notify(c) ((c)->state & MMC_STATE_SLEEP_NOTIFY) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -437,6 +441,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS) #define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED) #define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED) +#define mmc_card_set_sleep_notify(c) ((c)->state |= MMC_STATE_SLEEP_NOTIFY) +#define mmc_card_clr_sleep_notify(c) ((c)->state &= ~MMC_STATE_SLEEP_NOTIFY) /* * Quirk add/remove for MMC products. diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f471193..111e05d 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -286,6 +286,7 @@ struct mmc_host { MMC_CAP2_HS400_1_2V) #define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V) #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17) +#define MMC_CAP2_SLEEP_NOTIFY (1 << 18) /* sleep notify supported */ mmc_pm_flag_t pm_caps; /* supported pm features */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 124f562..bbb71ae 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -309,6 +309,7 @@ struct _mmc_csd { #define EXT_CSD_PWR_CL_52_360 202 /* RO */ #define EXT_CSD_PWR_CL_26_360 203 /* RO */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_SLEEP_NOTIFICATION_TIME 216 /* RO */ #define EXT_CSD_S_A_TIMEOUT 217 /* RO */ #define EXT_CSD_REL_WR_SEC_C 222 /* RO */ #define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */ @@ -403,6 +404,7 @@ struct _mmc_csd { #define EXT_CSD_POWER_ON 1 #define EXT_CSD_POWER_OFF_SHORT 2 #define EXT_CSD_POWER_OFF_LONG 3 +#define EXT_CSD_SLEEP_NOTIFICATION 4 #define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */ #define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */