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 3B57FC43458 for ; Wed, 8 Jul 2026 10:36:39 +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:Cc:To:From: Reply-To:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=vOJ1lbawyfv+DPk+HeplAa6YKzv4oMDP/vd2OChHbqk=; b=y5tawRyB8FnfPyz0dOLi2igCWi LD5IC8v6rxK7sB4SIeF3WMCi1yv5dELmkFtJZD2LZ5g1r3ihNqskgIg97if/h75zR+2TdZqSiYhGw 15GQXwdatM1SMVFmkd5x7WITUfBe31GGG0AkXHRA/kpxfzmNWvdFDBrdrt8aGClnxnkfxLkPG0WxH t9NPaaJpK0UcRTkb92VC4QBJB0DB4y/XdaPUtWEEB/1tmktHQpSnp+oIfpOXp+aGjSEMQnOmRLmcR RXnw8+9z3GQ9IGmqBERGcCxIpLuExw/Ytb9m3B4bPAoC7vpasx9U3dVaEJruJgN7ppLLlyGAWdv2B +0h3kQtw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1whPdl-0000000Guyn-0yEO; Wed, 08 Jul 2026 10:36:29 +0000 Received: from mail-pl1-x62b.google.com ([2607:f8b0:4864:20::62b]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1whPdg-0000000GuvJ-1L5w for linux-arm-kernel@lists.infradead.org; Wed, 08 Jul 2026 10:36:25 +0000 Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-2ca64c3ce5fso6054595ad.3 for ; Wed, 08 Jul 2026 03:36:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783506983; x=1784111783; darn=lists.infradead.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=vOJ1lbawyfv+DPk+HeplAa6YKzv4oMDP/vd2OChHbqk=; b=RJJZuFa29WHpGsSedsGb+saOvP9bya5eXqDcjFEK3XNrBCEQD0hdmwGGPxPHeOYl1r Mn9Kxz4gCwgNjlfVqNw/6AcJMAqXsZVcfLRljYfX8797d8QumvdBH1JlIS/ksR3aK2iH Nx6sc6UwC0/C99VENfvMADAyyfpS2WoHwszQ7HT2dO1z0HyZPVBsu5tFJeo49/H9xwmi pYIVoY+PPBdBqqtdlphxXUIel1Ng7HgWM86Hn6wm03wG5iwfw6XIqJr87Y6ElWT+Cssk Z9LGDdNWgGF4ZcNOAGNuhp9EYU6PmBZLOtj/oi+jj9d23nVPi0nefgdhzfDExPiUJs33 Afgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783506983; x=1784111783; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=vOJ1lbawyfv+DPk+HeplAa6YKzv4oMDP/vd2OChHbqk=; b=dYiVzBzUZdS4l2cMDD6JikXBqFQZwbyR+i2trtlfomYBahzq2tmUzUys/LOEgbFXG3 l0ZX9KruHlUtRddpqD5fmmEzvjQf8mlZcyqjq7z3DhBpoX3eL5Ci6eoK5h7r/jOqmdZx idGRSxTjipw/iUzSlOiVO9xnW+7VX7HNM/7BhaQIMu+invPb3C8vZEhPpFL2+beoCq8f r3lpppvtXckqr2Rf1qEBrTc3yzB54v2ws+acg/iCt3CYFc0irdSysFbUk2nHTkbrp2I8 2ECBLiFRW3u91euIqkXyVPZwee7DVWZ9AdvbTfo+BpV0/sKjEIn++w1w0zXe72gqsxw8 sKtw== X-Forwarded-Encrypted: i=1; AHgh+RqyWz9ZIuNJ39Bl3lcYQec4FdfU9qIhbfmaCplScU7hMJtosvnMwWM0tZXqlox6Untni3UdvNjTCimjXAZM9xl/@lists.infradead.org X-Gm-Message-State: AOJu0YyRMsCJd8hoIUWOLmm3jFOR/00HL0yR7sFBvIJARSauMzCnTu2Q Srwb/gp+VnFUtPH6BsJT1GHBRpagAai8g0En5GziARMN34P0DsQ2IyJZ X-Gm-Gg: AfdE7cm0C2SgJohQx+AHtcOs6tOZJjuzAZ5N1OCJtZEnnTL8pgxwmGayyklewJEVhNe 8a6pnal7a99Eo9UvRIG6ZnfdtDwaVLR871mpw1nCdAevV7ijVKGBN/qJUw3sTSpY1GZGCor+fXm iMULf/5KLiCBwgOgdpbtDyhvCm/gnPhpWnn8pY/p+colnnA9uFSQ3CKSZRn3KiOZIkrNa0b3Qjf KRPHfnvDgqvnu7Nuws4oXn1SaRWU/+bYEMgf8FzHmiqcaMuDYdje3073cBXNysTS1ysVlBoIff9 X8B3nVO5k1XXDbdO99WCRb1VTVAi6tBlJqEzBPgvQIiwEA1qu9/ol/tyNwjjIDlHDlvVE4Rqjfz 1eJetMkrznmUE8qy/e3pBSZ396G3WeLbTpNscYdClG4flQlQ6xxiWUUHH4NKvSuOjJiFZzcdV8g MFOf9MzDlJSGYJY0m9HbGp4Wk9k4wR2aA3Zz2k+NiPWJiK749lLkzYdSPsvCCQRjWXsmC9O8t9W DNwoottZZom X-Received: by 2002:a17:903:41cd:b0:2ca:bb7e:29a7 with SMTP id d9443c01a7336-2ccea58bee1mr21030585ad.41.1783506983211; Wed, 08 Jul 2026 03:36:23 -0700 (PDT) Received: from localhost.localdomain (60-250-196-139.hinet-ip.hinet.net. [60.250.196.139]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ccc9bdb9a3sm25987525ad.13.2026.07.08.03.36.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Jul 2026 03:36:22 -0700 (PDT) From: Joey Lu To: Vinod Koul , Neil Armstrong Cc: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Arnd Bergmann , Catalin Marinas , Jacky Huang , Shan-Chun Hung , Hui-Ping Chen , Joey Lu , linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Joey Lu Subject: [PATCH v3 3/3] phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support Date: Wed, 8 Jul 2026 18:36:06 +0800 Message-ID: <20260708103606.1462960-4-a0987203069@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260708103606.1462960-1-a0987203069@gmail.com> References: <20260708103606.1462960-1-a0987203069@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260708_033624_712578_01BDBF46 X-CRM114-Status: GOOD ( 29.24 ) 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 PHY0 and PHY1 use the same power-on/reset sequence in USBPMISCR, with PHY1 control bits shifted 16 positions relative to PHY0. A separate driver for PHY1 would duplicate this logic, so the existing driver is extended to manage both ports. The original driver polled only DEVCKSTB after releasing PHY0 from reset. When USB0 operates in host mode (USB ID pin floating or tied high) only HSTCKSTB and CK12MSTB assert; DEVCKSTB never sets. Polling exclusively for DEVCKSTB in host mode causes a 1 ms timeout on every phy_init() call from the EHCI driver. The init callback is changed to accept either host-mode or device-mode clock stability, whichever asserts first. The power_on and power_off callbacks are replaced by a single init callback that handles PHY reset and clock-stable polling, because there is no PHY-specific clock gate on MA35D1; the PHY analog block derives its reference from the HXT crystal. A read-only USB role switch is registered for PHY0 to expose the USB0 role to userspace via the standard role-switch sysfs interface. The .set callback returns -EOPNOTSUPP because the hardware mux is fully automatic with no software override path. Signed-off-by: Joey Lu --- drivers/phy/nuvoton/phy-ma35d1-usb2.c | 279 +++++++++++++++++++------- 1 file changed, 205 insertions(+), 74 deletions(-) diff --git a/drivers/phy/nuvoton/phy-ma35d1-usb2.c b/drivers/phy/nuvoton/phy-ma35d1-usb2.c index 9a459b700ed4..e2eac4f8c5bf 100644 --- a/drivers/phy/nuvoton/phy-ma35d1-usb2.c +++ b/drivers/phy/nuvoton/phy-ma35d1-usb2.c @@ -1,11 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2024 Nuvoton Technology Corp. + * Nuvoton MA35D1 USB 2.0 PHY driver + * + * Supports PHY0 (USB0 OTG port, shared between DWC2 gadget and EHCI0/OHCI0) + * and PHY1 (USB1 host-only port, used by EHCI1/OHCI1). The hardware mux on + * PHY0 switches automatically via the USB ID pin. + * + * Copyright (C) 2026 Nuvoton Technology Corp. */ #include #include #include -#include #include #include #include @@ -13,131 +18,257 @@ #include #include #include +#include -/* USB PHY Miscellaneous Control Register */ -#define MA35_SYS_REG_USBPMISCR 0x60 -#define PHY0POR BIT(0) /* PHY Power-On Reset Control Bit */ -#define PHY0SUSPEND BIT(1) /* PHY Suspend; 0: suspend, 1: operaion */ -#define PHY0COMN BIT(2) /* PHY Common Block Power-Down Control */ -#define PHY0DEVCKSTB BIT(10) /* PHY 60 MHz UTMI clock stable bit */ +#define MA35_SYS_PWRONOTP 0x04 +#define PWRONOTP_USBP0ID BIT(16) + +#define MA35_SYS_USBPMISCR 0x60 +#define USBPMISCR_PHY_POR(n) BIT(0 + (n) * 16) +#define USBPMISCR_PHY_SUSPEND(n) BIT(1 + (n) * 16) +#define USBPMISCR_PHY_COMN(n) BIT(2 + (n) * 16) +#define USBPMISCR_PHY_HSTCKSTB(n) BIT(8 + (n) * 16) +#define USBPMISCR_PHY_CK12MSTB(n) BIT(9 + (n) * 16) +#define USBPMISCR_PHY_DEVCKSTB(n) BIT(10 + (n) * 16) +/* Mask for control bits (POR, SUSPEND, COMN) */ +#define USBPMISCR_PHY_CTL_MASK(n) (0x7u << ((n) * 16)) +/* Host-mode ready */ +#define USBPMISCR_PHY_HOST_READY(n) (USBPMISCR_PHY_SUSPEND(n) | \ + USBPMISCR_PHY_HSTCKSTB(n) | \ + USBPMISCR_PHY_CK12MSTB(n)) +/* Device-mode ready */ +#define USBPMISCR_PHY_DEV_READY(n) (USBPMISCR_PHY_SUSPEND(n) | \ + USBPMISCR_PHY_DEVCKSTB(n)) +/* RCALCODE: 4-bit resistor trim */ +#define USBPMISCR_RCAL_SHIFT(n) (12 + (n) * 16) +#define USBPMISCR_RCAL_MASK(n) GENMASK(USBPMISCR_RCAL_SHIFT(n) + 3, \ + USBPMISCR_RCAL_SHIFT(n)) + +#define MA35_SYS_MISCFCR0 0x70 +/* USB host over-current detect polarity (shared, both ports) */ +#define MISCFCR0_UHOVRCURH BIT(12) + +#define MA35_PHY_NUM 2 + +struct ma35_phy_port { + struct phy *phy; + unsigned int idx; +}; struct ma35_usb_phy { - struct clk *clk; struct device *dev; struct regmap *sysreg; + struct ma35_phy_port port[MA35_PHY_NUM]; + struct usb_role_switch *role_sw; }; -static int ma35_usb_phy_power_on(struct phy *phy) +static int ma35_usb_phy_init(struct phy *phy) { - struct ma35_usb_phy *p_phy = phy_get_drvdata(phy); + struct ma35_phy_port *port = phy_get_drvdata(phy); + struct ma35_usb_phy *p = container_of(port - port->idx, + struct ma35_usb_phy, port[0]); + unsigned int n = port->idx; unsigned int val; int ret; - ret = clk_prepare_enable(p_phy->clk); - if (ret < 0) { - dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret); - return ret; - } + regmap_read(p->sysreg, MA35_SYS_USBPMISCR, &val); - regmap_read(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, &val); - if (val & PHY0SUSPEND) { - /* - * USB PHY0 is in operation mode already - * make sure USB PHY 60 MHz UTMI Interface Clock ready - */ - ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val, - val & PHY0DEVCKSTB, 10, 1000); - if (ret == 0) - return 0; - } + if (val & USBPMISCR_PHY_SUSPEND(n)) + return 0; - /* - * reset USB PHY0. - * wait until USB PHY0 60 MHz UTMI Interface Clock ready - */ - regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, (PHY0POR | PHY0SUSPEND)); + regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR, + USBPMISCR_PHY_CTL_MASK(n), + USBPMISCR_PHY_POR(n) | USBPMISCR_PHY_SUSPEND(n)); udelay(20); - /* make USB PHY0 enter operation mode */ - regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, PHY0SUSPEND); + regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR, + USBPMISCR_PHY_CTL_MASK(n), + USBPMISCR_PHY_SUSPEND(n)); - /* make sure USB PHY 60 MHz UTMI Interface Clock ready */ - ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val, - val & PHY0DEVCKSTB, 10, 1000); - if (ret == -ETIMEDOUT) { - dev_err(p_phy->dev, "Check PHY clock, Timeout: %d\n", ret); - clk_disable_unprepare(p_phy->clk); + if (n == 0) { + ret = regmap_read_poll_timeout(p->sysreg, MA35_SYS_USBPMISCR, + val, + ((val & USBPMISCR_PHY_HOST_READY(0)) == + USBPMISCR_PHY_HOST_READY(0)) || + ((val & USBPMISCR_PHY_DEV_READY(0)) == + USBPMISCR_PHY_DEV_READY(0)), + 10, 1000); + } else { + ret = regmap_read_poll_timeout(p->sysreg, MA35_SYS_USBPMISCR, + val, + (val & USBPMISCR_PHY_HOST_READY(n)) == + USBPMISCR_PHY_HOST_READY(n), + 10, 1000); + } + + if (ret) { + dev_err(p->dev, "USB PHY%u clock not stable (USBPMISCR=0x%08x)\n", + n, val); return ret; } return 0; } -static int ma35_usb_phy_power_off(struct phy *phy) +static const struct phy_ops ma35_usb_phy_ops = { + .init = ma35_usb_phy_init, + .owner = THIS_MODULE, +}; + +static int ma35_role_sw_set(struct usb_role_switch *sw, enum usb_role role) +{ + return -EOPNOTSUPP; +} + +static enum usb_role ma35_role_sw_get(struct usb_role_switch *sw) { - struct ma35_usb_phy *p_phy = phy_get_drvdata(phy); + struct ma35_usb_phy *p = usb_role_switch_get_drvdata(sw); + u32 val; + + regmap_read(p->sysreg, MA35_SYS_PWRONOTP, &val); + + return (val & PWRONOTP_USBP0ID) ? USB_ROLE_HOST : USB_ROLE_DEVICE; +} + +static int ma35_role_switch_init(struct platform_device *pdev, + struct ma35_usb_phy *p) +{ + struct usb_role_switch_desc sw_desc = {0}; + + sw_desc.set = ma35_role_sw_set; + sw_desc.get = ma35_role_sw_get; + sw_desc.allow_userspace_control = true; + sw_desc.driver_data = p; + sw_desc.fwnode = dev_fwnode(&pdev->dev); + + p->role_sw = usb_role_switch_register(&pdev->dev, &sw_desc); + if (IS_ERR(p->role_sw)) + return dev_err_probe(&pdev->dev, PTR_ERR(p->role_sw), + "failed to register role switch\n"); - clk_disable_unprepare(p_phy->clk); return 0; } -static const struct phy_ops ma35_usb_phy_ops = { - .power_on = ma35_usb_phy_power_on, - .power_off = ma35_usb_phy_power_off, - .owner = THIS_MODULE, -}; +static void ma35_role_switch_exit(struct ma35_usb_phy *p) +{ + if (p->role_sw) { + usb_role_switch_unregister(p->role_sw); + p->role_sw = NULL; + } +} + +static struct phy *ma35_usb_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct ma35_usb_phy *p = dev_get_drvdata(dev); + unsigned int idx; + + if (args->args_count == 0) + idx = 0; + else + idx = args->args[0]; + + if (idx >= MA35_PHY_NUM) + return ERR_PTR(-EINVAL); + + return p->port[idx].phy; +} static int ma35_usb_phy_probe(struct platform_device *pdev) { struct phy_provider *provider; - struct ma35_usb_phy *p_phy; - struct phy *phy; + struct ma35_usb_phy *p; + struct clk *clk; + int n, ret; + u32 code; - p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL); - if (!p_phy) + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + if (!p) return -ENOMEM; - p_phy->dev = &pdev->dev; - platform_set_drvdata(pdev, p_phy); + p->dev = &pdev->dev; + platform_set_drvdata(pdev, p); + + p->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "nuvoton,sys"); + if (IS_ERR(p->sysreg)) + return dev_err_probe(&pdev->dev, PTR_ERR(p->sysreg), + "failed to get SYS regmap\n"); + + clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), + "failed to get clock\n"); - p_phy->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sys"); - if (IS_ERR(p_phy->sysreg)) - return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->sysreg), - "Failed to get SYS registers\n"); + for (n = 0; n < MA35_PHY_NUM; n++) { + if (of_property_read_u32_index(pdev->dev.of_node, + "nuvoton,rcalcode", n, &code)) + continue; - p_phy->clk = of_clk_get(pdev->dev.of_node, 0); - if (IS_ERR(p_phy->clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->clk), - "failed to find usb_phy clock\n"); + if (code > 15) + return dev_err_probe(&pdev->dev, -EINVAL, + "rcalcode[%d] %u out of range (0-15)\n", + n, code); - phy = devm_phy_create(&pdev->dev, NULL, &ma35_usb_phy_ops); - if (IS_ERR(phy)) - return dev_err_probe(&pdev->dev, PTR_ERR(phy), "Failed to create PHY\n"); + regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR, + USBPMISCR_RCAL_MASK(n), + code << USBPMISCR_RCAL_SHIFT(n)); + } + + if (of_property_read_bool(pdev->dev.of_node, "nuvoton,oc-active-high")) + regmap_update_bits(p->sysreg, MA35_SYS_MISCFCR0, + MISCFCR0_UHOVRCURH, MISCFCR0_UHOVRCURH); + + for (n = 0; n < MA35_PHY_NUM; n++) { + p->port[n].idx = n; + + p->port[n].phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, + &ma35_usb_phy_ops); + if (IS_ERR(p->port[n].phy)) + return dev_err_probe(&pdev->dev, PTR_ERR(p->port[n].phy), + "failed to create PHY%d\n", n); - phy_set_drvdata(phy, p_phy); + phy_set_drvdata(p->port[n].phy, &p->port[n]); + } + + ret = ma35_role_switch_init(pdev, p); + if (ret) + return ret; - provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); - if (IS_ERR(provider)) + provider = devm_of_phy_provider_register(&pdev->dev, ma35_usb_phy_xlate); + if (IS_ERR(provider)) { + ma35_role_switch_exit(p); return dev_err_probe(&pdev->dev, PTR_ERR(provider), - "Failed to register PHY provider\n"); + "failed to register PHY provider\n"); + } + return 0; } +static void ma35_usb_phy_remove(struct platform_device *pdev) +{ + struct ma35_usb_phy *p = platform_get_drvdata(pdev); + + ma35_role_switch_exit(p); +} + static const struct of_device_id ma35_usb_phy_of_match[] = { - { .compatible = "nuvoton,ma35d1-usb2-phy", }, - { }, + { .compatible = "nuvoton,ma35d1-usb2-phy" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ma35_usb_phy_of_match); static struct platform_driver ma35_usb_phy_driver = { .probe = ma35_usb_phy_probe, - .driver = { - .name = "ma35d1-usb2-phy", - .of_match_table = ma35_usb_phy_of_match, + .remove = ma35_usb_phy_remove, + .driver = { + .name = "ma35d1-usb2-phy", + .of_match_table = ma35_usb_phy_of_match, }, }; module_platform_driver(ma35_usb_phy_driver); MODULE_DESCRIPTION("Nuvoton ma35d1 USB2.0 PHY driver"); MODULE_AUTHOR("Hui-Ping Chen "); +MODULE_AUTHOR("Joey Lu "); MODULE_LICENSE("GPL"); -- 2.43.0