From nobody Sat Feb 7 17:19:45 2026 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 06B322EA753 for ; Wed, 8 Oct 2025 06:00:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903216; cv=none; b=Jdu2ryrhMsXvEAc7NXKWgUE+EANIjAn2TWE2Z13USnWmQ8S6r9dkpPB7lgW6YU9C6zNLOKE0rYPLOyQHlUXxwDRCjRandVoZ/QNZkaInTjukWIT8XDkzCVkxLCTl8NWxV3KRo36jG6bRFZ8Y9LG8yPSckRQET9kdoctpjlFqgP8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903216; c=relaxed/simple; bh=qCgkJKps55l8RjxhpMVT2LM/JcRJvX3KzhzAmNcDzpA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=eAZsSIRo6pjxA5qxvcm5OPEEw9a1yoyM0xpK2OhyL9v4R1E7xgsaBCv7JneDbQ1TEMHkoIzCOqhXB8Lb+tb+z1ctRCxrd+vQR50IxrOmdNCxq9niGO0JnoTpQsQjN3OuX+mKe3QMLSi+WVrbYqB6FNW0bkeLh40uXFXf6Ef4TFk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=qK4OoFsU; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="qK4OoFsU" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-27c62320f16so78363035ad.1 for ; Tue, 07 Oct 2025 23:00:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1759903213; x=1760508013; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=fKVxg4WoPrUf/1g2EJBbMsdrqJ7J4uyOel2ApqCtl4o=; b=qK4OoFsU+VXEzIiFN7i0ybquBBt5H6YTJkYBNuFfY49HjvrS2nY6J5xQzTGApe0zoU Z6R6xO7krPM1aat2CDM+pKH/rnBr5Ga4B/ditnijm+SGfxnP/6XCQtoeuG39AOKRwTFa 2XipWoDdz15kTaYi+884s0CipnMGVKBCSSKv9wQRmV6UOyLRuiLGILTe7G47fAKzofhn d9KlEGHQzrLZIV3HYoNvqLXfj3G9JyanA+NnvPOdHPohLbLpL8hZSx0yPVsZoSlMh502 Pfc3ExlpcgYFgJ4ZY6+R85lxVXmRM6Kcv/buXI93S7MbkpHOWvpd38G84OqqCfmCS0Kv 53sg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759903213; x=1760508013; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=fKVxg4WoPrUf/1g2EJBbMsdrqJ7J4uyOel2ApqCtl4o=; b=eSaZNTEMeGDxz7a9cdSTDyXQsJ8jhEFnYBbtSbundOA4rKN3P+bBNz4yGs+DjEgDw+ Hr2Zffy1BWlI+frUmuVLkdr+fcUPXge0fFfWmX+BKfmtyrmSkE+UKLgD58oLfYMFu3vh hWz4RZc9xk9VLb4fJ5gtVE2/FIC9nH43IyauvVUWI3ZED0kY1C4x+P6X5uY/H1ZRezGS 19KNZP4tLJbovvhamzpEAJFWJUckZh/Bt+nOdC/h5sv5CE+FdkVFUTD6KBH4pBiLpywg HDutVCejgfp9AN3F0v5xTlXZ1AreVRo7Xn6c5IxAZnQY9Z3aihAtRb/g8jKIqrrRNYux Lumg== X-Forwarded-Encrypted: i=1; AJvYcCUgmGNROnIRBO2H7NgnRfLT3OTC/MBXqfFibYy/QXjJDGHt0QqKaftBSES/LU6N+hNjPvsBTan91oB5cfc=@vger.kernel.org X-Gm-Message-State: AOJu0YxzBXPCOOuhWtrDqSU6hQHOgaGIxEdPJdeuz5JB8ZO+Ho/Gzy2S /0xtV23GwFmuXI9eVRGbVg03AEF/KwFKHDAX+x9QQ6aezuz03G0T7jnLsQX5PuHEOFsdVRB9D2D BypmlXA== X-Google-Smtp-Source: AGHT+IEyo0B3o+ZPfKvOdrwXh59jKmgJAQXZpnESAfqmBlS0+CaamSvGek6ShAj0Lt8oc4gS4HOqEV0MfV4= X-Received: from plhu17.prod.google.com ([2002:a17:903:1251:b0:28e:7f4e:dd17]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:3807:b0:269:8d16:42d1 with SMTP id d9443c01a7336-290272e1e24mr28143825ad.50.1759903213112; Tue, 07 Oct 2025 23:00:13 -0700 (PDT) Date: Wed, 8 Oct 2025 05:59:57 +0000 In-Reply-To: <20251008060000.3136021-1-royluo@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251008060000.3136021-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.710.ga91ca5db03-goog Message-ID: <20251008060000.3136021-2-royluo@google.com> Subject: [PATCH v2 1/4] dt-bindings: usb: dwc3: Add Google Tensor G5 DWC3 From: Roy Luo To: Vinod Koul , Kishon Vijay Abraham I , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Greg Kroah-Hartman , Thinh Nguyen , Philipp Zabel , Peter Griffin , "=?UTF-8?q?Andr=C3=A9=20Draszik?=" , Tudor Ambarus Cc: Joy Chakraborty , Naveen Kumar , Roy Luo , Badhri Jagan Sridharan , linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Document the device tree bindings for the DWC3 USB controller found in Google Tensor SoCs, starting with the G5 generation. The Tensor G5 silicon represents a complete architectural departure from previous generations (like gs101), including entirely new clock/reset schemes, top-level wrapper and register interface. Consequently, existing Samsung/Exynos DWC3 USB bindings and drivers are incompatible, necessitating this new device tree binding. The USB controller on Tensor G5 is based on Synopsys DWC3 IP and features Dual-Role Device single port with hibernation support. Signed-off-by: Roy Luo --- .../bindings/usb/google,gs-dwc3.yaml | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/google,gs-dwc3.ya= ml diff --git a/Documentation/devicetree/bindings/usb/google,gs-dwc3.yaml b/Do= cumentation/devicetree/bindings/usb/google,gs-dwc3.yaml new file mode 100644 index 000000000000..9eb0bf726e8d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/google,gs-dwc3.yaml @@ -0,0 +1,145 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2025, Google LLC +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/google,gs-dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Google Tensor Series (G5+) DWC3 USB SoC Controller + +maintainers: + - Roy Luo + +description: | + Describes the DWC3 USB controller block implemented on Google Tensor SoC= s, + starting with the G5 generation. Based on Synopsys DWC3 IP, the controll= er + features Dual-Role Device single port with hibernation add-on. + +properties: + compatible: + items: + - enum: + - google,gs5-dwc3 + + reg: + minItems: 3 + maxItems: 3 + + reg-names: + description: | + The following memory regions must present: + - dwc3_core: Core DWC3 IP registers. + - host_cfg_csr: Hibernation control registers. + - usbint_csr: Hibernation interrupt registers. + items: + - const: dwc3_core + - const: host_cfg_csr + - const: usbint_csr + + interrupts: + minItems: 3 + maxItems: 3 + + interrupt-names: + description: | + The following interrupts must present: + - dwc_usb3: Core DWC3 interrupt. + - hs_pme_irq: High speed remote wakeup interrupt for hibernation. + - ss_pme_irq: Super speed remote wakeup interrupt for hibernation. + items: + - const: dwc_usb3 + - const: hs_pme_irq + - const: ss_pme_irq + + clocks: + minItems: 3 + maxItems: 3 + + clock-names: + minItems: 3 + maxItems: 3 + + resets: + minItems: 5 + maxItems: 5 + + reset-names: + items: + - const: usbc_non_sticky + - const: usbc_sticky + - const: usb_drd_bus + - const: u2phy_apb + - const: usb_top_csr + + power-domains: + minItems: 2 + maxItems: 2 + + power-domain-names: + description: | + The following power domain must present: + - usb_psw_pd: The child power domain of usb_top_pd. Turning it o= n puts the controller + into full power state, turning it off puts the co= ntroller into power + gated state. + - usb_top_pd: The parent power domain of usb_psw_pd. Turning it = on puts the controller + into power gated state, turning it off completely= shuts off the + controller. + items: + - const: usb_psw_pd + - const: usb_top_pd + + iommus: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - clocks + - resets + - reset-names + - power-domains + - power-domain-names + +allOf: + - $ref: snps,dwc3-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + soc { + #address-cells =3D <2>; + #size-cells =3D <2>; + + usb@c400000 { + compatible =3D "google,gs5-dwc3"; + reg =3D <0 0x0c400000 0 0xd060>, <0 0x0c450000 0 0x14>, <0 0x= 0c450020 0 0x8>; + reg-names =3D "dwc3_core", "host_cfg_csr", "usbint_csr"; + interrupts =3D , + , + ; + interrupt-names =3D "dwc_usb3", "hs_pme_irq", "ss_pme_irq"; + clocks =3D <&hsion_usbc_non_sticky_clk>, <&hsion_usbc_sticky_= clk>, + <&hsion_u2phy_apb_clk>; + clock-names =3D "usbc_non_sticky", "usbc_sticky", "u2phy_apb"; + resets =3D <&hsion_resets_usbc_non_sticky>, <&hsion_resets_usb= c_sticky>, + <&hsion_resets_usb_drd_bus>, <&hsion_resets_u2phy_apb= >, + <&hsion_resets_usb_top_csr>; + reset-names =3D "usbc_non_sticky", "usbc_sticky", + "usb_drd_bus", "u2phy_apb", + "usb_top_csr"; + power-domains =3D <&hsio_n_usb_psw_pd>, <&hsio_n_usb_pd>; + power-domain-names =3D "usb_psw_pd", "usb_top_pd"; + phys =3D <&usb_phy 0>; + phy-names =3D "usb2-phy"; + snps,quirk-frame-length-adjustment =3D <0x20>; + snps,gfladj-refclk-lpm-sel-quirk; + snps,incr-burst-type-adjustment =3D <4>; + }; + }; +... --=20 2.51.0.710.ga91ca5db03-goog From nobody Sat Feb 7 17:19:45 2026 Received: from mail-pf1-f201.google.com (mail-pf1-f201.google.com [209.85.210.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ADE552EAB98 for ; Wed, 8 Oct 2025 06:00:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903219; cv=none; b=Bu99M/6U9wFoCLTknavDtlzVVHeSz9yfgHgkJExRnFAeseKUQa0D99T2BgZXIFRRbfF20kAAFnzLXEAsNQwZBIW24Od5CytH0ZvnYgDsumI9VtVqSyjDtBWw4jybPtijnETqI9PYzxJkpX3M5vdccxb+i3J3rM/jwD2WoG8Lrt0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903219; c=relaxed/simple; bh=nzwyDM4gckqzqJNKzHDxSnQmR8jk9Hn2kdBip+WceNU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=EoQejvoicmvPr1EOjVwVOpvUcUs0rte/6H9b4mCWRgZfn5wJXcRivRRkGSGhkom9BGmdKsUHTWkDJRzES6Lh8gEpZcoHb6nwu5lPnL9mA07HqlfNC0UFdGZfCa5O8Na27xuDs6HTaVClnFuuajbNpDu9VbvQKWc0PPdLXQIh+/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=wLPHVtV/; arc=none smtp.client-ip=209.85.210.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="wLPHVtV/" Received: by mail-pf1-f201.google.com with SMTP id d2e1a72fcca58-7810af03a63so12279264b3a.3 for ; Tue, 07 Oct 2025 23:00:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1759903217; x=1760508017; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=C1S1HZsALDo0cFpkSNcdQcWYCHmdJaR4xyHZpfVsouc=; b=wLPHVtV/1gA6apqpObONHAsP3HUPCZi5pWCMoXvlGAgUmIrtw3Ajh7I03X6LbVeICY RvC0uj1pk8i8gJ9gWwII+FKdHiIB1xwz8/+wWs8DDz5GeBlAfl6+mHD6sQ45eQqHJqb6 ra3eKXyDwtmBkQ+vvcw88z15+DI1a7RKaZHNqJUlap8U+N07xAq3t3FgQ2oEVJuH0oCK NIYBy96P0OizxIh0Jsjm053L2zuaMR54B7ERsgrCJQN7xqbBO44okUCoEqZSVmS+Xn3q JYakGazV1LDqFUTyS5M+/NpwZTra7arbee9dDMZd+AZnUS5UeDu79nFrp1HnocHiHAbb 60BA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759903217; x=1760508017; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=C1S1HZsALDo0cFpkSNcdQcWYCHmdJaR4xyHZpfVsouc=; b=rPW/koDw3H8HJaZVCH7KDDcZ4HdDhn6eHYRvnFCkKt7h9Jq5LgYNWX2WRoQ3P43w7U nusqtj4T/ewbSC1hOe2KDvyldnVi7BU34xgeOabu1PPyEG9pbL2Dlc5hsbAEpYbRmkEo Uw/hZWNnA5Tyf8ZkDVoM58sP+mK7UEgNisbXw5ZrVRYNLjrO5LhZIgbSNmw1DJEYU4B4 Lhnau3tW4URLFGcuva/wG+qBdJeO3vKy2ySg2Glxlgm1dXIlbVcJdxAlZsIiY09QT5ht lQF7iuyidfBWRmco+hfqNiWfJ0Yc7cj9CX5MqezuZH3BuLYVmRYL845/8ouFQVk92Qza Y4wA== X-Forwarded-Encrypted: i=1; AJvYcCXbgDE9IfqWe0VdmiDvTKow+LcYS1JJ8za5X+od+A14u8+1i+VJEBA0Z876bnWBPzBZzdijS44JiVOf658=@vger.kernel.org X-Gm-Message-State: AOJu0YwoOZf2ep6bUuOEZ+Rr8zlwXZmd3c8C92LR3QIXKBotnDwtutfZ 6r89snCgJiIIut9EMWJvQMEzdyBB3iefdyvyn54Lc6ADN2WCN+8L+xK9sZPWfbiQP2ya3tpI0u3 X2TnhQQ== X-Google-Smtp-Source: AGHT+IFbK0pAsvf6VaWMTq944nLzy10OXYlG7R8jZOWZq55ApFRCkuPHayisagdgsjB5EBxRTvzYnCGYmT4= X-Received: from pjbrt15.prod.google.com ([2002:a17:90b:508f:b0:32d:def7:e60f]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:9191:b0:2b5:769f:2542 with SMTP id adf61e73a8af0-32da83db31cmr3085760637.36.1759903217050; Tue, 07 Oct 2025 23:00:17 -0700 (PDT) Date: Wed, 8 Oct 2025 05:59:58 +0000 In-Reply-To: <20251008060000.3136021-1-royluo@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251008060000.3136021-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.710.ga91ca5db03-goog Message-ID: <20251008060000.3136021-3-royluo@google.com> Subject: [PATCH v2 2/4] usb: dwc3: Add Google Tensor SoC DWC3 glue driver From: Roy Luo To: Vinod Koul , Kishon Vijay Abraham I , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Greg Kroah-Hartman , Thinh Nguyen , Philipp Zabel , Peter Griffin , "=?UTF-8?q?Andr=C3=A9=20Draszik?=" , Tudor Ambarus Cc: Joy Chakraborty , Naveen Kumar , Roy Luo , Badhri Jagan Sridharan , linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add support for the DWC3 USB controller found on Google Tensor G5. The controller features dual-role functionality and hibernation. The primary focus is implementing hibernation support in host mode, enabling the controller to enter a low-power state (D3). This is particularly relevant during system power state transition and runtime power management for power efficiency. Highlights: - Align suspend callback with dwc3_suspend_common() for deciding between a full teardown and hibernation in host mode. - Integration with `usb_psw_pd` and `usb_top_pd` power domains, managing their states and device links to support hibernation. - A notifier callback dwc3_google_usb_psw_pd_notifier() for `usb_psw_pd` power domain events to manage controller state transitions to/from D3. - Coordination of the `usbc_non_sticky` reset during power state transitions, asserting it on D3 entry and deasserting on D0 entry in hibernation scenario. - Handling of U2 (high-speed) and U3 (super-speed) PME interrupts that are generated by remote wakeup during hibernation. Co-developed-by: Joy Chakraborty Signed-off-by: Joy Chakraborty Co-developed-by: Naveen Kumar Signed-off-by: Naveen Kumar Signed-off-by: Roy Luo --- drivers/usb/dwc3/Kconfig | 10 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-google.c | 597 +++++++++++++++++++++++++++++++++ 3 files changed, 608 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-google.c diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 310d182e10b5..467515d5f937 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -189,4 +189,14 @@ config USB_DWC3_RTK or dual-role mode. Say 'Y' or 'M' if you have such device. =20 +config USB_DWC3_GOOGLE + tristate "Google Platform" + depends on OF && COMMON_CLK && RESET_CONTROLLER + default n + help + Support the DesignWare Core USB3 IP found on Google Tensor + SoCs, starting with the G5 generation. This driver includes + support for hibernation in host mode. + Say 'Y' or 'M' if you have one such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 830e6c9e5fe0..a94982630657 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_USB_DWC3_IMX8MP) +=3D dwc3-imx8mp.o obj-$(CONFIG_USB_DWC3_XILINX) +=3D dwc3-xilinx.o obj-$(CONFIG_USB_DWC3_OCTEON) +=3D dwc3-octeon.o obj-$(CONFIG_USB_DWC3_RTK) +=3D dwc3-rtk.o +obj-$(CONFIG_USB_DWC3_GOOGLE) +=3D dwc3-google.o diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c new file mode 100644 index 000000000000..67c51a051626 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-google.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-google.c - Google DWC3 Specific Glue Layer + * + * Copyright (c) 2025, Google LLC + * Author: Roy Luo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "glue.h" + +/* HOST CFG registers */ +#define HC_STATUS_OFFSET 0x0 +#define HC_STATUS_CURRENT_POWER_STATE_U2PMU GENMASK(1, 0) +#define HC_STATUS_CURRENT_POWER_STATE_U3PMU GENMASK(4, 3) + +#define HOST_CFG1_OFFSET 0x4 +#define HOST_CFG1_PME_EN BIT(3) +#define HOST_CFG1_PM_POWER_STATE_REQUEST GENMASK(5, 4) +#define HOST_CFG1_PM_POWER_STATE_D0 0x0 +#define HOST_CFG1_PM_POWER_STATE_D3 0x3 + +/* USBINT registers */ +#define USBINT_CFG1_OFFSET 0x0 +#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK BIT(2) +#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK BIT(3) +#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN BIT(8) +#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN BIT(9) +#define USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR BIT(14) +#define USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR BIT(15) + +#define USBINT_STATUS_OFFSET 0x4 +#define USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW BIT(2) +#define USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW BIT(3) + +#define DWC3_GOOGLE_MAX_RESETS 5 + +struct dwc3_google { + struct device *dev; + struct dwc3 dwc; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control_bulk_data rsts[DWC3_GOOGLE_MAX_RESETS]; + int num_rsts; + struct reset_control *usbc_non_sticky_rst; + struct device *usb_psw_pd; + struct device_link *usb_psw_pd_dl; + struct notifier_block usb_psw_pd_nb; + struct device *usb_top_pd; + struct device_link *usb_top_pd_dl; + void __iomem *host_cfg_base; + void __iomem *usbint_base; + int hs_pme_irq; + int ss_pme_irq; + bool is_hibernation; +}; + +#define to_dwc3_google(d) container_of((d), struct dwc3_google, dwc) + +static int dwc3_google_rst_init(struct dwc3_google *google) +{ + int ret; + + google->num_rsts =3D 5; + google->rsts[0].id =3D "usbc_non_sticky"; + google->rsts[1].id =3D "usbc_sticky"; + google->rsts[2].id =3D "usb_drd_bus"; + google->rsts[3].id =3D "u2phy_apb"; + google->rsts[4].id =3D "usb_top_csr"; + + ret =3D devm_reset_control_bulk_get_exclusive(google->dev, + google->num_rsts, + google->rsts); + + if (ret < 0) + return ret; + + google->usbc_non_sticky_rst =3D google->rsts[0].rstc; + + return 0; +} + +static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state) +{ + u32 reg; + int ret; + + reg =3D readl(google->host_cfg_base + HOST_CFG1_OFFSET); + reg &=3D ~HOST_CFG1_PM_POWER_STATE_REQUEST; + reg |=3D (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) | + HOST_CFG1_PME_EN); + writel(reg, google->host_cfg_base + HOST_CFG1_OFFSET); + + ret =3D readl_poll_timeout(google->host_cfg_base + HC_STATUS_OFFSET, reg, + (FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U2PMU, reg) =3D=3D state && + FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U3PMU, reg) =3D=3D state), + 10, 10000); + + if (ret) + dev_err(google->dev, "failed to set PMU state %d\n", state); + + return ret; +} + +/* + * Clear pme interrupts and report their status. + * The hardware requires write-1 then write-0 sequence to clear the interr= upt bits. + */ +static u32 dwc3_google_clear_pme_irqs(struct dwc3_google *google) +{ + u32 irq_status, reg_set, reg_clear; + + irq_status =3D readl(google->usbint_base + USBINT_STATUS_OFFSET); + irq_status &=3D (USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW | + USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW); + if (!irq_status) + return irq_status; + + reg_set =3D readl(google->usbint_base + USBINT_CFG1_OFFSET); + reg_clear =3D reg_set; + if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW) { + reg_set |=3D USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR; + reg_clear &=3D ~USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR; + } + if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW) { + reg_set |=3D USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR; + reg_clear &=3D ~USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR; + } + + writel(reg_set, google->usbint_base + USBINT_CFG1_OFFSET); + writel(reg_clear, google->usbint_base + USBINT_CFG1_OFFSET); + + return irq_status; +} + +static void dwc3_google_enable_pme_irq(struct dwc3_google *google) +{ + u32 reg; + + reg =3D readl(google->usbint_base + USBINT_CFG1_OFFSET); + reg &=3D ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK); + reg |=3D (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN); + writel(reg, google->usbint_base + USBINT_CFG1_OFFSET); + + enable_irq(google->hs_pme_irq); + enable_irq(google->ss_pme_irq); + enable_irq_wake(google->hs_pme_irq); + enable_irq_wake(google->ss_pme_irq); +} + +static void dwc3_google_disable_pme_irq(struct dwc3_google *google) +{ + u32 reg; + + reg =3D readl(google->usbint_base + USBINT_CFG1_OFFSET); + reg &=3D ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN); + reg |=3D (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK); + writel(reg, google->usbint_base + USBINT_CFG1_OFFSET); + + disable_irq_wake(google->hs_pme_irq); + disable_irq_wake(google->ss_pme_irq); + disable_irq_nosync(google->hs_pme_irq); + disable_irq_nosync(google->ss_pme_irq); +} + +static irqreturn_t dwc3_google_resume_irq(int irq, void *data) +{ + struct dwc3_google *google =3D data; + struct dwc3 *dwc =3D &google->dwc; + u32 irq_status, dr_role; + + irq_status =3D dwc3_google_clear_pme_irqs(google); + dr_role =3D dwc->current_dr_role; + + if (!irq_status || !google->is_hibernation || + dr_role !=3D DWC3_GCTL_PRTCAP_HOST) { + dev_warn(google->dev, "spurious pme irq %d, hibernation %d, dr_role %u\n= ", + irq, google->is_hibernation, dr_role); + return IRQ_HANDLED; + } + + if (dwc->xhci) + pm_runtime_resume(&dwc->xhci->dev); + + return IRQ_HANDLED; +} + +static int dwc3_google_request_irq(struct dwc3_google *google, struct plat= form_device *pdev, + const char *irq_name, const char *req_name) +{ + int ret; + int irq; + + irq =3D platform_get_irq_byname(pdev, irq_name); + if (irq < 0) { + dev_err(google->dev, "invalid irq name %s\n", irq_name); + return irq; + } + + irq_set_status_flags(irq, IRQ_NOAUTOEN); + ret =3D devm_request_threaded_irq(google->dev, irq, NULL, + dwc3_google_resume_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + req_name, google); + if (ret < 0) { + dev_err(google->dev, "failed to request irq %s\n", req_name); + return ret; + } + + return irq; +} + +static int dwc3_google_usb_psw_pd_notifier(struct notifier_block *nb, unsi= gned long action, void *d) +{ + struct dwc3_google *google =3D container_of(nb, struct dwc3_google, usb_p= sw_pd_nb); + int ret; + + if (!google->is_hibernation) + return NOTIFY_OK; + + if (action =3D=3D GENPD_NOTIFY_OFF) { + dev_dbg(google->dev, "enter D3 power state\n"); + dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D3); + ret =3D reset_control_assert(google->usbc_non_sticky_rst); + if (ret) + dev_err(google->dev, "non sticky reset assert failed: %d\n", ret); + } else if (action =3D=3D GENPD_NOTIFY_ON) { + dev_dbg(google->dev, "enter D0 power state\n"); + dwc3_google_clear_pme_irqs(google); + ret =3D reset_control_deassert(google->usbc_non_sticky_rst); + if (ret) + dev_err(google->dev, "non sticky reset deassert failed: %d\n", ret); + dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D0); + } + + return NOTIFY_OK; +} + +static void dwc3_google_pm_domain_deinit(struct dwc3_google *google) +{ + if (google->usb_top_pd_dl) + device_link_del(google->usb_top_pd_dl); + + if (!IS_ERR_OR_NULL(google->usb_top_pd)) { + device_set_wakeup_capable(google->usb_top_pd, false); + dev_pm_domain_detach(google->usb_top_pd, true); + } + + if (google->usb_psw_pd_dl) + device_link_del(google->usb_psw_pd_dl); + + if (!IS_ERR_OR_NULL(google->usb_psw_pd)) { + dev_pm_genpd_remove_notifier(google->usb_psw_pd); + dev_pm_domain_detach(google->usb_psw_pd, true); + } +} + +static int dwc3_google_pm_domain_init(struct dwc3_google *google) +{ + int ret; + + /* + * Establish PM RUNTIME link between dwc dev and its power domain usb_psw= _pd, + * register notifier block to handle hibernation. + */ + google->usb_psw_pd =3D dev_pm_domain_attach_by_name(google->dev, "usb_psw= _pd"); + if (IS_ERR_OR_NULL(google->usb_psw_pd)) { + dev_err(google->dev, "failed to get usb psw pd"); + ret =3D google->usb_psw_pd ? PTR_ERR(google->usb_psw_pd) : -ENODATA; + return ret; + } + + google->usb_psw_pd_nb.notifier_call =3D dwc3_google_usb_psw_pd_notifier; + ret =3D dev_pm_genpd_add_notifier(google->usb_psw_pd, &google->usb_psw_pd= _nb); + if (ret) { + dev_err(google->dev, "failed to add usb psw pd notifier"); + goto err; + } + + google->usb_psw_pd_dl =3D device_link_add(google->dev, google->usb_psw_pd, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!google->usb_psw_pd_dl) { + dev_err(google->usb_psw_pd, "failed to add device link"); + ret =3D -ENODEV; + goto err; + } + + /* + * usb_top_pd is the parent power domain of usb_psw_pd. Keeping usb_top_p= d on + * while usb_psw_pd is off places the controller in a power-gated state, + * essential for hibernation. Acquire a handle to usb_top_pd and sets it = as + * wakeup-capable to allow the domain to be left on during system suspend. + */ + google->usb_top_pd =3D dev_pm_domain_attach_by_name(google->dev, "usb_top= _pd"); + if (IS_ERR_OR_NULL(google->usb_top_pd)) { + dev_err(google->dev, "failed to get usb top pd"); + ret =3D google->usb_top_pd ? PTR_ERR(google->usb_top_pd) : -ENODATA; + goto err; + } + device_set_wakeup_capable(google->usb_top_pd, true); + + google->usb_top_pd_dl =3D device_link_add(google->dev, google->usb_top_pd, + DL_FLAG_STATELESS); + if (!google->usb_top_pd_dl) { + dev_err(google->usb_top_pd, "failed to add device link"); + ret =3D -ENODEV; + goto err; + } + + return 0; + +err: + dwc3_google_pm_domain_deinit(google); + + return ret; +} + +static int dwc3_google_probe(struct platform_device *pdev) +{ + struct dwc3_probe_data probe_data =3D {}; + struct device *dev =3D &pdev->dev; + struct dwc3_google *google; + struct resource *res; + int ret; + + google =3D devm_kzalloc(&pdev->dev, sizeof(*google), GFP_KERNEL); + if (!google) + return -ENOMEM; + + google->dev =3D &pdev->dev; + + ret =3D dwc3_google_pm_domain_init(google); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to init pdom\n"); + + ret =3D devm_clk_bulk_get_all_enabled(dev, &google->clks); + if (ret < 0) { + ret =3D dev_err_probe(&pdev->dev, ret, "failed to get and enable clks\n"= ); + goto err_deinit_pdom; + } + google->num_clks =3D ret; + + ret =3D dwc3_google_rst_init(google); + if (ret) { + ret =3D dev_err_probe(&pdev->dev, ret, "failed to get resets\n"); + goto err_deinit_pdom; + } + + ret =3D reset_control_bulk_deassert(google->num_rsts, google->rsts); + if (ret) { + ret =3D dev_err_probe(&pdev->dev, ret, "failed to deassert rsts\n"); + goto err_deinit_pdom; + } + + ret =3D dwc3_google_request_irq(google, pdev, "hs_pme_irq", "USB HS wakeu= p"); + if (ret < 0) { + ret =3D dev_err_probe(&pdev->dev, ret, "failed to request hs pme irq"); + goto err_reset_assert; + } + google->hs_pme_irq =3D ret; + + ret =3D dwc3_google_request_irq(google, pdev, "ss_pme_irq", "USB SS wakeu= p"); + if (ret < 0) { + ret =3D dev_err_probe(&pdev->dev, ret, "failed to request ss pme irq"); + goto err_reset_assert; + } + google->ss_pme_irq =3D ret; + + google->host_cfg_base =3D + devm_platform_ioremap_resource_byname(pdev, "host_cfg_csr"); + if (IS_ERR(google->host_cfg_base)) { + ret =3D dev_err_probe(&pdev->dev, PTR_ERR(google->host_cfg_base), + "invalid host cfg csr\n"); + goto err_reset_assert; + } + + google->usbint_base =3D + devm_platform_ioremap_resource_byname(pdev, "usbint_csr"); + if (IS_ERR(google->usbint_base)) { + ret =3D dev_err_probe(&pdev->dev, PTR_ERR(google->usbint_base), + "invalid usbint csr\n"); + goto err_reset_assert; + } + + res =3D platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3_core"); + if (!res) { + ret =3D dev_err_probe(dev, -ENODEV, "invalid dwc3 core memory\n"); + goto err_reset_assert; + } + + device_init_wakeup(dev, true); + + google->dwc.dev =3D dev; + probe_data.dwc =3D &google->dwc; + probe_data.res =3D res; + probe_data.ignore_clocks_and_resets =3D true; + ret =3D dwc3_core_probe(&probe_data); + if (ret) { + ret =3D dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); + goto err_reset_assert; + } + + return 0; + +err_reset_assert: + reset_control_bulk_assert(google->num_rsts, google->rsts); + +err_deinit_pdom: + dwc3_google_pm_domain_deinit(google); + + return ret; +} + +static void dwc3_google_remove(struct platform_device *pdev) +{ + struct dwc3 *dwc =3D platform_get_drvdata(pdev); + struct dwc3_google *google =3D to_dwc3_google(dwc); + + dwc3_core_remove(&google->dwc); + + reset_control_bulk_assert(google->num_rsts, google->rsts); + + dwc3_google_pm_domain_deinit(google); +} + +static int dwc3_google_suspend(struct dwc3_google *google, pm_message_t ms= g) +{ + if (pm_runtime_suspended(google->dev)) + return 0; + + if (google->dwc.current_dr_role =3D=3D DWC3_GCTL_PRTCAP_HOST) { + /* + * Follow dwc3_suspend_common() guidelines for deciding between + * a full teardown and hibernation. + */ + if (PMSG_IS_AUTO(msg) || device_may_wakeup(google->dev)) { + dev_dbg(google->dev, "enter hibernation"); + pm_runtime_get_sync(google->usb_top_pd); + device_wakeup_enable(google->usb_top_pd); + dwc3_google_enable_pme_irq(google); + google->is_hibernation =3D true; + return 0; + } + } + + reset_control_bulk_assert(google->num_rsts, google->rsts); + clk_bulk_disable_unprepare(google->num_clks, google->clks); + + return 0; +} + +static int dwc3_google_resume(struct dwc3_google *google, pm_message_t msg) +{ + int ret; + + if (google->is_hibernation) { + dev_dbg(google->dev, "exit hibernation"); + dwc3_google_disable_pme_irq(google); + device_wakeup_disable(google->usb_top_pd); + pm_runtime_put_sync(google->usb_top_pd); + google->is_hibernation =3D false; + return 0; + } + + ret =3D clk_bulk_prepare_enable(google->num_clks, google->clks); + if (ret) + return ret; + + ret =3D reset_control_bulk_deassert(google->num_rsts, google->rsts); + if (ret) { + clk_bulk_disable_unprepare(google->num_clks, google->clks); + return ret; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dwc3_google_pm_suspend(struct device *dev) +{ + struct dwc3 *dwc =3D dev_get_drvdata(dev); + struct dwc3_google *google =3D to_dwc3_google(dwc); + int ret; + + ret =3D dwc3_pm_suspend(&google->dwc); + if (ret) + return ret; + + return dwc3_google_suspend(google, PMSG_SUSPEND); +} + +static int dwc3_google_pm_resume(struct device *dev) +{ + struct dwc3 *dwc =3D dev_get_drvdata(dev); + struct dwc3_google *google =3D to_dwc3_google(dwc); + int ret; + + ret =3D dwc3_google_resume(google, PMSG_RESUME); + if (ret) + return ret; + + return dwc3_pm_resume(&google->dwc); +} + +static void dwc3_google_complete(struct device *dev) +{ + struct dwc3 *dwc =3D dev_get_drvdata(dev); + + dwc3_pm_complete(dwc); +} + +static int dwc3_google_prepare(struct device *dev) +{ + struct dwc3 *dwc =3D dev_get_drvdata(dev); + + return dwc3_pm_prepare(dwc); +} +#else +#define dwc3_google_complete NULL +#define dwc3_google_prepare NULL +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int dwc3_google_runtime_suspend(struct device *dev) +{ + struct dwc3 *dwc =3D dev_get_drvdata(dev); + struct dwc3_google *google =3D to_dwc3_google(dwc); + int ret; + + ret =3D dwc3_runtime_suspend(&google->dwc); + if (ret) + return ret; + + return dwc3_google_suspend(google, PMSG_AUTO_SUSPEND); +} + +static int dwc3_google_runtime_resume(struct device *dev) +{ + struct dwc3 *dwc =3D dev_get_drvdata(dev); + struct dwc3_google *google =3D to_dwc3_google(dwc); + int ret; + + ret =3D dwc3_google_resume(google, PMSG_AUTO_RESUME); + if (ret) + return ret; + + return dwc3_runtime_resume(&google->dwc); +} + +static int dwc3_google_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops dwc3_google_dev_pm_ops =3D { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_google_pm_suspend, dwc3_google_pm_resume) + SET_RUNTIME_PM_OPS(dwc3_google_runtime_suspend, dwc3_google_runtime_resum= e, + dwc3_google_runtime_idle) + .complete =3D dwc3_google_complete, + .prepare =3D dwc3_google_prepare, +}; + +static const struct of_device_id dwc3_google_of_match[] =3D { + { .compatible =3D "google,gs5-dwc3" }, + { } +}; +MODULE_DEVICE_TABLE(of, dwc3_google_of_match); + +static struct platform_driver dwc3_google_driver =3D { + .probe =3D dwc3_google_probe, + .remove =3D dwc3_google_remove, + .driver =3D { + .name =3D "google-dwc3", + .pm =3D &dwc3_google_dev_pm_ops, + .of_match_table =3D dwc3_google_of_match, + }, +}; + +module_platform_driver(dwc3_google_driver); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare DWC3 Google Glue Driver"); --=20 2.51.0.710.ga91ca5db03-goog From nobody Sat Feb 7 17:19:45 2026 Received: from mail-yx1-f74.google.com (mail-yx1-f74.google.com [74.125.224.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA2BA2EB5A6 for ; Wed, 8 Oct 2025 06:00:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903222; cv=none; b=U/26V8vgBMBTtAQXMyzf9FtQcI+WEvxeiTsqo5Vn6d7Yxs1iHMFp4Ybq76NrzS1UQeZyL4kNEmwmqzQ6FkQ4XDZQE4B6Um9373uN7eyx5AOSRA/cJfvPyZdYUjkhoCtkFbAGWn72T0W50C7elqskNgJ0vif9zgam961E1FrQmgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903222; c=relaxed/simple; bh=Di7zMVisYFTvAZ4tqhXBgQXgnhe5lS7Mu/ogKUkXSzY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=MJbqOoGx1QJ8r6OY2EyBdN+Gxu1Mb3HmsfoTRzXucKhMRW/DryRsOVocB6vWBj37QoDqKcfrDaIHz1qKBjvYEIRULu/mgA4ti+Sy9t5d9so7SgVMQCrenXncDD5zECuOlr+3O8o8fAG20doA9XzSe3af1DVChDh6o08uQwNzp9I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=1SWKruen; arc=none smtp.client-ip=74.125.224.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="1SWKruen" Received: by mail-yx1-f74.google.com with SMTP id 956f58d0204a3-63917818a4cso8629568d50.1 for ; Tue, 07 Oct 2025 23:00:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1759903219; x=1760508019; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=O3nQ2IBXVlqup3yhiw/z2ZC4B8Uznh8ZXWkNRIY9DZ0=; b=1SWKruenBTdy/NBtIGE0N0srkQ4Qpf0gO31eklbsrylxzwLpUFnD9h0WkPl6wQLGtq Z9fykvEiaiYYHjGP775gWyhAkhk4x7YZ8fSMvdS4ndRqdRFQWADZCuniHoqNCJew7p4V YYf0Z9WRChdnqxUMp8je7E1h45fcFahzc7GsdKHHkH3ZhqSH4gJSS9lmNia3XTHLxYlV lfp4Z/4TDGVbLMNZCqBQI6dlFwt6CiZxsVSLP3dQVbtLeA1wRatn5tP430Xd4lPGrnN4 eHpOoRLqI6Zl4/rW3Busxp2GE8veilXQK90hqzQhiPu/Acy8sGyuBtJXUY/MS4ZUcAb1 jKlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759903219; x=1760508019; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=O3nQ2IBXVlqup3yhiw/z2ZC4B8Uznh8ZXWkNRIY9DZ0=; b=evAbEBR4z4Vw0qt21Q+zup7NvYtaTcWInVvVoer7hD0bp4hr1Bdg13TzYkjvO4f6yq eMx+Srg57DchuA9zIhMJpkOepOLj5Wp33HSXHUe2YnSjBsnQ+Iq9tFoQAccMUJMX9XmP XMFmaE+RfXGrs8G9evsYZoqYjsJRZdBuJSoKnR4JW07OVJowyVyYphjd9V5MVCB2L9dV zhCDA8R82kFq6gjvk1Y4T9j4AAYrjgoOFKE11qxOzZ7pKrQit+F8MZz89QHj6VxL72Sy AGuDP2PgFUFA7O26kVSdp1x4oLv6YUJoXn0XNq1CP4xy5ooMZNS5+XIy+msBpPL5lRk7 Sc/w== X-Forwarded-Encrypted: i=1; AJvYcCXme4cbBS9aKxD1vuN6R+KaWTYgxyxGWMzErfcMSHlDQ9PNhJrpPxFEYrtGZGmENnqa11uhEcWGX0ZKJWg=@vger.kernel.org X-Gm-Message-State: AOJu0YwP+Yv5usjIrlXsbRhSjl/Kx3s9OV1zCkv/2CDAvwe+Wgfs4++w qAX2AOta2hXVR7CzwoSQY1tXIxwk9uTUGcEoTVj1eQUb1BdWyYaQ8CI7b94RKjDHSfiusLultsb 32x+8WA== X-Google-Smtp-Source: AGHT+IHkVcxa2pum021wuuoLs2okLrcMUb66ABXH6XTGYhxmFAHtMt5/klv2Td06QQp5AMBpCeX1Xhqxjbw= X-Received: from ybbch13.prod.google.com ([2002:a05:6902:b0d:b0:eb5:2c5e:6965]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a05:690c:6c08:b0:77f:667f:61f6 with SMTP id 00721157ae682-780e155533dmr45405427b3.15.1759903219488; Tue, 07 Oct 2025 23:00:19 -0700 (PDT) Date: Wed, 8 Oct 2025 05:59:59 +0000 In-Reply-To: <20251008060000.3136021-1-royluo@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251008060000.3136021-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.710.ga91ca5db03-goog Message-ID: <20251008060000.3136021-4-royluo@google.com> Subject: [PATCH v2 3/4] dt-bindings: phy: google: Add Google Tensor G5 USB PHY From: Roy Luo To: Vinod Koul , Kishon Vijay Abraham I , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Greg Kroah-Hartman , Thinh Nguyen , Philipp Zabel , Peter Griffin , "=?UTF-8?q?Andr=C3=A9=20Draszik?=" , Tudor Ambarus Cc: Joy Chakraborty , Naveen Kumar , Roy Luo , Badhri Jagan Sridharan , linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Document the device tree bindings for the USB PHY interfaces integrated with the DWC3 controller on Google Tensor SoCs, starting with G5 generation. Due to a complete architectural overhaul in the Google Tensor G5, the existing Samsung/Exynos USB PHY driver and binding for older generations of Google silicons such as gs101 are no longer compatible. The USB PHY on Tensor G5 includes two integrated Synopsys PHY IPs: the eUSB 2.0 PHY IP and the USB 3.2/DisplayPort combo PHY IP. Currently only USB high-speed is described and supported. Signed-off-by: Roy Luo --- .../bindings/phy/google,gs-usb-phy.yaml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/google,gs-usb-phy= .yaml diff --git a/Documentation/devicetree/bindings/phy/google,gs-usb-phy.yaml b= /Documentation/devicetree/bindings/phy/google,gs-usb-phy.yaml new file mode 100644 index 000000000000..22961e2da6ef --- /dev/null +++ b/Documentation/devicetree/bindings/phy/google,gs-usb-phy.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025, Google LLC +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/google,gs-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Google Tensor Series (G5+) USB PHY + +maintainers: + - Roy Luo + +description: | + Describes the USB PHY interfaces integrated with the DWC3 USB controller= on + Google Tensor SoCs, starting with the G5 generation. + Two specific PHY IPs from Synopsys are integrated, including eUSB 2.0 PH= Y IP + and USB 3.2/DisplayPort combo PHY IP. + The first phandle argument within the PHY specifier is used to identify = the + desired PHY. The currently supported value is:: + 0 - USB high-speed. + +properties: + compatible: + items: + - enum: + - google,gs5-usb-phy + + reg: + minItems: 3 + maxItems: 3 + + reg-names: + items: + - const: usb2_cfg_csr + - const: dp_top_csr + - const: usb_top_cfg_csr + + "#phy-cells": + const: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: usb2_phy_clk + + resets: + maxItems: 1 + + reset-names: + items: + - const: usb2_phy_reset + + power-domains: + maxItems: 1 + + orientation-switch: + type: boolean + description: + Indicates the PHY as a handler of USB Type-C orientation changes + +required: + - compatible + - reg + - reg-names + - "#phy-cells" + - clocks + - clock-names + - resets + - reset-names + +unevaluatedProperties: false + +examples: + - | + soc { + #address-cells =3D <2>; + #size-cells =3D <2>; + + usb_phy: usb_phy@c410000 { + compatible =3D "google,gs5-usb-phy"; + reg =3D <0 0x0c450014 0 0xc>, + <0 0x0c637000 0 0xa0>, + <0 0x0c45002c 0 0x4>; + reg-names =3D "usb2_cfg_csr", "dp_top_csr", "usb_top_cfg_csr"; + #phy-cells =3D <1>; + clocks =3D <&hsion_usb2_phy_reset_clk>; + clock-names =3D "usb2_phy_clk"; + resets =3D <&hsion_resets_usb2_phy>; + reset-names =3D "usb2_phy_reset"; + power-domains =3D <&hsio_n_usb_pd>; + orientation-switch; + }; + }; +... --=20 2.51.0.710.ga91ca5db03-goog From nobody Sat Feb 7 17:19:45 2026 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BDFC62EAD0A for ; Wed, 8 Oct 2025 06:00:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903224; cv=none; b=j69LeKNrR+WhWovuxuTnsczQe3s4gRjaE24af3FFAtsXFz5OvRQ7M1hXAeDCXoQw38jpk490jMMv84PI5AFMXYLScG+rS4Ww92/GIW6eXMSBqjocKWsTBcpC1iuO8Ra75rBnZPfY61CDC3QSjxCIrjCSf/8NkqVJN9iJBb3CfME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759903224; c=relaxed/simple; bh=1LsFgPYzpayfPJ/FsKvMjpCnXRfU9VdGJpb+XK443rc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=JwlI0XK3gAkR+y6Cxzu7GrNtapW1rySuz9s2HFkgmMdDAOVENCOtPU0R/0p++/+xiwtge0vMO+JxNkvsl28wlw1f2ro0goL7zkUEwHvuMdkWpghiFg2stEuzycCBWMVP+lBLyX3lzGp52memF0FMQGviNZBTdT/+MfdGOtfba7g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=cvshig18; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--royluo.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="cvshig18" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2681642efd9so70316995ad.2 for ; Tue, 07 Oct 2025 23:00:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1759903222; x=1760508022; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Ka8Q7nlHWOoI2RkFnFPZVDeuOoF3NtDVxhEhxWMkISg=; b=cvshig18HdBGmWJTR1yO/hnq7ZW58Z7yFQ6V3mMVsm+O/p54f1e1oecuTCIt65RPjC nUV2xD/VA3CBpgZtayxGqQkwfWEHZR8Oz+0AVBhItuRePlQQbQgE0okBeHH1GMiLq9+K IpsV0YV1aTxTmlNLE4Sh5QrIdhYNv1W3ejVGOW/Yhub2bea17htr1TyCRpIPSX/QqrNw hg4SY11E9pEKYirzrY4gETXknesvGUQ7LNaK06CUQvddYIDAl2ssh7Nd+FdA1SV8Cbzx rj3FrdPNM23JNrTJy3cTdJLVCdn629dxK0WUB31tNBsW/xmZTOEC5OMthsJxX7XHVAu5 lurA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759903222; x=1760508022; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Ka8Q7nlHWOoI2RkFnFPZVDeuOoF3NtDVxhEhxWMkISg=; b=Pz1qpzXoziw5xZt3msyKAg1EH+n+IB4eUdqnoQXIJJnlHvL7c8oSCciHz6L0kNoq6I xQfPQ5k+W6ds11coNkVNgRAtSAjNdhIVc4yWnciRPlJBjsCT5dSpzgzfJiVrKVlOQCjV EU8cI0RGPSgUlg/HtCj1MgIcRxhV0kr1FzHYCCNi/ltCF+pAY0PsPbtzNlyMmiSOEKFj ai6QX7qTkRztsKKG95dLzqpH9ux3YhNrIgaFKspvgw/rGfYuqVddf4P3bA5G5dPF35bQ lMnUPg3rQniLi+SjejhcaL3gv7uyoORobxsjCpZMBZ4TyeA0ys1d7eN+g+7YDvZD8eeq mmgQ== X-Forwarded-Encrypted: i=1; AJvYcCX/yS8eMAae83mq7WHdn9fi6s0XE5eE7/iOvXdvKqmyXdBtBrjaVhbCGTEceoNR3nlccPzZNNYW53jhFgg=@vger.kernel.org X-Gm-Message-State: AOJu0YzQj30yoX0ajSgbt2c24q/ZlnJW4grlLy9xSgM8wpOLRSHy/Ht+ DeX/QYUhy7Cnqzwp69EqmMi7wY2odBitL+HqYVN7Hh8Ru7jIoGCY0cFuI0zEtrdYWy4GGy1KWLV 5or0LDA== X-Google-Smtp-Source: AGHT+IHEgK0iSfGPTqrfxzDEFvxdlz9aMrrAMDN66bGMiYvI5hGjGDvEExy9qsuc4PeR2LLXj7IghdV+NhY= X-Received: from plot12.prod.google.com ([2002:a17:902:8c8c:b0:27d:6f45:42ec]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:1a6b:b0:25c:abb3:9bae with SMTP id d9443c01a7336-29027385222mr27269835ad.48.1759903222057; Tue, 07 Oct 2025 23:00:22 -0700 (PDT) Date: Wed, 8 Oct 2025 06:00:00 +0000 In-Reply-To: <20251008060000.3136021-1-royluo@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251008060000.3136021-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.710.ga91ca5db03-goog Message-ID: <20251008060000.3136021-5-royluo@google.com> Subject: [PATCH v2 4/4] phy: Add Google Tensor SoC USB PHY driver From: Roy Luo To: Vinod Koul , Kishon Vijay Abraham I , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Greg Kroah-Hartman , Thinh Nguyen , Philipp Zabel , Peter Griffin , "=?UTF-8?q?Andr=C3=A9=20Draszik?=" , Tudor Ambarus Cc: Joy Chakraborty , Naveen Kumar , Roy Luo , Badhri Jagan Sridharan , linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Support the USB PHY found on Google Tensor G5. This particular USB PHY supports both high-speed and super-speed operations, and is integrated with the SNPS DWC3 controller that's also on the SoC. This initial patch specifically adds functionality for high-speed. Co-developed-by: Joy Chakraborty Signed-off-by: Joy Chakraborty Co-developed-by: Naveen Kumar Signed-off-by: Naveen Kumar Signed-off-by: Roy Luo --- drivers/phy/Kconfig | 14 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-google-usb.c | 286 +++++++++++++++++++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 drivers/phy/phy-google-usb.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 58c911e1b2d2..0966ab0297df 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -101,6 +101,20 @@ config PHY_NXP_PTN3222 schemes. It supports all three USB 2.0 data rates: Low Speed, Full Speed and High Speed. =20 +config PHY_GOOGLE_USB + tristate "Google Tensor SoC USB PHY driver" + depends on HAS_IOMEM + depends on OF + depends on TYPEC + depends on USB_DWC3_GOOGLE + select GENERIC_PHY + default USB_DWC3_GOOGLE + help + Enable support for the USB PHY on Google Tensor SoCs, starting with + the G5 generation. This driver provides the PHY interfaces to + interact with the SNPS eUSB2 and USB 3.2/DisplayPort Combo PHY, both + of which are integrated with the DWC3 USB controller. + source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c670a8dac468..1d7a1331bd19 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PHY_SNPS_EUSB2) +=3D phy-snps-eusb2.o obj-$(CONFIG_USB_LGM_PHY) +=3D phy-lgm-usb.o obj-$(CONFIG_PHY_AIROHA_PCIE) +=3D phy-airoha-pcie.o obj-$(CONFIG_PHY_NXP_PTN3222) +=3D phy-nxp-ptn3222.o +obj-$(CONFIG_PHY_GOOGLE_USB) +=3D phy-google-usb.o obj-y +=3D allwinner/ \ amlogic/ \ broadcom/ \ diff --git a/drivers/phy/phy-google-usb.c b/drivers/phy/phy-google-usb.c new file mode 100644 index 000000000000..4027d38dc34b --- /dev/null +++ b/drivers/phy/phy-google-usb.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-google-usb.c - Google USB PHY driver + * + * Copyright (C) 2025, Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USBCS_USB2PHY_CFG19_OFFSET 0x0 +#define USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV GENMASK(19, 8) + +#define USBCS_USB2PHY_CFG21_OFFSET 0x8 +#define USBCS_USB2PHY_CFG21_PHY_ENABLE BIT(12) +#define USBCS_USB2PHY_CFG21_REF_FREQ_SEL GENMASK(15, 13) +#define USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL BIT(19) + +#define USBCS_PHY_CFG1_OFFSET 0x28 +#define USBCS_PHY_CFG1_SYS_VBUSVALID BIT(17) + +#define USBCS_TOP_CTRL_CFG1_OFFSET 0x0 +#define USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE BIT(5) + +enum google_usb_phy_id { + GOOGLE_USB2_PHY, + GOOGLE_USB_PHY_NUM, +}; + +struct google_usb_phy_instance { + int index; + struct phy *phy; + struct clk *clk; + struct reset_control *rst; +}; + +struct google_usb_phy { + struct device *dev; + void __iomem *usb2_cfg_base; + void __iomem *dp_top_base; + void __iomem *usb_top_cfg_base; + struct google_usb_phy_instance insts[GOOGLE_USB_PHY_NUM]; + /* serialize phy access */ + struct mutex phy_mutex; + struct typec_switch_dev *sw; + enum typec_orientation orientation; +}; + +static inline struct google_usb_phy *to_google_usb_phy(struct google_usb_p= hy_instance *inst) +{ + return container_of(inst, struct google_usb_phy, insts[inst->index]); +} + +static void set_vbus_valid(struct google_usb_phy *gphy) +{ + u32 reg; + + if (gphy->orientation =3D=3D TYPEC_ORIENTATION_NONE) { + reg =3D readl(gphy->dp_top_base + USBCS_PHY_CFG1_OFFSET); + reg &=3D ~USBCS_PHY_CFG1_SYS_VBUSVALID; + writel(reg, gphy->dp_top_base + USBCS_PHY_CFG1_OFFSET); + } else { + reg =3D readl(gphy->dp_top_base + USBCS_PHY_CFG1_OFFSET); + reg |=3D USBCS_PHY_CFG1_SYS_VBUSVALID; + writel(reg, gphy->dp_top_base + USBCS_PHY_CFG1_OFFSET); + } +} + +static int google_usb_set_orientation(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct google_usb_phy *gphy =3D typec_switch_get_drvdata(sw); + + dev_dbg(gphy->dev, "set orientation %d\n", orientation); + + gphy->orientation =3D orientation; + + if (pm_runtime_suspended(gphy->dev)) + return 0; + + guard(mutex)(&gphy->phy_mutex); + + set_vbus_valid(gphy); + + return 0; +} + +static int google_usb2_phy_init(struct phy *_phy) +{ + struct google_usb_phy_instance *inst =3D phy_get_drvdata(_phy); + struct google_usb_phy *gphy =3D to_google_usb_phy(inst); + u32 reg; + int ret =3D 0; + + dev_dbg(gphy->dev, "initializing usb2 phy\n"); + + guard(mutex)(&gphy->phy_mutex); + + /* + * TODO: usb2only mode should be removed once usb3 is supported + */ + reg =3D readl(gphy->usb_top_cfg_base + USBCS_TOP_CTRL_CFG1_OFFSET); + reg |=3D USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE; + writel(reg, gphy->usb_top_cfg_base + USBCS_TOP_CTRL_CFG1_OFFSET); + + reg =3D readl(gphy->usb2_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + reg &=3D ~USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL; + reg &=3D ~USBCS_USB2PHY_CFG21_REF_FREQ_SEL; + reg |=3D FIELD_PREP(USBCS_USB2PHY_CFG21_REF_FREQ_SEL, 0); + writel(reg, gphy->usb2_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + + reg =3D readl(gphy->usb2_cfg_base + USBCS_USB2PHY_CFG19_OFFSET); + reg &=3D ~USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV; + reg |=3D FIELD_PREP(USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV, 368); + writel(reg, gphy->usb2_cfg_base + USBCS_USB2PHY_CFG19_OFFSET); + + set_vbus_valid(gphy); + + ret =3D clk_prepare_enable(inst->clk); + if (ret) + return ret; + + ret =3D reset_control_deassert(inst->rst); + if (ret) { + clk_disable_unprepare(inst->clk); + return ret; + } + + reg =3D readl(gphy->usb2_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + reg |=3D USBCS_USB2PHY_CFG21_PHY_ENABLE; + writel(reg, gphy->usb2_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + + return ret; +} + +static int google_usb2_phy_exit(struct phy *_phy) +{ + struct google_usb_phy_instance *inst =3D phy_get_drvdata(_phy); + struct google_usb_phy *gphy =3D to_google_usb_phy(inst); + u32 reg; + + dev_dbg(gphy->dev, "exiting usb2 phy\n"); + + guard(mutex)(&gphy->phy_mutex); + + reg =3D readl(gphy->usb2_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + reg &=3D ~USBCS_USB2PHY_CFG21_PHY_ENABLE; + writel(reg, gphy->usb2_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + + reset_control_assert(inst->rst); + clk_disable_unprepare(inst->clk); + + return 0; +} + +static const struct phy_ops google_usb2_phy_ops =3D { + .init =3D google_usb2_phy_init, + .exit =3D google_usb2_phy_exit, +}; + +static struct phy *google_usb_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct google_usb_phy *gphy =3D dev_get_drvdata(dev); + + if (args->args[0] >=3D GOOGLE_USB_PHY_NUM) { + dev_err(dev, "invalid PHY index requested from DT\n"); + return ERR_PTR(-ENODEV); + } + return gphy->insts[args->args[0]].phy; +} + +static int google_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct google_usb_phy *gphy; + struct phy *phy; + struct google_usb_phy_instance *inst; + struct phy_provider *phy_provider; + struct typec_switch_desc sw_desc =3D { }; + int ret; + + gphy =3D devm_kzalloc(dev, sizeof(*gphy), GFP_KERNEL); + if (!gphy) + return -ENOMEM; + + dev_set_drvdata(dev, gphy); + gphy->dev =3D dev; + + ret =3D devm_mutex_init(dev, &gphy->phy_mutex); + if (ret) + return ret; + + gphy->usb2_cfg_base =3D devm_platform_ioremap_resource_byname(pdev, + "usb2_cfg_csr"); + if (IS_ERR(gphy->usb2_cfg_base)) + return dev_err_probe(dev, PTR_ERR(gphy->usb2_cfg_base), + "invalid usb2 cfg csr\n"); + + gphy->dp_top_base =3D devm_platform_ioremap_resource_byname(pdev, + "dp_top_csr"); + if (IS_ERR(gphy->dp_top_base)) + return dev_err_probe(dev, PTR_ERR(gphy->dp_top_base), + "invalid dp top csr\n"); + + gphy->usb_top_cfg_base =3D devm_platform_ioremap_resource_byname(pdev, + "usb_top_cfg_csr"); + if (IS_ERR(gphy->usb_top_cfg_base)) + return dev_err_probe(dev, PTR_ERR(gphy->usb_top_cfg_base), + "invalid usb top cfg csr\n"); + + inst =3D &gphy->insts[GOOGLE_USB2_PHY]; + inst->index =3D GOOGLE_USB2_PHY; + phy =3D devm_phy_create(dev, NULL, &google_usb2_phy_ops); + if (IS_ERR(phy)) + return dev_err_probe(dev, PTR_ERR(phy), + "failed to create usb2 phy instance\n"); + inst->phy =3D phy; + phy_set_drvdata(phy, inst); + inst->clk =3D devm_clk_get(dev, "usb2_phy_clk"); + if (IS_ERR(inst->clk)) + return dev_err_probe(dev, PTR_ERR(inst->clk), + "failed to get usb2 phy clk\n"); + inst->rst =3D devm_reset_control_get_exclusive(dev, "usb2_phy_reset"); + if (IS_ERR(inst->rst)) + return dev_err_probe(dev, PTR_ERR(inst->rst), + "failed to get usb2 phy reset\n"); + + phy_provider =3D devm_of_phy_provider_register(dev, google_usb_phy_xlate); + if (IS_ERR(phy_provider)) + return dev_err_probe(dev, PTR_ERR(phy_provider), + "failed to register phy provider\n"); + + pm_runtime_enable(dev); + + sw_desc.fwnode =3D dev_fwnode(dev); + sw_desc.drvdata =3D gphy; + sw_desc.name =3D fwnode_get_name(dev_fwnode(dev)); + sw_desc.set =3D google_usb_set_orientation; + + gphy->sw =3D typec_switch_register(dev, &sw_desc); + if (IS_ERR(gphy->sw)) + return dev_err_probe(dev, PTR_ERR(gphy->sw), + "failed to register typec switch\n"); + + return 0; +} + +static void google_usb_phy_remove(struct platform_device *pdev) +{ + struct google_usb_phy *gphy =3D dev_get_drvdata(&pdev->dev); + + typec_switch_unregister(gphy->sw); + pm_runtime_disable(&pdev->dev); +} + +static const struct of_device_id google_usb_phy_of_match[] =3D { + { + .compatible =3D "google,gs5-usb-phy", + }, + { } +}; +MODULE_DEVICE_TABLE(of, google_usb_phy_of_match); + +static struct platform_driver google_usb_phy =3D { + .probe =3D google_usb_phy_probe, + .remove =3D google_usb_phy_remove, + .driver =3D { + .name =3D "google-usb-phy", + .of_match_table =3D google_usb_phy_of_match, + } +}; + +module_platform_driver(google_usb_phy); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Google USB phy driver"); --=20 2.51.0.710.ga91ca5db03-goog