Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6fa47ee5 authored by Yaniv Gardi's avatar Yaniv Gardi
Browse files

phy: qcom-ufs: add support in UFS PHY for msmcobalt platform



Add support for new QCOM UFS PHY that is used in
future platforms.

Change-Id: I53f162738668ae9f24f5edb9c42a17f947e68b40
Signed-off-by: default avatarYaniv Gardi <ygardi@codeaurora.org>
parent 34f0b490
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -8,8 +8,8 @@ contain a phandle reference to UFS PHY node.


Required properties:
Required properties:
- compatible        : compatible list, contains "qcom,ufs-phy-qmp-20nm"
- compatible        : compatible list, contains "qcom,ufs-phy-qmp-20nm"
		      or "qcom,ufs-phy-qmp-14nm" according to the relevant
		      or "qcom,ufs-phy-qmp-14nm" or "qcom,ufs-phy-qmp-v3"
		      phy in use.
		      according to the relevant phy in use.
- reg               : should contain PHY register address space (mandatory),
- reg               : should contain PHY register address space (mandatory),
- reg-names         : indicates various resources passed to driver (via reg proptery) by name.
- reg-names         : indicates various resources passed to driver (via reg proptery) by name.
                      Required "reg-names" is "phy_mem".
                      Required "reg-names" is "phy_mem".
+5 −0
Original line number Original line Diff line number Diff line
@@ -271,6 +271,11 @@ choice
	  This select the type of UFS PHY to be used.
	  This select the type of UFS PHY to be used.
	  It must match the actual hardware found on your platform.
	  It must match the actual hardware found on your platform.


	config PHY_QCOM_UFS_V3
	bool "UFS QCOM v3 PHY"
	help
	  Select this if your platform has a v3 UFS PHY.

	config PHY_QCOM_UFS_14NM
	config PHY_QCOM_UFS_14NM
	bool "UFS QCOM 14nm PHY"
	bool "UFS QCOM 14nm PHY"
	help
	help
+1 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
phy_qcom_ufs_mod-y				+= phy-qcom-ufs.o
phy_qcom_ufs_mod-y				+= phy-qcom-ufs.o
phy_qcom_ufs_mod-$(CONFIG_PHY_QCOM_UFS_20NM)	+= phy-qcom-ufs-qmp-20nm.o
phy_qcom_ufs_mod-$(CONFIG_PHY_QCOM_UFS_20NM)	+= phy-qcom-ufs-qmp-20nm.o
phy_qcom_ufs_mod-$(CONFIG_PHY_QCOM_UFS_14NM)	+= phy-qcom-ufs-qmp-14nm.o
phy_qcom_ufs_mod-$(CONFIG_PHY_QCOM_UFS_14NM)	+= phy-qcom-ufs-qmp-14nm.o
phy_qcom_ufs_mod-$(CONFIG_PHY_QCOM_UFS_V3)	+= phy-qcom-ufs-qmp-v3.o
obj-$(CONFIG_PHY_QCOM_UFS)			+= phy_qcom_ufs_mod.o
obj-$(CONFIG_PHY_QCOM_UFS)			+= phy_qcom_ufs_mod.o
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
+231 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include "phy-qcom-ufs-qmp-v3.h"

#define UFS_PHY_NAME "ufs_phy_qmp_v3"

static
int ufs_qcom_phy_qmp_v3_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
					bool is_rate_B)
{
	int err;
	int tbl_size_A, tbl_size_B;
	struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;

	tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
	tbl_B = phy_cal_table_rate_B;

	tbl_A = phy_cal_table_rate_A;
	tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);

	err = ufs_qcom_phy_calibrate(ufs_qcom_phy,
				     tbl_A, tbl_size_A,
				     tbl_B, tbl_size_B,
				     is_rate_B);

	if (err)
		dev_err(ufs_qcom_phy->dev,
			"%s: ufs_qcom_phy_calibrate() failed %d\n",
			__func__, err);
	return err;
}

static int ufs_qcom_phy_qmp_v3_init(struct phy *generic_phy)
{
	struct ufs_qcom_phy_qmp_v3 *phy = phy_get_drvdata(generic_phy);
	struct ufs_qcom_phy *phy_common = &phy->common_cfg;
	int err;

	err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
	if (err) {
		dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
			__func__, err);
		goto out;
	}

	err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
	if (err) {
		dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
			__func__, err);
		goto out;
	}

out:
	return err;
}

static
void ufs_qcom_phy_qmp_v3_power_control(struct ufs_qcom_phy *phy,
					 bool power_ctrl)
{
	if (!power_ctrl) {
		/* apply analog power collapse */
		writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
		/*
		 * Make sure that PHY knows its analog rail is going to be
		 * powered OFF.
		 */
		mb();
	} else {
		/* bring PHY out of analog power collapse */
		writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);

		/*
		 * Before any transactions involving PHY, ensure PHY knows
		 * that it's analog rail is powered ON.
		 */
		mb();
	}
}

static inline
void ufs_qcom_phy_qmp_v3_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
{
	/*
	 * v3 PHY does not have TX_LANE_ENABLE register.
	 * Implement this function so as not to propagate error to caller.
	 */
}

static
void ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl)
{
	u32 temp;

	temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE);

	if (ctrl) /* enable RX LineCfg */
		temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT;
	else /* disable RX LineCfg */
		temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT;

	writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE);
	/* make sure that RX LineCfg config applied before we return */
	mb();
}

static inline void ufs_qcom_phy_qmp_v3_start_serdes(struct ufs_qcom_phy *phy)
{
	u32 tmp;

	tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
	tmp &= ~MASK_SERDES_START;
	tmp |= (1 << OFFSET_SERDES_START);
	writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
	/* Ensure register value is committed */
	mb();
}

static int ufs_qcom_phy_qmp_v3_is_pcs_ready(struct ufs_qcom_phy *phy_common)
{
	int err = 0;
	u32 val;

	err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
		val, (val & MASK_PCS_READY), 10, 1000000);
	if (err) {
		dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
			__func__, err);
		goto out;
	}

out:
	return err;
}

struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = {
	.init		= ufs_qcom_phy_qmp_v3_init,
	.exit		= ufs_qcom_phy_exit,
	.power_on	= ufs_qcom_phy_power_on,
	.power_off	= ufs_qcom_phy_power_off,
	.owner		= THIS_MODULE,
};

struct ufs_qcom_phy_specific_ops phy_v3_ops = {
	.calibrate_phy		= ufs_qcom_phy_qmp_v3_phy_calibrate,
	.start_serdes		= ufs_qcom_phy_qmp_v3_start_serdes,
	.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_v3_is_pcs_ready,
	.set_tx_lane_enable	= ufs_qcom_phy_qmp_v3_set_tx_lane_enable,
	.ctrl_rx_linecfg	= ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg,
	.power_control		= ufs_qcom_phy_qmp_v3_power_control,
};

static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct phy *generic_phy;
	struct ufs_qcom_phy_qmp_v3 *phy;
	int err = 0;

	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
	if (!phy) {
		err = -ENOMEM;
		goto out;
	}

	generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
				&ufs_qcom_phy_qmp_v3_phy_ops, &phy_v3_ops);

	if (!generic_phy) {
		dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
			__func__);
		err = -EIO;
		goto out;
	}

	phy_set_drvdata(generic_phy, phy);

	strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
		sizeof(phy->common_cfg.name));

out:
	return err;
}

static int ufs_qcom_phy_qmp_v3_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct phy *generic_phy = to_phy(dev);
	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
	int err = 0;

	err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
	if (err)
		dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
			__func__, err);

	return err;
}

static const struct of_device_id ufs_qcom_phy_qmp_v3_of_match[] = {
	{.compatible = "qcom,ufs-phy-qmp-v3"},
	{},
};
MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_of_match);

static struct platform_driver ufs_qcom_phy_qmp_v3_driver = {
	.probe = ufs_qcom_phy_qmp_v3_probe,
	.remove = ufs_qcom_phy_qmp_v3_remove,
	.driver = {
		.of_match_table = ufs_qcom_phy_qmp_v3_of_match,
		.name = "ufs_qcom_phy_qmp_v3",
		.owner = THIS_MODULE,
	},
};

module_platform_driver(ufs_qcom_phy_qmp_v3_driver);

MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3");
MODULE_LICENSE("GPL v2");
+136 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#ifndef UFS_QCOM_PHY_QMP_V3_H_
#define UFS_QCOM_PHY_QMP_V3_H_

#include "phy-qcom-ufs-i.h"

/* QCOM UFS PHY control registers */
#define COM_OFF(x)	(0x000 + x)
#define PHY_OFF(x)	(0xC00 + x)
#define TX_OFF(n, x)	(0x400 + (0x400 * n) + x)
#define RX_OFF(n, x)	(0x600 + (0x400 * n) + x)

/* UFS PHY QSERDES COM registers */
#define QSERDES_COM_BG_TIMER			COM_OFF(0x0C)
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		COM_OFF(0x34)
#define QSERDES_COM_SYS_CLK_CTRL		COM_OFF(0x3C)
#define QSERDES_COM_PLL_IVCO			COM_OFF(0x48)
#define QSERDES_COM_LOCK_CMP1_MODE0		COM_OFF(0x4C)
#define QSERDES_COM_LOCK_CMP2_MODE0		COM_OFF(0x50)
#define QSERDES_COM_LOCK_CMP3_MODE0		COM_OFF(0x54)
#define QSERDES_COM_LOCK_CMP1_MODE1		COM_OFF(0x58)
#define QSERDES_COM_LOCK_CMP2_MODE1		COM_OFF(0x5C)
#define QSERDES_COM_LOCK_CMP3_MODE1		COM_OFF(0x60)
#define QSERDES_COM_BG_TRIM			COM_OFF(0x70)
#define QSERDES_COM_CP_CTRL_MODE0		COM_OFF(0x78)
#define QSERDES_COM_CP_CTRL_MODE1		COM_OFF(0x7C)
#define QSERDES_COM_PLL_RCTRL_MODE0		COM_OFF(0x84)
#define QSERDES_COM_PLL_RCTRL_MODE1		COM_OFF(0x88)
#define QSERDES_COM_PLL_CCTRL_MODE0		COM_OFF(0x90)
#define QSERDES_COM_PLL_CCTRL_MODE1		COM_OFF(0x94)
#define QSERDES_COM_SYSCLK_EN_SEL		COM_OFF(0xAC)
#define QSERDES_COM_RESETSM_CNTRL		COM_OFF(0xB4)
#define QSERDES_COM_RESCODE_DIV_NUM		COM_OFF(0xC4)
#define QSERDES_COM_LOCK_CMP_EN			COM_OFF(0xC8)
#define QSERDES_COM_LOCK_CMP_CFG		COM_OFF(0xCC)
#define QSERDES_COM_DEC_START_MODE0		COM_OFF(0xD0)
#define QSERDES_COM_DEC_START_MODE1		COM_OFF(0xD4)
#define QSERDES_COM_DIV_FRAC_START1_MODE0	COM_OFF(0xDC)
#define QSERDES_COM_DIV_FRAC_START2_MODE0	COM_OFF(0xE0)
#define QSERDES_COM_DIV_FRAC_START3_MODE0	COM_OFF(0xE4)
#define QSERDES_COM_DIV_FRAC_START1_MODE1	COM_OFF(0xE8)
#define QSERDES_COM_DIV_FRAC_START2_MODE1	COM_OFF(0xEC)
#define QSERDES_COM_DIV_FRAC_START3_MODE1	COM_OFF(0xF0)
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0	COM_OFF(0x108)
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0	COM_OFF(0x10C)
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1	COM_OFF(0x110)
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1	COM_OFF(0x114)
#define QSERDES_COM_VCO_TUNE_CTRL		COM_OFF(0x124)
#define QSERDES_COM_VCO_TUNE_MAP		COM_OFF(0x128)
#define QSERDES_COM_VCO_TUNE1_MODE0		COM_OFF(0x12C)
#define QSERDES_COM_VCO_TUNE2_MODE0		COM_OFF(0x130)
#define QSERDES_COM_VCO_TUNE1_MODE1		COM_OFF(0x134)
#define QSERDES_COM_VCO_TUNE2_MODE1		COM_OFF(0x138)
#define QSERDES_COM_VCO_TUNE_TIMER1		COM_OFF(0x144)
#define QSERDES_COM_VCO_TUNE_TIMER2		COM_OFF(0x148)
#define QSERDES_COM_CLK_SELECT			COM_OFF(0x174)
#define QSERDES_COM_HSCLK_SEL			COM_OFF(0x178)
#define QSERDES_COM_CORECLK_DIV			COM_OFF(0x184)
#define QSERDES_COM_SW_RESET			COM_OFF(0x188)
#define QSERDES_COM_CORE_CLK_EN			COM_OFF(0x18C)
#define QSERDES_COM_CMN_CONFIG			COM_OFF(0x194)
#define QSERDES_COM_SVS_MODE_CLK_SEL		COM_OFF(0x19C)
#define QSERDES_COM_DEBUG_BUS0			COM_OFF(0x1A0)
#define QSERDES_COM_DEBUG_BUS1			COM_OFF(0x1A4)
#define QSERDES_COM_DEBUG_BUS2			COM_OFF(0x1A8)
#define QSERDES_COM_DEBUG_BUS3			COM_OFF(0x1AC)
#define QSERDES_COM_DEBUG_BUS_SEL		COM_OFF(0x1B0)
#define QSERDES_COM_CMN_MISC2			COM_OFF(0x1B8)
#define QSERDES_COM_CORECLK_DIV_MODE1		COM_OFF(0x1BC)

