Re: [v9 2/3] phy: qcom-snps: Add support for overriding phy tuning parameters

From: Krishna Kurapati PSSNV
Date: Fri Jun 17 2022 - 07:53:54 EST



On 6/17/2022 6:01 AM, Vinod Koul wrote:
On 13-06-22, 10:17, Krishna Kurapati wrote:
Add support for overriding electrical signal tuning parameters for
SNPS HS Phy.

Signed-off-by: Krishna Kurapati <quic_kriskura@xxxxxxxxxxx>
Reviewed-by: Pavankumar Kondeti <quic_pkondeti@xxxxxxxxxxx>
---
drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 260 +++++++++++++++++++++++++-
1 file changed, 258 insertions(+), 2 deletions(-)

diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
index 5d20378..a002e90 100644
--- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
+++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
@@ -52,6 +52,12 @@
#define USB2_SUSPEND_N BIT(2)
#define USB2_SUSPEND_N_SEL BIT(3)
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0 (0x6c)
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1 (0x70)
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2 (0x74)
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3 (0x78)
+#define PARAM_OVRD_MASK 0xFF
+
#define USB2_PHY_USB_PHY_CFG0 (0x94)
#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0)
#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1)
@@ -60,12 +66,69 @@
#define REFCLK_SEL_MASK GENMASK(1, 0)
#define REFCLK_SEL_DEFAULT (0x2 << 0)
+#define HS_DISCONNECT_MASK GENMASK(2, 0)
+#define SQUELCH_DETECTOR_MASK GENMASK(7, 5)
+
+#define HS_AMPLITUDE_MASK GENMASK(3, 0)
+#define PREEMPHASIS_DURATION_MASK BIT(5)
+#define PREEMPHASIS_AMPLITUDE_MASK GENMASK(7, 6)
+
+#define HS_RISE_FALL_MASK GENMASK(1, 0)
+#define HS_CROSSOVER_VOLTAGE_MASK GENMASK(3, 2)
+#define HS_OUTPUT_IMPEDANCE_MASK GENMASK(5, 4)
+
+#define LS_FS_OUTPUT_IMPEDANCE_MASK GENMASK(3, 0)
+
static const char * const qcom_snps_hsphy_vreg_names[] = {
"vdda-pll", "vdda33", "vdda18",
};
#define SNPS_HS_NUM_VREGS ARRAY_SIZE(qcom_snps_hsphy_vreg_names)
+struct override_param {
+ s32 value;
+ u8 reg;
+};
+
+#define OVERRIDE_PARAM(bps, val) { \
+ .value = bps, \
+ .reg = val, \
+}
+
+struct override_param_map {
+ const struct override_param *param_table;
+ u8 table_size;
+ u8 reg_offset;
+ u8 param_mask;
+};
+
+#define OVERRIDE_PARAM_MAP(table, num_elements, offset, mask) \
+{ \
+ .param_table = table, \
+ .table_size = num_elements, \
+ .reg_offset = offset, \
+ .param_mask = mask, \
+}
+
+struct phy_override_seq {
+ bool need_update;
+ u8 offset;
+ u8 value;
+ u8 mask;
+};
You should add table for values to register lookups and define masks for
each register type...

Lets not add tables like this please
Hi Vinod,

The structure phy_override_seqis to keep track of what parameters need an update to tune the Phy. It is not for storing bitmask and register addresses. The address offsets for registers and bitmasks in these register for all parameters are defined in terms of GENMASK/BIT above. And the look up tables are defined as OVERRIDE_PARAM_MAP[] with suffix _sc7280.

During probe, we parse the DT and see what all parameters need to be updated in the Phy.  We have added an array of phy_override_seq in qcom_snps_hsphy (one element each for every phy tune parameter). While checking for each parameter in DT, if it is defined, we get the DT value, go through the look up tables as provided by sc7280_idp[] and find out which register and bitmask to be modified and cache it in this structure and mark the need_update flag to be true.

Once probe call is done, the array update_seq_cfg[ARRAY_SIZE(phy_seq_props)] will be holding data regarding the bitmasks and respective registers that need an update. The actual register update is done in qcom_snps_hsphy_init call.

Regards,
Krishna,
+
+static const char * const phy_seq_props[] = {
+ "qcom,hs-disconnect-bp",
+ "qcom,squelch-detector-bp",
+ "qcom,hs-amplitude-bp",
+ "qcom,pre-emphasis-duration-bp",
+ "qcom,pre-emphasis-amplitude-bp",
+ "qcom,hs-rise-fall-time-bp",
+ "qcom,hs-crossover-voltage-microvolt",
+ "qcom,hs-output-impedance-micro-ohms",
+ "qcom,ls-fs-output-impedance-bp",
+};
+
/**
* struct qcom_snps_hsphy - snps hs phy attributes
*
@@ -91,6 +154,7 @@ struct qcom_snps_hsphy {
bool phy_initialized;
enum phy_mode mode;
+ struct phy_override_seq update_seq_cfg[ARRAY_SIZE(phy_seq_props)];
};
static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
@@ -173,10 +237,147 @@ static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
return 0;
}
+static const struct override_param hs_disconnect_sc7280[] = {
+ OVERRIDE_PARAM(-272, 0),
+ OVERRIDE_PARAM(0, 1),
+ OVERRIDE_PARAM(317, 2),
+ OVERRIDE_PARAM(630, 3),
+ OVERRIDE_PARAM(973, 4),
+ OVERRIDE_PARAM(1332, 5),
+ OVERRIDE_PARAM(1743, 6),
+ OVERRIDE_PARAM(2156, 7),
+};
+
+static const struct override_param squelch_det_threshold_sc7280[] = {
+ OVERRIDE_PARAM(-2090, 7),
+ OVERRIDE_PARAM(-1560, 6),
+ OVERRIDE_PARAM(-1030, 5),
+ OVERRIDE_PARAM(-530, 4),
+ OVERRIDE_PARAM(0, 3),
+ OVERRIDE_PARAM(530, 2),
+ OVERRIDE_PARAM(1060, 1),
+ OVERRIDE_PARAM(1590, 0),
+};
+
+static const struct override_param hs_amplitude_sc7280[] = {
+ OVERRIDE_PARAM(-660, 0),
+ OVERRIDE_PARAM(-440, 1),
+ OVERRIDE_PARAM(-220, 2),
+ OVERRIDE_PARAM(0, 3),
+ OVERRIDE_PARAM(230, 4),
+ OVERRIDE_PARAM(440, 5),
+ OVERRIDE_PARAM(650, 6),
+ OVERRIDE_PARAM(890, 7),
+ OVERRIDE_PARAM(1110, 8),
+ OVERRIDE_PARAM(1330, 9),
+ OVERRIDE_PARAM(1560, 10),
+ OVERRIDE_PARAM(1780, 11),
+ OVERRIDE_PARAM(2000, 12),
+ OVERRIDE_PARAM(2220, 13),
+ OVERRIDE_PARAM(2430, 14),
+ OVERRIDE_PARAM(2670, 15),
+};
+
+static const struct override_param preemphasis_duration_sc7280[] = {
+ OVERRIDE_PARAM(10000, 1),
+ OVERRIDE_PARAM(20000, 0),
+};
+
+static const struct override_param preemphasis_amplitude_sc7280[] = {
+ OVERRIDE_PARAM(10000, 1),
+ OVERRIDE_PARAM(20000, 2),
+ OVERRIDE_PARAM(30000, 3),
+ OVERRIDE_PARAM(40000, 0),
+};
+
+static const struct override_param hs_rise_fall_time_sc7280[] = {
+ OVERRIDE_PARAM(-4100, 3),
+ OVERRIDE_PARAM(0, 2),
+ OVERRIDE_PARAM(2810, 1),
+ OVERRIDE_PARAM(5430, 0),
+};
+
+static const struct override_param hs_crossover_voltage_sc7280[] = {
+ OVERRIDE_PARAM(-31000, 1),
+ OVERRIDE_PARAM(0, 3),
+ OVERRIDE_PARAM(28000, 2),
+};
+
+static const struct override_param hs_output_impedance_sc7280[] = {
+ OVERRIDE_PARAM(-2300000, 3),
+ OVERRIDE_PARAM(0, 2),
+ OVERRIDE_PARAM(2600000, 1),
+ OVERRIDE_PARAM(6100000, 0),
+};
+
+static const struct override_param ls_fs_output_impedance_sc7280[] = {
+ OVERRIDE_PARAM(-1053, 15),
+ OVERRIDE_PARAM(-557, 7),
+ OVERRIDE_PARAM(0, 3),
+ OVERRIDE_PARAM(612, 1),
+ OVERRIDE_PARAM(1310, 0),
+};
+
+static const struct override_param_map sc7280_idp[] = {
+ OVERRIDE_PARAM_MAP(
+ hs_disconnect_sc7280,
+ ARRAY_SIZE(hs_disconnect_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,
+ HS_DISCONNECT_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ squelch_det_threshold_sc7280,
+ ARRAY_SIZE(squelch_det_threshold_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,
+ SQUELCH_DETECTOR_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ hs_amplitude_sc7280,
+ ARRAY_SIZE(hs_amplitude_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
+ HS_AMPLITUDE_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ preemphasis_duration_sc7280,
+ ARRAY_SIZE(preemphasis_duration_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
+ PREEMPHASIS_DURATION_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ preemphasis_amplitude_sc7280,
+ ARRAY_SIZE(preemphasis_amplitude_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
+ PREEMPHASIS_AMPLITUDE_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ hs_rise_fall_time_sc7280,
+ ARRAY_SIZE(hs_rise_fall_time_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
+ HS_RISE_FALL_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ hs_crossover_voltage_sc7280,
+ ARRAY_SIZE(hs_crossover_voltage_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
+ HS_CROSSOVER_VOLTAGE_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ hs_output_impedance_sc7280,
+ ARRAY_SIZE(hs_output_impedance_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
+ HS_OUTPUT_IMPEDANCE_MASK),
+
+ OVERRIDE_PARAM_MAP(
+ ls_fs_output_impedance_sc7280,
+ ARRAY_SIZE(ls_fs_output_impedance_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3,
+ LS_FS_OUTPUT_IMPEDANCE_MASK),
+};
+
static int qcom_snps_hsphy_init(struct phy *phy)
{
struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
- int ret;
+ int ret, i;
dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__);
@@ -223,6 +424,14 @@ static int qcom_snps_hsphy_init(struct phy *phy)
qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
VBUSVLDEXT0, VBUSVLDEXT0);
+ for (i = 0; i < ARRAY_SIZE(hsphy->update_seq_cfg); i++) {
+ if (hsphy->update_seq_cfg[i].need_update)
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ hsphy->update_seq_cfg[i].offset,
+ hsphy->update_seq_cfg[i].mask,
+ hsphy->update_seq_cfg[i].value);
+ }
+
qcom_snps_hsphy_write_mask(hsphy->base,
USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
VREGBYPASS, VREGBYPASS);
@@ -280,7 +489,10 @@ static const struct phy_ops qcom_snps_hsphy_gen_ops = {
static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {
{ .compatible = "qcom,sm8150-usb-hs-phy", },
{ .compatible = "qcom,usb-snps-hs-5nm-phy", },
- { .compatible = "qcom,usb-snps-hs-7nm-phy", },
+ {
+ .compatible = "qcom,usb-snps-hs-7nm-phy",
+ .data = &sc7280_idp,
+ },
{ .compatible = "qcom,usb-snps-femto-v2-phy", },
{ }
};
@@ -291,6 +503,49 @@ static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = {
qcom_snps_hsphy_runtime_resume, NULL)
};
+static void qcom_snps_hsphy_override_param_update_val(
+ const struct override_param_map map,
+ s32 dt_val, struct phy_override_seq *seq_entry)
+{
+ int i;
+
+ /*
+ * Param table for each param is in increasing order
+ * of dt values. We need to iterate over the list to
+ * select the entry that has equal or the next highest value.
+ */
+ for (i = 0; i < map.table_size - 1; i++) {
+ if (map.param_table[i].value >= dt_val)
+ break;
+ }
+
+ seq_entry->need_update = true;
+ seq_entry->offset = map.reg_offset;
+ seq_entry->mask = map.param_mask;
+ seq_entry->value = map.param_table[i].reg << __ffs(map.param_mask);
+}
+
+static void qcom_snps_hsphy_read_override_param_seq(struct device *dev)
+{
+ struct device_node *node = dev->of_node;
+ s32 val;
+ int ret, i;
+ struct qcom_snps_hsphy *hsphy;
+ const struct override_param_map *cfg = of_device_get_match_data(dev);
+
+ hsphy = dev_get_drvdata(dev);
+
+ for (i = 0; i < ARRAY_SIZE(phy_seq_props); i++) {
+ ret = of_property_read_s32(node, phy_seq_props[i], &val);
+ if (!ret) {
+ dev_dbg(&hsphy->phy->dev, "Read param: %s val: %d\n",
+ phy_seq_props[i], val);
+ qcom_snps_hsphy_override_param_update_val(cfg[i], val,
+ &hsphy->update_seq_cfg[i]);
+ }
+ }
+}
+
static int qcom_snps_hsphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -352,6 +607,7 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, hsphy);
phy_set_drvdata(generic_phy, hsphy);
+ qcom_snps_hsphy_read_override_param_seq(dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (!IS_ERR(phy_provider))
--
2.7.4


--
linux-phy mailing list
linux-phy@xxxxxxxxxxxxxxxxxxx
https://lists.infradead.org/mailman/listinfo/linux-phy