From nobody Wed Dec 17 21:25:18 2025 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (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 6F2D3301493 for ; Fri, 10 Oct 2025 20:16:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127385; cv=none; b=a5/VgzQPvruIEw9vz0FGTS9IAVFpW1RCjQqojuze5iDPOq4FkIV1V1wR6FbU1x4VYuzCKWfPzZHSw2Vec+U3GRZD5rDyirY52OkBjGnjO7e8PrrtqGULfOzPwjgeEKzYp2RJgDgtFGzPuHHUWdGkIu2uBjusDB0c24/ZXedgMxQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127385; c=relaxed/simple; bh=m+F37612IGMHvLc16v/jAtfnK17+MxK/JVaGZhH/rOw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AEa0x2m+/Udbp/q/kSeLP7a0LA8qGOnEjbZ8gkFAERBlA/BcjnnQs03TCo4VZ/a4lSiLThPWEyVnxgNw6RxKX8+7Fclt68+FVuXrOnIcea5NGzn8UQ0hAPy0YP97tS6jdDItzY8TsFhXS0ushlGBcfwQ1VRrZIcWm1TE8qsEP/s= 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=AvwG2dEM; arc=none smtp.client-ip=209.85.216.73 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="AvwG2dEM" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-3304def7909so5631399a91.3 for ; Fri, 10 Oct 2025 13:16:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1760127383; x=1760732183; 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=Wl+K3dr5aF0u4Q2P+9MDk1HHOxlzdHXYY9jrwJSxmqs=; b=AvwG2dEMN1E/R9BuifQ/5Lp2oQ8QpsbrARbrFMTKu89cbaFyB60CxRQ/G+xD/K+fb5 0Pt5QGrJNZ7bW/GfAK/mRON/H+mSPEcLuICSVIqDhUfmFXQ7QWf+z2JtqK7knYI68GH7 czLFJ2eCMXJQyx1RXs7jbeTPOF1K8Bg7iy0SgIixC+B7rgbo12Y/nd0Jnlnkt3tMuJX6 LXSFf6igA0u2tsNE8O9B+AFoZB6rauCf/+JzHlEO0cIIeN/6Mv7YkKOlnsFk1hylq9uv ZurQQ2WWoYz9kUL3y5EggkjjDc/OFdLS7rvVtfLR3iDPqK1tnx6HKjbZUZT37K+0MthX wTEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760127383; x=1760732183; 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=Wl+K3dr5aF0u4Q2P+9MDk1HHOxlzdHXYY9jrwJSxmqs=; b=r6zol4uez8DKMapJm14prsWtqe5gUkk25zNwjd3szgm2flZptGmUoJhTbWYf143r9P M0MP+BdOLGpMMwggwgeM4HB3OM09OMQ/N22v/PZvcmD3CtmZ9+TTWl3xx9a6udTm5NYK mg83oLAg1Hi2d9VeLRr7MO6QXl0MoWDPYxm262+3vElf+z7qKImPppy6VQw6fmDD+9k5 TamYRcE2X7lyX9cuwjj4tWrknEO+uqccdC/Gitn9JegHU6wGL25HqeQVneAsPHGR3p7K LMSl1Ctj0bY4adQlsFcJh6KnHH02UMKrQ3rjLIG5Fs4F6cuquitbYlf/ZoyekNrEBYQq 92BA== X-Forwarded-Encrypted: i=1; AJvYcCU25wn/z0DC9V7WF6CNJvQ+4YZf6D6dnYbZgJjROPKY5/x+ETWSyLk6c0+xIUP9WwzQ0I63l+GpcEUeF3g=@vger.kernel.org X-Gm-Message-State: AOJu0Yzyr65zh8rWsx7HEyV1taAQabOyDnJq+j7L1/EOiOz2lprVRn82 RHWFaGrdmkPEAx237+hjGhyXgRvortS6OiPfvBkVeJ2L7SRQQYS6bzr7Wo+x/3LnVkY2t3++dNK NDu4uLw== X-Google-Smtp-Source: AGHT+IFk8ClGIAy+ZveXDAUA46gMVmfQcViJcyf3PjbmPLP8E3Q60Y+VuC/rLX+rN4bo089j/56MJ/qKNBU= X-Received: from pjbfr18.prod.google.com ([2002:a17:90a:e2d2:b0:33b:51fe:1a90]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:1e07:b0:339:f09b:d372 with SMTP id 98e67ed59e1d1-33b513b4c91mr19379313a91.23.1760127382638; Fri, 10 Oct 2025 13:16:22 -0700 (PDT) Date: Fri, 10 Oct 2025 20:16:04 +0000 In-Reply-To: <20251010201607.1190967-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: <20251010201607.1190967-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.740.g6adb054d12-goog Message-ID: <20251010201607.1190967-2-royluo@google.com> Subject: [PATCH v3 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 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,gs5-dwc3.yaml | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/google,gs5-dwc3.y= aml diff --git a/Documentation/devicetree/bindings/usb/google,gs5-dwc3.yaml b/D= ocumentation/devicetree/bindings/usb/google,gs5-dwc3.yaml new file mode 100644 index 000000000000..6fadea7f41e8 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/google,gs5-dwc3.yaml @@ -0,0 +1,141 @@ +# 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,gs5-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: + const: google,gs5-dwc3 + + reg: + items: + - description: Core DWC3 IP registers. + - description: USB host controller configuration registers. + - description: USB custom interrrupts control registers. + + reg-names: + items: + - const: dwc3_core + - const: host_cfg + - const: usbint_cfg + + interrupts: + items: + - description: Core DWC3 interrupt. + - description: High speed power management event for remote wakeup f= rom hibernation. + - description: Super speed power management event for remote wakeup = from hibernation. + + interrupt-names: + items: + - const: dwc_usb3 + - const: hs_pme + - const: ss_pme + + clocks: + items: + - description: Non-sticky module clock. + - description: Sticky module clock. + - description: USB2 PHY APB clock. + + clock-names: + items: + - const: non_sticky + - const: sticky + - const: u2phy_apb + + resets: + items: + - description: Non-sticky module reset. + - description: Sticky module reset. + - description: USB2 PHY APB reset. + - description: DRD bus reset. + - description: Top-level reset. + + reset-names: + items: + - const: non_sticky + - const: sticky + - const: u2phy_apb + - const: drd_bus + - const: top + + power-domains: + items: + - description: Power switchable domain, the child of top domain. + Turning it on puts the controller into full power state, + turning it off puts the controller into power gated state. + - description: Top domain, the parent of power switchable domain. + Turning it on puts the controller into power gated state, + turning it off completely shuts off the controller. + + power-domain-names: + items: + - const: psw + - const: top + + iommus: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - clocks + - clock-names + - 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", "usbint_cfg"; + interrupts =3D , + , + ; + interrupt-names =3D "dwc_usb3", "hs_pme", "ss_pme"; + clocks =3D <&hsion_usbc_non_sticky_clk>, <&hsion_usbc_sticky_= clk>, + <&hsion_u2phy_apb_clk>; + clock-names =3D "non_sticky", "sticky", "u2phy_apb"; + resets =3D <&hsion_resets_usbc_non_sticky>, <&hsion_resets_usb= c_sticky>, + <&hsion_resets_u2phy_apb>, <&hsion_resets_usb_drd_bus= >, + <&hsion_resets_usb_top>; + reset-names =3D "non_sticky", "sticky", "u2phy_apb", "drd_bus"= , "top"; + power-domains =3D <&hsio_n_usb_psw>, <&hsio_n_usb>; + power-domain-names =3D "psw", "top"; + 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.740.g6adb054d12-goog From nobody Wed Dec 17 21:25:18 2025 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.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 18B27302746 for ; Fri, 10 Oct 2025 20:16:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127389; cv=none; b=oib5DFGROgr8QwIvihYzqHX81nLLvEbURg4JlCSBGffD2W9KJGAhrNL1s5F1gSrEHkGETyhbrENFtSrfTYw6q+ytZiXsMt/DDRHKWEdcQWadJXJ6N9QxPtP1WczGqGoc5m85JcOsOJNH2wP1f31utDYClngCFH/WwvEGuICQMo4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127389; c=relaxed/simple; bh=ybQ43h4mtrpi0x4Mi8Q15Df05dPi3Wp815sJXYqIBtI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=pYEnKr7qkn2Vb1Z9XGsT+NtLlALtZrYzSkgyfLEnApUGaSO3Vi4kSFwKCqdZy8qrb/RKqqCzTqiavgVO/HQsD0ppijd99jHocHutPM5sSNHwaXdbee/cOG7xqKOj2lMsboxnAPZTl6TOGaxi4b0n8oEGHFKU+DUBcrnwECHarts= 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=GtPooiFe; arc=none smtp.client-ip=209.85.216.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="GtPooiFe" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-339b704e2e3so6742869a91.2 for ; Fri, 10 Oct 2025 13:16:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1760127386; x=1760732186; 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=MH5qEpLyYBFe0SHiLeV5Sn2ngfz8XIU5EYdZ8rGKyPI=; b=GtPooiFeiE9eSMPVI5nqTE/VjqezSuSG2QK+SF8C1eSjrTKWJausRo+myow+Me+FdY DF1dp984yHXXVyWIiAiegDyg2y0rn5RBHrLCKGjt+SYvEUKbaWiQP6YIi8EGWt5uSMPm mnbpoAWHHmydDOsKtI/BXnWLibhTOa2fqOitvHRGZ7l1JcYOFcI7D/dqg8pjBv/+b1ZI w9RzwwD5i8JfXaBtqpjSQ9ngfcWOJabM17YePrZevUw0jzCr6Wrpv7y5rfLRz7dBeRqu PDFsVRCC7tOgBSBHydw9btGZvQYPbcUAOpySD5/4IY3mjuTVYeHiHmRpn4Yzhf6YdWt7 CBGw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760127386; x=1760732186; 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=MH5qEpLyYBFe0SHiLeV5Sn2ngfz8XIU5EYdZ8rGKyPI=; b=LOb+xBDER0DREc8KykGnpCMNUaraXlvz47nipDC9mDKCrZ0UW++UkyLY/5d7SaKP78 oCgC3oAR5G86/qQy+9r3la3vLAzFXcSFea+BxeVUX1QZEyjERuixFmOAPZEBt9Nsv0kx Eh7AKpAnYlqZletUAN561gdmRDChLHYhVp6GnfN1XVuDSkoirjPP292YnX3kJFxF2Fs9 l71qTt2yBboi2SAr7ONu0vRDoUqt4r1h5am+U7/SW9XXtPJ8NvBGa97KAm5C5fog0pe4 jZfToljhyMyr+5Jxkynp18QUl9MtyreMlFZ/pl6zbsIUYYbklgqykzuiYS7kld08jrMN 8Obg== X-Forwarded-Encrypted: i=1; AJvYcCXz5TymPNujYSbE+9BmuQylGi++6CBTRxG2FpwC6X+TNJWXH/7kupF3ofYJgp4Y4gpDVMm2F7fWQrAkJ7w=@vger.kernel.org X-Gm-Message-State: AOJu0YyseYjlZERWkVfFR+r7xvDWStNPN27yMLIxzSYKuMyfSo2MYvA7 PajWSlhDJri4msh6GwrD+HWH+OesfQt88rPpmKHAJYLc6CK3lffF5sTNS27qjZKpdSxBvie930H t9cM19A== X-Google-Smtp-Source: AGHT+IHwq3gBUAzJnmngAEZHK3GnE2dqW/mZVUzG+5n+dp1KC/Je9qjALEEU4MtP8bwVIfWZbKWg61WDrlU= X-Received: from pjbem17.prod.google.com ([2002:a17:90b:151:b0:32d:dbd4:5cf3]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:3908:b0:335:21bf:3b99 with SMTP id 98e67ed59e1d1-33b5139a3c6mr17418947a91.32.1760127386310; Fri, 10 Oct 2025 13:16:26 -0700 (PDT) Date: Fri, 10 Oct 2025 20:16:05 +0000 In-Reply-To: <20251010201607.1190967-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: <20251010201607.1190967-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.740.g6adb054d12-goog Message-ID: <20251010201607.1190967-3-royluo@google.com> Subject: [PATCH v3 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 `psw` (power switchable) and `top` power domains, managing their states and device links to support hibernation. - A notifier callback dwc3_google_usb_psw_pd_notifier() for `psw` power domain events to manage controller state transitions to/from D3. - Coordination of the `non_sticky` reset during power state transitions, asserting it on D3 entry and deasserting on D0 entry in hibernation scenario. - Handling of high-speed and 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..ae4e9ed2a98f --- /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 *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_cfg_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 "non_sticky"; + google->rsts[1].id =3D "sticky"; + google->rsts[2].id =3D "u2phy_apb"; + google->rsts[3].id =3D "drd_bus"; + google->rsts[4].id =3D "top"; + + ret =3D devm_reset_control_bulk_get_exclusive(google->dev, + google->num_rsts, + google->rsts); + + if (ret < 0) + return ret; + + google->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_cfg_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_cfg_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_cfg_base + USBINT_CFG1_OFFSET); + writel(reg_clear, google->usbint_cfg_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_cfg_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_cfg_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_cfg_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_cfg_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->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->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, "psw"); + 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, "top"); + 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", "USB HS wakeup"); + 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", "USB SS wakeup"); + 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"); + 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_cfg_base =3D + devm_platform_ioremap_resource_byname(pdev, "usbint_cfg"); + if (IS_ERR(google->usbint_cfg_base)) { + ret =3D dev_err_probe(&pdev->dev, PTR_ERR(google->usbint_cfg_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.740.g6adb054d12-goog From nobody Wed Dec 17 21:25:18 2025 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (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 92FF7302761 for ; Fri, 10 Oct 2025 20:16:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127392; cv=none; b=am+ZxyOCxK54bPJZOJ0x1bxxZGeKv7aVU6mnVj6EDz/7gcoD8lmkm8N5Cq7KY8noWF/eWUSrt6Zj66m+Tc+k0kifxlmHBH7SZf//eydwLbCvxuW9uj27I2Ckm+vU6TIZFLc4QldGqp0ASJ6oHz7s/ndVHaa/4GKd9DXA71U8g6o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127392; c=relaxed/simple; bh=5laxyqh7RGrWvj7i4MuNeBhW45V+0GW5m1xO8+53Fn4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=u45Cyqzyx1MCaH8jPd8UZKH6ckPHKdGYvAsy6OYaTNUWNGRS/KxpByrBHhUffAAyWP03tnw7H+tOYUM7MYGJrqBeFlZ5oUtiMKE47w+561zwnzyPSKyZ7usBOOp370ChmDY+W075n6YwI2UZ8l4E6yEVP7ylgc7SPb5C0xGOSJ4= 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=WNT4nE6/; arc=none smtp.client-ip=209.85.216.73 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="WNT4nE6/" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-334b0876195so6787800a91.1 for ; Fri, 10 Oct 2025 13:16:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1760127390; x=1760732190; 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=rDMjRNZuXBcadxapfJv5DZnyvTWebZelvolDUVBwbeA=; b=WNT4nE6/hYpYSD2D400uErVO+dV2m9ZDnCKnmwkjkAmGjHZCBb/YDtYnAu7cDe6ea9 kS1Tf+Nt7ez5xbfw2dcTeJoClyiKFVmV1EzP27ve8qBgmMOnKMVSO978Bb3PLduj28mh V0NJEm5gMawxi0BOkmV14f4b0CUU5oI1CxmGe9nbrMvjXdXUev3p6oHeA1rZ0XGJaqOB 9F40YpXACBfNOXkYcArsSdf2pv4GdYcTERevZ8iqzy6gArGq8LuFw4mNKMZNqOrPd0+I g9qVHG+4Grla7lAMy1PxUiaXFPkGNi4UgvcjmRg2iG4qS3rCe27/6pm/8P4W+T7WMI0G t6Pg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760127390; x=1760732190; 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=rDMjRNZuXBcadxapfJv5DZnyvTWebZelvolDUVBwbeA=; b=XEgL9Kv4IN7PHS1fuEXwtQVvNW8b2HJcvNK4xaQK8dqyk4B9IVj12dY6Iz8oXhZscg oZMan8voxPaiMcbiuRoafwxUpGrQrIcCTBiRyn2LPL18iIIU6TAvJXj0ybLAHA2Yghqw nmkPze7z8GU2sE2udeLlVcywRGe4WMhTCGLlFePsDlifUVAhKu0Ss9akxIOY902/L4fH JnoIRA3ONYRFirqUjSoi2C6f8ft58r2pu/FJOkW+7sh0bCZ8DnaqtpiNhT4B2/lY3mAZ J4udvVtcj3tArIlXVsJofdu5AmOrJYI/NQ5IpiWG1kL2/PKb6v7kxBjN1ods9f8g20JH nvjA== X-Forwarded-Encrypted: i=1; AJvYcCUt6HhSh3O+ut4xgT/5hJaxT9k33m7t/cTVfMUHXrocNDMLH1sY1SYF5G+eBP27jPlPthnpQhCWY7vR7oM=@vger.kernel.org X-Gm-Message-State: AOJu0YxD3mHzwhUYwn6Sw5eywTt3Z2aOmtx9sqcmdunlzMeWarXrTHMx 3/M02EB2nHRdsgtubQz2oq/lWvTnbx/BjBjTGH0recpuuLXUOnBmM671y9QD0zHTnSLD5bzMxrj zGeXYhg== X-Google-Smtp-Source: AGHT+IG2O2/hmz4EGHxbz6LI/FnWz/lYeOwpP5uEupOv3mruD2AaSSt/AAWUjN2TixtwciX5BwSqJ0FHSFw= X-Received: from pjff6.prod.google.com ([2002:a17:90b:5626:b0:330:6c04:207]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4a83:b0:32e:b87e:a961 with SMTP id 98e67ed59e1d1-33b511173bfmr19125944a91.5.1760127389914; Fri, 10 Oct 2025 13:16:29 -0700 (PDT) Date: Fri, 10 Oct 2025 20:16:06 +0000 In-Reply-To: <20251010201607.1190967-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: <20251010201607.1190967-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.740.g6adb054d12-goog Message-ID: <20251010201607.1190967-4-royluo@google.com> Subject: [PATCH v3 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. 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. Due to a complete architectural overhaul in the Google Tensor G5, the existing Samsung/Exynos USB PHY binding for older generations of Google silicons such as gs101 are no longer compatible, necessitating this new device tree binding. Signed-off-by: Roy Luo --- .../bindings/phy/google,gs5-usb-phy.yaml | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/google,gs5-usb-ph= y.yaml diff --git a/Documentation/devicetree/bindings/phy/google,gs5-usb-phy.yaml = b/Documentation/devicetree/bindings/phy/google,gs5-usb-phy.yaml new file mode 100644 index 000000000000..a40de31ac768 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/google,gs5-usb-phy.yaml @@ -0,0 +1,88 @@ +# 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,gs5-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 hardware can support three PHY interfaces, which are selected using = the + first phandle argument in the PHY specifier:: + 0 - USB high-speed. + 1 - USB super-speed. + 2 - DisplayPort + +properties: + compatible: + const: google,gs5-usb-phy + + reg: + items: + - description: USB2 PHY configuration registers. + - description: DisplayPort top-level registers. + - description: USB top-level configuration registers. + + reg-names: + items: + - const: u2phy_cfg + - const: dp_top + - const: usb_top_cfg + + "#phy-cells": + const: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + 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 + - resets + - power-domains + - orientation-switch + +additionalProperties: 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 "u2phy_cfg", "dp_top", "usb_top_cfg"; + #phy-cells =3D <1>; + clocks =3D <&hsion_usb2_phy_reset_clk>; + resets =3D <&hsion_resets_usb2_phy>; + power-domains =3D <&hsio_n_usb_pd>; + orientation-switch; + }; + }; +... --=20 2.51.0.740.g6adb054d12-goog From nobody Wed Dec 17 21:25:18 2025 Received: from mail-yw1-f201.google.com (mail-yw1-f201.google.com [209.85.128.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 3D0A1302CB1 for ; Fri, 10 Oct 2025 20:16:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127398; cv=none; b=r7gga1X4eZ4GLnUTefPLf6y2DBdwBwI2QrZoxSJ4jQ1BGheiMtyoH5gE5asFhCRXQIpI8VcnxiIt16+O7kVgkTf+DMzEv4bxEMMWotS5Vkn3gLXMlMhwjckpDHkBa0q0Zpuko3cS9QJg66cOrRlLaq7vFb/LbDl5cCvMVjVTTgo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760127398; c=relaxed/simple; bh=Gxr/xHISJIb/7lG6eaw48pT14+frhZAw4fChJseEZ7s=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uBC/M2ytyV14Ze7v12/7u3GyQk9X2UwKmIqvCBC2Y35j/yDCkN8qgjRlAvT6uo+ISP7ctjxIccdFASs3S0VhDfdq6gB7ogkjgz3zIAR3SZk4FZ+PfILqFrclIZwfa+iXODRxFhlXMDq6MtCCcMvg89M9IokYBcjASK3Mqqc2uNs= 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=ZcdvpwkU; arc=none smtp.client-ip=209.85.128.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="ZcdvpwkU" Received: by mail-yw1-f201.google.com with SMTP id 00721157ae682-780fb6a1469so49010257b3.0 for ; Fri, 10 Oct 2025 13:16:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1760127393; x=1760732193; 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=9cy6GouhBVW6s2NTnVqmrY/myMZPh7UG7EmUWWoH+r4=; b=ZcdvpwkU2RuTUaSy2eIxQdkjQHcsKIjS3whDihImXxvjkdB6DadBHVuDGET31e9z/f oRKBGfst8qAMxQsoO9TROh2zNKTzpqwRX0GieF6tsrUhZZw/XR+KA4U+p7q50Zz7tcsL mVMaeyp/L7RfyDkCXNu5wPLMdWa3rtadz8r0N9AKqvZljqaksVGbHt+Z6BupAOvdbWRX 5fmao/+eXAIeIK9GM/U+oPtSD4HEbWgsKqfEIaRDP6TMh9bau62WLPN+j/jcK1DYkVpJ e8MVuPGqXAuUL/IxVKvVTwbttwS4PiLgX0EVU6mk3WOjYwZv8CXJ8UdwijLRu+60O+BZ OoBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760127393; x=1760732193; 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=9cy6GouhBVW6s2NTnVqmrY/myMZPh7UG7EmUWWoH+r4=; b=NGH4Dt+zHkDgRM5GdBazyzi4RpYZVkn8E8UwYEW/mzT8Uhc0bvmdcFynRVOUpK+DDj NrdrZaHCvasxXiA44KQLr0cZ3+6tF43Yb2VT7M3YfLD+8aUClWpXhRViKZSjEL3iVqTw asNHTzNxK5aCuBm+FcDwLkVF1lEAb7VFV7cC7ZGB5ZvYsJAjgVa3nswwQU/KtEec90bC 2meFGi1wep4/taY+tUAn3ysa0pUxcQmTBfLRq/4GWt4q3x47OnzMgFjjTQQB+GQDfnx9 uKdGDph/2l+hXbIEMrs/Vv/e/NyiTgMugzJfdwZTS3ssbVG4lvY4Zji5YmcLDUYz3/sj Wdww== X-Forwarded-Encrypted: i=1; AJvYcCWCQtSK+FmgiOCZCqE9x8f6WK5G07Mb+wBTdrMXaUqGvx0OIFoXOBx1fwer9M1Wh9+rgG9VnCeWwjl1+mQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxerrWMfiV4ECzPYuNcYvdk9XkiMJ4Bd2RlklxeaisivTiv7nLs FhlZZvfpllgyoMYP+BRnwEuSaE1n9YHoEfuOcjAyAPF4gBHUZw4ywSjcKnBazG/2QHPs0FIWbjt WG7vOWg== X-Google-Smtp-Source: AGHT+IEbWQmNfsUoS0E86B8Mc7MgJPmXk9Os/ELYWWcQPdwSpoX4iRHmFp337Cqx69RXAXo6fdN1nrsPZUQ= X-Received: from ywu4.prod.google.com ([2002:a05:690c:9a84:b0:767:a90d:1198]) (user=royluo job=prod-delivery.src-stubby-dispatcher) by 2002:a05:690c:3693:b0:780:f8b7:c177 with SMTP id 00721157ae682-780f8b7c4f5mr91345807b3.16.1760127393148; Fri, 10 Oct 2025 13:16:33 -0700 (PDT) Date: Fri, 10 Oct 2025 20:16:07 +0000 In-Reply-To: <20251010201607.1190967-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: <20251010201607.1190967-1-royluo@google.com> X-Mailer: git-send-email 2.51.0.740.g6adb054d12-goog Message-ID: <20251010201607.1190967-5-royluo@google.com> Subject: [PATCH v3 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 | 15 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-google-usb.c | 286 +++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 drivers/phy/phy-google-usb.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 58c911e1b2d2..a01f91d6e05e 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -101,6 +101,21 @@ 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. + This driver currently supports USB high-speed. + 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..883abe64300c --- /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 *u2phy_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->u2phy_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->u2phy_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + + reg =3D readl(gphy->u2phy_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->u2phy_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->u2phy_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + reg |=3D USBCS_USB2PHY_CFG21_PHY_ENABLE; + writel(reg, gphy->u2phy_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->u2phy_cfg_base + USBCS_USB2PHY_CFG21_OFFSET); + reg &=3D ~USBCS_USB2PHY_CFG21_PHY_ENABLE; + writel(reg, gphy->u2phy_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->u2phy_cfg_base =3D devm_platform_ioremap_resource_byname(pdev, + "u2phy_cfg"); + if (IS_ERR(gphy->u2phy_cfg_base)) + return dev_err_probe(dev, PTR_ERR(gphy->u2phy_cfg_base), + "invalid usb2 cfg csr\n"); + + gphy->dp_top_base =3D devm_platform_ioremap_resource_byname(pdev, + "dp_top"); + 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"); + 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, NULL); + 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(dev, NULL); + 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.740.g6adb054d12-goog