Re: [PATCH v7 2/5] phy: qcom-ufs: add support for 20nm phy
Reviewed-by: Dov Levenglick > This change adds a support for a 20nm qcom-ufs phy that is required in > platforms that use ufs-qcom controller. > > Signed-off-by: Yaniv Gardi > > --- > drivers/phy/Makefile| 1 + > drivers/phy/phy-qcom-ufs-i.h| 43 +- > drivers/phy/phy-qcom-ufs-qmp-20nm.c | 257 > > drivers/phy/phy-qcom-ufs-qmp-20nm.h | 235 > + > include/linux/phy/phy-qcom-ufs.h| 59 + > 5 files changed, 594 insertions(+), 1 deletion(-) > 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 include/linux/phy/phy-qcom-ufs.h > > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > index 335965d..781b2fa 100644 > --- a/drivers/phy/Makefile > +++ b/drivers/phy/Makefile > @@ -35,3 +35,4 @@ 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_PHY_QCOM_UFS) += phy-qcom-ufs.o > +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o > diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h > index dac200f..591a391 100644 > --- a/drivers/phy/phy-qcom-ufs-i.h > +++ b/drivers/phy/phy-qcom-ufs-i.h > @@ -15,15 +15,56 @@ > #ifndef UFS_QCOM_PHY_I_H_ > #define UFS_QCOM_PHY_I_H_ > > +#include > #include > +#include > #include > -#include > +#include > #include > #include > #include > > +#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; \ > +}) > + > +#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ > + { \ > + .reg_offset = reg, \ > + .cfg_value = val, \ > + } > + > #define UFS_QCOM_PHY_NAME_LEN30 > > +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; > 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 000..8332f96 > --- /dev/null > +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c > @@ -0,0 +1,257 @@ > +/* > + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#include "phy-qcom-ufs-qmp-20nm.h" > + > +#define UFS_PHY_NAME "ufs_phy_qmp_20nm" > + > +static > +int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy > *ufs_qcom_phy, > + bool is_rate_B) > +{ > + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; > + int tbl_size_A, tbl_size_B; > + u8 major = ufs_qcom_phy->host_ctrl_rev_major; > + u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; > + u16 step = ufs_qcom_phy->host_ctrl_rev_step; > + int err; > + > + if ((major == 0x1) && (minor == 0x002) && (step == 0x)) { > + 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 == 0x)) > { > + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); > + tbl_A = phy_cal_table_rate_A_1_3_0; > + } else { > + dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, > no calibration values\n", > + __func__); > + err = -ENODEV; > + goto out; > + } > + > + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); > + tbl_B = phy_cal_table_rate_B; > + > + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, > + tbl_B, tbl_size_B, > is_rate_B); > + > +
Re: [PATCH v7 4/5] phy: qcom-ufs: add support for 14nm phy
Reviewed-by: Dov Levenglick > This change adds a support for a 14nm qcom-ufs phy that is > required in platforms that use ufs-qcom controller. > > Signed-off-by: Yaniv Gardi > > --- > drivers/phy/Makefile| 1 + > drivers/phy/phy-qcom-ufs-qmp-14nm.c | 201 > > drivers/phy/phy-qcom-ufs-qmp-14nm.h | 177 +++ > 3 files changed, 379 insertions(+) > create mode 100644 drivers/phy/phy-qcom-ufs-qmp-14nm.c > create mode 100644 drivers/phy/phy-qcom-ufs-qmp-14nm.h > > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > index 781b2fa..cfbb720 100644 > --- a/drivers/phy/Makefile > +++ b/drivers/phy/Makefile > @@ -36,3 +36,4 @@ obj-$(CONFIG_PHY_STIH407_USB) += > phy-stih407-usb.o > obj-$(CONFIG_PHY_STIH41X_USB)+= phy-stih41x-usb.o > obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o > obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o > +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o > diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c > b/drivers/phy/phy-qcom-ufs-qmp-14nm.c > new file mode 100644 > index 000..f5fc50a > --- /dev/null > +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c > @@ -0,0 +1,201 @@ > +/* > + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#include "phy-qcom-ufs-qmp-14nm.h" > + > +#define UFS_PHY_NAME "ufs_phy_qmp_14nm" > +#define UFS_PHY_VDDA_PHY_UV (925000) > + > +static > +int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy > *ufs_qcom_phy, > + bool is_rate_B) > +{ > + int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); > + int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); > + int err; > + > + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, > + tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); > + > + if (err) > + dev_err(ufs_qcom_phy->dev, > + "%s: ufs_qcom_phy_calibrate() failed %d\n", > + __func__, err); > + return err; > +} > + > +static > +void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy > *phy_common) > +{ > + phy_common->quirks = > + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; > +} > + > +static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) > +{ > + struct ufs_qcom_phy_qmp_14nm *phy = phy_get_drvdata(generic_phy); > + struct ufs_qcom_phy *phy_common = >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; > + } > + phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; > + phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; > + > + ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); > + > +out: > + return err; > +} > + > +static > +void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool > val) > +{ > + writel_relaxed(val ? 0x1 : 0x0, phy->mmio + > UFS_PHY_POWER_DOWN_CONTROL); > + /* > + * Before any transactions involving PHY, ensure PHY knows > + * that it's analog rail is powered ON (or OFF). > + */ > + mb(); > +} > + > +static inline > +void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, > u32 val) > +{ > + /* > + * 14nm PHY does not have TX_LANE_ENABLE register. > + * Implement this function so as not to propagate error to caller. > + */ > +} > + > +static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy > *phy) > +{ > + u32 tmp; > + > + tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); > + tmp &= ~MASK_SERDES_START; > + tmp |= (1 << OFFSET_SERDES_START); > + writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); > + /* Ensure register value is committed */ > + mb(); > +} > + > +static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy > *phy_common) > +{ > + int err = 0; > + u32 val; > + > + err = readl_poll_timeout(phy_common->mmio + > UFS_PHY_PCS_READY_STATUS, > + val, (val &
Re: [PATCH v7 2/5] phy: qcom-ufs: add support for 20nm phy
Reviewed-by: Dov Levenglick > This change adds a support for a 20nm qcom-ufs phy that is required in > platforms that use ufs-qcom controller. > > Signed-off-by: Yaniv Gardi > > --- > drivers/phy/Makefile| 1 + > drivers/phy/phy-qcom-ufs-i.h| 43 +- > drivers/phy/phy-qcom-ufs-qmp-20nm.c | 257 > > drivers/phy/phy-qcom-ufs-qmp-20nm.h | 235 > + > include/linux/phy/phy-qcom-ufs.h| 59 + > 5 files changed, 594 insertions(+), 1 deletion(-) > 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 include/linux/phy/phy-qcom-ufs.h > > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > index 335965d..781b2fa 100644 > --- a/drivers/phy/Makefile > +++ b/drivers/phy/Makefile > @@ -35,3 +35,4 @@ 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_PHY_QCOM_UFS) += phy-qcom-ufs.o > +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o > diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h > index dac200f..591a391 100644 > --- a/drivers/phy/phy-qcom-ufs-i.h > +++ b/drivers/phy/phy-qcom-ufs-i.h > @@ -15,15 +15,56 @@ > #ifndef UFS_QCOM_PHY_I_H_ > #define UFS_QCOM_PHY_I_H_ > > +#include > #include > +#include > #include > -#include > +#include > #include > #include > #include > > +#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; \ > +}) > + > +#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ > + { \ > + .reg_offset = reg, \ > + .cfg_value = val, \ > + } > + > #define UFS_QCOM_PHY_NAME_LEN30 > > +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; > 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 000..8332f96 > --- /dev/null > +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c > @@ -0,0 +1,257 @@ > +/* > + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#include "phy-qcom-ufs-qmp-20nm.h" > + > +#define UFS_PHY_NAME "ufs_phy_qmp_20nm" > + > +static > +int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy > *ufs_qcom_phy, > + bool is_rate_B) > +{ > + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; > + int tbl_size_A, tbl_size_B; > + u8 major = ufs_qcom_phy->host_ctrl_rev_major; > + u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; > + u16 step = ufs_qcom_phy->host_ctrl_rev_step; > + int err; > + > + if ((major == 0x1) && (minor == 0x002) && (step == 0x)) { > + 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 == 0x)) > { > + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); > + tbl_A = phy_cal_table_rate_A_1_3_0; > + } else { > + dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, > no calibration values\n", > + __func__); > + err = -ENODEV; > + goto out; > + } > + > + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); > + tbl_B = phy_cal_table_rate_B; > + > + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, > + tbl_B, tbl_size_B, > is_rate_B); > + > +
Re: [PATCH v7 1/5] phy: qcom-ufs: add support for QUALCOMM Technologies UFS PHY drivers
Reviewed-by: Dov Levenglick > This change adds a generic and common API support for ufs phy QUALCOMM > Technologies. This support provides common code and also points > to specific phy callbacks to differentiate between different behaviors > of frequent use-cases (like power on, power off, phy calibration etc). > > Signed-off-by: Yaniv Gardi > > --- > drivers/phy/Kconfig | 7 + > drivers/phy/Makefile | 1 + > drivers/phy/phy-qcom-ufs-i.h | 118 +++ > drivers/phy/phy-qcom-ufs.c | 745 > +++ > 4 files changed, 871 insertions(+) > create mode 100644 drivers/phy/phy-qcom-ufs-i.h > create mode 100644 drivers/phy/phy-qcom-ufs.c > > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig > index ccad880..26a7623 100644 > --- a/drivers/phy/Kconfig > +++ b/drivers/phy/Kconfig > @@ -277,4 +277,11 @@ config PHY_STIH41X_USB > Enable this to support the USB transceiver that is part of > STMicroelectronics STiH41x SoC series. > > +config PHY_QCOM_UFS > + tristate "Qualcomm UFS PHY driver" > + depends on OF && ARCH_MSM > + select GENERIC_PHY > + help > + Support for UFS PHY on QCOM chipsets. > + > endmenu > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > index aa74f96..335965d 100644 > --- a/drivers/phy/Makefile > +++ b/drivers/phy/Makefile > @@ -34,3 +34,4 @@ 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_PHY_QCOM_UFS) += phy-qcom-ufs.o > diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h > new file mode 100644 > index 000..dac200f > --- /dev/null > +++ b/drivers/phy/phy-qcom-ufs-i.h > @@ -0,0 +1,118 @@ > +/* > + * 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_I_H_ > +#define UFS_QCOM_PHY_I_H_ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define UFS_QCOM_PHY_NAME_LEN30 > + > +struct ufs_qcom_phy_calibration { > + u32 reg_offset; > + u32 cfg_value; > +}; > + > +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; > +}; > + > +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; > + > + /* > + * 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 > BIT(0) > + > + u8 host_ctrl_rev_major; > + u16 host_ctrl_rev_minor; > + u16 host_ctrl_rev_step; > + > + 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 > + * @is_physical_coding_sublayer_ready: pointer to a function that > + * checks pcs readiness. returns 0 for success and non-zero for error. > + * @set_tx_lane_enable: pointer to a function that enable tx lanes > + * @power_control: pointer to a function that controls
Re: [PATCH v7 2/5] phy: qcom-ufs: add support for 20nm phy
Reviewed-by: Dov Levenglick d...@codeaurora.org This change adds a support for a 20nm qcom-ufs phy that is required in platforms that use ufs-qcom controller. Signed-off-by: Yaniv Gardi yga...@codeaurora.org --- drivers/phy/Makefile| 1 + drivers/phy/phy-qcom-ufs-i.h| 43 +- drivers/phy/phy-qcom-ufs-qmp-20nm.c | 257 drivers/phy/phy-qcom-ufs-qmp-20nm.h | 235 + include/linux/phy/phy-qcom-ufs.h| 59 + 5 files changed, 594 insertions(+), 1 deletion(-) 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 include/linux/phy/phy-qcom-ufs.h diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 335965d..781b2fa 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -35,3 +35,4 @@ 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_PHY_QCOM_UFS) += phy-qcom-ufs.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h index dac200f..591a391 100644 --- a/drivers/phy/phy-qcom-ufs-i.h +++ b/drivers/phy/phy-qcom-ufs-i.h @@ -15,15 +15,56 @@ #ifndef UFS_QCOM_PHY_I_H_ #define UFS_QCOM_PHY_I_H_ +#include linux/module.h #include linux/clk.h +#include linux/regulator/consumer.h #include linux/slab.h -#include linux/phy/phy.h +#include linux/phy/phy-qcom-ufs.h #include linux/platform_device.h #include linux/io.h #include linux/delay.h +#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; \ +}) + +#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ + { \ + .reg_offset = reg, \ + .cfg_value = val, \ + } + #define UFS_QCOM_PHY_NAME_LEN30 +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; 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 000..8332f96 --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include phy-qcom-ufs-qmp-20nm.h + +#define UFS_PHY_NAME ufs_phy_qmp_20nm + +static +int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + int tbl_size_A, tbl_size_B; + u8 major = ufs_qcom_phy-host_ctrl_rev_major; + u16 minor = ufs_qcom_phy-host_ctrl_rev_minor; + u16 step = ufs_qcom_phy-host_ctrl_rev_step; + int err; + + if ((major == 0x1) (minor == 0x002) (step == 0x)) { + 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 == 0x)) { + 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,
Re: [PATCH v7 4/5] phy: qcom-ufs: add support for 14nm phy
Reviewed-by: Dov Levenglick d...@codeaurora.org This change adds a support for a 14nm qcom-ufs phy that is required in platforms that use ufs-qcom controller. Signed-off-by: Yaniv Gardi yga...@codeaurora.org --- drivers/phy/Makefile| 1 + drivers/phy/phy-qcom-ufs-qmp-14nm.c | 201 drivers/phy/phy-qcom-ufs-qmp-14nm.h | 177 +++ 3 files changed, 379 insertions(+) create mode 100644 drivers/phy/phy-qcom-ufs-qmp-14nm.c create mode 100644 drivers/phy/phy-qcom-ufs-qmp-14nm.h diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 781b2fa..cfbb720 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -36,3 +36,4 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o obj-$(CONFIG_PHY_STIH41X_USB)+= phy-stih41x-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c new file mode 100644 index 000..f5fc50a --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include phy-qcom-ufs-qmp-14nm.h + +#define UFS_PHY_NAME ufs_phy_qmp_14nm +#define UFS_PHY_VDDA_PHY_UV (925000) + +static +int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); + int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + int err; + + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, + tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); + + if (err) + dev_err(ufs_qcom_phy-dev, + %s: ufs_qcom_phy_calibrate() failed %d\n, + __func__, err); + return err; +} + +static +void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) +{ + phy_common-quirks = + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; +} + +static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) +{ + struct ufs_qcom_phy_qmp_14nm *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; + } + phy_common-vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; + phy_common-vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; + + ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); + +out: + return err; +} + +static +void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) +{ + writel_relaxed(val ? 0x1 : 0x0, phy-mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON (or OFF). + */ + mb(); +} + +static inline +void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) +{ + /* + * 14nm PHY does not have TX_LANE_ENABLE register. + * Implement this function so as not to propagate error to caller. + */ +} + +static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) +{ + u32 tmp; + + tmp = readl_relaxed(phy-mmio + UFS_PHY_PHY_START); + tmp = ~MASK_SERDES_START; + tmp |= (1 OFFSET_SERDES_START); + writel_relaxed(tmp, phy-mmio + UFS_PHY_PHY_START); + /* Ensure register value is committed */ + mb(); +} + +static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) +{ + int err = 0; + u32 val; + + err = readl_poll_timeout(phy_common-mmio + UFS_PHY_PCS_READY_STATUS, + val, (val MASK_PCS_READY), 10, 100); + if (err) + dev_err(phy_common-dev, %s: poll for pcs failed err = %d\n, +
Re: [PATCH v7 2/5] phy: qcom-ufs: add support for 20nm phy
Reviewed-by: Dov Levenglick d...@codeaurora.org This change adds a support for a 20nm qcom-ufs phy that is required in platforms that use ufs-qcom controller. Signed-off-by: Yaniv Gardi yga...@codeaurora.org --- drivers/phy/Makefile| 1 + drivers/phy/phy-qcom-ufs-i.h| 43 +- drivers/phy/phy-qcom-ufs-qmp-20nm.c | 257 drivers/phy/phy-qcom-ufs-qmp-20nm.h | 235 + include/linux/phy/phy-qcom-ufs.h| 59 + 5 files changed, 594 insertions(+), 1 deletion(-) 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 include/linux/phy/phy-qcom-ufs.h diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 335965d..781b2fa 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -35,3 +35,4 @@ 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_PHY_QCOM_UFS) += phy-qcom-ufs.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h index dac200f..591a391 100644 --- a/drivers/phy/phy-qcom-ufs-i.h +++ b/drivers/phy/phy-qcom-ufs-i.h @@ -15,15 +15,56 @@ #ifndef UFS_QCOM_PHY_I_H_ #define UFS_QCOM_PHY_I_H_ +#include linux/module.h #include linux/clk.h +#include linux/regulator/consumer.h #include linux/slab.h -#include linux/phy/phy.h +#include linux/phy/phy-qcom-ufs.h #include linux/platform_device.h #include linux/io.h #include linux/delay.h +#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; \ +}) + +#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ + { \ + .reg_offset = reg, \ + .cfg_value = val, \ + } + #define UFS_QCOM_PHY_NAME_LEN30 +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; 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 000..8332f96 --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include phy-qcom-ufs-qmp-20nm.h + +#define UFS_PHY_NAME ufs_phy_qmp_20nm + +static +int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + int tbl_size_A, tbl_size_B; + u8 major = ufs_qcom_phy-host_ctrl_rev_major; + u16 minor = ufs_qcom_phy-host_ctrl_rev_minor; + u16 step = ufs_qcom_phy-host_ctrl_rev_step; + int err; + + if ((major == 0x1) (minor == 0x002) (step == 0x)) { + 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 == 0x)) { + 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,
Re: [PATCH v7 1/5] phy: qcom-ufs: add support for QUALCOMM Technologies UFS PHY drivers
Reviewed-by: Dov Levenglick d...@codeaurora.org This change adds a generic and common API support for ufs phy QUALCOMM Technologies. This support provides common code and also points to specific phy callbacks to differentiate between different behaviors of frequent use-cases (like power on, power off, phy calibration etc). Signed-off-by: Yaniv Gardi yga...@codeaurora.org --- drivers/phy/Kconfig | 7 + drivers/phy/Makefile | 1 + drivers/phy/phy-qcom-ufs-i.h | 118 +++ drivers/phy/phy-qcom-ufs.c | 745 +++ 4 files changed, 871 insertions(+) create mode 100644 drivers/phy/phy-qcom-ufs-i.h create mode 100644 drivers/phy/phy-qcom-ufs.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index ccad880..26a7623 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -277,4 +277,11 @@ config PHY_STIH41X_USB Enable this to support the USB transceiver that is part of STMicroelectronics STiH41x SoC series. +config PHY_QCOM_UFS + tristate Qualcomm UFS PHY driver + depends on OF ARCH_MSM + select GENERIC_PHY + help + Support for UFS PHY on QCOM chipsets. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index aa74f96..335965d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -34,3 +34,4 @@ 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_PHY_QCOM_UFS) += phy-qcom-ufs.o diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h new file mode 100644 index 000..dac200f --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-i.h @@ -0,0 +1,118 @@ +/* + * 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_I_H_ +#define UFS_QCOM_PHY_I_H_ + +#include linux/clk.h +#include linux/slab.h +#include linux/phy/phy.h +#include linux/platform_device.h +#include linux/io.h +#include linux/delay.h + +#define UFS_QCOM_PHY_NAME_LEN30 + +struct ufs_qcom_phy_calibration { + u32 reg_offset; + u32 cfg_value; +}; + +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; +}; + +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; + + /* + * 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 BIT(0) + + u8 host_ctrl_rev_major; + u16 host_ctrl_rev_minor; + u16 host_ctrl_rev_step; + + 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 + * @is_physical_coding_sublayer_ready: pointer to a function that + * checks pcs readiness. returns 0 for success and non-zero for error. + * @set_tx_lane_enable: pointer to a function that enable tx lanes + * @power_control: pointer to a function that controls analog rail of phy +