From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id D363BCCF9EE for ; Wed, 29 Oct 2025 17:37:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:To:From:Reply-To: Cc:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=mBOV5y14zLJY461SlCRrfLskZTeD8oaRyeqNtZ5L1HE=; b=2BHlvJB4yW+W8vQTLtL+txTU6q u0mGrLFBjXzjVWrPSjWsrMn7qYcrgtef2HuDAQchIuf9HIrn70vKEuOwkoUpyqrivy8wL+jN4et2a DzjjWY6pUkREztB0b0Fs+6Z9y6j6NwD4dEen7xQCof4fvB/wfJpe9loOj+9iLrPyrGzweCGPbM5Ks OMsxXDr3jM4lD/KLOG/Ds2FdYFll/N3V7b/lcDY9PThfywJkoxYfNI1VK1Jjc+bIUmPqwY2rhhsml vfvV2pjGHfvgFU+jTd5MBO+D/StAYOPrlAZZ+HjZ6B2R75UVpljQGQnNjCwOAbi0BB2Ty9zgNSfIM rFn8wWFw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1vEA7B-00000002E9J-2siR; Wed, 29 Oct 2025 17:37:41 +0000 Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1vEA70-00000002E34-23Dl for linux-arm-kernel@lists.infradead.org; Wed, 29 Oct 2025 17:37:32 +0000 Received: by mail-wm1-x32f.google.com with SMTP id 5b1f17b1804b1-4771b03267bso892385e9.0 for ; Wed, 29 Oct 2025 10:37:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1761759449; x=1762364249; darn=lists.infradead.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=mBOV5y14zLJY461SlCRrfLskZTeD8oaRyeqNtZ5L1HE=; b=aqR1bTi1s+qHfCUfINhAE3f/lrVvCAcHkrjYA8o72ybEkl2JTmIWPVseimlygL7S2P RxtNIMEsxcYTQNETpXkyOWlf3/oF30UlFHrt2daevoeKFzmtVLdUwjqOflB0tcY6Ztsn +V2ZUABYiJRqrnkefIMH6K3TPDqiN96SupiyF5GE4rnchZMY54xsIZRA+c/3Sz8jtU2G WT187PywBQcX16j0K+F+hVeFYYT0j3WKMJ0wZhucm7Tuv3NQI5+n5RKWp8ay1TkzGcf5 V7T596CRljJeLLf0DRYEIsm6rdTBVjiS9/b5yoAhTJhn0y+1aoTwx+atZW3APP+xTwS3 OM+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761759449; x=1762364249; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mBOV5y14zLJY461SlCRrfLskZTeD8oaRyeqNtZ5L1HE=; b=tvRNieIDZekJbAQCUgS/7bpFpbuBM1L8jS+c92w/i2no41ubey5teY4U88pQbA9Y20 4ziPtd0zW1VsEhGCL6PEYFM70kc0OGiw0MzajVYv1j7QpqRm+ql4fBS8Zee6WFGcv6M2 w6AuRrTEMaNp84iBl4s6Ib2JxJl0FEUEOp198laZVeT6+04YEndC4DfbAamOTnRkunIB D9q5Imv0EcUON9Lxyq1LxBliBnbeXy9QkHn8Lp7Huw3EYy7pDi/Q1fFIBw9xFICzejNm RRqpPGZ2SrIqsNYw8mB5hvF395m07ni5EGRsh5r2fkxbuz6dPS3FnLbOIooX3Xj6raFZ ja5g== X-Forwarded-Encrypted: i=1; AJvYcCWhRlS2L4V4KullVKAfBa9mFxdiaEHbQRtuJj+Ij+CzG2ZmJ6Qv87ULXrgpnMfpqlBax1Wa15LzAQOvkv2k9ipl@lists.infradead.org X-Gm-Message-State: AOJu0YxBM3sTbHd73nfaB4CqnkLzH/i2pu4KHfOgQlz8E4SNGuZM5ecw JT6RFF/YM3NXbxu4wBaUUlk7s0ZiW/vYiUvGW5PsgobhneFud4XYaiyE X-Gm-Gg: ASbGncvvCzJYhrAttBShbTBQY2QYXd4H3IFLBOOU+lOdqX5kmj+SmJpzu1DRY4nJ0Ld hREtCqwjSJ1VAYyaYXl3xXUEiFuGmnwMWoMM33JTAeQxIn/PIDHAF/I82oiykRqplJxiJqOKzPH bwUK+eHUBIR05DnVRfhJLhgh6W/+EnPgsiOTVEmMIGPHm372UgA8+YLzQuN/MtsCd7jp5616O91 5bF3wecPyjBmBySviuMwF3TORVGoSmdM70MXqvNCmQb60iEkZ9KrnWXhSgNJVkbgzOMLsxAWJ6H oW4/9qzkwMhPEEs978s7Be/4lQD0ueYDpc7xVyhOo7N/hn1AqOeka21KEcbHVCjP2e3it20GrrL J8xqGzcwacUBR+ZeDl7nhk1cOlt+IubD19RuKJbvL0wosORJP3y00x3FEmfhURA55jxuPRjYxEz azwFIcvqEUuTQ3h3qKW2pznUEeBKD+DQ== X-Google-Smtp-Source: AGHT+IHIdt+Y6vepqTCorK7FZTNvF1I3+IkB9WCPc3/F3TOwHBHlx42aT3+kkqaVB8QlNGJipSyajA== X-Received: by 2002:a05:600c:608e:b0:46e:432f:32ab with SMTP id 5b1f17b1804b1-4772684ed85mr1577445e9.33.1761759448397; Wed, 29 Oct 2025 10:37:28 -0700 (PDT) Received: from Ansuel-XPS24 (93-34-90-37.ip49.fastwebnet.it. [93.34.90.37]) by smtp.googlemail.com with ESMTPSA id ffacd0b85a97d-429952d9e80sm27493844f8f.28.2025.10.29.10.37.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Oct 2025 10:37:27 -0700 (PDT) From: Christian Marangi To: Christian Marangi , Vinod Koul , Kishon Vijay Abraham I , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Lorenzo Bianconi , linux-arm-kernel@lists.infradead.org, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 4/4] phy: airoha: Add support for Airoha AN7581 USB PHY Date: Wed, 29 Oct 2025 18:37:12 +0100 Message-ID: <20251029173713.7670-5-ansuelsmth@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251029173713.7670-1-ansuelsmth@gmail.com> References: <20251029173713.7670-1-ansuelsmth@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20251029_103730_756963_7962FD86 X-CRM114-Status: GOOD ( 24.81 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Add support for Airoha AN7581 USB PHY driver. AN7581 supports up to 2 USB port with USB 2.0 mode always supported and USB 3.0 mode available only if the Serdes port is correctly configured for USB 3.0. The first USB port on the SoC can be both used for USB 3.0 operation or Ethernet. The second USB port on the SoC can be both used for USB 3.0 operation or PCIe. Both port operation toggled by the SCU SSR register and configured by the USB PHY driver. If the USB 3.0 mode is not configured, the modes needs to be also disabled in the xHCI node or the driver will report unsable clock and fail probe. For USB 2.0 Slew Rate calibration, airoha,usb2-monitor-clk-sel is mandatory and is used to select the monitor clock for calibration. Normally it's 1 for USB port 1 and 2 for USB port 2. Signed-off-by: Christian Marangi --- MAINTAINERS | 1 + drivers/phy/airoha/Kconfig | 10 + drivers/phy/airoha/Makefile | 1 + drivers/phy/airoha/phy-airoha-usb.c | 615 ++++++++++++++++++++++++++++ scripts/tracepoint-update | Bin 0 -> 22952 bytes 5 files changed, 627 insertions(+) create mode 100644 drivers/phy/airoha/phy-airoha-usb.c create mode 100755 scripts/tracepoint-update diff --git a/MAINTAINERS b/MAINTAINERS index 0857baa8d5bb..cbfc9b1f7dd6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -768,6 +768,7 @@ M: Christian Marangi L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/phy/airoha,an7581-usb-phy.yaml +F: drivers/phy/airoha/phy-airoha-usb.c F: include/dt-bindings/phy/airoha,an7581-usb-phy.h AIRSPY MEDIA DRIVER diff --git a/drivers/phy/airoha/Kconfig b/drivers/phy/airoha/Kconfig index 70b7eac4a2bf..0675d8f2f9d1 100644 --- a/drivers/phy/airoha/Kconfig +++ b/drivers/phy/airoha/Kconfig @@ -11,3 +11,13 @@ config PHY_AIROHA_PCIE Say Y here to add support for Airoha PCIe PHY driver. This driver create the basic PHY instance and provides initialize callback for PCIe GEN3 port. + +config PHY_AIROHA_USB + tristate "Airoha USB PHY Driver" + depends on ARCH_AIROHA || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Say 'Y' here to add support for Airoha USB PHY driver. + This driver create the basic PHY instance and provides initialize + callback for USB port. diff --git a/drivers/phy/airoha/Makefile b/drivers/phy/airoha/Makefile index 3222f749546b..fd188d08c412 100644 --- a/drivers/phy/airoha/Makefile +++ b/drivers/phy/airoha/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o +obj-$(CONFIG_PHY_AIROHA_USB) += phy-airoha-usb.o diff --git a/drivers/phy/airoha/phy-airoha-usb.c b/drivers/phy/airoha/phy-airoha-usb.c new file mode 100644 index 000000000000..50950e6de771 --- /dev/null +++ b/drivers/phy/airoha/phy-airoha-usb.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Christian Marangi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SCU */ +#define AIROHA_SCU_SSR3 0x94 +#define AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL BIT(29) +#define AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL_HSGMII FIELD_PREP_CONST(AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL, 0x1) +#define AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL_USB FIELD_PREP_CONST(AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL, 0x1) +#define AIROHA_SCU_SSTR 0x9c +#define AIROHA_SCU_SSTR_USB_PCIE_SEL BIT(3) +#define AIROHA_SCU_SSTR_USB_PCIE_SEL_PCIE FIELD_PREP_CONST(AIROHA_SCU_SSTR_USB_PCIE_SEL, 0x0) +#define AIROHA_SCU_SSTR_USB_PCIE_SEL_USB FIELD_PREP_CONST(AIROHA_SCU_SSTR_USB_PCIE_SEL, 0x1) + +/* U2PHY */ +#define AIROHA_USB_PHY_FMCR0 0x100 +#define AIROHA_USB_PHY_MONCLK_SEL GENMASK(27, 26) +#define AIROHA_USB_PHY_MONCLK_SEL0 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x0) +#define AIROHA_USB_PHY_MONCLK_SEL1 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x1) +#define AIROHA_USB_PHY_MONCLK_SEL2 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x2) +#define AIROHA_USB_PHY_MONCLK_SEL3 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x3) +#define AIROHA_USB_PHY_FREQDET_EN BIT(24) +#define AIROHA_USB_PHY_CYCLECNT GENMASK(23, 0) +#define AIROHA_USB_PHY_FMMONR0 0x10c +#define AIROHA_USB_PHY_USB_FM_OUT GENMASK(31, 0) +#define AIROHA_USB_PHY_FMMONR1 0x110 +#define AIROHA_USB_PHY_FRCK_EN BIT(8) + +#define AIROHA_USB_PHY_USBPHYACR4 0x310 +#define AIROHA_USB_PHY_USB20_FS_CR GENMASK(10, 8) +#define AIROHA_USB_PHY_USB20_FS_CR_MAX FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x0) +#define AIROHA_USB_PHY_USB20_FS_CR_NORMAL FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x2) +#define AIROHA_USB_PHY_USB20_FS_CR_SMALLER FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x4) +#define AIROHA_USB_PHY_USB20_FS_CR_MIN FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x6) +#define AIROHA_USB_PHY_USB20_FS_SR GENMASK(2, 0) +#define AIROHA_USB_PHY_USB20_FS_SR_MAX FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x0) +#define AIROHA_USB_PHY_USB20_FS_SR_NORMAL FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x2) +#define AIROHA_USB_PHY_USB20_FS_SR_SMALLER FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x4) +#define AIROHA_USB_PHY_USB20_FS_SR_MIN FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x6) +#define AIROHA_USB_PHY_USBPHYACR5 0x314 +#define AIROHA_USB_PHY_USB20_HSTX_SRCAL_EN BIT(15) +#define AIROHA_USB_PHY_USB20_HSTX_SRCTRL GENMASK(14, 12) +#define AIROHA_USB_PHY_USBPHYACR6 0x318 +#define AIROHA_USB_PHY_USB20_BC11_SW_EN BIT(23) +#define AIROHA_USB_PHY_USB20_DISCTH GENMASK(7, 4) +#define AIROHA_USB_PHY_USB20_DISCTH_400 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x0) +#define AIROHA_USB_PHY_USB20_DISCTH_420 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x1) +#define AIROHA_USB_PHY_USB20_DISCTH_440 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x2) +#define AIROHA_USB_PHY_USB20_DISCTH_460 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x3) +#define AIROHA_USB_PHY_USB20_DISCTH_480 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x4) +#define AIROHA_USB_PHY_USB20_DISCTH_500 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x5) +#define AIROHA_USB_PHY_USB20_DISCTH_520 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x6) +#define AIROHA_USB_PHY_USB20_DISCTH_540 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x7) +#define AIROHA_USB_PHY_USB20_DISCTH_560 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x8) +#define AIROHA_USB_PHY_USB20_DISCTH_580 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x9) +#define AIROHA_USB_PHY_USB20_DISCTH_600 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xa) +#define AIROHA_USB_PHY_USB20_DISCTH_620 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xb) +#define AIROHA_USB_PHY_USB20_DISCTH_640 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xc) +#define AIROHA_USB_PHY_USB20_DISCTH_660 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xd) +#define AIROHA_USB_PHY_USB20_DISCTH_680 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xe) +#define AIROHA_USB_PHY_USB20_DISCTH_700 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xf) +#define AIROHA_USB_PHY_USB20_SQTH GENMASK(3, 0) +#define AIROHA_USB_PHY_USB20_SQTH_85 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x0) +#define AIROHA_USB_PHY_USB20_SQTH_90 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x1) +#define AIROHA_USB_PHY_USB20_SQTH_95 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x2) +#define AIROHA_USB_PHY_USB20_SQTH_100 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x3) +#define AIROHA_USB_PHY_USB20_SQTH_105 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x4) +#define AIROHA_USB_PHY_USB20_SQTH_110 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x5) +#define AIROHA_USB_PHY_USB20_SQTH_115 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x6) +#define AIROHA_USB_PHY_USB20_SQTH_120 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x7) +#define AIROHA_USB_PHY_USB20_SQTH_125 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x8) +#define AIROHA_USB_PHY_USB20_SQTH_130 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x9) +#define AIROHA_USB_PHY_USB20_SQTH_135 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xa) +#define AIROHA_USB_PHY_USB20_SQTH_140 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xb) +#define AIROHA_USB_PHY_USB20_SQTH_145 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xc) +#define AIROHA_USB_PHY_USB20_SQTH_150 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xd) +#define AIROHA_USB_PHY_USB20_SQTH_155 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xe) +#define AIROHA_USB_PHY_USB20_SQTH_160 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xf) + +#define AIROHA_USB_PHY_U2PHYDTM1 0x36c +#define AIROHA_USB_PHY_FORCE_IDDIG BIT(9) +#define AIROHA_USB_PHY_IDDIG BIT(1) + +#define AIROHA_USB_PHY_GPIO_CTLD 0x80c +#define AIROHA_USB_PHY_C60802_GPIO_CTLD GENMASK(31, 0) +#define AIROHA_USB_PHY_SSUSB_IP_SW_RST BIT(31) +#define AIROHA_USB_PHY_MCU_BUS_CK_GATE_EN BIT(30) +#define AIROHA_USB_PHY_FORCE_SSUSB_IP_SW_RST BIT(29) +#define AIROHA_USB_PHY_SSUSB_SW_RST BIT(28) + +#define AIROHA_USB_PHY_U3_PHYA_REG0 0xb00 +#define AIROHA_USB_PHY_SSUSB_BG_DIV GENMASK(29, 28) +#define AIROHA_USB_PHY_SSUSB_BG_DIV_2 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x0) +#define AIROHA_USB_PHY_SSUSB_BG_DIV_4 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x1) +#define AIROHA_USB_PHY_SSUSB_BG_DIV_8 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x2) +#define AIROHA_USB_PHY_SSUSB_BG_DIV_16 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x3) +#define AIROHA_USB_PHY_U3_PHYA_REG1 0xb04 +#define AIROHA_USB_PHY_SSUSB_XTAL_TOP_RESERVE GENMASK(25, 10) +#define AIROHA_USB_PHY_U3_PHYA_REG6 0xb18 +#define AIROHA_USB_PHY_SSUSB_CDR_RESERVE GENMASK(31, 24) +#define AIROHA_USB_PHY_U3_PHYA_REG8 0xb20 +#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY GENMASK(7, 6) +#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_32 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x0) +#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_64 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x1) +#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_128 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x2) +#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_216 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x3) + +#define AIROHA_USB_PHY_U3_PHYA_DA_REG19 0xc38 +#define AIROHA_USB_PHY_SSUSB_PLL_SSC_DELTA1_U3 GENMASK(15, 0) + +#define AIROHA_USB_PHY_U2_FM_DET_CYCLE_CNT 1024 +#define AIROHA_USB_PHY_REF_CK 20 +#define AIROHA_USB_PHY_U2_SR_COEF 28 +#define AIROHA_USB_PHY_U2_SR_COEF_DIVISOR 1000 + +#define AIROHA_USB_PHY_DEFAULT_SR_CALIBRATION 0x5 +#define AIROHA_USB_PHY_FREQDET_SLEEP 1000 /* 1ms */ +#define AIROHA_USB_PHY_FREQDET_TIMEOUT (AIROHA_USB_PHY_FREQDET_SLEEP * 10) + +struct airoha_usb_phy_instance { + struct phy *phy; + u32 type; +}; + +enum airoha_usb_phy_instance_type { + AIROHA_PHY_USB2, + AIROHA_PHY_USB3, + + AIROHA_PHY_USB_MAX, +}; + +struct airoha_usb_phy_priv { + struct device *dev; + struct regmap *regmap; + struct regmap *scu; + + unsigned int monclk_sel; + unsigned int serdes_port; + + struct airoha_usb_phy_instance *phys[AIROHA_PHY_USB_MAX]; +}; + +static void airoha_usb_phy_u2_slew_rate_calibration(struct airoha_usb_phy_priv *priv) +{ + u32 fm_out; + u32 srctrl; + + /* Enable HS TX SR calibration */ + regmap_set_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR5, + AIROHA_USB_PHY_USB20_HSTX_SRCAL_EN); + + usleep_range(1000, 1500); + + /* Enable Free run clock */ + regmap_set_bits(priv->regmap, AIROHA_USB_PHY_FMMONR1, + AIROHA_USB_PHY_FRCK_EN); + + /* Select Monitor Clock */ + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, + AIROHA_USB_PHY_MONCLK_SEL, + FIELD_PREP(AIROHA_USB_PHY_MONCLK_SEL, + priv->monclk_sel)); + + /* Set cyclecnt */ + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, + AIROHA_USB_PHY_CYCLECNT, + FIELD_PREP(AIROHA_USB_PHY_CYCLECNT, + AIROHA_USB_PHY_U2_FM_DET_CYCLE_CNT)); + + /* Enable Frequency meter */ + regmap_set_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, + AIROHA_USB_PHY_FREQDET_EN); + + /* Timeout can happen and we will apply workaround at the end */ + regmap_read_poll_timeout(priv->regmap, AIROHA_USB_PHY_FMMONR0, fm_out, + fm_out, AIROHA_USB_PHY_FREQDET_SLEEP, + AIROHA_USB_PHY_FREQDET_TIMEOUT); + + /* Disable Frequency meter */ + regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, + AIROHA_USB_PHY_FREQDET_EN); + + /* Disable Free run clock */ + regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_FMMONR1, + AIROHA_USB_PHY_FRCK_EN); + + /* Disable HS TX SR calibration */ + regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR5, + AIROHA_USB_PHY_USB20_HSTX_SRCAL_EN); + + usleep_range(1000, 1500); + + /* Frequency was not detected, use default SR calibration value */ + if (!fm_out) { + srctrl = AIROHA_USB_PHY_DEFAULT_SR_CALIBRATION; + dev_err(priv->dev, "Frequency not detected, using default SR calibration.\n"); + } else { + /* (1024 / FM_OUT) * REF_CK * U2_SR_COEF (round to the nearest digits) */ + srctrl = AIROHA_USB_PHY_REF_CK * AIROHA_USB_PHY_U2_SR_COEF; + srctrl = (srctrl * AIROHA_USB_PHY_U2_FM_DET_CYCLE_CNT) / fm_out; + srctrl = DIV_ROUND_CLOSEST(srctrl, AIROHA_USB_PHY_U2_SR_COEF_DIVISOR); + dev_dbg(priv->dev, "SR calibration applied: %x\n", srctrl); + } + + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR5, + AIROHA_USB_PHY_USB20_HSTX_SRCTRL, + FIELD_PREP(AIROHA_USB_PHY_USB20_HSTX_SRCTRL, srctrl)); +} + +static void airoha_usb_phy_u2_init(struct airoha_usb_phy_priv *priv) +{ + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR4, + AIROHA_USB_PHY_USB20_FS_CR, + AIROHA_USB_PHY_USB20_FS_CR_MIN); + + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR4, + AIROHA_USB_PHY_USB20_FS_SR, + AIROHA_USB_PHY_USB20_FS_SR_NORMAL); + + /* FIXME: evaluate if needed */ + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, + AIROHA_USB_PHY_USB20_SQTH, + AIROHA_USB_PHY_USB20_SQTH_130); + + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, + AIROHA_USB_PHY_USB20_DISCTH, + AIROHA_USB_PHY_USB20_DISCTH_600); + + /* Enable the USB port and then disable after calibration */ + regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, + AIROHA_USB_PHY_USB20_BC11_SW_EN); + + airoha_usb_phy_u2_slew_rate_calibration(priv); + + regmap_set_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, + AIROHA_USB_PHY_USB20_BC11_SW_EN); + + usleep_range(1000, 1500); +} + +/* + * USB 3.0 mode can only work if USB serdes is correctly set. + * This is validated in xLate function. + */ +static void airoha_usb_phy_u3_init(struct airoha_usb_phy_priv *priv) +{ + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG8, + AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, + AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_32); + + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG6, + AIROHA_USB_PHY_SSUSB_CDR_RESERVE, + FIELD_PREP(AIROHA_USB_PHY_SSUSB_CDR_RESERVE, 0xe)); + + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG0, + AIROHA_USB_PHY_SSUSB_BG_DIV, + AIROHA_USB_PHY_SSUSB_BG_DIV_4); + + regmap_set_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG1, + FIELD_PREP(AIROHA_USB_PHY_SSUSB_XTAL_TOP_RESERVE, 0x600)); + + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_DA_REG19, + AIROHA_USB_PHY_SSUSB_PLL_SSC_DELTA1_U3, + FIELD_PREP(AIROHA_USB_PHY_SSUSB_PLL_SSC_DELTA1_U3, 0x43)); +} + +static int airoha_usb_phy_init(struct phy *phy) +{ + struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); + struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + + switch (instance->type) { + case PHY_TYPE_USB2: + airoha_usb_phy_u2_init(priv); + break; + case PHY_TYPE_USB3: + if (phy_get_mode(phy) == PHY_MODE_PCIE) + return 0; + + airoha_usb_phy_u3_init(priv); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int airoha_usb_phy_u2_power_on(struct airoha_usb_phy_priv *priv) +{ + regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, + AIROHA_USB_PHY_USB20_BC11_SW_EN); + + usleep_range(1000, 1500); + + return 0; +} + +static int airoha_usb_phy_u3_power_on(struct airoha_usb_phy_priv *priv) +{ + regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_GPIO_CTLD, + AIROHA_USB_PHY_SSUSB_IP_SW_RST | + AIROHA_USB_PHY_MCU_BUS_CK_GATE_EN | + AIROHA_USB_PHY_FORCE_SSUSB_IP_SW_RST | + AIROHA_USB_PHY_SSUSB_SW_RST); + + usleep_range(1000, 1500); + + return 0; +} + +static int airoha_usb_phy_power_on(struct phy *phy) +{ + struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); + struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + + switch (instance->type) { + case PHY_TYPE_USB2: + airoha_usb_phy_u2_power_on(priv); + break; + case PHY_TYPE_USB3: + if (phy_get_mode(phy) == PHY_MODE_PCIE) + return 0; + + airoha_usb_phy_u3_power_on(priv); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int airoha_usb_phy_u2_power_off(struct airoha_usb_phy_priv *priv) +{ + regmap_set_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, + AIROHA_USB_PHY_USB20_BC11_SW_EN); + + usleep_range(1000, 1500); + + return 0; +} + +static int airoha_usb_phy_u3_power_off(struct airoha_usb_phy_priv *priv) +{ + regmap_set_bits(priv->regmap, AIROHA_USB_PHY_GPIO_CTLD, + AIROHA_USB_PHY_SSUSB_IP_SW_RST | + AIROHA_USB_PHY_FORCE_SSUSB_IP_SW_RST); + + usleep_range(1000, 1500); + + return 0; +} + +static int airoha_usb_phy_power_off(struct phy *phy) +{ + struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); + struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + + switch (instance->type) { + case PHY_TYPE_USB2: + airoha_usb_phy_u2_power_off(priv); + break; + case PHY_TYPE_USB3: + if (phy_get_mode(phy) == PHY_MODE_PCIE) + return 0; + + airoha_usb_phy_u3_power_off(priv); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int airoha_usb_phy_u2_set_mode(struct airoha_usb_phy_priv *priv, + enum phy_mode mode) +{ + u32 val; + + /* + * For Device and Host mode, enable force IDDIG. + * For Device set IDDIG, for Host clear IDDIG. + * For OTG disable force and clear IDDIG bit while at it. + */ + switch (mode) { + case PHY_MODE_USB_DEVICE: + val = AIROHA_USB_PHY_IDDIG; + break; + case PHY_MODE_USB_HOST: + val = AIROHA_USB_PHY_FORCE_IDDIG | + AIROHA_USB_PHY_FORCE_IDDIG; + break; + case PHY_MODE_USB_OTG: + val = 0; + break; + default: + return 0; + } + + regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U2PHYDTM1, + AIROHA_USB_PHY_FORCE_IDDIG | + AIROHA_USB_PHY_IDDIG, val); + + return 0; +} + +static int airoha_usb_phy_u3_set_mode(struct airoha_usb_phy_priv *priv, + enum phy_mode mode) +{ + u32 sel; + + /* Only USB1 supports Ethernet mode */ + if (mode == PHY_MODE_ETHERNET && + priv->serdes_port != AIROHA_SCU_SERDES_USB1) + return -EINVAL; + + /* Only USB2 supports PCIe mode */ + if (mode == PHY_MODE_PCIE && + priv->serdes_port != AIROHA_SCU_SERDES_USB2) + return -EINVAL; + + if (priv->serdes_port == AIROHA_SCU_SERDES_USB1) { + if (mode == PHY_MODE_ETHERNET) + sel = AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL_HSGMII; + else + sel = AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL_USB; + + regmap_update_bits(priv->scu, AIROHA_SCU_SSR3, + AIROHA_SCU_SSR3_SSUSB_HSGMII_SEL, sel); + } + + if (priv->serdes_port == AIROHA_SCU_SERDES_USB2) { + if (mode == PHY_MODE_PCIE) + sel = AIROHA_SCU_SSTR_USB_PCIE_SEL_PCIE; + else + sel = AIROHA_SCU_SSTR_USB_PCIE_SEL_USB; + + regmap_update_bits(priv->scu, AIROHA_SCU_SSTR, + AIROHA_SCU_SSTR_USB_PCIE_SEL, sel); + } + + return 0; +} + +static int airoha_usb_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); + struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + + switch (instance->type) { + case PHY_TYPE_USB2: + return airoha_usb_phy_u2_set_mode(priv, mode); + case PHY_TYPE_USB3: + return airoha_usb_phy_u3_set_mode(priv, mode); + default: + return 0; + } +} + +static struct phy *airoha_usb_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct airoha_usb_phy_priv *priv = dev_get_drvdata(dev); + struct airoha_usb_phy_instance *instance = NULL; + unsigned int index, phy_type; + + if (args->args_count != 1) { + dev_err(dev, "invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + phy_type = args->args[0]; + if (!(phy_type == PHY_TYPE_USB2 || phy_type == PHY_TYPE_USB3)) { + dev_err(dev, "unsupported device type: %d\n", phy_type); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < AIROHA_PHY_USB_MAX; index++) + if (priv->phys[index] && + phy_type == priv->phys[index]->type) { + instance = priv->phys[index]; + break; + } + + if (!instance) { + dev_err(dev, "failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + return instance->phy; +} + +static const struct phy_ops airoha_phy = { + .init = airoha_usb_phy_init, + .power_on = airoha_usb_phy_power_on, + .power_off = airoha_usb_phy_power_off, + .set_mode = airoha_usb_phy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct regmap_config airoha_usb_phy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int airoha_usb_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct airoha_usb_phy_priv *priv; + struct device *dev = &pdev->dev; + unsigned int index; + void *base; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + + ret = of_property_read_u32(dev->of_node, "airoha,usb2-monitor-clk-sel", + &priv->monclk_sel); + if (ret) + return dev_err_probe(dev, ret, "Monitor clock selection is mandatory for USB PHY calibration\n"); + + if (priv->monclk_sel > 3) + return dev_err_probe(dev, -EINVAL, "only 4 Monitor clock are selectable on the SoC\n"); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, &airoha_usb_phy_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + platform_set_drvdata(pdev, priv); + + for (index = 0; index < AIROHA_PHY_USB_MAX; index++) { + enum airoha_usb_phy_instance_type phy_type; + struct airoha_usb_phy_instance *instance; + + switch (index) { + case AIROHA_PHY_USB2: + phy_type = PHY_TYPE_USB2; + break; + case AIROHA_PHY_USB3: + phy_type = PHY_TYPE_USB3; + break; + } + + if (phy_type == PHY_TYPE_USB3) { + ret = of_property_read_u32(dev->of_node, "airoha,usb3-serdes", + &priv->serdes_port); + if (ret) + return dev_err_probe(dev, ret, "missing serdes line for USB 3.0\n"); + + priv->scu = syscon_regmap_lookup_by_phandle(dev->of_node, + "airoha,scu"); + if (IS_ERR(priv->scu)) + return dev_err_probe(dev, PTR_ERR(priv->scu), "failed to get SCU syscon\n"); + } + + instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return -ENOMEM; + + instance->type = phy_type; + priv->phys[index] = instance; + + instance->phy = devm_phy_create(dev, NULL, &airoha_phy); + if (IS_ERR(instance->phy)) + return dev_err_probe(dev, PTR_ERR(instance->phy), "failed to create phy\n"); + + phy_set_drvdata(instance->phy, instance); + } + + phy_provider = devm_of_phy_provider_register(&pdev->dev, airoha_usb_phy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id airoha_phy_id_table[] = { + { .compatible = "airoha,an7581-usb-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, airoha_phy_id_table); + +static struct platform_driver airoha_usb_driver = { + .probe = airoha_usb_phy_probe, + .driver = { + .name = "airoha-usb-phy", + .of_match_table = airoha_phy_id_table, + }, +}; + +module_platform_driver(airoha_usb_driver); + +MODULE_AUTHOR("Christian Marangi "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Airoha USB PHY driver"); diff --git a/scripts/tracepoint-update b/scripts/tracepoint-update new file mode 100755 index 0000000000000000000000000000000000000000..e3261675b1535f52e235c01d959a441fdd3d224e GIT binary patch literal 22952 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 literal 0 HcmV?d00001 -- 2.51.0