From nobody Fri Sep 25 20:47:54 2026 Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BD5DE582B96 for ; Tue, 8 Sep 2026 15:57:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883037; cv=none; b=U0bT4J5wm4tw482kIyAjlteFPY/uSJBUXtYD2hiJi7APPfvdb/a05H/dNz+gy6NAOyRKlVuZeSEqzMBMv+wEDWHTcZofbKg14x7O1ZG2Y7ggKB0M24w0FcTGHuJdQkd+tje64a0mUMD/oYnLAw4JhwF7weXm7kyYfPulJgept9Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883037; c=relaxed/simple; bh=HUiQgG0Ab58RzH7cTOULN/FwqxmG3Zy8ZtGvppbbjQU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RK/+55cNcB0eLHKqKS600za1kVg3OX91R4LrstsNIJtD80qhwqLEDRol4uBXqgbaJBWaTvSExt1fIPS9+ApGK2MNYJq/C+Oiahg1ysuYYLI0EfmDq827RDvPh+vfpE3l2Evh644+HuhVABFiuL2geLp4zSVsFSexqY65MXPvGSU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=NJlMwxPL; arc=none smtp.client-ip=209.85.221.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="NJlMwxPL" Received: by mail-wr1-f53.google.com with SMTP id ffacd0b85a97d-485843aeab8so5353068f8f.1 for ; Tue, 08 Sep 2026 08:57:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788883031; x=1789487831; darn=vger.kernel.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:content-type; bh=82jzbCOQ45+MaSZigwp+ZA2kvhC92VJszaAiVh+iTr8=; b=NJlMwxPLHxVA0SpxQ4+K6HUYtdhSPnFmkFCkhnGZ7XG6mDwzJEZKG6C4JwMOBzHojp fbZx6Z3XEFQjz54LkgoEWStuQdjj6L+7mElzKLg98TudZ2XvJCX3bu9/58Tzz+cSnc4J A/cUK5k6yWQnDkiLnYCbtF/ZGjdlAqfUial7qrxFKADY/XJ04uqna6qPqMlAbNmga9Ny q61ZBuA10nboB6Ss1/KrX0wnr7PKBlE0ccyQrMnXA7ogYjQ+YfqEaxA/14VVqzAdQ9at R/zXatFTC72IsnLD1R3JHKb206fRHEhHA0Hc8Hl5qEe0FpNK0h1SKBMCxNn/+l6KGGfn uIBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788883031; x=1789487831; 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:content-type; bh=82jzbCOQ45+MaSZigwp+ZA2kvhC92VJszaAiVh+iTr8=; b=l6LTA9pATwtgKy9f/PxOv+PEsvFzbT0H3j1GlHZj+7uUNZ+pYCJOVkTYuP4j8WBgTg LaSYfDRwaK5dAri56cBYsVdYg292BZDW1PRFZV4VrEPYN1zb6on1Hn/H3vSn8enwQarK u8MNIeCfPQ/EZAfqvzmPqy/HmE3LIX9TmtTGZoMGkzM+xkc153euDPcoYULwhgfGSNY4 R37CmUuXwi74Js+KxYZxUEk1zQlpOdX+pMc/IVrls32UNX5D0VxuSqq/jRUTkLvShNoh bzguCOgIRc0ZIOys5j21zC8NxANI0I5tZTz6F31IicEUhZyfm2FgpVN5sdMQPSaI8oEe KP3g== X-Forwarded-Encrypted: i=1; AKwUvBzznAOvkkgkE9DiscvIVfw9OBLnrn6nqtF10FgKTRtMiLdM5ydv+RE1zaEuWj4LgnqErH8o8bOtpPJbnwg=@vger.kernel.org X-Gm-Message-State: AFuF++kzkAqAAmrioIU6RKlHZfP7sA5f5v759uoFMzXiDOaWI4G9jjZy fUhIlpn9CYwoK0HJksRWHdPFsP4hu/tL+s3rTZcJRq+Sl8t9Hr9gTOUeKgx/McHhzVc= X-Gm-Gg: AYBFou03Z1ez/t5ZMmmNGm76YxEdxQ8e627XvhQIhHOK7qj5Pz2lAGzUoocSrvEVEqe bXE3iEtRXzXPpAlxth7qLQ5lkGSPegI+p6Jic/7lPQI1P7rb8xchmOFOdRx2KJTEAWyfHEGPE8P yKyQ6R+J7BhO+epBtPMV6HO54204fBJPF8R9rF7+WtSVK3W7JRCksQYk9DO5aveoUkox0Z72hur UXLDFanZ61zzk6Wh2O1LRkRbSApTl2JpB+6sGxpygbwLjZAxOushurr8gd0NMidUoBI48YDM3xe ATcLHaBYwlGlO3FYmzpUzMnVJTqxQweekQsvwLItYu2tBcBta7YFlCNWMStr8Kxsio8p94EP23Y 0f65FSoqUgYnoP7NNWcx7hD4JqOrtv+TMSdIH9RtU7NW2PVG8JSbxghCxtM5Rkr1I/+vHG6aOJD wMzjpMcoJkG9cDxYkMlFjiHmVMnNOqw18/yHnizS4= X-Received: by 2002:a05:6000:430b:b0:485:8ad6:3752 with SMTP id ffacd0b85a97d-4858ad63a6fmr53719486f8f.5.1788883031447; Tue, 08 Sep 2026 08:57:11 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-485883be709sm37345704f8f.21.2026.09.08.08.57.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 08:57:11 -0700 (PDT) From: Aleksei Sviridkin To: andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: ericwouds@gmail.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Aleksei Sviridkin Subject: [RFC PATCH net-next v2 1/7] dt-bindings: net: add Airoha EN8811H PHY MCU Date: Tue, 8 Sep 2026 15:57:01 +0000 Message-ID: <20260908155707.4164559-2-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260908155707.4164559-1-f@lex.la> References: <20260908155707.4164559-1-f@lex.la> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The EN8811H answers its PHY ID from power-on, but a link is only possible once its MD32 MCU runs firmware that the host loads over MDIO into volatile RAM. On systems that keep the firmware files in a filesystem, the files arrive long after the MDIO bus was scanned, and the PHY node alone cannot express that gap. Describe the MCU as an MDIO device of its own, with the PHY node on a child bus underneath it. The device downloads the firmware when the files appear, or adopts firmware the bootloader left running, and registers the child bus only then, so the PHY never becomes visible before the chip can serve it. The reset line belongs to this node, not to the PHY node: firmware lives in volatile RAM, so reset may be asserted only while the MCU is not executing it. On a PHY node the line would be pulsed by every phy_detach() and wipe the running firmware. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- .../bindings/net/airoha,en8811h-mcu.yaml | 119 ++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 125 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/airoha,en8811h-mc= u.yaml diff --git a/Documentation/devicetree/bindings/net/airoha,en8811h-mcu.yaml = b/Documentation/devicetree/bindings/net/airoha,en8811h-mcu.yaml new file mode 100644 index 000000000000..b44331d80315 --- /dev/null +++ b/Documentation/devicetree/bindings/net/airoha,en8811h-mcu.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/airoha,en8811h-mcu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN8811H PHY MCU + +maintainers: + - Aleksei Sviridkin + +description: | + The Airoha EN8811H 2.5G PHY is built around an MD32 core that executes + firmware from volatile RAM. The chip answers an MDIO address from + power-on, but the PHY function does not exist until firmware has been + loaded over MDIO, and it disappears again if that RAM is cleared. + + This node describes the MD32 core. The PHY function answers on a child + MDIO bus below it, at the same address the core occupies on the parent + bus, and only while firmware is running. + + The reset line is wired to the core, and asserting it clears the + firmware RAM. It therefore belongs to this node: a reset described on + the PHY node would be pulsed by that node's own lifecycle, whatever + the core is doing. + + A board that has firmware in hand while its MDIO bus is enumerated can + describe the PHY on its own, as airoha,en8811h.yaml does, and that form + stays valid. This binding is for the boards where firmware arrives + later, so that there is no PHY to describe until the core has it. + +properties: + $nodename: + pattern: "^mcu@[0-9a-f]+$" + + compatible: + const: airoha,en8811h-mcu + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: + The GPIO phandle and specifier for the reset signal wired to the + MD32 core. + + reset-assert-us: + description: + Delay after the reset was asserted in microseconds. If this + property is missing the delay will be skipped. + + reset-deassert-us: + description: + Delay after the reset was deasserted in microseconds. If this + property is missing the delay will be skipped. + + mdio: + $ref: mdio.yaml# + description: + The bus the PHY function answers on while firmware is running, at + the same address this node occupies on the parent bus. Only that + one address is passed through. + + patternProperties: + '^ethernet-phy@[0-9a-f]+$': + type: object + $ref: airoha,en8811h.yaml# + unevaluatedProperties: false + + properties: + reset-gpios: false + reset-assert-us: false + reset-deassert-us: false + resets: false + reset-names: false + + description: + The reset line belongs to the MCU node above. A reset described + here would be pulsed by this node's own lifecycle, and that eras= es + the firmware that created this PHY. + + unevaluatedProperties: false + +required: + - compatible + - reg + - mdio + +additionalProperties: false + +examples: + - | + #include + #include + + mdio { + #address-cells =3D <1>; + #size-cells =3D <0>; + + mcu@d { + compatible =3D "airoha,en8811h-mcu"; + reg =3D <0xd>; + reset-gpios =3D <&pio 14 GPIO_ACTIVE_LOW>; + reset-assert-us =3D <10000>; + reset-deassert-us =3D <20000>; + + mdio { + #address-cells =3D <1>; + #size-cells =3D <0>; + + ethernet-phy@d { + compatible =3D "ethernet-phy-id03a2.a411"; + reg =3D <0xd>; + interrupts-extended =3D <&pio 15 IRQ_TYPE_LEVEL_LOW>; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index b23fb6f2f4ef..4ca19275f4d0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -750,6 +750,12 @@ S: Supported F: fs/aio.c F: include/linux/*aio*.h =20 +AIROHA EN8811H MCU MDIO DRIVER +M: Aleksei Sviridkin +L: netdev@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/net/airoha,en8811h-mcu.yaml + AIROHA ETHERNET DRIVER M: Lorenzo Bianconi L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) --=20 2.53.0 From nobody Fri Sep 25 20:47:54 2026 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8A5FC582BAB for ; Tue, 8 Sep 2026 15:57:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883038; cv=none; b=h7uWsX+E1vKCXFuubtY4BxRUdpK/BqP0IWwypbnlsQ/z62Xl5oHFNXRVGfBsjYfj7chM1A8NIfv5ONTIuzNbQWrNq6Bbu7cvQmQ+Ic7KRc7RDszCq9fBJis16ri7efwnY6EqThBVwdUJyqAFN+YTz04tDuZTqsMIfHwKFewz/ms= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883038; c=relaxed/simple; bh=K244GoaR/SBN1qTkqznMbdB/qRyg1uAQxHof7eZyYLA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GYSC/kBTJD4WkMdHv0UJIh0nSz7Cp+PV822P0WnY7HKDajEDuxt1ppRaVtej9yTgEqfl3PThDtcPcko6M6Y7JhBt3xpDQRzBEfLhG1ihwfQOTu6bfXdJoXL8gNDUVU/mthuQGgpOTNZMNm7782LO9vLZjzrGNwihrIGlqwRaIDg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=BP8nITiC; arc=none smtp.client-ip=209.85.221.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="BP8nITiC" Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-4858303de5dso5860267f8f.2 for ; Tue, 08 Sep 2026 08:57:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788883033; x=1789487833; darn=vger.kernel.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:content-type; bh=nwJE/G6DLPhkatwmSaqyq8HEYQKnbZm9tru5auWihS0=; b=BP8nITiCIEsy0JEGklGDMKuWpAgorlXpkH2F6YRnmPqaFXdDadP5kPIJNyMvNluzlG aM539YAje5/MxDx7hzLBGUoaBuLKGfS9kPHLa+GT43rnS+S2dy3WxZ50e0V4RqrKO20z QrN5H7GtW4XojmW1rowEd73eYpdM3VpBh4Vsa+uNh+LzdLLPFz/SFLDOsg3QA7Lssb1i 1/qSvlHJRXiEKXy0xHpte6oxGp8vk1XkJBYLpsA7QPMHyaknAlcGqVV3LBpWaU/2rmOp ngC8yS/WL9i4K3epRWdtH3OrAG0tWU7Dyv3j1aYUwkkrQ5mKxGPpYeRulDnJO/oDGwkS FW2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788883033; x=1789487833; 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:content-type; bh=nwJE/G6DLPhkatwmSaqyq8HEYQKnbZm9tru5auWihS0=; b=RNM9UaZBJNeiHaFc/qdsUZGmA/FXpB1RbwWZeqcz4hdMwNKl6+puM8U4t3wu0c2ecQ kroVd2S21mS9mET5qLAY1omru+YKiSWXW4p2BGuk7HN40pqka9BamJhKQWRnlxj2hZTp dD5cg5c9R/7j6f24jkkNTzH0FO8LeWlDhcJ7L7mrfRV9ArmqsdZxWUOsZXbwWKRGHWd4 SeGWVLfVG6vnceKomjk/QroRLQk5Ud40Np87W/xQ1WIRqVsRXnY2A+hIo1Ougd4YAOLE B94rvnikkOhWBZT5AXFFSHd5gB29UVn8fbsujharru5+BrTpS7/vN8riuC1h1AoE51UK pP7g== X-Forwarded-Encrypted: i=1; AKwUvBzLZDexrLbCSxhSHRIgqb5FUb4WPMukHTrlfxQLv3W6ttEd3U1A+H2mqzA/tNATyJ+x6+sRQ3w0pG5gzkI=@vger.kernel.org X-Gm-Message-State: AFuF++kmbnFwSidN542+5nRxab7gFf2kL30A+MsS2Nq1y6b4XZeuuUmw ahimqIKqCBrrK0/99Q2Va+Np3PdXpIGuujWWKcUB5tzBVCnW1wmuYC9iUUc0I/0vjpk= X-Gm-Gg: AYBFou1J7d07o9pnIXC1ZcVoAhMh4TO83gxQEm/Nx0BiHzRwbiqNAKTyjBazDS7NKm4 O4ukC0ZF24KvRunne9f/NWrHrWSb0HE4oNzmpq3Wm2Epg+BLiufyrzJ55JwPOuLpKkyCu3gw7cF pTuhleBwkQEHr6N0+O3lZH7tS8KkTgFiwtq+ZDMEJvemG2pZqIj3nG+l64KUIkDn4eea9c4WP9O 1T+iTdPfQLidLS4iDk7K07LCw03JvnLQNoKRv/hkgZHAWpeFxG2d0vIRCiDQK2xq1kRl1tkUEva IsQ+cMLm8jZ6s4krdHXsDC/nZhKg6yIe7w05QW9Ygis+Y0EtVFNjQ9FK++OGnzu1R0a3jcJYTIp lbFyx1RiRY+ZLwCA5cdXryD8iwJzPDQGG0yvov0lBatO5cLcRQcO/A3Wo2KpPIh61rcvt0Z9jUo kLXtiqtiuFKNt/OixiFfHdc6nf2ybERmhb/nOKslMfMVCuMSuFYQ== X-Received: by 2002:a05:6000:2c11:b0:485:8c16:a340 with SMTP id ffacd0b85a97d-4858c16a82bmr28568008f8f.53.1788883033146; Tue, 08 Sep 2026 08:57:13 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-485883be709sm37345704f8f.21.2026.09.08.08.57.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 08:57:12 -0700 (PDT) From: Aleksei Sviridkin To: andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: ericwouds@gmail.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Aleksei Sviridkin Subject: [RFC PATCH net-next v2 2/7] net: phy: add mdiodev_lock(), mdiodev_lock_nested() and mdiodev_unlock() Date: Tue, 8 Sep 2026 15:57:02 +0000 Message-ID: <20260908155707.4164559-3-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260908155707.4164559-1-f@lex.la> References: <20260908155707.4164559-1-f@lex.la> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Code that holds the MDIO bus across a burst of transactions has phy_lock_mdio_bus(), which needs a phy_device. A library serving a chip before its phy_device exists has to open-code the same mutex. Add the mdio_device spelling beside it. The nested form is for a library whose caller already holds the bus one level up, which is what the EN8811H MDIO driver later in this series does. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- include/linux/phy.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index 3d8afe6b7f1c..e74a8f5cf5c6 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -2284,6 +2284,22 @@ static inline void phy_unlock_mdio_bus(struct phy_de= vice *phydev) mutex_unlock(&phydev->mdio.bus->mdio_lock); } =20 +static inline void mdiodev_lock(struct mdio_device *mdiodev) +{ + mutex_lock(&mdiodev->bus->mdio_lock); +} + +/* For a bus reached through another one, as mdiobus_read_nested() is. */ +static inline void mdiodev_lock_nested(struct mdio_device *mdiodev) +{ + mutex_lock_nested(&mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); +} + +static inline void mdiodev_unlock(struct mdio_device *mdiodev) +{ + mutex_unlock(&mdiodev->bus->mdio_lock); +} + void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) __printf(2, 3); char *phy_attached_info_irq(struct phy_device *phydev) --=20 2.53.0 From nobody Fri Sep 25 20:47:54 2026 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0F09583AA2 for ; Tue, 8 Sep 2026 15:57:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883040; cv=none; b=XPJ8IO2iqOF+dmdX+2s/ucgG3XTYddTpKfESkGx3i8qQRLR6KyuvXkZrrXf/EqzExK2MCzWrXl3lqegX+zMw0t0I0WuF0K4oOxN7P8drNySWsSPyOTeAQxcNjY425ZxUpqdXdtJ/sgaPyoRT3riT3d6hwESow66vAq/37vI3B5w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883040; c=relaxed/simple; bh=mzH/5XaUhv6NQCYgufBy5oWC9jpITM+/znfPg5sAlv0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DPw+YFU3EPj1I33b9q9leUggCjCygDjLHwnklAEbIV5jr/XehcJS1HbLgOCxAvpcnXfyyuvvqG199AvFF/MuoLrs+CSEA1HsvE/IF2G8aUKb8uyZ9sXHP78Rnq7/ovqZn86oRXli/7GT97QMrw5XVzkpYD2yucri+Gh4d2AT9wA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=ZZJ4jIUn; arc=none smtp.client-ip=209.85.221.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="ZZJ4jIUn" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-48589798dbbso3980957f8f.3 for ; Tue, 08 Sep 2026 08:57:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788883035; x=1789487835; darn=vger.kernel.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:content-type; bh=YfiP06w66PJnEqo2UKKQtS6GK/rrfhVqIhL6B6Ho9XA=; b=ZZJ4jIUnWChO1HRnNr8uRBBkq/7Tg98Mdj17P3zP0jkQ5oh5OikoapoNFvURgfMAq9 7gXKACengPgKiK6PHw+e/WLkv/vLn+WIjmV/uEShjNdc99Qro2EsPoIwypcPEGi9z/ej N+9SvWC/148IEVQWUVMz/qBe/ckSyuqxAciPR5N9//2j3bkp341EoeNWcwT5LhTZL/re 9oqqQVDB2DsK1Uq/OTocvGKitFQoJcBH8cd7l3iMLqFapiAtzvTdofKu6atB3Dlk4TW3 YxFhbnYjI0fbVP+/xHh0i1vLEUtU5jab2oFYpjrA8kZ/tZ2ezaopMwEoRYIqKLx5T5GL x6tw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788883035; x=1789487835; 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:content-type; bh=YfiP06w66PJnEqo2UKKQtS6GK/rrfhVqIhL6B6Ho9XA=; b=OI2tJoSQWbFvgXV4HfRV5CFAMHx1I5j/w+I6ZhOuRfjBMN5Yqtl+kHf1RB2gSY4PKd jea9Fh2IIgiDExdbUP7ZKGUC/7j3UZmYX1KIP7edY3v3LXWPTVCFTriSs8othj/P06fy SbjuRfsnIUeAyvrzPRgGDpCKpgZb8EHU5I8uzyEH9RNOedzfbAvahwnzaNJpRTbl0Dqk xrrwqAnrnfTW2ZBpPm7p7y4EBrjShLsSvhrLdaz4IyNfmyrchSDKbc+ZhVtmtmICbV7o YC8CflCv694xMtYhS1gv3lTO9dffZdFzqvOUaw0mYmEVcFTCDLNdkYycTVfnJjLyJUY9 e9xA== X-Forwarded-Encrypted: i=1; AKwUvByc5ksJxXZTAHdpZjmzHbpyOZVgZhTOsA/UV0fkC8/+VKs5Xpmpd8WF8QD4NqxN/ooIfim+bkcd6RSSOQU=@vger.kernel.org X-Gm-Message-State: AFuF++li1y23OL1YsaRdArzlUJixhOWEs1kkNeFLhEc6ytjjAxOwZYPn 8AGNt0vuxc4e+FRuIYYeIWNWkmNLkKyLEyc5vYHXIjqbGSaaOFXgScU2D5P+8SL5noU= X-Gm-Gg: AYBFou3n+4FTfGFgkz+1RRBq92W6KhKahICFQscbnMAUqdAB8Nn5ceF+ebPMRzAobD3 3mWVVnNqC1qWUucdLiovwQ9C4UZIsPM5csOgmQXF/vhFZ8Uk+cHFG8TMjoHow3idVRQ1lrM3M3A O+TgH0rTc2dpKfNpm/PXmXuevgXRamip3fe0lZrVY+KPbSR4G7FlgnmWvB/70M7+S4yjDiMP5tw IR+D0vRRaWf7K8HkiDIbpcXg6p2koI33UeMCwomGoA2B9bz1I8iIH7eovSv8qgT071gb0NF1Cpw aTeL7d+YyoxyxEf7TF9SLM+cAt0tBs4gvR4fqhyQwz4ue9QzLZk1f+cyUdCSBGJDcs5N4xeUC0o mT3SfXEdQ4pJVJwpsSHzAAQtsDyrYqdy8tU1PTzeLGgGkX6JNX6P1w1myXf78w8WOeKWGQsUnVc JmyX+Q6NmfT+FZhhxT+27qVNBP2reeMk2xB5znooqkisOtetj1CA== X-Received: by 2002:a05:6000:2209:b0:485:8a46:7057 with SMTP id ffacd0b85a97d-4858a46719cmr30096870f8f.41.1788883034530; Tue, 08 Sep 2026 08:57:14 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-485883be709sm37345704f8f.21.2026.09.08.08.57.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 08:57:14 -0700 (PDT) From: Aleksei Sviridkin To: andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: ericwouds@gmail.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Aleksei Sviridkin Subject: [RFC PATCH net-next v2 3/7] net: phy: air: type the buckpbus core on the mdio device Date: Tue, 8 Sep 2026 15:57:03 +0000 Message-ID: <20260908155707.4164559-4-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260908155707.4164559-1-f@lex.la> References: <20260908155707.4164559-1-f@lex.la> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The buckpbus accessors only need an MDIO device, but they take a phy_device, which ties them to a probed PHY. An upcoming MDIO device driver needs the same register access before any phy_device exists, since it runs precisely to make the PHY presentable. Retype the internal helpers onto an mdio_device and keep the exported phy_device API as page-selecting wrappers that pass &phydev->mdio. The EN8811H driver already reaches the AN8811HB pbus through an mdio_device, so this follows an existing direction rather than opening a new one. No functional change. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- drivers/net/phy/air_phy_lib.c | 80 ++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c index 5141db19fa5e..e314717843ae 100644 --- a/drivers/net/phy/air_phy_lib.c +++ b/drivers/net/phy/air_phy_lib.c @@ -14,31 +14,32 @@ =20 #include "air_phy_lib.h" =20 -static int __air_buckpbus_reg_read(struct phy_device *phydev, +static int __air_buckpbus_reg_read(struct mdio_device *mdiodev, u32 pbus_address, u32 *pbus_data) { int pbus_data_low, pbus_data_high; int ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_MODE, + AIR_BPBUS_MODE_ADDR_FIXED); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, - upper_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, - lower_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); if (ret < 0) return ret; =20 - pbus_data_high =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + pbus_data_high =3D __mdiodev_read(mdiodev, AIR_BPBUS_RD_DATA_HIGH); if (pbus_data_high < 0) return pbus_data_high; =20 - pbus_data_low =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + pbus_data_low =3D __mdiodev_read(mdiodev, AIR_BPBUS_RD_DATA_LOW); if (pbus_data_low < 0) return pbus_data_low; =20 @@ -46,64 +47,66 @@ static int __air_buckpbus_reg_read(struct phy_device *p= hydev, return 0; } =20 -static int __air_buckpbus_reg_write(struct phy_device *phydev, +static int __air_buckpbus_reg_write(struct mdio_device *mdiodev, u32 pbus_address, u32 pbus_data) { int ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_MODE, + AIR_BPBUS_MODE_ADDR_FIXED); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, - upper_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, - lower_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, - upper_16_bits(pbus_data)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, - lower_16_bits(pbus_data)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data)); if (ret < 0) return ret; =20 return 0; } =20 -static int __air_buckpbus_reg_modify(struct phy_device *phydev, +static int __air_buckpbus_reg_modify(struct mdio_device *mdiodev, u32 pbus_address, u32 mask, u32 set) { int pbus_data_low, pbus_data_high; u32 pbus_data_old, pbus_data_new; int ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_MODE, + AIR_BPBUS_MODE_ADDR_FIXED); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, - upper_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, - lower_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); if (ret < 0) return ret; =20 - pbus_data_high =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + pbus_data_high =3D __mdiodev_read(mdiodev, AIR_BPBUS_RD_DATA_HIGH); if (pbus_data_high < 0) return pbus_data_high; =20 - pbus_data_low =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + pbus_data_low =3D __mdiodev_read(mdiodev, AIR_BPBUS_RD_DATA_LOW); if (pbus_data_low < 0) return pbus_data_low; =20 @@ -112,23 +115,23 @@ static int __air_buckpbus_reg_modify(struct phy_devic= e *phydev, if (pbus_data_new =3D=3D pbus_data_old) return 0; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, - upper_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, - lower_16_bits(pbus_address)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, - upper_16_bits(pbus_data_new)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data_new)); if (ret < 0) return ret; =20 - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, - lower_16_bits(pbus_data_new)); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data_new)); if (ret < 0) return ret; =20 @@ -144,7 +147,8 @@ int air_phy_buckpbus_reg_read(struct phy_device *phydev= , u32 pbus_address, saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); =20 if (saved_page >=3D 0) { - ret =3D __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); + ret =3D __air_buckpbus_reg_read(&phydev->mdio, pbus_address, + pbus_data); if (ret < 0) phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, pbus_address, ret); @@ -163,7 +167,7 @@ int air_phy_buckpbus_reg_write(struct phy_device *phyde= v, u32 pbus_address, saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); =20 if (saved_page >=3D 0) { - ret =3D __air_buckpbus_reg_write(phydev, pbus_address, + ret =3D __air_buckpbus_reg_write(&phydev->mdio, pbus_address, pbus_data); if (ret < 0) phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, @@ -183,8 +187,8 @@ int air_phy_buckpbus_reg_modify(struct phy_device *phyd= ev, u32 pbus_address, saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); =20 if (saved_page >=3D 0) { - ret =3D __air_buckpbus_reg_modify(phydev, pbus_address, mask, - set); + ret =3D __air_buckpbus_reg_modify(&phydev->mdio, pbus_address, + mask, set); if (ret < 0) phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, pbus_address, ret); --=20 2.53.0 From nobody Fri Sep 25 20:47:54 2026 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 45A6757F74B for ; Tue, 8 Sep 2026 15:57:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883043; cv=none; b=n9F29VMpebfPTR6IXYMDaVn5ItVYrlxCCI7cNvw3Rz7eDHNM6nzM6c2+T17pArhr24D/b8JZ47ccQ7e9onDPX0MWZYESXwh4AJCgmxT22Ryq71YXVq66OwIPCLOVYlNBXsDgNvd27Z4hEnLUthenHZ/vS1SEmB1F3hann7v4onM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883043; c=relaxed/simple; bh=2p9yZ7Q6Do51PD3NpkTwPtfW3lD7wDbzamssqSTJumM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hzsa0dOfC6e1hvbDSBPKei8RDFseHWonrlVqWY8ENx2XB32aWvre0B/FSm6w2B8Vq7gZHZXB773quZk5aMS/sypJte/dS2R1Sgut57cTMKwwbemiWAGsyL6r49979zXl6by5l6LtByPiT/SGwDA3Fx+D//Xt8QGGRrZXPul72MU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=b8Dv7NTw; arc=none smtp.client-ip=209.85.221.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="b8Dv7NTw" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-482f2ee53e7so3621171f8f.1 for ; Tue, 08 Sep 2026 08:57:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788883037; x=1789487837; darn=vger.kernel.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:content-type; bh=mP/t1/2ZApz5cH6iFCwEZX4W+ynS2xMGthjrCFpGvyU=; b=b8Dv7NTwBS5tTUqS5rU0wBTgScJHvNAW5NaZUoMgCEDSIeFCONbxEa1LRFkLpM18KQ weCFKMCOXSl4jnirZA18B5As/Y7FQyPOpf64/mjcO4cJDZzq8myy2j+fuKG8RS3aFByN pj2Vi9DiJTkkBr3qwGnZe3LtmRv+b+sv1egNr8rSHFWWwP92QihSGAMd22gzWCP4XQxS hwLUnM686d05A8yyOT8xxOFJle+5/IQz9ac4VVWkXUS+d/QrJOHs6B3dIMFSGu7/IsSf J1mB+nXF6B4oK6p+yl15oQflWtvCd83ukOQMwdpEYUaCc/VBzaqzfKkjZs/b/u0Bh4fO I5tQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788883037; x=1789487837; 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:content-type; bh=mP/t1/2ZApz5cH6iFCwEZX4W+ynS2xMGthjrCFpGvyU=; b=hjwFIE1fNT/RbaRr/aOLfpLP6sh/qbh+BjBDDno8mMgnaOfAxshkei5vZXMt64uRRc e8oqRhGbL0u0cnp+tL8ZFAfNLlDajhUOt5pDSRFnMjEzSjPxdxwmnGV6EVJiHVFcPN57 gF17ivJElMaBp/7vP2crwg78bXwV3XIZDEmclWDNdrdTPBgFd5CFjFMcW/4NOSOoJYf9 Duf7+HEkroi+M5QJMiP7c0gTrvrOZry2Wc5C3HyqICXFLeZQiHSHJ3tLT0BLE2bnXIzh ohepdEqYu9tH/c3xzDD/aXSCwhJp4lqbEe9+4Vc/eWLr8DcQ76lujpXa7wzVm8VauZzx rkmA== X-Forwarded-Encrypted: i=1; AKwUvBwI4PKf0JZVoOhn0cJdbuYnT7nlu5Sxm5RFSIeAXnBJNLcDy/yWIEqy/moCo/G3XeDYNV/NPha5ZmFYoTQ=@vger.kernel.org X-Gm-Message-State: AFuF++lijpgf0KZGXH2ugIm3gv3JCPvwJj21U8+GrrhoIaI0f7Ve+uXc k4F+sKIrBdA0ZK/jmLh3sEbf6yPd27kFytv4J0iduo8shFwgPA+g9I3JJLVi6lucQNg= X-Gm-Gg: AYBFou3JssQXpGHCkcCh60X/Z+lzsRmGbPQKEdxMgTtx3eSEcTD6E4zsp32bsIAV2T8 s7mnJySc6wvRhpbqcGgbsptEg509/G6wPuhKY0eVRGDD9BitpvkdEo+dmXdcFNd6Blrhp8k52P3 7doGkofCxkjh6+eoLXQ4kCXqWaRX92wF3fP2S4G0dBidQ50bitMvbcK00pzhr8gK4hI7ZM8OdeJ 9wfcuuNC5IsMH9zaW9HOk19160Dep/OdxoydJth0AZRpDTmKDalLipRQe4ZF7W+Ct7RdLCNhxtK njDaT68Y1xAPtLKcpTAoYlEMOkRoKsIaOhGs5F5wlKgqeIfLEXSVtOOsLt7di/ab7B3QGchC9QI xm6XFJlkvBKqukOlVoBIGoNugUXMPBsU8G5X2BZeVV4KXyx1Ybxt7/vPLwaQCjjaMdvL5FyWpM5 ppu5++O7mEh7iy3foJZeNlLF/YDlAlTln5I2dEh30= X-Received: by 2002:a05:6000:220f:b0:485:8c17:9762 with SMTP id ffacd0b85a97d-4858c17993amr29729185f8f.36.1788883036147; Tue, 08 Sep 2026 08:57:16 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-485883be709sm37345704f8f.21.2026.09.08.08.57.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 08:57:15 -0700 (PDT) From: Aleksei Sviridkin To: andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: ericwouds@gmail.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Aleksei Sviridkin Subject: [RFC PATCH net-next v2 4/7] net: phy: air: move the EN8811H firmware download into the library Date: Tue, 8 Sep 2026 15:57:04 +0000 Message-ID: <20260908155707.4164559-5-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260908155707.4164559-1-f@lex.la> References: <20260908155707.4164559-1-f@lex.la> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The EN8811H firmware loader is welded to the PHY driver, but the next patch adds an MDIO device driver that must perform the same download before any phy_device exists. Move the download engine into the shared library, typed on the MDIO device like the buckpbus core. The moved helpers take the bus lock and save and restore the page register themselves, since phy_select_page() needs a phy_device. The write loop is broken into chunks that each take the bus lock on their own, because the larger of the two blobs held it for 65536 register writes at a stretch and stalled every other PHY on the bus. It did not matter while this only ran from phy_probe() on a quiet bus at boot; the next patches run it on a live system. The status poll asks for the MMD register through the Clause 22 indirection unconditionally. phy_read_mmd() resolved that from phydev->is_c45, which the Clause 22 probe path leaves false, so the constant records what the code already did rather than choosing it. It does the three selection writes itself rather than through mmd_phy_read(), which discards their errors and would let a failed selection read a different register back as a success. The readiness poll's timeout message moves out to the PHY driver's wrapper, which is where the errno was already being reported to a human. The poll is about to gain a caller that retries forever, and an unconditional message there would repeat with every retry; the status value it used to name stays behind at debug level. Two return values change with the move. The version read at the end of the download is now checked, so a bus error there fails the load instead of leaving the version at zero; en8811h_print_fw_version() still discards it on the AN8811HB path, which does not go through this download. And the readiness poll hands back the errno from a failed status read rather than -ENODEV, which reaches the AN8811HB probe too, since it shares the wait. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- drivers/net/phy/air_en8811h.c | 146 +---------------- drivers/net/phy/air_phy_lib.c | 301 ++++++++++++++++++++++++++++++++++ drivers/net/phy/air_phy_lib.h | 29 ++++ 3 files changed, 337 insertions(+), 139 deletions(-) diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index 16a9171cb838..6822774d9ae3 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -20,21 +20,15 @@ #include #include #include -#include =20 #include "air_phy_lib.h" =20 #define EN8811H_PHY_ID 0x03a2a411 #define AN8811HB_PHY_ID 0xc0ff04a0 =20 -#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" -#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" #define AN8811HB_MD32_DM "airoha/an8811hb/EthMD32_CRC.DM.bin" #define AN8811HB_MD32_DSP "airoha/an8811hb/EthMD32_CRC.DSP.bin" =20 -#define AIR_FW_ADDR_DM 0x00000000 -#define AIR_FW_ADDR_DSP 0x00100000 - /* MII Registers */ #define AIR_AUX_CTRL_STATUS 0x1d #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) @@ -44,8 +38,6 @@ #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc =20 /* Registers on MDIO_MMD_VEND1 */ -#define EN8811H_PHY_FW_STATUS 0x8009 -#define EN8811H_PHY_READY 0x02 =20 #define AIR_PHY_MCU_CMD_0 0x800b #define AIR_PHY_MCU_CMD_1 0x800c @@ -108,8 +100,6 @@ #define EN8811H_2P5G_LPA 0x3b30 #define EN8811H_2P5G_LPA_2P5G BIT(0) =20 -#define EN8811H_FW_VERSION 0x3b3c - #define EN8811H_POLARITY 0xca0f8 #define EN8811H_POLARITY_TX_NORMAL BIT(0) #define EN8811H_POLARITY_RX_REVERSE BIT(1) @@ -122,12 +112,6 @@ #define EN8811H_CLK_CGM 0xcf958 #define EN8811H_CLK_CGM_CKO BIT(26) =20 -#define EN8811H_FW_CTRL_1 0x0f0018 -#define EN8811H_FW_CTRL_1_START 0x0 -#define EN8811H_FW_CTRL_1_FINISH 0x1 -#define EN8811H_FW_CTRL_2 0x800000 -#define EN8811H_FW_CTRL_2_LOADING BIT(11) - #define AN8811HB_CRC_PM_SET1 0xf020c #define AN8811HB_CRC_PM_MON2 0xf0218 #define AN8811HB_CRC_PM_MON3 0xf021c @@ -270,86 +254,14 @@ static int __air_pbus_reg_write(struct mdio_device *m= diodev, upper_16_bits(pbus_data)); } =20 -static int __air_write_buf(struct phy_device *phydev, u32 address, - const struct firmware *fw) -{ - unsigned int offset; - int ret; - u16 val; - - ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, - upper_16_bits(address)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, - lower_16_bits(address)); - if (ret < 0) - return ret; - - for (offset =3D 0; offset < fw->size; offset +=3D 4) { - val =3D get_unaligned_le16(&fw->data[offset + 2]); - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val); - if (ret < 0) - return ret; - - val =3D get_unaligned_le16(&fw->data[offset]); - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val); - if (ret < 0) - return ret; - } - - return 0; -} - -static int air_write_buf(struct phy_device *phydev, u32 address, - const struct firmware *fw) -{ - int saved_page; - int ret =3D 0; - - if (fw->size % 4) { - phydev_err(phydev, "firmware size %zu is not a multiple of 4\n", - fw->size); - return -EINVAL; - } - - saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); - - if (saved_page >=3D 0) { - ret =3D __air_write_buf(phydev, address, fw); - if (ret < 0) - phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, - address, ret); - } - - return phy_restore_page(phydev, saved_page, ret); -} - static int en8811h_wait_mcu_ready(struct phy_device *phydev) { - int ret, reg_value; + int ret =3D air_en8811h_wait_mcu_ready(&phydev->mdio); =20 - ret =3D air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_FINISH); - if (ret) - return ret; + if (ret < 0) + phydev_err(phydev, "MCU not ready: %pe\n", ERR_PTR(ret)); =20 - /* Because of mdio-lock, may have to wait for multiple loads */ - ret =3D phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, - EN8811H_PHY_FW_STATUS, reg_value, - reg_value =3D=3D EN8811H_PHY_READY, - 20000, 7500000, true); - if (ret) { - phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value); - return -ENODEV; - } - - return 0; + return ret; } =20 static int an8811hb_check_crc(struct phy_device *phydev, u32 set1, @@ -411,7 +323,7 @@ static int an8811hb_load_file(struct phy_device *phydev= , const char *name, if (ret < 0) return ret; =20 - ret =3D air_write_buf(phydev, address, fw); + ret =3D air_fw_write_buf(&phydev->mdio, address, fw); release_firmware(fw); return ret; } @@ -507,54 +419,10 @@ static int an8811hb_load_firmware(struct phy_device *= phydev) =20 static int en8811h_load_firmware(struct phy_device *phydev) { - struct device *dev =3D &phydev->mdio.dev; - const struct firmware *fw1, *fw2; + struct en8811h_priv *priv =3D phydev->priv; int ret; =20 - ret =3D request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); - if (ret < 0) - return ret; - - ret =3D request_firmware_direct(&fw2, EN8811H_MD32_DSP, dev); - if (ret < 0) - goto en8811h_load_firmware_rel1; - - ret =3D air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); - if (ret < 0) - goto en8811h_load_firmware_out; - - ret =3D air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, - EN8811H_FW_CTRL_2_LOADING, - EN8811H_FW_CTRL_2_LOADING); - if (ret < 0) - goto en8811h_load_firmware_out; - - ret =3D air_write_buf(phydev, AIR_FW_ADDR_DM, fw1); - if (ret < 0) - goto en8811h_load_firmware_out; - - ret =3D air_write_buf(phydev, AIR_FW_ADDR_DSP, fw2); - if (ret < 0) - goto en8811h_load_firmware_out; - - ret =3D air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, - EN8811H_FW_CTRL_2_LOADING, 0); - if (ret < 0) - goto en8811h_load_firmware_out; - - ret =3D en8811h_wait_mcu_ready(phydev); - if (ret < 0) - goto en8811h_load_firmware_out; - - en8811h_print_fw_version(phydev); - -en8811h_load_firmware_out: - release_firmware(fw2); - -en8811h_load_firmware_rel1: - release_firmware(fw1); - + ret =3D air_en8811h_fw_download(&phydev->mdio, &priv->firmware_version); if (ret < 0) phydev_err(phydev, "Load firmware failed: %d\n", ret); =20 diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c index e314717843ae..5b7427e0d977 100644 --- a/drivers/net/phy/air_phy_lib.c +++ b/drivers/net/phy/air_phy_lib.c @@ -8,8 +8,12 @@ */ =20 #include +#include +#include +#include #include #include +#include #include =20 #include "air_phy_lib.h" @@ -198,6 +202,303 @@ int air_phy_buckpbus_reg_modify(struct phy_device *ph= ydev, u32 pbus_address, } EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_modify); =20 +static int __air_write_buf(struct mdio_device *mdiodev, u32 address, + const u8 *data, size_t len) +{ + unsigned int offset; + int ret; + u16 val; + + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_MODE, + AIR_BPBUS_MODE_ADDR_INCR); + if (ret < 0) + return ret; + + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(address)); + if (ret < 0) + return ret; + + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(address)); + if (ret < 0) + return ret; + + for (offset =3D 0; offset < len; offset +=3D 4) { + val =3D get_unaligned_le16(&data[offset + 2]); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_DATA_HIGH, val); + if (ret < 0) + return ret; + + val =3D get_unaligned_le16(&data[offset]); + ret =3D __mdiodev_write(mdiodev, AIR_BPBUS_WR_DATA_LOW, val); + if (ret < 0) + return ret; + } + + return 0; +} + +/* phy_select_page() needs a phy_device, which does not exist yet. */ +static int __air_mdio_select_page(struct mdio_device *mdiodev, int page) +{ + int saved_page, ret; + + saved_page =3D __mdiodev_read(mdiodev, AIR_EXT_PAGE_ACCESS); + if (saved_page < 0) + return saved_page; + + if (saved_page !=3D page) { + ret =3D __mdiodev_write(mdiodev, AIR_EXT_PAGE_ACCESS, page); + if (ret < 0) + return ret; + } + + return saved_page; +} + +static int __air_mdio_restore_page(struct mdio_device *mdiodev, + int saved_page, int page, int ret) +{ + int restore; + + if (saved_page !=3D page) { + restore =3D __mdiodev_write(mdiodev, AIR_EXT_PAGE_ACCESS, + saved_page); + if (ret >=3D 0 && restore < 0) + ret =3D restore; + } + + return ret; +} + +int air_fw_write_buf(struct mdio_device *mdiodev, u32 address, + const struct firmware *fw) +{ + size_t chunk, done =3D 0; + int saved_page, ret; + + if (fw->size % 4) { + dev_err(&mdiodev->dev, "firmware size %zu is not a multiple of 4\n", + fw->size); + return -EINVAL; + } + + while (done < fw->size) { + chunk =3D min_t(size_t, fw->size - done, AIR_FW_CHUNK_BYTES); + + mdiodev_lock(mdiodev); + + saved_page =3D __air_mdio_select_page(mdiodev, + AIR_PHY_PAGE_EXTENDED_4); + if (saved_page < 0) { + ret =3D saved_page; + } else { + ret =3D __air_write_buf(mdiodev, address + done, + fw->data + done, chunk); + ret =3D __air_mdio_restore_page(mdiodev, saved_page, + AIR_PHY_PAGE_EXTENDED_4, + ret); + } + + mdiodev_unlock(mdiodev); + if (ret < 0) + return ret; + + done +=3D chunk; + } + + return 0; +} +EXPORT_SYMBOL_GPL(air_fw_write_buf); + +static int air_mdio_buckpbus_reg_read(struct mdio_device *mdiodev, + u32 pbus_address, u32 *pbus_data) +{ + int saved_page, ret; + + mdiodev_lock(mdiodev); + + saved_page =3D __air_mdio_select_page(mdiodev, AIR_PHY_PAGE_EXTENDED_4); + if (saved_page < 0) { + ret =3D saved_page; + } else { + ret =3D __air_buckpbus_reg_read(mdiodev, pbus_address, pbus_data); + ret =3D __air_mdio_restore_page(mdiodev, saved_page, + AIR_PHY_PAGE_EXTENDED_4, ret); + } + + mdiodev_unlock(mdiodev); + return ret; +} + +static int air_mdio_buckpbus_reg_write(struct mdio_device *mdiodev, + u32 pbus_address, u32 pbus_data) +{ + int saved_page, ret; + + mdiodev_lock(mdiodev); + + saved_page =3D __air_mdio_select_page(mdiodev, AIR_PHY_PAGE_EXTENDED_4); + if (saved_page < 0) { + ret =3D saved_page; + } else { + ret =3D __air_buckpbus_reg_write(mdiodev, pbus_address, + pbus_data); + ret =3D __air_mdio_restore_page(mdiodev, saved_page, + AIR_PHY_PAGE_EXTENDED_4, ret); + } + + mdiodev_unlock(mdiodev); + return ret; +} + +static int air_mdio_buckpbus_reg_modify(struct mdio_device *mdiodev, + u32 pbus_address, u32 mask, u32 set) +{ + int saved_page, ret; + + mdiodev_lock(mdiodev); + + saved_page =3D __air_mdio_select_page(mdiodev, AIR_PHY_PAGE_EXTENDED_4); + if (saved_page < 0) { + ret =3D saved_page; + } else { + ret =3D __air_buckpbus_reg_modify(mdiodev, pbus_address, + mask, set); + ret =3D __air_mdio_restore_page(mdiodev, saved_page, + AIR_PHY_PAGE_EXTENDED_4, ret); + } + + mdiodev_unlock(mdiodev); + return ret; +} + +/* mmd_phy_read() drops the errors from the three writes that select the + * register, so a failed selection reads a different one back as success. + */ +static int __air_mmd_read(struct mdio_device *mdiodev, u16 devad, u16 regn= um) +{ + struct mii_bus *bus =3D mdiodev->bus; + int addr =3D mdiodev->addr; + int ret; + + ret =3D __mdiobus_write(bus, addr, MII_MMD_CTRL, devad); + if (ret < 0) + return ret; + + ret =3D __mdiobus_write(bus, addr, MII_MMD_DATA, regnum); + if (ret < 0) + return ret; + + ret =3D __mdiobus_write(bus, addr, MII_MMD_CTRL, + devad | MII_MMD_CTRL_NOINCR); + if (ret < 0) + return ret; + + return __mdiobus_read(bus, addr, MII_MMD_DATA); +} + +static int air_mmd_status_read(struct mdio_device *mdiodev) +{ + int ret; + + mdiodev_lock(mdiodev); + ret =3D __air_mmd_read(mdiodev, MDIO_MMD_VEND1, EN8811H_PHY_FW_STATUS); + mdiodev_unlock(mdiodev); + + return ret; +} + +int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev) +{ + int ret, reg_value; + + ret =3D air_mdio_buckpbus_reg_write(mdiodev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); + if (ret) + return ret; + + /* Because of mdio-lock, may have to wait for multiple loads. A read + * error ends the poll at once, like phy_read_mmd_poll_timeout(). + */ + ret =3D read_poll_timeout(air_mmd_status_read, reg_value, + reg_value < 0 || + reg_value =3D=3D EN8811H_PHY_READY, + 20000, 7500000, true, mdiodev); + if (reg_value < 0) + return reg_value; + if (ret) { + dev_dbg(&mdiodev->dev, "MCU not ready: 0x%x\n", reg_value); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_GPL(air_en8811h_wait_mcu_ready); + +int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version) +{ + const struct firmware *fw1, *fw2; + struct device *dev =3D &mdiodev->dev; + int ret; + + ret =3D request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); + if (ret < 0) + return ret; + + ret =3D request_firmware_direct(&fw2, EN8811H_MD32_DSP, dev); + if (ret < 0) + goto air_fw_download_rel1; + + ret =3D air_mdio_buckpbus_reg_write(mdiodev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); + if (ret < 0) + goto air_fw_download_out; + + ret =3D air_mdio_buckpbus_reg_modify(mdiodev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, + EN8811H_FW_CTRL_2_LOADING); + if (ret < 0) + goto air_fw_download_out; + + ret =3D air_fw_write_buf(mdiodev, AIR_FW_ADDR_DM, fw1); + if (ret < 0) + goto air_fw_download_out; + + ret =3D air_fw_write_buf(mdiodev, AIR_FW_ADDR_DSP, fw2); + if (ret < 0) + goto air_fw_download_out; + + ret =3D air_mdio_buckpbus_reg_modify(mdiodev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, 0); + if (ret < 0) + goto air_fw_download_out; + + ret =3D air_en8811h_wait_mcu_ready(mdiodev); + if (ret < 0) + goto air_fw_download_out; + + ret =3D air_mdio_buckpbus_reg_read(mdiodev, EN8811H_FW_VERSION, + fw_version); + if (ret < 0) + goto air_fw_download_out; + + dev_info(dev, "MD32 firmware version: %08x\n", *fw_version); + +air_fw_download_out: + release_firmware(fw2); + +air_fw_download_rel1: + release_firmware(fw1); + + /* No error print: callers log on their own terms, and a poller + * would repeat it on every retry. + */ + return ret; +} +EXPORT_SYMBOL_GPL(air_en8811h_fw_download); + int air_phy_read_page(struct phy_device *phydev) { return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h index 01bb32e7c7c9..70c8ba66bd73 100644 --- a/drivers/net/phy/air_phy_lib.h +++ b/drivers/net/phy/air_phy_lib.h @@ -16,6 +16,11 @@ #define AIR_PHY_PAGE_EXTENDED_1 0x0001 #define AIR_PHY_PAGE_EXTENDED_4 0x0004 =20 +/* Bytes written between bus releases, so the other PHYs on it get a turn. + * At a 2.5 MHz MDC this size holds the bus for about 30 ms. + */ +#define AIR_FW_CHUNK_BYTES 2304 + /* MII Registers Page 4*/ #define AIR_BPBUS_MODE 0x10 #define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 @@ -29,6 +34,23 @@ #define AIR_BPBUS_RD_DATA_HIGH 0x17 #define AIR_BPBUS_RD_DATA_LOW 0x18 =20 +#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" +#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" + +#define AIR_FW_ADDR_DM 0x00000000 +#define AIR_FW_ADDR_DSP 0x00100000 + +#define EN8811H_FW_CTRL_1 0x0f0018 +#define EN8811H_FW_CTRL_1_START 0x0 +#define EN8811H_FW_CTRL_1_FINISH 0x1 +#define EN8811H_FW_CTRL_2 0x800000 +#define EN8811H_FW_CTRL_2_LOADING BIT(11) + +#define EN8811H_PHY_FW_STATUS 0x8009 +#define EN8811H_PHY_READY 0x02 + +#define EN8811H_FW_VERSION 0x3b3c + int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_addres= s, u32 mask, u32 set); int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address, @@ -38,4 +60,11 @@ int air_phy_buckpbus_reg_write(struct phy_device *phydev= , u32 pbus_address, int air_phy_read_page(struct phy_device *phydev); int air_phy_write_page(struct phy_device *phydev, int page); =20 +struct firmware; + +int air_fw_write_buf(struct mdio_device *mdiodev, u32 address, + const struct firmware *fw); +int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev); +int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version); + #endif /* __AIR_PHY_LIB_H */ --=20 2.53.0 From nobody Fri Sep 25 20:47:54 2026 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 14F44583AA5 for ; Tue, 8 Sep 2026 15:57:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883044; cv=none; b=HoCNxBALN38VYkYP8YGpAWsKnpDK79xBWWhJr0d/uQVdfoUs2he90JT+a8GpTS9SEIAGLgRiohKRQdXSdwyN+Plpi17Tkgq/roskxpfRY4hcJLiCXLAd8niFyZoI53TMhxcAzsJr757mkgIcHBqv4WSAjFAXX163Yusd5aXjRJQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883044; c=relaxed/simple; bh=R+trTOYlMixGurkSaokPqFVhZzEOyMehQzqZ+3h2vsk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HU3rUfUdPN2O1bDgHsfHvmbbYgIoiKZ2eiek1RhPubqylvke+rZTM12MwdEC6ov7pkZVBI6SCnEzyKjGISlD1FXiG1n1iYHTjaJX5nhpTJaq/YiFnc5abjpgYgQaFgqnHwR1TWPRCp1BXujj/1PdGzMLYDk8F224FUaIj/LnFT0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=c0LVPc0d; arc=none smtp.client-ip=209.85.128.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="c0LVPc0d" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-49cdc81f40eso35069345e9.2 for ; Tue, 08 Sep 2026 08:57:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788883038; x=1789487838; darn=vger.kernel.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:content-type; bh=R61YL4bM9jY5pVzf4nxgHgxN6hURjg68AYKuV/uyHLo=; b=c0LVPc0dfNv6tKSwPGwQ7qtKI4ML2uEf1r5Z+cQawlNp85IHjIH8nWEYz1QTeJrnr+ UnSJW+fUGu3Cj4xinumaJVEm28zDIKoVY6JkCjOJQQlxDldLeWDKGZaEGrEwlc45jCA4 7WIunzhZUV+ejqUDH2ST/LRG+yQj8PJuwaF+eCYS37T44mrEK0o3UUZlbtESlVptc1Ex st5cqb3gWK1kAVK39/Jx0LpJI4OqA4hgV+pWrEFbKDkDIcFlVCwMW+fLjR9sf9c+w52U IcLRj/9HKmKERvxVX81bW0RX6eSAeeD2K4e0FJQGlmeKlWYl/UFehn+afEgEf+k5L4m4 JnKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788883038; x=1789487838; 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:content-type; bh=R61YL4bM9jY5pVzf4nxgHgxN6hURjg68AYKuV/uyHLo=; b=SLNPRebllyryEMIt4HHYKsWLjGxTzzX0PtuWbfnliPc4hmGjDD7Ng30B5F1vfPTsUf EGGNoQALTlW+V+0D92ia6HARjNZCzSh/09Yd50NNrpUk56d2CtUmIZewlpRQe46JafTM mNZqR3caNhwyQOhePlW+AV/H520EqHqT/siRwVLBfEGXwlWdPbiVQfA0kqnwOeynZKR0 ish3RyNycFgTOmofQT9mS4K9ipsuIZ+BpG3LgH+sYjzOE4OSaoNEtaKvfY7hq/rJHPey vKUNPsO/xX62It2rzTm15h8C+AtYA/3jNAHzE94a9e5CJOj277IAE3pumvanITYI4pGr b65A== X-Forwarded-Encrypted: i=1; AKwUvBwD3eoADwn32TQf6qWeiccuJ+LpzWcl5POeJidj2hbCleIIqbOM/2g9zIJ+MbUvmmJuPlJYir61lO5ALp0=@vger.kernel.org X-Gm-Message-State: AFuF++mse+fBb6Ub69dbGT/FaTPzfYp+ihCcuGKjJzoH3Fg/lv51XsYv 4V386sKoY8/15Aq6DsGMfm4fPHVL2uXViooIrSpBPvneeLvXvMIpZxex1sTZ28ZwTz0= X-Gm-Gg: AYBFou15yzVtXq+PdqdaxFwV4UOtdW/Ug05HIrLgz8jGc2I+g0Sgzw4lsGsiE8LNDFY FQx5VPOBGV4LjLeLekVRMwIRTbn0V7Qpd0pryAvvMrIpEpClZNNSOjMHRKKpZc3RbDZ2jsU+f+x IZBCvt5BqM0TcGsmgvOutVMV7izGS8fmPcgTeMZ797AIqdDUFH2/uvCDXJsow3rQjdtX1DUyX+Y bPTQLWg6j4ay3dBI+PhoxC4OdNuFQAXz/fOcJOj8+R2kUo+cLtZ6/OwisQWJX8PTpo6N8I84b/W nbi39sM42NuGCMSuXskP0M1z9SUqiHAl3J7OR01IgPxKK9yZ9LGbY6F0KD2LU07wgxhVBJcaKKx YuauFXs6qr+59at929hRv9jIvOUoY0EI7nSwUJv7QVEz9UzWwLlkzhYKyXqKItkKVkI1Klz0tNt g2GISDJ2ZDxOenGeZFbt/pFOO+dnfngymoc5BnbNwVYdwBoEpM8g== X-Received: by 2002:a05:600c:8715:b0:49d:17d4:aff2 with SMTP id 5b1f17b1804b1-49d17d4b01dmr147455505e9.1.1788883037684; Tue, 08 Sep 2026 08:57:17 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-485883be709sm37345704f8f.21.2026.09.08.08.57.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 08:57:17 -0700 (PDT) From: Aleksei Sviridkin To: andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: ericwouds@gmail.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Aleksei Sviridkin Subject: [RFC PATCH net-next v2 5/7] net: phy: air: skip the download when the MD32 is already running Date: Tue, 8 Sep 2026 15:57:05 +0000 Message-ID: <20260908155707.4164559-6-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260908155707.4164559-1-f@lex.la> References: <20260908155707.4164559-1-f@lex.la> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The download is unconditional, so a chip whose firmware was loaded by something else - a bootloader, an earlier bind of the PHY driver, or an MDIO device serving the chip - is reprogrammed at 144KB per probe. Read the status register the loader already polls for readiness and skip the download when it reports ready, only picking up the running firmware's version. Adopting is reported back distinctly from loading, because nothing on that path touches FW_CTRL_1: the PHY driver must leave the MCU restart to .config_init() rather than assume probe already did it. The image in RAM now wins over the files on disk. Where the PHY node carries no reset-gpios nothing clears that RAM on unbind, so writing new firmware files and rebinding keeps the old image running and reports its version as the current one. A power cycle, or a reset line on the PHY node for phy_detach() to assert, brings the reload back. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- drivers/net/phy/air_en8811h.c | 4 ++-- drivers/net/phy/air_phy_lib.c | 26 ++++++++++++++++++++++++++ drivers/net/phy/air_phy_lib.h | 3 +++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index 6822774d9ae3..d1c7c472f0e9 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -1024,8 +1024,8 @@ static int en8811h_probe(struct phy_device *phydev) if (ret < 0) return ret; =20 - /* mcu has just restarted after firmware load */ - priv->mcu_needs_restart =3D false; + /* Adopted firmware was never restarted here. */ + priv->mcu_needs_restart =3D (ret =3D=3D 1); =20 /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ phydev->c45_ids.mmds_present |=3D MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c index 5b7427e0d977..a5b6b3606bec 100644 --- a/drivers/net/phy/air_phy_lib.c +++ b/drivers/net/phy/air_phy_lib.c @@ -410,6 +410,17 @@ static int air_mmd_status_read(struct mdio_device *mdi= odev) return ret; } =20 +int air_en8811h_mcu_running(struct mdio_device *mdiodev) +{ + int ret =3D air_mmd_status_read(mdiodev); + + if (ret < 0) + return ret; + + return ret =3D=3D EN8811H_PHY_READY; +} +EXPORT_SYMBOL_GPL(air_en8811h_mcu_running); + int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev) { int ret, reg_value; @@ -443,6 +454,21 @@ int air_en8811h_fw_download(struct mdio_device *mdiode= v, u32 *fw_version) struct device *dev =3D &mdiodev->dev; int ret; =20 + ret =3D air_en8811h_mcu_running(mdiodev); + if (ret < 0) + return ret; + + if (ret) { + ret =3D air_mdio_buckpbus_reg_read(mdiodev, EN8811H_FW_VERSION, + fw_version); + if (ret < 0) + return ret; + + dev_info(dev, "MD32 already running, firmware %08x\n", + *fw_version); + return 1; + } + ret =3D request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); if (ret < 0) return ret; diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h index 70c8ba66bd73..226dc9f2eb07 100644 --- a/drivers/net/phy/air_phy_lib.h +++ b/drivers/net/phy/air_phy_lib.h @@ -64,7 +64,10 @@ struct firmware; =20 int air_fw_write_buf(struct mdio_device *mdiodev, u32 address, const struct firmware *fw); +/* Returns 1 running, 0 dormant, negative on a failed status read. */ +int air_en8811h_mcu_running(struct mdio_device *mdiodev); int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev); +/* Returns 1 when it adopted firmware that was already running. */ int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version); =20 #endif /* __AIR_PHY_LIB_H */ --=20 2.53.0 From nobody Fri Sep 25 20:47:54 2026 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3153C583AB5 for ; Tue, 8 Sep 2026 15:57:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883047; cv=none; b=DVnXBo4rCZubXa8rCzmXNLPitziUHA/Y4AxRrpe5mHT6EqThvvz9/OF1iesJA8a4ZLn8ur/aewcg6t1LGROdY0Nig4shp3TxilNLzPupbMrdijhsu8HmVMBwhwFn+vvgPGEZvW8SYOf150X3Na5IcBJ+J/NsJ5r/a2v72drGLT8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883047; c=relaxed/simple; bh=Ao/8J7UTQtuDshYKUS2qutRCBUl/5UwBjXZJzZrvRTw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZjG/x3HD9+fAMR5ryc1OEaXdaXgXA2JtKsmxVQQdDGTi1VLftgJq2dOkdPX8qGwwWYuh4eaY0Rf4G8008E4tpKvi0CvuX8yTq3hMfvaV8tn6lhzvyfOdHm60S5hUsHRmONclWaEPh9PsjY1yt3ZcxOXywmAJ7Tq7wdSgIWHvPWg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=O5jGAtem; arc=none smtp.client-ip=209.85.221.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="O5jGAtem" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-484392e3d33so3431861f8f.2 for ; Tue, 08 Sep 2026 08:57:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788883039; x=1789487839; darn=vger.kernel.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:content-type; bh=IkXgniOUiPREmfv+HSLCD0A+Drz8mQTli5pp/XRi4lU=; b=O5jGAtem1X0xWtsWWYm3IPNpReci4RiqBP3igoHMhUqKGtVokYbla+tLhFw3mWk+RM fVf179wiSR01HmM6nPqSPpfim9rrPX/pjDCi5YCKcq1ulknXdXcKHPQ4QUfCG9yj0nl6 Z4q8kuWHZ2147r/NymgZzLe7qYJ4Eyc6RdCny6N5VbGv3eve+QjszYrvtFAdsWxMI9v6 cmM1+Zn2ZxGXQDAjdQMH2fx4C62WFTlcjgSYnyWFpxTx3V0llP/tutZT+wcsYeYHMC/Y dT40XxWlnBaR+JGe0T5HGpMlkma7yxOZx48K+TUAeXUeE8WLfupHs9sEm7T4MSQIE8+S 2QXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788883039; x=1789487839; 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:content-type; bh=IkXgniOUiPREmfv+HSLCD0A+Drz8mQTli5pp/XRi4lU=; b=s6m0UR9BDRGBlTfwwAw4DMyWWap9VpmOrdSHBNd0DdwPMxcUvKFjZKBGv9WZMHhvHV C0mj+jXlpmQcOKGzj0j3mEE8x9PMVQTmnq4Ls8EEDwTplN1ZmDV0mxNkIn+3TnfUvFTu DpdxE3Iaq/hKgc86g7kv3z3IfcWuBPpQ0NvgdCCiTcKr5A+2MspmRxorOc0Hov8G6e9W Hx6JXK0sOnkQWXZeW6CWQHs7A3levMl+MHZtR082fcUGMlcWLR8/iXQJB4VmyqTEknTK 1JvSDrUaUdhjQQTbE4QVTzHZ/25UWf/wRzRdFUJn9NilXWtyFI6JOTRP834IZdmYEYyw e7aQ== X-Forwarded-Encrypted: i=1; AKwUvByshC7YWiIcZsC0G2MSoi2vEOc7+38x12C1WPNX794X8Z0TQdMlWv5Vn4u9URS0cQFHlDQU+6HJZNWOdTA=@vger.kernel.org X-Gm-Message-State: AFuF++kwmJ82HUCWZa49D2lXH4+WrKClA+2RGwSXyYNgWxR2XdnqkQWr au8e3Chp+nNc36Cw1IRN1uV3pX49YG2KoG7NK6iJf89HEsnYmnXa28CbqzVzTPys2O8= X-Gm-Gg: AYBFou16clGvGpP6eOfgsEFkSjxyG9Ysq+uQt9+d+CN0w6oxcBbHOoP/oBbaYR44tPc p8cDkF4yv4xYZVD48gglwOGDm8oSDeiUWC5oeLXrmTzEknjcEcJ2nApn+zCZ6WikJ/HWr3aXN8d pGYznQlczz72DntXj15EJ1942hqcMutiWT31wVaKJDwDZVu6qMIiZ2Khh7XGFE8/6P28OP46Mwb e/6vod5ywthLRCnLhL5xdGCOGVCvjFptts7biHaRQCrOi9S1GvYAp4miGXga+4Y/YJG9UgUULve iwCIWJDDL4R/50FRMgD7ukrjq/e8t/SlHlr2/mYPYV/pO2Xl1vuZh2w+Hi2QlY8fU3QoFtTNTc0 mU+fYOJQvY6ssdmpxb8lE2UOlZvvK2F4LPzLLUjZ73OVdBRDpF9KtDA5/EFurOR3Ux+e2/V7k/5 JeyOgcDtoz1akrwPDLTRZqmuO0FXnlXE4aQZ3ttBu7XkyPrW+++c8= X-Received: by 2002:a05:6000:490e:b0:485:82f9:44a4 with SMTP id ffacd0b85a97d-485872a010cmr36125987f8f.16.1788883039308; Tue, 08 Sep 2026 08:57:19 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-485883be709sm37345704f8f.21.2026.09.08.08.57.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 08:57:18 -0700 (PDT) From: Aleksei Sviridkin To: andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: ericwouds@gmail.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Aleksei Sviridkin Subject: [RFC PATCH net-next v2 6/7] net: mdio: add Airoha EN8811H MDIO device driver Date: Tue, 8 Sep 2026 15:57:06 +0000 Message-ID: <20260908155707.4164559-7-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260908155707.4164559-1-f@lex.la> References: <20260908155707.4164559-1-f@lex.la> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Until its firmware has been downloaded the EN8811H is not an Ethernet PHY, it is an MD32 microcontroller waiting in its bootloader. On systems that keep the firmware files in a filesystem, the files become readable long after the MDIO bus was scanned, and the PHY driver's probe-time download then cannot work at boot. Describe the chip as an MDIO device that polls for the files and downloads through the shared library helper once they can be read; a chip left running by the bootloader is adopted as-is. Polling rather than deferred probing because request_firmware_direct() has no usermode-helper fallback: an unmounted rootfs fails at once, and a deferral only parks the device until something else triggers the pending list, which need not happen when the files appear. There is no give-up path, since installing the firmware package on a running system is a normal thing to do. The reset line is claimed here rather than on the PHY node, and it is cycled only while the MD32 does not report a running firmware: that firmware lives in volatile RAM, so an assert on a running chip - such as the one phy_detach() performs on a PHY-node reset - would wipe it. A status read that fails says nothing about the firmware, so the line is left alone unless it is already asserted, which is reason enough for the chip not to have answered. Resume repeats the reset decision and the download in line, since a suspend that cut power leaves the MD32 back in its bootloader. Nothing yet publishes a PHY: this driver takes the chip over, and the bus that exposes it to the device tree is the next patch. Splitting there keeps the download and its retry policy separate from what registering a bus costs, which is its own failure mode with its own backoff. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- MAINTAINERS | 2 + drivers/net/mdio/Kconfig | 13 ++ drivers/net/mdio/Makefile | 1 + drivers/net/mdio/mdio-airoha-en8811h.c | 199 +++++++++++++++++++++++ drivers/net/phy/air_phy_lib.h | 9 +- include/linux/mdio/mdio-airoha-en8811h.h | 24 +++ 6 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 drivers/net/mdio/mdio-airoha-en8811h.c create mode 100644 include/linux/mdio/mdio-airoha-en8811h.h diff --git a/MAINTAINERS b/MAINTAINERS index 4ca19275f4d0..3bd86df2be57 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -755,6 +755,8 @@ M: Aleksei Sviridkin L: netdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/airoha,en8811h-mcu.yaml +F: drivers/net/mdio/mdio-airoha-en8811h.c +F: include/linux/mdio/mdio-airoha-en8811h.h =20 AIROHA ETHERNET DRIVER M: Lorenzo Bianconi diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index d44278f26fab..33efc6bc875f 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -29,6 +29,19 @@ config MDIO_AIROHA This module provides a driver for the MDIO busses found in the Airoha AN7583 SoC's. =20 +config MDIO_AIROHA_EN8811H + tristate "Airoha EN8811H MDIO device support" + depends on OF_MDIO + depends on FW_LOADER + select AIR_NET_PHYLIB + imply AIR_EN8811H_PHY + help + This module provides a driver for the Airoha EN8811H, which is an + MD32 microcontroller until firmware is downloaded into it and only + becomes an Ethernet PHY afterwards. The driver downloads that + firmware once it becomes readable, or adopts firmware a bootloader + left running. + config MDIO_SUN4I tristate "Allwinner sun4i MDIO interface support" depends on ARCH_SUNXI || COMPILE_TEST diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile index 048586746026..06d096675dac 100644 --- a/drivers/net/mdio/Makefile +++ b/drivers/net/mdio/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_FWNODE_MDIO) +=3D fwnode_mdio.o obj-$(CONFIG_OF_MDIO) +=3D of_mdio.o =20 obj-$(CONFIG_MDIO_AIROHA) +=3D mdio-airoha.o +obj-$(CONFIG_MDIO_AIROHA_EN8811H) +=3D mdio-airoha-en8811h.o obj-$(CONFIG_MDIO_ASPEED) +=3D mdio-aspeed.o obj-$(CONFIG_MDIO_BCM_IPROC) +=3D mdio-bcm-iproc.o obj-$(CONFIG_MDIO_BCM_UNIMAC) +=3D mdio-bcm-unimac.o diff --git a/drivers/net/mdio/mdio-airoha-en8811h.c b/drivers/net/mdio/mdio= -airoha-en8811h.c new file mode 100644 index 000000000000..d94d74d85f10 --- /dev/null +++ b/drivers/net/mdio/mdio-airoha-en8811h.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Airoha EN8811H MDIO device driver + * + * The EN8811H is an MD32 microcontroller until firmware is downloaded into + * it, and only then an Ethernet PHY. + * + * Copyright (C) 2026 Aleksei Sviridkin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define EN8811H_FW_POLL_MIN_MS 1000 +#define EN8811H_FW_POLL_MAX_MS 30000 +#define EN8811H_FW_WARN_MS 60000 + +struct en8811h_mcu { + struct mdio_device *mdiodev; + struct gpio_desc *reset_gpio; + struct delayed_work fw_poll; + unsigned int poll_ms; + unsigned int waited_ms; + u32 fw_version; + bool warned; +}; + +static void en8811h_mcu_fw_poll(struct work_struct *work) +{ + struct en8811h_mcu *mcu =3D container_of(to_delayed_work(work), + struct en8811h_mcu, fw_poll); + struct device *dev =3D &mcu->mdiodev->dev; + int ret; + + ret =3D air_en8811h_fw_download(mcu->mdiodev, &mcu->fw_version); + if (ret >=3D 0) { + dev_dbg(dev, "firmware %08x running after %ums\n", + mcu->fw_version, mcu->waited_ms); + ret =3D firmware_request_cache(dev, EN8811H_MD32_DM) ?: + firmware_request_cache(dev, EN8811H_MD32_DSP); + if (ret) + dev_dbg(dev, "not cached, resume will want the files: %pe\n", + ERR_PTR(ret)); + return; + } + + mcu->waited_ms +=3D mcu->poll_ms; + if (!mcu->warned && mcu->waited_ms >=3D EN8811H_FW_WARN_MS) { + if (ret =3D=3D -ENOENT) + dev_warn(dev, "still waiting for %s and %s\n", + EN8811H_MD32_DM, EN8811H_MD32_DSP); + else + dev_warn(dev, "firmware download keeps failing: %pe\n", + ERR_PTR(ret)); + mcu->warned =3D true; + } + + mcu->poll_ms =3D min(mcu->poll_ms * 2, EN8811H_FW_POLL_MAX_MS); + queue_delayed_work(system_freezable_wq, &mcu->fw_poll, + msecs_to_jiffies(mcu->poll_ms)); +} + +/* The firmware lives in volatile RAM: no reset while the MD32 reports rea= dy. */ +static void en8811h_mcu_reset_if_dormant(struct en8811h_mcu *mcu) +{ + struct mdio_device *mdiodev =3D mcu->mdiodev; + struct device *dev =3D &mdiodev->dev; + u32 assert_us =3D 0, deassert_us =3D 0; + int ret; + + ret =3D air_en8811h_mcu_running(mdiodev); + if (ret > 0) { + dev_dbg(dev, "MD32 already running, leaving reset alone\n"); + return; + } + + if (!mcu->reset_gpio) + return; + + /* A failed read is not a dormant chip, so do not touch a line that + * is already deasserted. An asserted one is why the read failed. + */ + if (ret < 0 && gpiod_get_value_cansleep(mcu->reset_gpio) <=3D 0) { + dev_dbg(dev, "MD32 state unknown (%d), leaving reset alone\n", + ret); + return; + } + + device_property_read_u32(dev, "reset-assert-us", &assert_us); + device_property_read_u32(dev, "reset-deassert-us", &deassert_us); + + ret =3D gpiod_direction_output(mcu->reset_gpio, 1); + if (ret) { + dev_warn(dev, "reset not asserted: %pe\n", ERR_PTR(ret)); + return; + } + + if (assert_us) + fsleep(assert_us); + + gpiod_set_value_cansleep(mcu->reset_gpio, 0); + if (deassert_us) + fsleep(deassert_us); +} + +static int en8811h_mcu_probe(struct mdio_device *mdiodev) +{ + struct device *dev =3D &mdiodev->dev; + struct en8811h_mcu *mcu; + + mcu =3D devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL); + if (!mcu) + return -ENOMEM; + + mcu->mdiodev =3D mdiodev; + mdiodev_set_drvdata(mdiodev, mcu); + + /* The core claims reset-gpios only for devices flagged as PHYs. */ + mcu->reset_gpio =3D devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + if (IS_ERR(mcu->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(mcu->reset_gpio), + "failed to get reset GPIO\n"); + + if (mcu->reset_gpio) + gpiod_set_consumer_name(mcu->reset_gpio, "EN8811H reset"); + + en8811h_mcu_reset_if_dormant(mcu); + + mcu->poll_ms =3D EN8811H_FW_POLL_MIN_MS; + INIT_DELAYED_WORK(&mcu->fw_poll, en8811h_mcu_fw_poll); + /* Freezable: neither the file lookup nor the download may land on + * a suspending bus. + */ + queue_delayed_work(system_freezable_wq, &mcu->fw_poll, 0); + + return 0; +} + +static void en8811h_mcu_remove(struct mdio_device *mdiodev) +{ + struct en8811h_mcu *mcu =3D mdiodev_get_drvdata(mdiodev); + + cancel_delayed_work_sync(&mcu->fw_poll); +} + +static int en8811h_mcu_resume(struct device *dev) +{ + struct en8811h_mcu *mcu =3D dev_get_drvdata(dev); + + int ret; + + /* Not on the workqueue: the child PHY's own resume calls + * phy_init_hw() straight after this one and needs the firmware by + * then. request_firmware() is answered from the cache the download + * registered, so it does not wait for a filesystem. + */ + en8811h_mcu_reset_if_dormant(mcu); + ret =3D air_en8811h_fw_download(mcu->mdiodev, &mcu->fw_version); + if (ret < 0) + dev_err(dev, "firmware not restored: %pe\n", ERR_PTR(ret)); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(en8811h_mcu_pm_ops, NULL, en8811h_mcu_resu= me); + +static const struct of_device_id en8811h_mcu_of_match[] =3D { + { .compatible =3D "airoha,en8811h-mcu" }, + { } +}; +MODULE_DEVICE_TABLE(of, en8811h_mcu_of_match); + +static struct mdio_driver en8811h_mcu_driver =3D { + .probe =3D en8811h_mcu_probe, + .remove =3D en8811h_mcu_remove, + .mdiodrv.driver =3D { + .name =3D "airoha-en8811h-mcu", + .of_match_table =3D en8811h_mcu_of_match, + .pm =3D pm_sleep_ptr(&en8811h_mcu_pm_ops), + }, +}; + +mdio_module_driver(en8811h_mcu_driver); + +MODULE_FIRMWARE(EN8811H_MD32_DM); +MODULE_FIRMWARE(EN8811H_MD32_DSP); + +MODULE_DESCRIPTION("Airoha EN8811H MDIO device driver"); +MODULE_AUTHOR("Aleksei Sviridkin "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h index 226dc9f2eb07..3391396aecd1 100644 --- a/drivers/net/phy/air_phy_lib.h +++ b/drivers/net/phy/air_phy_lib.h @@ -10,6 +10,8 @@ =20 #include =20 +#include + #define AIR_EXT_PAGE_ACCESS 0x1f =20 #define AIR_PHY_PAGE_STANDARD 0x0000 @@ -34,9 +36,6 @@ #define AIR_BPBUS_RD_DATA_HIGH 0x17 #define AIR_BPBUS_RD_DATA_LOW 0x18 =20 -#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" -#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" - #define AIR_FW_ADDR_DM 0x00000000 #define AIR_FW_ADDR_DSP 0x00100000 =20 @@ -64,10 +63,6 @@ struct firmware; =20 int air_fw_write_buf(struct mdio_device *mdiodev, u32 address, const struct firmware *fw); -/* Returns 1 running, 0 dormant, negative on a failed status read. */ -int air_en8811h_mcu_running(struct mdio_device *mdiodev); int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev); -/* Returns 1 when it adopted firmware that was already running. */ -int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version); =20 #endif /* __AIR_PHY_LIB_H */ diff --git a/include/linux/mdio/mdio-airoha-en8811h.h b/include/linux/mdio/= mdio-airoha-en8811h.h new file mode 100644 index 000000000000..0d23811e90dc --- /dev/null +++ b/include/linux/mdio/mdio-airoha-en8811h.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2026 Airoha Technology Corp. + * Copyright (C) 2026 Collabora Ltd. + * Louis-Alexis Eyraud + * Copyright (C) 2026 Aleksei Sviridkin + */ + +#ifndef __LINUX_MDIO_AIROHA_EN8811H_H +#define __LINUX_MDIO_AIROHA_EN8811H_H + +#include + +struct mdio_device; + +#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" +#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" + +/* Returns 1 running, 0 dormant, negative on a failed status read. */ +int air_en8811h_mcu_running(struct mdio_device *mdiodev); +/* Returns 1 when it adopted firmware that was already running. */ +int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version); + +#endif /* __LINUX_MDIO_AIROHA_EN8811H_H */ --=20 2.53.0 From nobody Fri Sep 25 20:47:54 2026 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8091058497F for ; Tue, 8 Sep 2026 15:57:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883052; cv=none; b=oAHvqOcVtpaeswn19V5kAAJbZ8VcB/9NnJ+Mi6FqQWaLKOVA8djIcbYZzFTyHBaCNb01mkBMYFgqXO3xPNHFEiHjWP7mzY0j4on6UhqxGXUuz47fhQuMFDN52qCJRl2sM0Iw1ue73u+eaDNQH+bTYK2Qkdw15Oa2zbX/iNYs8v8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788883052; c=relaxed/simple; bh=1OQN2hlqiJhxyXGllQ5L8EqTRRM5exxJTPl6s+WACzQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tEBV0RaF+aQw48VM5Kz1T4RP/0BtUMGvdHAyd633iHRpMOP5Zs014zq9dTBqVCfpsVNIKUSts2IJdaOVn2GCVjynTLxserQU+yT/MOJtkunAqIzYtO5LZRnd4AfpxcQ9B5ldw1olk/NsSlo2lTFF+8qjzO9dnvCaNfEzaISYMao= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la; spf=pass smtp.mailfrom=lex.la; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b=OjUcsGH1; arc=none smtp.client-ip=209.85.221.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lex.la Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lex.la Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lex.la header.i=@lex.la header.b="OjUcsGH1" Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-482f2ee53e7so3621248f8f.1 for ; Tue, 08 Sep 2026 08:57:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lex.la; s=google; t=1788883041; x=1789487841; darn=vger.kernel.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:content-type; bh=sY8q/8ZX78MYp+GbZ3CoGJbwlETSUAbbscvAdY9eIl8=; b=OjUcsGH1SIYtbRcgrBQ0HvKrjF8acTrOtTvNyKTdU9e3jwdBfe1Rl1Y+M4Lz7/UZDy em8rwWzIvIPVJBU57n9s8rK2Ugdq8A1+L55R5uHkUOfyZSFIbAv6cM2yCDpneBw02A+t BG66oYeH13WS7GIuIec/gJBGgu6FJpUd0g4MaR/U5alB+TTCKFMWFAi/+/eUThNyrLXl rGk7mIMxSm/Wu4HgPqEzBp1DNETrcsHavPAWtwOAxGfG44Pb7Aec/tksgwmIc0XNU2lu lrdszomW18n82Tikzybm1k5il/corz3OZYntIIPkq3aB1CDMZ6PkGUPo6a0xUqRLEy70 JAKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788883041; x=1789487841; 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:content-type; bh=sY8q/8ZX78MYp+GbZ3CoGJbwlETSUAbbscvAdY9eIl8=; b=kW7qQL/ckuUhNHgZYOZ8T/s0hQ5AkwlCb4rlyNEWO7kO7IFulYme3Md7sQnqQILpAD bopyHdyTITb0u0LnwubsgI2tpkEBCYY3rZsU8FiEPoICAtVOxtl/108oAXU3XCOi9hTc /TXstLgb/YGQ/ld4nf3SXcp94jxujMGSqLABo0lMHxI1VzXRibUrCCww9YDLJHBU6CWt YjAThxDdnlxh5Jb5lrByCu1hr4NZDx6qq6mapvI1AbPi/Il2AwzZV0XDqBrSRN0SJWhX fsS+8IPQiBIinEX8p4DZ8JmmBkEdnLOtvZHZUMuXRD6v3Plzgp6GX1FIgrUAFyhQMdlg 9yUA== X-Forwarded-Encrypted: i=1; AKwUvBxxnAhkPW0q93JFSdzLK2fo63D/r2QMQxph4H3cqbgnAeuKySM0s4HhxWElql+49BOV/7mi3CW8TpehU0w=@vger.kernel.org X-Gm-Message-State: AFuF++nZ8p33/QDwos4ySAGVpLASDaM96xrDJWYuUPm/SgumidpSyNKr Yup6gKXCYH77nKPdPgaCffeRhmk8fWNXx5q+Red9avdXlJbksSdWNq3D4YfciSX+QPI= X-Gm-Gg: AYBFou0EWd+ihaPvTUITHV4JeTuQWnAfNc5kDQrPWmTl6V3I9cOmAKhiQyFc2NVYElI Lafst7SJzCdUkMA2gcsYMNLODyZZBHi15jMOrsGX/r6vvTd2wUIDlfKhnoswyj4+lgaDfOWM7Kk GyFWG2ueVXChuugvgniqzN8OSo1jpDwrNmjP38i26VoKqd63O61eQzzsxA4tlK3D2OQBGKAmBQN KuxHmIOuyLTrLmipgfcq6QH2KezrR3IYckmEDDwcg4yZ4mKXhVYjtTbK2g+eJwJ80JxeO7xxchu wT6gnYN/yfTUreE+E9THU20ZfLCIzQo/N+40estua7KK4M9/hLQrbcovmpAv2FygtbCc8uYrrXD FIgEef3FP8QhN3qnnwjkBfYfjWVoNkFVsF1cJbleqIw+ARbsqGOzdwkvsZST9d6nFM51c2yHZMp uEQvTOQRFTBf/dVrIacLM8wYILu1J0vwxXCqOjwx8= X-Received: by 2002:a5d:5f52:0:b0:485:9233:a5ae with SMTP id ffacd0b85a97d-4859233aaf2mr21075663f8f.3.1788883040918; Tue, 08 Sep 2026 08:57:20 -0700 (PDT) Received: from remote-01 ([84.17.55.227]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-485883be709sm37345704f8f.21.2026.09.08.08.57.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 08:57:20 -0700 (PDT) From: Aleksei Sviridkin To: andrew@lunn.ch, andrew+netdev@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org Cc: ericwouds@gmail.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Aleksei Sviridkin Subject: [RFC PATCH net-next v2 7/7] net: mdio: en8811h: add the nested bus Date: Tue, 8 Sep 2026 15:57:07 +0000 Message-ID: <20260908155707.4164559-8-f@lex.la> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260908155707.4164559-1-f@lex.la> References: <20260908155707.4164559-1-f@lex.la> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Registering the bus is what publishes the PHY, so it must happen only once the MD32 is running its firmware. Put the PHY on a bus of its own rather than on the parent so that the device tree can describe it normally, interrupts included, and so that the MCU keeps ownership of the reset line the PHY must not touch. Only the address the MD32 answers on is passed through. Publishing the PHY there splits the two agents that drive this chip onto two mutexes: the PHY holds the child bus lock across a whole paged sequence while each frame under it takes the parent alone, so a download holding only the parent would land between two of those frames and leave the PHY writing to a page it did not select. This driver therefore takes the child bus lock around its own chip access once the bus exists, and the library beneath it takes the parent one level down. The level cannot be baked into the library: reached from here the parent sits below a lock already held, while reached from the PHY driver on this same bus the library takes the child itself. The caller passes it down. MDIO_MUTEX_NESTED is what the child bus's own accessors already use for the parent, so this adds no edge lockdep did not have; the enum's remaining value belongs to mdio-mux, so a bus stacked under another nested bus would want a new one. The download becomes the first of two phases, since registering the bus can fail on its own and wants its own backoff and warning. That second phase gives up once its minute is spent, where the first does not: firmware files can be installed at any time, but a registration that keeps failing repeats a bus creation, a message from the MDIO core and a pair of uevents on every retry, and only a deferred probe resolves by itself. A driver that stopped there needs to be unbound and bound again. Resume now puts a failed reload back to the poller rather than leaving it, but that restores the chip and not the port: the PHY below has run its own resume by then and failed, and nothing calls phy_init_hw() a second time, so it stays unconfigured until its driver is rebound. Telling a consumer to re-initialise is the same mechanism this driver lacks for telling one to let go, and it is not invented here. The sysfs bind attributes are suppressed, because unregistering this bus while its PHY is attached leaves the consumer holding a phy_device whose driver is gone, and the driver cannot disconnect a consumer it does not know. Module unload was already refused, since attaching a PHY takes a reference on the bus owner. Removing the parent MDIO bus still reaches this driver's remove and is not covered; that wants a way for a bus to tell its PHYs' consumers to let go. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- drivers/net/mdio/mdio-airoha-en8811h.c | 223 +++++++++++++++++++++-- drivers/net/phy/air_en8811h.c | 7 +- drivers/net/phy/air_phy_lib.c | 64 ++++--- drivers/net/phy/air_phy_lib.h | 4 +- include/linux/mdio/mdio-airoha-en8811h.h | 5 +- 5 files changed, 251 insertions(+), 52 deletions(-) diff --git a/drivers/net/mdio/mdio-airoha-en8811h.c b/drivers/net/mdio/mdio= -airoha-en8811h.c index d94d74d85f10..722455891472 100644 --- a/drivers/net/mdio/mdio-airoha-en8811h.c +++ b/drivers/net/mdio/mdio-airoha-en8811h.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,21 +30,144 @@ struct en8811h_mcu { struct mdio_device *mdiodev; struct gpio_desc *reset_gpio; struct delayed_work fw_poll; + struct mii_bus *bus; unsigned int poll_ms; unsigned int waited_ms; u32 fw_version; bool warned; + bool fw_running; }; =20 +static int en8811h_mcu_read(struct mii_bus *bus, int addr, int regnum) +{ + struct en8811h_mcu *mcu =3D bus->priv; + + if (addr !=3D mcu->mdiodev->addr) + return -ENODEV; + + return mdiobus_read_nested(mcu->mdiodev->bus, addr, regnum); +} + +static int en8811h_mcu_write(struct mii_bus *bus, int addr, int regnum, u1= 6 val) +{ + struct en8811h_mcu *mcu =3D bus->priv; + + if (addr !=3D mcu->mdiodev->addr) + return -ENODEV; + + return mdiobus_write_nested(mcu->mdiodev->bus, addr, regnum, val); +} + +static int en8811h_mcu_read_c45(struct mii_bus *bus, int addr, int devad, + int regnum) +{ + struct en8811h_mcu *mcu =3D bus->priv; + + if (addr !=3D mcu->mdiodev->addr) + return -ENODEV; + + return mdiobus_c45_read_nested(mcu->mdiodev->bus, addr, devad, regnum); +} + +static int en8811h_mcu_write_c45(struct mii_bus *bus, int addr, int devad, + int regnum, u16 val) +{ + struct en8811h_mcu *mcu =3D bus->priv; + + if (addr !=3D mcu->mdiodev->addr) + return -ENODEV; + + return mdiobus_c45_write_nested(mcu->mdiodev->bus, addr, devad, regnum, + val); +} + +static int en8811h_mcu_bus_register(struct en8811h_mcu *mcu) +{ + struct mii_bus *parent =3D mcu->mdiodev->bus; + struct device *dev =3D &mcu->mdiodev->dev; + struct device_node *np; + struct mii_bus *bus; + int ret; + + np =3D of_get_child_by_name(dev->of_node, "mdio"); + if (!np) + return -ENODEV; + + /* Not devm: this is retried, and a devm bus would only be freed at + * detach. + */ + bus =3D mdiobus_alloc(); + if (!bus) { + of_node_put(np); + return -ENOMEM; + } + + bus->name =3D "airoha-en8811h"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + bus->priv =3D mcu; + bus->parent =3D dev; + if (parent->read) { + bus->read =3D en8811h_mcu_read; + bus->write =3D en8811h_mcu_write; + } + if (parent->read_c45) { + bus->read_c45 =3D en8811h_mcu_read_c45; + bus->write_c45 =3D en8811h_mcu_write_c45; + } + + ret =3D of_mdiobus_register(bus, np); + of_node_put(np); + if (!ret && !mdiobus_get_phy(bus, mcu->mdiodev->addr)) { + /* An ID read that failed leaves the bus registered and the + * PHY absent; of_mdiobus_register() returns 0 either way. + */ + mdiobus_unregister(bus); + ret =3D -ENODEV; + } + if (ret) { + mdiobus_free(bus); + return ret; + } + + mcu->bus =3D bus; + return 0; +} + +/* Serialise with the PHY below, which reaches the same registers under + * this lock through phy_select_page(). + */ +static struct mii_bus *en8811h_mcu_chip_lock(struct en8811h_mcu *mcu) +{ + struct mii_bus *bus =3D mcu->bus; + + if (bus) + mutex_lock(&bus->mdio_lock); + + return bus; +} + +static void en8811h_mcu_chip_unlock(struct mii_bus *bus) +{ + if (bus) + mutex_unlock(&bus->mdio_lock); +} + static void en8811h_mcu_fw_poll(struct work_struct *work) { struct en8811h_mcu *mcu =3D container_of(to_delayed_work(work), struct en8811h_mcu, fw_poll); struct device *dev =3D &mcu->mdiodev->dev; + struct mii_bus *chip; int ret; =20 - ret =3D air_en8811h_fw_download(mcu->mdiodev, &mcu->fw_version); - if (ret >=3D 0) { + if (!mcu->fw_running) { + chip =3D en8811h_mcu_chip_lock(mcu); + ret =3D air_en8811h_fw_download(mcu->mdiodev, &mcu->fw_version, + !!chip); + en8811h_mcu_chip_unlock(chip); + if (ret < 0) + goto retry; + dev_dbg(dev, "firmware %08x running after %ums\n", mcu->fw_version, mcu->waited_ms); ret =3D firmware_request_cache(dev, EN8811H_MD32_DM) ?: @@ -50,34 +175,58 @@ static void en8811h_mcu_fw_poll(struct work_struct *wo= rk) if (ret) dev_dbg(dev, "not cached, resume will want the files: %pe\n", ERR_PTR(ret)); - return; + mcu->fw_running =3D true; + mcu->poll_ms =3D EN8811H_FW_POLL_MIN_MS; + mcu->waited_ms =3D 0; + mcu->warned =3D false; } =20 - mcu->waited_ms +=3D mcu->poll_ms; + /* Resume re-runs the download, so the bus can already be here, and + * fwnode_mdio defers while the PHY node's interrupt controller is + * missing, so a failure is not necessarily permanent. + */ + ret =3D mcu->bus ? 0 : en8811h_mcu_bus_register(mcu); + if (!ret) + return; + +retry: if (!mcu->warned && mcu->waited_ms >=3D EN8811H_FW_WARN_MS) { - if (ret =3D=3D -ENOENT) - dev_warn(dev, "still waiting for %s and %s\n", - EN8811H_MD32_DM, EN8811H_MD32_DSP); + if (!mcu->fw_running) + dev_warn(dev, "no firmware after %ums of waiting for %s and %s: %pe\n", + mcu->waited_ms, EN8811H_MD32_DM, + EN8811H_MD32_DSP, ERR_PTR(ret)); else - dev_warn(dev, "firmware download keeps failing: %pe\n", + dev_warn(dev, "no PHY at address %d, %ums after the firmware started: %= pe\n", + mcu->mdiodev->addr, mcu->waited_ms, ERR_PTR(ret)); mcu->warned =3D true; } =20 + /* Past its budget, only a deferral will change on its own: every + * other error repeats a bus registration, its message from the MDIO + * core and its uevents for the uptime. Firmware files are the + * exception, since they can be installed at any time. + */ + if (mcu->fw_running && ret !=3D -EPROBE_DEFER && + mcu->waited_ms >=3D EN8811H_FW_WARN_MS) + return; + mcu->poll_ms =3D min(mcu->poll_ms * 2, EN8811H_FW_POLL_MAX_MS); + /* Count the sleep ahead: the first run was immediate. */ + mcu->waited_ms +=3D mcu->poll_ms; queue_delayed_work(system_freezable_wq, &mcu->fw_poll, msecs_to_jiffies(mcu->poll_ms)); } =20 /* The firmware lives in volatile RAM: no reset while the MD32 reports rea= dy. */ -static void en8811h_mcu_reset_if_dormant(struct en8811h_mcu *mcu) +static void en8811h_mcu_reset_if_dormant(struct en8811h_mcu *mcu, bool nes= ted) { struct mdio_device *mdiodev =3D mcu->mdiodev; struct device *dev =3D &mdiodev->dev; u32 assert_us =3D 0, deassert_us =3D 0; int ret; =20 - ret =3D air_en8811h_mcu_running(mdiodev); + ret =3D air_en8811h_mcu_running(mdiodev, nested); if (ret > 0) { dev_dbg(dev, "MD32 already running, leaving reset alone\n"); return; @@ -116,6 +265,7 @@ static int en8811h_mcu_probe(struct mdio_device *mdiode= v) { struct device *dev =3D &mdiodev->dev; struct en8811h_mcu *mcu; + struct device_node *np; =20 mcu =3D devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL); if (!mcu) @@ -124,6 +274,15 @@ static int en8811h_mcu_probe(struct mdio_device *mdiod= ev) mcu->mdiodev =3D mdiodev; mdiodev_set_drvdata(mdiodev, mcu); =20 + /* Registration needs this only once the firmware runs, but a DT + * hole should fail the bind now, not as a work-item error later. + */ + np =3D of_get_child_by_name(dev->of_node, "mdio"); + if (!np) + return dev_err_probe(dev, -ENODEV, + "no mdio node describing the PHY\n"); + of_node_put(np); + /* The core claims reset-gpios only for devices flagged as PHYs. */ mcu->reset_gpio =3D devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); if (IS_ERR(mcu->reset_gpio)) @@ -133,7 +292,7 @@ static int en8811h_mcu_probe(struct mdio_device *mdiode= v) if (mcu->reset_gpio) gpiod_set_consumer_name(mcu->reset_gpio, "EN8811H reset"); =20 - en8811h_mcu_reset_if_dormant(mcu); + en8811h_mcu_reset_if_dormant(mcu, false); =20 mcu->poll_ms =3D EN8811H_FW_POLL_MIN_MS; INIT_DELAYED_WORK(&mcu->fw_poll, en8811h_mcu_fw_poll); @@ -150,23 +309,43 @@ static void en8811h_mcu_remove(struct mdio_device *md= iodev) struct en8811h_mcu *mcu =3D mdiodev_get_drvdata(mdiodev); =20 cancel_delayed_work_sync(&mcu->fw_poll); + if (mcu->bus) { + mdiobus_unregister(mcu->bus); + mdiobus_free(mcu->bus); + mcu->bus =3D NULL; + } } =20 static int en8811h_mcu_resume(struct device *dev) { struct en8811h_mcu *mcu =3D dev_get_drvdata(dev); - + struct mii_bus *chip; int ret; =20 - /* Not on the workqueue: the child PHY's own resume calls - * phy_init_hw() straight after this one and needs the firmware by - * then. request_firmware() is answered from the cache the download - * registered, so it does not wait for a filesystem. + /* Nothing to redo: the poll is armed and thaws with everything else. */ + if (!mcu->fw_running) + return 0; + + /* Not on the workqueue: a DSA port's PHY resumes right after this + * one and calls phy_init_hw(), which needs the firmware by then. */ - en8811h_mcu_reset_if_dormant(mcu); - ret =3D air_en8811h_fw_download(mcu->mdiodev, &mcu->fw_version); - if (ret < 0) - dev_err(dev, "firmware not restored: %pe\n", ERR_PTR(ret)); + chip =3D en8811h_mcu_chip_lock(mcu); + en8811h_mcu_reset_if_dormant(mcu, !!chip); + ret =3D air_en8811h_fw_download(mcu->mdiodev, &mcu->fw_version, !!chip); + en8811h_mcu_chip_unlock(chip); + if (ret < 0) { + /* The reload restores this chip, not the PHY below it: its + * own resume has already failed by then and nothing calls + * phy_init_hw() twice. + */ + dev_err(dev, "firmware not restored, reloading: %pe\n", + ERR_PTR(ret)); + mcu->fw_running =3D false; + mcu->poll_ms =3D EN8811H_FW_POLL_MIN_MS; + mcu->waited_ms =3D 0; + mcu->warned =3D false; + queue_delayed_work(system_freezable_wq, &mcu->fw_poll, 0); + } =20 return 0; } @@ -186,6 +365,10 @@ static struct mdio_driver en8811h_mcu_driver =3D { .name =3D "airoha-en8811h-mcu", .of_match_table =3D en8811h_mcu_of_match, .pm =3D pm_sleep_ptr(&en8811h_mcu_pm_ops), + /* Tearing the child bus down under an attached PHY is not + * something this driver can make safe on its own. + */ + .suppress_bind_attrs =3D true, }, }; =20 diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index d1c7c472f0e9..65fa1707f57d 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -256,7 +256,7 @@ static int __air_pbus_reg_write(struct mdio_device *mdi= odev, =20 static int en8811h_wait_mcu_ready(struct phy_device *phydev) { - int ret =3D air_en8811h_wait_mcu_ready(&phydev->mdio); + int ret =3D air_en8811h_wait_mcu_ready(&phydev->mdio, false); =20 if (ret < 0) phydev_err(phydev, "MCU not ready: %pe\n", ERR_PTR(ret)); @@ -323,7 +323,7 @@ static int an8811hb_load_file(struct phy_device *phydev= , const char *name, if (ret < 0) return ret; =20 - ret =3D air_fw_write_buf(&phydev->mdio, address, fw); + ret =3D air_fw_write_buf(&phydev->mdio, address, fw, false); release_firmware(fw); return ret; } @@ -422,7 +422,8 @@ static int en8811h_load_firmware(struct phy_device *phy= dev) struct en8811h_priv *priv =3D phydev->priv; int ret; =20 - ret =3D air_en8811h_fw_download(&phydev->mdio, &priv->firmware_version); + ret =3D air_en8811h_fw_download(&phydev->mdio, &priv->firmware_version, + false); if (ret < 0) phydev_err(phydev, "Load firmware failed: %d\n", ret); =20 diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c index a5b6b3606bec..7f7530273601 100644 --- a/drivers/net/phy/air_phy_lib.c +++ b/drivers/net/phy/air_phy_lib.c @@ -272,8 +272,17 @@ static int __air_mdio_restore_page(struct mdio_device = *mdiodev, return ret; } =20 +/* Nested when the caller already holds the bus this chip hangs below. */ +static void air_mdiodev_lock(struct mdio_device *mdiodev, bool nested) +{ + if (nested) + mdiodev_lock_nested(mdiodev); + else + mdiodev_lock(mdiodev); +} + int air_fw_write_buf(struct mdio_device *mdiodev, u32 address, - const struct firmware *fw) + const struct firmware *fw, bool nested) { size_t chunk, done =3D 0; int saved_page, ret; @@ -287,7 +296,7 @@ int air_fw_write_buf(struct mdio_device *mdiodev, u32 a= ddress, while (done < fw->size) { chunk =3D min_t(size_t, fw->size - done, AIR_FW_CHUNK_BYTES); =20 - mdiodev_lock(mdiodev); + air_mdiodev_lock(mdiodev, nested); =20 saved_page =3D __air_mdio_select_page(mdiodev, AIR_PHY_PAGE_EXTENDED_4); @@ -313,11 +322,12 @@ int air_fw_write_buf(struct mdio_device *mdiodev, u32= address, EXPORT_SYMBOL_GPL(air_fw_write_buf); =20 static int air_mdio_buckpbus_reg_read(struct mdio_device *mdiodev, - u32 pbus_address, u32 *pbus_data) + u32 pbus_address, u32 *pbus_data, + bool nested) { int saved_page, ret; =20 - mdiodev_lock(mdiodev); + air_mdiodev_lock(mdiodev, nested); =20 saved_page =3D __air_mdio_select_page(mdiodev, AIR_PHY_PAGE_EXTENDED_4); if (saved_page < 0) { @@ -333,11 +343,12 @@ static int air_mdio_buckpbus_reg_read(struct mdio_dev= ice *mdiodev, } =20 static int air_mdio_buckpbus_reg_write(struct mdio_device *mdiodev, - u32 pbus_address, u32 pbus_data) + u32 pbus_address, u32 pbus_data, + bool nested) { int saved_page, ret; =20 - mdiodev_lock(mdiodev); + air_mdiodev_lock(mdiodev, nested); =20 saved_page =3D __air_mdio_select_page(mdiodev, AIR_PHY_PAGE_EXTENDED_4); if (saved_page < 0) { @@ -354,11 +365,12 @@ static int air_mdio_buckpbus_reg_write(struct mdio_de= vice *mdiodev, } =20 static int air_mdio_buckpbus_reg_modify(struct mdio_device *mdiodev, - u32 pbus_address, u32 mask, u32 set) + u32 pbus_address, u32 mask, u32 set, + bool nested) { int saved_page, ret; =20 - mdiodev_lock(mdiodev); + air_mdiodev_lock(mdiodev, nested); =20 saved_page =3D __air_mdio_select_page(mdiodev, AIR_PHY_PAGE_EXTENDED_4); if (saved_page < 0) { @@ -399,20 +411,20 @@ static int __air_mmd_read(struct mdio_device *mdiodev= , u16 devad, u16 regnum) return __mdiobus_read(bus, addr, MII_MMD_DATA); } =20 -static int air_mmd_status_read(struct mdio_device *mdiodev) +static int air_mmd_status_read(struct mdio_device *mdiodev, bool nested) { int ret; =20 - mdiodev_lock(mdiodev); + air_mdiodev_lock(mdiodev, nested); ret =3D __air_mmd_read(mdiodev, MDIO_MMD_VEND1, EN8811H_PHY_FW_STATUS); mdiodev_unlock(mdiodev); =20 return ret; } =20 -int air_en8811h_mcu_running(struct mdio_device *mdiodev) +int air_en8811h_mcu_running(struct mdio_device *mdiodev, bool nested) { - int ret =3D air_mmd_status_read(mdiodev); + int ret =3D air_mmd_status_read(mdiodev, nested); =20 if (ret < 0) return ret; @@ -421,12 +433,12 @@ int air_en8811h_mcu_running(struct mdio_device *mdiod= ev) } EXPORT_SYMBOL_GPL(air_en8811h_mcu_running); =20 -int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev) +int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev, bool nested) { int ret, reg_value; =20 ret =3D air_mdio_buckpbus_reg_write(mdiodev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_FINISH); + EN8811H_FW_CTRL_1_FINISH, nested); if (ret) return ret; =20 @@ -436,7 +448,7 @@ int air_en8811h_wait_mcu_ready(struct mdio_device *mdio= dev) ret =3D read_poll_timeout(air_mmd_status_read, reg_value, reg_value < 0 || reg_value =3D=3D EN8811H_PHY_READY, - 20000, 7500000, true, mdiodev); + 20000, 7500000, true, mdiodev, nested); if (reg_value < 0) return reg_value; if (ret) { @@ -448,19 +460,20 @@ int air_en8811h_wait_mcu_ready(struct mdio_device *md= iodev) } EXPORT_SYMBOL_GPL(air_en8811h_wait_mcu_ready); =20 -int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version) +int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version, + bool nested) { const struct firmware *fw1, *fw2; struct device *dev =3D &mdiodev->dev; int ret; =20 - ret =3D air_en8811h_mcu_running(mdiodev); + ret =3D air_en8811h_mcu_running(mdiodev, nested); if (ret < 0) return ret; =20 if (ret) { ret =3D air_mdio_buckpbus_reg_read(mdiodev, EN8811H_FW_VERSION, - fw_version); + fw_version, nested); if (ret < 0) return ret; =20 @@ -478,35 +491,36 @@ int air_en8811h_fw_download(struct mdio_device *mdiod= ev, u32 *fw_version) goto air_fw_download_rel1; =20 ret =3D air_mdio_buckpbus_reg_write(mdiodev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); + EN8811H_FW_CTRL_1_START, nested); if (ret < 0) goto air_fw_download_out; =20 ret =3D air_mdio_buckpbus_reg_modify(mdiodev, EN8811H_FW_CTRL_2, EN8811H_FW_CTRL_2_LOADING, - EN8811H_FW_CTRL_2_LOADING); + EN8811H_FW_CTRL_2_LOADING, nested); if (ret < 0) goto air_fw_download_out; =20 - ret =3D air_fw_write_buf(mdiodev, AIR_FW_ADDR_DM, fw1); + ret =3D air_fw_write_buf(mdiodev, AIR_FW_ADDR_DM, fw1, nested); if (ret < 0) goto air_fw_download_out; =20 - ret =3D air_fw_write_buf(mdiodev, AIR_FW_ADDR_DSP, fw2); + ret =3D air_fw_write_buf(mdiodev, AIR_FW_ADDR_DSP, fw2, nested); if (ret < 0) goto air_fw_download_out; =20 ret =3D air_mdio_buckpbus_reg_modify(mdiodev, EN8811H_FW_CTRL_2, - EN8811H_FW_CTRL_2_LOADING, 0); + EN8811H_FW_CTRL_2_LOADING, 0, + nested); if (ret < 0) goto air_fw_download_out; =20 - ret =3D air_en8811h_wait_mcu_ready(mdiodev); + ret =3D air_en8811h_wait_mcu_ready(mdiodev, nested); if (ret < 0) goto air_fw_download_out; =20 ret =3D air_mdio_buckpbus_reg_read(mdiodev, EN8811H_FW_VERSION, - fw_version); + fw_version, nested); if (ret < 0) goto air_fw_download_out; =20 diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h index 3391396aecd1..56ba987a68cc 100644 --- a/drivers/net/phy/air_phy_lib.h +++ b/drivers/net/phy/air_phy_lib.h @@ -62,7 +62,7 @@ int air_phy_write_page(struct phy_device *phydev, int pag= e); struct firmware; =20 int air_fw_write_buf(struct mdio_device *mdiodev, u32 address, - const struct firmware *fw); -int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev); + const struct firmware *fw, bool nested); +int air_en8811h_wait_mcu_ready(struct mdio_device *mdiodev, bool nested); =20 #endif /* __AIR_PHY_LIB_H */ diff --git a/include/linux/mdio/mdio-airoha-en8811h.h b/include/linux/mdio/= mdio-airoha-en8811h.h index 0d23811e90dc..4494dc6c1022 100644 --- a/include/linux/mdio/mdio-airoha-en8811h.h +++ b/include/linux/mdio/mdio-airoha-en8811h.h @@ -17,8 +17,9 @@ struct mdio_device; #define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" =20 /* Returns 1 running, 0 dormant, negative on a failed status read. */ -int air_en8811h_mcu_running(struct mdio_device *mdiodev); +int air_en8811h_mcu_running(struct mdio_device *mdiodev, bool nested); /* Returns 1 when it adopted firmware that was already running. */ -int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version); +int air_en8811h_fw_download(struct mdio_device *mdiodev, u32 *fw_version, + bool nested); =20 #endif /* __LINUX_MDIO_AIROHA_EN8811H_H */ --=20 2.53.0