2019-06-21 15:39:38

by Claudiu Manoil

[permalink] [raw]
Subject: [PATCH net-next 6/6] net/mssc/ocelot: Add basic Felix switch driver

This supports a switch core ethernet device from Microsemi
(VSC9959) that can be integrated on different SoCs as a PCIe
endpoint device.

The switchdev functionality is provided by the core Ocelot
switch driver. In this regard, the current driver is an
instance of Microsemi's Ocelot core driver.

The patch adds the PCI device driver part and defines the
register map for the Felix switch core, as it has some
differences in register addresses and bitfield mappings
compared to the Ocelot switch. Also some registers or
bitfields present on Ocelot are not available on Felix.
That's why this driver has its own register map instance.
Other than that, the common registers and bitfields have the
same functionality and share the same name.

In a few cases, some h/w operations have to be done differently
on Felix due to missing bitfields. This is the case for the
switch core reset and init. Because for this operation Ocelot
uses some bits that are not present on Felix, the later has to
use a register from the global registers block (GCB) instead.

Signed-off-by: Catalin Horghidan <[email protected]>
Signed-off-by: Claudiu Manoil <[email protected]>
---
drivers/net/ethernet/mscc/Kconfig | 8 +
drivers/net/ethernet/mscc/Makefile | 9 +-
drivers/net/ethernet/mscc/felix_board.c | 392 +++++++++++++++++++++
drivers/net/ethernet/mscc/felix_regs.c | 448 ++++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot.h | 7 +
5 files changed, 862 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/felix_board.c
create mode 100644 drivers/net/ethernet/mscc/felix_regs.c

diff --git a/drivers/net/ethernet/mscc/Kconfig b/drivers/net/ethernet/mscc/Kconfig
index bcec0587cf61..e5a7fa69307e 100644
--- a/drivers/net/ethernet/mscc/Kconfig
+++ b/drivers/net/ethernet/mscc/Kconfig
@@ -29,4 +29,12 @@ config MSCC_OCELOT_SWITCH_OCELOT
This driver supports the Ocelot network switch device as present on
the Ocelot SoCs.

+config MSCC_FELIX_SWITCH
+ tristate "Felix switch driver"
+ depends on MSCC_OCELOT_SWITCH
+ depends on PCI
+ help
+ This driver supports the Felix network switch device, connected as a
+ PCI device.
+
endif # NET_VENDOR_MICROSEMI
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index 9a36c26095c8..81593feb2ea1 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -1,5 +1,10 @@
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
mscc_ocelot_common-y := ocelot.o ocelot_io.o
-mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o
-obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
+mscc_ocelot_common-y += ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o
+
+obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += mscc_ocelot.o
+mscc_ocelot-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) := ocelot_regs.o ocelot_board.o
+
+obj-$(CONFIG_MSCC_FELIX_SWITCH) += mscc_felix.o
+mscc_felix-$(CONFIG_MSCC_FELIX_SWITCH) := felix_regs.o felix_board.o
diff --git a/drivers/net/ethernet/mscc/felix_board.c b/drivers/net/ethernet/mscc/felix_board.c
new file mode 100644
index 000000000000..57f7a897b3ae
--- /dev/null
+++ b/drivers/net/ethernet/mscc/felix_board.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Felix Switch driver
+ *
+ * Copyright 2018-2019 NXP
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/phy_fixed.h>
+#include <linux/phy.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/iopoll.h>
+#include <net/switchdev.h>
+#include "ocelot.h"
+
+#define FELIX_DRV_VER_MAJ 1
+#define FELIX_DRV_VER_MIN 0
+
+#define FELIX_DRV_STR "Felix Switch driver"
+#define FELIX_DRV_VER_STR __stringify(FELIX_DRV_VER_MAJ) "." \
+ __stringify(FELIX_DRV_VER_MIN)
+
+#define PCI_DEVICE_ID_FELIX 0xEEF0
+
+/* Switch register block BAR */
+#define FELIX_SWITCH_BAR 4
+
+static struct pci_device_id felix_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_FELIX) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, felix_ids);
+
+static struct {
+ enum ocelot_target id;
+ struct resource res;
+} felix_io_res[] = {
+ { .id = ANA,
+ {
+ .start = 0x0280000,
+ .end = 0x028ffff,
+ .name = "ana",
+ }
+ },
+ { .id = QS,
+ {
+ .start = 0x0080000,
+ .end = 0x00800ff,
+ .name = "qs",
+ }
+ },
+ { .id = QSYS,
+ {
+ .start = 0x0200000,
+ .end = 0x021ffff,
+ .name = "qsys",
+ }
+ },
+ { .id = REW,
+ {
+ .start = 0x0030000,
+ .end = 0x003ffff,
+ .name = "rew",
+ }
+ },
+ { .id = SYS,
+ {
+ .start = 0x0010000,
+ .end = 0x001ffff,
+ .name = "sys",
+ }
+ },
+ { .id = S2,
+ {
+ .start = 0x0060000,
+ .end = 0x00603ff,
+ .name = "s2",
+ }
+ },
+ { .id = GCB,
+ {
+ .start = 0x0070000,
+ .end = 0x00701ff,
+ .name = "devcpu_gcb",
+ }
+ }
+};
+
+#define FELIX_MAX_NUM_PHY_PORTS 6
+
+#define FELIX_PORT_RES_START 0x0100000
+#define FELIX_PORT_RES_SIZE 0x10000
+
+static void __iomem *regs;
+
+static void felix_release_ports(struct ocelot *ocelot)
+{
+ struct ocelot_port *ocelot_port;
+ struct phy_device *phydev;
+ struct device_node *dn;
+ int i;
+
+ for (i = 0; i < ocelot->num_phys_ports; i++) {
+ ocelot_port = ocelot->ports[i];
+ if (!ocelot_port || !ocelot_port->phy || !ocelot_port->dev)
+ continue;
+
+ phydev = ocelot_port->phy;
+ unregister_netdev(ocelot_port->dev);
+ free_netdev(ocelot_port->dev);
+
+ if (phy_is_pseudo_fixed_link(phydev)) {
+ dn = phydev->mdio.dev.of_node;
+ /* decr refcnt: of_phy_register_fixed_link */
+ of_phy_deregister_fixed_link(dn);
+ }
+ phy_device_free(phydev); /* decr refcnt: of_phy_find_device */
+ }
+}
+
+static int felix_ports_init(struct pci_dev *pdev)
+{
+ struct ocelot *ocelot = pci_get_drvdata(pdev);
+ struct device_node *np = ocelot->dev->of_node;
+ struct device_node *phy_node, *portnp;
+ struct phy_device *phydev;
+ void __iomem *port_regs;
+ resource_size_t base;
+ u32 port;
+ int err;
+
+ ocelot->num_phys_ports = FELIX_MAX_NUM_PHY_PORTS;
+
+ np = of_get_child_by_name(np, "ethernet-ports");
+ if (!np) {
+ dev_err(&pdev->dev, "ethernet-ports sub-node not found\n");
+ return -ENODEV;
+ }
+
+ /* alloc netdev for each port */
+ err = ocelot_init(ocelot);
+ if (err)
+ return err;
+
+ base = pci_resource_start(pdev, FELIX_SWITCH_BAR);
+ for_each_available_child_of_node(np, portnp) {
+ struct resource res = {};
+ int phy_mode;
+
+ if (!portnp || !portnp->name ||
+ of_node_cmp(portnp->name, "port") ||
+ of_property_read_u32(portnp, "reg", &port))
+ continue;
+
+ if (port >= FELIX_MAX_NUM_PHY_PORTS) {
+ dev_err(ocelot->dev, "invalid port num: %d\n", port);
+ continue;
+ }
+
+ res.start = base + FELIX_PORT_RES_START +
+ FELIX_PORT_RES_SIZE * port;
+ res.end = res.start + FELIX_PORT_RES_SIZE - 1;
+ res.flags = IORESOURCE_MEM;
+ res.name = "port";
+
+ port_regs = devm_ioremap_resource(ocelot->dev, &res);
+ if (IS_ERR(port_regs)) {
+ dev_err(ocelot->dev,
+ "failed to map registers for port %d\n", port);
+ continue;
+ }
+
+ phy_node = of_parse_phandle(portnp, "phy-handle", 0);
+ if (!phy_node) {
+ if (!of_phy_is_fixed_link(portnp))
+ continue;
+
+ err = of_phy_register_fixed_link(portnp);
+ if (err < 0) {
+ dev_err(ocelot->dev,
+ "can't create fixed link for port:%d\n",
+ port);
+ continue;
+ }
+ phydev = of_phy_find_device(portnp);
+ } else {
+ phydev = of_phy_find_device(phy_node);
+ }
+
+ of_node_put(phy_node);
+
+ if (!phydev)
+ continue;
+
+ phy_mode = of_get_phy_mode(portnp);
+ if (phy_mode < 0)
+ phy_mode = PHY_INTERFACE_MODE_NA;
+
+ err = ocelot_probe_port(ocelot, port, port_regs, phydev);
+ if (err) {
+ dev_err(ocelot->dev, "failed to probe ports\n");
+ goto release_ports;
+ }
+
+ /* Felix configs */
+ ocelot->ports[port]->phy_mode = phy_mode;
+ }
+
+ return 0;
+
+release_ports:
+ felix_release_ports(ocelot);
+
+ return err;
+}
+
+#define FELIX_INIT_TIMEOUT 50000
+#define FELIX_GCB_RST_SLEEP 100
+#define FELIX_SYS_RAMINIT_SLEEP 80
+
+static int felix_gcb_soft_rst_status(struct ocelot *ocelot)
+{
+ int val;
+
+ regmap_field_read(ocelot->regfields[GCB_SOFT_RST_SWC_RST], &val);
+ return val;
+}
+
+static int felix_sys_ram_init_status(struct ocelot *ocelot)
+{
+ return ocelot_read(ocelot, SYS_RAM_INIT);
+}
+
+static int felix_init_switch_core(struct ocelot *ocelot)
+{
+ int val, err;
+
+ /* soft-reset the switch core */
+ regmap_field_write(ocelot->regfields[GCB_SOFT_RST_SWC_RST], 1);
+
+ err = readx_poll_timeout(felix_gcb_soft_rst_status, ocelot, val, !val,
+ FELIX_GCB_RST_SLEEP, FELIX_INIT_TIMEOUT);
+ if (err) {
+ dev_err(ocelot->dev, "timeout: switch core reset\n");
+ return err;
+ }
+
+ /* initialize switch mem ~40us */
+ ocelot_write(ocelot, SYS_RAM_INIT_RAM_INIT, SYS_RAM_INIT);
+ err = readx_poll_timeout(felix_sys_ram_init_status, ocelot, val, !val,
+ FELIX_SYS_RAMINIT_SLEEP, FELIX_INIT_TIMEOUT);
+ if (err) {
+ dev_err(ocelot->dev, "timeout: switch sram init\n");
+ return err;
+ }
+
+ /* enable switch core */
+ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1);
+
+ return 0;
+}
+
+static int felix_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct ocelot *ocelot;
+ resource_size_t base;
+ size_t len;
+ int i, err;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "device enable failed\n");
+ return err;
+ }
+
+ /* set up for high or low dma */
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err) {
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev,
+ "DMA configuration failed: 0x%x\n", err);
+ goto err_dma;
+ }
+ }
+
+ base = pci_resource_start(pdev, FELIX_SWITCH_BAR);
+
+ pci_set_master(pdev);
+
+ ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL);
+ if (!ocelot) {
+ err = -ENOMEM;
+ goto err_alloc_ocelot;
+ }
+
+ pci_set_drvdata(pdev, ocelot);
+ ocelot->dev = &pdev->dev;
+
+ len = pci_resource_len(pdev, FELIX_SWITCH_BAR);
+ if (!len) {
+ err = -EINVAL;
+ goto err_resource_len;
+ }
+
+ regs = pci_iomap(pdev, FELIX_SWITCH_BAR, len);
+ if (!regs) {
+ err = -ENXIO;
+ dev_err(&pdev->dev, "ioremap() failed\n");
+ goto err_iomap;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(felix_io_res); i++) {
+ struct resource *res = &felix_io_res[i].res;
+ struct regmap *target;
+
+ res->flags = IORESOURCE_MEM;
+ res->start += base;
+ res->end += base;
+
+ target = ocelot_io_init(ocelot, res);
+ if (IS_ERR(target)) {
+ err = PTR_ERR(target);
+ goto err_iomap;
+ }
+
+ ocelot->targets[felix_io_res[i].id] = target;
+ }
+
+ err = felix_chip_init(ocelot);
+ if (err)
+ goto err_chip_init;
+
+ err = felix_init_switch_core(ocelot);
+ if (err)
+ goto err_sw_core_init;
+
+ err = felix_ports_init(pdev);
+ if (err)
+ goto err_ports_init;
+
+ register_netdevice_notifier(&ocelot_netdevice_nb);
+ register_switchdev_notifier(&ocelot_switchdev_nb);
+ register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
+
+ dev_info(&pdev->dev, "%s v%s\n", FELIX_DRV_STR, FELIX_DRV_VER_STR);
+ return 0;
+
+err_ports_init:
+err_chip_init:
+err_sw_core_init:
+ pci_iounmap(pdev, regs);
+err_iomap:
+err_resource_len:
+err_alloc_ocelot:
+err_dma:
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static void felix_pci_remove(struct pci_dev *pdev)
+{
+ struct ocelot *ocelot;
+
+ ocelot = pci_get_drvdata(pdev);
+
+ /* stop workqueue thread */
+ ocelot_deinit(ocelot);
+ unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
+ unregister_switchdev_notifier(&ocelot_switchdev_nb);
+ unregister_netdevice_notifier(&ocelot_netdevice_nb);
+
+ felix_release_ports(ocelot);
+
+ pci_iounmap(pdev, regs);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver felix_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = felix_ids,
+ .probe = felix_pci_probe,
+ .remove = felix_pci_remove,
+};
+
+module_pci_driver(felix_pci_driver);
+
+MODULE_DESCRIPTION(FELIX_DRV_STR);
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/net/ethernet/mscc/felix_regs.c b/drivers/net/ethernet/mscc/felix_regs.c
new file mode 100644
index 000000000000..33e545505b4e
--- /dev/null
+++ b/drivers/net/ethernet/mscc/felix_regs.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Felix Switch driver
+ *
+ * Copyright 2017 Microsemi Corporation
+ * Copyright 2018-2019 NXP
+ */
+#include "ocelot.h"
+
+static const u32 felix_ana_regmap[] = {
+ REG(ANA_ADVLEARN, 0x0089a0),
+ REG(ANA_VLANMASK, 0x0089a4),
+ REG_RESERVED(ANA_PORT_B_DOMAIN),
+ REG(ANA_ANAGEFIL, 0x0089ac),
+ REG(ANA_ANEVENTS, 0x0089b0),
+ REG(ANA_STORMLIMIT_BURST, 0x0089b4),
+ REG(ANA_STORMLIMIT_CFG, 0x0089b8),
+ REG(ANA_ISOLATED_PORTS, 0x0089c8),
+ REG(ANA_COMMUNITY_PORTS, 0x0089cc),
+ REG(ANA_AUTOAGE, 0x0089d0),
+ REG(ANA_MACTOPTIONS, 0x0089d4),
+ REG(ANA_LEARNDISC, 0x0089d8),
+ REG(ANA_AGENCTRL, 0x0089dc),
+ REG(ANA_MIRRORPORTS, 0x0089e0),
+ REG(ANA_EMIRRORPORTS, 0x0089e4),
+ REG(ANA_FLOODING, 0x0089e8),
+ REG(ANA_FLOODING_IPMC, 0x008a08),
+ REG(ANA_SFLOW_CFG, 0x008a0c),
+ REG(ANA_PORT_MODE, 0x008a28),
+ REG(ANA_CUT_THRU_CFG, 0x008a48),
+ REG(ANA_PGID_PGID, 0x008400),
+ REG(ANA_TABLES_ANMOVED, 0x007f1c),
+ REG(ANA_TABLES_MACHDATA, 0x007f20),
+ REG(ANA_TABLES_MACLDATA, 0x007f24),
+ REG(ANA_TABLES_STREAMDATA, 0x007f28),
+ REG(ANA_TABLES_MACACCESS, 0x007f2c),
+ REG(ANA_TABLES_MACTINDX, 0x007f30),
+ REG(ANA_TABLES_VLANACCESS, 0x007f34),
+ REG(ANA_TABLES_VLANTIDX, 0x007f38),
+ REG(ANA_TABLES_ISDXACCESS, 0x007f3c),
+ REG(ANA_TABLES_ISDXTIDX, 0x007f40),
+ REG(ANA_TABLES_ENTRYLIM, 0x007f00),
+ REG(ANA_TABLES_PTP_ID_HIGH, 0x007f44),
+ REG(ANA_TABLES_PTP_ID_LOW, 0x007f48),
+ REG(ANA_TABLES_STREAMACCESS, 0x007f4c),
+ REG(ANA_TABLES_STREAMTIDX, 0x007f50),
+ REG(ANA_TABLES_SEQ_HISTORY, 0x007f54),
+ REG(ANA_TABLES_SEQ_MASK, 0x007f58),
+ REG(ANA_TABLES_SFID_MASK, 0x007f5c),
+ REG(ANA_TABLES_SFIDACCESS, 0x007f60),
+ REG(ANA_TABLES_SFIDTIDX, 0x007f64),
+ REG(ANA_MSTI_STATE, 0x008600),
+ REG(ANA_OAM_UPM_LM_CNT, 0x008000),
+ REG(ANA_SG_ACCESS_CTRL, 0x008a64),
+ REG(ANA_SG_CONFIG_REG_1, 0x007fb0),
+ REG(ANA_SG_CONFIG_REG_2, 0x007fb4),
+ REG(ANA_SG_CONFIG_REG_3, 0x007fb8),
+ REG(ANA_SG_CONFIG_REG_4, 0x007fbc),
+ REG(ANA_SG_CONFIG_REG_5, 0x007fc0),
+ REG(ANA_SG_GCL_GS_CONFIG, 0x007f80),
+ REG(ANA_SG_GCL_TI_CONFIG, 0x007f90),
+ REG(ANA_SG_STATUS_REG_1, 0x008980),
+ REG(ANA_SG_STATUS_REG_2, 0x008984),
+ REG(ANA_SG_STATUS_REG_3, 0x008988),
+ REG(ANA_PORT_VLAN_CFG, 0x007800),
+ REG(ANA_PORT_DROP_CFG, 0x007804),
+ REG(ANA_PORT_QOS_CFG, 0x007808),
+ REG(ANA_PORT_VCAP_CFG, 0x00780c),
+ REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007810),
+ REG(ANA_PORT_VCAP_S2_CFG, 0x00781c),
+ REG(ANA_PORT_PCP_DEI_MAP, 0x007820),
+ REG(ANA_PORT_CPU_FWD_CFG, 0x007860),
+ REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007864),
+ REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007868),
+ REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00786c),
+ REG(ANA_PORT_PORT_CFG, 0x007870),
+ REG(ANA_PORT_POL_CFG, 0x007874),
+ REG(ANA_PORT_PTP_CFG, 0x007878),
+ REG(ANA_PORT_PTP_DLY1_CFG, 0x00787c),
+ REG(ANA_PORT_PTP_DLY2_CFG, 0x007880),
+ REG(ANA_PORT_SFID_CFG, 0x007884),
+ REG(ANA_PFC_PFC_CFG, 0x008800),
+ REG_RESERVED(ANA_PFC_PFC_TIMER),
+ REG_RESERVED(ANA_IPT_OAM_MEP_CFG),
+ REG_RESERVED(ANA_IPT_IPT),
+ REG_RESERVED(ANA_PPT_PPT),
+ REG_RESERVED(ANA_FID_MAP_FID_MAP),
+ REG(ANA_AGGR_CFG, 0x008a68),
+ REG(ANA_CPUQ_CFG, 0x008a6c),
+ REG_RESERVED(ANA_CPUQ_CFG2),
+ REG(ANA_CPUQ_8021_CFG, 0x008a74),
+ REG(ANA_DSCP_CFG, 0x008ab4),
+ REG(ANA_DSCP_REWR_CFG, 0x008bb4),
+ REG(ANA_VCAP_RNG_TYPE_CFG, 0x008bf4),
+ REG(ANA_VCAP_RNG_VAL_CFG, 0x008c14),
+ REG_RESERVED(ANA_VRAP_CFG),
+ REG_RESERVED(ANA_VRAP_HDR_DATA),
+ REG_RESERVED(ANA_VRAP_HDR_MASK),
+ REG(ANA_DISCARD_CFG, 0x008c40),
+ REG(ANA_FID_CFG, 0x008c44),
+ 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_RESERVED(ANA_POL_STATE),
+ REG(ANA_POL_FLOWC, 0x008c48),
+ REG(ANA_POL_HYST, 0x008cb4),
+ REG_RESERVED(ANA_POL_MISC_CFG),
+};
+
+static const u32 felix_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_RESERVED(QS_INH_DBG),
+};
+
+static const u32 felix_s2_regmap[] = {
+ REG(S2_CORE_UPDATE_CTRL, 0x000000),
+ REG(S2_CORE_MV_CFG, 0x000004),
+ REG(S2_CACHE_ENTRY_DAT, 0x000008),
+ REG(S2_CACHE_MASK_DAT, 0x000108),
+ REG(S2_CACHE_ACTION_DAT, 0x000208),
+ REG(S2_CACHE_CNT_DAT, 0x000308),
+ REG(S2_CACHE_TG_DAT, 0x000388),
+};
+
+static const u32 felix_qsys_regmap[] = {
+ REG(QSYS_PORT_MODE, 0x00f460),
+ REG(QSYS_SWITCH_PORT_MODE, 0x00f480),
+ REG(QSYS_STAT_CNT_CFG, 0x00f49c),
+ REG(QSYS_EEE_CFG, 0x00f4a0),
+ REG(QSYS_EEE_THRES, 0x00f4b8),
+ REG(QSYS_IGR_NO_SHARING, 0x00f4bc),
+ REG(QSYS_EGR_NO_SHARING, 0x00f4c0),
+ REG(QSYS_SW_STATUS, 0x00f4c4),
+ REG(QSYS_EXT_CPU_CFG, 0x00f4e0),
+ REG_RESERVED(QSYS_PAD_CFG),
+ REG(QSYS_CPU_GROUP_MAP, 0x00f4e8),
+ REG_RESERVED(QSYS_QMAP),
+ REG_RESERVED(QSYS_ISDX_SGRP),
+ REG_RESERVED(QSYS_TIMED_FRAME_ENTRY),
+ REG(QSYS_TFRM_MISC, 0x00f50c),
+ REG(QSYS_TFRM_PORT_DLY, 0x00f510),
+ REG(QSYS_TFRM_TIMER_CFG_1, 0x00f514),
+ REG(QSYS_TFRM_TIMER_CFG_2, 0x00f518),
+ REG(QSYS_TFRM_TIMER_CFG_3, 0x00f51c),
+ REG(QSYS_TFRM_TIMER_CFG_4, 0x00f520),
+ REG(QSYS_TFRM_TIMER_CFG_5, 0x00f524),
+ REG(QSYS_TFRM_TIMER_CFG_6, 0x00f528),
+ REG(QSYS_TFRM_TIMER_CFG_7, 0x00f52c),
+ REG(QSYS_TFRM_TIMER_CFG_8, 0x00f530),
+ REG(QSYS_RED_PROFILE, 0x00f534),
+ REG(QSYS_RES_QOS_MODE, 0x00f574),
+ REG(QSYS_RES_CFG, 0x00c000),
+ REG(QSYS_RES_STAT, 0x00c004),
+ REG(QSYS_EGR_DROP_MODE, 0x00f578),
+ REG(QSYS_EQ_CTRL, 0x00f57c),
+ REG_RESERVED(QSYS_EVENTS_CORE),
+ REG(QSYS_QMAXSDU_CFG_0, 0x00f584),
+ REG(QSYS_QMAXSDU_CFG_1, 0x00f5a0),
+ REG(QSYS_QMAXSDU_CFG_2, 0x00f5bc),
+ REG(QSYS_QMAXSDU_CFG_3, 0x00f5d8),
+ REG(QSYS_QMAXSDU_CFG_4, 0x00f5f4),
+ REG(QSYS_QMAXSDU_CFG_5, 0x00f610),
+ REG(QSYS_QMAXSDU_CFG_6, 0x00f62c),
+ REG(QSYS_QMAXSDU_CFG_7, 0x00f648),
+ REG(QSYS_PREEMPTION_CFG, 0x00f664),
+ REG_RESERVED(QSYS_CIR_CFG),
+ REG(QSYS_EIR_CFG, 0x000004),
+ REG(QSYS_SE_CFG, 0x000008),
+ REG(QSYS_SE_DWRR_CFG, 0x00000c),
+ REG_RESERVED(QSYS_SE_CONNECT),
+ REG(QSYS_SE_DLB_SENSE, 0x000040),
+ REG(QSYS_CIR_STATE, 0x000044),
+ REG(QSYS_EIR_STATE, 0x000048),
+ REG_RESERVED(QSYS_SE_STATE),
+ REG(QSYS_HSCH_MISC_CFG, 0x00f67c),
+ REG(QSYS_TAG_CONFIG, 0x00f680),
+ REG(QSYS_TAS_PARAM_CFG_CTRL, 0x00f698),
+ REG(QSYS_PORT_MAX_SDU, 0x00f69c),
+ REG(QSYS_PARAM_CFG_REG_1, 0x00f440),
+ REG(QSYS_PARAM_CFG_REG_2, 0x00f444),
+ REG(QSYS_PARAM_CFG_REG_3, 0x00f448),
+ REG(QSYS_PARAM_CFG_REG_4, 0x00f44c),
+ REG(QSYS_PARAM_CFG_REG_5, 0x00f450),
+ REG(QSYS_GCL_CFG_REG_1, 0x00f454),
+ REG(QSYS_GCL_CFG_REG_2, 0x00f458),
+ REG(QSYS_PARAM_STATUS_REG_1, 0x00f400),
+ REG(QSYS_PARAM_STATUS_REG_2, 0x00f404),
+ REG(QSYS_PARAM_STATUS_REG_3, 0x00f408),
+ REG(QSYS_PARAM_STATUS_REG_4, 0x00f40c),
+ REG(QSYS_PARAM_STATUS_REG_5, 0x00f410),
+ REG(QSYS_PARAM_STATUS_REG_6, 0x00f414),
+ REG(QSYS_PARAM_STATUS_REG_7, 0x00f418),
+ REG(QSYS_PARAM_STATUS_REG_8, 0x00f41c),
+ REG(QSYS_PARAM_STATUS_REG_9, 0x00f420),
+ REG(QSYS_GCL_STATUS_REG_1, 0x00f424),
+ REG(QSYS_GCL_STATUS_REG_2, 0x00f428),
+};
+
+static const u32 felix_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_RED_TAG_CFG, 0x000058),
+ REG(REW_DSCP_REMAP_DP1_CFG, 0x000410),
+ REG(REW_DSCP_REMAP_CFG, 0x000510),
+ REG_RESERVED(REW_STAT_CFG),
+ REG_RESERVED(REW_REW_STICKY),
+ REG_RESERVED(REW_PPT),
+};
+
+static const u32 felix_sys_regmap[] = {
+ REG(SYS_COUNT_RX_OCTETS, 0x000000),
+ REG(SYS_COUNT_RX_MULTICAST, 0x000008),
+ REG(SYS_COUNT_RX_SHORTS, 0x000010),
+ REG(SYS_COUNT_RX_FRAGMENTS, 0x000014),
+ REG(SYS_COUNT_RX_JABBERS, 0x000018),
+ 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_LONGS, 0x000044),
+ REG(SYS_COUNT_TX_OCTETS, 0x000200),
+ REG(SYS_COUNT_TX_COLLISION, 0x000210),
+ REG(SYS_COUNT_TX_DROPS, 0x000214),
+ REG(SYS_COUNT_TX_64, 0x00021c),
+ REG(SYS_COUNT_TX_65_127, 0x000220),
+ REG(SYS_COUNT_TX_128_511, 0x000224),
+ REG(SYS_COUNT_TX_512_1023, 0x000228),
+ REG(SYS_COUNT_TX_1024_1526, 0x00022c),
+ REG(SYS_COUNT_TX_1527_MAX, 0x000230),
+ REG(SYS_COUNT_TX_AGING, 0x000278),
+ REG(SYS_RESET_CFG, 0x000e00),
+ REG(SYS_SR_ETYPE_CFG, 0x000e04),
+ REG(SYS_VLAN_ETYPE_CFG, 0x000e08),
+ REG(SYS_PORT_MODE, 0x000e0c),
+ REG(SYS_FRONT_PORT_MODE, 0x000e2c),
+ REG(SYS_FRM_AGING, 0x000e44),
+ REG(SYS_STAT_CFG, 0x000e48),
+ REG(SYS_SW_STATUS, 0x000e4c),
+ REG_RESERVED(SYS_MISC_CFG),
+ REG(SYS_REW_MAC_HIGH_CFG, 0x000e6c),
+ REG(SYS_REW_MAC_LOW_CFG, 0x000e84),
+ REG(SYS_TIMESTAMP_OFFSET, 0x000e9c),
+ REG(SYS_PAUSE_CFG, 0x000ea0),
+ REG(SYS_PAUSE_TOT_CFG, 0x000ebc),
+ REG(SYS_ATOP, 0x000ec0),
+ REG(SYS_ATOP_TOT_CFG, 0x000edc),
+ REG(SYS_MAC_FC_CFG, 0x000ee0),
+ REG(SYS_MMGT, 0x000ef8),
+ REG_RESERVED(SYS_MMGT_FAST),
+ REG_RESERVED(SYS_EVENTS_DIF),
+ REG_RESERVED(SYS_EVENTS_CORE),
+ REG_RESERVED(SYS_CNT),
+ REG(SYS_PTP_STATUS, 0x000f14),
+ REG(SYS_PTP_TXSTAMP, 0x000f18),
+ REG(SYS_PTP_NXT, 0x000f1c),
+ REG(SYS_PTP_CFG, 0x000f20),
+ REG(SYS_RAM_INIT, 0x000f24),
+ REG_RESERVED(SYS_CM_ADDR),
+ REG_RESERVED(SYS_CM_DATA_WR),
+ REG_RESERVED(SYS_CM_DATA_RD),
+ REG_RESERVED(SYS_CM_OP),
+ REG_RESERVED(SYS_CM_DATA),
+};
+
+static const u32 felix_gcb_regmap[] = {
+ REG(GCB_SOFT_RST, 0x000004),
+};
+
+static const u32 *felix_regmap[] = {
+ [ANA] = felix_ana_regmap,
+ [QS] = felix_qs_regmap,
+ [QSYS] = felix_qsys_regmap,
+ [REW] = felix_rew_regmap,
+ [SYS] = felix_sys_regmap,
+ [S2] = felix_s2_regmap,
+ [GCB] = felix_gcb_regmap,
+};
+
+static const struct reg_field felix_regfields[REGFIELD_MAX] = {
+ [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6),
+ [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5),
+ [ANA_ANEVENTS_FLOOD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 30, 30),
+ [ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 26, 26),
+ [ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 24, 24),
+ [ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 23, 23),
+ [ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 22, 22),
+ [ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 21, 21),
+ [ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 20, 20),
+ [ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 19, 19),
+ [ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
+ [ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 17, 17),
+ [ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 15, 15),
+ [ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 14, 14),
+ [ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 13, 13),
+ [ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 12, 12),
+ [ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
+ [ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
+ [ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 9, 9),
+ [ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 8, 8),
+ [ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 7, 7),
+ [ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
+ [ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
+ [ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 4, 4),
+ [ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 3, 3),
+ [ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 2, 2),
+ [ANA_ANEVENTS_SEQ_GEN_ERR_0] = REG_FIELD(ANA_ANEVENTS, 1, 1),
+ [ANA_ANEVENTS_SEQ_GEN_ERR_1] = REG_FIELD(ANA_ANEVENTS, 0, 0),
+ [ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 16, 16),
+ [ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 11, 12),
+ [ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 10),
+ [SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 0, 0),
+ [GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0),
+};
+
+static const struct ocelot_stat_layout felix_stats_layout[] = {
+ { .name = "rx_octets", .offset = 0x00, },
+ { .name = "rx_unicast", .offset = 0x01, },
+ { .name = "rx_multicast", .offset = 0x02, },
+ { .name = "rx_broadcast", .offset = 0x03, },
+ { .name = "rx_shorts", .offset = 0x04, },
+ { .name = "rx_fragments", .offset = 0x05, },
+ { .name = "rx_jabbers", .offset = 0x06, },
+ { .name = "rx_crc_align_errs", .offset = 0x07, },
+ { .name = "rx_sym_errs", .offset = 0x08, },
+ { .name = "rx_frames_below_65_octets", .offset = 0x09, },
+ { .name = "rx_frames_65_to_127_octets", .offset = 0x0A, },
+ { .name = "rx_frames_128_to_255_octets", .offset = 0x0B, },
+ { .name = "rx_frames_256_to_511_octets", .offset = 0x0C, },
+ { .name = "rx_frames_512_to_1023_octets", .offset = 0x0D, },
+ { .name = "rx_frames_1024_to_1526_octets", .offset = 0x0E, },
+ { .name = "rx_frames_over_1526_octets", .offset = 0x0F, },
+ { .name = "rx_pause", .offset = 0x10, },
+ { .name = "rx_control", .offset = 0x11, },
+ { .name = "rx_longs", .offset = 0x12, },
+ { .name = "rx_classified_drops", .offset = 0x13, },
+ { .name = "rx_red_prio_0", .offset = 0x14, },
+ { .name = "rx_red_prio_1", .offset = 0x15, },
+ { .name = "rx_red_prio_2", .offset = 0x16, },
+ { .name = "rx_red_prio_3", .offset = 0x17, },
+ { .name = "rx_red_prio_4", .offset = 0x18, },
+ { .name = "rx_red_prio_5", .offset = 0x19, },
+ { .name = "rx_red_prio_6", .offset = 0x1A, },
+ { .name = "rx_red_prio_7", .offset = 0x1B, },
+ { .name = "rx_yellow_prio_0", .offset = 0x1C, },
+ { .name = "rx_yellow_prio_1", .offset = 0x1D, },
+ { .name = "rx_yellow_prio_2", .offset = 0x1E, },
+ { .name = "rx_yellow_prio_3", .offset = 0x1F, },
+ { .name = "rx_yellow_prio_4", .offset = 0x20, },
+ { .name = "rx_yellow_prio_5", .offset = 0x21, },
+ { .name = "rx_yellow_prio_6", .offset = 0x22, },
+ { .name = "rx_yellow_prio_7", .offset = 0x23, },
+ { .name = "rx_green_prio_0", .offset = 0x24, },
+ { .name = "rx_green_prio_1", .offset = 0x25, },
+ { .name = "rx_green_prio_2", .offset = 0x26, },
+ { .name = "rx_green_prio_3", .offset = 0x27, },
+ { .name = "rx_green_prio_4", .offset = 0x28, },
+ { .name = "rx_green_prio_5", .offset = 0x29, },
+ { .name = "rx_green_prio_6", .offset = 0x2A, },
+ { .name = "rx_green_prio_7", .offset = 0x2B, },
+ { .name = "tx_octets", .offset = 0x80, },
+ { .name = "tx_unicast", .offset = 0x81, },
+ { .name = "tx_multicast", .offset = 0x82, },
+ { .name = "tx_broadcast", .offset = 0x83, },
+ { .name = "tx_collision", .offset = 0x84, },
+ { .name = "tx_drops", .offset = 0x85, },
+ { .name = "tx_pause", .offset = 0x86, },
+ { .name = "tx_frames_below_65_octets", .offset = 0x87, },
+ { .name = "tx_frames_65_to_127_octets", .offset = 0x88, },
+ { .name = "tx_frames_128_255_octets", .offset = 0x89, },
+ { .name = "tx_frames_256_511_octets", .offset = 0x8A, },
+ { .name = "tx_frames_512_1023_octets", .offset = 0x8B, },
+ { .name = "tx_frames_1024_1526_octets", .offset = 0x8C, },
+ { .name = "tx_frames_over_1526_octets", .offset = 0x8D, },
+ { .name = "tx_yellow_prio_0", .offset = 0x8E, },
+ { .name = "tx_yellow_prio_1", .offset = 0x8F, },
+ { .name = "tx_yellow_prio_2", .offset = 0x90, },
+ { .name = "tx_yellow_prio_3", .offset = 0x91, },
+ { .name = "tx_yellow_prio_4", .offset = 0x92, },
+ { .name = "tx_yellow_prio_5", .offset = 0x93, },
+ { .name = "tx_yellow_prio_6", .offset = 0x94, },
+ { .name = "tx_yellow_prio_7", .offset = 0x95, },
+ { .name = "tx_green_prio_0", .offset = 0x96, },
+ { .name = "tx_green_prio_1", .offset = 0x97, },
+ { .name = "tx_green_prio_2", .offset = 0x98, },
+ { .name = "tx_green_prio_3", .offset = 0x99, },
+ { .name = "tx_green_prio_4", .offset = 0x9A, },
+ { .name = "tx_green_prio_5", .offset = 0x9B, },
+ { .name = "tx_green_prio_6", .offset = 0x9C, },
+ { .name = "tx_green_prio_7", .offset = 0x9D, },
+ { .name = "tx_aged", .offset = 0x9E, },
+ { .name = "drop_local", .offset = 0x100, },
+ { .name = "drop_tail", .offset = 0x101, },
+ { .name = "drop_yellow_prio_0", .offset = 0x102, },
+ { .name = "drop_yellow_prio_1", .offset = 0x103, },
+ { .name = "drop_yellow_prio_2", .offset = 0x104, },
+ { .name = "drop_yellow_prio_3", .offset = 0x105, },
+ { .name = "drop_yellow_prio_4", .offset = 0x106, },
+ { .name = "drop_yellow_prio_5", .offset = 0x107, },
+ { .name = "drop_yellow_prio_6", .offset = 0x108, },
+ { .name = "drop_yellow_prio_7", .offset = 0x109, },
+ { .name = "drop_green_prio_0", .offset = 0x10A, },
+ { .name = "drop_green_prio_1", .offset = 0x10B, },
+ { .name = "drop_green_prio_2", .offset = 0x10C, },
+ { .name = "drop_green_prio_3", .offset = 0x10D, },
+ { .name = "drop_green_prio_4", .offset = 0x10E, },
+ { .name = "drop_green_prio_5", .offset = 0x10F, },
+ { .name = "drop_green_prio_6", .offset = 0x110, },
+ { .name = "drop_green_prio_7", .offset = 0x111, },
+};
+
+int felix_chip_init(struct ocelot *ocelot)
+{
+ int ret;
+
+ ocelot->map = felix_regmap;
+ ocelot->stats_layout = felix_stats_layout;
+ ocelot->num_stats = ARRAY_SIZE(felix_stats_layout);
+ ocelot->shared_queue_sz = 128 * 1024;
+
+ ret = ocelot_regfields_init(ocelot, felix_regfields);
+ if (ret) {
+ dev_err(ocelot->dev, "failed to init reg fields map\n");
+ return ret;
+ }
+
+ eth_random_addr(ocelot->base_mac);
+ ocelot->base_mac[5] &= 0xf0;
+ return 0;
+}
+EXPORT_SYMBOL(felix_chip_init);
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index 4235d7294772..1d9e76584037 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -63,6 +63,9 @@ struct frame_info {
#define REG_MASK GENMASK(TARGET_OFFSET - 1, 0)
#define REG(reg, offset) [reg & REG_MASK] = offset

+#define REG_RESERVED_ADDR 0xffffffff
+#define REG_RESERVED(reg) REG(reg, REG_RESERVED_ADDR)
+
enum ocelot_target {
ANA = 1,
QS,
@@ -70,6 +73,7 @@ enum ocelot_target {
REW,
SYS,
S2,
+ GCB,
HSIO,
TARGET_MAX,
};
@@ -343,6 +347,7 @@ enum ocelot_reg {
S2_CACHE_ACTION_DAT,
S2_CACHE_CNT_DAT,
S2_CACHE_TG_DAT,
+ GCB_SOFT_RST = GCB << TARGET_OFFSET,
};

enum ocelot_regfield {
@@ -390,6 +395,7 @@ enum ocelot_regfield {
SYS_RESET_CFG_CORE_ENA,
SYS_RESET_CFG_MEM_ENA,
SYS_RESET_CFG_MEM_INIT,
+ GCB_SOFT_RST_SWC_RST,
REGFIELD_MAX
};

@@ -501,6 +507,7 @@ struct regmap *ocelot_io_init(struct ocelot *ocelot, struct resource *res);
int ocelot_init(struct ocelot *ocelot);
void ocelot_deinit(struct ocelot *ocelot);
int ocelot_chip_init(struct ocelot *ocelot);
+int felix_chip_init(struct ocelot *ocelot);
int ocelot_probe_port(struct ocelot *ocelot, u8 port,
void __iomem *regs,
struct phy_device *phy);
--
2.17.1


2019-06-22 21:00:32

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next 6/6] net/mssc/ocelot: Add basic Felix switch driver

On Fri, Jun 21, 2019 at 06:38:52PM +0300, Claudiu Manoil wrote:
> This supports a switch core ethernet device from Microsemi
> (VSC9959) that can be integrated on different SoCs as a PCIe
> endpoint device.
>
> The switchdev functionality is provided by the core Ocelot
> switch driver. In this regard, the current driver is an
> instance of Microsemi's Ocelot core driver.
>
> The patch adds the PCI device driver part and defines the
> register map for the Felix switch core, as it has some
> differences in register addresses and bitfield mappings
> compared to the Ocelot switch. Also some registers or
> bitfields present on Ocelot are not available on Felix.
> That's why this driver has its own register map instance.
> Other than that, the common registers and bitfields have the
> same functionality and share the same name.
>
> In a few cases, some h/w operations have to be done differently
> on Felix due to missing bitfields. This is the case for the
> switch core reset and init. Because for this operation Ocelot
> uses some bits that are not present on Felix, the later has to
> use a register from the global registers block (GCB) instead.
>
> Signed-off-by: Catalin Horghidan <[email protected]>
> Signed-off-by: Claudiu Manoil <[email protected]>
> ---
> drivers/net/ethernet/mscc/Kconfig | 8 +
> drivers/net/ethernet/mscc/Makefile | 9 +-
> drivers/net/ethernet/mscc/felix_board.c | 392 +++++++++++++++++++++
> drivers/net/ethernet/mscc/felix_regs.c | 448 ++++++++++++++++++++++++
> drivers/net/ethernet/mscc/ocelot.h | 7 +
> 5 files changed, 862 insertions(+), 2 deletions(-)
> create mode 100644 drivers/net/ethernet/mscc/felix_board.c
> create mode 100644 drivers/net/ethernet/mscc/felix_regs.c
>
> diff --git a/drivers/net/ethernet/mscc/Kconfig b/drivers/net/ethernet/mscc/Kconfig
> index bcec0587cf61..e5a7fa69307e 100644
> --- a/drivers/net/ethernet/mscc/Kconfig
> +++ b/drivers/net/ethernet/mscc/Kconfig
> @@ -29,4 +29,12 @@ config MSCC_OCELOT_SWITCH_OCELOT
> This driver supports the Ocelot network switch device as present on
> the Ocelot SoCs.
>
> +config MSCC_FELIX_SWITCH
> + tristate "Felix switch driver"
> + depends on MSCC_OCELOT_SWITCH
> + depends on PCI
> + help
> + This driver supports the Felix network switch device, connected as a
> + PCI device.
> +
> endif # NET_VENDOR_MICROSEMI
> diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
> index 9a36c26095c8..81593feb2ea1 100644
> --- a/drivers/net/ethernet/mscc/Makefile
> +++ b/drivers/net/ethernet/mscc/Makefile
> @@ -1,5 +1,10 @@
> # SPDX-License-Identifier: (GPL-2.0 OR MIT)
> obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
> mscc_ocelot_common-y := ocelot.o ocelot_io.o
> -mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o
> -obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
> +mscc_ocelot_common-y += ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o
> +
> +obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += mscc_ocelot.o
> +mscc_ocelot-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) := ocelot_regs.o ocelot_board.o
> +
> +obj-$(CONFIG_MSCC_FELIX_SWITCH) += mscc_felix.o
> +mscc_felix-$(CONFIG_MSCC_FELIX_SWITCH) := felix_regs.o felix_board.o
> diff --git a/drivers/net/ethernet/mscc/felix_board.c b/drivers/net/ethernet/mscc/felix_board.c
> new file mode 100644
> index 000000000000..57f7a897b3ae
> --- /dev/null
> +++ b/drivers/net/ethernet/mscc/felix_board.c
> @@ -0,0 +1,392 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/* Felix Switch driver
> + *
> + * Copyright 2018-2019 NXP
> + */
> +
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/netdevice.h>
> +#include <linux/phy_fixed.h>
> +#include <linux/phy.h>
> +#include <linux/of_mdio.h>
> +#include <linux/of_net.h>
> +#include <linux/iopoll.h>
> +#include <net/switchdev.h>
> +#include "ocelot.h"
> +
> +#define FELIX_DRV_VER_MAJ 1
> +#define FELIX_DRV_VER_MIN 0
> +
> +#define FELIX_DRV_STR "Felix Switch driver"
> +#define FELIX_DRV_VER_STR __stringify(FELIX_DRV_VER_MAJ) "." \
> + __stringify(FELIX_DRV_VER_MIN)

Driver version strings are pretty pointless. What you really want to
know if the specific kernel version.

> +
> +#define FELIX_PORT_RES_START 0x0100000
> +#define FELIX_PORT_RES_SIZE 0x10000

This should really be in device tree. You then get a lot closer to the
binding for mscc-ocelot, and you can reuse more of its code.

> +static void felix_release_ports(struct ocelot *ocelot)
> +{
> + struct ocelot_port *ocelot_port;
> + struct phy_device *phydev;
> + struct device_node *dn;
> + int i;
> +
> + for (i = 0; i < ocelot->num_phys_ports; i++) {
> + ocelot_port = ocelot->ports[i];
> + if (!ocelot_port || !ocelot_port->phy || !ocelot_port->dev)
> + continue;

Phys are often optional, e.g. an RGMII interface to another switch, or
an SFP port.

> +
> + phydev = ocelot_port->phy;
> + unregister_netdev(ocelot_port->dev);
> + free_netdev(ocelot_port->dev);
> +
> + if (phy_is_pseudo_fixed_link(phydev)) {
> + dn = phydev->mdio.dev.of_node;
> + /* decr refcnt: of_phy_register_fixed_link */
> + of_phy_deregister_fixed_link(dn);
> + }
> + phy_device_free(phydev); /* decr refcnt: of_phy_find_device */

To be on the safe side, you should probably not free the netdev until
you free the phydev.

This function also seems pretty generic. Should it be shared?

> +static int felix_ports_init(struct pci_dev *pdev)
> +{
> + struct ocelot *ocelot = pci_get_drvdata(pdev);
> + struct device_node *np = ocelot->dev->of_node;
> + struct device_node *phy_node, *portnp;
> + struct phy_device *phydev;
> + void __iomem *port_regs;
> + resource_size_t base;
> + u32 port;
> + int err;
> +
> + ocelot->num_phys_ports = FELIX_MAX_NUM_PHY_PORTS;
> +
> + np = of_get_child_by_name(np, "ethernet-ports");
> + if (!np) {
> + dev_err(&pdev->dev, "ethernet-ports sub-node not found\n");
> + return -ENODEV;
> + }
> +
> + /* alloc netdev for each port */
> + err = ocelot_init(ocelot);
> + if (err)
> + return err;
> +
> + base = pci_resource_start(pdev, FELIX_SWITCH_BAR);
> + for_each_available_child_of_node(np, portnp) {
> + struct resource res = {};
> + int phy_mode;
> +
> + if (!portnp || !portnp->name ||
> + of_node_cmp(portnp->name, "port") ||

The name of the node should not matter.

> +static int felix_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
> +
> + register_netdevice_notifier(&ocelot_netdevice_nb);
> + register_switchdev_notifier(&ocelot_switchdev_nb);
> + register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);

This is also shared. So maybe move it into a common function?

> +
> + dev_info(&pdev->dev, "%s v%s\n", FELIX_DRV_STR, FELIX_DRV_VER_STR);
> + return 0;
> +
> +err_ports_init:
> +err_chip_init:
> +err_sw_core_init:
> + pci_iounmap(pdev, regs);
> +err_iomap:
> +err_resource_len:
> +err_alloc_ocelot:
> +err_dma:
> + pci_disable_device(pdev);
> +
> + return err;
> +}
> +
> +static void felix_pci_remove(struct pci_dev *pdev)
> +{
> + struct ocelot *ocelot;
> +
> + ocelot = pci_get_drvdata(pdev);
> +
> + /* stop workqueue thread */
> + ocelot_deinit(ocelot);
> + unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
> + unregister_switchdev_notifier(&ocelot_switchdev_nb);
> + unregister_netdevice_notifier(&ocelot_netdevice_nb);

This is also common.

Andrew

2019-06-24 14:13:59

by Claudiu Manoil

[permalink] [raw]
Subject: RE: [PATCH net-next 6/6] net/mssc/ocelot: Add basic Felix switch driver

>-----Original Message-----
>From: Andrew Lunn <[email protected]>
>Sent: Saturday, June 22, 2019 11:57 PM
>To: Claudiu Manoil <[email protected]>

[...]

Ok for all, I can work more on refactoring if we agree on the basics.
For instance I can change the driver to use reg-names, same as
mscc-ocelot, and factor out the common code.
So far my intention was to change the ocelot part as little as possible,
only where necessary.

Thanks,
Claudiu