/* UFS PHY registers */
#define UFS_PHY_PHY_START			PHY_OFF(0x00)
#define UFS_PHY_POWER_DOWN_CONTROL		PHY_OFF(0x04)
#define UFS_PHY_TX_LARGE_AMP_DRV_LVL		PHY_OFF(0x2C)
#define UFS_PHY_TX_SMALL_AMP_DRV_LVL		PHY_OFF(0x34)
#define UFS_PHY_LINECFG_DISABLE			PHY_OFF(0x130)
#define UFS_PHY_RX_SIGDET_CTRL2			PHY_OFF(0x140)
#define UFS_PHY_RX_PWM_GEAR_BAND		PHY_OFF(0x14C)
#define UFS_PHY_PCS_READY_STATUS		PHY_OFF(0x160)

/* UFS PHY TX registers */
#define QSERDES_TX_TRANSCEIVER_BIAS_EN			TX_OFF(0, 0x68)
#define QSERDES_TX_LANE_MODE				TX_OFF(0, 0x98)

/* UFS PHY RX registers */
#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF	RX_OFF(0, 0x30)
#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER	RX_OFF(0, 0x34)
#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH	RX_OFF(0, 0x38)
#define QSERDES_RX_UCDR_SVS_SO_GAIN		RX_OFF(0, 0x3C)
#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN	RX_OFF(0, 0x40)
#define QSERDES_RX_UCDR_SO_SATURATION_ENABLE	RX_OFF(0, 0x48)
#define QSERDES_RX_RX_TERM_BW			RX_OFF(0, 0x90)
#define QSERDES_RX_RX_EQ_GAIN1_LSB		RX_OFF(0, 0xC4)
#define QSERDES_RX_RX_EQ_GAIN1_MSB		RX_OFF(0, 0xC8)
#define QSERDES_RX_RX_EQ_GAIN2_LSB		RX_OFF(0, 0xCC)
#define QSERDES_RX_RX_EQ_GAIN2_MSB		RX_OFF(0, 0xD0)
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2	RX_OFF(0, 0xD8)
#define QSERDES_RX_SIGDET_CNTRL			RX_OFF(0, 0x114)
#define QSERDES_RX_SIGDET_LVL			RX_OFF(0, 0x118)
#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL	RX_OFF(0, 0x11C)
#define QSERDES_RX_RX_INTERFACE_MODE		RX_OFF(0, 0x12C)

#define UFS_PHY_RX_LINECFG_DISABLE_BIT		BIT(1)

/*
 * This structure represents the v3 specific phy.
 * common_cfg MUST remain the first field in this structure
 * in case extra fields are added. This way, when calling
 * get_ufs_qcom_phy() of generic phy, we can extract the
 * common phy structure (struct ufs_qcom_phy) out of it
 * regardless of the relevant specific phy.
 */
struct ufs_qcom_phy_qmp_v3 {
	struct ufs_qcom_phy common_cfg;
};

static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
};

static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
};

#endif