net: mscc: ocelot: make use of SerDes PHYs for handling their configuration

Previously, the SerDes muxing was hardcoded to a given mode in the MAC
controller driver. Now, the SerDes muxing is configured within the
Device Tree and is enforced in the MAC controller driver so we can have
a lot of different SerDes configurations.

Make use of the SerDes PHYs in the MAC controller to set up the SerDes
according to the SerDes<->switch port mapping and the communication mode
with the Ethernet PHY.

Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Quentin Schulz 2018-10-04 14:22:08 +02:00 committed by David S. Miller
parent 51f6b410fc
commit 71e32a20cf
5 changed files with 63 additions and 12 deletions

View File

@ -23,6 +23,8 @@ config MSCC_OCELOT_SWITCH
config MSCC_OCELOT_SWITCH_OCELOT config MSCC_OCELOT_SWITCH_OCELOT
tristate "Ocelot switch driver on Ocelot" tristate "Ocelot switch driver on Ocelot"
depends on MSCC_OCELOT_SWITCH depends on MSCC_OCELOT_SWITCH
depends on GENERIC_PHY
depends on OF_NET
help help
This driver supports the Ocelot network switch device as present on This driver supports the Ocelot network switch device as present on
the Ocelot SoCs. the Ocelot SoCs.

View File

@ -472,6 +472,7 @@ static int ocelot_port_open(struct net_device *dev)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port *port = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = port->ocelot;
enum phy_mode phy_mode;
int err; int err;
/* Enable receiving frames on the port, and activate auto-learning of /* Enable receiving frames on the port, and activate auto-learning of
@ -482,8 +483,21 @@ static int ocelot_port_open(struct net_device *dev)
ANA_PORT_PORT_CFG_PORTID_VAL(port->chip_port), ANA_PORT_PORT_CFG_PORTID_VAL(port->chip_port),
ANA_PORT_PORT_CFG, port->chip_port); ANA_PORT_PORT_CFG, port->chip_port);
if (port->serdes) {
if (port->phy_mode == PHY_INTERFACE_MODE_SGMII)
phy_mode = PHY_MODE_SGMII;
else
phy_mode = PHY_MODE_QSGMII;
err = phy_set_mode(port->serdes, phy_mode);
if (err) {
netdev_err(dev, "Could not set mode of SerDes\n");
return err;
}
}
err = phy_connect_direct(dev, port->phy, &ocelot_port_adjust_link, err = phy_connect_direct(dev, port->phy, &ocelot_port_adjust_link,
PHY_INTERFACE_MODE_NA); port->phy_mode);
if (err) { if (err) {
netdev_err(dev, "Could not attach to PHY\n"); netdev_err(dev, "Could not attach to PHY\n");
return err; return err;

View File

@ -11,9 +11,10 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <soc/mscc/ocelot_hsio.h>
#include "ocelot_ana.h" #include "ocelot_ana.h"
#include "ocelot_dev.h" #include "ocelot_dev.h"
@ -454,6 +455,9 @@ struct ocelot_port {
u8 vlan_aware; u8 vlan_aware;
u64 *stats; u64 *stats;
phy_interface_t phy_mode;
struct phy *serdes;
}; };
u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset); u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);

View File

@ -6,6 +6,7 @@
*/ */
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_net.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
@ -253,18 +254,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ocelot->multicast); INIT_LIST_HEAD(&ocelot->multicast);
ocelot_init(ocelot); ocelot_init(ocelot);
ocelot_rmw(ocelot, HSIO_HW_CFG_DEV1G_4_MODE |
HSIO_HW_CFG_DEV1G_6_MODE |
HSIO_HW_CFG_DEV1G_9_MODE,
HSIO_HW_CFG_DEV1G_4_MODE |
HSIO_HW_CFG_DEV1G_6_MODE |
HSIO_HW_CFG_DEV1G_9_MODE,
HSIO_HW_CFG);
for_each_available_child_of_node(ports, portnp) { for_each_available_child_of_node(ports, portnp) {
struct device_node *phy_node; struct device_node *phy_node;
struct phy_device *phy; struct phy_device *phy;
struct resource *res; struct resource *res;
struct phy *serdes;
enum phy_mode phy_mode;
void __iomem *regs; void __iomem *regs;
char res_name[8]; char res_name[8];
u32 port; u32 port;
@ -289,10 +284,45 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
continue; continue;
err = ocelot_probe_port(ocelot, port, regs, phy); err = ocelot_probe_port(ocelot, port, regs, phy);
if (err) { if (err)
dev_err(&pdev->dev, "failed to probe ports\n"); return err;
err = of_get_phy_mode(portnp);
if (err < 0)
ocelot->ports[port]->phy_mode = PHY_INTERFACE_MODE_NA;
else
ocelot->ports[port]->phy_mode = err;
switch (ocelot->ports[port]->phy_mode) {
case PHY_INTERFACE_MODE_NA:
continue;
case PHY_INTERFACE_MODE_SGMII:
phy_mode = PHY_MODE_SGMII;
break;
case PHY_INTERFACE_MODE_QSGMII:
phy_mode = PHY_MODE_QSGMII;
break;
default:
dev_err(ocelot->dev,
"invalid phy mode for port%d, (Q)SGMII only\n",
port);
return -EINVAL;
}
serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
if (IS_ERR(serdes)) {
err = PTR_ERR(serdes);
if (err == -EPROBE_DEFER)
dev_dbg(ocelot->dev, "deferring probe\n");
else
dev_err(ocelot->dev,
"missing SerDes phys for port%d\n",
port);
goto err_probe_ports; goto err_probe_ports;
} }
ocelot->ports[port]->serdes = serdes;
} }
register_netdevice_notifier(&ocelot_netdevice_nb); register_netdevice_notifier(&ocelot_netdevice_nb);

View File

@ -5,6 +5,7 @@
* Copyright (c) 2017 Microsemi Corporation * Copyright (c) 2017 Microsemi Corporation
*/ */
#include "ocelot.h" #include "ocelot.h"
#include <soc/mscc/ocelot_hsio.h>
static const u32 ocelot_ana_regmap[] = { static const u32 ocelot_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x009000), REG(ANA_ADVLEARN, 0x009000),