ufs: Porting UFS driver's changes from kernel msm-4.14 to msm-kona
This is a snapshot of the UFS driver taken as of msm-4.14 commit
<3d758ed5e344> ("scsi: squash of multiple fixes for msm-4.4 kernel"). This
change contains the UFS driver's changes for merging from kernel version
msm-4.14 to msm-kona.
Change-Id: Iece7757565b2fe683956c419cf98a110c4de07f9
Signed-off-by: Can Guo <cang@codeaurora.org>
This commit is contained in:
33
Documentation/devicetree/bindings/ufs/ufs-msm.txt
Normal file
33
Documentation/devicetree/bindings/ufs/ufs-msm.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
* MSM Universal Flash Storage (UFS) PHY
|
||||
|
||||
UFSPHY nodes are defined to describe on-chip UFS PHY hardware macro.
|
||||
Each UFS PHY node should have its own node.
|
||||
|
||||
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 "qcom,ufsphy"
|
||||
- reg : <registers mapping>
|
||||
- vdda-phy-supply : phandle to main PHY supply for analog domain
|
||||
- vdda-pll-supply : phandle to PHY PLL and Power-Gen block power supply
|
||||
|
||||
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
|
||||
|
||||
Example:
|
||||
|
||||
ufsphy1: ufsphy@0xfc597000 {
|
||||
compatible = "qcom,ufsphy";
|
||||
reg = <0xfc597000 0x800>;
|
||||
vdda-phy-supply = <&pma8084_l4>;
|
||||
vdda-pll-supply = <&pma8084_l12>;
|
||||
vdda-phy-max-microamp = <50000>;
|
||||
vdda-pll-max-microamp = <1000>;
|
||||
};
|
||||
|
||||
ufshc@0xfc598000 {
|
||||
...
|
||||
ufs-phy = <&ufsphy1>;
|
||||
};
|
||||
@@ -8,8 +8,17 @@ contain a phandle reference to UFS PHY node.
|
||||
|
||||
Required properties:
|
||||
- compatible : compatible list, contains one of the following -
|
||||
"qcom,ufs-phy-qmp-20nm" for 20nm ufs phy,
|
||||
"qcom,ufs-phy-qmp-14nm" for legacy 14nm ufs phy,
|
||||
"qcom,ufs-phy-qmp-v3" for V3 ufs phy present
|
||||
on msmcobalt platform,
|
||||
"qcom,ufs-phy-qmp-v4" for V4 ufs phy present
|
||||
on sm8150 platform,
|
||||
"qcom,ufs-phy-qrbtc-sdm845" for phy support
|
||||
for sdm845 emulation,
|
||||
"qcom,ufs-phy-qmp-v3-falcon" for phy support
|
||||
for msmfalcon,
|
||||
"qcom,ufs-phy-qmp-v3-660" for V3 ufs phy present
|
||||
on SDM660 platform,
|
||||
"qcom,msm8996-ufs-phy-qmp-14nm" for 14nm ufs phy
|
||||
present on MSM8996 chipset.
|
||||
- reg : should contain PHY register address space (mandatory),
|
||||
@@ -29,14 +38,19 @@ Optional properties:
|
||||
- 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-min-uV : specifies min voltage that can be set for reference clock supply
|
||||
- vddp-ref-clk-max-uV : specifies max voltage that can be set for reference clock supply
|
||||
- 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)
|
||||
|
||||
Example:
|
||||
|
||||
ufsphy1: ufsphy@fc597000 {
|
||||
compatible = "qcom,ufs-phy-qmp-20nm";
|
||||
compatible = "qcom,ufs-phy-qmp-14nm";
|
||||
reg = <0xfc597000 0x800>;
|
||||
reg-names = "phy_mem";
|
||||
#phy-cells = <0>;
|
||||
lanes-per-direction = <1>;
|
||||
vdda-phy-supply = <&pma8084_l4>;
|
||||
vdda-pll-supply = <&pma8084_l12>;
|
||||
vdda-phy-max-microamp = <50000>;
|
||||
|
||||
@@ -11,6 +11,11 @@ Required properties:
|
||||
"qcom,ufshc"
|
||||
- interrupts : <interrupt mapping for UFS host controller IRQ>
|
||||
- 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
|
||||
@@ -18,6 +23,8 @@ Optional properties:
|
||||
with "phys" attribute, provides phandle to UFS PHY node
|
||||
- vdd-hba-supply : phandle to UFS host controller supply regulator node
|
||||
- vcc-supply : phandle to VCC supply regulator node
|
||||
- vcc-voltage-level : specifies voltage levels for VCC supply.
|
||||
Should be specified in pairs (min, max), units uV.
|
||||
- vccq-supply : phandle to VCCQ supply regulator node
|
||||
- vccq2-supply : phandle to VCCQ2 supply regulator node
|
||||
- vcc-supply-1p8 : For embedded UFS devices, valid VCC range is 1.7-1.95V
|
||||
@@ -38,9 +45,35 @@ Optional properties:
|
||||
defined or a value in the array is "0" then it is assumed
|
||||
that the frequency is set by the parent clock or a
|
||||
fixed rate clock source.
|
||||
- rpm-level : UFS Runtime power management level. Following PM levels are supported:
|
||||
0 - Both UFS device and Link in active state (Highest power consumption)
|
||||
1 - UFS device in active state but Link in Hibern8 state
|
||||
2 - UFS device in Sleep state but Link in active state
|
||||
3 - UFS device in Sleep state and Link in hibern8 state (default PM level)
|
||||
4 - UFS device in Power-down state and Link in Hibern8 state
|
||||
5 - UFS device in Power-down state and Link in OFF state (Lowest power consumption)
|
||||
- spm-level : UFS System power management level. Allowed PM levels are same as rpm-level.
|
||||
- ufs-qcom-crypto : phandle to UFS-QCOM ICE (Inline Cryptographic Engine) node
|
||||
-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.
|
||||
- limit-tx-pwm-gear : Specify the max. limit on the TX PWM gear
|
||||
Valid range: 1-4. 1 => PWM-G1, 2 => PWM-G2, 3 => PWM-G3, 4 => PWM-G4
|
||||
- limit-rx-pwm-gear : Specify the max. limit on the RX PWM gear. Refer "limit-tx-pwm-gear" for expected values.
|
||||
- scsi-cmd-timeout : Specify the command timeout (in seconds) for scsi commands
|
||||
- dev-ref-clk-freq : Specify the device reference clock frequency, must be one of the following:
|
||||
0: 19.2 MHz
|
||||
1: 26 MHz
|
||||
2: 38.4 MHz
|
||||
3: 52 MHz
|
||||
Defaults to 26 MHz if not specified.
|
||||
- extcon: phandle to external connector (Refer Documentation/devicetree/bindings/extcon/extcon-gpio.txt for more details).
|
||||
- non-removable : defines if the connected ufs device is not removable
|
||||
- resets : reset node register
|
||||
- reset-names : describe reset node register, the "rst" corresponds to reset the whole UFS IP.
|
||||
|
||||
@@ -50,9 +83,10 @@ regulators or clocks are always on.
|
||||
Example:
|
||||
ufshc@fc598000 {
|
||||
compatible = "jedec,ufs-1.1";
|
||||
reg = <0xfc598000 0x800>;
|
||||
reg = <0xfc598000 0x800>, <0xfd512074 0x4>;
|
||||
interrupts = <0 28 0>;
|
||||
|
||||
ufs-qcom-crypto = <&ufs_ice>;
|
||||
vdd-hba-supply = <&xxx_reg0>;
|
||||
vdd-hba-fixed-regulator;
|
||||
vcc-supply = <&xxx_reg1>;
|
||||
@@ -70,4 +104,106 @@ Example:
|
||||
reset-names = "rst";
|
||||
phys = <&ufsphy1>;
|
||||
phy-names = "ufsphy";
|
||||
rpm-level = <3>;
|
||||
spm-level = <5>;
|
||||
dev-ref-clk-freq = <0>; /* reference clock freq: 19.2 MHz */
|
||||
};
|
||||
|
||||
==== MSM UFS platform driver properties =====
|
||||
* For UFS host controller in MSM platform following clocks are required -
|
||||
Controller clock source -
|
||||
"core_clk_src", max-clock-frequency-hz = 200MHz
|
||||
|
||||
Controller System clock branch:
|
||||
"core_clk" - Controller core clock
|
||||
|
||||
AHB/AXI interface clocks:
|
||||
"iface_clk" - AHB interface clock
|
||||
"bus_clk" - AXI bus master clock
|
||||
|
||||
PHY to controller symbol synchronization clocks:
|
||||
"rx_lane0_sync_clk" - RX Lane 0
|
||||
"rx_lane1_sync_clk" - RX Lane 1
|
||||
"tx_lane0_sync_clk" - TX Lane 0
|
||||
"tx_lane1_sync_clk" - TX Lane 1
|
||||
|
||||
Optional reference clock input to UFS device
|
||||
"ref_clk", max-clock-frequency-hz = 19.2MHz
|
||||
|
||||
* Following bus parameters are required -
|
||||
- qcom,msm-bus,name
|
||||
- qcom,msm-bus,num-cases
|
||||
- qcom,msm-bus,num-paths
|
||||
- qcom,msm-bus,vectors-KBps
|
||||
For the above four properties please refer to
|
||||
Documentation/devicetree/bindings/arm/msm/msm_bus.txt
|
||||
Note: The instantaneous bandwidth (IB) value in the vectors-KBps field should
|
||||
be zero as UFS data transfer path doesn't have latency requirements and
|
||||
voting for aggregated bandwidth (AB) should take care of providing
|
||||
optimum throughput requested.
|
||||
|
||||
- qcom,bus-vector-names: specifies string IDs for the corresponding
|
||||
bus vectors in the same order as qcom,msm-bus,vectors-KBps property.
|
||||
|
||||
* The following parameters are optional, but required in order for PM QoS to be
|
||||
enabled and functional in the driver:
|
||||
- qcom,pm-qos-cpu-groups: arrays of unsigned integers representing the cpu groups.
|
||||
The number of values in the array defines the number of cpu-groups.
|
||||
Each value is a bit-mask defining the cpus that take part in that cpu group.
|
||||
i.e. if bit N is set, then cpuN is a part of the cpu group. So basically,
|
||||
a cpu group corelated to a cpu cluster.
|
||||
A PM QoS request object is maintained for each cpu-group.
|
||||
- qcom,pm-qos-cpu-group-latency-us: array of values used for PM QoS voting, one for each cpu-group defined.
|
||||
the number of values must match the number of values defined in
|
||||
qcom,pm-qos-cpu-mask property.
|
||||
- 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 {
|
||||
...
|
||||
|
||||
qcom,msm-bus,name = "ufs1";
|
||||
qcom,msm-bus,num-cases = <22>;
|
||||
qcom,msm-bus,num-paths = <2>;
|
||||
qcom,msm-bus,vectors-KBps =
|
||||
<95 512 0 0>, <1 650 0 0>, /* No vote */
|
||||
|
||||
<95 512 922 0>, <1 650 1000 0>, /* PWM G1 */
|
||||
<95 512 1844 0>, <1 650 1000 0>, /* PWM G2 */
|
||||
<95 512 3688 0>, <1 650 1000 0>, /* PWM G3 */
|
||||
<95 512 7376 0>, <1 650 1000 0>, /* PWM G4 */
|
||||
<95 512 1844 0>, <1 650 1000 0>, /* PWM G1 L2 */
|
||||
<95 512 3688 0>, <1 650 1000 0>, /* PWM G2 L2 */
|
||||
<95 512 7376 0>, <1 650 1000 0>, /* PWM G3 L2 */
|
||||
<95 512 14752 0>, <1 650 1000 0>, /* PWM G4 L2 */
|
||||
|
||||
<95 512 127796 0>, <1 650 1000 0>, /* HS G1 RA */
|
||||
<95 512 255591 0>, <1 650 1000 0>, /* HS G2 RA */
|
||||
<95 512 511181 0>, <1 650 1000 0>, /* HS G3 RA */
|
||||
<95 512 255591 0>, <1 650 1000 0>, /* HS G1 RA L2 */
|
||||
<95 512 511181 0>, <1 650 1000 0>, /* HS G2 RA L2 */
|
||||
<95 512 1022362 0>, <1 650 1000 0>, /* HS G3 RA L2 */
|
||||
|
||||
<95 512 149422 0>, <1 650 1000 0>, /* HS G1 RB */
|
||||
<95 512 298189 0>, <1 650 1000 0>, /* HS G2 RB */
|
||||
<95 512 596378 0>, <1 650 1000 0>, /* HS G3 RB */
|
||||
<95 512 298189 0>, <1 650 1000 0>, /* HS G1 RB L2 */
|
||||
<95 512 596378 0>, <1 650 1000 0>, /* HS G2 RB L2 */
|
||||
<95 512 1192756 0>, <1 650 1000 0>, /* HS G3 RB L2 */
|
||||
|
||||
<95 512 4096000 0>, <1 650 1000 0>; /* Max. bandwidth */
|
||||
|
||||
qcom,bus-vector-names = "MIN",
|
||||
"PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1",
|
||||
"PWM_G1_L2", "PWM_G2_L2", "PWM_G3_L2", "PWM_G4_L2",
|
||||
"HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1",
|
||||
"HS_RA_G1_L2", "HS_RA_G2_L2", "HS_RA_G3_L2",
|
||||
"HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1",
|
||||
"HS_RB_G1_L2", "HS_RB_G2_L2", "HS_RB_G3_L2",
|
||||
"MAX";
|
||||
};
|
||||
|
||||
@@ -76,6 +76,7 @@ obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
|
||||
obj-$(CONFIG_NUBUS) += nubus/
|
||||
obj-y += macintosh/
|
||||
obj-$(CONFIG_IDE) += ide/
|
||||
obj-$(CONFIG_CRYPTO) += crypto/
|
||||
obj-y += scsi/
|
||||
obj-y += nvme/
|
||||
obj-$(CONFIG_ATA) += ata/
|
||||
@@ -134,7 +135,6 @@ obj-$(CONFIG_NEW_LEDS) += leds/
|
||||
obj-$(CONFIG_INFINIBAND) += infiniband/
|
||||
obj-$(CONFIG_SGI_SN) += sn/
|
||||
obj-y += firmware/
|
||||
obj-$(CONFIG_CRYPTO) += crypto/
|
||||
obj-$(CONFIG_SUPERH) += sh/
|
||||
ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
|
||||
obj-y += clocksource/
|
||||
|
||||
@@ -28,6 +28,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
|
||||
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
|
||||
struct devfreq_simple_ondemand_data *data = df->data;
|
||||
unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
|
||||
unsigned long min = (df->min_freq) ? df->min_freq : 0;
|
||||
|
||||
err = devfreq_update_stats(df);
|
||||
if (err)
|
||||
@@ -45,18 +46,30 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
|
||||
dfso_upthreshold < dfso_downdifferential)
|
||||
return -EINVAL;
|
||||
|
||||
/* Assume MAX if it is going to be divided by zero */
|
||||
if (stat->total_time == 0) {
|
||||
*freq = max;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prevent overflow */
|
||||
if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) {
|
||||
stat->busy_time >>= 7;
|
||||
stat->total_time >>= 7;
|
||||
}
|
||||
|
||||
if (data && data->simple_scaling) {
|
||||
if (stat->busy_time * 100 >
|
||||
stat->total_time * dfso_upthreshold)
|
||||
*freq = max;
|
||||
else if (stat->busy_time * 100 <
|
||||
stat->total_time * dfso_downdifferential)
|
||||
*freq = min;
|
||||
else
|
||||
*freq = df->previous_freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assume MAX if it is going to be divided by zero */
|
||||
if (stat->total_time == 0) {
|
||||
*freq = max;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set MAX if it's busy enough */
|
||||
if (stat->busy_time * 100 >
|
||||
stat->total_time * dfso_upthreshold) {
|
||||
|
||||
@@ -5,7 +5,7 @@ obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
|
||||
obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o
|
||||
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
|
||||
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-20nm.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qrbtc-sdm845.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v4.o
|
||||
obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
|
||||
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2016, 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
|
||||
@@ -77,6 +77,7 @@ struct ufs_qcom_phy_vreg {
|
||||
int min_uV;
|
||||
int max_uV;
|
||||
bool enabled;
|
||||
bool is_always_on;
|
||||
};
|
||||
|
||||
struct ufs_qcom_phy {
|
||||
@@ -90,11 +91,16 @@ struct ufs_qcom_phy {
|
||||
struct clk *ref_clk_src;
|
||||
struct clk *ref_clk_parent;
|
||||
struct clk *ref_clk;
|
||||
struct clk *ref_aux_clk;
|
||||
bool is_ref_clk_enabled;
|
||||
bool is_dev_ref_clk_enabled;
|
||||
struct ufs_qcom_phy_vreg vdda_pll;
|
||||
struct ufs_qcom_phy_vreg vdda_phy;
|
||||
struct ufs_qcom_phy_vreg vddp_ref_clk;
|
||||
|
||||
/* Number of lanes available (1 or 2) for Rx/Tx */
|
||||
u32 lanes_per_direction;
|
||||
|
||||
unsigned int quirks;
|
||||
|
||||
/*
|
||||
@@ -106,6 +112,23 @@ struct ufs_qcom_phy {
|
||||
*/
|
||||
#define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0)
|
||||
|
||||
/*
|
||||
* On some UFS PHY HW revisions, UFS PHY power up calibration sequence
|
||||
* cannot have SVS mode configuration otherwise calibration result
|
||||
* cannot be used in HS-G3. So there are additional register writes must
|
||||
* be done after the PHY is initialized but before the controller
|
||||
* requests hibernate exit.
|
||||
*/
|
||||
#define UFS_QCOM_PHY_QUIRK_SVS_MODE BIT(1)
|
||||
|
||||
/*
|
||||
* On some UFS PHY HW revisions, UFS PHY power up calibration sequence
|
||||
* requires manual VCO tuning code and its better to rely on the VCO
|
||||
* tuning code programmed by boot loader. Enable this quirk to enable
|
||||
* programming the manually tuned VCO code.
|
||||
*/
|
||||
#define UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING BIT(2)
|
||||
|
||||
u8 host_ctrl_rev_major;
|
||||
u16 host_ctrl_rev_minor;
|
||||
u16 host_ctrl_rev_step;
|
||||
@@ -116,6 +139,7 @@ struct ufs_qcom_phy {
|
||||
bool is_powered_on;
|
||||
bool is_started;
|
||||
struct ufs_qcom_phy_specific_ops *phy_spec_ops;
|
||||
u32 vco_tune1_mode1;
|
||||
|
||||
enum phy_mode mode;
|
||||
};
|
||||
@@ -128,14 +152,23 @@ struct ufs_qcom_phy {
|
||||
* @is_physical_coding_sublayer_ready: pointer to a function that
|
||||
* checks pcs readiness. returns 0 for success and non-zero for error.
|
||||
* @set_tx_lane_enable: pointer to a function that enable tx lanes
|
||||
* @ctrl_rx_linecfg: pointer to a function that controls the Host Rx LineCfg
|
||||
* state.
|
||||
* @power_control: pointer to a function that controls analog rail of 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);
|
||||
void (*start_serdes)(struct ufs_qcom_phy *phy);
|
||||
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
|
||||
void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
|
||||
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);
|
||||
@@ -153,4 +186,9 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A,
|
||||
struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B,
|
||||
bool is_rate_B);
|
||||
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
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2015, 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-14nm.h"
|
||||
|
||||
#define UFS_PHY_NAME "ufs_phy_qmp_14nm"
|
||||
#define UFS_PHY_VDDA_PHY_UV (925000)
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
bool is_rate_B)
|
||||
{
|
||||
int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
|
||||
int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
|
||||
int err;
|
||||
|
||||
err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A,
|
||||
tbl_size_A, phy_cal_table_rate_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);
|
||||
return err;
|
||||
}
|
||||
|
||||
static
|
||||
void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
|
||||
{
|
||||
phy_common->quirks =
|
||||
UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
bool is_rate_B = false;
|
||||
int ret;
|
||||
|
||||
if (phy_common->mode == PHY_MODE_UFS_HS_B)
|
||||
is_rate_B = true;
|
||||
|
||||
ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B);
|
||||
if (!ret)
|
||||
/* phy calibrated, but yet to be started */
|
||||
phy_common->is_started = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
phy_common->mode = PHY_MODE_INVALID;
|
||||
|
||||
if (mode > 0)
|
||||
phy_common->mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val)
|
||||
{
|
||||
writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
|
||||
/*
|
||||
* Before any transactions involving PHY, ensure PHY knows
|
||||
* that it's analog rail is powered ON (or OFF).
|
||||
*/
|
||||
mb();
|
||||
}
|
||||
|
||||
static inline
|
||||
void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
|
||||
{
|
||||
/*
|
||||
* 14nm PHY does not have TX_LANE_ENABLE register.
|
||||
* Implement this function so as not to propagate error to caller.
|
||||
*/
|
||||
}
|
||||
|
||||
static inline void ufs_qcom_phy_qmp_14nm_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_14nm_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 const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
|
||||
.init = ufs_qcom_phy_qmp_14nm_init,
|
||||
.exit = ufs_qcom_phy_qmp_14nm_exit,
|
||||
.power_on = ufs_qcom_phy_power_on,
|
||||
.power_off = ufs_qcom_phy_power_off,
|
||||
.set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
|
||||
.start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
|
||||
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
|
||||
.set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
|
||||
.power_control = ufs_qcom_phy_qmp_14nm_power_control,
|
||||
};
|
||||
|
||||
static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *generic_phy;
|
||||
struct ufs_qcom_phy_qmp_14nm *phy;
|
||||
struct ufs_qcom_phy *phy_common;
|
||||
int err = 0;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
phy_common = &phy->common_cfg;
|
||||
|
||||
generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common,
|
||||
&ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops);
|
||||
|
||||
if (!generic_phy) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ufs_qcom_phy_init_clks(phy_common);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ufs_qcom_phy_init_vregulators(phy_common);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV;
|
||||
phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV;
|
||||
|
||||
ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common);
|
||||
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
|
||||
strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name));
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = {
|
||||
{.compatible = "qcom,ufs-phy-qmp-14nm"},
|
||||
{.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match);
|
||||
|
||||
static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = {
|
||||
.probe = ufs_qcom_phy_qmp_14nm_probe,
|
||||
.driver = {
|
||||
.of_match_table = ufs_qcom_phy_qmp_14nm_of_match,
|
||||
.name = "ufs_qcom_phy_qmp_14nm",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ufs_qcom_phy_qmp_14nm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2015, 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_14NM_H_
|
||||
#define UFS_QCOM_PHY_QMP_14NM_H_
|
||||
|
||||
#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)
|
||||
|
||||
/* UFS PHY QSERDES COM registers */
|
||||
#define QSERDES_COM_BG_TIMER COM_OFF(0x0C)
|
||||
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34)
|
||||
#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C)
|
||||
#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_CP_CTRL_MODE0 COM_OFF(0x78)
|
||||
#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C)
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84)
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88)
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90)
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94)
|
||||
#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC)
|
||||
#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4)
|
||||
#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_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_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_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_TIMER1 COM_OFF(0x144)
|
||||
#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148)
|
||||
#define QSERDES_COM_CLK_SELECT COM_OFF(0x174)
|
||||
#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178)
|
||||
#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184)
|
||||
#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C)
|
||||
#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194)
|
||||
#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C)
|
||||
#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC)
|
||||
|
||||
/* UFS PHY registers */
|
||||
#define UFS_PHY_PHY_START PHY_OFF(0x00)
|
||||
#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04)
|
||||
#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168)
|
||||
|
||||
/* UFS PHY TX registers */
|
||||
#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68)
|
||||
#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94)
|
||||
|
||||
/* UFS PHY RX registers */
|
||||
#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40)
|
||||
#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90)
|
||||
#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4)
|
||||
#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8)
|
||||
#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC)
|
||||
#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0)
|
||||
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8)
|
||||
#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114)
|
||||
#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118)
|
||||
#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C)
|
||||
#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C)
|
||||
|
||||
/*
|
||||
* This structure represents the 14nm 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_14nm {
|
||||
struct ufs_qcom_phy common_cfg;
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
|
||||
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, 0xd7),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06),
|
||||
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, 0x05),
|
||||
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, 0x10),
|
||||
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, 0x14),
|
||||
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, 0x02),
|
||||
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18),
|
||||
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, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54),
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2015, 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-20nm.h"
|
||||
|
||||
#define UFS_PHY_NAME "ufs_phy_qmp_20nm"
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
bool is_rate_B)
|
||||
{
|
||||
struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
|
||||
int tbl_size_A, tbl_size_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;
|
||||
int err;
|
||||
|
||||
if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) {
|
||||
tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0);
|
||||
tbl_A = phy_cal_table_rate_A_1_2_0;
|
||||
} else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) {
|
||||
tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0);
|
||||
tbl_A = phy_cal_table_rate_A_1_3_0;
|
||||
} else {
|
||||
dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n",
|
||||
__func__);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
|
||||
tbl_B = phy_cal_table_rate_B;
|
||||
|
||||
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
|
||||
void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
|
||||
{
|
||||
phy_common->quirks =
|
||||
UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
bool is_rate_B = false;
|
||||
int ret;
|
||||
|
||||
if (phy_common->mode == PHY_MODE_UFS_HS_B)
|
||||
is_rate_B = true;
|
||||
|
||||
ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B);
|
||||
if (!ret)
|
||||
/* phy calibrated, but yet to be started */
|
||||
phy_common->is_started = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
phy_common->mode = PHY_MODE_INVALID;
|
||||
|
||||
if (mode > 0)
|
||||
phy_common->mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val)
|
||||
{
|
||||
bool hibern8_exit_after_pwr_collapse = phy->quirks &
|
||||
UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
|
||||
|
||||
if (val) {
|
||||
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();
|
||||
|
||||
if (hibern8_exit_after_pwr_collapse) {
|
||||
/*
|
||||
* Give atleast 1us delay after restoring PHY analog
|
||||
* power.
|
||||
*/
|
||||
usleep_range(1, 2);
|
||||
writel_relaxed(0x0A, phy->mmio +
|
||||
QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
|
||||
writel_relaxed(0x08, phy->mmio +
|
||||
QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
|
||||
/*
|
||||
* Make sure workaround is deactivated before proceeding
|
||||
* with normal PHY operations.
|
||||
*/
|
||||
mb();
|
||||
}
|
||||
} else {
|
||||
if (hibern8_exit_after_pwr_collapse) {
|
||||
writel_relaxed(0x0A, phy->mmio +
|
||||
QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
|
||||
writel_relaxed(0x02, phy->mmio +
|
||||
QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
|
||||
/*
|
||||
* Make sure that above workaround is activated before
|
||||
* PHY analog power collapse.
|
||||
*/
|
||||
mb();
|
||||
}
|
||||
|
||||
writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
|
||||
/*
|
||||
* ensure that PHY knows its PHY analog rail is going
|
||||
* to be powered down
|
||||
*/
|
||||
mb();
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
|
||||
{
|
||||
writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK,
|
||||
phy->mmio + UFS_PHY_TX_LANE_ENABLE);
|
||||
mb();
|
||||
}
|
||||
|
||||
static inline void ufs_qcom_phy_qmp_20nm_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);
|
||||
mb();
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_20nm_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 const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
|
||||
.init = ufs_qcom_phy_qmp_20nm_init,
|
||||
.exit = ufs_qcom_phy_qmp_20nm_exit,
|
||||
.power_on = ufs_qcom_phy_power_on,
|
||||
.power_off = ufs_qcom_phy_power_off,
|
||||
.set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
|
||||
.start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
|
||||
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
|
||||
.set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
|
||||
.power_control = ufs_qcom_phy_qmp_20nm_power_control,
|
||||
};
|
||||
|
||||
static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *generic_phy;
|
||||
struct ufs_qcom_phy_qmp_20nm *phy;
|
||||
struct ufs_qcom_phy *phy_common;
|
||||
int err = 0;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
phy_common = &phy->common_cfg;
|
||||
|
||||
generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common,
|
||||
&ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops);
|
||||
|
||||
if (!generic_phy) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ufs_qcom_phy_init_clks(phy_common);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ufs_qcom_phy_init_vregulators(phy_common);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common);
|
||||
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
|
||||
strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name));
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = {
|
||||
{.compatible = "qcom,ufs-phy-qmp-20nm"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match);
|
||||
|
||||
static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = {
|
||||
.probe = ufs_qcom_phy_qmp_20nm_probe,
|
||||
.driver = {
|
||||
.of_match_table = ufs_qcom_phy_qmp_20nm_of_match,
|
||||
.name = "ufs_qcom_phy_qmp_20nm",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ufs_qcom_phy_qmp_20nm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -1,235 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2015, 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_20NM_H_
|
||||
#define UFS_QCOM_PHY_QMP_20NM_H_
|
||||
|
||||
#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)
|
||||
|
||||
/* UFS PHY PLL block registers */
|
||||
#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0)
|
||||
#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04)
|
||||
#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14)
|
||||
#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24)
|
||||
#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28)
|
||||
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30)
|
||||
#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34)
|
||||
#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38)
|
||||
#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C)
|
||||
#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48)
|
||||
#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C)
|
||||
#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50)
|
||||
#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90)
|
||||
#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94)
|
||||
#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98)
|
||||
#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C)
|
||||
#define QSERDES_COM_BGTC COM_OFF(0xA0)
|
||||
#define QSERDES_COM_DEC_START1 COM_OFF(0xAC)
|
||||
#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0)
|
||||
#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8)
|
||||
#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC)
|
||||
#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100)
|
||||
#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104)
|
||||
#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108)
|
||||
#define QSERDES_COM_DEC_START2 COM_OFF(0x10C)
|
||||
#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110)
|
||||
#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114)
|
||||
#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118)
|
||||
|
||||
/* TX LANE n (0, 1) registers */
|
||||
#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08)
|
||||
#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C)
|
||||
#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54)
|
||||
|
||||
/* RX LANE n (0, 1) registers */
|
||||
#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0)
|
||||
#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8)
|
||||
#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8)
|
||||
#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC)
|
||||
#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0)
|
||||
#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4)
|
||||
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC)
|
||||
#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC)
|
||||
#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100)
|
||||
|
||||
/* UFS PHY registers */
|
||||
#define UFS_PHY_PHY_START PHY_OFF(0x00)
|
||||
#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4)
|
||||
#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44)
|
||||
#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08)
|
||||
#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C)
|
||||
#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10)
|
||||
#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14)
|
||||
#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34)
|
||||
#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38)
|
||||
#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C)
|
||||
#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40)
|
||||
#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68)
|
||||
#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28)
|
||||
#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C)
|
||||
#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48)
|
||||
#define UFS_PHY_PLL_CNTL PHY_OFF(0x50)
|
||||
#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54)
|
||||
#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C)
|
||||
#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58)
|
||||
#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60)
|
||||
#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64)
|
||||
#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C)
|
||||
#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4)
|
||||
#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0)
|
||||
#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8)
|
||||
#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4)
|
||||
#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC)
|
||||
#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8)
|
||||
#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC)
|
||||
#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100)
|
||||
#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c)
|
||||
#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160)
|
||||
#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7)
|
||||
#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6)
|
||||
#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5)
|
||||
#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4)
|
||||
#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3)
|
||||
#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2)
|
||||
#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1)
|
||||
#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0)
|
||||
#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164)
|
||||
#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168)
|
||||
#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C)
|
||||
#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170)
|
||||
#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174)
|
||||
|
||||
#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3
|
||||
|
||||
/*
|
||||
* This structure represents the 20nm 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_20nm {
|
||||
struct ufs_qcom_phy common_cfg;
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e),
|
||||
};
|
||||
|
||||
#endif
|
||||
239
drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.c
Normal file
239
drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.c
Normal file
@@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2018, 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-v4.h"
|
||||
|
||||
#define UFS_PHY_NAME "ufs_phy_qmp_v4"
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_v4_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
bool is_rate_B)
|
||||
{
|
||||
writel_relaxed(0x01, ufs_qcom_phy->mmio + UFS_PHY_SW_RESET);
|
||||
/* Ensure PHY is in reset before writing PHY calibration data */
|
||||
wmb();
|
||||
/*
|
||||
* Writing PHY calibration in this order:
|
||||
* 1. Write Rate-A calibration first (1-lane mode).
|
||||
* 2. Write 2nd lane configuration if needed.
|
||||
* 3. Write Rate-B calibration overrides
|
||||
*/
|
||||
ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_rate_A,
|
||||
ARRAY_SIZE(phy_cal_table_rate_A));
|
||||
if (ufs_qcom_phy->lanes_per_direction == 2)
|
||||
ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_2nd_lane,
|
||||
ARRAY_SIZE(phy_cal_table_2nd_lane));
|
||||
if (is_rate_B)
|
||||
ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_rate_B,
|
||||
ARRAY_SIZE(phy_cal_table_rate_B));
|
||||
|
||||
writel_relaxed(0x00, ufs_qcom_phy->mmio + UFS_PHY_SW_RESET);
|
||||
/* flush buffered writes */
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_v4_init(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy_qmp_v4 *phy = phy_get_drvdata(generic_phy);
|
||||
struct ufs_qcom_phy *phy_common = &phy->common_cfg;
|
||||
int err;
|
||||
|
||||
err = ufs_qcom_phy_init_clks(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(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 int ufs_qcom_phy_qmp_v4_exit(struct phy *generic_phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void ufs_qcom_phy_qmp_v4_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_v4_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
|
||||
{
|
||||
/*
|
||||
* v4 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_v4_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_v4_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_v4_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);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ufs_qcom_phy_qmp_v4_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_v4_phy_ops = {
|
||||
.init = ufs_qcom_phy_qmp_v4_init,
|
||||
.exit = ufs_qcom_phy_qmp_v4_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_v4_ops = {
|
||||
.calibrate_phy = ufs_qcom_phy_qmp_v4_phy_calibrate,
|
||||
.start_serdes = ufs_qcom_phy_qmp_v4_start_serdes,
|
||||
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_v4_is_pcs_ready,
|
||||
.set_tx_lane_enable = ufs_qcom_phy_qmp_v4_set_tx_lane_enable,
|
||||
.ctrl_rx_linecfg = ufs_qcom_phy_qmp_v4_ctrl_rx_linecfg,
|
||||
.power_control = ufs_qcom_phy_qmp_v4_power_control,
|
||||
.dbg_register_dump = ufs_qcom_phy_qmp_v4_dbg_register_dump,
|
||||
};
|
||||
|
||||
static int ufs_qcom_phy_qmp_v4_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *generic_phy;
|
||||
struct ufs_qcom_phy_qmp_v4 *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_v4_phy_ops, &phy_v4_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 const struct of_device_id ufs_qcom_phy_qmp_v4_of_match[] = {
|
||||
{.compatible = "qcom,ufs-phy-qmp-v4"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v4_of_match);
|
||||
|
||||
static struct platform_driver ufs_qcom_phy_qmp_v4_driver = {
|
||||
.probe = ufs_qcom_phy_qmp_v4_probe,
|
||||
.driver = {
|
||||
.of_match_table = ufs_qcom_phy_qmp_v4_of_match,
|
||||
.name = "ufs_qcom_phy_qmp_v4",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ufs_qcom_phy_qmp_v4_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v4");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
315
drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.h
Normal file
315
drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.h
Normal file
@@ -0,0 +1,315 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2018, 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_V4_H_
|
||||
#define UFS_QCOM_PHY_QMP_V4_H_
|
||||
|
||||
#include "phy-qcom-ufs-i.h"
|
||||
|
||||
/* QCOM UFS PHY control registers */
|
||||
#define COM_BASE 0x000
|
||||
#define COM_SIZE 0x1C0
|
||||
#define PHY_BASE 0xC00
|
||||
#define PHY_SIZE 0x200
|
||||
#define TX_BASE(n) (0x400 + (0x400 * n))
|
||||
#define TX_SIZE 0x16C
|
||||
#define RX_BASE(n) (0x600 + (0x400 * n))
|
||||
#define RX_SIZE 0x200
|
||||
#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_SYSCLK_EN_SEL COM_OFF(0x94)
|
||||
#define QSERDES_COM_HSCLK_SEL COM_OFF(0x158)
|
||||
#define QSERDES_COM_HSCLK_HS_SWITCH_SEL COM_OFF(0x15C)
|
||||
#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xA4)
|
||||
#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x10C)
|
||||
#define QSERDES_COM_PLL_IVCO COM_OFF(0x58)
|
||||
#define QSERDES_COM_VCO_TUNE_INITVAL2 COM_OFF(0x124)
|
||||
#define QSERDES_COM_BIN_VCOCAL_HSCLK_SEL COM_OFF(0x1BC)
|
||||
#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xBC)
|
||||
#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x74)
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x7C)
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x84)
|
||||
#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0xAC)
|
||||
#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0xB0)
|
||||
#define QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0 COM_OFF(0x1AC)
|
||||
#define QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0 COM_OFF(0x1B0)
|
||||
#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xC4)
|
||||
#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x78)
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x80)
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x88)
|
||||
#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0xB4)
|
||||
#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0xB8)
|
||||
#define QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE1 COM_OFF(0x1B4)
|
||||
#define QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE1 COM_OFF(0x1B8)
|
||||
#define QSERDES_COM_CMN_IPTRIM COM_OFF(0x60)
|
||||
|
||||
/* UFS PHY registers */
|
||||
#define UFS_PHY_PHY_START PHY_OFF(0x00)
|
||||
#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04)
|
||||
#define UFS_PHY_SW_RESET PHY_OFF(0x08)
|
||||
#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x180)
|
||||
#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x148)
|
||||
#define UFS_PHY_MULTI_LANE_CTRL1 PHY_OFF(0x1E0)
|
||||
#define UFS_PHY_RX_SIGDET_CTRL2 PHY_OFF(0x158)
|
||||
#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x30)
|
||||
#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x38)
|
||||
#define UFS_PHY_TX_MID_TERM_CTRL1 PHY_OFF(0x1D8)
|
||||
#define UFS_PHY_DEBUG_BUS_CLKSEL PHY_OFF(0x124)
|
||||
#define UFS_PHY_PLL_CNTL PHY_OFF(0x2C)
|
||||
#define UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB PHY_OFF(0x0C)
|
||||
#define UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB PHY_OFF(0x10)
|
||||
#define UFS_PHY_TX_PWM_GEAR_BAND PHY_OFF(0x160)
|
||||
#define UFS_PHY_TX_HS_GEAR_BAND PHY_OFF(0x168)
|
||||
#define UFS_PHY_TX_HSGEAR_CAPABILITY PHY_OFF(0x74)
|
||||
#define UFS_PHY_RX_HSGEAR_CAPABILITY PHY_OFF(0xB4)
|
||||
#define UFS_PHY_RX_MIN_HIBERN8_TIME PHY_OFF(0x150)
|
||||
|
||||
/* UFS PHY TX registers */
|
||||
#define QSERDES_TX0_PWM_GEAR_1_DIVIDER_BAND0_1 TX_OFF(0, 0xD8)
|
||||
#define QSERDES_TX0_PWM_GEAR_2_DIVIDER_BAND0_1 TX_OFF(0, 0xDC)
|
||||
#define QSERDES_TX0_PWM_GEAR_3_DIVIDER_BAND0_1 TX_OFF(0, 0xE0)
|
||||
#define QSERDES_TX0_PWM_GEAR_4_DIVIDER_BAND0_1 TX_OFF(0, 0xE4)
|
||||
#define QSERDES_TX0_LANE_MODE_1 TX_OFF(0, 0x84)
|
||||
#define QSERDES_TX0_TRAN_DRVR_EMP_EN TX_OFF(0, 0xB8)
|
||||
|
||||
#define QSERDES_TX1_PWM_GEAR_1_DIVIDER_BAND0_1 TX_OFF(1, 0xD8)
|
||||
#define QSERDES_TX1_PWM_GEAR_2_DIVIDER_BAND0_1 TX_OFF(1, 0xDC)
|
||||
#define QSERDES_TX1_PWM_GEAR_3_DIVIDER_BAND0_1 TX_OFF(1, 0xE0)
|
||||
#define QSERDES_TX1_PWM_GEAR_4_DIVIDER_BAND0_1 TX_OFF(1, 0xE4)
|
||||
#define QSERDES_TX1_LANE_MODE_1 TX_OFF(1, 0x84)
|
||||
#define QSERDES_TX1_TRAN_DRVR_EMP_EN TX_OFF(1, 0xB8)
|
||||
|
||||
/* UFS PHY RX registers */
|
||||
#define QSERDES_RX0_SIGDET_LVL RX_OFF(0, 0x120)
|
||||
#define QSERDES_RX0_SIGDET_CNTRL RX_OFF(0, 0x11C)
|
||||
#define QSERDES_RX0_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x124)
|
||||
#define QSERDES_RX0_RX_BAND RX_OFF(0, 0x128)
|
||||
#define QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x30)
|
||||
#define QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE RX_OFF(0, 0x34)
|
||||
#define QSERDES_RX0_UCDR_PI_CONTROLS RX_OFF(0, 0x44)
|
||||
#define QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW RX_OFF(0, 0x3C)
|
||||
#define QSERDES_RX0_UCDR_PI_CTRL2 RX_OFF(0, 0x48)
|
||||
#define QSERDES_RX0_RX_TERM_BW RX_OFF(0, 0x80)
|
||||
#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xEC)
|
||||
#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3 RX_OFF(0, 0xF0)
|
||||
#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4 RX_OFF(0, 0xF4)
|
||||
#define QSERDES_RX0_RX_OFFSET_ADAPTOR_CNTRL2 RX_OFF(0, 0x114)
|
||||
#define QSERDES_RX0_RX_IDAC_MEASURE_TIME RX_OFF(0, 0x100)
|
||||
#define QSERDES_RX0_RX_IDAC_TSETTLE_LOW RX_OFF(0, 0xF8)
|
||||
#define QSERDES_RX0_RX_IDAC_TSETTLE_HIGH RX_OFF(0, 0xFC)
|
||||
#define QSERDES_RX0_RX_MODE_00_LOW RX_OFF(0, 0x170)
|
||||
#define QSERDES_RX0_RX_MODE_00_HIGH RX_OFF(0, 0x174)
|
||||
#define QSERDES_RX0_RX_MODE_00_HIGH2 RX_OFF(0, 0x178)
|
||||
#define QSERDES_RX0_RX_MODE_00_HIGH3 RX_OFF(0, 0x17C)
|
||||
#define QSERDES_RX0_RX_MODE_00_HIGH4 RX_OFF(0, 0x180)
|
||||
#define QSERDES_RX0_RX_MODE_01_LOW RX_OFF(0, 0x184)
|
||||
#define QSERDES_RX0_RX_MODE_01_HIGH RX_OFF(0, 0x188)
|
||||
#define QSERDES_RX0_RX_MODE_01_HIGH2 RX_OFF(0, 0x18C)
|
||||
#define QSERDES_RX0_RX_MODE_01_HIGH3 RX_OFF(0, 0x190)
|
||||
#define QSERDES_RX0_RX_MODE_01_HIGH4 RX_OFF(0, 0x194)
|
||||
#define QSERDES_RX0_RX_MODE_10_LOW RX_OFF(0, 0x198)
|
||||
#define QSERDES_RX0_RX_MODE_10_HIGH RX_OFF(0, 0x19C)
|
||||
#define QSERDES_RX0_RX_MODE_10_HIGH2 RX_OFF(0, 0x1A0)
|
||||
#define QSERDES_RX0_RX_MODE_10_HIGH3 RX_OFF(0, 0x1A4)
|
||||
#define QSERDES_RX0_RX_MODE_10_HIGH4 RX_OFF(0, 0x1A8)
|
||||
#define QSERDES_RX0_AC_JTAG_ENABLE RX_OFF(0, 0x68)
|
||||
#define QSERDES_RX0_UCDR_FO_GAIN RX_OFF(0, 0x08)
|
||||
#define QSERDES_RX0_UCDR_SO_GAIN RX_OFF(0, 0x14)
|
||||
|
||||
#define QSERDES_RX1_SIGDET_LVL RX_OFF(1, 0x120)
|
||||
#define QSERDES_RX1_SIGDET_CNTRL RX_OFF(1, 0x11C)
|
||||
#define QSERDES_RX1_SIGDET_DEGLITCH_CNTRL RX_OFF(1, 0x124)
|
||||
#define QSERDES_RX1_RX_BAND RX_OFF(1, 0x128)
|
||||
#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_PI_CONTROLS RX_OFF(1, 0x44)
|
||||
#define QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW RX_OFF(1, 0x3C)
|
||||
#define QSERDES_RX1_UCDR_PI_CTRL2 RX_OFF(1, 0x48)
|
||||
#define QSERDES_RX1_RX_TERM_BW RX_OFF(1, 0x80)
|
||||
#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(1, 0xEC)
|
||||
#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3 RX_OFF(1, 0xF0)
|
||||
#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4 RX_OFF(1, 0xF4)
|
||||
#define QSERDES_RX1_RX_OFFSET_ADAPTOR_CNTRL2 RX_OFF(1, 0x114)
|
||||
#define QSERDES_RX1_RX_IDAC_MEASURE_TIME RX_OFF(1, 0x100)
|
||||
#define QSERDES_RX1_RX_IDAC_TSETTLE_LOW RX_OFF(1, 0xF8)
|
||||
#define QSERDES_RX1_RX_IDAC_TSETTLE_HIGH RX_OFF(1, 0xFC)
|
||||
#define QSERDES_RX1_RX_MODE_00_LOW RX_OFF(1, 0x170)
|
||||
#define QSERDES_RX1_RX_MODE_00_HIGH RX_OFF(1, 0x174)
|
||||
#define QSERDES_RX1_RX_MODE_00_HIGH2 RX_OFF(1, 0x178)
|
||||
#define QSERDES_RX1_RX_MODE_00_HIGH3 RX_OFF(1, 0x17C)
|
||||
#define QSERDES_RX1_RX_MODE_00_HIGH4 RX_OFF(1, 0x180)
|
||||
#define QSERDES_RX1_RX_MODE_01_LOW RX_OFF(1, 0x184)
|
||||
#define QSERDES_RX1_RX_MODE_01_HIGH RX_OFF(1, 0x188)
|
||||
#define QSERDES_RX1_RX_MODE_01_HIGH2 RX_OFF(1, 0x18C)
|
||||
#define QSERDES_RX1_RX_MODE_01_HIGH3 RX_OFF(1, 0x190)
|
||||
#define QSERDES_RX1_RX_MODE_01_HIGH4 RX_OFF(1, 0x194)
|
||||
#define QSERDES_RX1_RX_MODE_10_LOW RX_OFF(1, 0x198)
|
||||
#define QSERDES_RX1_RX_MODE_10_HIGH RX_OFF(1, 0x19C)
|
||||
#define QSERDES_RX1_RX_MODE_10_HIGH2 RX_OFF(1, 0x1A0)
|
||||
#define QSERDES_RX1_RX_MODE_10_HIGH3 RX_OFF(1, 0x1A4)
|
||||
#define QSERDES_RX1_RX_MODE_10_HIGH4 RX_OFF(1, 0x1A8)
|
||||
#define QSERDES_RX1_AC_JTAG_ENABLE RX_OFF(1, 0x68)
|
||||
#define QSERDES_RX1_UCDR_FO_GAIN RX_OFF(1, 0x08)
|
||||
#define QSERDES_RX1_UCDR_SO_GAIN RX_OFF(1, 0x14)
|
||||
|
||||
#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1)
|
||||
|
||||
/*
|
||||
* This structure represents the v4 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_v4 {
|
||||
struct ufs_qcom_phy common_cfg;
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xD9),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x11),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_HS_SWITCH_SEL, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
|
||||
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_LOCK_CMP1_MODE0, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0C),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xAC),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1E),
|
||||
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_LOCK_CMP1_MODE1, 0x32),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xDD),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_LANE_MODE_1, 0x05),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_TRAN_DRVR_EMP_EN, 0x0C),
|
||||
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_BAND, 0x18),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN, 0x0A),
|
||||
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_RX0_UCDR_PI_CTRL2, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_TERM_BW, 0x1B),
|
||||
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_RX_OFFSET_ADAPTOR_CNTRL2, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_IDAC_MEASURE_TIME, 0x10),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_IDAC_TSETTLE_LOW, 0xC0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_IDAC_TSETTLE_HIGH, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_LOW, 0x36),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH, 0x36),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH2, 0xF6),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH3, 0x3B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH4, 0x3D),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_LOW, 0xE0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH2, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH3, 0x3B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH4, 0xB1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_LOW, 0xE0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH2, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH3, 0x3B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH4, 0xB1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6F),
|
||||
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_TX_MID_TERM_CTRL1, 0x43),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_DEBUG_BUS_CLKSEL, 0x1F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PLL_CNTL, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x16),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0xD8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_PWM_GEAR_BAND, 0xAA),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HS_GEAR_BAND, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HSGEAR_CAPABILITY, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HSGEAR_CAPABILITY, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_AC_JTAG_ENABLE, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FO_GAIN, 0x0C),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_GAIN, 0x04),
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_2nd_lane[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x05),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_TRAN_DRVR_EMP_EN, 0x0C),
|
||||
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_BAND, 0x18),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN, 0x0A),
|
||||
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(QSERDES_RX1_UCDR_PI_CTRL2, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_TERM_BW, 0x1B),
|
||||
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_RX_OFFSET_ADAPTOR_CNTRL2, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_IDAC_MEASURE_TIME, 0x10),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_IDAC_TSETTLE_LOW, 0xC0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_IDAC_TSETTLE_HIGH, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_LOW, 0x36),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH, 0x36),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH2, 0xF6),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH3, 0x3B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH4, 0x3D),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_LOW, 0xE0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH2, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH3, 0x3B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH4, 0xB1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_LOW, 0xE0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH2, 0xC8),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH3, 0x3B),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH4, 0xB1),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_AC_JTAG_ENABLE, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FO_GAIN, 0x0C),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_GAIN, 0x04),
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x06),
|
||||
};
|
||||
|
||||
#endif
|
||||
158
drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c
Normal file
158
drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.c
Normal file
@@ -0,0 +1,158 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016, 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-qrbtc-sdm845.h"
|
||||
|
||||
#define UFS_PHY_NAME "ufs_phy_qrbtc_sdm845"
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qrbtc_sdm845_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;
|
||||
|
||||
tbl_A = phy_cal_table_rate_A;
|
||||
tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
|
||||
|
||||
tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
|
||||
tbl_B = phy_cal_table_rate_B;
|
||||
|
||||
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);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_qcom_phy_qrbtc_sdm845_is_pcs_ready(struct ufs_qcom_phy *phy_common)
|
||||
{
|
||||
int err = 0;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* The value we are polling for is 0x3D which represents the
|
||||
* following masks:
|
||||
* RESET_SM field: 0x5
|
||||
* RESTRIMDONE bit: BIT(3)
|
||||
* PLLLOCK bit: BIT(4)
|
||||
* READY bit: BIT(5)
|
||||
*/
|
||||
#define QSERDES_COM_RESET_SM_REG_POLL_VAL 0x3D
|
||||
err = readl_poll_timeout(phy_common->mmio + QSERDES_COM_RESET_SM,
|
||||
val, (val == QSERDES_COM_RESET_SM_REG_POLL_VAL), 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_qrbtc_sdm845_start_serdes(struct ufs_qcom_phy *phy)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
writel_relaxed(0x01, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
|
||||
|
||||
temp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
|
||||
temp |= 0x1;
|
||||
writel_relaxed(temp, phy->mmio + UFS_PHY_PHY_START);
|
||||
|
||||
/* Ensure register value is committed */
|
||||
mb();
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qrbtc_sdm845_init(struct phy *generic_phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qrbtc_sdm845_exit(struct phy *generic_phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct phy_ops ufs_qcom_phy_qrbtc_sdm845_phy_ops = {
|
||||
.init = ufs_qcom_phy_qrbtc_sdm845_init,
|
||||
.exit = ufs_qcom_phy_qrbtc_sdm845_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct ufs_qcom_phy_specific_ops phy_qrbtc_sdm845_ops = {
|
||||
.calibrate_phy = ufs_qcom_phy_qrbtc_sdm845_phy_calibrate,
|
||||
.start_serdes = ufs_qcom_phy_qrbtc_sdm845_start_serdes,
|
||||
.is_physical_coding_sublayer_ready =
|
||||
ufs_qcom_phy_qrbtc_sdm845_is_pcs_ready,
|
||||
};
|
||||
|
||||
static int ufs_qcom_phy_qrbtc_sdm845_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *generic_phy;
|
||||
struct ufs_qcom_phy_qrbtc_sdm845 *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_qrbtc_sdm845_phy_ops, &phy_qrbtc_sdm845_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 const struct of_device_id ufs_qcom_phy_qrbtc_sdm845_of_match[] = {
|
||||
{.compatible = "qcom,ufs-phy-qrbtc-sdm845"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qrbtc_sdm845_of_match);
|
||||
|
||||
static struct platform_driver ufs_qcom_phy_qrbtc_sdm845_driver = {
|
||||
.probe = ufs_qcom_phy_qrbtc_sdm845_probe,
|
||||
.driver = {
|
||||
.of_match_table = ufs_qcom_phy_qrbtc_sdm845_of_match,
|
||||
.name = "ufs_qcom_phy_qrbtc_sdm845",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ufs_qcom_phy_qrbtc_sdm845_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QRBTC SDM845");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
181
drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.h
Normal file
181
drivers/phy/qualcomm/phy-qcom-ufs-qrbtc-sdm845.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2016, 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_QRBTC_SDM845_H_
|
||||
#define UFS_QCOM_PHY_QRBTC_SDM845_H_
|
||||
|
||||
#include "phy-qcom-ufs-i.h"
|
||||
|
||||
/* QCOM UFS PHY control registers */
|
||||
#define COM_OFF(x) (0x000 + x)
|
||||
#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
|
||||
#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
|
||||
#define PHY_OFF(x) (0xC00 + x)
|
||||
#define PHY_USR(x) (x)
|
||||
|
||||
/* UFS PHY PLL block registers */
|
||||
#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x00)
|
||||
#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04)
|
||||
#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14)
|
||||
#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x18)
|
||||
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x20)
|
||||
#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x24)
|
||||
#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x28)
|
||||
#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x2C)
|
||||
#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0x38)
|
||||
#define QSERDES_COM_RES_CODE_TXBAND COM_OFF(0x3C)
|
||||
#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x40)
|
||||
#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x44)
|
||||
#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x48)
|
||||
#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x4C)
|
||||
#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x50)
|
||||
#define QSERDES_COM_DEC_START1 COM_OFF(0x64)
|
||||
#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x98)
|
||||
#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x9C)
|
||||
#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0xA0)
|
||||
#define QSERDES_COM_DEC_START2 COM_OFF(0xA4)
|
||||
#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0xA8)
|
||||
#define QSERDES_COM_PLL_CRCTRL COM_OFF(0xAC)
|
||||
#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0xB0)
|
||||
#define QSERDES_COM_RESET_SM COM_OFF(0xBC)
|
||||
|
||||
/* TX LANE n (0, 1) registers */
|
||||
#define QSERDES_TX_CLKBUF_ENABLE(n) TX_OFF(n, 0x4)
|
||||
|
||||
/* RX LANE n (0, 1) registers */
|
||||
#define QSERDES_RX_CDR_CONTROL(n) RX_OFF(n, 0x0)
|
||||
#define QSERDES_RX_RX_IQ_RXDET_EN(n) RX_OFF(n, 0x28)
|
||||
#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x34)
|
||||
#define QSERDES_RX_RX_BAND(n) RX_OFF(n, 0x38)
|
||||
#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x98)
|
||||
#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0x9C)
|
||||
#define QSERDES_RX_PWM_CNTRL1(n) RX_OFF(n, 0x80)
|
||||
#define QSERDES_RX_PWM_CNTRL2(n) RX_OFF(n, 0x84)
|
||||
#define QSERDES_RX_PWM_NDIV(n) RX_OFF(n, 0x88)
|
||||
#define QSERDES_RX_SIGDET_CNTRL2(n) RX_OFF(n, 0x8C)
|
||||
#define QSERDES_RX_UFS_CNTRL(n) RX_OFF(n, 0x90)
|
||||
|
||||
/* UFS PHY registers */
|
||||
#define UFS_PHY_PHY_START PHY_OFF(0x00)
|
||||
#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04)
|
||||
#define UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB PHY_OFF(0x08)
|
||||
#define UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB PHY_OFF(0x0C)
|
||||
#define UFS_PHY_RX_SYM_RESYNC_CTRL PHY_OFF(0x134)
|
||||
#define UFS_PHY_MULTI_LANE_CTRL1 PHY_OFF(0x1C4)
|
||||
|
||||
/* QRBTC V2 USER REGISTERS */
|
||||
#define U11_UFS_RESET_REG_OFFSET PHY_USR(0x4)
|
||||
#define U11_QRBTC_CONTROL_OFFSET PHY_USR(0x18)
|
||||
#define U11_QRBTC_TX_CLK_CTRL PHY_USR(0x20)
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PHY_START, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0x00),
|
||||
|
||||
/* QSERDES Common */
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x16),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_TXBAND, 0xC0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x24),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x10),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x13),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0x43),
|
||||
|
||||
/* QSERDES TX */
|
||||
/* Enable large amplitude setting */
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(0), 0x29),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(1), 0x29),
|
||||
|
||||
/* QSERDES RX0 */
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(0), 0x08),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL2(0), 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_NDIV(0), 0x30),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(0), 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0C),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(0), 0xC0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(0), 0x07),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_BAND(0), 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(0), 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(0), 0xF3),
|
||||
|
||||
/* QSERDES RX1 */
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(1), 0x08),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL2(1), 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_NDIV(1), 0x30),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(1), 0x40),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0C),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(1), 0xC0),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(1), 0x07),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_BAND(1), 0x06),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(1), 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(1), 0xF3),
|
||||
|
||||
/* QSERDES PLL Settings - Series A */
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x10),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xFF),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x07),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x07),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x01),
|
||||
|
||||
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02),
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
|
||||
/* QSERDES PLL Settings - Series B */
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x10),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1E),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x07),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x0F),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x07),
|
||||
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x01),
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This structure represents the qrbtc-sdm845 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_qrbtc_sdm845 {
|
||||
struct ufs_qcom_phy common_cfg;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2016, 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
|
||||
@@ -15,20 +15,33 @@
|
||||
#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_PLL_MIN_UV 1800000
|
||||
#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
|
||||
#define VDDP_REF_CLK_MAX_UV 1200000
|
||||
|
||||
#define UFS_PHY_DEFAULT_LANES_PER_DIRECTION 1
|
||||
|
||||
void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
struct ufs_qcom_phy_calibration *tbl,
|
||||
int tbl_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tbl_size; i++)
|
||||
writel_relaxed(tbl[i].cfg_value,
|
||||
ufs_qcom_phy->mmio + tbl[i].reg_offset);
|
||||
}
|
||||
EXPORT_SYMBOL(ufs_qcom_phy_write_tbl);
|
||||
|
||||
int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
struct ufs_qcom_phy_calibration *tbl_A,
|
||||
int tbl_size_A,
|
||||
struct ufs_qcom_phy_calibration *tbl_B,
|
||||
int tbl_size_B, bool is_rate_B)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
if (!tbl_A) {
|
||||
@@ -37,9 +50,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < tbl_size_A; i++)
|
||||
writel_relaxed(tbl_A[i].cfg_value,
|
||||
ufs_qcom_phy->mmio + tbl_A[i].reg_offset);
|
||||
ufs_qcom_phy_write_tbl(ufs_qcom_phy, tbl_A, tbl_size_A);
|
||||
|
||||
/*
|
||||
* In case we would like to work in rate B, we need
|
||||
@@ -55,9 +66,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < tbl_size_B; i++)
|
||||
writel_relaxed(tbl_B[i].cfg_value,
|
||||
ufs_qcom_phy->mmio + tbl_B[i].reg_offset);
|
||||
ufs_qcom_phy_write_tbl(ufs_qcom_phy, tbl_B, tbl_size_B);
|
||||
}
|
||||
|
||||
/* flush buffered writes */
|
||||
@@ -98,13 +107,6 @@ int ufs_qcom_phy_base_init(struct platform_device *pdev,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* "dev_ref_clk_ctrl_mem" is optional resource */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"dev_ref_clk_ctrl_mem");
|
||||
phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio))
|
||||
phy_common->dev_ref_clk_ctrl_mmio = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -139,6 +141,19 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "lanes-per-direction",
|
||||
&common_cfg->lanes_per_direction))
|
||||
common_cfg->lanes_per_direction =
|
||||
UFS_PHY_DEFAULT_LANES_PER_DIRECTION;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
@@ -179,15 +194,19 @@ int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common)
|
||||
"qcom,msm8996-ufs-phy-qmp-14nm"))
|
||||
goto skip_txrx_clk;
|
||||
|
||||
err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk",
|
||||
&phy_common->tx_iface_clk);
|
||||
if (err)
|
||||
goto out;
|
||||
/*
|
||||
* tx_iface_clk does not exist in newer version of ufs-phy HW,
|
||||
* so don't return error if it is not found
|
||||
*/
|
||||
__ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk",
|
||||
&phy_common->tx_iface_clk, false);
|
||||
|
||||
err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk",
|
||||
&phy_common->rx_iface_clk);
|
||||
if (err)
|
||||
goto out;
|
||||
/*
|
||||
* rx_iface_clk does not exist in newer version of ufs-phy HW,
|
||||
* so don't return error if it is not found
|
||||
*/
|
||||
__ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk",
|
||||
&phy_common->rx_iface_clk, false);
|
||||
|
||||
skip_txrx_clk:
|
||||
err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src",
|
||||
@@ -204,7 +223,15 @@ int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common)
|
||||
|
||||
err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk",
|
||||
&phy_common->ref_clk);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* "ref_aux_clk" is optional and only supported by certain
|
||||
* phy versions, don't abort init if it's not found.
|
||||
*/
|
||||
__ufs_qcom_phy_clk_get(phy_common->dev, "ref_aux_clk",
|
||||
&phy_common->ref_aux_clk, false);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -218,6 +245,14 @@ static int ufs_qcom_phy_init_vreg(struct device *dev,
|
||||
|
||||
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 -ENODATA;
|
||||
}
|
||||
}
|
||||
|
||||
vreg->name = name;
|
||||
vreg->reg = devm_regulator_get(dev, name);
|
||||
if (IS_ERR(vreg->reg)) {
|
||||
@@ -242,6 +277,9 @@ static int ufs_qcom_phy_init_vreg(struct device *dev,
|
||||
}
|
||||
err = 0;
|
||||
}
|
||||
snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name);
|
||||
vreg->is_always_on = of_property_read_bool(dev->of_node,
|
||||
prop_name);
|
||||
}
|
||||
|
||||
if (!strcmp(name, "vdda-pll")) {
|
||||
@@ -270,11 +308,10 @@ int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common)
|
||||
|
||||
err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy,
|
||||
"vdda-phy");
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk,
|
||||
ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk,
|
||||
"vddp-ref-clk");
|
||||
|
||||
out:
|
||||
@@ -291,6 +328,8 @@ static int ufs_qcom_phy_cfg_vreg(struct device *dev,
|
||||
int min_uV;
|
||||
int uA_load;
|
||||
|
||||
WARN_ON(!vreg);
|
||||
|
||||
if (regulator_count_voltages(reg) > 0) {
|
||||
min_uV = on ? vreg->min_uV : 0;
|
||||
ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
|
||||
@@ -382,9 +421,26 @@ static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy)
|
||||
goto out_disable_parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ref_aux_clk" is optional clock and only supported by certain
|
||||
* phy versions, hence make sure that clk reference is available
|
||||
* before trying to enable the clock.
|
||||
*/
|
||||
if (phy->ref_aux_clk) {
|
||||
ret = clk_prepare_enable(phy->ref_aux_clk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "%s: ref_aux_clk enable failed %d\n",
|
||||
__func__, ret);
|
||||
goto out_disable_ref;
|
||||
}
|
||||
}
|
||||
|
||||
phy->is_ref_clk_enabled = true;
|
||||
goto out;
|
||||
|
||||
out_disable_ref:
|
||||
if (phy->ref_clk)
|
||||
clk_disable_unprepare(phy->ref_clk);
|
||||
out_disable_parent:
|
||||
if (phy->ref_clk_parent)
|
||||
clk_disable_unprepare(phy->ref_clk_parent);
|
||||
@@ -399,7 +455,7 @@ static int ufs_qcom_phy_disable_vreg(struct device *dev,
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!vreg || !vreg->enabled)
|
||||
if (!vreg || !vreg->enabled || vreg->is_always_on)
|
||||
goto out;
|
||||
|
||||
ret = regulator_disable(vreg->reg);
|
||||
@@ -419,6 +475,13 @@ static int ufs_qcom_phy_disable_vreg(struct device *dev,
|
||||
static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy)
|
||||
{
|
||||
if (phy->is_ref_clk_enabled) {
|
||||
/*
|
||||
* "ref_aux_clk" is optional clock and only supported by
|
||||
* certain phy versions, hence make sure that clk reference
|
||||
* is available before trying to disable the clock.
|
||||
*/
|
||||
if (phy->ref_aux_clk)
|
||||
clk_disable_unprepare(phy->ref_aux_clk);
|
||||
clk_disable_unprepare(phy->ref_clk);
|
||||
/*
|
||||
* "ref_clk_parent" is optional clock hence make sure that clk
|
||||
@@ -489,6 +552,9 @@ static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy)
|
||||
if (phy->is_iface_clk_enabled)
|
||||
goto out;
|
||||
|
||||
if (!phy->tx_iface_clk)
|
||||
goto out;
|
||||
|
||||
ret = clk_prepare_enable(phy->tx_iface_clk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n",
|
||||
@@ -511,6 +577,9 @@ static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy)
|
||||
/* Turn OFF M-PHY RMMI interface clocks */
|
||||
void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy)
|
||||
{
|
||||
if (!phy->tx_iface_clk)
|
||||
return;
|
||||
|
||||
if (phy->is_iface_clk_enabled) {
|
||||
clk_disable_unprepare(phy->tx_iface_clk);
|
||||
clk_disable_unprepare(phy->rx_iface_clk);
|
||||
@@ -518,8 +587,9 @@ void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy)
|
||||
}
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy)
|
||||
int ufs_qcom_phy_start_serdes(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
int ret = 0;
|
||||
|
||||
if (!ufs_qcom_phy->phy_spec_ops->start_serdes) {
|
||||
@@ -538,19 +608,24 @@ int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
int ret = 0;
|
||||
|
||||
if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) {
|
||||
dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n",
|
||||
__func__);
|
||||
ret = -ENOTSUPP;
|
||||
} else {
|
||||
if (ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable)
|
||||
ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy,
|
||||
tx_lanes);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable);
|
||||
|
||||
int ufs_qcom_phy_ctrl_rx_linecfg(struct phy *generic_phy, bool ctrl)
|
||||
{
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
if (ufs_qcom_phy->phy_spec_ops->ctrl_rx_linecfg)
|
||||
ufs_qcom_phy->phy_spec_ops->ctrl_rx_linecfg(ufs_qcom_phy, ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
|
||||
u8 major, u16 minor, u16 step)
|
||||
{
|
||||
@@ -562,8 +637,39 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version);
|
||||
|
||||
static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy)
|
||||
int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B)
|
||||
{
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
int ret = 0;
|
||||
|
||||
if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) {
|
||||
dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n",
|
||||
__func__);
|
||||
ret = -ENOTSUPP;
|
||||
} else {
|
||||
ret = ufs_qcom_phy->phy_spec_ops->calibrate_phy(ufs_qcom_phy,
|
||||
is_rate_B);
|
||||
if (ret)
|
||||
dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy);
|
||||
|
||||
const char *ufs_qcom_phy_name(struct phy *phy)
|
||||
{
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
|
||||
|
||||
return ufs_qcom_phy->name;
|
||||
}
|
||||
EXPORT_SYMBOL(ufs_qcom_phy_name);
|
||||
|
||||
int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) {
|
||||
dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n",
|
||||
__func__);
|
||||
@@ -583,18 +689,6 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
|
||||
if (phy_common->is_powered_on)
|
||||
return 0;
|
||||
|
||||
if (!phy_common->is_started) {
|
||||
err = ufs_qcom_phy_start_serdes(phy_common);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ufs_qcom_phy_is_pcs_ready(phy_common);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
phy_common->is_started = true;
|
||||
}
|
||||
|
||||
err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
|
||||
if (err) {
|
||||
dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
|
||||
@@ -676,6 +770,42 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
|
||||
|
||||
int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable)
|
||||
{
|
||||
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||
int ret = 0;
|
||||
|
||||
if (ufs_qcom_phy->phy_spec_ops->configure_lpm) {
|
||||
ret = ufs_qcom_phy->phy_spec_ops->configure_lpm(ufs_qcom_phy,
|
||||
enable);
|
||||
if (ret)
|
||||
dev_err(ufs_qcom_phy->dev,
|
||||
"%s: configure_lpm(%s) failed %d\n",
|
||||
__func__, enable ? "enable" : "disable", ret);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
MODULE_AUTHOR("Yaniv Gardi <ygardi@codeaurora.org>");
|
||||
MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
|
||||
MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY");
|
||||
|
||||
@@ -279,7 +279,11 @@ int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
|
||||
rq->cmd_len = COMMAND_SIZE(cmd[0]);
|
||||
memcpy(rq->cmd, cmd, rq->cmd_len);
|
||||
rq->retries = retries;
|
||||
if (likely(!sdev->timeout_override))
|
||||
req->timeout = timeout;
|
||||
else
|
||||
req->timeout = sdev->timeout_override;
|
||||
|
||||
req->cmd_flags |= flags;
|
||||
req->rq_flags |= rq_flags | RQF_QUIET;
|
||||
|
||||
@@ -2452,6 +2456,33 @@ void scsi_unblock_requests(struct Scsi_Host *shost)
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_unblock_requests);
|
||||
|
||||
/*
|
||||
* Function: scsi_set_cmd_timeout_override()
|
||||
*
|
||||
* Purpose: Utility function used by low-level drivers to override
|
||||
timeout for the scsi commands.
|
||||
*
|
||||
* Arguments: sdev - scsi device in question
|
||||
* timeout - timeout in jiffies
|
||||
*
|
||||
* Returns: Nothing
|
||||
*
|
||||
* Lock status: No locks are assumed held.
|
||||
*
|
||||
* Notes: Some platforms might be very slow and command completion may
|
||||
* take much longer than default scsi command timeouts.
|
||||
* SCSI Read/Write command timeout can be changed by
|
||||
* blk_queue_rq_timeout() but there is no option to override
|
||||
* timeout for rest of the scsi commands. This function would
|
||||
* would allow this.
|
||||
*/
|
||||
void scsi_set_cmd_timeout_override(struct scsi_device *sdev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
sdev->timeout_override = timeout;
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_set_cmd_timeout_override);
|
||||
|
||||
int __init scsi_init_queue(void)
|
||||
{
|
||||
scsi_sdb_cache = kmem_cache_create("scsi_data_buffer",
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#include "scsi_priv.h"
|
||||
|
||||
static int do_scsi_runtime_resume(struct device *dev,
|
||||
const struct dev_pm_ops *pm);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
|
||||
@@ -77,10 +80,22 @@ static int scsi_dev_type_resume(struct device *dev,
|
||||
scsi_device_resume(to_scsi_device(dev));
|
||||
dev_dbg(dev, "scsi resume: %d\n", err);
|
||||
|
||||
if (err == 0) {
|
||||
if (err == 0 && (cb != do_scsi_runtime_resume)) {
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
err = pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
if (!err && scsi_is_sdev_device(dev)) {
|
||||
struct scsi_device *sdev = to_scsi_device(dev);
|
||||
|
||||
/*
|
||||
* If scsi device runtime PM is managed by block layer
|
||||
* then we should update request queue's runtime status
|
||||
* as well.
|
||||
*/
|
||||
if (sdev->request_queue->dev)
|
||||
blk_post_runtime_resume(sdev->request_queue, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -139,16 +154,6 @@ static int scsi_bus_resume_common(struct device *dev,
|
||||
else
|
||||
fn = NULL;
|
||||
|
||||
/*
|
||||
* Forcibly set runtime PM status of request queue to "active" to
|
||||
* make sure we can again get requests from the queue (see also
|
||||
* blk_pm_peek_request()).
|
||||
*
|
||||
* The resume hook will correct runtime PM status of the disk.
|
||||
*/
|
||||
if (scsi_is_sdev_device(dev) && pm_runtime_suspended(dev))
|
||||
blk_set_runtime_active(to_scsi_device(dev)->request_queue);
|
||||
|
||||
if (fn) {
|
||||
async_schedule_domain(fn, dev, &scsi_sd_pm_domain);
|
||||
|
||||
@@ -223,12 +228,32 @@ static int scsi_bus_restore(struct device *dev)
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int do_scsi_runtime_suspend(struct device *dev,
|
||||
const struct dev_pm_ops *pm)
|
||||
{
|
||||
return pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
|
||||
}
|
||||
|
||||
static int do_scsi_runtime_resume(struct device *dev,
|
||||
const struct dev_pm_ops *pm)
|
||||
{
|
||||
return pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
|
||||
}
|
||||
|
||||
static int sdev_runtime_suspend(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
struct scsi_device *sdev = to_scsi_device(dev);
|
||||
int err = 0;
|
||||
|
||||
if (!sdev->request_queue->dev) {
|
||||
err = scsi_dev_type_suspend(dev, do_scsi_runtime_suspend);
|
||||
if (err == -EAGAIN)
|
||||
pm_schedule_suspend(dev, jiffies_to_msecs(
|
||||
round_jiffies_up_relative(HZ/10)));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = blk_pre_runtime_suspend(sdev->request_queue);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -258,6 +283,9 @@ static int sdev_runtime_resume(struct device *dev)
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
int err = 0;
|
||||
|
||||
if (!sdev->request_queue->dev)
|
||||
return scsi_dev_type_resume(dev, do_scsi_runtime_resume);
|
||||
|
||||
blk_pre_runtime_resume(sdev->request_queue);
|
||||
if (pm && pm->runtime_resume)
|
||||
err = pm->runtime_resume(dev);
|
||||
|
||||
@@ -826,15 +826,10 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
|
||||
* well-known logical units. Force well-known type
|
||||
* to enumerate them correctly.
|
||||
*/
|
||||
if (scsi_is_wlun(sdev->lun) && sdev->type != TYPE_WLUN) {
|
||||
sdev_printk(KERN_WARNING, sdev,
|
||||
"%s: correcting incorrect peripheral device type 0x%x for W-LUN 0x%16xhN\n",
|
||||
__func__, sdev->type, (unsigned int)sdev->lun);
|
||||
if (scsi_is_wlun(sdev->lun) && sdev->type != TYPE_WLUN)
|
||||
sdev->type = TYPE_WLUN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (sdev->type == TYPE_RBC || sdev->type == TYPE_ROM) {
|
||||
/* RBC and MMC devices can return SCSI-3 compliance and yet
|
||||
* still not support REPORT LUNS, so make them act as
|
||||
@@ -969,6 +964,10 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
|
||||
|
||||
transport_configure_device(&sdev->sdev_gendev);
|
||||
|
||||
/* The LLD can override auto suspend tunables in ->slave_configure() */
|
||||
sdev->use_rpm_auto = 0;
|
||||
sdev->autosuspend_delay = SCSI_DEFAULT_AUTOSUSPEND_DELAY;
|
||||
|
||||
if (sdev->host->hostt->slave_configure) {
|
||||
ret = sdev->host->hostt->slave_configure(sdev);
|
||||
if (ret) {
|
||||
|
||||
@@ -1282,6 +1282,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
|
||||
device_enable_async_suspend(&sdev->sdev_gendev);
|
||||
scsi_autopm_get_target(starget);
|
||||
pm_runtime_set_active(&sdev->sdev_gendev);
|
||||
if (!sdev->use_rpm_auto)
|
||||
pm_runtime_forbid(&sdev->sdev_gendev);
|
||||
pm_runtime_enable(&sdev->sdev_gendev);
|
||||
scsi_autopm_put_target(starget);
|
||||
|
||||
@@ -957,7 +957,10 @@ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd)
|
||||
sector >>= ilog2(sdp->sector_size) - 9;
|
||||
nr_sectors >>= ilog2(sdp->sector_size) - 9;
|
||||
|
||||
if (likely(!sdp->timeout_override))
|
||||
rq->timeout = SD_WRITE_SAME_TIMEOUT;
|
||||
else
|
||||
rq->timeout = sdp->timeout_override;
|
||||
|
||||
if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff) {
|
||||
cmd->cmd_len = 16;
|
||||
@@ -1525,83 +1528,6 @@ static int media_not_present(struct scsi_disk *sdkp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sd_check_events - check media events
|
||||
* @disk: kernel device descriptor
|
||||
* @clearing: disk events currently being cleared
|
||||
*
|
||||
* Returns mask of DISK_EVENT_*.
|
||||
*
|
||||
* Note: this function is invoked from the block subsystem.
|
||||
**/
|
||||
static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
|
||||
{
|
||||
struct scsi_disk *sdkp = scsi_disk_get(disk);
|
||||
struct scsi_device *sdp;
|
||||
int retval;
|
||||
|
||||
if (!sdkp)
|
||||
return 0;
|
||||
|
||||
sdp = sdkp->device;
|
||||
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_check_events\n"));
|
||||
|
||||
/*
|
||||
* If the device is offline, don't send any commands - just pretend as
|
||||
* if the command failed. If the device ever comes back online, we
|
||||
* can deal with it then. It is only because of unrecoverable errors
|
||||
* that we would ever take a device offline in the first place.
|
||||
*/
|
||||
if (!scsi_device_online(sdp)) {
|
||||
set_media_not_present(sdkp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Using TEST_UNIT_READY enables differentiation between drive with
|
||||
* no cartridge loaded - NOT READY, drive with changed cartridge -
|
||||
* UNIT ATTENTION, or with same cartridge - GOOD STATUS.
|
||||
*
|
||||
* Drives that auto spin down. eg iomega jaz 1G, will be started
|
||||
* by sd_spinup_disk() from sd_revalidate_disk(), which happens whenever
|
||||
* sd_revalidate() is called.
|
||||
*/
|
||||
if (scsi_block_when_processing_errors(sdp)) {
|
||||
struct scsi_sense_hdr sshdr = { 0, };
|
||||
|
||||
retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
|
||||
&sshdr);
|
||||
|
||||
/* failed to execute TUR, assume media not present */
|
||||
if (host_byte(retval)) {
|
||||
set_media_not_present(sdkp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (media_not_present(sdkp, &sshdr))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* For removable scsi disk we have to recognise the presence
|
||||
* of a disk in the drive.
|
||||
*/
|
||||
if (!sdkp->media_present)
|
||||
sdp->changed = 1;
|
||||
sdkp->media_present = 1;
|
||||
out:
|
||||
/*
|
||||
* sdp->changed is set under the following conditions:
|
||||
*
|
||||
* Medium present state has changed in either direction.
|
||||
* Device has indicated UNIT_ATTENTION.
|
||||
*/
|
||||
retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0;
|
||||
sdp->changed = 0;
|
||||
scsi_disk_put(sdkp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sd_sync_cache(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr)
|
||||
{
|
||||
int retries, res;
|
||||
@@ -1797,7 +1723,6 @@ static const struct block_device_operations sd_fops = {
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = sd_compat_ioctl,
|
||||
#endif
|
||||
.check_events = sd_check_events,
|
||||
.revalidate_disk = sd_revalidate_disk,
|
||||
.unlock_native_capacity = sd_unlock_native_capacity,
|
||||
.pr_ops = &sd_pr_ops,
|
||||
@@ -2564,11 +2489,6 @@ sd_print_capacity(struct scsi_disk *sdkp,
|
||||
sizeof(cap_str_10));
|
||||
|
||||
if (sdkp->first_scan || old_capacity != sdkp->capacity) {
|
||||
sd_printk(KERN_NOTICE, sdkp,
|
||||
"%llu %d-byte logical blocks: (%s/%s)\n",
|
||||
(unsigned long long)sdkp->capacity,
|
||||
sector_size, cap_str_10, cap_str_2);
|
||||
|
||||
if (sdkp->physical_block_size != sector_size)
|
||||
sd_printk(KERN_NOTICE, sdkp,
|
||||
"%u-byte physical blocks\n",
|
||||
@@ -2658,16 +2578,13 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
|
||||
{
|
||||
int len = 0, res;
|
||||
struct scsi_device *sdp = sdkp->device;
|
||||
struct Scsi_Host *host = sdp->host;
|
||||
|
||||
int dbd;
|
||||
int modepage;
|
||||
int first_len;
|
||||
struct scsi_mode_data data;
|
||||
struct scsi_sense_hdr sshdr;
|
||||
int old_wce = sdkp->WCE;
|
||||
int old_rcd = sdkp->RCD;
|
||||
int old_dpofua = sdkp->DPOFUA;
|
||||
|
||||
|
||||
if (sdkp->cache_override)
|
||||
return;
|
||||
@@ -2689,6 +2606,9 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
|
||||
dbd = 8;
|
||||
} else {
|
||||
modepage = 8;
|
||||
if (host->set_dbd_for_caching)
|
||||
dbd = 8;
|
||||
else
|
||||
dbd = 0;
|
||||
}
|
||||
|
||||
@@ -2790,15 +2710,6 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
|
||||
if (sdkp->WCE && sdkp->write_prot)
|
||||
sdkp->WCE = 0;
|
||||
|
||||
if (sdkp->first_scan || old_wce != sdkp->WCE ||
|
||||
old_rcd != sdkp->RCD || old_dpofua != sdkp->DPOFUA)
|
||||
sd_printk(KERN_NOTICE, sdkp,
|
||||
"Write cache: %s, read cache: %s, %s\n",
|
||||
sdkp->WCE ? "enabled" : "disabled",
|
||||
sdkp->RCD ? "disabled" : "enabled",
|
||||
sdkp->DPOFUA ? "supports DPO and FUA"
|
||||
: "doesn't support DPO or FUA");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3132,10 +3043,10 @@ static int sd_revalidate_disk(struct gendisk *disk)
|
||||
if (sdkp->opt_xfer_blocks &&
|
||||
sdkp->opt_xfer_blocks <= dev_max &&
|
||||
sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS &&
|
||||
logical_to_bytes(sdp, sdkp->opt_xfer_blocks) >= PAGE_SIZE) {
|
||||
q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
|
||||
rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks);
|
||||
} else
|
||||
sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_SIZE)
|
||||
rw_max = q->limits.io_opt =
|
||||
sdkp->opt_xfer_blocks * sdp->sector_size;
|
||||
else
|
||||
rw_max = min_not_zero(logical_to_sectors(sdp, dev_max),
|
||||
(sector_t)BLK_DEF_MAX_SECTORS);
|
||||
|
||||
@@ -3271,6 +3182,9 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
|
||||
}
|
||||
|
||||
blk_pm_runtime_init(sdp->request_queue, dev);
|
||||
if (sdp->autosuspend_delay >= 0)
|
||||
pm_runtime_set_autosuspend_delay(dev, sdp->autosuspend_delay);
|
||||
|
||||
device_add_disk(dev, gd);
|
||||
if (sdkp->capacity)
|
||||
sd_dif_config_host(sdkp);
|
||||
@@ -3283,8 +3197,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
|
||||
sd_printk(KERN_NOTICE, sdkp, "supports TCG Opal\n");
|
||||
}
|
||||
|
||||
sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
|
||||
sdp->removable ? "removable " : "");
|
||||
scsi_autopm_put_device(sdp);
|
||||
put_device(&sdkp->dev);
|
||||
}
|
||||
@@ -3529,7 +3441,6 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
|
||||
return 0;
|
||||
|
||||
if (sdkp->WCE && sdkp->media_present) {
|
||||
sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
|
||||
ret = sd_sync_cache(sdkp, &sshdr);
|
||||
|
||||
if (ret) {
|
||||
@@ -3551,7 +3462,7 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
|
||||
}
|
||||
|
||||
if (sdkp->device->manage_start_stop) {
|
||||
sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
|
||||
sd_printk(KERN_DEBUG, sdkp, "Stopping disk\n");
|
||||
/* an error is not worth aborting a system sleep */
|
||||
ret = sd_start_stop_device(sdkp, 0);
|
||||
if (ignore_stop_errors)
|
||||
@@ -3582,7 +3493,7 @@ static int sd_resume(struct device *dev)
|
||||
if (!sdkp->device->manage_start_stop)
|
||||
return 0;
|
||||
|
||||
sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
|
||||
sd_printk(KERN_DEBUG, sdkp, "Starting disk\n");
|
||||
ret = sd_start_stop_device(sdkp, 1);
|
||||
if (!ret)
|
||||
opal_unlock_from_suspend(sdkp->opal_dev);
|
||||
|
||||
@@ -838,7 +838,11 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp,
|
||||
else
|
||||
at_head = 1;
|
||||
|
||||
if (likely(!sdp->device->timeout_override))
|
||||
srp->rq->timeout = timeout;
|
||||
else
|
||||
srp->rq->timeout = sdp->device->timeout_override;
|
||||
|
||||
kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */
|
||||
blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk,
|
||||
srp->rq, at_head, sg_rq_end_io);
|
||||
@@ -1555,9 +1559,6 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf)
|
||||
} else
|
||||
pr_warn("%s: sg_sys Invalid\n", __func__);
|
||||
|
||||
sdev_printk(KERN_NOTICE, scsidp, "Attached scsi generic sg%d "
|
||||
"type %d\n", sdp->index, scsidp->type);
|
||||
|
||||
dev_set_drvdata(cl_dev, sdp);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -101,6 +101,40 @@ config SCSI_UFS_QCOM
|
||||
Select this if you have UFS controller on QCOM chipset.
|
||||
If unsure, say N.
|
||||
|
||||
config SCSI_UFS_QCOM_ICE
|
||||
bool "QCOM specific hooks to Inline Crypto Engine for UFS driver"
|
||||
depends on SCSI_UFS_QCOM && CRYPTO_DEV_QCOM_ICE
|
||||
help
|
||||
This selects the QCOM specific additions to support Inline Crypto
|
||||
Engine (ICE).
|
||||
ICE accelerates the crypto operations and maintains the high UFS
|
||||
performance.
|
||||
|
||||
Select this if you have ICE supported for UFS on QCOM chipset.
|
||||
If unsure, say N.
|
||||
|
||||
config SCSI_UFS_TEST
|
||||
tristate "Universal Flash Storage host controller driver unit-tests"
|
||||
depends on SCSI_UFSHCD && IOSCHED_TEST
|
||||
default m
|
||||
help
|
||||
This adds UFS Host controller unit-test framework.
|
||||
The UFS unit-tests register as a block device test utility to
|
||||
the test-iosched and will be initiated when the test-iosched will
|
||||
be chosen to be the active I/O scheduler.
|
||||
|
||||
config SCSI_UFSHCD_CMD_LOGGING
|
||||
bool "Universal Flash Storage host controller driver layer command logging support"
|
||||
depends on SCSI_UFSHCD
|
||||
help
|
||||
This selects the UFS host controller driver layer command logging.
|
||||
UFS host controller driver layer command logging records all the
|
||||
command information sent from UFS host controller for debugging
|
||||
purpose.
|
||||
|
||||
Select this if you want above mentioned debug information captured.
|
||||
If unsure, say N.
|
||||
|
||||
config SCSI_UFS_HISI
|
||||
tristate "Hisilicon specific hooks to UFS controller platform driver"
|
||||
depends on (ARCH_HISI || COMPILE_TEST) && SCSI_UFSHCD_PLATFORM
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
|
||||
obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
|
||||
obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
|
||||
obj-$(CONFIG_SCSI_UFS_QCOM_ICE) += ufs-qcom-ice.o
|
||||
obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o
|
||||
ufshcd-core-objs := ufshcd.o ufs-sysfs.o
|
||||
obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
|
||||
obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
|
||||
obj-$(CONFIG_SCSI_UFS_TEST) += ufs_test.o
|
||||
obj-$(CONFIG_DEBUG_FS) += ufs-debugfs.o ufs-qcom-debugfs.o
|
||||
obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o
|
||||
|
||||
1739
drivers/scsi/ufs/ufs-debugfs.c
Normal file
1739
drivers/scsi/ufs/ufs-debugfs.c
Normal file
File diff suppressed because it is too large
Load Diff
76
drivers/scsi/ufs/ufs-debugfs.h
Normal file
76
drivers/scsi/ufs/ufs-debugfs.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2013-2016, 2018, 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.
|
||||
*
|
||||
* UFS debugfs - add debugfs interface to the ufshcd.
|
||||
* This is currently used for statistics collection and exporting from the
|
||||
* UFS driver.
|
||||
* This infrastructure can be used for debugging or direct tweaking
|
||||
* of the driver from userspace.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _UFS_DEBUGFS_H
|
||||
#define _UFS_DEBUGFS_H
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include "ufshcd.h"
|
||||
|
||||
enum ufsdbg_err_inject_scenario {
|
||||
ERR_INJECT_INTR,
|
||||
ERR_INJECT_PWR_CHANGE,
|
||||
ERR_INJECT_UIC,
|
||||
ERR_INJECT_DME_ATTR,
|
||||
ERR_INJECT_QUERY,
|
||||
ERR_INJECT_HIBERN8_ENTER,
|
||||
ERR_INJECT_HIBERN8_EXIT,
|
||||
ERR_INJECT_MAX_ERR_SCENARIOS,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void ufsdbg_add_debugfs(struct ufs_hba *hba);
|
||||
void ufsdbg_remove_debugfs(struct ufs_hba *hba);
|
||||
void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset, int num_regs,
|
||||
char *str, void *priv);
|
||||
void ufsdbg_set_err_state(struct ufs_hba *hba);
|
||||
void ufsdbg_clr_err_state(struct ufs_hba *hba);
|
||||
#else
|
||||
static inline void ufsdbg_add_debugfs(struct ufs_hba *hba)
|
||||
{
|
||||
}
|
||||
static inline void ufsdbg_remove_debugfs(struct ufs_hba *hba)
|
||||
{
|
||||
}
|
||||
static inline void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset,
|
||||
int num_regs, char *str, void *priv)
|
||||
{
|
||||
}
|
||||
void ufsdbg_set_err_state(struct ufs_hba *hba)
|
||||
{
|
||||
}
|
||||
void ufsdbg_clr_err_state(struct ufs_hba *hba)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UFS_FAULT_INJECTION
|
||||
void ufsdbg_error_inject_dispatcher(struct ufs_hba *hba,
|
||||
enum ufsdbg_err_inject_scenario err_scenario,
|
||||
int success_value, int *ret_value);
|
||||
#else
|
||||
static inline void ufsdbg_error_inject_dispatcher(struct ufs_hba *hba,
|
||||
enum ufsdbg_err_inject_scenario err_scenario,
|
||||
int success_value, int *ret_value)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* End of Header */
|
||||
391
drivers/scsi/ufs/ufs-qcom-debugfs.c
Normal file
391
drivers/scsi/ufs/ufs-qcom-debugfs.c
Normal file
@@ -0,0 +1,391 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2015, 2017-2018, 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 <linux/debugfs.h>
|
||||
#include "ufs-qcom.h"
|
||||
#include "ufs-qcom-debugfs.h"
|
||||
#include "ufs-debugfs.h"
|
||||
|
||||
#define TESTBUS_CFG_BUFF_LINE_SIZE sizeof("0xXY, 0xXY")
|
||||
|
||||
static void ufs_qcom_dbg_remove_debugfs(struct ufs_qcom_host *host);
|
||||
|
||||
static int ufs_qcom_dbg_print_en_read(void *data, u64 *attr_val)
|
||||
{
|
||||
struct ufs_qcom_host *host = data;
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
*attr_val = (u64)host->dbg_print_en;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ufs_qcom_dbg_print_en_set(void *data, u64 attr_id)
|
||||
{
|
||||
struct ufs_qcom_host *host = data;
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
if (attr_id & ~UFS_QCOM_DBG_PRINT_ALL)
|
||||
return -EINVAL;
|
||||
|
||||
host->dbg_print_en = (u32)attr_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ufs_qcom_dbg_print_en_ops,
|
||||
ufs_qcom_dbg_print_en_read,
|
||||
ufs_qcom_dbg_print_en_set,
|
||||
"%llu\n");
|
||||
|
||||
static int ufs_qcom_dbg_testbus_en_read(void *data, u64 *attr_val)
|
||||
{
|
||||
struct ufs_qcom_host *host = data;
|
||||
bool enabled;
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
enabled = !!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN);
|
||||
*attr_val = (u64)enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!!attr_id)
|
||||
host->dbg_print_en |= UFS_QCOM_DBG_PRINT_TEST_BUS_EN;
|
||||
else
|
||||
host->dbg_print_en &= ~UFS_QCOM_DBG_PRINT_TEST_BUS_EN;
|
||||
|
||||
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_DEBUGFS_ATTRIBUTE(ufs_qcom_dbg_testbus_en_ops,
|
||||
ufs_qcom_dbg_testbus_en_read,
|
||||
ufs_qcom_dbg_testbus_en_set,
|
||||
"%llu\n");
|
||||
|
||||
static int ufs_qcom_dbg_testbus_cfg_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct ufs_qcom_host *host = (struct ufs_qcom_host *)file->private;
|
||||
|
||||
seq_printf(file, "Current configuration: major=%d, minor=%d\n\n",
|
||||
host->testbus.select_major, host->testbus.select_minor);
|
||||
|
||||
/* Print usage */
|
||||
seq_puts(file,
|
||||
"To change the test-bus configuration, write 'MAJ,MIN' where:\n"
|
||||
"MAJ - major select\n"
|
||||
"MIN - minor select\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file,
|
||||
const char __user *ubuf, size_t cnt,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct ufs_qcom_host *host = file->f_mapping->host->i_private;
|
||||
char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {'\0'};
|
||||
loff_t buff_pos = 0;
|
||||
char *comma;
|
||||
int ret = 0;
|
||||
int major;
|
||||
int minor;
|
||||
unsigned long flags;
|
||||
struct ufs_hba *hba = host->hba;
|
||||
|
||||
|
||||
ret = simple_write_to_buffer(configuration,
|
||||
TESTBUS_CFG_BUFF_LINE_SIZE - 1,
|
||||
|
||||
&buff_pos, ubuf, cnt);
|
||||
if (ret < 0) {
|
||||
dev_err(host->hba->dev, "%s: failed to read user data\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
configuration[ret] = '\0';
|
||||
|
||||
comma = strnchr(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, ',');
|
||||
if (!comma || comma == configuration) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: error in configuration of testbus\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sscanf(configuration, "%i,%i", &major, &minor) != 2) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: couldn't parse input to 2 numeric values\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ufs_qcom_testbus_cfg_is_ok(host, major, minor)) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
host->testbus.select_major = (u8)major;
|
||||
host->testbus.select_minor = (u8)minor;
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
|
||||
/*
|
||||
* 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",
|
||||
__func__, host->testbus.select_major,
|
||||
host->testbus.select_minor);
|
||||
|
||||
out:
|
||||
return ret ? ret : cnt;
|
||||
}
|
||||
|
||||
static int ufs_qcom_dbg_testbus_cfg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ufs_qcom_dbg_testbus_cfg_show,
|
||||
inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ufs_qcom_dbg_testbus_cfg_desc = {
|
||||
.open = ufs_qcom_dbg_testbus_cfg_open,
|
||||
.read = seq_read,
|
||||
.write = ufs_qcom_dbg_testbus_cfg_write,
|
||||
};
|
||||
|
||||
static int ufs_qcom_dbg_testbus_bus_read(void *data, u64 *attr_val)
|
||||
{
|
||||
struct ufs_qcom_host *host = data;
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
pm_runtime_get_sync(host->hba->dev);
|
||||
ufshcd_hold(host->hba, false);
|
||||
*attr_val = (u64)ufshcd_readl(host->hba, UFS_TEST_BUS);
|
||||
ufshcd_release(host->hba, false);
|
||||
pm_runtime_put_sync(host->hba->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ufs_qcom_dbg_testbus_bus_ops,
|
||||
ufs_qcom_dbg_testbus_bus_read,
|
||||
NULL,
|
||||
"%llu\n");
|
||||
|
||||
static int ufs_qcom_dbg_dbg_regs_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct ufs_qcom_host *host = (struct ufs_qcom_host *)file->private;
|
||||
bool dbg_print_reg = !!(host->dbg_print_en &
|
||||
UFS_QCOM_DBG_PRINT_REGS_EN);
|
||||
|
||||
pm_runtime_get_sync(host->hba->dev);
|
||||
ufshcd_hold(host->hba, false);
|
||||
|
||||
/* Temporarily override the debug print enable */
|
||||
host->dbg_print_en |= UFS_QCOM_DBG_PRINT_REGS_EN;
|
||||
ufs_qcom_print_hw_debug_reg_all(host->hba, file, ufsdbg_pr_buf_to_std);
|
||||
/* Restore previous debug print enable value */
|
||||
if (!dbg_print_reg)
|
||||
host->dbg_print_en &= ~UFS_QCOM_DBG_PRINT_REGS_EN;
|
||||
|
||||
ufshcd_release(host->hba, false);
|
||||
pm_runtime_put_sync(host->hba->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ufs_qcom_dbg_dbg_regs_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
return single_open(file, ufs_qcom_dbg_dbg_regs_show,
|
||||
inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ufs_qcom_dbg_dbg_regs_desc = {
|
||||
.open = ufs_qcom_dbg_dbg_regs_open,
|
||||
.read = seq_read,
|
||||
};
|
||||
|
||||
static int ufs_qcom_dbg_pm_qos_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct ufs_qcom_host *host = (struct ufs_qcom_host *)file->private;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(host->hba->host->host_lock, flags);
|
||||
|
||||
seq_printf(file, "enabled: %d\n", host->pm_qos.is_enabled);
|
||||
for (i = 0; i < host->pm_qos.num_groups && host->pm_qos.groups; i++)
|
||||
seq_printf(file,
|
||||
"CPU Group #%d(mask=0x%lx): active_reqs=%d, state=%d, latency=%d\n",
|
||||
i, host->pm_qos.groups[i].mask.bits[0],
|
||||
host->pm_qos.groups[i].active_reqs,
|
||||
host->pm_qos.groups[i].state,
|
||||
host->pm_qos.groups[i].latency_us);
|
||||
|
||||
spin_unlock_irqrestore(host->hba->host->host_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ufs_qcom_dbg_pm_qos_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
return single_open(file, ufs_qcom_dbg_pm_qos_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ufs_qcom_dbg_pm_qos_desc = {
|
||||
.open = ufs_qcom_dbg_pm_qos_open,
|
||||
.read = seq_read,
|
||||
};
|
||||
|
||||
void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root)
|
||||
{
|
||||
struct ufs_qcom_host *host;
|
||||
|
||||
if (!hba || !hba->priv) {
|
||||
pr_err("%s: NULL host, exiting\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
host = hba->priv;
|
||||
host->debugfs_files.debugfs_root = debugfs_create_dir("qcom", root);
|
||||
if (IS_ERR(host->debugfs_files.debugfs_root))
|
||||
/* Don't complain -- debugfs just isn't enabled */
|
||||
goto err_no_root;
|
||||
if (!host->debugfs_files.debugfs_root) {
|
||||
/*
|
||||
* Complain -- debugfs is enabled, but it failed to
|
||||
* create the directory
|
||||
*/
|
||||
dev_err(host->hba->dev,
|
||||
"%s: NULL debugfs root directory, exiting\n", __func__);
|
||||
goto err_no_root;
|
||||
}
|
||||
|
||||
host->debugfs_files.dbg_print_en =
|
||||
debugfs_create_file("dbg_print_en", 0600,
|
||||
host->debugfs_files.debugfs_root, host,
|
||||
&ufs_qcom_dbg_print_en_ops);
|
||||
if (!host->debugfs_files.dbg_print_en) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed to create dbg_print_en debugfs entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->debugfs_files.testbus = debugfs_create_dir("testbus",
|
||||
host->debugfs_files.debugfs_root);
|
||||
if (!host->debugfs_files.testbus) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed create testbus directory\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->debugfs_files.testbus_en =
|
||||
debugfs_create_file("enable", 0600,
|
||||
host->debugfs_files.testbus, host,
|
||||
&ufs_qcom_dbg_testbus_en_ops);
|
||||
if (!host->debugfs_files.testbus_en) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed create testbus_en debugfs entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->debugfs_files.testbus_cfg =
|
||||
debugfs_create_file("configuration", 0600,
|
||||
host->debugfs_files.testbus, host,
|
||||
&ufs_qcom_dbg_testbus_cfg_desc);
|
||||
if (!host->debugfs_files.testbus_cfg) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed create testbus_cfg debugfs entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->debugfs_files.testbus_bus =
|
||||
debugfs_create_file("TEST_BUS", 0400,
|
||||
host->debugfs_files.testbus, host,
|
||||
&ufs_qcom_dbg_testbus_bus_ops);
|
||||
if (!host->debugfs_files.testbus_bus) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed create testbus_bus debugfs entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->debugfs_files.dbg_regs =
|
||||
debugfs_create_file("debug-regs", 0400,
|
||||
host->debugfs_files.debugfs_root, host,
|
||||
&ufs_qcom_dbg_dbg_regs_desc);
|
||||
if (!host->debugfs_files.dbg_regs) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed create dbg_regs debugfs entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->debugfs_files.pm_qos =
|
||||
debugfs_create_file("pm_qos", 0400,
|
||||
host->debugfs_files.debugfs_root, host,
|
||||
&ufs_qcom_dbg_pm_qos_desc);
|
||||
if (!host->debugfs_files.dbg_regs) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed create dbg_regs debugfs entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
ufs_qcom_dbg_remove_debugfs(host);
|
||||
err_no_root:
|
||||
dev_err(host->hba->dev, "%s: failed to initialize debugfs\n", __func__);
|
||||
}
|
||||
|
||||
static void ufs_qcom_dbg_remove_debugfs(struct ufs_qcom_host *host)
|
||||
{
|
||||
debugfs_remove_recursive(host->debugfs_files.debugfs_root);
|
||||
host->debugfs_files.debugfs_root = NULL;
|
||||
}
|
||||
25
drivers/scsi/ufs/ufs-qcom-debugfs.h
Normal file
25
drivers/scsi/ufs/ufs-qcom-debugfs.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015, 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 QCOM_DEBUGFS_H_
|
||||
#define QCOM_DEBUGFS_H_
|
||||
|
||||
#include "ufshcd.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root);
|
||||
#endif
|
||||
|
||||
#endif /* End of Header */
|
||||
725
drivers/scsi/ufs/ufs-qcom-ice.c
Normal file
725
drivers/scsi/ufs/ufs-qcom-ice.c
Normal file
@@ -0,0 +1,725 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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 <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <crypto/ice.h>
|
||||
|
||||
#include "ufshcd.h"
|
||||
#include "ufs-qcom-ice.h"
|
||||
#include "ufs-qcom-debugfs.h"
|
||||
|
||||
#define UFS_QCOM_CRYPTO_LABEL "ufs-qcom-crypto"
|
||||
/* Timeout waiting for ICE initialization, that requires TZ access */
|
||||
#define UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS 500
|
||||
|
||||
#define UFS_QCOM_ICE_DEFAULT_DBG_PRINT_EN 0
|
||||
|
||||
static struct workqueue_struct *ice_workqueue;
|
||||
|
||||
static void ufs_qcom_ice_dump_regs(struct ufs_qcom_host *qcom_host, int offset,
|
||||
int len, char *prefix)
|
||||
{
|
||||
print_hex_dump(KERN_ERR, prefix,
|
||||
len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE,
|
||||
16, 4, qcom_host->hba->mmio_base + offset, len * 4,
|
||||
false);
|
||||
}
|
||||
|
||||
void ufs_qcom_ice_print_regs(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(qcom_host->dbg_print_en & UFS_QCOM_DBG_PRINT_ICE_REGS_EN))
|
||||
return;
|
||||
|
||||
ufs_qcom_ice_dump_regs(qcom_host, REG_UFS_QCOM_ICE_CFG, 1,
|
||||
"REG_UFS_QCOM_ICE_CFG ");
|
||||
for (i = 0; i < NUM_QCOM_ICE_CTRL_INFO_n_REGS; i++) {
|
||||
pr_err("REG_UFS_QCOM_ICE_CTRL_INFO_1_%d = 0x%08X\n", i,
|
||||
ufshcd_readl(qcom_host->hba,
|
||||
(REG_UFS_QCOM_ICE_CTRL_INFO_1_n + 8 * i)));
|
||||
|
||||
pr_err("REG_UFS_QCOM_ICE_CTRL_INFO_2_%d = 0x%08X\n", i,
|
||||
ufshcd_readl(qcom_host->hba,
|
||||
(REG_UFS_QCOM_ICE_CTRL_INFO_2_n + 8 * i)));
|
||||
}
|
||||
|
||||
if (qcom_host->ice.pdev && qcom_host->ice.vops &&
|
||||
qcom_host->ice.vops->debug)
|
||||
qcom_host->ice.vops->debug(qcom_host->ice.pdev);
|
||||
}
|
||||
|
||||
static void ufs_qcom_ice_error_cb(void *host_ctrl, u32 error)
|
||||
{
|
||||
struct ufs_qcom_host *qcom_host = (struct ufs_qcom_host *)host_ctrl;
|
||||
|
||||
dev_err(qcom_host->hba->dev, "%s: Error in ice operation 0x%x\n",
|
||||
__func__, error);
|
||||
|
||||
if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_ACTIVE)
|
||||
qcom_host->ice.state = UFS_QCOM_ICE_STATE_DISABLED;
|
||||
}
|
||||
|
||||
static struct platform_device *ufs_qcom_ice_get_pdevice(struct device *ufs_dev)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct platform_device *ice_pdev = NULL;
|
||||
|
||||
node = of_parse_phandle(ufs_dev->of_node, UFS_QCOM_CRYPTO_LABEL, 0);
|
||||
|
||||
if (!node) {
|
||||
dev_err(ufs_dev, "%s: ufs-qcom-crypto property not specified\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ice_pdev = qcom_ice_get_pdevice(node);
|
||||
out:
|
||||
return ice_pdev;
|
||||
}
|
||||
|
||||
static
|
||||
struct qcom_ice_variant_ops *ufs_qcom_ice_get_vops(struct device *ufs_dev)
|
||||
{
|
||||
struct qcom_ice_variant_ops *ice_vops = NULL;
|
||||
struct device_node *node;
|
||||
|
||||
node = of_parse_phandle(ufs_dev->of_node, UFS_QCOM_CRYPTO_LABEL, 0);
|
||||
|
||||
if (!node) {
|
||||
dev_err(ufs_dev, "%s: ufs-qcom-crypto property not specified\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ice_vops = qcom_ice_get_variant_ops(node);
|
||||
|
||||
if (!ice_vops)
|
||||
dev_err(ufs_dev, "%s: invalid ice_vops\n", __func__);
|
||||
|
||||
of_node_put(node);
|
||||
out:
|
||||
return ice_vops;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_get_dev() - sets pointers to ICE data structs in UFS QCom host
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
*
|
||||
* Sets ICE platform device pointer and ICE vops structure
|
||||
* corresponding to the current UFS device.
|
||||
*
|
||||
* Return: -EINVAL in-case of invalid input parameters:
|
||||
* qcom_host, qcom_host->hba or qcom_host->hba->dev
|
||||
* -ENODEV in-case ICE device is not required
|
||||
* -EPROBE_DEFER in-case ICE is required and hasn't been probed yet
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
struct device *ufs_dev;
|
||||
int err = 0;
|
||||
|
||||
if (!qcom_host || !qcom_host->hba || !qcom_host->hba->dev) {
|
||||
pr_err("%s: invalid qcom_host %p or qcom_host->hba or qcom_host->hba->dev\n",
|
||||
__func__, qcom_host);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ufs_dev = qcom_host->hba->dev;
|
||||
|
||||
qcom_host->ice.vops = ufs_qcom_ice_get_vops(ufs_dev);
|
||||
qcom_host->ice.pdev = ufs_qcom_ice_get_pdevice(ufs_dev);
|
||||
|
||||
if (qcom_host->ice.pdev == ERR_PTR(-EPROBE_DEFER)) {
|
||||
dev_err(ufs_dev, "%s: ICE device not probed yet\n",
|
||||
__func__);
|
||||
qcom_host->ice.pdev = NULL;
|
||||
qcom_host->ice.vops = NULL;
|
||||
err = -EPROBE_DEFER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcom_host->ice.pdev || !qcom_host->ice.vops) {
|
||||
dev_err(ufs_dev, "%s: invalid platform device %p or vops %p\n",
|
||||
__func__, qcom_host->ice.pdev, qcom_host->ice.vops);
|
||||
qcom_host->ice.pdev = NULL;
|
||||
qcom_host->ice.vops = NULL;
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qcom_host->ice.state = UFS_QCOM_ICE_STATE_DISABLED;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ufs_qcom_ice_cfg_work(struct work_struct *work)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ufs_qcom_host *qcom_host =
|
||||
container_of(work, struct ufs_qcom_host, ice_cfg_work);
|
||||
|
||||
if (!qcom_host->ice.vops->config_start)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
|
||||
if (!qcom_host->req_pending) {
|
||||
qcom_host->work_pending = false;
|
||||
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.
|
||||
*/
|
||||
qcom_host->ice.vops->config_start(qcom_host->ice.pdev,
|
||||
qcom_host->req_pending, NULL, false);
|
||||
|
||||
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
|
||||
qcom_host->req_pending = NULL;
|
||||
qcom_host->work_pending = false;
|
||||
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_init() - initializes the ICE-UFS interface and ICE device
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
* qcom_host, qcom_host->hba and qcom_host->hba->dev should all
|
||||
* be valid pointers.
|
||||
*
|
||||
* Return: -EINVAL in-case of an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
struct device *ufs_dev = qcom_host->hba->dev;
|
||||
int err;
|
||||
|
||||
err = qcom_host->ice.vops->init(qcom_host->ice.pdev,
|
||||
qcom_host,
|
||||
ufs_qcom_ice_error_cb);
|
||||
if (err) {
|
||||
dev_err(ufs_dev, "%s: ice init failed. err = %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
} else {
|
||||
qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
qcom_host->dbg_print_en |= UFS_QCOM_ICE_DEFAULT_DBG_PRINT_EN;
|
||||
ice_workqueue = alloc_workqueue("ice-set-key",
|
||||
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
|
||||
if (!ice_workqueue) {
|
||||
dev_err(ufs_dev, "%s: workqueue allocation failed.\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
INIT_WORK(&qcom_host->ice_cfg_work, ufs_qcom_ice_cfg_work);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline bool ufs_qcom_is_data_cmd(char cmd_op, bool is_write)
|
||||
{
|
||||
if (is_write) {
|
||||
if (cmd_op == WRITE_6 || cmd_op == WRITE_10 ||
|
||||
cmd_op == WRITE_16)
|
||||
return true;
|
||||
} else {
|
||||
if (cmd_op == READ_6 || cmd_op == READ_10 ||
|
||||
cmd_op == READ_16)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host,
|
||||
struct scsi_cmnd *cmd, u8 *cc_index, bool *enable)
|
||||
{
|
||||
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",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
/*
|
||||
* config_start() returns -EAGAIN when a key slot is
|
||||
* available but still not configured. As configuration
|
||||
* requires a non-atomic context, this means we should
|
||||
* call the function again from the worker thread to do
|
||||
* the configuration. For this request the error will
|
||||
* propagate so it will be re-queued.
|
||||
*/
|
||||
if (err == -EAGAIN) {
|
||||
dev_dbg(qcom_host->hba->dev,
|
||||
"%s: scheduling task for ice setup\n",
|
||||
__func__);
|
||||
|
||||
if (!qcom_host->work_pending) {
|
||||
qcom_host->req_pending = cmd->request;
|
||||
|
||||
if (!queue_work(ice_workqueue,
|
||||
&qcom_host->ice_cfg_work)) {
|
||||
qcom_host->req_pending = NULL;
|
||||
|
||||
spin_unlock_irqrestore(
|
||||
&qcom_host->ice_work_lock,
|
||||
flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
qcom_host->work_pending = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
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))
|
||||
*enable = !ice_set.decr_bypass;
|
||||
|
||||
if (ice_set.crypto_data.key_index >= 0)
|
||||
*cc_index = (u8)ice_set.crypto_data.key_index;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_cfg_start() - starts configuring UFS's ICE registers
|
||||
* for an ICE transaction
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
* qcom_host, qcom_host->hba and qcom_host->hba->dev should all
|
||||
* be valid pointers.
|
||||
* @cmd: Pointer to a valid scsi command. cmd->request should also be
|
||||
* a valid pointer.
|
||||
*
|
||||
* Return: -EINVAL in-case of an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
||||
struct scsi_cmnd *cmd)
|
||||
{
|
||||
struct device *dev = qcom_host->hba->dev;
|
||||
int err = 0;
|
||||
struct ice_data_setting ice_set;
|
||||
unsigned int slot = 0;
|
||||
sector_t lba = 0;
|
||||
unsigned int ctrl_info_val = 0;
|
||||
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__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
|
||||
dev_err(dev, "%s: ice state (%d) is not active\n",
|
||||
__func__, qcom_host->ice.state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (qcom_host->hw_ver.major == 0x3) {
|
||||
/* nothing to do here for version 0x3, exit silently */
|
||||
return 0;
|
||||
}
|
||||
|
||||
req = cmd->request;
|
||||
if (req->bio)
|
||||
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) {
|
||||
dev_err(dev, "%s: slot (%d) is out of boundaries (0...%d)\n",
|
||||
__func__, slot, qcom_host->hba->nutrs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
memset(&ice_set, 0, sizeof(ice_set));
|
||||
if (qcom_host->ice.vops->config_start) {
|
||||
|
||||
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) {
|
||||
/*
|
||||
* config_start() returns -EAGAIN when a key slot is
|
||||
* available but still not configured. As configuration
|
||||
* requires a non-atomic context, this means we should
|
||||
* call the function again from the worker thread to do
|
||||
* the configuration. For this request the error will
|
||||
* propagate so it will be re-queued.
|
||||
*/
|
||||
if (err == -EAGAIN) {
|
||||
|
||||
dev_dbg(qcom_host->hba->dev,
|
||||
"%s: scheduling task for ice setup\n",
|
||||
__func__);
|
||||
|
||||
if (!qcom_host->work_pending) {
|
||||
|
||||
qcom_host->req_pending = cmd->request;
|
||||
if (!queue_work(ice_workqueue,
|
||||
&qcom_host->ice_cfg_work)) {
|
||||
qcom_host->req_pending = NULL;
|
||||
|
||||
spin_unlock_irqrestore(
|
||||
&qcom_host->ice_work_lock,
|
||||
flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
qcom_host->work_pending = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
cmd_op = cmd->cmnd[0];
|
||||
|
||||
#define UFS_QCOM_DIR_WRITE true
|
||||
#define UFS_QCOM_DIR_READ false
|
||||
/* if non data command, bypass shall be enabled */
|
||||
if (!ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_WRITE) &&
|
||||
!ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_READ))
|
||||
bypass = UFS_QCOM_ICE_ENABLE_BYPASS;
|
||||
/* if writing data command */
|
||||
else if (ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_WRITE))
|
||||
bypass = ice_set.encr_bypass ? UFS_QCOM_ICE_ENABLE_BYPASS :
|
||||
UFS_QCOM_ICE_DISABLE_BYPASS;
|
||||
/* if reading data command */
|
||||
else if (ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_READ))
|
||||
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 &
|
||||
MASK_UFS_QCOM_ICE_CTRL_INFO_KEY_INDEX)
|
||||
<< OFFSET_UFS_QCOM_ICE_CTRL_INFO_KEY_INDEX;
|
||||
|
||||
/* Configure data unit size of transfer request */
|
||||
ctrl_info_val |=
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_4_KB
|
||||
<< OFFSET_UFS_QCOM_ICE_CTRL_INFO_CDU;
|
||||
|
||||
/* Configure ICE bypass mode */
|
||||
ctrl_info_val |=
|
||||
(bypass & MASK_UFS_QCOM_ICE_CTRL_INFO_BYPASS)
|
||||
<< OFFSET_UFS_QCOM_ICE_CTRL_INFO_BYPASS;
|
||||
|
||||
if (qcom_host->hw_ver.major == 0x1) {
|
||||
ufshcd_writel(qcom_host->hba, lba,
|
||||
(REG_UFS_QCOM_ICE_CTRL_INFO_1_n + 8 * slot));
|
||||
|
||||
ufshcd_writel(qcom_host->hba, ctrl_info_val,
|
||||
(REG_UFS_QCOM_ICE_CTRL_INFO_2_n + 8 * slot));
|
||||
}
|
||||
if (qcom_host->hw_ver.major == 0x2) {
|
||||
ufshcd_writel(qcom_host->hba, (lba & 0xFFFFFFFF),
|
||||
(REG_UFS_QCOM_ICE_CTRL_INFO_1_n + 16 * slot));
|
||||
|
||||
ufshcd_writel(qcom_host->hba, ((lba >> 32) & 0xFFFFFFFF),
|
||||
(REG_UFS_QCOM_ICE_CTRL_INFO_2_n + 16 * slot));
|
||||
|
||||
ufshcd_writel(qcom_host->hba, ctrl_info_val,
|
||||
(REG_UFS_QCOM_ICE_CTRL_INFO_3_n + 16 * slot));
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure UFS-ICE registers are being configured
|
||||
* before next operation, otherwise UFS Host Controller might
|
||||
* set get errors
|
||||
*/
|
||||
mb();
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_cfg_end() - finishes configuring UFS's ICE registers
|
||||
* for an ICE transaction
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
* qcom_host, qcom_host->hba and
|
||||
* qcom_host->hba->dev should all
|
||||
* be valid pointers.
|
||||
* @cmd: Pointer to a valid scsi command. cmd->request should also be
|
||||
* a valid pointer.
|
||||
*
|
||||
* Return: -EINVAL in-case of an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_cfg_end(struct ufs_qcom_host *qcom_host, struct request *req)
|
||||
{
|
||||
int err = 0;
|
||||
struct device *dev = qcom_host->hba->dev;
|
||||
|
||||
if (qcom_host->ice.vops->config_end) {
|
||||
err = qcom_host->ice.vops->config_end(req);
|
||||
if (err) {
|
||||
dev_err(dev, "%s: error in ice_vops->config_end %d\n",
|
||||
__func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_reset() - resets UFS-ICE interface and ICE device
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
* qcom_host, qcom_host->hba and qcom_host->hba->dev should all
|
||||
* be valid pointers.
|
||||
*
|
||||
* Return: -EINVAL in-case of an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
struct device *dev = qcom_host->hba->dev;
|
||||
int err = 0;
|
||||
|
||||
if (!qcom_host->ice.pdev) {
|
||||
dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcom_host->ice.vops) {
|
||||
dev_err(dev, "%s: invalid ice_vops\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE)
|
||||
goto out;
|
||||
|
||||
if (qcom_host->ice.vops->reset) {
|
||||
err = qcom_host->ice.vops->reset(qcom_host->ice.pdev);
|
||||
if (err) {
|
||||
dev_err(dev, "%s: ice_vops->reset failed. err %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
|
||||
dev_err(qcom_host->hba->dev,
|
||||
"%s: error. ice.state (%d) is not in active state\n",
|
||||
__func__, qcom_host->ice.state);
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_resume() - resumes UFS-ICE interface and ICE device from power
|
||||
* collapse
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
* qcom_host, qcom_host->hba and qcom_host->hba->dev should all
|
||||
* be valid pointers.
|
||||
*
|
||||
* Return: -EINVAL in-case of an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
struct device *dev = qcom_host->hba->dev;
|
||||
int err = 0;
|
||||
|
||||
if (!qcom_host->ice.pdev) {
|
||||
dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qcom_host->ice.state !=
|
||||
UFS_QCOM_ICE_STATE_SUSPENDED) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcom_host->ice.vops) {
|
||||
dev_err(dev, "%s: invalid ice_vops\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (qcom_host->ice.vops->resume) {
|
||||
err = qcom_host->ice.vops->resume(qcom_host->ice.pdev);
|
||||
if (err) {
|
||||
dev_err(dev, "%s: ice_vops->resume failed. err %d\n",
|
||||
__func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_suspend() - suspends UFS-ICE interface and ICE device
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
* qcom_host, qcom_host->hba and qcom_host->hba->dev should all
|
||||
* be valid pointers.
|
||||
*
|
||||
* Return: -EINVAL in-case of an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
struct device *dev = qcom_host->hba->dev;
|
||||
int err = 0;
|
||||
|
||||
if (!qcom_host->ice.pdev) {
|
||||
dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qcom_host->ice.vops->suspend) {
|
||||
err = qcom_host->ice.vops->suspend(qcom_host->ice.pdev);
|
||||
if (err) {
|
||||
dev_err(qcom_host->hba->dev,
|
||||
"%s: ice_vops->suspend failed. err %d\n",
|
||||
__func__, err);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_ACTIVE) {
|
||||
qcom_host->ice.state = UFS_QCOM_ICE_STATE_SUSPENDED;
|
||||
} else if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_DISABLED) {
|
||||
dev_err(qcom_host->hba->dev,
|
||||
"%s: ice state is invalid: disabled\n",
|
||||
__func__);
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufs_qcom_ice_get_status() - returns the status of an ICE transaction
|
||||
* @qcom_host: Pointer to a UFS QCom internal host structure.
|
||||
* qcom_host, qcom_host->hba and qcom_host->hba->dev should all
|
||||
* be valid pointers.
|
||||
* @ice_status: Pointer to a valid output parameter.
|
||||
* < 0 in case of ICE transaction failure.
|
||||
* 0 otherwise.
|
||||
*
|
||||
* Return: -EINVAL in-case of an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host, int *ice_status)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
int err = 0;
|
||||
int stat = -EINVAL;
|
||||
|
||||
*ice_status = 0;
|
||||
|
||||
dev = qcom_host->hba->dev;
|
||||
if (!dev) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcom_host->ice.pdev) {
|
||||
dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcom_host->ice.vops) {
|
||||
dev_err(dev, "%s: invalid ice_vops\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (qcom_host->ice.vops->status) {
|
||||
stat = qcom_host->ice.vops->status(qcom_host->ice.pdev);
|
||||
if (stat < 0) {
|
||||
dev_err(dev, "%s: ice_vops->status failed. stat %d\n",
|
||||
__func__, stat);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*ice_status = stat;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
132
drivers/scsi/ufs/ufs-qcom-ice.h
Normal file
132
drivers/scsi/ufs/ufs-qcom-ice.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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_ICE_H_
|
||||
#define _UFS_QCOM_ICE_H_
|
||||
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#include "ufs-qcom.h"
|
||||
|
||||
/*
|
||||
* UFS host controller ICE registers. There are n [0..31]
|
||||
* of each of these registers
|
||||
*/
|
||||
enum {
|
||||
REG_UFS_QCOM_ICE_CFG = 0x2200,
|
||||
REG_UFS_QCOM_ICE_CTRL_INFO_1_n = 0x2204,
|
||||
REG_UFS_QCOM_ICE_CTRL_INFO_2_n = 0x2208,
|
||||
REG_UFS_QCOM_ICE_CTRL_INFO_3_n = 0x220C,
|
||||
};
|
||||
#define NUM_QCOM_ICE_CTRL_INFO_n_REGS 32
|
||||
|
||||
/* UFS QCOM ICE CTRL Info register offset */
|
||||
enum {
|
||||
OFFSET_UFS_QCOM_ICE_CTRL_INFO_BYPASS = 0,
|
||||
OFFSET_UFS_QCOM_ICE_CTRL_INFO_KEY_INDEX = 0x1,
|
||||
OFFSET_UFS_QCOM_ICE_CTRL_INFO_CDU = 0x6,
|
||||
};
|
||||
|
||||
/* UFS QCOM ICE CTRL Info register masks */
|
||||
enum {
|
||||
MASK_UFS_QCOM_ICE_CTRL_INFO_BYPASS = 0x1,
|
||||
MASK_UFS_QCOM_ICE_CTRL_INFO_KEY_INDEX = 0x1F,
|
||||
MASK_UFS_QCOM_ICE_CTRL_INFO_CDU = 0x8,
|
||||
};
|
||||
|
||||
/* UFS QCOM ICE encryption/decryption bypass state */
|
||||
enum {
|
||||
UFS_QCOM_ICE_DISABLE_BYPASS = 0,
|
||||
UFS_QCOM_ICE_ENABLE_BYPASS = 1,
|
||||
};
|
||||
|
||||
/* UFS QCOM ICE Crypto Data Unit of target DUN of Transfer Request */
|
||||
enum {
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_512_B = 0,
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_1_KB = 1,
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_2_KB = 2,
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_4_KB = 3,
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_8_KB = 4,
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_16_KB = 5,
|
||||
UFS_QCOM_ICE_TR_DATA_UNIT_32_KB = 6,
|
||||
};
|
||||
|
||||
/* UFS QCOM ICE internal state */
|
||||
enum {
|
||||
UFS_QCOM_ICE_STATE_DISABLED = 0,
|
||||
UFS_QCOM_ICE_STATE_ACTIVE = 1,
|
||||
UFS_QCOM_ICE_STATE_SUSPENDED = 2,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SCSI_UFS_QCOM_ICE
|
||||
int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host);
|
||||
int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host);
|
||||
int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host,
|
||||
struct scsi_cmnd *cmd, u8 *cc_index, bool *enable);
|
||||
int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
||||
struct scsi_cmnd *cmd);
|
||||
int ufs_qcom_ice_cfg_end(struct ufs_qcom_host *qcom_host,
|
||||
struct request *req);
|
||||
int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host);
|
||||
int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host);
|
||||
int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host);
|
||||
int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host, int *ice_status);
|
||||
void ufs_qcom_ice_print_regs(struct ufs_qcom_host *qcom_host);
|
||||
#else
|
||||
inline int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
if (qcom_host) {
|
||||
qcom_host->ice.pdev = NULL;
|
||||
qcom_host->ice.vops = NULL;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
inline int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inline int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host,
|
||||
struct scsi_cmnd *cmd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inline int ufs_qcom_ice_cfg_end(struct ufs_qcom_host *qcom_host,
|
||||
struct request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inline int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inline int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inline int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inline int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host,
|
||||
int *ice_status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inline void ufs_qcom_ice_print_regs(struct ufs_qcom_host *qcom_host)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SCSI_UFS_QCOM_ICE */
|
||||
|
||||
#endif /* UFS_QCOM_ICE_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2013-2018, 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,9 +15,14 @@
|
||||
#ifndef UFS_QCOM_H_
|
||||
#define UFS_QCOM_H_
|
||||
|
||||
#define MAX_UFS_QCOM_HOSTS 1
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include "ufshcd.h"
|
||||
|
||||
#define MAX_UFS_QCOM_HOSTS 2
|
||||
#define MAX_U32 (~(u32)0)
|
||||
#define MPHY_TX_FSM_STATE 0x41
|
||||
#define MPHY_RX_FSM_STATE 0xC1
|
||||
#define TX_FSM_HIBERN8 0x1
|
||||
#define HBRN8_POLL_TOUT_MS 100
|
||||
#define DEFAULT_CLK_RATE_HZ 1000000
|
||||
@@ -95,7 +101,7 @@ enum {
|
||||
#define QUNIPRO_SEL 0x1
|
||||
#define UTP_DBG_RAMS_EN 0x20000
|
||||
#define TEST_BUS_EN BIT(18)
|
||||
#define TEST_BUS_SEL GENMASK(22, 19)
|
||||
#define TEST_BUS_SEL 0x780000
|
||||
#define UFS_REG_TEST_BUS_EN BIT(30)
|
||||
|
||||
/* bit definitions for REG_UFS_CFG2 register */
|
||||
@@ -116,6 +122,17 @@ enum {
|
||||
DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\
|
||||
TMRLUT_HW_CGC_EN | OCSC_HW_CGC_EN)
|
||||
|
||||
/* bit definitions for UFS_AH8_CFG register */
|
||||
#define CC_UFS_HCLK_REQ_EN BIT(1)
|
||||
#define CC_UFS_SYS_CLK_REQ_EN BIT(2)
|
||||
#define CC_UFS_ICE_CORE_CLK_REQ_EN BIT(3)
|
||||
#define CC_UFS_UNIPRO_CORE_CLK_REQ_EN BIT(4)
|
||||
#define CC_UFS_AUXCLK_REQ_EN BIT(5)
|
||||
|
||||
#define UFS_HW_CLK_CTRL_EN (CC_UFS_SYS_CLK_REQ_EN |\
|
||||
CC_UFS_ICE_CORE_CLK_REQ_EN |\
|
||||
CC_UFS_UNIPRO_CORE_CLK_REQ_EN |\
|
||||
CC_UFS_AUXCLK_REQ_EN)
|
||||
/* bit offset */
|
||||
enum {
|
||||
OFFSET_UFS_PHY_SOFT_RESET = 1,
|
||||
@@ -145,10 +162,24 @@ enum ufs_qcom_phy_init_type {
|
||||
|
||||
/* QUniPro Vendor specific attributes */
|
||||
#define PA_VS_CONFIG_REG1 0x9000
|
||||
#define SAVECONFIGTIME_MODE_MASK 0x6000
|
||||
|
||||
#define PA_VS_CLK_CFG_REG 0x9004
|
||||
#define PA_VS_CLK_CFG_REG_MASK 0x1FF
|
||||
|
||||
#define PA_VS_CORE_CLK_40NS_CYCLES 0x9007
|
||||
#define PA_VS_CORE_CLK_40NS_CYCLES_MASK 0xF
|
||||
|
||||
#define DL_VS_CLK_CFG 0xA00B
|
||||
#define DL_VS_CLK_CFG_MASK 0x3FF
|
||||
|
||||
#define DME_VS_CORE_CLK_CTRL 0xD002
|
||||
/* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */
|
||||
#define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8)
|
||||
#define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK_V4 0xFFF
|
||||
#define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_OFFSET_V4 0x10
|
||||
#define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK 0xFF
|
||||
#define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8)
|
||||
#define DME_VS_CORE_CLK_CTRL_DME_HW_CGC_EN BIT(9)
|
||||
|
||||
static inline void
|
||||
ufs_qcom_get_controller_revision(struct ufs_hba *hba,
|
||||
@@ -207,6 +238,91 @@ struct ufs_qcom_testbus {
|
||||
u8 select_minor;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_qcom_ice_data - ICE related information
|
||||
* @vops: pointer to variant operations of ICE
|
||||
* @async_done: completion for supporting ICE's driver asynchronous nature
|
||||
* @pdev: pointer to the proper ICE platform device
|
||||
* @state: UFS-ICE interface's internal state (see
|
||||
* ufs-qcom-ice.h for possible internal states)
|
||||
* @quirks: UFS-ICE interface related quirks
|
||||
* @crypto_engine_err: crypto engine errors
|
||||
*/
|
||||
struct ufs_qcom_ice_data {
|
||||
struct qcom_ice_variant_ops *vops;
|
||||
struct platform_device *pdev;
|
||||
int state;
|
||||
|
||||
u16 quirks;
|
||||
|
||||
bool crypto_engine_err;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct qcom_debugfs_files {
|
||||
struct dentry *debugfs_root;
|
||||
struct dentry *dbg_print_en;
|
||||
struct dentry *testbus;
|
||||
struct dentry *testbus_en;
|
||||
struct dentry *testbus_cfg;
|
||||
struct dentry *testbus_bus;
|
||||
struct dentry *dbg_regs;
|
||||
struct dentry *pm_qos;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* PM QoS voting state */
|
||||
enum ufs_qcom_pm_qos_state {
|
||||
PM_QOS_UNVOTED,
|
||||
PM_QOS_VOTED,
|
||||
PM_QOS_REQ_VOTE,
|
||||
PM_QOS_REQ_UNVOTE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_qcom_pm_qos_cpu_group - data related to cluster PM QoS voting
|
||||
* logic
|
||||
* @req: request object for PM QoS
|
||||
* @vote_work: work object for voting procedure
|
||||
* @unvote_work: work object for un-voting procedure
|
||||
* @host: back pointer to the main structure
|
||||
* @state: voting state machine current state
|
||||
* @latency_us: requested latency value used for cluster voting, in
|
||||
* microseconds
|
||||
* @mask: cpu mask defined for this cluster
|
||||
* @active_reqs: number of active requests on this cluster
|
||||
*/
|
||||
struct ufs_qcom_pm_qos_cpu_group {
|
||||
struct pm_qos_request req;
|
||||
struct work_struct vote_work;
|
||||
struct work_struct unvote_work;
|
||||
struct ufs_qcom_host *host;
|
||||
enum ufs_qcom_pm_qos_state state;
|
||||
s32 latency_us;
|
||||
cpumask_t mask;
|
||||
int active_reqs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_qcom_pm_qos - data related to PM QoS voting logic
|
||||
* @groups: PM QoS cpu group state array
|
||||
* @enable_attr: sysfs attribute to enable/disable PM QoS voting logic
|
||||
* @latency_attr: sysfs attribute to set latency value
|
||||
* @workq: single threaded workqueue to run PM QoS voting/unvoting
|
||||
* @num_clusters: number of clusters defined
|
||||
* @default_cpu: cpu to use for voting for request not specifying a cpu
|
||||
* @is_enabled: flag specifying whether voting logic is enabled
|
||||
*/
|
||||
struct ufs_qcom_pm_qos {
|
||||
struct ufs_qcom_pm_qos_cpu_group *groups;
|
||||
struct device_attribute enable_attr;
|
||||
struct device_attribute latency_attr;
|
||||
struct workqueue_struct *workq;
|
||||
int num_groups;
|
||||
int default_cpu;
|
||||
bool is_enabled;
|
||||
};
|
||||
|
||||
struct ufs_qcom_host {
|
||||
/*
|
||||
* Set this capability if host controller supports the QUniPro mode
|
||||
@@ -221,6 +337,18 @@ struct ufs_qcom_host {
|
||||
* configuration even after UFS controller core power collapse.
|
||||
*/
|
||||
#define UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE 0x2
|
||||
|
||||
/*
|
||||
* Set this capability if host controller supports Qunipro internal
|
||||
* clock gating.
|
||||
*/
|
||||
#define UFS_QCOM_CAP_QUNIPRO_CLK_GATING 0x4
|
||||
|
||||
/*
|
||||
* Set this capability if host controller supports SVS2 frequencies.
|
||||
*/
|
||||
#define UFS_QCOM_CAP_SVS2 0x8
|
||||
|
||||
u32 caps;
|
||||
|
||||
struct phy *generic_phy;
|
||||
@@ -231,17 +359,34 @@ struct ufs_qcom_host {
|
||||
struct clk *tx_l0_sync_clk;
|
||||
struct clk *rx_l1_sync_clk;
|
||||
struct clk *tx_l1_sync_clk;
|
||||
|
||||
/* PM Quality-of-Service (QoS) data */
|
||||
struct ufs_qcom_pm_qos pm_qos;
|
||||
|
||||
bool disable_lpm;
|
||||
bool is_lane_clks_enabled;
|
||||
bool sec_cfg_updated;
|
||||
struct ufs_qcom_ice_data ice;
|
||||
|
||||
void __iomem *dev_ref_clk_ctrl_mmio;
|
||||
bool is_dev_ref_clk_enabled;
|
||||
struct ufs_hw_version hw_ver;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct qcom_debugfs_files debugfs_files;
|
||||
#endif
|
||||
|
||||
u32 dev_ref_clk_en_mask;
|
||||
|
||||
/* Bitmask for enabling debug prints */
|
||||
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;
|
||||
bool work_pending;
|
||||
bool is_phy_pwr_on;
|
||||
};
|
||||
|
||||
static inline u32
|
||||
@@ -257,7 +402,12 @@ ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
|
||||
#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
|
||||
#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
|
||||
|
||||
bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host, u8 select_major,
|
||||
u8 select_minor);
|
||||
int ufs_qcom_testbus_config(struct ufs_qcom_host *host);
|
||||
void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv,
|
||||
void (*print_fn)(struct ufs_hba *hba, int offset, int num_regs,
|
||||
char *str, void *priv));
|
||||
|
||||
static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host)
|
||||
{
|
||||
@@ -267,4 +417,14 @@ static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool ufs_qcom_cap_qunipro_clk_gating(struct ufs_qcom_host *host)
|
||||
{
|
||||
return !!(host->caps & UFS_QCOM_CAP_QUNIPRO_CLK_GATING);
|
||||
}
|
||||
|
||||
static inline bool ufs_qcom_cap_svs2(struct ufs_qcom_host *host)
|
||||
{
|
||||
return !!(host->caps & UFS_QCOM_CAP_SVS2);
|
||||
}
|
||||
|
||||
#endif /* UFS_QCOM_H_ */
|
||||
|
||||
@@ -50,6 +50,7 @@ static inline ssize_t ufs_sysfs_pm_lvl_store(struct device *dev,
|
||||
hba->rpm_lvl = value;
|
||||
else
|
||||
hba->spm_lvl = value;
|
||||
ufshcd_apply_pm_quirks(hba);
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
#include <scsi/ufs/ufs.h>
|
||||
|
||||
#define MAX_CDB_SIZE 16
|
||||
#define GENERAL_UPIU_REQUEST_SIZE 32
|
||||
@@ -64,6 +65,8 @@
|
||||
#define UFS_MAX_LUNS (SCSI_W_LUN_BASE + UFS_UPIU_MAX_UNIT_NUM_ID)
|
||||
#define UFS_UPIU_WLUN_ID (1 << 7)
|
||||
#define UFS_UPIU_MAX_GENERAL_LUN 8
|
||||
#define UFS_MAX_WLUS 4
|
||||
#define UFS_MAX_LUS (UFS_UPIU_MAX_GENERAL_LUN + UFS_MAX_WLUS)
|
||||
|
||||
/* Well known logical unit id in LUN field of UPIU */
|
||||
enum {
|
||||
@@ -128,63 +131,6 @@ enum {
|
||||
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
|
||||
};
|
||||
|
||||
/* Flag idn for Query Requests*/
|
||||
enum flag_idn {
|
||||
QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
|
||||
QUERY_FLAG_IDN_PERMANENT_WPE = 0x02,
|
||||
QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
|
||||
QUERY_FLAG_IDN_BKOPS_EN = 0x04,
|
||||
QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05,
|
||||
QUERY_FLAG_IDN_PURGE_ENABLE = 0x06,
|
||||
QUERY_FLAG_IDN_RESERVED2 = 0x07,
|
||||
QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08,
|
||||
QUERY_FLAG_IDN_BUSY_RTC = 0x09,
|
||||
QUERY_FLAG_IDN_RESERVED3 = 0x0A,
|
||||
QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B,
|
||||
};
|
||||
|
||||
/* Attribute idn for Query requests */
|
||||
enum attr_idn {
|
||||
QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
|
||||
QUERY_ATTR_IDN_RESERVED = 0x01,
|
||||
QUERY_ATTR_IDN_POWER_MODE = 0x02,
|
||||
QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
|
||||
QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
|
||||
QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
|
||||
QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
|
||||
QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
|
||||
QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
|
||||
QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
|
||||
QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
|
||||
QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
|
||||
QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
|
||||
QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
|
||||
QUERY_ATTR_IDN_EE_STATUS = 0x0E,
|
||||
QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
|
||||
QUERY_ATTR_IDN_CNTX_CONF = 0x10,
|
||||
QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
|
||||
QUERY_ATTR_IDN_RESERVED2 = 0x12,
|
||||
QUERY_ATTR_IDN_RESERVED3 = 0x13,
|
||||
QUERY_ATTR_IDN_FFU_STATUS = 0x14,
|
||||
QUERY_ATTR_IDN_PSA_STATE = 0x15,
|
||||
QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16,
|
||||
};
|
||||
|
||||
/* Descriptor idn for Query requests */
|
||||
enum desc_idn {
|
||||
QUERY_DESC_IDN_DEVICE = 0x0,
|
||||
QUERY_DESC_IDN_CONFIGURATION = 0x1,
|
||||
QUERY_DESC_IDN_UNIT = 0x2,
|
||||
QUERY_DESC_IDN_RFU_0 = 0x3,
|
||||
QUERY_DESC_IDN_INTERCONNECT = 0x4,
|
||||
QUERY_DESC_IDN_STRING = 0x5,
|
||||
QUERY_DESC_IDN_RFU_1 = 0x6,
|
||||
QUERY_DESC_IDN_GEOMETRY = 0x7,
|
||||
QUERY_DESC_IDN_POWER = 0x8,
|
||||
QUERY_DESC_IDN_HEALTH = 0x9,
|
||||
QUERY_DESC_IDN_MAX,
|
||||
};
|
||||
|
||||
enum desc_header_offset {
|
||||
QUERY_DESC_LENGTH_OFFSET = 0x00,
|
||||
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
|
||||
@@ -365,17 +311,13 @@ enum bkops_status {
|
||||
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
|
||||
};
|
||||
|
||||
/* UTP QUERY Transaction Specific Fields OpCode */
|
||||
enum query_opcode {
|
||||
UPIU_QUERY_OPCODE_NOP = 0x0,
|
||||
UPIU_QUERY_OPCODE_READ_DESC = 0x1,
|
||||
UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
|
||||
UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
|
||||
UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
|
||||
UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
|
||||
UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
|
||||
UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
|
||||
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
|
||||
/* bRefClkFreq attribute values */
|
||||
enum ref_clk_freq {
|
||||
REF_CLK_FREQ_19_2_MHZ = 0x0,
|
||||
REF_CLK_FREQ_26_MHZ = 0x1,
|
||||
REF_CLK_FREQ_38_4_MHZ = 0x2,
|
||||
REF_CLK_FREQ_52_MHZ = 0x3,
|
||||
REF_CLK_FREQ_MAX = REF_CLK_FREQ_52_MHZ,
|
||||
};
|
||||
|
||||
/* Query response result code */
|
||||
@@ -602,10 +544,30 @@ struct ufs_vreg_info {
|
||||
struct ufs_vreg *vdd_hba;
|
||||
};
|
||||
|
||||
/* Possible values for bDeviceSubClass of device descriptor */
|
||||
enum {
|
||||
UFS_DEV_EMBEDDED_BOOTABLE = 0x00,
|
||||
UFS_DEV_EMBEDDED_NON_BOOTABLE = 0x01,
|
||||
UFS_DEV_REMOVABLE_BOOTABLE = 0x02,
|
||||
UFS_DEV_REMOVABLE_NON_BOOTABLE = 0x03,
|
||||
};
|
||||
|
||||
struct ufs_dev_info {
|
||||
/* device descriptor info */
|
||||
u8 b_device_sub_class;
|
||||
u16 w_manufacturer_id;
|
||||
u8 i_product_name;
|
||||
|
||||
/* query flags */
|
||||
bool f_power_on_wp_en;
|
||||
|
||||
/* Keeps information if any of the LU is power on write protected */
|
||||
bool is_lu_power_on_wp;
|
||||
/* is Unit Attention Condition cleared on UFS Device LUN? */
|
||||
unsigned is_ufs_dev_wlun_ua_cleared:1;
|
||||
|
||||
/* Device deviations from standard UFS device spec. */
|
||||
unsigned int quirks;
|
||||
};
|
||||
|
||||
#define MAX_MODEL_LEN 16
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2014-2018, 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
|
||||
@@ -31,16 +31,17 @@
|
||||
* @quirk: device quirk
|
||||
*/
|
||||
struct ufs_dev_fix {
|
||||
struct ufs_dev_desc card;
|
||||
u16 w_manufacturer_id;
|
||||
char *model;
|
||||
unsigned int quirk;
|
||||
};
|
||||
|
||||
#define END_FIX { { 0 }, 0 }
|
||||
#define END_FIX { 0 }
|
||||
|
||||
/* add specific device quirk */
|
||||
#define UFS_FIX(_vendor, _model, _quirk) { \
|
||||
.card.wmanufacturerid = (_vendor),\
|
||||
.card.model = (_model), \
|
||||
.w_manufacturer_id = (_vendor),\
|
||||
.model = (_model), \
|
||||
.quirk = (_quirk), \
|
||||
}
|
||||
|
||||
@@ -131,4 +132,12 @@ struct ufs_dev_fix {
|
||||
*/
|
||||
#define UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME (1 << 8)
|
||||
|
||||
/*
|
||||
* Some UFS devices may stop responding after switching from HS-G1 to HS-G3.
|
||||
* Also, it is found that these devices work fine if we do 2 steps switch:
|
||||
* HS-G1 to HS-G2 followed by HS-G2 to HS-G3. Enabling this quirk for such
|
||||
* device would apply this 2 steps gear switch workaround.
|
||||
*/
|
||||
#define UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH (1 << 8)
|
||||
|
||||
#endif /* UFS_QUIRKS_H_ */
|
||||
|
||||
1409
drivers/scsi/ufs/ufs_test.c
Normal file
1409
drivers/scsi/ufs/ufs_test.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,22 @@
|
||||
|
||||
#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
|
||||
|
||||
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;
|
||||
@@ -128,10 +144,11 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba)
|
||||
static int ufshcd_populate_vreg(struct device *dev, const char *name,
|
||||
struct ufs_vreg **out_vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
int len, ret = 0;
|
||||
char prop_name[MAX_PROP_SIZE];
|
||||
struct ufs_vreg *vreg = NULL;
|
||||
struct device_node *np = dev->of_node;
|
||||
const __be32 *prop;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "%s: non DT initialization\n", __func__);
|
||||
@@ -170,8 +187,16 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name,
|
||||
vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV;
|
||||
vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV;
|
||||
} else {
|
||||
prop = of_get_property(np, "vcc-voltage-level", &len);
|
||||
if (!prop || (len != (2 * sizeof(__be32)))) {
|
||||
dev_warn(dev, "%s vcc-voltage-level property.\n",
|
||||
prop ? "invalid format" : "no");
|
||||
vreg->min_uV = UFS_VREG_VCC_MIN_UV;
|
||||
vreg->max_uV = UFS_VREG_VCC_MAX_UV;
|
||||
} else {
|
||||
vreg->min_uV = be32_to_cpup(&prop[0]);
|
||||
vreg->max_uV = be32_to_cpup(&prop[1]);
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(name, "vccq")) {
|
||||
vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
|
||||
@@ -221,7 +246,111 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void ufshcd_parse_pm_levels(struct ufs_hba *hba)
|
||||
{
|
||||
struct device *dev = hba->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
if (np) {
|
||||
if (of_property_read_u32(np, "rpm-level", &hba->rpm_lvl))
|
||||
hba->rpm_lvl = -1;
|
||||
if (of_property_read_u32(np, "spm-level", &hba->spm_lvl))
|
||||
hba->spm_lvl = -1;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int ufshcd_parse_extcon_info(struct ufs_hba *hba)
|
||||
{
|
||||
struct extcon_dev *extcon;
|
||||
|
||||
extcon = extcon_get_edev_by_phandle(hba->dev, 0);
|
||||
if (IS_ERR(extcon) && PTR_ERR(extcon) != -ENODEV)
|
||||
return PTR_ERR(extcon);
|
||||
|
||||
if (!IS_ERR(extcon))
|
||||
hba->extcon = extcon;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ufshcd_parse_gear_limits(struct ufs_hba *hba)
|
||||
{
|
||||
struct device *dev = hba->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
ret = of_property_read_u32(np, "limit-tx-hs-gear",
|
||||
&hba->limit_tx_hs_gear);
|
||||
if (ret)
|
||||
hba->limit_tx_hs_gear = -1;
|
||||
|
||||
ret = of_property_read_u32(np, "limit-rx-hs-gear",
|
||||
&hba->limit_rx_hs_gear);
|
||||
if (ret)
|
||||
hba->limit_rx_hs_gear = -1;
|
||||
|
||||
ret = of_property_read_u32(np, "limit-tx-pwm-gear",
|
||||
&hba->limit_tx_pwm_gear);
|
||||
if (ret)
|
||||
hba->limit_tx_pwm_gear = -1;
|
||||
|
||||
ret = of_property_read_u32(np, "limit-rx-pwm-gear",
|
||||
&hba->limit_rx_pwm_gear);
|
||||
if (ret)
|
||||
hba->limit_rx_pwm_gear = -1;
|
||||
}
|
||||
|
||||
static void ufshcd_parse_cmd_timeout(struct ufs_hba *hba)
|
||||
{
|
||||
struct device *dev = hba->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
ret = of_property_read_u32(np, "scsi-cmd-timeout",
|
||||
&hba->scsi_cmd_timeout);
|
||||
if (ret)
|
||||
hba->scsi_cmd_timeout = 0;
|
||||
}
|
||||
|
||||
static void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba)
|
||||
{
|
||||
struct device *dev = hba->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
ret = of_property_read_u32(np, "dev-ref-clk-freq",
|
||||
&hba->dev_ref_clk_freq);
|
||||
if (ret ||
|
||||
(hba->dev_ref_clk_freq < 0) ||
|
||||
(hba->dev_ref_clk_freq > REF_CLK_FREQ_52_MHZ))
|
||||
/* default setting */
|
||||
hba->dev_ref_clk_freq = REF_CLK_FREQ_26_MHZ;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/**
|
||||
* ufshcd_pltfrm_suspend - suspend power management function
|
||||
* @dev: pointer to device handle
|
||||
@@ -292,12 +421,12 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
|
||||
/**
|
||||
* ufshcd_pltfrm_init - probe routine of the driver
|
||||
* @pdev: pointer to Platform device handle
|
||||
* @vops: pointer to variant ops
|
||||
* @var: pointer to variant specific data
|
||||
*
|
||||
* Returns 0 on success, non-zero value on failure
|
||||
*/
|
||||
int ufshcd_pltfrm_init(struct platform_device *pdev,
|
||||
struct ufs_hba_variant_ops *vops)
|
||||
struct ufs_hba_variant *var)
|
||||
{
|
||||
struct ufs_hba *hba;
|
||||
void __iomem *mmio_base;
|
||||
@@ -325,7 +454,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
hba->vops = vops;
|
||||
hba->var = var;
|
||||
|
||||
err = ufshcd_parse_clock_info(hba);
|
||||
if (err) {
|
||||
@@ -340,24 +469,45 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
|
||||
goto dealloc_host;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
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);
|
||||
ufshcd_parse_cmd_timeout(hba);
|
||||
err = ufshcd_parse_extcon_info(hba);
|
||||
if (err)
|
||||
goto dealloc_host;
|
||||
|
||||
if (!dev->dma_mask)
|
||||
dev->dma_mask = &dev->coherent_dma_mask;
|
||||
|
||||
ufshcd_init_lanes_per_dir(hba);
|
||||
|
||||
err = ufshcd_init(hba, mmio_base, irq);
|
||||
if (err) {
|
||||
dev_err(dev, "Initialization failed\n");
|
||||
goto out_disable_rpm;
|
||||
goto dealloc_host;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, hba);
|
||||
|
||||
return 0;
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
out_disable_rpm:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
return 0;
|
||||
dealloc_host:
|
||||
ufshcd_dealloc_host(hba);
|
||||
out:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2015-2018, 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
|
||||
@@ -17,7 +18,7 @@
|
||||
#include "ufshcd.h"
|
||||
|
||||
int ufshcd_pltfrm_init(struct platform_device *pdev,
|
||||
struct ufs_hba_variant_ops *vops);
|
||||
struct ufs_hba_variant *var);
|
||||
void ufshcd_pltfrm_shutdown(struct platform_device *pdev);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -90,10 +90,9 @@ enum {
|
||||
MASK_64_ADDRESSING_SUPPORT = 0x01000000,
|
||||
MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
|
||||
MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
|
||||
MASK_CRYPTO_SUPPORT = 0x10000000,
|
||||
};
|
||||
|
||||
#define UFS_MASK(mask, offset) ((mask) << (offset))
|
||||
|
||||
/* UFS Version 08h */
|
||||
#define MINOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 0)
|
||||
#define MAJOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 16)
|
||||
@@ -104,6 +103,7 @@ enum {
|
||||
UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */
|
||||
UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */
|
||||
UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */
|
||||
UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -125,10 +125,16 @@ enum {
|
||||
#define UFSHCI_AHIBERN8_SCALE_MASK GENMASK(12, 10)
|
||||
#define UFSHCI_AHIBERN8_SCALE_FACTOR 10
|
||||
#define UFSHCI_AHIBERN8_MAX (1023 * 100000)
|
||||
#define AUTO_HIBERN8_IDLE_TIMER_MASK UFS_MASK(0x3FF, 0)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_MASK UFS_MASK(0x7, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_1_US UFS_MASK(0x0, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_10_US UFS_MASK(0x1, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_100_US UFS_MASK(0x2, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_1_MS UFS_MASK(0x3, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_10_MS UFS_MASK(0x4, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_100_MS UFS_MASK(0x5, 10)
|
||||
|
||||
/*
|
||||
* IS - Interrupt Status - 20h
|
||||
*/
|
||||
/* IS - Interrupt status (20h) / IE - Interrupt enable (24h) */
|
||||
#define UTP_TRANSFER_REQ_COMPL 0x1
|
||||
#define UIC_DME_END_PT_RESET 0x2
|
||||
#define UIC_ERROR 0x4
|
||||
@@ -143,6 +149,7 @@ enum {
|
||||
#define DEVICE_FATAL_ERROR 0x800
|
||||
#define CONTROLLER_FATAL_ERROR 0x10000
|
||||
#define SYSTEM_BUS_FATAL_ERROR 0x20000
|
||||
#define CRYPTO_ENGINE_FATAL_ERROR 0x40000
|
||||
|
||||
#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\
|
||||
UIC_HIBERNATE_EXIT |\
|
||||
@@ -150,14 +157,16 @@ enum {
|
||||
|
||||
#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK)
|
||||
|
||||
#define UFSHCD_ERROR_MASK (UIC_ERROR |\
|
||||
#define UFSHCD_ERROR_MASK (UIC_ERROR | UIC_LINK_LOST |\
|
||||
DEVICE_FATAL_ERROR |\
|
||||
CONTROLLER_FATAL_ERROR |\
|
||||
SYSTEM_BUS_FATAL_ERROR)
|
||||
SYSTEM_BUS_FATAL_ERROR |\
|
||||
CRYPTO_ENGINE_FATAL_ERROR)
|
||||
|
||||
#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\
|
||||
CONTROLLER_FATAL_ERROR |\
|
||||
SYSTEM_BUS_FATAL_ERROR)
|
||||
SYSTEM_BUS_FATAL_ERROR |\
|
||||
CRYPTO_ENGINE_FATAL_ERROR)
|
||||
|
||||
/* HCS - Host Controller Status 30h */
|
||||
#define DEVICE_PRESENT 0x1
|
||||
@@ -181,6 +190,43 @@ enum {
|
||||
PWR_FATAL_ERROR = 0x05,
|
||||
};
|
||||
|
||||
/* Host UIC error type */
|
||||
enum ufshcd_uic_err_type {
|
||||
UFS_UIC_ERROR_PA,
|
||||
UFS_UIC_ERROR_DL,
|
||||
UFS_UIC_ERROR_DME,
|
||||
};
|
||||
|
||||
/* Host UIC error code PHY adapter layer */
|
||||
enum ufshcd_ec_pa {
|
||||
UFS_EC_PA_LANE_0,
|
||||
UFS_EC_PA_LANE_1,
|
||||
UFS_EC_PA_LANE_2,
|
||||
UFS_EC_PA_LANE_3,
|
||||
UFS_EC_PA_LINE_RESET,
|
||||
UFS_EC_PA_MAX,
|
||||
};
|
||||
|
||||
/* Host UIC error code data link layer */
|
||||
enum ufshcd_ec_dl {
|
||||
UFS_EC_DL_NAC_RECEIVED,
|
||||
UFS_EC_DL_TCx_REPLAY_TIMER_EXPIRED,
|
||||
UFS_EC_DL_AFCx_REQUEST_TIMER_EXPIRED,
|
||||
UFS_EC_DL_FCx_PROTECT_TIMER_EXPIRED,
|
||||
UFS_EC_DL_CRC_ERROR,
|
||||
UFS_EC_DL_RX_BUFFER_OVERFLOW,
|
||||
UFS_EC_DL_MAX_FRAME_LENGTH_EXCEEDED,
|
||||
UFS_EC_DL_WRONG_SEQUENCE_NUMBER,
|
||||
UFS_EC_DL_AFC_FRAME_SYNTAX_ERROR,
|
||||
UFS_EC_DL_NAC_FRAME_SYNTAX_ERROR,
|
||||
UFS_EC_DL_EOF_SYNTAX_ERROR,
|
||||
UFS_EC_DL_FRAME_SYNTAX_ERROR,
|
||||
UFS_EC_DL_BAD_CTRL_SYMBOL_TYPE,
|
||||
UFS_EC_DL_PA_INIT_ERROR,
|
||||
UFS_EC_DL_PA_ERROR_IND_RECEIVED,
|
||||
UFS_EC_DL_MAX,
|
||||
};
|
||||
|
||||
/* HCE - Host Controller Enable 34h */
|
||||
#define CONTROLLER_ENABLE 0x1
|
||||
#define CONTROLLER_DISABLE 0x0
|
||||
@@ -188,6 +234,7 @@ enum {
|
||||
|
||||
/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
|
||||
#define UIC_PHY_ADAPTER_LAYER_ERROR 0x80000000
|
||||
#define UIC_PHY_ADAPTER_LAYER_GENERIC_ERROR 0x10
|
||||
#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F
|
||||
#define UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK 0xF
|
||||
|
||||
@@ -356,6 +403,9 @@ enum {
|
||||
OCS_PEER_COMM_FAILURE = 0x5,
|
||||
OCS_ABORTED = 0x6,
|
||||
OCS_FATAL_ERROR = 0x7,
|
||||
OCS_DEVICE_FATAL_ERROR = 0x8,
|
||||
OCS_INVALID_CRYPTO_CONFIG = 0x9,
|
||||
OCS_GENERAL_CRYPTO_ERROR = 0xA,
|
||||
OCS_INVALID_COMMAND_STATUS = 0x0F,
|
||||
MASK_OCS = 0x0F,
|
||||
};
|
||||
@@ -391,6 +441,8 @@ struct utp_transfer_cmd_desc {
|
||||
struct ufshcd_sg_entry prd_table[SG_ALL];
|
||||
};
|
||||
|
||||
#define UTRD_CRYPTO_ENABLE UFS_BIT(23)
|
||||
|
||||
/**
|
||||
* struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
|
||||
* @dword0: Descriptor Header DW0
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
#define CFGRXOVR4 0x00E9
|
||||
#define RXSQCTRL 0x00B5
|
||||
#define CFGRXOVR6 0x00BF
|
||||
#define MPHY_RX_ATTR_ADDR_START 0x81
|
||||
#define MPHY_RX_ATTR_ADDR_END 0xC1
|
||||
|
||||
#define is_mphy_tx_attr(attr) (attr < RX_MODE)
|
||||
#define RX_MIN_ACTIVATETIME_UNIT_US 100
|
||||
@@ -165,6 +167,14 @@
|
||||
/* PHY Adapter Protocol Constants */
|
||||
#define PA_MAXDATALANES 4
|
||||
|
||||
#define DL_FC0ProtectionTimeOutVal_Default 8191
|
||||
#define DL_TC0ReplayTimeOutVal_Default 65535
|
||||
#define DL_AFC0ReqTimeOutVal_Default 32767
|
||||
|
||||
#define DME_LocalFC0ProtectionTimeOutVal 0xD041
|
||||
#define DME_LocalTC0ReplayTimeOutVal 0xD042
|
||||
#define DME_LocalAFC0ReqTimeOutVal 0xD043
|
||||
|
||||
/* PA power modes */
|
||||
enum {
|
||||
FAST_MODE = 1,
|
||||
|
||||
@@ -158,44 +158,45 @@
|
||||
#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 151
|
||||
#define GCC_UFS_CARD_UNIPRO_CORE_CLK 152
|
||||
#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 153
|
||||
#define GCC_UFS_PHY_AHB_CLK 154
|
||||
#define GCC_UFS_PHY_AXI_CLK 155
|
||||
#define GCC_UFS_PHY_AXI_CLK_SRC 156
|
||||
#define GCC_UFS_PHY_ICE_CORE_CLK 157
|
||||
#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 158
|
||||
#define GCC_UFS_PHY_PHY_AUX_CLK 159
|
||||
#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 160
|
||||
#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 161
|
||||
#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 162
|
||||
#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 163
|
||||
#define GCC_UFS_PHY_UNIPRO_CORE_CLK 164
|
||||
#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 165
|
||||
#define GCC_USB30_PRIM_MASTER_CLK 166
|
||||
#define GCC_USB30_PRIM_MASTER_CLK_SRC 167
|
||||
#define GCC_USB30_PRIM_MOCK_UTMI_CLK 168
|
||||
#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 169
|
||||
#define GCC_USB30_PRIM_SLEEP_CLK 170
|
||||
#define GCC_USB30_SEC_MASTER_CLK 171
|
||||
#define GCC_USB30_SEC_MASTER_CLK_SRC 172
|
||||
#define GCC_USB30_SEC_MOCK_UTMI_CLK 173
|
||||
#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 174
|
||||
#define GCC_USB30_SEC_SLEEP_CLK 175
|
||||
#define GCC_USB3_PRIM_PHY_AUX_CLK 176
|
||||
#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 177
|
||||
#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 178
|
||||
#define GCC_USB3_PRIM_PHY_PIPE_CLK 179
|
||||
#define GCC_USB3_SEC_CLKREF_CLK 180
|
||||
#define GCC_USB3_SEC_PHY_AUX_CLK 181
|
||||
#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 182
|
||||
#define GCC_USB3_SEC_PHY_COM_AUX_CLK 183
|
||||
#define GCC_USB3_SEC_PHY_PIPE_CLK 184
|
||||
#define GCC_VIDEO_AHB_CLK 185
|
||||
#define GCC_VIDEO_AXI0_CLK 186
|
||||
#define GCC_VIDEO_AXI1_CLK 187
|
||||
#define GCC_VIDEO_XO_CLK 188
|
||||
#define GPLL0 189
|
||||
#define GPLL0_OUT_EVEN 190
|
||||
#define GPLL9 191
|
||||
#define GCC_UFS_MEM_CLKREF_CLK 154
|
||||
#define GCC_UFS_PHY_AHB_CLK 155
|
||||
#define GCC_UFS_PHY_AXI_CLK 156
|
||||
#define GCC_UFS_PHY_AXI_CLK_SRC 157
|
||||
#define GCC_UFS_PHY_ICE_CORE_CLK 158
|
||||
#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 159
|
||||
#define GCC_UFS_PHY_PHY_AUX_CLK 160
|
||||
#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 161
|
||||
#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 162
|
||||
#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 163
|
||||
#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 164
|
||||
#define GCC_UFS_PHY_UNIPRO_CORE_CLK 165
|
||||
#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 166
|
||||
#define GCC_USB30_PRIM_MASTER_CLK 167
|
||||
#define GCC_USB30_PRIM_MASTER_CLK_SRC 168
|
||||
#define GCC_USB30_PRIM_MOCK_UTMI_CLK 169
|
||||
#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 170
|
||||
#define GCC_USB30_PRIM_SLEEP_CLK 171
|
||||
#define GCC_USB30_SEC_MASTER_CLK 172
|
||||
#define GCC_USB30_SEC_MASTER_CLK_SRC 173
|
||||
#define GCC_USB30_SEC_MOCK_UTMI_CLK 174
|
||||
#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 175
|
||||
#define GCC_USB30_SEC_SLEEP_CLK 176
|
||||
#define GCC_USB3_PRIM_PHY_AUX_CLK 177
|
||||
#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 178
|
||||
#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 179
|
||||
#define GCC_USB3_PRIM_PHY_PIPE_CLK 180
|
||||
#define GCC_USB3_SEC_CLKREF_CLK 181
|
||||
#define GCC_USB3_SEC_PHY_AUX_CLK 182
|
||||
#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 183
|
||||
#define GCC_USB3_SEC_PHY_COM_AUX_CLK 184
|
||||
#define GCC_USB3_SEC_PHY_PIPE_CLK 185
|
||||
#define GCC_VIDEO_AHB_CLK 186
|
||||
#define GCC_VIDEO_AXI0_CLK 187
|
||||
#define GCC_VIDEO_AXI1_CLK 188
|
||||
#define GCC_VIDEO_XO_CLK 189
|
||||
#define GPLL0 190
|
||||
#define GPLL0_OUT_EVEN 191
|
||||
#define GPLL9 192
|
||||
|
||||
#define PCIE_0_GDSC 0
|
||||
#define PCIE_1_GDSC 1
|
||||
|
||||
@@ -322,6 +322,8 @@ enum req_flag_bits {
|
||||
__REQ_BACKGROUND, /* background IO */
|
||||
__REQ_NOWAIT, /* Don't wait if request will block */
|
||||
|
||||
__REQ_SORTED = __REQ_RAHEAD, /* elevator knows about this request */
|
||||
__REQ_URGENT, /* urgent request */
|
||||
/* command specific flags for REQ_OP_WRITE_ZEROES: */
|
||||
__REQ_NOUNMAP, /* do not free blocks when zeroing */
|
||||
|
||||
@@ -337,6 +339,7 @@ enum req_flag_bits {
|
||||
#define REQ_SYNC (1ULL << __REQ_SYNC)
|
||||
#define REQ_META (1ULL << __REQ_META)
|
||||
#define REQ_PRIO (1ULL << __REQ_PRIO)
|
||||
#define REQ_URGENT (1ULL << __REQ_URGENT)
|
||||
#define REQ_NOMERGE (1ULL << __REQ_NOMERGE)
|
||||
#define REQ_IDLE (1ULL << __REQ_IDLE)
|
||||
#define REQ_INTEGRITY (1ULL << __REQ_INTEGRITY)
|
||||
|
||||
@@ -236,6 +236,9 @@ extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
|
||||
* the governor may consider slowing the frequency down.
|
||||
* Specify 0 to use the default. Valid value = 0 to 100.
|
||||
* downdifferential < upthreshold must hold.
|
||||
* @simple_scaling: Setting this flag will scale the clocks up only if the
|
||||
* load is above @upthreshold and will scale the clocks
|
||||
* down only if the load is below @downdifferential.
|
||||
*
|
||||
* If the fed devfreq_simple_ondemand_data pointer is NULL to the governor,
|
||||
* the governor uses the default values.
|
||||
@@ -243,6 +246,7 @@ extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
|
||||
struct devfreq_simple_ondemand_data {
|
||||
unsigned int upthreshold;
|
||||
unsigned int downdifferential;
|
||||
unsigned int simple_scaling;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2016, 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
|
||||
@@ -31,8 +31,15 @@ void ufs_qcom_phy_enable_dev_ref_clk(struct phy *phy);
|
||||
*/
|
||||
void ufs_qcom_phy_disable_dev_ref_clk(struct phy *phy);
|
||||
|
||||
int ufs_qcom_phy_start_serdes(struct phy *generic_phy);
|
||||
int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy);
|
||||
int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B);
|
||||
int ufs_qcom_phy_set_tx_lane_enable(struct phy *phy, u32 tx_lanes);
|
||||
int ufs_qcom_phy_ctrl_rx_linecfg(struct phy *generic_phy, bool ctrl);
|
||||
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_ */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <scsi/scsi_common.h>
|
||||
#include <scsi/scsi_proto.h>
|
||||
|
||||
@@ -253,27 +254,6 @@ static inline int scsi_is_wlun(u64 lun)
|
||||
#define SCSI_INQ_PQ_NOT_CON 0x01
|
||||
#define SCSI_INQ_PQ_NOT_CAP 0x03
|
||||
|
||||
|
||||
/*
|
||||
* Here are some scsi specific ioctl commands which are sometimes useful.
|
||||
*
|
||||
* Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
|
||||
*/
|
||||
|
||||
/* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */
|
||||
#define SCSI_IOCTL_GET_IDLUN 0x5382
|
||||
|
||||
/* 0x5383 and 0x5384 were used for SCSI_IOCTL_TAGGED_{ENABLE,DISABLE} */
|
||||
|
||||
/* Used to obtain the host number of a device. */
|
||||
#define SCSI_IOCTL_PROBE_HOST 0x5385
|
||||
|
||||
/* Used to obtain the bus number for a device */
|
||||
#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
|
||||
|
||||
/* Used to obtain the PCI location of a device */
|
||||
#define SCSI_IOCTL_GET_PCI 0x5387
|
||||
|
||||
/* Pull a u32 out of a SCSI message (using BE SCSI conventions) */
|
||||
static inline __u32 scsi_to_u32(__u8 *ptr)
|
||||
{
|
||||
|
||||
@@ -199,6 +199,12 @@ struct scsi_device {
|
||||
unsigned broken_fua:1; /* Don't set FUA bit */
|
||||
unsigned lun_in_cdb:1; /* Store LUN bits in CDB[1] */
|
||||
unsigned unmap_limit_for_ws:1; /* Use the UNMAP limit for WRITE SAME */
|
||||
unsigned use_rpm_auto:1; /* Enable runtime PM auto suspend */
|
||||
|
||||
#define SCSI_DEFAULT_AUTOSUSPEND_DELAY -1
|
||||
int autosuspend_delay;
|
||||
/* If non-zero, use timeout (in jiffies) for all commands */
|
||||
unsigned int timeout_override;
|
||||
|
||||
atomic_t disk_events_disable_depth; /* disable depth for disk events */
|
||||
|
||||
@@ -455,6 +461,8 @@ extern void sdev_disable_disk_events(struct scsi_device *sdev);
|
||||
extern void sdev_enable_disk_events(struct scsi_device *sdev);
|
||||
extern int scsi_vpd_lun_id(struct scsi_device *, char *, size_t);
|
||||
extern int scsi_vpd_tpg_id(struct scsi_device *, int *);
|
||||
extern void scsi_set_cmd_timeout_override(struct scsi_device *sdev,
|
||||
unsigned int timeout);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int scsi_autopm_get_device(struct scsi_device *);
|
||||
|
||||
@@ -654,6 +654,12 @@ struct Scsi_Host {
|
||||
/* Host responded with short (<36 bytes) INQUIRY result */
|
||||
unsigned short_inquiry:1;
|
||||
|
||||
/*
|
||||
* Set "DBD" field in mode_sense caching mode page in case it is
|
||||
* mandatory by LLD standard.
|
||||
*/
|
||||
unsigned set_dbd_for_caching:1;
|
||||
|
||||
/*
|
||||
* Optional work queue to be utilized by the transport
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2018, 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
|
||||
@@ -54,8 +54,7 @@ UFSCHD_CLK_GATING_STATES;
|
||||
#define EM(a) { a, #a },
|
||||
#define EMe(a) { a, #a }
|
||||
|
||||
TRACE_EVENT(ufshcd_clk_gating,
|
||||
|
||||
DECLARE_EVENT_CLASS(ufshcd_state_change_template,
|
||||
TP_PROTO(const char *dev_name, int state),
|
||||
|
||||
TP_ARGS(dev_name, state),
|
||||
@@ -70,11 +69,37 @@ TRACE_EVENT(ufshcd_clk_gating,
|
||||
__entry->state = state;
|
||||
),
|
||||
|
||||
TP_printk("%s: gating state changed to %s",
|
||||
TP_printk("%s: state changed to %s",
|
||||
__get_str(dev_name),
|
||||
__print_symbolic(__entry->state, UFSCHD_CLK_GATING_STATES))
|
||||
);
|
||||
|
||||
DEFINE_EVENT_PRINT(ufshcd_state_change_template, ufshcd_clk_gating,
|
||||
TP_PROTO(const char *dev_name, int state),
|
||||
TP_ARGS(dev_name, state),
|
||||
TP_printk("%s: state changed to %s", __get_str(dev_name),
|
||||
__print_symbolic(__entry->state,
|
||||
{ CLKS_OFF, "CLKS_OFF" },
|
||||
{ CLKS_ON, "CLKS_ON" },
|
||||
{ REQ_CLKS_OFF, "REQ_CLKS_OFF" },
|
||||
{ REQ_CLKS_ON, "REQ_CLKS_ON" }))
|
||||
);
|
||||
|
||||
DEFINE_EVENT_PRINT(ufshcd_state_change_template, ufshcd_hibern8_on_idle,
|
||||
TP_PROTO(const char *dev_name, int state),
|
||||
TP_ARGS(dev_name, state),
|
||||
TP_printk("%s: state changed to %s", __get_str(dev_name),
|
||||
__print_symbolic(__entry->state,
|
||||
{ HIBERN8_ENTERED, "HIBERN8_ENTER" },
|
||||
{ HIBERN8_EXITED, "HIBERN8_EXIT" },
|
||||
{ REQ_HIBERN8_ENTER, "REQ_HIBERN8_ENTER" },
|
||||
{ REQ_HIBERN8_EXIT, "REQ_HIBERN8_EXIT" }))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(ufshcd_state_change_template, ufshcd_auto_bkops_state,
|
||||
TP_PROTO(const char *dev_name, int state),
|
||||
TP_ARGS(dev_name, state));
|
||||
|
||||
TRACE_EVENT(ufshcd_clk_scaling,
|
||||
|
||||
TP_PROTO(const char *dev_name, const char *state, const char *clk,
|
||||
@@ -103,26 +128,6 @@ TRACE_EVENT(ufshcd_clk_scaling,
|
||||
__entry->prev_state, __entry->curr_state)
|
||||
);
|
||||
|
||||
TRACE_EVENT(ufshcd_auto_bkops_state,
|
||||
|
||||
TP_PROTO(const char *dev_name, const char *state),
|
||||
|
||||
TP_ARGS(dev_name, state),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(dev_name, dev_name)
|
||||
__string(state, state)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(dev_name, dev_name);
|
||||
__assign_str(state, state);
|
||||
),
|
||||
|
||||
TP_printk("%s: auto bkops - %s",
|
||||
__get_str(dev_name), __get_str(state))
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(ufshcd_profiling_template,
|
||||
TP_PROTO(const char *dev_name, const char *profile_info, s64 time_us,
|
||||
int err),
|
||||
@@ -250,10 +255,10 @@ TRACE_EVENT(ufshcd_command,
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
"%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x",
|
||||
__get_str(str), __get_str(dev_name), __entry->tag,
|
||||
__entry->doorbell, __entry->transfer_len,
|
||||
__entry->intr, __entry->lba, (u32)__entry->opcode
|
||||
"%s: %14s: tag: %-2u cmd: 0x%-2x lba: %-9llu size: %-7d DB: 0x%-8x IS: 0x%x",
|
||||
__get_str(dev_name), __get_str(str), __entry->tag,
|
||||
(u32)__entry->opcode, __entry->lba, __entry->transfer_len,
|
||||
__entry->doorbell, __entry->intr
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
5
include/uapi/scsi/Kbuild
Normal file
5
include/uapi/scsi/Kbuild
Normal file
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
|
||||
|
||||
header-y += ufs/
|
||||
header-y += sg.h
|
||||
header-y += scsi_ioctl.h
|
||||
@@ -1,7 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef _SCSI_IOCTL_H
|
||||
#define _SCSI_IOCTL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define SCSI_IOCTL_SEND_COMMAND 1
|
||||
#define SCSI_IOCTL_TEST_UNIT_READY 2
|
||||
#define SCSI_IOCTL_BENCHMARK_COMMAND 3
|
||||
@@ -16,9 +18,25 @@
|
||||
#define SCSI_REMOVAL_PREVENT 1
|
||||
#define SCSI_REMOVAL_ALLOW 0
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* Here are some scsi specific ioctl commands which are sometimes useful.
|
||||
*
|
||||
* Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
|
||||
*/
|
||||
|
||||
struct scsi_device;
|
||||
/* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */
|
||||
#define SCSI_IOCTL_GET_IDLUN 0x5382
|
||||
|
||||
/* 0x5383 and 0x5384 were used for SCSI_IOCTL_TAGGED_{ENABLE,DISABLE} */
|
||||
|
||||
/* Used to obtain the host number of a device. */
|
||||
#define SCSI_IOCTL_PROBE_HOST 0x5385
|
||||
|
||||
/* Used to obtain the bus number for a device */
|
||||
#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
|
||||
|
||||
/* Used to obtain the PCI location of a device */
|
||||
#define SCSI_IOCTL_GET_PCI 0x5387
|
||||
|
||||
/*
|
||||
* Structures used for scsi_ioctl et al.
|
||||
@@ -41,9 +59,12 @@ typedef struct scsi_fctargaddress {
|
||||
unsigned char host_wwn[8]; // include NULL term.
|
||||
} Scsi_FCTargAddress;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct scsi_device;
|
||||
|
||||
int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev,
|
||||
int cmd, bool ndelay);
|
||||
extern int scsi_ioctl(struct scsi_device *, int, void __user *);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _SCSI_IOCTL_H */
|
||||
@@ -1,8 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef _SCSI_GENERIC_H
|
||||
#define _SCSI_GENERIC_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/param.h>
|
||||
|
||||
/*
|
||||
* History:
|
||||
@@ -33,30 +34,27 @@
|
||||
extern int sg_big_buff; /* for sysctl */
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct sg_iovec /* same structure as used by readv() Linux system */
|
||||
{ /* call. It defines one scatter-gather element. */
|
||||
void __user *iov_base; /* Starting address */
|
||||
size_t iov_len; /* Length in bytes */
|
||||
} sg_iovec_t;
|
||||
|
||||
|
||||
typedef struct sg_io_hdr
|
||||
{
|
||||
typedef struct sg_io_hdr {
|
||||
int interface_id; /* [i] 'S' for SCSI generic (required) */
|
||||
int dxfer_direction; /* [i] data transfer direction */
|
||||
unsigned char cmd_len; /* [i] SCSI command length */
|
||||
unsigned char mx_sb_len; /* [i] max length to write to sbp */
|
||||
unsigned short iovec_count; /* [i] 0 implies no scatter gather */
|
||||
unsigned int dxfer_len; /* [i] byte count of data transfer */
|
||||
void __user *dxferp; /* [i], [*io] points to data transfer memory
|
||||
or scatter gather list */
|
||||
void __user *dxferp; /* [i], [*io] points to data transfer memory */
|
||||
/* or scatter gather list */
|
||||
unsigned char __user *cmdp; /* [i], [*i] points to command to perform */
|
||||
void __user *sbp; /* [i], [*o] points to sense_buffer memory */
|
||||
void __user *sbp; /* [i], [*o] points to sense_buffer meimory */
|
||||
unsigned int timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
|
||||
unsigned int flags; /* [i] 0 -> default, see SG_FLAG... */
|
||||
int pack_id; /* [i->o] unused internally (normally) */
|
||||
void __user * usr_ptr; /* [i->o] unused internally */
|
||||
void __user *usr_ptr; /* [i->o] unused internally */
|
||||
unsigned char status; /* [o] scsi status */
|
||||
unsigned char masked_status;/* [o] shifted, masked scsi status */
|
||||
unsigned char msg_status; /* [o] messaging level data (optional) */
|
||||
@@ -120,8 +118,9 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
|
||||
char problem; /* 0 -> no problem detected, 1 -> error to report */
|
||||
int pack_id; /* pack_id associated with request */
|
||||
void __user *usr_ptr; /* user provided pointer (in new interface) */
|
||||
unsigned int duration; /* millisecs elapsed since written (req_state==1)
|
||||
or request duration (req_state==2) */
|
||||
unsigned int duration; /* millisecs elapsed since written */
|
||||
/* (req_state==1) or request duration */
|
||||
/* (req_state==2) */
|
||||
int unused;
|
||||
} sg_req_info_t; /* 20 bytes long on i386 */
|
||||
|
||||
@@ -198,6 +197,7 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
|
||||
#define SG_DEFAULT_RETRIES 0
|
||||
|
||||
/* Defaults, commented if they differ from original sg driver */
|
||||
#define SG_DEF_FORCE_LOW_DMA 0 /* was 1 -> memory below 16MB on i386 */
|
||||
#define SG_DEF_FORCE_PACK_ID 0
|
||||
#define SG_DEF_KEEP_ORPHAN 0
|
||||
#define SG_DEF_RESERVED_SIZE SG_SCATTER_SZ /* load time option */
|
||||
@@ -220,8 +220,7 @@ typedef struct sg_req_info Sg_req_info;
|
||||
|
||||
#define SG_MAX_SENSE 16 /* this only applies to the sg_header interface */
|
||||
|
||||
struct sg_header
|
||||
{
|
||||
struct sg_header {
|
||||
int pack_len; /* [o] reply_len (ie useless), ignored as input */
|
||||
int reply_len; /* [i] max length of expected reply (inc. sg_header) */
|
||||
int pack_id; /* [io] id number of packet (use ints >= 0) */
|
||||
@@ -232,10 +231,9 @@ struct sg_header
|
||||
unsigned int host_status:8; /* [o] host status (see "DID" codes) */
|
||||
unsigned int driver_status:8; /* [o] driver status+suggestion */
|
||||
unsigned int other_flags:10; /* unused */
|
||||
unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases:
|
||||
when target_status is CHECK_CONDITION or
|
||||
when target_status is COMMAND_TERMINATED or
|
||||
when (driver_status & DRIVER_SENSE) is true. */
|
||||
unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases: */
|
||||
/* when target_status is CHECK_CONDITION or when target_status is */
|
||||
/* COMMAND_TERMINATED or when (driver_status & DRIVER_SENSE) is true. */
|
||||
}; /* This structure is 36 bytes long on i386 */
|
||||
|
||||
|
||||
4
include/uapi/scsi/ufs/Kbuild
Normal file
4
include/uapi/scsi/ufs/Kbuild
Normal file
@@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
|
||||
# UAPI Header export list
|
||||
header-y += ioctl.h
|
||||
header-y += ufs.h
|
||||
59
include/uapi/scsi/ufs/ioctl.h
Normal file
59
include/uapi/scsi/ufs/ioctl.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef UAPI_UFS_IOCTL_H_
|
||||
#define UAPI_UFS_IOCTL_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* IOCTL opcode for ufs queries has the following opcode after
|
||||
* SCSI_IOCTL_GET_PCI
|
||||
*/
|
||||
#define UFS_IOCTL_QUERY 0x5388
|
||||
|
||||
/**
|
||||
* struct ufs_ioctl_query_data - used to transfer data to and from user via
|
||||
* ioctl
|
||||
* @opcode: type of data to query (descriptor/attribute/flag)
|
||||
* @idn: id of the data structure
|
||||
* @buf_size: number of allocated bytes/data size on return
|
||||
* @buffer: data location
|
||||
*
|
||||
* Received: buffer and buf_size (available space for transferred data)
|
||||
* Submitted: opcode, idn, length, buf_size
|
||||
*/
|
||||
struct ufs_ioctl_query_data {
|
||||
/*
|
||||
* User should select one of the opcode defined in "enum query_opcode".
|
||||
* Please check include/uapi/scsi/ufs/ufs.h for the definition of it.
|
||||
* Note that only UPIU_QUERY_OPCODE_READ_DESC,
|
||||
* UPIU_QUERY_OPCODE_READ_ATTR & UPIU_QUERY_OPCODE_READ_FLAG are
|
||||
* supported as of now. All other query_opcode would be considered
|
||||
* invalid.
|
||||
* As of now only read query operations are supported.
|
||||
*/
|
||||
__u32 opcode;
|
||||
/*
|
||||
* User should select one of the idn from "enum flag_idn" or "enum
|
||||
* attr_idn" or "enum desc_idn" based on whether opcode above is
|
||||
* attribute, flag or descriptor.
|
||||
* Please check include/uapi/scsi/ufs/ufs.h for the definition of it.
|
||||
*/
|
||||
__u8 idn;
|
||||
/*
|
||||
* User should specify the size of the buffer (buffer[0] below) where
|
||||
* it wants to read the query data (attribute/flag/descriptor).
|
||||
* As we might end up reading less data then what is specified in
|
||||
* buf_size. So we are updating buf_size to what exactly we have read.
|
||||
*/
|
||||
__u16 buf_size;
|
||||
/*
|
||||
* placeholder for the start of the data buffer where kernel will copy
|
||||
* the query data (attribute/flag/descriptor) read from the UFS device
|
||||
* Note:
|
||||
* For Read/Write Attribute you will have to allocate 4 bytes
|
||||
* For Read/Write Flag you will have to allocate 1 byte
|
||||
*/
|
||||
__u8 buffer[0];
|
||||
};
|
||||
|
||||
#endif /* UAPI_UFS_IOCTL_H_ */
|
||||
79
include/uapi/scsi/ufs/ufs.h
Normal file
79
include/uapi/scsi/ufs/ufs.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef UAPI_UFS_H_
|
||||
#define UAPI_UFS_H_
|
||||
|
||||
#define MAX_QUERY_IDN 0x12
|
||||
|
||||
/* Flag idn for Query Requests*/
|
||||
enum flag_idn {
|
||||
QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
|
||||
QUERY_FLAG_IDN_PERMANENT_WPE = 0x02,
|
||||
QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
|
||||
QUERY_FLAG_IDN_BKOPS_EN = 0x04,
|
||||
QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05,
|
||||
QUERY_FLAG_IDN_PURGE_ENABLE = 0x06,
|
||||
QUERY_FLAG_IDN_RESERVED2 = 0x07,
|
||||
QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08,
|
||||
QUERY_FLAG_IDN_BUSY_RTC = 0x09,
|
||||
QUERY_FLAG_IDN_RESERVED3 = 0x0A,
|
||||
QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B,
|
||||
};
|
||||
|
||||
/* Attribute idn for Query requests */
|
||||
enum attr_idn {
|
||||
QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
|
||||
QUERY_ATTR_IDN_RESERVED = 0x01,
|
||||
QUERY_ATTR_IDN_POWER_MODE = 0x02,
|
||||
QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
|
||||
QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
|
||||
QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
|
||||
QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
|
||||
QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
|
||||
QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
|
||||
QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
|
||||
QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
|
||||
QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
|
||||
QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
|
||||
QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
|
||||
QUERY_ATTR_IDN_EE_STATUS = 0x0E,
|
||||
QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
|
||||
QUERY_ATTR_IDN_CNTX_CONF = 0x10,
|
||||
QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
|
||||
QUERY_ATTR_IDN_RESERVED2 = 0x12,
|
||||
QUERY_ATTR_IDN_RESERVED3 = 0x13,
|
||||
QUERY_ATTR_IDN_FFU_STATUS = 0x14,
|
||||
QUERY_ATTR_IDN_PSA_STATE = 0x15,
|
||||
QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16,
|
||||
};
|
||||
|
||||
#define QUERY_ATTR_IDN_BOOT_LU_EN_MAX 0x02
|
||||
|
||||
/* Descriptor idn for Query requests */
|
||||
enum desc_idn {
|
||||
QUERY_DESC_IDN_DEVICE = 0x0,
|
||||
QUERY_DESC_IDN_CONFIGURATION = 0x1,
|
||||
QUERY_DESC_IDN_UNIT = 0x2,
|
||||
QUERY_DESC_IDN_RFU_0 = 0x3,
|
||||
QUERY_DESC_IDN_INTERCONNECT = 0x4,
|
||||
QUERY_DESC_IDN_STRING = 0x5,
|
||||
QUERY_DESC_IDN_RFU_1 = 0x6,
|
||||
QUERY_DESC_IDN_GEOMETRY = 0x7,
|
||||
QUERY_DESC_IDN_POWER = 0x8,
|
||||
QUERY_DESC_IDN_HEALTH = 0x9,
|
||||
QUERY_DESC_IDN_MAX,
|
||||
};
|
||||
|
||||
/* UTP QUERY Transaction Specific Fields OpCode */
|
||||
enum query_opcode {
|
||||
UPIU_QUERY_OPCODE_NOP = 0x0,
|
||||
UPIU_QUERY_OPCODE_READ_DESC = 0x1,
|
||||
UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
|
||||
UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
|
||||
UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
|
||||
UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
|
||||
UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
|
||||
UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
|
||||
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
|
||||
UPIU_QUERY_OPCODE_MAX,
|
||||
};
|
||||
#endif /* UAPI_UFS_H_ */
|
||||
@@ -1608,6 +1608,20 @@ config FAIL_MMC_REQUEST
|
||||
and to test how the mmc host driver handles retries from
|
||||
the block device.
|
||||
|
||||
config UFS_FAULT_INJECTION
|
||||
bool "Fault-injection capability for UFS IO"
|
||||
select DEBUG_FS
|
||||
depends on FAULT_INJECTION && SCSI_UFSHCD
|
||||
help
|
||||
Provide fault-injection capability for UFS IO.
|
||||
This will make the UFS host controller driver to randomly
|
||||
abort ongoing commands in the host controller, update OCS
|
||||
field according to the injected fatal error and can also
|
||||
forcefully hang the command indefinitely till upper layer
|
||||
timeout occurs. This is useful to test error handling in
|
||||
the UFS controller driver and test how the driver handles
|
||||
the retries from block/SCSI mid layer.
|
||||
|
||||
config FAULT_INJECTION_STACKTRACE_FILTER
|
||||
bool "stacktrace filter for fault-injection capabilities"
|
||||
depends on FAULT_INJECTION_DEBUG_FS && STACKTRACE_SUPPORT
|
||||
|
||||
Reference in New Issue
Block a user