diff options
| author | Subhash Jadavani <subhashj@codeaurora.org> | 2015-01-06 17:16:13 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 10:58:20 -0700 |
| commit | a7578e830eb19df60edaa1ef7f4028835ea7c522 (patch) | |
| tree | 8b669ddcbbf4dc5e37483612b80ce7891dc48a4e | |
| parent | ce7e8d0894ce8ac2ef897df29a6dc9c8f84c87cd (diff) | |
scsi: ufs-qcom: add QUniPro hardware support
New revisions of UFS host controller supports the new UniPro
hardware controller (referred as QUniPro). This patch adds
the support to enable this new UniPro controller hardware.
Change-Id: Iccbcc38c36e6d9b9fcbb5c7fd7a3e3326c1c4ce0
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
[subhashj@codeaurora.org: resolved trivial merge conflicts]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
| -rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 78 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshci.h | 2 | ||||
| -rw-r--r-- | include/linux/scsi/ufs/ufs-qcom.h | 22 | ||||
| -rw-r--r-- | include/linux/scsi/ufs/ufshcd.h | 4 |
5 files changed, 84 insertions, 24 deletions
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index d1391c9f021a..d10636f99512 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -234,6 +234,15 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba) return err; } +static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host) +{ + ufshcd_rmwl(host->hba, QUNIPRO_SEL, + ufs_qcom_cap_qunipro(host) ? QUNIPRO_SEL : 0, + REG_UFS_CFG1); + /* make sure above configuration is applied before we return */ + mb(); +} + static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) { struct ufs_qcom_host *host = hba->priv; @@ -276,6 +285,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) dev_err(hba->dev, "%s: is_physical_coding_sublayer_ready() failed, ret = %d\n", __func__, ret); + ufs_qcom_select_unipro_mode(host); + out: return ret; } @@ -335,12 +346,13 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status) } /** - * Returns non-zero for success (which rate of core_clk) and 0 - * in case of a failure + * Returns zero for success and non-zero in case of a failure */ -static unsigned long -ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) +static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, + u32 hs, u32 rate, bool update_link_startup_timer) { + int ret = 0; + struct ufs_qcom_host *host = hba->priv; struct ufs_clk_info *clki; u32 core_clk_period_in_ns; u32 tx_clk_cycles_per_us = 0; @@ -364,6 +376,16 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) {UFS_HS_G2, 0x49}, }; + /* + * The Qunipro controller does not use following registers: + * SYS1CLK_1US_REG, TX_SYMBOL_CLK_1US_REG, CLK_NS_REG & + * UFS_REG_PA_LINK_STARTUP_TIMER + * But UTP controller uses SYS1CLK_1US_REG register for Interrupt + * Aggregation logic. + */ + if (ufs_qcom_cap_qunipro(host) && !ufshcd_is_intr_aggr_allowed(hba)) + goto out; + if (gear == 0) { dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear); goto out_error; @@ -381,6 +403,9 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC; ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US); + if (ufs_qcom_cap_qunipro(host)) + goto out; + core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate; core_clk_period_in_ns <<= OFFSET_CLK_NS_REG; core_clk_period_in_ns &= MASK_CLK_NS_REG; @@ -432,32 +457,34 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) /* this register 2 fields shall be written at once */ ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, REG_UFS_TX_SYMBOL_CLK_NS_US); + + if (update_link_startup_timer) { + ufshcd_writel(hba, ((core_clk_rate / MSEC_PER_SEC) * 100), + REG_UFS_PA_LINK_STARTUP_TIMER); + /* + * make sure that this configuration is applied before + * we return + */ + mb(); + } goto out; out_error: - core_clk_rate = 0; + ret = -EINVAL; out: - return core_clk_rate; + return ret; } static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status) { - unsigned long core_clk_rate = 0; - u32 core_clk_cycles_per_100ms; - switch (status) { case PRE_CHANGE: - core_clk_rate = ufs_qcom_cfg_timers(hba, UFS_PWM_G1, - SLOWAUTO_MODE, 0); - if (!core_clk_rate) { + if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE, + 0, true)) { dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", __func__); return -EINVAL; } - core_clk_cycles_per_100ms = - (core_clk_rate / MSEC_PER_SEC) * 100; - ufshcd_writel(hba, core_clk_cycles_per_100ms, - REG_UFS_PA_LINK_STARTUP_TIMER); break; case POST_CHANGE: ufs_qcom_link_startup_post_change(hba); @@ -761,9 +788,9 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, break; case POST_CHANGE: - if (!ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, + if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, dev_req_params->pwr_rx, - dev_req_params->hs_rate)) { + dev_req_params->hs_rate, false)) { dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", __func__); /* @@ -822,6 +849,18 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) } } +static void ufs_qcom_set_caps(struct ufs_hba *hba) +{ + struct ufs_qcom_host *host = hba->priv; + u8 major; + u16 minor, step; + + ufs_qcom_get_controller_revision(hba, &major, &minor, &step); + + if (major >= 0x2) + host->caps = UFS_QCOM_CAP_QUNIPRO; +} + static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, const char *speed_mode) { @@ -1105,6 +1144,7 @@ static int ufs_qcom_init(struct ufs_hba *hba) if (err) goto out_disable_phy; + ufs_qcom_set_caps(hba); ufs_qcom_advertise_quirks(hba); hba->caps |= UFSHCD_CAP_CLK_GATING | @@ -1159,7 +1199,7 @@ void ufs_qcom_clk_scale_notify(struct ufs_hba *hba) ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, dev_req_params->pwr_rx, - dev_req_params->hs_rate); + dev_req_params->hs_rate, false); ufs_qcom_update_bus_bw_vote(host); } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 69c71035bf28..f0f3f9582aa3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3,7 +3,7 @@ * * This code is based on drivers/scsi/ufs/ufshcd.c * Copyright (C) 2011-2013 Samsung India Software Operations - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * Authors: * Santosh Yaraganavi <santosh.sy@samsung.com> diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index 61b7a14157d7..5d9aba7f3c47 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -111,8 +111,6 @@ enum { #define MANUFACTURE_ID_MASK UFS_MASK(0xFFFF, 0) #define PRODUCT_ID_MASK UFS_MASK(0xFFFF, 16) -#define UFS_BIT(x) (1L << (x)) - #define UTP_TRANSFER_REQ_COMPL UFS_BIT(0) #define UIC_DME_END_PT_RESET UFS_BIT(1) #define UIC_ERROR UFS_BIT(2) diff --git a/include/linux/scsi/ufs/ufs-qcom.h b/include/linux/scsi/ufs/ufs-qcom.h index 62db011f9432..047d38987f85 100644 --- a/include/linux/scsi/ufs/ufs-qcom.h +++ b/include/linux/scsi/ufs/ufs-qcom.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -75,6 +75,9 @@ enum { UFS_UFS_DBG_RD_EDTL_RAM = 0x1900, }; +/* bit definitions for REG_UFS_CFG1 register */ +#define QUNIPRO_SEL UFS_BIT(0) + /* bit definitions for REG_UFS_CFG2 register */ #define UAWM_HW_CGC_EN (1 << 0) #define UARM_HW_CGC_EN (1 << 1) @@ -180,6 +183,15 @@ struct ufs_qcom_ice_data { }; struct ufs_qcom_host { + /* + * Set this capability if host controller supports the QUniPro mode + * and if driver wants the Host controller to operate in QUniPro mode. + * Note: By default this capability will be kept enabled if host + * controller supports the QUniPro mode. + */ + #define UFS_QCOM_CAP_QUNIPRO UFS_BIT(0) + u32 caps; + struct phy *generic_phy; struct ufs_hba *hba; struct ufs_qcom_bus_vote bus_vote; @@ -203,4 +215,12 @@ struct ufs_qcom_host { #define VDDA_PLL_MIN_UV 1800000 #define VDDA_PLL_MAX_UV 1800000 +static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host) +{ + if (host->caps & UFS_QCOM_CAP_QUNIPRO) + return true; + else + return false; +} + #endif /* UFS_QCOM_H_ */ diff --git a/include/linux/scsi/ufs/ufshcd.h b/include/linux/scsi/ufs/ufshcd.h index b731b54abc9c..67c75a6a3ed3 100644 --- a/include/linux/scsi/ufs/ufshcd.h +++ b/include/linux/scsi/ufs/ufshcd.h @@ -3,7 +3,7 @@ * * This code is based on drivers/scsi/ufs/ufshcd.h * Copyright (C) 2011-2013 Samsung India Software Operations - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * Authors: * Santosh Yaraganavi <santosh.sy@samsung.com> @@ -71,6 +71,8 @@ #define UFSHCD "ufshcd" #define UFSHCD_DRIVER_VERSION "0.2" +#define UFS_BIT(x) BIT(x) + struct ufs_hba; enum dev_cmd_type { |
