diff options
Diffstat (limited to 'drivers/phy/qualcomm')
| -rw-r--r-- | drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 | ||||
| -rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 191 | ||||
| -rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 32 | ||||
| -rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h | 13 | ||||
| -rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp.h | 2 |
5 files changed, 189 insertions, 51 deletions
diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c index 0a0d2d9fc846..95cd3175926d 100644 --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c @@ -25,6 +25,7 @@ #define POR BIT(1) #define USB_PHY_HS_PHY_CTRL_COMMON0 (0x54) +#define PHY_ENABLE BIT(0) #define SIDDQ_SEL BIT(1) #define SIDDQ BIT(2) #define FSEL GENMASK(6, 4) @@ -81,6 +82,7 @@ struct m31_eusb2_priv_data { static const struct m31_phy_tbl_entry m31_eusb2_setup_tbl[] = { M31_EUSB_PHY_INIT_CFG(USB_PHY_CFG0, UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 1), M31_EUSB_PHY_INIT_CFG(USB_PHY_UTMI_CTRL5, POR, 1), + M31_EUSB_PHY_INIT_CFG(USB_PHY_HS_PHY_CTRL_COMMON0, PHY_ENABLE, 1), M31_EUSB_PHY_INIT_CFG(USB_PHY_CFG1, PLL_EN, 1), M31_EUSB_PHY_INIT_CFG(USB_PHY_FSEL_SEL, FSEL_SEL, 1), }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 7b5af30f1d02..9e2a6c5d0f58 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_graph.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> @@ -1643,14 +1644,9 @@ static const struct qmp_phy_init_tbl x1e80100_usb43dp_pcs_usb_tbl[] = { }; /* list of regulators */ -struct qmp_regulator_data { - const char *name; - unsigned int enable_load; -}; - -static struct qmp_regulator_data qmp_phy_vreg_l[] = { - { .name = "vdda-phy", .enable_load = 21800 }, - { .name = "vdda-pll", .enable_load = 36000 }, +static struct regulator_bulk_data qmp_phy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 21800, }, + { .supply = "vdda-pll", .init_load_uA = 36000, }, }; static const u8 qmp_dp_v3_pre_emphasis_hbr3_hbr2[4][4] = { @@ -1744,6 +1740,26 @@ static const u8 qmp_dp_v6_pre_emphasis_hbr_rbr[4][4] = { { 0x22, 0xff, 0xff, 0xff } }; +struct qmp_combo_lane_mapping { + unsigned int lanes_count; + enum typec_orientation orientation; + u32 lanes[4]; +}; + +static const struct qmp_combo_lane_mapping usb3_data_lanes[] = { + { 2, TYPEC_ORIENTATION_NORMAL, { 1, 0 }}, + { 2, TYPEC_ORIENTATION_REVERSE, { 2, 3 }}, +}; + +static const struct qmp_combo_lane_mapping dp_data_lanes[] = { + { 1, TYPEC_ORIENTATION_NORMAL, { 3 }}, + { 1, TYPEC_ORIENTATION_REVERSE, { 0 }}, + { 2, TYPEC_ORIENTATION_NORMAL, { 3, 2 }}, + { 2, TYPEC_ORIENTATION_REVERSE, { 0, 1 }}, + { 4, TYPEC_ORIENTATION_NORMAL, { 3, 2, 1, 0 }}, + { 4, TYPEC_ORIENTATION_REVERSE, { 0, 1, 2, 3 }}, +}; + struct qmp_combo; struct qmp_combo_offsets { @@ -1808,7 +1824,7 @@ struct qmp_phy_cfg { const char * const *reset_list; int num_resets; /* regulators to be requested */ - const struct qmp_regulator_data *vreg_list; + const struct regulator_bulk_data *vreg_list; int num_vregs; /* array of registers with different offsets */ @@ -3439,39 +3455,6 @@ static const struct dev_pm_ops qmp_combo_pm_ops = { qmp_combo_runtime_resume, NULL) }; -static int qmp_combo_vreg_init(struct qmp_combo *qmp) -{ - const struct qmp_phy_cfg *cfg = qmp->cfg; - struct device *dev = qmp->dev; - int num = cfg->num_vregs; - int ret, i; - - qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); - if (!qmp->vregs) - return -ENOMEM; - - for (i = 0; i < num; i++) - qmp->vregs[i].supply = cfg->vreg_list[i].name; - - ret = devm_regulator_bulk_get(dev, num, qmp->vregs); - if (ret) { - dev_err(dev, "failed at devm_regulator_bulk_get\n"); - return ret; - } - - for (i = 0; i < num; i++) { - ret = regulator_set_load(qmp->vregs[i].consumer, - cfg->vreg_list[i].enable_load); - if (ret) { - dev_err(dev, "failed to set load at %s\n", - qmp->vregs[i].supply); - return ret; - } - } - - return 0; -} - static int qmp_combo_reset_init(struct qmp_combo *qmp) { const struct qmp_phy_cfg *cfg = qmp->cfg; @@ -4117,6 +4100,84 @@ static struct phy *qmp_combo_phy_xlate(struct device *dev, const struct of_phand return ERR_PTR(-EINVAL); } +static void qmp_combo_find_lanes_orientation(const struct qmp_combo_lane_mapping *mapping, + unsigned int mapping_count, + u32 *lanes, unsigned int lanes_count, + enum typec_orientation *orientation) +{ + int i; + + for (i = 0; i < mapping_count; i++) { + if (mapping[i].lanes_count != lanes_count) + continue; + if (!memcmp(mapping[i].lanes, lanes, sizeof(u32) * lanes_count)) { + *orientation = mapping[i].orientation; + return; + } + } +} + +static int qmp_combo_get_dt_lanes_mapping(struct device *dev, unsigned int endpoint, + u32 *data_lanes, unsigned int max, + unsigned int *count) +{ + struct device_node *ep __free(device_node) = NULL; + int ret; + + ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, endpoint); + if (!ep) + return -EINVAL; + + ret = of_property_count_u32_elems(ep, "data-lanes"); + if (ret < 0) + return ret; + + *count = ret; + if (*count > max) + return -EINVAL; + + return of_property_read_u32_array(ep, "data-lanes", data_lanes, + min_t(unsigned int, *count, max)); +} + +static int qmp_combo_get_dt_dp_orientation(struct device *dev, + enum typec_orientation *orientation) +{ + unsigned int count; + u32 data_lanes[4]; + int ret; + + /* DP is described on the first endpoint of the first port */ + ret = qmp_combo_get_dt_lanes_mapping(dev, 0, data_lanes, 4, &count); + if (ret < 0) + return ret == -EINVAL ? 0 : ret; + + /* Search for a match and only update orientation if found */ + qmp_combo_find_lanes_orientation(dp_data_lanes, ARRAY_SIZE(dp_data_lanes), + data_lanes, count, orientation); + + return 0; +} + +static int qmp_combo_get_dt_usb3_orientation(struct device *dev, + enum typec_orientation *orientation) +{ + unsigned int count; + u32 data_lanes[2]; + int ret; + + /* USB3 is described on the second endpoint of the first port */ + ret = qmp_combo_get_dt_lanes_mapping(dev, 1, data_lanes, 2, &count); + if (ret < 0) + return ret == -EINVAL ? 0 : ret; + + /* Search for a match and only update orientation if found */ + qmp_combo_find_lanes_orientation(usb3_data_lanes, ARRAY_SIZE(usb3_data_lanes), + data_lanes, count, orientation); + + return 0; +} + static int qmp_combo_probe(struct platform_device *pdev) { struct qmp_combo *qmp; @@ -4144,7 +4205,8 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) return ret; - ret = qmp_combo_vreg_init(qmp); + ret = devm_regulator_bulk_get_const(dev, qmp->cfg->num_vregs, + qmp->cfg->vreg_list, &qmp->vregs); if (ret) return ret; @@ -4167,9 +4229,41 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) goto err_node_put; - ret = qmp_combo_typec_register(qmp); - if (ret) - goto err_node_put; + qmp->qmpphy_mode = QMPPHY_MODE_USB3DP; + + if (of_property_present(dev->of_node, "mode-switch") || + of_property_present(dev->of_node, "orientation-switch")) { + ret = qmp_combo_typec_register(qmp); + if (ret) + goto err_node_put; + } else { + enum typec_orientation dp_orientation = TYPEC_ORIENTATION_NONE; + enum typec_orientation usb3_orientation = TYPEC_ORIENTATION_NONE; + + ret = qmp_combo_get_dt_dp_orientation(dev, &dp_orientation); + if (ret) + goto err_node_put; + + ret = qmp_combo_get_dt_usb3_orientation(dev, &usb3_orientation); + if (ret) + goto err_node_put; + + if (dp_orientation == TYPEC_ORIENTATION_NONE && + usb3_orientation != TYPEC_ORIENTATION_NONE) { + qmp->qmpphy_mode = QMPPHY_MODE_USB3_ONLY; + qmp->orientation = usb3_orientation; + } else if (usb3_orientation == TYPEC_ORIENTATION_NONE && + dp_orientation != TYPEC_ORIENTATION_NONE) { + qmp->qmpphy_mode = QMPPHY_MODE_DP_ONLY; + qmp->orientation = dp_orientation; + } else if (dp_orientation != TYPEC_ORIENTATION_NONE && + dp_orientation == usb3_orientation) { + qmp->qmpphy_mode = QMPPHY_MODE_USB3DP; + qmp->orientation = dp_orientation; + } else { + dev_warn(dev, "unable to determine orientation & mode from data-lanes"); + } + } ret = drm_aux_bridge_register(dev); if (ret) @@ -4189,11 +4283,6 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) goto err_node_put; - /* - * The hw default is USB3_ONLY, but USB3+DP mode lets us more easily - * check both sub-blocks' init tables for blunders at probe time. - */ - qmp->qmpphy_mode = QMPPHY_MODE_USB3DP; qmp->usb_phy = devm_phy_create(dev, usb_np, &qmp_combo_usb_phy_ops); if (IS_ERR(qmp->usb_phy)) { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 62b1c845b627..86b1b7e2da86 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -100,6 +100,12 @@ static const unsigned int pciephy_v7_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V7_PCS_POWER_DOWN_CONTROL, }; +static const unsigned int pciephy_v8_50_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = QPHY_V8_50_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V8_50_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V8_50_PCS_POWER_DOWN_CONTROL, +}; + static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), @@ -3072,6 +3078,7 @@ struct qmp_pcie_offsets { u16 rx2; u16 txz; u16 rxz; + u16 txrxz; u16 ln_shrd; }; @@ -3356,6 +3363,12 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v6_30 = { .ln_shrd = 0x8000, }; +static const struct qmp_pcie_offsets qmp_pcie_offsets_v8_50 = { + .serdes = 0x8000, + .pcs = 0x9000, + .txrxz = 0xd000, +}; + static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .lanes = 1, @@ -4412,6 +4425,22 @@ static const struct qmp_phy_cfg qmp_v6_gen4x4_pciephy_cfg = { .phy_status = PHYSTATUS_4_20, }; +static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_cfg = { + .lanes = 4, + + .offsets = &qmp_pcie_offsets_v8_50, + + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + + .regs = pciephy_v8_50_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, +}; + static void qmp_pcie_init_port_b(struct qmp_pcie *qmp, const struct qmp_phy_cfg_tbls *tbls) { const struct qmp_phy_cfg *cfg = qmp->cfg; @@ -5163,6 +5192,9 @@ err_node_put: static const struct of_device_id qmp_pcie_of_match_table[] = { { + .compatible = "qcom,glymur-qmp-gen5x4-pcie-phy", + .data = &glymur_qmp_gen5x4_pciephy_cfg, + }, { .compatible = "qcom,ipq6018-qmp-pcie-phy", .data = &ipq6018_pciephy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h new file mode 100644 index 000000000000..325c127e8eb7 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_PCS_V8_50_H_ +#define QCOM_PHY_QMP_PCS_V8_50_H_ + +#define QPHY_V8_50_PCS_STATUS1 0x010 +#define QPHY_V8_50_PCS_START_CONTROL 0x05c +#define QPHY_V8_50_PCS_POWER_DOWN_CONTROL 0x64 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index f58c82b2dd23..da2a7ad2cdcc 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -58,6 +58,8 @@ #include "phy-qcom-qmp-pcs-v8.h" +#include "phy-qcom-qmp-pcs-v8_50.h" + /* QPHY_SW_RESET bit */ #define SW_RESET BIT(0) /* QPHY_POWER_DOWN_CONTROL */ |
