diff mbox series

[v2,1/1] usb: host: tegra: implement dts based xcvr setup

Message ID 20230821111953.5919-2-clamor95@gmail.com
State Changes Requested
Delegated to: Marek Vasut
Headers show
Series tegra-usb: support dts based xcvr setup | expand

Commit Message

Svyatoslav Ryhel Aug. 21, 2023, 11:19 a.m. UTC
Some devices (like ASUS TF201) may not use fuse based xcvr setup
which will result in not working USB line. To fix this situation
allow xcvr setup to be performed from device tree values if
nvidia,xcvr-setup-use-fuses is not defined.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/usb/host/ehci-tegra.c | 48 +++++++++++++++++++++++++++++++----
 1 file changed, 43 insertions(+), 5 deletions(-)

Comments

Marek Vasut Aug. 21, 2023, 1:41 p.m. UTC | #1
On 8/21/23 13:19, Svyatoslav Ryhel wrote:
> Some devices (like ASUS TF201) may not use fuse based xcvr setup
> which will result in not working USB line. To fix this situation
> allow xcvr setup to be performed from device tree values if
> nvidia,xcvr-setup-use-fuses is not defined.
> 
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
>   drivers/usb/host/ehci-tegra.c | 48 +++++++++++++++++++++++++++++++----
>   1 file changed, 43 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
> index 2cf1625670..a9aee908f3 100644
> --- a/drivers/usb/host/ehci-tegra.c
> +++ b/drivers/usb/host/ehci-tegra.c
> @@ -81,6 +81,8 @@ struct fdt_usb {
>   	enum periph_id periph_id;/* peripheral id */
>   	struct gpio_desc vbus_gpio;	/* GPIO for vbus enable */
>   	struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */
> +	bool xcvr_setup_use_fuses; /* Indicates that the value is read from the on-chip fuses */
> +	u32 xcvr_setup;	/* Input of XCVR cell, HS driver output control */
>   };
>   
>   /*
> @@ -464,10 +466,16 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
>   
>   		/* Recommended PHY settings for EYE diagram */
>   		val = readl(&usbctlr->utmip_xcvr_cfg0);
> -		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
> -				0x4 << UTMIP_XCVR_SETUP_SHIFT);
> -		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
> -				0x3 << UTMIP_XCVR_SETUP_MSB_SHIFT);
> +
> +		if (!config->xcvr_setup_use_fuses) {
> +			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
> +					config->xcvr_setup <<
> +					UTMIP_XCVR_SETUP_SHIFT);
> +			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
> +					config->xcvr_setup <<
> +					UTMIP_XCVR_SETUP_MSB_SHIFT);
> +		}
> +
>   		clrsetbits_le32(&val, UTMIP_XCVR_HSSLEW_MSB_MASK,
>   				0x8 << UTMIP_XCVR_HSSLEW_MSB_SHIFT);
>   		writel(val, &usbctlr->utmip_xcvr_cfg0);
> @@ -522,7 +530,9 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
>   	setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG);
>   
>   	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE);
> -	setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
> +
> +	if (config->xcvr_setup_use_fuses)
> +		setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
>   
>   	/*
>   	 * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT
> @@ -843,6 +853,8 @@ static const struct ehci_ops tegra_ehci_ops = {
>   static int ehci_usb_of_to_plat(struct udevice *dev)
>   {
>   	struct fdt_usb *priv = dev_get_priv(dev);
> +	u32 usb_phy_phandle;
> +	ofnode usb_phy_node;
>   	int ret;
>   
>   	ret = fdt_decode_usb(dev, priv);
> @@ -851,6 +863,32 @@ static int ehci_usb_of_to_plat(struct udevice *dev)
>   
>   	priv->type = dev_get_driver_data(dev);
>   
> +	ret = ofnode_read_u32(dev_ofnode(dev), "nvidia,phy", &usb_phy_phandle);
> +	if (ret) {
> +		log_debug("%s: required usb phy node isn't provided\n", __func__);
> +		priv->xcvr_setup_use_fuses = true;
> +		return 0;
> +	}
> +
> +	usb_phy_node = ofnode_get_by_phandle(usb_phy_phandle);
> +	if (!ofnode_valid(usb_phy_node)) {
> +		log_err("%s: failed to find usb phy node\n", __func__);
> +		return -EINVAL;
> +	}

Can we get a simple PHY driver instead of this hackage please ?

See the generic PHY stuff in other ehci drivers for example of how to 
interact with PHY drivers, and drivers/phy/ for PHY driver examples.
Svyatoslav Ryhel Aug. 21, 2023, 2:06 p.m. UTC | #2
21 серпня 2023 р. 16:41:51 GMT+03:00, Marek Vasut <marex@denx.de> написав(-ла):
>On 8/21/23 13:19, Svyatoslav Ryhel wrote:
>> Some devices (like ASUS TF201) may not use fuse based xcvr setup
>> which will result in not working USB line. To fix this situation
>> allow xcvr setup to be performed from device tree values if
>> nvidia,xcvr-setup-use-fuses is not defined.
>> 
>> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
>> ---
>>   drivers/usb/host/ehci-tegra.c | 48 +++++++++++++++++++++++++++++++----
>>   1 file changed, 43 insertions(+), 5 deletions(-)
>> 
>> diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
>> index 2cf1625670..a9aee908f3 100644
>> --- a/drivers/usb/host/ehci-tegra.c
>> +++ b/drivers/usb/host/ehci-tegra.c
>> @@ -81,6 +81,8 @@ struct fdt_usb {
>>   	enum periph_id periph_id;/* peripheral id */
>>   	struct gpio_desc vbus_gpio;	/* GPIO for vbus enable */
>>   	struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */
>> +	bool xcvr_setup_use_fuses; /* Indicates that the value is read from the on-chip fuses */
>> +	u32 xcvr_setup;	/* Input of XCVR cell, HS driver output control */
>>   };
>>     /*
>> @@ -464,10 +466,16 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
>>     		/* Recommended PHY settings for EYE diagram */
>>   		val = readl(&usbctlr->utmip_xcvr_cfg0);
>> -		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
>> -				0x4 << UTMIP_XCVR_SETUP_SHIFT);
>> -		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
>> -				0x3 << UTMIP_XCVR_SETUP_MSB_SHIFT);
>> +
>> +		if (!config->xcvr_setup_use_fuses) {
>> +			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
>> +					config->xcvr_setup <<
>> +					UTMIP_XCVR_SETUP_SHIFT);
>> +			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
>> +					config->xcvr_setup <<
>> +					UTMIP_XCVR_SETUP_MSB_SHIFT);
>> +		}
>> +
>>   		clrsetbits_le32(&val, UTMIP_XCVR_HSSLEW_MSB_MASK,
>>   				0x8 << UTMIP_XCVR_HSSLEW_MSB_SHIFT);
>>   		writel(val, &usbctlr->utmip_xcvr_cfg0);
>> @@ -522,7 +530,9 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
>>   	setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG);
>>     	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE);
>> -	setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
>> +
>> +	if (config->xcvr_setup_use_fuses)
>> +		setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
>>     	/*
>>   	 * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT
>> @@ -843,6 +853,8 @@ static const struct ehci_ops tegra_ehci_ops = {
>>   static int ehci_usb_of_to_plat(struct udevice *dev)
>>   {
>>   	struct fdt_usb *priv = dev_get_priv(dev);
>> +	u32 usb_phy_phandle;
>> +	ofnode usb_phy_node;
>>   	int ret;
>>     	ret = fdt_decode_usb(dev, priv);
>> @@ -851,6 +863,32 @@ static int ehci_usb_of_to_plat(struct udevice *dev)
>>     	priv->type = dev_get_driver_data(dev);
>>   +	ret = ofnode_read_u32(dev_ofnode(dev), "nvidia,phy", &usb_phy_phandle);
>> +	if (ret) {
>> +		log_debug("%s: required usb phy node isn't provided\n", __func__);
>> +		priv->xcvr_setup_use_fuses = true;
>> +		return 0;
>> +	}
>> +
>> +	usb_phy_node = ofnode_get_by_phandle(usb_phy_phandle);
>> +	if (!ofnode_valid(usb_phy_node)) {
>> +		log_err("%s: failed to find usb phy node\n", __func__);
>> +		return -EINVAL;
>> +	}
>
>Can we get a simple PHY driver instead of this hackage please ?
>
>See the generic PHY stuff in other ehci drivers for example of how to interact with PHY drivers, and drivers/phy/ for PHY driver examples.

No we can't. As I said, I will not start a split of tegra-echi to chipidea controller and phy because of this small tweak. It seems that you don't understand what your are requesting. Sorry for bothering.
Marek Vasut Aug. 21, 2023, 11:02 p.m. UTC | #3
On 8/21/23 16:06, Svyatoslav Ryhel wrote:
> 21 серпня 2023 р. 16:41:51 GMT+03:00, Marek Vasut <marex@denx.de> написав(-ла):
>> On 8/21/23 13:19, Svyatoslav Ryhel wrote:
>>> Some devices (like ASUS TF201) may not use fuse based xcvr setup
>>> which will result in not working USB line. To fix this situation
>>> allow xcvr setup to be performed from device tree values if
>>> nvidia,xcvr-setup-use-fuses is not defined.
>>>
>>> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
>>> ---
>>>    drivers/usb/host/ehci-tegra.c | 48 +++++++++++++++++++++++++++++++----
>>>    1 file changed, 43 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
>>> index 2cf1625670..a9aee908f3 100644
>>> --- a/drivers/usb/host/ehci-tegra.c
>>> +++ b/drivers/usb/host/ehci-tegra.c
>>> @@ -81,6 +81,8 @@ struct fdt_usb {
>>>    	enum periph_id periph_id;/* peripheral id */
>>>    	struct gpio_desc vbus_gpio;	/* GPIO for vbus enable */
>>>    	struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */
>>> +	bool xcvr_setup_use_fuses; /* Indicates that the value is read from the on-chip fuses */
>>> +	u32 xcvr_setup;	/* Input of XCVR cell, HS driver output control */
>>>    };
>>>      /*
>>> @@ -464,10 +466,16 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
>>>      		/* Recommended PHY settings for EYE diagram */
>>>    		val = readl(&usbctlr->utmip_xcvr_cfg0);
>>> -		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
>>> -				0x4 << UTMIP_XCVR_SETUP_SHIFT);
>>> -		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
>>> -				0x3 << UTMIP_XCVR_SETUP_MSB_SHIFT);
>>> +
>>> +		if (!config->xcvr_setup_use_fuses) {
>>> +			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
>>> +					config->xcvr_setup <<
>>> +					UTMIP_XCVR_SETUP_SHIFT);
>>> +			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
>>> +					config->xcvr_setup <<
>>> +					UTMIP_XCVR_SETUP_MSB_SHIFT);
>>> +		}
>>> +
>>>    		clrsetbits_le32(&val, UTMIP_XCVR_HSSLEW_MSB_MASK,
>>>    				0x8 << UTMIP_XCVR_HSSLEW_MSB_SHIFT);
>>>    		writel(val, &usbctlr->utmip_xcvr_cfg0);
>>> @@ -522,7 +530,9 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
>>>    	setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG);
>>>      	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE);
>>> -	setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
>>> +
>>> +	if (config->xcvr_setup_use_fuses)
>>> +		setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
>>>      	/*
>>>    	 * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT
>>> @@ -843,6 +853,8 @@ static const struct ehci_ops tegra_ehci_ops = {
>>>    static int ehci_usb_of_to_plat(struct udevice *dev)
>>>    {
>>>    	struct fdt_usb *priv = dev_get_priv(dev);
>>> +	u32 usb_phy_phandle;
>>> +	ofnode usb_phy_node;
>>>    	int ret;
>>>      	ret = fdt_decode_usb(dev, priv);
>>> @@ -851,6 +863,32 @@ static int ehci_usb_of_to_plat(struct udevice *dev)
>>>      	priv->type = dev_get_driver_data(dev);
>>>    +	ret = ofnode_read_u32(dev_ofnode(dev), "nvidia,phy", &usb_phy_phandle);
>>> +	if (ret) {
>>> +		log_debug("%s: required usb phy node isn't provided\n", __func__);
>>> +		priv->xcvr_setup_use_fuses = true;
>>> +		return 0;
>>> +	}
>>> +
>>> +	usb_phy_node = ofnode_get_by_phandle(usb_phy_phandle);
>>> +	if (!ofnode_valid(usb_phy_node)) {
>>> +		log_err("%s: failed to find usb phy node\n", __func__);
>>> +		return -EINVAL;
>>> +	}
>>
>> Can we get a simple PHY driver instead of this hackage please ?
>>
>> See the generic PHY stuff in other ehci drivers for example of how to interact with PHY drivers, and drivers/phy/ for PHY driver examples.
> 
> No we can't. As I said, I will not start a split of tegra-echi to chipidea controller and phy because of this small tweak. It seems that you don't understand what your are requesting. Sorry for bothering.

Since the i.MX EHCI controller is also ChipIdea HDRC and already uses a 
PHY driver, I think the split is perfectly doable.
diff mbox series

Patch

diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 2cf1625670..a9aee908f3 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -81,6 +81,8 @@  struct fdt_usb {
 	enum periph_id periph_id;/* peripheral id */
 	struct gpio_desc vbus_gpio;	/* GPIO for vbus enable */
 	struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */
+	bool xcvr_setup_use_fuses; /* Indicates that the value is read from the on-chip fuses */
+	u32 xcvr_setup;	/* Input of XCVR cell, HS driver output control */
 };
 
 /*
@@ -464,10 +466,16 @@  static int init_utmi_usb_controller(struct fdt_usb *config,
 
 		/* Recommended PHY settings for EYE diagram */
 		val = readl(&usbctlr->utmip_xcvr_cfg0);
-		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
-				0x4 << UTMIP_XCVR_SETUP_SHIFT);
-		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
-				0x3 << UTMIP_XCVR_SETUP_MSB_SHIFT);
+
+		if (!config->xcvr_setup_use_fuses) {
+			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
+					config->xcvr_setup <<
+					UTMIP_XCVR_SETUP_SHIFT);
+			clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
+					config->xcvr_setup <<
+					UTMIP_XCVR_SETUP_MSB_SHIFT);
+		}
+
 		clrsetbits_le32(&val, UTMIP_XCVR_HSSLEW_MSB_MASK,
 				0x8 << UTMIP_XCVR_HSSLEW_MSB_SHIFT);
 		writel(val, &usbctlr->utmip_xcvr_cfg0);
@@ -522,7 +530,9 @@  static int init_utmi_usb_controller(struct fdt_usb *config,
 	setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG);
 
 	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE);
-	setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
+
+	if (config->xcvr_setup_use_fuses)
+		setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
 
 	/*
 	 * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT
@@ -843,6 +853,8 @@  static const struct ehci_ops tegra_ehci_ops = {
 static int ehci_usb_of_to_plat(struct udevice *dev)
 {
 	struct fdt_usb *priv = dev_get_priv(dev);
+	u32 usb_phy_phandle;
+	ofnode usb_phy_node;
 	int ret;
 
 	ret = fdt_decode_usb(dev, priv);
@@ -851,6 +863,32 @@  static int ehci_usb_of_to_plat(struct udevice *dev)
 
 	priv->type = dev_get_driver_data(dev);
 
+	ret = ofnode_read_u32(dev_ofnode(dev), "nvidia,phy", &usb_phy_phandle);
+	if (ret) {
+		log_debug("%s: required usb phy node isn't provided\n", __func__);
+		priv->xcvr_setup_use_fuses = true;
+		return 0;
+	}
+
+	usb_phy_node = ofnode_get_by_phandle(usb_phy_phandle);
+	if (!ofnode_valid(usb_phy_node)) {
+		log_err("%s: failed to find usb phy node\n", __func__);
+		return -EINVAL;
+	}
+
+	priv->xcvr_setup_use_fuses = ofnode_read_bool(
+		usb_phy_node, "nvidia,xcvr-setup-use-fuses");
+
+	if (!priv->xcvr_setup_use_fuses) {
+		ret = ofnode_read_u32(usb_phy_node, "nvidia,xcvr-setup",
+				      &priv->xcvr_setup);
+		if (ret)
+			return ret;
+	}
+
+	log_debug("%s: xcvr_setup_use_fuses %d, xcvr_setup %d\n",
+		  __func__, priv->xcvr_setup_use_fuses, priv->xcvr_setup);
+
 	return 0;
 }