From 7f20333fe76780af19ee0d8d2dc6d04d35aa88d2 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 22 Sep 2017 15:33:59 -0400 Subject: soc: brcmstb: Add Product ID and Family ID helper functions Add Product ID and Family ID helper functions for brcmstb soc. Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- include/linux/soc/brcmstb/brcmstb.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'include/linux') diff --git a/include/linux/soc/brcmstb/brcmstb.h b/include/linux/soc/brcmstb/brcmstb.h index 337ce414e898..8baafd6908b9 100644 --- a/include/linux/soc/brcmstb/brcmstb.h +++ b/include/linux/soc/brcmstb/brcmstb.h @@ -1,10 +1,27 @@ #ifndef __BRCMSTB_SOC_H #define __BRCMSTB_SOC_H +static inline u32 BRCM_ID(u32 reg) +{ + return reg >> 28 ? reg >> 16 : reg >> 8; +} + +static inline u32 BRCM_REV(u32 reg) +{ + return reg & 0xff; +} + /* * Bus Interface Unit control register setup, must happen early during boot, * before SMP is brought up, called by machine entry point. */ void brcmstb_biuctrl_init(void); +/* + * Helper functions for getting family or product id from the + * SoC driver. + */ +u32 brcmstb_get_family_id(void); +u32 brcmstb_get_product_id(void); + #endif /* __BRCMSTB_SOC_H */ -- cgit v1.2.3 From 11a6e41c0ee503ffcb971d260bd07dc99b77f13a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 4 Sep 2017 14:53:13 +0200 Subject: phy: Return NULL if the phy is optional If we're trying to get a handle to an optional phy, then the phy framework being disabled shouldn't return an hard error. Instead, return NULL just like phy_optional_get does when there's no phy provided in the DT. Signed-off-by: Maxime Ripard Signed-off-by: Kishon Vijay Abraham I --- include/linux/phy/phy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index e694d4008c4a..10888a717860 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -291,7 +291,7 @@ static inline struct phy *devm_phy_get(struct device *dev, const char *string) static inline struct phy *devm_phy_optional_get(struct device *dev, const char *string) { - return ERR_PTR(-ENOSYS); + return NULL; } static inline struct phy *devm_of_phy_get(struct device *dev, -- cgit v1.2.3 From fd3e4c98e6e7a12dc47ef98ee12c0c3c024b5ee9 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 12 Oct 2017 11:49:33 +0530 Subject: phy: Add UFS PHY modes UFS phy has two modes for each High speed generation. These modes are identified by two rates of operations - Rate A, and Rate B. Add these UFS phy modes to phy framework. Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- include/linux/phy/phy.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 10888a717860..194d08174516 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -29,6 +29,8 @@ enum phy_mode { PHY_MODE_USB_OTG, PHY_MODE_SGMII, PHY_MODE_10GKR, + PHY_MODE_UFS_HS_A, + PHY_MODE_UFS_HS_B, }; /** -- cgit v1.2.3 From 052553af6a31b459adfdc6fd1eebced75de332fc Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 12 Oct 2017 11:49:36 +0530 Subject: ufs/phy: qcom: Refactor to use phy_init call Refactor ufs_qcom_power_up_sequence() to get rid of ugly exported phy APIs and use the phy_init() and phy_power_on() to do the phy initialization. Signed-off-by: Vivek Gautam Reviewed-by: Subhash Jadavani Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-ufs-i.h | 3 +- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 15 ++++++++-- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 15 ++++++++-- drivers/phy/qualcomm/phy-qcom-ufs.c | 42 ++++++++++------------------ drivers/scsi/ufs/ufs-qcom.c | 36 ++++++++++-------------- include/linux/phy/phy-qcom-ufs.h | 3 -- 6 files changed, 55 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h index 94326ed107c3..822c83b8efcd 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h @@ -114,6 +114,7 @@ struct ufs_qcom_phy { struct ufs_qcom_phy_calibration *cached_regs; int cached_regs_table_size; bool is_powered_on; + bool is_started; struct ufs_qcom_phy_specific_ops *phy_spec_ops; enum phy_mode mode; @@ -123,7 +124,6 @@ struct ufs_qcom_phy { * 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. @@ -132,7 +132,6 @@ struct ufs_qcom_phy { * and writes to QSERDES_RX_SIGDET_CNTRL attribute */ struct ufs_qcom_phy_specific_ops { - int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B); void (*start_serdes)(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); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c index af65785230b5..ba1895b76a5d 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c @@ -44,7 +44,19 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) { - return 0; + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + bool is_rate_B = false; + int ret; + + if (phy_common->mode == PHY_MODE_UFS_HS_B) + is_rate_B = true; + + ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B); + if (!ret) + /* phy calibrated, but yet to be started */ + phy_common->is_started = false; + + return ret; } static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) @@ -120,7 +132,6 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { }; static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { - .calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate, .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c index 5c18c41dbdb4..49f435c71147 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c @@ -63,7 +63,19 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) { - return 0; + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + bool is_rate_B = false; + int ret; + + if (phy_common->mode == PHY_MODE_UFS_HS_B) + is_rate_B = true; + + ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B); + if (!ret) + /* phy calibrated, but yet to be started */ + phy_common->is_started = false; + + return ret; } static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) @@ -178,7 +190,6 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { }; static 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, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c index 43865ef340e2..c5ff4525edef 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -518,9 +518,8 @@ void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) } } -int ufs_qcom_phy_start_serdes(struct phy *generic_phy) +static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_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) { @@ -533,7 +532,6 @@ int ufs_qcom_phy_start_serdes(struct phy *generic_phy) return ret; } -EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes); int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) { @@ -564,31 +562,8 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, } EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); -int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) -{ - 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, is_rate_B); - if (ret) - dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n", - __func__, ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy); - -int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) +static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_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__); @@ -598,7 +573,6 @@ int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) return ufs_qcom_phy->phy_spec_ops-> is_physical_coding_sublayer_ready(ufs_qcom_phy); } -EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready); int ufs_qcom_phy_power_on(struct phy *generic_phy) { @@ -609,6 +583,18 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) if (phy_common->is_powered_on) return 0; + if (!phy_common->is_started) { + err = ufs_qcom_phy_start_serdes(phy_common); + if (err) + return err; + + err = ufs_qcom_phy_is_pcs_ready(phy_common); + if (err) + return err; + + phy_common->is_started = true; + } + err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); if (err) { dev_err(dev, "%s enable vdda_phy failed, err=%d\n", diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 44c21d5818ee..890eafeb8ad4 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -281,10 +281,10 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) /* provide 1ms delay to let the reset pulse propagate */ usleep_range(1000, 1100); - ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B); - + /* phy initialization - calibrate the phy */ + ret = phy_init(phy); if (ret) { - dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret = %d\n", + dev_err(hba->dev, "%s: phy init failed, ret = %d\n", __func__, ret); goto out; } @@ -297,21 +297,22 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) * voltage, current to settle down before starting serdes. */ usleep_range(1000, 1100); - ret = ufs_qcom_phy_start_serdes(phy); + + /* power on phy - start serdes and phy's power and clocks */ + ret = phy_power_on(phy); if (ret) { - dev_err(hba->dev, "%s: ufs_qcom_phy_start_serdes() failed, ret = %d\n", + dev_err(hba->dev, "%s: phy power on failed, ret = %d\n", __func__, ret); - goto out; + goto out_disable_phy; } - 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); - ufs_qcom_select_unipro_mode(host); + return 0; + +out_disable_phy: + ufs_qcom_assert_reset(hba); + phy_exit(phy); out: return ret; } @@ -1276,14 +1277,9 @@ static int ufs_qcom_init(struct ufs_hba *hba) ufs_qcom_phy_save_controller_version(host->generic_phy, host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step); - phy_init(host->generic_phy); - err = phy_power_on(host->generic_phy); - if (err) - goto out_unregister_bus; - err = ufs_qcom_init_lane_clks(host); if (err) - goto out_disable_phy; + goto out_variant_clear; ufs_qcom_set_caps(hba); ufs_qcom_advertise_quirks(hba); @@ -1304,10 +1300,6 @@ static int ufs_qcom_init(struct ufs_hba *hba) goto out; -out_disable_phy: - phy_power_off(host->generic_phy); -out_unregister_bus: - phy_exit(host->generic_phy); out_variant_clear: ufshcd_set_variant(hba, NULL); out: diff --git a/include/linux/phy/phy-qcom-ufs.h b/include/linux/phy/phy-qcom-ufs.h index 35c070ea6ea3..0a2c18a9771d 100644 --- a/include/linux/phy/phy-qcom-ufs.h +++ b/include/linux/phy/phy-qcom-ufs.h @@ -31,10 +31,7 @@ void ufs_qcom_phy_enable_dev_ref_clk(struct phy *phy); */ void ufs_qcom_phy_disable_dev_ref_clk(struct phy *phy); -int ufs_qcom_phy_start_serdes(struct phy *phy); int ufs_qcom_phy_set_tx_lane_enable(struct phy *phy, u32 tx_lanes); -int ufs_qcom_phy_calibrate_phy(struct phy *phy, bool is_rate_B); -int ufs_qcom_phy_is_pcs_ready(struct phy *phy); void ufs_qcom_phy_save_controller_version(struct phy *phy, u8 major, u16 minor, u16 step); -- cgit v1.2.3 From 36914111e6829be36b23d1109214250b5ee1ee9c Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 9 Oct 2017 14:00:50 +0200 Subject: drivers: phy: add calibrate method Some quirky UDCs (like dwc3 on Exynos) need to have their phys calibrated e.g. for using super speed. This patch adds a new phy_calibrate() method. When the calibration should be used is dependent on actual chip. In case of dwc3 on Exynos the calibration must happen after usb_add_hcd() (while in host mode), because certain phy parameters like Tx LOS levels and boost levels need to be calibrated further post initialization of xHCI controller, to get SuperSpeed operations working. But an hcd must be prepared first in order to pass it to usb_add_hcd(), so, in particular, dwc3 registers must be available first, and in order for the latter to happen the phys must be initialized. This poses a chicken and egg problem if the calibration were to be performed in phy_init(). To break the circular dependency a separate method is added which can be called at a desired moment after phy intialization. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 15 +++++++++++++++ include/linux/phy/phy.h | 10 ++++++++++ 2 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a268f4d6f3e9..b4964b067aec 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -372,6 +372,21 @@ int phy_reset(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_reset); +int phy_calibrate(struct phy *phy) +{ + int ret; + + if (!phy || !phy->ops->calibrate) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->calibrate(phy); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_calibrate); + /** * _of_phy_get() - lookup and obtain a reference to a phy by phandle * @np: device_node for which to get the phy diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 194d08174516..4f8423a948d5 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -41,6 +41,7 @@ enum phy_mode { * @power_off: powering off the phy * @set_mode: set the mode of the phy * @reset: resetting the phy + * @calibrate: calibrate the phy * @owner: the module owner containing the ops */ struct phy_ops { @@ -50,6 +51,7 @@ struct phy_ops { int (*power_off)(struct phy *phy); int (*set_mode)(struct phy *phy, enum phy_mode mode); int (*reset)(struct phy *phy); + int (*calibrate)(struct phy *phy); struct module *owner; }; @@ -143,6 +145,7 @@ int phy_power_on(struct phy *phy); int phy_power_off(struct phy *phy); int phy_set_mode(struct phy *phy, enum phy_mode mode); int phy_reset(struct phy *phy); +int phy_calibrate(struct phy *phy); static inline int phy_get_bus_width(struct phy *phy) { return phy->attrs.bus_width; @@ -264,6 +267,13 @@ static inline int phy_reset(struct phy *phy) return -ENOSYS; } +static inline int phy_calibrate(struct phy *phy) +{ + if (!phy) + return 0; + return -ENOSYS; +} + static inline int phy_get_bus_width(struct phy *phy) { return -ENOSYS; -- cgit v1.2.3