diff options
Diffstat (limited to 'drivers/mmc/core/host.c')
-rw-r--r-- | drivers/mmc/core/host.c | 90 |
1 files changed, 71 insertions, 19 deletions
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 9b89a91b6b47..0b0577990ddc 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -35,6 +35,42 @@ static DEFINE_IDA(mmc_host_ida); +#ifdef CONFIG_PM_SLEEP +static int mmc_host_class_prepare(struct device *dev) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + /* + * It's safe to access the bus_ops pointer, as both userspace and the + * workqueue for detecting cards are frozen at this point. + */ + if (!host->bus_ops) + return 0; + + /* Validate conditions for system suspend. */ + if (host->bus_ops->pre_suspend) + return host->bus_ops->pre_suspend(host); + + return 0; +} + +static void mmc_host_class_complete(struct device *dev) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + _mmc_detect_change(host, 0, false); +} + +static const struct dev_pm_ops mmc_host_class_dev_pm_ops = { + .prepare = mmc_host_class_prepare, + .complete = mmc_host_class_complete, +}; + +#define MMC_HOST_CLASS_DEV_PM_OPS (&mmc_host_class_dev_pm_ops) +#else +#define MMC_HOST_CLASS_DEV_PM_OPS NULL +#endif + static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); @@ -46,6 +82,7 @@ static void mmc_host_classdev_release(struct device *dev) static struct class mmc_host_class = { .name = "mmc_host", .dev_release = mmc_host_classdev_release, + .pm = MMC_HOST_CLASS_DEV_PM_OPS, }; int mmc_register_host_class(void) @@ -209,8 +246,8 @@ mmc_of_parse_clk_phase(struct mmc_host *host, struct mmc_clk_phase_map *map) EXPORT_SYMBOL(mmc_of_parse_clk_phase); /** - * mmc_of_parse() - parse host's device-tree node - * @host: host whose node should be parsed. + * mmc_of_parse() - parse host's device properties + * @host: host whose properties should be parsed. * * To keep the rest of the MMC subsystem unaware of whether DT has been * used to to instantiate and configure this host instance or not, we @@ -379,44 +416,62 @@ EXPORT_SYMBOL(mmc_of_parse); /** * mmc_of_parse_voltage - return mask of supported voltages - * @np: The device node need to be parsed. + * @host: host whose properties should be parsed. * @mask: mask of voltages available for MMC/SD/SDIO * - * Parse the "voltage-ranges" DT property, returning zero if it is not + * Parse the "voltage-ranges" property, returning zero if it is not * found, negative errno if the voltage-range specification is invalid, * or one if the voltage-range is specified and successfully parsed. */ -int mmc_of_parse_voltage(struct device_node *np, u32 *mask) +int mmc_of_parse_voltage(struct mmc_host *host, u32 *mask) { - const u32 *voltage_ranges; + const char *prop = "voltage-ranges"; + struct device *dev = host->parent; + u32 *voltage_ranges; int num_ranges, i; + int ret; - voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges); - if (!voltage_ranges) { - pr_debug("%pOF: voltage-ranges unspecified\n", np); + if (!device_property_present(dev, prop)) { + dev_dbg(dev, "%s unspecified\n", prop); return 0; } - num_ranges = num_ranges / sizeof(*voltage_ranges) / 2; + + ret = device_property_count_u32(dev, prop); + if (ret < 0) + return ret; + + num_ranges = ret / 2; if (!num_ranges) { - pr_err("%pOF: voltage-ranges empty\n", np); + dev_err(dev, "%s empty\n", prop); return -EINVAL; } + voltage_ranges = kcalloc(2 * num_ranges, sizeof(*voltage_ranges), GFP_KERNEL); + if (!voltage_ranges) + return -ENOMEM; + + ret = device_property_read_u32_array(dev, prop, voltage_ranges, 2 * num_ranges); + if (ret) { + kfree(voltage_ranges); + return ret; + } + for (i = 0; i < num_ranges; i++) { const int j = i * 2; u32 ocr_mask; - ocr_mask = mmc_vddrange_to_ocrmask( - be32_to_cpu(voltage_ranges[j]), - be32_to_cpu(voltage_ranges[j + 1])); + ocr_mask = mmc_vddrange_to_ocrmask(voltage_ranges[j + 0], + voltage_ranges[j + 1]); if (!ocr_mask) { - pr_err("%pOF: voltage-range #%d is invalid\n", - np, i); + dev_err(dev, "range #%d in %s is invalid\n", i, prop); + kfree(voltage_ranges); return -EINVAL; } *mask |= ocr_mask; } + kfree(voltage_ranges); + return 1; } EXPORT_SYMBOL(mmc_of_parse_voltage); @@ -538,8 +593,6 @@ int mmc_add_host(struct mmc_host *host) #endif mmc_start_host(host); - mmc_register_pm_notifier(host); - return 0; } @@ -555,7 +608,6 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - mmc_unregister_pm_notifier(host); mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS |