Commit 154bc3b6 authored by Maxime Chevallier's avatar Maxime Chevallier Committed by Jakub Kicinski
Browse files

net: phy: qca807x: Support SFP through phy_port interface



QCA8072/8075 may be used as combo-port PHYs, with Serdes (100/1000BaseX)
 and Copper interfaces. The PHY has the ability to read the configuration
it's in.  If the configuration indicates the PHY is in combo mode, allow
registering up to 2 ports.

Register a dedicated set of port ops to handle the serdes port, and rely
on generic phylib SFP support for the SFP handling.

Reviewed-by: default avatarChristophe Leroy <christophe.leroy@csgroup.eu>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Tested-by: default avatarChristophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: default avatarMaxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20260108080041.553250-13-maxime.chevallier@bootlin.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4e26a284
Loading
Loading
Loading
Loading
+30 −42
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@
#include <linux/phy.h>
#include <linux/bitfield.h>
#include <linux/gpio/driver.h>
#include <linux/sfp.h>
#include <linux/phy_port.h>

#include "../phylib.h"
#include "qcom.h"
@@ -643,67 +643,54 @@ static int qca807x_phy_package_config_init_once(struct phy_device *phydev)
	return ret;
}

static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
static int qca807x_configure_serdes(struct phy_port *port, bool enable,
				    phy_interface_t interface)
{
	struct phy_device *phydev = upstream;
	const struct sfp_module_caps *caps;
	phy_interface_t iface;
	struct phy_device *phydev = port_phydev(port);
	int ret;

	caps = sfp_get_module_caps(phydev->sfp_bus);
	iface = sfp_select_interface(phydev->sfp_bus, caps->link_modes);
	if (!phydev)
		return -ENODEV;

	dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));

	switch (iface) {
	case PHY_INTERFACE_MODE_1000BASEX:
	case PHY_INTERFACE_MODE_100BASEX:
	if (enable) {
		/* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
		ret = phy_modify(phydev,
				 QCA807X_CHIP_CONFIGURATION,
				 QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
				 QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
		if (ret)
			return ret;
		/* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
		ret = phy_set_bits_mmd(phydev,
				       MDIO_MMD_AN,
				       QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
				       QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
		/* Select fiber page */
		ret = phy_clear_bits(phydev,
				     QCA807X_CHIP_CONFIGURATION,
				     QCA807X_BT_BX_REG_SEL);

		phydev->port = PORT_FIBRE;
		break;
	default:
		dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
		return -EINVAL;
		if (ret)
			return ret;
	}

	return ret;
	phydev->port = enable ? PORT_FIBRE : PORT_TP;

	return phy_modify(phydev, QCA807X_CHIP_CONFIGURATION,
			  QCA807X_BT_BX_REG_SEL,
			  enable ? 0 : QCA807X_BT_BX_REG_SEL);
}

static void qca807x_sfp_remove(void *upstream)
static const struct phy_port_ops qca807x_serdes_port_ops = {
	.configure_mii = qca807x_configure_serdes,
};

static int qca807x_attach_mii_port(struct phy_device *phydev,
				   struct phy_port *port)
{
	struct phy_device *phydev = upstream;
	__set_bit(PHY_INTERFACE_MODE_1000BASEX, port->interfaces);
	__set_bit(PHY_INTERFACE_MODE_100BASEX, port->interfaces);

	/* Select copper page */
	phy_set_bits(phydev,
		     QCA807X_CHIP_CONFIGURATION,
		     QCA807X_BT_BX_REG_SEL);
	port->ops = &qca807x_serdes_port_ops;

	phydev->port = PORT_TP;
	return 0;
}

static const struct sfp_upstream_ops qca807x_sfp_ops = {
	.attach = phy_sfp_attach,
	.detach = phy_sfp_detach,
	.module_insert = qca807x_sfp_insert,
	.module_remove = qca807x_sfp_remove,
	.connect_phy = phy_sfp_connect_phy,
	.disconnect_phy = phy_sfp_disconnect_phy,
};

static int qca807x_probe(struct phy_device *phydev)
{
	struct device_node *node = phydev->mdio.dev.of_node;
@@ -744,9 +731,8 @@ static int qca807x_probe(struct phy_device *phydev)

	/* Attach SFP bus on combo port*/
	if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
		ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
		if (ret)
			return ret;
		phydev->max_n_ports = 2;

		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
	}
@@ -824,6 +810,7 @@ static struct phy_driver qca807x_drivers[] = {
		.get_phy_stats		= qca807x_get_phy_stats,
		.set_wol		= at8031_set_wol,
		.get_wol		= at803x_get_wol,
		.attach_mii_port	= qca807x_attach_mii_port,
	},
	{
		PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
@@ -851,6 +838,7 @@ static struct phy_driver qca807x_drivers[] = {
		.get_phy_stats		= qca807x_get_phy_stats,
		.set_wol		= at8031_set_wol,
		.get_wol		= at803x_get_wol,
		.attach_mii_port	= qca807x_attach_mii_port,
	},
};
module_phy_driver(qca807x_drivers);