Add support for configuration and control of the VSC7511, VSC7512, VSC7513, and
VSC7514 chips over a SPI interface. The intent is to control these chips from an
external CPU. The expectation is to have most of the features of the
net/ethernet/mscc/ocelot_vsc7514 driver.
I have tried to heed all the advice from my first patch RFC. Thanks to everyone
for all the feedback.
The current status is that there are two functional "bugs" that need
investigation:
1. The first probe of the internal MDIO bus fails. I suspect this is related to
power supplies / grounding issues that would not appear on standard hardware.
2. Communication to the CPU bus doesn't seem to function properly. I suspect
this is due to the fact that ocelot / felix assumes it is using the built-in CPU
/ NPI port for forwarding, though I am not positive.
Nonetheless, these two issues likely won't require a large architecture change,
and perhaps those who know much more about the ocelot chips than I could chime
in.
Colin Foster (8):
net: dsa: ocelot: remove unnecessary pci_bar variables
net: dsa: ocelot: felix: move MDIO access to a common location
net: dsa: ocelot: felix: NULL check on variable
net: dsa: ocelot: felix: add interface for custom regmaps
net: mscc: ocelot: split register definitions to a separate file
net: mscc: ocelot: expose ocelot wm functions
net: dsa: ocelot: felix: add support for VSC75XX control over SPI
Update documentation for the VSC7512 SPI device
.../devicetree/bindings/net/dsa/ocelot.txt | 68 ++
drivers/net/dsa/ocelot/Kconfig | 12 +
drivers/net/dsa/ocelot/Makefile | 7 +
drivers/net/dsa/ocelot/felix.c | 6 +-
drivers/net/dsa/ocelot/felix.h | 4 +-
drivers/net/dsa/ocelot/felix_mdio.c | 145 +++
drivers/net/dsa/ocelot/felix_mdio.h | 11 +
drivers/net/dsa/ocelot/felix_vsc9959.c | 12 +-
drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c | 1068 +++++++++++++++++
drivers/net/dsa/ocelot/seville_vsc9953.c | 109 +-
drivers/net/ethernet/mscc/Makefile | 2 +
drivers/net/ethernet/mscc/ocelot.c | 8 +
drivers/net/ethernet/mscc/ocelot_regs.c | 310 +++++
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 323 +----
drivers/net/ethernet/mscc/ocelot_wm.c | 40 +
include/soc/mscc/ocelot.h | 24 +
include/soc/mscc/ocelot_regs.h | 21 +
17 files changed, 1737 insertions(+), 433 deletions(-)
create mode 100644 drivers/net/dsa/ocelot/felix_mdio.c
create mode 100644 drivers/net/dsa/ocelot/felix_mdio.h
create mode 100644 drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_regs.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_wm.c
create mode 100644 include/soc/mscc/ocelot_regs.h
--
2.25.1
Indirect MDIO access is a feature that doesn't need to be specific to the
Seville driver. Separate the feature to a common file so it can be shared.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/Makefile | 1 +
drivers/net/dsa/ocelot/felix_mdio.c | 145 +++++++++++++++++++++++
drivers/net/dsa/ocelot/felix_mdio.h | 11 ++
drivers/net/dsa/ocelot/seville_vsc9953.c | 108 ++---------------
4 files changed, 165 insertions(+), 100 deletions(-)
create mode 100644 drivers/net/dsa/ocelot/felix_mdio.c
create mode 100644 drivers/net/dsa/ocelot/felix_mdio.h
diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile
index f6dd131e7491..34b9b128efb8 100644
--- a/drivers/net/dsa/ocelot/Makefile
+++ b/drivers/net/dsa/ocelot/Makefile
@@ -8,4 +8,5 @@ mscc_felix-objs := \
mscc_seville-objs := \
felix.o \
+ felix_mdio.o \
seville_vsc9953.o
diff --git a/drivers/net/dsa/ocelot/felix_mdio.c b/drivers/net/dsa/ocelot/felix_mdio.c
new file mode 100644
index 000000000000..d58583311c9a
--- /dev/null
+++ b/drivers/net/dsa/ocelot/felix_mdio.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Distributed Switch Architecture VSC9953 driver
+ * Copyright (C) 2020, Maxim Kochetkov <[email protected]>
+ */
+#include <linux/types.h>
+#include <soc/mscc/ocelot.h>
+#include <linux/dsa/ocelot.h>
+#include <linux/iopoll.h>
+#include "felix.h"
+
+#define MSCC_MIIM_CMD_OPR_WRITE BIT(1)
+#define MSCC_MIIM_CMD_OPR_READ BIT(2)
+#define MSCC_MIIM_CMD_WRDATA_SHIFT 4
+#define MSCC_MIIM_CMD_REGAD_SHIFT 20
+#define MSCC_MIIM_CMD_PHYAD_SHIFT 25
+#define MSCC_MIIM_CMD_VLD BIT(31)
+
+#define FELIX_MDIO_MII_TIMEOUT 10000
+#define FELIX_MDIO_MII_RETRY 10
+
+static int felix_gcb_miim_pending_status(struct ocelot *ocelot)
+{
+ int val;
+
+ ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_PENDING, &val);
+
+ return val;
+}
+
+static int felix_gcb_miim_busy_status(struct ocelot *ocelot)
+{
+ int val;
+
+ ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_BUSY, &val);
+
+ return val;
+}
+
+static int felix_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
+ u16 value)
+{
+ struct ocelot *ocelot = bus->priv;
+ int err, cmd, val;
+
+ /* Wait while MIIM controller becomes idle */
+ err = readx_poll_timeout(felix_gcb_miim_pending_status, ocelot, val,
+ !val, FELIX_MDIO_MII_RETRY,
+ FELIX_MDIO_MII_TIMEOUT);
+ if (err) {
+ dev_err(ocelot->dev, "MDIO write: pending timeout\n");
+ goto out;
+ }
+
+ cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+ (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
+ (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
+ MSCC_MIIM_CMD_OPR_WRITE;
+
+ ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD);
+
+out:
+ return err;
+}
+
+static int felix_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct ocelot *ocelot = bus->priv;
+ int err, cmd, val;
+
+ /* Wait until MIIM controller becomes idle */
+ err = readx_poll_timeout(felix_gcb_miim_pending_status, ocelot, val,
+ !val, FELIX_MDIO_MII_RETRY,
+ FELIX_MDIO_MII_TIMEOUT);
+ if (err) {
+ dev_err(ocelot->dev, "MDIO read: pending timeout\n");
+ goto out;
+ }
+
+ /* Write the MIIM COMMAND register */
+ cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+ (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ;
+
+ ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD);
+
+ /* Wait while read operation via the MIIM controller is in progress */
+ err = readx_poll_timeout(felix_gcb_miim_busy_status, ocelot, val, !val,
+ FELIX_MDIO_MII_RETRY, FELIX_MDIO_MII_TIMEOUT);
+ if (err) {
+ dev_err(ocelot->dev, "MDIO read: busy timeout\n");
+ goto out;
+ }
+
+ val = ocelot_read(ocelot, GCB_MIIM_MII_DATA);
+
+ err = val & 0xFFFF;
+out:
+ return err;
+}
+
+int felix_mdio_register(struct ocelot *ocelot)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+ struct device *dev = ocelot->dev;
+ int rc;
+
+ /* Needed in order to initialize the bus mutex lock */
+ rc = mdiobus_register(felix->imdio);
+ if (rc < 0) {
+ dev_err(dev, "failed to register MDIO bus\n");
+ felix->imdio = NULL;
+ }
+
+ return rc;
+}
+
+int felix_mdio_bus_alloc(struct ocelot *ocelot)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+ struct device *dev = ocelot->dev;
+ struct mii_bus *bus;
+
+ bus = devm_mdiobus_alloc(dev);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->name = "Felix internal MDIO bus";
+ bus->read = felix_mdio_read;
+ bus->write = felix_mdio_write;
+ bus->parent = dev;
+ bus->priv = ocelot;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
+
+ felix->imdio = bus;
+
+ return 0;
+}
+
+void felix_mdio_bus_free(struct ocelot *ocelot)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+
+ if (felix->imdio)
+ mdiobus_unregister(felix->imdio);
+}
+
diff --git a/drivers/net/dsa/ocelot/felix_mdio.h b/drivers/net/dsa/ocelot/felix_mdio.h
new file mode 100644
index 000000000000..46fd41f605a9
--- /dev/null
+++ b/drivers/net/dsa/ocelot/felix_mdio.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Distributed Switch Architecture VSC9953 driver
+ * Copyright (C) 2020, Maxim Kochetkov <[email protected]>
+ */
+#include <linux/types.h>
+#include <soc/mscc/ocelot.h>
+
+int felix_mdio_bus_alloc(struct ocelot *ocelot);
+int felix_mdio_register(struct ocelot *ocelot);
+void felix_mdio_bus_free(struct ocelot *ocelot);
+
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 84f93a874d50..0e06750db264 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -11,13 +11,7 @@
#include <linux/dsa/ocelot.h>
#include <linux/iopoll.h>
#include "felix.h"
-
-#define MSCC_MIIM_CMD_OPR_WRITE BIT(1)
-#define MSCC_MIIM_CMD_OPR_READ BIT(2)
-#define MSCC_MIIM_CMD_WRDATA_SHIFT 4
-#define MSCC_MIIM_CMD_REGAD_SHIFT 20
-#define MSCC_MIIM_CMD_PHYAD_SHIFT 25
-#define MSCC_MIIM_CMD_VLD BIT(31)
+#include "felix_mdio.h"
static const u32 vsc9953_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x00b500),
@@ -857,7 +851,6 @@ static struct vcap_props vsc9953_vcap_props[] = {
#define VSC9953_INIT_TIMEOUT 50000
#define VSC9953_GCB_RST_SLEEP 100
#define VSC9953_SYS_RAMINIT_SLEEP 80
-#define VCS9953_MII_TIMEOUT 10000
static int vsc9953_gcb_soft_rst_status(struct ocelot *ocelot)
{
@@ -877,82 +870,6 @@ static int vsc9953_sys_ram_init_status(struct ocelot *ocelot)
return val;
}
-static int vsc9953_gcb_miim_pending_status(struct ocelot *ocelot)
-{
- int val;
-
- ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_PENDING, &val);
-
- return val;
-}
-
-static int vsc9953_gcb_miim_busy_status(struct ocelot *ocelot)
-{
- int val;
-
- ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_BUSY, &val);
-
- return val;
-}
-
-static int vsc9953_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
- u16 value)
-{
- struct ocelot *ocelot = bus->priv;
- int err, cmd, val;
-
- /* Wait while MIIM controller becomes idle */
- err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot,
- val, !val, 10, VCS9953_MII_TIMEOUT);
- if (err) {
- dev_err(ocelot->dev, "MDIO write: pending timeout\n");
- goto out;
- }
-
- cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
- (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
- (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
- MSCC_MIIM_CMD_OPR_WRITE;
-
- ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD);
-
-out:
- return err;
-}
-
-static int vsc9953_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
-{
- struct ocelot *ocelot = bus->priv;
- int err, cmd, val;
-
- /* Wait until MIIM controller becomes idle */
- err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot,
- val, !val, 10, VCS9953_MII_TIMEOUT);
- if (err) {
- dev_err(ocelot->dev, "MDIO read: pending timeout\n");
- goto out;
- }
-
- /* Write the MIIM COMMAND register */
- cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
- (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ;
-
- ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD);
-
- /* Wait while read operation via the MIIM controller is in progress */
- err = readx_poll_timeout(vsc9953_gcb_miim_busy_status, ocelot,
- val, !val, 10, VCS9953_MII_TIMEOUT);
- if (err) {
- dev_err(ocelot->dev, "MDIO read: busy timeout\n");
- goto out;
- }
-
- val = ocelot_read(ocelot, GCB_MIIM_MII_DATA);
-
- err = val & 0xFFFF;
-out:
- return err;
-}
/* CORE_ENA is in SYS:SYSTEM:RESET_CFG
* MEM_INIT is in SYS:SYSTEM:RESET_CFG
@@ -1086,7 +1003,6 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
{
struct felix *felix = ocelot_to_felix(ocelot);
struct device *dev = ocelot->dev;
- struct mii_bus *bus;
int port;
int rc;
@@ -1098,26 +1014,18 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
return -ENOMEM;
}
- bus = devm_mdiobus_alloc(dev);
- if (!bus)
- return -ENOMEM;
-
- bus->name = "VSC9953 internal MDIO bus";
- bus->read = vsc9953_mdio_read;
- bus->write = vsc9953_mdio_write;
- bus->parent = dev;
- bus->priv = ocelot;
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
+ rc = felix_mdio_bus_alloc(ocelot);
+ if (rc < 0) {
+ dev_err(dev, "failed to allocate MDIO bus\n");
+ return rc;
+ }
- /* Needed in order to initialize the bus mutex lock */
- rc = mdiobus_register(bus);
+ rc = felix_mdio_register(ocelot);
if (rc < 0) {
dev_err(dev, "failed to register MDIO bus\n");
return rc;
}
- felix->imdio = bus;
-
for (port = 0; port < felix->info->num_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port];
int addr = port + 4;
@@ -1162,7 +1070,7 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
mdio_device_free(pcs->mdio);
lynx_pcs_destroy(pcs);
}
- mdiobus_unregister(felix->imdio);
+ felix_mdio_bus_free(ocelot);
}
static const struct felix_info seville_info_vsc9953 = {
--
2.25.1
Moving these to a separate file will allow them to be shared to other
drivers.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/ethernet/mscc/Makefile | 1 +
drivers/net/ethernet/mscc/ocelot_regs.c | 310 +++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 295 +-------------------
include/soc/mscc/ocelot_regs.h | 21 ++
4 files changed, 333 insertions(+), 294 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_regs.c
create mode 100644 include/soc/mscc/ocelot_regs.h
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index 722c27694b21..d539a231a478 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -7,6 +7,7 @@ mscc_ocelot_switch_lib-y := \
ocelot_vcap.o \
ocelot_flower.o \
ocelot_ptp.o \
+ ocelot_regs.o \
ocelot_devlink.o
mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
diff --git a/drivers/net/ethernet/mscc/ocelot_regs.c b/drivers/net/ethernet/mscc/ocelot_regs.c
new file mode 100644
index 000000000000..6ded6c705f14
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_regs.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include "ocelot.h"
+
+const u32 ocelot_ana_regmap[] = {
+ REG(ANA_ADVLEARN, 0x009000),
+ REG(ANA_VLANMASK, 0x009004),
+ REG(ANA_PORT_B_DOMAIN, 0x009008),
+ REG(ANA_ANAGEFIL, 0x00900c),
+ REG(ANA_ANEVENTS, 0x009010),
+ REG(ANA_STORMLIMIT_BURST, 0x009014),
+ REG(ANA_STORMLIMIT_CFG, 0x009018),
+ REG(ANA_ISOLATED_PORTS, 0x009028),
+ REG(ANA_COMMUNITY_PORTS, 0x00902c),
+ REG(ANA_AUTOAGE, 0x009030),
+ REG(ANA_MACTOPTIONS, 0x009034),
+ REG(ANA_LEARNDISC, 0x009038),
+ REG(ANA_AGENCTRL, 0x00903c),
+ REG(ANA_MIRRORPORTS, 0x009040),
+ REG(ANA_EMIRRORPORTS, 0x009044),
+ REG(ANA_FLOODING, 0x009048),
+ REG(ANA_FLOODING_IPMC, 0x00904c),
+ REG(ANA_SFLOW_CFG, 0x009050),
+ REG(ANA_PORT_MODE, 0x009080),
+ REG(ANA_PGID_PGID, 0x008c00),
+ REG(ANA_TABLES_ANMOVED, 0x008b30),
+ REG(ANA_TABLES_MACHDATA, 0x008b34),
+ REG(ANA_TABLES_MACLDATA, 0x008b38),
+ REG(ANA_TABLES_MACACCESS, 0x008b3c),
+ REG(ANA_TABLES_MACTINDX, 0x008b40),
+ REG(ANA_TABLES_VLANACCESS, 0x008b44),
+ REG(ANA_TABLES_VLANTIDX, 0x008b48),
+ REG(ANA_TABLES_ISDXACCESS, 0x008b4c),
+ REG(ANA_TABLES_ISDXTIDX, 0x008b50),
+ REG(ANA_TABLES_ENTRYLIM, 0x008b00),
+ REG(ANA_TABLES_PTP_ID_HIGH, 0x008b54),
+ REG(ANA_TABLES_PTP_ID_LOW, 0x008b58),
+ REG(ANA_MSTI_STATE, 0x008e00),
+ REG(ANA_PORT_VLAN_CFG, 0x007000),
+ REG(ANA_PORT_DROP_CFG, 0x007004),
+ REG(ANA_PORT_QOS_CFG, 0x007008),
+ REG(ANA_PORT_VCAP_CFG, 0x00700c),
+ REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007010),
+ REG(ANA_PORT_VCAP_S2_CFG, 0x00701c),
+ REG(ANA_PORT_PCP_DEI_MAP, 0x007020),
+ REG(ANA_PORT_CPU_FWD_CFG, 0x007060),
+ REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007064),
+ REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007068),
+ REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00706c),
+ REG(ANA_PORT_PORT_CFG, 0x007070),
+ REG(ANA_PORT_POL_CFG, 0x007074),
+ REG(ANA_PORT_PTP_CFG, 0x007078),
+ REG(ANA_PORT_PTP_DLY1_CFG, 0x00707c),
+ REG(ANA_OAM_UPM_LM_CNT, 0x007c00),
+ REG(ANA_PORT_PTP_DLY2_CFG, 0x007080),
+ REG(ANA_PFC_PFC_CFG, 0x008800),
+ REG(ANA_PFC_PFC_TIMER, 0x008804),
+ REG(ANA_IPT_OAM_MEP_CFG, 0x008000),
+ REG(ANA_IPT_IPT, 0x008004),
+ REG(ANA_PPT_PPT, 0x008ac0),
+ REG(ANA_FID_MAP_FID_MAP, 0x000000),
+ REG(ANA_AGGR_CFG, 0x0090b4),
+ REG(ANA_CPUQ_CFG, 0x0090b8),
+ REG(ANA_CPUQ_CFG2, 0x0090bc),
+ REG(ANA_CPUQ_8021_CFG, 0x0090c0),
+ REG(ANA_DSCP_CFG, 0x009100),
+ REG(ANA_DSCP_REWR_CFG, 0x009200),
+ REG(ANA_VCAP_RNG_TYPE_CFG, 0x009240),
+ REG(ANA_VCAP_RNG_VAL_CFG, 0x009260),
+ REG(ANA_VRAP_CFG, 0x009280),
+ REG(ANA_VRAP_HDR_DATA, 0x009284),
+ REG(ANA_VRAP_HDR_MASK, 0x009288),
+ REG(ANA_DISCARD_CFG, 0x00928c),
+ REG(ANA_FID_CFG, 0x009290),
+ REG(ANA_POL_PIR_CFG, 0x004000),
+ REG(ANA_POL_CIR_CFG, 0x004004),
+ REG(ANA_POL_MODE_CFG, 0x004008),
+ REG(ANA_POL_PIR_STATE, 0x00400c),
+ REG(ANA_POL_CIR_STATE, 0x004010),
+ REG(ANA_POL_STATE, 0x004014),
+ REG(ANA_POL_FLOWC, 0x008b80),
+ REG(ANA_POL_HYST, 0x008bec),
+ REG(ANA_POL_MISC_CFG, 0x008bf0),
+};
+EXPORT_SYMBOL(ocelot_ana_regmap);
+
+const u32 ocelot_qs_regmap[] = {
+ REG(QS_XTR_GRP_CFG, 0x000000),
+ REG(QS_XTR_RD, 0x000008),
+ REG(QS_XTR_FRM_PRUNING, 0x000010),
+ REG(QS_XTR_FLUSH, 0x000018),
+ REG(QS_XTR_DATA_PRESENT, 0x00001c),
+ REG(QS_XTR_CFG, 0x000020),
+ REG(QS_INJ_GRP_CFG, 0x000024),
+ REG(QS_INJ_WR, 0x00002c),
+ REG(QS_INJ_CTRL, 0x000034),
+ REG(QS_INJ_STATUS, 0x00003c),
+ REG(QS_INJ_ERR, 0x000040),
+ REG(QS_INH_DBG, 0x000048),
+};
+EXPORT_SYMBOL(ocelot_qs_regmap);
+
+const u32 ocelot_qsys_regmap[] = {
+ REG(QSYS_PORT_MODE, 0x011200),
+ REG(QSYS_SWITCH_PORT_MODE, 0x011234),
+ REG(QSYS_STAT_CNT_CFG, 0x011264),
+ REG(QSYS_EEE_CFG, 0x011268),
+ REG(QSYS_EEE_THRES, 0x011294),
+ REG(QSYS_IGR_NO_SHARING, 0x011298),
+ REG(QSYS_EGR_NO_SHARING, 0x01129c),
+ REG(QSYS_SW_STATUS, 0x0112a0),
+ REG(QSYS_EXT_CPU_CFG, 0x0112d0),
+ REG(QSYS_PAD_CFG, 0x0112d4),
+ REG(QSYS_CPU_GROUP_MAP, 0x0112d8),
+ REG(QSYS_QMAP, 0x0112dc),
+ REG(QSYS_ISDX_SGRP, 0x011400),
+ REG(QSYS_TIMED_FRAME_ENTRY, 0x014000),
+ REG(QSYS_TFRM_MISC, 0x011310),
+ REG(QSYS_TFRM_PORT_DLY, 0x011314),
+ REG(QSYS_TFRM_TIMER_CFG_1, 0x011318),
+ REG(QSYS_TFRM_TIMER_CFG_2, 0x01131c),
+ REG(QSYS_TFRM_TIMER_CFG_3, 0x011320),
+ REG(QSYS_TFRM_TIMER_CFG_4, 0x011324),
+ REG(QSYS_TFRM_TIMER_CFG_5, 0x011328),
+ REG(QSYS_TFRM_TIMER_CFG_6, 0x01132c),
+ REG(QSYS_TFRM_TIMER_CFG_7, 0x011330),
+ REG(QSYS_TFRM_TIMER_CFG_8, 0x011334),
+ REG(QSYS_RED_PROFILE, 0x011338),
+ REG(QSYS_RES_QOS_MODE, 0x011378),
+ REG(QSYS_RES_CFG, 0x012000),
+ REG(QSYS_RES_STAT, 0x012004),
+ REG(QSYS_EGR_DROP_MODE, 0x01137c),
+ REG(QSYS_EQ_CTRL, 0x011380),
+ REG(QSYS_EVENTS_CORE, 0x011384),
+ REG(QSYS_CIR_CFG, 0x000000),
+ REG(QSYS_EIR_CFG, 0x000004),
+ REG(QSYS_SE_CFG, 0x000008),
+ REG(QSYS_SE_DWRR_CFG, 0x00000c),
+ REG(QSYS_SE_CONNECT, 0x00003c),
+ REG(QSYS_SE_DLB_SENSE, 0x000040),
+ REG(QSYS_CIR_STATE, 0x000044),
+ REG(QSYS_EIR_STATE, 0x000048),
+ REG(QSYS_SE_STATE, 0x00004c),
+ REG(QSYS_HSCH_MISC_CFG, 0x011388),
+};
+EXPORT_SYMBOL(ocelot_qsys_regmap);
+
+const u32 ocelot_rew_regmap[] = {
+ REG(REW_PORT_VLAN_CFG, 0x000000),
+ REG(REW_TAG_CFG, 0x000004),
+ REG(REW_PORT_CFG, 0x000008),
+ REG(REW_DSCP_CFG, 0x00000c),
+ REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010),
+ REG(REW_PTP_CFG, 0x000050),
+ REG(REW_PTP_DLY1_CFG, 0x000054),
+ REG(REW_DSCP_REMAP_DP1_CFG, 0x000690),
+ REG(REW_DSCP_REMAP_CFG, 0x000790),
+ REG(REW_STAT_CFG, 0x000890),
+ REG(REW_PPT, 0x000680),
+};
+EXPORT_SYMBOL(ocelot_rew_regmap);
+
+const u32 ocelot_sys_regmap[] = {
+ REG(SYS_COUNT_RX_OCTETS, 0x000000),
+ REG(SYS_COUNT_RX_UNICAST, 0x000004),
+ REG(SYS_COUNT_RX_MULTICAST, 0x000008),
+ REG(SYS_COUNT_RX_BROADCAST, 0x00000c),
+ REG(SYS_COUNT_RX_SHORTS, 0x000010),
+ REG(SYS_COUNT_RX_FRAGMENTS, 0x000014),
+ REG(SYS_COUNT_RX_JABBERS, 0x000018),
+ REG(SYS_COUNT_RX_CRC_ALIGN_ERRS, 0x00001c),
+ REG(SYS_COUNT_RX_SYM_ERRS, 0x000020),
+ REG(SYS_COUNT_RX_64, 0x000024),
+ REG(SYS_COUNT_RX_65_127, 0x000028),
+ REG(SYS_COUNT_RX_128_255, 0x00002c),
+ REG(SYS_COUNT_RX_256_1023, 0x000030),
+ REG(SYS_COUNT_RX_1024_1526, 0x000034),
+ REG(SYS_COUNT_RX_1527_MAX, 0x000038),
+ REG(SYS_COUNT_RX_PAUSE, 0x00003c),
+ REG(SYS_COUNT_RX_CONTROL, 0x000040),
+ REG(SYS_COUNT_RX_LONGS, 0x000044),
+ REG(SYS_COUNT_RX_CLASSIFIED_DROPS, 0x000048),
+ REG(SYS_COUNT_TX_OCTETS, 0x000100),
+ REG(SYS_COUNT_TX_UNICAST, 0x000104),
+ REG(SYS_COUNT_TX_MULTICAST, 0x000108),
+ REG(SYS_COUNT_TX_BROADCAST, 0x00010c),
+ REG(SYS_COUNT_TX_COLLISION, 0x000110),
+ REG(SYS_COUNT_TX_DROPS, 0x000114),
+ REG(SYS_COUNT_TX_PAUSE, 0x000118),
+ REG(SYS_COUNT_TX_64, 0x00011c),
+ REG(SYS_COUNT_TX_65_127, 0x000120),
+ REG(SYS_COUNT_TX_128_511, 0x000124),
+ REG(SYS_COUNT_TX_512_1023, 0x000128),
+ REG(SYS_COUNT_TX_1024_1526, 0x00012c),
+ REG(SYS_COUNT_TX_1527_MAX, 0x000130),
+ REG(SYS_COUNT_TX_AGING, 0x000170),
+ REG(SYS_RESET_CFG, 0x000508),
+ REG(SYS_CMID, 0x00050c),
+ REG(SYS_VLAN_ETYPE_CFG, 0x000510),
+ REG(SYS_PORT_MODE, 0x000514),
+ REG(SYS_FRONT_PORT_MODE, 0x000548),
+ REG(SYS_FRM_AGING, 0x000574),
+ REG(SYS_STAT_CFG, 0x000578),
+ REG(SYS_SW_STATUS, 0x00057c),
+ REG(SYS_MISC_CFG, 0x0005ac),
+ REG(SYS_REW_MAC_HIGH_CFG, 0x0005b0),
+ REG(SYS_REW_MAC_LOW_CFG, 0x0005dc),
+ REG(SYS_CM_ADDR, 0x000500),
+ REG(SYS_CM_DATA, 0x000504),
+ REG(SYS_PAUSE_CFG, 0x000608),
+ REG(SYS_PAUSE_TOT_CFG, 0x000638),
+ REG(SYS_ATOP, 0x00063c),
+ REG(SYS_ATOP_TOT_CFG, 0x00066c),
+ REG(SYS_MAC_FC_CFG, 0x000670),
+ REG(SYS_MMGT, 0x00069c),
+ REG(SYS_MMGT_FAST, 0x0006a0),
+ REG(SYS_EVENTS_DIF, 0x0006a4),
+ REG(SYS_EVENTS_CORE, 0x0006b4),
+ REG(SYS_CNT, 0x000000),
+ REG(SYS_PTP_STATUS, 0x0006b8),
+ REG(SYS_PTP_TXSTAMP, 0x0006bc),
+ REG(SYS_PTP_NXT, 0x0006c0),
+ REG(SYS_PTP_CFG, 0x0006c4),
+};
+EXPORT_SYMBOL(ocelot_sys_regmap);
+
+const u32 ocelot_vcap_regmap[] = {
+ /* VCAP_CORE_CFG */
+ REG(VCAP_CORE_UPDATE_CTRL, 0x000000),
+ REG(VCAP_CORE_MV_CFG, 0x000004),
+ /* VCAP_CORE_CACHE */
+ REG(VCAP_CACHE_ENTRY_DAT, 0x000008),
+ REG(VCAP_CACHE_MASK_DAT, 0x000108),
+ REG(VCAP_CACHE_ACTION_DAT, 0x000208),
+ REG(VCAP_CACHE_CNT_DAT, 0x000308),
+ REG(VCAP_CACHE_TG_DAT, 0x000388),
+ /* VCAP_CONST */
+ REG(VCAP_CONST_VCAP_VER, 0x000398),
+ REG(VCAP_CONST_ENTRY_WIDTH, 0x00039c),
+ REG(VCAP_CONST_ENTRY_CNT, 0x0003a0),
+ REG(VCAP_CONST_ENTRY_SWCNT, 0x0003a4),
+ REG(VCAP_CONST_ENTRY_TG_WIDTH, 0x0003a8),
+ REG(VCAP_CONST_ACTION_DEF_CNT, 0x0003ac),
+ REG(VCAP_CONST_ACTION_WIDTH, 0x0003b0),
+ REG(VCAP_CONST_CNT_WIDTH, 0x0003b4),
+ REG(VCAP_CONST_CORE_CNT, 0x0003b8),
+ REG(VCAP_CONST_IF_CNT, 0x0003bc),
+};
+EXPORT_SYMBOL(ocelot_vcap_regmap);
+
+const u32 ocelot_ptp_regmap[] = {
+ REG(PTP_PIN_CFG, 0x000000),
+ REG(PTP_PIN_TOD_SEC_MSB, 0x000004),
+ REG(PTP_PIN_TOD_SEC_LSB, 0x000008),
+ REG(PTP_PIN_TOD_NSEC, 0x00000c),
+ REG(PTP_PIN_WF_HIGH_PERIOD, 0x000014),
+ REG(PTP_PIN_WF_LOW_PERIOD, 0x000018),
+ REG(PTP_CFG_MISC, 0x0000a0),
+ REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4),
+ REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8),
+};
+EXPORT_SYMBOL(ocelot_ptp_regmap);
+
+const u32 ocelot_dev_gmii_regmap[] = {
+ REG(DEV_CLOCK_CFG, 0x0),
+ REG(DEV_PORT_MISC, 0x4),
+ REG(DEV_EVENTS, 0x8),
+ REG(DEV_EEE_CFG, 0xc),
+ REG(DEV_RX_PATH_DELAY, 0x10),
+ REG(DEV_TX_PATH_DELAY, 0x14),
+ REG(DEV_PTP_PREDICT_CFG, 0x18),
+ REG(DEV_MAC_ENA_CFG, 0x1c),
+ REG(DEV_MAC_MODE_CFG, 0x20),
+ REG(DEV_MAC_MAXLEN_CFG, 0x24),
+ REG(DEV_MAC_TAGS_CFG, 0x28),
+ REG(DEV_MAC_ADV_CHK_CFG, 0x2c),
+ REG(DEV_MAC_IFG_CFG, 0x30),
+ REG(DEV_MAC_HDX_CFG, 0x34),
+ REG(DEV_MAC_DBG_CFG, 0x38),
+ REG(DEV_MAC_FC_MAC_LOW_CFG, 0x3c),
+ REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x40),
+ REG(DEV_MAC_STICKY, 0x44),
+ REG(PCS1G_CFG, 0x48),
+ REG(PCS1G_MODE_CFG, 0x4c),
+ REG(PCS1G_SD_CFG, 0x50),
+ REG(PCS1G_ANEG_CFG, 0x54),
+ REG(PCS1G_ANEG_NP_CFG, 0x58),
+ REG(PCS1G_LB_CFG, 0x5c),
+ REG(PCS1G_DBG_CFG, 0x60),
+ REG(PCS1G_CDET_CFG, 0x64),
+ REG(PCS1G_ANEG_STATUS, 0x68),
+ REG(PCS1G_ANEG_NP_STATUS, 0x6c),
+ REG(PCS1G_LINK_STATUS, 0x70),
+ REG(PCS1G_LINK_DOWN_CNT, 0x74),
+ REG(PCS1G_STICKY, 0x78),
+ REG(PCS1G_DEBUG_STATUS, 0x7c),
+ REG(PCS1G_LPI_CFG, 0x80),
+ REG(PCS1G_LPI_WAKE_ERROR_CNT, 0x84),
+ REG(PCS1G_LPI_STATUS, 0x88),
+ REG(PCS1G_TSTPAT_MODE_CFG, 0x8c),
+ REG(PCS1G_TSTPAT_STATUS, 0x90),
+ REG(DEV_PCS_FX100_CFG, 0x94),
+ REG(DEV_PCS_FX100_STATUS, 0x98),
+};
+EXPORT_SYMBOL(ocelot_dev_gmii_regmap);
+
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 4bd7e9d9ec61..ef1bf24f51b5 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -16,303 +16,10 @@
#include <net/switchdev.h>
#include <soc/mscc/ocelot_vcap.h>
+#include <soc/mscc/ocelot_regs.h>
#include <soc/mscc/ocelot_hsio.h>
#include "ocelot.h"
-static const u32 ocelot_ana_regmap[] = {
- REG(ANA_ADVLEARN, 0x009000),
- REG(ANA_VLANMASK, 0x009004),
- REG(ANA_PORT_B_DOMAIN, 0x009008),
- REG(ANA_ANAGEFIL, 0x00900c),
- REG(ANA_ANEVENTS, 0x009010),
- REG(ANA_STORMLIMIT_BURST, 0x009014),
- REG(ANA_STORMLIMIT_CFG, 0x009018),
- REG(ANA_ISOLATED_PORTS, 0x009028),
- REG(ANA_COMMUNITY_PORTS, 0x00902c),
- REG(ANA_AUTOAGE, 0x009030),
- REG(ANA_MACTOPTIONS, 0x009034),
- REG(ANA_LEARNDISC, 0x009038),
- REG(ANA_AGENCTRL, 0x00903c),
- REG(ANA_MIRRORPORTS, 0x009040),
- REG(ANA_EMIRRORPORTS, 0x009044),
- REG(ANA_FLOODING, 0x009048),
- REG(ANA_FLOODING_IPMC, 0x00904c),
- REG(ANA_SFLOW_CFG, 0x009050),
- REG(ANA_PORT_MODE, 0x009080),
- REG(ANA_PGID_PGID, 0x008c00),
- REG(ANA_TABLES_ANMOVED, 0x008b30),
- REG(ANA_TABLES_MACHDATA, 0x008b34),
- REG(ANA_TABLES_MACLDATA, 0x008b38),
- REG(ANA_TABLES_MACACCESS, 0x008b3c),
- REG(ANA_TABLES_MACTINDX, 0x008b40),
- REG(ANA_TABLES_VLANACCESS, 0x008b44),
- REG(ANA_TABLES_VLANTIDX, 0x008b48),
- REG(ANA_TABLES_ISDXACCESS, 0x008b4c),
- REG(ANA_TABLES_ISDXTIDX, 0x008b50),
- REG(ANA_TABLES_ENTRYLIM, 0x008b00),
- REG(ANA_TABLES_PTP_ID_HIGH, 0x008b54),
- REG(ANA_TABLES_PTP_ID_LOW, 0x008b58),
- REG(ANA_MSTI_STATE, 0x008e00),
- REG(ANA_PORT_VLAN_CFG, 0x007000),
- REG(ANA_PORT_DROP_CFG, 0x007004),
- REG(ANA_PORT_QOS_CFG, 0x007008),
- REG(ANA_PORT_VCAP_CFG, 0x00700c),
- REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007010),
- REG(ANA_PORT_VCAP_S2_CFG, 0x00701c),
- REG(ANA_PORT_PCP_DEI_MAP, 0x007020),
- REG(ANA_PORT_CPU_FWD_CFG, 0x007060),
- REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007064),
- REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007068),
- REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00706c),
- REG(ANA_PORT_PORT_CFG, 0x007070),
- REG(ANA_PORT_POL_CFG, 0x007074),
- REG(ANA_PORT_PTP_CFG, 0x007078),
- REG(ANA_PORT_PTP_DLY1_CFG, 0x00707c),
- REG(ANA_OAM_UPM_LM_CNT, 0x007c00),
- REG(ANA_PORT_PTP_DLY2_CFG, 0x007080),
- REG(ANA_PFC_PFC_CFG, 0x008800),
- REG(ANA_PFC_PFC_TIMER, 0x008804),
- REG(ANA_IPT_OAM_MEP_CFG, 0x008000),
- REG(ANA_IPT_IPT, 0x008004),
- REG(ANA_PPT_PPT, 0x008ac0),
- REG(ANA_FID_MAP_FID_MAP, 0x000000),
- REG(ANA_AGGR_CFG, 0x0090b4),
- REG(ANA_CPUQ_CFG, 0x0090b8),
- REG(ANA_CPUQ_CFG2, 0x0090bc),
- REG(ANA_CPUQ_8021_CFG, 0x0090c0),
- REG(ANA_DSCP_CFG, 0x009100),
- REG(ANA_DSCP_REWR_CFG, 0x009200),
- REG(ANA_VCAP_RNG_TYPE_CFG, 0x009240),
- REG(ANA_VCAP_RNG_VAL_CFG, 0x009260),
- REG(ANA_VRAP_CFG, 0x009280),
- REG(ANA_VRAP_HDR_DATA, 0x009284),
- REG(ANA_VRAP_HDR_MASK, 0x009288),
- REG(ANA_DISCARD_CFG, 0x00928c),
- REG(ANA_FID_CFG, 0x009290),
- REG(ANA_POL_PIR_CFG, 0x004000),
- REG(ANA_POL_CIR_CFG, 0x004004),
- REG(ANA_POL_MODE_CFG, 0x004008),
- REG(ANA_POL_PIR_STATE, 0x00400c),
- REG(ANA_POL_CIR_STATE, 0x004010),
- REG(ANA_POL_STATE, 0x004014),
- REG(ANA_POL_FLOWC, 0x008b80),
- REG(ANA_POL_HYST, 0x008bec),
- REG(ANA_POL_MISC_CFG, 0x008bf0),
-};
-
-static const u32 ocelot_qs_regmap[] = {
- REG(QS_XTR_GRP_CFG, 0x000000),
- REG(QS_XTR_RD, 0x000008),
- REG(QS_XTR_FRM_PRUNING, 0x000010),
- REG(QS_XTR_FLUSH, 0x000018),
- REG(QS_XTR_DATA_PRESENT, 0x00001c),
- REG(QS_XTR_CFG, 0x000020),
- REG(QS_INJ_GRP_CFG, 0x000024),
- REG(QS_INJ_WR, 0x00002c),
- REG(QS_INJ_CTRL, 0x000034),
- REG(QS_INJ_STATUS, 0x00003c),
- REG(QS_INJ_ERR, 0x000040),
- REG(QS_INH_DBG, 0x000048),
-};
-
-static const u32 ocelot_qsys_regmap[] = {
- REG(QSYS_PORT_MODE, 0x011200),
- REG(QSYS_SWITCH_PORT_MODE, 0x011234),
- REG(QSYS_STAT_CNT_CFG, 0x011264),
- REG(QSYS_EEE_CFG, 0x011268),
- REG(QSYS_EEE_THRES, 0x011294),
- REG(QSYS_IGR_NO_SHARING, 0x011298),
- REG(QSYS_EGR_NO_SHARING, 0x01129c),
- REG(QSYS_SW_STATUS, 0x0112a0),
- REG(QSYS_EXT_CPU_CFG, 0x0112d0),
- REG(QSYS_PAD_CFG, 0x0112d4),
- REG(QSYS_CPU_GROUP_MAP, 0x0112d8),
- REG(QSYS_QMAP, 0x0112dc),
- REG(QSYS_ISDX_SGRP, 0x011400),
- REG(QSYS_TIMED_FRAME_ENTRY, 0x014000),
- REG(QSYS_TFRM_MISC, 0x011310),
- REG(QSYS_TFRM_PORT_DLY, 0x011314),
- REG(QSYS_TFRM_TIMER_CFG_1, 0x011318),
- REG(QSYS_TFRM_TIMER_CFG_2, 0x01131c),
- REG(QSYS_TFRM_TIMER_CFG_3, 0x011320),
- REG(QSYS_TFRM_TIMER_CFG_4, 0x011324),
- REG(QSYS_TFRM_TIMER_CFG_5, 0x011328),
- REG(QSYS_TFRM_TIMER_CFG_6, 0x01132c),
- REG(QSYS_TFRM_TIMER_CFG_7, 0x011330),
- REG(QSYS_TFRM_TIMER_CFG_8, 0x011334),
- REG(QSYS_RED_PROFILE, 0x011338),
- REG(QSYS_RES_QOS_MODE, 0x011378),
- REG(QSYS_RES_CFG, 0x012000),
- REG(QSYS_RES_STAT, 0x012004),
- REG(QSYS_EGR_DROP_MODE, 0x01137c),
- REG(QSYS_EQ_CTRL, 0x011380),
- REG(QSYS_EVENTS_CORE, 0x011384),
- REG(QSYS_CIR_CFG, 0x000000),
- REG(QSYS_EIR_CFG, 0x000004),
- REG(QSYS_SE_CFG, 0x000008),
- REG(QSYS_SE_DWRR_CFG, 0x00000c),
- REG(QSYS_SE_CONNECT, 0x00003c),
- REG(QSYS_SE_DLB_SENSE, 0x000040),
- REG(QSYS_CIR_STATE, 0x000044),
- REG(QSYS_EIR_STATE, 0x000048),
- REG(QSYS_SE_STATE, 0x00004c),
- REG(QSYS_HSCH_MISC_CFG, 0x011388),
-};
-
-static const u32 ocelot_rew_regmap[] = {
- REG(REW_PORT_VLAN_CFG, 0x000000),
- REG(REW_TAG_CFG, 0x000004),
- REG(REW_PORT_CFG, 0x000008),
- REG(REW_DSCP_CFG, 0x00000c),
- REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010),
- REG(REW_PTP_CFG, 0x000050),
- REG(REW_PTP_DLY1_CFG, 0x000054),
- REG(REW_DSCP_REMAP_DP1_CFG, 0x000690),
- REG(REW_DSCP_REMAP_CFG, 0x000790),
- REG(REW_STAT_CFG, 0x000890),
- REG(REW_PPT, 0x000680),
-};
-
-static const u32 ocelot_sys_regmap[] = {
- REG(SYS_COUNT_RX_OCTETS, 0x000000),
- REG(SYS_COUNT_RX_UNICAST, 0x000004),
- REG(SYS_COUNT_RX_MULTICAST, 0x000008),
- REG(SYS_COUNT_RX_BROADCAST, 0x00000c),
- REG(SYS_COUNT_RX_SHORTS, 0x000010),
- REG(SYS_COUNT_RX_FRAGMENTS, 0x000014),
- REG(SYS_COUNT_RX_JABBERS, 0x000018),
- REG(SYS_COUNT_RX_CRC_ALIGN_ERRS, 0x00001c),
- REG(SYS_COUNT_RX_SYM_ERRS, 0x000020),
- REG(SYS_COUNT_RX_64, 0x000024),
- REG(SYS_COUNT_RX_65_127, 0x000028),
- REG(SYS_COUNT_RX_128_255, 0x00002c),
- REG(SYS_COUNT_RX_256_1023, 0x000030),
- REG(SYS_COUNT_RX_1024_1526, 0x000034),
- REG(SYS_COUNT_RX_1527_MAX, 0x000038),
- REG(SYS_COUNT_RX_PAUSE, 0x00003c),
- REG(SYS_COUNT_RX_CONTROL, 0x000040),
- REG(SYS_COUNT_RX_LONGS, 0x000044),
- REG(SYS_COUNT_RX_CLASSIFIED_DROPS, 0x000048),
- REG(SYS_COUNT_TX_OCTETS, 0x000100),
- REG(SYS_COUNT_TX_UNICAST, 0x000104),
- REG(SYS_COUNT_TX_MULTICAST, 0x000108),
- REG(SYS_COUNT_TX_BROADCAST, 0x00010c),
- REG(SYS_COUNT_TX_COLLISION, 0x000110),
- REG(SYS_COUNT_TX_DROPS, 0x000114),
- REG(SYS_COUNT_TX_PAUSE, 0x000118),
- REG(SYS_COUNT_TX_64, 0x00011c),
- REG(SYS_COUNT_TX_65_127, 0x000120),
- REG(SYS_COUNT_TX_128_511, 0x000124),
- REG(SYS_COUNT_TX_512_1023, 0x000128),
- REG(SYS_COUNT_TX_1024_1526, 0x00012c),
- REG(SYS_COUNT_TX_1527_MAX, 0x000130),
- REG(SYS_COUNT_TX_AGING, 0x000170),
- REG(SYS_RESET_CFG, 0x000508),
- REG(SYS_CMID, 0x00050c),
- REG(SYS_VLAN_ETYPE_CFG, 0x000510),
- REG(SYS_PORT_MODE, 0x000514),
- REG(SYS_FRONT_PORT_MODE, 0x000548),
- REG(SYS_FRM_AGING, 0x000574),
- REG(SYS_STAT_CFG, 0x000578),
- REG(SYS_SW_STATUS, 0x00057c),
- REG(SYS_MISC_CFG, 0x0005ac),
- REG(SYS_REW_MAC_HIGH_CFG, 0x0005b0),
- REG(SYS_REW_MAC_LOW_CFG, 0x0005dc),
- REG(SYS_CM_ADDR, 0x000500),
- REG(SYS_CM_DATA, 0x000504),
- REG(SYS_PAUSE_CFG, 0x000608),
- REG(SYS_PAUSE_TOT_CFG, 0x000638),
- REG(SYS_ATOP, 0x00063c),
- REG(SYS_ATOP_TOT_CFG, 0x00066c),
- REG(SYS_MAC_FC_CFG, 0x000670),
- REG(SYS_MMGT, 0x00069c),
- REG(SYS_MMGT_FAST, 0x0006a0),
- REG(SYS_EVENTS_DIF, 0x0006a4),
- REG(SYS_EVENTS_CORE, 0x0006b4),
- REG(SYS_CNT, 0x000000),
- REG(SYS_PTP_STATUS, 0x0006b8),
- REG(SYS_PTP_TXSTAMP, 0x0006bc),
- REG(SYS_PTP_NXT, 0x0006c0),
- REG(SYS_PTP_CFG, 0x0006c4),
-};
-
-static const u32 ocelot_vcap_regmap[] = {
- /* VCAP_CORE_CFG */
- REG(VCAP_CORE_UPDATE_CTRL, 0x000000),
- REG(VCAP_CORE_MV_CFG, 0x000004),
- /* VCAP_CORE_CACHE */
- REG(VCAP_CACHE_ENTRY_DAT, 0x000008),
- REG(VCAP_CACHE_MASK_DAT, 0x000108),
- REG(VCAP_CACHE_ACTION_DAT, 0x000208),
- REG(VCAP_CACHE_CNT_DAT, 0x000308),
- REG(VCAP_CACHE_TG_DAT, 0x000388),
- /* VCAP_CONST */
- REG(VCAP_CONST_VCAP_VER, 0x000398),
- REG(VCAP_CONST_ENTRY_WIDTH, 0x00039c),
- REG(VCAP_CONST_ENTRY_CNT, 0x0003a0),
- REG(VCAP_CONST_ENTRY_SWCNT, 0x0003a4),
- REG(VCAP_CONST_ENTRY_TG_WIDTH, 0x0003a8),
- REG(VCAP_CONST_ACTION_DEF_CNT, 0x0003ac),
- REG(VCAP_CONST_ACTION_WIDTH, 0x0003b0),
- REG(VCAP_CONST_CNT_WIDTH, 0x0003b4),
- REG(VCAP_CONST_CORE_CNT, 0x0003b8),
- REG(VCAP_CONST_IF_CNT, 0x0003bc),
-};
-
-static const u32 ocelot_ptp_regmap[] = {
- REG(PTP_PIN_CFG, 0x000000),
- REG(PTP_PIN_TOD_SEC_MSB, 0x000004),
- REG(PTP_PIN_TOD_SEC_LSB, 0x000008),
- REG(PTP_PIN_TOD_NSEC, 0x00000c),
- REG(PTP_PIN_WF_HIGH_PERIOD, 0x000014),
- REG(PTP_PIN_WF_LOW_PERIOD, 0x000018),
- REG(PTP_CFG_MISC, 0x0000a0),
- REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4),
- REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8),
-};
-
-static const u32 ocelot_dev_gmii_regmap[] = {
- REG(DEV_CLOCK_CFG, 0x0),
- REG(DEV_PORT_MISC, 0x4),
- REG(DEV_EVENTS, 0x8),
- REG(DEV_EEE_CFG, 0xc),
- REG(DEV_RX_PATH_DELAY, 0x10),
- REG(DEV_TX_PATH_DELAY, 0x14),
- REG(DEV_PTP_PREDICT_CFG, 0x18),
- REG(DEV_MAC_ENA_CFG, 0x1c),
- REG(DEV_MAC_MODE_CFG, 0x20),
- REG(DEV_MAC_MAXLEN_CFG, 0x24),
- REG(DEV_MAC_TAGS_CFG, 0x28),
- REG(DEV_MAC_ADV_CHK_CFG, 0x2c),
- REG(DEV_MAC_IFG_CFG, 0x30),
- REG(DEV_MAC_HDX_CFG, 0x34),
- REG(DEV_MAC_DBG_CFG, 0x38),
- REG(DEV_MAC_FC_MAC_LOW_CFG, 0x3c),
- REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x40),
- REG(DEV_MAC_STICKY, 0x44),
- REG(PCS1G_CFG, 0x48),
- REG(PCS1G_MODE_CFG, 0x4c),
- REG(PCS1G_SD_CFG, 0x50),
- REG(PCS1G_ANEG_CFG, 0x54),
- REG(PCS1G_ANEG_NP_CFG, 0x58),
- REG(PCS1G_LB_CFG, 0x5c),
- REG(PCS1G_DBG_CFG, 0x60),
- REG(PCS1G_CDET_CFG, 0x64),
- REG(PCS1G_ANEG_STATUS, 0x68),
- REG(PCS1G_ANEG_NP_STATUS, 0x6c),
- REG(PCS1G_LINK_STATUS, 0x70),
- REG(PCS1G_LINK_DOWN_CNT, 0x74),
- REG(PCS1G_STICKY, 0x78),
- REG(PCS1G_DEBUG_STATUS, 0x7c),
- REG(PCS1G_LPI_CFG, 0x80),
- REG(PCS1G_LPI_WAKE_ERROR_CNT, 0x84),
- REG(PCS1G_LPI_STATUS, 0x88),
- REG(PCS1G_TSTPAT_MODE_CFG, 0x8c),
- REG(PCS1G_TSTPAT_STATUS, 0x90),
- REG(DEV_PCS_FX100_CFG, 0x94),
- REG(DEV_PCS_FX100_STATUS, 0x98),
-};
-
static const u32 *ocelot_regmap[TARGET_MAX] = {
[ANA] = ocelot_ana_regmap,
[QS] = ocelot_qs_regmap,
diff --git a/include/soc/mscc/ocelot_regs.h b/include/soc/mscc/ocelot_regs.h
new file mode 100644
index 000000000000..56e169818954
--- /dev/null
+++ b/include/soc/mscc/ocelot_regs.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2021 Innovative Advantage Inc.
+ */
+
+#ifndef OCELOT_REGS_H
+#define OCELOT_REGS_H
+
+extern const u32 ocelot_ana_regmap[];
+extern const u32 ocelot_qs_regmap[];
+extern const u32 ocelot_qsys_regmap[];
+extern const u32 ocelot_rew_regmap[];
+extern const u32 ocelot_sys_regmap[];
+extern const u32 ocelot_vcap_regmap[];
+extern const u32 ocelot_ptp_regmap[];
+extern const u32 ocelot_dev_gmii_regmap[];
+
+#endif
+
--
2.25.1
Add NULL check before dereferencing array
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/felix.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index ce607fbaaa3a..74ae322b2126 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -852,7 +852,7 @@ static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
struct felix *felix = ocelot_to_felix(ocelot);
struct dsa_port *dp = dsa_to_port(ds, port);
- if (felix->pcs[port])
+ if (felix->pcs && felix->pcs[port])
phylink_set_pcs(dp->pl, &felix->pcs[port]->pcs);
}
--
2.25.1
The pci_bar variables for the switch and imdio don't make sense for the
generic felix driver. Moving them to felix_vsc9959 to limit scope and
simplify the felix_info struct.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/felix.h | 2 --
drivers/net/dsa/ocelot/felix_vsc9959.c | 11 +++++------
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
index 4d96cad815d5..47769dd386db 100644
--- a/drivers/net/dsa/ocelot/felix.h
+++ b/drivers/net/dsa/ocelot/felix.h
@@ -20,8 +20,6 @@ struct felix_info {
int num_ports;
int num_tx_queues;
struct vcap_props *vcap;
- int switch_pci_bar;
- int imdio_pci_bar;
const struct ptp_clock_info *ptp_caps;
/* Some Ocelot switches are integrated into the SoC without the
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index f966a253d1c7..182ca749c8e2 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -1359,8 +1359,6 @@ static const struct felix_info felix_info_vsc9959 = {
.num_mact_rows = 2048,
.num_ports = 6,
.num_tx_queues = OCELOT_NUM_TC,
- .switch_pci_bar = 4,
- .imdio_pci_bar = 0,
.quirk_no_xtr_irq = true,
.ptp_caps = &vsc9959_ptp_caps,
.mdio_bus_alloc = vsc9959_mdio_bus_alloc,
@@ -1388,6 +1386,9 @@ static irqreturn_t felix_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
+#define VSC9959_SWITCH_PCI_BAR 4
+#define VSC9959_IMDIO_PCI_BAR 0
+
static int felix_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -1419,10 +1420,8 @@ static int felix_pci_probe(struct pci_dev *pdev,
ocelot->dev = &pdev->dev;
ocelot->num_flooding_pgids = OCELOT_NUM_TC;
felix->info = &felix_info_vsc9959;
- felix->switch_base = pci_resource_start(pdev,
- felix->info->switch_pci_bar);
- felix->imdio_base = pci_resource_start(pdev,
- felix->info->imdio_pci_bar);
+ felix->switch_base = pci_resource_start(pdev, VSC9959_SWITCH_PCI_BAR);
+ felix->imdio_base = pci_resource_start(pdev, VSC9959_IMDIO_PCI_BAR);
pci_set_master(pdev);
--
2.25.1
Signed-off-by: Colin Foster <[email protected]>
---
.../devicetree/bindings/net/dsa/ocelot.txt | 68 +++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/dsa/ocelot.txt b/Documentation/devicetree/bindings/net/dsa/ocelot.txt
index 7a271d070b72..f5d05bf8b093 100644
--- a/Documentation/devicetree/bindings/net/dsa/ocelot.txt
+++ b/Documentation/devicetree/bindings/net/dsa/ocelot.txt
@@ -8,6 +8,7 @@ Currently the switches supported by the felix driver are:
- VSC9959 (Felix)
- VSC9953 (Seville)
+- VSC7511, VSC7512, VSC7513, VSC7514 via SPI
The VSC9959 switch is found in the NXP LS1028A. It is a PCI device, part of the
larger ENETC root complex. As a result, the ethernet-switch node is a sub-node
@@ -211,3 +212,70 @@ Example:
};
};
};
+
+The VSC7513 and VSC7514 switches can be controlled internally via the MIPS
+processor. The VSC7511 and VSC7512 don't have this internal processor, but all
+four chips can be controlled externally through SPI with the following required
+properties:
+
+- compatible:
+ Can be "mscc,vsc7511", "mscc,vsc7512", "mscc,vsc7513", or
+ "mscc,vsc7514".
+
+Supported phy modes for all chips are:
+
+* phy_mode = "internal": on ports 0, 1, 2, 3
+
+Additionally, the VSC7512 and VSC7514 support SGMII and QSGMII on various ports,
+though that is currently untested.
+
+Example for control from a BeagleBone Black
+
+&spi0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ vsc7512: vsc7512@0 {
+ compatible = "mscc,vsc7512";
+ spi-max-frequency = <250000>;
+ reg = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ ethernet = <&mac>;
+ phy-mode = "internal";
+
+ fixed-link {
+ speed = <100>;
+ full-duplex;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "swp1";
+ status = "okay";
+ phy-mode = "internal";
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "swp2";
+ status = "okay";
+ phy-mode = "internal";
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "swp3";
+ status = "okay";
+ phy-mode = "internal";
+ };
+ };
+ };
+};
--
2.25.1
Add an interface so that non-mmio regmaps can be used
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/felix.c | 4 ++--
drivers/net/dsa/ocelot/felix.h | 2 ++
drivers/net/dsa/ocelot/felix_vsc9959.c | 1 +
drivers/net/dsa/ocelot/seville_vsc9953.c | 1 +
4 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 74ae322b2126..77644deb4a35 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -1124,7 +1124,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
res.start += felix->switch_base;
res.end += felix->switch_base;
- target = ocelot_regmap_init(ocelot, &res);
+ target = felix->info->init_regmap(ocelot, &res);
if (IS_ERR(target)) {
dev_err(ocelot->dev,
"Failed to map device memory space\n");
@@ -1161,7 +1161,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
res.start += felix->switch_base;
res.end += felix->switch_base;
- target = ocelot_regmap_init(ocelot, &res);
+ target = felix->info->init_regmap(ocelot, &res);
if (IS_ERR(target)) {
dev_err(ocelot->dev,
"Failed to map memory space for port %d\n",
diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
index 47769dd386db..25f664ef4947 100644
--- a/drivers/net/dsa/ocelot/felix.h
+++ b/drivers/net/dsa/ocelot/felix.h
@@ -45,6 +45,8 @@ struct felix_info {
enum tc_setup_type type, void *type_data);
void (*port_sched_speed_set)(struct ocelot *ocelot, int port,
u32 speed);
+ struct regmap *(*init_regmap)(struct ocelot *ocelot,
+ struct resource *res);
};
extern const struct dsa_switch_ops felix_switch_ops;
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 182ca749c8e2..31712a77f4b4 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -1367,6 +1367,7 @@ static const struct felix_info felix_info_vsc9959 = {
.prevalidate_phy_mode = vsc9959_prevalidate_phy_mode,
.port_setup_tc = vsc9959_port_setup_tc,
.port_sched_speed_set = vsc9959_sched_speed_set,
+ .init_regmap = ocelot_regmap_init,
};
static irqreturn_t felix_irq_handler(int irq, void *data)
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 0e06750db264..540cf5bc9c54 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1089,6 +1089,7 @@ static const struct felix_info seville_info_vsc9953 = {
.mdio_bus_free = vsc9953_mdio_bus_free,
.phylink_validate = vsc9953_phylink_validate,
.prevalidate_phy_mode = vsc9953_prevalidate_phy_mode,
+ .init_regmap = ocelot_regmap_init,
};
static int seville_probe(struct platform_device *pdev)
--
2.25.1
Utilize the Felix and Ocelot drivers to allow control of the VSC7511,
VSC7512, VSC7513 and VAC7514 chips from an external CPU over SPI.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/Kconfig | 12 +
drivers/net/dsa/ocelot/Makefile | 6 +
drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c | 1068 +++++++++++++++++++
drivers/net/ethernet/mscc/ocelot.c | 8 +
include/soc/mscc/ocelot.h | 19 +
5 files changed, 1113 insertions(+)
create mode 100644 drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c
diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig
index 932b6b6fe817..4528a0bb0a18 100644
--- a/drivers/net/dsa/ocelot/Kconfig
+++ b/drivers/net/dsa/ocelot/Kconfig
@@ -14,6 +14,18 @@ config NET_DSA_MSCC_FELIX
This driver supports the VSC9959 (Felix) switch, which is embedded as
a PCIe function of the NXP LS1028A ENETC RCiEP.
+config NET_DSA_MSCC_OCELOT_SPI
+ tristate "Ocelot Ethernet SPI switch support"
+ depends on NET_DSA && SPI
+ depends on NET_VENDOR_MICROSEMI
+ select MSCC_OCELOT_SWITCH_LIB
+ select NET_DSA_TAG_OCELOT_8021Q
+ select NET_DSA_TAG_OCELOT
+ help
+ This driver supports the VSC7511, VSC7512, VSC7513 and VSC7514 chips
+ when controlled through SPI. It can be used with the Microsemi dev
+ boards and an external CPU or custom hardware.
+
config NET_DSA_MSCC_SEVILLE
tristate "Ocelot / Seville Ethernet switch support"
depends on NET_DSA
diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile
index 34b9b128efb8..6ccd5482de7b 100644
--- a/drivers/net/dsa/ocelot/Makefile
+++ b/drivers/net/dsa/ocelot/Makefile
@@ -1,11 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NET_DSA_MSCC_FELIX) += mscc_felix.o
+obj-$(CONFIG_NET_DSA_MSCC_OCELOT_SPI) += mscc_ocelot_spi.o
obj-$(CONFIG_NET_DSA_MSCC_SEVILLE) += mscc_seville.o
mscc_felix-objs := \
felix.o \
felix_vsc9959.o
+mscc_ocelot_spi-objs := \
+ felix.o \
+ felix_mdio.o \
+ ocelot_vsc7512_spi.o
+
mscc_seville-objs := \
felix.o \
felix_mdio.o \
diff --git a/drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c b/drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c
new file mode 100644
index 000000000000..5ff028572a5a
--- /dev/null
+++ b/drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c
@@ -0,0 +1,1068 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Copyright 2017 Microsemi Corporation
+ * Copyright 2018-2019 NXP Semiconductors
+ * Copyright 2021 Innovative Advantage Inc.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/spi/spi.h>
+#include <linux/iopoll.h>
+#include <linux/kconfig.h>
+#include <linux/mdio.h>
+#include <soc/mscc/ocelot_qsys.h>
+#include <soc/mscc/ocelot_vcap.h>
+#include <soc/mscc/ocelot_ptp.h>
+#include <soc/mscc/ocelot_regs.h>
+#include <soc/mscc/ocelot_sys.h>
+#include <soc/mscc/ocelot.h>
+#include "felix.h"
+#include "felix_mdio.h"
+
+struct ocelot_spi_data {
+ int spi_padding_bytes;
+ struct felix felix;
+ struct spi_device *spi;
+};
+
+static const u32 vsc7512_dev_cpuorg_regmap[] = {
+ REG(DEV_CPUORG_IF_CTRL, 0x0000),
+ REG(DEV_CPUORG_IF_CFGSTAT, 0x0004),
+ REG(DEV_CPUORG_ORG_CFG, 0x0008),
+ REG(DEV_CPUORG_ERR_CNTS, 0x000c),
+ REG(DEV_CPUORG_TIMEOUT_CFG, 0x0010),
+ REG(DEV_CPUORG_GPR, 0x0014),
+ REG(DEV_CPUORG_MAILBOX_SET, 0x0018),
+ REG(DEV_CPUORG_MAILBOX_CLR, 0x001c),
+ REG(DEV_CPUORG_MAILBOX, 0x0020),
+ REG(DEV_CPUORG_SEMA_CFG, 0x0024),
+ REG(DEV_CPUORG_SEMA0, 0x0028),
+ REG(DEV_CPUORG_SEMA0_OWNER, 0x002c),
+ REG(DEV_CPUORG_SEMA1, 0x0030),
+ REG(DEV_CPUORG_SEMA1_OWNER, 0x0034),
+};
+
+static const u32 vsc7512_gcb_regmap[] = {
+ REG(GCB_SOFT_RST, 0x0008),
+ REG(GCB_MIIM_MII_STATUS, 0x009c),
+ REG(GCB_MIIM_MII_CMD, 0x00a4),
+ REG(GCB_MIIM_MII_DATA, 0x00a8),
+ REG(GCB_PHY_PHY_CFG, 0x00f0),
+ REG(GCB_PHY_PHY_STAT, 0x00f4),
+};
+
+static const u32 *vsc7512_regmap[TARGET_MAX] = {
+ [ANA] = ocelot_ana_regmap,
+ [QS] = ocelot_qs_regmap,
+ [QSYS] = ocelot_qsys_regmap,
+ [REW] = ocelot_rew_regmap,
+ [SYS] = ocelot_sys_regmap,
+ [S0] = ocelot_vcap_regmap,
+ [S1] = ocelot_vcap_regmap,
+ [S2] = ocelot_vcap_regmap,
+ [PTP] = ocelot_ptp_regmap,
+ [GCB] = vsc7512_gcb_regmap,
+ [DEV_GMII] = ocelot_dev_gmii_regmap,
+ [DEV_CPUORG] = vsc7512_dev_cpuorg_regmap,
+};
+
+#define VSC7512_BYTE_ORDER_LE 0x00000000
+#define VSC7512_BYTE_ORDER_BE 0x81818181
+#define VSC7512_BIT_ORDER_MSB 0x00000000
+#define VSC7512_BIT_ORDER_LSB 0x42424242
+
+static void ocelot_spi_reset_phys(struct ocelot *ocelot)
+{
+ ocelot_write(ocelot, 0x1ff, GCB_PHY_PHY_CFG);
+}
+
+struct ocelot_spi_data *felix_to_ocelot_spi(struct felix *felix)
+{
+ return container_of(felix, struct ocelot_spi_data, felix);
+}
+
+struct ocelot_spi_data *ocelot_to_ocelot_spi(struct ocelot *ocelot)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+
+ return felix_to_ocelot_spi(felix);
+}
+
+static int ocelot_spi_init_bus(struct ocelot *ocelot)
+{
+ struct ocelot_spi_data *ocelot_spi;
+ struct spi_device *spi;
+ u32 val, check;
+
+ ocelot_spi = ocelot_to_ocelot_spi(ocelot);
+ spi = ocelot_spi->spi;
+
+ val = 0;
+
+#ifdef __LITTLE_ENDIAN
+ val |= VSC7512_BYTE_ORDER_LE;
+#else
+ val |= VSC7512_BYTE_ORDER_BE;
+#endif
+
+ ocelot_write(ocelot, val, DEV_CPUORG_IF_CTRL);
+
+ val = ocelot_spi->spi_padding_bytes;
+ ocelot_write(ocelot, val, DEV_CPUORG_IF_CFGSTAT);
+
+ check = val | 0x02000000;
+
+ val = ocelot_read(ocelot, DEV_CPUORG_IF_CFGSTAT);
+ if (check != val) {
+ dev_err(&spi->dev,
+ "Error configuring SPI bus. V: 0x%08x != 0x%08x\n", val,
+ check);
+ return -ENODEV;
+ }
+
+ /* The internal copper phys need to be enabled before the mdio bus is
+ * scanned.
+ */
+ ocelot_spi_reset_phys(ocelot);
+
+ return 0;
+}
+
+static int vsc7512_reset(struct ocelot *ocelot)
+{
+ int retries = 100;
+ int ret, val;
+
+ ocelot_field_write(ocelot, GCB_SOFT_RST_CHIP_RST, 1);
+
+ /* Note: This is adapted from the PCIe reset strategy. The manual doesn't
+ * suggest how to do a reset over SPI, and the register strategy isn't
+ * possible.
+ */
+ msleep(100);
+
+ ret = ocelot_spi_init_bus(ocelot);
+ if (ret)
+ return ret;
+
+ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1);
+ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1);
+
+ do {
+ msleep(1);
+ regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT],
+ &val);
+ } while (val && --retries);
+
+ if (!retries)
+ return -ETIMEDOUT;
+
+ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1);
+
+ return 0;
+}
+
+static int vsc7512_spi_bus_init(struct ocelot *ocelot)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+ int rval;
+
+ rval = ocelot_spi_init_bus(ocelot);
+ if (rval) {
+ dev_err(ocelot->dev, "error initializing SPI bus\n");
+ goto clear_mdio;
+ }
+
+ rval = felix_mdio_register(ocelot);
+ if (rval)
+ dev_err(ocelot->dev, "error registering MDIO bus\n");
+
+ felix->ds->slave_mii_bus = felix->imdio;
+
+ return rval;
+
+clear_mdio:
+ felix->imdio = NULL;
+ return rval;
+}
+
+static const struct ocelot_ops vsc7512_ops = {
+ .bus_init = vsc7512_spi_bus_init,
+ .reset = vsc7512_reset,
+ .wm_enc = ocelot_wm_enc,
+ .wm_dec = ocelot_wm_dec,
+ .wm_stat = ocelot_wm_stat,
+ .port_to_netdev = felix_port_to_netdev,
+ .netdev_to_port = felix_netdev_to_port,
+};
+
+/* Addresses are relative to the SPI device's base address, downshifted by 2*/
+static const struct resource vsc7512_target_io_res[TARGET_MAX] = {
+ [ANA] = {
+ .start = 0x71880000,
+ .end = 0x7188ffff,
+ .name = "ana",
+ },
+ [QS] = {
+ .start = 0x71080000,
+ .end = 0x710800ff,
+ .name = "qs",
+ },
+ [QSYS] = {
+ .start = 0x71800000,
+ .end = 0x719fffff,
+ .name = "qsys",
+ },
+ [REW] = {
+ .start = 0x71030000,
+ .end = 0x7103ffff,
+ .name = "rew",
+ },
+ [SYS] = {
+ .start = 0x71010000,
+ .end = 0x7101ffff,
+ .name = "sys",
+ },
+ [S0] = {
+ .start = 0x71040000,
+ .end = 0x710403ff,
+ .name = "s0",
+ },
+ [S1] = {
+ .start = 0x71050000,
+ .end = 0x710503ff,
+ .name = "s1",
+ },
+ [S2] = {
+ .start = 0x71060000,
+ .end = 0x710603ff,
+ .name = "s2",
+ },
+ [GCB] = {
+ .start = 0x71070000,
+ .end = 0x710701ff,
+ .name = "devcpu_gcb",
+ },
+ [DEV_CPUORG] = {
+ .start = 0x71000000,
+ .end = 0x710003ff,
+ .name = "devcpu_org",
+ },
+};
+
+static const struct resource vsc7512_port_io_res[] = {
+ {
+ .start = 0x711e0000,
+ .end = 0x711effff,
+ .name = "port0",
+ },
+ {
+ .start = 0x711f0000,
+ .end = 0x711fffff,
+ .name = "port1",
+ },
+ {
+ .start = 0x71200000,
+ .end = 0x7120ffff,
+ .name = "port2",
+ },
+ {
+ .start = 0x71210000,
+ .end = 0x7121ffff,
+ .name = "port3",
+ },
+ {
+ .start = 0x71220000,
+ .end = 0x7122ffff,
+ .name = "port4",
+ },
+ {
+ .start = 0x71230000,
+ .end = 0x7123ffff,
+ .name = "port5",
+ },
+ {
+ .start = 0x71240000,
+ .end = 0x7124ffff,
+ .name = "port6",
+ },
+ {
+ .start = 0x71250000,
+ .end = 0x7125ffff,
+ .name = "port7",
+ },
+ {
+ .start = 0x71260000,
+ .end = 0x7126ffff,
+ .name = "port8",
+ },
+ {
+ .start = 0x71270000,
+ .end = 0x7127ffff,
+ .name = "port9",
+ },
+ {
+ .start = 0x71280000,
+ .end = 0x7128ffff,
+ .name = "port10",
+ },
+};
+
+static const struct reg_field vsc7512_regfields[REGFIELD_MAX] = {
+ [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11),
+ [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10),
+ [ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27),
+ [ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26),
+ [ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25),
+ [ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24),
+ [ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23),
+ [ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22),
+ [ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21),
+ [ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20),
+ [ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19),
+ [ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
+ [ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17),
+ [ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16),
+ [ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15),
+ [ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14),
+ [ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13),
+ [ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12),
+ [ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
+ [ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
+ [ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9),
+ [ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8),
+ [ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7),
+ [ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
+ [ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
+ [ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4),
+ [ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3),
+ [ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2),
+ [ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1),
+ [ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0),
+ [ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18),
+ [ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11),
+ [ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9),
+ [GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 1, 1),
+ [GCB_SOFT_RST_CHIP_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0),
+ [QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20),
+ [QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19),
+ [QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7),
+ [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3),
+ [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0),
+ [SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2),
+ [SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1),
+ [SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0),
+ /* Replicated per number of ports (12), register size 4 per port */
+ [QSYS_SWITCH_PORT_MODE_PORT_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 14, 14, 12, 4),
+ [QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 11, 13, 12, 4),
+ [QSYS_SWITCH_PORT_MODE_YEL_RSRVD] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 10, 10, 12, 4),
+ [QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 9, 9, 12, 4),
+ [QSYS_SWITCH_PORT_MODE_TX_PFC_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 1, 8, 12, 4),
+ [QSYS_SWITCH_PORT_MODE_TX_PFC_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 0, 0, 12, 4),
+ [SYS_PORT_MODE_DATA_WO_TS] = REG_FIELD_ID(SYS_PORT_MODE, 5, 6, 12, 4),
+ [SYS_PORT_MODE_INCL_INJ_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 3, 4, 12, 4),
+ [SYS_PORT_MODE_INCL_XTR_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 1, 2, 12, 4),
+ [SYS_PORT_MODE_INCL_HDR_ERR] = REG_FIELD_ID(SYS_PORT_MODE, 0, 0, 12, 4),
+ [SYS_PAUSE_CFG_PAUSE_START] = REG_FIELD_ID(SYS_PAUSE_CFG, 10, 18, 12, 4),
+ [SYS_PAUSE_CFG_PAUSE_STOP] = REG_FIELD_ID(SYS_PAUSE_CFG, 1, 9, 12, 4),
+ [SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 12, 4),
+ [GCB_MIIM_MII_STATUS_PENDING] = REG_FIELD(GCB_MIIM_MII_STATUS, 2, 2),
+ [GCB_MIIM_MII_STATUS_BUSY] = REG_FIELD(GCB_MIIM_MII_STATUS, 3, 3),
+};
+
+static const struct ocelot_stat_layout vsc7512_stats_layout[] = {
+ { .offset = 0x00, .name = "rx_octets", },
+ { .offset = 0x01, .name = "rx_unicast", },
+ { .offset = 0x02, .name = "rx_multicast", },
+ { .offset = 0x03, .name = "rx_broadcast", },
+ { .offset = 0x04, .name = "rx_shorts", },
+ { .offset = 0x05, .name = "rx_fragments", },
+ { .offset = 0x06, .name = "rx_jabbers", },
+ { .offset = 0x07, .name = "rx_crc_align_errs", },
+ { .offset = 0x08, .name = "rx_sym_errs", },
+ { .offset = 0x09, .name = "rx_frames_below_65_octets", },
+ { .offset = 0x0A, .name = "rx_frames_65_to_127_octets", },
+ { .offset = 0x0B, .name = "rx_frames_128_to_255_octets", },
+ { .offset = 0x0C, .name = "rx_frames_256_to_511_octets", },
+ { .offset = 0x0D, .name = "rx_frames_512_to_1023_octets", },
+ { .offset = 0x0E, .name = "rx_frames_1024_to_1526_octets", },
+ { .offset = 0x0F, .name = "rx_frames_over_1526_octets", },
+ { .offset = 0x10, .name = "rx_pause", },
+ { .offset = 0x11, .name = "rx_control", },
+ { .offset = 0x12, .name = "rx_longs", },
+ { .offset = 0x13, .name = "rx_classified_drops", },
+ { .offset = 0x14, .name = "rx_red_prio_0", },
+ { .offset = 0x15, .name = "rx_red_prio_1", },
+ { .offset = 0x16, .name = "rx_red_prio_2", },
+ { .offset = 0x17, .name = "rx_red_prio_3", },
+ { .offset = 0x18, .name = "rx_red_prio_4", },
+ { .offset = 0x19, .name = "rx_red_prio_5", },
+ { .offset = 0x1A, .name = "rx_red_prio_6", },
+ { .offset = 0x1B, .name = "rx_red_prio_7", },
+ { .offset = 0x1C, .name = "rx_yellow_prio_0", },
+ { .offset = 0x1D, .name = "rx_yellow_prio_1", },
+ { .offset = 0x1E, .name = "rx_yellow_prio_2", },
+ { .offset = 0x1F, .name = "rx_yellow_prio_3", },
+ { .offset = 0x20, .name = "rx_yellow_prio_4", },
+ { .offset = 0x21, .name = "rx_yellow_prio_5", },
+ { .offset = 0x22, .name = "rx_yellow_prio_6", },
+ { .offset = 0x23, .name = "rx_yellow_prio_7", },
+ { .offset = 0x24, .name = "rx_green_prio_0", },
+ { .offset = 0x25, .name = "rx_green_prio_1", },
+ { .offset = 0x26, .name = "rx_green_prio_2", },
+ { .offset = 0x27, .name = "rx_green_prio_3", },
+ { .offset = 0x28, .name = "rx_green_prio_4", },
+ { .offset = 0x29, .name = "rx_green_prio_5", },
+ { .offset = 0x2A, .name = "rx_green_prio_6", },
+ { .offset = 0x2B, .name = "rx_green_prio_7", },
+ { .offset = 0x40, .name = "tx_octets", },
+ { .offset = 0x41, .name = "tx_unicast", },
+ { .offset = 0x42, .name = "tx_multicast", },
+ { .offset = 0x43, .name = "tx_broadcast", },
+ { .offset = 0x44, .name = "tx_collision", },
+ { .offset = 0x45, .name = "tx_drops", },
+ { .offset = 0x46, .name = "tx_pause", },
+ { .offset = 0x47, .name = "tx_frames_below_65_octets", },
+ { .offset = 0x48, .name = "tx_frames_65_to_127_octets", },
+ { .offset = 0x49, .name = "tx_frames_128_255_octets", },
+ { .offset = 0x4A, .name = "tx_frames_256_511_octets", },
+ { .offset = 0x4B, .name = "tx_frames_512_1023_octets", },
+ { .offset = 0x4C, .name = "tx_frames_1024_1526_octets", },
+ { .offset = 0x4D, .name = "tx_frames_over_1526_octets", },
+ { .offset = 0x4E, .name = "tx_yellow_prio_0", },
+ { .offset = 0x4F, .name = "tx_yellow_prio_1", },
+ { .offset = 0x50, .name = "tx_yellow_prio_2", },
+ { .offset = 0x51, .name = "tx_yellow_prio_3", },
+ { .offset = 0x52, .name = "tx_yellow_prio_4", },
+ { .offset = 0x53, .name = "tx_yellow_prio_5", },
+ { .offset = 0x54, .name = "tx_yellow_prio_6", },
+ { .offset = 0x55, .name = "tx_yellow_prio_7", },
+ { .offset = 0x56, .name = "tx_green_prio_0", },
+ { .offset = 0x57, .name = "tx_green_prio_1", },
+ { .offset = 0x58, .name = "tx_green_prio_2", },
+ { .offset = 0x59, .name = "tx_green_prio_3", },
+ { .offset = 0x5A, .name = "tx_green_prio_4", },
+ { .offset = 0x5B, .name = "tx_green_prio_5", },
+ { .offset = 0x5C, .name = "tx_green_prio_6", },
+ { .offset = 0x5D, .name = "tx_green_prio_7", },
+ { .offset = 0x5E, .name = "tx_aged", },
+ { .offset = 0x80, .name = "drop_local", },
+ { .offset = 0x81, .name = "drop_tail", },
+ { .offset = 0x82, .name = "drop_yellow_prio_0", },
+ { .offset = 0x83, .name = "drop_yellow_prio_1", },
+ { .offset = 0x84, .name = "drop_yellow_prio_2", },
+ { .offset = 0x85, .name = "drop_yellow_prio_3", },
+ { .offset = 0x86, .name = "drop_yellow_prio_4", },
+ { .offset = 0x87, .name = "drop_yellow_prio_5", },
+ { .offset = 0x88, .name = "drop_yellow_prio_6", },
+ { .offset = 0x89, .name = "drop_yellow_prio_7", },
+ { .offset = 0x8A, .name = "drop_green_prio_0", },
+ { .offset = 0x8B, .name = "drop_green_prio_1", },
+ { .offset = 0x8C, .name = "drop_green_prio_2", },
+ { .offset = 0x8D, .name = "drop_green_prio_3", },
+ { .offset = 0x8E, .name = "drop_green_prio_4", },
+ { .offset = 0x8F, .name = "drop_green_prio_5", },
+ { .offset = 0x90, .name = "drop_green_prio_6", },
+ { .offset = 0x91, .name = "drop_green_prio_7", },
+};
+
+static unsigned int ocelot_spi_translate_address(unsigned int reg)
+{
+ return cpu_to_be32((reg & 0xffffff) >> 2);
+}
+
+struct ocelot_spi_regmap_context {
+ struct spi_device *spi;
+ u32 base;
+};
+
+static int ocelot_spi_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct ocelot_spi_regmap_context *regmap_context = context;
+ struct spi_transfer tx, padding, rx;
+ struct ocelot_spi_data *ocelot_spi;
+ struct spi_message msg;
+ struct spi_device *spi;
+ unsigned int addr;
+ u8 *tx_buf;
+
+ BUG_ON(!val);
+
+ spi = regmap_context->spi;
+
+ ocelot_spi = spi_get_drvdata(spi);
+
+ addr = ocelot_spi_translate_address(reg + regmap_context->base);
+ tx_buf = (u8 *)&addr;
+
+ spi_message_init(&msg);
+
+ memset(&tx, 0, sizeof(struct spi_transfer));
+
+ /* Ignore the first byte for the 24-bit address */
+ tx.tx_buf = &tx_buf[1];
+ tx.len = 3;
+
+ spi_message_add_tail(&tx, &msg);
+
+ if (ocelot_spi->spi_padding_bytes > 0) {
+ u8 dummy_buf[16] = {0};
+
+ memset(&padding, 0, sizeof(struct spi_transfer));
+
+ /* Just toggle the clock for padding bytes */
+ padding.len = ocelot_spi->spi_padding_bytes;
+ padding.tx_buf = dummy_buf;
+ padding.dummy_data = 1;
+
+ spi_message_add_tail(&padding, &msg);
+ }
+
+ memset(&rx, 0, sizeof(struct spi_transfer));
+ rx.rx_buf = val;
+ rx.len = 4;
+
+ spi_message_add_tail(&rx, &msg);
+
+ return spi_sync(spi, &msg);
+}
+
+static int ocelot_spi_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct ocelot_spi_regmap_context *regmap_context = context;
+ struct spi_transfer tx[2] = {0};
+ struct spi_message msg;
+ struct spi_device *spi;
+ unsigned int addr;
+ u8 *tx_buf;
+
+ spi = regmap_context->spi;
+
+ addr = ocelot_spi_translate_address(reg + regmap_context->base);
+ tx_buf = (u8 *)&addr;
+
+ spi_message_init(&msg);
+
+ /* Ignore the first byte for the 24-bit address and set the write bit */
+ tx_buf[1] |= BIT(7);
+ tx[0].tx_buf = &tx_buf[1];
+ tx[0].len = 3;
+
+ spi_message_add_tail(&tx[0], &msg);
+
+ memset(&tx[1], 0, sizeof(struct spi_transfer));
+ tx[1].tx_buf = &val;
+ tx[1].len = 4;
+
+ spi_message_add_tail(&tx[1], &msg);
+
+ return spi_sync(spi, &msg);
+}
+
+static const struct regmap_config ocelot_spi_regmap_config = {
+ .reg_bits = 24,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .reg_read = ocelot_spi_reg_read,
+ .reg_write = ocelot_spi_reg_write,
+
+ .max_register = 0xffffffff,
+ .use_single_write = true,
+ .use_single_read = true,
+ .can_multi_write = false,
+
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+};
+
+static void vsc7512_phylink_validate(struct ocelot *ocelot, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state)
+{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+ state->interface != ocelot_port->phy_mode) {
+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+ return;
+ }
+
+ phylink_set_port_modes(mask);
+ phylink_set(mask, Autoneg);
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+ phylink_set(mask, 10baseT_Half);
+ phylink_set(mask, 10baseT_Full);
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+ phylink_set(mask, 1000baseT_Half);
+ phylink_set(mask, 1000baseT_Full);
+
+ bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_and(state->advertising, state->advertising, mask,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static int vsc7512_prevalidate_phy_mode(struct ocelot *ocelot, int port,
+ phy_interface_t phy_mode)
+{
+ switch (phy_mode) {
+ case PHY_INTERFACE_MODE_INTERNAL:
+ if (port < 4)
+ return 0;
+ return -EOPNOTSUPP;
+ case PHY_INTERFACE_MODE_SGMII:
+ if (port < 8)
+ return 0;
+ return -EOPNOTSUPP;
+ case PHY_INTERFACE_MODE_QSGMII:
+ if (port == 7 || port == 8 || port == 10)
+ return 0;
+ return -EOPNOTSUPP;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int vsc7512_port_setup_tc(struct dsa_switch *ds, int port,
+ enum tc_setup_type type, void *type_data)
+{
+ return -EOPNOTSUPP;
+}
+
+static const struct vcap_field vsc7512_vcap_es0_keys[] = {
+ [VCAP_ES0_EGR_PORT] = { 0, 4 },
+ [VCAP_ES0_IGR_PORT] = { 4, 4 },
+ [VCAP_ES0_RSV] = { 8, 2 },
+ [VCAP_ES0_L2_MC] = { 10, 1 },
+ [VCAP_ES0_L2_BC] = { 11, 1 },
+ [VCAP_ES0_VID] = { 12, 12 },
+ [VCAP_ES0_DP] = { 24, 1 },
+ [VCAP_ES0_PCP] = { 25, 3 },
+};
+
+static const struct vcap_field vsc7512_vcap_es0_actions[] = {
+ [VCAP_ES0_ACT_PUSH_OUTER_TAG] = { 0, 2 },
+ [VCAP_ES0_ACT_PUSH_INNER_TAG] = { 2, 1 },
+ [VCAP_ES0_ACT_TAG_A_TPID_SEL] = { 3, 2 },
+ [VCAP_ES0_ACT_TAG_A_VID_SEL] = { 5, 1 },
+ [VCAP_ES0_ACT_TAG_A_PCP_SEL] = { 6, 2 },
+ [VCAP_ES0_ACT_TAG_A_DEI_SEL] = { 8, 2 },
+ [VCAP_ES0_ACT_TAG_B_TPID_SEL] = { 10, 2 },
+ [VCAP_ES0_ACT_TAG_B_VID_SEL] = { 12, 1 },
+ [VCAP_ES0_ACT_TAG_B_PCP_SEL] = { 13, 2 },
+ [VCAP_ES0_ACT_TAG_B_DEI_SEL] = { 15, 2 },
+ [VCAP_ES0_ACT_VID_A_VAL] = { 17, 12 },
+ [VCAP_ES0_ACT_PCP_A_VAL] = { 29, 3 },
+ [VCAP_ES0_ACT_DEI_A_VAL] = { 32, 1 },
+ [VCAP_ES0_ACT_VID_B_VAL] = { 33, 12 },
+ [VCAP_ES0_ACT_PCP_B_VAL] = { 45, 3 },
+ [VCAP_ES0_ACT_DEI_B_VAL] = { 48, 1 },
+ [VCAP_ES0_ACT_RSV] = { 49, 24 },
+ [VCAP_ES0_ACT_HIT_STICKY] = { 73, 1 },
+};
+
+static const struct vcap_field vsc7512_vcap_is1_keys[] = {
+ [VCAP_IS1_HK_TYPE] = { 0, 1 },
+ [VCAP_IS1_HK_LOOKUP] = { 1, 2 },
+ [VCAP_IS1_HK_IGR_PORT_MASK] = { 3, 12 },
+ [VCAP_IS1_HK_RSV] = { 15, 9 },
+ [VCAP_IS1_HK_OAM_Y1731] = { 24, 1 },
+ [VCAP_IS1_HK_L2_MC] = { 25, 1 },
+ [VCAP_IS1_HK_L2_BC] = { 26, 1 },
+ [VCAP_IS1_HK_IP_MC] = { 27, 1 },
+ [VCAP_IS1_HK_VLAN_TAGGED] = { 28, 1 },
+ [VCAP_IS1_HK_VLAN_DBL_TAGGED] = { 29, 1 },
+ [VCAP_IS1_HK_TPID] = { 30, 1 },
+ [VCAP_IS1_HK_VID] = { 31, 12 },
+ [VCAP_IS1_HK_DEI] = { 43, 1 },
+ [VCAP_IS1_HK_PCP] = { 44, 3 },
+ /* Specific Fields for IS1 Half Key S1_NORMAL */
+ [VCAP_IS1_HK_L2_SMAC] = { 47, 48 },
+ [VCAP_IS1_HK_ETYPE_LEN] = { 95, 1 },
+ [VCAP_IS1_HK_ETYPE] = { 96, 16 },
+ [VCAP_IS1_HK_IP_SNAP] = { 112, 1 },
+ [VCAP_IS1_HK_IP4] = { 113, 1 },
+ /* Layer-3 Information */
+ [VCAP_IS1_HK_L3_FRAGMENT] = { 114, 1 },
+ [VCAP_IS1_HK_L3_FRAG_OFS_GT0] = { 115, 1 },
+ [VCAP_IS1_HK_L3_OPTIONS] = { 116, 1 },
+ [VCAP_IS1_HK_L3_DSCP] = { 117, 6 },
+ [VCAP_IS1_HK_L3_IP4_SIP] = { 123, 32 },
+ /* Layer-4 Information */
+ [VCAP_IS1_HK_TCP_UDP] = { 155, 1 },
+ [VCAP_IS1_HK_TCP] = { 156, 1 },
+ [VCAP_IS1_HK_L4_SPORT] = { 157, 16 },
+ [VCAP_IS1_HK_L4_RNG] = { 173, 8 },
+ /* Specific Fields for IS1 Half Key S1_5TUPLE_IP4 */
+ [VCAP_IS1_HK_IP4_INNER_TPID] = { 47, 1 },
+ [VCAP_IS1_HK_IP4_INNER_VID] = { 48, 12 },
+ [VCAP_IS1_HK_IP4_INNER_DEI] = { 60, 1 },
+ [VCAP_IS1_HK_IP4_INNER_PCP] = { 61, 3 },
+ [VCAP_IS1_HK_IP4_IP4] = { 64, 1 },
+ [VCAP_IS1_HK_IP4_L3_FRAGMENT] = { 65, 1 },
+ [VCAP_IS1_HK_IP4_L3_FRAG_OFS_GT0] = { 66, 1 },
+ [VCAP_IS1_HK_IP4_L3_OPTIONS] = { 67, 1 },
+ [VCAP_IS1_HK_IP4_L3_DSCP] = { 68, 6 },
+ [VCAP_IS1_HK_IP4_L3_IP4_DIP] = { 74, 32 },
+ [VCAP_IS1_HK_IP4_L3_IP4_SIP] = { 106, 32 },
+ [VCAP_IS1_HK_IP4_L3_PROTO] = { 138, 8 },
+ [VCAP_IS1_HK_IP4_TCP_UDP] = { 146, 1 },
+ [VCAP_IS1_HK_IP4_TCP] = { 147, 1 },
+ [VCAP_IS1_HK_IP4_L4_RNG] = { 148, 8 },
+ [VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE] = { 156, 32 },
+};
+
+static const struct vcap_field vsc7512_vcap_is1_actions[] = {
+ [VCAP_IS1_ACT_DSCP_ENA] = { 0, 1 },
+ [VCAP_IS1_ACT_DSCP_VAL] = { 1, 6 },
+ [VCAP_IS1_ACT_QOS_ENA] = { 7, 1 },
+ [VCAP_IS1_ACT_QOS_VAL] = { 8, 3 },
+ [VCAP_IS1_ACT_DP_ENA] = { 11, 1 },
+ [VCAP_IS1_ACT_DP_VAL] = { 12, 1 },
+ [VCAP_IS1_ACT_PAG_OVERRIDE_MASK] = { 13, 8 },
+ [VCAP_IS1_ACT_PAG_VAL] = { 21, 8 },
+ [VCAP_IS1_ACT_RSV] = { 29, 9 },
+ /* The fields below are incorrectly shifted by 2 in the manual */
+ [VCAP_IS1_ACT_VID_REPLACE_ENA] = { 38, 1 },
+ [VCAP_IS1_ACT_VID_ADD_VAL] = { 39, 12 },
+ [VCAP_IS1_ACT_FID_SEL] = { 51, 2 },
+ [VCAP_IS1_ACT_FID_VAL] = { 53, 13 },
+ [VCAP_IS1_ACT_PCP_DEI_ENA] = { 66, 1 },
+ [VCAP_IS1_ACT_PCP_VAL] = { 67, 3 },
+ [VCAP_IS1_ACT_DEI_VAL] = { 70, 1 },
+ [VCAP_IS1_ACT_VLAN_POP_CNT_ENA] = { 71, 1 },
+ [VCAP_IS1_ACT_VLAN_POP_CNT] = { 72, 2 },
+ [VCAP_IS1_ACT_CUSTOM_ACE_TYPE_ENA] = { 74, 4 },
+ [VCAP_IS1_ACT_HIT_STICKY] = { 78, 1 },
+};
+
+static const struct vcap_field vsc7512_vcap_is2_keys[] = {
+ /* Common: 46 bits */
+ [VCAP_IS2_TYPE] = { 0, 4 },
+ [VCAP_IS2_HK_FIRST] = { 4, 1 },
+ [VCAP_IS2_HK_PAG] = { 5, 8 },
+ [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 12 },
+ [VCAP_IS2_HK_RSV2] = { 25, 1 },
+ [VCAP_IS2_HK_HOST_MATCH] = { 26, 1 },
+ [VCAP_IS2_HK_L2_MC] = { 27, 1 },
+ [VCAP_IS2_HK_L2_BC] = { 28, 1 },
+ [VCAP_IS2_HK_VLAN_TAGGED] = { 29, 1 },
+ [VCAP_IS2_HK_VID] = { 30, 12 },
+ [VCAP_IS2_HK_DEI] = { 42, 1 },
+ [VCAP_IS2_HK_PCP] = { 43, 3 },
+ /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */
+ [VCAP_IS2_HK_L2_DMAC] = { 46, 48 },
+ [VCAP_IS2_HK_L2_SMAC] = { 94, 48 },
+ /* MAC_ETYPE (TYPE=000) */
+ [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = { 142, 16 },
+ [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = { 158, 16 },
+ [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = { 174, 8 },
+ [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = { 182, 3 },
+ /* MAC_LLC (TYPE=001) */
+ [VCAP_IS2_HK_MAC_LLC_L2_LLC] = { 142, 40 },
+ /* MAC_SNAP (TYPE=010) */
+ [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = { 142, 40 },
+ /* MAC_ARP (TYPE=011) */
+ [VCAP_IS2_HK_MAC_ARP_SMAC] = { 46, 48 },
+ [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 94, 1 },
+ [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 95, 1 },
+ [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 96, 1 },
+ [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 97, 1 },
+ [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 98, 1 },
+ [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 99, 1 },
+ [VCAP_IS2_HK_MAC_ARP_OPCODE] = { 100, 2 },
+ [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = { 102, 32 },
+ [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = { 134, 32 },
+ [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = { 166, 1 },
+ /* IP4_TCP_UDP / IP4_OTHER common */
+ [VCAP_IS2_HK_IP4] = { 46, 1 },
+ [VCAP_IS2_HK_L3_FRAGMENT] = { 47, 1 },
+ [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 48, 1 },
+ [VCAP_IS2_HK_L3_OPTIONS] = { 49, 1 },
+ [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 50, 1 },
+ [VCAP_IS2_HK_L3_TOS] = { 51, 8 },
+ [VCAP_IS2_HK_L3_IP4_DIP] = { 59, 32 },
+ [VCAP_IS2_HK_L3_IP4_SIP] = { 91, 32 },
+ [VCAP_IS2_HK_DIP_EQ_SIP] = { 123, 1 },
+ /* IP4_TCP_UDP (TYPE=100) */
+ [VCAP_IS2_HK_TCP] = { 124, 1 },
+ [VCAP_IS2_HK_L4_DPORT] = { 125, 16 },
+ [VCAP_IS2_HK_L4_SPORT] = { 141, 16 },
+ [VCAP_IS2_HK_L4_RNG] = { 157, 8 },
+ [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = { 165, 1 },
+ [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = { 166, 1 },
+ [VCAP_IS2_HK_L4_FIN] = { 167, 1 },
+ [VCAP_IS2_HK_L4_SYN] = { 168, 1 },
+ [VCAP_IS2_HK_L4_RST] = { 169, 1 },
+ [VCAP_IS2_HK_L4_PSH] = { 170, 1 },
+ [VCAP_IS2_HK_L4_ACK] = { 171, 1 },
+ [VCAP_IS2_HK_L4_URG] = { 172, 1 },
+ [VCAP_IS2_HK_L4_1588_DOM] = { 173, 8 },
+ [VCAP_IS2_HK_L4_1588_VER] = { 181, 4 },
+ /* IP4_OTHER (TYPE=101) */
+ [VCAP_IS2_HK_IP4_L3_PROTO] = { 124, 8 },
+ [VCAP_IS2_HK_L3_PAYLOAD] = { 132, 56 },
+ /* IP6_STD (TYPE=110) */
+ [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 46, 1 },
+ [VCAP_IS2_HK_L3_IP6_SIP] = { 47, 128 },
+ [VCAP_IS2_HK_IP6_L3_PROTO] = { 175, 8 },
+ /* OAM (TYPE=111) */
+ [VCAP_IS2_HK_OAM_MEL_FLAGS] = { 142, 7 },
+ [VCAP_IS2_HK_OAM_VER] = { 149, 5 },
+ [VCAP_IS2_HK_OAM_OPCODE] = { 154, 8 },
+ [VCAP_IS2_HK_OAM_FLAGS] = { 162, 8 },
+ [VCAP_IS2_HK_OAM_MEPID] = { 170, 16 },
+ [VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = { 186, 1 },
+ [VCAP_IS2_HK_OAM_IS_Y1731] = { 187, 1 },
+};
+
+static const struct vcap_field vsc7512_vcap_is2_actions[] = {
+ [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1 },
+ [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1 },
+ [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3 },
+ [VCAP_IS2_ACT_MASK_MODE] = { 5, 2 },
+ [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1 },
+ [VCAP_IS2_ACT_LRN_DIS] = { 8, 1 },
+ [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1 },
+ [VCAP_IS2_ACT_POLICE_IDX] = { 10, 9 },
+ [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1 },
+ [VCAP_IS2_ACT_PORT_MASK] = { 20, 11 },
+ [VCAP_IS2_ACT_REW_OP] = { 31, 9 },
+ [VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 40, 1 },
+ [VCAP_IS2_ACT_RSV] = { 41, 2 },
+ [VCAP_IS2_ACT_ACL_ID] = { 43, 6 },
+ [VCAP_IS2_ACT_HIT_CNT] = { 49, 32 },
+};
+
+static struct vcap_props vsc7512_vcap_props[] = {
+ [VCAP_ES0] = {
+ .action_type_width = 0,
+ .action_table = {
+ [ES0_ACTION_TYPE_NORMAL] = {
+ .width = 73,
+ .count = 1,
+ },
+ },
+ .target = S0,
+ .keys = vsc7512_vcap_es0_keys,
+ .actions = vsc7512_vcap_es0_actions,
+ },
+ [VCAP_IS1] = {
+ .action_type_width = 0,
+ .action_table = {
+ [IS1_ACTION_TYPE_NORMAL] = {
+ .width = 78,
+ .count = 4,
+ },
+ },
+ .target = S1,
+ .keys = vsc7512_vcap_is1_keys,
+ .actions = vsc7512_vcap_is1_actions,
+ },
+ [VCAP_IS2] = {
+ .action_type_width = 1,
+ .action_table = {
+ [IS2_ACTION_TYPE_NORMAL] = {
+ .width = 49,
+ .count = 2,
+ },
+ [IS2_ACTION_TYPE_SMAC_SIP] = {
+ .width = 6,
+ .count = 4,
+ },
+ },
+ .target = S2,
+ .keys = vsc7512_vcap_is2_keys,
+ .actions = vsc7512_vcap_is2_actions,
+ },
+};
+
+static struct regmap *vsc7512_regmap_init(struct ocelot *ocelot,
+ struct resource *res)
+{
+ struct ocelot_spi_regmap_context *context;
+ struct regmap_config regmap_config;
+ struct ocelot_spi_data *ocelot_spi;
+ struct regmap *regmap;
+ struct device *dev;
+ char name[32];
+
+ ocelot_spi = ocelot_to_ocelot_spi(ocelot);
+ dev = &ocelot_spi->spi->dev;
+
+ context = devm_kzalloc(dev, sizeof(struct ocelot_spi_regmap_context),
+ GFP_KERNEL);
+
+ if (IS_ERR(context))
+ return ERR_CAST(context);
+
+ context->base = res->start;
+ context->spi = ocelot_spi->spi;
+
+ memcpy(®map_config, &ocelot_spi_regmap_config,
+ sizeof(ocelot_spi_regmap_config));
+
+ /* A unique bus name is required for each regmap */
+ if (res->name)
+ snprintf(name, sizeof(name) - 1, "ocelot_spi-%s", res->name);
+ else
+ snprintf(name, sizeof(name) - 1, "ocelot_spi@0x%08x",
+ res->start);
+
+ regmap_config.name = name;
+ regmap_config.max_register = res->end - res->start;
+
+ regmap = devm_regmap_init(dev, NULL, context, ®map_config);
+
+ if (IS_ERR(regmap))
+ return ERR_CAST(regmap);
+
+ return regmap;
+}
+
+static const struct felix_info ocelot_spi_info = {
+ .target_io_res = vsc7512_target_io_res,
+ .port_io_res = vsc7512_port_io_res,
+ .regfields = vsc7512_regfields,
+ .map = vsc7512_regmap,
+ .ops = &vsc7512_ops,
+ .stats_layout = vsc7512_stats_layout,
+ .num_stats = ARRAY_SIZE(vsc7512_stats_layout),
+ .vcap = vsc7512_vcap_props,
+ .num_mact_rows = 1024,
+
+ /* The 7512 and 7514 both have support for up to 10 ports. The 7511 and
+ * 7513 have support for 4. Due to lack of hardware to test and
+ * validate external phys, this is currently limited to 4 ports.
+ * Expanding this to 10 for the 7512 and 7514 and defining the
+ * appropriate phy-handle values in the device tree should be possible.
+ */
+ .num_ports = 4,
+ .num_tx_queues = OCELOT_NUM_TC,
+ .mdio_bus_alloc = felix_mdio_bus_alloc,
+ .mdio_bus_free = felix_mdio_bus_free,
+ .phylink_validate = vsc7512_phylink_validate,
+ .prevalidate_phy_mode = vsc7512_prevalidate_phy_mode,
+ .port_setup_tc = vsc7512_port_setup_tc,
+ .init_regmap = vsc7512_regmap_init,
+};
+
+static int ocelot_spi_probe(struct spi_device *spi)
+{
+ struct ocelot_spi_data *ocelot_spi;
+ struct dsa_switch *ds;
+ struct ocelot *ocelot;
+ struct felix *felix;
+ struct device *dev;
+ int err;
+
+ dev = &spi->dev;
+
+ ocelot_spi = devm_kzalloc(dev, sizeof(struct ocelot_spi_data),
+ GFP_KERNEL);
+
+ if (!ocelot_spi)
+ return -ENOMEM;
+
+ if (spi->max_speed_hz <= 500000) {
+ ocelot_spi->spi_padding_bytes = 0;
+ } else {
+ /* Calculation taken from the manual for IF_CFGSTAT:IF_CFG. Err
+ * on the side of more padding bytes, as having too few can be
+ * difficult to detect at runtime.
+ */
+ ocelot_spi->spi_padding_bytes = 1 +
+ (spi->max_speed_hz / 1000000 + 2) / 8;
+ }
+
+ dev_set_drvdata(dev, ocelot_spi);
+ ocelot_spi->spi = spi;
+
+ spi->bits_per_word = 8;
+
+ err = spi_setup(spi);
+ if (err < 0) {
+ dev_err(&spi->dev, "Error %d initializing SPI\n", err);
+ return err;
+ }
+
+ felix = &ocelot_spi->felix;
+
+ ocelot = &felix->ocelot;
+ ocelot->dev = dev;
+
+ /* Not sure about this */
+ ocelot->num_flooding_pgids = 1;
+
+ felix->info = &ocelot_spi_info;
+
+ ds = kzalloc(sizeof(*ds), GFP_KERNEL);
+ if (!ds) {
+ err = -ENOMEM;
+ dev_err(dev, "Failed to allocate DSA switch\n");
+ return err;
+ }
+
+ ds->dev = &spi->dev;
+ ds->num_ports = felix->info->num_ports;
+ ds->num_tx_queues = felix->info->num_tx_queues;
+ ds->ops = &felix_switch_ops;
+ ds->priv = ocelot;
+ felix->ds = ds;
+
+ err = dsa_register_switch(ds);
+ if (err) {
+ dev_err(dev, "Failed to register DSA switch: %d\n", err);
+ goto err_register_ds;
+ }
+
+ return 0;
+
+err_register_ds:
+ kfree(ds);
+ return err;
+}
+
+static int ocelot_spi_remove(struct spi_device *spi)
+{
+ struct ocelot_spi_data *ocelot_spi;
+ struct felix *felix;
+
+ ocelot_spi = spi_get_drvdata(spi);
+ felix = &ocelot_spi->felix;
+
+ dsa_unregister_switch(felix->ds);
+
+ kfree(felix->ds);
+
+ devm_kfree(&spi->dev, ocelot_spi);
+
+ return 0;
+}
+
+const struct of_device_id vsc7512_of_match[] = {
+ { .compatible = "mscc,vsc7514" },
+ { .compatible = "mscc,vsc7513" },
+ { .compatible = "mscc,vsc7512" },
+ { .compatible = "mscc,vsc7511" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, vsc7512_of_match);
+
+static struct spi_driver ocelot_vsc7512_spi_driver = {
+ .driver = {
+ .name = "vsc7512",
+ .of_match_table = of_match_ptr(vsc7512_of_match),
+ },
+ .probe = ocelot_spi_probe,
+ .remove = ocelot_spi_remove,
+};
+module_spi_driver(ocelot_vsc7512_spi_driver);
+
+MODULE_DESCRIPTION("Ocelot Switch SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index adfb9781799e..10937dc59fce 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -2016,6 +2016,14 @@ int ocelot_init(struct ocelot *ocelot)
int i, ret;
u32 port;
+ if (ocelot->ops->bus_init) {
+ ret = ocelot->ops->bus_init(ocelot);
+ if (ret) {
+ dev_err(ocelot->dev, "Bus init failed\n");
+ return ret;
+ }
+ }
+
if (ocelot->ops->reset) {
ret = ocelot->ops->reset(ocelot);
if (ret) {
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index ff6e65a266d6..f5383804e724 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -129,6 +129,7 @@ enum ocelot_target {
PTP,
GCB,
DEV_GMII,
+ DEV_CPUORG,
TARGET_MAX,
};
@@ -407,6 +408,8 @@ enum ocelot_reg {
GCB_MIIM_MII_STATUS,
GCB_MIIM_MII_CMD,
GCB_MIIM_MII_DATA,
+ GCB_PHY_PHY_CFG,
+ GCB_PHY_PHY_STAT,
DEV_CLOCK_CFG = DEV_GMII << TARGET_OFFSET,
DEV_PORT_MISC,
DEV_EVENTS,
@@ -446,6 +449,20 @@ enum ocelot_reg {
PCS1G_TSTPAT_STATUS,
DEV_PCS_FX100_CFG,
DEV_PCS_FX100_STATUS,
+ DEV_CPUORG_IF_CTRL = DEV_CPUORG << TARGET_OFFSET,
+ DEV_CPUORG_IF_CFGSTAT,
+ DEV_CPUORG_ORG_CFG,
+ DEV_CPUORG_ERR_CNTS,
+ DEV_CPUORG_TIMEOUT_CFG,
+ DEV_CPUORG_GPR,
+ DEV_CPUORG_MAILBOX_SET,
+ DEV_CPUORG_MAILBOX_CLR,
+ DEV_CPUORG_MAILBOX,
+ DEV_CPUORG_SEMA_CFG,
+ DEV_CPUORG_SEMA0,
+ DEV_CPUORG_SEMA0_OWNER,
+ DEV_CPUORG_SEMA1,
+ DEV_CPUORG_SEMA1_OWNER,
};
enum ocelot_regfield {
@@ -504,6 +521,7 @@ enum ocelot_regfield {
SYS_RESET_CFG_MEM_ENA,
SYS_RESET_CFG_MEM_INIT,
GCB_SOFT_RST_SWC_RST,
+ GCB_SOFT_RST_CHIP_RST,
GCB_MIIM_MII_STATUS_PENDING,
GCB_MIIM_MII_STATUS_BUSY,
SYS_PAUSE_CFG_PAUSE_START,
@@ -560,6 +578,7 @@ struct ocelot;
struct ocelot_ops {
struct net_device *(*port_to_netdev)(struct ocelot *ocelot, int port);
int (*netdev_to_port)(struct net_device *dev);
+ int (*bus_init)(struct ocelot *ocelot);
int (*reset)(struct ocelot *ocelot);
u16 (*wm_enc)(u16 value);
u16 (*wm_dec)(u16 value);
--
2.25.1
Expose ocelot_wm functions so they can be shared with other drivers.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/ethernet/mscc/Makefile | 1 +
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 28 ---------------
drivers/net/ethernet/mscc/ocelot_wm.c | 40 ++++++++++++++++++++++
include/soc/mscc/ocelot.h | 5 +++
4 files changed, 46 insertions(+), 28 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_wm.c
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index d539a231a478..4ea9ecdfa60c 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -8,6 +8,7 @@ mscc_ocelot_switch_lib-y := \
ocelot_flower.o \
ocelot_ptp.o \
ocelot_regs.o \
+ ocelot_wm.o \
ocelot_devlink.o
mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index ef1bf24f51b5..6e58f95a8dad 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -302,34 +302,6 @@ static int ocelot_reset(struct ocelot *ocelot)
return 0;
}
-/* Watermark encode
- * Bit 8: Unit; 0:1, 1:16
- * Bit 7-0: Value to be multiplied with unit
- */
-static u16 ocelot_wm_enc(u16 value)
-{
- WARN_ON(value >= 16 * BIT(8));
-
- if (value >= BIT(8))
- return BIT(8) | (value / 16);
-
- return value;
-}
-
-static u16 ocelot_wm_dec(u16 wm)
-{
- if (wm & BIT(8))
- return (wm & GENMASK(7, 0)) * 16;
-
- return wm;
-}
-
-static void ocelot_wm_stat(u32 val, u32 *inuse, u32 *maxuse)
-{
- *inuse = (val & GENMASK(23, 12)) >> 12;
- *maxuse = val & GENMASK(11, 0);
-}
-
static const struct ocelot_ops ocelot_ops = {
.reset = ocelot_reset,
.wm_enc = ocelot_wm_enc,
diff --git a/drivers/net/ethernet/mscc/ocelot_wm.c b/drivers/net/ethernet/mscc/ocelot_wm.c
new file mode 100644
index 000000000000..f9a11a6a059d
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_wm.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#include "ocelot.h"
+
+/* Watermark encode
+ * Bit 8: Unit; 0:1, 1:16
+ * Bit 7-0: Value to be multiplied with unit
+ */
+u16 ocelot_wm_enc(u16 value)
+{
+ WARN_ON(value >= 16 * BIT(8));
+
+ if (value >= BIT(8))
+ return BIT(8) | (value / 16);
+
+ return value;
+}
+EXPORT_SYMBOL(ocelot_wm_enc);
+
+u16 ocelot_wm_dec(u16 wm)
+{
+ if (wm & BIT(8))
+ return (wm & GENMASK(7, 0)) * 16;
+
+ return wm;
+}
+EXPORT_SYMBOL(ocelot_wm_dec);
+
+void ocelot_wm_stat(u32 val, u32 *inuse, u32 *maxuse)
+{
+ *inuse = (val & GENMASK(23, 12)) >> 12;
+ *maxuse = val & GENMASK(11, 0);
+}
+EXPORT_SYMBOL(ocelot_wm_stat);
+
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 2f5ce4d4fdbf..ff6e65a266d6 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -797,6 +797,11 @@ void ocelot_deinit(struct ocelot *ocelot);
void ocelot_init_port(struct ocelot *ocelot, int port);
void ocelot_deinit_port(struct ocelot *ocelot, int port);
+/* Watermark interface */
+u16 ocelot_wm_enc(u16 value);
+u16 ocelot_wm_dec(u16 wm);
+void ocelot_wm_stat(u32 val, u32 *inuse, u32 *maxuse);
+
/* DSA callbacks */
void ocelot_port_enable(struct ocelot *ocelot, int port,
struct phy_device *phy);
--
2.25.1
On Sat, Jul 10, 2021 at 12:25:54PM -0700, Colin Foster wrote:
> 1. The first probe of the internal MDIO bus fails. I suspect this is related to
> power supplies / grounding issues that would not appear on standard hardware.
Where and with what error code does it fail exactly? I don't understand
the concept of "the first probe"? You mean that there is an
-EPROBE_DEFER and it tries again immediately afterwards? Or you need to
unbind and rebind the driver to the device, or what?
> 2. Communication to the CPU bus doesn't seem to function properly. I suspect
> this is due to the fact that ocelot / felix assumes it is using the built-in CPU
> / NPI port for forwarding, though I am not positive.
What is the CPU bus and what doesn't function properly about it?
> Nonetheless, these two issues likely won't require a large architecture change,
> and perhaps those who know much more about the ocelot chips than I could chime
> in.
I guess you don't know if that's the case or not until you know what the
problem is...
On Sat, Jul 10, 2021 at 12:25:55PM -0700, Colin Foster wrote:
> The pci_bar variables for the switch and imdio don't make sense for the
> generic felix driver. Moving them to felix_vsc9959 to limit scope and
> simplify the felix_info struct.
>
> Signed-off-by: Colin Foster <[email protected]>
> ---
Reviewed-by: Vladimir Oltean <[email protected]>
Just one comment below.
> drivers/net/dsa/ocelot/felix.h | 2 --
> drivers/net/dsa/ocelot/felix_vsc9959.c | 11 +++++------
> 2 files changed, 5 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
> index 4d96cad815d5..47769dd386db 100644
> --- a/drivers/net/dsa/ocelot/felix.h
> +++ b/drivers/net/dsa/ocelot/felix.h
> @@ -20,8 +20,6 @@ struct felix_info {
> int num_ports;
> int num_tx_queues;
> struct vcap_props *vcap;
> - int switch_pci_bar;
> - int imdio_pci_bar;
> const struct ptp_clock_info *ptp_caps;
>
> /* Some Ocelot switches are integrated into the SoC without the
> diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
> index f966a253d1c7..182ca749c8e2 100644
> --- a/drivers/net/dsa/ocelot/felix_vsc9959.c
> +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
> @@ -1359,8 +1359,6 @@ static const struct felix_info felix_info_vsc9959 = {
> .num_mact_rows = 2048,
> .num_ports = 6,
> .num_tx_queues = OCELOT_NUM_TC,
> - .switch_pci_bar = 4,
> - .imdio_pci_bar = 0,
> .quirk_no_xtr_irq = true,
> .ptp_caps = &vsc9959_ptp_caps,
> .mdio_bus_alloc = vsc9959_mdio_bus_alloc,
> @@ -1388,6 +1386,9 @@ static irqreturn_t felix_irq_handler(int irq, void *data)
> return IRQ_HANDLED;
> }
>
> +#define VSC9959_SWITCH_PCI_BAR 4
> +#define VSC9959_IMDIO_PCI_BAR 0
> +
I would prefer these to be declared right below
#define VSC9959_TAS_GCL_ENTRY_MAX 63
and aligned with it
On Sat, Jul 10, 2021 at 12:25:56PM -0700, Colin Foster wrote:
> Indirect MDIO access is a feature that doesn't need to be specific to the
> Seville driver. Separate the feature to a common file so it can be shared.
>
> Signed-off-by: Colin Foster <[email protected]>
> ---
In fact, this same piece of hardware has a dedicated driver inside
drivers/net/mdio/mdio-mscc-miim.c. The only problem is that it doesn't
work with regmap, and especially not with a caller-supplied regmap. I
was too lazy to do that, but it is probably what should have been done.
By comparison, felix_vsc9959.c was coded up to work with an internal
MDIO bus whose ops are implemented by another driver (enetc_mdio). Maybe
you could take that as an example and have mdio-mscc-miim.c drive both
seville and ocelot.
On 10/07/2021 12:25:54-0700, Colin Foster wrote:
> Add support for configuration and control of the VSC7511, VSC7512, VSC7513, and
> VSC7514 chips over a SPI interface. The intent is to control these chips from an
> external CPU. The expectation is to have most of the features of the
> net/ethernet/mscc/ocelot_vsc7514 driver.
>
> I have tried to heed all the advice from my first patch RFC. Thanks to everyone
> for all the feedback.
>
> The current status is that there are two functional "bugs" that need
> investigation:
> 1. The first probe of the internal MDIO bus fails. I suspect this is related to
> power supplies / grounding issues that would not appear on standard hardware.
Did you properly reset the internal phys? mdio-mscc-miim.c does that.
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
On 10/07/2021 22:59:13+0300, Vladimir Oltean wrote:
> On Sat, Jul 10, 2021 at 12:25:56PM -0700, Colin Foster wrote:
> > Indirect MDIO access is a feature that doesn't need to be specific to the
> > Seville driver. Separate the feature to a common file so it can be shared.
> >
> > Signed-off-by: Colin Foster <[email protected]>
> > ---
>
> In fact, this same piece of hardware has a dedicated driver inside
> drivers/net/mdio/mdio-mscc-miim.c. The only problem is that it doesn't
> work with regmap, and especially not with a caller-supplied regmap. I
> was too lazy to do that, but it is probably what should have been done.
>
> By comparison, felix_vsc9959.c was coded up to work with an internal
> MDIO bus whose ops are implemented by another driver (enetc_mdio). Maybe
> you could take that as an example and have mdio-mscc-miim.c drive both
> seville and ocelot.
That was indeed going to be my comment.
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
On Sat, Jul 10, 2021 at 12:25:57PM -0700, Colin Foster wrote:
> Add NULL check before dereferencing array
>
> Signed-off-by: Colin Foster <[email protected]>
> ---
The patch is correct but is insufficiently documented. In particular,
people might interpret it as a bug fix and backport it to stable
kernels.
On Sat, Jul 10, 2021 at 12:26:02PM -0700, Colin Foster wrote:
> Signed-off-by: Colin Foster <[email protected]>
> ---
> .../devicetree/bindings/net/dsa/ocelot.txt | 68 +++++++++++++++++++
> 1 file changed, 68 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/dsa/ocelot.txt b/Documentation/devicetree/bindings/net/dsa/ocelot.txt
> index 7a271d070b72..f5d05bf8b093 100644
> --- a/Documentation/devicetree/bindings/net/dsa/ocelot.txt
> +++ b/Documentation/devicetree/bindings/net/dsa/ocelot.txt
> @@ -8,6 +8,7 @@ Currently the switches supported by the felix driver are:
>
> - VSC9959 (Felix)
> - VSC9953 (Seville)
> +- VSC7511, VSC7512, VSC7513, VSC7514 via SPI
>
> The VSC9959 switch is found in the NXP LS1028A. It is a PCI device, part of the
> larger ENETC root complex. As a result, the ethernet-switch node is a sub-node
> @@ -211,3 +212,70 @@ Example:
> };
> };
> };
> +
> +The VSC7513 and VSC7514 switches can be controlled internally via the MIPS
> +processor. The VSC7511 and VSC7512 don't have this internal processor, but all
> +four chips can be controlled externally through SPI with the following required
> +properties:
> +
> +- compatible:
> + Can be "mscc,vsc7511", "mscc,vsc7512", "mscc,vsc7513", or
> + "mscc,vsc7514".
> +
> +Supported phy modes for all chips are:
> +
> +* phy_mode = "internal": on ports 0, 1, 2, 3
> +
> +Additionally, the VSC7512 and VSC7514 support SGMII and QSGMII on various ports,
> +though that is currently untested.
> +
> +Example for control from a BeagleBone Black
> +
> +&spi0 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + status = "okay";
> +
> + vsc7512: vsc7512@0 {
ethernet-switch@0
> + compatible = "mscc,vsc7512";
> + spi-max-frequency = <250000>;
> + reg = <0>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> + ethernet = <&mac>;
> + phy-mode = "internal";
> +
> + fixed-link {
> + speed = <100>;
> + full-duplex;
> + };
> + };
> +
> + port@1 {
> + reg = <1>;
> + label = "swp1";
> + status = "okay";
I am not convinced that the status = "okay" lines are useful in the
example.
> + phy-mode = "internal";
This syntax is ambiguous and does not obviously mean that the port has
an internal copper PHY. Please see this discussion for other meanings of
no 'phy-handle' and no 'fixed-link'.
https://www.mail-archive.com/[email protected]/msg409571.html
I think it would be in the best interest of everyone to go through
phylink_of_phy_connect() instead of phylink_connect_phy(), aka use the
standard phy-handle property and create an mdio node under
ethernet-switch@0 where the internal PHY OF nodes are defined.
I don't know if this is true for VSC7512 or not, but for example on
NXP SJA1110, the internal PHYs can be accessed in 2 modes:
(a) through SPI transfers
(b) through an MDIO slave access point exposed by the switch chip, which
can be connected to an external MDIO controller
Some boards will use method (a), and others will use method (b).
Requiring a phy-handle under the port property is an absolutely generic
way to seamlessly deal with both cases. In case (a), the phy-handle
points to a child of an MDIO bus provided by the ocelot driver, in case
(b) the phy-handle points to a child provided by some other MDIO
controller driver.
> + };
> +
> + port@2 {
> + reg = <2>;
> + label = "swp2";
> + status = "okay";
> + phy-mode = "internal";
> + };
> +
> + port@3 {
> + reg = <3>;
> + label = "swp3";
> + status = "okay";
> + phy-mode = "internal";
> + };
> + };
> + };
> +};
> --
> 2.25.1
>
On Sat, Jul 10, 2021 at 12:26:00PM -0700, Colin Foster wrote:
> diff --git a/drivers/net/ethernet/mscc/ocelot_wm.c b/drivers/net/ethernet/mscc/ocelot_wm.c
> new file mode 100644
> index 000000000000..f9a11a6a059d
> --- /dev/null
> +++ b/drivers/net/ethernet/mscc/ocelot_wm.c
> @@ -0,0 +1,40 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/*
> + * Microsemi Ocelot Switch driver
> + *
> + * Copyright (c) 2017 Microsemi Corporation
> + */
> +
> +#include "ocelot.h"
> +
> +/* Watermark encode
> + * Bit 8: Unit; 0:1, 1:16
> + * Bit 7-0: Value to be multiplied with unit
> + */
> +u16 ocelot_wm_enc(u16 value)
> +{
> + WARN_ON(value >= 16 * BIT(8));
> +
> + if (value >= BIT(8))
> + return BIT(8) | (value / 16);
> +
> + return value;
> +}
> +EXPORT_SYMBOL(ocelot_wm_enc);
> +
> +u16 ocelot_wm_dec(u16 wm)
> +{
> + if (wm & BIT(8))
> + return (wm & GENMASK(7, 0)) * 16;
> +
> + return wm;
> +}
> +EXPORT_SYMBOL(ocelot_wm_dec);
> +
> +void ocelot_wm_stat(u32 val, u32 *inuse, u32 *maxuse)
> +{
> + *inuse = (val & GENMASK(23, 12)) >> 12;
> + *maxuse = val & GENMASK(11, 0);
> +}
> +EXPORT_SYMBOL(ocelot_wm_stat);
> +
Do not use blank lines at the end of files, 'git am' will complain that
the patches are whitespace damaged.
On Sat, Jul 10, 2021 at 12:26:01PM -0700, Colin Foster wrote:
> +static const struct felix_info ocelot_spi_info = {
> + .target_io_res = vsc7512_target_io_res,
> + .port_io_res = vsc7512_port_io_res,
> + .regfields = vsc7512_regfields,
> + .map = vsc7512_regmap,
> + .ops = &vsc7512_ops,
> + .stats_layout = vsc7512_stats_layout,
> + .num_stats = ARRAY_SIZE(vsc7512_stats_layout),
> + .vcap = vsc7512_vcap_props,
> + .num_mact_rows = 1024,
> +
> + /* The 7512 and 7514 both have support for up to 10 ports. The 7511 and
> + * 7513 have support for 4. Due to lack of hardware to test and
> + * validate external phys, this is currently limited to 4 ports.
> + * Expanding this to 10 for the 7512 and 7514 and defining the
> + * appropriate phy-handle values in the device tree should be possible.
> + */
> + .num_ports = 4,
Ouch, this was probably not a good move.
felix_setup() -> felix_init_structs sets ocelot->num_phys_ports based on
this value.
If you search for ocelot->num_phys_ports in ocelot and in felix, it is
widely used to denote "the index of the CPU port module within the
analyzer block", since the CPU port module's number is equal to the
number of the last physical port + 1. If VSC7512 has 10 ports, then the
CPU port module is port 10, and if you set num_ports to 4 you will cause
the driver to misbehave.
> + .num_tx_queues = OCELOT_NUM_TC,
> + .mdio_bus_alloc = felix_mdio_bus_alloc,
> + .mdio_bus_free = felix_mdio_bus_free,
> + .phylink_validate = vsc7512_phylink_validate,
> + .prevalidate_phy_mode = vsc7512_prevalidate_phy_mode,
> + .port_setup_tc = vsc7512_port_setup_tc,
> + .init_regmap = vsc7512_regmap_init,
> +};
> + /* Not sure about this */
> + ocelot->num_flooding_pgids = 1;
Why are you not sure? It's the same as ocelot.
On Sat, Jul 10, 2021 at 10:47:55PM +0300, Vladimir Oltean wrote:
> On Sat, Jul 10, 2021 at 12:25:54PM -0700, Colin Foster wrote:
> > 1. The first probe of the internal MDIO bus fails. I suspect this is related to
> > power supplies / grounding issues that would not appear on standard hardware.
>
> Where and with what error code does it fail exactly? I don't understand
> the concept of "the first probe"? You mean that there is an
> -EPROBE_DEFER and it tries again immediately afterwards? Or you need to
> unbind and rebind the driver to the device, or what?
My hardware I'm using for test / dev is a bit of a hack. Beaglebone
jumped to a VSC7512 dev board SPI lines after some modifications.
I believe I'm seeing grounding-related issues as a result of this. For
instance, if I power on the dev board before powering on the Beaglebone,
the BB will be held in reset. The same thing will happen if I reset the
BB when the Microsemi dev board has been powered on, but left
unconfigured.
When I run "modprobe mscc_ocelot_spi" the first time the driver fails to
enumerate swp1-3 with "failed to connect to port X: -19" message. But
after doing that, I can reset the BB to my heart's content. Future boots
will be able to successfully find those at initial mod insertion. And
reloading / rebinding the driver successfully registers the ports at
spi0.0-imdio:0X
>
> > 2. Communication to the CPU bus doesn't seem to function properly. I suspect
> > this is due to the fact that ocelot / felix assumes it is using the built-in CPU
> > / NPI port for forwarding, though I am not positive.
>
> What is the CPU bus and what doesn't function properly about it?
I'm still learning a lot about how the chip functions, especially now
that I have attained some functionality from the driver. There's a CPU
bus that can be utilized when you're running internally, and I believe
that can be configured as the NPI bus if you're running through PCIe.
When controlling through SPI I'm not convinced we'll have access to that
bus, and if the Ocelot driver relies on that functionality I've got some
more work in front of me.
>
> > Nonetheless, these two issues likely won't require a large architecture change,
> > and perhaps those who know much more about the ocelot chips than I could chime
> > in.
>
> I guess you don't know if that's the case or not until you know what the
> problem is...
True
On Sat, Jul 10, 2021 at 10:53:18PM +0300, Vladimir Oltean wrote:
> On Sat, Jul 10, 2021 at 12:25:55PM -0700, Colin Foster wrote:
> > The pci_bar variables for the switch and imdio don't make sense for the
> > generic felix driver. Moving them to felix_vsc9959 to limit scope and
> > simplify the felix_info struct.
> >
> > Signed-off-by: Colin Foster <[email protected]>
> > ---
>
> Reviewed-by: Vladimir Oltean <[email protected]>
>
> Just one comment below.
>
> > drivers/net/dsa/ocelot/felix.h | 2 --
> > drivers/net/dsa/ocelot/felix_vsc9959.c | 11 +++++------
> > 2 files changed, 5 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
> > index 4d96cad815d5..47769dd386db 100644
> > --- a/drivers/net/dsa/ocelot/felix.h
> > +++ b/drivers/net/dsa/ocelot/felix.h
> > @@ -20,8 +20,6 @@ struct felix_info {
> > int num_ports;
> > int num_tx_queues;
> > struct vcap_props *vcap;
> > - int switch_pci_bar;
> > - int imdio_pci_bar;
> > const struct ptp_clock_info *ptp_caps;
> >
> > /* Some Ocelot switches are integrated into the SoC without the
> > diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
> > index f966a253d1c7..182ca749c8e2 100644
> > --- a/drivers/net/dsa/ocelot/felix_vsc9959.c
> > +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
> > @@ -1359,8 +1359,6 @@ static const struct felix_info felix_info_vsc9959 = {
> > .num_mact_rows = 2048,
> > .num_ports = 6,
> > .num_tx_queues = OCELOT_NUM_TC,
> > - .switch_pci_bar = 4,
> > - .imdio_pci_bar = 0,
> > .quirk_no_xtr_irq = true,
> > .ptp_caps = &vsc9959_ptp_caps,
> > .mdio_bus_alloc = vsc9959_mdio_bus_alloc,
> > @@ -1388,6 +1386,9 @@ static irqreturn_t felix_irq_handler(int irq, void *data)
> > return IRQ_HANDLED;
> > }
> >
> > +#define VSC9959_SWITCH_PCI_BAR 4
> > +#define VSC9959_IMDIO_PCI_BAR 0
> > +
>
> I would prefer these to be declared right below
>
> #define VSC9959_TAS_GCL_ENTRY_MAX 63
>
> and aligned with it
Done
On Sat, Jul 10, 2021 at 10:59:13PM +0300, Vladimir Oltean wrote:
> On Sat, Jul 10, 2021 at 12:25:56PM -0700, Colin Foster wrote:
> > Indirect MDIO access is a feature that doesn't need to be specific to the
> > Seville driver. Separate the feature to a common file so it can be shared.
> >
> > Signed-off-by: Colin Foster <[email protected]>
> > ---
>
> In fact, this same piece of hardware has a dedicated driver inside
> drivers/net/mdio/mdio-mscc-miim.c. The only problem is that it doesn't
> work with regmap, and especially not with a caller-supplied regmap. I
> was too lazy to do that, but it is probably what should have been done.
>
> By comparison, felix_vsc9959.c was coded up to work with an internal
> MDIO bus whose ops are implemented by another driver (enetc_mdio). Maybe
> you could take that as an example and have mdio-mscc-miim.c drive both
> seville and ocelot.
I didn't know about that code. I'll definitely look into it.
On Sat, Jul 10, 2021 at 10:01:45PM +0200, Alexandre Belloni wrote:
> On 10/07/2021 12:25:54-0700, Colin Foster wrote:
> > Add support for configuration and control of the VSC7511, VSC7512, VSC7513, and
> > VSC7514 chips over a SPI interface. The intent is to control these chips from an
> > external CPU. The expectation is to have most of the features of the
> > net/ethernet/mscc/ocelot_vsc7514 driver.
> >
> > I have tried to heed all the advice from my first patch RFC. Thanks to everyone
> > for all the feedback.
> >
> > The current status is that there are two functional "bugs" that need
> > investigation:
> > 1. The first probe of the internal MDIO bus fails. I suspect this is related to
> > power supplies / grounding issues that would not appear on standard hardware.
>
> Did you properly reset the internal phys? mdio-mscc-miim.c does that.
That code looks familiar to what I wrote, but the mdio-mscc-miim.c
implementation has the 500ms delay. That might be all I need...
On Sat, Jul 10, 2021 at 11:06:28PM +0300, Vladimir Oltean wrote:
> On Sat, Jul 10, 2021 at 12:25:57PM -0700, Colin Foster wrote:
> > Add NULL check before dereferencing array
> >
> > Signed-off-by: Colin Foster <[email protected]>
> > ---
>
> The patch is correct but is insufficiently documented. In particular,
> people might interpret it as a bug fix and backport it to stable
> kernels.
That makes sense. I'll clarify that it is only something that'll affect
future drivers and not a bug fix.
On Sat, Jul 10, 2021 at 11:34:11PM +0300, Vladimir Oltean wrote:
> On Sat, Jul 10, 2021 at 12:26:02PM -0700, Colin Foster wrote:
> > Signed-off-by: Colin Foster <[email protected]>
> > ---
> > .../devicetree/bindings/net/dsa/ocelot.txt | 68 +++++++++++++++++++
> > 1 file changed, 68 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/net/dsa/ocelot.txt b/Documentation/devicetree/bindings/net/dsa/ocelot.txt
> > index 7a271d070b72..f5d05bf8b093 100644
> > --- a/Documentation/devicetree/bindings/net/dsa/ocelot.txt
> > +++ b/Documentation/devicetree/bindings/net/dsa/ocelot.txt
> > @@ -8,6 +8,7 @@ Currently the switches supported by the felix driver are:
> >
> > - VSC9959 (Felix)
> > - VSC9953 (Seville)
> > +- VSC7511, VSC7512, VSC7513, VSC7514 via SPI
> >
> > The VSC9959 switch is found in the NXP LS1028A. It is a PCI device, part of the
> > larger ENETC root complex. As a result, the ethernet-switch node is a sub-node
> > @@ -211,3 +212,70 @@ Example:
> > };
> > };
> > };
> > +
> > +The VSC7513 and VSC7514 switches can be controlled internally via the MIPS
> > +processor. The VSC7511 and VSC7512 don't have this internal processor, but all
> > +four chips can be controlled externally through SPI with the following required
> > +properties:
> > +
> > +- compatible:
> > + Can be "mscc,vsc7511", "mscc,vsc7512", "mscc,vsc7513", or
> > + "mscc,vsc7514".
> > +
> > +Supported phy modes for all chips are:
> > +
> > +* phy_mode = "internal": on ports 0, 1, 2, 3
> > +
> > +Additionally, the VSC7512 and VSC7514 support SGMII and QSGMII on various ports,
> > +though that is currently untested.
> > +
> > +Example for control from a BeagleBone Black
> > +
> > +&spi0 {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > + status = "okay";
> > +
> > + vsc7512: vsc7512@0 {
>
> ethernet-switch@0
>
> > + compatible = "mscc,vsc7512";
> > + spi-max-frequency = <250000>;
> > + reg = <0>;
> > +
> > + ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + port@0 {
> > + reg = <0>;
> > + ethernet = <&mac>;
> > + phy-mode = "internal";
> > +
> > + fixed-link {
> > + speed = <100>;
> > + full-duplex;
> > + };
> > + };
> > +
> > + port@1 {
> > + reg = <1>;
> > + label = "swp1";
> > + status = "okay";
>
> I am not convinced that the status = "okay" lines are useful in the
> example.
Fair enough
>
> > + phy-mode = "internal";
>
> This syntax is ambiguous and does not obviously mean that the port has
> an internal copper PHY. Please see this discussion for other meanings of
> no 'phy-handle' and no 'fixed-link'.
>
> https://www.mail-archive.com/[email protected]/msg409571.html
>
> I think it would be in the best interest of everyone to go through
> phylink_of_phy_connect() instead of phylink_connect_phy(), aka use the
> standard phy-handle property and create an mdio node under
> ethernet-switch@0 where the internal PHY OF nodes are defined.
>
> I don't know if this is true for VSC7512 or not, but for example on
> NXP SJA1110, the internal PHYs can be accessed in 2 modes:
> (a) through SPI transfers
> (b) through an MDIO slave access point exposed by the switch chip, which
> can be connected to an external MDIO controller
>
> Some boards will use method (a), and others will use method (b).
>
> Requiring a phy-handle under the port property is an absolutely generic
> way to seamlessly deal with both cases. In case (a), the phy-handle
> points to a child of an MDIO bus provided by the ocelot driver, in case
> (b) the phy-handle points to a child provided by some other MDIO
> controller driver.
>
Yes, the Ocelot chips have the same functionality with the indirect /
direct access. It seems like this would be coupled with the other
discussion of updating mdio-mscc-miim.c so that the MDIO bus can be
defined.
And thank you for pointing out some examples. Having some starting
points really helps!
> > + };
> > +
> > + port@2 {
> > + reg = <2>;
> > + label = "swp2";
> > + status = "okay";
> > + phy-mode = "internal";
> > + };
> > +
> > + port@3 {
> > + reg = <3>;
> > + label = "swp3";
> > + status = "okay";
> > + phy-mode = "internal";
> > + };
> > + };
> > + };
> > +};
> > --
> > 2.25.1
> >
On Sat, Jul 10, 2021 at 11:52:05PM +0300, Vladimir Oltean wrote:
> On Sat, Jul 10, 2021 at 12:26:01PM -0700, Colin Foster wrote:
> > +static const struct felix_info ocelot_spi_info = {
> > + .target_io_res = vsc7512_target_io_res,
> > + .port_io_res = vsc7512_port_io_res,
> > + .regfields = vsc7512_regfields,
> > + .map = vsc7512_regmap,
> > + .ops = &vsc7512_ops,
> > + .stats_layout = vsc7512_stats_layout,
> > + .num_stats = ARRAY_SIZE(vsc7512_stats_layout),
> > + .vcap = vsc7512_vcap_props,
> > + .num_mact_rows = 1024,
> > +
> > + /* The 7512 and 7514 both have support for up to 10 ports. The 7511 and
> > + * 7513 have support for 4. Due to lack of hardware to test and
> > + * validate external phys, this is currently limited to 4 ports.
> > + * Expanding this to 10 for the 7512 and 7514 and defining the
> > + * appropriate phy-handle values in the device tree should be possible.
> > + */
> > + .num_ports = 4,
>
> Ouch, this was probably not a good move.
> felix_setup() -> felix_init_structs sets ocelot->num_phys_ports based on
> this value.
> If you search for ocelot->num_phys_ports in ocelot and in felix, it is
> widely used to denote "the index of the CPU port module within the
> analyzer block", since the CPU port module's number is equal to the
> number of the last physical port + 1. If VSC7512 has 10 ports, then the
> CPU port module is port 10, and if you set num_ports to 4 you will cause
> the driver to misbehave.
Yes, this is part of my concern with the CPU / NPI module mentioned
before. In my hardware, I'd have port 0 plugged to the external CPU. In
Ocelot it is the internal bus, and in Felix it is the NPI. In this SPI
design, does the driver lose significant functionality by not having
access to those ports?
In my test setup (and our expected production) we'd have port 0
connected to the external chip, and ports 1-3 exposed. Does Ocelot need
to be modified to allow a parameter for the CPU port?
And obviously I'd imagine this would want to be done in such a way that
it doesn't break existing device trees...
>
> > + .num_tx_queues = OCELOT_NUM_TC,
> > + .mdio_bus_alloc = felix_mdio_bus_alloc,
> > + .mdio_bus_free = felix_mdio_bus_free,
> > + .phylink_validate = vsc7512_phylink_validate,
> > + .prevalidate_phy_mode = vsc7512_prevalidate_phy_mode,
> > + .port_setup_tc = vsc7512_port_setup_tc,
> > + .init_regmap = vsc7512_regmap_init,
> > +};
>
> > + /* Not sure about this */
> > + ocelot->num_flooding_pgids = 1;
>
> Why are you not sure? It's the same as ocelot.
Sorry - missed removing that comment... Removed.
On 11/07/2021 10:09:27-0700, Colin Foster wrote:
> On Sat, Jul 10, 2021 at 11:52:05PM +0300, Vladimir Oltean wrote:
> > On Sat, Jul 10, 2021 at 12:26:01PM -0700, Colin Foster wrote:
> > > +static const struct felix_info ocelot_spi_info = {
> > > + .target_io_res = vsc7512_target_io_res,
> > > + .port_io_res = vsc7512_port_io_res,
> > > + .regfields = vsc7512_regfields,
> > > + .map = vsc7512_regmap,
> > > + .ops = &vsc7512_ops,
> > > + .stats_layout = vsc7512_stats_layout,
> > > + .num_stats = ARRAY_SIZE(vsc7512_stats_layout),
> > > + .vcap = vsc7512_vcap_props,
> > > + .num_mact_rows = 1024,
> > > +
> > > + /* The 7512 and 7514 both have support for up to 10 ports. The 7511 and
> > > + * 7513 have support for 4. Due to lack of hardware to test and
> > > + * validate external phys, this is currently limited to 4 ports.
> > > + * Expanding this to 10 for the 7512 and 7514 and defining the
> > > + * appropriate phy-handle values in the device tree should be possible.
> > > + */
> > > + .num_ports = 4,
> >
> > Ouch, this was probably not a good move.
> > felix_setup() -> felix_init_structs sets ocelot->num_phys_ports based on
> > this value.
> > If you search for ocelot->num_phys_ports in ocelot and in felix, it is
> > widely used to denote "the index of the CPU port module within the
> > analyzer block", since the CPU port module's number is equal to the
> > number of the last physical port + 1. If VSC7512 has 10 ports, then the
> > CPU port module is port 10, and if you set num_ports to 4 you will cause
> > the driver to misbehave.
>
> Yes, this is part of my concern with the CPU / NPI module mentioned
> before. In my hardware, I'd have port 0 plugged to the external CPU. In
> Ocelot it is the internal bus, and in Felix it is the NPI. In this SPI
> design, does the driver lose significant functionality by not having
> access to those ports?
>
From the switchdev driver perspective, the CPU port is special because
it is the one allowing to send and receive frames to/from the exposed
ethernet interfaces. However, the goal is definitively to use that as
little as possible (especially since as implemented right now,
throughput is about 20Mbps).
I didn't have a look at the DSA implementation but I wouldn't expect the
NPI port to be that special.
> In my test setup (and our expected production) we'd have port 0
> connected to the external chip, and ports 1-3 exposed. Does Ocelot need
> to be modified to allow a parameter for the CPU port?
>
DSA is what allows you to select which of the port is the port connected
to the BBB (this is the CPU port in DSA parlance). This is what you see
in the example in Documentation/devicetree/bindings/net/dsa/ocelot.txt
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com