diff options
Diffstat (limited to 'drivers/mmc/core/bus.c')
| -rw-r--r-- | drivers/mmc/core/bus.c | 171 |
1 files changed, 136 insertions, 35 deletions
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index bdb165f9304..d2dbf02022b 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -11,8 +11,12 @@ * MMC card bus driver model */ +#include <linux/export.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/pm_runtime.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> @@ -21,13 +25,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, +static ssize_t 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: @@ -36,15 +39,19 @@ static ssize_t mmc_type_show(struct device *dev, return sprintf(buf, "SD\n"); case MMC_TYPE_SDIO: return sprintf(buf, "SDIO\n"); + case MMC_TYPE_SD_COMBO: + return sprintf(buf, "SDcombo\n"); default: return -EFAULT; } } +static DEVICE_ATTR_RO(type); -static struct device_attribute mmc_dev_attrs[] = { - __ATTR(type, S_IRUGO, mmc_type_show, NULL), - __ATTR_NULL, +static struct attribute *mmc_dev_attrs[] = { + &dev_attr_type.attr, + NULL, }; +ATTRIBUTE_GROUPS(mmc_dev); /* * This currently matches any MMC driver to any MMC card - drivers @@ -59,7 +66,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; @@ -73,6 +80,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) case MMC_TYPE_SDIO: type = "SDIO"; break; + case MMC_TYPE_SD_COMBO: + type = "SDcombo"; + break; default: type = NULL; } @@ -99,7 +109,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); } @@ -107,44 +117,108 @@ 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); return 0; } -static int mmc_bus_suspend(struct device *dev, pm_message_t state) +static void mmc_bus_shutdown(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret; + + if (dev->driver && drv->shutdown) + drv->shutdown(card); + + if (host->bus_ops->shutdown) { + ret = host->bus_ops->shutdown(host); + if (ret) + pr_warn("%s: error %d during shutdown\n", + mmc_hostname(host), ret); + } +} + +#ifdef CONFIG_PM_SLEEP +static int mmc_bus_suspend(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - int ret = 0; + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret; + + if (dev->driver && drv->suspend) { + ret = drv->suspend(card); + if (ret) + return ret; + } - if (dev->driver && drv->suspend) - ret = drv->suspend(card, state); + ret = host->bus_ops->suspend(host); return ret; } 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); - int ret = 0; + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret; + + ret = host->bus_ops->resume(host); + if (ret) + pr_warn("%s: error %d during resume (card was removed?)\n", + mmc_hostname(host), ret); if (dev->driver && drv->resume) ret = drv->resume(card); + return ret; } +#endif + +#ifdef CONFIG_PM_RUNTIME + +static int mmc_runtime_suspend(struct device *dev) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + + return host->bus_ops->runtime_suspend(host); +} + +static int mmc_runtime_resume(struct device *dev) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + + return host->bus_ops->runtime_resume(host); +} + +static int mmc_runtime_idle(struct device *dev) +{ + return 0; +} + +#endif /* !CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops mmc_bus_pm_ops = { + SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, + mmc_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume) +}; static struct bus_type mmc_bus_type = { .name = "mmc", - .dev_attrs = mmc_dev_attrs, + .dev_groups = mmc_dev_groups, .match = mmc_bus_match, .uevent = mmc_bus_uevent, .probe = mmc_bus_probe, .remove = mmc_bus_remove, - .suspend = mmc_bus_suspend, - .resume = mmc_bus_resume, + .shutdown = mmc_bus_shutdown, + .pm = &mmc_bus_pm_ops, }; int mmc_register_bus(void) @@ -183,12 +257,11 @@ 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); - if (card->info) - kfree(card->info); + kfree(card->info); kfree(card); } @@ -223,6 +296,15 @@ int mmc_add_card(struct mmc_card *card) { int ret; const char *type; + const char *uhs_bus_speed_mode = ""; + static const char *const uhs_speeds[] = { + [UHS_SDR12_BUS_SPEED] = "SDR12 ", + [UHS_SDR25_BUS_SPEED] = "SDR25 ", + [UHS_SDR50_BUS_SPEED] = "SDR50 ", + [UHS_SDR104_BUS_SPEED] = "SDR104 ", + [UHS_DDR50_BUS_SPEED] = "DDR50 ", + }; + dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); @@ -232,36 +314,55 @@ int mmc_add_card(struct mmc_card *card) break; case MMC_TYPE_SD: type = "SD"; - if (mmc_card_blockaddr(card)) - type = "SDHC"; + if (mmc_card_blockaddr(card)) { + if (mmc_card_ext_capacity(card)) + type = "SDXC"; + else + type = "SDHC"; + } break; case MMC_TYPE_SDIO: type = "SDIO"; break; + case MMC_TYPE_SD_COMBO: + type = "SD-combo"; + if (mmc_card_blockaddr(card)) + type = "SDHC-combo"; + break; default: type = "?"; break; } + if (mmc_card_uhs(card) && + (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds))) + uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed]; + if (mmc_host_is_spi(card->host)) { - printk(KERN_INFO "%s: new %s%s card on SPI\n", + pr_info("%s: new %s%s%s card on SPI\n", mmc_hostname(card->host), - mmc_card_highspeed(card) ? "high speed " : "", + mmc_card_hs(card) ? "high speed " : "", + mmc_card_ddr52(card) ? "DDR " : "", type); } else { - printk(KERN_INFO "%s: new %s%s card at address %04x\n", + pr_info("%s: new %s%s%s%s%s card at address %04x\n", mmc_hostname(card->host), - mmc_card_highspeed(card) ? "high speed " : "", - type, card->rca); + mmc_card_uhs(card) ? "ultra high speed " : + (mmc_card_hs(card) ? "high speed " : ""), + mmc_card_hs400(card) ? "HS400 " : + (mmc_card_hs200(card) ? "HS200 " : ""), + mmc_card_ddr52(card) ? "DDR " : "", + uhs_bus_speed_mode, type, card->rca); } - ret = device_add(&card->dev); - if (ret) - return ret; - #ifdef CONFIG_DEBUG_FS mmc_add_card_debugfs(card); #endif + mmc_init_context_info(card->host); + + ret = device_add(&card->dev); + if (ret) + return ret; mmc_card_set_present(card); @@ -280,10 +381,10 @@ void mmc_remove_card(struct mmc_card *card) if (mmc_card_present(card)) { if (mmc_host_is_spi(card->host)) { - printk(KERN_INFO "%s: SPI card removed\n", + pr_info("%s: SPI card removed\n", mmc_hostname(card->host)); } else { - printk(KERN_INFO "%s: card %04x removed\n", + pr_info("%s: card %04x removed\n", mmc_hostname(card->host), card->rca); } device_del(&card->dev); |
