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

Krishna Kurapati PSSNV quic_kriskura at quicinc.com
Fri Jun 17 04:53:35 PDT 2022


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 at quicinc.com>
>> Reviewed-by: Pavankumar Kondeti <quic_pkondeti at quicinc.com>
>> ---
>>   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 at lists.infradead.org
>> https://lists.infradead.org/mailman/listinfo/linux-phy



More information about the linux-phy mailing list