Merge branch 'add-sparx5i-driver'
Steen Hegelund says: ==================== Adding the Sparx5i Switch Driver This series provides the Microchip Sparx5i Switch Driver The SparX-5 Enterprise Ethernet switch family provides a rich set of Enterprise switching features such as advanced TCAM-based VLAN and QoS processing enabling delivery of differentiated services, and security through TCAMbased frame processing using versatile content aware processor (VCAP). IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is supported with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K IPv4/3K IPv6 (S,G) multicast groups. L3 security features include source guard and reverse path forwarding (uRPF) tasks. Additional L3 features include VRF-Lite and IP tunnels (IP over GRE/IP). The SparX-5 switch family features a highly flexible set of Ethernet ports with support for 10G and 25G aggregation links, QSGMII, USGMII, and USXGMII. The device integrates a powerful 1 GHz dual-core ARM® Cortex®-A53 CPU enabling full management of the switch and advanced Enterprise applications. The SparX-5 switch family targets managed Layer 2 and Layer 3 equipment in SMB, SME, and Enterprise where high port count 1G/2.5G/5G/10G switching with 10G/25G aggregation links is required. The SparX-5 switch family consists of following SKUs: VSC7546 SparX-5-64 supports up to 64 Gbps of bandwidth with the following primary port configurations. - 6 ×10G - 16 × 2.5G + 2 × 10G - 24 × 1G + 4 × 10G VSC7549 SparX-5-90 supports up to 90 Gbps of bandwidth with the following primary port configurations. - 9 × 10G - 16 × 2.5G + 4 × 10G - 48 × 1G + 4 × 10G VSC7552 SparX-5-128 supports up to 128 Gbps of bandwidth with the following primary port configurations. - 12 × 10G - 6 x 10G + 2 x 25G - 16 × 2.5G + 8 × 10G - 48 × 1G + 8 × 10G VSC7556 SparX-5-160 supports up to 160 Gbps of bandwidth with the following primary port configurations. - 16 × 10G - 10 × 10G + 2 × 25G - 16 × 2.5G + 10 × 10G - 48 × 1G + 10 × 10G VSC7558 SparX-5-200 supports up to 200 Gbps of bandwidth with the following primary port configurations. - 20 × 10G - 8 × 25G In addition, the device supports one 10/100/1000/2500/5000 Mbps SGMII/SerDes node processor interface (NPI) Ethernet port. Time sensitive networking (TSN) is supported through a comprehensive set of features including frame preemption, cut-through, frame replication and elimination for reliability, enhanced scheduling: credit-based shaping, time-aware shaping, cyclic queuing, and forwarding, and per-stream policing and filtering. Together with IEEE 1588 and IEEE 802.1AS support, this guarantees low-latency deterministic networking for Industrial Ethernet. The Sparx5i support is developed on the PCB134 and PCB135 evaluation boards. - PCB134 main networking features: - 12x SFP+ front 10G module slots (connected to Sparx5i through SFI). - 8x SFP28 front 25G module slots (connected to Sparx5i through SFI high speed). - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port (on-board VSC8211 PHY connected to Sparx5i through SGMII). - PCB135 main networking features: - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each connected to VSC7558 through QSGMII. - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY each port connects to VSC7558 through SFI. - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high speed. - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII using a loopback add-on PCB) This series provides support for: - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G devices and media types. - Port module configuration for 10M to 25G speeds with SGMII, QSGMII, 1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes. - SerDes configuration via the Sparx5i SerDes driver (see below). - Host mode providing register based injection and extraction. - Switch mode providing MAC/VLAN table learning and Layer2 switching offloaded to the Sparx5i switch. - STP state, VLAN support, host/bridge port mode, Forwarding DB, and configuration and statistics via ethtool. More support will be added at a later stage. The Sparx5i Chip Register Model can be browsed at this location: https://github.com/microchip-ung/sparx-5_reginfo and the datasheet is available here: https://ww1.microchip.com/downloads/en/DeviceDoc/SparX-5_Family_L2L3_Enterprise_10G_Ethernet_Switches_Datasheet_00003822B.pdf The series depends on the following series currently on their way into the kernel: - 25G Base-R phy mode Link: https://lore.kernel.org/r/20210611125453.313308-1-steen.hegelund@microchip.com/ - Sparx5 Reset Driver Link: https://lore.kernel.org/r/20210416084054.2922327-1-steen.hegelund@microchip.com/ ChangeLog: v5: - cover letter - updated the description to match the latest data sheets - basic driver - added error message in case of reset controller error - port struct: replacing has_sfp with inband, adding pause_adv - host mode - port cleanup: unregisters netdevs and then removes phylink etc - checking for pause_adv when comparing port config changes - getting duplex and pause state in the link_up callback. - getting inband, autoneg and pause_adv config in the pcs_config callback. - port - use only the pause_adv bits when getting aneg status - use the inband state when updating the PCS and port config v4: - basic driver: Using devm_reset_control_get_optional_shared to get the reset control, and let the reset framework check if it is valid. - host mode (phylink): Use the PCS operations to get state and update configuration. Removed the setting of interface modes. Let phylink control this. Using the new 5gbase-r and 25gbase-r modes. Using a helper function to check if one of the 3 base-r modes has been selected. Currently it will not be possible to change the interface mode by changing the speed (e.g via ethtool). This will be added later. v3: - basic driver: - removed unneeded braces - release reference to ports node after use - use dev_err_probe to handle DEFER - update error value when bailing out (a few cases) - updated formatting of port struct and grouping of bool values - simplified the spx5_rmw and spx5_inst_rmw inline functions - host mode (netdev): - removed lockless flag - added port timer init - host mode (packet - manual injection): - updated error counters in error situations - implemented timer handling of watermark threshold: stop and restart netif queues. - fixed error message handling (rate limited) - fixed comment style error - used DIV_ROUND_UP macro - removed a debug message for open ports v2: - Updated bindings: - drop minItems for the reg property - Statistics implementation: - Reorganized statistics into ethtool groups: eth-phy, eth-mac, eth-ctrl, rmon as defined by the IEEE 802.3 categories and RFC 2819. - The remaining statistics are provided by the classic ethtool statistics command. - Hostmode support: - Removed netdev renaming - Validate ethernet address in sparx5_set_mac_address() ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
67faf76d26
@ -0,0 +1,226 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/microchip,sparx5-switch.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip Sparx5 Ethernet switch controller
|
||||
|
||||
maintainers:
|
||||
- Steen Hegelund <steen.hegelund@microchip.com>
|
||||
- Lars Povlsen <lars.povlsen@microchip.com>
|
||||
|
||||
description: |
|
||||
The SparX-5 Enterprise Ethernet switch family provides a rich set of
|
||||
Enterprise switching features such as advanced TCAM-based VLAN and
|
||||
QoS processing enabling delivery of differentiated services, and
|
||||
security through TCAM-based frame processing using versatile content
|
||||
aware processor (VCAP).
|
||||
|
||||
IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is supported
|
||||
with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K IPv4/3K
|
||||
IPv6 (S,G) multicast groups.
|
||||
|
||||
L3 security features include source guard and reverse path
|
||||
forwarding (uRPF) tasks. Additional L3 features include VRF-Lite and
|
||||
IP tunnels (IP over GRE/IP).
|
||||
|
||||
The SparX-5 switch family targets managed Layer 2 and Layer 3
|
||||
equipment in SMB, SME, and Enterprise where high port count
|
||||
1G/2.5G/5G/10G switching with 10G/25G aggregation links is required.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^switch@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
const: microchip,sparx5-switch
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: cpu target
|
||||
- description: devices target
|
||||
- description: general control block target
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: cpu
|
||||
- const: devices
|
||||
- const: gcb
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: register based extraction
|
||||
- description: frame dma based extraction
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: xtr
|
||||
- const: fdma
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: Reset controller used for switch core reset (soft reset)
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: switch
|
||||
|
||||
mac-address: true
|
||||
|
||||
ethernet-ports:
|
||||
type: object
|
||||
patternProperties:
|
||||
"^port@[0-9a-f]+$":
|
||||
type: object
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
reg:
|
||||
description: Switch port number
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
description:
|
||||
phandle of a Ethernet SerDes PHY. This defines which SerDes
|
||||
instance will handle the Ethernet traffic.
|
||||
|
||||
phy-mode:
|
||||
description:
|
||||
This specifies the interface used by the Ethernet SerDes towards
|
||||
the PHY or SFP.
|
||||
|
||||
microchip,bandwidth:
|
||||
description: Specifies bandwidth in Mbit/s allocated to the port.
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
maximum: 25000
|
||||
|
||||
phy-handle:
|
||||
description:
|
||||
phandle of a Ethernet PHY. This is optional and if provided it
|
||||
points to the cuPHY used by the Ethernet SerDes.
|
||||
|
||||
sfp:
|
||||
description:
|
||||
phandle of an SFP. This is optional and used when not specifying
|
||||
a cuPHY. It points to the SFP node that describes the SFP used by
|
||||
the Ethernet SerDes.
|
||||
|
||||
managed: true
|
||||
|
||||
microchip,sd-sgpio:
|
||||
description:
|
||||
Index of the ports Signal Detect SGPIO in the set of 384 SGPIOs
|
||||
This is optional, and only needed if the default used index is
|
||||
is not correct.
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
minimum: 0
|
||||
maximum: 383
|
||||
|
||||
required:
|
||||
- reg
|
||||
- phys
|
||||
- phy-mode
|
||||
- microchip,bandwidth
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- phy-handle
|
||||
- required:
|
||||
- sfp
|
||||
- managed
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- resets
|
||||
- reset-names
|
||||
- ethernet-ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
switch: switch@600000000 {
|
||||
compatible = "microchip,sparx5-switch";
|
||||
reg = <0 0x401000>,
|
||||
<0x10004000 0x7fc000>,
|
||||
<0x11010000 0xaf0000>;
|
||||
reg-names = "cpu", "devices", "gcb";
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "xtr";
|
||||
resets = <&reset 0>;
|
||||
reset-names = "switch";
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port0: port@0 {
|
||||
reg = <0>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 13>;
|
||||
phy-handle = <&phy0>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
/* ... */
|
||||
/* Then the 25G interfaces */
|
||||
port60: port@60 {
|
||||
reg = <60>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 29>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth60>;
|
||||
managed = "in-band-status";
|
||||
microchip,sd-sgpio = <365>;
|
||||
};
|
||||
port61: port@61 {
|
||||
reg = <61>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 30>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth61>;
|
||||
managed = "in-band-status";
|
||||
microchip,sd-sgpio = <369>;
|
||||
};
|
||||
port62: port@62 {
|
||||
reg = <62>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 31>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth62>;
|
||||
managed = "in-band-status";
|
||||
microchip,sd-sgpio = <373>;
|
||||
};
|
||||
port63: port@63 {
|
||||
reg = <63>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 32>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth63>;
|
||||
managed = "in-band-status";
|
||||
microchip,sd-sgpio = <377>;
|
||||
};
|
||||
/* Finally the Management interface */
|
||||
port64: port@64 {
|
||||
reg = <64>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 0>;
|
||||
phy-handle = <&phy64>;
|
||||
phy-mode = "sgmii";
|
||||
mac-address = [ 00 00 00 01 02 03 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
# vim: set ts=2 sw=2 sts=2 tw=80 et cc=80 ft=yaml :
|
@ -135,9 +135,12 @@
|
||||
};
|
||||
};
|
||||
|
||||
reset@611010008 {
|
||||
compatible = "microchip,sparx5-chip-reset";
|
||||
reset: reset-controller@611010008 {
|
||||
compatible = "microchip,sparx5-switch-reset";
|
||||
reg = <0x6 0x11010008 0x4>;
|
||||
reg-names = "gcb";
|
||||
#reset-cells = <1>;
|
||||
cpu-syscon = <&cpu_ctrl>;
|
||||
};
|
||||
|
||||
uart0: serial@600100000 {
|
||||
@ -275,6 +278,21 @@
|
||||
"GPIO_46", "GPIO_47";
|
||||
function = "emmc";
|
||||
};
|
||||
|
||||
miim1_pins: miim1-pins {
|
||||
pins = "GPIO_56", "GPIO_57";
|
||||
function = "miim";
|
||||
};
|
||||
|
||||
miim2_pins: miim2-pins {
|
||||
pins = "GPIO_58", "GPIO_59";
|
||||
function = "miim";
|
||||
};
|
||||
|
||||
miim3_pins: miim3-pins {
|
||||
pins = "GPIO_52", "GPIO_53";
|
||||
function = "miim";
|
||||
};
|
||||
};
|
||||
|
||||
sgpio0: gpio@61101036c {
|
||||
@ -285,6 +303,8 @@
|
||||
clocks = <&sys_clk>;
|
||||
pinctrl-0 = <&sgpio0_pins>;
|
||||
pinctrl-names = "default";
|
||||
resets = <&reset 0>;
|
||||
reset-names = "switch";
|
||||
reg = <0x6 0x1101036c 0x100>;
|
||||
sgpio_in0: gpio@0 {
|
||||
compatible = "microchip,sparx5-sgpio-bank";
|
||||
@ -292,6 +312,9 @@
|
||||
gpio-controller;
|
||||
#gpio-cells = <3>;
|
||||
ngpios = <96>;
|
||||
interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
};
|
||||
sgpio_out0: gpio@1 {
|
||||
compatible = "microchip,sparx5-sgpio-bank";
|
||||
@ -310,6 +333,8 @@
|
||||
clocks = <&sys_clk>;
|
||||
pinctrl-0 = <&sgpio1_pins>;
|
||||
pinctrl-names = "default";
|
||||
resets = <&reset 0>;
|
||||
reset-names = "switch";
|
||||
reg = <0x6 0x11010484 0x100>;
|
||||
sgpio_in1: gpio@0 {
|
||||
compatible = "microchip,sparx5-sgpio-bank";
|
||||
@ -317,6 +342,9 @@
|
||||
gpio-controller;
|
||||
#gpio-cells = <3>;
|
||||
ngpios = <96>;
|
||||
interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
};
|
||||
sgpio_out1: gpio@1 {
|
||||
compatible = "microchip,sparx5-sgpio-bank";
|
||||
@ -335,6 +363,8 @@
|
||||
clocks = <&sys_clk>;
|
||||
pinctrl-0 = <&sgpio2_pins>;
|
||||
pinctrl-names = "default";
|
||||
resets = <&reset 0>;
|
||||
reset-names = "switch";
|
||||
reg = <0x6 0x1101059c 0x100>;
|
||||
sgpio_in2: gpio@0 {
|
||||
reg = <0>;
|
||||
@ -342,6 +372,9 @@
|
||||
gpio-controller;
|
||||
#gpio-cells = <3>;
|
||||
ngpios = <96>;
|
||||
interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
};
|
||||
sgpio_out2: gpio@1 {
|
||||
compatible = "microchip,sparx5-sgpio-bank";
|
||||
@ -386,5 +419,62 @@
|
||||
#thermal-sensor-cells = <0>;
|
||||
clocks = <&ahb_clk>;
|
||||
};
|
||||
|
||||
mdio0: mdio@6110102b0 {
|
||||
compatible = "mscc,ocelot-miim";
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x6 0x110102b0 0x24>;
|
||||
};
|
||||
|
||||
mdio1: mdio@6110102d4 {
|
||||
compatible = "mscc,ocelot-miim";
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&miim1_pins>;
|
||||
pinctrl-names = "default";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x6 0x110102d4 0x24>;
|
||||
};
|
||||
|
||||
mdio2: mdio@6110102f8 {
|
||||
compatible = "mscc,ocelot-miim";
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&miim2_pins>;
|
||||
pinctrl-names = "default";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x6 0x110102d4 0x24>;
|
||||
};
|
||||
|
||||
mdio3: mdio@61101031c {
|
||||
compatible = "mscc,ocelot-miim";
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&miim3_pins>;
|
||||
pinctrl-names = "default";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x6 0x1101031c 0x24>;
|
||||
};
|
||||
|
||||
serdes: serdes@10808000 {
|
||||
compatible = "microchip,sparx5-serdes";
|
||||
#phy-cells = <1>;
|
||||
clocks = <&sys_clk>;
|
||||
reg = <0x6 0x10808000 0x5d0000>;
|
||||
};
|
||||
|
||||
switch: switch@0x600000000 {
|
||||
compatible = "microchip,sparx5-switch";
|
||||
reg = <0x6 0 0x401000>,
|
||||
<0x6 0x10004000 0x7fc000>,
|
||||
<0x6 0x11010000 0xaf0000>;
|
||||
reg-names = "cpu", "dev", "gcb";
|
||||
interrupt-names = "xtr";
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
resets = <&reset 0>;
|
||||
reset-names = "switch";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -7,30 +7,6 @@
|
||||
#include "sparx5_pcb_common.dtsi"
|
||||
|
||||
/{
|
||||
aliases {
|
||||
i2c0 = &i2c0;
|
||||
i2c100 = &i2c100;
|
||||
i2c101 = &i2c101;
|
||||
i2c102 = &i2c102;
|
||||
i2c103 = &i2c103;
|
||||
i2c104 = &i2c104;
|
||||
i2c105 = &i2c105;
|
||||
i2c106 = &i2c106;
|
||||
i2c107 = &i2c107;
|
||||
i2c108 = &i2c108;
|
||||
i2c109 = &i2c109;
|
||||
i2c110 = &i2c110;
|
||||
i2c111 = &i2c111;
|
||||
i2c112 = &i2c112;
|
||||
i2c113 = &i2c113;
|
||||
i2c114 = &i2c114;
|
||||
i2c115 = &i2c115;
|
||||
i2c116 = &i2c116;
|
||||
i2c117 = &i2c117;
|
||||
i2c118 = &i2c118;
|
||||
i2c119 = &i2c119;
|
||||
};
|
||||
|
||||
gpio-restart {
|
||||
compatible = "gpio-restart";
|
||||
gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
|
||||
@ -298,17 +274,10 @@
|
||||
|
||||
&spi0 {
|
||||
status = "okay";
|
||||
spi@0 {
|
||||
compatible = "spi-mux";
|
||||
mux-controls = <&mux>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>; /* CS0 */
|
||||
spi-flash@9 {
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0x9>; /* SPI */
|
||||
};
|
||||
spi-flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -328,6 +297,33 @@
|
||||
};
|
||||
};
|
||||
|
||||
&sgpio0 {
|
||||
status = "okay";
|
||||
microchip,sgpio-port-ranges = <8 15>;
|
||||
gpio@0 {
|
||||
ngpios = <64>;
|
||||
};
|
||||
gpio@1 {
|
||||
ngpios = <64>;
|
||||
};
|
||||
};
|
||||
|
||||
&sgpio1 {
|
||||
status = "okay";
|
||||
microchip,sgpio-port-ranges = <24 31>;
|
||||
gpio@0 {
|
||||
ngpios = <64>;
|
||||
};
|
||||
gpio@1 {
|
||||
ngpios = <64>;
|
||||
};
|
||||
};
|
||||
|
||||
&sgpio2 {
|
||||
status = "okay";
|
||||
microchip,sgpio-port-ranges = <0 0>, <11 31>;
|
||||
};
|
||||
|
||||
&gpio {
|
||||
i2cmux_pins_i: i2cmux-pins-i {
|
||||
pins = "GPIO_16", "GPIO_17", "GPIO_18", "GPIO_19",
|
||||
@ -415,9 +411,9 @@
|
||||
|
||||
&i2c0_imux {
|
||||
pinctrl-names =
|
||||
"i2c100", "i2c101", "i2c102", "i2c103",
|
||||
"i2c104", "i2c105", "i2c106", "i2c107",
|
||||
"i2c108", "i2c109", "i2c110", "i2c111", "idle";
|
||||
"i2c_sfp1", "i2c_sfp2", "i2c_sfp3", "i2c_sfp4",
|
||||
"i2c_sfp5", "i2c_sfp6", "i2c_sfp7", "i2c_sfp8",
|
||||
"i2c_sfp9", "i2c_sfp10", "i2c_sfp11", "i2c_sfp12", "idle";
|
||||
pinctrl-0 = <&i2cmux_0>;
|
||||
pinctrl-1 = <&i2cmux_1>;
|
||||
pinctrl-2 = <&i2cmux_2>;
|
||||
@ -431,62 +427,62 @@
|
||||
pinctrl-10 = <&i2cmux_10>;
|
||||
pinctrl-11 = <&i2cmux_11>;
|
||||
pinctrl-12 = <&i2cmux_pins_i>;
|
||||
i2c100: i2c_sfp1 {
|
||||
i2c_sfp1: i2c_sfp1 {
|
||||
reg = <0x0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c101: i2c_sfp2 {
|
||||
i2c_sfp2: i2c_sfp2 {
|
||||
reg = <0x1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c102: i2c_sfp3 {
|
||||
i2c_sfp3: i2c_sfp3 {
|
||||
reg = <0x2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c103: i2c_sfp4 {
|
||||
i2c_sfp4: i2c_sfp4 {
|
||||
reg = <0x3>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c104: i2c_sfp5 {
|
||||
i2c_sfp5: i2c_sfp5 {
|
||||
reg = <0x4>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c105: i2c_sfp6 {
|
||||
i2c_sfp6: i2c_sfp6 {
|
||||
reg = <0x5>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c106: i2c_sfp7 {
|
||||
i2c_sfp7: i2c_sfp7 {
|
||||
reg = <0x6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c107: i2c_sfp8 {
|
||||
i2c_sfp8: i2c_sfp8 {
|
||||
reg = <0x7>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c108: i2c_sfp9 {
|
||||
i2c_sfp9: i2c_sfp9 {
|
||||
reg = <0x8>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c109: i2c_sfp10 {
|
||||
i2c_sfp10: i2c_sfp10 {
|
||||
reg = <0x9>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c110: i2c_sfp11 {
|
||||
i2c_sfp11: i2c_sfp11 {
|
||||
reg = <0xa>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c111: i2c_sfp12 {
|
||||
i2c_sfp12: i2c_sfp12 {
|
||||
reg = <0xb>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -499,44 +495,413 @@
|
||||
&gpio 61 GPIO_ACTIVE_HIGH
|
||||
&gpio 54 GPIO_ACTIVE_HIGH>;
|
||||
idle-state = <0x8>;
|
||||
i2c112: i2c_sfp13 {
|
||||
i2c_sfp13: i2c_sfp13 {
|
||||
reg = <0x0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c113: i2c_sfp14 {
|
||||
i2c_sfp14: i2c_sfp14 {
|
||||
reg = <0x1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c114: i2c_sfp15 {
|
||||
i2c_sfp15: i2c_sfp15 {
|
||||
reg = <0x2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c115: i2c_sfp16 {
|
||||
i2c_sfp16: i2c_sfp16 {
|
||||
reg = <0x3>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c116: i2c_sfp17 {
|
||||
i2c_sfp17: i2c_sfp17 {
|
||||
reg = <0x4>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c117: i2c_sfp18 {
|
||||
i2c_sfp18: i2c_sfp18 {
|
||||
reg = <0x5>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c118: i2c_sfp19 {
|
||||
i2c_sfp19: i2c_sfp19 {
|
||||
reg = <0x6>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c119: i2c_sfp20 {
|
||||
i2c_sfp20: i2c_sfp20 {
|
||||
reg = <0x7>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
&mdio3 {
|
||||
status = "ok";
|
||||
phy64: ethernet-phy@64 {
|
||||
reg = <28>;
|
||||
};
|
||||
};
|
||||
|
||||
&axi {
|
||||
sfp_eth12: sfp-eth12 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp1>;
|
||||
tx-disable-gpios = <&sgpio_out2 11 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 11 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 11 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 12 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth13: sfp-eth13 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp2>;
|
||||
tx-disable-gpios = <&sgpio_out2 12 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 12 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 12 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 13 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth14: sfp-eth14 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp3>;
|
||||
tx-disable-gpios = <&sgpio_out2 13 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 13 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 13 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 14 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth15: sfp-eth15 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp4>;
|
||||
tx-disable-gpios = <&sgpio_out2 14 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 14 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 14 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 15 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth48: sfp-eth48 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp5>;
|
||||
tx-disable-gpios = <&sgpio_out2 15 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 15 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 15 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 16 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth49: sfp-eth49 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp6>;
|
||||
tx-disable-gpios = <&sgpio_out2 16 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 16 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 16 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 17 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth50: sfp-eth50 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp7>;
|
||||
tx-disable-gpios = <&sgpio_out2 17 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 17 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 17 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 18 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth51: sfp-eth51 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp8>;
|
||||
tx-disable-gpios = <&sgpio_out2 18 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 18 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 18 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 19 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth52: sfp-eth52 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp9>;
|
||||
tx-disable-gpios = <&sgpio_out2 19 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 19 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 19 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 20 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth53: sfp-eth53 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp10>;
|
||||
tx-disable-gpios = <&sgpio_out2 20 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 20 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 20 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 21 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth54: sfp-eth54 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp11>;
|
||||
tx-disable-gpios = <&sgpio_out2 21 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 21 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 21 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 22 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth55: sfp-eth55 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp12>;
|
||||
tx-disable-gpios = <&sgpio_out2 22 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 22 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 22 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 23 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth56: sfp-eth56 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp13>;
|
||||
tx-disable-gpios = <&sgpio_out2 23 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 23 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 23 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 24 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth57: sfp-eth57 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp14>;
|
||||
tx-disable-gpios = <&sgpio_out2 24 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 24 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 24 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 25 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth58: sfp-eth58 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp15>;
|
||||
tx-disable-gpios = <&sgpio_out2 25 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 25 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 25 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 26 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth59: sfp-eth59 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp16>;
|
||||
tx-disable-gpios = <&sgpio_out2 26 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 26 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 26 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 27 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth60: sfp-eth60 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp17>;
|
||||
tx-disable-gpios = <&sgpio_out2 27 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 27 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 27 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 28 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth61: sfp-eth61 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp18>;
|
||||
tx-disable-gpios = <&sgpio_out2 28 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 28 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 28 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 29 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth62: sfp-eth62 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp19>;
|
||||
tx-disable-gpios = <&sgpio_out2 29 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 29 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 29 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 30 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth63: sfp-eth63 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp20>;
|
||||
tx-disable-gpios = <&sgpio_out2 30 1 GPIO_ACTIVE_LOW>;
|
||||
los-gpios = <&sgpio_in2 30 1 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 30 2 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 31 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
&switch {
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* 10G SFPs */
|
||||
port12: port@12 {
|
||||
reg = <12>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 13>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth12>;
|
||||
microchip,sd-sgpio = <301>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port13: port@13 {
|
||||
reg = <13>;
|
||||
/* Example: CU SFP, 1G speed */
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 14>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth13>;
|
||||
microchip,sd-sgpio = <305>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port14: port@14 {
|
||||
reg = <14>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 15>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth14>;
|
||||
microchip,sd-sgpio = <309>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port15: port@15 {
|
||||
reg = <15>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 16>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth15>;
|
||||
microchip,sd-sgpio = <313>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port48: port@48 {
|
||||
reg = <48>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 17>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth48>;
|
||||
microchip,sd-sgpio = <317>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port49: port@49 {
|
||||
reg = <49>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 18>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth49>;
|
||||
microchip,sd-sgpio = <321>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port50: port@50 {
|
||||
reg = <50>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 19>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth50>;
|
||||
microchip,sd-sgpio = <325>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port51: port@51 {
|
||||
reg = <51>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 20>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth51>;
|
||||
microchip,sd-sgpio = <329>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port52: port@52 {
|
||||
reg = <52>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 21>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth52>;
|
||||
microchip,sd-sgpio = <333>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port53: port@53 {
|
||||
reg = <53>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 22>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth53>;
|
||||
microchip,sd-sgpio = <337>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port54: port@54 {
|
||||
reg = <54>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 23>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth54>;
|
||||
microchip,sd-sgpio = <341>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port55: port@55 {
|
||||
reg = <55>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 24>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth55>;
|
||||
microchip,sd-sgpio = <345>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
/* 25G SFPs */
|
||||
port56: port@56 {
|
||||
reg = <56>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 25>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth56>;
|
||||
microchip,sd-sgpio = <349>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port57: port@57 {
|
||||
reg = <57>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 26>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth57>;
|
||||
microchip,sd-sgpio = <353>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port58: port@58 {
|
||||
reg = <58>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 27>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth58>;
|
||||
microchip,sd-sgpio = <357>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port59: port@59 {
|
||||
reg = <59>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 28>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth59>;
|
||||
microchip,sd-sgpio = <361>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port60: port@60 {
|
||||
reg = <60>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 29>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth60>;
|
||||
microchip,sd-sgpio = <365>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port61: port@61 {
|
||||
reg = <61>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 30>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth61>;
|
||||
microchip,sd-sgpio = <369>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port62: port@62 {
|
||||
reg = <62>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 31>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth62>;
|
||||
microchip,sd-sgpio = <373>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port63: port@63 {
|
||||
reg = <63>;
|
||||
microchip,bandwidth = <10000>;
|
||||
phys = <&serdes 32>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth63>;
|
||||
microchip,sd-sgpio = <377>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
/* Finally the Management interface */
|
||||
port64: port@64 {
|
||||
reg = <64>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 0>;
|
||||
phy-handle = <&phy64>;
|
||||
phy-mode = "sgmii";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -7,14 +7,6 @@
|
||||
#include "sparx5_pcb_common.dtsi"
|
||||
|
||||
/{
|
||||
aliases {
|
||||
i2c0 = &i2c0;
|
||||
i2c152 = &i2c152;
|
||||
i2c153 = &i2c153;
|
||||
i2c154 = &i2c154;
|
||||
i2c155 = &i2c155;
|
||||
};
|
||||
|
||||
gpio-restart {
|
||||
compatible = "gpio-restart";
|
||||
gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
|
||||
@ -97,17 +89,10 @@
|
||||
|
||||
&spi0 {
|
||||
status = "okay";
|
||||
spi@0 {
|
||||
compatible = "spi-mux";
|
||||
mux-controls = <&mux>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>; /* CS0 */
|
||||
spi-flash@9 {
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0x9>; /* SPI */
|
||||
};
|
||||
spi-flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -138,6 +123,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
&sgpio2 {
|
||||
status = "okay";
|
||||
microchip,sgpio-port-ranges = <0 0>, <16 18>, <28 31>;
|
||||
};
|
||||
|
||||
&axi {
|
||||
i2c0_imux: i2c0-imux@0 {
|
||||
compatible = "i2c-mux-pinctrl";
|
||||
@ -149,31 +139,614 @@
|
||||
|
||||
&i2c0_imux {
|
||||
pinctrl-names =
|
||||
"i2c152", "i2c153", "i2c154", "i2c155",
|
||||
"i2c_sfp1", "i2c_sfp2", "i2c_sfp3", "i2c_sfp4",
|
||||
"idle";
|
||||
pinctrl-0 = <&i2cmux_s29>;
|
||||
pinctrl-1 = <&i2cmux_s30>;
|
||||
pinctrl-2 = <&i2cmux_s31>;
|
||||
pinctrl-3 = <&i2cmux_s32>;
|
||||
pinctrl-4 = <&i2cmux_pins_i>;
|
||||
i2c152: i2c_sfp1 {
|
||||
i2c_sfp1: i2c_sfp1 {
|
||||
reg = <0x0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c153: i2c_sfp2 {
|
||||
i2c_sfp2: i2c_sfp2 {
|
||||
reg = <0x1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c154: i2c_sfp3 {
|
||||
i2c_sfp3: i2c_sfp3 {
|
||||
reg = <0x2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
i2c155: i2c_sfp4 {
|
||||
i2c_sfp4: i2c_sfp4 {
|
||||
reg = <0x3>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
&axi {
|
||||
sfp_eth60: sfp-eth60 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp1>;
|
||||
tx-disable-gpios = <&sgpio_out2 28 0 GPIO_ACTIVE_LOW>;
|
||||
rate-select0-gpios = <&sgpio_out2 28 1 GPIO_ACTIVE_HIGH>;
|
||||
los-gpios = <&sgpio_in2 28 0 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 28 1 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 28 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth61: sfp-eth61 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp2>;
|
||||
tx-disable-gpios = <&sgpio_out2 29 0 GPIO_ACTIVE_LOW>;
|
||||
rate-select0-gpios = <&sgpio_out2 29 1 GPIO_ACTIVE_HIGH>;
|
||||
los-gpios = <&sgpio_in2 29 0 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 29 1 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 29 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth62: sfp-eth62 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp3>;
|
||||
tx-disable-gpios = <&sgpio_out2 30 0 GPIO_ACTIVE_LOW>;
|
||||
rate-select0-gpios = <&sgpio_out2 30 1 GPIO_ACTIVE_HIGH>;
|
||||
los-gpios = <&sgpio_in2 30 0 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 30 1 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 30 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
sfp_eth63: sfp-eth63 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c_sfp4>;
|
||||
tx-disable-gpios = <&sgpio_out2 31 0 GPIO_ACTIVE_LOW>;
|
||||
rate-select0-gpios = <&sgpio_out2 31 1 GPIO_ACTIVE_HIGH>;
|
||||
los-gpios = <&sgpio_in2 31 0 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&sgpio_in2 31 1 GPIO_ACTIVE_LOW>;
|
||||
tx-fault-gpios = <&sgpio_in2 31 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
&mdio0 {
|
||||
status = "ok";
|
||||
phy0: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
phy1: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
phy2: ethernet-phy@2 {
|
||||
reg = <2>;
|
||||
};
|
||||
phy3: ethernet-phy@3 {
|
||||
reg = <3>;
|
||||
};
|
||||
phy4: ethernet-phy@4 {
|
||||
reg = <4>;
|
||||
};
|
||||
phy5: ethernet-phy@5 {
|
||||
reg = <5>;
|
||||
};
|
||||
phy6: ethernet-phy@6 {
|
||||
reg = <6>;
|
||||
};
|
||||
phy7: ethernet-phy@7 {
|
||||
reg = <7>;
|
||||
};
|
||||
phy8: ethernet-phy@8 {
|
||||
reg = <8>;
|
||||
};
|
||||
phy9: ethernet-phy@9 {
|
||||
reg = <9>;
|
||||
};
|
||||
phy10: ethernet-phy@10 {
|
||||
reg = <10>;
|
||||
};
|
||||
phy11: ethernet-phy@11 {
|
||||
reg = <11>;
|
||||
};
|
||||
phy12: ethernet-phy@12 {
|
||||
reg = <12>;
|
||||
};
|
||||
phy13: ethernet-phy@13 {
|
||||
reg = <13>;
|
||||
};
|
||||
phy14: ethernet-phy@14 {
|
||||
reg = <14>;
|
||||
};
|
||||
phy15: ethernet-phy@15 {
|
||||
reg = <15>;
|
||||
};
|
||||
phy16: ethernet-phy@16 {
|
||||
reg = <16>;
|
||||
};
|
||||
phy17: ethernet-phy@17 {
|
||||
reg = <17>;
|
||||
};
|
||||
phy18: ethernet-phy@18 {
|
||||
reg = <18>;
|
||||
};
|
||||
phy19: ethernet-phy@19 {
|
||||
reg = <19>;
|
||||
};
|
||||
phy20: ethernet-phy@20 {
|
||||
reg = <20>;
|
||||
};
|
||||
phy21: ethernet-phy@21 {
|
||||
reg = <21>;
|
||||
};
|
||||
phy22: ethernet-phy@22 {
|
||||
reg = <22>;
|
||||
};
|
||||
phy23: ethernet-phy@23 {
|
||||
reg = <23>;
|
||||
};
|
||||
};
|
||||
|
||||
&mdio1 {
|
||||
status = "ok";
|
||||
phy24: ethernet-phy@24 {
|
||||
reg = <0>;
|
||||
};
|
||||
phy25: ethernet-phy@25 {
|
||||
reg = <1>;
|
||||
};
|
||||
phy26: ethernet-phy@26 {
|
||||
reg = <2>;
|
||||
};
|
||||
phy27: ethernet-phy@27 {
|
||||
reg = <3>;
|
||||
};
|
||||
phy28: ethernet-phy@28 {
|
||||
reg = <4>;
|
||||
};
|
||||
phy29: ethernet-phy@29 {
|
||||
reg = <5>;
|
||||
};
|
||||
phy30: ethernet-phy@30 {
|
||||
reg = <6>;
|
||||
};
|
||||
phy31: ethernet-phy@31 {
|
||||
reg = <7>;
|
||||
};
|
||||
phy32: ethernet-phy@32 {
|
||||
reg = <8>;
|
||||
};
|
||||
phy33: ethernet-phy@33 {
|
||||
reg = <9>;
|
||||
};
|
||||
phy34: ethernet-phy@34 {
|
||||
reg = <10>;
|
||||
};
|
||||
phy35: ethernet-phy@35 {
|
||||
reg = <11>;
|
||||
};
|
||||
phy36: ethernet-phy@36 {
|
||||
reg = <12>;
|
||||
};
|
||||
phy37: ethernet-phy@37 {
|
||||
reg = <13>;
|
||||
};
|
||||
phy38: ethernet-phy@38 {
|
||||
reg = <14>;
|
||||
};
|
||||
phy39: ethernet-phy@39 {
|
||||
reg = <15>;
|
||||
};
|
||||
phy40: ethernet-phy@40 {
|
||||
reg = <16>;
|
||||
};
|
||||
phy41: ethernet-phy@41 {
|
||||
reg = <17>;
|
||||
};
|
||||
phy42: ethernet-phy@42 {
|
||||
reg = <18>;
|
||||
};
|
||||
phy43: ethernet-phy@43 {
|
||||
reg = <19>;
|
||||
};
|
||||
phy44: ethernet-phy@44 {
|
||||
reg = <20>;
|
||||
};
|
||||
phy45: ethernet-phy@45 {
|
||||
reg = <21>;
|
||||
};
|
||||
phy46: ethernet-phy@46 {
|
||||
reg = <22>;
|
||||
};
|
||||
phy47: ethernet-phy@47 {
|
||||
reg = <23>;
|
||||
};
|
||||
};
|
||||
|
||||
&mdio3 {
|
||||
status = "ok";
|
||||
phy64: ethernet-phy@64 {
|
||||
reg = <28>;
|
||||
};
|
||||
};
|
||||
|
||||
&switch {
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port0: port@0 {
|
||||
reg = <0>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 13>;
|
||||
phy-handle = <&phy0>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port1: port@1 {
|
||||
reg = <1>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 13>;
|
||||
phy-handle = <&phy1>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port2: port@2 {
|
||||
reg = <2>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 13>;
|
||||
phy-handle = <&phy2>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port3: port@3 {
|
||||
reg = <3>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 13>;
|
||||
phy-handle = <&phy3>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port4: port@4 {
|
||||
reg = <4>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 14>;
|
||||
phy-handle = <&phy4>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port5: port@5 {
|
||||
reg = <5>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 14>;
|
||||
phy-handle = <&phy5>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port6: port@6 {
|
||||
reg = <6>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 14>;
|
||||
phy-handle = <&phy6>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port7: port@7 {
|
||||
reg = <7>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 14>;
|
||||
phy-handle = <&phy7>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port8: port@8 {
|
||||
reg = <8>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 15>;
|
||||
phy-handle = <&phy8>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port9: port@9 {
|
||||
reg = <9>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 15>;
|
||||
phy-handle = <&phy9>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port10: port@10 {
|
||||
reg = <10>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 15>;
|
||||
phy-handle = <&phy10>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port11: port@11 {
|
||||
reg = <11>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 15>;
|
||||
phy-handle = <&phy11>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port12: port@12 {
|
||||
reg = <12>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 16>;
|
||||
phy-handle = <&phy12>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port13: port@13 {
|
||||
reg = <13>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 16>;
|
||||
phy-handle = <&phy13>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port14: port@14 {
|
||||
reg = <14>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 16>;
|
||||
phy-handle = <&phy14>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port15: port@15 {
|
||||
reg = <15>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 16>;
|
||||
phy-handle = <&phy15>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port16: port@16 {
|
||||
reg = <16>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 17>;
|
||||
phy-handle = <&phy16>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port17: port@17 {
|
||||
reg = <17>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 17>;
|
||||
phy-handle = <&phy17>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port18: port@18 {
|
||||
reg = <18>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 17>;
|
||||
phy-handle = <&phy18>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port19: port@19 {
|
||||
reg = <19>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 17>;
|
||||
phy-handle = <&phy19>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port20: port@20 {
|
||||
reg = <20>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 18>;
|
||||
phy-handle = <&phy20>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port21: port@21 {
|
||||
reg = <21>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 18>;
|
||||
phy-handle = <&phy21>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port22: port@22 {
|
||||
reg = <22>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 18>;
|
||||
phy-handle = <&phy22>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port23: port@23 {
|
||||
reg = <23>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 18>;
|
||||
phy-handle = <&phy23>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port24: port@24 {
|
||||
reg = <24>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 19>;
|
||||
phy-handle = <&phy24>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port25: port@25 {
|
||||
reg = <25>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 19>;
|
||||
phy-handle = <&phy25>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port26: port@26 {
|
||||
reg = <26>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 19>;
|
||||
phy-handle = <&phy26>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port27: port@27 {
|
||||
reg = <27>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 19>;
|
||||
phy-handle = <&phy27>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port28: port@28 {
|
||||
reg = <28>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 20>;
|
||||
phy-handle = <&phy28>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port29: port@29 {
|
||||
reg = <29>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 20>;
|
||||
phy-handle = <&phy29>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port30: port@30 {
|
||||
reg = <30>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 20>;
|
||||
phy-handle = <&phy30>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port31: port@31 {
|
||||
reg = <31>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 20>;
|
||||
phy-handle = <&phy31>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port32: port@32 {
|
||||
reg = <32>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 21>;
|
||||
phy-handle = <&phy32>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port33: port@33 {
|
||||
reg = <33>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 21>;
|
||||
phy-handle = <&phy33>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port34: port@34 {
|
||||
reg = <34>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 21>;
|
||||
phy-handle = <&phy34>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port35: port@35 {
|
||||
reg = <35>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 21>;
|
||||
phy-handle = <&phy35>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port36: port@36 {
|
||||
reg = <36>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 22>;
|
||||
phy-handle = <&phy36>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port37: port@37 {
|
||||
reg = <37>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 22>;
|
||||
phy-handle = <&phy37>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port38: port@38 {
|
||||
reg = <38>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 22>;
|
||||
phy-handle = <&phy38>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port39: port@39 {
|
||||
reg = <39>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 22>;
|
||||
phy-handle = <&phy39>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port40: port@40 {
|
||||
reg = <40>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 23>;
|
||||
phy-handle = <&phy40>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port41: port@41 {
|
||||
reg = <41>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 23>;
|
||||
phy-handle = <&phy41>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port42: port@42 {
|
||||
reg = <42>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 23>;
|
||||
phy-handle = <&phy42>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port43: port@43 {
|
||||
reg = <43>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 23>;
|
||||
phy-handle = <&phy43>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port44: port@44 {
|
||||
reg = <44>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 24>;
|
||||
phy-handle = <&phy44>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port45: port@45 {
|
||||
reg = <45>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 24>;
|
||||
phy-handle = <&phy45>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port46: port@46 {
|
||||
reg = <46>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 24>;
|
||||
phy-handle = <&phy46>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
port47: port@47 {
|
||||
reg = <47>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 24>;
|
||||
phy-handle = <&phy47>;
|
||||
phy-mode = "qsgmii";
|
||||
};
|
||||
/* Then the 25G interfaces */
|
||||
port60: port@60 {
|
||||
reg = <60>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 29>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth60>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port61: port@61 {
|
||||
reg = <61>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 30>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth61>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port62: port@62 {
|
||||
reg = <62>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 31>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth62>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
port63: port@63 {
|
||||
reg = <63>;
|
||||
microchip,bandwidth = <25000>;
|
||||
phys = <&serdes 32>;
|
||||
phy-mode = "10gbase-r";
|
||||
sfp = <&sfp_eth63>;
|
||||
managed = "in-band-status";
|
||||
};
|
||||
/* Finally the Management interface */
|
||||
port64: port@64 {
|
||||
reg = <64>;
|
||||
microchip,bandwidth = <1000>;
|
||||
phys = <&serdes 0>;
|
||||
phy-handle = <&phy64>;
|
||||
phy-mode = "sgmii";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -54,4 +54,6 @@ config LAN743X
|
||||
To compile this driver as a module, choose M here. The module will be
|
||||
called lan743x.
|
||||
|
||||
source "drivers/net/ethernet/microchip/sparx5/Kconfig"
|
||||
|
||||
endif # NET_VENDOR_MICROCHIP
|
||||
|
@ -8,3 +8,5 @@ obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o
|
||||
obj-$(CONFIG_LAN743X) += lan743x.o
|
||||
|
||||
lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o
|
||||
|
||||
obj-$(CONFIG_SPARX5_SWITCH) += sparx5/
|
||||
|
9
drivers/net/ethernet/microchip/sparx5/Kconfig
Normal file
9
drivers/net/ethernet/microchip/sparx5/Kconfig
Normal file
@ -0,0 +1,9 @@
|
||||
config SPARX5_SWITCH
|
||||
tristate "Sparx5 switch driver"
|
||||
depends on NET_SWITCHDEV
|
||||
depends on HAS_IOMEM
|
||||
select PHYLINK
|
||||
select PHY_SPARX5_SERDES
|
||||
select RESET_CONTROLLER
|
||||
help
|
||||
This driver supports the Sparx5 network switch device.
|
10
drivers/net/ethernet/microchip/sparx5/Makefile
Normal file
10
drivers/net/ethernet/microchip/sparx5/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Makefile for the Microchip Sparx5 network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
|
||||
|
||||
sparx5-switch-objs := sparx5_main.o sparx5_packet.o \
|
||||
sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \
|
||||
sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o
|
596
drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
Normal file
596
drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
Normal file
@ -0,0 +1,596 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
|
||||
/* QSYS calendar information */
|
||||
#define SPX5_PORTS_PER_CALREG 10 /* Ports mapped in a calendar register */
|
||||
#define SPX5_CALBITS_PER_PORT 3 /* Bit per port in calendar register */
|
||||
|
||||
/* DSM calendar information */
|
||||
#define SPX5_DSM_CAL_LEN 64
|
||||
#define SPX5_DSM_CAL_EMPTY 0xFFFF
|
||||
#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
|
||||
#define SPX5_DSM_CAL_TAXIS 8
|
||||
#define SPX5_DSM_CAL_BW_LOSS 553
|
||||
|
||||
#define SPX5_TAXI_PORT_MAX 70
|
||||
|
||||
#define SPEED_12500 12500
|
||||
|
||||
/* Maps from taxis to port numbers */
|
||||
static u32 sparx5_taxi_ports[SPX5_DSM_CAL_TAXIS][SPX5_DSM_CAL_MAX_DEVS_PER_TAXI] = {
|
||||
{57, 12, 0, 1, 2, 16, 17, 18, 19, 20, 21, 22, 23},
|
||||
{58, 13, 3, 4, 5, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||
{59, 14, 6, 7, 8, 32, 33, 34, 35, 36, 37, 38, 39},
|
||||
{60, 15, 9, 10, 11, 40, 41, 42, 43, 44, 45, 46, 47},
|
||||
{61, 48, 49, 50, 99, 99, 99, 99, 99, 99, 99, 99, 99},
|
||||
{62, 51, 52, 53, 99, 99, 99, 99, 99, 99, 99, 99, 99},
|
||||
{56, 63, 54, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99},
|
||||
{64, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
|
||||
};
|
||||
|
||||
struct sparx5_calendar_data {
|
||||
u32 schedule[SPX5_DSM_CAL_LEN];
|
||||
u32 avg_dist[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
|
||||
u32 taxi_ports[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
|
||||
u32 taxi_speeds[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
|
||||
u32 dev_slots[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
|
||||
u32 new_slots[SPX5_DSM_CAL_LEN];
|
||||
u32 temp_sched[SPX5_DSM_CAL_LEN];
|
||||
u32 indices[SPX5_DSM_CAL_LEN];
|
||||
u32 short_list[SPX5_DSM_CAL_LEN];
|
||||
u32 long_list[SPX5_DSM_CAL_LEN];
|
||||
};
|
||||
|
||||
static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
|
||||
{
|
||||
switch (sparx5->target_ct) {
|
||||
case SPX5_TARGET_CT_7546:
|
||||
case SPX5_TARGET_CT_7546TSN:
|
||||
return 65000;
|
||||
case SPX5_TARGET_CT_7549:
|
||||
case SPX5_TARGET_CT_7549TSN:
|
||||
return 91000;
|
||||
case SPX5_TARGET_CT_7552:
|
||||
case SPX5_TARGET_CT_7552TSN:
|
||||
return 129000;
|
||||
case SPX5_TARGET_CT_7556:
|
||||
case SPX5_TARGET_CT_7556TSN:
|
||||
return 161000;
|
||||
case SPX5_TARGET_CT_7558:
|
||||
case SPX5_TARGET_CT_7558TSN:
|
||||
return 201000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is used in calendar configuration */
|
||||
enum sparx5_cal_bw {
|
||||
SPX5_CAL_SPEED_NONE = 0,
|
||||
SPX5_CAL_SPEED_1G = 1,
|
||||
SPX5_CAL_SPEED_2G5 = 2,
|
||||
SPX5_CAL_SPEED_5G = 3,
|
||||
SPX5_CAL_SPEED_10G = 4,
|
||||
SPX5_CAL_SPEED_25G = 5,
|
||||
SPX5_CAL_SPEED_0G5 = 6,
|
||||
SPX5_CAL_SPEED_12G5 = 7
|
||||
};
|
||||
|
||||
static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
|
||||
{
|
||||
switch (cclock) {
|
||||
case SPX5_CORE_CLOCK_250MHZ: return 83000; /* 250000 / 3 */
|
||||
case SPX5_CORE_CLOCK_500MHZ: return 166000; /* 500000 / 3 */
|
||||
case SPX5_CORE_CLOCK_625MHZ: return 208000; /* 625000 / 3 */
|
||||
default: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
|
||||
{
|
||||
switch (speed) {
|
||||
case SPX5_CAL_SPEED_1G: return 1000;
|
||||
case SPX5_CAL_SPEED_2G5: return 2500;
|
||||
case SPX5_CAL_SPEED_5G: return 5000;
|
||||
case SPX5_CAL_SPEED_10G: return 10000;
|
||||
case SPX5_CAL_SPEED_25G: return 25000;
|
||||
case SPX5_CAL_SPEED_0G5: return 500;
|
||||
case SPX5_CAL_SPEED_12G5: return 12500;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 sparx5_bandwidth_to_calendar(u32 bw)
|
||||
{
|
||||
switch (bw) {
|
||||
case SPEED_10: return SPX5_CAL_SPEED_0G5;
|
||||
case SPEED_100: return SPX5_CAL_SPEED_0G5;
|
||||
case SPEED_1000: return SPX5_CAL_SPEED_1G;
|
||||
case SPEED_2500: return SPX5_CAL_SPEED_2G5;
|
||||
case SPEED_5000: return SPX5_CAL_SPEED_5G;
|
||||
case SPEED_10000: return SPX5_CAL_SPEED_10G;
|
||||
case SPEED_12500: return SPX5_CAL_SPEED_12G5;
|
||||
case SPEED_25000: return SPX5_CAL_SPEED_25G;
|
||||
case SPEED_UNKNOWN: return SPX5_CAL_SPEED_1G;
|
||||
default: return SPX5_CAL_SPEED_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
|
||||
u32 portno)
|
||||
{
|
||||
struct sparx5_port *port;
|
||||
|
||||
if (portno >= SPX5_PORTS) {
|
||||
/* Internal ports */
|
||||
if (portno == SPX5_PORT_CPU_0 || portno == SPX5_PORT_CPU_1) {
|
||||
/* Equals 1.25G */
|
||||
return SPX5_CAL_SPEED_2G5;
|
||||
} else if (portno == SPX5_PORT_VD0) {
|
||||
/* IPMC only idle BW */
|
||||
return SPX5_CAL_SPEED_NONE;
|
||||
} else if (portno == SPX5_PORT_VD1) {
|
||||
/* OAM only idle BW */
|
||||
return SPX5_CAL_SPEED_NONE;
|
||||
} else if (portno == SPX5_PORT_VD2) {
|
||||
/* IPinIP gets only idle BW */
|
||||
return SPX5_CAL_SPEED_NONE;
|
||||
}
|
||||
/* not in port map */
|
||||
return SPX5_CAL_SPEED_NONE;
|
||||
}
|
||||
/* Front ports - may be used */
|
||||
port = sparx5->ports[portno];
|
||||
if (!port)
|
||||
return SPX5_CAL_SPEED_NONE;
|
||||
return sparx5_bandwidth_to_calendar(port->conf.bandwidth);
|
||||
}
|
||||
|
||||
/* Auto configure the QSYS calendar based on port configuration */
|
||||
int sparx5_config_auto_calendar(struct sparx5 *sparx5)
|
||||
{
|
||||
u32 cal[7], value, idx, portno;
|
||||
u32 max_core_bw;
|
||||
u32 total_bw = 0, used_port_bw = 0;
|
||||
int err = 0;
|
||||
enum sparx5_cal_bw spd;
|
||||
|
||||
memset(cal, 0, sizeof(cal));
|
||||
|
||||
max_core_bw = sparx5_clk_to_bandwidth(sparx5->coreclock);
|
||||
if (max_core_bw == 0) {
|
||||
dev_err(sparx5->dev, "Core clock not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Setup the calendar with the bandwidth to each port */
|
||||
for (portno = 0; portno < SPX5_PORTS_ALL; portno++) {
|
||||
u64 reg, offset, this_bw;
|
||||
|
||||
spd = sparx5_get_port_cal_speed(sparx5, portno);
|
||||
if (spd == SPX5_CAL_SPEED_NONE)
|
||||
continue;
|
||||
|
||||
this_bw = sparx5_cal_speed_to_value(spd);
|
||||
if (portno < SPX5_PORTS)
|
||||
used_port_bw += this_bw;
|
||||
else
|
||||
/* Internal ports are granted half the value */
|
||||
this_bw = this_bw / 2;
|
||||
total_bw += this_bw;
|
||||
reg = portno;
|
||||
offset = do_div(reg, SPX5_PORTS_PER_CALREG);
|
||||
cal[reg] |= spd << (offset * SPX5_CALBITS_PER_PORT);
|
||||
}
|
||||
|
||||
if (used_port_bw > sparx5_target_bandwidth(sparx5)) {
|
||||
dev_err(sparx5->dev,
|
||||
"Port BW %u above target BW %u\n",
|
||||
used_port_bw, sparx5_target_bandwidth(sparx5));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (total_bw > max_core_bw) {
|
||||
dev_err(sparx5->dev,
|
||||
"Total BW %u above switch core BW %u\n",
|
||||
total_bw, max_core_bw);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Halt the calendar while changing it */
|
||||
spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
|
||||
QSYS_CAL_CTRL_CAL_MODE,
|
||||
sparx5, QSYS_CAL_CTRL);
|
||||
|
||||
/* Assign port bandwidth to auto calendar */
|
||||
for (idx = 0; idx < ARRAY_SIZE(cal); idx++)
|
||||
spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx));
|
||||
|
||||
/* Increase grant rate of all ports to account for
|
||||
* core clock ppm deviations
|
||||
*/
|
||||
spx5_rmw(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(671), /* 672->671 */
|
||||
QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE,
|
||||
sparx5,
|
||||
QSYS_CAL_CTRL);
|
||||
|
||||
/* Grant idle usage to VD 0-2 */
|
||||
for (idx = 2; idx < 5; idx++)
|
||||
spx5_wr(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_SET(12),
|
||||
sparx5,
|
||||
HSCH_OUTB_SHARE_ENA(idx));
|
||||
|
||||
/* Enable Auto mode */
|
||||
spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(8),
|
||||
QSYS_CAL_CTRL_CAL_MODE,
|
||||
sparx5, QSYS_CAL_CTRL);
|
||||
|
||||
/* Verify successful calendar config */
|
||||
value = spx5_rd(sparx5, QSYS_CAL_CTRL);
|
||||
if (QSYS_CAL_CTRL_CAL_AUTO_ERROR_GET(value)) {
|
||||
dev_err(sparx5->dev, "QSYS calendar error\n");
|
||||
err = -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static u32 sparx5_dsm_exb_gcd(u32 a, u32 b)
|
||||
{
|
||||
if (b == 0)
|
||||
return a;
|
||||
return sparx5_dsm_exb_gcd(b, a % b);
|
||||
}
|
||||
|
||||
static u32 sparx5_dsm_cal_len(u32 *cal)
|
||||
{
|
||||
u32 idx = 0, len = 0;
|
||||
|
||||
while (idx < SPX5_DSM_CAL_LEN) {
|
||||
if (cal[idx] != SPX5_DSM_CAL_EMPTY)
|
||||
len++;
|
||||
idx++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static u32 sparx5_dsm_cp_cal(u32 *sched)
|
||||
{
|
||||
u32 idx = 0, tmp;
|
||||
|
||||
while (idx < SPX5_DSM_CAL_LEN) {
|
||||
if (sched[idx] != SPX5_DSM_CAL_EMPTY) {
|
||||
tmp = sched[idx];
|
||||
sched[idx] = SPX5_DSM_CAL_EMPTY;
|
||||
return tmp;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return SPX5_DSM_CAL_EMPTY;
|
||||
}
|
||||
|
||||
static int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
|
||||
struct sparx5_calendar_data *data)
|
||||
{
|
||||
bool slow_mode;
|
||||
u32 gcd, idx, sum, min, factor;
|
||||
u32 num_of_slots, slot_spd, empty_slots;
|
||||
u32 taxi_bw, clk_period_ps;
|
||||
|
||||
clk_period_ps = sparx5_clk_period(sparx5->coreclock);
|
||||
taxi_bw = 128 * 1000000 / clk_period_ps;
|
||||
slow_mode = !!(clk_period_ps > 2000);
|
||||
memcpy(data->taxi_ports, &sparx5_taxi_ports[taxi],
|
||||
sizeof(data->taxi_ports));
|
||||
|
||||
for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
|
||||
data->new_slots[idx] = SPX5_DSM_CAL_EMPTY;
|
||||
data->schedule[idx] = SPX5_DSM_CAL_EMPTY;
|
||||
data->temp_sched[idx] = SPX5_DSM_CAL_EMPTY;
|
||||
}
|
||||
/* Default empty calendar */
|
||||
data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
|
||||
|
||||
/* Map ports to taxi positions */
|
||||
for (idx = 0; idx < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; idx++) {
|
||||
u32 portno = data->taxi_ports[idx];
|
||||
|
||||
if (portno < SPX5_TAXI_PORT_MAX) {
|
||||
data->taxi_speeds[idx] = sparx5_cal_speed_to_value
|
||||
(sparx5_get_port_cal_speed(sparx5, portno));
|
||||
} else {
|
||||
data->taxi_speeds[idx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sum = 0;
|
||||
min = 25000;
|
||||
for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
|
||||
u32 jdx;
|
||||
|
||||
sum += data->taxi_speeds[idx];
|
||||
if (data->taxi_speeds[idx] && data->taxi_speeds[idx] < min)
|
||||
min = data->taxi_speeds[idx];
|
||||
gcd = min;
|
||||
for (jdx = 0; jdx < ARRAY_SIZE(data->taxi_speeds); jdx++)
|
||||
gcd = sparx5_dsm_exb_gcd(gcd, data->taxi_speeds[jdx]);
|
||||
}
|
||||
if (sum == 0) /* Empty calendar */
|
||||
return 0;
|
||||
/* Make room for overhead traffic */
|
||||
factor = 100 * 100 * 1000 / (100 * 100 - SPX5_DSM_CAL_BW_LOSS);
|
||||
|
||||
if (sum * factor > (taxi_bw * 1000)) {
|
||||
dev_err(sparx5->dev,
|
||||
"Taxi %u, Requested BW %u above available BW %u\n",
|
||||
taxi, sum, taxi_bw);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (idx = 0; idx < 4; idx++) {
|
||||
u32 raw_spd;
|
||||
|
||||
if (idx == 0)
|
||||
raw_spd = gcd / 5;
|
||||
else if (idx == 1)
|
||||
raw_spd = gcd / 2;
|
||||
else if (idx == 2)
|
||||
raw_spd = gcd;
|
||||
else
|
||||
raw_spd = min;
|
||||
slot_spd = raw_spd * factor / 1000;
|
||||
num_of_slots = taxi_bw / slot_spd;
|
||||
if (num_of_slots <= 64)
|
||||
break;
|
||||
}
|
||||
|
||||
num_of_slots = num_of_slots > 64 ? 64 : num_of_slots;
|
||||
slot_spd = taxi_bw / num_of_slots;
|
||||
|
||||
sum = 0;
|
||||
for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
|
||||
u32 spd = data->taxi_speeds[idx];
|
||||
u32 adjusted_speed = data->taxi_speeds[idx] * factor / 1000;
|
||||
|
||||
if (adjusted_speed > 0) {
|
||||
data->avg_dist[idx] = (128 * 1000000 * 10) /
|
||||
(adjusted_speed * clk_period_ps);
|
||||
} else {
|
||||
data->avg_dist[idx] = -1;
|
||||
}
|
||||
data->dev_slots[idx] = ((spd * factor / slot_spd) + 999) / 1000;
|
||||
if (spd != 25000 && (spd != 10000 || !slow_mode)) {
|
||||
if (num_of_slots < (5 * data->dev_slots[idx])) {
|
||||
dev_err(sparx5->dev,
|
||||
"Taxi %u, speed %u, Low slot sep.\n",
|
||||
taxi, spd);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
sum += data->dev_slots[idx];
|
||||
if (sum > num_of_slots) {
|
||||
dev_err(sparx5->dev,
|
||||
"Taxi %u with overhead factor %u\n",
|
||||
taxi, factor);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
empty_slots = num_of_slots - sum;
|
||||
|
||||
for (idx = 0; idx < empty_slots; idx++)
|
||||
data->schedule[idx] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
|
||||
|
||||
for (idx = 1; idx < num_of_slots; idx++) {
|
||||
u32 indices_len = 0;
|
||||
u32 slot, jdx, kdx, ts;
|
||||
s32 cnt;
|
||||
u32 num_of_old_slots, num_of_new_slots, tgt_score;
|
||||
|
||||
for (slot = 0; slot < ARRAY_SIZE(data->dev_slots); slot++) {
|
||||
if (data->dev_slots[slot] == idx) {
|
||||
data->indices[indices_len] = slot;
|
||||
indices_len++;
|
||||
}
|
||||
}
|
||||
if (indices_len == 0)
|
||||
continue;
|
||||
kdx = 0;
|
||||
for (slot = 0; slot < idx; slot++) {
|
||||
for (jdx = 0; jdx < indices_len; jdx++, kdx++)
|
||||
data->new_slots[kdx] = data->indices[jdx];
|
||||
}
|
||||
|
||||
for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
|
||||
if (data->schedule[slot] == SPX5_DSM_CAL_EMPTY)
|
||||
break;
|
||||
}
|
||||
|
||||
num_of_old_slots = slot;
|
||||
num_of_new_slots = kdx;
|
||||
cnt = 0;
|
||||
ts = 0;
|
||||
|
||||
if (num_of_new_slots > num_of_old_slots) {
|
||||
memcpy(data->short_list, data->schedule,
|
||||
sizeof(data->short_list));
|
||||
memcpy(data->long_list, data->new_slots,
|
||||
sizeof(data->long_list));
|
||||
tgt_score = 100000 * num_of_old_slots /
|
||||
num_of_new_slots;
|
||||
} else {
|
||||
memcpy(data->short_list, data->new_slots,
|
||||
sizeof(data->short_list));
|
||||
memcpy(data->long_list, data->schedule,
|
||||
sizeof(data->long_list));
|
||||
tgt_score = 100000 * num_of_new_slots /
|
||||
num_of_old_slots;
|
||||
}
|
||||
|
||||
while (sparx5_dsm_cal_len(data->short_list) > 0 ||
|
||||
sparx5_dsm_cal_len(data->long_list) > 0) {
|
||||
u32 act = 0;
|
||||
|
||||
if (sparx5_dsm_cal_len(data->short_list) > 0) {
|
||||
data->temp_sched[ts] =
|
||||
sparx5_dsm_cp_cal(data->short_list);
|
||||
ts++;
|
||||
cnt += 100000;
|
||||
act = 1;
|
||||
}
|
||||
while (sparx5_dsm_cal_len(data->long_list) > 0 &&
|
||||
cnt > 0) {
|
||||
data->temp_sched[ts] =
|
||||
sparx5_dsm_cp_cal(data->long_list);
|
||||
ts++;
|
||||
cnt -= tgt_score;
|
||||
act = 1;
|
||||
}
|
||||
if (act == 0) {
|
||||
dev_err(sparx5->dev,
|
||||
"Error in DSM calendar calculation\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
|
||||
if (data->temp_sched[slot] == SPX5_DSM_CAL_EMPTY)
|
||||
break;
|
||||
}
|
||||
for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
|
||||
data->schedule[slot] = data->temp_sched[slot];
|
||||
data->temp_sched[slot] = SPX5_DSM_CAL_EMPTY;
|
||||
data->new_slots[slot] = SPX5_DSM_CAL_EMPTY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_dsm_calendar_check(struct sparx5 *sparx5,
|
||||
struct sparx5_calendar_data *data)
|
||||
{
|
||||
u32 num_of_slots, idx, port;
|
||||
int cnt, max_dist;
|
||||
u32 slot_indices[SPX5_DSM_CAL_LEN], distances[SPX5_DSM_CAL_LEN];
|
||||
u32 cal_length = sparx5_dsm_cal_len(data->schedule);
|
||||
|
||||
for (port = 0; port < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; port++) {
|
||||
num_of_slots = 0;
|
||||
max_dist = data->avg_dist[port];
|
||||
for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
|
||||
slot_indices[idx] = SPX5_DSM_CAL_EMPTY;
|
||||
distances[idx] = SPX5_DSM_CAL_EMPTY;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < cal_length; idx++) {
|
||||
if (data->schedule[idx] == port) {
|
||||
slot_indices[num_of_slots] = idx;
|
||||
num_of_slots++;
|
||||
}
|
||||
}
|
||||
|
||||
slot_indices[num_of_slots] = slot_indices[0] + cal_length;
|
||||
|
||||
for (idx = 0; idx < num_of_slots; idx++) {
|
||||
distances[idx] = (slot_indices[idx + 1] -
|
||||
slot_indices[idx]) * 10;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < num_of_slots; idx++) {
|
||||
u32 jdx, kdx;
|
||||
|
||||
cnt = distances[idx] - max_dist;
|
||||
if (cnt < 0)
|
||||
cnt = -cnt;
|
||||
kdx = 0;
|
||||
for (jdx = (idx + 1) % num_of_slots;
|
||||
jdx != idx;
|
||||
jdx = (jdx + 1) % num_of_slots, kdx++) {
|
||||
cnt = cnt + distances[jdx] - max_dist;
|
||||
if (cnt < 0)
|
||||
cnt = -cnt;
|
||||
if (cnt > max_dist)
|
||||
goto check_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
check_err:
|
||||
dev_err(sparx5->dev,
|
||||
"Port %u: distance %u above limit %d\n",
|
||||
port, cnt, max_dist);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi,
|
||||
struct sparx5_calendar_data *data)
|
||||
{
|
||||
u32 idx;
|
||||
u32 cal_len = sparx5_dsm_cal_len(data->schedule), len;
|
||||
|
||||
spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(1),
|
||||
sparx5,
|
||||
DSM_TAXI_CAL_CFG(taxi));
|
||||
for (idx = 0; idx < cal_len; idx++) {
|
||||
spx5_rmw(DSM_TAXI_CAL_CFG_CAL_IDX_SET(idx),
|
||||
DSM_TAXI_CAL_CFG_CAL_IDX,
|
||||
sparx5,
|
||||
DSM_TAXI_CAL_CFG(taxi));
|
||||
spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_VAL_SET(data->schedule[idx]),
|
||||
DSM_TAXI_CAL_CFG_CAL_PGM_VAL,
|
||||
sparx5,
|
||||
DSM_TAXI_CAL_CFG(taxi));
|
||||
}
|
||||
spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(0),
|
||||
sparx5,
|
||||
DSM_TAXI_CAL_CFG(taxi));
|
||||
len = DSM_TAXI_CAL_CFG_CAL_CUR_LEN_GET(spx5_rd(sparx5,
|
||||
DSM_TAXI_CAL_CFG(taxi)));
|
||||
if (len != cal_len - 1)
|
||||
goto update_err;
|
||||
return 0;
|
||||
update_err:
|
||||
dev_err(sparx5->dev, "Incorrect calendar length: %u\n", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Configure the DSM calendar based on port configuration */
|
||||
int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
|
||||
{
|
||||
int taxi;
|
||||
struct sparx5_calendar_data *data;
|
||||
int err = 0;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
for (taxi = 0; taxi < SPX5_DSM_CAL_TAXIS; ++taxi) {
|
||||
err = sparx5_dsm_calendar_calc(sparx5, taxi, data);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "DSM calendar calculation failed\n");
|
||||
goto cal_out;
|
||||
}
|
||||
err = sparx5_dsm_calendar_check(sparx5, data);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "DSM calendar check failed\n");
|
||||
goto cal_out;
|
||||
}
|
||||
err = sparx5_dsm_calendar_update(sparx5, taxi, data);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "DSM calendar update failed\n");
|
||||
goto cal_out;
|
||||
}
|
||||
}
|
||||
cal_out:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
1227
drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
Normal file
1227
drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
Normal file
File diff suppressed because it is too large
Load Diff
500
drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
Normal file
500
drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
Normal file
@ -0,0 +1,500 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <net/switchdev.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
|
||||
/* Commands for Mac Table Command register */
|
||||
#define MAC_CMD_LEARN 0 /* Insert (Learn) 1 entry */
|
||||
#define MAC_CMD_UNLEARN 1 /* Unlearn (Forget) 1 entry */
|
||||
#define MAC_CMD_LOOKUP 2 /* Look up 1 entry */
|
||||
#define MAC_CMD_READ 3 /* Read entry at Mac Table Index */
|
||||
#define MAC_CMD_WRITE 4 /* Write entry at Mac Table Index */
|
||||
#define MAC_CMD_SCAN 5 /* Scan (Age or find next) */
|
||||
#define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */
|
||||
#define MAC_CMD_CLEAR_ALL 7 /* Delete all entries in table */
|
||||
|
||||
/* Commands for MAC_ENTRY_ADDR_TYPE */
|
||||
#define MAC_ENTRY_ADDR_TYPE_UPSID_PN 0
|
||||
#define MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1
|
||||
#define MAC_ENTRY_ADDR_TYPE_GLAG 2
|
||||
#define MAC_ENTRY_ADDR_TYPE_MC_IDX 3
|
||||
|
||||
#define TABLE_UPDATE_SLEEP_US 10
|
||||
#define TABLE_UPDATE_TIMEOUT_US 100000
|
||||
|
||||
struct sparx5_mact_entry {
|
||||
struct list_head list;
|
||||
unsigned char mac[ETH_ALEN];
|
||||
u32 flags;
|
||||
#define MAC_ENT_ALIVE BIT(0)
|
||||
#define MAC_ENT_MOVED BIT(1)
|
||||
#define MAC_ENT_LOCK BIT(2)
|
||||
u16 vid;
|
||||
u16 port;
|
||||
};
|
||||
|
||||
static int sparx5_mact_get_status(struct sparx5 *sparx5)
|
||||
{
|
||||
return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
|
||||
}
|
||||
|
||||
static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readx_poll_timeout(sparx5_mact_get_status,
|
||||
sparx5, val,
|
||||
LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
|
||||
TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static void sparx5_mact_select(struct sparx5 *sparx5,
|
||||
const unsigned char mac[ETH_ALEN],
|
||||
u16 vid)
|
||||
{
|
||||
u32 macl = 0, mach = 0;
|
||||
|
||||
/* Set the MAC address to handle and the vlan associated in a format
|
||||
* understood by the hardware.
|
||||
*/
|
||||
mach |= vid << 16;
|
||||
mach |= mac[0] << 8;
|
||||
mach |= mac[1] << 0;
|
||||
macl |= mac[2] << 24;
|
||||
macl |= mac[3] << 16;
|
||||
macl |= mac[4] << 8;
|
||||
macl |= mac[5] << 0;
|
||||
|
||||
spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
|
||||
spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
|
||||
}
|
||||
|
||||
int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
|
||||
const unsigned char mac[ETH_ALEN], u16 vid)
|
||||
{
|
||||
int addr, type, ret;
|
||||
|
||||
if (pgid < SPX5_PORTS) {
|
||||
type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
|
||||
addr = pgid % 32;
|
||||
addr += (pgid / 32) << 5; /* Add upsid */
|
||||
} else {
|
||||
type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
|
||||
addr = pgid - SPX5_PORTS;
|
||||
}
|
||||
|
||||
mutex_lock(&sparx5->lock);
|
||||
|
||||
sparx5_mact_select(sparx5, mac, vid);
|
||||
|
||||
/* MAC entry properties */
|
||||
spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |
|
||||
LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |
|
||||
LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |
|
||||
LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),
|
||||
sparx5, LRN_MAC_ACCESS_CFG_2);
|
||||
spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);
|
||||
|
||||
/* Insert/learn new entry */
|
||||
spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |
|
||||
LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
|
||||
sparx5, LRN_COMMON_ACCESS_CTRL);
|
||||
|
||||
ret = sparx5_mact_wait_for_completion(sparx5);
|
||||
|
||||
mutex_unlock(&sparx5->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
|
||||
return sparx5_mact_forget(sparx5, addr, port->pvid);
|
||||
}
|
||||
|
||||
int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
|
||||
return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
|
||||
}
|
||||
|
||||
static int sparx5_mact_get(struct sparx5 *sparx5,
|
||||
unsigned char mac[ETH_ALEN],
|
||||
u16 *vid, u32 *pcfg2)
|
||||
{
|
||||
u32 mach, macl, cfg2;
|
||||
int ret = -ENOENT;
|
||||
|
||||
cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
|
||||
if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {
|
||||
mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);
|
||||
macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);
|
||||
mac[0] = ((mach >> 8) & 0xff);
|
||||
mac[1] = ((mach >> 0) & 0xff);
|
||||
mac[2] = ((macl >> 24) & 0xff);
|
||||
mac[3] = ((macl >> 16) & 0xff);
|
||||
mac[4] = ((macl >> 8) & 0xff);
|
||||
mac[5] = ((macl >> 0) & 0xff);
|
||||
*vid = mach >> 16;
|
||||
*pcfg2 = cfg2;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool sparx5_mact_getnext(struct sparx5 *sparx5,
|
||||
unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
|
||||
{
|
||||
u32 cfg2;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sparx5->lock);
|
||||
|
||||
sparx5_mact_select(sparx5, mac, *vid);
|
||||
|
||||
spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |
|
||||
LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
|
||||
sparx5, LRN_SCAN_NEXT_CFG);
|
||||
spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
|
||||
(MAC_CMD_FIND_SMALLEST) |
|
||||
LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
|
||||
sparx5, LRN_COMMON_ACCESS_CTRL);
|
||||
|
||||
ret = sparx5_mact_wait_for_completion(sparx5);
|
||||
if (ret == 0) {
|
||||
ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);
|
||||
if (ret == 0)
|
||||
*pcfg2 = cfg2;
|
||||
}
|
||||
|
||||
mutex_unlock(&sparx5->lock);
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
static int sparx5_mact_lookup(struct sparx5 *sparx5,
|
||||
const unsigned char mac[ETH_ALEN],
|
||||
u16 vid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sparx5->lock);
|
||||
|
||||
sparx5_mact_select(sparx5, mac, vid);
|
||||
|
||||
/* Issue a lookup command */
|
||||
spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |
|
||||
LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
|
||||
sparx5, LRN_COMMON_ACCESS_CTRL);
|
||||
|
||||
ret = sparx5_mact_wait_for_completion(sparx5);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET
|
||||
(spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2));
|
||||
|
||||
out:
|
||||
mutex_unlock(&sparx5->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sparx5_mact_forget(struct sparx5 *sparx5,
|
||||
const unsigned char mac[ETH_ALEN], u16 vid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sparx5->lock);
|
||||
|
||||
sparx5_mact_select(sparx5, mac, vid);
|
||||
|
||||
/* Issue an unlearn command */
|
||||
spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |
|
||||
LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
|
||||
sparx5, LRN_COMMON_ACCESS_CTRL);
|
||||
|
||||
ret = sparx5_mact_wait_for_completion(sparx5);
|
||||
|
||||
mutex_unlock(&sparx5->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
|
||||
const unsigned char *mac,
|
||||
u16 vid, u16 port_index)
|
||||
{
|
||||
struct sparx5_mact_entry *mact_entry;
|
||||
|
||||
mact_entry = devm_kzalloc(sparx5->dev,
|
||||
sizeof(*mact_entry), GFP_ATOMIC);
|
||||
if (!mact_entry)
|
||||
return NULL;
|
||||
|
||||
memcpy(mact_entry->mac, mac, ETH_ALEN);
|
||||
mact_entry->vid = vid;
|
||||
mact_entry->port = port_index;
|
||||
return mact_entry;
|
||||
}
|
||||
|
||||
static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
|
||||
const unsigned char *mac,
|
||||
u16 vid, u16 port_index)
|
||||
{
|
||||
struct sparx5_mact_entry *mact_entry;
|
||||
struct sparx5_mact_entry *res = NULL;
|
||||
|
||||
mutex_lock(&sparx5->mact_lock);
|
||||
list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
|
||||
if (mact_entry->vid == vid &&
|
||||
ether_addr_equal(mac, mact_entry->mac) &&
|
||||
mact_entry->port == port_index) {
|
||||
res = mact_entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&sparx5->mact_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
|
||||
const char *mac, u16 vid,
|
||||
struct net_device *dev, bool offloaded)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info info;
|
||||
|
||||
info.addr = mac;
|
||||
info.vid = vid;
|
||||
info.offloaded = offloaded;
|
||||
call_switchdev_notifiers(type, dev, &info.info, NULL);
|
||||
}
|
||||
|
||||
int sparx5_add_mact_entry(struct sparx5 *sparx5,
|
||||
struct sparx5_port *port,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct sparx5_mact_entry *mact_entry;
|
||||
int ret;
|
||||
|
||||
ret = sparx5_mact_lookup(sparx5, addr, vid);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
/* In case the entry already exists, don't add it again to SW,
|
||||
* just update HW, but we need to look in the actual HW because
|
||||
* it is possible for an entry to be learn by HW and before the
|
||||
* mact thread to start the frame will reach CPU and the CPU will
|
||||
* add the entry but without the extern_learn flag.
|
||||
*/
|
||||
mact_entry = find_mact_entry(sparx5, addr, vid, port->portno);
|
||||
if (mact_entry)
|
||||
goto update_hw;
|
||||
|
||||
/* Add the entry in SW MAC table not to get the notification when
|
||||
* SW is pulling again
|
||||
*/
|
||||
mact_entry = alloc_mact_entry(sparx5, addr, vid, port->portno);
|
||||
if (!mact_entry)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&sparx5->mact_lock);
|
||||
list_add_tail(&mact_entry->list, &sparx5->mact_entries);
|
||||
mutex_unlock(&sparx5->mact_lock);
|
||||
|
||||
update_hw:
|
||||
ret = sparx5_mact_learn(sparx5, port->portno, addr, vid);
|
||||
|
||||
/* New entry? */
|
||||
if (mact_entry->flags == 0) {
|
||||
mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */
|
||||
sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid,
|
||||
port->ndev, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sparx5_del_mact_entry(struct sparx5 *sparx5,
|
||||
const unsigned char *addr,
|
||||
u16 vid)
|
||||
{
|
||||
struct sparx5_mact_entry *mact_entry, *tmp;
|
||||
|
||||
/* Delete the entry in SW MAC table not to get the notification when
|
||||
* SW is pulling again
|
||||
*/
|
||||
mutex_lock(&sparx5->mact_lock);
|
||||
list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
|
||||
list) {
|
||||
if ((vid == 0 || mact_entry->vid == vid) &&
|
||||
ether_addr_equal(addr, mact_entry->mac)) {
|
||||
list_del(&mact_entry->list);
|
||||
devm_kfree(sparx5->dev, mact_entry);
|
||||
|
||||
sparx5_mact_forget(sparx5, addr, mact_entry->vid);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&sparx5->mact_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
|
||||
unsigned char mac[ETH_ALEN],
|
||||
u16 vid, u32 cfg2)
|
||||
{
|
||||
struct sparx5_mact_entry *mact_entry;
|
||||
bool found = false;
|
||||
u16 port;
|
||||
|
||||
if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
|
||||
MAC_ENTRY_ADDR_TYPE_UPSID_PN)
|
||||
return;
|
||||
|
||||
port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
|
||||
if (port >= SPX5_PORTS)
|
||||
return;
|
||||
|
||||
if (!test_bit(port, sparx5->bridge_mask))
|
||||
return;
|
||||
|
||||
mutex_lock(&sparx5->mact_lock);
|
||||
list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
|
||||
if (mact_entry->vid == vid &&
|
||||
ether_addr_equal(mac, mact_entry->mac)) {
|
||||
found = true;
|
||||
mact_entry->flags |= MAC_ENT_ALIVE;
|
||||
if (mact_entry->port != port) {
|
||||
dev_warn(sparx5->dev, "Entry move: %d -> %d\n",
|
||||
mact_entry->port, port);
|
||||
mact_entry->port = port;
|
||||
mact_entry->flags |= MAC_ENT_MOVED;
|
||||
}
|
||||
/* Entry handled */
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&sparx5->mact_lock);
|
||||
|
||||
if (found && !(mact_entry->flags & MAC_ENT_MOVED))
|
||||
/* Present, not moved */
|
||||
return;
|
||||
|
||||
if (!found) {
|
||||
/* Entry not found - now add */
|
||||
mact_entry = alloc_mact_entry(sparx5, mac, vid, port);
|
||||
if (!mact_entry)
|
||||
return;
|
||||
|
||||
mact_entry->flags |= MAC_ENT_ALIVE;
|
||||
mutex_lock(&sparx5->mact_lock);
|
||||
list_add_tail(&mact_entry->list, &sparx5->mact_entries);
|
||||
mutex_unlock(&sparx5->mact_lock);
|
||||
}
|
||||
|
||||
/* New or moved entry - notify bridge */
|
||||
sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
|
||||
mac, vid, sparx5->ports[port]->ndev,
|
||||
true);
|
||||
}
|
||||
|
||||
void sparx5_mact_pull_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *del_work = to_delayed_work(work);
|
||||
struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
|
||||
mact_work);
|
||||
struct sparx5_mact_entry *mact_entry, *tmp;
|
||||
unsigned char mac[ETH_ALEN];
|
||||
u32 cfg2;
|
||||
u16 vid;
|
||||
int ret;
|
||||
|
||||
/* Reset MAC entry flags */
|
||||
mutex_lock(&sparx5->mact_lock);
|
||||
list_for_each_entry(mact_entry, &sparx5->mact_entries, list)
|
||||
mact_entry->flags &= MAC_ENT_LOCK;
|
||||
mutex_unlock(&sparx5->mact_lock);
|
||||
|
||||
/* MAIN mac address processing loop */
|
||||
vid = 0;
|
||||
memset(mac, 0, sizeof(mac));
|
||||
do {
|
||||
mutex_lock(&sparx5->lock);
|
||||
sparx5_mact_select(sparx5, mac, vid);
|
||||
spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
|
||||
sparx5, LRN_SCAN_NEXT_CFG);
|
||||
spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
|
||||
(MAC_CMD_FIND_SMALLEST) |
|
||||
LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
|
||||
sparx5, LRN_COMMON_ACCESS_CTRL);
|
||||
ret = sparx5_mact_wait_for_completion(sparx5);
|
||||
if (ret == 0)
|
||||
ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);
|
||||
mutex_unlock(&sparx5->lock);
|
||||
if (ret == 0)
|
||||
sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);
|
||||
} while (ret == 0);
|
||||
|
||||
mutex_lock(&sparx5->mact_lock);
|
||||
list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
|
||||
list) {
|
||||
/* If the entry is in HW or permanent, then skip */
|
||||
if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))
|
||||
continue;
|
||||
|
||||
sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
|
||||
mact_entry->mac, mact_entry->vid,
|
||||
sparx5->ports[mact_entry->port]->ndev,
|
||||
true);
|
||||
|
||||
list_del(&mact_entry->list);
|
||||
devm_kfree(sparx5->dev, mact_entry);
|
||||
}
|
||||
mutex_unlock(&sparx5->mact_lock);
|
||||
|
||||
queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
|
||||
SPX5_MACT_PULL_DELAY);
|
||||
}
|
||||
|
||||
void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
|
||||
{
|
||||
int value = max(1, msecs / 10); /* unit 10 ms */
|
||||
|
||||
spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */
|
||||
LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */
|
||||
LRN_AUTOAGE_CFG_UNIT_SIZE |
|
||||
LRN_AUTOAGE_CFG_PERIOD_VAL,
|
||||
sparx5,
|
||||
LRN_AUTOAGE_CFG(0));
|
||||
}
|
||||
|
||||
void sparx5_mact_init(struct sparx5 *sparx5)
|
||||
{
|
||||
mutex_init(&sparx5->lock);
|
||||
|
||||
/* Flush MAC table */
|
||||
spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
|
||||
LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
|
||||
sparx5, LRN_COMMON_ACCESS_CTRL);
|
||||
|
||||
if (sparx5_mact_wait_for_completion(sparx5) != 0)
|
||||
dev_warn(sparx5->dev, "MAC flush error\n");
|
||||
|
||||
sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);
|
||||
}
|
852
drivers/net/ethernet/microchip/sparx5/sparx5_main.c
Normal file
852
drivers/net/ethernet/microchip/sparx5/sparx5_main.c
Normal file
@ -0,0 +1,852 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*
|
||||
* The Sparx5 Chip Register Model can be browsed at this location:
|
||||
* https://github.com/microchip-ung/sparx-5_reginfo
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <net/switchdev.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
#include "sparx5_port.h"
|
||||
|
||||
#define QLIM_WM(fraction) \
|
||||
((SPX5_BUFFER_MEMORY / SPX5_BUFFER_CELL_SZ - 100) * (fraction) / 100)
|
||||
#define IO_RANGES 3
|
||||
|
||||
struct initial_port_config {
|
||||
u32 portno;
|
||||
struct device_node *node;
|
||||
struct sparx5_port_config conf;
|
||||
struct phy *serdes;
|
||||
};
|
||||
|
||||
struct sparx5_ram_config {
|
||||
void __iomem *init_reg;
|
||||
u32 init_val;
|
||||
};
|
||||
|
||||
struct sparx5_main_io_resource {
|
||||
enum sparx5_target id;
|
||||
phys_addr_t offset;
|
||||
int range;
|
||||
};
|
||||
|
||||
static const struct sparx5_main_io_resource sparx5_main_iomap[] = {
|
||||
{ TARGET_CPU, 0, 0 }, /* 0x600000000 */
|
||||
{ TARGET_FDMA, 0x80000, 0 }, /* 0x600080000 */
|
||||
{ TARGET_PCEP, 0x400000, 0 }, /* 0x600400000 */
|
||||
{ TARGET_DEV2G5, 0x10004000, 1 }, /* 0x610004000 */
|
||||
{ TARGET_DEV5G, 0x10008000, 1 }, /* 0x610008000 */
|
||||
{ TARGET_PCS5G_BR, 0x1000c000, 1 }, /* 0x61000c000 */
|
||||
{ TARGET_DEV2G5 + 1, 0x10010000, 1 }, /* 0x610010000 */
|
||||
{ TARGET_DEV5G + 1, 0x10014000, 1 }, /* 0x610014000 */
|
||||
{ TARGET_PCS5G_BR + 1, 0x10018000, 1 }, /* 0x610018000 */
|
||||
{ TARGET_DEV2G5 + 2, 0x1001c000, 1 }, /* 0x61001c000 */
|
||||
{ TARGET_DEV5G + 2, 0x10020000, 1 }, /* 0x610020000 */
|
||||
{ TARGET_PCS5G_BR + 2, 0x10024000, 1 }, /* 0x610024000 */
|
||||
{ TARGET_DEV2G5 + 6, 0x10028000, 1 }, /* 0x610028000 */
|
||||
{ TARGET_DEV5G + 6, 0x1002c000, 1 }, /* 0x61002c000 */
|
||||
{ TARGET_PCS5G_BR + 6, 0x10030000, 1 }, /* 0x610030000 */
|
||||
{ TARGET_DEV2G5 + 7, 0x10034000, 1 }, /* 0x610034000 */
|
||||
{ TARGET_DEV5G + 7, 0x10038000, 1 }, /* 0x610038000 */
|
||||
{ TARGET_PCS5G_BR + 7, 0x1003c000, 1 }, /* 0x61003c000 */
|
||||
{ TARGET_DEV2G5 + 8, 0x10040000, 1 }, /* 0x610040000 */
|
||||
{ TARGET_DEV5G + 8, 0x10044000, 1 }, /* 0x610044000 */
|
||||
{ TARGET_PCS5G_BR + 8, 0x10048000, 1 }, /* 0x610048000 */
|
||||
{ TARGET_DEV2G5 + 9, 0x1004c000, 1 }, /* 0x61004c000 */
|
||||
{ TARGET_DEV5G + 9, 0x10050000, 1 }, /* 0x610050000 */
|
||||
{ TARGET_PCS5G_BR + 9, 0x10054000, 1 }, /* 0x610054000 */
|
||||
{ TARGET_DEV2G5 + 10, 0x10058000, 1 }, /* 0x610058000 */
|
||||
{ TARGET_DEV5G + 10, 0x1005c000, 1 }, /* 0x61005c000 */
|
||||
{ TARGET_PCS5G_BR + 10, 0x10060000, 1 }, /* 0x610060000 */
|
||||
{ TARGET_DEV2G5 + 11, 0x10064000, 1 }, /* 0x610064000 */
|
||||
{ TARGET_DEV5G + 11, 0x10068000, 1 }, /* 0x610068000 */
|
||||
{ TARGET_PCS5G_BR + 11, 0x1006c000, 1 }, /* 0x61006c000 */
|
||||
{ TARGET_DEV2G5 + 12, 0x10070000, 1 }, /* 0x610070000 */
|
||||
{ TARGET_DEV10G, 0x10074000, 1 }, /* 0x610074000 */
|
||||
{ TARGET_PCS10G_BR, 0x10078000, 1 }, /* 0x610078000 */
|
||||
{ TARGET_DEV2G5 + 14, 0x1007c000, 1 }, /* 0x61007c000 */
|
||||
{ TARGET_DEV10G + 2, 0x10080000, 1 }, /* 0x610080000 */
|
||||
{ TARGET_PCS10G_BR + 2, 0x10084000, 1 }, /* 0x610084000 */
|
||||
{ TARGET_DEV2G5 + 15, 0x10088000, 1 }, /* 0x610088000 */
|
||||
{ TARGET_DEV10G + 3, 0x1008c000, 1 }, /* 0x61008c000 */
|
||||
{ TARGET_PCS10G_BR + 3, 0x10090000, 1 }, /* 0x610090000 */
|
||||
{ TARGET_DEV2G5 + 16, 0x10094000, 1 }, /* 0x610094000 */
|
||||
{ TARGET_DEV2G5 + 17, 0x10098000, 1 }, /* 0x610098000 */
|
||||
{ TARGET_DEV2G5 + 18, 0x1009c000, 1 }, /* 0x61009c000 */
|
||||
{ TARGET_DEV2G5 + 19, 0x100a0000, 1 }, /* 0x6100a0000 */
|
||||
{ TARGET_DEV2G5 + 20, 0x100a4000, 1 }, /* 0x6100a4000 */
|
||||
{ TARGET_DEV2G5 + 21, 0x100a8000, 1 }, /* 0x6100a8000 */
|
||||
{ TARGET_DEV2G5 + 22, 0x100ac000, 1 }, /* 0x6100ac000 */
|
||||
{ TARGET_DEV2G5 + 23, 0x100b0000, 1 }, /* 0x6100b0000 */
|
||||
{ TARGET_DEV2G5 + 32, 0x100b4000, 1 }, /* 0x6100b4000 */
|
||||
{ TARGET_DEV2G5 + 33, 0x100b8000, 1 }, /* 0x6100b8000 */
|
||||
{ TARGET_DEV2G5 + 34, 0x100bc000, 1 }, /* 0x6100bc000 */
|
||||
{ TARGET_DEV2G5 + 35, 0x100c0000, 1 }, /* 0x6100c0000 */
|
||||
{ TARGET_DEV2G5 + 36, 0x100c4000, 1 }, /* 0x6100c4000 */
|
||||
{ TARGET_DEV2G5 + 37, 0x100c8000, 1 }, /* 0x6100c8000 */
|
||||
{ TARGET_DEV2G5 + 38, 0x100cc000, 1 }, /* 0x6100cc000 */
|
||||
{ TARGET_DEV2G5 + 39, 0x100d0000, 1 }, /* 0x6100d0000 */
|
||||
{ TARGET_DEV2G5 + 40, 0x100d4000, 1 }, /* 0x6100d4000 */
|
||||
{ TARGET_DEV2G5 + 41, 0x100d8000, 1 }, /* 0x6100d8000 */
|
||||
{ TARGET_DEV2G5 + 42, 0x100dc000, 1 }, /* 0x6100dc000 */
|
||||
{ TARGET_DEV2G5 + 43, 0x100e0000, 1 }, /* 0x6100e0000 */
|
||||
{ TARGET_DEV2G5 + 44, 0x100e4000, 1 }, /* 0x6100e4000 */
|
||||
{ TARGET_DEV2G5 + 45, 0x100e8000, 1 }, /* 0x6100e8000 */
|
||||
{ TARGET_DEV2G5 + 46, 0x100ec000, 1 }, /* 0x6100ec000 */
|
||||
{ TARGET_DEV2G5 + 47, 0x100f0000, 1 }, /* 0x6100f0000 */
|
||||
{ TARGET_DEV2G5 + 57, 0x100f4000, 1 }, /* 0x6100f4000 */
|
||||
{ TARGET_DEV25G + 1, 0x100f8000, 1 }, /* 0x6100f8000 */
|
||||
{ TARGET_PCS25G_BR + 1, 0x100fc000, 1 }, /* 0x6100fc000 */
|
||||
{ TARGET_DEV2G5 + 59, 0x10104000, 1 }, /* 0x610104000 */
|
||||
{ TARGET_DEV25G + 3, 0x10108000, 1 }, /* 0x610108000 */
|
||||
{ TARGET_PCS25G_BR + 3, 0x1010c000, 1 }, /* 0x61010c000 */
|
||||
{ TARGET_DEV2G5 + 60, 0x10114000, 1 }, /* 0x610114000 */
|
||||
{ TARGET_DEV25G + 4, 0x10118000, 1 }, /* 0x610118000 */
|
||||
{ TARGET_PCS25G_BR + 4, 0x1011c000, 1 }, /* 0x61011c000 */
|
||||
{ TARGET_DEV2G5 + 64, 0x10124000, 1 }, /* 0x610124000 */
|
||||
{ TARGET_DEV5G + 12, 0x10128000, 1 }, /* 0x610128000 */
|
||||
{ TARGET_PCS5G_BR + 12, 0x1012c000, 1 }, /* 0x61012c000 */
|
||||
{ TARGET_PORT_CONF, 0x10130000, 1 }, /* 0x610130000 */
|
||||
{ TARGET_DEV2G5 + 3, 0x10404000, 1 }, /* 0x610404000 */
|
||||
{ TARGET_DEV5G + 3, 0x10408000, 1 }, /* 0x610408000 */
|
||||
{ TARGET_PCS5G_BR + 3, 0x1040c000, 1 }, /* 0x61040c000 */
|
||||
{ TARGET_DEV2G5 + 4, 0x10410000, 1 }, /* 0x610410000 */
|
||||
{ TARGET_DEV5G + 4, 0x10414000, 1 }, /* 0x610414000 */
|
||||
{ TARGET_PCS5G_BR + 4, 0x10418000, 1 }, /* 0x610418000 */
|
||||
{ TARGET_DEV2G5 + 5, 0x1041c000, 1 }, /* 0x61041c000 */
|
||||
{ TARGET_DEV5G + 5, 0x10420000, 1 }, /* 0x610420000 */
|
||||
{ TARGET_PCS5G_BR + 5, 0x10424000, 1 }, /* 0x610424000 */
|
||||
{ TARGET_DEV2G5 + 13, 0x10428000, 1 }, /* 0x610428000 */
|
||||
{ TARGET_DEV10G + 1, 0x1042c000, 1 }, /* 0x61042c000 */
|
||||
{ TARGET_PCS10G_BR + 1, 0x10430000, 1 }, /* 0x610430000 */
|
||||
{ TARGET_DEV2G5 + 24, 0x10434000, 1 }, /* 0x610434000 */
|
||||
{ TARGET_DEV2G5 + 25, 0x10438000, 1 }, /* 0x610438000 */
|
||||
{ TARGET_DEV2G5 + 26, 0x1043c000, 1 }, /* 0x61043c000 */
|
||||
{ TARGET_DEV2G5 + 27, 0x10440000, 1 }, /* 0x610440000 */
|
||||
{ TARGET_DEV2G5 + 28, 0x10444000, 1 }, /* 0x610444000 */
|
||||
{ TARGET_DEV2G5 + 29, 0x10448000, 1 }, /* 0x610448000 */
|
||||
{ TARGET_DEV2G5 + 30, 0x1044c000, 1 }, /* 0x61044c000 */
|
||||
{ TARGET_DEV2G5 + 31, 0x10450000, 1 }, /* 0x610450000 */
|
||||
{ TARGET_DEV2G5 + 48, 0x10454000, 1 }, /* 0x610454000 */
|
||||
{ TARGET_DEV10G + 4, 0x10458000, 1 }, /* 0x610458000 */
|
||||
{ TARGET_PCS10G_BR + 4, 0x1045c000, 1 }, /* 0x61045c000 */
|
||||
{ TARGET_DEV2G5 + 49, 0x10460000, 1 }, /* 0x610460000 */
|
||||
{ TARGET_DEV10G + 5, 0x10464000, 1 }, /* 0x610464000 */
|
||||
{ TARGET_PCS10G_BR + 5, 0x10468000, 1 }, /* 0x610468000 */
|
||||
{ TARGET_DEV2G5 + 50, 0x1046c000, 1 }, /* 0x61046c000 */
|
||||
{ TARGET_DEV10G + 6, 0x10470000, 1 }, /* 0x610470000 */
|
||||
{ TARGET_PCS10G_BR + 6, 0x10474000, 1 }, /* 0x610474000 */
|
||||
{ TARGET_DEV2G5 + 51, 0x10478000, 1 }, /* 0x610478000 */
|
||||
{ TARGET_DEV10G + 7, 0x1047c000, 1 }, /* 0x61047c000 */
|
||||
{ TARGET_PCS10G_BR + 7, 0x10480000, 1 }, /* 0x610480000 */
|
||||
{ TARGET_DEV2G5 + 52, 0x10484000, 1 }, /* 0x610484000 */
|
||||
{ TARGET_DEV10G + 8, 0x10488000, 1 }, /* 0x610488000 */
|
||||
{ TARGET_PCS10G_BR + 8, 0x1048c000, 1 }, /* 0x61048c000 */
|
||||
{ TARGET_DEV2G5 + 53, 0x10490000, 1 }, /* 0x610490000 */
|
||||
{ TARGET_DEV10G + 9, 0x10494000, 1 }, /* 0x610494000 */
|
||||
{ TARGET_PCS10G_BR + 9, 0x10498000, 1 }, /* 0x610498000 */
|
||||
{ TARGET_DEV2G5 + 54, 0x1049c000, 1 }, /* 0x61049c000 */
|
||||
{ TARGET_DEV10G + 10, 0x104a0000, 1 }, /* 0x6104a0000 */
|
||||
{ TARGET_PCS10G_BR + 10, 0x104a4000, 1 }, /* 0x6104a4000 */
|
||||
{ TARGET_DEV2G5 + 55, 0x104a8000, 1 }, /* 0x6104a8000 */
|
||||
{ TARGET_DEV10G + 11, 0x104ac000, 1 }, /* 0x6104ac000 */
|
||||
{ TARGET_PCS10G_BR + 11, 0x104b0000, 1 }, /* 0x6104b0000 */
|
||||
{ TARGET_DEV2G5 + 56, 0x104b4000, 1 }, /* 0x6104b4000 */
|
||||
{ TARGET_DEV25G, 0x104b8000, 1 }, /* 0x6104b8000 */
|
||||
{ TARGET_PCS25G_BR, 0x104bc000, 1 }, /* 0x6104bc000 */
|
||||
{ TARGET_DEV2G5 + 58, 0x104c4000, 1 }, /* 0x6104c4000 */
|
||||
{ TARGET_DEV25G + 2, 0x104c8000, 1 }, /* 0x6104c8000 */
|
||||
{ TARGET_PCS25G_BR + 2, 0x104cc000, 1 }, /* 0x6104cc000 */
|
||||
{ TARGET_DEV2G5 + 61, 0x104d4000, 1 }, /* 0x6104d4000 */
|
||||
{ TARGET_DEV25G + 5, 0x104d8000, 1 }, /* 0x6104d8000 */
|
||||
{ TARGET_PCS25G_BR + 5, 0x104dc000, 1 }, /* 0x6104dc000 */
|
||||
{ TARGET_DEV2G5 + 62, 0x104e4000, 1 }, /* 0x6104e4000 */
|
||||
{ TARGET_DEV25G + 6, 0x104e8000, 1 }, /* 0x6104e8000 */
|
||||
{ TARGET_PCS25G_BR + 6, 0x104ec000, 1 }, /* 0x6104ec000 */
|
||||
{ TARGET_DEV2G5 + 63, 0x104f4000, 1 }, /* 0x6104f4000 */
|
||||
{ TARGET_DEV25G + 7, 0x104f8000, 1 }, /* 0x6104f8000 */
|
||||
{ TARGET_PCS25G_BR + 7, 0x104fc000, 1 }, /* 0x6104fc000 */
|
||||
{ TARGET_DSM, 0x10504000, 1 }, /* 0x610504000 */
|
||||
{ TARGET_ASM, 0x10600000, 1 }, /* 0x610600000 */
|
||||
{ TARGET_GCB, 0x11010000, 2 }, /* 0x611010000 */
|
||||
{ TARGET_QS, 0x11030000, 2 }, /* 0x611030000 */
|
||||
{ TARGET_ANA_ACL, 0x11050000, 2 }, /* 0x611050000 */
|
||||
{ TARGET_LRN, 0x11060000, 2 }, /* 0x611060000 */
|
||||
{ TARGET_VCAP_SUPER, 0x11080000, 2 }, /* 0x611080000 */
|
||||
{ TARGET_QSYS, 0x110a0000, 2 }, /* 0x6110a0000 */
|
||||
{ TARGET_QFWD, 0x110b0000, 2 }, /* 0x6110b0000 */
|
||||
{ TARGET_XQS, 0x110c0000, 2 }, /* 0x6110c0000 */
|
||||
{ TARGET_CLKGEN, 0x11100000, 2 }, /* 0x611100000 */
|
||||
{ TARGET_ANA_AC_POL, 0x11200000, 2 }, /* 0x611200000 */
|
||||
{ TARGET_QRES, 0x11280000, 2 }, /* 0x611280000 */
|
||||
{ TARGET_EACL, 0x112c0000, 2 }, /* 0x6112c0000 */
|
||||
{ TARGET_ANA_CL, 0x11400000, 2 }, /* 0x611400000 */
|
||||
{ TARGET_ANA_L3, 0x11480000, 2 }, /* 0x611480000 */
|
||||
{ TARGET_HSCH, 0x11580000, 2 }, /* 0x611580000 */
|
||||
{ TARGET_REW, 0x11600000, 2 }, /* 0x611600000 */
|
||||
{ TARGET_ANA_L2, 0x11800000, 2 }, /* 0x611800000 */
|
||||
{ TARGET_ANA_AC, 0x11900000, 2 }, /* 0x611900000 */
|
||||
{ TARGET_VOP, 0x11a00000, 2 }, /* 0x611a00000 */
|
||||
};
|
||||
|
||||
static int sparx5_create_targets(struct sparx5 *sparx5)
|
||||
{
|
||||
struct resource *iores[IO_RANGES];
|
||||
void __iomem *iomem[IO_RANGES];
|
||||
void __iomem *begin[IO_RANGES];
|
||||
int range_id[IO_RANGES];
|
||||
int idx, jdx;
|
||||
|
||||
for (idx = 0, jdx = 0; jdx < ARRAY_SIZE(sparx5_main_iomap); jdx++) {
|
||||
const struct sparx5_main_io_resource *iomap = &sparx5_main_iomap[jdx];
|
||||
|
||||
if (idx == iomap->range) {
|
||||
range_id[idx] = jdx;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
for (idx = 0; idx < IO_RANGES; idx++) {
|
||||
iores[idx] = platform_get_resource(sparx5->pdev, IORESOURCE_MEM,
|
||||
idx);
|
||||
iomem[idx] = devm_ioremap(sparx5->dev,
|
||||
iores[idx]->start,
|
||||
iores[idx]->end - iores[idx]->start
|
||||
+ 1);
|
||||
if (IS_ERR(iomem[idx])) {
|
||||
dev_err(sparx5->dev, "Unable to get switch registers: %s\n",
|
||||
iores[idx]->name);
|
||||
return PTR_ERR(iomem[idx]);
|
||||
}
|
||||
begin[idx] = iomem[idx] - sparx5_main_iomap[range_id[idx]].offset;
|
||||
}
|
||||
for (jdx = 0; jdx < ARRAY_SIZE(sparx5_main_iomap); jdx++) {
|
||||
const struct sparx5_main_io_resource *iomap = &sparx5_main_iomap[jdx];
|
||||
|
||||
sparx5->regs[iomap->id] = begin[iomap->range] + iomap->offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_create_port(struct sparx5 *sparx5,
|
||||
struct initial_port_config *config)
|
||||
{
|
||||
struct sparx5_port *spx5_port;
|
||||
struct net_device *ndev;
|
||||
struct phylink *phylink;
|
||||
int err;
|
||||
|
||||
ndev = sparx5_create_netdev(sparx5, config->portno);
|
||||
if (IS_ERR(ndev)) {
|
||||
dev_err(sparx5->dev, "Could not create net device: %02u\n",
|
||||
config->portno);
|
||||
return PTR_ERR(ndev);
|
||||
}
|
||||
spx5_port = netdev_priv(ndev);
|
||||
spx5_port->of_node = config->node;
|
||||
spx5_port->serdes = config->serdes;
|
||||
spx5_port->pvid = NULL_VID;
|
||||
spx5_port->signd_internal = true;
|
||||
spx5_port->signd_active_high = true;
|
||||
spx5_port->signd_enable = true;
|
||||
spx5_port->max_vlan_tags = SPX5_PORT_MAX_TAGS_NONE;
|
||||
spx5_port->vlan_type = SPX5_VLAN_PORT_TYPE_UNAWARE;
|
||||
spx5_port->custom_etype = 0x8880; /* Vitesse */
|
||||
spx5_port->phylink_pcs.poll = true;
|
||||
spx5_port->phylink_pcs.ops = &sparx5_phylink_pcs_ops;
|
||||
sparx5->ports[config->portno] = spx5_port;
|
||||
|
||||
err = sparx5_port_init(sparx5, spx5_port, &config->conf);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "port init failed\n");
|
||||
return err;
|
||||
}
|
||||
spx5_port->conf = config->conf;
|
||||
|
||||
/* Setup VLAN */
|
||||
sparx5_vlan_port_setup(sparx5, spx5_port->portno);
|
||||
|
||||
/* Create a phylink for PHY management. Also handles SFPs */
|
||||
spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
|
||||
spx5_port->phylink_config.type = PHYLINK_NETDEV;
|
||||
spx5_port->phylink_config.pcs_poll = true;
|
||||
|
||||
phylink = phylink_create(&spx5_port->phylink_config,
|
||||
of_fwnode_handle(config->node),
|
||||
config->conf.phy_mode,
|
||||
&sparx5_phylink_mac_ops);
|
||||
if (IS_ERR(phylink))
|
||||
return PTR_ERR(phylink);
|
||||
|
||||
spx5_port->phylink = phylink;
|
||||
phylink_set_pcs(phylink, &spx5_port->phylink_pcs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_init_ram(struct sparx5 *s5)
|
||||
{
|
||||
const struct sparx5_ram_config spx5_ram_cfg[] = {
|
||||
{spx5_reg_get(s5, ANA_AC_STAT_RESET), ANA_AC_STAT_RESET_RESET},
|
||||
{spx5_reg_get(s5, ASM_STAT_CFG), ASM_STAT_CFG_STAT_CNT_CLR_SHOT},
|
||||
{spx5_reg_get(s5, QSYS_RAM_INIT), QSYS_RAM_INIT_RAM_INIT},
|
||||
{spx5_reg_get(s5, REW_RAM_INIT), QSYS_RAM_INIT_RAM_INIT},
|
||||
{spx5_reg_get(s5, VOP_RAM_INIT), QSYS_RAM_INIT_RAM_INIT},
|
||||
{spx5_reg_get(s5, ANA_AC_RAM_INIT), QSYS_RAM_INIT_RAM_INIT},
|
||||
{spx5_reg_get(s5, ASM_RAM_INIT), QSYS_RAM_INIT_RAM_INIT},
|
||||
{spx5_reg_get(s5, EACL_RAM_INIT), QSYS_RAM_INIT_RAM_INIT},
|
||||
{spx5_reg_get(s5, VCAP_SUPER_RAM_INIT), QSYS_RAM_INIT_RAM_INIT},
|
||||
{spx5_reg_get(s5, DSM_RAM_INIT), QSYS_RAM_INIT_RAM_INIT}
|
||||
};
|
||||
const struct sparx5_ram_config *cfg;
|
||||
u32 value, pending, jdx, idx;
|
||||
|
||||
for (jdx = 0; jdx < 10; jdx++) {
|
||||
pending = ARRAY_SIZE(spx5_ram_cfg);
|
||||
for (idx = 0; idx < ARRAY_SIZE(spx5_ram_cfg); idx++) {
|
||||
cfg = &spx5_ram_cfg[idx];
|
||||
if (jdx == 0) {
|
||||
writel(cfg->init_val, cfg->init_reg);
|
||||
} else {
|
||||
value = readl(cfg->init_reg);
|
||||
if ((value & cfg->init_val) != cfg->init_val)
|
||||
pending--;
|
||||
}
|
||||
}
|
||||
if (!pending)
|
||||
break;
|
||||
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
if (pending > 0) {
|
||||
/* Still initializing, should be complete in
|
||||
* less than 1ms
|
||||
*/
|
||||
dev_err(s5->dev, "Memory initialization error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_init_switchcore(struct sparx5 *sparx5)
|
||||
{
|
||||
u32 value;
|
||||
int err = 0;
|
||||
|
||||
spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(1),
|
||||
EACL_POL_EACL_CFG_EACL_FORCE_INIT,
|
||||
sparx5,
|
||||
EACL_POL_EACL_CFG);
|
||||
|
||||
spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(0),
|
||||
EACL_POL_EACL_CFG_EACL_FORCE_INIT,
|
||||
sparx5,
|
||||
EACL_POL_EACL_CFG);
|
||||
|
||||
/* Initialize memories, if not done already */
|
||||
value = spx5_rd(sparx5, HSCH_RESET_CFG);
|
||||
if (!(value & HSCH_RESET_CFG_CORE_ENA)) {
|
||||
err = sparx5_init_ram(sparx5);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Reset counters */
|
||||
spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5, ANA_AC_STAT_RESET);
|
||||
spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5, ASM_STAT_CFG);
|
||||
|
||||
/* Enable switch-core and queue system */
|
||||
spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_init_coreclock(struct sparx5 *sparx5)
|
||||
{
|
||||
enum sparx5_core_clockfreq freq = sparx5->coreclock;
|
||||
u32 clk_div, clk_period, pol_upd_int, idx;
|
||||
|
||||
/* Verify if core clock frequency is supported on target.
|
||||
* If 'VTSS_CORE_CLOCK_DEFAULT' then the highest supported
|
||||
* freq. is used
|
||||
*/
|
||||
switch (sparx5->target_ct) {
|
||||
case SPX5_TARGET_CT_7546:
|
||||
if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
|
||||
freq = SPX5_CORE_CLOCK_250MHZ;
|
||||
else if (sparx5->coreclock != SPX5_CORE_CLOCK_250MHZ)
|
||||
freq = 0; /* Not supported */
|
||||
break;
|
||||
case SPX5_TARGET_CT_7549:
|
||||
case SPX5_TARGET_CT_7552:
|
||||
case SPX5_TARGET_CT_7556:
|
||||
if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
|
||||
freq = SPX5_CORE_CLOCK_500MHZ;
|
||||
else if (sparx5->coreclock != SPX5_CORE_CLOCK_500MHZ)
|
||||
freq = 0; /* Not supported */
|
||||
break;
|
||||
case SPX5_TARGET_CT_7558:
|
||||
case SPX5_TARGET_CT_7558TSN:
|
||||
if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
|
||||
freq = SPX5_CORE_CLOCK_625MHZ;
|
||||
else if (sparx5->coreclock != SPX5_CORE_CLOCK_625MHZ)
|
||||
freq = 0; /* Not supported */
|
||||
break;
|
||||
case SPX5_TARGET_CT_7546TSN:
|
||||
if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
|
||||
freq = SPX5_CORE_CLOCK_625MHZ;
|
||||
break;
|
||||
case SPX5_TARGET_CT_7549TSN:
|
||||
case SPX5_TARGET_CT_7552TSN:
|
||||
case SPX5_TARGET_CT_7556TSN:
|
||||
if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
|
||||
freq = SPX5_CORE_CLOCK_625MHZ;
|
||||
else if (sparx5->coreclock == SPX5_CORE_CLOCK_250MHZ)
|
||||
freq = 0; /* Not supported */
|
||||
break;
|
||||
default:
|
||||
dev_err(sparx5->dev, "Target (%#04x) not supported\n",
|
||||
sparx5->target_ct);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch (freq) {
|
||||
case SPX5_CORE_CLOCK_250MHZ:
|
||||
clk_div = 10;
|
||||
pol_upd_int = 312;
|
||||
break;
|
||||
case SPX5_CORE_CLOCK_500MHZ:
|
||||
clk_div = 5;
|
||||
pol_upd_int = 624;
|
||||
break;
|
||||
case SPX5_CORE_CLOCK_625MHZ:
|
||||
clk_div = 4;
|
||||
pol_upd_int = 780;
|
||||
break;
|
||||
default:
|
||||
dev_err(sparx5->dev, "%d coreclock not supported on (%#04x)\n",
|
||||
sparx5->coreclock, sparx5->target_ct);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Update state with chosen frequency */
|
||||
sparx5->coreclock = freq;
|
||||
|
||||
/* Configure the LCPLL */
|
||||
spx5_rmw(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV_SET(clk_div) |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV_SET(0) |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR_SET(0) |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL_SET(0) |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA_SET(0) |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA_SET(1),
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA |
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA,
|
||||
sparx5,
|
||||
CLKGEN_LCPLL1_CORE_CLK_CFG);
|
||||
|
||||
clk_period = sparx5_clk_period(freq);
|
||||
|
||||
spx5_rmw(HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS_SET(clk_period / 100),
|
||||
HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS,
|
||||
sparx5,
|
||||
HSCH_SYS_CLK_PER);
|
||||
|
||||
spx5_rmw(ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS_SET(clk_period / 100),
|
||||
ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS,
|
||||
sparx5,
|
||||
ANA_AC_POL_BDLB_DLB_CTRL);
|
||||
|
||||
spx5_rmw(ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS_SET(clk_period / 100),
|
||||
ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS,
|
||||
sparx5,
|
||||
ANA_AC_POL_SLB_DLB_CTRL);
|
||||
|
||||
spx5_rmw(LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS_SET(clk_period / 100),
|
||||
LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS,
|
||||
sparx5,
|
||||
LRN_AUTOAGE_CFG_1);
|
||||
|
||||
for (idx = 0; idx < 3; idx++)
|
||||
spx5_rmw(GCB_SIO_CLOCK_SYS_CLK_PERIOD_SET(clk_period / 100),
|
||||
GCB_SIO_CLOCK_SYS_CLK_PERIOD,
|
||||
sparx5,
|
||||
GCB_SIO_CLOCK(idx));
|
||||
|
||||
spx5_rmw(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_SET
|
||||
((256 * 1000) / clk_period),
|
||||
HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY,
|
||||
sparx5,
|
||||
HSCH_TAS_STATEMACHINE_CFG);
|
||||
|
||||
spx5_rmw(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
|
||||
ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT,
|
||||
sparx5,
|
||||
ANA_AC_POL_POL_UPD_INT_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_qlim_set(struct sparx5 *sparx5)
|
||||
{
|
||||
u32 res, dp, prio;
|
||||
|
||||
for (res = 0; res < 2; res++) {
|
||||
for (prio = 0; prio < 8; prio++)
|
||||
spx5_wr(0xFFF, sparx5,
|
||||
QRES_RES_CFG(prio + 630 + res * 1024));
|
||||
|
||||
for (dp = 0; dp < 4; dp++)
|
||||
spx5_wr(0xFFF, sparx5,
|
||||
QRES_RES_CFG(dp + 638 + res * 1024));
|
||||
}
|
||||
|
||||
/* Set 80,90,95,100% of memory size for top watermarks */
|
||||
spx5_wr(QLIM_WM(80), sparx5, XQS_QLIMIT_SHR_QLIM_CFG(0));
|
||||
spx5_wr(QLIM_WM(90), sparx5, XQS_QLIMIT_SHR_CTOP_CFG(0));
|
||||
spx5_wr(QLIM_WM(95), sparx5, XQS_QLIMIT_SHR_ATOP_CFG(0));
|
||||
spx5_wr(QLIM_WM(100), sparx5, XQS_QLIMIT_SHR_TOP_CFG(0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Some boards needs to map the SGPIO for signal detect explicitly to the
|
||||
* port module
|
||||
*/
|
||||
static void sparx5_board_init(struct sparx5 *sparx5)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (!sparx5->sd_sgpio_remapping)
|
||||
return;
|
||||
|
||||
/* Enable SGPIO Signal Detect remapping */
|
||||
spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
|
||||
GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
|
||||
sparx5,
|
||||
GCB_HW_SGPIO_SD_CFG);
|
||||
|
||||
/* Refer to LOS SGPIO */
|
||||
for (idx = 0; idx < SPX5_PORTS; idx++)
|
||||
if (sparx5->ports[idx])
|
||||
if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
|
||||
spx5_wr(sparx5->ports[idx]->conf.sd_sgpio,
|
||||
sparx5,
|
||||
GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
|
||||
}
|
||||
|
||||
static int sparx5_start(struct sparx5 *sparx5)
|
||||
{
|
||||
u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
char queue_name[32];
|
||||
u32 idx;
|
||||
int err;
|
||||
|
||||
/* Setup own UPSIDs */
|
||||
for (idx = 0; idx < 3; idx++) {
|
||||
spx5_wr(idx, sparx5, ANA_AC_OWN_UPSID(idx));
|
||||
spx5_wr(idx, sparx5, ANA_CL_OWN_UPSID(idx));
|
||||
spx5_wr(idx, sparx5, ANA_L2_OWN_UPSID(idx));
|
||||
spx5_wr(idx, sparx5, REW_OWN_UPSID(idx));
|
||||
}
|
||||
|
||||
/* Enable CPU ports */
|
||||
for (idx = SPX5_PORTS; idx < SPX5_PORTS_ALL; idx++)
|
||||
spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1),
|
||||
QFWD_SWITCH_PORT_MODE_PORT_ENA,
|
||||
sparx5,
|
||||
QFWD_SWITCH_PORT_MODE(idx));
|
||||
|
||||
/* Init masks */
|
||||
sparx5_update_fwd(sparx5);
|
||||
|
||||
/* CPU copy CPU pgids */
|
||||
spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
|
||||
sparx5, ANA_AC_PGID_MISC_CFG(PGID_CPU));
|
||||
spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
|
||||
sparx5, ANA_AC_PGID_MISC_CFG(PGID_BCAST));
|
||||
|
||||
/* Recalc injected frame FCS */
|
||||
for (idx = SPX5_PORT_CPU_0; idx <= SPX5_PORT_CPU_1; idx++)
|
||||
spx5_rmw(ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA_SET(1),
|
||||
ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA,
|
||||
sparx5, ANA_CL_FILTER_CTRL(idx));
|
||||
|
||||
/* Init MAC table, ageing */
|
||||
sparx5_mact_init(sparx5);
|
||||
|
||||
/* Setup VLANs */
|
||||
sparx5_vlan_init(sparx5);
|
||||
|
||||
/* Add host mode BC address (points only to CPU) */
|
||||
sparx5_mact_learn(sparx5, PGID_CPU, broadcast, NULL_VID);
|
||||
|
||||
/* Enable queue limitation watermarks */
|
||||
sparx5_qlim_set(sparx5);
|
||||
|
||||
err = sparx5_config_auto_calendar(sparx5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sparx5_config_dsm_calendar(sparx5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Init stats */
|
||||
err = sparx_stats_init(sparx5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Init mact_sw struct */
|
||||
mutex_init(&sparx5->mact_lock);
|
||||
INIT_LIST_HEAD(&sparx5->mact_entries);
|
||||
snprintf(queue_name, sizeof(queue_name), "%s-mact",
|
||||
dev_name(sparx5->dev));
|
||||
sparx5->mact_queue = create_singlethread_workqueue(queue_name);
|
||||
INIT_DELAYED_WORK(&sparx5->mact_work, sparx5_mact_pull_work);
|
||||
queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
|
||||
SPX5_MACT_PULL_DELAY);
|
||||
|
||||
err = sparx5_register_netdevs(sparx5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sparx5_board_init(sparx5);
|
||||
err = sparx5_register_notifier_blocks(sparx5);
|
||||
|
||||
/* Start register based INJ/XTR */
|
||||
err = -ENXIO;
|
||||
if (err && sparx5->xtr_irq >= 0) {
|
||||
err = devm_request_irq(sparx5->dev, sparx5->xtr_irq,
|
||||
sparx5_xtr_handler, IRQF_SHARED,
|
||||
"sparx5-xtr", sparx5);
|
||||
if (!err)
|
||||
err = sparx5_manual_injection_mode(sparx5);
|
||||
if (err)
|
||||
sparx5->xtr_irq = -ENXIO;
|
||||
} else {
|
||||
sparx5->xtr_irq = -ENXIO;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sparx5_cleanup_ports(struct sparx5 *sparx5)
|
||||
{
|
||||
sparx5_unregister_netdevs(sparx5);
|
||||
sparx5_destroy_netdevs(sparx5);
|
||||
}
|
||||
|
||||
static int mchp_sparx5_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct initial_port_config *configs, *config;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *ports, *portnp;
|
||||
struct reset_control *reset;
|
||||
struct sparx5 *sparx5;
|
||||
int idx = 0, err = 0;
|
||||
u8 *mac_addr;
|
||||
|
||||
if (!np && !pdev->dev.platform_data)
|
||||
return -ENODEV;
|
||||
|
||||
sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5), GFP_KERNEL);
|
||||
if (!sparx5)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, sparx5);
|
||||
sparx5->pdev = pdev;
|
||||
sparx5->dev = &pdev->dev;
|
||||
|
||||
/* Do switch core reset if available */
|
||||
reset = devm_reset_control_get_optional_shared(&pdev->dev, "switch");
|
||||
if (IS_ERR(reset))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(reset),
|
||||
"Failed to get switch reset controller.\n");
|
||||
reset_control_reset(reset);
|
||||
|
||||
/* Default values, some from DT */
|
||||
sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
|
||||
|
||||
ports = of_get_child_by_name(np, "ethernet-ports");
|
||||
if (!ports) {
|
||||
dev_err(sparx5->dev, "no ethernet-ports child node found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
sparx5->port_count = of_get_child_count(ports);
|
||||
|
||||
configs = kcalloc(sparx5->port_count,
|
||||
sizeof(struct initial_port_config), GFP_KERNEL);
|
||||
if (!configs) {
|
||||
err = -ENOMEM;
|
||||
goto cleanup_pnode;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(ports, portnp) {
|
||||
struct sparx5_port_config *conf;
|
||||
struct phy *serdes;
|
||||
u32 portno;
|
||||
|
||||
err = of_property_read_u32(portnp, "reg", &portno);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "port reg property error\n");
|
||||
continue;
|
||||
}
|
||||
config = &configs[idx];
|
||||
conf = &config->conf;
|
||||
conf->speed = SPEED_UNKNOWN;
|
||||
conf->bandwidth = SPEED_UNKNOWN;
|
||||
err = of_get_phy_mode(portnp, &conf->phy_mode);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "port %u: missing phy-mode\n",
|
||||
portno);
|
||||
continue;
|
||||
}
|
||||
err = of_property_read_u32(portnp, "microchip,bandwidth",
|
||||
&conf->bandwidth);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "port %u: missing bandwidth\n",
|
||||
portno);
|
||||
continue;
|
||||
}
|
||||
err = of_property_read_u32(portnp, "microchip,sd-sgpio", &conf->sd_sgpio);
|
||||
if (err)
|
||||
conf->sd_sgpio = ~0;
|
||||
else
|
||||
sparx5->sd_sgpio_remapping = true;
|
||||
serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
|
||||
if (IS_ERR(serdes)) {
|
||||
err = dev_err_probe(sparx5->dev, PTR_ERR(serdes),
|
||||
"port %u: missing serdes\n",
|
||||
portno);
|
||||
goto cleanup_config;
|
||||
}
|
||||
config->portno = portno;
|
||||
config->node = portnp;
|
||||
config->serdes = serdes;
|
||||
|
||||
conf->media = PHY_MEDIA_DAC;
|
||||
conf->serdes_reset = true;
|
||||
conf->portmode = conf->phy_mode;
|
||||
conf->power_down = true;
|
||||
idx++;
|
||||
}
|
||||
|
||||
err = sparx5_create_targets(sparx5);
|
||||
if (err)
|
||||
goto cleanup_config;
|
||||
|
||||
if (of_get_mac_address(np, mac_addr)) {
|
||||
dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
|
||||
eth_random_addr(sparx5->base_mac);
|
||||
sparx5->base_mac[5] = 0;
|
||||
} else {
|
||||
ether_addr_copy(sparx5->base_mac, mac_addr);
|
||||
}
|
||||
|
||||
sparx5->xtr_irq = platform_get_irq_byname(sparx5->pdev, "xtr");
|
||||
|
||||
/* Read chip ID to check CPU interface */
|
||||
sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
|
||||
|
||||
sparx5->target_ct = (enum spx5_target_chiptype)
|
||||
GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
|
||||
|
||||
/* Initialize Switchcore and internal RAMs */
|
||||
err = sparx5_init_switchcore(sparx5);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "Switchcore initialization error\n");
|
||||
goto cleanup_config;
|
||||
}
|
||||
|
||||
/* Initialize the LC-PLL (core clock) and set affected registers */
|
||||
err = sparx5_init_coreclock(sparx5);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "LC-PLL initialization error\n");
|
||||
goto cleanup_config;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < sparx5->port_count; ++idx) {
|
||||
config = &configs[idx];
|
||||
if (!config->node)
|
||||
continue;
|
||||
|
||||
err = sparx5_create_port(sparx5, config);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "port create error\n");
|
||||
goto cleanup_ports;
|
||||
}
|
||||
}
|
||||
|
||||
err = sparx5_start(sparx5);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev, "Start failed\n");
|
||||
goto cleanup_ports;
|
||||
}
|
||||
goto cleanup_config;
|
||||
|
||||
cleanup_ports:
|
||||
sparx5_cleanup_ports(sparx5);
|
||||
cleanup_config:
|
||||
kfree(configs);
|
||||
cleanup_pnode:
|
||||
of_node_put(ports);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mchp_sparx5_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sparx5 *sparx5 = platform_get_drvdata(pdev);
|
||||
|
||||
if (sparx5->xtr_irq) {
|
||||
disable_irq(sparx5->xtr_irq);
|
||||
sparx5->xtr_irq = -ENXIO;
|
||||
}
|
||||
sparx5_cleanup_ports(sparx5);
|
||||
/* Unregister netdevs */
|
||||
sparx5_unregister_notifier_blocks(sparx5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mchp_sparx5_match[] = {
|
||||
{ .compatible = "microchip,sparx5-switch" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mchp_sparx5_match);
|
||||
|
||||
static struct platform_driver mchp_sparx5_driver = {
|
||||
.probe = mchp_sparx5_probe,
|
||||
.remove = mchp_sparx5_remove,
|
||||
.driver = {
|
||||
.name = "sparx5-switch",
|
||||
.of_match_table = mchp_sparx5_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mchp_sparx5_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Microchip Sparx5 switch driver");
|
||||
MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
375
drivers/net/ethernet/microchip/sparx5/sparx5_main.h
Normal file
375
drivers/net/ethernet/microchip/sparx5/sparx5_main.h
Normal file
@ -0,0 +1,375 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#ifndef __SPARX5_MAIN_H__
|
||||
#define __SPARX5_MAIN_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/hrtimer.h>
|
||||
|
||||
/* Target chip type */
|
||||
enum spx5_target_chiptype {
|
||||
SPX5_TARGET_CT_7546 = 0x7546, /* SparX-5-64 Enterprise */
|
||||
SPX5_TARGET_CT_7549 = 0x7549, /* SparX-5-90 Enterprise */
|
||||
SPX5_TARGET_CT_7552 = 0x7552, /* SparX-5-128 Enterprise */
|
||||
SPX5_TARGET_CT_7556 = 0x7556, /* SparX-5-160 Enterprise */
|
||||
SPX5_TARGET_CT_7558 = 0x7558, /* SparX-5-200 Enterprise */
|
||||
SPX5_TARGET_CT_7546TSN = 0x47546, /* SparX-5-64i Industrial */
|
||||
SPX5_TARGET_CT_7549TSN = 0x47549, /* SparX-5-90i Industrial */
|
||||
SPX5_TARGET_CT_7552TSN = 0x47552, /* SparX-5-128i Industrial */
|
||||
SPX5_TARGET_CT_7556TSN = 0x47556, /* SparX-5-160i Industrial */
|
||||
SPX5_TARGET_CT_7558TSN = 0x47558, /* SparX-5-200i Industrial */
|
||||
};
|
||||
|
||||
enum sparx5_port_max_tags {
|
||||
SPX5_PORT_MAX_TAGS_NONE, /* No extra tags allowed */
|
||||
SPX5_PORT_MAX_TAGS_ONE, /* Single tag allowed */
|
||||
SPX5_PORT_MAX_TAGS_TWO /* Single and double tag allowed */
|
||||
};
|
||||
|
||||
enum sparx5_vlan_port_type {
|
||||
SPX5_VLAN_PORT_TYPE_UNAWARE, /* VLAN unaware port */
|
||||
SPX5_VLAN_PORT_TYPE_C, /* C-port */
|
||||
SPX5_VLAN_PORT_TYPE_S, /* S-port */
|
||||
SPX5_VLAN_PORT_TYPE_S_CUSTOM /* S-port using custom type */
|
||||
};
|
||||
|
||||
#define SPX5_PORTS 65
|
||||
#define SPX5_PORT_CPU (SPX5_PORTS) /* Next port is CPU port */
|
||||
#define SPX5_PORT_CPU_0 (SPX5_PORT_CPU + 0) /* CPU Port 65 */
|
||||
#define SPX5_PORT_CPU_1 (SPX5_PORT_CPU + 1) /* CPU Port 66 */
|
||||
#define SPX5_PORT_VD0 (SPX5_PORT_CPU + 2) /* VD0/Port 67 used for IPMC */
|
||||
#define SPX5_PORT_VD1 (SPX5_PORT_CPU + 3) /* VD1/Port 68 used for AFI/OAM */
|
||||
#define SPX5_PORT_VD2 (SPX5_PORT_CPU + 4) /* VD2/Port 69 used for IPinIP*/
|
||||
#define SPX5_PORTS_ALL (SPX5_PORT_CPU + 5) /* Total number of ports */
|
||||
|
||||
#define PGID_BASE SPX5_PORTS /* Starts after port PGIDs */
|
||||
#define PGID_UC_FLOOD (PGID_BASE + 0)
|
||||
#define PGID_MC_FLOOD (PGID_BASE + 1)
|
||||
#define PGID_IPV4_MC_DATA (PGID_BASE + 2)
|
||||
#define PGID_IPV4_MC_CTRL (PGID_BASE + 3)
|
||||
#define PGID_IPV6_MC_DATA (PGID_BASE + 4)
|
||||
#define PGID_IPV6_MC_CTRL (PGID_BASE + 5)
|
||||
#define PGID_BCAST (PGID_BASE + 6)
|
||||
#define PGID_CPU (PGID_BASE + 7)
|
||||
|
||||
#define IFH_LEN 9 /* 36 bytes */
|
||||
#define NULL_VID 0
|
||||
#define SPX5_MACT_PULL_DELAY (2 * HZ)
|
||||
#define SPX5_STATS_CHECK_DELAY (1 * HZ)
|
||||
#define SPX5_PRIOS 8 /* Number of priority queues */
|
||||
#define SPX5_BUFFER_CELL_SZ 184 /* Cell size */
|
||||
#define SPX5_BUFFER_MEMORY 4194280 /* 22795 words * 184 bytes */
|
||||
|
||||
#define XTR_QUEUE 0
|
||||
#define INJ_QUEUE 0
|
||||
|
||||
struct sparx5;
|
||||
|
||||
struct sparx5_port_config {
|
||||
phy_interface_t portmode;
|
||||
u32 bandwidth;
|
||||
int speed;
|
||||
int duplex;
|
||||
enum phy_media media;
|
||||
bool inband;
|
||||
bool power_down;
|
||||
bool autoneg;
|
||||
bool serdes_reset;
|
||||
u32 pause;
|
||||
u32 pause_adv;
|
||||
phy_interface_t phy_mode;
|
||||
u32 sd_sgpio;
|
||||
};
|
||||
|
||||
struct sparx5_port {
|
||||
struct net_device *ndev;
|
||||
struct sparx5 *sparx5;
|
||||
struct device_node *of_node;
|
||||
struct phy *serdes;
|
||||
struct sparx5_port_config conf;
|
||||
struct phylink_config phylink_config;
|
||||
struct phylink *phylink;
|
||||
struct phylink_pcs phylink_pcs;
|
||||
u16 portno;
|
||||
/* Ingress default VLAN (pvid) */
|
||||
u16 pvid;
|
||||
/* Egress default VLAN (vid) */
|
||||
u16 vid;
|
||||
bool signd_internal;
|
||||
bool signd_active_high;
|
||||
bool signd_enable;
|
||||
bool flow_control;
|
||||
enum sparx5_port_max_tags max_vlan_tags;
|
||||
enum sparx5_vlan_port_type vlan_type;
|
||||
u32 custom_etype;
|
||||
u32 ifh[IFH_LEN];
|
||||
bool vlan_aware;
|
||||
struct hrtimer inj_timer;
|
||||
};
|
||||
|
||||
enum sparx5_core_clockfreq {
|
||||
SPX5_CORE_CLOCK_DEFAULT, /* Defaults to the highest supported frequency */
|
||||
SPX5_CORE_CLOCK_250MHZ, /* 250MHZ core clock frequency */
|
||||
SPX5_CORE_CLOCK_500MHZ, /* 500MHZ core clock frequency */
|
||||
SPX5_CORE_CLOCK_625MHZ, /* 625MHZ core clock frequency */
|
||||
};
|
||||
|
||||
struct sparx5 {
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
u32 chip_id;
|
||||
enum spx5_target_chiptype target_ct;
|
||||
void __iomem *regs[NUM_TARGETS];
|
||||
int port_count;
|
||||
struct mutex lock; /* MAC reg lock */
|
||||
/* port structures are in net device */
|
||||
struct sparx5_port *ports[SPX5_PORTS];
|
||||
enum sparx5_core_clockfreq coreclock;
|
||||
/* Statistics */
|
||||
u32 num_stats;
|
||||
u32 num_ethtool_stats;
|
||||
const char * const *stats_layout;
|
||||
u64 *stats;
|
||||
/* Workqueue for reading stats */
|
||||
struct mutex queue_stats_lock;
|
||||
struct delayed_work stats_work;
|
||||
struct workqueue_struct *stats_queue;
|
||||
/* Notifiers */
|
||||
struct notifier_block netdevice_nb;
|
||||
struct notifier_block switchdev_nb;
|
||||
struct notifier_block switchdev_blocking_nb;
|
||||
/* Switch state */
|
||||
u8 base_mac[ETH_ALEN];
|
||||
/* Associated bridge device (when bridged) */
|
||||
struct net_device *hw_bridge_dev;
|
||||
/* Bridged interfaces */
|
||||
DECLARE_BITMAP(bridge_mask, SPX5_PORTS);
|
||||
DECLARE_BITMAP(bridge_fwd_mask, SPX5_PORTS);
|
||||
DECLARE_BITMAP(bridge_lrn_mask, SPX5_PORTS);
|
||||
DECLARE_BITMAP(vlan_mask[VLAN_N_VID], SPX5_PORTS);
|
||||
/* SW MAC table */
|
||||
struct list_head mact_entries;
|
||||
/* mac table list (mact_entries) mutex */
|
||||
struct mutex mact_lock;
|
||||
struct delayed_work mact_work;
|
||||
struct workqueue_struct *mact_queue;
|
||||
/* Board specifics */
|
||||
bool sd_sgpio_remapping;
|
||||
/* Register based inj/xtr */
|
||||
int xtr_irq;
|
||||
};
|
||||
|
||||
/* sparx5_switchdev.c */
|
||||
int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
|
||||
void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
|
||||
|
||||
/* sparx5_packet.c */
|
||||
irqreturn_t sparx5_xtr_handler(int irq, void *_priv);
|
||||
int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev);
|
||||
int sparx5_manual_injection_mode(struct sparx5 *sparx5);
|
||||
void sparx5_port_inj_timer_setup(struct sparx5_port *port);
|
||||
|
||||
/* sparx5_mactable.c */
|
||||
void sparx5_mact_pull_work(struct work_struct *work);
|
||||
int sparx5_mact_learn(struct sparx5 *sparx5, int port,
|
||||
const unsigned char mac[ETH_ALEN], u16 vid);
|
||||
bool sparx5_mact_getnext(struct sparx5 *sparx5,
|
||||
unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2);
|
||||
int sparx5_mact_forget(struct sparx5 *sparx5,
|
||||
const unsigned char mac[ETH_ALEN], u16 vid);
|
||||
int sparx5_add_mact_entry(struct sparx5 *sparx5,
|
||||
struct sparx5_port *port,
|
||||
const unsigned char *addr, u16 vid);
|
||||
int sparx5_del_mact_entry(struct sparx5 *sparx5,
|
||||
const unsigned char *addr,
|
||||
u16 vid);
|
||||
int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr);
|
||||
int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr);
|
||||
void sparx5_set_ageing(struct sparx5 *sparx5, int msecs);
|
||||
void sparx5_mact_init(struct sparx5 *sparx5);
|
||||
|
||||
/* sparx5_vlan.c */
|
||||
void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable);
|
||||
void sparx5_update_fwd(struct sparx5 *sparx5);
|
||||
void sparx5_vlan_init(struct sparx5 *sparx5);
|
||||
void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno);
|
||||
int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
|
||||
bool untagged);
|
||||
int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid);
|
||||
void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
|
||||
|
||||
/* sparx5_calendar.c */
|
||||
int sparx5_config_auto_calendar(struct sparx5 *sparx5);
|
||||
int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
|
||||
|
||||
/* sparx5_ethtool.c */
|
||||
void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats);
|
||||
int sparx_stats_init(struct sparx5 *sparx5);
|
||||
|
||||
/* sparx5_netdev.c */
|
||||
bool sparx5_netdevice_check(const struct net_device *dev);
|
||||
struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
|
||||
int sparx5_register_netdevs(struct sparx5 *sparx5);
|
||||
void sparx5_destroy_netdevs(struct sparx5 *sparx5);
|
||||
void sparx5_unregister_netdevs(struct sparx5 *sparx5);
|
||||
|
||||
/* Clock period in picoseconds */
|
||||
static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
|
||||
{
|
||||
switch (cclock) {
|
||||
case SPX5_CORE_CLOCK_250MHZ:
|
||||
return 4000;
|
||||
case SPX5_CORE_CLOCK_500MHZ:
|
||||
return 2000;
|
||||
case SPX5_CORE_CLOCK_625MHZ:
|
||||
default:
|
||||
return 1600;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sparx5_is_baser(phy_interface_t interface)
|
||||
{
|
||||
return interface == PHY_INTERFACE_MODE_5GBASER ||
|
||||
interface == PHY_INTERFACE_MODE_10GBASER ||
|
||||
interface == PHY_INTERFACE_MODE_25GBASER;
|
||||
}
|
||||
|
||||
extern const struct phylink_mac_ops sparx5_phylink_mac_ops;
|
||||
extern const struct phylink_pcs_ops sparx5_phylink_pcs_ops;
|
||||
extern const struct ethtool_ops sparx5_ethtool_ops;
|
||||
|
||||
/* Calculate raw offset */
|
||||
static inline __pure int spx5_offset(int id, int tinst, int tcnt,
|
||||
int gbase, int ginst,
|
||||
int gcnt, int gwidth,
|
||||
int raddr, int rinst,
|
||||
int rcnt, int rwidth)
|
||||
{
|
||||
WARN_ON((tinst) >= tcnt);
|
||||
WARN_ON((ginst) >= gcnt);
|
||||
WARN_ON((rinst) >= rcnt);
|
||||
return gbase + ((ginst) * gwidth) +
|
||||
raddr + ((rinst) * rwidth);
|
||||
}
|
||||
|
||||
/* Read, Write and modify registers content.
|
||||
* The register definition macros start at the id
|
||||
*/
|
||||
static inline void __iomem *spx5_addr(void __iomem *base[],
|
||||
int id, int tinst, int tcnt,
|
||||
int gbase, int ginst,
|
||||
int gcnt, int gwidth,
|
||||
int raddr, int rinst,
|
||||
int rcnt, int rwidth)
|
||||
{
|
||||
WARN_ON((tinst) >= tcnt);
|
||||
WARN_ON((ginst) >= gcnt);
|
||||
WARN_ON((rinst) >= rcnt);
|
||||
return base[id + (tinst)] +
|
||||
gbase + ((ginst) * gwidth) +
|
||||
raddr + ((rinst) * rwidth);
|
||||
}
|
||||
|
||||
static inline void __iomem *spx5_inst_addr(void __iomem *base,
|
||||
int gbase, int ginst,
|
||||
int gcnt, int gwidth,
|
||||
int raddr, int rinst,
|
||||
int rcnt, int rwidth)
|
||||
{
|
||||
WARN_ON((ginst) >= gcnt);
|
||||
WARN_ON((rinst) >= rcnt);
|
||||
return base +
|
||||
gbase + ((ginst) * gwidth) +
|
||||
raddr + ((rinst) * rwidth);
|
||||
}
|
||||
|
||||
static inline u32 spx5_rd(struct sparx5 *sparx5, int id, int tinst, int tcnt,
|
||||
int gbase, int ginst, int gcnt, int gwidth,
|
||||
int raddr, int rinst, int rcnt, int rwidth)
|
||||
{
|
||||
return readl(spx5_addr(sparx5->regs, id, tinst, tcnt, gbase, ginst,
|
||||
gcnt, gwidth, raddr, rinst, rcnt, rwidth));
|
||||
}
|
||||
|
||||
static inline u32 spx5_inst_rd(void __iomem *iomem, int id, int tinst, int tcnt,
|
||||
int gbase, int ginst, int gcnt, int gwidth,
|
||||
int raddr, int rinst, int rcnt, int rwidth)
|
||||
{
|
||||
return readl(spx5_inst_addr(iomem, gbase, ginst,
|
||||
gcnt, gwidth, raddr, rinst, rcnt, rwidth));
|
||||
}
|
||||
|
||||
static inline void spx5_wr(u32 val, struct sparx5 *sparx5,
|
||||
int id, int tinst, int tcnt,
|
||||
int gbase, int ginst, int gcnt, int gwidth,
|
||||
int raddr, int rinst, int rcnt, int rwidth)
|
||||
{
|
||||
writel(val, spx5_addr(sparx5->regs, id, tinst, tcnt,
|
||||
gbase, ginst, gcnt, gwidth,
|
||||
raddr, rinst, rcnt, rwidth));
|
||||
}
|
||||
|
||||
static inline void spx5_inst_wr(u32 val, void __iomem *iomem,
|
||||
int id, int tinst, int tcnt,
|
||||
int gbase, int ginst, int gcnt, int gwidth,
|
||||
int raddr, int rinst, int rcnt, int rwidth)
|
||||
{
|
||||
writel(val, spx5_inst_addr(iomem,
|
||||
gbase, ginst, gcnt, gwidth,
|
||||
raddr, rinst, rcnt, rwidth));
|
||||
}
|
||||
|
||||
static inline void spx5_rmw(u32 val, u32 mask, struct sparx5 *sparx5,
|
||||
int id, int tinst, int tcnt,
|
||||
int gbase, int ginst, int gcnt, int gwidth,
|
||||
int raddr, int rinst, int rcnt, int rwidth)
|
||||
{
|
||||
u32 nval;
|
||||
|
||||
nval = readl(spx5_addr(sparx5->regs, id, tinst, tcnt, gbase, ginst,
|
||||
gcnt, gwidth, raddr, rinst, rcnt, rwidth));
|
||||
nval = (nval & ~mask) | (val & mask);
|
||||
writel(nval, spx5_addr(sparx5->regs, id, tinst, tcnt, gbase, ginst,
|
||||
gcnt, gwidth, raddr, rinst, rcnt, rwidth));
|
||||
}
|
||||
|
||||
static inline void spx5_inst_rmw(u32 val, u32 mask, void __iomem *iomem,
|
||||
int id, int tinst, int tcnt,
|
||||
int gbase, int ginst, int gcnt, int gwidth,
|
||||
int raddr, int rinst, int rcnt, int rwidth)
|
||||
{
|
||||
u32 nval;
|
||||
|
||||
nval = readl(spx5_inst_addr(iomem, gbase, ginst, gcnt, gwidth, raddr,
|
||||
rinst, rcnt, rwidth));
|
||||
nval = (nval & ~mask) | (val & mask);
|
||||
writel(nval, spx5_inst_addr(iomem, gbase, ginst, gcnt, gwidth, raddr,
|
||||
rinst, rcnt, rwidth));
|
||||
}
|
||||
|
||||
static inline void __iomem *spx5_inst_get(struct sparx5 *sparx5, int id, int tinst)
|
||||
{
|
||||
return sparx5->regs[id + tinst];
|
||||
}
|
||||
|
||||
static inline void __iomem *spx5_reg_get(struct sparx5 *sparx5,
|
||||
int id, int tinst, int tcnt,
|
||||
int gbase, int ginst, int gcnt, int gwidth,
|
||||
int raddr, int rinst, int rcnt, int rwidth)
|
||||
{
|
||||
return spx5_addr(sparx5->regs, id, tinst, tcnt,
|
||||
gbase, ginst, gcnt, gwidth,
|
||||
raddr, rinst, rcnt, rwidth);
|
||||
}
|
||||
|
||||
#endif /* __SPARX5_MAIN_H__ */
|
4642
drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
Normal file
4642
drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
Normal file
File diff suppressed because it is too large
Load Diff
264
drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
Normal file
264
drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
Normal file
@ -0,0 +1,264 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
#include "sparx5_port.h"
|
||||
|
||||
/* The IFH bit position of the first VSTAX bit. This is because the
|
||||
* VSTAX bit positions in Data sheet is starting from zero.
|
||||
*/
|
||||
#define VSTAX 73
|
||||
|
||||
static void ifh_encode_bitfield(void *ifh, u64 value, u32 pos, u32 width)
|
||||
{
|
||||
u8 *ifh_hdr = ifh;
|
||||
/* Calculate the Start IFH byte position of this IFH bit position */
|
||||
u32 byte = (35 - (pos / 8));
|
||||
/* Calculate the Start bit position in the Start IFH byte */
|
||||
u32 bit = (pos % 8);
|
||||
u64 encode = GENMASK(bit + width - 1, bit) & (value << bit);
|
||||
|
||||
/* Max width is 5 bytes - 40 bits. In worst case this will
|
||||
* spread over 6 bytes - 48 bits
|
||||
*/
|
||||
compiletime_assert(width <= 40, "Unsupported width, must be <= 40");
|
||||
|
||||
/* The b0-b7 goes into the start IFH byte */
|
||||
if (encode & 0xFF)
|
||||
ifh_hdr[byte] |= (u8)((encode & 0xFF));
|
||||
/* The b8-b15 goes into the next IFH byte */
|
||||
if (encode & 0xFF00)
|
||||
ifh_hdr[byte - 1] |= (u8)((encode & 0xFF00) >> 8);
|
||||
/* The b16-b23 goes into the next IFH byte */
|
||||
if (encode & 0xFF0000)
|
||||
ifh_hdr[byte - 2] |= (u8)((encode & 0xFF0000) >> 16);
|
||||
/* The b24-b31 goes into the next IFH byte */
|
||||
if (encode & 0xFF000000)
|
||||
ifh_hdr[byte - 3] |= (u8)((encode & 0xFF000000) >> 24);
|
||||
/* The b32-b39 goes into the next IFH byte */
|
||||
if (encode & 0xFF00000000)
|
||||
ifh_hdr[byte - 4] |= (u8)((encode & 0xFF00000000) >> 32);
|
||||
/* The b40-b47 goes into the next IFH byte */
|
||||
if (encode & 0xFF0000000000)
|
||||
ifh_hdr[byte - 5] |= (u8)((encode & 0xFF0000000000) >> 40);
|
||||
}
|
||||
|
||||
static void sparx5_set_port_ifh(void *ifh_hdr, u16 portno)
|
||||
{
|
||||
/* VSTAX.RSV = 1. MSBit must be 1 */
|
||||
ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 79, 1);
|
||||
/* VSTAX.INGR_DROP_MODE = Enable. Don't make head-of-line blocking */
|
||||
ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 55, 1);
|
||||
/* MISC.CPU_MASK/DPORT = Destination port */
|
||||
ifh_encode_bitfield(ifh_hdr, portno, 29, 8);
|
||||
/* MISC.PIPELINE_PT */
|
||||
ifh_encode_bitfield(ifh_hdr, 16, 37, 5);
|
||||
/* MISC.PIPELINE_ACT */
|
||||
ifh_encode_bitfield(ifh_hdr, 1, 42, 3);
|
||||
/* FWD.SRC_PORT = CPU */
|
||||
ifh_encode_bitfield(ifh_hdr, SPX5_PORT_CPU, 46, 7);
|
||||
/* FWD.SFLOW_ID (disable SFlow sampling) */
|
||||
ifh_encode_bitfield(ifh_hdr, 124, 57, 7);
|
||||
/* FWD.UPDATE_FCS = Enable. Enforce update of FCS. */
|
||||
ifh_encode_bitfield(ifh_hdr, 1, 67, 1);
|
||||
}
|
||||
|
||||
static int sparx5_port_open(struct net_device *ndev)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(ndev);
|
||||
int err = 0;
|
||||
|
||||
sparx5_port_enable(port, true);
|
||||
err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
|
||||
if (err) {
|
||||
netdev_err(ndev, "Could not attach to PHY\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
phylink_start(port->phylink);
|
||||
|
||||
if (!ndev->phydev) {
|
||||
/* power up serdes */
|
||||
port->conf.power_down = false;
|
||||
if (port->conf.serdes_reset)
|
||||
err = sparx5_serdes_set(port->sparx5, port, &port->conf);
|
||||
else
|
||||
err = phy_power_on(port->serdes);
|
||||
if (err)
|
||||
netdev_err(ndev, "%s failed\n", __func__);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sparx5_port_stop(struct net_device *ndev)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(ndev);
|
||||
int err = 0;
|
||||
|
||||
sparx5_port_enable(port, false);
|
||||
phylink_stop(port->phylink);
|
||||
phylink_disconnect_phy(port->phylink);
|
||||
|
||||
if (!ndev->phydev) {
|
||||
/* power down serdes */
|
||||
port->conf.power_down = true;
|
||||
if (port->conf.serdes_reset)
|
||||
err = sparx5_serdes_set(port->sparx5, port, &port->conf);
|
||||
else
|
||||
err = phy_power_off(port->serdes);
|
||||
if (err)
|
||||
netdev_err(ndev, "%s failed\n", __func__);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sparx5_set_rx_mode(struct net_device *dev)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
|
||||
if (!test_bit(port->portno, sparx5->bridge_mask))
|
||||
__dev_mc_sync(dev, sparx5_mc_sync, sparx5_mc_unsync);
|
||||
}
|
||||
|
||||
static int sparx5_port_get_phys_port_name(struct net_device *dev,
|
||||
char *buf, size_t len)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, len, "p%d", port->portno);
|
||||
if (ret >= len)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_set_mac_address(struct net_device *dev, void *p)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
const struct sockaddr *addr = p;
|
||||
|
||||
if (!is_valid_ether_addr(addr->sa_data))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
/* Remove current */
|
||||
sparx5_mact_forget(sparx5, dev->dev_addr, port->pvid);
|
||||
|
||||
/* Add new */
|
||||
sparx5_mact_learn(sparx5, PGID_CPU, addr->sa_data, port->pvid);
|
||||
|
||||
/* Record the address */
|
||||
ether_addr_copy(dev->dev_addr, addr->sa_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_get_port_parent_id(struct net_device *dev,
|
||||
struct netdev_phys_item_id *ppid)
|
||||
{
|
||||
struct sparx5_port *sparx5_port = netdev_priv(dev);
|
||||
struct sparx5 *sparx5 = sparx5_port->sparx5;
|
||||
|
||||
ppid->id_len = sizeof(sparx5->base_mac);
|
||||
memcpy(&ppid->id, &sparx5->base_mac, ppid->id_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops sparx5_port_netdev_ops = {
|
||||
.ndo_open = sparx5_port_open,
|
||||
.ndo_stop = sparx5_port_stop,
|
||||
.ndo_start_xmit = sparx5_port_xmit_impl,
|
||||
.ndo_set_rx_mode = sparx5_set_rx_mode,
|
||||
.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
|
||||
.ndo_set_mac_address = sparx5_set_mac_address,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
.ndo_get_stats64 = sparx5_get_stats64,
|
||||
.ndo_get_port_parent_id = sparx5_get_port_parent_id,
|
||||
};
|
||||
|
||||
bool sparx5_netdevice_check(const struct net_device *dev)
|
||||
{
|
||||
return dev && (dev->netdev_ops == &sparx5_port_netdev_ops);
|
||||
}
|
||||
|
||||
struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
|
||||
{
|
||||
struct sparx5_port *spx5_port;
|
||||
struct net_device *ndev;
|
||||
u64 val;
|
||||
|
||||
ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct sparx5_port));
|
||||
if (!ndev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
SET_NETDEV_DEV(ndev, sparx5->dev);
|
||||
spx5_port = netdev_priv(ndev);
|
||||
spx5_port->ndev = ndev;
|
||||
spx5_port->sparx5 = sparx5;
|
||||
spx5_port->portno = portno;
|
||||
sparx5_set_port_ifh(spx5_port->ifh, portno);
|
||||
|
||||
ndev->netdev_ops = &sparx5_port_netdev_ops;
|
||||
ndev->ethtool_ops = &sparx5_ethtool_ops;
|
||||
|
||||
val = ether_addr_to_u64(sparx5->base_mac) + portno + 1;
|
||||
u64_to_ether_addr(val, ndev->dev_addr);
|
||||
|
||||
return ndev;
|
||||
}
|
||||
|
||||
int sparx5_register_netdevs(struct sparx5 *sparx5)
|
||||
{
|
||||
int portno;
|
||||
int err;
|
||||
|
||||
for (portno = 0; portno < SPX5_PORTS; portno++)
|
||||
if (sparx5->ports[portno]) {
|
||||
err = register_netdev(sparx5->ports[portno]->ndev);
|
||||
if (err) {
|
||||
dev_err(sparx5->dev,
|
||||
"port: %02u: netdev registration failed\n",
|
||||
portno);
|
||||
return err;
|
||||
}
|
||||
sparx5_port_inj_timer_setup(sparx5->ports[portno]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sparx5_destroy_netdevs(struct sparx5 *sparx5)
|
||||
{
|
||||
struct sparx5_port *port;
|
||||
int portno;
|
||||
|
||||
for (portno = 0; portno < SPX5_PORTS; portno++) {
|
||||
port = sparx5->ports[portno];
|
||||
if (port && port->phylink) {
|
||||
/* Disconnect the phy */
|
||||
rtnl_lock();
|
||||
sparx5_port_stop(port->ndev);
|
||||
phylink_disconnect_phy(port->phylink);
|
||||
rtnl_unlock();
|
||||
phylink_destroy(port->phylink);
|
||||
port->phylink = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sparx5_unregister_netdevs(struct sparx5 *sparx5)
|
||||
{
|
||||
int portno;
|
||||
|
||||
for (portno = 0; portno < SPX5_PORTS; portno++)
|
||||
if (sparx5->ports[portno])
|
||||
unregister_netdev(sparx5->ports[portno]->ndev);
|
||||
}
|
||||
|
320
drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
Normal file
320
drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
Normal file
@ -0,0 +1,320 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
|
||||
#define XTR_EOF_0 ntohl((__force __be32)0x80000000u)
|
||||
#define XTR_EOF_1 ntohl((__force __be32)0x80000001u)
|
||||
#define XTR_EOF_2 ntohl((__force __be32)0x80000002u)
|
||||
#define XTR_EOF_3 ntohl((__force __be32)0x80000003u)
|
||||
#define XTR_PRUNED ntohl((__force __be32)0x80000004u)
|
||||
#define XTR_ABORT ntohl((__force __be32)0x80000005u)
|
||||
#define XTR_ESCAPE ntohl((__force __be32)0x80000006u)
|
||||
#define XTR_NOT_READY ntohl((__force __be32)0x80000007u)
|
||||
|
||||
#define XTR_VALID_BYTES(x) (4 - ((x) & 3))
|
||||
|
||||
#define INJ_TIMEOUT_NS 50000
|
||||
|
||||
struct frame_info {
|
||||
int src_port;
|
||||
};
|
||||
|
||||
static void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
|
||||
{
|
||||
/* Start flush */
|
||||
spx5_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(grp)), sparx5, QS_XTR_FLUSH);
|
||||
|
||||
/* Allow to drain */
|
||||
mdelay(1);
|
||||
|
||||
/* All Queues normal */
|
||||
spx5_wr(0, sparx5, QS_XTR_FLUSH);
|
||||
}
|
||||
|
||||
static void sparx5_ifh_parse(u32 *ifh, struct frame_info *info)
|
||||
{
|
||||
u8 *xtr_hdr = (u8 *)ifh;
|
||||
|
||||
/* FWD is bit 45-72 (28 bits), but we only read the 27 LSB for now */
|
||||
u32 fwd =
|
||||
((u32)xtr_hdr[27] << 24) |
|
||||
((u32)xtr_hdr[28] << 16) |
|
||||
((u32)xtr_hdr[29] << 8) |
|
||||
((u32)xtr_hdr[30] << 0);
|
||||
fwd = (fwd >> 5);
|
||||
info->src_port = FIELD_GET(GENMASK(7, 1), fwd);
|
||||
}
|
||||
|
||||
static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
|
||||
{
|
||||
bool eof_flag = false, pruned_flag = false, abort_flag = false;
|
||||
struct net_device *netdev;
|
||||
struct sparx5_port *port;
|
||||
struct frame_info fi;
|
||||
int i, byte_cnt = 0;
|
||||
struct sk_buff *skb;
|
||||
u32 ifh[IFH_LEN];
|
||||
u32 *rxbuf;
|
||||
|
||||
/* Get IFH */
|
||||
for (i = 0; i < IFH_LEN; i++)
|
||||
ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
|
||||
|
||||
/* Decode IFH (whats needed) */
|
||||
sparx5_ifh_parse(ifh, &fi);
|
||||
|
||||
/* Map to port netdev */
|
||||
port = fi.src_port < SPX5_PORTS ?
|
||||
sparx5->ports[fi.src_port] : NULL;
|
||||
if (!port || !port->ndev) {
|
||||
dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
|
||||
sparx5_xtr_flush(sparx5, grp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Have netdev, get skb */
|
||||
netdev = port->ndev;
|
||||
skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
|
||||
if (!skb) {
|
||||
sparx5_xtr_flush(sparx5, grp);
|
||||
dev_err(sparx5->dev, "No skb allocated\n");
|
||||
netdev->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
rxbuf = (u32 *)skb->data;
|
||||
|
||||
/* Now, pull frame data */
|
||||
while (!eof_flag) {
|
||||
u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
|
||||
u32 cmp = val;
|
||||
|
||||
if (byte_swap)
|
||||
cmp = ntohl((__force __be32)val);
|
||||
|
||||
switch (cmp) {
|
||||
case XTR_NOT_READY:
|
||||
break;
|
||||
case XTR_ABORT:
|
||||
/* No accompanying data */
|
||||
abort_flag = true;
|
||||
eof_flag = true;
|
||||
break;
|
||||
case XTR_EOF_0:
|
||||
case XTR_EOF_1:
|
||||
case XTR_EOF_2:
|
||||
case XTR_EOF_3:
|
||||
/* This assumes STATUS_WORD_POS == 1, Status
|
||||
* just after last data
|
||||
*/
|
||||
byte_cnt -= (4 - XTR_VALID_BYTES(val));
|
||||
eof_flag = true;
|
||||
break;
|
||||
case XTR_PRUNED:
|
||||
/* But get the last 4 bytes as well */
|
||||
eof_flag = true;
|
||||
pruned_flag = true;
|
||||
fallthrough;
|
||||
case XTR_ESCAPE:
|
||||
*rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
|
||||
byte_cnt += 4;
|
||||
rxbuf++;
|
||||
break;
|
||||
default:
|
||||
*rxbuf = val;
|
||||
byte_cnt += 4;
|
||||
rxbuf++;
|
||||
}
|
||||
}
|
||||
|
||||
if (abort_flag || pruned_flag || !eof_flag) {
|
||||
netdev_err(netdev, "Discarded frame: abort:%d pruned:%d eof:%d\n",
|
||||
abort_flag, pruned_flag, eof_flag);
|
||||
kfree_skb(skb);
|
||||
netdev->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Everything we see on an interface that is in the HW bridge
|
||||
* has already been forwarded
|
||||
*/
|
||||
if (test_bit(port->portno, sparx5->bridge_mask))
|
||||
skb->offload_fwd_mark = 1;
|
||||
|
||||
/* Finish up skb */
|
||||
skb_put(skb, byte_cnt - ETH_FCS_LEN);
|
||||
eth_skb_pad(skb);
|
||||
skb->protocol = eth_type_trans(skb, netdev);
|
||||
netif_rx(skb);
|
||||
netdev->stats.rx_bytes += skb->len;
|
||||
netdev->stats.rx_packets++;
|
||||
}
|
||||
|
||||
static int sparx5_inject(struct sparx5 *sparx5,
|
||||
u32 *ifh,
|
||||
struct sk_buff *skb,
|
||||
struct net_device *ndev)
|
||||
{
|
||||
int grp = INJ_QUEUE;
|
||||
u32 val, w, count;
|
||||
u8 *buf;
|
||||
|
||||
val = spx5_rd(sparx5, QS_INJ_STATUS);
|
||||
if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
|
||||
pr_err_ratelimited("Injection: Queue not ready: 0x%lx\n",
|
||||
QS_INJ_STATUS_FIFO_RDY_GET(val));
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Indicate SOF */
|
||||
spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
|
||||
QS_INJ_CTRL_GAP_SIZE_SET(1),
|
||||
sparx5, QS_INJ_CTRL(grp));
|
||||
|
||||
/* Write the IFH to the chip. */
|
||||
for (w = 0; w < IFH_LEN; w++)
|
||||
spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
|
||||
|
||||
/* Write words, round up */
|
||||
count = DIV_ROUND_UP(skb->len, 4);
|
||||
buf = skb->data;
|
||||
for (w = 0; w < count; w++, buf += 4) {
|
||||
val = get_unaligned((const u32 *)buf);
|
||||
spx5_wr(val, sparx5, QS_INJ_WR(grp));
|
||||
}
|
||||
|
||||
/* Add padding */
|
||||
while (w < (60 / 4)) {
|
||||
spx5_wr(0, sparx5, QS_INJ_WR(grp));
|
||||
w++;
|
||||
}
|
||||
|
||||
/* Indicate EOF and valid bytes in last word */
|
||||
spx5_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) |
|
||||
QS_INJ_CTRL_VLD_BYTES_SET(skb->len < 60 ? 0 : skb->len % 4) |
|
||||
QS_INJ_CTRL_EOF_SET(1),
|
||||
sparx5, QS_INJ_CTRL(grp));
|
||||
|
||||
/* Add dummy CRC */
|
||||
spx5_wr(0, sparx5, QS_INJ_WR(grp));
|
||||
w++;
|
||||
|
||||
val = spx5_rd(sparx5, QS_INJ_STATUS);
|
||||
if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
|
||||
struct sparx5_port *port = netdev_priv(ndev);
|
||||
|
||||
pr_err_ratelimited("Injection: Watermark reached: 0x%lx\n",
|
||||
QS_INJ_STATUS_WMARK_REACHED_GET(val));
|
||||
netif_stop_queue(ndev);
|
||||
hrtimer_start(&port->inj_timer, INJ_TIMEOUT_NS,
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
int ret;
|
||||
|
||||
ret = sparx5_inject(sparx5, port->ifh, skb, dev);
|
||||
|
||||
if (ret == NETDEV_TX_OK) {
|
||||
stats->tx_bytes += skb->len;
|
||||
stats->tx_packets++;
|
||||
skb_tx_timestamp(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
} else {
|
||||
stats->tx_dropped++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart sparx5_injection_timeout(struct hrtimer *tmr)
|
||||
{
|
||||
struct sparx5_port *port = container_of(tmr, struct sparx5_port,
|
||||
inj_timer);
|
||||
int grp = INJ_QUEUE;
|
||||
u32 val;
|
||||
|
||||
val = spx5_rd(port->sparx5, QS_INJ_STATUS);
|
||||
if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
|
||||
pr_err_ratelimited("Injection: Reset watermark count\n");
|
||||
/* Reset Watermark count to restart */
|
||||
spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
|
||||
DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
|
||||
port->sparx5,
|
||||
DSM_DEV_TX_STOP_WM_CFG(port->portno));
|
||||
}
|
||||
netif_wake_queue(port->ndev);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
int sparx5_manual_injection_mode(struct sparx5 *sparx5)
|
||||
{
|
||||
const int byte_swap = 1;
|
||||
int portno;
|
||||
|
||||
/* Change mode to manual extraction and injection */
|
||||
spx5_wr(QS_XTR_GRP_CFG_MODE_SET(1) |
|
||||
QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |
|
||||
QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),
|
||||
sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));
|
||||
spx5_wr(QS_INJ_GRP_CFG_MODE_SET(1) |
|
||||
QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),
|
||||
sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
|
||||
|
||||
/* CPU ports capture setup */
|
||||
for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
|
||||
/* ASM CPU port: No preamble, IFH, enable padding */
|
||||
spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
|
||||
ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |
|
||||
ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */
|
||||
sparx5, ASM_PORT_CFG(portno));
|
||||
|
||||
/* Reset WM cnt to unclog queued frames */
|
||||
spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
|
||||
DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
|
||||
sparx5,
|
||||
DSM_DEV_TX_STOP_WM_CFG(portno));
|
||||
|
||||
/* Set Disassembler Stop Watermark level */
|
||||
spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(0),
|
||||
DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
|
||||
sparx5,
|
||||
DSM_DEV_TX_STOP_WM_CFG(portno));
|
||||
|
||||
/* Enable Disassembler buffer underrun watchdog
|
||||
*/
|
||||
spx5_rmw(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(0),
|
||||
DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS,
|
||||
sparx5,
|
||||
DSM_BUF_CFG(portno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
|
||||
{
|
||||
struct sparx5 *s5 = _sparx5;
|
||||
int poll = 64;
|
||||
|
||||
/* Check data in queue */
|
||||
while (spx5_rd(s5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE) && poll-- > 0)
|
||||
sparx5_xtr_grp(s5, XTR_QUEUE, false);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void sparx5_port_inj_timer_setup(struct sparx5_port *port)
|
||||
{
|
||||
hrtimer_init(&port->inj_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
port->inj_timer.function = sparx5_injection_timeout;
|
||||
}
|
210
drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
Normal file
210
drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
Normal file
@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/sfp.h>
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
#include "sparx5_port.h"
|
||||
|
||||
static bool port_conf_has_changed(struct sparx5_port_config *a, struct sparx5_port_config *b)
|
||||
{
|
||||
if (a->speed != b->speed ||
|
||||
a->portmode != b->portmode ||
|
||||
a->autoneg != b->autoneg ||
|
||||
a->pause_adv != b->pause_adv ||
|
||||
a->power_down != b->power_down ||
|
||||
a->media != b->media)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sparx5_phylink_validate(struct phylink_config *config,
|
||||
unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
||||
|
||||
phylink_set(mask, Autoneg);
|
||||
phylink_set_port_modes(mask);
|
||||
phylink_set(mask, Pause);
|
||||
phylink_set(mask, Asym_Pause);
|
||||
|
||||
switch (state->interface) {
|
||||
case PHY_INTERFACE_MODE_5GBASER:
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
case PHY_INTERFACE_MODE_25GBASER:
|
||||
case PHY_INTERFACE_MODE_NA:
|
||||
if (port->conf.bandwidth == SPEED_5000)
|
||||
phylink_set(mask, 5000baseT_Full);
|
||||
if (port->conf.bandwidth == SPEED_10000) {
|
||||
phylink_set(mask, 5000baseT_Full);
|
||||
phylink_set(mask, 10000baseT_Full);
|
||||
phylink_set(mask, 10000baseCR_Full);
|
||||
phylink_set(mask, 10000baseSR_Full);
|
||||
phylink_set(mask, 10000baseLR_Full);
|
||||
phylink_set(mask, 10000baseLRM_Full);
|
||||
phylink_set(mask, 10000baseER_Full);
|
||||
}
|
||||
if (port->conf.bandwidth == SPEED_25000) {
|
||||
phylink_set(mask, 5000baseT_Full);
|
||||
phylink_set(mask, 10000baseT_Full);
|
||||
phylink_set(mask, 10000baseCR_Full);
|
||||
phylink_set(mask, 10000baseSR_Full);
|
||||
phylink_set(mask, 10000baseLR_Full);
|
||||
phylink_set(mask, 10000baseLRM_Full);
|
||||
phylink_set(mask, 10000baseER_Full);
|
||||
phylink_set(mask, 25000baseCR_Full);
|
||||
phylink_set(mask, 25000baseSR_Full);
|
||||
}
|
||||
if (state->interface != PHY_INTERFACE_MODE_NA)
|
||||
break;
|
||||
fallthrough;
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
phylink_set(mask, 10baseT_Half);
|
||||
phylink_set(mask, 10baseT_Full);
|
||||
phylink_set(mask, 100baseT_Half);
|
||||
phylink_set(mask, 100baseT_Full);
|
||||
phylink_set(mask, 1000baseT_Full);
|
||||
phylink_set(mask, 1000baseX_Full);
|
||||
if (state->interface != PHY_INTERFACE_MODE_NA)
|
||||
break;
|
||||
fallthrough;
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
if (state->interface != PHY_INTERFACE_MODE_2500BASEX) {
|
||||
phylink_set(mask, 1000baseT_Full);
|
||||
phylink_set(mask, 1000baseX_Full);
|
||||
}
|
||||
if (state->interface == PHY_INTERFACE_MODE_2500BASEX ||
|
||||
state->interface == PHY_INTERFACE_MODE_NA) {
|
||||
phylink_set(mask, 2500baseT_Full);
|
||||
phylink_set(mask, 2500baseX_Full);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||
return;
|
||||
}
|
||||
bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||
bitmap_and(state->advertising, state->advertising, mask,
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||
}
|
||||
|
||||
static void sparx5_phylink_mac_config(struct phylink_config *config,
|
||||
unsigned int mode,
|
||||
const struct phylink_link_state *state)
|
||||
{
|
||||
/* Currently not used */
|
||||
}
|
||||
|
||||
static void sparx5_phylink_mac_link_up(struct phylink_config *config,
|
||||
struct phy_device *phy,
|
||||
unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
int speed, int duplex,
|
||||
bool tx_pause, bool rx_pause)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
|
||||
struct sparx5_port_config conf;
|
||||
int err;
|
||||
|
||||
conf = port->conf;
|
||||
conf.duplex = duplex;
|
||||
conf.pause = 0;
|
||||
conf.pause |= tx_pause ? MLO_PAUSE_TX : 0;
|
||||
conf.pause |= rx_pause ? MLO_PAUSE_RX : 0;
|
||||
conf.speed = speed;
|
||||
/* Configure the port to speed/duplex/pause */
|
||||
err = sparx5_port_config(port->sparx5, port, &conf);
|
||||
if (err)
|
||||
netdev_err(port->ndev, "port config failed: %d\n", err);
|
||||
}
|
||||
|
||||
static void sparx5_phylink_mac_link_down(struct phylink_config *config,
|
||||
unsigned int mode,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
/* Currently not used */
|
||||
}
|
||||
|
||||
static struct sparx5_port *sparx5_pcs_to_port(struct phylink_pcs *pcs)
|
||||
{
|
||||
return container_of(pcs, struct sparx5_port, phylink_pcs);
|
||||
}
|
||||
|
||||
static void sparx5_pcs_get_state(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct sparx5_port *port = sparx5_pcs_to_port(pcs);
|
||||
struct sparx5_port_status status;
|
||||
|
||||
sparx5_get_port_status(port->sparx5, port, &status);
|
||||
state->link = status.link && !status.link_down;
|
||||
state->an_complete = status.an_complete;
|
||||
state->speed = status.speed;
|
||||
state->duplex = status.duplex;
|
||||
state->pause = status.pause;
|
||||
}
|
||||
|
||||
static int sparx5_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct sparx5_port *port = sparx5_pcs_to_port(pcs);
|
||||
struct sparx5_port_config conf;
|
||||
int ret = 0;
|
||||
|
||||
conf = port->conf;
|
||||
conf.power_down = false;
|
||||
conf.portmode = interface;
|
||||
conf.inband = phylink_autoneg_inband(mode);
|
||||
conf.autoneg = phylink_test(advertising, Autoneg);
|
||||
conf.pause_adv = 0;
|
||||
if (phylink_test(advertising, Pause))
|
||||
conf.pause_adv |= ADVERTISE_1000XPAUSE;
|
||||
if (phylink_test(advertising, Asym_Pause))
|
||||
conf.pause_adv |= ADVERTISE_1000XPSE_ASYM;
|
||||
if (sparx5_is_baser(interface)) {
|
||||
if (phylink_test(advertising, FIBRE))
|
||||
conf.media = PHY_MEDIA_SR;
|
||||
else
|
||||
conf.media = PHY_MEDIA_DAC;
|
||||
}
|
||||
if (!port_conf_has_changed(&port->conf, &conf))
|
||||
return ret;
|
||||
/* Enable the PCS matching this interface type */
|
||||
ret = sparx5_port_pcs_set(port->sparx5, port, &conf);
|
||||
if (ret)
|
||||
netdev_err(port->ndev, "port PCS config failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sparx5_pcs_aneg_restart(struct phylink_pcs *pcs)
|
||||
{
|
||||
/* Currently not used */
|
||||
}
|
||||
|
||||
const struct phylink_pcs_ops sparx5_phylink_pcs_ops = {
|
||||
.pcs_get_state = sparx5_pcs_get_state,
|
||||
.pcs_config = sparx5_pcs_config,
|
||||
.pcs_an_restart = sparx5_pcs_aneg_restart,
|
||||
};
|
||||
|
||||
const struct phylink_mac_ops sparx5_phylink_mac_ops = {
|
||||
.validate = sparx5_phylink_validate,
|
||||
.mac_config = sparx5_phylink_mac_config,
|
||||
.mac_link_down = sparx5_phylink_mac_link_down,
|
||||
.mac_link_up = sparx5_phylink_mac_link_up,
|
||||
};
|
1146
drivers/net/ethernet/microchip/sparx5/sparx5_port.c
Normal file
1146
drivers/net/ethernet/microchip/sparx5/sparx5_port.c
Normal file
File diff suppressed because it is too large
Load Diff
93
drivers/net/ethernet/microchip/sparx5/sparx5_port.h
Normal file
93
drivers/net/ethernet/microchip/sparx5/sparx5_port.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#ifndef __SPARX5_PORT_H__
|
||||
#define __SPARX5_PORT_H__
|
||||
|
||||
#include "sparx5_main.h"
|
||||
|
||||
static inline bool sparx5_port_is_2g5(int portno)
|
||||
{
|
||||
return portno >= 16 && portno <= 47;
|
||||
}
|
||||
|
||||
static inline bool sparx5_port_is_5g(int portno)
|
||||
{
|
||||
return portno <= 11 || portno == 64;
|
||||
}
|
||||
|
||||
static inline bool sparx5_port_is_10g(int portno)
|
||||
{
|
||||
return (portno >= 12 && portno <= 15) || (portno >= 48 && portno <= 55);
|
||||
}
|
||||
|
||||
static inline bool sparx5_port_is_25g(int portno)
|
||||
{
|
||||
return portno >= 56 && portno <= 63;
|
||||
}
|
||||
|
||||
static inline u32 sparx5_to_high_dev(int port)
|
||||
{
|
||||
if (sparx5_port_is_5g(port))
|
||||
return TARGET_DEV5G;
|
||||
if (sparx5_port_is_10g(port))
|
||||
return TARGET_DEV10G;
|
||||
return TARGET_DEV25G;
|
||||
}
|
||||
|
||||
static inline u32 sparx5_to_pcs_dev(int port)
|
||||
{
|
||||
if (sparx5_port_is_5g(port))
|
||||
return TARGET_PCS5G_BR;
|
||||
if (sparx5_port_is_10g(port))
|
||||
return TARGET_PCS10G_BR;
|
||||
return TARGET_PCS25G_BR;
|
||||
}
|
||||
|
||||
static inline int sparx5_port_dev_index(int port)
|
||||
{
|
||||
if (sparx5_port_is_2g5(port))
|
||||
return port;
|
||||
if (sparx5_port_is_5g(port))
|
||||
return (port <= 11 ? port : 12);
|
||||
if (sparx5_port_is_10g(port))
|
||||
return (port >= 12 && port <= 15) ?
|
||||
port - 12 : port - 44;
|
||||
return (port - 56);
|
||||
}
|
||||
|
||||
int sparx5_port_init(struct sparx5 *sparx5,
|
||||
struct sparx5_port *spx5_port,
|
||||
struct sparx5_port_config *conf);
|
||||
|
||||
int sparx5_port_config(struct sparx5 *sparx5,
|
||||
struct sparx5_port *spx5_port,
|
||||
struct sparx5_port_config *conf);
|
||||
|
||||
int sparx5_port_pcs_set(struct sparx5 *sparx5,
|
||||
struct sparx5_port *port,
|
||||
struct sparx5_port_config *conf);
|
||||
|
||||
int sparx5_serdes_set(struct sparx5 *sparx5,
|
||||
struct sparx5_port *spx5_port,
|
||||
struct sparx5_port_config *conf);
|
||||
|
||||
struct sparx5_port_status {
|
||||
bool link;
|
||||
bool link_down;
|
||||
int speed;
|
||||
bool an_complete;
|
||||
int duplex;
|
||||
int pause;
|
||||
};
|
||||
|
||||
int sparx5_get_port_status(struct sparx5 *sparx5,
|
||||
struct sparx5_port *port,
|
||||
struct sparx5_port_status *status);
|
||||
|
||||
void sparx5_port_enable(struct sparx5_port *port, bool enable);
|
||||
|
||||
#endif /* __SPARX5_PORT_H__ */
|
508
drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
Normal file
508
drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
Normal file
@ -0,0 +1,508 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/if_bridge.h>
|
||||
#include <net/switchdev.h>
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
|
||||
static struct workqueue_struct *sparx5_owq;
|
||||
|
||||
struct sparx5_switchdev_event_work {
|
||||
struct work_struct work;
|
||||
struct switchdev_notifier_fdb_info fdb_info;
|
||||
struct net_device *dev;
|
||||
unsigned long event;
|
||||
};
|
||||
|
||||
static void sparx5_port_attr_bridge_flags(struct sparx5_port *port,
|
||||
struct switchdev_brport_flags flags)
|
||||
{
|
||||
if (flags.mask & BR_MCAST_FLOOD)
|
||||
sparx5_pgid_update_mask(port, PGID_MC_FLOOD, true);
|
||||
}
|
||||
|
||||
static void sparx5_attr_stp_state_set(struct sparx5_port *port,
|
||||
u8 state)
|
||||
{
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
|
||||
if (!test_bit(port->portno, sparx5->bridge_mask)) {
|
||||
netdev_err(port->ndev,
|
||||
"Controlling non-bridged port %d?\n", port->portno);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case BR_STATE_FORWARDING:
|
||||
set_bit(port->portno, sparx5->bridge_fwd_mask);
|
||||
fallthrough;
|
||||
case BR_STATE_LEARNING:
|
||||
set_bit(port->portno, sparx5->bridge_lrn_mask);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* All other states treated as blocking */
|
||||
clear_bit(port->portno, sparx5->bridge_fwd_mask);
|
||||
clear_bit(port->portno, sparx5->bridge_lrn_mask);
|
||||
break;
|
||||
}
|
||||
|
||||
/* apply the bridge_fwd_mask to all the ports */
|
||||
sparx5_update_fwd(sparx5);
|
||||
}
|
||||
|
||||
static void sparx5_port_attr_ageing_set(struct sparx5_port *port,
|
||||
unsigned long ageing_clock_t)
|
||||
{
|
||||
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
|
||||
u32 ageing_time = jiffies_to_msecs(ageing_jiffies);
|
||||
|
||||
sparx5_set_ageing(port->sparx5, ageing_time);
|
||||
}
|
||||
|
||||
static int sparx5_port_attr_set(struct net_device *dev,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
|
||||
switch (attr->id) {
|
||||
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
|
||||
sparx5_port_attr_bridge_flags(port, attr->u.brport_flags);
|
||||
break;
|
||||
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
|
||||
sparx5_attr_stp_state_set(port, attr->u.stp_state);
|
||||
break;
|
||||
case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
|
||||
sparx5_port_attr_ageing_set(port, attr->u.ageing_time);
|
||||
break;
|
||||
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
|
||||
port->vlan_aware = attr->u.vlan_filtering;
|
||||
sparx5_vlan_port_apply(port->sparx5, port);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_port_bridge_join(struct sparx5_port *port,
|
||||
struct net_device *bridge)
|
||||
{
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
|
||||
if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
|
||||
/* First bridged port */
|
||||
sparx5->hw_bridge_dev = bridge;
|
||||
else
|
||||
if (sparx5->hw_bridge_dev != bridge)
|
||||
/* This is adding the port to a second bridge, this is
|
||||
* unsupported
|
||||
*/
|
||||
return -ENODEV;
|
||||
|
||||
set_bit(port->portno, sparx5->bridge_mask);
|
||||
|
||||
/* Port enters in bridge mode therefor don't need to copy to CPU
|
||||
* frames for multicast in case the bridge is not requesting them
|
||||
*/
|
||||
__dev_mc_unsync(port->ndev, sparx5_mc_unsync);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sparx5_port_bridge_leave(struct sparx5_port *port,
|
||||
struct net_device *bridge)
|
||||
{
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
|
||||
clear_bit(port->portno, sparx5->bridge_mask);
|
||||
if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
|
||||
sparx5->hw_bridge_dev = NULL;
|
||||
|
||||
/* Clear bridge vlan settings before updating the port settings */
|
||||
port->vlan_aware = 0;
|
||||
port->pvid = NULL_VID;
|
||||
port->vid = NULL_VID;
|
||||
|
||||
/* Port enters in host more therefore restore mc list */
|
||||
__dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync);
|
||||
}
|
||||
|
||||
static int sparx5_port_changeupper(struct net_device *dev,
|
||||
struct netdev_notifier_changeupper_info *info)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
int err = 0;
|
||||
|
||||
if (netif_is_bridge_master(info->upper_dev)) {
|
||||
if (info->linking)
|
||||
err = sparx5_port_bridge_join(port, info->upper_dev);
|
||||
else
|
||||
sparx5_port_bridge_leave(port, info->upper_dev);
|
||||
|
||||
sparx5_vlan_port_apply(port->sparx5, port);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sparx5_port_add_addr(struct net_device *dev, bool up)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
u16 vid = port->pvid;
|
||||
|
||||
if (up)
|
||||
sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, vid);
|
||||
else
|
||||
sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_netdevice_port_event(struct net_device *dev,
|
||||
struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!sparx5_netdevice_check(dev))
|
||||
return 0;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_CHANGEUPPER:
|
||||
err = sparx5_port_changeupper(dev, ptr);
|
||||
break;
|
||||
case NETDEV_PRE_UP:
|
||||
err = sparx5_port_add_addr(dev, true);
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
err = sparx5_port_add_addr(dev, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sparx5_netdevice_event(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
int ret = 0;
|
||||
|
||||
ret = sparx5_netdevice_port_event(dev, nb, event, ptr);
|
||||
|
||||
return notifier_from_errno(ret);
|
||||
}
|
||||
|
||||
static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)
|
||||
{
|
||||
struct sparx5_switchdev_event_work *switchdev_work =
|
||||
container_of(work, struct sparx5_switchdev_event_work, work);
|
||||
struct net_device *dev = switchdev_work->dev;
|
||||
struct switchdev_notifier_fdb_info *fdb_info;
|
||||
struct sparx5_port *port;
|
||||
struct sparx5 *sparx5;
|
||||
|
||||
rtnl_lock();
|
||||
if (!sparx5_netdevice_check(dev))
|
||||
goto out;
|
||||
|
||||
port = netdev_priv(dev);
|
||||
sparx5 = port->sparx5;
|
||||
|
||||
fdb_info = &switchdev_work->fdb_info;
|
||||
|
||||
switch (switchdev_work->event) {
|
||||
case SWITCHDEV_FDB_ADD_TO_DEVICE:
|
||||
if (!fdb_info->added_by_user)
|
||||
break;
|
||||
sparx5_add_mact_entry(sparx5, port, fdb_info->addr,
|
||||
fdb_info->vid);
|
||||
break;
|
||||
case SWITCHDEV_FDB_DEL_TO_DEVICE:
|
||||
if (!fdb_info->added_by_user)
|
||||
break;
|
||||
sparx5_del_mact_entry(sparx5, fdb_info->addr, fdb_info->vid);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
rtnl_unlock();
|
||||
kfree(switchdev_work->fdb_info.addr);
|
||||
kfree(switchdev_work);
|
||||
dev_put(dev);
|
||||
}
|
||||
|
||||
static void sparx5_schedule_work(struct work_struct *work)
|
||||
{
|
||||
queue_work(sparx5_owq, work);
|
||||
}
|
||||
|
||||
static int sparx5_switchdev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
|
||||
struct sparx5_switchdev_event_work *switchdev_work;
|
||||
struct switchdev_notifier_fdb_info *fdb_info;
|
||||
struct switchdev_notifier_info *info = ptr;
|
||||
int err;
|
||||
|
||||
switch (event) {
|
||||
case SWITCHDEV_PORT_ATTR_SET:
|
||||
err = switchdev_handle_port_attr_set(dev, ptr,
|
||||
sparx5_netdevice_check,
|
||||
sparx5_port_attr_set);
|
||||
return notifier_from_errno(err);
|
||||
case SWITCHDEV_FDB_ADD_TO_DEVICE:
|
||||
fallthrough;
|
||||
case SWITCHDEV_FDB_DEL_TO_DEVICE:
|
||||
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
|
||||
if (!switchdev_work)
|
||||
return NOTIFY_BAD;
|
||||
|
||||
switchdev_work->dev = dev;
|
||||
switchdev_work->event = event;
|
||||
|
||||
fdb_info = container_of(info,
|
||||
struct switchdev_notifier_fdb_info,
|
||||
info);
|
||||
INIT_WORK(&switchdev_work->work,
|
||||
sparx5_switchdev_bridge_fdb_event_work);
|
||||
memcpy(&switchdev_work->fdb_info, ptr,
|
||||
sizeof(switchdev_work->fdb_info));
|
||||
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
|
||||
if (!switchdev_work->fdb_info.addr)
|
||||
goto err_addr_alloc;
|
||||
|
||||
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
|
||||
fdb_info->addr);
|
||||
dev_hold(dev);
|
||||
|
||||
sparx5_schedule_work(&switchdev_work->work);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
err_addr_alloc:
|
||||
kfree(switchdev_work);
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
|
||||
static void sparx5_sync_port_dev_addr(struct sparx5 *sparx5,
|
||||
struct sparx5_port *port,
|
||||
u16 vid, bool add)
|
||||
{
|
||||
if (!port ||
|
||||
!test_bit(port->portno, sparx5->bridge_mask))
|
||||
return; /* Skip null/host interfaces */
|
||||
|
||||
/* Bridge connects to vid? */
|
||||
if (add) {
|
||||
/* Add port MAC address from the VLAN */
|
||||
sparx5_mact_learn(sparx5, PGID_CPU,
|
||||
port->ndev->dev_addr, vid);
|
||||
} else {
|
||||
/* Control port addr visibility depending on
|
||||
* port VLAN connectivity.
|
||||
*/
|
||||
if (test_bit(port->portno, sparx5->vlan_mask[vid]))
|
||||
sparx5_mact_learn(sparx5, PGID_CPU,
|
||||
port->ndev->dev_addr, vid);
|
||||
else
|
||||
sparx5_mact_forget(sparx5,
|
||||
port->ndev->dev_addr, vid);
|
||||
}
|
||||
}
|
||||
|
||||
static void sparx5_sync_bridge_dev_addr(struct net_device *dev,
|
||||
struct sparx5 *sparx5,
|
||||
u16 vid, bool add)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* First, handle bridge address'es */
|
||||
if (add) {
|
||||
sparx5_mact_learn(sparx5, PGID_CPU, dev->dev_addr,
|
||||
vid);
|
||||
sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
|
||||
vid);
|
||||
} else {
|
||||
sparx5_mact_forget(sparx5, dev->dev_addr, vid);
|
||||
sparx5_mact_forget(sparx5, dev->broadcast, vid);
|
||||
}
|
||||
|
||||
/* Now look at bridged ports */
|
||||
for (i = 0; i < SPX5_PORTS; i++)
|
||||
sparx5_sync_port_dev_addr(sparx5, sparx5->ports[i], vid, add);
|
||||
}
|
||||
|
||||
static int sparx5_handle_port_vlan_add(struct net_device *dev,
|
||||
struct notifier_block *nb,
|
||||
const struct switchdev_obj_port_vlan *v)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
|
||||
if (netif_is_bridge_master(dev)) {
|
||||
if (v->flags & BRIDGE_VLAN_INFO_BRENTRY) {
|
||||
struct sparx5 *sparx5 =
|
||||
container_of(nb, struct sparx5,
|
||||
switchdev_blocking_nb);
|
||||
|
||||
sparx5_sync_bridge_dev_addr(dev, sparx5, v->vid, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sparx5_netdevice_check(dev))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return sparx5_vlan_vid_add(port, v->vid,
|
||||
v->flags & BRIDGE_VLAN_INFO_PVID,
|
||||
v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
|
||||
}
|
||||
|
||||
static int sparx5_handle_port_obj_add(struct net_device *dev,
|
||||
struct notifier_block *nb,
|
||||
struct switchdev_notifier_port_obj_info *info)
|
||||
{
|
||||
const struct switchdev_obj *obj = info->obj;
|
||||
int err;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
||||
err = sparx5_handle_port_vlan_add(dev, nb,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj));
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
info->handled = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sparx5_handle_port_vlan_del(struct net_device *dev,
|
||||
struct notifier_block *nb,
|
||||
u16 vid)
|
||||
{
|
||||
struct sparx5_port *port = netdev_priv(dev);
|
||||
int ret;
|
||||
|
||||
/* Master bridge? */
|
||||
if (netif_is_bridge_master(dev)) {
|
||||
struct sparx5 *sparx5 =
|
||||
container_of(nb, struct sparx5,
|
||||
switchdev_blocking_nb);
|
||||
|
||||
sparx5_sync_bridge_dev_addr(dev, sparx5, vid, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sparx5_netdevice_check(dev))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = sparx5_vlan_vid_del(port, vid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Delete the port MAC address with the matching VLAN information */
|
||||
sparx5_mact_forget(port->sparx5, port->ndev->dev_addr, vid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparx5_handle_port_obj_del(struct net_device *dev,
|
||||
struct notifier_block *nb,
|
||||
struct switchdev_notifier_port_obj_info *info)
|
||||
{
|
||||
const struct switchdev_obj *obj = info->obj;
|
||||
int err;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
||||
err = sparx5_handle_port_vlan_del(dev, nb,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj)->vid);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
info->handled = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sparx5_switchdev_blocking_event(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
|
||||
int err;
|
||||
|
||||
switch (event) {
|
||||
case SWITCHDEV_PORT_OBJ_ADD:
|
||||
err = sparx5_handle_port_obj_add(dev, nb, ptr);
|
||||
return notifier_from_errno(err);
|
||||
case SWITCHDEV_PORT_OBJ_DEL:
|
||||
err = sparx5_handle_port_obj_del(dev, nb, ptr);
|
||||
return notifier_from_errno(err);
|
||||
case SWITCHDEV_PORT_ATTR_SET:
|
||||
err = switchdev_handle_port_attr_set(dev, ptr,
|
||||
sparx5_netdevice_check,
|
||||
sparx5_port_attr_set);
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
int sparx5_register_notifier_blocks(struct sparx5 *s5)
|
||||
{
|
||||
int err;
|
||||
|
||||
s5->netdevice_nb.notifier_call = sparx5_netdevice_event;
|
||||
err = register_netdevice_notifier(&s5->netdevice_nb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
s5->switchdev_nb.notifier_call = sparx5_switchdev_event;
|
||||
err = register_switchdev_notifier(&s5->switchdev_nb);
|
||||
if (err)
|
||||
goto err_switchdev_nb;
|
||||
|
||||
s5->switchdev_blocking_nb.notifier_call = sparx5_switchdev_blocking_event;
|
||||
err = register_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
|
||||
if (err)
|
||||
goto err_switchdev_blocking_nb;
|
||||
|
||||
sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0);
|
||||
if (!sparx5_owq)
|
||||
goto err_switchdev_blocking_nb;
|
||||
|
||||
return 0;
|
||||
|
||||
err_switchdev_blocking_nb:
|
||||
unregister_switchdev_notifier(&s5->switchdev_nb);
|
||||
err_switchdev_nb:
|
||||
unregister_netdevice_notifier(&s5->netdevice_nb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void sparx5_unregister_notifier_blocks(struct sparx5 *s5)
|
||||
{
|
||||
destroy_workqueue(sparx5_owq);
|
||||
|
||||
unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
|
||||
unregister_switchdev_notifier(&s5->switchdev_nb);
|
||||
unregister_netdevice_notifier(&s5->netdevice_nb);
|
||||
}
|
224
drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
Normal file
224
drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
Normal file
@ -0,0 +1,224 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip Sparx5 Switch driver
|
||||
*
|
||||
* Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include "sparx5_main_regs.h"
|
||||
#include "sparx5_main.h"
|
||||
|
||||
static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
|
||||
{
|
||||
u32 mask[3];
|
||||
|
||||
/* Divide up mask in 32 bit words */
|
||||
bitmap_to_arr32(mask, sparx5->vlan_mask[vid], SPX5_PORTS);
|
||||
|
||||
/* Output mask to respective registers */
|
||||
spx5_wr(mask[0], sparx5, ANA_L3_VLAN_MASK_CFG(vid));
|
||||
spx5_wr(mask[1], sparx5, ANA_L3_VLAN_MASK_CFG1(vid));
|
||||
spx5_wr(mask[2], sparx5, ANA_L3_VLAN_MASK_CFG2(vid));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sparx5_vlan_init(struct sparx5 *sparx5)
|
||||
{
|
||||
u16 vid;
|
||||
|
||||
spx5_rmw(ANA_L3_VLAN_CTRL_VLAN_ENA_SET(1),
|
||||
ANA_L3_VLAN_CTRL_VLAN_ENA,
|
||||
sparx5,
|
||||
ANA_L3_VLAN_CTRL);
|
||||
|
||||
/* Map VLAN = FID */
|
||||
for (vid = NULL_VID; vid < VLAN_N_VID; vid++)
|
||||
spx5_rmw(ANA_L3_VLAN_CFG_VLAN_FID_SET(vid),
|
||||
ANA_L3_VLAN_CFG_VLAN_FID,
|
||||
sparx5,
|
||||
ANA_L3_VLAN_CFG(vid));
|
||||
}
|
||||
|
||||
void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno)
|
||||
{
|
||||
struct sparx5_port *port = sparx5->ports[portno];
|
||||
|
||||
/* Configure PVID */
|
||||
spx5_rmw(ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(0) |
|
||||
ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid),
|
||||
ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA |
|
||||
ANA_CL_VLAN_CTRL_PORT_VID,
|
||||
sparx5,
|
||||
ANA_CL_VLAN_CTRL(port->portno));
|
||||
}
|
||||
|
||||
int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
|
||||
bool untagged)
|
||||
{
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
int ret;
|
||||
|
||||
/* Make the port a member of the VLAN */
|
||||
set_bit(port->portno, sparx5->vlan_mask[vid]);
|
||||
ret = sparx5_vlant_set_mask(sparx5, vid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Default ingress vlan classification */
|
||||
if (pvid)
|
||||
port->pvid = vid;
|
||||
|
||||
/* Untagged egress vlan classification */
|
||||
if (untagged && port->vid != vid) {
|
||||
if (port->vid) {
|
||||
netdev_err(port->ndev,
|
||||
"Port already has a native VLAN: %d\n",
|
||||
port->vid);
|
||||
return -EBUSY;
|
||||
}
|
||||
port->vid = vid;
|
||||
}
|
||||
|
||||
sparx5_vlan_port_apply(sparx5, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid)
|
||||
{
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
int ret;
|
||||
|
||||
/* 8021q removes VID 0 on module unload for all interfaces
|
||||
* with VLAN filtering feature. We need to keep it to receive
|
||||
* untagged traffic.
|
||||
*/
|
||||
if (vid == 0)
|
||||
return 0;
|
||||
|
||||
/* Stop the port from being a member of the vlan */
|
||||
clear_bit(port->portno, sparx5->vlan_mask[vid]);
|
||||
ret = sparx5_vlant_set_mask(sparx5, vid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Ingress */
|
||||
if (port->pvid == vid)
|
||||
port->pvid = 0;
|
||||
|
||||
/* Egress */
|
||||
if (port->vid == vid)
|
||||
port->vid = 0;
|
||||
|
||||
sparx5_vlan_port_apply(sparx5, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable)
|
||||
{
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
u32 val, mask;
|
||||
|
||||
/* mask is spread across 3 registers x 32 bit */
|
||||
if (port->portno < 32) {
|
||||
mask = BIT(port->portno);
|
||||
val = enable ? mask : 0;
|
||||
spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG(pgid));
|
||||
} else if (port->portno < 64) {
|
||||
mask = BIT(port->portno - 32);
|
||||
val = enable ? mask : 0;
|
||||
spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG1(pgid));
|
||||
} else if (port->portno < SPX5_PORTS) {
|
||||
mask = BIT(port->portno - 64);
|
||||
val = enable ? mask : 0;
|
||||
spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG2(pgid));
|
||||
} else {
|
||||
netdev_err(port->ndev, "Invalid port no: %d\n", port->portno);
|
||||
}
|
||||
}
|
||||
|
||||
void sparx5_update_fwd(struct sparx5 *sparx5)
|
||||
{
|
||||
DECLARE_BITMAP(workmask, SPX5_PORTS);
|
||||
u32 mask[3];
|
||||
int port;
|
||||
|
||||
/* Divide up fwd mask in 32 bit words */
|
||||
bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
|
||||
|
||||
/* Update flood masks */
|
||||
for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
|
||||
spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
|
||||
spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
|
||||
spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
|
||||
}
|
||||
|
||||
/* Update SRC masks */
|
||||
for (port = 0; port < SPX5_PORTS; port++) {
|
||||
if (test_bit(port, sparx5->bridge_fwd_mask)) {
|
||||
/* Allow to send to all bridged but self */
|
||||
bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
|
||||
clear_bit(port, workmask);
|
||||
bitmap_to_arr32(mask, workmask, SPX5_PORTS);
|
||||
spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
|
||||
spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
|
||||
spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
|
||||
} else {
|
||||
spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
|
||||
spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
|
||||
spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
|
||||
}
|
||||
}
|
||||
|
||||
/* Learning enabled only for bridged ports */
|
||||
bitmap_and(workmask, sparx5->bridge_fwd_mask,
|
||||
sparx5->bridge_lrn_mask, SPX5_PORTS);
|
||||
bitmap_to_arr32(mask, workmask, SPX5_PORTS);
|
||||
|
||||
/* Apply learning mask */
|
||||
spx5_wr(mask[0], sparx5, ANA_L2_AUTO_LRN_CFG);
|
||||
spx5_wr(mask[1], sparx5, ANA_L2_AUTO_LRN_CFG1);
|
||||
spx5_wr(mask[2], sparx5, ANA_L2_AUTO_LRN_CFG2);
|
||||
}
|
||||
|
||||
void sparx5_vlan_port_apply(struct sparx5 *sparx5,
|
||||
struct sparx5_port *port)
|
||||
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Configure PVID, vlan aware */
|
||||
val = ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(port->vlan_aware) |
|
||||
ANA_CL_VLAN_CTRL_VLAN_POP_CNT_SET(port->vlan_aware) |
|
||||
ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid);
|
||||
spx5_wr(val, sparx5, ANA_CL_VLAN_CTRL(port->portno));
|
||||
|
||||
val = 0;
|
||||
if (port->vlan_aware && !port->pvid)
|
||||
/* If port is vlan-aware and tagged, drop untagged and
|
||||
* priority tagged frames.
|
||||
*/
|
||||
val = ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA_SET(1) |
|
||||
ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS_SET(1) |
|
||||
ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS_SET(1);
|
||||
spx5_wr(val, sparx5,
|
||||
ANA_CL_VLAN_FILTER_CTRL(port->portno, 0));
|
||||
|
||||
/* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q */
|
||||
val = REW_TAG_CTRL_TAG_TPID_CFG_SET(0);
|
||||
if (port->vlan_aware) {
|
||||
if (port->vid)
|
||||
/* Tag all frames except when VID == DEFAULT_VLAN */
|
||||
val |= REW_TAG_CTRL_TAG_CFG_SET(1);
|
||||
else
|
||||
val |= REW_TAG_CTRL_TAG_CFG_SET(3);
|
||||
}
|
||||
spx5_wr(val, sparx5, REW_TAG_CTRL(port->portno));
|
||||
|
||||
/* Egress VID */
|
||||
spx5_rmw(REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
|
||||
REW_PORT_VLAN_CFG_PORT_VID,
|
||||
sparx5,
|
||||
REW_PORT_VLAN_CFG(port->portno));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user