From b0b8f2dbc5b6c1a20e0a5d11bcd7bd4ae6383d30 Mon Sep 17 00:00:00 2001 From: Asutosh Das Date: Fri, 28 Jun 2019 15:05:50 +0530 Subject: [PATCH] ufs: ufs-qcom: Add support for presilicon configuration The UFS phy during pre-silicon runs in HS-G1 and doesn't support low-power modes. It provides a method to disable-lpm and restrict the gear to the desired value. Change-Id: Iad7fae4aa4ed04546a761209d74107ad62d92ac7 Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufs-qcom.c | 62 ++++++++++++++++++++++++++++++++----- drivers/scsi/ufs/ufs-qcom.h | 7 ++++- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index b4d1b5c22987..b57167895411 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -40,6 +40,8 @@ static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, u32 clk_cycles); +static void ufs_qcom_parse_gear_limits(struct ufs_qcom_host *host); +static void ufs_qcom_parse_lpm(struct ufs_qcom_host *host); static struct ufs_qcom_host *rcdev_to_ufs_host(struct reset_controller_dev *rcd) { @@ -809,12 +811,14 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, switch (status) { case PRE_CHANGE: + ufs_qcom_cap.hs_rx_gear = host->limit_rx_hs_gear; + ufs_qcom_cap.hs_tx_gear = host->limit_tx_hs_gear; + ufs_qcom_cap.pwm_tx_gear = host->limit_tx_pwm_gear; + ufs_qcom_cap.pwm_rx_gear = host->limit_rx_pwm_gear; + ufs_qcom_cap.tx_lanes = UFS_QCOM_LIMIT_NUM_LANES_TX; ufs_qcom_cap.rx_lanes = UFS_QCOM_LIMIT_NUM_LANES_RX; - ufs_qcom_cap.hs_rx_gear = UFS_QCOM_LIMIT_HSGEAR_RX; - ufs_qcom_cap.hs_tx_gear = UFS_QCOM_LIMIT_HSGEAR_TX; - ufs_qcom_cap.pwm_rx_gear = UFS_QCOM_LIMIT_PWMGEAR_RX; - ufs_qcom_cap.pwm_tx_gear = UFS_QCOM_LIMIT_PWMGEAR_TX; + ufs_qcom_cap.rx_pwr_pwm = UFS_QCOM_LIMIT_RX_PWR_PWM; ufs_qcom_cap.tx_pwr_pwm = UFS_QCOM_LIMIT_TX_PWR_PWM; ufs_qcom_cap.rx_pwr_hs = UFS_QCOM_LIMIT_RX_PWR_HS; @@ -956,15 +960,20 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP); } + + if (host->disable_lpm) + hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; } static void ufs_qcom_set_caps(struct ufs_hba *hba) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); - hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; - hba->caps |= UFSHCD_CAP_CLK_SCALING; - hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; + if (!host->disable_lpm) { + hba->caps |= UFSHCD_CAP_CLK_GATING | + UFSHCD_CAP_HIBERN8_WITH_CLK_GATING | + UFSHCD_CAP_CLK_SCALING | UFSHCD_CAP_AUTO_BKOPS_SUSPEND; + } if (host->hw_ver.major >= 0x2) { host->caps = UFS_QCOM_CAP_QUNIPRO | @@ -1167,6 +1176,11 @@ static int ufs_qcom_init(struct ufs_hba *hba) if (err) goto out_variant_clear; + ufs_qcom_parse_gear_limits(host); + ufs_qcom_parse_lpm(host); + if (host->disable_lpm) + pm_runtime_forbid(host->hba->dev); + ufs_qcom_set_caps(hba); ufs_qcom_advertise_quirks(hba); @@ -1537,6 +1551,40 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) usleep_range(1000, 1100); } +/* + * ufs_qcom_parse_gear_limits - read from DTS if gears should be limited + */ +static void ufs_qcom_parse_gear_limits(struct ufs_qcom_host *host) +{ + struct device_node *np = host->hba->dev->of_node; + + if (!np) + return; + + host->limit_tx_hs_gear = UFS_QCOM_LIMIT_HSGEAR_TX; + host->limit_rx_hs_gear = UFS_QCOM_LIMIT_HSGEAR_RX; + host->limit_tx_pwm_gear = UFS_QCOM_LIMIT_PWMGEAR_TX; + host->limit_rx_pwm_gear = UFS_QCOM_LIMIT_PWMGEAR_RX; + + of_property_read_u32(np, "limit-tx-hs-gear", &host->limit_tx_hs_gear); + of_property_read_u32(np, "limit-rx-hs-gear", &host->limit_rx_hs_gear); + of_property_read_u32(np, "limit-tx-pwm-gear", &host->limit_tx_pwm_gear); + of_property_read_u32(np, "limit-rx-pwm-gear", &host->limit_rx_pwm_gear); +} + +/* + * ufs_qcom_parse_lpm - read from DTS whether LPM modes should be disabled. + */ +static void ufs_qcom_parse_lpm(struct ufs_qcom_host *host) +{ + struct device_node *node = host->hba->dev->of_node; + + host->disable_lpm = of_property_read_bool(node, "qcom,disable-lpm"); + if (host->disable_lpm) + dev_info(host->hba->dev, "(%s) All LPM is disabled\n", + __func__); +} + /** * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations * diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index 001915d1e0e4..4a651d48b3af 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. */ #ifndef UFS_QCOM_H_ @@ -232,6 +232,11 @@ struct ufs_qcom_host { struct ufs_qcom_testbus testbus; struct reset_controller_dev rcdev; + int limit_tx_hs_gear; + int limit_rx_hs_gear; + int limit_tx_pwm_gear; + int limit_rx_pwm_gear; + bool disable_lpm; }; static inline u32