My apologies for this next RFC taking so long. Life got in the way.
The patch set in general is to add support for the VSC7511, VSC7512,
VSC7513 and VSC7514 devices controlled over SPI. The driver is
relatively functional for the internal phy ports (0-3) on the VSC7512.
As I'll discuss, it is not yet functional for other ports yet.
I still think there are enough updates to bounce by the community
in case I'm terribly off base or doomed to chase my tail.
The main changes for V4 are trying to get pinctrl-ocelot and
pinctrl-microchip-sgpio functional. Without pinctrl-ocelot,
communication to external phys won't work. Without communication to
external phys, PCS ports 4-7 on the dev board won't work. Nor will any
fiber ports.
The hardware setup I'm using for development is a beaglebone black, with
jumpers from SPI0 to the microchip VSC7512 dev board. The microchip dev
board has been modified to not boot from flash, but wait for SPI. An
ethernet cable is connected from the beaglebone ethernet to port 0 of
the dev board.
The device tree I'm using for the VSC7512 is below. Note that ports 4-7
are still not expected to work, but left in as placeholders for when
they do.
&spi0 {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
ethernet-switch@0{
compatible = "mscc,vsc7512";
spi-max-frequency = <250000>;
reg = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "cpu";
status = "okay";
ethernet = <&mac_sw>;
phy-handle = <&sw_phy0>;
phy-mode = "internal";
};
port@1 {
reg = <1>;
label = "swp1";
status = "okay";
phy-handle = <&sw_phy1>;
phy-mode = "internal";
};
port@2 {
reg = <2>;
label = "swp2";
status = "okay";
phy-handle = <&sw_phy2>;
phy-mode = "internal";
};
port@3 {
reg = <3>;
label = "swp3";
status = "okay";
phy-handle = <&sw_phy3>;
phy-mode = "internal";
};
port@4 {
reg = <4>;
label = "swp4";
status = "okay";
phy-handle = <&sw_phy4>;
phy-mode = "sgmii";
};
port@5 {
reg = <5>;
label = "swp5";
status = "okay";
phy-handle = <&sw_phy5>;
phy-mode = "sgmii";
};
port@6 {
reg = <6>;
label = "swp6";
status = "okay";
phy-handle = <&sw_phy6>;
phy-mode = "sgmii";
};
port@7 {
reg = <7>;
label = "swp7";
status = "okay";
phy-handle = <&sw_phy7>;
phy-mode = "sgmii";
};
};
mdio {
#address-cells = <1>;
#size-cells = <0>;
sw_phy0: ethernet-phy@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x0>;
};
sw_phy1: ethernet-phy@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x1>;
};
sw_phy2: ethernet-phy@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x2>;
};
sw_phy3: ethernet-phy@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x3>;
};
sw_phy4: ethernet-phy@4 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x4>;
};
sw_phy5: ethernet-phy@5 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x5>;
};
sw_phy6: ethernet-phy@6 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x6>;
};
sw_phy7: ethernet-phy@7 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x7>;
};
};
gpio: pinctrl {
compatible = "mscc,ocelot-pinctrl";
#address-cells = <1>;
#size-cells = <0>;
#gpio_cells = <2>;
gpio-ranges = <&gpio 0 0 22>;
led_shift_reg_pins: led-shift-reg-pins {
pins = "GPIO_0", "GPIO_1", "GPIO_2", "GPIO_3";
function = "sg0";
};
};
sgpio: sgpio {
compatible = "mscc,ocelot-sgpio";
#address-cells = <1>;
#size-cells = <0>;
bus-frequency=<12500000>;
clocks = <&ocelot_clock>;
microchip,sgpio-port-ranges = <0 31>;
sgpio_in0: sgpio@0 {
compatible = "microchip,sparx5-sgpio-bank";
reg = <0>;
gpio-controller;
#gpio-cells = <3>;
ngpios = <32>;
};
sgpio_out1: sgpio@1 {
compatible = "microchip,sparx5-sgpio-bank";
reg = <1>;
gpio-controller;
gpio-cells = <3>;
ngpios = <32>;
};
};
};
};
My main focus is getting the ocelot-pinctrl driver fully functional. My
current hope is that it would correctly set GPIO pins 0-3 into the "sg0"
state. That is not the case right now, and I'll be looking into why. The
behavior I'm hoping for is to be able to configure the sgpio LEDs for
activity at the very least. Link status would be a bonus.
I do have pinctrl by way of debugfs and sysfs. There aren't any debug
LEDs that are attached to unused pins, unfortunately. That would've been
really helpful. So there's a key takeaway for dev-board manufacturers.
As you'll see, the main changes to the three drivers I'm utilizing
(mscc_miim, pinctrl-ocelot, and pinctrl-microchip-sgpio) follow a
similar path. First, convert everything to regmap. Second, expose
whatever functions are necessary to fully utilize an external regmap.
One thing to note: I've been following a pattern of adding "offset"
variables to these drivers. I'm looking for feedback here, because I
don't like it - however I feel like it is the "least bad" interface I
could come up with.
Specifically, ocelot has a regmap for GCB. ocelot-pinctrl would create a
smaller regmap at an address of "GCB + 0x34".
There are three options I saw here:
1. Have vsc7512_spi create a new regmap at GCB + 0x34 and pass that to
ocelot-pinctrl
2. Give ocelot-pinctrl the concept of a "parent bus" by which it could
request a regmap.
3. Keep the same GCB regmap, but pass in 0x34 as an offset.
I will admit that option 2 sounds very enticing, but I don't know if
that type of interaction exists. If not, implementing it is probably
outside the scope of a first patch set. As such, I opted for option 3.
Version 4 also fixes some logic for MDIO probing. It wasn't using the
device tree by way of of_mdiobus_register. Now it is.
The relevant boot log for the switch / MDIO bus is here. As expected,
devices 4-7 are missing. If nothing else, that is telling me that the
device tree is working.
[ 4.005195] mdio_bus spi0.0-mii:03: using lookup tables for GPIO lookup
[ 4.005205] mdio_bus spi0.0-mii:03: No GPIO consumer reset found
[ 4.006586] mdio_bus spi0.0-mii: MDIO device at address 4 is missing.
[ 4.014333] mdio_bus spi0.0-mii: MDIO device at address 5 is missing.
[ 4.022009] mdio_bus spi0.0-mii: MDIO device at address 6 is missing.
[ 4.029573] mdio_bus spi0.0-mii: MDIO device at address 7 is missing.
[ 8.386624] vsc7512 spi0.0: PHY [spi0.0-mii:00] driver [Generic PHY] (irq=POLL)
[ 8.397222] vsc7512 spi0.0: configuring for phy/internal link mode
[ 8.419484] vsc7512 spi0.0 swp1 (uninitialized): PHY [spi0.0-mii:01] driver [Generic PHY] (irq=POLL)
[ 8.437278] vsc7512 spi0.0 swp2 (uninitialized): PHY [spi0.0-mii:02] driver [Generic PHY] (irq=POLL)
[ 8.452867] vsc7512 spi0.0 swp3 (uninitialized): PHY [spi0.0-mii:03] driver [Generic PHY] (irq=POLL)
[ 8.465007] vsc7512 spi0.0 swp4 (uninitialized): no phy at 4
[ 8.470721] vsc7512 spi0.0 swp4 (uninitialized): failed to connect to PHY: -ENODEV
[ 8.478388] vsc7512 spi0.0 swp4 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 4
[ 8.489636] vsc7512 spi0.0 swp5 (uninitialized): no phy at 5
[ 8.495371] vsc7512 spi0.0 swp5 (uninitialized): failed to connect to PHY: -ENODEV
[ 8.502996] vsc7512 spi0.0 swp5 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 5
[ 8.514186] vsc7512 spi0.0 swp6 (uninitialized): no phy at 6
[ 8.519882] vsc7512 spi0.0 swp6 (uninitialized): failed to connect to PHY: -ENODEV
[ 8.527539] vsc7512 spi0.0 swp6 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 6
[ 8.538716] vsc7512 spi0.0 swp7 (uninitialized): no phy at 7
[ 8.544451] vsc7512 spi0.0 swp7 (uninitialized): failed to connect to PHY: -ENODEV
[ 8.552079] vsc7512 spi0.0 swp7 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 7
[ 8.571962] device eth0 entered promiscuous mode
[ 8.576684] DSA: tree 0 setup
[ 10.490093] vsc7512 spi0.0: Link is Up - 100Mbps/Full - flow control off
Much later on, I created a bridge with STP (and two ports jumped
together) as a test. Everything seems to be working as expected.
[59839.920340] cpsw-switch 4a100000.switch: starting ndev. mode: dual_mac
[59840.013636] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver (mii_bus:phy_addr=4a101000.mdio:00, irq=POLL)
[59840.031444] 8021q: adding VLAN 0 to HW filter on device eth0
[59840.057406] vsc7512 spi0.0 swp1: configuring for phy/internal link mode
[59840.089302] vsc7512 spi0.0 swp2: configuring for phy/internal link mode
[59840.121514] vsc7512 spi0.0 swp3: configuring for phy/internal link mode
[59840.167589] br0: port 1(swp1) entered blocking state
[59840.172818] br0: port 1(swp1) entered disabled state
[59840.191078] device swp1 entered promiscuous mode
[59840.224855] br0: port 2(swp2) entered blocking state
[59840.229893] br0: port 2(swp2) entered disabled state
[59840.245844] device swp2 entered promiscuous mode
[59840.270839] br0: port 3(swp3) entered blocking state
[59840.276003] br0: port 3(swp3) entered disabled state
[59840.291674] device swp3 entered promiscuous mode
[59840.663239] vsc7512 spi0.0: Link is Down
[59841.691641] vsc7512 spi0.0: Link is Up - 100Mbps/Full - flow control off
[59842.167897] cpsw-switch 4a100000.switch eth0: Link is Up - 100Mbps/Full - flow control off
[59842.176481] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[59843.216121] vsc7512 spi0.0 swp1: Link is Up - 1Gbps/Full - flow control rx/tx
[59843.231076] IPv6: ADDRCONF(NETDEV_CHANGE): swp1: link becomes ready
[59843.237593] br0: port 1(swp1) entered blocking state
[59843.242629] br0: port 1(swp1) entered listening state
[59843.301447] vsc7512 spi0.0 swp3: Link is Up - 1Gbps/Full - flow control rx/tx
[59843.309027] IPv6: ADDRCONF(NETDEV_CHANGE): swp3: link becomes ready
[59843.315544] br0: port 3(swp3) entered blocking state
[59843.320545] br0: port 3(swp3) entered listening state
[59845.042058] br0: port 3(swp3) entered blocking state
[59858.401566] br0: port 1(swp1) entered learning state
[59871.841910] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
[59873.761495] br0: port 1(swp1) entered forwarding state
[59873.766703] br0: topology change detected, propagating
[59873.776278] IPv6: ADDRCONF(NETDEV_CHANGE): br0: link becomes ready
[59902.561908] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
[59926.494446] vsc7512 spi0.0 swp2: Link is Up - 1Gbps/Full - flow control rx/tx
[59926.501959] IPv6: ADDRCONF(NETDEV_CHANGE): swp2: link becomes ready
[59926.508702] br0: port 2(swp2) entered blocking state
[59926.513868] br0: port 2(swp2) entered listening state
[59941.601540] br0: port 2(swp2) entered learning state
[59956.961493] br0: port 2(swp2) entered forwarding state
[59956.966711] br0: topology change detected, propagating
[59968.481839] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
In order to make this work, I have modified the cpsw driver, and now the
cpsw_new driver, to allow for frames over 1500 bytes. Otherwise the
tagging protocol will not work between the beaglebone and the VSC7512. I
plan to eventually try to get those changes in mainline, but I don't
want to get distracted from my initial goal.
RFC history:
v1 (accidentally named vN)
* Initial architecture. Not functional
* General concepts laid out
v2
* Near functional. No CPU port communication, but control over all
external ports
* Cleaned up regmap implementation from v1
v3
* Functional
* Shared MDIO transactions routed through mdio-mscc-miim
* CPU / NPI port enabled by way of vsc7512_enable_npi_port /
felix->info->enable_npi_port
* NPI port tagging functional - Requires a CPU port driver that supports
frames of 1520 bytes. Verified with a patch to the cpsw driver
v4
* Functional
* Device tree fixes
* Add hooks for pinctrl-ocelot - some functionality by way of sysfs
* Add hooks for pinctrl-microsemi-sgpio - not yet fully functional
* Remove lynx_pcs interface for a generic phylink_pcs. The goal here
is to have an ocelot_pcs that will work for each configuration of
every port.
Colin Foster (23):
net: dsa: ocelot: remove unnecessary pci_bar variables
net: mdio: mscc-miim: convert to a regmap implementation
net: dsa: ocelot: seville: utilize of_mdiobus_register
net: dsa: ocelot: felix: switch to mdio-mscc-miim driver for indirect
mdio access
net: dsa: ocelot: felix: Remove requirement for PCS in felix devices
net: dsa: ocelot: felix: add interface for custom regmaps
net: dsa: ocelot: felix: add per-device-per-port quirks
net: mscc: ocelot: split register definitions to a separate file
net: mscc: ocelot: expose ocelot wm functions
pinctrl: ocelot: combine get resource and ioremap into single call
pinctrl: ocelot: update pinctrl to automatic base address
pinctrl: ocelot: convert pinctrl to regmap
pinctrl: ocelot: expose ocelot_pinctrl_core_probe interface
pinctrl: microchip-sgpio: update to support regmap
device property: add helper function fwnode_get_child_node_count
pinctrl: microchip-sgpio: change device tree matches to use nodes
instead of device
pinctrl: microchip-sgpio: expose microchip_sgpio_core_probe interface
net: phy: lynx: refactor Lynx PCS module to use generic phylink_pcs
net: dsa: felix: name change for clarity from pcs to mdio_device
net: dsa: seville: name change for clarity from pcs to mdio_device
net: ethernet: enetc: name change for clarity from pcs to mdio_device
net: pcs: lynx: use a common naming scheme for all lynx_pcs variables
net: dsa: ocelot: felix: add support for VSC75XX control over SPI
drivers/base/property.c | 20 +-
drivers/net/dsa/ocelot/Kconfig | 16 +
drivers/net/dsa/ocelot/Makefile | 7 +
drivers/net/dsa/ocelot/felix.c | 29 +-
drivers/net/dsa/ocelot/felix.h | 10 +-
drivers/net/dsa/ocelot/felix_mdio.c | 54 +
drivers/net/dsa/ocelot/felix_mdio.h | 13 +
drivers/net/dsa/ocelot/felix_vsc9959.c | 38 +-
drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c | 946 ++++++++++++++++++
drivers/net/dsa/ocelot/seville_vsc9953.c | 136 +--
.../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 12 +-
.../net/ethernet/freescale/dpaa2/dpaa2-mac.h | 3 +-
.../net/ethernet/freescale/enetc/enetc_pf.c | 27 +-
.../net/ethernet/freescale/enetc/enetc_pf.h | 4 +-
drivers/net/ethernet/mscc/Makefile | 3 +-
drivers/net/ethernet/mscc/ocelot.c | 8 +
drivers/net/ethernet/mscc/ocelot_devlink.c | 31 +
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 548 +---------
drivers/net/ethernet/mscc/vsc7514_regs.c | 522 ++++++++++
drivers/net/mdio/mdio-mscc-miim.c | 167 +++-
drivers/net/pcs/pcs-lynx.c | 36 +-
drivers/pinctrl/pinctrl-microchip-sgpio.c | 127 ++-
drivers/pinctrl/pinctrl-ocelot.c | 207 ++--
include/linux/mdio/mdio-mscc-miim.h | 19 +
include/linux/pcs-lynx.h | 9 +-
include/linux/property.h | 1 +
include/soc/mscc/ocelot.h | 60 ++
include/soc/mscc/vsc7514_regs.h | 27 +
28 files changed, 2219 insertions(+), 861 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/vsc7514_regs.c
create mode 100644 include/linux/mdio/mdio-mscc-miim.h
create mode 100644 include/soc/mscc/vsc7514_regs.h
--
2.25.1
A simple variable update from "pcs" to "mdio_device" for the mdio device
will make things a little cleaner.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/ethernet/freescale/enetc/enetc_pf.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 125a539b0654..3d93ac1376c6 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -829,7 +829,7 @@ static int enetc_imdio_create(struct enetc_pf *pf)
struct device *dev = &pf->si->pdev->dev;
struct enetc_mdio_priv *mdio_priv;
struct phylink_pcs *phylink_pcs;
- struct mdio_device *pcs;
+ struct mdio_device *mdio_device;
struct mii_bus *bus;
int err;
@@ -853,16 +853,16 @@ static int enetc_imdio_create(struct enetc_pf *pf)
goto free_mdio_bus;
}
- pcs = mdio_device_create(bus, 0);
- if (IS_ERR(pcs)) {
- err = PTR_ERR(pcs);
- dev_err(dev, "cannot create pcs (%d)\n", err);
+ mdio_device = mdio_device_create(bus, 0);
+ if (IS_ERR(mdio_device)) {
+ err = PTR_ERR(mdio_device);
+ dev_err(dev, "cannot create mdio device (%d)\n", err);
goto unregister_mdiobus;
}
- phylink_pcs = lynx_pcs_create(pcs);
+ phylink_pcs = lynx_pcs_create(mdio_device);
if (!phylink_pcs) {
- mdio_device_free(pcs);
+ mdio_device_free(mdio_device);
err = -ENOMEM;
dev_err(dev, "cannot create lynx pcs (%d)\n", err);
goto unregister_mdiobus;
--
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/ocelot_devlink.c | 31 ++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 28 -------------------
include/soc/mscc/ocelot.h | 5 ++++
3 files changed, 36 insertions(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/mscc/ocelot_devlink.c b/drivers/net/ethernet/mscc/ocelot_devlink.c
index b8737efd2a85..d9ea75a14f2f 100644
--- a/drivers/net/ethernet/mscc/ocelot_devlink.c
+++ b/drivers/net/ethernet/mscc/ocelot_devlink.c
@@ -487,6 +487,37 @@ static void ocelot_watermark_init(struct ocelot *ocelot)
ocelot_setup_sharing_watermarks(ocelot);
}
+/* 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);
+
/* Pool size and type are fixed up at runtime. Keeping this structure to
* look up the cell size multipliers.
*/
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 2c763784f69b..3715d89f5d4c 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -303,34 +303,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/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index fef3a36b0210..9a4ded4bff04 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -766,6 +766,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_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data);
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data);
--
2.25.1
Utilize regmap instead of __iomem to perform indirect mdio access. This
will allow for custom regmaps to be used by way of the mscc_miim_setup
function.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/mdio/mdio-mscc-miim.c | 148 +++++++++++++++++++++---------
1 file changed, 105 insertions(+), 43 deletions(-)
diff --git a/drivers/net/mdio/mdio-mscc-miim.c b/drivers/net/mdio/mdio-mscc-miim.c
index 17f98f609ec8..ea599b980bbf 100644
--- a/drivers/net/mdio/mdio-mscc-miim.c
+++ b/drivers/net/mdio/mdio-mscc-miim.c
@@ -14,6 +14,7 @@
#include <linux/of_mdio.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#define MSCC_MIIM_REG_STATUS 0x0
#define MSCC_MIIM_STATUS_STAT_PENDING BIT(2)
@@ -35,37 +36,47 @@
#define MSCC_PHY_REG_PHY_STATUS 0x4
struct mscc_miim_dev {
- void __iomem *regs;
- void __iomem *phy_regs;
+ struct regmap *regs;
+ struct regmap *phy_regs;
};
/* When high resolution timers aren't built-in: we can't use usleep_range() as
* we would sleep way too long. Use udelay() instead.
*/
-#define mscc_readl_poll_timeout(addr, val, cond, delay_us, timeout_us) \
-({ \
- if (!IS_ENABLED(CONFIG_HIGH_RES_TIMERS)) \
- readl_poll_timeout_atomic(addr, val, cond, delay_us, \
- timeout_us); \
- readl_poll_timeout(addr, val, cond, delay_us, timeout_us); \
+#define mscc_readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us)\
+({ \
+ if (!IS_ENABLED(CONFIG_HIGH_RES_TIMERS)) \
+ readx_poll_timeout_atomic(op, addr, val, cond, delay_us, \
+ timeout_us); \
+ readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us); \
})
-static int mscc_miim_wait_ready(struct mii_bus *bus)
+static int mscc_miim_status(struct mii_bus *bus)
{
struct mscc_miim_dev *miim = bus->priv;
+ int val, err;
+
+ err = regmap_read(miim->regs, MSCC_MIIM_REG_STATUS, &val);
+ if (err < 0)
+ WARN_ONCE(1, "mscc miim status read error %d\n", err);
+
+ return val;
+}
+
+static int mscc_miim_wait_ready(struct mii_bus *bus)
+{
u32 val;
- return mscc_readl_poll_timeout(miim->regs + MSCC_MIIM_REG_STATUS, val,
+ return mscc_readx_poll_timeout(mscc_miim_status, bus, val,
!(val & MSCC_MIIM_STATUS_STAT_BUSY), 50,
10000);
}
static int mscc_miim_wait_pending(struct mii_bus *bus)
{
- struct mscc_miim_dev *miim = bus->priv;
u32 val;
- return mscc_readl_poll_timeout(miim->regs + MSCC_MIIM_REG_STATUS, val,
+ return mscc_readx_poll_timeout(mscc_miim_status, bus, val,
!(val & MSCC_MIIM_STATUS_STAT_PENDING),
50, 10000);
}
@@ -73,22 +84,30 @@ static int mscc_miim_wait_pending(struct mii_bus *bus)
static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
{
struct mscc_miim_dev *miim = bus->priv;
+ int ret, err;
u32 val;
- int ret;
ret = mscc_miim_wait_pending(bus);
if (ret)
goto out;
- writel(MSCC_MIIM_CMD_VLD | (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
- (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ,
- miim->regs + MSCC_MIIM_REG_CMD);
+ err = regmap_write(miim->regs, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD |
+ (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+ (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
+ MSCC_MIIM_CMD_OPR_READ);
+
+ if (err < 0)
+ WARN_ONCE(1, "mscc miim write cmd reg error %d\n", err);
ret = mscc_miim_wait_ready(bus);
if (ret)
goto out;
- val = readl(miim->regs + MSCC_MIIM_REG_DATA);
+ err = regmap_read(miim->regs, MSCC_MIIM_REG_DATA, &val);
+
+ if (err < 0)
+ WARN_ONCE(1, "mscc miim read data reg error %d\n", err);
+
if (val & MSCC_MIIM_DATA_ERROR) {
ret = -EIO;
goto out;
@@ -103,18 +122,20 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id,
int regnum, u16 value)
{
struct mscc_miim_dev *miim = bus->priv;
- int ret;
+ int err, ret;
ret = mscc_miim_wait_pending(bus);
if (ret < 0)
goto out;
- writel(MSCC_MIIM_CMD_VLD | (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
- (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
- (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
- MSCC_MIIM_CMD_OPR_WRITE,
- miim->regs + MSCC_MIIM_REG_CMD);
+ err = regmap_write(miim->regs, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD |
+ (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+ (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
+ (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
+ MSCC_MIIM_CMD_OPR_WRITE);
+ if (err < 0)
+ WARN_ONCE(1, "mscc miim write error %d\n", err);
out:
return ret;
}
@@ -122,24 +143,37 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id,
static int mscc_miim_reset(struct mii_bus *bus)
{
struct mscc_miim_dev *miim = bus->priv;
+ int err;
if (miim->phy_regs) {
- writel(0, miim->phy_regs + MSCC_PHY_REG_PHY_CFG);
- writel(0x1ff, miim->phy_regs + MSCC_PHY_REG_PHY_CFG);
+ err = regmap_write(miim->phy_regs, MSCC_PHY_REG_PHY_CFG, 0);
+ if (err < 0)
+ WARN_ONCE(1, "mscc reset set error %d\n", err);
+
+ err = regmap_write(miim->phy_regs, MSCC_PHY_REG_PHY_CFG, 0x1ff);
+ if (err < 0)
+ WARN_ONCE(1, "mscc reset clear error %d\n", err);
+
mdelay(500);
}
return 0;
}
-static int mscc_miim_probe(struct platform_device *pdev)
+static const struct regmap_config mscc_miim_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int mscc_miim_setup(struct device *dev, struct mii_bus *bus,
+ struct regmap *mii_regmap, struct regmap *phy_regmap)
{
- struct mscc_miim_dev *dev;
- struct resource *res;
+ struct mscc_miim_dev *miim;
struct mii_bus *bus;
int ret;
- bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*dev));
+ bus = devm_mdiobus_alloc_size(dev, sizeof(*miim));
if (!bus)
return -ENOMEM;
@@ -147,26 +181,54 @@ static int mscc_miim_probe(struct platform_device *pdev)
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
bus->reset = mscc_miim_reset;
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
- bus->parent = &pdev->dev;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
+ bus->parent = dev;
+
+ miim = bus->priv;
+
+ miim->regs = mii_regmap;
+ miim->phy_regs = phy_regmap;
+
+ return 0;
+}
+
+static int mscc_miim_probe(struct platform_device *pdev)
+{
+ struct regmap *mii_regmap, *phy_regmap;
+ void __iomem *regs, *phy_regs;
+ struct mscc_miim_dev *dev;
+ struct mii_bus *bus;
+ int ret;
- dev = bus->priv;
- dev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
- if (IS_ERR(dev->regs)) {
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(regs)) {
dev_err(&pdev->dev, "Unable to map MIIM registers\n");
- return PTR_ERR(dev->regs);
+ return PTR_ERR(regs);
}
- /* This resource is optional */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res) {
- dev->phy_regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(dev->phy_regs)) {
- dev_err(&pdev->dev, "Unable to map internal phy registers\n");
- return PTR_ERR(dev->phy_regs);
- }
+ mii_regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &mscc_miim_regmap_config);
+
+ if (IS_ERR(mii_regmap)) {
+ dev_err(&pdev->dev, "Unable to create MIIM regmap\n");
+ return PTR_ERR(mii_regmap);
}
+ phy_regs = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(dev->phy_regs)) {
+ dev_err(&pdev->dev, "Unable to map internal phy registers\n");
+ return PTR_ERR(dev->phy_regs);
+ }
+
+ phy_regmap = devm_regmap_init_mmio(&pdev->dev, phy_regs,
+ &mscc_miim_regmap_config);
+ if (IS_ERR(phy_regmap)) {
+ dev_err(&pdev->dev, "Unable to create phy register regmap\n");
+ return PTR_ERR(dev->phy_regs);
+ }
+
+ mscc_miim_setup(&pdev->dev, bus, mii_regmap, phy_regmap);
+
ret = of_mdiobus_register(bus, pdev->dev.of_node);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
--
2.25.1
Simple cleanup to make two function calls only one.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/pinctrl/pinctrl-ocelot.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
index 0a36ec8775a3..cc7fb0556169 100644
--- a/drivers/pinctrl/pinctrl-ocelot.c
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -1378,8 +1378,7 @@ static int ocelot_pinctrl_probe(struct platform_device *pdev)
/* Pinconf registers */
if (info->desc->confops) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
dev_dbg(dev, "Failed to ioremap config registers (no extended pinconf)\n");
else
--
2.25.1
Switch to a shared MDIO access implementation now provided by
drivers/net/mdio/mdio-mscc-miim.c
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/Kconfig | 1 +
drivers/net/dsa/ocelot/Makefile | 1 +
drivers/net/dsa/ocelot/felix_mdio.c | 54 ++++++++++++
drivers/net/dsa/ocelot/felix_mdio.h | 13 +++
drivers/net/dsa/ocelot/seville_vsc9953.c | 108 ++---------------------
drivers/net/mdio/mdio-mscc-miim.c | 37 +++++---
include/linux/mdio/mdio-mscc-miim.h | 19 ++++
7 files changed, 123 insertions(+), 110 deletions(-)
create mode 100644 drivers/net/dsa/ocelot/felix_mdio.c
create mode 100644 drivers/net/dsa/ocelot/felix_mdio.h
create mode 100644 include/linux/mdio/mdio-mscc-miim.h
diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig
index 9948544ba1c4..220b0b027b55 100644
--- a/drivers/net/dsa/ocelot/Kconfig
+++ b/drivers/net/dsa/ocelot/Kconfig
@@ -21,6 +21,7 @@ config NET_DSA_MSCC_SEVILLE
depends on NET_VENDOR_MICROSEMI
depends on HAS_IOMEM
depends on PTP_1588_CLOCK_OPTIONAL
+ select MDIO_MSCC_MIIM
select MSCC_OCELOT_SWITCH_LIB
select NET_DSA_TAG_OCELOT_8021Q
select NET_DSA_TAG_OCELOT
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..34375285756b
--- /dev/null
+++ b/drivers/net/dsa/ocelot/felix_mdio.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Distributed Switch Architecture VSC9953 driver
+ * Copyright (C) 2020, Maxim Kochetkov <[email protected]>
+ * Copyright (C) 2021 Innovative Advantage
+ */
+#include <linux/of_mdio.h>
+#include <linux/types.h>
+#include <soc/mscc/ocelot.h>
+#include <linux/dsa/ocelot.h>
+#include <linux/mdio/mdio-mscc-miim.h>
+#include "felix.h"
+#include "felix_mdio.h"
+
+int felix_of_mdio_register(struct ocelot *ocelot, struct device_node *np)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+ struct device *dev = ocelot->dev;
+ int rc;
+
+ /* Needed in order to initialize the bus mutex lock */
+ rc = of_mdiobus_register(felix->imdio, np);
+ 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;
+ int err;
+
+ err = mscc_miim_setup(dev, &bus, ocelot->targets[GCB],
+ ocelot->map[GCB][GCB_MIIM_MII_STATUS & REG_MASK],
+ ocelot->targets[GCB],
+ ocelot->map[GCB][GCB_PHY_PHY_CFG & REG_MASK]);
+
+ if (!err)
+ felix->imdio = bus;
+
+ return err;
+}
+
+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..93286f598c3b
--- /dev/null
+++ b/drivers/net/dsa/ocelot/felix_mdio.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/* Shared code for indirect MDIO access for Felix drivers
+ *
+ * Author: Colin Foster <[email protected]>
+ * Copyright (C) 2021 Innovative Advantage
+ */
+#include <linux/of.h>
+#include <linux/types.h>
+#include <soc/mscc/ocelot.h>
+
+int felix_mdio_bus_alloc(struct ocelot *ocelot);
+int felix_of_mdio_register(struct ocelot *ocelot, struct device_node *np);
+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 84681642d237..610bdfd31903 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
@@ -1084,7 +1001,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;
@@ -1096,26 +1012,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 = of_mdiobus_register(bus, NULL);
+ rc = felix_of_mdio_register(ocelot, NULL);
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;
@@ -1160,7 +1068,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 = {
diff --git a/drivers/net/mdio/mdio-mscc-miim.c b/drivers/net/mdio/mdio-mscc-miim.c
index ea599b980bbf..cf3fa7a4459c 100644
--- a/drivers/net/mdio/mdio-mscc-miim.c
+++ b/drivers/net/mdio/mdio-mscc-miim.c
@@ -10,6 +10,7 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
+#include <linux/mdio/mdio-mscc-miim.h>
#include <linux/module.h>
#include <linux/of_mdio.h>
#include <linux/phy.h>
@@ -37,7 +38,9 @@
struct mscc_miim_dev {
struct regmap *regs;
+ int mii_status_offset;
struct regmap *phy_regs;
+ int phy_reset_offset;
};
/* When high resolution timers aren't built-in: we can't use usleep_range() as
@@ -56,7 +59,8 @@ static int mscc_miim_status(struct mii_bus *bus)
struct mscc_miim_dev *miim = bus->priv;
int val, err;
- err = regmap_read(miim->regs, MSCC_MIIM_REG_STATUS, &val);
+ err = regmap_read(miim->regs,
+ MSCC_MIIM_REG_STATUS + miim->mii_status_offset, &val);
if (err < 0)
WARN_ONCE(1, "mscc miim status read error %d\n", err);
@@ -91,7 +95,9 @@ static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
if (ret)
goto out;
- err = regmap_write(miim->regs, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD |
+ err = regmap_write(miim->regs,
+ MSCC_MIIM_REG_CMD + miim->mii_status_offset,
+ MSCC_MIIM_CMD_VLD |
(mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
(regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
MSCC_MIIM_CMD_OPR_READ);
@@ -103,7 +109,8 @@ static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
if (ret)
goto out;
- err = regmap_read(miim->regs, MSCC_MIIM_REG_DATA, &val);
+ err = regmap_read(miim->regs,
+ MSCC_MIIM_REG_DATA + miim->mii_status_offset, &val);
if (err < 0)
WARN_ONCE(1, "mscc miim read data reg error %d\n", err);
@@ -128,7 +135,9 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id,
if (ret < 0)
goto out;
- err = regmap_write(miim->regs, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD |
+ err = regmap_write(miim->regs,
+ MSCC_MIIM_REG_CMD + miim->mii_status_offset,
+ MSCC_MIIM_CMD_VLD |
(mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
(regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
(value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
@@ -143,14 +152,17 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id,
static int mscc_miim_reset(struct mii_bus *bus)
{
struct mscc_miim_dev *miim = bus->priv;
+ int offset = miim->phy_reset_offset;
int err;
if (miim->phy_regs) {
- err = regmap_write(miim->phy_regs, MSCC_PHY_REG_PHY_CFG, 0);
+ err = regmap_write(miim->phy_regs,
+ MSCC_PHY_REG_PHY_CFG + offset, 0);
if (err < 0)
WARN_ONCE(1, "mscc reset set error %d\n", err);
- err = regmap_write(miim->phy_regs, MSCC_PHY_REG_PHY_CFG, 0x1ff);
+ err = regmap_write(miim->phy_regs,
+ MSCC_PHY_REG_PHY_CFG + offset, 0x1ff);
if (err < 0)
WARN_ONCE(1, "mscc reset clear error %d\n", err);
@@ -166,12 +178,12 @@ static const struct regmap_config mscc_miim_regmap_config = {
.reg_stride = 4,
};
-static int mscc_miim_setup(struct device *dev, struct mii_bus *bus,
- struct regmap *mii_regmap, struct regmap *phy_regmap)
+int mscc_miim_setup(struct device *dev, struct mii_bus **pbus,
+ struct regmap *mii_regmap, int status_offset,
+ struct regmap *phy_regmap, int reset_offset)
{
struct mscc_miim_dev *miim;
struct mii_bus *bus;
- int ret;
bus = devm_mdiobus_alloc_size(dev, sizeof(*miim));
if (!bus)
@@ -187,10 +199,15 @@ static int mscc_miim_setup(struct device *dev, struct mii_bus *bus,
miim = bus->priv;
miim->regs = mii_regmap;
+ miim->mii_status_offset = status_offset;
miim->phy_regs = phy_regmap;
+ miim->phy_reset_offset = reset_offset;
+
+ *pbus = bus;
return 0;
}
+EXPORT_SYMBOL(mscc_miim_setup);
static int mscc_miim_probe(struct platform_device *pdev)
{
@@ -227,7 +244,7 @@ static int mscc_miim_probe(struct platform_device *pdev)
return PTR_ERR(dev->phy_regs);
}
- mscc_miim_setup(&pdev->dev, bus, mii_regmap, phy_regmap);
+ mscc_miim_setup(&pdev->dev, &bus, mii_regmap, 0, phy_regmap, 0);
ret = of_mdiobus_register(bus, pdev->dev.of_node);
if (ret < 0) {
diff --git a/include/linux/mdio/mdio-mscc-miim.h b/include/linux/mdio/mdio-mscc-miim.h
new file mode 100644
index 000000000000..3ceab7b6ffc1
--- /dev/null
+++ b/include/linux/mdio/mdio-mscc-miim.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Driver for the MDIO interface of Microsemi network switches.
+ *
+ * Author: Colin Foster <[email protected]>
+ * Copyright (C) 2021 Innovative Advantage
+ */
+#ifndef MDIO_MSCC_MIIM_H
+#define MDIO_MSCC_MIIM_H
+
+#include <linux/device.h>
+#include <linux/phy.h>
+#include <linux/regmap.h>
+
+int mscc_miim_setup(struct device *device, struct mii_bus **bus,
+ struct regmap *mii_regmap, int status_offset,
+ struct regmap *phy_regmap, int reset_offset);
+
+#endif
--
2.25.1
Move 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 | 3 +-
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 520 +-------------------
drivers/net/ethernet/mscc/vsc7514_regs.c | 522 +++++++++++++++++++++
include/soc/mscc/vsc7514_regs.h | 27 ++
4 files changed, 562 insertions(+), 510 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/vsc7514_regs.c
create mode 100644 include/soc/mscc/vsc7514_regs.h
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index 722c27694b21..dfa939376d6c 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -7,7 +7,8 @@ mscc_ocelot_switch_lib-y := \
ocelot_vcap.o \
ocelot_flower.o \
ocelot_ptp.o \
- ocelot_devlink.o
+ ocelot_devlink.o \
+ vsc7514_regs.o
mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
mscc_ocelot-y := \
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 38103b0255b0..2c763784f69b 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -18,313 +18,20 @@
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_hsio.h>
+#include <soc/mscc/vsc7514_regs.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,
- [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,
- [DEV_GMII] = ocelot_dev_gmii_regmap,
+ [ANA] = vsc7514_ana_regmap,
+ [QS] = vsc7514_qs_regmap,
+ [QSYS] = vsc7514_qsys_regmap,
+ [REW] = vsc7514_rew_regmap,
+ [SYS] = vsc7514_sys_regmap,
+ [S0] = vsc7514_vcap_regmap,
+ [S1] = vsc7514_vcap_regmap,
+ [S2] = vsc7514_vcap_regmap,
+ [PTP] = vsc7514_ptp_regmap,
+ [DEV_GMII] = vsc7514_dev_gmii_regmap,
};
static const struct reg_field ocelot_regfields[REGFIELD_MAX] = {
@@ -633,211 +340,6 @@ static const struct ocelot_ops ocelot_ops = {
.netdev_to_port = ocelot_netdev_to_port,
};
-static const struct vcap_field vsc7514_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 vsc7514_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 vsc7514_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 vsc7514_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 vsc7514_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 vsc7514_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 vsc7514_vcap_props[] = {
[VCAP_ES0] = {
.action_type_width = 0,
diff --git a/drivers/net/ethernet/mscc/vsc7514_regs.c b/drivers/net/ethernet/mscc/vsc7514_regs.c
new file mode 100644
index 000000000000..b041756d1b78
--- /dev/null
+++ b/drivers/net/ethernet/mscc/vsc7514_regs.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ * Copyright (c) 2021 Innovative Advantage
+ */
+#include <soc/mscc/ocelot_vcap.h>
+#include "ocelot.h"
+
+const u32 vsc7514_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(vsc7514_ana_regmap);
+
+const u32 vsc7514_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(vsc7514_qs_regmap);
+
+const u32 vsc7514_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(vsc7514_qsys_regmap);
+
+const u32 vsc7514_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(vsc7514_rew_regmap);
+
+const u32 vsc7514_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(vsc7514_sys_regmap);
+
+const u32 vsc7514_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(vsc7514_vcap_regmap);
+
+const u32 vsc7514_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(vsc7514_ptp_regmap);
+
+const u32 vsc7514_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(vsc7514_dev_gmii_regmap);
+
+const struct vcap_field vsc7514_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 },
+};
+EXPORT_SYMBOL(vsc7514_vcap_es0_keys);
+
+const struct vcap_field vsc7514_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 },
+};
+EXPORT_SYMBOL(vsc7514_vcap_es0_actions);
+
+const struct vcap_field vsc7514_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 },
+};
+EXPORT_SYMBOL(vsc7514_vcap_is1_keys);
+
+const struct vcap_field vsc7514_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 },
+};
+EXPORT_SYMBOL(vsc7514_vcap_is1_actions);
+
+const struct vcap_field vsc7514_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 },
+};
+EXPORT_SYMBOL(vsc7514_vcap_is2_keys);
+
+const struct vcap_field vsc7514_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 },
+};
+EXPORT_SYMBOL(vsc7514_vcap_is2_actions);
diff --git a/include/soc/mscc/vsc7514_regs.h b/include/soc/mscc/vsc7514_regs.h
new file mode 100644
index 000000000000..c39f64079a0f
--- /dev/null
+++ b/include/soc/mscc/vsc7514_regs.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2021 Innovative Advantage Inc.
+ */
+
+#ifndef VSC7514_REGS_H
+#define VSC7514_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[];
+
+extern const struct vcap_field vsc7514_vcap_es0_keys[];
+extern const struct vcap_field vsc7514_vcap_es0_actions[];
+extern const struct vcap_field vsc7514_vcap_is1_keys[];
+extern const struct vcap_field vsc7514_vcap_is1_actions[];
+extern const struct vcap_field vsc7514_vcap_is2_keys[];
+extern const struct vcap_field vsc7514_vcap_is2_actions[];
+
+#endif
--
2.25.1
Switch seville to use of_mdiobus_register(bus, NULL) instead of just
mdiobus_register. This code is about to be pulled into a separate module
that can optionally define ports by the device_node.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 92eae63150ea..84681642d237 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1108,7 +1108,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
/* Needed in order to initialize the bus mutex lock */
- rc = mdiobus_register(bus);
+ rc = of_mdiobus_register(bus, NULL);
if (rc < 0) {
dev_err(dev, "failed to register MDIO bus\n");
return rc;
--
2.25.1
pcs-lynx.c used lynx_pcs and lynx as a variable name within the same file.
This standardizes all internal variables to just "lynx"
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/pcs/pcs-lynx.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c
index 7ff7f86ad430..fd3445374955 100644
--- a/drivers/net/pcs/pcs-lynx.c
+++ b/drivers/net/pcs/pcs-lynx.c
@@ -345,17 +345,17 @@ static const struct phylink_pcs_ops lynx_pcs_phylink_ops = {
struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
{
- struct lynx_pcs *lynx_pcs;
+ struct lynx_pcs *lynx;
- lynx_pcs = kzalloc(sizeof(*lynx_pcs), GFP_KERNEL);
- if (!lynx_pcs)
+ lynx = kzalloc(sizeof(*lynx), GFP_KERNEL);
+ if (!lynx)
return NULL;
- lynx_pcs->mdio = mdio;
- lynx_pcs->pcs.ops = &lynx_pcs_phylink_ops;
- lynx_pcs->pcs.poll = true;
+ lynx->mdio = mdio;
+ lynx->pcs.ops = &lynx_pcs_phylink_ops;
+ lynx->pcs.poll = true;
- return lynx_to_phylink_pcs(lynx_pcs);
+ return lynx_to_phylink_pcs(lynx);
}
EXPORT_SYMBOL(lynx_pcs_create);
--
2.25.1
struct gpio_chip recommends passing -1 as base to gpiolib. Doing so avoids
conflicts when the chip is external and gpiochip0 already exists.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/pinctrl/pinctrl-ocelot.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
index cc7fb0556169..f015404c425c 100644
--- a/drivers/pinctrl/pinctrl-ocelot.c
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -1308,7 +1308,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
gc = &info->gpio_chip;
gc->ngpio = info->desc->npins;
gc->parent = &pdev->dev;
- gc->base = 0;
+ gc->base = -1;
gc->of_node = info->dev->of_node;
gc->label = "ocelot-gpio";
--
2.25.1
Existing felix devices all have an initialized pcs array. Future devices
might not, so running a NULL check on the array before dereferencing it
will allow those future drivers to not crash at this point
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 327cc4654806..94702042246c 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -820,7 +820,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
Utilize the Felix and Ocelot drivers to allow control of the VSC7511,
VSC7512, VSC7513 and VSC7514 chips from an external CPU over SPI.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/Kconfig | 15 +
drivers/net/dsa/ocelot/Makefile | 6 +
drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c | 946 ++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot.c | 8 +
include/soc/mscc/ocelot.h | 26 +
include/soc/mscc/vsc7514_regs.h | 16 +-
6 files changed, 1009 insertions(+), 8 deletions(-)
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 220b0b027b55..43982909b6bf 100644
--- a/drivers/net/dsa/ocelot/Kconfig
+++ b/drivers/net/dsa/ocelot/Kconfig
@@ -15,6 +15,21 @@ 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 MDIO_MSCC_MIIM
+ select PINCTRL_MICROCHIP_SGPIO
+ select PINCTRL_OCELOT
+ 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..c4a6f4b7a717
--- /dev/null
+++ b/drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c
@@ -0,0 +1,946 @@
+// 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 <linux/phylink.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/regmap.h>
+#include <soc/mscc/ocelot_ana.h>
+#include <soc/mscc/ocelot_dev.h>
+#include <soc/mscc/ocelot_qsys.h>
+#include <soc/mscc/ocelot_vcap.h>
+#include <soc/mscc/ocelot_ptp.h>
+#include <soc/mscc/ocelot_sys.h>
+#include <soc/mscc/ocelot.h>
+#include <soc/mscc/vsc7514_regs.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_GPIO_GPIO_OUT_SET, 0x0034),
+ REG(GCB_GPIO_GPIO_OUT_CLR, 0x0038),
+ REG(GCB_GPIO_GPIO_OUT, 0x003c),
+ REG(GCB_GPIO_GPIO_IN, 0x0040),
+ REG(GCB_GPIO_GPIO_OE, 0x0044),
+ REG(GCB_GPIO_GPIO_ALT, 0x0054),
+ 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),
+ REG(GCB_SIO_CTRL_SIO_INPUT_DATA, 0x00f8),
+};
+
+static const u32 *vsc7512_regmap[TARGET_MAX] = {
+ [ANA] = vsc7514_ana_regmap,
+ [QS] = vsc7514_qs_regmap,
+ [QSYS] = vsc7514_qsys_regmap,
+ [REW] = vsc7514_rew_regmap,
+ [SYS] = vsc7514_sys_regmap,
+ [S0] = vsc7514_vcap_regmap,
+ [S1] = vsc7514_vcap_regmap,
+ [S2] = vsc7514_vcap_regmap,
+ [PTP] = vsc7514_ptp_regmap,
+ [GCB] = vsc7512_gcb_regmap,
+ [DEV_GMII] = vsc7514_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, 0, GCB_PHY_PHY_CFG);
+ ocelot_write(ocelot, 0x1ff, GCB_PHY_PHY_CFG);
+ mdelay(500);
+}
+
+static struct ocelot_spi_data *felix_to_ocelot_spi(struct felix *felix)
+{
+ return container_of(felix, struct ocelot_spi_data, felix);
+}
+
+static 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 u32 ocelot_offset_from_reg_base(struct ocelot *ocelot, u32 target,
+ u32 reg)
+{
+ return ocelot->map[target][reg & REG_MASK];
+}
+
+static void ocelot_spi_register_pinctrl(struct ocelot *ocelot)
+{
+ struct device_node *pinctrl_node;
+ struct device *dev = ocelot->dev;
+ struct regmap *regmap;
+ u32 pinctrl_offset;
+ int err;
+
+ pinctrl_node = of_get_child_by_name(dev->of_node, "pinctrl");
+ if (!pinctrl_node)
+ return;
+
+ regmap = ocelot->targets[GCB];
+ pinctrl_offset = ocelot_offset_from_reg_base(ocelot, GCB,
+ GCB_GPIO_GPIO_OUT_SET);
+
+ err = ocelot_pinctrl_core_probe(dev, NULL, regmap, pinctrl_offset, NULL,
+ 0, pinctrl_node);
+ if (err) {
+ dev_info(dev, "error setting up pinctrl device\n");
+ return;
+ }
+}
+
+static int vsc7512_spi_bus_init(struct ocelot *ocelot)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+ struct device *dev = ocelot->dev;
+ struct device_node *mdio_node;
+ int rval;
+
+ rval = ocelot_spi_init_bus(ocelot);
+ if (rval) {
+ dev_err(ocelot->dev, "error initializing SPI bus\n");
+ goto clear_mdio;
+ }
+
+ /* Set up the pins before probing the MDIO bus */
+ ocelot_spi_register_pinctrl(ocelot);
+
+ mdio_node = of_get_child_by_name(dev->of_node, "mdio");
+ if (!mdio_node)
+ dev_info(ocelot->dev,
+ "mdio children not found in device tree\n");
+
+ rval = felix_of_mdio_register(ocelot, mdio_node);
+ 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 = 0x7107022b,
+ .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;
+
+ WARN_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, Pause);
+ phylink_set(mask, Autoneg);
+ 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 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 = vsc7514_vcap_es0_keys,
+ .actions = vsc7514_vcap_es0_actions,
+ },
+ [VCAP_IS1] = {
+ .action_type_width = 0,
+ .action_table = {
+ [IS1_ACTION_TYPE_NORMAL] = {
+ .width = 78,
+ .count = 4,
+ },
+ },
+ .target = S1,
+ .keys = vsc7514_vcap_is1_keys,
+ .actions = vsc7514_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 = vsc7514_vcap_is2_keys,
+ .actions = vsc7514_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 unsigned long vsc7512_get_quirk_for_port(struct ocelot *ocelot,
+ int port)
+{
+ /* Currently Ocelot PCS is not functioning. When that happens, different
+ * ports will have different quirks, which will need to be addressed
+ * here.
+ */
+ return 0;
+}
+
+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,
+ .num_ports = 11,
+ .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,
+ .get_quirk_for_port = vsc7512_get_quirk_for_port,
+};
+
+static void ocelot_spi_register_sgpio(struct ocelot *ocelot)
+{
+ struct device *dev = ocelot->dev;
+ struct device_node *sgpio_node;
+ u32 offset;
+ int err;
+
+ sgpio_node = of_get_child_by_name(dev->of_node, "sgpio");
+ if (!sgpio_node)
+ return;
+
+ offset = ocelot_offset_from_reg_base(ocelot, GCB,
+ GCB_SIO_CTRL_SIO_INPUT_DATA);
+ err = microchip_sgpio_core_probe(dev, sgpio_node, ocelot->targets[GCB],
+ offset);
+
+ if (err)
+ dev_info(dev, "error setting up sgpio device\n");
+}
+
+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;
+
+ 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;
+ felix->tag_proto = DSA_TAG_PROTO_OCELOT;
+
+ err = dsa_register_switch(ds);
+
+ if (err) {
+ dev_err(dev, "Failed to register DSA switch: %d\n", err);
+ goto err_register_ds;
+ }
+
+ ocelot_spi_register_sgpio(ocelot);
+
+ 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 e6c18b598d5c..5a4c046b5e20 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -2243,6 +2243,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 8c27f8f79fff..6aeb7eac73f5 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -121,6 +121,7 @@ enum ocelot_target {
PTP,
GCB,
DEV_GMII,
+ DEV_CPUORG,
TARGET_MAX,
};
@@ -396,9 +397,18 @@ enum ocelot_reg {
PTP_CLK_CFG_ADJ_CFG,
PTP_CLK_CFG_ADJ_FREQ,
GCB_SOFT_RST = GCB << TARGET_OFFSET,
+ GCB_GPIO_GPIO_OUT_SET,
+ GCB_GPIO_GPIO_OUT_CLR,
+ GCB_GPIO_GPIO_OUT,
+ GCB_GPIO_GPIO_IN,
+ GCB_GPIO_GPIO_OE,
+ GCB_GPIO_GPIO_ALT,
GCB_MIIM_MII_STATUS,
GCB_MIIM_MII_CMD,
GCB_MIIM_MII_DATA,
+ GCB_PHY_PHY_CFG,
+ GCB_PHY_PHY_STAT,
+ GCB_SIO_CTRL_SIO_INPUT_DATA,
DEV_CLOCK_CFG = DEV_GMII << TARGET_OFFSET,
DEV_PORT_MISC,
DEV_EVENTS,
@@ -438,6 +448,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 {
@@ -496,6 +520,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,
@@ -552,6 +577,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);
diff --git a/include/soc/mscc/vsc7514_regs.h b/include/soc/mscc/vsc7514_regs.h
index c39f64079a0f..98743e252012 100644
--- a/include/soc/mscc/vsc7514_regs.h
+++ b/include/soc/mscc/vsc7514_regs.h
@@ -8,14 +8,14 @@
#ifndef VSC7514_REGS_H
#define VSC7514_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[];
+extern const u32 vsc7514_ana_regmap[];
+extern const u32 vsc7514_qs_regmap[];
+extern const u32 vsc7514_qsys_regmap[];
+extern const u32 vsc7514_rew_regmap[];
+extern const u32 vsc7514_sys_regmap[];
+extern const u32 vsc7514_vcap_regmap[];
+extern const u32 vsc7514_ptp_regmap[];
+extern const u32 vsc7514_dev_gmii_regmap[];
extern const struct vcap_field vsc7514_vcap_es0_keys[];
extern const struct vcap_field vsc7514_vcap_es0_actions[];
--
2.25.1
In order to allow external control via SPI, memory-mapped areas must be
changed to use the generic regmap interface. This is step 1, and is
followed by an implementation that allows a custom regmap.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/pinctrl/pinctrl-ocelot.c | 66 ++++++++++++++++++++++++++------
1 file changed, 55 insertions(+), 11 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
index f015404c425c..b9acb80d6b3f 100644
--- a/drivers/pinctrl/pinctrl-ocelot.c
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -152,7 +152,7 @@ struct ocelot_pinctrl {
struct pinctrl_dev *pctl;
struct gpio_chip gpio_chip;
struct regmap *map;
- void __iomem *pincfg;
+ struct regmap *pincfg;
struct pinctrl_desc *desc;
struct ocelot_pmx_func func[FUNC_MAX];
u8 stride;
@@ -819,7 +819,11 @@ static int ocelot_hw_get_value(struct ocelot_pinctrl *info,
int ret = -EOPNOTSUPP;
if (info->pincfg) {
- u32 regcfg = readl(info->pincfg + (pin * sizeof(u32)));
+ u32 regcfg;
+
+ ret = regmap_read(info->pincfg, pin, ®cfg);
+ if (ret)
+ return ret;
ret = 0;
switch (reg) {
@@ -843,6 +847,24 @@ static int ocelot_hw_get_value(struct ocelot_pinctrl *info,
return ret;
}
+static int ocelot_pincfg_clrsetbits(struct ocelot_pinctrl *info, u32 regaddr,
+ u32 clrbits, u32 setbits)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(info->pincfg, regaddr, &val);
+ if (ret)
+ return ret;
+
+ val &= ~clrbits;
+ val |= setbits;
+
+ ret = regmap_write(info->pincfg, regaddr, val);
+
+ return ret;
+}
+
static int ocelot_hw_set_value(struct ocelot_pinctrl *info,
unsigned int pin,
unsigned int reg,
@@ -851,21 +873,23 @@ static int ocelot_hw_set_value(struct ocelot_pinctrl *info,
int ret = -EOPNOTSUPP;
if (info->pincfg) {
- void __iomem *regaddr = info->pincfg + (pin * sizeof(u32));
ret = 0;
switch (reg) {
case PINCONF_BIAS:
- ocelot_clrsetbits(regaddr, BIAS_BITS, val);
+ ret = ocelot_pincfg_clrsetbits(info, pin, BIAS_BITS,
+ val);
break;
case PINCONF_SCHMITT:
- ocelot_clrsetbits(regaddr, SCHMITT_BIT, val);
+ ret = ocelot_pincfg_clrsetbits(info, pin, SCHMITT_BIT,
+ val);
break;
case PINCONF_DRIVE_STRENGTH:
if (val <= 3)
- ocelot_clrsetbits(regaddr, DRIVE_BITS, val);
+ ret = ocelot_pincfg_clrsetbits(info, pin,
+ DRIVE_BITS, val);
else
ret = -EINVAL;
break;
@@ -1340,12 +1364,32 @@ static const struct of_device_id ocelot_pinctrl_of_match[] = {
{},
};
+static struct regmap *ocelot_pinctrl_create_pincfg(struct platform_device *pdev)
+{
+ void __iomem *base;
+
+ const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 32,
+ };
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ dev_dbg(&pdev->dev, "Failed to ioremap config registers (no extended pinconf)\n");
+ return NULL;
+ }
+
+ return devm_regmap_init_mmio(&pdev->dev, base, ®map_config);
+}
+
static int ocelot_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ocelot_pinctrl *info;
+ struct regmap *pincfg;
void __iomem *base;
- struct resource *res;
int ret;
struct regmap_config regmap_config = {
.reg_bits = 32,
@@ -1378,11 +1422,11 @@ static int ocelot_pinctrl_probe(struct platform_device *pdev)
/* Pinconf registers */
if (info->desc->confops) {
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- dev_dbg(dev, "Failed to ioremap config registers (no extended pinconf)\n");
+ pincfg = ocelot_pinctrl_create_pincfg(pdev);
+ if (IS_ERR(pincfg))
+ dev_dbg(dev, "Failed to create pincfg regmap\n");
else
- info->pincfg = base;
+ info->pincfg = pincfg;
}
ret = ocelot_pinctrl_register(pdev, info);
--
2.25.1
This is step 2 to allow a custom regmap interface into the driver. This
will allow regmaps that use other interfaces to fully utilize the pinctrl
features.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/pinctrl/pinctrl-ocelot.c | 150 ++++++++++++++++++++-----------
include/soc/mscc/ocelot.h | 18 ++++
2 files changed, 118 insertions(+), 50 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
index b9acb80d6b3f..f8d2494b335c 100644
--- a/drivers/pinctrl/pinctrl-ocelot.c
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -25,9 +25,6 @@
#include "pinconf.h"
#include "pinmux.h"
-#define ocelot_clrsetbits(addr, clear, set) \
- writel((readl(addr) & ~(clear)) | (set), (addr))
-
/* PINCONFIG bits (sparx5 only) */
enum {
PINCONF_BIAS,
@@ -35,6 +32,9 @@ enum {
PINCONF_DRIVE_STRENGTH,
};
+#define ocelot_pinctrl_determine_stride(desc) \
+ (1 + (desc->npins - 1) / 32)
+
#define BIAS_PD_BIT BIT(4)
#define BIAS_PU_BIT BIT(3)
#define BIAS_BITS (BIAS_PD_BIT|BIAS_PU_BIT)
@@ -149,10 +149,13 @@ struct ocelot_pin_caps {
struct ocelot_pinctrl {
struct device *dev;
+ struct device_node *node;
struct pinctrl_dev *pctl;
struct gpio_chip gpio_chip;
struct regmap *map;
+ u32 regmap_offset;
struct regmap *pincfg;
+ u32 pincfg_offset;
struct pinctrl_desc *desc;
struct ocelot_pmx_func func[FUNC_MAX];
u8 stride;
@@ -714,7 +717,8 @@ static int ocelot_pin_function_idx(struct ocelot_pinctrl *info,
return -1;
}
-#define REG_ALT(msb, info, p) (OCELOT_GPIO_ALT0 * (info)->stride + 4 * ((msb) + ((info)->stride * ((p) / 32))))
+#define REG_ALT(msb, info, p) (OCELOT_GPIO_ALT0 * (info)->stride + 4 * ((msb) + \
+ ((info)->stride * ((p) / 32))) + (info)->regmap_offset)
static int ocelot_pinmux_set_mux(struct pinctrl_dev *pctldev,
unsigned int selector, unsigned int group)
@@ -744,7 +748,8 @@ static int ocelot_pinmux_set_mux(struct pinctrl_dev *pctldev,
return 0;
}
-#define REG(r, info, p) ((r) * (info)->stride + (4 * ((p) / 32)))
+#define REG(r, info, p) (((r) * (info)->stride + (4 * ((p) / 32))) + \
+ (info)->regmap_offset)
static int ocelot_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
@@ -766,10 +771,8 @@ static int ocelot_gpio_request_enable(struct pinctrl_dev *pctldev,
struct ocelot_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
unsigned int p = offset % 32;
- regmap_update_bits(info->map, REG_ALT(0, info, offset),
- BIT(p), 0);
- regmap_update_bits(info->map, REG_ALT(1, info, offset),
- BIT(p), 0);
+ regmap_update_bits(info->map, REG_ALT(0, info, offset), BIT(p), 0);
+ regmap_update_bits(info->map, REG_ALT(1, info, offset), BIT(p), 0);
return 0;
}
@@ -821,11 +824,11 @@ static int ocelot_hw_get_value(struct ocelot_pinctrl *info,
if (info->pincfg) {
u32 regcfg;
- ret = regmap_read(info->pincfg, pin, ®cfg);
+ ret = regmap_read(info->pincfg, pin + info->pincfg_offset,
+ ®cfg);
if (ret)
return ret;
- ret = 0;
switch (reg) {
case PINCONF_BIAS:
*val = regcfg & BIAS_BITS;
@@ -853,14 +856,14 @@ static int ocelot_pincfg_clrsetbits(struct ocelot_pinctrl *info, u32 regaddr,
u32 val;
int ret;
- ret = regmap_read(info->pincfg, regaddr, &val);
+ ret = regmap_read(info->pincfg, regaddr + info->pincfg_offset, &val);
if (ret)
return ret;
val &= ~clrbits;
val |= setbits;
- ret = regmap_write(info->pincfg, regaddr, val);
+ ret = regmap_write(info->pincfg, regaddr + info->pincfg_offset, val);
return ret;
}
@@ -873,7 +876,6 @@ static int ocelot_hw_set_value(struct ocelot_pinctrl *info,
int ret = -EOPNOTSUPP;
if (info->pincfg) {
-
ret = 0;
switch (reg) {
case PINCONF_BIAS:
@@ -1019,15 +1021,16 @@ static int ocelot_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
if (arg)
regmap_write(info->map,
REG(OCELOT_GPIO_OUT_SET, info,
- pin),
+ pin) + info->regmap_offset,
BIT(p));
else
regmap_write(info->map,
REG(OCELOT_GPIO_OUT_CLR, info,
- pin),
+ pin) + info->regmap_offset,
BIT(p));
regmap_update_bits(info->map,
- REG(OCELOT_GPIO_OE, info, pin),
+ REG(OCELOT_GPIO_OE, info, pin) +
+ info->regmap_offset,
BIT(p),
param == PIN_CONFIG_INPUT_ENABLE ?
0 : BIT(p));
@@ -1138,20 +1141,20 @@ static int ocelot_create_group_func_map(struct device *dev,
return 0;
}
-static int ocelot_pinctrl_register(struct platform_device *pdev,
+static int ocelot_pinctrl_register(struct device *dev,
struct ocelot_pinctrl *info)
{
int ret;
- ret = ocelot_create_group_func_map(&pdev->dev, info);
+ ret = ocelot_create_group_func_map(dev, info);
if (ret) {
- dev_err(&pdev->dev, "Unable to create group func map.\n");
+ dev_err(dev, "Unable to create group func map.\n");
return ret;
}
- info->pctl = devm_pinctrl_register(&pdev->dev, info->desc, info);
+ info->pctl = devm_pinctrl_register(dev, info->desc, info);
if (IS_ERR(info->pctl)) {
- dev_err(&pdev->dev, "Failed to register pinctrl\n");
+ dev_err(dev, "Failed to register pinctrl\n");
return PTR_ERR(info->pctl);
}
@@ -1320,7 +1323,7 @@ static void ocelot_irq_handler(struct irq_desc *desc)
}
}
-static int ocelot_gpiochip_register(struct platform_device *pdev,
+static int ocelot_gpiochip_register(struct device *dev,
struct ocelot_pinctrl *info)
{
struct gpio_chip *gc;
@@ -1331,7 +1334,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
gc = &info->gpio_chip;
gc->ngpio = info->desc->npins;
- gc->parent = &pdev->dev;
+ gc->parent = dev;
gc->base = -1;
gc->of_node = info->dev->of_node;
gc->label = "ocelot-gpio";
@@ -1342,8 +1345,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
girq->chip = &ocelot_irqchip;
girq->parent_handler = ocelot_irq_handler;
girq->num_parents = 1;
- girq->parents = devm_kcalloc(&pdev->dev, 1,
- sizeof(*girq->parents),
+ girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
@@ -1352,7 +1354,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
girq->handler = handle_edge_irq;
}
- return devm_gpiochip_add_data(&pdev->dev, gc, info);
+ return devm_gpiochip_add_data(dev, gc, info);
}
static const struct of_device_id ocelot_pinctrl_of_match[] = {
@@ -1384,56 +1386,104 @@ static struct regmap *ocelot_pinctrl_create_pincfg(struct platform_device *pdev)
return devm_regmap_init_mmio(&pdev->dev, base, ®map_config);
}
+static struct pinctrl_desc
+*ocelot_pinctrl_match_from_node(struct device_node *device_node)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(of_match_ptr(ocelot_pinctrl_of_match),
+ device_node);
+
+ if (match)
+ return (struct pinctrl_desc *)match->data;
+
+ return NULL;
+}
+
+int ocelot_pinctrl_core_probe(struct device *dev,
+ struct pinctrl_desc *pinctrl_desc,
+ struct regmap *regmap_base, u32 regmap_offset,
+ struct regmap *pincfg_base, u32 pincfg_offset,
+ struct device_node *device_node)
+{
+ struct ocelot_pinctrl *info;
+ int ret;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (!pinctrl_desc)
+ pinctrl_desc = ocelot_pinctrl_match_from_node(device_node);
+ if (!pinctrl_desc) {
+ dev_err(dev, "Failed to find device match\n");
+ return -ENODEV;
+ }
+
+ info->desc = pinctrl_desc;
+ info->stride = ocelot_pinctrl_determine_stride(info->desc);
+ info->dev = dev;
+ info->node = device_node;
+ info->map = regmap_base;
+ info->pincfg = pincfg_base;
+ info->regmap_offset = regmap_offset;
+ info->pincfg_offset = pincfg_offset;
+
+ ret = ocelot_pinctrl_register(dev, info);
+ if (ret)
+ return ret;
+
+ ret = ocelot_gpiochip_register(dev, info);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(ocelot_pinctrl_core_probe);
+
static int ocelot_pinctrl_probe(struct platform_device *pdev)
{
+ struct pinctrl_desc *pinctrl_desc;
struct device *dev = &pdev->dev;
- struct ocelot_pinctrl *info;
+ struct regmap *regmap;
struct regmap *pincfg;
void __iomem *base;
- int ret;
+ int ret, stride;
struct regmap_config regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
- info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->desc = (struct pinctrl_desc *)device_get_match_data(dev);
+ pinctrl_desc = (struct pinctrl_desc *)device_get_match_data(dev);
base = devm_ioremap_resource(dev,
platform_get_resource(pdev, IORESOURCE_MEM, 0));
if (IS_ERR(base))
return PTR_ERR(base);
- info->stride = 1 + (info->desc->npins - 1) / 32;
+ stride = ocelot_pinctrl_determine_stride(pinctrl_desc);
- regmap_config.max_register = OCELOT_GPIO_SD_MAP * info->stride + 15 * 4;
+ regmap_config.max_register = OCELOT_GPIO_SD_MAP * stride + 15 * 4;
- info->map = devm_regmap_init_mmio(dev, base, ®map_config);
- if (IS_ERR(info->map)) {
+ regmap = devm_regmap_init_mmio(dev, base, ®map_config);
+ if (IS_ERR(regmap)) {
dev_err(dev, "Failed to create regmap\n");
- return PTR_ERR(info->map);
+ return PTR_ERR(regmap);
}
- dev_set_drvdata(dev, info->map);
- info->dev = dev;
+ dev_set_drvdata(dev, regmap);
/* Pinconf registers */
- if (info->desc->confops) {
+ if (pinctrl_desc->confops) {
pincfg = ocelot_pinctrl_create_pincfg(pdev);
- if (IS_ERR(pincfg))
+ if (IS_ERR(pincfg)) {
dev_dbg(dev, "Failed to create pincfg regmap\n");
- else
- info->pincfg = pincfg;
+ pincfg = NULL;
+ }
}
- ret = ocelot_pinctrl_register(pdev, info);
- if (ret)
- return ret;
-
- ret = ocelot_gpiochip_register(pdev, info);
+ ret = ocelot_pinctrl_core_probe(dev, pinctrl_desc, regmap, 0, pincfg, 0,
+ dev->of_node);
if (ret)
return ret;
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 9a4ded4bff04..14acfe82d0a4 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -6,6 +6,7 @@
#define _SOC_MSCC_OCELOT_H
#include <linux/ptp_clock_kernel.h>
+#include <linux/pinctrl/pinctrl.h>
#include <linux/net_tstamp.h>
#include <linux/if_vlan.h>
#include <linux/regmap.h>
@@ -912,4 +913,21 @@ ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port,
}
#endif
+#if IS_ENABLED(CONFIG_PINCTRL_OCELOT)
+int ocelot_pinctrl_core_probe(struct device *dev,
+ struct pinctrl_desc *pinctrl_desc,
+ struct regmap *regmap_base, u32 regmap_offset,
+ struct regmap *pincfg_base, u32 pincfg_offset,
+ struct device_node *device_node);
+#else
+int ocelot_pinctrl_core_probe(struct device *dev,
+ struct pinctrl_desc *pinctrl_desc,
+ struct regmap *regmap_base, u32 regmap_offset,
+ struct regmap *pincfg_base, u32 pincfg_offset,
+ struct device_node *device_node)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
#endif
--
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 | 6 +++---
drivers/net/dsa/ocelot/seville_vsc9953.c | 1 +
4 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 94702042246c..615f6b84c688 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -1016,7 +1016,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");
@@ -1053,7 +1053,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 d7da307fc071..81a86bd60f03 100644
--- a/drivers/net/dsa/ocelot/felix.h
+++ b/drivers/net/dsa/ocelot/felix.h
@@ -46,6 +46,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 0b3ccfd54603..789e1ff0d13b 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -17,6 +17,8 @@
#include "felix.h"
#define VSC9959_TAS_GCL_ENTRY_MAX 63
+#define VSC9959_SWITCH_PCI_BAR 4
+#define VSC9959_IMDIO_PCI_BAR 0
static const u32 vsc9959_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x0089a0),
@@ -1365,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)
@@ -1384,9 +1387,6 @@ 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)
{
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 610bdfd31903..47da279a8ff7 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1087,6 +1087,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
Adopt regmap instead of a direct memory map so that custom regmaps and
other interfaces can be supported
Signed-off-by: Colin Foster <[email protected]>
---
drivers/pinctrl/pinctrl-microchip-sgpio.c | 37 +++++++++++++++++------
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-microchip-sgpio.c b/drivers/pinctrl/pinctrl-microchip-sgpio.c
index 78765faa245a..762611f76438 100644
--- a/drivers/pinctrl/pinctrl-microchip-sgpio.c
+++ b/drivers/pinctrl/pinctrl-microchip-sgpio.c
@@ -17,6 +17,7 @@
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include "core.h"
@@ -113,7 +114,8 @@ struct sgpio_priv {
u32 bitcount;
u32 ports;
u32 clock;
- u32 __iomem *regs;
+ struct regmap *regs;
+ u32 regs_offset;
const struct sgpio_properties *properties;
};
@@ -136,29 +138,32 @@ static inline int sgpio_addr_to_pin(struct sgpio_priv *priv, int port, int bit)
static inline u32 sgpio_readl(struct sgpio_priv *priv, u32 rno, u32 off)
{
- u32 __iomem *reg = &priv->regs[priv->properties->regoff[rno] + off];
+ u32 val = 0;
+
+ regmap_read(priv->regs,
+ priv->properties->regoff[rno] + off + priv->regs_offset,
+ &val);
- return readl(reg);
+ return val;
}
static inline void sgpio_writel(struct sgpio_priv *priv,
u32 val, u32 rno, u32 off)
{
- u32 __iomem *reg = &priv->regs[priv->properties->regoff[rno] + off];
-
- writel(val, reg);
+ regmap_write(priv->regs,
+ priv->properties->regoff[rno] + off + priv->regs_offset,
+ val);
}
static inline void sgpio_clrsetbits(struct sgpio_priv *priv,
u32 rno, u32 off, u32 clear, u32 set)
{
- u32 __iomem *reg = &priv->regs[priv->properties->regoff[rno] + off];
- u32 val = readl(reg);
+ u32 val = sgpio_readl(priv, rno, off);
val &= ~clear;
val |= set;
- writel(val, reg);
+ sgpio_writel(priv, val, rno, off);
}
static inline void sgpio_configure_bitstream(struct sgpio_priv *priv)
@@ -807,7 +812,13 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
struct reset_control *reset;
struct sgpio_priv *priv;
struct clk *clk;
+ u32 __iomem *regs;
u32 val;
+ struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -832,9 +843,15 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->regs = devm_regmap_init_mmio(dev, regs, ®map_config);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
+
+ priv->regs_offset = 0;
priv->properties = device_get_match_data(dev);
priv->in.is_input = true;
--
2.25.1
Functions existed for determining the node count by device, but not by
fwnode_handle. In the case where a driver could either be defined as a
standalone device or a node of a different device, parsing from the root of
the device might not make sense. As such, it becomes necessary to parse
from a child node instead of the device root node.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/base/property.c | 20 ++++++++++++++++----
include/linux/property.h | 1 +
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index f1f35b48ab8b..2ee675e1529d 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -845,19 +845,31 @@ bool fwnode_device_is_available(const struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(fwnode_device_is_available);
/**
- * device_get_child_node_count - return the number of child nodes for device
- * @dev: Device to cound the child nodes for
+ * fwnode_get_child_node_count - return the number of child nodes for the fwnode
+ * @fwnode: Node to count the childe nodes for
*/
-unsigned int device_get_child_node_count(struct device *dev)
+unsigned int fwnode_get_child_node_count(struct fwnode_handle *fwnode)
{
struct fwnode_handle *child;
unsigned int count = 0;
- device_for_each_child_node(dev, child)
+ fwnode_for_each_child_node(fwnode, child)
count++;
return count;
}
+EXPORT_SYMBOL_GPL(fwnode_get_child_node_count);
+
+/**
+ * device_get_child_node_count - return the number of child nodes for device
+ * @dev: Device to count the child nodes for
+ */
+unsigned int device_get_child_node_count(struct device *dev)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+
+ return fwnode_get_child_node_count(fwnode);
+}
EXPORT_SYMBOL_GPL(device_get_child_node_count);
bool device_dma_supported(struct device *dev)
diff --git a/include/linux/property.h b/include/linux/property.h
index 88fa726a76df..6dc71029cfc5 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -122,6 +122,7 @@ void fwnode_handle_put(struct fwnode_handle *fwnode);
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index);
+unsigned int fwnode_get_child_node_count(struct fwnode_handle *fwnode);
unsigned int device_get_child_node_count(struct device *dev);
static inline bool device_property_read_bool(struct device *dev,
--
2.25.1
microchip-sgpio is being updated to support being a device of a device. As
such, standard devicetree parsing functions (device_property_count_u32) are
being changed to a lower level functions like fwnode_property_count_u32.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/pinctrl/pinctrl-microchip-sgpio.c | 72 +++++++++++++++--------
1 file changed, 46 insertions(+), 26 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-microchip-sgpio.c b/drivers/pinctrl/pinctrl-microchip-sgpio.c
index 762611f76438..10736ef5c6ca 100644
--- a/drivers/pinctrl/pinctrl-microchip-sgpio.c
+++ b/drivers/pinctrl/pinctrl-microchip-sgpio.c
@@ -109,6 +109,7 @@ struct sgpio_bank {
struct sgpio_priv {
struct device *dev;
+ struct device_node *dev_node;
struct sgpio_bank in;
struct sgpio_bank out;
u32 bitcount;
@@ -524,12 +525,16 @@ static int microchip_sgpio_of_xlate(struct gpio_chip *gc,
static int microchip_sgpio_get_ports(struct sgpio_priv *priv)
{
const char *range_property_name = "microchip,sgpio-port-ranges";
+ struct device_node *dev_node = priv->dev_node;
+ const struct fwnode_handle *fwnode_handle;
struct device *dev = priv->dev;
u32 range_params[64];
int i, nranges, ret;
+ fwnode_handle = of_fwnode_handle(dev_node);
+
/* Calculate port mask */
- nranges = device_property_count_u32(dev, range_property_name);
+ nranges = fwnode_property_count_u32(fwnode_handle, range_property_name);
if (nranges < 2 || nranges % 2 || nranges > ARRAY_SIZE(range_params)) {
dev_err(dev, "%s port range: '%s' property\n",
nranges == -EINVAL ? "Missing" : "Invalid",
@@ -537,7 +542,7 @@ static int microchip_sgpio_get_ports(struct sgpio_priv *priv)
return -EINVAL;
}
- ret = device_property_read_u32_array(dev, range_property_name,
+ ret = fwnode_property_read_u32_array(fwnode_handle, range_property_name,
range_params, nranges);
if (ret) {
dev_err(dev, "failed to parse '%s' property: %d\n",
@@ -804,11 +809,38 @@ static int microchip_sgpio_register_bank(struct device *dev,
return ret;
}
+static const struct of_device_id microchip_sgpio_gpio_of_match[] = {
+ {
+ .compatible = "microchip,sparx5-sgpio",
+ .data = &properties_sparx5,
+ }, {
+ .compatible = "mscc,luton-sgpio",
+ .data = &properties_luton,
+ }, {
+ .compatible = "mscc,ocelot-sgpio",
+ .data = &properties_ocelot,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct sgpio_properties
+*microchip_sgpio_match_from_node(struct device_node *node)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(of_match_ptr(microchip_sgpio_gpio_of_match), node);
+ if (match)
+ return (struct sgpio_properties *)match->data;
+ return NULL;
+}
+
static int microchip_sgpio_probe(struct platform_device *pdev)
{
int div_clock = 0, ret, port, i, nbanks;
struct device *dev = &pdev->dev;
- struct fwnode_handle *fwnode;
+ struct device_node *node = dev_of_node(dev);
+ struct fwnode_handle *child, *fwnode;
struct reset_control *reset;
struct sgpio_priv *priv;
struct clk *clk;
@@ -825,18 +857,21 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
return -ENOMEM;
priv->dev = dev;
+ priv->dev_node = node;
+
+ fwnode = of_fwnode_handle(node);
- reset = devm_reset_control_get_optional_shared(&pdev->dev, "switch");
+ reset = devm_reset_control_get_optional_shared(dev, "switch");
if (IS_ERR(reset))
return dev_err_probe(dev, PTR_ERR(reset), "Failed to get reset\n");
reset_control_reset(reset);
- clk = devm_clk_get(dev, NULL);
+ clk = devm_get_clk_from_child(dev, node, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clock\n");
div_clock = clk_get_rate(clk);
- if (device_property_read_u32(dev, "bus-frequency", &priv->clock))
+ if (fwnode_property_read_u32(fwnode, "bus-frequency", &priv->clock))
priv->clock = 12500000;
if (priv->clock == 0 || priv->clock > (div_clock / 2)) {
dev_err(dev, "Invalid frequency %d\n", priv->clock);
@@ -852,7 +887,7 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
return PTR_ERR(priv->regs);
priv->regs_offset = 0;
- priv->properties = device_get_match_data(dev);
+ priv->properties = microchip_sgpio_match_from_node(node);
priv->in.is_input = true;
/* Get rest of device properties */
@@ -860,17 +895,17 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
if (ret)
return ret;
- nbanks = device_get_child_node_count(dev);
+ nbanks = fwnode_get_child_node_count(fwnode);
if (nbanks != 2) {
dev_err(dev, "Must have 2 banks (have %d)\n", nbanks);
return -EINVAL;
}
i = 0;
- device_for_each_child_node(dev, fwnode) {
- ret = microchip_sgpio_register_bank(dev, priv, fwnode, i++);
+ fwnode_for_each_child_node(fwnode, child) {
+ ret = microchip_sgpio_register_bank(dev, priv, child, i++);
if (ret) {
- fwnode_handle_put(fwnode);
+ fwnode_handle_put(child);
return ret;
}
}
@@ -892,21 +927,6 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id microchip_sgpio_gpio_of_match[] = {
- {
- .compatible = "microchip,sparx5-sgpio",
- .data = &properties_sparx5,
- }, {
- .compatible = "mscc,luton-sgpio",
- .data = &properties_luton,
- }, {
- .compatible = "mscc,ocelot-sgpio",
- .data = &properties_ocelot,
- }, {
- /* sentinel */
- }
-};
-
static struct platform_driver microchip_sgpio_pinctrl_driver = {
.driver = {
.name = "pinctrl-microchip-sgpio",
--
2.25.1
Allow external drivers to hook into microchip_sgpio with custom regmaps. In
the case where the sgpio is part of an external chip like a VSC7512
controlled over SPI, a pre-existing SPI regmap can be used.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/pinctrl/pinctrl-microchip-sgpio.c | 46 ++++++++++++++---------
include/soc/mscc/ocelot.h | 11 ++++++
2 files changed, 40 insertions(+), 17 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-microchip-sgpio.c b/drivers/pinctrl/pinctrl-microchip-sgpio.c
index 10736ef5c6ca..2ddee50707d0 100644
--- a/drivers/pinctrl/pinctrl-microchip-sgpio.c
+++ b/drivers/pinctrl/pinctrl-microchip-sgpio.c
@@ -835,22 +835,15 @@ static struct sgpio_properties
return NULL;
}
-static int microchip_sgpio_probe(struct platform_device *pdev)
+int microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
+ struct regmap *regmap, u32 offset)
{
int div_clock = 0, ret, port, i, nbanks;
- struct device *dev = &pdev->dev;
- struct device_node *node = dev_of_node(dev);
struct fwnode_handle *child, *fwnode;
struct reset_control *reset;
struct sgpio_priv *priv;
struct clk *clk;
- u32 __iomem *regs;
u32 val;
- struct regmap_config regmap_config = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- };
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -878,18 +871,14 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- priv->regs = devm_regmap_init_mmio(dev, regs, ®map_config);
- if (IS_ERR(priv->regs))
- return PTR_ERR(priv->regs);
-
+ priv->regs = regmap;
priv->regs_offset = 0;
priv->properties = microchip_sgpio_match_from_node(node);
priv->in.is_input = true;
+ if (!priv->properties)
+ return dev_err_probe(dev, -EINVAL, "No property match found\n");
+
/* Get rest of device properties */
ret = microchip_sgpio_get_ports(priv);
if (ret)
@@ -926,6 +915,29 @@ static int microchip_sgpio_probe(struct platform_device *pdev)
return 0;
}
+EXPORT_SYMBOL(microchip_sgpio_core_probe);
+
+static int microchip_sgpio_probe(struct platform_device *pdev)
+{
+ struct regmap_config regmap_config = {0};
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ u32 __iomem *regs;
+
+ regmap_config.reg_bits = 32;
+ regmap_config.val_bits = 32;
+ regmap_config.reg_stride = 4;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ regmap = devm_regmap_init_mmio(dev, regs, ®map_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return microchip_sgpio_core_probe(dev, dev->of_node, regmap, 0);
+}
static struct platform_driver microchip_sgpio_pinctrl_driver = {
.driver = {
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 14acfe82d0a4..8c27f8f79fff 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -930,4 +930,15 @@ int ocelot_pinctrl_core_probe(struct device *dev,
}
#endif
+#if IS_ENABLED(CONFIG_PINCTRL_MICROCHIP_SGPIO)
+int microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
+ struct regmap *regmap, u32 offset);
+#else
+int microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
+ struct regmap *regmap, u32 offset)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
#endif
--
2.25.1
Remove references to lynx_pcs structures so drivers like the Felix DSA
can reference alternate PCS drivers.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/felix.c | 3 +--
drivers/net/dsa/ocelot/felix.h | 2 +-
drivers/net/dsa/ocelot/felix_vsc9959.c | 18 +++++++-------
drivers/net/dsa/ocelot/seville_vsc9953.c | 22 +++++++++--------
.../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 12 ++++++----
.../net/ethernet/freescale/dpaa2/dpaa2-mac.h | 3 +--
.../net/ethernet/freescale/enetc/enetc_pf.c | 15 +++++++-----
.../net/ethernet/freescale/enetc/enetc_pf.h | 4 ++--
drivers/net/pcs/pcs-lynx.c | 24 +++++++++++++++----
include/linux/pcs-lynx.h | 9 +++----
10 files changed, 65 insertions(+), 47 deletions(-)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index f53db233148d..6d27c29a9c7b 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -21,7 +21,6 @@
#include <linux/of_net.h>
#include <linux/pci.h>
#include <linux/of.h>
-#include <linux/pcs-lynx.h>
#include <net/pkt_sched.h>
#include <net/dsa.h>
#include "felix.h"
@@ -821,7 +820,7 @@ static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
struct dsa_port *dp = dsa_to_port(ds, port);
if (felix->pcs && felix->pcs[port])
- phylink_set_pcs(dp->pl, &felix->pcs[port]->pcs);
+ phylink_set_pcs(dp->pl, felix->pcs[port]);
}
unsigned long felix_quirks_have_rate_adaptation(struct ocelot *ocelot,
diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
index b5fa5b7325b1..f3f606fbd88d 100644
--- a/drivers/net/dsa/ocelot/felix.h
+++ b/drivers/net/dsa/ocelot/felix.h
@@ -59,7 +59,7 @@ struct felix {
const struct felix_info *info;
struct ocelot ocelot;
struct mii_bus *imdio;
- struct lynx_pcs **pcs;
+ struct phylink_pcs **pcs;
resource_size_t switch_base;
resource_size_t imdio_base;
enum dsa_tag_protocol tag_proto;
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 5056f39dc47e..636bfb096c66 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -1044,7 +1044,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
int rc;
felix->pcs = devm_kcalloc(dev, felix->info->num_ports,
- sizeof(struct lynx_pcs *),
+ sizeof(struct phylink_pcs *),
GFP_KERNEL);
if (!felix->pcs) {
dev_err(dev, "failed to allocate array for PCS PHYs\n");
@@ -1093,8 +1093,8 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
for (port = 0; port < felix->info->num_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port];
+ struct phylink_pcs *phylink_pcs;
struct mdio_device *pcs;
- struct lynx_pcs *lynx;
if (dsa_is_unused_port(felix->ds, port))
continue;
@@ -1106,13 +1106,13 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
if (IS_ERR(pcs))
continue;
- lynx = lynx_pcs_create(pcs);
- if (!lynx) {
+ phylink_pcs = lynx_pcs_create(pcs);
+ if (!phylink_pcs) {
mdio_device_free(pcs);
continue;
}
- felix->pcs[port] = lynx;
+ felix->pcs[port] = phylink_pcs;
dev_info(dev, "Found PCS at internal MDIO address %d\n", port);
}
@@ -1126,13 +1126,13 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
int port;
for (port = 0; port < ocelot->num_phys_ports; port++) {
- struct lynx_pcs *pcs = felix->pcs[port];
+ struct phylink_pcs *phylink_pcs = felix->pcs[port];
- if (!pcs)
+ if (!phylink_pcs)
continue;
- mdio_device_free(pcs->mdio);
- lynx_pcs_destroy(pcs);
+ mdio_device_free(phylink_pcs->mdio);
+ lynx_pcs_destroy(phylink_pcs);
}
mdiobus_unregister(felix->imdio);
}
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 362ba66401d8..49fc8220d636 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1005,7 +1005,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
int rc;
felix->pcs = devm_kcalloc(dev, felix->info->num_ports,
- sizeof(struct phy_device *),
+ sizeof(struct phylink_pcs *),
GFP_KERNEL);
if (!felix->pcs) {
dev_err(dev, "failed to allocate array for PCS PHYs\n");
@@ -1026,9 +1026,9 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
for (port = 0; port < felix->info->num_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port];
- int addr = port + 4;
+ struct phylink_pcs *phylink_pcs;
struct mdio_device *pcs;
- struct lynx_pcs *lynx;
+ int addr = port + 4;
if (dsa_is_unused_port(felix->ds, port))
continue;
@@ -1040,13 +1040,13 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
if (IS_ERR(pcs))
continue;
- lynx = lynx_pcs_create(pcs);
- if (!lynx) {
+ phylink_pcs = lynx_pcs_create(pcs);
+ if (!phylink_pcs) {
mdio_device_free(pcs);
continue;
}
- felix->pcs[port] = lynx;
+ felix->pcs[port] = phylink_pcs;
dev_info(dev, "Found PCS at internal MDIO address %d\n", addr);
}
@@ -1060,13 +1060,15 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
int port;
for (port = 0; port < ocelot->num_phys_ports; port++) {
- struct lynx_pcs *pcs = felix->pcs[port];
+ struct phylink_pcs *phylink_pcs = felix->pcs[port];
+ struct mdio_device *mdio_device;
- if (!pcs)
+ if (!phylink_pcs)
continue;
- mdio_device_free(pcs->mdio);
- lynx_pcs_destroy(pcs);
+ mdio_device = lynx_pcs_get_mdio(phylink_pcs);
+ mdio_device_free(mdio_device);
+ lynx_pcs_destroy(phylink_pcs);
}
felix_mdio_bus_free(ocelot);
}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
index ef8f0a055024..e5fa181fbe2e 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
@@ -286,11 +286,13 @@ static int dpaa2_pcs_create(struct dpaa2_mac *mac,
static void dpaa2_pcs_destroy(struct dpaa2_mac *mac)
{
- struct lynx_pcs *pcs = mac->pcs;
+ struct phylink_pcs *phylink_pcs = mac->pcs;
- if (pcs) {
- struct device *dev = &pcs->mdio->dev;
- lynx_pcs_destroy(pcs);
+ if (phylink_pcs) {
+ struct mdio_device *mdio = lynx_get_mdio_device(phylink_pcs);
+ struct device *dev = &mdio->dev;
+
+ lynx_pcs_destroy(phylink_pcs);
put_device(dev);
mac->pcs = NULL;
}
@@ -349,7 +351,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
mac->phylink = phylink;
if (mac->pcs)
- phylink_set_pcs(mac->phylink, &mac->pcs->pcs);
+ phylink_set_pcs(mac->phylink, mac->pcs);
err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
if (err) {
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
index 7842cbb2207a..1331a8477fe4 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
@@ -7,7 +7,6 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/phylink.h>
-#include <linux/pcs-lynx.h>
#include "dpmac.h"
#include "dpmac-cmd.h"
@@ -23,7 +22,7 @@ struct dpaa2_mac {
struct phylink *phylink;
phy_interface_t if_mode;
enum dpmac_link_type if_link_type;
- struct lynx_pcs *pcs;
+ struct phylink_pcs *pcs;
struct fwnode_handle *fw_node;
};
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 0e87c7043b77..125a539b0654 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -828,7 +828,7 @@ static int enetc_imdio_create(struct enetc_pf *pf)
{
struct device *dev = &pf->si->pdev->dev;
struct enetc_mdio_priv *mdio_priv;
- struct lynx_pcs *pcs_lynx;
+ struct phylink_pcs *phylink_pcs;
struct mdio_device *pcs;
struct mii_bus *bus;
int err;
@@ -860,8 +860,8 @@ static int enetc_imdio_create(struct enetc_pf *pf)
goto unregister_mdiobus;
}
- pcs_lynx = lynx_pcs_create(pcs);
- if (!pcs_lynx) {
+ phylink_pcs = lynx_pcs_create(pcs);
+ if (!phylink_pcs) {
mdio_device_free(pcs);
err = -ENOMEM;
dev_err(dev, "cannot create lynx pcs (%d)\n", err);
@@ -869,7 +869,7 @@ static int enetc_imdio_create(struct enetc_pf *pf)
}
pf->imdio = bus;
- pf->pcs = pcs_lynx;
+ pf->pcs = phylink_pcs;
return 0;
@@ -882,8 +882,11 @@ static int enetc_imdio_create(struct enetc_pf *pf)
static void enetc_imdio_remove(struct enetc_pf *pf)
{
+ struct mdio_device *mdio_device;
+
if (pf->pcs) {
- mdio_device_free(pf->pcs->mdio);
+ mdio_device = lynx_get_mdio_device(pf->pcs);
+ mdio_device_free(mdio_device);
lynx_pcs_destroy(pf->pcs);
}
if (pf->imdio) {
@@ -980,7 +983,7 @@ static void enetc_pl_mac_config(struct phylink_config *config,
priv = netdev_priv(pf->si->ndev);
if (pf->pcs)
- phylink_set_pcs(priv->phylink, &pf->pcs->pcs);
+ phylink_set_pcs(priv->phylink, &pf->pcs);
}
static void enetc_force_rgmii_mac(struct enetc_hw *hw, int speed, int duplex)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 263946c51e37..c26bd66e4597 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -2,7 +2,7 @@
/* Copyright 2017-2019 NXP */
#include "enetc.h"
-#include <linux/pcs-lynx.h>
+#include <linux/phylink.h>
#define ENETC_PF_NUM_RINGS 8
@@ -46,7 +46,7 @@ struct enetc_pf {
struct mii_bus *mdio; /* saved for cleanup */
struct mii_bus *imdio;
- struct lynx_pcs *pcs;
+ struct phylink_pcs *pcs;
phy_interface_t if_mode;
struct phylink_config phylink_config;
diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c
index af36cd647bf5..7ff7f86ad430 100644
--- a/drivers/net/pcs/pcs-lynx.c
+++ b/drivers/net/pcs/pcs-lynx.c
@@ -22,6 +22,11 @@
#define IF_MODE_SPEED_MSK GENMASK(3, 2)
#define IF_MODE_HALF_DUPLEX BIT(4)
+struct lynx_pcs {
+ struct phylink_pcs pcs;
+ struct mdio_device *mdio;
+};
+
enum sgmii_speed {
SGMII_SPEED_10 = 0,
SGMII_SPEED_100 = 1,
@@ -30,6 +35,15 @@ enum sgmii_speed {
};
#define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs)
+#define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs)
+
+struct mdio_device *lynx_get_mdio_device(struct phylink_pcs *pcs)
+{
+ struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
+
+ return lynx->mdio;
+}
+EXPORT_SYMBOL(lynx_get_mdio_device);
static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs,
struct phylink_link_state *state)
@@ -329,7 +343,7 @@ static const struct phylink_pcs_ops lynx_pcs_phylink_ops = {
.pcs_link_up = lynx_pcs_link_up,
};
-struct lynx_pcs *lynx_pcs_create(struct mdio_device *mdio)
+struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
{
struct lynx_pcs *lynx_pcs;
@@ -341,13 +355,15 @@ struct lynx_pcs *lynx_pcs_create(struct mdio_device *mdio)
lynx_pcs->pcs.ops = &lynx_pcs_phylink_ops;
lynx_pcs->pcs.poll = true;
- return lynx_pcs;
+ return lynx_to_phylink_pcs(lynx_pcs);
}
EXPORT_SYMBOL(lynx_pcs_create);
-void lynx_pcs_destroy(struct lynx_pcs *pcs)
+void lynx_pcs_destroy(struct phylink_pcs *pcs)
{
- kfree(pcs);
+ struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
+
+ kfree(lynx);
}
EXPORT_SYMBOL(lynx_pcs_destroy);
diff --git a/include/linux/pcs-lynx.h b/include/linux/pcs-lynx.h
index a6440d6ebe95..5712cc2ce775 100644
--- a/include/linux/pcs-lynx.h
+++ b/include/linux/pcs-lynx.h
@@ -9,13 +9,10 @@
#include <linux/mdio.h>
#include <linux/phylink.h>
-struct lynx_pcs {
- struct phylink_pcs pcs;
- struct mdio_device *mdio;
-};
+struct mdio_device *lynx_get_mdio_device(struct phylink_pcs *pcs);
-struct lynx_pcs *lynx_pcs_create(struct mdio_device *mdio);
+struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio);
-void lynx_pcs_destroy(struct lynx_pcs *pcs);
+void lynx_pcs_destroy(struct phylink_pcs *pcs);
#endif /* __LINUX_PCS_LYNX_H */
--
2.25.1
Simple rename of a variable to make things more logical.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/felix_vsc9959.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 636bfb096c66..b1032b7abaea 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -1094,7 +1094,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
for (port = 0; port < felix->info->num_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct phylink_pcs *phylink_pcs;
- struct mdio_device *pcs;
+ struct mdio_device *mdio_device;
if (dsa_is_unused_port(felix->ds, port))
continue;
@@ -1102,13 +1102,13 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)
continue;
- pcs = mdio_device_create(felix->imdio, port);
- if (IS_ERR(pcs))
+ mdio_device = mdio_device_create(felix->imdio, port);
+ if (IS_ERR(mdio_device))
continue;
- phylink_pcs = lynx_pcs_create(pcs);
+ phylink_pcs = lynx_pcs_create(mdio_device);
if (!phylink_pcs) {
- mdio_device_free(pcs);
+ mdio_device_free(mdio_device);
continue;
}
--
2.25.1
A simple variable update from "pcs" to "mdio_device" for the mdio device
will make things a little cleaner.
Signed-off-by: Colin Foster <[email protected]>
---
drivers/net/dsa/ocelot/seville_vsc9953.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 49fc8220d636..268c09042824 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1027,7 +1027,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
for (port = 0; port < felix->info->num_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct phylink_pcs *phylink_pcs;
- struct mdio_device *pcs;
+ struct mdio_device *mdio_device;
int addr = port + 4;
if (dsa_is_unused_port(felix->ds, port))
@@ -1036,13 +1036,13 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)
continue;
- pcs = mdio_device_create(felix->imdio, addr);
+ mdio_device = mdio_device_create(felix->imdio, addr);
if (IS_ERR(pcs))
continue;
- phylink_pcs = lynx_pcs_create(pcs);
+ phylink_pcs = lynx_pcs_create(mdio_device);
if (!phylink_pcs) {
- mdio_device_free(pcs);
+ mdio_device_free(mdio_device);
continue;
}
--
2.25.1
On Mon, Nov 15, 2021 at 10:23:20PM -0800, Colin Foster wrote:
> Functions existed for determining the node count by device, but not by
> fwnode_handle. In the case where a driver could either be defined as a
> standalone device or a node of a different device, parsing from the root of
> the device might not make sense. As such, it becomes necessary to parse
> from a child node instead of the device root node.
>
> Signed-off-by: Colin Foster <[email protected]>
> ---
> drivers/base/property.c | 20 ++++++++++++++++----
> include/linux/property.h | 1 +
> 2 files changed, 17 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/base/property.c b/drivers/base/property.c
> index f1f35b48ab8b..2ee675e1529d 100644
> --- a/drivers/base/property.c
> +++ b/drivers/base/property.c
> @@ -845,19 +845,31 @@ bool fwnode_device_is_available(const struct fwnode_handle *fwnode)
> EXPORT_SYMBOL_GPL(fwnode_device_is_available);
>
> /**
> - * device_get_child_node_count - return the number of child nodes for device
> - * @dev: Device to cound the child nodes for
> + * fwnode_get_child_node_count - return the number of child nodes for the fwnode
> + * @fwnode: Node to count the childe nodes for
> */
> -unsigned int device_get_child_node_count(struct device *dev)
> +unsigned int fwnode_get_child_node_count(struct fwnode_handle *fwnode)
> {
> struct fwnode_handle *child;
> unsigned int count = 0;
>
> - device_for_each_child_node(dev, child)
> + fwnode_for_each_child_node(fwnode, child)
> count++;
>
> return count;
> }
> +EXPORT_SYMBOL_GPL(fwnode_get_child_node_count);
> +
> +/**
> + * device_get_child_node_count - return the number of child nodes for device
> + * @dev: Device to count the child nodes for
> + */
> +unsigned int device_get_child_node_count(struct device *dev)
> +{
> + struct fwnode_handle *fwnode = dev_fwnode(dev);
> +
> + return fwnode_get_child_node_count(fwnode);
> +}
> EXPORT_SYMBOL_GPL(device_get_child_node_count);
>
> bool device_dma_supported(struct device *dev)
> diff --git a/include/linux/property.h b/include/linux/property.h
> index 88fa726a76df..6dc71029cfc5 100644
> --- a/include/linux/property.h
> +++ b/include/linux/property.h
> @@ -122,6 +122,7 @@ void fwnode_handle_put(struct fwnode_handle *fwnode);
>
> int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index);
>
> +unsigned int fwnode_get_child_node_count(struct fwnode_handle *fwnode);
> unsigned int device_get_child_node_count(struct device *dev);
You can now make device_get_child_node_count() an inline function:
static inline unsigned int device_get_child_node_count(struct device *dev)
{
return fwnode_get_child_node_count(dev_fwnode(dev));
}
thanks,
--
heikki
On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> My apologies for this next RFC taking so long. Life got in the way.
>
>
> The patch set in general is to add support for the VSC7511, VSC7512,
> VSC7513 and VSC7514 devices controlled over SPI. The driver is
> relatively functional for the internal phy ports (0-3) on the VSC7512.
> As I'll discuss, it is not yet functional for other ports yet.
Since series touches fwnode, please Cc next time to Daniel Scally.
It also appears [1] that somewhere in PHY code a bug is hidden
(at least I think so).
[1]: https://lore.kernel.org/lkml/[email protected]/T/#u
--
With Best Regards,
Andy Shevchenko
On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> My apologies for this next RFC taking so long. Life got in the way.
>
>
> The patch set in general is to add support for the VSC7511, VSC7512,
> VSC7513 and VSC7514 devices controlled over SPI. The driver is
> relatively functional for the internal phy ports (0-3) on the VSC7512.
> As I'll discuss, it is not yet functional for other ports yet.
>
> I still think there are enough updates to bounce by the community
> in case I'm terribly off base or doomed to chase my tail.
>
I would try to get rid of some of the patches now, instead of gathering
a larger and larger pile. Here is a list of patches that I think could
be submitted right away (possibly as part of independent series;
parallelize as much as you can):
[01/23] net: dsa: ocelot: remove unnecessary pci_bar variables
[02/23] net: mdio: mscc-miim: convert to a regmap implementation
[03/23] net: dsa: ocelot: seville: utilize of_mdiobus_register
[04/23] net: dsa: ocelot: felix: switch to mdio-mscc-miim driver for indirect mdio …
[05/23] net: dsa: ocelot: felix: Remove requirement for PCS in felix devices
[06/23] net: dsa: ocelot: felix: add interface for custom regmaps
[07/23] net: dsa: ocelot: felix: add per-device-per-port quirks
[08/23] net: mscc: ocelot: split register definitions to a separate file
[09/23] net: mscc: ocelot: expose ocelot wm functions
[18/23] net: phy: lynx: refactor Lynx PCS module to use generic phylink_pcs
[19/23] net: dsa: felix: name change for clarity from pcs to mdio_device
[20/23] net: dsa: seville: name change for clarity from pcs to mdio_device
[21/23] net: ethernet: enetc: name change for clarity from pcs to mdio_device
[22/23] net: pcs: lynx: use a common naming scheme for all lynx_pcs variables
Now that you've submitted them all already, let's wait for some feedback
before resending smaller chunks.
>
> The main changes for V4 are trying to get pinctrl-ocelot and
> pinctrl-microchip-sgpio functional. Without pinctrl-ocelot,
> communication to external phys won't work. Without communication to
> external phys, PCS ports 4-7 on the dev board won't work. Nor will any
> fiber ports.
>
>
> The hardware setup I'm using for development is a beaglebone black, with
> jumpers from SPI0 to the microchip VSC7512 dev board. The microchip dev
> board has been modified to not boot from flash, but wait for SPI. An
> ethernet cable is connected from the beaglebone ethernet to port 0 of
> the dev board.
>
>
> The device tree I'm using for the VSC7512 is below. Note that ports 4-7
> are still not expected to work, but left in as placeholders for when
> they do.
>
>
> &spi0 {
> #address-cells = <1>;
> #size-cells = <0>;
> status = "okay";
>
> ethernet-switch@0{
> compatible = "mscc,vsc7512";
> spi-max-frequency = <250000>;
Can't go faster than 250 KHz? That's sad.
> reg = <0>;
>
> ports {
there's an extra preceding space here, for all 4 lines from "compatible" to "ports".
> #address-cells = <1>;
> #size-cells = <0>;
>
> port@0 {
> reg = <0>;
> label = "cpu";
> status = "okay";
> ethernet = <&mac_sw>;
> phy-handle = <&sw_phy0>;
> phy-mode = "internal";
> };
>
> port@1 {
> reg = <1>;
> label = "swp1";
> status = "okay";
> phy-handle = <&sw_phy1>;
> phy-mode = "internal";
> };
>
> port@2 {
> reg = <2>;
> label = "swp2";
> status = "okay";
> phy-handle = <&sw_phy2>;
> phy-mode = "internal";
> };
>
> port@3 {
> reg = <3>;
> label = "swp3";
> status = "okay";
> phy-handle = <&sw_phy3>;
> phy-mode = "internal";
> };
>
> port@4 {
> reg = <4>;
> label = "swp4";
> status = "okay";
> phy-handle = <&sw_phy4>;
> phy-mode = "sgmii";
> };
>
> port@5 {
> reg = <5>;
> label = "swp5";
> status = "okay";
> phy-handle = <&sw_phy5>;
> phy-mode = "sgmii";
> };
>
> port@6 {
> reg = <6>;
> label = "swp6";
> status = "okay";
> phy-handle = <&sw_phy6>;
> phy-mode = "sgmii";
> };
>
> port@7 {
> reg = <7>;
> label = "swp7";
> status = "okay";
> phy-handle = <&sw_phy7>;
> phy-mode = "sgmii";
> };
> };
>
> mdio {
> #address-cells = <1>;
> #size-cells = <0>;
>
> sw_phy0: ethernet-phy@0 {
> #address-cells = <1>;
> #size-cells = <0>;
PHY nodes don't need #address-cells and #size-cells.
> reg = <0x0>;
> };
>
> sw_phy1: ethernet-phy@1 {
> #address-cells = <1>;
> #size-cells = <0>;
> reg = <0x1>;
> };
>
> sw_phy2: ethernet-phy@2 {
> #address-cells = <1>;
> #size-cells = <0>;
> reg = <0x2>;
> };
>
> sw_phy3: ethernet-phy@3 {
> #address-cells = <1>;
> #size-cells = <0>;
> reg = <0x3>;
> };
>
> sw_phy4: ethernet-phy@4 {
> #address-cells = <1>;
> #size-cells = <0>;
> reg = <0x4>;
> };
>
> sw_phy5: ethernet-phy@5 {
> #address-cells = <1>;
> #size-cells = <0>;
> reg = <0x5>;
> };
>
> sw_phy6: ethernet-phy@6 {
> #address-cells = <1>;
> #size-cells = <0>;
> reg = <0x6>;
> };
>
> sw_phy7: ethernet-phy@7 {
> #address-cells = <1>;
> #size-cells = <0>;
> reg = <0x7>;
> };
> };
>
> gpio: pinctrl {
> compatible = "mscc,ocelot-pinctrl";
> #address-cells = <1>;
> #size-cells = <0>;
I don't think that #address-cells and #size-cells are needed here, since
"led-shift-reg-pins" does not have any address unit.
> #gpio_cells = <2>;
> gpio-ranges = <&gpio 0 0 22>;
>
> led_shift_reg_pins: led-shift-reg-pins {
> pins = "GPIO_0", "GPIO_1", "GPIO_2", "GPIO_3";
> function = "sg0";
> };
> };
>
> sgpio: sgpio {
> compatible = "mscc,ocelot-sgpio";
> #address-cells = <1>;
> #size-cells = <0>;
> bus-frequency=<12500000>;
> clocks = <&ocelot_clock>;
> microchip,sgpio-port-ranges = <0 31>;
>
> sgpio_in0: sgpio@0 {
> compatible = "microchip,sparx5-sgpio-bank";
> reg = <0>;
> gpio-controller;
> #gpio-cells = <3>;
> ngpios = <32>;
> };
>
> sgpio_out1: sgpio@1 {
> compatible = "microchip,sparx5-sgpio-bank";
> reg = <1>;
> gpio-controller;
> gpio-cells = <3>;
> ngpios = <32>;
> };
> };
> };
> };
>
>
> My main focus is getting the ocelot-pinctrl driver fully functional. My
> current hope is that it would correctly set GPIO pins 0-3 into the "sg0"
> state. That is not the case right now, and I'll be looking into why. The
> behavior I'm hoping for is to be able to configure the sgpio LEDs for
> activity at the very least. Link status would be a bonus.
>
>
> I do have pinctrl by way of debugfs and sysfs. There aren't any debug
> LEDs that are attached to unused pins, unfortunately. That would've been
> really helpful. So there's a key takeaway for dev-board manufacturers.
>
>
> As you'll see, the main changes to the three drivers I'm utilizing
> (mscc_miim, pinctrl-ocelot, and pinctrl-microchip-sgpio) follow a
> similar path. First, convert everything to regmap. Second, expose
> whatever functions are necessary to fully utilize an external regmap.
>
>
> One thing to note: I've been following a pattern of adding "offset"
> variables to these drivers. I'm looking for feedback here, because I
> don't like it - however I feel like it is the "least bad" interface I
> could come up with.
>
>
> Specifically, ocelot has a regmap for GCB. ocelot-pinctrl would create a
> smaller regmap at an address of "GCB + 0x34".
>
>
> There are three options I saw here:
> 1. Have vsc7512_spi create a new regmap at GCB + 0x34 and pass that to
> ocelot-pinctrl
> 2. Give ocelot-pinctrl the concept of a "parent bus" by which it could
> request a regmap.
> 3. Keep the same GCB regmap, but pass in 0x34 as an offset.
>
>
> I will admit that option 2 sounds very enticing, but I don't know if
> that type of interaction exists. If not, implementing it is probably
> outside the scope of a first patch set. As such, I opted for option 3.
I think that type of interaction is called "mfd", potentially even "syscon".
>
>
> Version 4 also fixes some logic for MDIO probing. It wasn't using the
> device tree by way of of_mdiobus_register. Now it is.
>
>
> The relevant boot log for the switch / MDIO bus is here. As expected,
> devices 4-7 are missing. If nothing else, that is telling me that the
> device tree is working.
>
> [ 4.005195] mdio_bus spi0.0-mii:03: using lookup tables for GPIO lookup
> [ 4.005205] mdio_bus spi0.0-mii:03: No GPIO consumer reset found
> [ 4.006586] mdio_bus spi0.0-mii: MDIO device at address 4 is missing.
> [ 4.014333] mdio_bus spi0.0-mii: MDIO device at address 5 is missing.
> [ 4.022009] mdio_bus spi0.0-mii: MDIO device at address 6 is missing.
> [ 4.029573] mdio_bus spi0.0-mii: MDIO device at address 7 is missing.
> [ 8.386624] vsc7512 spi0.0: PHY [spi0.0-mii:00] driver [Generic PHY] (irq=POLL)
> [ 8.397222] vsc7512 spi0.0: configuring for phy/internal link mode
> [ 8.419484] vsc7512 spi0.0 swp1 (uninitialized): PHY [spi0.0-mii:01] driver [Generic PHY] (irq=POLL)
> [ 8.437278] vsc7512 spi0.0 swp2 (uninitialized): PHY [spi0.0-mii:02] driver [Generic PHY] (irq=POLL)
> [ 8.452867] vsc7512 spi0.0 swp3 (uninitialized): PHY [spi0.0-mii:03] driver [Generic PHY] (irq=POLL)
> [ 8.465007] vsc7512 spi0.0 swp4 (uninitialized): no phy at 4
> [ 8.470721] vsc7512 spi0.0 swp4 (uninitialized): failed to connect to PHY: -ENODEV
> [ 8.478388] vsc7512 spi0.0 swp4 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 4
> [ 8.489636] vsc7512 spi0.0 swp5 (uninitialized): no phy at 5
> [ 8.495371] vsc7512 spi0.0 swp5 (uninitialized): failed to connect to PHY: -ENODEV
> [ 8.502996] vsc7512 spi0.0 swp5 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 5
> [ 8.514186] vsc7512 spi0.0 swp6 (uninitialized): no phy at 6
> [ 8.519882] vsc7512 spi0.0 swp6 (uninitialized): failed to connect to PHY: -ENODEV
> [ 8.527539] vsc7512 spi0.0 swp6 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 6
> [ 8.538716] vsc7512 spi0.0 swp7 (uninitialized): no phy at 7
> [ 8.544451] vsc7512 spi0.0 swp7 (uninitialized): failed to connect to PHY: -ENODEV
> [ 8.552079] vsc7512 spi0.0 swp7 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 7
> [ 8.571962] device eth0 entered promiscuous mode
> [ 8.576684] DSA: tree 0 setup
> [ 10.490093] vsc7512 spi0.0: Link is Up - 100Mbps/Full - flow control off
>
>
> Much later on, I created a bridge with STP (and two ports jumped
> together) as a test. Everything seems to be working as expected.
>
>
> [59839.920340] cpsw-switch 4a100000.switch: starting ndev. mode: dual_mac
> [59840.013636] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver (mii_bus:phy_addr=4a101000.mdio:00, irq=POLL)
> [59840.031444] 8021q: adding VLAN 0 to HW filter on device eth0
> [59840.057406] vsc7512 spi0.0 swp1: configuring for phy/internal link mode
> [59840.089302] vsc7512 spi0.0 swp2: configuring for phy/internal link mode
> [59840.121514] vsc7512 spi0.0 swp3: configuring for phy/internal link mode
> [59840.167589] br0: port 1(swp1) entered blocking state
> [59840.172818] br0: port 1(swp1) entered disabled state
> [59840.191078] device swp1 entered promiscuous mode
> [59840.224855] br0: port 2(swp2) entered blocking state
> [59840.229893] br0: port 2(swp2) entered disabled state
> [59840.245844] device swp2 entered promiscuous mode
> [59840.270839] br0: port 3(swp3) entered blocking state
> [59840.276003] br0: port 3(swp3) entered disabled state
> [59840.291674] device swp3 entered promiscuous mode
> [59840.663239] vsc7512 spi0.0: Link is Down
> [59841.691641] vsc7512 spi0.0: Link is Up - 100Mbps/Full - flow control off
> [59842.167897] cpsw-switch 4a100000.switch eth0: Link is Up - 100Mbps/Full - flow control off
> [59842.176481] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
> [59843.216121] vsc7512 spi0.0 swp1: Link is Up - 1Gbps/Full - flow control rx/tx
> [59843.231076] IPv6: ADDRCONF(NETDEV_CHANGE): swp1: link becomes ready
> [59843.237593] br0: port 1(swp1) entered blocking state
> [59843.242629] br0: port 1(swp1) entered listening state
> [59843.301447] vsc7512 spi0.0 swp3: Link is Up - 1Gbps/Full - flow control rx/tx
> [59843.309027] IPv6: ADDRCONF(NETDEV_CHANGE): swp3: link becomes ready
> [59843.315544] br0: port 3(swp3) entered blocking state
> [59843.320545] br0: port 3(swp3) entered listening state
> [59845.042058] br0: port 3(swp3) entered blocking state
> [59858.401566] br0: port 1(swp1) entered learning state
> [59871.841910] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
> [59873.761495] br0: port 1(swp1) entered forwarding state
> [59873.766703] br0: topology change detected, propagating
> [59873.776278] IPv6: ADDRCONF(NETDEV_CHANGE): br0: link becomes ready
> [59902.561908] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
> [59926.494446] vsc7512 spi0.0 swp2: Link is Up - 1Gbps/Full - flow control rx/tx
> [59926.501959] IPv6: ADDRCONF(NETDEV_CHANGE): swp2: link becomes ready
> [59926.508702] br0: port 2(swp2) entered blocking state
> [59926.513868] br0: port 2(swp2) entered listening state
> [59941.601540] br0: port 2(swp2) entered learning state
> [59956.961493] br0: port 2(swp2) entered forwarding state
> [59956.966711] br0: topology change detected, propagating
> [59968.481839] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
>
>
> In order to make this work, I have modified the cpsw driver, and now the
> cpsw_new driver, to allow for frames over 1500 bytes. Otherwise the
> tagging protocol will not work between the beaglebone and the VSC7512. I
> plan to eventually try to get those changes in mainline, but I don't
> want to get distracted from my initial goal.
>
>
> RFC history:
> v1 (accidentally named vN)
> * Initial architecture. Not functional
> * General concepts laid out
>
> v2
> * Near functional. No CPU port communication, but control over all
> external ports
> * Cleaned up regmap implementation from v1
>
> v3
> * Functional
> * Shared MDIO transactions routed through mdio-mscc-miim
> * CPU / NPI port enabled by way of vsc7512_enable_npi_port /
> felix->info->enable_npi_port
> * NPI port tagging functional - Requires a CPU port driver that supports
> frames of 1520 bytes. Verified with a patch to the cpsw driver
>
> v4
> * Functional
> * Device tree fixes
> * Add hooks for pinctrl-ocelot - some functionality by way of sysfs
> * Add hooks for pinctrl-microsemi-sgpio - not yet fully functional
> * Remove lynx_pcs interface for a generic phylink_pcs. The goal here
> is to have an ocelot_pcs that will work for each configuration of
> every port.
>
>
>
> Colin Foster (23):
> net: dsa: ocelot: remove unnecessary pci_bar variables
> net: mdio: mscc-miim: convert to a regmap implementation
> net: dsa: ocelot: seville: utilize of_mdiobus_register
> net: dsa: ocelot: felix: switch to mdio-mscc-miim driver for indirect
> mdio access
> net: dsa: ocelot: felix: Remove requirement for PCS in felix devices
> net: dsa: ocelot: felix: add interface for custom regmaps
> net: dsa: ocelot: felix: add per-device-per-port quirks
> net: mscc: ocelot: split register definitions to a separate file
> net: mscc: ocelot: expose ocelot wm functions
> pinctrl: ocelot: combine get resource and ioremap into single call
> pinctrl: ocelot: update pinctrl to automatic base address
> pinctrl: ocelot: convert pinctrl to regmap
> pinctrl: ocelot: expose ocelot_pinctrl_core_probe interface
> pinctrl: microchip-sgpio: update to support regmap
> device property: add helper function fwnode_get_child_node_count
> pinctrl: microchip-sgpio: change device tree matches to use nodes
> instead of device
> pinctrl: microchip-sgpio: expose microchip_sgpio_core_probe interface
> net: phy: lynx: refactor Lynx PCS module to use generic phylink_pcs
> net: dsa: felix: name change for clarity from pcs to mdio_device
> net: dsa: seville: name change for clarity from pcs to mdio_device
> net: ethernet: enetc: name change for clarity from pcs to mdio_device
> net: pcs: lynx: use a common naming scheme for all lynx_pcs variables
> net: dsa: ocelot: felix: add support for VSC75XX control over SPI
>
> drivers/base/property.c | 20 +-
> drivers/net/dsa/ocelot/Kconfig | 16 +
> drivers/net/dsa/ocelot/Makefile | 7 +
> drivers/net/dsa/ocelot/felix.c | 29 +-
> drivers/net/dsa/ocelot/felix.h | 10 +-
> drivers/net/dsa/ocelot/felix_mdio.c | 54 +
> drivers/net/dsa/ocelot/felix_mdio.h | 13 +
> drivers/net/dsa/ocelot/felix_vsc9959.c | 38 +-
> drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c | 946 ++++++++++++++++++
> drivers/net/dsa/ocelot/seville_vsc9953.c | 136 +--
> .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 12 +-
> .../net/ethernet/freescale/dpaa2/dpaa2-mac.h | 3 +-
> .../net/ethernet/freescale/enetc/enetc_pf.c | 27 +-
> .../net/ethernet/freescale/enetc/enetc_pf.h | 4 +-
> drivers/net/ethernet/mscc/Makefile | 3 +-
> drivers/net/ethernet/mscc/ocelot.c | 8 +
> drivers/net/ethernet/mscc/ocelot_devlink.c | 31 +
> drivers/net/ethernet/mscc/ocelot_vsc7514.c | 548 +---------
> drivers/net/ethernet/mscc/vsc7514_regs.c | 522 ++++++++++
> drivers/net/mdio/mdio-mscc-miim.c | 167 +++-
> drivers/net/pcs/pcs-lynx.c | 36 +-
> drivers/pinctrl/pinctrl-microchip-sgpio.c | 127 ++-
> drivers/pinctrl/pinctrl-ocelot.c | 207 ++--
> include/linux/mdio/mdio-mscc-miim.h | 19 +
> include/linux/pcs-lynx.h | 9 +-
> include/linux/property.h | 1 +
> include/soc/mscc/ocelot.h | 60 ++
> include/soc/mscc/vsc7514_regs.h | 27 +
> 28 files changed, 2219 insertions(+), 861 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/vsc7514_regs.c
> create mode 100644 include/linux/mdio/mdio-mscc-miim.h
> create mode 100644 include/soc/mscc/vsc7514_regs.h
>
> --
> 2.25.1
>
On Mon, Nov 15, 2021 at 10:23:08PM -0800, Colin Foster wrote:
> Switch seville to use of_mdiobus_register(bus, NULL) instead of just
> mdiobus_register. This code is about to be pulled into a separate module
> that can optionally define ports by the device_node.
>
> Signed-off-by: Colin Foster <[email protected]>
> ---
Reviewed-by: Vladimir Oltean <[email protected]>
> drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 92eae63150ea..84681642d237 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1108,7 +1108,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
> snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
>
> /* Needed in order to initialize the bus mutex lock */
> - rc = mdiobus_register(bus);
> + rc = of_mdiobus_register(bus, NULL);
> if (rc < 0) {
> dev_err(dev, "failed to register MDIO bus\n");
> return rc;
> --
> 2.25.1
>
On Tue, Nov 16, 2021 at 12:34:17PM +0200, Andy Shevchenko wrote:
> On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> > My apologies for this next RFC taking so long. Life got in the way.
> >
> >
> > The patch set in general is to add support for the VSC7511, VSC7512,
> > VSC7513 and VSC7514 devices controlled over SPI. The driver is
> > relatively functional for the internal phy ports (0-3) on the VSC7512.
> > As I'll discuss, it is not yet functional for other ports yet.
>
>
> Since series touches fwnode, please Cc next time to Daniel Scally.
Thank you. I will do this next time.
For my future reference, is there a way that I could have known this? Or
is this just knowledge that comes with experience? The email list I got
was from running the patch set through get_maintainers.
> It also appears [1] that somewhere in PHY code a bug is hidden
> (at least I think so).
Thank you for this information. I'll keep an eye out!
>
> [1]: https://lore.kernel.org/lkml/[email protected]/T/#u
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
On Tue, Nov 16, 2021 at 11:10:59AM +0000, Vladimir Oltean wrote:
> On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> > My apologies for this next RFC taking so long. Life got in the way.
> >
> >
> > The patch set in general is to add support for the VSC7511, VSC7512,
> > VSC7513 and VSC7514 devices controlled over SPI. The driver is
> > relatively functional for the internal phy ports (0-3) on the VSC7512.
> > As I'll discuss, it is not yet functional for other ports yet.
> >
> > I still think there are enough updates to bounce by the community
> > in case I'm terribly off base or doomed to chase my tail.
> >
>
> I would try to get rid of some of the patches now, instead of gathering
> a larger and larger pile. Here is a list of patches that I think could
> be submitted right away (possibly as part of independent series;
> parallelize as much as you can):
>
> [01/23] net: dsa: ocelot: remove unnecessary pci_bar variables
> [02/23] net: mdio: mscc-miim: convert to a regmap implementation
> [03/23] net: dsa: ocelot: seville: utilize of_mdiobus_register
> [04/23] net: dsa: ocelot: felix: switch to mdio-mscc-miim driver for indirect mdio …
> [05/23] net: dsa: ocelot: felix: Remove requirement for PCS in felix devices
> [06/23] net: dsa: ocelot: felix: add interface for custom regmaps
> [07/23] net: dsa: ocelot: felix: add per-device-per-port quirks
> [08/23] net: mscc: ocelot: split register definitions to a separate file
> [09/23] net: mscc: ocelot: expose ocelot wm functions
> [18/23] net: phy: lynx: refactor Lynx PCS module to use generic phylink_pcs
> [19/23] net: dsa: felix: name change for clarity from pcs to mdio_device
> [20/23] net: dsa: seville: name change for clarity from pcs to mdio_device
> [21/23] net: ethernet: enetc: name change for clarity from pcs to mdio_device
> [22/23] net: pcs: lynx: use a common naming scheme for all lynx_pcs variables
>
> Now that you've submitted them all already, let's wait for some feedback
> before resending smaller chunks.
Thank you for your response as always!
This will make v5 a lot easier for me to keep straight. I think this one
might also be a good one to submit earlier, since it actually does
change behavior:
pinctrl: ocelot: update pinctrl to automatic base address
I'd probably want to drag this trivial one along as well:
pinctrl: ocelot: combine get resource and ioremap into single call
>
> >
> > The main changes for V4 are trying to get pinctrl-ocelot and
> > pinctrl-microchip-sgpio functional. Without pinctrl-ocelot,
> > communication to external phys won't work. Without communication to
> > external phys, PCS ports 4-7 on the dev board won't work. Nor will any
> > fiber ports.
> >
> >
> > The hardware setup I'm using for development is a beaglebone black, with
> > jumpers from SPI0 to the microchip VSC7512 dev board. The microchip dev
> > board has been modified to not boot from flash, but wait for SPI. An
> > ethernet cable is connected from the beaglebone ethernet to port 0 of
> > the dev board.
> >
> >
> > The device tree I'm using for the VSC7512 is below. Note that ports 4-7
> > are still not expected to work, but left in as placeholders for when
> > they do.
> >
> >
> > &spi0 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > status = "okay";
> >
> > ethernet-switch@0{
> > compatible = "mscc,vsc7512";
> > spi-max-frequency = <250000>;
>
> Can't go faster than 250 KHz? That's sad.
Haven't tried faster speeds. During the early days there was an error on
my setup due to too long of wires. Crosstalk / capacitance... I slowed
the SPI bus to a crawl while debugging that and never turned it back up.
If it'll help anyone: transitions on the MOSI I believe were causing
glitches on the CS line. This made the VSC7512 sad.
>
> > reg = <0>;
> >
> > ports {
>
> there's an extra preceding space here, for all 4 lines from "compatible" to "ports".
I'll fix it. Thanks.
>
> > #address-cells = <1>;
> > #size-cells = <0>;
> >
> > port@0 {
> > reg = <0>;
> > label = "cpu";
> > status = "okay";
> > ethernet = <&mac_sw>;
> > phy-handle = <&sw_phy0>;
> > phy-mode = "internal";
> > };
> >
> > port@1 {
> > reg = <1>;
> > label = "swp1";
> > status = "okay";
> > phy-handle = <&sw_phy1>;
> > phy-mode = "internal";
> > };
> >
> > port@4 {
> > reg = <4>;
> > label = "swp4";
> > status = "okay";
> > phy-handle = <&sw_phy4>;
> > phy-mode = "sgmii";
> > };
> > };
> >
> > mdio {
> > #address-cells = <1>;
> > #size-cells = <0>;
> >
> > sw_phy0: ethernet-phy@0 {
> > #address-cells = <1>;
> > #size-cells = <0>;
>
> PHY nodes don't need #address-cells and #size-cells.
Thank you. I'll remove these and the ones below.
>
> > reg = <0x0>;
> > };
> >
> > sw_phy1: ethernet-phy@1 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > reg = <0x1>;
> > };
> >
> > sw_phy2: ethernet-phy@2 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > reg = <0x2>;
> > };
> >
> > sw_phy3: ethernet-phy@3 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > reg = <0x3>;
> > };
> >
> > sw_phy4: ethernet-phy@4 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > reg = <0x4>;
> > };
> >
> > sw_phy5: ethernet-phy@5 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > reg = <0x5>;
> > };
> >
> > sw_phy6: ethernet-phy@6 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > reg = <0x6>;
> > };
> >
> > sw_phy7: ethernet-phy@7 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > reg = <0x7>;
> > };
> > };
> >
> > gpio: pinctrl {
> > compatible = "mscc,ocelot-pinctrl";
> > #address-cells = <1>;
> > #size-cells = <0>;
>
> I don't think that #address-cells and #size-cells are needed here, since
> "led-shift-reg-pins" does not have any address unit.
>
> > #gpio_cells = <2>;
> > gpio-ranges = <&gpio 0 0 22>;
> >
> > led_shift_reg_pins: led-shift-reg-pins {
> > pins = "GPIO_0", "GPIO_1", "GPIO_2", "GPIO_3";
> > function = "sg0";
> > };
> > };
> >
> > sgpio: sgpio {
> > compatible = "mscc,ocelot-sgpio";
> > #address-cells = <1>;
> > #size-cells = <0>;
> > bus-frequency=<12500000>;
> > clocks = <&ocelot_clock>;
> > microchip,sgpio-port-ranges = <0 31>;
> >
> > sgpio_in0: sgpio@0 {
> > compatible = "microchip,sparx5-sgpio-bank";
> > reg = <0>;
> > gpio-controller;
> > #gpio-cells = <3>;
> > ngpios = <32>;
> > };
> >
> > sgpio_out1: sgpio@1 {
> > compatible = "microchip,sparx5-sgpio-bank";
> > reg = <1>;
> > gpio-controller;
> > gpio-cells = <3>;
> > ngpios = <32>;
> > };
> > };
> > };
> > };
> >
> >
> > My main focus is getting the ocelot-pinctrl driver fully functional. My
> > current hope is that it would correctly set GPIO pins 0-3 into the "sg0"
> > state. That is not the case right now, and I'll be looking into why. The
> > behavior I'm hoping for is to be able to configure the sgpio LEDs for
> > activity at the very least. Link status would be a bonus.
> >
> >
> > I do have pinctrl by way of debugfs and sysfs. There aren't any debug
> > LEDs that are attached to unused pins, unfortunately. That would've been
> > really helpful. So there's a key takeaway for dev-board manufacturers.
> >
> >
> > As you'll see, the main changes to the three drivers I'm utilizing
> > (mscc_miim, pinctrl-ocelot, and pinctrl-microchip-sgpio) follow a
> > similar path. First, convert everything to regmap. Second, expose
> > whatever functions are necessary to fully utilize an external regmap.
> >
> >
> > One thing to note: I've been following a pattern of adding "offset"
> > variables to these drivers. I'm looking for feedback here, because I
> > don't like it - however I feel like it is the "least bad" interface I
> > could come up with.
> >
> >
> > Specifically, ocelot has a regmap for GCB. ocelot-pinctrl would create a
> > smaller regmap at an address of "GCB + 0x34".
> >
> >
> > There are three options I saw here:
> > 1. Have vsc7512_spi create a new regmap at GCB + 0x34 and pass that to
> > ocelot-pinctrl
> > 2. Give ocelot-pinctrl the concept of a "parent bus" by which it could
> > request a regmap.
> > 3. Keep the same GCB regmap, but pass in 0x34 as an offset.
> >
> >
> > I will admit that option 2 sounds very enticing, but I don't know if
> > that type of interaction exists. If not, implementing it is probably
> > outside the scope of a first patch set. As such, I opted for option 3.
>
> I think that type of interaction is called "mfd", potentially even "syscon".
Before diving in, I'd come across mfd and thought that might be the
answer. I'll reconsider it now that I have several months of staring at
kernel code under my belt. Maybe an mfd that does SPI setup and chip
resetting. Then I could remove all SPI code from ocelot_vsc7512_spi.
>
> >
> >
> > Version 4 also fixes some logic for MDIO probing. It wasn't using the
> > device tree by way of of_mdiobus_register. Now it is.
> >
> >
> > The relevant boot log for the switch / MDIO bus is here. As expected,
> > devices 4-7 are missing. If nothing else, that is telling me that the
> > device tree is working.
> >
> > [ 4.005195] mdio_bus spi0.0-mii:03: using lookup tables for GPIO lookup
> > [ 4.005205] mdio_bus spi0.0-mii:03: No GPIO consumer reset found
> > [ 4.006586] mdio_bus spi0.0-mii: MDIO device at address 4 is missing.
> > [ 4.014333] mdio_bus spi0.0-mii: MDIO device at address 5 is missing.
> > [ 4.022009] mdio_bus spi0.0-mii: MDIO device at address 6 is missing.
> > [ 4.029573] mdio_bus spi0.0-mii: MDIO device at address 7 is missing.
> > [ 8.386624] vsc7512 spi0.0: PHY [spi0.0-mii:00] driver [Generic PHY] (irq=POLL)
> > [ 8.397222] vsc7512 spi0.0: configuring for phy/internal link mode
> > [ 8.419484] vsc7512 spi0.0 swp1 (uninitialized): PHY [spi0.0-mii:01] driver [Generic PHY] (irq=POLL)
> > [ 8.437278] vsc7512 spi0.0 swp2 (uninitialized): PHY [spi0.0-mii:02] driver [Generic PHY] (irq=POLL)
> > [ 8.452867] vsc7512 spi0.0 swp3 (uninitialized): PHY [spi0.0-mii:03] driver [Generic PHY] (irq=POLL)
> > [ 8.465007] vsc7512 spi0.0 swp4 (uninitialized): no phy at 4
> > [ 8.470721] vsc7512 spi0.0 swp4 (uninitialized): failed to connect to PHY: -ENODEV
> > [ 8.478388] vsc7512 spi0.0 swp4 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 4
> > [ 8.489636] vsc7512 spi0.0 swp5 (uninitialized): no phy at 5
> > [ 8.495371] vsc7512 spi0.0 swp5 (uninitialized): failed to connect to PHY: -ENODEV
> > [ 8.502996] vsc7512 spi0.0 swp5 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 5
> > [ 8.514186] vsc7512 spi0.0 swp6 (uninitialized): no phy at 6
> > [ 8.519882] vsc7512 spi0.0 swp6 (uninitialized): failed to connect to PHY: -ENODEV
> > [ 8.527539] vsc7512 spi0.0 swp6 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 6
> > [ 8.538716] vsc7512 spi0.0 swp7 (uninitialized): no phy at 7
> > [ 8.544451] vsc7512 spi0.0 swp7 (uninitialized): failed to connect to PHY: -ENODEV
> > [ 8.552079] vsc7512 spi0.0 swp7 (uninitialized): error -19 setting up PHY for tree 0, switch 0, port 7
> > [ 8.571962] device eth0 entered promiscuous mode
> > [ 8.576684] DSA: tree 0 setup
> > [ 10.490093] vsc7512 spi0.0: Link is Up - 100Mbps/Full - flow control off
> >
> >
> > Much later on, I created a bridge with STP (and two ports jumped
> > together) as a test. Everything seems to be working as expected.
> >
> >
> > [59839.920340] cpsw-switch 4a100000.switch: starting ndev. mode: dual_mac
> > [59840.013636] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver (mii_bus:phy_addr=4a101000.mdio:00, irq=POLL)
> > [59840.031444] 8021q: adding VLAN 0 to HW filter on device eth0
> > [59840.057406] vsc7512 spi0.0 swp1: configuring for phy/internal link mode
> > [59840.089302] vsc7512 spi0.0 swp2: configuring for phy/internal link mode
> > [59840.121514] vsc7512 spi0.0 swp3: configuring for phy/internal link mode
> > [59840.167589] br0: port 1(swp1) entered blocking state
> > [59840.172818] br0: port 1(swp1) entered disabled state
> > [59840.191078] device swp1 entered promiscuous mode
> > [59840.224855] br0: port 2(swp2) entered blocking state
> > [59840.229893] br0: port 2(swp2) entered disabled state
> > [59840.245844] device swp2 entered promiscuous mode
> > [59840.270839] br0: port 3(swp3) entered blocking state
> > [59840.276003] br0: port 3(swp3) entered disabled state
> > [59840.291674] device swp3 entered promiscuous mode
> > [59840.663239] vsc7512 spi0.0: Link is Down
> > [59841.691641] vsc7512 spi0.0: Link is Up - 100Mbps/Full - flow control off
> > [59842.167897] cpsw-switch 4a100000.switch eth0: Link is Up - 100Mbps/Full - flow control off
> > [59842.176481] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
> > [59843.216121] vsc7512 spi0.0 swp1: Link is Up - 1Gbps/Full - flow control rx/tx
> > [59843.231076] IPv6: ADDRCONF(NETDEV_CHANGE): swp1: link becomes ready
> > [59843.237593] br0: port 1(swp1) entered blocking state
> > [59843.242629] br0: port 1(swp1) entered listening state
> > [59843.301447] vsc7512 spi0.0 swp3: Link is Up - 1Gbps/Full - flow control rx/tx
> > [59843.309027] IPv6: ADDRCONF(NETDEV_CHANGE): swp3: link becomes ready
> > [59843.315544] br0: port 3(swp3) entered blocking state
> > [59843.320545] br0: port 3(swp3) entered listening state
> > [59845.042058] br0: port 3(swp3) entered blocking state
> > [59858.401566] br0: port 1(swp1) entered learning state
> > [59871.841910] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
> > [59873.761495] br0: port 1(swp1) entered forwarding state
> > [59873.766703] br0: topology change detected, propagating
> > [59873.776278] IPv6: ADDRCONF(NETDEV_CHANGE): br0: link becomes ready
> > [59902.561908] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
> > [59926.494446] vsc7512 spi0.0 swp2: Link is Up - 1Gbps/Full - flow control rx/tx
> > [59926.501959] IPv6: ADDRCONF(NETDEV_CHANGE): swp2: link becomes ready
> > [59926.508702] br0: port 2(swp2) entered blocking state
> > [59926.513868] br0: port 2(swp2) entered listening state
> > [59941.601540] br0: port 2(swp2) entered learning state
> > [59956.961493] br0: port 2(swp2) entered forwarding state
> > [59956.966711] br0: topology change detected, propagating
> > [59968.481839] br0: received packet on swp1 with own address as source address (addr:24:76:25:76:35:37, vlan:0)
> >
> >
> > In order to make this work, I have modified the cpsw driver, and now the
> > cpsw_new driver, to allow for frames over 1500 bytes. Otherwise the
> > tagging protocol will not work between the beaglebone and the VSC7512. I
> > plan to eventually try to get those changes in mainline, but I don't
> > want to get distracted from my initial goal.
> >
> >
> > RFC history:
> > v1 (accidentally named vN)
> > * Initial architecture. Not functional
> > * General concepts laid out
> >
> > v2
> > * Near functional. No CPU port communication, but control over all
> > external ports
> > * Cleaned up regmap implementation from v1
> >
> > v3
> > * Functional
> > * Shared MDIO transactions routed through mdio-mscc-miim
> > * CPU / NPI port enabled by way of vsc7512_enable_npi_port /
> > felix->info->enable_npi_port
> > * NPI port tagging functional - Requires a CPU port driver that supports
> > frames of 1520 bytes. Verified with a patch to the cpsw driver
> >
> > v4
> > * Functional
> > * Device tree fixes
> > * Add hooks for pinctrl-ocelot - some functionality by way of sysfs
> > * Add hooks for pinctrl-microsemi-sgpio - not yet fully functional
> > * Remove lynx_pcs interface for a generic phylink_pcs. The goal here
> > is to have an ocelot_pcs that will work for each configuration of
> > every port.
> >
> >
> >
> > Colin Foster (23):
> > net: dsa: ocelot: remove unnecessary pci_bar variables
> > net: mdio: mscc-miim: convert to a regmap implementation
> > net: dsa: ocelot: seville: utilize of_mdiobus_register
> > net: dsa: ocelot: felix: switch to mdio-mscc-miim driver for indirect
> > mdio access
> > net: dsa: ocelot: felix: Remove requirement for PCS in felix devices
> > net: dsa: ocelot: felix: add interface for custom regmaps
> > net: dsa: ocelot: felix: add per-device-per-port quirks
> > net: mscc: ocelot: split register definitions to a separate file
> > net: mscc: ocelot: expose ocelot wm functions
> > pinctrl: ocelot: combine get resource and ioremap into single call
> > pinctrl: ocelot: update pinctrl to automatic base address
> > pinctrl: ocelot: convert pinctrl to regmap
> > pinctrl: ocelot: expose ocelot_pinctrl_core_probe interface
> > pinctrl: microchip-sgpio: update to support regmap
> > device property: add helper function fwnode_get_child_node_count
> > pinctrl: microchip-sgpio: change device tree matches to use nodes
> > instead of device
> > pinctrl: microchip-sgpio: expose microchip_sgpio_core_probe interface
> > net: phy: lynx: refactor Lynx PCS module to use generic phylink_pcs
> > net: dsa: felix: name change for clarity from pcs to mdio_device
> > net: dsa: seville: name change for clarity from pcs to mdio_device
> > net: ethernet: enetc: name change for clarity from pcs to mdio_device
> > net: pcs: lynx: use a common naming scheme for all lynx_pcs variables
> > net: dsa: ocelot: felix: add support for VSC75XX control over SPI
> >
> > drivers/base/property.c | 20 +-
> > drivers/net/dsa/ocelot/Kconfig | 16 +
> > drivers/net/dsa/ocelot/Makefile | 7 +
> > drivers/net/dsa/ocelot/felix.c | 29 +-
> > drivers/net/dsa/ocelot/felix.h | 10 +-
> > drivers/net/dsa/ocelot/felix_mdio.c | 54 +
> > drivers/net/dsa/ocelot/felix_mdio.h | 13 +
> > drivers/net/dsa/ocelot/felix_vsc9959.c | 38 +-
> > drivers/net/dsa/ocelot/ocelot_vsc7512_spi.c | 946 ++++++++++++++++++
> > drivers/net/dsa/ocelot/seville_vsc9953.c | 136 +--
> > .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 12 +-
> > .../net/ethernet/freescale/dpaa2/dpaa2-mac.h | 3 +-
> > .../net/ethernet/freescale/enetc/enetc_pf.c | 27 +-
> > .../net/ethernet/freescale/enetc/enetc_pf.h | 4 +-
> > drivers/net/ethernet/mscc/Makefile | 3 +-
> > drivers/net/ethernet/mscc/ocelot.c | 8 +
> > drivers/net/ethernet/mscc/ocelot_devlink.c | 31 +
> > drivers/net/ethernet/mscc/ocelot_vsc7514.c | 548 +---------
> > drivers/net/ethernet/mscc/vsc7514_regs.c | 522 ++++++++++
> > drivers/net/mdio/mdio-mscc-miim.c | 167 +++-
> > drivers/net/pcs/pcs-lynx.c | 36 +-
> > drivers/pinctrl/pinctrl-microchip-sgpio.c | 127 ++-
> > drivers/pinctrl/pinctrl-ocelot.c | 207 ++--
> > include/linux/mdio/mdio-mscc-miim.h | 19 +
> > include/linux/pcs-lynx.h | 9 +-
> > include/linux/property.h | 1 +
> > include/soc/mscc/ocelot.h | 60 ++
> > include/soc/mscc/vsc7514_regs.h | 27 +
> > 28 files changed, 2219 insertions(+), 861 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/vsc7514_regs.c
> > create mode 100644 include/linux/mdio/mdio-mscc-miim.h
> > create mode 100644 include/soc/mscc/vsc7514_regs.h
> >
> > --
> > 2.25.1
> >
On Tue, Nov 16, 2021 at 07:04:04AM -0800, Colin Foster wrote:
> On Tue, Nov 16, 2021 at 12:34:17PM +0200, Andy Shevchenko wrote:
> > On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> > > My apologies for this next RFC taking so long. Life got in the way.
> > >
> > > The patch set in general is to add support for the VSC7511, VSC7512,
> > > VSC7513 and VSC7514 devices controlled over SPI. The driver is
> > > relatively functional for the internal phy ports (0-3) on the VSC7512.
> > > As I'll discuss, it is not yet functional for other ports yet.
> >
> > Since series touches fwnode, please Cc next time to Daniel Scally.
>
> Thank you. I will do this next time.
>
> For my future reference, is there a way that I could have known this? Or
> is this just knowledge that comes with experience? The email list I got
> was from running the patch set through get_maintainers.
It's just an ad-hoc since there are a few independent teams are doing
something that related to fwnode APIs: Daniel due to camera work for ACPI
enabled platforms, Qian due to a design bug with fwnode, Anand due to IIO
drivers, you are due to PHY (and possibly others I have no knowledge about).
I haven't asked for the others I listed above since their work probably
doesn't cross what you are doing, but camera might be closer.
> > It also appears [1] that somewhere in PHY code a bug is hidden
> > (at least I think so).
>
> Thank you for this information. I'll keep an eye out!
>
> > [1]: https://lore.kernel.org/lkml/[email protected]/T/#u
--
With Best Regards,
Andy Shevchenko
Hello,
On 15/11/2021 22:23:16-0800, Colin Foster wrote:
> struct gpio_chip recommends passing -1 as base to gpiolib. Doing so avoids
> conflicts when the chip is external and gpiochip0 already exists.
>
> Signed-off-by: Colin Foster <[email protected]>
> ---
> drivers/pinctrl/pinctrl-ocelot.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
> index cc7fb0556169..f015404c425c 100644
> --- a/drivers/pinctrl/pinctrl-ocelot.c
> +++ b/drivers/pinctrl/pinctrl-ocelot.c
> @@ -1308,7 +1308,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
> gc = &info->gpio_chip;
> gc->ngpio = info->desc->npins;
> gc->parent = &pdev->dev;
> - gc->base = 0;
> + gc->base = -1;
I can't remember why but I'm pretty sure I did that on purpose but this
indeed cause issues when the chip is external. I've asked Cl?ment to
check, let's see what the result is ;)
> gc->of_node = info->dev->of_node;
> gc->label = "ocelot-gpio";
>
> --
> 2.25.1
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
On Tue, Nov 16, 2021 at 07:32:08AM -0800, Colin Foster wrote:
> > > One thing to note: I've been following a pattern of adding "offset"
> > > variables to these drivers. I'm looking for feedback here, because I
> > > don't like it - however I feel like it is the "least bad" interface I
> > > could come up with.
> > >
> > > Specifically, ocelot has a regmap for GCB. ocelot-pinctrl would create a
> > > smaller regmap at an address of "GCB + 0x34".
> > >
> > > There are three options I saw here:
> > > 1. Have vsc7512_spi create a new regmap at GCB + 0x34 and pass that to
> > > ocelot-pinctrl
> > > 2. Give ocelot-pinctrl the concept of a "parent bus" by which it could
> > > request a regmap.
> > > 3. Keep the same GCB regmap, but pass in 0x34 as an offset.
> > >
> > >
> > > I will admit that option 2 sounds very enticing, but I don't know if
> > > that type of interaction exists. If not, implementing it is probably
> > > outside the scope of a first patch set. As such, I opted for option 3.
> >
> > I think that type of interaction is called "mfd", potentially even "syscon".
>
> Before diving in, I'd come across mfd and thought that might be the
> answer. I'll reconsider it now that I have several months of staring at
> kernel code under my belt. Maybe an mfd that does SPI setup and chip
> resetting. Then I could remove all SPI code from ocelot_vsc7512_spi.
That sounds acceptable to me.
On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> My apologies for this next RFC taking so long. Life got in the way.
>
>
> The patch set in general is to add support for the VSC7511, VSC7512,
> VSC7513 and VSC7514 devices controlled over SPI. The driver is
> relatively functional for the internal phy ports (0-3) on the VSC7512.
> As I'll discuss, it is not yet functional for other ports yet.
>
>
> I still think there are enough updates to bounce by the community
> in case I'm terribly off base or doomed to chase my tail.
I wanted to do some regression-testing with this patch set on the
Seville switch, but up until now I've been trying to actually make it
compile. See the changes required for that. Note that "can compile"
doesn't mean "can compile without warnings". Please check the build
reports on each individual patch on Patchwork and make sure the next
submission is warning-free. Note that there's a considerable amount of
drivers to build-test in both on and off configurations.
https://patchwork.kernel.org/project/netdevbpf/patch/[email protected]/
-- >8 -------------------------------------------------------------------------
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index b1032b7abaea..fbe78357ca94 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -1127,11 +1127,13 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
for (port = 0; port < ocelot->num_phys_ports; port++) {
struct phylink_pcs *phylink_pcs = felix->pcs[port];
+ struct mdio_device *mdio_device;
if (!phylink_pcs)
continue;
- mdio_device_free(phylink_pcs->mdio);
+ mdio_device = lynx_get_mdio_device(phylink_pcs);
+ mdio_device_free(mdio_device);
lynx_pcs_destroy(phylink_pcs);
}
mdiobus_unregister(felix->imdio);
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 268c09042824..12a87d8f977d 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1037,7 +1037,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
continue;
mdio_device = mdio_device_create(felix->imdio, addr);
- if (IS_ERR(pcs))
+ if (IS_ERR(mdio_device))
continue;
phylink_pcs = lynx_pcs_create(mdio_device);
@@ -1066,7 +1066,7 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
if (!phylink_pcs)
continue;
- mdio_device = lynx_pcs_get_mdio(phylink_pcs);
+ mdio_device = lynx_get_mdio_device(phylink_pcs);
mdio_device_free(mdio_device);
lynx_pcs_destroy(phylink_pcs);
}
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 3d93ac1376c6..3ab581b777eb 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -8,6 +8,7 @@
#include <linux/of_platform.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
+#include <linux/pcs-lynx.h>
#include "enetc_ierb.h"
#include "enetc_pf.h"
@@ -983,7 +984,7 @@ static void enetc_pl_mac_config(struct phylink_config *config,
priv = netdev_priv(pf->si->ndev);
if (pf->pcs)
- phylink_set_pcs(priv->phylink, &pf->pcs);
+ phylink_set_pcs(priv->phylink, pf->pcs);
}
static void enetc_force_rgmii_mac(struct enetc_hw *hw, int speed, int duplex)
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
index f8d2494b335c..5f9fc9252c79 100644
--- a/drivers/pinctrl/pinctrl-ocelot.c
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <soc/mscc/ocelot.h>
#include "core.h"
#include "pinconf.h"
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 6aeb7eac73f5..7571becba545 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -946,11 +946,12 @@ int ocelot_pinctrl_core_probe(struct device *dev,
struct regmap *pincfg_base, u32 pincfg_offset,
struct device_node *device_node);
#else
-int ocelot_pinctrl_core_probe(struct device *dev,
- struct pinctrl_desc *pinctrl_desc,
- struct regmap *regmap_base, u32 regmap_offset,
- struct regmap *pincfg_base, u32 pincfg_offset,
- struct device_node *device_node)
+static inline int
+ocelot_pinctrl_core_probe(struct device *dev,
+ struct pinctrl_desc *pinctrl_desc,
+ struct regmap *regmap_base, u32 regmap_offset,
+ struct regmap *pincfg_base, u32 pincfg_offset,
+ struct device_node *device_node)
{
return -EOPNOTSUPP;
}
@@ -960,8 +961,9 @@ int ocelot_pinctrl_core_probe(struct device *dev,
int microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
struct regmap *regmap, u32 offset);
#else
-int microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
- struct regmap *regmap, u32 offset)
+static inline int
+microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
+ struct regmap *regmap, u32 offset)
{
return -EOPNOTSUPP;
}
-- >8 -------------------------------------------------------------------------
On Tue, Nov 16, 2021 at 10:56:54PM +0000, Vladimir Oltean wrote:
> On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> > My apologies for this next RFC taking so long. Life got in the way.
> >
> >
> > The patch set in general is to add support for the VSC7511, VSC7512,
> > VSC7513 and VSC7514 devices controlled over SPI. The driver is
> > relatively functional for the internal phy ports (0-3) on the VSC7512.
> > As I'll discuss, it is not yet functional for other ports yet.
> >
> >
> > I still think there are enough updates to bounce by the community
> > in case I'm terribly off base or doomed to chase my tail.
>
> I wanted to do some regression-testing with this patch set on the
> Seville switch, but up until now I've been trying to actually make it
> compile. See the changes required for that. Note that "can compile"
> doesn't mean "can compile without warnings". Please check the build
> reports on each individual patch on Patchwork and make sure the next
> submission is warning-free. Note that there's a considerable amount of
> drivers to build-test in both on and off configurations.
> https://patchwork.kernel.org/project/netdevbpf/patch/[email protected]/
I'm very embarrassed. I scrambled at the end to try to clean things up
and didn't run enough tests. Sorry about that!
>
> -- >8 -------------------------------------------------------------------------
> diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
> index b1032b7abaea..fbe78357ca94 100644
> --- a/drivers/net/dsa/ocelot/felix_vsc9959.c
> +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
> @@ -1127,11 +1127,13 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
>
> for (port = 0; port < ocelot->num_phys_ports; port++) {
> struct phylink_pcs *phylink_pcs = felix->pcs[port];
> + struct mdio_device *mdio_device;
>
> if (!phylink_pcs)
> continue;
>
> - mdio_device_free(phylink_pcs->mdio);
> + mdio_device = lynx_get_mdio_device(phylink_pcs);
> + mdio_device_free(mdio_device);
> lynx_pcs_destroy(phylink_pcs);
> }
> mdiobus_unregister(felix->imdio);
> diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> index 268c09042824..12a87d8f977d 100644
> --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> @@ -1037,7 +1037,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
> continue;
>
> mdio_device = mdio_device_create(felix->imdio, addr);
> - if (IS_ERR(pcs))
> + if (IS_ERR(mdio_device))
> continue;
>
> phylink_pcs = lynx_pcs_create(mdio_device);
> @@ -1066,7 +1066,7 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
> if (!phylink_pcs)
> continue;
>
> - mdio_device = lynx_pcs_get_mdio(phylink_pcs);
> + mdio_device = lynx_get_mdio_device(phylink_pcs);
> mdio_device_free(mdio_device);
> lynx_pcs_destroy(phylink_pcs);
> }
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> index 3d93ac1376c6..3ab581b777eb 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> @@ -8,6 +8,7 @@
> #include <linux/of_platform.h>
> #include <linux/of_mdio.h>
> #include <linux/of_net.h>
> +#include <linux/pcs-lynx.h>
> #include "enetc_ierb.h"
> #include "enetc_pf.h"
>
> @@ -983,7 +984,7 @@ static void enetc_pl_mac_config(struct phylink_config *config,
>
> priv = netdev_priv(pf->si->ndev);
> if (pf->pcs)
> - phylink_set_pcs(priv->phylink, &pf->pcs);
> + phylink_set_pcs(priv->phylink, pf->pcs);
> }
>
> static void enetc_force_rgmii_mac(struct enetc_hw *hw, int speed, int duplex)
> diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
> index f8d2494b335c..5f9fc9252c79 100644
> --- a/drivers/pinctrl/pinctrl-ocelot.c
> +++ b/drivers/pinctrl/pinctrl-ocelot.c
> @@ -20,6 +20,7 @@
> #include <linux/platform_device.h>
> #include <linux/regmap.h>
> #include <linux/slab.h>
> +#include <soc/mscc/ocelot.h>
>
> #include "core.h"
> #include "pinconf.h"
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 6aeb7eac73f5..7571becba545 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -946,11 +946,12 @@ int ocelot_pinctrl_core_probe(struct device *dev,
> struct regmap *pincfg_base, u32 pincfg_offset,
> struct device_node *device_node);
> #else
> -int ocelot_pinctrl_core_probe(struct device *dev,
> - struct pinctrl_desc *pinctrl_desc,
> - struct regmap *regmap_base, u32 regmap_offset,
> - struct regmap *pincfg_base, u32 pincfg_offset,
> - struct device_node *device_node)
> +static inline int
> +ocelot_pinctrl_core_probe(struct device *dev,
> + struct pinctrl_desc *pinctrl_desc,
> + struct regmap *regmap_base, u32 regmap_offset,
> + struct regmap *pincfg_base, u32 pincfg_offset,
> + struct device_node *device_node)
> {
> return -EOPNOTSUPP;
> }
> @@ -960,8 +961,9 @@ int ocelot_pinctrl_core_probe(struct device *dev,
> int microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
> struct regmap *regmap, u32 offset);
> #else
> -int microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
> - struct regmap *regmap, u32 offset)
> +static inline int
> +microchip_sgpio_core_probe(struct device *dev, struct device_node *node,
> + struct regmap *regmap, u32 offset)
> {
> return -EOPNOTSUPP;
> }
> -- >8 -------------------------------------------------------------------------
On Tue, Nov 16, 2021 at 03:44:13PM -0800, Colin Foster wrote:
> On Tue, Nov 16, 2021 at 10:56:54PM +0000, Vladimir Oltean wrote:
> > On Mon, Nov 15, 2021 at 10:23:05PM -0800, Colin Foster wrote:
> > > My apologies for this next RFC taking so long. Life got in the way.
> > >
> > > The patch set in general is to add support for the VSC7511, VSC7512,
> > > VSC7513 and VSC7514 devices controlled over SPI. The driver is
> > > relatively functional for the internal phy ports (0-3) on the VSC7512.
> > > As I'll discuss, it is not yet functional for other ports yet.
> > >
> > >
> > > I still think there are enough updates to bounce by the community
> > > in case I'm terribly off base or doomed to chase my tail.
> >
> > I wanted to do some regression-testing with this patch set on the
> > Seville switch, but up until now I've been trying to actually make it
> > compile. See the changes required for that. Note that "can compile"
> > doesn't mean "can compile without warnings". Please check the build
> > reports on each individual patch on Patchwork and make sure the next
> > submission is warning-free. Note that there's a considerable amount of
> > drivers to build-test in both on and off configurations.
> > https://patchwork.kernel.org/project/netdevbpf/patch/[email protected]/
>
> I'm very embarrassed. I scrambled at the end to try to clean things up
> and didn't run enough tests. Sorry about that!
Don't worry, just make a note of one more thing to check next time.
My T1040RDB doesn't seem to want to boot right now, I don't know what's
up with it. My regression testing might be delayed for a little bit,
sorry for that.
On 11/15/21 10:23 PM, Colin Foster wrote:
> Switch seville to use of_mdiobus_register(bus, NULL) instead of just
> mdiobus_register. This code is about to be pulled into a separate module
> that can optionally define ports by the device_node.
>
> Signed-off-by: Colin Foster <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
--
Florian
Le Tue, 16 Nov 2021 18:36:33 +0100,
Alexandre Belloni <[email protected]> a écrit :
> Hello,
>
> On 15/11/2021 22:23:16-0800, Colin Foster wrote:
> > struct gpio_chip recommends passing -1 as base to gpiolib. Doing so avoids
> > conflicts when the chip is external and gpiochip0 already exists.
> >
> > Signed-off-by: Colin Foster <[email protected]>
> > ---
> > drivers/pinctrl/pinctrl-ocelot.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
> > index cc7fb0556169..f015404c425c 100644
> > --- a/drivers/pinctrl/pinctrl-ocelot.c
> > +++ b/drivers/pinctrl/pinctrl-ocelot.c
> > @@ -1308,7 +1308,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
> > gc = &info->gpio_chip;
> > gc->ngpio = info->desc->npins;
> > gc->parent = &pdev->dev;
> > - gc->base = 0;
> > + gc->base = -1;
>
> I can't remember why but I'm pretty sure I did that on purpose but this
> indeed cause issues when the chip is external. I've asked Clément to
> check, let's see what the result is ;)
After testing, it works on ocelot pcb123 board.
Tested-by: Clément Léger <[email protected]>
>
> > gc->of_node = info->dev->of_node;
> > gc->label = "ocelot-gpio";
> >
> > --
> > 2.25.1
> >
>
--
Clément Léger,
Embedded Linux and Kernel engineer at Bootlin
https://bootlin.com
Hi Colin,
nice work!
On Tue, Nov 16, 2021 at 7:23 AM Colin Foster
<[email protected]> wrote:
> pinctrl: ocelot: combine get resource and ioremap into single call
> pinctrl: ocelot: update pinctrl to automatic base address
> pinctrl: ocelot: convert pinctrl to regmap
> pinctrl: ocelot: expose ocelot_pinctrl_core_probe interface
> pinctrl: microchip-sgpio: update to support regmap
> device property: add helper function fwnode_get_child_node_count
> pinctrl: microchip-sgpio: change device tree matches to use nodes
> instead of device
> pinctrl: microchip-sgpio: expose microchip_sgpio_core_probe interface
Can these patches be broken out to its own series and handled
separately from the DSA stuff or is there build-time dependencies?
Yours,
Linus Walleij
Hi Linus,
On Fri, Nov 19, 2021 at 02:58:47AM +0100, Linus Walleij wrote:
> Hi Colin,
>
> nice work!
Thanks :)
>
> On Tue, Nov 16, 2021 at 7:23 AM Colin Foster
> <[email protected]> wrote:
>
> > pinctrl: ocelot: combine get resource and ioremap into single call
> > pinctrl: ocelot: update pinctrl to automatic base address
> > pinctrl: ocelot: convert pinctrl to regmap
> > pinctrl: ocelot: expose ocelot_pinctrl_core_probe interface
> > pinctrl: microchip-sgpio: update to support regmap
> > device property: add helper function fwnode_get_child_node_count
> > pinctrl: microchip-sgpio: change device tree matches to use nodes
> > instead of device
> > pinctrl: microchip-sgpio: expose microchip_sgpio_core_probe interface
>
> Can these patches be broken out to its own series and handled
> separately from the DSA stuff or is there build-time dependencies?
These should all be able to be a separate series if I did my job right.
Everything should have no functional change except for this:
> > pinctrl: ocelot: update pinctrl to automatic base address
Fortunately this was tested by Clément and didn't seem to have any
ill-fated side effects.
I assume this isn't something I wouldn't want to submit to net-next...
is there a different place (tree? board? list?) where those should be
submitted?
>
> Yours,
> Linus Walleij
On Wed, Nov 17, 2021 at 02:47:05PM +0100, Clément Léger wrote:
> Le Tue, 16 Nov 2021 18:36:33 +0100,
> Alexandre Belloni <[email protected]> a écrit :
>
> > Hello,
> >
> > On 15/11/2021 22:23:16-0800, Colin Foster wrote:
> > > struct gpio_chip recommends passing -1 as base to gpiolib. Doing so avoids
> > > conflicts when the chip is external and gpiochip0 already exists.
> > >
> > > Signed-off-by: Colin Foster <[email protected]>
> > > ---
> > > drivers/pinctrl/pinctrl-ocelot.c | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
> > > index cc7fb0556169..f015404c425c 100644
> > > --- a/drivers/pinctrl/pinctrl-ocelot.c
> > > +++ b/drivers/pinctrl/pinctrl-ocelot.c
> > > @@ -1308,7 +1308,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
> > > gc = &info->gpio_chip;
> > > gc->ngpio = info->desc->npins;
> > > gc->parent = &pdev->dev;
> > > - gc->base = 0;
> > > + gc->base = -1;
> >
> > I can't remember why but I'm pretty sure I did that on purpose but this
> > indeed cause issues when the chip is external. I've asked Clément to
> > check, let's see what the result is ;)
>
> After testing, it works on ocelot pcb123 board.
Thank you! Glad to hear it didn't break anything.
>
> Tested-by: Clément Léger <[email protected]>
>
> >
> > > gc->of_node = info->dev->of_node;
> > > gc->label = "ocelot-gpio";
> > >
> > > --
> > > 2.25.1
> > >
> >
>
>
>
> --
> Clément Léger,
> Embedded Linux and Kernel engineer at Bootlin
> https://bootlin.com
On Fri, Nov 19, 2021 at 3:14 AM Colin Foster
<[email protected]> wrote:
> On Fri, Nov 19, 2021 at 02:58:47AM +0100, Linus Walleij wrote:
> > > pinctrl: ocelot: combine get resource and ioremap into single call
> > > pinctrl: ocelot: update pinctrl to automatic base address
> > > pinctrl: ocelot: convert pinctrl to regmap
> > > pinctrl: ocelot: expose ocelot_pinctrl_core_probe interface
> > > pinctrl: microchip-sgpio: update to support regmap
> > > device property: add helper function fwnode_get_child_node_count
> > > pinctrl: microchip-sgpio: change device tree matches to use nodes
> > > instead of device
> > > pinctrl: microchip-sgpio: expose microchip_sgpio_core_probe interface
> >
> > Can these patches be broken out to its own series and handled
> > separately from the DSA stuff or is there build-time dependencies?
>
> These should all be able to be a separate series if I did my job right.
> Everything should have no functional change except for this:
>
> > > pinctrl: ocelot: update pinctrl to automatic base address
>
> Fortunately this was tested by Clément and didn't seem to have any
> ill-fated side effects.
>
> I assume this isn't something I wouldn't want to submit to net-next...
> is there a different place (tree? board? list?) where those should be
> submitted?
To the pinctrl maintainer i.e. me + [email protected]
then I can just apply them if there are no more review comments.
Yours,
Linus Walleij