Merge branch 'net-phy-add-support-for-new-aeonsemi-phys'

Christian Marangi says:

====================
net: phy: Add support for new Aeonsemi PHYs

Add support for new Aeonsemi 10G C45 PHYs. These PHYs intergate an IPC
to setup some configuration and require special handling to sync with
the parity bit. The parity bit is a way the IPC use to follow correct
order of command sent.

Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
AS21210PB1 that all register with the PHY ID 0x7500 0x7500
before the firmware is loaded.

The big special thing about this PHY is that it does provide
a generic PHY ID in C45 register that change to the correct one
one the firmware is loaded.

In practice:
- MMD 0x7 ID 0x7500 0x9410 -> FW LOAD -> ID 0x7500 0x9422

To handle this, we operate on .match_phy_device where
we check the PHY ID, if the ID match the generic one,
we load the firmware and we return 0 (PHY driver doesn't
match). Then PHY core will try the next PHY driver in the list
and this time the PHY is correctly filled in and we register
for it.

To help in the matching and not modify part of the PHY device
struct, .match_phy_device is extended to provide also the
current phy_driver is trying to match for. This add the
extra benefits that some other PHY can simplify their
.match_phy_device OP.
====================

Link: https://patch.msgid.link/20250517201353.5137-1-ansuelsmth@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-05-21 15:56:11 -07:00
16 changed files with 1338 additions and 67 deletions

View File

@@ -0,0 +1,122 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/aeonsemi,as21xxx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Aeonsemi AS21XXX Ethernet PHY
maintainers:
- Christian Marangi <ansuelsmth@gmail.com>
description: |
Aeonsemi AS21xxx Ethernet PHYs requires a firmware to be loaded to actually
work. The same firmware is compatible with various PHYs of the same family.
A PHY with not firmware loaded will be exposed on the MDIO bus with ID
0x7500 0x7500 or 0x7500 0x9410 on C45 registers.
This can be done and is implemented by OEM in 2 different way:
- Attached SPI flash directly to the PHY with the firmware. The PHY
will self load the firmware in the presence of this configuration.
- Manually provided firmware loaded from a file in the filesystem.
Each PHY can support up to 5 LEDs.
AS2xxx PHY Name logic:
AS21x1xxB1
^ ^^
| |J: Supports SyncE/PTP
| |P: No SyncE/PTP support
| 1: Supports 2nd Serdes
| 2: Not 2nd Serdes support
0: 10G, 5G, 2.5G
5: 5G, 2.5G
2: 2.5G
allOf:
- $ref: ethernet-phy.yaml#
select:
properties:
compatible:
contains:
enum:
- ethernet-phy-id7500.9410
- ethernet-phy-id7500.9402
- ethernet-phy-id7500.9412
- ethernet-phy-id7500.9422
- ethernet-phy-id7500.9432
- ethernet-phy-id7500.9442
- ethernet-phy-id7500.9452
- ethernet-phy-id7500.9462
- ethernet-phy-id7500.9472
- ethernet-phy-id7500.9482
- ethernet-phy-id7500.9492
required:
- compatible
properties:
reg:
maxItems: 1
firmware-name:
description: specify the name of PHY firmware to load
maxItems: 1
required:
- compatible
- reg
if:
properties:
compatible:
contains:
const: ethernet-phy-id7500.9410
then:
required:
- firmware-name
else:
properties:
firmware-name: false
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethernet-phy@1f {
compatible = "ethernet-phy-id7500.9410",
"ethernet-phy-ieee802.3-c45";
reg = <31>;
firmware-name = "as21x1x_fw.bin";
leds {
#address-cells = <1>;
#size-cells = <0>;
led@0 {
reg = <0>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_LAN;
function-enumerator = <0>;
default-state = "keep";
};
led@1 {
reg = <1>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_LAN;
function-enumerator = <1>;
default-state = "keep";
};
};
};
};

View File

@@ -646,6 +646,13 @@ F: drivers/iio/accel/adxl380.h
F: drivers/iio/accel/adxl380_i2c.c
F: drivers/iio/accel/adxl380_spi.c
AEONSEMI PHY DRIVER
M: Christian Marangi <ansuelsmth@gmail.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/aeonsemi,as21xxx.yaml
F: drivers/net/phy/as21xxx.c
AF8133J THREE-AXIS MAGNETOMETER DRIVER
M: Ondřej Jirman <megi@xff.cz>
S: Maintained

