diff --git a/aarch64-boards b/aarch64-boards index 9e2fafa..50427ad 100644 --- a/aarch64-boards +++ b/aarch64-boards @@ -1,5 +1,6 @@ a64-olinuxino amarula_a64_relic +apple_m1 bananapi_m2_plus_h5 bananapi_m64 beelink_gs1 @@ -35,6 +36,7 @@ nanopi-m4-rk3399 nanopi_neo2 nanopi-neo4-rk3399 nanopi_neo_plus2 +nanopi_r1s_h5 nanopi-r2s-rk3328 nanopi-r4s-rk3399 odroid-c2 diff --git a/phy-rockchip-inno-usb2-fix-hang-when-multiple-controllers-exit.patch b/phy-rockchip-inno-usb2-fix-hang-when-multiple-controllers-exit.patch deleted file mode 100644 index 81f1cdb..0000000 --- a/phy-rockchip-inno-usb2-fix-hang-when-multiple-controllers-exit.patch +++ /dev/null @@ -1,233 +0,0 @@ -From patchwork Tue Apr 6 15:10:59 2021 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Patchwork-Submitter: Icenowy Zheng -X-Patchwork-Id: 1462876 -Return-Path: -X-Original-To: incoming@patchwork.ozlabs.org -Delivered-To: patchwork-incoming@bilbo.ozlabs.org -Authentication-Results: ozlabs.org; - spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de - (client-ip=85.214.62.61; helo=phobos.denx.de; - envelope-from=u-boot-bounces@lists.denx.de; receiver=) -Authentication-Results: ozlabs.org; - dkim=fail reason="signature verification failed" (1024-bit key; - unprotected) header.d=mymailcheap.com header.i=@mymailcheap.com - header.a=rsa-sha256 header.s=default header.b=unszVtqT; - dkim=fail reason="signature verification failed" (1024-bit key; - unprotected) header.d=aosc.io header.i=@aosc.io header.a=rsa-sha256 - header.s=default header.b=mWwYCh9A; - dkim-atps=neutral -Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) - (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) - key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest - SHA256) - (No client certificate requested) - by ozlabs.org (Postfix) with ESMTPS id 4FF9wc3GdNz9sW1 - for ; Wed, 7 Apr 2021 01:11:42 +1000 (AEST) -Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) - by phobos.denx.de (Postfix) with ESMTP id C1F9C8040A; - Tue, 6 Apr 2021 17:11:32 +0200 (CEST) -Authentication-Results: phobos.denx.de; - dmarc=none (p=none dis=none) header.from=aosc.io -Authentication-Results: phobos.denx.de; - spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de -Authentication-Results: phobos.denx.de; - dkim=fail reason="signature verification failed" (1024-bit key; - unprotected) header.d=mymailcheap.com header.i=@mymailcheap.com - header.b="unszVtqT"; - dkim=fail reason="signature verification failed" (1024-bit key; - unprotected) header.d=aosc.io header.i=@aosc.io header.b="mWwYCh9A"; - dkim-atps=neutral -Received: by phobos.denx.de (Postfix, from userid 109) - id E4F22805B4; Tue, 6 Apr 2021 17:11:30 +0200 (CEST) -X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de -X-Spam-Level: -X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, - DKIM_VALID,DKIM_VALID_AU,SPF_HELO_NONE autolearn=ham - autolearn_force=no version=3.4.2 -Received: from relay3.mymailcheap.com (relay3.mymailcheap.com - [217.182.66.161]) - (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) - (No client certificate requested) - by phobos.denx.de (Postfix) with ESMTPS id 2FC4A8039D - for ; Tue, 6 Apr 2021 17:11:27 +0200 (CEST) -Authentication-Results: phobos.denx.de; - dmarc=none (p=none dis=none) header.from=aosc.io -Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=icenowy@aosc.io -Received: from filter2.mymailcheap.com (filter2.mymailcheap.com - [91.134.140.82]) - by relay3.mymailcheap.com (Postfix) with ESMTPS id A6AC13ECDF; - Tue, 6 Apr 2021 17:11:26 +0200 (CEST) -Received: from localhost (localhost [127.0.0.1]) - by filter2.mymailcheap.com (Postfix) with ESMTP id 88D512A915; - Tue, 6 Apr 2021 17:11:26 +0200 (CEST) -DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=mymailcheap.com; - s=default; t=1617721886; - bh=qb5gt5o7M9qjdJd67A3WHGZp/76w4s4FS6+u1VuEh/o=; - h=From:To:Cc:Subject:Date:From; - b=unszVtqTnO9MGZVbgLH5H0cMo3VfQ+g5377blmOUcrtzMSqNbDD5SM25MUhlTUKiB - hTFq4L53gszBYfRPfh7Z5XgNVERULL5Zx0+WnVjZ7kUIp/wS3IQqxe81ChoFEsLTr8 - 22UB9NN3OSVkuN/jRAzX+lKpugVGBHwOCjZBZukQ= -Received: from filter2.mymailcheap.com ([127.0.0.1]) - by localhost (filter2.mymailcheap.com [127.0.0.1]) (amavisd-new, port 10024) - with ESMTP id V9lVOgo6gApH; Tue, 6 Apr 2021 17:11:25 +0200 (CEST) -Received: from mail20.mymailcheap.com (mail20.mymailcheap.com [51.83.111.147]) - (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) - (No client certificate requested) - by filter2.mymailcheap.com (Postfix) with ESMTPS; - Tue, 6 Apr 2021 17:11:25 +0200 (CEST) -Received: from [213.133.102.83] (ml.mymailcheap.com [213.133.102.83]) - by mail20.mymailcheap.com (Postfix) with ESMTP id C9EA3400B6; - Tue, 6 Apr 2021 15:11:24 +0000 (UTC) -Authentication-Results: mail20.mymailcheap.com; dkim=pass (1024-bit key; - unprotected) header.d=aosc.io header.i=@aosc.io header.b="mWwYCh9A"; - dkim-atps=neutral -AI-Spam-Status: Not processed -Received: from ice-e5v2.lan (unknown [59.41.163.135]) - (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) - key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest - SHA256) (No client certificate requested) - by mail20.mymailcheap.com (Postfix) with ESMTPSA id 251F840C16; - Tue, 6 Apr 2021 15:11:10 +0000 (UTC) -DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=aosc.io; s=default; - t=1617721874; bh=qb5gt5o7M9qjdJd67A3WHGZp/76w4s4FS6+u1VuEh/o=; - h=From:To:Cc:Subject:Date:From; - b=mWwYCh9AM8eRWm9hiEqirytotj/G/C1p9r2fd0zUxPhS0UQH+kw6L3RWZnsfmXAYq - HmYFiM3ktIfwevqQw89tOi8c2ktb49iYOyHXkfi8hTZl1dNsJDS94uyzBiHYY+U7x5 - P0aTRRaL5C8ua2c1GedoPx06nrARO/KMEFy39rxI= -From: Icenowy Zheng -To: Simon Glass , Kever Yang , - Frank Wang , - Jagan Teki -Cc: u-boot@lists.denx.de, - Icenowy Zheng -Subject: [PATCH] phy: rockchip: inno-usb2: fix hang when multiple controllers - exit -Date: Tue, 6 Apr 2021 23:10:59 +0800 -Message-Id: <20210406151059.1187379-1-icenowy@aosc.io> -X-Mailer: git-send-email 2.30.2 -MIME-Version: 1.0 -X-Rspamd-Server: mail20.mymailcheap.com -X-Spamd-Result: default: False [4.90 / 20.00]; RCVD_VIA_SMTP_AUTH(0.00)[]; - ARC_NA(0.00)[]; R_DKIM_ALLOW(0.00)[aosc.io:s=default]; - RECEIVED_SPAMHAUS_PBL(0.00)[59.41.163.135:received]; - FROM_HAS_DN(0.00)[]; TO_DN_SOME(0.00)[]; - R_MISSING_CHARSET(2.50)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; - MIME_GOOD(-0.10)[text/plain]; DMARC_NA(0.00)[aosc.io]; - BROKEN_CONTENT_TYPE(1.50)[]; R_SPF_SOFTFAIL(0.00)[~all]; - RCPT_COUNT_FIVE(0.00)[6]; ML_SERVERS(-3.10)[213.133.102.83]; - DKIM_TRACE(0.00)[aosc.io:+]; MID_CONTAINS_FROM(1.00)[]; - RCVD_NO_TLS_LAST(0.10)[]; FROM_EQ_ENVFROM(0.00)[]; - MIME_TRACE(0.00)[0:+]; - ASN(0.00)[asn:24940, ipnet:213.133.96.0/19, country:DE]; - RCVD_COUNT_TWO(0.00)[2]; - HFILTER_HELO_BAREIP(3.00)[213.133.102.83,1] -X-Rspamd-Queue-Id: C9EA3400B6 -X-BeenThere: u-boot@lists.denx.de -X-Mailman-Version: 2.1.34 -Precedence: list -List-Id: U-Boot discussion -List-Unsubscribe: , - -List-Archive: -List-Post: -List-Help: -List-Subscribe: , - -Errors-To: u-boot-bounces@lists.denx.de -Sender: "U-Boot" -X-Virus-Scanned: clamav-milter 0.102.4 at phobos.denx.de -X-Virus-Status: Clean - -The OHCI and EHCI controllers are both bound to the same PHY. They will -both do init and power_on operations when the controller is brought up -and both do power_off and exit when the controller is stopped. However, -the PHY uclass of U-Boot is not as sane as we thought -- they won't -maintain a status mark for PHYs, and thus the functions of the PHYs -could be called for multiple times. Calling init/power_on for multiple -times have no severe problems, however calling power_off/exit for -multiple times have a problem -- the first exit call will stop the PHY -clock, and power_off/exit calls after it still trying to write to PHY -registers. The write operation to PHY registers will fail because clock -is already stopped. - -Adapt the count mechanism from phy-sun4i-usb to both init/exit and -power_on/power_off functions to phy-rockchip-inno-usb2 to fix this -problem. With this stopping USB controllers (manually or before booting -a kernel) will work. - -Signed-off-by: Icenowy Zheng -Fixes: ac97a9ece14e ("phy: rockchip: Add Rockchip USB2PHY driver") ---- - drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 21 +++++++++++++++++++ - 1 file changed, 21 insertions(+) - -diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c -index 62b8ba3a4a..be9cc99d90 100644 ---- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c -+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c -@@ -62,6 +62,8 @@ struct rockchip_usb2phy { - void *reg_base; - struct clk phyclk; - const struct rockchip_usb2phy_cfg *phy_cfg; -+ int init_count; -+ int power_on_count; - }; - - static inline int property_enable(void *reg_base, -@@ -92,6 +94,10 @@ static int rockchip_usb2phy_power_on(struct phy *phy) - struct rockchip_usb2phy *priv = dev_get_priv(parent); - const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy); - -+ priv->power_on_count++; -+ if (priv->power_on_count != 1) -+ return 0; -+ - property_enable(priv->reg_base, &port_cfg->phy_sus, false); - - /* waiting for the utmi_clk to become stable */ -@@ -106,6 +112,10 @@ static int rockchip_usb2phy_power_off(struct phy *phy) - struct rockchip_usb2phy *priv = dev_get_priv(parent); - const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy); - -+ priv->power_on_count--; -+ if (priv->power_on_count != 0) -+ return 0; -+ - property_enable(priv->reg_base, &port_cfg->phy_sus, true); - - return 0; -@@ -118,6 +128,10 @@ static int rockchip_usb2phy_init(struct phy *phy) - const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy); - int ret; - -+ priv->init_count++; -+ if (priv->init_count != 1) -+ return 0; -+ - ret = clk_enable(&priv->phyclk); - if (ret) { - dev_err(phy->dev, "failed to enable phyclk (ret=%d)\n", ret); -@@ -140,6 +154,10 @@ static int rockchip_usb2phy_exit(struct phy *phy) - struct udevice *parent = dev_get_parent(phy->dev); - struct rockchip_usb2phy *priv = dev_get_priv(parent); - -+ priv->init_count--; -+ if (priv->init_count != 0) -+ return 0; -+ - clk_disable(&priv->phyclk); - - return 0; -@@ -212,6 +230,9 @@ static int rockchip_usb2phy_probe(struct udevice *dev) - return ret; - } - -+ priv->power_on_count = 0; -+ priv->init_count = 0; -+ - return 0; - } - diff --git a/uboot-tools.spec b/uboot-tools.spec index f06fbe0..92d15ee 100644 --- a/uboot-tools.spec +++ b/uboot-tools.spec @@ -2,7 +2,7 @@ Name: uboot-tools Version: 2022.01 -Release: 0.2%{?candidate:.%{candidate}}%{?dist} +Release: 0.3%{?candidate:.%{candidate}}%{?dist} Summary: U-Boot utilities License: GPLv2+ BSD LGPL-2.1+ LGPL-2.0+ URL: http://www.denx.de/wiki/U-Boot @@ -17,14 +17,15 @@ Source2: aarch64-boards Patch1: uefi-distro-load-FDT-from-any-partition-on-boot-device.patch # Board fixes and enablement +Patch2: v2-console-usb-kbd-Limit-poll-frequency-to-improve-performance.patch # RPi - uses RPI firmware device tree for HAT support -Patch2: rpi-Enable-using-the-DT-provided-by-the-Raspberry-Pi.patch -Patch3: rpi-fallback-to-max-clock-for-mmc.patch -Patch4: rpi-bcm2835_sdhost-firmware-managed-clock.patch +Patch3: rpi-Enable-using-the-DT-provided-by-the-Raspberry-Pi.patch +Patch4: rpi-fallback-to-max-clock-for-mmc.patch +Patch5: rpi-bcm2835_sdhost-firmware-managed-clock.patch # Rockchips improvements -Patch5: phy-rockchip-inno-usb2-fix-hang-when-multiple-controllers-exit.patch -Patch6: dts-rockchip-rk3399-enable-emmc-phy-for-spl.patch -Patch7: 0001-Revert-spi-spi-uclass-Add-support-to-manually-reloca.patch +Patch6: v3-phy-Track-power-on-and-init-counts-in-uclass.patch +Patch7: dts-rockchip-rk3399-enable-emmc-phy-for-spl.patch +Patch8: 0001-Revert-spi-spi-uclass-Add-support-to-manually-reloca.patch BuildRequires: bc BuildRequires: dtc @@ -250,6 +251,9 @@ cp -p board/warp7/README builds/docs/README.warp7 %endif %changelog +* Wed Jan 05 2022 Peter Robinson - 2022.01-0.3.rc4 +- Upstream fixes for PHY and UEFI + * Mon Dec 20 2021 Peter Robinson - 2022.01-0.2.rc4 - Update to 2022.01 RC4 diff --git a/v2-console-usb-kbd-Limit-poll-frequency-to-improve-performance.patch b/v2-console-usb-kbd-Limit-poll-frequency-to-improve-performance.patch new file mode 100644 index 0000000..5eb366c --- /dev/null +++ b/v2-console-usb-kbd-Limit-poll-frequency-to-improve-performance.patch @@ -0,0 +1,178 @@ +From patchwork Wed Dec 22 22:51:26 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Thomas Watson +X-Patchwork-Id: 1572368 +X-Patchwork-Delegate: marek.vasut@gmail.com +Return-Path: +X-Original-To: incoming@patchwork.ozlabs.org +Delivered-To: patchwork-incoming@bilbo.ozlabs.org +Authentication-Results: bilbo.ozlabs.org; + dkim=pass (2048-bit key; + unprotected) header.d=icloud.com header.i=@icloud.com header.a=rsa-sha256 + header.s=1a1hai header.b=SkTQ1vLj; + dkim-atps=neutral +Authentication-Results: ozlabs.org; + spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de + (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; + envelope-from=u-boot-bounces@lists.denx.de; receiver=) +Received: from phobos.denx.de (phobos.denx.de + [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) + key-exchange X25519 server-signature RSA-PSS (4096 bits)) + (No client certificate requested) + by bilbo.ozlabs.org (Postfix) with ESMTPS id 4JK8Mc4B31z9s0r + for ; Thu, 23 Dec 2021 10:16:10 +1100 (AEDT) +Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) + by phobos.denx.de (Postfix) with ESMTP id D051D830C3; + Thu, 23 Dec 2021 00:15:59 +0100 (CET) +Authentication-Results: phobos.denx.de; + dmarc=pass (p=quarantine dis=none) header.from=icloud.com +Authentication-Results: phobos.denx.de; + spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de +Authentication-Results: phobos.denx.de; + dkim=pass (2048-bit key; + unprotected) header.d=icloud.com header.i=@icloud.com header.b="SkTQ1vLj"; + dkim-atps=neutral +Received: by phobos.denx.de (Postfix, from userid 109) + id 0DA67830F5; Wed, 22 Dec 2021 23:52:26 +0100 (CET) +X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de +X-Spam-Level: +X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, + DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_ENVFROM_END_DIGIT, + FREEMAIL_FROM,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=ham + autolearn_force=no version=3.4.2 +Received: from st43p00im-ztbu10073601.me.com (st43p00im-ztbu10073601.me.com + [17.58.63.184]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) + (No client certificate requested) + by phobos.denx.de (Postfix) with ESMTPS id 1598D830C2 + for ; Wed, 22 Dec 2021 23:52:23 +0100 (CET) +Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) + header.from=icloud.com +Authentication-Results: phobos.denx.de; + spf=pass smtp.mailfrom=twatson52@icloud.com +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; + s=1a1hai; t=1640213541; + bh=9Gu/RsbBH8Lwy0caxjtXwguQwc3tOXLqzye8cafMd78=; + h=From:To:Subject:Date:Message-Id:MIME-Version; + b=SkTQ1vLjDZUB4S3pQ+QOuMAQs7y78hvdCVG59JmuiBtL/pLL9IskzfDYEEVxmBQ8j + QIbZXSdaHlAH3tL3ajaqL0+IJp7J/yQ5/wpmmTsRMBdk0xcgSGilBCve35DMoKYFu1 + YSaXYI8GDtaBL5R9EGZAW6SekyeIWfyy03PQpawqJpTlqUNZuLdrr9mGRF/q2fbSHd + Fr4qzePCKzk3GsUhdfkh8hEDS4lOGVAPa3K2mGEpK6K/+Gv3rCckgYrV5IAJQ8YpdP + SCmRL3N03P5wkt5WiYasawJW4pcx92J2RXtyikC9fc6p67bGXkUJA+8dOtxlOyBY8E + 8TrKBWY9cvKTw== +Received: from jacobs-p52.memphis.edu + (99-122-90-201.lightspeed.mmphtn.sbcglobal.net [99.122.90.201]) + by st43p00im-ztbu10073601.me.com (Postfix) with ESMTPSA id 283205A0974; + Wed, 22 Dec 2021 22:52:21 +0000 (UTC) +From: Thomas Watson +To: u-boot@lists.denx.de +Cc: Thomas Watson , + Marek Vasut +Subject: [PATCH v2] console: usb: kbd: Limit poll frequency to improve + performance +Date: Wed, 22 Dec 2021 16:51:26 -0600 +Message-Id: <20211222225124.7154-1-twatson52@icloud.com> +X-Mailer: git-send-email 2.31.1 +MIME-Version: 1.0 +X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.425, 18.0.790 + definitions=2021-12-22_02:2021-12-22, + 2021-12-22 signatures=0 +X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 + malwarescore=0 + phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 mlxscore=0 + mlxlogscore=934 adultscore=0 classifier=spam adjust=0 reason=mlx + scancount=1 engine=8.0.1-2009150000 definitions=main-2112220119 +X-Mailman-Approved-At: Thu, 23 Dec 2021 00:15:58 +0100 +X-BeenThere: u-boot@lists.denx.de +X-Mailman-Version: 2.1.38 +Precedence: list +List-Id: U-Boot discussion +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +Errors-To: u-boot-bounces@lists.denx.de +Sender: "U-Boot" +X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de +X-Virus-Status: Clean + +Using the XHCI driver, the function `usb_kbd_poll_for_event` takes +30-40ms to run. The exact time is dependent on the polling interval the +keyboard requests in its descriptor, and likely cannot be significantly +reduced without major rework to the XHCI driver. + +The U-Boot EFI console service sets a timer to poll the keyboard every 5 +microseconds, and this timer is checked every time a block is read off +disk. The net effect is that, on my system, loading a ~40MiB kernel and +initrd takes about 62 seconds with a slower keyboard and 53 seconds +with a faster one, with the vast majority of the time spent polling the +keyboard. + +To solve this problem, this patch adds a 20ms delay between consecutive +calls to `usb_kbd_poll_for_event`. This is sufficient to reduce the +total loading time to under half a second for both keyboards, and does +not impact the perceived keystroke latency. + +Signed-off-by: Thomas Watson +--- +This revision wraps the comment at 80 characters and also should not +have been corrupted by my e-mail client. + + common/usb_kbd.c | 20 +++++++++++++++----- + 1 file changed, 15 insertions(+), 5 deletions(-) + +diff --git a/common/usb_kbd.c b/common/usb_kbd.c +index afad260d3d..5408c821b4 100644 +--- a/common/usb_kbd.c ++++ b/common/usb_kbd.c +@@ -118,7 +118,7 @@ struct usb_kbd_pdata { + extern int __maybe_unused net_busy_flag; + + /* The period of time between two calls of usb_kbd_testc(). */ +-static unsigned long __maybe_unused kbd_testc_tms; ++static unsigned long kbd_testc_tms; + + /* Puts character in the queue and sets up the in and out pointer. */ + static void usb_kbd_put_queue(struct usb_kbd_pdata *data, u8 c) +@@ -394,21 +394,31 @@ static int usb_kbd_testc(struct stdio_dev *sdev) + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + ++ /* ++ * Polling the keyboard for an event can take dozens of milliseconds. ++ * Add a delay between polls to avoid blocking activity which polls ++ * rapidly, like the UEFI console timer. ++ */ ++ unsigned long poll_delay = CONFIG_SYS_HZ / 50; ++ + #ifdef CONFIG_CMD_NET + /* + * If net_busy_flag is 1, NET transfer is running, + * then we check key-pressed every second (first check may be + * less than 1 second) to improve TFTP booting performance. + */ +- if (net_busy_flag && (get_timer(kbd_testc_tms) < CONFIG_SYS_HZ)) +- return 0; +- kbd_testc_tms = get_timer(0); ++ if (net_busy_flag) ++ poll_delay = CONFIG_SYS_HZ; + #endif ++ + dev = stdio_get_by_name(sdev->name); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + +- usb_kbd_poll_for_event(usb_kbd_dev); ++ if (get_timer(kbd_testc_tms) >= poll_delay) { ++ usb_kbd_poll_for_event(usb_kbd_dev); ++ kbd_testc_tms = get_timer(0); ++ } + + return !(data->usb_in_pointer == data->usb_out_pointer); + } diff --git a/v3-phy-Track-power-on-and-init-counts-in-uclass.patch b/v3-phy-Track-power-on-and-init-counts-in-uclass.patch new file mode 100644 index 0000000..d6cc31d --- /dev/null +++ b/v3-phy-Track-power-on-and-init-counts-in-uclass.patch @@ -0,0 +1,556 @@ +From patchwork Thu Dec 30 19:36:51 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Alper Nebi Yasak +X-Patchwork-Id: 1574229 +Return-Path: +X-Original-To: incoming@patchwork.ozlabs.org +Delivered-To: patchwork-incoming@bilbo.ozlabs.org +Authentication-Results: bilbo.ozlabs.org; + dkim=pass (2048-bit key; + unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 + header.s=20210112 header.b=cK81Bg2I; + dkim-atps=neutral +Authentication-Results: ozlabs.org; + spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de + (client-ip=85.214.62.61; helo=phobos.denx.de; + envelope-from=u-boot-bounces@lists.denx.de; receiver=) +Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) + key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest + SHA256) + (No client certificate requested) + by bilbo.ozlabs.org (Postfix) with ESMTPS id 4JPz7X0bFyz9s0r + for ; Fri, 31 Dec 2021 06:37:26 +1100 (AEDT) +Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) + by phobos.denx.de (Postfix) with ESMTP id 0F0B2804C8; + Thu, 30 Dec 2021 20:37:18 +0100 (CET) +Authentication-Results: phobos.denx.de; + dmarc=pass (p=none dis=none) header.from=gmail.com +Authentication-Results: phobos.denx.de; + spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de +Authentication-Results: phobos.denx.de; + dkim=pass (2048-bit key; + unprotected) header.d=gmail.com header.i=@gmail.com header.b="cK81Bg2I"; + dkim-atps=neutral +Received: by phobos.denx.de (Postfix, from userid 109) + id AEDBD811BB; Thu, 30 Dec 2021 20:37:16 +0100 (CET) +X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de +X-Spam-Level: +X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, + DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,SPF_HELO_NONE, + SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 +Received: from mail-ed1-x52d.google.com (mail-ed1-x52d.google.com + [IPv6:2a00:1450:4864:20::52d]) + (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) + (No client certificate requested) + by phobos.denx.de (Postfix) with ESMTPS id DFF2E81184 + for ; Thu, 30 Dec 2021 20:37:12 +0100 (CET) +Authentication-Results: phobos.denx.de; + dmarc=pass (p=none dis=none) header.from=gmail.com +Authentication-Results: phobos.denx.de; + spf=pass smtp.mailfrom=alpernebiyasak@gmail.com +Received: by mail-ed1-x52d.google.com with SMTP id x15so101755439edv.1 + for ; Thu, 30 Dec 2021 11:37:12 -0800 (PST) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; + h=from:to:cc:subject:date:message-id:mime-version + :content-transfer-encoding; + bh=o95ZU+qnz+og5Dat/q/VTF9YYww8PBCzA+ABZSXXolA=; + b=cK81Bg2IxzE3JOMrYmY71vV4AwQM5u4QfMs8UyJlfcZ1/+ES5KYKqOqFfpKFnoHmhj + dExR4ZG55QcIoQvX2QjSsh5m4oqpvqaJRCHmG8MNFscusyVvJvZidSC4A6MsKFm/xDhO + 6trpXrffdaK4mrMLrlpNwtX4zvGFrQ8BfwsRN6vkYzissuXrMEHMB8gnY2z9FMvnQepB + suI6C2oVprB/tyo+iFw6q9vnxnFxbNMaU6Kbevqekv1lG4k2bAKTTcgLTUETQ8fbYtPt + 8f1NCTBCNS2fRO0fx1tGhK9Ymw8hLuO8f0Bzrj+ZixtmYiR9QRbZU/OYM/dvO0SVCQAo + 6/cA== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; + h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version + :content-transfer-encoding; + bh=o95ZU+qnz+og5Dat/q/VTF9YYww8PBCzA+ABZSXXolA=; + b=2fadn4WWHwuQAEwrV9KPDpx5of4g6aU2eu41AiBB7973C1mGKZc9wITb6nQTG1VesX + 2N/LdQdgK6TKFXUcJnujd8Kh2+aTbmxK5aa4CT48DOc+FjvNWln6+GW3Cu2NYVuM1P+P + AC8aeVMpezmVLZMnUFgc46wf/9vd/U0SG6I5Iv3/89f4IjiajTPgp2yZZSSBPmK0QQoJ + y+w0KYKeICH1Q/IiYUvQjgEvWY3DdK6si2QT+UO9I0xo+gkK3CwaEpVIVQ6HC75FEPLZ + HRLoHTUYGCu30AZsXToNOOR/eX5cjyzON64fUyBqO69BiI43Hh9x8usz+VdI8B2LcOT+ + 7WuQ== +X-Gm-Message-State: AOAM533kWmvGofl0Ku1us2MhhVDF3yCz3i6FyVMhb7qhAA3dYLiAjW43 + WXFRC/b1oHVMXZDbmQz2s6c0KYDb8KI= +X-Google-Smtp-Source: + ABdhPJwyxnfOXQuo28I9zKzDtG6KUXF845MW0EPlPCt/upF5MoMLbHxEazYxf+uas6Ce8af3fcfE5g== +X-Received: by 2002:a50:d74e:: with SMTP id + i14mr31016125edj.243.1640893032437; + Thu, 30 Dec 2021 11:37:12 -0800 (PST) +Received: from localhost.localdomain ([178.233.26.119]) + by smtp.gmail.com with ESMTPSA id y13sm9836432edq.77.2021.12.30.11.37.09 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Thu, 30 Dec 2021 11:37:11 -0800 (PST) +From: Alper Nebi Yasak +To: u-boot@lists.denx.de +Cc: Frank Wang , + Andre Przywara , Tom Rini , + Simon Glass , Neil Armstrong , + Grant Likely , + Joe Hershberger , + Peter Robinson , + Jagan Teki , + Patrick Delaunay , + Grant Likely , Icenowy Zheng , + Alper Nebi Yasak +Subject: [PATCH v3] phy: Track power-on and init counts in uclass +Date: Thu, 30 Dec 2021 22:36:51 +0300 +Message-Id: <20211230193652.33514-1-alpernebiyasak@gmail.com> +X-Mailer: git-send-email 2.34.1 +MIME-Version: 1.0 +X-BeenThere: u-boot@lists.denx.de +X-Mailman-Version: 2.1.38 +Precedence: list +List-Id: U-Boot discussion +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +Errors-To: u-boot-bounces@lists.denx.de +Sender: "U-Boot" +X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de +X-Virus-Status: Clean + +On boards using the RK3399 SoC, the USB OHCI and EHCI controllers share +the same PHY device instance. While these controllers are being stopped +they both attempt to power-off and deinitialize it, but trying to +power-off the deinitialized PHY device results in a hang. This usually +happens just before booting an OS, and can be explicitly triggered by +running "usb start; usb stop" in the U-Boot shell. + +Implement a uclass-wide counting mechanism for PHY initialization and +power state change requests, so that we don't power-off/deinitialize a +PHY instance until all of its users want it done. The Allwinner A10 USB +PHY driver does this counting in-driver, remove those parts in favour of +this in-uclass implementation. + +The sandbox PHY operations test needs some changes since the uclass will +no longer call into the drivers for actions matching its tracked state +(e.g. powering-off a powered-off PHY). Update that test, and add a new +one which simulates multiple users of a single PHY. + +The major complication here is that PHY handles aren't deduplicated per +instance, so the obvious idea of putting the counts in the PHY handles +don't immediately work. It seems possible to bind a child udevice per +PHY instance to the PHY provider and deduplicate the handles in each +child's uclass-private areas, like in the CLK framework. An alternative +approach could be to use those bound child udevices themselves as the +PHY handles. Instead, to avoid the architectural changes those would +require, this patch solves things by dynamically allocating a list of +structs (one per instance) in the provider's uclass-private area. + +Signed-off-by: Alper Nebi Yasak +Reviewed-by: Simon Glass +--- + +Changes in v3: +- Add tag: "Reviewed-by: Simon Glass " +- Add comment for phy_counts struct + +v2: https://patchwork.ozlabs.org/project/uboot/patch/20211224130549.20276-1-alpernebiyasak@gmail.com/ + +Changes in v2: +- Rename {phy_,}id_priv -> {phy_,}counts +- Split phy_get_uclass_priv -> phy_{alloc,get}_counts +- Allocate counts (or return error) in generic_phy_get_by_*() +- Remove now-unnecessary null checks for counts of valid phy handles + +v1: https://patchwork.ozlabs.org/project/uboot/patch/20211210200124.19226-1-alpernebiyasak@gmail.com/ + + drivers/phy/allwinner/phy-sun4i-usb.c | 9 -- + drivers/phy/phy-uclass.c | 137 ++++++++++++++++++++++++++ + test/dm/phy.c | 83 +++++++++++++++- + 3 files changed, 215 insertions(+), 14 deletions(-) + +diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c +index ab2a5d17fcff..86c589a65fd3 100644 +--- a/drivers/phy/allwinner/phy-sun4i-usb.c ++++ b/drivers/phy/allwinner/phy-sun4i-usb.c +@@ -125,7 +125,6 @@ struct sun4i_usb_phy_info { + + struct sun4i_usb_phy_plat { + void __iomem *pmu; +- int power_on_count; + int gpio_vbus; + int gpio_vbus_det; + int gpio_id_det; +@@ -225,10 +224,6 @@ static int sun4i_usb_phy_power_on(struct phy *phy) + initial_usb_scan_delay = 0; + } + +- usb_phy->power_on_count++; +- if (usb_phy->power_on_count != 1) +- return 0; +- + if (usb_phy->gpio_vbus >= 0) + gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_UP); + +@@ -240,10 +235,6 @@ static int sun4i_usb_phy_power_off(struct phy *phy) + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + +- usb_phy->power_on_count--; +- if (usb_phy->power_on_count != 0) +- return 0; +- + if (usb_phy->gpio_vbus >= 0) + gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_DISABLE); + +diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c +index 59683a080cd7..cec46c2c4103 100644 +--- a/drivers/phy/phy-uclass.c ++++ b/drivers/phy/phy-uclass.c +@@ -11,12 +11,96 @@ + #include + #include + #include ++#include ++ ++/** ++ * struct phy_counts - Init and power-on counts of a single PHY port ++ * ++ * This structure is used to keep track of PHY initialization and power ++ * state change requests, so that we don't power off and deinitialize a ++ * PHY instance until all of its users want it done. Otherwise, multiple ++ * consumers using the same PHY port can cause problems (e.g. one might ++ * call power_off() after another's exit() and hang indefinitely). ++ * ++ * @id: The PHY ID within a PHY provider ++ * @power_on_count: Times generic_phy_power_on() was called for this ID ++ * without a matching generic_phy_power_off() afterwards ++ * @init_count: Times generic_phy_init() was called for this ID ++ * without a matching generic_phy_exit() afterwards ++ * @list: Handle for a linked list of these structures corresponding to ++ * ports of the same PHY provider ++ */ ++struct phy_counts { ++ unsigned long id; ++ int power_on_count; ++ int init_count; ++ struct list_head list; ++}; + + static inline struct phy_ops *phy_dev_ops(struct udevice *dev) + { + return (struct phy_ops *)dev->driver->ops; + } + ++static struct phy_counts *phy_get_counts(struct phy *phy) ++{ ++ struct list_head *uc_priv; ++ struct phy_counts *counts; ++ ++ if (!generic_phy_valid(phy)) ++ return NULL; ++ ++ uc_priv = dev_get_uclass_priv(phy->dev); ++ list_for_each_entry(counts, uc_priv, list) ++ if (counts->id == phy->id) ++ return counts; ++ ++ return NULL; ++} ++ ++static int phy_alloc_counts(struct phy *phy) ++{ ++ struct list_head *uc_priv; ++ struct phy_counts *counts; ++ ++ if (!generic_phy_valid(phy)) ++ return 0; ++ if (phy_get_counts(phy)) ++ return 0; ++ ++ uc_priv = dev_get_uclass_priv(phy->dev); ++ counts = kzalloc(sizeof(*counts), GFP_KERNEL); ++ if (!counts) ++ return -ENOMEM; ++ ++ counts->id = phy->id; ++ counts->power_on_count = 0; ++ counts->init_count = 0; ++ list_add(&counts->list, uc_priv); ++ ++ return 0; ++} ++ ++static int phy_uclass_pre_probe(struct udevice *dev) ++{ ++ struct list_head *uc_priv = dev_get_uclass_priv(dev); ++ ++ INIT_LIST_HEAD(uc_priv); ++ ++ return 0; ++} ++ ++static int phy_uclass_pre_remove(struct udevice *dev) ++{ ++ struct list_head *uc_priv = dev_get_uclass_priv(dev); ++ struct phy_counts *counts, *next; ++ ++ list_for_each_entry_safe(counts, next, uc_priv, list) ++ kfree(counts); ++ ++ return 0; ++} ++ + static int generic_phy_xlate_offs_flags(struct phy *phy, + struct ofnode_phandle_args *args) + { +@@ -88,6 +172,12 @@ int generic_phy_get_by_index_nodev(ofnode node, int index, struct phy *phy) + goto err; + } + ++ ret = phy_alloc_counts(phy); ++ if (ret) { ++ debug("phy_alloc_counts() failed: %d\n", ret); ++ goto err; ++ } ++ + return 0; + + err: +@@ -118,6 +208,7 @@ int generic_phy_get_by_name(struct udevice *dev, const char *phy_name, + + int generic_phy_init(struct phy *phy) + { ++ struct phy_counts *counts; + struct phy_ops const *ops; + int ret; + +@@ -126,10 +217,19 @@ int generic_phy_init(struct phy *phy) + ops = phy_dev_ops(phy->dev); + if (!ops->init) + return 0; ++ ++ counts = phy_get_counts(phy); ++ if (counts->init_count > 0) { ++ counts->init_count++; ++ return 0; ++ } ++ + ret = ops->init(phy); + if (ret) + dev_err(phy->dev, "PHY: Failed to init %s: %d.\n", + phy->dev->name, ret); ++ else ++ counts->init_count = 1; + + return ret; + } +@@ -154,6 +254,7 @@ int generic_phy_reset(struct phy *phy) + + int generic_phy_exit(struct phy *phy) + { ++ struct phy_counts *counts; + struct phy_ops const *ops; + int ret; + +@@ -162,16 +263,28 @@ int generic_phy_exit(struct phy *phy) + ops = phy_dev_ops(phy->dev); + if (!ops->exit) + return 0; ++ ++ counts = phy_get_counts(phy); ++ if (counts->init_count == 0) ++ return 0; ++ if (counts->init_count > 1) { ++ counts->init_count--; ++ return 0; ++ } ++ + ret = ops->exit(phy); + if (ret) + dev_err(phy->dev, "PHY: Failed to exit %s: %d.\n", + phy->dev->name, ret); ++ else ++ counts->init_count = 0; + + return ret; + } + + int generic_phy_power_on(struct phy *phy) + { ++ struct phy_counts *counts; + struct phy_ops const *ops; + int ret; + +@@ -180,16 +293,26 @@ int generic_phy_power_on(struct phy *phy) + ops = phy_dev_ops(phy->dev); + if (!ops->power_on) + return 0; ++ ++ counts = phy_get_counts(phy); ++ if (counts->power_on_count > 0) { ++ counts->power_on_count++; ++ return 0; ++ } ++ + ret = ops->power_on(phy); + if (ret) + dev_err(phy->dev, "PHY: Failed to power on %s: %d.\n", + phy->dev->name, ret); ++ else ++ counts->power_on_count = 1; + + return ret; + } + + int generic_phy_power_off(struct phy *phy) + { ++ struct phy_counts *counts; + struct phy_ops const *ops; + int ret; + +@@ -198,10 +321,21 @@ int generic_phy_power_off(struct phy *phy) + ops = phy_dev_ops(phy->dev); + if (!ops->power_off) + return 0; ++ ++ counts = phy_get_counts(phy); ++ if (counts->power_on_count == 0) ++ return 0; ++ if (counts->power_on_count > 1) { ++ counts->power_on_count--; ++ return 0; ++ } ++ + ret = ops->power_off(phy); + if (ret) + dev_err(phy->dev, "PHY: Failed to power off %s: %d.\n", + phy->dev->name, ret); ++ else ++ counts->power_on_count = 0; + + return ret; + } +@@ -316,4 +450,7 @@ int generic_phy_power_off_bulk(struct phy_bulk *bulk) + UCLASS_DRIVER(phy) = { + .id = UCLASS_PHY, + .name = "phy", ++ .pre_probe = phy_uclass_pre_probe, ++ .pre_remove = phy_uclass_pre_remove, ++ .per_device_auto = sizeof(struct list_head), + }; +diff --git a/test/dm/phy.c b/test/dm/phy.c +index ecbd47bf12fd..df4c73fc701f 100644 +--- a/test/dm/phy.c ++++ b/test/dm/phy.c +@@ -79,12 +79,15 @@ static int dm_test_phy_ops(struct unit_test_state *uts) + ut_assertok(generic_phy_power_off(&phy1)); + + /* +- * test operations after exit(). +- * The sandbox phy driver does not allow it. ++ * Test power_on() failure after exit(). ++ * The sandbox phy driver does not allow power-on/off after ++ * exit, but the uclass counts power-on/init calls and skips ++ * calling the driver's ops when e.g. powering off an already ++ * powered-off phy. + */ + ut_assertok(generic_phy_exit(&phy1)); + ut_assert(generic_phy_power_on(&phy1) != 0); +- ut_assert(generic_phy_power_off(&phy1) != 0); ++ ut_assertok(generic_phy_power_off(&phy1)); + + /* + * test normal operations again (after re-init) +@@ -99,6 +102,17 @@ static int dm_test_phy_ops(struct unit_test_state *uts) + */ + ut_assertok(generic_phy_reset(&phy1)); + ++ /* ++ * Test power_off() failure after exit(). ++ * For this we need to call exit() while the phy is powered-on, ++ * so that the uclass actually calls the driver's power-off() ++ * and reports the resulting failure. ++ */ ++ ut_assertok(generic_phy_power_on(&phy1)); ++ ut_assertok(generic_phy_exit(&phy1)); ++ ut_assert(generic_phy_power_off(&phy1) != 0); ++ ut_assertok(generic_phy_power_on(&phy1)); ++ + /* PHY2 has a known problem with power off */ + ut_assertok(generic_phy_init(&phy2)); + ut_assertok(generic_phy_power_on(&phy2)); +@@ -106,8 +120,8 @@ static int dm_test_phy_ops(struct unit_test_state *uts) + + /* PHY3 has a known problem with power off and power on */ + ut_assertok(generic_phy_init(&phy3)); +- ut_asserteq(-EIO, generic_phy_power_off(&phy3)); +- ut_asserteq(-EIO, generic_phy_power_off(&phy3)); ++ ut_asserteq(-EIO, generic_phy_power_on(&phy3)); ++ ut_assertok(generic_phy_power_off(&phy3)); + + return 0; + } +@@ -145,3 +159,62 @@ static int dm_test_phy_bulk(struct unit_test_state *uts) + return 0; + } + DM_TEST(dm_test_phy_bulk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); ++ ++static int dm_test_phy_multi_exit(struct unit_test_state *uts) ++{ ++ struct phy phy1_method1; ++ struct phy phy1_method2; ++ struct phy phy1_method3; ++ struct udevice *parent; ++ ++ /* Get the same phy instance in 3 different ways. */ ++ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, ++ "gen_phy_user", &parent)); ++ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method1)); ++ ut_asserteq(0, phy1_method1.id); ++ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method2)); ++ ut_asserteq(0, phy1_method2.id); ++ ut_asserteq_ptr(phy1_method1.dev, phy1_method1.dev); ++ ++ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, ++ "gen_phy_user1", &parent)); ++ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method3)); ++ ut_asserteq(0, phy1_method3.id); ++ ut_asserteq_ptr(phy1_method1.dev, phy1_method3.dev); ++ ++ /* ++ * Test using the same PHY from different handles. ++ * In non-test code these could be in different drivers. ++ */ ++ ++ /* ++ * These must only call the driver's ops at the first init() ++ * and power_on(). ++ */ ++ ut_assertok(generic_phy_init(&phy1_method1)); ++ ut_assertok(generic_phy_init(&phy1_method2)); ++ ut_assertok(generic_phy_power_on(&phy1_method1)); ++ ut_assertok(generic_phy_power_on(&phy1_method2)); ++ ut_assertok(generic_phy_init(&phy1_method3)); ++ ut_assertok(generic_phy_power_on(&phy1_method3)); ++ ++ /* ++ * These must not call the driver's ops as other handles still ++ * want the PHY powered-on and initialized. ++ */ ++ ut_assertok(generic_phy_power_off(&phy1_method3)); ++ ut_assertok(generic_phy_exit(&phy1_method3)); ++ ++ /* ++ * We would get an error here if the generic_phy_exit() above ++ * actually called the driver's exit(), as the sandbox driver ++ * doesn't allow power-off() after exit(). ++ */ ++ ut_assertok(generic_phy_power_off(&phy1_method1)); ++ ut_assertok(generic_phy_power_off(&phy1_method2)); ++ ut_assertok(generic_phy_exit(&phy1_method1)); ++ ut_assertok(generic_phy_exit(&phy1_method2)); ++ ++ return 0; ++} ++DM_TEST(dm_test_phy_multi_exit, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);