net: dsa: ocelot: add external ocelot switch control

Add control of an external VSC7512 chip.

Currently the four copper phy ports are fully functional. Communication to
external phys is also functional, but the SGMII / QSGMII interfaces are
currently non-functional.

Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Tested-by: Vladimir Oltean <vladimir.oltean@nxp.com> # regression
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Colin Foster 2023-01-27 11:35:58 -08:00 committed by Jakub Kicinski
parent 11fc80cbb2
commit 3d7316ac81
4 changed files with 186 additions and 0 deletions

View File

@ -15155,6 +15155,7 @@ M: Colin Foster <colin.foster@in-advantage.com>
S: Supported
F: Documentation/devicetree/bindings/mfd/mscc,ocelot.yaml
F: drivers/mfd/ocelot*
F: drivers/net/dsa/ocelot/ocelot_ext.c
F: include/linux/mfd/ocelot.h
OCXL (Open Coherent Accelerator Processor Interface OpenCAPI) DRIVER

View File

@ -8,6 +8,26 @@ config NET_DSA_MSCC_FELIX_DSA_LIB
Its name comes from the first hardware chip to make use of it
(VSC9959), code named Felix.
config NET_DSA_MSCC_OCELOT_EXT
tristate "Ocelot External Ethernet switch support"
depends on NET_DSA && SPI
depends on NET_VENDOR_MICROSEMI
select MDIO_MSCC_MIIM
select MFD_OCELOT_CORE
select MSCC_OCELOT_SWITCH_LIB
select NET_DSA_MSCC_FELIX_DSA_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.
The Ocelot switch family is a set of multi-port networking chips. All
of these chips have the ability to be controlled externally through
SPI or PCIe interfaces.
Say "Y" here to enable external control to these chips.
config NET_DSA_MSCC_FELIX
tristate "Ocelot / Felix Ethernet switch support"
depends on NET_DSA && PCI

View File

@ -1,8 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NET_DSA_MSCC_FELIX_DSA_LIB) += mscc_felix_dsa_lib.o
obj-$(CONFIG_NET_DSA_MSCC_FELIX) += mscc_felix.o
obj-$(CONFIG_NET_DSA_MSCC_OCELOT_EXT) += mscc_ocelot_ext.o
obj-$(CONFIG_NET_DSA_MSCC_SEVILLE) += mscc_seville.o
mscc_felix_dsa_lib-objs := felix.o
mscc_felix-objs := felix_vsc9959.o
mscc_ocelot_ext-objs := ocelot_ext.o
mscc_seville-objs := seville_vsc9953.o

View File

@ -0,0 +1,163 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright 2021-2022 Innovative Advantage Inc.
*/
#include <linux/mfd/ocelot.h>
#include <linux/phylink.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <soc/mscc/ocelot.h>
#include <soc/mscc/vsc7514_regs.h>
#include "felix.h"
#define VSC7514_NUM_PORTS 11
#define OCELOT_PORT_MODE_SERDES (OCELOT_PORT_MODE_SGMII | \
OCELOT_PORT_MODE_QSGMII)
static const u32 vsc7512_port_modes[VSC7514_NUM_PORTS] = {
OCELOT_PORT_MODE_INTERNAL,
OCELOT_PORT_MODE_INTERNAL,
OCELOT_PORT_MODE_INTERNAL,
OCELOT_PORT_MODE_INTERNAL,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
};
static const struct ocelot_ops ocelot_ext_ops = {
.reset = ocelot_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,
};
static const char * const vsc7512_resource_names[TARGET_MAX] = {
[SYS] = "sys",
[REW] = "rew",
[S0] = "s0",
[S1] = "s1",
[S2] = "s2",
[QS] = "qs",
[QSYS] = "qsys",
[ANA] = "ana",
};
static const struct felix_info vsc7512_info = {
.resource_names = vsc7512_resource_names,
.regfields = vsc7514_regfields,
.map = vsc7514_regmap,
.ops = &ocelot_ext_ops,
.vcap = vsc7514_vcap_props,
.num_mact_rows = 1024,
.num_ports = VSC7514_NUM_PORTS,
.num_tx_queues = OCELOT_NUM_TC,
.port_modes = vsc7512_port_modes,
};
static int ocelot_ext_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dsa_switch *ds;
struct ocelot *ocelot;
struct felix *felix;
int err;
felix = kzalloc(sizeof(*felix), GFP_KERNEL);
if (!felix)
return -ENOMEM;
dev_set_drvdata(dev, felix);
ocelot = &felix->ocelot;
ocelot->dev = dev;
ocelot->num_flooding_pgids = 1;
felix->info = &vsc7512_info;
ds = kzalloc(sizeof(*ds), GFP_KERNEL);
if (!ds) {
err = -ENOMEM;
dev_err_probe(dev, err, "Failed to allocate DSA switch\n");
goto err_free_felix;
}
ds->dev = 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;
felix->tag_proto = DSA_TAG_PROTO_OCELOT;
err = dsa_register_switch(ds);
if (err) {
dev_err_probe(dev, err, "Failed to register DSA switch\n");
goto err_free_ds;
}
return 0;
err_free_ds:
kfree(ds);
err_free_felix:
kfree(felix);
return err;
}
static int ocelot_ext_remove(struct platform_device *pdev)
{
struct felix *felix = dev_get_drvdata(&pdev->dev);
if (!felix)
return 0;
dsa_unregister_switch(felix->ds);
kfree(felix->ds);
kfree(felix);
return 0;
}
static void ocelot_ext_shutdown(struct platform_device *pdev)
{
struct felix *felix = dev_get_drvdata(&pdev->dev);
if (!felix)
return;
dsa_switch_shutdown(felix->ds);
dev_set_drvdata(&pdev->dev, NULL);
}
static const struct of_device_id ocelot_ext_switch_of_match[] = {
{ .compatible = "mscc,vsc7512-switch" },
{ },
};
MODULE_DEVICE_TABLE(of, ocelot_ext_switch_of_match);
static struct platform_driver ocelot_ext_switch_driver = {
.driver = {
.name = "ocelot-switch",
.of_match_table = of_match_ptr(ocelot_ext_switch_of_match),
},
.probe = ocelot_ext_probe,
.remove = ocelot_ext_remove,
.shutdown = ocelot_ext_shutdown,
};
module_platform_driver(ocelot_ext_switch_driver);
MODULE_DESCRIPTION("External Ocelot Switch driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(MFD_OCELOT);