View File

@@ -76,6 +76,18 @@ config SFP
comment "MII PHY device drivers"
config AS21XXX_PHY
tristate "Aeonsemi AS21xxx PHYs"
help
Currently supports the Aeonsemi AS21xxx PHY.
These are C45 PHYs 10G that require all a generic firmware.
Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
AS21210PB1 that all register with the PHY ID 0x7500 0x7500
before the firmware is loaded.
config AIR_EN8811H_PHY
tristate "Airoha EN8811H 2.5 Gigabit PHY"
help

View File

@@ -32,6 +32,7 @@ obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
obj-$(CONFIG_AMD_PHY) += amd.o
obj-$(CONFIG_AMCC_QT2025_PHY) += qt2025.o
obj-$(CONFIG_AQUANTIA_PHY) += aquantia/
obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o
ifdef CONFIG_AX88796B_RUST_PHY
obj-$(CONFIG_AX88796B_PHY) += ax88796b_rust.o
else

1087
drivers/net/phy/as21xxx.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -185,14 +185,10 @@ static irqreturn_t bcm87xx_handle_interrupt(struct phy_device *phydev)
return IRQ_HANDLED;
}
static int bcm8706_match_phy_device(struct phy_device *phydev)
static int bcm87xx_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706;
}
static int bcm8727_match_phy_device(struct phy_device *phydev)
{
return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727;
return phydev->c45_ids.device_ids[4] == phydrv->phy_id;
}
static struct phy_driver bcm87xx_driver[] = {
@@ -206,7 +202,7 @@ static struct phy_driver bcm87xx_driver[] = {
.read_status = bcm87xx_read_status,
.config_intr = bcm87xx_config_intr,
.handle_interrupt = bcm87xx_handle_interrupt,
.match_phy_device = bcm8706_match_phy_device,
.match_phy_device = bcm87xx_match_phy_device,
}, {
.phy_id = PHY_ID_BCM8727,
.phy_id_mask = 0xffffffff,
@@ -217,7 +213,7 @@ static struct phy_driver bcm87xx_driver[] = {
.read_status = bcm87xx_read_status,
.config_intr = bcm87xx_config_intr,
.handle_interrupt = bcm87xx_handle_interrupt,
.match_phy_device = bcm8727_match_phy_device,
.match_phy_device = bcm87xx_match_phy_device,
} };
module_phy_driver(bcm87xx_driver);

View File

@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(struct phy_device *phydev, bool ip101a)
return ip101a == !ret;
}
static int ip101a_match_phy_device(struct phy_device *phydev)
static int ip101a_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return ip101a_g_match_phy_device(phydev, true);
}
static int ip101g_match_phy_device(struct phy_device *phydev)
static int ip101g_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return ip101a_g_match_phy_device(phydev, false);
}

View File

@@ -1264,7 +1264,8 @@ static int mv3310_get_number_of_ports(struct phy_device *phydev)
return ret + 1;
}
static int mv3310_match_phy_device(struct phy_device *phydev)
static int mv3310_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
@@ -1273,7 +1274,8 @@ static int mv3310_match_phy_device(struct phy_device *phydev)
return mv3310_get_number_of_ports(phydev) == 1;
}
static int mv3340_match_phy_device(struct phy_device *phydev)
static int mv3340_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
@@ -1297,12 +1299,14 @@ static int mv211x_match_phy_device(struct phy_device *phydev, bool has_5g)
return !!(val & MDIO_PCS_SPEED_5G) == has_5g;
}
static int mv2110_match_phy_device(struct phy_device *phydev)
static int mv2110_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return mv211x_match_phy_device(phydev, true);
}
static int mv2111_match_phy_device(struct phy_device *phydev)
static int mv2111_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return mv211x_match_phy_device(phydev, false);
}

View File

@@ -768,7 +768,8 @@ static int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev,
return !ret;
}
static int ksz8051_match_phy_device(struct phy_device *phydev)
static int ksz8051_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return ksz8051_ksz8795_match_phy_device(phydev, true);
}
@@ -888,7 +889,8 @@ static int ksz8061_config_init(struct phy_device *phydev)
return kszphy_config_init(phydev);
}
static int ksz8795_match_phy_device(struct phy_device *phydev)
static int ksz8795_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return ksz8051_ksz8795_match_phy_device(phydev, false);
}

View File

