Skip to content

Commit

Permalink
Merge branch 'mscc-ocelot-add-support-for-SerDes-muxing-configuration'
Browse files Browse the repository at this point in the history
Quentin Schulz says:

====================
mscc: ocelot: add support for SerDes muxing configuration

The Ocelot switch has currently an hardcoded SerDes muxing that suits only
a particular use case. Any other board setup will fail to work.

To prepare for upcoming boards' support that do not have the same muxing,
create a PHY driver that will handle all possible cases.

A SerDes can work in SGMII, QSGMII or PCIe and is also muxed to use a
given port depending on the selected mode or board design.

The SerDes configuration is in the middle of an address space (HSIO) that
is used to configure some parts in the MAC controller driver, that is why
we need to use a syscon so that we can write to the same address space from
different drivers safely using regmap.

This breaks backward compatibility but it's fine because there's only one
board at the moment that is using what's modified in this patch series.
This will break git bisect.

Even though this patch series is about SerDes __muxing__ configuration, the
DT node is named serdes for the simple reason that I couldn't find any
mention to SerDes anywhere else from the address space handled by this
driver.

v4:
  - add reviewed-by,
  - format the patch series with -M for identifying renamed files,
  - add parent info in DT binding of the SerDes IP,
  - move to macros SERDES[16]G(X) instead of multiple SERDES[16]G_[012345]
  constants,
  - move to SERDES[16]G_MAX being the last VALID macro of a type, so
  migrate to <= conditions instead of < when iterating,
  - create a SERDES_MUX_SGMII and SERDES_MUX_QSGMII macro so the muxing
  configurations are a tad more readable,
  - use a bunch of unsigned int instead of int,
  - return -EOPNOTSUPP for SERDES6G/PCIe until it's supported,
  - simplify condition when there is an error code returned by
  devm_of_phy_get,

v3:
  - add Paul Burton's Acked-By on MIPS patches so that the patch series can
  be merged in the net tree in its entirety,

v2:
  - use a switch case for setting the phy_mode in the SerDes driver as
  suggested by Andrew,
  - stop replacing the value of the error pointer in the SerDes driver,
  - use a dev_dbg for the deferring of the probe in the SerDes driver,
  - use constants in the Device Tree to select the SerDes macro in use with
  a port,
  - adapt the SerDes driver to use those constants,
  - add a header file in include/dt-bindings for the constants,
  - fix space/tab issue,
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 5, 2018
2 parents 068b88c + 71e32a2 commit 4a84810
Show file tree
Hide file tree
Showing 17 changed files with 559 additions and 180 deletions.
16 changes: 16 additions & 0 deletions Documentation/devicetree/bindings/mips/mscc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,19 @@ Example:
compatible = "mscc,ocelot-cpu-syscon", "syscon";
reg = <0x70000000 0x2c>;
};

o HSIO regs:

The SoC has a few registers (HSIO) handling miscellaneous functionalities:
configuration and status of PLL5, RCOMP, SyncE, SerDes configurations and
status, SerDes muxing and a thermal sensor.

Required properties:
- compatible: Should be "mscc,ocelot-hsio", "syscon", "simple-mfd"
- reg : Should contain registers location and length

Example:
syscon@10d0000 {
compatible = "mscc,ocelot-hsio", "syscon", "simple-mfd";
reg = <0x10d0000 0x10000>;
};
9 changes: 3 additions & 6 deletions Documentation/devicetree/bindings/net/mscc-ocelot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Required properties:
- "sys"
- "rew"
- "qs"
- "hsio"
- "qsys"
- "ana"
- "portX" with X from 0 to the number of last port index available on that
Expand Down Expand Up @@ -45,7 +44,6 @@ Example:
reg = <0x1010000 0x10000>,
<0x1030000 0x10000>,
<0x1080000 0x100>,
<0x10d0000 0x10000>,
<0x11e0000 0x100>,
<0x11f0000 0x100>,
<0x1200000 0x100>,
Expand All @@ -59,10 +57,9 @@ Example:
<0x1280000 0x100>,
<0x1800000 0x80000>,
<0x1880000 0x10000>;
reg-names = "sys", "rew", "qs", "hsio", "port0",
"port1", "port2", "port3", "port4", "port5",
"port6", "port7", "port8", "port9", "port10",
"qsys", "ana";
reg-names = "sys", "rew", "qs", "port0", "port1", "port2",
"port3", "port4", "port5", "port6", "port7",
"port8", "port9", "port10", "qsys", "ana";
interrupts = <21 22>;
interrupt-names = "xtr", "inj";

Expand Down
43 changes: 43 additions & 0 deletions Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Microsemi Ocelot SerDes muxing driver
-------------------------------------

On Microsemi Ocelot, there is a handful of registers in HSIO address
space for setting up the SerDes to switch port muxing.

