diff options
| author | Yaniv Gardi <ygardi@codeaurora.org> | 2015-07-28 18:32:57 +0300 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:01:23 -0700 |
| commit | 014e52504b851de014b042c7e12277e43df87e94 (patch) | |
| tree | 873ccf30fae1917ef27bd42c0d8f4034466c8970 | |
| parent | 75fd8ad82bad3b88edc03ee58de807dcf26dd8a0 (diff) | |
phy: qcom-ufs: add UFS PHY support for msmcobalt rumi platform
Add support for QRBTC V2 UFS PHY that is used in msmcobalt rumi platform.
Change-Id: I21ad3f0db23ea16d05ba40593cc7650e1a443702
Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
| -rw-r--r-- | Documentation/devicetree/bindings/ufs/ufs-qcom.txt | 2 | ||||
| -rw-r--r-- | drivers/phy/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/phy/Makefile | 1 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qrbtc-v2.c | 182 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qrbtc-v2.h | 114 |
5 files changed, 303 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/ufs/ufs-qcom.txt b/Documentation/devicetree/bindings/ufs/ufs-qcom.txt index 0bc4fdfeed55..8d6c887b16a6 100644 --- a/Documentation/devicetree/bindings/ufs/ufs-qcom.txt +++ b/Documentation/devicetree/bindings/ufs/ufs-qcom.txt @@ -9,7 +9,7 @@ contain a phandle reference to UFS PHY node. Required properties: - compatible : compatible list, contains "qcom,ufs-phy-qmp-20nm" 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-names : indicates various resources passed to driver (via reg proptery) by name. Required "reg-names" is "phy_mem". diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 39b3e1030497..17116411d417 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -373,6 +373,11 @@ choice This select the type of UFS PHY to be used. 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 bool "UFS QCOM v3 PHY" help diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 4efbdb58ba6a..fd15c3452af7 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -40,6 +40,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_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_QRBTC_V2) += phy-qcom-ufs-qrbtc-v2.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_SPEAR1340_MIPHY) += phy-spear1340-miphy.o diff --git a/drivers/phy/phy-qcom-ufs-qrbtc-v2.c b/drivers/phy/phy-qcom-ufs-qrbtc-v2.c new file mode 100644 index 000000000000..89212544af2c --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qrbtc-v2.c @@ -0,0 +1,182 @@ +/* + * 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"); diff --git a/drivers/phy/phy-qcom-ufs-qrbtc-v2.h b/drivers/phy/phy-qcom-ufs-qrbtc-v2.h new file mode 100644 index 000000000000..784d8a50bc0e --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qrbtc-v2.h @@ -0,0 +1,114 @@ +/* + * 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 |
