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 | 