A SerDes X can be "muxed" to work with switch port Y or Z for example.
One specific SerDes can also be used as a PCIe interface.

Hence, a SerDes represents an interface, be it an Ethernet or a PCIe one.

There are two kinds of SerDes: SERDES1G supports 10/100Mbps in
half/full-duplex and 1000Mbps in full-duplex mode while SERDES6G supports
10/100Mbps in half/full-duplex and 1000/2500Mbps in full-duplex mode.

Also, SERDES6G number (aka "macro") 0 is the only interface supporting
QSGMII.

This is a child of the HSIO syscon ("mscc,ocelot-hsio", see
Documentation/devicetree/bindings/mips/mscc.txt) on the Microsemi Ocelot.

Required properties:

- compatible: should be "mscc,vsc7514-serdes"
- #phy-cells : from the generic phy bindings, must be 2.
The first number defines the input port to use for a given
SerDes macro. The second defines the macro to use. They are
defined in dt-bindings/phy/phy-ocelot-serdes.h

Example:

serdes: serdes {
compatible = "mscc,vsc7514-serdes";
#phy-cells = <2>;
};

ethernet {
port1 {
phy-handle = <&phy_foo>;
/* Link SERDES1G_5 to port1 */
phys = <&serdes 1 SERDES1G_5>;
};
};
19 changes: 14 additions & 5 deletions arch/mips/boot/dts/mscc/ocelot.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@
reg = <0x1010000 0x10000>,
<0x1030000 0x10000>,
<0x1080000 0x100>,
<0x10d0000 0x10000>,
<0x11e0000 0x100>,
<0x11f0000 0x100>,
<0x1200000 0x100>,
Expand All @@ -121,10 +120,10 @@
<0x1280000 0x100>,
<0x1800000 0x80000>,
<0x1880000 0x10000>;
reg-names = "sys", "rew", "qs", "hsio", "port0",
"port1", "port2", "port3", "port4", "port5",
"port6", "port7", "port8", "port9", "port10",
"qsys", "ana";
reg-names = "sys", "rew", "qs", "port0", "port1",
"port2", "port3", "port4", "port5", "port6",
"port7", "port8", "port9", "port10", "qsys",
"ana";
interrupts = <21 22>;
interrupt-names = "xtr", "inj";

Expand Down Expand Up @@ -231,5 +230,15 @@
pinctrl-0 = <&miim1>;
status = "disabled";
};

