mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-23 16:31:15 -05:00
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: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Tested-by: Christophe Leroy <christophe.leroy@csgroup.eu> Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> Link: https://patch.msgid.link/20260108080041.553250-13-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
4e26a284b9
commit
154bc3b66c
@@ -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)
|
||||
{
|
||||
struct phy_device *phydev = upstream;
|
||||
|
||||
/* Select copper page */
|
||||
phy_set_bits(phydev,
|
||||
QCA807X_CHIP_CONFIGURATION,
|
||||
QCA807X_BT_BX_REG_SEL);
|
||||
|
||||
phydev->port = PORT_TP;
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
__set_bit(PHY_INTERFACE_MODE_1000BASEX, port->interfaces);
|
||||
__set_bit(PHY_INTERFACE_MODE_100BASEX, port->interfaces);
|
||||
|
||||
port->ops = &qca807x_serdes_port_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user