Commit da68c4eb258cd9f3f0b8aeb7e46b8118bb6358b6

Authored by Nicolas Pitre
Committed by Linus Torvalds
1 parent 9e506f35b1

sdio: introduce API for special power management features

This patch series provides the core changes needed to allow SDIO cards to
remain powered and active while the host system is suspended, and let them
wake up the host system when needed.  This is used to implement
wake-on-lan with SDIO wireless cards at the moment.  Patches to add that
support to the libertas driver will be posted separately.

This patch:

Some SDIO cards have the ability to keep on running autonomously when the
host system is suspended, and wake it up when needed.  This however
requires that the host controller preserve power to the card, and
configure itself appropriately for wake-up.

There is however 4 layers of abstractions involved: the host controller
driver, the MMC core code, the SDIO card management code, and the actual
SDIO function driver.  To make things simple and manageable, host drivers
must advertise their PM capabilities with a feature bitmask, then function
drivers can query and set those features from their suspend method.  Then
each layer in the suspend call chain is expected to act upon those bits
accordingly.

[akpm@linux-foundation.org: fix typo in comment]
Signed-off-by: Nicolas Pitre <nico@marvell.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 5 changed files with 98 additions and 3 deletions Side-by-side Diff

drivers/mmc/core/core.c
... ... @@ -1151,6 +1151,9 @@
1151 1151 cancel_delayed_work(&host->detect);
1152 1152 mmc_flush_scheduled_work();
1153 1153  
  1154 + /* clear pm flags now and let card drivers set them as needed */
  1155 + host->pm_flags = 0;
  1156 +
1154 1157 mmc_bus_get(host);
1155 1158 if (host->bus_ops && !host->bus_dead) {
1156 1159 if (host->bus_ops->remove)
1157 1160  
... ... @@ -1273,12 +1276,13 @@
1273 1276 mmc_claim_host(host);
1274 1277 mmc_detach_bus(host);
1275 1278 mmc_release_host(host);
  1279 + host->pm_flags = 0;
1276 1280 err = 0;
1277 1281 }
1278 1282 }
1279 1283 mmc_bus_put(host);
1280 1284  
1281   - if (!err)
  1285 + if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER))
1282 1286 mmc_power_off(host);
1283 1287  
1284 1288 return err;
... ... @@ -1296,8 +1300,10 @@
1296 1300  
1297 1301 mmc_bus_get(host);
1298 1302 if (host->bus_ops && !host->bus_dead) {
1299   - mmc_power_up(host);
1300   - mmc_select_voltage(host, host->ocr);
  1303 + if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
  1304 + mmc_power_up(host);
  1305 + mmc_select_voltage(host, host->ocr);
  1306 + }
1301 1307 BUG_ON(!host->bus_ops->resume);
1302 1308 err = host->bus_ops->resume(host);
1303 1309 if (err) {
drivers/mmc/core/sdio_io.c
... ... @@ -640,4 +640,53 @@
640 640 *err_ret = ret;
641 641 }
642 642 EXPORT_SYMBOL_GPL(sdio_f0_writeb);
  643 +
  644 +/**
  645 + * sdio_get_host_pm_caps - get host power management capabilities
  646 + * @func: SDIO function attached to host
  647 + *
  648 + * Returns a capability bitmask corresponding to power management
  649 + * features supported by the host controller that the card function
  650 + * might rely upon during a system suspend. The host doesn't need
  651 + * to be claimed, nor the function active, for this information to be
  652 + * obtained.
  653 + */
  654 +mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func)
  655 +{
  656 + BUG_ON(!func);
  657 + BUG_ON(!func->card);
  658 +
  659 + return func->card->host->pm_caps;
  660 +}
  661 +EXPORT_SYMBOL_GPL(sdio_get_host_pm_caps);
  662 +
  663 +/**
  664 + * sdio_set_host_pm_flags - set wanted host power management capabilities
  665 + * @func: SDIO function attached to host
  666 + *
  667 + * Set a capability bitmask corresponding to wanted host controller
  668 + * power management features for the upcoming suspend state.
  669 + * This must be called, if needed, each time the suspend method of
  670 + * the function driver is called, and must contain only bits that
  671 + * were returned by sdio_get_host_pm_caps().
  672 + * The host doesn't need to be claimed, nor the function active,
  673 + * for this information to be set.
  674 + */
  675 +int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
  676 +{
  677 + struct mmc_host *host;
  678 +
  679 + BUG_ON(!func);
  680 + BUG_ON(!func->card);
  681 +
  682 + host = func->card->host;
  683 +
  684 + if (flags & ~host->pm_caps)
  685 + return -EINVAL;
  686 +
  687 + /* function suspend methods are serialized, hence no lock needed */
  688 + host->pm_flags |= flags;
  689 + return 0;
  690 +}
  691 +EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags);
include/linux/mmc/host.h
... ... @@ -14,6 +14,7 @@
14 14 #include <linux/sched.h>
15 15  
16 16 #include <linux/mmc/core.h>
  17 +#include <linux/mmc/pm.h>
17 18  
18 19 struct mmc_ios {
19 20 unsigned int clock; /* clock rate */
... ... @@ -152,6 +153,8 @@
152 153 #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
153 154 #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
154 155  
  156 + mmc_pm_flag_t pm_caps; /* supported pm features */
  157 +
155 158 /* host specific block data */
156 159 unsigned int max_seg_size; /* see blk_queue_max_segment_size */
157 160 unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
... ... @@ -196,6 +199,8 @@
196 199 unsigned int sdio_irqs;
197 200 struct task_struct *sdio_irq_thread;
198 201 atomic_t sdio_irq_thread_abort;
  202 +
  203 + mmc_pm_flag_t pm_flags; /* requested pm features */
199 204  
200 205 #ifdef CONFIG_LEDS_TRIGGERS
201 206 struct led_trigger *led; /* activity led */
include/linux/mmc/pm.h
  1 +/*
  2 + * linux/include/linux/mmc/pm.h
  3 + *
  4 + * Author: Nicolas Pitre
  5 + * Copyright: (C) 2009 Marvell Technology Group Ltd.
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License version 2 as
  9 + * published by the Free Software Foundation.
  10 + */
  11 +
  12 +#ifndef LINUX_MMC_PM_H
  13 +#define LINUX_MMC_PM_H
  14 +
  15 +/*
  16 + * These flags are used to describe power management features that
  17 + * some cards (typically SDIO cards) might wish to benefit from when
  18 + * the host system is being suspended. There are several layers of
  19 + * abstractions involved, from the host controller driver, to the MMC core
  20 + * code, to the SDIO core code, to finally get to the actual SDIO function
  21 + * driver. This file is therefore used for common definitions shared across
  22 + * all those layers.
  23 + */
  24 +
  25 +typedef unsigned int mmc_pm_flag_t;
  26 +
  27 +#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */
  28 +#define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */
  29 +
  30 +#endif
include/linux/mmc/sdio_func.h
... ... @@ -15,6 +15,8 @@
15 15 #include <linux/device.h>
16 16 #include <linux/mod_devicetable.h>
17 17  
  18 +#include <linux/mmc/pm.h>
  19 +
18 20 struct mmc_card;
19 21 struct sdio_func;
20 22  
... ... @@ -152,6 +154,9 @@
152 154 unsigned int addr, int *err_ret);
153 155 extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
154 156 unsigned int addr, int *err_ret);
  157 +
  158 +extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func);
  159 +extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags);
155 160  
156 161 #endif