ufs: snapshot of UFS controller & PHY driver
This is a snapshot of UFS driver (host controller and PHY) taken as of
msm-4.4 commit <98b0bcfa> ("wil6210: fix memory access violation in
wil_memcpy_from/toio_32").
Snapshot is taken of following files:
drivers/scsi/ufs/*
drivers/phy/phy-qcom-ufs*
drivers/phy/Makefile
include/linux/phy/phy-qcom-ufs.h
include/uapi/scsi/ufs/*
include/trace/events/ufs.h
Documentation/scsi/ufs.txt
Documentation/devicetree/bindings/ufs/*
Fixed some of the minor compilation errrors after taking the snapshot.
Change-Id: Ia0c437de30f17f3f5e331b174522e9f6843599c9
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
afe2a79dd8
commit
9c8077087e
20 changed files with 1983 additions and 392 deletions
|
|
@ -7,11 +7,12 @@ To bind UFS PHY with UFS host controller, the controller node should
|
|||
contain a phandle reference to UFS PHY node.
|
||||
|
||||
Required properties:
|
||||
- compatible : compatible list, contains one of the following:
|
||||
- compatible : compatible list, contains one of the following
|
||||
according to the relevant phy in use:
|
||||
"qcom,ufs-phy-qmp-14nm"
|
||||
"qcom,ufs-phy-qmp-v3"
|
||||
"qcom,ufs-phy-qrbtc-sdm845"
|
||||
according to the relevant phy in use.
|
||||
"qcom,ufs-phy-qmp-v3-660"
|
||||
- reg : should contain PHY register address space (mandatory),
|
||||
- reg-names : indicates various resources passed to driver (via reg proptery) by name.
|
||||
Required "reg-names" is "phy_mem".
|
||||
|
|
@ -27,9 +28,6 @@ according to the relevant phy in use.
|
|||
Optional properties:
|
||||
- vdda-phy-max-microamp : specifies max. load that can be drawn from phy supply
|
||||
- vdda-pll-max-microamp : specifies max. load that can be drawn from pll supply
|
||||
- vddp-ref-clk-supply : phandle to UFS device ref_clk pad power supply
|
||||
- vddp-ref-clk-max-microamp : specifies max. load that can be drawn from this supply
|
||||
- vddp-ref-clk-always-on : specifies if this supply needs to be kept always on
|
||||
- qcom,disable-lpm : disable various LPM mechanisms in UFS for platform compatibility
|
||||
(limit link to PWM Gear-1, 1-lane slow mode; disable hibernate, and avoid suspend/resume)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ Required properties:
|
|||
- reg : <registers mapping>
|
||||
first entry should contain UFS host controller register address space (mandatory),
|
||||
second entry is the device ref. clock control register map (optional).
|
||||
- reset : reset specifier pair consists of phandle for the reset provider
|
||||
and reset lines used by this controller.
|
||||
- reset-names : reset signal name strings sorted in the same order as the resets property.
|
||||
|
||||
Optional properties:
|
||||
- phys : phandle to UFS PHY node
|
||||
|
|
@ -52,6 +55,8 @@ Optional properties:
|
|||
- lanes-per-direction: number of lanes available per direction - either 1 or 2.
|
||||
Note that it is assume same number of lanes is used both directions at once.
|
||||
If not specified, default is 2 lanes per direction.
|
||||
- pinctrl-names, pinctrl-0, pinctrl-1,.. pinctrl-n: Refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
|
||||
for these optional properties
|
||||
- limit-tx-hs-gear : Specify the max. limit on the TX HS gear.
|
||||
Valid range: 1-3. 1 => HS-G1, 2 => HS-G2, 3 => HS-G3
|
||||
- limit-rx-hs-gear : Specify the max. limit on the RX HS gear. Refer "limit-tx-hs-gear" for expected values.
|
||||
|
|
@ -89,6 +94,8 @@ Example:
|
|||
clocks = <&core 0>, <&ref 0>, <&iface 0>;
|
||||
clock-names = "core_clk", "ref_clk", "iface_clk";
|
||||
freq-table-hz = <100000000 200000000>, <0 0>, <0 0>;
|
||||
resets = <clock_gcc GCC_UFS_BCR>;
|
||||
reset-names = "core_reset";
|
||||
phys = <&ufsphy1>;
|
||||
phy-names = "ufsphy";
|
||||
rpm-level = <3>;
|
||||
|
|
@ -146,6 +153,9 @@ enabled and functional in the driver:
|
|||
- qcom,pm-qos-default-cpu: PM QoS voting is based on the cpu associated with each IO request by the block layer.
|
||||
This defined the default cpu used for PM QoS voting in case a specific cpu value is not available.
|
||||
|
||||
- qcom,vddp-ref-clk-supply : reference clock to ufs device. Controlled by the host driver.
|
||||
- qcom,vddp-ref-clk-max-microamp : specifies max. load that can be drawn for
|
||||
ref-clk supply.
|
||||
Example:
|
||||
ufshc@0xfc598000 {
|
||||
...
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
|
|||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v3.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qrbtc-sdm845.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v3-660.o
|
||||
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
||||
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
|
||||
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ struct ufs_qcom_phy {
|
|||
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
|
||||
* @configure_lpm: pointer to a function that configures the phy
|
||||
* for low power mode.
|
||||
* @dbg_register_dump: pointer to a function that dumps phy registers for debug.
|
||||
*/
|
||||
struct ufs_qcom_phy_specific_ops {
|
||||
int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
|
||||
|
|
@ -161,6 +162,7 @@ struct ufs_qcom_phy_specific_ops {
|
|||
void (*ctrl_rx_linecfg)(struct ufs_qcom_phy *phy, bool ctrl);
|
||||
void (*power_control)(struct ufs_qcom_phy *phy, bool val);
|
||||
int (*configure_lpm)(struct ufs_qcom_phy *phy, bool enable);
|
||||
void (*dbg_register_dump)(struct ufs_qcom_phy *phy);
|
||||
};
|
||||
|
||||
struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
|
||||
|
|
@ -184,5 +186,6 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
|||
void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
struct ufs_qcom_phy_calibration *tbl,
|
||||
int tbl_size);
|
||||
|
||||
void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy,
|
||||
int offset, int len, char *prefix);
|
||||
#endif
|
||||
|
|
|
|||
260
drivers/phy/phy-qcom-ufs-qmp-v3-660.c
Normal file
260
drivers/phy/phy-qcom-ufs-qmp-v3-660.c
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2017, 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
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "phy-qcom-ufs-qmp-v3-660.h"
|
||||
|
||||
#define UFS_PHY_NAME "ufs_phy_qmp_v3_660"
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_v3_660_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
bool is_rate_B)
|
||||
{
|
||||
int err;
|
||||
int tbl_size_A, tbl_size_B;
|
||||
struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
|
||||
u8 major = ufs_qcom_phy->host_ctrl_rev_major;
|
||||
u16 minor = ufs_qcom_phy->host_ctrl_rev_minor;
|
||||
u16 step = ufs_qcom_phy->host_ctrl_rev_step;
|
||||
|
||||
tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
|
||||
tbl_B = phy_cal_table_rate_B;
|
||||
|
||||
if ((major == 0x3) && (minor == 0x001) && (step == 0x001)) {
|
||||
tbl_A = phy_cal_table_rate_A_3_1_1;
|
||||
tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_3_1_1);
|
||||
} else {
|
||||
dev_err(ufs_qcom_phy->dev,
|
||||
"%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n",
|
||||
__func__, major, minor, step);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ufs_qcom_phy_calibrate(ufs_qcom_phy,
|
||||
tbl_A, tbl_size_A,
|
||||
tbl_B, tbl_size_B,
|
||||
is_rate_B);
|
||||
|
||||
if (err)
|
||||
dev_err(ufs_qcom_phy->dev,
|
||||
"%s: ufs_qcom_phy_calibrate() failed %d\n",
|
||||
__func__, err);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_v3_660_init(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy_qmp_v3_660 *phy = phy_get_drvdata(generic_phy);
|
||||
struct ufs_qcom_phy *phy_common = &phy->common_cfg;
|
||||
int err;
|
||||
|
||||
err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
|
||||
if (err) {
|
||||
dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
|
||||
if (err) {
|
||||
dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static
|
||||
void ufs_qcom_phy_qmp_v3_660_power_control(struct ufs_qcom_phy *phy,
|
||||
bool power_ctrl)
|
||||
{
|
||||
if (!power_ctrl) {
|
||||
/* apply analog power collapse */
|
||||
writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
|
||||
/*
|
||||
* Make sure that PHY knows its analog rail is going to be
|
||||
* powered OFF.
|
||||
*/
|
||||
mb();
|
||||
} else {
|
||||
/* bring PHY out of analog power collapse */
|
||||
writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
|
||||
|
||||
/*
|
||||
* Before any transactions involving PHY, ensure PHY knows
|
||||
* that it's analog rail is powered ON.
|
||||
*/
|
||||
mb();
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
void ufs_qcom_phy_qmp_v3_660_set_tx_lane_enable(struct ufs_qcom_phy *phy,
|
||||
u32 val)
|
||||
{
|
||||
/*
|
||||
* v3 PHY does not have TX_LANE_ENABLE register.
|
||||
* Implement this function so as not to propagate error to caller.
|
||||
*/
|
||||
}
|
||||
|
||||
static
|
||||
void ufs_qcom_phy_qmp_v3_660_ctrl_rx_linecfg(struct ufs_qcom_phy *phy,
|
||||
bool ctrl)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE);
|
||||
|
||||
if (ctrl) /* enable RX LineCfg */
|
||||
temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT;
|
||||
else /* disable RX LineCfg */
|
||||
temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT;
|
||||
|
||||
writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE);
|
||||
/* Make sure that RX LineCfg config applied before we return */
|
||||
mb();
|
||||
}
|
||||
|
||||
static inline void ufs_qcom_phy_qmp_v3_660_start_serdes(
|
||||
struct ufs_qcom_phy *phy)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
|
||||
tmp &= ~MASK_SERDES_START;
|
||||
tmp |= (1 << OFFSET_SERDES_START);
|
||||
writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
|
||||
/* Ensure register value is committed */
|
||||
mb();
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_v3_660_is_pcs_ready(
|
||||
struct ufs_qcom_phy *phy_common)
|
||||
{
|
||||
int err = 0;
|
||||
u32 val;
|
||||
|
||||
err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
|
||||
val, (val & MASK_PCS_READY), 10, 1000000);
|
||||
if (err)
|
||||
dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
|
||||
__func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ufs_qcom_phy_qmp_v3_660_dbg_register_dump(
|
||||
struct ufs_qcom_phy *phy)
|
||||
{
|
||||
ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE,
|
||||
"PHY QSERDES COM Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE,
|
||||
"PHY Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, RX_BASE, RX_SIZE,
|
||||
"PHY RX0 Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, TX_BASE, TX_SIZE,
|
||||
"PHY TX0 Registers ");
|
||||
}
|
||||
|
||||
struct phy_ops ufs_qcom_phy_qmp_v3_660_phy_ops = {
|
||||
.init = ufs_qcom_phy_qmp_v3_660_init,
|
||||
.exit = ufs_qcom_phy_exit,
|
||||
.power_on = ufs_qcom_phy_power_on,
|
||||
.power_off = ufs_qcom_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct ufs_qcom_phy_specific_ops phy_v3_660_ops = {
|
||||
.calibrate_phy = ufs_qcom_phy_qmp_v3_660_phy_calibrate,
|
||||
.start_serdes = ufs_qcom_phy_qmp_v3_660_start_serdes,
|
||||
.is_physical_coding_sublayer_ready =
|
||||
ufs_qcom_phy_qmp_v3_660_is_pcs_ready,
|
||||
.set_tx_lane_enable = ufs_qcom_phy_qmp_v3_660_set_tx_lane_enable,
|
||||
.ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_660_ctrl_rx_linecfg,
|
||||
.power_control = ufs_qcom_phy_qmp_v3_660_power_control,
|
||||
.dbg_register_dump = ufs_qcom_phy_qmp_v3_660_dbg_register_dump,
|
||||
};
|
||||
|
||||
static int ufs_qcom_phy_qmp_v3_660_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *generic_phy;
|
||||
struct ufs_qcom_phy_qmp_v3_660 *phy;
|
||||
int err = 0;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
|
||||
&ufs_qcom_phy_qmp_v3_660_phy_ops,
|
||||
&phy_v3_660_ops);
|
||||
|
||||
if (!generic_phy) {
|
||||
dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
|
||||
__func__);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
|
||||
strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
|
||||
sizeof(phy->common_cfg.name));
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_v3_660_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *generic_phy = to_phy(dev);
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
int err = 0;
|
||||
|
||||
err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
|
||||
if (err)
|
||||
dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
|
||||
__func__, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id ufs_qcom_phy_qmp_v3_660_of_match[] = {
|
||||
{.compatible = "qcom,ufs-phy-qmp-v3-660"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_660_of_match);
|
||||
|
||||
static struct platform_driver ufs_qcom_phy_qmp_v3_660_driver = {
|
||||
.probe = ufs_qcom_phy_qmp_v3_660_probe,
|
||||
.remove = ufs_qcom_phy_qmp_v3_660_remove,
|
||||
.driver = {
|
||||
.of_match_table = ufs_qcom_phy_qmp_v3_660_of_match,
|
||||
.name = "ufs_qcom_phy_qmp_v3_660",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ufs_qcom_phy_qmp_v3_660_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3 660");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
283
drivers/phy/phy-qcom-ufs-qmp-v3-660.h
Normal file
283
drivers/phy/phy-qcom-ufs-qmp-v3-660.h
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2017, 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
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UFS_QCOM_PHY_QMP_V3_660_H_
|
||||
#define UFS_QCOM_PHY_QMP_V3_660_H_
|
||||
|
||||
#include "phy-qcom-ufs-i.h"
|
||||
|
||||
/* QCOM UFS PHY control registers */
|
||||
#define COM_BASE 0x000
|
||||
#define COM_OFF(x) (COM_BASE + x)
|
||||
#define COM_SIZE 0x1C0
|
||||
|
||||
#define TX_BASE 0x400
|
||||
#define TX_OFF(x) (TX_BASE + x)
|
||||
#define TX_SIZE 0x128
|
||||
|
||||
#define RX_BASE 0x600
|
||||
#define RX_OFF(x) (RX_BASE + x)
|
||||
#define RX_SIZE 0x1FC
|
||||
|
||||
#define PHY_BASE 0xC00
|
||||
#define PHY_OFF(x) (PHY_BASE + x)
|
||||
#define PHY_SIZE 0x1B4
|
||||
|
||||
/* UFS PHY QSERDES COM registers */
|
||||
#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00)
|
||||
#define QSERDES_COM_ATB_SEL2 COM_OFF(0x04)
|
||||
#define QSERDES_COM_FREQ_UPDATE COM_OFF(0x08)
|
||||
#define QSERDES_COM_BG_TIMER COM_OFF(0x0C)
|
||||
#define QSERDES_COM_SSC_EN_CENTER COM_OFF(0x10)
|
||||
#define QSERDES_COM_SSC_ADJ_PER1 COM_OFF(0x14)
|
||||
#define QSERDES_COM_SSC_ADJ_PER2 COM_OFF(0x18)
|
||||
#define QSERDES_COM_SSC_PER1 COM_OFF(0x1C)
|
||||
#define QSERDES_COM_SSC_PER2 COM_OFF(0x20)
|
||||
#define QSERDES_COM_SSC_STEP_SIZE1 COM_OFF(0x24)
|
||||
#define QSERDES_COM_SSC_STEP_SIZE2 COM_OFF(0x28)
|
||||
#define QSERDES_COM_POST_DIV COM_OFF(0x2C)
|
||||
#define QSERDES_COM_POST_DIV_MUX COM_OFF(0x30)
|
||||
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34)
|
||||
#define QSERDES_COM_CLK_ENABLE1 COM_OFF(0x38)
|
||||
#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C)
|
||||
#define QSERDES_COM_SYSCLK_BUF_ENABLE COM_OFF(0x40)
|
||||
#define QSERDES_COM_PLL_EN COM_OFF(0x44)
|
||||
#define QSERDES_COM_PLL_IVCO COM_OFF(0x48)
|
||||
#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0X4C)
|
||||
#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0X50)
|
||||
#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0X54)
|
||||
#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0X58)
|
||||
#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0X5C)
|
||||
#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0X60)
|
||||
#define QSERDES_COM_CMD_RSVD0 COM_OFF(0x64)
|
||||
#define QSERDES_COM_EP_CLOCK_DETECT_CTRL COM_OFF(0x68)
|
||||
#define QSERDES_COM_SYSCLK_DET_COMP_STATUS COM_OFF(0x6C)
|
||||
#define QSERDES_COM_BG_TRIM COM_OFF(0x70)
|
||||
#define QSERDES_COM_CLK_EP_DIV COM_OFF(0x74)
|
||||
#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78)
|
||||
#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C)
|
||||
#define QSERDES_COM_CMN_RSVD1 COM_OFF(0x80)
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84)
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88)
|
||||
#define QSERDES_COM_CMN_RSVD2 COM_OFF(0x8C)
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90)
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94)
|
||||
#define QSERDES_COM_CMN_RSVD3 COM_OFF(0x98)
|
||||
#define QSERDES_COM_PLL_CNTRL COM_OFF(0x9C)
|
||||
#define QSERDES_COM_PHASE_SEL_CTRL COM_OFF(0xA0)
|
||||
#define QSERDES_COM_PHASE_SEL_DC COM_OFF(0xA4)
|
||||
#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM COM_OFF(0xA8)
|
||||
#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC)
|
||||
#define QSERDES_COM_CML_SYSCLK_SEL COM_OFF(0xB0)
|
||||
#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4)
|
||||
#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0xB8)
|
||||
#define QSERDES_COM_RESTRIM_CTRL COM_OFF(0xBC)
|
||||
#define QSERDES_COM_RESTRIM_CTRL2 COM_OFF(0xC0)
|
||||
#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8)
|
||||
#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC)
|
||||
#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0)
|
||||
#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4)
|
||||
#define QSERDES_COM_VCOCAL_DEADMAN_CTRL COM_OFF(0xD8)
|
||||
#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC)
|
||||
#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0)
|
||||
#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4)
|
||||
#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8)
|
||||
#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC)
|
||||
#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0)
|
||||
#define QSERDES_COM_VCO_TUNE_MINVAL1 COM_OFF(0xF4)
|
||||
#define QSERDES_COM_VCO_TUNE_MINVAL2 COM_OFF(0xF8)
|
||||
#define QSERDES_COM_CMN_RSVD4 COM_OFF(0xFC)
|
||||
#define QSERDES_COM_INTEGLOOP_INITVAL COM_OFF(0x100)
|
||||
#define QSERDES_COM_INTEGLOOP_EN COM_OFF(0x104)
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108)
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C)
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110)
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114)
|
||||
#define QSERDES_COM_VCO_TUNE_MAXVAL1 COM_OFF(0x118)
|
||||
#define QSERDES_COM_VCO_TUNE_MAXVAL2 COM_OFF(0x11C)
|
||||
#define QSERDES_COM_RES_TRIM_CONTROL2 COM_OFF(0x120)
|
||||
#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124)
|
||||
#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128)
|
||||
#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C)
|
||||
#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130)
|
||||
#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134)
|
||||
#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138)
|
||||
#define QSERDES_COM_VCO_TUNE_INITVAL1 COM_OFF(0x13C)
|
||||
#define QSERDES_COM_VCO_TUNE_INITVAL2 COM_OFF(0x140)
|
||||
#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144)
|
||||
#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148)
|
||||
#define QSERDES_COM_SAR COM_OFF(0x14C)
|
||||
#define QSERDES_COM_SAR_CLK COM_OFF(0x150)
|
||||
#define QSERDES_COM_SAR_CODE_OUT_STATUS COM_OFF(0x154)
|
||||
#define QSERDES_COM_SAR_CODE_READY_STATUS COM_OFF(0x158)
|
||||
#define QSERDES_COM_CMN_STATUS COM_OFF(0x15C)
|
||||
#define QSERDES_COM_RESET_SM_STATUS COM_OFF(0x160)
|
||||
#define QSERDES_COM_RESTRIM_CODE_STATUS COM_OFF(0x164)
|
||||
#define QSERDES_COM_PLLCAL_CODE1_STATUS COM_OFF(0x168)
|
||||
#define QSERDES_COM_PLLCAL_CODE2_STATUS COM_OFF(0x16C)
|
||||
#define QSERDES_COM_BG_CTRL COM_OFF(0x170)
|
||||
#define QSERDES_COM_CLK_SELECT COM_OFF(0x174)
|
||||
#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178)
|
||||
#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS COM_OFF(0x17C)
|
||||
#define QSERDES_COM_PLL_ANALOG COM_OFF(0x180)
|
||||
#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184)
|
||||
#define QSERDES_COM_SW_RESET COM_OFF(0x188)
|
||||
#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C)
|
||||
#define QSERDES_COM_C_READY_STATUS COM_OFF(0x190)
|
||||
#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194)
|
||||
#define QSERDES_COM_CMN_RATE_OVERRIDE COM_OFF(0x198)
|
||||
#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C)
|
||||
#define QSERDES_COM_DEBUG_BUS0 COM_OFF(0x1A0)
|
||||
#define QSERDES_COM_DEBUG_BUS1 COM_OFF(0x1A4)
|
||||
#define QSERDES_COM_DEBUG_BUS2 COM_OFF(0x1A8)
|
||||
#define QSERDES_COM_DEBUG_BUS3 COM_OFF(0x1AC)
|
||||
#define QSERDES_COM_DEBUG_BUS_SEL COM_OFF(0x1B0)
|
||||
#define QSERDES_COM_CMN_MISC1 COM_OFF(0x1B4)
|
||||
#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC)
|
||||
#define QSERDES_COM_CMN_RSVD5 COM_OFF(0x1C0)
|
||||
|
||||
/* UFS PHY registers */
|
||||
#define UFS_PHY_PHY_START PHY_OFF(0x00)
|
||||
#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04)
|
||||
#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x34)
|
||||
#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x3C)
|
||||
#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP PHY_OFF(0xCC)
|
||||
#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x138)
|
||||
#define UFS_PHY_RX_SYM_RESYNC_CTRL PHY_OFF(0x13C)
|
||||
#define UFS_PHY_RX_SIGDET_CTRL2 PHY_OFF(0x148)
|
||||
#define UFS_PHY_RX_PWM_GEAR_BAND PHY_OFF(0x154)
|
||||
#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168)
|
||||
|
||||
/* UFS PHY TX registers */
|
||||
#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0x68)
|
||||
#define QSERDES_TX_LANE_MODE TX_OFF(0x94)
|
||||
|
||||
/* UFS PHY RX registers */
|
||||
#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF RX_OFF(0x30)
|
||||
#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(0x34)
|
||||
#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH RX_OFF(0x38)
|
||||
#define QSERDES_RX_UCDR_SVS_SO_GAIN RX_OFF(0x3C)
|
||||
#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0x40)
|
||||
#define QSERDES_RX_UCDR_SO_SATURATION_ENABLE RX_OFF(0x48)
|
||||
#define QSERDES_RX_RX_TERM_BW RX_OFF(0x90)
|
||||
#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0xC4)
|
||||
#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0xC8)
|
||||
#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0xCC)
|
||||
#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0xD0)
|
||||
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0xD8)
|
||||
#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0x114)
|
||||
#define QSERDES_RX_SIGDET_LVL RX_OFF(0x118)
|
||||
#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0x11C)
|
||||
#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0x12C)
|
||||
|
||||
|
||||
#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1)
|
||||
|
||||
/*
|
||||
* This structure represents the v3 660 specific phy.
|
||||
* common_cfg MUST remain the first field in this structure
|
||||
* in case extra fields are added. This way, when calling
|
||||
* get_ufs_qcom_phy() of generic phy, we can extract the
|
||||
* common phy structure (struct ufs_qcom_phy) out of it
|
||||
* regardless of the relevant specific phy.
|
||||
*/
|
||||
struct ufs_qcom_phy_qmp_v3_660 {
|
||||
struct ufs_qcom_phy common_cfg;
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_1[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
|
||||
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x06),
|
||||
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D),
|
||||
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TRIM, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x15),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_SATURATION_ENABLE, 0x4B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL1, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6c),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03),
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x44),
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2017, 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
|
||||
|
|
@ -23,12 +23,26 @@ int ufs_qcom_phy_qmp_v3_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
|||
int err;
|
||||
int tbl_size_A, tbl_size_B;
|
||||
struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
|
||||
u8 major = ufs_qcom_phy->host_ctrl_rev_major;
|
||||
u16 minor = ufs_qcom_phy->host_ctrl_rev_minor;
|
||||
u16 step = ufs_qcom_phy->host_ctrl_rev_step;
|
||||
|
||||
tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
|
||||
tbl_B = phy_cal_table_rate_B;
|
||||
|
||||
tbl_A = phy_cal_table_rate_A;
|
||||
tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
|
||||
if ((major == 0x3) && (minor == 0x000) && (step == 0x0000)) {
|
||||
tbl_A = phy_cal_table_rate_A_3_0_0;
|
||||
tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_3_0_0);
|
||||
} else if ((major == 0x3) && (minor == 0x001) && (step == 0x0000)) {
|
||||
tbl_A = phy_cal_table_rate_A_3_1_0;
|
||||
tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_3_1_0);
|
||||
} else {
|
||||
dev_err(ufs_qcom_phy->dev,
|
||||
"%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n",
|
||||
__func__, major, minor, step);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ufs_qcom_phy_calibrate(ufs_qcom_phy,
|
||||
tbl_A, tbl_size_A,
|
||||
|
|
@ -39,6 +53,8 @@ int ufs_qcom_phy_qmp_v3_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
|||
dev_err(ufs_qcom_phy->dev,
|
||||
"%s: ufs_qcom_phy_calibrate() failed %d\n",
|
||||
__func__, err);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -178,6 +194,22 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void ufs_qcom_phy_qmp_v3_dbg_register_dump(struct ufs_qcom_phy *phy)
|
||||
{
|
||||
ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE,
|
||||
"PHY QSERDES COM Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE,
|
||||
"PHY Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, RX_BASE(0), RX_SIZE,
|
||||
"PHY RX0 Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, TX_BASE(0), TX_SIZE,
|
||||
"PHY TX0 Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, RX_BASE(1), RX_SIZE,
|
||||
"PHY RX1 Registers ");
|
||||
ufs_qcom_phy_dump_regs(phy, TX_BASE(1), TX_SIZE,
|
||||
"PHY TX1 Registers ");
|
||||
}
|
||||
|
||||
struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = {
|
||||
.init = ufs_qcom_phy_qmp_v3_init,
|
||||
.exit = ufs_qcom_phy_exit,
|
||||
|
|
@ -194,6 +226,7 @@ struct ufs_qcom_phy_specific_ops phy_v3_ops = {
|
|||
.ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg,
|
||||
.power_control = ufs_qcom_phy_qmp_v3_power_control,
|
||||
.configure_lpm = ufs_qcom_phy_qmp_v3_configure_lpm,
|
||||
.dbg_register_dump = ufs_qcom_phy_qmp_v3_dbg_register_dump,
|
||||
};
|
||||
|
||||
static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2017, 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
|
||||
|
|
@ -18,10 +18,18 @@
|
|||
#include "phy-qcom-ufs-i.h"
|
||||
|
||||
/* QCOM UFS PHY control registers */
|
||||
#define COM_OFF(x) (0x000 + x)
|
||||
#define PHY_OFF(x) (0xC00 + x)
|
||||
#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
|
||||
#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
|
||||
#define COM_BASE 0x000
|
||||
#define COM_SIZE 0x18C
|
||||
#define PHY_BASE 0xC00
|
||||
#define PHY_SIZE 0x1DC
|
||||
#define TX_BASE(n) (0x400 + (0x400 * n))
|
||||
#define TX_SIZE 0x128
|
||||
#define RX_BASE(n) (0x600 + (0x400 * n))
|
||||
#define RX_SIZE 0x1FC
|
||||
#define COM_OFF(x) (COM_BASE + x)
|
||||
#define PHY_OFF(x) (PHY_BASE + x)
|
||||
#define TX_OFF(n, x) (TX_BASE(n) + x)
|
||||
#define RX_OFF(n, x) (RX_BASE(n) + x)
|
||||
|
||||
/* UFS PHY QSERDES COM registers */
|
||||
#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00)
|
||||
|
|
@ -133,9 +141,13 @@
|
|||
#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x34)
|
||||
#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x130)
|
||||
#define UFS_PHY_RX_SYM_RESYNC_CTRL PHY_OFF(0x134)
|
||||
#define UFS_PHY_RX_MIN_HIBERN8_TIME PHY_OFF(0x138)
|
||||
#define UFS_PHY_RX_SIGDET_CTRL1 PHY_OFF(0x13C)
|
||||
#define UFS_PHY_RX_SIGDET_CTRL2 PHY_OFF(0x140)
|
||||
#define UFS_PHY_RX_PWM_GEAR_BAND PHY_OFF(0x14C)
|
||||
#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x160)
|
||||
#define UFS_PHY_TX_MID_TERM_CTRL1 PHY_OFF(0x1BC)
|
||||
#define UFS_PHY_MULTI_LANE_CTRL1 PHY_OFF(0x1C4)
|
||||
|
||||
/* UFS PHY TX registers */
|
||||
#define QSERDES_TX0_TRANSCEIVER_BIAS_EN TX_OFF(0, 0x5C)
|
||||
|
|
@ -143,6 +155,9 @@
|
|||
#define QSERDES_TX0_LANE_MODE_2 TX_OFF(0, 0x90)
|
||||
#define QSERDES_TX0_LANE_MODE_3 TX_OFF(0, 0x94)
|
||||
|
||||
#define QSERDES_TX1_LANE_MODE_1 TX_OFF(1, 0x8C)
|
||||
|
||||
|
||||
/* UFS PHY RX registers */
|
||||
#define QSERDES_RX0_UCDR_SVS_SO_GAIN_HALF RX_OFF(0, 0x24)
|
||||
#define QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(0, 0x28)
|
||||
|
|
@ -163,6 +178,22 @@
|
|||
#define QSERDES_RX0_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x10C)
|
||||
#define QSERDES_RX0_RX_INTERFACE_MODE RX_OFF(0, 0x11C)
|
||||
|
||||
#define QSERDES_RX1_UCDR_SVS_SO_GAIN_HALF RX_OFF(1, 0x24)
|
||||
#define QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(1, 0x28)
|
||||
#define QSERDES_RX1_UCDR_SVS_SO_GAIN RX_OFF(1, 0x2C)
|
||||
#define QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN RX_OFF(1, 0x30)
|
||||
#define QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE RX_OFF(1, 0x34)
|
||||
#define QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW RX_OFF(1, 0x3C)
|
||||
#define QSERDES_RX1_UCDR_PI_CONTROLS RX_OFF(1, 0x44)
|
||||
#define QSERDES_RX1_RX_TERM_BW RX_OFF(1, 0x7C)
|
||||
#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(1, 0xD4)
|
||||
#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3 RX_OFF(1, 0xD8)
|
||||
#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4 RX_OFF(1, 0xDC)
|
||||
#define QSERDES_RX1_SIGDET_CNTRL RX_OFF(1, 0x104)
|
||||
#define QSERDES_RX1_SIGDET_LVL RX_OFF(1, 0x108)
|
||||
#define QSERDES_RX1_SIGDET_DEGLITCH_CNTRL RX_OFF(1, 0x10C)
|
||||
#define QSERDES_RX1_RX_INTERFACE_MODE RX_OFF(1, 0x11C)
|
||||
|
||||
#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1)
|
||||
|
||||
/*
|
||||
|
|
@ -177,7 +208,7 @@ struct ufs_qcom_phy_qmp_v3 {
|
|||
struct ufs_qcom_phy common_cfg;
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_0_0[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xD5),
|
||||
|
|
@ -234,6 +265,87 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
|
|||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0x9A), /* 8 us */
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xD5),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0A),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL1, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x36),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0xDA),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0C),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x36),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x3F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xC1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_LANE_MODE_1, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_LVL, 0x24),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_CNTRL, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_DEGLITCH_CNTRL, 0x1E),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_INTERFACE_MODE, 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN, 0x0B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_TERM_BW, 0x5B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4, 0x1D),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_HALF, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_LVL, 0x24),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_CNTRL, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_DEGLITCH_CNTRL, 0x1E),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_INTERFACE_MODE, 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN, 0x0B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_TERM_BW, 0x5B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4, 0x1D),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_HALF, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN, 0x04),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0xF1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6C),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_MID_TERM_CTRL1, 0x43),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL1, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0x9A), /* 8 us */
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
#include "phy-qcom-ufs-i.h"
|
||||
|
||||
#define MAX_PROP_NAME 32
|
||||
#define VDDA_PHY_MIN_UV 1000000
|
||||
#define VDDA_PHY_MAX_UV 1000000
|
||||
#define VDDA_PHY_MIN_UV 800000
|
||||
#define VDDA_PHY_MAX_UV 925000
|
||||
#define VDDA_PLL_MIN_UV 1200000
|
||||
#define VDDA_PLL_MAX_UV 1800000
|
||||
#define VDDP_REF_CLK_MIN_UV 1200000
|
||||
|
|
@ -113,6 +113,14 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* UFS PHY power management is managed by its parent (UFS host
|
||||
* controller) hence set the no the no runtime PM callbacks flag
|
||||
* on UFS PHY device to avoid any accidental attempt to call the
|
||||
* PM callbacks for PHY device.
|
||||
*/
|
||||
pm_runtime_no_callbacks(&generic_phy->dev);
|
||||
|
||||
common_cfg->phy_spec_ops = phy_spec_ops;
|
||||
common_cfg->dev = dev;
|
||||
|
||||
|
|
@ -191,27 +199,20 @@ ufs_qcom_phy_init_clks(struct phy *generic_phy,
|
|||
struct ufs_qcom_phy *phy_common)
|
||||
{
|
||||
int err;
|
||||
struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
err = ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk",
|
||||
&phy_common->tx_iface_clk);
|
||||
/*
|
||||
* tx_iface_clk does not exist in newer version of ufs-phy HW,
|
||||
* so don't return error if it is not found
|
||||
*/
|
||||
if (err)
|
||||
dev_dbg(phy->dev, "%s: failed to get tx_iface_clk\n",
|
||||
__func__);
|
||||
__ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk",
|
||||
&phy_common->tx_iface_clk, false);
|
||||
|
||||
err = ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk",
|
||||
&phy_common->rx_iface_clk);
|
||||
/*
|
||||
* rx_iface_clk does not exist in newer version of ufs-phy HW,
|
||||
* so don't return error if it is not found
|
||||
*/
|
||||
if (err)
|
||||
dev_dbg(phy->dev, "%s: failed to get rx_iface_clk\n",
|
||||
__func__);
|
||||
__ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk",
|
||||
&phy_common->rx_iface_clk, false);
|
||||
|
||||
err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk_src",
|
||||
&phy_common->ref_clk_src);
|
||||
|
|
@ -246,7 +247,6 @@ ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
|
|||
struct ufs_qcom_phy *phy_common)
|
||||
{
|
||||
int err;
|
||||
int vdda_phy_uV;
|
||||
|
||||
err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_pll,
|
||||
"vdda-pll");
|
||||
|
|
@ -258,10 +258,6 @@ ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
vdda_phy_uV = regulator_get_voltage(phy_common->vdda_phy.reg);
|
||||
phy_common->vdda_phy.max_uV = vdda_phy_uV;
|
||||
phy_common->vdda_phy.min_uV = vdda_phy_uV;
|
||||
|
||||
/* vddp-ref-clk-* properties are optional */
|
||||
__ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vddp_ref_clk,
|
||||
"vddp-ref-clk", true);
|
||||
|
|
@ -279,6 +275,14 @@ static int __ufs_qcom_phy_init_vreg(struct phy *phy,
|
|||
|
||||
char prop_name[MAX_PROP_NAME];
|
||||
|
||||
if (dev->of_node) {
|
||||
snprintf(prop_name, MAX_PROP_NAME, "%s-supply", name);
|
||||
if (!of_parse_phandle(dev->of_node, prop_name, 0)) {
|
||||
dev_dbg(dev, "No vreg data found for %s\n", prop_name);
|
||||
return optional ? err : -ENODATA;
|
||||
}
|
||||
}
|
||||
|
||||
vreg->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!vreg->name) {
|
||||
err = -ENOMEM;
|
||||
|
|
@ -786,3 +790,21 @@ int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable)
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ufs_qcom_phy_configure_lpm);
|
||||
|
||||
void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy, int offset,
|
||||
int len, char *prefix)
|
||||
{
|
||||
print_hex_dump(KERN_ERR, prefix,
|
||||
len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE,
|
||||
16, 4, phy->mmio + offset, len, false);
|
||||
}
|
||||
EXPORT_SYMBOL(ufs_qcom_phy_dump_regs);
|
||||
|
||||
void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
if (ufs_qcom_phy->phy_spec_ops->dbg_register_dump)
|
||||
ufs_qcom_phy->phy_spec_ops->dbg_register_dump(ufs_qcom_phy);
|
||||
}
|
||||
EXPORT_SYMBOL(ufs_qcom_phy_dbg_register_dump);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2013-2017, 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
|
||||
|
|
@ -1412,8 +1412,10 @@ static ssize_t ufsdbg_reset_controller_write(struct file *filp,
|
|||
struct ufs_hba *hba = filp->f_mapping->host->i_private;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
pm_runtime_get_sync(hba->dev);
|
||||
ufshcd_hold(hba, false);
|
||||
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
/*
|
||||
* simulating a dummy error in order to "convince"
|
||||
* eh_work to actually reset the controller
|
||||
|
|
@ -1421,9 +1423,13 @@ static ssize_t ufsdbg_reset_controller_write(struct file *filp,
|
|||
hba->saved_err |= INT_FATAL_ERRORS;
|
||||
hba->silence_err_logs = true;
|
||||
schedule_work(&hba->eh_work);
|
||||
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
|
||||
flush_work(&hba->eh_work);
|
||||
|
||||
ufshcd_release(hba, false);
|
||||
pm_runtime_put_sync(hba->dev);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
|
@ -1471,8 +1477,8 @@ DEFINE_SIMPLE_ATTRIBUTE(ufsdbg_err_state,
|
|||
void ufsdbg_add_debugfs(struct ufs_hba *hba)
|
||||
{
|
||||
if (!hba) {
|
||||
dev_err(hba->dev, "%s: NULL hba, exiting", __func__);
|
||||
goto err_no_root;
|
||||
pr_err("%s: NULL hba, exiting", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
hba->debugfs_files.debugfs_root = debugfs_create_dir(dev_name(hba->dev),
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ static int ufs_qcom_dbg_testbus_en_read(void *data, u64 *attr_val)
|
|||
static int ufs_qcom_dbg_testbus_en_set(void *data, u64 attr_id)
|
||||
{
|
||||
struct ufs_qcom_host *host = data;
|
||||
int ret = 0;
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
|
@ -76,7 +77,13 @@ static int ufs_qcom_dbg_testbus_en_set(void *data, u64 attr_id)
|
|||
else
|
||||
host->dbg_print_en &= ~UFS_QCOM_DBG_PRINT_TEST_BUS_EN;
|
||||
|
||||
return ufs_qcom_testbus_config(host);
|
||||
pm_runtime_get_sync(host->hba->dev);
|
||||
ufshcd_hold(host->hba, false);
|
||||
ret = ufs_qcom_testbus_config(host);
|
||||
ufshcd_release(host->hba, false);
|
||||
pm_runtime_put_sync(host->hba->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_testbus_en_ops,
|
||||
|
|
@ -142,7 +149,11 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file,
|
|||
* Sanity check of the {major, minor} tuple is done in the
|
||||
* config function
|
||||
*/
|
||||
pm_runtime_get_sync(host->hba->dev);
|
||||
ufshcd_hold(host->hba, false);
|
||||
ret = ufs_qcom_testbus_config(host);
|
||||
ufshcd_release(host->hba, false);
|
||||
pm_runtime_put_sync(host->hba->dev);
|
||||
if (!ret)
|
||||
dev_dbg(host->hba->dev,
|
||||
"%s: New configuration: major=%d, minor=%d\n",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2014-2017, 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
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <crypto/ice.h>
|
||||
|
||||
#include "ufs-qcom-ice.h"
|
||||
|
|
@ -168,13 +169,23 @@ out:
|
|||
|
||||
static void ufs_qcom_ice_cfg_work(struct work_struct *work)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ice_data_setting ice_set;
|
||||
struct ufs_qcom_host *qcom_host =
|
||||
container_of(work, struct ufs_qcom_host, ice_cfg_work);
|
||||
struct request *req_pending = NULL;
|
||||
|
||||
if (!qcom_host->ice.vops->config_start || !qcom_host->req_pending)
|
||||
if (!qcom_host->ice.vops->config_start)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
|
||||
req_pending = qcom_host->req_pending;
|
||||
if (!req_pending) {
|
||||
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
|
||||
|
||||
/*
|
||||
* config_start is called again as previous attempt returned -EAGAIN,
|
||||
* this call shall now take care of the necessary key setup.
|
||||
|
|
@ -185,12 +196,17 @@ static void ufs_qcom_ice_cfg_work(struct work_struct *work)
|
|||
qcom_host->ice.vops->config_start(qcom_host->ice.pdev,
|
||||
qcom_host->req_pending, &ice_set, false);
|
||||
|
||||
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
|
||||
qcom_host->req_pending = NULL;
|
||||
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
|
||||
|
||||
/*
|
||||
* Resume with requests processing. We assume config_start has been
|
||||
* successful, but even if it wasn't we still must resume in order to
|
||||
* allow for the request to be retried.
|
||||
*/
|
||||
ufshcd_scsi_unblock_requests(qcom_host->hba);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -246,6 +262,7 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host,
|
|||
struct ice_data_setting ice_set;
|
||||
char cmd_op = cmd->cmnd[0];
|
||||
int err;
|
||||
unsigned long flags;
|
||||
|
||||
if (!qcom_host->ice.pdev || !qcom_host->ice.vops) {
|
||||
dev_dbg(qcom_host->hba->dev, "%s: ice device is not enabled\n",
|
||||
|
|
@ -255,6 +272,10 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host,
|
|||
|
||||
if (qcom_host->ice.vops->config_start) {
|
||||
memset(&ice_set, 0, sizeof(ice_set));
|
||||
|
||||
spin_lock_irqsave(
|
||||
&qcom_host->ice_work_lock, flags);
|
||||
|
||||
err = qcom_host->ice.vops->config_start(qcom_host->ice.pdev,
|
||||
cmd->request, &ice_set, true);
|
||||
if (err) {
|
||||
|
|
@ -272,19 +293,41 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host,
|
|||
dev_dbg(qcom_host->hba->dev,
|
||||
"%s: scheduling task for ice setup\n",
|
||||
__func__);
|
||||
qcom_host->req_pending = cmd->request;
|
||||
if (schedule_work(&qcom_host->ice_cfg_work))
|
||||
|
||||
if (!qcom_host->req_pending) {
|
||||
ufshcd_scsi_block_requests(
|
||||
qcom_host->hba);
|
||||
qcom_host->req_pending = cmd->request;
|
||||
|
||||
if (!schedule_work(
|
||||
&qcom_host->ice_cfg_work)) {
|
||||
qcom_host->req_pending = NULL;
|
||||
|
||||
spin_unlock_irqrestore(
|
||||
&qcom_host->ice_work_lock,
|
||||
flags);
|
||||
|
||||
ufshcd_scsi_unblock_requests(
|
||||
qcom_host->hba);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
dev_err(qcom_host->hba->dev,
|
||||
"%s: error in ice_vops->config %d\n",
|
||||
__func__, err);
|
||||
if (err != -EBUSY)
|
||||
dev_err(qcom_host->hba->dev,
|
||||
"%s: error in ice_vops->config %d\n",
|
||||
__func__, err);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&qcom_host->ice_work_lock,
|
||||
flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
|
||||
|
||||
if (ufs_qcom_is_data_cmd(cmd_op, true))
|
||||
*enable = !ice_set.encr_bypass;
|
||||
else if (ufs_qcom_is_data_cmd(cmd_op, false))
|
||||
|
|
@ -320,6 +363,7 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
|||
unsigned int bypass = 0;
|
||||
struct request *req;
|
||||
char cmd_op;
|
||||
unsigned long flags;
|
||||
|
||||
if (!qcom_host->ice.pdev || !qcom_host->ice.vops) {
|
||||
dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
|
||||
|
|
@ -339,7 +383,8 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
|||
|
||||
req = cmd->request;
|
||||
if (req->bio)
|
||||
lba = req->bio->bi_iter.bi_sector;
|
||||
lba = (req->bio->bi_iter.bi_sector) >>
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_4_KB;
|
||||
|
||||
slot = req->tag;
|
||||
if (slot < 0 || slot > qcom_host->hba->nutrs) {
|
||||
|
|
@ -348,8 +393,13 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&ice_set, 0, sizeof(ice_set));
|
||||
|
||||
if (qcom_host->ice.vops->config_start) {
|
||||
memset(&ice_set, 0, sizeof(ice_set));
|
||||
|
||||
spin_lock_irqsave(
|
||||
&qcom_host->ice_work_lock, flags);
|
||||
|
||||
err = qcom_host->ice.vops->config_start(qcom_host->ice.pdev,
|
||||
req, &ice_set, true);
|
||||
if (err) {
|
||||
|
|
@ -364,13 +414,44 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
|||
* request processing.
|
||||
*/
|
||||
if (err == -EAGAIN) {
|
||||
qcom_host->req_pending = req;
|
||||
if (schedule_work(&qcom_host->ice_cfg_work))
|
||||
|
||||
dev_dbg(qcom_host->hba->dev,
|
||||
"%s: scheduling task for ice setup\n",
|
||||
__func__);
|
||||
|
||||
if (!qcom_host->req_pending) {
|
||||
ufshcd_scsi_block_requests(
|
||||
qcom_host->hba);
|
||||
qcom_host->req_pending = cmd->request;
|
||||
if (!schedule_work(
|
||||
&qcom_host->ice_cfg_work)) {
|
||||
qcom_host->req_pending = NULL;
|
||||
|
||||
spin_unlock_irqrestore(
|
||||
&qcom_host->ice_work_lock,
|
||||
flags);
|
||||
|
||||
ufshcd_scsi_unblock_requests(
|
||||
qcom_host->hba);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (err != -EBUSY)
|
||||
dev_err(qcom_host->hba->dev,
|
||||
"%s: error in ice_vops->config %d\n",
|
||||
__func__, err);
|
||||
}
|
||||
goto out;
|
||||
|
||||
spin_unlock_irqrestore(
|
||||
&qcom_host->ice_work_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(
|
||||
&qcom_host->ice_work_lock, flags);
|
||||
}
|
||||
|
||||
cmd_op = cmd->cmnd[0];
|
||||
|
|
@ -390,6 +471,7 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
|||
bypass = ice_set.decr_bypass ? UFS_QCOM_ICE_ENABLE_BYPASS :
|
||||
UFS_QCOM_ICE_DISABLE_BYPASS;
|
||||
|
||||
|
||||
/* Configure ICE index */
|
||||
ctrl_info_val =
|
||||
(ice_set.crypto_data.key_index &
|
||||
|
|
@ -398,8 +480,7 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
|||
|
||||
/* Configure data unit size of transfer request */
|
||||
ctrl_info_val |=
|
||||
(UFS_QCOM_ICE_TR_DATA_UNIT_4_KB &
|
||||
MASK_UFS_QCOM_ICE_CTRL_INFO_CDU)
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_4_KB
|
||||
<< OFFSET_UFS_QCOM_ICE_CTRL_INFO_CDU;
|
||||
|
||||
/* Configure ICE bypass mode */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016, Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2017, 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
|
||||
|
|
@ -33,6 +33,9 @@
|
|||
#include "ufs-qcom-ice.h"
|
||||
#include "ufs-qcom-debugfs.h"
|
||||
|
||||
#define MAX_PROP_SIZE 32
|
||||
#define VDDP_REF_CLK_MIN_UV 1200000
|
||||
#define VDDP_REF_CLK_MAX_UV 1200000
|
||||
/* TODO: further tuning for this parameter may be required */
|
||||
#define UFS_QCOM_PM_QOS_UNVOTE_TIMEOUT_US (10000) /* microseconds */
|
||||
|
||||
|
|
@ -97,13 +100,10 @@ static int ufs_qcom_host_clk_get(struct device *dev,
|
|||
int err = 0;
|
||||
|
||||
clk = devm_clk_get(dev, name);
|
||||
if (IS_ERR(clk)) {
|
||||
if (IS_ERR(clk))
|
||||
err = PTR_ERR(clk);
|
||||
dev_err(dev, "%s: failed to get %s err %d",
|
||||
__func__, name, err);
|
||||
} else {
|
||||
else
|
||||
*clk_out = clk;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -182,20 +182,29 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
|
|||
|
||||
err = ufs_qcom_host_clk_get(dev,
|
||||
"rx_lane0_sync_clk", &host->rx_l0_sync_clk);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(dev, "%s: failed to get rx_lane0_sync_clk, err %d",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ufs_qcom_host_clk_get(dev,
|
||||
"tx_lane0_sync_clk", &host->tx_l0_sync_clk);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(dev, "%s: failed to get tx_lane0_sync_clk, err %d",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* In case of single lane per direction, don't read lane1 clocks */
|
||||
if (host->hba->lanes_per_direction > 1) {
|
||||
err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
|
||||
&host->rx_l1_sync_clk);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(dev, "%s: failed to get rx_lane1_sync_clk, err %d",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The tx lane1 clk could be muxed, hence keep this optional */
|
||||
ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
|
||||
|
|
@ -387,8 +396,9 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
|
|||
/**
|
||||
* Returns zero for success and non-zero in case of a failure
|
||||
*/
|
||||
static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
|
||||
u32 hs, u32 rate, bool update_link_startup_timer)
|
||||
static int __ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
|
||||
u32 hs, u32 rate, bool update_link_startup_timer,
|
||||
bool is_pre_scale_up)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
|
|
@ -435,8 +445,12 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
|
|||
}
|
||||
|
||||
list_for_each_entry(clki, &hba->clk_list_head, list) {
|
||||
if (!strcmp(clki->name, "core_clk"))
|
||||
core_clk_rate = clk_get_rate(clki->clk);
|
||||
if (!strcmp(clki->name, "core_clk")) {
|
||||
if (is_pre_scale_up)
|
||||
core_clk_rate = clki->max_freq;
|
||||
else
|
||||
core_clk_rate = clk_get_rate(clki->clk);
|
||||
}
|
||||
}
|
||||
|
||||
/* If frequency is smaller than 1MHz, set to 1MHz */
|
||||
|
|
@ -533,6 +547,13 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
|
||||
u32 hs, u32 rate, bool update_link_startup_timer)
|
||||
{
|
||||
return __ufs_qcom_cfg_timers(hba, gear, hs, rate,
|
||||
update_link_startup_timer, false);
|
||||
}
|
||||
|
||||
static int ufs_qcom_link_startup_pre_change(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
|
|
@ -666,40 +687,105 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
|
|||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int ufs_qcom_config_vreg(struct device *dev,
|
||||
struct ufs_vreg *vreg, bool on)
|
||||
{
|
||||
int ret = 0;
|
||||
struct regulator *reg;
|
||||
int min_uV, uA_load;
|
||||
|
||||
if (!vreg) {
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg = vreg->reg;
|
||||
if (regulator_count_voltages(reg) > 0) {
|
||||
min_uV = on ? vreg->min_uV : 0;
|
||||
ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: %s set voltage failed, err=%d\n",
|
||||
__func__, vreg->name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
uA_load = on ? vreg->max_uA : 0;
|
||||
ret = regulator_set_load(vreg->reg, uA_load);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_enable_vreg(struct device *dev, struct ufs_vreg *vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (vreg->enabled)
|
||||
return ret;
|
||||
|
||||
ret = ufs_qcom_config_vreg(dev, vreg, true);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regulator_enable(vreg->reg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
vreg->enabled = true;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_disable_vreg(struct device *dev, struct ufs_vreg *vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!vreg->enabled)
|
||||
return ret;
|
||||
|
||||
ret = regulator_disable(vreg->reg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = ufs_qcom_config_vreg(dev, vreg, false);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
vreg->enabled = false;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
struct phy *phy = host->generic_phy;
|
||||
int ret = 0;
|
||||
|
||||
if (ufs_qcom_is_link_off(hba)) {
|
||||
/*
|
||||
* Disable the tx/rx lane symbol clocks before PHY is
|
||||
* powered down as the PLL source should be disabled
|
||||
* after downstream clocks are disabled.
|
||||
*/
|
||||
ufs_qcom_disable_lane_clks(host);
|
||||
phy_power_off(phy);
|
||||
ret = ufs_qcom_ice_suspend(host);
|
||||
if (ret)
|
||||
dev_err(hba->dev, "%s: failed ufs_qcom_ice_suspend %d\n",
|
||||
__func__, ret);
|
||||
|
||||
/* Assert PHY soft reset */
|
||||
ufs_qcom_assert_reset(hba);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If UniPro link is not active, PHY ref_clk, main PHY analog power
|
||||
* rail and low noise analog power rail for PLL can be switched off.
|
||||
* If UniPro link is not active or OFF, PHY ref_clk, main PHY analog
|
||||
* power rail and low noise analog power rail for PLL can be
|
||||
* switched off.
|
||||
*/
|
||||
if (!ufs_qcom_is_link_active(hba)) {
|
||||
ufs_qcom_disable_lane_clks(host);
|
||||
phy_power_off(phy);
|
||||
ufs_qcom_ice_suspend(host);
|
||||
}
|
||||
|
||||
if (host->vddp_ref_clk && ufs_qcom_is_link_off(hba))
|
||||
ret = ufs_qcom_disable_vreg(hba->dev,
|
||||
host->vddp_ref_clk);
|
||||
ufs_qcom_ice_suspend(host);
|
||||
|
||||
if (ufs_qcom_is_link_off(hba)) {
|
||||
/* Assert PHY soft reset */
|
||||
ufs_qcom_assert_reset(hba);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* Unvote PM QoS */
|
||||
ufs_qcom_pm_qos_suspend(host);
|
||||
|
||||
|
|
@ -720,6 +806,11 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (host->vddp_ref_clk && (hba->rpm_lvl > UFS_PM_LVL_3 ||
|
||||
hba->spm_lvl > UFS_PM_LVL_3))
|
||||
ufs_qcom_enable_vreg(hba->dev,
|
||||
host->vddp_ref_clk);
|
||||
|
||||
err = ufs_qcom_enable_lane_clks(host);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
@ -739,7 +830,35 @@ out:
|
|||
|
||||
static int ufs_qcom_full_reset(struct ufs_hba *hba)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
int ret = -ENOTSUPP;
|
||||
|
||||
if (!hba->core_reset) {
|
||||
dev_err(hba->dev, "%s: failed, err = %d\n", __func__,
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = reset_control_assert(hba->core_reset);
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "%s: core_reset assert failed, err = %d\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hardware requirement for delay between assert/deassert
|
||||
* is at least 3-4 sleep clock (32.7KHz) cycles, which comes to
|
||||
* ~125us (4/32768). To be on the safe side add 200us delay.
|
||||
*/
|
||||
usleep_range(200, 210);
|
||||
|
||||
ret = reset_control_deassert(hba->core_reset);
|
||||
if (ret)
|
||||
dev_err(hba->dev, "%s: core_reset deassert failed, err = %d\n",
|
||||
__func__, ret);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCSI_UFS_QCOM_ICE
|
||||
|
|
@ -757,7 +876,8 @@ static int ufs_qcom_crypto_req_setup(struct ufs_hba *hba,
|
|||
|
||||
/* Use request LBA as the DUN value */
|
||||
if (req->bio)
|
||||
*dun = req->bio->bi_iter.bi_sector;
|
||||
*dun = (req->bio->bi_iter.bi_sector) >>
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_4_KB;
|
||||
|
||||
ret = ufs_qcom_ice_req_setup(host, lrbp->cmd, cc_index, enable);
|
||||
|
||||
|
|
@ -978,7 +1098,7 @@ static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result)
|
|||
}
|
||||
}
|
||||
|
||||
static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote)
|
||||
static int __ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
|
|
@ -1009,7 +1129,7 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host)
|
|||
|
||||
vote = ufs_qcom_get_bus_vote(host, mode);
|
||||
if (vote >= 0)
|
||||
err = ufs_qcom_set_bus_vote(host, vote);
|
||||
err = __ufs_qcom_set_bus_vote(host, vote);
|
||||
else
|
||||
err = vote;
|
||||
|
||||
|
|
@ -1020,6 +1140,35 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ufs_qcom_set_bus_vote(struct ufs_hba *hba, bool on)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
int vote, err;
|
||||
|
||||
/*
|
||||
* In case ufs_qcom_init() is not yet done, simply ignore.
|
||||
* This ufs_qcom_set_bus_vote() shall be called from
|
||||
* ufs_qcom_init() after init is done.
|
||||
*/
|
||||
if (!host)
|
||||
return 0;
|
||||
|
||||
if (on) {
|
||||
vote = host->bus_vote.saved_vote;
|
||||
if (vote == host->bus_vote.min_bw_vote)
|
||||
ufs_qcom_update_bus_bw_vote(host);
|
||||
} else {
|
||||
vote = host->bus_vote.min_bw_vote;
|
||||
}
|
||||
|
||||
err = __ufs_qcom_set_bus_vote(host, vote);
|
||||
if (err)
|
||||
dev_err(hba->dev, "%s: set bus vote failed %d\n",
|
||||
__func__, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -1096,7 +1245,7 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote)
|
||||
static int ufs_qcom_set_bus_vote(struct ufs_hba *hba, bool on)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1373,7 +1522,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
|||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
int err;
|
||||
int vote = 0;
|
||||
|
||||
/*
|
||||
* In case ufs_qcom_init() is not yet done, simply ignore.
|
||||
|
|
@ -1398,9 +1546,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
|||
/* enable the device ref clock for HS mode*/
|
||||
if (ufshcd_is_hs_mode(&hba->pwr_info))
|
||||
ufs_qcom_dev_ref_clk_ctrl(host, true);
|
||||
vote = host->bus_vote.saved_vote;
|
||||
if (vote == host->bus_vote.min_bw_vote)
|
||||
ufs_qcom_update_bus_bw_vote(host);
|
||||
|
||||
err = ufs_qcom_ice_resume(host);
|
||||
if (err)
|
||||
|
|
@ -1412,21 +1557,19 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
|||
|
||||
/* M-PHY RMMI interface clocks can be turned off */
|
||||
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
|
||||
if (!ufs_qcom_is_link_active(hba)) {
|
||||
if (!is_gating_context)
|
||||
/* turn off UFS local PHY ref_clk */
|
||||
ufs_qcom_phy_disable_ref_clk(host->generic_phy);
|
||||
/*
|
||||
* If auto hibern8 is supported then the link will already
|
||||
* be in hibern8 state and the ref clock can be gated.
|
||||
*/
|
||||
if (ufshcd_is_auto_hibern8_supported(hba) ||
|
||||
!ufs_qcom_is_link_active(hba)) {
|
||||
/* turn off UFS local PHY ref_clk */
|
||||
ufs_qcom_phy_disable_ref_clk(host->generic_phy);
|
||||
/* disable device ref_clk */
|
||||
ufs_qcom_dev_ref_clk_ctrl(host, false);
|
||||
}
|
||||
vote = host->bus_vote.min_bw_vote;
|
||||
}
|
||||
|
||||
err = ufs_qcom_set_bus_vote(host, vote);
|
||||
if (err)
|
||||
dev_err(hba->dev, "%s: set bus vote failed %d\n",
|
||||
__func__, err);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1850,6 +1993,57 @@ static void ufs_qcom_save_host_ptr(struct ufs_hba *hba)
|
|||
dev_err(hba->dev, "invalid host index %d\n", id);
|
||||
}
|
||||
|
||||
static int ufs_qcom_parse_reg_info(struct ufs_qcom_host *host, char *name,
|
||||
struct ufs_vreg **out_vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
char prop_name[MAX_PROP_SIZE];
|
||||
struct ufs_vreg *vreg = NULL;
|
||||
struct device *dev = host->hba->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "%s: non DT initialization\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
|
||||
if (!of_parse_phandle(np, prop_name, 0)) {
|
||||
dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
|
||||
__func__, prop_name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
|
||||
if (!vreg)
|
||||
return -ENOMEM;
|
||||
|
||||
vreg->name = name;
|
||||
|
||||
snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
|
||||
ret = of_property_read_u32(np, prop_name, &vreg->max_uA);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: unable to find %s err %d\n",
|
||||
__func__, prop_name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vreg->reg = devm_regulator_get(dev, vreg->name);
|
||||
if (IS_ERR(vreg->reg)) {
|
||||
ret = PTR_ERR(vreg->reg);
|
||||
dev_err(dev, "%s: %s get failed, err=%d\n",
|
||||
__func__, vreg->name, ret);
|
||||
}
|
||||
vreg->min_uV = VDDP_REF_CLK_MIN_UV;
|
||||
vreg->max_uV = VDDP_REF_CLK_MAX_UV;
|
||||
|
||||
out:
|
||||
if (!ret)
|
||||
*out_vreg = vreg;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_init - bind phy with controller
|
||||
* @hba: host controller instance
|
||||
|
|
@ -1877,14 +2071,9 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
|||
|
||||
/* Make a two way bind between the qcom host and the hba */
|
||||
host->hba = hba;
|
||||
ufshcd_set_variant(hba, host);
|
||||
spin_lock_init(&host->ice_work_lock);
|
||||
|
||||
/*
|
||||
* voting/devoting device ref_clk source is time consuming hence
|
||||
* skip devoting it during aggressive clock gating. This clock
|
||||
* will still be gated off during runtime suspend.
|
||||
*/
|
||||
hba->no_ref_clk_gating = true;
|
||||
ufshcd_set_variant(hba, host);
|
||||
|
||||
err = ufs_qcom_ice_get_dev(host);
|
||||
if (err == -EPROBE_DEFER) {
|
||||
|
|
@ -1969,14 +2158,24 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
|||
ufs_qcom_phy_save_controller_version(host->generic_phy,
|
||||
host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step);
|
||||
|
||||
err = ufs_qcom_parse_reg_info(host, "qcom,vddp-ref-clk",
|
||||
&host->vddp_ref_clk);
|
||||
phy_init(host->generic_phy);
|
||||
err = phy_power_on(host->generic_phy);
|
||||
if (err)
|
||||
goto out_unregister_bus;
|
||||
if (host->vddp_ref_clk) {
|
||||
err = ufs_qcom_enable_vreg(dev, host->vddp_ref_clk);
|
||||
if (err) {
|
||||
dev_err(dev, "%s: failed enabling ref clk supply: %d\n",
|
||||
__func__, err);
|
||||
goto out_disable_phy;
|
||||
}
|
||||
}
|
||||
|
||||
err = ufs_qcom_init_lane_clks(host);
|
||||
if (err)
|
||||
goto out_disable_phy;
|
||||
goto out_disable_vddp;
|
||||
|
||||
ufs_qcom_parse_lpm(host);
|
||||
if (host->disable_lpm)
|
||||
|
|
@ -1984,6 +2183,7 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
|||
ufs_qcom_set_caps(hba);
|
||||
ufs_qcom_advertise_quirks(hba);
|
||||
|
||||
ufs_qcom_set_bus_vote(hba, true);
|
||||
ufs_qcom_setup_clocks(hba, true, false);
|
||||
|
||||
host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN;
|
||||
|
|
@ -1999,6 +2199,9 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
|||
|
||||
goto out;
|
||||
|
||||
out_disable_vddp:
|
||||
if (host->vddp_ref_clk)
|
||||
ufs_qcom_disable_vreg(dev, host->vddp_ref_clk);
|
||||
out_disable_phy:
|
||||
phy_power_off(host->generic_phy);
|
||||
out_unregister_bus:
|
||||
|
|
@ -2059,6 +2262,11 @@ static inline int ufs_qcom_configure_lpm(struct ufs_hba *hba, bool enable)
|
|||
if (!ufs_qcom_cap_svs2(host))
|
||||
goto out;
|
||||
|
||||
if (!((host->hw_ver.major == 0x3) &&
|
||||
(host->hw_ver.minor == 0x0) &&
|
||||
(host->hw_ver.step == 0x0)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* The link should be put in hibern8 state before
|
||||
* configuring the PHY to enter/exit SVS2 mode.
|
||||
|
|
@ -2079,61 +2287,49 @@ out:
|
|||
static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
struct ufs_pa_layer_attr *attr = &host->dev_req_params;
|
||||
int err = 0;
|
||||
|
||||
if (!ufs_qcom_cap_qunipro(host))
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
return ufs_qcom_configure_lpm(hba, false);
|
||||
}
|
||||
err = ufs_qcom_configure_lpm(hba, false);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
static int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
|
||||
if (!ufs_qcom_cap_qunipro(host))
|
||||
return 0;
|
||||
if (attr)
|
||||
__ufs_qcom_cfg_timers(hba, attr->gear_rx, attr->pwr_rx,
|
||||
attr->hs_rate, false, true);
|
||||
|
||||
/* set unipro core clock cycles to 150 and clear clock divider */
|
||||
return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150);
|
||||
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ufs_qcom_clk_scale_down_pre_change(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
u32 core_clk_ctrl_reg;
|
||||
int err = 0;
|
||||
|
||||
if (!ufs_qcom_cap_qunipro(host))
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
err = ufs_qcom_configure_lpm(hba, true);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ufshcd_dme_get(hba,
|
||||
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
|
||||
&core_clk_ctrl_reg);
|
||||
|
||||
/* make sure CORE_CLK_DIV_EN is cleared */
|
||||
if (!err &&
|
||||
(core_clk_ctrl_reg & DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT)) {
|
||||
core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT;
|
||||
err = ufshcd_dme_set(hba,
|
||||
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
|
||||
core_clk_ctrl_reg);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
return ufs_qcom_configure_lpm(hba, true);
|
||||
}
|
||||
|
||||
static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
struct ufs_pa_layer_attr *attr = &host->dev_req_params;
|
||||
int err = 0;
|
||||
|
||||
if (!ufs_qcom_cap_qunipro(host))
|
||||
return 0;
|
||||
|
||||
if (attr)
|
||||
ufs_qcom_cfg_timers(hba, attr->gear_rx, attr->pwr_rx,
|
||||
attr->hs_rate, false);
|
||||
|
||||
if (ufs_qcom_cap_svs2(host))
|
||||
/*
|
||||
* For SVS2 set unipro core clock cycles to 37 and
|
||||
|
|
@ -2154,7 +2350,6 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
|
|||
bool scale_up, enum ufs_notify_change_status status)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params;
|
||||
int err = 0;
|
||||
|
||||
switch (status) {
|
||||
|
|
@ -2165,19 +2360,9 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
|
|||
err = ufs_qcom_clk_scale_down_pre_change(hba);
|
||||
break;
|
||||
case POST_CHANGE:
|
||||
if (scale_up)
|
||||
err = ufs_qcom_clk_scale_up_post_change(hba);
|
||||
else
|
||||
if (!scale_up)
|
||||
err = ufs_qcom_clk_scale_down_post_change(hba);
|
||||
|
||||
if (err || !dev_req_params)
|
||||
goto out;
|
||||
|
||||
ufs_qcom_cfg_timers(hba,
|
||||
dev_req_params->gear_rx,
|
||||
dev_req_params->pwr_rx,
|
||||
dev_req_params->hs_rate,
|
||||
false);
|
||||
ufs_qcom_update_bus_bw_vote(host);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -2186,7 +2371,6 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
|
|||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -2277,17 +2461,21 @@ void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv,
|
|||
|
||||
static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
|
||||
{
|
||||
if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
|
||||
if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN) {
|
||||
ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN,
|
||||
UFS_REG_TEST_BUS_EN, REG_UFS_CFG1);
|
||||
ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
|
||||
else
|
||||
} else {
|
||||
ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN, 0, REG_UFS_CFG1);
|
||||
ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
|
||||
}
|
||||
}
|
||||
|
||||
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
|
||||
{
|
||||
/* provide a legal default configuration */
|
||||
host->testbus.select_major = TSTBUS_UAWM;
|
||||
host->testbus.select_minor = 1;
|
||||
host->testbus.select_major = TSTBUS_UNIPRO;
|
||||
host->testbus.select_minor = 37;
|
||||
}
|
||||
|
||||
static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
|
||||
|
|
@ -2304,7 +2492,7 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
|
|||
* mappings of select_minor, since there is no harm in
|
||||
* configuring a non-existent select_minor
|
||||
*/
|
||||
if (host->testbus.select_minor > 0x1F) {
|
||||
if (host->testbus.select_minor > 0xFF) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: 0x%05X is not a legal testbus option\n",
|
||||
__func__, host->testbus.select_minor);
|
||||
|
|
@ -2314,6 +2502,11 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller of this function must make sure that the controller
|
||||
* is out of runtime suspend and appropriate clocks are enabled
|
||||
* before accessing.
|
||||
*/
|
||||
int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
|
||||
{
|
||||
int reg;
|
||||
|
|
@ -2373,7 +2566,8 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
|
|||
break;
|
||||
case TSTBUS_UNIPRO:
|
||||
reg = UFS_UNIPRO_CFG;
|
||||
offset = 1;
|
||||
offset = 20;
|
||||
mask = 0xFFF;
|
||||
break;
|
||||
/*
|
||||
* No need for a default case, since
|
||||
|
|
@ -2383,8 +2577,6 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
|
|||
}
|
||||
mask <<= offset;
|
||||
|
||||
pm_runtime_get_sync(host->hba->dev);
|
||||
ufshcd_hold(host->hba, false);
|
||||
ufshcd_rmwl(host->hba, TEST_BUS_SEL,
|
||||
(u32)host->testbus.select_major << 19,
|
||||
REG_UFS_CFG1);
|
||||
|
|
@ -2392,8 +2584,11 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
|
|||
(u32)host->testbus.select_minor << offset,
|
||||
reg);
|
||||
ufs_qcom_enable_test_bus(host);
|
||||
ufshcd_release(host->hba, false);
|
||||
pm_runtime_put_sync(host->hba->dev);
|
||||
/*
|
||||
* Make sure the test bus configuration is
|
||||
* committed before returning.
|
||||
*/
|
||||
mb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2403,15 +2598,47 @@ static void ufs_qcom_testbus_read(struct ufs_hba *hba)
|
|||
ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS ");
|
||||
}
|
||||
|
||||
static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
|
||||
static void ufs_qcom_print_unipro_testbus(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
u32 *testbus = NULL;
|
||||
int i, nminor = 256, testbus_len = nminor * sizeof(u32);
|
||||
|
||||
testbus = kmalloc(testbus_len, GFP_KERNEL);
|
||||
if (!testbus)
|
||||
return;
|
||||
|
||||
host->testbus.select_major = TSTBUS_UNIPRO;
|
||||
for (i = 0; i < nminor; i++) {
|
||||
host->testbus.select_minor = i;
|
||||
ufs_qcom_testbus_config(host);
|
||||
testbus[i] = ufshcd_readl(hba, UFS_TEST_BUS);
|
||||
}
|
||||
print_hex_dump(KERN_ERR, "UNIPRO_TEST_BUS ", DUMP_PREFIX_OFFSET,
|
||||
16, 4, testbus, testbus_len, false);
|
||||
kfree(testbus);
|
||||
}
|
||||
|
||||
static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba, bool no_sleep)
|
||||
{
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
struct phy *phy = host->generic_phy;
|
||||
|
||||
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
|
||||
"HCI Vendor Specific Registers ");
|
||||
|
||||
ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
|
||||
|
||||
if (no_sleep)
|
||||
return;
|
||||
|
||||
/* sleep a bit intermittently as we are dumping too much data */
|
||||
usleep_range(1000, 1100);
|
||||
ufs_qcom_testbus_read(hba);
|
||||
usleep_range(1000, 1100);
|
||||
ufs_qcom_print_unipro_testbus(hba);
|
||||
usleep_range(1000, 1100);
|
||||
ufs_qcom_phy_dbg_register_dump(phy);
|
||||
usleep_range(1000, 1100);
|
||||
ufs_qcom_ice_print_regs(host);
|
||||
}
|
||||
|
||||
|
|
@ -2436,6 +2663,7 @@ static struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
|
|||
.full_reset = ufs_qcom_full_reset,
|
||||
.update_sec_cfg = ufs_qcom_update_sec_cfg,
|
||||
.get_scale_down_gear = ufs_qcom_get_scale_down_gear,
|
||||
.set_bus_vote = ufs_qcom_set_bus_vote,
|
||||
.dbg_register_dump = ufs_qcom_dump_dbg_regs,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
.add_debugfs = ufs_qcom_dbg_add_debugfs,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2013-2017, 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
|
||||
|
|
@ -100,6 +100,7 @@ enum {
|
|||
#define QUNIPRO_SEL UFS_BIT(0)
|
||||
#define TEST_BUS_EN BIT(18)
|
||||
#define TEST_BUS_SEL GENMASK(22, 19)
|
||||
#define UFS_REG_TEST_BUS_EN BIT(30)
|
||||
|
||||
/* bit definitions for REG_UFS_CFG2 register */
|
||||
#define UAWM_HW_CGC_EN (1 << 0)
|
||||
|
|
@ -369,8 +370,10 @@ struct ufs_qcom_host {
|
|||
u32 dbg_print_en;
|
||||
struct ufs_qcom_testbus testbus;
|
||||
|
||||
spinlock_t ice_work_lock;
|
||||
struct work_struct ice_cfg_work;
|
||||
struct request *req_pending;
|
||||
struct ufs_vreg *vddp_ref_clk;
|
||||
};
|
||||
|
||||
static inline u32
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2013-2017, 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
|
||||
|
|
@ -689,13 +689,13 @@ static void scenario_free_end_io_fn(struct request *rq, int err)
|
|||
__blk_put_request(test_iosched->req_q, test_rq->rq);
|
||||
spin_unlock_irqrestore(&test_iosched->lock, flags);
|
||||
|
||||
test_iosched_free_test_req_data_buffer(test_rq);
|
||||
kfree(test_rq);
|
||||
|
||||
if (err)
|
||||
pr_err("%s: request %d completed, err=%d", __func__,
|
||||
test_rq->req_id, err);
|
||||
|
||||
test_iosched_free_test_req_data_buffer(test_rq);
|
||||
kfree(test_rq);
|
||||
|
||||
check_test_completion(test_iosched);
|
||||
}
|
||||
|
||||
|
|
@ -984,14 +984,14 @@ static void long_test_free_end_io_fn(struct request *rq, int err)
|
|||
return;
|
||||
}
|
||||
|
||||
test_iosched_free_test_req_data_buffer(test_rq);
|
||||
kfree(test_rq);
|
||||
utd->completed_req_count++;
|
||||
|
||||
if (err)
|
||||
pr_err("%s: request %d completed, err=%d", __func__,
|
||||
test_rq->req_id, err);
|
||||
|
||||
test_iosched_free_test_req_data_buffer(test_rq);
|
||||
kfree(test_rq);
|
||||
utd->completed_req_count++;
|
||||
|
||||
check_test_completion(test_iosched);
|
||||
}
|
||||
|
||||
|
|
@ -1007,7 +1007,7 @@ static void long_test_free_end_io_fn(struct request *rq, int err)
|
|||
static int run_long_test(struct test_iosched *test_iosched)
|
||||
{
|
||||
int ret = 0;
|
||||
int direction, num_bios_per_request;
|
||||
int direction, num_bios_per_request = 1;
|
||||
static unsigned int inserted_requests;
|
||||
u32 sector, seed, num_bios, seq_sector_delta;
|
||||
struct ufs_test_data *utd = test_iosched->blk_dev_test_data;
|
||||
|
|
@ -1028,14 +1028,12 @@ static int run_long_test(struct test_iosched *test_iosched)
|
|||
/* Set test parameters */
|
||||
switch (test_iosched->test_info.testcase) {
|
||||
case UFS_TEST_LONG_RANDOM_READ:
|
||||
num_bios_per_request = 1;
|
||||
utd->long_test_num_reqs = (utd->sector_range * SECTOR_SIZE) /
|
||||
(LONG_RAND_TEST_REQ_RATIO * TEST_BIO_SIZE *
|
||||
num_bios_per_request);
|
||||
direction = READ;
|
||||
break;
|
||||
case UFS_TEST_LONG_RANDOM_WRITE:
|
||||
num_bios_per_request = 1;
|
||||
utd->long_test_num_reqs = (utd->sector_range * SECTOR_SIZE) /
|
||||
(LONG_RAND_TEST_REQ_RATIO * TEST_BIO_SIZE *
|
||||
num_bios_per_request);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,22 @@
|
|||
#include "ufshcd.h"
|
||||
#include "ufshcd-pltfrm.h"
|
||||
|
||||
static int ufshcd_parse_reset_info(struct ufs_hba *hba)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
hba->core_reset = devm_reset_control_get(hba->dev,
|
||||
"core_reset");
|
||||
if (IS_ERR(hba->core_reset)) {
|
||||
ret = PTR_ERR(hba->core_reset);
|
||||
dev_err(hba->dev, "core_reset unavailable,err = %d\n",
|
||||
ret);
|
||||
hba->core_reset = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufshcd_parse_clock_info(struct ufs_hba *hba)
|
||||
{
|
||||
int ret = 0;
|
||||
|
|
@ -297,6 +313,20 @@ static void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba)
|
|||
hba->dev_ref_clk_freq = REF_CLK_FREQ_26_MHZ;
|
||||
}
|
||||
|
||||
static int ufshcd_parse_pinctrl_info(struct ufs_hba *hba)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Try to obtain pinctrl handle */
|
||||
hba->pctrl = devm_pinctrl_get(hba->dev);
|
||||
if (IS_ERR(hba->pctrl)) {
|
||||
ret = PTR_ERR(hba->pctrl);
|
||||
hba->pctrl = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/**
|
||||
* ufshcd_pltfrm_suspend - suspend power management function
|
||||
|
|
@ -401,6 +431,20 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
|
|||
goto dealloc_host;
|
||||
}
|
||||
|
||||
err = ufshcd_parse_reset_info(hba);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "%s: reset parse failed %d\n",
|
||||
__func__, err);
|
||||
goto dealloc_host;
|
||||
}
|
||||
|
||||
err = ufshcd_parse_pinctrl_info(hba);
|
||||
if (err) {
|
||||
dev_dbg(&pdev->dev, "%s: unable to parse pinctrl data %d\n",
|
||||
__func__, err);
|
||||
/* let's not fail the probe */
|
||||
}
|
||||
|
||||
ufshcd_parse_dev_ref_clk_freq(hba);
|
||||
ufshcd_parse_pm_levels(hba);
|
||||
ufshcd_parse_gear_limits(hba);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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-2016, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Authors:
|
||||
* Santosh Yaraganavi <santosh.sy@samsung.com>
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -55,6 +56,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include "unipro.h"
|
||||
|
||||
#include <asm/irq.h>
|
||||
|
|
@ -309,6 +311,7 @@ struct ufs_pwr_mode_info {
|
|||
* @update_sec_cfg: called to restore host controller secure configuration
|
||||
* @get_scale_down_gear: called to get the minimum supported gear to
|
||||
* scale down
|
||||
* @set_bus_vote: called to vote for the required bus bandwidth
|
||||
* @add_debugfs: used to add debugfs entries
|
||||
* @remove_debugfs: used to remove debugfs entries
|
||||
*/
|
||||
|
|
@ -332,9 +335,10 @@ struct ufs_hba_variant_ops {
|
|||
int (*suspend)(struct ufs_hba *, enum ufs_pm_op);
|
||||
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
|
||||
int (*full_reset)(struct ufs_hba *);
|
||||
void (*dbg_register_dump)(struct ufs_hba *hba);
|
||||
void (*dbg_register_dump)(struct ufs_hba *hba, bool no_sleep);
|
||||
int (*update_sec_cfg)(struct ufs_hba *hba, bool restore_sec_cfg);
|
||||
u32 (*get_scale_down_gear)(struct ufs_hba *);
|
||||
int (*set_bus_vote)(struct ufs_hba *, bool);
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void (*add_debugfs)(struct ufs_hba *hba, struct dentry *root);
|
||||
void (*remove_debugfs)(struct ufs_hba *hba);
|
||||
|
|
@ -393,8 +397,9 @@ enum clk_gating_state {
|
|||
|
||||
/**
|
||||
* struct ufs_clk_gating - UFS clock gating related info
|
||||
* @gate_work: worker to turn off clocks after some delay as specified in
|
||||
* delay_ms
|
||||
* @gate_hrtimer: hrtimer to invoke @gate_work after some delay as
|
||||
* specified in @delay_ms
|
||||
* @gate_work: worker to turn off clocks
|
||||
* @ungate_work: worker to turn on clocks that will be used in case of
|
||||
* interrupt context
|
||||
* @state: the current clocks state
|
||||
|
|
@ -412,7 +417,8 @@ enum clk_gating_state {
|
|||
* completion before gating clocks.
|
||||
*/
|
||||
struct ufs_clk_gating {
|
||||
struct delayed_work gate_work;
|
||||
struct hrtimer gate_hrtimer;
|
||||
struct work_struct gate_work;
|
||||
struct work_struct ungate_work;
|
||||
enum clk_gating_state state;
|
||||
unsigned long delay_ms;
|
||||
|
|
@ -425,6 +431,7 @@ struct ufs_clk_gating {
|
|||
struct device_attribute enable_attr;
|
||||
bool is_enabled;
|
||||
int active_reqs;
|
||||
struct workqueue_struct *ungating_workq;
|
||||
};
|
||||
|
||||
/* Hibern8 state */
|
||||
|
|
@ -801,6 +808,7 @@ struct ufs_hba {
|
|||
u32 saved_uic_err;
|
||||
u32 saved_ce_err;
|
||||
bool silence_err_logs;
|
||||
bool force_host_reset;
|
||||
|
||||
/* Device management request data */
|
||||
struct ufs_dev_cmd dev_cmd;
|
||||
|
|
@ -882,17 +890,33 @@ struct ufs_hba {
|
|||
enum bkops_status urgent_bkops_lvl;
|
||||
bool is_urgent_bkops_lvl_checked;
|
||||
|
||||
struct rw_semaphore clk_scaling_lock;
|
||||
/* sync b/w diff contexts */
|
||||
struct rw_semaphore lock;
|
||||
unsigned long shutdown_in_prog;
|
||||
|
||||
struct reset_control *core_reset;
|
||||
/* If set, don't gate device ref_clk during clock gating */
|
||||
bool no_ref_clk_gating;
|
||||
|
||||
int scsi_block_reqs_cnt;
|
||||
|
||||
bool full_init_linereset;
|
||||
struct pinctrl *pctrl;
|
||||
|
||||
int latency_hist_enabled;
|
||||
struct io_latency_state io_lat_s;
|
||||
};
|
||||
|
||||
static inline void ufshcd_mark_shutdown_ongoing(struct ufs_hba *hba)
|
||||
{
|
||||
set_bit(0, &hba->shutdown_in_prog);
|
||||
}
|
||||
|
||||
static inline bool ufshcd_is_shutdown_ongoing(struct ufs_hba *hba)
|
||||
{
|
||||
return !!(test_bit(0, &hba->shutdown_in_prog));
|
||||
}
|
||||
|
||||
/* Returns true if clocks can be gated. Otherwise false */
|
||||
static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
|
||||
{
|
||||
|
|
@ -1233,10 +1257,11 @@ static inline int ufshcd_vops_full_reset(struct ufs_hba *hba)
|
|||
}
|
||||
|
||||
|
||||
static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba)
|
||||
static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba,
|
||||
bool no_sleep)
|
||||
{
|
||||
if (hba->var && hba->var->vops && hba->var->vops->dbg_register_dump)
|
||||
hba->var->vops->dbg_register_dump(hba);
|
||||
hba->var->vops->dbg_register_dump(hba, no_sleep);
|
||||
}
|
||||
|
||||
static inline int ufshcd_vops_update_sec_cfg(struct ufs_hba *hba,
|
||||
|
|
@ -1255,6 +1280,13 @@ static inline u32 ufshcd_vops_get_scale_down_gear(struct ufs_hba *hba)
|
|||
return UFS_HS_G1;
|
||||
}
|
||||
|
||||
static inline int ufshcd_vops_set_bus_vote(struct ufs_hba *hba, bool on)
|
||||
{
|
||||
if (hba->var && hba->var->vops && hba->var->vops->set_bus_vote)
|
||||
return hba->var->vops->set_bus_vote(hba, on);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static inline void ufshcd_vops_add_debugfs(struct ufs_hba *hba,
|
||||
struct dentry *root)
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ enum {
|
|||
|
||||
/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
|
||||
#define UIC_PHY_ADAPTER_LAYER_ERROR UFS_BIT(31)
|
||||
#define UIC_PHY_ADAPTER_LAYER_GENERIC_ERROR UFS_BIT(4)
|
||||
#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F
|
||||
#define UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK 0xF
|
||||
|
||||
|
|
|
|||
|
|
@ -58,5 +58,6 @@ void ufs_qcom_phy_save_controller_version(struct phy *phy,
|
|||
u8 major, u16 minor, u16 step);
|
||||
const char *ufs_qcom_phy_name(struct phy *phy);
|
||||
int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable);
|
||||
void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy);
|
||||
|
||||
#endif /* PHY_QCOM_UFS_H_ */
|
||||
|
|
|
|||
Loading…
Reference in a new issue