[PATCH v4 net-next] net: phy: mediatek: support MT7530 PHYs on EN71221 MCM

Caleb James DeLisle cjd at cjdns.fr
Tue Sep 15 04:03:32 PDT 2026


On 15/09/2026 12:48, Daniel Golle wrote:
> On Tue, Sep 15, 2026 at 10:35:50AM +0000, Caleb James DeLisle wrote:
>> The EcoNet EN751221 multi-chip module implementation of the MT7530
>> requires some additional configuration of the PHYs on startup.
>> The reason for this is not known, but it is possible that it has
>> to do with the fact that the EN751221 MCM implementation of the
>> MT7530 runs at an abnormal PLL frequency (362.5Mhz).
>>
>> Detect whether the MT7530 PHY is attached to the MDIO bus of an
>> EcoNet EN751221 switch and if so, apply the necessary register
>> updates. Additionally, never attempt to configure an MT7530
>> identified PHY which does not have gigabit support because the same
>> ID is used for another (FE) PHY.
>>
>> Co-developed-by: Matheus Sampaio Queiroga <srherobrine20 at gmail.com>
>> Signed-off-by: Matheus Sampaio Queiroga <srherobrine20 at gmail.com>
>> Signed-off-by: Caleb James DeLisle <cjd at cjdns.fr>
>> [...]
>> +static bool en751221_is_mcm_phy(struct phy_device *phydev)
>> +{
>> +	struct device *parent = phydev->mdio.bus->parent;
>> +
>> +	return parent && parent->of_node &&
>> +	       of_device_is_compatible(parent->of_node, "econet,en751221");
> I thought you had found better options than using the parent of_node...?


I documented my findings re registers that are different, but I don't 
have any documentation of those registers so to me they might as well be 
reading outside air temperature, so there is nothing I could safely do 
except the parent of_node.


>
>> +}
>> +
>> +/*
>> + * MTK_GPHY_ID_MT7530 ID is also used for an EcoNet SoC FE phy, but that PHY
>> + * does not advertise ESTATUS_1000_TFULL.
>> + */
>> +static int mt7530_phy_match(struct phy_device *phydev,
>> +			    const struct phy_driver *phydrv)
>> +{
> You miss the check for the PHY ID, see also below.
>
> I'd suggest something like
> 	int ret;
>
> 	ret = genphy_match_phy_device(phydev, phydrv);
> 	if (!ret)
> 		return 0;
>
>> +	return (phy_read(phydev, MII_ESTATUS) & ESTATUS_1000_TFULL) != 0 &&
> The above should be helper function used by both .match_phy_device ops.


Sounds good.


>
>> +		!en751221_is_mcm_phy(phydev);
>> +}
>> +
>> +static int en751221_phy_match(struct phy_device *phydev,
>> +			      const struct phy_driver *phydrv)
>> +{
>> +	return (phy_read(phydev, MII_ESTATUS) & ESTATUS_1000_TFULL) != 0 &&
>> +		en751221_is_mcm_phy(phydev);
>> +}
>> +
>>   static int mt7531_phy_config_init(struct phy_device *phydev)
>>   {
>>   	mtk_gephy_config_init(phydev);
>> @@ -135,6 +195,23 @@ static struct phy_driver mtk_gephy_driver[] = {
>>   		 */
>>   		.config_intr	= genphy_no_config_intr,
>>   		.handle_interrupt = genphy_handle_interrupt_no_ack,
>> +		.match_phy_device = mt7530_phy_match,
>> +		.suspend	= genphy_suspend,
>> +		.resume		= genphy_resume,
>> +		.read_page	= mtk_phy_read_page,
>> +		.write_page	= mtk_phy_write_page,
>> +	},
>> +	{
>> +		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530),
> This assigns phy_id and phy_id_mask, which are used by
> genphy_match_phy_device(). However, you are setting a .match_phy_device
> op which doesn't call genphy_match_phy_device(), so this is ignored.


I see, I should have checked that more closely. Thanks, will fix.


Caleb

>
>> +		.name		= "EcoNet EN751221 MCM PHY",
>> +		.probe		= mt7530_phy_probe,
>> +		.config_init	= en751221_mcm_phy_config_init,
>> +		/* Interrupts are handled by the switch, not the PHY
>> +		 * itself.
>> +		 */
>> +		.config_intr	= genphy_no_config_intr,
>> +		.handle_interrupt = genphy_handle_interrupt_no_ack,
>> +		.match_phy_device = en751221_phy_match,
>>   		.suspend	= genphy_suspend,
>>   		.resume		= genphy_resume,
>>   		.read_page	= mtk_phy_read_page,
>> -- 
>> 2.39.5
>>



More information about the linux-arm-kernel mailing list