Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp1615204imm; Wed, 19 Sep 2018 23:26:49 -0700 (PDT) X-Google-Smtp-Source: ANB0VdbOHd7vT6k+4IsLhnzCR6SREJzcwMc7PdgrxgrdSnd8uYHgknHkZo8UQQbR+aj5ijV5P974 X-Received: by 2002:a62:868b:: with SMTP id x133-v6mr39866244pfd.252.1537424809216; Wed, 19 Sep 2018 23:26:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1537424809; cv=none; d=google.com; s=arc-20160816; b=XCwCv14LLi6Tt/49QkQ6CA5jVOlc75LRZaomxprjDNIG971Wb491lj66xWfClevKdn m4z+WVQUFaoDuHhoITtKsnfw3N18xls3Cn67tLmakXURPc2SkdQpVe0jbDxlwO1v3GxW A53nl/uG2Piwe4Ys79EQXnd9Wtjls9ruo6nxQgbyq1x73mKkta8dvH+FTjyui5tjFcz3 U6BImUsVlpM26BztyM9OnfKx1xT38YLVFezCXj0UqyhY5AR3XDyYjxKd2+/8gjR9ygPT vEClDOlebgw2qPNOWfH91pb14jD/yRfZixG90fjhE+wbiYzsGSeTRdLuP9eqcARODC7j dm9w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dmarc-filter:dkim-signature:dkim-signature; bh=poR4dvAj7DjNbaz0A2Er6lV96Wdu7VdPGIQN6s/Xd6g=; b=uHQMWswfj9Hv07e2I5noFISqAhmpvQusYzl3XDbNrW+jDWLC+uPyCIwIOqoSC4jiAm WKSf5TeOIGaCcHrEyO3tTsiRKFFbF+QUf9YcSBioyTZSsdpIw1WxYhFJGZXUEtC4WZHp AhvNle6P+j9OsaqTd9e3o+/e5lGB8qc/g0V2RU5gkWK62Wb9ZXyR5NwJBgb+q7L6JtFT +xoYjESIpXYWW2Da8olK6KNMUYdulFNfL8+D0aG4ig4gs5rMbKs7vDAurqLScBw3Y8X3 olEM2Evx//vo1vyqMxo25hnf6H5NAJ6lYlQpWHTUJzGBk1hWNq1QgCCA0+3OOCaImhdi ZsKg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=a5JC0c4g; dkim=pass header.i=@codeaurora.org header.s=default header.b=Gc48HiNh; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 31-v6si22883110plc.288.2018.09.19.23.26.33; Wed, 19 Sep 2018 23:26:49 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=a5JC0c4g; dkim=pass header.i=@codeaurora.org header.s=default header.b=Gc48HiNh; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732316AbeITMGj (ORCPT + 99 others); Thu, 20 Sep 2018 08:06:39 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:33164 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732264AbeITMGj (ORCPT ); Thu, 20 Sep 2018 08:06:39 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 0A75860C88; Thu, 20 Sep 2018 06:24:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1537424690; bh=NM/1H7v2BHuRt9pjk5FKXNfjlw28qlksuUSivznA05k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a5JC0c4geBwr/MyTGHE3rBmKBI8Io31hlGLDccNm0eJvkVwPuNPaENrF86AM16Cde a9UjWqRbXfZskL3Prs+9Fz4L1G46htnA4BwwUqAxnQgqMm2bb9/FdKm4sjkUcCg0CU xJg7dHY3eAEk+QqfF24SF2BE/uF3026vY3HgxswU= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from vbadigan-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: vbadigan@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id B150A60C54; Thu, 20 Sep 2018 06:24:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1537424685; bh=NM/1H7v2BHuRt9pjk5FKXNfjlw28qlksuUSivznA05k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Gc48HiNhlHA9sRQB2nKyOEHtwAGoExIFqPGtkl/aMUuo7utEKM6cUVI/xka5SjVTW WSHthkA9ayloZzaRosQTAtD8wFTlxX9Owl0MARbw3Hpw2MDPAfEedqTPof2tUE7UjK c3L5kHipam/KrwCiioOWuAmg7dYI0HtLRlGYptEM= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org B150A60C54 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=vbadigan@codeaurora.org From: Veerabhadrarao Badiganti To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org Cc: linux-mmc@vger.kernel.org, asutoshd@codeaurora.org, riteshh@codeaurora.org, stummala@codeaurora.org, sayalil@codeaurora.org, evgreen@chromium.org, dianders@google.com, Vijay Viswanath , Venkat Gopalakrishnan , Veerabhadrarao Badiganti , linux-kernel@vger.kernel.org (open list) Subject: [PATCH V2 3/3] mmc: sdhci-msm: Use internal voltage control Date: Thu, 20 Sep 2018 11:52:38 +0530 Message-Id: <1537424558-17989-4-git-send-email-vbadigan@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1537424558-17989-1-git-send-email-vbadigan@codeaurora.org> References: <1537424558-17989-1-git-send-email-vbadigan@codeaurora.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Vijay Viswanath Some sdhci-msm controllers require that voltage switching be done after the HW is ready for it. The HW informs its readiness through power irq. The voltage switching should happen only then. Use the quirk for internal voltage switching and then control the voltage switching using power irq. Signed-off-by: Asutosh Das Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath Signed-off-by: Veerabhadrarao Badiganti --- drivers/mmc/host/sdhci-msm.c | 211 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 203 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3cc8bfe..486462d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -43,7 +43,9 @@ #define CORE_PWRCTL_IO_LOW BIT(2) #define CORE_PWRCTL_IO_HIGH BIT(3) #define CORE_PWRCTL_BUS_SUCCESS BIT(0) +#define CORE_PWRCTL_BUS_FAIL BIT(1) #define CORE_PWRCTL_IO_SUCCESS BIT(2) +#define CORE_PWRCTL_IO_FAIL BIT(3) #define REQ_BUS_OFF BIT(0) #define REQ_BUS_ON BIT(1) #define REQ_IO_LOW BIT(2) @@ -258,6 +260,10 @@ struct sdhci_msm_host { bool mci_removed; const struct sdhci_msm_variant_ops *var_ops; const struct sdhci_msm_offset *offset; + bool vmmc_load; + u32 vmmc_level[2]; + bool vqmmc_load; + u32 vqmmc_level[2]; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -1211,6 +1217,74 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } +static int sdhci_msm_set_vmmc(struct sdhci_msm_host *msm_host, + struct mmc_host *mmc, int level) +{ + int load = msm_host->vmmc_level[level]; + int ret; + + if (IS_ERR(mmc->supply.vmmc)) + return 0; + + if (msm_host->vmmc_load) { + ret = regulator_set_load(mmc->supply.vmmc, load); + if (ret) + goto out; + } + + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, mmc->ios.vdd); +out: + if (ret) + pr_err("%s: vmmc set load/ocr failed: %d\n", + mmc_hostname(mmc), ret); + + return ret; +} + +static int sdhci_msm_set_vqmmc(struct sdhci_msm_host *msm_host, + struct mmc_host *mmc, int level) +{ + int load = msm_host->vqmmc_level[level]; + int ret; + struct mmc_ios ios; + + if (IS_ERR(mmc->supply.vqmmc)) + return 0; + + if (msm_host->vqmmc_load) { + ret = regulator_set_load(mmc->supply.vqmmc, load); + if (ret) + goto out; + } + + /* + * The IO voltage regulator maynot always support a voltage close to + * vdd. Set IO voltage based on capability of the regulator. + */ + if (level) { + if (msm_host->caps_0 & CORE_3_0V_SUPPORT) + ios.signal_voltage = MMC_SIGNAL_VOLTAGE_330; + else if (msm_host->caps_0 & CORE_1_8V_SUPPORT) + ios.signal_voltage = MMC_SIGNAL_VOLTAGE_180; + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + pr_debug("%s: %s: setting signal voltage: %d\n", + mmc_hostname(mmc), __func__, + ios.signal_voltage); + ret = mmc_regulator_set_vqmmc(mmc, &ios); + if (ret) + goto out; + } + ret = regulator_enable(mmc->supply.vqmmc); + } else { + ret = regulator_disable(mmc->supply.vqmmc); + } +out: + if (ret) + pr_err("%s: vqmmc failed: %d\n", mmc_hostname(mmc), ret); + + return ret; +} + static inline void sdhci_msm_init_pwr_irq_wait(struct sdhci_msm_host *msm_host) { init_waitqueue_head(&msm_host->pwr_irq_wait); @@ -1314,8 +1388,9 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = host->mmc; u32 irq_status, irq_ack = 0; - int retry = 10; + int retry = 10, ret = 0; u32 pwr_state = 0, io_level = 0; u32 config; const struct sdhci_msm_offset *msm_offset = msm_host->offset; @@ -1351,14 +1426,34 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) /* Handle BUS ON/OFF*/ if (irq_status & CORE_PWRCTL_BUS_ON) { - pwr_state = REQ_BUS_ON; - io_level = REQ_IO_HIGH; - irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + ret = sdhci_msm_set_vmmc(msm_host, mmc, 1); + if (!ret) + ret = sdhci_msm_set_vqmmc(msm_host, mmc, 1); + + if (!ret) { + pwr_state = REQ_BUS_ON; + io_level = REQ_IO_HIGH; + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + } else { + pr_err("%s: BUS_ON req failed(%d). irq_status: 0x%08x\n", + mmc_hostname(mmc), ret, irq_status); + irq_ack |= CORE_PWRCTL_BUS_FAIL; + } } if (irq_status & CORE_PWRCTL_BUS_OFF) { - pwr_state = REQ_BUS_OFF; - io_level = REQ_IO_LOW; - irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + ret = sdhci_msm_set_vmmc(msm_host, mmc, 0); + if (!ret) + ret = sdhci_msm_set_vqmmc(msm_host, mmc, 0); + + if (!ret) { + pwr_state = REQ_BUS_OFF; + io_level = REQ_IO_LOW; + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + } else { + pr_err("%s: BUS_ON req failed(%d). irq_status: 0x%08x\n", + mmc_hostname(mmc), ret, irq_status); + irq_ack |= CORE_PWRCTL_BUS_FAIL; + } } /* Handle IO LOW/HIGH */ if (irq_status & CORE_PWRCTL_IO_LOW) { @@ -1370,6 +1465,15 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) irq_ack |= CORE_PWRCTL_IO_SUCCESS; } + if (io_level && !IS_ERR(mmc->supply.vqmmc) && !pwr_state) { + ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios); + if (ret) + pr_err("%s: IO_level setting failed(%d). signal_voltage: %d, vdd: %d irq_status: 0x%08x\n", + mmc_hostname(mmc), ret, + mmc->ios.signal_voltage, mmc->ios.vdd, + irq_status); + } + /* * The driver has to acknowledge the interrupt, switch voltages and * report back if it succeded or not to this register. The voltage @@ -1605,6 +1709,91 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } +static int sdhci_msm_register_vreg(struct sdhci_msm_host *msm_host) +{ + int ret = 0; + + ret = mmc_regulator_get_supply(msm_host->mmc); + if (ret) + return ret; + if (device_property_read_u32_array(&msm_host->pdev->dev, + "qcom,vmmc-current-level-microamp", + msm_host->vmmc_level, 2) >= 0) + msm_host->vmmc_load = true; + if (device_property_read_u32_array(&msm_host->pdev->dev, + "qcom,vqmmc-current-level-microamp", + msm_host->vqmmc_level, 2) >= 0) + msm_host->vqmmc_load = true; + + sdhci_msm_set_regulator_caps(msm_host); + + return 0; + +} + +static int sdhci_msm_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + u16 ctrl; + + /* + * Signal Voltage Switching is only applicable for Host Controllers + * v3.00 and above. + */ + if (host->version < SDHCI_SPEC_300) + return 0; + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + if (!(host->flags & SDHCI_SIGNALING_330)) + return -EINVAL; + /* Set 1.8V Signal Enable in the Host Control2 register to 0 */ + ctrl &= ~SDHCI_CTRL_VDD_180; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + /* 3.3V regulator output should be stable within 5 ms */ + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (!(ctrl & SDHCI_CTRL_VDD_180)) + return 0; + + pr_warn("%s: 3.3V regulator output did not became stable\n", + mmc_hostname(mmc)); + + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_180: + if (!(host->flags & SDHCI_SIGNALING_180)) + return -EINVAL; + + /* + * Enable 1.8V Signal Enable in the Host Control2 + * register + */ + ctrl |= SDHCI_CTRL_VDD_180; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + /* 1.8V regulator output should be stable within 5 ms */ + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (ctrl & SDHCI_CTRL_VDD_180) + return 0; + + pr_warn("%s: 1.8V regulator output did not became stable\n", + mmc_hostname(mmc)); + + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_120: + if (!(host->flags & SDHCI_SIGNALING_120)) + return -EINVAL; + return 0; + default: + /* No signal voltage switch required */ + return 0; + } + +} + static const struct sdhci_msm_variant_ops mci_var_ops = { .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, @@ -1644,6 +1833,7 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) .set_uhs_signaling = sdhci_msm_set_uhs_signaling, .write_w = sdhci_msm_writew, .write_b = sdhci_msm_writeb, + .set_power = sdhci_set_power_noreg, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { @@ -1818,6 +2008,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_offset->core_vendor_spec_capabilities0); } + ret = sdhci_msm_register_vreg(msm_host); + if (ret) + goto clk_disable; + /* * Power on reset state may trigger power irq if previous status of * PWRCTL was either BUS_ON or IO_HIGH_V. So before enabling pwr irq @@ -1862,11 +2056,12 @@ static int sdhci_msm_probe(struct platform_device *pdev) MSM_MMC_AUTOSUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); + host->mmc_host_ops.start_signal_voltage_switch = + sdhci_msm_start_signal_voltage_switch; host->mmc_host_ops.execute_tuning = sdhci_msm_execute_tuning; ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; - sdhci_msm_set_regulator_caps(msm_host); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project