hsio: syscon@10d0000 {
compatible = "mscc,ocelot-hsio", "syscon", "simple-mfd";
reg = <0x10d0000 0x10000>;

serdes: serdes {
compatible = "mscc,vsc7514-serdes";
#phy-cells = <2>;
};
};
};
};
2 changes: 2 additions & 0 deletions drivers/net/ethernet/mscc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ config MSCC_OCELOT_SWITCH
config MSCC_OCELOT_SWITCH_OCELOT
tristate "Ocelot switch driver on Ocelot"
depends on MSCC_OCELOT_SWITCH
depends on GENERIC_PHY
depends on OF_NET
help
This driver supports the Ocelot network switch device as present on
the Ocelot SoCs.
Expand Down
16 changes: 15 additions & 1 deletion drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ static int ocelot_port_open(struct net_device *dev)
{
struct ocelot_port *port = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot;
enum phy_mode phy_mode;
int err;

/* Enable receiving frames on the port, and activate auto-learning of
Expand All @@ -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, 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,
PHY_INTERFACE_MODE_NA);
port->phy_mode);
if (err) {
netdev_err(dev, "Could not attach to PHY\n");
return err;
Expand Down
79 changes: 5 additions & 74 deletions drivers/net/ethernet/mscc/ocelot.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>

#include "ocelot_ana.h"
#include "ocelot_dev.h"
#include "ocelot_hsio.h"
#include "ocelot_qsys.h"
#include "ocelot_rew.h"
#include "ocelot_sys.h"
Expand Down Expand Up @@ -333,79 +334,6 @@ enum ocelot_reg {
SYS_CM_DATA_RD,
SYS_CM_OP,
SYS_CM_DATA,
HSIO_PLL5G_CFG0 = HSIO << TARGET_OFFSET,
HSIO_PLL5G_CFG1,
HSIO_PLL5G_CFG2,
HSIO_PLL5G_CFG3,
HSIO_PLL5G_CFG4,
HSIO_PLL5G_CFG5,
HSIO_PLL5G_CFG6,
HSIO_PLL5G_STATUS0,
HSIO_PLL5G_STATUS1,
HSIO_PLL5G_BIST_CFG0,
HSIO_PLL5G_BIST_CFG1,
HSIO_PLL5G_BIST_CFG2,
HSIO_PLL5G_BIST_STAT0,
HSIO_PLL5G_BIST_STAT1,
HSIO_RCOMP_CFG0,
HSIO_RCOMP_STATUS,
HSIO_SYNC_ETH_CFG,
HSIO_SYNC_ETH_PLL_CFG,
HSIO_S1G_DES_CFG,
HSIO_S1G_IB_CFG,
HSIO_S1G_OB_CFG,
HSIO_S1G_SER_CFG,
HSIO_S1G_COMMON_CFG,
HSIO_S1G_PLL_CFG,
HSIO_S1G_PLL_STATUS,
HSIO_S1G_DFT_CFG0,
HSIO_S1G_DFT_CFG1,
HSIO_S1G_DFT_CFG2,
HSIO_S1G_TP_CFG,
HSIO_S1G_RC_PLL_BIST_CFG,
HSIO_S1G_MISC_CFG,
HSIO_S1G_DFT_STATUS,
HSIO_S1G_MISC_STATUS,
HSIO_MCB_S1G_ADDR_CFG,
HSIO_S6G_DIG_CFG,
HSIO_S6G_DFT_CFG0,
HSIO_S6G_DFT_CFG1,
HSIO_S6G_DFT_CFG2,
HSIO_S6G_TP_CFG0,
HSIO_S6G_TP_CFG1,
HSIO_S6G_RC_PLL_BIST_CFG,
HSIO_S6G_MISC_CFG,
HSIO_S6G_OB_ANEG_CFG,
HSIO_S6G_DFT_STATUS,
HSIO_S6G_ERR_CNT,
HSIO_S6G_MISC_STATUS,
HSIO_S6G_DES_CFG,
HSIO_S6G_IB_CFG,
HSIO_S6G_IB_CFG1,
HSIO_S6G_IB_CFG2,
HSIO_S6G_IB_CFG3,
HSIO_S6G_IB_CFG4,
HSIO_S6G_IB_CFG5,
HSIO_S6G_OB_CFG,
HSIO_S6G_OB_CFG1,
HSIO_S6G_SER_CFG,
HSIO_S6G_COMMON_CFG,
HSIO_S6G_PLL_CFG,
HSIO_S6G_ACJTAG_CFG,
HSIO_S6G_GP_CFG,
HSIO_S6G_IB_STATUS0,
HSIO_S6G_IB_STATUS1,
HSIO_S6G_ACJTAG_STATUS,
HSIO_S6G_PLL_STATUS,
HSIO_S6G_REVID,
HSIO_MCB_S6G_ADDR_CFG,
HSIO_HW_CFG,
HSIO_HW_QSGMII_CFG,
HSIO_HW_QSGMII_STAT,
HSIO_CLK_CFG,
HSIO_TEMP_SENSOR_CTRL,
HSIO_TEMP_SENSOR_CFG,
HSIO_TEMP_SENSOR_STAT,
};

enum ocelot_regfield {
Expand Down Expand Up @@ -527,6 +455,9 @@ struct ocelot_port {
u8 vlan_aware;

u64 *stats;

phy_interface_t phy_mode;
struct phy *serdes;
};

u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
Expand Down
61 changes: 50 additions & 11 deletions drivers/net/ethernet/mscc/ocelot_board.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_net.h>
#include <linux/netdevice.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/mfd/syscon.h>
#include <linux/skbuff.h>

#include "ocelot.h"
Expand Down Expand Up @@ -168,6 +170,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct device_node *ports, *portnp;
struct ocelot *ocelot;
struct regmap *hsio;
u32 val;

struct {
Expand All @@ -179,7 +182,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
{ QSYS, "qsys" },
{ ANA, "ana" },
{ QS, "qs" },
{ HSIO, "hsio" },
};

if (!np && !pdev->dev.platform_data)
Expand All @@ -202,6 +204,14 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
ocelot->targets[res[i].id] = target;
}

hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio");
if (IS_ERR(hsio)) {
dev_err(&pdev->dev, "missing hsio syscon\n");
return PTR_ERR(hsio);
}

ocelot->targets[HSIO] = hsio;

err = ocelot_chip_init(ocelot);
if (err)
return err;
Expand Down Expand Up @@ -244,18 +254,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ocelot->multicast);
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) {
struct device_node *phy_node;
struct phy_device *phy;
struct resource *res;
struct phy *serdes;
enum phy_mode phy_mode;
void __iomem *regs;
char res_name[8];
u32 port;
Expand All @@ -280,10 +284,45 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
continue;

err = ocelot_probe_port(ocelot, port, regs, phy);
if (err) {
dev_err(&pdev->dev, "failed to probe ports\n");
if (err)
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;
}

ocelot->ports[port]->serdes = serdes;
}

register_netdevice_notifier(&ocelot_netdevice_nb);
Expand Down
Loading

0 comments on commit 4a84810

Please sign in to comment.