@@ -19,7 +19,6 @@
#include "nxp-c45-tja11xx.h"
#define PHY_ID_MASK GENMASK(31, 4)
/* Same id: TJA1103, TJA1104 */
#define PHY_ID_TJA_1103 0x001BB010
/* Same id: TJA1120, TJA1121 */
@@ -1966,28 +1965,24 @@ static int nxp_c45_macsec_ability(struct phy_device *phydev)
return macsec_ability;
}
static int tja1103_match_phy_device(struct phy_device *phydev)
static int tja11xx_no_macsec_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) &&
!nxp_c45_macsec_ability(phydev);
if (!phy_id_compare(phydev->phy_id, phydrv->phy_id,
phydrv->phy_id_mask))
return 0;
return !nxp_c45_macsec_ability(phydev);
}
static int tja1104_match_phy_device(struct phy_device *phydev)
static int tja11xx_macsec_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) &&
nxp_c45_macsec_ability(phydev);
}
if (!phy_id_compare(phydev->phy_id, phydrv->phy_id,
phydrv->phy_id_mask))
return 0;
static int tja1120_match_phy_device(struct phy_device *phydev)
{
return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1120, PHY_ID_MASK) &&
!nxp_c45_macsec_ability(phydev);
}
static int tja1121_match_phy_device(struct phy_device *phydev)
{
return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1120, PHY_ID_MASK) &&
nxp_c45_macsec_ability(phydev);
return nxp_c45_macsec_ability(phydev);
}
static const struct nxp_c45_regmap tja1120_regmap = {
@@ -2060,6 +2055,7 @@ static const struct nxp_c45_phy_data tja1120_phy_data = {
static struct phy_driver nxp_c45_driver[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103),
.name = "NXP C45 TJA1103",
.get_features = nxp_c45_get_features,
.driver_data = &tja1103_phy_data,
@@ -2081,9 +2077,10 @@ static struct phy_driver nxp_c45_driver[] = {
.get_sqi = nxp_c45_get_sqi,
.get_sqi_max = nxp_c45_get_sqi_max,
.remove = nxp_c45_remove,
.match_phy_device = tja1103_match_phy_device,
.match_phy_device = tja11xx_no_macsec_match_phy_device,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103),
.name = "NXP C45 TJA1104",
.get_features = nxp_c45_get_features,
.driver_data = &tja1103_phy_data,
@@ -2105,9 +2102,10 @@ static struct phy_driver nxp_c45_driver[] = {
.get_sqi = nxp_c45_get_sqi,
.get_sqi_max = nxp_c45_get_sqi_max,
.remove = nxp_c45_remove,
.match_phy_device = tja1104_match_phy_device,
.match_phy_device = tja11xx_macsec_match_phy_device,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_TJA_1120),
.name = "NXP C45 TJA1120",
.get_features = nxp_c45_get_features,
.driver_data = &tja1120_phy_data,
@@ -2130,9 +2128,10 @@ static struct phy_driver nxp_c45_driver[] = {
.get_sqi = nxp_c45_get_sqi,
.get_sqi_max = nxp_c45_get_sqi_max,
.remove = nxp_c45_remove,
.match_phy_device = tja1120_match_phy_device,
.match_phy_device = tja11xx_no_macsec_match_phy_device,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_TJA_1120),
.name = "NXP C45 TJA1121",
.get_features = nxp_c45_get_features,
.driver_data = &tja1120_phy_data,
@@ -2155,7 +2154,7 @@ static struct phy_driver nxp_c45_driver[] = {
.get_sqi = nxp_c45_get_sqi,
.get_sqi_max = nxp_c45_get_sqi_max,
.remove = nxp_c45_remove,
.match_phy_device = tja1121_match_phy_device,
.match_phy_device = tja11xx_macsec_match_phy_device,
},
};

View File

@@ -651,12 +651,14 @@ static int tja1102_match_phy_device(struct phy_device *phydev, bool port0)
return !ret;
}
static int tja1102_p0_match_phy_device(struct phy_device *phydev)
static int tja1102_p0_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return tja1102_match_phy_device(phydev, true);
}
static int tja1102_p1_match_phy_device(struct phy_device *phydev)
static int tja1102_p1_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return tja1102_match_phy_device(phydev, false);
}

View File

