Commit 4a84810e authored by David S. Miller's avatar David S. Miller

Merge branch 'mscc-ocelot-add-support-for-SerDes-muxing-configuration'

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 068b88cc 71e32a20
......@@ -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>;
};
......@@ -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
......@@ -45,7 +44,6 @@ Example:
reg = <0x1010000 0x10000>,
<0x1030000 0x10000>,
<0x1080000 0x100>,
<0x10d0000 0x10000>,
<0x11e0000 0x100>,
<0x11f0000 0x100>,
<0x1200000 0x100>,
......@@ -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";
......
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>;
};
};
......@@ -107,7 +107,6 @@ switch@1010000 {
reg = <0x1010000 0x10000>,
<0x1030000 0x10000>,
<0x1080000 0x100>,
<0x10d0000 0x10000>,
<0x11e0000 0x100>,
<0x11f0000 0x100>,
<0x1200000 0x100>,
......@@ -121,10 +120,10 @@ switch@1010000 {
<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";
......@@ -231,5 +230,15 @@ mdio1: mdio@10700c0 {
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>;
};
};
};
};
......@@ -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.
......
......@@ -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
......@@ -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;
......
......@@ -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"
......@@ -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 {
......@@ -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);
......
......@@ -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"
......@@ -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 {
......@@ -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)
......@@ -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;
......@@ -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;
......@@ -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);
......
......@@ -5,6 +5,7 @@
* Copyright (c) 2017 Microsemi Corporation
*/
#include "ocelot.h"
#include <soc/mscc/ocelot_hsio.h>
static const u32 ocelot_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x009000),
......@@ -102,82 +103,6 @@ static const u32 ocelot_qs_regmap[] = {
REG(QS_INH_DBG, 0x000048),
};
static const u32 ocelot_hsio_regmap[] = {
REG(HSIO_PLL5G_CFG0, 0x000000),
REG(HSIO_PLL5G_CFG1, 0x000004),
REG(HSIO_PLL5G_CFG2, 0x000008),
REG(HSIO_PLL5G_CFG3, 0x00000c),
REG(HSIO_PLL5G_CFG4, 0x000010),
REG(HSIO_PLL5G_CFG5, 0x000014),
REG(HSIO_PLL5G_CFG6, 0x000018),
REG(HSIO_PLL5G_STATUS0, 0x00001c),
REG(HSIO_PLL5G_STATUS1, 0x000020),
REG(HSIO_PLL5G_BIST_CFG0, 0x000024),
REG(HSIO_PLL5G_BIST_CFG1, 0x000028),
REG(HSIO_PLL5G_BIST_CFG2, 0x00002c),
REG(HSIO_PLL5G_BIST_STAT0, 0x000030),
REG(HSIO_PLL5G_BIST_STAT1, 0x000034),
REG(HSIO_RCOMP_CFG0, 0x000038),
REG(HSIO_RCOMP_STATUS, 0x00003c),
REG(HSIO_SYNC_ETH_CFG, 0x000040),
REG(HSIO_SYNC_ETH_PLL_CFG, 0x000048),
REG(HSIO_S1G_DES_CFG, 0x00004c),
REG(HSIO_S1G_IB_CFG, 0x000050),
REG(HSIO_S1G_OB_CFG, 0x000054),
REG(HSIO_S1G_SER_CFG, 0x000058),
REG(HSIO_S1G_COMMON_CFG, 0x00005c),
REG(HSIO_S1G_PLL_CFG, 0x000060),
REG(HSIO_S1G_PLL_STATUS, 0x000064),
REG(HSIO_S1G_DFT_CFG0, 0x000068),
REG(HSIO_S1G_DFT_CFG1, 0x00006c),
REG(HSIO_S1G_DFT_CFG2, 0x000070),
REG(HSIO_S1G_TP_CFG, 0x000074),
REG(HSIO_S1G_RC_PLL_BIST_CFG, 0x000078),
REG(HSIO_S1G_MISC_CFG, 0x00007c),
REG(HSIO_S1G_DFT_STATUS, 0x000080),
REG(HSIO_S1G_MISC_STATUS, 0x000084),
REG(HSIO_MCB_S1G_ADDR_CFG, 0x000088),
REG(HSIO_S6G_DIG_CFG, 0x00008c),
REG(HSIO_S6G_DFT_CFG0, 0x000090),
REG(HSIO_S6G_DFT_CFG1, 0x000094),
REG(HSIO_S6G_DFT_CFG2, 0x000098),
REG(HSIO_S6G_TP_CFG0, 0x00009c),
REG(HSIO_S6G_TP_CFG1, 0x0000a0),
REG(HSIO_S6G_RC_PLL_BIST_CFG, 0x0000a4),
REG(HSIO_S6G_MISC_CFG, 0x0000a8),
REG(HSIO_S6G_OB_ANEG_CFG, 0x0000ac),
REG(HSIO_S6G_DFT_STATUS, 0x0000b0),
REG(HSIO_S6G_ERR_CNT, 0x0000b4),
REG(HSIO_S6G_MISC_STATUS, 0x0000b8),
REG(HSIO_S6G_DES_CFG, 0x0000bc),
REG(HSIO_S6G_IB_CFG, 0x0000c0),
REG(HSIO_S6G_IB_CFG1, 0x0000c4),
REG(HSIO_S6G_IB_CFG2, 0x0000c8),
REG(HSIO_S6G_IB_CFG3, 0x0000cc),
REG(HSIO_S6G_IB_CFG4, 0x0000d0),
REG(HSIO_S6G_IB_CFG5, 0x0000d4),
REG(HSIO_S6G_OB_CFG, 0x0000d8),
REG(HSIO_S6G_OB_CFG1, 0x0000dc),
REG(HSIO_S6G_SER_CFG, 0x0000e0),
REG(HSIO_S6G_COMMON_CFG, 0x0000e4),
REG(HSIO_S6G_PLL_CFG, 0x0000e8),
REG(HSIO_S6G_ACJTAG_CFG, 0x0000ec),
REG(HSIO_S6G_GP_CFG, 0x0000f0),
REG(HSIO_S6G_IB_STATUS0, 0x0000f4),
REG(HSIO_S6G_IB_STATUS1, 0x0000f8),
REG(HSIO_S6G_ACJTAG_STATUS, 0x0000fc),
REG(HSIO_S6G_PLL_STATUS, 0x000100),
REG(HSIO_S6G_REVID, 0x000104),
REG(HSIO_MCB_S6G_ADDR_CFG, 0x000108),
REG(HSIO_HW_CFG, 0x00010c),
REG(HSIO_HW_QSGMII_CFG, 0x000110),
REG(HSIO_HW_QSGMII_STAT, 0x000114),
REG(HSIO_CLK_CFG, 0x000118),
REG(HSIO_TEMP_SENSOR_CTRL, 0x00011c),
REG(HSIO_TEMP_SENSOR_CFG, 0x000120),
REG(HSIO_TEMP_SENSOR_STAT, 0x000124),
};
static const u32 ocelot_qsys_regmap[] = {
REG(QSYS_PORT_MODE, 0x011200),
REG(QSYS_SWITCH_PORT_MODE, 0x011234),
......@@ -302,7 +227,6 @@ static const u32 ocelot_sys_regmap[] = {
static const u32 *ocelot_regmap[] = {
[ANA] = ocelot_ana_regmap,
[QS] = ocelot_qs_regmap,
[HSIO] = ocelot_hsio_regmap,
[QSYS] = ocelot_qsys_regmap,
[REW] = ocelot_rew_regmap,
[SYS] = ocelot_sys_regmap,
......@@ -453,9 +377,11 @@ static void ocelot_pll5_init(struct ocelot *ocelot)
/* Configure PLL5. This will need a proper CCF driver
* The values are coming from the VTSS API for Ocelot
*/
ocelot_write(ocelot, HSIO_PLL5G_CFG4_IB_CTRL(0x7600) |
HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8), HSIO_PLL5G_CFG4);
ocelot_write(ocelot, HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) |
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4,
HSIO_PLL5G_CFG4_IB_CTRL(0x7600) |
HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8));
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0,
HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) |
HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) |
HSIO_PLL5G_CFG0_ENA_BIAS |
HSIO_PLL5G_CFG0_ENA_VCO_BUF |
......@@ -465,13 +391,14 @@ static void ocelot_pll5_init(struct ocelot *ocelot)
HSIO_PLL5G_CFG0_SELBGV820(4) |
HSIO_PLL5G_CFG0_DIV4 |
HSIO_PLL5G_CFG0_ENA_CLKTREE |
HSIO_PLL5G_CFG0_ENA_LANE, HSIO_PLL5G_CFG0);
ocelot_write(ocelot, HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET |
HSIO_PLL5G_CFG0_ENA_LANE);
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2,
HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET |
HSIO_PLL5G_CFG2_EN_RESET_OVERRUN |
HSIO_PLL5G_CFG2_GAIN_TEST(0x8) |
HSIO_PLL5G_CFG2_ENA_AMPCTRL |
HSIO_PLL5G_CFG2_PWD_AMPCTRL_N |
HSIO_PLL5G_CFG2_AMPC_SEL(0x10), HSIO_PLL5G_CFG2);
HSIO_PLL5G_CFG2_AMPC_SEL(0x10));
}
int ocelot_chip_init(struct ocelot *ocelot)
......
......@@ -48,6 +48,7 @@ source "drivers/phy/lantiq/Kconfig"
source "drivers/phy/marvell/Kconfig"
source "drivers/phy/mediatek/Kconfig"
source "drivers/phy/motorola/Kconfig"
source "drivers/phy/mscc/Kconfig"
source "drivers/phy/qualcomm/Kconfig"
source "drivers/phy/ralink/Kconfig"
source "drivers/phy/renesas/Kconfig"
......
......@@ -18,6 +18,7 @@ obj-y += broadcom/ \
hisilicon/ \
marvell/ \
motorola/ \
mscc/ \
qualcomm/ \
ralink/ \
samsung/ \
......
#
# Phy drivers for Microsemi devices
#
config PHY_OCELOT_SERDES
tristate "SerDes PHY driver for Microsemi Ocelot"
select GENERIC_PHY
depends on OF
depends on MFD_SYSCON
help
Enable this for supporting SerDes muxing with Microsemi Ocelot.
#
# Makefile for the Microsemi phy drivers.
#
obj-$(CONFIG_PHY_OCELOT_SERDES) := phy-ocelot-serdes.o
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* SerDes PHY driver for Microsemi Ocelot
*
* Copyright (c) 2018 Microsemi
*
*/
#include <linux/err.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <soc/mscc/ocelot_hsio.h>
#include <dt-bindings/phy/phy-ocelot-serdes.h>
struct serdes_ctrl {
struct regmap *regs;
struct device *dev;
struct phy *phys[SERDES_MAX];
};
struct serdes_macro {
u8 idx;
/* Not used when in QSGMII or PCIe mode */
int port;
struct serdes_ctrl *ctrl;
};
#define MCB_S1G_CFG_TIMEOUT 50
static int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)
{
unsigned int regval;
regmap_write(regmap, HSIO_MCB_S1G_ADDR_CFG, op |
HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR(BIT(macro)));
return regmap_read_poll_timeout(regmap, HSIO_MCB_S1G_ADDR_CFG, regval,
(regval & op) != op, 100,
MCB_S1G_CFG_TIMEOUT * 1000);
}
static int serdes_commit_mcb_s1g(struct regmap *regmap, u8 macro)
{
return __serdes_write_mcb_s1g(regmap, macro,
HSIO_MCB_S1G_ADDR_CFG_SERDES1G_WR_ONE_SHOT);
}
static int serdes_update_mcb_s1g(struct regmap *regmap, u8 macro)
{
return __serdes_write_mcb_s1g(regmap, macro,
HSIO_MCB_S1G_ADDR_CFG_SERDES1G_RD_ONE_SHOT);
}
static int serdes_init_s1g(struct regmap *regmap, u8 serdes)
{
int ret;
ret = serdes_update_mcb_s1g(regmap, serdes);
if (ret)
return ret;
regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
HSIO_S1G_COMMON_CFG_SYS_RST |
HSIO_S1G_COMMON_CFG_ENA_LANE |
HSIO_S1G_COMMON_CFG_ENA_ELOOP |
HSIO_S1G_COMMON_CFG_ENA_FLOOP,
HSIO_S1G_COMMON_CFG_ENA_LANE);
regmap_update_bits(regmap, HSIO_S1G_PLL_CFG,
HSIO_S1G_PLL_CFG_PLL_FSM_ENA |
HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_M,
HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA(200) |
HSIO_S1G_PLL_CFG_PLL_FSM_ENA);
regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
HSIO_S1G_MISC_CFG_DES_100FX_CPMD_ENA |
HSIO_S1G_MISC_CFG_LANE_RST,
HSIO_S1G_MISC_CFG_LANE_RST);
ret = serdes_commit_mcb_s1g(regmap, serdes);
if (ret)
return ret;
regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
HSIO_S1G_COMMON_CFG_SYS_RST,
HSIO_S1G_COMMON_CFG_SYS_RST);
regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
HSIO_S1G_MISC_CFG_LANE_RST, 0);
ret = serdes_commit_mcb_s1g(regmap, serdes);
if (ret)
return ret;
return 0;
}
struct serdes_mux {
u8 idx;
u8 port;
enum phy_mode mode;
u32 mask;
u32 mux;
};
#define SERDES_MUX(_idx, _port, _mode, _mask, _mux) { \
.idx = _idx, \
.port = _port, \
.mode = _mode, \
.mask = _mask, \
.mux = _mux, \
}
#define SERDES_MUX_SGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_SGMII, m, c)
#define SERDES_MUX_QSGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_QSGMII, m, c)
static const struct serdes_mux ocelot_serdes_muxes[] = {
SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0),
SERDES_MUX_SGMII(SERDES1G(1), 1, HSIO_HW_CFG_DEV1G_5_MODE, 0),
SERDES_MUX_SGMII(SERDES1G(1), 5, HSIO_HW_CFG_QSGMII_ENA |
HSIO_HW_CFG_DEV1G_5_MODE, HSIO_HW_CFG_DEV1G_5_MODE),
SERDES_MUX_SGMII(SERDES1G(2), 2, HSIO_HW_CFG_DEV1G_4_MODE, 0),
SERDES_MUX_SGMII(SERDES1G(2), 4, HSIO_HW_CFG_QSGMII_ENA |
HSIO_HW_CFG_DEV1G_4_MODE, HSIO_HW_CFG_DEV1G_4_MODE),
SERDES_MUX_SGMII(SERDES1G(3), 3, HSIO_HW_CFG_DEV1G_6_MODE, 0),
SERDES_MUX_SGMII(SERDES1G(3), 6, HSIO_HW_CFG_QSGMII_ENA |
HSIO_HW_CFG_DEV1G_6_MODE, HSIO_HW_CFG_DEV1G_6_MODE),
SERDES_MUX_SGMII(SERDES1G(4), 4, HSIO_HW_CFG_QSGMII_ENA |
HSIO_HW_CFG_DEV1G_4_MODE | HSIO_HW_CFG_DEV1G_9_MODE,
0),
SERDES_MUX_SGMII(SERDES1G(4), 9, HSIO_HW_CFG_DEV1G_4_MODE |
HSIO_HW_CFG_DEV1G_9_MODE, HSIO_HW_CFG_DEV1G_4_MODE |
HSIO_HW_CFG_DEV1G_9_MODE),
SERDES_MUX_SGMII(SERDES1G(5), 5, HSIO_HW_CFG_QSGMII_ENA |
HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
0),
SERDES_MUX_SGMII(SERDES1G(5), 10, HSIO_HW_CFG_PCIE_ENA |
HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE),
SERDES_MUX_QSGMII(SERDES6G(0), 4, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA),
SERDES_MUX_QSGMII(SERDES6G(0), 5, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA),
SERDES_MUX_QSGMII(SERDES6G(0), 6, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA),
SERDES_MUX_SGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA, 0),
SERDES_MUX_QSGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA),
SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0),
SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA |
HSIO_HW_CFG_DEV2G5_10_MODE, 0),
SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, HSIO_HW_CFG_PCIE_ENA,
HSIO_HW_CFG_PCIE_ENA),
};
static int serdes_set_mode(struct phy *phy, enum phy_mode mode)
{
struct serdes_macro *macro = phy_get_drvdata(phy);
unsigned int i;
int ret;
for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) {
if (macro->idx != ocelot_serdes_muxes[i].idx ||
mode != ocelot_serdes_muxes[i].mode)
continue;
if (mode != PHY_MODE_QSGMII &&
macro->port != ocelot_serdes_muxes[i].port)
continue;
ret = regmap_update_bits(macro->ctrl->regs, HSIO_HW_CFG,
ocelot_serdes_muxes[i].mask,
ocelot_serdes_muxes[i].mux);
if (ret)
return ret;
if (macro->idx <= SERDES1G_MAX)
return serdes_init_s1g(macro->ctrl->regs, macro->idx);
/* SERDES6G and PCIe not supported yet */
return -EOPNOTSUPP;
}
return -EINVAL;
}
static const struct phy_ops serdes_ops = {
.set_mode = serdes_set_mode,
.owner = THIS_MODULE,
};
static struct phy *serdes_simple_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct serdes_ctrl *ctrl = dev_get_drvdata(dev);
unsigned int port, idx, i;
if (args->args_count != 2)
return ERR_PTR(-EINVAL);
port = args->args[0];
idx = args->args[1];
for (i = 0; i <= SERDES_MAX; i++) {
struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]);
if (idx != macro->idx)
continue;
/* SERDES6G(0) is the only SerDes capable of QSGMII */
if (idx != SERDES6G(0) && macro->port >= 0)
return ERR_PTR(-EBUSY);
macro->port = port;
return ctrl->phys[i];
}
return ERR_PTR(-ENODEV);
}
static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy)
{
struct serdes_macro *macro;
*phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops);
if (IS_ERR(*phy))
return PTR_ERR(*phy);
macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL);
if (!macro)
return -ENOMEM;
macro->idx = idx;
macro->ctrl = ctrl;
macro->port = -1;
phy_set_drvdata(*phy, macro);
return 0;
}
static int serdes_probe(struct platform_device *pdev)
{
struct phy_provider *provider;
struct serdes_ctrl *ctrl;
unsigned int i;
int ret;
ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return -ENOMEM;
ctrl->dev = &pdev->dev;
ctrl->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);
if (!ctrl->regs)
return -ENODEV;
for (i = 0; i <= SERDES_MAX; i++) {
ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]);
if (ret)
return ret;
}
dev_set_drvdata(&pdev->dev, ctrl);
provider = devm_of_phy_provider_register(ctrl->dev,
serdes_simple_xlate);
return PTR_ERR_OR_ZERO(provider);
}
static const struct of_device_id serdes_ids[] = {
{ .compatible = "mscc,vsc7514-serdes", },
{},
};
MODULE_DEVICE_TABLE(of, serdes_ids);
static struct platform_driver mscc_ocelot_serdes = {
.probe = serdes_probe,
.driver = {
.name = "mscc,ocelot-serdes",
.of_match_table = of_match_ptr(serdes_ids),
},
};
module_platform_driver(mscc_ocelot_serdes);
MODULE_AUTHOR("Quentin Schulz <quentin.schulz@bootlin.com>");
MODULE_DESCRIPTION("SerDes driver for Microsemi Ocelot");
MODULE_LICENSE("Dual MIT/GPL");
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/* Copyright (c) 2018 Microsemi Corporation */
#ifndef __PHY_OCELOT_SERDES_H__
#define __PHY_OCELOT_SERDES_H__
#define SERDES1G(x) (x)
#define SERDES1G_MAX SERDES1G(5)
#define SERDES6G(x) (SERDES1G_MAX + 1 + (x))
#define SERDES6G_MAX SERDES6G(2)
#define SERDES_MAX SERDES6G_MAX
#endif
......@@ -37,9 +37,11 @@ enum phy_mode {
PHY_MODE_USB_OTG,
PHY_MODE_SGMII,
PHY_MODE_2500SGMII,
PHY_MODE_QSGMII,
PHY_MODE_10GKR,
PHY_MODE_UFS_HS_A,
PHY_MODE_UFS_HS_B,
PHY_MODE_PCIE,
};
/**
......
......@@ -8,6 +8,80 @@
#ifndef _MSCC_OCELOT_HSIO_H_
#define _MSCC_OCELOT_HSIO_H_
#define HSIO_PLL5G_CFG0 0x0000
#define HSIO_PLL5G_CFG1 0x0004
#define HSIO_PLL5G_CFG2 0x0008
#define HSIO_PLL5G_CFG3 0x000c
#define HSIO_PLL5G_CFG4 0x0010
#define HSIO_PLL5G_CFG5 0x0014
#define HSIO_PLL5G_CFG6 0x0018
#define HSIO_PLL5G_STATUS0 0x001c
#define HSIO_PLL5G_STATUS1 0x0020
#define HSIO_PLL5G_BIST_CFG0 0x0024
#define HSIO_PLL5G_BIST_CFG1 0x0028
#define HSIO_PLL5G_BIST_CFG2 0x002c
#define HSIO_PLL5G_BIST_STAT0 0x0030
#define HSIO_PLL5G_BIST_STAT1 0x0034
#define HSIO_RCOMP_CFG0 0x0038
#define HSIO_RCOMP_STATUS 0x003c
#define HSIO_SYNC_ETH_CFG 0x0040
#define HSIO_SYNC_ETH_PLL_CFG 0x0048
#define HSIO_S1G_DES_CFG 0x004c
#define HSIO_S1G_IB_CFG 0x0050
#define HSIO_S1G_OB_CFG 0x0054
#define HSIO_S1G_SER_CFG 0x0058
#define HSIO_S1G_COMMON_CFG 0x005c
#define HSIO_S1G_PLL_CFG 0x0060
#define HSIO_S1G_PLL_STATUS 0x0064
#define HSIO_S1G_DFT_CFG0 0x0068
#define HSIO_S1G_DFT_CFG1 0x006c
#define HSIO_S1G_DFT_CFG2 0x0070
#define HSIO_S1G_TP_CFG 0x0074
#define HSIO_S1G_RC_PLL_BIST_CFG 0x0078
#define HSIO_S1G_MISC_CFG 0x007c
#define HSIO_S1G_DFT_STATUS 0x0080
#define HSIO_S1G_MISC_STATUS 0x0084
#define HSIO_MCB_S1G_ADDR_CFG 0x0088
#define HSIO_S6G_DIG_CFG 0x008c
#define HSIO_S6G_DFT_CFG0 0x0090
#define HSIO_S6G_DFT_CFG1 0x0094
#define HSIO_S6G_DFT_CFG2 0x0098
#define HSIO_S6G_TP_CFG0 0x009c
#define HSIO_S6G_TP_CFG1 0x00a0
#define HSIO_S6G_RC_PLL_BIST_CFG 0x00a4
#define HSIO_S6G_MISC_CFG 0x00a8
#define HSIO_S6G_OB_ANEG_CFG 0x00ac
#define HSIO_S6G_DFT_STATUS 0x00b0
#define HSIO_S6G_ERR_CNT 0x00b4
#define HSIO_S6G_MISC_STATUS 0x00b8
#define HSIO_S6G_DES_CFG 0x00bc
#define HSIO_S6G_IB_CFG 0x00c0
#define HSIO_S6G_IB_CFG1 0x00c4
#define HSIO_S6G_IB_CFG2 0x00c8
#define HSIO_S6G_IB_CFG3 0x00cc
#define HSIO_S6G_IB_CFG4 0x00d0
#define HSIO_S6G_IB_CFG5 0x00d4
#define HSIO_S6G_OB_CFG 0x00d8
#define HSIO_S6G_OB_CFG1 0x00dc
#define HSIO_S6G_SER_CFG 0x00e0
#define HSIO_S6G_COMMON_CFG 0x00e4
#define HSIO_S6G_PLL_CFG 0x00e8
#define HSIO_S6G_ACJTAG_CFG 0x00ec
#define HSIO_S6G_GP_CFG 0x00f0
#define HSIO_S6G_IB_STATUS0 0x00f4
#define HSIO_S6G_IB_STATUS1 0x00f8
#define HSIO_S6G_ACJTAG_STATUS 0x00fc
#define HSIO_S6G_PLL_STATUS 0x0100
#define HSIO_S6G_REVID 0x0104
#define HSIO_MCB_S6G_ADDR_CFG 0x0108
#define HSIO_HW_CFG 0x010c
#define HSIO_HW_QSGMII_CFG 0x0110
#define HSIO_HW_QSGMII_STAT 0x0114
#define HSIO_CLK_CFG 0x0118
#define HSIO_TEMP_SENSOR_CTRL 0x011c
#define HSIO_TEMP_SENSOR_CFG 0x0120
#define HSIO_TEMP_SENSOR_STAT 0x0124
#define HSIO_PLL5G_CFG0_ENA_ROT BIT(31)
#define HSIO_PLL5G_CFG0_ENA_LANE BIT(30)
#define HSIO_PLL5G_CFG0_ENA_CLKTREE BIT(29)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment