diff options
| -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 { |
