Message ID | 1508409706-27026-5-git-send-email-adrian.hunter@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 19 October 2017 at 12:41, Adrian Hunter <adrian.hunter@intel.com> wrote: > 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: Adrian Hunter <adrian.hunter@intel.com> > --- > drivers/mmc/host/sdhci-acpi.c | 108 ++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 108 insertions(+) > > diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c > index 5bb5880403b2..b988997a1e80 100644 > --- a/drivers/mmc/host/sdhci-acpi.c > +++ b/drivers/mmc/host/sdhci-acpi.c > @@ -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; > + } > + This hole ACPI thing looks weird. Could perhaps the ACPI core, model this a vqmmc regulator instead? That would keep the ACPI specific part closer to ACPI and we can treat this similar to other mmc host drivers. > + ACPI_FREE(obj); > + > + return err; > +} > + [...] Kind regards Uffe -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 20/10/17 12:16, Ulf Hansson wrote: > On 19 October 2017 at 12:41, Adrian Hunter <adrian.hunter@intel.com> wrote: >> 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: Adrian Hunter <adrian.hunter@intel.com> >> --- >> drivers/mmc/host/sdhci-acpi.c | 108 ++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 108 insertions(+) >> >> diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c >> index 5bb5880403b2..b988997a1e80 100644 >> --- a/drivers/mmc/host/sdhci-acpi.c >> +++ b/drivers/mmc/host/sdhci-acpi.c >> @@ -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; >> + } >> + > > This hole ACPI thing looks weird. DSMs are normal. > Could perhaps the ACPI core, model > this a vqmmc regulator instead? The regulator framework does not support that. > > That would keep the ACPI specific part closer to ACPI and we can treat > this similar to other mmc host drivers. Device-specific methods (DSM) are device specific so they belong in the driver not ACPI core. So the code would all end up here anyway but be more complicated and more obfuscated. -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 19 October 2017 at 12:41, Adrian Hunter <adrian.hunter@intel.com> wrote: > 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: Adrian Hunter <adrian.hunter@intel.com> Thanks, applied for next! Kind regards Uffe > --- > drivers/mmc/host/sdhci-acpi.c | 108 ++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 108 insertions(+) > > diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c > index 5bb5880403b2..b988997a1e80 100644 > --- a/drivers/mmc/host/sdhci-acpi.c > +++ b/drivers/mmc/host/sdhci-acpi.c > @@ -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 int intel_probe_slot(struct platform_device *pdev, const char *hid, > 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 int intel_probe_slot(struct platform_device *pdev, const char *hid, > .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 int intel_probe_slot(struct platform_device *pdev, const char *hid, > 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 = { > -- > 1.9.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 5bb5880403b2..b988997a1e80 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -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 int intel_probe_slot(struct platform_device *pdev, const char *hid, 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 int intel_probe_slot(struct platform_device *pdev, const char *hid, .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 int intel_probe_slot(struct platform_device *pdev, const char *hid, 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 = {
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: Adrian Hunter <adrian.hunter@intel.com> --- drivers/mmc/host/sdhci-acpi.c | 108 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+)