Re: [PATCH v4] scsi: ufs: add support of generic PHY and ICE in Qualcomm chips

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Reviewed-by: Dolev Raviv <draviv@xxxxxxxxxxxxxx>

> In this change we add support to the generic PHY framework.
> Two UFS phys are implemented:
> qmp-20nm and qmp-28nm.
>
> Also, the files in this change implement the UFS HW (controller & PHY)
> specific behavior in Qualcomm chips.
> Relocation of a few header files is needed in order to expose routines
> and data structures between PHY driver and UFS driver.
>
> Also, this change include the implementation of Inline Crypto Engine (ICE)
> in Qualcomm chips.
>
> Signed-off-by: Yaniv Gardi <ygardi@xxxxxxxxxxxxxx>
> ---
>  Documentation/devicetree/bindings/ufs/ufs-msm.txt  |   49 +
>  .../devicetree/bindings/ufs/ufshcd-pltfrm.txt      |  113 +-
>  drivers/phy/Makefile                               |    3 +
>  drivers/phy/phy-qcom-ufs-qmp-20nm.c                |  266 +++++
>  drivers/phy/phy-qcom-ufs-qmp-20nm.h                |  232 ++++
>  drivers/phy/phy-qcom-ufs-qmp-28nm.c                |  331 ++++++
>  drivers/phy/phy-qcom-ufs-qmp-28nm.h                |  738 ++++++++++++
>  drivers/phy/phy-qcom-ufs.c                         |  777 +++++++++++++
>  drivers/scsi/ufs/Kconfig                           |   24 +
>  drivers/scsi/ufs/Makefile                          |    2 +
>  drivers/scsi/ufs/ufs-qcom-ice.c                    |  522 +++++++++
>  drivers/scsi/ufs/ufs-qcom-ice.h                    |  113 ++
>  drivers/scsi/ufs/ufs-qcom.c                        | 1204
> ++++++++++++++++++++
>  drivers/scsi/ufs/ufs.h                             |  491 --------
>  drivers/scsi/ufs/ufshcd-pci.c                      |    2 +-
>  drivers/scsi/ufs/ufshcd-pltfrm.c                   |    2 +-
>  drivers/scsi/ufs/ufshcd.c                          |   97 +-
>  drivers/scsi/ufs/ufshcd.h                          |  596 ----------
>  drivers/scsi/ufs/unipro.h                          |  207 ----
>  include/linux/phy/phy-qcom-ufs.h                   |  205 ++++
>  include/linux/scsi/ufs/ufs-qcom.h                  |  210 ++++
>  include/linux/scsi/ufs/ufs.h                       |  491 ++++++++
>  include/linux/scsi/ufs/ufshcd.h                    |  654 +++++++++++
>  include/linux/scsi/ufs/unipro.h                    |  207 ++++
>  24 files changed, 6232 insertions(+), 1304 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/ufs/ufs-msm.txt
>  create mode 100644 drivers/phy/phy-qcom-ufs-qmp-20nm.c
>  create mode 100644 drivers/phy/phy-qcom-ufs-qmp-20nm.h
>  create mode 100644 drivers/phy/phy-qcom-ufs-qmp-28nm.c
>  create mode 100644 drivers/phy/phy-qcom-ufs-qmp-28nm.h
>  create mode 100644 drivers/phy/phy-qcom-ufs.c
>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.c
>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.h
>  create mode 100644 drivers/scsi/ufs/ufs-qcom.c
>  delete mode 100644 drivers/scsi/ufs/ufs.h
>  delete mode 100644 drivers/scsi/ufs/ufshcd.h
>  delete mode 100644 drivers/scsi/ufs/unipro.h
>  create mode 100644 include/linux/phy/phy-qcom-ufs.h
>  create mode 100644 include/linux/scsi/ufs/ufs-qcom.h
>  create mode 100644 include/linux/scsi/ufs/ufs.h
>  create mode 100644 include/linux/scsi/ufs/ufshcd.h
>  create mode 100644 include/linux/scsi/ufs/unipro.h
>
> diff --git a/Documentation/devicetree/bindings/ufs/ufs-msm.txt
> b/Documentation/devicetree/bindings/ufs/ufs-msm.txt
> new file mode 100644
> index 0000000..a9bf139
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/ufs/ufs-msm.txt
> @@ -0,0 +1,49 @@
> +* 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,ufs-phy-qmp-28nm",
> +                      "qcom,ufs-phy-qmp-20nm" or "qcom,ufs-phy-qmp-14nm"
> +                      according to the relevant phy in use
> +- reg               : should contain PHY register address space
> (mandatory),
> +                      device PHY control register map (optional).
> +- reg-names         : indicates various resources passed to driver (via
> reg proptery) by name.
> +                      Required "reg-names" is "phy_mem" and
> "dev_ref_clk_ctrl_mem" is optional.
> +- #phy-cells        : This property shall be set to 0
> +- vdda-phy-supply   : phandle to main PHY supply for analog domain
> +- vdda-pll-supply   : phandle to PHY PLL and Power-Gen block power supply
> +- clocks	    : List of phandle and clock specifier pairs
> +- clock-names       : List of clock input name strings sorted in the same
> +		      order as the clocks property. "ref_clk_src", "ref_clk",
> +		      "tx_iface_clk" & "rx_iface_clk" are mandatory but
> +		      "ref_clk_parent" is optional
> +
> +Optional properties:
> +- vdda-phy-max-microamp : specifies max. load that can be drawn from phy
> supply
> +- vdda-pll-max-microamp : specifies max. load that can be drawn from pll
> supply
> +- vddp-ref-clk-supply   : phandle to UFS device ref_clk pad power supply
> +- vddp-ref-clk-max-microamp : specifies max. load that can be drawn from
> this supply
> +- vddp-ref-clk-always-on : specifies if this supply needs to be kept
> always on
> +
> +Example:
> +
> +	ufsphy1: ufsphy@0xfc597000 {
> +		compatible = "qcom,ufs-phy-qmp-28nm";
> +		reg = <0xfc597000 0x800>, <0xfd512074 0x4>;
> +		reg-names = "phy_mem", "dev_ref_clk_ctrl_mem";
> +		#phy-cells = <0>;
> +		vdda-phy-supply = <&pma8084_l4>;
> +		vdda-pll-supply = <&pma8084_l12>;
> +		vdda-phy-max-microamp = <50000>;
> +		vdda-pll-max-microamp = <1000>;
> +	};
> +
> +	ufshc@0xfc598000 {
> +		...
> +		phys = <&ufsphy1>;
> +	};
> diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> index 5357919..3512616 100644
> --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> @@ -4,11 +4,14 @@ UFSHC nodes are defined to describe on-chip UFS host
> controllers.
>  Each UFS controller instance should have its own node.
>
>  Required properties:
> -- compatible        : compatible list, contains "jedec,ufs-1.1"
> +- compatible        : compatible list, contains "jedec,ufs-1.1",
> "qcom,ufshc"
>  - interrupts        : <interrupt mapping for UFS host controller IRQ>
>  - reg               : <registers mapping>
>
>  Optional properties:
> +- phys                  : phandle to UFS PHY node
> +- phy-names             : the string "ufsphy" when is found in a node,
> along
> +                          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
>  - vccq-supply           : phandle to VCCQ supply regulator node
> @@ -31,6 +34,15 @@ 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 suppported:
> +			  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
>
>  Note: If above properties are not defined it can be assumed that the
> supply
>  regulators or clocks are always on.
> @@ -41,6 +53,8 @@ Example:
>  		reg = <0xfc598000 0x800>;
>  		interrupts = <0 28 0>;
>
> +		ufs-phy = <&ufsphy>;
> +		ufs-qcom-crypto = <&ufs_ice>;
>  		vdd-hba-supply = <&xxx_reg0>;
>  		vdd-hba-fixed-regulator;
>  		vcc-supply = <&xxx_reg1>;
> @@ -54,4 +68,101 @@ Example:
>  		clocks = <&core 0>, <&ref 0>, <&iface 0>;
>  		clock-names = "core_clk", "ref_clk", "iface_clk";
>  		freq-table-hz = <100000000 200000000>, <0 0>, <0 0>;
> +		rpm-level = <3>;
> +		spm-level = <5>;
> +	};
> +
> +==== 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.
> +
> +- qcom,cpu-dma-latency-us: optional parameter specifying the allowed
> CPU-DMA
> +  latency parameter for PM QOS, in units of microseconds. If this
> parameter is
> +  not specified a default of 200us is used.
> +- qcom,cpu-affinity: this is a string that specifies the pm QoS request
> type.
> +  The supported cpu affinity modes are:
> +  "all_cores" - PM_QOS_REQ_ALL_CORES is applicable to all CPU cores that
> are
> +  online and this would have a power impact when there are more number of
> CPUs.
> +  "affine_irq" - PM_QOS_REQ_AFFINE_IRQ request type shall update/apply
> the vote
> +  only to that CPU to which this IRQ's affinity is set to.
> +  "affine_cores" - PM_QOS_REQ_AFFINE_CORES request type is used for
> targets that
> +  have little cluster and will update/apply the vote to all the cores in
> the
> +  little cluster.
> +  The default CPU affinity mode is PM_QOS_REQ_AFFINE_IRQ.
> +
> +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";
>  	};
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index c4590fc..4fb782c 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -31,3 +31,6 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+=
> phy-spear1340-miphy.o
>  obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
>  obj-$(CONFIG_PHY_STIH407_USB)		+= phy-stih407-usb.o
>  obj-$(CONFIG_PHY_STIH41X_USB)		+= phy-stih41x-usb.o
> +obj-$(CONFIG_SCSI_UFS_QCOM) 	+= phy-qcom-ufs.o
> +obj-$(CONFIG_SCSI_UFS_QCOM) 	+= phy-qcom-ufs-qmp-28nm.o
> +obj-$(CONFIG_SCSI_UFS_QCOM) 	+= phy-qcom-ufs-qmp-20nm.o
> diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c
> b/drivers/phy/phy-qcom-ufs-qmp-20nm.c
> new file mode 100644
> index 0000000..31a3b54
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c
> @@ -0,0 +1,266 @@
> +/*
> + * Copyright (c) 2013-2014, 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/phy/phy-qcom-ufs.h>
> +#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)
> +{
> +	struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
> +	int tbl_size_A, tbl_size_B;
> +	int rate = UFS_QCOM_LIMIT_HS_RATE;
> +	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, rate);
> +
> +	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_qmp_20nm *phy = phy_get_drvdata(generic_phy);
> +	struct ufs_qcom_phy *phy_common = &phy->common_cfg;
> +	int err = 0;
> +
> +	err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
> +	if (err) {
> +		dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
> +	if (err) {
> +		dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed
> %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common);
> +
> +out:
> +	return err;
> +}
> +
> +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;
> +}
> +
> +struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
> +	.init		= ufs_qcom_phy_qmp_20nm_init,
> +	.exit		= ufs_qcom_phy_exit,
> +	.power_on	= ufs_qcom_phy_power_on,
> +	.power_off	= ufs_qcom_phy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
> +	.calibrate_phy		= ufs_qcom_phy_qmp_20nm_phy_calibrate,
> +	.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;
> +	int err = 0;
> +
> +	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
> +	if (!phy) {
> +		dev_err(dev, "%s: failed to allocate phy\n", __func__);
> +		err = -ENOMEM;
> +		goto out;
> +	}
> +
> +	generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
> +				&ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops);
> +
> +	if (!generic_phy) {
> +		dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
> +			__func__);
> +		err = -EIO;
> +		goto out;
> +	}
> +
> +	phy_set_drvdata(generic_phy, phy);
> +
> +	strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
> +			sizeof(phy->common_cfg.name));
> +
> +out:
> +	return err;
> +}
> +
> +static int ufs_qcom_phy_qmp_20nm_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct phy *generic_phy = to_phy(dev);
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
> +	int err = 0;
> +
> +	err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
> +	if (err)
> +		dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
> +			__func__, err);
> +
> +	return err;
> +}
> +
> +static const struct of_device_id ufs_qcom_phy_qmp_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,
> +	.remove = ufs_qcom_phy_qmp_20nm_remove,
> +	.driver = {
> +		.of_match_table = ufs_qcom_phy_qmp_20nm_of_match,
> +		.name = "ufs_qcom_phy_qmp_20nm",
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +module_platform_driver(ufs_qcom_phy_qmp_20nm_driver);
> +
> +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.h
> b/drivers/phy/phy-qcom-ufs-qmp-20nm.h
> new file mode 100644
> index 0000000..895c259
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.h
> @@ -0,0 +1,232 @@
> +/*
> + * Copyright (c) 2013-2014, 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 <linux/phy/phy-qcom-ufs.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_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(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(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
> diff --git a/drivers/phy/phy-qcom-ufs-qmp-28nm.c
> b/drivers/phy/phy-qcom-ufs-qmp-28nm.c
> new file mode 100644
> index 0000000..512b1c9
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-ufs-qmp-28nm.c
> @@ -0,0 +1,331 @@
> +/*
> + * Copyright (c) 2013-2014, 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/phy/phy.h>
> +
> +#include <linux/phy/phy-qcom-ufs.h>
> +#include "phy-qcom-ufs-qmp-28nm.h"
> +
> +#define UFS_PHY_NAME "ufs_qcom_phy_qmp_28nm"
> +
> +static
> +void ufs_qcom_phy_qmp_28nm_power_control(struct ufs_qcom_phy *phy, bool
> val)
> +{
> +	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. This also ensures
> +		 * that PHY is out of power collapse before enabling the
> +		 * SIGDET.
> +		 */
> +		mb();
> +		if (phy->quirks & UFS_QCOM_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE) {
> +			writel_relaxed(0xC0,
> +				phy->mmio + QSERDES_RX_SIGDET_CNTRL(0));
> +			writel_relaxed(0xC0,
> +				phy->mmio + QSERDES_RX_SIGDET_CNTRL(1));
> +			/*
> +			 * make sure that SIGDET is enabled before proceeding
> +			 * further.
> +			 */
> +			 mb();
> +		}
> +	} else {
> +		 if (phy->quirks &
> +				UFS_QCOM_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE) {
> +			writel_relaxed(0x0,
> +				phy->mmio + QSERDES_RX_SIGDET_CNTRL(0));
> +			writel_relaxed(0x0,
> +				phy->mmio + QSERDES_RX_SIGDET_CNTRL(1));
> +			/*
> +			 * Ensure that SIGDET is disabled before PHY 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_28nm_advertise_quirks(struct ufs_qcom_phy
> *phy_common)
> +{
> +	phy_common->quirks = UFS_QCOM_PHY_QUIRK_CFG_RESTORE
> +				| UFS_QCOM_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE;
> +}
> +
> +static int ufs_qcom_phy_qmp_28nm_init(struct phy *generic_phy)
> +{
> +	struct ufs_qcom_phy_qmp_28nm *phy = phy_get_drvdata(generic_phy);
> +	struct ufs_qcom_phy *phy_common = &phy->common_cfg;
> +	int err = 0;
> +
> +	err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
> +	if (err) {
> +		dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
> +	if (err) {
> +		dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed
> %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	ufs_qcom_phy_qmp_28nm_advertise_quirks(phy_common);
> +
> +out:
> +	return err;
> +}
> +
> +static int ufs_qcom_phy_qmp_28nm_calibrate(struct ufs_qcom_phy
> *ufs_qcom_phy)
> +{
> +	struct ufs_qcom_phy_calibration *tbl_A = NULL, *tbl_B;
> +	int tbl_size_A = 0, tbl_size_B;
> +	int rate = UFS_QCOM_LIMIT_HS_RATE;
> +	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 == 0x001) && (step == 0x0000)) {
> +		tbl_size_A = ARRAY_SIZE(phy_cal_table_ctrl_1_1_0_rate_A);
> +		tbl_A = phy_cal_table_ctrl_1_1_0_rate_A;
> +	} else if ((major == 0x1) && (minor == 0x001) && (step == 0x0001)) {
> +		tbl_size_A = ARRAY_SIZE(phy_cal_table_ctrl_1_1_1_rate_A);
> +		tbl_A = phy_cal_table_ctrl_1_1_1_rate_A;
> +	}
> +
> +	tbl_B = phy_cal_table_rate_B;
> +	tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
> +
> +	err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A,
> +			      tbl_B, tbl_size_B, rate);
> +	if (err)
> +		dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n",
> +			__func__, err);
> +
> +	return err;
> +}
> +
> +static
> +u32 ufs_qcom_phy_qmp_28nm_read_attr(struct ufs_qcom_phy *phy_common, u32
> attr)
> +
> +{
> +	u32 l0, l1;
> +
> +	writel_relaxed(attr, phy_common->mmio + UFS_PHY_RMMI_ATTRID);
> +	/* Read attribute value for both lanes */
> +	writel_relaxed((UFS_PHY_RMMI_CFGRD_L0 | UFS_PHY_RMMI_CFGRD_L1),
> +		       phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
> +
> +	l0 = readl_relaxed(phy_common->mmio + UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS);
> +	l1 = readl_relaxed(phy_common->mmio + UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS);
> +	/* Both lanes should have the same value for same attribute type */
> +	if (unlikely(l0 != l1))
> +		dev_warn(phy_common->dev, "%s: attr 0x%x values are not same for Lane-0
> and Lane-1, l0=0x%x, l1=0x%x",
> +				__func__, attr, l0, l1);
> +
> +	/* must clear now */
> +	writel_relaxed(0x00, phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
> +
> +	return l0;
> +}
> +
> +static void
> +ufs_qcom_phy_qmp_28nm_write_attr(struct ufs_qcom_phy *phy_common,
> +				 u32 attr, u32 val)
> +{
> +	writel_relaxed(attr, phy_common->mmio + UFS_PHY_RMMI_ATTRID);
> +	writel_relaxed(val, phy_common->mmio + UFS_PHY_RMMI_ATTRWRVAL);
> +	/* update attribute for both lanes */
> +	writel_relaxed((UFS_PHY_RMMI_CFGWR_L0 | UFS_PHY_RMMI_CFGWR_L1),
> +		       phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
> +	if (is_mphy_tx_attr(attr))
> +		writel_relaxed((UFS_PHY_RMMI_TX_CFGUPDT_L0 |
> +				UFS_PHY_RMMI_TX_CFGUPDT_L1),
> +			       phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
> +	else
> +		writel_relaxed((UFS_PHY_RMMI_RX_CFGUPDT_L0 |
> +				UFS_PHY_RMMI_RX_CFGUPDT_L1),
> +			       phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
> +
> +	writel_relaxed(0x00, phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
> +}
> +
> +static
> +void ufs_qcom_phy_qmp_28nm_save_configuration(struct ufs_qcom_phy
> *phy_common)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(cached_phy_regs); i++)
> +		cached_phy_regs[i].cfg_value =
> +			readl_relaxed(phy_common->mmio +
> +				      cached_phy_regs[i].reg_offset);
> +
> +	for (i = 0; i < ARRAY_SIZE(cached_phy_attr); i++)
> +		cached_phy_attr[i].value =
> +			ufs_qcom_phy_qmp_28nm_read_attr(phy_common,
> +					cached_phy_attr[i].att);
> +}
> +
> +static void
> +ufs_qcom_phy_qmp_28nm_restore_configuration(struct ufs_qcom_phy
> *phy_common)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(cached_phy_attr); i++)
> +		ufs_qcom_phy_qmp_28nm_write_attr(phy_common,
> +			cached_phy_attr[i].att, cached_phy_attr[i].value);
> +}
> +
> +static
> +void ufs_qcom_phy_qmp_28nm_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_28nm_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_28nm_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: phy init failed, %d\n",
> +			__func__, err);
> +
> +	return err;
> +}
> +
> +struct phy_ops ufs_qcom_phy_qmp_28nm_phy_ops = {
> +	.init		= ufs_qcom_phy_qmp_28nm_init,
> +	.exit		= ufs_qcom_phy_exit,
> +	.power_on	= ufs_qcom_phy_power_on,
> +	.power_off	= ufs_qcom_phy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +struct ufs_qcom_phy_specific_ops phy_28nm_ops = {
> +	.calibrate_phy		= ufs_qcom_phy_qmp_28nm_calibrate,
> +	.start_serdes		= ufs_qcom_phy_qmp_28nm_start_serdes,
> +	.save_configuration	= ufs_qcom_phy_qmp_28nm_save_configuration,
> +	.restore_configuration	= ufs_qcom_phy_qmp_28nm_restore_configuration,
> +	.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_28nm_is_pcs_ready,
> +	.set_tx_lane_enable	= ufs_qcom_phy_qmp_28nm_set_tx_lane_enable,
> +	.power_control		= ufs_qcom_phy_qmp_28nm_power_control,
> +};
> +
> +static int ufs_qcom_phy_qmp_28nm_probe(struct platform_device *pdev)
> +{
> +	struct ufs_qcom_phy_qmp_28nm *phy;
> +	struct device *dev = &pdev->dev;
> +	int err = 0;
> +	struct phy *generic_phy;
> +
> +	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
> +	if (!phy) {
> +		err = -ENOMEM;
> +		dev_err(dev, "%s: failed to allocate phy\n", __func__);
> +		goto out;
> +	}
> +
> +	phy->common_cfg.cached_regs =
> +			(struct ufs_qcom_phy_calibration *)cached_phy_regs;
> +	phy->common_cfg.cached_regs_table_size =
> +				ARRAY_SIZE(cached_phy_regs);
> +
> +	generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
> +				&ufs_qcom_phy_qmp_28nm_phy_ops, &phy_28nm_ops);
> +
> +	if (!generic_phy) {
> +		dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
> +			__func__);
> +		err = -EIO;
> +		goto out;
> +	}
> +
> +	phy_set_drvdata(generic_phy, phy);
> +
> +	strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
> +			sizeof(phy->common_cfg.name));
> +
> +out:
> +	return err;
> +}
> +
> +static int ufs_qcom_phy_qmp_28nm_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct phy *generic_phy = to_phy(dev);
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
> +	int err = 0;
> +
> +	err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
> +	if (err)
> +		dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
> +			__func__, err);
> +
> +	return err;
> +}
> +
> +static const struct of_device_id ufs_qcom_phy_qmp_28nm_of_match[] = {
> +	{.compatible = "qcom,ufs-phy-qmp-28nm"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_28nm_of_match);
> +
> +static struct platform_driver ufs_qcom_phy_qmp_28nm_driver = {
> +	.probe = ufs_qcom_phy_qmp_28nm_probe,
> +	.remove = ufs_qcom_phy_qmp_28nm_remove,
> +	.driver = {
> +		.of_match_table = ufs_qcom_phy_qmp_28nm_of_match,
> +		.name = "ufs_qcom_phy_qmp_28nm",
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +module_platform_driver(ufs_qcom_phy_qmp_28nm_driver);
> +
> +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 28nm");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/phy/phy-qcom-ufs-qmp-28nm.h
> b/drivers/phy/phy-qcom-ufs-qmp-28nm.h
> new file mode 100644
> index 0000000..b406d99
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-ufs-qmp-28nm.h
> @@ -0,0 +1,738 @@
> +/*
> + * Copyright (c) 2013-2014, 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_28NM_H_
> +#define UFS_QCOM_PHY_QMP_28NM_H_
> +
> +#include <linux/phy/phy-qcom-ufs.h>
> +
> +/* QCOM UFS PHY control registers */
> +
> +#define COM_OFF(x)     (0x000 + x)
> +#define PHY_OFF(x)     (0x700 + x)
> +#define TX_OFF(n, x)   (0x100 + (0x400 * n) + x)
> +#define RX_OFF(n, x)   (0x200 + (0x400 * n) + 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_CMN_MODE                                COM_OFF(0x08)
> +#define QSERDES_COM_IE_TRIM                                 COM_OFF(0x0C)
> +#define QSERDES_COM_IP_TRIM                                 COM_OFF(0x10)
> +#define QSERDES_COM_PLL_CNTRL                               COM_OFF(0x14)
> +#define QSERDES_COM_PLL_IP_SETI                             COM_OFF(0x18)
> +#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL                    COM_OFF(0x1C)
> +#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_ATB_SEL1                                COM_OFF(0x30)
> +#define QSERDES_COM_ATB_SEL2                                COM_OFF(0x34)
> +#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_RES_TRIM_OFFSET                         COM_OFF(0x54)
> +#define QSERDES_COM_BGTC                                    COM_OFF(0x58)
> +#define QSERDES_COM_PLL_TEST_UPDN_RESTRIMSTEP               COM_OFF(0x5C)
> +#define QSERDES_COM_PLL_VCO_TUNE                            COM_OFF(0x60)
> +#define QSERDES_COM_DEC_START1                              COM_OFF(0x64)
> +#define QSERDES_COM_PLL_AMP_OS                              COM_OFF(0x68)
> +#define QSERDES_COM_SSC_EN_CENTER                           COM_OFF(0x6C)
> +#define QSERDES_COM_SSC_ADJ_PER1                            COM_OFF(0x70)
> +#define QSERDES_COM_SSC_ADJ_PER2                            COM_OFF(0x74)
> +#define QSERDES_COM_SSC_PER1                                COM_OFF(0x78)
> +#define QSERDES_COM_SSC_PER2                                COM_OFF(0x7C)
> +#define QSERDES_COM_SSC_STEP_SIZE1                          COM_OFF(0x80)
> +#define QSERDES_COM_SSC_STEP_SIZE2                          COM_OFF(0x84)
> +#define QSERDES_COM_RES_TRIM_SEARCH                         COM_OFF(0x88)
> +#define QSERDES_COM_RES_TRIM_FREEZE                         COM_OFF(0x8C)
> +#define QSERDES_COM_RES_TRIM_EN_VCOCALDONE                  COM_OFF(0x90)
> +#define QSERDES_COM_FAUX_EN                                 COM_OFF(0x94)
> +#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_PLL_FREQUPDATE                          COM_OFF(0xB4)
> +#define QSERDES_COM_PLL_VCO_HIGH                            COM_OFF(0xB8)
> +#define QSERDES_COM_RESET_SM                                COM_OFF(0xBC)
> +
> +/* UFS PHY registers */
> +#define UFS_PHY_PHY_START                                   PHY_OFF(0x00)
> +#define UFS_PHY_POWER_DOWN_CONTROL                          PHY_OFF(0x04)
> +#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_TIMER_100US_SYSCLK_STEPS_MSB                PHY_OFF(0x18)
> +#define UFS_PHY_TIMER_100US_SYSCLK_STEPS_LSB                PHY_OFF(0x1C)
> +#define UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB                PHY_OFF(0x20)
> +#define UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB                PHY_OFF(0x24)
> +#define UFS_PHY_LINE_RESET_TIME                             PHY_OFF(0x28)
> +#define UFS_PHY_LINE_RESET_GRANULARITY                      PHY_OFF(0x2C)
> +#define UFS_PHY_CONTROLSYM_ONE_HOT_DISABLE                  PHY_OFF(0x30)
> +#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_TX_LANE_ENABLE                              PHY_OFF(0x44)
> +#define UFS_PHY_TSYNC_RSYNC_CNTL                            PHY_OFF(0x48)
> +#define UFS_PHY_RETIME_BUFFER_EN                            PHY_OFF(0x4C)
> +#define UFS_PHY_PLL_CNTL                                    PHY_OFF(0x50)
> +#define UFS_PHY_TX_LARGE_AMP_DRV_LVL                        PHY_OFF(0x54)
> +#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL                   PHY_OFF(0x58)
> +#define UFS_PHY_TX_SMALL_AMP_DRV_LVL                        PHY_OFF(0x5C)
> +#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_OMC_STATUS_RDVAL                            PHY_OFF(0x68)
> +#define UFS_PHY_RX_SYNC_WAIT_TIME                           PHY_OFF(0x6C)
> +#define UFS_PHY_L0_BIST_CTRL                                PHY_OFF(0x70)
> +#define UFS_PHY_L1_BIST_CTRL                                PHY_OFF(0x74)
> +#define UFS_PHY_BIST_PRBS_POLY0                             PHY_OFF(0x78)
> +#define UFS_PHY_BIST_PRBS_POLY1                             PHY_OFF(0x7C)
> +#define UFS_PHY_BIST_PRBS_SEED0                             PHY_OFF(0x80)
> +#define UFS_PHY_BIST_PRBS_SEED1                             PHY_OFF(0x84)
> +#define UFS_PHY_BIST_FIXED_PAT_CTRL                         PHY_OFF(0x88)
> +#define UFS_PHY_BIST_FIXED_PAT0_DATA                        PHY_OFF(0x8C)
> +#define UFS_PHY_BIST_FIXED_PAT1_DATA                        PHY_OFF(0x90)
> +#define UFS_PHY_BIST_FIXED_PAT2_DATA                        PHY_OFF(0x94)
> +#define UFS_PHY_BIST_FIXED_PAT3_DATA                        PHY_OFF(0x98)
> +#define UFS_PHY_TX_HSGEAR_CAPABILITY                        PHY_OFF(0x9C)
> +#define UFS_PHY_TX_PWMGEAR_CAPABILITY                       PHY_OFF(0xA0)
> +#define UFS_PHY_TX_AMPLITUDE_CAPABILITY                     PHY_OFF(0xA4)
> +#define UFS_PHY_TX_EXTERNALSYNC_CAPABILITY                  PHY_OFF(0xA8)
> +#define UFS_PHY_TX_HS_UNTERMINATED_LINE_DRIVE_CAPABILITY    PHY_OFF(0xAC)
> +#define UFS_PHY_TX_LS_TERMINATED_LINE_DRIVE_CAPABILITY      PHY_OFF(0xB0)
> +#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xB4)
> +#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xB8)
> +#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY          PHY_OFF(0xBC)
> +#define UFS_PHY_TX_REF_CLOCK_SHARED_CAPABILITY              PHY_OFF(0xC0)
> +#define UFS_PHY_TX_PHY_MAJORMINOR_RELEASE_CAPABILITY        PHY_OFF(0xC4)
> +#define UFS_PHY_TX_PHY_EDITORIAL_RELEASE_CAPABILITY         PHY_OFF(0xC8)
> +#define UFS_PHY_TX_HIBERN8TIME_CAPABILITY                   PHY_OFF(0xCC)
> +#define UFS_PHY_RX_HSGEAR_CAPABILITY                        PHY_OFF(0xD0)
> +#define UFS_PHY_RX_PWMGEAR_CAPABILITY                       PHY_OFF(0xD4)
> +#define UFS_PHY_RX_HS_UNTERMINATED_CAPABILITY               PHY_OFF(0xD8)
> +#define UFS_PHY_RX_LS_TERMINATED_CAPABILITY                 PHY_OFF(0xDC)
> +#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xE0)
> +#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xE4)
> +#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY          PHY_OFF(0xE8)
> +#define UFS_PHY_RX_REF_CLOCK_SHARED_CAPABILITY              PHY_OFF(0xEC)
> +#define UFS_PHY_RX_HS_G1_SYNC_LENGTH_CAPABILITY             PHY_OFF(0xF0)
> +#define UFS_PHY_RX_HS_G1_PREPARE_LENGTH_CAPABILITY          PHY_OFF(0xF4)
> +#define UFS_PHY_RX_LS_PREPARE_LENGTH_CAPABILITY             PHY_OFF(0xF8)
> +#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_PHY_MAJORMINOR_RELEASE_CAPABILITY
> PHY_OFF(0x104)
> +#define UFS_PHY_RX_PHY_EDITORIAL_RELEASE_CAPABILITY
> PHY_OFF(0x108)
> +#define UFS_PHY_RX_HIBERN8TIME_CAPABILITY
> PHY_OFF(0x10C)
> +#define UFS_PHY_RX_HS_G2_SYNC_LENGTH_CAPABILITY
> PHY_OFF(0x110)
> +#define UFS_PHY_RX_HS_G3_SYNC_LENGTH_CAPABILITY
> PHY_OFF(0x114)
> +#define UFS_PHY_RX_HS_G2_PREPARE_LENGTH_CAPABILITY
> PHY_OFF(0x118)
> +#define UFS_PHY_RX_HS_G3_PREPARE_LENGTH_CAPABILITY
> PHY_OFF(0x11C)
> +#define UFS_PHY_DEBUG_BUS_SEL
> PHY_OFF(0x120)
> +#define UFS_PHY_DEBUG_BUS_0_STATUS_CHK
> PHY_OFF(0x124)
> +#define UFS_PHY_DEBUG_BUS_1_STATUS_CHK
> PHY_OFF(0x128)
> +#define UFS_PHY_DEBUG_BUS_2_STATUS_CHK
> PHY_OFF(0x12C)
> +#define UFS_PHY_DEBUG_BUS_3_STATUS_CHK
> PHY_OFF(0x130)
> +#define UFS_PHY_PCS_READY_STATUS
> PHY_OFF(0x134)
> +#define UFS_PHY_L0_BIST_CHK_ERR_CNT_L_STATUS
> PHY_OFF(0x138)
> +#define UFS_PHY_L0_BIST_CHK_ERR_CNT_H_STATUS
> PHY_OFF(0x13C)
> +#define UFS_PHY_L1_BIST_CHK_ERR_CNT_L_STATUS
> PHY_OFF(0x140)
> +#define UFS_PHY_L1_BIST_CHK_ERR_CNT_H_STATUS
> PHY_OFF(0x144)
> +#define UFS_PHY_L0_BIST_CHK_STATUS
> PHY_OFF(0x148)
> +#define UFS_PHY_L1_BIST_CHK_STATUS
> PHY_OFF(0x14C)
> +#define UFS_PHY_DEBUG_BUS_0_STATUS
> PHY_OFF(0x150)
> +#define UFS_PHY_DEBUG_BUS_1_STATUS
> PHY_OFF(0x154)
> +#define UFS_PHY_DEBUG_BUS_2_STATUS
> PHY_OFF(0x158)
> +#define UFS_PHY_DEBUG_BUS_3_STATUS
> PHY_OFF(0x15C)
> +#define UFS_PHY_RMMI_ATTR_CTRL
> PHY_OFF(0x16C)
> +#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(0x170)
> +#define UFS_PHY_RMMI_ATTRWRVAL				    PHY_OFF(0x174)
> +#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS		    PHY_OFF(0x178)
> +#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS		    PHY_OFF(0x17C)
> +
> +/* TX LANE n (0, 1) registers */
> +#define QSERDES_TX_BIST_MODE_LANENO(n)                      TX_OFF(n,
> 0x00)
> +#define QSERDES_TX_CLKBUF_ENABLE(n)                         TX_OFF(n,
> 0x04)
> +#define QSERDES_TX_TX_EMP_POST1_LVL(n)                      TX_OFF(n,
> 0x08)
> +#define QSERDES_TX_TX_DRV_LVL(n)                            TX_OFF(n,
> 0x0C)
> +#define QSERDES_TX_RESET_TSYNC_EN(n)                        TX_OFF(n,
> 0x10)
> +#define QSERDES_TX_LPB_EN(n)                                TX_OFF(n,
> 0x14)
> +#define QSERDES_TX_RES_CODE(n)                              TX_OFF(n,
> 0x18)
> +#define QSERDES_TX_PERL_LENGTH1(n)                          TX_OFF(n,
> 0x1C)
> +#define QSERDES_TX_PERL_LENGTH2(n)                          TX_OFF(n,
> 0x20)
> +#define QSERDES_TX_SERDES_BYP_EN_OUT(n)                     TX_OFF(n,
> 0x24)
> +#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(n)           TX_OFF(n,
> 0x28)
> +#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(n)            TX_OFF(n,
> 0x2C)
> +#define QSERDES_TX_BIST_PATTERN1(n)                         TX_OFF(n,
> 0x30)
> +#define QSERDES_TX_BIST_PATTERN2(n)                         TX_OFF(n,
> 0x34)
> +#define QSERDES_TX_BIST_PATTERN3(n)                         TX_OFF(n,
> 0x38)
> +#define QSERDES_TX_BIST_PATTERN4(n)                         TX_OFF(n,
> 0x3C)
> +#define QSERDES_TX_BIST_PATTERN5(n)                         TX_OFF(n,
> 0x40)
> +#define QSERDES_TX_BIST_PATTERN6(n)                         TX_OFF(n,
> 0x44)
> +#define QSERDES_TX_BIST_PATTERN7(n)                         TX_OFF(n,
> 0x48)
> +#define QSERDES_TX_BIST_PATTERN8(n)                         TX_OFF(n,
> 0x4C)
> +#define QSERDES_TX_LANE_MODE(n)                             TX_OFF(n,
> 0x50)
> +#define QSERDES_TX_ATB_SEL(n)                               TX_OFF(n,
> 0x54)
> +#define QSERDES_TX_REC_DETECT_LVL(n)                        TX_OFF(n,
> 0x58)
> +#define QSERDES_TX_PRBS_SEED1(n)                            TX_OFF(n,
> 0x5C)
> +#define QSERDES_TX_PRBS_SEED2(n)                            TX_OFF(n,
> 0x60)
> +#define QSERDES_TX_PRBS_SEED3(n)                            TX_OFF(n,
> 0x64)
> +#define QSERDES_TX_PRBS_SEED4(n)                            TX_OFF(n,
> 0x68)
> +#define QSERDES_TX_RESET_GEN(n)                             TX_OFF(n,
> 0x6C)
> +#define QSERDES_TX_TRAN_DRVR_EMP_EN(n)                      TX_OFF(n,
> 0x70)
> +#define QSERDES_TX_TX_INTERFACE_MODE(n)                     TX_OFF(n,
> 0x74)
> +#define QSERDES_TX_BIST_STATUS(n)                           TX_OFF(n,
> 0x78)
> +#define QSERDES_TX_BIST_ERROR_COUNT1(n)                     TX_OFF(n,
> 0x7C)
> +#define QSERDES_TX_BIST_ERROR_COUNT2(n)                     TX_OFF(n,
> 0x80)
> +
> +/* RX LANE n (0, 1) registers */
> +#define QSERDES_RX_CDR_CONTROL(n)                           RX_OFF(n,
> 0x00)
> +#define QSERDES_RX_AUX_CONTROL(n)                           RX_OFF(n,
> 0x04)
> +#define QSERDES_RX_AUX_DATA_TCODE(n)                        RX_OFF(n,
> 0x08)
> +#define QSERDES_RX_RCLK_AUXDATA_SEL(n)                      RX_OFF(n,
> 0x0C)
> +#define QSERDES_RX_EQ_CONTROL(n)                            RX_OFF(n,
> 0x10)
> +#define QSERDES_RX_RX_EQ_GAIN2(n)                           RX_OFF(n,
> 0x14)
> +#define QSERDES_RX_AC_JTAG_INIT(n)                          RX_OFF(n,
> 0x18)
> +#define QSERDES_RX_AC_JTAG_LVL_EN(n)                        RX_OFF(n,
> 0x1C)
> +#define QSERDES_RX_AC_JTAG_MODE(n)                          RX_OFF(n,
> 0x20)
> +#define QSERDES_RX_AC_JTAG_RESET(n)                         RX_OFF(n,
> 0x24)
> +#define QSERDES_RX_RX_IQ_RXDET_EN(n)                        RX_OFF(n,
> 0x28)
> +#define QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(n)            RX_OFF(n,
> 0x2C)
> +#define QSERDES_RX_RX_EQ_GAIN1(n)                           RX_OFF(n,
> 0x30)
> +#define QSERDES_RX_SIGDET_CNTRL(n)                          RX_OFF(n,
> 0x34)
> +#define QSERDES_RX_RX_BAND(n)                               RX_OFF(n,
> 0x38)
> +#define QSERDES_RX_CDR_FREEZE_UP_DN(n)                      RX_OFF(n,
> 0x3C)
> +#define QSERDES_RX_RX_INTERFACE_MODE(n)                     RX_OFF(n,
> 0x40)
> +#define QSERDES_RX_JITTER_GEN_MODE(n)                       RX_OFF(n,
> 0x44)
> +#define QSERDES_RX_BUJ_AMP(n)                               RX_OFF(n,
> 0x48)
> +#define QSERDES_RX_SJ_AMP1(n)                               RX_OFF(n,
> 0x4C)
> +#define QSERDES_RX_SJ_AMP2(n)                               RX_OFF(n,
> 0x50)
> +#define QSERDES_RX_SJ_PER1(n)                               RX_OFF(n,
> 0x54)
> +#define QSERDES_RX_SJ_PER2(n)                               RX_OFF(n,
> 0x58)
> +#define QSERDES_RX_BUJ_STEP_FREQ1(n)                        RX_OFF(n,
> 0x5C)
> +#define QSERDES_RX_BUJ_STEP_FREQ2(n)                        RX_OFF(n,
> 0x60)
> +#define QSERDES_RX_PPM_OFFSET1(n)                           RX_OFF(n,
> 0x64)
> +#define QSERDES_RX_PPM_OFFSET2(n)                           RX_OFF(n,
> 0x68)
> +#define QSERDES_RX_SIGN_PPM_PERIOD1(n)                      RX_OFF(n,
> 0x6C)
> +#define QSERDES_RX_SIGN_PPM_PERIOD2(n)                      RX_OFF(n,
> 0x70)
> +#define QSERDES_RX_SSC_CTRL(n)                              RX_OFF(n,
> 0x74)
> +#define QSERDES_RX_SSC_COUNT1(n)                            RX_OFF(n,
> 0x78)
> +#define QSERDES_RX_SSC_COUNT2(n)                            RX_OFF(n,
> 0x7C)
> +#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)
> +#define QSERDES_RX_CDR_CONTROL3(n)                          RX_OFF(n,
> 0x94)
> +#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_CDR_CONTROL_EIGHTH(n)                    RX_OFF(n,
> 0xA0)
> +#define QSERDES_RX_UCDR_FO_GAIN(n)                          RX_OFF(n,
> 0xA4)
> +#define QSERDES_RX_UCDR_SO_GAIN(n)                          RX_OFF(n,
> 0xA8)
> +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(n)         RX_OFF(n,
> 0xAC)
> +#define QSERDES_RX_UCDR_FO_TO_SO_DELAY(n)                   RX_OFF(n,
> 0xB0)
> +#define QSERDES_RX_PI_CTRL1(n)                              RX_OFF(n,
> 0xB4)
> +#define QSERDES_RX_PI_CTRL2(n)                              RX_OFF(n,
> 0xB8)
> +#define QSERDES_RX_PI_QUAD(n)                               RX_OFF(n,
> 0xBC)
> +#define QSERDES_RX_IDATA1(n)                                RX_OFF(n,
> 0xC0)
> +#define QSERDES_RX_IDATA2(n)                                RX_OFF(n,
> 0xC4)
> +#define QSERDES_RX_AUX_DATA1(n)                             RX_OFF(n,
> 0xC8)
> +#define QSERDES_RX_AUX_DATA2(n)                             RX_OFF(n,
> 0xCC)
> +#define QSERDES_RX_AC_JTAG_OUTP(n)                          RX_OFF(n,
> 0xD0)
> +#define QSERDES_RX_AC_JTAG_OUTN(n)                          RX_OFF(n,
> 0xD4)
> +#define QSERDES_RX_RX_SIGDET_PWMDECSTATUS(n)                RX_OFF(n,
> 0xD8)
> +
> +#define UFS_PHY_TX_LANE_ENABLE_MASK		0x3
> +
> +/*
> + * This structure represents the 28nm 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_28nm {
> +	struct ufs_qcom_phy common_cfg;
> +};
> +
> +static struct ufs_qcom_phy_calibration phy_cal_table_ctrl_1_1_0_rate_A[]
> = {
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xFF),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x24),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x08),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
> +	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, 0x67),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x01),
> +	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_RX_PWM_CNTRL1(0), 0x43),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(1), 0x43),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(0), 0x22),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x12),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x2a),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(1), 0x22),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x12),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x2a),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(0), 0xC0),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(1), 0xC0),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(0), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(1), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G1_CLK_DIVIDER, 0x50),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G2_CLK_DIVIDER, 0x28),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G3_CLK_DIVIDER, 0x10),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G4_CLK_DIVIDER, 0x08),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER, 0xa8),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER, 0x54),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER, 0x2a),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER, 0x15),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_OMC_STATUS_RDVAL, 0xff),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_TIME, 0x1f),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_GRANULARITY, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TSYNC_RSYNC_CNTL, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PLL_CNTL, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x1a),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CFG_CHANGE_CNT_VAL, 0x09),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYNC_WAIT_TIME, 0x30),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x08),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0xc8),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY, 0x10),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(0), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(0), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(1), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(1), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_TRIM_OFFSET, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BGTC, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_AMP_OS, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(0), 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(1), 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(0), 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(1), 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(0), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(1), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_MSB, 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_LSB, 0x80),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x27),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CONTROLSYM_ONE_HOT_DISABLE, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RETIME_BUFFER_EN, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HSGEAR_CAPABILITY, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_PWMGEAR_CAPABILITY, 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_AMPLITUDE_CAPABILITY, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_EXTERNALSYNC_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_HS_UNTERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_LS_TERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HIBERN8TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HSGEAR_CAPABILITY, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWMGEAR_CAPABILITY, 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_UNTERMINATED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_TERMINATED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G1_SYNC_LENGTH_CAPABILITY, 0x48),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_HS_G1_PREPARE_LENGTH_CAPABILITY, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_PREPARE_LENGTH_CAPABILITY, 0x09),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HIBERN8TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G2_SYNC_LENGTH_CAPABILITY, 0x48),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G3_SYNC_LENGTH_CAPABILITY, 0x48),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_HS_G2_PREPARE_LENGTH_CAPABILITY, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_HS_G3_PREPARE_LENGTH_CAPABILITY, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(0), 0x09),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(0), 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(0), 0x73),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(0), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(0), 0x22),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(0), 0x0a),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(0), 0x06),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(0), 0x35),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(1), 0x09),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(1), 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(1), 0x73),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(1), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(1), 0x22),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(1), 0x0a),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(1), 0x06),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(1), 0x35),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_MODE, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_IE_TRIM, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_IP_TRIM, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_IN_SYNC_SEL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_TEST_UPDN_RESTRIMSTEP, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_FAUX_EN, 0x00),
> +};
> +
> +static struct ufs_qcom_phy_calibration phy_cal_table_ctrl_1_1_1_rate_A[]
> = {
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0x43),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x24),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x08),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
> +	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_RESETSM_CNTRL, 0x10),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(0), 0x43),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(1), 0x43),
> +	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_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(0), 0xC0),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(1), 0xC0),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(0), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(1), 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G1_CLK_DIVIDER, 0x30),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G2_CLK_DIVIDER, 0x18),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G3_CLK_DIVIDER, 0x0c),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PWM_G4_CLK_DIVIDER, 0x06),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER, 0xa8),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER, 0x54),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER, 0x2a),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER, 0x15),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_OMC_STATUS_RDVAL, 0xff),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_TIME, 0x1f),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_GRANULARITY, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TSYNC_RSYNC_CNTL, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PLL_CNTL, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x1a),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CFG_CHANGE_CNT_VAL, 0x09),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYNC_WAIT_TIME, 0x30),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x08),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0xc8),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY, 0x10),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(0), 0x1f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(0), 0x17),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(1), 0x1f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(1), 0x17),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(1), 0x00),
> +	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(QSERDES_COM_RES_TRIM_OFFSET, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BGTC, 0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_AMP_OS, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(0), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(1), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_MSB, 0x07),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_LSB, 0x80),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x27),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_CONTROLSYM_ONE_HOT_DISABLE, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RETIME_BUFFER_EN, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HSGEAR_CAPABILITY, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_PWMGEAR_CAPABILITY, 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_AMPLITUDE_CAPABILITY, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_EXTERNALSYNC_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_HS_UNTERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		UFS_PHY_TX_LS_TERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HIBERN8TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HSGEAR_CAPABILITY, 0x03),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWMGEAR_CAPABILITY, 0x04),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_UNTERMINATED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_TERMINATED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G1_SYNC_LENGTH_CAPABILITY, 0x48),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G1_PREPARE_LENGTH_CAPABILITY,
> +									0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_PREPARE_LENGTH_CAPABILITY, 0x0a),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HIBERN8TIME_CAPABILITY, 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G2_SYNC_LENGTH_CAPABILITY, 0x48),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G3_SYNC_LENGTH_CAPABILITY, 0x48),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G2_PREPARE_LENGTH_CAPABILITY,
> +									0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G3_PREPARE_LENGTH_CAPABILITY,
> +									0x0f),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(0), 0x09),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(0), 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(0), 0x51),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(0), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(0), 0x22),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(0), 0x0a),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(0), 0x06),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(0), 0x35),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(0), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(1), 0x09),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(1), 0x01),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(1), 0x51),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(1), 0x05),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(1), 0x22),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(1), 0x0a),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(1), 0x06),
> +	UFS_QCOM_PHY_CAL_ENTRY(
> +		QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(1), 0x35),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(1), 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_MODE, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_IE_TRIM, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_IP_TRIM, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_IN_SYNC_SEL, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_TEST_UPDN_RESTRIMSTEP, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_FAUX_EN, 0x00),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x08),
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x08),
> +};
> +
> +static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
> +	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
> +	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),
> +};
> +
> +static struct ufs_qcom_phy_calibration cached_phy_regs[] = {
> +	{QSERDES_COM_PLL_CRCTRL},
> +	{QSERDES_COM_PLL_CNTRL},
> +	{QSERDES_COM_SYSCLK_EN_SEL},
> +	{QSERDES_COM_SYS_CLK_CTRL},
> +	{QSERDES_COM_PLL_CLKEPDIV},
> +	{QSERDES_COM_DEC_START1},
> +	{QSERDES_COM_DEC_START2},
> +	{QSERDES_COM_DIV_FRAC_START1},
> +	{QSERDES_COM_DIV_FRAC_START2},
> +	{QSERDES_COM_DIV_FRAC_START3},
> +	{QSERDES_COM_PLLLOCK_CMP1},
> +	{QSERDES_COM_PLLLOCK_CMP2},
> +	{QSERDES_COM_PLLLOCK_CMP3},
> +	{QSERDES_COM_PLLLOCK_CMP_EN},
> +	{QSERDES_COM_RESETSM_CNTRL},
> +	{QSERDES_COM_PLL_RXTXEPCLK_EN},
> +	{QSERDES_RX_PWM_CNTRL1(0)},
> +	{QSERDES_RX_PWM_CNTRL1(1)},
> +	{QSERDES_RX_CDR_CONTROL(0)},
> +	{QSERDES_RX_CDR_CONTROL_HALF(0)},
> +	{QSERDES_RX_CDR_CONTROL_QUARTER(0)},
> +	{QSERDES_RX_CDR_CONTROL(1)},
> +	{QSERDES_RX_CDR_CONTROL_HALF(1)},
> +	{QSERDES_RX_CDR_CONTROL_QUARTER(1)},
> +	{QSERDES_RX_SIGDET_CNTRL(0)},
> +	{QSERDES_RX_SIGDET_CNTRL(1)},
> +	{QSERDES_RX_SIGDET_CNTRL2(0)},
> +	{QSERDES_RX_SIGDET_CNTRL2(1)},
> +	{QSERDES_RX_RX_EQ_GAIN1(0)},
> +	{QSERDES_RX_RX_EQ_GAIN2(0)},
> +	{QSERDES_RX_RX_EQ_GAIN1(1)},
> +	{QSERDES_RX_RX_EQ_GAIN2(1)},
> +	{QSERDES_COM_PLL_IP_SETI},
> +	{QSERDES_COM_PLL_CP_SETI},
> +	{QSERDES_COM_PLL_IP_SETP},
> +	{QSERDES_COM_PLL_CP_SETP},
> +	{UFS_PHY_PWM_G1_CLK_DIVIDER},
> +	{UFS_PHY_PWM_G2_CLK_DIVIDER},
> +	{UFS_PHY_PWM_G3_CLK_DIVIDER},
> +	{UFS_PHY_PWM_G4_CLK_DIVIDER},
> +	{UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER},
> +	{UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER},
> +	{UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER},
> +	{UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER},
> +	{UFS_PHY_OMC_STATUS_RDVAL},
> +	{UFS_PHY_LINE_RESET_TIME},
> +	{UFS_PHY_LINE_RESET_GRANULARITY},
> +	{UFS_PHY_TSYNC_RSYNC_CNTL},
> +	{UFS_PHY_PLL_CNTL},
> +	{UFS_PHY_TX_LARGE_AMP_DRV_LVL},
> +	{UFS_PHY_TX_SMALL_AMP_DRV_LVL},
> +	{UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL},
> +	{UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL},
> +	{UFS_PHY_CFG_CHANGE_CNT_VAL},
> +	{UFS_PHY_RX_SYNC_WAIT_TIME},
> +	{UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY},
> +	{UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY},
> +	{UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY},
> +	{UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY},
> +	{UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY},
> +	{UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY},
> +	{UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY},
> +	{UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY},
> +	{QSERDES_RX_CDR_CONTROL3(0)},
> +	{QSERDES_RX_CDR_CONTROL3(1)},
> +	{QSERDES_COM_RES_TRIM_OFFSET},
> +	{QSERDES_COM_BGTC},
> +	{QSERDES_COM_PLL_AMP_OS},
> +};
> +
> +static struct ufs_qcom_phy_stored_attributes cached_phy_attr[] = {
> +	{TX_MODE},
> +	{TX_HSRATE_SERIES},
> +	{TX_HSGEAR},
> +	{TX_PWMGEAR},
> +	{TX_AMPLITUDE},
> +	{TX_HS_SLEWRATE},
> +	{TX_SYNC_SOURCE},
> +	{TX_HS_PREPARE_LENGTH},
> +	{TX_LS_PREPARE_LENGTH},
> +	{TX_LCC_ENABLE},
> +	{TX_PWM_BURST_CLOSURE_EXTENSION},
> +	{TX_BYPASS_8B10B_ENABLE},
> +	{TX_DRIVER_POLARITY},
> +	{TX_HS_UNTERMINATED_LINE_DRIVE_ENABLE},
> +	{TX_LS_TERMINATED_LINE_DRIVE_ENABLE},
> +	{TX_LCC_SEQUENCER},
> +	{TX_MIN_ACTIVATETIME},
> +	{TX_PWM_G6_G7_SYNC_LENGTH},
> +	{RX_MODE},
> +	{RX_HSRATE_SERIES},
> +	{RX_HSGEAR},
> +	{RX_PWMGEAR},
> +	{RX_LS_TERMINATED_ENABLE},
> +	{RX_HS_UNTERMINATED_ENABLE},
> +	{RX_ENTER_HIBERN8},
> +	{RX_BYPASS_8B10B_ENABLE},
> +	{RX_TERMINATION_FORCE_ENABLE},
> +};
> +
> +#endif
> diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
> new file mode 100644
> index 0000000..9ba6adf
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-ufs.c
> @@ -0,0 +1,777 @@
> +/*
> + * Copyright (c) 2013-2014, 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/phy/phy-qcom-ufs.h>
> +
> +static int __ufs_qcom_phy_init_vreg(struct phy *, struct
> ufs_qcom_phy_vreg *,
> +				    const char *, bool);
> +static int ufs_qcom_phy_init_vreg(struct phy *, struct ufs_qcom_phy_vreg
> *,
> +				  const char *);
> +
> +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, int rate)
> +{
> +	int i;
> +	int ret = 0;
> +
> +	if (!tbl_A) {
> +		dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__);
> +		ret = EINVAL;
> +		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);
> +
> +	/*
> +	 * In case we would like to work in rate B, we need
> +	 * to override a registers that were configured in rate A table
> +	 * with registers of rate B table.
> +	 * table.
> +	 */
> +	if (rate == PA_HS_MODE_B) {
> +		if (!tbl_B) {
> +			dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL",
> +				__func__);
> +			ret = EINVAL;
> +			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);
> +	}
> +
> +	/* flush buffered writes */
> +	mb();
> +
> +out:
> +	return ret;
> +}
> +
> +struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
> +				struct ufs_qcom_phy *common_cfg,
> +				struct phy_ops *ufs_qcom_phy_gen_ops,
> +				struct ufs_qcom_phy_specific_ops *phy_spec_ops)
> +{
> +	int err;
> +	struct device *dev = &pdev->dev;
> +	struct phy *generic_phy = NULL;
> +	struct phy_provider *phy_provider;
> +
> +	err = ufs_qcom_phy_base_init(pdev, common_cfg);
> +	if (err) {
> +		dev_err(dev, "%s: phy base init failed %d\n", __func__, err);
> +		goto out;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		err = PTR_ERR(phy_provider);
> +		dev_err(dev, "%s: failed to register phy %d\n", __func__, err);
> +		goto out;
> +	}
> +
> +	generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops, NULL);
> +	if (IS_ERR(generic_phy)) {
> +		err =  PTR_ERR(generic_phy);
> +		dev_err(dev, "%s: failed to create phy %d\n", __func__, err);
> +		goto out;
> +	}
> +
> +	common_cfg->phy_spec_ops = phy_spec_ops;
> +	common_cfg->dev = dev;
> +
> +out:
> +	return generic_phy;
> +}
> +
> +/*
> + * This assumes the embedded phy structure inside generic_phy is of type
> + * struct ufs_qcom_phy. In order to function properly it's crucial
> + * to keep the embedded struct "struct ufs_qcom_phy common_cfg"
> + * as the first inside generic_phy.
> + */
> +struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy)
> +{
> +	return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy);
> +}
> +
> +int ufs_qcom_phy_base_init(struct platform_device *pdev,
> +			   struct ufs_qcom_phy *phy_common)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	int err = 0;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
> +	if (!res) {
> +		dev_err(dev, "%s: phy_mem resource not found\n", __func__);
> +		err = -ENOMEM;
> +		goto out;
> +	}
> +
> +	phy_common->mmio = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(phy_common->mmio)) {
> +		err = PTR_ERR(phy_common->mmio);
> +		phy_common->mmio = NULL;
> +		dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	/* "dev_ref_clk_ctrl_mem" is optional resource */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +					   "dev_ref_clk_ctrl_mem");
> +	if (!res) {
> +		dev_dbg(dev, "%s: dev_ref_clk_ctrl_mem resource not found\n",
> +			__func__);
> +		goto out;
> +	}
> +
> +	phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(phy_common->dev_ref_clk_ctrl_mmio)) {
> +		err = PTR_ERR(phy_common->dev_ref_clk_ctrl_mmio);
> +		phy_common->dev_ref_clk_ctrl_mmio = NULL;
> +		dev_err(dev, "%s: ioremap for dev_ref_clk_ctrl_mem resource failed
> %d\n",
> +			__func__, err);
> +	}
> +
> +out:
> +	return err;
> +}
> +
> +static int __ufs_qcom_phy_clk_get(struct phy *phy,
> +			 const char *name, struct clk **clk_out, bool err_print)
> +{
> +	struct clk *clk;
> +	int err = 0;
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
> +	struct device *dev = ufs_qcom_phy->dev;
> +
> +	clk = devm_clk_get(dev, name);
> +	if (IS_ERR(clk)) {
> +		err = PTR_ERR(clk);
> +		if (err_print)
> +			dev_err(dev, "failed to get %s err %d", name, err);
> +	} else {
> +		*clk_out = clk;
> +	}
> +
> +	return err;
> +}
> +
> +int ufs_qcom_phy_clk_get(struct phy *phy,
> +			 const char *name, struct clk **clk_out)
> +{
> +	return __ufs_qcom_phy_clk_get(phy, name, clk_out, true);
> +}
> +
> +int
> +ufs_qcom_phy_init_clks(struct phy *generic_phy,
> +		       struct ufs_qcom_phy *phy_common)
> +{
> +	int err;
> +
> +	err = ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk",
> +				   &phy_common->tx_iface_clk);
> +	if (err)
> +		goto out;
> +
> +	err = ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk",
> +				   &phy_common->rx_iface_clk);
> +	if (err)
> +		goto out;
> +
> +	err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk_src",
> +				   &phy_common->ref_clk_src);
> +	if (err)
> +		goto out;
> +
> +	/*
> +	 * "ref_clk_parent" is optional hence don't abort init if it's not
> +	 * found.
> +	 */
> +	__ufs_qcom_phy_clk_get(generic_phy, "ref_clk_parent",
> +				   &phy_common->ref_clk_parent, false);
> +
> +	err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk",
> +				   &phy_common->ref_clk);
> +
> +out:
> +	return err;
> +}
> +
> +int
> +ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
> +			      struct ufs_qcom_phy *phy_common)
> +{
> +	int err;
> +
> +	err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_pll,
> +		"vdda-pll");
> +	if (err)
> +		goto out;
> +
> +	err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_phy,
> +		"vdda-phy");
> +
> +	if (err)
> +		goto out;
> +
> +	/* vddp-ref-clk-* properties are optional */
> +	__ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vddp_ref_clk,
> +				 "vddp-ref-clk", true);
> +out:
> +	return err;
> +}
> +
> +static int __ufs_qcom_phy_init_vreg(struct phy *phy,
> +		struct ufs_qcom_phy_vreg *vreg, const char *name, bool optional)
> +{
> +	int err = 0;
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
> +	struct device *dev = ufs_qcom_phy->dev;
> +
> +	char prop_name[MAX_PROP_NAME];
> +
> +	vreg->name = kstrdup(name, GFP_KERNEL);
> +	if (!vreg->name) {
> +		err = -ENOMEM;
> +		goto out;
> +	}
> +
> +	vreg->reg = devm_regulator_get(dev, name);
> +	if (IS_ERR(vreg->reg)) {
> +		err = PTR_ERR(vreg->reg);
> +		vreg->reg = NULL;
> +		if (!optional)
> +			dev_err(dev, "failed to get %s, %d\n", name, err);
> +		goto out;
> +	}
> +
> +	if (dev->of_node) {
> +		snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name);
> +		err = of_property_read_u32(dev->of_node,
> +					prop_name, &vreg->max_uA);
> +		if (err && err != -EINVAL) {
> +			dev_err(dev, "%s: failed to read %s\n",
> +					__func__, prop_name);
> +			goto out;
> +		} else if (err == -EINVAL || !vreg->max_uA) {
> +			if (regulator_count_voltages(vreg->reg) > 0) {
> +				dev_err(dev, "%s: %s is mandatory\n",
> +						__func__, prop_name);
> +				goto out;
> +			}
> +			err = 0;
> +		}
> +		snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name);
> +		if (of_get_property(dev->of_node, prop_name, NULL))
> +			vreg->is_always_on = true;
> +		else
> +			vreg->is_always_on = false;
> +	}
> +
> +	if (!strcmp(name, "vdda-pll")) {
> +		vreg->max_uV = VDDA_PLL_MAX_UV;
> +		vreg->min_uV = VDDA_PLL_MIN_UV;
> +	} else if (!strcmp(name, "vdda-phy")) {
> +		vreg->max_uV = VDDA_PHY_MAX_UV;
> +		vreg->min_uV = VDDA_PHY_MIN_UV;
> +	} else if (!strcmp(name, "vddp-ref-clk")) {
> +		vreg->max_uV = VDDP_REF_CLK_MAX_UV;
> +		vreg->min_uV = VDDP_REF_CLK_MIN_UV;
> +	}
> +
> +out:
> +	if (err)
> +		kfree(vreg->name);
> +	return err;
> +}
> +
> +static int ufs_qcom_phy_init_vreg(struct phy *phy,
> +			struct ufs_qcom_phy_vreg *vreg, const char *name)
> +{
> +	return __ufs_qcom_phy_init_vreg(phy, vreg, name, false);
> +}
> +
> +int ufs_qcom_phy_cfg_vreg(struct phy *phy,
> +			  struct ufs_qcom_phy_vreg *vreg, bool on)
> +{
> +	int ret = 0;
> +	struct regulator *reg = vreg->reg;
> +	const char *name = vreg->name;
> +	int min_uV;
> +	int uA_load;
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
> +	struct device *dev = ufs_qcom_phy->dev;
> +
> +	BUG_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);
> +		if (ret) {
> +			dev_err(dev, "%s: %s set voltage failed, err=%d\n",
> +					__func__, name, ret);
> +			goto out;
> +		}
> +		uA_load = on ? vreg->max_uA : 0;
> +		ret = regulator_set_optimum_mode(reg, uA_load);
> +		if (ret >= 0) {
> +			/*
> +			 * regulator_set_optimum_mode() returns new regulator
> +			 * mode upon success.
> +			 */
> +			ret = 0;
> +		} else {
> +			dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
> +					__func__, name, uA_load, ret);
> +			goto out;
> +		}
> +	}
> +out:
> +	return ret;
> +}
> +
> +int ufs_qcom_phy_enable_vreg(struct phy *phy,
> +			     struct ufs_qcom_phy_vreg *vreg)
> +{
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
> +	struct device *dev = ufs_qcom_phy->dev;
> +	int ret = 0;
> +
> +	if (!vreg || vreg->enabled)
> +		goto out;
> +
> +	ret = ufs_qcom_phy_cfg_vreg(phy, vreg, true);
> +	if (ret) {
> +		dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n",
> +			__func__, ret);
> +		goto out;
> +	}
> +
> +	ret = regulator_enable(vreg->reg);
> +	if (ret) {
> +		dev_err(dev, "%s: enable failed, err=%d\n",
> +				__func__, ret);
> +		goto out;
> +	}
> +
> +	vreg->enabled = true;
> +out:
> +	return ret;
> +}
> +
> +int ufs_qcom_phy_enable_ref_clk(struct phy *generic_phy)
> +{
> +	int ret = 0;
> +	struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
> +
> +	if (phy->is_ref_clk_enabled)
> +		goto out;
> +
> +	/*
> +	 * reference clock is propagated in a daisy-chained manner from
> +	 * source to phy, so ungate them at each stage.
> +	 */
> +	ret = clk_prepare_enable(phy->ref_clk_src);
> +	if (ret) {
> +		dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n",
> +				__func__, ret);
> +		goto out;
> +	}
> +
> +	/*
> +	 * "ref_clk_parent" is optional clock hence make sure that clk reference
> +	 * is available before trying to enable the clock.
> +	 */
> +	if (phy->ref_clk_parent) {
> +		ret = clk_prepare_enable(phy->ref_clk_parent);
> +		if (ret) {
> +			dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n",
> +					__func__, ret);
> +			goto out_disable_src;
> +		}
> +	}
> +
> +	ret = clk_prepare_enable(phy->ref_clk);
> +	if (ret) {
> +		dev_err(phy->dev, "%s: ref_clk enable failed %d\n",
> +				__func__, ret);
> +		goto out_disable_parent;
> +	}
> +
> +	phy->is_ref_clk_enabled = true;
> +	goto out;
> +
> +out_disable_parent:
> +	if (phy->ref_clk_parent)
> +		clk_disable_unprepare(phy->ref_clk_parent);
> +out_disable_src:
> +	clk_disable_unprepare(phy->ref_clk_src);
> +out:
> +	return ret;
> +}
> +
> +int ufs_qcom_phy_disable_vreg(struct phy *phy,
> +			      struct ufs_qcom_phy_vreg *vreg)
> +{
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
> +	struct device *dev = ufs_qcom_phy->dev;
> +	int ret = 0;
> +
> +	if (!vreg || !vreg->enabled || vreg->is_always_on)
> +		goto out;
> +
> +	ret = regulator_disable(vreg->reg);
> +
> +	if (!ret) {
> +		/* ignore errors on applying disable config */
> +		ufs_qcom_phy_cfg_vreg(phy, vreg, false);
> +		vreg->enabled = false;
> +	} else {
> +		dev_err(dev, "%s: %s disable failed, err=%d\n",
> +				__func__, vreg->name, ret);
> +	}
> +out:
> +	return ret;
> +}
> +
> +void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy)
> +{
> +	struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
> +
> +	if (phy->is_ref_clk_enabled) {
> +		clk_disable_unprepare(phy->ref_clk);
> +		/*
> +		 * "ref_clk_parent" is optional clock hence make sure that clk
> +		 * reference is available before trying to disable the clock.
> +		 */
> +		if (phy->ref_clk_parent)
> +			clk_disable_unprepare(phy->ref_clk_parent);
> +		clk_disable_unprepare(phy->ref_clk_src);
> +		phy->is_ref_clk_enabled = false;
> +	}
> +}
> +
> +#define UFS_REF_CLK_EN	(1 << 5)
> +
> +static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool
> enable)
> +{
> +	struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
> +
> +	if (phy->dev_ref_clk_ctrl_mmio &&
> +	    (enable ^ phy->is_dev_ref_clk_enabled)) {
> +		u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio);
> +
> +		if (enable)
> +			temp |= UFS_REF_CLK_EN;
> +		else
> +			temp &= ~UFS_REF_CLK_EN;
> +
> +		writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio);
> +		/* ensure that ref_clk is enabled/disabled before we return */
> +		wmb();
> +		phy->is_dev_ref_clk_enabled = enable;
> +	}
> +}
> +
> +void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy)
> +{
> +	ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true);
> +}
> +
> +void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy)
> +{
> +	ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false);
> +}
> +
> +void ufs_qcom_phy_restore_swi_regs(struct phy *generic_phy)
> +{
> +	int i;
> +	struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
> +
> +	for (i = 0; i < phy->cached_regs_table_size; i++) {
> +		struct ufs_qcom_phy_calibration *table =
> +			(struct ufs_qcom_phy_calibration *)phy->cached_regs;
> +		writel_relaxed(table[i].cfg_value, phy->mmio +
> +				table[i].reg_offset);
> +	}
> +
> +	/* flush buffered writes */
> +	mb();
> +}
> +
> +/* Turn ON M-PHY RMMI interface clocks */
> +int ufs_qcom_phy_enable_iface_clk(struct phy *generic_phy)
> +{
> +	struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
> +	int ret = 0;
> +
> +	if (phy->is_iface_clk_enabled)
> +		goto out;
> +
> +	ret = clk_prepare_enable(phy->tx_iface_clk);
> +	if (ret) {
> +		dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n",
> +				__func__, ret);
> +		goto out;
> +	}
> +	ret = clk_prepare_enable(phy->rx_iface_clk);
> +	if (ret) {
> +		clk_disable_unprepare(phy->tx_iface_clk);
> +		dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also
> tx_iface_clk\n",
> +				__func__, ret);
> +		goto out;
> +	}
> +	phy->is_iface_clk_enabled = true;
> +
> +out:
> +	return ret;
> +}
> +
> +/* Turn OFF M-PHY RMMI interface clocks */
> +void ufs_qcom_phy_disable_iface_clk(struct phy *generic_phy)
> +{
> +	struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
> +
> +	if (phy->is_iface_clk_enabled) {
> +		clk_disable_unprepare(phy->tx_iface_clk);
> +		clk_disable_unprepare(phy->rx_iface_clk);
> +		phy->is_iface_clk_enabled = false;
> +	}
> +}
> +
> +int ufs_qcom_phy_is_cfg_restore_quirk_enabled(struct phy *phy)
> +{
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
> +
> +	return ufs_qcom_phy->quirks & UFS_QCOM_PHY_QUIRK_CFG_RESTORE;
> +}
> +
> +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) {
> +		dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not
> supported\n",
> +			__func__);
> +		ret = -ENOTSUPP;
> +	} else {
> +		ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy);
> +	}
> +
> +	return ret;
> +}
> +
> +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 {
> +		ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy,
> +							       tx_lanes);
> +	}
> +
> +	return ret;
> +}
> +
> +void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
> +					  u8 major, u16 minor, u16 step)
> +{
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
> +
> +	ufs_qcom_phy->host_ctrl_rev_major = major;
> +	ufs_qcom_phy->host_ctrl_rev_minor = minor;
> +	ufs_qcom_phy->host_ctrl_rev_step = step;
> +}
> +
> +int ufs_qcom_phy_calibrate_phy(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->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);
> +		if (ret)
> +			dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n",
> +				__func__, ret);
> +	}
> +
> +	return ret;
> +}
> +
> +int ufs_qcom_phy_remove(struct phy *generic_phy,
> +			struct ufs_qcom_phy *ufs_qcom_phy)
> +{
> +	phy_power_off(generic_phy);
> +
> +	kfree(ufs_qcom_phy->vdda_pll.name);
> +	kfree(ufs_qcom_phy->vdda_phy.name);
> +
> +	return 0;
> +}
> +
> +int ufs_qcom_phy_exit(struct phy *generic_phy)
> +{
> +	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
> +
> +	if (ufs_qcom_phy->is_powered_on)
> +		phy_power_off(generic_phy);
> +
> +	return 0;
> +}
> +
> +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__);
> +		return -ENOTSUPP;
> +	}
> +
> +	return ufs_qcom_phy->phy_spec_ops->
> +			is_physical_coding_sublayer_ready(ufs_qcom_phy);
> +}
> +
> +int ufs_qcom_phy_save_configuration(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->save_configuration) {
> +		dev_err(ufs_qcom_phy->dev, "%s: save_configuration() callback is not
> supported\n",
> +			__func__);
> +		ret = -ENOTSUPP;
> +	} else {
> +		ufs_qcom_phy->phy_spec_ops->save_configuration(ufs_qcom_phy);
> +	}
> +
> +	return ret;
> +}
> +
> +int ufs_qcom_phy_restore_configuration(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->restore_configuration) {
> +		dev_err(ufs_qcom_phy->dev, "%s: restore_configuration() callback is not
> supported\n",
> +			__func__);
> +		ret = -ENOTSUPP;
> +	} else {
> +		ufs_qcom_phy->phy_spec_ops->restore_configuration(ufs_qcom_phy);
> +	}
> +
> +	return ret;
> +}
> +
> +int ufs_qcom_phy_power_on(struct phy *generic_phy)
> +{
> +	struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
> +	struct device *dev = phy_common->dev;
> +	int err;
> +
> +	err = ufs_qcom_phy_enable_vreg(generic_phy, &phy_common->vdda_phy);
> +	if (err) {
> +		dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	phy_common->phy_spec_ops->power_control(phy_common, true);
> +
> +	/* vdda_pll also enables ref clock LDOs so enable it first */
> +	err = ufs_qcom_phy_enable_vreg(generic_phy, &phy_common->vdda_pll);
> +	if (err) {
> +		dev_err(dev, "%s enable vdda_pll failed, err=%d\n",
> +			__func__, err);
> +		goto out_disable_phy;
> +	}
> +
> +	err = ufs_qcom_phy_enable_ref_clk(generic_phy);
> +	if (err) {
> +		dev_err(dev, "%s enable phy ref clock failed, err=%d\n",
> +			__func__, err);
> +		goto out_disable_pll;
> +	}
> +
> +	/* enable device PHY ref_clk pad rail */
> +	if (phy_common->vddp_ref_clk.reg) {
> +		err = ufs_qcom_phy_enable_vreg(generic_phy,
> +					       &phy_common->vddp_ref_clk);
> +		if (err) {
> +			dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n",
> +				__func__, err);
> +			goto out_disable_ref_clk;
> +		}
> +	}
> +
> +	phy_common->is_powered_on = true;
> +	goto out;
> +
> +out_disable_ref_clk:
> +	ufs_qcom_phy_disable_ref_clk(generic_phy);
> +out_disable_pll:
> +	ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
> +out_disable_phy:
> +	ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
> +out:
> +	return err;
> +}
> +
> +int ufs_qcom_phy_power_off(struct phy *generic_phy)
> +{
> +	struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
> +
> +	phy_common->phy_spec_ops->power_control(phy_common, false);
> +
> +	if (phy_common->vddp_ref_clk.reg)
> +		ufs_qcom_phy_disable_vreg(generic_phy,
> +					  &phy_common->vddp_ref_clk);
> +	ufs_qcom_phy_disable_ref_clk(generic_phy);
> +
> +	ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
> +	ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
> +	phy_common->is_powered_on = false;
> +
> +	return 0;
> +}
> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
> index 6e07b2a..509cf55 100644
> --- a/drivers/scsi/ufs/Kconfig
> +++ b/drivers/scsi/ufs/Kconfig
> @@ -70,3 +70,27 @@ config SCSI_UFSHCD_PLATFORM
>  	If you have a controller with this interface, say Y or M here.
>
>  	  If unsure, say N.
> +
> +config SCSI_UFS_QCOM
> +	bool "QCOM specific hooks to UFS controller platform driver"
> +	depends on SCSI_UFSHCD_PLATFORM && ARCH_MSM
> +	help
> +	  This selects the QCOM specific additions to UFSHCD platform driver.
> +	  UFS host on QCOM needs some vendor specific configuration before
> +	  accessing the hardware which includes PHY configuration and vendor
> +	  specific registers.
> +
> +	  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.
> \ No newline at end of file
> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
> index 1e5bd48..31adca5 100644
> --- a/drivers/scsi/ufs/Makefile
> +++ b/drivers/scsi/ufs/Makefile
> @@ -1,4 +1,6 @@
>  # UFSHCD makefile
> +obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
> +obj-$(CONFIG_SCSI_UFS_QCOM_ICE) += ufs-qcom-ice.o
>  obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
>  obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
>  obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
> diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c
> b/drivers/scsi/ufs/ufs-qcom-ice.c
> new file mode 100644
> index 0000000..2ade16d
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-qcom-ice.c
> @@ -0,0 +1,522 @@
> +/* Copyright (c) 2014, 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/async.h>
> +#include <linux/blkdev.h>
> +#include <linux/scsi/ufs/ufshcd.h>
> +#include <crypto/ice.h>
> +
> +#include "ufs-qcom-ice.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
> +
> +static void ufs_qcom_ice_success_cb(void *host_ctrl,
> +				enum ice_event_completion evt)
> +{
> +	struct ufs_qcom_host *qcom_host = (struct ufs_qcom_host *)host_ctrl;
> +
> +	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_DISABLED &&
> +	    evt == ICE_INIT_COMPLETION)
> +		qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
> +	 else if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_SUSPENDED &&
> +		   evt == ICE_RESUME_COMPLETION)
> +		qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
> +
> +	complete(&qcom_host->ice.async_done);
> +}
> +
> +static void ufs_qcom_ice_error_cb(void *host_ctrl, enum ice_error_code
> evt)
> +{
> +	struct ufs_qcom_host *qcom_host = (struct ufs_qcom_host *)host_ctrl;
> +
> +	dev_err(qcom_host->hba->dev, "%s: Error in ice operation %d",
> +		__func__, evt);
> +
> +	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_ACTIVE)
> +		qcom_host->ice.state = UFS_QCOM_ICE_STATE_DISABLED;
> +
> +	complete(&qcom_host->ice.async_done);
> +}
> +
> +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;
> +
> +}
> +
> +/**
> + * 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 = -EINVAL;
> +
> +	init_completion(&qcom_host->ice.async_done);
> +	err = qcom_host->ice.vops->init(qcom_host->ice.pdev,
> +				qcom_host,
> +				ufs_qcom_ice_success_cb,
> +				ufs_qcom_ice_error_cb);
> +	if (err) {
> +		dev_err(ufs_dev, "%s: ice init failed. err = %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
> +			msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
> +		dev_err(qcom_host->hba->dev,
> +			"%s: error. got timeout after %d ms\n",
> +			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
> +		err = -ETIMEDOUT;
> +		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;
> +}
> +
> +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;
> +}
> +
> +/**
> + * ufs_qcom_ice_cfg() - configures 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(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_2_val = 0;
> +	unsigned int bypass = 0;
> +	struct request *req;
> +	char cmd_op;
> +
> +	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;
> +	}
> +
> +	req = cmd->request;
> +	if (req->bio)
> +		lba = req->bio->bi_sector;
> +
> +	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, sizeof(ice_set), 0);
> +	if (qcom_host->ice.vops->config) {
> +		err = qcom_host->ice.vops->config(qcom_host->ice.pdev,
> +							req, &ice_set);
> +
> +		if (err) {
> +			dev_err(dev, "%s: error in ice_vops->config %d\n",
> +				__func__, err);
> +			goto out;
> +		}
> +	}
> +
> +	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_2_val =
> +		(ice_set.crypto_data.key_index &
> +		 MASK_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX)
> +		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX;
> +
> +	/* Configure data unit size of transfer request */
> +	ctrl_info_2_val |=
> +		(UFS_QCOM_ICE_TR_DATA_UNIT_4_KB &
> +		 MASK_UFS_QCOM_ICE_CTRL_INFO_2_CDU)
> +		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_CDU;
> +
> +	/* Configure ICE bypass mode */
> +	ctrl_info_2_val |=
> +		(bypass & MASK_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS)
> +		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS;
> +
> +	ufshcd_writel(qcom_host->hba, lba,
> +		     (REG_UFS_QCOM_ICE_CTRL_INFO_1_n + 8 * slot));
> +
> +	ufshcd_writel(qcom_host->hba, ctrl_info_2_val,
> +		     (REG_UFS_QCOM_ICE_CTRL_INFO_2_n + 8 * 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_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;
> +
> +	init_completion(&qcom_host->ice.async_done);
> +
> +	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 (!wait_for_completion_timeout(&qcom_host->ice.async_done,
> +	     msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
> +		dev_err(dev,
> +			"%s: error. got timeout after %d ms\n",
> +			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
> +		err = -ETIMEDOUT;
> +	}
> +
> +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;
> +	}
> +
> +	init_completion(&qcom_host->ice.async_done);
> +
> +	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 -EINVAL;
> +		}
> +	}
> +
> +	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
> +			msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
> +		dev_err(dev,
> +			"%s: error. got timeout after %d ms\n",
> +			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
> +		err = -ETIMEDOUT;
> +		goto out;
> +	}
> +
> +	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE)
> +		err = -EINVAL;
> +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;
> +}
> diff --git a/drivers/scsi/ufs/ufs-qcom-ice.h
> b/drivers/scsi/ufs/ufs-qcom-ice.h
> new file mode 100644
> index 0000000..1ed8e92
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-qcom-ice.h
> @@ -0,0 +1,113 @@
> +/* Copyright (c) 2014, 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 <linux/scsi/ufs/ufs-qcom.h>
> +
> +/*
> + * UFS host controller ICE registers. There are n [0..31]
> + * of each of these registers
> + */
> +enum {
> +	REG_UFS_QCOM_ICE_CTRL_INFO_1_n           = 0x2204,
> +	REG_UFS_QCOM_ICE_CTRL_INFO_2_n           = 0x2208,
> +};
> +
> +/* UFS QCOM ICE CTRL Info 2 register offset */
> +enum {
> +	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS     = 0,
> +	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX  = 0x1,
> +	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_CDU        = 0x6,
> +};
> +
> +/* UFS QCOM ICE CTRL Info 2 register masks */
> +enum {
> +	MASK_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS     = 0x1,
> +	MASK_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX  = 0x1F,
> +	MASK_UFS_QCOM_ICE_CTRL_INFO_2_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_cfg(struct ufs_qcom_host *qcom_host, struct scsi_cmnd
> *cmd);
> +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);
> +#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(struct ufs_qcom_host *qcom_host,
> +			    struct scsi_cmnd *cmd)
> +{
> +	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;
> +}
> +#endif /* CONFIG_SCSI_UFS_QCOM_ICE */
> +
> +#endif /* UFS_QCOM_ICE_H_ */
> diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
> new file mode 100644
> index 0000000..101aecc
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-qcom.c
> @@ -0,0 +1,1204 @@
> +/*
> + * Copyright (c) 2013-2014, 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/phy/phy.h>
> +
> +#include <linux/scsi/ufs/ufshcd.h>
> +#include <linux/scsi/ufs/ufs-qcom.h>
> +#include <linux/phy/phy-qcom-ufs.h>
> +#include "ufshci.h"
> +#include "ufs-qcom-ice.h"
> +
> +static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
> +
> +static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char
> *result);
> +static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host,
> +		const char *speed_mode);
> +static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote);
> +static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool
> restore_sec_cfg);
> +
> +static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32
> *tx_lanes)
> +{
> +	int err = 0;
> +
> +	err = ufshcd_dme_get(hba,
> +			UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), tx_lanes);
> +	if (err)
> +		dev_err(hba->dev, "%s: couldn't read PA_CONNECTEDTXDATALANES %d\n",
> +				__func__, err);
> +
> +	return err;
> +}
> +
> +static int ufs_qcom_host_clk_get(struct device *dev,
> +		const char *name, struct clk **clk_out)
> +{
> +	struct clk *clk;
> +	int err = 0;
> +
> +	clk = devm_clk_get(dev, name);
> +	if (IS_ERR(clk)) {
> +		err = PTR_ERR(clk);
> +		dev_err(dev, "%s: failed to get %s err %d",
> +				__func__, name, err);
> +	} else {
> +		*clk_out = clk;
> +	}
> +
> +	return err;
> +}
> +
> +static int ufs_qcom_host_clk_enable(struct device *dev,
> +		const char *name, struct clk *clk)
> +{
> +	int err = 0;
> +
> +	err = clk_prepare_enable(clk);
> +	if (err)
> +		dev_err(dev, "%s: %s enable failed %d\n", __func__, name, err);
> +
> +	return err;
> +}
> +
> +static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host)
> +{
> +	if (!host->is_lane_clks_enabled)
> +		return;
> +
> +	clk_disable_unprepare(host->tx_l1_sync_clk);
> +	clk_disable_unprepare(host->tx_l0_sync_clk);
> +	clk_disable_unprepare(host->rx_l1_sync_clk);
> +	clk_disable_unprepare(host->rx_l0_sync_clk);
> +
> +	host->is_lane_clks_enabled = false;
> +}
> +
> +static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
> +{
> +	int err = 0;
> +	struct device *dev = host->hba->dev;
> +
> +	if (host->is_lane_clks_enabled)
> +		return 0;
> +
> +	err = ufs_qcom_host_clk_enable(dev, "rx_lane0_sync_clk",
> +		host->rx_l0_sync_clk);
> +	if (err)
> +		goto out;
> +
> +	err = ufs_qcom_host_clk_enable(dev, "tx_lane0_sync_clk",
> +		host->tx_l0_sync_clk);
> +	if (err)
> +		goto disable_rx_l0;
> +
> +	if (host->lanes_per_direction > 1) {
> +		err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
> +			host->rx_l1_sync_clk);
> +		if (err)
> +			goto disable_tx_l0;
> +
> +		err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
> +			host->tx_l1_sync_clk);
> +		if (err)
> +			goto disable_rx_l1;
> +	}
> +	host->is_lane_clks_enabled = true;
> +	goto out;
> +
> +disable_rx_l1:
> +	clk_disable_unprepare(host->rx_l1_sync_clk);
> +disable_tx_l0:
> +	clk_disable_unprepare(host->tx_l0_sync_clk);
> +disable_rx_l0:
> +	clk_disable_unprepare(host->rx_l0_sync_clk);
> +out:
> +	return err;
> +}
> +
> +static int ufs_qcom_init_lanes(struct ufs_qcom_host *host)
> +{
> +	int err = 0;
> +	struct device *dev = host->hba->dev;
> +	struct device_node *np = dev->of_node;
> +
> +	err = of_property_read_u32(np, "qcom,lanes-per-direction",
> +		&host->lanes_per_direction);
> +	if (err) {
> +		dev_err(dev,
> +			"%s: failed to read lanes-per-direction, err=%d\n",
> +			__func__, err);
> +		return err;
> +	}
> +	printk("%s: lanes-per-direction = %u\n", __func__,
> host->lanes_per_direction);
> +
> +	err = ufs_qcom_host_clk_get(dev,
> +			"rx_lane0_sync_clk", &host->rx_l0_sync_clk);
> +	if (err)
> +		goto out;
> +
> +	err = ufs_qcom_host_clk_get(dev,
> +			"tx_lane0_sync_clk", &host->tx_l0_sync_clk);
> +	if (err)
> +		goto out;
> +
> +	/* in case of single lane per direction, don't read lane1 clocks */
> +	if (host->lanes_per_direction > 1) {
> +		err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
> +			&host->rx_l1_sync_clk);
> +		if (err)
> +			goto out;
> +
> +		err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
> +			&host->tx_l1_sync_clk);
> +	}
> +out:
> +	return err;
> +}
> +
> +static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct phy *phy = host->generic_phy;
> +	u32 tx_lanes;
> +	int err = 0;
> +
> +	err = ufs_qcom_get_connected_tx_lanes(hba, &tx_lanes);
> +	if (err)
> +		goto out;
> +
> +	err = ufs_qcom_phy_set_tx_lane_enable(phy, tx_lanes);
> +	if (err)
> +		dev_err(hba->dev, "%s: ufs_qcom_phy_set_tx_lane_enable failed\n",
> +			__func__);
> +
> +out:
> +	return err;
> +}
> +
> +static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
> +{
> +	int err;
> +	u32 tx_fsm_val = 0;
> +	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
> +
> +	do {
> +		err = ufshcd_dme_get(hba,
> +			UIC_ARG_MIB(MPHY_TX_FSM_STATE), &tx_fsm_val);
> +		if (err || tx_fsm_val == TX_FSM_HIBERN8)
> +			break;
> +
> +		/* sleep for max. 200us */
> +		usleep_range(100, 200);
> +	} while (time_before(jiffies, timeout));
> +
> +	/*
> +	 * we might have scheduled out for long during polling so
> +	 * check the state again.
> +	 */
> +	if (time_after(jiffies, timeout))
> +		err = ufshcd_dme_get(hba,
> +				UIC_ARG_MIB(MPHY_TX_FSM_STATE), &tx_fsm_val);
> +
> +	if (err) {
> +		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
> +				__func__, err);
> +	} else if (tx_fsm_val != TX_FSM_HIBERN8) {
> +		err = tx_fsm_val;
> +		dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
> +				__func__, err);
> +	}
> +
> +	return err;
> +}
> +
> +static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct phy *phy = host->generic_phy;
> +	int ret = 0;
> +	u8 major;
> +	u16 minor, step;
> +
> +	/* Assert PHY reset and apply PHY calibration values */
> +	ufs_qcom_assert_reset(hba);
> +	/* provide 1ms delay to let the reset pulse propagate */
> +	usleep_range(1000, 1100);
> +
> +	ufs_qcom_get_controller_revision(hba, &major, &minor, &step);
> +	ufs_qcom_phy_save_controller_version(phy, major, minor, step);
> +	ret = ufs_qcom_phy_calibrate_phy(phy);
> +	if (ret) {
> +		dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret =
> %d\n",
> +			__func__, ret);
> +		goto out;
> +	}
> +
> +	/* De-assert PHY reset and start serdes */
> +	ufs_qcom_deassert_reset(hba);
> +
> +	/*
> +	 * after reset deassertion, phy will need all ref clocks,
> +	 * voltage, current to settle down before starting serdes.
> +	 */
> +	usleep_range(1000, 1100);
> +	ret = ufs_qcom_phy_start_serdes(phy);
> +	if (ret) {
> +		dev_err(hba->dev, "%s: ufs_qcom_phy_start_serdes() failed, ret = %d\n",
> +			__func__, ret);
> +		goto out;
> +	}
> +
> +	ret = ufs_qcom_phy_is_pcs_ready(phy);
> +	if (ret)
> +		dev_err(hba->dev, "%s: is_physical_coding_sublayer_ready() failed, ret
> = %d\n",
> +			__func__, ret);
> +
> +out:
> +	return ret;
> +}
> +
> +/*
> + * The UTP controller has a number of internal clock gating cells (CGCs).
> + * Internal hardware sub-modules within the UTP controller control the
> CGCs.
> + * Hardware CGCs disable the clock to inactivate UTP sub-modules not
> involved
> + * in a specific operation, UTP controller CGCs are by default disabled
> and
> + * this function enables them (after every UFS link startup) to save some
> power
> + * leakage.
> + */
> +static void ufs_qcom_enable_hw_clk_gating(struct ufs_hba *hba)
> +{
> +	ufshcd_writel(hba,
> +		ufshcd_readl(hba, REG_UFS_CFG2) | REG_UFS_CFG2_CGC_EN_ALL,
> +		REG_UFS_CFG2);
> +
> +	/* Ensure that HW clock gating is enabled before next operations */
> +	mb();
> +}
> +
> +static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	int err = 0;
> +
> +	switch (status) {
> +	case PRE_CHANGE:
> +		ufs_qcom_power_up_sequence(hba);
> +		/*
> +		 * The PHY PLL output is the source of tx/rx lane symbol
> +		 * clocks, hence, enable the lane clocks only after PHY
> +		 * is initialized.
> +		 */
> +		err = ufs_qcom_enable_lane_clks(host);
> +		break;
> +	case POST_CHANGE:
> +		/* check if UFS PHY moved from DISABLED to HIBERN8 */
> +		err = ufs_qcom_check_hibern8(hba);
> +		ufs_qcom_enable_hw_clk_gating(hba);
> +		if (!err) {
> +			err = ufs_qcom_ice_reset(host);
> +			if (err)
> +				dev_err(hba->dev,
> +					"%s: ufs_qcom_ice_reset() failed %d\n",
> +					__func__, err);
> +		}
> +
> +		break;
> +	default:
> +		dev_err(hba->dev, "%s: invalid status %d\n", __func__, status);
> +		err = -EINVAL;
> +		break;
> +	}
> +	return err;
> +}
> +
> +/**
> + * Returns non-zero for success (which rate of core_clk) and 0
> + * in case of a failure
> + */
> +static unsigned long
> +ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate)
> +{
> +	struct ufs_clk_info *clki;
> +	u32 core_clk_period_in_ns;
> +	u32 tx_clk_cycles_per_us = 0;
> +	unsigned long core_clk_rate = 0;
> +	u32 core_clk_cycles_per_us = 0;
> +
> +	static u32 pwm_fr_table[][2] = {
> +		{UFS_PWM_G1, 0x1},
> +		{UFS_PWM_G2, 0x1},
> +		{UFS_PWM_G3, 0x1},
> +		{UFS_PWM_G4, 0x1},
> +	};
> +
> +	static u32 hs_fr_table_rA[][2] = {
> +		{UFS_HS_G1, 0x1F},
> +		{UFS_HS_G2, 0x3e},
> +	};
> +
> +	static u32 hs_fr_table_rB[][2] = {
> +		{UFS_HS_G1, 0x24},
> +		{UFS_HS_G2, 0x49},
> +	};
> +
> +	if (gear == 0) {
> +		dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear);
> +		goto out_error;
> +	}
> +
> +	list_for_each_entry(clki, &hba->clk_list_head, list) {
> +		if (!strcmp(clki->name, "core_clk"))
> +			core_clk_rate = clk_get_rate(clki->clk);
> +	}
> +
> +	/* If frequency is smaller than 1MHz, set to 1MHz */
> +	if (core_clk_rate < DEFAULT_CLK_RATE_HZ)
> +		core_clk_rate = DEFAULT_CLK_RATE_HZ;
> +
> +	core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC;
> +	ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US);
> +
> +	core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate;
> +	core_clk_period_in_ns <<= OFFSET_CLK_NS_REG;
> +	core_clk_period_in_ns &= MASK_CLK_NS_REG;
> +
> +	switch (hs) {
> +	case FASTAUTO_MODE:
> +	case FAST_MODE:
> +		if (rate == PA_HS_MODE_A) {
> +			if (gear > ARRAY_SIZE(hs_fr_table_rA)) {
> +				dev_err(hba->dev,
> +					"%s: index %d exceeds table size %zu\n",
> +					__func__, gear,
> +					ARRAY_SIZE(hs_fr_table_rA));
> +				goto out_error;
> +			}
> +			tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1];
> +		} else if (rate == PA_HS_MODE_B) {
> +			if (gear > ARRAY_SIZE(hs_fr_table_rB)) {
> +				dev_err(hba->dev,
> +					"%s: index %d exceeds table size %zu\n",
> +					__func__, gear,
> +					ARRAY_SIZE(hs_fr_table_rB));
> +				goto out_error;
> +			}
> +			tx_clk_cycles_per_us = hs_fr_table_rB[gear-1][1];
> +		} else {
> +			dev_err(hba->dev, "%s: invalid rate = %d\n",
> +				__func__, rate);
> +			goto out_error;
> +		}
> +		break;
> +	case SLOWAUTO_MODE:
> +	case SLOW_MODE:
> +		if (gear > ARRAY_SIZE(pwm_fr_table)) {
> +			dev_err(hba->dev,
> +					"%s: index %d exceeds table size %zu\n",
> +					__func__, gear,
> +					ARRAY_SIZE(pwm_fr_table));
> +			goto out_error;
> +		}
> +		tx_clk_cycles_per_us = pwm_fr_table[gear-1][1];
> +		break;
> +	case UNCHANGED:
> +	default:
> +		dev_err(hba->dev, "%s: invalid mode = %d\n", __func__, hs);
> +		goto out_error;
> +	}
> +
> +	/* this register 2 fields shall be written at once */
> +	ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us,
> +						REG_UFS_TX_SYMBOL_CLK_NS_US);
> +	goto out;
> +
> +out_error:
> +	core_clk_rate = 0;
> +out:
> +	return core_clk_rate;
> +}
> +
> +static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status)
> +{
> +	unsigned long core_clk_rate = 0;
> +	u32 core_clk_cycles_per_100ms;
> +
> +	switch (status) {
> +	case PRE_CHANGE:
> +		core_clk_rate = ufs_qcom_cfg_timers(hba, UFS_PWM_G1,
> +						    SLOWAUTO_MODE, 0);
> +		if (!core_clk_rate) {
> +			dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n",
> +				__func__);
> +			return -EINVAL;
> +		}
> +		core_clk_cycles_per_100ms =
> +			(core_clk_rate / MSEC_PER_SEC) * 100;
> +		ufshcd_writel(hba, core_clk_cycles_per_100ms,
> +					REG_UFS_PA_LINK_STARTUP_TIMER);
> +		break;
> +	case POST_CHANGE:
> +		ufs_qcom_link_startup_post_change(hba);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct phy *phy = host->generic_phy;
> +	int ret = 0;
> +
> +	if (ufs_qcom_is_link_off(hba)) {
> +		/*
> +		 * Disable the tx/rx lane symbol clocks before PHY is
> +		 * powered down as the PLL source should be disabled
> +		 * after downstream clocks are disabled.
> +		 */
> +		ufs_qcom_disable_lane_clks(host);
> +		phy_power_off(phy);
> +		ret = ufs_qcom_ice_suspend(host);
> +		if (ret)
> +			dev_err(hba->dev, "%s: failed ufs_qcom_ice_suspend %d\n",
> +					__func__, ret);
> +
> +		/* Assert PHY soft reset */
> +		ufs_qcom_assert_reset(hba);
> +		goto out;
> +	}
> +
> +	/*
> +	 * If UniPro link is not active, PHY ref_clk, main PHY analog power
> +	 * rail and low noise analog power rail for PLL can be switched off.
> +	 */
> +	if (!ufs_qcom_is_link_active(hba)) {
> +		if (ufs_qcom_phy_is_cfg_restore_quirk_enabled(phy) &&
> +		    ufs_qcom_is_link_hibern8(hba)) {
> +			ret = ufs_qcom_phy_save_configuration(phy);
> +			if (ret)
> +				dev_err(hba->dev, "%s: failed ufs_qcom_phy_save_configuration %d\n",
> +					__func__, ret);
> +		}
> +		phy_power_off(phy);
> +		ufs_qcom_ice_suspend(host);
> +	}
> +
> +out:
> +	return ret;
> +}
> +
> +static bool ufs_qcom_is_phy_config_restore_required(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct phy *phy = host->generic_phy;
> +
> +	return ufs_qcom_phy_is_cfg_restore_quirk_enabled(phy)
> +		&& ufshcd_is_link_hibern8(hba)
> +		&& hba->is_sys_suspended;
> +}
> +
> +static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct phy *phy = host->generic_phy;
> +	int err;
> +
> +	if (ufs_qcom_is_phy_config_restore_required(hba)) {
> +		ufs_qcom_assert_reset(hba);
> +		/* provide 1ms delay to let the reset pulse propagate */
> +		usleep_range(1000, 1100);
> +	}
> +
> +	err = phy_power_on(phy);
> +	if (err) {
> +		dev_err(hba->dev, "%s: failed enabling regs, err = %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	if (ufs_qcom_is_phy_config_restore_required(hba)) {
> +		ufs_qcom_phy_restore_swi_regs(phy);
> +
> +		/* De-assert PHY reset and start serdes */
> +		ufs_qcom_deassert_reset(hba);
> +
> +		/*
> +		 * after reset deassertion, phy will need all ref clocks,
> +		 * voltage, current to settle down before starting serdes.
> +		 */
> +		usleep_range(1000, 1100);
> +
> +		err = ufs_qcom_phy_start_serdes(phy);
> +		if (err) {
> +			dev_err(hba->dev, "%s: ufs_qcom_phy_start_serdes() failed, err =
> %d\n",
> +				__func__, err);
> +			goto out;
> +		}
> +
> +		err = ufs_qcom_phy_restore_configuration(phy);
> +		if (err) {
> +			dev_err(hba->dev, "%s: ufs_qcom_phy_restore_configuration() failed,
> err = %d\n",
> +				__func__, err);
> +			goto out;
> +		}
> +
> +		err = ufs_qcom_phy_is_pcs_ready(phy);
> +		if (err) {
> +			dev_err(hba->dev, "%s: is_physical_coding_sublayer_ready() failed, err
> = %d\n",
> +				__func__, err);
> +			goto out;
> +		}
> +	}
> +
> +	err = ufs_qcom_ice_resume(host);
> +	if (err) {
> +		dev_err(hba->dev, "%s: ufs_qcom_ice_resume failed, err = %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	hba->is_sys_suspended = false;
> +
> +out:
> +	return err;
> +}
> +
> +static
> +int ufs_qcom_crytpo_engine_cfg(struct ufs_hba *hba, unsigned int
> task_tag)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct ufshcd_lrb *lrbp = &hba->lrb[task_tag];
> +	int err = 0;
> +
> +	if (!host->ice.pdev ||
> +	    !lrbp->cmd || lrbp->command_type != UTP_CMD_TYPE_SCSI)
> +		goto out;
> +
> +	err = ufs_qcom_ice_cfg(host, lrbp->cmd);
> +out:
> +	return err;
> +}
> +
> +static int ufs_qcom_crypto_engine_eh(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	int ice_status = 0;
> +	int err = 0;
> +
> +	host->ice.crypto_engine_err = 0;
> +
> +	if (host->ice.quirks &
> +	    UFS_QCOM_ICE_QUIRK_HANDLE_CRYPTO_ENGINE_ERRORS) {
> +		err = ufs_qcom_ice_get_status(host, &ice_status);
> +		if (!err)
> +			host->ice.crypto_engine_err = ice_status;
> +
> +		if (host->ice.crypto_engine_err) {
> +			dev_err(hba->dev, "%s handling crypto engine error\n",
> +					__func__);
> +			/*
> +			 * block commands from scsi mid-layer.
> +			 * As crypto error is a fatal error and will result in
> +			 * a host reset we should leave scsi mid layer blocked
> +			 * until host reset is completed.
> +			 * Host reset will be handled in a seperate workqueue
> +			 * and will be triggered from ufshcd_check_errors.
> +			 */
> +			scsi_block_requests(hba->host);
> +
> +			ufshcd_abort_outstanding_transfer_requests(hba,
> +					DID_TARGET_FAILURE);
> +		}
> +	}
> +
> +	return host->ice.crypto_engine_err;
> +}
> +
> +static int ufs_qcom_crypto_engine_get_err(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +
> +	return host->ice.crypto_engine_err;
> +}
> +
> +static void ufs_qcom_crypto_engine_reset_err(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +
> +	host->ice.crypto_engine_err = 0;
> +}
> +
> +struct ufs_qcom_dev_params {
> +	u32 pwm_rx_gear;	/* pwm rx gear to work in */
> +	u32 pwm_tx_gear;	/* pwm tx gear to work in */
> +	u32 hs_rx_gear;		/* hs rx gear to work in */
> +	u32 hs_tx_gear;		/* hs tx gear to work in */
> +	u32 rx_lanes;		/* number of rx lanes */
> +	u32 tx_lanes;		/* number of tx lanes */
> +	u32 rx_pwr_pwm;		/* rx pwm working pwr */
> +	u32 tx_pwr_pwm;		/* tx pwm working pwr */
> +	u32 rx_pwr_hs;		/* rx hs working pwr */
> +	u32 tx_pwr_hs;		/* tx hs working pwr */
> +	u32 hs_rate;		/* rate A/B to work in HS */
> +	u32 desired_working_mode;
> +};
> +
> +static int ufs_qcom_get_pwr_dev_param(struct ufs_qcom_dev_params
> *qcom_param,
> +				      struct ufs_pa_layer_attr *dev_max,
> +				      struct ufs_pa_layer_attr *agreed_pwr)
> +{
> +	int min_qcom_gear;
> +	int min_dev_gear;
> +	bool is_dev_sup_hs = false;
> +	bool is_qcom_max_hs = false;
> +
> +	if (dev_max->pwr_rx == FAST_MODE)
> +		is_dev_sup_hs = true;
> +
> +	if (qcom_param->desired_working_mode == FAST) {
> +		is_qcom_max_hs = true;
> +		min_qcom_gear = min_t(u32, qcom_param->hs_rx_gear,
> +				      qcom_param->hs_tx_gear);
> +	} else {
> +		min_qcom_gear = min_t(u32, qcom_param->pwm_rx_gear,
> +				      qcom_param->pwm_tx_gear);
> +	}
> +
> +	/*
> +	 * device doesn't support HS but qcom_param->desired_working_mode is
> +	 * HS, thus device and qcom_param don't agree
> +	 */
> +	if (!is_dev_sup_hs && is_qcom_max_hs) {
> +		pr_err("%s: failed to agree on power mode (device doesn't support HS
> but requested power is HS)\n",
> +			__func__);
> +		return -ENOTSUPP;
> +	} else if (is_dev_sup_hs && is_qcom_max_hs) {
> +		/*
> +		 * since device supports HS, it supports FAST_MODE.
> +		 * since qcom_param->desired_working_mode is also HS
> +		 * then final decision (FAST/FASTAUTO) is done according
> +		 * to qcom_params as it is the restricting factor
> +		 */
> +		agreed_pwr->pwr_rx = agreed_pwr->pwr_tx =
> +						qcom_param->rx_pwr_hs;
> +	} else {
> +		/*
> +		 * here qcom_param->desired_working_mode is PWM.
> +		 * it doesn't matter whether device supports HS or PWM,
> +		 * in both cases qcom_param->desired_working_mode will
> +		 * determine the mode
> +		 */
> +		 agreed_pwr->pwr_rx = agreed_pwr->pwr_tx =
> +						qcom_param->rx_pwr_pwm;
> +	}
> +
> +	/*
> +	 * we would like tx to work in the minimum number of lanes
> +	 * between device capability and vendor preferences.
> +	 * the same decision will be made for rx
> +	 */
> +	agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx,
> +						qcom_param->tx_lanes);
> +	agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx,
> +						qcom_param->rx_lanes);
> +
> +	/* device maximum gear is the minimum between device rx and tx gears */
> +	min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx);
> +
> +	/*
> +	 * if both device capabilities and vendor pre-defined preferences are
> +	 * both HS or both PWM then set the minimum gear to be the chosen
> +	 * working gear.
> +	 * if one is PWM and one is HS then the one that is PWM get to decide
> +	 * what is the gear, as it is the one that also decided previously what
> +	 * pwr the device will be configured to.
> +	 */
> +	if ((is_dev_sup_hs && is_qcom_max_hs) ||
> +	    (!is_dev_sup_hs && !is_qcom_max_hs))
> +		agreed_pwr->gear_rx = agreed_pwr->gear_tx =
> +			min_t(u32, min_dev_gear, min_qcom_gear);
> +	else if (!is_dev_sup_hs)
> +		agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_dev_gear;
> +	else
> +		agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_qcom_gear;
> +
> +	agreed_pwr->hs_rate = qcom_param->hs_rate;
> +	return 0;
> +}
> +
> +static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host)
> +{
> +	int vote;
> +	int err = 0;
> +	char mode[BUS_VECTOR_NAME_LEN];
> +
> +	ufs_qcom_get_speed_mode(&host->dev_req_params, mode);
> +
> +	vote = ufs_qcom_get_bus_vote(host, mode);
> +	if (vote >= 0)
> +		err = ufs_qcom_set_bus_vote(host, vote);
> +	else
> +		err = vote;
> +
> +	if (err)
> +		dev_err(host->hba->dev, "%s: failed %d\n", __func__, err);
> +	else
> +		host->bus_vote.saved_vote = vote;
> +	return err;
> +}
> +
> +static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
> +				bool status,
> +				struct ufs_pa_layer_attr *dev_max_params,
> +				struct ufs_pa_layer_attr *dev_req_params)
> +{
> +	u32 val;
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct phy *phy = host->generic_phy;
> +	struct ufs_qcom_dev_params ufs_qcom_cap;
> +	int ret = 0;
> +	int res = 0;
> +
> +	if (!dev_req_params) {
> +		pr_err("%s: incoming dev_req_params is NULL\n", __func__);
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	switch (status) {
> +	case PRE_CHANGE:
> +		ufs_qcom_cap.tx_lanes = UFS_QCOM_LIMIT_NUM_LANES_TX;
> +		ufs_qcom_cap.rx_lanes = UFS_QCOM_LIMIT_NUM_LANES_RX;
> +		ufs_qcom_cap.hs_rx_gear = UFS_QCOM_LIMIT_HSGEAR_RX;
> +		ufs_qcom_cap.hs_tx_gear = UFS_QCOM_LIMIT_HSGEAR_TX;
> +		ufs_qcom_cap.pwm_rx_gear = UFS_QCOM_LIMIT_PWMGEAR_RX;
> +		ufs_qcom_cap.pwm_tx_gear = UFS_QCOM_LIMIT_PWMGEAR_TX;
> +		ufs_qcom_cap.rx_pwr_pwm = UFS_QCOM_LIMIT_RX_PWR_PWM;
> +		ufs_qcom_cap.tx_pwr_pwm = UFS_QCOM_LIMIT_TX_PWR_PWM;
> +		ufs_qcom_cap.rx_pwr_hs = UFS_QCOM_LIMIT_RX_PWR_HS;
> +		ufs_qcom_cap.tx_pwr_hs = UFS_QCOM_LIMIT_TX_PWR_HS;
> +		ufs_qcom_cap.hs_rate = UFS_QCOM_LIMIT_HS_RATE;
> +		ufs_qcom_cap.desired_working_mode =
> +					UFS_QCOM_LIMIT_DESIRED_MODE;
> +
> +		ret = ufs_qcom_get_pwr_dev_param(&ufs_qcom_cap,
> +						 dev_max_params,
> +						 dev_req_params);
> +		if (ret) {
> +			pr_err("%s: failed to determine capabilities\n",
> +					__func__);
> +			goto out;
> +		}
> +
> +		break;
> +	case POST_CHANGE:
> +		if (!ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
> +					dev_req_params->pwr_rx,
> +					dev_req_params->hs_rate)) {
> +			dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n",
> +				__func__);
> +			/*
> +			 * we return error code at the end of the routine,
> +			 * but continue to configure UFS_PHY_TX_LANE_ENABLE
> +			 * and bus voting as usual
> +			 */
> +			ret = -EINVAL;
> +		}
> +
> +		val = ~(MAX_U32 << dev_req_params->lane_tx);
> +		res = ufs_qcom_phy_set_tx_lane_enable(phy, val);
> +		if (res) {
> +			dev_err(hba->dev, "%s: ufs_qcom_phy_set_tx_lane_enable() failed res =
> %d\n",
> +				__func__, res);
> +			ret = res;
> +		}
> +
> +		/* cache the power mode parameters to use internally */
> +		memcpy(&host->dev_req_params,
> +				dev_req_params, sizeof(*dev_req_params));
> +		ufs_qcom_update_bus_bw_vote(host);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +out:
> +	return ret;
> +}
> +
> +/**
> + * ufs_qcom_advertise_quirks - advertise the known QCOM UFS controller
> quirks
> + * @hba: host controller instance
> + *
> + * QCOM UFS host controller might have some non standard behaviours
> (quirks)
> + * than what is specified by UFSHCI specification. Advertise all such
> + * quirks to standard UFS host controller driver so standard takes them
> into
> + * account.
> + */
> +static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
> +{
> +	u8 major;
> +	u16 minor, step;
> +
> +	ufs_qcom_get_controller_revision(hba, &major, &minor, &step);
> +
> +	if (major == 0x1) {
> +		hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
> +			      | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP
> +			      | UFSHCD_QUIRK_BROKEN_LCC
> +			      | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE);
> +
> +		if ((minor == 0x001) && (step == 0x0001))
> +			hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR;
> +	}
> +}
> +
> +static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host,
> +		const char *speed_mode)
> +{
> +	struct device *dev = host->hba->dev;
> +	struct device_node *np = dev->of_node;
> +	int err;
> +	const char *key = "qcom,bus-vector-names";
> +
> +	if (!speed_mode) {
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN"))
> +		err = of_property_match_string(np, key, "MAX");
> +	else
> +		err = of_property_match_string(np, key, speed_mode);
> +
> +out:
> +	if (err < 0)
> +		dev_err(dev, "%s: Invalid %s mode %d\n",
> +				__func__, speed_mode, err);
> +	return err;
> +}
> +
> +static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote)
> +{
> +	if (vote != host->bus_vote.curr_vote)
> +		host->bus_vote.curr_vote = vote;
> +
> +	return 0;
> +}
> +
> +static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char
> *result)
> +{
> +	int gear = max_t(u32, p->gear_rx, p->gear_tx);
> +	int lanes = max_t(u32, p->lane_rx, p->lane_tx);
> +	int pwr;
> +
> +	/* default to PWM Gear 1, Lane 1 if power mode is not initialized */
> +	if (!gear)
> +		gear = 1;
> +
> +	if (!lanes)
> +		lanes = 1;
> +
> +	if (!p->pwr_rx && !p->pwr_tx) {
> +		pwr = SLOWAUTO_MODE;
> +		snprintf(result, BUS_VECTOR_NAME_LEN, "MIN");
> +	} else if (p->pwr_rx == FAST_MODE || p->pwr_rx == FASTAUTO_MODE ||
> +		 p->pwr_tx == FAST_MODE || p->pwr_tx == FASTAUTO_MODE) {
> +		pwr = FAST_MODE;
> +		snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS",
> +			 p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes);
> +	} else {
> +		pwr = SLOW_MODE;
> +		snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d",
> +			 "PWM", gear, lanes);
> +	}
> +}
> +
> +static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	int err;
> +	int vote = 0;
> +
> +	/*
> +	 * In case ufs_qcom_init() is not yet done, simply ignore.
> +	 * This ufs_qcom_setup_clocks() shall be called from
> +	 * ufs_qcom_init() after init is done.
> +	 */
> +	if (!host)
> +		return 0;
> +
> +	if (on) {
> +		err = ufs_qcom_phy_enable_iface_clk(host->generic_phy);
> +		if (err)
> +			goto out;
> +
> +		err = ufs_qcom_phy_enable_ref_clk(host->generic_phy);
> +		if (err) {
> +			dev_err(hba->dev, "%s enable phy ref clock failed, err=%d\n",
> +				__func__, err);
> +			ufs_qcom_phy_disable_iface_clk(host->generic_phy);
> +			goto out;
> +		}
> +		/* enable the device ref clock */
> +		ufs_qcom_phy_enable_dev_ref_clk(host->generic_phy);
> +		vote = host->bus_vote.saved_vote;
> +		if (vote == host->bus_vote.min_bw_vote)
> +			ufs_qcom_update_bus_bw_vote(host);
> +	} else {
> +		/* M-PHY RMMI interface clocks can be turned off */
> +		ufs_qcom_phy_disable_iface_clk(host->generic_phy);
> +		if (!ufs_qcom_is_link_active(hba)) {
> +			/* turn off UFS local PHY ref_clk */
> +			ufs_qcom_phy_disable_ref_clk(host->generic_phy);
> +			/* disable device ref_clk */
> +			ufs_qcom_phy_disable_dev_ref_clk(host->generic_phy);
> +		}
> +		vote = host->bus_vote.min_bw_vote;
> +	}
> +
> +	err = ufs_qcom_set_bus_vote(host, vote);
> +	if (err)
> +		dev_err(hba->dev, "%s: set bus vote failed %d\n",
> +				__func__, err);
> +
> +out:
> +	return err;
> +}
> +
> +#define	ANDROID_BOOT_DEV_MAX	30
> +static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
> +static int get_android_boot_dev(char *str)
> +{
> +	strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX);
> +	return 1;
> +}
> +__setup("androidboot.bootdevice=", get_android_boot_dev);
> +
> +/**
> + * ufs_qcom_init - bind phy with controller
> + * @hba: host controller instance
> + *
> + * Binds PHY with controller and powers up PHY enabling clocks
> + * and regulators.
> + *
> + * Returns -EPROBE_DEFER if binding fails, returns negative error
> + * on phy power up failure and returns zero on success.
> + */
> +static int ufs_qcom_init(struct ufs_hba *hba)
> +{
> +	int err;
> +	struct device *dev = hba->dev;
> +	struct ufs_qcom_host *host;
> +
> +	if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev)))
> +		return -ENODEV;
> +
> +	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
> +	if (!host) {
> +		err = -ENOMEM;
> +		dev_err(dev, "%s: no memory for qcom ufs host\n", __func__);
> +		goto out;
> +	}
> +
> +	host->hba = hba;
> +	hba->priv = (void *)host;
> +
> +	err = ufs_qcom_ice_get_dev(host);
> +	if (err == -EPROBE_DEFER) {
> +		/*
> +		 * UFS driver might be probed before ICE driver does.
> +		 * In that case we would like to return EPROBE_DEFER code
> +		 * in order to delay its probing.
> +		 */
> +		dev_err(dev, "%s: required ICE device not probed yet err = %d\n",
> +			__func__, err);
> +		goto out_host_free;
> +
> +	} else if (err == -ENODEV) {
> +		/*
> +		 * ICE device is not enabled in DTS file. No need for further
> +		 * initialization of ICE driver.
> +		 */
> +		dev_warn(dev, "%s: ICE device is not enabled",
> +			__func__);
> +	} else if (err) {
> +		dev_err(dev, "%s: ufs_qcom_ice_get_dev failed %d\n",
> +			__func__, err);
> +		goto out_host_free;
> +	}
> +
> +	host->generic_phy = devm_phy_get(dev, "ufsphy");
> +
> +	if (IS_ERR(host->generic_phy)) {
> +		err = PTR_ERR(host->generic_phy);
> +		dev_err(dev, "%s: PHY get failed %d\n", __func__, err);
> +		goto out;
> +	}
> +
> +	/* restore the secure configuration */
> +	ufs_qcom_update_sec_cfg(hba, true);
> +
> +	phy_init(host->generic_phy);
> +	err = phy_power_on(host->generic_phy);
> +	if (err)
> +		goto out_unregister_bus;
> +
> +	err = ufs_qcom_init_lanes(host);
> +	if (err)
> +		goto out_disable_phy;
> +
> +	ufs_qcom_advertise_quirks(hba);
> +
> +	hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_CLK_SCALING;
> +	hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
> +
> +	ufs_qcom_setup_clocks(hba, true);
> +	if (host->ice.pdev) {
> +		err = ufs_qcom_ice_init(host);
> +		if (err) {
> +			dev_err(dev, "%s: ICE driver initialization failed (%d)\n",
> +				__func__, err);
> +			device_remove_file(dev, &host->bus_vote.max_bus_bw);
> +			goto out_disable_phy;
> +		}
> +	}
> +
> +	if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
> +		ufs_qcom_hosts[hba->dev->id] = host;
> +
> +	goto out;
> +
> +out_disable_phy:
> +	phy_power_off(host->generic_phy);
> +out_unregister_bus:
> +	phy_exit(host->generic_phy);
> +out_host_free:
> +	devm_kfree(dev, host);
> +	hba->priv = NULL;
> +out:
> +	return err;
> +}
> +
> +static void ufs_qcom_exit(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +
> +	ufs_qcom_disable_lane_clks(host);
> +	phy_power_off(host->generic_phy);
> +}
> +
> +
> +static void ufs_qcom_clk_scale_notify(struct ufs_hba *hba)
> +{
> +	struct ufs_qcom_host *host = hba->priv;
> +	struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params;
> +
> +	if (!dev_req_params)
> +		return;
> +
> +	ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
> +				dev_req_params->pwr_rx,
> +				dev_req_params->hs_rate);
> +	ufs_qcom_update_bus_bw_vote(host);
> +}
> +
> +/*
> + * This function should be called to restore the security configuration
> of UFS
> + * register space after coming out of UFS host core power collapse.
> + *
> + * @hba: host controller instance
> + * @restore_sec_cfg: Set "true" if secure configuration needs to be
> restored
> + * and set "false" when secure configuration is lost.
> + */
> +static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool
> restore_sec_cfg)
> +{
> +	int ret = 0, scm_ret = 0;
> +	struct ufs_qcom_host *host = hba->priv;
> +
> +	/* scm command buffer structrue */
> +	struct msm_scm_cmd_buf {
> +		unsigned int device_id;
> +		unsigned int spare;
> +	} cbuf = {0};
> +	#define RESTORE_SEC_CFG_CMD	0x2
> +	#define UFS_TZ_DEV_ID		19
> +
> +	if (!host || !hba->vreg_info.vdd_hba ||
> +	    !(host->sec_cfg_updated ^ restore_sec_cfg)) {
> +		return 0;
> +	} else if (!restore_sec_cfg) {
> +		/*
> +		 * Clear the flag so next time when this function is called
> +		 * with restore_sec_cfg set to true, we can restore the secure
> +		 * configuration.
> +		 */
> +		host->sec_cfg_updated = false;
> +		goto out;
> +	} else if (hba->clk_gating.state != CLKS_ON) {
> +		/*
> +		 * Clocks should be ON to restore the host controller secure
> +		 * configuration.
> +		 */
> +		goto out;
> +	}
> +
> +	/*
> +	 * If we are here, Host controller clocks are running, Host controller
> +	 * power collapse feature is supported and Host controller has just came
> +	 * out of power collapse.
> +	 */
> +	cbuf.device_id = UFS_TZ_DEV_ID;
> +
> +out:
> +	dev_dbg(hba->dev, "%s: ip: restore_sec_cfg %d, op: restore_sec_cfg %d,
> ret %d scm_ret %d\n",
> +		__func__, restore_sec_cfg, host->sec_cfg_updated, ret, scm_ret);
> +	return ret;
> +}
> +
> +/**
> + * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
> + *
> + * The variant operations configure the necessary controller and PHY
> + * handshake during initialization.
> + */
> +static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
> +	.name                   = "qcom",
> +	.init                   = ufs_qcom_init,
> +	.exit                   = ufs_qcom_exit,
> +	.clk_scale_notify	= ufs_qcom_clk_scale_notify,
> +	.setup_clocks           = ufs_qcom_setup_clocks,
> +	.hce_enable_notify      = ufs_qcom_hce_enable_notify,
> +	.link_startup_notify    = ufs_qcom_link_startup_notify,
> +	.pwr_change_notify	= ufs_qcom_pwr_change_notify,
> +	.suspend		= ufs_qcom_suspend,
> +	.resume			= ufs_qcom_resume,
> +	.update_sec_cfg		= ufs_qcom_update_sec_cfg,
> +	.crypto_engine_cfg	= ufs_qcom_crytpo_engine_cfg,
> +	.crypto_engine_eh	= ufs_qcom_crypto_engine_eh,
> +	.crypto_engine_get_err	= ufs_qcom_crypto_engine_get_err,
> +	.crypto_engine_reset_err = ufs_qcom_crypto_engine_reset_err,
> +};
> +EXPORT_SYMBOL(ufs_hba_qcom_vops);
> diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
> deleted file mode 100644
> index 42c459a..0000000
> --- a/drivers/scsi/ufs/ufs.h
> +++ /dev/null
> @@ -1,491 +0,0 @@
> -/*
> - * Universal Flash Storage Host controller driver
> - *
> - * This code is based on drivers/scsi/ufs/ufs.h
> - * Copyright (C) 2011-2013 Samsung India Software Operations
> - *
> - * Authors:
> - *	Santosh Yaraganavi <santosh.sy@xxxxxxxxxxx>
> - *	Vinayak Holikatti <h.vinayak@xxxxxxxxxxx>
> - *
> - * This program is free software; you can redistribute it and/or
> - * modify it under the terms of the GNU General Public License
> - * as published by the Free Software Foundation; either version 2
> - * of the License, or (at your option) any later version.
> - * See the COPYING file in the top-level directory or visit
> - * <http://www.gnu.org/licenses/gpl-2.0.html>
> - *
> - * 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.
> - *
> - * This program is provided "AS IS" and "WITH ALL FAULTS" and
> - * without warranty of any kind. You are solely responsible for
> - * determining the appropriateness of using and distributing
> - * the program and assume all risks associated with your exercise
> - * of rights with respect to the program, including but not limited
> - * to infringement of third party rights, the risks and costs of
> - * program errors, damage to or loss of data, programs or equipment,
> - * and unavailability or interruption of operations. Under no
> - * circumstances will the contributor of this Program be liable for
> - * any damages of any kind arising from your use or distribution of
> - * this program.
> - */
> -
> -#ifndef _UFS_H
> -#define _UFS_H
> -
> -#include <linux/mutex.h>
> -#include <linux/types.h>
> -
> -#define MAX_CDB_SIZE	16
> -#define GENERAL_UPIU_REQUEST_SIZE 32
> -#define QUERY_DESC_MAX_SIZE       255
> -#define QUERY_DESC_MIN_SIZE       2
> -#define QUERY_OSF_SIZE            (GENERAL_UPIU_REQUEST_SIZE - \
> -					(sizeof(struct utp_upiu_header)))
> -
> -#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
> -			cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
> -			 (byte1 << 8) | (byte0))
> -/*
> - * UFS device may have standard LUs and LUN id could be from 0x00 to
> - * 0x7F. Standard LUs use "Peripheral Device Addressing Format".
> - * UFS device may also have the Well Known LUs (also referred as W-LU)
> - * which again could be from 0x00 to 0x7F. For W-LUs, device only use
> - * the "Extended Addressing Format" which means the W-LUNs would be
> - * from 0xc100 (SCSI_W_LUN_BASE) onwards.
> - * This means max. LUN number reported from UFS device could be 0xC17F.
> - */
> -#define UFS_UPIU_MAX_UNIT_NUM_ID	0x7F
> -#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
> -
> -/* Well known logical unit id in LUN field of UPIU */
> -enum {
> -	UFS_UPIU_REPORT_LUNS_WLUN	= 0x81,
> -	UFS_UPIU_UFS_DEVICE_WLUN	= 0xD0,
> -	UFS_UPIU_BOOT_WLUN		= 0xB0,
> -	UFS_UPIU_RPMB_WLUN		= 0xC4,
> -};
> -
> -/*
> - * UFS Protocol Information Unit related definitions
> - */
> -
> -/* Task management functions */
> -enum {
> -	UFS_ABORT_TASK		= 0x01,
> -	UFS_ABORT_TASK_SET	= 0x02,
> -	UFS_CLEAR_TASK_SET	= 0x04,
> -	UFS_LOGICAL_RESET	= 0x08,
> -	UFS_QUERY_TASK		= 0x80,
> -	UFS_QUERY_TASK_SET	= 0x81,
> -};
> -
> -/* UTP UPIU Transaction Codes Initiator to Target */
> -enum {
> -	UPIU_TRANSACTION_NOP_OUT	= 0x00,
> -	UPIU_TRANSACTION_COMMAND	= 0x01,
> -	UPIU_TRANSACTION_DATA_OUT	= 0x02,
> -	UPIU_TRANSACTION_TASK_REQ	= 0x04,
> -	UPIU_TRANSACTION_QUERY_REQ	= 0x16,
> -};
> -
> -/* UTP UPIU Transaction Codes Target to Initiator */
> -enum {
> -	UPIU_TRANSACTION_NOP_IN		= 0x20,
> -	UPIU_TRANSACTION_RESPONSE	= 0x21,
> -	UPIU_TRANSACTION_DATA_IN	= 0x22,
> -	UPIU_TRANSACTION_TASK_RSP	= 0x24,
> -	UPIU_TRANSACTION_READY_XFER	= 0x31,
> -	UPIU_TRANSACTION_QUERY_RSP	= 0x36,
> -	UPIU_TRANSACTION_REJECT_UPIU	= 0x3F,
> -};
> -
> -/* UPIU Read/Write flags */
> -enum {
> -	UPIU_CMD_FLAGS_NONE	= 0x00,
> -	UPIU_CMD_FLAGS_WRITE	= 0x20,
> -	UPIU_CMD_FLAGS_READ	= 0x40,
> -};
> -
> -/* UPIU Task Attributes */
> -enum {
> -	UPIU_TASK_ATTR_SIMPLE	= 0x00,
> -	UPIU_TASK_ATTR_ORDERED	= 0x01,
> -	UPIU_TASK_ATTR_HEADQ	= 0x02,
> -	UPIU_TASK_ATTR_ACA	= 0x03,
> -};
> -
> -/* UPIU Query request function */
> -enum {
> -	UPIU_QUERY_FUNC_STANDARD_READ_REQUEST           = 0x01,
> -	UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST          = 0x81,
> -};
> -
> -/* Flag idn for Query Requests*/
> -enum flag_idn {
> -	QUERY_FLAG_IDN_FDEVICEINIT      = 0x01,
> -	QUERY_FLAG_IDN_PWR_ON_WPE	= 0x03,
> -	QUERY_FLAG_IDN_BKOPS_EN         = 0x04,
> -};
> -
> -/* Attribute idn for Query requests */
> -enum attr_idn {
> -	QUERY_ATTR_IDN_ACTIVE_ICC_LVL	= 0x03,
> -	QUERY_ATTR_IDN_BKOPS_STATUS	= 0x05,
> -	QUERY_ATTR_IDN_EE_CONTROL	= 0x0D,
> -	QUERY_ATTR_IDN_EE_STATUS	= 0x0E,
> -};
> -
> -/* Descriptor idn for Query requests */
> -enum desc_idn {
> -	QUERY_DESC_IDN_DEVICE		= 0x0,
> -	QUERY_DESC_IDN_CONFIGURAION	= 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_MAX,
> -};
> -
> -enum desc_header_offset {
> -	QUERY_DESC_LENGTH_OFFSET	= 0x00,
> -	QUERY_DESC_DESC_TYPE_OFFSET	= 0x01,
> -};
> -
> -enum ufs_desc_max_size {
> -	QUERY_DESC_DEVICE_MAX_SIZE		= 0x1F,
> -	QUERY_DESC_CONFIGURAION_MAX_SIZE	= 0x90,
> -	QUERY_DESC_UNIT_MAX_SIZE		= 0x23,
> -	QUERY_DESC_INTERCONNECT_MAX_SIZE	= 0x06,
> -	/*
> -	 * Max. 126 UNICODE characters (2 bytes per character) plus 2 bytes
> -	 * of descriptor header.
> -	 */
> -	QUERY_DESC_STRING_MAX_SIZE		= 0xFE,
> -	QUERY_DESC_GEOMETRY_MAZ_SIZE		= 0x44,
> -	QUERY_DESC_POWER_MAX_SIZE		= 0x62,
> -	QUERY_DESC_RFU_MAX_SIZE			= 0x00,
> -};
> -
> -/* Unit descriptor parameters offsets in bytes*/
> -enum unit_desc_param {
> -	UNIT_DESC_PARAM_LEN			= 0x0,
> -	UNIT_DESC_PARAM_TYPE			= 0x1,
> -	UNIT_DESC_PARAM_UNIT_INDEX		= 0x2,
> -	UNIT_DESC_PARAM_LU_ENABLE		= 0x3,
> -	UNIT_DESC_PARAM_BOOT_LUN_ID		= 0x4,
> -	UNIT_DESC_PARAM_LU_WR_PROTECT		= 0x5,
> -	UNIT_DESC_PARAM_LU_Q_DEPTH		= 0x6,
> -	UNIT_DESC_PARAM_MEM_TYPE		= 0x8,
> -	UNIT_DESC_PARAM_DATA_RELIABILITY	= 0x9,
> -	UNIT_DESC_PARAM_LOGICAL_BLK_SIZE	= 0xA,
> -	UNIT_DESC_PARAM_LOGICAL_BLK_COUNT	= 0xB,
> -	UNIT_DESC_PARAM_ERASE_BLK_SIZE		= 0x13,
> -	UNIT_DESC_PARAM_PROVISIONING_TYPE	= 0x17,
> -	UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT	= 0x18,
> -	UNIT_DESC_PARAM_CTX_CAPABILITIES	= 0x20,
> -	UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1	= 0x22,
> -};
> -
> -/*
> - * Logical Unit Write Protect
> - * 00h: LU not write protected
> - * 01h: LU write protected when fPowerOnWPEn =1
> - * 02h: LU permanently write protected when fPermanentWPEn =1
> - */
> -enum ufs_lu_wp_type {
> -	UFS_LU_NO_WP		= 0x00,
> -	UFS_LU_POWER_ON_WP	= 0x01,
> -	UFS_LU_PERM_WP		= 0x02,
> -};
> -
> -/* bActiveICCLevel parameter current units */
> -enum {
> -	UFSHCD_NANO_AMP		= 0,
> -	UFSHCD_MICRO_AMP	= 1,
> -	UFSHCD_MILI_AMP		= 2,
> -	UFSHCD_AMP		= 3,
> -};
> -
> -#define POWER_DESC_MAX_SIZE			0x62
> -#define POWER_DESC_MAX_ACTV_ICC_LVLS		16
> -
> -/* Attribute  bActiveICCLevel parameter bit masks definitions */
> -#define ATTR_ICC_LVL_UNIT_OFFSET	14
> -#define ATTR_ICC_LVL_UNIT_MASK		(0x3 << ATTR_ICC_LVL_UNIT_OFFSET)
> -#define ATTR_ICC_LVL_VALUE_MASK		0x3FF
> -
> -/* Power descriptor parameters offsets in bytes */
> -enum power_desc_param_offset {
> -	PWR_DESC_LEN			= 0x0,
> -	PWR_DESC_TYPE			= 0x1,
> -	PWR_DESC_ACTIVE_LVLS_VCC_0	= 0x2,
> -	PWR_DESC_ACTIVE_LVLS_VCCQ_0	= 0x22,
> -	PWR_DESC_ACTIVE_LVLS_VCCQ2_0	= 0x42,
> -};
> -
> -/* Exception event mask values */
> -enum {
> -	MASK_EE_STATUS		= 0xFFFF,
> -	MASK_EE_URGENT_BKOPS	= (1 << 2),
> -};
> -
> -/* Background operation status */
> -enum bkops_status {
> -	BKOPS_STATUS_NO_OP               = 0x0,
> -	BKOPS_STATUS_NON_CRITICAL        = 0x1,
> -	BKOPS_STATUS_PERF_IMPACT         = 0x2,
> -	BKOPS_STATUS_CRITICAL            = 0x3,
> -	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,
> -};
> -
> -/* Query response result code */
> -enum {
> -	QUERY_RESULT_SUCCESS                    = 0x00,
> -	QUERY_RESULT_NOT_READABLE               = 0xF6,
> -	QUERY_RESULT_NOT_WRITEABLE              = 0xF7,
> -	QUERY_RESULT_ALREADY_WRITTEN            = 0xF8,
> -	QUERY_RESULT_INVALID_LENGTH             = 0xF9,
> -	QUERY_RESULT_INVALID_VALUE              = 0xFA,
> -	QUERY_RESULT_INVALID_SELECTOR           = 0xFB,
> -	QUERY_RESULT_INVALID_INDEX              = 0xFC,
> -	QUERY_RESULT_INVALID_IDN                = 0xFD,
> -	QUERY_RESULT_INVALID_OPCODE             = 0xFE,
> -	QUERY_RESULT_GENERAL_FAILURE            = 0xFF,
> -};
> -
> -/* UTP Transfer Request Command Type (CT) */
> -enum {
> -	UPIU_COMMAND_SET_TYPE_SCSI	= 0x0,
> -	UPIU_COMMAND_SET_TYPE_UFS	= 0x1,
> -	UPIU_COMMAND_SET_TYPE_QUERY	= 0x2,
> -};
> -
> -/* UTP Transfer Request Command Offset */
> -#define UPIU_COMMAND_TYPE_OFFSET	28
> -
> -/* Offset of the response code in the UPIU header */
> -#define UPIU_RSP_CODE_OFFSET		8
> -
> -enum {
> -	MASK_SCSI_STATUS		= 0xFF,
> -	MASK_TASK_RESPONSE              = 0xFF00,
> -	MASK_RSP_UPIU_RESULT            = 0xFFFF,
> -	MASK_QUERY_DATA_SEG_LEN         = 0xFFFF,
> -	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
> -	MASK_RSP_EXCEPTION_EVENT        = 0x10000,
> -};
> -
> -/* Task management service response */
> -enum {
> -	UPIU_TASK_MANAGEMENT_FUNC_COMPL		= 0x00,
> -	UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
> -	UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED	= 0x08,
> -	UPIU_TASK_MANAGEMENT_FUNC_FAILED	= 0x05,
> -	UPIU_INCORRECT_LOGICAL_UNIT_NO		= 0x09,
> -};
> -
> -/* UFS device power modes */
> -enum ufs_dev_pwr_mode {
> -	UFS_ACTIVE_PWR_MODE	= 1,
> -	UFS_SLEEP_PWR_MODE	= 2,
> -	UFS_POWERDOWN_PWR_MODE	= 3,
> -};
> -
> -/**
> - * struct utp_upiu_header - UPIU header structure
> - * @dword_0: UPIU header DW-0
> - * @dword_1: UPIU header DW-1
> - * @dword_2: UPIU header DW-2
> - */
> -struct utp_upiu_header {
> -	__be32 dword_0;
> -	__be32 dword_1;
> -	__be32 dword_2;
> -};
> -
> -/**
> - * struct utp_upiu_cmd - Command UPIU structure
> - * @data_transfer_len: Data Transfer Length DW-3
> - * @cdb: Command Descriptor Block CDB DW-4 to DW-7
> - */
> -struct utp_upiu_cmd {
> -	__be32 exp_data_transfer_len;
> -	u8 cdb[MAX_CDB_SIZE];
> -};
> -
> -/**
> - * struct utp_upiu_query - upiu request buffer structure for
> - * query request.
> - * @opcode: command to perform B-0
> - * @idn: a value that indicates the particular type of data B-1
> - * @index: Index to further identify data B-2
> - * @selector: Index to further identify data B-3
> - * @reserved_osf: spec reserved field B-4,5
> - * @length: number of descriptor bytes to read/write B-6,7
> - * @value: Attribute value to be written DW-5
> - * @reserved: spec reserved DW-6,7
> - */
> -struct utp_upiu_query {
> -	u8 opcode;
> -	u8 idn;
> -	u8 index;
> -	u8 selector;
> -	__be16 reserved_osf;
> -	__be16 length;
> -	__be32 value;
> -	__be32 reserved[2];
> -};
> -
> -/**
> - * struct utp_upiu_req - general upiu request structure
> - * @header:UPIU header structure DW-0 to DW-2
> - * @sc: fields structure for scsi command DW-3 to DW-7
> - * @qr: fields structure for query request DW-3 to DW-7
> - */
> -struct utp_upiu_req {
> -	struct utp_upiu_header header;
> -	union {
> -		struct utp_upiu_cmd sc;
> -		struct utp_upiu_query qr;
> -	};
> -};
> -
> -/**
> - * struct utp_cmd_rsp - Response UPIU structure
> - * @residual_transfer_count: Residual transfer count DW-3
> - * @reserved: Reserved double words DW-4 to DW-7
> - * @sense_data_len: Sense data length DW-8 U16
> - * @sense_data: Sense data field DW-8 to DW-12
> - */
> -struct utp_cmd_rsp {
> -	__be32 residual_transfer_count;
> -	__be32 reserved[4];
> -	__be16 sense_data_len;
> -	u8 sense_data[18];
> -};
> -
> -/**
> - * struct utp_upiu_rsp - general upiu response structure
> - * @header: UPIU header structure DW-0 to DW-2
> - * @sr: fields structure for scsi command DW-3 to DW-12
> - * @qr: fields structure for query request DW-3 to DW-7
> - */
> -struct utp_upiu_rsp {
> -	struct utp_upiu_header header;
> -	union {
> -		struct utp_cmd_rsp sr;
> -		struct utp_upiu_query qr;
> -	};
> -};
> -
> -/**
> - * struct utp_upiu_task_req - Task request UPIU structure
> - * @header - UPIU header structure DW0 to DW-2
> - * @input_param1: Input parameter 1 DW-3
> - * @input_param2: Input parameter 2 DW-4
> - * @input_param3: Input parameter 3 DW-5
> - * @reserved: Reserved double words DW-6 to DW-7
> - */
> -struct utp_upiu_task_req {
> -	struct utp_upiu_header header;
> -	__be32 input_param1;
> -	__be32 input_param2;
> -	__be32 input_param3;
> -	__be32 reserved[2];
> -};
> -
> -/**
> - * struct utp_upiu_task_rsp - Task Management Response UPIU structure
> - * @header: UPIU header structure DW0-DW-2
> - * @output_param1: Ouput parameter 1 DW3
> - * @output_param2: Output parameter 2 DW4
> - * @reserved: Reserved double words DW-5 to DW-7
> - */
> -struct utp_upiu_task_rsp {
> -	struct utp_upiu_header header;
> -	__be32 output_param1;
> -	__be32 output_param2;
> -	__be32 reserved[3];
> -};
> -
> -/**
> - * struct ufs_query_req - parameters for building a query request
> - * @query_func: UPIU header query function
> - * @upiu_req: the query request data
> - */
> -struct ufs_query_req {
> -	u8 query_func;
> -	struct utp_upiu_query upiu_req;
> -};
> -
> -/**
> - * struct ufs_query_resp - UPIU QUERY
> - * @response: device response code
> - * @upiu_res: query response data
> - */
> -struct ufs_query_res {
> -	u8 response;
> -	struct utp_upiu_query upiu_res;
> -};
> -
> -#define UFS_VREG_VCC_MIN_UV	   2700000 /* uV */
> -#define UFS_VREG_VCC_MAX_UV	   3600000 /* uV */
> -#define UFS_VREG_VCC_1P8_MIN_UV    1700000 /* uV */
> -#define UFS_VREG_VCC_1P8_MAX_UV    1950000 /* uV */
> -#define UFS_VREG_VCCQ_MIN_UV	   1100000 /* uV */
> -#define UFS_VREG_VCCQ_MAX_UV	   1300000 /* uV */
> -#define UFS_VREG_VCCQ2_MIN_UV	   1650000 /* uV */
> -#define UFS_VREG_VCCQ2_MAX_UV	   1950000 /* uV */
> -
> -/*
> - * VCCQ & VCCQ2 current requirement when UFS device is in sleep state
> - * and link is in Hibern8 state.
> - */
> -#define UFS_VREG_LPM_LOAD_UA	1000 /* uA */
> -
> -struct ufs_vreg {
> -	struct regulator *reg;
> -	const char *name;
> -	bool enabled;
> -	int min_uV;
> -	int max_uV;
> -	int min_uA;
> -	int max_uA;
> -};
> -
> -struct ufs_vreg_info {
> -	struct ufs_vreg *vcc;
> -	struct ufs_vreg *vccq;
> -	struct ufs_vreg *vccq2;
> -	struct ufs_vreg *vdd_hba;
> -};
> -
> -struct ufs_dev_info {
> -	bool f_power_on_wp_en;
> -	/* Keeps information if any of the LU is power on write protected */
> -	bool is_lu_power_on_wp;
> -};
> -
> -#endif /* End of Header */
> diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
> index 955ed55..2a18b8e 100644
> --- a/drivers/scsi/ufs/ufshcd-pci.c
> +++ b/drivers/scsi/ufs/ufshcd-pci.c
> @@ -33,7 +33,7 @@
>   * this program.
>   */
>
> -#include "ufshcd.h"
> +#include <linux/scsi/ufs/ufshcd.h>
>  #include <linux/pci.h>
>  #include <linux/pm_runtime.h>
>
> diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c
> b/drivers/scsi/ufs/ufshcd-pltfrm.c
> index 1c3467b..5170811 100644
> --- a/drivers/scsi/ufs/ufshcd-pltfrm.c
> +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
> @@ -37,7 +37,7 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/of.h>
>
> -#include "ufshcd.h"
> +#include <linux/scsi/ufs/ufshcd.h>
>
>  static const struct of_device_id ufs_of_match[];
>  static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index a7ff00d..815eccb 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -40,8 +40,9 @@
>  #include <linux/async.h>
>  #include <linux/devfreq.h>
>
> -#include "ufshcd.h"
> -#include "unipro.h"
> +#include <linux/scsi/ufs/ufshcd.h>
> +#include <linux/scsi/ufs/unipro.h>
> +#include "ufshci.h"
>
>  #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
>  				 UTP_TASK_REQ_COMPL |\
> @@ -778,11 +779,24 @@ static void ufshcd_clk_scaling_update_busy(struct
> ufs_hba *hba)
>   * @task_tag: Task tag of the command
>   */
>  static inline
> -void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
> +int ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
>  {
> +	int ret = 0;
> +
> +	if (hba->vops->crypto_engine_cfg) {
> +		ret = hba->vops->crypto_engine_cfg(hba, task_tag);
> +		if (ret) {
> +			dev_err(hba->dev,
> +				"%s: failed to configure crypto engine %d\n",
> +				__func__, ret);
> +			return ret;
> +		}
> +	}
>  	ufshcd_clk_scaling_start_busy(hba);
>  	__set_bit(task_tag, &hba->outstanding_reqs);
>  	ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
> +
> +	return ret;
>  }
>
>  /**
> @@ -1335,7 +1349,14 @@ static int ufshcd_queuecommand(struct Scsi_Host
> *host, struct scsi_cmnd *cmd)
>
>  	/* issue command to the controller */
>  	spin_lock_irqsave(hba->host->host_lock, flags);
> -	ufshcd_send_command(hba, tag);
> +
> +	err = ufshcd_send_command(hba, tag);
> +	if (err) {
> +		dev_err(hba->dev, "%s: failed sending command, %d\n",
> +							__func__, err);
> +		err = DID_ERROR;
> +	}
> +
>  out_unlock:
>  	spin_unlock_irqrestore(hba->host->host_lock, flags);
>  out:
> @@ -1535,9 +1556,13 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
>  	hba->dev_cmd.complete = &wait;
>
>  	spin_lock_irqsave(hba->host->host_lock, flags);
> -	ufshcd_send_command(hba, tag);
> +	err = ufshcd_send_command(hba, tag);
>  	spin_unlock_irqrestore(hba->host->host_lock, flags);
> -
> +	if (err) {
> +		dev_err(hba->dev, "%s: failed sending command, %d\n",
> +							__func__, err);
> +		goto out_put_tag;
> +	}
>  	err = ufshcd_wait_for_dev_cmd(hba, lrbp, timeout);
>
>  out_put_tag:
> @@ -2516,6 +2541,15 @@ out:
>  }
>
>  /**
> + * ufshcd_hba_stop - Send controller to reset state
> + * @hba: per adapter instance
> + */
> +static inline void ufshcd_hba_stop(struct ufs_hba *hba)
> +{
> +	ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
> +}
> +
> +/**
>   * ufshcd_hba_enable - initialize the controller
>   * @hba: per adapter instance
>   *
> @@ -3017,6 +3051,42 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba
> *hba, u32 intr_status)
>  }
>
>  /**
> + * ufshcd_abort_outstanding_requests - abort all outstanding transfer
> requests.
> + * @hba: per adapter instance
> + * @result: error result to inform scsi layer about
> + */
> +void ufshcd_abort_outstanding_transfer_requests(struct ufs_hba *hba, int
> result)
> +{
> +	u8 index;
> +	struct ufshcd_lrb *lrbp;
> +	struct scsi_cmnd *cmd;
> +
> +	if (!hba->outstanding_reqs)
> +		return;
> +
> +	for_each_set_bit(index, &hba->outstanding_reqs, hba->nutrs) {
> +		lrbp = &hba->lrb[index];
> +		cmd = lrbp->cmd;
> +		if (cmd) {
> +			scsi_dma_unmap(cmd);
> +			cmd->result = result;
> +			/* Mark completed command as NULL in LRB */
> +			lrbp->cmd = NULL;
> +			/* Clear pending transfer requests */
> +			ufshcd_clear_cmd(hba, index);
> +			__clear_bit(index, &hba->outstanding_tasks);
> +			clear_bit_unlock(index, &hba->lrb_in_use);
> +			/* Do not touch lrbp after scsi done */
> +			cmd->scsi_done(cmd);
> +			ufshcd_release(hba);
> +		} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) {
> +			if (hba->dev_cmd.complete)
> +				complete(hba->dev_cmd.complete);
> +		}
> +	}
> +}
> +
> +/**
>   * ufshcd_transfer_req_compl - handle SCSI and query command completion
>   * @hba: per adapter instance
>   */
> @@ -3336,6 +3406,7 @@ static void ufshcd_err_handler(struct work_struct
> *work)
>  	u32 err_tm = 0;
>  	int err = 0;
>  	int tag;
> +	int crypto_engine_err = 0;
>
>  	hba = container_of(work, struct ufs_hba, eh_work);
>
> @@ -3354,6 +3425,9 @@ static void ufshcd_err_handler(struct work_struct
> *work)
>  	/* Complete requests that have door-bell cleared by h/w */
>  	ufshcd_transfer_req_compl(hba);
>  	ufshcd_tmc_handler(hba);
> +
> +	if (hba->vops && hba->vops->crypto_engine_get_err)
> +		crypto_engine_err = hba->vops->crypto_engine_get_err(hba);
>  	spin_unlock_irqrestore(hba->host->host_lock, flags);
>
>  	/* Clear pending transfer requests */
> @@ -3435,8 +3509,12 @@ static void ufshcd_update_uic_error(struct ufs_hba
> *hba)
>  static void ufshcd_check_errors(struct ufs_hba *hba)
>  {
>  	bool queue_eh_work = false;
> +	int crypto_engine_err = 0;
>
> -	if (hba->errors & INT_FATAL_ERRORS)
> +	if (hba->vops && hba->vops->crypto_engine_get_err)
> +		crypto_engine_err = hba->vops->crypto_engine_get_err(hba);
> +
> +	if (hba->errors & INT_FATAL_ERRORS || crypto_engine_err)
>  		queue_eh_work = true;
>
>  	if (hba->errors & UIC_ERROR) {
> @@ -3488,6 +3566,11 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)
>   */
>  static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
>  {
> +	bool crypto_engine_err = false;
> +
> +	if (hba->vops && hba->vops->crypto_engine_eh)
> +		crypto_engine_err = hba->vops->crypto_engine_eh(hba);
> +
>  	hba->errors = UFSHCD_ERROR_MASK & intr_status;
>  	if (hba->errors)
>  		ufshcd_check_errors(hba);
> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> deleted file mode 100644
> index 4a574aa..0000000
> --- a/drivers/scsi/ufs/ufshcd.h
> +++ /dev/null
> @@ -1,596 +0,0 @@
> -/*
> - * Universal Flash Storage Host controller driver
> - *
> - * This code is based on drivers/scsi/ufs/ufshcd.h
> - * Copyright (C) 2011-2013 Samsung India Software Operations
> - *
> - * Authors:
> - *	Santosh Yaraganavi <santosh.sy@xxxxxxxxxxx>
> - *	Vinayak Holikatti <h.vinayak@xxxxxxxxxxx>
> - *
> - * This program is free software; you can redistribute it and/or
> - * modify it under the terms of the GNU General Public License
> - * as published by the Free Software Foundation; either version 2
> - * of the License, or (at your option) any later version.
> - * See the COPYING file in the top-level directory or visit
> - * <http://www.gnu.org/licenses/gpl-2.0.html>
> - *
> - * 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.
> - *
> - * This program is provided "AS IS" and "WITH ALL FAULTS" and
> - * without warranty of any kind. You are solely responsible for
> - * determining the appropriateness of using and distributing
> - * the program and assume all risks associated with your exercise
> - * of rights with respect to the program, including but not limited
> - * to infringement of third party rights, the risks and costs of
> - * program errors, damage to or loss of data, programs or equipment,
> - * and unavailability or interruption of operations. Under no
> - * circumstances will the contributor of this Program be liable for
> - * any damages of any kind arising from your use or distribution of
> - * this program.
> - */
> -
> -#ifndef _UFSHCD_H
> -#define _UFSHCD_H
> -
> -#include <linux/module.h>
> -#include <linux/kernel.h>
> -#include <linux/init.h>
> -#include <linux/interrupt.h>
> -#include <linux/io.h>
> -#include <linux/delay.h>
> -#include <linux/slab.h>
> -#include <linux/spinlock.h>
> -#include <linux/workqueue.h>
> -#include <linux/errno.h>
> -#include <linux/types.h>
> -#include <linux/wait.h>
> -#include <linux/bitops.h>
> -#include <linux/pm_runtime.h>
> -#include <linux/clk.h>
> -#include <linux/completion.h>
> -#include <linux/regulator/consumer.h>
> -
> -#include <asm/irq.h>
> -#include <asm/byteorder.h>
> -#include <scsi/scsi.h>
> -#include <scsi/scsi_cmnd.h>
> -#include <scsi/scsi_host.h>
> -#include <scsi/scsi_tcq.h>
> -#include <scsi/scsi_dbg.h>
> -#include <scsi/scsi_eh.h>
> -
> -#include "ufs.h"
> -#include "ufshci.h"
> -
> -#define UFSHCD "ufshcd"
> -#define UFSHCD_DRIVER_VERSION "0.2"
> -
> -struct ufs_hba;
> -
> -enum dev_cmd_type {
> -	DEV_CMD_TYPE_NOP		= 0x0,
> -	DEV_CMD_TYPE_QUERY		= 0x1,
> -};
> -
> -/**
> - * struct uic_command - UIC command structure
> - * @command: UIC command
> - * @argument1: UIC command argument 1
> - * @argument2: UIC command argument 2
> - * @argument3: UIC command argument 3
> - * @cmd_active: Indicate if UIC command is outstanding
> - * @result: UIC command result
> - * @done: UIC command completion
> - */
> -struct uic_command {
> -	u32 command;
> -	u32 argument1;
> -	u32 argument2;
> -	u32 argument3;
> -	int cmd_active;
> -	int result;
> -	struct completion done;
> -};
> -
> -/* Used to differentiate the power management options */
> -enum ufs_pm_op {
> -	UFS_RUNTIME_PM,
> -	UFS_SYSTEM_PM,
> -	UFS_SHUTDOWN_PM,
> -};
> -
> -#define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM)
> -#define ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM)
> -#define ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM)
> -
> -/* Host <-> Device UniPro Link state */
> -enum uic_link_state {
> -	UIC_LINK_OFF_STATE	= 0, /* Link powered down or disabled */
> -	UIC_LINK_ACTIVE_STATE	= 1, /* Link is in Fast/Slow/Sleep state */
> -	UIC_LINK_HIBERN8_STATE	= 2, /* Link is in Hibernate state */
> -};
> -
> -#define ufshcd_is_link_off(hba) ((hba)->uic_link_state ==
> UIC_LINK_OFF_STATE)
> -#define ufshcd_is_link_active(hba) ((hba)->uic_link_state == \
> -				    UIC_LINK_ACTIVE_STATE)
> -#define ufshcd_is_link_hibern8(hba) ((hba)->uic_link_state == \
> -				    UIC_LINK_HIBERN8_STATE)
> -#define ufshcd_set_link_off(hba) ((hba)->uic_link_state =
> UIC_LINK_OFF_STATE)
> -#define ufshcd_set_link_active(hba) ((hba)->uic_link_state = \
> -				    UIC_LINK_ACTIVE_STATE)
> -#define ufshcd_set_link_hibern8(hba) ((hba)->uic_link_state = \
> -				    UIC_LINK_HIBERN8_STATE)
> -
> -/*
> - * UFS Power management levels.
> - * Each level is in increasing order of power savings.
> - */
> -enum ufs_pm_level {
> -	UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE */
> -	UFS_PM_LVL_1, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE */
> -	UFS_PM_LVL_2, /* UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE */
> -	UFS_PM_LVL_3, /* UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE */
> -	UFS_PM_LVL_4, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE */
> -	UFS_PM_LVL_5, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE */
> -	UFS_PM_LVL_MAX
> -};
> -
> -struct ufs_pm_lvl_states {
> -	enum ufs_dev_pwr_mode dev_state;
> -	enum uic_link_state link_state;
> -};
> -
> -/**
> - * struct ufshcd_lrb - local reference block
> - * @utr_descriptor_ptr: UTRD address of the command
> - * @ucd_req_ptr: UCD address of the command
> - * @ucd_rsp_ptr: Response UPIU address for this command
> - * @ucd_prdt_ptr: PRDT address of the command
> - * @cmd: pointer to SCSI command
> - * @sense_buffer: pointer to sense buffer address of the SCSI command
> - * @sense_bufflen: Length of the sense buffer
> - * @scsi_status: SCSI status of the command
> - * @command_type: SCSI, UFS, Query.
> - * @task_tag: Task tag of the command
> - * @lun: LUN of the command
> - * @intr_cmd: Interrupt command (doesn't participate in interrupt
> aggregation)
> - */
> -struct ufshcd_lrb {
> -	struct utp_transfer_req_desc *utr_descriptor_ptr;
> -	struct utp_upiu_req *ucd_req_ptr;
> -	struct utp_upiu_rsp *ucd_rsp_ptr;
> -	struct ufshcd_sg_entry *ucd_prdt_ptr;
> -
> -	struct scsi_cmnd *cmd;
> -	u8 *sense_buffer;
> -	unsigned int sense_bufflen;
> -	int scsi_status;
> -
> -	int command_type;
> -	int task_tag;
> -	u8 lun; /* UPIU LUN id field is only 8-bit wide */
> -	bool intr_cmd;
> -};
> -
> -/**
> - * struct ufs_query - holds relevent data structures for query request
> - * @request: request upiu and function
> - * @descriptor: buffer for sending/receiving descriptor
> - * @response: response upiu and response
> - */
> -struct ufs_query {
> -	struct ufs_query_req request;
> -	u8 *descriptor;
> -	struct ufs_query_res response;
> -};
> -
> -/**
> - * struct ufs_dev_cmd - all assosiated fields with device management
> commands
> - * @type: device management command type - Query, NOP OUT
> - * @lock: lock to allow one command at a time
> - * @complete: internal commands completion
> - * @tag_wq: wait queue until free command slot is available
> - */
> -struct ufs_dev_cmd {
> -	enum dev_cmd_type type;
> -	struct mutex lock;
> -	struct completion *complete;
> -	wait_queue_head_t tag_wq;
> -	struct ufs_query query;
> -};
> -
> -/**
> - * struct ufs_clk_info - UFS clock related info
> - * @list: list headed by hba->clk_list_head
> - * @clk: clock node
> - * @name: clock name
> - * @max_freq: maximum frequency supported by the clock
> - * @min_freq: min frequency that can be used for clock scaling
> - * @curr_freq: indicates the current frequency that it is set to
> - * @enabled: variable to check against multiple enable/disable
> - */
> -struct ufs_clk_info {
> -	struct list_head list;
> -	struct clk *clk;
> -	const char *name;
> -	u32 max_freq;
> -	u32 min_freq;
> -	u32 curr_freq;
> -	bool enabled;
> -};
> -
> -#define PRE_CHANGE      0
> -#define POST_CHANGE     1
> -
> -struct ufs_pa_layer_attr {
> -	u32 gear_rx;
> -	u32 gear_tx;
> -	u32 lane_rx;
> -	u32 lane_tx;
> -	u32 pwr_rx;
> -	u32 pwr_tx;
> -	u32 hs_rate;
> -};
> -
> -struct ufs_pwr_mode_info {
> -	bool is_valid;
> -	struct ufs_pa_layer_attr info;
> -};
> -
> -/**
> - * struct ufs_hba_variant_ops - variant specific callbacks
> - * @name: variant name
> - * @init: called when the driver is initialized
> - * @exit: called to cleanup everything done in init
> - * @clk_scale_notify: notifies that clks are scaled up/down
> - * @setup_clocks: called before touching any of the controller registers
> - * @setup_regulators: called before accessing the host controller
> - * @hce_enable_notify: called before and after HCE enable bit is set to
> allow
> - *                     variant specific Uni-Pro initialization.
> - * @link_startup_notify: called before and after Link startup is carried
> out
> - *                       to allow variant specific Uni-Pro
> initialization.
> - * @pwr_change_notify: called before and after a power mode change
> - *			is carried out to allow vendor spesific capabilities
> - *			to be set.
> - * @suspend: called during host controller PM callback
> - * @resume: called during host controller PM callback
> - */
> -struct ufs_hba_variant_ops {
> -	const char *name;
> -	int	(*init)(struct ufs_hba *);
> -	void    (*exit)(struct ufs_hba *);
> -	void    (*clk_scale_notify)(struct ufs_hba *);
> -	int     (*setup_clocks)(struct ufs_hba *, bool);
> -	int     (*setup_regulators)(struct ufs_hba *, bool);
> -	int     (*hce_enable_notify)(struct ufs_hba *, bool);
> -	int     (*link_startup_notify)(struct ufs_hba *, bool);
> -	int	(*pwr_change_notify)(struct ufs_hba *,
> -					bool, struct ufs_pa_layer_attr *,
> -					struct ufs_pa_layer_attr *);
> -	int     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
> -	int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
> -};
> -
> -/* clock gating state  */
> -enum clk_gating_state {
> -	CLKS_OFF,
> -	CLKS_ON,
> -	REQ_CLKS_OFF,
> -	REQ_CLKS_ON,
> -};
> -
> -/**
> - * struct ufs_clk_gating - UFS clock gating related info
> - * @gate_work: worker to turn off clocks after some delay as specified in
> - * delay_ms
> - * @ungate_work: worker to turn on clocks that will be used in case of
> - * interrupt context
> - * @state: the current clocks state
> - * @delay_ms: gating delay in ms
> - * @is_suspended: clk gating is suspended when set to 1 which can be used
> - * during suspend/resume
> - * @delay_attr: sysfs attribute to control delay_attr
> - * @active_reqs: number of requests that are pending and should be waited
> for
> - * completion before gating clocks.
> - */
> -struct ufs_clk_gating {
> -	struct delayed_work gate_work;
> -	struct work_struct ungate_work;
> -	enum clk_gating_state state;
> -	unsigned long delay_ms;
> -	bool is_suspended;
> -	struct device_attribute delay_attr;
> -	int active_reqs;
> -};
> -
> -struct ufs_clk_scaling {
> -	ktime_t  busy_start_t;
> -	bool is_busy_started;
> -	unsigned long  tot_busy_t;
> -	unsigned long window_start_t;
> -};
> -
> -/**
> - * struct ufs_init_prefetch - contains data that is pre-fetched once
> during
> - * initialization
> - * @icc_level: icc level which was read during initialization
> - */
> -struct ufs_init_prefetch {
> -	u32 icc_level;
> -};
> -
> -/**
> - * struct ufs_hba - per adapter private structure
> - * @mmio_base: UFSHCI base register address
> - * @ucdl_base_addr: UFS Command Descriptor base address
> - * @utrdl_base_addr: UTP Transfer Request Descriptor base address
> - * @utmrdl_base_addr: UTP Task Management Descriptor base address
> - * @ucdl_dma_addr: UFS Command Descriptor DMA address
> - * @utrdl_dma_addr: UTRDL DMA address
> - * @utmrdl_dma_addr: UTMRDL DMA address
> - * @host: Scsi_Host instance of the driver
> - * @dev: device handle
> - * @lrb: local reference block
> - * @lrb_in_use: lrb in use
> - * @outstanding_tasks: Bits representing outstanding task requests
> - * @outstanding_reqs: Bits representing outstanding transfer requests
> - * @capabilities: UFS Controller Capabilities
> - * @nutrs: Transfer Request Queue depth supported by controller
> - * @nutmrs: Task Management Queue depth supported by controller
> - * @ufs_version: UFS Version to which controller complies
> - * @vops: pointer to variant specific operations
> - * @priv: pointer to variant specific private data
> - * @irq: Irq number of the controller
> - * @active_uic_cmd: handle of active UIC command
> - * @uic_cmd_mutex: mutex for uic command
> - * @tm_wq: wait queue for task management
> - * @tm_tag_wq: wait queue for free task management slots
> - * @tm_slots_in_use: bit map of task management request slots in use
> - * @pwr_done: completion for power mode change
> - * @tm_condition: condition variable for task management
> - * @ufshcd_state: UFSHCD states
> - * @eh_flags: Error handling flags
> - * @intr_mask: Interrupt Mask Bits
> - * @ee_ctrl_mask: Exception event control mask
> - * @is_powered: flag to check if HBA is powered
> - * @is_init_prefetch: flag to check if data was pre-fetched in
> initialization
> - * @init_prefetch_data: data pre-fetched during initialization
> - * @eh_work: Worker to handle UFS errors that require s/w attention
> - * @eeh_work: Worker to handle exception events
> - * @errors: HBA errors
> - * @uic_error: UFS interconnect layer error status
> - * @saved_err: sticky error mask
> - * @saved_uic_err: sticky UIC error mask
> - * @dev_cmd: ufs device management command information
> - * @auto_bkops_enabled: to track whether bkops is enabled in device
> - * @vreg_info: UFS device voltage regulator information
> - * @clk_list_head: UFS host controller clocks list node head
> - * @pwr_info: holds current power mode
> - * @max_pwr_info: keeps the device max valid pwm
> - */
> -struct ufs_hba {
> -	void __iomem *mmio_base;
> -
> -	/* Virtual memory reference */
> -	struct utp_transfer_cmd_desc *ucdl_base_addr;
> -	struct utp_transfer_req_desc *utrdl_base_addr;
> -	struct utp_task_req_desc *utmrdl_base_addr;
> -
> -	/* DMA memory reference */
> -	dma_addr_t ucdl_dma_addr;
> -	dma_addr_t utrdl_dma_addr;
> -	dma_addr_t utmrdl_dma_addr;
> -
> -	struct Scsi_Host *host;
> -	struct device *dev;
> -	/*
> -	 * This field is to keep a reference to "scsi_device" corresponding to
> -	 * "UFS device" W-LU.
> -	 */
> -	struct scsi_device *sdev_ufs_device;
> -
> -	enum ufs_dev_pwr_mode curr_dev_pwr_mode;
> -	enum uic_link_state uic_link_state;
> -	/* Desired UFS power management level during runtime PM */
> -	enum ufs_pm_level rpm_lvl;
> -	/* Desired UFS power management level during system PM */
> -	enum ufs_pm_level spm_lvl;
> -	int pm_op_in_progress;
> -
> -	struct ufshcd_lrb *lrb;
> -	unsigned long lrb_in_use;
> -
> -	unsigned long outstanding_tasks;
> -	unsigned long outstanding_reqs;
> -
> -	u32 capabilities;
> -	int nutrs;
> -	int nutmrs;
> -	u32 ufs_version;
> -	struct ufs_hba_variant_ops *vops;
> -	void *priv;
> -	unsigned int irq;
> -	bool is_irq_enabled;
> -
> -
> -	wait_queue_head_t tm_wq;
> -	wait_queue_head_t tm_tag_wq;
> -	unsigned long tm_condition;
> -	unsigned long tm_slots_in_use;
> -
> -	struct uic_command *active_uic_cmd;
> -	struct mutex uic_cmd_mutex;
> -	struct completion *uic_async_done;
> -
> -	u32 ufshcd_state;
> -	u32 eh_flags;
> -	u32 intr_mask;
> -	u16 ee_ctrl_mask;
> -	bool is_powered;
> -	bool is_init_prefetch;
> -	struct ufs_init_prefetch init_prefetch_data;
> -
> -	/* Work Queues */
> -	struct work_struct eh_work;
> -	struct work_struct eeh_work;
> -
> -	/* HBA Errors */
> -	u32 errors;
> -	u32 uic_error;
> -	u32 saved_err;
> -	u32 saved_uic_err;
> -
> -	/* Device management request data */
> -	struct ufs_dev_cmd dev_cmd;
> -
> -	/* Keeps information of the UFS device connected to this host */
> -	struct ufs_dev_info dev_info;
> -	bool auto_bkops_enabled;
> -	struct ufs_vreg_info vreg_info;
> -	struct list_head clk_list_head;
> -
> -	bool wlun_dev_clr_ua;
> -
> -	struct ufs_pa_layer_attr pwr_info;
> -	struct ufs_pwr_mode_info max_pwr_info;
> -
> -	struct ufs_clk_gating clk_gating;
> -	/* Control to enable/disable host capabilities */
> -	u32 caps;
> -	/* Allow dynamic clk gating */
> -#define UFSHCD_CAP_CLK_GATING	(1 << 0)
> -	/* Allow hiberb8 with clk gating */
> -#define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1)
> -	/* Allow dynamic clk scaling */
> -#define UFSHCD_CAP_CLK_SCALING	(1 << 2)
> -	/* Allow auto bkops to enabled during runtime suspend */
> -#define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)
> -
> -	struct devfreq *devfreq;
> -	struct ufs_clk_scaling clk_scaling;
> -	bool is_sys_suspended;
> -};
> -
> -/* Returns true if clocks can be gated. Otherwise false */
> -static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
> -{
> -	return hba->caps & UFSHCD_CAP_CLK_GATING;
> -}
> -static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
> -{
> -	return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
> -}
> -static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba)
> -{
> -	return hba->caps & UFSHCD_CAP_CLK_SCALING;
> -}
> -static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba
> *hba)
> -{
> -	return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
> -}
> -
> -#define ufshcd_writel(hba, val, reg)	\
> -	writel((val), (hba)->mmio_base + (reg))
> -#define ufshcd_readl(hba, reg)	\
> -	readl((hba)->mmio_base + (reg))
> -
> -/**
> - * ufshcd_rmwl - read modify write into a register
> - * @hba - per adapter instance
> - * @mask - mask to apply on read value
> - * @val - actual value to write
> - * @reg - register address
> - */
> -static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val,
> u32 reg)
> -{
> -	u32 tmp;
> -
> -	tmp = ufshcd_readl(hba, reg);
> -	tmp &= ~mask;
> -	tmp |= (val & mask);
> -	ufshcd_writel(hba, tmp, reg);
> -}
> -
> -int ufshcd_alloc_host(struct device *, struct ufs_hba **);
> -int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int);
> -void ufshcd_remove(struct ufs_hba *);
> -
> -/**
> - * ufshcd_hba_stop - Send controller to reset state
> - * @hba: per adapter instance
> - */
> -static inline void ufshcd_hba_stop(struct ufs_hba *hba)
> -{
> -	ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
> -}
> -
> -static inline void check_upiu_size(void)
> -{
> -	BUILD_BUG_ON(ALIGNED_UPIU_SIZE <
> -		GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE);
> -}
> -
> -extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
> -extern int ufshcd_runtime_resume(struct ufs_hba *hba);
> -extern int ufshcd_runtime_idle(struct ufs_hba *hba);
> -extern int ufshcd_system_suspend(struct ufs_hba *hba);
> -extern int ufshcd_system_resume(struct ufs_hba *hba);
> -extern int ufshcd_shutdown(struct ufs_hba *hba);
> -extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
> -			       u8 attr_set, u32 mib_val, u8 peer);
> -extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
> -			       u32 *mib_val, u8 peer);
> -
> -/* UIC command interfaces for DME primitives */
> -#define DME_LOCAL	0
> -#define DME_PEER	1
> -#define ATTR_SET_NOR	0	/* NORMAL */
> -#define ATTR_SET_ST	1	/* STATIC */
> -
> -static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
> -				 u32 mib_val)
> -{
> -	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
> -				   mib_val, DME_LOCAL);
> -}
> -
> -static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
> -				    u32 mib_val)
> -{
> -	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
> -				   mib_val, DME_LOCAL);
> -}
> -
> -static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
> -				      u32 mib_val)
> -{
> -	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
> -				   mib_val, DME_PEER);
> -}
> -
> -static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32
> attr_sel,
> -					 u32 mib_val)
> -{
> -	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
> -				   mib_val, DME_PEER);
> -}
> -
> -static inline int ufshcd_dme_get(struct ufs_hba *hba,
> -				 u32 attr_sel, u32 *mib_val)
> -{
> -	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL);
> -}
> -
> -static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
> -				      u32 attr_sel, u32 *mib_val)
> -{
> -	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
> -}
> -
> -int ufshcd_hold(struct ufs_hba *hba, bool async);
> -void ufshcd_release(struct ufs_hba *hba);
> -#endif /* End of Header */
> diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
> deleted file mode 100644
> index 3fc3e21..0000000
> --- a/drivers/scsi/ufs/unipro.h
> +++ /dev/null
> @@ -1,207 +0,0 @@
> -/*
> - * drivers/scsi/ufs/unipro.h
> - *
> - * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - */
> -
> -#ifndef _UNIPRO_H_
> -#define _UNIPRO_H_
> -
> -/*
> - * M-TX Configuration Attributes
> - */
> -#define TX_MODE					0x0021
> -#define TX_HSRATE_SERIES			0x0022
> -#define TX_HSGEAR				0x0023
> -#define TX_PWMGEAR				0x0024
> -#define TX_AMPLITUDE				0x0025
> -#define TX_HS_SLEWRATE				0x0026
> -#define TX_SYNC_SOURCE				0x0027
> -#define TX_HS_SYNC_LENGTH			0x0028
> -#define TX_HS_PREPARE_LENGTH			0x0029
> -#define TX_LS_PREPARE_LENGTH			0x002A
> -#define TX_HIBERN8_CONTROL			0x002B
> -#define TX_LCC_ENABLE				0x002C
> -#define TX_PWM_BURST_CLOSURE_EXTENSION		0x002D
> -#define TX_BYPASS_8B10B_ENABLE			0x002E
> -#define TX_DRIVER_POLARITY			0x002F
> -#define TX_HS_UNTERMINATED_LINE_DRIVE_ENABLE	0x0030
> -#define TX_LS_TERMINATED_LINE_DRIVE_ENABLE	0x0031
> -#define TX_LCC_SEQUENCER			0x0032
> -#define TX_MIN_ACTIVATETIME			0x0033
> -#define TX_PWM_G6_G7_SYNC_LENGTH		0x0034
> -
> -/*
> - * M-RX Configuration Attributes
> - */
> -#define RX_MODE					0x00A1
> -#define RX_HSRATE_SERIES			0x00A2
> -#define RX_HSGEAR				0x00A3
> -#define RX_PWMGEAR				0x00A4
> -#define RX_LS_TERMINATED_ENABLE			0x00A5
> -#define RX_HS_UNTERMINATED_ENABLE		0x00A6
> -#define RX_ENTER_HIBERN8			0x00A7
> -#define RX_BYPASS_8B10B_ENABLE			0x00A8
> -#define RX_TERMINATION_FORCE_ENABLE		0x0089
> -
> -#define is_mphy_tx_attr(attr)			(attr < RX_MODE)
> -/*
> - * PHY Adpater attributes
> - */
> -#define PA_ACTIVETXDATALANES	0x1560
> -#define PA_ACTIVERXDATALANES	0x1580
> -#define PA_TXTRAILINGCLOCKS	0x1564
> -#define PA_PHY_TYPE		0x1500
> -#define PA_AVAILTXDATALANES	0x1520
> -#define PA_AVAILRXDATALANES	0x1540
> -#define PA_MINRXTRAILINGCLOCKS	0x1543
> -#define PA_TXPWRSTATUS		0x1567
> -#define PA_RXPWRSTATUS		0x1582
> -#define PA_TXFORCECLOCK		0x1562
> -#define PA_TXPWRMODE		0x1563
> -#define PA_LEGACYDPHYESCDL	0x1570
> -#define PA_MAXTXSPEEDFAST	0x1521
> -#define PA_MAXTXSPEEDSLOW	0x1522
> -#define PA_MAXRXSPEEDFAST	0x1541
> -#define PA_MAXRXSPEEDSLOW	0x1542
> -#define PA_TXLINKSTARTUPHS	0x1544
> -#define PA_TXSPEEDFAST		0x1565
> -#define PA_TXSPEEDSLOW		0x1566
> -#define PA_REMOTEVERINFO	0x15A0
> -#define PA_TXGEAR		0x1568
> -#define PA_TXTERMINATION	0x1569
> -#define PA_HSSERIES		0x156A
> -#define PA_PWRMODE		0x1571
> -#define PA_RXGEAR		0x1583
> -#define PA_RXTERMINATION	0x1584
> -#define PA_MAXRXPWMGEAR		0x1586
> -#define PA_MAXRXHSGEAR		0x1587
> -#define PA_RXHSUNTERMCAP	0x15A5
> -#define PA_RXLSTERMCAP		0x15A6
> -#define PA_PACPREQTIMEOUT	0x1590
> -#define PA_PACPREQEOBTIMEOUT	0x1591
> -#define PA_HIBERN8TIME		0x15A7
> -#define PA_LOCALVERINFO		0x15A9
> -#define PA_TACTIVATE		0x15A8
> -#define PA_PACPFRAMECOUNT	0x15C0
> -#define PA_PACPERRORCOUNT	0x15C1
> -#define PA_PHYTESTCONTROL	0x15C2
> -#define PA_PWRMODEUSERDATA0	0x15B0
> -#define PA_PWRMODEUSERDATA1	0x15B1
> -#define PA_PWRMODEUSERDATA2	0x15B2
> -#define PA_PWRMODEUSERDATA3	0x15B3
> -#define PA_PWRMODEUSERDATA4	0x15B4
> -#define PA_PWRMODEUSERDATA5	0x15B5
> -#define PA_PWRMODEUSERDATA6	0x15B6
> -#define PA_PWRMODEUSERDATA7	0x15B7
> -#define PA_PWRMODEUSERDATA8	0x15B8
> -#define PA_PWRMODEUSERDATA9	0x15B9
> -#define PA_PWRMODEUSERDATA10	0x15BA
> -#define PA_PWRMODEUSERDATA11	0x15BB
> -#define PA_CONNECTEDTXDATALANES	0x1561
> -#define PA_CONNECTEDRXDATALANES	0x1581
> -#define PA_LOGICALLANEMAP	0x15A1
> -#define PA_SLEEPNOCONFIGTIME	0x15A2
> -#define PA_STALLNOCONFIGTIME	0x15A3
> -#define PA_SAVECONFIGTIME	0x15A4
> -
> -/* PA power modes */
> -enum {
> -	FAST_MODE	= 1,
> -	SLOW_MODE	= 2,
> -	FASTAUTO_MODE	= 4,
> -	SLOWAUTO_MODE	= 5,
> -	UNCHANGED	= 7,
> -};
> -
> -/* PA TX/RX Frequency Series */
> -enum {
> -	PA_HS_MODE_A	= 1,
> -	PA_HS_MODE_B	= 2,
> -};
> -
> -enum ufs_pwm_gear_tag {
> -	UFS_PWM_DONT_CHANGE,	/* Don't change Gear */
> -	UFS_PWM_G1,		/* PWM Gear 1 (default for reset) */
> -	UFS_PWM_G2,		/* PWM Gear 2 */
> -	UFS_PWM_G3,		/* PWM Gear 3 */
> -	UFS_PWM_G4,		/* PWM Gear 4 */
> -	UFS_PWM_G5,		/* PWM Gear 5 */
> -	UFS_PWM_G6,		/* PWM Gear 6 */
> -	UFS_PWM_G7,		/* PWM Gear 7 */
> -};
> -
> -enum ufs_hs_gear_tag {
> -	UFS_HS_DONT_CHANGE,	/* Don't change Gear */
> -	UFS_HS_G1,		/* HS Gear 1 (default for reset) */
> -	UFS_HS_G2,		/* HS Gear 2 */
> -	UFS_HS_G3,		/* HS Gear 3 */
> -};
> -
> -/*
> - * Data Link Layer Attributes
> - */
> -#define DL_TC0TXFCTHRESHOLD	0x2040
> -#define DL_FC0PROTTIMEOUTVAL	0x2041
> -#define DL_TC0REPLAYTIMEOUTVAL	0x2042
> -#define DL_AFC0REQTIMEOUTVAL	0x2043
> -#define DL_AFC0CREDITTHRESHOLD	0x2044
> -#define DL_TC0OUTACKTHRESHOLD	0x2045
> -#define DL_TC1TXFCTHRESHOLD	0x2060
> -#define DL_FC1PROTTIMEOUTVAL	0x2061
> -#define DL_TC1REPLAYTIMEOUTVAL	0x2062
> -#define DL_AFC1REQTIMEOUTVAL	0x2063
> -#define DL_AFC1CREDITTHRESHOLD	0x2064
> -#define DL_TC1OUTACKTHRESHOLD	0x2065
> -#define DL_TXPREEMPTIONCAP	0x2000
> -#define DL_TC0TXMAXSDUSIZE	0x2001
> -#define DL_TC0RXINITCREDITVAL	0x2002
> -#define DL_TC0TXBUFFERSIZE	0x2005
> -#define DL_PEERTC0PRESENT	0x2046
> -#define DL_PEERTC0RXINITCREVAL	0x2047
> -#define DL_TC1TXMAXSDUSIZE	0x2003
> -#define DL_TC1RXINITCREDITVAL	0x2004
> -#define DL_TC1TXBUFFERSIZE	0x2006
> -#define DL_PEERTC1PRESENT	0x2066
> -#define DL_PEERTC1RXINITCREVAL	0x2067
> -
> -/*
> - * Network Layer Attributes
> - */
> -#define N_DEVICEID		0x3000
> -#define N_DEVICEID_VALID	0x3001
> -#define N_TC0TXMAXSDUSIZE	0x3020
> -#define N_TC1TXMAXSDUSIZE	0x3021
> -
> -/*
> - * Transport Layer Attributes
> - */
> -#define T_NUMCPORTS		0x4000
> -#define T_NUMTESTFEATURES	0x4001
> -#define T_CONNECTIONSTATE	0x4020
> -#define T_PEERDEVICEID		0x4021
> -#define T_PEERCPORTID		0x4022
> -#define T_TRAFFICCLASS		0x4023
> -#define T_PROTOCOLID		0x4024
> -#define T_CPORTFLAGS		0x4025
> -#define T_TXTOKENVALUE		0x4026
> -#define T_RXTOKENVALUE		0x4027
> -#define T_LOCALBUFFERSPACE	0x4028
> -#define T_PEERBUFFERSPACE	0x4029
> -#define T_CREDITSTOSEND		0x402A
> -#define T_CPORTMODE		0x402B
> -#define T_TC0TXMAXSDUSIZE	0x4060
> -#define T_TC1TXMAXSDUSIZE	0x4061
> -
> -/* Boolean attribute values */
> -enum {
> -	FALSE = 0,
> -	TRUE,
> -};
> -
> -#endif /* _UNIPRO_H_ */
> diff --git a/include/linux/phy/phy-qcom-ufs.h
> b/include/linux/phy/phy-qcom-ufs.h
> new file mode 100644
> index 0000000..27f93a7
> --- /dev/null
> +++ b/include/linux/phy/phy-qcom-ufs.h
> @@ -0,0 +1,205 @@
> +/*
> + * Copyright (c) 2013-2014, 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_H_
> +#define UFS_QCOM_PHY_H_
> +
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/phy/phy.h>
> +
> +#include <linux/scsi/ufs/ufshcd.h>
> +#include <linux/scsi/ufs/unipro.h>
> +#include <linux/scsi/ufs/ufs-qcom.h>
> +
> +#define UFS_QCOM_PHY_CAL_ENTRY(reg, val)	\
> +	{				\
> +		.reg_offset = reg,	\
> +		.cfg_value = val,	\
> +	}
> +
> +#define UFS_QCOM_PHY_NAME_LEN	30
> +
> +enum {
> +	MASK_SERDES_START       = 0x1,
> +	MASK_PCS_READY          = 0x1,
> +};
> +
> +enum {
> +	OFFSET_SERDES_START     = 0x0,
> +};
> +
> +struct ufs_qcom_phy_stored_attributes {
> +	u32 att;
> +	u32 value;
> +};
> +
> +struct ufs_qcom_phy_calibration {
> +	u32 reg_offset;
> +	u32 cfg_value;
> +};
> +
> +struct ufs_qcom_phy {
> +	struct list_head list;
> +	struct device *dev;
> +	void __iomem *mmio;
> +	void __iomem *dev_ref_clk_ctrl_mmio;
> +	struct clk *tx_iface_clk;
> +	struct clk *rx_iface_clk;
> +	bool is_iface_clk_enabled;
> +	struct clk *ref_clk_src;
> +	struct clk *ref_clk_parent;
> +	struct clk *ref_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;
> +	unsigned int quirks;
> +	u8 host_ctrl_rev_major;
> +	u16 host_ctrl_rev_minor;
> +	u16 host_ctrl_rev_step;
> +
> +	/*
> +	 * As part of UFS power management, UFS link would be put in hibernate
> +	 * and UFS device would be put in SLEEP mode as part of runtime/system
> +	 * suspend callback. But when system goes into suspend with VDD
> +	 * minimization, UFS PHY states are being reset which means UFS link
> +	 * hibernate exit command on system resume would fail.
> +	 * If this quirk is enabled then above issue is workaround by saving
> +	 * the UFS PHY state information before system goes into suspend and
> +	 * restoring the saved state information during system resume but
> +	 * before executing the hibern8 exit command.
> +	 * Note that this quirk will help restoring the PHY state if even when
> +	 * link in not kept in hibern8 during suspend.
> +	 *
> +	 * Here is the list of steps to save/restore the configuration:
> +	 * Before entering into system suspend:
> +	 *	1. Read Critical PCS SWI Registers  + less critical PHY CSR
> +	 *	2. Read RMMI Attributes
> +	 * Enter into system suspend
> +	 * After exiting from system suspend:
> +	 *	1. Set UFS_PHY_SOFT_RESET bit in UFS_CFG1 register of the UFS
> +	 *	   Controller
> +	 *	2. Write 0x01 to the UFS_PHY_POWER_DOWN_CONTROL register in the
> +	 *	   UFS PHY
> +	 *	3. Write back the values of the PHY SWI registers
> +	 *	4. Clear UFS_PHY_SOFT_RESET bit in UFS_CFG1 register of the UFS
> +	 *	   Controller
> +	 *	5. Write 0x01 to the UFS_PHY_PHY_START in the UFS PHY. This will
> +	 *	   start the PLL calibration and bring-up of the PHY.
> +	 *	6. Write back the values to the PHY RMMI Attributes
> +	 *	7. Wait for UFS_PHY_PCS_READY_STATUS[0] to be '1'
> +	 */
> +	#define UFS_QCOM_PHY_QUIRK_CFG_RESTORE		(1 << 0)
> +
> +	/*
> +	* If UFS PHY power down is deasserted and power is restored to analog
> +	* circuits, the rx_sigdet can glitch. If the glitch is wide enough,
> +	* it can trigger the digital logic to think it saw a DIF-N and cause
> +	* it to exit Hibern8. Disabling the rx_sigdet during power-up masks
> +	* the glitch.
> +	*/
> +	#define UFS_QCOM_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE	(1 << 1)
> +
> +	/*
> +	* If UFS link is put into Hibern8 and if UFS PHY analog hardware is
> +	* power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8
> +	* exit might fail even after powering on UFS PHY analog hardware.
> +	* Enabling this quirk will help to solve above issue by doing
> +	* custom PHY settings just before PHY analog power collapse.
> +	*/
> +	#define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE	(1 << 2)
> +
> +	char name[UFS_QCOM_PHY_NAME_LEN];
> +	struct ufs_qcom_phy_calibration *cached_regs;
> +	int cached_regs_table_size;
> +	bool is_powered_on;
> +	struct ufs_qcom_phy_specific_ops *phy_spec_ops;
> +};
> +
> +/**
> + * struct ufs_qcom_phy_specific_ops - set of pointers to functions which
> have a
> + * specific implementation per phy. Each UFS phy, should implement
> + * those functions according to its spec and requirements
> + * @calibrate_phy: pointer to a function that calibrate the phy
> + * @start_serdes: pointer to a function that starts the serdes
> + * @save_configuration: pointer to a function that saves phy
> + * configuration
> + * @is_physical_coding_sublayer_ready: pointer to a function that
> + * checks pcs readiness
> + * @set_tx_lane_enable: pointer to a function that enable tx lanes
> + * @power_control: pointer to a function that controls analog rail of phy
> + * and writes to QSERDES_RX_SIGDET_CNTRL attribute
> + */
> +struct ufs_qcom_phy_specific_ops {
> +	int (*calibrate_phy) (struct ufs_qcom_phy *phy);
> +	void (*start_serdes) (struct ufs_qcom_phy *phy);
> +	void (*save_configuration)(struct ufs_qcom_phy *phy);
> +	void (*restore_configuration)(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 (*power_control) (struct ufs_qcom_phy *phy, bool val);
> +};
> +
> +int ufs_qcom_phy_cfg_vreg(struct phy *phy,
> +			struct ufs_qcom_phy_vreg *vreg, bool on);
> +int ufs_qcom_phy_enable_vreg(struct phy *phy,
> +			struct ufs_qcom_phy_vreg *vreg);
> +int ufs_qcom_phy_disable_vreg(struct phy *phy,
> +			struct ufs_qcom_phy_vreg *vreg);
> +int ufs_qcom_phy_enable_ref_clk(struct phy *phy);
> +void ufs_qcom_phy_disable_ref_clk(struct phy *phy);
> +void ufs_qcom_phy_enable_dev_ref_clk(struct phy *);
> +void ufs_qcom_phy_disable_dev_ref_clk(struct phy *);
> +int ufs_qcom_phy_enable_iface_clk(struct phy *phy);
> +void ufs_qcom_phy_disable_iface_clk(struct phy *phy);
> +void ufs_qcom_phy_restore_swi_regs(struct phy *phy);
> +int ufs_qcom_phy_link_startup_post_change(struct phy *phy,
> +			struct ufs_hba *hba);
> +int ufs_qcom_phy_base_init(struct platform_device *pdev,
> +			struct ufs_qcom_phy *ufs_qcom_phy_ops);
> +int ufs_qcom_phy_is_cfg_restore_quirk_enabled(struct phy *phy);
> +struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
> +int ufs_qcom_phy_start_serdes(struct phy *generic_phy);
> +int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32
> tx_lanes);
> +int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy);
> +int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy);
> +int ufs_qcom_phy_save_configuration(struct phy *generic_phy);
> +int ufs_qcom_phy_restore_configuration(struct phy *generic_phy);
> +void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
> +			u8 major, u16 minor, u16 step);
> +int ufs_qcom_phy_power_on(struct phy *generic_phy);
> +int ufs_qcom_phy_power_off(struct phy *generic_phy);
> +int ufs_qcom_phy_exit(struct phy *generic_phy);
> +int ufs_qcom_phy_init_clks(struct phy *generic_phy,
> +			struct ufs_qcom_phy *phy_common);
> +int ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
> +			struct ufs_qcom_phy *phy_common);
> +int ufs_qcom_phy_remove(struct phy *generic_phy,
> +		       struct ufs_qcom_phy *ufs_qcom_phy);
> +struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
> +			struct ufs_qcom_phy *common_cfg,
> +			struct phy_ops *ufs_qcom_phy_gen_ops,
> +			struct ufs_qcom_phy_specific_ops *phy_spec_ops);
> +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,
> +			int rate);
> +#endif
> diff --git a/include/linux/scsi/ufs/ufs-qcom.h
> b/include/linux/scsi/ufs/ufs-qcom.h
> new file mode 100644
> index 0000000..e38e997
> --- /dev/null
> +++ b/include/linux/scsi/ufs/ufs-qcom.h
> @@ -0,0 +1,210 @@
> +/* Copyright (c) 2013-2014, 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_H_
> +#define UFS_QCOM_H_
> +
> +#include <linux/phy/phy.h>
> +
> +#define MAX_UFS_QCOM_HOSTS	1
> +#define MAX_U32                 (~(u32)0)
> +#define MPHY_TX_FSM_STATE       0x41
> +#define TX_FSM_HIBERN8          0x1
> +#define HBRN8_POLL_TOUT_MS      100
> +#define DEFAULT_CLK_RATE_HZ     1000000
> +#define BUS_VECTOR_NAME_LEN     32
> +
> +#define UFS_HW_VER_MAJOR_SHFT	(28)
> +#define UFS_HW_VER_MAJOR_MASK	(0x000F << UFS_HW_VER_MAJOR_SHFT)
> +#define UFS_HW_VER_MINOR_SHFT	(16)
> +#define UFS_HW_VER_MINOR_MASK	(0x0FFF << UFS_HW_VER_MINOR_SHFT)
> +#define UFS_HW_VER_STEP_SHFT	(0)
> +#define UFS_HW_VER_STEP_MASK	(0xFFFF << UFS_HW_VER_STEP_SHFT)
> +
> +/* vendor specific pre-defined parameters */
> +#define SLOW 1
> +#define FAST 2
> +
> +#define UFS_QCOM_LIMIT_NUM_LANES_RX	2
> +#define UFS_QCOM_LIMIT_NUM_LANES_TX	2
> +#define UFS_QCOM_LIMIT_HSGEAR_RX	UFS_HS_G2
> +#define UFS_QCOM_LIMIT_HSGEAR_TX	UFS_HS_G2
> +#define UFS_QCOM_LIMIT_PWMGEAR_RX	UFS_PWM_G4
> +#define UFS_QCOM_LIMIT_PWMGEAR_TX	UFS_PWM_G4
> +#define UFS_QCOM_LIMIT_RX_PWR_PWM	SLOW_MODE
> +#define UFS_QCOM_LIMIT_TX_PWR_PWM	SLOW_MODE
> +#define UFS_QCOM_LIMIT_RX_PWR_HS	FAST_MODE
> +#define UFS_QCOM_LIMIT_TX_PWR_HS	FAST_MODE
> +#define UFS_QCOM_LIMIT_HS_RATE		PA_HS_MODE_B
> +#define UFS_QCOM_LIMIT_DESIRED_MODE	FAST
> +
> +/* QCOM UFS host controller vendor specific registers */
> +enum {
> +	REG_UFS_SYS1CLK_1US                 = 0xC0,
> +	REG_UFS_TX_SYMBOL_CLK_NS_US         = 0xC4,
> +	REG_UFS_LOCAL_PORT_ID_REG           = 0xC8,
> +	REG_UFS_PA_ERR_CODE                 = 0xCC,
> +	REG_UFS_RETRY_TIMER_REG             = 0xD0,
> +	REG_UFS_PA_LINK_STARTUP_TIMER       = 0xD8,
> +	REG_UFS_CFG1                        = 0xDC,
> +	REG_UFS_CFG2                        = 0xE0,
> +	REG_UFS_HW_VERSION                  = 0xE4,
> +
> +	UFS_DBG_RD_REG_UAWM			= 0x100,
> +	UFS_DBG_RD_REG_UARM			= 0x200,
> +	UFS_DBG_RD_REG_TXUC			= 0x300,
> +	UFS_DBG_RD_REG_RXUC			= 0x400,
> +	UFS_DBG_RD_REG_DFC			= 0x500,
> +	UFS_DBG_RD_REG_TRLUT			= 0x600,
> +	UFS_DBG_RD_REG_TMRLUT			= 0x700,
> +	UFS_UFS_DBG_RD_REG_OCSC			= 0x800,
> +
> +	UFS_UFS_DBG_RD_DESC_RAM			= 0x1500,
> +	UFS_UFS_DBG_RD_PRDT_RAM			= 0x1700,
> +	UFS_UFS_DBG_RD_RESP_RAM			= 0x1800,
> +	UFS_UFS_DBG_RD_EDTL_RAM			= 0x1900,
> +};
> +
> +/* bit definitions for REG_UFS_CFG2 register */
> +#define UAWM_HW_CGC_EN		(1 << 0)
> +#define UARM_HW_CGC_EN		(1 << 1)
> +#define TXUC_HW_CGC_EN		(1 << 2)
> +#define RXUC_HW_CGC_EN		(1 << 3)
> +#define DFC_HW_CGC_EN		(1 << 4)
> +#define TRLUT_HW_CGC_EN		(1 << 5)
> +#define TMRLUT_HW_CGC_EN	(1 << 6)
> +#define OCSC_HW_CGC_EN		(1 << 7)
> +
> +#define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\
> +				 TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\
> +				 DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\
> +				 TMRLUT_HW_CGC_EN | OCSC_HW_CGC_EN)
> +
> +/* bit offset */
> +enum {
> +	OFFSET_UFS_PHY_SOFT_RESET           = 1,
> +	OFFSET_CLK_NS_REG                   = 10,
> +};
> +
> +/* bit masks */
> +enum {
> +	MASK_UFS_PHY_SOFT_RESET             = 0x2,
> +	MASK_TX_SYMBOL_CLK_1US_REG          = 0x3FF,
> +	MASK_CLK_NS_REG                     = 0xFFFC00,
> +};
> +
> +enum ufs_qcom_phy_init_type {
> +	UFS_PHY_INIT_FULL,
> +	UFS_PHY_INIT_CFG_RESTORE,
> +};
> +
> +struct ufs_qcom_phy_vreg {
> +	const char *name;
> +	struct regulator *reg;
> +	int max_uA;
> +	int min_uV;
> +	int max_uV;
> +	bool enabled;
> +	bool is_always_on;
> +};
> +
> +static inline void
> +ufs_qcom_get_controller_revision(struct ufs_hba *hba,
> +				 u8 *major, u16 *minor, u16 *step)
> +{
> +	u32 ver = ufshcd_readl(hba, REG_UFS_HW_VERSION);
> +
> +	*major = (ver & UFS_HW_VER_MAJOR_MASK) >> UFS_HW_VER_MAJOR_SHFT;
> +	*minor = (ver & UFS_HW_VER_MINOR_MASK) >> UFS_HW_VER_MINOR_SHFT;
> +	*step = (ver & UFS_HW_VER_STEP_MASK) >> UFS_HW_VER_STEP_SHFT;
> +};
> +
> +static inline void ufs_qcom_assert_reset(struct ufs_hba *hba)
> +{
> +	ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
> +			1 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
> +	mb();
> +}
> +
> +static inline void ufs_qcom_deassert_reset(struct ufs_hba *hba)
> +{
> +	ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
> +			0 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
> +	mb();
> +}
> +
> +struct ufs_qcom_bus_vote {
> +	uint32_t client_handle;
> +	uint32_t curr_vote;
> +	int min_bw_vote;
> +	int max_bw_vote;
> +	int saved_vote;
> +	bool is_max_bw_needed;
> +	struct device_attribute max_bus_bw;
> +};
> +
> +/**
> + * 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 completion async_done;
> +	struct platform_device *pdev;
> +	int state;
> +
> +	/*
> +	 * If UFS host controller should handle cryptographic engine's
> +	 * errors, enables this quirk.
> +	 */
> +	#define UFS_QCOM_ICE_QUIRK_HANDLE_CRYPTO_ENGINE_ERRORS	UFS_BIT(0)
> +
> +	u16 quirks;
> +
> +	bool crypto_engine_err;
> +};
> +
> +struct ufs_qcom_host {
> +	struct phy *generic_phy;
> +	struct ufs_hba *hba;
> +	struct ufs_qcom_bus_vote bus_vote;
> +	struct ufs_pa_layer_attr dev_req_params;
> +	struct clk *rx_l0_sync_clk;
> +	struct clk *tx_l0_sync_clk;
> +	struct clk *rx_l1_sync_clk;
> +	struct clk *tx_l1_sync_clk;
> +	bool is_lane_clks_enabled;
> +	bool sec_cfg_updated;
> +	struct ufs_qcom_ice_data ice;
> +	u32 lanes_per_direction;
> +};
> +
> +#define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba)
> +#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
> +#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
> +
> +#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_PLL_MAX_UV            1800000
> +#define VDDP_REF_CLK_MIN_UV        1200000
> +#define VDDP_REF_CLK_MAX_UV        1200000
> +
> +#endif /* UFS_QCOM_H_ */
> diff --git a/include/linux/scsi/ufs/ufs.h b/include/linux/scsi/ufs/ufs.h
> new file mode 100644
> index 0000000..42c459a
> --- /dev/null
> +++ b/include/linux/scsi/ufs/ufs.h
> @@ -0,0 +1,491 @@
> +/*
> + * Universal Flash Storage Host controller driver
> + *
> + * This code is based on drivers/scsi/ufs/ufs.h
> + * Copyright (C) 2011-2013 Samsung India Software Operations
> + *
> + * Authors:
> + *	Santosh Yaraganavi <santosh.sy@xxxxxxxxxxx>
> + *	Vinayak Holikatti <h.vinayak@xxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * See the COPYING file in the top-level directory or visit
> + * <http://www.gnu.org/licenses/gpl-2.0.html>
> + *
> + * 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.
> + *
> + * This program is provided "AS IS" and "WITH ALL FAULTS" and
> + * without warranty of any kind. You are solely responsible for
> + * determining the appropriateness of using and distributing
> + * the program and assume all risks associated with your exercise
> + * of rights with respect to the program, including but not limited
> + * to infringement of third party rights, the risks and costs of
> + * program errors, damage to or loss of data, programs or equipment,
> + * and unavailability or interruption of operations. Under no
> + * circumstances will the contributor of this Program be liable for
> + * any damages of any kind arising from your use or distribution of
> + * this program.
> + */
> +
> +#ifndef _UFS_H
> +#define _UFS_H
> +
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +
> +#define MAX_CDB_SIZE	16
> +#define GENERAL_UPIU_REQUEST_SIZE 32
> +#define QUERY_DESC_MAX_SIZE       255
> +#define QUERY_DESC_MIN_SIZE       2
> +#define QUERY_OSF_SIZE            (GENERAL_UPIU_REQUEST_SIZE - \
> +					(sizeof(struct utp_upiu_header)))
> +
> +#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
> +			cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
> +			 (byte1 << 8) | (byte0))
> +/*
> + * UFS device may have standard LUs and LUN id could be from 0x00 to
> + * 0x7F. Standard LUs use "Peripheral Device Addressing Format".
> + * UFS device may also have the Well Known LUs (also referred as W-LU)
> + * which again could be from 0x00 to 0x7F. For W-LUs, device only use
> + * the "Extended Addressing Format" which means the W-LUNs would be
> + * from 0xc100 (SCSI_W_LUN_BASE) onwards.
> + * This means max. LUN number reported from UFS device could be 0xC17F.
> + */
> +#define UFS_UPIU_MAX_UNIT_NUM_ID	0x7F
> +#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
> +
> +/* Well known logical unit id in LUN field of UPIU */
> +enum {
> +	UFS_UPIU_REPORT_LUNS_WLUN	= 0x81,
> +	UFS_UPIU_UFS_DEVICE_WLUN	= 0xD0,
> +	UFS_UPIU_BOOT_WLUN		= 0xB0,
> +	UFS_UPIU_RPMB_WLUN		= 0xC4,
> +};
> +
> +/*
> + * UFS Protocol Information Unit related definitions
> + */
> +
> +/* Task management functions */
> +enum {
> +	UFS_ABORT_TASK		= 0x01,
> +	UFS_ABORT_TASK_SET	= 0x02,
> +	UFS_CLEAR_TASK_SET	= 0x04,
> +	UFS_LOGICAL_RESET	= 0x08,
> +	UFS_QUERY_TASK		= 0x80,
> +	UFS_QUERY_TASK_SET	= 0x81,
> +};
> +
> +/* UTP UPIU Transaction Codes Initiator to Target */
> +enum {
> +	UPIU_TRANSACTION_NOP_OUT	= 0x00,
> +	UPIU_TRANSACTION_COMMAND	= 0x01,
> +	UPIU_TRANSACTION_DATA_OUT	= 0x02,
> +	UPIU_TRANSACTION_TASK_REQ	= 0x04,
> +	UPIU_TRANSACTION_QUERY_REQ	= 0x16,
> +};
> +
> +/* UTP UPIU Transaction Codes Target to Initiator */
> +enum {
> +	UPIU_TRANSACTION_NOP_IN		= 0x20,
> +	UPIU_TRANSACTION_RESPONSE	= 0x21,
> +	UPIU_TRANSACTION_DATA_IN	= 0x22,
> +	UPIU_TRANSACTION_TASK_RSP	= 0x24,
> +	UPIU_TRANSACTION_READY_XFER	= 0x31,
> +	UPIU_TRANSACTION_QUERY_RSP	= 0x36,
> +	UPIU_TRANSACTION_REJECT_UPIU	= 0x3F,
> +};
> +
> +/* UPIU Read/Write flags */
> +enum {
> +	UPIU_CMD_FLAGS_NONE	= 0x00,
> +	UPIU_CMD_FLAGS_WRITE	= 0x20,
> +	UPIU_CMD_FLAGS_READ	= 0x40,
> +};
> +
> +/* UPIU Task Attributes */
> +enum {
> +	UPIU_TASK_ATTR_SIMPLE	= 0x00,
> +	UPIU_TASK_ATTR_ORDERED	= 0x01,
> +	UPIU_TASK_ATTR_HEADQ	= 0x02,
> +	UPIU_TASK_ATTR_ACA	= 0x03,
> +};
> +
> +/* UPIU Query request function */
> +enum {
> +	UPIU_QUERY_FUNC_STANDARD_READ_REQUEST           = 0x01,
> +	UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST          = 0x81,
> +};
> +
> +/* Flag idn for Query Requests*/
> +enum flag_idn {
> +	QUERY_FLAG_IDN_FDEVICEINIT      = 0x01,
> +	QUERY_FLAG_IDN_PWR_ON_WPE	= 0x03,
> +	QUERY_FLAG_IDN_BKOPS_EN         = 0x04,
> +};
> +
> +/* Attribute idn for Query requests */
> +enum attr_idn {
> +	QUERY_ATTR_IDN_ACTIVE_ICC_LVL	= 0x03,
> +	QUERY_ATTR_IDN_BKOPS_STATUS	= 0x05,
> +	QUERY_ATTR_IDN_EE_CONTROL	= 0x0D,
> +	QUERY_ATTR_IDN_EE_STATUS	= 0x0E,
> +};
> +
> +/* Descriptor idn for Query requests */
> +enum desc_idn {
> +	QUERY_DESC_IDN_DEVICE		= 0x0,
> +	QUERY_DESC_IDN_CONFIGURAION	= 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_MAX,
> +};
> +
> +enum desc_header_offset {
> +	QUERY_DESC_LENGTH_OFFSET	= 0x00,
> +	QUERY_DESC_DESC_TYPE_OFFSET	= 0x01,
> +};
> +
> +enum ufs_desc_max_size {
> +	QUERY_DESC_DEVICE_MAX_SIZE		= 0x1F,
> +	QUERY_DESC_CONFIGURAION_MAX_SIZE	= 0x90,
> +	QUERY_DESC_UNIT_MAX_SIZE		= 0x23,
> +	QUERY_DESC_INTERCONNECT_MAX_SIZE	= 0x06,
> +	/*
> +	 * Max. 126 UNICODE characters (2 bytes per character) plus 2 bytes
> +	 * of descriptor header.
> +	 */
> +	QUERY_DESC_STRING_MAX_SIZE		= 0xFE,
> +	QUERY_DESC_GEOMETRY_MAZ_SIZE		= 0x44,
> +	QUERY_DESC_POWER_MAX_SIZE		= 0x62,
> +	QUERY_DESC_RFU_MAX_SIZE			= 0x00,
> +};
> +
> +/* Unit descriptor parameters offsets in bytes*/
> +enum unit_desc_param {
> +	UNIT_DESC_PARAM_LEN			= 0x0,
> +	UNIT_DESC_PARAM_TYPE			= 0x1,
> +	UNIT_DESC_PARAM_UNIT_INDEX		= 0x2,
> +	UNIT_DESC_PARAM_LU_ENABLE		= 0x3,
> +	UNIT_DESC_PARAM_BOOT_LUN_ID		= 0x4,
> +	UNIT_DESC_PARAM_LU_WR_PROTECT		= 0x5,
> +	UNIT_DESC_PARAM_LU_Q_DEPTH		= 0x6,
> +	UNIT_DESC_PARAM_MEM_TYPE		= 0x8,
> +	UNIT_DESC_PARAM_DATA_RELIABILITY	= 0x9,
> +	UNIT_DESC_PARAM_LOGICAL_BLK_SIZE	= 0xA,
> +	UNIT_DESC_PARAM_LOGICAL_BLK_COUNT	= 0xB,
> +	UNIT_DESC_PARAM_ERASE_BLK_SIZE		= 0x13,
> +	UNIT_DESC_PARAM_PROVISIONING_TYPE	= 0x17,
> +	UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT	= 0x18,
> +	UNIT_DESC_PARAM_CTX_CAPABILITIES	= 0x20,
> +	UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1	= 0x22,
> +};
> +
> +/*
> + * Logical Unit Write Protect
> + * 00h: LU not write protected
> + * 01h: LU write protected when fPowerOnWPEn =1
> + * 02h: LU permanently write protected when fPermanentWPEn =1
> + */
> +enum ufs_lu_wp_type {
> +	UFS_LU_NO_WP		= 0x00,
> +	UFS_LU_POWER_ON_WP	= 0x01,
> +	UFS_LU_PERM_WP		= 0x02,
> +};
> +
> +/* bActiveICCLevel parameter current units */
> +enum {
> +	UFSHCD_NANO_AMP		= 0,
> +	UFSHCD_MICRO_AMP	= 1,
> +	UFSHCD_MILI_AMP		= 2,
> +	UFSHCD_AMP		= 3,
> +};
> +
> +#define POWER_DESC_MAX_SIZE			0x62
> +#define POWER_DESC_MAX_ACTV_ICC_LVLS		16
> +
> +/* Attribute  bActiveICCLevel parameter bit masks definitions */
> +#define ATTR_ICC_LVL_UNIT_OFFSET	14
> +#define ATTR_ICC_LVL_UNIT_MASK		(0x3 << ATTR_ICC_LVL_UNIT_OFFSET)
> +#define ATTR_ICC_LVL_VALUE_MASK		0x3FF
> +
> +/* Power descriptor parameters offsets in bytes */
> +enum power_desc_param_offset {
> +	PWR_DESC_LEN			= 0x0,
> +	PWR_DESC_TYPE			= 0x1,
> +	PWR_DESC_ACTIVE_LVLS_VCC_0	= 0x2,
> +	PWR_DESC_ACTIVE_LVLS_VCCQ_0	= 0x22,
> +	PWR_DESC_ACTIVE_LVLS_VCCQ2_0	= 0x42,
> +};
> +
> +/* Exception event mask values */
> +enum {
> +	MASK_EE_STATUS		= 0xFFFF,
> +	MASK_EE_URGENT_BKOPS	= (1 << 2),
> +};
> +
> +/* Background operation status */
> +enum bkops_status {
> +	BKOPS_STATUS_NO_OP               = 0x0,
> +	BKOPS_STATUS_NON_CRITICAL        = 0x1,
> +	BKOPS_STATUS_PERF_IMPACT         = 0x2,
> +	BKOPS_STATUS_CRITICAL            = 0x3,
> +	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,
> +};
> +
> +/* Query response result code */
> +enum {
> +	QUERY_RESULT_SUCCESS                    = 0x00,
> +	QUERY_RESULT_NOT_READABLE               = 0xF6,
> +	QUERY_RESULT_NOT_WRITEABLE              = 0xF7,
> +	QUERY_RESULT_ALREADY_WRITTEN            = 0xF8,
> +	QUERY_RESULT_INVALID_LENGTH             = 0xF9,
> +	QUERY_RESULT_INVALID_VALUE              = 0xFA,
> +	QUERY_RESULT_INVALID_SELECTOR           = 0xFB,
> +	QUERY_RESULT_INVALID_INDEX              = 0xFC,
> +	QUERY_RESULT_INVALID_IDN                = 0xFD,
> +	QUERY_RESULT_INVALID_OPCODE             = 0xFE,
> +	QUERY_RESULT_GENERAL_FAILURE            = 0xFF,
> +};
> +
> +/* UTP Transfer Request Command Type (CT) */
> +enum {
> +	UPIU_COMMAND_SET_TYPE_SCSI	= 0x0,
> +	UPIU_COMMAND_SET_TYPE_UFS	= 0x1,
> +	UPIU_COMMAND_SET_TYPE_QUERY	= 0x2,
> +};
> +
> +/* UTP Transfer Request Command Offset */
> +#define UPIU_COMMAND_TYPE_OFFSET	28
> +
> +/* Offset of the response code in the UPIU header */
> +#define UPIU_RSP_CODE_OFFSET		8
> +
> +enum {
> +	MASK_SCSI_STATUS		= 0xFF,
> +	MASK_TASK_RESPONSE              = 0xFF00,
> +	MASK_RSP_UPIU_RESULT            = 0xFFFF,
> +	MASK_QUERY_DATA_SEG_LEN         = 0xFFFF,
> +	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
> +	MASK_RSP_EXCEPTION_EVENT        = 0x10000,
> +};
> +
> +/* Task management service response */
> +enum {
> +	UPIU_TASK_MANAGEMENT_FUNC_COMPL		= 0x00,
> +	UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
> +	UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED	= 0x08,
> +	UPIU_TASK_MANAGEMENT_FUNC_FAILED	= 0x05,
> +	UPIU_INCORRECT_LOGICAL_UNIT_NO		= 0x09,
> +};
> +
> +/* UFS device power modes */
> +enum ufs_dev_pwr_mode {
> +	UFS_ACTIVE_PWR_MODE	= 1,
> +	UFS_SLEEP_PWR_MODE	= 2,
> +	UFS_POWERDOWN_PWR_MODE	= 3,
> +};
> +
> +/**
> + * struct utp_upiu_header - UPIU header structure
> + * @dword_0: UPIU header DW-0
> + * @dword_1: UPIU header DW-1
> + * @dword_2: UPIU header DW-2
> + */
> +struct utp_upiu_header {
> +	__be32 dword_0;
> +	__be32 dword_1;
> +	__be32 dword_2;
> +};
> +
> +/**
> + * struct utp_upiu_cmd - Command UPIU structure
> + * @data_transfer_len: Data Transfer Length DW-3
> + * @cdb: Command Descriptor Block CDB DW-4 to DW-7
> + */
> +struct utp_upiu_cmd {
> +	__be32 exp_data_transfer_len;
> +	u8 cdb[MAX_CDB_SIZE];
> +};
> +
> +/**
> + * struct utp_upiu_query - upiu request buffer structure for
> + * query request.
> + * @opcode: command to perform B-0
> + * @idn: a value that indicates the particular type of data B-1
> + * @index: Index to further identify data B-2
> + * @selector: Index to further identify data B-3
> + * @reserved_osf: spec reserved field B-4,5
> + * @length: number of descriptor bytes to read/write B-6,7
> + * @value: Attribute value to be written DW-5
> + * @reserved: spec reserved DW-6,7
> + */
> +struct utp_upiu_query {
> +	u8 opcode;
> +	u8 idn;
> +	u8 index;
> +	u8 selector;
> +	__be16 reserved_osf;
> +	__be16 length;
> +	__be32 value;
> +	__be32 reserved[2];
> +};
> +
> +/**
> + * struct utp_upiu_req - general upiu request structure
> + * @header:UPIU header structure DW-0 to DW-2
> + * @sc: fields structure for scsi command DW-3 to DW-7
> + * @qr: fields structure for query request DW-3 to DW-7
> + */
> +struct utp_upiu_req {
> +	struct utp_upiu_header header;
> +	union {
> +		struct utp_upiu_cmd sc;
> +		struct utp_upiu_query qr;
> +	};
> +};
> +
> +/**
> + * struct utp_cmd_rsp - Response UPIU structure
> + * @residual_transfer_count: Residual transfer count DW-3
> + * @reserved: Reserved double words DW-4 to DW-7
> + * @sense_data_len: Sense data length DW-8 U16
> + * @sense_data: Sense data field DW-8 to DW-12
> + */
> +struct utp_cmd_rsp {
> +	__be32 residual_transfer_count;
> +	__be32 reserved[4];
> +	__be16 sense_data_len;
> +	u8 sense_data[18];
> +};
> +
> +/**
> + * struct utp_upiu_rsp - general upiu response structure
> + * @header: UPIU header structure DW-0 to DW-2
> + * @sr: fields structure for scsi command DW-3 to DW-12
> + * @qr: fields structure for query request DW-3 to DW-7
> + */
> +struct utp_upiu_rsp {
> +	struct utp_upiu_header header;
> +	union {
> +		struct utp_cmd_rsp sr;
> +		struct utp_upiu_query qr;
> +	};
> +};
> +
> +/**
> + * struct utp_upiu_task_req - Task request UPIU structure
> + * @header - UPIU header structure DW0 to DW-2
> + * @input_param1: Input parameter 1 DW-3
> + * @input_param2: Input parameter 2 DW-4
> + * @input_param3: Input parameter 3 DW-5
> + * @reserved: Reserved double words DW-6 to DW-7
> + */
> +struct utp_upiu_task_req {
> +	struct utp_upiu_header header;
> +	__be32 input_param1;
> +	__be32 input_param2;
> +	__be32 input_param3;
> +	__be32 reserved[2];
> +};
> +
> +/**
> + * struct utp_upiu_task_rsp - Task Management Response UPIU structure
> + * @header: UPIU header structure DW0-DW-2
> + * @output_param1: Ouput parameter 1 DW3
> + * @output_param2: Output parameter 2 DW4
> + * @reserved: Reserved double words DW-5 to DW-7
> + */
> +struct utp_upiu_task_rsp {
> +	struct utp_upiu_header header;
> +	__be32 output_param1;
> +	__be32 output_param2;
> +	__be32 reserved[3];
> +};
> +
> +/**
> + * struct ufs_query_req - parameters for building a query request
> + * @query_func: UPIU header query function
> + * @upiu_req: the query request data
> + */
> +struct ufs_query_req {
> +	u8 query_func;
> +	struct utp_upiu_query upiu_req;
> +};
> +
> +/**
> + * struct ufs_query_resp - UPIU QUERY
> + * @response: device response code
> + * @upiu_res: query response data
> + */
> +struct ufs_query_res {
> +	u8 response;
> +	struct utp_upiu_query upiu_res;
> +};
> +
> +#define UFS_VREG_VCC_MIN_UV	   2700000 /* uV */
> +#define UFS_VREG_VCC_MAX_UV	   3600000 /* uV */
> +#define UFS_VREG_VCC_1P8_MIN_UV    1700000 /* uV */
> +#define UFS_VREG_VCC_1P8_MAX_UV    1950000 /* uV */
> +#define UFS_VREG_VCCQ_MIN_UV	   1100000 /* uV */
> +#define UFS_VREG_VCCQ_MAX_UV	   1300000 /* uV */
> +#define UFS_VREG_VCCQ2_MIN_UV	   1650000 /* uV */
> +#define UFS_VREG_VCCQ2_MAX_UV	   1950000 /* uV */
> +
> +/*
> + * VCCQ & VCCQ2 current requirement when UFS device is in sleep state
> + * and link is in Hibern8 state.
> + */
> +#define UFS_VREG_LPM_LOAD_UA	1000 /* uA */
> +
> +struct ufs_vreg {
> +	struct regulator *reg;
> +	const char *name;
> +	bool enabled;
> +	int min_uV;
> +	int max_uV;
> +	int min_uA;
> +	int max_uA;
> +};
> +
> +struct ufs_vreg_info {
> +	struct ufs_vreg *vcc;
> +	struct ufs_vreg *vccq;
> +	struct ufs_vreg *vccq2;
> +	struct ufs_vreg *vdd_hba;
> +};
> +
> +struct ufs_dev_info {
> +	bool f_power_on_wp_en;
> +	/* Keeps information if any of the LU is power on write protected */
> +	bool is_lu_power_on_wp;
> +};
> +
> +#endif /* End of Header */
> diff --git a/include/linux/scsi/ufs/ufshcd.h
> b/include/linux/scsi/ufs/ufshcd.h
> new file mode 100644
> index 0000000..713723d
> --- /dev/null
> +++ b/include/linux/scsi/ufs/ufshcd.h
> @@ -0,0 +1,654 @@
> +/*
> + * Universal Flash Storage Host controller driver
> + *
> + * This code is based on drivers/scsi/ufs/ufshcd.h
> + * Copyright (C) 2011-2013 Samsung India Software Operations
> + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> + *
> + * Authors:
> + *	Santosh Yaraganavi <santosh.sy@xxxxxxxxxxx>
> + *	Vinayak Holikatti <h.vinayak@xxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * See the COPYING file in the top-level directory or visit
> + * <http://www.gnu.org/licenses/gpl-2.0.html>
> + *
> + * 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.
> + *
> + * This program is provided "AS IS" and "WITH ALL FAULTS" and
> + * without warranty of any kind. You are solely responsible for
> + * determining the appropriateness of using and distributing
> + * the program and assume all risks associated with your exercise
> + * of rights with respect to the program, including but not limited
> + * to infringement of third party rights, the risks and costs of
> + * program errors, damage to or loss of data, programs or equipment,
> + * and unavailability or interruption of operations. Under no
> + * circumstances will the contributor of this Program be liable for
> + * any damages of any kind arising from your use or distribution of
> + * this program.
> + */
> +
> +#ifndef _UFSHCD_H
> +#define _UFSHCD_H
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/workqueue.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/wait.h>
> +#include <linux/bitops.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <asm/irq.h>
> +#include <asm/byteorder.h>
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_cmnd.h>
> +#include <scsi/scsi_host.h>
> +#include <scsi/scsi_tcq.h>
> +#include <scsi/scsi_dbg.h>
> +#include <scsi/scsi_eh.h>
> +
> +#include "ufs.h"
> +
> +#define UFSHCD "ufshcd"
> +#define UFSHCD_DRIVER_VERSION "0.2"
> +
> +struct ufs_hba;
> +
> +/**
> + * readl_poll_timeout - Periodically poll an address until a condition is
> met or a timeout occurs
> + * @addr: Address to poll
> + * @val: Variable to read the value into
> + * @cond: Break condition (usually involving @val)
> + * @sleep_us: Maximum time to sleep between reads in us (0 tight-loops)
> + * @timeout_us: Timeout in us, 0 means never timeout
> + *
> + * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
> + * case, the last read value at @addr is stored in @val. Must not
> + * be called from atomic context if sleep_us or timeout_us are used.
> + */
> +#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
> +({ \
> +	ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
> +	might_sleep_if(timeout_us); \
> +	for (;;) { \
> +		(val) = readl(addr); \
> +		if (cond) \
> +			break; \
> +		if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
> +			(val) = readl(addr); \
> +			break; \
> +		} \
> +		if (sleep_us) \
> +			usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
> +	} \
> +	(cond) ? 0 : -ETIMEDOUT; \
> +})
> +
> +enum dev_cmd_type {
> +	DEV_CMD_TYPE_NOP		= 0x0,
> +	DEV_CMD_TYPE_QUERY		= 0x1,
> +};
> +
> +/**
> + * struct uic_command - UIC command structure
> + * @command: UIC command
> + * @argument1: UIC command argument 1
> + * @argument2: UIC command argument 2
> + * @argument3: UIC command argument 3
> + * @cmd_active: Indicate if UIC command is outstanding
> + * @result: UIC command result
> + * @done: UIC command completion
> + */
> +struct uic_command {
> +	u32 command;
> +	u32 argument1;
> +	u32 argument2;
> +	u32 argument3;
> +	int cmd_active;
> +	int result;
> +	struct completion done;
> +};
> +
> +/* Used to differentiate the power management options */
> +enum ufs_pm_op {
> +	UFS_RUNTIME_PM,
> +	UFS_SYSTEM_PM,
> +	UFS_SHUTDOWN_PM,
> +};
> +
> +#define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM)
> +#define ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM)
> +#define ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM)
> +
> +/* Host <-> Device UniPro Link state */
> +enum uic_link_state {
> +	UIC_LINK_OFF_STATE	= 0, /* Link powered down or disabled */
> +	UIC_LINK_ACTIVE_STATE	= 1, /* Link is in Fast/Slow/Sleep state */
> +	UIC_LINK_HIBERN8_STATE	= 2, /* Link is in Hibernate state */
> +};
> +
> +#define ufshcd_is_link_off(hba) ((hba)->uic_link_state ==
> UIC_LINK_OFF_STATE)
> +#define ufshcd_is_link_active(hba) ((hba)->uic_link_state == \
> +				    UIC_LINK_ACTIVE_STATE)
> +#define ufshcd_is_link_hibern8(hba) ((hba)->uic_link_state == \
> +				    UIC_LINK_HIBERN8_STATE)
> +#define ufshcd_set_link_off(hba) ((hba)->uic_link_state =
> UIC_LINK_OFF_STATE)
> +#define ufshcd_set_link_active(hba) ((hba)->uic_link_state = \
> +				    UIC_LINK_ACTIVE_STATE)
> +#define ufshcd_set_link_hibern8(hba) ((hba)->uic_link_state = \
> +				    UIC_LINK_HIBERN8_STATE)
> +
> +/*
> + * UFS Power management levels.
> + * Each level is in increasing order of power savings.
> + */
> +enum ufs_pm_level {
> +	UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE */
> +	UFS_PM_LVL_1, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE */
> +	UFS_PM_LVL_2, /* UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE */
> +	UFS_PM_LVL_3, /* UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE */
> +	UFS_PM_LVL_4, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE */
> +	UFS_PM_LVL_5, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE */
> +	UFS_PM_LVL_MAX
> +};
> +
> +struct ufs_pm_lvl_states {
> +	enum ufs_dev_pwr_mode dev_state;
> +	enum uic_link_state link_state;
> +};
> +
> +/**
> + * struct ufshcd_lrb - local reference block
> + * @utr_descriptor_ptr: UTRD address of the command
> + * @ucd_req_ptr: UCD address of the command
> + * @ucd_rsp_ptr: Response UPIU address for this command
> + * @ucd_prdt_ptr: PRDT address of the command
> + * @cmd: pointer to SCSI command
> + * @sense_buffer: pointer to sense buffer address of the SCSI command
> + * @sense_bufflen: Length of the sense buffer
> + * @scsi_status: SCSI status of the command
> + * @command_type: SCSI, UFS, Query.
> + * @task_tag: Task tag of the command
> + * @lun: LUN of the command
> + * @intr_cmd: Interrupt command (doesn't participate in interrupt
> aggregation)
> + */
> +struct ufshcd_lrb {
> +	struct utp_transfer_req_desc *utr_descriptor_ptr;
> +	struct utp_upiu_req *ucd_req_ptr;
> +	struct utp_upiu_rsp *ucd_rsp_ptr;
> +	struct ufshcd_sg_entry *ucd_prdt_ptr;
> +
> +	struct scsi_cmnd *cmd;
> +	u8 *sense_buffer;
> +	unsigned int sense_bufflen;
> +	int scsi_status;
> +
> +	int command_type;
> +	int task_tag;
> +	u8 lun; /* UPIU LUN id field is only 8-bit wide */
> +	bool intr_cmd;
> +};
> +
> +/**
> + * struct ufs_query - holds relevent data structures for query request
> + * @request: request upiu and function
> + * @descriptor: buffer for sending/receiving descriptor
> + * @response: response upiu and response
> + */
> +struct ufs_query {
> +	struct ufs_query_req request;
> +	u8 *descriptor;
> +	struct ufs_query_res response;
> +};
> +
> +/**
> + * struct ufs_dev_cmd - all assosiated fields with device management
> commands
> + * @type: device management command type - Query, NOP OUT
> + * @lock: lock to allow one command at a time
> + * @complete: internal commands completion
> + * @tag_wq: wait queue until free command slot is available
> + */
> +struct ufs_dev_cmd {
> +	enum dev_cmd_type type;
> +	struct mutex lock;
> +	struct completion *complete;
> +	wait_queue_head_t tag_wq;
> +	struct ufs_query query;
> +};
> +
> +/**
> + * struct ufs_clk_info - UFS clock related info
> + * @list: list headed by hba->clk_list_head
> + * @clk: clock node
> + * @name: clock name
> + * @max_freq: maximum frequency supported by the clock
> + * @min_freq: min frequency that can be used for clock scaling
> + * @curr_freq: indicates the current frequency that it is set to
> + * @enabled: variable to check against multiple enable/disable
> + */
> +struct ufs_clk_info {
> +	struct list_head list;
> +	struct clk *clk;
> +	const char *name;
> +	u32 max_freq;
> +	u32 min_freq;
> +	u32 curr_freq;
> +	bool enabled;
> +};
> +
> +#define PRE_CHANGE      0
> +#define POST_CHANGE     1
> +
> +struct ufs_pa_layer_attr {
> +	u32 gear_rx;
> +	u32 gear_tx;
> +	u32 lane_rx;
> +	u32 lane_tx;
> +	u32 pwr_rx;
> +	u32 pwr_tx;
> +	u32 hs_rate;
> +};
> +
> +struct ufs_pwr_mode_info {
> +	bool is_valid;
> +	struct ufs_pa_layer_attr info;
> +};
> +
> +/**
> + * struct ufs_hba_variant_ops - variant specific callbacks
> + * @name: variant name
> + * @init: called when the driver is initialized
> + * @exit: called to cleanup everything done in init
> + * @clk_scale_notify: notifies that clks are scaled up/down
> + * @setup_clocks: called before touching any of the controller registers
> + * @setup_regulators: called before accessing the host controller
> + * @hce_enable_notify: called before and after HCE enable bit is set to
> allow
> + *                     variant specific Uni-Pro initialization.
> + * @link_startup_notify: called before and after Link startup is carried
> out
> + *                       to allow variant specific Uni-Pro
> initialization.
> + * @pwr_change_notify: called before and after a power mode change
> + *			is carried out to allow vendor spesific capabilities
> + *			to be set.
> + * @suspend: called during host controller PM callback
> + * @resume: called during host controller PM callback
> + */
> +struct ufs_hba_variant_ops {
> +	const char *name;
> +	int	(*init)(struct ufs_hba *);
> +	void    (*exit)(struct ufs_hba *);
> +	void    (*clk_scale_notify)(struct ufs_hba *);
> +	int     (*setup_clocks)(struct ufs_hba *, bool);
> +	int     (*setup_regulators)(struct ufs_hba *, bool);
> +	int     (*hce_enable_notify)(struct ufs_hba *, bool);
> +	int     (*link_startup_notify)(struct ufs_hba *, bool);
> +	int	(*pwr_change_notify)(struct ufs_hba *,
> +					bool, struct ufs_pa_layer_attr *,
> +					struct ufs_pa_layer_attr *);
> +	int     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
> +	int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
> +	int	(*update_sec_cfg)(struct ufs_hba *hba, bool restore_sec_cfg);
> +	int	(*crypto_engine_cfg)(struct ufs_hba *, unsigned int);
> +	int	(*crypto_engine_eh)(struct ufs_hba *);
> +	int	(*crypto_engine_get_err)(struct ufs_hba *);
> +	void	(*crypto_engine_reset_err)(struct ufs_hba *);
> +};
> +
> +/* clock gating state  */
> +enum clk_gating_state {
> +	CLKS_OFF,
> +	CLKS_ON,
> +	REQ_CLKS_OFF,
> +	REQ_CLKS_ON,
> +};
> +
> +/**
> + * struct ufs_clk_gating - UFS clock gating related info
> + * @gate_work: worker to turn off clocks after some delay as specified in
> + * delay_ms
> + * @ungate_work: worker to turn on clocks that will be used in case of
> + * interrupt context
> + * @state: the current clocks state
> + * @delay_ms: gating delay in ms
> + * @is_suspended: clk gating is suspended when set to 1 which can be used
> + * during suspend/resume
> + * @delay_attr: sysfs attribute to control delay_attr
> + * @active_reqs: number of requests that are pending and should be waited
> for
> + * completion before gating clocks.
> + */
> +struct ufs_clk_gating {
> +	struct delayed_work gate_work;
> +	struct work_struct ungate_work;
> +	enum clk_gating_state state;
> +	unsigned long delay_ms;
> +	bool is_suspended;
> +	struct device_attribute delay_attr;
> +	int active_reqs;
> +};
> +
> +struct ufs_clk_scaling {
> +	ktime_t  busy_start_t;
> +	bool is_busy_started;
> +	unsigned long  tot_busy_t;
> +	unsigned long window_start_t;
> +};
> +
> +/**
> + * struct ufs_init_prefetch - contains data that is pre-fetched once
> during
> + * initialization
> + * @icc_level: icc level which was read during initialization
> + */
> +struct ufs_init_prefetch {
> +	u32 icc_level;
> +};
> +
> +/**
> + * struct ufs_hba - per adapter private structure
> + * @mmio_base: UFSHCI base register address
> + * @ucdl_base_addr: UFS Command Descriptor base address
> + * @utrdl_base_addr: UTP Transfer Request Descriptor base address
> + * @utmrdl_base_addr: UTP Task Management Descriptor base address
> + * @ucdl_dma_addr: UFS Command Descriptor DMA address
> + * @utrdl_dma_addr: UTRDL DMA address
> + * @utmrdl_dma_addr: UTMRDL DMA address
> + * @host: Scsi_Host instance of the driver
> + * @dev: device handle
> + * @lrb: local reference block
> + * @lrb_in_use: lrb in use
> + * @outstanding_tasks: Bits representing outstanding task requests
> + * @outstanding_reqs: Bits representing outstanding transfer requests
> + * @capabilities: UFS Controller Capabilities
> + * @nutrs: Transfer Request Queue depth supported by controller
> + * @nutmrs: Task Management Queue depth supported by controller
> + * @ufs_version: UFS Version to which controller complies
> + * @vops: pointer to variant specific operations
> + * @priv: pointer to variant specific private data
> + * @irq: Irq number of the controller
> + * @active_uic_cmd: handle of active UIC command
> + * @uic_cmd_mutex: mutex for uic command
> + * @tm_wq: wait queue for task management
> + * @tm_tag_wq: wait queue for free task management slots
> + * @tm_slots_in_use: bit map of task management request slots in use
> + * @pwr_done: completion for power mode change
> + * @tm_condition: condition variable for task management
> + * @ufshcd_state: UFSHCD states
> + * @eh_flags: Error handling flags
> + * @intr_mask: Interrupt Mask Bits
> + * @ee_ctrl_mask: Exception event control mask
> + * @is_powered: flag to check if HBA is powered
> + * @is_init_prefetch: flag to check if data was pre-fetched in
> initialization
> + * @init_prefetch_data: data pre-fetched during initialization
> + * @eh_work: Worker to handle UFS errors that require s/w attention
> + * @eeh_work: Worker to handle exception events
> + * @errors: HBA errors
> + * @uic_error: UFS interconnect layer error status
> + * @saved_err: sticky error mask
> + * @saved_uic_err: sticky UIC error mask
> + * @dev_cmd: ufs device management command information
> + * @auto_bkops_enabled: to track whether bkops is enabled in device
> + * @vreg_info: UFS device voltage regulator information
> + * @clk_list_head: UFS host controller clocks list node head
> + * @pwr_info: holds current power mode
> + * @max_pwr_info: keeps the device max valid pwm
> + */
> +struct ufs_hba {
> +	void __iomem *mmio_base;
> +
> +	/* Virtual memory reference */
> +	struct utp_transfer_cmd_desc *ucdl_base_addr;
> +	struct utp_transfer_req_desc *utrdl_base_addr;
> +	struct utp_task_req_desc *utmrdl_base_addr;
> +
> +	/* DMA memory reference */
> +	dma_addr_t ucdl_dma_addr;
> +	dma_addr_t utrdl_dma_addr;
> +	dma_addr_t utmrdl_dma_addr;
> +
> +	struct Scsi_Host *host;
> +	struct device *dev;
> +	/*
> +	 * This field is to keep a reference to "scsi_device" corresponding to
> +	 * "UFS device" W-LU.
> +	 */
> +	struct scsi_device *sdev_ufs_device;
> +
> +	enum ufs_dev_pwr_mode curr_dev_pwr_mode;
> +	enum uic_link_state uic_link_state;
> +	/* Desired UFS power management level during runtime PM */
> +	enum ufs_pm_level rpm_lvl;
> +	/* Desired UFS power management level during system PM */
> +	enum ufs_pm_level spm_lvl;
> +	int pm_op_in_progress;
> +
> +	struct ufshcd_lrb *lrb;
> +	unsigned long lrb_in_use;
> +
> +	unsigned long outstanding_tasks;
> +	unsigned long outstanding_reqs;
> +
> +	u32 capabilities;
> +	int nutrs;
> +	int nutmrs;
> +	u32 ufs_version;
> +	struct ufs_hba_variant_ops *vops;
> +	void *priv;
> +	unsigned int irq;
> +	bool is_irq_enabled;
> +
> +	/* Interrupt aggregation support is broken */
> +	#define UFSHCD_QUIRK_BROKEN_INTR_AGGR			UFS_BIT(0)
> +
> +	/*
> +	 * delay before each dme command is required as the unipro
> +	 * layer has shown instabilities
> +	 */
> +	#define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS		UFS_BIT(1)
> +
> +	/*
> +	 * If UFS host controller is having issue in processing LCC (Line
> +	 * Control Command) coming from device then enable this quirk.
> +	 * When this quirk is enabled, host controller driver should disable
> +	 * the LCC transmission on UFS device (by clearing TX_LCC_ENABLE
> +	 * attribute of device to 0).
> +	 */
> +	#define UFSHCD_QUIRK_BROKEN_LCC				UFS_BIT(2)
> +
> +	/*
> +	 * The attribute PA_RXHSUNTERMCAP specifies whether or not the
> +	 * inbound Link supports unterminated line in HS mode. Setting this
> +	 * attribute to 1 fixes moving to HS gear.
> +	 */
> +	#define UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP		UFS_BIT(3)
> +
> +	/*
> +	 * This quirk needs to be enabled if the host contoller only allows
> +	 * accessing the peer dme attributes in AUTO mode (FAST AUTO or
> +	 * SLOW AUTO).
> +	 */
> +	#define UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE		UFS_BIT(4)
> +
> +	unsigned int quirks;	/* Deviations from standard UFSHCI spec. */
> +
> +	/* Device deviations from standard UFS device spec. */
> +	unsigned int dev_quirks;
> +
> +	wait_queue_head_t tm_wq;
> +	wait_queue_head_t tm_tag_wq;
> +	unsigned long tm_condition;
> +	unsigned long tm_slots_in_use;
> +
> +	struct uic_command *active_uic_cmd;
> +	struct mutex uic_cmd_mutex;
> +	struct completion *uic_async_done;
> +
> +	u32 ufshcd_state;
> +	u32 eh_flags;
> +	u32 intr_mask;
> +	u16 ee_ctrl_mask;
> +	bool is_powered;
> +	bool is_init_prefetch;
> +	struct ufs_init_prefetch init_prefetch_data;
> +
> +	/* Work Queues */
> +	struct work_struct eh_work;
> +	struct work_struct eeh_work;
> +
> +	/* HBA Errors */
> +	u32 errors;
> +	u32 uic_error;
> +	u32 saved_err;
> +	u32 saved_uic_err;
> +
> +	/* Device management request data */
> +	struct ufs_dev_cmd dev_cmd;
> +
> +	/* Keeps information of the UFS device connected to this host */
> +	struct ufs_dev_info dev_info;
> +	bool auto_bkops_enabled;
> +	struct ufs_vreg_info vreg_info;
> +	struct list_head clk_list_head;
> +
> +	bool wlun_dev_clr_ua;
> +
> +	struct ufs_pa_layer_attr pwr_info;
> +	struct ufs_pwr_mode_info max_pwr_info;
> +
> +	struct ufs_clk_gating clk_gating;
> +	/* Control to enable/disable host capabilities */
> +	u32 caps;
> +	/* Allow dynamic clk gating */
> +#define UFSHCD_CAP_CLK_GATING	(1 << 0)
> +	/* Allow hiberb8 with clk gating */
> +#define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1)
> +	/* Allow dynamic clk scaling */
> +#define UFSHCD_CAP_CLK_SCALING	(1 << 2)
> +	/* Allow auto bkops to enabled during runtime suspend */
> +#define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)
> +
> +	struct devfreq *devfreq;
> +	struct ufs_clk_scaling clk_scaling;
> +	bool is_sys_suspended;
> +};
> +
> +/* Returns true if clocks can be gated. Otherwise false */
> +static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
> +{
> +	return hba->caps & UFSHCD_CAP_CLK_GATING;
> +}
> +static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
> +{
> +	return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
> +}
> +static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba)
> +{
> +	return hba->caps & UFSHCD_CAP_CLK_SCALING;
> +}
> +static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba
> *hba)
> +{
> +	return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
> +}
> +
> +#define ufshcd_writel(hba, val, reg)	\
> +	writel((val), (hba)->mmio_base + (reg))
> +#define ufshcd_readl(hba, reg)	\
> +	readl((hba)->mmio_base + (reg))
> +
> +/**
> + * ufshcd_rmwl - read modify write into a register
> + * @hba - per adapter instance
> + * @mask - mask to apply on read value
> + * @val - actual value to write
> + * @reg - register address
> + */
> +static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val,
> u32 reg)
> +{
> +	u32 tmp;
> +
> +	tmp = ufshcd_readl(hba, reg);
> +	tmp &= ~mask;
> +	tmp |= (val & mask);
> +	ufshcd_writel(hba, tmp, reg);
> +}
> +
> +int ufshcd_alloc_host(struct device *, struct ufs_hba **);
> +int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int);
> +void ufshcd_remove(struct ufs_hba *);
> +
> +extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
> +extern int ufshcd_runtime_resume(struct ufs_hba *hba);
> +extern int ufshcd_runtime_idle(struct ufs_hba *hba);
> +extern int ufshcd_system_suspend(struct ufs_hba *hba);
> +extern int ufshcd_system_resume(struct ufs_hba *hba);
> +extern int ufshcd_shutdown(struct ufs_hba *hba);
> +extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
> +			       u8 attr_set, u32 mib_val, u8 peer);
> +extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
> +			       u32 *mib_val, u8 peer);
> +
> +/* UIC command interfaces for DME primitives */
> +#define DME_LOCAL	0
> +#define DME_PEER	1
> +#define ATTR_SET_NOR	0	/* NORMAL */
> +#define ATTR_SET_ST	1	/* STATIC */
> +
> +static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
> +				 u32 mib_val)
> +{
> +	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
> +				   mib_val, DME_LOCAL);
> +}
> +
> +static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
> +				    u32 mib_val)
> +{
> +	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
> +				   mib_val, DME_LOCAL);
> +}
> +
> +static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
> +				      u32 mib_val)
> +{
> +	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
> +				   mib_val, DME_PEER);
> +}
> +
> +static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32
> attr_sel,
> +					 u32 mib_val)
> +{
> +	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
> +				   mib_val, DME_PEER);
> +}
> +
> +static inline int ufshcd_dme_get(struct ufs_hba *hba,
> +				 u32 attr_sel, u32 *mib_val)
> +{
> +	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL);
> +}
> +
> +static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
> +				      u32 attr_sel, u32 *mib_val)
> +{
> +	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
> +}
> +
> +int ufshcd_hold(struct ufs_hba *hba, bool async);
> +void ufshcd_release(struct ufs_hba *hba);
> +void ufshcd_abort_outstanding_transfer_requests(struct ufs_hba *hba,
> +		int result);
> +#endif /* End of Header */
> diff --git a/include/linux/scsi/ufs/unipro.h
> b/include/linux/scsi/ufs/unipro.h
> new file mode 100644
> index 0000000..3fc3e21
> --- /dev/null
> +++ b/include/linux/scsi/ufs/unipro.h
> @@ -0,0 +1,207 @@
> +/*
> + * drivers/scsi/ufs/unipro.h
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef _UNIPRO_H_
> +#define _UNIPRO_H_
> +
> +/*
> + * M-TX Configuration Attributes
> + */
> +#define TX_MODE					0x0021
> +#define TX_HSRATE_SERIES			0x0022
> +#define TX_HSGEAR				0x0023
> +#define TX_PWMGEAR				0x0024
> +#define TX_AMPLITUDE				0x0025
> +#define TX_HS_SLEWRATE				0x0026
> +#define TX_SYNC_SOURCE				0x0027
> +#define TX_HS_SYNC_LENGTH			0x0028
> +#define TX_HS_PREPARE_LENGTH			0x0029
> +#define TX_LS_PREPARE_LENGTH			0x002A
> +#define TX_HIBERN8_CONTROL			0x002B
> +#define TX_LCC_ENABLE				0x002C
> +#define TX_PWM_BURST_CLOSURE_EXTENSION		0x002D
> +#define TX_BYPASS_8B10B_ENABLE			0x002E
> +#define TX_DRIVER_POLARITY			0x002F
> +#define TX_HS_UNTERMINATED_LINE_DRIVE_ENABLE	0x0030
> +#define TX_LS_TERMINATED_LINE_DRIVE_ENABLE	0x0031
> +#define TX_LCC_SEQUENCER			0x0032
> +#define TX_MIN_ACTIVATETIME			0x0033
> +#define TX_PWM_G6_G7_SYNC_LENGTH		0x0034
> +
> +/*
> + * M-RX Configuration Attributes
> + */
> +#define RX_MODE					0x00A1
> +#define RX_HSRATE_SERIES			0x00A2
> +#define RX_HSGEAR				0x00A3
> +#define RX_PWMGEAR				0x00A4
> +#define RX_LS_TERMINATED_ENABLE			0x00A5
> +#define RX_HS_UNTERMINATED_ENABLE		0x00A6
> +#define RX_ENTER_HIBERN8			0x00A7
> +#define RX_BYPASS_8B10B_ENABLE			0x00A8
> +#define RX_TERMINATION_FORCE_ENABLE		0x0089
> +
> +#define is_mphy_tx_attr(attr)			(attr < RX_MODE)
> +/*
> + * PHY Adpater attributes
> + */
> +#define PA_ACTIVETXDATALANES	0x1560
> +#define PA_ACTIVERXDATALANES	0x1580
> +#define PA_TXTRAILINGCLOCKS	0x1564
> +#define PA_PHY_TYPE		0x1500
> +#define PA_AVAILTXDATALANES	0x1520
> +#define PA_AVAILRXDATALANES	0x1540
> +#define PA_MINRXTRAILINGCLOCKS	0x1543
> +#define PA_TXPWRSTATUS		0x1567
> +#define PA_RXPWRSTATUS		0x1582
> +#define PA_TXFORCECLOCK		0x1562
> +#define PA_TXPWRMODE		0x1563
> +#define PA_LEGACYDPHYESCDL	0x1570
> +#define PA_MAXTXSPEEDFAST	0x1521
> +#define PA_MAXTXSPEEDSLOW	0x1522
> +#define PA_MAXRXSPEEDFAST	0x1541
> +#define PA_MAXRXSPEEDSLOW	0x1542
> +#define PA_TXLINKSTARTUPHS	0x1544
> +#define PA_TXSPEEDFAST		0x1565
> +#define PA_TXSPEEDSLOW		0x1566
> +#define PA_REMOTEVERINFO	0x15A0
> +#define PA_TXGEAR		0x1568
> +#define PA_TXTERMINATION	0x1569
> +#define PA_HSSERIES		0x156A
> +#define PA_PWRMODE		0x1571
> +#define PA_RXGEAR		0x1583
> +#define PA_RXTERMINATION	0x1584
> +#define PA_MAXRXPWMGEAR		0x1586
> +#define PA_MAXRXHSGEAR		0x1587
> +#define PA_RXHSUNTERMCAP	0x15A5
> +#define PA_RXLSTERMCAP		0x15A6
> +#define PA_PACPREQTIMEOUT	0x1590
> +#define PA_PACPREQEOBTIMEOUT	0x1591
> +#define PA_HIBERN8TIME		0x15A7
> +#define PA_LOCALVERINFO		0x15A9
> +#define PA_TACTIVATE		0x15A8
> +#define PA_PACPFRAMECOUNT	0x15C0
> +#define PA_PACPERRORCOUNT	0x15C1
> +#define PA_PHYTESTCONTROL	0x15C2
> +#define PA_PWRMODEUSERDATA0	0x15B0
> +#define PA_PWRMODEUSERDATA1	0x15B1
> +#define PA_PWRMODEUSERDATA2	0x15B2
> +#define PA_PWRMODEUSERDATA3	0x15B3
> +#define PA_PWRMODEUSERDATA4	0x15B4
> +#define PA_PWRMODEUSERDATA5	0x15B5
> +#define PA_PWRMODEUSERDATA6	0x15B6
> +#define PA_PWRMODEUSERDATA7	0x15B7
> +#define PA_PWRMODEUSERDATA8	0x15B8
> +#define PA_PWRMODEUSERDATA9	0x15B9
> +#define PA_PWRMODEUSERDATA10	0x15BA
> +#define PA_PWRMODEUSERDATA11	0x15BB
> +#define PA_CONNECTEDTXDATALANES	0x1561
> +#define PA_CONNECTEDRXDATALANES	0x1581
> +#define PA_LOGICALLANEMAP	0x15A1
> +#define PA_SLEEPNOCONFIGTIME	0x15A2
> +#define PA_STALLNOCONFIGTIME	0x15A3
> +#define PA_SAVECONFIGTIME	0x15A4
> +
> +/* PA power modes */
> +enum {
> +	FAST_MODE	= 1,
> +	SLOW_MODE	= 2,
> +	FASTAUTO_MODE	= 4,
> +	SLOWAUTO_MODE	= 5,
> +	UNCHANGED	= 7,
> +};
> +
> +/* PA TX/RX Frequency Series */
> +enum {
> +	PA_HS_MODE_A	= 1,
> +	PA_HS_MODE_B	= 2,
> +};
> +
> +enum ufs_pwm_gear_tag {
> +	UFS_PWM_DONT_CHANGE,	/* Don't change Gear */
> +	UFS_PWM_G1,		/* PWM Gear 1 (default for reset) */
> +	UFS_PWM_G2,		/* PWM Gear 2 */
> +	UFS_PWM_G3,		/* PWM Gear 3 */
> +	UFS_PWM_G4,		/* PWM Gear 4 */
> +	UFS_PWM_G5,		/* PWM Gear 5 */
> +	UFS_PWM_G6,		/* PWM Gear 6 */
> +	UFS_PWM_G7,		/* PWM Gear 7 */
> +};
> +
> +enum ufs_hs_gear_tag {
> +	UFS_HS_DONT_CHANGE,	/* Don't change Gear */
> +	UFS_HS_G1,		/* HS Gear 1 (default for reset) */
> +	UFS_HS_G2,		/* HS Gear 2 */
> +	UFS_HS_G3,		/* HS Gear 3 */
> +};
> +
> +/*
> + * Data Link Layer Attributes
> + */
> +#define DL_TC0TXFCTHRESHOLD	0x2040
> +#define DL_FC0PROTTIMEOUTVAL	0x2041
> +#define DL_TC0REPLAYTIMEOUTVAL	0x2042
> +#define DL_AFC0REQTIMEOUTVAL	0x2043
> +#define DL_AFC0CREDITTHRESHOLD	0x2044
> +#define DL_TC0OUTACKTHRESHOLD	0x2045
> +#define DL_TC1TXFCTHRESHOLD	0x2060
> +#define DL_FC1PROTTIMEOUTVAL	0x2061
> +#define DL_TC1REPLAYTIMEOUTVAL	0x2062
> +#define DL_AFC1REQTIMEOUTVAL	0x2063
> +#define DL_AFC1CREDITTHRESHOLD	0x2064
> +#define DL_TC1OUTACKTHRESHOLD	0x2065
> +#define DL_TXPREEMPTIONCAP	0x2000
> +#define DL_TC0TXMAXSDUSIZE	0x2001
> +#define DL_TC0RXINITCREDITVAL	0x2002
> +#define DL_TC0TXBUFFERSIZE	0x2005
> +#define DL_PEERTC0PRESENT	0x2046
> +#define DL_PEERTC0RXINITCREVAL	0x2047
> +#define DL_TC1TXMAXSDUSIZE	0x2003
> +#define DL_TC1RXINITCREDITVAL	0x2004
> +#define DL_TC1TXBUFFERSIZE	0x2006
> +#define DL_PEERTC1PRESENT	0x2066
> +#define DL_PEERTC1RXINITCREVAL	0x2067
> +
> +/*
> + * Network Layer Attributes
> + */
> +#define N_DEVICEID		0x3000
> +#define N_DEVICEID_VALID	0x3001
> +#define N_TC0TXMAXSDUSIZE	0x3020
> +#define N_TC1TXMAXSDUSIZE	0x3021
> +
> +/*
> + * Transport Layer Attributes
> + */
> +#define T_NUMCPORTS		0x4000
> +#define T_NUMTESTFEATURES	0x4001
> +#define T_CONNECTIONSTATE	0x4020
> +#define T_PEERDEVICEID		0x4021
> +#define T_PEERCPORTID		0x4022
> +#define T_TRAFFICCLASS		0x4023
> +#define T_PROTOCOLID		0x4024
> +#define T_CPORTFLAGS		0x4025
> +#define T_TXTOKENVALUE		0x4026
> +#define T_RXTOKENVALUE		0x4027
> +#define T_LOCALBUFFERSPACE	0x4028
> +#define T_PEERBUFFERSPACE	0x4029
> +#define T_CREDITSTOSEND		0x402A
> +#define T_CPORTMODE		0x402B
> +#define T_TC0TXMAXSDUSIZE	0x4060
> +#define T_TC1TXMAXSDUSIZE	0x4061
> +
> +/* Boolean attribute values */
> +enum {
> +	FALSE = 0,
> +	TRUE,
> +};
> +
> +#endif /* _UNIPRO_H_ */
> --
> 1.8.5.2
>
>


-- 
Thanks,
Dolev

-- 
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux
Foundation Collaborative Project


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux