From nobody Mon May 25 05:13:49 2026 Received: from mail.cjdns.fr (mail.cjdns.fr [5.135.140.105]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 95F1E1A3165; Mon, 18 May 2026 14:14:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=5.135.140.105 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779113643; cv=none; b=nhwo3ZhataImu9LyL94e3X75IaRzQy3s0Nz5fXlYarLNfHL0M7Gz13zFNz5JQNcnlBwidBVOu8Z65lTN/kU9mEKCBrFB/xyAzamTuhykh4nKw+Nf1wFGLtsIylDGm3U+OzxXfORr0j722CmLUaEmkSDFuwIyBr9ikhOf6C3eLU4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779113643; c=relaxed/simple; bh=SIYEAqjr2zmbJF5H0zuQNww2SC9zebWfATS+/XXLDY4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=V8w3M6AysGrqTgipG0oAU0Hf5vH51haurDqrMMFWFjFDhI2z2jWsmFLmeUos9yyemLKFot3wsqfevFjSYEoJCWAuMpdsH0QefL83qxt0Dkd6VkAfrzB5gQis1c8rDrFSNuM5u6YxJclevbbudUwyXR7KhcOTa8wNHhzDP1c8jEo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=cjdns.fr; spf=pass smtp.mailfrom=cjdns.fr; dkim=pass (2048-bit key) header.d=cjdns.fr header.i=@cjdns.fr header.b=RVSj/0dU; arc=none smtp.client-ip=5.135.140.105 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=cjdns.fr Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cjdns.fr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cjdns.fr header.i=@cjdns.fr header.b="RVSj/0dU" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id E99FB412ECA; Mon, 18 May 2026 16:13:51 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cjdns.fr; s=dkim; t=1779113633; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=k6pdOU+b4KLB22qUcje1fbtGobrJHihoy0yvfUJmdr4=; b=RVSj/0dU2KqbYXIfQMFQcRTJOK+ScyAxGT9VFYUcT3KtEpQbk3KL7AT5r0AZh6aal5k4ay 0o1gHALxFKAXt0Fy+lKDrRGZttSVevlSvOQnw3zB5mMVoWeGKsXA0h8XeNxgrhJnt3tWaq Np6hNVQQ9oLLP6vg/0E7/Icu1xrQW1aKxA1DhDl+LOO4B2SuQ2muomdSTBS9Gn2h/4Hmfl R1jjc7ya+A3jcA5gkAtmNPaU3S+i7ZnLXVIBft5Wy4NFlWbcnzh+Kf1giCFl/bBY0gpadE bUKr3MWEYzB7bAFsH6VsNHas9TVMuUIjLuGDX3l1IPxyZAb5yLwb2ZmcOTCC0A== From: Caleb James DeLisle To: linux-mips@vger.kernel.org Cc: vkoul@kernel.org, neil.armstrong@linaro.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Caleb James DeLisle Subject: [PATCH 1/2] dt-bindings: phy: econet: Document EN751221 USB PHY Date: Mon, 18 May 2026 14:13:42 +0000 Message-Id: <20260518141343.401555-2-cjd@cjdns.fr> In-Reply-To: <20260518141343.401555-1-cjd@cjdns.fr> References: <20260518141343.401555-1-cjd@cjdns.fr> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Document the USB PHY devices which appear in EcoNet EN751221, EN751627, and EN7528 based SoCs. Signed-off-by: Caleb James DeLisle --- .../bindings/phy/econet,en751221-usb-phy.yaml | 128 ++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 134 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/econet,en751221-u= sb-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.= yaml b/Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml new file mode 100644 index 000000000000..a44f59601747 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml @@ -0,0 +1,128 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +# Copyright (C) 2024 EcoNet +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/econet,en751221-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: EcoNet EN751221 USB PHY + +maintainers: + - Caleb James DeLisle + +description: | + USB PHY controller found on EcoNet EN751221 SoCs as well as on EN751627 = and + EN7528. These devices generally have two ports, one of which is a USB 3.= 0, + and the other is USB 2.0. The USB 3.0 port is driven by one of two PHY + blocks, depending on whether the connected device has negotiated USB 3.0= or + 2.0. These PHYs are also used on other EcoNet silicon in varying + configurations, such as only port 0 (the USB 3.0 port), or only port 1 (= the + USB 2.0 port). + +properties: + compatible: + enum: + - econet,en751221-usb-phy + - econet,en751627-usb-phy + - econet,en7528-usb-phy + + reg: + maxItems: 1 + + "#address-cells": true + "#size-cells": true + ranges: true + + clocks: + maxItems: 1 + description: | + Crystal oscillator clock source. EcoNet devices run at either 20Mhz = or + 25Mhz. 25Mhz devices require additional tuning in the USB 3.0 PHY. + + clock-names: + items: + - const: xtal + +patternProperties: + "^usb-phy@[0-9a-f]+$": + type: object + description: USB 2.0 or 3.0 PHY sub-node. + + properties: + compatible: + enum: + - econet,usb2-phy + - econet,usb3-phy + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + econet,usb-port-id: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Physical port number. Since USB 3.0 requires a second PHY for th= e 2.0 + fallback, multiple PHYs can map to the same physical port. + + "#phy-cells": + const: 0 + + required: + - compatible + - reg + - econet,usb-port-id + - "#phy-cells" + + additionalProperties: false + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - ranges + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + + usb-phy@1fa80000 { + compatible =3D "econet,en751221-usb-phy"; + reg =3D <0x1fa80000 0x700>; + #address-cells =3D <1>; + #size-cells =3D <1>; + ranges; + clocks =3D <&xtalclk>; + clock-names =3D "xtal"; + + usb-phy@1fa80800 { + compatible =3D "econet,usb2-phy"; + reg =3D <0x1fa80800 0x100>; + resets =3D <&scuclk EN751221_USB_PHY_P0_RST>; + econet,usb-port-id =3D <0>; + #phy-cells =3D <0>; + }; + + usb-phy@1fa80900 { + compatible =3D "econet,usb3-phy"; + reg =3D <0x1fa80900 0x700>; + resets =3D <&scuclk EN751221_USB_PHY_P0_RST>; + econet,usb-port-id =3D <0>; + #phy-cells =3D <0>; + }; + + usb-phy@1fa81000 { + compatible =3D "econet,usb2-phy"; + reg =3D <0x1fa81000 0x100>; + resets =3D <&scuclk EN751221_USB_PHY_P1_RST>; + econet,usb-port-id =3D <1>; + #phy-cells =3D <0>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index f877e5aaf2c7..1a90a1736294 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9204,6 +9204,12 @@ F: drivers/irqchip/irq-econet-en751221.c F: include/dt-bindings/clock/econet,en751221-scu.h F: include/dt-bindings/reset/econet,en751221-scu.h =20 +ECONET USB PHY DRIVER +M: Caleb James DeLisle +L: linux-mips@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml + ECRYPT FILE SYSTEM M: Tyler Hicks L: ecryptfs@vger.kernel.org --=20 2.39.5 From nobody Mon May 25 05:13:49 2026 Received: from mail.cjdns.fr (mail.cjdns.fr [5.135.140.105]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B70881FBEA8; Mon, 18 May 2026 14:14:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=5.135.140.105 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779113644; cv=none; b=gF6cvE4QlN283k4o8uncSAOndPoZoxREsPzKRpKoEcu8eYvS/Rq1G2tTGZVEyeOpU3bn5iAoALDepGgBUKJnENOl+zIsaOBI9jEJpWqE+vdlgnVnksRIp3Dg6WygX/cbNpd/y4KbGilR0xEenaPAqxuUKlbGrcxFw/oGxv5nIno= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779113644; c=relaxed/simple; bh=rIDD9sMu6vrXcKb5GILbk/xMXs2Brt+Psz3lT+sR0eA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=rSyc9H/p5yLiK04MONn4MY9hiSMy9kJ3EVB4vSUuJqnoaWD3TkjucaH1epfrweWOgo8aigmK7/GSIlAUhlrD02aon49oCXVZpkyPr3tIDh9XKfCF2T1yYC7KEcuIVc0Y9DUMPAenv3KlhJnCF4Zdq6V9dftKd1zG/ybJdx9c/c4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=cjdns.fr; spf=none smtp.mailfrom=cjdns.fr; dkim=pass (2048-bit key) header.d=cjdns.fr header.i=@cjdns.fr header.b=nK1eBKvG; arc=none smtp.client-ip=5.135.140.105 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=cjdns.fr Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=cjdns.fr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cjdns.fr header.i=@cjdns.fr header.b="nK1eBKvG" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 1AB40412F6F; Mon, 18 May 2026 16:13:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cjdns.fr; s=dkim; t=1779113639; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=lAEtOVhxL47rMN5ASFOhDrgx469XHP0gDm2RAGJXgfk=; b=nK1eBKvG67x9FORzjAScRey6wqy1UaB5UQwALVwW//Mc6jruZG4ViW5yfTQLIcm034IZ05 JXGK9jYCP+blAfc1tmSbvXLfRdJH1wwEW0M09IxxtWwmIAEH3XZYwsiym5wooecsF0DL45 7ZUvujwDP1IPBZ/94WZ0OERU2po4jvzRr4xhj7xX5rM21uCmO73zMI+05GhPdJvSy3okEu nykDxsZI8IFTmZxVg5xi6s9yysSWypjbCIc/c575tViB8JXR89Dno0xyIN2iAv620VWN3u ZojGybARc6E8nKYplSBkdtii+0ksimJWLhif7IP2zc2iSAkJPDafdxbP7azHIQ== From: Caleb James DeLisle To: linux-mips@vger.kernel.org Cc: vkoul@kernel.org, neil.armstrong@linaro.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Caleb James DeLisle , Benjamin Larsson , Ahmed Naseef Subject: [PATCH 2/2] phy: econet: Add EcoNet USB PHY Date: Mon, 18 May 2026 14:13:43 +0000 Message-Id: <20260518141343.401555-3-cjd@cjdns.fr> In-Reply-To: <20260518141343.401555-1-cjd@cjdns.fr> References: <20260518141343.401555-1-cjd@cjdns.fr> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Add support for USB PHY found on EcoNet EN751221, EN751627, and EN7528 based SoCs. This PHY is somewhat similar to Mediatek tphy-v1, but in terms of setup ritual it has essentially nothing in common. Based on work originally by Benjamin Larsson with bug fixes and added support for EN751627 and EN7528. Tested on: * EN7526G (SmartFiber XP8421-B) USB 2.0 * EN7526F (ChinaMobile GS3101) USB 2.0 * EN7516 (Zyxel EX3301-T0) USB 2.0 Tested by Ahmed Naseef on EN7528 (Dasan H660GM) USB 2.0 Tested by Benjamin Larsson on unreleased EN751221 board with USB 3.0 Originally-by: Benjamin Larsson Signed-off-by: Caleb James DeLisle Tested-by: Ahmed Naseef Tested-by: Benjamin Larsson Link: https://github.com/openwrt/openwrt/pull/23264#issuecomment-4415906568 --- MAINTAINERS | 1 + drivers/phy/Kconfig | 13 + drivers/phy/Makefile | 1 + drivers/phy/phy-econet-usb.c | 558 +++++++++++++++++++++++++++++++++++ 4 files changed, 573 insertions(+) create mode 100644 drivers/phy/phy-econet-usb.c diff --git a/MAINTAINERS b/MAINTAINERS index 1a90a1736294..74cf07bbc49f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9209,6 +9209,7 @@ M: Caleb James DeLisle L: linux-mips@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml +F: drivers/phy/phy-econet-usb.c =20 ECRYPT FILE SYSTEM M: Tyler Hicks diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 227b9a4c612e..0a3bec9af93e 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -66,6 +66,19 @@ config PHY_CAN_TRANSCEIVER functional modes using gpios and sets the attribute max link rate, for CAN drivers. =20 +config PHY_ECONET_USB + tristate "EcoNet USB PHY driver" + depends on ECONET || COMPILE_TEST + depends on OF + select GENERIC_PHY + select REGMAP_MMIO + help + Enable support for the USB PHY on EcoNet EN751221, EN751627, and + EN7528 SoCs. These PHYs support USB 2.0 and USB 3.0. SoCs typically + have two ports, one supporting USB 2.0 only and the other supporting + USB 3.0. Though some chip configurations have only the 2.0 capable + port and some only have the 3.0 capable port. + config PHY_GOOGLE_USB tristate "Google Tensor SoC USB PHY driver" select GENERIC_PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f49d83f00a3d..8a58bb151198 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_PHY) +=3D phy-core.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) +=3D phy-core-mipi-dphy.o obj-$(CONFIG_PHY_AIROHA_PCIE) +=3D phy-airoha-pcie.o obj-$(CONFIG_PHY_CAN_TRANSCEIVER) +=3D phy-can-transceiver.o +obj-$(CONFIG_PHY_ECONET_USB) +=3D phy-econet-usb.o obj-$(CONFIG_PHY_GOOGLE_USB) +=3D phy-google-usb.o obj-$(CONFIG_USB_LGM_PHY) +=3D phy-lgm-usb.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) +=3D phy-lpc18xx-usb-otg.o diff --git a/drivers/phy/phy-econet-usb.c b/drivers/phy/phy-econet-usb.c new file mode 100644 index 000000000000..e8fa2e80eb13 --- /dev/null +++ b/drivers/phy/phy-econet-usb.c @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Benjamin Larsson + * Author: Caleb James DeLisle + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SSUSB SIFSLV SPLLC (0x0) */ +#define SPLLC 0x00 + +/* SSUSB SIFSLV FMREG (0x100) offset based on SSUSB SIFSLV SPLLC */ +#define FMREG 0x100 +#define FMCR0 0x00 +#define RG_MONCLK_SEL GENMASK(27, 26) +#define RG_FREQDET_EN BIT(24) +#define RG_CYCLECNT GENMASK(23, 0) +#define FMMONR0 0x0c +#define RG_FM_OUT GENMASK(31, 0) +#define FMMONR1 0x10 +#define RG_FM_VALID BIT(0) +#define RG_FRCK_EN BIT(8) + +/* SSUSB SIFSLV U2PHY COM (0x800, 0x1000) */ +#define U2PHY_COM 0x00 +#define USBPHYACR0 0x10 +#define RG_HSTX_SRCAL_EN BIT(23) +#define RG_HSTX_SRCTRL GENMASK(18, 16) +#define USBPHYACR3 0x1C +#define RG_PUPD GENMASK(12, 8) +#define RG_PUPD_EN BIT(12) +#define RG_PUPD_PD_DM BIT(10) +#define RG_PUPD_PD_DP BIT(8) +#define RG_REV GENMASK(7, 0) +#define REVISION_P0 FIELD_PREP(RG_REV, 0x08) +#define REVISION_P0_DOWN FIELD_PREP(RG_REV, 0x80) +#define REVISION_P1 FIELD_PREP(RG_REV, 0x00) +#define U2PHYDCR1 0x64 +#define RG_USB20_SW_PLLMODE GENMASK(19, 18) + +/* SSUSB SIFSLV U3PHYD (0x900) */ +#define U3PHYD 0x00 +#define LFPS1 0x0c +#define RG_SSUSB_FWAKE_TH GENMASK(21, 16) + +/* SSUSB SIFSLV U3PHYD BANK2 (0xA00) offset based on SSUSB SIFSLV U3PHYD */ +#define U3PHYD_BANK2 0x100 +#define B2_PHYD_RXDET1 0x028 +#define RG_SSUSB_RXDET_STB2_SET GENMASK(17, 9) +#define B2_PHYD_RXDET2 0x02C +#define RG_SSUSB_RXDET_STB2_SET_P3 GENMASK(8, 0) + +/* SSUSB SIFSLV U3PHYA (0xB00) offset based on SSUSB SIFSLV U3PHYD */ +#define U3PHYA 0x200 +#define U3PHYA_REG2 0x008 +#define RG_SSUSB_SYSPLL_LF BIT(31) +#define RG_SSUSB_SYSPLL_FBDIV GENMASK(30, 24) +#define RG_SSUSB_SYSPLL_POSDIV GENMASK(23, 22) +#define RG_SSUSB_SYSPLL_VCO_DIV_SEL BIT(21) +#define RG_SSUSB_SYSPLL_BLP BIT(20) +#define RG_SSUSB_SYSPLL_BP BIT(19) +#define RG_SSUSB_SYSPLL_BR BIT(18) +#define RG_SSUSB_SYSPLL_BC BIT(17) +#define RG_SSUSB_SYSPLL_DIVEN GENMASK(16, 14) +#define RG_SSUSB_SYSPLL_FPEN BIT(13) +#define RG_SSUSB_SYSPLL_MONCK_EN BIT(12) +#define RG_SSUSB_SYSPLL_MONVC_EN BIT(11) +#define RG_SSUSB_SYSPLL_MONREF_EN BIT(10) +#define RG_SSUSB_SYSPLL_VOD_EN BIT(9) +#define RG_SSUSB_SYSPLL_CK_SEL BIT(8) +#define U3PHYA_REG4 0x010 +#define RG_SSUSB_SYSPLL_PCW_NCPO GENMASK(31, 1) +#define U3PHYA_REG9 0x024 +#define RG_SSUSB_PLL_DDS_DMY GENMASK(31, 16) +#define RG_SSUSB_PLL_SSC_PRD GENMASK(15, 0) +#define U3PHYA_REGB 0x02c +#define RG_SSUSB_RX_50_TAR GENMASK(13, 12) + +/* SSUSB SIFSLV U3PHYA DA (0xC00) offset based on SSUSB SIFSLV U3PHYD */ +#define U3PHYA_DA 0x300 +#define U3PHYA_DA_REG9 0x01C +#define RG_SSUSB_PLL_FBKDIV_PE2H GENMASK(30, 24) +#define RG_SSUSB_PLL_FBKDIV_PE1D GENMASK(22, 16) +#define RG_SSUSB_PLL_FBKDIV_PE1H GENMASK(14, 8) +#define RG_SSUSB_PLL_FBKDIV_U3 GENMASK(6, 0) +#define U3PHYA_DA_REG12 0x024 +#define RG_SSUSB_PLL_PCW_NCPO_U3 GENMASK(30, 0) +#define U3PHYA_DA_REG13 0x028 +#define RG_SSUSB_PLL_PCW_NCPO_PE1H GENMASK(30, 0) +#define U3PHYA_DA_REG14 0x02C +#define RG_SSUSB_PLL_PCW_NCPO_PE1D GENMASK(30, 0) +#define U3PHYA_DA_REG15 0x030 +#define RG_SSUSB_PLL_PCW_NCPO_PE2H GENMASK(30, 0) +#define U3PHYA_DA_REG19 0x038 +#define RG_SSUSB_PLL_SSC_DELTA1_PE1H GENMASK(31, 16) +#define RG_SSUSB_PLL_SSC_DELTA1_U3 GENMASK(15, 0) +#define U3PHYA_DA_REG20 0x03C +#define RG_SSUSB_PLL_SSC_DELTA1_PE2H GENMASK(31, 16) +#define RG_SSUSB_PLL_SSC_DELTA1_PE1D GENMASK(15, 0) +#define U3PHYA_DA_REG21 0x040 +#define RG_SSUSB_PLL_SSC_DELTA_U3 GENMASK(31, 16) +#define RG_SSUSB_PLL_SSC_DELTA1_PE2D GENMASK(15, 0) +#define U3PHYA_DA_REG23 0x044 +#define RG_SSUSB_PLL_SSC_DELTA_PE1D GENMASK(31, 16) +#define RG_SSUSB_PLL_SSC_DELTA_PE1H GENMASK(15, 0) +#define U3PHYA_DA_REG25 0x048 +#define RG_SSUSB_PLL_SSC_DELTA_PE2D GENMASK(31, 16) +#define RG_SSUSB_PLL_SSC_DELTA_PE2H GENMASK(15, 0) + +#define ECONET_USB_PHY_U2_FM_DET_CYCLE_CNT 1024 +#define ECONET_USB_PHY_REF_CK 20 +#define ECONET_USB_PHY_U2_SR_COEF 28 +#define ECONET_USB_PHY_U2_SR_COEF_DIVISOR 1000 +#define ECONET_USB_PHY_U2_SR_DEFAULT 0x4 + +#define ECONET_USB_PHY_FREQDET_SLEEP 1000 /* 1ms */ +#define ECONET_USB_PHY_FREQDET_TIMEOUT (ECONET_USB_PHY_FREQDET_SLEEP * 10) + +#define ECONET_USB_PHY_MAX_INSTANCE 3 + +#define ECONET_PORT_ID_FIELD "econet,usb-port-id" + +enum econet_phy_soc { + SOC_EN751221 =3D 1, + SOC_EN751627, + SOC_EN7528, +}; + +/** + * econet_phy_instance - the individual PHY block + * @phy: The phy object + * @regmap: A regmap for the phy block, per DT subnode reg field + * @type: PHY_TYPE_USB2 / PHY_TYPE_USB3 depending on PHY block + * @port_id: Number of the physical port [0,1] + * + * There are typically 3 PHY blocks, 2x USB 2.0 blocks, and 1x USB 3.0. Th= e USB + * 3.0 PHY is piggybacked on top of one of the USB2 ports and activates wh= en + * the connected device negotiates a USB 3.0 link. Main actions like power= ing + * on the port are governed in the USB 2.0 block. + */ +struct econet_phy_instance { + struct phy *phy; + struct regmap *regmap; + int type; + int port_id; +}; + +/** + * econet_usb_phy_priv the main PHY driver structure + * @dev: The root device + * @regmap: A regmap of registers that are shared between the PHY blocks + * @clk: The system Xtal clock, if 25Mhz then we need additional configura= tion + * @phys: The individual PHY blocks, normally there are three: Two USB 2.0= and + * one USB 3.0 which is shared on the same port at one of the USB 2= .0. + * @soc: The ID of the device, which is relevant to some of the tuning rit= ual. + */ +struct econet_usb_phy_priv { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct econet_phy_instance phys[ECONET_USB_PHY_MAX_INSTANCE]; + enum econet_phy_soc soc; +}; + +static void u2_slew_rate_calibration(struct econet_usb_phy_priv *priv, + struct econet_phy_instance *instance) +{ + struct regmap *com =3D instance->regmap; + struct regmap *regmap =3D priv->regmap; + int fm_out =3D 0; + u32 srctrl; + + /* Enable HS TX SR calibration */ + regmap_set_bits(com, U2PHY_COM + USBPHYACR0, RG_HSTX_SRCAL_EN); + + usleep_range(1000, 1500); + + /* Enable Free run clock */ + regmap_set_bits(regmap, FMREG + FMMONR1, RG_FRCK_EN); + + /* Select Monitor Clock */ + regmap_update_bits(regmap, FMREG + FMCR0, RG_MONCLK_SEL, + FIELD_PREP(RG_MONCLK_SEL, instance->port_id)); + + /* Set cyclecnt */ + regmap_update_bits(regmap, FMREG + FMCR0, RG_CYCLECNT, + FIELD_PREP_CONST(RG_CYCLECNT, + ECONET_USB_PHY_U2_FM_DET_CYCLE_CNT)); + + /* Enable Frequency meter */ + regmap_set_bits(regmap, FMREG + FMCR0, RG_FREQDET_EN); + + /* Timeout can happen and we will apply workaround at the end */ + regmap_read_poll_timeout(regmap, FMREG + FMMONR0, fm_out, + fm_out, ECONET_USB_PHY_FREQDET_SLEEP, + ECONET_USB_PHY_FREQDET_TIMEOUT); + + /* Disable Frequency meter */ + regmap_clear_bits(regmap, FMREG + FMCR0, RG_FREQDET_EN); + + /* Disable Free run clock */ + regmap_clear_bits(regmap, FMREG + FMMONR1, RG_FRCK_EN); + + /* Disable HS TX SR calibration */ + regmap_clear_bits(com, U2PHY_COM + USBPHYACR0, RG_HSTX_SRCAL_EN); + + usleep_range(1000, 1500); + + if (!fm_out) { + srctrl =3D ECONET_USB_PHY_U2_SR_DEFAULT; + dev_warn(&instance->phy->dev, + "frequency detection failed, using default slew rate 0x%x\n", + srctrl); + } else { + /* (1024 / FM_OUT) * REF_CK * U2_SR_COEF (round to nearest) */ + srctrl =3D ECONET_USB_PHY_REF_CK * ECONET_USB_PHY_U2_SR_COEF; + srctrl =3D (srctrl * ECONET_USB_PHY_U2_FM_DET_CYCLE_CNT) / fm_out; + srctrl =3D DIV_ROUND_CLOSEST(srctrl, + ECONET_USB_PHY_U2_SR_COEF_DIVISOR); + dev_info(&instance->phy->dev, + "frequency meter result 0x%x, set slew rate 0x%x\n", + fm_out, srctrl); + } + + /* Set slew rate */ + regmap_update_bits(com, U2PHY_COM + USBPHYACR0, RG_HSTX_SRCTRL, + FIELD_PREP(RG_HSTX_SRCTRL, srctrl)); +} + +static int econet_usb_phy_u2_init(struct econet_usb_phy_priv *priv, + struct econet_phy_instance *instance) +{ + struct regmap *com =3D instance->regmap; + + regmap_update_bits(com, U2PHY_COM + USBPHYACR3, RG_PUPD | RG_REV, + instance->port_id =3D=3D 0 ? REVISION_P0 : REVISION_P1); + + /* set SW PLL Stable mode to 1 for U2 LPM device remote wakeup */ + if (priv->soc !=3D SOC_EN7528) + regmap_update_bits(com, U2PHY_COM + U2PHYDCR1, + RG_USB20_SW_PLLMODE, + FIELD_PREP(RG_USB20_SW_PLLMODE, 0x1)); + + u2_slew_rate_calibration(priv, instance); + + usleep_range(1000, 1500); + + return 0; +} + +static int u3_setup_25mhz_xtal(struct econet_usb_phy_priv *priv, + struct econet_phy_instance *instance) +{ + struct regmap *u3phyd =3D instance->regmap; + + /* Setup 25MHz XTAL */ + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG9, + RG_SSUSB_PLL_FBKDIV_U3, + FIELD_PREP(RG_SSUSB_PLL_FBKDIV_U3, 0x18)); + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG9, + RG_SSUSB_PLL_FBKDIV_PE1H, + FIELD_PREP(RG_SSUSB_PLL_FBKDIV_PE1H, 0x18)); + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG9, + RG_SSUSB_PLL_FBKDIV_PE2H, + FIELD_PREP(RG_SSUSB_PLL_FBKDIV_PE2H, 0x18)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG12, + RG_SSUSB_PLL_PCW_NCPO_U3, + FIELD_PREP(RG_SSUSB_PLL_PCW_NCPO_U3, 0x18000000)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG13, + RG_SSUSB_PLL_PCW_NCPO_PE1H, + FIELD_PREP(RG_SSUSB_PLL_PCW_NCPO_PE1H, 0x18000000)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG15, + RG_SSUSB_PLL_PCW_NCPO_PE2H, + FIELD_PREP(RG_SSUSB_PLL_PCW_NCPO_PE2H, 0x18000000)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG19, + RG_SSUSB_PLL_SSC_DELTA1_PE1H, + FIELD_PREP(RG_SSUSB_PLL_SSC_DELTA1_PE1H, 0x4a)); + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG19, + RG_SSUSB_PLL_SSC_DELTA1_U3, + FIELD_PREP(RG_SSUSB_PLL_SSC_DELTA1_U3, 0x4a)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG20, + RG_SSUSB_PLL_SSC_DELTA1_PE2H, + FIELD_PREP(RG_SSUSB_PLL_SSC_DELTA1_PE2H, 0x4a)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG21, + RG_SSUSB_PLL_SSC_DELTA_U3, + FIELD_PREP(RG_SSUSB_PLL_SSC_DELTA_U3, 0x48)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG23, + RG_SSUSB_PLL_SSC_DELTA_PE1H, + FIELD_PREP(RG_SSUSB_PLL_SSC_DELTA_PE1H, 0x48)); + + regmap_update_bits(u3phyd, U3PHYA_DA + U3PHYA_DA_REG25, + RG_SSUSB_PLL_SSC_DELTA_PE2H, + FIELD_PREP(RG_SSUSB_PLL_SSC_DELTA_PE2H, 0x48)); + + regmap_update_bits(u3phyd, U3PHYA + U3PHYA_REG9, + RG_SSUSB_PLL_SSC_PRD, + FIELD_PREP(RG_SSUSB_PLL_SSC_PRD, 0x190)); + regmap_update_bits(u3phyd, U3PHYA + U3PHYA_REG4, + RG_SSUSB_SYSPLL_PCW_NCPO, + FIELD_PREP(RG_SSUSB_SYSPLL_PCW_NCPO, 0xe000000)); + regmap_update_bits(u3phyd, U3PHYA + U3PHYA_REG2, + RG_SSUSB_SYSPLL_FBDIV, + FIELD_PREP(RG_SSUSB_SYSPLL_FBDIV, 0xe)); + return 0; +} + +static int econet_usb_phy_u3_init(struct econet_usb_phy_priv *priv, + struct econet_phy_instance *instance) +{ + struct regmap *u3phyd =3D instance->regmap; + + if (priv->soc =3D=3D SOC_EN751221) { + /* Patch TxDetRx Timing for E1 */ + regmap_update_bits(u3phyd, U3PHYD_BANK2 + B2_PHYD_RXDET1, + RG_SSUSB_RXDET_STB2_SET, + FIELD_PREP(RG_SSUSB_RXDET_STB2_SET, 0x10)); + + regmap_update_bits(u3phyd, U3PHYD_BANK2 + B2_PHYD_RXDET2, + RG_SSUSB_RXDET_STB2_SET_P3, + FIELD_PREP(RG_SSUSB_RXDET_STB2_SET_P3, 0x10)); + + /* Patch LFPS Filter Threshold for E1 */ + regmap_update_bits(u3phyd, U3PHYD + LFPS1, RG_SSUSB_FWAKE_TH, + FIELD_PREP(RG_SSUSB_FWAKE_TH, 0x34)); + + } else if (priv->soc =3D=3D SOC_EN7528) { + /* combo phy Rx R FT mean value too high, target R -5 Ohm */ + regmap_update_bits(u3phyd, U3PHYA + U3PHYA_REGB, + RG_SSUSB_RX_50_TAR, + FIELD_PREP(RG_SSUSB_RX_50_TAR, 0x01)); + } + + if (clk_get_rate(priv->clk) =3D=3D 25 * 1000 * 1000) + u3_setup_25mhz_xtal(priv, instance); + + return 0; +} + +static int econet_usb_phy_init(struct phy *phy) +{ + struct econet_phy_instance *instance =3D phy_get_drvdata(phy); + struct econet_usb_phy_priv *priv =3D dev_get_drvdata(phy->dev.parent); + + if (instance->type =3D=3D PHY_TYPE_USB2) + return econet_usb_phy_u2_init(priv, instance); + else if (instance->type =3D=3D PHY_TYPE_USB3) + return econet_usb_phy_u3_init(priv, instance); + + return -EINVAL; +} + +static int econet_usb_phy_exit(struct phy *phy) +{ + struct econet_phy_instance *instance =3D phy_get_drvdata(phy); + + /* + * Disable the port by setting PHY revision to REVISION_P0_DOWN and + * enabling pulldown resistors on D+ and D- lines. The USB 3.0 PHY + * piggybacks on top of the USB 2.0 for the same port and does not + * have any such switch. + */ + if (instance->type =3D=3D PHY_TYPE_USB2) + return regmap_update_bits(instance->regmap, + U2PHY_COM + USBPHYACR3, + RG_PUPD | RG_REV, + RG_PUPD_EN | RG_PUPD_PD_DM | + RG_PUPD_PD_DP | REVISION_P0_DOWN); + + return 0; +} + +static struct phy *econet_usb_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct econet_usb_phy_priv *priv =3D dev_get_drvdata(dev); + struct device_node *phy_np =3D args->np; + int index; + + if (!of_device_is_available(phy_np)) { + dev_err(dev, "can't use disabled phy\n"); + return ERR_PTR(-EINVAL); + } + + for (index =3D 0; index < ARRAY_SIZE(priv->phys); index++) { + /* Empty slots if the DT doesn't specify all subnodes */ + if (!priv->phys[index].phy) + continue; + + if (phy_np !=3D priv->phys[index].phy->dev.of_node) + continue; + + return priv->phys[index].phy; + } + + dev_err(dev, "failed to find phy\n"); + return ERR_PTR(-EINVAL); +} + +static const struct phy_ops econet_phy =3D { + .init =3D econet_usb_phy_init, + .exit =3D econet_usb_phy_exit, + .owner =3D THIS_MODULE, +}; + +static const struct regmap_config econet_usb_phy_regmap_config =3D { + .reg_bits =3D 32, + .val_bits =3D 32, + .reg_stride =3D 4, +}; + +static const struct of_device_id econet_phy_id_table[] =3D { + { .compatible =3D "econet,en751221-usb-phy", .data =3D (void *) SOC_EN751= 221 }, + { .compatible =3D "econet,en751627-usb-phy", .data =3D (void *) SOC_EN751= 627 }, + { .compatible =3D "econet,en7528-usb-phy", .data =3D (void *) SOC_EN7528 = }, + { }, +}; +MODULE_DEVICE_TABLE(of, econet_phy_id_table); + +static int econet_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct device_node *np =3D dev->of_node; + struct econet_usb_phy_priv *priv; + const struct of_device_id *match; + struct phy_provider *provider; + void *base; + int phy_n; + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev =3D dev; + + base =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "getting reg\n"); + + platform_set_drvdata(pdev, priv); + priv->regmap =3D devm_regmap_init_mmio(dev, base, &econet_usb_phy_regmap_= config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->clk =3D devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "getting clk\n"); + + match =3D of_match_node(econet_phy_id_table, np); + if (!match || !match->data) + return dev_err_probe(dev, -EINVAL, "no OF match data\n"); + + priv->soc =3D (enum econet_phy_soc)(uintptr_t)match->data; + + phy_n =3D 0; + for_each_child_of_node_scoped(np, child_np) { + struct econet_phy_instance *instance; + void __iomem *phy_base; + struct device *subdev; + struct resource res; + int retval; + + if (phy_n >=3D ARRAY_SIZE(priv->phys)) + return dev_err_probe(dev, -EINVAL, + "too many PHY subnodes\n"); + + instance =3D &priv->phys[phy_n]; + + if (of_device_is_compatible(child_np, "econet,usb2-phy")) + instance->type =3D PHY_TYPE_USB2; + else if (of_device_is_compatible(child_np, "econet,usb3-phy")) + instance->type =3D PHY_TYPE_USB3; + else + return dev_err_probe(dev, -EINVAL, + "phy %d unknown type\n", phy_n); + + instance->phy =3D devm_phy_create(dev, child_np, &econet_phy); + if (IS_ERR(instance->phy)) + return dev_err_probe(dev, PTR_ERR(instance->phy), + "failed to create phy\n"); + + subdev =3D &instance->phy->dev; + retval =3D of_address_to_resource(child_np, 0, &res); + if (retval) + return dev_err_probe(subdev, retval, + "getting reg for phy %d\n", + phy_n); + + phy_base =3D devm_ioremap_resource(subdev, &res); + if (IS_ERR(phy_base)) + return dev_err_probe(subdev, PTR_ERR(phy_base), + "remapping reg for phy %d\n", + phy_n); + + instance->regmap =3D + devm_regmap_init_mmio(subdev, phy_base, + &econet_usb_phy_regmap_config); + if (IS_ERR(instance->regmap)) + return dev_err_probe(subdev, PTR_ERR(instance->regmap), + "making regmap for phy %d\n", + phy_n); + + retval =3D device_property_read_u32(subdev, ECONET_PORT_ID_FIELD, + &instance->port_id); + if (retval) + return dev_err_probe(subdev, retval, + "getting %s for phy %d\n", + ECONET_PORT_ID_FIELD, phy_n); + + phy_set_drvdata(instance->phy, instance); + + /* + * Initial setup ritual disables both ports. On QFP silicon, + * only one port is wired (port1) but port0 still has a PHY + * present which is still disabled in setup. + */ + retval =3D econet_usb_phy_exit(instance->phy); + if (retval) + dev_warn(subdev, "stopping phy %d: %pe\n", phy_n, + ERR_PTR(retval)); + + phy_n++; + } + + provider =3D devm_of_phy_provider_register(dev, econet_usb_phy_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static struct platform_driver econet_usb_phy_driver =3D { + .probe =3D econet_usb_phy_probe, + .driver =3D { + .name =3D "econet-usb-phy", + .of_match_table =3D econet_phy_id_table, + }, +}; + +module_platform_driver(econet_usb_phy_driver); + +MODULE_AUTHOR("Benjamin Larsson "); +MODULE_AUTHOR("Caleb James DeLisle "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Econet USB PHY driver"); --=20 2.39.5