Commit e497a24d authored by Tuomas Tynkkynen's avatar Tuomas Tynkkynen Committed by Felipe Balbi

usb: phy: tegra: Program new PHY parameters

The Tegra30 TRM recommends configuration of certain PHY parameters for
optimal quality. Program the following registers based on device tree
parameters:

- UTMIP_XCVR_HSSLEW: HS slew rate control.
- UTMIP_HSSQUELCH_LEVEL: HS squelch detector level
- UTMIP_HSDISCON_LEVEL: HS disconnect detector level.

These registers exist in Tegra20, but programming them hasn't been
necessary, so these parameters won't be set on Tegra20 to keep the
device trees backward compatible.

Additionally, the UTMIP_XCVR_SETUP parameter can be set from fuses
instead of a software-programmed value, as the optimal value can
vary between invidual boards. The boolean property
nvidia,xcvr-setup-use-fuses can be used to enable this behaviour.
Signed-off-by: default avatarTuomas Tynkkynen <ttynkkynen@nvidia.com>
Tested-by: default avatarStephen Warren <swarren@nvidia.com>
Reviewed-by: default avatarStephen Warren <swarren@nvidia.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 91e66700
...@@ -99,11 +99,15 @@ ...@@ -99,11 +99,15 @@
#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) #define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) #define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
#define UTMIP_XCVR_LSBIAS_SEL (1 << 21) #define UTMIP_XCVR_LSBIAS_SEL (1 << 21)
#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) #define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4)
#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25)
#define UTMIP_BIAS_CFG0 0x80c #define UTMIP_BIAS_CFG0 0x80c
#define UTMIP_OTGPD (1 << 11) #define UTMIP_OTGPD (1 << 11)
#define UTMIP_BIASPD (1 << 10) #define UTMIP_BIASPD (1 << 10)
#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0)
#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2)
#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24)
#define UTMIP_HSRX_CFG0 0x810 #define UTMIP_HSRX_CFG0 0x810
#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) #define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
...@@ -255,6 +259,7 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) ...@@ -255,6 +259,7 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy)
{ {
unsigned long val, flags; unsigned long val, flags;
void __iomem *base = phy->pad_regs; void __iomem *base = phy->pad_regs;
struct tegra_utmip_config *config = phy->config;
clk_prepare_enable(phy->pad_clk); clk_prepare_enable(phy->pad_clk);
...@@ -263,6 +268,16 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) ...@@ -263,6 +268,16 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy)
if (utmip_pad_count++ == 0) { if (utmip_pad_count++ == 0) {
val = readl(base + UTMIP_BIAS_CFG0); val = readl(base + UTMIP_BIAS_CFG0);
val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
if (phy->soc_config->requires_extra_tuning_parameters) {
val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) |
UTMIP_HSDISCON_LEVEL(~0) |
UTMIP_HSDISCON_LEVEL_MSB(~0));
val |= UTMIP_HSSQUELCH_LEVEL(config->hssquelch_level);
val |= UTMIP_HSDISCON_LEVEL(config->hsdiscon_level);
val |= UTMIP_HSDISCON_LEVEL_MSB(config->hsdiscon_level);
}
writel(val, base + UTMIP_BIAS_CFG0); writel(val, base + UTMIP_BIAS_CFG0);
} }
...@@ -431,12 +446,20 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) ...@@ -431,12 +446,20 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL | UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL |
UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) | UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) |
UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0));
UTMIP_XCVR_HSSLEW_MSB(~0));
val |= UTMIP_XCVR_SETUP(config->xcvr_setup); if (!config->xcvr_setup_use_fuses) {
val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup); val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup);
}
val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
if (phy->soc_config->requires_extra_tuning_parameters) {
val &= ~(UTMIP_XCVR_HSSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0));
val |= UTMIP_XCVR_HSSLEW(config->xcvr_hsslew);
val |= UTMIP_XCVR_HSSLEW_MSB(config->xcvr_hsslew);
}
writel(val, base + UTMIP_XCVR_CFG0); writel(val, base + UTMIP_XCVR_CFG0);
val = readl(base + UTMIP_XCVR_CFG1); val = readl(base + UTMIP_XCVR_CFG1);
...@@ -450,14 +473,14 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) ...@@ -450,14 +473,14 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val |= UTMIP_BIAS_PDTRK_COUNT(0x5); val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
writel(val, base + UTMIP_BIAS_CFG1); writel(val, base + UTMIP_BIAS_CFG1);
if (phy->is_legacy_phy) { val = readl(base + UTMIP_SPARE_CFG0);
val = readl(base + UTMIP_SPARE_CFG0); if (config->xcvr_setup_use_fuses)
if (phy->mode == USB_DR_MODE_PERIPHERAL) val |= FUSE_SETUP_SEL;
val &= ~FUSE_SETUP_SEL; else
else val &= ~FUSE_SETUP_SEL;
val |= FUSE_SETUP_SEL; writel(val, base + UTMIP_SPARE_CFG0);
writel(val, base + UTMIP_SPARE_CFG0);
} else { if (!phy->is_legacy_phy) {
val = readl(base + USB_SUSP_CTRL); val = readl(base + USB_SUSP_CTRL);
val |= UTMIP_PHY_ENABLE; val |= UTMIP_PHY_ENABLE;
writel(val, base + USB_SUSP_CTRL); writel(val, base + USB_SUSP_CTRL);
...@@ -888,11 +911,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, ...@@ -888,11 +911,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
if (err < 0) if (err < 0)
return err; return err;
err = read_utmi_param(pdev, "nvidia,xcvr-setup",
&config->xcvr_setup);
if (err < 0)
return err;
err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew", err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew",
&config->xcvr_lsfslew); &config->xcvr_lsfslew);
if (err < 0) if (err < 0)
...@@ -903,6 +921,33 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, ...@@ -903,6 +921,33 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
if (err < 0) if (err < 0)
return err; return err;
if (tegra_phy->soc_config->requires_extra_tuning_parameters) {
err = read_utmi_param(pdev, "nvidia,xcvr-hsslew",
&config->xcvr_hsslew);
if (err < 0)
return err;
err = read_utmi_param(pdev, "nvidia,hssquelch-level",
&config->hssquelch_level);
if (err < 0)
return err;
err = read_utmi_param(pdev, "nvidia,hsdiscon-level",
&config->hsdiscon_level);
if (err < 0)
return err;
}
config->xcvr_setup_use_fuses = of_property_read_bool(
pdev->dev.of_node, "nvidia,xcvr-setup-use-fuses");
if (!config->xcvr_setup_use_fuses) {
err = read_utmi_param(pdev, "nvidia,xcvr-setup",
&config->xcvr_setup);
if (err < 0)
return err;
}
return 0; return 0;
} }
......
...@@ -41,9 +41,13 @@ struct tegra_utmip_config { ...@@ -41,9 +41,13 @@ struct tegra_utmip_config {
u8 elastic_limit; u8 elastic_limit;
u8 idle_wait_delay; u8 idle_wait_delay;
u8 term_range_adj; u8 term_range_adj;
bool xcvr_setup_use_fuses;
u8 xcvr_setup; u8 xcvr_setup;
u8 xcvr_lsfslew; u8 xcvr_lsfslew;
u8 xcvr_lsrslew; u8 xcvr_lsrslew;
u8 xcvr_hsslew;
u8 hssquelch_level;
u8 hsdiscon_level;
}; };
enum tegra_usb_phy_port_speed { enum tegra_usb_phy_port_speed {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment