You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (66 commits) mmc: add new sdhci-pxa driver for Marvell SoCs mmc: make number of mmcblk minors configurable mmc_spi: Recover from CRC errors for r/w operation over SPI. mmc: sdhci-pltfm: add -pltfm driver for imx35/51 mmc: sdhci-of-esdhc: factor out common stuff mmc: sdhci_pltfm: pass more data on custom init call mmc: sdhci: introduce get_ro private write-protect hook mmc: sdhci-pltfm: move .h file into appropriate subdir mmc: sdhci-pltfm: Add structure for host-specific data mmc: fix cb710 kconfig dependency warning mmc: cb710: remove debugging printk (info duplicated from mmc-core) mmc: cb710: clear irq handler on init() error path mmc: cb710: remove unnecessary msleep() mmc: cb710: implement get_cd() callback mmc: cb710: partially demystify clock selection mmc: add a file to debugfs for changing host clock at runtime mmc: sdhci: allow for eMMC 74 clock generation by controller mmc: sdhci: highspeed: check for mmc as well as sd cards mmc: sdhci: Add Moorestown device support mmc: sdhci: Intel Medfield support ...
This commit is contained in:
@@ -2520,6 +2520,12 @@ Your cooperation is appreciated.
|
||||
8 = /dev/mmcblk1 Second SD/MMC card
|
||||
...
|
||||
|
||||
The start of next SD/MMC card can be configured with
|
||||
CONFIG_MMC_BLOCK_MINORS, or overridden at boot/modprobe
|
||||
time using the mmcblk.perdev_minors option. That would
|
||||
bump the offset between each card to be the configured
|
||||
value instead of the default 8.
|
||||
|
||||
179 char CCube DVXChip-based PCI products
|
||||
0 = /dev/dvxirq0 First DVX device
|
||||
1 = /dev/dvxirq1 Second DVX device
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* linux/arch/arm/plat-pxa/include/plat/sdhci.h
|
||||
*
|
||||
* Copyright 2010 Marvell
|
||||
* Zhangfei Gao <zhangfei.gao@marvell.com>
|
||||
*
|
||||
* PXA Platform - SDHCI platform data definitions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __PLAT_PXA_SDHCI_H
|
||||
#define __PLAT_PXA_SDHCI_H
|
||||
|
||||
/* pxa specific flag */
|
||||
/* Require clock free running */
|
||||
#define PXA_FLAG_DISABLE_CLOCK_GATING (1<<0)
|
||||
|
||||
/*
|
||||
* struct pxa_sdhci_platdata() - Platform device data for PXA SDHCI
|
||||
* @max_speed: the maximum speed supported
|
||||
* @quirks: quirks of specific device
|
||||
* @flags: flags for platform requirement
|
||||
*/
|
||||
struct sdhci_pxa_platdata {
|
||||
unsigned int max_speed;
|
||||
unsigned int quirks;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
#endif /* __PLAT_PXA_SDHCI_H */
|
||||
@@ -2,9 +2,7 @@
|
||||
# Makefile for the kernel mmc device drivers.
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MMC_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
subdir-ccflags-$(CONFIG_MMC_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_MMC) += core/
|
||||
obj-$(CONFIG_MMC) += card/
|
||||
|
||||
@@ -14,6 +14,23 @@ config MMC_BLOCK
|
||||
mount the filesystem. Almost everyone wishing MMC support
|
||||
should say Y or M here.
|
||||
|
||||
config MMC_BLOCK_MINORS
|
||||
int "Number of minors per block device"
|
||||
range 4 256
|
||||
default 8
|
||||
help
|
||||
Number of minors per block device. One is needed for every
|
||||
partition on the disk (plus one for the whole disk).
|
||||
|
||||
Number of total MMC minors available is 256, so your number
|
||||
of supported block devices will be limited to 256 divided
|
||||
by this number.
|
||||
|
||||
Default is 8 to be backwards compatible with previous
|
||||
hardwired device numbering.
|
||||
|
||||
If unsure, say 8 here.
|
||||
|
||||
config MMC_BLOCK_BOUNCE
|
||||
bool "Use bounce buffer for simple hosts"
|
||||
depends on MMC_BLOCK
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
# Makefile for MMC/SD card drivers
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MMC_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
|
||||
mmc_block-objs := block.o queue.o
|
||||
obj-$(CONFIG_MMC_TEST) += mmc_test.o
|
||||
|
||||
+38
-27
@@ -43,15 +43,27 @@
|
||||
#include "queue.h"
|
||||
|
||||
MODULE_ALIAS("mmc:block");
|
||||
|
||||
/*
|
||||
* max 8 partitions per card
|
||||
*/
|
||||
#define MMC_SHIFT 3
|
||||
#define MMC_NUM_MINORS (256 >> MMC_SHIFT)
|
||||
#ifdef MODULE_PARAM_PREFIX
|
||||
#undef MODULE_PARAM_PREFIX
|
||||
#endif
|
||||
#define MODULE_PARAM_PREFIX "mmcblk."
|
||||
|
||||
static DEFINE_MUTEX(block_mutex);
|
||||
static DECLARE_BITMAP(dev_use, MMC_NUM_MINORS);
|
||||
|
||||
/*
|
||||
* The defaults come from config options but can be overriden by module
|
||||
* or bootarg options.
|
||||
*/
|
||||
static int perdev_minors = CONFIG_MMC_BLOCK_MINORS;
|
||||
|
||||
/*
|
||||
* We've only got one major, so number of mmcblk devices is
|
||||
* limited to 256 / number of minors per device.
|
||||
*/
|
||||
static int max_devices;
|
||||
|
||||
/* 256 minors, so at most 256 separate devices */
|
||||
static DECLARE_BITMAP(dev_use, 256);
|
||||
|
||||
/*
|
||||
* There is one mmc_blk_data per slot.
|
||||
@@ -67,6 +79,9 @@ struct mmc_blk_data {
|
||||
|
||||
static DEFINE_MUTEX(open_lock);
|
||||
|
||||
module_param(perdev_minors, int, 0444);
|
||||
MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
|
||||
|
||||
static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
@@ -88,10 +103,10 @@ static void mmc_blk_put(struct mmc_blk_data *md)
|
||||
md->usage--;
|
||||
if (md->usage == 0) {
|
||||
int devmaj = MAJOR(disk_devt(md->disk));
|
||||
int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT;
|
||||
int devidx = MINOR(disk_devt(md->disk)) / perdev_minors;
|
||||
|
||||
if (!devmaj)
|
||||
devidx = md->disk->first_minor >> MMC_SHIFT;
|
||||
devidx = md->disk->first_minor / perdev_minors;
|
||||
|
||||
blk_cleanup_queue(md->queue.queue);
|
||||
|
||||
@@ -373,7 +388,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
||||
readcmd = MMC_READ_SINGLE_BLOCK;
|
||||
writecmd = MMC_WRITE_BLOCK;
|
||||
}
|
||||
|
||||
if (rq_data_dir(req) == READ) {
|
||||
brq.cmd.opcode = readcmd;
|
||||
brq.data.flags |= MMC_DATA_READ;
|
||||
@@ -567,8 +581,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
struct mmc_blk_data *md;
|
||||
int devidx, ret;
|
||||
|
||||
devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
|
||||
if (devidx >= MMC_NUM_MINORS)
|
||||
devidx = find_first_zero_bit(dev_use, max_devices);
|
||||
if (devidx >= max_devices)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
__set_bit(devidx, dev_use);
|
||||
|
||||
@@ -585,7 +599,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
*/
|
||||
md->read_only = mmc_blk_readonly(card);
|
||||
|
||||
md->disk = alloc_disk(1 << MMC_SHIFT);
|
||||
md->disk = alloc_disk(perdev_minors);
|
||||
if (md->disk == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_kfree;
|
||||
@@ -602,7 +616,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
md->queue.data = md;
|
||||
|
||||
md->disk->major = MMC_BLOCK_MAJOR;
|
||||
md->disk->first_minor = devidx << MMC_SHIFT;
|
||||
md->disk->first_minor = devidx * perdev_minors;
|
||||
md->disk->fops = &mmc_bdops;
|
||||
md->disk->private_data = md;
|
||||
md->disk->queue = md->queue.queue;
|
||||
@@ -620,7 +634,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
* messages to tell when the card is present.
|
||||
*/
|
||||
|
||||
sprintf(md->disk->disk_name, "mmcblk%d", devidx);
|
||||
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
|
||||
"mmcblk%d", devidx);
|
||||
|
||||
blk_queue_logical_block_size(md->queue.queue, 512);
|
||||
|
||||
@@ -651,23 +666,15 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
static int
|
||||
mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
int err;
|
||||
|
||||
/* Block-addressed cards ignore MMC_SET_BLOCKLEN. */
|
||||
if (mmc_card_blockaddr(card))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
cmd.arg = 512;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 5);
|
||||
err = mmc_set_blocklen(card, 512);
|
||||
mmc_release_host(card->host);
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
|
||||
md->disk->disk_name, cmd.arg, err);
|
||||
printk(KERN_ERR "%s: unable to set block size to 512: %d\n",
|
||||
md->disk->disk_name, err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -678,7 +685,6 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
int err;
|
||||
|
||||
char cap_str[10];
|
||||
|
||||
/*
|
||||
@@ -768,6 +774,11 @@ static int __init mmc_blk_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
|
||||
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
|
||||
|
||||
max_devices = 256 / perdev_minors;
|
||||
|
||||
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
+392
-77
File diff suppressed because it is too large
Load Diff
@@ -146,7 +146,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMC_BLOCK_BOUNCE
|
||||
if (host->max_hw_segs == 1) {
|
||||
if (host->max_segs == 1) {
|
||||
unsigned int bouncesz;
|
||||
|
||||
bouncesz = MMC_QUEUE_BOUNCESZ;
|
||||
@@ -196,21 +196,23 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
|
||||
blk_queue_bounce_limit(mq->queue, limit);
|
||||
blk_queue_max_hw_sectors(mq->queue,
|
||||
min(host->max_blk_count, host->max_req_size / 512));
|
||||
blk_queue_max_segments(mq->queue, host->max_hw_segs);
|
||||
blk_queue_max_segments(mq->queue, host->max_segs);
|
||||
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
|
||||
|
||||
mq->sg = kmalloc(sizeof(struct scatterlist) *
|
||||
host->max_phys_segs, GFP_KERNEL);
|
||||
host->max_segs, GFP_KERNEL);
|
||||
if (!mq->sg) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup_queue;
|
||||
}
|
||||
sg_init_table(mq->sg, host->max_phys_segs);
|
||||
sg_init_table(mq->sg, host->max_segs);
|
||||
}
|
||||
|
||||
init_MUTEX(&mq->thread_sem);
|
||||
sema_init(&mq->thread_sem, 1);
|
||||
|
||||
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d",
|
||||
host->index);
|
||||
|
||||
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");
|
||||
if (IS_ERR(mq->thread)) {
|
||||
ret = PTR_ERR(mq->thread);
|
||||
goto free_bounce_sg;
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
# Makefile for the kernel mmc core.
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MMC_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MMC) += mmc_core.o
|
||||
mmc_core-y := core.o bus.o host.o \
|
||||
mmc.o mmc_ops.o sd.o sd_ops.o \
|
||||
|
||||
+48
-10
@@ -14,6 +14,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
@@ -22,13 +23,12 @@
|
||||
#include "sdio_cis.h"
|
||||
#include "bus.h"
|
||||
|
||||
#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
|
||||
#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
|
||||
|
||||
static ssize_t mmc_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
switch (card->type) {
|
||||
case MMC_TYPE_MMC:
|
||||
@@ -62,7 +62,7 @@ static int mmc_bus_match(struct device *dev, struct device_driver *drv)
|
||||
static int
|
||||
mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
const char *type;
|
||||
int retval = 0;
|
||||
|
||||
@@ -105,7 +105,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
static int mmc_bus_probe(struct device *dev)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return drv->probe(card);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ static int mmc_bus_probe(struct device *dev)
|
||||
static int mmc_bus_remove(struct device *dev)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
drv->remove(card);
|
||||
|
||||
@@ -123,7 +123,7 @@ static int mmc_bus_remove(struct device *dev)
|
||||
static int mmc_bus_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && drv->suspend)
|
||||
@@ -134,7 +134,7 @@ static int mmc_bus_suspend(struct device *dev, pm_message_t state)
|
||||
static int mmc_bus_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && drv->resume)
|
||||
@@ -142,6 +142,41 @@ static int mmc_bus_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int mmc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return mmc_power_save_host(card->host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return mmc_power_restore_host(card->host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_idle(struct device *dev)
|
||||
{
|
||||
return pm_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mmc_bus_pm_ops = {
|
||||
.runtime_suspend = mmc_runtime_suspend,
|
||||
.runtime_resume = mmc_runtime_resume,
|
||||
.runtime_idle = mmc_runtime_idle,
|
||||
};
|
||||
|
||||
#define MMC_PM_OPS_PTR (&mmc_bus_pm_ops)
|
||||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
#define MMC_PM_OPS_PTR NULL
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static struct bus_type mmc_bus_type = {
|
||||
.name = "mmc",
|
||||
.dev_attrs = mmc_dev_attrs,
|
||||
@@ -151,6 +186,7 @@ static struct bus_type mmc_bus_type = {
|
||||
.remove = mmc_bus_remove,
|
||||
.suspend = mmc_bus_suspend,
|
||||
.resume = mmc_bus_resume,
|
||||
.pm = MMC_PM_OPS_PTR,
|
||||
};
|
||||
|
||||
int mmc_register_bus(void)
|
||||
@@ -189,7 +225,7 @@ EXPORT_SYMBOL(mmc_unregister_driver);
|
||||
|
||||
static void mmc_release_card(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
sdio_free_common_cis(card);
|
||||
|
||||
@@ -254,14 +290,16 @@ int mmc_add_card(struct mmc_card *card)
|
||||
}
|
||||
|
||||
if (mmc_host_is_spi(card->host)) {
|
||||
printk(KERN_INFO "%s: new %s%s card on SPI\n",
|
||||
printk(KERN_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 " : "",
|
||||
type);
|
||||
} else {
|
||||
printk(KERN_INFO "%s: new %s%s card at address %04x\n",
|
||||
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_highspeed(card) ? "high speed " : "",
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
type, card->rca);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#define MMC_DEV_ATTR(name, fmt, args...) \
|
||||
static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct mmc_card *card = container_of(dev, struct mmc_card, dev); \
|
||||
struct mmc_card *card = mmc_dev_to_card(dev); \
|
||||
return sprintf(buf, fmt, args); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
|
||||
|
||||
+116
-63
@@ -58,6 +58,7 @@ int mmc_assume_removable;
|
||||
#else
|
||||
int mmc_assume_removable = 1;
|
||||
#endif
|
||||
EXPORT_SYMBOL(mmc_assume_removable);
|
||||
module_param_named(removable, mmc_assume_removable, bool, 0644);
|
||||
MODULE_PARM_DESC(
|
||||
removable,
|
||||
@@ -649,13 +650,23 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
|
||||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change data bus width and DDR mode of a host.
|
||||
*/
|
||||
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
|
||||
unsigned int ddr)
|
||||
{
|
||||
host->ios.bus_width = width;
|
||||
host->ios.ddr = ddr;
|
||||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change data bus width of a host.
|
||||
*/
|
||||
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
|
||||
{
|
||||
host->ios.bus_width = width;
|
||||
mmc_set_ios(host);
|
||||
mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -771,8 +782,9 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
|
||||
|
||||
/**
|
||||
* mmc_regulator_set_ocr - set regulator to match host->ios voltage
|
||||
* @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
|
||||
* @mmc: the host to regulate
|
||||
* @supply: regulator to use
|
||||
* @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
@@ -780,15 +792,12 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
|
||||
* a particular supply voltage. This would normally be called from the
|
||||
* set_ios() method.
|
||||
*/
|
||||
int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit)
|
||||
int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit)
|
||||
{
|
||||
int result = 0;
|
||||
int min_uV, max_uV;
|
||||
int enabled;
|
||||
|
||||
enabled = regulator_is_enabled(supply);
|
||||
if (enabled < 0)
|
||||
return enabled;
|
||||
|
||||
if (vdd_bit) {
|
||||
int tmp;
|
||||
@@ -819,17 +828,25 @@ int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit)
|
||||
else
|
||||
result = 0;
|
||||
|
||||
if (result == 0 && !enabled)
|
||||
if (result == 0 && !mmc->regulator_enabled) {
|
||||
result = regulator_enable(supply);
|
||||
} else if (enabled) {
|
||||
if (!result)
|
||||
mmc->regulator_enabled = true;
|
||||
}
|
||||
} else if (mmc->regulator_enabled) {
|
||||
result = regulator_disable(supply);
|
||||
if (result == 0)
|
||||
mmc->regulator_enabled = false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
dev_err(mmc_dev(mmc),
|
||||
"could not set regulator OCR (%d)\n", result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_regulator_set_ocr);
|
||||
|
||||
#endif
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
/*
|
||||
* Mask off any voltages we don't support and select
|
||||
@@ -907,12 +924,7 @@ static void mmc_power_up(struct mmc_host *host)
|
||||
*/
|
||||
mmc_delay(10);
|
||||
|
||||
if (host->f_min > 400000) {
|
||||
pr_warning("%s: Minimum clock frequency too high for "
|
||||
"identification mode\n", mmc_hostname(host));
|
||||
host->ios.clock = host->f_min;
|
||||
} else
|
||||
host->ios.clock = 400000;
|
||||
host->ios.clock = host->f_init;
|
||||
|
||||
host->ios.power_mode = MMC_POWER_ON;
|
||||
mmc_set_ios(host);
|
||||
@@ -1397,6 +1409,21 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_erase_group_aligned);
|
||||
|
||||
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
|
||||
if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card))
|
||||
return 0;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
cmd.arg = blocklen;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
return mmc_wait_for_cmd(card->host, &cmd, 5);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_set_blocklen);
|
||||
|
||||
void mmc_rescan(struct work_struct *work)
|
||||
{
|
||||
struct mmc_host *host =
|
||||
@@ -1404,6 +1431,8 @@ void mmc_rescan(struct work_struct *work)
|
||||
u32 ocr;
|
||||
int err;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
@@ -1443,55 +1472,71 @@ void mmc_rescan(struct work_struct *work)
|
||||
if (host->ops->get_cd && host->ops->get_cd(host) == 0)
|
||||
goto out;
|
||||
|
||||
mmc_claim_host(host);
|
||||
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
|
||||
mmc_claim_host(host);
|
||||
|
||||
mmc_power_up(host);
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
if (freqs[i] >= host->f_min)
|
||||
host->f_init = freqs[i];
|
||||
else if (!i || freqs[i-1] > host->f_min)
|
||||
host->f_init = host->f_min;
|
||||
else {
|
||||
mmc_release_host(host);
|
||||
goto out;
|
||||
}
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
pr_info("%s: %s: trying to init card at %u Hz\n",
|
||||
mmc_hostname(host), __func__, host->f_init);
|
||||
#endif
|
||||
mmc_power_up(host);
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
|
||||
/*
|
||||
* First we search for SDIO...
|
||||
*/
|
||||
err = mmc_send_io_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sdio(host, ocr)) {
|
||||
mmc_claim_host(host);
|
||||
/* try SDMEM (but not MMC) even if SDIO is broken */
|
||||
if (mmc_send_app_op_cond(host, 0, &ocr))
|
||||
goto out_fail;
|
||||
/*
|
||||
* First we search for SDIO...
|
||||
*/
|
||||
err = mmc_send_io_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sdio(host, ocr)) {
|
||||
mmc_claim_host(host);
|
||||
/*
|
||||
* Try SDMEM (but not MMC) even if SDIO
|
||||
* is broken.
|
||||
*/
|
||||
if (mmc_send_app_op_cond(host, 0, &ocr))
|
||||
goto out_fail;
|
||||
|
||||
if (mmc_attach_sd(host, ocr))
|
||||
mmc_power_off(host);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ...then normal SD...
|
||||
*/
|
||||
err = mmc_send_app_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sd(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ...then normal SD...
|
||||
*/
|
||||
err = mmc_send_app_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sd(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ...and finally MMC.
|
||||
*/
|
||||
err = mmc_send_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_mmc(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* ...and finally MMC.
|
||||
*/
|
||||
err = mmc_send_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_mmc(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_fail:
|
||||
mmc_release_host(host);
|
||||
mmc_power_off(host);
|
||||
|
||||
mmc_release_host(host);
|
||||
mmc_power_off(host);
|
||||
}
|
||||
out:
|
||||
if (host->caps & MMC_CAP_NEEDS_POLL)
|
||||
mmc_schedule_delayed_work(&host->detect, HZ);
|
||||
@@ -1538,37 +1583,45 @@ void mmc_stop_host(struct mmc_host *host)
|
||||
mmc_power_off(host);
|
||||
}
|
||||
|
||||
void mmc_power_save_host(struct mmc_host *host)
|
||||
int mmc_power_save_host(struct mmc_host *host)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
|
||||
mmc_bus_put(host);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (host->bus_ops->power_save)
|
||||
host->bus_ops->power_save(host);
|
||||
ret = host->bus_ops->power_save(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
|
||||
mmc_power_off(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_power_save_host);
|
||||
|
||||
void mmc_power_restore_host(struct mmc_host *host)
|
||||
int mmc_power_restore_host(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
|
||||
mmc_bus_put(host);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mmc_power_up(host);
|
||||
host->bus_ops->power_restore(host);
|
||||
ret = host->bus_ops->power_restore(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_power_restore_host);
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ struct mmc_bus_ops {
|
||||
void (*detect)(struct mmc_host *);
|
||||
int (*suspend)(struct mmc_host *);
|
||||
int (*resume)(struct mmc_host *);
|
||||
void (*power_save)(struct mmc_host *);
|
||||
void (*power_restore)(struct mmc_host *);
|
||||
int (*power_save)(struct mmc_host *);
|
||||
int (*power_restore)(struct mmc_host *);
|
||||
};
|
||||
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||
@@ -35,6 +35,8 @@ void mmc_set_chip_select(struct mmc_host *host, int mode);
|
||||
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
|
||||
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
|
||||
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
|
||||
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
|
||||
unsigned int ddr);
|
||||
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
|
||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||
|
||||
@@ -58,7 +60,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
|
||||
|
||||
/* Module parameters */
|
||||
extern int use_spi_crc;
|
||||
extern int mmc_assume_removable;
|
||||
|
||||
/* Debugfs information for hosts and cards */
|
||||
void mmc_add_host_debugfs(struct mmc_host *host);
|
||||
|
||||
@@ -134,6 +134,33 @@ static const struct file_operations mmc_ios_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int mmc_clock_opt_get(void *data, u64 *val)
|
||||
{
|
||||
struct mmc_host *host = data;
|
||||
|
||||
*val = host->ios.clock;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_clock_opt_set(void *data, u64 val)
|
||||
{
|
||||
struct mmc_host *host = data;
|
||||
|
||||
/* We need this check due to input value is u64 */
|
||||
if (val > host->f_max)
|
||||
return -EINVAL;
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_set_clock(host, (unsigned int) val);
|
||||
mmc_release_host(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
|
||||
"%llu\n");
|
||||
|
||||
void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
{
|
||||
struct dentry *root;
|
||||
@@ -150,11 +177,15 @@ void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
host->debugfs_root = root;
|
||||
|
||||
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
|
||||
goto err_ios;
|
||||
goto err_node;
|
||||
|
||||
if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
|
||||
&mmc_clock_fops))
|
||||
goto err_node;
|
||||
|
||||
return;
|
||||
|
||||
err_ios:
|
||||
err_node:
|
||||
debugfs_remove_recursive(root);
|
||||
host->debugfs_root = NULL;
|
||||
err_root:
|
||||
|
||||
@@ -94,8 +94,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||
* By default, hosts do not support SGIO or large requests.
|
||||
* They have to set these according to their abilities.
|
||||
*/
|
||||
host->max_hw_segs = 1;
|
||||
host->max_phys_segs = 1;
|
||||
host->max_segs = 1;
|
||||
host->max_seg_size = PAGE_CACHE_SIZE;
|
||||
|
||||
host->max_req_size = PAGE_CACHE_SIZE;
|
||||
|
||||
+48
-10
@@ -258,6 +258,21 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
||||
}
|
||||
|
||||
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
|
||||
case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
|
||||
EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_52;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_DDR_1_2V | EXT_CSD_CARD_TYPE_52 |
|
||||
EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_2V;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_52 |
|
||||
EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_8V;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
break;
|
||||
@@ -360,7 +375,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
struct mmc_card *oldcard)
|
||||
{
|
||||
struct mmc_card *card;
|
||||
int err;
|
||||
int err, ddr = MMC_SDR_MODE;
|
||||
u32 cid[4];
|
||||
unsigned int max_dtr;
|
||||
|
||||
@@ -503,17 +518,35 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
mmc_set_clock(host, max_dtr);
|
||||
|
||||
/*
|
||||
* Activate wide bus (if supported).
|
||||
* Indicate DDR mode (if supported).
|
||||
*/
|
||||
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)))
|
||||
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)))
|
||||
ddr = MMC_1_2V_DDR_MODE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate wide bus and DDR (if supported).
|
||||
*/
|
||||
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
|
||||
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
|
||||
unsigned ext_csd_bit, bus_width;
|
||||
|
||||
if (host->caps & MMC_CAP_8_BIT_DATA) {
|
||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
|
||||
if (ddr)
|
||||
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
|
||||
else
|
||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
|
||||
bus_width = MMC_BUS_WIDTH_8;
|
||||
} else {
|
||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
|
||||
if (ddr)
|
||||
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4;
|
||||
else
|
||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
|
||||
bus_width = MMC_BUS_WIDTH_4;
|
||||
}
|
||||
|
||||
@@ -524,12 +557,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
goto free_card;
|
||||
|
||||
if (err) {
|
||||
printk(KERN_WARNING "%s: switch to bus width %d "
|
||||
printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
|
||||
"failed\n", mmc_hostname(card->host),
|
||||
1 << bus_width);
|
||||
1 << bus_width, ddr);
|
||||
err = 0;
|
||||
} else {
|
||||
mmc_set_bus_width(card->host, bus_width);
|
||||
mmc_card_set_ddr_mode(card);
|
||||
mmc_set_bus_width_ddr(card->host, bus_width, ddr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,12 +657,16 @@ static int mmc_resume(struct mmc_host *host)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mmc_power_restore(struct mmc_host *host)
|
||||
static int mmc_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_claim_host(host);
|
||||
mmc_init_card(host, host->ocr, host->card);
|
||||
ret = mmc_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_sleep(struct mmc_host *host)
|
||||
@@ -685,7 +723,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
|
||||
{
|
||||
const struct mmc_bus_ops *bus_ops;
|
||||
|
||||
if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
|
||||
if (!mmc_card_is_removable(host))
|
||||
bus_ops = &mmc_ops_unsafe;
|
||||
else
|
||||
bus_ops = &mmc_ops;
|
||||
|
||||
@@ -722,12 +722,16 @@ static int mmc_sd_resume(struct mmc_host *host)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mmc_sd_power_restore(struct mmc_host *host)
|
||||
static int mmc_sd_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_claim_host(host);
|
||||
mmc_sd_init_card(host, host->ocr, host->card);
|
||||
ret = mmc_sd_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_sd_ops = {
|
||||
@@ -750,7 +754,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
|
||||
{
|
||||
const struct mmc_bus_ops *bus_ops;
|
||||
|
||||
if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
|
||||
if (!mmc_card_is_removable(host))
|
||||
bus_ops = &mmc_sd_ops_unsafe;
|
||||
else
|
||||
bus_ops = &mmc_sd_ops;
|
||||
|
||||
+45
-9
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
@@ -456,7 +457,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
||||
return -ENOENT;
|
||||
|
||||
card = oldcard;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO) {
|
||||
@@ -546,6 +546,11 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
/* Make sure card is powered before detecting it */
|
||||
err = pm_runtime_get_sync(&host->card->dev);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
/*
|
||||
@@ -555,6 +560,7 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
||||
|
||||
mmc_release_host(host);
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
mmc_sdio_remove(host);
|
||||
|
||||
@@ -562,6 +568,9 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
||||
mmc_detach_bus(host);
|
||||
mmc_release_host(host);
|
||||
}
|
||||
|
||||
/* Tell PM core that we're done */
|
||||
pm_runtime_put(&host->card->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -614,14 +623,6 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||
mmc_claim_host(host);
|
||||
err = mmc_sdio_init_card(host, host->ocr, host->card,
|
||||
(host->pm_flags & MMC_PM_KEEP_POWER));
|
||||
if (!err) {
|
||||
/* We may have switched to 1-bit mode during suspend. */
|
||||
err = sdio_enable_4bit_bus(host->card);
|
||||
if (err > 0) {
|
||||
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
if (!err && host->sdio_irqs)
|
||||
mmc_signal_sdio_irq(host);
|
||||
mmc_release_host(host);
|
||||
@@ -647,11 +648,29 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_sdio_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_sdio_init_card(host, host->ocr, host->card,
|
||||
(host->pm_flags & MMC_PM_KEEP_POWER));
|
||||
if (!ret && host->sdio_irqs)
|
||||
mmc_signal_sdio_irq(host);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||
.remove = mmc_sdio_remove,
|
||||
.detect = mmc_sdio_detect,
|
||||
.suspend = mmc_sdio_suspend,
|
||||
.resume = mmc_sdio_resume,
|
||||
.power_restore = mmc_sdio_power_restore,
|
||||
};
|
||||
|
||||
|
||||
@@ -698,6 +717,18 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
||||
goto err;
|
||||
card = host->card;
|
||||
|
||||
/*
|
||||
* Let runtime PM core know our card is active
|
||||
*/
|
||||
err = pm_runtime_set_active(&card->dev);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Enable runtime PM for this card
|
||||
*/
|
||||
pm_runtime_enable(&card->dev);
|
||||
|
||||
/*
|
||||
* The number of functions on the card is encoded inside
|
||||
* the ocr.
|
||||
@@ -712,6 +743,11 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
||||
err = sdio_init_func(host->card, i + 1);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Enable Runtime PM for this func
|
||||
*/
|
||||
pm_runtime_enable(&card->sdio_func[i]->dev);
|
||||
}
|
||||
|
||||
mmc_release_host(host);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
@@ -125,21 +126,46 @@ static int sdio_bus_probe(struct device *dev)
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
/* Unbound SDIO functions are always suspended.
|
||||
* During probe, the function is set active and the usage count
|
||||
* is incremented. If the driver supports runtime PM,
|
||||
* it should call pm_runtime_put_noidle() in its probe routine and
|
||||
* pm_runtime_get_noresume() in its remove routine.
|
||||
*/
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* Set the default block size so the driver is sure it's something
|
||||
* sensible. */
|
||||
sdio_claim_host(func);
|
||||
ret = sdio_set_block_size(func, 0);
|
||||
sdio_release_host(func);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto disable_runtimepm;
|
||||
|
||||
return drv->probe(func, id);
|
||||
ret = drv->probe(func, id);
|
||||
if (ret)
|
||||
goto disable_runtimepm;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_runtimepm:
|
||||
pm_runtime_put_noidle(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdio_bus_remove(struct device *dev)
|
||||
{
|
||||
struct sdio_driver *drv = to_sdio_driver(dev->driver);
|
||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||
int ret;
|
||||
|
||||
/* Make sure card is powered before invoking ->remove() */
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
drv->remove(func);
|
||||
|
||||
@@ -151,9 +177,63 @@ static int sdio_bus_remove(struct device *dev)
|
||||
sdio_release_host(func);
|
||||
}
|
||||
|
||||
/* First, undo the increment made directly above */
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
/* Then undo the runtime PM settings in sdio_bus_probe() */
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int sdio_bus_pm_prepare(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* Resume an SDIO device which was suspended at run time at this
|
||||
* point, in order to allow standard SDIO suspend/resume paths
|
||||
* to keep working as usual.
|
||||
*
|
||||
* Ultimately, the SDIO driver itself will decide (in its
|
||||
* suspend handler, or lack thereof) whether the card should be
|
||||
* removed or kept, and if kept, at what power state.
|
||||
*
|
||||
* At this point, PM core have increased our use count, so it's
|
||||
* safe to directly resume the device. After system is resumed
|
||||
* again, PM core will drop back its runtime PM use count, and if
|
||||
* needed device will be suspended again.
|
||||
*
|
||||
* The end result is guaranteed to be a power state that is
|
||||
* coherent with the device's runtime PM use count.
|
||||
*
|
||||
* The return value of pm_runtime_resume is deliberately unchecked
|
||||
* since there is little point in failing system suspend if a
|
||||
* device can't be resumed.
|
||||
*/
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sdio_bus_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(
|
||||
pm_generic_runtime_suspend,
|
||||
pm_generic_runtime_resume,
|
||||
pm_generic_runtime_idle
|
||||
)
|
||||
.prepare = sdio_bus_pm_prepare,
|
||||
};
|
||||
|
||||
#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops)
|
||||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
#define SDIO_PM_OPS_PTR NULL
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static struct bus_type sdio_bus_type = {
|
||||
.name = "sdio",
|
||||
.dev_attrs = sdio_dev_attrs,
|
||||
@@ -161,6 +241,7 @@ static struct bus_type sdio_bus_type = {
|
||||
.uevent = sdio_bus_uevent,
|
||||
.probe = sdio_bus_probe,
|
||||
.remove = sdio_bus_remove,
|
||||
.pm = SDIO_PM_OPS_PTR,
|
||||
};
|
||||
|
||||
int sdio_register_bus(void)
|
||||
|
||||
@@ -130,6 +130,16 @@ config MMC_SDHCI_CNS3XXX
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_ESDHC_IMX
|
||||
bool "SDHCI platform support for the Freescale eSDHC i.MX controller"
|
||||
depends on MMC_SDHCI_PLTFM && (ARCH_MX25 || ARCH_MX35 || ARCH_MX5)
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the Freescale eSDHC controller support on the platform
|
||||
bus, found on platforms like mx35/51.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_S3C
|
||||
tristate "SDHCI support on Samsung S3C SoC"
|
||||
depends on MMC_SDHCI && PLAT_SAMSUNG
|
||||
@@ -145,6 +155,18 @@ config MMC_SDHCI_S3C
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_PXA
|
||||
tristate "Marvell PXA168/PXA910/MMP2 SD Host Controller support"
|
||||
depends on ARCH_PXA || ARCH_MMP
|
||||
select MMC_SDHCI
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the Marvell(R) PXA168/PXA910/MMP2 SD Host Controller.
|
||||
If you have a PXA168/PXA910/MMP2 platform with SD Host Controller
|
||||
and a card slot, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_SPEAR
|
||||
tristate "SDHCI support on ST SPEAr platform"
|
||||
depends on MMC_SDHCI && PLAT_SPEAR
|
||||
@@ -395,6 +417,7 @@ config MMC_TMIO
|
||||
config MMC_CB710
|
||||
tristate "ENE CB710 MMC/SD Interface support"
|
||||
depends on PCI
|
||||
select MISC_DEVICES
|
||||
select CB710_CORE
|
||||
help
|
||||
This option enables support for MMC/SD part of ENE CB710/720 Flash
|
||||
@@ -451,3 +474,17 @@ config MMC_JZ4740
|
||||
SoCs.
|
||||
If you have a board based on such a SoC and with a SD/MMC slot,
|
||||
say Y or M here.
|
||||
|
||||
config MMC_USHC
|
||||
tristate "USB SD Host Controller (USHC) support"
|
||||
depends on USB
|
||||
help
|
||||
This selects support for USB SD Host Controllers based on
|
||||
the Cypress Astoria chip with firmware compliant with CSR's
|
||||
USB SD Host Controller specification (CS-118793-SP).
|
||||
|
||||
CSR boards with this device include: USB<>SDIO (M1985v2),
|
||||
and Ultrasira.
|
||||
|
||||
Note: These controllers only support SDIO cards and do not
|
||||
support MMC or SD memory cards.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user