Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c451c13 authored by Adrian Hunter's avatar Adrian Hunter Committed by Ulf Hansson
Browse files

mmc: sdhci-acpi: Fix voltage switch for some Intel host controllers



Some Intel host controllers use an ACPI device-specific method to ensure
correct voltage switching. Fix voltage switch for those, by adding a call
to the DSM.

Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent f07b7952
Loading
Loading
Loading
Loading
+108 −0
Original line number Diff line number Diff line
@@ -96,6 +96,105 @@ static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
	return c->slot && (c->slot->flags & flag);
}

enum {
	INTEL_DSM_FNS		=  0,
	INTEL_DSM_V18_SWITCH	=  3,
	INTEL_DSM_V33_SWITCH	=  4,
};

struct intel_host {
	u32	dsm_fns;
};

static const guid_t intel_dsm_guid =
	GUID_INIT(0xF6C13EA5, 0x65CD, 0x461F,
		  0xAB, 0x7A, 0x29, 0xF7, 0xE8, 0xD5, 0xBD, 0x61);

static int __intel_dsm(struct intel_host *intel_host, struct device *dev,
		       unsigned int fn, u32 *result)
{
	union acpi_object *obj;
	int err = 0;

	obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL);
	if (!obj)
		return -EOPNOTSUPP;

	if (obj->type == ACPI_TYPE_INTEGER) {
		*result = obj->integer.value;
	} else if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length > 0) {
		size_t len = min_t(size_t, obj->buffer.length, 4);

		*result = 0;
		memcpy(result, obj->buffer.pointer, len);
	} else {
		dev_err(dev, "%s DSM fn %u obj->type %d obj->buffer.length %d\n",
			__func__, fn, obj->type, obj->buffer.length);
		err = -EINVAL;
	}

	ACPI_FREE(obj);

	return err;
}

static int intel_dsm(struct intel_host *intel_host, struct device *dev,
		     unsigned int fn, u32 *result)
{
	if (fn > 31 || !(intel_host->dsm_fns & (1 << fn)))
		return -EOPNOTSUPP;

	return __intel_dsm(intel_host, dev, fn, result);
}

static void intel_dsm_init(struct intel_host *intel_host, struct device *dev,
			   struct mmc_host *mmc)
{
	int err;

	err = __intel_dsm(intel_host, dev, INTEL_DSM_FNS, &intel_host->dsm_fns);
	if (err) {
		pr_debug("%s: DSM not supported, error %d\n",
			 mmc_hostname(mmc), err);
		return;
	}

	pr_debug("%s: DSM function mask %#x\n",
		 mmc_hostname(mmc), intel_host->dsm_fns);
}

static int intel_start_signal_voltage_switch(struct mmc_host *mmc,
					     struct mmc_ios *ios)
{
	struct device *dev = mmc_dev(mmc);
	struct sdhci_acpi_host *c = dev_get_drvdata(dev);
	struct intel_host *intel_host = sdhci_acpi_priv(c);
	unsigned int fn;
	u32 result = 0;
	int err;

	err = sdhci_start_signal_voltage_switch(mmc, ios);
	if (err)
		return err;

	switch (ios->signal_voltage) {
	case MMC_SIGNAL_VOLTAGE_330:
		fn = INTEL_DSM_V33_SWITCH;
		break;
	case MMC_SIGNAL_VOLTAGE_180:
		fn = INTEL_DSM_V18_SWITCH;
		break;
	default:
		return 0;
	}

	err = intel_dsm(intel_host, dev, fn, &result);
	pr_debug("%s: %s DSM fn %u error %d result %u\n",
		 mmc_hostname(mmc), __func__, fn, err, result);

	return 0;
}

static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
{
	u8 reg;
@@ -280,6 +379,7 @@ static int intel_probe_slot(struct platform_device *pdev, const char *hid,
			    const char *uid)
{
	struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
	struct intel_host *intel_host = sdhci_acpi_priv(c);
	struct sdhci_host *host = c->host;

	if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
@@ -290,6 +390,11 @@ static int intel_probe_slot(struct platform_device *pdev, const char *hid,
	if (hid && !strcmp(hid, "80865ACA"))
		host->mmc_host_ops.get_cd = bxt_get_cd;

	intel_dsm_init(intel_host, &pdev->dev, host->mmc);

	host->mmc_host_ops.start_signal_voltage_switch =
					intel_start_signal_voltage_switch;

	return 0;
}

@@ -304,6 +409,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
		   SDHCI_QUIRK2_STOP_WITH_TC |
		   SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
	.probe_slot	= intel_probe_slot,
	.priv_size	= sizeof(struct intel_host),
};

static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
@@ -315,6 +421,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
	.flags   = SDHCI_ACPI_RUNTIME_PM,
	.pm_caps = MMC_PM_KEEP_POWER,
	.probe_slot	= intel_probe_slot,
	.priv_size	= sizeof(struct intel_host),
};

static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
@@ -325,6 +432,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
		   SDHCI_QUIRK2_STOP_WITH_TC,
	.caps    = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM,
	.probe_slot	= intel_probe_slot,
	.priv_size	= sizeof(struct intel_host),
};

static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd_3v = {