From patchwork Sun Aug 4 17:23:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Wunderlich X-Patchwork-Id: 1141815 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=public-files.de Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; secure) header.d=gmx.net header.i=@gmx.net header.b="lqxWY24w"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 461nrd6qNzz9sMr for ; Mon, 5 Aug 2019 03:26:57 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 9F6C7C21C93; Sun, 4 Aug 2019 17:26:22 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC, T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 565A6C21C50; Sun, 4 Aug 2019 17:26:18 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id CDD3EC21DF9; Sun, 4 Aug 2019 17:24:35 +0000 (UTC) Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) by lists.denx.de (Postfix) with ESMTPS id 5B521C21DA6 for ; Sun, 4 Aug 2019 17:24:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1564939442; bh=I6b8s5auITPMbmv+bDy6BzX11CQ1cVnUSYE8RxHxh2s=; h=X-UI-Sender-Class:From:To:Subject:Date:In-Reply-To:References; b=lqxWY24w0il5OMun9eR2Mv1enUnEtQZLNhCm880cP4DA1FpWKUg2abhH2XzbClKGl DsBPkort7hbtFaD0qcPo+TNJHCufMOe0ZbmBcc5taN/qDBurNR7/YcC2zF2WhztwEB 7t/5FLrWq9HaT3HKaxnytkVDFiyMIIbAN340u7VM= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([217.61.144.189]) by mail.gmx.com (mrgmx103 [212.227.17.168]) with ESMTPSA (Nemesis) id 0LzKQf-1iP4YV2FeL-014QAk; Sun, 04 Aug 2019 19:24:02 +0200 From: Frank Wunderlich To: Albert Aribaud , Christian Gmeiner , Daniel Schwierzeck , Frank Wunderlich , GSS_MTK_Uboot_upstream , Hou Zhiqiang , Jean-Jacques Hiblot , Marek Vasut , Mark Lee , Neil Armstrong , Oleksandr Rybalko , Prabhakar Kushwaha , Ramon Fried , Ryder Lee , Simon Glass , Stefan Roese , Tuomas Tynkkynen , u-boot@lists.denx.de, Weijie Gao Date: Sun, 4 Aug 2019 19:23:38 +0200 Message-Id: <20190804172342.5225-3-frank-w@public-files.de> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190804172342.5225-1-frank-w@public-files.de> References: <20190804172342.5225-1-frank-w@public-files.de> X-Provags-ID: V03:K1:HFxjuGph+D+Errcj4o3pl1c/4IkJm950tL+ZgjmIfU8GjQDXuPf kJTAOTlkDusK+LQBysffvaR2AE4Jino2Z963COWeMrP/XzkIb89fEvSYTpRAeYJ7M3hlKe6 Gsgzwo3WBFb85ON6yeLNlkmwfcUXAzic79KlsL0UEJGH4e6Z6wodwipDPEbyhClOrvCqMQQ YBU9OMMIi1vhk4hKUeTIw== X-UI-Out-Filterresults: notjunk:1; V03:K0:JxkFsnl+o4k=:1HNkZ8VECjWL8+wugvVzQ8 0/4VE3QcETZX3/+5VABErBOeGF/OB6sEjsbWjOIF4PQf2A8Z3SZxvyDR4CgZsenZomxtyV603 WsKM1FeLctwHOAvI3TCwVfU35jJ27wuutVd7QhCzOCOTuuNuUWL9UTkInG+v73XsB+s0+wl1Q r09YVKzLT/3b7T1P8leUNwO6VdiJXB2Xkqgh6Me4r6c2m8b0ALxU5Mwo3B7Ca/Br2f5tN02xP 1RSogiPrAwZi18gQpkzBUZzMst3BhfV4RrbTTM2g0wdS2x5l4y+WVADNF/jzqAU6RX0sV6zse qAq7lRFQxBQiDcF8PViAgEeqzjmSwCFTPdpecFBtDJlk2AlZTQ/av8X4DNar7+rS57S/e7hcM vd0duesBXz4My54293b4VD7FnEguzzbd4Vmx7DCOdSj23n3/cD7nUr/b21N+KZVD7kMgRsYqe s9DniUBvMlW3z2RfY4Rf/cBMESzIO0vBJrCJ2oQaJdCoAAjFKpTXpky4LvlKbmt3xaGata+L+ CRz4arKOKpDyn5nbN1QdVvhdhqW4jkC4NFba32pfq++b1Ctd5WahQgdHqZxWWYbKBBN8s9G6B hlGMoQY2r/Di02Ow794jBBO/IMCygs6xQCxFM7OEH0u4aGpnRd/pEKxlo3gVu1dsUAb0F/UUy zdo+hFRutDoMzo3sDNPfLH33lXSOLfohBBHTV8V+MCGcH0wDYo85TEERLYbqBRDfQDX3p+11r nIx1d0kvczcmK4PiQT+4NodDz0/RWsPPazWWMBLWju/GRBlUTRDd6T8Bem+Gh6Q10cZI7FtGX CFMBjaWioBnaaj100GDGwJlr9Y0PFvtidABlPWVPqC+9pMvH8AZXsmkMK5QvnRYbWBsLSPpsR 4bMoiFMGQqVFxLf1hF1HwPG6vNHWo+Mb+piLiJXewnXxrEln74+Nrr56AkqznZXloEnXIB69P rPiTHfrXsk6ohqDSDU05K3dK40pb9QmWccIII/Zr0Q8MaQXjOuyJVrRmILkNOHtRXU3nqPw2q /HHTPcZuxlEeEjEwN2R3V/ZqpLX0JZnlvoWozQKXpbwj4/oELxGBd6ErlnegBB3sDLRkMJ3Y8 KUAEkjkCno0vFgIdHCPfO1TsH5TZdqggprvtGOEGpT6lHgoTq6v0wL36w== Subject: [U-Boot] [PATCH v2 2/6] phy: mediatek: add MediaTek T-PHY support for PCIe X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Ryder Lee The driver provides PHY for USB2, USB3.0, PCIe and SATA, and now we just enable PCIe. As for the other functionalities will be added gradually in upcoming days. Tested-by: Frank Wunderlich Signed-off-by: Frank Wunderlich Signed-off-by: Ryder Lee --- drivers/phy/Kconfig | 11 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-mtk-tphy.c | 388 +++++++++++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/phy/phy-mtk-tphy.c -- 2.17.1 diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 957efb3984..2099dd9547 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -190,4 +190,15 @@ config MT76X8_USB_PHY This PHY is found on MT76x8 devices supporting USB. +config PHY_MTK_TPHY + bool "MediaTek T-PHY Driver" + depends on PHY + depends on ARCH_MEDIATEK + help + MediaTek T-PHY driver supports usb2.0, usb3.0 ports, PCIe and + SATA, and meanwhile supports two version T-PHY which have + different banks layout, the T-PHY with shared banks between + multi-ports is first version, otherwise is second veriosn, + so you can easily distinguish them by banks layout. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 90646ca55b..15b4d58a2d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o +obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c new file mode 100644 index 0000000000..422e727c22 --- /dev/null +++ b/drivers/phy/phy-mtk-tphy.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015 - 2019 MediaTek Inc. + * Author: Chunfeng Yun + * Ryder Lee + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* version V1 sub-banks offset base address */ +/* banks shared by multiple phys */ +#define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */ +#define SSUSB_SIFSLV_V1_CHIP 0x300 /* shared by u3 phys */ +/* u3/pcie/sata phy banks */ +#define SSUSB_SIFSLV_V1_U3PHYD 0x000 +#define SSUSB_SIFSLV_V1_U3PHYA 0x200 + +#define U3P_U3_CHIP_GPIO_CTLD 0x0c +#define P3C_REG_IP_SW_RST BIT(31) +#define P3C_MCU_BUS_CK_GATE_EN BIT(30) +#define P3C_FORCE_IP_SW_RST BIT(29) + +#define U3P_U3_CHIP_GPIO_CTLE 0x10 +#define P3C_RG_SWRST_U3_PHYD BIT(25) +#define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24) + +#define U3P_U3_PHYA_REG0 0x000 +#define P3A_RG_CLKDRV_OFF GENMASK(3, 2) +#define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2) + +#define U3P_U3_PHYA_REG1 0x004 +#define P3A_RG_CLKDRV_AMP GENMASK(31, 29) +#define P3A_RG_CLKDRV_AMP_VAL(x) ((0x7 & (x)) << 29) + +#define U3P_U3_PHYA_DA_REG0 0x100 +#define P3A_RG_XTAL_EXT_PE2H GENMASK(17, 16) +#define P3A_RG_XTAL_EXT_PE2H_VAL(x) ((0x3 & (x)) << 16) +#define P3A_RG_XTAL_EXT_PE1H GENMASK(13, 12) +#define P3A_RG_XTAL_EXT_PE1H_VAL(x) ((0x3 & (x)) << 12) +#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10) +#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10) + +#define U3P_U3_PHYA_DA_REG4 0x108 +#define P3A_RG_PLL_DIVEN_PE2H GENMASK(21, 19) +#define P3A_RG_PLL_BC_PE2H GENMASK(7, 6) +#define P3A_RG_PLL_BC_PE2H_VAL(x) ((0x3 & (x)) << 6) + +#define U3P_U3_PHYA_DA_REG5 0x10c +#define P3A_RG_PLL_BR_PE2H GENMASK(29, 28) +#define P3A_RG_PLL_BR_PE2H_VAL(x) ((0x3 & (x)) << 28) +#define P3A_RG_PLL_IC_PE2H GENMASK(15, 12) +#define P3A_RG_PLL_IC_PE2H_VAL(x) ((0xf & (x)) << 12) + +#define U3P_U3_PHYA_DA_REG6 0x110 +#define P3A_RG_PLL_IR_PE2H GENMASK(19, 16) +#define P3A_RG_PLL_IR_PE2H_VAL(x) ((0xf & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG7 0x114 +#define P3A_RG_PLL_BP_PE2H GENMASK(19, 16) +#define P3A_RG_PLL_BP_PE2H_VAL(x) ((0xf & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG20 0x13c +#define P3A_RG_PLL_DELTA1_PE2H GENMASK(31, 16) +#define P3A_RG_PLL_DELTA1_PE2H_VAL(x) ((0xffff & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG25 0x148 +#define P3A_RG_PLL_DELTA_PE2H GENMASK(15, 0) +#define P3A_RG_PLL_DELTA_PE2H_VAL(x) (0xffff & (x)) + +#define U3P_U3_PHYD_RXDET1 0x128 +#define P3D_RG_RXDET_STB2_SET GENMASK(17, 9) +#define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9) + +#define U3P_U3_PHYD_RXDET2 0x12c +#define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0) +#define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x)) + +struct u3phy_banks { + void __iomem *spllc; + void __iomem *chip; + void __iomem *phyd; /* include u3phyd_bank2 */ + void __iomem *phya; /* include u3phya_da */ +}; + +struct mtk_phy_instance { + void __iomem *port_base; + const struct device_node *np; + + struct u3phy_banks u3_banks; + + /* reference clock of anolog phy */ + struct clk ref_clk; + u32 index; + u8 type; +}; + +struct mtk_tphy { + void __iomem *sif_base; + struct mtk_phy_instance **phys; + int nphys; +}; + +static void pcie_phy_instance_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + u32 tmp; + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0); + tmp &= ~(P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H); + tmp |= P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | P3A_RG_XTAL_EXT_PE2H_VAL(0x2); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0); + + /* ref clk drive */ + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG1); + tmp &= ~P3A_RG_CLKDRV_AMP; + tmp |= P3A_RG_CLKDRV_AMP_VAL(0x4); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG1); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); + tmp &= ~P3A_RG_CLKDRV_OFF; + tmp |= P3A_RG_CLKDRV_OFF_VAL(0x1); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); + + /* SSC delta -5000ppm */ + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG20); + tmp &= ~P3A_RG_PLL_DELTA1_PE2H; + tmp |= P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG20); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG25); + tmp &= ~P3A_RG_PLL_DELTA_PE2H; + tmp |= P3A_RG_PLL_DELTA_PE2H_VAL(0x36); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG25); + + /* change pll BW 0.6M */ + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG5); + tmp &= ~(P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H); + tmp |= P3A_RG_PLL_BR_PE2H_VAL(0x1) | P3A_RG_PLL_IC_PE2H_VAL(0x1); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG5); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG4); + tmp &= ~(P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H); + tmp |= P3A_RG_PLL_BC_PE2H_VAL(0x3); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG4); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG6); + tmp &= ~P3A_RG_PLL_IR_PE2H; + tmp |= P3A_RG_PLL_IR_PE2H_VAL(0x2); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG6); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG7); + tmp &= ~P3A_RG_PLL_BP_PE2H; + tmp |= P3A_RG_PLL_BP_PE2H_VAL(0xa); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG7); + + /* Tx Detect Rx Timing: 10us -> 5us */ + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1); + tmp &= ~P3D_RG_RXDET_STB2_SET; + tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10); + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1); + + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2); + tmp &= ~P3D_RG_RXDET_STB2_SET_P3; + tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10); + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2); + + /* wait for PCIe subsys register to active */ + udelay(3000); +} + +static void pcie_phy_instance_power_on(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *bank = &instance->u3_banks; + u32 tmp; + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); + tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); + tmp &= ~(P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); +} + +static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) + +{ + struct u3phy_banks *bank = &instance->u3_banks; + u32 tmp; + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); + tmp |= P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST; + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); + tmp |= P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD; + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); +} + +static void phy_v1_banks_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + + switch (instance->type) { + case PHY_TYPE_PCIE: + u3_banks->spllc = tphy->sif_base + SSUSB_SIFSLV_V1_SPLLC; + u3_banks->chip = tphy->sif_base + SSUSB_SIFSLV_V1_CHIP; + u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V1_U3PHYD; + u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA; + break; + default: + return; + } +} + +static int mtk_phy_init(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + int ret; + + /* we may use a fixed-clock here */ + ret = clk_enable(&instance->ref_clk); + if (ret && ret != -ENOSYS) + return ret; + + switch (instance->type) { + case PHY_TYPE_PCIE: + pcie_phy_instance_init(tphy, instance); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_phy_power_on(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + pcie_phy_instance_power_on(tphy, instance); + + return 0; +} + +static int mtk_phy_power_off(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + pcie_phy_instance_power_off(tphy, instance); + + return 0; +} + +static int mtk_phy_exit(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + clk_disable(&instance->ref_clk); + + return 0; +} + +static int mtk_phy_xlate(struct phy *phy, + struct ofnode_phandle_args *args) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = NULL; + const struct device_node *phy_np = ofnode_to_np(args->node); + u32 index; + + if (!phy_np) { + dev_err(phy->dev, "null pointer phy node\n"); + return -EINVAL; + } + + if (args->args_count < 1) { + dev_err(phy->dev, "invalid number of cells in 'phy' property\n"); + return -EINVAL; + } + + for (index = 0; index < tphy->nphys; index++) + if (phy_np == tphy->phys[index]->np) { + instance = tphy->phys[index]; + break; + } + + if (!instance) { + dev_err(phy->dev, "failed to find appropriate phy\n"); + return -EINVAL; + } + + phy->id = index; + instance->type = args->args[1]; + if (!(instance->type == PHY_TYPE_USB2 || + instance->type == PHY_TYPE_USB3 || + instance->type == PHY_TYPE_PCIE || + instance->type == PHY_TYPE_SATA)) { + dev_err(phy->dev, "unsupported device type\n"); + return -EINVAL; + } + + phy_v1_banks_init(tphy, instance); + + return 0; +} + +static const struct phy_ops mtk_tphy_ops = { + .init = mtk_phy_init, + .exit = mtk_phy_exit, + .power_on = mtk_phy_power_on, + .power_off = mtk_phy_power_off, + .of_xlate = mtk_phy_xlate, +}; + +static int mtk_tphy_probe(struct udevice *dev) +{ + struct mtk_tphy *tphy = dev_get_priv(dev); + ofnode subnode; + int index = 0; + + dev_for_each_subnode(subnode, dev) + tphy->nphys++; + + tphy->phys = devm_kcalloc(dev, tphy->nphys, sizeof(*tphy->phys), + GFP_KERNEL); + if (!tphy->phys) + return -ENOMEM; + + tphy->sif_base = dev_read_addr_ptr(dev); + if (!tphy->sif_base) + return -ENOENT; + + dev_for_each_subnode(subnode, dev) { + struct mtk_phy_instance *instance; + fdt_addr_t addr; + int err; + + instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return -ENOMEM; + + addr = ofnode_get_addr(subnode); + if (addr == FDT_ADDR_T_NONE) + return -ENOMEM; + + instance->port_base = map_sysmem(addr, 0); + instance->index = index; + instance->np = ofnode_to_np(subnode); + tphy->phys[index] = instance; + index++; + + err = clk_get_by_index_nodev(subnode, 0, &instance->ref_clk); + if (err) + return err; + } + + return 0; +} + +static const struct udevice_id mtk_tphy_id_table[] = { + { .compatible = "mediatek,generic-tphy-v1", }, + { } +}; + +U_BOOT_DRIVER(mtk_tphy) = { + .name = "mtk-tphy", + .id = UCLASS_PHY, + .of_match = mtk_tphy_id_table, + .ops = &mtk_tphy_ops, + .probe = mtk_tphy_probe, + .priv_auto_alloc_size = sizeof(struct mtk_tphy), +};