@@ -543,20 +543,26 @@ static int phy_scan_fixups(struct phy_device *phydev)
return 0;
}
static int phy_bus_match(struct device *dev, const struct device_driver *drv)
/**
* genphy_match_phy_device - match a PHY device with a PHY driver
* @phydev: target phy_device struct
* @phydrv: target phy_driver struct
*
* Description: Checks whether the given PHY device matches the specified
* PHY driver. For Clause 45 PHYs, iterates over the available device
* identifiers and compares them against the driver's expected PHY ID,
* applying the provided mask. For Clause 22 PHYs, a direct ID comparison
* is performed.
*
* Return: 1 if the PHY device matches the driver, 0 otherwise.
*/
int genphy_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
struct phy_device *phydev = to_phy_device(dev);
const struct phy_driver *phydrv = to_phy_driver(drv);
const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
int i;
if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
return 0;
if (phydrv->match_phy_device)
return phydrv->match_phy_device(phydev);
if (phydev->is_c45) {
const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
int i;
for (i = 1; i < num_ids; i++) {
if (phydev->c45_ids.device_ids[i] == 0xffffffff)
continue;
@@ -565,11 +571,27 @@ static int phy_bus_match(struct device *dev, const struct device_driver *drv)
phydrv->phy_id, phydrv->phy_id_mask))
return 1;
}
return 0;
} else {
return phy_id_compare(phydev->phy_id, phydrv->phy_id,
phydrv->phy_id_mask);
}
return phy_id_compare(phydev->phy_id, phydrv->phy_id,
phydrv->phy_id_mask);
}
EXPORT_SYMBOL_GPL(genphy_match_phy_device);
static int phy_bus_match(struct device *dev, const struct device_driver *drv)
{
struct phy_device *phydev = to_phy_device(dev);
const struct phy_driver *phydrv = to_phy_driver(drv);
if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
return 0;
if (phydrv->match_phy_device)
return phydrv->match_phy_device(phydev, phydrv);
return genphy_match_phy_device(phydev, phydrv);
}
static ssize_t

View File

@@ -1315,13 +1315,15 @@ static bool rtlgen_supports_mmd(struct phy_device *phydev)
return val > 0;
}
static int rtlgen_match_phy_device(struct phy_device *phydev)
static int rtlgen_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return phydev->phy_id == RTL_GENERIC_PHYID &&
!rtlgen_supports_2_5gbps(phydev);
}
static int rtl8226_match_phy_device(struct phy_device *phydev)
static int rtl8226_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return phydev->phy_id == RTL_GENERIC_PHYID &&
rtlgen_supports_2_5gbps(phydev) &&
@@ -1337,32 +1339,38 @@ static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
return !is_c45 && (id == phydev->phy_id);
}
static int rtl8221b_match_phy_device(struct phy_device *phydev)
static int rtl8221b_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev);
}
static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
}
static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
}
static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
}
static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
}
static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev)
static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
if (phydev->is_c45)
return false;
@@ -1381,7 +1389,8 @@ static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev)
return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev);
}
static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
static int rtl8251b_c45_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return rtlgen_is_c45_match(phydev, RTL_8251B, true);
}

View File

@@ -67,7 +67,8 @@ static int teranetics_read_status(struct phy_device *phydev)
return 0;
}
static int teranetics_match_phy_device(struct phy_device *phydev)
static int teranetics_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
}

View File

@@ -990,7 +990,8 @@ struct phy_driver {
* driver for the given phydev. If NULL, matching is based on
* phy_id and phy_id_mask.
*/
int (*match_phy_device)(struct phy_device *phydev);
int (*match_phy_device)(struct phy_device *phydev,
const struct phy_driver *phydrv);
/**
* @set_wol: Some devices (e.g. qnap TS-119P II) require PHY
@@ -1867,6 +1868,9 @@ char *phy_attached_info_irq(struct phy_device *phydev)
__malloc;
void phy_attached_info(struct phy_device *phydev);
int genphy_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv);
/* Clause 22 PHY */
int genphy_read_abilities(struct phy_device *phydev);
int genphy_setup_forced(struct phy_device *phydev);

View File

@@ -421,6 +421,7 @@ impl<T: Driver> Adapter<T> {
/// `phydev` must be passed by the corresponding callback in `phy_driver`.
unsafe extern "C" fn match_phy_device_callback(
phydev: *mut bindings::phy_device,
_phydrv: *const bindings::phy_driver,
) -> crate::ffi::c_int {
// SAFETY: This callback is called only in contexts
// where we hold `phy_device->lock`, so the accessors on