mbox series

[V5,0/7] Tegra XUSB charger detect support

Message ID 1595238948-20531-1-git-send-email-nkristam@nvidia.com
Headers show
Series Tegra XUSB charger detect support | expand

Message

Nagarjuna Kristam July 20, 2020, 9:55 a.m. UTC
This patch series adds charger detect support on XUSB hardware used in
Tegra210 and Tegra186 SoCs.

This patchset is composed with :
 - dt bindings of XUSB Pad Controller
 - Tegra XUSB device mode driver to add vbus_draw support 
 - Tegra PHY driver for charger detect support

Tests done:
 - Connect USB cable from ubuntu host to micro-B port of DUT to detect
   SDP_TYPE charger
 - Connect USB cable from external powered USB hub(which inturn connects
   to ubuntu host) to micro-B port of DUT to detect CDP_TYPE charger.
 - Connect USB cable from USB charger to micro-B port of DUT to detect
   DCP_TYPE charger.
DUT: Jetson-tx1, Jetson tx2.

V5:
 - Fixed kernel robot warnings to updated functions as static.
V4:
 - Added ACKed-by details for PHY driver.
 - Used BIT macro instead of (1 << index) usage as suggested by Chunfeng Yun.
V3:
 - Added ACKed-by details for PHY driver and DT changes.
 - Functions and its arguments are aligned.
 - Tabs are used for alignment of MACRO's
 - For vbus_draw USDC callback, usb_phy set_power error is propogated.
 - Fixed various comments given by thierry.
V2:
 - Added ACKed-by details for DT patches.
 - All patches rebased.

Nagarjuna Kristam (7):
  dt-bindings: phy: tegra-xusb: Add charger-detect property
  phy: tegra: xusb: Add support for UTMI pad power control
  phy: tegra: xusb: Add USB2 pad power control support for Tegra210
  phy: tegra: xusb: Add soc ops API to enable UTMI PAD protection
  phy: tegra: xusb: Add support for charger detect
  phy: tegra: xusb: Enable charger detect for Tegra186
  phy: tegra: xusb: Enable charger detect for Tegra210

 .../bindings/phy/nvidia,tegra124-xusb-padctl.txt   |   4 +
 drivers/phy/tegra/Makefile                         |   2 +-
 drivers/phy/tegra/cd.c                             | 283 +++++++++++++++++++++
 drivers/phy/tegra/xusb-tegra186.c                  |  92 +++++--
 drivers/phy/tegra/xusb-tegra210.c                  | 223 +++++++++++-----
 drivers/phy/tegra/xusb.c                           |  80 ++++++
 drivers/phy/tegra/xusb.h                           |  22 ++
 7 files changed, 621 insertions(+), 85 deletions(-)
 create mode 100644 drivers/phy/tegra/cd.c

Comments

Vinod Koul Aug. 23, 2020, 2:03 p.m. UTC | #1
On 20-07-20, 15:25, Nagarjuna Kristam wrote:
> Add USB2 pad power on and off API's for TEgra210 and provide its control
> via soc ops. It can be used by operations like charger detect to power on
> and off USB2 pad if needed.
> 
> Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com>
> Acked-by: Thierry Reding <treding@nvidia.com>
> ---
> V5:
>  - Made tegra210_usb2_pad_power_on() and tegra210_usb2_pad_power_down() static.
> ---
> V4:
>  - No changes
> ---
> V3:
>  - Added Acked-by updates to commit message.
> ---
> V2:
>  - Patch re-based.
> ---
>  drivers/phy/tegra/xusb-tegra210.c | 190 ++++++++++++++++++++++++++------------
>  1 file changed, 133 insertions(+), 57 deletions(-)
> 
> diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
> index 66bd461..2e5f71c 100644
> --- a/drivers/phy/tegra/xusb-tegra210.c
> +++ b/drivers/phy/tegra/xusb-tegra210.c
> @@ -994,6 +994,128 @@ static int tegra210_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl,
>  	return 0;
>  }
>  
> +static void tegra210_usb2_bias_pad_power_on(struct tegra_xusb_usb2_pad *pad)
> +{
> +	struct tegra_xusb_padctl *padctl = pad->base.padctl;
> +	u32 value;
> +
> +	if (pad->enable++ > 0)
> +		return;
> +
> +	dev_dbg(padctl->dev, "power on BIAS PAD & USB2 tracking\n");
> +
> +	if (clk_prepare_enable(pad->clk))
> +		dev_warn(padctl->dev, "failed to enable BIAS PAD & USB2 tracking\n");

do you want to proceed ahead even if clock is not enabled..?

> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
> +	value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
> +		    XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) |
> +		   (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK <<
> +		    XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT));
> +	value |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL <<
> +		  XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) |
> +		 (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL <<
> +		  XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT);

