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

Commit 5224754e authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "phy: qcom-ufs: add UFS PHY support for msmcobalt rumi platform"

parents 1a66b694 d22e5d63
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -9,7 +9,7 @@ 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" or "qcom,ufs-phy-qmp-v3"
		      or "qcom,ufs-phy-qmp-14nm" or "qcom,ufs-phy-qmp-v3"
		      according to the relevant phy in use.
		      or "qcom,ufs-phy-qrbtc-v2" 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_QRBTC_V2
	bool "UFS QCOM QRBTC V2 PHY"
	help
	  Select this if your platform has a QRBTC-V2 UFS PHY.

	config PHY_QCOM_UFS_V3
	config PHY_QCOM_UFS_V3
	bool "UFS QCOM v3 PHY"
	bool "UFS QCOM v3 PHY"
	help
	help
+1 −0
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ 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
phy_qcom_ufs_mod-$(CONFIG_PHY_QCOM_UFS_V3)	+= phy-qcom-ufs-qmp-v3.o
phy_qcom_ufs_mod-$(CONFIG_PHY_QCOM_UFS_QRBTC_V2)	+= phy-qcom-ufs-qrbtc-v2.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
+182 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include "phy-qcom-ufs-qrbtc-v2.h"

#define UFS_PHY_NAME "ufs_phy_qrbtc_v2"

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

	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,
				     NULL, 0,
				     false);

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

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

	/*
	 * The value we are polling for is 0x3D which represents the
	 * following masks:
	 * RESET_SM field: 0x5
	 * RESTRIMDONE bit: BIT(3)
	 * PLLLOCK bit: BIT(4)
	 * READY bit: BIT(5)
	 */
	#define QSERDES_COM_RESET_SM_REG_POLL_VAL	0x3D
	err = readl_poll_timeout(phy_common->mmio + QSERDES_COM_RESET_SM,
		val, (val == QSERDES_COM_RESET_SM_REG_POLL_VAL), 10, 1000000);

	if (err)
		dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
			__func__, err);

	return err;
}

static void ufs_qcom_phy_qrbtc_v2_start_serdes(struct ufs_qcom_phy *phy)
{
	u32 temp;

	writel_relaxed(0x01, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL_OFFSET);

	temp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START_OFFSET);
	temp |= 0x1;
	writel_relaxed(temp, phy->mmio + UFS_PHY_PHY_START_OFFSET);

	/* Ensure register value is committed */
	mb();
}

static int ufs_qcom_phy_qrbtc_v2_init(struct phy *generic_phy)
{
	struct ufs_qcom_phy_qrbtc_v2 *phy = phy_get_drvdata(generic_phy);
	struct ufs_qcom_phy *phy_common = &phy->common_cfg;
	int err = 0;

	writel_relaxed(0x15f, phy_common->mmio + U11_UFS_RESET_REG_OFFSET);

	/* 50ms are required to stabilize the reset */
	usleep_range(50000, 50100);
	writel_relaxed(0x0, phy_common->mmio + U11_UFS_RESET_REG_OFFSET);

	/* Set R3PC REF CLK */
	writel_relaxed(0x80, phy_common->mmio + U11_QRBTC_CONTROL_OFFSET);

	ufs_qcom_phy_qrbtc_v2_phy_calibrate(phy_common, false);
	ufs_qcom_phy_qrbtc_v2_start_serdes(phy_common);
	ufs_qcom_phy_qrbtc_v2_is_pcs_ready(phy_common);

	return err;
}

struct phy_ops ufs_qcom_phy_qrbtc_v2_phy_ops = {
	.init		= ufs_qcom_phy_qrbtc_v2_init,
	.exit		= ufs_qcom_phy_exit,
	.owner		= THIS_MODULE,
};

struct ufs_qcom_phy_specific_ops phy_qrbtc_v2_ops = {
	.calibrate_phy		= ufs_qcom_phy_qrbtc_v2_phy_calibrate,
	.start_serdes		= ufs_qcom_phy_qrbtc_v2_start_serdes,
	.is_physical_coding_sublayer_ready =
				ufs_qcom_phy_qrbtc_v2_is_pcs_ready,
};

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

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

	generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
		&ufs_qcom_phy_qrbtc_v2_phy_ops, &phy_qrbtc_v2_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_qrbtc_v2_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_qrbtc_v2_of_match[] = {
	{.compatible = "qcom,ufs-phy-qrbtc-v2"},
	{},
};
MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qrbtc_v2_of_match);

static struct platform_driver ufs_qcom_phy_qrbtc_v2_driver = {
	.probe = ufs_qcom_phy_qrbtc_v2_probe,
	.remove = ufs_qcom_phy_qrbtc_v2_remove,
	.driver = {
		.of_match_table = ufs_qcom_phy_qrbtc_v2_of_match,
		.name = "ufs_qcom_phy_qrbtc_v2",
		.owner = THIS_MODULE,
	},
};

module_platform_driver(ufs_qcom_phy_qrbtc_v2_driver);

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

#ifndef UFS_QCOM_PHY_QRBTC_V2_H_
#define UFS_QCOM_PHY_QRBTC_V2_H_

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

/* QCOM UFS PHY control registers */
#define COM_OFF(x)	(0x000 + x)
#define PHY_OFF(x)	(0x700 + x)
#define PHY_USR(x)	(0x11000 + x)

#define UFS_PHY_PHY_START_OFFSET		PHY_OFF(0x00)
#define UFS_PHY_POWER_DOWN_CONTROL_OFFSET	PHY_OFF(0x04)

#define	QSERDES_COM_BIAS_EN_CLKBUFLR_EN_OFFSET	COM_OFF(0x20)
#define QSERDES_COM_SYSCLK_EN_SEL		COM_OFF(0x38)
#define QSERDES_COM_SYS_CLK_CTRL		COM_OFF(0x00)
#define QSERDES_COM_RES_CODE_TXBAND		COM_OFF(0x3C)
#define QSERDES_COM_PLL_VCOTAIL_EN		COM_OFF(0x04)
#define QSERDES_COM_PLL_CNTRL			COM_OFF(0x14)
#define QSERDES_COM_PLL_CLKEPDIV		COM_OFF(0xB0)
#define QSERDES_COM_RESETSM_CNTRL		COM_OFF(0x40)
#define QSERDES_COM_PLL_RXTXEPCLK_EN		COM_OFF(0xA8)
#define QSERDES_COM_PLL_CRCTRL			COM_OFF(0xAC)
#define QSERDES_COM_DEC_START1			COM_OFF(0x64)
#define QSERDES_COM_DEC_START2			COM_OFF(0xA4)
#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_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_PLL_IP_SETI			COM_OFF(0x18)
#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_RESET_SM			COM_OFF(0xBC)
#define QSERDES_COM_PWM_CNTRL1			COM_OFF(0x280)
#define QSERDES_COM_PWM_CNTRL2			COM_OFF(0x284)
#define QSERDES_COM_PWM_NDIV			COM_OFF(0x288)
#define QSERDES_COM_CDR_CONTROL			COM_OFF(0x200)
#define QSERDES_COM_CDR_CONTROL_HALF		COM_OFF(0x298)
#define QSERDES_COM_CDR_CONTROL_QUARTER		COM_OFF(0x29C)
#define QSERDES_COM_SIGDET_CNTRL		COM_OFF(0x234)
#define QSERDES_COM_SIGDET_CNTRL2		COM_OFF(0x28C)
#define QSERDES_COM_UFS_CNTRL			COM_OFF(0x290)

/* QRBTC V2 USER REGISTERS */
#define U11_UFS_RESET_REG_OFFSET		PHY_USR(0x4)
#define U11_QRBTC_CONTROL_OFFSET		PHY_USR(0x18)

static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
	UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PHY_START_OFFSET, 0x00),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN_OFFSET, 0x3F),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x03),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x16),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_TXBAND, 0xC0),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0x03),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x88),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x30),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0x94),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x02),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x8C),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0xAE),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x1F),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xF7),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x13),
	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_PLL_IP_SETI, 0x01),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3B),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x0A),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x04),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PWM_CNTRL1, 0x8F),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PWM_CNTRL2, 0x61),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PWM_NDIV, 0x4F),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CDR_CONTROL, 0xF2),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CDR_CONTROL_HALF, 0x2A),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CDR_CONTROL_QUARTER, 0x2A),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SIGDET_CNTRL, 0xC0),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SIGDET_CNTRL2, 0x07),
	UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_UFS_CNTRL, 0x18),
};

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

#endif