diff options
233 files changed, 10814 insertions, 4597 deletions
diff --git a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml index 64c20c92c07d..85fefe3a0444 100644 --- a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml @@ -22,6 +22,7 @@ select: - amlogic,meson8m2-dwmac - amlogic,meson-gxbb-dwmac - amlogic,meson-axg-dwmac + - amlogic,meson-g12a-dwmac required: - compatible @@ -36,6 +37,7 @@ allOf: - amlogic,meson8m2-dwmac - amlogic,meson-gxbb-dwmac - amlogic,meson-axg-dwmac + - amlogic,meson-g12a-dwmac then: properties: @@ -95,6 +97,7 @@ properties: - amlogic,meson8m2-dwmac - amlogic,meson-gxbb-dwmac - amlogic,meson-axg-dwmac + - amlogic,meson-g12a-dwmac contains: enum: - snps,dwmac-3.70a diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml index 9b1f1147ca36..a9e547ac7905 100644 --- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml @@ -162,6 +162,18 @@ properties: description: Specifies a reference to a node representing a SFP cage. + rx-internal-delay-ps: + description: | + RGMII Receive PHY Clock Delay defined in pico seconds. This is used for + PHY's that have configurable RX internal delays. If this property is + present then the PHY applies the RX delay. + + tx-internal-delay-ps: + description: | + RGMII Transmit PHY Clock Delay defined in pico seconds. This is used for + PHY's that have configurable TX internal delays. If this property is + present then the PHY applies the TX delay. + required: - reg diff --git a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt index 5ff37c68c941..87a27d775d48 100644 --- a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt +++ b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt @@ -31,6 +31,8 @@ Optional properties: VSC8531_LINK_100_ACTIVITY (2), VSC8531_LINK_ACTIVITY (0) and VSC8531_DUPLEX_COLLISION (8). +- load-save-gpios : GPIO used for the load/save operation of the PTP + hardware clock (PHC). Table: 1 - Edge rate change @@ -67,4 +69,5 @@ Example: vsc8531,edge-slowdown = <7>; vsc8531,led-0-mode = <LINK_1000_ACTIVITY>; vsc8531,led-1-mode = <LINK_100_ACTIVITY>; + load-save-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>; }; diff --git a/Documentation/devicetree/bindings/net/ti,dp83869.yaml b/Documentation/devicetree/bindings/net/ti,dp83869.yaml index 5b69ef03bbf7..71e90a3e4652 100644 --- a/Documentation/devicetree/bindings/net/ti,dp83869.yaml +++ b/Documentation/devicetree/bindings/net/ti,dp83869.yaml @@ -8,7 +8,7 @@ $schema: "http://devicetree.org/meta-schemas/core.yaml#" title: TI DP83869 ethernet PHY allOf: - - $ref: "ethernet-controller.yaml#" + - $ref: "ethernet-phy.yaml#" maintainers: - Dan Murphy <dmurphy@ti.com> @@ -64,6 +64,18 @@ properties: Operational mode for the PHY. If this is not set then the operational mode is set by the straps. see dt-bindings/net/ti-dp83869.h for values + rx-internal-delay-ps: + description: Delay is in pico seconds + enum: [ 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000 ] + default: 2000 + + tx-internal-delay-ps: + description: Delay is in pico seconds + enum: [ 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000 ] + default: 2000 + required: - reg @@ -80,5 +92,7 @@ examples: ti,op-mode = <DP83869_RGMII_COPPER_ETHERNET>; ti,max-output-impedance = "true"; ti,clk-output-sel = <DP83869_CLK_O_SEL_CHN_A_RCLK>; + rx-internal-delay-ps = <2000>; + tx-internal-delay-ps = <2000>; }; }; diff --git a/Documentation/networking/devlink/devlink-info.rst b/Documentation/networking/devlink/devlink-info.rst index 3fe11401b838..7572bf6de5c1 100644 --- a/Documentation/networking/devlink/devlink-info.rst +++ b/Documentation/networking/devlink/devlink-info.rst @@ -44,9 +44,11 @@ versions is generally discouraged - here, and via any other Linux API. reported for two ports of the same device or on two hosts of a multi-host device should be identical. - .. note:: ``devlink-info`` API should be extended with a new field - if devices want to report board/product serial number (often - reported in PCI *Vital Product Data* capability). + * - ``board.serial_number`` + - Board serial number of the device. + + This is usually the serial number of the board, often available in + PCI *Vital Product Data*. * - ``fixed`` - Group for hardware identifiers, and versions of components @@ -201,10 +203,6 @@ Future work The following extensions could be useful: - - product serial number - NIC boards often get labeled with a board serial - number rather than ASIC serial number; it'd be useful to add board serial - numbers to the API if they can be retrieved from the device; - - on-disk firmware file names - drivers list the file names of firmware they may need to load onto devices via the ``MODULE_FIRMWARE()`` macro. These, however, are per module, rather than per device. It'd be useful to list diff --git a/arch/mips/boot/dts/mscc/ocelot_pcb120.dts b/arch/mips/boot/dts/mscc/ocelot_pcb120.dts index 33991fd209f5..897de5025d7f 100644 --- a/arch/mips/boot/dts/mscc/ocelot_pcb120.dts +++ b/arch/mips/boot/dts/mscc/ocelot_pcb120.dts @@ -3,6 +3,7 @@ /dts-v1/; +#include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/phy/phy-ocelot-serdes.h> #include "ocelot.dtsi" @@ -25,6 +26,11 @@ pins = "GPIO_4"; function = "gpio"; }; + + phy_load_save_pins: phy_load_save_pins { + pins = "GPIO_10"; + function = "ptp2"; + }; }; &mdio0 { @@ -34,27 +40,31 @@ &mdio1 { status = "okay"; pinctrl-names = "default"; - pinctrl-0 = <&miim1>, <&phy_int_pins>; + pinctrl-0 = <&miim1>, <&phy_int_pins>, <&phy_load_save_pins>; phy7: ethernet-phy@0 { reg = <0>; interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&gpio>; + load-save-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>; }; phy6: ethernet-phy@1 { reg = <1>; interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&gpio>; + load-save-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>; }; phy5: ethernet-phy@2 { reg = <2>; interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&gpio>; + load-save-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>; }; phy4: ethernet-phy@3 { reg = <3>; interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&gpio>; + load-save-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>; }; }; diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c index 621364bb6b12..4274906f8654 100644 --- a/drivers/isdn/hardware/mISDN/hfcsusb.c +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -261,8 +261,7 @@ hfcsusb_ph_info(struct hfcsusb *hw) phi->bch[i].Flags = hw->bch[i].Flags; } _queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY, - sizeof(struct ph_info_dch) + dch->dev.nrbchan * - sizeof(struct ph_info_ch), phi, GFP_ATOMIC); + struct_size(phi, bch, dch->dev.nrbchan), phi, GFP_ATOMIC); kfree(phi); } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 004919aea5fb..4ef99efc37f6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -79,6 +79,7 @@ #include <net/pkt_sched.h> #include <linux/rculist.h> #include <net/flow_dissector.h> +#include <net/xfrm.h> #include <net/bonding.h> #include <net/bond_3ad.h> #include <net/bond_alb.h> @@ -278,8 +279,6 @@ const char *bond_mode_name(int mode) return names[mode]; } -/*---------------------------------- VLAN -----------------------------------*/ - /** * bond_dev_queue_xmit - Prepare skb for xmit. * @@ -302,6 +301,8 @@ netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, return dev_queue_xmit(skb); } +/*---------------------------------- VLAN -----------------------------------*/ + /* In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid, * We don't protect the slave list iteration with a lock because: * a. This operation is performed in IOCTL context, @@ -372,6 +373,84 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, return 0; } +/*---------------------------------- XFRM -----------------------------------*/ + +#ifdef CONFIG_XFRM_OFFLOAD +/** + * bond_ipsec_add_sa - program device with a security association + * @xs: pointer to transformer state struct + **/ +static int bond_ipsec_add_sa(struct xfrm_state *xs) +{ + struct net_device *bond_dev = xs->xso.dev; + struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave = rtnl_dereference(bond->curr_active_slave); + + xs->xso.real_dev = slave->dev; + bond->xs = xs; + + if (!(slave->dev->xfrmdev_ops + && slave->dev->xfrmdev_ops->xdo_dev_state_add)) { + slave_warn(bond_dev, slave->dev, "Slave does not support ipsec offload\n"); + return -EINVAL; + } + + return slave->dev->xfrmdev_ops->xdo_dev_state_add(xs); +} + +/** + * bond_ipsec_del_sa - clear out this specific SA + * @xs: pointer to transformer state struct + **/ +static void bond_ipsec_del_sa(struct xfrm_state *xs) +{ + struct net_device *bond_dev = xs->xso.dev; + struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave = rtnl_dereference(bond->curr_active_slave); + + if (!slave) + return; + + xs->xso.real_dev = slave->dev; + + if (!(slave->dev->xfrmdev_ops + && slave->dev->xfrmdev_ops->xdo_dev_state_delete)) { + slave_warn(bond_dev, slave->dev, "%s: no slave xdo_dev_state_delete\n", __func__); + return; + } + + slave->dev->xfrmdev_ops->xdo_dev_state_delete(xs); +} + +/** + * bond_ipsec_offload_ok - can this packet use the xfrm hw offload + * @skb: current data packet + * @xs: pointer to transformer state struct + **/ +static bool bond_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs) +{ + struct net_device *bond_dev = xs->xso.dev; + struct bonding *bond = netdev_priv(bond_dev); + struct slave *curr_active = rtnl_dereference(bond->curr_active_slave); + struct net_device *slave_dev = curr_active->dev; + + if (!(slave_dev->xfrmdev_ops + && slave_dev->xfrmdev_ops->xdo_dev_offload_ok)) { + slave_warn(bond_dev, slave_dev, "%s: no slave xdo_dev_offload_ok\n", __func__); + return false; + } + + xs->xso.real_dev = slave_dev; + return slave_dev->xfrmdev_ops->xdo_dev_offload_ok(skb, xs); +} + +static const struct xfrmdev_ops bond_xfrmdev_ops = { + .xdo_dev_state_add = bond_ipsec_add_sa, + .xdo_dev_state_delete = bond_ipsec_del_sa, + .xdo_dev_offload_ok = bond_ipsec_offload_ok, +}; +#endif /* CONFIG_XFRM_OFFLOAD */ + /*------------------------------- Link status -------------------------------*/ /* Set the carrier state for the master according to the state of its @@ -879,6 +958,11 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) return; if (new_active) { +#ifdef CONFIG_XFRM_OFFLOAD + if ((BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) && bond->xs) + bond_ipsec_del_sa(bond->xs); +#endif /* CONFIG_XFRM_OFFLOAD */ + new_active->last_link_up = jiffies; if (new_active->link == BOND_LINK_BACK) { @@ -941,6 +1025,13 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) bond_should_notify_peers(bond); } +#ifdef CONFIG_XFRM_OFFLOAD + if (old_active && bond->xs) { + xfrm_dev_state_flush(dev_net(bond->dev), bond->dev, true); + bond_ipsec_add_sa(bond->xs); + } +#endif /* CONFIG_XFRM_OFFLOAD */ + call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, bond->dev); if (should_notify_peers) { bond->send_peer_notif--; @@ -1127,15 +1218,24 @@ static netdev_features_t bond_fix_features(struct net_device *dev, #define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ NETIF_F_RXCSUM | NETIF_F_ALL_TSO) +#ifdef CONFIG_XFRM_OFFLOAD +#define BOND_XFRM_FEATURES (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \ + NETIF_F_GSO_ESP) +#endif /* CONFIG_XFRM_OFFLOAD */ + #define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ NETIF_F_ALL_TSO) + static void bond_compute_features(struct bonding *bond) { unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; netdev_features_t vlan_features = BOND_VLAN_FEATURES; netdev_features_t enc_features = BOND_ENC_FEATURES; +#ifdef CONFIG_XFRM_OFFLOAD + netdev_features_t xfrm_features = BOND_XFRM_FEATURES; +#endif /* CONFIG_XFRM_OFFLOAD */ netdev_features_t mpls_features = BOND_MPLS_FEATURES; struct net_device *bond_dev = bond->dev; struct list_head *iter; @@ -1157,6 +1257,12 @@ static void bond_compute_features(struct bonding *bond) slave->dev->hw_enc_features, BOND_ENC_FEATURES); +#ifdef CONFIG_XFRM_OFFLOAD + xfrm_features = netdev_increment_features(xfrm_features, + slave->dev->hw_enc_features, + BOND_XFRM_FEATURES); +#endif /* CONFIG_XFRM_OFFLOAD */ + mpls_features = netdev_increment_features(mpls_features, slave->dev->mpls_features, BOND_MPLS_FEATURES); @@ -1176,6 +1282,9 @@ done: NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX | NETIF_F_GSO_UDP_L4; +#ifdef CONFIG_XFRM_OFFLOAD + bond_dev->hw_enc_features |= xfrm_features; +#endif /* CONFIG_XFRM_OFFLOAD */ bond_dev->mpls_features = mpls_features; bond_dev->gso_max_segs = gso_max_segs; netif_set_gso_max_size(bond_dev, gso_max_size); @@ -1464,6 +1573,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, slave_dbg(bond_dev, slave_dev, "is !NETIF_F_VLAN_CHALLENGED\n"); } + if (slave_dev->features & NETIF_F_HW_ESP) + slave_dbg(bond_dev, slave_dev, "is esp-hw-offload capable\n"); + /* Old ifenslave binaries are no longer supported. These can * be identified with moderate accuracy by the state of the slave: * the current ifenslave will set the interface down prior to @@ -4540,6 +4652,13 @@ void bond_setup(struct net_device *bond_dev) bond_dev->priv_flags |= IFF_BONDING | IFF_UNICAST_FLT | IFF_NO_QUEUE; bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); +#ifdef CONFIG_XFRM_OFFLOAD + /* set up xfrm device ops (only supported in active-backup right now) */ + if ((BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)) + bond_dev->xfrmdev_ops = &bond_xfrmdev_ops; + bond->xs = NULL; +#endif /* CONFIG_XFRM_OFFLOAD */ + /* don't acquire bond device's netif_tx_lock when transmitting */ bond_dev->features |= NETIF_F_LLTX; @@ -4558,6 +4677,10 @@ void bond_setup(struct net_device *bond_dev) NETIF_F_HW_VLAN_CTAG_FILTER; bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL | NETIF_F_GSO_UDP_L4; +#ifdef CONFIG_XFRM_OFFLOAD + if ((BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)) + bond_dev->hw_features |= BOND_XFRM_FEATURES; +#endif /* CONFIG_XFRM_OFFLOAD */ bond_dev->features |= bond_dev->hw_features; bond_dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; } diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig index a5b7cca03d09..3d3c2a6fb0c0 100644 --- a/drivers/net/dsa/ocelot/Kconfig +++ b/drivers/net/dsa/ocelot/Kconfig @@ -4,7 +4,9 @@ config NET_DSA_MSCC_FELIX depends on NET_DSA && PCI depends on NET_VENDOR_MICROSEMI depends on NET_VENDOR_FREESCALE - select MSCC_OCELOT_SWITCH + depends on HAS_IOMEM + depends on REGMAP_MMIO + select MSCC_OCELOT_SWITCH_LIB select NET_DSA_TAG_OCELOT select FSL_ENETC_MDIO help diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 66648986e6e3..25046777c993 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -59,6 +59,29 @@ static int felix_fdb_del(struct dsa_switch *ds, int port, return ocelot_fdb_del(ocelot, port, addr, vid); } +/* This callback needs to be present */ +static int felix_mdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + return 0; +} + +static void felix_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct ocelot *ocelot = ds->priv; + + ocelot_port_mdb_add(ocelot, port, mdb); +} + +static int felix_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_mdb_del(ocelot, port, mdb); +} + static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port, u8 state) { @@ -771,6 +794,9 @@ static const struct dsa_switch_ops felix_switch_ops = { .port_fdb_dump = felix_fdb_dump, .port_fdb_add = felix_fdb_add, .port_fdb_del = felix_fdb_del, + .port_mdb_prepare = felix_mdb_prepare, + .port_mdb_add = felix_mdb_add, + .port_mdb_del = felix_mdb_del, .port_bridge_join = felix_bridge_join, .port_bridge_leave = felix_bridge_leave, .port_stp_state_set = felix_bridge_stp_state_set, diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 1dd9e348152d..2067776773f7 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -557,7 +557,7 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = { { .offset = 0x111, .name = "drop_green_prio_7", }, }; -struct vcap_field vsc9959_vcap_is2_keys[] = { +static struct vcap_field vsc9959_vcap_is2_keys[] = { /* Common: 41 bits */ [VCAP_IS2_TYPE] = { 0, 4}, [VCAP_IS2_HK_FIRST] = { 4, 1}, @@ -637,7 +637,7 @@ struct vcap_field vsc9959_vcap_is2_keys[] = { [VCAP_IS2_HK_OAM_IS_Y1731] = {182, 1}, }; -struct vcap_field vsc9959_vcap_is2_actions[] = { +static struct vcap_field vsc9959_vcap_is2_actions[] = { [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 7c86056b9401..e24a99031b80 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -97,8 +97,7 @@ (AR9331_SW_PORT_STATUS_TXMAC | AR9331_SW_PORT_STATUS_RXMAC) #define AR9331_SW_PORT_STATUS_LINK_MASK \ - (AR9331_SW_PORT_STATUS_LINK_EN | AR9331_SW_PORT_STATUS_FLOW_LINK_EN | \ - AR9331_SW_PORT_STATUS_DUPLEX_MODE | \ + (AR9331_SW_PORT_STATUS_DUPLEX_MODE | \ AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \ AR9331_SW_PORT_STATUS_SPEED_M) @@ -410,33 +409,10 @@ static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port, struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct regmap *regmap = priv->regmap; int ret; - u32 val; - - switch (state->speed) { - case SPEED_1000: - val = AR9331_SW_PORT_STATUS_SPEED_1000; - break; - case SPEED_100: - val = AR9331_SW_PORT_STATUS_SPEED_100; - break; - case SPEED_10: - val = AR9331_SW_PORT_STATUS_SPEED_10; - break; - default: - return; - } - - if (state->duplex) - val |= AR9331_SW_PORT_STATUS_DUPLEX_MODE; - - if (state->pause & MLO_PAUSE_TX) - val |= AR9331_SW_PORT_STATUS_TX_FLOW_EN; - - if (state->pause & MLO_PAUSE_RX) - val |= AR9331_SW_PORT_STATUS_RX_FLOW_EN; ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), - AR9331_SW_PORT_STATUS_LINK_MASK, val); + AR9331_SW_PORT_STATUS_LINK_EN | + AR9331_SW_PORT_STATUS_FLOW_LINK_EN, 0); if (ret) dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); } @@ -464,11 +440,37 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, { struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct regmap *regmap = priv->regmap; + u32 val; int ret; + val = AR9331_SW_PORT_STATUS_MAC_MASK; + switch (speed) { + case SPEED_1000: + val |= AR9331_SW_PORT_STATUS_SPEED_1000; + break; + case SPEED_100: + val |= AR9331_SW_PORT_STATUS_SPEED_100; + break; + case SPEED_10: + val |= AR9331_SW_PORT_STATUS_SPEED_10; + break; + default: + return; + } + + if (duplex) + val |= AR9331_SW_PORT_STATUS_DUPLEX_MODE; + + if (tx_pause) + val |= AR9331_SW_PORT_STATUS_TX_FLOW_EN; + + if (rx_pause) + val |= AR9331_SW_PORT_STATUS_RX_FLOW_EN; + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), - AR9331_SW_PORT_STATUS_MAC_MASK, - AR9331_SW_PORT_STATUS_MAC_MASK); + AR9331_SW_PORT_STATUS_MAC_MASK | + AR9331_SW_PORT_STATUS_LINK_MASK, + val); if (ret) dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); } diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index d2b5ab403e06..4acad5fa0c84 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -14,6 +14,7 @@ #include <linux/of_platform.h> #include <linux/if_bridge.h> #include <linux/mdio.h> +#include <linux/phylink.h> #include <linux/gpio/consumer.h> #include <linux/etherdevice.h> @@ -418,55 +419,6 @@ qca8k_mib_init(struct qca8k_priv *priv) mutex_unlock(&priv->reg_mutex); } -static int -qca8k_set_pad_ctrl(struct qca8k_priv *priv, int port, int mode) -{ - u32 reg, val; - - switch (port) { - case 0: - reg = QCA8K_REG_PORT0_PAD_CTRL; - break; - case 6: - reg = QCA8K_REG_PORT6_PAD_CTRL; - break; - default: - pr_err("Can't set PAD_CTRL on port %d\n", port); - return -EINVAL; - } - - /* Configure a port to be directly connected to an external - * PHY or MAC. - */ - switch (mode) { - case PHY_INTERFACE_MODE_RGMII: - /* RGMII mode means no delay so don't enable the delay */ - val = QCA8K_PORT_PAD_RGMII_EN; - qca8k_write(priv, reg, val); - break; - case PHY_INTERFACE_MODE_RGMII_ID: - /* RGMII_ID needs internal delay. This is enabled through - * PORT5_PAD_CTRL for all ports, rather than individual port - * registers - */ - qca8k_write(priv, reg, - QCA8K_PORT_PAD_RGMII_EN | - QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | - QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); - qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, - QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); - break; - case PHY_INTERFACE_MODE_SGMII: - qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); - break; - default: - pr_err("xMII mode %d not supported\n", mode); - return -EINVAL; - } - - return 0; -} - static void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) { @@ -639,9 +591,7 @@ static int qca8k_setup(struct dsa_switch *ds) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - phy_interface_t phy_mode = PHY_INTERFACE_MODE_NA; int ret, i; - u32 mask; /* Make sure that port 0 is the cpu port */ if (!dsa_is_cpu_port(ds, 0)) { @@ -661,24 +611,9 @@ qca8k_setup(struct dsa_switch *ds) if (ret) return ret; - /* Initialize CPU port pad mode (xMII type, delays...) */ - ret = of_get_phy_mode(dsa_to_port(ds, QCA8K_CPU_PORT)->dn, &phy_mode); - if (ret) { - pr_err("Can't find phy-mode for master device\n"); - return ret; - } - ret = qca8k_set_pad_ctrl(priv, QCA8K_CPU_PORT, phy_mode); - if (ret < 0) - return ret; - - /* Enable CPU Port, force it to maximum bandwidth and full-duplex */ - mask = QCA8K_PORT_STATUS_SPEED_1000 | QCA8K_PORT_STATUS_TXFLOW | - QCA8K_PORT_STATUS_RXFLOW | QCA8K_PORT_STATUS_DUPLEX; - qca8k_write(priv, QCA8K_REG_PORT_STATUS(QCA8K_CPU_PORT), mask); + /* Enable CPU Port */ qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); - qca8k_port_set_status(priv, QCA8K_CPU_PORT, 1); - priv->port_sts[QCA8K_CPU_PORT].enabled = 1; /* Enable MIB counters */ qca8k_mib_init(priv); @@ -693,10 +628,9 @@ qca8k_setup(struct dsa_switch *ds) qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), QCA8K_PORT_LOOKUP_MEMBER, 0); - /* Disable MAC by default on all user ports */ + /* Disable MAC by default on all ports */ for (i = 1; i < QCA8K_NUM_PORTS; i++) - if (dsa_is_user_port(ds, i)) - qca8k_port_set_status(priv, i, 0); + qca8k_port_set_status(priv, i, 0); /* Forward all unknown frames to CPU port for Linux processing */ qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, @@ -713,7 +647,7 @@ qca8k_setup(struct dsa_switch *ds) QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); } - /* Invividual user ports get connected to CPU port only */ + /* Individual user ports get connected to CPU port only */ if (dsa_is_user_port(ds, i)) { int shift = 16 * (i % 2); @@ -739,48 +673,257 @@ qca8k_setup(struct dsa_switch *ds) /* Flush the FDB table */ qca8k_fdb_flush(priv); + /* We don't have interrupts for link changes, so we need to poll */ + ds->pcs_poll = true; + return 0; } static void -qca8k_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phy) +qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + const struct phylink_link_state *state) { struct qca8k_priv *priv = ds->priv; - u32 reg; + u32 reg, val; + + switch (port) { + case 0: /* 1st CPU port */ + if (state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII) + return; + + reg = QCA8K_REG_PORT0_PAD_CTRL; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + /* Internal PHY, nothing to do */ + return; + case 6: /* 2nd CPU port / external PHY */ + if (state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX) + return; + + reg = QCA8K_REG_PORT6_PAD_CTRL; + break; + default: + dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); + return; + } + + if (port != 6 && phylink_autoneg_inband(mode)) { + dev_err(ds->dev, "%s: in-band negotiation unsupported\n", + __func__); + return; + } + + switch (state->interface) { + case PHY_INTERFACE_MODE_RGMII: + /* RGMII mode means no delay so don't enable the delay */ + qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); + break; + case PHY_INTERFACE_MODE_RGMII_ID: + /* RGMII_ID needs internal delay. This is enabled through + * PORT5_PAD_CTRL for all ports, rather than individual port + * registers + */ + qca8k_write(priv, reg, + QCA8K_PORT_PAD_RGMII_EN | + QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | + QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); + qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, + QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + /* Enable SGMII on the port */ + qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); - /* Force fixed-link setting for CPU port, skip others. */ - if (!phy_is_pseudo_fixed_link(phy)) + /* Enable/disable SerDes auto-negotiation as necessary */ + val = qca8k_read(priv, QCA8K_REG_PWS); + if (phylink_autoneg_inband(mode)) + val &= ~QCA8K_PWS_SERDES_AEN_DIS; + else + val |= QCA8K_PWS_SERDES_AEN_DIS; + qca8k_write(priv, QCA8K_REG_PWS, val); + + /* Configure the SGMII parameters */ + val = qca8k_read(priv, QCA8K_REG_SGMII_CTRL); + + val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | + QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD; + + if (dsa_is_cpu_port(ds, port)) { + /* CPU port, we're talking to the CPU MAC, be a PHY */ + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_PHY; + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_MAC; + } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_BASEX; + } + + qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); + break; + default: + dev_err(ds->dev, "xMII mode %s not supported for port %d\n", + phy_modes(state->interface), port); return; + } +} - /* Set port speed */ - switch (phy->speed) { - case 10: - reg = QCA8K_PORT_STATUS_SPEED_10; +static void +qca8k_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + switch (port) { + case 0: /* 1st CPU port */ + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII) + goto unsupported; break; - case 100: - reg = QCA8K_PORT_STATUS_SPEED_100; + case 1: + case 2: + case 3: + case 4: + case 5: + /* Internal PHY */ + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_GMII) + goto unsupported; break; - case 1000: - reg = QCA8K_PORT_STATUS_SPEED_1000; + case 6: /* 2nd CPU port / external PHY */ + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && + state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX) + goto unsupported; break; default: - dev_dbg(priv->dev, "port%d link speed %dMbps not supported.\n", - port, phy->speed); +unsupported: + linkmode_zero(supported); return; } - /* Set duplex mode */ - if (phy->duplex == DUPLEX_FULL) - reg |= QCA8K_PORT_STATUS_DUPLEX; + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); - /* Force flow control */ - if (dsa_is_cpu_port(ds, port)) - reg |= QCA8K_PORT_STATUS_RXFLOW | QCA8K_PORT_STATUS_TXFLOW; + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + phylink_set(mask, 1000baseX_Full); + + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + linkmode_and(supported, supported, mask); + linkmode_and(state->advertising, state->advertising, mask); +} + +static int +qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct qca8k_priv *priv = ds->priv; + u32 reg; + + reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port)); + + state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); + state->an_complete = state->link; + state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); + state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : + DUPLEX_HALF; + + switch (reg & QCA8K_PORT_STATUS_SPEED) { + case QCA8K_PORT_STATUS_SPEED_10: + state->speed = SPEED_10; + break; + case QCA8K_PORT_STATUS_SPEED_100: + state->speed = SPEED_100; + break; + case QCA8K_PORT_STATUS_SPEED_1000: + state->speed = SPEED_1000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } + + state->pause = MLO_PAUSE_NONE; + if (reg & QCA8K_PORT_STATUS_RXFLOW) + state->pause |= MLO_PAUSE_RX; + if (reg & QCA8K_PORT_STATUS_TXFLOW) + state->pause |= MLO_PAUSE_TX; + + return 1; +} + +static void +qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) +{ + struct qca8k_priv *priv = ds->priv; - /* Force link down before changing MAC options */ qca8k_port_set_status(priv, port, 0); +} + +static void +qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface, struct phy_device *phydev, + int speed, int duplex, bool tx_pause, bool rx_pause) +{ + struct qca8k_priv *priv = ds->priv; + u32 reg; + + if (phylink_autoneg_inband(mode)) { + reg = QCA8K_PORT_STATUS_LINK_AUTO; + } else { + switch (speed) { + case SPEED_10: + reg = QCA8K_PORT_STATUS_SPEED_10; + break; + case SPEED_100: + reg = QCA8K_PORT_STATUS_SPEED_100; + break; + case SPEED_1000: + reg = QCA8K_PORT_STATUS_SPEED_1000; + break; + default: + reg = QCA8K_PORT_STATUS_LINK_AUTO; + break; + } + + if (duplex == DUPLEX_FULL) + reg |= QCA8K_PORT_STATUS_DUPLEX; + + if (rx_pause || dsa_is_cpu_port(ds, port)) + reg |= QCA8K_PORT_STATUS_RXFLOW; + + if (tx_pause || dsa_is_cpu_port(ds, port)) + reg |= QCA8K_PORT_STATUS_TXFLOW; + } + + reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; + qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); - qca8k_port_set_status(priv, port, 1); } static void @@ -937,13 +1080,11 @@ qca8k_port_enable(struct dsa_switch *ds, int port, { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - if (!dsa_is_user_port(ds, port)) - return 0; - qca8k_port_set_status(priv, port, 1); priv->port_sts[port].enabled = 1; - phy_support_asym_pause(phy); + if (dsa_is_user_port(ds, port)) + phy_support_asym_pause(phy); return 0; } @@ -1026,7 +1167,6 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port, static const struct dsa_switch_ops qca8k_switch_ops = { .get_tag_protocol = qca8k_get_tag_protocol, .setup = qca8k_setup, - .adjust_link = qca8k_adjust_link, .get_strings = qca8k_get_strings, .get_ethtool_stats = qca8k_get_ethtool_stats, .get_sset_count = qca8k_get_sset_count, @@ -1040,6 +1180,11 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .port_fdb_add = qca8k_port_fdb_add, .port_fdb_del = qca8k_port_fdb_del, .port_fdb_dump = qca8k_port_fdb_dump, + .phylink_validate = qca8k_phylink_validate, + .phylink_mac_link_state = qca8k_phylink_mac_link_state, + .phylink_mac_config = qca8k_phylink_mac_config, + .phylink_mac_link_down = qca8k_phylink_mac_link_down, + .phylink_mac_link_up = qca8k_phylink_mac_link_up, }; static int diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index 42d6ea24eb14..10ef2bca2cde 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -36,6 +36,8 @@ #define QCA8K_MAX_DELAY 3 #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) #define QCA8K_PORT_PAD_SGMII_EN BIT(7) +#define QCA8K_REG_PWS 0x010 +#define QCA8K_PWS_SERDES_AEN_DIS BIT(7) #define QCA8K_REG_MODULE_EN 0x030 #define QCA8K_MODULE_EN_MIB BIT(0) #define QCA8K_REG_MIB 0x034 @@ -69,6 +71,7 @@ #define QCA8K_PORT_STATUS_LINK_UP BIT(8) #define QCA8K_PORT_STATUS_LINK_AUTO BIT(9) #define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10) +#define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12) #define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4)) #define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2) #define QCA8K_PORT_HDR_CTRL_RX_S 2 @@ -77,6 +80,16 @@ #define QCA8K_PORT_HDR_CTRL_ALL 2 #define QCA8K_PORT_HDR_CTRL_MGMT 1 #define QCA8K_PORT_HDR_CTRL_NONE 0 +#define QCA8K_REG_SGMII_CTRL 0x0e0 +#define QCA8K_SGMII_EN_PLL BIT(1) +#define QCA8K_SGMII_EN_RX BIT(2) +#define QCA8K_SGMII_EN_TX BIT(3) +#define QCA8K_SGMII_EN_SD BIT(4) +#define QCA8K_SGMII_CLK125M_DELAY BIT(7) +#define QCA8K_SGMII_MODE_CTRL_MASK (BIT(22) | BIT(23)) +#define QCA8K_SGMII_MODE_CTRL_BASEX (0 << 22) +#define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22) +#define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22) /* EEE control registers */ #define QCA8K_REG_EEE_CTRL 0x100 diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 29ed21687295..ba70b40a9a95 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -262,12 +262,12 @@ int sja1105_static_config_upload(struct sja1105_private *priv); int sja1105_inhibit_tx(const struct sja1105_private *priv, unsigned long port_bitmap, bool tx_inhibited); -extern struct sja1105_info sja1105e_info; -extern struct sja1105_info sja1105t_info; -extern struct sja1105_info sja1105p_info; -extern struct sja1105_info sja1105q_info; -extern struct sja1105_info sja1105r_info; -extern struct sja1105_info sja1105s_info; +extern const struct sja1105_info sja1105e_info; +extern const struct sja1105_info sja1105t_info; +extern const struct sja1105_info sja1105p_info; +extern const struct sja1105_info sja1105q_info; +extern const struct sja1105_info sja1105r_info; +extern const struct sja1105_info sja1105s_info; /* From sja1105_clocking.c */ diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 4471eeccc293..75247f342124 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -638,9 +638,7 @@ static size_t sja1105pqrs_cbs_entry_packing(void *buf, void *entry_ptr, #define OP_SEARCH BIT(3) /* SJA1105E/T: First generation */ -struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, +const struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_VL_LOOKUP] = { .entry_packing = sja1105et_vl_lookup_entry_packing, .cmd_packing = sja1105_vl_lookup_cmd_packing, @@ -649,8 +647,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SJA1105_SIZE_VL_LOOKUP_DYN_CMD, .addr = 0x35, }, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, [BLK_IDX_L2_LOOKUP] = { .entry_packing = sja1105et_dyn_l2_lookup_entry_packing, .cmd_packing = sja1105et_l2_lookup_cmd_packing, @@ -667,7 +663,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, .addr = 0x20, }, - [BLK_IDX_L2_POLICING] = {0}, [BLK_IDX_VLAN_LOOKUP] = { .entry_packing = sja1105_vlan_lookup_entry_packing, .cmd_packing = sja1105_vlan_lookup_cmd_packing, @@ -692,9 +687,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD, .addr = 0x36, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .entry_packing = sja1105et_l2_lookup_params_entry_packing, .cmd_packing = sja1105et_l2_lookup_params_cmd_packing, @@ -703,8 +695,6 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, .addr = 0x38, }, - [BLK_IDX_L2_FORWARDING_PARAMS] = {0}, - [BLK_IDX_AVB_PARAMS] = {0}, [BLK_IDX_GENERAL_PARAMS] = { .entry_packing = sja1105et_general_params_entry_packing, .cmd_packing = sja1105et_general_params_cmd_packing, @@ -729,13 +719,10 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105ET_SIZE_CBS_DYN_CMD, .addr = 0x2c, }, - [BLK_IDX_XMII_PARAMS] = {0}, }; /* SJA1105P/Q/R/S: Second generation */ -struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, +const struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_VL_LOOKUP] = { .entry_packing = sja1105_vl_lookup_entry_packing, .cmd_packing = sja1105_vl_lookup_cmd_packing, @@ -744,8 +731,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SJA1105_SIZE_VL_LOOKUP_DYN_CMD, .addr = 0x47, }, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, [BLK_IDX_L2_LOOKUP] = { .entry_packing = sja1105pqrs_dyn_l2_lookup_entry_packing, .cmd_packing = sja1105pqrs_l2_lookup_cmd_packing, @@ -762,7 +747,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, .addr = 0x24, }, - [BLK_IDX_L2_POLICING] = {0}, [BLK_IDX_VLAN_LOOKUP] = { .entry_packing = sja1105_vlan_lookup_entry_packing, .cmd_packing = sja1105_vlan_lookup_cmd_packing, @@ -787,9 +771,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD, .addr = 0x4B, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .entry_packing = sja1105pqrs_l2_lookup_params_entry_packing, .cmd_packing = sja1105pqrs_l2_lookup_params_cmd_packing, @@ -798,7 +779,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, .addr = 0x54, }, - [BLK_IDX_L2_FORWARDING_PARAMS] = {0}, [BLK_IDX_AVB_PARAMS] = { .entry_packing = sja1105pqrs_avb_params_entry_packing, .cmd_packing = sja1105pqrs_avb_params_cmd_packing, @@ -831,7 +811,6 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { .packed_size = SJA1105PQRS_SIZE_CBS_DYN_CMD, .addr = 0x32, }, - [BLK_IDX_XMII_PARAMS] = {0}, }; /* Provides read access to the settings through the dynamic interface diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.h b/drivers/net/dsa/sja1105/sja1105_dynamic_config.h index 1fc0d13dc623..28d4eb5efb8b 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.h +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.h @@ -34,7 +34,7 @@ struct sja1105_mgmt_entry { u64 index; }; -extern struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN]; -extern struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN]; +extern const struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN]; +extern const struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN]; #endif diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index bb52b9c841b2..704dcf1d1c01 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -507,7 +507,7 @@ static struct sja1105_regs sja1105pqrs_regs = { .ptpsyncts = 0x1F, }; -struct sja1105_info sja1105e_info = { +const struct sja1105_info sja1105e_info = { .device_id = SJA1105E_DEVICE_ID, .part_no = SJA1105ET_PART_NO, .static_ops = sja1105e_table_ops, @@ -523,7 +523,8 @@ struct sja1105_info sja1105e_info = { .regs = &sja1105et_regs, .name = "SJA1105E", }; -struct sja1105_info sja1105t_info = { + +const struct sja1105_info sja1105t_info = { .device_id = SJA1105T_DEVICE_ID, .part_no = SJA1105ET_PART_NO, .static_ops = sja1105t_table_ops, @@ -539,7 +540,8 @@ struct sja1105_info sja1105t_info = { .regs = &sja1105et_regs, .name = "SJA1105T", }; -struct sja1105_info sja1105p_info = { + +const struct sja1105_info sja1105p_info = { .device_id = SJA1105PR_DEVICE_ID, .part_no = SJA1105P_PART_NO, .static_ops = sja1105p_table_ops, @@ -556,7 +558,8 @@ struct sja1105_info sja1105p_info = { .regs = &sja1105pqrs_regs, .name = "SJA1105P", }; -struct sja1105_info sja1105q_info = { + +const struct sja1105_info sja1105q_info = { .device_id = SJA1105QS_DEVICE_ID, .part_no = SJA1105Q_PART_NO, .static_ops = sja1105q_table_ops, @@ -573,7 +576,8 @@ struct sja1105_info sja1105q_info = { .regs = &sja1105pqrs_regs, .name = "SJA1105Q", }; -struct sja1105_info sja1105r_info = { + +const struct sja1105_info sja1105r_info = { .device_id = SJA1105PR_DEVICE_ID, .part_no = SJA1105R_PART_NO, .static_ops = sja1105r_table_ops, @@ -590,7 +594,8 @@ struct sja1105_info sja1105r_info = { .regs = &sja1105pqrs_regs, .name = "SJA1105R", }; -struct sja1105_info sja1105s_info = { + +const struct sja1105_info sja1105s_info = { .device_id = SJA1105QS_DEVICE_ID, .part_no = SJA1105S_PART_NO, .static_ops = sja1105s_table_ops, diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c index ff3fe471efc2..139b7b4fbd0d 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.c +++ b/drivers/net/dsa/sja1105/sja1105_static_config.c @@ -838,12 +838,7 @@ sja1105_static_config_get_length(const struct sja1105_static_config *config) /* Compatibility matrices */ /* SJA1105E: First generation, no TTEthernet */ -struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, - [BLK_IDX_VL_LOOKUP] = {0}, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, +const struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { [BLK_IDX_L2_LOOKUP] = { .packing = sja1105et_l2_lookup_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_entry), @@ -874,9 +869,6 @@ struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { .packed_entry_size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY, .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .packing = sja1105et_l2_lookup_params_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry), @@ -916,7 +908,7 @@ struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = { }; /* SJA1105T: First generation, TTEthernet */ -struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = { +const struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = { [BLK_IDX_SCHEDULE] = { .packing = sja1105_schedule_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_schedule_entry), @@ -1034,12 +1026,7 @@ struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = { }; /* SJA1105P: Second generation, no TTEthernet, no SGMII */ -struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, - [BLK_IDX_VL_LOOKUP] = {0}, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, +const struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { [BLK_IDX_L2_LOOKUP] = { .packing = sja1105pqrs_l2_lookup_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_entry), @@ -1070,9 +1057,6 @@ struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .packing = sja1105pqrs_l2_lookup_params_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry), @@ -1112,7 +1096,7 @@ struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = { }; /* SJA1105Q: Second generation, TTEthernet, no SGMII */ -struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = { +const struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = { [BLK_IDX_SCHEDULE] = { .packing = sja1105_schedule_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_schedule_entry), @@ -1230,12 +1214,7 @@ struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = { }; /* SJA1105R: Second generation, no TTEthernet, SGMII */ -struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { - [BLK_IDX_SCHEDULE] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS] = {0}, - [BLK_IDX_VL_LOOKUP] = {0}, - [BLK_IDX_VL_POLICING] = {0}, - [BLK_IDX_VL_FORWARDING] = {0}, +const struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { [BLK_IDX_L2_LOOKUP] = { .packing = sja1105pqrs_l2_lookup_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_entry), @@ -1266,9 +1245,6 @@ struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, }, - [BLK_IDX_SCHEDULE_PARAMS] = {0}, - [BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS] = {0}, - [BLK_IDX_VL_FORWARDING_PARAMS] = {0}, [BLK_IDX_L2_LOOKUP_PARAMS] = { .packing = sja1105pqrs_l2_lookup_params_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry), @@ -1308,7 +1284,7 @@ struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = { }; /* SJA1105S: Second generation, TTEthernet, SGMII */ -struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX] = { +const struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX] = { [BLK_IDX_SCHEDULE] = { .packing = sja1105_schedule_entry_packing, .unpacked_entry_size = sizeof(struct sja1105_schedule_entry), diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.h b/drivers/net/dsa/sja1105/sja1105_static_config.h index ee0f10062763..bc7606899289 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.h +++ b/drivers/net/dsa/sja1105/sja1105_static_config.h @@ -381,12 +381,12 @@ struct sja1105_static_config { struct sja1105_table tables[BLK_IDX_MAX]; }; -extern struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX]; -extern struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX]; +extern const struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX]; size_t sja1105_table_header_packing(void *buf, void *hdr, enum packing_op op); void diff --git a/drivers/net/dsa/sja1105/sja1105_tas.c b/drivers/net/dsa/sja1105/sja1105_tas.c index 3aa1a8b5f766..31d8acff1f01 100644 --- a/drivers/net/dsa/sja1105/sja1105_tas.c +++ b/drivers/net/dsa/sja1105/sja1105_tas.c @@ -475,8 +475,7 @@ bool sja1105_gating_check_conflicts(struct sja1105_private *priv, int port, if (list_empty(&gating_cfg->entries)) return false; - dummy = kzalloc(sizeof(struct tc_taprio_sched_entry) * num_entries + - sizeof(struct tc_taprio_qopt_offload), GFP_KERNEL); + dummy = kzalloc(struct_size(dummy, entries, num_entries), GFP_KERNEL); if (!dummy) { NL_SET_ERR_MSG_MOD(extack, "Failed to allocate memory"); return true; diff --git a/drivers/net/dsa/sja1105/sja1105_vl.c b/drivers/net/dsa/sja1105/sja1105_vl.c index af3565160db6..ffc4042b4502 100644 --- a/drivers/net/dsa/sja1105/sja1105_vl.c +++ b/drivers/net/dsa/sja1105/sja1105_vl.c @@ -778,7 +778,7 @@ int sja1105_vl_stats(struct sja1105_private *priv, int port, pkts = timingerr + unreleased + lengtherr; - flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, + flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, 0, jiffies - rule->vl.stats.lastused, FLOW_ACTION_HW_STATS_IMMEDIATE); diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 7a1286f8e983..c6591b33abcc 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1580,9 +1580,10 @@ static void amd8111e_tx_timeout(struct net_device *dev, unsigned int txqueue) if(!err) netif_wake_queue(dev); } -static int amd8111e_suspend(struct pci_dev *pci_dev, pm_message_t state) + +static int amd8111e_suspend(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata(pci_dev); + struct net_device *dev = dev_get_drvdata(dev_d); struct amd8111e_priv *lp = netdev_priv(dev); if (!netif_running(dev)) @@ -1609,34 +1610,24 @@ static int amd8111e_suspend(struct pci_dev *pci_dev, pm_message_t state) if(lp->options & OPTION_WAKE_PHY_ENABLE) amd8111e_enable_link_change(lp); - pci_enable_wake(pci_dev, PCI_D3hot, 1); - pci_enable_wake(pci_dev, PCI_D3cold, 1); + device_set_wakeup_enable(dev_d, 1); } else{ - pci_enable_wake(pci_dev, PCI_D3hot, 0); - pci_enable_wake(pci_dev, PCI_D3cold, 0); + device_set_wakeup_enable(dev_d, 0); } - pci_save_state(pci_dev); - pci_set_power_state(pci_dev, PCI_D3hot); - return 0; } -static int amd8111e_resume(struct pci_dev *pci_dev) + +static int amd8111e_resume(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata(pci_dev); + struct net_device *dev = dev_get_drvdata(dev_d); struct amd8111e_priv *lp = netdev_priv(dev); if (!netif_running(dev)) return 0; - pci_set_power_state(pci_dev, PCI_D0); - pci_restore_state(pci_dev); - - pci_enable_wake(pci_dev, PCI_D3hot, 0); - pci_enable_wake(pci_dev, PCI_D3cold, 0); /* D3 cold */ - netif_device_attach(dev); spin_lock_irq(&lp->lock); @@ -1918,13 +1909,14 @@ static const struct pci_device_id amd8111e_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, amd8111e_pci_tbl); +static SIMPLE_DEV_PM_OPS(amd8111e_pm_ops, amd8111e_suspend, amd8111e_resume); + static struct pci_driver amd8111e_driver = { .name = MODULE_NAME, .id_table = amd8111e_pci_tbl, .probe = amd8111e_probe_one, .remove = amd8111e_remove_one, - .suspend = amd8111e_suspend, - .resume = amd8111e_resume + .driver.pm = &amd8111e_pm_ops }; module_pci_driver(amd8111e_driver); diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index 07e8211eea51..d32f54d760e7 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -2913,30 +2913,27 @@ static void pcnet32_watchdog(struct timer_list *t) mod_timer(&lp->watchdog_timer, round_jiffies(PCNET32_WATCHDOG_TIMEOUT)); } -static int pcnet32_pm_suspend(struct pci_dev *pdev, pm_message_t state) +static int pcnet32_pm_suspend(struct device *device_d) { - struct net_device *dev = pci_get_drvdata(pdev); + struct net_device *dev = dev_get_drvdata(device_d); if (netif_running(dev)) { netif_device_detach(dev); pcnet32_close(dev); } - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; } -static int pcnet32_pm_resume(struct pci_dev *pdev) +static int pcnet32_pm_resume(struct device *device_d) { - struct net_device *dev = pci_get_drvdata(pdev); - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); + struct net_device *dev = dev_get_drvdata(device_d); if (netif_running(dev)) { pcnet32_open(dev); netif_device_attach(dev); } + return 0; } @@ -2957,13 +2954,16 @@ static void pcnet32_remove_one(struct pci_dev *pdev) } } +static SIMPLE_DEV_PM_OPS(pcnet32_pm_ops, pcnet32_pm_suspend, pcnet32_pm_resume); + static struct pci_driver pcnet32_driver = { .name = DRV_NAME, .probe = pcnet32_probe_pci, .remove = pcnet32_remove_one, .id_table = pcnet32_pci_tbl, - .suspend = pcnet32_pm_suspend, - .resume = pcnet32_pm_resume, + .driver = { + .pm = &pcnet32_pm_ops, + }, }; /* An additional parameter that may be passed in... */ diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index 7b86240ecd5f..90cb55eb5466 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -421,10 +421,9 @@ static void xgbe_pci_remove(struct pci_dev *pdev) xgbe_free_pdata(pdata); } -#ifdef CONFIG_PM -static int xgbe_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused xgbe_pci_suspend(struct device *dev) { - struct xgbe_prv_data *pdata = pci_get_drvdata(pdev); + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); struct net_device *netdev = pdata->netdev; int ret = 0; @@ -438,9 +437,9 @@ static int xgbe_pci_suspend(struct pci_dev *pdev, pm_message_t state) return ret; } -static int xgbe_pci_resume(struct pci_dev *pdev) +static int __maybe_unused xgbe_pci_resume(struct device *dev) { - struct xgbe_prv_data *pdata = pci_get_drvdata(pdev); + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); struct net_device *netdev = pdata->netdev; int ret = 0; @@ -460,7 +459,6 @@ static int xgbe_pci_resume(struct pci_dev *pdev) return ret; } -#endif /* CONFIG_PM */ static const struct xgbe_version_data xgbe_v2a = { .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v2, @@ -502,15 +500,16 @@ static const struct pci_device_id xgbe_pci_table[] = { }; MODULE_DEVICE_TABLE(pci, xgbe_pci_table); +static SIMPLE_DEV_PM_OPS(xgbe_pci_pm_ops, xgbe_pci_suspend, xgbe_pci_resume); + static struct pci_driver xgbe_driver = { .name = XGBE_DRV_NAME, .id_table = xgbe_pci_table, .probe = xgbe_pci_probe, .remove = xgbe_pci_remove, -#ifdef CONFIG_PM - .suspend = xgbe_pci_suspend, - .resume = xgbe_pci_resume, -#endif + .driver = { + .pm = &xgbe_pci_pm_ops, + } }; int xgbe_pci_init(void) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_common.h b/drivers/net/ethernet/aquantia/atlantic/aq_common.h index 52ad9433cabc..23b2d390fcdd 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_common.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_common.h @@ -58,11 +58,19 @@ #define AQ_NIC_RATE_1G BIT(4) #define AQ_NIC_RATE_100M BIT(5) #define AQ_NIC_RATE_10M BIT(6) +#define AQ_NIC_RATE_1G_HALF BIT(7) +#define AQ_NIC_RATE_100M_HALF BIT(8) +#define AQ_NIC_RATE_10M_HALF BIT(9) -#define AQ_NIC_RATE_EEE_10G BIT(7) -#define AQ_NIC_RATE_EEE_5G BIT(8) -#define AQ_NIC_RATE_EEE_2G5 BIT(9) -#define AQ_NIC_RATE_EEE_1G BIT(10) -#define AQ_NIC_RATE_EEE_100M BIT(11) +#define AQ_NIC_RATE_EEE_10G BIT(10) +#define AQ_NIC_RATE_EEE_5G BIT(11) +#define AQ_NIC_RATE_EEE_2G5 BIT(12) +#define AQ_NIC_RATE_EEE_1G BIT(13) +#define AQ_NIC_RATE_EEE_100M BIT(14) +#define AQ_NIC_RATE_EEE_MSK (AQ_NIC_RATE_EEE_10G |\ + AQ_NIC_RATE_EEE_5G |\ + AQ_NIC_RATE_EEE_2G5 |\ + AQ_NIC_RATE_EEE_1G |\ + AQ_NIC_RATE_EEE_100M) #endif /* AQ_COMMON_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c index 743d3b13b39d..e53ba7bfaf61 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -/* - * aQuantia Corporation Network Driver - * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved +/* Atlantic Network Driver + * + * Copyright (C) 2014-2019 aQuantia Corporation + * Copyright (C) 2019-2020 Marvell International Ltd. */ /* File aq_ethtool.c: Definition of ethertool related functions. */ @@ -611,16 +612,13 @@ static int aq_ethtool_get_ts_info(struct net_device *ndev, return 0; } -static enum hw_atl_fw2x_rate eee_mask_to_ethtool_mask(u32 speed) +static u32 eee_mask_to_ethtool_mask(u32 speed) { u32 rate = 0; if (speed & AQ_NIC_RATE_EEE_10G) rate |= SUPPORTED_10000baseT_Full; - if (speed & AQ_NIC_RATE_EEE_2G5) - rate |= SUPPORTED_2500baseX_Full; - if (speed & AQ_NIC_RATE_EEE_1G) rate |= SUPPORTED_1000baseT_Full; @@ -656,7 +654,7 @@ static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee) eee->eee_enabled = !!eee->advertised; eee->tx_lpi_enabled = eee->eee_enabled; - if (eee->advertised & eee->lp_advertised) + if ((supported_rates & rate) & AQ_NIC_RATE_EEE_MSK) eee->eee_active = true; return 0; @@ -838,6 +836,7 @@ static int aq_ethtool_set_priv_flags(struct net_device *ndev, u32 flags) struct aq_nic_s *aq_nic = netdev_priv(ndev); struct aq_nic_cfg_s *cfg; u32 priv_flags; + int ret = 0; cfg = aq_nic_get_cfg(aq_nic); priv_flags = cfg->priv_flags; @@ -859,10 +858,10 @@ static int aq_ethtool_set_priv_flags(struct net_device *ndev, u32 flags) dev_open(ndev, NULL); } } else if ((priv_flags ^ flags) & AQ_HW_LOOPBACK_MASK) { - aq_nic_set_loopback(aq_nic); + ret = aq_nic_set_loopback(aq_nic); } - return 0; + return ret; } const struct ethtool_ops aq_ethtool_ops = { diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h index ed5b465bc664..f2663ad22209 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* - * aQuantia Corporation Network Driver - * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved +/* Atlantic Network Driver + * + * Copyright (C) 2014-2019 aQuantia Corporation + * Copyright (C) 2019-2020 Marvell International Ltd. */ /* File aq_hw.h: Declaration of abstract interface for NIC hardware specific @@ -69,6 +70,9 @@ struct aq_hw_caps_s { struct aq_hw_link_status_s { unsigned int mbps; + bool full_duplex; + u32 lp_link_speed_msk; + u32 lp_flow_control; }; struct aq_stats_s { diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 4435c6374f7e..647b22d89b1a 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -935,12 +935,17 @@ static void aq_nic_update_ndev_stats(struct aq_nic_s *self) void aq_nic_get_link_ksettings(struct aq_nic_s *self, struct ethtool_link_ksettings *cmd) { + u32 lp_link_speed_msk; + if (self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_FIBRE) cmd->base.port = PORT_FIBRE; else cmd->base.port = PORT_TP; - /* This driver supports only 10G capable adapters, so DUPLEX_FULL */ - cmd->base.duplex = DUPLEX_FULL; + + cmd->base.duplex = DUPLEX_UNKNOWN; + if (self->link_status.mbps) + cmd->base.duplex = self->link_status.full_duplex ? + DUPLEX_FULL : DUPLEX_HALF; cmd->base.autoneg = self->aq_nic_cfg.is_autoneg; ethtool_link_ksettings_zero_link_mode(cmd, supported); @@ -961,14 +966,26 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self, ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full); + if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_1G_HALF) + ethtool_link_ksettings_add_link_mode(cmd, supported, + 1000baseT_Half); + if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_100M) ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Full); + if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_100M_HALF) + ethtool_link_ksettings_add_link_mode(cmd, supported, + 100baseT_Half); + if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_10M) ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Full); + if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_10M_HALF) + ethtool_link_ksettings_add_link_mode(cmd, supported, + 10baseT_Half); + if (self->aq_nic_cfg.aq_hw_caps->flow_control) { ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); @@ -988,30 +1005,42 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self, if (self->aq_nic_cfg.is_autoneg) ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); - if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10G) + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10G) ethtool_link_ksettings_add_link_mode(cmd, advertising, 10000baseT_Full); - if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_5G) + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_5G) ethtool_link_ksettings_add_link_mode(cmd, advertising, 5000baseT_Full); - if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_2G5) + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_2G5) ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full); - if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_1G) + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_1G) ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full); - if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_100M) + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_1G_HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 1000baseT_Half); + + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_100M) ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full); - if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10M) + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_100M_HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Half); + + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10M) ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full); + if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10M_HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Half); + if (self->aq_nic_cfg.fc.cur & AQ_NIC_FC_RX) ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); @@ -1026,32 +1055,84 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self, ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); else ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); + + ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising); + lp_link_speed_msk = self->aq_hw->aq_link_status.lp_link_speed_msk; + + if (lp_link_speed_msk & AQ_NIC_RATE_10G) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 10000baseT_Full); + + if (lp_link_speed_msk & AQ_NIC_RATE_5G) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 5000baseT_Full); + + if (lp_link_speed_msk & AQ_NIC_RATE_2G5) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 2500baseT_Full); + + if (lp_link_speed_msk & AQ_NIC_RATE_1G) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 1000baseT_Full); + + if (lp_link_speed_msk & AQ_NIC_RATE_1G_HALF) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 1000baseT_Half); + + if (lp_link_speed_msk & AQ_NIC_RATE_100M) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 100baseT_Full); + + if (lp_link_speed_msk & AQ_NIC_RATE_100M_HALF) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 100baseT_Half); + + if (lp_link_speed_msk & AQ_NIC_RATE_10M) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 10baseT_Full); + + if (lp_link_speed_msk & AQ_NIC_RATE_10M_HALF) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + 10baseT_Half); + + if (self->aq_hw->aq_link_status.lp_flow_control & AQ_NIC_FC_RX) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + Pause); + if (!!(self->aq_hw->aq_link_status.lp_flow_control & AQ_NIC_FC_TX) ^ + !!(self->aq_hw->aq_link_status.lp_flow_control & AQ_NIC_FC_RX)) + ethtool_link_ksettings_add_link_mode(cmd, lp_advertising, + Asym_Pause); } int aq_nic_set_link_ksettings(struct aq_nic_s *self, const struct ethtool_link_ksettings *cmd) { - u32 speed = 0U; + int fduplex = (cmd->base.duplex == DUPLEX_FULL); + u32 speed = cmd->base.speed; u32 rate = 0U; int err = 0; + if (!fduplex && speed > SPEED_1000) { + err = -EINVAL; + goto err_exit; + } + if (cmd->base.autoneg == AUTONEG_ENABLE) { rate = self->aq_nic_cfg.aq_hw_caps->link_speed_msk; self->aq_nic_cfg.is_autoneg = true; } else { - speed = cmd->base.speed; - switch (speed) { case SPEED_10: - rate = AQ_NIC_RATE_10M; + rate = fduplex ? AQ_NIC_RATE_10M : AQ_NIC_RATE_10M_HALF; break; case SPEED_100: - rate = AQ_NIC_RATE_100M; + rate = fduplex ? AQ_NIC_RATE_100M + : AQ_NIC_RATE_100M_HALF; break; case SPEED_1000: - rate = AQ_NIC_RATE_1G; + rate = fduplex ? AQ_NIC_RATE_1G : AQ_NIC_RATE_1G_HALF; break; case SPEED_2500: diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c index 14d79f70cad7..b023c3324a59 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c @@ -108,7 +108,7 @@ static int hw_atl_b0_hw_reset(struct aq_hw_s *self) return err; } -static int hw_atl_b0_set_fc(struct aq_hw_s *self, u32 fc, u32 tc) +int hw_atl_b0_set_fc(struct aq_hw_s *self, u32 fc, u32 tc) { hw_atl_rpb_rx_xoff_en_per_tc_set(self, !!(fc & AQ_NIC_FC_RX), tc); @@ -1556,7 +1556,7 @@ static int hw_atl_b0_hw_vlan_ctrl(struct aq_hw_s *self, bool enable) return aq_hw_err_from_flags(self); } -static int hw_atl_b0_set_loopback(struct aq_hw_s *self, u32 mode, bool enable) +int hw_atl_b0_set_loopback(struct aq_hw_s *self, u32 mode, bool enable) { switch (mode) { case AQ_HW_LOOPBACK_DMA_SYS: diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.h index 30f468f2084d..66d158900141 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.h @@ -62,6 +62,9 @@ void hw_atl_b0_hw_init_rx_rss_ctrl1(struct aq_hw_s *self); int hw_atl_b0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr); +int hw_atl_b0_set_fc(struct aq_hw_s *self, u32 fc, u32 tc); +int hw_atl_b0_set_loopback(struct aq_hw_s *self, u32 mode, bool enable); + int hw_atl_b0_hw_start(struct aq_hw_s *self); int hw_atl_b0_hw_irq_enable(struct aq_hw_s *self, u64 mask); diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c index 73c0f41df8d8..1d9dee4951f9 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c @@ -704,6 +704,7 @@ int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self) return -EBUSY; } } + link_status->full_duplex = true; return 0; } diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c index eeedd8c90067..013676cd38e4 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c @@ -274,6 +274,7 @@ static int aq_fw2x_update_link_status(struct aq_hw_s *self) } else { link_status->mbps = 0; } + link_status->full_duplex = true; return 0; } diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c index 8df9d4ef36f0..c65e6daad0e5 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c @@ -64,8 +64,11 @@ const struct aq_hw_caps_s hw_atl2_caps_aqc113 = { AQ_NIC_RATE_5G | AQ_NIC_RATE_2G5 | AQ_NIC_RATE_1G | + AQ_NIC_RATE_1G_HALF | AQ_NIC_RATE_100M | - AQ_NIC_RATE_10M, + AQ_NIC_RATE_100M_HALF | + AQ_NIC_RATE_10M | + AQ_NIC_RATE_10M_HALF, }; static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self) @@ -178,6 +181,8 @@ static int hw_atl2_hw_qos_set(struct aq_hw_s *self) threshold = (rx_buff_size * (1024U / 32U) * 50U) / 100U; hw_atl_rpb_rx_buff_lo_threshold_per_tc_set(self, threshold, tc); + + hw_atl_b0_set_fc(self, self->aq_nic_cfg->fc.req, tc); } /* QoS 802.1p priority -> TC mapping */ @@ -838,4 +843,6 @@ const struct aq_hw_ops hw_atl2_ops = { .hw_get_hw_stats = hw_atl2_utils_get_hw_stats, .hw_get_fw_version = hw_atl2_utils_get_fw_version, .hw_set_offload = hw_atl_b0_hw_offload_set, + .hw_set_loopback = hw_atl_b0_set_loopback, + .hw_set_fc = hw_atl_b0_set_fc, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c index 0ffc33bd67d0..3a9352190816 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c @@ -7,6 +7,7 @@ #include "aq_hw.h" #include "aq_hw_utils.h" +#include "aq_nic.h" #include "hw_atl/hw_atl_llh.h" #include "hw_atl2_utils.h" #include "hw_atl2_llh.h" @@ -135,6 +136,67 @@ static void a2_link_speed_mask2fw(u32 speed, link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G); link_options->rate_100M = !!(speed & AQ_NIC_RATE_100M); link_options->rate_10M = !!(speed & AQ_NIC_RATE_10M); + + link_options->rate_1G_hd = !!(speed & AQ_NIC_RATE_1G_HALF); + link_options->rate_100M_hd = !!(speed & AQ_NIC_RATE_100M_HALF); + link_options->rate_10M_hd = !!(speed & AQ_NIC_RATE_10M_HALF); +} + +static u32 a2_fw_dev_to_eee_mask(struct device_link_caps_s *device_link_caps) +{ + u32 rate = 0; + + if (device_link_caps->eee_10G) + rate |= AQ_NIC_RATE_EEE_10G; + if (device_link_caps->eee_5G) + rate |= AQ_NIC_RATE_EEE_5G; + if (device_link_caps->eee_2P5G) + rate |= AQ_NIC_RATE_EEE_2G5; + if (device_link_caps->eee_1G) + rate |= AQ_NIC_RATE_EEE_1G; + if (device_link_caps->eee_100M) + rate |= AQ_NIC_RATE_EEE_100M; + + return rate; +} + +static u32 a2_fw_lkp_to_mask(struct lkp_link_caps_s *lkp_link_caps) +{ + u32 rate = 0; + + if (lkp_link_caps->rate_10G) + rate |= AQ_NIC_RATE_10G; + if (lkp_link_caps->rate_5G) + rate |= AQ_NIC_RATE_5G; + if (lkp_link_caps->rate_N5G) + rate |= AQ_NIC_RATE_5GSR; + if (lkp_link_caps->rate_2P5G) + rate |= AQ_NIC_RATE_2G5; + if (lkp_link_caps->rate_1G) + rate |= AQ_NIC_RATE_1G; + if (lkp_link_caps->rate_1G_hd) + rate |= AQ_NIC_RATE_1G_HALF; + if (lkp_link_caps->rate_100M) + rate |= AQ_NIC_RATE_100M; + if (lkp_link_caps->rate_100M_hd) + rate |= AQ_NIC_RATE_100M_HALF; + if (lkp_link_caps->rate_10M) + rate |= AQ_NIC_RATE_10M; + if (lkp_link_caps->rate_10M_hd) + rate |= AQ_NIC_RATE_10M_HALF; + + if (lkp_link_caps->eee_10G) + rate |= AQ_NIC_RATE_EEE_10G; + if (lkp_link_caps->eee_5G) + rate |= AQ_NIC_RATE_EEE_5G; + if (lkp_link_caps->eee_2P5G) + rate |= AQ_NIC_RATE_EEE_2G5; + if (lkp_link_caps->eee_1G) + rate |= AQ_NIC_RATE_EEE_1G; + if (lkp_link_caps->eee_100M) + rate |= AQ_NIC_RATE_EEE_100M; + + return rate; } static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed) @@ -149,6 +211,26 @@ static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed) return hw_atl2_shared_buffer_finish_ack(self); } +static void aq_a2_fw_set_mpi_flow_control(struct aq_hw_s *self, + struct link_options_s *link_options) +{ + u32 flow_control = self->aq_nic_cfg->fc.req; + + link_options->pause_rx = !!(flow_control & AQ_NIC_FC_RX); + link_options->pause_tx = !!(flow_control & AQ_NIC_FC_TX); +} + +static void aq_a2_fw_upd_eee_rate_bits(struct aq_hw_s *self, + struct link_options_s *link_options, + u32 eee_speeds) +{ + link_options->eee_10G = !!(eee_speeds & AQ_NIC_RATE_EEE_10G); + link_options->eee_5G = !!(eee_speeds & AQ_NIC_RATE_EEE_5G); + link_options->eee_2P5G = !!(eee_speeds & AQ_NIC_RATE_EEE_2G5); + link_options->eee_1G = !!(eee_speeds & AQ_NIC_RATE_EEE_1G); + link_options->eee_100M = !!(eee_speeds & AQ_NIC_RATE_EEE_100M); +} + static int aq_a2_fw_set_state(struct aq_hw_s *self, enum hal_atl_utils_fw_state_e state) { @@ -159,6 +241,9 @@ static int aq_a2_fw_set_state(struct aq_hw_s *self, switch (state) { case MPI_INIT: link_options.link_up = 1U; + aq_a2_fw_upd_eee_rate_bits(self, &link_options, + self->aq_nic_cfg->eee_speeds); + aq_a2_fw_set_mpi_flow_control(self, &link_options); break; case MPI_DEINIT: link_options.link_up = 0U; @@ -176,6 +261,7 @@ static int aq_a2_fw_set_state(struct aq_hw_s *self, static int aq_a2_fw_update_link_status(struct aq_hw_s *self) { + struct lkp_link_caps_s lkp_link_caps; struct link_status_s link_status; hw_atl2_shared_buffer_read(self, link_status, link_status); @@ -202,6 +288,15 @@ static int aq_a2_fw_update_link_status(struct aq_hw_s *self) default: self->aq_link_status.mbps = 0; } + self->aq_link_status.full_duplex = link_status.duplex; + + hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps); + + self->aq_link_status.lp_link_speed_msk = + a2_fw_lkp_to_mask(&lkp_link_caps); + self->aq_link_status.lp_flow_control = + ((lkp_link_caps.pause_rx) ? AQ_NIC_FC_RX : 0) | + ((lkp_link_caps.pause_tx) ? AQ_NIC_FC_TX : 0); return 0; } @@ -260,6 +355,34 @@ static int aq_a2_fw_update_stats(struct aq_hw_s *self) return 0; } +static int aq_a2_fw_set_eee_rate(struct aq_hw_s *self, u32 speed) +{ + struct link_options_s link_options; + + hw_atl2_shared_buffer_get(self, link_options, link_options); + + aq_a2_fw_upd_eee_rate_bits(self, &link_options, speed); + + hw_atl2_shared_buffer_write(self, link_options, link_options); + + return hw_atl2_shared_buffer_finish_ack(self); +} + +static int aq_a2_fw_get_eee_rate(struct aq_hw_s *self, u32 *rate, + u32 *supported_rates) +{ + struct device_link_caps_s device_link_caps; + struct lkp_link_caps_s lkp_link_caps; + + hw_atl2_shared_buffer_read(self, device_link_caps, device_link_caps); + hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps); + + *supported_rates = a2_fw_dev_to_eee_mask(&device_link_caps); + *rate = a2_fw_lkp_to_mask(&lkp_link_caps); + + return 0; +} + static int aq_a2_fw_renegotiate(struct aq_hw_s *self) { struct link_options_s link_options; @@ -280,6 +403,52 @@ static int aq_a2_fw_renegotiate(struct aq_hw_s *self) return err; } +static int aq_a2_fw_set_flow_control(struct aq_hw_s *self) +{ + struct link_options_s link_options; + + hw_atl2_shared_buffer_get(self, link_options, link_options); + + aq_a2_fw_set_mpi_flow_control(self, &link_options); + + hw_atl2_shared_buffer_write(self, link_options, link_options); + + return hw_atl2_shared_buffer_finish_ack(self); +} + +static u32 aq_a2_fw_get_flow_control(struct aq_hw_s *self, u32 *fcmode) +{ + struct link_status_s link_status; + + hw_atl2_shared_buffer_read(self, link_status, link_status); + + *fcmode = ((link_status.pause_rx) ? AQ_NIC_FC_RX : 0) | + ((link_status.pause_tx) ? AQ_NIC_FC_TX : 0); + return 0; +} + +static int aq_a2_fw_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable) +{ + struct link_options_s link_options; + + hw_atl2_shared_buffer_get(self, link_options, link_options); + + switch (mode) { + case AQ_HW_LOOPBACK_PHYINT_SYS: + link_options.internal_loopback = enable; + break; + case AQ_HW_LOOPBACK_PHYEXT_SYS: + link_options.external_loopback = enable; + break; + default: + return -EINVAL; + } + + hw_atl2_shared_buffer_write(self, link_options, link_options); + + return hw_atl2_shared_buffer_finish_ack(self); +} + u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self) { struct version_s version; @@ -317,4 +486,9 @@ const struct aq_fw_ops aq_a2_fw_ops = { .set_state = aq_a2_fw_set_state, .update_link_status = aq_a2_fw_update_link_status, .update_stats = aq_a2_fw_update_stats, + .set_eee_rate = aq_a2_fw_set_eee_rate, + .get_eee_rate = aq_a2_fw_get_eee_rate, + .set_flow_control = aq_a2_fw_set_flow_control, + .get_flow_control = aq_a2_fw_get_flow_control, + .set_phyloopback = aq_a2_fw_set_phyloopback, }; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index a812beb46325..2bd610fafc58 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -411,6 +411,12 @@ static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req, return rc; } + if (strlen(bp->board_serialno)) { + rc = devlink_info_board_serial_number_put(req, bp->board_serialno); + if (rc) + return rc; + } + sprintf(buf, "%X", bp->chip_num); rc = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, buf); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c index 4a11c1e7cc02..0a9a4467d7c7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -1638,7 +1638,7 @@ static int bnxt_tc_get_flow_stats(struct bnxt *bp, lastused = flow->lastused; spin_unlock(&flow->stats_lock); - flow_stats_update(&tc_flow_cmd->stats, stats.bytes, stats.packets, + flow_stats_update(&tc_flow_cmd->stats, stats.bytes, stats.packets, 0, lastused, FLOW_ACTION_HW_STATS_DELAYED); return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 6dd65f9b347c..8e59c2825533 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -95,12 +95,10 @@ int octeon_init_instr_queue(struct octeon_device *oct, /* Initialize a list to holds requests that have been posted to Octeon * but has yet to be fetched by octeon */ - iq->request_list = vmalloc_node((sizeof(*iq->request_list) * num_descs), - numa_node); + iq->request_list = vzalloc_node(array_size(num_descs, sizeof(*iq->request_list)), + numa_node); if (!iq->request_list) - iq->request_list = - vmalloc(array_size(num_descs, - sizeof(*iq->request_list))); + iq->request_list = vzalloc(array_size(num_descs, sizeof(*iq->request_list))); if (!iq->request_list) { lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma); dev_err(&oct->pci_dev->dev, "Alloc failed for IQ[%d] nr free list\n", @@ -108,8 +106,6 @@ int octeon_init_instr_queue(struct octeon_device *oct, return 1; } - memset(iq->request_list, 0, sizeof(*iq->request_list) * num_descs); - dev_dbg(&oct->pci_dev->dev, "IQ[%d]: base: %p basedma: %pad count: %d\n", iq_no, iq->base_addr, &iq->base_addr_dma, iq->max_count); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 069e7413f1ef..a45223f0cca5 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -1489,9 +1489,10 @@ static int nicvf_sq_append_tso(struct nicvf *nic, struct snd_queue *sq, int seg_subdescs = 0, desc_cnt = 0; int seg_len, total_len, data_left; int hdr_qentry = qentry; - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int hdr_len; + + hdr_len = tso_start(skb, &tso); - tso_start(skb, &tso); total_len = skb->len - hdr_len; while (total_len > 0) { char *hdr; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h index dcab94cc2dee..876f90e5795e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h @@ -350,167 +350,6 @@ struct cudbg_qdesc_info { #define IREG_NUM_ELEM 4 -static const u32 t6_tp_pio_array[][IREG_NUM_ELEM] = { - {0x7e40, 0x7e44, 0x020, 28}, /* t6_tp_pio_regs_20_to_3b */ - {0x7e40, 0x7e44, 0x040, 10}, /* t6_tp_pio_regs_40_to_49 */ - {0x7e40, 0x7e44, 0x050, 10}, /* t6_tp_pio_regs_50_to_59 */ - {0x7e40, 0x7e44, 0x060, 14}, /* t6_tp_pio_regs_60_to_6d */ - {0x7e40, 0x7e44, 0x06F, 1}, /* t6_tp_pio_regs_6f */ - {0x7e40, 0x7e44, 0x070, 6}, /* t6_tp_pio_regs_70_to_75 */ - {0x7e40, 0x7e44, 0x130, 18}, /* t6_tp_pio_regs_130_to_141 */ - {0x7e40, 0x7e44, 0x145, 19}, /* t6_tp_pio_regs_145_to_157 */ - {0x7e40, 0x7e44, 0x160, 1}, /* t6_tp_pio_regs_160 */ - {0x7e40, 0x7e44, 0x230, 25}, /* t6_tp_pio_regs_230_to_248 */ - {0x7e40, 0x7e44, 0x24a, 3}, /* t6_tp_pio_regs_24c */ - {0x7e40, 0x7e44, 0x8C0, 1} /* t6_tp_pio_regs_8c0 */ -}; - -static const u32 t5_tp_pio_array[][IREG_NUM_ELEM] = { - {0x7e40, 0x7e44, 0x020, 28}, /* t5_tp_pio_regs_20_to_3b */ - {0x7e40, 0x7e44, 0x040, 19}, /* t5_tp_pio_regs_40_to_52 */ - {0x7e40, 0x7e44, 0x054, 2}, /* t5_tp_pio_regs_54_to_55 */ - {0x7e40, 0x7e44, 0x060, 13}, /* t5_tp_pio_regs_60_to_6c */ - {0x7e40, 0x7e44, 0x06F, 1}, /* t5_tp_pio_regs_6f */ - {0x7e40, 0x7e44, 0x120, 4}, /* t5_tp_pio_regs_120_to_123 */ - {0x7e40, 0x7e44, 0x12b, 2}, /* t5_tp_pio_regs_12b_to_12c */ - {0x7e40, 0x7e44, 0x12f, 21}, /* t5_tp_pio_regs_12f_to_143 */ - {0x7e40, 0x7e44, 0x145, 19}, /* t5_tp_pio_regs_145_to_157 */ - {0x7e40, 0x7e44, 0x230, 25}, /* t5_tp_pio_regs_230_to_248 */ - {0x7e40, 0x7e44, 0x8C0, 1} /* t5_tp_pio_regs_8c0 */ -}; - -static const u32 t6_tp_tm_pio_array[][IREG_NUM_ELEM] = { - {0x7e18, 0x7e1c, 0x0, 12} -}; - -static const u32 t5_tp_tm_pio_array[][IREG_NUM_ELEM] = { - {0x7e18, 0x7e1c, 0x0, 12} -}; - -static const u32 t6_tp_mib_index_array[6][IREG_NUM_ELEM] = { - {0x7e50, 0x7e54, 0x0, 13}, - {0x7e50, 0x7e54, 0x10, 6}, - {0x7e50, 0x7e54, 0x18, 21}, - {0x7e50, 0x7e54, 0x30, 32}, - {0x7e50, 0x7e54, 0x50, 22}, - {0x7e50, 0x7e54, 0x68, 12} -}; - -static const u32 t5_tp_mib_index_array[9][IREG_NUM_ELEM] = { - {0x7e50, 0x7e54, 0x0, 13}, - {0x7e50, 0x7e54, 0x10, 6}, - {0x7e50, 0x7e54, 0x18, 8}, - {0x7e50, 0x7e54, 0x20, 13}, - {0x7e50, 0x7e54, 0x30, 16}, - {0x7e50, 0x7e54, 0x40, 16}, - {0x7e50, 0x7e54, 0x50, 16}, - {0x7e50, 0x7e54, 0x60, 6}, - {0x7e50, 0x7e54, 0x68, 4} -}; - -static const u32 t5_sge_dbg_index_array[2][IREG_NUM_ELEM] = { - {0x10cc, 0x10d0, 0x0, 16}, - {0x10cc, 0x10d4, 0x0, 16}, -}; - -static const u32 t6_sge_qbase_index_array[] = { - /* 1 addr reg SGE_QBASE_INDEX and 4 data reg SGE_QBASE_MAP[0-3] */ - 0x1250, 0x1240, 0x1244, 0x1248, 0x124c, -}; - -static const u32 t5_pcie_pdbg_array[][IREG_NUM_ELEM] = { - {0x5a04, 0x5a0c, 0x00, 0x20}, /* t5_pcie_pdbg_regs_00_to_20 */ - {0x5a04, 0x5a0c, 0x21, 0x20}, /* t5_pcie_pdbg_regs_21_to_40 */ - {0x5a04, 0x5a0c, 0x41, 0x10}, /* t5_pcie_pdbg_regs_41_to_50 */ -}; - -static const u32 t5_pcie_cdbg_array[][IREG_NUM_ELEM] = { - {0x5a10, 0x5a18, 0x00, 0x20}, /* t5_pcie_cdbg_regs_00_to_20 */ - {0x5a10, 0x5a18, 0x21, 0x18}, /* t5_pcie_cdbg_regs_21_to_37 */ -}; - -static const u32 t5_pm_rx_array[][IREG_NUM_ELEM] = { - {0x8FD0, 0x8FD4, 0x10000, 0x20}, /* t5_pm_rx_regs_10000_to_10020 */ - {0x8FD0, 0x8FD4, 0x10021, 0x0D}, /* t5_pm_rx_regs_10021_to_1002c */ -}; - -static const u32 t5_pm_tx_array[][IREG_NUM_ELEM] = { - {0x8FF0, 0x8FF4, 0x10000, 0x20}, /* t5_pm_tx_regs_10000_to_10020 */ - {0x8FF0, 0x8FF4, 0x10021, 0x1D}, /* t5_pm_tx_regs_10021_to_1003c */ -}; - #define CUDBG_NUM_PCIE_CONFIG_REGS 0x61 -static const u32 t5_pcie_config_array[][2] = { - {0x0, 0x34}, - {0x3c, 0x40}, - {0x50, 0x64}, - {0x70, 0x80}, - {0x94, 0xa0}, - {0xb0, 0xb8}, - {0xd0, 0xd4}, - {0x100, 0x128}, - {0x140, 0x148}, - {0x150, 0x164}, - {0x170, 0x178}, - {0x180, 0x194}, - {0x1a0, 0x1b8}, - {0x1c0, 0x208}, -}; - -static const u32 t6_ma_ireg_array[][IREG_NUM_ELEM] = { - {0x78f8, 0x78fc, 0xa000, 23}, /* t6_ma_regs_a000_to_a016 */ - {0x78f8, 0x78fc, 0xa400, 30}, /* t6_ma_regs_a400_to_a41e */ - {0x78f8, 0x78fc, 0xa800, 20} /* t6_ma_regs_a800_to_a813 */ -}; - -static const u32 t6_ma_ireg_array2[][IREG_NUM_ELEM] = { - {0x78f8, 0x78fc, 0xe400, 17}, /* t6_ma_regs_e400_to_e600 */ - {0x78f8, 0x78fc, 0xe640, 13} /* t6_ma_regs_e640_to_e7c0 */ -}; - -static const u32 t6_up_cim_reg_array[][IREG_NUM_ELEM + 1] = { - {0x7b50, 0x7b54, 0x2000, 0x20, 0}, /* up_cim_2000_to_207c */ - {0x7b50, 0x7b54, 0x2080, 0x1d, 0}, /* up_cim_2080_to_20fc */ - {0x7b50, 0x7b54, 0x00, 0x20, 0}, /* up_cim_00_to_7c */ - {0x7b50, 0x7b54, 0x80, 0x20, 0}, /* up_cim_80_to_fc */ - {0x7b50, 0x7b54, 0x100, 0x11, 0}, /* up_cim_100_to_14c */ - {0x7b50, 0x7b54, 0x200, 0x10, 0}, /* up_cim_200_to_23c */ - {0x7b50, 0x7b54, 0x240, 0x2, 0}, /* up_cim_240_to_244 */ - {0x7b50, 0x7b54, 0x250, 0x2, 0}, /* up_cim_250_to_254 */ - {0x7b50, 0x7b54, 0x260, 0x2, 0}, /* up_cim_260_to_264 */ - {0x7b50, 0x7b54, 0x270, 0x2, 0}, /* up_cim_270_to_274 */ - {0x7b50, 0x7b54, 0x280, 0x20, 0}, /* up_cim_280_to_2fc */ - {0x7b50, 0x7b54, 0x300, 0x20, 0}, /* up_cim_300_to_37c */ - {0x7b50, 0x7b54, 0x380, 0x14, 0}, /* up_cim_380_to_3cc */ - {0x7b50, 0x7b54, 0x4900, 0x4, 0x4}, /* up_cim_4900_to_4c60 */ - {0x7b50, 0x7b54, 0x4904, 0x4, 0x4}, /* up_cim_4904_to_4c64 */ - {0x7b50, 0x7b54, 0x4908, 0x4, 0x4}, /* up_cim_4908_to_4c68 */ - {0x7b50, 0x7b54, 0x4910, 0x4, 0x4}, /* up_cim_4910_to_4c70 */ - {0x7b50, 0x7b54, 0x4914, 0x4, 0x4}, /* up_cim_4914_to_4c74 */ - {0x7b50, 0x7b54, 0x4920, 0x10, 0x10}, /* up_cim_4920_to_4a10 */ - {0x7b50, 0x7b54, 0x4924, 0x10, 0x10}, /* up_cim_4924_to_4a14 */ - {0x7b50, 0x7b54, 0x4928, 0x10, 0x10}, /* up_cim_4928_to_4a18 */ - {0x7b50, 0x7b54, 0x492c, 0x10, 0x10}, /* up_cim_492c_to_4a1c */ -}; - -static const u32 t5_up_cim_reg_array[][IREG_NUM_ELEM + 1] = { - {0x7b50, 0x7b54, 0x2000, 0x20, 0}, /* up_cim_2000_to_207c */ - {0x7b50, 0x7b54, 0x2080, 0x19, 0}, /* up_cim_2080_to_20ec */ - {0x7b50, 0x7b54, 0x00, 0x20, 0}, /* up_cim_00_to_7c */ - {0x7b50, 0x7b54, 0x80, 0x20, 0}, /* up_cim_80_to_fc */ - {0x7b50, 0x7b54, 0x100, 0x11, 0}, /* up_cim_100_to_14c */ - {0x7b50, 0x7b54, 0x200, 0x10, 0}, /* up_cim_200_to_23c */ - {0x7b50, 0x7b54, 0x240, 0x2, 0}, /* up_cim_240_to_244 */ - {0x7b50, 0x7b54, 0x250, 0x2, 0}, /* up_cim_250_to_254 */ - {0x7b50, 0x7b54, 0x260, 0x2, 0}, /* up_cim_260_to_264 */ - {0x7b50, 0x7b54, 0x270, 0x2, 0}, /* up_cim_270_to_274 */ - {0x7b50, 0x7b54, 0x280, 0x20, 0}, /* up_cim_280_to_2fc */ - {0x7b50, 0x7b54, 0x300, 0x20, 0}, /* up_cim_300_to_37c */ - {0x7b50, 0x7b54, 0x380, 0x14, 0}, /* up_cim_380_to_3cc */ -}; - -static const u32 t6_hma_ireg_array[][IREG_NUM_ELEM] = { - {0x51320, 0x51324, 0xa000, 32} /* t6_hma_regs_a000_to_a01f */ -}; #endif /* __CUDBG_ENTITY_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h index fc3813050f0d..c84719e3ca08 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h @@ -70,7 +70,8 @@ enum cudbg_dbg_entity_type { CUDBG_HMA_INDIRECT = 67, CUDBG_HMA = 68, CUDBG_QDESC = 70, - CUDBG_MAX_ENTITY = 71, + CUDBG_FLASH = 71, + CUDBG_MAX_ENTITY = 72, }; struct cudbg_init { diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c index d8ab8e366818..75474f810249 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c @@ -15,6 +15,412 @@ #include "cudbg_lib.h" #include "cudbg_zlib.h" +static const u32 t6_tp_pio_array[][IREG_NUM_ELEM] = { + {0x7e40, 0x7e44, 0x020, 28}, /* t6_tp_pio_regs_20_to_3b */ + {0x7e40, 0x7e44, 0x040, 10}, /* t6_tp_pio_regs_40_to_49 */ + {0x7e40, 0x7e44, 0x050, 10}, /* t6_tp_pio_regs_50_to_59 */ + {0x7e40, 0x7e44, 0x060, 14}, /* t6_tp_pio_regs_60_to_6d */ + {0x7e40, 0x7e44, 0x06F, 1}, /* t6_tp_pio_regs_6f */ + {0x7e40, 0x7e44, 0x070, 6}, /* t6_tp_pio_regs_70_to_75 */ + {0x7e40, 0x7e44, 0x130, 18}, /* t6_tp_pio_regs_130_to_141 */ + {0x7e40, 0x7e44, 0x145, 19}, /* t6_tp_pio_regs_145_to_157 */ + {0x7e40, 0x7e44, 0x160, 1}, /* t6_tp_pio_regs_160 */ + {0x7e40, 0x7e44, 0x230, 25}, /* t6_tp_pio_regs_230_to_248 */ + {0x7e40, 0x7e44, 0x24a, 3}, /* t6_tp_pio_regs_24c */ + {0x7e40, 0x7e44, 0x8C0, 1} /* t6_tp_pio_regs_8c0 */ +}; + +static const u32 t5_tp_pio_array[][IREG_NUM_ELEM] = { + {0x7e40, 0x7e44, 0x020, 28}, /* t5_tp_pio_regs_20_to_3b */ + {0x7e40, 0x7e44, 0x040, 19}, /* t5_tp_pio_regs_40_to_52 */ + {0x7e40, 0x7e44, 0x054, 2}, /* t5_tp_pio_regs_54_to_55 */ + {0x7e40, 0x7e44, 0x060, 13}, /* t5_tp_pio_regs_60_to_6c */ + {0x7e40, 0x7e44, 0x06F, 1}, /* t5_tp_pio_regs_6f */ + {0x7e40, 0x7e44, 0x120, 4}, /* t5_tp_pio_regs_120_to_123 */ + {0x7e40, 0x7e44, 0x12b, 2}, /* t5_tp_pio_regs_12b_to_12c */ + {0x7e40, 0x7e44, 0x12f, 21}, /* t5_tp_pio_regs_12f_to_143 */ + {0x7e40, 0x7e44, 0x145, 19}, /* t5_tp_pio_regs_145_to_157 */ + {0x7e40, 0x7e44, 0x230, 25}, /* t5_tp_pio_regs_230_to_248 */ + {0x7e40, 0x7e44, 0x8C0, 1} /* t5_tp_pio_regs_8c0 */ +}; + +static const u32 t6_tp_tm_pio_array[][IREG_NUM_ELEM] = { + {0x7e18, 0x7e1c, 0x0, 12} +}; + +static const u32 t5_tp_tm_pio_array[][IREG_NUM_ELEM] = { + {0x7e18, 0x7e1c, 0x0, 12} +}; + +static const u32 t6_tp_mib_index_array[6][IREG_NUM_ELEM] = { + {0x7e50, 0x7e54, 0x0, 13}, + {0x7e50, 0x7e54, 0x10, 6}, + {0x7e50, 0x7e54, 0x18, 21}, + {0x7e50, 0x7e54, 0x30, 32}, + {0x7e50, 0x7e54, 0x50, 22}, + {0x7e50, 0x7e54, 0x68, 12} +}; + +static const u32 t5_tp_mib_index_array[9][IREG_NUM_ELEM] = { + {0x7e50, 0x7e54, 0x0, 13}, + {0x7e50, 0x7e54, 0x10, 6}, + {0x7e50, 0x7e54, 0x18, 8}, + {0x7e50, 0x7e54, 0x20, 13}, + {0x7e50, 0x7e54, 0x30, 16}, + {0x7e50, 0x7e54, 0x40, 16}, + {0x7e50, 0x7e54, 0x50, 16}, + {0x7e50, 0x7e54, 0x60, 6}, + {0x7e50, 0x7e54, 0x68, 4} +}; + +static const u32 t5_sge_dbg_index_array[2][IREG_NUM_ELEM] = { + {0x10cc, 0x10d0, 0x0, 16}, + {0x10cc, 0x10d4, 0x0, 16}, +}; + +static const u32 t6_sge_qbase_index_array[] = { + /* 1 addr reg SGE_QBASE_INDEX and 4 data reg SGE_QBASE_MAP[0-3] */ + 0x1250, 0x1240, 0x1244, 0x1248, 0x124c, +}; + +static const u32 t5_pcie_pdbg_array[][IREG_NUM_ELEM] = { + {0x5a04, 0x5a0c, 0x00, 0x20}, /* t5_pcie_pdbg_regs_00_to_20 */ + {0x5a04, 0x5a0c, 0x21, 0x20}, /* t5_pcie_pdbg_regs_21_to_40 */ + {0x5a04, 0x5a0c, 0x41, 0x10}, /* t5_pcie_pdbg_regs_41_to_50 */ +}; + +static const u32 t5_pcie_cdbg_array[][IREG_NUM_ELEM] = { + {0x5a10, 0x5a18, 0x00, 0x20}, /* t5_pcie_cdbg_regs_00_to_20 */ + {0x5a10, 0x5a18, 0x21, 0x18}, /* t5_pcie_cdbg_regs_21_to_37 */ +}; + +static const u32 t5_pm_rx_array[][IREG_NUM_ELEM] = { + {0x8FD0, 0x8FD4, 0x10000, 0x20}, /* t5_pm_rx_regs_10000_to_10020 */ + {0x8FD0, 0x8FD4, 0x10021, 0x0D}, /* t5_pm_rx_regs_10021_to_1002c */ +}; + +static const u32 t5_pm_tx_array[][IREG_NUM_ELEM] = { + {0x8FF0, 0x8FF4, 0x10000, 0x20}, /* t5_pm_tx_regs_10000_to_10020 */ + {0x8FF0, 0x8FF4, 0x10021, 0x1D}, /* t5_pm_tx_regs_10021_to_1003c */ +}; + +static const u32 t5_pcie_config_array[][2] = { + {0x0, 0x34}, + {0x3c, 0x40}, + {0x50, 0x64}, + {0x70, 0x80}, + {0x94, 0xa0}, + {0xb0, 0xb8}, + {0xd0, 0xd4}, + {0x100, 0x128}, + {0x140, 0x148}, + {0x150, 0x164}, + {0x170, 0x178}, + {0x180, 0x194}, + {0x1a0, 0x1b8}, + {0x1c0, 0x208}, +}; + +static const u32 t6_ma_ireg_array[][IREG_NUM_ELEM] = { + {0x78f8, 0x78fc, 0xa000, 23}, /* t6_ma_regs_a000_to_a016 */ + {0x78f8, 0x78fc, 0xa400, 30}, /* t6_ma_regs_a400_to_a41e */ + {0x78f8, 0x78fc, 0xa800, 20} /* t6_ma_regs_a800_to_a813 */ +}; + +static const u32 t6_ma_ireg_array2[][IREG_NUM_ELEM] = { + {0x78f8, 0x78fc, 0xe400, 17}, /* t6_ma_regs_e400_to_e600 */ + {0x78f8, 0x78fc, 0xe640, 13} /* t6_ma_regs_e640_to_e7c0 */ +}; + +static const u32 t6_up_cim_reg_array[][IREG_NUM_ELEM + 1] = { + {0x7b50, 0x7b54, 0x2000, 0x20, 0}, /* up_cim_2000_to_207c */ + {0x7b50, 0x7b54, 0x2080, 0x1d, 0}, /* up_cim_2080_to_20fc */ + {0x7b50, 0x7b54, 0x00, 0x20, 0}, /* up_cim_00_to_7c */ + {0x7b50, 0x7b54, 0x80, 0x20, 0}, /* up_cim_80_to_fc */ + {0x7b50, 0x7b54, 0x100, 0x11, 0}, /* up_cim_100_to_14c */ + {0x7b50, 0x7b54, 0x200, 0x10, 0}, /* up_cim_200_to_23c */ + {0x7b50, 0x7b54, 0x240, 0x2, 0}, /* up_cim_240_to_244 */ + {0x7b50, 0x7b54, 0x250, 0x2, 0}, /* up_cim_250_to_254 */ + {0x7b50, 0x7b54, 0x260, 0x2, 0}, /* up_cim_260_to_264 */ + {0x7b50, 0x7b54, 0x270, 0x2, 0}, /* up_cim_270_to_274 */ + {0x7b50, 0x7b54, 0x280, 0x20, 0}, /* up_cim_280_to_2fc */ + {0x7b50, 0x7b54, 0x300, 0x20, 0}, /* up_cim_300_to_37c */ + {0x7b50, 0x7b54, 0x380, 0x14, 0}, /* up_cim_380_to_3cc */ + {0x7b50, 0x7b54, 0x4900, 0x4, 0x4}, /* up_cim_4900_to_4c60 */ + {0x7b50, 0x7b54, 0x4904, 0x4, 0x4}, /* up_cim_4904_to_4c64 */ + {0x7b50, 0x7b54, 0x4908, 0x4, 0x4}, /* up_cim_4908_to_4c68 */ + {0x7b50, 0x7b54, 0x4910, 0x4, 0x4}, /* up_cim_4910_to_4c70 */ + {0x7b50, 0x7b54, 0x4914, 0x4, 0x4}, /* up_cim_4914_to_4c74 */ + {0x7b50, 0x7b54, 0x4920, 0x10, 0x10}, /* up_cim_4920_to_4a10 */ + {0x7b50, 0x7b54, 0x4924, 0x10, 0x10}, /* up_cim_4924_to_4a14 */ + {0x7b50, 0x7b54, 0x4928, 0x10, 0x10}, /* up_cim_4928_to_4a18 */ + {0x7b50, 0x7b54, 0x492c, 0x10, 0x10}, /* up_cim_492c_to_4a1c */ +}; + +static const u32 t5_up_cim_reg_array[][IREG_NUM_ELEM + 1] = { + {0x7b50, 0x7b54, 0x2000, 0x20, 0}, /* up_cim_2000_to_207c */ + {0x7b50, 0x7b54, 0x2080, 0x19, 0}, /* up_cim_2080_to_20ec */ + {0x7b50, 0x7b54, 0x00, 0x20, 0}, /* up_cim_00_to_7c */ + {0x7b50, 0x7b54, 0x80, 0x20, 0}, /* up_cim_80_to_fc */ + {0x7b50, 0x7b54, 0x100, 0x11, 0}, /* up_cim_100_to_14c */ + {0x7b50, 0x7b54, 0x200, 0x10, 0}, /* up_cim_200_to_23c */ + {0x7b50, 0x7b54, 0x240, 0x2, 0}, /* up_cim_240_to_244 */ + {0x7b50, 0x7b54, 0x250, 0x2, 0}, /* up_cim_250_to_254 */ + {0x7b50, 0x7b54, 0x260, 0x2, 0}, /* up_cim_260_to_264 */ + {0x7b50, 0x7b54, 0x270, 0x2, 0}, /* up_cim_270_to_274 */ + {0x7b50, 0x7b54, 0x280, 0x20, 0}, /* up_cim_280_to_2fc */ + {0x7b50, 0x7b54, 0x300, 0x20, 0}, /* up_cim_300_to_37c */ + {0x7b50, 0x7b54, 0x380, 0x14, 0}, /* up_cim_380_to_3cc */ +}; + +static const u32 t6_hma_ireg_array[][IREG_NUM_ELEM] = { + {0x51320, 0x51324, 0xa000, 32} /* t6_hma_regs_a000_to_a01f */ +}; + +u32 cudbg_get_entity_length(struct adapter *adap, u32 entity) +{ + struct cudbg_tcam tcam_region = { 0 }; + u32 value, n = 0, len = 0; + + switch (entity) { + case CUDBG_REG_DUMP: + switch (CHELSIO_CHIP_VERSION(adap->params.chip)) { + case CHELSIO_T4: + len = T4_REGMAP_SIZE; + break; + case CHELSIO_T5: + case CHELSIO_T6: + len = T5_REGMAP_SIZE; + break; + default: + break; + } + break; + case CUDBG_DEV_LOG: + len = adap->params.devlog.size; + break; + case CUDBG_CIM_LA: + if (is_t6(adap->params.chip)) { + len = adap->params.cim_la_size / 10 + 1; + len *= 10 * sizeof(u32); + } else { + len = adap->params.cim_la_size / 8; + len *= 8 * sizeof(u32); + } + len += sizeof(u32); /* for reading CIM LA configuration */ + break; + case CUDBG_CIM_MA_LA: + len = 2 * CIM_MALA_SIZE * 5 * sizeof(u32); + break; + case CUDBG_CIM_QCFG: + len = sizeof(struct cudbg_cim_qcfg); + break; + case CUDBG_CIM_IBQ_TP0: + case CUDBG_CIM_IBQ_TP1: + case CUDBG_CIM_IBQ_ULP: + case CUDBG_CIM_IBQ_SGE0: + case CUDBG_CIM_IBQ_SGE1: + case CUDBG_CIM_IBQ_NCSI: + len = CIM_IBQ_SIZE * 4 * sizeof(u32); + break; + case CUDBG_CIM_OBQ_ULP0: + len = cudbg_cim_obq_size(adap, 0); + break; + case CUDBG_CIM_OBQ_ULP1: + len = cudbg_cim_obq_size(adap, 1); + break; + case CUDBG_CIM_OBQ_ULP2: + len = cudbg_cim_obq_size(adap, 2); + break; + case CUDBG_CIM_OBQ_ULP3: + len = cudbg_cim_obq_size(adap, 3); + break; + case CUDBG_CIM_OBQ_SGE: + len = cudbg_cim_obq_size(adap, 4); + break; + case CUDBG_CIM_OBQ_NCSI: + len = cudbg_cim_obq_size(adap, 5); + break; + case CUDBG_CIM_OBQ_RXQ0: + len = cudbg_cim_obq_size(adap, 6); + break; + case CUDBG_CIM_OBQ_RXQ1: + len = cudbg_cim_obq_size(adap, 7); + break; + case CUDBG_EDC0: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & EDRAM0_ENABLE_F) { + value = t4_read_reg(adap, MA_EDRAM0_BAR_A); + len = EDRAM0_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; + case CUDBG_EDC1: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & EDRAM1_ENABLE_F) { + value = t4_read_reg(adap, MA_EDRAM1_BAR_A); + len = EDRAM1_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; + case CUDBG_MC0: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & EXT_MEM0_ENABLE_F) { + value = t4_read_reg(adap, MA_EXT_MEMORY0_BAR_A); + len = EXT_MEM0_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; + case CUDBG_MC1: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & EXT_MEM1_ENABLE_F) { + value = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A); + len = EXT_MEM1_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; + case CUDBG_RSS: + len = t4_chip_rss_size(adap) * sizeof(u16); + break; + case CUDBG_RSS_VF_CONF: + len = adap->params.arch.vfcount * + sizeof(struct cudbg_rss_vf_conf); + break; + case CUDBG_PATH_MTU: + len = NMTUS * sizeof(u16); + break; + case CUDBG_PM_STATS: + len = sizeof(struct cudbg_pm_stats); + break; + case CUDBG_HW_SCHED: + len = sizeof(struct cudbg_hw_sched); + break; + case CUDBG_TP_INDIRECT: + switch (CHELSIO_CHIP_VERSION(adap->params.chip)) { + case CHELSIO_T5: + n = sizeof(t5_tp_pio_array) + + sizeof(t5_tp_tm_pio_array) + + sizeof(t5_tp_mib_index_array); + break; + case CHELSIO_T6: + n = sizeof(t6_tp_pio_array) + + sizeof(t6_tp_tm_pio_array) + + sizeof(t6_tp_mib_index_array); + break; + default: + break; + } + n = n / (IREG_NUM_ELEM * sizeof(u32)); + len = sizeof(struct ireg_buf) * n; + break; + case CUDBG_SGE_INDIRECT: + len = sizeof(struct ireg_buf) * 2 + + sizeof(struct sge_qbase_reg_field); + break; + case CUDBG_ULPRX_LA: + len = sizeof(struct cudbg_ulprx_la); + break; + case CUDBG_TP_LA: + len = sizeof(struct cudbg_tp_la) + TPLA_SIZE * sizeof(u64); + break; + case CUDBG_MEMINFO: + len = sizeof(struct cudbg_ver_hdr) + + sizeof(struct cudbg_meminfo); + break; + case CUDBG_CIM_PIF_LA: + len = sizeof(struct cudbg_cim_pif_la); + len += 2 * CIM_PIFLA_SIZE * 6 * sizeof(u32); + break; + case CUDBG_CLK: + len = sizeof(struct cudbg_clk_info); + break; + case CUDBG_PCIE_INDIRECT: + n = sizeof(t5_pcie_pdbg_array) / (IREG_NUM_ELEM * sizeof(u32)); + len = sizeof(struct ireg_buf) * n * 2; + break; + case CUDBG_PM_INDIRECT: + n = sizeof(t5_pm_rx_array) / (IREG_NUM_ELEM * sizeof(u32)); + len = sizeof(struct ireg_buf) * n * 2; + break; + case CUDBG_TID_INFO: + len = sizeof(struct cudbg_tid_info_region_rev1); + break; + case CUDBG_PCIE_CONFIG: + len = sizeof(u32) * CUDBG_NUM_PCIE_CONFIG_REGS; + break; + case CUDBG_DUMP_CONTEXT: + len = cudbg_dump_context_size(adap); + break; + case CUDBG_MPS_TCAM: + len = sizeof(struct cudbg_mps_tcam) * + adap->params.arch.mps_tcam_size; + break; + case CUDBG_VPD_DATA: + len = sizeof(struct cudbg_vpd_data); + break; + case CUDBG_LE_TCAM: + cudbg_fill_le_tcam_info(adap, &tcam_region); + len = sizeof(struct cudbg_tcam) + + sizeof(struct cudbg_tid_data) * tcam_region.max_tid; + break; + case CUDBG_CCTRL: + len = sizeof(u16) * NMTUS * NCCTRL_WIN; + break; + case CUDBG_MA_INDIRECT: + if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) { + n = sizeof(t6_ma_ireg_array) / + (IREG_NUM_ELEM * sizeof(u32)); + len = sizeof(struct ireg_buf) * n * 2; + } + break; + case CUDBG_ULPTX_LA: + len = sizeof(struct cudbg_ver_hdr) + + sizeof(struct cudbg_ulptx_la); + break; + case CUDBG_UP_CIM_INDIRECT: + n = 0; + if (is_t5(adap->params.chip)) + n = sizeof(t5_up_cim_reg_array) / + ((IREG_NUM_ELEM + 1) * sizeof(u32)); + else if (is_t6(adap->params.chip)) + n = sizeof(t6_up_cim_reg_array) / + ((IREG_NUM_ELEM + 1) * sizeof(u32)); + len = sizeof(struct ireg_buf) * n; + break; + case CUDBG_PBT_TABLE: + len = sizeof(struct cudbg_pbt_tables); + break; + case CUDBG_MBOX_LOG: + len = sizeof(struct cudbg_mbox_log) * adap->mbox_log->size; + break; + case CUDBG_HMA_INDIRECT: + if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) { + n = sizeof(t6_hma_ireg_array) / + (IREG_NUM_ELEM * sizeof(u32)); + len = sizeof(struct ireg_buf) * n; + } + break; + case CUDBG_HMA: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & HMA_MUX_F) { + /* In T6, there's no MC1. So, HMA shares MC1 + * address space. + */ + value = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A); + len = EXT_MEM1_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; + case CUDBG_QDESC: + cudbg_fill_qdesc_num_and_size(adap, NULL, &len); + break; + default: + break; + } + + return len; +} + static int cudbg_do_compression(struct cudbg_init *pdbg_init, struct cudbg_buffer *pin_buff, struct cudbg_buffer *dbg_buff) @@ -3158,3 +3564,40 @@ out_free: return rc; } + +int cudbg_collect_flash(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err) +{ + struct adapter *padap = pdbg_init->adap; + u32 count = padap->params.sf_size, n; + struct cudbg_buffer temp_buff = {0}; + u32 addr, i; + int rc; + + addr = FLASH_EXP_ROM_START; + + for (i = 0; i < count; i += SF_PAGE_SIZE) { + n = min_t(u32, count - i, SF_PAGE_SIZE); + + rc = cudbg_get_buff(pdbg_init, dbg_buff, n, &temp_buff); + if (rc) { + cudbg_err->sys_warn = CUDBG_STATUS_PARTIAL_DATA; + goto out; + } + rc = t4_read_flash(padap, addr, n, (u32 *)temp_buff.data, 0); + if (rc) + goto out; + + addr += (n * 4); + rc = cudbg_write_and_release_buff(pdbg_init, &temp_buff, + dbg_buff); + if (rc) { + cudbg_err->sys_warn = CUDBG_STATUS_PARTIAL_DATA; + goto out; + } + } + +out: + return rc; +} diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h index 10ee6ed1d932..d6d6cd298930 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h @@ -162,7 +162,11 @@ int cudbg_collect_hma_meminfo(struct cudbg_init *pdbg_init, int cudbg_collect_qdesc(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); +int cudbg_collect_flash(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err); +u32 cudbg_get_entity_length(struct adapter *adap, u32 entity); struct cudbg_entity_hdr *cudbg_get_entity_hdr(void *outbuf, int i); void cudbg_align_debug_buffer(struct cudbg_buffer *dbg_buff, struct cudbg_entity_hdr *entity_hdr); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index cf69c6edcfec..d811df1b93d9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -139,6 +139,64 @@ enum cc_fec { FEC_BASER_RS = 1 << 2 /* BaseR/Reed-Solomon */ }; +enum { + CXGB4_ETHTOOL_FLASH_FW = 1, + CXGB4_ETHTOOL_FLASH_PHY = 2, + CXGB4_ETHTOOL_FLASH_BOOT = 3, + CXGB4_ETHTOOL_FLASH_BOOTCFG = 4 +}; + +struct cxgb4_bootcfg_data { + __le16 signature; + __u8 reserved[2]; +}; + +struct cxgb4_pcir_data { + __le32 signature; /* Signature. The string "PCIR" */ + __le16 vendor_id; /* Vendor Identification */ + __le16 device_id; /* Device Identification */ + __u8 vital_product[2]; /* Pointer to Vital Product Data */ + __u8 length[2]; /* PCIR Data Structure Length */ + __u8 revision; /* PCIR Data Structure Revision */ + __u8 class_code[3]; /* Class Code */ + __u8 image_length[2]; /* Image Length. Multiple of 512B */ + __u8 code_revision[2]; /* Revision Level of Code/Data */ + __u8 code_type; + __u8 indicator; + __u8 reserved[2]; +}; + +/* BIOS boot headers */ +struct cxgb4_pci_exp_rom_header { + __le16 signature; /* ROM Signature. Should be 0xaa55 */ + __u8 reserved[22]; /* Reserved per processor Architecture data */ + __le16 pcir_offset; /* Offset to PCI Data Structure */ +}; + +/* Legacy PCI Expansion ROM Header */ +struct legacy_pci_rom_hdr { + __u8 signature[2]; /* ROM Signature. Should be 0xaa55 */ + __u8 size512; /* Current Image Size in units of 512 bytes */ + __u8 initentry_point[4]; + __u8 cksum; /* Checksum computed on the entire Image */ + __u8 reserved[16]; /* Reserved */ + __le16 pcir_offset; /* Offset to PCI Data Struture */ +}; + +#define CXGB4_HDR_CODE1 0x00 +#define CXGB4_HDR_CODE2 0x03 +#define CXGB4_HDR_INDI 0x80 + +/* BOOT constants */ +enum { + BOOT_CFG_SIG = 0x4243, + BOOT_SIZE_INC = 512, + BOOT_SIGNATURE = 0xaa55, + BOOT_MIN_SIZE = sizeof(struct cxgb4_pci_exp_rom_header), + BOOT_MAX_SIZE = 1024 * BOOT_SIZE_INC, + PCIR_SIGNATURE = 0x52494350 +}; + struct port_stats { u64 tx_octets; /* total # of octets in good frames */ u64 tx_frames; /* all good frames */ @@ -492,6 +550,11 @@ struct trace_params { unsigned char port; }; +struct cxgb4_fw_data { + __be32 signature; + __u8 reserved[4]; +}; + /* Firmware Port Capabilities types. */ typedef u16 fw_port_cap16_t; /* 16-bit Port Capabilities integral value */ @@ -1003,6 +1066,17 @@ struct mps_entries_ref { refcount_t refcnt; }; +struct cxgb4_ethtool_filter_info { + u32 *loc_array; /* Array holding the actual TIDs set to filters */ + unsigned long *bmap; /* Bitmap for managing filters in use */ + u32 in_use; /* # of filters in use */ +}; + +struct cxgb4_ethtool_filter { + u32 nentries; /* Adapter wide number of supported filters */ + struct cxgb4_ethtool_filter_info *port; /* Per port entry */ +}; + struct adapter { void __iomem *regs; void __iomem *bar2; @@ -1128,6 +1202,9 @@ struct adapter { /* TC MATCHALL classifier offload */ struct cxgb4_tc_matchall *tc_matchall; + + /* Ethtool n-tuple */ + struct cxgb4_ethtool_filter *ethtool_filters; }; /* Support for "sched-class" command to allow a TX Scheduling Class to be @@ -1736,8 +1813,7 @@ int t4_get_pfres(struct adapter *adapter); int t4_read_flash(struct adapter *adapter, unsigned int addr, unsigned int nwords, u32 *data, int byte_oriented); int t4_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size); -int t4_load_phy_fw(struct adapter *adap, - int win, spinlock_t *lock, +int t4_load_phy_fw(struct adapter *adap, int win, int (*phy_fw_version)(const u8 *, size_t), const u8 *phy_fw_data, size_t phy_fw_size); int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver); @@ -1988,6 +2064,10 @@ void t4_register_netevent_notifier(void); int t4_i2c_rd(struct adapter *adap, unsigned int mbox, int port, unsigned int devid, unsigned int offset, unsigned int len, u8 *buf); +int t4_load_boot(struct adapter *adap, u8 *boot_data, + unsigned int boot_addr, unsigned int size); +int t4_load_bootcfg(struct adapter *adap, + const u8 *cfg_data, unsigned int size); void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, struct sge_fl *fl); void free_tx_desc(struct adapter *adap, struct sge_txq *q, unsigned int n, bool unmap); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c index e374b413d9ac..77648e4ab4cc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c @@ -66,249 +66,9 @@ static const struct cxgb4_collect_entity cxgb4_collect_hw_dump[] = { { CUDBG_HMA_INDIRECT, cudbg_collect_hma_indirect }, }; -static u32 cxgb4_get_entity_length(struct adapter *adap, u32 entity) -{ - struct cudbg_tcam tcam_region = { 0 }; - u32 value, n = 0, len = 0; - - switch (entity) { - case CUDBG_REG_DUMP: - switch (CHELSIO_CHIP_VERSION(adap->params.chip)) { - case CHELSIO_T4: - len = T4_REGMAP_SIZE; - break; - case CHELSIO_T5: - case CHELSIO_T6: - len = T5_REGMAP_SIZE; - break; - default: - break; - } - break; - case CUDBG_DEV_LOG: - len = adap->params.devlog.size; - break; - case CUDBG_CIM_LA: - if (is_t6(adap->params.chip)) { - len = adap->params.cim_la_size / 10 + 1; - len *= 10 * sizeof(u32); - } else { - len = adap->params.cim_la_size / 8; - len *= 8 * sizeof(u32); - } - len += sizeof(u32); /* for reading CIM LA configuration */ - break; - case CUDBG_CIM_MA_LA: - len = 2 * CIM_MALA_SIZE * 5 * sizeof(u32); - break; - case CUDBG_CIM_QCFG: - len = sizeof(struct cudbg_cim_qcfg); - break; - case CUDBG_CIM_IBQ_TP0: - case CUDBG_CIM_IBQ_TP1: - case CUDBG_CIM_IBQ_ULP: - case CUDBG_CIM_IBQ_SGE0: - case CUDBG_CIM_IBQ_SGE1: - case CUDBG_CIM_IBQ_NCSI: - len = CIM_IBQ_SIZE * 4 * sizeof(u32); - break; - case CUDBG_CIM_OBQ_ULP0: - len = cudbg_cim_obq_size(adap, 0); - break; - case CUDBG_CIM_OBQ_ULP1: - len = cudbg_cim_obq_size(adap, 1); - break; - case CUDBG_CIM_OBQ_ULP2: - len = cudbg_cim_obq_size(adap, 2); - break; - case CUDBG_CIM_OBQ_ULP3: - len = cudbg_cim_obq_size(adap, 3); - break; - case CUDBG_CIM_OBQ_SGE: - len = cudbg_cim_obq_size(adap, 4); - break; - case CUDBG_CIM_OBQ_NCSI: - len = cudbg_cim_obq_size(adap, 5); - break; - case CUDBG_CIM_OBQ_RXQ0: - len = cudbg_cim_obq_size(adap, 6); - break; - case CUDBG_CIM_OBQ_RXQ1: - len = cudbg_cim_obq_size(adap, 7); - break; - case CUDBG_EDC0: - value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); - if (value & EDRAM0_ENABLE_F) { - value = t4_read_reg(adap, MA_EDRAM0_BAR_A); - len = EDRAM0_SIZE_G(value); - } - len = cudbg_mbytes_to_bytes(len); - break; - case CUDBG_EDC1: - value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); - if (value & EDRAM1_ENABLE_F) { - value = t4_read_reg(adap, MA_EDRAM1_BAR_A); - len = EDRAM1_SIZE_G(value); - } - len = cudbg_mbytes_to_bytes(len); - break; - case CUDBG_MC0: - value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); - if (value & EXT_MEM0_ENABLE_F) { - value = t4_read_reg(adap, MA_EXT_MEMORY0_BAR_A); - len = EXT_MEM0_SIZE_G(value); - } - len = cudbg_mbytes_to_bytes(len); - break; - case CUDBG_MC1: - value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); - if (value & EXT_MEM1_ENABLE_F) { - value = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A); - len = EXT_MEM1_SIZE_G(value); - } - len = cudbg_mbytes_to_bytes(len); - break; - case CUDBG_RSS: - len = t4_chip_rss_size(adap) * sizeof(u16); - break; - case CUDBG_RSS_VF_CONF: - len = adap->params.arch.vfcount * - sizeof(struct cudbg_rss_vf_conf); - break; - case CUDBG_PATH_MTU: - len = NMTUS * sizeof(u16); - break; - case CUDBG_PM_STATS: - len = sizeof(struct cudbg_pm_stats); - break; - case CUDBG_HW_SCHED: - len = sizeof(struct cudbg_hw_sched); - break; - case CUDBG_TP_INDIRECT: - switch (CHELSIO_CHIP_VERSION(adap->params.chip)) { - case CHELSIO_T5: - n = sizeof(t5_tp_pio_array) + - sizeof(t5_tp_tm_pio_array) + - sizeof(t5_tp_mib_index_array); - break; - case CHELSIO_T6: - n = sizeof(t6_tp_pio_array) + - sizeof(t6_tp_tm_pio_array) + - sizeof(t6_tp_mib_index_array); - break; - default: - break; - } - n = n / (IREG_NUM_ELEM * sizeof(u32)); - len = sizeof(struct ireg_buf) * n; - break; - case CUDBG_SGE_INDIRECT: - len = sizeof(struct ireg_buf) * 2 + - sizeof(struct sge_qbase_reg_field); - break; - case CUDBG_ULPRX_LA: - len = sizeof(struct cudbg_ulprx_la); - break; - case CUDBG_TP_LA: - len = sizeof(struct cudbg_tp_la) + TPLA_SIZE * sizeof(u64); - break; - case CUDBG_MEMINFO: - len = sizeof(struct cudbg_ver_hdr) + - sizeof(struct cudbg_meminfo); - break; - case CUDBG_CIM_PIF_LA: - len = sizeof(struct cudbg_cim_pif_la); - len += 2 * CIM_PIFLA_SIZE * 6 * sizeof(u32); - break; - case CUDBG_CLK: - len = sizeof(struct cudbg_clk_info); - break; - case CUDBG_PCIE_INDIRECT: - n = sizeof(t5_pcie_pdbg_array) / (IREG_NUM_ELEM * sizeof(u32)); - len = sizeof(struct ireg_buf) * n * 2; - break; - case CUDBG_PM_INDIRECT: - n = sizeof(t5_pm_rx_array) / (IREG_NUM_ELEM * sizeof(u32)); - len = sizeof(struct ireg_buf) * n * 2; - break; - case CUDBG_TID_INFO: - len = sizeof(struct cudbg_tid_info_region_rev1); - break; - case CUDBG_PCIE_CONFIG: - len = sizeof(u32) * CUDBG_NUM_PCIE_CONFIG_REGS; - break; - case CUDBG_DUMP_CONTEXT: - len = cudbg_dump_context_size(adap); - break; - case CUDBG_MPS_TCAM: - len = sizeof(struct cudbg_mps_tcam) * - adap->params.arch.mps_tcam_size; - break; - case CUDBG_VPD_DATA: - len = sizeof(struct cudbg_vpd_data); - break; - case CUDBG_LE_TCAM: - cudbg_fill_le_tcam_info(adap, &tcam_region); - len = sizeof(struct cudbg_tcam) + - sizeof(struct cudbg_tid_data) * tcam_region.max_tid; - break; - case CUDBG_CCTRL: - len = sizeof(u16) * NMTUS * NCCTRL_WIN; - break; - case CUDBG_MA_INDIRECT: - if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) { - n = sizeof(t6_ma_ireg_array) / - (IREG_NUM_ELEM * sizeof(u32)); - len = sizeof(struct ireg_buf) * n * 2; - } - break; - case CUDBG_ULPTX_LA: - len = sizeof(struct cudbg_ver_hdr) + - sizeof(struct cudbg_ulptx_la); - break; - case CUDBG_UP_CIM_INDIRECT: - n = 0; - if (is_t5(adap->params.chip)) - n = sizeof(t5_up_cim_reg_array) / - ((IREG_NUM_ELEM + 1) * sizeof(u32)); - else if (is_t6(adap->params.chip)) - n = sizeof(t6_up_cim_reg_array) / - ((IREG_NUM_ELEM + 1) * sizeof(u32)); - len = sizeof(struct ireg_buf) * n; - break; - case CUDBG_PBT_TABLE: - len = sizeof(struct cudbg_pbt_tables); - break; - case CUDBG_MBOX_LOG: - len = sizeof(struct cudbg_mbox_log) * adap->mbox_log->size; - break; - case CUDBG_HMA_INDIRECT: - if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) { - n = sizeof(t6_hma_ireg_array) / - (IREG_NUM_ELEM * sizeof(u32)); - len = sizeof(struct ireg_buf) * n; - } - break; - case CUDBG_HMA: - value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); - if (value & HMA_MUX_F) { - /* In T6, there's no MC1. So, HMA shares MC1 - * address space. - */ - value = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A); - len = EXT_MEM1_SIZE_G(value); - } - len = cudbg_mbytes_to_bytes(len); - break; - case CUDBG_QDESC: - cudbg_fill_qdesc_num_and_size(adap, NULL, &len); - break; - default: - break; - } - - return len; -} +static const struct cxgb4_collect_entity cxgb4_collect_flash_dump[] = { + { CUDBG_FLASH, cudbg_collect_flash }, +}; u32 cxgb4_get_dump_length(struct adapter *adap, u32 flag) { @@ -319,17 +79,20 @@ u32 cxgb4_get_dump_length(struct adapter *adap, u32 flag) if (flag & CXGB4_ETH_DUMP_HW) { for (i = 0; i < ARRAY_SIZE(cxgb4_collect_hw_dump); i++) { entity = cxgb4_collect_hw_dump[i].entity; - len += cxgb4_get_entity_length(adap, entity); + len += cudbg_get_entity_length(adap, entity); } } if (flag & CXGB4_ETH_DUMP_MEM) { for (i = 0; i < ARRAY_SIZE(cxgb4_collect_mem_dump); i++) { entity = cxgb4_collect_mem_dump[i].entity; - len += cxgb4_get_entity_length(adap, entity); + len += cudbg_get_entity_length(adap, entity); } } + if (flag & CXGB4_ETH_DUMP_FLASH) + len += adap->params.sf_size; + /* If compression is enabled, a smaller destination buffer is enough */ wsize = cudbg_get_workspace_size(); if (wsize && len > CUDBG_DUMP_BUFF_SIZE) @@ -468,6 +231,13 @@ int cxgb4_cudbg_collect(struct adapter *adap, void *buf, u32 *buf_size, buf, &total_size); + if (flag & CXGB4_ETH_DUMP_FLASH) + cxgb4_cudbg_collect_entity(&cudbg_init, &dbg_buff, + cxgb4_collect_flash_dump, + ARRAY_SIZE(cxgb4_collect_flash_dump), + buf, + &total_size); + cudbg_free_compress_buff(&cudbg_init); cudbg_hdr->data_len = total_size; if (cudbg_init.compress_type != CUDBG_COMPRESSION_NONE) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h index 66b805c7a92c..c04a49b6378d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h @@ -27,6 +27,7 @@ enum CXGB4_ETHTOOL_DUMP_FLAGS { CXGB4_ETH_DUMP_NONE = ETH_FW_DUMP_DISABLE, CXGB4_ETH_DUMP_MEM = (1 << 0), /* On-Chip Memory Dumps */ CXGB4_ETH_DUMP_HW = (1 << 1), /* various FW and HW dumps */ + CXGB4_ETH_DUMP_FLASH = (1 << 2), /* Dump flash memory */ }; #define CXGB4_ETH_DUMP_ALL (CXGB4_ETH_DUMP_MEM | CXGB4_ETH_DUMP_HW) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index f27be1132d37..b66a2e6cbbeb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -10,6 +10,8 @@ #include "t4_regs.h" #include "t4fw_api.h" #include "cxgb4_cudbg.h" +#include "cxgb4_filter.h" +#include "cxgb4_tc_flower.h" #define EEPROM_MAGIC 0x38E2F10C @@ -23,6 +25,14 @@ static void set_msglevel(struct net_device *dev, u32 val) netdev2adap(dev)->msg_enable = val; } +static const char * const flash_region_strings[] = { + "All", + "Firmware", + "PHY Firmware", + "Boot", + "Boot CFG", +}; + static const char stats_strings[][ETH_GSTRING_LEN] = { "tx_octets_ok ", "tx_frames_ok ", @@ -1235,15 +1245,211 @@ out: return err; } -static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) +static int cxgb4_ethtool_flash_bootcfg(struct net_device *netdev, + const u8 *data, u32 size) { + struct adapter *adap = netdev2adap(netdev); int ret; - const struct firmware *fw; + + ret = t4_load_bootcfg(adap, data, size); + if (ret) + dev_err(adap->pdev_dev, "Failed to load boot cfg image\n"); + + return ret; +} + +static int cxgb4_ethtool_flash_boot(struct net_device *netdev, + const u8 *bdata, u32 size) +{ + struct adapter *adap = netdev2adap(netdev); + unsigned int offset; + u8 *data; + int ret; + + data = kmemdup(bdata, size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + offset = OFFSET_G(t4_read_reg(adap, PF_REG(0, PCIE_PF_EXPROM_OFST_A))); + + ret = t4_load_boot(adap, data, offset, size); + if (ret) + dev_err(adap->pdev_dev, "Failed to load boot image\n"); + + kfree(data); + return ret; +} + +#define CXGB4_PHY_SIG 0x130000ea + +static int cxgb4_validate_phy_image(const u8 *data, u32 *size) +{ + struct cxgb4_fw_data *header; + + header = (struct cxgb4_fw_data *)data; + if (be32_to_cpu(header->signature) != CXGB4_PHY_SIG) + return -EINVAL; + + return 0; +} + +static int cxgb4_ethtool_flash_phy(struct net_device *netdev, + const u8 *data, u32 size) +{ + struct adapter *adap = netdev2adap(netdev); + int ret; + + ret = cxgb4_validate_phy_image(data, NULL); + if (ret) { + dev_err(adap->pdev_dev, "PHY signature mismatch\n"); + return ret; + } + + spin_lock_bh(&adap->win0_lock); + ret = t4_load_phy_fw(adap, MEMWIN_NIC, NULL, data, size); + spin_unlock_bh(&adap->win0_lock); + if (ret) + dev_err(adap->pdev_dev, "Failed to load PHY FW\n"); + + return ret; +} + +static int cxgb4_ethtool_flash_fw(struct net_device *netdev, + const u8 *data, u32 size) +{ struct adapter *adap = netdev2adap(netdev); unsigned int mbox = PCIE_FW_MASTER_M + 1; - u32 pcie_fw; + int ret; + + /* If the adapter has been fully initialized then we'll go ahead and + * try to get the firmware's cooperation in upgrading to the new + * firmware image otherwise we'll try to do the entire job from the + * host ... and we always "force" the operation in this path. + */ + if (adap->flags & CXGB4_FULL_INIT_DONE) + mbox = adap->mbox; + + ret = t4_fw_upgrade(adap, mbox, data, size, 1); + if (ret) + dev_err(adap->pdev_dev, + "Failed to flash firmware\n"); + + return ret; +} + +static int cxgb4_ethtool_flash_region(struct net_device *netdev, + const u8 *data, u32 size, u32 region) +{ + struct adapter *adap = netdev2adap(netdev); + int ret; + + switch (region) { + case CXGB4_ETHTOOL_FLASH_FW: + ret = cxgb4_ethtool_flash_fw(netdev, data, size); + break; + case CXGB4_ETHTOOL_FLASH_PHY: + ret = cxgb4_ethtool_flash_phy(netdev, data, size); + break; + case CXGB4_ETHTOOL_FLASH_BOOT: + ret = cxgb4_ethtool_flash_boot(netdev, data, size); + break; + case CXGB4_ETHTOOL_FLASH_BOOTCFG: + ret = cxgb4_ethtool_flash_bootcfg(netdev, data, size); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + if (!ret) + dev_info(adap->pdev_dev, + "loading %s successful, reload cxgb4 driver\n", + flash_region_strings[region]); + return ret; +} + +#define CXGB4_FW_SIG 0x4368656c +#define CXGB4_FW_SIG_OFFSET 0x160 + +static int cxgb4_validate_fw_image(const u8 *data, u32 *size) +{ + struct cxgb4_fw_data *header; + + header = (struct cxgb4_fw_data *)&data[CXGB4_FW_SIG_OFFSET]; + if (be32_to_cpu(header->signature) != CXGB4_FW_SIG) + return -EINVAL; + + if (size) + *size = be16_to_cpu(((struct fw_hdr *)data)->len512) * 512; + + return 0; +} + +static int cxgb4_validate_bootcfg_image(const u8 *data, u32 *size) +{ + struct cxgb4_bootcfg_data *header; + + header = (struct cxgb4_bootcfg_data *)data; + if (le16_to_cpu(header->signature) != BOOT_CFG_SIG) + return -EINVAL; + + return 0; +} + +static int cxgb4_validate_boot_image(const u8 *data, u32 *size) +{ + struct cxgb4_pci_exp_rom_header *exp_header; + struct cxgb4_pcir_data *pcir_header; + struct legacy_pci_rom_hdr *header; + const u8 *cur_header = data; + u16 pcir_offset; + + exp_header = (struct cxgb4_pci_exp_rom_header *)data; + + if (le16_to_cpu(exp_header->signature) != BOOT_SIGNATURE) + return -EINVAL; + + if (size) { + do { + header = (struct legacy_pci_rom_hdr *)cur_header; + pcir_offset = le16_to_cpu(header->pcir_offset); + pcir_header = (struct cxgb4_pcir_data *)(cur_header + + pcir_offset); + + *size += header->size512 * 512; + cur_header += header->size512 * 512; + } while (!(pcir_header->indicator & CXGB4_HDR_INDI)); + } + + return 0; +} + +static int cxgb4_ethtool_get_flash_region(const u8 *data, u32 *size) +{ + if (!cxgb4_validate_fw_image(data, size)) + return CXGB4_ETHTOOL_FLASH_FW; + if (!cxgb4_validate_boot_image(data, size)) + return CXGB4_ETHTOOL_FLASH_BOOT; + if (!cxgb4_validate_phy_image(data, size)) + return CXGB4_ETHTOOL_FLASH_PHY; + if (!cxgb4_validate_bootcfg_image(data, size)) + return CXGB4_ETHTOOL_FLASH_BOOTCFG; + + return -EOPNOTSUPP; +} + +static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) +{ + struct adapter *adap = netdev2adap(netdev); + const struct firmware *fw; unsigned int master; u8 master_vld = 0; + const u8 *fw_data; + size_t fw_size; + u32 size = 0; + u32 pcie_fw; + int region; + int ret; pcie_fw = t4_read_reg(adap, PCIE_FW_A); master = PCIE_FW_MASTER_G(pcie_fw); @@ -1261,19 +1467,32 @@ static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) if (ret < 0) return ret; - /* If the adapter has been fully initialized then we'll go ahead and - * try to get the firmware's cooperation in upgrading to the new - * firmware image otherwise we'll try to do the entire job from the - * host ... and we always "force" the operation in this path. - */ - if (adap->flags & CXGB4_FULL_INIT_DONE) - mbox = adap->mbox; + fw_data = fw->data; + fw_size = fw->size; + if (ef->region == ETHTOOL_FLASH_ALL_REGIONS) { + while (fw_size > 0) { + size = 0; + region = cxgb4_ethtool_get_flash_region(fw_data, &size); + if (region < 0 || !size) { + ret = region; + goto out_free_fw; + } + + ret = cxgb4_ethtool_flash_region(netdev, fw_data, size, + region); + if (ret) + goto out_free_fw; + + fw_data += size; + fw_size -= size; + } + } else { + ret = cxgb4_ethtool_flash_region(netdev, fw_data, fw_size, + ef->region); + } - ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1); +out_free_fw: release_firmware(fw); - if (!ret) - dev_info(adap->pdev_dev, - "loaded firmware %s, reload cxgb4 driver\n", ef->data); return ret; } @@ -1355,10 +1574,120 @@ static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key, return -EPERM; } +static struct filter_entry *cxgb4_get_filter_entry(struct adapter *adap, + u32 ftid) +{ + struct tid_info *t = &adap->tids; + struct filter_entry *f; + + if (ftid < t->nhpftids) + f = &adap->tids.hpftid_tab[ftid]; + else if (ftid < t->nftids) + f = &adap->tids.ftid_tab[ftid - t->nhpftids]; + else + f = lookup_tid(&adap->tids, ftid); + + return f; +} + +static void cxgb4_fill_filter_rule(struct ethtool_rx_flow_spec *fs, + struct ch_filter_specification *dfs) +{ + switch (dfs->val.proto) { + case IPPROTO_TCP: + if (dfs->type) + fs->flow_type = TCP_V6_FLOW; + else + fs->flow_type = TCP_V4_FLOW; + break; + case IPPROTO_UDP: + if (dfs->type) + fs->flow_type = UDP_V6_FLOW; + else + fs->flow_type = UDP_V4_FLOW; + break; + } + + if (dfs->type) { + fs->h_u.tcp_ip6_spec.psrc = cpu_to_be16(dfs->val.fport); + fs->m_u.tcp_ip6_spec.psrc = cpu_to_be16(dfs->mask.fport); + fs->h_u.tcp_ip6_spec.pdst = cpu_to_be16(dfs->val.lport); + fs->m_u.tcp_ip6_spec.pdst = cpu_to_be16(dfs->mask.lport); + memcpy(&fs->h_u.tcp_ip6_spec.ip6src, &dfs->val.fip[0], + sizeof(fs->h_u.tcp_ip6_spec.ip6src)); + memcpy(&fs->m_u.tcp_ip6_spec.ip6src, &dfs->mask.fip[0], + sizeof(fs->m_u.tcp_ip6_spec.ip6src)); + memcpy(&fs->h_u.tcp_ip6_spec.ip6dst, &dfs->val.lip[0], + sizeof(fs->h_u.tcp_ip6_spec.ip6dst)); + memcpy(&fs->m_u.tcp_ip6_spec.ip6dst, &dfs->mask.lip[0], + sizeof(fs->m_u.tcp_ip6_spec.ip6dst)); + fs->h_u.tcp_ip6_spec.tclass = dfs->val.tos; + fs->m_u.tcp_ip6_spec.tclass = dfs->mask.tos; + } else { + fs->h_u.tcp_ip4_spec.psrc = cpu_to_be16(dfs->val.fport); + fs->m_u.tcp_ip4_spec.psrc = cpu_to_be16(dfs->mask.fport); + fs->h_u.tcp_ip4_spec.pdst = cpu_to_be16(dfs->val.lport); + fs->m_u.tcp_ip4_spec.pdst = cpu_to_be16(dfs->mask.lport); + memcpy(&fs->h_u.tcp_ip4_spec.ip4src, &dfs->val.fip[0], + sizeof(fs->h_u.tcp_ip4_spec.ip4src)); + memcpy(&fs->m_u.tcp_ip4_spec.ip4src, &dfs->mask.fip[0], + sizeof(fs->m_u.tcp_ip4_spec.ip4src)); + memcpy(&fs->h_u.tcp_ip4_spec.ip4dst, &dfs->val.lip[0], + sizeof(fs->h_u.tcp_ip4_spec.ip4dst)); + memcpy(&fs->m_u.tcp_ip4_spec.ip4dst, &dfs->mask.lip[0], + sizeof(fs->m_u.tcp_ip4_spec.ip4dst)); + fs->h_u.tcp_ip4_spec.tos = dfs->val.tos; + fs->m_u.tcp_ip4_spec.tos = dfs->mask.tos; + } + fs->h_ext.vlan_tci = cpu_to_be16(dfs->val.ivlan); + fs->m_ext.vlan_tci = cpu_to_be16(dfs->mask.ivlan); + fs->flow_type |= FLOW_EXT; + + if (dfs->action == FILTER_DROP) + fs->ring_cookie = RX_CLS_FLOW_DISC; + else + fs->ring_cookie = dfs->iq; +} + +static int cxgb4_ntuple_get_filter(struct net_device *dev, + struct ethtool_rxnfc *cmd, + unsigned int loc) +{ + const struct port_info *pi = netdev_priv(dev); + struct adapter *adap = netdev2adap(dev); + struct filter_entry *f; + int ftid; + + if (!(adap->flags & CXGB4_FULL_INIT_DONE)) + return -EAGAIN; + + /* Check for maximum filter range */ + if (!adap->ethtool_filters) + return -EOPNOTSUPP; + + if (loc >= adap->ethtool_filters->nentries) + return -ERANGE; + + if (!test_bit(loc, adap->ethtool_filters->port[pi->port_id].bmap)) + return -ENOENT; + + ftid = adap->ethtool_filters->port[pi->port_id].loc_array[loc]; + + /* Fetch filter_entry */ + f = cxgb4_get_filter_entry(adap, ftid); + + cxgb4_fill_filter_rule(&cmd->fs, &f->fs); + + return 0; +} + static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rules) { const struct port_info *pi = netdev_priv(dev); + struct adapter *adap = netdev2adap(dev); + unsigned int count = 0, index = 0; + int ret = 0; switch (info->cmd) { case ETHTOOL_GRXFH: { @@ -1414,10 +1743,144 @@ static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, case ETHTOOL_GRXRINGS: info->data = pi->nqsets; return 0; + case ETHTOOL_GRXCLSRLCNT: + info->rule_cnt = + adap->ethtool_filters->port[pi->port_id].in_use; + return 0; + case ETHTOOL_GRXCLSRULE: + return cxgb4_ntuple_get_filter(dev, info, info->fs.location); + case ETHTOOL_GRXCLSRLALL: + info->data = adap->ethtool_filters->nentries; + while (count < info->rule_cnt) { + ret = cxgb4_ntuple_get_filter(dev, info, index); + if (!ret) + rules[count++] = index; + index++; + } + return 0; } + return -EOPNOTSUPP; } +static int cxgb4_ntuple_del_filter(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct cxgb4_ethtool_filter_info *filter_info; + struct adapter *adapter = netdev2adap(dev); + struct port_info *pi = netdev_priv(dev); + struct filter_entry *f; + u32 filter_id; + int ret; + + if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) + return -EAGAIN; /* can still change nfilters */ + + if (!adapter->ethtool_filters) + return -EOPNOTSUPP; + + if (cmd->fs.location >= adapter->ethtool_filters->nentries) { + dev_err(adapter->pdev_dev, + "Location must be < %u", + adapter->ethtool_filters->nentries); + return -ERANGE; + } + + filter_info = &adapter->ethtool_filters->port[pi->port_id]; + + if (!test_bit(cmd->fs.location, filter_info->bmap)) + return -ENOENT; + + filter_id = filter_info->loc_array[cmd->fs.location]; + f = cxgb4_get_filter_entry(adapter, filter_id); + + ret = cxgb4_flow_rule_destroy(dev, f->fs.tc_prio, &f->fs, filter_id); + if (ret) + goto err; + + clear_bit(cmd->fs.location, filter_info->bmap); + filter_info->in_use--; + +err: + return ret; +} + +/* Add Ethtool n-tuple filters. */ +static int cxgb4_ntuple_set_filter(struct net_device *netdev, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec_input input = {}; + struct cxgb4_ethtool_filter_info *filter_info; + struct adapter *adapter = netdev2adap(netdev); + struct port_info *pi = netdev_priv(netdev); + struct ch_filter_specification fs; + struct ethtool_rx_flow_rule *flow; + u32 tid; + int ret; + + if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) + return -EAGAIN; /* can still change nfilters */ + + if (!adapter->ethtool_filters) + return -EOPNOTSUPP; + + if (cmd->fs.location >= adapter->ethtool_filters->nentries) { + dev_err(adapter->pdev_dev, + "Location must be < %u", + adapter->ethtool_filters->nentries); + return -ERANGE; + } + + if (test_bit(cmd->fs.location, + adapter->ethtool_filters->port[pi->port_id].bmap)) + return -EEXIST; + + memset(&fs, 0, sizeof(fs)); + + input.fs = &cmd->fs; + flow = ethtool_rx_flow_rule_create(&input); + if (IS_ERR(flow)) { + ret = PTR_ERR(flow); + goto exit; + } + + fs.hitcnts = 1; + + ret = cxgb4_flow_rule_replace(netdev, flow->rule, cmd->fs.location, + NULL, &fs, &tid); + if (ret) + goto free; + + filter_info = &adapter->ethtool_filters->port[pi->port_id]; + + filter_info->loc_array[cmd->fs.location] = tid; + set_bit(cmd->fs.location, filter_info->bmap); + filter_info->in_use++; + +free: + ethtool_rx_flow_rule_destroy(flow); +exit: + return ret; +} + +static int set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + ret = cxgb4_ntuple_set_filter(dev, cmd); + break; + case ETHTOOL_SRXCLSRLDEL: + ret = cxgb4_ntuple_del_filter(dev, cmd); + break; + default: + break; + } + + return ret; +} + static int set_dump(struct net_device *dev, struct ethtool_dump *eth_dump) { struct adapter *adapter = netdev2adap(dev); @@ -1623,6 +2086,7 @@ static const struct ethtool_ops cxgb_ethtool_ops = { .get_regs_len = get_regs_len, .get_regs = get_regs, .get_rxnfc = get_rxnfc, + .set_rxnfc = set_rxnfc, .get_rxfh_indir_size = get_rss_table_size, .get_rxfh = get_rss_table, .set_rxfh = set_rss_table, @@ -1637,6 +2101,87 @@ static const struct ethtool_ops cxgb_ethtool_ops = { .set_priv_flags = cxgb4_set_priv_flags, }; +void cxgb4_cleanup_ethtool_filters(struct adapter *adap) +{ + struct cxgb4_ethtool_filter_info *eth_filter_info; + u8 i; + + if (!adap->ethtool_filters) + return; + + eth_filter_info = adap->ethtool_filters->port; + + if (eth_filter_info) { + for (i = 0; i < adap->params.nports; i++) { + kvfree(eth_filter_info[i].loc_array); + kfree(eth_filter_info[i].bmap); + } + kfree(eth_filter_info); + } + + kfree(adap->ethtool_filters); +} + +int cxgb4_init_ethtool_filters(struct adapter *adap) +{ + struct cxgb4_ethtool_filter_info *eth_filter_info; + struct cxgb4_ethtool_filter *eth_filter; + struct tid_info *tids = &adap->tids; + u32 nentries, i; + int ret; + + eth_filter = kzalloc(sizeof(*eth_filter), GFP_KERNEL); + if (!eth_filter) + return -ENOMEM; + + eth_filter_info = kcalloc(adap->params.nports, + sizeof(*eth_filter_info), + GFP_KERNEL); + if (!eth_filter_info) { + ret = -ENOMEM; + goto free_eth_filter; + } + + eth_filter->port = eth_filter_info; + + nentries = tids->nhpftids + tids->nftids; + if (is_hashfilter(adap)) + nentries += tids->nhash + + (adap->tids.stid_base - adap->tids.tid_base); + eth_filter->nentries = nentries; + + for (i = 0; i < adap->params.nports; i++) { + eth_filter->port[i].loc_array = kvzalloc(nentries, GFP_KERNEL); + if (!eth_filter->port[i].loc_array) { + ret = -ENOMEM; + goto free_eth_finfo; + } + + eth_filter->port[i].bmap = kcalloc(BITS_TO_LONGS(nentries), + sizeof(unsigned long), + GFP_KERNEL); + if (!eth_filter->port[i].bmap) { + ret = -ENOMEM; + goto free_eth_finfo; + } + } + + adap->ethtool_filters = eth_filter; + return 0; + +free_eth_finfo: + while (i-- > 0) { + kfree(eth_filter->port[i].bmap); + kvfree(eth_filter->port[i].loc_array); + } + kfree(eth_filter_info); + +free_eth_filter: + kfree(eth_filter); + + return ret; +} + void cxgb4_set_ethtool_ops(struct net_device *netdev) { netdev->ethtool_ops = &cxgb_ethtool_ops; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c index 7a7f61a8cdf4..482b633f7679 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c @@ -1159,6 +1159,11 @@ bool is_filter_exact_match(struct adapter *adap, if (!is_hashfilter(adap)) return false; + if ((atomic_read(&adap->tids.hash_tids_in_use) + + atomic_read(&adap->tids.tids_in_use)) >= + (adap->tids.nhash + (adap->tids.stid_base - adap->tids.tid_base))) + return false; + /* Keep tunnel VNI match disabled for hash-filters for now */ if (fs->mask.encap_vld) return false; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h index b0751c0611ec..807a8dafec45 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.h @@ -53,4 +53,6 @@ void clear_all_filters(struct adapter *adapter); void init_hash_filter(struct adapter *adap); bool is_filter_exact_match(struct adapter *adap, struct ch_filter_specification *fs); +void cxgb4_cleanup_ethtool_filters(struct adapter *adap); +int cxgb4_init_ethtool_filters(struct adapter *adap); #endif /* __CXGB4_FILTER_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 0329a6b52087..234f01389cb1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -4147,9 +4147,10 @@ static int adap_init0_phy(struct adapter *adap) /* Load PHY Firmware onto adapter. */ - ret = t4_load_phy_fw(adap, MEMWIN_NIC, &adap->win0_lock, - phy_info->phy_fw_version, + spin_lock_bh(&adap->win0_lock); + ret = t4_load_phy_fw(adap, MEMWIN_NIC, phy_info->phy_fw_version, (u8 *)phyf->data, phyf->size); + spin_unlock_bh(&adap->win0_lock); if (ret < 0) dev_err(adap->pdev_dev, "PHY Firmware transfer error %d\n", -ret); @@ -5861,6 +5862,7 @@ static void free_some_resources(struct adapter *adapter) cxgb4_cleanup_tc_mqprio(adapter); cxgb4_cleanup_tc_flower(adapter); cxgb4_cleanup_tc_u32(adapter); + cxgb4_cleanup_ethtool_filters(adapter); kfree(adapter->sge.egr_map); kfree(adapter->sge.ingr_map); kfree(adapter->sge.starving_fl); @@ -6371,7 +6373,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_RXHASH | NETIF_F_GRO | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | - NETIF_F_HW_TC; + NETIF_F_HW_TC | NETIF_F_NTUPLE; if (chip_ver > CHELSIO_T5) { netdev->hw_enc_features |= NETIF_F_IP_CSUM | @@ -6494,6 +6496,24 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) i); } + if (is_offload(adapter) || is_hashfilter(adapter)) { + if (t4_read_reg(adapter, LE_DB_CONFIG_A) & HASHEN_F) { + u32 v; + + v = t4_read_reg(adapter, LE_DB_HASH_CONFIG_A); + if (chip_ver <= CHELSIO_T5) { + adapter->tids.nhash = 1 << HASHTIDSIZE_G(v); + v = t4_read_reg(adapter, LE_DB_TID_HASHBASE_A); + adapter->tids.hash_base = v / 4; + } else { + adapter->tids.nhash = HASHTBLSIZE_G(v) << 3; + v = t4_read_reg(adapter, + T6_LE_DB_HASH_TID_BASE_A); + adapter->tids.hash_base = v; + } + } + } + if (tid_init(&adapter->tids) < 0) { dev_warn(&pdev->dev, "could not allocate TID table, " "continuing\n"); @@ -6515,22 +6535,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (cxgb4_init_tc_matchall(adapter)) dev_warn(&pdev->dev, "could not offload tc matchall, continuing\n"); - } - - if (is_offload(adapter) || is_hashfilter(adapter)) { - if (t4_read_reg(adapter, LE_DB_CONFIG_A) & HASHEN_F) { - u32 hash_base, hash_reg; - - if (chip_ver <= CHELSIO_T5) { - hash_reg = LE_DB_TID_HASHBASE_A; - hash_base = t4_read_reg(adapter, hash_reg); - adapter->tids.hash_base = hash_base / 4; - } else { - hash_reg = T6_LE_DB_HASH_TID_BASE_A; - hash_base = t4_read_reg(adapter, hash_reg); - adapter->tids.hash_base = hash_base; - } - } + if (cxgb4_init_ethtool_filters(adapter)) + dev_warn(&pdev->dev, + "could not initialize ethtool filters, continuing\n"); } /* See what interrupts we'll be using */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c index 59b65d4db086..ae0e998d9338 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c @@ -77,19 +77,9 @@ static struct ch_tc_flower_entry *ch_flower_lookup(struct adapter *adap, } static void cxgb4_process_flow_match(struct net_device *dev, - struct flow_cls_offload *cls, + struct flow_rule *rule, struct ch_filter_specification *fs) { - struct flow_rule *rule = flow_cls_offload_flow_rule(cls); - u16 addr_type = 0; - - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { - struct flow_match_control match; - - flow_rule_match_control(rule, &match); - addr_type = match.key->addr_type; - } - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { struct flow_match_basic match; u16 ethtype_key, ethtype_mask; @@ -112,7 +102,7 @@ static void cxgb4_process_flow_match(struct net_device *dev, fs->mask.proto = match.mask->ip_proto; } - if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { struct flow_match_ipv4_addrs match; flow_rule_match_ipv4_addrs(rule, &match); @@ -127,7 +117,7 @@ static void cxgb4_process_flow_match(struct net_device *dev, memcpy(&fs->nat_fip[0], &match.key->src, sizeof(match.key->src)); } - if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { struct flow_match_ipv6_addrs match; flow_rule_match_ipv6_addrs(rule, &match); @@ -220,9 +210,8 @@ static void cxgb4_process_flow_match(struct net_device *dev, } static int cxgb4_validate_flow_match(struct net_device *dev, - struct flow_cls_offload *cls) + struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; u16 ethtype_mask = 0; u16 ethtype_key = 0; @@ -426,6 +415,11 @@ void cxgb4_process_flow_actions(struct net_device *in, process_pedit_field(fs, val, mask, offset, htype); } break; + case FLOW_ACTION_QUEUE: + fs->action = FILTER_PASS; + fs->dirsteer = 1; + fs->iq = act->queue.index; + break; default: break; } @@ -610,6 +604,9 @@ int cxgb4_validate_flow_actions(struct net_device *dev, act_pedit = true; } break; + case FLOW_ACTION_QUEUE: + /* Do nothing. cxgb4_set_filter will validate */ + break; default: netdev_err(dev, "%s: Unsupported action\n", __func__); return -EOPNOTSUPP; @@ -683,14 +680,11 @@ out_unlock: spin_unlock_bh(&t->ftid_lock); } -int cxgb4_tc_flower_replace(struct net_device *dev, - struct flow_cls_offload *cls) +int cxgb4_flow_rule_replace(struct net_device *dev, struct flow_rule *rule, + u32 tc_prio, struct netlink_ext_ack *extack, + struct ch_filter_specification *fs, u32 *tid) { - struct flow_rule *rule = flow_cls_offload_flow_rule(cls); - struct netlink_ext_ack *extack = cls->common.extack; struct adapter *adap = netdev2adap(dev); - struct ch_tc_flower_entry *ch_flower; - struct ch_filter_specification *fs; struct filter_ctx ctx; u8 inet_family; int fidx, ret; @@ -698,18 +692,10 @@ int cxgb4_tc_flower_replace(struct net_device *dev, if (cxgb4_validate_flow_actions(dev, &rule->action, extack)) return -EOPNOTSUPP; - if (cxgb4_validate_flow_match(dev, cls)) + if (cxgb4_validate_flow_match(dev, rule)) return -EOPNOTSUPP; - ch_flower = allocate_flower_entry(); - if (!ch_flower) { - netdev_err(dev, "%s: ch_flower alloc failed.\n", __func__); - return -ENOMEM; - } - - fs = &ch_flower->fs; - fs->hitcnts = 1; - cxgb4_process_flow_match(dev, cls, fs); + cxgb4_process_flow_match(dev, rule, fs); cxgb4_process_flow_actions(dev, &rule->action, fs); fs->hash = is_filter_exact_match(adap, fs); @@ -720,12 +706,11 @@ int cxgb4_tc_flower_replace(struct net_device *dev, * existing rules. */ fidx = cxgb4_get_free_ftid(dev, inet_family, fs->hash, - cls->common.prio); + tc_prio); if (fidx < 0) { NL_SET_ERR_MSG_MOD(extack, "No free LETCAM index available"); - ret = -ENOMEM; - goto free_entry; + return -ENOMEM; } if (fidx < adap->tids.nhpftids) { @@ -739,42 +724,70 @@ int cxgb4_tc_flower_replace(struct net_device *dev, if (fs->hash) fidx = 0; - fs->tc_prio = cls->common.prio; - fs->tc_cookie = cls->cookie; + fs->tc_prio = tc_prio; init_completion(&ctx.completion); ret = __cxgb4_set_filter(dev, fidx, fs, &ctx); if (ret) { netdev_err(dev, "%s: filter creation err %d\n", __func__, ret); - goto free_entry; + return ret; } /* Wait for reply */ ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ); - if (!ret) { - ret = -ETIMEDOUT; - goto free_entry; - } + if (!ret) + return -ETIMEDOUT; - ret = ctx.result; /* Check if hw returned error for filter creation */ + if (ctx.result) + return ctx.result; + + *tid = ctx.tid; + + if (fs->hash) + cxgb4_tc_flower_hash_prio_add(adap, tc_prio); + + return 0; +} + +int cxgb4_tc_flower_replace(struct net_device *dev, + struct flow_cls_offload *cls) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct netlink_ext_ack *extack = cls->common.extack; + struct adapter *adap = netdev2adap(dev); + struct ch_tc_flower_entry *ch_flower; + struct ch_filter_specification *fs; + int ret; + + ch_flower = allocate_flower_entry(); + if (!ch_flower) { + netdev_err(dev, "%s: ch_flower alloc failed.\n", __func__); + return -ENOMEM; + } + + fs = &ch_flower->fs; + fs->hitcnts = 1; + fs->tc_cookie = cls->cookie; + + ret = cxgb4_flow_rule_replace(dev, rule, cls->common.prio, extack, fs, + &ch_flower->filter_id); if (ret) goto free_entry; ch_flower->tc_flower_cookie = cls->cookie; - ch_flower->filter_id = ctx.tid; ret = rhashtable_insert_fast(&adap->flower_tbl, &ch_flower->node, adap->flower_ht_params); if (ret) goto del_filter; - if (fs->hash) - cxgb4_tc_flower_hash_prio_add(adap, cls->common.prio); - return 0; del_filter: + if (fs->hash) + cxgb4_tc_flower_hash_prio_del(adap, cls->common.prio); + cxgb4_del_filter(dev, ch_flower->filter_id, &ch_flower->fs); free_entry: @@ -782,23 +795,38 @@ free_entry: return ret; } +int cxgb4_flow_rule_destroy(struct net_device *dev, u32 tc_prio, + struct ch_filter_specification *fs, int tid) +{ + struct adapter *adap = netdev2adap(dev); + u8 hash; + int ret; + + hash = fs->hash; + + ret = cxgb4_del_filter(dev, tid, fs); + if (ret) + return ret; + + if (hash) + cxgb4_tc_flower_hash_prio_del(adap, tc_prio); + + return ret; +} + int cxgb4_tc_flower_destroy(struct net_device *dev, struct flow_cls_offload *cls) { struct adapter *adap = netdev2adap(dev); struct ch_tc_flower_entry *ch_flower; - u32 tc_prio; - bool hash; int ret; ch_flower = ch_flower_lookup(adap, cls->cookie); if (!ch_flower) return -ENOENT; - hash = ch_flower->fs.hash; - tc_prio = ch_flower->fs.tc_prio; - - ret = cxgb4_del_filter(dev, ch_flower->filter_id, &ch_flower->fs); + ret = cxgb4_flow_rule_destroy(dev, ch_flower->fs.tc_prio, + &ch_flower->fs, ch_flower->filter_id); if (ret) goto err; @@ -810,9 +838,6 @@ int cxgb4_tc_flower_destroy(struct net_device *dev, } kfree_rcu(ch_flower, rcu); - if (hash) - cxgb4_tc_flower_hash_prio_del(adap, tc_prio); - err: return ret; } @@ -892,7 +917,7 @@ int cxgb4_tc_flower_stats(struct net_device *dev, if (ofld_stats->prev_packet_count != packets) ofld_stats->last_used = jiffies; flow_stats_update(&cls->stats, bytes - ofld_stats->byte_count, - packets - ofld_stats->packet_count, + packets - ofld_stats->packet_count, 0, ofld_stats->last_used, FLOW_ACTION_HW_STATS_IMMEDIATE); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h index 0a30c96b81ff..befa459324fb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h @@ -121,6 +121,11 @@ int cxgb4_tc_flower_destroy(struct net_device *dev, struct flow_cls_offload *cls); int cxgb4_tc_flower_stats(struct net_device *dev, struct flow_cls_offload *cls); +int cxgb4_flow_rule_replace(struct net_device *dev, struct flow_rule *rule, + u32 tc_prio, struct netlink_ext_ack *extack, + struct ch_filter_specification *fs, u32 *tid); +int cxgb4_flow_rule_destroy(struct net_device *dev, u32 tc_prio, + struct ch_filter_specification *fs, int tid); int cxgb4_init_tc_flower(struct adapter *adap); void cxgb4_cleanup_tc_flower(struct adapter *adap); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c index c88c47a14fbb..c439b5bce9c9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c @@ -346,7 +346,7 @@ int cxgb4_tc_matchall_stats(struct net_device *dev, flow_stats_update(&cls_matchall->stats, bytes - tc_port_matchall->ingress.bytes, packets - tc_port_matchall->ingress.packets, - tc_port_matchall->ingress.last_used, + 0, tc_port_matchall->ingress.last_used, FLOW_ACTION_HW_STATS_IMMEDIATE); tc_port_matchall->ingress.packets = packets; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h index dbce99b209d6..a963fd0b4540 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h @@ -106,6 +106,8 @@ struct tid_info { unsigned long *stid_bmap; unsigned int nstids; unsigned int stid_base; + + unsigned int nhash; unsigned int hash_base; union aopen_entry *atid_tab; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 32a45dc51ed7..f9c6f13d7c99 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2432,7 +2432,7 @@ int cxgb4_ethofld_send_flowc(struct net_device *dev, u32 eotid, u32 tc) struct sk_buff *skb; int ret = 0; - len = sizeof(*flowc) + sizeof(struct fw_flowc_mnemval) * nparams; + len = struct_size(flowc, mnemval, nparams); len16 = DIV_ROUND_UP(len, 16); entry = cxgb4_lookup_eotid(&adap->tids, eotid); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 1aa6dc10dc0b..70fe189202be 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -3752,7 +3752,6 @@ int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver) * t4_load_phy_fw - download port PHY firmware * @adap: the adapter * @win: the PCI-E Memory Window index to use for t4_memory_rw() - * @win_lock: the lock to use to guard the memory copy * @phy_fw_version: function to check PHY firmware versions * @phy_fw_data: the PHY firmware image to write * @phy_fw_size: image size @@ -3761,9 +3760,7 @@ int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver) * @phy_fw_version is supplied, then it will be used to determine if * it's necessary to perform the transfer by comparing the version * of any existing adapter PHY firmware with that of the passed in - * PHY firmware image. If @win_lock is non-NULL then it will be used - * around the call to t4_memory_rw() which transfers the PHY firmware - * to the adapter. + * PHY firmware image. * * A negative error number will be returned if an error occurs. If * version number support is available and there's no need to upgrade @@ -3775,14 +3772,13 @@ int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver) * contents. Thus, loading PHY firmware on such adapters must happen * after any FW_RESET_CMDs ... */ -int t4_load_phy_fw(struct adapter *adap, - int win, spinlock_t *win_lock, +int t4_load_phy_fw(struct adapter *adap, int win, int (*phy_fw_version)(const u8 *, size_t), const u8 *phy_fw_data, size_t phy_fw_size) { + int cur_phy_fw_ver = 0, new_phy_fw_vers = 0; unsigned long mtype = 0, maddr = 0; u32 param, val; - int cur_phy_fw_ver = 0, new_phy_fw_vers = 0; int ret; /* If we have version number support, then check to see if the adapter @@ -3822,13 +3818,9 @@ int t4_load_phy_fw(struct adapter *adap, /* Copy the supplied PHY Firmware image to the adapter memory location * allocated by the adapter firmware. */ - if (win_lock) - spin_lock_bh(win_lock); ret = t4_memory_rw(adap, win, mtype, maddr, phy_fw_size, (__be32 *)phy_fw_data, T4_MEMORY_WRITE); - if (win_lock) - spin_unlock_bh(win_lock); if (ret) return ret; @@ -10481,3 +10473,280 @@ int t4_set_vlan_acl(struct adapter *adap, unsigned int mbox, unsigned int vf, return t4_wr_mbox(adap, adap->mbox, &vlan_cmd, sizeof(vlan_cmd), NULL); } + +/** + * modify_device_id - Modifies the device ID of the Boot BIOS image + * @device_id: the device ID to write. + * @boot_data: the boot image to modify. + * + * Write the supplied device ID to the boot BIOS image. + */ +static void modify_device_id(int device_id, u8 *boot_data) +{ + struct cxgb4_pcir_data *pcir_header; + struct legacy_pci_rom_hdr *header; + u8 *cur_header = boot_data; + u16 pcir_offset; + + /* Loop through all chained images and change the device ID's */ + do { + header = (struct legacy_pci_rom_hdr *)cur_header; + pcir_offset = le16_to_cpu(header->pcir_offset); + pcir_header = (struct cxgb4_pcir_data *)(cur_header + + pcir_offset); + + /** + * Only modify the Device ID if code type is Legacy or HP. + * 0x00: Okay to modify + * 0x01: FCODE. Do not modify + * 0x03: Okay to modify + * 0x04-0xFF: Do not modify + */ + if (pcir_header->code_type == CXGB4_HDR_CODE1) { + u8 csum = 0; + int i; + + /** + * Modify Device ID to match current adatper + */ + pcir_header->device_id = cpu_to_le16(device_id); + + /** + * Set checksum temporarily to 0. + * We will recalculate it later. + */ + header->cksum = 0x0; + + /** + * Calculate and update checksum + */ + for (i = 0; i < (header->size512 * 512); i++) + csum += cur_header[i]; + + /** + * Invert summed value to create the checksum + * Writing new checksum value directly to the boot data + */ + cur_header[7] = -csum; + + } else if (pcir_header->code_type == CXGB4_HDR_CODE2) { + /** + * Modify Device ID to match current adatper + */ + pcir_header->device_id = cpu_to_le16(device_id); + } + + /** + * Move header pointer up to the next image in the ROM. + */ + cur_header += header->size512 * 512; + } while (!(pcir_header->indicator & CXGB4_HDR_INDI)); +} + +/** + * t4_load_boot - download boot flash + * @adap: the adapter + * @boot_data: the boot image to write + * @boot_addr: offset in flash to write boot_data + * @size: image size + * + * Write the supplied boot image to the card's serial flash. + * The boot image has the following sections: a 28-byte header and the + * boot image. + */ +int t4_load_boot(struct adapter *adap, u8 *boot_data, + unsigned int boot_addr, unsigned int size) +{ + unsigned int sf_sec_size = adap->params.sf_size / adap->params.sf_nsec; + unsigned int boot_sector = (boot_addr * 1024); + struct cxgb4_pci_exp_rom_header *header; + struct cxgb4_pcir_data *pcir_header; + int pcir_offset; + unsigned int i; + u16 device_id; + int ret, addr; + + /** + * Make sure the boot image does not encroach on the firmware region + */ + if ((boot_sector + size) >> 16 > FLASH_FW_START_SEC) { + dev_err(adap->pdev_dev, "boot image encroaching on firmware region\n"); + return -EFBIG; + } + + /* Get boot header */ + header = (struct cxgb4_pci_exp_rom_header *)boot_data; + pcir_offset = le16_to_cpu(header->pcir_offset); + /* PCIR Data Structure */ + pcir_header = (struct cxgb4_pcir_data *)&boot_data[pcir_offset]; + + /** + * Perform some primitive sanity testing to avoid accidentally + * writing garbage over the boot sectors. We ought to check for + * more but it's not worth it for now ... + */ + if (size < BOOT_MIN_SIZE || size > BOOT_MAX_SIZE) { + dev_err(adap->pdev_dev, "boot image too small/large\n"); + return -EFBIG; + } + + if (le16_to_cpu(header->signature) != BOOT_SIGNATURE) { + dev_err(adap->pdev_dev, "Boot image missing signature\n"); + return -EINVAL; + } + + /* Check PCI header signature */ + if (le32_to_cpu(pcir_header->signature) != PCIR_SIGNATURE) { + dev_err(adap->pdev_dev, "PCI header missing signature\n"); + return -EINVAL; + } + + /* Check Vendor ID matches Chelsio ID*/ + if (le16_to_cpu(pcir_header->vendor_id) != PCI_VENDOR_ID_CHELSIO) { + dev_err(adap->pdev_dev, "Vendor ID missing signature\n"); + return -EINVAL; + } + + /** + * The boot sector is comprised of the Expansion-ROM boot, iSCSI boot, + * and Boot configuration data sections. These 3 boot sections span + * sectors 0 to 7 in flash and live right before the FW image location. + */ + i = DIV_ROUND_UP(size ? size : FLASH_FW_START, sf_sec_size); + ret = t4_flash_erase_sectors(adap, boot_sector >> 16, + (boot_sector >> 16) + i - 1); + + /** + * If size == 0 then we're simply erasing the FLASH sectors associated + * with the on-adapter option ROM file + */ + if (ret || size == 0) + goto out; + /* Retrieve adapter's device ID */ + pci_read_config_word(adap->pdev, PCI_DEVICE_ID, &device_id); + /* Want to deal with PF 0 so I strip off PF 4 indicator */ + device_id = device_id & 0xf0ff; + + /* Check PCIE Device ID */ + if (le16_to_cpu(pcir_header->device_id) != device_id) { + /** + * Change the device ID in the Boot BIOS image to match + * the Device ID of the current adapter. + */ + modify_device_id(device_id, boot_data); + } + + /** + * Skip over the first SF_PAGE_SIZE worth of data and write it after + * we finish copying the rest of the boot image. This will ensure + * that the BIOS boot header will only be written if the boot image + * was written in full. + */ + addr = boot_sector; + for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) { + addr += SF_PAGE_SIZE; + boot_data += SF_PAGE_SIZE; + ret = t4_write_flash(adap, addr, SF_PAGE_SIZE, boot_data); + if (ret) + goto out; + } + + ret = t4_write_flash(adap, boot_sector, SF_PAGE_SIZE, + (const u8 *)header); + +out: + if (ret) + dev_err(adap->pdev_dev, "boot image load failed, error %d\n", + ret); + return ret; +} + +/** + * t4_flash_bootcfg_addr - return the address of the flash + * optionrom configuration + * @adapter: the adapter + * + * Return the address within the flash where the OptionROM Configuration + * is stored, or an error if the device FLASH is too small to contain + * a OptionROM Configuration. + */ +static int t4_flash_bootcfg_addr(struct adapter *adapter) +{ + /** + * If the device FLASH isn't large enough to hold a Firmware + * Configuration File, return an error. + */ + if (adapter->params.sf_size < + FLASH_BOOTCFG_START + FLASH_BOOTCFG_MAX_SIZE) + return -ENOSPC; + + return FLASH_BOOTCFG_START; +} + +int t4_load_bootcfg(struct adapter *adap, const u8 *cfg_data, unsigned int size) +{ + unsigned int sf_sec_size = adap->params.sf_size / adap->params.sf_nsec; + struct cxgb4_bootcfg_data *header; + unsigned int flash_cfg_start_sec; + unsigned int addr, npad; + int ret, i, n, cfg_addr; + + cfg_addr = t4_flash_bootcfg_addr(adap); + if (cfg_addr < 0) + return cfg_addr; + + addr = cfg_addr; + flash_cfg_start_sec = addr / SF_SEC_SIZE; + + if (size > FLASH_BOOTCFG_MAX_SIZE) { + dev_err(adap->pdev_dev, "bootcfg file too large, max is %u bytes\n", + FLASH_BOOTCFG_MAX_SIZE); + return -EFBIG; + } + + header = (struct cxgb4_bootcfg_data *)cfg_data; + if (le16_to_cpu(header->signature) != BOOT_CFG_SIG) { + dev_err(adap->pdev_dev, "Wrong bootcfg signature\n"); + ret = -EINVAL; + goto out; + } + + i = DIV_ROUND_UP(FLASH_BOOTCFG_MAX_SIZE, + sf_sec_size); + ret = t4_flash_erase_sectors(adap, flash_cfg_start_sec, + flash_cfg_start_sec + i - 1); + + /** + * If size == 0 then we're simply erasing the FLASH sectors associated + * with the on-adapter OptionROM Configuration File. + */ + if (ret || size == 0) + goto out; + + /* this will write to the flash up to SF_PAGE_SIZE at a time */ + for (i = 0; i < size; i += SF_PAGE_SIZE) { + n = min_t(u32, size - i, SF_PAGE_SIZE); + + ret = t4_write_flash(adap, addr, n, cfg_data); + if (ret) + goto out; + + addr += SF_PAGE_SIZE; + cfg_data += SF_PAGE_SIZE; + } + + npad = ((size + 4 - 1) & ~3) - size; + for (i = 0; i < npad; i++) { + u8 data = 0; + + ret = t4_write_flash(adap, cfg_addr + size + i, 1, &data); + if (ret) + goto out; + } + +out: + if (ret) + dev_err(adap->pdev_dev, "boot config data %s failed %d\n", + (size == 0 ? "clear" : "download"), ret); + return ret; +} diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 4a9fcd6c226c..065c01c654ff 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -563,6 +563,12 @@ #define AIVEC_V(x) ((x) << AIVEC_S) #define PCIE_PF_CLI_A 0x44 + +#define PCIE_PF_EXPROM_OFST_A 0x4c +#define OFFSET_S 10 +#define OFFSET_M 0x3fffU +#define OFFSET_G(x) (((x) >> OFFSET_S) & OFFSET_M) + #define PCIE_INT_CAUSE_A 0x3004 #define UNXSPLCPLERR_S 29 @@ -3038,6 +3044,10 @@ #define HASHTIDSIZE_M 0x3fU #define HASHTIDSIZE_G(x) (((x) >> HASHTIDSIZE_S) & HASHTIDSIZE_M) +#define HASHTBLSIZE_S 3 +#define HASHTBLSIZE_M 0x1ffffU +#define HASHTBLSIZE_G(x) (((x) >> HASHTBLSIZE_S) & HASHTBLSIZE_M) + #define LE_DB_HASH_TID_BASE_A 0x19c30 #define LE_DB_HASH_TBL_BASE_ADDR_A 0x19c30 #define LE_DB_INT_CAUSE_A 0x19c3c diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index 592454f444ce..cb116b530f5e 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -2105,11 +2105,10 @@ static void de_remove_one(struct pci_dev *pdev) free_netdev(dev); } -#ifdef CONFIG_PM - -static int de_suspend (struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused de_suspend(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata (pdev); + struct pci_dev *pdev = to_pci_dev(dev_d); + struct net_device *dev = pci_get_drvdata(pdev); struct de_private *de = netdev_priv(dev); rtnl_lock(); @@ -2136,7 +2135,6 @@ static int de_suspend (struct pci_dev *pdev, pm_message_t state) de_clean_rings(de); de_adapter_sleep(de); - pci_disable_device(pdev); } else { netif_device_detach(dev); } @@ -2144,21 +2142,17 @@ static int de_suspend (struct pci_dev *pdev, pm_message_t state) return 0; } -static int de_resume (struct pci_dev *pdev) +static int __maybe_unused de_resume(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata (pdev); + struct pci_dev *pdev = to_pci_dev(dev_d); + struct net_device *dev = pci_get_drvdata(pdev); struct de_private *de = netdev_priv(dev); - int retval = 0; rtnl_lock(); if (netif_device_present(dev)) goto out; if (!netif_running(dev)) goto out_attach; - if ((retval = pci_enable_device(pdev))) { - netdev_err(dev, "pci_enable_device failed in resume\n"); - goto out; - } pci_set_master(pdev); de_init_rings(de); de_init_hw(de); @@ -2169,17 +2163,14 @@ out: return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(de_pm_ops, de_suspend, de_resume); static struct pci_driver de_driver = { .name = DRV_NAME, .id_table = de_pci_tbl, .probe = de_init_one, .remove = de_remove_one, -#ifdef CONFIG_PM - .suspend = de_suspend, - .resume = de_resume, -#endif + .driver.pm = &de_pm_ops, }; static int __init de_init (void) diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index c1884fc9ad32..c3b4abff48b5 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -2081,14 +2081,11 @@ static const struct pci_device_id dmfe_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); - -#ifdef CONFIG_PM -static int dmfe_suspend(struct pci_dev *pci_dev, pm_message_t state) +static int __maybe_unused dmfe_suspend(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata(pci_dev); + struct net_device *dev = dev_get_drvdata(dev_d); struct dmfe_board_info *db = netdev_priv(dev); void __iomem *ioaddr = db->ioaddr; - u32 tmp; /* Disable upper layer interface */ netif_device_detach(dev); @@ -2105,63 +2102,35 @@ static int dmfe_suspend(struct pci_dev *pci_dev, pm_message_t state) dmfe_free_rxbuffer(db); /* Enable WOL */ - pci_read_config_dword(pci_dev, 0x40, &tmp); - tmp &= ~(DMFE_WOL_LINKCHANGE|DMFE_WOL_MAGICPACKET); - - if (db->wol_mode & WAKE_PHY) - tmp |= DMFE_WOL_LINKCHANGE; - if (db->wol_mode & WAKE_MAGIC) - tmp |= DMFE_WOL_MAGICPACKET; - - pci_write_config_dword(pci_dev, 0x40, tmp); - - pci_enable_wake(pci_dev, PCI_D3hot, 1); - pci_enable_wake(pci_dev, PCI_D3cold, 1); - - /* Power down device*/ - pci_save_state(pci_dev); - pci_set_power_state(pci_dev, pci_choose_state (pci_dev, state)); + device_wakeup_enable(dev_d); return 0; } -static int dmfe_resume(struct pci_dev *pci_dev) +static int __maybe_unused dmfe_resume(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata(pci_dev); - u32 tmp; - - pci_set_power_state(pci_dev, PCI_D0); - pci_restore_state(pci_dev); + struct net_device *dev = dev_get_drvdata(dev_d); /* Re-initialize DM910X board */ dmfe_init_dm910x(dev); /* Disable WOL */ - pci_read_config_dword(pci_dev, 0x40, &tmp); - - tmp &= ~(DMFE_WOL_LINKCHANGE | DMFE_WOL_MAGICPACKET); - pci_write_config_dword(pci_dev, 0x40, tmp); - - pci_enable_wake(pci_dev, PCI_D3hot, 0); - pci_enable_wake(pci_dev, PCI_D3cold, 0); + device_wakeup_disable(dev_d); /* Restart upper layer interface */ netif_device_attach(dev); return 0; } -#else -#define dmfe_suspend NULL -#define dmfe_resume NULL -#endif + +static SIMPLE_DEV_PM_OPS(dmfe_pm_ops, dmfe_suspend, dmfe_resume); static struct pci_driver dmfe_driver = { .name = "dmfe", .id_table = dmfe_pci_tbl, .probe = dmfe_init_one, .remove = dmfe_remove_one, - .suspend = dmfe_suspend, - .resume = dmfe_resume + .driver.pm = &dmfe_pm_ops, }; MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 15efc294f513..9db23527275a 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1803,13 +1803,9 @@ static void tulip_set_wolopts (struct pci_dev *pdev, u32 wolopts) } } -#ifdef CONFIG_PM - - -static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused tulip_suspend(struct device *dev_d) { - pci_power_t pstate; - struct net_device *dev = pci_get_drvdata(pdev); + struct net_device *dev = dev_get_drvdata(dev_d); struct tulip_private *tp = netdev_priv(dev); if (!dev) @@ -1825,45 +1821,27 @@ static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) free_irq(tp->pdev->irq, dev); save_state: - pci_save_state(pdev); - pci_disable_device(pdev); - pstate = pci_choose_state(pdev, state); - if (state.event == PM_EVENT_SUSPEND && pstate != PCI_D0) { - int rc; - - tulip_set_wolopts(pdev, tp->wolinfo.wolopts); - rc = pci_enable_wake(pdev, pstate, tp->wolinfo.wolopts); - if (rc) - pr_err("pci_enable_wake failed (%d)\n", rc); - } - pci_set_power_state(pdev, pstate); + tulip_set_wolopts(to_pci_dev(dev_d), tp->wolinfo.wolopts); + device_set_wakeup_enable(dev_d, !!tp->wolinfo.wolopts); return 0; } - -static int tulip_resume(struct pci_dev *pdev) +static int __maybe_unused tulip_resume(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata(pdev); + struct pci_dev *pdev = to_pci_dev(dev_d); + struct net_device *dev = dev_get_drvdata(dev_d); struct tulip_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->base_addr; - int retval; unsigned int tmp; + int retval = 0; if (!dev) return -EINVAL; - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - if (!netif_running(dev)) return 0; - if ((retval = pci_enable_device(pdev))) { - pr_err("pci_enable_device failed in resume\n"); - return retval; - } - retval = request_irq(pdev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev); if (retval) { @@ -1872,8 +1850,7 @@ static int tulip_resume(struct pci_dev *pdev) } if (tp->flags & COMET_PM) { - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_enable_wake(pdev, PCI_D3cold, 0); + device_set_wakeup_enable(dev_d, 0); /* Clear the PMES flag */ tmp = ioread32(ioaddr + CSR20); @@ -1891,9 +1868,6 @@ static int tulip_resume(struct pci_dev *pdev) return 0; } -#endif /* CONFIG_PM */ - - static void tulip_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata (pdev); @@ -1937,15 +1911,14 @@ static void poll_tulip (struct net_device *dev) } #endif +static SIMPLE_DEV_PM_OPS(tulip_pm_ops, tulip_suspend, tulip_resume); + static struct pci_driver tulip_driver = { .name = DRV_NAME, .id_table = tulip_pci_tbl, .probe = tulip_init_one, .remove = tulip_remove_one, -#ifdef CONFIG_PM - .suspend = tulip_suspend, - .resume = tulip_resume, -#endif /* CONFIG_PM */ + .driver.pm = &tulip_pm_ops, }; diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index f726436b1985..f942399f0f32 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -1163,65 +1163,41 @@ static void uli526x_dynamic_reset(struct net_device *dev) netif_wake_queue(dev); } - -#ifdef CONFIG_PM - /* * Suspend the interface. */ -static int uli526x_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused uli526x_suspend(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata(pdev); - pci_power_t power_state; - int err; + struct net_device *dev = dev_get_drvdata(dev_d); ULI526X_DBUG(0, "uli526x_suspend", 0); - pci_save_state(pdev); - if (!netif_running(dev)) return 0; netif_device_detach(dev); uli526x_reset_prepare(dev); - power_state = pci_choose_state(pdev, state); - pci_enable_wake(pdev, power_state, 0); - err = pci_set_power_state(pdev, power_state); - if (err) { - netif_device_attach(dev); - /* Re-initialize ULI526X board */ - uli526x_init(dev); - /* Restart upper layer interface */ - netif_wake_queue(dev); - } + device_set_wakeup_enable(dev_d, 0); - return err; + return 0; } /* * Resume the interface. */ -static int uli526x_resume(struct pci_dev *pdev) +static int __maybe_unused uli526x_resume(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata(pdev); - int err; + struct net_device *dev = dev_get_drvdata(dev_d); ULI526X_DBUG(0, "uli526x_resume", 0); - pci_restore_state(pdev); if (!netif_running(dev)) return 0; - err = pci_set_power_state(pdev, PCI_D0); - if (err) { - netdev_warn(dev, "Could not put device into D0\n"); - return err; - } - netif_device_attach(dev); /* Re-initialize ULI526X board */ uli526x_init(dev); @@ -1231,14 +1207,6 @@ static int uli526x_resume(struct pci_dev *pdev) return 0; } -#else /* !CONFIG_PM */ - -#define uli526x_suspend NULL -#define uli526x_resume NULL - -#endif /* !CONFIG_PM */ - - /* * free all allocated rx buffer */ @@ -1761,14 +1729,14 @@ static const struct pci_device_id uli526x_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, uli526x_pci_tbl); +static SIMPLE_DEV_PM_OPS(uli526x_pm_ops, uli526x_suspend, uli526x_resume); static struct pci_driver uli526x_driver = { .name = "uli526x", .id_table = uli526x_pci_tbl, .probe = uli526x_init_one, .remove = uli526x_remove_one, - .suspend = uli526x_suspend, - .resume = uli526x_resume, + .driver.pm = &uli526x_pm_ops, }; MODULE_AUTHOR("Peer Chen, peer.chen@uli.com.tw"); diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index 4d5e4fa53023..5dcc66f60144 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -1530,8 +1530,6 @@ static void w840_remove1(struct pci_dev *pdev) } } -#ifdef CONFIG_PM - /* * suspend/resume synchronization: * - open, close, do_ioctl: @@ -1555,9 +1553,9 @@ static void w840_remove1(struct pci_dev *pdev) * Detach must occur under spin_unlock_irq(), interrupts from a detached * device would cause an irq storm. */ -static int w840_suspend (struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused w840_suspend(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata (pdev); + struct net_device *dev = dev_get_drvdata(dev_d); struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = np->base_addr; @@ -1590,21 +1588,15 @@ static int w840_suspend (struct pci_dev *pdev, pm_message_t state) return 0; } -static int w840_resume (struct pci_dev *pdev) +static int __maybe_unused w840_resume(struct device *dev_d) { - struct net_device *dev = pci_get_drvdata (pdev); + struct net_device *dev = dev_get_drvdata(dev_d); struct netdev_private *np = netdev_priv(dev); - int retval = 0; rtnl_lock(); if (netif_device_present(dev)) goto out; /* device not suspended */ if (netif_running(dev)) { - if ((retval = pci_enable_device(pdev))) { - dev_err(&dev->dev, - "pci_enable_device failed in resume\n"); - goto out; - } spin_lock_irq(&np->lock); iowrite32(1, np->base_addr+PCIBusCfg); ioread32(np->base_addr+PCIBusCfg); @@ -1622,19 +1614,17 @@ static int w840_resume (struct pci_dev *pdev) } out: rtnl_unlock(); - return retval; + return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(w840_pm_ops, w840_suspend, w840_resume); static struct pci_driver w840_driver = { .name = DRV_NAME, .id_table = w840_pci_tbl, .probe = w840_probe1, .remove = w840_remove1, -#ifdef CONFIG_PM - .suspend = w840_suspend, - .resume = w840_resume, -#endif + .driver.pm = &w840_pm_ops, }; static int __init w840_init(void) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c index c453a23045c1..2880ca02d7e7 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c @@ -90,6 +90,10 @@ static int dpaa2_dbg_fqs_show(struct seq_file *file, void *offset) if (err) fcnt = 0; + /* Skip FQs with no traffic */ + if (!fq->stats.frames && !fcnt) + continue; + seq_printf(file, "%5d%16d%16d%16s%16llu%16u\n", fq->fqid, fq->target_cpu, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h index 9801528db2a5..5fb5f14e01ec 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h @@ -10,7 +10,6 @@ #include <linux/skbuff.h> #include <linux/netdevice.h> -#include "dpaa2-eth.h" #include <linux/tracepoint.h> #define TR_FMT "[%s] fd: addr=0x%llx, len=%u, off=%u" diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index f150cd454fa4..712bbfdbe7d7 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -611,6 +611,10 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv, sg_init_table(scl, nr_frags + 1); num_sg = skb_to_sgvec(skb, scl, 0, skb->len); + if (unlikely(num_sg < 0)) { + err = -ENOMEM; + goto dma_map_sg_failed; + } num_dma_bufs = dma_map_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL); if (unlikely(!num_dma_bufs)) { err = -ENOMEM; @@ -1109,7 +1113,7 @@ static void drain_bufs(struct dpaa2_eth_priv *priv, int count) buf_array, count); if (ret < 0) { if (ret == -EBUSY && - retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) + retries++ < DPAA2_ETH_SWP_BUSY_RETRIES) continue; netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); return; diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c index 6b479ba66465..426100607854 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c @@ -1558,10 +1558,10 @@ int dpni_get_statistics(struct fsl_mc_io *mc_io, * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @token: Token of DPNI object * @cg_point: Congestion point - * @q_type: Queue type on which the taildrop is configured. + * @qtype: Queue type on which the taildrop is configured. * Only Rx queues are supported for now * @tc: Traffic class to apply this taildrop to - * @q_index: Index of the queue if the DPNI supports multiple queues for + * @index: Index of the queue if the DPNI supports multiple queues for * traffic distribution. Ignored if CONGESTION_POINT is not 0. * @taildrop: Taildrop structure * @@ -1602,10 +1602,10 @@ int dpni_set_taildrop(struct fsl_mc_io *mc_io, * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @token: Token of DPNI object * @cg_point: Congestion point - * @q_type: Queue type on which the taildrop is configured. + * @qtype: Queue type on which the taildrop is configured. * Only Rx queues are supported for now * @tc: Traffic class to apply this taildrop to - * @q_index: Index of the queue if the DPNI supports multiple queues for + * @index: Index of the queue if the DPNI supports multiple queues for * traffic distribution. Ignored if CONGESTION_POINT is not 0. * @taildrop: Taildrop structure * diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index 96831f49925c..ad261e67f5a8 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1713,7 +1713,7 @@ int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) int enetc_alloc_msix(struct enetc_ndev_priv *priv) { struct pci_dev *pdev = priv->si->pdev; - int size, v_tx_rings; + int v_tx_rings; int i, n, err, nvec; nvec = ENETC_BDR_INT_BASE_IDX + priv->bdr_int_num; @@ -1728,15 +1728,13 @@ int enetc_alloc_msix(struct enetc_ndev_priv *priv) /* # of tx rings per int vector */ v_tx_rings = priv->num_tx_rings / priv->bdr_int_num; - size = sizeof(struct enetc_int_vector) + - sizeof(struct enetc_bdr) * v_tx_rings; for (i = 0; i < priv->bdr_int_num; i++) { struct enetc_int_vector *v; struct enetc_bdr *bdr; int j; - v = kzalloc(size, GFP_KERNEL); + v = kzalloc(struct_size(v, tx_ring, v_tx_rings), GFP_KERNEL); if (!v) { err = -ENOMEM; goto fail; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index ce0d321c0639..fc357bc56835 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -570,6 +570,7 @@ enum bdcr_cmd_class { BDCR_CMD_STREAM_IDENTIFY, BDCR_CMD_STREAM_FILTER, BDCR_CMD_STREAM_GCL, + BDCR_CMD_FLOW_METER, __BDCR_CMD_MAX_LEN, BDCR_CMD_MAX_LEN = __BDCR_CMD_MAX_LEN - 1, }; @@ -736,10 +737,33 @@ struct sgcl_data { struct sgce sgcl[0]; }; +#define ENETC_CBDR_FMI_MR BIT(0) +#define ENETC_CBDR_FMI_MREN BIT(1) +#define ENETC_CBDR_FMI_DOY BIT(2) +#define ENETC_CBDR_FMI_CM BIT(3) +#define ENETC_CBDR_FMI_CF BIT(4) +#define ENETC_CBDR_FMI_NDOR BIT(5) +#define ENETC_CBDR_FMI_OALEN BIT(6) +#define ENETC_CBDR_FMI_IRFPP_MASK GENMASK(4, 0) + +/* class 10: command 0/1, Flow Meter Instance Set, short Format */ +struct fmi_conf { + __le32 cir; + __le32 cbs; + __le32 eir; + __le32 ebs; + u8 conf; + u8 res1; + u8 ir_fpp; + u8 res2[4]; + u8 en; +}; + struct enetc_cbd { union{ struct sfi_conf sfi_conf; struct sgi_table sgi_table; + struct fmi_conf fmi_conf; struct { __le32 addr[2]; union { diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c index fd3df19eaa32..4f670cbdf186 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c @@ -389,6 +389,7 @@ struct enetc_psfp_filter { u32 index; s32 handle; s8 prio; + u32 maxsdu; u32 gate_id; s32 meter_id; refcount_t refcount; @@ -407,10 +408,26 @@ struct enetc_psfp_gate { struct action_gate_entry entries[0]; }; +/* Only enable the green color frame now + * Will add eir and ebs color blind, couple flag etc when + * policing action add more offloading parameters + */ +struct enetc_psfp_meter { + u32 index; + u32 cir; + u32 cbs; + refcount_t refcount; + struct hlist_node node; +}; + +#define ENETC_PSFP_FLAGS_FMI BIT(0) + struct enetc_stream_filter { struct enetc_streamid sid; u32 sfi_index; u32 sgi_index; + u32 flags; + u32 fmi_index; struct flow_stats stats; struct hlist_node node; }; @@ -421,6 +438,7 @@ struct enetc_psfp { struct hlist_head stream_list; struct hlist_head psfp_filter_list; struct hlist_head psfp_gate_list; + struct hlist_head psfp_meter_list; spinlock_t psfp_lock; /* spinlock for the struct enetc_psfp r/w */ }; @@ -430,6 +448,12 @@ static struct actions_fwd enetc_act_fwd[] = { BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS), FILTER_ACTION_TYPE_PSFP }, + { + BIT(FLOW_ACTION_POLICE) | + BIT(FLOW_ACTION_GATE), + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS), + FILTER_ACTION_TYPE_PSFP + }, /* example for ACL actions */ { BIT(FLOW_ACTION_DROP), @@ -594,8 +618,12 @@ static int enetc_streamfilter_hw_set(struct enetc_ndev_priv *priv, /* Filter Type. Identifies the contents of the MSDU/FM_INST_INDEX * field as being either an MSDU value or an index into the Flow * Meter Instance table. - * TODO: no limit max sdu */ + if (sfi->maxsdu) { + sfi_config->msdu = + cpu_to_le16(sfi->maxsdu); + sfi_config->multi |= 0x40; + } if (sfi->meter_id >= 0) { sfi_config->fm_inst_table_index = cpu_to_le16(sfi->meter_id); @@ -831,6 +859,47 @@ exit: return err; } +static int enetc_flowmeter_hw_set(struct enetc_ndev_priv *priv, + struct enetc_psfp_meter *fmi, + u8 enable) +{ + struct enetc_cbd cbd = { .cmd = 0 }; + struct fmi_conf *fmi_config; + u64 temp = 0; + + cbd.index = cpu_to_le16((u16)fmi->index); + cbd.cls = BDCR_CMD_FLOW_METER; + cbd.status_flags = 0x80; + + if (!enable) + return enetc_send_cmd(priv->si, &cbd); + + fmi_config = &cbd.fmi_conf; + fmi_config->en = 0x80; + + if (fmi->cir) { + temp = (u64)8000 * fmi->cir; + temp = div_u64(temp, 3725); + } + + fmi_config->cir = cpu_to_le32((u32)temp); + fmi_config->cbs = cpu_to_le32(fmi->cbs); + + /* Default for eir ebs disable */ + fmi_config->eir = 0; + fmi_config->ebs = 0; + + /* Default: + * mark red disable + * drop on yellow disable + * color mode disable + * couple flag disable + */ + fmi_config->conf = 0; + + return enetc_send_cmd(priv->si, &cbd); +} + static struct enetc_stream_filter *enetc_get_stream_by_index(u32 index) { struct enetc_stream_filter *f; @@ -864,6 +933,17 @@ static struct enetc_psfp_filter *enetc_get_filter_by_index(u32 index) return NULL; } +static struct enetc_psfp_meter *enetc_get_meter_by_index(u32 index) +{ + struct enetc_psfp_meter *m; + + hlist_for_each_entry(m, &epsfp.psfp_meter_list, node) + if (m->index == index) + return m; + + return NULL; +} + static struct enetc_psfp_filter *enetc_psfp_check_sfi(struct enetc_psfp_filter *sfi) { @@ -872,6 +952,7 @@ static struct enetc_psfp_filter hlist_for_each_entry(s, &epsfp.psfp_filter_list, node) if (s->gate_id == sfi->gate_id && s->prio == sfi->prio && + s->maxsdu == sfi->maxsdu && s->meter_id == sfi->meter_id) return s; @@ -922,9 +1003,27 @@ static void stream_gate_unref(struct enetc_ndev_priv *priv, u32 index) } } +static void flow_meter_unref(struct enetc_ndev_priv *priv, u32 index) +{ + struct enetc_psfp_meter *fmi; + u8 z; + + fmi = enetc_get_meter_by_index(index); + WARN_ON(!fmi); + z = refcount_dec_and_test(&fmi->refcount); + if (z) { + enetc_flowmeter_hw_set(priv, fmi, false); + hlist_del(&fmi->node); + kfree(fmi); + } +} + static void remove_one_chain(struct enetc_ndev_priv *priv, struct enetc_stream_filter *filter) { + if (filter->flags & ENETC_PSFP_FLAGS_FMI) + flow_meter_unref(priv, filter->fmi_index); + stream_gate_unref(priv, filter->sgi_index); stream_filter_unref(priv, filter->sfi_index); @@ -935,7 +1034,8 @@ static void remove_one_chain(struct enetc_ndev_priv *priv, static int enetc_psfp_hw_set(struct enetc_ndev_priv *priv, struct enetc_streamid *sid, struct enetc_psfp_filter *sfi, - struct enetc_psfp_gate *sgi) + struct enetc_psfp_gate *sgi, + struct enetc_psfp_meter *fmi) { int err; @@ -953,8 +1053,16 @@ static int enetc_psfp_hw_set(struct enetc_ndev_priv *priv, if (err) goto revert_sfi; + if (fmi) { + err = enetc_flowmeter_hw_set(priv, fmi, true); + if (err) + goto revert_sgi; + } + return 0; +revert_sgi: + enetc_streamgate_hw_set(priv, sgi, false); revert_sfi: if (sfi) enetc_streamfilter_hw_set(priv, sfi, false); @@ -979,9 +1087,11 @@ static struct actions_fwd *enetc_check_flow_actions(u64 acts, static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, struct flow_cls_offload *f) { + struct flow_action_entry *entryg = NULL, *entryp = NULL; struct flow_rule *rule = flow_cls_offload_flow_rule(f); struct netlink_ext_ack *extack = f->common.extack; struct enetc_stream_filter *filter, *old_filter; + struct enetc_psfp_meter *fmi = NULL, *old_fmi; struct enetc_psfp_filter *sfi, *old_sfi; struct enetc_psfp_gate *sgi, *old_sgi; struct flow_action_entry *entry; @@ -997,9 +1107,12 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, flow_action_for_each(i, entry, &rule->action) if (entry->id == FLOW_ACTION_GATE) - break; + entryg = entry; + else if (entry->id == FLOW_ACTION_POLICE) + entryp = entry; - if (entry->id != FLOW_ACTION_GATE) + /* Not support without gate action */ + if (!entryg) return -EINVAL; filter = kzalloc(sizeof(*filter), GFP_KERNEL); @@ -1079,19 +1192,19 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, } /* parsing gate action */ - if (entry->gate.index >= priv->psfp_cap.max_psfp_gate) { + if (entryg->gate.index >= priv->psfp_cap.max_psfp_gate) { NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!"); err = -ENOSPC; goto free_filter; } - if (entry->gate.num_entries >= priv->psfp_cap.max_psfp_gatelist) { + if (entryg->gate.num_entries >= priv->psfp_cap.max_psfp_gatelist) { NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!"); err = -ENOSPC; goto free_filter; } - entries_size = struct_size(sgi, entries, entry->gate.num_entries); + entries_size = struct_size(sgi, entries, entryg->gate.num_entries); sgi = kzalloc(entries_size, GFP_KERNEL); if (!sgi) { err = -ENOMEM; @@ -1099,18 +1212,18 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, } refcount_set(&sgi->refcount, 1); - sgi->index = entry->gate.index; - sgi->init_ipv = entry->gate.prio; - sgi->basetime = entry->gate.basetime; - sgi->cycletime = entry->gate.cycletime; - sgi->num_entries = entry->gate.num_entries; + sgi->index = entryg->gate.index; + sgi->init_ipv = entryg->gate.prio; + sgi->basetime = entryg->gate.basetime; + sgi->cycletime = entryg->gate.cycletime; + sgi->num_entries = entryg->gate.num_entries; e = sgi->entries; - for (i = 0; i < entry->gate.num_entries; i++) { - e[i].gate_state = entry->gate.entries[i].gate_state; - e[i].interval = entry->gate.entries[i].interval; - e[i].ipv = entry->gate.entries[i].ipv; - e[i].maxoctets = entry->gate.entries[i].maxoctets; + for (i = 0; i < entryg->gate.num_entries; i++) { + e[i].gate_state = entryg->gate.entries[i].gate_state; + e[i].interval = entryg->gate.entries[i].interval; + e[i].ipv = entryg->gate.entries[i].ipv; + e[i].maxoctets = entryg->gate.entries[i].maxoctets; } filter->sgi_index = sgi->index; @@ -1123,10 +1236,35 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, refcount_set(&sfi->refcount, 1); sfi->gate_id = sgi->index; - - /* flow meter not support yet */ sfi->meter_id = ENETC_PSFP_WILDCARD; + /* Flow meter and max frame size */ + if (entryp) { + if (entryp->police.burst) { + u64 temp; + + fmi = kzalloc(sizeof(*fmi), GFP_KERNEL); + if (!fmi) { + err = -ENOMEM; + goto free_sfi; + } + refcount_set(&fmi->refcount, 1); + fmi->cir = entryp->police.rate_bytes_ps; + /* Convert to original burst value */ + temp = entryp->police.burst * fmi->cir; + temp = div_u64(temp, 1000000000ULL); + + fmi->cbs = temp; + fmi->index = entryp->police.index; + filter->flags |= ENETC_PSFP_FLAGS_FMI; + filter->fmi_index = fmi->index; + sfi->meter_id = fmi->index; + } + + if (entryp->police.mtu) + sfi->maxsdu = entryp->police.mtu; + } + /* prio ref the filter prio */ if (f->common.prio && f->common.prio <= BIT(3)) sfi->prio = f->common.prio - 1; @@ -1141,7 +1279,7 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, if (sfi->handle < 0) { NL_SET_ERR_MSG_MOD(extack, "No Stream Filter resource!"); err = -ENOSPC; - goto free_sfi; + goto free_fmi; } sfi->index = index; @@ -1157,11 +1295,23 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, } err = enetc_psfp_hw_set(priv, &filter->sid, - sfi_overwrite ? NULL : sfi, sgi); + sfi_overwrite ? NULL : sfi, sgi, fmi); if (err) - goto free_sfi; + goto free_fmi; spin_lock(&epsfp.psfp_lock); + if (filter->flags & ENETC_PSFP_FLAGS_FMI) { + old_fmi = enetc_get_meter_by_index(filter->fmi_index); + if (old_fmi) { + fmi->refcount = old_fmi->refcount; + refcount_set(&fmi->refcount, + refcount_read(&old_fmi->refcount) + 1); + hlist_del(&old_fmi->node); + kfree(old_fmi); + } + hlist_add_head(&fmi->node, &epsfp.psfp_meter_list); + } + /* Remove the old node if exist and update with a new node */ old_sgi = enetc_get_gate_by_index(filter->sgi_index); if (old_sgi) { @@ -1192,6 +1342,8 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, return 0; +free_fmi: + kfree(fmi); free_sfi: kfree(sfi); free_gate: @@ -1290,13 +1442,20 @@ static int enetc_psfp_get_stats(struct enetc_ndev_priv *priv, return -EINVAL; spin_lock(&epsfp.psfp_lock); - stats.pkts = counters.matching_frames_count - filter->stats.pkts; + stats.pkts = counters.matching_frames_count + + counters.not_passing_sdu_count - + filter->stats.pkts; + stats.drops = counters.not_passing_frames_count + + counters.not_passing_sdu_count + + counters.red_frames_count - + filter->stats.drops; stats.lastused = filter->stats.lastused; filter->stats.pkts += stats.pkts; + filter->stats.drops += stats.drops; spin_unlock(&epsfp.psfp_lock); - flow_stats_update(&f->stats, 0x0, stats.pkts, stats.lastused, - FLOW_ACTION_HW_STATS_DELAYED); + flow_stats_update(&f->stats, 0x0, stats.pkts, stats.drops, + stats.lastused, FLOW_ACTION_HW_STATS_DELAYED); return 0; } diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 2d0d313ee7c5..9f80a33c5b16 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -710,8 +710,7 @@ static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); - int total_len, data_left; + int hdr_len, total_len, data_left; struct bufdesc *bdp = txq->bd.cur; struct tso_t tso; unsigned int index = 0; @@ -731,7 +730,7 @@ static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, } /* Initialize the TSO handler, and prepare the first payload */ - tso_start(skb, &tso); + hdr_len = tso_start(skb, &tso); total_len = skb->len - hdr_len; while (total_len > 0) { diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index c82c85ef5fb3..98be51d8b08c 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -245,14 +245,19 @@ static int xgmac_mdio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mii_bus *bus; - struct resource res; + struct resource *res; struct mdio_fsl_priv *priv; int ret; - ret = of_address_to_resource(np, 0, &res); - if (ret) { + /* In DPAA-1, MDIO is one of the many FMan sub-devices. The FMan + * defines a register space that spans a large area, covering all the + * subdevice areas. Therefore, MDIO cannot claim exclusive access to + * this register area. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { dev_err(&pdev->dev, "could not obtain address\n"); - return ret; + return -EINVAL; } bus = mdiobus_alloc_size(sizeof(struct mdio_fsl_priv)); @@ -263,21 +268,22 @@ static int xgmac_mdio_probe(struct platform_device *pdev) bus->read = xgmac_mdio_read; bus->write = xgmac_mdio_write; bus->parent = &pdev->dev; - snprintf(bus->id, MII_BUS_ID_SIZE, "%llx", (unsigned long long)res.start); + bus->probe_capabilities = MDIOBUS_C22_C45; + snprintf(bus->id, MII_BUS_ID_SIZE, "%pa", &res->start); /* Set the PHY base address */ priv = bus->priv; - priv->mdio_base = of_iomap(np, 0); + priv->mdio_base = ioremap(res->start, resource_size(res)); if (!priv->mdio_base) { ret = -ENOMEM; goto err_ioremap; } - priv->is_little_endian = of_property_read_bool(pdev->dev.of_node, - "little-endian"); + priv->is_little_endian = device_property_read_bool(&pdev->dev, + "little-endian"); - priv->has_a011043 = of_property_read_bool(pdev->dev.of_node, - "fsl,erratum-a011043"); + priv->has_a011043 = device_property_read_bool(&pdev->dev, + "fsl,erratum-a011043"); ret = of_mdiobus_register(bus, np); if (ret) { @@ -320,10 +326,17 @@ static const struct of_device_id xgmac_mdio_match[] = { }; MODULE_DEVICE_TABLE(of, xgmac_mdio_match); +static const struct acpi_device_id xgmac_acpi_match[] = { + { "NXP0006" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgmac_acpi_match); + static struct platform_driver xgmac_mdio_driver = { .driver = { .name = "fsl-fman_xmdio", .of_match_table = xgmac_mdio_match, + .acpi_match_table = xgmac_acpi_match, }, .probe = xgmac_mdio_probe, .remove = xgmac_mdio_remove, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index b14f2abc2425..b319a766889f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -8,6 +8,7 @@ #include <linux/cpu_rmap.h> #endif #include <linux/if_vlan.h> +#include <linux/irq.h> #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/module.h> @@ -154,6 +155,7 @@ static int hns3_nic_init_irq(struct hns3_nic_priv *priv) tqp_vectors->name[HNAE3_INT_NAME_LEN - 1] = '\0'; + irq_set_status_flags(tqp_vectors->vector_irq, IRQ_NOAUTOEN); ret = request_irq(tqp_vectors->vector_irq, hns3_irq_handle, 0, tqp_vectors->name, tqp_vectors); if (ret) { @@ -163,8 +165,6 @@ static int hns3_nic_init_irq(struct hns3_nic_priv *priv) return ret; } - disable_irq(tqp_vectors->vector_irq); - irq_set_affinity_hint(tqp_vectors->vector_irq, &tqp_vectors->affinity_mask); @@ -2097,10 +2097,8 @@ static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, ae_dev); ret = hnae3_register_ae_dev(ae_dev); - if (ret) { - devm_kfree(&pdev->dev, ae_dev); + if (ret) pci_set_drvdata(pdev, NULL); - } return ret; } @@ -2157,7 +2155,6 @@ static void hns3_shutdown(struct pci_dev *pdev) struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); hnae3_unregister_ae_dev(ae_dev); - devm_kfree(&pdev->dev, ae_dev); pci_set_drvdata(pdev, NULL); if (system_state == SYSTEM_POWER_OFF) @@ -2408,7 +2405,7 @@ static int hns3_alloc_desc(struct hns3_enet_ring *ring) return 0; } -static int hns3_reserve_buffer_map(struct hns3_enet_ring *ring, +static int hns3_alloc_and_map_buffer(struct hns3_enet_ring *ring, struct hns3_desc_cb *cb) { int ret; @@ -2429,9 +2426,9 @@ out: return ret; } -static int hns3_alloc_buffer_attach(struct hns3_enet_ring *ring, int i) +static int hns3_alloc_and_attach_buffer(struct hns3_enet_ring *ring, int i) { - int ret = hns3_reserve_buffer_map(ring, &ring->desc_cb[i]); + int ret = hns3_alloc_and_map_buffer(ring, &ring->desc_cb[i]); if (ret) return ret; @@ -2447,7 +2444,7 @@ static int hns3_alloc_ring_buffers(struct hns3_enet_ring *ring) int i, j, ret; for (i = 0; i < ring->desc_num; i++) { - ret = hns3_alloc_buffer_attach(ring, i); + ret = hns3_alloc_and_attach_buffer(ring, i); if (ret) goto out_buffer_fail; } @@ -2476,6 +2473,11 @@ static void hns3_reuse_buffer(struct hns3_enet_ring *ring, int i) ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma + ring->desc_cb[i].page_offset); ring->desc[i].rx.bd_base_info = 0; + + dma_sync_single_for_device(ring_to_dev(ring), + ring->desc_cb[i].dma + ring->desc_cb[i].page_offset, + hns3_buf_size(ring), + DMA_FROM_DEVICE); } static void hns3_nic_reclaim_desc(struct hns3_enet_ring *ring, int head, @@ -2593,7 +2595,7 @@ static void hns3_nic_alloc_rx_buffers(struct hns3_enet_ring *ring, hns3_reuse_buffer(ring, ring->next_to_use); } else { - ret = hns3_reserve_buffer_map(ring, &res_cbs); + ret = hns3_alloc_and_map_buffer(ring, &res_cbs); if (ret) { u64_stats_update_begin(&ring->syncp); ring->stats.sw_err_cnt++; @@ -2921,6 +2923,11 @@ static int hns3_add_frag(struct hns3_enet_ring *ring) skb = ring->tail_skb; } + dma_sync_single_for_cpu(ring_to_dev(ring), + desc_cb->dma + desc_cb->page_offset, + hns3_buf_size(ring), + DMA_FROM_DEVICE); + hns3_nic_reuse_page(skb, ring->frag_num++, ring, 0, desc_cb); trace_hns3_rx_desc(ring); ring_ptr_move_fw(ring, next_to_clean); @@ -3072,8 +3079,14 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring) if (unlikely(!(bd_base_info & BIT(HNS3_RXD_VLD_B)))) return -ENXIO; - if (!skb) - ring->va = (unsigned char *)desc_cb->buf + desc_cb->page_offset; + if (!skb) { + ring->va = desc_cb->buf + desc_cb->page_offset; + + dma_sync_single_for_cpu(ring_to_dev(ring), + desc_cb->dma + desc_cb->page_offset, + hns3_buf_size(ring), + DMA_FROM_DEVICE); + } /* Prefetch first cache line of first page * Idea is to cache few bytes of the header of the packet. Our L1 Cache @@ -4187,7 +4200,7 @@ static int hns3_clear_rx_ring(struct hns3_enet_ring *ring) * stack, so we need to replace the buffer here. */ if (!ring->desc_cb[ring->next_to_use].reuse_flag) { - ret = hns3_reserve_buffer_map(ring, &res_cbs); + ret = hns3_alloc_and_map_buffer(ring, &res_cbs); if (ret) { u64_stats_update_begin(&ring->syncp); ring->stats.sw_err_cnt++; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index 66cd4395f781..9f64077ee834 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -407,7 +407,7 @@ struct hns3_enet_ring { u32 pull_len; /* head length for current packet */ u32 frag_num; - unsigned char *va; /* first buffer address for current packet */ + void *va; /* first buffer address for current packet */ u32 flag; /* ring attribute */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c index 113f6087c7c9..6516980965a2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c @@ -427,7 +427,7 @@ static struct xfrm_state *ixgbe_ipsec_find_rx_state(struct ixgbe_ipsec *ipsec, static int ixgbe_ipsec_parse_proto_keys(struct xfrm_state *xs, u32 *mykey, u32 *mysalt) { - struct net_device *dev = xs->xso.dev; + struct net_device *dev = xs->xso.real_dev; unsigned char *key_data; char *alg_name = NULL; int key_len; @@ -477,7 +477,7 @@ static int ixgbe_ipsec_parse_proto_keys(struct xfrm_state *xs, **/ static int ixgbe_ipsec_check_mgmt_ip(struct xfrm_state *xs) { - struct net_device *dev = xs->xso.dev; + struct net_device *dev = xs->xso.real_dev; struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_hw *hw = &adapter->hw; u32 mfval, manc, reg; @@ -560,7 +560,7 @@ static int ixgbe_ipsec_check_mgmt_ip(struct xfrm_state *xs) **/ static int ixgbe_ipsec_add_sa(struct xfrm_state *xs) { - struct net_device *dev = xs->xso.dev; + struct net_device *dev = xs->xso.real_dev; struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_ipsec *ipsec = adapter->ipsec; struct ixgbe_hw *hw = &adapter->hw; @@ -745,7 +745,7 @@ static int ixgbe_ipsec_add_sa(struct xfrm_state *xs) **/ static void ixgbe_ipsec_del_sa(struct xfrm_state *xs) { - struct net_device *dev = xs->xso.dev; + struct net_device *dev = xs->xso.real_dev; struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_ipsec *ipsec = adapter->ipsec; struct ixgbe_hw *hw = &adapter->hw; diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 4d4b6243318a..90e6111ce534 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -816,10 +816,9 @@ static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb, struct net_device *dev) { struct mv643xx_eth_private *mp = txq_to_mp(txq); - int total_len, data_left, ret; + int hdr_len, total_len, data_left, ret; int desc_count = 0; struct tso_t tso; - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); struct tx_desc *first_tx_desc; u32 first_cmd_sts = 0; @@ -832,7 +831,7 @@ static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb, first_tx_desc = &txq->tx_desc_area[txq->tx_curr_desc]; /* Initialize the TSO handler, and prepare the first payload */ - tso_start(skb, &tso); + hdr_len = tso_start(skb, &tso); total_len = skb->len - hdr_len; while (total_len > 0) { diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index c639e3a29302..197d8c6d189d 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2606,11 +2606,10 @@ mvneta_tso_put_data(struct net_device *dev, struct mvneta_tx_queue *txq, static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev, struct mvneta_tx_queue *txq) { - int total_len, data_left; + int hdr_len, total_len, data_left; int desc_count = 0; struct mvneta_port *pp = netdev_priv(dev); struct tso_t tso; - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); int i; /* Count needed descriptors */ @@ -2623,7 +2622,7 @@ static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev, } /* Initialize the TSO handler, and prepare the first payload */ - tso_start(skb, &tso); + hdr_len = tso_start(skb, &tso); total_len = skb->len - hdr_len; while (total_len > 0) { diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 24f4d8e0da98..212fc3b54310 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -1114,6 +1114,17 @@ mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask) } } +/* Only GOP port 0 has an XLG MAC */ +static bool mvpp2_port_supports_xlg(struct mvpp2_port *port) +{ + return port->gop_id == 0; +} + +static bool mvpp2_port_supports_rgmii(struct mvpp2_port *port) +{ + return !(port->priv->hw_version == MVPP22 && port->gop_id == 0); +} + /* Port configuration routines */ static bool mvpp2_is_xlg(phy_interface_t interface) { @@ -1121,6 +1132,17 @@ static bool mvpp2_is_xlg(phy_interface_t interface) interface == PHY_INTERFACE_MODE_XAUI; } +static void mvpp2_modify(void __iomem *ptr, u32 mask, u32 set) +{ + u32 old, val; + + old = val = readl(ptr); + val &= ~mask; + val |= set; + if (old != val) + writel(val, ptr); +} + static void mvpp22_gop_init_rgmii(struct mvpp2_port *port) { struct mvpp2 *priv = port->priv; @@ -1194,7 +1216,7 @@ static int mvpp22_gop_init(struct mvpp2_port *port) case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - if (port->gop_id == 0) + if (!mvpp2_port_supports_rgmii(port)) goto invalid_conf; mvpp22_gop_init_rgmii(port); break; @@ -1204,7 +1226,7 @@ static int mvpp22_gop_init(struct mvpp2_port *port) mvpp22_gop_init_sgmii(port); break; case PHY_INTERFACE_MODE_10GBASER: - if (port->gop_id != 0) + if (!mvpp2_port_supports_xlg(port)) goto invalid_conf; mvpp22_gop_init_10gkr(port); break; @@ -1246,7 +1268,7 @@ static void mvpp22_gop_unmask_irq(struct mvpp2_port *port) writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK); } - if (port->gop_id == 0) { + if (mvpp2_port_supports_xlg(port)) { /* Enable the XLG/GIG irqs for this port */ val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); if (mvpp2_is_xlg(port->phy_interface)) @@ -1261,7 +1283,7 @@ static void mvpp22_gop_mask_irq(struct mvpp2_port *port) { u32 val; - if (port->gop_id == 0) { + if (mvpp2_port_supports_xlg(port)) { val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); val &= ~(MVPP22_XLG_EXT_INT_MASK_XLG | MVPP22_XLG_EXT_INT_MASK_GIG); @@ -1290,7 +1312,7 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port) writel(val, port->base + MVPP22_GMAC_INT_MASK); } - if (port->gop_id == 0) { + if (mvpp2_port_supports_xlg(port)) { val = readl(port->base + MVPP22_XLG_INT_MASK); val |= MVPP22_XLG_INT_MASK_LINK; writel(val, port->base + MVPP22_XLG_INT_MASK); @@ -1328,8 +1350,8 @@ static void mvpp2_port_enable(struct mvpp2_port *port) { u32 val; - /* Only GOP port 0 has an XLG MAC */ - if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface)) { + if (mvpp2_port_supports_xlg(port) && + mvpp2_is_xlg(port->phy_interface)) { val = readl(port->base + MVPP22_XLG_CTRL0_REG); val |= MVPP22_XLG_CTRL0_PORT_EN; val &= ~MVPP22_XLG_CTRL0_MIB_CNT_DIS; @@ -1346,8 +1368,8 @@ static void mvpp2_port_disable(struct mvpp2_port *port) { u32 val; - /* Only GOP port 0 has an XLG MAC */ - if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface)) { + if (mvpp2_port_supports_xlg(port) && + mvpp2_is_xlg(port->phy_interface)) { val = readl(port->base + MVPP22_XLG_CTRL0_REG); val &= ~MVPP22_XLG_CTRL0_PORT_EN; writel(val, port->base + MVPP22_XLG_CTRL0_REG); @@ -2740,7 +2762,8 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id) mvpp22_gop_mask_irq(port); - if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface)) { + if (mvpp2_port_supports_xlg(port) && + mvpp2_is_xlg(port->phy_interface)) { val = readl(port->base + MVPP22_XLG_INT_STAT); if (val & MVPP22_XLG_INT_STAT_LINK) { event = true; @@ -3160,9 +3183,8 @@ static int mvpp2_tx_tso(struct sk_buff *skb, struct net_device *dev, struct mvpp2_txq_pcpu *txq_pcpu) { struct mvpp2_port *port = netdev_priv(dev); + int hdr_sz, i, len, descs = 0; struct tso_t tso; - int hdr_sz = skb_transport_offset(skb) + tcp_hdrlen(skb); - int i, len, descs = 0; /* Check number of available descriptors */ if (mvpp2_aggr_desc_num_check(port, aggr_txq, tso_count_descs(skb)) || @@ -3170,7 +3192,8 @@ static int mvpp2_tx_tso(struct sk_buff *skb, struct net_device *dev, tso_count_descs(skb))) return 0; - tso_start(skb, &tso); + hdr_sz = tso_start(skb, &tso); + len = skb->len - hdr_sz; while (len > 0) { int left = min_t(int, skb_shinfo(skb)->gso_size, len); @@ -3430,8 +3453,7 @@ static void mvpp22_mode_reconfigure(struct mvpp2_port *port) mvpp22_pcs_reset_deassert(port); - /* Only GOP port 0 has an XLG MAC */ - if (port->gop_id == 0) { + if (mvpp2_port_supports_xlg(port)) { ctrl3 = readl(port->base + MVPP22_XLG_CTRL3_REG); ctrl3 &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK; @@ -3443,7 +3465,7 @@ static void mvpp22_mode_reconfigure(struct mvpp2_port *port) writel(ctrl3, port->base + MVPP22_XLG_CTRL3_REG); } - if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface)) + if (mvpp2_port_supports_xlg(port) && mvpp2_is_xlg(port->phy_interface)) mvpp2_xlg_max_rx_size_set(port); else mvpp2_gmac_max_rx_size_set(port); @@ -4756,26 +4778,30 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, eth_hw_addr_random(dev); } +static struct mvpp2_port *mvpp2_phylink_to_port(struct phylink_config *config) +{ + return container_of(config, struct mvpp2_port, phylink_config); +} + static void mvpp2_phylink_validate(struct phylink_config *config, unsigned long *supported, struct phylink_link_state *state) { - struct mvpp2_port *port = container_of(config, struct mvpp2_port, - phylink_config); + struct mvpp2_port *port = mvpp2_phylink_to_port(config); __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; /* Invalid combinations */ switch (state->interface) { case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_XAUI: - if (port->gop_id != 0) + if (!mvpp2_port_supports_xlg(port)) goto empty_set; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - if (port->priv->hw_version == MVPP22 && port->gop_id == 0) + if (!mvpp2_port_supports_rgmii(port)) goto empty_set; break; default: @@ -4791,7 +4817,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config, case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_XAUI: case PHY_INTERFACE_MODE_NA: - if (port->gop_id == 0) { + if (mvpp2_port_supports_xlg(port)) { phylink_set(mask, 10000baseT_Full); phylink_set(mask, 10000baseCR_Full); phylink_set(mask, 10000baseSR_Full); @@ -4902,8 +4928,7 @@ static void mvpp2_gmac_pcs_get_state(struct mvpp2_port *port, static void mvpp2_phylink_mac_pcs_get_state(struct phylink_config *config, struct phylink_link_state *state) { - struct mvpp2_port *port = container_of(config, struct mvpp2_port, - phylink_config); + struct mvpp2_port *port = mvpp2_phylink_to_port(config); if (port->priv->hw_version == MVPP22 && port->gop_id == 0) { u32 mode = readl(port->base + MVPP22_XLG_CTRL3_REG); @@ -4920,8 +4945,7 @@ static void mvpp2_phylink_mac_pcs_get_state(struct phylink_config *config, static void mvpp2_mac_an_restart(struct phylink_config *config) { - struct mvpp2_port *port = container_of(config, struct mvpp2_port, - phylink_config); + struct mvpp2_port *port = mvpp2_phylink_to_port(config); u32 val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); writel(val | MVPP2_GMAC_IN_BAND_RESTART_AN, @@ -4933,38 +4957,21 @@ static void mvpp2_mac_an_restart(struct phylink_config *config) static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode, const struct phylink_link_state *state) { - u32 old_ctrl0, ctrl0; - u32 old_ctrl4, ctrl4; - - old_ctrl0 = ctrl0 = readl(port->base + MVPP22_XLG_CTRL0_REG); - old_ctrl4 = ctrl4 = readl(port->base + MVPP22_XLG_CTRL4_REG); - - ctrl0 |= MVPP22_XLG_CTRL0_MAC_RESET_DIS; - - if (state->pause & MLO_PAUSE_TX) - ctrl0 |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN; - else - ctrl0 &= ~MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN; - - if (state->pause & MLO_PAUSE_RX) - ctrl0 |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN; - else - ctrl0 &= ~MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN; - - ctrl4 &= ~(MVPP22_XLG_CTRL4_MACMODSELECT_GMAC | - MVPP22_XLG_CTRL4_EN_IDLE_CHECK); - ctrl4 |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC; + u32 val; - if (old_ctrl0 != ctrl0) - writel(ctrl0, port->base + MVPP22_XLG_CTRL0_REG); - if (old_ctrl4 != ctrl4) - writel(ctrl4, port->base + MVPP22_XLG_CTRL4_REG); + mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG, + MVPP22_XLG_CTRL0_MAC_RESET_DIS, + MVPP22_XLG_CTRL0_MAC_RESET_DIS); + mvpp2_modify(port->base + MVPP22_XLG_CTRL4_REG, + MVPP22_XLG_CTRL4_MACMODSELECT_GMAC | + MVPP22_XLG_CTRL4_EN_IDLE_CHECK | + MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC, + MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC); - if (!(old_ctrl0 & MVPP22_XLG_CTRL0_MAC_RESET_DIS)) { - while (!(readl(port->base + MVPP22_XLG_CTRL0_REG) & - MVPP22_XLG_CTRL0_MAC_RESET_DIS)) - continue; - } + /* Wait for reset to deassert */ + do { + val = readl(port->base + MVPP22_XLG_CTRL0_REG); + } while (!(val & MVPP22_XLG_CTRL0_MAC_RESET_DIS)); } static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, @@ -5094,13 +5101,12 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { - struct net_device *dev = to_net_dev(config->dev); - struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port *port = mvpp2_phylink_to_port(config); bool change_interface = port->phy_interface != state->interface; /* Check for invalid configuration */ if (mvpp2_is_xlg(state->interface) && port->gop_id != 0) { - netdev_err(dev, "Invalid mode on %s\n", dev->name); + netdev_err(port->dev, "Invalid mode on %s\n", port->dev->name); return; } @@ -5140,25 +5146,26 @@ static void mvpp2_mac_link_up(struct phylink_config *config, int speed, int duplex, bool tx_pause, bool rx_pause) { - struct net_device *dev = to_net_dev(config->dev); - struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port *port = mvpp2_phylink_to_port(config); u32 val; if (mvpp2_is_xlg(interface)) { if (!phylink_autoneg_inband(mode)) { - val = readl(port->base + MVPP22_XLG_CTRL0_REG); - val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_DOWN; - val |= MVPP22_XLG_CTRL0_FORCE_LINK_PASS; - writel(val, port->base + MVPP22_XLG_CTRL0_REG); + val = MVPP22_XLG_CTRL0_FORCE_LINK_PASS; + if (tx_pause) + val |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN; + if (rx_pause) + val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN; + + mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG, + MVPP22_XLG_CTRL0_FORCE_LINK_DOWN | + MVPP22_XLG_CTRL0_FORCE_LINK_PASS | + MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN | + MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN, val); } } else { if (!phylink_autoneg_inband(mode)) { - val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); - val &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | - MVPP2_GMAC_CONFIG_MII_SPEED | - MVPP2_GMAC_CONFIG_GMII_SPEED | - MVPP2_GMAC_CONFIG_FULL_DUPLEX); - val |= MVPP2_GMAC_FORCE_LINK_PASS; + val = MVPP2_GMAC_FORCE_LINK_PASS; if (speed == SPEED_1000 || speed == SPEED_2500) val |= MVPP2_GMAC_CONFIG_GMII_SPEED; @@ -5168,34 +5175,40 @@ static void mvpp2_mac_link_up(struct phylink_config *config, if (duplex == DUPLEX_FULL) val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; - writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + mvpp2_modify(port->base + MVPP2_GMAC_AUTONEG_CONFIG, + MVPP2_GMAC_FORCE_LINK_DOWN | + MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX, val); } /* We can always update the flow control enable bits; * these will only be effective if flow control AN * (MVPP2_GMAC_FLOW_CTRL_AUTONEG) is disabled. */ - val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); - val &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN); + val = 0; if (tx_pause) val |= MVPP22_CTRL4_TX_FC_EN; if (rx_pause) val |= MVPP22_CTRL4_RX_FC_EN; - writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); + + mvpp2_modify(port->base + MVPP22_GMAC_CTRL_4_REG, + MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN, + val); } mvpp2_port_enable(port); mvpp2_egress_enable(port); mvpp2_ingress_enable(port); - netif_tx_wake_all_queues(dev); + netif_tx_wake_all_queues(port->dev); } static void mvpp2_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { - struct net_device *dev = to_net_dev(config->dev); - struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port *port = mvpp2_phylink_to_port(config); u32 val; if (!phylink_autoneg_inband(mode)) { @@ -5212,7 +5225,7 @@ static void mvpp2_mac_link_down(struct phylink_config *config, } } - netif_tx_stop_all_queues(dev); + netif_tx_stop_all_queues(port->dev); mvpp2_egress_disable(port); mvpp2_ingress_disable(port); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/common.h b/drivers/net/ethernet/marvell/octeontx2/af/common.h index cd33c2e6ca5f..f48eb66ed021 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/common.h @@ -43,7 +43,7 @@ struct qmem { void *base; dma_addr_t iova; int alloc_sz; - u8 entry_sz; + u16 entry_sz; u8 align; u32 qsize; }; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index b04f5429d72d..3a5b34a2a7a6 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -619,13 +619,14 @@ static void otx2_sq_append_tso(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, struct sk_buff *skb, u16 qidx) { struct netdev_queue *txq = netdev_get_tx_queue(pfvf->netdev, qidx); - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); - int tcp_data, seg_len, pkt_len, offset; + int hdr_len, tcp_data, seg_len, pkt_len, offset; struct nix_sqe_hdr_s *sqe_hdr; int first_sqe = sq->head; struct sg_list list; struct tso_t tso; + hdr_len = tso_start(skb, &tso); + /* Map SKB's fragments to DMA. * It's done here to avoid mapping for every TSO segment's packet. */ @@ -636,7 +637,6 @@ static void otx2_sq_append_tso(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, netdev_tx_sent_queue(txq, skb->len); - tso_start(skb, &tso); tcp_data = skb->len - hdr_len; while (tcp_data > 0) { char *hdr; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index f6a1f8666f95..20db302d31ce 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -344,29 +344,9 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode, /* Setup gmac */ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); mcr_new = mcr_cur; - mcr_new &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | - MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | - MAC_MCR_FORCE_RX_FC); mcr_new |= MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; - switch (state->speed) { - case SPEED_2500: - case SPEED_1000: - mcr_new |= MAC_MCR_SPEED_1000; - break; - case SPEED_100: - mcr_new |= MAC_MCR_SPEED_100; - break; - } - if (state->duplex == DUPLEX_FULL) { - mcr_new |= MAC_MCR_FORCE_DPX; - if (state->pause & MLO_PAUSE_TX) - mcr_new |= MAC_MCR_FORCE_TX_FC; - if (state->pause & MLO_PAUSE_RX) - mcr_new |= MAC_MCR_FORCE_RX_FC; - } - /* Only update control register when needed! */ if (mcr_new != mcr_cur) mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); @@ -443,6 +423,31 @@ static void mtk_mac_link_up(struct phylink_config *config, phylink_config); u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | + MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | + MAC_MCR_FORCE_RX_FC); + + /* Configure speed */ + switch (speed) { + case SPEED_2500: + case SPEED_1000: + mcr |= MAC_MCR_SPEED_1000; + break; + case SPEED_100: + mcr |= MAC_MCR_SPEED_100; + break; + } + + /* Configure duplex */ + if (duplex == DUPLEX_FULL) + mcr |= MAC_MCR_FORCE_DPX; + + /* Configure pause modes - phylink will avoid these for half duplex */ + if (tx_pause) + mcr |= MAC_MCR_FORCE_TX_FC; + if (rx_pause) + mcr |= MAC_MCR_FORCE_RX_FC; + mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN; mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index a99fe4b02b9b..c709e9a385f6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -113,6 +113,8 @@ static const struct devlink_ops mlx5_devlink_ops = { .eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get, .eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set, .eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get, + .port_function_hw_addr_get = mlx5_devlink_port_function_hw_addr_get, + .port_function_hw_addr_set = mlx5_devlink_port_function_hw_addr_set, #endif .flash_update = mlx5_devlink_flash_update, .info_get = mlx5_devlink_info_get, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c index a7551274be58..ad3594c4afcb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c @@ -676,7 +676,7 @@ static void mlx5_fw_tracer_handle_traces(struct work_struct *work) block_count = tracer->buff.size / TRACER_BLOCK_SIZE_BYTE; start_offset = tracer->buff.consumer_index * TRACER_BLOCK_SIZE_BYTE; - /* Copy the block to local buffer to avoid HW override while being processed*/ + /* Copy the block to local buffer to avoid HW override while being processed */ memcpy(tmp_trace_block, tracer->buff.log_buf + start_offset, TRACER_BLOCK_SIZE_BYTE); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c index baa162432e75..a0913836c973 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c @@ -10,6 +10,7 @@ #include <linux/spinlock.h> #include <linux/notifier.h> #include <net/netevent.h> +#include <net/arp.h> #include "neigh.h" #include "tc.h" #include "en_rep.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c index 430025550fad..c7107da03212 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c @@ -672,7 +672,7 @@ mlx5_tc_ct_block_flow_offload_stats(struct mlx5_ct_ft *ft, return -ENOENT; mlx5_fc_query_cached(entry->counter, &bytes, &packets, &lastuse); - flow_stats_update(&f->stats, bytes, packets, lastuse, + flow_stats_update(&f->stats, bytes, packets, 0, lastuse, FLOW_ACTION_HW_STATS_DELAYED); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.c index 7b17fcd0a56d..331ca2b0f8a4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.c @@ -215,16 +215,3 @@ int mlx5e_xsk_setup_umem(struct net_device *dev, struct xdp_umem *umem, u16 qid) return umem ? mlx5e_xsk_enable_umem(priv, umem, ix) : mlx5e_xsk_disable_umem(priv, ix); } - -u16 mlx5e_xsk_first_unused_channel(struct mlx5e_params *params, struct mlx5e_xsk *xsk) -{ - u16 res = xsk->refcnt ? params->num_channels : 0; - - while (res) { - if (mlx5e_xsk_get_umem(params, xsk, res - 1)) - break; - --res; - } - - return res; -} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.h index 25b4cbe58b54..bada94973586 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/umem.h @@ -26,6 +26,4 @@ int mlx5e_xsk_setup_umem(struct net_device *dev, struct xdp_umem *umem, u16 qid) int mlx5e_xsk_resize_reuseq(struct xdp_umem *umem, u32 nentries); -u16 mlx5e_xsk_first_unused_channel(struct mlx5e_params *params, struct mlx5e_xsk *xsk); - #endif /* __MLX5_EN_XSK_UMEM_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 92eb3bad4acd..bc55c82b55ba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -207,7 +207,7 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) { - struct net_device *netdev = x->xso.dev; + struct net_device *netdev = x->xso.real_dev; struct mlx5e_priv *priv; priv = netdev_priv(netdev); @@ -285,7 +285,7 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) static int mlx5e_xfrm_add_state(struct xfrm_state *x) { struct mlx5e_ipsec_sa_entry *sa_entry = NULL; - struct net_device *netdev = x->xso.dev; + struct net_device *netdev = x->xso.real_dev; struct mlx5_accel_esp_xfrm_attrs attrs; struct mlx5e_priv *priv; unsigned int sa_handle; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index 014639ea06e3..c4c9d6cda7e6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -220,7 +220,7 @@ static int arfs_create_groups(struct mlx5e_flow_table *ft, sizeof(*ft->g), GFP_KERNEL); in = kvzalloc(inlen, GFP_KERNEL); if (!in || !ft->g) { - kvfree(ft->g); + kfree(ft->g); kvfree(in); return -ENOMEM; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 006807e04eda..ed2430677b12 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -35,7 +35,6 @@ #include <net/switchdev.h> #include <net/pkt_cls.h> #include <net/act_api.h> -#include <net/arp.h> #include <net/devlink.h> #include <net/ipv6_stubs.h> @@ -1181,12 +1180,6 @@ is_devlink_port_supported(const struct mlx5_core_dev *dev, mlx5_eswitch_is_vf_vport(dev->priv.eswitch, rpriv->rep->vport); } -static unsigned int -vport_to_devlink_port_index(const struct mlx5_core_dev *dev, u16 vport_num) -{ - return (MLX5_CAP_GEN(dev, vhca_id) << 16) | vport_num; -} - static int register_devlink_port(struct mlx5_core_dev *dev, struct mlx5e_rep_priv *rpriv) { @@ -1200,7 +1193,7 @@ static int register_devlink_port(struct mlx5_core_dev *dev, return 0; mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid); - dl_port_index = vport_to_devlink_port_index(dev, rep->vport); + dl_port_index = mlx5_esw_vport_to_devlink_port_index(dev, rep->vport); pfnum = PCI_FUNC(dev->pdev->devfn); if (rep->vport == MLX5_VPORT_UPLINK) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 7fc84f58e28a..bc9c0ac15f99 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -4828,7 +4828,7 @@ int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv, no_peer_counter: mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS); out: - flow_stats_update(&f->stats, bytes, packets, lastuse, + flow_stats_update(&f->stats, bytes, packets, 0, lastuse, FLOW_ACTION_HW_STATS_DELAYED); trace_mlx5e_stats_flower(f); errout: @@ -4946,7 +4946,7 @@ void mlx5e_tc_stats_matchall(struct mlx5e_priv *priv, dpkts = cur_stats.rx_packets - rpriv->prev_vf_vport_stats.rx_packets; dbytes = cur_stats.rx_bytes - rpriv->prev_vf_vport_stats.rx_bytes; rpriv->prev_vf_vport_stats = cur_stats; - flow_stats_update(&ma->stats, dbytes, dpkts, jiffies, + flow_stats_update(&ma->stats, dbytes, dpkts, 0, jiffies, FLOW_ACTION_HW_STATS_DELAYED); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 5c330b0cae21..1561eaa89ffd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -40,6 +40,14 @@ #ifdef CONFIG_MLX5_ESWITCH +int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags); + +struct mlx5e_tc_update_priv { + struct net_device *tun_dev; +}; + +#if IS_ENABLED(CONFIG_MLX5_CLS_ACT) + struct tunnel_match_key { struct flow_dissector_key_control enc_control; struct flow_dissector_key_keyid enc_key_id; @@ -114,8 +122,6 @@ void mlx5e_put_encap_flow_list(struct mlx5e_priv *priv, struct list_head *flow_l struct mlx5e_neigh_hash_entry; void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe); -int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags); - void mlx5e_tc_reoffload_flows_work(struct work_struct *work); enum mlx5e_tc_attr_to_reg { @@ -142,10 +148,6 @@ extern struct mlx5e_tc_attr_to_reg_mapping mlx5e_tc_attr_to_reg_mappings[]; bool mlx5e_is_valid_eswitch_fwd_dev(struct mlx5e_priv *priv, struct net_device *out_dev); -struct mlx5e_tc_update_priv { - struct net_device *tun_dev; -}; - struct mlx5e_tc_mod_hdr_acts { int num_actions; int max_actions; @@ -174,8 +176,6 @@ void mlx5e_tc_set_ethertype(struct mlx5_core_dev *mdev, struct flow_match_basic *match, bool outer, void *headers_c, void *headers_v); -#if IS_ENABLED(CONFIG_MLX5_CLS_ACT) - int mlx5e_tc_nic_init(struct mlx5e_priv *priv); void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 1116ab9bea6c..c656c9f081c1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -63,6 +63,29 @@ struct vport_addr { static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw); static void esw_cleanup_vepa_rules(struct mlx5_eswitch *esw); +static int mlx5_eswitch_check(const struct mlx5_core_dev *dev) +{ + if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + return -EOPNOTSUPP; + + if (!MLX5_ESWITCH_MANAGER(dev)) + return -EPERM; + + return 0; +} + +struct mlx5_eswitch *mlx5_devlink_eswitch_get(struct devlink *devlink) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + int err; + + err = mlx5_eswitch_check(dev); + if (err) + return ERR_PTR(err); + + return dev->priv.eswitch; +} + struct mlx5_vport *__must_check mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num) { @@ -1127,7 +1150,7 @@ int mlx5_esw_modify_vport_rate(struct mlx5_eswitch *esw, u16 vport_num, MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW); } -static void node_guid_gen_from_mac(u64 *node_guid, u8 mac[ETH_ALEN]) +static void node_guid_gen_from_mac(u64 *node_guid, const u8 *mac) { ((u8 *)node_guid)[7] = mac[0]; ((u8 *)node_guid)[6] = mac[1]; @@ -1778,46 +1801,135 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) } /* Vport Administration */ -int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, - u16 vport, u8 mac[ETH_ALEN]) +static int +mlx5_esw_set_vport_mac_locked(struct mlx5_eswitch *esw, + struct mlx5_vport *evport, const u8 *mac) { - struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport); + u16 vport_num = evport->vport; u64 node_guid; int err = 0; - if (IS_ERR(evport)) - return PTR_ERR(evport); if (is_multicast_ether_addr(mac)) return -EINVAL; - mutex_lock(&esw->state_lock); - if (evport->info.spoofchk && !is_valid_ether_addr(mac)) mlx5_core_warn(esw->dev, "Set invalid MAC while spoofchk is on, vport(%d)\n", - vport); + vport_num); - err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac); + err = mlx5_modify_nic_vport_mac_address(esw->dev, vport_num, mac); if (err) { mlx5_core_warn(esw->dev, "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n", - vport, err); - goto unlock; + vport_num, err); + return err; } node_guid_gen_from_mac(&node_guid, mac); - err = mlx5_modify_nic_vport_node_guid(esw->dev, vport, node_guid); + err = mlx5_modify_nic_vport_node_guid(esw->dev, vport_num, node_guid); if (err) mlx5_core_warn(esw->dev, "Failed to set vport %d node guid, err = %d. RDMA_CM will not function properly for this VF.\n", - vport, err); + vport_num, err); ether_addr_copy(evport->info.mac, mac); evport->info.node_guid = node_guid; if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY) err = esw_acl_ingress_lgcy_setup(esw, evport); -unlock: + return err; +} + +int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, + u16 vport, const u8 *mac) +{ + struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport); + int err = 0; + + if (IS_ERR(evport)) + return PTR_ERR(evport); + + mutex_lock(&esw->state_lock); + err = mlx5_esw_set_vport_mac_locked(esw, evport, mac); + mutex_unlock(&esw->state_lock); + return err; +} + +static bool +is_port_function_supported(const struct mlx5_eswitch *esw, u16 vport_num) +{ + return vport_num == MLX5_VPORT_PF || + mlx5_eswitch_is_vf_vport(esw, vport_num); +} + +int mlx5_devlink_port_function_hw_addr_get(struct devlink *devlink, + struct devlink_port *port, + u8 *hw_addr, int *hw_addr_len, + struct netlink_ext_ack *extack) +{ + struct mlx5_eswitch *esw; + struct mlx5_vport *vport; + int err = -EOPNOTSUPP; + u16 vport_num; + + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); + + vport_num = mlx5_esw_devlink_port_index_to_vport_num(port->index); + if (!is_port_function_supported(esw, vport_num)) + return -EOPNOTSUPP; + + vport = mlx5_eswitch_get_vport(esw, vport_num); + if (IS_ERR(vport)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid port"); + return PTR_ERR(vport); + } + + mutex_lock(&esw->state_lock); + if (vport->enabled) { + ether_addr_copy(hw_addr, vport->info.mac); + *hw_addr_len = ETH_ALEN; + err = 0; + } else { + NL_SET_ERR_MSG_MOD(extack, "Eswitch vport is disabled"); + } + mutex_unlock(&esw->state_lock); + return err; +} + +int mlx5_devlink_port_function_hw_addr_set(struct devlink *devlink, + struct devlink_port *port, + const u8 *hw_addr, int hw_addr_len, + struct netlink_ext_ack *extack) +{ + struct mlx5_eswitch *esw; + struct mlx5_vport *vport; + int err = -EOPNOTSUPP; + u16 vport_num; + + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) { + NL_SET_ERR_MSG_MOD(extack, "Eswitch doesn't support set hw_addr"); + return PTR_ERR(esw); + } + + vport_num = mlx5_esw_devlink_port_index_to_vport_num(port->index); + if (!is_port_function_supported(esw, vport_num)) { + NL_SET_ERR_MSG_MOD(extack, "Port doesn't support set hw_addr"); + return -EINVAL; + } + vport = mlx5_eswitch_get_vport(esw, vport_num); + if (IS_ERR(vport)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid port"); + return PTR_ERR(vport); + } + + mutex_lock(&esw->state_lock); + if (vport->enabled) + err = mlx5_esw_set_vport_mac_locked(esw, vport, hw_addr); + else + NL_SET_ERR_MSG_MOD(extack, "Eswitch vport is disabled"); mutex_unlock(&esw->state_lock); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index a5175e98c0b3..522cadc09149 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -44,16 +44,6 @@ #include "lib/mpfs.h" #include "en/tc_ct.h" -#define FDB_TC_MAX_CHAIN 3 -#define FDB_FT_CHAIN (FDB_TC_MAX_CHAIN + 1) -#define FDB_TC_SLOW_PATH_CHAIN (FDB_FT_CHAIN + 1) - -/* The index of the last real chain (FT) + 1 as chain zero is valid as well */ -#define FDB_NUM_CHAINS (FDB_FT_CHAIN + 1) - -#define FDB_TC_MAX_PRIO 16 -#define FDB_TC_LEVELS_PER_PRIO 2 - #ifdef CONFIG_MLX5_ESWITCH #define ESW_OFFLOADS_DEFAULT_NUM_GROUPS 15 @@ -311,7 +301,7 @@ int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int num_vfs); void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw, bool clear_vf); void mlx5_eswitch_disable(struct mlx5_eswitch *esw, bool clear_vf); int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, - u16 vport, u8 mac[ETH_ALEN]); + u16 vport, const u8 *mac); int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, u16 vport, int link_state); int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, @@ -450,6 +440,15 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, struct netlink_ext_ack *extack); int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, enum devlink_eswitch_encap_mode *encap); +int mlx5_devlink_port_function_hw_addr_get(struct devlink *devlink, + struct devlink_port *port, + u8 *hw_addr, int *hw_addr_len, + struct netlink_ext_ack *extack); +int mlx5_devlink_port_function_hw_addr_set(struct devlink *devlink, + struct devlink_port *port, + const u8 *hw_addr, int hw_addr_len, + struct netlink_ext_ack *extack); + void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type); int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw, @@ -565,6 +564,19 @@ static inline u16 mlx5_eswitch_index_to_vport_num(struct mlx5_eswitch *esw, return index; } +static inline unsigned int +mlx5_esw_vport_to_devlink_port_index(const struct mlx5_core_dev *dev, + u16 vport_num) +{ + return (MLX5_CAP_GEN(dev, vhca_id) << 16) | vport_num; +} + +static inline u16 +mlx5_esw_devlink_port_index_to_vport_num(unsigned int dl_port_index) +{ + return dl_port_index & 0xffff; +} + /* TODO: This mlx5e_tc function shouldn't be called by eswitch */ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw); @@ -634,6 +646,7 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw); for ((vport) = (nvfs); \ (vport) >= (esw)->first_host_vport; (vport)--) +struct mlx5_eswitch *mlx5_devlink_eswitch_get(struct devlink *devlink); struct mlx5_vport *__must_check mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 060354bb211a..74a2b76c7c07 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -2279,17 +2279,6 @@ static int esw_inline_mode_to_devlink(u8 mlx5_mode, u8 *mode) return 0; } -static int mlx5_eswitch_check(const struct mlx5_core_dev *dev) -{ - if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) - return -EOPNOTSUPP; - - if(!MLX5_ESWITCH_MANAGER(dev)) - return -EPERM; - - return 0; -} - static int eswitch_devlink_esw_mode_check(const struct mlx5_eswitch *esw) { /* devlink commands in NONE eswitch mode are currently supported only @@ -2302,14 +2291,13 @@ static int eswitch_devlink_esw_mode_check(const struct mlx5_eswitch *esw) int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack) { - struct mlx5_core_dev *dev = devlink_priv(devlink); - struct mlx5_eswitch *esw = dev->priv.eswitch; u16 cur_mlx5_mode, mlx5_mode = 0; + struct mlx5_eswitch *esw; int err; - err = mlx5_eswitch_check(dev); - if (err) - return err; + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); if (esw_mode_from_devlink(mode, &mlx5_mode)) return -EINVAL; @@ -2338,16 +2326,15 @@ unlock: int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) { - struct mlx5_core_dev *dev = devlink_priv(devlink); - struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_eswitch *esw; int err; - err = mlx5_eswitch_check(dev); - if (err) - return err; + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); mutex_lock(&esw->mode_lock); - err = eswitch_devlink_esw_mode_check(dev->priv.eswitch); + err = eswitch_devlink_esw_mode_check(esw); if (err) goto unlock; @@ -2361,13 +2348,13 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); - struct mlx5_eswitch *esw = dev->priv.eswitch; int err, vport, num_vport; + struct mlx5_eswitch *esw; u8 mlx5_mode; - err = mlx5_eswitch_check(dev); - if (err) - return err; + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); mutex_lock(&esw->mode_lock); err = eswitch_devlink_esw_mode_check(esw); @@ -2424,13 +2411,12 @@ out: int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode) { - struct mlx5_core_dev *dev = devlink_priv(devlink); - struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_eswitch *esw; int err; - err = mlx5_eswitch_check(dev); - if (err) - return err; + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); mutex_lock(&esw->mode_lock); err = eswitch_devlink_esw_mode_check(esw); @@ -2448,12 +2434,12 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); - struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_eswitch *esw; int err; - err = mlx5_eswitch_check(dev); - if (err) - return err; + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); mutex_lock(&esw->mode_lock); err = eswitch_devlink_esw_mode_check(esw); @@ -2508,13 +2494,13 @@ unlock: int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, enum devlink_eswitch_encap_mode *encap) { - struct mlx5_core_dev *dev = devlink_priv(devlink); - struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_eswitch *esw; int err; - err = mlx5_eswitch_check(dev); - if (err) - return err; + esw = mlx5_devlink_eswitch_get(devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); + mutex_lock(&esw->mode_lock); err = eswitch_devlink_esw_mode_check(esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 13e2fb79c21a..e47a66983935 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -41,7 +41,6 @@ #include "diag/fs_tracepoint.h" #include "accel/ipsec.h" #include "fpga/ipsec.h" -#include "eswitch.h" #define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\ sizeof(struct init_tree_node)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 825b662f809b..afe7f0bffb93 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -39,6 +39,16 @@ #include <linux/llist.h> #include <steering/fs_dr.h> +#define FDB_TC_MAX_CHAIN 3 +#define FDB_FT_CHAIN (FDB_TC_MAX_CHAIN + 1) +#define FDB_TC_SLOW_PATH_CHAIN (FDB_FT_CHAIN + 1) + +/* The index of the last real chain (FT) + 1 as chain zero is valid as well */ +#define FDB_NUM_CHAINS (FDB_FT_CHAIN + 1) + +#define FDB_TC_MAX_PRIO 16 +#define FDB_TC_LEVELS_PER_PRIO 2 + struct mlx5_modify_hdr { enum mlx5_flow_namespace_type ns_type; union { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c index 82c766a95165..be34330d89cc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c @@ -40,7 +40,6 @@ struct mlx5_vxlan { struct mlx5_core_dev *mdev; - spinlock_t lock; /* protect vxlan table */ /* max_num_ports is usuallly 4, 16 buckets is more than enough */ DECLARE_HASHTABLE(htable, 4); int num_ports; @@ -78,45 +77,47 @@ static int mlx5_vxlan_core_del_port_cmd(struct mlx5_core_dev *mdev, u16 port) return mlx5_cmd_exec_in(mdev, delete_vxlan_udp_dport, in); } -static struct mlx5_vxlan_port* -mlx5_vxlan_lookup_port_locked(struct mlx5_vxlan *vxlan, u16 port) +bool mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port) { struct mlx5_vxlan_port *vxlanp; + bool found = false; - hash_for_each_possible(vxlan->htable, vxlanp, hlist, port) { - if (vxlanp->udp_port == port) - return vxlanp; - } + if (!mlx5_vxlan_allowed(vxlan)) + return NULL; - return NULL; + rcu_read_lock(); + hash_for_each_possible_rcu(vxlan->htable, vxlanp, hlist, port) + if (vxlanp->udp_port == port) { + found = true; + break; + } + rcu_read_unlock(); + + return found; } -struct mlx5_vxlan_port *mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port) +static struct mlx5_vxlan_port *vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port) { struct mlx5_vxlan_port *vxlanp; - if (!mlx5_vxlan_allowed(vxlan)) - return NULL; - - spin_lock_bh(&vxlan->lock); - vxlanp = mlx5_vxlan_lookup_port_locked(vxlan, port); - spin_unlock_bh(&vxlan->lock); - - return vxlanp; + hash_for_each_possible(vxlan->htable, vxlanp, hlist, port) + if (vxlanp->udp_port == port) + return vxlanp; + return NULL; } int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port) { struct mlx5_vxlan_port *vxlanp; - int ret = -ENOSPC; + int ret = 0; - vxlanp = mlx5_vxlan_lookup_port(vxlan, port); + mutex_lock(&vxlan->sync_lock); + vxlanp = vxlan_lookup_port(vxlan, port); if (vxlanp) { refcount_inc(&vxlanp->refcount); - return 0; + goto unlock; } - mutex_lock(&vxlan->sync_lock); if (vxlan->num_ports >= mlx5_vxlan_max_udp_ports(vxlan->mdev)) { mlx5_core_info(vxlan->mdev, "UDP port (%d) not offloaded, max number of UDP ports (%d) are already offloaded\n", @@ -138,9 +139,7 @@ int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port) vxlanp->udp_port = port; refcount_set(&vxlanp->refcount, 1); - spin_lock_bh(&vxlan->lock); - hash_add(vxlan->htable, &vxlanp->hlist, port); - spin_unlock_bh(&vxlan->lock); + hash_add_rcu(vxlan->htable, &vxlanp->hlist, port); vxlan->num_ports++; mutex_unlock(&vxlan->sync_lock); @@ -157,34 +156,26 @@ unlock: int mlx5_vxlan_del_port(struct mlx5_vxlan *vxlan, u16 port) { struct mlx5_vxlan_port *vxlanp; - bool remove = false; int ret = 0; mutex_lock(&vxlan->sync_lock); - spin_lock_bh(&vxlan->lock); - vxlanp = mlx5_vxlan_lookup_port_locked(vxlan, port); + vxlanp = vxlan_lookup_port(vxlan, port); if (!vxlanp) { ret = -ENOENT; goto out_unlock; } if (refcount_dec_and_test(&vxlanp->refcount)) { - hash_del(&vxlanp->hlist); - remove = true; - } - -out_unlock: - spin_unlock_bh(&vxlan->lock); - - if (remove) { + hash_del_rcu(&vxlanp->hlist); + synchronize_rcu(); mlx5_vxlan_core_del_port_cmd(vxlan->mdev, port); kfree(vxlanp); vxlan->num_ports--; } +out_unlock: mutex_unlock(&vxlan->sync_lock); - return ret; } @@ -201,7 +192,6 @@ struct mlx5_vxlan *mlx5_vxlan_create(struct mlx5_core_dev *mdev) vxlan->mdev = mdev; mutex_init(&vxlan->sync_lock); - spin_lock_init(&vxlan->lock); hash_init(vxlan->htable); /* Hardware adds 4789 (IANA_VXLAN_UDP_PORT) by default */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.h index 8fb0eb08fa6d..6d599f4a8acd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.h @@ -50,15 +50,14 @@ struct mlx5_vxlan *mlx5_vxlan_create(struct mlx5_core_dev *mdev); void mlx5_vxlan_destroy(struct mlx5_vxlan *vxlan); int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port); int mlx5_vxlan_del_port(struct mlx5_vxlan *vxlan, u16 port); -struct mlx5_vxlan_port *mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port); +bool mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port); #else static inline struct mlx5_vxlan* mlx5_vxlan_create(struct mlx5_core_dev *mdev) { return ERR_PTR(-EOPNOTSUPP); } static inline void mlx5_vxlan_destroy(struct mlx5_vxlan *vxlan) { return; } static inline int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port) { return -EOPNOTSUPP; } static inline int mlx5_vxlan_del_port(struct mlx5_vxlan *vxlan, u16 port) { return -EOPNOTSUPP; } -static inline struct mx5_vxlan_port* -mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port) { return NULL; } +static inline bool mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port) { return false; } #endif #endif /* __MLX5_VXLAN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index c107d92dc118..88cdb9bb4c4a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -173,7 +173,7 @@ int mlx5_query_mac_address(struct mlx5_core_dev *mdev, u8 *addr) EXPORT_SYMBOL_GPL(mlx5_query_mac_address); int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev, - u16 vport, u8 *addr) + u16 vport, const u8 *addr) { void *in; int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c index c3d04319ff44..30a7d5afdec7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c @@ -1684,3 +1684,54 @@ int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block, return 0; } EXPORT_SYMBOL(mlxsw_afa_block_append_mcrouter); + +/* L4 Port Action + * -------------- + * The L4_PORT_ACTION is used for modifying the sport and dport fields of the packet, e.g. for NAT. + * If (the L4 is TCP) or if (the L4 is UDP and checksum field!=0) then the L4 checksum is updated. + */ + +#define MLXSW_AFA_L4PORT_CODE 0x12 +#define MLXSW_AFA_L4PORT_SIZE 1 + +enum mlxsw_afa_l4port_s_d { + /* configure src_l4_port */ + MLXSW_AFA_L4PORT_S_D_SRC, + /* configure dst_l4_port */ + MLXSW_AFA_L4PORT_S_D_DST, +}; + +/* afa_l4port_s_d + * Source or destination. + */ +MLXSW_ITEM32(afa, l4port, s_d, 0x00, 31, 1); + +/* afa_l4port_l4_port + * Number of port to change to. + */ +MLXSW_ITEM32(afa, l4port, l4_port, 0x08, 0, 16); + +static void mlxsw_afa_l4port_pack(char *payload, enum mlxsw_afa_l4port_s_d s_d, u16 l4_port) +{ + mlxsw_afa_l4port_s_d_set(payload, s_d); + mlxsw_afa_l4port_l4_port_set(payload, l4_port); +} + +int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport, u16 l4_port, + struct netlink_ext_ack *extack) +{ + enum mlxsw_afa_l4port_s_d s_d = is_dport ? MLXSW_AFA_L4PORT_S_D_DST : + MLXSW_AFA_L4PORT_S_D_SRC; + char *act = mlxsw_afa_block_append_action(block, + MLXSW_AFA_L4PORT_CODE, + MLXSW_AFA_L4PORT_SIZE); + + if (IS_ERR(act)) { + NL_SET_ERR_MSG_MOD(extack, "Cannot append L4_PORT action"); + return PTR_ERR(act); + } + + mlxsw_afa_l4port_pack(act, s_d, l4_port); + return 0; +} +EXPORT_SYMBOL(mlxsw_afa_block_append_l4port); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h index 8c2705e16ef7..a72350399bcf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h @@ -82,5 +82,7 @@ int mlxsw_afa_block_append_fid_set(struct mlxsw_afa_block *block, u16 fid, int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block, u16 expected_irif, u16 min_mtu, bool rmid_valid, u32 kvdl_index); +int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport, u16 l4_port, + struct netlink_ext_ack *extack); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 029ea344ad65..a5c5363915dc 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -45,8 +45,8 @@ #include "../mlxfw/mlxfw.h" #define MLXSW_SP1_FWREV_MAJOR 13 -#define MLXSW_SP1_FWREV_MINOR 2000 -#define MLXSW_SP1_FWREV_SUBMINOR 2714 +#define MLXSW_SP1_FWREV_MINOR 2007 +#define MLXSW_SP1_FWREV_SUBMINOR 1168 #define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702 static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { @@ -62,8 +62,8 @@ static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { "." __stringify(MLXSW_SP1_FWREV_SUBMINOR) ".mfa2" #define MLXSW_SP2_FWREV_MAJOR 29 -#define MLXSW_SP2_FWREV_MINOR 2000 -#define MLXSW_SP2_FWREV_SUBMINOR 2714 +#define MLXSW_SP2_FWREV_MINOR 2007 +#define MLXSW_SP2_FWREV_SUBMINOR 1168 static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { .major = MLXSW_SP2_FWREV_MAJOR, @@ -76,6 +76,21 @@ static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { "." __stringify(MLXSW_SP2_FWREV_MINOR) \ "." __stringify(MLXSW_SP2_FWREV_SUBMINOR) ".mfa2" +#define MLXSW_SP3_FWREV_MAJOR 30 +#define MLXSW_SP3_FWREV_MINOR 2007 +#define MLXSW_SP3_FWREV_SUBMINOR 1168 + +static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = { + .major = MLXSW_SP3_FWREV_MAJOR, + .minor = MLXSW_SP3_FWREV_MINOR, + .subminor = MLXSW_SP3_FWREV_SUBMINOR, +}; + +#define MLXSW_SP3_FW_FILENAME \ + "mellanox/mlxsw_spectrum3-" __stringify(MLXSW_SP3_FWREV_MAJOR) \ + "." __stringify(MLXSW_SP3_FWREV_MINOR) \ + "." __stringify(MLXSW_SP3_FWREV_SUBMINOR) ".mfa2" + static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum"; static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2"; static const char mlxsw_sp3_driver_name[] = "mlxsw_spectrum3"; @@ -4594,6 +4609,7 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core, mlxsw_sp->afa_ops = &mlxsw_sp1_act_afa_ops; mlxsw_sp->afk_ops = &mlxsw_sp1_afk_ops; mlxsw_sp->mr_tcam_ops = &mlxsw_sp1_mr_tcam_ops; + mlxsw_sp->acl_rulei_ops = &mlxsw_sp1_acl_rulei_ops; mlxsw_sp->acl_tcam_ops = &mlxsw_sp1_acl_tcam_ops; mlxsw_sp->nve_ops_arr = mlxsw_sp1_nve_ops_arr; mlxsw_sp->mac_mask = mlxsw_sp1_mac_mask; @@ -4621,6 +4637,7 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core, mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops; mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops; mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops; + mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops; mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops; mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr; mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask; @@ -4640,10 +4657,13 @@ static int mlxsw_sp3_init(struct mlxsw_core *mlxsw_core, { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); + mlxsw_sp->req_rev = &mlxsw_sp3_fw_rev; + mlxsw_sp->fw_filename = MLXSW_SP3_FW_FILENAME; mlxsw_sp->kvdl_ops = &mlxsw_sp2_kvdl_ops; mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops; mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops; mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops; + mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops; mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops; mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr; mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask; @@ -6330,3 +6350,4 @@ MODULE_DEVICE_TABLE(pci, mlxsw_sp2_pci_id_table); MODULE_DEVICE_TABLE(pci, mlxsw_sp3_pci_id_table); MODULE_FIRMWARE(MLXSW_SP1_FW_FILENAME); MODULE_FIRMWARE(MLXSW_SP2_FW_FILENAME); +MODULE_FIRMWARE(MLXSW_SP3_FW_FILENAME); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3abe3e7d89bc..3e794d58184b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -120,6 +120,7 @@ struct mlxsw_sp_kvdl; struct mlxsw_sp_nve; struct mlxsw_sp_kvdl_ops; struct mlxsw_sp_mr_tcam_ops; +struct mlxsw_sp_acl_rulei_ops; struct mlxsw_sp_acl_tcam_ops; struct mlxsw_sp_nve_ops; struct mlxsw_sp_sb_vals; @@ -164,6 +165,7 @@ struct mlxsw_sp { const struct mlxsw_afa_ops *afa_ops; const struct mlxsw_afk_ops *afk_ops; const struct mlxsw_sp_mr_tcam_ops *mr_tcam_ops; + const struct mlxsw_sp_acl_rulei_ops *acl_rulei_ops; const struct mlxsw_sp_acl_tcam_ops *acl_tcam_ops; const struct mlxsw_sp_nve_ops **nve_ops_arr; const struct mlxsw_sp_rif_ops **rif_ops_arr; @@ -854,6 +856,17 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp); u32 mlxsw_sp_acl_region_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp); int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val); +struct mlxsw_sp_acl_mangle_action; + +struct mlxsw_sp_acl_rulei_ops { + int (*act_mangle_field)(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei, + struct mlxsw_sp_acl_mangle_action *mact, u32 val, + struct netlink_ext_ack *extack); +}; + +extern struct mlxsw_sp_acl_rulei_ops mlxsw_sp1_acl_rulei_ops; +extern struct mlxsw_sp_acl_rulei_ops mlxsw_sp2_acl_rulei_ops; + /* spectrum_acl_tcam.c */ struct mlxsw_sp_acl_tcam; struct mlxsw_sp_acl_tcam_region; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 47da9ee0045d..a671156a1428 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -508,6 +508,8 @@ enum mlxsw_sp_acl_mangle_field { MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD, MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP, MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN, + MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT, + MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT, }; struct mlxsw_sp_acl_mangle_action { @@ -538,13 +540,26 @@ struct mlxsw_sp_acl_mangle_action { MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_IP6, \ _offset, _mask, _shift, _field) +#define MLXSW_SP_ACL_MANGLE_ACTION_TCP(_offset, _mask, _shift, _field) \ + MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_TCP, _offset, _mask, _shift, _field) + +#define MLXSW_SP_ACL_MANGLE_ACTION_UDP(_offset, _mask, _shift, _field) \ + MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_UDP, _offset, _mask, _shift, _field) + static struct mlxsw_sp_acl_mangle_action mlxsw_sp_acl_mangle_actions[] = { MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff00ffff, 16, IP_DSFIELD), MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff03ffff, 18, IP_DSCP), MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xfffcffff, 16, IP_ECN), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf00fffff, 20, IP_DSFIELD), MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf03fffff, 22, IP_DSCP), MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xffcfffff, 20, IP_ECN), + + MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0x0000ffff, 16, IP_SPORT), + MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0xffff0000, 0, IP_DPORT), + + MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0x0000ffff, 16, IP_SPORT), + MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0xffff0000, 0, IP_DPORT), }; static int @@ -563,11 +578,48 @@ mlxsw_sp_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, case MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN: return mlxsw_afa_block_append_qos_ecn(rulei->act_block, val, extack); + default: + return -EOPNOTSUPP; } +} - /* We shouldn't have gotten a match in the first place! */ - WARN_ONCE(1, "Unhandled mangle field"); - return -EINVAL; +static int mlxsw_sp1_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct mlxsw_sp_acl_mangle_action *mact, + u32 val, struct netlink_ext_ack *extack) +{ + int err; + + err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack); + if (err != -EOPNOTSUPP) + return err; + + NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field"); + return err; +} + +static int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct mlxsw_sp_acl_mangle_action *mact, + u32 val, struct netlink_ext_ack *extack) +{ + int err; + + err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack); + if (err != -EOPNOTSUPP) + return err; + + switch (mact->field) { + case MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT: + return mlxsw_afa_block_append_l4port(rulei->act_block, false, val, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT: + return mlxsw_afa_block_append_l4port(rulei->act_block, true, val, extack); + default: + break; + } + + NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field"); + return err; } int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp, @@ -576,6 +628,7 @@ int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp, u32 offset, u32 mask, u32 val, struct netlink_ext_ack *extack) { + const struct mlxsw_sp_acl_rulei_ops *acl_rulei_ops = mlxsw_sp->acl_rulei_ops; struct mlxsw_sp_acl_mangle_action *mact; size_t i; @@ -585,13 +638,13 @@ int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp, mact->offset == offset && mact->mask == mask) { val >>= mact->shift; - return mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, - rulei, mact, - val, extack); + return acl_rulei_ops->act_mangle_field(mlxsw_sp, + rulei, mact, + val, extack); } } - NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field"); + NL_SET_ERR_MSG_MOD(extack, "Unknown mangle field"); return -EINVAL; } @@ -930,3 +983,11 @@ int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val) return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(mlxsw_sp, &acl->tcam, val); } + +struct mlxsw_sp_acl_rulei_ops mlxsw_sp1_acl_rulei_ops = { + .act_mangle_field = mlxsw_sp1_acl_rulei_act_mangle_field, +}; + +struct mlxsw_sp_acl_rulei_ops mlxsw_sp2_acl_rulei_ops = { + .act_mangle_field = mlxsw_sp2_acl_rulei_act_mangle_field, +}; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c index 49a72a8f1f57..f8e3d635b9e2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c @@ -138,7 +138,7 @@ static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, err = mlxsw_sp_port_pg_destroy(mlxsw_sp_port, my_ets->prio_tc, ets->prio_tc); if (err) - netdev_warn(dev, "Failed to remove ununsed PGs\n"); + netdev_warn(dev, "Failed to remove unused PGs\n"); return 0; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 51e1b3930c56..61d21043d83a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -633,7 +633,7 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp, if (err) goto err_rule_get_stats; - flow_stats_update(&f->stats, bytes, packets, lastuse, used_hw_stats); + flow_stats_update(&f->stats, bytes, packets, 0, lastuse, used_hw_stats); mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); return 0; diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index f1711ac86d0c..2373e72d2d29 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -807,26 +807,29 @@ static int lan743x_mac_init(struct lan743x_adapter *adapter) data |= MAC_CR_CNTR_RST_; lan743x_csr_write(adapter, MAC_CR, data); - mac_addr_hi = lan743x_csr_read(adapter, MAC_RX_ADDRH); - mac_addr_lo = lan743x_csr_read(adapter, MAC_RX_ADDRL); - adapter->mac_address[0] = mac_addr_lo & 0xFF; - adapter->mac_address[1] = (mac_addr_lo >> 8) & 0xFF; - adapter->mac_address[2] = (mac_addr_lo >> 16) & 0xFF; - adapter->mac_address[3] = (mac_addr_lo >> 24) & 0xFF; - adapter->mac_address[4] = mac_addr_hi & 0xFF; - adapter->mac_address[5] = (mac_addr_hi >> 8) & 0xFF; - - if (((mac_addr_hi & 0x0000FFFF) == 0x0000FFFF) && - mac_addr_lo == 0xFFFFFFFF) { - mac_address_valid = false; - } else if (!is_valid_ether_addr(adapter->mac_address)) { - mac_address_valid = false; - } - - if (!mac_address_valid) - eth_random_addr(adapter->mac_address); + if (!is_valid_ether_addr(adapter->mac_address)) { + mac_addr_hi = lan743x_csr_read(adapter, MAC_RX_ADDRH); + mac_addr_lo = lan743x_csr_read(adapter, MAC_RX_ADDRL); + adapter->mac_address[0] = mac_addr_lo & 0xFF; + adapter->mac_address[1] = (mac_addr_lo >> 8) & 0xFF; + adapter->mac_address[2] = (mac_addr_lo >> 16) & 0xFF; + adapter->mac_address[3] = (mac_addr_lo >> 24) & 0xFF; + adapter->mac_address[4] = mac_addr_hi & 0xFF; + adapter->mac_address[5] = (mac_addr_hi >> 8) & 0xFF; + + if (((mac_addr_hi & 0x0000FFFF) == 0x0000FFFF) && + mac_addr_lo == 0xFFFFFFFF) { + mac_address_valid = false; + } else if (!is_valid_ether_addr(adapter->mac_address)) { + mac_address_valid = false; + } + + if (!mac_address_valid) + eth_random_addr(adapter->mac_address); + } lan743x_mac_set_address(adapter, adapter->mac_address); ether_addr_copy(netdev->dev_addr, adapter->mac_address); + return 0; } @@ -2817,6 +2820,7 @@ static int lan743x_pcidev_probe(struct pci_dev *pdev, { struct lan743x_adapter *adapter = NULL; struct net_device *netdev = NULL; + const void *mac_addr; int ret = -ENODEV; netdev = devm_alloc_etherdev(&pdev->dev, @@ -2833,6 +2837,10 @@ static int lan743x_pcidev_probe(struct pci_dev *pdev, NETIF_MSG_IFDOWN | NETIF_MSG_TX_QUEUED; netdev->max_mtu = LAN743X_MAX_FRAME_SIZE; + mac_addr = of_get_mac_address(pdev->dev.of_node); + if (!IS_ERR(mac_addr)) + ether_addr_copy(adapter->mac_address, mac_addr); + ret = lan743x_pci_init(adapter, pdev); if (ret) goto return_error; diff --git a/drivers/net/ethernet/mscc/Kconfig b/drivers/net/ethernet/mscc/Kconfig index bcec0587cf61..3cfd1b629886 100644 --- a/drivers/net/ethernet/mscc/Kconfig +++ b/drivers/net/ethernet/mscc/Kconfig @@ -11,22 +11,24 @@ config NET_VENDOR_MICROSEMI if NET_VENDOR_MICROSEMI +# Users should depend on NET_SWITCHDEV, HAS_IOMEM, PHYLIB and REGMAP_MMIO +config MSCC_OCELOT_SWITCH_LIB + tristate + help + This is a hardware support library for Ocelot network switches. It is + used by switchdev as well as by DSA drivers. + config MSCC_OCELOT_SWITCH tristate "Ocelot switch driver" depends on NET_SWITCHDEV - depends on HAS_IOMEM - select PHYLIB - select REGMAP_MMIO - help - This driver supports the Ocelot network switch device. - -config MSCC_OCELOT_SWITCH_OCELOT - tristate "Ocelot switch driver on Ocelot" - depends on MSCC_OCELOT_SWITCH depends on GENERIC_PHY + depends on REGMAP_MMIO + depends on HAS_IOMEM + depends on PHYLIB depends on OF_NET + select MSCC_OCELOT_SWITCH_LIB help This driver supports the Ocelot network switch device as present on - the Ocelot SoCs. + the Ocelot SoCs (VSC7514). endif # NET_VENDOR_MICROSEMI diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile index 91b33b55054e..58f94c3d80f9 100644 --- a/drivers/net/ethernet/mscc/Makefile +++ b/drivers/net/ethernet/mscc/Makefile @@ -1,5 +1,13 @@ # SPDX-License-Identifier: (GPL-2.0 OR MIT) -obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o -mscc_ocelot_common-y := ocelot.o ocelot_io.o -mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o ocelot_ptp.o -obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o +obj-$(CONFIG_MSCC_OCELOT_SWITCH_LIB) += mscc_ocelot_switch_lib.o +mscc_ocelot_switch_lib-y := \ + ocelot.o \ + ocelot_io.o \ + ocelot_police.o \ + ocelot_vcap.o \ + ocelot_flower.o \ + ocelot_ptp.o +obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o +mscc_ocelot-y := \ + ocelot_vsc7514.o \ + ocelot_net.o diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 9cfe1fd98c30..e815aad8d85e 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -4,42 +4,13 @@ * * Copyright (c) 2017 Microsemi Corporation */ -#include <linux/etherdevice.h> -#include <linux/ethtool.h> #include <linux/if_bridge.h> -#include <linux/if_ether.h> -#include <linux/if_vlan.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/phy.h> -#include <linux/skbuff.h> -#include <linux/iopoll.h> -#include <net/arp.h> -#include <net/netevent.h> -#include <net/rtnetlink.h> -#include <net/switchdev.h> - #include "ocelot.h" -#include "ocelot_ace.h" +#include "ocelot_vcap.h" #define TABLE_UPDATE_SLEEP_US 10 #define TABLE_UPDATE_TIMEOUT_US 100000 -/* MAC table entry types. - * ENTRYTYPE_NORMAL is subject to aging. - * ENTRYTYPE_LOCKED is not subject to aging. - * ENTRYTYPE_MACv4 is not subject to aging. For IPv4 multicast. - * ENTRYTYPE_MACv6 is not subject to aging. For IPv6 multicast. - */ -enum macaccess_entry_type { - ENTRYTYPE_NORMAL = 0, - ENTRYTYPE_LOCKED, - ENTRYTYPE_MACv4, - ENTRYTYPE_MACv6, -}; - struct ocelot_mact_entry { u8 mac[ETH_ALEN]; u16 vid; @@ -84,10 +55,9 @@ static void ocelot_mact_select(struct ocelot *ocelot, } -static int ocelot_mact_learn(struct ocelot *ocelot, int port, - const unsigned char mac[ETH_ALEN], - unsigned int vid, - enum macaccess_entry_type type) +int ocelot_mact_learn(struct ocelot *ocelot, int port, + const unsigned char mac[ETH_ALEN], + unsigned int vid, enum macaccess_entry_type type) { ocelot_mact_select(ocelot, mac, vid); @@ -100,10 +70,10 @@ static int ocelot_mact_learn(struct ocelot *ocelot, int port, return ocelot_mact_wait_for_completion(ocelot); } +EXPORT_SYMBOL(ocelot_mact_learn); -static int ocelot_mact_forget(struct ocelot *ocelot, - const unsigned char mac[ETH_ALEN], - unsigned int vid) +int ocelot_mact_forget(struct ocelot *ocelot, + const unsigned char mac[ETH_ALEN], unsigned int vid) { ocelot_mact_select(ocelot, mac, vid); @@ -114,6 +84,7 @@ static int ocelot_mact_forget(struct ocelot *ocelot, return ocelot_mact_wait_for_completion(ocelot); } +EXPORT_SYMBOL(ocelot_mact_forget); static void ocelot_mact_init(struct ocelot *ocelot) { @@ -168,20 +139,6 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask) return ocelot_vlant_wait_for_completion(ocelot); } -static void ocelot_vlan_mode(struct ocelot *ocelot, int port, - netdev_features_t features) -{ - u32 val; - - /* Filtering */ - val = ocelot_read(ocelot, ANA_VLANMASK); - if (features & NETIF_F_HW_VLAN_CTAG_FILTER) - val |= BIT(port); - else - val &= ~BIT(port); - ocelot_write(ocelot, val, ANA_VLANMASK); -} - static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port, u16 vid) { @@ -295,26 +252,6 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, } EXPORT_SYMBOL(ocelot_vlan_add); -static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid, - bool untagged) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; - int port = priv->chip_port; - int ret; - - ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged); - if (ret) - return ret; - - /* Add the port MAC address to with the right VLAN information */ - ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, vid, - ENTRYTYPE_LOCKED); - - return 0; -} - int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid) { struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -338,30 +275,6 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid) } EXPORT_SYMBOL(ocelot_vlan_del); -static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - 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; - - ret = ocelot_vlan_del(ocelot, port, vid); - if (ret) - return ret; - - /* Del the port MAC address to with the right VLAN information */ - ocelot_mact_forget(ocelot, dev->dev_addr, vid); - - return 0; -} - static void ocelot_vlan_init(struct ocelot *ocelot) { u16 port, vid; @@ -492,15 +405,6 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_adjust_link); -static void ocelot_port_adjust_link(struct net_device *dev) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - ocelot_adjust_link(ocelot, port, dev->phydev); -} - void ocelot_port_enable(struct ocelot *ocelot, int port, struct phy_device *phy) { @@ -514,40 +418,6 @@ void ocelot_port_enable(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_port_enable); -static int ocelot_port_open(struct net_device *dev) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; - int port = priv->chip_port; - int err; - - if (priv->serdes) { - err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET, - ocelot_port->phy_mode); - if (err) { - netdev_err(dev, "Could not set mode of SerDes\n"); - return err; - } - } - - err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link, - ocelot_port->phy_mode); - if (err) { - netdev_err(dev, "Could not attach to PHY\n"); - return err; - } - - dev->phydev = priv->phy; - - phy_attached_info(priv->phy); - phy_start(priv->phy); - - ocelot_port_enable(ocelot, port, priv->phy); - - return 0; -} - void ocelot_port_disable(struct ocelot *ocelot, int port) { struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -558,41 +428,6 @@ void ocelot_port_disable(struct ocelot *ocelot, int port) } EXPORT_SYMBOL(ocelot_port_disable); -static int ocelot_port_stop(struct net_device *dev) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - phy_disconnect(priv->phy); - - dev->phydev = NULL; - - ocelot_port_disable(ocelot, port); - - return 0; -} - -/* Generate the IFH for frame injection - * - * The IFH is a 128bit-value - * bit 127: bypass the analyzer processing - * bit 56-67: destination mask - * bit 28-29: pop_cnt: 3 disables all rewriting of the frame - * bit 20-27: cpu extraction queue mask - * bit 16: tag type 0: C-tag, 1: S-tag - * bit 0-11: VID - */ -static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info) -{ - ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21); - ifh[1] = (0xf00 & info->port) >> 8; - ifh[2] = (0xff & info->port) << 24; - ifh[3] = (info->tag_type << 16) | info->vid; - - return 0; -} - int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, struct sk_buff *skb) { @@ -611,77 +446,6 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, } EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb); -static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct skb_shared_info *shinfo = skb_shinfo(skb); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; - u32 val, ifh[OCELOT_TAG_LEN / 4]; - struct frame_info info = {}; - u8 grp = 0; /* Send everything on CPU group 0 */ - unsigned int i, count, last; - int port = priv->chip_port; - - val = ocelot_read(ocelot, QS_INJ_STATUS); - if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) || - (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))) - return NETDEV_TX_BUSY; - - ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | - QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp); - - info.port = BIT(port); - info.tag_type = IFH_TAG_TYPE_C; - info.vid = skb_vlan_tag_get(skb); - - /* Check if timestamping is needed */ - if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) { - info.rew_op = ocelot_port->ptp_cmd; - if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) - info.rew_op |= (ocelot_port->ts_id % 4) << 3; - } - - ocelot_gen_ifh(ifh, &info); - - for (i = 0; i < OCELOT_TAG_LEN / 4; i++) - ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]), - QS_INJ_WR, grp); - - count = (skb->len + 3) / 4; - last = skb->len % 4; - for (i = 0; i < count; i++) { - ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp); - } - - /* Add padding */ - while (i < (OCELOT_BUFFER_CELL_SZ / 4)) { - ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp); - i++; - } - - /* Indicate EOF and valid bytes in last word */ - ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | - QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) | - QS_INJ_CTRL_EOF, - QS_INJ_CTRL, grp); - - /* Add dummy CRC */ - ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp); - skb_tx_timestamp(skb); - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) { - ocelot_port->ts_id++; - return NETDEV_TX_OK; - } - - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; -} - static void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts) { @@ -767,117 +531,14 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) } EXPORT_SYMBOL(ocelot_get_txtstamp); -static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; - - return ocelot_mact_forget(ocelot, addr, ocelot_port->pvid); -} - -static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; - - return ocelot_mact_learn(ocelot, PGID_CPU, addr, ocelot_port->pvid, - ENTRYTYPE_LOCKED); -} - -static void ocelot_set_rx_mode(struct net_device *dev) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - u32 val; - int i; - - /* This doesn't handle promiscuous mode because the bridge core is - * setting IFF_PROMISC on all slave interfaces and all frames would be - * forwarded to the CPU port. - */ - val = GENMASK(ocelot->num_phys_ports - 1, 0); - for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++) - ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i); - - __dev_mc_sync(dev, ocelot_mc_sync, ocelot_mc_unsync); -} - -static int ocelot_port_get_phys_port_name(struct net_device *dev, - char *buf, size_t len) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - int port = priv->chip_port; - int ret; - - ret = snprintf(buf, len, "p%d", port); - if (ret >= len) - return -EINVAL; - - return 0; -} - -static int ocelot_port_set_mac_address(struct net_device *dev, void *p) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; - const struct sockaddr *addr = p; - - /* Learn the new net device MAC address in the mac table. */ - ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid, - ENTRYTYPE_LOCKED); - /* Then forget the previous one. */ - ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid); - - ether_addr_copy(dev->dev_addr, addr->sa_data); - return 0; -} - -static void ocelot_get_stats64(struct net_device *dev, - struct rtnl_link_stats64 *stats) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - /* Configure the port to read the stats from */ - ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), - SYS_STAT_CFG); - - /* Get Rx stats */ - stats->rx_bytes = ocelot_read(ocelot, SYS_COUNT_RX_OCTETS); - stats->rx_packets = ocelot_read(ocelot, SYS_COUNT_RX_SHORTS) + - ocelot_read(ocelot, SYS_COUNT_RX_FRAGMENTS) + - ocelot_read(ocelot, SYS_COUNT_RX_JABBERS) + - ocelot_read(ocelot, SYS_COUNT_RX_LONGS) + - ocelot_read(ocelot, SYS_COUNT_RX_64) + - ocelot_read(ocelot, SYS_COUNT_RX_65_127) + - ocelot_read(ocelot, SYS_COUNT_RX_128_255) + - ocelot_read(ocelot, SYS_COUNT_RX_256_1023) + - ocelot_read(ocelot, SYS_COUNT_RX_1024_1526) + - ocelot_read(ocelot, SYS_COUNT_RX_1527_MAX); - stats->multicast = ocelot_read(ocelot, SYS_COUNT_RX_MULTICAST); - stats->rx_dropped = dev->stats.rx_dropped; - - /* Get Tx stats */ - stats->tx_bytes = ocelot_read(ocelot, SYS_COUNT_TX_OCTETS); - stats->tx_packets = ocelot_read(ocelot, SYS_COUNT_TX_64) + - ocelot_read(ocelot, SYS_COUNT_TX_65_127) + - ocelot_read(ocelot, SYS_COUNT_TX_128_511) + - ocelot_read(ocelot, SYS_COUNT_TX_512_1023) + - ocelot_read(ocelot, SYS_COUNT_TX_1024_1526) + - ocelot_read(ocelot, SYS_COUNT_TX_1527_MAX); - stats->tx_dropped = ocelot_read(ocelot, SYS_COUNT_TX_DROPS) + - ocelot_read(ocelot, SYS_COUNT_TX_AGING); - stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION); -} - int ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr, u16 vid) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + int pgid = port; + + if (port == ocelot->npi) + pgid = PGID_CPU; if (!vid) { if (!ocelot_port->vlan_aware) @@ -893,23 +554,10 @@ int ocelot_fdb_add(struct ocelot *ocelot, int port, return -EINVAL; } - return ocelot_mact_learn(ocelot, port, addr, vid, ENTRYTYPE_LOCKED); + return ocelot_mact_learn(ocelot, pgid, addr, vid, ENTRYTYPE_LOCKED); } EXPORT_SYMBOL(ocelot_fdb_add); -static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, - u16 vid, u16 flags, - struct netlink_ext_ack *extack) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - return ocelot_fdb_add(ocelot, port, addr, vid); -} - int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr, u16 vid) { @@ -917,26 +565,8 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_fdb_del); -static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, u16 vid) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - return ocelot_fdb_del(ocelot, port, addr, vid); -} - -struct ocelot_dump_ctx { - struct net_device *dev; - struct sk_buff *skb; - struct netlink_callback *cb; - int idx; -}; - -static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, - bool is_static, void *data) +int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, + bool is_static, void *data) { struct ocelot_dump_ctx *dump = data; u32 portid = NETLINK_CB(dump->cb->skb).portid; @@ -977,6 +607,7 @@ nla_put_failure: nlmsg_cancel(dump->skb, nlh); return -EMSGSIZE; } +EXPORT_SYMBOL(ocelot_port_fdb_do_dump); static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col, struct ocelot_mact_entry *entry) @@ -1058,74 +689,6 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_fdb_dump); -static int ocelot_port_fdb_dump(struct sk_buff *skb, - struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, int *idx) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - struct ocelot_dump_ctx dump = { - .dev = dev, - .skb = skb, - .cb = cb, - .idx = *idx, - }; - int port = priv->chip_port; - int ret; - - ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump); - - *idx = dump.idx; - - return ret; -} - -static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto, - u16 vid) -{ - return ocelot_vlan_vid_add(dev, vid, false, false); -} - -static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, - u16 vid) -{ - return ocelot_vlan_vid_del(dev, vid); -} - -static int ocelot_set_features(struct net_device *dev, - netdev_features_t features) -{ - netdev_features_t changed = dev->features ^ features; - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) && - priv->tc.offload_cnt) { - netdev_err(dev, - "Cannot disable HW TC offload while offloads active\n"); - return -EBUSY; - } - - if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) - ocelot_vlan_mode(ocelot, port, features); - - return 0; -} - -static int ocelot_get_port_parent_id(struct net_device *dev, - struct netdev_phys_item_id *ppid) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - - ppid->id_len = sizeof(ocelot->base_mac); - memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len); - - return 0; -} - int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr) { return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, @@ -1198,46 +761,6 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) } EXPORT_SYMBOL(ocelot_hwstamp_set); -static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - /* If the attached PHY device isn't capable of timestamping operations, - * use our own (when possible). - */ - if (!phy_has_hwtstamp(dev->phydev) && ocelot->ptp) { - switch (cmd) { - case SIOCSHWTSTAMP: - return ocelot_hwstamp_set(ocelot, port, ifr); - case SIOCGHWTSTAMP: - return ocelot_hwstamp_get(ocelot, port, ifr); - } - } - - return phy_mii_ioctl(dev->phydev, ifr, cmd); -} - -static const struct net_device_ops ocelot_port_netdev_ops = { - .ndo_open = ocelot_port_open, - .ndo_stop = ocelot_port_stop, - .ndo_start_xmit = ocelot_port_xmit, - .ndo_set_rx_mode = ocelot_set_rx_mode, - .ndo_get_phys_port_name = ocelot_port_get_phys_port_name, - .ndo_set_mac_address = ocelot_port_set_mac_address, - .ndo_get_stats64 = ocelot_get_stats64, - .ndo_fdb_add = ocelot_port_fdb_add, - .ndo_fdb_del = ocelot_port_fdb_del, - .ndo_fdb_dump = ocelot_port_fdb_dump, - .ndo_vlan_rx_add_vid = ocelot_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid, - .ndo_set_features = ocelot_set_features, - .ndo_get_port_parent_id = ocelot_get_port_parent_id, - .ndo_setup_tc = ocelot_setup_tc, - .ndo_do_ioctl = ocelot_ioctl, -}; - void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) { int i; @@ -1251,16 +774,6 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) } EXPORT_SYMBOL(ocelot_get_strings); -static void ocelot_port_get_strings(struct net_device *netdev, u32 sset, - u8 *data) -{ - struct ocelot_port_private *priv = netdev_priv(netdev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - ocelot_get_strings(ocelot, port, sset, data); -} - static void ocelot_update_stats(struct ocelot *ocelot) { int i, j; @@ -1314,17 +827,6 @@ void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) } EXPORT_SYMBOL(ocelot_get_ethtool_stats); -static void ocelot_port_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, - u64 *data) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - ocelot_get_ethtool_stats(ocelot, port, data); -} - int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) { if (sset != ETH_SS_STATS) @@ -1334,15 +836,6 @@ int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) } EXPORT_SYMBOL(ocelot_get_sset_count); -static int ocelot_port_get_sset_count(struct net_device *dev, int sset) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - return ocelot_get_sset_count(ocelot, port, sset); -} - int ocelot_get_ts_info(struct ocelot *ocelot, int port, struct ethtool_ts_info *info) { @@ -1368,28 +861,6 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_get_ts_info); -static int ocelot_port_get_ts_info(struct net_device *dev, - struct ethtool_ts_info *info) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - if (!ocelot->ptp) - return ethtool_op_get_ts_info(dev, info); - - return ocelot_get_ts_info(ocelot, port, info); -} - -static const struct ethtool_ops ocelot_ethtool_ops = { - .get_strings = ocelot_port_get_strings, - .get_ethtool_stats = ocelot_port_get_ethtool_stats, - .get_sset_count = ocelot_port_get_sset_count, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, - .get_ts_info = ocelot_port_get_ts_info, -}; - void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) { u32 port_cfg; @@ -1445,16 +916,6 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) } EXPORT_SYMBOL(ocelot_bridge_stp_state_set); -static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port, - struct switchdev_trans *trans, - u8 state) -{ - if (switchdev_trans_ph_prepare(trans)) - return; - - ocelot_bridge_stp_state_set(ocelot, port, state); -} - void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs) { unsigned int age_period = ANA_AUTOAGE_AGE_PERIOD(msecs / 2000); @@ -1469,165 +930,142 @@ void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs) } EXPORT_SYMBOL(ocelot_set_ageing_time); -static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int 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); - - ocelot_set_ageing_time(ocelot, ageing_time); -} - -static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc) +static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot, + const unsigned char *addr, + u16 vid) { - u32 cpu_fwd_mcast = ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA | - ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA | - ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA; - u32 val = 0; + struct ocelot_multicast *mc; - if (mc) - val = cpu_fwd_mcast; + list_for_each_entry(mc, &ocelot->multicast, list) { + if (ether_addr_equal(mc->addr, addr) && mc->vid == vid) + return mc; + } - ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast, - ANA_PORT_CPU_FWD_CFG, port); + return NULL; } -static int ocelot_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr, - struct switchdev_trans *trans) +static enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr) { - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - int err = 0; - - switch (attr->id) { - case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - ocelot_port_attr_stp_state_set(ocelot, port, trans, - attr->u.stp_state); - break; - case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: - ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time); - break; - case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: - ocelot_port_vlan_filtering(ocelot, port, - attr->u.vlan_filtering); - break; - case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: - ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled); - break; - default: - err = -EOPNOTSUPP; - break; - } - - return err; + if (addr[0] == 0x01 && addr[1] == 0x00 && addr[2] == 0x5e) + return ENTRYTYPE_MACv4; + if (addr[0] == 0x33 && addr[1] == 0x33) + return ENTRYTYPE_MACv6; + return ENTRYTYPE_NORMAL; } -static int ocelot_port_obj_add_vlan(struct net_device *dev, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) +static int ocelot_mdb_get_pgid(struct ocelot *ocelot, + enum macaccess_entry_type entry_type) { - int ret; - u16 vid; + int pgid; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - ret = ocelot_vlan_vid_add(dev, vid, - vlan->flags & BRIDGE_VLAN_INFO_PVID, - vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); - if (ret) - return ret; - } - - return 0; -} + /* According to VSC7514 datasheet 3.9.1.5 IPv4 Multicast Entries and + * 3.9.1.6 IPv6 Multicast Entries, "Instead of a lookup in the + * destination mask table (PGID), the destination set is programmed as + * part of the entry MAC address.", and the DEST_IDX is set to 0. + */ + if (entry_type == ENTRYTYPE_MACv4 || + entry_type == ENTRYTYPE_MACv6) + return 0; -static int ocelot_port_vlan_del_vlan(struct net_device *dev, - const struct switchdev_obj_port_vlan *vlan) -{ - int ret; - u16 vid; + for_each_nonreserved_multicast_dest_pgid(ocelot, pgid) { + struct ocelot_multicast *mc; + bool used = false; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - ret = ocelot_vlan_vid_del(dev, vid); + list_for_each_entry(mc, &ocelot->multicast, list) { + if (mc->pgid == pgid) { + used = true; + break; + } + } - if (ret) - return ret; + if (!used) + return pgid; } - return 0; + return -1; } -static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot, - const unsigned char *addr, - u16 vid) +static void ocelot_encode_ports_to_mdb(unsigned char *addr, + struct ocelot_multicast *mc, + enum macaccess_entry_type entry_type) { - struct ocelot_multicast *mc; + memcpy(addr, mc->addr, ETH_ALEN); - list_for_each_entry(mc, &ocelot->multicast, list) { - if (ether_addr_equal(mc->addr, addr) && mc->vid == vid) - return mc; + if (entry_type == ENTRYTYPE_MACv4) { + addr[0] = 0; + addr[1] = mc->ports >> 8; + addr[2] = mc->ports & 0xff; + } else if (entry_type == ENTRYTYPE_MACv6) { + addr[0] = mc->ports >> 8; + addr[1] = mc->ports & 0xff; } - - return NULL; } -static int ocelot_port_obj_add_mdb(struct net_device *dev, - const struct switchdev_obj_port_mdb *mdb, - struct switchdev_trans *trans) +int ocelot_port_mdb_add(struct ocelot *ocelot, int port, + const struct switchdev_obj_port_mdb *mdb) { - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + enum macaccess_entry_type entry_type; unsigned char addr[ETH_ALEN]; struct ocelot_multicast *mc; - int port = priv->chip_port; u16 vid = mdb->vid; bool new = false; + if (port == ocelot->npi) + port = ocelot->num_phys_ports; + if (!vid) vid = ocelot_port->pvid; + entry_type = ocelot_classify_mdb(mdb->addr); + mc = ocelot_multicast_get(ocelot, mdb->addr, vid); if (!mc) { + int pgid = ocelot_mdb_get_pgid(ocelot, entry_type); + + if (pgid < 0) { + dev_err(ocelot->dev, + "No more PGIDs available for mdb %pM vid %d\n", + mdb->addr, vid); + return -ENOSPC; + } + mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; memcpy(mc->addr, mdb->addr, ETH_ALEN); mc->vid = vid; + mc->pgid = pgid; list_add_tail(&mc->list, &ocelot->multicast); new = true; } - memcpy(addr, mc->addr, ETH_ALEN); - addr[0] = 0; - if (!new) { - addr[2] = mc->ports << 0; - addr[1] = mc->ports << 8; + ocelot_encode_ports_to_mdb(addr, mc, entry_type); ocelot_mact_forget(ocelot, addr, vid); } mc->ports |= BIT(port); - addr[2] = mc->ports << 0; - addr[1] = mc->ports << 8; + ocelot_encode_ports_to_mdb(addr, mc, entry_type); - return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4); + return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type); } +EXPORT_SYMBOL(ocelot_port_mdb_add); -static int ocelot_port_obj_del_mdb(struct net_device *dev, - const struct switchdev_obj_port_mdb *mdb) +int ocelot_port_mdb_del(struct ocelot *ocelot, int port, + const struct switchdev_obj_port_mdb *mdb) { - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + enum macaccess_entry_type entry_type; unsigned char addr[ETH_ALEN]; struct ocelot_multicast *mc; - int port = priv->chip_port; u16 vid = mdb->vid; + if (port == ocelot->npi) + port = ocelot->num_phys_ports; + if (!vid) vid = ocelot_port->pvid; @@ -1635,10 +1073,9 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, if (!mc) return -ENOENT; - memcpy(addr, mc->addr, ETH_ALEN); - addr[2] = mc->ports << 0; - addr[1] = mc->ports << 8; - addr[0] = 0; + entry_type = ocelot_classify_mdb(mdb->addr); + + ocelot_encode_ports_to_mdb(addr, mc, entry_type); ocelot_mact_forget(ocelot, addr, vid); mc->ports &= ~BIT(port); @@ -1648,55 +1085,11 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, return 0; } - addr[2] = mc->ports << 0; - addr[1] = mc->ports << 8; - - return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4); -} - -static int ocelot_port_obj_add(struct net_device *dev, - const struct switchdev_obj *obj, - struct switchdev_trans *trans, - struct netlink_ext_ack *extack) -{ - int ret = 0; - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_VLAN: - ret = ocelot_port_obj_add_vlan(dev, - SWITCHDEV_OBJ_PORT_VLAN(obj), - trans); - break; - case SWITCHDEV_OBJ_ID_PORT_MDB: - ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj), - trans); - break; - default: - return -EOPNOTSUPP; - } - - return ret; -} - -static int ocelot_port_obj_del(struct net_device *dev, - const struct switchdev_obj *obj) -{ - int ret = 0; + ocelot_encode_ports_to_mdb(addr, mc, entry_type); - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_VLAN: - ret = ocelot_port_vlan_del_vlan(dev, - SWITCHDEV_OBJ_PORT_VLAN(obj)); - break; - case SWITCHDEV_OBJ_ID_PORT_MDB: - ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj)); - break; - default: - return -EOPNOTSUPP; - } - - return ret; + return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type); } +EXPORT_SYMBOL(ocelot_port_mdb_del); int ocelot_port_bridge_join(struct ocelot *ocelot, int port, struct net_device *bridge) @@ -1735,10 +1128,10 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot) int i, port, lag; /* Reset destination and aggregation PGIDS */ - for (port = 0; port < ocelot->num_phys_ports; port++) + for_each_unicast_dest_pgid(ocelot, port) ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port); - for (i = PGID_AGGR; i < PGID_SRC; i++) + for_each_aggr_pgid(ocelot, i) ocelot_write_rix(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0), ANA_PGID_PGID, i); @@ -1760,7 +1153,7 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot) aggr_count++; } - for (i = PGID_AGGR; i < PGID_SRC; i++) { + for_each_aggr_pgid(ocelot, i) { u32 ac; ac = ocelot_read_rix(ocelot, ANA_PGID_PGID, i); @@ -1788,8 +1181,8 @@ static void ocelot_setup_lag(struct ocelot *ocelot, int lag) } } -static int ocelot_port_lag_join(struct ocelot *ocelot, int port, - struct net_device *bond) +int ocelot_port_lag_join(struct ocelot *ocelot, int port, + struct net_device *bond) { struct net_device *ndev; u32 bond_mask = 0; @@ -1826,9 +1219,10 @@ static int ocelot_port_lag_join(struct ocelot *ocelot, int port, return 0; } +EXPORT_SYMBOL(ocelot_port_lag_join); -static void ocelot_port_lag_leave(struct ocelot *ocelot, int port, - struct net_device *bond) +void ocelot_port_lag_leave(struct ocelot *ocelot, int port, + struct net_device *bond) { u32 port_cfg; int i; @@ -1856,151 +1250,7 @@ static void ocelot_port_lag_leave(struct ocelot *ocelot, int port, ocelot_set_aggr_pgids(ocelot); } - -/* Checks if the net_device instance given to us originate from our driver. */ -static bool ocelot_netdevice_dev_check(const struct net_device *dev) -{ - return dev->netdev_ops == &ocelot_port_netdev_ops; -} - -static int ocelot_netdevice_port_event(struct net_device *dev, - unsigned long event, - struct netdev_notifier_changeupper_info *info) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot_port *ocelot_port = &priv->port; - struct ocelot *ocelot = ocelot_port->ocelot; - int port = priv->chip_port; - int err = 0; - - switch (event) { - case NETDEV_CHANGEUPPER: - if (netif_is_bridge_master(info->upper_dev)) { - if (info->linking) { - err = ocelot_port_bridge_join(ocelot, port, - info->upper_dev); - } else { - err = ocelot_port_bridge_leave(ocelot, port, - info->upper_dev); - } - } - if (netif_is_lag_master(info->upper_dev)) { - if (info->linking) - err = ocelot_port_lag_join(ocelot, port, - info->upper_dev); - else - ocelot_port_lag_leave(ocelot, port, - info->upper_dev); - } - break; - default: - break; - } - - return err; -} - -static int ocelot_netdevice_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct netdev_notifier_changeupper_info *info = ptr; - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - int ret = 0; - - if (!ocelot_netdevice_dev_check(dev)) - return 0; - - if (event == NETDEV_PRECHANGEUPPER && - netif_is_lag_master(info->upper_dev)) { - struct netdev_lag_upper_info *lag_upper_info = info->upper_info; - struct netlink_ext_ack *extack; - - if (lag_upper_info && - lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { - extack = netdev_notifier_info_to_extack(&info->info); - NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type"); - - ret = -EINVAL; - goto notify; - } - } - - if (netif_is_lag_master(dev)) { - struct net_device *slave; - struct list_head *iter; - - netdev_for_each_lower_dev(dev, slave, iter) { - ret = ocelot_netdevice_port_event(slave, event, info); - if (ret) - goto notify; - } - } else { - ret = ocelot_netdevice_port_event(dev, event, info); - } - -notify: - return notifier_from_errno(ret); -} - -struct notifier_block ocelot_netdevice_nb __read_mostly = { - .notifier_call = ocelot_netdevice_event, -}; -EXPORT_SYMBOL(ocelot_netdevice_nb); - -static int ocelot_switchdev_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct net_device *dev = switchdev_notifier_info_to_dev(ptr); - int err; - - switch (event) { - case SWITCHDEV_PORT_ATTR_SET: - err = switchdev_handle_port_attr_set(dev, ptr, - ocelot_netdevice_dev_check, - ocelot_port_attr_set); - return notifier_from_errno(err); - } - - return NOTIFY_DONE; -} - -struct notifier_block ocelot_switchdev_nb __read_mostly = { - .notifier_call = ocelot_switchdev_event, -}; -EXPORT_SYMBOL(ocelot_switchdev_nb); - -static int ocelot_switchdev_blocking_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct net_device *dev = switchdev_notifier_info_to_dev(ptr); - int err; - - switch (event) { - /* Blocking events. */ - case SWITCHDEV_PORT_OBJ_ADD: - err = switchdev_handle_port_obj_add(dev, ptr, - ocelot_netdevice_dev_check, - ocelot_port_obj_add); - return notifier_from_errno(err); - case SWITCHDEV_PORT_OBJ_DEL: - err = switchdev_handle_port_obj_del(dev, ptr, - ocelot_netdevice_dev_check, - ocelot_port_obj_del); - return notifier_from_errno(err); - case SWITCHDEV_PORT_ATTR_SET: - err = switchdev_handle_port_attr_set(dev, ptr, - ocelot_netdevice_dev_check, - ocelot_port_attr_set); - return notifier_from_errno(err); - } - - return NOTIFY_DONE; -} - -struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = { - .notifier_call = ocelot_switchdev_blocking_event, -}; -EXPORT_SYMBOL(ocelot_switchdev_blocking_nb); +EXPORT_SYMBOL(ocelot_port_lag_leave); /* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu. * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG. @@ -2109,52 +1359,6 @@ void ocelot_init_port(struct ocelot *ocelot, int port) } EXPORT_SYMBOL(ocelot_init_port); -int ocelot_probe_port(struct ocelot *ocelot, u8 port, - void __iomem *regs, - struct phy_device *phy) -{ - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; - struct net_device *dev; - int err; - - dev = alloc_etherdev(sizeof(struct ocelot_port_private)); - if (!dev) - return -ENOMEM; - SET_NETDEV_DEV(dev, ocelot->dev); - priv = netdev_priv(dev); - priv->dev = dev; - priv->phy = phy; - priv->chip_port = port; - ocelot_port = &priv->port; - ocelot_port->ocelot = ocelot; - ocelot_port->regs = regs; - ocelot->ports[port] = ocelot_port; - - dev->netdev_ops = &ocelot_port_netdev_ops; - dev->ethtool_ops = &ocelot_ethtool_ops; - - dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS | - NETIF_F_HW_TC; - dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC; - - memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN); - dev->dev_addr[ETH_ALEN - 1] += port; - ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid, - ENTRYTYPE_LOCKED); - - ocelot_init_port(ocelot, port); - - err = register_netdev(dev); - if (err) { - dev_err(ocelot->dev, "register_netdev failed\n"); - free_netdev(dev); - } - - return err; -} -EXPORT_SYMBOL(ocelot_probe_port); - /* Configure and enable the CPU port module, which is a set of queues. * If @npi contains a valid port index, the CPU port module is connected * to the Node Processor Interface (NPI). This is the mode through which @@ -2255,7 +1459,7 @@ int ocelot_init(struct ocelot *ocelot) INIT_LIST_HEAD(&ocelot->multicast); ocelot_mact_init(ocelot); ocelot_vlan_init(ocelot); - ocelot_ace_init(ocelot); + ocelot_vcap_init(ocelot); for (port = 0; port < ocelot->num_phys_ports; port++) { /* Clear all counters (5 groups) */ @@ -2311,7 +1515,7 @@ int ocelot_init(struct ocelot *ocelot) } /* Allow broadcast MAC frames. */ - for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++) { + for_each_nonreserved_multicast_dest_pgid(ocelot, i) { u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0)); ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i); diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index f0a15aa187f2..394362e23c47 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -25,7 +25,6 @@ #include <soc/mscc/ocelot.h> #include "ocelot_rew.h" #include "ocelot_qs.h" -#include "ocelot_tc.h" #define OCELOT_BUFFER_CELL_SZ 60 @@ -47,6 +46,14 @@ struct ocelot_multicast { unsigned char addr[ETH_ALEN]; u16 vid; u16 ports; + int pgid; +}; + +struct ocelot_port_tc { + bool block_shared; + unsigned long offload_cnt; + + unsigned long police_id; }; struct ocelot_port_private { @@ -60,13 +67,44 @@ struct ocelot_port_private { struct ocelot_port_tc tc; }; +struct ocelot_dump_ctx { + struct net_device *dev; + struct sk_buff *skb; + struct netlink_callback *cb; + int idx; +}; + +/* MAC table entry types. + * ENTRYTYPE_NORMAL is subject to aging. + * ENTRYTYPE_LOCKED is not subject to aging. + * ENTRYTYPE_MACv4 is not subject to aging. For IPv4 multicast. + * ENTRYTYPE_MACv6 is not subject to aging. For IPv6 multicast. + */ +enum macaccess_entry_type { + ENTRYTYPE_NORMAL = 0, + ENTRYTYPE_LOCKED, + ENTRYTYPE_MACv4, + ENTRYTYPE_MACv6, +}; + +int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, + bool is_static, void *data); +int ocelot_mact_learn(struct ocelot *ocelot, int port, + const unsigned char mac[ETH_ALEN], + unsigned int vid, enum macaccess_entry_type type); +int ocelot_mact_forget(struct ocelot *ocelot, + const unsigned char mac[ETH_ALEN], unsigned int vid); +int ocelot_port_lag_join(struct ocelot *ocelot, int port, + struct net_device *bond); +void ocelot_port_lag_leave(struct ocelot *ocelot, int port, + struct net_device *bond); + u32 ocelot_port_readl(struct ocelot_port *port, u32 reg); void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg); #define ocelot_field_write(ocelot, reg, val) regmap_field_write((ocelot)->regfields[(reg)], (val)) #define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val)) -int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops); int ocelot_probe_port(struct ocelot *ocelot, u8 port, void __iomem *regs, struct phy_device *phy); diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c deleted file mode 100644 index 4a15d2ff8b70..000000000000 --- a/drivers/net/ethernet/mscc/ocelot_board.c +++ /dev/null @@ -1,626 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* - * Microsemi Ocelot Switch driver - * - * Copyright (c) 2017 Microsemi Corporation - */ -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/of_net.h> -#include <linux/netdevice.h> -#include <linux/of_mdio.h> -#include <linux/of_platform.h> -#include <linux/mfd/syscon.h> -#include <linux/skbuff.h> -#include <net/switchdev.h> - -#include <soc/mscc/ocelot_vcap.h> -#include "ocelot.h" - -#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0)) -#define VSC7514_VCAP_IS2_CNT 64 -#define VSC7514_VCAP_IS2_ENTRY_WIDTH 376 -#define VSC7514_VCAP_IS2_ACTION_WIDTH 99 -#define VSC7514_VCAP_PORT_CNT 11 - -static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info) -{ - u8 llen, wlen; - u64 ifh[2]; - - ifh[0] = be64_to_cpu(((__force __be64 *)_ifh)[0]); - ifh[1] = be64_to_cpu(((__force __be64 *)_ifh)[1]); - - wlen = IFH_EXTRACT_BITFIELD64(ifh[0], 7, 8); - llen = IFH_EXTRACT_BITFIELD64(ifh[0], 15, 6); - - info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80; - - info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32); - - info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4); - - info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1); - info->vid = IFH_EXTRACT_BITFIELD64(ifh[1], 0, 12); - - return 0; -} - -static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh, - u32 *rval) -{ - u32 val; - u32 bytes_valid; - - val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); - if (val == XTR_NOT_READY) { - if (ifh) - return -EIO; - - do { - val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); - } while (val == XTR_NOT_READY); - } - - switch (val) { - case XTR_ABORT: - return -EIO; - case XTR_EOF_0: - case XTR_EOF_1: - case XTR_EOF_2: - case XTR_EOF_3: - case XTR_PRUNED: - bytes_valid = XTR_VALID_BYTES(val); - val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); - if (val == XTR_ESCAPE) - *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); - else - *rval = val; - - return bytes_valid; - case XTR_ESCAPE: - *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); - - return 4; - default: - *rval = val; - - return 4; - } -} - -static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) -{ - struct ocelot *ocelot = arg; - int i = 0, grp = 0; - int err = 0; - - if (!(ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))) - return IRQ_NONE; - - do { - struct skb_shared_hwtstamps *shhwtstamps; - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; - u64 tod_in_ns, full_ts_in_ns; - struct frame_info info = {}; - struct net_device *dev; - u32 ifh[4], val, *buf; - struct timespec64 ts; - int sz, len, buf_len; - struct sk_buff *skb; - - for (i = 0; i < OCELOT_TAG_LEN / 4; i++) { - err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]); - if (err != 4) - break; - } - - if (err != 4) - break; - - /* At this point the IFH was read correctly, so it is safe to - * presume that there is no error. The err needs to be reset - * otherwise a frame could come in CPU queue between the while - * condition and the check for error later on. And in that case - * the new frame is just removed and not processed. - */ - err = 0; - - ocelot_parse_ifh(ifh, &info); - - ocelot_port = ocelot->ports[info.port]; - priv = container_of(ocelot_port, struct ocelot_port_private, - port); - dev = priv->dev; - - skb = netdev_alloc_skb(dev, info.len); - - if (unlikely(!skb)) { - netdev_err(dev, "Unable to allocate sk_buff\n"); - err = -ENOMEM; - break; - } - buf_len = info.len - ETH_FCS_LEN; - buf = (u32 *)skb_put(skb, buf_len); - - len = 0; - do { - sz = ocelot_rx_frame_word(ocelot, grp, false, &val); - *buf++ = val; - len += sz; - } while (len < buf_len); - - /* Read the FCS */ - sz = ocelot_rx_frame_word(ocelot, grp, false, &val); - /* Update the statistics if part of the FCS was read before */ - len -= ETH_FCS_LEN - sz; - - if (unlikely(dev->features & NETIF_F_RXFCS)) { - buf = (u32 *)skb_put(skb, ETH_FCS_LEN); - *buf = val; - } - - if (sz < 0) { - err = sz; - break; - } - - if (ocelot->ptp) { - ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); - - tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec); - if ((tod_in_ns & 0xffffffff) < info.timestamp) - full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) | - info.timestamp; - else - full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) | - info.timestamp; - - shhwtstamps = skb_hwtstamps(skb); - memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); - shhwtstamps->hwtstamp = full_ts_in_ns; - } - - /* Everything we see on an interface that is in the HW bridge - * has already been forwarded. - */ - if (ocelot->bridge_mask & BIT(info.port)) - skb->offload_fwd_mark = 1; - - skb->protocol = eth_type_trans(skb, dev); - if (!skb_defer_rx_timestamp(skb)) - netif_rx(skb); - dev->stats.rx_bytes += len; - dev->stats.rx_packets++; - } while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)); - - if (err) - while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) - ocelot_read_rix(ocelot, QS_XTR_RD, grp); - - return IRQ_HANDLED; -} - -static irqreturn_t ocelot_ptp_rdy_irq_handler(int irq, void *arg) -{ - struct ocelot *ocelot = arg; - - ocelot_get_txtstamp(ocelot); - - return IRQ_HANDLED; -} - -static const struct of_device_id mscc_ocelot_match[] = { - { .compatible = "mscc,vsc7514-switch" }, - { } -}; -MODULE_DEVICE_TABLE(of, mscc_ocelot_match); - -static int ocelot_reset(struct ocelot *ocelot) -{ - int retries = 100; - u32 val; - - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1); - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); - - do { - msleep(1); - regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], - &val); - } while (val && --retries); - - if (!retries) - return -ETIMEDOUT; - - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); - - return 0; -} - -static const struct ocelot_ops ocelot_ops = { - .reset = ocelot_reset, -}; - -static const struct vcap_field vsc7514_vcap_is2_keys[] = { - /* Common: 46 bits */ - [VCAP_IS2_TYPE] = { 0, 4}, - [VCAP_IS2_HK_FIRST] = { 4, 1}, - [VCAP_IS2_HK_PAG] = { 5, 8}, - [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 12}, - [VCAP_IS2_HK_RSV2] = { 25, 1}, - [VCAP_IS2_HK_HOST_MATCH] = { 26, 1}, - [VCAP_IS2_HK_L2_MC] = { 27, 1}, - [VCAP_IS2_HK_L2_BC] = { 28, 1}, - [VCAP_IS2_HK_VLAN_TAGGED] = { 29, 1}, - [VCAP_IS2_HK_VID] = { 30, 12}, - [VCAP_IS2_HK_DEI] = { 42, 1}, - [VCAP_IS2_HK_PCP] = { 43, 3}, - /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ - [VCAP_IS2_HK_L2_DMAC] = { 46, 48}, - [VCAP_IS2_HK_L2_SMAC] = { 94, 48}, - /* MAC_ETYPE (TYPE=000) */ - [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {142, 16}, - [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {158, 16}, - [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {174, 8}, - [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {182, 3}, - /* MAC_LLC (TYPE=001) */ - [VCAP_IS2_HK_MAC_LLC_L2_LLC] = {142, 40}, - /* MAC_SNAP (TYPE=010) */ - [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {142, 40}, - /* MAC_ARP (TYPE=011) */ - [VCAP_IS2_HK_MAC_ARP_SMAC] = { 46, 48}, - [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 94, 1}, - [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 95, 1}, - [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 96, 1}, - [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 97, 1}, - [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 98, 1}, - [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 99, 1}, - [VCAP_IS2_HK_MAC_ARP_OPCODE] = {100, 2}, - [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = {102, 32}, - [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {134, 32}, - [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {166, 1}, - /* IP4_TCP_UDP / IP4_OTHER common */ - [VCAP_IS2_HK_IP4] = { 46, 1}, - [VCAP_IS2_HK_L3_FRAGMENT] = { 47, 1}, - [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 48, 1}, - [VCAP_IS2_HK_L3_OPTIONS] = { 49, 1}, - [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 50, 1}, - [VCAP_IS2_HK_L3_TOS] = { 51, 8}, - [VCAP_IS2_HK_L3_IP4_DIP] = { 59, 32}, - [VCAP_IS2_HK_L3_IP4_SIP] = { 91, 32}, - [VCAP_IS2_HK_DIP_EQ_SIP] = {123, 1}, - /* IP4_TCP_UDP (TYPE=100) */ - [VCAP_IS2_HK_TCP] = {124, 1}, - [VCAP_IS2_HK_L4_SPORT] = {125, 16}, - [VCAP_IS2_HK_L4_DPORT] = {141, 16}, - [VCAP_IS2_HK_L4_RNG] = {157, 8}, - [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {165, 1}, - [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {166, 1}, - [VCAP_IS2_HK_L4_URG] = {167, 1}, - [VCAP_IS2_HK_L4_ACK] = {168, 1}, - [VCAP_IS2_HK_L4_PSH] = {169, 1}, - [VCAP_IS2_HK_L4_RST] = {170, 1}, - [VCAP_IS2_HK_L4_SYN] = {171, 1}, - [VCAP_IS2_HK_L4_FIN] = {172, 1}, - [VCAP_IS2_HK_L4_1588_DOM] = {173, 8}, - [VCAP_IS2_HK_L4_1588_VER] = {181, 4}, - /* IP4_OTHER (TYPE=101) */ - [VCAP_IS2_HK_IP4_L3_PROTO] = {124, 8}, - [VCAP_IS2_HK_L3_PAYLOAD] = {132, 56}, - /* IP6_STD (TYPE=110) */ - [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 46, 1}, - [VCAP_IS2_HK_L3_IP6_SIP] = { 47, 128}, - [VCAP_IS2_HK_IP6_L3_PROTO] = {175, 8}, - /* OAM (TYPE=111) */ - [VCAP_IS2_HK_OAM_MEL_FLAGS] = {142, 7}, - [VCAP_IS2_HK_OAM_VER] = {149, 5}, - [VCAP_IS2_HK_OAM_OPCODE] = {154, 8}, - [VCAP_IS2_HK_OAM_FLAGS] = {162, 8}, - [VCAP_IS2_HK_OAM_MEPID] = {170, 16}, - [VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = {186, 1}, - [VCAP_IS2_HK_OAM_IS_Y1731] = {187, 1}, -}; - -static const struct vcap_field vsc7514_vcap_is2_actions[] = { - [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, - [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, - [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, - [VCAP_IS2_ACT_MASK_MODE] = { 5, 2}, - [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1}, - [VCAP_IS2_ACT_LRN_DIS] = { 8, 1}, - [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1}, - [VCAP_IS2_ACT_POLICE_IDX] = { 10, 9}, - [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1}, - [VCAP_IS2_ACT_PORT_MASK] = { 20, 11}, - [VCAP_IS2_ACT_REW_OP] = { 31, 9}, - [VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 40, 1}, - [VCAP_IS2_ACT_RSV] = { 41, 2}, - [VCAP_IS2_ACT_ACL_ID] = { 43, 6}, - [VCAP_IS2_ACT_HIT_CNT] = { 49, 32}, -}; - -static const struct vcap_props vsc7514_vcap_props[] = { - [VCAP_IS2] = { - .tg_width = 2, - .sw_count = 4, - .entry_count = VSC7514_VCAP_IS2_CNT, - .entry_width = VSC7514_VCAP_IS2_ENTRY_WIDTH, - .action_count = VSC7514_VCAP_IS2_CNT + - VSC7514_VCAP_PORT_CNT + 2, - .action_width = 99, - .action_type_width = 1, - .action_table = { - [IS2_ACTION_TYPE_NORMAL] = { - .width = 49, - .count = 2 - }, - [IS2_ACTION_TYPE_SMAC_SIP] = { - .width = 6, - .count = 4 - }, - }, - .counter_words = 4, - .counter_width = 32, - }, -}; - -static struct ptp_clock_info ocelot_ptp_clock_info = { - .owner = THIS_MODULE, - .name = "ocelot ptp", - .max_adj = 0x7fffffff, - .n_alarm = 0, - .n_ext_ts = 0, - .n_per_out = OCELOT_PTP_PINS_NUM, - .n_pins = OCELOT_PTP_PINS_NUM, - .pps = 0, - .gettime64 = ocelot_ptp_gettime64, - .settime64 = ocelot_ptp_settime64, - .adjtime = ocelot_ptp_adjtime, - .adjfine = ocelot_ptp_adjfine, - .verify = ocelot_ptp_verify, - .enable = ocelot_ptp_enable, -}; - -static int mscc_ocelot_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct device_node *ports, *portnp; - int err, irq_xtr, irq_ptp_rdy; - struct ocelot *ocelot; - struct regmap *hsio; - unsigned int i; - - struct { - enum ocelot_target id; - char *name; - u8 optional:1; - } io_target[] = { - { SYS, "sys" }, - { REW, "rew" }, - { QSYS, "qsys" }, - { ANA, "ana" }, - { QS, "qs" }, - { S2, "s2" }, - { PTP, "ptp", 1 }, - }; - - if (!np && !pdev->dev.platform_data) - return -ENODEV; - - ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL); - if (!ocelot) - return -ENOMEM; - - platform_set_drvdata(pdev, ocelot); - ocelot->dev = &pdev->dev; - - for (i = 0; i < ARRAY_SIZE(io_target); i++) { - struct regmap *target; - struct resource *res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - io_target[i].name); - - target = ocelot_regmap_init(ocelot, res); - if (IS_ERR(target)) { - if (io_target[i].optional) { - ocelot->targets[io_target[i].id] = NULL; - continue; - } - return PTR_ERR(target); - } - - ocelot->targets[io_target[i].id] = target; - } - - hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio"); - if (IS_ERR(hsio)) { - dev_err(&pdev->dev, "missing hsio syscon\n"); - return PTR_ERR(hsio); - } - - ocelot->targets[HSIO] = hsio; - - err = ocelot_chip_init(ocelot, &ocelot_ops); - if (err) - return err; - - irq_xtr = platform_get_irq_byname(pdev, "xtr"); - if (irq_xtr < 0) - return -ENODEV; - - err = devm_request_threaded_irq(&pdev->dev, irq_xtr, NULL, - ocelot_xtr_irq_handler, IRQF_ONESHOT, - "frame extraction", ocelot); - if (err) - return err; - - irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy"); - if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) { - err = devm_request_threaded_irq(&pdev->dev, irq_ptp_rdy, NULL, - ocelot_ptp_rdy_irq_handler, - IRQF_ONESHOT, "ptp ready", - ocelot); - if (err) - return err; - - /* Both the PTP interrupt and the PTP bank are available */ - ocelot->ptp = 1; - } - - ports = of_get_child_by_name(np, "ethernet-ports"); - if (!ports) { - dev_err(&pdev->dev, "no ethernet-ports child node found\n"); - return -ENODEV; - } - - ocelot->num_phys_ports = of_get_child_count(ports); - - ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports, - sizeof(struct ocelot_port *), GFP_KERNEL); - - ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys; - ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions; - ocelot->vcap = vsc7514_vcap_props; - - ocelot_init(ocelot); - if (ocelot->ptp) { - err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info); - if (err) { - dev_err(ocelot->dev, - "Timestamp initialization failed\n"); - ocelot->ptp = 0; - } - } - - /* No NPI port */ - ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE, - OCELOT_TAG_PREFIX_NONE); - - for_each_available_child_of_node(ports, portnp) { - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; - struct device_node *phy_node; - phy_interface_t phy_mode; - struct phy_device *phy; - struct resource *res; - struct phy *serdes; - void __iomem *regs; - char res_name[8]; - u32 port; - - if (of_property_read_u32(portnp, "reg", &port)) - continue; - - snprintf(res_name, sizeof(res_name), "port%d", port); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - res_name); - regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(regs)) - continue; - - phy_node = of_parse_phandle(portnp, "phy-handle", 0); - if (!phy_node) - continue; - - phy = of_phy_find_device(phy_node); - of_node_put(phy_node); - if (!phy) - continue; - - err = ocelot_probe_port(ocelot, port, regs, phy); - if (err) { - of_node_put(portnp); - goto out_put_ports; - } - - ocelot_port = ocelot->ports[port]; - priv = container_of(ocelot_port, struct ocelot_port_private, - port); - - of_get_phy_mode(portnp, &phy_mode); - - ocelot_port->phy_mode = phy_mode; - - switch (ocelot_port->phy_mode) { - case PHY_INTERFACE_MODE_NA: - continue; - case PHY_INTERFACE_MODE_SGMII: - break; - case PHY_INTERFACE_MODE_QSGMII: - /* Ensure clock signals and speed is set on all - * QSGMII links - */ - ocelot_port_writel(ocelot_port, - DEV_CLOCK_CFG_LINK_SPEED - (OCELOT_SPEED_1000), - DEV_CLOCK_CFG); - break; - default: - dev_err(ocelot->dev, - "invalid phy mode for port%d, (Q)SGMII only\n", - port); - of_node_put(portnp); - err = -EINVAL; - goto out_put_ports; - } - - serdes = devm_of_phy_get(ocelot->dev, portnp, NULL); - if (IS_ERR(serdes)) { - err = PTR_ERR(serdes); - if (err == -EPROBE_DEFER) - dev_dbg(ocelot->dev, "deferring probe\n"); - else - dev_err(ocelot->dev, - "missing SerDes phys for port%d\n", - port); - - of_node_put(portnp); - goto out_put_ports; - } - - priv->serdes = serdes; - } - - register_netdevice_notifier(&ocelot_netdevice_nb); - register_switchdev_notifier(&ocelot_switchdev_nb); - register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); - - dev_info(&pdev->dev, "Ocelot switch probed\n"); - -out_put_ports: - of_node_put(ports); - return err; -} - -static int mscc_ocelot_remove(struct platform_device *pdev) -{ - struct ocelot *ocelot = platform_get_drvdata(pdev); - - ocelot_deinit_timestamp(ocelot); - ocelot_deinit(ocelot); - unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); - unregister_switchdev_notifier(&ocelot_switchdev_nb); - unregister_netdevice_notifier(&ocelot_netdevice_nb); - - return 0; -} - -static struct platform_driver mscc_ocelot_driver = { - .probe = mscc_ocelot_probe, - .remove = mscc_ocelot_remove, - .driver = { - .name = "ocelot-switch", - .of_match_table = mscc_ocelot_match, - }, -}; - -module_platform_driver(mscc_ocelot_driver); - -MODULE_DESCRIPTION("Microsemi Ocelot switch driver"); -MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>"); -MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index 5ce172e22b43..f2a85b06a6e7 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -6,10 +6,10 @@ #include <net/pkt_cls.h> #include <net/tc_act/tc_gact.h> -#include "ocelot_ace.h" +#include "ocelot_vcap.h" static int ocelot_flower_parse_action(struct flow_cls_offload *f, - struct ocelot_ace_rule *ace) + struct ocelot_vcap_filter *filter) { const struct flow_action_entry *a; s64 burst; @@ -26,17 +26,17 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, flow_action_for_each(i, a, &f->rule->action) { switch (a->id) { case FLOW_ACTION_DROP: - ace->action = OCELOT_ACL_ACTION_DROP; + filter->action = OCELOT_VCAP_ACTION_DROP; break; case FLOW_ACTION_TRAP: - ace->action = OCELOT_ACL_ACTION_TRAP; + filter->action = OCELOT_VCAP_ACTION_TRAP; break; case FLOW_ACTION_POLICE: - ace->action = OCELOT_ACL_ACTION_POLICE; + filter->action = OCELOT_VCAP_ACTION_POLICE; rate = a->police.rate_bytes_ps; - ace->pol.rate = div_u64(rate, 1000) * 8; + filter->pol.rate = div_u64(rate, 1000) * 8; burst = rate * PSCHED_NS2TICKS(a->police.burst); - ace->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC); + filter->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC); break; default: return -EOPNOTSUPP; @@ -47,7 +47,7 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, } static int ocelot_flower_parse(struct flow_cls_offload *f, - struct ocelot_ace_rule *ace) + struct ocelot_vcap_filter *filter) { struct flow_rule *rule = flow_cls_offload_flow_rule(f); struct flow_dissector *dissector = rule->match.dissector; @@ -88,14 +88,14 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, return -EOPNOTSUPP; flow_rule_match_eth_addrs(rule, &match); - ace->type = OCELOT_ACE_TYPE_ETYPE; - ether_addr_copy(ace->frame.etype.dmac.value, + filter->key_type = OCELOT_VCAP_KEY_ETYPE; + ether_addr_copy(filter->key.etype.dmac.value, match.key->dst); - ether_addr_copy(ace->frame.etype.smac.value, + ether_addr_copy(filter->key.etype.smac.value, match.key->src); - ether_addr_copy(ace->frame.etype.dmac.mask, + ether_addr_copy(filter->key.etype.dmac.mask, match.mask->dst); - ether_addr_copy(ace->frame.etype.smac.mask, + ether_addr_copy(filter->key.etype.smac.mask, match.mask->src); goto finished_key_parsing; } @@ -105,18 +105,18 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, flow_rule_match_basic(rule, &match); if (ntohs(match.key->n_proto) == ETH_P_IP) { - ace->type = OCELOT_ACE_TYPE_IPV4; - ace->frame.ipv4.proto.value[0] = + filter->key_type = OCELOT_VCAP_KEY_IPV4; + filter->key.ipv4.proto.value[0] = match.key->ip_proto; - ace->frame.ipv4.proto.mask[0] = + filter->key.ipv4.proto.mask[0] = match.mask->ip_proto; match_protocol = false; } if (ntohs(match.key->n_proto) == ETH_P_IPV6) { - ace->type = OCELOT_ACE_TYPE_IPV6; - ace->frame.ipv6.proto.value[0] = + filter->key_type = OCELOT_VCAP_KEY_IPV6; + filter->key.ipv6.proto.value[0] = match.key->ip_proto; - ace->frame.ipv6.proto.mask[0] = + filter->key.ipv6.proto.mask[0] = match.mask->ip_proto; match_protocol = false; } @@ -128,16 +128,16 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, u8 *tmp; flow_rule_match_ipv4_addrs(rule, &match); - tmp = &ace->frame.ipv4.sip.value.addr[0]; + tmp = &filter->key.ipv4.sip.value.addr[0]; memcpy(tmp, &match.key->src, 4); - tmp = &ace->frame.ipv4.sip.mask.addr[0]; + tmp = &filter->key.ipv4.sip.mask.addr[0]; memcpy(tmp, &match.mask->src, 4); - tmp = &ace->frame.ipv4.dip.value.addr[0]; + tmp = &filter->key.ipv4.dip.value.addr[0]; memcpy(tmp, &match.key->dst, 4); - tmp = &ace->frame.ipv4.dip.mask.addr[0]; + tmp = &filter->key.ipv4.dip.mask.addr[0]; memcpy(tmp, &match.mask->dst, 4); match_protocol = false; } @@ -151,10 +151,10 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, struct flow_match_ports match; flow_rule_match_ports(rule, &match); - ace->frame.ipv4.sport.value = ntohs(match.key->src); - ace->frame.ipv4.sport.mask = ntohs(match.mask->src); - ace->frame.ipv4.dport.value = ntohs(match.key->dst); - ace->frame.ipv4.dport.mask = ntohs(match.mask->dst); + filter->key.ipv4.sport.value = ntohs(match.key->src); + filter->key.ipv4.sport.mask = ntohs(match.mask->src); + filter->key.ipv4.dport.value = ntohs(match.key->dst); + filter->key.ipv4.dport.mask = ntohs(match.mask->dst); match_protocol = false; } @@ -162,11 +162,11 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, struct flow_match_vlan match; flow_rule_match_vlan(rule, &match); - ace->type = OCELOT_ACE_TYPE_ANY; - ace->vlan.vid.value = match.key->vlan_id; - ace->vlan.vid.mask = match.mask->vlan_id; - ace->vlan.pcp.value[0] = match.key->vlan_priority; - ace->vlan.pcp.mask[0] = match.mask->vlan_priority; + filter->key_type = OCELOT_VCAP_KEY_ANY; + filter->vlan.vid.value = match.key->vlan_id; + filter->vlan.vid.mask = match.mask->vlan_id; + filter->vlan.pcp.value[0] = match.key->vlan_priority; + filter->vlan.pcp.mask[0] = match.mask->vlan_priority; match_protocol = false; } @@ -175,99 +175,77 @@ finished_key_parsing: /* TODO: support SNAP, LLC etc */ if (proto < ETH_P_802_3_MIN) return -EOPNOTSUPP; - ace->type = OCELOT_ACE_TYPE_ETYPE; - *(u16 *)ace->frame.etype.etype.value = htons(proto); - *(u16 *)ace->frame.etype.etype.mask = 0xffff; + filter->key_type = OCELOT_VCAP_KEY_ETYPE; + *(__be16 *)filter->key.etype.etype.value = htons(proto); + *(__be16 *)filter->key.etype.etype.mask = htons(0xffff); } - /* else, a rule of type OCELOT_ACE_TYPE_ANY is implicitly added */ + /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */ - ace->prio = f->common.prio; - ace->id = f->cookie; - return ocelot_flower_parse_action(f, ace); + filter->prio = f->common.prio; + filter->id = f->cookie; + return ocelot_flower_parse_action(f, filter); } -static -struct ocelot_ace_rule *ocelot_ace_rule_create(struct ocelot *ocelot, int port, - struct flow_cls_offload *f) +static struct ocelot_vcap_filter +*ocelot_vcap_filter_create(struct ocelot *ocelot, int port, + struct flow_cls_offload *f) { - struct ocelot_ace_rule *ace; + struct ocelot_vcap_filter *filter; - ace = kzalloc(sizeof(*ace), GFP_KERNEL); - if (!ace) + filter = kzalloc(sizeof(*filter), GFP_KERNEL); + if (!filter) return NULL; - ace->ingress_port_mask = BIT(port); - return ace; + filter->ingress_port_mask = BIT(port); + return filter; } int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, struct flow_cls_offload *f, bool ingress) { - struct ocelot_ace_rule *ace; + struct ocelot_vcap_filter *filter; int ret; - ace = ocelot_ace_rule_create(ocelot, port, f); - if (!ace) + filter = ocelot_vcap_filter_create(ocelot, port, f); + if (!filter) return -ENOMEM; - ret = ocelot_flower_parse(f, ace); + ret = ocelot_flower_parse(f, filter); if (ret) { - kfree(ace); + kfree(filter); return ret; } - return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack); + return ocelot_vcap_filter_add(ocelot, filter, f->common.extack); } EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace); int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, struct flow_cls_offload *f, bool ingress) { - struct ocelot_ace_rule ace; + struct ocelot_vcap_filter filter; - ace.prio = f->common.prio; - ace.id = f->cookie; + filter.prio = f->common.prio; + filter.id = f->cookie; - return ocelot_ace_rule_offload_del(ocelot, &ace); + return ocelot_vcap_filter_del(ocelot, &filter); } EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy); int ocelot_cls_flower_stats(struct ocelot *ocelot, int port, struct flow_cls_offload *f, bool ingress) { - struct ocelot_ace_rule ace; + struct ocelot_vcap_filter filter; int ret; - ace.prio = f->common.prio; - ace.id = f->cookie; - ret = ocelot_ace_rule_stats_update(ocelot, &ace); + filter.prio = f->common.prio; + filter.id = f->cookie; + ret = ocelot_vcap_filter_stats_update(ocelot, &filter); if (ret) return ret; - flow_stats_update(&f->stats, 0x0, ace.stats.pkts, 0x0, + flow_stats_update(&f->stats, 0x0, filter.stats.pkts, 0, 0x0, FLOW_ACTION_HW_STATS_IMMEDIATE); return 0; } EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats); - -int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, - struct flow_cls_offload *f, - bool ingress) -{ - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->chip_port; - - if (!ingress) - return -EOPNOTSUPP; - - switch (f->command) { - case FLOW_CLS_REPLACE: - return ocelot_cls_flower_replace(ocelot, port, f, ingress); - case FLOW_CLS_DESTROY: - return ocelot_cls_flower_destroy(ocelot, port, f, ingress); - case FLOW_CLS_STATS: - return ocelot_cls_flower_stats(ocelot, port, f, ingress); - default: - return -EOPNOTSUPP; - } -} diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c new file mode 100644 index 000000000000..702b42543fb7 --- /dev/null +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -0,0 +1,1057 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Microsemi Ocelot Switch driver + * + * Copyright (c) 2017, 2019 Microsemi Corporation + */ + +#include <linux/if_bridge.h> +#include "ocelot.h" +#include "ocelot_vcap.h" + +int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, + struct flow_cls_offload *f, + bool ingress) +{ + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + if (!ingress) + return -EOPNOTSUPP; + + switch (f->command) { + case FLOW_CLS_REPLACE: + return ocelot_cls_flower_replace(ocelot, port, f, ingress); + case FLOW_CLS_DESTROY: + return ocelot_cls_flower_destroy(ocelot, port, f, ingress); + case FLOW_CLS_STATS: + return ocelot_cls_flower_stats(ocelot, port, f, ingress); + default: + return -EOPNOTSUPP; + } +} + +static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, + struct tc_cls_matchall_offload *f, + bool ingress) +{ + struct netlink_ext_ack *extack = f->common.extack; + struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_policer pol = { 0 }; + struct flow_action_entry *action; + int port = priv->chip_port; + int err; + + if (!ingress) { + NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); + return -EOPNOTSUPP; + } + + switch (f->command) { + case TC_CLSMATCHALL_REPLACE: + if (!flow_offload_has_one_action(&f->rule->action)) { + NL_SET_ERR_MSG_MOD(extack, + "Only one action is supported"); + return -EOPNOTSUPP; + } + + if (priv->tc.block_shared) { + NL_SET_ERR_MSG_MOD(extack, + "Rate limit is not supported on shared blocks"); + return -EOPNOTSUPP; + } + + action = &f->rule->action.entries[0]; + + if (action->id != FLOW_ACTION_POLICE) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); + return -EOPNOTSUPP; + } + + if (priv->tc.police_id && priv->tc.police_id != f->cookie) { + NL_SET_ERR_MSG_MOD(extack, + "Only one policer per port is supported"); + return -EEXIST; + } + + pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; + pol.burst = (u32)div_u64(action->police.rate_bytes_ps * + PSCHED_NS2TICKS(action->police.burst), + PSCHED_TICKS_PER_SEC); + + err = ocelot_port_policer_add(ocelot, port, &pol); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Could not add policer"); + return err; + } + + priv->tc.police_id = f->cookie; + priv->tc.offload_cnt++; + return 0; + case TC_CLSMATCHALL_DESTROY: + if (priv->tc.police_id != f->cookie) + return -ENOENT; + + err = ocelot_port_policer_del(ocelot, port); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Could not delete policer"); + return err; + } + priv->tc.police_id = 0; + priv->tc.offload_cnt--; + return 0; + case TC_CLSMATCHALL_STATS: + default: + return -EOPNOTSUPP; + } +} + +static int ocelot_setup_tc_block_cb(enum tc_setup_type type, + void *type_data, + void *cb_priv, bool ingress) +{ + struct ocelot_port_private *priv = cb_priv; + + if (!tc_cls_can_offload_and_chain0(priv->dev, type_data)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSMATCHALL: + return ocelot_setup_tc_cls_matchall(priv, type_data, ingress); + case TC_SETUP_CLSFLOWER: + return ocelot_setup_tc_cls_flower(priv, type_data, ingress); + default: + return -EOPNOTSUPP; + } +} + +static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type, + void *type_data, + void *cb_priv) +{ + return ocelot_setup_tc_block_cb(type, type_data, + cb_priv, true); +} + +static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type, + void *type_data, + void *cb_priv) +{ + return ocelot_setup_tc_block_cb(type, type_data, + cb_priv, false); +} + +static LIST_HEAD(ocelot_block_cb_list); + +static int ocelot_setup_tc_block(struct ocelot_port_private *priv, + struct flow_block_offload *f) +{ + struct flow_block_cb *block_cb; + flow_setup_cb_t *cb; + + if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) { + cb = ocelot_setup_tc_block_cb_ig; + priv->tc.block_shared = f->block_shared; + } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) { + cb = ocelot_setup_tc_block_cb_eg; + } else { + return -EOPNOTSUPP; + } + + f->driver_block_list = &ocelot_block_cb_list; + + switch (f->command) { + case FLOW_BLOCK_BIND: + if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list)) + return -EBUSY; + + block_cb = flow_block_cb_alloc(cb, priv, priv, NULL); + if (IS_ERR(block_cb)) + return PTR_ERR(block_cb); + + flow_block_cb_add(block_cb, f); + list_add_tail(&block_cb->driver_list, f->driver_block_list); + return 0; + case FLOW_BLOCK_UNBIND: + block_cb = flow_block_cb_lookup(f->block, cb, priv); + if (!block_cb) + return -ENOENT; + + flow_block_cb_remove(block_cb, f); + list_del(&block_cb->driver_list); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + + switch (type) { + case TC_SETUP_BLOCK: + return ocelot_setup_tc_block(priv, type_data); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static void ocelot_port_adjust_link(struct net_device *dev) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + ocelot_adjust_link(ocelot, port, dev->phydev); +} + +static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid, + bool untagged) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + int port = priv->chip_port; + int ret; + + ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged); + if (ret) + return ret; + + /* Add the port MAC address to with the right VLAN information */ + ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, vid, + ENTRYTYPE_LOCKED); + + return 0; +} + +static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + 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; + + ret = ocelot_vlan_del(ocelot, port, vid); + if (ret) + return ret; + + /* Del the port MAC address to with the right VLAN information */ + ocelot_mact_forget(ocelot, dev->dev_addr, vid); + + return 0; +} + +static int ocelot_port_open(struct net_device *dev) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + int port = priv->chip_port; + int err; + + if (priv->serdes) { + err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET, + ocelot_port->phy_mode); + if (err) { + netdev_err(dev, "Could not set mode of SerDes\n"); + return err; + } + } + + err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link, + ocelot_port->phy_mode); + if (err) { + netdev_err(dev, "Could not attach to PHY\n"); + return err; + } + + dev->phydev = priv->phy; + + phy_attached_info(priv->phy); + phy_start(priv->phy); + + ocelot_port_enable(ocelot, port, priv->phy); + + return 0; +} + +static int ocelot_port_stop(struct net_device *dev) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + phy_disconnect(priv->phy); + + dev->phydev = NULL; + + ocelot_port_disable(ocelot, port); + + return 0; +} + +/* Generate the IFH for frame injection + * + * The IFH is a 128bit-value + * bit 127: bypass the analyzer processing + * bit 56-67: destination mask + * bit 28-29: pop_cnt: 3 disables all rewriting of the frame + * bit 20-27: cpu extraction queue mask + * bit 16: tag type 0: C-tag, 1: S-tag + * bit 0-11: VID + */ +static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info) +{ + ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21); + ifh[1] = (0xf00 & info->port) >> 8; + ifh[2] = (0xff & info->port) << 24; + ifh[3] = (info->tag_type << 16) | info->vid; + + return 0; +} + +static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct skb_shared_info *shinfo = skb_shinfo(skb); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + u32 val, ifh[OCELOT_TAG_LEN / 4]; + struct frame_info info = {}; + u8 grp = 0; /* Send everything on CPU group 0 */ + unsigned int i, count, last; + int port = priv->chip_port; + + val = ocelot_read(ocelot, QS_INJ_STATUS); + if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) || + (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))) + return NETDEV_TX_BUSY; + + ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | + QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp); + + info.port = BIT(port); + info.tag_type = IFH_TAG_TYPE_C; + info.vid = skb_vlan_tag_get(skb); + + /* Check if timestamping is needed */ + if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) { + info.rew_op = ocelot_port->ptp_cmd; + if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) + info.rew_op |= (ocelot_port->ts_id % 4) << 3; + } + + ocelot_gen_ifh(ifh, &info); + + for (i = 0; i < OCELOT_TAG_LEN / 4; i++) + ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]), + QS_INJ_WR, grp); + + count = (skb->len + 3) / 4; + last = skb->len % 4; + for (i = 0; i < count; i++) + ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp); + + /* Add padding */ + while (i < (OCELOT_BUFFER_CELL_SZ / 4)) { + ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp); + i++; + } + + /* Indicate EOF and valid bytes in last word */ + ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | + QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) | + QS_INJ_CTRL_EOF, + QS_INJ_CTRL, grp); + + /* Add dummy CRC */ + ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp); + skb_tx_timestamp(skb); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) { + ocelot_port->ts_id++; + return NETDEV_TX_OK; + } + + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + + return ocelot_mact_forget(ocelot, addr, ocelot_port->pvid); +} + +static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + + return ocelot_mact_learn(ocelot, PGID_CPU, addr, ocelot_port->pvid, + ENTRYTYPE_LOCKED); +} + +static void ocelot_set_rx_mode(struct net_device *dev) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + u32 val; + int i; + + /* This doesn't handle promiscuous mode because the bridge core is + * setting IFF_PROMISC on all slave interfaces and all frames would be + * forwarded to the CPU port. + */ + val = GENMASK(ocelot->num_phys_ports - 1, 0); + for_each_nonreserved_multicast_dest_pgid(ocelot, i) + ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i); + + __dev_mc_sync(dev, ocelot_mc_sync, ocelot_mc_unsync); +} + +static int ocelot_port_get_phys_port_name(struct net_device *dev, + char *buf, size_t len) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + int port = priv->chip_port; + int ret; + + ret = snprintf(buf, len, "p%d", port); + if (ret >= len) + return -EINVAL; + + return 0; +} + +static int ocelot_port_set_mac_address(struct net_device *dev, void *p) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + const struct sockaddr *addr = p; + + /* Learn the new net device MAC address in the mac table. */ + ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid, + ENTRYTYPE_LOCKED); + /* Then forget the previous one. */ + ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid); + + ether_addr_copy(dev->dev_addr, addr->sa_data); + return 0; +} + +static void ocelot_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + /* Configure the port to read the stats from */ + ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), + SYS_STAT_CFG); + + /* Get Rx stats */ + stats->rx_bytes = ocelot_read(ocelot, SYS_COUNT_RX_OCTETS); + stats->rx_packets = ocelot_read(ocelot, SYS_COUNT_RX_SHORTS) + + ocelot_read(ocelot, SYS_COUNT_RX_FRAGMENTS) + + ocelot_read(ocelot, SYS_COUNT_RX_JABBERS) + + ocelot_read(ocelot, SYS_COUNT_RX_LONGS) + + ocelot_read(ocelot, SYS_COUNT_RX_64) + + ocelot_read(ocelot, SYS_COUNT_RX_65_127) + + ocelot_read(ocelot, SYS_COUNT_RX_128_255) + + ocelot_read(ocelot, SYS_COUNT_RX_256_1023) + + ocelot_read(ocelot, SYS_COUNT_RX_1024_1526) + + ocelot_read(ocelot, SYS_COUNT_RX_1527_MAX); + stats->multicast = ocelot_read(ocelot, SYS_COUNT_RX_MULTICAST); + stats->rx_dropped = dev->stats.rx_dropped; + + /* Get Tx stats */ + stats->tx_bytes = ocelot_read(ocelot, SYS_COUNT_TX_OCTETS); + stats->tx_packets = ocelot_read(ocelot, SYS_COUNT_TX_64) + + ocelot_read(ocelot, SYS_COUNT_TX_65_127) + + ocelot_read(ocelot, SYS_COUNT_TX_128_511) + + ocelot_read(ocelot, SYS_COUNT_TX_512_1023) + + ocelot_read(ocelot, SYS_COUNT_TX_1024_1526) + + ocelot_read(ocelot, SYS_COUNT_TX_1527_MAX); + stats->tx_dropped = ocelot_read(ocelot, SYS_COUNT_TX_DROPS) + + ocelot_read(ocelot, SYS_COUNT_TX_AGING); + stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION); +} + +static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, + u16 vid, u16 flags, + struct netlink_ext_ack *extack) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + return ocelot_fdb_add(ocelot, port, addr, vid); +} + +static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + return ocelot_fdb_del(ocelot, port, addr, vid); +} + +static int ocelot_port_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct net_device *dev, + struct net_device *filter_dev, int *idx) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_dump_ctx dump = { + .dev = dev, + .skb = skb, + .cb = cb, + .idx = *idx, + }; + int port = priv->chip_port; + int ret; + + ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump); + + *idx = dump.idx; + + return ret; +} + +static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto, + u16 vid) +{ + return ocelot_vlan_vid_add(dev, vid, false, false); +} + +static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, + u16 vid) +{ + return ocelot_vlan_vid_del(dev, vid); +} + +static void ocelot_vlan_mode(struct ocelot *ocelot, int port, + netdev_features_t features) +{ + u32 val; + + /* Filtering */ + val = ocelot_read(ocelot, ANA_VLANMASK); + if (features & NETIF_F_HW_VLAN_CTAG_FILTER) + val |= BIT(port); + else + val &= ~BIT(port); + ocelot_write(ocelot, val, ANA_VLANMASK); +} + +static int ocelot_set_features(struct net_device *dev, + netdev_features_t features) +{ + netdev_features_t changed = dev->features ^ features; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) && + priv->tc.offload_cnt) { + netdev_err(dev, + "Cannot disable HW TC offload while offloads active\n"); + return -EBUSY; + } + + if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) + ocelot_vlan_mode(ocelot, port, features); + + return 0; +} + +static int ocelot_get_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + + ppid->id_len = sizeof(ocelot->base_mac); + memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len); + + return 0; +} + +static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + /* If the attached PHY device isn't capable of timestamping operations, + * use our own (when possible). + */ + if (!phy_has_hwtstamp(dev->phydev) && ocelot->ptp) { + switch (cmd) { + case SIOCSHWTSTAMP: + return ocelot_hwstamp_set(ocelot, port, ifr); + case SIOCGHWTSTAMP: + return ocelot_hwstamp_get(ocelot, port, ifr); + } + } + + return phy_mii_ioctl(dev->phydev, ifr, cmd); +} + +static const struct net_device_ops ocelot_port_netdev_ops = { + .ndo_open = ocelot_port_open, + .ndo_stop = ocelot_port_stop, + .ndo_start_xmit = ocelot_port_xmit, + .ndo_set_rx_mode = ocelot_set_rx_mode, + .ndo_get_phys_port_name = ocelot_port_get_phys_port_name, + .ndo_set_mac_address = ocelot_port_set_mac_address, + .ndo_get_stats64 = ocelot_get_stats64, + .ndo_fdb_add = ocelot_port_fdb_add, + .ndo_fdb_del = ocelot_port_fdb_del, + .ndo_fdb_dump = ocelot_port_fdb_dump, + .ndo_vlan_rx_add_vid = ocelot_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid, + .ndo_set_features = ocelot_set_features, + .ndo_get_port_parent_id = ocelot_get_port_parent_id, + .ndo_setup_tc = ocelot_setup_tc, + .ndo_do_ioctl = ocelot_ioctl, +}; + +static void ocelot_port_get_strings(struct net_device *netdev, u32 sset, + u8 *data) +{ + struct ocelot_port_private *priv = netdev_priv(netdev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + ocelot_get_strings(ocelot, port, sset, data); +} + +static void ocelot_port_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + ocelot_get_ethtool_stats(ocelot, port, data); +} + +static int ocelot_port_get_sset_count(struct net_device *dev, int sset) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + return ocelot_get_sset_count(ocelot, port, sset); +} + +static int ocelot_port_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + if (!ocelot->ptp) + return ethtool_op_get_ts_info(dev, info); + + return ocelot_get_ts_info(ocelot, port, info); +} + +static const struct ethtool_ops ocelot_ethtool_ops = { + .get_strings = ocelot_port_get_strings, + .get_ethtool_stats = ocelot_port_get_ethtool_stats, + .get_sset_count = ocelot_port_get_sset_count, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_ts_info = ocelot_port_get_ts_info, +}; + +static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port, + struct switchdev_trans *trans, + u8 state) +{ + if (switchdev_trans_ph_prepare(trans)) + return; + + ocelot_bridge_stp_state_set(ocelot, port, state); +} + +static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int 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); + + ocelot_set_ageing_time(ocelot, ageing_time); +} + +static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc) +{ + u32 cpu_fwd_mcast = ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA | + ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA | + ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA; + u32 val = 0; + + if (mc) + val = cpu_fwd_mcast; + + ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast, + ANA_PORT_CPU_FWD_CFG, port); +} + +static int ocelot_port_attr_set(struct net_device *dev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + int err = 0; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + ocelot_port_attr_stp_state_set(ocelot, port, trans, + attr->u.stp_state); + break; + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: + ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time); + break; + case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: + ocelot_port_vlan_filtering(ocelot, port, + attr->u.vlan_filtering); + break; + case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: + ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int ocelot_port_obj_add_vlan(struct net_device *dev, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + int ret; + u16 vid; + + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { + ret = ocelot_vlan_vid_add(dev, vid, + vlan->flags & BRIDGE_VLAN_INFO_PVID, + vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + if (ret) + return ret; + } + + return 0; +} + +static int ocelot_port_vlan_del_vlan(struct net_device *dev, + const struct switchdev_obj_port_vlan *vlan) +{ + int ret; + u16 vid; + + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { + ret = ocelot_vlan_vid_del(dev, vid); + + if (ret) + return ret; + } + + return 0; +} + +static int ocelot_port_obj_add_mdb(struct net_device *dev, + const struct switchdev_obj_port_mdb *mdb, + struct switchdev_trans *trans) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + int port = priv->chip_port; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return ocelot_port_mdb_add(ocelot, port, mdb); +} + +static int ocelot_port_obj_del_mdb(struct net_device *dev, + const struct switchdev_obj_port_mdb *mdb) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + int port = priv->chip_port; + + return ocelot_port_mdb_del(ocelot, port, mdb); +} + +static int ocelot_port_obj_add(struct net_device *dev, + const struct switchdev_obj *obj, + struct switchdev_trans *trans, + struct netlink_ext_ack *extack) +{ + int ret = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + ret = ocelot_port_obj_add_vlan(dev, + SWITCHDEV_OBJ_PORT_VLAN(obj), + trans); + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj), + trans); + break; + default: + return -EOPNOTSUPP; + } + + return ret; +} + +static int ocelot_port_obj_del(struct net_device *dev, + const struct switchdev_obj *obj) +{ + int ret = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + ret = ocelot_port_vlan_del_vlan(dev, + SWITCHDEV_OBJ_PORT_VLAN(obj)); + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj)); + break; + default: + return -EOPNOTSUPP; + } + + return ret; +} + +/* Checks if the net_device instance given to us originate from our driver. */ +static bool ocelot_netdevice_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &ocelot_port_netdev_ops; +} + +static int ocelot_netdevice_port_event(struct net_device *dev, + unsigned long event, + struct netdev_notifier_changeupper_info *info) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + int port = priv->chip_port; + int err = 0; + + switch (event) { + case NETDEV_CHANGEUPPER: + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) { + err = ocelot_port_bridge_join(ocelot, port, + info->upper_dev); + } else { + err = ocelot_port_bridge_leave(ocelot, port, + info->upper_dev); + } + } + if (netif_is_lag_master(info->upper_dev)) { + if (info->linking) + err = ocelot_port_lag_join(ocelot, port, + info->upper_dev); + else + ocelot_port_lag_leave(ocelot, port, + info->upper_dev); + } + break; + default: + break; + } + + return err; +} + +static int ocelot_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct netdev_notifier_changeupper_info *info = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + int ret = 0; + + if (!ocelot_netdevice_dev_check(dev)) + return 0; + + if (event == NETDEV_PRECHANGEUPPER && + netif_is_lag_master(info->upper_dev)) { + struct netdev_lag_upper_info *lag_upper_info = info->upper_info; + struct netlink_ext_ack *extack; + + if (lag_upper_info && + lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + extack = netdev_notifier_info_to_extack(&info->info); + NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type"); + + ret = -EINVAL; + goto notify; + } + } + + if (netif_is_lag_master(dev)) { + struct net_device *slave; + struct list_head *iter; + + netdev_for_each_lower_dev(dev, slave, iter) { + ret = ocelot_netdevice_port_event(slave, event, info); + if (ret) + goto notify; + } + } else { + ret = ocelot_netdevice_port_event(dev, event, info); + } + +notify: + return notifier_from_errno(ret); +} + +struct notifier_block ocelot_netdevice_nb __read_mostly = { + .notifier_call = ocelot_netdevice_event, +}; +EXPORT_SYMBOL(ocelot_netdevice_nb); + +static int ocelot_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + int err; + + switch (event) { + case SWITCHDEV_PORT_ATTR_SET: + err = switchdev_handle_port_attr_set(dev, ptr, + ocelot_netdevice_dev_check, + ocelot_port_attr_set); + return notifier_from_errno(err); + } + + return NOTIFY_DONE; +} + +struct notifier_block ocelot_switchdev_nb __read_mostly = { + .notifier_call = ocelot_switchdev_event, +}; +EXPORT_SYMBOL(ocelot_switchdev_nb); + +static int ocelot_switchdev_blocking_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + int err; + + switch (event) { + /* Blocking events. */ + case SWITCHDEV_PORT_OBJ_ADD: + err = switchdev_handle_port_obj_add(dev, ptr, + ocelot_netdevice_dev_check, + ocelot_port_obj_add); + return notifier_from_errno(err); + case SWITCHDEV_PORT_OBJ_DEL: + err = switchdev_handle_port_obj_del(dev, ptr, + ocelot_netdevice_dev_check, + ocelot_port_obj_del); + return notifier_from_errno(err); + case SWITCHDEV_PORT_ATTR_SET: + err = switchdev_handle_port_attr_set(dev, ptr, + ocelot_netdevice_dev_check, + ocelot_port_attr_set); + return notifier_from_errno(err); + } + + return NOTIFY_DONE; +} + +struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = { + .notifier_call = ocelot_switchdev_blocking_event, +}; +EXPORT_SYMBOL(ocelot_switchdev_blocking_nb); + +int ocelot_probe_port(struct ocelot *ocelot, u8 port, + void __iomem *regs, + struct phy_device *phy) +{ + struct ocelot_port_private *priv; + struct ocelot_port *ocelot_port; + struct net_device *dev; + int err; + + dev = alloc_etherdev(sizeof(struct ocelot_port_private)); + if (!dev) + return -ENOMEM; + SET_NETDEV_DEV(dev, ocelot->dev); + priv = netdev_priv(dev); + priv->dev = dev; + priv->phy = phy; + priv->chip_port = port; + ocelot_port = &priv->port; + ocelot_port->ocelot = ocelot; + ocelot_port->regs = regs; + ocelot->ports[port] = ocelot_port; + + dev->netdev_ops = &ocelot_port_netdev_ops; + dev->ethtool_ops = &ocelot_ethtool_ops; + + dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS | + NETIF_F_HW_TC; + dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC; + + memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN); + dev->dev_addr[ETH_ALEN - 1] += port; + ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid, + ENTRYTYPE_LOCKED); + + ocelot_init_port(ocelot, port); + + err = register_netdev(dev); + if (err) { + dev_err(ocelot->dev, "register_netdev failed\n"); + free_netdev(dev); + } + + return err; +} +EXPORT_SYMBOL(ocelot_probe_port); diff --git a/drivers/net/ethernet/mscc/ocelot_police.c b/drivers/net/ethernet/mscc/ocelot_police.c index 2e1d8e187332..6f5068c1041a 100644 --- a/drivers/net/ethernet/mscc/ocelot_police.c +++ b/drivers/net/ethernet/mscc/ocelot_police.c @@ -7,16 +7,6 @@ #include <soc/mscc/ocelot.h> #include "ocelot_police.h" -enum mscc_qos_rate_mode { - MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */ - MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */ - MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */ - MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */ - __MSCC_QOS_RATE_MODE_END, - NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END, - MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1, -}; - /* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */ #define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */ #define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */ @@ -30,19 +20,8 @@ enum mscc_qos_rate_mode { /* Default policer order */ #define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */ -struct qos_policer_conf { - enum mscc_qos_rate_mode mode; - bool dlb; /* Enable DLB (dual leaky bucket mode */ - bool cf; /* Coupling flag (ignored in SLB mode) */ - u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */ - u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */ - u32 pir; /* PIR in kbps/fps */ - u32 pbs; /* PBS in bytes/frames */ - u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */ -}; - -static int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, - struct qos_policer_conf *conf) +int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, + struct qos_policer_conf *conf) { u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE; u32 cir = 0, cbs = 0, pir = 0, pbs = 0; @@ -228,27 +207,3 @@ int ocelot_port_policer_del(struct ocelot *ocelot, int port) return 0; } EXPORT_SYMBOL(ocelot_port_policer_del); - -int ocelot_ace_policer_add(struct ocelot *ocelot, u32 pol_ix, - struct ocelot_policer *pol) -{ - struct qos_policer_conf pp = { 0 }; - - if (!pol) - return -EINVAL; - - pp.mode = MSCC_QOS_RATE_MODE_DATA; - pp.pir = pol->rate; - pp.pbs = pol->burst; - - return qos_policer_conf_set(ocelot, 0, pol_ix, &pp); -} - -int ocelot_ace_policer_del(struct ocelot *ocelot, u32 pol_ix) -{ - struct qos_policer_conf pp = { 0 }; - - pp.mode = MSCC_QOS_RATE_MODE_DISABLED; - - return qos_policer_conf_set(ocelot, 0, pol_ix, &pp); -} diff --git a/drivers/net/ethernet/mscc/ocelot_police.h b/drivers/net/ethernet/mscc/ocelot_police.h index 792abd28010a..7adb05f71999 100644 --- a/drivers/net/ethernet/mscc/ocelot_police.h +++ b/drivers/net/ethernet/mscc/ocelot_police.h @@ -9,9 +9,28 @@ #include "ocelot.h" -int ocelot_ace_policer_add(struct ocelot *ocelot, u32 pol_ix, - struct ocelot_policer *pol); +enum mscc_qos_rate_mode { + MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */ + MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */ + MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */ + MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */ + __MSCC_QOS_RATE_MODE_END, + NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END, + MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1, +}; -int ocelot_ace_policer_del(struct ocelot *ocelot, u32 pol_ix); +struct qos_policer_conf { + enum mscc_qos_rate_mode mode; + bool dlb; /* Enable DLB (dual leaky bucket mode */ + bool cf; /* Coupling flag (ignored in SLB mode) */ + u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */ + u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */ + u32 pir; /* PIR in kbps/fps */ + u32 pbs; /* PBS in bytes/frames */ + u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */ +}; + +int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, + struct qos_policer_conf *conf); #endif /* _MSCC_OCELOT_POLICE_H_ */ diff --git a/drivers/net/ethernet/mscc/ocelot_regs.c b/drivers/net/ethernet/mscc/ocelot_regs.c deleted file mode 100644 index 81d81ff75646..000000000000 --- a/drivers/net/ethernet/mscc/ocelot_regs.c +++ /dev/null @@ -1,450 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* - * Microsemi Ocelot Switch driver - * - * Copyright (c) 2017 Microsemi Corporation - */ -#include "ocelot.h" -#include <soc/mscc/ocelot_hsio.h> - -static const u32 ocelot_ana_regmap[] = { - REG(ANA_ADVLEARN, 0x009000), - REG(ANA_VLANMASK, 0x009004), - REG(ANA_PORT_B_DOMAIN, 0x009008), - REG(ANA_ANAGEFIL, 0x00900c), - REG(ANA_ANEVENTS, 0x009010), - REG(ANA_STORMLIMIT_BURST, 0x009014), - REG(ANA_STORMLIMIT_CFG, 0x009018), - REG(ANA_ISOLATED_PORTS, 0x009028), - REG(ANA_COMMUNITY_PORTS, 0x00902c), - REG(ANA_AUTOAGE, 0x009030), - REG(ANA_MACTOPTIONS, 0x009034), - REG(ANA_LEARNDISC, 0x009038), - REG(ANA_AGENCTRL, 0x00903c), - REG(ANA_MIRRORPORTS, 0x009040), - REG(ANA_EMIRRORPORTS, 0x009044), - REG(ANA_FLOODING, 0x009048), - REG(ANA_FLOODING_IPMC, 0x00904c), - REG(ANA_SFLOW_CFG, 0x009050), - REG(ANA_PORT_MODE, 0x009080), - REG(ANA_PGID_PGID, 0x008c00), - REG(ANA_TABLES_ANMOVED, 0x008b30), - REG(ANA_TABLES_MACHDATA, 0x008b34), - REG(ANA_TABLES_MACLDATA, 0x008b38), - REG(ANA_TABLES_MACACCESS, 0x008b3c), - REG(ANA_TABLES_MACTINDX, 0x008b40), - REG(ANA_TABLES_VLANACCESS, 0x008b44), - REG(ANA_TABLES_VLANTIDX, 0x008b48), - REG(ANA_TABLES_ISDXACCESS, 0x008b4c), - REG(ANA_TABLES_ISDXTIDX, 0x008b50), - REG(ANA_TABLES_ENTRYLIM, 0x008b00), - REG(ANA_TABLES_PTP_ID_HIGH, 0x008b54), - REG(ANA_TABLES_PTP_ID_LOW, 0x008b58), - REG(ANA_MSTI_STATE, 0x008e00), - REG(ANA_PORT_VLAN_CFG, 0x007000), - REG(ANA_PORT_DROP_CFG, 0x007004), - REG(ANA_PORT_QOS_CFG, 0x007008), - REG(ANA_PORT_VCAP_CFG, 0x00700c), - REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007010), - REG(ANA_PORT_VCAP_S2_CFG, 0x00701c), - REG(ANA_PORT_PCP_DEI_MAP, 0x007020), - REG(ANA_PORT_CPU_FWD_CFG, 0x007060), - REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007064), - REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007068), - REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00706c), - REG(ANA_PORT_PORT_CFG, 0x007070), - REG(ANA_PORT_POL_CFG, 0x007074), - REG(ANA_PORT_PTP_CFG, 0x007078), - REG(ANA_PORT_PTP_DLY1_CFG, 0x00707c), - REG(ANA_OAM_UPM_LM_CNT, 0x007c00), - REG(ANA_PORT_PTP_DLY2_CFG, 0x007080), - REG(ANA_PFC_PFC_CFG, 0x008800), - REG(ANA_PFC_PFC_TIMER, 0x008804), - REG(ANA_IPT_OAM_MEP_CFG, 0x008000), - REG(ANA_IPT_IPT, 0x008004), - REG(ANA_PPT_PPT, 0x008ac0), - REG(ANA_FID_MAP_FID_MAP, 0x000000), - REG(ANA_AGGR_CFG, 0x0090b4), - REG(ANA_CPUQ_CFG, 0x0090b8), - REG(ANA_CPUQ_CFG2, 0x0090bc), - REG(ANA_CPUQ_8021_CFG, 0x0090c0), - REG(ANA_DSCP_CFG, 0x009100), - REG(ANA_DSCP_REWR_CFG, 0x009200), - REG(ANA_VCAP_RNG_TYPE_CFG, 0x009240), - REG(ANA_VCAP_RNG_VAL_CFG, 0x009260), - REG(ANA_VRAP_CFG, 0x009280), - REG(ANA_VRAP_HDR_DATA, 0x009284), - REG(ANA_VRAP_HDR_MASK, 0x009288), - REG(ANA_DISCARD_CFG, 0x00928c), - REG(ANA_FID_CFG, 0x009290), - REG(ANA_POL_PIR_CFG, 0x004000), - REG(ANA_POL_CIR_CFG, 0x004004), - REG(ANA_POL_MODE_CFG, 0x004008), - REG(ANA_POL_PIR_STATE, 0x00400c), - REG(ANA_POL_CIR_STATE, 0x004010), - REG(ANA_POL_STATE, 0x004014), - REG(ANA_POL_FLOWC, 0x008b80), - REG(ANA_POL_HYST, 0x008bec), - REG(ANA_POL_MISC_CFG, 0x008bf0), -}; - -static const u32 ocelot_qs_regmap[] = { - REG(QS_XTR_GRP_CFG, 0x000000), - REG(QS_XTR_RD, 0x000008), - REG(QS_XTR_FRM_PRUNING, 0x000010), - REG(QS_XTR_FLUSH, 0x000018), - REG(QS_XTR_DATA_PRESENT, 0x00001c), - REG(QS_XTR_CFG, 0x000020), - REG(QS_INJ_GRP_CFG, 0x000024), - REG(QS_INJ_WR, 0x00002c), - REG(QS_INJ_CTRL, 0x000034), - REG(QS_INJ_STATUS, 0x00003c), - REG(QS_INJ_ERR, 0x000040), - REG(QS_INH_DBG, 0x000048), -}; - -static const u32 ocelot_qsys_regmap[] = { - REG(QSYS_PORT_MODE, 0x011200), - REG(QSYS_SWITCH_PORT_MODE, 0x011234), - REG(QSYS_STAT_CNT_CFG, 0x011264), - REG(QSYS_EEE_CFG, 0x011268), - REG(QSYS_EEE_THRES, 0x011294), - REG(QSYS_IGR_NO_SHARING, 0x011298), - REG(QSYS_EGR_NO_SHARING, 0x01129c), - REG(QSYS_SW_STATUS, 0x0112a0), - REG(QSYS_EXT_CPU_CFG, 0x0112d0), - REG(QSYS_PAD_CFG, 0x0112d4), - REG(QSYS_CPU_GROUP_MAP, 0x0112d8), - REG(QSYS_QMAP, 0x0112dc), - REG(QSYS_ISDX_SGRP, 0x011400), - REG(QSYS_TIMED_FRAME_ENTRY, 0x014000), - REG(QSYS_TFRM_MISC, 0x011310), - REG(QSYS_TFRM_PORT_DLY, 0x011314), - REG(QSYS_TFRM_TIMER_CFG_1, 0x011318), - REG(QSYS_TFRM_TIMER_CFG_2, 0x01131c), - REG(QSYS_TFRM_TIMER_CFG_3, 0x011320), - REG(QSYS_TFRM_TIMER_CFG_4, 0x011324), - REG(QSYS_TFRM_TIMER_CFG_5, 0x011328), - REG(QSYS_TFRM_TIMER_CFG_6, 0x01132c), - REG(QSYS_TFRM_TIMER_CFG_7, 0x011330), - REG(QSYS_TFRM_TIMER_CFG_8, 0x011334), - REG(QSYS_RED_PROFILE, 0x011338), - REG(QSYS_RES_QOS_MODE, 0x011378), - REG(QSYS_RES_CFG, 0x012000), - REG(QSYS_RES_STAT, 0x012004), - REG(QSYS_EGR_DROP_MODE, 0x01137c), - REG(QSYS_EQ_CTRL, 0x011380), - REG(QSYS_EVENTS_CORE, 0x011384), - REG(QSYS_CIR_CFG, 0x000000), - REG(QSYS_EIR_CFG, 0x000004), - REG(QSYS_SE_CFG, 0x000008), - REG(QSYS_SE_DWRR_CFG, 0x00000c), - REG(QSYS_SE_CONNECT, 0x00003c), - REG(QSYS_SE_DLB_SENSE, 0x000040), - REG(QSYS_CIR_STATE, 0x000044), - REG(QSYS_EIR_STATE, 0x000048), - REG(QSYS_SE_STATE, 0x00004c), - REG(QSYS_HSCH_MISC_CFG, 0x011388), -}; - -static const u32 ocelot_rew_regmap[] = { - REG(REW_PORT_VLAN_CFG, 0x000000), - REG(REW_TAG_CFG, 0x000004), - REG(REW_PORT_CFG, 0x000008), - REG(REW_DSCP_CFG, 0x00000c), - REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010), - REG(REW_PTP_CFG, 0x000050), - REG(REW_PTP_DLY1_CFG, 0x000054), - REG(REW_DSCP_REMAP_DP1_CFG, 0x000690), - REG(REW_DSCP_REMAP_CFG, 0x000790), - REG(REW_STAT_CFG, 0x000890), - REG(REW_PPT, 0x000680), -}; - -static const u32 ocelot_sys_regmap[] = { - REG(SYS_COUNT_RX_OCTETS, 0x000000), - REG(SYS_COUNT_RX_UNICAST, 0x000004), - REG(SYS_COUNT_RX_MULTICAST, 0x000008), - REG(SYS_COUNT_RX_BROADCAST, 0x00000c), - REG(SYS_COUNT_RX_SHORTS, 0x000010), - REG(SYS_COUNT_RX_FRAGMENTS, 0x000014), - REG(SYS_COUNT_RX_JABBERS, 0x000018), - REG(SYS_COUNT_RX_CRC_ALIGN_ERRS, 0x00001c), - REG(SYS_COUNT_RX_SYM_ERRS, 0x000020), - REG(SYS_COUNT_RX_64, 0x000024), - REG(SYS_COUNT_RX_65_127, 0x000028), - REG(SYS_COUNT_RX_128_255, 0x00002c), - REG(SYS_COUNT_RX_256_1023, 0x000030), - REG(SYS_COUNT_RX_1024_1526, 0x000034), - REG(SYS_COUNT_RX_1527_MAX, 0x000038), - REG(SYS_COUNT_RX_PAUSE, 0x00003c), - REG(SYS_COUNT_RX_CONTROL, 0x000040), - REG(SYS_COUNT_RX_LONGS, 0x000044), - REG(SYS_COUNT_RX_CLASSIFIED_DROPS, 0x000048), - REG(SYS_COUNT_TX_OCTETS, 0x000100), - REG(SYS_COUNT_TX_UNICAST, 0x000104), - REG(SYS_COUNT_TX_MULTICAST, 0x000108), - REG(SYS_COUNT_TX_BROADCAST, 0x00010c), - REG(SYS_COUNT_TX_COLLISION, 0x000110), - REG(SYS_COUNT_TX_DROPS, 0x000114), - REG(SYS_COUNT_TX_PAUSE, 0x000118), - REG(SYS_COUNT_TX_64, 0x00011c), - REG(SYS_COUNT_TX_65_127, 0x000120), - REG(SYS_COUNT_TX_128_511, 0x000124), - REG(SYS_COUNT_TX_512_1023, 0x000128), - REG(SYS_COUNT_TX_1024_1526, 0x00012c), - REG(SYS_COUNT_TX_1527_MAX, 0x000130), - REG(SYS_COUNT_TX_AGING, 0x000170), - REG(SYS_RESET_CFG, 0x000508), - REG(SYS_CMID, 0x00050c), - REG(SYS_VLAN_ETYPE_CFG, 0x000510), - REG(SYS_PORT_MODE, 0x000514), - REG(SYS_FRONT_PORT_MODE, 0x000548), - REG(SYS_FRM_AGING, 0x000574), - REG(SYS_STAT_CFG, 0x000578), - REG(SYS_SW_STATUS, 0x00057c), - REG(SYS_MISC_CFG, 0x0005ac), - REG(SYS_REW_MAC_HIGH_CFG, 0x0005b0), - REG(SYS_REW_MAC_LOW_CFG, 0x0005dc), - REG(SYS_CM_ADDR, 0x000500), - REG(SYS_CM_DATA, 0x000504), - REG(SYS_PAUSE_CFG, 0x000608), - REG(SYS_PAUSE_TOT_CFG, 0x000638), - REG(SYS_ATOP, 0x00063c), - REG(SYS_ATOP_TOT_CFG, 0x00066c), - REG(SYS_MAC_FC_CFG, 0x000670), - REG(SYS_MMGT, 0x00069c), - REG(SYS_MMGT_FAST, 0x0006a0), - REG(SYS_EVENTS_DIF, 0x0006a4), - REG(SYS_EVENTS_CORE, 0x0006b4), - REG(SYS_CNT, 0x000000), - REG(SYS_PTP_STATUS, 0x0006b8), - REG(SYS_PTP_TXSTAMP, 0x0006bc), - REG(SYS_PTP_NXT, 0x0006c0), - REG(SYS_PTP_CFG, 0x0006c4), -}; - -static const u32 ocelot_s2_regmap[] = { - REG(S2_CORE_UPDATE_CTRL, 0x000000), - REG(S2_CORE_MV_CFG, 0x000004), - REG(S2_CACHE_ENTRY_DAT, 0x000008), - REG(S2_CACHE_MASK_DAT, 0x000108), - REG(S2_CACHE_ACTION_DAT, 0x000208), - REG(S2_CACHE_CNT_DAT, 0x000308), - REG(S2_CACHE_TG_DAT, 0x000388), -}; - -static const u32 ocelot_ptp_regmap[] = { - REG(PTP_PIN_CFG, 0x000000), - REG(PTP_PIN_TOD_SEC_MSB, 0x000004), - REG(PTP_PIN_TOD_SEC_LSB, 0x000008), - REG(PTP_PIN_TOD_NSEC, 0x00000c), - REG(PTP_PIN_WF_HIGH_PERIOD, 0x000014), - REG(PTP_PIN_WF_LOW_PERIOD, 0x000018), - REG(PTP_CFG_MISC, 0x0000a0), - REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4), - REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8), -}; - -static const u32 *ocelot_regmap[] = { - [ANA] = ocelot_ana_regmap, - [QS] = ocelot_qs_regmap, - [QSYS] = ocelot_qsys_regmap, - [REW] = ocelot_rew_regmap, - [SYS] = ocelot_sys_regmap, - [S2] = ocelot_s2_regmap, - [PTP] = ocelot_ptp_regmap, -}; - -static const struct reg_field ocelot_regfields[] = { - [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11), - [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10), - [ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27), - [ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26), - [ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25), - [ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24), - [ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23), - [ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22), - [ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21), - [ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20), - [ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19), - [ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18), - [ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17), - [ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16), - [ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15), - [ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14), - [ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13), - [ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12), - [ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11), - [ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10), - [ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9), - [ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8), - [ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7), - [ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6), - [ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5), - [ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4), - [ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3), - [ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2), - [ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1), - [ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0), - [ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18), - [ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11), - [ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9), - [QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20), - [QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19), - [QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7), - [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3), - [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0), - [SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2), - [SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1), - [SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0), -}; - -static const struct ocelot_stat_layout ocelot_stats_layout[] = { - { .name = "rx_octets", .offset = 0x00, }, - { .name = "rx_unicast", .offset = 0x01, }, - { .name = "rx_multicast", .offset = 0x02, }, - { .name = "rx_broadcast", .offset = 0x03, }, - { .name = "rx_shorts", .offset = 0x04, }, - { .name = "rx_fragments", .offset = 0x05, }, - { .name = "rx_jabbers", .offset = 0x06, }, - { .name = "rx_crc_align_errs", .offset = 0x07, }, - { .name = "rx_sym_errs", .offset = 0x08, }, - { .name = "rx_frames_below_65_octets", .offset = 0x09, }, - { .name = "rx_frames_65_to_127_octets", .offset = 0x0A, }, - { .name = "rx_frames_128_to_255_octets", .offset = 0x0B, }, - { .name = "rx_frames_256_to_511_octets", .offset = 0x0C, }, - { .name = "rx_frames_512_to_1023_octets", .offset = 0x0D, }, - { .name = "rx_frames_1024_to_1526_octets", .offset = 0x0E, }, - { .name = "rx_frames_over_1526_octets", .offset = 0x0F, }, - { .name = "rx_pause", .offset = 0x10, }, - { .name = "rx_control", .offset = 0x11, }, - { .name = "rx_longs", .offset = 0x12, }, - { .name = "rx_classified_drops", .offset = 0x13, }, - { .name = "rx_red_prio_0", .offset = 0x14, }, - { .name = "rx_red_prio_1", .offset = 0x15, }, - { .name = "rx_red_prio_2", .offset = 0x16, }, - { .name = "rx_red_prio_3", .offset = 0x17, }, - { .name = "rx_red_prio_4", .offset = 0x18, }, - { .name = "rx_red_prio_5", .offset = 0x19, }, - { .name = "rx_red_prio_6", .offset = 0x1A, }, - { .name = "rx_red_prio_7", .offset = 0x1B, }, - { .name = "rx_yellow_prio_0", .offset = 0x1C, }, - { .name = "rx_yellow_prio_1", .offset = 0x1D, }, - { .name = "rx_yellow_prio_2", .offset = 0x1E, }, - { .name = "rx_yellow_prio_3", .offset = 0x1F, }, - { .name = "rx_yellow_prio_4", .offset = 0x20, }, - { .name = "rx_yellow_prio_5", .offset = 0x21, }, - { .name = "rx_yellow_prio_6", .offset = 0x22, }, - { .name = "rx_yellow_prio_7", .offset = 0x23, }, - { .name = "rx_green_prio_0", .offset = 0x24, }, - { .name = "rx_green_prio_1", .offset = 0x25, }, - { .name = "rx_green_prio_2", .offset = 0x26, }, - { .name = "rx_green_prio_3", .offset = 0x27, }, - { .name = "rx_green_prio_4", .offset = 0x28, }, - { .name = "rx_green_prio_5", .offset = 0x29, }, - { .name = "rx_green_prio_6", .offset = 0x2A, }, - { .name = "rx_green_prio_7", .offset = 0x2B, }, - { .name = "tx_octets", .offset = 0x40, }, - { .name = "tx_unicast", .offset = 0x41, }, - { .name = "tx_multicast", .offset = 0x42, }, - { .name = "tx_broadcast", .offset = 0x43, }, - { .name = "tx_collision", .offset = 0x44, }, - { .name = "tx_drops", .offset = 0x45, }, - { .name = "tx_pause", .offset = 0x46, }, - { .name = "tx_frames_below_65_octets", .offset = 0x47, }, - { .name = "tx_frames_65_to_127_octets", .offset = 0x48, }, - { .name = "tx_frames_128_255_octets", .offset = 0x49, }, - { .name = "tx_frames_256_511_octets", .offset = 0x4A, }, - { .name = "tx_frames_512_1023_octets", .offset = 0x4B, }, - { .name = "tx_frames_1024_1526_octets", .offset = 0x4C, }, - { .name = "tx_frames_over_1526_octets", .offset = 0x4D, }, - { .name = "tx_yellow_prio_0", .offset = 0x4E, }, - { .name = "tx_yellow_prio_1", .offset = 0x4F, }, - { .name = "tx_yellow_prio_2", .offset = 0x50, }, - { .name = "tx_yellow_prio_3", .offset = 0x51, }, - { .name = "tx_yellow_prio_4", .offset = 0x52, }, - { .name = "tx_yellow_prio_5", .offset = 0x53, }, - { .name = "tx_yellow_prio_6", .offset = 0x54, }, - { .name = "tx_yellow_prio_7", .offset = 0x55, }, - { .name = "tx_green_prio_0", .offset = 0x56, }, - { .name = "tx_green_prio_1", .offset = 0x57, }, - { .name = "tx_green_prio_2", .offset = 0x58, }, - { .name = "tx_green_prio_3", .offset = 0x59, }, - { .name = "tx_green_prio_4", .offset = 0x5A, }, - { .name = "tx_green_prio_5", .offset = 0x5B, }, - { .name = "tx_green_prio_6", .offset = 0x5C, }, - { .name = "tx_green_prio_7", .offset = 0x5D, }, - { .name = "tx_aged", .offset = 0x5E, }, - { .name = "drop_local", .offset = 0x80, }, - { .name = "drop_tail", .offset = 0x81, }, - { .name = "drop_yellow_prio_0", .offset = 0x82, }, - { .name = "drop_yellow_prio_1", .offset = 0x83, }, - { .name = "drop_yellow_prio_2", .offset = 0x84, }, - { .name = "drop_yellow_prio_3", .offset = 0x85, }, - { .name = "drop_yellow_prio_4", .offset = 0x86, }, - { .name = "drop_yellow_prio_5", .offset = 0x87, }, - { .name = "drop_yellow_prio_6", .offset = 0x88, }, - { .name = "drop_yellow_prio_7", .offset = 0x89, }, - { .name = "drop_green_prio_0", .offset = 0x8A, }, - { .name = "drop_green_prio_1", .offset = 0x8B, }, - { .name = "drop_green_prio_2", .offset = 0x8C, }, - { .name = "drop_green_prio_3", .offset = 0x8D, }, - { .name = "drop_green_prio_4", .offset = 0x8E, }, - { .name = "drop_green_prio_5", .offset = 0x8F, }, - { .name = "drop_green_prio_6", .offset = 0x90, }, - { .name = "drop_green_prio_7", .offset = 0x91, }, -}; - -static void ocelot_pll5_init(struct ocelot *ocelot) -{ - /* Configure PLL5. This will need a proper CCF driver - * The values are coming from the VTSS API for Ocelot - */ - regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4, - HSIO_PLL5G_CFG4_IB_CTRL(0x7600) | - HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8)); - regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0, - HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) | - HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) | - HSIO_PLL5G_CFG0_ENA_BIAS | - HSIO_PLL5G_CFG0_ENA_VCO_BUF | - HSIO_PLL5G_CFG0_ENA_CP1 | - HSIO_PLL5G_CFG0_SELCPI(2) | - HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) | - HSIO_PLL5G_CFG0_SELBGV820(4) | - HSIO_PLL5G_CFG0_DIV4 | - HSIO_PLL5G_CFG0_ENA_CLKTREE | - HSIO_PLL5G_CFG0_ENA_LANE); - regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2, - HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET | - HSIO_PLL5G_CFG2_EN_RESET_OVERRUN | - HSIO_PLL5G_CFG2_GAIN_TEST(0x8) | - HSIO_PLL5G_CFG2_ENA_AMPCTRL | - HSIO_PLL5G_CFG2_PWD_AMPCTRL_N | - HSIO_PLL5G_CFG2_AMPC_SEL(0x10)); -} - -int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops) -{ - int ret; - - ocelot->map = ocelot_regmap; - ocelot->stats_layout = ocelot_stats_layout; - ocelot->num_stats = ARRAY_SIZE(ocelot_stats_layout); - ocelot->shared_queue_sz = 224 * 1024; - ocelot->num_mact_rows = 1024; - ocelot->ops = ops; - - ret = ocelot_regfields_init(ocelot, ocelot_regfields); - if (ret) - return ret; - - ocelot_pll5_init(ocelot); - - eth_random_addr(ocelot->base_mac); - ocelot->base_mac[5] &= 0xf0; - - return 0; -} -EXPORT_SYMBOL(ocelot_chip_init); diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c deleted file mode 100644 index b7baf7624e18..000000000000 --- a/drivers/net/ethernet/mscc/ocelot_tc.c +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* Microsemi Ocelot Switch TC driver - * - * Copyright (c) 2019 Microsemi Corporation - */ - -#include <soc/mscc/ocelot.h> -#include "ocelot_tc.h" -#include "ocelot_ace.h" -#include <net/pkt_cls.h> - -static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, - struct tc_cls_matchall_offload *f, - bool ingress) -{ - struct netlink_ext_ack *extack = f->common.extack; - struct ocelot *ocelot = priv->port.ocelot; - struct ocelot_policer pol = { 0 }; - struct flow_action_entry *action; - int port = priv->chip_port; - int err; - - if (!ingress) { - NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); - return -EOPNOTSUPP; - } - - switch (f->command) { - case TC_CLSMATCHALL_REPLACE: - if (!flow_offload_has_one_action(&f->rule->action)) { - NL_SET_ERR_MSG_MOD(extack, - "Only one action is supported"); - return -EOPNOTSUPP; - } - - if (priv->tc.block_shared) { - NL_SET_ERR_MSG_MOD(extack, - "Rate limit is not supported on shared blocks"); - return -EOPNOTSUPP; - } - - action = &f->rule->action.entries[0]; - - if (action->id != FLOW_ACTION_POLICE) { - NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); - return -EOPNOTSUPP; - } - - if (priv->tc.police_id && priv->tc.police_id != f->cookie) { - NL_SET_ERR_MSG_MOD(extack, - "Only one policer per port is supported"); - return -EEXIST; - } - - pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; - pol.burst = (u32)div_u64(action->police.rate_bytes_ps * - PSCHED_NS2TICKS(action->police.burst), - PSCHED_TICKS_PER_SEC); - - err = ocelot_port_policer_add(ocelot, port, &pol); - if (err) { - NL_SET_ERR_MSG_MOD(extack, "Could not add policer"); - return err; - } - - priv->tc.police_id = f->cookie; - priv->tc.offload_cnt++; - return 0; - case TC_CLSMATCHALL_DESTROY: - if (priv->tc.police_id != f->cookie) - return -ENOENT; - - err = ocelot_port_policer_del(ocelot, port); - if (err) { - NL_SET_ERR_MSG_MOD(extack, - "Could not delete policer"); - return err; - } - priv->tc.police_id = 0; - priv->tc.offload_cnt--; - return 0; - case TC_CLSMATCHALL_STATS: /* fall through */ - default: - return -EOPNOTSUPP; - } -} - -static int ocelot_setup_tc_block_cb(enum tc_setup_type type, - void *type_data, - void *cb_priv, bool ingress) -{ - struct ocelot_port_private *priv = cb_priv; - - if (!tc_cls_can_offload_and_chain0(priv->dev, type_data)) - return -EOPNOTSUPP; - - switch (type) { - case TC_SETUP_CLSMATCHALL: - return ocelot_setup_tc_cls_matchall(priv, type_data, ingress); - case TC_SETUP_CLSFLOWER: - return ocelot_setup_tc_cls_flower(priv, type_data, ingress); - default: - return -EOPNOTSUPP; - } -} - -static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type, - void *type_data, - void *cb_priv) -{ - return ocelot_setup_tc_block_cb(type, type_data, - cb_priv, true); -} - -static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type, - void *type_data, - void *cb_priv) -{ - return ocelot_setup_tc_block_cb(type, type_data, - cb_priv, false); -} - -static LIST_HEAD(ocelot_block_cb_list); - -static int ocelot_setup_tc_block(struct ocelot_port_private *priv, - struct flow_block_offload *f) -{ - struct flow_block_cb *block_cb; - flow_setup_cb_t *cb; - - if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) { - cb = ocelot_setup_tc_block_cb_ig; - priv->tc.block_shared = f->block_shared; - } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) { - cb = ocelot_setup_tc_block_cb_eg; - } else { - return -EOPNOTSUPP; - } - - f->driver_block_list = &ocelot_block_cb_list; - - switch (f->command) { - case FLOW_BLOCK_BIND: - if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list)) - return -EBUSY; - - block_cb = flow_block_cb_alloc(cb, priv, priv, NULL); - if (IS_ERR(block_cb)) - return PTR_ERR(block_cb); - - flow_block_cb_add(block_cb, f); - list_add_tail(&block_cb->driver_list, f->driver_block_list); - return 0; - case FLOW_BLOCK_UNBIND: - block_cb = flow_block_cb_lookup(f->block, cb, priv); - if (!block_cb) - return -ENOENT; - - flow_block_cb_remove(block_cb, f); - list_del(&block_cb->driver_list); - return 0; - default: - return -EOPNOTSUPP; - } -} - -int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type, - void *type_data) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - - switch (type) { - case TC_SETUP_BLOCK: - return ocelot_setup_tc_block(priv, type_data); - default: - return -EOPNOTSUPP; - } - return 0; -} diff --git a/drivers/net/ethernet/mscc/ocelot_tc.h b/drivers/net/ethernet/mscc/ocelot_tc.h deleted file mode 100644 index 61757c2250a6..000000000000 --- a/drivers/net/ethernet/mscc/ocelot_tc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ -/* Microsemi Ocelot Switch driver - * - * Copyright (c) 2019 Microsemi Corporation - */ - -#ifndef _MSCC_OCELOT_TC_H_ -#define _MSCC_OCELOT_TC_H_ - -#include <linux/netdevice.h> - -struct ocelot_port_tc { - bool block_shared; - unsigned long offload_cnt; - - unsigned long police_id; -}; - -int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type, - void *type_data); - -#endif /* _MSCC_OCELOT_TC_H_ */ diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_vcap.c index dfd82a3baab2..3ef620faf995 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.c +++ b/drivers/net/ethernet/mscc/ocelot_vcap.c @@ -8,7 +8,7 @@ #include <soc/mscc/ocelot_vcap.h> #include "ocelot_police.h" -#include "ocelot_ace.h" +#include "ocelot_vcap.h" #include "ocelot_s2.h" #define OCELOT_POLICER_DISCARD 0x17f @@ -119,7 +119,8 @@ static void vcap_cache2entry(struct ocelot *ocelot, struct vcap_data *data) static void vcap_action2cache(struct ocelot *ocelot, struct vcap_data *data) { const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; - u32 action_words, i, width, mask; + u32 action_words, mask; + int i, width; /* Encode action type */ width = vcap_is2->action_type_width; @@ -141,7 +142,8 @@ static void vcap_action2cache(struct ocelot *ocelot, struct vcap_data *data) static void vcap_cache2action(struct ocelot *ocelot, struct vcap_data *data) { const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; - u32 action_words, i, width; + u32 action_words; + int i, width; action_words = DIV_ROUND_UP(vcap_is2->action_width, ENTRY_WIDTH); @@ -161,8 +163,8 @@ static void vcap_cache2action(struct ocelot *ocelot, struct vcap_data *data) static void is2_data_get(struct ocelot *ocelot, struct vcap_data *data, int ix) { const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; - u32 i, col, offset, count, cnt, base; - u32 width = vcap_is2->tg_width; + int i, col, offset, count, cnt, base; + int width = vcap_is2->tg_width; count = (data->tg_sw == VCAP_TG_HALF ? 2 : 4); col = (ix % 2); @@ -300,10 +302,10 @@ static void vcap_action_set(struct ocelot *ocelot, struct vcap_data *data, } static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, - struct ocelot_ace_rule *ace) + struct ocelot_vcap_filter *filter) { - switch (ace->action) { - case OCELOT_ACL_ACTION_DROP: + switch (filter->action) { + case OCELOT_VCAP_ACTION_DROP: vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1); @@ -312,7 +314,7 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0); break; - case OCELOT_ACL_ACTION_TRAP: + case OCELOT_VCAP_ACTION_TRAP: vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 0); @@ -320,12 +322,12 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 1); break; - case OCELOT_ACL_ACTION_POLICE: + case OCELOT_VCAP_ACTION_POLICE: vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_IDX, - ace->pol_ix); + filter->pol_ix); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0); break; @@ -333,11 +335,11 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, } static void is2_entry_set(struct ocelot *ocelot, int ix, - struct ocelot_ace_rule *ace) + struct ocelot_vcap_filter *filter) { const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + struct ocelot_vcap_key_vlan *tag = &filter->vlan; u32 val, msk, type, type_mask = 0xf, i, count; - struct ocelot_ace_vlan *tag = &ace->vlan; struct ocelot_vcap_u64 payload; struct vcap_data data; int row = (ix / 2); @@ -353,19 +355,19 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, data.tg_sw = VCAP_TG_HALF; is2_data_get(ocelot, &data, ix); data.tg = (data.tg & ~data.tg_mask); - if (ace->prio != 0) + if (filter->prio != 0) data.tg |= data.tg_value; data.type = IS2_ACTION_TYPE_NORMAL; vcap_key_set(ocelot, &data, VCAP_IS2_HK_PAG, 0, 0); vcap_key_set(ocelot, &data, VCAP_IS2_HK_IGR_PORT_MASK, 0, - ~ace->ingress_port_mask); + ~filter->ingress_port_mask); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_FIRST, OCELOT_VCAP_BIT_1); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_HOST_MATCH, OCELOT_VCAP_BIT_ANY); - vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_MC, ace->dmac_mc); - vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_BC, ace->dmac_bc); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_MC, filter->dmac_mc); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_BC, filter->dmac_bc); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_VLAN_TAGGED, tag->tagged); vcap_key_set(ocelot, &data, VCAP_IS2_HK_VID, tag->vid.value, tag->vid.mask); @@ -373,9 +375,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, tag->pcp.value[0], tag->pcp.mask[0]); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_DEI, tag->dei); - switch (ace->type) { - case OCELOT_ACE_TYPE_ETYPE: { - struct ocelot_ace_frame_etype *etype = &ace->frame.etype; + switch (filter->key_type) { + case OCELOT_VCAP_KEY_ETYPE: { + struct ocelot_vcap_key_etype *etype = &filter->key.etype; type = IS2_TYPE_ETYPE; vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, @@ -396,8 +398,8 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, etype->data.value, etype->data.mask); break; } - case OCELOT_ACE_TYPE_LLC: { - struct ocelot_ace_frame_llc *llc = &ace->frame.llc; + case OCELOT_VCAP_KEY_LLC: { + struct ocelot_vcap_key_llc *llc = &filter->key.llc; type = IS2_TYPE_LLC; vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, @@ -412,8 +414,8 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, payload.value, payload.mask); break; } - case OCELOT_ACE_TYPE_SNAP: { - struct ocelot_ace_frame_snap *snap = &ace->frame.snap; + case OCELOT_VCAP_KEY_SNAP: { + struct ocelot_vcap_key_snap *snap = &filter->key.snap; type = IS2_TYPE_SNAP; vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, @@ -421,12 +423,12 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_SMAC, snap->smac.value, snap->smac.mask); vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_SNAP_L2_SNAP, - ace->frame.snap.snap.value, - ace->frame.snap.snap.mask); + filter->key.snap.snap.value, + filter->key.snap.snap.mask); break; } - case OCELOT_ACE_TYPE_ARP: { - struct ocelot_ace_frame_arp *arp = &ace->frame.arp; + case OCELOT_VCAP_KEY_ARP: { + struct ocelot_vcap_key_arp *arp = &filter->key.arp; type = IS2_TYPE_ARP; vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_ARP_SMAC, @@ -467,20 +469,20 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, 0, 0); break; } - case OCELOT_ACE_TYPE_IPV4: - case OCELOT_ACE_TYPE_IPV6: { + case OCELOT_VCAP_KEY_IPV4: + case OCELOT_VCAP_KEY_IPV6: { enum ocelot_vcap_bit sip_eq_dip, sport_eq_dport, seq_zero, tcp; enum ocelot_vcap_bit ttl, fragment, options, tcp_ack, tcp_urg; enum ocelot_vcap_bit tcp_fin, tcp_syn, tcp_rst, tcp_psh; - struct ocelot_ace_frame_ipv4 *ipv4 = NULL; - struct ocelot_ace_frame_ipv6 *ipv6 = NULL; + struct ocelot_vcap_key_ipv4 *ipv4 = NULL; + struct ocelot_vcap_key_ipv6 *ipv6 = NULL; struct ocelot_vcap_udp_tcp *sport, *dport; struct ocelot_vcap_ipv4 sip, dip; struct ocelot_vcap_u8 proto, ds; struct ocelot_vcap_u48 *ip_data; - if (ace->type == OCELOT_ACE_TYPE_IPV4) { - ipv4 = &ace->frame.ipv4; + if (filter->key_type == OCELOT_VCAP_KEY_IPV4) { + ipv4 = &filter->key.ipv4; ttl = ipv4->ttl; fragment = ipv4->fragment; options = ipv4->options; @@ -501,7 +503,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, sport_eq_dport = ipv4->sport_eq_dport; seq_zero = ipv4->seq_zero; } else { - ipv6 = &ace->frame.ipv6; + ipv6 = &filter->key.ipv6; ttl = ipv6->ttl; fragment = OCELOT_VCAP_BIT_ANY; options = OCELOT_VCAP_BIT_ANY; @@ -605,7 +607,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, } break; } - case OCELOT_ACE_TYPE_ANY: + case OCELOT_VCAP_KEY_ANY: default: type = 0; type_mask = 0; @@ -621,9 +623,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, } vcap_key_set(ocelot, &data, VCAP_IS2_TYPE, type, type_mask); - is2_action_set(ocelot, &data, ace); + is2_action_set(ocelot, &data, filter); vcap_data_set(data.counter, data.counter_offset, - vcap_is2->counter_width, ace->stats.pkts); + vcap_is2->counter_width, filter->stats.pkts); /* Write row */ vcap_entry2cache(ocelot, &data); @@ -631,7 +633,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, vcap_row_cmd(ocelot, row, VCAP_CMD_WRITE, VCAP_SEL_ALL); } -static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule, +static void is2_entry_get(struct ocelot *ocelot, struct ocelot_vcap_filter *filter, int ix) { const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; @@ -646,55 +648,99 @@ static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule, cnt = vcap_data_get(data.counter, data.counter_offset, vcap_is2->counter_width); - rule->stats.pkts = cnt; + filter->stats.pkts = cnt; } -static void ocelot_ace_rule_add(struct ocelot *ocelot, - struct ocelot_acl_block *block, - struct ocelot_ace_rule *rule) +static int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix, + struct ocelot_policer *pol) { - struct ocelot_ace_rule *tmp; + struct qos_policer_conf pp = { 0 }; + + if (!pol) + return -EINVAL; + + pp.mode = MSCC_QOS_RATE_MODE_DATA; + pp.pir = pol->rate; + pp.pbs = pol->burst; + + return qos_policer_conf_set(ocelot, 0, pol_ix, &pp); +} + +static void ocelot_vcap_policer_del(struct ocelot *ocelot, + struct ocelot_vcap_block *block, + u32 pol_ix) +{ + struct ocelot_vcap_filter *filter; + struct qos_policer_conf pp = {0}; + int index = -1; + + if (pol_ix < block->pol_lpr) + return; + + list_for_each_entry(filter, &block->rules, list) { + index++; + if (filter->action == OCELOT_VCAP_ACTION_POLICE && + filter->pol_ix < pol_ix) { + filter->pol_ix += 1; + ocelot_vcap_policer_add(ocelot, filter->pol_ix, + &filter->pol); + is2_entry_set(ocelot, index, filter); + } + } + + pp.mode = MSCC_QOS_RATE_MODE_DISABLED; + qos_policer_conf_set(ocelot, 0, pol_ix, &pp); + + block->pol_lpr++; +} + +static void ocelot_vcap_filter_add_to_block(struct ocelot *ocelot, + struct ocelot_vcap_block *block, + struct ocelot_vcap_filter *filter) +{ + struct ocelot_vcap_filter *tmp; struct list_head *pos, *n; - if (rule->action == OCELOT_ACL_ACTION_POLICE) { + if (filter->action == OCELOT_VCAP_ACTION_POLICE) { block->pol_lpr--; - rule->pol_ix = block->pol_lpr; - ocelot_ace_policer_add(ocelot, rule->pol_ix, &rule->pol); + filter->pol_ix = block->pol_lpr; + ocelot_vcap_policer_add(ocelot, filter->pol_ix, &filter->pol); } block->count++; if (list_empty(&block->rules)) { - list_add(&rule->list, &block->rules); + list_add(&filter->list, &block->rules); return; } list_for_each_safe(pos, n, &block->rules) { - tmp = list_entry(pos, struct ocelot_ace_rule, list); - if (rule->prio < tmp->prio) + tmp = list_entry(pos, struct ocelot_vcap_filter, list); + if (filter->prio < tmp->prio) break; } - list_add(&rule->list, pos->prev); + list_add(&filter->list, pos->prev); } -static int ocelot_ace_rule_get_index_id(struct ocelot_acl_block *block, - struct ocelot_ace_rule *rule) +static int ocelot_vcap_block_get_filter_index(struct ocelot_vcap_block *block, + struct ocelot_vcap_filter *filter) { - struct ocelot_ace_rule *tmp; + struct ocelot_vcap_filter *tmp; int index = -1; list_for_each_entry(tmp, &block->rules, list) { ++index; - if (rule->id == tmp->id) + if (filter->id == tmp->id) break; } return index; } -static struct ocelot_ace_rule* -ocelot_ace_rule_get_rule_index(struct ocelot_acl_block *block, int index) +static struct ocelot_vcap_filter* +ocelot_vcap_block_find_filter(struct ocelot_vcap_block *block, + int index) { - struct ocelot_ace_rule *tmp; + struct ocelot_vcap_filter *tmp; int i = 0; list_for_each_entry(tmp, &block->rules, list) { @@ -737,15 +783,16 @@ static void ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port, ANA_PORT_VCAP_S2_CFG, port); } -static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace) +static bool +ocelot_vcap_is_problematic_mac_etype(struct ocelot_vcap_filter *filter) { u16 proto, mask; - if (ace->type != OCELOT_ACE_TYPE_ETYPE) + if (filter->key_type != OCELOT_VCAP_KEY_ETYPE) return false; - proto = ntohs(*(u16 *)ace->frame.etype.etype.value); - mask = ntohs(*(u16 *)ace->frame.etype.etype.mask); + proto = ntohs(*(__be16 *)filter->key.etype.etype.value); + mask = ntohs(*(__be16 *)filter->key.etype.etype.mask); /* ETH_P_ALL match, so all protocols below are included */ if (mask == 0) @@ -760,49 +807,51 @@ static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace) return false; } -static bool ocelot_ace_is_problematic_non_mac_etype(struct ocelot_ace_rule *ace) +static bool +ocelot_vcap_is_problematic_non_mac_etype(struct ocelot_vcap_filter *filter) { - if (ace->type == OCELOT_ACE_TYPE_SNAP) + if (filter->key_type == OCELOT_VCAP_KEY_SNAP) return true; - if (ace->type == OCELOT_ACE_TYPE_ARP) + if (filter->key_type == OCELOT_VCAP_KEY_ARP) return true; - if (ace->type == OCELOT_ACE_TYPE_IPV4) + if (filter->key_type == OCELOT_VCAP_KEY_IPV4) return true; - if (ace->type == OCELOT_ACE_TYPE_IPV6) + if (filter->key_type == OCELOT_VCAP_KEY_IPV6) return true; return false; } -static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot, - struct ocelot_ace_rule *ace) +static bool +ocelot_exclusive_mac_etype_filter_rules(struct ocelot *ocelot, + struct ocelot_vcap_filter *filter) { - struct ocelot_acl_block *block = &ocelot->acl_block; - struct ocelot_ace_rule *tmp; + struct ocelot_vcap_block *block = &ocelot->block; + struct ocelot_vcap_filter *tmp; unsigned long port; int i; - if (ocelot_ace_is_problematic_mac_etype(ace)) { + if (ocelot_vcap_is_problematic_mac_etype(filter)) { /* Search for any non-MAC_ETYPE rules on the port */ for (i = 0; i < block->count; i++) { - tmp = ocelot_ace_rule_get_rule_index(block, i); - if (tmp->ingress_port_mask & ace->ingress_port_mask && - ocelot_ace_is_problematic_non_mac_etype(tmp)) + tmp = ocelot_vcap_block_find_filter(block, i); + if (tmp->ingress_port_mask & filter->ingress_port_mask && + ocelot_vcap_is_problematic_non_mac_etype(tmp)) return false; } - for_each_set_bit(port, &ace->ingress_port_mask, + for_each_set_bit(port, &filter->ingress_port_mask, ocelot->num_phys_ports) ocelot_match_all_as_mac_etype(ocelot, port, true); - } else if (ocelot_ace_is_problematic_non_mac_etype(ace)) { + } else if (ocelot_vcap_is_problematic_non_mac_etype(filter)) { /* Search for any MAC_ETYPE rules on the port */ for (i = 0; i < block->count; i++) { - tmp = ocelot_ace_rule_get_rule_index(block, i); - if (tmp->ingress_port_mask & ace->ingress_port_mask && - ocelot_ace_is_problematic_mac_etype(tmp)) + tmp = ocelot_vcap_block_find_filter(block, i); + if (tmp->ingress_port_mask & filter->ingress_port_mask && + ocelot_vcap_is_problematic_mac_etype(tmp)) return false; } - for_each_set_bit(port, &ace->ingress_port_mask, + for_each_set_bit(port, &filter->ingress_port_mask, ocelot->num_phys_ports) ocelot_match_all_as_mac_etype(ocelot, port, false); } @@ -810,75 +859,51 @@ static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot, return true; } -int ocelot_ace_rule_offload_add(struct ocelot *ocelot, - struct ocelot_ace_rule *rule, - struct netlink_ext_ack *extack) +int ocelot_vcap_filter_add(struct ocelot *ocelot, + struct ocelot_vcap_filter *filter, + struct netlink_ext_ack *extack) { - struct ocelot_acl_block *block = &ocelot->acl_block; - struct ocelot_ace_rule *ace; + struct ocelot_vcap_block *block = &ocelot->block; int i, index; - if (!ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) { + if (!ocelot_exclusive_mac_etype_filter_rules(ocelot, filter)) { NL_SET_ERR_MSG_MOD(extack, "Cannot mix MAC_ETYPE with non-MAC_ETYPE rules"); return -EBUSY; } - /* Add rule to the linked list */ - ocelot_ace_rule_add(ocelot, block, rule); + /* Add filter to the linked list */ + ocelot_vcap_filter_add_to_block(ocelot, block, filter); - /* Get the index of the inserted rule */ - index = ocelot_ace_rule_get_index_id(block, rule); + /* Get the index of the inserted filter */ + index = ocelot_vcap_block_get_filter_index(block, filter); - /* Move down the rules to make place for the new rule */ + /* Move down the rules to make place for the new filter */ for (i = block->count - 1; i > index; i--) { - ace = ocelot_ace_rule_get_rule_index(block, i); - is2_entry_set(ocelot, i, ace); - } - - /* Now insert the new rule */ - is2_entry_set(ocelot, index, rule); - return 0; -} - -static void ocelot_ace_police_del(struct ocelot *ocelot, - struct ocelot_acl_block *block, - u32 ix) -{ - struct ocelot_ace_rule *ace; - int index = -1; - - if (ix < block->pol_lpr) - return; + struct ocelot_vcap_filter *tmp; - list_for_each_entry(ace, &block->rules, list) { - index++; - if (ace->action == OCELOT_ACL_ACTION_POLICE && - ace->pol_ix < ix) { - ace->pol_ix += 1; - ocelot_ace_policer_add(ocelot, ace->pol_ix, - &ace->pol); - is2_entry_set(ocelot, index, ace); - } + tmp = ocelot_vcap_block_find_filter(block, i); + is2_entry_set(ocelot, i, tmp); } - ocelot_ace_policer_del(ocelot, block->pol_lpr); - block->pol_lpr++; + /* Now insert the new filter */ + is2_entry_set(ocelot, index, filter); + return 0; } -static void ocelot_ace_rule_del(struct ocelot *ocelot, - struct ocelot_acl_block *block, - struct ocelot_ace_rule *rule) +static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot, + struct ocelot_vcap_block *block, + struct ocelot_vcap_filter *filter) { - struct ocelot_ace_rule *tmp; + struct ocelot_vcap_filter *tmp; struct list_head *pos, *q; list_for_each_safe(pos, q, &block->rules) { - tmp = list_entry(pos, struct ocelot_ace_rule, list); - if (tmp->id == rule->id) { - if (tmp->action == OCELOT_ACL_ACTION_POLICE) - ocelot_ace_police_del(ocelot, block, - tmp->pol_ix); + tmp = list_entry(pos, struct ocelot_vcap_filter, list); + if (tmp->id == filter->id) { + if (tmp->action == OCELOT_VCAP_ACTION_POLICE) + ocelot_vcap_policer_del(ocelot, block, + tmp->pol_ix); list_del(pos); kfree(tmp); @@ -888,56 +913,57 @@ static void ocelot_ace_rule_del(struct ocelot *ocelot, block->count--; } -int ocelot_ace_rule_offload_del(struct ocelot *ocelot, - struct ocelot_ace_rule *rule) +int ocelot_vcap_filter_del(struct ocelot *ocelot, + struct ocelot_vcap_filter *filter) { - struct ocelot_acl_block *block = &ocelot->acl_block; - struct ocelot_ace_rule del_ace; - struct ocelot_ace_rule *ace; + struct ocelot_vcap_block *block = &ocelot->block; + struct ocelot_vcap_filter del_filter; int i, index; - memset(&del_ace, 0, sizeof(del_ace)); + memset(&del_filter, 0, sizeof(del_filter)); - /* Gets index of the rule */ - index = ocelot_ace_rule_get_index_id(block, rule); + /* Gets index of the filter */ + index = ocelot_vcap_block_get_filter_index(block, filter); - /* Delete rule */ - ocelot_ace_rule_del(ocelot, block, rule); + /* Delete filter */ + ocelot_vcap_block_remove_filter(ocelot, block, filter); - /* Move up all the blocks over the deleted rule */ + /* Move up all the blocks over the deleted filter */ for (i = index; i < block->count; i++) { - ace = ocelot_ace_rule_get_rule_index(block, i); - is2_entry_set(ocelot, i, ace); + struct ocelot_vcap_filter *tmp; + + tmp = ocelot_vcap_block_find_filter(block, i); + is2_entry_set(ocelot, i, tmp); } - /* Now delete the last rule, because it is duplicated */ - is2_entry_set(ocelot, block->count, &del_ace); + /* Now delete the last filter, because it is duplicated */ + is2_entry_set(ocelot, block->count, &del_filter); return 0; } -int ocelot_ace_rule_stats_update(struct ocelot *ocelot, - struct ocelot_ace_rule *rule) +int ocelot_vcap_filter_stats_update(struct ocelot *ocelot, + struct ocelot_vcap_filter *filter) { - struct ocelot_acl_block *block = &ocelot->acl_block; - struct ocelot_ace_rule *tmp; + struct ocelot_vcap_block *block = &ocelot->block; + struct ocelot_vcap_filter *tmp; int index; - index = ocelot_ace_rule_get_index_id(block, rule); - is2_entry_get(ocelot, rule, index); + index = ocelot_vcap_block_get_filter_index(block, filter); + is2_entry_get(ocelot, filter, index); /* After we get the result we need to clear the counters */ - tmp = ocelot_ace_rule_get_rule_index(block, index); + tmp = ocelot_vcap_block_find_filter(block, index); tmp->stats.pkts = 0; is2_entry_set(ocelot, index, tmp); return 0; } -int ocelot_ace_init(struct ocelot *ocelot) +int ocelot_vcap_init(struct ocelot *ocelot) { const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; - struct ocelot_acl_block *block = &ocelot->acl_block; + struct ocelot_vcap_block *block = &ocelot->block; struct vcap_data data; memset(&data, 0, sizeof(data)); @@ -968,7 +994,7 @@ int ocelot_ace_init(struct ocelot *ocelot) block->pol_lpr = OCELOT_POLICER_DISCARD - 1; - INIT_LIST_HEAD(&ocelot->acl_block.rules); + INIT_LIST_HEAD(&ocelot->block.rules); return 0; } diff --git a/drivers/net/ethernet/mscc/ocelot_ace.h b/drivers/net/ethernet/mscc/ocelot_vcap.h index 099e177f2617..0dfbfc011b2e 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.h +++ b/drivers/net/ethernet/mscc/ocelot_vcap.h @@ -3,8 +3,8 @@ * Copyright (c) 2019 Microsemi Corporation */ -#ifndef _MSCC_OCELOT_ACE_H_ -#define _MSCC_OCELOT_ACE_H_ +#ifndef _MSCC_OCELOT_VCAP_H_ +#define _MSCC_OCELOT_VCAP_H_ #include "ocelot.h" #include "ocelot_police.h" @@ -76,31 +76,31 @@ struct ocelot_vcap_udp_tcp { u16 mask; }; -enum ocelot_ace_type { - OCELOT_ACE_TYPE_ANY, - OCELOT_ACE_TYPE_ETYPE, - OCELOT_ACE_TYPE_LLC, - OCELOT_ACE_TYPE_SNAP, - OCELOT_ACE_TYPE_ARP, - OCELOT_ACE_TYPE_IPV4, - OCELOT_ACE_TYPE_IPV6 +enum ocelot_vcap_key_type { + OCELOT_VCAP_KEY_ANY, + OCELOT_VCAP_KEY_ETYPE, + OCELOT_VCAP_KEY_LLC, + OCELOT_VCAP_KEY_SNAP, + OCELOT_VCAP_KEY_ARP, + OCELOT_VCAP_KEY_IPV4, + OCELOT_VCAP_KEY_IPV6 }; -struct ocelot_ace_vlan { +struct ocelot_vcap_key_vlan { struct ocelot_vcap_vid vid; /* VLAN ID (12 bit) */ struct ocelot_vcap_u8 pcp; /* PCP (3 bit) */ enum ocelot_vcap_bit dei; /* DEI */ enum ocelot_vcap_bit tagged; /* Tagged/untagged frame */ }; -struct ocelot_ace_frame_etype { +struct ocelot_vcap_key_etype { struct ocelot_vcap_u48 dmac; struct ocelot_vcap_u48 smac; struct ocelot_vcap_u16 etype; struct ocelot_vcap_u16 data; /* MAC data */ }; -struct ocelot_ace_frame_llc { +struct ocelot_vcap_key_llc { struct ocelot_vcap_u48 dmac; struct ocelot_vcap_u48 smac; @@ -108,7 +108,7 @@ struct ocelot_ace_frame_llc { struct ocelot_vcap_u32 llc; }; -struct ocelot_ace_frame_snap { +struct ocelot_vcap_key_snap { struct ocelot_vcap_u48 dmac; struct ocelot_vcap_u48 smac; @@ -116,7 +116,7 @@ struct ocelot_ace_frame_snap { struct ocelot_vcap_u40 snap; }; -struct ocelot_ace_frame_arp { +struct ocelot_vcap_key_arp { struct ocelot_vcap_u48 smac; enum ocelot_vcap_bit arp; /* Opcode ARP/RARP */ enum ocelot_vcap_bit req; /* Opcode request/reply */ @@ -133,7 +133,7 @@ struct ocelot_ace_frame_arp { struct ocelot_vcap_ipv4 dip; /* Target IP address */ }; -struct ocelot_ace_frame_ipv4 { +struct ocelot_vcap_key_ipv4 { enum ocelot_vcap_bit ttl; /* TTL zero */ enum ocelot_vcap_bit fragment; /* Fragment */ enum ocelot_vcap_bit options; /* Header options */ @@ -155,7 +155,7 @@ struct ocelot_ace_frame_ipv4 { enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */ }; -struct ocelot_ace_frame_ipv6 { +struct ocelot_vcap_key_ipv6 { struct ocelot_vcap_u8 proto; /* IPv6 protocol */ struct ocelot_vcap_u128 sip; /* IPv6 source (byte 0-7 ignored) */ enum ocelot_vcap_bit ttl; /* TTL zero */ @@ -174,58 +174,58 @@ struct ocelot_ace_frame_ipv6 { enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */ }; -enum ocelot_ace_action { - OCELOT_ACL_ACTION_DROP, - OCELOT_ACL_ACTION_TRAP, - OCELOT_ACL_ACTION_POLICE, +enum ocelot_vcap_action { + OCELOT_VCAP_ACTION_DROP, + OCELOT_VCAP_ACTION_TRAP, + OCELOT_VCAP_ACTION_POLICE, }; -struct ocelot_ace_stats { +struct ocelot_vcap_stats { u64 bytes; u64 pkts; u64 used; }; -struct ocelot_ace_rule { +struct ocelot_vcap_filter { struct list_head list; u16 prio; u32 id; - enum ocelot_ace_action action; - struct ocelot_ace_stats stats; + enum ocelot_vcap_action action; + struct ocelot_vcap_stats stats; unsigned long ingress_port_mask; enum ocelot_vcap_bit dmac_mc; enum ocelot_vcap_bit dmac_bc; - struct ocelot_ace_vlan vlan; + struct ocelot_vcap_key_vlan vlan; - enum ocelot_ace_type type; + enum ocelot_vcap_key_type key_type; union { - /* ocelot_ACE_TYPE_ANY: No specific fields */ - struct ocelot_ace_frame_etype etype; - struct ocelot_ace_frame_llc llc; - struct ocelot_ace_frame_snap snap; - struct ocelot_ace_frame_arp arp; - struct ocelot_ace_frame_ipv4 ipv4; - struct ocelot_ace_frame_ipv6 ipv6; - } frame; + /* OCELOT_VCAP_KEY_ANY: No specific fields */ + struct ocelot_vcap_key_etype etype; + struct ocelot_vcap_key_llc llc; + struct ocelot_vcap_key_snap snap; + struct ocelot_vcap_key_arp arp; + struct ocelot_vcap_key_ipv4 ipv4; + struct ocelot_vcap_key_ipv6 ipv6; + } key; struct ocelot_policer pol; u32 pol_ix; }; -int ocelot_ace_rule_offload_add(struct ocelot *ocelot, - struct ocelot_ace_rule *rule, - struct netlink_ext_ack *extack); -int ocelot_ace_rule_offload_del(struct ocelot *ocelot, - struct ocelot_ace_rule *rule); -int ocelot_ace_rule_stats_update(struct ocelot *ocelot, - struct ocelot_ace_rule *rule); +int ocelot_vcap_filter_add(struct ocelot *ocelot, + struct ocelot_vcap_filter *rule, + struct netlink_ext_ack *extack); +int ocelot_vcap_filter_del(struct ocelot *ocelot, + struct ocelot_vcap_filter *rule); +int ocelot_vcap_filter_stats_update(struct ocelot *ocelot, + struct ocelot_vcap_filter *rule); -int ocelot_ace_init(struct ocelot *ocelot); +int ocelot_vcap_init(struct ocelot *ocelot); int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, struct flow_cls_offload *f, bool ingress); -#endif /* _MSCC_OCELOT_ACE_H_ */ +#endif /* _MSCC_OCELOT_VCAP_H_ */ diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c new file mode 100644 index 000000000000..43716e8dc0ac --- /dev/null +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c @@ -0,0 +1,1068 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Microsemi Ocelot Switch driver + * + * Copyright (c) 2017 Microsemi Corporation + */ +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of_net.h> +#include <linux/netdevice.h> +#include <linux/of_mdio.h> +#include <linux/of_platform.h> +#include <linux/mfd/syscon.h> +#include <linux/skbuff.h> +#include <net/switchdev.h> + +#include <soc/mscc/ocelot_vcap.h> +#include <soc/mscc/ocelot_hsio.h> +#include "ocelot.h" + +#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0)) +#define VSC7514_VCAP_IS2_CNT 64 +#define VSC7514_VCAP_IS2_ENTRY_WIDTH 376 +#define VSC7514_VCAP_IS2_ACTION_WIDTH 99 +#define VSC7514_VCAP_PORT_CNT 11 + +static const u32 ocelot_ana_regmap[] = { + REG(ANA_ADVLEARN, 0x009000), + REG(ANA_VLANMASK, 0x009004), + REG(ANA_PORT_B_DOMAIN, 0x009008), + REG(ANA_ANAGEFIL, 0x00900c), + REG(ANA_ANEVENTS, 0x009010), + REG(ANA_STORMLIMIT_BURST, 0x009014), + REG(ANA_STORMLIMIT_CFG, 0x009018), + REG(ANA_ISOLATED_PORTS, 0x009028), + REG(ANA_COMMUNITY_PORTS, 0x00902c), + REG(ANA_AUTOAGE, 0x009030), + REG(ANA_MACTOPTIONS, 0x009034), + REG(ANA_LEARNDISC, 0x009038), + REG(ANA_AGENCTRL, 0x00903c), + REG(ANA_MIRRORPORTS, 0x009040), + REG(ANA_EMIRRORPORTS, 0x009044), + REG(ANA_FLOODING, 0x009048), + REG(ANA_FLOODING_IPMC, 0x00904c), + REG(ANA_SFLOW_CFG, 0x009050), + REG(ANA_PORT_MODE, 0x009080), + REG(ANA_PGID_PGID, 0x008c00), + REG(ANA_TABLES_ANMOVED, 0x008b30), + REG(ANA_TABLES_MACHDATA, 0x008b34), + REG(ANA_TABLES_MACLDATA, 0x008b38), + REG(ANA_TABLES_MACACCESS, 0x008b3c), + REG(ANA_TABLES_MACTINDX, 0x008b40), + REG(ANA_TABLES_VLANACCESS, 0x008b44), + REG(ANA_TABLES_VLANTIDX, 0x008b48), + REG(ANA_TABLES_ISDXACCESS, 0x008b4c), + REG(ANA_TABLES_ISDXTIDX, 0x008b50), + REG(ANA_TABLES_ENTRYLIM, 0x008b00), + REG(ANA_TABLES_PTP_ID_HIGH, 0x008b54), + REG(ANA_TABLES_PTP_ID_LOW, 0x008b58), + REG(ANA_MSTI_STATE, 0x008e00), + REG(ANA_PORT_VLAN_CFG, 0x007000), + REG(ANA_PORT_DROP_CFG, 0x007004), + REG(ANA_PORT_QOS_CFG, 0x007008), + REG(ANA_PORT_VCAP_CFG, 0x00700c), + REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007010), + REG(ANA_PORT_VCAP_S2_CFG, 0x00701c), + REG(ANA_PORT_PCP_DEI_MAP, 0x007020), + REG(ANA_PORT_CPU_FWD_CFG, 0x007060), + REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007064), + REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007068), + REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00706c), + REG(ANA_PORT_PORT_CFG, 0x007070), + REG(ANA_PORT_POL_CFG, 0x007074), + REG(ANA_PORT_PTP_CFG, 0x007078), + REG(ANA_PORT_PTP_DLY1_CFG, 0x00707c), + REG(ANA_OAM_UPM_LM_CNT, 0x007c00), + REG(ANA_PORT_PTP_DLY2_CFG, 0x007080), + REG(ANA_PFC_PFC_CFG, 0x008800), + REG(ANA_PFC_PFC_TIMER, 0x008804), + REG(ANA_IPT_OAM_MEP_CFG, 0x008000), + REG(ANA_IPT_IPT, 0x008004), + REG(ANA_PPT_PPT, 0x008ac0), + REG(ANA_FID_MAP_FID_MAP, 0x000000), + REG(ANA_AGGR_CFG, 0x0090b4), + REG(ANA_CPUQ_CFG, 0x0090b8), + REG(ANA_CPUQ_CFG2, 0x0090bc), + REG(ANA_CPUQ_8021_CFG, 0x0090c0), + REG(ANA_DSCP_CFG, 0x009100), + REG(ANA_DSCP_REWR_CFG, 0x009200), + REG(ANA_VCAP_RNG_TYPE_CFG, 0x009240), + REG(ANA_VCAP_RNG_VAL_CFG, 0x009260), + REG(ANA_VRAP_CFG, 0x009280), + REG(ANA_VRAP_HDR_DATA, 0x009284), + REG(ANA_VRAP_HDR_MASK, 0x009288), + REG(ANA_DISCARD_CFG, 0x00928c), + REG(ANA_FID_CFG, 0x009290), + REG(ANA_POL_PIR_CFG, 0x004000), + REG(ANA_POL_CIR_CFG, 0x004004), + REG(ANA_POL_MODE_CFG, 0x004008), + REG(ANA_POL_PIR_STATE, 0x00400c), + REG(ANA_POL_CIR_STATE, 0x004010), + REG(ANA_POL_STATE, 0x004014), + REG(ANA_POL_FLOWC, 0x008b80), + REG(ANA_POL_HYST, 0x008bec), + REG(ANA_POL_MISC_CFG, 0x008bf0), +}; + +static const u32 ocelot_qs_regmap[] = { + REG(QS_XTR_GRP_CFG, 0x000000), + REG(QS_XTR_RD, 0x000008), + REG(QS_XTR_FRM_PRUNING, 0x000010), + REG(QS_XTR_FLUSH, 0x000018), + REG(QS_XTR_DATA_PRESENT, 0x00001c), + REG(QS_XTR_CFG, 0x000020), + REG(QS_INJ_GRP_CFG, 0x000024), + REG(QS_INJ_WR, 0x00002c), + REG(QS_INJ_CTRL, 0x000034), + REG(QS_INJ_STATUS, 0x00003c), + REG(QS_INJ_ERR, 0x000040), + REG(QS_INH_DBG, 0x000048), +}; + +static const u32 ocelot_qsys_regmap[] = { + REG(QSYS_PORT_MODE, 0x011200), + REG(QSYS_SWITCH_PORT_MODE, 0x011234), + REG(QSYS_STAT_CNT_CFG, 0x011264), + REG(QSYS_EEE_CFG, 0x011268), + REG(QSYS_EEE_THRES, 0x011294), + REG(QSYS_IGR_NO_SHARING, 0x011298), + REG(QSYS_EGR_NO_SHARING, 0x01129c), + REG(QSYS_SW_STATUS, 0x0112a0), + REG(QSYS_EXT_CPU_CFG, 0x0112d0), + REG(QSYS_PAD_CFG, 0x0112d4), + REG(QSYS_CPU_GROUP_MAP, 0x0112d8), + REG(QSYS_QMAP, 0x0112dc), + REG(QSYS_ISDX_SGRP, 0x011400), + REG(QSYS_TIMED_FRAME_ENTRY, 0x014000), + REG(QSYS_TFRM_MISC, 0x011310), + REG(QSYS_TFRM_PORT_DLY, 0x011314), + REG(QSYS_TFRM_TIMER_CFG_1, 0x011318), + REG(QSYS_TFRM_TIMER_CFG_2, 0x01131c), + REG(QSYS_TFRM_TIMER_CFG_3, 0x011320), + REG(QSYS_TFRM_TIMER_CFG_4, 0x011324), + REG(QSYS_TFRM_TIMER_CFG_5, 0x011328), + REG(QSYS_TFRM_TIMER_CFG_6, 0x01132c), + REG(QSYS_TFRM_TIMER_CFG_7, 0x011330), + REG(QSYS_TFRM_TIMER_CFG_8, 0x011334), + REG(QSYS_RED_PROFILE, 0x011338), + REG(QSYS_RES_QOS_MODE, 0x011378), + REG(QSYS_RES_CFG, 0x012000), + REG(QSYS_RES_STAT, 0x012004), + REG(QSYS_EGR_DROP_MODE, 0x01137c), + REG(QSYS_EQ_CTRL, 0x011380), + REG(QSYS_EVENTS_CORE, 0x011384), + REG(QSYS_CIR_CFG, 0x000000), + REG(QSYS_EIR_CFG, 0x000004), + REG(QSYS_SE_CFG, 0x000008), + REG(QSYS_SE_DWRR_CFG, 0x00000c), + REG(QSYS_SE_CONNECT, 0x00003c), + REG(QSYS_SE_DLB_SENSE, 0x000040), + REG(QSYS_CIR_STATE, 0x000044), + REG(QSYS_EIR_STATE, 0x000048), + REG(QSYS_SE_STATE, 0x00004c), + REG(QSYS_HSCH_MISC_CFG, 0x011388), +}; + +static const u32 ocelot_rew_regmap[] = { + REG(REW_PORT_VLAN_CFG, 0x000000), + REG(REW_TAG_CFG, 0x000004), + REG(REW_PORT_CFG, 0x000008), + REG(REW_DSCP_CFG, 0x00000c), + REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010), + REG(REW_PTP_CFG, 0x000050), + REG(REW_PTP_DLY1_CFG, 0x000054), + REG(REW_DSCP_REMAP_DP1_CFG, 0x000690), + REG(REW_DSCP_REMAP_CFG, 0x000790), + REG(REW_STAT_CFG, 0x000890), + REG(REW_PPT, 0x000680), +}; + +static const u32 ocelot_sys_regmap[] = { + REG(SYS_COUNT_RX_OCTETS, 0x000000), + REG(SYS_COUNT_RX_UNICAST, 0x000004), + REG(SYS_COUNT_RX_MULTICAST, 0x000008), + REG(SYS_COUNT_RX_BROADCAST, 0x00000c), + REG(SYS_COUNT_RX_SHORTS, 0x000010), + REG(SYS_COUNT_RX_FRAGMENTS, 0x000014), + REG(SYS_COUNT_RX_JABBERS, 0x000018), + REG(SYS_COUNT_RX_CRC_ALIGN_ERRS, 0x00001c), + REG(SYS_COUNT_RX_SYM_ERRS, 0x000020), + REG(SYS_COUNT_RX_64, 0x000024), + REG(SYS_COUNT_RX_65_127, 0x000028), + REG(SYS_COUNT_RX_128_255, 0x00002c), + REG(SYS_COUNT_RX_256_1023, 0x000030), + REG(SYS_COUNT_RX_1024_1526, 0x000034), + REG(SYS_COUNT_RX_1527_MAX, 0x000038), + REG(SYS_COUNT_RX_PAUSE, 0x00003c), + REG(SYS_COUNT_RX_CONTROL, 0x000040), + REG(SYS_COUNT_RX_LONGS, 0x000044), + REG(SYS_COUNT_RX_CLASSIFIED_DROPS, 0x000048), + REG(SYS_COUNT_TX_OCTETS, 0x000100), + REG(SYS_COUNT_TX_UNICAST, 0x000104), + REG(SYS_COUNT_TX_MULTICAST, 0x000108), + REG(SYS_COUNT_TX_BROADCAST, 0x00010c), + REG(SYS_COUNT_TX_COLLISION, 0x000110), + REG(SYS_COUNT_TX_DROPS, 0x000114), + REG(SYS_COUNT_TX_PAUSE, 0x000118), + REG(SYS_COUNT_TX_64, 0x00011c), + REG(SYS_COUNT_TX_65_127, 0x000120), + REG(SYS_COUNT_TX_128_511, 0x000124), + REG(SYS_COUNT_TX_512_1023, 0x000128), + REG(SYS_COUNT_TX_1024_1526, 0x00012c), + REG(SYS_COUNT_TX_1527_MAX, 0x000130), + REG(SYS_COUNT_TX_AGING, 0x000170), + REG(SYS_RESET_CFG, 0x000508), + REG(SYS_CMID, 0x00050c), + REG(SYS_VLAN_ETYPE_CFG, 0x000510), + REG(SYS_PORT_MODE, 0x000514), + REG(SYS_FRONT_PORT_MODE, 0x000548), + REG(SYS_FRM_AGING, 0x000574), + REG(SYS_STAT_CFG, 0x000578), + REG(SYS_SW_STATUS, 0x00057c), + REG(SYS_MISC_CFG, 0x0005ac), + REG(SYS_REW_MAC_HIGH_CFG, 0x0005b0), + REG(SYS_REW_MAC_LOW_CFG, 0x0005dc), + REG(SYS_CM_ADDR, 0x000500), + REG(SYS_CM_DATA, 0x000504), + REG(SYS_PAUSE_CFG, 0x000608), + REG(SYS_PAUSE_TOT_CFG, 0x000638), + REG(SYS_ATOP, 0x00063c), + REG(SYS_ATOP_TOT_CFG, 0x00066c), + REG(SYS_MAC_FC_CFG, 0x000670), + REG(SYS_MMGT, 0x00069c), + REG(SYS_MMGT_FAST, 0x0006a0), + REG(SYS_EVENTS_DIF, 0x0006a4), + REG(SYS_EVENTS_CORE, 0x0006b4), + REG(SYS_CNT, 0x000000), + REG(SYS_PTP_STATUS, 0x0006b8), + REG(SYS_PTP_TXSTAMP, 0x0006bc), + REG(SYS_PTP_NXT, 0x0006c0), + REG(SYS_PTP_CFG, 0x0006c4), +}; + +static const u32 ocelot_s2_regmap[] = { + REG(S2_CORE_UPDATE_CTRL, 0x000000), + REG(S2_CORE_MV_CFG, 0x000004), + REG(S2_CACHE_ENTRY_DAT, 0x000008), + REG(S2_CACHE_MASK_DAT, 0x000108), + REG(S2_CACHE_ACTION_DAT, 0x000208), + REG(S2_CACHE_CNT_DAT, 0x000308), + REG(S2_CACHE_TG_DAT, 0x000388), +}; + +static const u32 ocelot_ptp_regmap[] = { + REG(PTP_PIN_CFG, 0x000000), + REG(PTP_PIN_TOD_SEC_MSB, 0x000004), + REG(PTP_PIN_TOD_SEC_LSB, 0x000008), + REG(PTP_PIN_TOD_NSEC, 0x00000c), + REG(PTP_PIN_WF_HIGH_PERIOD, 0x000014), + REG(PTP_PIN_WF_LOW_PERIOD, 0x000018), + REG(PTP_CFG_MISC, 0x0000a0), + REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4), + REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8), +}; + +static const u32 *ocelot_regmap[] = { + [ANA] = ocelot_ana_regmap, + [QS] = ocelot_qs_regmap, + [QSYS] = ocelot_qsys_regmap, + [REW] = ocelot_rew_regmap, + [SYS] = ocelot_sys_regmap, + [S2] = ocelot_s2_regmap, + [PTP] = ocelot_ptp_regmap, +}; + +static const struct reg_field ocelot_regfields[] = { + [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11), + [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10), + [ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27), + [ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26), + [ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25), + [ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24), + [ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23), + [ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22), + [ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21), + [ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20), + [ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19), + [ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18), + [ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17), + [ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16), + [ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15), + [ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14), + [ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13), + [ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12), + [ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11), + [ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10), + [ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9), + [ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8), + [ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7), + [ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6), + [ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5), + [ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4), + [ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3), + [ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2), + [ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1), + [ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0), + [ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18), + [ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11), + [ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9), + [QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20), + [QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19), + [QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7), + [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3), + [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0), + [SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2), + [SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1), + [SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0), +}; + +static const struct ocelot_stat_layout ocelot_stats_layout[] = { + { .name = "rx_octets", .offset = 0x00, }, + { .name = "rx_unicast", .offset = 0x01, }, + { .name = "rx_multicast", .offset = 0x02, }, + { .name = "rx_broadcast", .offset = 0x03, }, + { .name = "rx_shorts", .offset = 0x04, }, + { .name = "rx_fragments", .offset = 0x05, }, + { .name = "rx_jabbers", .offset = 0x06, }, + { .name = "rx_crc_align_errs", .offset = 0x07, }, + { .name = "rx_sym_errs", .offset = 0x08, }, + { .name = "rx_frames_below_65_octets", .offset = 0x09, }, + { .name = "rx_frames_65_to_127_octets", .offset = 0x0A, }, + { .name = "rx_frames_128_to_255_octets", .offset = 0x0B, }, + { .name = "rx_frames_256_to_511_octets", .offset = 0x0C, }, + { .name = "rx_frames_512_to_1023_octets", .offset = 0x0D, }, + { .name = "rx_frames_1024_to_1526_octets", .offset = 0x0E, }, + { .name = "rx_frames_over_1526_octets", .offset = 0x0F, }, + { .name = "rx_pause", .offset = 0x10, }, + { .name = "rx_control", .offset = 0x11, }, + { .name = "rx_longs", .offset = 0x12, }, + { .name = "rx_classified_drops", .offset = 0x13, }, + { .name = "rx_red_prio_0", .offset = 0x14, }, + { .name = "rx_red_prio_1", .offset = 0x15, }, + { .name = "rx_red_prio_2", .offset = 0x16, }, + { .name = "rx_red_prio_3", .offset = 0x17, }, + { .name = "rx_red_prio_4", .offset = 0x18, }, + { .name = "rx_red_prio_5", .offset = 0x19, }, + { .name = "rx_red_prio_6", .offset = 0x1A, }, + { .name = "rx_red_prio_7", .offset = 0x1B, }, + { .name = "rx_yellow_prio_0", .offset = 0x1C, }, + { .name = "rx_yellow_prio_1", .offset = 0x1D, }, + { .name = "rx_yellow_prio_2", .offset = 0x1E, }, + { .name = "rx_yellow_prio_3", .offset = 0x1F, }, + { .name = "rx_yellow_prio_4", .offset = 0x20, }, + { .name = "rx_yellow_prio_5", .offset = 0x21, }, + { .name = "rx_yellow_prio_6", .offset = 0x22, }, + { .name = "rx_yellow_prio_7", .offset = 0x23, }, + { .name = "rx_green_prio_0", .offset = 0x24, }, + { .name = "rx_green_prio_1", .offset = 0x25, }, + { .name = "rx_green_prio_2", .offset = 0x26, }, + { .name = "rx_green_prio_3", .offset = 0x27, }, + { .name = "rx_green_prio_4", .offset = 0x28, }, + { .name = "rx_green_prio_5", .offset = 0x29, }, + { .name = "rx_green_prio_6", .offset = 0x2A, }, + { .name = "rx_green_prio_7", .offset = 0x2B, }, + { .name = "tx_octets", .offset = 0x40, }, + { .name = "tx_unicast", .offset = 0x41, }, + { .name = "tx_multicast", .offset = 0x42, }, + { .name = "tx_broadcast", .offset = 0x43, }, + { .name = "tx_collision", .offset = 0x44, }, + { .name = "tx_drops", .offset = 0x45, }, + { .name = "tx_pause", .offset = 0x46, }, + { .name = "tx_frames_below_65_octets", .offset = 0x47, }, + { .name = "tx_frames_65_to_127_octets", .offset = 0x48, }, + { .name = "tx_frames_128_255_octets", .offset = 0x49, }, + { .name = "tx_frames_256_511_octets", .offset = 0x4A, }, + { .name = "tx_frames_512_1023_octets", .offset = 0x4B, }, + { .name = "tx_frames_1024_1526_octets", .offset = 0x4C, }, + { .name = "tx_frames_over_1526_octets", .offset = 0x4D, }, + { .name = "tx_yellow_prio_0", .offset = 0x4E, }, + { .name = "tx_yellow_prio_1", .offset = 0x4F, }, + { .name = "tx_yellow_prio_2", .offset = 0x50, }, + { .name = "tx_yellow_prio_3", .offset = 0x51, }, + { .name = "tx_yellow_prio_4", .offset = 0x52, }, + { .name = "tx_yellow_prio_5", .offset = 0x53, }, + { .name = "tx_yellow_prio_6", .offset = 0x54, }, + { .name = "tx_yellow_prio_7", .offset = 0x55, }, + { .name = "tx_green_prio_0", .offset = 0x56, }, + { .name = "tx_green_prio_1", .offset = 0x57, }, + { .name = "tx_green_prio_2", .offset = 0x58, }, + { .name = "tx_green_prio_3", .offset = 0x59, }, + { .name = "tx_green_prio_4", .offset = 0x5A, }, + { .name = "tx_green_prio_5", .offset = 0x5B, }, + { .name = "tx_green_prio_6", .offset = 0x5C, }, + { .name = "tx_green_prio_7", .offset = 0x5D, }, + { .name = "tx_aged", .offset = 0x5E, }, + { .name = "drop_local", .offset = 0x80, }, + { .name = "drop_tail", .offset = 0x81, }, + { .name = "drop_yellow_prio_0", .offset = 0x82, }, + { .name = "drop_yellow_prio_1", .offset = 0x83, }, + { .name = "drop_yellow_prio_2", .offset = 0x84, }, + { .name = "drop_yellow_prio_3", .offset = 0x85, }, + { .name = "drop_yellow_prio_4", .offset = 0x86, }, + { .name = "drop_yellow_prio_5", .offset = 0x87, }, + { .name = "drop_yellow_prio_6", .offset = 0x88, }, + { .name = "drop_yellow_prio_7", .offset = 0x89, }, + { .name = "drop_green_prio_0", .offset = 0x8A, }, + { .name = "drop_green_prio_1", .offset = 0x8B, }, + { .name = "drop_green_prio_2", .offset = 0x8C, }, + { .name = "drop_green_prio_3", .offset = 0x8D, }, + { .name = "drop_green_prio_4", .offset = 0x8E, }, + { .name = "drop_green_prio_5", .offset = 0x8F, }, + { .name = "drop_green_prio_6", .offset = 0x90, }, + { .name = "drop_green_prio_7", .offset = 0x91, }, +}; + +static void ocelot_pll5_init(struct ocelot *ocelot) +{ + /* Configure PLL5. This will need a proper CCF driver + * The values are coming from the VTSS API for Ocelot + */ + regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4, + HSIO_PLL5G_CFG4_IB_CTRL(0x7600) | + HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8)); + regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0, + HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) | + HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) | + HSIO_PLL5G_CFG0_ENA_BIAS | + HSIO_PLL5G_CFG0_ENA_VCO_BUF | + HSIO_PLL5G_CFG0_ENA_CP1 | + HSIO_PLL5G_CFG0_SELCPI(2) | + HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) | + HSIO_PLL5G_CFG0_SELBGV820(4) | + HSIO_PLL5G_CFG0_DIV4 | + HSIO_PLL5G_CFG0_ENA_CLKTREE | + HSIO_PLL5G_CFG0_ENA_LANE); + regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2, + HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET | + HSIO_PLL5G_CFG2_EN_RESET_OVERRUN | + HSIO_PLL5G_CFG2_GAIN_TEST(0x8) | + HSIO_PLL5G_CFG2_ENA_AMPCTRL | + HSIO_PLL5G_CFG2_PWD_AMPCTRL_N | + HSIO_PLL5G_CFG2_AMPC_SEL(0x10)); +} + +static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops) +{ + int ret; + + ocelot->map = ocelot_regmap; + ocelot->stats_layout = ocelot_stats_layout; + ocelot->num_stats = ARRAY_SIZE(ocelot_stats_layout); + ocelot->shared_queue_sz = 224 * 1024; + ocelot->num_mact_rows = 1024; + ocelot->ops = ops; + + ret = ocelot_regfields_init(ocelot, ocelot_regfields); + if (ret) + return ret; + + ocelot_pll5_init(ocelot); + + eth_random_addr(ocelot->base_mac); + ocelot->base_mac[5] &= 0xf0; + + return 0; +} + +static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info) +{ + u8 llen, wlen; + u64 ifh[2]; + + ifh[0] = be64_to_cpu(((__force __be64 *)_ifh)[0]); + ifh[1] = be64_to_cpu(((__force __be64 *)_ifh)[1]); + + wlen = IFH_EXTRACT_BITFIELD64(ifh[0], 7, 8); + llen = IFH_EXTRACT_BITFIELD64(ifh[0], 15, 6); + + info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80; + + info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32); + + info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4); + + info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1); + info->vid = IFH_EXTRACT_BITFIELD64(ifh[1], 0, 12); + + return 0; +} + +static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh, + u32 *rval) +{ + u32 val; + u32 bytes_valid; + + val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); + if (val == XTR_NOT_READY) { + if (ifh) + return -EIO; + + do { + val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); + } while (val == XTR_NOT_READY); + } + + switch (val) { + case XTR_ABORT: + return -EIO; + case XTR_EOF_0: + case XTR_EOF_1: + case XTR_EOF_2: + case XTR_EOF_3: + case XTR_PRUNED: + bytes_valid = XTR_VALID_BYTES(val); + val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); + if (val == XTR_ESCAPE) + *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); + else + *rval = val; + + return bytes_valid; + case XTR_ESCAPE: + *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); + + return 4; + default: + *rval = val; + + return 4; + } +} + +static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) +{ + struct ocelot *ocelot = arg; + int i = 0, grp = 0; + int err = 0; + + if (!(ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))) + return IRQ_NONE; + + do { + struct skb_shared_hwtstamps *shhwtstamps; + struct ocelot_port_private *priv; + struct ocelot_port *ocelot_port; + u64 tod_in_ns, full_ts_in_ns; + struct frame_info info = {}; + struct net_device *dev; + u32 ifh[4], val, *buf; + struct timespec64 ts; + int sz, len, buf_len; + struct sk_buff *skb; + + for (i = 0; i < OCELOT_TAG_LEN / 4; i++) { + err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]); + if (err != 4) + break; + } + + if (err != 4) + break; + + /* At this point the IFH was read correctly, so it is safe to + * presume that there is no error. The err needs to be reset + * otherwise a frame could come in CPU queue between the while + * condition and the check for error later on. And in that case + * the new frame is just removed and not processed. + */ + err = 0; + + ocelot_parse_ifh(ifh, &info); + + ocelot_port = ocelot->ports[info.port]; + priv = container_of(ocelot_port, struct ocelot_port_private, + port); + dev = priv->dev; + + skb = netdev_alloc_skb(dev, info.len); + + if (unlikely(!skb)) { + netdev_err(dev, "Unable to allocate sk_buff\n"); + err = -ENOMEM; + break; + } + buf_len = info.len - ETH_FCS_LEN; + buf = (u32 *)skb_put(skb, buf_len); + + len = 0; + do { + sz = ocelot_rx_frame_word(ocelot, grp, false, &val); + *buf++ = val; + len += sz; + } while (len < buf_len); + + /* Read the FCS */ + sz = ocelot_rx_frame_word(ocelot, grp, false, &val); + /* Update the statistics if part of the FCS was read before */ + len -= ETH_FCS_LEN - sz; + + if (unlikely(dev->features & NETIF_F_RXFCS)) { + buf = (u32 *)skb_put(skb, ETH_FCS_LEN); + *buf = val; + } + + if (sz < 0) { + err = sz; + break; + } + + if (ocelot->ptp) { + ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); + + tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec); + if ((tod_in_ns & 0xffffffff) < info.timestamp) + full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) | + info.timestamp; + else + full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) | + info.timestamp; + + shhwtstamps = skb_hwtstamps(skb); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = full_ts_in_ns; + } + + /* Everything we see on an interface that is in the HW bridge + * has already been forwarded. + */ + if (ocelot->bridge_mask & BIT(info.port)) + skb->offload_fwd_mark = 1; + + skb->protocol = eth_type_trans(skb, dev); + if (!skb_defer_rx_timestamp(skb)) + netif_rx(skb); + dev->stats.rx_bytes += len; + dev->stats.rx_packets++; + } while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)); + + if (err) + while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) + ocelot_read_rix(ocelot, QS_XTR_RD, grp); + + return IRQ_HANDLED; +} + +static irqreturn_t ocelot_ptp_rdy_irq_handler(int irq, void *arg) +{ + struct ocelot *ocelot = arg; + + ocelot_get_txtstamp(ocelot); + + return IRQ_HANDLED; +} + +static const struct of_device_id mscc_ocelot_match[] = { + { .compatible = "mscc,vsc7514-switch" }, + { } +}; +MODULE_DEVICE_TABLE(of, mscc_ocelot_match); + +static int ocelot_reset(struct ocelot *ocelot) +{ + int retries = 100; + u32 val; + + regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1); + regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); + + do { + msleep(1); + regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], + &val); + } while (val && --retries); + + if (!retries) + return -ETIMEDOUT; + + regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); + regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); + + return 0; +} + +static const struct ocelot_ops ocelot_ops = { + .reset = ocelot_reset, +}; + +static const struct vcap_field vsc7514_vcap_is2_keys[] = { + /* Common: 46 bits */ + [VCAP_IS2_TYPE] = { 0, 4}, + [VCAP_IS2_HK_FIRST] = { 4, 1}, + [VCAP_IS2_HK_PAG] = { 5, 8}, + [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 12}, + [VCAP_IS2_HK_RSV2] = { 25, 1}, + [VCAP_IS2_HK_HOST_MATCH] = { 26, 1}, + [VCAP_IS2_HK_L2_MC] = { 27, 1}, + [VCAP_IS2_HK_L2_BC] = { 28, 1}, + [VCAP_IS2_HK_VLAN_TAGGED] = { 29, 1}, + [VCAP_IS2_HK_VID] = { 30, 12}, + [VCAP_IS2_HK_DEI] = { 42, 1}, + [VCAP_IS2_HK_PCP] = { 43, 3}, + /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ + [VCAP_IS2_HK_L2_DMAC] = { 46, 48}, + [VCAP_IS2_HK_L2_SMAC] = { 94, 48}, + /* MAC_ETYPE (TYPE=000) */ + [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {142, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {158, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {174, 8}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {182, 3}, + /* MAC_LLC (TYPE=001) */ + [VCAP_IS2_HK_MAC_LLC_L2_LLC] = {142, 40}, + /* MAC_SNAP (TYPE=010) */ + [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {142, 40}, + /* MAC_ARP (TYPE=011) */ + [VCAP_IS2_HK_MAC_ARP_SMAC] = { 46, 48}, + [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 94, 1}, + [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 95, 1}, + [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 96, 1}, + [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 97, 1}, + [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 98, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 99, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE] = {100, 2}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = {102, 32}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {134, 32}, + [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {166, 1}, + /* IP4_TCP_UDP / IP4_OTHER common */ + [VCAP_IS2_HK_IP4] = { 46, 1}, + [VCAP_IS2_HK_L3_FRAGMENT] = { 47, 1}, + [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 48, 1}, + [VCAP_IS2_HK_L3_OPTIONS] = { 49, 1}, + [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 50, 1}, + [VCAP_IS2_HK_L3_TOS] = { 51, 8}, + [VCAP_IS2_HK_L3_IP4_DIP] = { 59, 32}, + [VCAP_IS2_HK_L3_IP4_SIP] = { 91, 32}, + [VCAP_IS2_HK_DIP_EQ_SIP] = {123, 1}, + /* IP4_TCP_UDP (TYPE=100) */ + [VCAP_IS2_HK_TCP] = {124, 1}, + [VCAP_IS2_HK_L4_SPORT] = {125, 16}, + [VCAP_IS2_HK_L4_DPORT] = {141, 16}, + [VCAP_IS2_HK_L4_RNG] = {157, 8}, + [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {165, 1}, + [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {166, 1}, + [VCAP_IS2_HK_L4_URG] = {167, 1}, + [VCAP_IS2_HK_L4_ACK] = {168, 1}, + [VCAP_IS2_HK_L4_PSH] = {169, 1}, + [VCAP_IS2_HK_L4_RST] = {170, 1}, + [VCAP_IS2_HK_L4_SYN] = {171, 1}, + [VCAP_IS2_HK_L4_FIN] = {172, 1}, + [VCAP_IS2_HK_L4_1588_DOM] = {173, 8}, + [VCAP_IS2_HK_L4_1588_VER] = {181, 4}, + /* IP4_OTHER (TYPE=101) */ + [VCAP_IS2_HK_IP4_L3_PROTO] = {124, 8}, + [VCAP_IS2_HK_L3_PAYLOAD] = {132, 56}, + /* IP6_STD (TYPE=110) */ + [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 46, 1}, + [VCAP_IS2_HK_L3_IP6_SIP] = { 47, 128}, + [VCAP_IS2_HK_IP6_L3_PROTO] = {175, 8}, + /* OAM (TYPE=111) */ + [VCAP_IS2_HK_OAM_MEL_FLAGS] = {142, 7}, + [VCAP_IS2_HK_OAM_VER] = {149, 5}, + [VCAP_IS2_HK_OAM_OPCODE] = {154, 8}, + [VCAP_IS2_HK_OAM_FLAGS] = {162, 8}, + [VCAP_IS2_HK_OAM_MEPID] = {170, 16}, + [VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = {186, 1}, + [VCAP_IS2_HK_OAM_IS_Y1731] = {187, 1}, +}; + +static const struct vcap_field vsc7514_vcap_is2_actions[] = { + [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, + [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, + [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, + [VCAP_IS2_ACT_MASK_MODE] = { 5, 2}, + [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1}, + [VCAP_IS2_ACT_LRN_DIS] = { 8, 1}, + [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1}, + [VCAP_IS2_ACT_POLICE_IDX] = { 10, 9}, + [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1}, + [VCAP_IS2_ACT_PORT_MASK] = { 20, 11}, + [VCAP_IS2_ACT_REW_OP] = { 31, 9}, + [VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 40, 1}, + [VCAP_IS2_ACT_RSV] = { 41, 2}, + [VCAP_IS2_ACT_ACL_ID] = { 43, 6}, + [VCAP_IS2_ACT_HIT_CNT] = { 49, 32}, +}; + +static const struct vcap_props vsc7514_vcap_props[] = { + [VCAP_IS2] = { + .tg_width = 2, + .sw_count = 4, + .entry_count = VSC7514_VCAP_IS2_CNT, + .entry_width = VSC7514_VCAP_IS2_ENTRY_WIDTH, + .action_count = VSC7514_VCAP_IS2_CNT + + VSC7514_VCAP_PORT_CNT + 2, + .action_width = 99, + .action_type_width = 1, + .action_table = { + [IS2_ACTION_TYPE_NORMAL] = { + .width = 49, + .count = 2 + }, + [IS2_ACTION_TYPE_SMAC_SIP] = { + .width = 6, + .count = 4 + }, + }, + .counter_words = 4, + .counter_width = 32, + }, +}; + +static struct ptp_clock_info ocelot_ptp_clock_info = { + .owner = THIS_MODULE, + .name = "ocelot ptp", + .max_adj = 0x7fffffff, + .n_alarm = 0, + .n_ext_ts = 0, + .n_per_out = OCELOT_PTP_PINS_NUM, + .n_pins = OCELOT_PTP_PINS_NUM, + .pps = 0, + .gettime64 = ocelot_ptp_gettime64, + .settime64 = ocelot_ptp_settime64, + .adjtime = ocelot_ptp_adjtime, + .adjfine = ocelot_ptp_adjfine, + .verify = ocelot_ptp_verify, + .enable = ocelot_ptp_enable, +}; + +static int mscc_ocelot_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *ports, *portnp; + int err, irq_xtr, irq_ptp_rdy; + struct ocelot *ocelot; + struct regmap *hsio; + unsigned int i; + + struct { + enum ocelot_target id; + char *name; + u8 optional:1; + } io_target[] = { + { SYS, "sys" }, + { REW, "rew" }, + { QSYS, "qsys" }, + { ANA, "ana" }, + { QS, "qs" }, + { S2, "s2" }, + { PTP, "ptp", 1 }, + }; + + if (!np && !pdev->dev.platform_data) + return -ENODEV; + + ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL); + if (!ocelot) + return -ENOMEM; + + platform_set_drvdata(pdev, ocelot); + ocelot->dev = &pdev->dev; + + for (i = 0; i < ARRAY_SIZE(io_target); i++) { + struct regmap *target; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + io_target[i].name); + + target = ocelot_regmap_init(ocelot, res); + if (IS_ERR(target)) { + if (io_target[i].optional) { + ocelot->targets[io_target[i].id] = NULL; + continue; + } + return PTR_ERR(target); + } + + ocelot->targets[io_target[i].id] = target; + } + + hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio"); + if (IS_ERR(hsio)) { + dev_err(&pdev->dev, "missing hsio syscon\n"); + return PTR_ERR(hsio); + } + + ocelot->targets[HSIO] = hsio; + + err = ocelot_chip_init(ocelot, &ocelot_ops); + if (err) + return err; + + irq_xtr = platform_get_irq_byname(pdev, "xtr"); + if (irq_xtr < 0) + return -ENODEV; + + err = devm_request_threaded_irq(&pdev->dev, irq_xtr, NULL, + ocelot_xtr_irq_handler, IRQF_ONESHOT, + "frame extraction", ocelot); + if (err) + return err; + + irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy"); + if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) { + err = devm_request_threaded_irq(&pdev->dev, irq_ptp_rdy, NULL, + ocelot_ptp_rdy_irq_handler, + IRQF_ONESHOT, "ptp ready", + ocelot); + if (err) + return err; + + /* Both the PTP interrupt and the PTP bank are available */ + ocelot->ptp = 1; + } + + ports = of_get_child_by_name(np, "ethernet-ports"); + if (!ports) { + dev_err(&pdev->dev, "no ethernet-ports child node found\n"); + return -ENODEV; + } + + ocelot->num_phys_ports = of_get_child_count(ports); + + ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports, + sizeof(struct ocelot_port *), GFP_KERNEL); + + ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys; + ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions; + ocelot->vcap = vsc7514_vcap_props; + + ocelot_init(ocelot); + if (ocelot->ptp) { + err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info); + if (err) { + dev_err(ocelot->dev, + "Timestamp initialization failed\n"); + ocelot->ptp = 0; + } + } + + /* No NPI port */ + ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE, + OCELOT_TAG_PREFIX_NONE); + + for_each_available_child_of_node(ports, portnp) { + struct ocelot_port_private *priv; + struct ocelot_port *ocelot_port; + struct device_node *phy_node; + phy_interface_t phy_mode; + struct phy_device *phy; + struct resource *res; + struct phy *serdes; + void __iomem *regs; + char res_name[8]; + u32 port; + + if (of_property_read_u32(portnp, "reg", &port)) + continue; + + snprintf(res_name, sizeof(res_name), "port%d", port); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + res_name); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + continue; + + phy_node = of_parse_phandle(portnp, "phy-handle", 0); + if (!phy_node) + continue; + + phy = of_phy_find_device(phy_node); + of_node_put(phy_node); + if (!phy) + continue; + + err = ocelot_probe_port(ocelot, port, regs, phy); + if (err) { + of_node_put(portnp); + goto out_put_ports; + } + + ocelot_port = ocelot->ports[port]; + priv = container_of(ocelot_port, struct ocelot_port_private, + port); + + of_get_phy_mode(portnp, &phy_mode); + + ocelot_port->phy_mode = phy_mode; + + switch (ocelot_port->phy_mode) { + case PHY_INTERFACE_MODE_NA: + continue; + case PHY_INTERFACE_MODE_SGMII: + break; + case PHY_INTERFACE_MODE_QSGMII: + /* Ensure clock signals and speed is set on all + * QSGMII links + */ + ocelot_port_writel(ocelot_port, + DEV_CLOCK_CFG_LINK_SPEED + (OCELOT_SPEED_1000), + DEV_CLOCK_CFG); + break; + default: + dev_err(ocelot->dev, + "invalid phy mode for port%d, (Q)SGMII only\n", + port); + of_node_put(portnp); + err = -EINVAL; + goto out_put_ports; + } + + serdes = devm_of_phy_get(ocelot->dev, portnp, NULL); + if (IS_ERR(serdes)) { + err = PTR_ERR(serdes); + if (err == -EPROBE_DEFER) + dev_dbg(ocelot->dev, "deferring probe\n"); + else + dev_err(ocelot->dev, + "missing SerDes phys for port%d\n", + port); + + of_node_put(portnp); + goto out_put_ports; + } + + priv->serdes = serdes; + } + + register_netdevice_notifier(&ocelot_netdevice_nb); + register_switchdev_notifier(&ocelot_switchdev_nb); + register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); + + dev_info(&pdev->dev, "Ocelot switch probed\n"); + +out_put_ports: + of_node_put(ports); + return err; +} + +static int mscc_ocelot_remove(struct platform_device *pdev) +{ + struct ocelot *ocelot = platform_get_drvdata(pdev); + + ocelot_deinit_timestamp(ocelot); + ocelot_deinit(ocelot); + unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); + unregister_switchdev_notifier(&ocelot_switchdev_nb); + unregister_netdevice_notifier(&ocelot_netdevice_nb); + + return 0; +} + +static struct platform_driver mscc_ocelot_driver = { + .probe = mscc_ocelot_probe, + .remove = mscc_ocelot_remove, + .driver = { + .name = "ocelot-switch", + .of_match_table = mscc_ocelot_match, + }, +}; + +module_platform_driver(mscc_ocelot_driver); + +MODULE_DESCRIPTION("Microsemi Ocelot switch driver"); +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index d7340dc09b4c..3af27bb5f4b0 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -1491,7 +1491,7 @@ nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev, nfp_flower_update_merge_stats(app, nfp_flow); flow_stats_update(&flow->stats, priv->stats[ctx_id].bytes, - priv->stats[ctx_id].pkts, priv->stats[ctx_id].used, + priv->stats[ctx_id].pkts, 0, priv->stats[ctx_id].used, FLOW_ACTION_HW_STATS_DELAYED); priv->stats[ctx_id].pkts = 0; diff --git a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c index d18a830e4264..bb327d48d1ab 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c @@ -319,7 +319,7 @@ nfp_flower_stats_rate_limiter(struct nfp_app *app, struct net_device *netdev, prev_stats->bytes = curr_stats->bytes; spin_unlock_bh(&fl_priv->qos_stats_lock); - flow_stats_update(&flow->stats, diff_bytes, diff_pkts, + flow_stats_update(&flow->stats, diff_bytes, diff_pkts, 0, repr_priv->qos_table.last_update, FLOW_ACTION_HW_STATS_DELAYED); return 0; diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index b660ddbe4025..226205099253 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -145,8 +145,8 @@ static const struct { [RTL_GIGA_MAC_VER_50] = {"RTL8168ep/8111ep" }, [RTL_GIGA_MAC_VER_51] = {"RTL8168ep/8111ep" }, [RTL_GIGA_MAC_VER_52] = {"RTL8168fp/RTL8117", FIRMWARE_8168FP_3}, - [RTL_GIGA_MAC_VER_60] = {"RTL8125" }, - [RTL_GIGA_MAC_VER_61] = {"RTL8125", FIRMWARE_8125A_3}, + [RTL_GIGA_MAC_VER_60] = {"RTL8125A" }, + [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3}, }; static const struct pci_device_id rtl8169_pci_tbl[] = { @@ -529,8 +529,6 @@ enum rtl_rx_desc_bit { RxVlanTag = (1 << 16), /* VLAN tag available */ }; -#define RsvdMask 0x3fffc000 - #define RTL_GSO_MAX_SIZE_V1 32000 #define RTL_GSO_MAX_SEGS_V1 24 #define RTL_GSO_MAX_SIZE_V2 64000 @@ -613,7 +611,6 @@ struct rtl8169_private { struct { DECLARE_BITMAP(flags, RTL_FLAG_MAX); - struct mutex mutex; struct work_struct work; } wk; @@ -665,16 +662,6 @@ static inline struct device *tp_to_dev(struct rtl8169_private *tp) return &tp->pci_dev->dev; } -static void rtl_lock_work(struct rtl8169_private *tp) -{ - mutex_lock(&tp->wk.mutex); -} - -static void rtl_unlock_work(struct rtl8169_private *tp) -{ - mutex_unlock(&tp->wk.mutex); -} - static void rtl_lock_config_regs(struct rtl8169_private *tp) { RTL_W8(tp, Cfg9346, Cfg9346_Lock); @@ -1350,10 +1337,8 @@ static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct rtl8169_private *tp = netdev_priv(dev); - rtl_lock_work(tp); wol->supported = WAKE_ANY; wol->wolopts = tp->saved_wolopts; - rtl_unlock_work(tp); } static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) @@ -1424,23 +1409,12 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct rtl8169_private *tp = netdev_priv(dev); - struct device *d = tp_to_dev(tp); if (wol->wolopts & ~WAKE_ANY) return -EINVAL; - pm_runtime_get_noresume(d); - - rtl_lock_work(tp); - tp->saved_wolopts = wol->wolopts; - - if (pm_runtime_active(d)) - __rtl8169_set_wol(tp, tp->saved_wolopts); - - rtl_unlock_work(tp); - - pm_runtime_put_noidle(d); + __rtl8169_set_wol(tp, tp->saved_wolopts); return 0; } @@ -1504,8 +1478,6 @@ static int rtl8169_set_features(struct net_device *dev, { struct rtl8169_private *tp = netdev_priv(dev); - rtl_lock_work(tp); - rtl_set_rx_config_features(tp, features); if (features & NETIF_F_RXCSUM) @@ -1523,8 +1495,6 @@ static int rtl8169_set_features(struct net_device *dev, RTL_W16(tp, CPlusCmd, tp->cp_cmd); rtl_pci_commit(tp); - rtl_unlock_work(tp); - return 0; } @@ -1550,10 +1520,8 @@ static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, u32 *dw = p; int i; - rtl_lock_work(tp); for (i = 0; i < R8169_REGS_SIZE; i += 4) memcpy_fromio(dw++, data++, 4); - rtl_unlock_work(tp); } static const char rtl8169_gstrings[][ETH_GSTRING_LEN] = { @@ -1659,17 +1627,10 @@ static void rtl8169_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct rtl8169_private *tp = netdev_priv(dev); - struct device *d = tp_to_dev(tp); - struct rtl8169_counters *counters = tp->counters; - - ASSERT_RTNL(); - - pm_runtime_get_noresume(d); - - if (pm_runtime_active(d)) - rtl8169_update_counters(tp); + struct rtl8169_counters *counters; - pm_runtime_put_noidle(d); + counters = tp->counters; + rtl8169_update_counters(tp); data[0] = le64_to_cpu(counters->tx_packets); data[1] = le64_to_cpu(counters->rx_packets); @@ -1733,16 +1694,16 @@ struct rtl_coalesce_info { #define COALESCE_DELAY(d) { (d), 8 * (d), 16 * (d), 32 * (d) } static const struct rtl_coalesce_info rtl_coalesce_info_8169[] = { - { SPEED_10, COALESCE_DELAY(40960) }, - { SPEED_100, COALESCE_DELAY(2560) }, { SPEED_1000, COALESCE_DELAY(320) }, + { SPEED_100, COALESCE_DELAY(2560) }, + { SPEED_10, COALESCE_DELAY(40960) }, { 0 }, }; static const struct rtl_coalesce_info rtl_coalesce_info_8168_8136[] = { - { SPEED_10, COALESCE_DELAY(40960) }, - { SPEED_100, COALESCE_DELAY(2560) }, { SPEED_1000, COALESCE_DELAY(5000) }, + { SPEED_100, COALESCE_DELAY(2560) }, + { SPEED_10, COALESCE_DELAY(40960) }, { 0 }, }; #undef COALESCE_DELAY @@ -1758,6 +1719,10 @@ rtl_coalesce_info(struct rtl8169_private *tp) else ci = rtl_coalesce_info_8168_8136; + /* if speed is unknown assume highest one */ + if (tp->phydev->speed == SPEED_UNKNOWN) + return ci; + for (; ci->speed; ci++) { if (tp->phydev->speed == ci->speed) return ci; @@ -1872,8 +1837,6 @@ static int rtl_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) units = DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000U, scale); w |= FIELD_PREP(RTL_COALESCE_RX_USECS, units); - rtl_lock_work(tp); - RTL_W16(tp, IntrMitigate, w); /* Meaning of PktCntrDisable bit changed from RTL8168e-vl */ @@ -1889,56 +1852,32 @@ static int rtl_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) RTL_W16(tp, CPlusCmd, tp->cp_cmd); rtl_pci_commit(tp); - rtl_unlock_work(tp); - return 0; } static int rtl8169_get_eee(struct net_device *dev, struct ethtool_eee *data) { struct rtl8169_private *tp = netdev_priv(dev); - struct device *d = tp_to_dev(tp); - int ret; if (!rtl_supports_eee(tp)) return -EOPNOTSUPP; - pm_runtime_get_noresume(d); - - if (!pm_runtime_active(d)) { - ret = -EOPNOTSUPP; - } else { - ret = phy_ethtool_get_eee(tp->phydev, data); - } - - pm_runtime_put_noidle(d); - - return ret; + return phy_ethtool_get_eee(tp->phydev, data); } static int rtl8169_set_eee(struct net_device *dev, struct ethtool_eee *data) { struct rtl8169_private *tp = netdev_priv(dev); - struct device *d = tp_to_dev(tp); int ret; if (!rtl_supports_eee(tp)) return -EOPNOTSUPP; - pm_runtime_get_noresume(d); - - if (!pm_runtime_active(d)) { - ret = -EOPNOTSUPP; - goto out; - } - ret = phy_ethtool_set_eee(tp->phydev, data); if (!ret) tp->eee_adv = phy_read_mmd(dev->phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); -out: - pm_runtime_put_noidle(d); return ret; } @@ -2130,7 +2069,7 @@ static void rtl8168_config_eee_mac(struct rtl8169_private *tp) rtl_eri_set_bits(tp, 0x1b0, 0x0003); } -static void rtl8125_config_eee_mac(struct rtl8169_private *tp) +static void rtl8125a_config_eee_mac(struct rtl8169_private *tp) { r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0)); r8168_mac_ocp_modify(tp, 0xeb62, 0, BIT(2) | BIT(1)); @@ -2199,8 +2138,6 @@ static void rtl8169_init_phy(struct rtl8169_private *tp) static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr) { - rtl_lock_work(tp); - rtl_unlock_config_regs(tp); RTL_W32(tp, MAC4, addr[4] | addr[5] << 8); @@ -2213,26 +2150,18 @@ static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr) rtl_rar_exgmac_set(tp, addr); rtl_lock_config_regs(tp); - - rtl_unlock_work(tp); } static int rtl_set_mac_address(struct net_device *dev, void *p) { struct rtl8169_private *tp = netdev_priv(dev); - struct device *d = tp_to_dev(tp); int ret; ret = eth_mac_addr(dev, p); if (ret) return ret; - pm_runtime_get_noresume(d); - - if (pm_runtime_active(d)) - rtl_rar_set(tp, dev->dev_addr); - - pm_runtime_put_noidle(d); + rtl_rar_set(tp, dev->dev_addr); return 0; } @@ -2297,10 +2226,14 @@ static void rtl_pll_power_down(struct rtl8169_private *tp) default: break; } + + clk_disable_unprepare(tp->clk); } static void rtl_pll_power_up(struct rtl8169_private *tp) { + clk_prepare_enable(tp->clk); + switch (tp->mac_version) { case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_33: case RTL_GIGA_MAC_VER_37: @@ -3601,15 +3534,15 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp) rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10); - rtl8125_config_eee_mac(tp); + rtl8125a_config_eee_mac(tp); RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); udelay(10); } -static void rtl_hw_start_8125_1(struct rtl8169_private *tp) +static void rtl_hw_start_8125a_1(struct rtl8169_private *tp) { - static const struct ephy_info e_info_8125_1[] = { + static const struct ephy_info e_info_8125a_1[] = { { 0x01, 0xffff, 0xa812 }, { 0x09, 0xffff, 0x520c }, { 0x04, 0xffff, 0xd000 }, @@ -3641,14 +3574,14 @@ static void rtl_hw_start_8125_1(struct rtl8169_private *tp) /* disable aspm and clock request before access ephy */ rtl_hw_aspm_clkreq_enable(tp, false); - rtl_ephy_init(tp, e_info_8125_1); + rtl_ephy_init(tp, e_info_8125a_1); rtl_hw_start_8125_common(tp); } -static void rtl_hw_start_8125_2(struct rtl8169_private *tp) +static void rtl_hw_start_8125a_2(struct rtl8169_private *tp) { - static const struct ephy_info e_info_8125_2[] = { + static const struct ephy_info e_info_8125a_2[] = { { 0x04, 0xffff, 0xd000 }, { 0x0a, 0xffff, 0x8653 }, { 0x23, 0xffff, 0xab66 }, @@ -3668,7 +3601,7 @@ static void rtl_hw_start_8125_2(struct rtl8169_private *tp) /* disable aspm and clock request before access ephy */ rtl_hw_aspm_clkreq_enable(tp, false); - rtl_ephy_init(tp, e_info_8125_2); + rtl_ephy_init(tp, e_info_8125a_2); rtl_hw_start_8125_common(tp); } @@ -3722,8 +3655,8 @@ static void rtl_hw_config(struct rtl8169_private *tp) [RTL_GIGA_MAC_VER_50] = rtl_hw_start_8168ep_2, [RTL_GIGA_MAC_VER_51] = rtl_hw_start_8168ep_3, [RTL_GIGA_MAC_VER_52] = rtl_hw_start_8117, - [RTL_GIGA_MAC_VER_60] = rtl_hw_start_8125_1, - [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125_2, + [RTL_GIGA_MAC_VER_60] = rtl_hw_start_8125a_1, + [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, }; if (hw_configs[tp->mac_version]) @@ -3931,10 +3864,12 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp) netdev_reset_queue(tp->dev); } -static void rtl8169_hw_reset(struct rtl8169_private *tp, bool going_down) +static void rtl8169_cleanup(struct rtl8169_private *tp, bool going_down) { + napi_disable(&tp->napi); + /* Give a racing hard_start_xmit a few cycles to complete. */ - synchronize_rcu(); + synchronize_net(); /* Disable interrupts */ rtl8169_irq_mask_and_ack(tp); @@ -3972,20 +3907,17 @@ no_reset: static void rtl_reset_work(struct rtl8169_private *tp) { - struct net_device *dev = tp->dev; int i; - napi_disable(&tp->napi); - netif_stop_queue(dev); + netif_stop_queue(tp->dev); - rtl8169_hw_reset(tp, false); + rtl8169_cleanup(tp, false); for (i = 0; i < NUM_RX_DESC; i++) rtl8169_mark_to_asic(tp->RxDescArray + i); napi_enable(&tp->napi); rtl_hw_start(tp); - netif_wake_queue(dev); } static void rtl8169_tx_timeout(struct net_device *dev, unsigned int txqueue) @@ -4564,16 +4496,18 @@ static void rtl_task(struct work_struct *work) struct rtl8169_private *tp = container_of(work, struct rtl8169_private, wk.work); - rtl_lock_work(tp); + rtnl_lock(); if (!netif_running(tp->dev) || !test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags)) goto out_unlock; - if (test_and_clear_bit(RTL_FLAG_TASK_RESET_PENDING, tp->wk.flags)) + if (test_and_clear_bit(RTL_FLAG_TASK_RESET_PENDING, tp->wk.flags)) { rtl_reset_work(tp); + netif_wake_queue(tp->dev); + } out_unlock: - rtl_unlock_work(tp); + rtnl_unlock(); } static int rtl8169_poll(struct napi_struct *napi, int budget) @@ -4635,19 +4569,27 @@ static int r8169_phy_connect(struct rtl8169_private *tp) static void rtl8169_down(struct rtl8169_private *tp) { - rtl_lock_work(tp); - /* Clear all task flags */ bitmap_zero(tp->wk.flags, RTL_FLAG_MAX); phy_stop(tp->phydev); - napi_disable(&tp->napi); - rtl8169_hw_reset(tp, true); + rtl8169_update_counters(tp); + + rtl8169_cleanup(tp, true); rtl_pll_power_down(tp); +} + +static void rtl8169_up(struct rtl8169_private *tp) +{ + rtl_pll_power_up(tp); + rtl8169_init_phy(tp); + napi_enable(&tp->napi); + set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags); + rtl_reset_work(tp); - rtl_unlock_work(tp); + phy_start(tp->phydev); } static int rtl8169_close(struct net_device *dev) @@ -4657,9 +4599,6 @@ static int rtl8169_close(struct net_device *dev) pm_runtime_get_sync(&pdev->dev); - /* Update counters before going down */ - rtl8169_update_counters(tp); - netif_stop_queue(dev); rtl8169_down(tp); rtl8169_rx_clear(tp); @@ -4728,25 +4667,10 @@ static int rtl_open(struct net_device *dev) if (retval) goto err_free_irq; - rtl_lock_work(tp); - - set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags); - - napi_enable(&tp->napi); - - rtl8169_init_phy(tp); - - rtl_pll_power_up(tp); - - rtl_hw_start(tp); - + rtl8169_up(tp); rtl8169_init_counter_offsets(tp); - - phy_start(tp->phydev); netif_start_queue(dev); - rtl_unlock_work(tp); - pm_runtime_put_sync(&pdev->dev); out: return retval; @@ -4818,11 +4742,10 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) static void rtl8169_net_suspend(struct rtl8169_private *tp) { - if (!netif_running(tp->dev)) - return; - netif_device_detach(tp->dev); - rtl8169_down(tp); + + if (netif_running(tp->dev)) + rtl8169_down(tp); } #ifdef CONFIG_PM @@ -4831,38 +4754,23 @@ static int __maybe_unused rtl8169_suspend(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); + rtnl_lock(); rtl8169_net_suspend(tp); - clk_disable_unprepare(tp->clk); + rtnl_unlock(); return 0; } -static void __rtl8169_resume(struct rtl8169_private *tp) -{ - netif_device_attach(tp->dev); - - rtl_pll_power_up(tp); - rtl8169_init_phy(tp); - - phy_start(tp->phydev); - - rtl_lock_work(tp); - napi_enable(&tp->napi); - set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags); - rtl_reset_work(tp); - rtl_unlock_work(tp); -} - -static int __maybe_unused rtl8169_resume(struct device *device) +static int rtl8169_resume(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); rtl_rar_set(tp, tp->dev->dev_addr); - clk_prepare_enable(tp->clk); + if (tp->TxDescArray) + rtl8169_up(tp); - if (netif_running(tp->dev)) - __rtl8169_resume(tp); + netif_device_attach(tp->dev); return 0; } @@ -4871,17 +4779,15 @@ static int rtl8169_runtime_suspend(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); - if (!tp->TxDescArray) + if (!tp->TxDescArray) { + netif_device_detach(tp->dev); return 0; + } - rtl_lock_work(tp); + rtnl_lock(); __rtl8169_set_wol(tp, WAKE_PHY); - rtl_unlock_work(tp); - rtl8169_net_suspend(tp); - - /* Update counters before going runtime suspend */ - rtl8169_update_counters(tp); + rtnl_unlock(); return 0; } @@ -4890,18 +4796,9 @@ static int rtl8169_runtime_resume(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); - rtl_rar_set(tp, tp->dev->dev_addr); - - if (!tp->TxDescArray) - return 0; - - rtl_lock_work(tp); __rtl8169_set_wol(tp, tp->saved_wolopts); - rtl_unlock_work(tp); - __rtl8169_resume(tp); - - return 0; + return rtl8169_resume(device); } static int rtl8169_runtime_idle(struct device *device) @@ -4943,7 +4840,9 @@ static void rtl_shutdown(struct pci_dev *pdev) { struct rtl8169_private *tp = pci_get_drvdata(pdev); + rtnl_lock(); rtl8169_net_suspend(tp); + rtnl_unlock(); /* Restore original MAC address */ rtl_rar_set(tp, tp->dev->perm_addr); @@ -5349,7 +5248,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return rc; } - mutex_init(&tp->wk.mutex); INIT_WORK(&tp->wk.work, rtl_task); u64_stats_init(&tp->rx_stats.syncp); u64_stats_init(&tp->tx_stats.syncp); @@ -5435,8 +5333,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) jumbo_max, tp->mac_version <= RTL_GIGA_MAC_VER_06 ? "ok" : "ko"); - if (r8168_check_dash(tp)) + if (r8168_check_dash(tp)) { + netdev_info(dev, "DASH enabled\n"); rtl8168_driver_start(tp); + } if (pci_dev_run_wake(pdev)) pm_runtime_put_sync(&pdev->dev); diff --git a/drivers/net/ethernet/realtek/r8169_phy_config.c b/drivers/net/ethernet/realtek/r8169_phy_config.c index b73f7d023e99..0cf4893e5624 100644 --- a/drivers/net/ethernet/realtek/r8169_phy_config.c +++ b/drivers/net/ethernet/realtek/r8169_phy_config.c @@ -89,7 +89,7 @@ static void rtl8168h_config_eee_phy(struct phy_device *phydev) phy_modify_paged(phydev, 0xa42, 0x14, 0x0000, 0x0080); } -static void rtl8125_config_eee_phy(struct phy_device *phydev) +static void rtl8125a_config_eee_phy(struct phy_device *phydev) { rtl8168h_config_eee_phy(phydev); @@ -1140,8 +1140,8 @@ static void rtl8106e_hw_phy_config(struct rtl8169_private *tp, rtl_writephy_batch(phydev, phy_reg_init); } -static void rtl8125_1_hw_phy_config(struct rtl8169_private *tp, - struct phy_device *phydev) +static void rtl8125a_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) { phy_modify_paged(phydev, 0xad4, 0x10, 0x03ff, 0x0084); phy_modify_paged(phydev, 0xad4, 0x17, 0x0000, 0x0010); @@ -1175,11 +1175,11 @@ static void rtl8125_1_hw_phy_config(struct rtl8169_private *tp, phy_modify_paged(phydev, 0xa5c, 0x10, 0x0400, 0x0000); rtl8168g_enable_gphy_10m(phydev); - rtl8125_config_eee_phy(phydev); + rtl8125a_config_eee_phy(phydev); } -static void rtl8125_2_hw_phy_config(struct rtl8169_private *tp, - struct phy_device *phydev) +static void rtl8125a_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) { int i; @@ -1240,7 +1240,7 @@ static void rtl8125_2_hw_phy_config(struct rtl8169_private *tp, phy_modify_paged(phydev, 0xa86, 0x15, 0x0001, 0x0000); rtl8168g_enable_gphy_10m(phydev); - rtl8125_config_eee_phy(phydev); + rtl8125a_config_eee_phy(phydev); } void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, @@ -1300,8 +1300,8 @@ void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, [RTL_GIGA_MAC_VER_50] = rtl8168ep_2_hw_phy_config, [RTL_GIGA_MAC_VER_51] = rtl8168ep_2_hw_phy_config, [RTL_GIGA_MAC_VER_52] = rtl8117_hw_phy_config, - [RTL_GIGA_MAC_VER_60] = rtl8125_1_hw_phy_config, - [RTL_GIGA_MAC_VER_61] = rtl8125_2_hw_phy_config, + [RTL_GIGA_MAC_VER_60] = rtl8125a_1_hw_phy_config, + [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, }; if (phy_configs[ver]) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 234e8b6816ce..544bc621146c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -491,6 +491,10 @@ static const struct of_device_id meson8b_dwmac_match[] = { .compatible = "amlogic,meson-axg-dwmac", .data = &meson_axg_dwmac_data, }, + { + .compatible = "amlogic,meson-g12a-dwmac", + .data = &meson_axg_dwmac_data, + }, { } }; MODULE_DEVICE_TABLE(of, meson8b_dwmac_match); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c index e6696495f126..e113b1376fdd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c @@ -1094,7 +1094,7 @@ static int stmmac_test_rxp(struct stmmac_priv *priv) if (!priv->dma_cap.frpsel) return -EOPNOTSUPP; - sel = kzalloc(sizeof(*sel) + nk * sizeof(struct tc_u32_key), GFP_KERNEL); + sel = kzalloc(struct_size(sel, keys, nk), GFP_KERNEL); if (!sel) return -ENOMEM; diff --git a/drivers/net/ethernet/ti/am65-cpsw-qos.c b/drivers/net/ethernet/ti/am65-cpsw-qos.c index 32eac04468bb..3bdd4dbcd2ff 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-qos.c +++ b/drivers/net/ethernet/ti/am65-cpsw-qos.c @@ -505,7 +505,6 @@ static int am65_cpsw_set_taprio(struct net_device *ndev, void *type_data) struct am65_cpsw_port *port = am65_ndev_to_port(ndev); struct tc_taprio_qopt_offload *taprio = type_data; struct am65_cpsw_est *est_new; - size_t size; int ret = 0; if (taprio->cycle_time_extension) { @@ -513,10 +512,9 @@ static int am65_cpsw_set_taprio(struct net_device *ndev, void *type_data) return -EOPNOTSUPP; } - size = sizeof(struct tc_taprio_sched_entry) * taprio->num_entries + - sizeof(struct am65_cpsw_est); - - est_new = devm_kzalloc(&ndev->dev, size, GFP_KERNEL); + est_new = devm_kzalloc(&ndev->dev, + struct_size(est_new, taprio.entries, taprio->num_entries), + GFP_KERNEL); if (!est_new) return -ENOMEM; diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index 480ab7251515..3e3883ad88b0 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1473,7 +1473,7 @@ do_reset(struct net_device *dev, int full) unsigned int ioaddr = dev->base_addr; unsigned value; - pr_debug("%s: do_reset(%p,%d)\n", dev? dev->name:"eth?", dev, full); + pr_debug("%s: do_reset(%p,%d)\n", dev->name, dev, full); hardreset(dev); PutByte(XIRCREG_CR, SoftReset); /* set */ diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c index 1dd19d0cb269..37643c468e19 100644 --- a/drivers/net/phy/dp83822.c +++ b/drivers/net/phy/dp83822.c @@ -26,7 +26,9 @@ #define MII_DP83822_PHYSCR 0x11 #define MII_DP83822_MISR1 0x12 #define MII_DP83822_MISR2 0x13 +#define MII_DP83822_RCSR 0x17 #define MII_DP83822_RESET_CTRL 0x1f +#define MII_DP83822_GENCFG 0x465 #define DP83822_HW_RESET BIT(15) #define DP83822_SW_RESET BIT(14) @@ -77,6 +79,10 @@ #define DP83822_WOL_INDICATION_SEL BIT(8) #define DP83822_WOL_CLR_INDICATION BIT(11) +/* RSCR bits */ +#define DP83822_RX_CLK_SHIFT BIT(12) +#define DP83822_TX_CLK_SHIFT BIT(11) + static int dp83822_ack_interrupt(struct phy_device *phydev) { int err; @@ -255,7 +261,7 @@ static int dp83822_config_intr(struct phy_device *phydev) return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status); } -static int dp83822_config_init(struct phy_device *phydev) +static int dp8382x_disable_wol(struct phy_device *phydev) { int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON; @@ -264,6 +270,46 @@ static int dp83822_config_init(struct phy_device *phydev) MII_DP83822_WOL_CFG, value); } +static int dp83822_config_init(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int rgmii_delay; + s32 rx_int_delay; + s32 tx_int_delay; + int err = 0; + + if (phy_interface_is_rgmii(phydev)) { + rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, + true); + + if (rx_int_delay <= 0) + rgmii_delay = 0; + else + rgmii_delay = DP83822_RX_CLK_SHIFT; + + tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, + false); + if (tx_int_delay <= 0) + rgmii_delay &= ~DP83822_TX_CLK_SHIFT; + else + rgmii_delay |= DP83822_TX_CLK_SHIFT; + + if (rgmii_delay) { + err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RCSR, rgmii_delay); + if (err) + return err; + } + } + + return dp8382x_disable_wol(phydev); +} + +static int dp8382x_config_init(struct phy_device *phydev) +{ + return dp8382x_disable_wol(phydev); +} + static int dp83822_phy_reset(struct phy_device *phydev) { int err; @@ -272,9 +318,7 @@ static int dp83822_phy_reset(struct phy_device *phydev) if (err < 0) return err; - dp83822_config_init(phydev); - - return 0; + return phydev->drv->config_init(phydev); } static int dp83822_suspend(struct phy_device *phydev) @@ -318,14 +362,29 @@ static int dp83822_resume(struct phy_device *phydev) .resume = dp83822_resume, \ } +#define DP8382X_PHY_DRIVER(_id, _name) \ + { \ + PHY_ID_MATCH_MODEL(_id), \ + .name = (_name), \ + /* PHY_BASIC_FEATURES */ \ + .soft_reset = dp83822_phy_reset, \ + .config_init = dp8382x_config_init, \ + .get_wol = dp83822_get_wol, \ + .set_wol = dp83822_set_wol, \ + .ack_interrupt = dp83822_ack_interrupt, \ + .config_intr = dp83822_config_intr, \ + .suspend = dp83822_suspend, \ + .resume = dp83822_resume, \ + } + static struct phy_driver dp83822_driver[] = { DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"), - DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), - DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), - DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), - DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), - DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), - DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), + DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), + DP8382X_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), + DP8382X_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), + DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), + DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), + DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), }; module_phy_driver(dp83822_driver); diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 53ed3abc26c9..58103152c601 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -64,6 +64,10 @@ #define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) #define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) +/* RGMIIDCTL */ +#define DP83869_RGMII_CLK_DELAY_SHIFT 4 +#define DP83869_CLK_DELAY_DEF 7 + /* STRAP_STS1 bits */ #define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) #define DP83869_STRAP_STS1_RESERVED BIT(11) @@ -78,9 +82,6 @@ #define DP83869_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 12) #define DP83869_PHYCR_RESERVED_MASK BIT(11) -/* RGMIIDCTL bits */ -#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4 - /* IO_MUX_CFG bits */ #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f @@ -108,6 +109,8 @@ enum { struct dp83869_private { int tx_fifo_depth; int rx_fifo_depth; + s32 rx_int_delay; + s32 tx_int_delay; int io_impedance; int port_mirroring; bool rxctrl_strap_quirk; @@ -177,11 +180,16 @@ static int dp83869_set_strapped_mode(struct phy_device *phydev) } #if IS_ENABLED(CONFIG_OF_MDIO) +static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, + 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000}; + static int dp83869_of_init(struct phy_device *phydev) { struct dp83869_private *dp83869 = phydev->priv; struct device *dev = &phydev->mdio.dev; struct device_node *of_node = dev->of_node; + int delay_size = ARRAY_SIZE(dp83869_internal_delay); int ret; if (!of_node) @@ -235,6 +243,20 @@ static int dp83869_of_init(struct phy_device *phydev) &dp83869->tx_fifo_depth)) dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB; + dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev, + &dp83869_internal_delay[0], + delay_size, true); + if (dp83869->rx_int_delay < 0) + dp83869->rx_int_delay = + dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; + + dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev, + &dp83869_internal_delay[0], + delay_size, false); + if (dp83869->tx_int_delay < 0) + dp83869->tx_int_delay = + dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; + return ret; } #else @@ -397,6 +419,31 @@ static int dp83869_config_init(struct phy_device *phydev) dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT); + if (phy_interface_is_rgmii(phydev)) { + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, + dp83869->rx_int_delay | + dp83869->tx_int_delay << DP83869_RGMII_CLK_DELAY_SHIFT); + if (ret) + return ret; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); + val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + val |= (DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + val |= DP83869_RGMII_TX_CLK_DELAY_EN; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + val |= DP83869_RGMII_RX_CLK_DELAY_EN; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, + val); + } + return ret; } diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index c9ecf3c8c3fd..bb86ac0bd092 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -2625,12 +2625,12 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1101", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &marvell_config_init, - .config_aneg = &m88e1101_config_aneg, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = marvell_config_init, + .config_aneg = m88e1101_config_aneg, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2643,12 +2643,12 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1112", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1111_config_init, - .config_aneg = &marvell_config_aneg, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1111_config_init, + .config_aneg = marvell_config_aneg, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2663,13 +2663,13 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1111", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1111_config_init, - .config_aneg = &marvell_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1111_config_init, + .config_aneg = marvell_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2684,12 +2684,12 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1118", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1118_config_init, - .config_aneg = &m88e1118_config_aneg, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1118_config_init, + .config_aneg = m88e1118_config_aneg, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2701,15 +2701,15 @@ static struct phy_driver marvell_drivers[] = { .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1121R", /* PHY_GBIT_FEATURES */ - .probe = &m88e1121_probe, - .config_init = &marvell_config_init, - .config_aneg = &m88e1121_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .did_interrupt = &m88e1121_did_interrupt, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .probe = m88e1121_probe, + .config_init = marvell_config_init, + .config_aneg = m88e1121_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2724,16 +2724,16 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1318S", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1318_config_init, - .config_aneg = &m88e1318_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .did_interrupt = &m88e1121_did_interrupt, - .get_wol = &m88e1318_get_wol, - .set_wol = &m88e1318_set_wol, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1318_config_init, + .config_aneg = m88e1318_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .get_wol = m88e1318_get_wol, + .set_wol = m88e1318_set_wol, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2746,13 +2746,13 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1145", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1145_config_init, - .config_aneg = &m88e1101_config_aneg, - .read_status = &genphy_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1145_config_init, + .config_aneg = m88e1101_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2767,12 +2767,12 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1149R", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1149_config_init, - .config_aneg = &m88e1118_config_aneg, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1149_config_init, + .config_aneg = m88e1118_config_aneg, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2785,12 +2785,12 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1240", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1111_config_init, - .config_aneg = &marvell_config_aneg, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1111_config_init, + .config_aneg = marvell_config_aneg, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2803,11 +2803,11 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1116R", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = &m88e1116r_config_init, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e1116r_config_init, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2822,17 +2822,17 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1510", .features = PHY_GBIT_FIBRE_FEATURES, .flags = PHY_POLL_CABLE_TEST, - .probe = &m88e1510_probe, - .config_init = &m88e1510_config_init, - .config_aneg = &m88e1510_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .did_interrupt = &m88e1121_did_interrupt, - .get_wol = &m88e1318_get_wol, - .set_wol = &m88e1318_set_wol, - .resume = &marvell_resume, - .suspend = &marvell_suspend, + .probe = m88e1510_probe, + .config_init = m88e1510_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .get_wol = m88e1318_get_wol, + .set_wol = m88e1318_set_wol, + .resume = marvell_resume, + .suspend = marvell_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2852,14 +2852,14 @@ static struct phy_driver marvell_drivers[] = { /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, .probe = m88e1510_probe, - .config_init = &marvell_config_init, - .config_aneg = &m88e1510_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .did_interrupt = &m88e1121_did_interrupt, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = marvell_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2878,14 +2878,14 @@ static struct phy_driver marvell_drivers[] = { .probe = m88e1510_probe, /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, - .config_init = &marvell_config_init, - .config_aneg = &m88e1510_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .did_interrupt = &m88e1121_did_interrupt, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = marvell_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2903,14 +2903,14 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E3016", /* PHY_BASIC_FEATURES */ .probe = marvell_probe, - .config_init = &m88e3016_config_init, - .aneg_done = &marvell_aneg_done, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .did_interrupt = &m88e1121_did_interrupt, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = m88e3016_config_init, + .aneg_done = marvell_aneg_done, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2924,14 +2924,14 @@ static struct phy_driver marvell_drivers[] = { /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, .probe = m88e6390_probe, - .config_init = &marvell_config_init, - .config_aneg = &m88e6390_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .did_interrupt = &m88e1121_did_interrupt, - .resume = &genphy_resume, - .suspend = &genphy_suspend, + .config_init = marvell_config_init, + .config_aneg = m88e6390_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, .read_page = marvell_read_page, .write_page = marvell_write_page, .get_sset_count = marvell_get_sset_count, @@ -2943,6 +2943,50 @@ static struct phy_driver marvell_drivers[] = { .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, .cable_test_get_status = marvell_vct7_cable_test_get_status, }, + { + .phy_id = MARVELL_PHY_ID_88E1340S, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1340S", + .probe = m88e1510_probe, + /* PHY_GBIT_FEATURES */ + .config_init = marvell_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, + .read_page = marvell_read_page, + .write_page = marvell_write_page, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, + .get_tunable = m88e1540_get_tunable, + .set_tunable = m88e1540_set_tunable, + }, + { + .phy_id = MARVELL_PHY_ID_88E1548P, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1548P", + .probe = m88e1510_probe, + .features = PHY_GBIT_FIBRE_FEATURES, + .config_init = marvell_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, + .ack_interrupt = marvell_ack_interrupt, + .config_intr = marvell_config_intr, + .did_interrupt = m88e1121_did_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, + .read_page = marvell_read_page, + .write_page = marvell_write_page, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, + .get_tunable = m88e1540_get_tunable, + .set_tunable = m88e1540_set_tunable, + }, }; module_phy_driver(marvell_drivers); @@ -2963,6 +3007,8 @@ static struct mdio_device_id __maybe_unused marvell_tbl[] = { { MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK }, { MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK }, { MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1340S, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1548P, MARVELL_PHY_ID_MASK }, { } }; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 6ceee82b2839..ab9233c558d8 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -739,10 +739,24 @@ EXPORT_SYMBOL(mdiobus_free); */ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) { - struct phy_device *phydev; + struct phy_device *phydev = ERR_PTR(-ENODEV); int err; - phydev = get_phy_device(bus, addr, false); + switch (bus->probe_capabilities) { + case MDIOBUS_NO_CAP: + case MDIOBUS_C22: + phydev = get_phy_device(bus, addr, false); + break; + case MDIOBUS_C45: + phydev = get_phy_device(bus, addr, true); + break; + case MDIOBUS_C22_C45: + phydev = get_phy_device(bus, addr, false); + if (IS_ERR(phydev)) + phydev = get_phy_device(bus, addr, true); + break; + } + if (IS_ERR(phydev)) return phydev; diff --git a/drivers/net/phy/mscc/Makefile b/drivers/net/phy/mscc/Makefile index 10af42cd9839..d8e22a4eeeff 100644 --- a/drivers/net/phy/mscc/Makefile +++ b/drivers/net/phy/mscc/Makefile @@ -8,3 +8,7 @@ mscc-objs := mscc_main.o ifdef CONFIG_MACSEC mscc-objs += mscc_macsec.o endif + +ifdef CONFIG_NETWORK_PHY_TIMESTAMPING +mscc-objs += mscc_ptp.o +endif diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h index fbcee5fce7b2..9481bce94c2e 100644 --- a/drivers/net/phy/mscc/mscc.h +++ b/drivers/net/phy/mscc/mscc.h @@ -133,6 +133,7 @@ enum rgmii_clock_delay { * in the same package. */ #define MSCC_PHY_PAGE_EXTENDED_GPIO 0x0010 /* Extended reg - GPIO */ +#define MSCC_PHY_PAGE_1588 0x1588 /* PTP (1588) */ #define MSCC_PHY_PAGE_TEST 0x2a30 /* Test reg */ #define MSCC_PHY_PAGE_TR 0x52b5 /* Token ring registers */ @@ -252,6 +253,7 @@ enum rgmii_clock_delay { /* Test page Registers */ #define MSCC_PHY_TEST_PAGE_5 5 #define MSCC_PHY_TEST_PAGE_8 8 +#define TR_CLK_DISABLE 0x8000 #define MSCC_PHY_TEST_PAGE_9 9 #define MSCC_PHY_TEST_PAGE_20 20 #define MSCC_PHY_TEST_PAGE_24 24 @@ -372,6 +374,35 @@ struct vsc8531_private { unsigned long ingr_flows; unsigned long egr_flows; #endif + + struct mii_timestamper mii_ts; + + bool input_clk_init; + struct vsc85xx_ptp *ptp; + /* LOAD/SAVE GPIO pin, used for retrieving or setting time to the PHC. */ + struct gpio_desc *load_save; + + /* For multiple port PHYs; the MDIO address of the base PHY in the + * pair of two PHYs that share a 1588 engine. PHY0 and PHY2 are coupled. + * PHY1 and PHY3 as well. PHY0 and PHY1 are base PHYs for their + * respective pair. + */ + unsigned int ts_base_addr; + u8 ts_base_phy; + + /* ts_lock: used for per-PHY timestamping operations. + * phc_lock: used for per-PHY PHC opertations. + */ + struct mutex ts_lock; + struct mutex phc_lock; +}; + +/* Shared structure between the PHYs of the same package. + * gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO + * is shared. + */ +struct vsc85xx_shared_private { + struct mutex gpio_lock; }; #if IS_ENABLED(CONFIG_OF_MDIO) @@ -398,4 +429,36 @@ static inline void vsc8584_config_macsec_intr(struct phy_device *phydev) } #endif +#if IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING) +void vsc85xx_link_change_notify(struct phy_device *phydev); +void vsc8584_config_ts_intr(struct phy_device *phydev); +int vsc8584_ptp_init(struct phy_device *phydev); +int vsc8584_ptp_probe_once(struct phy_device *phydev); +int vsc8584_ptp_probe(struct phy_device *phydev); +irqreturn_t vsc8584_handle_ts_interrupt(struct phy_device *phydev); +#else +static inline void vsc85xx_link_change_notify(struct phy_device *phydev) +{ +} +static inline void vsc8584_config_ts_intr(struct phy_device *phydev) +{ +} +static inline int vsc8584_ptp_init(struct phy_device *phydev) +{ + return 0; +} +static inline int vsc8584_ptp_probe_once(struct phy_device *phydev) +{ + return 0; +} +static inline int vsc8584_ptp_probe(struct phy_device *phydev) +{ + return 0; +} +static inline irqreturn_t vsc8584_handle_ts_interrupt(struct phy_device *phydev) +{ + return IRQ_NONE; +} +#endif + #endif /* _MSCC_PHY_H_ */ diff --git a/drivers/net/phy/mscc/mscc_fc_buffer.h b/drivers/net/phy/mscc/mscc_fc_buffer.h index 3803e826c37d..399e803395a5 100644 --- a/drivers/net/phy/mscc/mscc_fc_buffer.h +++ b/drivers/net/phy/mscc/mscc_fc_buffer.h @@ -2,7 +2,7 @@ /* * Driver for Microsemi VSC85xx PHYs * - * Copyright (C) 2019 Microsemi Corporation + * Copyright (C) 2020 Microsemi Corporation */ #ifndef _MSCC_PHY_FC_BUFFER_H_ diff --git a/drivers/net/phy/mscc/mscc_mac.h b/drivers/net/phy/mscc/mscc_mac.h index 59b6837c60b3..8dd38dc6edbf 100644 --- a/drivers/net/phy/mscc/mscc_mac.h +++ b/drivers/net/phy/mscc/mscc_mac.h @@ -2,7 +2,7 @@ /* * Driver for Microsemi VSC85xx PHYs * - * Copyright (c) 2017 Microsemi Corporation + * Copyright (c) 2020 Microsemi Corporation */ #ifndef _MSCC_PHY_LINE_MAC_H_ diff --git a/drivers/net/phy/mscc/mscc_macsec.c b/drivers/net/phy/mscc/mscc_macsec.c index d53ca884b5c9..1d4c012194e9 100644 --- a/drivers/net/phy/mscc/mscc_macsec.c +++ b/drivers/net/phy/mscc/mscc_macsec.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: (GPL-2.0 OR MIT) /* - * Driver for Microsemi VSC85xx PHYs + * Driver for Microsemi VSC85xx PHYs - MACsec support * - * Author: Nagaraju Lakkaraju + * Author: Antoine Tenart * License: Dual MIT/GPL - * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2020 Microsemi Corporation */ #include <linux/phy.h> @@ -285,7 +285,9 @@ static void vsc8584_macsec_mac_init(struct phy_device *phydev, MSCC_MAC_CFG_PKTINF_CFG_STRIP_PREAMBLE_ENA | MSCC_MAC_CFG_PKTINF_CFG_INSERT_PREAMBLE_ENA | (bank == HOST_MAC ? - MSCC_MAC_CFG_PKTINF_CFG_ENABLE_TX_PADDING : 0)); + MSCC_MAC_CFG_PKTINF_CFG_ENABLE_TX_PADDING : 0) | + (IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING) ? + MSCC_MAC_CFG_PKTINF_CFG_MACSEC_BYPASS_NUM_PTP_STALL_CLKS(0x8) : 0)); val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_MODE_CFG); val &= ~MSCC_MAC_CFG_MODE_CFG_DISABLE_DIC; @@ -383,21 +385,23 @@ static void vsc8584_macsec_flow(struct phy_device *phydev, } if (bank == MACSEC_INGR && flow->match.sci && flow->rx_sa->sc->sci) { + u64 sci = (__force u64)flow->rx_sa->sc->sci; + match |= MSCC_MS_SAM_MISC_MATCH_TCI(BIT(3)); mask |= MSCC_MS_SAM_MASK_TCI_MASK(BIT(3)) | MSCC_MS_SAM_MASK_SCI_MASK; vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_LO(idx), - lower_32_bits(flow->rx_sa->sc->sci)); + lower_32_bits(sci)); vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_HI(idx), - upper_32_bits(flow->rx_sa->sc->sci)); + upper_32_bits(sci)); } if (flow->match.etype) { mask |= MSCC_MS_SAM_MASK_MAC_ETYPE_MASK; vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MAC_SA_MATCH_HI(idx), - MSCC_MS_SAM_MAC_SA_MATCH_HI_ETYPE(htons(flow->etype))); + MSCC_MS_SAM_MAC_SA_MATCH_HI_ETYPE((__force u32)htons(flow->etype))); } match |= MSCC_MS_SAM_MISC_MATCH_PRIORITY(flow->priority); @@ -521,7 +525,7 @@ static int vsc8584_macsec_transformation(struct phy_device *phydev, int i, ret, index = flow->index; u32 rec = 0, control = 0; u8 hkey[16]; - sci_t sci; + u64 sci; ret = vsc8584_macsec_derive_key(flow->key, priv->secy->key_len, hkey); if (ret) @@ -579,7 +583,7 @@ static int vsc8584_macsec_transformation(struct phy_device *phydev, priv->secy->replay_window); /* Set the input vectors */ - sci = bank == MACSEC_INGR ? flow->rx_sa->sc->sci : priv->secy->sci; + sci = (__force u64)(bank == MACSEC_INGR ? flow->rx_sa->sc->sci : priv->secy->sci); vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), lower_32_bits(sci)); vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), diff --git a/drivers/net/phy/mscc/mscc_macsec.h b/drivers/net/phy/mscc/mscc_macsec.h index d751f2946b79..9c6d25e36de2 100644 --- a/drivers/net/phy/mscc/mscc_macsec.h +++ b/drivers/net/phy/mscc/mscc_macsec.h @@ -2,7 +2,7 @@ /* * Driver for Microsemi VSC85xx PHYs * - * Copyright (c) 2018 Microsemi Corporation + * Copyright (c) 2020 Microsemi Corporation */ #ifndef _MSCC_PHY_MACSEC_H_ diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index 5ddc44f87eaf..a4fbf3a4fa97 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -629,7 +629,7 @@ static int vsc8531_pre_init_seq_set(struct phy_device *phydev) if (rc < 0) return rc; rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_TEST, - MSCC_PHY_TEST_PAGE_8, 0x8000, 0x8000); + MSCC_PHY_TEST_PAGE_8, TR_CLK_DISABLE, TR_CLK_DISABLE); if (rc < 0) return rc; @@ -1026,7 +1026,7 @@ static int vsc8574_config_pre_init(struct phy_device *phydev) phy_base_write(phydev, MSCC_PHY_TEST_PAGE_5, 0x1b20); reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); - reg |= 0x8000; + reg |= TR_CLK_DISABLE; phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); @@ -1046,7 +1046,7 @@ static int vsc8574_config_pre_init(struct phy_device *phydev) phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); - reg &= ~0x8000; + reg &= ~TR_CLK_DISABLE; phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); @@ -1196,7 +1196,7 @@ static int vsc8584_config_pre_init(struct phy_device *phydev) phy_base_write(phydev, MSCC_PHY_TEST_PAGE_5, 0x1f20); reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); - reg |= 0x8000; + reg |= TR_CLK_DISABLE; phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); @@ -1225,7 +1225,7 @@ static int vsc8584_config_pre_init(struct phy_device *phydev) phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); - reg &= ~0x8000; + reg &= ~TR_CLK_DISABLE; phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg); phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); @@ -1288,7 +1288,7 @@ static void vsc8584_get_base_addr(struct phy_device *phydev) struct vsc8531_private *vsc8531 = phydev->priv; u16 val, addr; - mutex_lock(&phydev->mdio.bus->mdio_lock); + phy_lock_mdio_bus(phydev); __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED); addr = __phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_4); @@ -1297,12 +1297,28 @@ static void vsc8584_get_base_addr(struct phy_device *phydev) val = __phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); - if (val & PHY_ADDR_REVERSED) + /* In the package, there are two pairs of PHYs (PHY0 + PHY2 and + * PHY1 + PHY3). The first PHY of each pair (PHY0 and PHY1) is + * the base PHY for timestamping operations. + */ + vsc8531->ts_base_addr = phydev->mdio.addr; + vsc8531->ts_base_phy = addr; + + if (val & PHY_ADDR_REVERSED) { vsc8531->base_addr = phydev->mdio.addr + addr; - else + if (addr > 1) { + vsc8531->ts_base_addr += 2; + vsc8531->ts_base_phy += 2; + } + } else { vsc8531->base_addr = phydev->mdio.addr - addr; + if (addr > 1) { + vsc8531->ts_base_addr -= 2; + vsc8531->ts_base_phy -= 2; + } + } vsc8531->addr = addr; } @@ -1315,7 +1331,7 @@ static int vsc8584_config_init(struct phy_device *phydev) phydev->mdix_ctrl = ETH_TP_MDI_AUTO; - mutex_lock(&phydev->mdio.bus->mdio_lock); + phy_lock_mdio_bus(phydev); /* Some parts of the init sequence are identical for every PHY in the * package. Some parts are modifying the GPIO register bank which is a @@ -1359,8 +1375,10 @@ static int vsc8584_config_init(struct phy_device *phydev) goto err; } - phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, - MSCC_PHY_PAGE_EXTENDED_GPIO); + ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_EXTENDED_GPIO); + if (ret) + goto err; val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); val &= ~MAC_CFG_MASK; @@ -1379,6 +1397,11 @@ static int vsc8584_config_init(struct phy_device *phydev) if (ret) goto err; + ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STANDARD); + if (ret) + goto err; + if (!phy_interface_is_rgmii(phydev)) { val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT | PROC_CMD_READ_MOD_WRITE_PORT; @@ -1412,13 +1435,15 @@ static int vsc8584_config_init(struct phy_device *phydev) if (ret) goto err; - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); ret = vsc8584_macsec_init(phydev); if (ret) return ret; - phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); + ret = vsc8584_ptp_init(phydev); + if (ret) + return ret; val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1); val &= ~(MEDIA_OP_MODE_MASK | VSC8584_MAC_IF_SELECTION_MASK); @@ -1449,18 +1474,26 @@ static int vsc8584_config_init(struct phy_device *phydev) return 0; err: - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); return ret; } static irqreturn_t vsc8584_handle_interrupt(struct phy_device *phydev) { + irqreturn_t ret; int irq_status; irq_status = phy_read(phydev, MII_VSC85XX_INT_STATUS); - if (irq_status < 0 || !(irq_status & MII_VSC85XX_INT_MASK_MASK)) + if (irq_status < 0) return IRQ_NONE; + /* Timestamping IRQ does not set a bit in the global INT_STATUS, so + * irq_status would be 0. + */ + ret = vsc8584_handle_ts_interrupt(phydev); + if (!(irq_status & MII_VSC85XX_INT_MASK_MASK)) + return ret; + if (irq_status & MII_VSC85XX_INT_MASK_EXT) vsc8584_handle_macsec_interrupt(phydev); @@ -1727,7 +1760,7 @@ static int vsc8514_config_init(struct phy_device *phydev) phydev->mdix_ctrl = ETH_TP_MDI_AUTO; - mutex_lock(&phydev->mdio.bus->mdio_lock); + phy_lock_mdio_bus(phydev); /* Some parts of the init sequence are identical for every PHY in the * package. Some parts are modifying the GPIO register bank which is a @@ -1743,15 +1776,21 @@ static int vsc8514_config_init(struct phy_device *phydev) if (phy_package_init_once(phydev)) vsc8514_config_pre_init(phydev); - phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, - MSCC_PHY_PAGE_EXTENDED_GPIO); + ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_EXTENDED_GPIO); + if (ret) + goto err; val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); val &= ~MAC_CFG_MASK; val |= MAC_CFG_QSGMII; ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); + if (ret) + goto err; + ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STANDARD); if (ret) goto err; @@ -1815,14 +1854,14 @@ static int vsc8514_config_init(struct phy_device *phydev) reg = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, PHY_S6G_PLL_STATUS); if (reg == 0xffffffff) { - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); return -EIO; } } while (time_before(jiffies, deadline) && (reg & BIT(12))); if (reg & BIT(12)) { - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); return -ETIMEDOUT; } @@ -1842,23 +1881,18 @@ static int vsc8514_config_init(struct phy_device *phydev) reg = vsc85xx_csr_ctrl_phy_read(phydev, PHY_MCB_TARGET, PHY_S6G_IB_STATUS0); if (reg == 0xffffffff) { - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); return -EIO; } } while (time_before(jiffies, deadline) && !(reg & BIT(8))); if (!(reg & BIT(8))) { - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); return -ETIMEDOUT; } - mutex_unlock(&phydev->mdio.bus->mdio_lock); - - ret = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); - - if (ret) - return ret; + phy_unlock_mdio_bus(phydev); ret = phy_modify(phydev, MSCC_PHY_EXT_PHY_CNTL_1, MEDIA_OP_MODE_MASK, MEDIA_OP_MODE_COPPER << MEDIA_OP_MODE_POS); @@ -1880,7 +1914,7 @@ static int vsc8514_config_init(struct phy_device *phydev) return ret; err: - mutex_unlock(&phydev->mdio.bus->mdio_lock); + phy_unlock_mdio_bus(phydev); return ret; } @@ -1900,6 +1934,7 @@ static int vsc85xx_config_intr(struct phy_device *phydev) if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { vsc8584_config_macsec_intr(phydev); + vsc8584_config_ts_intr(phydev); rc = phy_write(phydev, MII_VSC85XX_INT_MASK, MII_VSC85XX_INT_MASK_MASK); @@ -1999,6 +2034,7 @@ static int vsc8584_probe(struct phy_device *phydev) u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY, VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY, VSC8531_DUPLEX_COLLISION}; + int ret; if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) { dev_err(&phydev->mdio.dev, "Only VSC8584 revB is supported.\n"); @@ -2012,8 +2048,8 @@ static int vsc8584_probe(struct phy_device *phydev) phydev->priv = vsc8531; vsc8584_get_base_addr(phydev); - devm_phy_package_join(&phydev->mdio.dev, phydev, - vsc8531->base_addr, 0); + devm_phy_package_join(&phydev->mdio.dev, phydev, vsc8531->base_addr, + sizeof(struct vsc85xx_shared_private)); vsc8531->nleds = 4; vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES; @@ -2024,6 +2060,16 @@ static int vsc8584_probe(struct phy_device *phydev) if (!vsc8531->stats) return -ENOMEM; + if (phy_package_probe_once(phydev)) { + ret = vsc8584_ptp_probe_once(phydev); + if (ret) + return ret; + } + + ret = vsc8584_ptp_probe(phydev); + if (ret) + return ret; + return vsc85xx_dt_led_modes_get(phydev, default_mode); } @@ -2403,6 +2449,7 @@ static struct phy_driver vsc85xx_driver[] = { .get_sset_count = &vsc85xx_get_sset_count, .get_strings = &vsc85xx_get_strings, .get_stats = &vsc85xx_get_stats, + .link_change_notify = &vsc85xx_link_change_notify, } }; diff --git a/drivers/net/phy/mscc/mscc_ptp.c b/drivers/net/phy/mscc/mscc_ptp.c new file mode 100644 index 000000000000..ef3441747348 --- /dev/null +++ b/drivers/net/phy/mscc/mscc_ptp.c @@ -0,0 +1,1593 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Driver for Microsemi VSC85xx PHYs - timestamping and PHC support + * + * Authors: Quentin Schulz & Antoine Tenart + * License: Dual MIT/GPL + * Copyright (c) 2020 Microsemi Corporation + */ + +#include <linux/gpio/consumer.h> +#include <linux/ip.h> +#include <linux/net_tstamp.h> +#include <linux/mii.h> +#include <linux/phy.h> +#include <linux/ptp_classify.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/udp.h> +#include <asm/unaligned.h> + +#include "mscc.h" +#include "mscc_ptp.h" + +/* Two PHYs share the same 1588 processor and it's to be entirely configured + * through the base PHY of this processor. + */ +/* phydev->bus->mdio_lock should be locked when using this function */ +static int phy_ts_base_write(struct phy_device *phydev, u32 regnum, u16 val) +{ + struct vsc8531_private *priv = phydev->priv; + + WARN_ON_ONCE(!mutex_is_locked(&phydev->mdio.bus->mdio_lock)); + return __mdiobus_write(phydev->mdio.bus, priv->ts_base_addr, regnum, + val); +} + +/* phydev->bus->mdio_lock should be locked when using this function */ +static int phy_ts_base_read(struct phy_device *phydev, u32 regnum) +{ + struct vsc8531_private *priv = phydev->priv; + + WARN_ON_ONCE(!mutex_is_locked(&phydev->mdio.bus->mdio_lock)); + return __mdiobus_read(phydev->mdio.bus, priv->ts_base_addr, regnum); +} + +enum ts_blk_hw { + INGRESS_ENGINE_0, + EGRESS_ENGINE_0, + INGRESS_ENGINE_1, + EGRESS_ENGINE_1, + INGRESS_ENGINE_2, + EGRESS_ENGINE_2, + PROCESSOR_0, + PROCESSOR_1, +}; + +enum ts_blk { + INGRESS, + EGRESS, + PROCESSOR, +}; + +static u32 vsc85xx_ts_read_csr(struct phy_device *phydev, enum ts_blk blk, + u16 addr) +{ + struct vsc8531_private *priv = phydev->priv; + bool base_port = phydev->mdio.addr == priv->ts_base_addr; + u32 val, cnt = 0; + enum ts_blk_hw blk_hw; + + switch (blk) { + case INGRESS: + blk_hw = base_port ? INGRESS_ENGINE_0 : INGRESS_ENGINE_1; + break; + case EGRESS: + blk_hw = base_port ? EGRESS_ENGINE_0 : EGRESS_ENGINE_1; + break; + case PROCESSOR: + default: + blk_hw = base_port ? PROCESSOR_0 : PROCESSOR_1; + break; + } + + phy_lock_mdio_bus(phydev); + + phy_ts_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_1588); + + phy_ts_base_write(phydev, MSCC_PHY_TS_BIU_ADDR_CNTL, BIU_ADDR_EXE | + BIU_ADDR_READ | BIU_BLK_ID(blk_hw) | + BIU_CSR_ADDR(addr)); + + do { + val = phy_ts_base_read(phydev, MSCC_PHY_TS_BIU_ADDR_CNTL); + } while (!(val & BIU_ADDR_EXE) && cnt++ < BIU_ADDR_CNT_MAX); + + val = phy_ts_base_read(phydev, MSCC_PHY_TS_CSR_DATA_MSB); + val <<= 16; + val |= phy_ts_base_read(phydev, MSCC_PHY_TS_CSR_DATA_LSB); + + phy_ts_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); + + phy_unlock_mdio_bus(phydev); + + return val; +} + +static void vsc85xx_ts_write_csr(struct phy_device *phydev, enum ts_blk blk, + u16 addr, u32 val) +{ + struct vsc8531_private *priv = phydev->priv; + bool base_port = phydev->mdio.addr == priv->ts_base_addr; + u32 reg, bypass, cnt = 0, lower = val & 0xffff, upper = val >> 16; + bool cond = (addr == MSCC_PHY_PTP_LTC_CTRL || + addr == MSCC_PHY_1588_INGR_VSC85XX_INT_MASK || + addr == MSCC_PHY_1588_VSC85XX_INT_MASK || + addr == MSCC_PHY_1588_INGR_VSC85XX_INT_STATUS || + addr == MSCC_PHY_1588_VSC85XX_INT_STATUS) && + blk == PROCESSOR; + enum ts_blk_hw blk_hw; + + switch (blk) { + case INGRESS: + blk_hw = base_port ? INGRESS_ENGINE_0 : INGRESS_ENGINE_1; + break; + case EGRESS: + blk_hw = base_port ? EGRESS_ENGINE_0 : EGRESS_ENGINE_1; + break; + case PROCESSOR: + default: + blk_hw = base_port ? PROCESSOR_0 : PROCESSOR_1; + break; + } + + phy_lock_mdio_bus(phydev); + + bypass = phy_ts_base_read(phydev, MSCC_PHY_BYPASS_CONTROL); + + phy_ts_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_1588); + + if (!cond || (cond && upper)) + phy_ts_base_write(phydev, MSCC_PHY_TS_CSR_DATA_MSB, upper); + + phy_ts_base_write(phydev, MSCC_PHY_TS_CSR_DATA_LSB, lower); + + phy_ts_base_write(phydev, MSCC_PHY_TS_BIU_ADDR_CNTL, BIU_ADDR_EXE | + BIU_ADDR_WRITE | BIU_BLK_ID(blk_hw) | + BIU_CSR_ADDR(addr)); + + do { + reg = phy_ts_base_read(phydev, MSCC_PHY_TS_BIU_ADDR_CNTL); + } while (!(reg & BIU_ADDR_EXE) && cnt++ < BIU_ADDR_CNT_MAX); + + phy_ts_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); + + if (cond && upper) + phy_ts_base_write(phydev, MSCC_PHY_BYPASS_CONTROL, bypass); + + phy_unlock_mdio_bus(phydev); +} + +/* Pick bytes from PTP header */ +#define PTP_HEADER_TRNSP_MSG 26 +#define PTP_HEADER_DOMAIN_NUM 25 +#define PTP_HEADER_BYTE_8_31(x) (31 - (x)) +#define MAC_ADDRESS_BYTE(x) ((x) + (35 - ETH_ALEN + 1)) + +static int vsc85xx_ts_fsb_init(struct phy_device *phydev) +{ + u8 sig_sel[16] = {}; + signed char i, pos = 0; + + /* Seq ID is 2B long and starts at 30th byte */ + for (i = 1; i >= 0; i--) + sig_sel[pos++] = PTP_HEADER_BYTE_8_31(30 + i); + + /* DomainNum */ + sig_sel[pos++] = PTP_HEADER_DOMAIN_NUM; + + /* MsgType */ + sig_sel[pos++] = PTP_HEADER_TRNSP_MSG; + + /* MAC address is 6B long */ + for (i = ETH_ALEN - 1; i >= 0; i--) + sig_sel[pos++] = MAC_ADDRESS_BYTE(i); + + /* Fill the last bytes of the signature to reach a 16B signature */ + for (; pos < ARRAY_SIZE(sig_sel); pos++) + sig_sel[pos] = PTP_HEADER_TRNSP_MSG; + + for (i = 0; i <= 2; i++) { + u32 val = 0; + + for (pos = i * 5 + 4; pos >= i * 5; pos--) + val = (val << 6) | sig_sel[pos]; + + vsc85xx_ts_write_csr(phydev, EGRESS, MSCC_PHY_ANA_FSB_REG(i), + val); + } + + vsc85xx_ts_write_csr(phydev, EGRESS, MSCC_PHY_ANA_FSB_REG(3), + sig_sel[15]); + + return 0; +} + +static const u32 vsc85xx_egr_latency[] = { + /* Copper Egress */ + 1272, /* 1000Mbps */ + 12516, /* 100Mbps */ + 125444, /* 10Mbps */ + /* Fiber Egress */ + 1277, /* 1000Mbps */ + 12537, /* 100Mbps */ +}; + +static const u32 vsc85xx_egr_latency_macsec[] = { + /* Copper Egress ON */ + 3496, /* 1000Mbps */ + 34760, /* 100Mbps */ + 347844, /* 10Mbps */ + /* Fiber Egress ON */ + 3502, /* 1000Mbps */ + 34780, /* 100Mbps */ +}; + +static const u32 vsc85xx_ingr_latency[] = { + /* Copper Ingress */ + 208, /* 1000Mbps */ + 304, /* 100Mbps */ + 2023, /* 10Mbps */ + /* Fiber Ingress */ + 98, /* 1000Mbps */ + 197, /* 100Mbps */ +}; + +static const u32 vsc85xx_ingr_latency_macsec[] = { + /* Copper Ingress */ + 2408, /* 1000Mbps */ + 22300, /* 100Mbps */ + 222009, /* 10Mbps */ + /* Fiber Ingress */ + 2299, /* 1000Mbps */ + 22192, /* 100Mbps */ +}; + +static void vsc85xx_ts_set_latencies(struct phy_device *phydev) +{ + u32 val, ingr_latency, egr_latency; + u8 idx; + + /* No need to set latencies of packets if the PHY is not connected */ + if (!phydev->link) + return; + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_STALL_LATENCY, + STALL_EGR_LATENCY(phydev->speed)); + + switch (phydev->speed) { + case SPEED_100: + idx = 1; + break; + case SPEED_1000: + idx = 0; + break; + default: + idx = 2; + break; + } + + ingr_latency = IS_ENABLED(CONFIG_MACSEC) ? + vsc85xx_ingr_latency_macsec[idx] : vsc85xx_ingr_latency[idx]; + egr_latency = IS_ENABLED(CONFIG_MACSEC) ? + vsc85xx_egr_latency_macsec[idx] : vsc85xx_egr_latency[idx]; + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_LOCAL_LATENCY, + PTP_INGR_LOCAL_LATENCY(ingr_latency)); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_INGR_TSP_CTRL); + val |= PHY_PTP_INGR_TSP_CTRL_LOAD_DELAYS; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_TSP_CTRL, + val); + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_LOCAL_LATENCY, + PTP_EGR_LOCAL_LATENCY(egr_latency)); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_TSP_CTRL); + val |= PHY_PTP_EGR_TSP_CTRL_LOAD_DELAYS; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_TSP_CTRL, val); +} + +static int vsc85xx_ts_disable_flows(struct phy_device *phydev, enum ts_blk blk) +{ + u8 i; + + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_NXT_COMP, 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_UDP_CHKSUM, + IP1_NXT_PROT_UDP_CHKSUM_WIDTH(2)); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP2_NXT_PROT_NXT_COMP, 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP2_NXT_PROT_UDP_CHKSUM, + IP2_NXT_PROT_UDP_CHKSUM_WIDTH(2)); + vsc85xx_ts_write_csr(phydev, blk, MSCC_PHY_ANA_MPLS_COMP_NXT_COMP, 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_PHY_ANA_ETH1_NTX_PROT, 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_PHY_ANA_ETH2_NTX_PROT, 0); + + for (i = 0; i < COMP_MAX_FLOWS; i++) { + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_ENA(i), + IP1_FLOW_VALID_CH0 | IP1_FLOW_VALID_CH1); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP2_FLOW_ENA(i), + IP2_FLOW_VALID_CH0 | IP2_FLOW_VALID_CH1); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_ENA(i), + ETH1_FLOW_VALID_CH0 | ETH1_FLOW_VALID_CH1); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH2_FLOW_ENA(i), + ETH2_FLOW_VALID_CH0 | ETH2_FLOW_VALID_CH1); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_MPLS_FLOW_CTRL(i), + MPLS_FLOW_VALID_CH0 | MPLS_FLOW_VALID_CH1); + + if (i >= PTP_COMP_MAX_FLOWS) + continue; + + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_PTP_FLOW_ENA(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_DOMAIN_RANGE(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_MASK_UPPER(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_MASK_LOWER(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_MATCH_UPPER(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_MATCH_LOWER(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_PTP_ACTION(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_PTP_ACTION2(i), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_PTP_0_FIELD(i), 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_OAM_PTP_FLOW_ENA(i), + 0); + } + + return 0; +} + +static int vsc85xx_ts_eth_cmp1_sig(struct phy_device *phydev) +{ + u32 val; + + val = vsc85xx_ts_read_csr(phydev, EGRESS, MSCC_PHY_ANA_ETH1_NTX_PROT); + val &= ~ANA_ETH1_NTX_PROT_SIG_OFF_MASK; + val |= ANA_ETH1_NTX_PROT_SIG_OFF(0); + vsc85xx_ts_write_csr(phydev, EGRESS, MSCC_PHY_ANA_ETH1_NTX_PROT, val); + + val = vsc85xx_ts_read_csr(phydev, EGRESS, MSCC_PHY_ANA_FSB_CFG); + val &= ~ANA_FSB_ADDR_FROM_BLOCK_SEL_MASK; + val |= ANA_FSB_ADDR_FROM_ETH1; + vsc85xx_ts_write_csr(phydev, EGRESS, MSCC_PHY_ANA_FSB_CFG, val); + + return 0; +} + +static struct vsc85xx_ptphdr *get_ptp_header_l4(struct sk_buff *skb, + struct iphdr *iphdr, + struct udphdr *udphdr) +{ + if (iphdr->version != 4 || iphdr->protocol != IPPROTO_UDP) + return NULL; + + return (struct vsc85xx_ptphdr *)(((unsigned char *)udphdr) + UDP_HLEN); +} + +static struct vsc85xx_ptphdr *get_ptp_header_tx(struct sk_buff *skb) +{ + struct ethhdr *ethhdr = eth_hdr(skb); + struct udphdr *udphdr; + struct iphdr *iphdr; + + if (ethhdr->h_proto == htons(ETH_P_1588)) + return (struct vsc85xx_ptphdr *)(((unsigned char *)ethhdr) + + skb_mac_header_len(skb)); + + if (ethhdr->h_proto != htons(ETH_P_IP)) + return NULL; + + iphdr = ip_hdr(skb); + udphdr = udp_hdr(skb); + + return get_ptp_header_l4(skb, iphdr, udphdr); +} + +static struct vsc85xx_ptphdr *get_ptp_header_rx(struct sk_buff *skb, + enum hwtstamp_rx_filters rx_filter) +{ + struct udphdr *udphdr; + struct iphdr *iphdr; + + if (rx_filter == HWTSTAMP_FILTER_PTP_V2_L2_EVENT) + return (struct vsc85xx_ptphdr *)skb->data; + + iphdr = (struct iphdr *)skb->data; + udphdr = (struct udphdr *)(skb->data + iphdr->ihl * 4); + + return get_ptp_header_l4(skb, iphdr, udphdr); +} + +static int get_sig(struct sk_buff *skb, u8 *sig) +{ + struct vsc85xx_ptphdr *ptphdr = get_ptp_header_tx(skb); + struct ethhdr *ethhdr = eth_hdr(skb); + unsigned int i; + + if (!ptphdr) + return -EOPNOTSUPP; + + sig[0] = (__force u16)ptphdr->seq_id >> 8; + sig[1] = (__force u16)ptphdr->seq_id & GENMASK(7, 0); + sig[2] = ptphdr->domain; + sig[3] = ptphdr->tsmt & GENMASK(3, 0); + + memcpy(&sig[4], ethhdr->h_dest, ETH_ALEN); + + /* Fill the last bytes of the signature to reach a 16B signature */ + for (i = 10; i < 16; i++) + sig[i] = ptphdr->tsmt & GENMASK(3, 0); + + return 0; +} + +static void vsc85xx_dequeue_skb(struct vsc85xx_ptp *ptp) +{ + struct skb_shared_hwtstamps shhwtstamps; + struct vsc85xx_ts_fifo fifo; + struct sk_buff *skb; + u8 skb_sig[16], *p; + int i, len; + u32 reg; + + memset(&fifo, 0, sizeof(fifo)); + p = (u8 *)&fifo; + + reg = vsc85xx_ts_read_csr(ptp->phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_TS_FIFO(0)); + if (reg & PTP_EGR_TS_FIFO_EMPTY) + return; + + *p++ = reg & 0xff; + *p++ = (reg >> 8) & 0xff; + + /* Read the current FIFO item. Reading FIFO6 pops the next one. */ + for (i = 1; i < 7; i++) { + reg = vsc85xx_ts_read_csr(ptp->phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_TS_FIFO(i)); + *p++ = reg & 0xff; + *p++ = (reg >> 8) & 0xff; + *p++ = (reg >> 16) & 0xff; + *p++ = (reg >> 24) & 0xff; + } + + len = skb_queue_len(&ptp->tx_queue); + if (len < 1) + return; + + while (len--) { + skb = __skb_dequeue(&ptp->tx_queue); + if (!skb) + return; + + /* Can't get the signature of the packet, won't ever + * be able to have one so let's dequeue the packet. + */ + if (get_sig(skb, skb_sig) < 0) { + kfree_skb(skb); + continue; + } + + /* Check if we found the signature we were looking for. */ + if (!memcmp(skb_sig, fifo.sig, sizeof(fifo.sig))) { + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(fifo.secs, fifo.ns); + skb_complete_tx_timestamp(skb, &shhwtstamps); + + return; + } + + /* Valid signature but does not match the one of the + * packet in the FIFO right now, reschedule it for later + * packets. + */ + __skb_queue_tail(&ptp->tx_queue, skb); + } +} + +static void vsc85xx_get_tx_ts(struct vsc85xx_ptp *ptp) +{ + u32 reg; + + do { + vsc85xx_dequeue_skb(ptp); + + /* If other timestamps are available in the FIFO, process them. */ + reg = vsc85xx_ts_read_csr(ptp->phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_TS_FIFO_CTRL); + } while (PTP_EGR_FIFO_LEVEL_LAST_READ(reg) > 1); +} + +static int vsc85xx_ptp_cmp_init(struct phy_device *phydev, enum ts_blk blk) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + bool base = phydev->mdio.addr == vsc8531->ts_base_addr; + enum vsc85xx_ptp_msg_type msgs[] = { + PTP_MSG_TYPE_SYNC, + PTP_MSG_TYPE_DELAY_REQ + }; + u32 val; + u8 i; + + for (i = 0; i < ARRAY_SIZE(msgs); i++) { + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_PTP_FLOW_ENA(i), + base ? PTP_FLOW_VALID_CH0 : + PTP_FLOW_VALID_CH1); + + val = vsc85xx_ts_read_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_DOMAIN_RANGE(i)); + val &= ~PTP_FLOW_DOMAIN_RANGE_ENA; + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_DOMAIN_RANGE(i), val); + + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_MATCH_UPPER(i), + msgs[i] << 24); + + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_MASK_UPPER(i), + PTP_FLOW_MSG_TYPE_MASK); + } + + return 0; +} + +static int vsc85xx_eth_cmp1_init(struct phy_device *phydev, enum ts_blk blk) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + bool base = phydev->mdio.addr == vsc8531->ts_base_addr; + u32 val; + + vsc85xx_ts_write_csr(phydev, blk, MSCC_PHY_ANA_ETH1_NXT_PROT_TAG, 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_PHY_ANA_ETH1_NTX_PROT_VLAN_TPID, + ANA_ETH1_NTX_PROT_VLAN_TPID(ETH_P_8021AD)); + + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_ENA(0), + base ? ETH1_FLOW_VALID_CH0 : ETH1_FLOW_VALID_CH1); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_MATCH_MODE(0), + ANA_ETH1_FLOW_MATCH_VLAN_TAG2); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_ADDR_MATCH1(0), 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_ADDR_MATCH2(0), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_ETH1_FLOW_VLAN_RANGE_I_TAG(0), 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_VLAN_TAG1(0), 0); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_ETH1_FLOW_VLAN_TAG2_I_TAG(0), 0); + + val = vsc85xx_ts_read_csr(phydev, blk, + MSCC_ANA_ETH1_FLOW_MATCH_MODE(0)); + val &= ~ANA_ETH1_FLOW_MATCH_VLAN_TAG_MASK; + val |= ANA_ETH1_FLOW_MATCH_VLAN_VERIFY; + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_MATCH_MODE(0), + val); + + return 0; +} + +static int vsc85xx_ip_cmp1_init(struct phy_device *phydev, enum ts_blk blk) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + bool base = phydev->mdio.addr == vsc8531->ts_base_addr; + u32 val; + + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_MATCH2_UPPER, + PTP_EV_PORT); + /* Match on dest port only, ignore src */ + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_MASK2_UPPER, + 0xffff); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_MATCH2_LOWER, + 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_MASK2_LOWER, 0); + + val = vsc85xx_ts_read_csr(phydev, blk, MSCC_ANA_IP1_FLOW_ENA(0)); + val &= ~IP1_FLOW_ENA_CHANNEL_MASK_MASK; + val |= base ? IP1_FLOW_VALID_CH0 : IP1_FLOW_VALID_CH1; + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_ENA(0), val); + + /* Match all IPs */ + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MATCH_UPPER(0), 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MASK_UPPER(0), 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MATCH_UPPER_MID(0), + 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MASK_UPPER_MID(0), + 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MATCH_LOWER_MID(0), + 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MASK_LOWER_MID(0), + 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MATCH_LOWER(0), 0); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_MASK_LOWER(0), 0); + + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_PTP_IP_CHKSUM_SEL, 0); + + return 0; +} + +static int vsc85xx_adjfine(struct ptp_clock_info *info, long scaled_ppm) +{ + struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps); + struct phy_device *phydev = ptp->phydev; + struct vsc8531_private *priv = phydev->priv; + u64 adj = 0; + u32 val; + + if (abs(scaled_ppm) < 66 || abs(scaled_ppm) > 65536UL * 1000000UL) + return 0; + + adj = div64_u64(1000000ULL * 65536ULL, abs(scaled_ppm)); + if (adj > 1000000000L) + adj = 1000000000L; + + val = PTP_AUTO_ADJ_NS_ROLLOVER(adj); + val |= scaled_ppm > 0 ? PTP_AUTO_ADJ_ADD_1NS : PTP_AUTO_ADJ_SUB_1NS; + + mutex_lock(&priv->phc_lock); + + /* Update the ppb val in nano seconds to the auto adjust reg. */ + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_AUTO_ADJ, + val); + + /* The auto adjust update val is set to 0 after write operation. */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL); + val |= PTP_LTC_CTRL_AUTO_ADJ_UPDATE; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL, val); + + mutex_unlock(&priv->phc_lock); + + return 0; +} + +static int __vsc85xx_gettime(struct ptp_clock_info *info, struct timespec64 *ts) +{ + struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps); + struct phy_device *phydev = ptp->phydev; + struct vsc85xx_shared_private *shared = + (struct vsc85xx_shared_private *)phydev->shared->priv; + struct vsc8531_private *priv = phydev->priv; + u32 val; + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL); + val |= PTP_LTC_CTRL_SAVE_ENA; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL, val); + + /* Local Time Counter (LTC) is put in SAVE* regs on rising edge of + * LOAD_SAVE pin. + */ + mutex_lock(&shared->gpio_lock); + gpiod_set_value(priv->load_save, 1); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_LTC_SAVED_SEC_MSB); + + ts->tv_sec = ((time64_t)val) << 32; + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_LTC_SAVED_SEC_LSB); + ts->tv_sec += val; + + ts->tv_nsec = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_LTC_SAVED_NS); + + gpiod_set_value(priv->load_save, 0); + mutex_unlock(&shared->gpio_lock); + + return 0; +} + +static int vsc85xx_gettime(struct ptp_clock_info *info, struct timespec64 *ts) +{ + struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps); + struct phy_device *phydev = ptp->phydev; + struct vsc8531_private *priv = phydev->priv; + + mutex_lock(&priv->phc_lock); + __vsc85xx_gettime(info, ts); + mutex_unlock(&priv->phc_lock); + + return 0; +} + +static int __vsc85xx_settime(struct ptp_clock_info *info, + const struct timespec64 *ts) +{ + struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps); + struct phy_device *phydev = ptp->phydev; + struct vsc85xx_shared_private *shared = + (struct vsc85xx_shared_private *)phydev->shared->priv; + struct vsc8531_private *priv = phydev->priv; + u32 val; + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_LOAD_SEC_MSB, + PTP_LTC_LOAD_SEC_MSB(ts->tv_sec)); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_LOAD_SEC_LSB, + PTP_LTC_LOAD_SEC_LSB(ts->tv_sec)); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_LOAD_NS, + PTP_LTC_LOAD_NS(ts->tv_nsec)); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL); + val |= PTP_LTC_CTRL_LOAD_ENA; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL, val); + + /* Local Time Counter (LTC) is set from LOAD* regs on rising edge of + * LOAD_SAVE pin. + */ + mutex_lock(&shared->gpio_lock); + gpiod_set_value(priv->load_save, 1); + + val &= ~PTP_LTC_CTRL_LOAD_ENA; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL, val); + + gpiod_set_value(priv->load_save, 0); + mutex_unlock(&shared->gpio_lock); + + return 0; +} + +static int vsc85xx_settime(struct ptp_clock_info *info, + const struct timespec64 *ts) +{ + struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps); + struct phy_device *phydev = ptp->phydev; + struct vsc8531_private *priv = phydev->priv; + + mutex_lock(&priv->phc_lock); + __vsc85xx_settime(info, ts); + mutex_unlock(&priv->phc_lock); + + return 0; +} + +static int vsc85xx_adjtime(struct ptp_clock_info *info, s64 delta) +{ + struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps); + struct phy_device *phydev = ptp->phydev; + struct vsc8531_private *priv = phydev->priv; + u32 val; + + /* Can't recover that big of an offset. Let's set the time directly. */ + if (abs(delta) >= NSEC_PER_SEC) { + struct timespec64 ts; + u64 now; + + mutex_lock(&priv->phc_lock); + + __vsc85xx_gettime(info, &ts); + now = ktime_to_ns(timespec64_to_ktime(ts)); + ts = ns_to_timespec64(now + delta); + __vsc85xx_settime(info, &ts); + + mutex_unlock(&priv->phc_lock); + + return 0; + } + + mutex_lock(&priv->phc_lock); + + val = PTP_LTC_OFFSET_VAL(abs(delta)) | PTP_LTC_OFFSET_ADJ; + if (delta > 0) + val |= PTP_LTC_OFFSET_ADD; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_OFFSET, val); + + mutex_unlock(&priv->phc_lock); + + return 0; +} + +static int vsc85xx_eth1_next_comp(struct phy_device *phydev, enum ts_blk blk, + u32 next_comp, u32 etype) +{ + u32 val; + + val = vsc85xx_ts_read_csr(phydev, blk, MSCC_PHY_ANA_ETH1_NTX_PROT); + val &= ~ANA_ETH1_NTX_PROT_COMPARATOR_MASK; + val |= next_comp; + vsc85xx_ts_write_csr(phydev, blk, MSCC_PHY_ANA_ETH1_NTX_PROT, val); + + val = ANA_ETH1_NXT_PROT_ETYPE_MATCH(etype) | + ANA_ETH1_NXT_PROT_ETYPE_MATCH_ENA; + vsc85xx_ts_write_csr(phydev, blk, + MSCC_PHY_ANA_ETH1_NXT_PROT_ETYPE_MATCH, val); + + return 0; +} + +static int vsc85xx_ip1_next_comp(struct phy_device *phydev, enum ts_blk blk, + u32 next_comp, u32 header) +{ + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_NXT_COMP, + ANA_IP1_NXT_PROT_NXT_COMP_BYTES_HDR(header) | + next_comp); + + return 0; +} + +static int vsc85xx_ts_ptp_action_flow(struct phy_device *phydev, enum ts_blk blk, u8 flow, enum ptp_cmd cmd) +{ + u32 val; + + /* Check non-zero reserved field */ + val = PTP_FLOW_PTP_0_FIELD_PTP_FRAME | PTP_FLOW_PTP_0_FIELD_RSVRD_CHECK; + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_PTP_0_FIELD(flow), val); + + val = PTP_FLOW_PTP_ACTION_CORR_OFFSET(8) | + PTP_FLOW_PTP_ACTION_TIME_OFFSET(8) | + PTP_FLOW_PTP_ACTION_PTP_CMD(cmd == PTP_SAVE_IN_TS_FIFO ? + PTP_NOP : cmd); + if (cmd == PTP_SAVE_IN_TS_FIFO) + val |= PTP_FLOW_PTP_ACTION_SAVE_LOCAL_TIME; + else if (cmd == PTP_WRITE_NS) + val |= PTP_FLOW_PTP_ACTION_MOD_FRAME_STATUS_UPDATE | + PTP_FLOW_PTP_ACTION_MOD_FRAME_STATUS_BYTE_OFFSET(6); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_PTP_FLOW_PTP_ACTION(flow), + val); + + if (cmd == PTP_WRITE_1588) + /* Rewrite timestamp directly in frame */ + val = PTP_FLOW_PTP_ACTION2_REWRITE_OFFSET(34) | + PTP_FLOW_PTP_ACTION2_REWRITE_BYTES(10); + else if (cmd == PTP_SAVE_IN_TS_FIFO) + /* no rewrite */ + val = PTP_FLOW_PTP_ACTION2_REWRITE_OFFSET(0) | + PTP_FLOW_PTP_ACTION2_REWRITE_BYTES(0); + else + /* Write in reserved field */ + val = PTP_FLOW_PTP_ACTION2_REWRITE_OFFSET(16) | + PTP_FLOW_PTP_ACTION2_REWRITE_BYTES(4); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_PTP_ACTION2(flow), val); + + return 0; +} + +static int vsc85xx_ptp_conf(struct phy_device *phydev, enum ts_blk blk, + bool one_step, bool enable) +{ + enum vsc85xx_ptp_msg_type msgs[] = { + PTP_MSG_TYPE_SYNC, + PTP_MSG_TYPE_DELAY_REQ + }; + u32 val; + u8 i; + + for (i = 0; i < ARRAY_SIZE(msgs); i++) { + if (blk == INGRESS) + vsc85xx_ts_ptp_action_flow(phydev, blk, msgs[i], + PTP_WRITE_NS); + else if (msgs[i] == PTP_MSG_TYPE_SYNC && one_step) + /* no need to know Sync t when sending in one_step */ + vsc85xx_ts_ptp_action_flow(phydev, blk, msgs[i], + PTP_WRITE_1588); + else + vsc85xx_ts_ptp_action_flow(phydev, blk, msgs[i], + PTP_SAVE_IN_TS_FIFO); + + val = vsc85xx_ts_read_csr(phydev, blk, + MSCC_ANA_PTP_FLOW_ENA(i)); + val &= ~PTP_FLOW_ENA; + if (enable) + val |= PTP_FLOW_ENA; + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_PTP_FLOW_ENA(i), + val); + } + + return 0; +} + +static int vsc85xx_eth1_conf(struct phy_device *phydev, enum ts_blk blk, + bool enable) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + u32 val = ANA_ETH1_FLOW_ADDR_MATCH2_DEST; + + if (vsc8531->ptp->rx_filter == HWTSTAMP_FILTER_PTP_V2_L2_EVENT) { + /* PTP over Ethernet multicast address for SYNC and DELAY msg */ + u8 ptp_multicast[6] = {0x01, 0x1b, 0x19, 0x00, 0x00, 0x00}; + + val |= ANA_ETH1_FLOW_ADDR_MATCH2_FULL_ADDR | + get_unaligned_be16(&ptp_multicast[4]); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_ETH1_FLOW_ADDR_MATCH2(0), val); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_ETH1_FLOW_ADDR_MATCH1(0), + get_unaligned_be32(ptp_multicast)); + } else { + val |= ANA_ETH1_FLOW_ADDR_MATCH2_ANY_MULTICAST; + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_ETH1_FLOW_ADDR_MATCH2(0), val); + vsc85xx_ts_write_csr(phydev, blk, + MSCC_ANA_ETH1_FLOW_ADDR_MATCH1(0), 0); + } + + val = vsc85xx_ts_read_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_ENA(0)); + val &= ~ETH1_FLOW_ENA; + if (enable) + val |= ETH1_FLOW_ENA; + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_ETH1_FLOW_ENA(0), val); + + return 0; +} + +static int vsc85xx_ip1_conf(struct phy_device *phydev, enum ts_blk blk, + bool enable) +{ + u32 val; + + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_IP1_MODE, + ANA_IP1_NXT_PROT_IPV4 | + ANA_IP1_NXT_PROT_FLOW_OFFSET_IPV4); + + /* Matching UDP protocol number */ + val = ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MASK(0xff) | + ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MATCH(IPPROTO_UDP) | + ANA_IP1_NXT_PROT_IP_MATCH1_PROT_OFF(9); + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_IP_MATCH1, + val); + + /* End of IP protocol, start of next protocol (UDP) */ + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_OFFSET2, + ANA_IP1_NXT_PROT_OFFSET2(20)); + + val = vsc85xx_ts_read_csr(phydev, blk, + MSCC_ANA_IP1_NXT_PROT_UDP_CHKSUM); + val &= ~(IP1_NXT_PROT_UDP_CHKSUM_OFF_MASK | + IP1_NXT_PROT_UDP_CHKSUM_WIDTH_MASK); + val |= IP1_NXT_PROT_UDP_CHKSUM_WIDTH(2); + + val &= ~(IP1_NXT_PROT_UDP_CHKSUM_UPDATE | + IP1_NXT_PROT_UDP_CHKSUM_CLEAR); + /* UDP checksum offset in IPv4 packet + * according to: https://tools.ietf.org/html/rfc768 + */ + val |= IP1_NXT_PROT_UDP_CHKSUM_OFF(26) | IP1_NXT_PROT_UDP_CHKSUM_CLEAR; + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_NXT_PROT_UDP_CHKSUM, + val); + + val = vsc85xx_ts_read_csr(phydev, blk, MSCC_ANA_IP1_FLOW_ENA(0)); + val &= ~(IP1_FLOW_MATCH_ADDR_MASK | IP1_FLOW_ENA); + val |= IP1_FLOW_MATCH_DEST_SRC_ADDR; + if (enable) + val |= IP1_FLOW_ENA; + vsc85xx_ts_write_csr(phydev, blk, MSCC_ANA_IP1_FLOW_ENA(0), val); + + return 0; +} + +static int vsc85xx_ts_engine_init(struct phy_device *phydev, bool one_step) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + bool ptp_l4, base = phydev->mdio.addr == vsc8531->ts_base_addr; + u8 eng_id = base ? 0 : 1; + u32 val; + + ptp_l4 = vsc8531->ptp->rx_filter == HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_ANALYZER_MODE); + /* Disable INGRESS and EGRESS so engine eng_id can be reconfigured */ + val &= ~(PTP_ANALYZER_MODE_EGR_ENA(BIT(eng_id)) | + PTP_ANALYZER_MODE_INGR_ENA(BIT(eng_id))); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ANALYZER_MODE, + val); + + if (vsc8531->ptp->rx_filter == HWTSTAMP_FILTER_PTP_V2_L2_EVENT) { + vsc85xx_eth1_next_comp(phydev, INGRESS, + ANA_ETH1_NTX_PROT_PTP_OAM, ETH_P_1588); + vsc85xx_eth1_next_comp(phydev, EGRESS, + ANA_ETH1_NTX_PROT_PTP_OAM, ETH_P_1588); + } else { + vsc85xx_eth1_next_comp(phydev, INGRESS, + ANA_ETH1_NTX_PROT_IP_UDP_ACH_1, + ETH_P_IP); + vsc85xx_eth1_next_comp(phydev, EGRESS, + ANA_ETH1_NTX_PROT_IP_UDP_ACH_1, + ETH_P_IP); + /* Header length of IPv[4/6] + UDP */ + vsc85xx_ip1_next_comp(phydev, INGRESS, + ANA_ETH1_NTX_PROT_PTP_OAM, 28); + vsc85xx_ip1_next_comp(phydev, EGRESS, + ANA_ETH1_NTX_PROT_PTP_OAM, 28); + } + + vsc85xx_eth1_conf(phydev, INGRESS, + vsc8531->ptp->rx_filter != HWTSTAMP_FILTER_NONE); + vsc85xx_ip1_conf(phydev, INGRESS, + ptp_l4 && vsc8531->ptp->rx_filter != HWTSTAMP_FILTER_NONE); + vsc85xx_ptp_conf(phydev, INGRESS, one_step, + vsc8531->ptp->rx_filter != HWTSTAMP_FILTER_NONE); + + vsc85xx_eth1_conf(phydev, EGRESS, + vsc8531->ptp->tx_type != HWTSTAMP_TX_OFF); + vsc85xx_ip1_conf(phydev, EGRESS, + ptp_l4 && vsc8531->ptp->tx_type != HWTSTAMP_TX_OFF); + vsc85xx_ptp_conf(phydev, EGRESS, one_step, + vsc8531->ptp->tx_type != HWTSTAMP_TX_OFF); + + val &= ~PTP_ANALYZER_MODE_EGR_ENA(BIT(eng_id)); + if (vsc8531->ptp->tx_type != HWTSTAMP_TX_OFF) + val |= PTP_ANALYZER_MODE_EGR_ENA(BIT(eng_id)); + + val &= ~PTP_ANALYZER_MODE_INGR_ENA(BIT(eng_id)); + if (vsc8531->ptp->rx_filter != HWTSTAMP_FILTER_NONE) + val |= PTP_ANALYZER_MODE_INGR_ENA(BIT(eng_id)); + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ANALYZER_MODE, + val); + + return 0; +} + +void vsc85xx_link_change_notify(struct phy_device *phydev) +{ + struct vsc8531_private *priv = phydev->priv; + + mutex_lock(&priv->ts_lock); + vsc85xx_ts_set_latencies(phydev); + mutex_unlock(&priv->ts_lock); +} + +static void vsc85xx_ts_reset_fifo(struct phy_device *phydev) +{ + u32 val; + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_TS_FIFO_CTRL); + val |= PTP_EGR_TS_FIFO_RESET; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_TS_FIFO_CTRL, + val); + + val &= ~PTP_EGR_TS_FIFO_RESET; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_TS_FIFO_CTRL, + val); +} + +static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) +{ + struct vsc8531_private *vsc8531 = + container_of(mii_ts, struct vsc8531_private, mii_ts); + struct phy_device *phydev = vsc8531->ptp->phydev; + struct hwtstamp_config cfg; + bool one_step = false; + u32 val; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + if (cfg.flags) + return -EINVAL; + + switch (cfg.tx_type) { + case HWTSTAMP_TX_ONESTEP_SYNC: + one_step = true; + break; + case HWTSTAMP_TX_ON: + break; + case HWTSTAMP_TX_OFF: + break; + default: + return -ERANGE; + } + + vsc8531->ptp->tx_type = cfg.tx_type; + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + /* ETH->IP->UDP->PTP */ + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + /* ETH->PTP */ + break; + default: + return -ERANGE; + } + + vsc8531->ptp->rx_filter = cfg.rx_filter; + + mutex_lock(&vsc8531->ts_lock); + + __skb_queue_purge(&vsc8531->ptp->tx_queue); + __skb_queue_head_init(&vsc8531->ptp->tx_queue); + + /* Disable predictor while configuring the 1588 block */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_INGR_PREDICTOR); + val &= ~PTP_INGR_PREDICTOR_EN; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_PREDICTOR, + val); + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_PREDICTOR); + val &= ~PTP_EGR_PREDICTOR_EN; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_PREDICTOR, + val); + + /* Bypass egress or ingress blocks if timestamping isn't used */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_IFACE_CTRL); + val &= ~(PTP_IFACE_CTRL_EGR_BYPASS | PTP_IFACE_CTRL_INGR_BYPASS); + if (vsc8531->ptp->tx_type == HWTSTAMP_TX_OFF) + val |= PTP_IFACE_CTRL_EGR_BYPASS; + if (vsc8531->ptp->rx_filter == HWTSTAMP_FILTER_NONE) + val |= PTP_IFACE_CTRL_INGR_BYPASS; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_IFACE_CTRL, val); + + /* Resetting FIFO so that it's empty after reconfiguration */ + vsc85xx_ts_reset_fifo(phydev); + + vsc85xx_ts_engine_init(phydev, one_step); + + /* Re-enable predictors now */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_INGR_PREDICTOR); + val |= PTP_INGR_PREDICTOR_EN; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_PREDICTOR, + val); + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_PREDICTOR); + val |= PTP_EGR_PREDICTOR_EN; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_PREDICTOR, + val); + + vsc8531->ptp->configured = 1; + mutex_unlock(&vsc8531->ts_lock); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static int vsc85xx_ts_info(struct mii_timestamper *mii_ts, + struct ethtool_ts_info *info) +{ + struct vsc8531_private *vsc8531 = + container_of(mii_ts, struct vsc8531_private, mii_ts); + + info->phc_index = ptp_clock_index(vsc8531->ptp->ptp_clock); + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON) | + (1 << HWTSTAMP_TX_ONESTEP_SYNC); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT); + + return 0; +} + +static void vsc85xx_txtstamp(struct mii_timestamper *mii_ts, + struct sk_buff *skb, int type) +{ + struct vsc8531_private *vsc8531 = + container_of(mii_ts, struct vsc8531_private, mii_ts); + + if (!vsc8531->ptp->configured) + return; + + if (vsc8531->ptp->tx_type == HWTSTAMP_TX_OFF) { + kfree_skb(skb); + return; + } + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + + mutex_lock(&vsc8531->ts_lock); + __skb_queue_tail(&vsc8531->ptp->tx_queue, skb); + mutex_unlock(&vsc8531->ts_lock); +} + +static bool vsc85xx_rxtstamp(struct mii_timestamper *mii_ts, + struct sk_buff *skb, int type) +{ + struct vsc8531_private *vsc8531 = + container_of(mii_ts, struct vsc8531_private, mii_ts); + struct skb_shared_hwtstamps *shhwtstamps = NULL; + struct vsc85xx_ptphdr *ptphdr; + struct timespec64 ts; + unsigned long ns; + + if (!vsc8531->ptp->configured) + return false; + + if (vsc8531->ptp->rx_filter == HWTSTAMP_FILTER_NONE || + type == PTP_CLASS_NONE) + return false; + + vsc85xx_gettime(&vsc8531->ptp->caps, &ts); + + ptphdr = get_ptp_header_rx(skb, vsc8531->ptp->rx_filter); + if (!ptphdr) + return false; + + shhwtstamps = skb_hwtstamps(skb); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + + ns = ntohl(ptphdr->rsrvd2); + + /* nsec is in reserved field */ + if (ts.tv_nsec < ns) + ts.tv_sec--; + + shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ns); + netif_rx_ni(skb); + + return true; +} + +static const struct ptp_clock_info vsc85xx_clk_caps = { + .owner = THIS_MODULE, + .name = "VSC85xx timer", + .max_adj = S32_MAX, + .n_alarm = 0, + .n_pins = 0, + .n_ext_ts = 0, + .n_per_out = 0, + .pps = 0, + .adjtime = &vsc85xx_adjtime, + .adjfine = &vsc85xx_adjfine, + .gettime64 = &vsc85xx_gettime, + .settime64 = &vsc85xx_settime, +}; + +static struct vsc8531_private *vsc8584_base_priv(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + + if (vsc8531->ts_base_addr != phydev->mdio.addr) { + struct mdio_device *dev; + + dev = phydev->mdio.bus->mdio_map[vsc8531->ts_base_addr]; + phydev = container_of(dev, struct phy_device, mdio); + + return phydev->priv; + } + + return vsc8531; +} + +static bool vsc8584_is_1588_input_clk_configured(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = vsc8584_base_priv(phydev); + + return vsc8531->input_clk_init; +} + +static void vsc8584_set_input_clk_configured(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = vsc8584_base_priv(phydev); + + vsc8531->input_clk_init = true; +} + +static int __vsc8584_init_ptp(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + u32 ltc_seq_e[] = { 0, 400000, 0, 0, 0 }; + u8 ltc_seq_a[] = { 8, 6, 5, 4, 2 }; + u32 val; + + if (!vsc8584_is_1588_input_clk_configured(phydev)) { + phy_lock_mdio_bus(phydev); + + /* 1588_DIFF_INPUT_CLK configuration: Use an external clock for + * the LTC, as per 3.13.29 in the VSC8584 datasheet. + */ + phy_ts_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_1588); + phy_ts_base_write(phydev, 29, 0x7ae0); + phy_ts_base_write(phydev, 30, 0xb71c); + phy_ts_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STANDARD); + + phy_unlock_mdio_bus(phydev); + + vsc8584_set_input_clk_configured(phydev); + } + + /* Disable predictor before configuring the 1588 block */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_INGR_PREDICTOR); + val &= ~PTP_INGR_PREDICTOR_EN; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_PREDICTOR, + val); + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_PREDICTOR); + val &= ~PTP_EGR_PREDICTOR_EN; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_PREDICTOR, + val); + + /* By default, the internal clock of fixed rate 250MHz is used */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL); + val &= ~PTP_LTC_CTRL_CLK_SEL_MASK; + val |= PTP_LTC_CTRL_CLK_SEL_INTERNAL_250; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_CTRL, val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_SEQUENCE); + val &= ~PTP_LTC_SEQUENCE_A_MASK; + val |= PTP_LTC_SEQUENCE_A(ltc_seq_a[PHC_CLK_250MHZ]); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_SEQUENCE, val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_SEQ); + val &= ~(PTP_LTC_SEQ_ERR_MASK | PTP_LTC_SEQ_ADD_SUB); + if (ltc_seq_e[PHC_CLK_250MHZ]) + val |= PTP_LTC_SEQ_ADD_SUB; + val |= PTP_LTC_SEQ_ERR(ltc_seq_e[PHC_CLK_250MHZ]); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_SEQ, val); + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_LTC_1PPS_WIDTH_ADJ, + PPS_WIDTH_ADJ); + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_DELAY_FIFO, + IS_ENABLED(CONFIG_MACSEC) ? + PTP_INGR_DELAY_FIFO_DEPTH_MACSEC : + PTP_INGR_DELAY_FIFO_DEPTH_DEFAULT); + + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_DELAY_FIFO, + IS_ENABLED(CONFIG_MACSEC) ? + PTP_EGR_DELAY_FIFO_DEPTH_MACSEC : + PTP_EGR_DELAY_FIFO_DEPTH_DEFAULT); + + /* Enable n-phase sampler for Viper Rev-B */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_ACCUR_CFG_STATUS); + val &= ~(PTP_ACCUR_PPS_OUT_BYPASS | PTP_ACCUR_PPS_IN_BYPASS | + PTP_ACCUR_EGR_SOF_BYPASS | PTP_ACCUR_INGR_SOF_BYPASS | + PTP_ACCUR_LOAD_SAVE_BYPASS); + val |= PTP_ACCUR_PPS_OUT_CALIB_ERR | PTP_ACCUR_PPS_OUT_CALIB_DONE | + PTP_ACCUR_PPS_IN_CALIB_ERR | PTP_ACCUR_PPS_IN_CALIB_DONE | + PTP_ACCUR_EGR_SOF_CALIB_ERR | PTP_ACCUR_EGR_SOF_CALIB_DONE | + PTP_ACCUR_INGR_SOF_CALIB_ERR | PTP_ACCUR_INGR_SOF_CALIB_DONE | + PTP_ACCUR_LOAD_SAVE_CALIB_ERR | PTP_ACCUR_LOAD_SAVE_CALIB_DONE; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ACCUR_CFG_STATUS, + val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_ACCUR_CFG_STATUS); + val |= PTP_ACCUR_CALIB_TRIGG; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ACCUR_CFG_STATUS, + val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_ACCUR_CFG_STATUS); + val &= ~PTP_ACCUR_CALIB_TRIGG; + val |= PTP_ACCUR_PPS_OUT_CALIB_ERR | PTP_ACCUR_PPS_OUT_CALIB_DONE | + PTP_ACCUR_PPS_IN_CALIB_ERR | PTP_ACCUR_PPS_IN_CALIB_DONE | + PTP_ACCUR_EGR_SOF_CALIB_ERR | PTP_ACCUR_EGR_SOF_CALIB_DONE | + PTP_ACCUR_INGR_SOF_CALIB_ERR | PTP_ACCUR_INGR_SOF_CALIB_DONE | + PTP_ACCUR_LOAD_SAVE_CALIB_ERR | PTP_ACCUR_LOAD_SAVE_CALIB_DONE; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ACCUR_CFG_STATUS, + val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_ACCUR_CFG_STATUS); + val |= PTP_ACCUR_CALIB_TRIGG; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ACCUR_CFG_STATUS, + val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_ACCUR_CFG_STATUS); + val &= ~PTP_ACCUR_CALIB_TRIGG; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ACCUR_CFG_STATUS, + val); + + /* Do not access FIFO via SI */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_TSTAMP_FIFO_SI); + val &= ~PTP_TSTAMP_FIFO_SI_EN; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_TSTAMP_FIFO_SI, + val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_INGR_REWRITER_CTRL); + val &= ~PTP_INGR_REWRITER_REDUCE_PREAMBLE; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_REWRITER_CTRL, + val); + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_REWRITER_CTRL); + val &= ~PTP_EGR_REWRITER_REDUCE_PREAMBLE; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_REWRITER_CTRL, + val); + + /* Put the flag that indicates the frame has been modified to bit 7 */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_INGR_REWRITER_CTRL); + val |= PTP_INGR_REWRITER_FLAG_BIT_OFF(7) | PTP_INGR_REWRITER_FLAG_VAL; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_REWRITER_CTRL, + val); + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_REWRITER_CTRL); + val |= PTP_EGR_REWRITER_FLAG_BIT_OFF(7); + val &= ~PTP_EGR_REWRITER_FLAG_VAL; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_REWRITER_CTRL, + val); + + /* 30bit mode for RX timestamp, only the nanoseconds are kept in + * reserved field. + */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_INGR_TSP_CTRL); + val |= PHY_PTP_INGR_TSP_CTRL_FRACT_NS; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_INGR_TSP_CTRL, + val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_TSP_CTRL); + val |= PHY_PTP_EGR_TSP_CTRL_FRACT_NS; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_TSP_CTRL, val); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_SERIAL_TOD_IFACE); + val |= PTP_SERIAL_TOD_IFACE_LS_AUTO_CLR; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_SERIAL_TOD_IFACE, + val); + + vsc85xx_ts_fsb_init(phydev); + + /* Set the Egress timestamp FIFO configuration and status register */ + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_EGR_TS_FIFO_CTRL); + val &= ~(PTP_EGR_TS_FIFO_SIG_BYTES_MASK | PTP_EGR_TS_FIFO_THRESH_MASK); + /* 16 bytes for the signature, 10 for the timestamp in the TS FIFO */ + val |= PTP_EGR_TS_FIFO_SIG_BYTES(16) | PTP_EGR_TS_FIFO_THRESH(7); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_EGR_TS_FIFO_CTRL, + val); + + vsc85xx_ts_reset_fifo(phydev); + + val = PTP_IFACE_CTRL_CLK_ENA; + if (!IS_ENABLED(CONFIG_MACSEC)) + val |= PTP_IFACE_CTRL_GMII_PROT; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_IFACE_CTRL, val); + + vsc85xx_ts_set_latencies(phydev); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_VERSION_CODE); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, MSCC_PHY_PTP_IFACE_CTRL); + val |= PTP_IFACE_CTRL_EGR_BYPASS; + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_IFACE_CTRL, val); + + vsc85xx_ts_disable_flows(phydev, EGRESS); + vsc85xx_ts_disable_flows(phydev, INGRESS); + + val = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_PTP_ANALYZER_MODE); + /* Disable INGRESS and EGRESS so engine eng_id can be reconfigured */ + val &= ~(PTP_ANALYZER_MODE_EGR_ENA_MASK | + PTP_ANALYZER_MODE_INGR_ENA_MASK | + PTP_ANA_INGR_ENCAP_FLOW_MODE_MASK | + PTP_ANA_EGR_ENCAP_FLOW_MODE_MASK); + /* Strict matching in flow (packets should match flows from the same + * index in all enabled comparators (except PTP)). + */ + val |= PTP_ANA_SPLIT_ENCAP_FLOW | PTP_ANA_INGR_ENCAP_FLOW_MODE(0x7) | + PTP_ANA_EGR_ENCAP_FLOW_MODE(0x7); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_PTP_ANALYZER_MODE, + val); + + /* Initialized for ingress and egress flows: + * - The Ethernet comparator. + * - The IP comparator. + * - The PTP comparator. + */ + vsc85xx_eth_cmp1_init(phydev, INGRESS); + vsc85xx_ip_cmp1_init(phydev, INGRESS); + vsc85xx_ptp_cmp_init(phydev, INGRESS); + vsc85xx_eth_cmp1_init(phydev, EGRESS); + vsc85xx_ip_cmp1_init(phydev, EGRESS); + vsc85xx_ptp_cmp_init(phydev, EGRESS); + + vsc85xx_ts_eth_cmp1_sig(phydev); + + vsc8531->mii_ts.rxtstamp = vsc85xx_rxtstamp; + vsc8531->mii_ts.txtstamp = vsc85xx_txtstamp; + vsc8531->mii_ts.hwtstamp = vsc85xx_hwtstamp; + vsc8531->mii_ts.ts_info = vsc85xx_ts_info; + phydev->mii_ts = &vsc8531->mii_ts; + + memcpy(&vsc8531->ptp->caps, &vsc85xx_clk_caps, sizeof(vsc85xx_clk_caps)); + + vsc8531->ptp->ptp_clock = ptp_clock_register(&vsc8531->ptp->caps, + &phydev->mdio.dev); + if (IS_ERR(vsc8531->ptp->ptp_clock)) + return PTR_ERR(vsc8531->ptp->ptp_clock); + + return 0; +} + +void vsc8584_config_ts_intr(struct phy_device *phydev) +{ + struct vsc8531_private *priv = phydev->priv; + + mutex_lock(&priv->ts_lock); + vsc85xx_ts_write_csr(phydev, PROCESSOR, MSCC_PHY_1588_VSC85XX_INT_MASK, + VSC85XX_1588_INT_MASK_MASK); + mutex_unlock(&priv->ts_lock); +} + +int vsc8584_ptp_init(struct phy_device *phydev) +{ + switch (phydev->phy_id & phydev->drv->phy_id_mask) { + case PHY_ID_VSC8575: + case PHY_ID_VSC8582: + case PHY_ID_VSC8584: + return __vsc8584_init_ptp(phydev); + } + + return 0; +} + +irqreturn_t vsc8584_handle_ts_interrupt(struct phy_device *phydev) +{ + struct vsc8531_private *priv = phydev->priv; + int rc; + + mutex_lock(&priv->ts_lock); + rc = vsc85xx_ts_read_csr(phydev, PROCESSOR, + MSCC_PHY_1588_VSC85XX_INT_STATUS); + /* Ack the PTP interrupt */ + vsc85xx_ts_write_csr(phydev, PROCESSOR, + MSCC_PHY_1588_VSC85XX_INT_STATUS, rc); + + if (!(rc & VSC85XX_1588_INT_MASK_MASK)) { + mutex_unlock(&priv->ts_lock); + return IRQ_NONE; + } + + if (rc & VSC85XX_1588_INT_FIFO_ADD) { + vsc85xx_get_tx_ts(priv->ptp); + } else if (rc & VSC85XX_1588_INT_FIFO_OVERFLOW) { + __skb_queue_purge(&priv->ptp->tx_queue); + vsc85xx_ts_reset_fifo(phydev); + } + + mutex_unlock(&priv->ts_lock); + return IRQ_HANDLED; +} + +int vsc8584_ptp_probe(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + + vsc8531->ptp = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531->ptp), + GFP_KERNEL); + if (!vsc8531->ptp) + return -ENOMEM; + + mutex_init(&vsc8531->phc_lock); + mutex_init(&vsc8531->ts_lock); + + /* Retrieve the shared load/save GPIO. Request it as non exclusive as + * the same GPIO can be requested by all the PHYs of the same package. + * This GPIO must be used with the gpio_lock taken (the lock is shared + * between all PHYs). + */ + vsc8531->load_save = devm_gpiod_get_optional(&phydev->mdio.dev, "load-save", + GPIOD_FLAGS_BIT_NONEXCLUSIVE | + GPIOD_OUT_LOW); + if (IS_ERR(vsc8531->load_save)) { + phydev_err(phydev, "Can't get load-save GPIO (%ld)\n", + PTR_ERR(vsc8531->load_save)); + return PTR_ERR(vsc8531->load_save); + } + + vsc8531->ptp->phydev = phydev; + + return 0; +} + +int vsc8584_ptp_probe_once(struct phy_device *phydev) +{ + struct vsc85xx_shared_private *shared = + (struct vsc85xx_shared_private *)phydev->shared->priv; + + /* Initialize shared GPIO lock */ + mutex_init(&shared->gpio_lock); + + return 0; +} diff --git a/drivers/net/phy/mscc/mscc_ptp.h b/drivers/net/phy/mscc/mscc_ptp.h new file mode 100644 index 000000000000..3ea163af0f4f --- /dev/null +++ b/drivers/net/phy/mscc/mscc_ptp.h @@ -0,0 +1,477 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Driver for Microsemi VSC85xx PHYs + * + * Copyright (c) 2020 Microsemi Corporation + */ + +#ifndef _MSCC_PHY_PTP_H_ +#define _MSCC_PHY_PTP_H_ + +/* 1588 page Registers */ +#define MSCC_PHY_TS_BIU_ADDR_CNTL 16 +#define BIU_ADDR_EXE 0x8000 +#define BIU_ADDR_READ 0x4000 +#define BIU_ADDR_WRITE 0x0000 +#define BIU_BLK_ID(x) ((x) << 11) +#define BIU_CSR_ADDR(x) (x) +#define BIU_ADDR_CNT_MAX 8 + +#define MSCC_PHY_TS_CSR_DATA_LSB 17 +#define MSCC_PHY_TS_CSR_DATA_MSB 18 + +#define MSCC_PHY_1588_INGR_VSC85XX_INT_STATUS 0x002d +#define MSCC_PHY_1588_VSC85XX_INT_STATUS 0x004d +#define VSC85XX_1588_INT_FIFO_ADD 0x0004 +#define VSC85XX_1588_INT_FIFO_OVERFLOW 0x0001 + +#define MSCC_PHY_1588_INGR_VSC85XX_INT_MASK 0x002e +#define MSCC_PHY_1588_VSC85XX_INT_MASK 0x004e +#define VSC85XX_1588_INT_MASK_MASK (VSC85XX_1588_INT_FIFO_ADD | \ + VSC85XX_1588_INT_FIFO_OVERFLOW) + +/* TS CSR addresses */ +#define MSCC_PHY_ANA_ETH1_NTX_PROT 0x0000 +#define ANA_ETH1_NTX_PROT_SIG_OFF_MASK GENMASK(20, 16) +#define ANA_ETH1_NTX_PROT_SIG_OFF(x) (((x) << 16) & ANA_ETH1_NTX_PROT_SIG_OFF_MASK) +#define ANA_ETH1_NTX_PROT_COMPARATOR_MASK GENMASK(2, 0) +#define ANA_ETH1_NTX_PROT_PTP_OAM 0x0005 +#define ANA_ETH1_NTX_PROT_MPLS 0x0004 +#define ANA_ETH1_NTX_PROT_IP_UDP_ACH_2 0x0003 +#define ANA_ETH1_NTX_PROT_IP_UDP_ACH_1 0x0002 +#define ANA_ETH1_NTX_PROT_ETH2 0x0001 + +#define MSCC_PHY_PTP_IFACE_CTRL 0x0000 +#define PTP_IFACE_CTRL_CLK_ENA 0x0040 +#define PTP_IFACE_CTRL_INGR_BYPASS 0x0008 +#define PTP_IFACE_CTRL_EGR_BYPASS 0x0004 +#define PTP_IFACE_CTRL_MII_PROT 0x0003 +#define PTP_IFACE_CTRL_GMII_PROT 0x0002 +#define PTP_IFACE_CTRL_XGMII_64_PROT 0x0000 + +#define MSCC_PHY_ANA_ETH1_NTX_PROT_VLAN_TPID 0x0001 +#define ANA_ETH1_NTX_PROT_VLAN_TPID_MASK GENMASK(31, 16) +#define ANA_ETH1_NTX_PROT_VLAN_TPID(x) (((x) << 16) & ANA_ETH1_NTX_PROT_VLAN_TPID_MASK) + +#define MSCC_PHY_PTP_ANALYZER_MODE 0x0001 +#define PTP_ANA_SPLIT_ENCAP_FLOW 0x1000000 +#define PTP_ANA_EGR_ENCAP_FLOW_MODE_MASK GENMASK(22, 20) +#define PTP_ANA_EGR_ENCAP_FLOW_MODE(x) (((x) << 20) & PTP_ANA_EGR_ENCAP_FLOW_MODE_MASK) +#define PTP_ANA_INGR_ENCAP_FLOW_MODE_MASK GENMASK(18, 16) +#define PTP_ANA_INGR_ENCAP_FLOW_MODE(x) (((x) << 16) & PTP_ANA_INGR_ENCAP_FLOW_MODE_MASK) +#define PTP_ANALYZER_MODE_EGR_ENA_MASK GENMASK(6, 4) +#define PTP_ANALYZER_MODE_EGR_ENA(x) (((x) << 4) & PTP_ANALYZER_MODE_EGR_ENA_MASK) +#define PTP_ANALYZER_MODE_INGR_ENA_MASK GENMASK(2, 0) +#define PTP_ANALYZER_MODE_INGR_ENA(x) ((x) & PTP_ANALYZER_MODE_INGR_ENA_MASK) + +#define MSCC_PHY_ANA_ETH1_NXT_PROT_TAG 0x0002 +#define ANA_ETH1_NXT_PROT_TAG_ENA 0x0001 + +#define MSCC_PHY_PTP_MODE_CTRL 0x0002 +#define PTP_MODE_CTRL_MODE_MASK GENMASK(2, 0) +#define PTP_MODE_CTRL_PKT_MODE 0x0004 + +#define MSCC_PHY_ANA_ETH1_NXT_PROT_ETYPE_MATCH 0x0003 +#define ANA_ETH1_NXT_PROT_ETYPE_MATCH_ENA 0x10000 +#define ANA_ETH1_NXT_PROT_ETYPE_MATCH_MASK GENMASK(15, 0) +#define ANA_ETH1_NXT_PROT_ETYPE_MATCH(x) ((x) & ANA_ETH1_NXT_PROT_ETYPE_MATCH_MASK) + +#define MSCC_PHY_PTP_VERSION_CODE 0x0003 +#define PTP_IP_VERSION_MASK GENMASK(7, 0) +#define PTP_IP_VERSION_2_1 0x0021 + +#define MSCC_ANA_ETH1_FLOW_ENA(x) (0x0010 + ((x) << 4)) +#define ETH1_FLOW_ENA_CHANNEL_MASK_MASK GENMASK(9, 8) +#define ETH1_FLOW_ENA_CHANNEL_MASK(x) (((x) << 8) & ETH1_FLOW_ENA_CHANNEL_MASK_MASK) +#define ETH1_FLOW_VALID_CH1 ETH1_FLOW_ENA_CHANNEL_MASK(2) +#define ETH1_FLOW_VALID_CH0 ETH1_FLOW_ENA_CHANNEL_MASK(1) +#define ETH1_FLOW_ENA 0x0001 + +#define MSCC_ANA_ETH1_FLOW_MATCH_MODE(x) (MSCC_ANA_ETH1_FLOW_ENA(x) + 1) +#define ANA_ETH1_FLOW_MATCH_VLAN_TAG_MASK GENMASK(7, 6) +#define ANA_ETH1_FLOW_MATCH_VLAN_TAG(x) (((x) << 6) & ANA_ETH1_FLOW_MATCH_VLAN_TAG_MASK) +#define ANA_ETH1_FLOW_MATCH_VLAN_TAG2 0x0200 +#define ANA_ETH1_FLOW_MATCH_VLAN_VERIFY 0x0010 + +#define MSCC_ANA_ETH1_FLOW_ADDR_MATCH1(x) (MSCC_ANA_ETH1_FLOW_ENA(x) + 2) + +#define MSCC_ANA_ETH1_FLOW_ADDR_MATCH2(x) (MSCC_ANA_ETH1_FLOW_ENA(x) + 3) +#define ANA_ETH1_FLOW_ADDR_MATCH2_MASK_MASK GENMASK(22, 20) +#define ANA_ETH1_FLOW_ADDR_MATCH2_ANY_MULTICAST 0x400000 +#define ANA_ETH1_FLOW_ADDR_MATCH2_FULL_ADDR 0x100000 +#define ANA_ETH1_FLOW_ADDR_MATCH2_SRC_DEST_MASK GENMASK(17, 16) +#define ANA_ETH1_FLOW_ADDR_MATCH2_SRC_DEST 0x020000 +#define ANA_ETH1_FLOW_ADDR_MATCH2_SRC 0x010000 +#define ANA_ETH1_FLOW_ADDR_MATCH2_DEST 0x000000 + +#define MSCC_ANA_ETH1_FLOW_VLAN_RANGE_I_TAG(x) (MSCC_ANA_ETH1_FLOW_ENA(x) + 4) +#define MSCC_ANA_ETH1_FLOW_VLAN_TAG1(x) (MSCC_ANA_ETH1_FLOW_ENA(x) + 5) +#define MSCC_ANA_ETH1_FLOW_VLAN_TAG2_I_TAG(x) (MSCC_ANA_ETH1_FLOW_ENA(x) + 6) + +#define MSCC_PHY_PTP_LTC_CTRL 0x0010 +#define PTP_LTC_CTRL_CLK_SEL_MASK GENMASK(14, 12) +#define PTP_LTC_CTRL_CLK_SEL(x) (((x) << 12) & PTP_LTC_CTRL_CLK_SEL_MASK) +#define PTP_LTC_CTRL_CLK_SEL_INTERNAL_250 PTP_LTC_CTRL_CLK_SEL(5) +#define PTP_LTC_CTRL_AUTO_ADJ_UPDATE 0x0010 +#define PTP_LTC_CTRL_ADD_SUB_1NS_REQ 0x0008 +#define PTP_LTC_CTRL_ADD_1NS 0x0004 +#define PTP_LTC_CTRL_SAVE_ENA 0x0002 +#define PTP_LTC_CTRL_LOAD_ENA 0x0001 + +#define MSCC_PHY_PTP_LTC_LOAD_SEC_MSB 0x0011 +#define PTP_LTC_LOAD_SEC_MSB(x) (((x) & GENMASK_ULL(47, 32)) >> 32) + +#define MSCC_PHY_PTP_LTC_LOAD_SEC_LSB 0x0012 +#define PTP_LTC_LOAD_SEC_LSB(x) ((x) & GENMASK(31, 0)) + +#define MSCC_PHY_PTP_LTC_LOAD_NS 0x0013 +#define PTP_LTC_LOAD_NS(x) ((x) & GENMASK(31, 0)) + +#define MSCC_PHY_PTP_LTC_SAVED_SEC_MSB 0x0014 +#define MSCC_PHY_PTP_LTC_SAVED_SEC_LSB 0x0015 +#define MSCC_PHY_PTP_LTC_SAVED_NS 0x0016 + +#define MSCC_PHY_PTP_LTC_SEQUENCE 0x0017 +#define PTP_LTC_SEQUENCE_A_MASK GENMASK(3, 0) +#define PTP_LTC_SEQUENCE_A(x) ((x) & PTP_LTC_SEQUENCE_A_MASK) + +#define MSCC_PHY_PTP_LTC_SEQ 0x0018 +#define PTP_LTC_SEQ_ADD_SUB 0x80000 +#define PTP_LTC_SEQ_ERR_MASK GENMASK(18, 0) +#define PTP_LTC_SEQ_ERR(x) ((x) & PTP_LTC_SEQ_ERR_MASK) + +#define MSCC_PHY_PTP_LTC_AUTO_ADJ 0x001a +#define PTP_AUTO_ADJ_NS_ROLLOVER(x) ((x) & GENMASK(29, 0)) +#define PTP_AUTO_ADJ_ADD_SUB_1NS_MASK GENMASK(31, 30) +#define PTP_AUTO_ADJ_SUB_1NS 0x80000000 +#define PTP_AUTO_ADJ_ADD_1NS 0x40000000 + +#define MSCC_PHY_PTP_LTC_1PPS_WIDTH_ADJ 0x001b +#define PTP_LTC_1PPS_WIDTH_ADJ_MASK GENMASK(29, 0) + +#define MSCC_PHY_PTP_TSTAMP_FIFO_SI 0x0020 +#define PTP_TSTAMP_FIFO_SI_EN 0x0001 + +#define MSCC_PHY_PTP_INGR_PREDICTOR 0x0022 +#define PTP_INGR_PREDICTOR_EN 0x0001 + +#define MSCC_PHY_PTP_EGR_PREDICTOR 0x0026 +#define PTP_EGR_PREDICTOR_EN 0x0001 + +#define MSCC_PHY_PTP_INGR_TSP_CTRL 0x0035 +#define PHY_PTP_INGR_TSP_CTRL_FRACT_NS 0x0004 +#define PHY_PTP_INGR_TSP_CTRL_LOAD_DELAYS 0x0001 + +#define MSCC_PHY_PTP_INGR_LOCAL_LATENCY 0x0037 +#define PTP_INGR_LOCAL_LATENCY_MASK GENMASK(22, 0) +#define PTP_INGR_LOCAL_LATENCY(x) ((x) & PTP_INGR_LOCAL_LATENCY_MASK) + +#define MSCC_PHY_PTP_INGR_DELAY_FIFO 0x003a +#define PTP_INGR_DELAY_FIFO_DEPTH_MACSEC 0x0013 +#define PTP_INGR_DELAY_FIFO_DEPTH_DEFAULT 0x000f + +#define MSCC_PHY_PTP_INGR_TS_FIFO(x) (0x005c + (x)) +#define PTP_INGR_TS_FIFO_EMPTY 0x80000000 + +#define MSCC_PHY_PTP_INGR_REWRITER_CTRL 0x0044 +#define PTP_INGR_REWRITER_REDUCE_PREAMBLE 0x0010 +#define PTP_INGR_REWRITER_FLAG_VAL 0x0008 +#define PTP_INGR_REWRITER_FLAG_BIT_OFF_M GENMASK(2, 0) +#define PTP_INGR_REWRITER_FLAG_BIT_OFF(x) ((x) & PTP_INGR_REWRITER_FLAG_BIT_OFF_M) + +#define MSCC_PHY_PTP_EGR_STALL_LATENCY 0x004f + +#define MSCC_PHY_PTP_EGR_TSP_CTRL 0x0055 +#define PHY_PTP_EGR_TSP_CTRL_FRACT_NS 0x0004 +#define PHY_PTP_EGR_TSP_CTRL_LOAD_DELAYS 0x0001 + +#define MSCC_PHY_PTP_EGR_LOCAL_LATENCY 0x0057 +#define PTP_EGR_LOCAL_LATENCY_MASK GENMASK(22, 0) +#define PTP_EGR_LOCAL_LATENCY(x) ((x) & PTP_EGR_LOCAL_LATENCY_MASK) + +#define MSCC_PHY_PTP_EGR_DELAY_FIFO 0x005a +#define PTP_EGR_DELAY_FIFO_DEPTH_MACSEC 0x0013 +#define PTP_EGR_DELAY_FIFO_DEPTH_DEFAULT 0x000f + +#define MSCC_PHY_PTP_EGR_TS_FIFO_CTRL 0x005b +#define PTP_EGR_TS_FIFO_RESET 0x10000 +#define PTP_EGR_FIFO_LEVEL_LAST_READ_MASK GENMASK(15, 12) +#define PTP_EGR_FIFO_LEVEL_LAST_READ(x) (((x) & PTP_EGR_FIFO_LEVEL_LAST_READ_MASK) >> 12) +#define PTP_EGR_TS_FIFO_THRESH_MASK GENMASK(11, 8) +#define PTP_EGR_TS_FIFO_THRESH(x) (((x) << 8) & PTP_EGR_TS_FIFO_THRESH_MASK) +#define PTP_EGR_TS_FIFO_SIG_BYTES_MASK GENMASK(4, 0) +#define PTP_EGR_TS_FIFO_SIG_BYTES(x) ((x) & PTP_EGR_TS_FIFO_SIG_BYTES_MASK) + +#define MSCC_PHY_PTP_EGR_TS_FIFO(x) (0x005c + (x)) +#define PTP_EGR_TS_FIFO_EMPTY 0x80000000 +#define PTP_EGR_TS_FIFO_0_MASK GENMASK(15, 0) + +#define MSCC_PHY_PTP_EGR_REWRITER_CTRL 0x0064 +#define PTP_EGR_REWRITER_REDUCE_PREAMBLE 0x0010 +#define PTP_EGR_REWRITER_FLAG_VAL 0x0008 +#define PTP_EGR_REWRITER_FLAG_BIT_OFF_M GENMASK(2, 0) +#define PTP_EGR_REWRITER_FLAG_BIT_OFF(x) ((x) & PTP_EGR_REWRITER_FLAG_BIT_OFF_M) + +#define MSCC_PHY_PTP_SERIAL_TOD_IFACE 0x006e +#define PTP_SERIAL_TOD_IFACE_LS_AUTO_CLR 0x0004 + +#define MSCC_PHY_PTP_LTC_OFFSET 0x0070 +#define PTP_LTC_OFFSET_ADJ BIT(31) +#define PTP_LTC_OFFSET_ADD BIT(30) +#define PTP_LTC_OFFSET_VAL(x) (x) + +#define MSCC_PHY_PTP_ACCUR_CFG_STATUS 0x0074 +#define PTP_ACCUR_PPS_OUT_CALIB_ERR 0x20000 +#define PTP_ACCUR_PPS_OUT_CALIB_DONE 0x10000 +#define PTP_ACCUR_PPS_IN_CALIB_ERR 0x4000 +#define PTP_ACCUR_PPS_IN_CALIB_DONE 0x2000 +#define PTP_ACCUR_EGR_SOF_CALIB_ERR 0x1000 +#define PTP_ACCUR_EGR_SOF_CALIB_DONE 0x0800 +#define PTP_ACCUR_INGR_SOF_CALIB_ERR 0x0400 +#define PTP_ACCUR_INGR_SOF_CALIB_DONE 0x0200 +#define PTP_ACCUR_LOAD_SAVE_CALIB_ERR 0x0100 +#define PTP_ACCUR_LOAD_SAVE_CALIB_DONE 0x0080 +#define PTP_ACCUR_CALIB_TRIGG 0x0040 +#define PTP_ACCUR_PPS_OUT_BYPASS 0x0010 +#define PTP_ACCUR_PPS_IN_BYPASS 0x0008 +#define PTP_ACCUR_EGR_SOF_BYPASS 0x0004 +#define PTP_ACCUR_INGR_SOF_BYPASS 0x0002 +#define PTP_ACCUR_LOAD_SAVE_BYPASS 0x0001 + +#define MSCC_PHY_ANA_ETH2_NTX_PROT 0x0090 +#define ANA_ETH2_NTX_PROT_COMPARATOR_MASK GENMASK(2, 0) +#define ANA_ETH2_NTX_PROT_PTP_OAM 0x0005 +#define ANA_ETH2_NTX_PROT_MPLS 0x0004 +#define ANA_ETH2_NTX_PROT_IP_UDP_ACH_2 0x0003 +#define ANA_ETH2_NTX_PROT_IP_UDP_ACH_1 0x0002 +#define ANA_ETH2_NTX_PROT_ETH2 0x0001 + +#define MSCC_PHY_ANA_ETH2_NXT_PROT_ETYPE_MATCH 0x0003 +#define ANA_ETH2_NXT_PROT_ETYPE_MATCH_ENA 0x10000 +#define ANA_ETH2_NXT_PROT_ETYPE_MATCH_MASK GENMASK(15, 0) +#define ANA_ETH2_NXT_PROT_ETYPE_MATCH(x) ((x) & ANA_ETH2_NXT_PROT_ETYPE_MATCH_MASK) + +#define MSCC_ANA_ETH2_FLOW_ENA(x) (0x00a0 + ((x) << 4)) +#define ETH2_FLOW_ENA_CHANNEL_MASK_MASK GENMASK(9, 8) +#define ETH2_FLOW_ENA_CHANNEL_MASK(x) (((x) << 8) & ETH2_FLOW_ENA_CHANNEL_MASK_MASK) +#define ETH2_FLOW_VALID_CH1 ETH2_FLOW_ENA_CHANNEL_MASK(2) +#define ETH2_FLOW_VALID_CH0 ETH2_FLOW_ENA_CHANNEL_MASK(1) + +#define MSCC_PHY_ANA_MPLS_COMP_NXT_COMP 0x0120 +#define ANA_MPLS_NTX_PROT_COMPARATOR_MASK GENMASK(2, 0) +#define ANA_MPLS_NTX_PROT_PTP_OAM 0x0005 +#define ANA_MPLS_NTX_PROT_MPLS 0x0004 +#define ANA_MPLS_NTX_PROT_IP_UDP_ACH_2 0x0003 +#define ANA_MPLS_NTX_PROT_IP_UDP_ACH_1 0x0002 +#define ANA_MPLS_NTX_PROT_ETH2 0x0001 + +#define MSCC_ANA_MPLS_FLOW_CTRL(x) (0x0130 + ((x) << 4)) +#define MPLS_FLOW_CTRL_CHANNEL_MASK_MASK GENMASK(25, 24) +#define MPLS_FLOW_CTRL_CHANNEL_MASK(x) (((x) << 24) & MPLS_FLOW_CTRL_CHANNEL_MASK_MASK) +#define MPLS_FLOW_VALID_CH1 MPLS_FLOW_CTRL_CHANNEL_MASK(2) +#define MPLS_FLOW_VALID_CH0 MPLS_FLOW_CTRL_CHANNEL_MASK(1) + +#define MSCC_ANA_IP1_NXT_PROT_NXT_COMP 0x01b0 +#define ANA_IP1_NXT_PROT_NXT_COMP_BYTES_HDR_MASK GENMASK(15, 8) +#define ANA_IP1_NXT_PROT_NXT_COMP_BYTES_HDR(x) (((x) << 8) & ANA_IP1_NXT_PROT_NXT_COMP_BYTES_HDR_MASK) +#define ANA_IP1_NXT_PROT_NXT_COMP_PTP_OAM 0x0005 +#define ANA_IP1_NXT_PROT_NXT_COMP_IP_UDP_ACH2 0x0003 + +#define MSCC_ANA_IP1_NXT_PROT_IP1_MODE 0x01b1 +#define ANA_IP1_NXT_PROT_FLOW_OFFSET_IPV4 0x0c00 +#define ANA_IP1_NXT_PROT_FLOW_OFFSET_IPV6 0x0800 +#define ANA_IP1_NXT_PROT_IPV6 0x0001 +#define ANA_IP1_NXT_PROT_IPV4 0x0000 + +#define MSCC_ANA_IP1_NXT_PROT_IP_MATCH1 0x01b2 +#define ANA_IP1_NXT_PROT_IP_MATCH1_PROT_OFF_MASK GENMASK(20, 16) +#define ANA_IP1_NXT_PROT_IP_MATCH1_PROT_OFF(x) (((x) << 16) & ANA_IP1_NXT_PROT_IP_MATCH1_PROT_OFF_MASK) +#define ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MASK_MASK GENMASK(15, 8) +#define ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MASK(x) (((x) << 15) & ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MASK_MASK) +#define ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MATCH_MASK GENMASK(7, 0) +#define ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MATCH(x) ((x) & ANA_IP1_NXT_PROT_IP_MATCH1_PROT_MATCH_MASK) + +#define MSCC_ANA_IP1_NXT_PROT_MATCH2_UPPER 0x01b3 +#define MSCC_ANA_IP1_NXT_PROT_MATCH2_LOWER 0x01b4 +#define MSCC_ANA_IP1_NXT_PROT_MASK2_UPPER 0x01b5 +#define MSCC_ANA_IP1_NXT_PROT_MASK2_LOWER 0x01b6 + +#define MSCC_ANA_IP1_NXT_PROT_OFFSET2 0x01b7 +#define ANA_IP1_NXT_PROT_OFFSET2_MASK GENMASK(6, 0) +#define ANA_IP1_NXT_PROT_OFFSET2(x) ((x) & ANA_IP1_NXT_PROT_OFFSET2_MASK) + +#define MSCC_ANA_IP1_NXT_PROT_UDP_CHKSUM 0x01b8 +#define IP1_NXT_PROT_UDP_CHKSUM_OFF_MASK GENMASK(15, 8) +#define IP1_NXT_PROT_UDP_CHKSUM_OFF(x) (((x) << 8) & IP1_NXT_PROT_UDP_CHKSUM_OFF_MASK) +#define IP1_NXT_PROT_UDP_CHKSUM_WIDTH_MASK GENMASK(5, 4) +#define IP1_NXT_PROT_UDP_CHKSUM_WIDTH(x) (((x) << 4) & IP1_NXT_PROT_UDP_CHKSUM_WIDTH_MASK) +#define IP1_NXT_PROT_UDP_CHKSUM_UPDATE 0x0002 +#define IP1_NXT_PROT_UDP_CHKSUM_CLEAR 0x0001 + +#define MSCC_ANA_IP1_FLOW_ENA(x) (0x01c0 + ((x) << 4)) +#define IP1_FLOW_MATCH_ADDR_MASK GENMASK(9, 8) +#define IP1_FLOW_MATCH_DEST_SRC_ADDR 0x0200 +#define IP1_FLOW_MATCH_DEST_ADDR 0x0100 +#define IP1_FLOW_MATCH_SRC_ADDR 0x0000 +#define IP1_FLOW_ENA_CHANNEL_MASK_MASK GENMASK(5, 4) +#define IP1_FLOW_ENA_CHANNEL_MASK(x) (((x) << 4) & IP1_FLOW_ENA_CHANNEL_MASK_MASK) +#define IP1_FLOW_VALID_CH1 IP1_FLOW_ENA_CHANNEL_MASK(2) +#define IP1_FLOW_VALID_CH0 IP1_FLOW_ENA_CHANNEL_MASK(1) +#define IP1_FLOW_ENA 0x0001 + +#define MSCC_ANA_OAM_PTP_FLOW_ENA(x) (0x1e0 + ((x) << 4)) +#define MSCC_ANA_OAM_PTP_FLOW_MATCH_LOWER(x) (MSCC_ANA_OAM_PTP_FLOW_ENA(x) + 2) +#define MSCC_ANA_OAM_PTP_FLOW_MASK_LOWER(x) (MSCC_ANA_OAM_PTP_FLOW_ENA(x) + 4) + +#define MSCC_ANA_OAM_PTP_FLOW_PTP_0_FIELD(x) (MSCC_ANA_OAM_PTP_FLOW_ENA(x) + 8) + +#define MSCC_ANA_IP1_FLOW_MATCH_UPPER(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 1) +#define MSCC_ANA_IP1_FLOW_MATCH_UPPER_MID(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 2) +#define MSCC_ANA_IP1_FLOW_MATCH_LOWER_MID(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 3) +#define MSCC_ANA_IP1_FLOW_MATCH_LOWER(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 4) +#define MSCC_ANA_IP1_FLOW_MASK_UPPER(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 5) +#define MSCC_ANA_IP1_FLOW_MASK_UPPER_MID(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 6) +#define MSCC_ANA_IP1_FLOW_MASK_LOWER_MID(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 7) +#define MSCC_ANA_IP1_FLOW_MASK_LOWER(x) (MSCC_ANA_IP1_FLOW_ENA(x) + 8) + +#define MSCC_ANA_IP2_NXT_PROT_NXT_COMP 0x0240 +#define ANA_IP2_NXT_PROT_NXT_COMP_BYTES_HDR_MASK GENMASK(15, 8) +#define ANA_IP2_NXT_PROT_NXT_COMP_BYTES_HDR(x) (((x) << 8) & ANA_IP2_NXT_PROT_NXT_COMP_BYTES_HDR_MASK) +#define ANA_IP2_NXT_PROT_NXT_COMP_PTP_OAM 0x0005 +#define ANA_IP2_NXT_PROT_NXT_COMP_IP_UDP_ACH2 0x0003 + +#define MSCC_ANA_IP2_NXT_PROT_UDP_CHKSUM 0x0248 +#define IP2_NXT_PROT_UDP_CHKSUM_OFF_MASK GENMASK(15, 8) +#define IP2_NXT_PROT_UDP_CHKSUM_OFF(x) (((x) << 8) & IP2_NXT_PROT_UDP_CHKSUM_OFF_MASK) +#define IP2_NXT_PROT_UDP_CHKSUM_WIDTH_MASK GENMASK(5, 4) +#define IP2_NXT_PROT_UDP_CHKSUM_WIDTH(x) (((x) << 4) & IP2_NXT_PROT_UDP_CHKSUM_WIDTH_MASK) + +#define MSCC_ANA_IP2_FLOW_ENA(x) (0x0250 + ((x) << 4)) +#define IP2_FLOW_ENA_CHANNEL_MASK_MASK GENMASK(5, 4) +#define IP2_FLOW_ENA_CHANNEL_MASK(x) (((x) << 4) & IP2_FLOW_ENA_CHANNEL_MASK_MASK) +#define IP2_FLOW_VALID_CH1 IP2_FLOW_ENA_CHANNEL_MASK(2) +#define IP2_FLOW_VALID_CH0 IP2_FLOW_ENA_CHANNEL_MASK(1) + +#define MSCC_ANA_PTP_FLOW_ENA(x) (0x02d0 + ((x) << 4)) +#define PTP_FLOW_ENA_CHANNEL_MASK_MASK GENMASK(5, 4) +#define PTP_FLOW_ENA_CHANNEL_MASK(x) (((x) << 4) & PTP_FLOW_ENA_CHANNEL_MASK_MASK) +#define PTP_FLOW_VALID_CH1 PTP_FLOW_ENA_CHANNEL_MASK(2) +#define PTP_FLOW_VALID_CH0 PTP_FLOW_ENA_CHANNEL_MASK(1) +#define PTP_FLOW_ENA 0x0001 + +#define MSCC_ANA_PTP_FLOW_MATCH_UPPER(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 1) +#define PTP_FLOW_MSG_TYPE_MASK 0x0F000000 +#define PTP_FLOW_MSG_PDELAY_RESP 0x04000000 +#define PTP_FLOW_MSG_PDELAY_REQ 0x02000000 +#define PTP_FLOW_MSG_DELAY_REQ 0x01000000 +#define PTP_FLOW_MSG_SYNC 0x00000000 + +#define MSCC_ANA_PTP_FLOW_MATCH_LOWER(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 2) +#define MSCC_ANA_PTP_FLOW_MASK_UPPER(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 3) +#define MSCC_ANA_PTP_FLOW_MASK_LOWER(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 4) + +#define MSCC_ANA_PTP_FLOW_DOMAIN_RANGE(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 5) +#define PTP_FLOW_DOMAIN_RANGE_ENA 0x0001 + +#define MSCC_ANA_PTP_FLOW_PTP_ACTION(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 6) +#define PTP_FLOW_PTP_ACTION_MOD_FRAME_STATUS_UPDATE 0x10000000 +#define PTP_FLOW_PTP_ACTION_MOD_FRAME_STATUS_BYTE_OFFSET_MASK GENMASK(26, 24) +#define PTP_FLOW_PTP_ACTION_MOD_FRAME_STATUS_BYTE_OFFSET(x) (((x) << 24) & PTP_FLOW_PTP_ACTION_MOD_FRAME_STATUS_BYTE_OFFSET_MASK) +#define PTP_FLOW_PTP_ACTION_PTP_CMD_MASK GENMASK(3, 0) +#define PTP_FLOW_PTP_ACTION_PTP_CMD(x) ((x) & PTP_FLOW_PTP_ACTION_PTP_CMD_MASK) +#define PTP_FLOW_PTP_ACTION_SUB_DELAY_ASYM 0x00200000 +#define PTP_FLOW_PTP_ACTION_ADD_DELAY_ASYM 0x00100000 +#define PTP_FLOW_PTP_ACTION_TIME_OFFSET_MASK GENMASK(15, 10) +#define PTP_FLOW_PTP_ACTION_TIME_OFFSET(x) (((x) << 10) & PTP_FLOW_PTP_ACTION_TIME_OFFSET_MASK) +#define PTP_FLOW_PTP_ACTION_CORR_OFFSET_MASK GENMASK(9, 5) +#define PTP_FLOW_PTP_ACTION_CORR_OFFSET(x) (((x) << 5) & PTP_FLOW_PTP_ACTION_CORR_OFFSET_MASK) +#define PTP_FLOW_PTP_ACTION_SAVE_LOCAL_TIME 0x00000010 + +#define MSCC_ANA_PTP_FLOW_PTP_ACTION2(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 7) +#define PTP_FLOW_PTP_ACTION2_REWRITE_OFFSET_MASK GENMASK(15, 8) +#define PTP_FLOW_PTP_ACTION2_REWRITE_OFFSET(x) (((x) << 8) & PTP_FLOW_PTP_ACTION2_REWRITE_OFFSET_MASK) +#define PTP_FLOW_PTP_ACTION2_REWRITE_BYTES_MASK GENMASK(3, 0) +#define PTP_FLOW_PTP_ACTION2_REWRITE_BYTES(x) ((x) & PTP_FLOW_PTP_ACTION2_REWRITE_BYTES_MASK) + +#define MSCC_ANA_PTP_FLOW_PTP_0_FIELD(x) (MSCC_ANA_PTP_FLOW_ENA(x) + 8) +#define PTP_FLOW_PTP_0_FIELD_PTP_FRAME 0x8000 +#define PTP_FLOW_PTP_0_FIELD_RSVRD_CHECK 0x4000 +#define PTP_FLOW_PTP_0_FIELD_OFFSET_MASK GENMASK(13, 8) +#define PTP_FLOW_PTP_0_FIELD_OFFSET(x) (((x) << 8) & PTP_FLOW_PTP_0_FIELD_OFFSET_MASK) +#define PTP_FLOW_PTP_0_FIELD_BYTES_MASK GENMASK(3, 0) +#define PTP_FLOW_PTP_0_FIELD_BYTES(x) ((x) & PTP_FLOW_PTP_0_FIELD_BYTES_MASK) + +#define MSCC_ANA_PTP_IP_CHKSUM_SEL 0x0330 +#define ANA_PTP_IP_CHKSUM_SEL_IP_COMP_2 0x0001 +#define ANA_PTP_IP_CHKSUM_SEL_IP_COMP_1 0x0000 + +#define MSCC_PHY_ANA_FSB_CFG 0x331 +#define ANA_FSB_ADDR_FROM_BLOCK_SEL_MASK GENMASK(1, 0) +#define ANA_FSB_ADDR_FROM_IP2 0x0003 +#define ANA_FSB_ADDR_FROM_IP1 0x0002 +#define ANA_FSB_ADDR_FROM_ETH2 0x0001 +#define ANA_FSB_ADDR_FROM_ETH1 0x0000 + +#define MSCC_PHY_ANA_FSB_REG(x) (0x332 + (x)) + +#define COMP_MAX_FLOWS 8 +#define PTP_COMP_MAX_FLOWS 6 + +#define PPS_WIDTH_ADJ 0x1dcd6500 +#define STALL_EGR_LATENCY(x) (1536000 / (x)) + +/* PHC clock available frequencies. */ +enum { + PHC_CLK_125MHZ, + PHC_CLK_156_25MHZ, + PHC_CLK_200MHZ, + PHC_CLK_250MHZ, + PHC_CLK_500MHZ, +}; + +enum ptp_cmd { + PTP_NOP = 0, + PTP_WRITE_1588 = 5, + PTP_WRITE_NS = 7, + PTP_SAVE_IN_TS_FIFO = 11, /* invalid when writing in reg */ +}; + +enum vsc85xx_ptp_msg_type { + PTP_MSG_TYPE_SYNC, + PTP_MSG_TYPE_DELAY_REQ, +}; + +struct vsc85xx_ptphdr { + u8 tsmt; /* transportSpecific | messageType */ + u8 ver; /* reserved0 | versionPTP */ + __be16 msglen; + u8 domain; + u8 rsrvd1; + __be16 flags; + __be64 correction; + __be32 rsrvd2; + __be64 clk_identity; + __be16 src_port_id; + __be16 seq_id; + u8 ctrl; + u8 log_interval; +} __attribute__((__packed__)); + +/* Represents an entry in the timestamping FIFO */ +struct vsc85xx_ts_fifo { + u32 ns; + u64 secs:48; + u8 sig[16]; +} __attribute__((__packed__)); + +struct vsc85xx_ptp { + struct phy_device *phydev; + struct ptp_clock *ptp_clock; + struct ptp_clock_info caps; + struct sk_buff_head tx_queue; + enum hwtstamp_tx_types tx_type; + enum hwtstamp_rx_filters rx_filter; + u8 configured:1; +}; + +#endif /* _MSCC_PHY_PTP_H_ */ diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index defe09d94422..bd11e62bfdfe 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -219,7 +219,7 @@ int genphy_c45_read_link(struct phy_device *phydev) int val, devad; bool link = true; - if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) { + if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) { val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); if (val < 0) return val; @@ -409,7 +409,7 @@ int genphy_c45_pma_read_abilities(struct phy_device *phydev) int val; linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); - if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) { + if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) { val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); if (val < 0) return val; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index b4978c5fb2ca..5998fb505b21 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -31,6 +31,7 @@ #include <linux/mdio.h> #include <linux/io.h> #include <linux/uaccess.h> +#include <linux/property.h> MODULE_DESCRIPTION("PHY library"); MODULE_AUTHOR("Andy Fleming"); @@ -661,6 +662,28 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, } EXPORT_SYMBOL(phy_device_create); +/* phy_c45_probe_present - checks to see if a MMD is present in the package + * @bus: the target MII bus + * @prtad: PHY package address on the MII bus + * @devad: PHY device (MMD) address + * + * Read the MDIO_STAT2 register, and check whether a device is responding + * at this address. + * + * Returns: negative error number on bus access error, zero if no device + * is responding, or positive if a device is present. + */ +static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad) +{ + int stat2; + + stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2); + if (stat2 < 0) + return stat2; + + return (stat2 & MDIO_STAT2_DEVPRST) == MDIO_STAT2_DEVPRST_VAL; +} + /* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers. * @bus: the target MII bus * @addr: PHY address on the MII bus @@ -687,9 +710,6 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, return -EIO; *devices_in_package |= phy_reg; - /* Bit 0 doesn't represent a device, it indicates c22 regs presence */ - *devices_in_package &= ~BIT(0); - return 0; } @@ -697,54 +717,77 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. * @bus: the target MII bus * @addr: PHY address on the MII bus - * @phy_id: where to store the ID retrieved. * @c45_ids: where to store the c45 ID information. * - * If the PHY devices-in-package appears to be valid, it and the - * corresponding identifiers are stored in @c45_ids, zero is stored - * in @phy_id. Otherwise 0xffffffff is stored in @phy_id. Returns - * zero on success. + * Read the PHY "devices in package". If this appears to be valid, read + * the PHY identifiers for each device. Return the "devices in package" + * and identifiers in @c45_ids. * + * Returns zero on success, %-EIO on bus access error, or %-ENODEV if + * the "devices in package" is invalid. */ -static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, +static int get_phy_c45_ids(struct mii_bus *bus, int addr, struct phy_c45_device_ids *c45_ids) { const int num_ids = ARRAY_SIZE(c45_ids->device_ids); - u32 *devs = &c45_ids->devices_in_package; - int i, phy_reg; + u32 devs_in_pkg = 0; + int i, ret, phy_reg; /* Find first non-zero Devices In package. Device zero is reserved * for 802.3 c45 complied PHYs, so don't probe it at first. */ - for (i = 1; i < num_ids && *devs == 0; i++) { - phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, devs); - if (phy_reg < 0) - return -EIO; - - if ((*devs & 0x1fffffff) == 0x1fffffff) { - /* If mostly Fs, there is no device there, - * then let's continue to probe more, as some - * 10G PHYs have zero Devices In package, - * e.g. Cortina CS4315/CS4340 PHY. + for (i = 1; i < MDIO_MMD_NUM && devs_in_pkg == 0; i++) { + if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) { + /* Check that there is a device present at this + * address before reading the devices-in-package + * register to avoid reading garbage from the PHY. + * Some PHYs (88x3310) vendor space is not IEEE802.3 + * compliant. */ - phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, devs); - if (phy_reg < 0) + ret = phy_c45_probe_present(bus, addr, i); + if (ret < 0) return -EIO; - /* no device there, let's get out of here */ - if ((*devs & 0x1fffffff) == 0x1fffffff) { - *phy_id = 0xffffffff; - return 0; - } else { - break; - } + + if (!ret) + continue; } + phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg); + if (phy_reg < 0) + return -EIO; + } + + if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) { + /* If mostly Fs, there is no device there, then let's probe + * MMD 0, as some 10G PHYs have zero Devices In package, + * e.g. Cortina CS4315/CS4340 PHY. + */ + phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg); + if (phy_reg < 0) + return -EIO; + + /* no device there, let's get out of here */ + if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) + return -ENODEV; } /* Now probe Device Identifiers for each device present. */ for (i = 1; i < num_ids; i++) { - if (!(c45_ids->devices_in_package & (1 << i))) + if (!(devs_in_pkg & (1 << i))) continue; + if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) { + /* Probe the "Device Present" bits for the vendor MMDs + * to ignore these if they do not contain IEEE 802.3 + * registers. + */ + ret = phy_c45_probe_present(bus, addr, i); + if (ret < 0) + return ret; + + if (!ret) + continue; + } + phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1); if (phy_reg < 0) return -EIO; @@ -755,34 +798,29 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, return -EIO; c45_ids->device_ids[i] |= phy_reg; } - *phy_id = 0; + + c45_ids->devices_in_package = devs_in_pkg; + /* Bit 0 doesn't represent a device, it indicates c22 regs presence */ + c45_ids->mmds_present = devs_in_pkg & ~BIT(0); + return 0; } /** - * get_phy_id - reads the specified addr for its ID. + * get_phy_c22_id - reads the specified addr for its clause 22 ID. * @bus: the target MII bus * @addr: PHY address on the MII bus * @phy_id: where to store the ID retrieved. - * @is_c45: If true the PHY uses the 802.3 clause 45 protocol - * @c45_ids: where to store the c45 ID information. - * - * Description: In the case of a 802.3-c22 PHY, reads the ID registers - * of the PHY at @addr on the @bus, stores it in @phy_id and returns - * zero on success. - * - * In the case of a 802.3-c45 PHY, get_phy_c45_ids() is invoked, and - * its return value is in turn returned. * + * Read the 802.3 clause 22 PHY ID from the PHY at @addr on the @bus, + * placing it in @phy_id. Return zero on successful read and the ID is + * valid, %-EIO on bus access error, or %-ENODEV if no device responds + * or invalid ID. */ -static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, - bool is_c45, struct phy_c45_device_ids *c45_ids) +static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id) { int phy_reg; - if (is_c45) - return get_phy_c45_ids(bus, addr, phy_id, c45_ids); - /* Grab the bits from PHYIR1, and put them in the upper half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); if (phy_reg < 0) { @@ -801,6 +839,10 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, *phy_id |= phy_reg; + /* If the phy_id is mostly Fs, there is no device there */ + if ((*phy_id & 0x1fffffff) == 0x1fffffff) + return -ENODEV; + return 0; } @@ -811,8 +853,17 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, * @addr: PHY address on the MII bus * @is_c45: If true the PHY uses the 802.3 clause 45 protocol * - * Description: Reads the ID registers of the PHY at @addr on the - * @bus, then allocates and returns the phy_device to represent it. + * Probe for a PHY at @addr on @bus. + * + * When probing for a clause 22 PHY, then read the ID registers. If we find + * a valid ID, allocate and return a &struct phy_device. + * + * When probing for a clause 45 PHY, read the "devices in package" registers. + * If the "devices in package" appears valid, read the ID registers for each + * MMD, allocate and return a &struct phy_device. + * + * Returns an allocated &struct phy_device on success, %-ENODEV if there is + * no PHY present, or %-EIO on bus access error. */ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) { @@ -821,16 +872,17 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) int r; c45_ids.devices_in_package = 0; + c45_ids.mmds_present = 0; memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids)); - r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); + if (is_c45) + r = get_phy_c45_ids(bus, addr, &c45_ids); + else + r = get_phy_c22_id(bus, addr, &phy_id); + if (r) return ERR_PTR(r); - /* If the phy_id is mostly Fs, there is no device there */ - if ((phy_id & 0x1fffffff) == 0x1fffffff) - return ERR_PTR(-ENODEV); - return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids); } EXPORT_SYMBOL(get_phy_device); @@ -2663,6 +2715,104 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) } EXPORT_SYMBOL(phy_get_pause); +#if IS_ENABLED(CONFIG_OF_MDIO) +static int phy_get_int_delay_property(struct device *dev, const char *name) +{ + s32 int_delay; + int ret; + + ret = device_property_read_u32(dev, name, &int_delay); + if (ret) + return ret; + + return int_delay; +} +#else +static int phy_get_int_delay_property(struct device *dev, const char *name) +{ + return -EINVAL; +} +#endif + +/** + * phy_get_delay_index - returns the index of the internal delay + * @phydev: phy_device struct + * @dev: pointer to the devices device struct + * @delay_values: array of delays the PHY supports + * @size: the size of the delay array + * @is_rx: boolean to indicate to get the rx internal delay + * + * Returns the index within the array of internal delay passed in. + * If the device property is not present then the interface type is checked + * if the interface defines use of internal delay then a 1 is returned otherwise + * a 0 is returned. + * The array must be in ascending order. If PHY does not have an ascending order + * array then size = 0 and the value of the delay property is returned. + * Return -EINVAL if the delay is invalid or cannot be found. + */ +s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, + const int *delay_values, int size, bool is_rx) +{ + s32 delay; + int i; + + if (is_rx) { + delay = phy_get_int_delay_property(dev, "rx-internal-delay-ps"); + if (delay < 0 && size == 0) { + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + return 1; + else + return 0; + } + + } else { + delay = phy_get_int_delay_property(dev, "tx-internal-delay-ps"); + if (delay < 0 && size == 0) { + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + return 1; + else + return 0; + } + } + + if (delay < 0) + return delay; + + if (delay && size == 0) + return delay; + + if (delay < delay_values[0] || delay > delay_values[size - 1]) { + phydev_err(phydev, "Delay %d is out of range\n", delay); + return -EINVAL; + } + + if (delay == delay_values[0]) + return 0; + + for (i = 1; i < size; i++) { + if (delay == delay_values[i]) + return i; + + /* Find an approximate index by looking up the table */ + if (delay > delay_values[i - 1] && + delay < delay_values[i]) { + if (delay - delay_values[i - 1] < + delay_values[i] - delay) + return i - 1; + else + return i; + } + } + + phydev_err(phydev, "error finding internal delay index for %d\n", + delay); + + return -EINVAL; +} +EXPORT_SYMBOL(phy_get_internal_delay); + static bool phy_drv_supports_irq(struct phy_driver *phydrv) { return phydrv->config_intr && phydrv->ack_interrupt; diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 3b7c70e6c5dd..dae6c8b51d7f 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -429,7 +429,8 @@ static void phylink_mac_config_up(struct phylink *pl, static void phylink_mac_pcs_an_restart(struct phylink *pl) { if (pl->link_config.an_enabled && - phy_interface_mode_is_8023z(pl->link_config.interface)) { + phy_interface_mode_is_8023z(pl->link_config.interface) && + phylink_autoneg_inband(pl->cur_link_an_mode)) { if (pl->pcs_ops) pl->pcs_ops->pcs_an_restart(pl->config); else @@ -1657,11 +1658,11 @@ static int phylink_phy_read(struct phylink *pl, unsigned int phy_id, case MII_BMSR: case MII_PHYSID1: case MII_PHYSID2: - devad = __ffs(phydev->c45_ids.devices_in_package); + devad = __ffs(phydev->c45_ids.mmds_present); break; case MII_ADVERTISE: case MII_LPA: - if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) + if (!(phydev->c45_ids.mmds_present & MDIO_DEVS_AN)) return -EINVAL; devad = MDIO_MMD_AN; if (reg == MII_ADVERTISE) @@ -1697,11 +1698,11 @@ static int phylink_phy_write(struct phylink *pl, unsigned int phy_id, case MII_BMSR: case MII_PHYSID1: case MII_PHYSID2: - devad = __ffs(phydev->c45_ids.devices_in_package); + devad = __ffs(phydev->c45_ids.mmds_present); break; case MII_ADVERTISE: case MII_LPA: - if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) + if (!(phydev->c45_ids.mmds_present & MDIO_DEVS_AN)) return -EINVAL; devad = MDIO_MMD_AN; if (reg == MII_ADVERTISE) @@ -1845,6 +1846,54 @@ int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) } EXPORT_SYMBOL_GPL(phylink_mii_ioctl); +/** + * phylink_speed_down() - set the non-SFP PHY to lowest speed supported by both + * link partners + * @pl: a pointer to a &struct phylink returned from phylink_create() + * @sync: perform action synchronously + * + * If we have a PHY that is not part of a SFP module, then set the speed + * as described in the phy_speed_down() function. Please see this function + * for a description of the @sync parameter. + * + * Returns zero if there is no PHY, otherwise as per phy_speed_down(). + */ +int phylink_speed_down(struct phylink *pl, bool sync) +{ + int ret = 0; + + ASSERT_RTNL(); + + if (!pl->sfp_bus && pl->phydev) + ret = phy_speed_down(pl->phydev, sync); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_speed_down); + +/** + * phylink_speed_up() - restore the advertised speeds prior to the call to + * phylink_speed_down() + * @pl: a pointer to a &struct phylink returned from phylink_create() + * + * If we have a PHY that is not part of a SFP module, then restore the + * PHY speeds as per phy_speed_up(). + * + * Returns zero if there is no PHY, otherwise as per phy_speed_up(). + */ +int phylink_speed_up(struct phylink *pl) +{ + int ret = 0; + + ASSERT_RTNL(); + + if (!pl->sfp_bus && pl->phydev) + ret = phy_speed_up(pl->phydev); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_speed_up); + static void phylink_sfp_attach(void *upstream, struct sfp_bus *bus) { struct phylink *pl = upstream; diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index dacb4f680fd4..c2e44083032c 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -1335,6 +1335,10 @@ static int __init tbnet_init(void) tb_property_add_immediate(tbnet_dir, "prtcid", 1); tb_property_add_immediate(tbnet_dir, "prtcvers", 1); tb_property_add_immediate(tbnet_dir, "prtcrevs", 1); + /* Currently only announce support for match frags ID (bit 1). Bit 0 + * is reserved for full E2E flow control which we do not support at + * the moment. + */ tb_property_add_immediate(tbnet_dir, "prtcstns", TBNET_MATCH_FRAGS_ID); diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 43928a1c2f2a..46599606ff10 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -21,6 +21,7 @@ #include <net/rtnetlink.h> #include <linux/u64_stats_sync.h> #include <linux/hashtable.h> +#include <linux/spinlock_types.h> #include <linux/inetdevice.h> #include <net/arp.h> @@ -35,12 +36,76 @@ #include <net/netns/generic.h> #define DRV_NAME "vrf" -#define DRV_VERSION "1.0" +#define DRV_VERSION "1.1" #define FIB_RULE_PREF 1000 /* default preference for FIB rules */ +#define HT_MAP_BITS 4 +#define HASH_INITVAL ((u32)0xcafef00d) + +struct vrf_map { + DECLARE_HASHTABLE(ht, HT_MAP_BITS); + spinlock_t vmap_lock; + + /* shared_tables: + * count how many distinct tables do not comply with the strict mode + * requirement. + * shared_tables value must be 0 in order to enable the strict mode. + * + * example of the evolution of shared_tables: + * | time + * add vrf0 --> table 100 shared_tables = 0 | t0 + * add vrf1 --> table 101 shared_tables = 0 | t1 + * add vrf2 --> table 100 shared_tables = 1 | t2 + * add vrf3 --> table 100 shared_tables = 1 | t3 + * add vrf4 --> table 101 shared_tables = 2 v t4 + * + * shared_tables is a "step function" (or "staircase function") + * and it is increased by one when the second vrf is associated to a + * table. + * + * at t2, vrf0 and vrf2 are bound to table 100: shared_tables = 1. + * + * at t3, another dev (vrf3) is bound to the same table 100 but the + * value of shared_tables is still 1. + * This means that no matter how many new vrfs will register on the + * table 100, the shared_tables will not increase (considering only + * table 100). + * + * at t4, vrf4 is bound to table 101, and shared_tables = 2. + * + * Looking at the value of shared_tables we can immediately know if + * the strict_mode can or cannot be enforced. Indeed, strict_mode + * can be enforced iff shared_tables = 0. + * + * Conversely, shared_tables is decreased when a vrf is de-associated + * from a table with exactly two associated vrfs. + */ + u32 shared_tables; + + bool strict_mode; +}; + +struct vrf_map_elem { + struct hlist_node hnode; + struct list_head vrf_list; /* VRFs registered to this table */ + + u32 table_id; + int users; + int ifindex; +}; + static unsigned int vrf_net_id; +/* per netns vrf data */ +struct netns_vrf { + /* protected by rtnl lock */ + bool add_fib_rules; + + struct vrf_map vmap; + struct ctl_table_header *ctl_hdr; +}; + struct net_vrf { struct rtable __rcu *rth; struct rt6_info __rcu *rt6; @@ -48,6 +113,9 @@ struct net_vrf { struct fib6_table *fib6_table; #endif u32 tb_id; + + struct list_head me_list; /* entry in vrf_map_elem */ + int ifindex; }; struct pcpu_dstats { @@ -103,6 +171,260 @@ static void vrf_get_stats64(struct net_device *dev, } } +static struct vrf_map *netns_vrf_map(struct net *net) +{ + struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id); + + return &nn_vrf->vmap; +} + +static struct vrf_map *netns_vrf_map_by_dev(struct net_device *dev) +{ + return netns_vrf_map(dev_net(dev)); +} + +static int vrf_map_elem_get_vrf_ifindex(struct vrf_map_elem *me) +{ + struct list_head *me_head = &me->vrf_list; + struct net_vrf *vrf; + + if (list_empty(me_head)) + return -ENODEV; + + vrf = list_first_entry(me_head, struct net_vrf, me_list); + + return vrf->ifindex; +} + +static struct vrf_map_elem *vrf_map_elem_alloc(gfp_t flags) +{ + struct vrf_map_elem *me; + + me = kmalloc(sizeof(*me), flags); + if (!me) + return NULL; + + return me; +} + +static void vrf_map_elem_free(struct vrf_map_elem *me) +{ + kfree(me); +} + +static void vrf_map_elem_init(struct vrf_map_elem *me, int table_id, + int ifindex, int users) +{ + me->table_id = table_id; + me->ifindex = ifindex; + me->users = users; + INIT_LIST_HEAD(&me->vrf_list); +} + +static struct vrf_map_elem *vrf_map_lookup_elem(struct vrf_map *vmap, + u32 table_id) +{ + struct vrf_map_elem *me; + u32 key; + + key = jhash_1word(table_id, HASH_INITVAL); + hash_for_each_possible(vmap->ht, me, hnode, key) { + if (me->table_id == table_id) + return me; + } + + return NULL; +} + +static void vrf_map_add_elem(struct vrf_map *vmap, struct vrf_map_elem *me) +{ + u32 table_id = me->table_id; + u32 key; + + key = jhash_1word(table_id, HASH_INITVAL); + hash_add(vmap->ht, &me->hnode, key); +} + +static void vrf_map_del_elem(struct vrf_map_elem *me) +{ + hash_del(&me->hnode); +} + +static void vrf_map_lock(struct vrf_map *vmap) __acquires(&vmap->vmap_lock) +{ + spin_lock(&vmap->vmap_lock); +} + +static void vrf_map_unlock(struct vrf_map *vmap) __releases(&vmap->vmap_lock) +{ + spin_unlock(&vmap->vmap_lock); +} + +static bool vrf_strict_mode(struct vrf_map *vmap) +{ + bool strict_mode; + + vrf_map_lock(vmap); + strict_mode = vmap->strict_mode; + vrf_map_unlock(vmap); + + return strict_mode; +} + +static int vrf_strict_mode_change(struct vrf_map *vmap, bool new_mode) +{ + bool *cur_mode; + int res = 0; + + vrf_map_lock(vmap); + + cur_mode = &vmap->strict_mode; + if (*cur_mode == new_mode) + goto unlock; + + if (*cur_mode) { + /* disable strict mode */ + *cur_mode = false; + } else { + if (vmap->shared_tables) { + /* we cannot allow strict_mode because there are some + * vrfs that share one or more tables. + */ + res = -EBUSY; + goto unlock; + } + + /* no tables are shared among vrfs, so we can go back + * to 1:1 association between a vrf with its table. + */ + *cur_mode = true; + } + +unlock: + vrf_map_unlock(vmap); + + return res; +} + +/* called with rtnl lock held */ +static int +vrf_map_register_dev(struct net_device *dev, struct netlink_ext_ack *extack) +{ + struct vrf_map *vmap = netns_vrf_map_by_dev(dev); + struct net_vrf *vrf = netdev_priv(dev); + struct vrf_map_elem *new_me, *me; + u32 table_id = vrf->tb_id; + bool free_new_me = false; + int users; + int res; + + /* we pre-allocate elements used in the spin-locked section (so that we + * keep the spinlock as short as possibile). + */ + new_me = vrf_map_elem_alloc(GFP_KERNEL); + if (!new_me) + return -ENOMEM; + + vrf_map_elem_init(new_me, table_id, dev->ifindex, 0); + + vrf_map_lock(vmap); + + me = vrf_map_lookup_elem(vmap, table_id); + if (!me) { + me = new_me; + vrf_map_add_elem(vmap, me); + goto link_vrf; + } + + /* we already have an entry in the vrf_map, so it means there is (at + * least) a vrf registered on the specific table. + */ + free_new_me = true; + if (vmap->strict_mode) { + /* vrfs cannot share the same table */ + NL_SET_ERR_MSG(extack, "Table is used by another VRF"); + res = -EBUSY; + goto unlock; + } + +link_vrf: + users = ++me->users; + if (users == 2) + ++vmap->shared_tables; + + list_add(&vrf->me_list, &me->vrf_list); + + res = 0; + +unlock: + vrf_map_unlock(vmap); + + /* clean-up, if needed */ + if (free_new_me) + vrf_map_elem_free(new_me); + + return res; +} + +/* called with rtnl lock held */ +static void vrf_map_unregister_dev(struct net_device *dev) +{ + struct vrf_map *vmap = netns_vrf_map_by_dev(dev); + struct net_vrf *vrf = netdev_priv(dev); + u32 table_id = vrf->tb_id; + struct vrf_map_elem *me; + int users; + + vrf_map_lock(vmap); + + me = vrf_map_lookup_elem(vmap, table_id); + if (!me) + goto unlock; + + list_del(&vrf->me_list); + + users = --me->users; + if (users == 1) { + --vmap->shared_tables; + } else if (users == 0) { + vrf_map_del_elem(me); + + /* no one will refer to this element anymore */ + vrf_map_elem_free(me); + } + +unlock: + vrf_map_unlock(vmap); +} + +/* return the vrf device index associated with the table_id */ +static int vrf_ifindex_lookup_by_table_id(struct net *net, u32 table_id) +{ + struct vrf_map *vmap = netns_vrf_map(net); + struct vrf_map_elem *me; + int ifindex; + + vrf_map_lock(vmap); + + if (!vmap->strict_mode) { + ifindex = -EPERM; + goto unlock; + } + + me = vrf_map_lookup_elem(vmap, table_id); + if (!me) { + ifindex = -ENODEV; + goto unlock; + } + + ifindex = vrf_map_elem_get_vrf_ifindex(me); + +unlock: + vrf_map_unlock(vmap); + + return ifindex; +} + /* by default VRF devices do not have a qdisc and are expected * to be created with only a single queue. */ @@ -1319,6 +1641,8 @@ static void vrf_dellink(struct net_device *dev, struct list_head *head) netdev_for_each_lower_dev(dev, port_dev, iter) vrf_del_slave(dev, port_dev); + vrf_map_unregister_dev(dev); + unregister_netdevice_queue(dev, head); } @@ -1327,6 +1651,7 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, struct netlink_ext_ack *extack) { struct net_vrf *vrf = netdev_priv(dev); + struct netns_vrf *nn_vrf; bool *add_fib_rules; struct net *net; int err; @@ -1349,11 +1674,26 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, if (err) goto out; + /* mapping between table_id and vrf; + * note: such binding could not be done in the dev init function + * because dev->ifindex id is not available yet. + */ + vrf->ifindex = dev->ifindex; + + err = vrf_map_register_dev(dev, extack); + if (err) { + unregister_netdevice(dev); + goto out; + } + net = dev_net(dev); - add_fib_rules = net_generic(net, vrf_net_id); + nn_vrf = net_generic(net, vrf_net_id); + + add_fib_rules = &nn_vrf->add_fib_rules; if (*add_fib_rules) { err = vrf_add_fib_rules(dev); if (err) { + vrf_map_unregister_dev(dev); unregister_netdevice(dev); goto out; } @@ -1440,20 +1780,102 @@ static struct notifier_block vrf_notifier_block __read_mostly = { .notifier_call = vrf_device_event, }; +static int vrf_map_init(struct vrf_map *vmap) +{ + spin_lock_init(&vmap->vmap_lock); + hash_init(vmap->ht); + + vmap->strict_mode = false; + + return 0; +} + +static int vrf_shared_table_handler(struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + struct net *net = (struct net *)table->extra1; + struct vrf_map *vmap = netns_vrf_map(net); + int proc_strict_mode = 0; + struct ctl_table tmp = { + .procname = table->procname, + .data = &proc_strict_mode, + .maxlen = sizeof(int), + .mode = table->mode, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }; + int ret; + + if (!write) + proc_strict_mode = vrf_strict_mode(vmap); + + ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); + + if (write && ret == 0) + ret = vrf_strict_mode_change(vmap, (bool)proc_strict_mode); + + return ret; +} + +static const struct ctl_table vrf_table[] = { + { + .procname = "strict_mode", + .data = NULL, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = vrf_shared_table_handler, + /* set by the vrf_netns_init */ + .extra1 = NULL, + }, + { }, +}; + /* Initialize per network namespace state */ static int __net_init vrf_netns_init(struct net *net) { - bool *add_fib_rules = net_generic(net, vrf_net_id); + struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id); + struct ctl_table *table; + int res; + + nn_vrf->add_fib_rules = true; + vrf_map_init(&nn_vrf->vmap); + + table = kmemdup(vrf_table, sizeof(vrf_table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + /* init the extra1 parameter with the reference to current netns */ + table[0].extra1 = net; - *add_fib_rules = true; + nn_vrf->ctl_hdr = register_net_sysctl(net, "net/vrf", table); + if (!nn_vrf->ctl_hdr) { + res = -ENOMEM; + goto free_table; + } return 0; + +free_table: + kfree(table); + + return res; +} + +static void __net_exit vrf_netns_exit(struct net *net) +{ + struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id); + struct ctl_table *table; + + table = nn_vrf->ctl_hdr->ctl_table_arg; + unregister_net_sysctl_table(nn_vrf->ctl_hdr); + kfree(table); } static struct pernet_operations vrf_net_ops __net_initdata = { .init = vrf_netns_init, + .exit = vrf_netns_exit, .id = &vrf_net_id, - .size = sizeof(bool), + .size = sizeof(struct netns_vrf), }; static int __init vrf_init_module(void) @@ -1466,14 +1888,24 @@ static int __init vrf_init_module(void) if (rc < 0) goto error; + rc = l3mdev_table_lookup_register(L3MDEV_TYPE_VRF, + vrf_ifindex_lookup_by_table_id); + if (rc < 0) + goto unreg_pernet; + rc = rtnl_link_register(&vrf_link_ops); - if (rc < 0) { - unregister_pernet_subsys(&vrf_net_ops); - goto error; - } + if (rc < 0) + goto table_lookup_unreg; return 0; +table_lookup_unreg: + l3mdev_table_lookup_unregister(L3MDEV_TYPE_VRF, + vrf_ifindex_lookup_by_table_id); + +unreg_pernet: + unregister_pernet_subsys(&vrf_net_ops); + error: unregister_netdevice_notifier(&vrf_notifier_block); return rc; diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index ef6f818ce5b3..eb84507de28a 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -385,7 +385,7 @@ struct phy_device *of_phy_connect(struct net_device *dev, if (!phy) return NULL; - phy->dev_flags = flags; + phy->dev_flags |= flags; ret = phy_connect_direct(dev, phy, hndlr, iface); diff --git a/drivers/ptp/ptp_pch.c b/drivers/ptp/ptp_pch.c index dcd6e00c8046..ce10ecd41ba0 100644 --- a/drivers/ptp/ptp_pch.c +++ b/drivers/ptp/ptp_pch.c @@ -508,40 +508,8 @@ static const struct ptp_clock_info ptp_pch_caps = { .enable = ptp_pch_enable, }; - -#ifdef CONFIG_PM -static s32 pch_suspend(struct pci_dev *pdev, pm_message_t state) -{ - pci_disable_device(pdev); - pci_enable_wake(pdev, PCI_D3hot, 0); - - if (pci_save_state(pdev) != 0) { - dev_err(&pdev->dev, "could not save PCI config state\n"); - return -ENOMEM; - } - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - - return 0; -} - -static s32 pch_resume(struct pci_dev *pdev) -{ - s32 ret; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, "pci_enable_device failed\n"); - return ret; - } - pci_enable_wake(pdev, PCI_D3hot, 0); - return 0; -} -#else #define pch_suspend NULL #define pch_resume NULL -#endif static void pch_remove(struct pci_dev *pdev) { @@ -684,13 +652,14 @@ static const struct pci_device_id pch_ieee1588_pcidev_id[] = { {0} }; +static SIMPLE_DEV_PM_OPS(pch_pm_ops, pch_suspend, pch_resume); + static struct pci_driver pch_driver = { .name = KBUILD_MODNAME, .id_table = pch_ieee1588_pcidev_id, .probe = pch_probe, .remove = pch_remove, - .suspend = pch_suspend, - .resume = pch_resume, + .driver.pm = &pch_pm_ops, }; static void __exit ptp_pch_exit(void) diff --git a/include/linux/icmpv6.h b/include/linux/icmpv6.h index 33d379602314..1b3371ae8193 100644 --- a/include/linux/icmpv6.h +++ b/include/linux/icmpv6.h @@ -13,12 +13,32 @@ static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb) #include <linux/netdevice.h> #if IS_ENABLED(CONFIG_IPV6) -extern void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info); typedef void ip6_icmp_send_t(struct sk_buff *skb, u8 type, u8 code, __u32 info, const struct in6_addr *force_saddr); +#if IS_BUILTIN(CONFIG_IPV6) +void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, + const struct in6_addr *force_saddr); +static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) +{ + icmp6_send(skb, type, code, info, NULL); +} +static inline int inet6_register_icmp_sender(ip6_icmp_send_t *fn) +{ + BUILD_BUG_ON(fn != icmp6_send); + return 0; +} +static inline int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn) +{ + BUILD_BUG_ON(fn != icmp6_send); + return 0; +} +#else +extern void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info); extern int inet6_register_icmp_sender(ip6_icmp_send_t *fn); extern int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn); +#endif + int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type, unsigned int data_len); diff --git a/include/linux/indirect_call_wrapper.h b/include/linux/indirect_call_wrapper.h index 00d7e8e919c6..54c02c84906a 100644 --- a/include/linux/indirect_call_wrapper.h +++ b/include/linux/indirect_call_wrapper.h @@ -23,6 +23,16 @@ likely(f == f2) ? f2(__VA_ARGS__) : \ INDIRECT_CALL_1(f, f1, __VA_ARGS__); \ }) +#define INDIRECT_CALL_3(f, f3, f2, f1, ...) \ + ({ \ + likely(f == f3) ? f3(__VA_ARGS__) : \ + INDIRECT_CALL_2(f, f2, f1, __VA_ARGS__); \ + }) +#define INDIRECT_CALL_4(f, f4, f3, f2, f1, ...) \ + ({ \ + likely(f == f4) ? f4(__VA_ARGS__) : \ + INDIRECT_CALL_3(f, f3, f2, f1, __VA_ARGS__); \ + }) #define INDIRECT_CALLABLE_DECLARE(f) f #define INDIRECT_CALLABLE_SCOPE @@ -30,6 +40,8 @@ #else #define INDIRECT_CALL_1(f, f1, ...) f(__VA_ARGS__) #define INDIRECT_CALL_2(f, f2, f1, ...) f(__VA_ARGS__) +#define INDIRECT_CALL_3(f, f3, f2, f1, ...) f(__VA_ARGS__) +#define INDIRECT_CALL_4(f, f4, f3, f2, f1, ...) f(__VA_ARGS__) #define INDIRECT_CALLABLE_DECLARE(f) #define INDIRECT_CALLABLE_SCOPE static #endif diff --git a/include/linux/marvell_phy.h b/include/linux/marvell_phy.h index af6b11d4d673..ff7b7607c8cf 100644 --- a/include/linux/marvell_phy.h +++ b/include/linux/marvell_phy.h @@ -15,10 +15,12 @@ #define MARVELL_PHY_ID_88E1149R 0x01410e50 #define MARVELL_PHY_ID_88E1240 0x01410e30 #define MARVELL_PHY_ID_88E1318S 0x01410e90 +#define MARVELL_PHY_ID_88E1340S 0x01410dc0 #define MARVELL_PHY_ID_88E1116R 0x01410e40 #define MARVELL_PHY_ID_88E1510 0x01410dd0 #define MARVELL_PHY_ID_88E1540 0x01410eb0 #define MARVELL_PHY_ID_88E1545 0x01410ea0 +#define MARVELL_PHY_ID_88E1548P 0x01410ec0 #define MARVELL_PHY_ID_88E3016 0x01410e60 #define MARVELL_PHY_ID_88X3310 0x002b09a0 #define MARVELL_PHY_ID_88E2110 0x002b09b0 diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index 8170da1e9f70..4db87bcfce7b 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -75,7 +75,7 @@ void mlx5_query_min_inline(struct mlx5_core_dev *mdev, u8 *min_inline); int mlx5_modify_nic_vport_min_inline(struct mlx5_core_dev *mdev, u16 vport, u8 min_inline); int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *dev, - u16 vport, u8 *addr); + u16 vport, const u8 *addr); int mlx5_query_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 *mtu); int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu); int mlx5_query_nic_vport_system_image_guid(struct mlx5_core_dev *mdev, diff --git a/include/linux/phy.h b/include/linux/phy.h index b693b609b2f5..ecc7640705b9 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -244,7 +244,8 @@ struct phy_package_shared { }; /* used as bit number in atomic bitops */ -#define PHY_SHARED_F_INIT_DONE 0 +#define PHY_SHARED_F_INIT_DONE 0 +#define PHY_SHARED_F_PROBE_DONE 1 /* * The Bus class for PHYs. Devices which provide access to @@ -298,6 +299,14 @@ struct mii_bus { /* RESET GPIO descriptor pointer */ struct gpio_desc *reset_gpiod; + /* bus capabilities, used for probing */ + enum { + MDIOBUS_NO_CAP = 0, + MDIOBUS_C22, + MDIOBUS_C45, + MDIOBUS_C22_C45, + } probe_capabilities; + /* protect access to the shared element */ struct mutex shared_lock; @@ -388,14 +397,18 @@ enum phy_state { PHY_CABLETEST, }; +#define MDIO_MMD_NUM 32 + /** * struct phy_c45_device_ids - 802.3-c45 Device Identifiers - * @devices_in_package: Bit vector of devices present. + * @devices_in_package: IEEE 802.3 devices in package register value. + * @mmds_present: bit vector of MMDs present. * @device_ids: The device identifer for each present device. */ struct phy_c45_device_ids { u32 devices_in_package; - u32 device_ids[8]; + u32 mmds_present; + u32 device_ids[MDIO_MMD_NUM]; }; struct macsec_context; @@ -1431,6 +1444,10 @@ void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx); bool phy_validate_pause(struct phy_device *phydev, struct ethtool_pauseparam *pp); void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause); + +s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, + const int *delay_values, int size, bool is_rx); + void phy_resolve_pause(unsigned long *local_adv, unsigned long *partner_adv, bool *tx_pause, bool *rx_pause); @@ -1555,14 +1572,25 @@ static inline int __phy_package_write(struct phy_device *phydev, return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); } -static inline bool phy_package_init_once(struct phy_device *phydev) +static inline bool __phy_package_set_once(struct phy_device *phydev, + unsigned int b) { struct phy_package_shared *shared = phydev->shared; if (!shared) return false; - return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags); + return !test_and_set_bit(b, &shared->flags); +} + +static inline bool phy_package_init_once(struct phy_device *phydev) +{ + return __phy_package_set_once(phydev, PHY_SHARED_F_INIT_DONE); +} + +static inline bool phy_package_probe_once(struct phy_device *phydev) +{ + return __phy_package_set_once(phydev, PHY_SHARED_F_PROBE_DONE); } extern struct bus_type mdio_bus_type; diff --git a/include/linux/phylink.h b/include/linux/phylink.h index cc5b452a184e..b32b8b45421b 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -392,6 +392,8 @@ int phylink_init_eee(struct phylink *, bool); int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *); int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *); int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); +int phylink_speed_down(struct phylink *pl, bool sync); +int phylink_speed_up(struct phylink *pl); #define phylink_zero(bm) \ bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS) diff --git a/include/net/act_api.h b/include/net/act_api.h index 8c3934880670..cb382a89ea58 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -106,7 +106,7 @@ struct tc_action_ops { struct netlink_callback *, int, const struct tc_action_ops *, struct netlink_ext_ack *); - void (*stats_update)(struct tc_action *, u64, u32, u64, bool); + void (*stats_update)(struct tc_action *, u64, u64, u64, u64, bool); size_t (*get_fill_size)(const struct tc_action *act); struct net_device *(*get_dev)(const struct tc_action *a, tc_action_priv_destructor *destructor); @@ -232,8 +232,8 @@ static inline void tcf_action_inc_overlimit_qstats(struct tc_action *a) spin_unlock(&a->tcfa_lock); } -void tcf_action_update_stats(struct tc_action *a, u64 bytes, u32 packets, - bool drop, bool hw); +void tcf_action_update_stats(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, bool hw); int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); int tcf_action_check_ctrlact(int action, struct tcf_proto *tp, @@ -244,13 +244,14 @@ struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, #endif /* CONFIG_NET_CLS_ACT */ static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes, - u64 packets, u64 lastuse, bool hw) + u64 packets, u64 drops, + u64 lastuse, bool hw) { #ifdef CONFIG_NET_CLS_ACT if (!a->ops->stats_update) return; - a->ops->stats_update(a, bytes, packets, lastuse, hw); + a->ops->stats_update(a, bytes, packets, drops, lastuse, hw); #endif } diff --git a/include/net/bonding.h b/include/net/bonding.h index aa854a9c01e2..a00e1764e9b1 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -238,6 +238,9 @@ struct bonding { struct dentry *debug_dir; #endif /* CONFIG_DEBUG_FS */ struct rtnl_link_stats64 bond_stats; +#ifdef CONFIG_XFRM_OFFLOAD + struct xfrm_state *xs; +#endif /* CONFIG_XFRM_OFFLOAD */ }; #define bond_slave_get_rcu(dev) \ diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index 86e028388bad..b001fa91c14e 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -114,7 +114,11 @@ static inline void skb_mark_napi_id(struct sk_buff *skb, struct napi_struct *napi) { #ifdef CONFIG_NET_RX_BUSY_POLL - skb->napi_id = napi->napi_id; + /* If the skb was already marked with a valid NAPI ID, avoid overwriting + * it. + */ + if (skb->napi_id < MIN_NAPI_ID) + skb->napi_id = napi->napi_id; #endif } diff --git a/include/net/devlink.h b/include/net/devlink.h index 1df6dfec26c2..428f55f8197c 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1107,6 +1107,28 @@ struct devlink_ops { int (*trap_policer_counter_get)(struct devlink *devlink, const struct devlink_trap_policer *policer, u64 *p_drops); + /** + * @port_function_hw_addr_get: Port function's hardware address get function. + * + * Should be used by device drivers to report the hardware address of a function managed + * by the devlink port. Driver should return -EOPNOTSUPP if it doesn't support port + * function handling for a particular port. + * + * Note: @extack can be NULL when port notifier queries the port function. + */ + int (*port_function_hw_addr_get)(struct devlink *devlink, struct devlink_port *port, + u8 *hw_addr, int *hw_addr_len, + struct netlink_ext_ack *extack); + /** + * @port_function_hw_addr_set: Port function's hardware address set function. + * + * Should be used by device drivers to set the hardware address of a function managed + * by the devlink port. Driver should return -EOPNOTSUPP if it doesn't support port + * function handling for a particular port. + */ + int (*port_function_hw_addr_set)(struct devlink *devlink, struct devlink_port *port, + const u8 *hw_addr, int hw_addr_len, + struct netlink_ext_ack *extack); }; static inline void *devlink_priv(struct devlink *devlink) @@ -1262,6 +1284,8 @@ int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn); int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name); +int devlink_info_board_serial_number_put(struct devlink_info_req *req, + const char *bsn); int devlink_info_version_fixed_put(struct devlink_info_req *req, const char *version_name, const char *version_value); diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 6315324b9dc2..3bafb5124ac0 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -232,8 +232,10 @@ struct flow_action_entry { bool truncate; } sample; struct { /* FLOW_ACTION_POLICE */ + u32 index; s64 burst; u64 rate_bytes_ps; + u32 mtu; } police; struct { /* FLOW_ACTION_CT */ int action; @@ -389,17 +391,20 @@ static inline bool flow_rule_match_key(const struct flow_rule *rule, struct flow_stats { u64 pkts; u64 bytes; + u64 drops; u64 lastused; enum flow_action_hw_stats used_hw_stats; bool used_hw_stats_valid; }; static inline void flow_stats_update(struct flow_stats *flow_stats, - u64 bytes, u64 pkts, u64 lastused, + u64 bytes, u64 pkts, + u64 drops, u64 lastused, enum flow_action_hw_stats used_hw_stats) { flow_stats->pkts += pkts; flow_stats->bytes += bytes; + flow_stats->drops += drops; flow_stats->lastused = max_t(u64, flow_stats->lastused, lastused); /* The driver should pass value with a maximum of one bit set. diff --git a/include/net/ip.h b/include/net/ip.h index 04ebe7bf54c6..862c9545833a 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -231,11 +231,7 @@ struct sk_buff *ip_make_skb(struct sock *sk, struct flowi4 *fl4, struct ipcm_cookie *ipc, struct rtable **rtp, struct inet_cork *cork, unsigned int flags); -static inline int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, - struct flowi *fl) -{ - return __ip_queue_xmit(sk, skb, fl, inet_sk(sk)->tos); -} +int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl); static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4) { diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h index 27ec612cd4a4..b3f4eaa88672 100644 --- a/include/net/ip6_checksum.h +++ b/include/net/ip6_checksum.h @@ -85,15 +85,6 @@ static inline void tcp_v6_gso_csum_prep(struct sk_buff *skb) th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0); } -#if IS_ENABLED(CONFIG_IPV6) -static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb) -{ - struct ipv6_pinfo *np = inet6_sk(sk); - - __tcp_v6_send_check(skb, &np->saddr, &sk->sk_v6_daddr); -} -#endif - static inline __sum16 udp_v6_check(int len, const struct in6_addr *saddr, const struct in6_addr *daddr, diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 3f615a29766e..cc8356fd927f 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -19,6 +19,7 @@ #include <net/netlink.h> #include <net/inetpeer.h> #include <net/fib_notifier.h> +#include <linux/indirect_call_wrapper.h> #ifdef CONFIG_IPV6_MULTIPLE_TABLES #define FIB6_TABLE_HASHSZ 256 @@ -552,6 +553,41 @@ struct bpf_iter__ipv6_route { }; #endif +INDIRECT_CALLABLE_DECLARE(struct rt6_info *ip6_pol_route_output(struct net *net, + struct fib6_table *table, + struct flowi6 *fl6, + const struct sk_buff *skb, + int flags)); +INDIRECT_CALLABLE_DECLARE(struct rt6_info *ip6_pol_route_input(struct net *net, + struct fib6_table *table, + struct flowi6 *fl6, + const struct sk_buff *skb, + int flags)); +INDIRECT_CALLABLE_DECLARE(struct rt6_info *__ip6_route_redirect(struct net *net, + struct fib6_table *table, + struct flowi6 *fl6, + const struct sk_buff *skb, + int flags)); +INDIRECT_CALLABLE_DECLARE(struct rt6_info *ip6_pol_route_lookup(struct net *net, + struct fib6_table *table, + struct flowi6 *fl6, + const struct sk_buff *skb, + int flags)); +static inline struct rt6_info *pol_lookup_func(pol_lookup_t lookup, + struct net *net, + struct fib6_table *table, + struct flowi6 *fl6, + const struct sk_buff *skb, + int flags) +{ + return INDIRECT_CALL_4(lookup, + ip6_pol_route_output, + ip6_pol_route_input, + ip6_pol_route_lookup, + __ip6_route_redirect, + net, table, fl6, skb, flags); +} + #ifdef CONFIG_IPV6_MULTIPLE_TABLES static inline bool fib6_has_custom_rules(const struct net *net) { diff --git a/include/net/l3mdev.h b/include/net/l3mdev.h index e942372b077b..031c661aa14d 100644 --- a/include/net/l3mdev.h +++ b/include/net/l3mdev.h @@ -10,6 +10,16 @@ #include <net/dst.h> #include <net/fib_rules.h> +enum l3mdev_type { + L3MDEV_TYPE_UNSPEC, + L3MDEV_TYPE_VRF, + __L3MDEV_TYPE_MAX +}; + +#define L3MDEV_TYPE_MAX (__L3MDEV_TYPE_MAX - 1) + +typedef int (*lookup_by_table_id_t)(struct net *net, u32 table_d); + /** * struct l3mdev_ops - l3mdev operations * @@ -37,6 +47,15 @@ struct l3mdev_ops { #ifdef CONFIG_NET_L3_MASTER_DEV +int l3mdev_table_lookup_register(enum l3mdev_type l3type, + lookup_by_table_id_t fn); + +void l3mdev_table_lookup_unregister(enum l3mdev_type l3type, + lookup_by_table_id_t fn); + +int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type, struct net *net, + u32 table_id); + int l3mdev_fib_rule_match(struct net *net, struct flowi *fl, struct fib_lookup_arg *arg); @@ -281,6 +300,26 @@ struct sk_buff *l3mdev_ip6_out(struct sock *sk, struct sk_buff *skb) } static inline +int l3mdev_table_lookup_register(enum l3mdev_type l3type, + lookup_by_table_id_t fn) +{ + return -EOPNOTSUPP; +} + +static inline +void l3mdev_table_lookup_unregister(enum l3mdev_type l3type, + lookup_by_table_id_t fn) +{ +} + +static inline +int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type, struct net *net, + u32 table_id) +{ + return -ENODEV; +} + +static inline int l3mdev_fib_rule_match(struct net *net, struct flowi *fl, struct fib_lookup_arg *arg) { diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index ed65619cbc47..ff017e5b3ea2 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -262,7 +262,7 @@ static inline void tcf_exts_put_net(struct tcf_exts *exts) static inline void tcf_exts_stats_update(const struct tcf_exts *exts, - u64 bytes, u64 packets, u64 lastuse, + u64 bytes, u64 packets, u64 drops, u64 lastuse, u8 used_hw_stats, bool used_hw_stats_valid) { #ifdef CONFIG_NET_CLS_ACT @@ -273,7 +273,8 @@ tcf_exts_stats_update(const struct tcf_exts *exts, for (i = 0; i < exts->nr_actions; i++) { struct tc_action *a = exts->actions[i]; - tcf_action_stats_update(a, bytes, packets, lastuse, true); + tcf_action_stats_update(a, bytes, packets, drops, + lastuse, true); a->used_hw_stats = used_hw_stats; a->used_hw_stats_valid = used_hw_stats_valid; } diff --git a/include/net/rpl.h b/include/net/rpl.h index dceff60e8baf..308ef0a05cae 100644 --- a/include/net/rpl.h +++ b/include/net/rpl.h @@ -26,12 +26,6 @@ static inline void rpl_exit(void) {} /* Worst decompression memory usage ipv6 address (16) + pad 7 */ #define IPV6_RPL_SRH_WORST_SWAP_SIZE (sizeof(struct in6_addr) + 7) -static inline size_t ipv6_rpl_srh_alloc_size(unsigned char n) -{ - return sizeof(struct ipv6_rpl_sr_hdr) + - ((n + 1) * sizeof(struct in6_addr)); -} - size_t ipv6_rpl_srh_size(unsigned char n, unsigned char cmpri, unsigned char cmpre); diff --git a/include/net/tc_act/tc_police.h b/include/net/tc_act/tc_police.h index f098ad4424be..cd973b10ae8c 100644 --- a/include/net/tc_act/tc_police.h +++ b/include/net/tc_act/tc_police.h @@ -69,4 +69,14 @@ static inline s64 tcf_police_tcfp_burst(const struct tc_action *act) return params->tcfp_burst; } +static inline u32 tcf_police_tcfp_mtu(const struct tc_action *act) +{ + struct tcf_police *police = to_police(act); + struct tcf_police_params *params; + + params = rcu_dereference_protected(police->params, + lockdep_is_held(&police->tcf_lock)); + return params->tcfp_mtu; +} + #endif /* __NET_TC_POLICE_H */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 4de9485f73d9..27f848ab3995 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -25,6 +25,7 @@ #include <linux/skbuff.h> #include <linux/kref.h> #include <linux/ktime.h> +#include <linux/indirect_call_wrapper.h> #include <net/inet_connection_sock.h> #include <net/inet_timewait_sock.h> @@ -906,6 +907,8 @@ static inline void tcp_skb_bpf_redirect_clear(struct sk_buff *skb) TCP_SKB_CB(skb)->bpf.sk_redir = NULL; } +extern const struct inet_connection_sock_af_ops ipv4_specific; + #if IS_ENABLED(CONFIG_IPV6) /* This is the variant of inet6_iif() that must be used by TCP, * as TCP moves IP6CB into a different location in skb->cb[] @@ -931,6 +934,13 @@ static inline int tcp_v6_sdif(const struct sk_buff *skb) #endif return 0; } + +extern const struct inet_connection_sock_af_ops ipv6_specific; + +INDIRECT_CALLABLE_DECLARE(void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)); +INDIRECT_CALLABLE_DECLARE(int tcp_v6_rcv(struct sk_buff *skb)); +INDIRECT_CALLABLE_DECLARE(void tcp_v6_early_demux(struct sk_buff *skb)); + #endif static inline bool inet_exact_dif_match(struct net *net, struct sk_buff *skb) @@ -1947,6 +1957,10 @@ void tcp_v4_destroy_sock(struct sock *sk); struct sk_buff *tcp_gso_segment(struct sk_buff *skb, netdev_features_t features); struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb); +INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff)); +INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)); +INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff)); +INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)); int tcp_gro_complete(struct sk_buff *skb); void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr); diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index a8f6020f1196..da06613c9603 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -56,9 +56,6 @@ ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp, __u16 srcp, #define LOOPBACK4_IPV6 cpu_to_be32(0x7f000006) -/* address family specific functions */ -extern const struct inet_connection_sock_af_ops ipv4_specific; - void inet6_destroy_sock(struct sock *sk); #define IPV6_SEQ_DGRAM_HEADER \ diff --git a/include/net/tso.h b/include/net/tso.h index 7e166a570349..62c98a9c60f1 100644 --- a/include/net/tso.h +++ b/include/net/tso.h @@ -4,21 +4,22 @@ #include <net/ip.h> -#define TSO_HEADER_SIZE 128 +#define TSO_HEADER_SIZE 256 struct tso_t { - int next_frag_idx; - void *data; - size_t size; - u16 ip_id; - bool ipv6; - u32 tcp_seq; + int next_frag_idx; + int size; + void *data; + u16 ip_id; + u8 tlen; /* transport header len */ + bool ipv6; + u32 tcp_seq; }; -int tso_count_descs(struct sk_buff *skb); -void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, +int tso_count_descs(const struct sk_buff *skb); +void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso, int size, bool is_last); -void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size); -void tso_start(struct sk_buff *skb, struct tso_t *tso); +void tso_build_data(const struct sk_buff *skb, struct tso_t *tso, int size); +int tso_start(struct sk_buff *skb, struct tso_t *tso); #endif /* _TSO_H */ diff --git a/include/net/udp.h b/include/net/udp.h index a8fa6c0c6ded..5a2d677432f0 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -27,6 +27,7 @@ #include <linux/ipv6.h> #include <linux/seq_file.h> #include <linux/poll.h> +#include <linux/indirect_call_wrapper.h> /** * struct udp_skb_cb - UDP(-Lite) private variables @@ -166,6 +167,12 @@ static inline void udp_csum_pull_header(struct sk_buff *skb) typedef struct sock *(*udp_lookup_t)(struct sk_buff *skb, __be16 sport, __be16 dport); +INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp4_gro_receive(struct list_head *, + struct sk_buff *)); +INDIRECT_CALLABLE_DECLARE(int udp4_gro_complete(struct sk_buff *, int)); +INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp6_gro_receive(struct list_head *, + struct sk_buff *)); +INDIRECT_CALLABLE_DECLARE(int udp6_gro_complete(struct sk_buff *, int)); struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, struct udphdr *uh, struct sock *sk); int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index c7d213c9f9d8..f9e1fda82ddf 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -127,6 +127,7 @@ struct xfrm_state_walk { struct xfrm_state_offload { struct net_device *dev; + struct net_device *real_dev; unsigned long offload_handle; unsigned int num_exthdrs; u8 flags; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 4953e9994df3..e050f8121ba2 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -65,6 +65,21 @@ #define PGID_MCIPV4 62 #define PGID_MCIPV6 63 +#define for_each_unicast_dest_pgid(ocelot, pgid) \ + for ((pgid) = 0; \ + (pgid) < (ocelot)->num_phys_ports; \ + (pgid)++) + +#define for_each_nonreserved_multicast_dest_pgid(ocelot, pgid) \ + for ((pgid) = (ocelot)->num_phys_ports + 1; \ + (pgid) < PGID_CPU; \ + (pgid)++) + +#define for_each_aggr_pgid(ocelot, pgid) \ + for ((pgid) = PGID_AGGR; \ + (pgid) < PGID_SRC; \ + (pgid)++) + /* Aggregation PGIDs, one per Link Aggregation Code */ #define PGID_AGGR 64 @@ -470,7 +485,7 @@ struct ocelot_ops { int (*reset)(struct ocelot *ocelot); }; -struct ocelot_acl_block { +struct ocelot_vcap_block { struct list_head rules; int count; int pol_lpr; @@ -535,7 +550,7 @@ struct ocelot { struct list_head multicast; - struct ocelot_acl_block acl_block; + struct ocelot_vcap_block block; const struct vcap_field *vcap_is2_keys; const struct vcap_field *vcap_is2_actions; @@ -641,5 +656,9 @@ int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, struct flow_cls_offload *f, bool ingress); int ocelot_cls_flower_stats(struct ocelot *ocelot, int port, struct flow_cls_offload *f, bool ingress); +int ocelot_port_mdb_add(struct ocelot *ocelot, int port, + const struct switchdev_obj_port_mdb *mdb); +int ocelot_port_mdb_del(struct ocelot *ocelot, int port, + const struct switchdev_obj_port_mdb *mdb); #endif diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 08563e6a424d..87c83a82991b 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -451,6 +451,10 @@ enum devlink_attr { DEVLINK_ATTR_TRAP_POLICER_RATE, /* u64 */ DEVLINK_ATTR_TRAP_POLICER_BURST, /* u64 */ + DEVLINK_ATTR_PORT_FUNCTION, /* nested */ + + DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER, /* string */ + /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, @@ -497,4 +501,12 @@ enum devlink_resource_unit { DEVLINK_RESOURCE_UNIT_ENTRY, }; +enum devlink_port_function_attr { + DEVLINK_PORT_FUNCTION_ATTR_UNSPEC, + DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, /* binary */ + + __DEVLINK_PORT_FUNCTION_ATTR_MAX, + DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1 +}; + #endif /* _UAPI_LINUX_DEVLINK_H_ */ diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h index eefcda8ca44e..dc8b72201f6c 100644 --- a/include/uapi/linux/neighbour.h +++ b/include/uapi/linux/neighbour.h @@ -30,6 +30,7 @@ enum { NDA_SRC_VNI, NDA_PROTOCOL, /* Originator of entry */ NDA_NH_ID, + NDA_FDB_EXT_ATTRS, __NDA_MAX }; @@ -172,4 +173,27 @@ enum { }; #define NDTA_MAX (__NDTA_MAX - 1) + /* FDB activity notification bits used in NFEA_ACTIVITY_NOTIFY: + * - FDB_NOTIFY_BIT - notify on activity/expire for any entry + * - FDB_NOTIFY_INACTIVE_BIT - mark as inactive to avoid multiple notifications + */ +enum { + FDB_NOTIFY_BIT = (1 << 0), + FDB_NOTIFY_INACTIVE_BIT = (1 << 1) +}; + +/* embedded into NDA_FDB_EXT_ATTRS: + * [NDA_FDB_EXT_ATTRS] = { + * [NFEA_ACTIVITY_NOTIFY] + * ... + * } + */ +enum { + NFEA_UNSPEC, + NFEA_ACTIVITY_NOTIFY, + NFEA_DONT_REFRESH, + __NFEA_MAX +}; +#define NFEA_MAX (__NFEA_MAX - 1) + #endif diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 073e71ef6bdd..879e64950a0a 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -257,12 +257,12 @@ enum { /* rtm_protocol */ -#define RTPROT_UNSPEC 0 -#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; - not used by current IPv4 */ -#define RTPROT_KERNEL 2 /* Route installed by kernel */ -#define RTPROT_BOOT 3 /* Route installed during boot */ -#define RTPROT_STATIC 4 /* Route installed by administrator */ +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ /* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; they are just passed from user and back as is. @@ -271,22 +271,23 @@ enum { avoid conflicts. */ -#define RTPROT_GATED 8 /* Apparently, GateD */ -#define RTPROT_RA 9 /* RDISC/ND router advertisements */ -#define RTPROT_MRT 10 /* Merit MRT */ -#define RTPROT_ZEBRA 11 /* Zebra */ -#define RTPROT_BIRD 12 /* BIRD */ -#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ -#define RTPROT_XORP 14 /* XORP */ -#define RTPROT_NTK 15 /* Netsukuku */ -#define RTPROT_DHCP 16 /* DHCP client */ -#define RTPROT_MROUTED 17 /* Multicast daemon */ -#define RTPROT_BABEL 42 /* Babel daemon */ -#define RTPROT_BGP 186 /* BGP Routes */ -#define RTPROT_ISIS 187 /* ISIS Routes */ -#define RTPROT_OSPF 188 /* OSPF Routes */ -#define RTPROT_RIP 189 /* RIP Routes */ -#define RTPROT_EIGRP 192 /* EIGRP Routes */ +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ +#define RTPROT_DHCP 16 /* DHCP client */ +#define RTPROT_MROUTED 17 /* Multicast daemon */ +#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */ +#define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_BGP 186 /* BGP Routes */ +#define RTPROT_ISIS 187 /* ISIS Routes */ +#define RTPROT_OSPF 188 /* OSPF Routes */ +#define RTPROT_RIP 189 /* RIP Routes */ +#define RTPROT_EIGRP 192 /* EIGRP Routes */ /* rtm_scope diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 4877a0db16c6..9db504baa094 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -349,12 +349,21 @@ void br_fdb_cleanup(struct work_struct *work) */ rcu_read_lock(); hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { - unsigned long this_timer; + unsigned long this_timer = f->updated + delay; if (test_bit(BR_FDB_STATIC, &f->flags) || - test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags)) + test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags)) { + if (test_bit(BR_FDB_NOTIFY, &f->flags)) { + if (time_after(this_timer, now)) + work_delay = min(work_delay, + this_timer - now); + else if (!test_and_set_bit(BR_FDB_NOTIFY_INACTIVE, + &f->flags)) + fdb_notify(br, f, RTM_NEWNEIGH, false); + } continue; - this_timer = f->updated + delay; + } + if (time_after(this_timer, now)) { work_delay = min(work_delay, this_timer - now); } else { @@ -556,11 +565,17 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, return ret; } +/* returns true if the fdb was modified */ +static bool __fdb_mark_active(struct net_bridge_fdb_entry *fdb) +{ + return !!(test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags) && + test_and_clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)); +} + void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr, u16 vid, unsigned long flags) { struct net_bridge_fdb_entry *fdb; - bool fdb_modified = false; /* some users want to always flood. */ if (hold_time(br) == 0) @@ -575,6 +590,12 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, source->dev->name, addr, vid); } else { unsigned long now = jiffies; + bool fdb_modified = false; + + if (now != fdb->updated) { + fdb->updated = now; + fdb_modified = __fdb_mark_active(fdb); + } /* fastpath: update of existing entry */ if (unlikely(source != fdb->dst && @@ -587,8 +608,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, clear_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags); } - if (now != fdb->updated) - fdb->updated = now; + if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); if (unlikely(fdb_modified)) { @@ -667,6 +687,23 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, &fdb->key.vlan_id)) goto nla_put_failure; + if (test_bit(BR_FDB_NOTIFY, &fdb->flags)) { + struct nlattr *nest = nla_nest_start(skb, NDA_FDB_EXT_ATTRS); + u8 notify_bits = FDB_NOTIFY_BIT; + + if (!nest) + goto nla_put_failure; + if (test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)) + notify_bits |= FDB_NOTIFY_INACTIVE_BIT; + + if (nla_put_u8(skb, NFEA_ACTIVITY_NOTIFY, notify_bits)) { + nla_nest_cancel(skb, nest); + goto nla_put_failure; + } + + nla_nest_end(skb, nest); + } + nlmsg_end(skb, nlh); return 0; @@ -681,7 +718,9 @@ static inline size_t fdb_nlmsg_size(void) + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + nla_total_size(sizeof(u32)) /* NDA_MASTER */ + nla_total_size(sizeof(u16)) /* NDA_VLAN */ - + nla_total_size(sizeof(struct nda_cacheinfo)); + + nla_total_size(sizeof(struct nda_cacheinfo)) + + nla_total_size(0) /* NDA_FDB_EXT_ATTRS */ + + nla_total_size(sizeof(u8)); /* NFEA_ACTIVITY_NOTIFY */ } static void fdb_notify(struct net_bridge *br, @@ -791,14 +830,41 @@ errout: return err; } +/* returns true if the fdb is modified */ +static bool fdb_handle_notify(struct net_bridge_fdb_entry *fdb, u8 notify) +{ + bool modified = false; + + /* allow to mark an entry as inactive, usually done on creation */ + if ((notify & FDB_NOTIFY_INACTIVE_BIT) && + !test_and_set_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)) + modified = true; + + if ((notify & FDB_NOTIFY_BIT) && + !test_and_set_bit(BR_FDB_NOTIFY, &fdb->flags)) { + /* enabled activity tracking */ + modified = true; + } else if (!(notify & FDB_NOTIFY_BIT) && + test_and_clear_bit(BR_FDB_NOTIFY, &fdb->flags)) { + /* disabled activity tracking, clear notify state */ + clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags); + modified = true; + } + + return modified; +} + /* Update (create or replace) forwarding database entry */ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, - const u8 *addr, u16 state, u16 flags, u16 vid, - u8 ndm_flags) + const u8 *addr, struct ndmsg *ndm, u16 flags, u16 vid, + struct nlattr *nfea_tb[]) { - bool is_sticky = !!(ndm_flags & NTF_STICKY); + bool is_sticky = !!(ndm->ndm_flags & NTF_STICKY); + bool refresh = !nfea_tb[NFEA_DONT_REFRESH]; struct net_bridge_fdb_entry *fdb; + u16 state = ndm->ndm_state; bool modified = false; + u8 notify = 0; /* If the port cannot learn allow only local and static entries */ if (source && !(state & NUD_PERMANENT) && !(state & NUD_NOARP) && @@ -815,6 +881,13 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, if (is_sticky && (state & NUD_PERMANENT)) return -EINVAL; + if (nfea_tb[NFEA_ACTIVITY_NOTIFY]) { + notify = nla_get_u8(nfea_tb[NFEA_ACTIVITY_NOTIFY]); + if ((notify & ~BR_FDB_NOTIFY_SETTABLE_BITS) || + (notify & BR_FDB_NOTIFY_SETTABLE_BITS) == FDB_NOTIFY_INACTIVE_BIT) + return -EINVAL; + } + fdb = br_fdb_find(br, addr, vid); if (fdb == NULL) { if (!(flags & NLM_F_CREATE)) @@ -858,11 +931,15 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, modified = true; } + if (fdb_handle_notify(fdb, notify)) + modified = true; + set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); fdb->used = jiffies; if (modified) { - fdb->updated = jiffies; + if (refresh) + fdb->updated = jiffies; fdb_notify(br, fdb, RTM_NEWNEIGH, true); } @@ -871,7 +948,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, - u16 nlh_flags, u16 vid) + u16 nlh_flags, u16 vid, struct nlattr *nfea_tb[]) { int err = 0; @@ -893,20 +970,25 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, err = br_fdb_external_learn_add(br, p, addr, vid, true); } else { spin_lock_bh(&br->hash_lock); - err = fdb_add_entry(br, p, addr, ndm->ndm_state, - nlh_flags, vid, ndm->ndm_flags); + err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb); spin_unlock_bh(&br->hash_lock); } return err; } +static const struct nla_policy br_nda_fdb_pol[NFEA_MAX + 1] = { + [NFEA_ACTIVITY_NOTIFY] = { .type = NLA_U8 }, + [NFEA_DONT_REFRESH] = { .type = NLA_FLAG }, +}; + /* Add new permanent fdb entry with RTM_NEWNEIGH */ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 vid, u16 nlh_flags, struct netlink_ext_ack *extack) { + struct nlattr *nfea_tb[NFEA_MAX + 1], *attr; struct net_bridge_vlan_group *vg; struct net_bridge_port *p = NULL; struct net_bridge_vlan *v; @@ -939,6 +1021,16 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], vg = nbp_vlan_group(p); } + if (tb[NDA_FDB_EXT_ATTRS]) { + attr = tb[NDA_FDB_EXT_ATTRS]; + err = nla_parse_nested(nfea_tb, NFEA_MAX, attr, + br_nda_fdb_pol, extack); + if (err) + return err; + } else { + memset(nfea_tb, 0, sizeof(struct nlattr *) * (NFEA_MAX + 1)); + } + if (vid) { v = br_vlan_find(vg, vid); if (!v || !br_vlan_should_use(v)) { @@ -947,9 +1039,9 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], } /* VID was specified, so use it. */ - err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid); + err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid, nfea_tb); } else { - err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0); + err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0, nfea_tb); if (err || !vg || !vg->num_vlans) goto out; @@ -960,7 +1052,8 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], list_for_each_entry(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue; - err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid); + err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid, + nfea_tb); if (err) goto out; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 2130fe0194e6..6a7d8e218ae7 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -48,6 +48,8 @@ enum { /* Path to usermode spanning tree program */ #define BR_STP_PROG "/sbin/bridge-stp" +#define BR_FDB_NOTIFY_SETTABLE_BITS (FDB_NOTIFY_BIT | FDB_NOTIFY_INACTIVE_BIT) + typedef struct bridge_id bridge_id; typedef struct mac_addr mac_addr; typedef __u16 port_id; @@ -184,6 +186,8 @@ enum { BR_FDB_ADDED_BY_USER, BR_FDB_ADDED_BY_EXT_LEARN, BR_FDB_OFFLOADED, + BR_FDB_NOTIFY, + BR_FDB_NOTIFY_INACTIVE }; struct net_bridge_fdb_key { diff --git a/net/core/dev.c b/net/core/dev.c index 90b59fc50dc9..3a46b86cbd67 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -143,6 +143,7 @@ #include <linux/net_namespace.h> #include <linux/indirect_call_wrapper.h> #include <net/devlink.h> +#include <linux/pm_runtime.h> #include "net-sysfs.h" @@ -1492,8 +1493,13 @@ static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack) ASSERT_RTNL(); - if (!netif_device_present(dev)) - return -ENODEV; + if (!netif_device_present(dev)) { + /* may be detached because parent is runtime-suspended */ + if (dev->dev.parent) + pm_runtime_resume(dev->dev.parent); + if (!netif_device_present(dev)) + return -ENODEV; + } /* Block netpoll from trying to do any rx path servicing. * If we don't do this there is a chance ndo_poll_controller @@ -6685,7 +6691,9 @@ static int napi_poll(struct napi_struct *n, struct list_head *repoll) trace_napi_poll(n, work, weight); } - WARN_ON_ONCE(work > weight); + if (unlikely(work > weight)) + pr_err_once("NAPI poll function %pS returned %d, exceeding its budget of %d.\n", + n->poll, work, weight); if (likely(work < weight)) goto out_unlock; diff --git a/net/core/devlink.c b/net/core/devlink.c index 2cafbc808b09..6ae36808c152 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -85,6 +85,10 @@ EXPORT_SYMBOL(devlink_dpipe_header_ipv6); EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg); EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr); +static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = { + [DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY }, +}; + static LIST_HEAD(devlink_list); /* devlink_mutex @@ -563,10 +567,54 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg, return 0; } +static int +devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port, + struct netlink_ext_ack *extack) +{ + struct devlink *devlink = port->devlink; + const struct devlink_ops *ops; + struct nlattr *function_attr; + bool empty_nest = true; + int err = 0; + + function_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PORT_FUNCTION); + if (!function_attr) + return -EMSGSIZE; + + ops = devlink->ops; + if (ops->port_function_hw_addr_get) { + int hw_addr_len; + u8 hw_addr[MAX_ADDR_LEN]; + + err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack); + if (err == -EOPNOTSUPP) { + /* Port function attributes are optional for a port. If port doesn't + * support function attribute, returning -EOPNOTSUPP is not an error. + */ + err = 0; + goto out; + } else if (err) { + goto out; + } + err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr); + if (err) + goto out; + empty_nest = false; + } + +out: + if (err || empty_nest) + nla_nest_cancel(msg, function_attr); + else + nla_nest_end(msg, function_attr); + return err; +} + static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink, struct devlink_port *devlink_port, enum devlink_command cmd, u32 portid, - u32 seq, int flags) + u32 seq, int flags, + struct netlink_ext_ack *extack) { void *hdr; @@ -607,6 +655,8 @@ static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink, spin_unlock_bh(&devlink_port->type_lock); if (devlink_nl_port_attrs_put(msg, devlink_port)) goto nla_put_failure; + if (devlink_nl_port_function_attrs_put(msg, devlink_port, extack)) + goto nla_put_failure; genlmsg_end(msg, hdr); return 0; @@ -634,7 +684,8 @@ static void devlink_port_notify(struct devlink_port *devlink_port, if (!msg) return; - err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0); + err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0, + NULL); if (err) { nlmsg_free(msg); return; @@ -708,7 +759,8 @@ static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb, err = devlink_nl_port_fill(msg, devlink, devlink_port, DEVLINK_CMD_PORT_NEW, - info->snd_portid, info->snd_seq, 0); + info->snd_portid, info->snd_seq, 0, + info->extack); if (err) { nlmsg_free(msg); return err; @@ -740,7 +792,8 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, DEVLINK_CMD_NEW, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, - NLM_F_MULTI); + NLM_F_MULTI, + cb->extack); if (err) { mutex_unlock(&devlink->lock); goto out; @@ -778,6 +831,67 @@ static int devlink_port_type_set(struct devlink *devlink, return -EOPNOTSUPP; } +static int +devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *port, + const struct nlattr *attr, struct netlink_ext_ack *extack) +{ + const struct devlink_ops *ops; + const u8 *hw_addr; + int hw_addr_len; + int err; + + hw_addr = nla_data(attr); + hw_addr_len = nla_len(attr); + if (hw_addr_len > MAX_ADDR_LEN) { + NL_SET_ERR_MSG_MOD(extack, "Port function hardware address too long"); + return -EINVAL; + } + if (port->type == DEVLINK_PORT_TYPE_ETH) { + if (hw_addr_len != ETH_ALEN) { + NL_SET_ERR_MSG_MOD(extack, "Address must be 6 bytes for Ethernet device"); + return -EINVAL; + } + if (!is_unicast_ether_addr(hw_addr)) { + NL_SET_ERR_MSG_MOD(extack, "Non-unicast hardware address unsupported"); + return -EINVAL; + } + } + + ops = devlink->ops; + if (!ops->port_function_hw_addr_set) { + NL_SET_ERR_MSG_MOD(extack, "Port doesn't support function attributes"); + return -EOPNOTSUPP; + } + + err = ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack); + if (err) + return err; + + devlink_port_notify(port, DEVLINK_CMD_PORT_NEW); + return 0; +} + +static int +devlink_port_function_set(struct devlink *devlink, struct devlink_port *port, + const struct nlattr *attr, struct netlink_ext_ack *extack) +{ + struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1]; + int err; + + err = nla_parse_nested(tb, DEVLINK_PORT_FUNCTION_ATTR_MAX, attr, + devlink_function_nl_policy, extack); + if (err < 0) { + NL_SET_ERR_MSG_MOD(extack, "Fail to parse port function attributes"); + return err; + } + + attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]; + if (attr) + err = devlink_port_function_hw_addr_set(devlink, port, attr, extack); + + return err; +} + static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb, struct genl_info *info) { @@ -793,6 +907,16 @@ static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb, if (err) return err; } + + if (info->attrs[DEVLINK_ATTR_PORT_FUNCTION]) { + struct nlattr *attr = info->attrs[DEVLINK_ATTR_PORT_FUNCTION]; + struct netlink_ext_ack *extack = info->extack; + + err = devlink_port_function_set(devlink, devlink_port, attr, extack); + if (err) + return err; + } + return 0; } @@ -4378,6 +4502,14 @@ int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn) } EXPORT_SYMBOL_GPL(devlink_info_serial_number_put); +int devlink_info_board_serial_number_put(struct devlink_info_req *req, + const char *bsn) +{ + return nla_put_string(req->msg, DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER, + bsn); +} +EXPORT_SYMBOL_GPL(devlink_info_board_serial_number_put); + static int devlink_info_version_put(struct devlink_info_req *req, int attr, const char *version_name, const char *version_value) @@ -6709,6 +6841,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_TRAP_POLICER_ID] = { .type = NLA_U32 }, [DEVLINK_ATTR_TRAP_POLICER_RATE] = { .type = NLA_U64 }, [DEVLINK_ATTR_TRAP_POLICER_BURST] = { .type = NLA_U64 }, + [DEVLINK_ATTR_PORT_FUNCTION] = { .type = NLA_NESTED }, }; static const struct genl_ops devlink_nl_ops[] = { diff --git a/net/core/neighbour.c b/net/core/neighbour.c index ef6b5a8f629c..8e39e28b0a8d 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1783,6 +1783,7 @@ const struct nla_policy nda_policy[NDA_MAX+1] = { [NDA_MASTER] = { .type = NLA_U32 }, [NDA_PROTOCOL] = { .type = NLA_U8 }, [NDA_NH_ID] = { .type = NLA_U32 }, + [NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED }, }; static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, diff --git a/net/core/tso.c b/net/core/tso.c index d4d5c077ad72..4148f6d48953 100644 --- a/net/core/tso.c +++ b/net/core/tso.c @@ -6,18 +6,17 @@ #include <asm/unaligned.h> /* Calculate expected number of TX descriptors */ -int tso_count_descs(struct sk_buff *skb) +int tso_count_descs(const struct sk_buff *skb) { /* The Marvell Way */ return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags; } EXPORT_SYMBOL(tso_count_descs); -void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, +void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso, int size, bool is_last) { - struct tcphdr *tcph; - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int hdr_len = skb_transport_offset(skb) + tso->tlen; int mac_hdr_len = skb_network_offset(skb); memcpy(hdr, skb->data, hdr_len); @@ -30,23 +29,31 @@ void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, } else { struct ipv6hdr *iph = (void *)(hdr + mac_hdr_len); - iph->payload_len = htons(size + tcp_hdrlen(skb)); + iph->payload_len = htons(size + tso->tlen); } - tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb)); - put_unaligned_be32(tso->tcp_seq, &tcph->seq); + hdr += skb_transport_offset(skb); + if (tso->tlen != sizeof(struct udphdr)) { + struct tcphdr *tcph = (struct tcphdr *)hdr; - if (!is_last) { - /* Clear all special flags for not last packet */ - tcph->psh = 0; - tcph->fin = 0; - tcph->rst = 0; + put_unaligned_be32(tso->tcp_seq, &tcph->seq); + + if (!is_last) { + /* Clear all special flags for not last packet */ + tcph->psh = 0; + tcph->fin = 0; + tcph->rst = 0; + } + } else { + struct udphdr *uh = (struct udphdr *)hdr; + + uh->len = htons(sizeof(*uh) + size); } } EXPORT_SYMBOL(tso_build_hdr); -void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size) +void tso_build_data(const struct sk_buff *skb, struct tso_t *tso, int size) { - tso->tcp_seq += size; + tso->tcp_seq += size; /* not worth avoiding this operation for UDP */ tso->size -= size; tso->data += size; @@ -62,12 +69,14 @@ void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size) } EXPORT_SYMBOL(tso_build_data); -void tso_start(struct sk_buff *skb, struct tso_t *tso) +int tso_start(struct sk_buff *skb, struct tso_t *tso) { - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int tlen = skb_is_gso_tcp(skb) ? tcp_hdrlen(skb) : sizeof(struct udphdr); + int hdr_len = skb_transport_offset(skb) + tlen; + tso->tlen = tlen; tso->ip_id = ntohs(ip_hdr(skb)->id); - tso->tcp_seq = ntohl(tcp_hdr(skb)->seq); + tso->tcp_seq = (tlen != sizeof(struct udphdr)) ? ntohl(tcp_hdr(skb)->seq) : 0; tso->next_frag_idx = 0; tso->ipv6 = vlan_get_protocol(skb) == htons(ETH_P_IPV6); @@ -83,5 +92,6 @@ void tso_start(struct sk_buff *skb, struct tso_t *tso) tso->data = skb_frag_address(frag); tso->next_frag_idx++; } + return hdr_len; } EXPORT_SYMBOL(tso_start); diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index d2a4553bcf39..84dde5a2066e 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1736,7 +1736,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, struct net_device *netdev; struct dcbmsg *dcb = nlmsg_data(nlh); struct nlattr *tb[DCB_ATTR_MAX + 1]; - u32 portid = skb ? NETLINK_CB(skb).portid : 0; + u32 portid = NETLINK_CB(skb).portid; int ret = -EINVAL; struct sk_buff *reply_skb; struct nlmsghdr *reply_nlh = NULL; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 06b9983325cc..9eb7e4b62d9b 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -670,7 +670,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type if (decnet_debug_level & 1) printk(KERN_DEBUG "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n", - (int)flags, (dev) ? dev->name : "???", len, skb->len, + (int)flags, dev->name, len, skb->len, padlen); if (flags & DN_RT_PKT_CNTL) { diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 21d5fc0f6bb3..83f22196d64c 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1918,7 +1918,7 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) if (copy_to_user(useraddr, &stats, sizeof(stats))) goto out; useraddr += sizeof(stats); - if (n_stats && copy_to_user(useraddr, data, n_stats * sizeof(u64))) + if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64)))) goto out; ret = 0; @@ -1973,7 +1973,7 @@ static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr) if (copy_to_user(useraddr, &stats, sizeof(stats))) goto out; useraddr += sizeof(stats); - if (n_stats && copy_to_user(useraddr, data, n_stats * sizeof(u64))) + if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64)))) goto out; ret = 0; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 02aa5cb3a4fd..ea6ed6d487ed 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1432,10 +1432,6 @@ static struct sk_buff *ipip_gso_segment(struct sk_buff *skb, return inet_gso_segment(skb, features); } -INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *, - struct sk_buff *)); -INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp4_gro_receive(struct list_head *, - struct sk_buff *)); struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb) { const struct net_offload *ops; @@ -1608,8 +1604,6 @@ int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) return -EINVAL; } -INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *, int)); -INDIRECT_CALLABLE_DECLARE(int udp4_gro_complete(struct sk_buff *, int)); int inet_gro_complete(struct sk_buff *skb, int nhoff) { __be16 newlen = htons(skb->len - nhoff); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 090d3097ee15..d946356187ed 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -539,6 +539,12 @@ no_route: } EXPORT_SYMBOL(__ip_queue_xmit); +int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) +{ + return __ip_queue_xmit(sk, skb, fl, inet_sk(sk)->tos); +} +EXPORT_SYMBOL(ip_queue_xmit); + static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from) { to->pkt_type = from->pkt_type; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index a50e1990a845..04b70fe31fa2 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1064,6 +1064,10 @@ static void tcp_update_skb_after_send(struct sock *sk, struct sk_buff *skb, list_move_tail(&skb->tcp_tsorted_anchor, &tp->tsorted_sent_queue); } +INDIRECT_CALLABLE_DECLARE(int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)); +INDIRECT_CALLABLE_DECLARE(int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)); +INDIRECT_CALLABLE_DECLARE(void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb)); + /* This routine actually transmits TCP packets queued in by * tcp_do_sendmsg(). This is used by both the initial * transmission and possible later retransmissions. @@ -1207,7 +1211,9 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, } #endif - icsk->icsk_af_ops->send_check(sk, skb); + INDIRECT_CALL_INET(icsk->icsk_af_ops->send_check, + tcp_v6_send_check, tcp_v4_send_check, + sk, skb); if (likely(tcb->tcp_flags & TCPHDR_ACK)) tcp_event_ack_sent(sk, tcp_skb_pcount(skb), rcv_nxt); @@ -1235,7 +1241,9 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, tcp_add_tx_delay(skb, tp); - err = icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl); + err = INDIRECT_CALL_INET(icsk->icsk_af_ops->queue_xmit, + inet6_csk_xmit, ip_queue_xmit, + sk, skb, &inet->cork.fl); if (unlikely(err > 0)) { tcp_enter_cwr(sk); diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 5a8bbcdcaf2b..e9b366994475 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -580,7 +580,7 @@ looped_back: hdr->segments_left--; i = n - hdr->segments_left; - buf = kzalloc(ipv6_rpl_srh_alloc_size(n + 1) * 2, GFP_ATOMIC); + buf = kcalloc(struct_size(hdr, segments.addr, n + 2), 2, GFP_ATOMIC); if (unlikely(!buf)) { kfree_skb(skb); return -1; diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index fafe556d21e0..6053ef851555 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -111,11 +111,13 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, } else { struct rt6_info *rt; - rt = lookup(net, net->ipv6.fib6_local_tbl, fl6, skb, flags); + rt = pol_lookup_func(lookup, + net, net->ipv6.fib6_local_tbl, fl6, skb, flags); if (rt != net->ipv6.ip6_null_entry && rt->dst.error != -EAGAIN) return &rt->dst; ip6_rt_put_flags(rt, flags); - rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags); + rt = pol_lookup_func(lookup, + net, net->ipv6.fib6_main_tbl, fl6, skb, flags); if (rt->dst.error != -EAGAIN) return &rt->dst; ip6_rt_put_flags(rt, flags); @@ -226,7 +228,8 @@ static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp, goto out; } - rt = lookup(net, table, flp6, arg->lookup_data, flags); + rt = pol_lookup_func(lookup, + net, table, flp6, arg->lookup_data, flags); if (rt != net->ipv6.ip6_null_entry) { err = fib6_rule_saddr(net, rule, flags, flp6, ip6_dst_idev(&rt->dst)->dev); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index fc5000370030..91e0f2fd2523 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -439,8 +439,8 @@ static int icmp6_iif(const struct sk_buff *skb) /* * Send an ICMP message in response to a packet in error */ -static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, - const struct in6_addr *force_saddr) +void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, + const struct in6_addr *force_saddr) { struct inet6_dev *idev = NULL; struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -625,6 +625,7 @@ out: out_bh_enable: local_bh_enable(); } +EXPORT_SYMBOL(icmp6_send); /* Slightly more convenient version of icmp6_send. */ diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 49ee89bbcba0..25a90f3f705c 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -314,7 +314,8 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, { struct rt6_info *rt; - rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags); + rt = pol_lookup_func(lookup, + net, net->ipv6.fib6_main_tbl, fl6, skb, flags); if (rt->dst.error == -EAGAIN) { ip6_rt_put_flags(rt, flags); rt = net->ipv6.ip6_null_entry; diff --git a/net/ipv6/ip6_icmp.c b/net/ipv6/ip6_icmp.c index e0086758b6ee..70c8c2f36c98 100644 --- a/net/ipv6/ip6_icmp.c +++ b/net/ipv6/ip6_icmp.c @@ -9,6 +9,8 @@ #if IS_ENABLED(CONFIG_IPV6) +#if !IS_BUILTIN(CONFIG_IPV6) + static ip6_icmp_send_t __rcu *ip6_icmp_send; int inet6_register_icmp_sender(ip6_icmp_send_t *fn) @@ -37,14 +39,12 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) rcu_read_lock(); send = rcu_dereference(ip6_icmp_send); - - if (!send) - goto out; - send(skb, type, code, info, NULL); -out: + if (send) + send(skb, type, code, info, NULL); rcu_read_unlock(); } EXPORT_SYMBOL(icmpv6_send); +#endif #if IS_ENABLED(CONFIG_NF_NAT) #include <net/netfilter/nf_conntrack.h> diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 7fbb44736a34..a80f90bf3ae7 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -13,6 +13,8 @@ #include <net/protocol.h> #include <net/ipv6.h> #include <net/inet_common.h> +#include <net/tcp.h> +#include <net/udp.h> #include "ip6_offload.h" @@ -177,10 +179,6 @@ static int ipv6_exthdrs_len(struct ipv6hdr *iph, return len; } -INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp6_gro_receive(struct list_head *, - struct sk_buff *)); -INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp6_gro_receive(struct list_head *, - struct sk_buff *)); INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head, struct sk_buff *skb) { @@ -319,8 +317,6 @@ static struct sk_buff *ip4ip6_gro_receive(struct list_head *head, return inet_gro_receive(head, skb); } -INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *, int)); -INDIRECT_CALLABLE_DECLARE(int udp6_gro_complete(struct sk_buff *, int)); INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff) { const struct net_offload *ops; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 82cbb46a2a4f..5852039ca9cf 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1207,7 +1207,7 @@ fallback: return nrt; } -static struct rt6_info *ip6_pol_route_lookup(struct net *net, +INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net, struct fib6_table *table, struct flowi6 *fl6, const struct sk_buff *skb, @@ -2274,7 +2274,7 @@ out: } EXPORT_SYMBOL_GPL(ip6_pol_route); -static struct rt6_info *ip6_pol_route_input(struct net *net, +INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, struct flowi6 *fl6, const struct sk_buff *skb, @@ -2465,7 +2465,7 @@ void ip6_route_input(struct sk_buff *skb) &fl6, skb, flags)); } -static struct rt6_info *ip6_pol_route_output(struct net *net, +INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, struct flowi6 *fl6, const struct sk_buff *skb, @@ -2912,7 +2912,7 @@ struct ip6rd_flowi { struct in6_addr gateway; }; -static struct rt6_info *__ip6_route_redirect(struct net *net, +INDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net, struct fib6_table *table, struct flowi6 *fl6, const struct sk_buff *skb, diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c index c3ececd7cfc1..5fdf3ebb953f 100644 --- a/net/ipv6/rpl_iptunnel.c +++ b/net/ipv6/rpl_iptunnel.c @@ -136,8 +136,7 @@ static int rpl_do_srh_inline(struct sk_buff *skb, const struct rpl_lwt *rlwt, oldhdr = ipv6_hdr(skb); - buf = kzalloc(ipv6_rpl_srh_alloc_size(srh->segments_left - 1) * 2, - GFP_ATOMIC); + buf = kcalloc(struct_size(srh, segments.addr, srh->segments_left), 2, GFP_ATOMIC); if (!buf) return -ENOMEM; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f67d45ff00b4..4502db706f75 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1811,6 +1811,13 @@ static struct timewait_sock_ops tcp6_timewait_sock_ops = { .twsk_destructor = tcp_twsk_destructor, }; +INDIRECT_CALLABLE_SCOPE void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + + __tcp_v6_send_check(skb, &np->saddr, &sk->sk_v6_daddr); +} + const struct inet_connection_sock_af_ops ipv6_specific = { .queue_xmit = inet6_csk_xmit, .send_check = tcp_v6_send_check, diff --git a/net/l3mdev/l3mdev.c b/net/l3mdev/l3mdev.c index f35899d45a9a..e71ca5aec684 100644 --- a/net/l3mdev/l3mdev.c +++ b/net/l3mdev/l3mdev.c @@ -9,6 +9,99 @@ #include <net/fib_rules.h> #include <net/l3mdev.h> +static DEFINE_SPINLOCK(l3mdev_lock); + +struct l3mdev_handler { + lookup_by_table_id_t dev_lookup; +}; + +static struct l3mdev_handler l3mdev_handlers[L3MDEV_TYPE_MAX + 1]; + +static int l3mdev_check_type(enum l3mdev_type l3type) +{ + if (l3type <= L3MDEV_TYPE_UNSPEC || l3type > L3MDEV_TYPE_MAX) + return -EINVAL; + + return 0; +} + +int l3mdev_table_lookup_register(enum l3mdev_type l3type, + lookup_by_table_id_t fn) +{ + struct l3mdev_handler *hdlr; + int res; + + res = l3mdev_check_type(l3type); + if (res) + return res; + + hdlr = &l3mdev_handlers[l3type]; + + spin_lock(&l3mdev_lock); + + if (hdlr->dev_lookup) { + res = -EBUSY; + goto unlock; + } + + hdlr->dev_lookup = fn; + res = 0; + +unlock: + spin_unlock(&l3mdev_lock); + + return res; +} +EXPORT_SYMBOL_GPL(l3mdev_table_lookup_register); + +void l3mdev_table_lookup_unregister(enum l3mdev_type l3type, + lookup_by_table_id_t fn) +{ + struct l3mdev_handler *hdlr; + + if (l3mdev_check_type(l3type)) + return; + + hdlr = &l3mdev_handlers[l3type]; + + spin_lock(&l3mdev_lock); + + if (hdlr->dev_lookup == fn) + hdlr->dev_lookup = NULL; + + spin_unlock(&l3mdev_lock); +} +EXPORT_SYMBOL_GPL(l3mdev_table_lookup_unregister); + +int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type, + struct net *net, u32 table_id) +{ + lookup_by_table_id_t lookup; + struct l3mdev_handler *hdlr; + int ifindex = -EINVAL; + int res; + + res = l3mdev_check_type(l3type); + if (res) + return res; + + hdlr = &l3mdev_handlers[l3type]; + + spin_lock(&l3mdev_lock); + + lookup = hdlr->dev_lookup; + if (!lookup) + goto unlock; + + ifindex = lookup(net, table_id); + +unlock: + spin_unlock(&l3mdev_lock); + + return ifindex; +} +EXPORT_SYMBOL_GPL(l3mdev_ifindex_lookup_by_table_id); + /** * l3mdev_master_ifindex - get index of L3 master device * @dev: targeted interface diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index c6eeaf3e8dcb..482f53aed30a 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -355,11 +355,6 @@ static inline void mptcp_subflow_tcp_fallback(struct sock *sk, inet_csk(sk)->icsk_af_ops = ctx->icsk_af_ops; } -extern const struct inet_connection_sock_af_ops ipv4_specific; -#if IS_ENABLED(CONFIG_MPTCP_IPV6) -extern const struct inet_connection_sock_af_ops ipv6_specific; -#endif - void mptcp_proto_init(void); #if IS_ENABLED(CONFIG_MPTCP_IPV6) int mptcp_proto_v6_init(void); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 8ac7eb0a8309..063d8aaf2900 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1059,14 +1059,13 @@ err: return err; } -void tcf_action_update_stats(struct tc_action *a, u64 bytes, u32 packets, - bool drop, bool hw) +void tcf_action_update_stats(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, bool hw) { if (a->cpu_bstats) { _bstats_cpu_update(this_cpu_ptr(a->cpu_bstats), bytes, packets); - if (drop) - this_cpu_ptr(a->cpu_qstats)->drops += packets; + this_cpu_ptr(a->cpu_qstats)->drops += drops; if (hw) _bstats_cpu_update(this_cpu_ptr(a->cpu_bstats_hw), @@ -1075,8 +1074,7 @@ void tcf_action_update_stats(struct tc_action *a, u64 bytes, u32 packets, } _bstats_update(&a->tcfa_bstats, bytes, packets); - if (drop) - a->tcfa_qstats.drops += packets; + a->tcfa_qstats.drops += drops; if (hw) _bstats_update(&a->tcfa_bstats_hw, bytes, packets); } @@ -1475,7 +1473,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, { struct net *net = sock_net(skb->sk); struct nlattr *tca[TCA_ROOT_MAX + 1]; - u32 portid = skb ? NETLINK_CB(skb).portid : 0; + u32 portid = NETLINK_CB(skb).portid; int ret = 0, ovr = 0; if ((n->nlmsg_type != RTM_GETACTION) && diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index e9f3576cbf71..1b9c6d4a1b6b 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -1450,12 +1450,12 @@ static int tcf_ct_search(struct net *net, struct tc_action **a, u32 index) return tcf_idr_search(tn, a, index); } -static void tcf_stats_update(struct tc_action *a, u64 bytes, u32 packets, - u64 lastuse, bool hw) +static void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, u64 lastuse, bool hw) { struct tcf_ct *c = to_ct(a); - tcf_action_update_stats(a, bytes, packets, false, hw); + tcf_action_update_stats(a, bytes, packets, drops, hw); c->tcf_tm.lastuse = max_t(u64, c->tcf_tm.lastuse, lastuse); } diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 416065772719..410e3bbfb9ca 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -171,14 +171,15 @@ static int tcf_gact_act(struct sk_buff *skb, const struct tc_action *a, return action; } -static void tcf_gact_stats_update(struct tc_action *a, u64 bytes, u32 packets, - u64 lastuse, bool hw) +static void tcf_gact_stats_update(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, u64 lastuse, bool hw) { struct tcf_gact *gact = to_gact(a); int action = READ_ONCE(gact->tcf_action); struct tcf_t *tm = &gact->tcf_tm; - tcf_action_update_stats(a, bytes, packets, action == TC_ACT_SHOT, hw); + tcf_action_update_stats(a, bytes, packets, + action == TC_ACT_SHOT ? packets : drops, hw); tm->lastuse = max_t(u64, tm->lastuse, lastuse); } diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index 323ae7f6315d..1fb8d428d2c1 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -578,13 +578,13 @@ static int tcf_gate_walker(struct net *net, struct sk_buff *skb, return tcf_generic_walker(tn, skb, cb, type, ops, extack); } -static void tcf_gate_stats_update(struct tc_action *a, u64 bytes, u32 packets, - u64 lastuse, bool hw) +static void tcf_gate_stats_update(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, u64 lastuse, bool hw) { struct tcf_gate *gact = to_gate(a); struct tcf_t *tm = &gact->tcf_tm; - tcf_action_update_stats(a, bytes, packets, false, hw); + tcf_action_update_stats(a, bytes, packets, drops, hw); tm->lastuse = max_t(u64, tm->lastuse, lastuse); } diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 83dd82fc9f40..b2705318993b 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -312,13 +312,13 @@ out: return retval; } -static void tcf_stats_update(struct tc_action *a, u64 bytes, u32 packets, - u64 lastuse, bool hw) +static void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, u64 lastuse, bool hw) { struct tcf_mirred *m = to_mirred(a); struct tcf_t *tm = &m->tcf_tm; - tcf_action_update_stats(a, bytes, packets, false, hw); + tcf_action_update_stats(a, bytes, packets, drops, hw); tm->lastuse = max_t(u64, tm->lastuse, lastuse); } diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index d41d6200d9de..66986db062ed 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -409,13 +409,13 @@ done: return p->tcf_action; } -static void tcf_pedit_stats_update(struct tc_action *a, u64 bytes, u32 packets, - u64 lastuse, bool hw) +static void tcf_pedit_stats_update(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, u64 lastuse, bool hw) { struct tcf_pedit *d = to_pedit(a); struct tcf_t *tm = &d->tcf_tm; - tcf_action_update_stats(a, bytes, packets, false, hw); + tcf_action_update_stats(a, bytes, packets, drops, hw); tm->lastuse = max_t(u64, tm->lastuse, lastuse); } diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 8b7a0ac96c51..0b431d493768 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -288,13 +288,13 @@ static void tcf_police_cleanup(struct tc_action *a) } static void tcf_police_stats_update(struct tc_action *a, - u64 bytes, u32 packets, + u64 bytes, u64 packets, u64 drops, u64 lastuse, bool hw) { struct tcf_police *police = to_police(a); struct tcf_t *tm = &police->tcf_tm; - tcf_action_update_stats(a, bytes, packets, false, hw); + tcf_action_update_stats(a, bytes, packets, drops, hw); tm->lastuse = max_t(u64, tm->lastuse, lastuse); } diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index b125b2be4467..361b863e0634 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -74,12 +74,13 @@ err: } static void tcf_skbedit_stats_update(struct tc_action *a, u64 bytes, - u32 packets, u64 lastuse, bool hw) + u64 packets, u64 drops, + u64 lastuse, bool hw) { struct tcf_skbedit *d = to_skbedit(a); struct tcf_t *tm = &d->tcf_tm; - tcf_action_update_stats(a, bytes, packets, false, hw); + tcf_action_update_stats(a, bytes, packets, drops, hw); tm->lastuse = max_t(u64, tm->lastuse, lastuse); } diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index c91d3958fcbb..a5ff9f68ab02 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c @@ -302,13 +302,13 @@ static int tcf_vlan_walker(struct net *net, struct sk_buff *skb, return tcf_generic_walker(tn, skb, cb, type, ops, extack); } -static void tcf_vlan_stats_update(struct tc_action *a, u64 bytes, u32 packets, - u64 lastuse, bool hw) +static void tcf_vlan_stats_update(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, u64 lastuse, bool hw) { struct tcf_vlan *v = to_vlan(a); struct tcf_t *tm = &v->tcf_tm; - tcf_action_update_stats(a, bytes, packets, false, hw); + tcf_action_update_stats(a, bytes, packets, drops, hw); tm->lastuse = max_t(u64, tm->lastuse, lastuse); } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index faa78b7dd962..5bfa6b985bb8 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3663,6 +3663,8 @@ int tc_setup_flow_action(struct flow_action *flow_action, entry->police.burst = tcf_police_tcfp_burst(act); entry->police.rate_bytes_ps = tcf_police_rate_bytes_ps(act); + entry->police.mtu = tcf_police_tcfp_mtu(act); + entry->police.index = act->tcfa_index; } else if (is_tcf_ct(act)) { entry->id = FLOW_ACTION_CT; entry->ct.action = tcf_ct_action(act); diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index b2da37286082..391971672d54 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -491,6 +491,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f, tcf_exts_stats_update(&f->exts, cls_flower.stats.bytes, cls_flower.stats.pkts, + cls_flower.stats.drops, cls_flower.stats.lastused, cls_flower.stats.used_hw_stats, cls_flower.stats.used_hw_stats_valid); diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 8d39dbcf1746..cafb84480bab 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -338,7 +338,8 @@ static void mall_stats_hw_filter(struct tcf_proto *tp, tc_setup_cb_call(block, TC_SETUP_CLSMATCHALL, &cls_mall, false, true); tcf_exts_stats_update(&head->exts, cls_mall.stats.bytes, - cls_mall.stats.pkts, cls_mall.stats.lastused, + cls_mall.stats.pkts, cls_mall.stats.drops, + cls_mall.stats.lastused, cls_mall.stats.used_hw_stats, cls_mall.stats.used_hw_stats_valid); } diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 61e95029c18f..78bec347b8b6 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -533,7 +533,7 @@ tcindex_change(struct net *net, struct sk_buff *in_skb, pr_debug("tcindex_change(tp %p,handle 0x%08x,tca %p,arg %p),opt %p," "p %p,r %p,*arg %p\n", - tp, handle, tca, arg, opt, p, r, arg ? *arg : NULL); + tp, handle, tca, arg, opt, p, r, *arg); if (!opt) return 0; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index e15ff335953d..771b068f8254 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -796,9 +796,7 @@ static struct tc_u_knode *u32_init_knode(struct net *net, struct tcf_proto *tp, struct tc_u32_sel *s = &n->sel; struct tc_u_knode *new; - new = kzalloc(sizeof(*n) + s->nkeys*sizeof(struct tc_u32_key), - GFP_KERNEL); - + new = kzalloc(struct_size(new, sel.keys, s->nkeys), GFP_KERNEL); if (!new) return NULL; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 9a3449b56bd6..11ebba60da3b 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1093,8 +1093,7 @@ skip: int err; /* Only support running class lockless if parent is lockless */ - if (new && (new->flags & TCQ_F_NOLOCK) && - parent && !(parent->flags & TCQ_F_NOLOCK)) + if (new && (new->flags & TCQ_F_NOLOCK) && !(parent->flags & TCQ_F_NOLOCK)) qdisc_clear_nolock(new); if (!cops || !cops->graft) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index ca813697728e..65a95cb094e8 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -312,8 +312,8 @@ static const u8 precedence[] = { }; static const u8 diffserv8[] = { - 2, 5, 1, 2, 4, 2, 2, 2, - 0, 2, 1, 2, 1, 2, 1, 2, + 2, 0, 1, 2, 4, 2, 2, 2, + 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 4, 2, 4, 2, 4, 2, 3, 2, 3, 2, 3, 2, 3, 2, 6, 2, 3, 2, 3, 2, 3, 2, @@ -323,7 +323,7 @@ static const u8 diffserv8[] = { }; static const u8 diffserv4[] = { - 0, 2, 0, 0, 2, 0, 0, 0, + 0, 1, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, @@ -334,7 +334,7 @@ static const u8 diffserv4[] = { }; static const u8 diffserv3[] = { - 0, 0, 0, 0, 2, 0, 0, 0, + 0, 1, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index b1eb12d33b9a..e981992634dd 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -1108,11 +1108,10 @@ static void setup_txtime(struct taprio_sched *q, static struct tc_taprio_qopt_offload *taprio_offload_alloc(int num_entries) { - size_t size = sizeof(struct tc_taprio_sched_entry) * num_entries + - sizeof(struct __tc_taprio_qopt_offload); struct __tc_taprio_qopt_offload *__offload; - __offload = kzalloc(size, GFP_KERNEL); + __offload = kzalloc(struct_size(__offload, offload.entries, num_entries), + GFP_KERNEL); if (!__offload) return NULL; diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 383f87bc1061..940d176e0e87 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -250,8 +250,8 @@ static void tipc_bcast_select_xmit_method(struct net *net, int dests, * Consumes the buffer chain. * Returns 0 if success, otherwise errno: -EHOSTUNREACH,-EMSGSIZE */ -static int tipc_bcast_xmit(struct net *net, struct sk_buff_head *pkts, - u16 *cong_link_cnt) +int tipc_bcast_xmit(struct net *net, struct sk_buff_head *pkts, + u16 *cong_link_cnt) { struct tipc_link *l = tipc_bc_sndlink(net); struct sk_buff_head xmitq; @@ -752,7 +752,7 @@ void tipc_nlist_purge(struct tipc_nlist *nl) nl->local = false; } -u32 tipc_bcast_get_broadcast_mode(struct net *net) +u32 tipc_bcast_get_mode(struct net *net) { struct tipc_bc_base *bb = tipc_bc_base(net); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 4240c95188b1..2d9352dc7b0e 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -90,6 +90,8 @@ void tipc_bcast_toggle_rcast(struct net *net, bool supp); int tipc_mcast_xmit(struct net *net, struct sk_buff_head *pkts, struct tipc_mc_method *method, struct tipc_nlist *dests, u16 *cong_link_cnt); +int tipc_bcast_xmit(struct net *net, struct sk_buff_head *pkts, + u16 *cong_link_cnt); int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb); void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr); @@ -101,7 +103,7 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]); int tipc_bclink_reset_stats(struct net *net, struct tipc_link *l); -u32 tipc_bcast_get_broadcast_mode(struct net *net); +u32 tipc_bcast_get_mode(struct net *net); u32 tipc_bcast_get_broadcast_ratio(struct net *net); void tipc_mcast_filter_msg(struct net *net, struct sk_buff_head *defq, diff --git a/net/tipc/link.c b/net/tipc/link.c index ee3b8d0576b8..1c579357ccdf 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1385,12 +1385,12 @@ u16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, struct tipc_link *l, p = (struct tipc_gap_ack_blks *)msg_data(hdr); sz = ntohs(p->len); /* Sanity check */ - if (sz == tipc_gap_ack_blks_sz(p->ugack_cnt + p->bgack_cnt)) { + if (sz == struct_size(p, gacks, p->ugack_cnt + p->bgack_cnt)) { /* Good, check if the desired type exists */ if ((uc && p->ugack_cnt) || (!uc && p->bgack_cnt)) goto ok; /* Backward compatible: peer might not support bc, but uc? */ - } else if (uc && sz == tipc_gap_ack_blks_sz(p->ugack_cnt)) { + } else if (uc && sz == struct_size(p, gacks, p->ugack_cnt)) { if (p->ugack_cnt) { p->bgack_cnt = 0; goto ok; @@ -1472,7 +1472,7 @@ static u16 tipc_build_gap_ack_blks(struct tipc_link *l, struct tipc_msg *hdr) __tipc_build_gap_ack_blks(ga, l, ga->bgack_cnt) : 0; /* Total len */ - len = tipc_gap_ack_blks_sz(ga->bgack_cnt + ga->ugack_cnt); + len = struct_size(ga, gacks, ga->bgack_cnt + ga->ugack_cnt); ga->len = htons(len); return len; } @@ -1521,7 +1521,7 @@ static int tipc_link_advance_transmq(struct tipc_link *l, struct tipc_link *r, gacks = &ga->gacks[ga->bgack_cnt]; } else if (ga) { /* Copy the Gap ACKs, bc part, for later renewal if needed */ - this_ga = kmemdup(ga, tipc_gap_ack_blks_sz(ga->bgack_cnt), + this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt), GFP_ATOMIC); if (likely(this_ga)) { this_ga->start_index = 0; @@ -2745,7 +2745,7 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, void *hdr; struct nlattr *attrs; struct nlattr *prop; - u32 bc_mode = tipc_bcast_get_broadcast_mode(net); + u32 bc_mode = tipc_bcast_get_mode(net); u32 bc_ratio = tipc_bcast_get_broadcast_ratio(net); if (!bcl) diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 58660d56bc83..1016e96db5c4 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -189,11 +189,9 @@ struct tipc_gap_ack_blks { struct tipc_gap_ack gacks[]; }; -#define tipc_gap_ack_blks_sz(n) (sizeof(struct tipc_gap_ack_blks) + \ - sizeof(struct tipc_gap_ack) * (n)) - #define MAX_GAP_ACK_BLKS 128 -#define MAX_GAP_ACK_BLKS_SZ tipc_gap_ack_blks_sz(MAX_GAP_ACK_BLKS) +#define MAX_GAP_ACK_BLKS_SZ (sizeof(struct tipc_gap_ack_blks) + \ + sizeof(struct tipc_gap_ack) * MAX_GAP_ACK_BLKS) static inline struct tipc_msg *buf_msg(struct sk_buff *skb) { @@ -438,6 +436,36 @@ static inline void msg_set_errcode(struct tipc_msg *m, u32 err) msg_set_bits(m, 1, 25, 0xf, err); } +static inline void msg_set_bulk(struct tipc_msg *m) +{ + msg_set_bits(m, 1, 28, 0x1, 1); +} + +static inline u32 msg_is_bulk(struct tipc_msg *m) +{ + return msg_bits(m, 1, 28, 0x1); +} + +static inline void msg_set_last_bulk(struct tipc_msg *m) +{ + msg_set_bits(m, 1, 27, 0x1, 1); +} + +static inline u32 msg_is_last_bulk(struct tipc_msg *m) +{ + return msg_bits(m, 1, 27, 0x1); +} + +static inline void msg_set_non_legacy(struct tipc_msg *m) +{ + msg_set_bits(m, 1, 26, 0x1, 1); +} + +static inline u32 msg_is_legacy(struct tipc_msg *m) +{ + return !msg_bits(m, 1, 26, 0x1); +} + static inline u32 msg_reroute_cnt(struct tipc_msg *m) { return msg_bits(m, 1, 21, 0xf); @@ -567,6 +595,16 @@ static inline void msg_set_origport(struct tipc_msg *m, u32 p) msg_set_word(m, 4, p); } +static inline u16 msg_named_seqno(struct tipc_msg *m) +{ + return msg_bits(m, 4, 0, 0xffff); +} + +static inline void msg_set_named_seqno(struct tipc_msg *m, u16 n) +{ + msg_set_bits(m, 4, 0, 0xffff, n); +} + static inline u32 msg_destport(struct tipc_msg *m) { return msg_word(m, 5); diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 5feaf3b67380..2f9c148f17e2 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -102,7 +102,8 @@ struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ) pr_warn("Publication distribution failure\n"); return NULL; } - + msg_set_named_seqno(buf_msg(skb), nt->snd_nxt++); + msg_set_non_legacy(buf_msg(skb)); item = (struct distr_item *)msg_data(buf_msg(skb)); publ_to_item(item, publ); return skb; @@ -114,8 +115,8 @@ struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ) struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ) { struct name_table *nt = tipc_name_table(net); - struct sk_buff *buf; struct distr_item *item; + struct sk_buff *skb; write_lock_bh(&nt->cluster_scope_lock); list_del(&publ->binding_node); @@ -123,15 +124,16 @@ struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ) if (publ->scope == TIPC_NODE_SCOPE) return NULL; - buf = named_prepare_buf(net, WITHDRAWAL, ITEM_SIZE, 0); - if (!buf) { + skb = named_prepare_buf(net, WITHDRAWAL, ITEM_SIZE, 0); + if (!skb) { pr_warn("Withdrawal distribution failure\n"); return NULL; } - - item = (struct distr_item *)msg_data(buf_msg(buf)); + msg_set_named_seqno(buf_msg(skb), nt->snd_nxt++); + msg_set_non_legacy(buf_msg(skb)); + item = (struct distr_item *)msg_data(buf_msg(skb)); publ_to_item(item, publ); - return buf; + return skb; } /** @@ -141,7 +143,7 @@ struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ) * @pls: linked list of publication items to be packed into buffer chain */ static void named_distribute(struct net *net, struct sk_buff_head *list, - u32 dnode, struct list_head *pls) + u32 dnode, struct list_head *pls, u16 seqno) { struct publication *publ; struct sk_buff *skb = NULL; @@ -149,6 +151,7 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, u32 msg_dsz = ((tipc_node_get_mtu(net, dnode, 0, false) - INT_H_SIZE) / ITEM_SIZE) * ITEM_SIZE; u32 msg_rem = msg_dsz; + struct tipc_msg *hdr; list_for_each_entry(publ, pls, binding_node) { /* Prepare next buffer: */ @@ -159,8 +162,11 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, pr_warn("Bulk publication failure\n"); return; } - msg_set_bc_ack_invalid(buf_msg(skb), true); - item = (struct distr_item *)msg_data(buf_msg(skb)); + hdr = buf_msg(skb); + msg_set_bc_ack_invalid(hdr, true); + msg_set_bulk(hdr); + msg_set_non_legacy(hdr); + item = (struct distr_item *)msg_data(hdr); } /* Pack publication into message: */ @@ -176,24 +182,35 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, } } if (skb) { - msg_set_size(buf_msg(skb), INT_H_SIZE + (msg_dsz - msg_rem)); + hdr = buf_msg(skb); + msg_set_size(hdr, INT_H_SIZE + (msg_dsz - msg_rem)); skb_trim(skb, INT_H_SIZE + (msg_dsz - msg_rem)); __skb_queue_tail(list, skb); } + hdr = buf_msg(skb_peek_tail(list)); + msg_set_last_bulk(hdr); + msg_set_named_seqno(hdr, seqno); } /** * tipc_named_node_up - tell specified node about all publications by this node */ -void tipc_named_node_up(struct net *net, u32 dnode) +void tipc_named_node_up(struct net *net, u32 dnode, u16 capabilities) { struct name_table *nt = tipc_name_table(net); + struct tipc_net *tn = tipc_net(net); struct sk_buff_head head; + u16 seqno; __skb_queue_head_init(&head); + spin_lock_bh(&tn->nametbl_lock); + if (!(capabilities & TIPC_NAMED_BCAST)) + nt->rc_dests++; + seqno = nt->snd_nxt; + spin_unlock_bh(&tn->nametbl_lock); read_lock_bh(&nt->cluster_scope_lock); - named_distribute(net, &head, dnode, &nt->cluster_scope); + named_distribute(net, &head, dnode, &nt->cluster_scope, seqno); tipc_node_xmit(net, &head, dnode, 0); read_unlock_bh(&nt->cluster_scope_lock); } @@ -245,13 +262,21 @@ static void tipc_dist_queue_purge(struct net *net, u32 addr) spin_unlock_bh(&tn->nametbl_lock); } -void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr) +void tipc_publ_notify(struct net *net, struct list_head *nsub_list, + u32 addr, u16 capabilities) { + struct name_table *nt = tipc_name_table(net); + struct tipc_net *tn = tipc_net(net); + struct publication *publ, *tmp; list_for_each_entry_safe(publ, tmp, nsub_list, binding_node) tipc_publ_purge(net, publ, addr); tipc_dist_queue_purge(net, addr); + spin_lock_bh(&tn->nametbl_lock); + if (!(capabilities & TIPC_NAMED_BCAST)) + nt->rc_dests--; + spin_unlock_bh(&tn->nametbl_lock); } /** @@ -295,29 +320,62 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, return false; } +static struct sk_buff *tipc_named_dequeue(struct sk_buff_head *namedq, + u16 *rcv_nxt, bool *open) +{ + struct sk_buff *skb, *tmp; + struct tipc_msg *hdr; + u16 seqno; + + skb_queue_walk_safe(namedq, skb, tmp) { + skb_linearize(skb); + hdr = buf_msg(skb); + seqno = msg_named_seqno(hdr); + if (msg_is_last_bulk(hdr)) { + *rcv_nxt = seqno; + *open = true; + } + + if (msg_is_bulk(hdr) || msg_is_legacy(hdr)) { + __skb_unlink(skb, namedq); + return skb; + } + + if (*open && (*rcv_nxt == seqno)) { + (*rcv_nxt)++; + __skb_unlink(skb, namedq); + return skb; + } + + if (less(seqno, *rcv_nxt)) { + __skb_unlink(skb, namedq); + kfree_skb(skb); + continue; + } + } + return NULL; +} + /** * tipc_named_rcv - process name table update messages sent by another node */ -void tipc_named_rcv(struct net *net, struct sk_buff_head *inputq) +void tipc_named_rcv(struct net *net, struct sk_buff_head *namedq, + u16 *rcv_nxt, bool *open) { - struct tipc_net *tn = net_generic(net, tipc_net_id); - struct tipc_msg *msg; + struct tipc_net *tn = tipc_net(net); struct distr_item *item; - uint count; - u32 node; + struct tipc_msg *hdr; struct sk_buff *skb; - int mtype; + u32 count, node; spin_lock_bh(&tn->nametbl_lock); - for (skb = skb_dequeue(inputq); skb; skb = skb_dequeue(inputq)) { - skb_linearize(skb); - msg = buf_msg(skb); - mtype = msg_type(msg); - item = (struct distr_item *)msg_data(msg); - count = msg_data_sz(msg) / ITEM_SIZE; - node = msg_orignode(msg); + while ((skb = tipc_named_dequeue(namedq, rcv_nxt, open))) { + hdr = buf_msg(skb); + node = msg_orignode(hdr); + item = (struct distr_item *)msg_data(hdr); + count = msg_data_sz(hdr) / ITEM_SIZE; while (count--) { - tipc_update_nametbl(net, item, node, mtype); + tipc_update_nametbl(net, item, node, msg_type(hdr)); item++; } kfree_skb(skb); @@ -345,6 +403,6 @@ void tipc_named_reinit(struct net *net) publ->node = self; list_for_each_entry_rcu(publ, &nt->cluster_scope, binding_node) publ->node = self; - + nt->rc_dests = 0; spin_unlock_bh(&tn->nametbl_lock); } diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h index 63fc73e0fa6c..092323158f06 100644 --- a/net/tipc/name_distr.h +++ b/net/tipc/name_distr.h @@ -67,11 +67,14 @@ struct distr_item { __be32 key; }; +void tipc_named_bcast(struct net *net, struct sk_buff *skb); struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ); struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ); -void tipc_named_node_up(struct net *net, u32 dnode); -void tipc_named_rcv(struct net *net, struct sk_buff_head *msg_queue); +void tipc_named_node_up(struct net *net, u32 dnode, u16 capabilities); +void tipc_named_rcv(struct net *net, struct sk_buff_head *namedq, + u16 *rcv_nxt, bool *open); void tipc_named_reinit(struct net *net); -void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr); +void tipc_publ_notify(struct net *net, struct list_head *nsub_list, + u32 addr, u16 capabilities); #endif diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 359b2bc888cf..2ac33d32edc2 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -729,6 +729,7 @@ struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, struct tipc_net *tn = tipc_net(net); struct publication *p = NULL; struct sk_buff *skb = NULL; + u32 rc_dests; spin_lock_bh(&tn->nametbl_lock); @@ -743,12 +744,14 @@ struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, nt->local_publ_count++; skb = tipc_named_publish(net, p); } + rc_dests = nt->rc_dests; exit: spin_unlock_bh(&tn->nametbl_lock); if (skb) - tipc_node_broadcast(net, skb); + tipc_node_broadcast(net, skb, rc_dests); return p; + } /** @@ -762,6 +765,7 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 self = tipc_own_addr(net); struct sk_buff *skb = NULL; struct publication *p; + u32 rc_dests; spin_lock_bh(&tn->nametbl_lock); @@ -775,10 +779,11 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, pr_err("Failed to remove local publication {%u,%u,%u}/%u\n", type, lower, upper, key); } + rc_dests = nt->rc_dests; spin_unlock_bh(&tn->nametbl_lock); if (skb) { - tipc_node_broadcast(net, skb); + tipc_node_broadcast(net, skb, rc_dests); return 1; } return 0; diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index 728bc7016c38..8064e1986e2c 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -106,6 +106,8 @@ struct name_table { struct list_head cluster_scope; rwlock_t cluster_scope_lock; u32 local_publ_count; + u32 rc_dests; + u32 snd_nxt; }; int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb); diff --git a/net/tipc/node.c b/net/tipc/node.c index a4c2816c3746..030a51c4d1fa 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -75,6 +75,8 @@ struct tipc_bclink_entry { struct sk_buff_head arrvq; struct sk_buff_head inputq2; struct sk_buff_head namedq; + u16 named_rcv_nxt; + bool named_open; }; /** @@ -396,10 +398,10 @@ static void tipc_node_write_unlock(struct tipc_node *n) write_unlock_bh(&n->lock); if (flags & TIPC_NOTIFY_NODE_DOWN) - tipc_publ_notify(net, publ_list, addr); + tipc_publ_notify(net, publ_list, addr, n->capabilities); if (flags & TIPC_NOTIFY_NODE_UP) - tipc_named_node_up(net, addr); + tipc_named_node_up(net, addr, n->capabilities); if (flags & TIPC_NOTIFY_LINK_UP) { tipc_mon_peer_up(net, addr, bearer_id); @@ -1483,6 +1485,7 @@ static void node_lost_contact(struct tipc_node *n, /* Clean up broadcast state */ tipc_bcast_remove_peer(n->net, n->bc_entry.link); + __skb_queue_purge(&n->bc_entry.namedq); /* Abort any ongoing link failover */ for (i = 0; i < MAX_BEARERS; i++) { @@ -1729,12 +1732,23 @@ int tipc_node_distr_xmit(struct net *net, struct sk_buff_head *xmitq) return 0; } -void tipc_node_broadcast(struct net *net, struct sk_buff *skb) +void tipc_node_broadcast(struct net *net, struct sk_buff *skb, int rc_dests) { + struct sk_buff_head xmitq; struct sk_buff *txskb; struct tipc_node *n; + u16 dummy; u32 dst; + /* Use broadcast if all nodes support it */ + if (!rc_dests && tipc_bcast_get_mode(net) != BCLINK_MODE_RCAST) { + __skb_queue_head_init(&xmitq); + __skb_queue_tail(&xmitq, skb); + tipc_bcast_xmit(net, &xmitq, &dummy); + return; + } + + /* Otherwise use legacy replicast method */ rcu_read_lock(); list_for_each_entry_rcu(n, tipc_nodes(net), list) { dst = n->addr; @@ -1749,7 +1763,6 @@ void tipc_node_broadcast(struct net *net, struct sk_buff *skb) tipc_node_xmit_skb(net, txskb, dst, 0); } rcu_read_unlock(); - kfree_skb(skb); } @@ -1844,7 +1857,9 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id /* Handle NAME_DISTRIBUTOR messages sent from 1.7 nodes */ if (!skb_queue_empty(&n->bc_entry.namedq)) - tipc_named_rcv(net, &n->bc_entry.namedq); + tipc_named_rcv(net, &n->bc_entry.namedq, + &n->bc_entry.named_rcv_nxt, + &n->bc_entry.named_open); /* If reassembly or retransmission failure => reset all links to peer */ if (rc & TIPC_LINK_DOWN_EVT) @@ -2114,7 +2129,9 @@ rcv: tipc_node_link_down(n, bearer_id, false); if (unlikely(!skb_queue_empty(&n->bc_entry.namedq))) - tipc_named_rcv(net, &n->bc_entry.namedq); + tipc_named_rcv(net, &n->bc_entry.namedq, + &n->bc_entry.named_rcv_nxt, + &n->bc_entry.named_open); if (unlikely(!skb_queue_empty(&n->bc_entry.inputq1))) tipc_node_mcast_rcv(n); diff --git a/net/tipc/node.h b/net/tipc/node.h index a6803b449a2c..9f6f13f1604f 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -55,7 +55,8 @@ enum { TIPC_MCAST_RBCTL = (1 << 7), TIPC_GAP_ACK_BLOCK = (1 << 8), TIPC_TUNNEL_ENHANCED = (1 << 9), - TIPC_NAGLE = (1 << 10) + TIPC_NAGLE = (1 << 10), + TIPC_NAMED_BCAST = (1 << 11) }; #define TIPC_NODE_CAPABILITIES (TIPC_SYN_BIT | \ @@ -68,7 +69,8 @@ enum { TIPC_MCAST_RBCTL | \ TIPC_GAP_ACK_BLOCK | \ TIPC_TUNNEL_ENHANCED | \ - TIPC_NAGLE) + TIPC_NAGLE | \ + TIPC_NAMED_BCAST) #define INVALID_BEARER_ID -1 @@ -101,7 +103,7 @@ int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dest, u32 selector); void tipc_node_subscribe(struct net *net, struct list_head *subscr, u32 addr); void tipc_node_unsubscribe(struct net *net, struct list_head *subscr, u32 addr); -void tipc_node_broadcast(struct net *net, struct sk_buff *skb); +void tipc_node_broadcast(struct net *net, struct sk_buff *skb, int rc_dests); int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port); void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port); int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel, bool connected); diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 626096bd0d29..edf11893dbe8 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -106,6 +106,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur struct sk_buff *skb2, *nskb, *pskb = NULL; netdev_features_t esp_features = features; struct xfrm_offload *xo = xfrm_offload(skb); + struct net_device *dev = skb->dev; struct sec_path *sp; if (!xo || (xo->flags & XFRM_XMIT)) @@ -119,6 +120,10 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND) return skb; + /* This skb was already validated on the upper/virtual dev */ + if ((x->xso.dev != dev) && (x->xso.real_dev == dev)) + return skb; + local_irq_save(flags); sd = this_cpu_ptr(&softnet_data); err = !skb_queue_empty(&sd->xfrm_backlog); @@ -131,25 +136,20 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur xo->flags |= XFRM_XMIT; - if (skb_is_gso(skb)) { - struct net_device *dev = skb->dev; - - if (unlikely(x->xso.dev != dev)) { - struct sk_buff *segs; + if (skb_is_gso(skb) && unlikely(x->xso.dev != dev)) { + struct sk_buff *segs; - /* Packet got rerouted, fixup features and segment it. */ - esp_features = esp_features & ~(NETIF_F_HW_ESP - | NETIF_F_GSO_ESP); + /* Packet got rerouted, fixup features and segment it. */ + esp_features = esp_features & ~(NETIF_F_HW_ESP | NETIF_F_GSO_ESP); - segs = skb_gso_segment(skb, esp_features); - if (IS_ERR(segs)) { - kfree_skb(skb); - atomic_long_inc(&dev->tx_dropped); - return NULL; - } else { - consume_skb(skb); - skb = segs; - } + segs = skb_gso_segment(skb, esp_features); + if (IS_ERR(segs)) { + kfree_skb(skb); + atomic_long_inc(&dev->tx_dropped); + return NULL; + } else { + consume_skb(skb); + skb = segs; } } @@ -261,6 +261,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, } xso->dev = dev; + xso->real_dev = dev; xso->num_exthdrs = 1; xso->flags = xuo->flags; diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 895ec992b2f1..bfacb960450f 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -17,6 +17,7 @@ TEST_PROGS += route_localnet.sh TEST_PROGS += reuseaddr_ports_exhausted.sh TEST_PROGS += txtimestamp.sh TEST_PROGS += vrf-xfrm-tests.sh +TEST_PROGS += rxtimestamp.sh TEST_PROGS_EXTENDED := in_netns.sh TEST_GEN_FILES = socket nettest TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any diff --git a/tools/testing/selftests/net/forwarding/pedit_l4port.sh b/tools/testing/selftests/net/forwarding/pedit_l4port.sh new file mode 100755 index 000000000000..5f20d289ee43 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/pedit_l4port.sh @@ -0,0 +1,198 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# This test sends traffic from H1 to H2. Either on ingress of $swp1, or on egress of $swp2, the +# traffic is acted upon by a pedit action. An ingress filter installed on $h2 verifies that the +# packet looks like expected. +# +# +----------------------+ +----------------------+ +# | H1 | | H2 | +# | + $h1 | | $h2 + | +# | | 192.0.2.1/28 | | 192.0.2.2/28 | | +# +----|-----------------+ +----------------|-----+ +# | | +# +----|----------------------------------------------------------------|-----+ +# | SW | | | +# | +-|----------------------------------------------------------------|-+ | +# | | + $swp1 BR $swp2 + | | +# | +--------------------------------------------------------------------+ | +# +---------------------------------------------------------------------------+ + +ALL_TESTS=" + ping_ipv4 + test_udp_sport + test_udp_dport + test_tcp_sport + test_tcp_dport +" + +NUM_NETIFS=4 +source lib.sh +source tc_common.sh + +: ${HIT_TIMEOUT:=2000} # ms + +h1_create() +{ + simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64 +} + +h1_destroy() +{ + simple_if_fini $h1 192.0.2.1/28 2001:db8:1::1/64 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.2/28 2001:db8:1::2/64 + tc qdisc add dev $h2 clsact +} + +h2_destroy() +{ + tc qdisc del dev $h2 clsact + simple_if_fini $h2 192.0.2.2/28 2001:db8:1::2/64 +} + +switch_create() +{ + ip link add name br1 up type bridge vlan_filtering 1 + ip link set dev $swp1 master br1 + ip link set dev $swp1 up + ip link set dev $swp2 master br1 + ip link set dev $swp2 up + + tc qdisc add dev $swp1 clsact + tc qdisc add dev $swp2 clsact +} + +switch_destroy() +{ + tc qdisc del dev $swp2 clsact + tc qdisc del dev $swp1 clsact + + ip link set dev $swp2 nomaster + ip link set dev $swp1 nomaster + ip link del dev br1 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + h2mac=$(mac_get $h2) + + vrf_prepare + h1_create + h2_create + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + h2_destroy + h1_destroy + vrf_cleanup +} + +ping_ipv4() +{ + ping_test $h1 192.0.2.2 +} + +ping_ipv6() +{ + ping6_test $h1 2001:db8:1::2 +} + +do_test_pedit_l4port_one() +{ + local pedit_locus=$1; shift + local pedit_prot=$1; shift + local pedit_action=$1; shift + local match_prot=$1; shift + local match_flower=$1; shift + local mz_flags=$1; shift + local saddr=$1; shift + local daddr=$1; shift + + tc filter add $pedit_locus handle 101 pref 1 \ + flower action pedit ex munge $pedit_action + tc filter add dev $h2 ingress handle 101 pref 1 prot $match_prot \ + flower skip_hw $match_flower action pass + + RET=0 + + $MZ $mz_flags $h1 -c 10 -d 20msec -p 100 \ + -a own -b $h2mac -q -t $pedit_prot sp=54321,dp=12345 + + local pkts + pkts=$(busywait "$TC_HIT_TIMEOUT" until_counter_is ">= 10" \ + tc_rule_handle_stats_get "dev $h2 ingress" 101) + check_err $? "Expected to get 10 packets, but got $pkts." + + pkts=$(tc_rule_handle_stats_get "$pedit_locus" 101) + ((pkts >= 10)) + check_err $? "Expected to get 10 packets on pedit rule, but got $pkts." + + log_test "$pedit_locus pedit $pedit_action" + + tc filter del dev $h2 ingress pref 1 + tc filter del $pedit_locus pref 1 +} + +do_test_pedit_l4port() +{ + local locus=$1; shift + local prot=$1; shift + local pedit_port=$1; shift + local flower_port=$1; shift + local port + + for port in 1 11111 65535; do + do_test_pedit_l4port_one "$locus" "$prot" \ + "$prot $pedit_port set $port" \ + ip "ip_proto $prot $flower_port $port" \ + "-A 192.0.2.1 -B 192.0.2.2" + done +} + +test_udp_sport() +{ + do_test_pedit_l4port "dev $swp1 ingress" udp sport src_port + do_test_pedit_l4port "dev $swp2 egress" udp sport src_port +} + +test_udp_dport() +{ + do_test_pedit_l4port "dev $swp1 ingress" udp dport dst_port + do_test_pedit_l4port "dev $swp2 egress" udp dport dst_port +} + +test_tcp_sport() +{ + do_test_pedit_l4port "dev $swp1 ingress" tcp sport src_port + do_test_pedit_l4port "dev $swp2 egress" tcp sport src_port +} + +test_tcp_dport() +{ + do_test_pedit_l4port "dev $swp1 ingress" tcp dport dst_port + do_test_pedit_l4port "dev $swp2 egress" tcp dport dst_port +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/rxtimestamp.c b/tools/testing/selftests/net/rxtimestamp.c index 422e7761254d..d4ea86a13e52 100644 --- a/tools/testing/selftests/net/rxtimestamp.c +++ b/tools/testing/selftests/net/rxtimestamp.c @@ -44,6 +44,7 @@ struct test_case { struct options sockopt; struct tstamps expected; bool enabled; + bool warn_on_fail; }; struct sof_flag { @@ -89,7 +90,7 @@ static struct test_case test_cases[] = { }, { { so_timestamping: SOF_TIMESTAMPING_SOFTWARE }, - {} + warn_on_fail : true }, { { so_timestamping: SOF_TIMESTAMPING_RX_SOFTWARE @@ -115,6 +116,7 @@ static struct option long_options[] = { { "tcp", no_argument, 0, 't' }, { "udp", no_argument, 0, 'u' }, { "ip", no_argument, 0, 'i' }, + { "strict", no_argument, 0, 'S' }, { NULL, 0, NULL, 0 }, }; @@ -327,6 +329,7 @@ int main(int argc, char **argv) { bool all_protocols = true; bool all_tests = true; + bool strict = false; int arg_index = 0; int failures = 0; int s, t; @@ -363,6 +366,9 @@ int main(int argc, char **argv) all_protocols = false; socket_types[0].enabled = true; break; + case 'S': + strict = true; + break; default: error(1, 0, "Failed to parse parameters."); } @@ -379,7 +385,8 @@ int main(int argc, char **argv) printf("Starting testcase %d...\n", t); if (run_test_case(socket_types[s], test_cases[t])) { - failures++; + if (strict || !test_cases[t].warn_on_fail) + failures++; printf("FAILURE in test case "); print_test_case(&test_cases[t]); } diff --git a/tools/testing/selftests/net/rxtimestamp.sh b/tools/testing/selftests/net/rxtimestamp.sh new file mode 100755 index 000000000000..91631e88bf46 --- /dev/null +++ b/tools/testing/selftests/net/rxtimestamp.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +./in_netns.sh ./rxtimestamp $@ diff --git a/tools/testing/selftests/net/vrf_strict_mode_test.sh b/tools/testing/selftests/net/vrf_strict_mode_test.sh new file mode 100755 index 000000000000..5274f4a1fba1 --- /dev/null +++ b/tools/testing/selftests/net/vrf_strict_mode_test.sh @@ -0,0 +1,390 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# This test is designed for testing the new VRF strict_mode functionality. + +ret=0 + +# identifies the "init" network namespace which is often called root network +# namespace. +INIT_NETNS_NAME="init" + +PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} + +log_test() +{ + local rc=$1 + local expected=$2 + local msg="$3" + + if [ ${rc} -eq ${expected} ]; then + nsuccess=$((nsuccess+1)) + printf "\n TEST: %-60s [ OK ]\n" "${msg}" + else + ret=1 + nfail=$((nfail+1)) + printf "\n TEST: %-60s [FAIL]\n" "${msg}" + if [ "${PAUSE_ON_FAIL}" = "yes" ]; then + echo + echo "hit enter to continue, 'q' to quit" + read a + [ "$a" = "q" ] && exit 1 + fi + fi +} + +print_log_test_results() +{ + if [ "$TESTS" != "none" ]; then + printf "\nTests passed: %3d\n" ${nsuccess} + printf "Tests failed: %3d\n" ${nfail} + fi +} + +log_section() +{ + echo + echo "################################################################################" + echo "TEST SECTION: $*" + echo "################################################################################" +} + +ip_expand_args() +{ + local nsname=$1 + local nsarg="" + + if [ "${nsname}" != "${INIT_NETNS_NAME}" ]; then + nsarg="-netns ${nsname}" + fi + + echo "${nsarg}" +} + +vrf_count() +{ + local nsname=$1 + local nsarg="$(ip_expand_args ${nsname})" + + ip ${nsarg} -o link show type vrf | wc -l +} + +count_vrf_by_table_id() +{ + local nsname=$1 + local tableid=$2 + local nsarg="$(ip_expand_args ${nsname})" + + ip ${nsarg} -d -o link show type vrf | grep "table ${tableid}" | wc -l +} + +add_vrf() +{ + local nsname=$1 + local vrfname=$2 + local vrftable=$3 + local nsarg="$(ip_expand_args ${nsname})" + + ip ${nsarg} link add ${vrfname} type vrf table ${vrftable} &>/dev/null +} + +add_vrf_and_check() +{ + local nsname=$1 + local vrfname=$2 + local vrftable=$3 + local cnt + local rc + + add_vrf ${nsname} ${vrfname} ${vrftable}; rc=$? + + cnt=$(count_vrf_by_table_id ${nsname} ${vrftable}) + + log_test ${rc} 0 "${nsname}: add vrf ${vrfname}, ${cnt} vrfs for table ${vrftable}" +} + +add_vrf_and_check_fail() +{ + local nsname=$1 + local vrfname=$2 + local vrftable=$3 + local cnt + local rc + + add_vrf ${nsname} ${vrfname} ${vrftable}; rc=$? + + cnt=$(count_vrf_by_table_id ${nsname} ${vrftable}) + + log_test ${rc} 2 "${nsname}: CANNOT add vrf ${vrfname}, ${cnt} vrfs for table ${vrftable}" +} + +del_vrf_and_check() +{ + local nsname=$1 + local vrfname=$2 + local nsarg="$(ip_expand_args ${nsname})" + + ip ${nsarg} link del ${vrfname} + log_test $? 0 "${nsname}: remove vrf ${vrfname}" +} + +config_vrf_and_check() +{ + local nsname=$1 + local addr=$2 + local vrfname=$3 + local nsarg="$(ip_expand_args ${nsname})" + + ip ${nsarg} link set dev ${vrfname} up && \ + ip ${nsarg} addr add ${addr} dev ${vrfname} + log_test $? 0 "${nsname}: vrf ${vrfname} up, addr ${addr}" +} + +read_strict_mode() +{ + local nsname=$1 + local rval + local rc=0 + local nsexec="" + + if [ "${nsname}" != "${INIT_NETNS_NAME}" ]; then + # a custom network namespace is provided + nsexec="ip netns exec ${nsname}" + fi + + rval="$(${nsexec} bash -c "cat /proc/sys/net/vrf/strict_mode" | \ + grep -E "^[0-1]$")" &> /dev/null + if [ $? -ne 0 ]; then + # set errors + rval=255 + rc=1 + fi + + # on success, rval can be only 0 or 1; on error, rval is equal to 255 + echo ${rval} + return ${rc} +} + +read_strict_mode_compare_and_check() +{ + local nsname=$1 + local expected=$2 + local res + + res="$(read_strict_mode ${nsname})" + log_test ${res} ${expected} "${nsname}: check strict_mode=${res}" +} + +set_strict_mode() +{ + local nsname=$1 + local val=$2 + local nsexec="" + + if [ "${nsname}" != "${INIT_NETNS_NAME}" ]; then + # a custom network namespace is provided + nsexec="ip netns exec ${nsname}" + fi + + ${nsexec} bash -c "echo ${val} >/proc/sys/net/vrf/strict_mode" &>/dev/null +} + +enable_strict_mode() +{ + local nsname=$1 + + set_strict_mode ${nsname} 1 +} + +disable_strict_mode() +{ + local nsname=$1 + + set_strict_mode ${nsname} 0 +} + +disable_strict_mode_and_check() +{ + local nsname=$1 + + disable_strict_mode ${nsname} + log_test $? 0 "${nsname}: disable strict_mode (=0)" +} + +enable_strict_mode_and_check() +{ + local nsname=$1 + + enable_strict_mode ${nsname} + log_test $? 0 "${nsname}: enable strict_mode (=1)" +} + +enable_strict_mode_and_check_fail() +{ + local nsname=$1 + + enable_strict_mode ${nsname} + log_test $? 1 "${nsname}: CANNOT enable strict_mode" +} + +strict_mode_check_default() +{ + local nsname=$1 + local strictmode + local vrfcnt + + vrfcnt=$(vrf_count ${nsname}) + strictmode=$(read_strict_mode ${nsname}) + log_test ${strictmode} 0 "${nsname}: strict_mode=0 by default, ${vrfcnt} vrfs" +} + +setup() +{ + modprobe vrf + + ip netns add testns + ip netns exec testns ip link set lo up +} + +cleanup() +{ + ip netns del testns 2>/dev/null + + ip link del vrf100 2>/dev/null + ip link del vrf101 2>/dev/null + ip link del vrf102 2>/dev/null + + echo 0 >/proc/sys/net/vrf/strict_mode 2>/dev/null +} + +vrf_strict_mode_tests_init() +{ + vrf_strict_mode_check_support init + + strict_mode_check_default init + + add_vrf_and_check init vrf100 100 + config_vrf_and_check init 172.16.100.1/24 vrf100 + + enable_strict_mode_and_check init + + add_vrf_and_check_fail init vrf101 100 + + disable_strict_mode_and_check init + + add_vrf_and_check init vrf101 100 + config_vrf_and_check init 172.16.101.1/24 vrf101 + + enable_strict_mode_and_check_fail init + + del_vrf_and_check init vrf101 + + enable_strict_mode_and_check init + + add_vrf_and_check init vrf102 102 + config_vrf_and_check init 172.16.102.1/24 vrf102 + + # the strict_modle is enabled in the init +} + +vrf_strict_mode_tests_testns() +{ + vrf_strict_mode_check_support testns + + strict_mode_check_default testns + + enable_strict_mode_and_check testns + + add_vrf_and_check testns vrf100 100 + config_vrf_and_check testns 10.0.100.1/24 vrf100 + + add_vrf_and_check_fail testns vrf101 100 + + add_vrf_and_check_fail testns vrf102 100 + + add_vrf_and_check testns vrf200 200 + + disable_strict_mode_and_check testns + + add_vrf_and_check testns vrf101 100 + + add_vrf_and_check testns vrf102 100 + + #the strict_mode is disabled in the testns +} + +vrf_strict_mode_tests_mix() +{ + read_strict_mode_compare_and_check init 1 + + read_strict_mode_compare_and_check testns 0 + + del_vrf_and_check testns vrf101 + + del_vrf_and_check testns vrf102 + + disable_strict_mode_and_check init + + enable_strict_mode_and_check testns + + enable_strict_mode_and_check init + enable_strict_mode_and_check init + + disable_strict_mode_and_check testns + disable_strict_mode_and_check testns + + read_strict_mode_compare_and_check init 1 + + read_strict_mode_compare_and_check testns 0 +} + +vrf_strict_mode_tests() +{ + log_section "VRF strict_mode test on init network namespace" + vrf_strict_mode_tests_init + + log_section "VRF strict_mode test on testns network namespace" + vrf_strict_mode_tests_testns + + log_section "VRF strict_mode test mixing init and testns network namespaces" + vrf_strict_mode_tests_mix +} + +vrf_strict_mode_check_support() +{ + local nsname=$1 + local output + local rc + + output="$(lsmod | grep '^vrf' | awk '{print $1}')" + if [ -z "${output}" ]; then + modinfo vrf || return $? + fi + + # we do not care about the value of the strict_mode; we only check if + # the strict_mode parameter is available or not. + read_strict_mode ${nsname} &>/dev/null; rc=$? + log_test ${rc} 0 "${nsname}: net.vrf.strict_mode is available" + + return ${rc} +} + +if [ "$(id -u)" -ne 0 ];then + echo "SKIP: Need root privileges" + exit 0 +fi + +if [ ! -x "$(command -v ip)" ]; then + echo "SKIP: Could not run test without ip tool" + exit 0 +fi + +cleanup &> /dev/null + +setup +vrf_strict_mode_tests +cleanup + +print_log_test_results + +exit $ret |