I recently found <linux/bitfield.h> I think this would look lot neater
if we use FIELD_PREP or u32_encode_bits() and you can drop shift defines

Since this is not new code but moved here, I will leave it upto you to
change this, either ways is fine by me.
Vinod Koul Aug. 31, 2020, 7:35 a.m. UTC | #2
On 20-07-20, 15:25, Nagarjuna Kristam wrote:
> When USB charger is enabled, UTMI PAD needs to be protected according
> to the direction and current level. Add support for the same on Tegra210
> and Tegra186.
> 
> Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com>
> Acked-by: Thierry Reding <treding@nvidia.com>
> ---
> V5:
>  - No changes.
> ---
> V4:
>  - Added Acked-by updates to commit message.
> ---
> V3:
>  - Alligned function and its arguments.
>  - Fixed other comments from Thierry.
> ---
> V2:
>  - Commit message coorected.
>  - Patch re-based.
> ---
>  drivers/phy/tegra/xusb-tegra186.c | 40 +++++++++++++++++++++++++++++++++++++++
>  drivers/phy/tegra/xusb-tegra210.c | 32 +++++++++++++++++++++++++++++++
>  drivers/phy/tegra/xusb.h          | 13 +++++++++++++
>  3 files changed, 85 insertions(+)
> 
> diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
> index f862254..59b78a7 100644
> --- a/drivers/phy/tegra/xusb-tegra186.c
> +++ b/drivers/phy/tegra/xusb-tegra186.c
> @@ -68,6 +68,13 @@
>  #define   PORTX_SPEED_SUPPORT_MASK		(0x3)
>  #define     PORT_SPEED_SUPPORT_GEN1		(0x0)
>  
> +#define USB2_BATTERY_CHRG_OTGPADX_CTL1(x)       (0x84 + (x) * 0x40)
> +#define  PD_VREG                                (1 << 6)
> +#define  VREG_LEV(x)                            (((x) & 0x3) << 7)
> +#define  VREG_DIR(x)                            (((x) & 0x3) << 11)

maybe FIELD_GET() would be better, avoids error with shifts

> +#define  VREG_DIR_IN                            VREG_DIR(1)
> +#define  VREG_DIR_OUT                           VREG_DIR(2)
> +
>  #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x)	(0x88 + (x) * 0x40)
>  #define  HS_CURR_LEVEL(x)			((x) & 0x3f)
>  #define  TERM_SEL				BIT(25)
> @@ -289,6 +296,37 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
>  	usb2->powered_on = false;
>  }
>  
> +static void
> +tegra186_xusb_padctl_utmi_pad_set_protection(struct tegra_xusb_port *port,
> +					     int level,
> +					     enum tegra_vbus_dir dir)
> +{
> +	u32 value;
> +	struct tegra_xusb_padctl *padctl = port->padctl;
> +	unsigned int index = port->index;
> +
> +	value = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
> +
> +	if (level < 0) {
> +		/* disable pad protection */
> +		value |= PD_VREG;
> +		value &= ~VREG_LEV(~0);
> +		value &= ~VREG_DIR(~0);
> +	} else {
> +		if (dir == TEGRA_VBUS_SOURCE)
> +			value |= VREG_DIR_OUT;
> +		else if (dir == TEGRA_VBUS_SINK)
> +			value |= VREG_DIR_IN;
> +
> +		value &= ~PD_VREG;
> +		value &= ~VREG_DIR(~0);
> +		value &= ~VREG_LEV(~0);
> +		value |= VREG_LEV(level);

This would look much cleaner with FIELD_PREP() or u32_encoded_bits()

> +	}
> +
> +	padctl_writel(padctl, value, USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
> +}
> +
>  static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
>  					       bool status)
>  {
> @@ -935,6 +973,8 @@ static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
>  	.vbus_override = tegra186_xusb_padctl_vbus_override,
>  	.utmi_pad_power_on = tegra_phy_xusb_utmi_pad_power_on,
>  	.utmi_pad_power_down = tegra_phy_xusb_utmi_pad_power_down,
> +	.utmi_pad_set_protection =
> +			tegra186_xusb_padctl_utmi_pad_set_protection,

single line please

>  };
>  
>  #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
> diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
> index 2e5f71c..3aff284 100644
> --- a/drivers/phy/tegra/xusb-tegra210.c
> +++ b/drivers/phy/tegra/xusb-tegra210.c
> @@ -74,6 +74,8 @@
>  #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK 0x3
>  #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_VAL 0x1
>  #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6)
> +#define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(x) (((x) & 0x3) << 7)
> +#define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_DIR(x) (((x) & 0x3) << 11)
>  
>  #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x088 + (x) * 0x40)
>  #define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 29)
> @@ -1116,6 +1118,34 @@ static void tegra210_usb2_pad_power_down(struct phy *phy)
>  	usb2->powered_on = false;
>  }
>  
> +static void
> +tegra210_xusb_padctl_utmi_pad_set_protection(struct tegra_xusb_port *port,
> +					     int level,
> +					     enum tegra_vbus_dir dir)

single line and aligned to preceeding parenthesis (hint checkpatch.pl
with --strict option tells you so)


> +{
> +	u32 value;
> +	struct tegra_xusb_padctl *padctl = port->padctl;
> +	unsigned int index = port->index;
> +
> +	value = padctl_readl(padctl,
> +			     XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));

Single line please

> +
> +	if (level < 0) {
> +		/* disable pad protection */
> +		value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18;
> +		value &= USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(~0);
> +		value &= ~USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_DIR(~0);
> +	} else {
> +		value &= ~XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18;
> +		value &= ~USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_DIR(~0);
> +		value &= USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(~0);
> +		value |= USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(level);
> +	}
> +
> +	padctl_writel(padctl, value,
> +		      XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
> +}
> +
>  static int tegra210_usb2_phy_set_mode(struct phy *phy, enum phy_mode mode,
>  				      int submode)
>  {
> @@ -2291,6 +2321,8 @@ static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = {
>  	.utmi_port_reset = tegra210_utmi_port_reset,
>  	.utmi_pad_power_on = tegra210_usb2_pad_power_on,
>  	.utmi_pad_power_down = tegra210_usb2_pad_power_down,
> +	.utmi_pad_set_protection =
> +			tegra210_xusb_padctl_utmi_pad_set_protection,

Here too

>  };
>  
>  static const char * const tegra210_xusb_padctl_supply_names[] = {
> diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
> index 6995fc4..475bcc6 100644
> --- a/drivers/phy/tegra/xusb.h
> +++ b/drivers/phy/tegra/xusb.h
> @@ -259,6 +259,17 @@ to_sata_pad(struct tegra_xusb_pad *pad)
>   */
>  struct tegra_xusb_port_ops;
>  
> +/*
> + * Tegra OTG port VBUS direction:
> + * default (based on port capability) or
> + * as source or sink
> + */
> +enum tegra_vbus_dir {
> +	TEGRA_VBUS_DEFAULT,
> +	TEGRA_VBUS_SOURCE,
> +	TEGRA_VBUS_SINK
> +};
> +
>  struct tegra_xusb_port {
>  	struct tegra_xusb_padctl *padctl;
>  	struct tegra_xusb_lane *lane;
> @@ -398,6 +409,8 @@ struct tegra_xusb_padctl_ops {
>  	int (*utmi_port_reset)(struct phy *phy);
>  	void (*utmi_pad_power_on)(struct phy *phy);
>  	void (*utmi_pad_power_down)(struct phy *phy);
> +	void (*utmi_pad_set_protection)(struct tegra_xusb_port *port,
> +					int level, enum tegra_vbus_dir dir);
>  };
>  
>  struct tegra_xusb_padctl_soc {
> -- 
> 2.7.4
Vinod Koul Aug. 31, 2020, 8:50 a.m. UTC | #3
On 20-07-20, 15:25, Nagarjuna Kristam wrote:

> +#define USB2_BATTERY_CHRG_OTGPADX_CTL0(x)	(0x80 + (x) * 0x40)
> +#define  PD_CHG					BIT(0)
> +#define  VDCD_DET_FILTER_EN			BIT(4)
> +#define  VDAT_DET				BIT(5)
> +#define  VDAT_DET_FILTER_EN			BIT(8)
> +#define  OP_SINK_EN				BIT(9)
> +#define  OP_SRC_EN				BIT(10)
> +#define  ON_SINK_EN				BIT(11)
> +#define  ON_SRC_EN				BIT(12)
> +#define  OP_I_SRC_EN				BIT(13)
> +#define  ZIP_FILTER_EN				BIT(21)
> +#define  ZIN_FILTER_EN				BIT(25)
> +#define  DCD_DETECTED				BIT(26)
> +
> +#define USB2_BATTERY_CHRG_OTGPADX_CTL1(x)	(0x84 + (x) * 0x40)
> +#define  PD_VREG				BIT(6)
> +#define  VREG_LEV(x)				(((x) & 0x3) << 7)
> +#define  VREG_DIR(x)				(((x) & 0x3) << 11)

GENMASK and FIELD_PREP please

> +static void
> +tegra_xusb_padctl_set_debounce_time(struct tegra_xusb_padctl *padctl,
> +				    u32 debounce)
> +{
> +	u32 value;
> +
> +	value = padctl_readl(padctl,
> +		XUSB_PADCTL_USB2_BATTERY_CHRG_TDCD_DBNC_TIMER_0);

Single line

> +static void
> +tegra_xusb_padctl_charger_detect_on(struct tegra_xusb_padctl *padctl, u32 index)
> +{
> +	u32 value;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
> +	value &= ~USB2_OTG_PD_ZI;
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
> +	value |= (USB2_OTG_PD2 | USB2_OTG_PD2_OVRD_EN);
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
> +
> +	value = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(index));
> +	value &= ~PD_CHG;
> +	padctl_writel(padctl, value, USB2_BATTERY_CHRG_OTGPADX_CTL0(index));

maybe a padctl_updatel() helper to do read, modify and write

> +static void tegra_xusb_padctl_dcd(struct tegra_xusb_padctl *padctl, u32 index)
> +{
> +	u32 value;
> +	unsigned int offset;
> +	bool ret = false;
> +
> +	/* Turn on IDP_SRC */
> +	value = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(index));
> +	value |= OP_I_SRC_EN;
> +	padctl_writel(padctl, value, USB2_BATTERY_CHRG_OTGPADX_CTL0(index));
> +
> +	/* Turn on D- pull-down resistor */
> +	value = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
> +	value |= USBON_RPD_OVRD_VAL;
> +	padctl_writel(padctl, value, USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
> +
> +	/* Wait for TDCD_DBNC (DCD debounce), refer to BC1.2 spec Table 5 */
> +	usleep_range(10000, 20000);
> +
> +	offset = USB2_BATTERY_CHRG_OTGPADX_CTL0(index);
> +	ret = readl_poll_timeout(padctl->regs + offset, value,
> +				 value & DCD_DETECTED, 20000,
> +				 TDCD_TIMEOUT_MS * 1000);
> +	if (!ret)
> +		dev_warn(padctl->dev, "%s: DCD timeout.", __func__);

Not dev_err() ? and continue after this?

> +static bool
> +tegra_xusb_padctl_primary_secondary(struct tegra_xusb_padctl *padctl, u32 index,
> +				    bool is_primary)
> +{
> +	u32 value;
> +	u32 config = is_primary ? (OP_SRC_EN | ON_SINK_EN) :
> +				  (ON_SRC_EN | OP_SINK_EN);
> +	bool ret = false;
> +
> +	if (is_primary)
> +		/* data contact detection */
> +		tegra_xusb_padctl_dcd(padctl, index);
> +
> +	/* Source D- to D+ */
> +	value = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(index));
> +	value |= config;
> +	padctl_writel(padctl, value, USB2_BATTERY_CHRG_OTGPADX_CTL0(index));
> +
> +	/*
> +	 * Wait for TVDPSRC_ON/TVDMSRC_ON(D+/- voltage source on time),
> +	 * refer to BC1.2 spec Table 5
> +	 */
> +	msleep(40);
> +
> +	value = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(index));
> +	if (is_primary)
> +		ret = !!(value & VDAT_DET);
> +	else
> +		ret = !(value & VDAT_DET);

How about 

        ret = !(value & VDAT_DET);
        if (is_primary)
                ret = !ret;

> +#define VON_DIV2P0_DET BIT(0)
> +#define VON_DIV2P7_DET BIT(1)
> +#define VOP_DIV2P0_DET BIT(2)
> +#define VOP_DIV2P7_DET BIT(3)
> +
> +#define VREG_CUR_LEVEL_0        500
> +#define VREG_CUR_LEVEL_1        900
> +#define VREG_CUR_LEVEL_2        1500
> +#define VREG_CUR_LEVEL_3        2000

tabs or spaces, pick one please, not both

> +
> +#define IS_CUR_IN_RANGE(ma, low, high)  \
> +	((ma >= VREG_CUR_LEVEL_##low) && (ma <= (VREG_CUR_LEVEL_##high - 1)))
> +#define VREG_LVL(ma, level)     IS_CUR_IN_RANGE(ma, level, level + 1)
> +
> +static void tegra_xusb_padctl_vbus_pad_portection(struct tegra_xusb_port *port)
> +{
> +	struct tegra_xusb_padctl *padctl = port->padctl;
> +	int level = 0;
> +	enum tegra_vbus_dir dir = TEGRA_VBUS_SINK;
> +	int max_ua, min_ua;

reverse christmas tree please

> +static enum usb_charger_type tegra_xusb_charger_detect(struct usb_phy *usb_phy)
> +{
> +	struct tegra_xusb_port *port = container_of(usb_phy,
> +						    struct tegra_xusb_port,
> +						    usb_phy);

how about:

        struct tegra_xusb_port *port = 
                        container_of(usb_phy, struct tegra_xusb_port, usb_phy);