diff options
| author | Gilad Broner <gbroner@codeaurora.org> | 2014-12-21 10:48:42 +0200 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 10:58:26 -0700 |
| commit | 5cdbfe11ca75af09b97123498075b56a7fa02b8d (patch) | |
| tree | fbd8d7c3d3806f5db17665296553d38a8f06128e | |
| parent | f6786d1fec861506b3e3339cd4f6066c75cdb203 (diff) | |
scsi: ufs-qcom: set device ref. clk bit
As of HW major version 2, a new bit 'UFS_DEV_REF_CLK_EN' was added
to UFS_CFG1 register which needs to be set as part of hibernate
enter/exit sequences during suspend/resume.
Change-Id: I66a6a75dc5a1cf130b1cee90ae20f9f950edfb3a
Signed-off-by: Gilad Broner <gbroner@codeaurora.org>
[imaund@codeaurora.org: Resolved context conflicts and updated a
conditional in ufs_qcom_advertise_quirks to use the ufs_qcom_host
struct when querying major hw versions]
Signed-off-by: Ian Maund <imaund@codeaurora.org>
[subhashj@codeaurora.org: resolved trivial merge conflicts]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
[venkatg@codeaurora.org: resolved trivial merge conflicts,
drop changes to include/linux/phy/phy-qcom-ufs.h]
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
| -rw-r--r-- | drivers/phy/phy-qcom-ufs.c | 33 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 83 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 1 | ||||
| -rw-r--r-- | include/linux/scsi/ufs/ufs-qcom.h | 10 | ||||
| -rw-r--r-- | include/linux/scsi/ufs/ufshcd.h | 9 |
5 files changed, 92 insertions, 44 deletions
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index 2a053c3cd30b..1e6d1835f44d 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -144,7 +144,6 @@ int ufs_qcom_phy_base_init(struct platform_device *pdev, phy_common->mmio = NULL; dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", __func__, err); - goto out; } /* "dev_ref_clk_ctrl_mem" is optional resource */ @@ -445,38 +444,6 @@ void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy) } } -#define UFS_REF_CLK_EN (1 << 5) - -static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable) -{ - struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); - - if (phy->dev_ref_clk_ctrl_mmio && - (enable ^ phy->is_dev_ref_clk_enabled)) { - u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio); - - if (enable) - temp |= UFS_REF_CLK_EN; - else - temp &= ~UFS_REF_CLK_EN; - - writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio); - /* ensure that ref_clk is enabled/disabled before we return */ - wmb(); - phy->is_dev_ref_clk_enabled = enable; - } -} - -void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy) -{ - ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true); -} - -void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy) -{ - ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false); -} - /* Turn ON M-PHY RMMI interface clocks */ int ufs_qcom_phy_enable_iface_clk(struct phy *generic_phy) { diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 9cbbc51793f2..1d47641a02f7 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -774,6 +774,44 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) return err; } +#define UFS_REF_CLK_EN (1 << 5) +static void ufs_qcom_enable_dev_ref_clk(struct ufs_qcom_host *host, bool enable) +{ + if (host->dev_ref_clk_ctrl_mmio && + (enable ^ host->is_dev_ref_clk_enabled)) { + u32 temp = readl_relaxed(host->dev_ref_clk_ctrl_mmio); + + if (enable) + temp |= UFS_REF_CLK_EN; + else + temp &= ~UFS_REF_CLK_EN; + + /* + * If we are here to disable this clock it might be immediately + * after entering into hibern8 in which case we need to make + * sure that device ref_clk is active at least 1us after the + * hibern8 enter. + */ + if (!enable) + udelay(1); + + writel_relaxed(temp, host->dev_ref_clk_ctrl_mmio); + + /* ensure that ref_clk is enabled/disabled before we return */ + wmb(); + + /* + * If we call hibern8 exit after this, we need to make sure that + * device ref_clk is stable for at least 1us before the hibern8 + * exit command. + */ + if (enable) + udelay(1); + + host->is_dev_ref_clk_enabled = enable; + } +} + static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, bool status, struct ufs_pa_layer_attr *dev_max_params, @@ -817,6 +855,10 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, goto out; } + /* enable the device ref clock before changing to HS mode */ + if (!ufshcd_is_hs_mode(&hba->pwr_info) && + ufshcd_is_hs_mode(dev_req_params)) + ufs_qcom_enable_dev_ref_clk(host, true); break; case POST_CHANGE: if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, @@ -844,6 +886,11 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); ufs_qcom_update_bus_bw_vote(host); + + /* disable the device ref clock if entered PWM mode */ + if (ufshcd_is_hs_mode(&hba->pwr_info) && + !ufshcd_is_hs_mode(dev_req_params)) + ufs_qcom_enable_dev_ref_clk(host, false); break; default: ret = -EINVAL; @@ -864,23 +911,19 @@ out: */ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) { - u8 major; - u16 minor, step; struct ufs_qcom_host *host = hba->priv; - ufs_qcom_get_controller_revision(hba, &major, &minor, &step); - - if (major == 0x1) { + if (host->hw_ver.major == 0x1) { hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP | UFSHCD_QUIRK_BROKEN_LCC | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE); - if ((minor == 0x001) && (step == 0x0001)) + if (host->hw_ver.minor == 0x001 && host->hw_ver.step == 0x0001) hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR; } - if (major >= 0x2) { + if (host->hw_ver.major >= 0x2) { hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; if (!ufs_qcom_cap_qunipro(host)) @@ -1003,8 +1046,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) ufs_qcom_phy_disable_iface_clk(host->generic_phy); goto out; } - /* enable the device ref clock */ - ufs_qcom_phy_enable_dev_ref_clk(host->generic_phy); + /* enable the device ref clock for HS mode*/ + if (ufshcd_is_hs_mode(&hba->pwr_info)) + ufs_qcom_enable_dev_ref_clk(host, true); vote = host->bus_vote.saved_vote; if (vote == host->bus_vote.min_bw_vote) ufs_qcom_update_bus_bw_vote(host); @@ -1015,7 +1059,7 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) /* turn off UFS local PHY ref_clk */ ufs_qcom_phy_disable_ref_clk(host->generic_phy); /* disable device ref_clk */ - ufs_qcom_phy_disable_dev_ref_clk(host->generic_phy); + ufs_qcom_enable_dev_ref_clk(host, false); } vote = host->bus_vote.min_bw_vote; } @@ -1123,9 +1167,11 @@ static int ufs_qcom_init(struct ufs_hba *hba) { int err; struct device *dev = hba->dev; + struct platform_device *pdev = to_platform_device(dev); struct ufs_qcom_host *host; u8 major; u16 minor, step; + struct resource *res; if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev))) return -ENODEV; @@ -1194,6 +1240,8 @@ static int ufs_qcom_init(struct ufs_hba *hba) goto out_disable_phy; ufs_qcom_set_caps(hba); + ufs_qcom_get_controller_revision(hba, &host->hw_ver.major, + &host->hw_ver.minor, &host->hw_ver.step); ufs_qcom_advertise_quirks(hba); hba->caps |= UFSHCD_CAP_CLK_GATING | @@ -1202,6 +1250,21 @@ static int ufs_qcom_init(struct ufs_hba *hba) hba->caps |= UFSHCD_CAP_HIBERN8_ENTER_ON_IDLE; ufs_qcom_setup_clocks(hba, true); + /* "dev_ref_clk_ctrl_mem" is optional resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_dbg(dev, "%s: dev_ref_clk_ctrl_mem resource not found\n", + __func__); + } else { + host->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(host->dev_ref_clk_ctrl_mmio)) { + dev_warn(dev, + "%s: could not map dev_ref_clk_ctrl_mmio, err %ld\n", + __func__, PTR_ERR(host->dev_ref_clk_ctrl_mmio)); + host->dev_ref_clk_ctrl_mmio = NULL; + } + } + if (hba->dev->id < MAX_UFS_QCOM_HOSTS) ufs_qcom_hosts[hba->dev->id] = host; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6945e41e864d..40ebaf2e25e9 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -43,7 +43,6 @@ #include <linux/nls.h> #include <linux/of.h> #include <linux/scsi/ufs/ufshcd.h> -#include <linux/scsi/ufs/unipro.h> #include "ufshci.h" #include "ufs_quirks.h" #include "debugfs.h" diff --git a/include/linux/scsi/ufs/ufs-qcom.h b/include/linux/scsi/ufs/ufs-qcom.h index 047d38987f85..dee92b9ee54f 100644 --- a/include/linux/scsi/ufs/ufs-qcom.h +++ b/include/linux/scsi/ufs/ufs-qcom.h @@ -182,6 +182,13 @@ struct ufs_qcom_ice_data { bool crypto_engine_err; }; +/* Host controller hardware version: major.minor.step */ +struct ufs_hw_version { + u16 step; + u16 minor; + u8 major; +}; + struct ufs_qcom_host { /* * Set this capability if host controller supports the QUniPro mode @@ -203,6 +210,9 @@ struct ufs_qcom_host { bool is_lane_clks_enabled; bool sec_cfg_updated; struct ufs_qcom_ice_data ice; + void __iomem *dev_ref_clk_ctrl_mmio; + bool is_dev_ref_clk_enabled; + struct ufs_hw_version hw_ver; }; #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba) diff --git a/include/linux/scsi/ufs/ufshcd.h b/include/linux/scsi/ufs/ufshcd.h index e3a5c23f39ea..b4d7b5f60995 100644 --- a/include/linux/scsi/ufs/ufshcd.h +++ b/include/linux/scsi/ufs/ufshcd.h @@ -55,6 +55,7 @@ #include <linux/completion.h> #include <linux/regulator/consumer.h> #include <linux/pm_qos.h> +#include <linux/scsi/ufs/unipro.h> #include <asm/irq.h> #include <asm/byteorder.h> @@ -940,6 +941,14 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba, int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size); +static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info) +{ + return (pwr_info->pwr_rx == FAST_MODE || + pwr_info->pwr_rx == FASTAUTO_MODE) && + (pwr_info->pwr_tx == FAST_MODE || + pwr_info->pwr_tx == FASTAUTO_MODE); +} + #define ASCII_STD true #define UTF16_STD false int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf, |
