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:
parent
51f6b410fc
commit
71e32a20cf
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user