summaryrefslogtreecommitdiff
path: root/drivers/mmc/core/host.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core/host.c')
-rw-r--r--drivers/mmc/core/host.c365
1 files changed, 298 insertions, 67 deletions
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 1503412f826c..88c95dbfd9cf 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/mmc/core/host.c
*
@@ -5,10 +6,6 @@
* Copyright (C) 2007-2008 Pierre Ossman
* Copyright (C) 2010 Linus Walleij
*
- * 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.
- *
* MMC host class device management
*/
@@ -16,7 +13,6 @@
#include <linux/err.h>
#include <linux/idr.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/pagemap.h>
#include <linux/export.h>
#include <linux/leds.h>
@@ -27,6 +23,7 @@
#include <linux/mmc/slot-gpio.h>
#include "core.h"
+#include "crypto.h"
#include "host.h"
#include "slot-gpio.h"
#include "pwrseq.h"
@@ -36,16 +33,64 @@
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);
- ida_simple_remove(&mmc_host_ida, host->index);
+ wakeup_source_unregister(host->ws);
+ if (of_alias_get_id(host->parent->of_node, "mmc") < 0)
+ ida_free(&mmc_host_ida, host->index);
kfree(host);
}
-static struct class mmc_host_class = {
+static int mmc_host_classdev_shutdown(struct device *dev)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+
+ __mmc_stop_host(host);
+ return 0;
+}
+
+static const struct class mmc_host_class = {
.name = "mmc_host",
.dev_release = mmc_host_classdev_release,
+ .shutdown_pre = mmc_host_classdev_shutdown,
+ .pm = MMC_HOST_CLASS_DEV_PM_OPS,
};
int mmc_register_host_class(void)
@@ -58,6 +103,10 @@ void mmc_unregister_host_class(void)
class_unregister(&mmc_host_class);
}
+/**
+ * mmc_retune_enable() - enter a transfer mode that requires retuning
+ * @host: host which should retune now
+ */
void mmc_retune_enable(struct mmc_host *host)
{
host->can_retune = 1;
@@ -68,13 +117,12 @@ void mmc_retune_enable(struct mmc_host *host)
/*
* Pause re-tuning for a small set of operations. The pause begins after the
- * next command and after first doing re-tuning.
+ * next command.
*/
void mmc_retune_pause(struct mmc_host *host)
{
if (!host->retune_paused) {
host->retune_paused = 1;
- mmc_retune_needed(host);
mmc_retune_hold(host);
}
}
@@ -89,18 +137,23 @@ void mmc_retune_unpause(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_retune_unpause);
+/**
+ * mmc_retune_disable() - exit a transfer mode that requires retuning
+ * @host: host which should not retune anymore
+ *
+ * It is not meant for temporarily preventing retuning!
+ */
void mmc_retune_disable(struct mmc_host *host)
{
mmc_retune_unpause(host);
host->can_retune = 0;
- del_timer_sync(&host->retune_timer);
- host->retune_now = 0;
- host->need_retune = 0;
+ timer_delete_sync(&host->retune_timer);
+ mmc_retune_clear(host);
}
void mmc_retune_timer_stop(struct mmc_host *host)
{
- del_timer_sync(&host->retune_timer);
+ timer_delete_sync(&host->retune_timer);
}
EXPORT_SYMBOL(mmc_retune_timer_stop);
@@ -118,6 +171,7 @@ void mmc_retune_release(struct mmc_host *host)
else
WARN_ON(1);
}
+EXPORT_SYMBOL(mmc_retune_release);
int mmc_retune(struct mmc_host *host)
{
@@ -142,9 +196,6 @@ int mmc_retune(struct mmc_host *host)
goto out;
return_to_hs400 = true;
-
- if (host->ops->prepare_hs400_tuning)
- host->ops->prepare_hs400_tuning(host, &host->ios);
}
err = mmc_execute_tuning(host->card);
@@ -159,29 +210,69 @@ out:
return err;
}
-static void mmc_retune_timer(unsigned long data)
+static void mmc_retune_timer(struct timer_list *t)
{
- struct mmc_host *host = (struct mmc_host *)data;
+ struct mmc_host *host = timer_container_of(host, t, retune_timer);
mmc_retune_needed(host);
}
+static void mmc_of_parse_timing_phase(struct device *dev, const char *prop,
+ struct mmc_clk_phase *phase)
+{
+ int degrees[2] = {0};
+ int rc;
+
+ rc = device_property_read_u32_array(dev, prop, degrees, 2);
+ phase->valid = !rc;
+ if (phase->valid) {
+ phase->in_deg = degrees[0];
+ phase->out_deg = degrees[1];
+ }
+}
+
+void
+mmc_of_parse_clk_phase(struct device *dev, struct mmc_clk_phase_map *map)
+{
+ mmc_of_parse_timing_phase(dev, "clk-phase-legacy",
+ &map->phase[MMC_TIMING_LEGACY]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs",
+ &map->phase[MMC_TIMING_MMC_HS]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-sd-hs",
+ &map->phase[MMC_TIMING_SD_HS]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr12",
+ &map->phase[MMC_TIMING_UHS_SDR12]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr25",
+ &map->phase[MMC_TIMING_UHS_SDR25]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr50",
+ &map->phase[MMC_TIMING_UHS_SDR50]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr104",
+ &map->phase[MMC_TIMING_UHS_SDR104]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-ddr50",
+ &map->phase[MMC_TIMING_UHS_DDR50]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-ddr52",
+ &map->phase[MMC_TIMING_MMC_DDR52]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs200",
+ &map->phase[MMC_TIMING_MMC_HS200]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs400",
+ &map->phase[MMC_TIMING_MMC_HS400]);
+}
+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
+ * used to instantiate and configure this host instance or not, we
* parse the properties and set respective generic mmc-host flags and
* parameters.
*/
int mmc_of_parse(struct mmc_host *host)
{
struct device *dev = host->parent;
- u32 bus_width;
+ u32 bus_width, drv_type, cd_debounce_delay_ms;
int ret;
- bool cd_cap_invert, cd_gpio_invert = false;
- bool ro_cap_invert, ro_gpio_invert = false;
if (!dev || !dev_fwnode(dev))
return 0;
@@ -196,7 +287,7 @@ int mmc_of_parse(struct mmc_host *host)
switch (bus_width) {
case 8:
host->caps |= MMC_CAP_8_BIT_DATA;
- /* Hosts capable of 8-bit transfers can also do 4 bits */
+ fallthrough; /* Hosts capable of 8-bit can also do 4 bits */
case 4:
host->caps |= MMC_CAP_4_BIT_DATA;
break;
@@ -211,6 +302,8 @@ int mmc_of_parse(struct mmc_host *host)
/* f_max is obtained from the optional "max-frequency" property */
device_property_read_u32(dev, "max-frequency", &host->f_max);
+ device_property_read_u32(dev, "max-sd-hs-hz", &host->max_sd_hs_hz);
+
/*
* Configure CD and WP pins. They are both by default active low to
* match the SDHCI spec. If GPIOs are provided for CD and / or WP, the
@@ -224,40 +317,34 @@ int mmc_of_parse(struct mmc_host *host)
*/
/* Parse Card Detection */
+
if (device_property_read_bool(dev, "non-removable")) {
host->caps |= MMC_CAP_NONREMOVABLE;
} else {
- cd_cap_invert = device_property_read_bool(dev, "cd-inverted");
+ if (device_property_read_bool(dev, "cd-inverted"))
+ host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
+
+ if (device_property_read_u32(dev, "cd-debounce-delay-ms",
+ &cd_debounce_delay_ms))
+ cd_debounce_delay_ms = 200;
if (device_property_read_bool(dev, "broken-cd"))
host->caps |= MMC_CAP_NEEDS_POLL;
- ret = mmc_gpiod_request_cd(host, "cd", 0, true,
- 0, &cd_gpio_invert);
+ ret = mmc_gpiod_request_cd(host, "cd", 0, false,
+ cd_debounce_delay_ms * 1000);
if (!ret)
dev_info(host->parent, "Got CD GPIO\n");
else if (ret != -ENOENT && ret != -ENOSYS)
return ret;
-
- /*
- * There are two ways to flag that the CD line is inverted:
- * through the cd-inverted flag and by the GPIO line itself
- * being inverted from the GPIO subsystem. This is a leftover
- * from the times when the GPIO subsystem did not make it
- * possible to flag a line as inverted.
- *
- * If the capability on the host AND the GPIO line are
- * both inverted, the end result is that the CD line is
- * not inverted.
- */
- if (cd_cap_invert ^ cd_gpio_invert)
- host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
}
/* Parse Write Protection */
- ro_cap_invert = device_property_read_bool(dev, "wp-inverted");
- ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
+ if (device_property_read_bool(dev, "wp-inverted"))
+ host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
+
+ ret = mmc_gpiod_request_ro(host, "wp", 0, 0);
if (!ret)
dev_info(host->parent, "Got WP GPIO\n");
else if (ret != -ENOENT && ret != -ENOSYS)
@@ -266,10 +353,6 @@ int mmc_of_parse(struct mmc_host *host)
if (device_property_read_bool(dev, "disable-wp"))
host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
- /* See the comment on CD inversion above */
- if (ro_cap_invert ^ ro_gpio_invert)
- host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
-
if (device_property_read_bool(dev, "cap-sd-highspeed"))
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (device_property_read_bool(dev, "cap-mmc-highspeed"))
@@ -292,6 +375,8 @@ int mmc_of_parse(struct mmc_host *host)
host->caps |= MMC_CAP_SDIO_IRQ;
if (device_property_read_bool(dev, "full-pwr-cycle"))
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
+ if (device_property_read_bool(dev, "full-pwr-cycle-in-suspend"))
+ host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND;
if (device_property_read_bool(dev, "keep-power-in-suspend"))
host->pm_caps |= MMC_PM_KEEP_POWER;
if (device_property_read_bool(dev, "wakeup-source") ||
@@ -319,6 +404,18 @@ int mmc_of_parse(struct mmc_host *host)
host->caps2 |= MMC_CAP2_NO_SD;
if (device_property_read_bool(dev, "no-mmc"))
host->caps2 |= MMC_CAP2_NO_MMC;
+ if (device_property_read_bool(dev, "no-mmc-hs400"))
+ host->caps2 &= ~(MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V |
+ MMC_CAP2_HS400_ES);
+
+ /* Must be after "non-removable" check */
+ if (device_property_read_u32(dev, "fixed-emmc-driver-type", &drv_type) == 0) {
+ if (host->caps & MMC_CAP_NONREMOVABLE)
+ host->fixed_drv_type = drv_type;
+ else
+ dev_err(host->parent,
+ "can't use fixed driver type, media is removable\n");
+ }
host->dsr_req = !device_property_read_u32(dev, "dsr", &host->dsr);
if (host->dsr_req && (host->dsr & ~0xffff)) {
@@ -328,12 +425,91 @@ int mmc_of_parse(struct mmc_host *host)
host->dsr_req = 0;
}
+ device_property_read_u32(dev, "post-power-on-delay-ms",
+ &host->ios.power_delay_ms);
+
return mmc_pwrseq_alloc(host);
}
EXPORT_SYMBOL(mmc_of_parse);
/**
+ * mmc_of_parse_voltage - return mask of supported voltages
+ * @host: host whose properties should be parsed.
+ * @mask: mask of voltages available for MMC/SD/SDIO
+ *
+ * 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 mmc_host *host, u32 *mask)
+{
+ const char *prop = "voltage-ranges";
+ struct device *dev = host->parent;
+ u32 *voltage_ranges;
+ int num_ranges, i;
+ int ret;
+
+ if (!device_property_present(dev, prop)) {
+ dev_dbg(dev, "%s unspecified\n", prop);
+ return 0;
+ }
+
+ ret = device_property_count_u32(dev, prop);
+ if (ret < 0)
+ return ret;
+
+ num_ranges = ret / 2;
+ if (!num_ranges) {
+ 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(voltage_ranges[j + 0],
+ voltage_ranges[j + 1]);
+ if (!ocr_mask) {
+ 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);
+
+/**
+ * mmc_first_nonreserved_index() - get the first index that is not reserved
+ */
+static int mmc_first_nonreserved_index(void)
+{
+ int max;
+
+ max = of_alias_get_highest_id("mmc");
+ if (max < 0)
+ return 0;
+
+ return max + 1;
+}
+
+/**
* mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure
* @dev: pointer to host device model structure
@@ -342,8 +518,9 @@ EXPORT_SYMBOL(mmc_of_parse);
*/
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
- int err;
+ int index;
struct mmc_host *host;
+ int alias_id, min_idx, max_idx;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
if (!host)
@@ -352,15 +529,25 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
/* scanning will be enabled when we're ready */
host->rescan_disable = 1;
- err = ida_simple_get(&mmc_host_ida, 0, 0, GFP_KERNEL);
- if (err < 0) {
- kfree(host);
- return NULL;
+ alias_id = of_alias_get_id(dev->of_node, "mmc");
+ if (alias_id >= 0) {
+ index = alias_id;
+ } else {
+ min_idx = mmc_first_nonreserved_index();
+ max_idx = 0;
+
+ index = ida_alloc_range(&mmc_host_ida, min_idx, max_idx - 1,
+ GFP_KERNEL);
+ if (index < 0) {
+ kfree(host);
+ return NULL;
+ }
}
- host->index = err;
+ host->index = index;
dev_set_name(&host->class_dev, "mmc%d", host->index);
+ host->ws = wakeup_source_register(NULL, dev_name(&host->class_dev));
host->parent = dev;
host->class_dev.parent = dev;
@@ -370,16 +557,16 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
if (mmc_gpio_alloc(host)) {
put_device(&host->class_dev);
- ida_simple_remove(&mmc_host_ida, host->index);
- kfree(host);
return NULL;
}
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
- INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
- setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
+ INIT_WORK(&host->sdio_irq_work, sdio_irq_work);
+ timer_setup(&host->retune_timer, mmc_retune_timer, 0);
+
+ INIT_WORK(&host->supply.uv_work, mmc_undervoltage_workfn);
/*
* By default, hosts do not support SGIO or large requests.
@@ -392,11 +579,60 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->max_blk_size = 512;
host->max_blk_count = PAGE_SIZE / 512;
+ host->fixed_drv_type = -EINVAL;
+ host->ios.power_delay_ms = 10;
+ host->ios.power_mode = MMC_POWER_UNDEFINED;
+
return host;
}
EXPORT_SYMBOL(mmc_alloc_host);
+static void devm_mmc_host_release(struct device *dev, void *res)
+{
+ mmc_free_host(*(struct mmc_host **)res);
+}
+
+struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra)
+{
+ struct mmc_host **dr, *host;
+
+ dr = devres_alloc(devm_mmc_host_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return NULL;
+
+ host = mmc_alloc_host(extra, dev);
+ if (!host) {
+ devres_free(dr);
+ return NULL;
+ }
+
+ *dr = host;
+ devres_add(dev, dr);
+
+ return host;
+}
+EXPORT_SYMBOL(devm_mmc_alloc_host);
+
+static int mmc_validate_host_caps(struct mmc_host *host)
+{
+ struct device *dev = host->parent;
+ u32 caps = host->caps, caps2 = host->caps2;
+
+ if (caps & MMC_CAP_SDIO_IRQ && !host->ops->enable_sdio_irq) {
+ dev_warn(dev, "missing ->enable_sdio_irq() ops\n");
+ return -EINVAL;
+ }
+
+ if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) &&
+ !(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) {
+ dev_warn(dev, "drop HS400 support since no 8-bit bus\n");
+ host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400;
+ }
+
+ return 0;
+}
+
/**
* mmc_add_host - initialise host hardware
* @host: mmc host
@@ -409,8 +645,9 @@ int mmc_add_host(struct mmc_host *host)
{
int err;
- WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
- !host->ops->enable_sdio_irq);
+ err = mmc_validate_host_caps(host);
+ if (err)
+ return err;
err = device_add(&host->class_dev);
if (err)
@@ -418,13 +655,9 @@ int mmc_add_host(struct mmc_host *host)
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
-#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
-#endif
mmc_start_host(host);
- mmc_register_pm_notifier(host);
-
return 0;
}
@@ -440,12 +673,9 @@ 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
mmc_remove_host_debugfs(host);
-#endif
device_del(&host->class_dev);
@@ -462,6 +692,7 @@ EXPORT_SYMBOL(mmc_remove_host);
*/
void mmc_free_host(struct mmc_host *host)
{
+ cancel_delayed_work_sync(&host->detect);
mmc_pwrseq_free(host);
put_device(&host->class_dev);
}