From 10ebd8556b7990499c896a550e3d416b444211e6 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 10 May 2024 02:23:07 +0000
Subject: [PATCH] add led
---
kernel/drivers/mmc/core/host.c | 271 +++++++++++++++++++++++++++++++----------------------
1 files changed, 158 insertions(+), 113 deletions(-)
diff --git a/kernel/drivers/mmc/core/host.c b/kernel/drivers/mmc/core/host.c
index 45842e9..86c02ff 100644
--- a/kernel/drivers/mmc/core/host.c
+++ b/kernel/drivers/mmc/core/host.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/mmc/core/host.c
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* 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
*/
@@ -18,6 +15,7 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pagemap.h>
+#include <linux/pm_wakeup.h>
#include <linux/export.h>
#include <linux/leds.h>
#include <linux/slab.h>
@@ -37,16 +35,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_simple_remove(&mmc_host_ida, host->index);
kfree(host);
+}
+
+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 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)
@@ -66,7 +112,6 @@
mod_timer(&host->retune_timer,
jiffies + host->retune_period * HZ);
}
-EXPORT_SYMBOL(mmc_retune_enable);
/*
* Pause re-tuning for a small set of operations. The pause begins after the
@@ -99,7 +144,6 @@
host->retune_now = 0;
host->need_retune = 0;
}
-EXPORT_SYMBOL(mmc_retune_disable);
void mmc_retune_timer_stop(struct mmc_host *host)
{
@@ -181,8 +225,6 @@
struct device *dev = host->parent;
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;
@@ -197,7 +239,7 @@
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;
@@ -225,10 +267,12 @@
*/
/* 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))
@@ -237,33 +281,20 @@
if (device_property_read_bool(dev, "broken-cd"))
host->caps |= MMC_CAP_NEEDS_POLL;
- ret = mmc_gpiod_request_cd(host, "cd", 0, true,
- cd_debounce_delay_ms * 1000,
- &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)
@@ -271,10 +302,6 @@
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;
@@ -298,6 +325,8 @@
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") ||
@@ -325,6 +354,8 @@
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-prescan-powerup"))
+ host->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
/* Must be after "non-removable" check */
if (device_property_read_u32(dev, "fixed-emmc-driver-type", &drv_type) == 0) {
@@ -334,20 +365,6 @@
dev_err(host->parent,
"can't use fixed driver type, media is removable\n");
}
-
- if (device_property_read_bool(dev, "supports-sd"))
- host->restrict_caps |= RESTRICT_CARD_TYPE_SD;
- if (device_property_read_bool(dev, "supports-sdio"))
- host->restrict_caps |= RESTRICT_CARD_TYPE_SDIO;
- if (device_property_read_bool(dev, "supports-emmc"))
- host->restrict_caps |= RESTRICT_CARD_TYPE_EMMC;
-
-#ifdef CONFIG_SDIO_KEEPALIVE
- host->support_chip_alive =
- device_property_read_bool(dev, "supports-chip-alive");
- host->logic_remove_card =
- device_property_read_bool(dev, "logic-remove-card");
-#endif
host->dsr_req = !device_property_read_u32(dev, "dsr", &host->dsr);
if (host->dsr_req && (host->dsr & ~0xffff)) {
@@ -364,6 +381,50 @@
}
EXPORT_SYMBOL(mmc_of_parse);
+
+/**
+ * mmc_of_parse_voltage - return mask of supported voltages
+ * @np: The device node need to be parsed.
+ * @mask: mask of voltages available for MMC/SD/SDIO
+ *
+ * Parse the "voltage-ranges" DT 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)
+{
+ const u32 *voltage_ranges;
+ int num_ranges, i;
+
+ voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
+ if (!voltage_ranges) {
+ pr_debug("%pOF: voltage-ranges unspecified\n", np);
+ return 0;
+ }
+ num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
+ if (!num_ranges) {
+ pr_err("%pOF: voltage-ranges empty\n", np);
+ return -EINVAL;
+ }
+
+ 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]));
+ if (!ocr_mask) {
+ pr_err("%pOF: voltage-range #%d is invalid\n",
+ np, i);
+ return -EINVAL;
+ }
+ *mask |= ocr_mask;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(mmc_of_parse_voltage);
/**
* mmc_first_nonreserved_index() - get the first index that is not reserved
@@ -388,7 +449,7 @@
*/
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;
@@ -401,22 +462,22 @@
alias_id = of_alias_get_id(dev->of_node, "mmc");
if (alias_id >= 0) {
- min_idx = alias_id;
- max_idx = alias_id + 1;
+ index = alias_id;
} else {
min_idx = mmc_first_nonreserved_index();
max_idx = 0;
+
+ index = ida_simple_get(&mmc_host_ida, min_idx, max_idx, GFP_KERNEL);
+ if (index < 0) {
+ kfree(host);
+ return NULL;
+ }
}
- err = ida_simple_get(&mmc_host_ida, min_idx, max_idx, GFP_KERNEL);
- if (err < 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;
@@ -448,14 +509,48 @@
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);
-struct mmc_host *primary_sdio_host;
-EXPORT_SYMBOL(primary_sdio_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 ERR_PTR(-ENOMEM);
+
+ host = mmc_alloc_host(extra, dev);
+ if (IS_ERR(host)) {
+ devres_free(dr);
+ return host;
+ }
+
+ *dr = host;
+ devres_add(dev, dr);
+
+ return host;
+}
+EXPORT_SYMBOL(devm_mmc_alloc_host);
+
+static int mmc_validate_host_caps(struct mmc_host *host)
+{
+ if (host->caps & MMC_CAP_SDIO_IRQ && !host->ops->enable_sdio_irq) {
+ dev_warn(host->parent, "missing ->enable_sdio_irq() ops\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
/**
* mmc_add_host - initialise host hardware
@@ -469,8 +564,9 @@
{
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)
@@ -483,12 +579,6 @@
#endif
mmc_start_host(host);
- if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
- mmc_register_pm_notifier(host);
-
- if (host->restrict_caps & RESTRICT_CARD_TYPE_SDIO)
- primary_sdio_host = host;
-
return 0;
}
@@ -504,8 +594,6 @@
*/
void mmc_remove_host(struct mmc_host *host)
{
- if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
- mmc_unregister_pm_notifier(host);
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
@@ -527,51 +615,8 @@
*/
void mmc_free_host(struct mmc_host *host)
{
- mmc_crypto_free_host(host);
mmc_pwrseq_free(host);
put_device(&host->class_dev);
}
EXPORT_SYMBOL(mmc_free_host);
-
-/**
- * mmc_host_rescan - triger software rescan flow
- * @host: mmc host
- *
- * rescan slot attach in the assigned host.
- * If @host is NULL, default rescan primary_sdio_host
- * saved by mmc_add_host().
- * OR, rescan host from argument.
- *
- */
-int mmc_host_rescan(struct mmc_host *host, int val, int is_cap_sdio_irq)
-{
- if (NULL != primary_sdio_host) {
- if (!host)
- host = primary_sdio_host;
- else
- pr_info("%s: mmc_host_rescan pass in host from argument!\n",
- mmc_hostname(host));
- } else {
- pr_err("sdio: host isn't initialization successfully.\n");
- return -ENOMEDIUM;
- }
-
- pr_info("%s:mmc host rescan start!\n", mmc_hostname(host));
-
- /* 0: oob 1:cap-sdio-irq */
- if (is_cap_sdio_irq == 1) {
- host->caps |= MMC_CAP_SDIO_IRQ;
- } else if (is_cap_sdio_irq == 0) {
- host->caps &= ~MMC_CAP_SDIO_IRQ;
- } else {
- dev_err(&host->class_dev, "sdio: host doesn't identify oob or sdio_irq mode!\n");
- return -ENOMEDIUM;
- }
-
- if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->set_sdio_status)
- host->ops->set_sdio_status(host, val);
-
- return 0;
-}
-EXPORT_SYMBOL(mmc_host_rescan);
--
Gitblit v1.6.2