From: heiko@sntech.de (Heiko Stuebner)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 2/7] mmc: core: Add mmc_regulator_set_vqmmc()
Date: Mon, 12 Oct 2015 14:48:25 +0200 [thread overview]
Message-ID: <1444654110-32293-3-git-send-email-heiko@sntech.de> (raw)
In-Reply-To: <1444654110-32293-1-git-send-email-heiko@sntech.de>
From: Douglas Anderson <dianders@chromium.org>
This adds logic to the MMC core to set VQMMC. This is expected to be
called by MMC drivers like dw_mmc as part of (or instead of) their
start_signal_voltage_switch() callback.
A few notes:
* When setting the signal voltage to 3.3V we do our best to make VQMMC
and VMMC match. It's been reported that this makes some old cards
happy since they were tested back in the day before UHS when VQMMC
and VMMC were provided by the same regulator. A nice side effect of
this is that we don't end up on the hairy edge of VQMMC (2.7V),
which some EEs claim is a little too close to the minimum for
comfort.
This is done in two steps. At first we try to find a VQMMC within
a 0.3V tolerance of VMMC and if this is not supported by the
supplying regulator we try to find a suitable voltage within the
whole 2.7V-3.6V area of the spec.
* The two step approach is currently necessary, as the used
regulator_set_voltage_triplet(min, target, max) uses a simple
implementation that just tries two basic steps:
regulator_set_voltage(target, max);
regulator_set_voltage(min, target);
So with only one step with 2.7-3.6V borders, if a suitable voltage
is a bit below VMMC, we would directly get the lowest 2.7V
which some boards (like Rockchips) don't like at all.
* When setting the signal voltage to 1.8V or 1.2V we aim for that
specific voltage instead of picking the lowest one in the range.
* We very purposely don't print errors in mmc_regulator_set_vqmmc().
There are cases where the MMC core will try several different
voltages and we don't want to pollute the logs.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/mmc/core/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 7 +++++
2 files changed, 85 insertions(+)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 548b5e1..da0a243 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1456,6 +1456,84 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
}
EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr);
+static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
+ int min_uV, int target_uV,
+ int max_uV)
+{
+ /*
+ * Check if supported first to avoid errors since we may try several
+ * signal levels during power up and don't want to show errors.
+ */
+ if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
+ return -EINVAL;
+
+ return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
+ max_uV);
+}
+
+/**
+ * mmc_regulator_set_vqmmc - Set VQMMC as per the ios
+ *
+ * For 3.3V signaling, we try to match VQMMC to VMMC as closely as possible.
+ * That will match the behavior of old boards where VQMMC and VMMC were supplied
+ * by the same supply. The Bus Operating conditions for 3.3V signaling in the
+ * SD card spec also define VQMMC in terms of VMMC.
+ * If this is not possible we'll try the full 2.7-3.6V of the spec.
+ *
+ * For 1.2V and 1.8V signaling we'll try to get as close as possible to the
+ * requested voltage. This is definitely a good idea for UHS where there's a
+ * separate regulator on the card that's trying to make 1.8V and it's best if
+ * we match.
+ *
+ * This function is expected to be used by a controller's
+ * start_signal_voltage_switch() function.
+ */
+int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct device *dev = mmc_dev(mmc);
+ int ret, volt, min_uV, max_uV;
+
+ /* If no vqmmc supply then we can't change the voltage */
+ if (IS_ERR(mmc->supply.vqmmc))
+ return -EINVAL;
+
+ switch (ios->signal_voltage) {
+ case MMC_SIGNAL_VOLTAGE_120:
+ return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ 1100000, 1200000, 1300000);
+ case MMC_SIGNAL_VOLTAGE_180:
+ return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ 1700000, 1800000, 1950000);
+ case MMC_SIGNAL_VOLTAGE_330:
+ ret = mmc_ocrbitnum_to_vdd(mmc->ios.vdd, &volt, &max_uV);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "%s: found vmmc voltage range of %d-%duV\n",
+ __func__, volt, max_uV);
+
+ min_uV = max(volt - 300000, 2700000);
+ max_uV = min(max_uV + 200000, 3600000);
+
+ /*
+ * Due to a limitation in the current implementation of
+ * regulator_set_voltage_triplet() which is taking the lowest
+ * voltage possible if below the target, search for a suitable
+ * voltage in two steps and try to stay close to vmmc
+ * with a 0.3V tolerance@first.
+ */
+ if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ min_uV, volt, max_uV))
+ return 0;
+
+ return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ 2700000, volt, 3600000);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
+
#endif /* CONFIG_REGULATOR */
int mmc_regulator_get_supply(struct mmc_host *mmc)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 83b81fd..a2a78eb 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -423,6 +423,7 @@ int mmc_regulator_get_ocrmask(struct regulator *supply);
int mmc_regulator_set_ocr(struct mmc_host *mmc,
struct regulator *supply,
unsigned short vdd_bit);
+int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios);
#else
static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
{
@@ -435,6 +436,12 @@ static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
{
return 0;
}
+
+static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ return -EINVAL;
+}
#endif
int mmc_regulator_get_supply(struct mmc_host *mmc);
--
2.6.1
next prev parent reply other threads:[~2015-10-12 12:48 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-10-12 12:48 [PATCH v3 0/7] mmc: dw_mmc-rockchip: allow tuning using the clk-phase api Heiko Stuebner
2015-10-12 12:48 ` [PATCH v3 1/7] mmc: core: move ocr-bit to voltage translation into separate function Heiko Stuebner
2015-10-12 13:26 ` kbuild test robot
2015-10-12 13:44 ` Heiko Stübner
2015-10-12 16:00 ` [PATCH v3.1 " Heiko Stübner
2015-10-12 12:48 ` Heiko Stuebner [this message]
2015-10-12 12:48 ` [PATCH v3 3/7] mmc: dw_mmc: Use mmc_regulator_set_vqmmc in start_signal_voltage_switch Heiko Stuebner
2015-10-12 12:48 ` [PATCH v3 4/7] mmc: dw_mmc-rockchip: dt-binding: Add tuning related things Heiko Stuebner
2015-10-12 12:48 ` [PATCH v3 5/7] mmc: dw_mmc-rockchip: MMC tuning with the clock phase framework Heiko Stuebner
2015-10-12 12:48 ` [PATCH v3 6/7] ARM: dts: rockchip: Add drive/sample clocks for rk3288 dw_mmc devices Heiko Stuebner
2015-10-12 12:48 ` [PATCH v3 7/7] ARM: dts: rockchip: add tuning related settings to veyron devices Heiko Stuebner
2015-10-16 8:25 ` [PATCH v3 0/7] mmc: dw_mmc-rockchip: allow tuning using the clk-phase api Ulf Hansson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1444654110-32293-3-git-send-email-heiko@sntech.de \
--to=heiko@sntech.de \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox