diff options
Diffstat (limited to 'drivers/net')
370 files changed, 18291 insertions, 10128 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 260f9f46668b..1ebb4b943876 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -42,6 +42,7 @@ config BONDING tristate "Bonding driver support" depends on INET depends on IPV6 || IPV6=n + depends on TLS || TLS_DEVICE=n help Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet Channels together. This is called 'Etherchannel' by Cisco, diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c index cf607ffcf358..81223f6bebcc 100644 --- a/drivers/net/arcnet/com20020_cs.c +++ b/drivers/net/arcnet/com20020_cs.c @@ -67,7 +67,7 @@ static void regdump(struct net_device *dev) /* set up the address register */ count = 0; arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag, - ioaddr, com20020_REG_W_ADDR_HI); + ioaddr, COM20020_REG_W_ADDR_HI); arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); for (count = 0; count < 256 + 32; count++) { diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index 85de5f96c02b..1b8f59713fc7 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -532,11 +532,12 @@ static void bareudp_setup(struct net_device *dev) dev->netdev_ops = &bareudp_netdev_ops; dev->needs_free_netdev = true; SET_NETDEV_DEVTYPE(dev, &bareudp_type); - dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_LLTX; dev->features |= NETIF_F_GSO_SOFTWARE; - dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; + dev->hw_features |= NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; dev->hard_header_len = 0; dev->addr_len = 0; @@ -658,7 +659,6 @@ static int bareudp_newlink(struct net *net, struct net_device *dev, struct netlink_ext_ack *extack) { struct bareudp_conf conf; - LIST_HEAD(list_kill); int err; err = bareudp2info(data, &conf, extack); @@ -676,8 +676,7 @@ static int bareudp_newlink(struct net *net, struct net_device *dev, return 0; err_unconfig: - bareudp_dellink(dev, &list_kill); - unregister_netdevice_many(&list_kill); + bareudp_dellink(dev, NULL); return err; } @@ -729,7 +728,6 @@ struct net_device *bareudp_dev_create(struct net *net, const char *name, { struct nlattr *tb[IFLA_MAX + 1]; struct net_device *dev; - LIST_HEAD(list_kill); int err; memset(tb, 0, sizeof(tb)); @@ -753,8 +751,7 @@ struct net_device *bareudp_dev_create(struct net *net, const char *name, return dev; err: - bareudp_dellink(dev, &list_kill); - unregister_netdevice_many(&list_kill); + bareudp_dellink(dev, NULL); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(bareudp_dev_create); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 5fe5232cc3f3..74cbbb22470b 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -83,6 +83,9 @@ #include <net/bonding.h> #include <net/bond_3ad.h> #include <net/bond_alb.h> +#if IS_ENABLED(CONFIG_TLS_DEVICE) +#include <net/tls.h> +#endif #include "bonding_priv.h" @@ -164,7 +167,7 @@ module_param(xmit_hash_policy, charp, 0); MODULE_PARM_DESC(xmit_hash_policy, "balance-alb, balance-tlb, balance-xor, 802.3ad hashing method; " "0 for layer 2 (default), 1 for layer 3+4, " "2 for layer 2+3, 3 for encap layer 2+3, " - "4 for encap layer 3+4"); + "4 for encap layer 3+4, 5 for vlan+srcmac"); module_param(arp_interval, int, 0); MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds"); module_param_array(arp_ip_target, charp, NULL, 0); @@ -301,6 +304,19 @@ netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, return dev_queue_xmit(skb); } +bool bond_sk_check(struct bonding *bond) +{ + switch (BOND_MODE(bond)) { + case BOND_MODE_8023AD: + case BOND_MODE_XOR: + if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34) + return true; + fallthrough; + default: + return false; + } +} + /*---------------------------------- VLAN -----------------------------------*/ /* In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid, @@ -1212,6 +1228,13 @@ static netdev_features_t bond_fix_features(struct net_device *dev, netdev_features_t mask; struct slave *slave; +#if IS_ENABLED(CONFIG_TLS_DEVICE) + if (bond_sk_check(bond)) + features |= BOND_TLS_FEATURES; + else + features &= ~BOND_TLS_FEATURES; +#endif + mask = features; features &= ~NETIF_F_ONE_FOR_ALL; @@ -1434,6 +1457,8 @@ static enum netdev_lag_hash bond_lag_hash_type(struct bonding *bond, return NETDEV_LAG_HASH_E23; case BOND_XMIT_POLICY_ENCAP34: return NETDEV_LAG_HASH_E34; + case BOND_XMIT_POLICY_VLAN_SRCMAC: + return NETDEV_LAG_HASH_VLAN_SRCMAC; default: return NETDEV_LAG_HASH_UNKNOWN; } @@ -1922,6 +1947,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, goto err_unregister; } + bond_lower_state_changed(new_slave); + res = bond_sysfs_slave_add(new_slave); if (res) { slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res); @@ -3494,6 +3521,27 @@ static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk, return true; } +static u32 bond_vlan_srcmac_hash(struct sk_buff *skb) +{ + struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb); + u32 srcmac_vendor = 0, srcmac_dev = 0; + u16 vlan; + int i; + + for (i = 0; i < 3; i++) + srcmac_vendor = (srcmac_vendor << 8) | mac_hdr->h_source[i]; + + for (i = 3; i < ETH_ALEN; i++) + srcmac_dev = (srcmac_dev << 8) | mac_hdr->h_source[i]; + + if (!skb_vlan_tag_present(skb)) + return srcmac_vendor ^ srcmac_dev; + + vlan = skb_vlan_tag_get(skb); + + return vlan ^ srcmac_vendor ^ srcmac_dev; +} + /* Extract the appropriate headers based on bond's xmit policy */ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb, struct flow_keys *fk) @@ -3501,10 +3549,14 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb, bool l34 = bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34; int noff, proto = -1; - if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23) { + switch (bond->params.xmit_policy) { + case BOND_XMIT_POLICY_ENCAP23: + case BOND_XMIT_POLICY_ENCAP34: memset(fk, 0, sizeof(*fk)); return __skb_flow_dissect(NULL, skb, &flow_keys_bonding, fk, NULL, 0, 0, 0, 0); + default: + break; } fk->ports.ports = 0; @@ -3539,6 +3591,16 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb, return true; } +static u32 bond_ip_hash(u32 hash, struct flow_keys *flow) +{ + hash ^= (__force u32)flow_get_u32_dst(flow) ^ + (__force u32)flow_get_u32_src(flow); + hash ^= (hash >> 16); + hash ^= (hash >> 8); + /* discard lowest hash bit to deal with the common even ports pattern */ + return hash >> 1; +} + /** * bond_xmit_hash - generate a hash value based on the xmit policy * @bond: bonding device @@ -3556,6 +3618,9 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb) skb->l4_hash) return skb->hash; + if (bond->params.xmit_policy == BOND_XMIT_POLICY_VLAN_SRCMAC) + return bond_vlan_srcmac_hash(skb); + if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 || !bond_flow_dissect(bond, skb, &flow)) return bond_eth_hash(skb); @@ -3569,12 +3634,8 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb) else memcpy(&hash, &flow.ports.ports, sizeof(hash)); } - hash ^= (__force u32)flow_get_u32_dst(&flow) ^ - (__force u32)flow_get_u32_src(&flow); - hash ^= (hash >> 16); - hash ^= (hash >> 8); - return hash >> 1; + return bond_ip_hash(hash, &flow); } /*-------------------------- Device entry points ----------------------------*/ @@ -4547,6 +4608,95 @@ static struct net_device *bond_xmit_get_slave(struct net_device *master_dev, return NULL; } +static void bond_sk_to_flow(struct sock *sk, struct flow_keys *flow) +{ + switch (sk->sk_family) { +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + if (sk->sk_ipv6only || + ipv6_addr_type(&sk->sk_v6_daddr) != IPV6_ADDR_MAPPED) { + flow->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + flow->addrs.v6addrs.src = inet6_sk(sk)->saddr; + flow->addrs.v6addrs.dst = sk->sk_v6_daddr; + break; + } + fallthrough; +#endif + default: /* AF_INET */ + flow->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + flow->addrs.v4addrs.src = inet_sk(sk)->inet_rcv_saddr; + flow->addrs.v4addrs.dst = inet_sk(sk)->inet_daddr; + break; + } + + flow->ports.src = inet_sk(sk)->inet_sport; + flow->ports.dst = inet_sk(sk)->inet_dport; +} + +/** + * bond_sk_hash_l34 - generate a hash value based on the socket's L3 and L4 fields + * @sk: socket to use for headers + * + * This function will extract the necessary field from the socket and use + * them to generate a hash based on the LAYER34 xmit_policy. + * Assumes that sk is a TCP or UDP socket. + */ +static u32 bond_sk_hash_l34(struct sock *sk) +{ + struct flow_keys flow; + u32 hash; + + bond_sk_to_flow(sk, &flow); + + /* L4 */ + memcpy(&hash, &flow.ports.ports, sizeof(hash)); + /* L3 */ + return bond_ip_hash(hash, &flow); +} + +static struct net_device *__bond_sk_get_lower_dev(struct bonding *bond, + struct sock *sk) +{ + struct bond_up_slave *slaves; + struct slave *slave; + unsigned int count; + u32 hash; + + slaves = rcu_dereference(bond->usable_slaves); + count = slaves ? READ_ONCE(slaves->count) : 0; + if (unlikely(!count)) + return NULL; + + hash = bond_sk_hash_l34(sk); + slave = slaves->arr[hash % count]; + + return slave->dev; +} + +static struct net_device *bond_sk_get_lower_dev(struct net_device *dev, + struct sock *sk) +{ + struct bonding *bond = netdev_priv(dev); + struct net_device *lower = NULL; + + rcu_read_lock(); + if (bond_sk_check(bond)) + lower = __bond_sk_get_lower_dev(bond, sk); + rcu_read_unlock(); + + return lower; +} + +#if IS_ENABLED(CONFIG_TLS_DEVICE) +static netdev_tx_t bond_tls_device_xmit(struct bonding *bond, struct sk_buff *skb, + struct net_device *dev) +{ + if (likely(bond_get_slave_by_dev(bond, tls_get_ctx(skb->sk)->netdev))) + return bond_dev_queue_xmit(bond, skb, tls_get_ctx(skb->sk)->netdev); + return bond_tx_drop(dev, skb); +} +#endif + static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct bonding *bond = netdev_priv(dev); @@ -4555,6 +4705,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev !bond_slave_override(bond, skb)) return NETDEV_TX_OK; +#if IS_ENABLED(CONFIG_TLS_DEVICE) + if (skb->sk && tls_is_sk_tx_device_offloaded(skb->sk)) + return bond_tls_device_xmit(bond, skb, dev); +#endif + switch (BOND_MODE(bond)) { case BOND_MODE_ROUNDROBIN: return bond_xmit_roundrobin(skb, dev); @@ -4683,6 +4838,7 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_fix_features = bond_fix_features, .ndo_features_check = passthru_features_check, .ndo_get_xmit_slave = bond_xmit_get_slave, + .ndo_sk_get_lower_dev = bond_sk_get_lower_dev, }; static const struct device_type bond_type = { @@ -4754,6 +4910,10 @@ void bond_setup(struct net_device *bond_dev) if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) bond_dev->features |= BOND_XFRM_FEATURES; #endif /* CONFIG_XFRM_OFFLOAD */ +#if IS_ENABLED(CONFIG_TLS_DEVICE) + if (bond_sk_check(bond)) + bond_dev->features |= BOND_TLS_FEATURES; +#endif } /* Destroy a bonding device. diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index a4e4e15f574d..77d7c38bd435 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -96,12 +96,13 @@ static const struct bond_opt_value bond_pps_tbl[] = { }; static const struct bond_opt_value bond_xmit_hashtype_tbl[] = { - { "layer2", BOND_XMIT_POLICY_LAYER2, BOND_VALFLAG_DEFAULT}, - { "layer3+4", BOND_XMIT_POLICY_LAYER34, 0}, - { "layer2+3", BOND_XMIT_POLICY_LAYER23, 0}, - { "encap2+3", BOND_XMIT_POLICY_ENCAP23, 0}, - { "encap3+4", BOND_XMIT_POLICY_ENCAP34, 0}, - { NULL, -1, 0}, + { "layer2", BOND_XMIT_POLICY_LAYER2, BOND_VALFLAG_DEFAULT}, + { "layer3+4", BOND_XMIT_POLICY_LAYER34, 0}, + { "layer2+3", BOND_XMIT_POLICY_LAYER23, 0}, + { "encap2+3", BOND_XMIT_POLICY_ENCAP23, 0}, + { "encap3+4", BOND_XMIT_POLICY_ENCAP34, 0}, + { "vlan+srcmac", BOND_XMIT_POLICY_VLAN_SRCMAC, 0}, + { NULL, -1, 0}, }; static const struct bond_opt_value bond_arp_validate_tbl[] = { @@ -745,17 +746,30 @@ const struct bond_option *bond_opt_get(unsigned int option) return &bond_opts[option]; } -static void bond_set_xfrm_features(struct net_device *bond_dev, u64 mode) +static bool bond_set_xfrm_features(struct bonding *bond) { if (!IS_ENABLED(CONFIG_XFRM_OFFLOAD)) - return; + return false; + + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) + bond->dev->wanted_features |= BOND_XFRM_FEATURES; + else + bond->dev->wanted_features &= ~BOND_XFRM_FEATURES; + + return true; +} + +static bool bond_set_tls_features(struct bonding *bond) +{ + if (!IS_ENABLED(CONFIG_TLS_DEVICE)) + return false; - if (mode == BOND_MODE_ACTIVEBACKUP) - bond_dev->wanted_features |= BOND_XFRM_FEATURES; + if (bond_sk_check(bond)) + bond->dev->wanted_features |= BOND_TLS_FEATURES; else - bond_dev->wanted_features &= ~BOND_XFRM_FEATURES; + bond->dev->wanted_features &= ~BOND_TLS_FEATURES; - netdev_update_features(bond_dev); + return true; } static int bond_option_mode_set(struct bonding *bond, @@ -780,13 +794,20 @@ static int bond_option_mode_set(struct bonding *bond, if (newval->value == BOND_MODE_ALB) bond->params.tlb_dynamic_lb = 1; - if (bond->dev->reg_state == NETREG_REGISTERED) - bond_set_xfrm_features(bond->dev, newval->value); - /* don't cache arp_validate between modes */ bond->params.arp_validate = BOND_ARP_VALIDATE_NONE; bond->params.mode = newval->value; + if (bond->dev->reg_state == NETREG_REGISTERED) { + bool update = false; + + update |= bond_set_xfrm_features(bond); + update |= bond_set_tls_features(bond); + + if (update) + netdev_update_features(bond->dev); + } + return 0; } @@ -1219,6 +1240,10 @@ static int bond_option_xmit_hash_policy_set(struct bonding *bond, newval->string, newval->value); bond->params.xmit_policy = newval->value; + if (bond->dev->reg_state == NETREG_REGISTERED) + if (bond_set_tls_features(bond)) + netdev_update_features(bond->dev); + return 0; } diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 22164300122d..a2b4463d8480 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -7,12 +7,7 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o obj-$(CONFIG_CAN_VXCAN) += vxcan.o obj-$(CONFIG_CAN_SLCAN) += slcan.o -obj-$(CONFIG_CAN_DEV) += can-dev.o -can-dev-y += dev.o -can-dev-y += rx-offload.o - -can-dev-$(CONFIG_CAN_LEDS) += led.o - +obj-y += dev/ obj-y += rcar/ obj-y += spi/ obj-y += usb/ diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 5284f0ab3b06..9ad9b39f480e 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -484,7 +484,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_bytes += cf->len; /* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */ - can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv)); + can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv), 0); /* * we have to stop the queue and deliver all messages in case @@ -856,7 +856,7 @@ static void at91_irq_tx(struct net_device *dev, u32 reg_sr) if (likely(reg_msr & AT91_MSR_MRDY && ~reg_msr & AT91_MSR_MABT)) { /* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */ - can_get_echo_skb(dev, mb - get_mb_tx_first(priv)); + can_get_echo_skb(dev, mb - get_mb_tx_first(priv), NULL); dev->stats.tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); } diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 63f48b016ecd..ef474bae47a1 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -476,7 +476,7 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, */ c_can_setup_tx_object(dev, IF_TX, frame, idx); priv->dlc[idx] = frame->len; - can_put_echo_skb(skb, dev, idx); + can_put_echo_skb(skb, dev, idx, 0); /* Update the active bits */ atomic_add((1 << idx), &priv->tx_active); @@ -733,7 +733,7 @@ static void c_can_do_tx(struct net_device *dev) pend &= ~(1 << idx); obj = idx + C_CAN_MSG_OBJ_TX_FIRST; c_can_inval_tx_object(dev, IF_RX, obj); - can_get_echo_skb(dev, idx); + can_get_echo_skb(dev, idx, NULL); bytes += priv->dlc[idx]; pkts++; } diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c index 8d9f332c35e0..f8a130f594e2 100644 --- a/drivers/net/can/cc770/cc770.c +++ b/drivers/net/can/cc770/cc770.c @@ -702,8 +702,8 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o) stats->tx_bytes += cf->len; stats->tx_packets++; - can_put_echo_skb(priv->tx_skb, dev, 0); - can_get_echo_skb(dev, 0); + can_put_echo_skb(priv->tx_skb, dev, 0, 0); + can_get_echo_skb(dev, 0, NULL); priv->tx_skb = NULL; netif_wake_queue(dev); diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c deleted file mode 100644 index c73e2a65c904..000000000000 --- a/drivers/net/can/dev.c +++ /dev/null @@ -1,1338 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix - * Copyright (C) 2006 Andrey Volkov, Varma Electronics - * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/workqueue.h> -#include <linux/can.h> -#include <linux/can/can-ml.h> -#include <linux/can/dev.h> -#include <linux/can/skb.h> -#include <linux/can/netlink.h> -#include <linux/can/led.h> -#include <linux/of.h> -#include <net/rtnetlink.h> - -#define MOD_DESC "CAN device driver interface" - -MODULE_DESCRIPTION(MOD_DESC); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); - -/* CAN DLC to real data length conversion helpers */ - -static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, - 8, 12, 16, 20, 24, 32, 48, 64}; - -/* get data length from raw data length code (DLC) */ -u8 can_fd_dlc2len(u8 dlc) -{ - return dlc2len[dlc & 0x0F]; -} -EXPORT_SYMBOL_GPL(can_fd_dlc2len); - -static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */ - 9, 9, 9, 9, /* 9 - 12 */ - 10, 10, 10, 10, /* 13 - 16 */ - 11, 11, 11, 11, /* 17 - 20 */ - 12, 12, 12, 12, /* 21 - 24 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */ - 14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */ - 14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */ - 15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */ - -/* map the sanitized data length to an appropriate data length code */ -u8 can_fd_len2dlc(u8 len) -{ - if (unlikely(len > 64)) - return 0xF; - - return len2dlc[len]; -} -EXPORT_SYMBOL_GPL(can_fd_len2dlc); - -#ifdef CONFIG_CAN_CALC_BITTIMING -#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ - -/* Bit-timing calculation derived from: - * - * Code based on LinCAN sources and H8S2638 project - * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz - * Copyright 2005 Stanislav Marek - * email: pisa@cmp.felk.cvut.cz - * - * Calculates proper bit-timing parameters for a specified bit-rate - * and sample-point, which can then be used to set the bit-timing - * registers of the CAN controller. You can find more information - * in the header file linux/can/netlink.h. - */ -static int -can_update_sample_point(const struct can_bittiming_const *btc, - unsigned int sample_point_nominal, unsigned int tseg, - unsigned int *tseg1_ptr, unsigned int *tseg2_ptr, - unsigned int *sample_point_error_ptr) -{ - unsigned int sample_point_error, best_sample_point_error = UINT_MAX; - unsigned int sample_point, best_sample_point = 0; - unsigned int tseg1, tseg2; - int i; - - for (i = 0; i <= 1; i++) { - tseg2 = tseg + CAN_SYNC_SEG - - (sample_point_nominal * (tseg + CAN_SYNC_SEG)) / - 1000 - i; - tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max); - tseg1 = tseg - tseg2; - if (tseg1 > btc->tseg1_max) { - tseg1 = btc->tseg1_max; - tseg2 = tseg - tseg1; - } - - sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) / - (tseg + CAN_SYNC_SEG); - sample_point_error = abs(sample_point_nominal - sample_point); - - if (sample_point <= sample_point_nominal && - sample_point_error < best_sample_point_error) { - best_sample_point = sample_point; - best_sample_point_error = sample_point_error; - *tseg1_ptr = tseg1; - *tseg2_ptr = tseg2; - } - } - - if (sample_point_error_ptr) - *sample_point_error_ptr = best_sample_point_error; - - return best_sample_point; -} - -static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, - const struct can_bittiming_const *btc) -{ - struct can_priv *priv = netdev_priv(dev); - unsigned int bitrate; /* current bitrate */ - unsigned int bitrate_error; /* difference between current and nominal value */ - unsigned int best_bitrate_error = UINT_MAX; - unsigned int sample_point_error; /* difference between current and nominal value */ - unsigned int best_sample_point_error = UINT_MAX; - unsigned int sample_point_nominal; /* nominal sample point */ - unsigned int best_tseg = 0; /* current best value for tseg */ - unsigned int best_brp = 0; /* current best value for brp */ - unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0; - u64 v64; - - /* Use CiA recommended sample points */ - if (bt->sample_point) { - sample_point_nominal = bt->sample_point; - } else { - if (bt->bitrate > 800000) - sample_point_nominal = 750; - else if (bt->bitrate > 500000) - sample_point_nominal = 800; - else - sample_point_nominal = 875; - } - - /* tseg even = round down, odd = round up */ - for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1; - tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) { - tsegall = CAN_SYNC_SEG + tseg / 2; - - /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ - brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2; - - /* choose brp step which is possible in system */ - brp = (brp / btc->brp_inc) * btc->brp_inc; - if (brp < btc->brp_min || brp > btc->brp_max) - continue; - - bitrate = priv->clock.freq / (brp * tsegall); - bitrate_error = abs(bt->bitrate - bitrate); - - /* tseg brp biterror */ - if (bitrate_error > best_bitrate_error) - continue; - - /* reset sample point error if we have a better bitrate */ - if (bitrate_error < best_bitrate_error) - best_sample_point_error = UINT_MAX; - - can_update_sample_point(btc, sample_point_nominal, tseg / 2, - &tseg1, &tseg2, &sample_point_error); - if (sample_point_error > best_sample_point_error) - continue; - - best_sample_point_error = sample_point_error; - best_bitrate_error = bitrate_error; - best_tseg = tseg / 2; - best_brp = brp; - - if (bitrate_error == 0 && sample_point_error == 0) - break; - } - - if (best_bitrate_error) { - /* Error in one-tenth of a percent */ - v64 = (u64)best_bitrate_error * 1000; - do_div(v64, bt->bitrate); - bitrate_error = (u32)v64; - if (bitrate_error > CAN_CALC_MAX_ERROR) { - netdev_err(dev, - "bitrate error %d.%d%% too high\n", - bitrate_error / 10, bitrate_error % 10); - return -EDOM; - } - netdev_warn(dev, "bitrate error %d.%d%%\n", - bitrate_error / 10, bitrate_error % 10); - } - - /* real sample point */ - bt->sample_point = can_update_sample_point(btc, sample_point_nominal, - best_tseg, &tseg1, &tseg2, - NULL); - - v64 = (u64)best_brp * 1000 * 1000 * 1000; - do_div(v64, priv->clock.freq); - bt->tq = (u32)v64; - bt->prop_seg = tseg1 / 2; - bt->phase_seg1 = tseg1 - bt->prop_seg; - bt->phase_seg2 = tseg2; - - /* check for sjw user settings */ - if (!bt->sjw || !btc->sjw_max) { - bt->sjw = 1; - } else { - /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */ - if (bt->sjw > btc->sjw_max) - bt->sjw = btc->sjw_max; - /* bt->sjw must not be higher than tseg2 */ - if (tseg2 < bt->sjw) - bt->sjw = tseg2; - } - - bt->brp = best_brp; - - /* real bitrate */ - bt->bitrate = priv->clock.freq / - (bt->brp * (CAN_SYNC_SEG + tseg1 + tseg2)); - - return 0; -} -#else /* !CONFIG_CAN_CALC_BITTIMING */ -static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, - const struct can_bittiming_const *btc) -{ - netdev_err(dev, "bit-timing calculation not available\n"); - return -EINVAL; -} -#endif /* CONFIG_CAN_CALC_BITTIMING */ - -/* Checks the validity of the specified bit-timing parameters prop_seg, - * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate - * prescaler value brp. You can find more information in the header - * file linux/can/netlink.h. - */ -static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt, - const struct can_bittiming_const *btc) -{ - struct can_priv *priv = netdev_priv(dev); - int tseg1, alltseg; - u64 brp64; - - tseg1 = bt->prop_seg + bt->phase_seg1; - if (!bt->sjw) - bt->sjw = 1; - if (bt->sjw > btc->sjw_max || - tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max || - bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max) - return -ERANGE; - - brp64 = (u64)priv->clock.freq * (u64)bt->tq; - if (btc->brp_inc > 1) - do_div(brp64, btc->brp_inc); - brp64 += 500000000UL - 1; - do_div(brp64, 1000000000UL); /* the practicable BRP */ - if (btc->brp_inc > 1) - brp64 *= btc->brp_inc; - bt->brp = (u32)brp64; - - if (bt->brp < btc->brp_min || bt->brp > btc->brp_max) - return -EINVAL; - - alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1; - bt->bitrate = priv->clock.freq / (bt->brp * alltseg); - bt->sample_point = ((tseg1 + 1) * 1000) / alltseg; - - return 0; -} - -/* Checks the validity of predefined bitrate settings */ -static int -can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt, - const u32 *bitrate_const, - const unsigned int bitrate_const_cnt) -{ - struct can_priv *priv = netdev_priv(dev); - unsigned int i; - - for (i = 0; i < bitrate_const_cnt; i++) { - if (bt->bitrate == bitrate_const[i]) - break; - } - - if (i >= priv->bitrate_const_cnt) - return -EINVAL; - - return 0; -} - -static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt, - const struct can_bittiming_const *btc, - const u32 *bitrate_const, - const unsigned int bitrate_const_cnt) -{ - int err; - - /* Depending on the given can_bittiming parameter structure the CAN - * timing parameters are calculated based on the provided bitrate OR - * alternatively the CAN timing parameters (tq, prop_seg, etc.) are - * provided directly which are then checked and fixed up. - */ - if (!bt->tq && bt->bitrate && btc) - err = can_calc_bittiming(dev, bt, btc); - else if (bt->tq && !bt->bitrate && btc) - err = can_fixup_bittiming(dev, bt, btc); - else if (!bt->tq && bt->bitrate && bitrate_const) - err = can_validate_bitrate(dev, bt, bitrate_const, - bitrate_const_cnt); - else - err = -EINVAL; - - return err; -} - -static void can_update_state_error_stats(struct net_device *dev, - enum can_state new_state) -{ - struct can_priv *priv = netdev_priv(dev); - - if (new_state <= priv->state) - return; - - switch (new_state) { - case CAN_STATE_ERROR_WARNING: - priv->can_stats.error_warning++; - break; - case CAN_STATE_ERROR_PASSIVE: - priv->can_stats.error_passive++; - break; - case CAN_STATE_BUS_OFF: - priv->can_stats.bus_off++; - break; - default: - break; - } -} - -static int can_tx_state_to_frame(struct net_device *dev, enum can_state state) -{ - switch (state) { - case CAN_STATE_ERROR_ACTIVE: - return CAN_ERR_CRTL_ACTIVE; - case CAN_STATE_ERROR_WARNING: - return CAN_ERR_CRTL_TX_WARNING; - case CAN_STATE_ERROR_PASSIVE: - return CAN_ERR_CRTL_TX_PASSIVE; - default: - return 0; - } -} - -static int can_rx_state_to_frame(struct net_device *dev, enum can_state state) -{ - switch (state) { - case CAN_STATE_ERROR_ACTIVE: - return CAN_ERR_CRTL_ACTIVE; - case CAN_STATE_ERROR_WARNING: - return CAN_ERR_CRTL_RX_WARNING; - case CAN_STATE_ERROR_PASSIVE: - return CAN_ERR_CRTL_RX_PASSIVE; - default: - return 0; - } -} - -static const char *can_get_state_str(const enum can_state state) -{ - switch (state) { - case CAN_STATE_ERROR_ACTIVE: - return "Error Active"; - case CAN_STATE_ERROR_WARNING: - return "Error Warning"; - case CAN_STATE_ERROR_PASSIVE: - return "Error Passive"; - case CAN_STATE_BUS_OFF: - return "Bus Off"; - case CAN_STATE_STOPPED: - return "Stopped"; - case CAN_STATE_SLEEPING: - return "Sleeping"; - default: - return "<unknown>"; - } - - return "<unknown>"; -} - -void can_change_state(struct net_device *dev, struct can_frame *cf, - enum can_state tx_state, enum can_state rx_state) -{ - struct can_priv *priv = netdev_priv(dev); - enum can_state new_state = max(tx_state, rx_state); - - if (unlikely(new_state == priv->state)) { - netdev_warn(dev, "%s: oops, state did not change", __func__); - return; - } - - netdev_dbg(dev, "Controller changed from %s State (%d) into %s State (%d).\n", - can_get_state_str(priv->state), priv->state, - can_get_state_str(new_state), new_state); - - can_update_state_error_stats(dev, new_state); - priv->state = new_state; - - if (!cf) - return; - - if (unlikely(new_state == CAN_STATE_BUS_OFF)) { - cf->can_id |= CAN_ERR_BUSOFF; - return; - } - - cf->can_id |= CAN_ERR_CRTL; - cf->data[1] |= tx_state >= rx_state ? - can_tx_state_to_frame(dev, tx_state) : 0; - cf->data[1] |= tx_state <= rx_state ? - can_rx_state_to_frame(dev, rx_state) : 0; -} -EXPORT_SYMBOL_GPL(can_change_state); - -/* Local echo of CAN messages - * - * CAN network devices *should* support a local echo functionality - * (see Documentation/networking/can.rst). To test the handling of CAN - * interfaces that do not support the local echo both driver types are - * implemented. In the case that the driver does not support the echo - * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core - * to perform the echo as a fallback solution. - */ -static void can_flush_echo_skb(struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; - int i; - - for (i = 0; i < priv->echo_skb_max; i++) { - if (priv->echo_skb[i]) { - kfree_skb(priv->echo_skb[i]); - priv->echo_skb[i] = NULL; - stats->tx_dropped++; - stats->tx_aborted_errors++; - } - } -} - -/* Put the skb on the stack to be looped backed locally lateron - * - * The function is typically called in the start_xmit function - * of the device driver. The driver must protect access to - * priv->echo_skb, if necessary. - */ -int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, - unsigned int idx) -{ - struct can_priv *priv = netdev_priv(dev); - - BUG_ON(idx >= priv->echo_skb_max); - - /* check flag whether this packet has to be looped back */ - if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK || - (skb->protocol != htons(ETH_P_CAN) && - skb->protocol != htons(ETH_P_CANFD))) { - kfree_skb(skb); - return 0; - } - - if (!priv->echo_skb[idx]) { - skb = can_create_echo_skb(skb); - if (!skb) - return -ENOMEM; - - /* make settings for echo to reduce code in irq context */ - skb->pkt_type = PACKET_BROADCAST; - skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->dev = dev; - - /* save this skb for tx interrupt echo handling */ - priv->echo_skb[idx] = skb; - } else { - /* locking problem with netif_stop_queue() ?? */ - netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx); - kfree_skb(skb); - return -EBUSY; - } - - return 0; -} -EXPORT_SYMBOL_GPL(can_put_echo_skb); - -struct sk_buff * -__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr) -{ - struct can_priv *priv = netdev_priv(dev); - - if (idx >= priv->echo_skb_max) { - netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n", - __func__, idx, priv->echo_skb_max); - return NULL; - } - - if (priv->echo_skb[idx]) { - /* Using "struct canfd_frame::len" for the frame - * length is supported on both CAN and CANFD frames. - */ - struct sk_buff *skb = priv->echo_skb[idx]; - struct canfd_frame *cf = (struct canfd_frame *)skb->data; - - /* get the real payload length for netdev statistics */ - if (cf->can_id & CAN_RTR_FLAG) - *len_ptr = 0; - else - *len_ptr = cf->len; - - priv->echo_skb[idx] = NULL; - - return skb; - } - - return NULL; -} - -/* Get the skb from the stack and loop it back locally - * - * The function is typically called when the TX done interrupt - * is handled in the device driver. The driver must protect - * access to priv->echo_skb, if necessary. - */ -unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx) -{ - struct sk_buff *skb; - u8 len; - - skb = __can_get_echo_skb(dev, idx, &len); - if (!skb) - return 0; - - skb_get(skb); - if (netif_rx(skb) == NET_RX_SUCCESS) - dev_consume_skb_any(skb); - else - dev_kfree_skb_any(skb); - - return len; -} -EXPORT_SYMBOL_GPL(can_get_echo_skb); - -/* Remove the skb from the stack and free it. - * - * The function is typically called when TX failed. - */ -void can_free_echo_skb(struct net_device *dev, unsigned int idx) -{ - struct can_priv *priv = netdev_priv(dev); - - BUG_ON(idx >= priv->echo_skb_max); - - if (priv->echo_skb[idx]) { - dev_kfree_skb_any(priv->echo_skb[idx]); - priv->echo_skb[idx] = NULL; - } -} -EXPORT_SYMBOL_GPL(can_free_echo_skb); - -/* CAN device restart for bus-off recovery */ -static void can_restart(struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; - struct sk_buff *skb; - struct can_frame *cf; - int err; - - BUG_ON(netif_carrier_ok(dev)); - - /* No synchronization needed because the device is bus-off and - * no messages can come in or go out. - */ - can_flush_echo_skb(dev); - - /* send restart message upstream */ - skb = alloc_can_err_skb(dev, &cf); - if (!skb) - goto restart; - - cf->can_id |= CAN_ERR_RESTARTED; - - stats->rx_packets++; - stats->rx_bytes += cf->len; - - netif_rx_ni(skb); - -restart: - netdev_dbg(dev, "restarted\n"); - priv->can_stats.restarts++; - - /* Now restart the device */ - err = priv->do_set_mode(dev, CAN_MODE_START); - - netif_carrier_on(dev); - if (err) - netdev_err(dev, "Error %d during restart", err); -} - -static void can_restart_work(struct work_struct *work) -{ - struct delayed_work *dwork = to_delayed_work(work); - struct can_priv *priv = container_of(dwork, struct can_priv, - restart_work); - - can_restart(priv->dev); -} - -int can_restart_now(struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - - /* A manual restart is only permitted if automatic restart is - * disabled and the device is in the bus-off state - */ - if (priv->restart_ms) - return -EINVAL; - if (priv->state != CAN_STATE_BUS_OFF) - return -EBUSY; - - cancel_delayed_work_sync(&priv->restart_work); - can_restart(dev); - - return 0; -} - -/* CAN bus-off - * - * This functions should be called when the device goes bus-off to - * tell the netif layer that no more packets can be sent or received. - * If enabled, a timer is started to trigger bus-off recovery. - */ -void can_bus_off(struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - - if (priv->restart_ms) - netdev_info(dev, "bus-off, scheduling restart in %d ms\n", - priv->restart_ms); - else - netdev_info(dev, "bus-off\n"); - - netif_carrier_off(dev); - - if (priv->restart_ms) - schedule_delayed_work(&priv->restart_work, - msecs_to_jiffies(priv->restart_ms)); -} -EXPORT_SYMBOL_GPL(can_bus_off); - -static void can_setup(struct net_device *dev) -{ - dev->type = ARPHRD_CAN; - dev->mtu = CAN_MTU; - dev->hard_header_len = 0; - dev->addr_len = 0; - dev->tx_queue_len = 10; - - /* New-style flags. */ - dev->flags = IFF_NOARP; - dev->features = NETIF_F_HW_CSUM; -} - -struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) -{ - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + - sizeof(struct can_frame)); - if (unlikely(!skb)) - return NULL; - - skb->protocol = htons(ETH_P_CAN); - skb->pkt_type = PACKET_BROADCAST; - skb->ip_summed = CHECKSUM_UNNECESSARY; - - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - - can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; - - *cf = skb_put_zero(skb, sizeof(struct can_frame)); - - return skb; -} -EXPORT_SYMBOL_GPL(alloc_can_skb); - -struct sk_buff *alloc_canfd_skb(struct net_device *dev, - struct canfd_frame **cfd) -{ - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + - sizeof(struct canfd_frame)); - if (unlikely(!skb)) - return NULL; - - skb->protocol = htons(ETH_P_CANFD); - skb->pkt_type = PACKET_BROADCAST; - skb->ip_summed = CHECKSUM_UNNECESSARY; - - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - - can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; - - *cfd = skb_put_zero(skb, sizeof(struct canfd_frame)); - - return skb; -} -EXPORT_SYMBOL_GPL(alloc_canfd_skb); - -struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf) -{ - struct sk_buff *skb; - - skb = alloc_can_skb(dev, cf); - if (unlikely(!skb)) - return NULL; - - (*cf)->can_id = CAN_ERR_FLAG; - (*cf)->len = CAN_ERR_DLC; - - return skb; -} -EXPORT_SYMBOL_GPL(alloc_can_err_skb); - -/* Allocate and setup space for the CAN network device */ -struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max, - unsigned int txqs, unsigned int rxqs) -{ - struct net_device *dev; - struct can_priv *priv; - int size; - - /* We put the driver's priv, the CAN mid layer priv and the - * echo skb into the netdevice's priv. The memory layout for - * the netdev_priv is like this: - * - * +-------------------------+ - * | driver's priv | - * +-------------------------+ - * | struct can_ml_priv | - * +-------------------------+ - * | array of struct sk_buff | - * +-------------------------+ - */ - - size = ALIGN(sizeof_priv, NETDEV_ALIGN) + sizeof(struct can_ml_priv); - - if (echo_skb_max) - size = ALIGN(size, sizeof(struct sk_buff *)) + - echo_skb_max * sizeof(struct sk_buff *); - - dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup, - txqs, rxqs); - if (!dev) - return NULL; - - priv = netdev_priv(dev); - priv->dev = dev; - - dev->ml_priv = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN); - - if (echo_skb_max) { - priv->echo_skb_max = echo_skb_max; - priv->echo_skb = (void *)priv + - (size - echo_skb_max * sizeof(struct sk_buff *)); - } - - priv->state = CAN_STATE_STOPPED; - - INIT_DELAYED_WORK(&priv->restart_work, can_restart_work); - - return dev; -} -EXPORT_SYMBOL_GPL(alloc_candev_mqs); - -/* Free space of the CAN network device */ -void free_candev(struct net_device *dev) -{ - free_netdev(dev); -} -EXPORT_SYMBOL_GPL(free_candev); - -/* changing MTU and control mode for CAN/CANFD devices */ -int can_change_mtu(struct net_device *dev, int new_mtu) -{ - struct can_priv *priv = netdev_priv(dev); - - /* Do not allow changing the MTU while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - - /* allow change of MTU according to the CANFD ability of the device */ - switch (new_mtu) { - case CAN_MTU: - /* 'CANFD-only' controllers can not switch to CAN_MTU */ - if (priv->ctrlmode_static & CAN_CTRLMODE_FD) - return -EINVAL; - - priv->ctrlmode &= ~CAN_CTRLMODE_FD; - break; - - case CANFD_MTU: - /* check for potential CANFD ability */ - if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) && - !(priv->ctrlmode_static & CAN_CTRLMODE_FD)) - return -EINVAL; - - priv->ctrlmode |= CAN_CTRLMODE_FD; - break; - - default: - return -EINVAL; - } - - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL_GPL(can_change_mtu); - -/* Common open function when the device gets opened. - * - * This function should be called in the open function of the device - * driver. - */ -int open_candev(struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - - if (!priv->bittiming.bitrate) { - netdev_err(dev, "bit-timing not yet defined\n"); - return -EINVAL; - } - - /* For CAN FD the data bitrate has to be >= the arbitration bitrate */ - if ((priv->ctrlmode & CAN_CTRLMODE_FD) && - (!priv->data_bittiming.bitrate || - priv->data_bittiming.bitrate < priv->bittiming.bitrate)) { - netdev_err(dev, "incorrect/missing data bit-timing\n"); - return -EINVAL; - } - - /* Switch carrier on if device was stopped while in bus-off state */ - if (!netif_carrier_ok(dev)) - netif_carrier_on(dev); - - return 0; -} -EXPORT_SYMBOL_GPL(open_candev); - -#ifdef CONFIG_OF -/* Common function that can be used to understand the limitation of - * a transceiver when it provides no means to determine these limitations - * at runtime. - */ -void of_can_transceiver(struct net_device *dev) -{ - struct device_node *dn; - struct can_priv *priv = netdev_priv(dev); - struct device_node *np = dev->dev.parent->of_node; - int ret; - - dn = of_get_child_by_name(np, "can-transceiver"); - if (!dn) - return; - - ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max); - of_node_put(dn); - if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max)) - netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n"); -} -EXPORT_SYMBOL_GPL(of_can_transceiver); -#endif - -/* Common close function for cleanup before the device gets closed. - * - * This function should be called in the close function of the device - * driver. - */ -void close_candev(struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - - cancel_delayed_work_sync(&priv->restart_work); - can_flush_echo_skb(dev); -} -EXPORT_SYMBOL_GPL(close_candev); - -/* CAN netlink interface */ -static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { - [IFLA_CAN_STATE] = { .type = NLA_U32 }, - [IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) }, - [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 }, - [IFLA_CAN_RESTART] = { .type = NLA_U32 }, - [IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) }, - [IFLA_CAN_BITTIMING_CONST] - = { .len = sizeof(struct can_bittiming_const) }, - [IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) }, - [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) }, - [IFLA_CAN_DATA_BITTIMING] - = { .len = sizeof(struct can_bittiming) }, - [IFLA_CAN_DATA_BITTIMING_CONST] - = { .len = sizeof(struct can_bittiming_const) }, - [IFLA_CAN_TERMINATION] = { .type = NLA_U16 }, -}; - -static int can_validate(struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - bool is_can_fd = false; - - /* Make sure that valid CAN FD configurations always consist of - * - nominal/arbitration bittiming - * - data bittiming - * - control mode with CAN_CTRLMODE_FD set - */ - - if (!data) - return 0; - - if (data[IFLA_CAN_CTRLMODE]) { - struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); - - is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD; - } - - if (is_can_fd) { - if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING]) - return -EOPNOTSUPP; - } - - if (data[IFLA_CAN_DATA_BITTIMING]) { - if (!is_can_fd || !data[IFLA_CAN_BITTIMING]) - return -EOPNOTSUPP; - } - - return 0; -} - -static int can_changelink(struct net_device *dev, struct nlattr *tb[], - struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct can_priv *priv = netdev_priv(dev); - int err; - - /* We need synchronization with dev->stop() */ - ASSERT_RTNL(); - - if (data[IFLA_CAN_BITTIMING]) { - struct can_bittiming bt; - - /* Do not allow changing bittiming while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - - /* Calculate bittiming parameters based on - * bittiming_const if set, otherwise pass bitrate - * directly via do_set_bitrate(). Bail out if neither - * is given. - */ - if (!priv->bittiming_const && !priv->do_set_bittiming) - return -EOPNOTSUPP; - - memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt)); - err = can_get_bittiming(dev, &bt, - priv->bittiming_const, - priv->bitrate_const, - priv->bitrate_const_cnt); - if (err) - return err; - - if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) { - netdev_err(dev, "arbitration bitrate surpasses transceiver capabilities of %d bps\n", - priv->bitrate_max); - return -EINVAL; - } - - memcpy(&priv->bittiming, &bt, sizeof(bt)); - - if (priv->do_set_bittiming) { - /* Finally, set the bit-timing registers */ - err = priv->do_set_bittiming(dev); - if (err) - return err; - } - } - - if (data[IFLA_CAN_CTRLMODE]) { - struct can_ctrlmode *cm; - u32 ctrlstatic; - u32 maskedflags; - - /* Do not allow changing controller mode while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - cm = nla_data(data[IFLA_CAN_CTRLMODE]); - ctrlstatic = priv->ctrlmode_static; - maskedflags = cm->flags & cm->mask; - - /* check whether provided bits are allowed to be passed */ - if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic)) - return -EOPNOTSUPP; - - /* do not check for static fd-non-iso if 'fd' is disabled */ - if (!(maskedflags & CAN_CTRLMODE_FD)) - ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO; - - /* make sure static options are provided by configuration */ - if ((maskedflags & ctrlstatic) != ctrlstatic) - return -EOPNOTSUPP; - - /* clear bits to be modified and copy the flag values */ - priv->ctrlmode &= ~cm->mask; - priv->ctrlmode |= maskedflags; - - /* CAN_CTRLMODE_FD can only be set when driver supports FD */ - if (priv->ctrlmode & CAN_CTRLMODE_FD) - dev->mtu = CANFD_MTU; - else - dev->mtu = CAN_MTU; - } - - if (data[IFLA_CAN_RESTART_MS]) { - /* Do not allow changing restart delay while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]); - } - - if (data[IFLA_CAN_RESTART]) { - /* Do not allow a restart while not running */ - if (!(dev->flags & IFF_UP)) - return -EINVAL; - err = can_restart_now(dev); - if (err) - return err; - } - - if (data[IFLA_CAN_DATA_BITTIMING]) { - struct can_bittiming dbt; - - /* Do not allow changing bittiming while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - - /* Calculate bittiming parameters based on - * data_bittiming_const if set, otherwise pass bitrate - * directly via do_set_bitrate(). Bail out if neither - * is given. - */ - if (!priv->data_bittiming_const && !priv->do_set_data_bittiming) - return -EOPNOTSUPP; - - memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]), - sizeof(dbt)); - err = can_get_bittiming(dev, &dbt, - priv->data_bittiming_const, - priv->data_bitrate_const, - priv->data_bitrate_const_cnt); - if (err) - return err; - - if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) { - netdev_err(dev, "canfd data bitrate surpasses transceiver capabilities of %d bps\n", - priv->bitrate_max); - return -EINVAL; - } - - memcpy(&priv->data_bittiming, &dbt, sizeof(dbt)); - - if (priv->do_set_data_bittiming) { - /* Finally, set the bit-timing registers */ - err = priv->do_set_data_bittiming(dev); - if (err) - return err; - } - } - - if (data[IFLA_CAN_TERMINATION]) { - const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]); - const unsigned int num_term = priv->termination_const_cnt; - unsigned int i; - - if (!priv->do_set_termination) - return -EOPNOTSUPP; - - /* check whether given value is supported by the interface */ - for (i = 0; i < num_term; i++) { - if (termval == priv->termination_const[i]) - break; - } - if (i >= num_term) - return -EINVAL; - - /* Finally, set the termination value */ - err = priv->do_set_termination(dev, termval); - if (err) - return err; - - priv->termination = termval; - } - - return 0; -} - -static size_t can_get_size(const struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - size_t size = 0; - - if (priv->bittiming.bitrate) /* IFLA_CAN_BITTIMING */ - size += nla_total_size(sizeof(struct can_bittiming)); - if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ - size += nla_total_size(sizeof(struct can_bittiming_const)); - size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */ - size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ - size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */ - size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ - if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ - size += nla_total_size(sizeof(struct can_berr_counter)); - if (priv->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */ - size += nla_total_size(sizeof(struct can_bittiming)); - if (priv->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */ - size += nla_total_size(sizeof(struct can_bittiming_const)); - if (priv->termination_const) { - size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */ - size += nla_total_size(sizeof(*priv->termination_const) * /* IFLA_CAN_TERMINATION_CONST */ - priv->termination_const_cnt); - } - if (priv->bitrate_const) /* IFLA_CAN_BITRATE_CONST */ - size += nla_total_size(sizeof(*priv->bitrate_const) * - priv->bitrate_const_cnt); - if (priv->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */ - size += nla_total_size(sizeof(*priv->data_bitrate_const) * - priv->data_bitrate_const_cnt); - size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */ - - return size; -} - -static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - struct can_ctrlmode cm = {.flags = priv->ctrlmode}; - struct can_berr_counter bec = { }; - enum can_state state = priv->state; - - if (priv->do_get_state) - priv->do_get_state(dev, &state); - - if ((priv->bittiming.bitrate && - nla_put(skb, IFLA_CAN_BITTIMING, - sizeof(priv->bittiming), &priv->bittiming)) || - - (priv->bittiming_const && - nla_put(skb, IFLA_CAN_BITTIMING_CONST, - sizeof(*priv->bittiming_const), priv->bittiming_const)) || - - nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) || - nla_put_u32(skb, IFLA_CAN_STATE, state) || - nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) || - nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) || - - (priv->do_get_berr_counter && - !priv->do_get_berr_counter(dev, &bec) && - nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) || - - (priv->data_bittiming.bitrate && - nla_put(skb, IFLA_CAN_DATA_BITTIMING, - sizeof(priv->data_bittiming), &priv->data_bittiming)) || - - (priv->data_bittiming_const && - nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST, - sizeof(*priv->data_bittiming_const), - priv->data_bittiming_const)) || - - (priv->termination_const && - (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) || - nla_put(skb, IFLA_CAN_TERMINATION_CONST, - sizeof(*priv->termination_const) * - priv->termination_const_cnt, - priv->termination_const))) || - - (priv->bitrate_const && - nla_put(skb, IFLA_CAN_BITRATE_CONST, - sizeof(*priv->bitrate_const) * - priv->bitrate_const_cnt, - priv->bitrate_const)) || - - (priv->data_bitrate_const && - nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST, - sizeof(*priv->data_bitrate_const) * - priv->data_bitrate_const_cnt, - priv->data_bitrate_const)) || - - (nla_put(skb, IFLA_CAN_BITRATE_MAX, - sizeof(priv->bitrate_max), - &priv->bitrate_max)) - ) - - return -EMSGSIZE; - - return 0; -} - -static size_t can_get_xstats_size(const struct net_device *dev) -{ - return sizeof(struct can_device_stats); -} - -static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - - if (nla_put(skb, IFLA_INFO_XSTATS, - sizeof(priv->can_stats), &priv->can_stats)) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -EMSGSIZE; -} - -static int can_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - return -EOPNOTSUPP; -} - -static void can_dellink(struct net_device *dev, struct list_head *head) -{ -} - -static struct rtnl_link_ops can_link_ops __read_mostly = { - .kind = "can", - .maxtype = IFLA_CAN_MAX, - .policy = can_policy, - .setup = can_setup, - .validate = can_validate, - .newlink = can_newlink, - .changelink = can_changelink, - .dellink = can_dellink, - .get_size = can_get_size, - .fill_info = can_fill_info, - .get_xstats_size = can_get_xstats_size, - .fill_xstats = can_fill_xstats, -}; - -/* Register the CAN network device */ -int register_candev(struct net_device *dev) -{ - struct can_priv *priv = netdev_priv(dev); - - /* Ensure termination_const, termination_const_cnt and - * do_set_termination consistency. All must be either set or - * unset. - */ - if ((!priv->termination_const != !priv->termination_const_cnt) || - (!priv->termination_const != !priv->do_set_termination)) - return -EINVAL; - - if (!priv->bitrate_const != !priv->bitrate_const_cnt) - return -EINVAL; - - if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt) - return -EINVAL; - - dev->rtnl_link_ops = &can_link_ops; - netif_carrier_off(dev); - - return register_netdev(dev); -} -EXPORT_SYMBOL_GPL(register_candev); - -/* Unregister the CAN network device */ -void unregister_candev(struct net_device *dev) -{ - unregister_netdev(dev); -} -EXPORT_SYMBOL_GPL(unregister_candev); - -/* Test if a network device is a candev based device - * and return the can_priv* if so. - */ -struct can_priv *safe_candev_priv(struct net_device *dev) -{ - if (dev->type != ARPHRD_CAN || dev->rtnl_link_ops != &can_link_ops) - return NULL; - - return netdev_priv(dev); -} -EXPORT_SYMBOL_GPL(safe_candev_priv); - -static __init int can_dev_init(void) -{ - int err; - - can_led_notifier_init(); - - err = rtnl_link_register(&can_link_ops); - if (!err) - pr_info(MOD_DESC "\n"); - - return err; -} -module_init(can_dev_init); - -static __exit void can_dev_exit(void) -{ - rtnl_link_unregister(&can_link_ops); - - can_led_notifier_exit(); -} -module_exit(can_dev_exit); - -MODULE_ALIAS_RTNL_LINK("can"); diff --git a/drivers/net/can/dev/Makefile b/drivers/net/can/dev/Makefile new file mode 100644 index 000000000000..3e2e207861fc --- /dev/null +++ b/drivers/net/can/dev/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CAN_DEV) += can-dev.o +can-dev-y += bittiming.o +can-dev-y += dev.o +can-dev-y += length.o +can-dev-y += netlink.o +can-dev-y += rx-offload.o +can-dev-y += skb.o + +can-dev-$(CONFIG_CAN_LEDS) += led.o diff --git a/drivers/net/can/dev/bittiming.c b/drivers/net/can/dev/bittiming.c new file mode 100644 index 000000000000..f7fe226bb395 --- /dev/null +++ b/drivers/net/can/dev/bittiming.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix + * Copyright (C) 2006 Andrey Volkov, Varma Electronics + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + */ + +#include <linux/can/dev.h> + +#ifdef CONFIG_CAN_CALC_BITTIMING +#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ + +/* Bit-timing calculation derived from: + * + * Code based on LinCAN sources and H8S2638 project + * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz + * Copyright 2005 Stanislav Marek + * email: pisa@cmp.felk.cvut.cz + * + * Calculates proper bit-timing parameters for a specified bit-rate + * and sample-point, which can then be used to set the bit-timing + * registers of the CAN controller. You can find more information + * in the header file linux/can/netlink.h. + */ +static int +can_update_sample_point(const struct can_bittiming_const *btc, + unsigned int sample_point_nominal, unsigned int tseg, + unsigned int *tseg1_ptr, unsigned int *tseg2_ptr, + unsigned int *sample_point_error_ptr) +{ + unsigned int sample_point_error, best_sample_point_error = UINT_MAX; + unsigned int sample_point, best_sample_point = 0; + unsigned int tseg1, tseg2; + int i; + + for (i = 0; i <= 1; i++) { + tseg2 = tseg + CAN_SYNC_SEG - + (sample_point_nominal * (tseg + CAN_SYNC_SEG)) / + 1000 - i; + tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max); + tseg1 = tseg - tseg2; + if (tseg1 > btc->tseg1_max) { + tseg1 = btc->tseg1_max; + tseg2 = tseg - tseg1; + } + + sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) / + (tseg + CAN_SYNC_SEG); + sample_point_error = abs(sample_point_nominal - sample_point); + + if (sample_point <= sample_point_nominal && + sample_point_error < best_sample_point_error) { + best_sample_point = sample_point; + best_sample_point_error = sample_point_error; + *tseg1_ptr = tseg1; + *tseg2_ptr = tseg2; + } + } + + if (sample_point_error_ptr) + *sample_point_error_ptr = best_sample_point_error; + + return best_sample_point; +} + +int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc) +{ + struct can_priv *priv = netdev_priv(dev); + unsigned int bitrate; /* current bitrate */ + unsigned int bitrate_error; /* difference between current and nominal value */ + unsigned int best_bitrate_error = UINT_MAX; + unsigned int sample_point_error; /* difference between current and nominal value */ + unsigned int best_sample_point_error = UINT_MAX; + unsigned int sample_point_nominal; /* nominal sample point */ + unsigned int best_tseg = 0; /* current best value for tseg */ + unsigned int best_brp = 0; /* current best value for brp */ + unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0; + u64 v64; + + /* Use CiA recommended sample points */ + if (bt->sample_point) { + sample_point_nominal = bt->sample_point; + } else { + if (bt->bitrate > 800000) + sample_point_nominal = 750; + else if (bt->bitrate > 500000) + sample_point_nominal = 800; + else + sample_point_nominal = 875; + } + + /* tseg even = round down, odd = round up */ + for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1; + tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) { + tsegall = CAN_SYNC_SEG + tseg / 2; + + /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ + brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2; + + /* choose brp step which is possible in system */ + brp = (brp / btc->brp_inc) * btc->brp_inc; + if (brp < btc->brp_min || brp > btc->brp_max) + continue; + + bitrate = priv->clock.freq / (brp * tsegall); + bitrate_error = abs(bt->bitrate - bitrate); + + /* tseg brp biterror */ + if (bitrate_error > best_bitrate_error) + continue; + + /* reset sample point error if we have a better bitrate */ + if (bitrate_error < best_bitrate_error) + best_sample_point_error = UINT_MAX; + + can_update_sample_point(btc, sample_point_nominal, tseg / 2, + &tseg1, &tseg2, &sample_point_error); + if (sample_point_error > best_sample_point_error) + continue; + + best_sample_point_error = sample_point_error; + best_bitrate_error = bitrate_error; + best_tseg = tseg / 2; + best_brp = brp; + + if (bitrate_error == 0 && sample_point_error == 0) + break; + } + + if (best_bitrate_error) { + /* Error in one-tenth of a percent */ + v64 = (u64)best_bitrate_error * 1000; + do_div(v64, bt->bitrate); + bitrate_error = (u32)v64; + if (bitrate_error > CAN_CALC_MAX_ERROR) { + netdev_err(dev, + "bitrate error %d.%d%% too high\n", + bitrate_error / 10, bitrate_error % 10); + return -EDOM; + } + netdev_warn(dev, "bitrate error %d.%d%%\n", + bitrate_error / 10, bitrate_error % 10); + } + + /* real sample point */ + bt->sample_point = can_update_sample_point(btc, sample_point_nominal, + best_tseg, &tseg1, &tseg2, + NULL); + + v64 = (u64)best_brp * 1000 * 1000 * 1000; + do_div(v64, priv->clock.freq); + bt->tq = (u32)v64; + bt->prop_seg = tseg1 / 2; + bt->phase_seg1 = tseg1 - bt->prop_seg; + bt->phase_seg2 = tseg2; + + /* check for sjw user settings */ + if (!bt->sjw || !btc->sjw_max) { + bt->sjw = 1; + } else { + /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */ + if (bt->sjw > btc->sjw_max) + bt->sjw = btc->sjw_max; + /* bt->sjw must not be higher than tseg2 */ + if (tseg2 < bt->sjw) + bt->sjw = tseg2; + } + + bt->brp = best_brp; + + /* real bitrate */ + bt->bitrate = priv->clock.freq / + (bt->brp * (CAN_SYNC_SEG + tseg1 + tseg2)); + + return 0; +} +#endif /* CONFIG_CAN_CALC_BITTIMING */ + +/* Checks the validity of the specified bit-timing parameters prop_seg, + * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate + * prescaler value brp. You can find more information in the header + * file linux/can/netlink.h. + */ +static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc) +{ + struct can_priv *priv = netdev_priv(dev); + int tseg1, alltseg; + u64 brp64; + + tseg1 = bt->prop_seg + bt->phase_seg1; + if (!bt->sjw) + bt->sjw = 1; + if (bt->sjw > btc->sjw_max || + tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max || + bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max) + return -ERANGE; + + brp64 = (u64)priv->clock.freq * (u64)bt->tq; + if (btc->brp_inc > 1) + do_div(brp64, btc->brp_inc); + brp64 += 500000000UL - 1; + do_div(brp64, 1000000000UL); /* the practicable BRP */ + if (btc->brp_inc > 1) + brp64 *= btc->brp_inc; + bt->brp = (u32)brp64; + + if (bt->brp < btc->brp_min || bt->brp > btc->brp_max) + return -EINVAL; + + alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1; + bt->bitrate = priv->clock.freq / (bt->brp * alltseg); + bt->sample_point = ((tseg1 + 1) * 1000) / alltseg; + + return 0; +} + +/* Checks the validity of predefined bitrate settings */ +static int +can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt, + const u32 *bitrate_const, + const unsigned int bitrate_const_cnt) +{ + struct can_priv *priv = netdev_priv(dev); + unsigned int i; + + for (i = 0; i < bitrate_const_cnt; i++) { + if (bt->bitrate == bitrate_const[i]) + break; + } + + if (i >= priv->bitrate_const_cnt) + return -EINVAL; + + return 0; +} + +int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc, + const u32 *bitrate_const, + const unsigned int bitrate_const_cnt) +{ + int err; + + /* Depending on the given can_bittiming parameter structure the CAN + * timing parameters are calculated based on the provided bitrate OR + * alternatively the CAN timing parameters (tq, prop_seg, etc.) are + * provided directly which are then checked and fixed up. + */ + if (!bt->tq && bt->bitrate && btc) + err = can_calc_bittiming(dev, bt, btc); + else if (bt->tq && !bt->bitrate && btc) + err = can_fixup_bittiming(dev, bt, btc); + else if (!bt->tq && bt->bitrate && bitrate_const) + err = can_validate_bitrate(dev, bt, bitrate_const, + bitrate_const_cnt); + else + err = -EINVAL; + + return err; +} diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c new file mode 100644 index 000000000000..d9281ae853f8 --- /dev/null +++ b/drivers/net/can/dev/dev.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix + * Copyright (C) 2006 Andrey Volkov, Varma Electronics + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/workqueue.h> +#include <linux/can.h> +#include <linux/can/can-ml.h> +#include <linux/can/dev.h> +#include <linux/can/skb.h> +#include <linux/can/led.h> +#include <linux/of.h> + +#define MOD_DESC "CAN device driver interface" + +MODULE_DESCRIPTION(MOD_DESC); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); + +static void can_update_state_error_stats(struct net_device *dev, + enum can_state new_state) +{ + struct can_priv *priv = netdev_priv(dev); + + if (new_state <= priv->state) + return; + + switch (new_state) { + case CAN_STATE_ERROR_WARNING: + priv->can_stats.error_warning++; + break; + case CAN_STATE_ERROR_PASSIVE: + priv->can_stats.error_passive++; + break; + case CAN_STATE_BUS_OFF: + priv->can_stats.bus_off++; + break; + default: + break; + } +} + +static int can_tx_state_to_frame(struct net_device *dev, enum can_state state) +{ + switch (state) { + case CAN_STATE_ERROR_ACTIVE: + return CAN_ERR_CRTL_ACTIVE; + case CAN_STATE_ERROR_WARNING: + return CAN_ERR_CRTL_TX_WARNING; + case CAN_STATE_ERROR_PASSIVE: + return CAN_ERR_CRTL_TX_PASSIVE; + default: + return 0; + } +} + +static int can_rx_state_to_frame(struct net_device *dev, enum can_state state) +{ + switch (state) { + case CAN_STATE_ERROR_ACTIVE: + return CAN_ERR_CRTL_ACTIVE; + case CAN_STATE_ERROR_WARNING: + return CAN_ERR_CRTL_RX_WARNING; + case CAN_STATE_ERROR_PASSIVE: + return CAN_ERR_CRTL_RX_PASSIVE; + default: + return 0; + } +} + +const char *can_get_state_str(const enum can_state state) +{ + switch (state) { + case CAN_STATE_ERROR_ACTIVE: + return "Error Active"; + case CAN_STATE_ERROR_WARNING: + return "Error Warning"; + case CAN_STATE_ERROR_PASSIVE: + return "Error Passive"; + case CAN_STATE_BUS_OFF: + return "Bus Off"; + case CAN_STATE_STOPPED: + return "Stopped"; + case CAN_STATE_SLEEPING: + return "Sleeping"; + default: + return "<unknown>"; + } + + return "<unknown>"; +} +EXPORT_SYMBOL_GPL(can_get_state_str); + +void can_change_state(struct net_device *dev, struct can_frame *cf, + enum can_state tx_state, enum can_state rx_state) +{ + struct can_priv *priv = netdev_priv(dev); + enum can_state new_state = max(tx_state, rx_state); + + if (unlikely(new_state == priv->state)) { + netdev_warn(dev, "%s: oops, state did not change", __func__); + return; + } + + netdev_dbg(dev, "Controller changed from %s State (%d) into %s State (%d).\n", + can_get_state_str(priv->state), priv->state, + can_get_state_str(new_state), new_state); + + can_update_state_error_stats(dev, new_state); + priv->state = new_state; + + if (!cf) + return; + + if (unlikely(new_state == CAN_STATE_BUS_OFF)) { + cf->can_id |= CAN_ERR_BUSOFF; + return; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= tx_state >= rx_state ? + can_tx_state_to_frame(dev, tx_state) : 0; + cf->data[1] |= tx_state <= rx_state ? + can_rx_state_to_frame(dev, rx_state) : 0; +} +EXPORT_SYMBOL_GPL(can_change_state); + +/* CAN device restart for bus-off recovery */ +static void can_restart(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *cf; + int err; + + BUG_ON(netif_carrier_ok(dev)); + + /* No synchronization needed because the device is bus-off and + * no messages can come in or go out. + */ + can_flush_echo_skb(dev); + + /* send restart message upstream */ + skb = alloc_can_err_skb(dev, &cf); + if (!skb) + goto restart; + + cf->can_id |= CAN_ERR_RESTARTED; + + stats->rx_packets++; + stats->rx_bytes += cf->len; + + netif_rx_ni(skb); + +restart: + netdev_dbg(dev, "restarted\n"); + priv->can_stats.restarts++; + + /* Now restart the device */ + err = priv->do_set_mode(dev, CAN_MODE_START); + + netif_carrier_on(dev); + if (err) + netdev_err(dev, "Error %d during restart", err); +} + +static void can_restart_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct can_priv *priv = container_of(dwork, struct can_priv, + restart_work); + + can_restart(priv->dev); +} + +int can_restart_now(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + /* A manual restart is only permitted if automatic restart is + * disabled and the device is in the bus-off state + */ + if (priv->restart_ms) + return -EINVAL; + if (priv->state != CAN_STATE_BUS_OFF) + return -EBUSY; + + cancel_delayed_work_sync(&priv->restart_work); + can_restart(dev); + + return 0; +} + +/* CAN bus-off + * + * This functions should be called when the device goes bus-off to + * tell the netif layer that no more packets can be sent or received. + * If enabled, a timer is started to trigger bus-off recovery. + */ +void can_bus_off(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + if (priv->restart_ms) + netdev_info(dev, "bus-off, scheduling restart in %d ms\n", + priv->restart_ms); + else + netdev_info(dev, "bus-off\n"); + + netif_carrier_off(dev); + + if (priv->restart_ms) + schedule_delayed_work(&priv->restart_work, + msecs_to_jiffies(priv->restart_ms)); +} +EXPORT_SYMBOL_GPL(can_bus_off); + +void can_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = CAN_MTU; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 10; + + /* New-style flags. */ + dev->flags = IFF_NOARP; + dev->features = NETIF_F_HW_CSUM; +} + +/* Allocate and setup space for the CAN network device */ +struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max, + unsigned int txqs, unsigned int rxqs) +{ + struct net_device *dev; + struct can_priv *priv; + int size; + + /* We put the driver's priv, the CAN mid layer priv and the + * echo skb into the netdevice's priv. The memory layout for + * the netdev_priv is like this: + * + * +-------------------------+ + * | driver's priv | + * +-------------------------+ + * | struct can_ml_priv | + * +-------------------------+ + * | array of struct sk_buff | + * +-------------------------+ + */ + + size = ALIGN(sizeof_priv, NETDEV_ALIGN) + sizeof(struct can_ml_priv); + + if (echo_skb_max) + size = ALIGN(size, sizeof(struct sk_buff *)) + + echo_skb_max * sizeof(struct sk_buff *); + + dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup, + txqs, rxqs); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + priv->dev = dev; + + dev->ml_priv = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN); + + if (echo_skb_max) { + priv->echo_skb_max = echo_skb_max; + priv->echo_skb = (void *)priv + + (size - echo_skb_max * sizeof(struct sk_buff *)); + } + + priv->state = CAN_STATE_STOPPED; + + INIT_DELAYED_WORK(&priv->restart_work, can_restart_work); + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_candev_mqs); + +/* Free space of the CAN network device */ +void free_candev(struct net_device *dev) +{ + free_netdev(dev); +} +EXPORT_SYMBOL_GPL(free_candev); + +/* changing MTU and control mode for CAN/CANFD devices */ +int can_change_mtu(struct net_device *dev, int new_mtu) +{ + struct can_priv *priv = netdev_priv(dev); + + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + /* allow change of MTU according to the CANFD ability of the device */ + switch (new_mtu) { + case CAN_MTU: + /* 'CANFD-only' controllers can not switch to CAN_MTU */ + if (priv->ctrlmode_static & CAN_CTRLMODE_FD) + return -EINVAL; + + priv->ctrlmode &= ~CAN_CTRLMODE_FD; + break; + + case CANFD_MTU: + /* check for potential CANFD ability */ + if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) && + !(priv->ctrlmode_static & CAN_CTRLMODE_FD)) + return -EINVAL; + + priv->ctrlmode |= CAN_CTRLMODE_FD; + break; + + default: + return -EINVAL; + } + + dev->mtu = new_mtu; + return 0; +} +EXPORT_SYMBOL_GPL(can_change_mtu); + +/* Common open function when the device gets opened. + * + * This function should be called in the open function of the device + * driver. + */ +int open_candev(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + if (!priv->bittiming.bitrate) { + netdev_err(dev, "bit-timing not yet defined\n"); + return -EINVAL; + } + + /* For CAN FD the data bitrate has to be >= the arbitration bitrate */ + if ((priv->ctrlmode & CAN_CTRLMODE_FD) && + (!priv->data_bittiming.bitrate || + priv->data_bittiming.bitrate < priv->bittiming.bitrate)) { + netdev_err(dev, "incorrect/missing data bit-timing\n"); + return -EINVAL; + } + + /* Switch carrier on if device was stopped while in bus-off state */ + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(open_candev); + +#ifdef CONFIG_OF +/* Common function that can be used to understand the limitation of + * a transceiver when it provides no means to determine these limitations + * at runtime. + */ +void of_can_transceiver(struct net_device *dev) +{ + struct device_node *dn; + struct can_priv *priv = netdev_priv(dev); + struct device_node *np = dev->dev.parent->of_node; + int ret; + + dn = of_get_child_by_name(np, "can-transceiver"); + if (!dn) + return; + + ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max); + of_node_put(dn); + if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max)) + netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n"); +} +EXPORT_SYMBOL_GPL(of_can_transceiver); +#endif + +/* Common close function for cleanup before the device gets closed. + * + * This function should be called in the close function of the device + * driver. + */ +void close_candev(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + cancel_delayed_work_sync(&priv->restart_work); + can_flush_echo_skb(dev); +} +EXPORT_SYMBOL_GPL(close_candev); + +/* Register the CAN network device */ +int register_candev(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + /* Ensure termination_const, termination_const_cnt and + * do_set_termination consistency. All must be either set or + * unset. + */ + if ((!priv->termination_const != !priv->termination_const_cnt) || + (!priv->termination_const != !priv->do_set_termination)) + return -EINVAL; + + if (!priv->bitrate_const != !priv->bitrate_const_cnt) + return -EINVAL; + + if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt) + return -EINVAL; + + dev->rtnl_link_ops = &can_link_ops; + netif_carrier_off(dev); + + return register_netdev(dev); +} +EXPORT_SYMBOL_GPL(register_candev); + +/* Unregister the CAN network device */ +void unregister_candev(struct net_device *dev) +{ + unregister_netdev(dev); +} +EXPORT_SYMBOL_GPL(unregister_candev); + +/* Test if a network device is a candev based device + * and return the can_priv* if so. + */ +struct can_priv *safe_candev_priv(struct net_device *dev) +{ + if (dev->type != ARPHRD_CAN || dev->rtnl_link_ops != &can_link_ops) + return NULL; + + return netdev_priv(dev); +} +EXPORT_SYMBOL_GPL(safe_candev_priv); + +static __init int can_dev_init(void) +{ + int err; + + can_led_notifier_init(); + + err = can_netlink_register(); + if (!err) + pr_info(MOD_DESC "\n"); + + return err; +} +module_init(can_dev_init); + +static __exit void can_dev_exit(void) +{ + can_netlink_unregister(); + + can_led_notifier_exit(); +} +module_exit(can_dev_exit); + +MODULE_ALIAS_RTNL_LINK("can"); diff --git a/drivers/net/can/dev/length.c b/drivers/net/can/dev/length.c new file mode 100644 index 000000000000..b48140b1102e --- /dev/null +++ b/drivers/net/can/dev/length.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2012, 2020 Oliver Hartkopp <socketcan@hartkopp.net> + */ + +#include <linux/can/dev.h> + +/* CAN DLC to real data length conversion helpers */ + +static const u8 dlc2len[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 12, 16, 20, 24, 32, 48, 64 +}; + +/* get data length from raw data length code (DLC) */ +u8 can_fd_dlc2len(u8 dlc) +{ + return dlc2len[dlc & 0x0F]; +} +EXPORT_SYMBOL_GPL(can_fd_dlc2len); + +static const u8 len2dlc[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */ + 9, 9, 9, 9, /* 9 - 12 */ + 10, 10, 10, 10, /* 13 - 16 */ + 11, 11, 11, 11, /* 17 - 20 */ + 12, 12, 12, 12, /* 21 - 24 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */ + 15, 15, 15, 15, 15, 15, 15, 15 /* 57 - 64 */ +}; + +/* map the sanitized data length to an appropriate data length code */ +u8 can_fd_len2dlc(u8 len) +{ + /* check for length mapping table size at build time */ + BUILD_BUG_ON(ARRAY_SIZE(len2dlc) != CANFD_MAX_DLEN + 1); + + if (unlikely(len > CANFD_MAX_DLEN)) + return CANFD_MAX_DLC; + + return len2dlc[len]; +} +EXPORT_SYMBOL_GPL(can_fd_len2dlc); + +/** + * can_skb_get_frame_len() - Calculate the CAN Frame length in bytes + * of a given skb. + * @skb: socket buffer of a CAN message. + * + * Do a rough calculation: bit stuffing is ignored and length in bits + * is rounded up to a length in bytes. + * + * Rationale: this function is to be used for the BQL functions + * (netdev_sent_queue() and netdev_completed_queue()) which expect a + * value in bytes. Just using skb->len is insufficient because it will + * return the constant value of CAN(FD)_MTU. Doing the bit stuffing + * calculation would be too expensive in term of computing resources + * for no noticeable gain. + * + * Remarks: The payload of CAN FD frames with BRS flag are sent at a + * different bitrate. Currently, the can-utils canbusload tool does + * not support CAN-FD yet and so we could not run any benchmark to + * measure the impact. There might be possible improvement here. + * + * Return: length in bytes. + */ +unsigned int can_skb_get_frame_len(const struct sk_buff *skb) +{ + const struct canfd_frame *cf = (const struct canfd_frame *)skb->data; + u8 len; + + if (can_is_canfd_skb(skb)) + len = canfd_sanitize_len(cf->len); + else if (cf->can_id & CAN_RTR_FLAG) + len = 0; + else + len = cf->len; + + if (can_is_canfd_skb(skb)) { + if (cf->can_id & CAN_EFF_FLAG) + len += CANFD_FRAME_OVERHEAD_EFF; + else + len += CANFD_FRAME_OVERHEAD_SFF; + } else { + if (cf->can_id & CAN_EFF_FLAG) + len += CAN_FRAME_OVERHEAD_EFF; + else + len += CAN_FRAME_OVERHEAD_SFF; + } + + return len; +} +EXPORT_SYMBOL_GPL(can_skb_get_frame_len); diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c new file mode 100644 index 000000000000..867f6be31230 --- /dev/null +++ b/drivers/net/can/dev/netlink.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix + * Copyright (C) 2006 Andrey Volkov, Varma Electronics + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + */ + +#include <linux/can/dev.h> +#include <net/rtnetlink.h> + +static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { + [IFLA_CAN_STATE] = { .type = NLA_U32 }, + [IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) }, + [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 }, + [IFLA_CAN_RESTART] = { .type = NLA_U32 }, + [IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) }, + [IFLA_CAN_BITTIMING_CONST] + = { .len = sizeof(struct can_bittiming_const) }, + [IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) }, + [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) }, + [IFLA_CAN_DATA_BITTIMING] + = { .len = sizeof(struct can_bittiming) }, + [IFLA_CAN_DATA_BITTIMING_CONST] + = { .len = sizeof(struct can_bittiming_const) }, + [IFLA_CAN_TERMINATION] = { .type = NLA_U16 }, +}; + +static int can_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + bool is_can_fd = false; + + /* Make sure that valid CAN FD configurations always consist of + * - nominal/arbitration bittiming + * - data bittiming + * - control mode with CAN_CTRLMODE_FD set + */ + + if (!data) + return 0; + + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); + + is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD; + } + + if (is_can_fd) { + if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING]) + return -EOPNOTSUPP; + } + + if (data[IFLA_CAN_DATA_BITTIMING]) { + if (!is_can_fd || !data[IFLA_CAN_BITTIMING]) + return -EOPNOTSUPP; + } + + return 0; +} + +static int can_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct can_priv *priv = netdev_priv(dev); + int err; + + /* We need synchronization with dev->stop() */ + ASSERT_RTNL(); + + if (data[IFLA_CAN_BITTIMING]) { + struct can_bittiming bt; + + /* Do not allow changing bittiming while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + /* Calculate bittiming parameters based on + * bittiming_const if set, otherwise pass bitrate + * directly via do_set_bitrate(). Bail out if neither + * is given. + */ + if (!priv->bittiming_const && !priv->do_set_bittiming) + return -EOPNOTSUPP; + + memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt)); + err = can_get_bittiming(dev, &bt, + priv->bittiming_const, + priv->bitrate_const, + priv->bitrate_const_cnt); + if (err) + return err; + + if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) { + netdev_err(dev, "arbitration bitrate surpasses transceiver capabilities of %d bps\n", + priv->bitrate_max); + return -EINVAL; + } + + memcpy(&priv->bittiming, &bt, sizeof(bt)); + + if (priv->do_set_bittiming) { + /* Finally, set the bit-timing registers */ + err = priv->do_set_bittiming(dev); + if (err) + return err; + } + } + + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm; + u32 ctrlstatic; + u32 maskedflags; + + /* Do not allow changing controller mode while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + cm = nla_data(data[IFLA_CAN_CTRLMODE]); + ctrlstatic = priv->ctrlmode_static; + maskedflags = cm->flags & cm->mask; + + /* check whether provided bits are allowed to be passed */ + if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic)) + return -EOPNOTSUPP; + + /* do not check for static fd-non-iso if 'fd' is disabled */ + if (!(maskedflags & CAN_CTRLMODE_FD)) + ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO; + + /* make sure static options are provided by configuration */ + if ((maskedflags & ctrlstatic) != ctrlstatic) + return -EOPNOTSUPP; + + /* clear bits to be modified and copy the flag values */ + priv->ctrlmode &= ~cm->mask; + priv->ctrlmode |= maskedflags; + + /* CAN_CTRLMODE_FD can only be set when driver supports FD */ + if (priv->ctrlmode & CAN_CTRLMODE_FD) + dev->mtu = CANFD_MTU; + else + dev->mtu = CAN_MTU; + } + + if (data[IFLA_CAN_RESTART_MS]) { + /* Do not allow changing restart delay while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]); + } + + if (data[IFLA_CAN_RESTART]) { + /* Do not allow a restart while not running */ + if (!(dev->flags & IFF_UP)) + return -EINVAL; + err = can_restart_now(dev); + if (err) + return err; + } + + if (data[IFLA_CAN_DATA_BITTIMING]) { + struct can_bittiming dbt; + + /* Do not allow changing bittiming while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + /* Calculate bittiming parameters based on + * data_bittiming_const if set, otherwise pass bitrate + * directly via do_set_bitrate(). Bail out if neither + * is given. + */ + if (!priv->data_bittiming_const && !priv->do_set_data_bittiming) + return -EOPNOTSUPP; + + memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]), + sizeof(dbt)); + err = can_get_bittiming(dev, &dbt, + priv->data_bittiming_const, + priv->data_bitrate_const, + priv->data_bitrate_const_cnt); + if (err) + return err; + + if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) { + netdev_err(dev, "canfd data bitrate surpasses transceiver capabilities of %d bps\n", + priv->bitrate_max); + return -EINVAL; + } + + memcpy(&priv->data_bittiming, &dbt, sizeof(dbt)); + + if (priv->do_set_data_bittiming) { + /* Finally, set the bit-timing registers */ + err = priv->do_set_data_bittiming(dev); + if (err) + return err; + } + } + + if (data[IFLA_CAN_TERMINATION]) { + const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]); + const unsigned int num_term = priv->termination_const_cnt; + unsigned int i; + + if (!priv->do_set_termination) + return -EOPNOTSUPP; + + /* check whether given value is supported by the interface */ + for (i = 0; i < num_term; i++) { + if (termval == priv->termination_const[i]) + break; + } + if (i >= num_term) + return -EINVAL; + + /* Finally, set the termination value */ + err = priv->do_set_termination(dev, termval); + if (err) + return err; + + priv->termination = termval; + } + + return 0; +} + +static size_t can_get_size(const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + size_t size = 0; + + if (priv->bittiming.bitrate) /* IFLA_CAN_BITTIMING */ + size += nla_total_size(sizeof(struct can_bittiming)); + if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ + size += nla_total_size(sizeof(struct can_bittiming_const)); + size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ + size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ + if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ + size += nla_total_size(sizeof(struct can_berr_counter)); + if (priv->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */ + size += nla_total_size(sizeof(struct can_bittiming)); + if (priv->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */ + size += nla_total_size(sizeof(struct can_bittiming_const)); + if (priv->termination_const) { + size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */ + size += nla_total_size(sizeof(*priv->termination_const) * /* IFLA_CAN_TERMINATION_CONST */ + priv->termination_const_cnt); + } + if (priv->bitrate_const) /* IFLA_CAN_BITRATE_CONST */ + size += nla_total_size(sizeof(*priv->bitrate_const) * + priv->bitrate_const_cnt); + if (priv->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */ + size += nla_total_size(sizeof(*priv->data_bitrate_const) * + priv->data_bitrate_const_cnt); + size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */ + + return size; +} + +static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct can_ctrlmode cm = {.flags = priv->ctrlmode}; + struct can_berr_counter bec = { }; + enum can_state state = priv->state; + + if (priv->do_get_state) + priv->do_get_state(dev, &state); + + if ((priv->bittiming.bitrate && + nla_put(skb, IFLA_CAN_BITTIMING, + sizeof(priv->bittiming), &priv->bittiming)) || + + (priv->bittiming_const && + nla_put(skb, IFLA_CAN_BITTIMING_CONST, + sizeof(*priv->bittiming_const), priv->bittiming_const)) || + + nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) || + nla_put_u32(skb, IFLA_CAN_STATE, state) || + nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) || + nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) || + + (priv->do_get_berr_counter && + !priv->do_get_berr_counter(dev, &bec) && + nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) || + + (priv->data_bittiming.bitrate && + nla_put(skb, IFLA_CAN_DATA_BITTIMING, + sizeof(priv->data_bittiming), &priv->data_bittiming)) || + + (priv->data_bittiming_const && + nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST, + sizeof(*priv->data_bittiming_const), + priv->data_bittiming_const)) || + + (priv->termination_const && + (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) || + nla_put(skb, IFLA_CAN_TERMINATION_CONST, + sizeof(*priv->termination_const) * + priv->termination_const_cnt, + priv->termination_const))) || + + (priv->bitrate_const && + nla_put(skb, IFLA_CAN_BITRATE_CONST, + sizeof(*priv->bitrate_const) * + priv->bitrate_const_cnt, + priv->bitrate_const)) || + + (priv->data_bitrate_const && + nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST, + sizeof(*priv->data_bitrate_const) * + priv->data_bitrate_const_cnt, + priv->data_bitrate_const)) || + + (nla_put(skb, IFLA_CAN_BITRATE_MAX, + sizeof(priv->bitrate_max), + &priv->bitrate_max)) + ) + + return -EMSGSIZE; + + return 0; +} + +static size_t can_get_xstats_size(const struct net_device *dev) +{ + return sizeof(struct can_device_stats); +} + +static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + if (nla_put(skb, IFLA_INFO_XSTATS, + sizeof(priv->can_stats), &priv->can_stats)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static int can_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + return -EOPNOTSUPP; +} + +static void can_dellink(struct net_device *dev, struct list_head *head) +{ +} + +struct rtnl_link_ops can_link_ops __read_mostly = { + .kind = "can", + .maxtype = IFLA_CAN_MAX, + .policy = can_policy, + .setup = can_setup, + .validate = can_validate, + .newlink = can_newlink, + .changelink = can_changelink, + .dellink = can_dellink, + .get_size = can_get_size, + .fill_info = can_fill_info, + .get_xstats_size = can_get_xstats_size, + .fill_xstats = can_fill_xstats, +}; + +int can_netlink_register(void) +{ + return rtnl_link_register(&can_link_ops); +} + +void can_netlink_unregister(void) +{ + rtnl_link_unregister(&can_link_ops); +} diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/dev/rx-offload.c index 3c1912c0430b..ab2c1543786c 100644 --- a/drivers/net/can/rx-offload.c +++ b/drivers/net/can/dev/rx-offload.c @@ -263,7 +263,8 @@ int can_rx_offload_queue_sorted(struct can_rx_offload *offload, EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted); unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, - unsigned int idx, u32 timestamp) + unsigned int idx, u32 timestamp, + unsigned int *frame_len_ptr) { struct net_device *dev = offload->dev; struct net_device_stats *stats = &dev->stats; @@ -271,7 +272,7 @@ unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, u8 len; int err; - skb = __can_get_echo_skb(dev, idx, &len); + skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr); if (!skb) return 0; diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c new file mode 100644 index 000000000000..6a64fe410987 --- /dev/null +++ b/drivers/net/can/dev/skb.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix + * Copyright (C) 2006 Andrey Volkov, Varma Electronics + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + */ + +#include <linux/can/dev.h> + +/* Local echo of CAN messages + * + * CAN network devices *should* support a local echo functionality + * (see Documentation/networking/can.rst). To test the handling of CAN + * interfaces that do not support the local echo both driver types are + * implemented. In the case that the driver does not support the echo + * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core + * to perform the echo as a fallback solution. + */ +void can_flush_echo_skb(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + int i; + + for (i = 0; i < priv->echo_skb_max; i++) { + if (priv->echo_skb[i]) { + kfree_skb(priv->echo_skb[i]); + priv->echo_skb[i] = NULL; + stats->tx_dropped++; + stats->tx_aborted_errors++; + } + } +} + +/* Put the skb on the stack to be looped backed locally lateron + * + * The function is typically called in the start_xmit function + * of the device driver. The driver must protect access to + * priv->echo_skb, if necessary. + */ +int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, + unsigned int idx, unsigned int frame_len) +{ + struct can_priv *priv = netdev_priv(dev); + + BUG_ON(idx >= priv->echo_skb_max); + + /* check flag whether this packet has to be looped back */ + if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK || + (skb->protocol != htons(ETH_P_CAN) && + skb->protocol != htons(ETH_P_CANFD))) { + kfree_skb(skb); + return 0; + } + + if (!priv->echo_skb[idx]) { + skb = can_create_echo_skb(skb); + if (!skb) + return -ENOMEM; + + /* make settings for echo to reduce code in irq context */ + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->dev = dev; + + /* save frame_len to reuse it when transmission is completed */ + can_skb_prv(skb)->frame_len = frame_len; + + skb_tx_timestamp(skb); + + /* save this skb for tx interrupt echo handling */ + priv->echo_skb[idx] = skb; + } else { + /* locking problem with netif_stop_queue() ?? */ + netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx); + kfree_skb(skb); + return -EBUSY; + } + + return 0; +} +EXPORT_SYMBOL_GPL(can_put_echo_skb); + +struct sk_buff * +__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr, + unsigned int *frame_len_ptr) +{ + struct can_priv *priv = netdev_priv(dev); + + if (idx >= priv->echo_skb_max) { + netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n", + __func__, idx, priv->echo_skb_max); + return NULL; + } + + if (priv->echo_skb[idx]) { + /* Using "struct canfd_frame::len" for the frame + * length is supported on both CAN and CANFD frames. + */ + struct sk_buff *skb = priv->echo_skb[idx]; + struct can_skb_priv *can_skb_priv = can_skb_prv(skb); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + + /* get the real payload length for netdev statistics */ + if (cf->can_id & CAN_RTR_FLAG) + *len_ptr = 0; + else + *len_ptr = cf->len; + + if (frame_len_ptr) + *frame_len_ptr = can_skb_priv->frame_len; + + priv->echo_skb[idx] = NULL; + + return skb; + } + + return NULL; +} + +/* Get the skb from the stack and loop it back locally + * + * The function is typically called when the TX done interrupt + * is handled in the device driver. The driver must protect + * access to priv->echo_skb, if necessary. + */ +unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx, + unsigned int *frame_len_ptr) +{ + struct sk_buff *skb; + u8 len; + + skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr); + if (!skb) + return 0; + + skb_get(skb); + if (netif_rx(skb) == NET_RX_SUCCESS) + dev_consume_skb_any(skb); + else + dev_kfree_skb_any(skb); + + return len; +} +EXPORT_SYMBOL_GPL(can_get_echo_skb); + +/* Remove the skb from the stack and free it. + * + * The function is typically called when TX failed. + */ +void can_free_echo_skb(struct net_device *dev, unsigned int idx) +{ + struct can_priv *priv = netdev_priv(dev); + + BUG_ON(idx >= priv->echo_skb_max); + + if (priv->echo_skb[idx]) { + dev_kfree_skb_any(priv->echo_skb[idx]); + priv->echo_skb[idx] = NULL; + } +} +EXPORT_SYMBOL_GPL(can_free_echo_skb); + +struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + + sizeof(struct can_frame)); + if (unlikely(!skb)) + return NULL; + + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + + *cf = skb_put_zero(skb, sizeof(struct can_frame)); + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_can_skb); + +struct sk_buff *alloc_canfd_skb(struct net_device *dev, + struct canfd_frame **cfd) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + + sizeof(struct canfd_frame)); + if (unlikely(!skb)) + return NULL; + + skb->protocol = htons(ETH_P_CANFD); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + + *cfd = skb_put_zero(skb, sizeof(struct canfd_frame)); + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_canfd_skb); + +struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = alloc_can_skb(dev, cf); + if (unlikely(!skb)) + return NULL; + + (*cf)->can_id = CAN_ERR_FLAG; + (*cf)->len = CAN_ERR_DLC; + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_can_err_skb); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 038fe1036df2..971ada36e37f 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -9,6 +9,7 @@ // // Based on code originally by Andrey Volkov <avolkov@varma-el.com> +#include <dt-bindings/firmware/imx/rsrc.h> #include <linux/bitfield.h> #include <linux/can.h> #include <linux/can/dev.h> @@ -17,6 +18,7 @@ #include <linux/can/rx-offload.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/firmware/imx/sci.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mfd/syscon.h> @@ -242,6 +244,8 @@ #define FLEXCAN_QUIRK_SUPPORT_FD BIT(9) /* support memory detection and correction */ #define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10) +/* Setup stop mode with SCU firmware to support wakeup */ +#define FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW BIT(11) /* Structure of the message buffer */ struct flexcan_mb { @@ -347,6 +351,7 @@ struct flexcan_priv { u8 mb_count; u8 mb_size; u8 clk_src; /* clock source of CAN Protocol Engine */ + u8 scu_idx; u64 rx_mask; u64 tx_mask; @@ -358,6 +363,9 @@ struct flexcan_priv { struct regulator *reg_xceiver; struct flexcan_stop_mode stm; + /* IPC handle when setup stop mode by System Controller firmware(scfw) */ + struct imx_sc_ipc *sc_ipc_handle; + /* Read and Write APIs */ u32 (*read)(void __iomem *addr); void (*write)(u32 val, void __iomem *addr); @@ -387,7 +395,7 @@ static const struct flexcan_devtype_data fsl_imx6q_devtype_data = { static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = { .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE | - FLEXCAN_QUIRK_SUPPORT_FD, + FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW, }; static struct flexcan_devtype_data fsl_imx8mp_devtype_data = { @@ -546,18 +554,42 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable) priv->write(reg_mcr, ®s->mcr); } +static int flexcan_stop_mode_enable_scfw(struct flexcan_priv *priv, bool enabled) +{ + u8 idx = priv->scu_idx; + u32 rsrc_id, val; + + rsrc_id = IMX_SC_R_CAN(idx); + + if (enabled) + val = 1; + else + val = 0; + + /* stop mode request via scu firmware */ + return imx_sc_misc_set_control(priv->sc_ipc_handle, rsrc_id, + IMX_SC_C_IPG_STOP, val); +} + static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; u32 reg_mcr; + int ret; reg_mcr = priv->read(®s->mcr); reg_mcr |= FLEXCAN_MCR_SLF_WAK; priv->write(reg_mcr, ®s->mcr); /* enable stop request */ - regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, - 1 << priv->stm.req_bit, 1 << priv->stm.req_bit); + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) { + ret = flexcan_stop_mode_enable_scfw(priv, true); + if (ret < 0) + return ret; + } else { + regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, + 1 << priv->stm.req_bit, 1 << priv->stm.req_bit); + } return flexcan_low_power_enter_ack(priv); } @@ -566,10 +598,17 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; u32 reg_mcr; + int ret; /* remove stop request */ - regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, - 1 << priv->stm.req_bit, 0); + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) { + ret = flexcan_stop_mode_enable_scfw(priv, false); + if (ret < 0) + return ret; + } else { + regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, + 1 << priv->stm.req_bit, 0); + } reg_mcr = priv->read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_SLF_WAK; @@ -776,7 +815,7 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de priv->write(data, &priv->tx_mb->data[i / sizeof(u32)]); } - can_put_echo_skb(skb, dev, 0); + can_put_echo_skb(skb, dev, 0, 0); priv->write(can_id, &priv->tx_mb->can_id); priv->write(ctrl, &priv->tx_mb->can_ctrl); @@ -1083,8 +1122,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) u32 reg_ctrl = priv->read(&priv->tx_mb->can_ctrl); handled = IRQ_HANDLED; - stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload, - 0, reg_ctrl << 16); + stats->tx_bytes += + can_rx_offload_get_echo_skb(&priv->offload, 0, + reg_ctrl << 16, NULL); stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); @@ -1867,7 +1907,7 @@ static void unregister_flexcandev(struct net_device *dev) unregister_candev(dev); } -static int flexcan_setup_stop_mode(struct platform_device *pdev) +static int flexcan_setup_stop_mode_gpr(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; @@ -1912,11 +1952,6 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) "gpr %s req_gpr=0x02%x req_bit=%u\n", gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit); - device_set_wakeup_capable(&pdev->dev, true); - - if (of_property_read_bool(np, "wakeup-source")) - device_set_wakeup_enable(&pdev->dev, true); - return 0; out_put_node: @@ -1924,6 +1959,58 @@ out_put_node: return ret; } +static int flexcan_setup_stop_mode_scfw(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct flexcan_priv *priv; + u8 scu_idx; + int ret; + + ret = of_property_read_u8(pdev->dev.of_node, "fsl,scu-index", &scu_idx); + if (ret < 0) { + dev_dbg(&pdev->dev, "failed to get scu index\n"); + return ret; + } + + priv = netdev_priv(dev); + priv->scu_idx = scu_idx; + + /* this function could be deferred probe, return -EPROBE_DEFER */ + return imx_scu_get_handle(&priv->sc_ipc_handle); +} + +/* flexcan_setup_stop_mode - Setup stop mode for wakeup + * + * Return: = 0 setup stop mode successfully or doesn't support this feature + * < 0 fail to setup stop mode (could be deferred probe) + */ +static int flexcan_setup_stop_mode(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct flexcan_priv *priv; + int ret; + + priv = netdev_priv(dev); + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) + ret = flexcan_setup_stop_mode_scfw(pdev); + else if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) + ret = flexcan_setup_stop_mode_gpr(pdev); + else + /* return 0 directly if doesn't support stop mode feature */ + return 0; + + if (ret) + return ret; + + device_set_wakeup_capable(&pdev->dev, true); + + if (of_property_read_bool(pdev->dev.of_node, "wakeup-source")) + device_set_wakeup_enable(&pdev->dev, true); + + return 0; +} + static const struct of_device_id flexcan_of_match[] = { { .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, }, { .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, }, @@ -2054,17 +2141,20 @@ static int flexcan_probe(struct platform_device *pdev) goto failed_register; } + err = flexcan_setup_stop_mode(pdev); + if (err < 0) { + if (err != -EPROBE_DEFER) + dev_err(&pdev->dev, "setup stop mode failed\n"); + goto failed_setup_stop_mode; + } + of_can_transceiver(dev); devm_can_led_init(dev); - if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) { - err = flexcan_setup_stop_mode(pdev); - if (err) - dev_dbg(&pdev->dev, "failed to setup stop-mode\n"); - } - return 0; + failed_setup_stop_mode: + unregister_flexcandev(dev); failed_register: pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c index f5d94a692576..4a8453290530 100644 --- a/drivers/net/can/grcan.c +++ b/drivers/net/can/grcan.c @@ -517,7 +517,7 @@ static int catch_up_echo_skb(struct net_device *dev, int budget, bool echo) stats->tx_packets++; stats->tx_bytes += priv->txdlc[i]; priv->txdlc[i] = 0; - can_get_echo_skb(dev, i); + can_get_echo_skb(dev, i, NULL); } else { /* For cleanup of untransmitted messages */ can_free_echo_skb(dev, i); @@ -1448,7 +1448,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb, * taken. */ priv->txdlc[slotindex] = cf->len; /* Store dlc for statistics */ - can_put_echo_skb(skb, dev, slotindex); + can_put_echo_skb(skb, dev, slotindex, 0); /* Make sure everything is written before allowing hardware to * read from the memory diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c index 86b0e1406a21..5bb957a26bc6 100644 --- a/drivers/net/can/ifi_canfd/ifi_canfd.c +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c @@ -629,7 +629,7 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) /* TX IRQ */ if (isr & IFI_CANFD_INTERRUPT_TXFIFO_REMOVE) { - stats->tx_bytes += can_get_echo_skb(ndev, 0); + stats->tx_bytes += can_get_echo_skb(ndev, 0, NULL); stats->tx_packets++; can_led_event(ndev, CAN_LED_EVENT_TX); } @@ -922,7 +922,7 @@ static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb, writel(0, priv->base + IFI_CANFD_TXFIFO_REPEATCOUNT); writel(0, priv->base + IFI_CANFD_TXFIFO_SUSPEND_US); - can_put_echo_skb(skb, ndev, 0); + can_put_echo_skb(skb, ndev, 0, 0); /* Start the transmission */ writel(IFI_CANFD_TXSTCMD_ADD_MSG, priv->base + IFI_CANFD_TXSTCMD); diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 969cedb9b0b6..37e05010ca91 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -778,7 +778,7 @@ static netdev_tx_t kvaser_pciefd_start_xmit(struct sk_buff *skb, spin_lock_irqsave(&can->echo_lock, irq_flags); /* Prepare and save echo skb in internal slot */ - can_put_echo_skb(skb, netdev, can->echo_idx); + can_put_echo_skb(skb, netdev, can->echo_idx, 0); /* Move echo index to the next slot */ can->echo_idx = (can->echo_idx + 1) % can->can.echo_skb_max; @@ -1467,7 +1467,7 @@ static int kvaser_pciefd_handle_eack_packet(struct kvaser_pciefd *pcie, can->reg_base + KVASER_PCIEFD_KCAN_CTRL_REG); } else { int echo_idx = p->header[0] & KVASER_PCIEFD_PACKET_SEQ_MSK; - int dlc = can_get_echo_skb(can->can.dev, echo_idx); + int dlc = can_get_echo_skb(can->can.dev, echo_idx, NULL); struct net_device_stats *stats = &can->can.dev->stats; stats->tx_bytes += dlc; @@ -1533,7 +1533,7 @@ static int kvaser_pciefd_handle_ack_packet(struct kvaser_pciefd *pcie, netdev_dbg(can->can.dev, "Packet was flushed\n"); } else { int echo_idx = p->header[0] & KVASER_PCIEFD_PACKET_SEQ_MSK; - int dlc = can_get_echo_skb(can->can.dev, echo_idx); + int dlc = can_get_echo_skb(can->can.dev, echo_idx, NULL); u8 count = ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NPACKETS_REG) & 0xff; diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile index ef7963ff2006..d717bbc9e033 100644 --- a/drivers/net/can/m_can/Makefile +++ b/drivers/net/can/m_can/Makefile @@ -7,3 +7,7 @@ obj-$(CONFIG_CAN_M_CAN) += m_can.o obj-$(CONFIG_CAN_M_CAN_PCI) += m_can_pci.o obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o + +tcan4x5x-objs := +tcan4x5x-objs += tcan4x5x-core.o +tcan4x5x-objs += tcan4x5x-regmap.o diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index da551fd0f502..3752520a7d4b 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -930,7 +930,7 @@ static void m_can_echo_tx_event(struct net_device *dev) (fgi << TXEFA_EFAI_SHIFT))); /* update stats */ - stats->tx_bytes += can_get_echo_skb(dev, msg_mark); + stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); stats->tx_packets++; } } @@ -972,7 +972,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) if (cdev->version == 30) { if (ir & IR_TC) { /* Transmission Complete Interrupt*/ - stats->tx_bytes += can_get_echo_skb(dev, 0); + stats->tx_bytes += can_get_echo_skb(dev, 0, NULL); stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); netif_wake_queue(dev); @@ -1483,7 +1483,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) M_CAN_FIFO_DATA(i / 4), *(u32 *)(cf->data + i)); - can_put_echo_skb(skb, dev, 0); + can_put_echo_skb(skb, dev, 0, 0); if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { cccr = m_can_read(cdev, M_CAN_CCCR); @@ -1554,7 +1554,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) /* Push loopback echo. * Will be looped back on TX interrupt based on message marker */ - can_put_echo_skb(skb, dev, putidx); + can_put_echo_skb(skb, dev, putidx, 0); /* Enable TX FIFO element to start transfer */ m_can_write(cdev, M_CAN_TXBAR, (1 << putidx)); diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x-core.c index 970f0e9d19bf..b7caec769ddb 100644 --- a/drivers/net/can/m_can/tcan4x5x.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -2,15 +2,8 @@ // SPI to CAN driver for the Texas Instruments TCAN4x5x // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ -#include <linux/regmap.h> -#include <linux/spi/spi.h> +#include "tcan4x5x.h" -#include <linux/regulator/consumer.h> -#include <linux/gpio/consumer.h> - -#include "m_can.h" - -#define DEVICE_NAME "tcan4x5x" #define TCAN4X5X_EXT_CLK_DEF 40000000 #define TCAN4X5X_DEV_ID0 0x00 @@ -88,14 +81,10 @@ #define TCAN4X5X_MRAM_START 0x8000 #define TCAN4X5X_MCAN_OFFSET 0x1000 -#define TCAN4X5X_MAX_REGISTER 0x8fff #define TCAN4X5X_CLEAR_ALL_INT 0xffffffff #define TCAN4X5X_SET_ALL_INT 0xffffffff -#define TCAN4X5X_WRITE_CMD (0x61 << 24) -#define TCAN4X5X_READ_CMD (0x41 << 24) - #define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6)) #define TCAN4X5X_MODE_SLEEP 0x00 #define TCAN4X5X_MODE_STANDBY BIT(6) @@ -113,18 +102,6 @@ #define TCAN4X5X_WD_3_S_TIMER BIT(29) #define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29)) -struct tcan4x5x_priv { - struct m_can_classdev cdev; - - struct regmap *regmap; - struct spi_device *spi; - - struct gpio_desc *reset_gpio; - struct gpio_desc *device_wake_gpio; - struct gpio_desc *device_state_gpio; - struct regulator *power; -}; - static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev) { return container_of(cdev, struct tcan4x5x_priv, cdev); @@ -167,72 +144,6 @@ static int tcan4x5x_reset(struct tcan4x5x_priv *priv) return ret; } -static int regmap_spi_gather_write(void *context, const void *reg, - size_t reg_len, const void *val, - size_t val_len) -{ - struct device *dev = context; - struct spi_device *spi = to_spi_device(dev); - struct spi_message m; - u32 addr; - struct spi_transfer t[2] = { - { .tx_buf = &addr, .len = reg_len, .cs_change = 0,}, - { .tx_buf = val, .len = val_len, }, - }; - - addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 2; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - return spi_sync(spi, &m); -} - -static int tcan4x5x_regmap_write(void *context, const void *data, size_t count) -{ - u16 *reg = (u16 *)(data); - const u32 *val = data + 4; - - return regmap_spi_gather_write(context, reg, 4, val, count - 4); -} - -static int regmap_spi_async_write(void *context, - const void *reg, size_t reg_len, - const void *val, size_t val_len, - struct regmap_async *a) -{ - return -ENOTSUPP; -} - -static struct regmap_async *regmap_spi_async_alloc(void) -{ - return NULL; -} - -static int tcan4x5x_regmap_read(void *context, - const void *reg, size_t reg_size, - void *val, size_t val_size) -{ - struct device *dev = context; - struct spi_device *spi = to_spi_device(dev); - - u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2; - - return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size); -} - -static struct regmap_bus tcan4x5x_bus = { - .write = tcan4x5x_regmap_write, - .gather_write = regmap_spi_gather_write, - .async_write = regmap_spi_async_write, - .async_alloc = regmap_spi_async_alloc, - .read = tcan4x5x_regmap_read, - .read_flag_mask = 0x00, - .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, - .val_format_endian_default = REGMAP_ENDIAN_NATIVE, -}; - static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg) { struct tcan4x5x_priv *priv = cdev_to_priv(cdev); @@ -387,13 +298,6 @@ static int tcan4x5x_get_gpios(struct m_can_classdev *cdev) return 0; } -static const struct regmap_config tcan4x5x_regmap = { - .reg_bits = 32, - .val_bits = 32, - .cache_type = REGCACHE_NONE, - .max_register = TCAN4X5X_MAX_REGISTER, -}; - static struct m_can_ops tcan4x5x_ops = { .init = tcan4x5x_init, .read_reg = tcan4x5x_read_reg, @@ -450,17 +354,14 @@ static int tcan4x5x_can_probe(struct spi_device *spi) spi_set_drvdata(spi, priv); /* Configure the SPI bus */ - spi->bits_per_word = 32; + spi->bits_per_word = 8; ret = spi_setup(spi); if (ret) goto out_m_can_class_free_dev; - priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus, - &spi->dev, &tcan4x5x_regmap); - if (IS_ERR(priv->regmap)) { - ret = PTR_ERR(priv->regmap); + ret = tcan4x5x_regmap_init(priv); + if (ret) goto out_m_can_class_free_dev; - } ret = tcan4x5x_power_enable(priv->power, 1); if (ret) @@ -502,23 +403,26 @@ static int tcan4x5x_can_remove(struct spi_device *spi) } static const struct of_device_id tcan4x5x_of_match[] = { - { .compatible = "ti,tcan4x5x", }, - { } + { + .compatible = "ti,tcan4x5x", + }, { + /* sentinel */ + }, }; MODULE_DEVICE_TABLE(of, tcan4x5x_of_match); static const struct spi_device_id tcan4x5x_id_table[] = { { - .name = "tcan4x5x", - .driver_data = 0, + .name = "tcan4x5x", + }, { + /* sentinel */ }, - { } }; MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table); static struct spi_driver tcan4x5x_can_driver = { .driver = { - .name = DEVICE_NAME, + .name = KBUILD_MODNAME, .of_match_table = tcan4x5x_of_match, .pm = NULL, }, diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c new file mode 100644 index 000000000000..ca80dbaf7a3f --- /dev/null +++ b/drivers/net/can/m_can/tcan4x5x-regmap.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver +// +// Copyright (c) 2020 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// Copyright (c) 2018-2019 Texas Instruments Incorporated +// http://www.ti.com/ + +#include "tcan4x5x.h" + +#define TCAN4X5X_SPI_INSTRUCTION_WRITE (0x61 << 24) +#define TCAN4X5X_SPI_INSTRUCTION_READ (0x41 << 24) + +#define TCAN4X5X_MAX_REGISTER 0x8ffc + +static int tcan4x5x_regmap_gather_write(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len) +{ + struct spi_device *spi = context; + struct tcan4x5x_priv *priv = spi_get_drvdata(spi); + struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx; + struct spi_transfer xfer[] = { + { + .tx_buf = buf_tx, + .len = sizeof(buf_tx->cmd) + val_len, + }, + }; + + memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd.cmd) + + sizeof(buf_tx->cmd.addr)); + tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len); + memcpy(buf_tx->data, val, val_len); + + return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); +} + +static int tcan4x5x_regmap_write(void *context, const void *data, size_t count) +{ + return tcan4x5x_regmap_gather_write(context, data, sizeof(__be32), + data + sizeof(__be32), + count - sizeof(__be32)); +} + +static int tcan4x5x_regmap_read(void *context, + const void *reg_buf, size_t reg_len, + void *val_buf, size_t val_len) +{ + struct spi_device *spi = context; + struct tcan4x5x_priv *priv = spi_get_drvdata(spi); + struct tcan4x5x_map_buf *buf_rx = &priv->map_buf_rx; + struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx; + struct spi_transfer xfer[2] = { + { + .tx_buf = buf_tx, + } + }; + struct spi_message msg; + int err; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); + + memcpy(&buf_tx->cmd, reg_buf, sizeof(buf_tx->cmd.cmd) + + sizeof(buf_tx->cmd.addr)); + tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len); + + if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) { + xfer[0].len = sizeof(buf_tx->cmd); + + xfer[1].rx_buf = val_buf; + xfer[1].len = val_len; + spi_message_add_tail(&xfer[1], &msg); + } else { + xfer[0].rx_buf = buf_rx; + xfer[0].len = sizeof(buf_tx->cmd) + val_len; + + if (TCAN4X5X_SANITIZE_SPI) + memset(buf_tx->data, 0x0, val_len); + } + + err = spi_sync(spi, &msg); + if (err) + return err; + + if (!(spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX)) + memcpy(val_buf, buf_rx->data, val_len); + + return 0; +} + +static const struct regmap_range tcan4x5x_reg_table_yes_range[] = { + regmap_reg_range(0x0000, 0x002c), /* Device ID and SPI Registers */ + regmap_reg_range(0x0800, 0x083c), /* Device configuration registers and Interrupt Flags*/ + regmap_reg_range(0x1000, 0x10fc), /* M_CAN */ + regmap_reg_range(0x8000, 0x87fc), /* MRAM */ +}; + +static const struct regmap_access_table tcan4x5x_reg_table = { + .yes_ranges = tcan4x5x_reg_table_yes_range, + .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_range), +}; + +static const struct regmap_config tcan4x5x_regmap = { + .reg_bits = 24, + .reg_stride = 4, + .pad_bits = 8, + .val_bits = 32, + .wr_table = &tcan4x5x_reg_table, + .rd_table = &tcan4x5x_reg_table, + .max_register = TCAN4X5X_MAX_REGISTER, + .cache_type = REGCACHE_NONE, + .read_flag_mask = (__force unsigned long) + cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_READ), + .write_flag_mask = (__force unsigned long) + cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_WRITE), +}; + +static const struct regmap_bus tcan4x5x_bus = { + .write = tcan4x5x_regmap_write, + .gather_write = tcan4x5x_regmap_gather_write, + .read = tcan4x5x_regmap_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, + .max_raw_read = 256, + .max_raw_write = 256, +}; + +int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv) +{ + priv->regmap = devm_regmap_init(&priv->spi->dev, &tcan4x5x_bus, + priv->spi, &tcan4x5x_regmap); + return PTR_ERR_OR_ZERO(priv->regmap); +} diff --git a/drivers/net/can/m_can/tcan4x5x.h b/drivers/net/can/m_can/tcan4x5x.h new file mode 100644 index 000000000000..c66da829b795 --- /dev/null +++ b/drivers/net/can/m_can/tcan4x5x.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver + * + * Copyright (c) 2020 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> + */ + +#ifndef _TCAN4X5X_H +#define _TCAN4X5X_H + +#include <linux/gpio/consumer.h> +#include <linux/regmap.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include "m_can.h" + +#define TCAN4X5X_SANITIZE_SPI 1 + +struct __packed tcan4x5x_buf_cmd { + u8 cmd; + __be16 addr; + u8 len; +}; + +struct tcan4x5x_map_buf { + struct tcan4x5x_buf_cmd cmd; + u8 data[256 * sizeof(u32)]; +} ____cacheline_aligned; + +struct tcan4x5x_priv { + struct m_can_classdev cdev; + + struct regmap *regmap; + struct spi_device *spi; + + struct gpio_desc *reset_gpio; + struct gpio_desc *device_wake_gpio; + struct gpio_desc *device_state_gpio; + struct regulator *power; + + struct tcan4x5x_map_buf map_buf_rx; + struct tcan4x5x_map_buf map_buf_tx; +}; + +static inline void +tcan4x5x_spi_cmd_set_len(struct tcan4x5x_buf_cmd *cmd, u8 len) +{ + /* number of u32 */ + cmd->len = len >> 2; +} + +int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv); + +#endif diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index 5ed00a1558e1..fa32e418eb29 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -270,7 +270,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head); - can_put_echo_skb(skb, dev, buf_id); + can_put_echo_skb(skb, dev, buf_id, 0); /* Enable interrupt. */ priv->tx_active |= 1 << buf_id; @@ -448,7 +448,7 @@ static irqreturn_t mscan_isr(int irq, void *dev_id) out_8(®s->cantbsel, mask); stats->tx_bytes += in_8(®s->tx.dlr); stats->tx_packets++; - can_get_echo_skb(dev, entry->id); + can_get_echo_skb(dev, entry->id, NULL); priv->tx_active &= ~mask; list_del(pos); } diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c index 4f9e7ec192aa..92a54a5fd4c5 100644 --- a/drivers/net/can/pch_can.c +++ b/drivers/net/can/pch_can.c @@ -711,7 +711,7 @@ static void pch_can_tx_complete(struct net_device *ndev, u32 int_stat) struct net_device_stats *stats = &(priv->ndev->stats); u32 dlc; - can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_END - 1); + can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_END - 1, NULL); iowrite32(PCH_CMASK_RX_TX_GET | PCH_CMASK_CLRINTPND, &priv->regs->ifregs[1].cmask); pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, int_stat); @@ -924,7 +924,7 @@ static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev) &priv->regs->ifregs[1].data[i / 2]); } - can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1); + can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1, 0); /* Set the size of the data. Update if2_mcont */ iowrite32(cf->len | PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT | diff --git a/drivers/net/can/peak_canfd/peak_canfd.c b/drivers/net/can/peak_canfd/peak_canfd.c index c5334b0c3038..00847cbaf7b6 100644 --- a/drivers/net/can/peak_canfd/peak_canfd.c +++ b/drivers/net/can/peak_canfd/peak_canfd.c @@ -266,7 +266,7 @@ static int pucan_handle_can_rx(struct peak_canfd_priv *priv, unsigned long flags; spin_lock_irqsave(&priv->echo_lock, flags); - can_get_echo_skb(priv->ndev, msg->client); + can_get_echo_skb(priv->ndev, msg->client, NULL); /* count bytes of the echo instead of skb */ stats->tx_bytes += cf_len; @@ -716,7 +716,7 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb, spin_lock_irqsave(&priv->echo_lock, flags); /* prepare and save echo skb in internal slot */ - can_put_echo_skb(skb, ndev, priv->echo_idx); + can_put_echo_skb(skb, ndev, priv->echo_idx, 0); /* move echo index to the next slot */ priv->echo_idx = (priv->echo_idx + 1) % priv->can.echo_skb_max; diff --git a/drivers/net/can/rcar/rcar_can.c b/drivers/net/can/rcar/rcar_can.c index c803327f8f79..4870c4ea190a 100644 --- a/drivers/net/can/rcar/rcar_can.c +++ b/drivers/net/can/rcar/rcar_can.c @@ -386,7 +386,7 @@ static void rcar_can_tx_done(struct net_device *ndev) stats->tx_bytes += priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH]; priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0; - can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH); + can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH, NULL); priv->tx_tail++; netif_wake_queue(ndev); } @@ -617,7 +617,7 @@ static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb, writeb(cf->len, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc); priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->len; - can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH); + can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH, 0); priv->tx_head++; /* Start Tx: write 0xff to the TFPCR register to increment * the CPU-side pointer for the transmit FIFO to the next diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 2778ed5c61d1..d8d233e62990 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -1044,7 +1044,7 @@ static void rcar_canfd_tx_done(struct net_device *ndev) stats->tx_packets++; stats->tx_bytes += priv->tx_len[sent]; priv->tx_len[sent] = 0; - can_get_echo_skb(ndev, sent); + can_get_echo_skb(ndev, sent, NULL); spin_lock_irqsave(&priv->tx_lock, flags); priv->tx_tail++; @@ -1390,7 +1390,7 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb, } priv->tx_len[priv->tx_head % RCANFD_FIFO_DEPTH] = cf->len; - can_put_echo_skb(skb, ndev, priv->tx_head % RCANFD_FIFO_DEPTH); + can_put_echo_skb(skb, ndev, priv->tx_head % RCANFD_FIFO_DEPTH, 0); spin_lock_irqsave(&priv->tx_lock, flags); priv->tx_head++; diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index b6a7003c51d2..9e86488ba55f 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -318,7 +318,7 @@ static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb, for (i = 0; i < cf->len; i++) priv->write_reg(priv, dreg++, cf->data[i]); - can_put_echo_skb(skb, dev, 0); + can_put_echo_skb(skb, dev, 0, 0); if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) cmd_reg_val |= CMD_AT; @@ -531,7 +531,7 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) stats->tx_bytes += priv->read_reg(priv, SJA1000_FI) & 0xf; stats->tx_packets++; - can_get_echo_skb(dev, 0); + can_get_echo_skb(dev, 0, NULL); } netif_wake_queue(dev); can_led_event(dev, CAN_LED_EVENT_TX); diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index 40070c930202..c44f3411e561 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -104,7 +104,7 @@ static netdev_tx_t softing_netdev_start_xmit(struct sk_buff *skb, card->tx.last_bus = priv->index; ++card->tx.pending; ++priv->tx.pending; - can_put_echo_skb(skb, dev, priv->tx.echo_put); + can_put_echo_skb(skb, dev, priv->tx.echo_put, 0); ++priv->tx.echo_put; if (priv->tx.echo_put >= TX_ECHO_SKB_MAX) priv->tx.echo_put = 0; @@ -284,7 +284,7 @@ static int softing_handle_1(struct softing *card) skb = priv->can.echo_skb[priv->tx.echo_get]; if (skb) skb->tstamp = ktime; - can_get_echo_skb(netdev, priv->tx.echo_get); + can_get_echo_skb(netdev, priv->tx.echo_get, NULL); ++priv->tx.echo_get; if (priv->tx.echo_get >= TX_ECHO_SKB_MAX) priv->tx.echo_get = 0; diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c index f9455de94786..c3e020c90111 100644 --- a/drivers/net/can/spi/hi311x.c +++ b/drivers/net/can/spi/hi311x.c @@ -586,7 +586,7 @@ static void hi3110_tx_work_handler(struct work_struct *ws) frame = (struct can_frame *)priv->tx_skb->data; hi3110_hw_tx(spi, frame); priv->tx_len = 1 + frame->len; - can_put_echo_skb(priv->tx_skb, net, 0); + can_put_echo_skb(priv->tx_skb, net, 0, 0); priv->tx_skb = NULL; } } @@ -725,7 +725,7 @@ static irqreturn_t hi3110_can_ist(int irq, void *dev_id) net->stats.tx_bytes += priv->tx_len - 1; can_led_event(net, CAN_LED_EVENT_TX); if (priv->tx_len) { - can_get_echo_skb(net, 0); + can_get_echo_skb(net, 0, NULL); priv->tx_len = 0; } netif_wake_queue(net); diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index 25859d16d06f..f69fb4238a65 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -1002,7 +1002,7 @@ static void mcp251x_tx_work_handler(struct work_struct *ws) frame->len = CAN_FRAME_MAX_DATA_LEN; mcp251x_hw_tx(spi, frame, 0); priv->tx_len = 1 + frame->len; - can_put_echo_skb(priv->tx_skb, net, 0); + can_put_echo_skb(priv->tx_skb, net, 0, 0); priv->tx_skb = NULL; } } @@ -1171,7 +1171,7 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) net->stats.tx_bytes += priv->tx_len - 1; can_led_event(net, CAN_LED_EVENT_TX); if (priv->tx_len) { - can_get_echo_skb(net, 0); + can_get_echo_skb(net, 0, NULL); priv->tx_len = 0; } netif_wake_queue(net); diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c index f07e8b737d31..3638b474d86b 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c @@ -335,6 +335,8 @@ static void mcp251xfd_ring_init(struct mcp251xfd_priv *priv) u8 len; int i, j; + netdev_reset_queue(priv->ndev); + /* TEF */ tef_ring = priv->tef; tef_ring->head = 0; @@ -1249,7 +1251,8 @@ mcp251xfd_handle_tefif_recover(const struct mcp251xfd_priv *priv, const u32 seq) static int mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv, - const struct mcp251xfd_hw_tef_obj *hw_tef_obj) + const struct mcp251xfd_hw_tef_obj *hw_tef_obj, + unsigned int *frame_len_ptr) { struct net_device_stats *stats = &priv->ndev->stats; u32 seq, seq_masked, tef_tail_masked; @@ -1271,7 +1274,8 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv, stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload, mcp251xfd_get_tef_tail(priv), - hw_tef_obj->ts); + hw_tef_obj->ts, + frame_len_ptr); stats->tx_packets++; priv->tef->tail++; @@ -1308,6 +1312,7 @@ mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv, const u8 offset, const u8 len) { const struct mcp251xfd_tx_ring *tx_ring = priv->tx; + const int val_bytes = regmap_get_val_bytes(priv->map_rx); if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && (offset > tx_ring->obj_num || @@ -1322,12 +1327,13 @@ mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv, return regmap_bulk_read(priv->map_rx, mcp251xfd_get_tef_obj_addr(offset), hw_tef_obj, - sizeof(*hw_tef_obj) / sizeof(u32) * len); + sizeof(*hw_tef_obj) / val_bytes * len); } static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) { struct mcp251xfd_hw_tef_obj hw_tef_obj[MCP251XFD_TX_OBJ_NUM_MAX]; + unsigned int total_frame_len = 0; u8 tef_tail, len, l; int err, i; @@ -1349,7 +1355,9 @@ static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) } for (i = 0; i < len; i++) { - err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i]); + unsigned int frame_len; + + err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i], &frame_len); /* -EAGAIN means the Sequence Number in the TEF * doesn't match our tef_tail. This can happen if we * read the TEF objects too early. Leave loop let the @@ -1359,6 +1367,8 @@ static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) goto out_netif_wake_queue; if (err) return err; + + total_frame_len += frame_len; } out_netif_wake_queue: @@ -1389,6 +1399,7 @@ static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) return err; tx_ring->tail += len; + netdev_completed_queue(priv->ndev, len, total_frame_len); err = mcp251xfd_check_tef_tail(priv); if (err) @@ -1438,6 +1449,7 @@ mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv, struct sk_buff *skb) { struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + u8 dlc; if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_IDE) { u32 sid, eid; @@ -1453,9 +1465,10 @@ mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv, hw_rx_obj->id); } + dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC, hw_rx_obj->flags); + /* CANFD */ if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF) { - u8 dlc; if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_ESI) cfd->flags |= CANFD_ESI; @@ -1463,17 +1476,17 @@ mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv, if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_BRS) cfd->flags |= CANFD_BRS; - dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC, hw_rx_obj->flags); cfd->len = can_fd_dlc2len(dlc); } else { if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR) cfd->can_id |= CAN_RTR_FLAG; - cfd->len = can_cc_dlc2len(FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC, - hw_rx_obj->flags)); + can_frame_set_cc_len((struct can_frame *)cfd, dlc, + priv->can.ctrlmode); } - memcpy(cfd->data, hw_rx_obj->data, cfd->len); + if (!(hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR)) + memcpy(cfd->data, hw_rx_obj->data, cfd->len); } static int @@ -1510,12 +1523,13 @@ mcp251xfd_rx_obj_read(const struct mcp251xfd_priv *priv, struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj, const u8 offset, const u8 len) { + const int val_bytes = regmap_get_val_bytes(priv->map_rx); int err; err = regmap_bulk_read(priv->map_rx, mcp251xfd_get_rx_obj_addr(ring, offset), hw_rx_obj, - len * ring->obj_size / sizeof(u32)); + len * ring->obj_size / val_bytes); return err; } @@ -2137,6 +2151,7 @@ static int mcp251xfd_handle_spicrcif(struct mcp251xfd_priv *priv) static irqreturn_t mcp251xfd_irq(int irq, void *dev_id) { struct mcp251xfd_priv *priv = dev_id; + const int val_bytes = regmap_get_val_bytes(priv->map_reg); irqreturn_t handled = IRQ_NONE; int err; @@ -2162,7 +2177,7 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id) err = regmap_bulk_read(priv->map_reg, MCP251XFD_REG_INT, &priv->regs_status, sizeof(priv->regs_status) / - sizeof(u32)); + val_bytes); if (err) goto out_fail; @@ -2300,7 +2315,7 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv, union mcp251xfd_tx_obj_load_buf *load_buf; u8 dlc; u32 id, flags; - int offset, len; + int len_sanitized = 0, len; if (cfd->can_id & CAN_EFF_FLAG) { u32 sid, eid; @@ -2321,12 +2336,12 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv, * harm, only the lower 7 bits will be transferred into the * TEF object. */ - dlc = can_fd_len2dlc(cfd->len); - flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK, seq) | - FIELD_PREP(MCP251XFD_OBJ_FLAGS_DLC, dlc); + flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK, seq); if (cfd->can_id & CAN_RTR_FLAG) flags |= MCP251XFD_OBJ_FLAGS_RTR; + else + len_sanitized = canfd_sanitize_len(cfd->len); /* CANFD */ if (can_is_canfd_skb(skb)) { @@ -2337,8 +2352,15 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv, if (cfd->flags & CANFD_BRS) flags |= MCP251XFD_OBJ_FLAGS_BRS; + + dlc = can_fd_len2dlc(cfd->len); + } else { + dlc = can_get_cc_dlc((struct can_frame *)cfd, + priv->can.ctrlmode); } + flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_DLC, dlc); + load_buf = &tx_obj->buf; if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_TX) hw_tx_obj = &load_buf->crc.hw_tx_obj; @@ -2348,17 +2370,22 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv, put_unaligned_le32(id, &hw_tx_obj->id); put_unaligned_le32(flags, &hw_tx_obj->flags); - /* Clear data at end of CAN frame */ - offset = round_down(cfd->len, sizeof(u32)); - len = round_up(can_fd_dlc2len(dlc), sizeof(u32)) - offset; - if (MCP251XFD_SANITIZE_CAN && len) - memset(hw_tx_obj->data + offset, 0x0, len); + /* Copy data */ memcpy(hw_tx_obj->data, cfd->data, cfd->len); + /* Clear unused data at end of CAN frame */ + if (MCP251XFD_SANITIZE_CAN && len_sanitized) { + int pad_len; + + pad_len = len_sanitized - cfd->len; + if (pad_len) + memset(hw_tx_obj->data + cfd->len, 0x0, pad_len); + } + /* Number of bytes to be written into the RAM of the controller */ len = sizeof(hw_tx_obj->id) + sizeof(hw_tx_obj->flags); if (MCP251XFD_SANITIZE_CAN) - len += round_up(can_fd_dlc2len(dlc), sizeof(u32)); + len += round_up(len_sanitized, sizeof(u32)); else len += round_up(cfd->len, sizeof(u32)); @@ -2418,6 +2445,7 @@ static netdev_tx_t mcp251xfd_start_xmit(struct sk_buff *skb, struct mcp251xfd_priv *priv = netdev_priv(ndev); struct mcp251xfd_tx_ring *tx_ring = priv->tx; struct mcp251xfd_tx_obj *tx_obj; + unsigned int frame_len; u8 tx_head; int err; @@ -2433,10 +2461,12 @@ static netdev_tx_t mcp251xfd_start_xmit(struct sk_buff *skb, /* Stop queue if we occupy the complete TX FIFO */ tx_head = mcp251xfd_get_tx_head(tx_ring); tx_ring->head++; - if (tx_ring->head - tx_ring->tail >= tx_ring->obj_num) + if (mcp251xfd_get_tx_free(tx_ring) == 0) netif_stop_queue(ndev); - can_put_echo_skb(skb, ndev, tx_head); + frame_len = can_skb_get_frame_len(skb); + can_put_echo_skb(skb, ndev, tx_head, frame_len); + netdev_sent_queue(priv->ndev, frame_len); err = mcp251xfd_tx_obj_write(priv, tx_obj); if (err) @@ -2885,7 +2915,8 @@ static int mcp251xfd_probe(struct spi_device *spi) priv->can.data_bittiming_const = &mcp251xfd_data_bittiming_const; priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | - CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO; + CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_CC_LEN8_DLC; priv->ndev = ndev; priv->spi = spi; priv->rx_int = rx_int; diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c index 783b63218b7b..54aa7c25c4de 100644 --- a/drivers/net/can/sun4i_can.c +++ b/drivers/net/can/sun4i_can.c @@ -448,7 +448,7 @@ static netdev_tx_t sun4ican_start_xmit(struct sk_buff *skb, struct net_device *d writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR); - can_put_echo_skb(skb, dev, 0); + can_put_echo_skb(skb, dev, 0, 0); if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ); @@ -655,7 +655,7 @@ static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id) readl(priv->base + SUN4I_REG_RBUF_RBACK_START_ADDR) & 0xf; stats->tx_packets++; - can_get_echo_skb(dev, 0); + can_get_echo_skb(dev, 0, NULL); netif_wake_queue(dev); can_led_event(dev, CAN_LED_EVENT_TX); } diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index a6850ff0b55b..73245d8836a9 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -513,7 +513,7 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) be32_to_cpu(*(__be32 *)(cf->data + 4))); else *(u32 *)(cf->data + 4) = 0; - can_put_echo_skb(skb, ndev, mbxno); + can_put_echo_skb(skb, ndev, mbxno, 0); spin_lock_irqsave(&priv->mbx_lock, flags); --priv->tx_head; @@ -757,7 +757,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) stamp = hecc_read_stamp(priv, mbxno); stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload, - mbxno, stamp); + mbxno, stamp, NULL); stats->tx_packets++; can_led_event(ndev, CAN_LED_EVENT_TX); --priv->tx_tail; diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 25eee4466364..18f40eb20360 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -518,7 +518,7 @@ static void ems_usb_write_bulk_callback(struct urb *urb) netdev->stats.tx_packets++; netdev->stats.tx_bytes += context->dlc; - can_get_echo_skb(netdev, context->echo_index); + can_get_echo_skb(netdev, context->echo_index, NULL); /* Release context */ context->echo_index = MAX_TX_URBS; @@ -801,7 +801,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->tx_submitted); - can_put_echo_skb(skb, netdev, context->echo_index); + can_put_echo_skb(skb, netdev, context->echo_index, 0); atomic_inc(&dev->active_tx_urbs); diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index 9eed75a4b678..562acbf454fd 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -357,7 +357,7 @@ static void esd_usb2_tx_done_msg(struct esd_usb2_net_priv *priv, if (!msg->msg.txdone.status) { stats->tx_packets++; stats->tx_bytes += context->len; - can_get_echo_skb(netdev, context->echo_index); + can_get_echo_skb(netdev, context->echo_index, NULL); } else { stats->tx_errors++; can_free_echo_skb(netdev, context->echo_index); @@ -783,7 +783,7 @@ static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, usb_anchor_urb(urb, &priv->tx_submitted); - can_put_echo_skb(skb, netdev, context->echo_index); + can_put_echo_skb(skb, netdev, context->echo_index, 0); atomic_inc(&priv->active_tx_jobs); diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 0487095e1fd0..a00dc1904415 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -370,7 +370,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) goto resubmit_urb; } - can_get_echo_skb(netdev, hf->echo_id); + can_get_echo_skb(netdev, hf->echo_id, NULL); gs_free_tx_context(txc); @@ -525,7 +525,7 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->tx_submitted); - can_put_echo_skb(skb, netdev, idx); + can_put_echo_skb(skb, netdev, idx, 0); atomic_inc(&dev->active_tx_urbs); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index e2d58846c40c..2b7efd296758 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -578,7 +578,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, context->priv = priv; - can_put_echo_skb(skb, netdev, context->echo_index); + can_put_echo_skb(skb, netdev, context->echo_index, 0); usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index 480bd2ecb296..dcee8dc828ec 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -1151,7 +1151,7 @@ static void kvaser_usb_hydra_tx_acknowledge(const struct kvaser_usb *dev, spin_lock_irqsave(&priv->tx_contexts_lock, irq_flags); - can_get_echo_skb(priv->netdev, context->echo_index); + can_get_echo_skb(priv->netdev, context->echo_index, NULL); context->echo_index = dev->max_tx_urbs; --priv->active_tx_contexts; netif_wake_queue(priv->netdev); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 98c016ef0607..59ba7c7beec0 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -594,7 +594,7 @@ static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev, spin_lock_irqsave(&priv->tx_contexts_lock, flags); - can_get_echo_skb(priv->netdev, context->echo_index); + can_get_echo_skb(priv->netdev, context->echo_index, NULL); context->echo_index = dev->max_tx_urbs; --priv->active_tx_contexts; netif_wake_queue(priv->netdev); diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c index df54eb7d4b36..1f649d178010 100644 --- a/drivers/net/can/usb/mcba_usb.c +++ b/drivers/net/can/usb/mcba_usb.c @@ -237,7 +237,7 @@ static void mcba_usb_write_bulk_callback(struct urb *urb) netdev->stats.tx_bytes += ctx->dlc; can_led_event(netdev, CAN_LED_EVENT_TX); - can_get_echo_skb(netdev, ctx->ndx); + can_get_echo_skb(netdev, ctx->ndx, NULL); } if (urb->status) @@ -355,7 +355,7 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, if (cf->can_id & CAN_RTR_FLAG) usb_msg.dlc |= MCBA_DLC_RTR_MASK; - can_put_echo_skb(skb, priv->netdev, ctx->ndx); + can_put_echo_skb(skb, priv->netdev, ctx->ndx, 0); err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx); if (err) @@ -466,7 +466,7 @@ static void mcba_usb_process_ka_usb(struct mcba_priv *priv, struct mcba_usb_msg_ka_usb *msg) { if (unlikely(priv->usb_ka_first_pass)) { - netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n", + netdev_info(priv->netdev, "PIC USB version %u.%u\n", msg->soft_ver_major, msg->soft_ver_minor); priv->usb_ka_first_pass = false; @@ -492,7 +492,7 @@ static void mcba_usb_process_ka_can(struct mcba_priv *priv, struct mcba_usb_msg_ka_can *msg) { if (unlikely(priv->can_ka_first_pass)) { - netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n", + netdev_info(priv->netdev, "PIC CAN version %u.%u\n", msg->soft_ver_major, msg->soft_ver_minor); priv->can_ka_first_pass = false; @@ -554,7 +554,7 @@ static void mcba_usb_process_rx(struct mcba_priv *priv, break; default: - netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)", + netdev_warn(priv->netdev, "Unsupported msg (0x%X)", msg->cmd_id); break; } diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 251835ea15aa..573b11559d73 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -309,7 +309,7 @@ static void peak_usb_write_bulk_callback(struct urb *urb) } /* should always release echo skb and corresponding context */ - can_get_echo_skb(netdev, context->echo_index); + can_get_echo_skb(netdev, context->echo_index, NULL); context->echo_index = PCAN_USB_MAX_TX_URBS; /* do wakeup tx queue in case of success only */ @@ -365,7 +365,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, usb_anchor_urb(urb, &dev->tx_submitted); - can_put_echo_skb(skb, netdev, context->echo_index); + can_put_echo_skb(skb, netdev, context->echo_index, 0); atomic_inc(&dev->active_tx_urbs); diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index 7d92da8954fe..fa403c080871 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -672,7 +672,7 @@ static void ucan_tx_complete_msg(struct ucan_priv *up, /* update statistics */ up->netdev->stats.tx_packets++; up->netdev->stats.tx_bytes += dlc; - can_get_echo_skb(up->netdev, echo_index); + can_get_echo_skb(up->netdev, echo_index, NULL); } else { up->netdev->stats.tx_dropped++; can_free_echo_skb(up->netdev, echo_index); @@ -1137,7 +1137,7 @@ static netdev_tx_t ucan_start_xmit(struct sk_buff *skb, /* put the skb on can loopback stack */ spin_lock_irqsave(&up->echo_skb_lock, flags); - can_put_echo_skb(skb, up->netdev, echo_index); + can_put_echo_skb(skb, up->netdev, echo_index, 0); spin_unlock_irqrestore(&up->echo_skb_lock, flags); /* transmit it */ diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c index 44478304ff46..e8c42430a4fc 100644 --- a/drivers/net/can/usb/usb_8dev.c +++ b/drivers/net/can/usb/usb_8dev.c @@ -585,7 +585,7 @@ static void usb_8dev_write_bulk_callback(struct urb *urb) netdev->stats.tx_packets++; netdev->stats.tx_bytes += context->dlc; - can_get_echo_skb(netdev, context->echo_index); + can_get_echo_skb(netdev, context->echo_index, NULL); can_led_event(netdev, CAN_LED_EVENT_TX); @@ -664,7 +664,7 @@ static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb, urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &priv->tx_submitted); - can_put_echo_skb(skb, netdev, context->echo_index); + can_put_echo_skb(skb, netdev, context->echo_index, 0); atomic_inc(&priv->active_tx_urbs); diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index 3f54edee92eb..37fa19c62d73 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -592,9 +592,9 @@ static void xcan_write_frame(struct net_device *ndev, struct sk_buff *skb, if (!(priv->devtype.flags & XCAN_FLAG_TX_MAILBOXES) && (priv->devtype.flags & XCAN_FLAG_TXFEMP)) - can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max); + can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max, 0); else - can_put_echo_skb(skb, ndev, 0); + can_put_echo_skb(skb, ndev, 0, 0); priv->tx_head++; @@ -1292,7 +1292,7 @@ static void xcan_tx_interrupt(struct net_device *ndev, u32 isr) while (frames_sent--) { stats->tx_bytes += can_get_echo_skb(ndev, priv->tx_tail % - priv->tx_max); + priv->tx_max, NULL); priv->tx_tail++; stats->tx_packets++; } diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index f6a0488589fc..3af373e90806 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -60,6 +60,8 @@ source "drivers/net/dsa/qca/Kconfig" source "drivers/net/dsa/sja1105/Kconfig" +source "drivers/net/dsa/xrs700x/Kconfig" + config NET_DSA_QCA8K tristate "Qualcomm Atheros QCA8K Ethernet switch family support" depends on NET_DSA diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index a84adb140a04..f3598c040994 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -24,3 +24,4 @@ obj-y += mv88e6xxx/ obj-y += ocelot/ obj-y += qca/ obj-y += sja1105/ +obj-y += xrs700x/ diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 95c7fa171e35..23fc7225c8d1 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1374,26 +1374,22 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, } EXPORT_SYMBOL(b53_phylink_mac_link_up); -int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, - struct switchdev_trans *trans) +int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) { struct b53_device *dev = ds->priv; - if (switchdev_trans_ph_prepare(trans)) - return 0; - b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering); return 0; } EXPORT_SYMBOL(b53_vlan_filtering); -int b53_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int b53_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct b53_device *dev = ds->priv; - if ((is5325(dev) || is5365(dev)) && vlan->vid_begin == 0) + if ((is5325(dev) || is5365(dev)) && vlan->vid == 0) return -EOPNOTSUPP; /* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of @@ -1404,47 +1400,50 @@ int b53_vlan_prepare(struct dsa_switch *ds, int port, !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) return -EINVAL; - if (vlan->vid_end >= dev->num_vlans) + if (vlan->vid >= dev->num_vlans) return -ERANGE; b53_enable_vlan(dev, true, ds->vlan_filtering); return 0; } -EXPORT_SYMBOL(b53_vlan_prepare); -void b53_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +int b53_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct b53_device *dev = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; struct b53_vlan *vl; - u16 vid; + int err; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - vl = &dev->vlans[vid]; + err = b53_vlan_prepare(ds, port, vlan); + if (err) + return err; - b53_get_vlan_entry(dev, vid, vl); + vl = &dev->vlans[vlan->vid]; - if (vid == 0 && vid == b53_default_pvid(dev)) - untagged = true; + b53_get_vlan_entry(dev, vlan->vid, vl); - vl->members |= BIT(port); - if (untagged && !dsa_is_cpu_port(ds, port)) - vl->untag |= BIT(port); - else - vl->untag &= ~BIT(port); + if (vlan->vid == 0 && vlan->vid == b53_default_pvid(dev)) + untagged = true; - b53_set_vlan_entry(dev, vid, vl); - b53_fast_age_vlan(dev, vid); - } + vl->members |= BIT(port); + if (untagged && !dsa_is_cpu_port(ds, port)) + vl->untag |= BIT(port); + else + vl->untag &= ~BIT(port); + + b53_set_vlan_entry(dev, vlan->vid, vl); + b53_fast_age_vlan(dev, vlan->vid); if (pvid && !dsa_is_cpu_port(ds, port)) { b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), - vlan->vid_end); - b53_fast_age_vlan(dev, vid); + vlan->vid); + b53_fast_age_vlan(dev, vlan->vid); } + + return 0; } EXPORT_SYMBOL(b53_vlan_add); @@ -1454,27 +1453,24 @@ int b53_vlan_del(struct dsa_switch *ds, int port, struct b53_device *dev = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct b53_vlan *vl; - u16 vid; u16 pvid; b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - vl = &dev->vlans[vid]; + vl = &dev->vlans[vlan->vid]; - b53_get_vlan_entry(dev, vid, vl); + b53_get_vlan_entry(dev, vlan->vid, vl); - vl->members &= ~BIT(port); + vl->members &= ~BIT(port); - if (pvid == vid) - pvid = b53_default_pvid(dev); + if (pvid == vlan->vid) + pvid = b53_default_pvid(dev); - if (untagged && !dsa_is_cpu_port(ds, port)) - vl->untag &= ~(BIT(port)); + if (untagged && !dsa_is_cpu_port(ds, port)) + vl->untag &= ~(BIT(port)); - b53_set_vlan_entry(dev, vid, vl); - b53_fast_age_vlan(dev, vid); - } + b53_set_vlan_entry(dev, vlan->vid, vl); + b53_fast_age_vlan(dev, vlan->vid); b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), pvid); b53_fast_age_vlan(dev, pvid); @@ -1751,8 +1747,8 @@ int b53_fdb_dump(struct dsa_switch *ds, int port, } EXPORT_SYMBOL(b53_fdb_dump); -int b53_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +int b53_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) { struct b53_device *priv = ds->priv; @@ -1762,19 +1758,7 @@ int b53_mdb_prepare(struct dsa_switch *ds, int port, if (is5325(priv) || is5365(priv)) return -EOPNOTSUPP; - return 0; -} -EXPORT_SYMBOL(b53_mdb_prepare); - -void b53_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) -{ - struct b53_device *priv = ds->priv; - int ret; - - ret = b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, true); - if (ret) - dev_err(ds->dev, "failed to add MDB entry\n"); + return b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, true); } EXPORT_SYMBOL(b53_mdb_add); @@ -2207,7 +2191,6 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_fast_age = b53_br_fast_age, .port_egress_floods = b53_br_egress_floods, .port_vlan_filtering = b53_vlan_filtering, - .port_vlan_prepare = b53_vlan_prepare, .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, .port_fdb_dump = b53_fdb_dump, @@ -2215,7 +2198,6 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_fdb_del = b53_fdb_del, .port_mirror_add = b53_mirror_add, .port_mirror_del = b53_mirror_del, - .port_mdb_prepare = b53_mdb_prepare, .port_mdb_add = b53_mdb_add, .port_mdb_del = b53_mdb_del, .port_max_mtu = b53_get_max_mtu, @@ -2459,6 +2441,20 @@ static const struct b53_chip_data b53_switch_chips[] = { .jumbo_pm_reg = B53_JUMBO_PORT_MASK, .jumbo_size_reg = B53_JUMBO_MAX_SIZE, }, + /* Starfighter 2 */ + { + .chip_id = BCM4908_DEVICE_ID, + .dev_name = "BCM4908", + .vlans = 4096, + .enabled_ports = 0x1bf, + .arl_bins = 4, + .arl_buckets = 256, + .cpu_port = 8, /* TODO: ports 4, 5, 8 */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + }, { .chip_id = BCM7445_DEVICE_ID, .dev_name = "BCM7445", @@ -2606,9 +2602,8 @@ struct b53_device *b53_switch_alloc(struct device *base, dev->priv = priv; dev->ops = ops; ds->ops = &b53_switch_ops; - ds->configure_vlan_while_not_filtering = true; ds->untag_bridge_pvid = true; - dev->vlan_enabled = ds->configure_vlan_while_not_filtering; + dev->vlan_enabled = true; mutex_init(&dev->reg_mutex); mutex_init(&dev->stats_mutex); diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 7c67409bb186..0d2cc0453bef 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -64,6 +64,7 @@ struct b53_io_ops { #define B53_INVALID_LANE 0xff enum { + BCM4908_DEVICE_ID = 0x4908, BCM5325_DEVICE_ID = 0x25, BCM5365_DEVICE_ID = 0x65, BCM5389_DEVICE_ID = 0x89, @@ -347,12 +348,9 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, struct phy_device *phydev, int speed, int duplex, bool tx_pause, bool rx_pause); -int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, - struct switchdev_trans *trans); -int b53_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan); -void b53_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan); +int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering); +int b53_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); int b53_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); int b53_fdb_add(struct dsa_switch *ds, int port, @@ -361,10 +359,8 @@ int b53_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int b53_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); -int b53_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); -void b53_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); +int b53_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb); int b53_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb); int b53_mirror_add(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 445226720ff2..1857aa9aa84a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -105,7 +105,8 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) b53_brcm_hdr_setup(ds, port); if (port == 8) { - if (priv->type == BCM7445_DEVICE_ID) + if (priv->type == BCM4908_DEVICE_ID || + priv->type == BCM7445_DEVICE_ID) offset = CORE_STS_OVERRIDE_IMP; else offset = CORE_STS_OVERRIDE_IMP2; @@ -719,7 +720,8 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port, u32 reg, offset; if (port != core_readl(priv, CORE_IMP0_PRT_ID)) { - if (priv->type == BCM7445_DEVICE_ID) + if (priv->type == BCM4908_DEVICE_ID || + priv->type == BCM7445_DEVICE_ID) offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); else offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); @@ -746,7 +748,8 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, bcm_sf2_sw_mac_link_set(ds, port, interface, true); if (port != core_readl(priv, CORE_IMP0_PRT_ID)) { - if (priv->type == BCM7445_DEVICE_ID) + if (priv->type == BCM4908_DEVICE_ID || + priv->type == BCM7445_DEVICE_ID) offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); else offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); @@ -1117,7 +1120,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .port_stp_state_set = b53_br_set_stp_state, .port_fast_age = b53_br_fast_age, .port_vlan_filtering = b53_vlan_filtering, - .port_vlan_prepare = b53_vlan_prepare, .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, .port_fdb_dump = b53_fdb_dump, @@ -1127,7 +1129,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .set_rxnfc = bcm_sf2_set_rxnfc, .port_mirror_add = b53_mirror_add, .port_mirror_del = b53_mirror_del, - .port_mdb_prepare = b53_mdb_prepare, .port_mdb_add = b53_mdb_add, .port_mdb_del = b53_mdb_del, }; @@ -1139,6 +1140,30 @@ struct bcm_sf2_of_data { unsigned int num_cfp_rules; }; +static const u16 bcm_sf2_4908_reg_offsets[] = { + [REG_SWITCH_CNTRL] = 0x00, + [REG_SWITCH_STATUS] = 0x04, + [REG_DIR_DATA_WRITE] = 0x08, + [REG_DIR_DATA_READ] = 0x0c, + [REG_SWITCH_REVISION] = 0x10, + [REG_PHY_REVISION] = 0x14, + [REG_SPHY_CNTRL] = 0x24, + [REG_CROSSBAR] = 0xc8, + [REG_RGMII_0_CNTRL] = 0xe0, + [REG_RGMII_1_CNTRL] = 0xec, + [REG_RGMII_2_CNTRL] = 0xf8, + [REG_LED_0_CNTRL] = 0x40, + [REG_LED_1_CNTRL] = 0x4c, + [REG_LED_2_CNTRL] = 0x58, +}; + +static const struct bcm_sf2_of_data bcm_sf2_4908_data = { + .type = BCM4908_DEVICE_ID, + .core_reg_align = 0, + .reg_offsets = bcm_sf2_4908_reg_offsets, + .num_cfp_rules = 0, /* FIXME */ +}; + /* Register offsets for the SWITCH_REG_* block */ static const u16 bcm_sf2_7445_reg_offsets[] = { [REG_SWITCH_CNTRL] = 0x00, @@ -1187,6 +1212,9 @@ static const struct bcm_sf2_of_data bcm_sf2_7278_data = { }; static const struct of_device_id bcm_sf2_of_match[] = { + { .compatible = "brcm,bcm4908-switch", + .data = &bcm_sf2_4908_data + }, { .compatible = "brcm,bcm7445-switch-v4.0", .data = &bcm_sf2_7445_data }, diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index d82cee5d9202..ed45d16250e1 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -885,18 +885,15 @@ static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port, return -EINVAL; vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK; - vlan.vid_begin = vid; - vlan.vid_end = vid; + vlan.vid = vid; if (cpu_to_be32(fs->h_ext.data[1]) & 1) vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED; else vlan.flags = 0; - ret = ds->ops->port_vlan_prepare(ds, port_num, &vlan); + ret = ds->ops->port_vlan_add(ds, port_num, &vlan); if (ret) return ret; - - ds->ops->port_vlan_add(ds, port_num, &vlan); } /* @@ -942,8 +939,7 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, return -EINVAL; if ((fs->flow_type & FLOW_EXT) && - !(ds->ops->port_vlan_prepare || ds->ops->port_vlan_add || - ds->ops->port_vlan_del)) + !(ds->ops->port_vlan_add || ds->ops->port_vlan_del)) return -EOPNOTSUPP; if (fs->location != RX_CLS_LOC_ANY && diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index d8a5e6269c0e..1d2d55c9f8aa 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h @@ -17,6 +17,7 @@ enum bcm_sf2_reg_offs { REG_SWITCH_REVISION, REG_PHY_REVISION, REG_SPHY_CNTRL, + REG_CROSSBAR, REG_RGMII_0_CNTRL, REG_RGMII_1_CNTRL, REG_RGMII_2_CNTRL, diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index e38906ae8f23..8c283f59158b 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -190,8 +190,7 @@ static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port, } static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering, - struct switchdev_trans *trans) + bool vlan_filtering) { dev_dbg(ds->dev, "%s: port: %d, vlan_filtering: %d\n", __func__, port, vlan_filtering); @@ -199,53 +198,36 @@ static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port, return 0; } -static int -dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct dsa_loop_priv *ps = ds->priv; - struct mii_bus *bus = ps->bus; - - dev_dbg(ds->dev, "%s: port: %d, vlan: %d-%d", - __func__, port, vlan->vid_begin, vlan->vid_end); - - /* Just do a sleeping operation to make lockdep checks effective */ - mdiobus_read(bus, ps->port_base + port, MII_BMSR); - - if (vlan->vid_end > ARRAY_SIZE(ps->vlans)) - return -ERANGE; - - return 0; -} - -static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int dsa_loop_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; struct dsa_loop_vlan *vl; - u16 vid; + + if (vlan->vid >= ARRAY_SIZE(ps->vlans)) + return -ERANGE; /* Just do a sleeping operation to make lockdep checks effective */ mdiobus_read(bus, ps->port_base + port, MII_BMSR); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - vl = &ps->vlans[vid]; + vl = &ps->vlans[vlan->vid]; - vl->members |= BIT(port); - if (untagged) - vl->untagged |= BIT(port); - else - vl->untagged &= ~BIT(port); + vl->members |= BIT(port); + if (untagged) + vl->untagged |= BIT(port); + else + vl->untagged &= ~BIT(port); - dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", - __func__, port, vid, untagged ? "un" : "", pvid); - } + dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", + __func__, port, vlan->vid, untagged ? "un" : "", pvid); if (pvid) - ps->ports[port].pvid = vid; + ps->ports[port].pvid = vlan->vid; + + return 0; } static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, @@ -253,26 +235,24 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct dsa_loop_priv *ps = ds->priv; + u16 pvid = ps->ports[port].pvid; struct mii_bus *bus = ps->bus; struct dsa_loop_vlan *vl; - u16 vid, pvid = ps->ports[port].pvid; /* Just do a sleeping operation to make lockdep checks effective */ mdiobus_read(bus, ps->port_base + port, MII_BMSR); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - vl = &ps->vlans[vid]; + vl = &ps->vlans[vlan->vid]; - vl->members &= ~BIT(port); - if (untagged) - vl->untagged &= ~BIT(port); + vl->members &= ~BIT(port); + if (untagged) + vl->untagged &= ~BIT(port); - if (pvid == vid) - pvid = 1; + if (pvid == vlan->vid) + pvid = 1; - dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", - __func__, port, vid, untagged ? "un" : "", pvid); - } + dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", + __func__, port, vlan->vid, untagged ? "un" : "", pvid); ps->ports[port].pvid = pvid; return 0; @@ -307,7 +287,6 @@ static const struct dsa_switch_ops dsa_loop_driver = { .port_bridge_leave = dsa_loop_port_bridge_leave, .port_stp_state_set = dsa_loop_port_stp_state_set, .port_vlan_filtering = dsa_loop_port_vlan_filtering, - .port_vlan_prepare = dsa_loop_port_vlan_prepare, .port_vlan_add = dsa_loop_port_vlan_add, .port_vlan_del = dsa_loop_port_vlan_del, .port_change_mtu = dsa_loop_port_change_mtu, @@ -344,7 +323,6 @@ static int dsa_loop_drv_probe(struct mdio_device *mdiodev) ds->dev = &mdiodev->dev; ds->ops = &dsa_loop_driver; ds->priv = ps; - ds->configure_vlan_while_not_filtering = true; ps->bus = mdiodev->bus; dev_set_drvdata(&mdiodev->dev, ds); diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index 6420b76ea37c..4cc51fb37e67 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -3,7 +3,7 @@ * DSA driver for: * Hirschmann Hellcreek TSN switch. * - * Copyright (C) 2019,2020 Linutronix GmbH + * Copyright (C) 2019-2021 Linutronix GmbH * Author Kurt Kanzenbach <kurt@linutronix.de> */ @@ -153,6 +153,13 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid, hellcreek_write(hellcreek, val, HR_VIDCFG); } +static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port) +{ + u16 val = port << TR_TGDSEL_TDGSEL_SHIFT; + + hellcreek_write(hellcreek, val, TR_TGDSEL); +} + static int hellcreek_wait_until_ready(struct hellcreek *hellcreek) { u16 val; @@ -348,14 +355,12 @@ static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port, */ for (i = 0; i < hellcreek->pdata->num_ports; ++i) { const u16 restricted_vid = hellcreek_private_vid(i); - u16 vid; if (!dsa_is_user_port(ds, i)) continue; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - if (vid == restricted_vid) - return -EBUSY; + if (vlan->vid == restricted_vid) + return -EBUSY; } return 0; @@ -440,34 +445,35 @@ static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port, mutex_unlock(&hellcreek->reg_lock); } -static void hellcreek_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int hellcreek_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; struct hellcreek *hellcreek = ds->priv; - u16 vid; + int err; + + err = hellcreek_vlan_prepare(ds, port, vlan); + if (err) + return err; - dev_dbg(hellcreek->dev, "Add VLANs (%d -- %d) on port %d, %s, %s\n", - vlan->vid_begin, vlan->vid_end, port, - untagged ? "untagged" : "tagged", + dev_dbg(hellcreek->dev, "Add VLAN %d on port %d, %s, %s\n", + vlan->vid, port, untagged ? "untagged" : "tagged", pvid ? "PVID" : "no PVID"); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - hellcreek_apply_vlan(hellcreek, port, vid, pvid, untagged); + hellcreek_apply_vlan(hellcreek, port, vlan->vid, pvid, untagged); + + return 0; } static int hellcreek_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct hellcreek *hellcreek = ds->priv; - u16 vid; - dev_dbg(hellcreek->dev, "Remove VLANs (%d -- %d) on port %d\n", - vlan->vid_begin, vlan->vid_end, port); + dev_dbg(hellcreek->dev, "Remove VLAN %d on port %d\n", vlan->vid, port); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - hellcreek_unapply_vlan(hellcreek, port, vid); + hellcreek_unapply_vlan(hellcreek, port, vlan->vid); return 0; } @@ -866,14 +872,10 @@ static int hellcreek_fdb_dump(struct dsa_switch *ds, int port, } static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering, - struct switchdev_trans *trans) + bool vlan_filtering) { struct hellcreek *hellcreek = ds->priv; - if (switchdev_trans_ph_prepare(trans)) - return 0; - dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n", vlan_filtering ? "Enable" : "Disable", port); @@ -1038,11 +1040,6 @@ static int hellcreek_setup(struct dsa_switch *ds) /* Configure PCP <-> TC mapping */ hellcreek_setup_tc_identity_mapping(hellcreek); - /* Allow VLAN configurations while not filtering which is the default - * for new DSA drivers. - */ - ds->configure_vlan_while_not_filtering = true; - /* The VLAN awareness is a global switch setting. Therefore, mixed vlan * filtering setups are not supported. */ @@ -1135,6 +1132,296 @@ out: return ret; } +static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port, + const struct tc_taprio_qopt_offload *schedule) +{ + const struct tc_taprio_sched_entry *cur, *initial, *next; + size_t i; + + cur = initial = &schedule->entries[0]; + next = cur + 1; + + for (i = 1; i <= schedule->num_entries; ++i) { + u16 data; + u8 gates; + + cur++; + next++; + + if (i == schedule->num_entries) + gates = initial->gate_mask ^ + cur->gate_mask; + else + gates = next->gate_mask ^ + cur->gate_mask; + + data = gates; + + if (i == schedule->num_entries) + data |= TR_GCLDAT_GCLWRLAST; + + /* Gates states */ + hellcreek_write(hellcreek, data, TR_GCLDAT); + + /* Time interval */ + hellcreek_write(hellcreek, + cur->interval & 0x0000ffff, + TR_GCLTIL); + hellcreek_write(hellcreek, + (cur->interval & 0xffff0000) >> 16, + TR_GCLTIH); + + /* Commit entry */ + data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) | + (initial->gate_mask << + TR_GCLCMD_INIT_GATE_STATES_SHIFT); + hellcreek_write(hellcreek, data, TR_GCLCMD); + } +} + +static void hellcreek_set_cycle_time(struct hellcreek *hellcreek, + const struct tc_taprio_qopt_offload *schedule) +{ + u32 cycle_time = schedule->cycle_time; + + hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL); + hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH); +} + +static void hellcreek_switch_schedule(struct hellcreek *hellcreek, + ktime_t start_time) +{ + struct timespec64 ts = ktime_to_timespec64(start_time); + + /* Start schedule at this point of time */ + hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL); + hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH); + + /* Arm timer, set seconds and switch schedule */ + hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG | + ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) << + TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD); +} + +static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port) +{ + struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; + s64 base_time_ns, current_ns; + + /* The switch allows a schedule to be started only eight seconds within + * the future. Therefore, check the current PTP time if the schedule is + * startable or not. + */ + + /* Use the "cached" time. That should be alright, as it's updated quite + * frequently in the PTP code. + */ + mutex_lock(&hellcreek->ptp_lock); + current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts; + mutex_unlock(&hellcreek->ptp_lock); + + /* Calculate difference to admin base time */ + base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time); + + return base_time_ns - current_ns < (s64)8 * NSEC_PER_SEC; +} + +static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port) +{ + struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; + ktime_t base_time, current_time; + s64 current_ns; + u32 cycle_time; + + /* First select port */ + hellcreek_select_tgd(hellcreek, port); + + /* Forward base time into the future if needed */ + mutex_lock(&hellcreek->ptp_lock); + current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts; + mutex_unlock(&hellcreek->ptp_lock); + + current_time = ns_to_ktime(current_ns); + base_time = hellcreek_port->current_schedule->base_time; + cycle_time = hellcreek_port->current_schedule->cycle_time; + + if (ktime_compare(current_time, base_time) > 0) { + s64 n; + + n = div64_s64(ktime_sub_ns(current_time, base_time), + cycle_time); + base_time = ktime_add_ns(base_time, (n + 1) * cycle_time); + } + + /* Set admin base time and switch schedule */ + hellcreek_switch_schedule(hellcreek, base_time); + + taprio_offload_free(hellcreek_port->current_schedule); + hellcreek_port->current_schedule = NULL; + + dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n", + hellcreek_port->port); +} + +static void hellcreek_check_schedule(struct work_struct *work) +{ + struct delayed_work *dw = to_delayed_work(work); + struct hellcreek_port *hellcreek_port; + struct hellcreek *hellcreek; + bool startable; + + hellcreek_port = dw_to_hellcreek_port(dw); + hellcreek = hellcreek_port->hellcreek; + + mutex_lock(&hellcreek->reg_lock); + + /* Check starting time */ + startable = hellcreek_schedule_startable(hellcreek, + hellcreek_port->port); + if (startable) { + hellcreek_start_schedule(hellcreek, hellcreek_port->port); + mutex_unlock(&hellcreek->reg_lock); + return; + } + + mutex_unlock(&hellcreek->reg_lock); + + /* Reschedule */ + schedule_delayed_work(&hellcreek_port->schedule_work, + HELLCREEK_SCHEDULE_PERIOD); +} + +static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port, + struct tc_taprio_qopt_offload *taprio) +{ + struct hellcreek *hellcreek = ds->priv; + struct hellcreek_port *hellcreek_port; + bool startable; + u16 ctrl; + + hellcreek_port = &hellcreek->ports[port]; + + dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n", + port); + + /* First cancel delayed work */ + cancel_delayed_work_sync(&hellcreek_port->schedule_work); + + mutex_lock(&hellcreek->reg_lock); + + if (hellcreek_port->current_schedule) { + taprio_offload_free(hellcreek_port->current_schedule); + hellcreek_port->current_schedule = NULL; + } + hellcreek_port->current_schedule = taprio_offload_get(taprio); + + /* Then select port */ + hellcreek_select_tgd(hellcreek, port); + + /* Enable gating and keep defaults */ + ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN; + hellcreek_write(hellcreek, ctrl, TR_TGDCTRL); + + /* Cancel pending schedule */ + hellcreek_write(hellcreek, 0x00, TR_ESTCMD); + + /* Setup a new schedule */ + hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule); + + /* Configure cycle time */ + hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule); + + /* Check starting time */ + startable = hellcreek_schedule_startable(hellcreek, port); + if (startable) { + hellcreek_start_schedule(hellcreek, port); + mutex_unlock(&hellcreek->reg_lock); + return 0; + } + + mutex_unlock(&hellcreek->reg_lock); + + /* Schedule periodic schedule check */ + schedule_delayed_work(&hellcreek_port->schedule_work, + HELLCREEK_SCHEDULE_PERIOD); + + return 0; +} + +static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port) +{ + struct hellcreek *hellcreek = ds->priv; + struct hellcreek_port *hellcreek_port; + + hellcreek_port = &hellcreek->ports[port]; + + dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port); + + /* First cancel delayed work */ + cancel_delayed_work_sync(&hellcreek_port->schedule_work); + + mutex_lock(&hellcreek->reg_lock); + + if (hellcreek_port->current_schedule) { + taprio_offload_free(hellcreek_port->current_schedule); + hellcreek_port->current_schedule = NULL; + } + + /* Then select port */ + hellcreek_select_tgd(hellcreek, port); + + /* Disable gating and return to regular switching flow */ + hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT, + TR_TGDCTRL); + + mutex_unlock(&hellcreek->reg_lock); + + return 0; +} + +static bool hellcreek_validate_schedule(struct hellcreek *hellcreek, + struct tc_taprio_qopt_offload *schedule) +{ + size_t i; + + /* Does this hellcreek version support Qbv in hardware? */ + if (!hellcreek->pdata->qbv_support) + return false; + + /* cycle time can only be 32bit */ + if (schedule->cycle_time > (u32)-1) + return false; + + /* cycle time extension is not supported */ + if (schedule->cycle_time_extension) + return false; + + /* Only set command is supported */ + for (i = 0; i < schedule->num_entries; ++i) + if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES) + return false; + + return true; +} + +static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data) +{ + struct tc_taprio_qopt_offload *taprio = type_data; + struct hellcreek *hellcreek = ds->priv; + + if (type != TC_SETUP_QDISC_TAPRIO) + return -EOPNOTSUPP; + + if (!hellcreek_validate_schedule(hellcreek, taprio)) + return -EOPNOTSUPP; + + if (taprio->enable) + return hellcreek_port_set_schedule(ds, port, taprio); + + return hellcreek_port_del_schedule(ds, port); +} + static const struct dsa_switch_ops hellcreek_ds_ops = { .get_ethtool_stats = hellcreek_get_ethtool_stats, .get_sset_count = hellcreek_get_sset_count, @@ -1153,12 +1440,12 @@ static const struct dsa_switch_ops hellcreek_ds_ops = { .port_hwtstamp_get = hellcreek_port_hwtstamp_get, .port_prechangeupper = hellcreek_port_prechangeupper, .port_rxtstamp = hellcreek_port_rxtstamp, + .port_setup_tc = hellcreek_port_setup_tc, .port_stp_state_set = hellcreek_port_stp_state_set, .port_txtstamp = hellcreek_port_txtstamp, .port_vlan_add = hellcreek_vlan_add, .port_vlan_del = hellcreek_vlan_del, .port_vlan_filtering = hellcreek_vlan_filtering, - .port_vlan_prepare = hellcreek_vlan_prepare, .setup = hellcreek_setup, }; @@ -1208,6 +1495,9 @@ static int hellcreek_probe(struct platform_device *pdev) port->hellcreek = hellcreek; port->port = i; + + INIT_DELAYED_WORK(&port->schedule_work, + hellcreek_check_schedule); } mutex_init(&hellcreek->reg_lock); diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h index e81781ebc31c..854639f87247 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.h +++ b/drivers/net/dsa/hirschmann/hellcreek.h @@ -3,7 +3,7 @@ * DSA driver for: * Hirschmann Hellcreek TSN switch. * - * Copyright (C) 2019,2020 Linutronix GmbH + * Copyright (C) 2019-2021 Linutronix GmbH * Author Kurt Kanzenbach <kurt@linutronix.de> */ @@ -21,6 +21,7 @@ #include <linux/ptp_clock_kernel.h> #include <linux/timecounter.h> #include <net/dsa.h> +#include <net/pkt_sched.h> /* Ports: * - 0: CPU @@ -246,6 +247,10 @@ struct hellcreek_port { /* Per-port timestamping resources */ struct hellcreek_port_hwtstamp port_hwtstamp; + + /* Per-port Qbv schedule information */ + struct tc_taprio_qopt_offload *current_schedule; + struct delayed_work schedule_work; }; struct hellcreek_fdb_entry { @@ -283,4 +288,14 @@ struct hellcreek { size_t fdb_entries; }; +/* A Qbv schedule can only started up to 8 seconds in the future. If the delta + * between the base time and the current ptp time is larger than 8 seconds, then + * use periodic work to check for the schedule to be started. The delayed work + * cannot be armed directly to $base_time - 8 + X, because for large deltas the + * PTP frequency matters. + */ +#define HELLCREEK_SCHEDULE_PERIOD (2 * HZ) +#define dw_to_hellcreek_port(dw) \ + container_of(dw, struct hellcreek_port, schedule_work) + #endif /* _HELLCREEK_H_ */ diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index aa1142d6a9f5..344374025426 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1232,14 +1232,19 @@ static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port, return 0; } -static void lan9303_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static int lan9303_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) { struct lan9303 *chip = ds->priv; + int err; + + err = lan9303_port_mdb_prepare(ds, port, mdb); + if (err) + return err; dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr, mdb->vid); - lan9303_alr_add_port(chip, mdb->addr, port, false); + return lan9303_alr_add_port(chip, mdb->addr, port, false); } static int lan9303_port_mdb_del(struct dsa_switch *ds, int port, @@ -1274,7 +1279,6 @@ static const struct dsa_switch_ops lan9303_switch_ops = { .port_fdb_add = lan9303_port_fdb_add, .port_fdb_del = lan9303_port_fdb_del, .port_fdb_dump = lan9303_port_fdb_dump, - .port_mdb_prepare = lan9303_port_mdb_prepare, .port_mdb_add = lan9303_port_mdb_add, .port_mdb_del = lan9303_port_mdb_del, }; diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 662e68a0e7e6..9fec97773a15 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -727,23 +727,14 @@ static int gswip_pce_load_microcode(struct gswip_priv *priv) } static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering, - struct switchdev_trans *trans) + bool vlan_filtering) { + struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; struct gswip_priv *priv = ds->priv; /* Do not allow changing the VLAN filtering options while in bridge */ - if (switchdev_trans_ph_prepare(trans)) { - struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; - - if (!bridge) - return 0; - - if (!!(priv->port_vlan_filter & BIT(port)) != vlan_filtering) - return -EIO; - - return 0; - } + if (bridge && !!(priv->port_vlan_filter & BIT(port)) != vlan_filtering) + return -EIO; if (vlan_filtering) { /* Use port based VLAN tag */ @@ -781,15 +772,8 @@ static int gswip_setup(struct dsa_switch *ds) /* disable port fetch/store dma on all ports */ for (i = 0; i < priv->hw_info->max_ports; i++) { - struct switchdev_trans trans; - - /* Skip the prepare phase, this shouldn't return an error - * during setup. - */ - trans.ph_prepare = false; - gswip_port_disable(ds, i); - gswip_port_vlan_filtering(ds, i, false, &trans); + gswip_port_vlan_filtering(ds, i, false); } /* enable Switch */ @@ -843,6 +827,9 @@ static int gswip_setup(struct dsa_switch *ds) } gswip_port_enable(ds, cpu_port, NULL); + + ds->configure_vlan_while_not_filtering = false; + return 0; } @@ -1146,56 +1133,55 @@ static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port, struct gswip_priv *priv = ds->priv; struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; unsigned int max_ports = priv->hw_info->max_ports; - u16 vid; - int i; int pos = max_ports; + int i, idx = -1; /* We only support VLAN filtering on bridges */ if (!dsa_is_cpu_port(ds, port) && !bridge) return -EOPNOTSUPP; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - int idx = -1; + /* Check if there is already a page for this VLAN */ + for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { + if (priv->vlans[i].bridge == bridge && + priv->vlans[i].vid == vlan->vid) { + idx = i; + break; + } + } - /* Check if there is already a page for this VLAN */ - for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { - if (priv->vlans[i].bridge == bridge && - priv->vlans[i].vid == vid) { - idx = i; + /* If this VLAN is not programmed yet, we have to reserve + * one entry in the VLAN table. Make sure we start at the + * next position round. + */ + if (idx == -1) { + /* Look for a free slot */ + for (; pos < ARRAY_SIZE(priv->vlans); pos++) { + if (!priv->vlans[pos].bridge) { + idx = pos; + pos++; break; } } - /* If this VLAN is not programmed yet, we have to reserve - * one entry in the VLAN table. Make sure we start at the - * next position round. - */ - if (idx == -1) { - /* Look for a free slot */ - for (; pos < ARRAY_SIZE(priv->vlans); pos++) { - if (!priv->vlans[pos].bridge) { - idx = pos; - pos++; - break; - } - } - - if (idx == -1) - return -ENOSPC; - } + if (idx == -1) + return -ENOSPC; } return 0; } -static void gswip_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int gswip_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct gswip_priv *priv = ds->priv; struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; - u16 vid; + int err; + + err = gswip_port_vlan_prepare(ds, port, vlan); + if (err) + return err; /* We have to receive all packets on the CPU port and should not * do any VLAN filtering here. This is also called with bridge @@ -1203,10 +1189,10 @@ static void gswip_port_vlan_add(struct dsa_switch *ds, int port, * this. */ if (dsa_is_cpu_port(ds, port)) - return; + return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - gswip_vlan_add_aware(priv, bridge, port, vid, untagged, pvid); + return gswip_vlan_add_aware(priv, bridge, port, vlan->vid, + untagged, pvid); } static int gswip_port_vlan_del(struct dsa_switch *ds, int port, @@ -1215,8 +1201,6 @@ static int gswip_port_vlan_del(struct dsa_switch *ds, int port, struct gswip_priv *priv = ds->priv; struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; - u16 vid; - int err; /* We have to receive all packets on the CPU port and should not * do any VLAN filtering here. This is also called with bridge @@ -1226,13 +1210,7 @@ static int gswip_port_vlan_del(struct dsa_switch *ds, int port, if (dsa_is_cpu_port(ds, port)) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - err = gswip_vlan_remove(priv, bridge, port, vid, pvid, true); - if (err) - return err; - } - - return 0; + return gswip_vlan_remove(priv, bridge, port, vlan->vid, pvid, true); } static void gswip_port_fast_age(struct dsa_switch *ds, int port) @@ -1611,7 +1589,6 @@ static const struct dsa_switch_ops gswip_switch_ops = { .port_bridge_leave = gswip_port_bridge_leave, .port_fast_age = gswip_port_fast_age, .port_vlan_filtering = gswip_port_vlan_filtering, - .port_vlan_prepare = gswip_port_vlan_prepare, .port_vlan_add = gswip_port_vlan_add, .port_vlan_del = gswip_port_vlan_del, .port_stp_state_set = gswip_port_stp_state_set, diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index a4570ba29c83..c87d445b30fd 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -783,55 +783,53 @@ static void ksz8795_flush_dyn_mac_table(struct ksz_device *dev, int port) } static int ksz8795_port_vlan_filtering(struct dsa_switch *ds, int port, - bool flag, - struct switchdev_trans *trans) + bool flag) { struct ksz_device *dev = ds->priv; - if (switchdev_trans_ph_prepare(trans)) - return 0; - ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag); return 0; } -static void ksz8795_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int ksz8795_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct ksz_device *dev = ds->priv; - u16 data, vid, new_pvid = 0; + u16 data, new_pvid = 0; u8 fid, member, valid; ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - ksz8795_r_vlan_table(dev, vid, &data); - ksz8795_from_vlan(data, &fid, &member, &valid); + ksz8795_r_vlan_table(dev, vlan->vid, &data); + ksz8795_from_vlan(data, &fid, &member, &valid); - /* First time to setup the VLAN entry. */ - if (!valid) { - /* Need to find a way to map VID to FID. */ - fid = 1; - valid = 1; - } - member |= BIT(port); + /* First time to setup the VLAN entry. */ + if (!valid) { + /* Need to find a way to map VID to FID. */ + fid = 1; + valid = 1; + } + member |= BIT(port); - ksz8795_to_vlan(fid, member, valid, &data); - ksz8795_w_vlan_table(dev, vid, data); + ksz8795_to_vlan(fid, member, valid, &data); + ksz8795_w_vlan_table(dev, vlan->vid, data); - /* change PVID */ - if (vlan->flags & BRIDGE_VLAN_INFO_PVID) - new_pvid = vid; - } + /* change PVID */ + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) + new_pvid = vlan->vid; if (new_pvid) { + u16 vid; + ksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid); vid &= 0xfff; vid |= new_pvid; ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, vid); } + + return 0; } static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port, @@ -839,7 +837,7 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port, { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct ksz_device *dev = ds->priv; - u16 data, vid, pvid, new_pvid = 0; + u16 data, pvid, new_pvid = 0; u8 fid, member, valid; ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid); @@ -847,24 +845,22 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port, ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - ksz8795_r_vlan_table(dev, vid, &data); - ksz8795_from_vlan(data, &fid, &member, &valid); + ksz8795_r_vlan_table(dev, vlan->vid, &data); + ksz8795_from_vlan(data, &fid, &member, &valid); - member &= ~BIT(port); + member &= ~BIT(port); - /* Invalidate the entry if no more member. */ - if (!member) { - fid = 0; - valid = 0; - } + /* Invalidate the entry if no more member. */ + if (!member) { + fid = 0; + valid = 0; + } - if (pvid == vid) - new_pvid = 1; + if (pvid == vlan->vid) + new_pvid = 1; - ksz8795_to_vlan(fid, member, valid, &data); - ksz8795_w_vlan_table(dev, vid, data); - } + ksz8795_to_vlan(fid, member, valid, &data); + ksz8795_w_vlan_table(dev, vlan->vid, data); if (new_pvid != pvid) ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, pvid); @@ -1098,6 +1094,8 @@ static int ksz8795_setup(struct dsa_switch *ds) ksz_init_mib_timer(dev); + ds->configure_vlan_while_not_filtering = false; + return 0; } @@ -1116,11 +1114,9 @@ static const struct dsa_switch_ops ksz8795_switch_ops = { .port_stp_state_set = ksz8795_port_stp_state_set, .port_fast_age = ksz_port_fast_age, .port_vlan_filtering = ksz8795_port_vlan_filtering, - .port_vlan_prepare = ksz_port_vlan_prepare, .port_vlan_add = ksz8795_port_vlan_add, .port_vlan_del = ksz8795_port_vlan_del, .port_fdb_dump = ksz_port_fdb_dump, - .port_mdb_prepare = ksz_port_mdb_prepare, .port_mdb_add = ksz_port_mdb_add, .port_mdb_del = ksz_port_mdb_del, .port_mirror_add = ksz8795_port_mirror_add, diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 42e647b67abd..00e38c8e0d01 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -493,14 +493,10 @@ static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) } static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, - bool flag, - struct switchdev_trans *trans) + bool flag) { struct ksz_device *dev = ds->priv; - if (switchdev_trans_ph_prepare(trans)) - return 0; - if (flag) { ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL, PORT_VLAN_LOOKUP_VID_0, true); @@ -514,38 +510,40 @@ static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, return 0; } -static void ksz9477_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int ksz9477_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct ksz_device *dev = ds->priv; u32 vlan_table[3]; - u16 vid; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + int err; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - if (ksz9477_get_vlan_table(dev, vid, vlan_table)) { - dev_dbg(dev->dev, "Failed to get vlan table\n"); - return; - } - - vlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M); - if (untagged) - vlan_table[1] |= BIT(port); - else - vlan_table[1] &= ~BIT(port); - vlan_table[1] &= ~(BIT(dev->cpu_port)); + err = ksz9477_get_vlan_table(dev, vlan->vid, vlan_table); + if (err) { + dev_dbg(dev->dev, "Failed to get vlan table\n"); + return err; + } - vlan_table[2] |= BIT(port) | BIT(dev->cpu_port); + vlan_table[0] = VLAN_VALID | (vlan->vid & VLAN_FID_M); + if (untagged) + vlan_table[1] |= BIT(port); + else + vlan_table[1] &= ~BIT(port); + vlan_table[1] &= ~(BIT(dev->cpu_port)); - if (ksz9477_set_vlan_table(dev, vid, vlan_table)) { - dev_dbg(dev->dev, "Failed to set vlan table\n"); - return; - } + vlan_table[2] |= BIT(port) | BIT(dev->cpu_port); - /* change PVID */ - if (vlan->flags & BRIDGE_VLAN_INFO_PVID) - ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid); + err = ksz9477_set_vlan_table(dev, vlan->vid, vlan_table); + if (err) { + dev_dbg(dev->dev, "Failed to set vlan table\n"); + return err; } + + /* change PVID */ + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) + ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vlan->vid); + + return 0; } static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, @@ -554,30 +552,27 @@ static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, struct ksz_device *dev = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; u32 vlan_table[3]; - u16 vid; u16 pvid; ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid); pvid = pvid & 0xFFF; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - if (ksz9477_get_vlan_table(dev, vid, vlan_table)) { - dev_dbg(dev->dev, "Failed to get vlan table\n"); - return -ETIMEDOUT; - } + if (ksz9477_get_vlan_table(dev, vlan->vid, vlan_table)) { + dev_dbg(dev->dev, "Failed to get vlan table\n"); + return -ETIMEDOUT; + } - vlan_table[2] &= ~BIT(port); + vlan_table[2] &= ~BIT(port); - if (pvid == vid) - pvid = 1; + if (pvid == vlan->vid) + pvid = 1; - if (untagged) - vlan_table[1] &= ~BIT(port); + if (untagged) + vlan_table[1] &= ~BIT(port); - if (ksz9477_set_vlan_table(dev, vid, vlan_table)) { - dev_dbg(dev->dev, "Failed to set vlan table\n"); - return -ETIMEDOUT; - } + if (ksz9477_set_vlan_table(dev, vlan->vid, vlan_table)) { + dev_dbg(dev->dev, "Failed to set vlan table\n"); + return -ETIMEDOUT; } ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid); @@ -784,14 +779,15 @@ exit: return ret; } -static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) { struct ksz_device *dev = ds->priv; u32 static_table[4]; u32 data; int index; u32 mac_hi, mac_lo; + int err = 0; mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]); mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16)); @@ -806,7 +802,8 @@ static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port, ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data); /* wait to be finished */ - if (ksz9477_wait_alu_sta_ready(dev)) { + err = ksz9477_wait_alu_sta_ready(dev); + if (err) { dev_dbg(dev->dev, "Failed to read ALU STATIC\n"); goto exit; } @@ -829,8 +826,10 @@ static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port, } /* no available entry */ - if (index == dev->num_statics) + if (index == dev->num_statics) { + err = -ENOSPC; goto exit; + } /* add entry */ static_table[0] = ALU_V_STATIC_VALID; @@ -852,6 +851,7 @@ static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port, exit: mutex_unlock(&dev->alu_mutex); + return err; } static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, @@ -1381,6 +1381,8 @@ static int ksz9477_setup(struct dsa_switch *ds) ksz_init_mib_timer(dev); + ds->configure_vlan_while_not_filtering = false; + return 0; } @@ -1399,13 +1401,11 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .port_stp_state_set = ksz9477_port_stp_state_set, .port_fast_age = ksz_port_fast_age, .port_vlan_filtering = ksz9477_port_vlan_filtering, - .port_vlan_prepare = ksz_port_vlan_prepare, .port_vlan_add = ksz9477_port_vlan_add, .port_vlan_del = ksz9477_port_vlan_del, .port_fdb_dump = ksz9477_port_fdb_dump, .port_fdb_add = ksz9477_port_fdb_add, .port_fdb_del = ksz9477_port_fdb_del, - .port_mdb_prepare = ksz_port_mdb_prepare, .port_mdb_add = ksz9477_port_mdb_add, .port_mdb_del = ksz9477_port_mdb_del, .port_mirror_add = ksz9477_port_mirror_add, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 389abfd27770..a7e5ac60baef 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -213,15 +213,6 @@ void ksz_port_fast_age(struct dsa_switch *ds, int port) } EXPORT_SYMBOL_GPL(ksz_port_fast_age); -int ksz_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - /* nothing needed */ - - return 0; -} -EXPORT_SYMBOL_GPL(ksz_port_vlan_prepare); - int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { @@ -253,16 +244,8 @@ int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, } EXPORT_SYMBOL_GPL(ksz_port_fdb_dump); -int ksz_port_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) -{ - /* nothing to do */ - return 0; -} -EXPORT_SYMBOL_GPL(ksz_port_mdb_prepare); - -void ksz_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +int ksz_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) { struct ksz_device *dev = ds->priv; struct alu_struct alu; @@ -284,7 +267,7 @@ void ksz_port_mdb_add(struct dsa_switch *ds, int port, /* no available entry */ if (index == dev->num_statics && !empty) - return; + return -ENOSPC; /* add entry */ if (index == dev->num_statics) { @@ -301,6 +284,8 @@ void ksz_port_mdb_add(struct dsa_switch *ds, int port, alu.fid = mdb->vid; } dev->dev_ops->w_sta_mac_table(dev, index, &alu); + + return 0; } EXPORT_SYMBOL_GPL(ksz_port_mdb_add); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 720f22275c84..f212775372ce 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -161,14 +161,10 @@ int ksz_port_bridge_join(struct dsa_switch *ds, int port, void ksz_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br); void ksz_port_fast_age(struct dsa_switch *ds, int port); -int ksz_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan); int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); -int ksz_port_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); -void ksz_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); +int ksz_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb); int ksz_port_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb); int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index a67cac15a724..eb13ba79dd01 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -18,6 +18,7 @@ #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> #include <net/dsa.h> #include "mt7530.h" @@ -1376,12 +1377,8 @@ mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid) static int mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering, - struct switchdev_trans *trans) + bool vlan_filtering) { - if (switchdev_trans_ph_prepare(trans)) - return 0; - if (vlan_filtering) { /* The port is being kept as VLAN-unaware port when bridge is * set up with vlan_filtering not being set, Otherwise, the @@ -1397,15 +1394,6 @@ mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, return 0; } -static int -mt7530_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - /* nothing needed */ - - return 0; -} - static void mt7530_hw_vlan_add(struct mt7530_priv *priv, struct mt7530_hw_vlan_entry *entry) @@ -1493,7 +1481,7 @@ mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid, mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, vid); } -static void +static int mt7530_port_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { @@ -1501,23 +1489,21 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port, bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; struct mt7530_hw_vlan_entry new_entry; struct mt7530_priv *priv = ds->priv; - u16 vid; mutex_lock(&priv->reg_mutex); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - mt7530_hw_vlan_entry_init(&new_entry, port, untagged); - mt7530_hw_vlan_update(priv, vid, &new_entry, - mt7530_hw_vlan_add); - } + mt7530_hw_vlan_entry_init(&new_entry, port, untagged); + mt7530_hw_vlan_update(priv, vlan->vid, &new_entry, mt7530_hw_vlan_add); if (pvid) { mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, - G0_PORT_VID(vlan->vid_end)); - priv->ports[port].pvid = vlan->vid_end; + G0_PORT_VID(vlan->vid)); + priv->ports[port].pvid = vlan->vid; } mutex_unlock(&priv->reg_mutex); + + return 0; } static int @@ -1526,22 +1512,20 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port, { struct mt7530_hw_vlan_entry target_entry; struct mt7530_priv *priv = ds->priv; - u16 vid, pvid; + u16 pvid; mutex_lock(&priv->reg_mutex); pvid = priv->ports[port].pvid; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - mt7530_hw_vlan_entry_init(&target_entry, port, 0); - mt7530_hw_vlan_update(priv, vid, &target_entry, - mt7530_hw_vlan_del); + mt7530_hw_vlan_entry_init(&target_entry, port, 0); + mt7530_hw_vlan_update(priv, vlan->vid, &target_entry, + mt7530_hw_vlan_del); - /* PVID is being restored to the default whenever the PVID port - * is being removed from the VLAN. - */ - if (pvid == vid) - pvid = G0_PORT_VID_DEF; - } + /* PVID is being restored to the default whenever the PVID port + * is being removed from the VLAN. + */ + if (pvid == vlan->vid) + pvid = G0_PORT_VID_DEF; mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid); priv->ports[port].pvid = pvid; @@ -1639,6 +1623,109 @@ mtk_get_tag_protocol(struct dsa_switch *ds, int port, } } +static inline u32 +mt7530_gpio_to_bit(unsigned int offset) +{ + /* Map GPIO offset to register bit + * [ 2: 0] port 0 LED 0..2 as GPIO 0..2 + * [ 6: 4] port 1 LED 0..2 as GPIO 3..5 + * [10: 8] port 2 LED 0..2 as GPIO 6..8 + * [14:12] port 3 LED 0..2 as GPIO 9..11 + * [18:16] port 4 LED 0..2 as GPIO 12..14 + */ + return BIT(offset + offset / 3); +} + +static int +mt7530_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct mt7530_priv *priv = gpiochip_get_data(gc); + u32 bit = mt7530_gpio_to_bit(offset); + + return !!(mt7530_read(priv, MT7530_LED_GPIO_DATA) & bit); +} + +static void +mt7530_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct mt7530_priv *priv = gpiochip_get_data(gc); + u32 bit = mt7530_gpio_to_bit(offset); + + if (value) + mt7530_set(priv, MT7530_LED_GPIO_DATA, bit); + else + mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit); +} + +static int +mt7530_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct mt7530_priv *priv = gpiochip_get_data(gc); + u32 bit = mt7530_gpio_to_bit(offset); + + return (mt7530_read(priv, MT7530_LED_GPIO_DIR) & bit) ? + GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int +mt7530_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct mt7530_priv *priv = gpiochip_get_data(gc); + u32 bit = mt7530_gpio_to_bit(offset); + + mt7530_clear(priv, MT7530_LED_GPIO_OE, bit); + mt7530_clear(priv, MT7530_LED_GPIO_DIR, bit); + + return 0; +} + +static int +mt7530_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct mt7530_priv *priv = gpiochip_get_data(gc); + u32 bit = mt7530_gpio_to_bit(offset); + + mt7530_set(priv, MT7530_LED_GPIO_DIR, bit); + + if (value) + mt7530_set(priv, MT7530_LED_GPIO_DATA, bit); + else + mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit); + + mt7530_set(priv, MT7530_LED_GPIO_OE, bit); + + return 0; +} + +static int +mt7530_setup_gpio(struct mt7530_priv *priv) +{ + struct device *dev = priv->dev; + struct gpio_chip *gc; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + mt7530_write(priv, MT7530_LED_GPIO_OE, 0); + mt7530_write(priv, MT7530_LED_GPIO_DIR, 0); + mt7530_write(priv, MT7530_LED_IO_MODE, 0); + + gc->label = "mt7530"; + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->get_direction = mt7530_gpio_get_direction; + gc->direction_input = mt7530_gpio_direction_input; + gc->direction_output = mt7530_gpio_direction_output; + gc->get = mt7530_gpio_get; + gc->set = mt7530_gpio_set; + gc->base = -1; + gc->ngpio = 15; + gc->can_sleep = true; + + return devm_gpiochip_add_data(dev, gc, priv); +} + static int mt7530_setup(struct dsa_switch *ds) { @@ -1656,7 +1743,6 @@ mt7530_setup(struct dsa_switch *ds) * as two netdev instances. */ dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent; - ds->configure_vlan_while_not_filtering = true; ds->mtu_enforcement_ingress = true; if (priv->id == ID_MT7530) { @@ -1781,6 +1867,12 @@ mt7530_setup(struct dsa_switch *ds) } } + if (of_property_read_bool(priv->dev->of_node, "gpio-controller")) { + ret = mt7530_setup_gpio(priv); + if (ret) + return ret; + } + mt7530_setup_port5(ds, interface); /* Flush the FDB table */ @@ -1895,7 +1987,6 @@ mt7531_setup(struct dsa_switch *ds) PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); } - ds->configure_vlan_while_not_filtering = true; ds->mtu_enforcement_ingress = true; /* Flush the FDB table */ @@ -2618,7 +2709,6 @@ static const struct dsa_switch_ops mt7530_switch_ops = { .port_fdb_del = mt7530_port_fdb_del, .port_fdb_dump = mt7530_port_fdb_dump, .port_vlan_filtering = mt7530_port_vlan_filtering, - .port_vlan_prepare = mt7530_port_vlan_prepare, .port_vlan_add = mt7530_port_vlan_add, .port_vlan_del = mt7530_port_vlan_del, .port_mirror_add = mt753x_port_mirror_add, diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 32d8969b3ace..64a9bb377e15 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -554,6 +554,26 @@ enum mt7531_clk_skew { #define MT7531_GPIO12_RG_RXD3_MASK GENMASK(19, 16) #define MT7531_EXT_P_MDIO_12 (2 << 16) +/* Registers for LED GPIO control (MT7530 only) + * All registers follow this pattern: + * [ 2: 0] port 0 + * [ 6: 4] port 1 + * [10: 8] port 2 + * [14:12] port 3 + * [18:16] port 4 + */ + +/* LED enable, 0: Disable, 1: Enable (Default) */ +#define MT7530_LED_EN 0x7d00 +/* LED mode, 0: GPIO mode, 1: PHY mode (Default) */ +#define MT7530_LED_IO_MODE 0x7d04 +/* GPIO direction, 0: Input, 1: Output */ +#define MT7530_LED_GPIO_DIR 0x7d10 +/* GPIO output enable, 0: Disable, 1: Enable */ +#define MT7530_LED_GPIO_OE 0x7d14 +/* GPIO value, 0: Low, 1: High */ +#define MT7530_LED_GPIO_DATA 0x7d18 + #define MT7530_CREV 0x7ffc #define CHIP_NAME_SHIFT 16 #define MT7530_ID 0x7530 diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig index 51185e4d7d15..05af632b0f59 100644 --- a/drivers/net/dsa/mv88e6xxx/Kconfig +++ b/drivers/net/dsa/mv88e6xxx/Kconfig @@ -9,23 +9,10 @@ config NET_DSA_MV88E6XXX This driver adds support for most of the Marvell 88E6xxx models of Ethernet switch chips, except 88E6060. -config NET_DSA_MV88E6XXX_GLOBAL2 - bool "Switch Global 2 Registers support" - default y - depends on NET_DSA_MV88E6XXX - help - This registers set at internal SMI address 0x1C provides extended - features like EEPROM interface, trunking, cross-chip setup, etc. - - It is required on most chips. If the chip you compile the support for - doesn't have such registers set, say N here. In doubt, say Y. - config NET_DSA_MV88E6XXX_PTP bool "PTP support for Marvell 88E6xxx" default n - depends on NET_DSA_MV88E6XXX_GLOBAL2 depends on PTP_1588_CLOCK - imply NETWORK_PHY_TIMESTAMPING help Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch chips that support it. diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index 4b080b448ce7..c8eca2b6f959 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -5,9 +5,9 @@ mv88e6xxx-objs += devlink.o mv88e6xxx-objs += global1.o mv88e6xxx-objs += global1_atu.o mv88e6xxx-objs += global1_vtu.o -mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o -mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_avb.o -mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_scratch.o +mv88e6xxx-objs += global2.o +mv88e6xxx-objs += global2_avb.o +mv88e6xxx-objs += global2_scratch.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o mv88e6xxx-objs += phy.o mv88e6xxx-objs += port.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index eafe6bedc692..b99f27b8c084 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1396,15 +1396,32 @@ static int mv88e6xxx_mac_setup(struct mv88e6xxx_chip *chip) static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) { + struct dsa_switch_tree *dst = chip->ds->dst; + struct dsa_switch *ds; + struct dsa_port *dp; u16 pvlan = 0; if (!mv88e6xxx_has_pvt(chip)) return 0; /* Skip the local source device, which uses in-chip port VLAN */ - if (dev != chip->ds->index) + if (dev != chip->ds->index) { pvlan = mv88e6xxx_port_vlan(chip, dev, port); + ds = dsa_switch_find(dst->index, dev); + dp = ds ? dsa_to_port(ds, port) : NULL; + if (dp && dp->lag_dev) { + /* As the PVT is used to limit flooding of + * FORWARD frames, which use the LAG ID as the + * source port, we must translate dev/port to + * the special "LAG device" in the PVT, using + * the LAG ID as the port number. + */ + dev = MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK; + port = dsa_lag_id(dst, dp->lag_dev); + } + } + return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan); } @@ -1529,72 +1546,69 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) } static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, - u16 vid_begin, u16 vid_end) + u16 vid) { struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_vtu_entry vlan; int i, err; + if (!vid) + return -EOPNOTSUPP; + /* DSA and CPU ports have to be members of multiple vlans */ if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) return 0; - if (!vid_begin) - return -EOPNOTSUPP; - - vlan.vid = vid_begin - 1; + vlan.vid = vid - 1; vlan.valid = false; - do { - err = mv88e6xxx_vtu_getnext(chip, &vlan); - if (err) - return err; + err = mv88e6xxx_vtu_getnext(chip, &vlan); + if (err) + return err; - if (!vlan.valid) - break; + if (!vlan.valid) + return 0; - if (vlan.vid > vid_end) - break; + if (vlan.vid != vid) + return 0; - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) - continue; + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) + continue; - if (!dsa_to_port(ds, i)->slave) - continue; + if (!dsa_to_port(ds, i)->slave) + continue; - if (vlan.member[i] == - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) - continue; + if (vlan.member[i] == + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) + continue; - if (dsa_to_port(ds, i)->bridge_dev == - dsa_to_port(ds, port)->bridge_dev) - break; /* same bridge, check next VLAN */ + if (dsa_to_port(ds, i)->bridge_dev == + dsa_to_port(ds, port)->bridge_dev) + break; /* same bridge, check next VLAN */ - if (!dsa_to_port(ds, i)->bridge_dev) - continue; + if (!dsa_to_port(ds, i)->bridge_dev) + continue; - dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n", - port, vlan.vid, i, - netdev_name(dsa_to_port(ds, i)->bridge_dev)); - return -EOPNOTSUPP; - } - } while (vlan.vid < vid_end); + dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n", + port, vlan.vid, i, + netdev_name(dsa_to_port(ds, i)->bridge_dev)); + return -EOPNOTSUPP; + } return 0; } static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering, - struct switchdev_trans *trans) + bool vlan_filtering) { struct mv88e6xxx_chip *chip = ds->priv; u16 mode = vlan_filtering ? MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE : MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED; int err; - if (switchdev_trans_ph_prepare(trans)) - return mv88e6xxx_max_vid(chip) ? 0 : -EOPNOTSUPP; + if (!mv88e6xxx_max_vid(chip)) + return -EOPNOTSUPP; mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); @@ -1617,13 +1631,9 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, * members, do not support it (yet) and fallback to software VLAN. */ mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin, - vlan->vid_end); + err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid); mv88e6xxx_reg_unlock(chip); - /* We don't need any dynamic resource from the kernel (yet), - * so skip the prepare phase. - */ return err; } @@ -1923,9 +1933,6 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, struct mv88e6xxx_vtu_entry vlan; int i, err; - if (!vid) - return -EOPNOTSUPP; - vlan.vid = vid - 1; vlan.valid = false; @@ -1970,18 +1977,19 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, return 0; } -static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct mv88e6xxx_chip *chip = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; bool warn; u8 member; - u16 vid; + int err; - if (!mv88e6xxx_max_vid(chip)) - return; + err = mv88e6xxx_port_vlan_prepare(ds, port, vlan); + if (err) + return err; if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED; @@ -1997,16 +2005,25 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, mv88e6xxx_reg_lock(chip); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - if (mv88e6xxx_port_vlan_join(chip, port, vid, member, warn)) - dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port, - vid, untagged ? 'u' : 't'); - - if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end)) - dev_err(ds->dev, "p%d: failed to set PVID %d\n", port, - vlan->vid_end); + err = mv88e6xxx_port_vlan_join(chip, port, vlan->vid, member, warn); + if (err) { + dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port, + vlan->vid, untagged ? 'u' : 't'); + goto out; + } + if (pvid) { + err = mv88e6xxx_port_set_pvid(chip, port, vlan->vid); + if (err) { + dev_err(ds->dev, "p%d: failed to set PVID %d\n", + port, vlan->vid); + goto out; + } + } +out: mv88e6xxx_reg_unlock(chip); + + return err; } static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip, @@ -2055,8 +2072,8 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct mv88e6xxx_chip *chip = ds->priv; - u16 pvid, vid; int err = 0; + u16 pvid; if (!mv88e6xxx_max_vid(chip)) return -EOPNOTSUPP; @@ -2067,16 +2084,14 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, if (err) goto unlock; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - err = mv88e6xxx_port_vlan_leave(chip, port, vid); + err = mv88e6xxx_port_vlan_leave(chip, port, vlan->vid); + if (err) + goto unlock; + + if (vlan->vid == pvid) { + err = mv88e6xxx_port_set_pvid(chip, port, 0); if (err) goto unlock; - - if (vid == pvid) { - err = mv88e6xxx_port_set_pvid(chip, port, 0); - if (err) - goto unlock; - } } unlock: @@ -2860,7 +2875,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) chip->ds = ds; ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); - ds->configure_vlan_while_not_filtering = true; mv88e6xxx_reg_lock(chip); @@ -4035,8 +4049,8 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6250_g1_reset, - .vtu_getnext = mv88e6250_g1_vtu_getnext, - .vtu_loadpurge = mv88e6250_g1_vtu_loadpurge, + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6250_ptp_ops, .phylink_validate = mv88e6065_phylink_validate, @@ -5213,10 +5227,6 @@ static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip) /* Update the compatible info with the probed one */ chip->info = info; - err = mv88e6xxx_g2_require(chip); - if (err) - return err; - dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n", chip->info->prod_num, chip->info->name, rev); @@ -5249,27 +5259,18 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, return chip->info->tag_protocol; } -static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) -{ - /* We don't need any dynamic resource from the kernel (yet), - * so skip the prepare phase. - */ - - return 0; -} - -static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) { struct mv88e6xxx_chip *chip = ds->priv; + int err; mv88e6xxx_reg_lock(chip); - if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, - MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC)) - dev_err(ds->dev, "p%d: failed to load multicast MAC address\n", - port); + err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, + MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC); mv88e6xxx_reg_unlock(chip); + + return err; } static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, @@ -5375,6 +5376,275 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port, return err; } +static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds, + struct net_device *lag, + struct netdev_lag_upper_info *info) +{ + struct mv88e6xxx_chip *chip = ds->priv; + struct dsa_port *dp; + int id, members = 0; + + if (!mv88e6xxx_has_lag(chip)) + return false; + + id = dsa_lag_id(ds->dst, lag); + if (id < 0 || id >= ds->num_lag_ids) + return false; + + dsa_lag_foreach_port(dp, ds->dst, lag) + /* Includes the port joining the LAG */ + members++; + + if (members > 8) + return false; + + /* We could potentially relax this to include active + * backup in the future. + */ + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) + return false; + + /* Ideally we would also validate that the hash type matches + * the hardware. Alas, this is always set to unknown on team + * interfaces. + */ + return true; +} + +static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct net_device *lag) +{ + struct mv88e6xxx_chip *chip = ds->priv; + struct dsa_port *dp; + u16 map = 0; + int id; + + id = dsa_lag_id(ds->dst, lag); + + /* Build the map of all ports to distribute flows destined for + * this LAG. This can be either a local user port, or a DSA + * port if the LAG port is on a remote chip. + */ + dsa_lag_foreach_port(dp, ds->dst, lag) + map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index)); + + return mv88e6xxx_g2_trunk_mapping_write(chip, id, map); +} + +static const u8 mv88e6xxx_lag_mask_table[8][8] = { + /* Row number corresponds to the number of active members in a + * LAG. Each column states which of the eight hash buckets are + * mapped to the column:th port in the LAG. + * + * Example: In a LAG with three active ports, the second port + * ([2][1]) would be selected for traffic mapped to buckets + * 3,4,5 (0x38). + */ + { 0xff, 0, 0, 0, 0, 0, 0, 0 }, + { 0x0f, 0xf0, 0, 0, 0, 0, 0, 0 }, + { 0x07, 0x38, 0xc0, 0, 0, 0, 0, 0 }, + { 0x03, 0x0c, 0x30, 0xc0, 0, 0, 0, 0 }, + { 0x03, 0x0c, 0x30, 0x40, 0x80, 0, 0, 0 }, + { 0x03, 0x0c, 0x10, 0x20, 0x40, 0x80, 0, 0 }, + { 0x03, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0 }, + { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, +}; + +static void mv88e6xxx_lag_set_port_mask(u16 *mask, int port, + int num_tx, int nth) +{ + u8 active = 0; + int i; + + num_tx = num_tx <= 8 ? num_tx : 8; + if (nth < num_tx) + active = mv88e6xxx_lag_mask_table[num_tx - 1][nth]; + + for (i = 0; i < 8; i++) { + if (BIT(i) & active) + mask[i] |= BIT(port); + } +} + +static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) +{ + struct mv88e6xxx_chip *chip = ds->priv; + unsigned int id, num_tx; + struct net_device *lag; + struct dsa_port *dp; + int i, err, nth; + u16 mask[8]; + u16 ivec; + + /* Assume no port is a member of any LAG. */ + ivec = BIT(mv88e6xxx_num_ports(chip)) - 1; + + /* Disable all masks for ports that _are_ members of a LAG. */ + list_for_each_entry(dp, &ds->dst->ports, list) { + if (!dp->lag_dev || dp->ds != ds) + continue; + + ivec &= ~BIT(dp->index); + } + + for (i = 0; i < 8; i++) + mask[i] = ivec; + + /* Enable the correct subset of masks for all LAG ports that + * are in the Tx set. + */ + dsa_lags_foreach_id(id, ds->dst) { + lag = dsa_lag_dev(ds->dst, id); + if (!lag) + continue; + + num_tx = 0; + dsa_lag_foreach_port(dp, ds->dst, lag) { + if (dp->lag_tx_enabled) + num_tx++; + } + + if (!num_tx) + continue; + + nth = 0; + dsa_lag_foreach_port(dp, ds->dst, lag) { + if (!dp->lag_tx_enabled) + continue; + + if (dp->ds == ds) + mv88e6xxx_lag_set_port_mask(mask, dp->index, + num_tx, nth); + + nth++; + } + } + + for (i = 0; i < 8; i++) { + err = mv88e6xxx_g2_trunk_mask_write(chip, i, true, mask[i]); + if (err) + return err; + } + + return 0; +} + +static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds, + struct net_device *lag) +{ + int err; + + err = mv88e6xxx_lag_sync_masks(ds); + + if (!err) + err = mv88e6xxx_lag_sync_map(ds, lag); + + return err; +} + +static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_lag_sync_masks(ds); + mv88e6xxx_reg_unlock(chip); + return err; +} + +static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port, + struct net_device *lag, + struct netdev_lag_upper_info *info) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err, id; + + if (!mv88e6xxx_lag_can_offload(ds, lag, info)) + return -EOPNOTSUPP; + + id = dsa_lag_id(ds->dst, lag); + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_port_set_trunk(chip, port, true, id); + if (err) + goto err_unlock; + + err = mv88e6xxx_lag_sync_masks_map(ds, lag); + if (err) + goto err_clear_trunk; + + mv88e6xxx_reg_unlock(chip); + return 0; + +err_clear_trunk: + mv88e6xxx_port_set_trunk(chip, port, false, 0); +err_unlock: + mv88e6xxx_reg_unlock(chip); + return err; +} + +static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port, + struct net_device *lag) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err_sync, err_trunk; + + mv88e6xxx_reg_lock(chip); + err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag); + err_trunk = mv88e6xxx_port_set_trunk(chip, port, false, 0); + mv88e6xxx_reg_unlock(chip); + return err_sync ? : err_trunk; +} + +static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index, + int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_lag_sync_masks(ds); + mv88e6xxx_reg_unlock(chip); + return err; +} + +static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index, + int port, struct net_device *lag, + struct netdev_lag_upper_info *info) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + if (!mv88e6xxx_lag_can_offload(ds, lag, info)) + return -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_lag_sync_masks_map(ds, lag); + if (err) + goto unlock; + + err = mv88e6xxx_pvt_map(chip, sw_index, port); + +unlock: + mv88e6xxx_reg_unlock(chip); + return err; +} + +static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index, + int port, struct net_device *lag) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err_sync, err_pvt; + + mv88e6xxx_reg_lock(chip); + err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag); + err_pvt = mv88e6xxx_pvt_map(chip, sw_index, port); + mv88e6xxx_reg_unlock(chip); + return err_sync ? : err_pvt; +} + static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .get_tag_protocol = mv88e6xxx_get_tag_protocol, .setup = mv88e6xxx_setup, @@ -5408,13 +5678,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_stp_state_set = mv88e6xxx_port_stp_state_set, .port_fast_age = mv88e6xxx_port_fast_age, .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, - .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_dump = mv88e6xxx_port_fdb_dump, - .port_mdb_prepare = mv88e6xxx_port_mdb_prepare, .port_mdb_add = mv88e6xxx_port_mdb_add, .port_mdb_del = mv88e6xxx_port_mdb_del, .port_mirror_add = mv88e6xxx_port_mirror_add, @@ -5429,6 +5697,12 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .devlink_param_get = mv88e6xxx_devlink_param_get, .devlink_param_set = mv88e6xxx_devlink_param_set, .devlink_info_get = mv88e6xxx_devlink_info_get, + .port_lag_change = mv88e6xxx_port_lag_change, + .port_lag_join = mv88e6xxx_port_lag_join, + .port_lag_leave = mv88e6xxx_port_lag_leave, + .crosschip_lag_change = mv88e6xxx_crosschip_lag_change, + .crosschip_lag_join = mv88e6xxx_crosschip_lag_join, + .crosschip_lag_leave = mv88e6xxx_crosschip_lag_leave, }; static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) @@ -5448,6 +5722,12 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) ds->ageing_time_min = chip->info->age_time_coeff; ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; + /* Some chips support up to 32, but that requires enabling the + * 5-bit port mode, which we do not support. 640k^W16 ought to + * be enough for anyone. + */ + ds->num_lag_ids = mv88e6xxx_has_lag(chip) ? 16 : 0; + dev_set_drvdata(dev, ds); return dsa_register_switch(ds); diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 3543055bcb51..788b3f585ef3 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -662,6 +662,11 @@ static inline bool mv88e6xxx_has_pvt(struct mv88e6xxx_chip *chip) return chip->info->pvt; } +static inline bool mv88e6xxx_has_lag(struct mv88e6xxx_chip *chip) +{ + return !!chip->info->global2_addr; +} + static inline unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip) { return chip->info->num_databases; diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 80a182c5b98a..7c396964d0b2 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -336,10 +336,6 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); -int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 7b96396be609..ae12c981923e 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -336,39 +336,6 @@ int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_vtu_vid_read(chip, entry); } -int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - u16 val; - int err; - - err = mv88e6xxx_g1_vtu_getnext(chip, entry); - if (err) - return err; - - if (entry->valid) { - err = mv88e6185_g1_vtu_data_read(chip, entry); - if (err) - return err; - - err = mv88e6185_g1_stu_data_read(chip, entry); - if (err) - return err; - - /* VTU DBNum[3:0] are located in VTU Operation 3:0 - * VTU DBNum[5:4] are located in VTU Operation 9:8 - */ - err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val); - if (err) - return err; - - entry->fid = val & 0x000f; - entry->fid |= (val & 0x0300) >> 4; - } - - return 0; -} - int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { @@ -389,7 +356,7 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return err; /* VTU DBNum[3:0] are located in VTU Operation 3:0 - * VTU DBNum[7:4] are located in VTU Operation 11:8 + * VTU DBNum[7:4] ([5:4] for 6250) are located in VTU Operation 11:8 (9:8) */ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val); if (err) @@ -397,6 +364,7 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, entry->fid = val & 0x000f; entry->fid |= (val & 0x0f00) >> 4; + entry->fid &= mv88e6xxx_num_databases(chip) - 1; } return 0; @@ -466,35 +434,6 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return 0; } -int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE; - int err; - - err = mv88e6xxx_g1_vtu_op_wait(chip); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); - if (err) - return err; - - if (entry->valid) { - err = mv88e6185_g1_vtu_data_write(chip, entry); - if (err) - return err; - - /* VTU DBNum[3:0] are located in VTU Operation 3:0 - * VTU DBNum[5:4] are located in VTU Operation 9:8 - */ - op |= entry->fid & 0x000f; - op |= (entry->fid & 0x0030) << 4; - } - - return mv88e6xxx_g1_vtu_op(chip, op); -} - int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { @@ -516,6 +455,10 @@ int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, /* VTU DBNum[3:0] are located in VTU Operation 3:0 * VTU DBNum[7:4] are located in VTU Operation 11:8 + * + * For the 6250/6220, the latter are really [5:4] and + * 9:8, but in those cases bits 7:6 of entry->fid are + * 0 since they have num_databases = 64. */ op |= entry->fid & 0x000f; op |= (entry->fid & 0x00f0) << 4; diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 75b227d0f73b..da8bac8813e1 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -126,8 +126,8 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, /* Offset 0x07: Trunk Mask Table register */ -static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, - bool hash, u16 mask) +int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, + bool hash, u16 mask) { u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip)); @@ -140,8 +140,8 @@ static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, /* Offset 0x08: Trunk Mapping Table register */ -static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, - u16 map) +int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, + u16 map) { const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; u16 val = (id << 11) | (map & port_mask); diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 1f42ee656816..4127f82275ad 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -101,6 +101,7 @@ #define MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN 0x3000 #define MV88E6XXX_G2_PVT_ADDR_OP_READ 0x4000 #define MV88E6XXX_G2_PVT_ADDR_PTR_MASK 0x01ff +#define MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK 0x1f /* Offset 0x0C: Cross-chip Port VLAN Data Register */ #define MV88E6XXX_G2_PVT_DATA 0x0c @@ -295,13 +296,6 @@ #define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG 1 #define MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ 2 -#ifdef CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 - -static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) -{ - return 0; -} - int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val); int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg, @@ -345,6 +339,10 @@ int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, + bool hash, u16 mask); +int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, + u16 map); int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, @@ -365,179 +363,4 @@ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin); int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats); -#else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ - -static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) -{ - if (chip->info->global2_addr) { - dev_err(chip->dev, "this chip requires CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 enabled\n"); - return -EOPNOTSUPP; - } - - return 0; -} - -static inline int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, - int reg, int bit, int val) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, - int port) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, - int port) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, - struct mii_bus *bus, - int addr, int reg, u16 *val) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, - struct mii_bus *bus, - int addr, int reg, u16 val) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, - u8 *addr) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, - struct ethtool_eeprom *eeprom, - u8 *data) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, - struct ethtool_eeprom *eeprom, - u8 *data) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, - struct ethtool_eeprom *eeprom, - u8 *data) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, - struct ethtool_eeprom *eeprom, - u8 *data) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, - int src_dev, int src_port, u16 data) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) -{ - return -EOPNOTSUPP; -} - -static inline void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) -{ -} - -static inline int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip, - struct mii_bus *bus) -{ - return 0; -} - -static inline void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip, - struct mii_bus *bus) -{ -} - -static inline int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) -{ - return -EOPNOTSUPP; -} - -static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {}; -static const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = {}; -static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {}; - -static const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = {}; -static const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {}; -static const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {}; - -static const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = {}; - -static inline int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, - bool external) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, - int target, int port) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, - u16 kind, u16 bin) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, - u16 *stats) -{ - return -EOPNOTSUPP; -} - -#endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ - #endif /* _MV88E6XXX_GLOBAL2_H */ diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 77a5fd1798cd..4b46e10a2dde 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -851,6 +851,27 @@ int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val); } +int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port, + bool trunk, u8 id) +{ + u16 val; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val); + if (err) + return err; + + val &= ~MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK; + + if (trunk) + val |= MV88E6XXX_PORT_CTL1_TRUNK_PORT | + (id << MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT); + else + val &= ~MV88E6XXX_PORT_CTL1_TRUNK_PORT; + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val); +} + /* Offset 0x06: Port Based VLAN Map */ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 500e1d4896ff..a729bba050df 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -168,6 +168,9 @@ /* Offset 0x05: Port Control 1 */ #define MV88E6XXX_PORT_CTL1 0x05 #define MV88E6XXX_PORT_CTL1_MESSAGE_PORT 0x8000 +#define MV88E6XXX_PORT_CTL1_TRUNK_PORT 0x4000 +#define MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK 0x0f00 +#define MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT 8 #define MV88E6XXX_PORT_CTL1_FID_11_4_MASK 0x00ff /* Offset 0x06: Port Based VLAN Map */ @@ -351,6 +354,8 @@ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, u16 etype); int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, bool message_port); +int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port, + bool trunk, u8 id); int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port, size_t size); int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 7dc230677b78..767cbdccdb3e 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -65,19 +65,12 @@ 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) +static int 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); + return ocelot_port_mdb_add(ocelot, port, mdb); } static int felix_mdb_del(struct dsa_switch *ds, int port, @@ -116,8 +109,7 @@ static int felix_vlan_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct ocelot *ocelot = ds->priv; - u16 vid, flags = vlan->flags; - int err; + u16 flags = vlan->flags; /* Ocelot switches copy frames as-is to the CPU, so the flags: * egress-untagged or not, pvid or not, make no difference. This @@ -130,61 +122,40 @@ static int felix_vlan_prepare(struct dsa_switch *ds, int port, if (port == ocelot->npi) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - err = ocelot_vlan_prepare(ocelot, port, vid, - flags & BRIDGE_VLAN_INFO_PVID, - flags & BRIDGE_VLAN_INFO_UNTAGGED); - if (err) - return err; - } - - return 0; + return ocelot_vlan_prepare(ocelot, port, vlan->vid, + flags & BRIDGE_VLAN_INFO_PVID, + flags & BRIDGE_VLAN_INFO_UNTAGGED); } -static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, - struct switchdev_trans *trans) +static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) { struct ocelot *ocelot = ds->priv; - return ocelot_port_vlan_filtering(ocelot, port, enabled, trans); + return ocelot_port_vlan_filtering(ocelot, port, enabled); } -static void felix_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int felix_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct ocelot *ocelot = ds->priv; u16 flags = vlan->flags; - u16 vid; int err; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - err = ocelot_vlan_add(ocelot, port, vid, - flags & BRIDGE_VLAN_INFO_PVID, - flags & BRIDGE_VLAN_INFO_UNTAGGED); - if (err) { - dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n", - vid, port, err); - return; - } - } + err = felix_vlan_prepare(ds, port, vlan); + if (err) + return err; + + return ocelot_vlan_add(ocelot, port, vlan->vid, + flags & BRIDGE_VLAN_INFO_PVID, + flags & BRIDGE_VLAN_INFO_UNTAGGED); } static int felix_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct ocelot *ocelot = ds->priv; - u16 vid; - int err; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - err = ocelot_vlan_del(ocelot, port, vid); - if (err) { - dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n", - vid, port, err); - return err; - } - } - return 0; + return ocelot_vlan_del(ocelot, port, vlan->vid); } static int felix_port_enable(struct dsa_switch *ds, int port, @@ -328,7 +299,7 @@ static void felix_port_qos_map_init(struct ocelot *ocelot, int port) ANA_PORT_QOS_CFG, port); - for (i = 0; i < FELIX_NUM_TC * 2; i++) { + for (i = 0; i < OCELOT_NUM_TC * 2; i++) { ocelot_rmw_ix(ocelot, (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & i) | ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL(i), @@ -451,12 +422,12 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) ocelot->map = felix->info->map; ocelot->stats_layout = felix->info->stats_layout; ocelot->num_stats = felix->info->num_stats; - ocelot->shared_queue_sz = felix->info->shared_queue_sz; ocelot->num_mact_rows = felix->info->num_mact_rows; ocelot->vcap = felix->info->vcap; ocelot->ops = felix->info->ops; ocelot->inj_prefix = OCELOT_TAG_PREFIX_SHORT; ocelot->xtr_prefix = OCELOT_TAG_PREFIX_SHORT; + ocelot->devlink = felix->ds->devlink; port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t), GFP_KERNEL); @@ -618,6 +589,10 @@ static int felix_setup(struct dsa_switch *ds) felix_port_qos_map_init(ocelot, port); } + err = ocelot_devlink_sb_register(ocelot); + if (err) + return err; + /* Include the CPU port module in the forwarding mask for unknown * unicast - the hardware default value for ANA_FLOODING_FLD_UNICAST * excludes BIT(ocelot->num_phys_ports), and so does ocelot_init, since @@ -628,7 +603,7 @@ static int felix_setup(struct dsa_switch *ds) ANA_PGID_PGID, PGID_UC); ds->mtu_enforcement_ingress = true; - ds->configure_vlan_while_not_filtering = true; + ds->assisted_learning_on_cpu_port = true; return 0; } @@ -639,14 +614,15 @@ static void felix_teardown(struct dsa_switch *ds) struct felix *felix = ocelot_to_felix(ocelot); int port; - if (felix->info->mdio_bus_free) - felix->info->mdio_bus_free(ocelot); + ocelot_devlink_sb_unregister(ocelot); + ocelot_deinit_timestamp(ocelot); + ocelot_deinit(ocelot); for (port = 0; port < ocelot->num_phys_ports; port++) ocelot_deinit_port(ocelot, port); - ocelot_deinit_timestamp(ocelot); - /* stop workqueue thread */ - ocelot_deinit(ocelot); + + if (felix->info->mdio_bus_free) + felix->info->mdio_bus_free(ocelot); } static int felix_hwtstamp_get(struct dsa_switch *ds, int port, @@ -780,46 +756,156 @@ static int felix_port_setup_tc(struct dsa_switch *ds, int port, return -EOPNOTSUPP; } +static int felix_sb_pool_get(struct dsa_switch *ds, unsigned int sb_index, + u16 pool_index, + struct devlink_sb_pool_info *pool_info) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_pool_get(ocelot, sb_index, pool_index, pool_info); +} + +static int felix_sb_pool_set(struct dsa_switch *ds, unsigned int sb_index, + u16 pool_index, u32 size, + enum devlink_sb_threshold_type threshold_type, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_pool_set(ocelot, sb_index, pool_index, size, + threshold_type, extack); +} + +static int felix_sb_port_pool_get(struct dsa_switch *ds, int port, + unsigned int sb_index, u16 pool_index, + u32 *p_threshold) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_port_pool_get(ocelot, port, sb_index, pool_index, + p_threshold); +} + +static int felix_sb_port_pool_set(struct dsa_switch *ds, int port, + unsigned int sb_index, u16 pool_index, + u32 threshold, struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_port_pool_set(ocelot, port, sb_index, pool_index, + threshold, extack); +} + +static int felix_sb_tc_pool_bind_get(struct dsa_switch *ds, int port, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 *p_pool_index, u32 *p_threshold) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_tc_pool_bind_get(ocelot, port, sb_index, tc_index, + pool_type, p_pool_index, + p_threshold); +} + +static int felix_sb_tc_pool_bind_set(struct dsa_switch *ds, int port, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 pool_index, u32 threshold, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_tc_pool_bind_set(ocelot, port, sb_index, tc_index, + pool_type, pool_index, threshold, + extack); +} + +static int felix_sb_occ_snapshot(struct dsa_switch *ds, + unsigned int sb_index) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_occ_snapshot(ocelot, sb_index); +} + +static int felix_sb_occ_max_clear(struct dsa_switch *ds, + unsigned int sb_index) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_occ_max_clear(ocelot, sb_index); +} + +static int felix_sb_occ_port_pool_get(struct dsa_switch *ds, int port, + unsigned int sb_index, u16 pool_index, + u32 *p_cur, u32 *p_max) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_occ_port_pool_get(ocelot, port, sb_index, pool_index, + p_cur, p_max); +} + +static int felix_sb_occ_tc_port_bind_get(struct dsa_switch *ds, int port, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u32 *p_cur, u32 *p_max) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_sb_occ_tc_port_bind_get(ocelot, port, sb_index, tc_index, + pool_type, p_cur, p_max); +} + const struct dsa_switch_ops felix_switch_ops = { - .get_tag_protocol = felix_get_tag_protocol, - .setup = felix_setup, - .teardown = felix_teardown, - .set_ageing_time = felix_set_ageing_time, - .get_strings = felix_get_strings, - .get_ethtool_stats = felix_get_ethtool_stats, - .get_sset_count = felix_get_sset_count, - .get_ts_info = felix_get_ts_info, - .phylink_validate = felix_phylink_validate, - .phylink_mac_config = felix_phylink_mac_config, - .phylink_mac_link_down = felix_phylink_mac_link_down, - .phylink_mac_link_up = felix_phylink_mac_link_up, - .port_enable = felix_port_enable, - .port_disable = felix_port_disable, - .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, - .port_vlan_prepare = felix_vlan_prepare, - .port_vlan_filtering = felix_vlan_filtering, - .port_vlan_add = felix_vlan_add, - .port_vlan_del = felix_vlan_del, - .port_hwtstamp_get = felix_hwtstamp_get, - .port_hwtstamp_set = felix_hwtstamp_set, - .port_rxtstamp = felix_rxtstamp, - .port_txtstamp = felix_txtstamp, - .port_change_mtu = felix_change_mtu, - .port_max_mtu = felix_get_max_mtu, - .port_policer_add = felix_port_policer_add, - .port_policer_del = felix_port_policer_del, - .cls_flower_add = felix_cls_flower_add, - .cls_flower_del = felix_cls_flower_del, - .cls_flower_stats = felix_cls_flower_stats, - .port_setup_tc = felix_port_setup_tc, + .get_tag_protocol = felix_get_tag_protocol, + .setup = felix_setup, + .teardown = felix_teardown, + .set_ageing_time = felix_set_ageing_time, + .get_strings = felix_get_strings, + .get_ethtool_stats = felix_get_ethtool_stats, + .get_sset_count = felix_get_sset_count, + .get_ts_info = felix_get_ts_info, + .phylink_validate = felix_phylink_validate, + .phylink_mac_config = felix_phylink_mac_config, + .phylink_mac_link_down = felix_phylink_mac_link_down, + .phylink_mac_link_up = felix_phylink_mac_link_up, + .port_enable = felix_port_enable, + .port_disable = felix_port_disable, + .port_fdb_dump = felix_fdb_dump, + .port_fdb_add = felix_fdb_add, + .port_fdb_del = felix_fdb_del, + .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, + .port_vlan_filtering = felix_vlan_filtering, + .port_vlan_add = felix_vlan_add, + .port_vlan_del = felix_vlan_del, + .port_hwtstamp_get = felix_hwtstamp_get, + .port_hwtstamp_set = felix_hwtstamp_set, + .port_rxtstamp = felix_rxtstamp, + .port_txtstamp = felix_txtstamp, + .port_change_mtu = felix_change_mtu, + .port_max_mtu = felix_get_max_mtu, + .port_policer_add = felix_port_policer_add, + .port_policer_del = felix_port_policer_del, + .cls_flower_add = felix_cls_flower_add, + .cls_flower_del = felix_cls_flower_del, + .cls_flower_stats = felix_cls_flower_stats, + .port_setup_tc = felix_port_setup_tc, + .devlink_sb_pool_get = felix_sb_pool_get, + .devlink_sb_pool_set = felix_sb_pool_set, + .devlink_sb_port_pool_get = felix_sb_port_pool_get, + .devlink_sb_port_pool_set = felix_sb_port_pool_set, + .devlink_sb_tc_pool_bind_get = felix_sb_tc_pool_bind_get, + .devlink_sb_tc_pool_bind_set = felix_sb_tc_pool_bind_set, + .devlink_sb_occ_snapshot = felix_sb_occ_snapshot, + .devlink_sb_occ_max_clear = felix_sb_occ_max_clear, + .devlink_sb_occ_port_pool_get = felix_sb_occ_port_pool_get, + .devlink_sb_occ_tc_port_bind_get= felix_sb_occ_tc_port_bind_get, }; struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port) diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 4c717324ac2f..994835cb9307 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -5,7 +5,6 @@ #define _MSCC_FELIX_H #define ocelot_to_felix(o) container_of((o), struct felix, ocelot) -#define FELIX_NUM_TC 8 /* Platform-specific information */ struct felix_info { @@ -15,7 +14,6 @@ struct felix_info { const struct reg_field *regfields; const u32 *const *map; const struct ocelot_ops *ops; - int shared_queue_sz; int num_mact_rows; const struct ocelot_stat_layout *stats_layout; unsigned int num_stats; diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 2e5bbdca5ea4..f9711e69b8d5 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1006,9 +1006,27 @@ static u16 vsc9959_wm_enc(u16 value) return value; } +static u16 vsc9959_wm_dec(u16 wm) +{ + WARN_ON(wm & ~GENMASK(8, 0)); + + if (wm & BIT(8)) + return (wm & GENMASK(7, 0)) * 16; + + return wm; +} + +static void vsc9959_wm_stat(u32 val, u32 *inuse, u32 *maxuse) +{ + *inuse = (val & GENMASK(23, 12)) >> 12; + *maxuse = val & GENMASK(11, 0); +} + static const struct ocelot_ops vsc9959_ops = { .reset = vsc9959_reset, .wm_enc = vsc9959_wm_enc, + .wm_dec = vsc9959_wm_dec, + .wm_stat = vsc9959_wm_stat, .port_to_netdev = felix_port_to_netdev, .netdev_to_port = felix_netdev_to_port, }; @@ -1356,10 +1374,9 @@ static const struct felix_info felix_info_vsc9959 = { .stats_layout = vsc9959_stats_layout, .num_stats = ARRAY_SIZE(vsc9959_stats_layout), .vcap = vsc9959_vcap_props, - .shared_queue_sz = 128 * 1024, .num_mact_rows = 2048, .num_ports = 6, - .num_tx_queues = FELIX_NUM_TC, + .num_tx_queues = OCELOT_NUM_TC, .switch_pci_bar = 4, .imdio_pci_bar = 0, .ptp_caps = &vsc9959_ptp_caps, @@ -1408,17 +1425,6 @@ static int felix_pci_probe(struct pci_dev *pdev, goto err_pci_enable; } - /* set up for high or low dma */ - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (err) { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "DMA configuration failed: 0x%x\n", err); - goto err_dma; - } - } - felix = kzalloc(sizeof(struct felix), GFP_KERNEL); if (!felix) { err = -ENOMEM; @@ -1429,7 +1435,7 @@ static int felix_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, felix); ocelot = &felix->ocelot; ocelot->dev = &pdev->dev; - ocelot->num_flooding_pgids = FELIX_NUM_TC; + ocelot->num_flooding_pgids = OCELOT_NUM_TC; felix->info = &felix_info_vsc9959; felix->switch_base = pci_resource_start(pdev, felix->info->switch_pci_bar); @@ -1474,9 +1480,8 @@ err_register_ds: kfree(ds); err_alloc_ds: err_alloc_irq: -err_alloc_felix: kfree(felix); -err_dma: +err_alloc_felix: pci_disable_device(pdev); err_pci_enable: return err; diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index ebbaf6817ec8..5e9bfdea50be 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -1057,9 +1057,27 @@ static u16 vsc9953_wm_enc(u16 value) return value; } +static u16 vsc9953_wm_dec(u16 wm) +{ + WARN_ON(wm & ~GENMASK(9, 0)); + + if (wm & BIT(9)) + return (wm & GENMASK(8, 0)) * 16; + + return wm; +} + +static void vsc9953_wm_stat(u32 val, u32 *inuse, u32 *maxuse) +{ + *inuse = (val & GENMASK(25, 13)) >> 13; + *maxuse = val & GENMASK(12, 0); +} + static const struct ocelot_ops vsc9953_ops = { .reset = vsc9953_reset, .wm_enc = vsc9953_wm_enc, + .wm_dec = vsc9953_wm_dec, + .wm_stat = vsc9953_wm_stat, .port_to_netdev = felix_port_to_netdev, .netdev_to_port = felix_netdev_to_port, }; @@ -1181,9 +1199,9 @@ static const struct felix_info seville_info_vsc9953 = { .stats_layout = vsc9953_stats_layout, .num_stats = ARRAY_SIZE(vsc9953_stats_layout), .vcap = vsc9953_vcap_props, - .shared_queue_sz = 256 * 1024, .num_mact_rows = 2048, .num_ports = 10, + .num_tx_queues = OCELOT_NUM_TC, .mdio_bus_alloc = vsc9953_mdio_bus_alloc, .mdio_bus_free = vsc9953_mdio_bus_free, .phylink_validate = vsc9953_phylink_validate, diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 4d49c5f2b790..ca2ad77b71f1 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -101,6 +101,9 @@ AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \ AR9331_SW_PORT_STATUS_SPEED_M) +/* MIB registers */ +#define AR9331_MIB_COUNTER(x) (0x20000 + ((x) * 0x100)) + /* Phy bypass mode * ------------------------------------------------------------------------ * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | @@ -154,6 +157,66 @@ #define AR9331_SW_MDIO_POLL_SLEEP_US 1 #define AR9331_SW_MDIO_POLL_TIMEOUT_US 20 +/* The interval should be small enough to avoid overflow of 32bit MIBs */ +/* + * FIXME: until we can read MIBs from stats64 call directly (i.e. sleep + * there), we have to poll stats more frequently then it is actually needed. + * For overflow protection, normally, 100 sec interval should have been OK. + */ +#define STATS_INTERVAL_JIFFIES (3 * HZ) + +struct ar9331_sw_stats_raw { + u32 rxbroad; /* 0x00 */ + u32 rxpause; /* 0x04 */ + u32 rxmulti; /* 0x08 */ + u32 rxfcserr; /* 0x0c */ + u32 rxalignerr; /* 0x10 */ + u32 rxrunt; /* 0x14 */ + u32 rxfragment; /* 0x18 */ + u32 rx64byte; /* 0x1c */ + u32 rx128byte; /* 0x20 */ + u32 rx256byte; /* 0x24 */ + u32 rx512byte; /* 0x28 */ + u32 rx1024byte; /* 0x2c */ + u32 rx1518byte; /* 0x30 */ + u32 rxmaxbyte; /* 0x34 */ + u32 rxtoolong; /* 0x38 */ + u32 rxgoodbyte; /* 0x3c */ + u32 rxgoodbyte_hi; + u32 rxbadbyte; /* 0x44 */ + u32 rxbadbyte_hi; + u32 rxoverflow; /* 0x4c */ + u32 filtered; /* 0x50 */ + u32 txbroad; /* 0x54 */ + u32 txpause; /* 0x58 */ + u32 txmulti; /* 0x5c */ + u32 txunderrun; /* 0x60 */ + u32 tx64byte; /* 0x64 */ + u32 tx128byte; /* 0x68 */ + u32 tx256byte; /* 0x6c */ + u32 tx512byte; /* 0x70 */ + u32 tx1024byte; /* 0x74 */ + u32 tx1518byte; /* 0x78 */ + u32 txmaxbyte; /* 0x7c */ + u32 txoversize; /* 0x80 */ + u32 txbyte; /* 0x84 */ + u32 txbyte_hi; + u32 txcollision; /* 0x8c */ + u32 txabortcol; /* 0x90 */ + u32 txmulticol; /* 0x94 */ + u32 txsinglecol; /* 0x98 */ + u32 txexcdefer; /* 0x9c */ + u32 txdefer; /* 0xa0 */ + u32 txlatecol; /* 0xa4 */ +}; + +struct ar9331_sw_port { + int idx; + struct delayed_work mib_read; + struct rtnl_link_stats64 stats; + struct spinlock stats_lock; +}; + struct ar9331_sw_priv { struct device *dev; struct dsa_switch ds; @@ -165,8 +228,17 @@ struct ar9331_sw_priv { struct mii_bus *sbus; /* mdio slave */ struct regmap *regmap; struct reset_control *sw_reset; + struct ar9331_sw_port port[AR9331_SW_PORTS]; }; +static struct ar9331_sw_priv *ar9331_sw_port_to_priv(struct ar9331_sw_port *port) +{ + struct ar9331_sw_port *p = port - port->idx; + + return (struct ar9331_sw_priv *)((void *)p - + offsetof(struct ar9331_sw_priv, port)); +} + /* Warning: switch reset will reset last AR9331_SW_MDIO_PHY_MODE_PAGE request * If some kind of optimization is used, the request should be repeated. */ @@ -330,6 +402,8 @@ static int ar9331_sw_setup(struct dsa_switch *ds) if (ret) goto error; + ds->configure_vlan_while_not_filtering = false; + return 0; error: dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); @@ -424,6 +498,7 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, phy_interface_t interface) { struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct ar9331_sw_port *p = &priv->port[port]; struct regmap *regmap = priv->regmap; int ret; @@ -431,6 +506,8 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, AR9331_SW_PORT_STATUS_MAC_MASK, 0); if (ret) dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); + + cancel_delayed_work_sync(&p->mib_read); } static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, @@ -441,10 +518,13 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, bool tx_pause, bool rx_pause) { struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct ar9331_sw_port *p = &priv->port[port]; struct regmap *regmap = priv->regmap; u32 val; int ret; + schedule_delayed_work(&p->mib_read, 0); + val = AR9331_SW_PORT_STATUS_MAC_MASK; switch (speed) { case SPEED_1000: @@ -477,6 +557,73 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); } +static void ar9331_read_stats(struct ar9331_sw_port *port) +{ + struct ar9331_sw_priv *priv = ar9331_sw_port_to_priv(port); + struct rtnl_link_stats64 *stats = &port->stats; + struct ar9331_sw_stats_raw raw; + int ret; + + /* Do the slowest part first, to avoid needless locking for long time */ + ret = regmap_bulk_read(priv->regmap, AR9331_MIB_COUNTER(port->idx), + &raw, sizeof(raw) / sizeof(u32)); + if (ret) { + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); + return; + } + /* All MIB counters are cleared automatically on read */ + + spin_lock(&port->stats_lock); + + stats->rx_bytes += raw.rxgoodbyte; + stats->tx_bytes += raw.txbyte; + + stats->rx_packets += raw.rx64byte + raw.rx128byte + raw.rx256byte + + raw.rx512byte + raw.rx1024byte + raw.rx1518byte + raw.rxmaxbyte; + stats->tx_packets += raw.tx64byte + raw.tx128byte + raw.tx256byte + + raw.tx512byte + raw.tx1024byte + raw.tx1518byte + raw.txmaxbyte; + + stats->rx_length_errors += raw.rxrunt + raw.rxfragment + raw.rxtoolong; + stats->rx_crc_errors += raw.rxfcserr; + stats->rx_frame_errors += raw.rxalignerr; + stats->rx_missed_errors += raw.rxoverflow; + stats->rx_dropped += raw.filtered; + stats->rx_errors += raw.rxfcserr + raw.rxalignerr + raw.rxrunt + + raw.rxfragment + raw.rxoverflow + raw.rxtoolong; + + stats->tx_window_errors += raw.txlatecol; + stats->tx_fifo_errors += raw.txunderrun; + stats->tx_aborted_errors += raw.txabortcol; + stats->tx_errors += raw.txoversize + raw.txabortcol + raw.txunderrun + + raw.txlatecol; + + stats->multicast += raw.rxmulti; + stats->collisions += raw.txcollision; + + spin_unlock(&port->stats_lock); +} + +static void ar9331_do_stats_poll(struct work_struct *work) +{ + struct ar9331_sw_port *port = container_of(work, struct ar9331_sw_port, + mib_read.work); + + ar9331_read_stats(port); + + schedule_delayed_work(&port->mib_read, STATS_INTERVAL_JIFFIES); +} + +static void ar9331_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct ar9331_sw_port *p = &priv->port[port]; + + spin_lock(&p->stats_lock); + memcpy(s, &p->stats, sizeof(*s)); + spin_unlock(&p->stats_lock); +} + static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, @@ -485,6 +632,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .phylink_mac_config = ar9331_sw_phylink_mac_config, .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down, .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up, + .get_stats64 = ar9331_get_stats64, }; static irqreturn_t ar9331_sw_irq(int irq, void *data) @@ -796,7 +944,7 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev) { struct ar9331_sw_priv *priv; struct dsa_switch *ds; - int ret; + int ret, i; priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -831,6 +979,14 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev) ds->ops = &priv->ops; dev_set_drvdata(&mdiodev->dev, priv); + for (i = 0; i < ARRAY_SIZE(priv->port); i++) { + struct ar9331_sw_port *port = &priv->port[i]; + + port->idx = i; + spin_lock_init(&port->stats_lock); + INIT_DELAYED_WORK(&port->mib_read, ar9331_do_stats_poll); + } + ret = dsa_register_switch(ds); if (ret) goto err_remove_irq; @@ -846,6 +1002,13 @@ err_remove_irq: static void ar9331_sw_remove(struct mdio_device *mdiodev) { struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(priv->port); i++) { + struct ar9331_sw_port *port = &priv->port[i]; + + cancel_delayed_work_sync(&port->mib_read); + } irq_domain_remove(priv->irqdomain); mdiobus_unregister(priv->mbus); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 5bdac669a339..6127823d6c2e 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -1294,14 +1294,10 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port, } static int -qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, - struct switchdev_trans *trans) +qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) { struct qca8k_priv *priv = ds->priv; - if (switchdev_trans_ph_prepare(trans)) - return 0; - if (vlan_filtering) { qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), QCA8K_PORT_LOOKUP_VLAN_MODE, @@ -1316,13 +1312,6 @@ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, } static int -qca8k_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - return 0; -} - -static void qca8k_port_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { @@ -1330,24 +1319,24 @@ qca8k_port_vlan_add(struct dsa_switch *ds, int port, bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; struct qca8k_priv *priv = ds->priv; int ret = 0; - u16 vid; - for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid) - ret = qca8k_vlan_add(priv, port, vid, untagged); - - if (ret) + ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); + if (ret) { dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); + return ret; + } if (pvid) { int shift = 16 * (port % 2); qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), - 0xfff << shift, - vlan->vid_end << shift); + 0xfff << shift, vlan->vid << shift); qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), - QCA8K_PORT_VLAN_CVID(vlan->vid_end) | - QCA8K_PORT_VLAN_SVID(vlan->vid_end)); + QCA8K_PORT_VLAN_CVID(vlan->vid) | + QCA8K_PORT_VLAN_SVID(vlan->vid)); } + + return 0; } static int @@ -1356,11 +1345,8 @@ qca8k_port_vlan_del(struct dsa_switch *ds, int port, { struct qca8k_priv *priv = ds->priv; int ret = 0; - u16 vid; - - for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid) - ret = qca8k_vlan_del(priv, port, vid); + ret = qca8k_vlan_del(priv, port, vlan->vid); if (ret) dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); @@ -1393,7 +1379,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .port_fdb_del = qca8k_port_fdb_del, .port_fdb_dump = qca8k_port_fdb_dump, .port_vlan_filtering = qca8k_port_vlan_filtering, - .port_vlan_prepare = qca8k_port_vlan_prepare, .port_vlan_add = qca8k_port_vlan_add, .port_vlan_del = qca8k_port_vlan_del, .phylink_validate = qca8k_phylink_validate, @@ -1446,7 +1431,6 @@ qca8k_sw_probe(struct mdio_device *mdiodev) priv->ds->dev = &mdiodev->dev; priv->ds->num_ports = QCA8K_NUM_PORTS; - priv->ds->configure_vlan_while_not_filtering = true; priv->ds->priv = priv; priv->ops = qca8k_switch_ops; priv->ds->ops = &priv->ops; diff --git a/drivers/net/dsa/realtek-smi-core.h b/drivers/net/dsa/realtek-smi-core.h index 6b6a3dec0984..26376b052594 100644 --- a/drivers/net/dsa/realtek-smi-core.h +++ b/drivers/net/dsa/realtek-smi-core.h @@ -131,12 +131,9 @@ int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable); int rtl8366_reset_vlan(struct realtek_smi *smi); int rtl8366_init_vlan(struct realtek_smi *smi); int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering, - struct switchdev_trans *trans); -int rtl8366_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan); -void rtl8366_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan); + bool vlan_filtering); +int rtl8366_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); int rtl8366_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, diff --git a/drivers/net/dsa/rtl8366.c b/drivers/net/dsa/rtl8366.c index 83d481ef9273..3b24f2e13200 100644 --- a/drivers/net/dsa/rtl8366.c +++ b/drivers/net/dsa/rtl8366.c @@ -340,20 +340,15 @@ int rtl8366_init_vlan(struct realtek_smi *smi) } EXPORT_SYMBOL_GPL(rtl8366_init_vlan); -int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, - struct switchdev_trans *trans) +int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) { struct realtek_smi *smi = ds->priv; struct rtl8366_vlan_4k vlan4k; int ret; /* Use VLAN nr port + 1 since VLAN0 is not valid */ - if (switchdev_trans_ph_prepare(trans)) { - if (!smi->ops->is_vlan_valid(smi, port + 1)) - return -EINVAL; - - return 0; - } + if (!smi->ops->is_vlan_valid(smi, port + 1)) + return -EINVAL; dev_info(smi->dev, "%s filtering on port %d\n", vlan_filtering ? "enable" : "disable", @@ -379,76 +374,56 @@ int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, } EXPORT_SYMBOL_GPL(rtl8366_vlan_filtering); -int rtl8366_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct realtek_smi *smi = ds->priv; - u16 vid; - - for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++) - if (!smi->ops->is_vlan_valid(smi, vid)) - return -EINVAL; - - dev_info(smi->dev, "prepare VLANs %04x..%04x\n", - vlan->vid_begin, vlan->vid_end); - - /* Enable VLAN in the hardware - * FIXME: what's with this 4k business? - * Just rtl8366_enable_vlan() seems inconclusive. - */ - return rtl8366_enable_vlan4k(smi, true); -} -EXPORT_SYMBOL_GPL(rtl8366_vlan_prepare); - -void rtl8366_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +int rtl8366_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); struct realtek_smi *smi = ds->priv; u32 member = 0; u32 untag = 0; - u16 vid; int ret; - for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++) - if (!smi->ops->is_vlan_valid(smi, vid)) - return; + if (!smi->ops->is_vlan_valid(smi, vlan->vid)) + return -EINVAL; + + /* Enable VLAN in the hardware + * FIXME: what's with this 4k business? + * Just rtl8366_enable_vlan() seems inconclusive. + */ + ret = rtl8366_enable_vlan4k(smi, true); + if (ret) + return ret; dev_info(smi->dev, "add VLAN %d on port %d, %s, %s\n", - vlan->vid_begin, - port, - untagged ? "untagged" : "tagged", + vlan->vid, port, untagged ? "untagged" : "tagged", pvid ? " PVID" : "no PVID"); if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) dev_err(smi->dev, "port is DSA or CPU port\n"); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - member |= BIT(port); - - if (untagged) - untag |= BIT(port); + member |= BIT(port); - ret = rtl8366_set_vlan(smi, vid, member, untag, 0); - if (ret) - dev_err(smi->dev, - "failed to set up VLAN %04x", - vid); + if (untagged) + untag |= BIT(port); - if (!pvid) - continue; + ret = rtl8366_set_vlan(smi, vlan->vid, member, untag, 0); + if (ret) { + dev_err(smi->dev, "failed to set up VLAN %04x", vlan->vid); + return ret; + } - ret = rtl8366_set_pvid(smi, port, vid); - if (ret) - dev_err(smi->dev, - "failed to set PVID on port %d to VLAN %04x", - port, vid); + if (!pvid) + return 0; - if (!ret) - dev_dbg(smi->dev, "VLAN add: added VLAN %d with PVID on port %d\n", - vid, port); + ret = rtl8366_set_pvid(smi, port, vlan->vid); + if (ret) { + dev_err(smi->dev, "failed to set PVID on port %d to VLAN %04x", + port, vlan->vid); + return ret; } + + return 0; } EXPORT_SYMBOL_GPL(rtl8366_vlan_add); @@ -456,46 +431,39 @@ int rtl8366_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct realtek_smi *smi = ds->priv; - u16 vid; - int ret; - - dev_info(smi->dev, "del VLAN on port %d\n", port); + int ret, i; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - int i; + dev_info(smi->dev, "del VLAN %04x on port %d\n", vlan->vid, port); - dev_info(smi->dev, "del VLAN %04x\n", vid); + for (i = 0; i < smi->num_vlan_mc; i++) { + struct rtl8366_vlan_mc vlanmc; - for (i = 0; i < smi->num_vlan_mc; i++) { - struct rtl8366_vlan_mc vlanmc; + ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); + if (ret) + return ret; - ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); - if (ret) + if (vlan->vid == vlanmc.vid) { + /* Remove this port from the VLAN */ + vlanmc.member &= ~BIT(port); + vlanmc.untag &= ~BIT(port); + /* + * If no ports are members of this VLAN + * anymore then clear the whole member + * config so it can be reused. + */ + if (!vlanmc.member && vlanmc.untag) { + vlanmc.vid = 0; + vlanmc.priority = 0; + vlanmc.fid = 0; + } + ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); + if (ret) { + dev_err(smi->dev, + "failed to remove VLAN %04x\n", + vlan->vid); return ret; - - if (vid == vlanmc.vid) { - /* Remove this port from the VLAN */ - vlanmc.member &= ~BIT(port); - vlanmc.untag &= ~BIT(port); - /* - * If no ports are members of this VLAN - * anymore then clear the whole member - * config so it can be reused. - */ - if (!vlanmc.member && vlanmc.untag) { - vlanmc.vid = 0; - vlanmc.priority = 0; - vlanmc.fid = 0; - } - ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); - if (ret) { - dev_err(smi->dev, - "failed to remove VLAN %04x\n", - vid); - return ret; - } - break; } + break; } } diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/rtl8366rb.c index cfe56960f44b..a89093bc6c6a 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/rtl8366rb.c @@ -601,108 +601,114 @@ static int rtl8366rb_set_addr(struct realtek_smi *smi) /* Found in a vendor driver */ +/* Struct for handling the jam tables' entries */ +struct rtl8366rb_jam_tbl_entry { + u16 reg; + u16 val; +}; + /* For the "version 0" early silicon, appear in most source releases */ -static const u16 rtl8366rb_init_jam_ver_0[] = { - 0x000B, 0x0001, 0x03A6, 0x0100, 0x03A7, 0x0001, 0x02D1, 0x3FFF, - 0x02D2, 0x3FFF, 0x02D3, 0x3FFF, 0x02D4, 0x3FFF, 0x02D5, 0x3FFF, - 0x02D6, 0x3FFF, 0x02D7, 0x3FFF, 0x02D8, 0x3FFF, 0x022B, 0x0688, - 0x022C, 0x0FAC, 0x03D0, 0x4688, 0x03D1, 0x01F5, 0x0000, 0x0830, - 0x02F9, 0x0200, 0x02F7, 0x7FFF, 0x02F8, 0x03FF, 0x0080, 0x03E8, - 0x0081, 0x00CE, 0x0082, 0x00DA, 0x0083, 0x0230, 0xBE0F, 0x2000, - 0x0231, 0x422A, 0x0232, 0x422A, 0x0233, 0x422A, 0x0234, 0x422A, - 0x0235, 0x422A, 0x0236, 0x422A, 0x0237, 0x422A, 0x0238, 0x422A, - 0x0239, 0x422A, 0x023A, 0x422A, 0x023B, 0x422A, 0x023C, 0x422A, - 0x023D, 0x422A, 0x023E, 0x422A, 0x023F, 0x422A, 0x0240, 0x422A, - 0x0241, 0x422A, 0x0242, 0x422A, 0x0243, 0x422A, 0x0244, 0x422A, - 0x0245, 0x422A, 0x0246, 0x422A, 0x0247, 0x422A, 0x0248, 0x422A, - 0x0249, 0x0146, 0x024A, 0x0146, 0x024B, 0x0146, 0xBE03, 0xC961, - 0x024D, 0x0146, 0x024E, 0x0146, 0x024F, 0x0146, 0x0250, 0x0146, - 0xBE64, 0x0226, 0x0252, 0x0146, 0x0253, 0x0146, 0x024C, 0x0146, - 0x0251, 0x0146, 0x0254, 0x0146, 0xBE62, 0x3FD0, 0x0084, 0x0320, - 0x0255, 0x0146, 0x0256, 0x0146, 0x0257, 0x0146, 0x0258, 0x0146, - 0x0259, 0x0146, 0x025A, 0x0146, 0x025B, 0x0146, 0x025C, 0x0146, - 0x025D, 0x0146, 0x025E, 0x0146, 0x025F, 0x0146, 0x0260, 0x0146, - 0x0261, 0xA23F, 0x0262, 0x0294, 0x0263, 0xA23F, 0x0264, 0x0294, - 0x0265, 0xA23F, 0x0266, 0x0294, 0x0267, 0xA23F, 0x0268, 0x0294, - 0x0269, 0xA23F, 0x026A, 0x0294, 0x026B, 0xA23F, 0x026C, 0x0294, - 0x026D, 0xA23F, 0x026E, 0x0294, 0x026F, 0xA23F, 0x0270, 0x0294, - 0x02F5, 0x0048, 0xBE09, 0x0E00, 0xBE1E, 0x0FA0, 0xBE14, 0x8448, - 0xBE15, 0x1007, 0xBE4A, 0xA284, 0xC454, 0x3F0B, 0xC474, 0x3F0B, - 0xBE48, 0x3672, 0xBE4B, 0x17A7, 0xBE4C, 0x0B15, 0xBE52, 0x0EDD, - 0xBE49, 0x8C00, 0xBE5B, 0x785C, 0xBE5C, 0x785C, 0xBE5D, 0x785C, - 0xBE61, 0x368A, 0xBE63, 0x9B84, 0xC456, 0xCC13, 0xC476, 0xCC13, - 0xBE65, 0x307D, 0xBE6D, 0x0005, 0xBE6E, 0xE120, 0xBE2E, 0x7BAF, +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_0[] = { + {0x000B, 0x0001}, {0x03A6, 0x0100}, {0x03A7, 0x0001}, {0x02D1, 0x3FFF}, + {0x02D2, 0x3FFF}, {0x02D3, 0x3FFF}, {0x02D4, 0x3FFF}, {0x02D5, 0x3FFF}, + {0x02D6, 0x3FFF}, {0x02D7, 0x3FFF}, {0x02D8, 0x3FFF}, {0x022B, 0x0688}, + {0x022C, 0x0FAC}, {0x03D0, 0x4688}, {0x03D1, 0x01F5}, {0x0000, 0x0830}, + {0x02F9, 0x0200}, {0x02F7, 0x7FFF}, {0x02F8, 0x03FF}, {0x0080, 0x03E8}, + {0x0081, 0x00CE}, {0x0082, 0x00DA}, {0x0083, 0x0230}, {0xBE0F, 0x2000}, + {0x0231, 0x422A}, {0x0232, 0x422A}, {0x0233, 0x422A}, {0x0234, 0x422A}, + {0x0235, 0x422A}, {0x0236, 0x422A}, {0x0237, 0x422A}, {0x0238, 0x422A}, + {0x0239, 0x422A}, {0x023A, 0x422A}, {0x023B, 0x422A}, {0x023C, 0x422A}, + {0x023D, 0x422A}, {0x023E, 0x422A}, {0x023F, 0x422A}, {0x0240, 0x422A}, + {0x0241, 0x422A}, {0x0242, 0x422A}, {0x0243, 0x422A}, {0x0244, 0x422A}, + {0x0245, 0x422A}, {0x0246, 0x422A}, {0x0247, 0x422A}, {0x0248, 0x422A}, + {0x0249, 0x0146}, {0x024A, 0x0146}, {0x024B, 0x0146}, {0xBE03, 0xC961}, + {0x024D, 0x0146}, {0x024E, 0x0146}, {0x024F, 0x0146}, {0x0250, 0x0146}, + {0xBE64, 0x0226}, {0x0252, 0x0146}, {0x0253, 0x0146}, {0x024C, 0x0146}, + {0x0251, 0x0146}, {0x0254, 0x0146}, {0xBE62, 0x3FD0}, {0x0084, 0x0320}, + {0x0255, 0x0146}, {0x0256, 0x0146}, {0x0257, 0x0146}, {0x0258, 0x0146}, + {0x0259, 0x0146}, {0x025A, 0x0146}, {0x025B, 0x0146}, {0x025C, 0x0146}, + {0x025D, 0x0146}, {0x025E, 0x0146}, {0x025F, 0x0146}, {0x0260, 0x0146}, + {0x0261, 0xA23F}, {0x0262, 0x0294}, {0x0263, 0xA23F}, {0x0264, 0x0294}, + {0x0265, 0xA23F}, {0x0266, 0x0294}, {0x0267, 0xA23F}, {0x0268, 0x0294}, + {0x0269, 0xA23F}, {0x026A, 0x0294}, {0x026B, 0xA23F}, {0x026C, 0x0294}, + {0x026D, 0xA23F}, {0x026E, 0x0294}, {0x026F, 0xA23F}, {0x0270, 0x0294}, + {0x02F5, 0x0048}, {0xBE09, 0x0E00}, {0xBE1E, 0x0FA0}, {0xBE14, 0x8448}, + {0xBE15, 0x1007}, {0xBE4A, 0xA284}, {0xC454, 0x3F0B}, {0xC474, 0x3F0B}, + {0xBE48, 0x3672}, {0xBE4B, 0x17A7}, {0xBE4C, 0x0B15}, {0xBE52, 0x0EDD}, + {0xBE49, 0x8C00}, {0xBE5B, 0x785C}, {0xBE5C, 0x785C}, {0xBE5D, 0x785C}, + {0xBE61, 0x368A}, {0xBE63, 0x9B84}, {0xC456, 0xCC13}, {0xC476, 0xCC13}, + {0xBE65, 0x307D}, {0xBE6D, 0x0005}, {0xBE6E, 0xE120}, {0xBE2E, 0x7BAF}, }; /* This v1 init sequence is from Belkin F5D8235 U-Boot release */ -static const u16 rtl8366rb_init_jam_ver_1[] = { - 0x0000, 0x0830, 0x0001, 0x8000, 0x0400, 0x8130, 0xBE78, 0x3C3C, - 0x0431, 0x5432, 0xBE37, 0x0CE4, 0x02FA, 0xFFDF, 0x02FB, 0xFFE0, - 0xC44C, 0x1585, 0xC44C, 0x1185, 0xC44C, 0x1585, 0xC46C, 0x1585, - 0xC46C, 0x1185, 0xC46C, 0x1585, 0xC451, 0x2135, 0xC471, 0x2135, - 0xBE10, 0x8140, 0xBE15, 0x0007, 0xBE6E, 0xE120, 0xBE69, 0xD20F, - 0xBE6B, 0x0320, 0xBE24, 0xB000, 0xBE23, 0xFF51, 0xBE22, 0xDF20, - 0xBE21, 0x0140, 0xBE20, 0x00BB, 0xBE24, 0xB800, 0xBE24, 0x0000, - 0xBE24, 0x7000, 0xBE23, 0xFF51, 0xBE22, 0xDF60, 0xBE21, 0x0140, - 0xBE20, 0x0077, 0xBE24, 0x7800, 0xBE24, 0x0000, 0xBE2E, 0x7B7A, - 0xBE36, 0x0CE4, 0x02F5, 0x0048, 0xBE77, 0x2940, 0x000A, 0x83E0, - 0xBE79, 0x3C3C, 0xBE00, 0x1340, +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_1[] = { + {0x0000, 0x0830}, {0x0001, 0x8000}, {0x0400, 0x8130}, {0xBE78, 0x3C3C}, + {0x0431, 0x5432}, {0xBE37, 0x0CE4}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, + {0xC44C, 0x1585}, {0xC44C, 0x1185}, {0xC44C, 0x1585}, {0xC46C, 0x1585}, + {0xC46C, 0x1185}, {0xC46C, 0x1585}, {0xC451, 0x2135}, {0xC471, 0x2135}, + {0xBE10, 0x8140}, {0xBE15, 0x0007}, {0xBE6E, 0xE120}, {0xBE69, 0xD20F}, + {0xBE6B, 0x0320}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF20}, + {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, {0xBE24, 0x0000}, + {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, {0xBE21, 0x0140}, + {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, {0xBE2E, 0x7B7A}, + {0xBE36, 0x0CE4}, {0x02F5, 0x0048}, {0xBE77, 0x2940}, {0x000A, 0x83E0}, + {0xBE79, 0x3C3C}, {0xBE00, 0x1340}, }; /* This v2 init sequence is from Belkin F5D8235 U-Boot release */ -static const u16 rtl8366rb_init_jam_ver_2[] = { - 0x0450, 0x0000, 0x0400, 0x8130, 0x000A, 0x83ED, 0x0431, 0x5432, - 0xC44F, 0x6250, 0xC46F, 0x6250, 0xC456, 0x0C14, 0xC476, 0x0C14, - 0xC44C, 0x1C85, 0xC44C, 0x1885, 0xC44C, 0x1C85, 0xC46C, 0x1C85, - 0xC46C, 0x1885, 0xC46C, 0x1C85, 0xC44C, 0x0885, 0xC44C, 0x0881, - 0xC44C, 0x0885, 0xC46C, 0x0885, 0xC46C, 0x0881, 0xC46C, 0x0885, - 0xBE2E, 0x7BA7, 0xBE36, 0x1000, 0xBE37, 0x1000, 0x8000, 0x0001, - 0xBE69, 0xD50F, 0x8000, 0x0000, 0xBE69, 0xD50F, 0xBE6E, 0x0320, - 0xBE77, 0x2940, 0xBE78, 0x3C3C, 0xBE79, 0x3C3C, 0xBE6E, 0xE120, - 0x8000, 0x0001, 0xBE15, 0x1007, 0x8000, 0x0000, 0xBE15, 0x1007, - 0xBE14, 0x0448, 0xBE1E, 0x00A0, 0xBE10, 0x8160, 0xBE10, 0x8140, - 0xBE00, 0x1340, 0x0F51, 0x0010, +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_2[] = { + {0x0450, 0x0000}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432}, + {0xC44F, 0x6250}, {0xC46F, 0x6250}, {0xC456, 0x0C14}, {0xC476, 0x0C14}, + {0xC44C, 0x1C85}, {0xC44C, 0x1885}, {0xC44C, 0x1C85}, {0xC46C, 0x1C85}, + {0xC46C, 0x1885}, {0xC46C, 0x1C85}, {0xC44C, 0x0885}, {0xC44C, 0x0881}, + {0xC44C, 0x0885}, {0xC46C, 0x0885}, {0xC46C, 0x0881}, {0xC46C, 0x0885}, + {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001}, + {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6E, 0x0320}, + {0xBE77, 0x2940}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120}, + {0x8000, 0x0001}, {0xBE15, 0x1007}, {0x8000, 0x0000}, {0xBE15, 0x1007}, + {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, {0xBE10, 0x8140}, + {0xBE00, 0x1340}, {0x0F51, 0x0010}, }; /* Appears in a DDWRT code dump */ -static const u16 rtl8366rb_init_jam_ver_3[] = { - 0x0000, 0x0830, 0x0400, 0x8130, 0x000A, 0x83ED, 0x0431, 0x5432, - 0x0F51, 0x0017, 0x02F5, 0x0048, 0x02FA, 0xFFDF, 0x02FB, 0xFFE0, - 0xC456, 0x0C14, 0xC476, 0x0C14, 0xC454, 0x3F8B, 0xC474, 0x3F8B, - 0xC450, 0x2071, 0xC470, 0x2071, 0xC451, 0x226B, 0xC471, 0x226B, - 0xC452, 0xA293, 0xC472, 0xA293, 0xC44C, 0x1585, 0xC44C, 0x1185, - 0xC44C, 0x1585, 0xC46C, 0x1585, 0xC46C, 0x1185, 0xC46C, 0x1585, - 0xC44C, 0x0185, 0xC44C, 0x0181, 0xC44C, 0x0185, 0xC46C, 0x0185, - 0xC46C, 0x0181, 0xC46C, 0x0185, 0xBE24, 0xB000, 0xBE23, 0xFF51, - 0xBE22, 0xDF20, 0xBE21, 0x0140, 0xBE20, 0x00BB, 0xBE24, 0xB800, - 0xBE24, 0x0000, 0xBE24, 0x7000, 0xBE23, 0xFF51, 0xBE22, 0xDF60, - 0xBE21, 0x0140, 0xBE20, 0x0077, 0xBE24, 0x7800, 0xBE24, 0x0000, - 0xBE2E, 0x7BA7, 0xBE36, 0x1000, 0xBE37, 0x1000, 0x8000, 0x0001, - 0xBE69, 0xD50F, 0x8000, 0x0000, 0xBE69, 0xD50F, 0xBE6B, 0x0320, - 0xBE77, 0x2800, 0xBE78, 0x3C3C, 0xBE79, 0x3C3C, 0xBE6E, 0xE120, - 0x8000, 0x0001, 0xBE10, 0x8140, 0x8000, 0x0000, 0xBE10, 0x8140, - 0xBE15, 0x1007, 0xBE14, 0x0448, 0xBE1E, 0x00A0, 0xBE10, 0x8160, - 0xBE10, 0x8140, 0xBE00, 0x1340, 0x0450, 0x0000, 0x0401, 0x0000, +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_3[] = { + {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432}, + {0x0F51, 0x0017}, {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, + {0xC456, 0x0C14}, {0xC476, 0x0C14}, {0xC454, 0x3F8B}, {0xC474, 0x3F8B}, + {0xC450, 0x2071}, {0xC470, 0x2071}, {0xC451, 0x226B}, {0xC471, 0x226B}, + {0xC452, 0xA293}, {0xC472, 0xA293}, {0xC44C, 0x1585}, {0xC44C, 0x1185}, + {0xC44C, 0x1585}, {0xC46C, 0x1585}, {0xC46C, 0x1185}, {0xC46C, 0x1585}, + {0xC44C, 0x0185}, {0xC44C, 0x0181}, {0xC44C, 0x0185}, {0xC46C, 0x0185}, + {0xC46C, 0x0181}, {0xC46C, 0x0185}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, + {0xBE22, 0xDF20}, {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, + {0xBE24, 0x0000}, {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, + {0xBE21, 0x0140}, {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, + {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001}, + {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6B, 0x0320}, + {0xBE77, 0x2800}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120}, + {0x8000, 0x0001}, {0xBE10, 0x8140}, {0x8000, 0x0000}, {0xBE10, 0x8140}, + {0xBE15, 0x1007}, {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, + {0xBE10, 0x8140}, {0xBE00, 0x1340}, {0x0450, 0x0000}, {0x0401, 0x0000}, }; /* Belkin F5D8235 v1, "belkin,f5d8235-v1" */ -static const u16 rtl8366rb_init_jam_f5d8235[] = { - 0x0242, 0x02BF, 0x0245, 0x02BF, 0x0248, 0x02BF, 0x024B, 0x02BF, - 0x024E, 0x02BF, 0x0251, 0x02BF, 0x0254, 0x0A3F, 0x0256, 0x0A3F, - 0x0258, 0x0A3F, 0x025A, 0x0A3F, 0x025C, 0x0A3F, 0x025E, 0x0A3F, - 0x0263, 0x007C, 0x0100, 0x0004, 0xBE5B, 0x3500, 0x800E, 0x200F, - 0xBE1D, 0x0F00, 0x8001, 0x5011, 0x800A, 0xA2F4, 0x800B, 0x17A3, - 0xBE4B, 0x17A3, 0xBE41, 0x5011, 0xBE17, 0x2100, 0x8000, 0x8304, - 0xBE40, 0x8304, 0xBE4A, 0xA2F4, 0x800C, 0xA8D5, 0x8014, 0x5500, - 0x8015, 0x0004, 0xBE4C, 0xA8D5, 0xBE59, 0x0008, 0xBE09, 0x0E00, - 0xBE36, 0x1036, 0xBE37, 0x1036, 0x800D, 0x00FF, 0xBE4D, 0x00FF, +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_f5d8235[] = { + {0x0242, 0x02BF}, {0x0245, 0x02BF}, {0x0248, 0x02BF}, {0x024B, 0x02BF}, + {0x024E, 0x02BF}, {0x0251, 0x02BF}, {0x0254, 0x0A3F}, {0x0256, 0x0A3F}, + {0x0258, 0x0A3F}, {0x025A, 0x0A3F}, {0x025C, 0x0A3F}, {0x025E, 0x0A3F}, + {0x0263, 0x007C}, {0x0100, 0x0004}, {0xBE5B, 0x3500}, {0x800E, 0x200F}, + {0xBE1D, 0x0F00}, {0x8001, 0x5011}, {0x800A, 0xA2F4}, {0x800B, 0x17A3}, + {0xBE4B, 0x17A3}, {0xBE41, 0x5011}, {0xBE17, 0x2100}, {0x8000, 0x8304}, + {0xBE40, 0x8304}, {0xBE4A, 0xA2F4}, {0x800C, 0xA8D5}, {0x8014, 0x5500}, + {0x8015, 0x0004}, {0xBE4C, 0xA8D5}, {0xBE59, 0x0008}, {0xBE09, 0x0E00}, + {0xBE36, 0x1036}, {0xBE37, 0x1036}, {0x800D, 0x00FF}, {0xBE4D, 0x00FF}, }; /* DGN3500, "netgear,dgn3500", "netgear,dgn3500b" */ -static const u16 rtl8366rb_init_jam_dgn3500[] = { - 0x0000, 0x0830, 0x0400, 0x8130, 0x000A, 0x83ED, 0x0F51, 0x0017, - 0x02F5, 0x0048, 0x02FA, 0xFFDF, 0x02FB, 0xFFE0, 0x0450, 0x0000, - 0x0401, 0x0000, 0x0431, 0x0960, +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_dgn3500[] = { + {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0F51, 0x0017}, + {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, {0x0450, 0x0000}, + {0x0401, 0x0000}, {0x0431, 0x0960}, }; /* This jam table activates "green ethernet", which means low power mode @@ -710,16 +716,53 @@ static const u16 rtl8366rb_init_jam_dgn3500[] = { * necessary, and the ports should enter power saving mode 10 seconds after * a cable is disconnected. Seems to always be the same. */ -static const u16 rtl8366rb_green_jam[][2] = { +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_green_jam[] = { {0xBE78, 0x323C}, {0xBE77, 0x5000}, {0xBE2E, 0x7BA7}, {0xBE59, 0x3459}, {0xBE5A, 0x745A}, {0xBE5B, 0x785C}, {0xBE5C, 0x785C}, {0xBE6E, 0xE120}, {0xBE79, 0x323C}, }; +/* Function that jams the tables in the proper registers */ +static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, + int jam_size, struct realtek_smi *smi, + bool write_dbg) +{ + u32 val; + int ret; + int i; + + for (i = 0; i < jam_size; i++) { + if ((jam_table[i].reg & 0xBE00) == 0xBE00) { + ret = regmap_read(smi->map, + RTL8366RB_PHY_ACCESS_BUSY_REG, + &val); + if (ret) + return ret; + if (!(val & RTL8366RB_PHY_INT_BUSY)) { + ret = regmap_write(smi->map, + RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_WRITE); + if (ret) + return ret; + } + } + if (write_dbg) + dev_dbg(smi->dev, "jam %04x into register %04x\n", + jam_table[i].val, + jam_table[i].reg); + ret = regmap_write(smi->map, + jam_table[i].reg, + jam_table[i].val); + if (ret) + return ret; + } + return 0; +} + static int rtl8366rb_setup(struct dsa_switch *ds) { struct realtek_smi *smi = ds->priv; - const u16 *jam_table; + const struct rtl8366rb_jam_tbl_entry *jam_table; struct rtl8366rb *rb; u32 chip_ver = 0; u32 chip_id = 0; @@ -788,54 +831,16 @@ static int rtl8366rb_setup(struct dsa_switch *ds) jam_size = ARRAY_SIZE(rtl8366rb_init_jam_dgn3500); } - i = 0; - while (i < jam_size) { - if ((jam_table[i] & 0xBE00) == 0xBE00) { - ret = regmap_read(smi->map, - RTL8366RB_PHY_ACCESS_BUSY_REG, - &val); - if (ret) - return ret; - if (!(val & RTL8366RB_PHY_INT_BUSY)) { - ret = regmap_write(smi->map, - RTL8366RB_PHY_ACCESS_CTRL_REG, - RTL8366RB_PHY_CTRL_WRITE); - if (ret) - return ret; - } - } - dev_dbg(smi->dev, "jam %04x into register %04x\n", - jam_table[i + 1], - jam_table[i]); - ret = regmap_write(smi->map, - jam_table[i], - jam_table[i + 1]); - if (ret) - return ret; - i += 2; - } + ret = rtl8366rb_jam_table(jam_table, jam_size, smi, true); + if (ret) + return ret; /* Set up the "green ethernet" feature */ - i = 0; - while (i < ARRAY_SIZE(rtl8366rb_green_jam)) { - ret = regmap_read(smi->map, RTL8366RB_PHY_ACCESS_BUSY_REG, - &val); - if (ret) - return ret; - if (!(val & RTL8366RB_PHY_INT_BUSY)) { - ret = regmap_write(smi->map, - RTL8366RB_PHY_ACCESS_CTRL_REG, - RTL8366RB_PHY_CTRL_WRITE); - if (ret) - return ret; - ret = regmap_write(smi->map, - rtl8366rb_green_jam[i][0], - rtl8366rb_green_jam[i][1]); - if (ret) - return ret; - i++; - } - } + ret = rtl8366rb_jam_table(rtl8366rb_green_jam, + ARRAY_SIZE(rtl8366rb_green_jam), smi, false); + if (ret) + return ret; + ret = regmap_write(smi->map, RTL8366RB_GREEN_FEATURE_REG, (chip_ver == 1) ? 0x0007 : 0x0003); @@ -972,6 +977,8 @@ static int rtl8366rb_setup(struct dsa_switch *ds) return -ENODEV; } + ds->configure_vlan_while_not_filtering = false; + return 0; } @@ -1504,7 +1511,6 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = { .get_ethtool_stats = rtl8366_get_ethtool_stats, .get_sset_count = rtl8366_get_sset_count, .port_vlan_filtering = rtl8366_vlan_filtering, - .port_vlan_prepare = rtl8366_vlan_prepare, .port_vlan_add = rtl8366_vlan_add, .port_vlan_del = rtl8366_vlan_del, .port_enable = rtl8366rb_port_enable, diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 4ebc4a5a7b35..d582308c2401 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -245,8 +245,7 @@ enum sja1105_reset_reason { int sja1105_static_config_reload(struct sja1105_private *priv, enum sja1105_reset_reason reason); -int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, - struct switchdev_trans *trans); +int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled); void sja1105_frame_memory_partitioning(struct sja1105_private *priv); /* From sja1105_devlink.c */ diff --git a/drivers/net/dsa/sja1105/sja1105_devlink.c b/drivers/net/dsa/sja1105/sja1105_devlink.c index 4a2ec395bcb0..b4bf1b10e66c 100644 --- a/drivers/net/dsa/sja1105/sja1105_devlink.c +++ b/drivers/net/dsa/sja1105/sja1105_devlink.c @@ -135,7 +135,6 @@ static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv, rtnl_lock(); for (port = 0; port < ds->num_ports; port++) { - struct switchdev_trans trans; struct dsa_port *dp; if (!dsa_is_user_port(ds, port)) @@ -144,13 +143,7 @@ static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv, dp = dsa_to_port(ds, port); vlan_filtering = dsa_port_is_vlan_filtering(dp); - trans.ph_prepare = true; - rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans); - if (rc) - break; - - trans.ph_prepare = false; - rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans); + rc = sja1105_vlan_filtering(ds, port, vlan_filtering); if (rc) break; } diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 4ca029650993..282253543f3b 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -317,7 +317,7 @@ static int sja1105_init_static_vlan(struct sja1105_private *priv) table->entry_count = 0; } - table->entries = kcalloc(1, table->ops->unpacked_entry_size, + table->entries = kzalloc(table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; @@ -1524,17 +1524,10 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, return 0; } -/* This callback needs to be present */ -static int sja1105_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) -{ - return 0; -} - -static void sja1105_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static int sja1105_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) { - sja1105_fdb_add(ds, port, mdb->addr, mdb->vid); + return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid); } static int sja1105_mdb_del(struct dsa_switch *ds, int port, @@ -2607,35 +2600,11 @@ out: return rc; } -static int sja1105_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct sja1105_private *priv = ds->priv; - u16 vid; - - if (priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) - return 0; - - /* If the user wants best-effort VLAN filtering (aka vlan_filtering - * bridge plus tagging), be sure to at least deny alterations to the - * configuration done by dsa_8021q. - */ - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - if (vid_is_dsa_8021q(vid)) { - dev_err(ds->dev, "Range 1024-3071 reserved for dsa_8021q operation\n"); - return -EBUSY; - } - } - - return 0; -} - /* The TPID setting belongs to the General Parameters table, * which can only be partially reconfigured at runtime (and not the TPID). * So a switch reset is required. */ -int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, - struct switchdev_trans *trans) +int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) { struct sja1105_l2_lookup_params_entry *l2_lookup_params; struct sja1105_general_params_entry *general_params; @@ -2647,16 +2616,12 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, u16 tpid, tpid2; int rc; - if (switchdev_trans_ph_prepare(trans)) { - list_for_each_entry(rule, &priv->flow_block.rules, list) { - if (rule->type == SJA1105_RULE_VL) { - dev_err(ds->dev, - "Cannot change VLAN filtering with active VL rules\n"); - return -EBUSY; - } + list_for_each_entry(rule, &priv->flow_block.rules, list) { + if (rule->type == SJA1105_RULE_VL) { + dev_err(ds->dev, + "Cannot change VLAN filtering with active VL rules\n"); + return -EBUSY; } - - return 0; } if (enabled) { @@ -2794,29 +2759,34 @@ static int sja1105_vlan_del_one(struct dsa_switch *ds, int port, u16 vid, return 0; } -static void sja1105_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int sja1105_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct sja1105_private *priv = ds->priv; bool vlan_table_changed = false; - u16 vid; int rc; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - rc = sja1105_vlan_add_one(ds, port, vid, vlan->flags, - &priv->bridge_vlans); - if (rc < 0) - return; - if (rc > 0) - vlan_table_changed = true; + /* If the user wants best-effort VLAN filtering (aka vlan_filtering + * bridge plus tagging), be sure to at least deny alterations to the + * configuration done by dsa_8021q. + */ + if (priv->vlan_state != SJA1105_VLAN_FILTERING_FULL && + vid_is_dsa_8021q(vlan->vid)) { + dev_err(ds->dev, "Range 1024-3071 reserved for dsa_8021q operation\n"); + return -EBUSY; } + rc = sja1105_vlan_add_one(ds, port, vlan->vid, vlan->flags, + &priv->bridge_vlans); + if (rc < 0) + return rc; + if (rc > 0) + vlan_table_changed = true; + if (!vlan_table_changed) - return; + return 0; - rc = sja1105_build_vlan_table(priv, true); - if (rc) - dev_err(ds->dev, "Failed to build VLAN table: %d\n", rc); + return sja1105_build_vlan_table(priv, true); } static int sja1105_vlan_del(struct dsa_switch *ds, int port, @@ -2824,14 +2794,11 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port, { struct sja1105_private *priv = ds->priv; bool vlan_table_changed = false; - u16 vid; int rc; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - rc = sja1105_vlan_del_one(ds, port, vid, &priv->bridge_vlans); - if (rc > 0) - vlan_table_changed = true; - } + rc = sja1105_vlan_del_one(ds, port, vlan->vid, &priv->bridge_vlans); + if (rc > 0) + vlan_table_changed = true; if (!vlan_table_changed) return 0; @@ -2934,8 +2901,6 @@ static int sja1105_setup(struct dsa_switch *ds) ds->mtu_enforcement_ingress = true; - ds->configure_vlan_while_not_filtering = true; - rc = sja1105_devlink_setup(ds); if (rc < 0) return rc; @@ -3298,11 +3263,9 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .port_bridge_join = sja1105_bridge_join, .port_bridge_leave = sja1105_bridge_leave, .port_stp_state_set = sja1105_bridge_stp_state_set, - .port_vlan_prepare = sja1105_vlan_prepare, .port_vlan_filtering = sja1105_vlan_filtering, .port_vlan_add = sja1105_vlan_add, .port_vlan_del = sja1105_vlan_del, - .port_mdb_prepare = sja1105_mdb_prepare, .port_mdb_add = sja1105_mdb_add, .port_mdb_del = sja1105_mdb_del, .port_hwtstamp_get = sja1105_hwtstamp_get, diff --git a/drivers/net/dsa/xrs700x/Kconfig b/drivers/net/dsa/xrs700x/Kconfig new file mode 100644 index 000000000000..d10a4dce1676 --- /dev/null +++ b/drivers/net/dsa/xrs700x/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_XRS700X + tristate + depends on NET_DSA + select NET_DSA_TAG_XRS700X + select REGMAP + help + This enables support for Arrow SpeedChips XRS7003/7004 gigabit + Ethernet switches. + +config NET_DSA_XRS700X_I2C + tristate "Arrow XRS7000X series switch in I2C mode" + depends on NET_DSA && I2C + select NET_DSA_XRS700X + select REGMAP_I2C + help + Enable I2C support for Arrow SpeedChips XRS7003/7004 gigabit Ethernet + switches. + +config NET_DSA_XRS700X_MDIO + tristate "Arrow XRS7000X series switch in MDIO mode" + depends on NET_DSA + select NET_DSA_XRS700X + help + Enable MDIO support for Arrow SpeedChips XRS7003/7004 gigabit Ethernet + switches. diff --git a/drivers/net/dsa/xrs700x/Makefile b/drivers/net/dsa/xrs700x/Makefile new file mode 100644 index 000000000000..51a3a7d9296a --- /dev/null +++ b/drivers/net/dsa/xrs700x/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_NET_DSA_XRS700X) += xrs700x.o +obj-$(CONFIG_NET_DSA_XRS700X_I2C) += xrs700x_i2c.o +obj-$(CONFIG_NET_DSA_XRS700X_MDIO) += xrs700x_mdio.o diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c new file mode 100644 index 000000000000..259f5e657c46 --- /dev/null +++ b/drivers/net/dsa/xrs700x/xrs700x.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 NovaTech LLC + * George McCollister <george.mccollister@gmail.com> + */ + +#include <net/dsa.h> +#include <linux/if_bridge.h> +#include <linux/of_device.h> +#include "xrs700x.h" +#include "xrs700x_reg.h" + +#define XRS700X_MIB_INTERVAL msecs_to_jiffies(3000) + +#define XRS7003E_ID 0x100 +#define XRS7003F_ID 0x101 +#define XRS7004E_ID 0x200 +#define XRS7004F_ID 0x201 + +const struct xrs700x_info xrs7003e_info = {XRS7003E_ID, "XRS7003E", 3}; +EXPORT_SYMBOL(xrs7003e_info); + +const struct xrs700x_info xrs7003f_info = {XRS7003F_ID, "XRS7003F", 3}; +EXPORT_SYMBOL(xrs7003f_info); + +const struct xrs700x_info xrs7004e_info = {XRS7004E_ID, "XRS7004E", 4}; +EXPORT_SYMBOL(xrs7004e_info); + +const struct xrs700x_info xrs7004f_info = {XRS7004F_ID, "XRS7004F", 4}; +EXPORT_SYMBOL(xrs7004f_info); + +struct xrs700x_regfield { + struct reg_field rf; + struct regmap_field **rmf; +}; + +struct xrs700x_mib { + unsigned int offset; + const char *name; + int stats64_offset; +}; + +#define XRS700X_MIB_ETHTOOL_ONLY(o, n) {o, n, -1} +#define XRS700X_MIB(o, n, m) {o, n, offsetof(struct rtnl_link_stats64, m)} + +static const struct xrs700x_mib xrs700x_mibs[] = { + XRS700X_MIB(XRS_RX_GOOD_OCTETS_L, "rx_good_octets", rx_bytes), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_BAD_OCTETS_L, "rx_bad_octets"), + XRS700X_MIB(XRS_RX_UNICAST_L, "rx_unicast", rx_packets), + XRS700X_MIB(XRS_RX_BROADCAST_L, "rx_broadcast", rx_packets), + XRS700X_MIB(XRS_RX_MULTICAST_L, "rx_multicast", multicast), + XRS700X_MIB(XRS_RX_UNDERSIZE_L, "rx_undersize", rx_length_errors), + XRS700X_MIB(XRS_RX_FRAGMENTS_L, "rx_fragments", rx_length_errors), + XRS700X_MIB(XRS_RX_OVERSIZE_L, "rx_oversize", rx_length_errors), + XRS700X_MIB(XRS_RX_JABBER_L, "rx_jabber", rx_length_errors), + XRS700X_MIB(XRS_RX_ERR_L, "rx_err", rx_errors), + XRS700X_MIB(XRS_RX_CRC_L, "rx_crc", rx_crc_errors), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_64_L, "rx_64"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_65_127_L, "rx_65_127"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_128_255_L, "rx_128_255"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_256_511_L, "rx_256_511"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_512_1023_L, "rx_512_1023"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_1024_1536_L, "rx_1024_1536"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_HSR_PRP_L, "rx_hsr_prp"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_WRONGLAN_L, "rx_wronglan"), + XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_DUPLICATE_L, "rx_duplicate"), + XRS700X_MIB(XRS_TX_OCTETS_L, "tx_octets", tx_bytes), + XRS700X_MIB(XRS_TX_UNICAST_L, "tx_unicast", tx_packets), + XRS700X_MIB(XRS_TX_BROADCAST_L, "tx_broadcast", tx_packets), + XRS700X_MIB(XRS_TX_MULTICAST_L, "tx_multicast", tx_packets), + XRS700X_MIB_ETHTOOL_ONLY(XRS_TX_HSR_PRP_L, "tx_hsr_prp"), + XRS700X_MIB(XRS_PRIQ_DROP_L, "priq_drop", tx_dropped), + XRS700X_MIB(XRS_EARLY_DROP_L, "early_drop", tx_dropped), +}; + +static void xrs700x_get_strings(struct dsa_switch *ds, int port, + u32 stringset, u8 *data) +{ + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) { + strscpy(data, xrs700x_mibs[i].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } +} + +static int xrs700x_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + if (sset != ETH_SS_STATS) + return -EOPNOTSUPP; + + return ARRAY_SIZE(xrs700x_mibs); +} + +static void xrs700x_read_port_counters(struct xrs700x *priv, int port) +{ + struct xrs700x_port *p = &priv->ports[port]; + struct rtnl_link_stats64 stats; + int i; + + memset(&stats, 0, sizeof(stats)); + + mutex_lock(&p->mib_mutex); + + /* Capture counter values */ + regmap_write(priv->regmap, XRS_CNT_CTRL(port), 1); + + for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) { + unsigned int high = 0, low = 0, reg; + + reg = xrs700x_mibs[i].offset + XRS_PORT_OFFSET * port; + regmap_read(priv->regmap, reg, &low); + regmap_read(priv->regmap, reg + 2, &high); + + p->mib_data[i] += (high << 16) | low; + + if (xrs700x_mibs[i].stats64_offset >= 0) { + u8 *s = (u8 *)&stats + xrs700x_mibs[i].stats64_offset; + *(u64 *)s += p->mib_data[i]; + } + } + + /* multicast must be added to rx_packets (which already includes + * unicast and broadcast) + */ + stats.rx_packets += stats.multicast; + + u64_stats_update_begin(&p->syncp); + p->stats64 = stats; + u64_stats_update_end(&p->syncp); + + mutex_unlock(&p->mib_mutex); +} + +static void xrs700x_mib_work(struct work_struct *work) +{ + struct xrs700x *priv = container_of(work, struct xrs700x, + mib_work.work); + int i; + + for (i = 0; i < priv->ds->num_ports; i++) + xrs700x_read_port_counters(priv, i); + + schedule_delayed_work(&priv->mib_work, XRS700X_MIB_INTERVAL); +} + +static void xrs700x_get_ethtool_stats(struct dsa_switch *ds, int port, + u64 *data) +{ + struct xrs700x *priv = ds->priv; + struct xrs700x_port *p = &priv->ports[port]; + + xrs700x_read_port_counters(priv, port); + + mutex_lock(&p->mib_mutex); + memcpy(data, p->mib_data, sizeof(*data) * ARRAY_SIZE(xrs700x_mibs)); + mutex_unlock(&p->mib_mutex); +} + +static void xrs700x_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) +{ + struct xrs700x *priv = ds->priv; + struct xrs700x_port *p = &priv->ports[port]; + unsigned int start; + + do { + start = u64_stats_fetch_begin(&p->syncp); + *s = p->stats64; + } while (u64_stats_fetch_retry(&p->syncp, start)); +} + +static int xrs700x_setup_regmap_range(struct xrs700x *priv) +{ + struct xrs700x_regfield regfields[] = { + { + .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 0, 1, + priv->ds->num_ports, + XRS_PORT_OFFSET), + .rmf = &priv->ps_forward + }, + { + .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 2, 3, + priv->ds->num_ports, + XRS_PORT_OFFSET), + .rmf = &priv->ps_management + }, + { + .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 4, 9, + priv->ds->num_ports, + XRS_PORT_OFFSET), + .rmf = &priv->ps_sel_speed + }, + { + .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 10, 11, + priv->ds->num_ports, + XRS_PORT_OFFSET), + .rmf = &priv->ps_cur_speed + } + }; + int i = 0; + + for (; i < ARRAY_SIZE(regfields); i++) { + *regfields[i].rmf = devm_regmap_field_alloc(priv->dev, + priv->regmap, + regfields[i].rf); + if (IS_ERR(*regfields[i].rmf)) + return PTR_ERR(*regfields[i].rmf); + } + + return 0; +} + +static enum dsa_tag_protocol xrs700x_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol m) +{ + return DSA_TAG_PROTO_XRS700X; +} + +static int xrs700x_reset(struct dsa_switch *ds) +{ + struct xrs700x *priv = ds->priv; + unsigned int val; + int ret; + + ret = regmap_write(priv->regmap, XRS_GENERAL, XRS_GENERAL_RESET); + if (ret) + goto error; + + ret = regmap_read_poll_timeout(priv->regmap, XRS_GENERAL, + val, !(val & XRS_GENERAL_RESET), + 10, 1000); +error: + if (ret) { + dev_err_ratelimited(priv->dev, "error resetting switch: %d\n", + ret); + } + + return ret; +} + +static void xrs700x_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) +{ + struct xrs700x *priv = ds->priv; + unsigned int bpdus = 1; + unsigned int val; + + switch (state) { + case BR_STATE_DISABLED: + bpdus = 0; + fallthrough; + case BR_STATE_BLOCKING: + case BR_STATE_LISTENING: + val = XRS_PORT_DISABLED; + break; + case BR_STATE_LEARNING: + val = XRS_PORT_LEARNING; + break; + case BR_STATE_FORWARDING: + val = XRS_PORT_FORWARDING; + break; + default: + dev_err(ds->dev, "invalid STP state: %d\n", state); + return; + } + + regmap_fields_write(priv->ps_forward, port, val); + + /* Enable/disable inbound policy added by xrs700x_port_add_bpdu_ipf() + * which allows BPDU forwarding to the CPU port when the front facing + * port is in disabled/learning state. + */ + regmap_update_bits(priv->regmap, XRS_ETH_ADDR_CFG(port, 0), 1, bpdus); + + dev_dbg_ratelimited(priv->dev, "%s - port: %d, state: %u, val: 0x%x\n", + __func__, port, state, val); +} + +/* Add an inbound policy filter which matches the BPDU destination MAC + * and forwards to the CPU port. Leave the policy disabled, it will be + * enabled as needed. + */ +static int xrs700x_port_add_bpdu_ipf(struct dsa_switch *ds, int port) +{ + struct xrs700x *priv = ds->priv; + unsigned int val = 0; + int i = 0; + int ret; + + /* Compare all 48 bits of the destination MAC address. */ + ret = regmap_write(priv->regmap, XRS_ETH_ADDR_CFG(port, 0), 48 << 2); + if (ret) + return ret; + + /* match BPDU destination 01:80:c2:00:00:00 */ + for (i = 0; i < sizeof(eth_stp_addr); i += 2) { + ret = regmap_write(priv->regmap, XRS_ETH_ADDR_0(port, 0) + i, + eth_stp_addr[i] | + (eth_stp_addr[i + 1] << 8)); + if (ret) + return ret; + } + + /* Mirror BPDU to CPU port */ + for (i = 0; i < ds->num_ports; i++) { + if (dsa_is_cpu_port(ds, i)) + val |= BIT(i); + } + + ret = regmap_write(priv->regmap, XRS_ETH_ADDR_FWD_MIRROR(port, 0), val); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, XRS_ETH_ADDR_FWD_ALLOW(port, 0), 0); + if (ret) + return ret; + + return 0; +} + +static int xrs700x_port_setup(struct dsa_switch *ds, int port) +{ + bool cpu_port = dsa_is_cpu_port(ds, port); + struct xrs700x *priv = ds->priv; + unsigned int val = 0; + int ret, i; + + xrs700x_port_stp_state_set(ds, port, BR_STATE_DISABLED); + + /* Disable forwarding to non-CPU ports */ + for (i = 0; i < ds->num_ports; i++) { + if (!dsa_is_cpu_port(ds, i)) + val |= BIT(i); + } + + /* 1 = Disable forwarding to the port */ + ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), val); + if (ret) + return ret; + + val = cpu_port ? XRS_PORT_MODE_MANAGEMENT : XRS_PORT_MODE_NORMAL; + ret = regmap_fields_write(priv->ps_management, port, val); + if (ret) + return ret; + + if (!cpu_port) { + ret = xrs700x_port_add_bpdu_ipf(ds, port); + if (ret) + return ret; + } + + return 0; +} + +static int xrs700x_setup(struct dsa_switch *ds) +{ + struct xrs700x *priv = ds->priv; + int ret, i; + + ret = xrs700x_reset(ds); + if (ret) + return ret; + + for (i = 0; i < ds->num_ports; i++) { + ret = xrs700x_port_setup(ds, i); + if (ret) + return ret; + } + + schedule_delayed_work(&priv->mib_work, XRS700X_MIB_INTERVAL); + + return 0; +} + +static void xrs700x_teardown(struct dsa_switch *ds) +{ + struct xrs700x *priv = ds->priv; + + cancel_delayed_work_sync(&priv->mib_work); +} + +static void xrs700x_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: + break; + case 1: + case 2: + case 3: + phylink_set(mask, 1000baseT_Full); + break; + default: + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, "Unsupported port: %i\n", port); + return; + } + + phylink_set_port_modes(mask); + + /* The switch only supports full duplex. */ + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static void xrs700x_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 xrs700x *priv = ds->priv; + unsigned int val; + + switch (speed) { + case SPEED_1000: + val = XRS_PORT_SPEED_1000; + break; + case SPEED_100: + val = XRS_PORT_SPEED_100; + break; + case SPEED_10: + val = XRS_PORT_SPEED_10; + break; + default: + return; + } + + regmap_fields_write(priv->ps_sel_speed, port, val); + + dev_dbg_ratelimited(priv->dev, "%s: port: %d mode: %u speed: %u\n", + __func__, port, mode, speed); +} + +static int xrs700x_bridge_common(struct dsa_switch *ds, int port, + struct net_device *bridge, bool join) +{ + unsigned int i, cpu_mask = 0, mask = 0; + struct xrs700x *priv = ds->priv; + int ret; + + for (i = 0; i < ds->num_ports; i++) { + if (dsa_is_cpu_port(ds, i)) + continue; + + cpu_mask |= BIT(i); + + if (dsa_to_port(ds, i)->bridge_dev == bridge) + continue; + + mask |= BIT(i); + } + + for (i = 0; i < ds->num_ports; i++) { + if (dsa_to_port(ds, i)->bridge_dev != bridge) + continue; + + /* 1 = Disable forwarding to the port */ + ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(i), mask); + if (ret) + return ret; + } + + if (!join) { + ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), + cpu_mask); + if (ret) + return ret; + } + + return 0; +} + +static int xrs700x_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + return xrs700x_bridge_common(ds, port, bridge, true); +} + +static void xrs700x_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + xrs700x_bridge_common(ds, port, bridge, false); +} + +static const struct dsa_switch_ops xrs700x_ops = { + .get_tag_protocol = xrs700x_get_tag_protocol, + .setup = xrs700x_setup, + .teardown = xrs700x_teardown, + .port_stp_state_set = xrs700x_port_stp_state_set, + .phylink_validate = xrs700x_phylink_validate, + .phylink_mac_link_up = xrs700x_mac_link_up, + .get_strings = xrs700x_get_strings, + .get_sset_count = xrs700x_get_sset_count, + .get_ethtool_stats = xrs700x_get_ethtool_stats, + .get_stats64 = xrs700x_get_stats64, + .port_bridge_join = xrs700x_bridge_join, + .port_bridge_leave = xrs700x_bridge_leave, +}; + +static int xrs700x_detect(struct xrs700x *priv) +{ + const struct xrs700x_info *info; + unsigned int id; + int ret; + + ret = regmap_read(priv->regmap, XRS_DEV_ID0, &id); + if (ret) { + dev_err(priv->dev, "error %d while reading switch id.\n", + ret); + return ret; + } + + info = of_device_get_match_data(priv->dev); + if (!info) + return -EINVAL; + + if (info->id == id) { + priv->ds->num_ports = info->num_ports; + dev_info(priv->dev, "%s detected.\n", info->name); + return 0; + } + + dev_err(priv->dev, "expected switch id 0x%x but found 0x%x.\n", + info->id, id); + + return -ENODEV; +} + +struct xrs700x *xrs700x_switch_alloc(struct device *base, void *devpriv) +{ + struct dsa_switch *ds; + struct xrs700x *priv; + + ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); + if (!ds) + return NULL; + + ds->dev = base; + + priv = devm_kzalloc(base, sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + INIT_DELAYED_WORK(&priv->mib_work, xrs700x_mib_work); + + ds->ops = &xrs700x_ops; + ds->priv = priv; + priv->dev = base; + + priv->ds = ds; + priv->priv = devpriv; + + return priv; +} +EXPORT_SYMBOL(xrs700x_switch_alloc); + +static int xrs700x_alloc_port_mib(struct xrs700x *priv, int port) +{ + struct xrs700x_port *p = &priv->ports[port]; + + p->mib_data = devm_kcalloc(priv->dev, ARRAY_SIZE(xrs700x_mibs), + sizeof(*p->mib_data), GFP_KERNEL); + if (!p->mib_data) + return -ENOMEM; + + mutex_init(&p->mib_mutex); + u64_stats_init(&p->syncp); + + return 0; +} + +int xrs700x_switch_register(struct xrs700x *priv) +{ + int ret; + int i; + + ret = xrs700x_detect(priv); + if (ret) + return ret; + + ret = xrs700x_setup_regmap_range(priv); + if (ret) + return ret; + + priv->ports = devm_kcalloc(priv->dev, priv->ds->num_ports, + sizeof(*priv->ports), GFP_KERNEL); + if (!priv->ports) + return -ENOMEM; + + for (i = 0; i < priv->ds->num_ports; i++) { + ret = xrs700x_alloc_port_mib(priv, i); + if (ret) + return ret; + } + + return dsa_register_switch(priv->ds); +} +EXPORT_SYMBOL(xrs700x_switch_register); + +void xrs700x_switch_remove(struct xrs700x *priv) +{ + dsa_unregister_switch(priv->ds); +} +EXPORT_SYMBOL(xrs700x_switch_remove); + +MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>"); +MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/xrs700x/xrs700x.h b/drivers/net/dsa/xrs700x/xrs700x.h new file mode 100644 index 000000000000..ff62cf61b091 --- /dev/null +++ b/drivers/net/dsa/xrs700x/xrs700x.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/workqueue.h> +#include <linux/u64_stats_sync.h> +#include <uapi/linux/if_link.h> + +struct xrs700x_info { + unsigned int id; + const char *name; + size_t num_ports; +}; + +extern const struct xrs700x_info xrs7003e_info; +extern const struct xrs700x_info xrs7003f_info; +extern const struct xrs700x_info xrs7004e_info; +extern const struct xrs700x_info xrs7004f_info; + +struct xrs700x_port { + struct mutex mib_mutex; /* protects mib_data */ + u64 *mib_data; + struct rtnl_link_stats64 stats64; + struct u64_stats_sync syncp; +}; + +struct xrs700x { + struct dsa_switch *ds; + struct device *dev; + void *priv; + struct regmap *regmap; + struct regmap_field *ps_forward; + struct regmap_field *ps_management; + struct regmap_field *ps_sel_speed; + struct regmap_field *ps_cur_speed; + struct delayed_work mib_work; + struct xrs700x_port *ports; +}; + +struct xrs700x *xrs700x_switch_alloc(struct device *base, void *devpriv); +int xrs700x_switch_register(struct xrs700x *priv); +void xrs700x_switch_remove(struct xrs700x *priv); diff --git a/drivers/net/dsa/xrs700x/xrs700x_i2c.c b/drivers/net/dsa/xrs700x/xrs700x_i2c.c new file mode 100644 index 000000000000..a5f8883af829 --- /dev/null +++ b/drivers/net/dsa/xrs700x/xrs700x_i2c.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 NovaTech LLC + * George McCollister <george.mccollister@gmail.com> + */ + +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include "xrs700x.h" +#include "xrs700x_reg.h" + +static int xrs700x_i2c_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + unsigned char buf[4]; + int ret; + + buf[0] = reg >> 23 & 0xff; + buf[1] = reg >> 15 & 0xff; + buf[2] = reg >> 7 & 0xff; + buf[3] = (reg & 0x7f) << 1; + + ret = i2c_master_send(i2c, buf, sizeof(buf)); + if (ret < 0) { + dev_err(dev, "xrs i2c_master_send returned %d\n", ret); + return ret; + } + + ret = i2c_master_recv(i2c, buf, 2); + if (ret < 0) { + dev_err(dev, "xrs i2c_master_recv returned %d\n", ret); + return ret; + } + + *val = buf[0] << 8 | buf[1]; + + return 0; +} + +static int xrs700x_i2c_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + unsigned char buf[6]; + int ret; + + buf[0] = reg >> 23 & 0xff; + buf[1] = reg >> 15 & 0xff; + buf[2] = reg >> 7 & 0xff; + buf[3] = (reg & 0x7f) << 1 | 1; + buf[4] = val >> 8 & 0xff; + buf[5] = val & 0xff; + + ret = i2c_master_send(i2c, buf, sizeof(buf)); + if (ret < 0) { + dev_err(dev, "xrs i2c_master_send returned %d\n", ret); + return ret; + } + + return 0; +} + +static const struct regmap_config xrs700x_i2c_regmap_config = { + .val_bits = 16, + .reg_stride = 2, + .reg_bits = 32, + .pad_bits = 0, + .write_flag_mask = 0, + .read_flag_mask = 0, + .reg_read = xrs700x_i2c_reg_read, + .reg_write = xrs700x_i2c_reg_write, + .max_register = 0, + .cache_type = REGCACHE_NONE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG +}; + +static int xrs700x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct xrs700x *priv; + int ret; + + priv = xrs700x_switch_alloc(&i2c->dev, i2c); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init(&i2c->dev, NULL, &i2c->dev, + &xrs700x_i2c_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&i2c->dev, "Failed to initialize regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, priv); + + ret = xrs700x_switch_register(priv); + + /* Main DSA driver may not be started yet. */ + if (ret) + return ret; + + return 0; +} + +static int xrs700x_i2c_remove(struct i2c_client *i2c) +{ + struct xrs700x *priv = i2c_get_clientdata(i2c); + + xrs700x_switch_remove(priv); + + return 0; +} + +static const struct i2c_device_id xrs700x_i2c_id[] = { + { "xrs700x-switch", 0 }, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, xrs700x_i2c_id); + +static const struct of_device_id xrs700x_i2c_dt_ids[] = { + { .compatible = "arrow,xrs7003e", .data = &xrs7003e_info }, + { .compatible = "arrow,xrs7003f", .data = &xrs7003f_info }, + { .compatible = "arrow,xrs7004e", .data = &xrs7004e_info }, + { .compatible = "arrow,xrs7004f", .data = &xrs7004f_info }, + {}, +}; +MODULE_DEVICE_TABLE(of, xrs700x_i2c_dt_ids); + +static struct i2c_driver xrs700x_i2c_driver = { + .driver = { + .name = "xrs700x-i2c", + .of_match_table = of_match_ptr(xrs700x_i2c_dt_ids), + }, + .probe = xrs700x_i2c_probe, + .remove = xrs700x_i2c_remove, + .id_table = xrs700x_i2c_id, +}; + +module_i2c_driver(xrs700x_i2c_driver); + +MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>"); +MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/xrs700x/xrs700x_mdio.c b/drivers/net/dsa/xrs700x/xrs700x_mdio.c new file mode 100644 index 000000000000..a10ee28eb86e --- /dev/null +++ b/drivers/net/dsa/xrs700x/xrs700x_mdio.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 NovaTech LLC + * George McCollister <george.mccollister@gmail.com> + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/if_vlan.h> +#include "xrs700x.h" +#include "xrs700x_reg.h" + +#define XRS_MDIO_IBA0 0x10 +#define XRS_MDIO_IBA1 0x11 +#define XRS_MDIO_IBD 0x14 + +#define XRS_IB_READ 0x0 +#define XRS_IB_WRITE 0x1 + +static int xrs700x_mdio_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct mdio_device *mdiodev = context; + struct device *dev = &mdiodev->dev; + u16 uval; + int ret; + + uval = (u16)FIELD_GET(GENMASK(31, 16), reg); + + ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA1, uval); + if (ret < 0) { + dev_err(dev, "xrs mdiobus_write returned %d\n", ret); + return ret; + } + + uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_READ); + + ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA0, uval); + if (ret < 0) { + dev_err(dev, "xrs mdiobus_write returned %d\n", ret); + return ret; + } + + ret = mdiobus_read(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBD); + if (ret < 0) { + dev_err(dev, "xrs mdiobus_read returned %d\n", ret); + return ret; + } + + *val = (unsigned int)ret; + + return 0; +} + +static int xrs700x_mdio_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct mdio_device *mdiodev = context; + struct device *dev = &mdiodev->dev; + u16 uval; + int ret; + + ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBD, (u16)val); + if (ret < 0) { + dev_err(dev, "xrs mdiobus_write returned %d\n", ret); + return ret; + } + + uval = (u16)FIELD_GET(GENMASK(31, 16), reg); + + ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA1, uval); + if (ret < 0) { + dev_err(dev, "xrs mdiobus_write returned %d\n", ret); + return ret; + } + + uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_WRITE); + + ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA0, uval); + if (ret < 0) { + dev_err(dev, "xrs mdiobus_write returned %d\n", ret); + return ret; + } + + return 0; +} + +static const struct regmap_config xrs700x_mdio_regmap_config = { + .val_bits = 16, + .reg_stride = 2, + .reg_bits = 32, + .pad_bits = 0, + .write_flag_mask = 0, + .read_flag_mask = 0, + .reg_read = xrs700x_mdio_reg_read, + .reg_write = xrs700x_mdio_reg_write, + .max_register = XRS_VLAN(VLAN_N_VID - 1), + .cache_type = REGCACHE_NONE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG +}; + +static int xrs700x_mdio_probe(struct mdio_device *mdiodev) +{ + struct xrs700x *priv; + int ret; + + priv = xrs700x_switch_alloc(&mdiodev->dev, mdiodev); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev, + &xrs700x_mdio_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&mdiodev->dev, "Failed to initialize regmap: %d\n", ret); + return ret; + } + + dev_set_drvdata(&mdiodev->dev, priv); + + ret = xrs700x_switch_register(priv); + + /* Main DSA driver may not be started yet. */ + if (ret) + return ret; + + return 0; +} + +static void xrs700x_mdio_remove(struct mdio_device *mdiodev) +{ + struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev); + + xrs700x_switch_remove(priv); +} + +static const struct of_device_id xrs700x_mdio_dt_ids[] = { + { .compatible = "arrow,xrs7003e", .data = &xrs7003e_info }, + { .compatible = "arrow,xrs7003f", .data = &xrs7003f_info }, + { .compatible = "arrow,xrs7004e", .data = &xrs7004e_info }, + { .compatible = "arrow,xrs7004f", .data = &xrs7004f_info }, + {}, +}; +MODULE_DEVICE_TABLE(of, xrs700x_mdio_dt_ids); + +static struct mdio_driver xrs700x_mdio_driver = { + .mdiodrv.driver = { + .name = "xrs700x-mdio", + .of_match_table = xrs700x_mdio_dt_ids, + }, + .probe = xrs700x_mdio_probe, + .remove = xrs700x_mdio_remove, +}; + +mdio_module_driver(xrs700x_mdio_driver); + +MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>"); +MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA MDIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/xrs700x/xrs700x_reg.h b/drivers/net/dsa/xrs700x/xrs700x_reg.h new file mode 100644 index 000000000000..a135d4d92b6d --- /dev/null +++ b/drivers/net/dsa/xrs700x/xrs700x_reg.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Register Base Addresses */ +#define XRS_DEVICE_ID_BASE 0x0 +#define XRS_GPIO_BASE 0x10000 +#define XRS_PORT_OFFSET 0x10000 +#define XRS_PORT_BASE(x) (0x200000 + XRS_PORT_OFFSET * (x)) +#define XRS_RTC_BASE 0x280000 +#define XRS_TS_OFFSET 0x8000 +#define XRS_TS_BASE(x) (0x290000 + XRS_TS_OFFSET * (x)) +#define XRS_SWITCH_CONF_BASE 0x300000 + +/* Device Identification Registers */ +#define XRS_DEV_ID0 (XRS_DEVICE_ID_BASE + 0) +#define XRS_DEV_ID1 (XRS_DEVICE_ID_BASE + 2) +#define XRS_INT_ID0 (XRS_DEVICE_ID_BASE + 4) +#define XRS_INT_ID1 (XRS_DEVICE_ID_BASE + 6) +#define XRS_REV_ID (XRS_DEVICE_ID_BASE + 8) + +/* GPIO Registers */ +#define XRS_CONFIG0 (XRS_GPIO_BASE + 0x1000) +#define XRS_INPUT_STATUS0 (XRS_GPIO_BASE + 0x1002) +#define XRS_CONFIG1 (XRS_GPIO_BASE + 0x1004) +#define XRS_INPUT_STATUS1 (XRS_GPIO_BASE + 0x1006) +#define XRS_CONFIG2 (XRS_GPIO_BASE + 0x1008) +#define XRS_INPUT_STATUS2 (XRS_GPIO_BASE + 0x100a) + +/* Port Configuration Registers */ +#define XRS_PORT_GEN_BASE(x) (XRS_PORT_BASE(x) + 0x0) +#define XRS_PORT_HSR_BASE(x) (XRS_PORT_BASE(x) + 0x2000) +#define XRS_PORT_PTP_BASE(x) (XRS_PORT_BASE(x) + 0x4000) +#define XRS_PORT_CNT_BASE(x) (XRS_PORT_BASE(x) + 0x6000) +#define XRS_PORT_IPO_BASE(x) (XRS_PORT_BASE(x) + 0x8000) + +/* Port Configuration Registers - General and State */ +#define XRS_PORT_STATE(x) (XRS_PORT_GEN_BASE(x) + 0x0) +#define XRS_PORT_FORWARDING 0 +#define XRS_PORT_LEARNING 1 +#define XRS_PORT_DISABLED 2 +#define XRS_PORT_MODE_NORMAL 0 +#define XRS_PORT_MODE_MANAGEMENT 1 +#define XRS_PORT_SPEED_1000 0x12 +#define XRS_PORT_SPEED_100 0x20 +#define XRS_PORT_SPEED_10 0x30 +#define XRS_PORT_VLAN(x) (XRS_PORT_GEN_BASE(x) + 0x10) +#define XRS_PORT_VLAN0_MAPPING(x) (XRS_PORT_GEN_BASE(x) + 0x12) +#define XRS_PORT_FWD_MASK(x) (XRS_PORT_GEN_BASE(x) + 0x14) +#define XRS_PORT_VLAN_PRIO(x) (XRS_PORT_GEN_BASE(x) + 0x16) + +/* Port Configuration Registers - HSR/PRP */ +#define XRS_HSR_CFG(x) (XRS_PORT_HSR_BASE(x) + 0x0) + +/* Port Configuration Registers - PTP */ +#define XRS_PTP_RX_SYNC_DELAY_NS_LO(x) (XRS_PORT_PTP_BASE(x) + 0x2) +#define XRS_PTP_RX_SYNC_DELAY_NS_HI(x) (XRS_PORT_PTP_BASE(x) + 0x4) +#define XRS_PTP_RX_EVENT_DELAY_NS(x) (XRS_PORT_PTP_BASE(x) + 0xa) +#define XRS_PTP_TX_EVENT_DELAY_NS(x) (XRS_PORT_PTP_BASE(x) + 0x12) + +/* Port Configuration Registers - Counter */ +#define XRS_CNT_CTRL(x) (XRS_PORT_CNT_BASE(x) + 0x0) +#define XRS_RX_GOOD_OCTETS_L (XRS_PORT_CNT_BASE(0) + 0x200) +#define XRS_RX_GOOD_OCTETS_H (XRS_PORT_CNT_BASE(0) + 0x202) +#define XRS_RX_BAD_OCTETS_L (XRS_PORT_CNT_BASE(0) + 0x204) +#define XRS_RX_BAD_OCTETS_H (XRS_PORT_CNT_BASE(0) + 0x206) +#define XRS_RX_UNICAST_L (XRS_PORT_CNT_BASE(0) + 0x208) +#define XRS_RX_UNICAST_H (XRS_PORT_CNT_BASE(0) + 0x20a) +#define XRS_RX_BROADCAST_L (XRS_PORT_CNT_BASE(0) + 0x20c) +#define XRS_RX_BROADCAST_H (XRS_PORT_CNT_BASE(0) + 0x20e) +#define XRS_RX_MULTICAST_L (XRS_PORT_CNT_BASE(0) + 0x210) +#define XRS_RX_MULTICAST_H (XRS_PORT_CNT_BASE(0) + 0x212) +#define XRS_RX_UNDERSIZE_L (XRS_PORT_CNT_BASE(0) + 0x214) +#define XRS_RX_UNDERSIZE_H (XRS_PORT_CNT_BASE(0) + 0x216) +#define XRS_RX_FRAGMENTS_L (XRS_PORT_CNT_BASE(0) + 0x218) +#define XRS_RX_FRAGMENTS_H (XRS_PORT_CNT_BASE(0) + 0x21a) +#define XRS_RX_OVERSIZE_L (XRS_PORT_CNT_BASE(0) + 0x21c) +#define XRS_RX_OVERSIZE_H (XRS_PORT_CNT_BASE(0) + 0x21e) +#define XRS_RX_JABBER_L (XRS_PORT_CNT_BASE(0) + 0x220) +#define XRS_RX_JABBER_H (XRS_PORT_CNT_BASE(0) + 0x222) +#define XRS_RX_ERR_L (XRS_PORT_CNT_BASE(0) + 0x224) +#define XRS_RX_ERR_H (XRS_PORT_CNT_BASE(0) + 0x226) +#define XRS_RX_CRC_L (XRS_PORT_CNT_BASE(0) + 0x228) +#define XRS_RX_CRC_H (XRS_PORT_CNT_BASE(0) + 0x22a) +#define XRS_RX_64_L (XRS_PORT_CNT_BASE(0) + 0x22c) +#define XRS_RX_64_H (XRS_PORT_CNT_BASE(0) + 0x22e) +#define XRS_RX_65_127_L (XRS_PORT_CNT_BASE(0) + 0x230) +#define XRS_RX_65_127_H (XRS_PORT_CNT_BASE(0) + 0x232) +#define XRS_RX_128_255_L (XRS_PORT_CNT_BASE(0) + 0x234) +#define XRS_RX_128_255_H (XRS_PORT_CNT_BASE(0) + 0x236) +#define XRS_RX_256_511_L (XRS_PORT_CNT_BASE(0) + 0x238) +#define XRS_RX_256_511_H (XRS_PORT_CNT_BASE(0) + 0x23a) +#define XRS_RX_512_1023_L (XRS_PORT_CNT_BASE(0) + 0x23c) +#define XRS_RX_512_1023_H (XRS_PORT_CNT_BASE(0) + 0x23e) +#define XRS_RX_1024_1536_L (XRS_PORT_CNT_BASE(0) + 0x240) +#define XRS_RX_1024_1536_H (XRS_PORT_CNT_BASE(0) + 0x242) +#define XRS_RX_HSR_PRP_L (XRS_PORT_CNT_BASE(0) + 0x244) +#define XRS_RX_HSR_PRP_H (XRS_PORT_CNT_BASE(0) + 0x246) +#define XRS_RX_WRONGLAN_L (XRS_PORT_CNT_BASE(0) + 0x248) +#define XRS_RX_WRONGLAN_H (XRS_PORT_CNT_BASE(0) + 0x24a) +#define XRS_RX_DUPLICATE_L (XRS_PORT_CNT_BASE(0) + 0x24c) +#define XRS_RX_DUPLICATE_H (XRS_PORT_CNT_BASE(0) + 0x24e) +#define XRS_TX_OCTETS_L (XRS_PORT_CNT_BASE(0) + 0x280) +#define XRS_TX_OCTETS_H (XRS_PORT_CNT_BASE(0) + 0x282) +#define XRS_TX_UNICAST_L (XRS_PORT_CNT_BASE(0) + 0x284) +#define XRS_TX_UNICAST_H (XRS_PORT_CNT_BASE(0) + 0x286) +#define XRS_TX_BROADCAST_L (XRS_PORT_CNT_BASE(0) + 0x288) +#define XRS_TX_BROADCAST_H (XRS_PORT_CNT_BASE(0) + 0x28a) +#define XRS_TX_MULTICAST_L (XRS_PORT_CNT_BASE(0) + 0x28c) +#define XRS_TX_MULTICAST_H (XRS_PORT_CNT_BASE(0) + 0x28e) +#define XRS_TX_HSR_PRP_L (XRS_PORT_CNT_BASE(0) + 0x290) +#define XRS_TX_HSR_PRP_H (XRS_PORT_CNT_BASE(0) + 0x292) +#define XRS_PRIQ_DROP_L (XRS_PORT_CNT_BASE(0) + 0x2c0) +#define XRS_PRIQ_DROP_H (XRS_PORT_CNT_BASE(0) + 0x2c2) +#define XRS_EARLY_DROP_L (XRS_PORT_CNT_BASE(0) + 0x2c4) +#define XRS_EARLY_DROP_H (XRS_PORT_CNT_BASE(0) + 0x2c6) + +/* Port Configuration Registers - Inbound Policy 0 - 15 */ +#define XRS_ETH_ADDR_CFG(x, p) (XRS_PORT_IPO_BASE(x) + \ + (p) * 0x20 + 0x0) +#define XRS_ETH_ADDR_FWD_ALLOW(x, p) (XRS_PORT_IPO_BASE(x) + \ + (p) * 0x20 + 0x2) +#define XRS_ETH_ADDR_FWD_MIRROR(x, p) (XRS_PORT_IPO_BASE(x) + \ + (p) * 0x20 + 0x4) +#define XRS_ETH_ADDR_0(x, p) (XRS_PORT_IPO_BASE(x) + \ + (p) * 0x20 + 0x8) +#define XRS_ETH_ADDR_1(x, p) (XRS_PORT_IPO_BASE(x) + \ + (p) * 0x20 + 0xa) +#define XRS_ETH_ADDR_2(x, p) (XRS_PORT_IPO_BASE(x) + \ + (p) * 0x20 + 0xc) + +/* RTC Registers */ +#define XRS_CUR_NSEC0 (XRS_RTC_BASE + 0x1004) +#define XRS_CUR_NSEC1 (XRS_RTC_BASE + 0x1006) +#define XRS_CUR_SEC0 (XRS_RTC_BASE + 0x1008) +#define XRS_CUR_SEC1 (XRS_RTC_BASE + 0x100a) +#define XRS_CUR_SEC2 (XRS_RTC_BASE + 0x100c) +#define XRS_TIME_CC0 (XRS_RTC_BASE + 0x1010) +#define XRS_TIME_CC1 (XRS_RTC_BASE + 0x1012) +#define XRS_TIME_CC2 (XRS_RTC_BASE + 0x1014) +#define XRS_STEP_SIZE0 (XRS_RTC_BASE + 0x1020) +#define XRS_STEP_SIZE1 (XRS_RTC_BASE + 0x1022) +#define XRS_STEP_SIZE2 (XRS_RTC_BASE + 0x1024) +#define XRS_ADJUST_NSEC0 (XRS_RTC_BASE + 0x1034) +#define XRS_ADJUST_NSEC1 (XRS_RTC_BASE + 0x1036) +#define XRS_ADJUST_SEC0 (XRS_RTC_BASE + 0x1038) +#define XRS_ADJUST_SEC1 (XRS_RTC_BASE + 0x103a) +#define XRS_ADJUST_SEC2 (XRS_RTC_BASE + 0x103c) +#define XRS_TIME_CMD (XRS_RTC_BASE + 0x1040) + +/* Time Stamper Registers */ +#define XRS_TS_CTRL(x) (XRS_TS_BASE(x) + 0x1000) +#define XRS_TS_INT_MASK(x) (XRS_TS_BASE(x) + 0x1008) +#define XRS_TS_INT_STATUS(x) (XRS_TS_BASE(x) + 0x1010) +#define XRS_TS_NSEC0(x) (XRS_TS_BASE(x) + 0x1104) +#define XRS_TS_NSEC1(x) (XRS_TS_BASE(x) + 0x1106) +#define XRS_TS_SEC0(x) (XRS_TS_BASE(x) + 0x1108) +#define XRS_TS_SEC1(x) (XRS_TS_BASE(x) + 0x110a) +#define XRS_TS_SEC2(x) (XRS_TS_BASE(x) + 0x110c) +#define XRS_PNCT0(x) (XRS_TS_BASE(x) + 0x1110) +#define XRS_PNCT1(x) (XRS_TS_BASE(x) + 0x1112) + +/* Switch Configuration Registers */ +#define XRS_SWITCH_GEN_BASE (XRS_SWITCH_CONF_BASE + 0x0) +#define XRS_SWITCH_TS_BASE (XRS_SWITCH_CONF_BASE + 0x2000) +#define XRS_SWITCH_VLAN_BASE (XRS_SWITCH_CONF_BASE + 0x4000) + +/* Switch Configuration Registers - General */ +#define XRS_GENERAL (XRS_SWITCH_GEN_BASE + 0x10) +#define XRS_GENERAL_TIME_TRAILER BIT(9) +#define XRS_GENERAL_MOD_SYNC BIT(10) +#define XRS_GENERAL_CUT_THRU BIT(13) +#define XRS_GENERAL_CLR_MAC_TBL BIT(14) +#define XRS_GENERAL_RESET BIT(15) +#define XRS_MT_CLEAR_MASK (XRS_SWITCH_GEN_BASE + 0x12) +#define XRS_ADDRESS_AGING (XRS_SWITCH_GEN_BASE + 0x20) +#define XRS_TS_CTRL_TX (XRS_SWITCH_GEN_BASE + 0x28) +#define XRS_TS_CTRL_RX (XRS_SWITCH_GEN_BASE + 0x2a) +#define XRS_INT_MASK (XRS_SWITCH_GEN_BASE + 0x2c) +#define XRS_INT_STATUS (XRS_SWITCH_GEN_BASE + 0x2e) +#define XRS_MAC_TABLE0 (XRS_SWITCH_GEN_BASE + 0x200) +#define XRS_MAC_TABLE1 (XRS_SWITCH_GEN_BASE + 0x202) +#define XRS_MAC_TABLE2 (XRS_SWITCH_GEN_BASE + 0x204) +#define XRS_MAC_TABLE3 (XRS_SWITCH_GEN_BASE + 0x206) + +/* Switch Configuration Registers - Frame Timestamp */ +#define XRS_TX_TS_NS_LO(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x0) +#define XRS_TX_TS_NS_HI(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x2) +#define XRS_TX_TS_S_LO(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x4) +#define XRS_TX_TS_S_HI(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x6) +#define XRS_TX_TS_HDR(t, h) (XRS_SWITCH_TS_BASE + 0x80 * (t) + \ + 0x2 * (h) + 0xe) +#define XRS_RX_TS_NS_LO(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + \ + 0x200) +#define XRS_RX_TS_NS_HI(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + \ + 0x202) +#define XRS_RX_TS_S_LO(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + \ + 0x204) +#define XRS_RX_TS_S_HI(t) (XRS_SWITCH_TS_BASE + 0x80 * (t) + \ + 0x206) +#define XRS_RX_TS_HDR(t, h) (XRS_SWITCH_TS_BASE + 0x80 * (t) + \ + 0x2 * (h) + 0xe) + +/* Switch Configuration Registers - VLAN */ +#define XRS_VLAN(v) (XRS_SWITCH_VLAN_BASE + 0x2 * (v)) diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index de50e8b9e656..ad04660b97b8 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -33,7 +33,6 @@ source "drivers/net/ethernet/apple/Kconfig" source "drivers/net/ethernet/aquantia/Kconfig" source "drivers/net/ethernet/arc/Kconfig" source "drivers/net/ethernet/atheros/Kconfig" -source "drivers/net/ethernet/aurora/Kconfig" source "drivers/net/ethernet/broadcom/Kconfig" source "drivers/net/ethernet/brocade/Kconfig" source "drivers/net/ethernet/cadence/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index f8f38dcb5f8a..1e7dc8a7762d 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_NET_VENDOR_APPLE) += apple/ obj-$(CONFIG_NET_VENDOR_AQUANTIA) += aquantia/ obj-$(CONFIG_NET_VENDOR_ARC) += arc/ obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/ -obj-$(CONFIG_NET_VENDOR_AURORA) += aurora/ obj-$(CONFIG_NET_VENDOR_CADENCE) += cadence/ obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_NET_VENDOR_BROCADE) += brocade/ diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 06596fa1f9fe..1db6cfd2b55c 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -1585,10 +1585,9 @@ static int ena_xdp_handle_buff(struct ena_ring *rx_ring, struct xdp_buff *xdp) int ret; rx_info = &rx_ring->rx_buffer_info[rx_ring->ena_bufs[0].req_id]; - xdp->data = page_address(rx_info->page) + rx_info->page_offset; - xdp_set_data_meta_invalid(xdp); - xdp->data_hard_start = page_address(rx_info->page); - xdp->data_end = xdp->data + rx_ring->ena_bufs[0].len; + xdp_prepare_buff(xdp, page_address(rx_info->page), + rx_info->page_offset, + rx_ring->ena_bufs[0].len, false); /* If for some reason we received a bigger packet than * we expect, then we simply drop it */ @@ -1634,8 +1633,7 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi, netif_dbg(rx_ring->adapter, rx_status, rx_ring->netdev, "%s qid %d\n", __func__, rx_ring->qid); res_budget = budget; - xdp.rxq = &rx_ring->xdp_rxq; - xdp.frame_sz = ENA_PAGE_SIZE; + xdp_init_buff(&xdp, ENA_PAGE_SIZE, &rx_ring->xdp_rxq); do { xdp_verdict = XDP_PASS; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 2709a2db5657..99b6d5a9f1d9 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -2295,8 +2295,6 @@ static const struct net_device_ops xgbe_netdev_ops = { .ndo_setup_tc = xgbe_setup_tc, .ndo_fix_features = xgbe_fix_features, .ndo_set_features = xgbe_set_features, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = xgbe_features_check, }; diff --git a/drivers/net/ethernet/aurora/Kconfig b/drivers/net/ethernet/aurora/Kconfig deleted file mode 100644 index 9ee30ea90bfa..000000000000 --- a/drivers/net/ethernet/aurora/Kconfig +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config NET_VENDOR_AURORA - bool "Aurora VLSI devices" - default y - help - If you have a network (Ethernet) device belonging to this class, - say Y. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - questions about Aurora devices. If you say Y, you will be asked - for your specific device in the following questions. - -if NET_VENDOR_AURORA - -config AURORA_NB8800 - tristate "Aurora AU-NB8800 support" - depends on HAS_DMA - select PHYLIB - help - Support for the AU-NB8800 gigabit Ethernet controller. - -endif diff --git a/drivers/net/ethernet/aurora/Makefile b/drivers/net/ethernet/aurora/Makefile deleted file mode 100644 index f3d599867619..000000000000 --- a/drivers/net/ethernet/aurora/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_AURORA_NB8800) += nb8800.o diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c deleted file mode 100644 index 5b20185cbd62..000000000000 --- a/drivers/net/ethernet/aurora/nb8800.c +++ /dev/null @@ -1,1520 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2015 Mans Rullgard <mans@mansr.com> - * - * Mostly rewritten, based on driver from Sigma Designs. Original - * copyright notice below. - * - * Driver for tangox SMP864x/SMP865x/SMP867x/SMP868x builtin Ethernet Mac. - * - * Copyright (C) 2005 Maxime Bizon <mbizon@freebox.fr> - */ - -#include <linux/module.h> -#include <linux/etherdevice.h> -#include <linux/delay.h> -#include <linux/ethtool.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/of_device.h> -#include <linux/of_mdio.h> -#include <linux/of_net.h> -#include <linux/dma-mapping.h> -#include <linux/phy.h> -#include <linux/cache.h> -#include <linux/jiffies.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <asm/barrier.h> - -#include "nb8800.h" - -static void nb8800_tx_done(struct net_device *dev); -static int nb8800_dma_stop(struct net_device *dev); - -static inline u8 nb8800_readb(struct nb8800_priv *priv, int reg) -{ - return readb_relaxed(priv->base + reg); -} - -static inline u32 nb8800_readl(struct nb8800_priv *priv, int reg) -{ - return readl_relaxed(priv->base + reg); -} - -static inline void nb8800_writeb(struct nb8800_priv *priv, int reg, u8 val) -{ - writeb_relaxed(val, priv->base + reg); -} - -static inline void nb8800_writew(struct nb8800_priv *priv, int reg, u16 val) -{ - writew_relaxed(val, priv->base + reg); -} - -static inline void nb8800_writel(struct nb8800_priv *priv, int reg, u32 val) -{ - writel_relaxed(val, priv->base + reg); -} - -static inline void nb8800_maskb(struct nb8800_priv *priv, int reg, - u32 mask, u32 val) -{ - u32 old = nb8800_readb(priv, reg); - u32 new = (old & ~mask) | (val & mask); - - if (new != old) - nb8800_writeb(priv, reg, new); -} - -static inline void nb8800_maskl(struct nb8800_priv *priv, int reg, - u32 mask, u32 val) -{ - u32 old = nb8800_readl(priv, reg); - u32 new = (old & ~mask) | (val & mask); - - if (new != old) - nb8800_writel(priv, reg, new); -} - -static inline void nb8800_modb(struct nb8800_priv *priv, int reg, u8 bits, - bool set) -{ - nb8800_maskb(priv, reg, bits, set ? bits : 0); -} - -static inline void nb8800_setb(struct nb8800_priv *priv, int reg, u8 bits) -{ - nb8800_maskb(priv, reg, bits, bits); -} - -static inline void nb8800_clearb(struct nb8800_priv *priv, int reg, u8 bits) -{ - nb8800_maskb(priv, reg, bits, 0); -} - -static inline void nb8800_modl(struct nb8800_priv *priv, int reg, u32 bits, - bool set) -{ - nb8800_maskl(priv, reg, bits, set ? bits : 0); -} - -static inline void nb8800_setl(struct nb8800_priv *priv, int reg, u32 bits) -{ - nb8800_maskl(priv, reg, bits, bits); -} - -static inline void nb8800_clearl(struct nb8800_priv *priv, int reg, u32 bits) -{ - nb8800_maskl(priv, reg, bits, 0); -} - -static int nb8800_mdio_wait(struct mii_bus *bus) -{ - struct nb8800_priv *priv = bus->priv; - u32 val; - - return readl_poll_timeout_atomic(priv->base + NB8800_MDIO_CMD, - val, !(val & MDIO_CMD_GO), 1, 1000); -} - -static int nb8800_mdio_cmd(struct mii_bus *bus, u32 cmd) -{ - struct nb8800_priv *priv = bus->priv; - int err; - - err = nb8800_mdio_wait(bus); - if (err) - return err; - - nb8800_writel(priv, NB8800_MDIO_CMD, cmd); - udelay(10); - nb8800_writel(priv, NB8800_MDIO_CMD, cmd | MDIO_CMD_GO); - - return nb8800_mdio_wait(bus); -} - -static int nb8800_mdio_read(struct mii_bus *bus, int phy_id, int reg) -{ - struct nb8800_priv *priv = bus->priv; - u32 val; - int err; - - err = nb8800_mdio_cmd(bus, MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg)); - if (err) - return err; - - val = nb8800_readl(priv, NB8800_MDIO_STS); - if (val & MDIO_STS_ERR) - return 0xffff; - - return val & 0xffff; -} - -static int nb8800_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) -{ - u32 cmd = MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg) | - MDIO_CMD_DATA(val) | MDIO_CMD_WR; - - return nb8800_mdio_cmd(bus, cmd); -} - -static void nb8800_mac_tx(struct net_device *dev, bool enable) -{ - struct nb8800_priv *priv = netdev_priv(dev); - - while (nb8800_readl(priv, NB8800_TXC_CR) & TCR_EN) - cpu_relax(); - - nb8800_modb(priv, NB8800_TX_CTL1, TX_EN, enable); -} - -static void nb8800_mac_rx(struct net_device *dev, bool enable) -{ - nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_EN, enable); -} - -static void nb8800_mac_af(struct net_device *dev, bool enable) -{ - nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_AF_EN, enable); -} - -static void nb8800_start_rx(struct net_device *dev) -{ - nb8800_setl(netdev_priv(dev), NB8800_RXC_CR, RCR_EN); -} - -static int nb8800_alloc_rx(struct net_device *dev, unsigned int i, bool napi) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct nb8800_rx_desc *rxd = &priv->rx_descs[i]; - struct nb8800_rx_buf *rxb = &priv->rx_bufs[i]; - int size = L1_CACHE_ALIGN(RX_BUF_SIZE); - dma_addr_t dma_addr; - struct page *page; - unsigned long offset; - void *data; - - data = napi ? napi_alloc_frag(size) : netdev_alloc_frag(size); - if (!data) - return -ENOMEM; - - page = virt_to_head_page(data); - offset = data - page_address(page); - - dma_addr = dma_map_page(&dev->dev, page, offset, RX_BUF_SIZE, - DMA_FROM_DEVICE); - - if (dma_mapping_error(&dev->dev, dma_addr)) { - skb_free_frag(data); - return -ENOMEM; - } - - rxb->page = page; - rxb->offset = offset; - rxd->desc.s_addr = dma_addr; - - return 0; -} - -static void nb8800_receive(struct net_device *dev, unsigned int i, - unsigned int len) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct nb8800_rx_desc *rxd = &priv->rx_descs[i]; - struct page *page = priv->rx_bufs[i].page; - int offset = priv->rx_bufs[i].offset; - void *data = page_address(page) + offset; - dma_addr_t dma = rxd->desc.s_addr; - struct sk_buff *skb; - unsigned int size; - int err; - - size = len <= RX_COPYBREAK ? len : RX_COPYHDR; - - skb = napi_alloc_skb(&priv->napi, size); - if (!skb) { - netdev_err(dev, "rx skb allocation failed\n"); - dev->stats.rx_dropped++; - return; - } - - if (len <= RX_COPYBREAK) { - dma_sync_single_for_cpu(&dev->dev, dma, len, DMA_FROM_DEVICE); - skb_put_data(skb, data, len); - dma_sync_single_for_device(&dev->dev, dma, len, - DMA_FROM_DEVICE); - } else { - err = nb8800_alloc_rx(dev, i, true); - if (err) { - netdev_err(dev, "rx buffer allocation failed\n"); - dev->stats.rx_dropped++; - dev_kfree_skb(skb); - return; - } - - dma_unmap_page(&dev->dev, dma, RX_BUF_SIZE, DMA_FROM_DEVICE); - skb_put_data(skb, data, RX_COPYHDR); - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - offset + RX_COPYHDR, len - RX_COPYHDR, - RX_BUF_SIZE); - } - - skb->protocol = eth_type_trans(skb, dev); - napi_gro_receive(&priv->napi, skb); -} - -static void nb8800_rx_error(struct net_device *dev, u32 report) -{ - if (report & RX_LENGTH_ERR) - dev->stats.rx_length_errors++; - - if (report & RX_FCS_ERR) - dev->stats.rx_crc_errors++; - - if (report & RX_FIFO_OVERRUN) - dev->stats.rx_fifo_errors++; - - if (report & RX_ALIGNMENT_ERROR) - dev->stats.rx_frame_errors++; - - dev->stats.rx_errors++; -} - -static int nb8800_poll(struct napi_struct *napi, int budget) -{ - struct net_device *dev = napi->dev; - struct nb8800_priv *priv = netdev_priv(dev); - struct nb8800_rx_desc *rxd; - unsigned int last = priv->rx_eoc; - unsigned int next; - int work = 0; - - nb8800_tx_done(dev); - -again: - do { - unsigned int len; - - next = (last + 1) % RX_DESC_COUNT; - - rxd = &priv->rx_descs[next]; - - if (!rxd->report) - break; - - len = RX_BYTES_TRANSFERRED(rxd->report); - - if (IS_RX_ERROR(rxd->report)) - nb8800_rx_error(dev, rxd->report); - else - nb8800_receive(dev, next, len); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - if (rxd->report & RX_MULTICAST_PKT) - dev->stats.multicast++; - - rxd->report = 0; - last = next; - work++; - } while (work < budget); - - if (work) { - priv->rx_descs[last].desc.config |= DESC_EOC; - wmb(); /* ensure new EOC is written before clearing old */ - priv->rx_descs[priv->rx_eoc].desc.config &= ~DESC_EOC; - priv->rx_eoc = last; - nb8800_start_rx(dev); - } - - if (work < budget) { - nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq); - - /* If a packet arrived after we last checked but - * before writing RX_ITR, the interrupt will be - * delayed, so we retrieve it now. - */ - if (priv->rx_descs[next].report) - goto again; - - napi_complete_done(napi, work); - } - - return work; -} - -static void __nb8800_tx_dma_start(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct nb8800_tx_buf *txb; - u32 txc_cr; - - txb = &priv->tx_bufs[priv->tx_queue]; - if (!txb->ready) - return; - - txc_cr = nb8800_readl(priv, NB8800_TXC_CR); - if (txc_cr & TCR_EN) - return; - - nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc); - wmb(); /* ensure desc addr is written before starting DMA */ - nb8800_writel(priv, NB8800_TXC_CR, txc_cr | TCR_EN); - - priv->tx_queue = (priv->tx_queue + txb->chain_len) % TX_DESC_COUNT; -} - -static void nb8800_tx_dma_start(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - - spin_lock_irq(&priv->tx_lock); - __nb8800_tx_dma_start(dev); - spin_unlock_irq(&priv->tx_lock); -} - -static void nb8800_tx_dma_start_irq(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - - spin_lock(&priv->tx_lock); - __nb8800_tx_dma_start(dev); - spin_unlock(&priv->tx_lock); -} - -static netdev_tx_t nb8800_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct nb8800_tx_desc *txd; - struct nb8800_tx_buf *txb; - struct nb8800_dma_desc *desc; - dma_addr_t dma_addr; - unsigned int dma_len; - unsigned int align; - unsigned int next; - bool xmit_more; - - if (atomic_read(&priv->tx_free) <= NB8800_DESC_LOW) { - netif_stop_queue(dev); - return NETDEV_TX_BUSY; - } - - align = (8 - (uintptr_t)skb->data) & 7; - - dma_len = skb->len - align; - dma_addr = dma_map_single(&dev->dev, skb->data + align, - dma_len, DMA_TO_DEVICE); - - if (dma_mapping_error(&dev->dev, dma_addr)) { - netdev_err(dev, "tx dma mapping error\n"); - kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - - xmit_more = netdev_xmit_more(); - if (atomic_dec_return(&priv->tx_free) <= NB8800_DESC_LOW) { - netif_stop_queue(dev); - xmit_more = false; - } - - next = priv->tx_next; - txb = &priv->tx_bufs[next]; - txd = &priv->tx_descs[next]; - desc = &txd->desc[0]; - - next = (next + 1) % TX_DESC_COUNT; - - if (align) { - memcpy(txd->buf, skb->data, align); - - desc->s_addr = - txb->dma_desc + offsetof(struct nb8800_tx_desc, buf); - desc->n_addr = txb->dma_desc + sizeof(txd->desc[0]); - desc->config = DESC_BTS(2) | DESC_DS | align; - - desc++; - } - - desc->s_addr = dma_addr; - desc->n_addr = priv->tx_bufs[next].dma_desc; - desc->config = DESC_BTS(2) | DESC_DS | DESC_EOF | dma_len; - - if (!xmit_more) - desc->config |= DESC_EOC; - - txb->skb = skb; - txb->dma_addr = dma_addr; - txb->dma_len = dma_len; - - if (!priv->tx_chain) { - txb->chain_len = 1; - priv->tx_chain = txb; - } else { - priv->tx_chain->chain_len++; - } - - netdev_sent_queue(dev, skb->len); - - priv->tx_next = next; - - if (!xmit_more) { - smp_wmb(); - priv->tx_chain->ready = true; - priv->tx_chain = NULL; - nb8800_tx_dma_start(dev); - } - - return NETDEV_TX_OK; -} - -static void nb8800_tx_error(struct net_device *dev, u32 report) -{ - if (report & TX_LATE_COLLISION) - dev->stats.collisions++; - - if (report & TX_PACKET_DROPPED) - dev->stats.tx_dropped++; - - if (report & TX_FIFO_UNDERRUN) - dev->stats.tx_fifo_errors++; - - dev->stats.tx_errors++; -} - -static void nb8800_tx_done(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - unsigned int limit = priv->tx_next; - unsigned int done = priv->tx_done; - unsigned int packets = 0; - unsigned int len = 0; - - while (done != limit) { - struct nb8800_tx_desc *txd = &priv->tx_descs[done]; - struct nb8800_tx_buf *txb = &priv->tx_bufs[done]; - struct sk_buff *skb; - - if (!txd->report) - break; - - skb = txb->skb; - len += skb->len; - - dma_unmap_single(&dev->dev, txb->dma_addr, txb->dma_len, - DMA_TO_DEVICE); - - if (IS_TX_ERROR(txd->report)) { - nb8800_tx_error(dev, txd->report); - kfree_skb(skb); - } else { - consume_skb(skb); - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += TX_BYTES_TRANSFERRED(txd->report); - dev->stats.collisions += TX_EARLY_COLLISIONS(txd->report); - - txb->skb = NULL; - txb->ready = false; - txd->report = 0; - - done = (done + 1) % TX_DESC_COUNT; - packets++; - } - - if (packets) { - smp_mb__before_atomic(); - atomic_add(packets, &priv->tx_free); - netdev_completed_queue(dev, packets, len); - netif_wake_queue(dev); - priv->tx_done = done; - } -} - -static irqreturn_t nb8800_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct nb8800_priv *priv = netdev_priv(dev); - irqreturn_t ret = IRQ_NONE; - u32 val; - - /* tx interrupt */ - val = nb8800_readl(priv, NB8800_TXC_SR); - if (val) { - nb8800_writel(priv, NB8800_TXC_SR, val); - - if (val & TSR_DI) - nb8800_tx_dma_start_irq(dev); - - if (val & TSR_TI) - napi_schedule_irqoff(&priv->napi); - - if (unlikely(val & TSR_DE)) - netdev_err(dev, "TX DMA error\n"); - - /* should never happen with automatic status retrieval */ - if (unlikely(val & TSR_TO)) - netdev_err(dev, "TX Status FIFO overflow\n"); - - ret = IRQ_HANDLED; - } - - /* rx interrupt */ - val = nb8800_readl(priv, NB8800_RXC_SR); - if (val) { - nb8800_writel(priv, NB8800_RXC_SR, val); - - if (likely(val & (RSR_RI | RSR_DI))) { - nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_poll); - napi_schedule_irqoff(&priv->napi); - } - - if (unlikely(val & RSR_DE)) - netdev_err(dev, "RX DMA error\n"); - - /* should never happen with automatic status retrieval */ - if (unlikely(val & RSR_RO)) - netdev_err(dev, "RX Status FIFO overflow\n"); - - ret = IRQ_HANDLED; - } - - return ret; -} - -static void nb8800_mac_config(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - bool gigabit = priv->speed == SPEED_1000; - u32 mac_mode_mask = RGMII_MODE | HALF_DUPLEX | GMAC_MODE; - u32 mac_mode = 0; - u32 slot_time; - u32 phy_clk; - u32 ict; - - if (!priv->duplex) - mac_mode |= HALF_DUPLEX; - - if (gigabit) { - if (phy_interface_is_rgmii(dev->phydev)) - mac_mode |= RGMII_MODE; - - mac_mode |= GMAC_MODE; - phy_clk = 125000000; - - /* Should be 512 but register is only 8 bits */ - slot_time = 255; - } else { - phy_clk = 25000000; - slot_time = 128; - } - - ict = DIV_ROUND_UP(phy_clk, clk_get_rate(priv->clk)); - - nb8800_writeb(priv, NB8800_IC_THRESHOLD, ict); - nb8800_writeb(priv, NB8800_SLOT_TIME, slot_time); - nb8800_maskb(priv, NB8800_MAC_MODE, mac_mode_mask, mac_mode); -} - -static void nb8800_pause_config(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - u32 rxcr; - - if (priv->pause_aneg) { - if (!phydev || !phydev->link) - return; - - priv->pause_rx = phydev->pause; - priv->pause_tx = phydev->pause ^ phydev->asym_pause; - } - - nb8800_modb(priv, NB8800_RX_CTL, RX_PAUSE_EN, priv->pause_rx); - - rxcr = nb8800_readl(priv, NB8800_RXC_CR); - if (!!(rxcr & RCR_FL) == priv->pause_tx) - return; - - if (netif_running(dev)) { - napi_disable(&priv->napi); - netif_tx_lock_bh(dev); - nb8800_dma_stop(dev); - nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx); - nb8800_start_rx(dev); - netif_tx_unlock_bh(dev); - napi_enable(&priv->napi); - } else { - nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx); - } -} - -static void nb8800_link_reconfigure(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - int change = 0; - - if (phydev->link) { - if (phydev->speed != priv->speed) { - priv->speed = phydev->speed; - change = 1; - } - - if (phydev->duplex != priv->duplex) { - priv->duplex = phydev->duplex; - change = 1; - } - - if (change) - nb8800_mac_config(dev); - - nb8800_pause_config(dev); - } - - if (phydev->link != priv->link) { - priv->link = phydev->link; - change = 1; - } - - if (change) - phy_print_status(phydev); -} - -static void nb8800_update_mac_addr(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - int i; - - for (i = 0; i < ETH_ALEN; i++) - nb8800_writeb(priv, NB8800_SRC_ADDR(i), dev->dev_addr[i]); - - for (i = 0; i < ETH_ALEN; i++) - nb8800_writeb(priv, NB8800_UC_ADDR(i), dev->dev_addr[i]); -} - -static int nb8800_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sock = addr; - - if (netif_running(dev)) - return -EBUSY; - - ether_addr_copy(dev->dev_addr, sock->sa_data); - nb8800_update_mac_addr(dev); - - return 0; -} - -static void nb8800_mc_init(struct net_device *dev, int val) -{ - struct nb8800_priv *priv = netdev_priv(dev); - - nb8800_writeb(priv, NB8800_MC_INIT, val); - readb_poll_timeout_atomic(priv->base + NB8800_MC_INIT, val, !val, - 1, 1000); -} - -static void nb8800_set_rx_mode(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct netdev_hw_addr *ha; - int i; - - if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { - nb8800_mac_af(dev, false); - return; - } - - nb8800_mac_af(dev, true); - nb8800_mc_init(dev, 0); - - netdev_for_each_mc_addr(ha, dev) { - for (i = 0; i < ETH_ALEN; i++) - nb8800_writeb(priv, NB8800_MC_ADDR(i), ha->addr[i]); - - nb8800_mc_init(dev, 0xff); - } -} - -#define RX_DESC_SIZE (RX_DESC_COUNT * sizeof(struct nb8800_rx_desc)) -#define TX_DESC_SIZE (TX_DESC_COUNT * sizeof(struct nb8800_tx_desc)) - -static void nb8800_dma_free(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - unsigned int i; - - if (priv->rx_bufs) { - for (i = 0; i < RX_DESC_COUNT; i++) - if (priv->rx_bufs[i].page) - put_page(priv->rx_bufs[i].page); - - kfree(priv->rx_bufs); - priv->rx_bufs = NULL; - } - - if (priv->tx_bufs) { - for (i = 0; i < TX_DESC_COUNT; i++) - kfree_skb(priv->tx_bufs[i].skb); - - kfree(priv->tx_bufs); - priv->tx_bufs = NULL; - } - - if (priv->rx_descs) { - dma_free_coherent(dev->dev.parent, RX_DESC_SIZE, priv->rx_descs, - priv->rx_desc_dma); - priv->rx_descs = NULL; - } - - if (priv->tx_descs) { - dma_free_coherent(dev->dev.parent, TX_DESC_SIZE, priv->tx_descs, - priv->tx_desc_dma); - priv->tx_descs = NULL; - } -} - -static void nb8800_dma_reset(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct nb8800_rx_desc *rxd; - struct nb8800_tx_desc *txd; - unsigned int i; - - for (i = 0; i < RX_DESC_COUNT; i++) { - dma_addr_t rx_dma = priv->rx_desc_dma + i * sizeof(*rxd); - - rxd = &priv->rx_descs[i]; - rxd->desc.n_addr = rx_dma + sizeof(*rxd); - rxd->desc.r_addr = - rx_dma + offsetof(struct nb8800_rx_desc, report); - rxd->desc.config = priv->rx_dma_config; - rxd->report = 0; - } - - rxd->desc.n_addr = priv->rx_desc_dma; - rxd->desc.config |= DESC_EOC; - - priv->rx_eoc = RX_DESC_COUNT - 1; - - for (i = 0; i < TX_DESC_COUNT; i++) { - struct nb8800_tx_buf *txb = &priv->tx_bufs[i]; - dma_addr_t r_dma = txb->dma_desc + - offsetof(struct nb8800_tx_desc, report); - - txd = &priv->tx_descs[i]; - txd->desc[0].r_addr = r_dma; - txd->desc[1].r_addr = r_dma; - txd->report = 0; - } - - priv->tx_next = 0; - priv->tx_queue = 0; - priv->tx_done = 0; - atomic_set(&priv->tx_free, TX_DESC_COUNT); - - nb8800_writel(priv, NB8800_RX_DESC_ADDR, priv->rx_desc_dma); - - wmb(); /* ensure all setup is written before starting */ -} - -static int nb8800_dma_init(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - unsigned int n_rx = RX_DESC_COUNT; - unsigned int n_tx = TX_DESC_COUNT; - unsigned int i; - int err; - - priv->rx_descs = dma_alloc_coherent(dev->dev.parent, RX_DESC_SIZE, - &priv->rx_desc_dma, GFP_KERNEL); - if (!priv->rx_descs) - goto err_out; - - priv->rx_bufs = kcalloc(n_rx, sizeof(*priv->rx_bufs), GFP_KERNEL); - if (!priv->rx_bufs) - goto err_out; - - for (i = 0; i < n_rx; i++) { - err = nb8800_alloc_rx(dev, i, false); - if (err) - goto err_out; - } - - priv->tx_descs = dma_alloc_coherent(dev->dev.parent, TX_DESC_SIZE, - &priv->tx_desc_dma, GFP_KERNEL); - if (!priv->tx_descs) - goto err_out; - - priv->tx_bufs = kcalloc(n_tx, sizeof(*priv->tx_bufs), GFP_KERNEL); - if (!priv->tx_bufs) - goto err_out; - - for (i = 0; i < n_tx; i++) - priv->tx_bufs[i].dma_desc = - priv->tx_desc_dma + i * sizeof(struct nb8800_tx_desc); - - nb8800_dma_reset(dev); - - return 0; - -err_out: - nb8800_dma_free(dev); - - return -ENOMEM; -} - -static int nb8800_dma_stop(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct nb8800_tx_buf *txb = &priv->tx_bufs[0]; - struct nb8800_tx_desc *txd = &priv->tx_descs[0]; - int retry = 5; - u32 txcr; - u32 rxcr; - int err; - unsigned int i; - - /* wait for tx to finish */ - err = readl_poll_timeout_atomic(priv->base + NB8800_TXC_CR, txcr, - !(txcr & TCR_EN) && - priv->tx_done == priv->tx_next, - 1000, 1000000); - if (err) - return err; - - /* The rx DMA only stops if it reaches the end of chain. - * To make this happen, we set the EOC flag on all rx - * descriptors, put the device in loopback mode, and send - * a few dummy frames. The interrupt handler will ignore - * these since NAPI is disabled and no real frames are in - * the tx queue. - */ - - for (i = 0; i < RX_DESC_COUNT; i++) - priv->rx_descs[i].desc.config |= DESC_EOC; - - txd->desc[0].s_addr = - txb->dma_desc + offsetof(struct nb8800_tx_desc, buf); - txd->desc[0].config = DESC_BTS(2) | DESC_DS | DESC_EOF | DESC_EOC | 8; - memset(txd->buf, 0, sizeof(txd->buf)); - - nb8800_mac_af(dev, false); - nb8800_setb(priv, NB8800_MAC_MODE, LOOPBACK_EN); - - do { - nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc); - wmb(); - nb8800_writel(priv, NB8800_TXC_CR, txcr | TCR_EN); - - err = readl_poll_timeout_atomic(priv->base + NB8800_RXC_CR, - rxcr, !(rxcr & RCR_EN), - 1000, 100000); - } while (err && --retry); - - nb8800_mac_af(dev, true); - nb8800_clearb(priv, NB8800_MAC_MODE, LOOPBACK_EN); - nb8800_dma_reset(dev); - - return retry ? 0 : -ETIMEDOUT; -} - -static void nb8800_pause_adv(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - - if (!phydev) - return; - - phy_set_asym_pause(phydev, priv->pause_rx, priv->pause_tx); -} - -static int nb8800_open(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct phy_device *phydev; - int err; - - /* clear any pending interrupts */ - nb8800_writel(priv, NB8800_RXC_SR, 0xf); - nb8800_writel(priv, NB8800_TXC_SR, 0xf); - - err = nb8800_dma_init(dev); - if (err) - return err; - - err = request_irq(dev->irq, nb8800_irq, 0, dev_name(&dev->dev), dev); - if (err) - goto err_free_dma; - - nb8800_mac_rx(dev, true); - nb8800_mac_tx(dev, true); - - phydev = of_phy_connect(dev, priv->phy_node, - nb8800_link_reconfigure, 0, - priv->phy_mode); - if (!phydev) { - err = -ENODEV; - goto err_free_irq; - } - - nb8800_pause_adv(dev); - - netdev_reset_queue(dev); - napi_enable(&priv->napi); - netif_start_queue(dev); - - nb8800_start_rx(dev); - phy_start(phydev); - - return 0; - -err_free_irq: - free_irq(dev->irq, dev); -err_free_dma: - nb8800_dma_free(dev); - - return err; -} - -static int nb8800_stop(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - - phy_stop(phydev); - - netif_stop_queue(dev); - napi_disable(&priv->napi); - - nb8800_dma_stop(dev); - nb8800_mac_rx(dev, false); - nb8800_mac_tx(dev, false); - - phy_disconnect(phydev); - - free_irq(dev->irq, dev); - - nb8800_dma_free(dev); - - return 0; -} - -static const struct net_device_ops nb8800_netdev_ops = { - .ndo_open = nb8800_open, - .ndo_stop = nb8800_stop, - .ndo_start_xmit = nb8800_xmit, - .ndo_set_mac_address = nb8800_set_mac_address, - .ndo_set_rx_mode = nb8800_set_rx_mode, - .ndo_do_ioctl = phy_do_ioctl, - .ndo_validate_addr = eth_validate_addr, -}; - -static void nb8800_get_pauseparam(struct net_device *dev, - struct ethtool_pauseparam *pp) -{ - struct nb8800_priv *priv = netdev_priv(dev); - - pp->autoneg = priv->pause_aneg; - pp->rx_pause = priv->pause_rx; - pp->tx_pause = priv->pause_tx; -} - -static int nb8800_set_pauseparam(struct net_device *dev, - struct ethtool_pauseparam *pp) -{ - struct nb8800_priv *priv = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - - priv->pause_aneg = pp->autoneg; - priv->pause_rx = pp->rx_pause; - priv->pause_tx = pp->tx_pause; - - nb8800_pause_adv(dev); - - if (!priv->pause_aneg) - nb8800_pause_config(dev); - else if (phydev) - phy_start_aneg(phydev); - - return 0; -} - -static const char nb8800_stats_names[][ETH_GSTRING_LEN] = { - "rx_bytes_ok", - "rx_frames_ok", - "rx_undersize_frames", - "rx_fragment_frames", - "rx_64_byte_frames", - "rx_127_byte_frames", - "rx_255_byte_frames", - "rx_511_byte_frames", - "rx_1023_byte_frames", - "rx_max_size_frames", - "rx_oversize_frames", - "rx_bad_fcs_frames", - "rx_broadcast_frames", - "rx_multicast_frames", - "rx_control_frames", - "rx_pause_frames", - "rx_unsup_control_frames", - "rx_align_error_frames", - "rx_overrun_frames", - "rx_jabber_frames", - "rx_bytes", - "rx_frames", - - "tx_bytes_ok", - "tx_frames_ok", - "tx_64_byte_frames", - "tx_127_byte_frames", - "tx_255_byte_frames", - "tx_511_byte_frames", - "tx_1023_byte_frames", - "tx_max_size_frames", - "tx_oversize_frames", - "tx_broadcast_frames", - "tx_multicast_frames", - "tx_control_frames", - "tx_pause_frames", - "tx_underrun_frames", - "tx_single_collision_frames", - "tx_multi_collision_frames", - "tx_deferred_collision_frames", - "tx_late_collision_frames", - "tx_excessive_collision_frames", - "tx_bytes", - "tx_frames", - "tx_collisions", -}; - -#define NB8800_NUM_STATS ARRAY_SIZE(nb8800_stats_names) - -static int nb8800_get_sset_count(struct net_device *dev, int sset) -{ - if (sset == ETH_SS_STATS) - return NB8800_NUM_STATS; - - return -EOPNOTSUPP; -} - -static void nb8800_get_strings(struct net_device *dev, u32 sset, u8 *buf) -{ - if (sset == ETH_SS_STATS) - memcpy(buf, &nb8800_stats_names, sizeof(nb8800_stats_names)); -} - -static u32 nb8800_read_stat(struct net_device *dev, int index) -{ - struct nb8800_priv *priv = netdev_priv(dev); - - nb8800_writeb(priv, NB8800_STAT_INDEX, index); - - return nb8800_readl(priv, NB8800_STAT_DATA); -} - -static void nb8800_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *estats, u64 *st) -{ - unsigned int i; - u32 rx, tx; - - for (i = 0; i < NB8800_NUM_STATS / 2; i++) { - rx = nb8800_read_stat(dev, i); - tx = nb8800_read_stat(dev, i | 0x80); - st[i] = rx; - st[i + NB8800_NUM_STATS / 2] = tx; - } -} - -static const struct ethtool_ops nb8800_ethtool_ops = { - .nway_reset = phy_ethtool_nway_reset, - .get_link = ethtool_op_get_link, - .get_pauseparam = nb8800_get_pauseparam, - .set_pauseparam = nb8800_set_pauseparam, - .get_sset_count = nb8800_get_sset_count, - .get_strings = nb8800_get_strings, - .get_ethtool_stats = nb8800_get_ethtool_stats, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, -}; - -static int nb8800_hw_init(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - u32 val; - - val = TX_RETRY_EN | TX_PAD_EN | TX_APPEND_FCS; - nb8800_writeb(priv, NB8800_TX_CTL1, val); - - /* Collision retry count */ - nb8800_writeb(priv, NB8800_TX_CTL2, 5); - - val = RX_PAD_STRIP | RX_AF_EN; - nb8800_writeb(priv, NB8800_RX_CTL, val); - - /* Chosen by fair dice roll */ - nb8800_writeb(priv, NB8800_RANDOM_SEED, 4); - - /* TX cycles per deferral period */ - nb8800_writeb(priv, NB8800_TX_SDP, 12); - - /* The following three threshold values have been - * experimentally determined for good results. - */ - - /* RX/TX FIFO threshold for partial empty (64-bit entries) */ - nb8800_writeb(priv, NB8800_PE_THRESHOLD, 0); - - /* RX/TX FIFO threshold for partial full (64-bit entries) */ - nb8800_writeb(priv, NB8800_PF_THRESHOLD, 255); - - /* Buffer size for transmit (64-bit entries) */ - nb8800_writeb(priv, NB8800_TX_BUFSIZE, 64); - - /* Configure tx DMA */ - - val = nb8800_readl(priv, NB8800_TXC_CR); - val &= TCR_LE; /* keep endian setting */ - val |= TCR_DM; /* DMA descriptor mode */ - val |= TCR_RS; /* automatically store tx status */ - val |= TCR_DIE; /* interrupt on DMA chain completion */ - val |= TCR_TFI(7); /* interrupt after 7 frames transmitted */ - val |= TCR_BTS(2); /* 32-byte bus transaction size */ - nb8800_writel(priv, NB8800_TXC_CR, val); - - /* TX complete interrupt after 10 ms or 7 frames (see above) */ - val = clk_get_rate(priv->clk) / 100; - nb8800_writel(priv, NB8800_TX_ITR, val); - - /* Configure rx DMA */ - - val = nb8800_readl(priv, NB8800_RXC_CR); - val &= RCR_LE; /* keep endian setting */ - val |= RCR_DM; /* DMA descriptor mode */ - val |= RCR_RS; /* automatically store rx status */ - val |= RCR_DIE; /* interrupt at end of DMA chain */ - val |= RCR_RFI(7); /* interrupt after 7 frames received */ - val |= RCR_BTS(2); /* 32-byte bus transaction size */ - nb8800_writel(priv, NB8800_RXC_CR, val); - - /* The rx interrupt can fire before the DMA has completed - * unless a small delay is added. 50 us is hopefully enough. - */ - priv->rx_itr_irq = clk_get_rate(priv->clk) / 20000; - - /* In NAPI poll mode we want to disable interrupts, but the - * hardware does not permit this. Delay 10 ms instead. - */ - priv->rx_itr_poll = clk_get_rate(priv->clk) / 100; - - nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq); - - priv->rx_dma_config = RX_BUF_SIZE | DESC_BTS(2) | DESC_DS | DESC_EOF; - - /* Flow control settings */ - - /* Pause time of 0.1 ms */ - val = 100000 / 512; - nb8800_writeb(priv, NB8800_PQ1, val >> 8); - nb8800_writeb(priv, NB8800_PQ2, val & 0xff); - - /* Auto-negotiate by default */ - priv->pause_aneg = true; - priv->pause_rx = true; - priv->pause_tx = true; - - nb8800_mc_init(dev, 0); - - return 0; -} - -static int nb8800_tangox_init(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - u32 pad_mode = PAD_MODE_MII; - - switch (priv->phy_mode) { - case PHY_INTERFACE_MODE_MII: - case PHY_INTERFACE_MODE_GMII: - pad_mode = PAD_MODE_MII; - break; - - case PHY_INTERFACE_MODE_RGMII: - case PHY_INTERFACE_MODE_RGMII_ID: - case PHY_INTERFACE_MODE_RGMII_RXID: - case PHY_INTERFACE_MODE_RGMII_TXID: - pad_mode = PAD_MODE_RGMII; - break; - - default: - dev_err(dev->dev.parent, "unsupported phy mode %s\n", - phy_modes(priv->phy_mode)); - return -EINVAL; - } - - nb8800_writeb(priv, NB8800_TANGOX_PAD_MODE, pad_mode); - - return 0; -} - -static int nb8800_tangox_reset(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - int clk_div; - - nb8800_writeb(priv, NB8800_TANGOX_RESET, 0); - usleep_range(1000, 10000); - nb8800_writeb(priv, NB8800_TANGOX_RESET, 1); - - wmb(); /* ensure reset is cleared before proceeding */ - - clk_div = DIV_ROUND_UP(clk_get_rate(priv->clk), 2 * MAX_MDC_CLOCK); - nb8800_writew(priv, NB8800_TANGOX_MDIO_CLKDIV, clk_div); - - return 0; -} - -static const struct nb8800_ops nb8800_tangox_ops = { - .init = nb8800_tangox_init, - .reset = nb8800_tangox_reset, -}; - -static int nb8800_tango4_init(struct net_device *dev) -{ - struct nb8800_priv *priv = netdev_priv(dev); - int err; - - err = nb8800_tangox_init(dev); - if (err) - return err; - - /* On tango4 interrupt on DMA completion per frame works and gives - * better performance despite generating more rx interrupts. - */ - - /* Disable unnecessary interrupt on rx completion */ - nb8800_clearl(priv, NB8800_RXC_CR, RCR_RFI(7)); - - /* Request interrupt on descriptor DMA completion */ - priv->rx_dma_config |= DESC_ID; - - return 0; -} - -static const struct nb8800_ops nb8800_tango4_ops = { - .init = nb8800_tango4_init, - .reset = nb8800_tangox_reset, -}; - -static const struct of_device_id nb8800_dt_ids[] = { - { - .compatible = "aurora,nb8800", - }, - { - .compatible = "sigma,smp8642-ethernet", - .data = &nb8800_tangox_ops, - }, - { - .compatible = "sigma,smp8734-ethernet", - .data = &nb8800_tango4_ops, - }, - { } -}; -MODULE_DEVICE_TABLE(of, nb8800_dt_ids); - -static int nb8800_probe(struct platform_device *pdev) -{ - const struct of_device_id *match; - const struct nb8800_ops *ops = NULL; - struct nb8800_priv *priv; - struct resource *res; - struct net_device *dev; - struct mii_bus *bus; - const unsigned char *mac; - void __iomem *base; - int irq; - int ret; - - match = of_match_device(nb8800_dt_ids, &pdev->dev); - if (match) - ops = match->data; - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -EINVAL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - dev_dbg(&pdev->dev, "AU-NB8800 Ethernet at %pa\n", &res->start); - - dev = alloc_etherdev(sizeof(*priv)); - if (!dev) - return -ENOMEM; - - platform_set_drvdata(pdev, dev); - SET_NETDEV_DEV(dev, &pdev->dev); - - priv = netdev_priv(dev); - priv->base = base; - - ret = of_get_phy_mode(pdev->dev.of_node, &priv->phy_mode); - if (ret) - priv->phy_mode = PHY_INTERFACE_MODE_RGMII; - - priv->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(priv->clk); - goto err_free_dev; - } - - ret = clk_prepare_enable(priv->clk); - if (ret) - goto err_free_dev; - - spin_lock_init(&priv->tx_lock); - - if (ops && ops->reset) { - ret = ops->reset(dev); - if (ret) - goto err_disable_clk; - } - - bus = devm_mdiobus_alloc(&pdev->dev); - if (!bus) { - ret = -ENOMEM; - goto err_disable_clk; - } - - bus->name = "nb8800-mii"; - bus->read = nb8800_mdio_read; - bus->write = nb8800_mdio_write; - bus->parent = &pdev->dev; - snprintf(bus->id, MII_BUS_ID_SIZE, "%lx.nb8800-mii", - (unsigned long)res->start); - bus->priv = priv; - - ret = of_mdiobus_register(bus, pdev->dev.of_node); - if (ret) { - dev_err(&pdev->dev, "failed to register MII bus\n"); - goto err_disable_clk; - } - - if (of_phy_is_fixed_link(pdev->dev.of_node)) { - ret = of_phy_register_fixed_link(pdev->dev.of_node); - if (ret < 0) { - dev_err(&pdev->dev, "bad fixed-link spec\n"); - goto err_free_bus; - } - priv->phy_node = of_node_get(pdev->dev.of_node); - } - - if (!priv->phy_node) - priv->phy_node = of_parse_phandle(pdev->dev.of_node, - "phy-handle", 0); - - if (!priv->phy_node) { - dev_err(&pdev->dev, "no PHY specified\n"); - ret = -ENODEV; - goto err_free_bus; - } - - priv->mii_bus = bus; - - ret = nb8800_hw_init(dev); - if (ret) - goto err_deregister_fixed_link; - - if (ops && ops->init) { - ret = ops->init(dev); - if (ret) - goto err_deregister_fixed_link; - } - - dev->netdev_ops = &nb8800_netdev_ops; - dev->ethtool_ops = &nb8800_ethtool_ops; - dev->flags |= IFF_MULTICAST; - dev->irq = irq; - - mac = of_get_mac_address(pdev->dev.of_node); - if (!IS_ERR(mac)) - ether_addr_copy(dev->dev_addr, mac); - - if (!is_valid_ether_addr(dev->dev_addr)) - eth_hw_addr_random(dev); - - nb8800_update_mac_addr(dev); - - netif_carrier_off(dev); - - ret = register_netdev(dev); - if (ret) { - netdev_err(dev, "failed to register netdev\n"); - goto err_free_dma; - } - - netif_napi_add(dev, &priv->napi, nb8800_poll, NAPI_POLL_WEIGHT); - - netdev_info(dev, "MAC address %pM\n", dev->dev_addr); - - return 0; - -err_free_dma: - nb8800_dma_free(dev); -err_deregister_fixed_link: - if (of_phy_is_fixed_link(pdev->dev.of_node)) - of_phy_deregister_fixed_link(pdev->dev.of_node); -err_free_bus: - of_node_put(priv->phy_node); - mdiobus_unregister(bus); -err_disable_clk: - clk_disable_unprepare(priv->clk); -err_free_dev: - free_netdev(dev); - - return ret; -} - -static int nb8800_remove(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct nb8800_priv *priv = netdev_priv(ndev); - - unregister_netdev(ndev); - if (of_phy_is_fixed_link(pdev->dev.of_node)) - of_phy_deregister_fixed_link(pdev->dev.of_node); - of_node_put(priv->phy_node); - - mdiobus_unregister(priv->mii_bus); - - clk_disable_unprepare(priv->clk); - - nb8800_dma_free(ndev); - free_netdev(ndev); - - return 0; -} - -static struct platform_driver nb8800_driver = { - .driver = { - .name = "nb8800", - .of_match_table = nb8800_dt_ids, - }, - .probe = nb8800_probe, - .remove = nb8800_remove, -}; - -module_platform_driver(nb8800_driver); - -MODULE_DESCRIPTION("Aurora AU-NB8800 Ethernet driver"); -MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/aurora/nb8800.h b/drivers/net/ethernet/aurora/nb8800.h deleted file mode 100644 index 40941fb6065b..000000000000 --- a/drivers/net/ethernet/aurora/nb8800.h +++ /dev/null @@ -1,316 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _NB8800_H_ -#define _NB8800_H_ - -#include <linux/types.h> -#include <linux/skbuff.h> -#include <linux/phy.h> -#include <linux/clk.h> -#include <linux/bitops.h> - -#define RX_DESC_COUNT 256 -#define TX_DESC_COUNT 256 - -#define NB8800_DESC_LOW 4 - -#define RX_BUF_SIZE 1552 - -#define RX_COPYBREAK 256 -#define RX_COPYHDR 128 - -#define MAX_MDC_CLOCK 2500000 - -/* Stargate Solutions SSN8800 core registers */ -#define NB8800_TX_CTL1 0x000 -#define TX_TPD BIT(5) -#define TX_APPEND_FCS BIT(4) -#define TX_PAD_EN BIT(3) -#define TX_RETRY_EN BIT(2) -#define TX_EN BIT(0) - -#define NB8800_TX_CTL2 0x001 - -#define NB8800_RX_CTL 0x004 -#define RX_BC_DISABLE BIT(7) -#define RX_RUNT BIT(6) -#define RX_AF_EN BIT(5) -#define RX_PAUSE_EN BIT(3) -#define RX_SEND_CRC BIT(2) -#define RX_PAD_STRIP BIT(1) -#define RX_EN BIT(0) - -#define NB8800_RANDOM_SEED 0x008 -#define NB8800_TX_SDP 0x14 -#define NB8800_TX_TPDP1 0x18 -#define NB8800_TX_TPDP2 0x19 -#define NB8800_SLOT_TIME 0x1c - -#define NB8800_MDIO_CMD 0x020 -#define MDIO_CMD_GO BIT(31) -#define MDIO_CMD_WR BIT(26) -#define MDIO_CMD_ADDR(x) ((x) << 21) -#define MDIO_CMD_REG(x) ((x) << 16) -#define MDIO_CMD_DATA(x) ((x) << 0) - -#define NB8800_MDIO_STS 0x024 -#define MDIO_STS_ERR BIT(31) - -#define NB8800_MC_ADDR(i) (0x028 + (i)) -#define NB8800_MC_INIT 0x02e -#define NB8800_UC_ADDR(i) (0x03c + (i)) - -#define NB8800_MAC_MODE 0x044 -#define RGMII_MODE BIT(7) -#define HALF_DUPLEX BIT(4) -#define BURST_EN BIT(3) -#define LOOPBACK_EN BIT(2) -#define GMAC_MODE BIT(0) - -#define NB8800_IC_THRESHOLD 0x050 -#define NB8800_PE_THRESHOLD 0x051 -#define NB8800_PF_THRESHOLD 0x052 -#define NB8800_TX_BUFSIZE 0x054 -#define NB8800_FIFO_CTL 0x056 -#define NB8800_PQ1 0x060 -#define NB8800_PQ2 0x061 -#define NB8800_SRC_ADDR(i) (0x06a + (i)) -#define NB8800_STAT_DATA 0x078 -#define NB8800_STAT_INDEX 0x07c -#define NB8800_STAT_CLEAR 0x07d - -#define NB8800_SLEEP_MODE 0x07e -#define SLEEP_MODE BIT(0) - -#define NB8800_WAKEUP 0x07f -#define WAKEUP BIT(0) - -/* Aurora NB8800 host interface registers */ -#define NB8800_TXC_CR 0x100 -#define TCR_LK BIT(12) -#define TCR_DS BIT(11) -#define TCR_BTS(x) (((x) & 0x7) << 8) -#define TCR_DIE BIT(7) -#define TCR_TFI(x) (((x) & 0x7) << 4) -#define TCR_LE BIT(3) -#define TCR_RS BIT(2) -#define TCR_DM BIT(1) -#define TCR_EN BIT(0) - -#define NB8800_TXC_SR 0x104 -#define TSR_DE BIT(3) -#define TSR_DI BIT(2) -#define TSR_TO BIT(1) -#define TSR_TI BIT(0) - -#define NB8800_TX_SAR 0x108 -#define NB8800_TX_DESC_ADDR 0x10c - -#define NB8800_TX_REPORT_ADDR 0x110 -#define TX_BYTES_TRANSFERRED(x) (((x) >> 16) & 0xffff) -#define TX_FIRST_DEFERRAL BIT(7) -#define TX_EARLY_COLLISIONS(x) (((x) >> 3) & 0xf) -#define TX_LATE_COLLISION BIT(2) -#define TX_PACKET_DROPPED BIT(1) -#define TX_FIFO_UNDERRUN BIT(0) -#define IS_TX_ERROR(r) ((r) & 0x07) - -#define NB8800_TX_FIFO_SR 0x114 -#define NB8800_TX_ITR 0x118 - -#define NB8800_RXC_CR 0x200 -#define RCR_FL BIT(13) -#define RCR_LK BIT(12) -#define RCR_DS BIT(11) -#define RCR_BTS(x) (((x) & 7) << 8) -#define RCR_DIE BIT(7) -#define RCR_RFI(x) (((x) & 7) << 4) -#define RCR_LE BIT(3) -#define RCR_RS BIT(2) -#define RCR_DM BIT(1) -#define RCR_EN BIT(0) - -#define NB8800_RXC_SR 0x204 -#define RSR_DE BIT(3) -#define RSR_DI BIT(2) -#define RSR_RO BIT(1) -#define RSR_RI BIT(0) - -#define NB8800_RX_SAR 0x208 -#define NB8800_RX_DESC_ADDR 0x20c - -#define NB8800_RX_REPORT_ADDR 0x210 -#define RX_BYTES_TRANSFERRED(x) (((x) >> 16) & 0xFFFF) -#define RX_MULTICAST_PKT BIT(9) -#define RX_BROADCAST_PKT BIT(8) -#define RX_LENGTH_ERR BIT(7) -#define RX_FCS_ERR BIT(6) -#define RX_RUNT_PKT BIT(5) -#define RX_FIFO_OVERRUN BIT(4) -#define RX_LATE_COLLISION BIT(3) -#define RX_ALIGNMENT_ERROR BIT(2) -#define RX_ERROR_MASK 0xfc -#define IS_RX_ERROR(r) ((r) & RX_ERROR_MASK) - -#define NB8800_RX_FIFO_SR 0x214 -#define NB8800_RX_ITR 0x218 - -/* Sigma Designs SMP86xx additional registers */ -#define NB8800_TANGOX_PAD_MODE 0x400 -#define PAD_MODE_MASK 0x7 -#define PAD_MODE_MII 0x0 -#define PAD_MODE_RGMII 0x1 -#define PAD_MODE_GTX_CLK_INV BIT(3) -#define PAD_MODE_GTX_CLK_DELAY BIT(4) - -#define NB8800_TANGOX_MDIO_CLKDIV 0x420 -#define NB8800_TANGOX_RESET 0x424 - -/* Hardware DMA descriptor */ -struct nb8800_dma_desc { - u32 s_addr; /* start address */ - u32 n_addr; /* next descriptor address */ - u32 r_addr; /* report address */ - u32 config; -} __aligned(8); - -#define DESC_ID BIT(23) -#define DESC_EOC BIT(22) -#define DESC_EOF BIT(21) -#define DESC_LK BIT(20) -#define DESC_DS BIT(19) -#define DESC_BTS(x) (((x) & 0x7) << 16) - -/* DMA descriptor and associated data for rx. - * Allocated from coherent memory. - */ -struct nb8800_rx_desc { - /* DMA descriptor */ - struct nb8800_dma_desc desc; - - /* Status report filled in by hardware */ - u32 report; -}; - -/* Address of buffer on rx ring */ -struct nb8800_rx_buf { - struct page *page; - unsigned long offset; -}; - -/* DMA descriptors and associated data for tx. - * Allocated from coherent memory. - */ -struct nb8800_tx_desc { - /* DMA descriptor. The second descriptor is used if packet - * data is unaligned. - */ - struct nb8800_dma_desc desc[2]; - - /* Status report filled in by hardware */ - u32 report; - - /* Bounce buffer for initial unaligned part of packet */ - u8 buf[8] __aligned(8); -}; - -/* Packet in tx queue */ -struct nb8800_tx_buf { - /* Currently queued skb */ - struct sk_buff *skb; - - /* DMA address of the first descriptor */ - dma_addr_t dma_desc; - - /* DMA address of packet data */ - dma_addr_t dma_addr; - - /* Length of DMA mapping, less than skb->len if alignment - * buffer is used. - */ - unsigned int dma_len; - - /* Number of packets in chain starting here */ - unsigned int chain_len; - - /* Packet chain ready to be submitted to hardware */ - bool ready; -}; - -struct nb8800_priv { - struct napi_struct napi; - - void __iomem *base; - - /* RX DMA descriptors */ - struct nb8800_rx_desc *rx_descs; - - /* RX buffers referenced by DMA descriptors */ - struct nb8800_rx_buf *rx_bufs; - - /* Current end of chain */ - u32 rx_eoc; - - /* Value for rx interrupt time register in NAPI interrupt mode */ - u32 rx_itr_irq; - - /* Value for rx interrupt time register in NAPI poll mode */ - u32 rx_itr_poll; - - /* Value for config field of rx DMA descriptors */ - u32 rx_dma_config; - - /* TX DMA descriptors */ - struct nb8800_tx_desc *tx_descs; - - /* TX packet queue */ - struct nb8800_tx_buf *tx_bufs; - - /* Number of free tx queue entries */ - atomic_t tx_free; - - /* First free tx queue entry */ - u32 tx_next; - - /* Next buffer to transmit */ - u32 tx_queue; - - /* Start of current packet chain */ - struct nb8800_tx_buf *tx_chain; - - /* Next buffer to reclaim */ - u32 tx_done; - - /* Lock for DMA activation */ - spinlock_t tx_lock; - - struct mii_bus *mii_bus; - struct device_node *phy_node; - - /* PHY connection type from DT */ - phy_interface_t phy_mode; - - /* Current link status */ - int speed; - int duplex; - int link; - - /* Pause settings */ - bool pause_aneg; - bool pause_rx; - bool pause_tx; - - /* DMA base address of rx descriptors, see rx_descs above */ - dma_addr_t rx_desc_dma; - - /* DMA base address of tx descriptors, see tx_descs above */ - dma_addr_t tx_desc_dma; - - struct clk *clk; -}; - -struct nb8800_ops { - int (*init)(struct net_device *dev); - int (*reset)(struct net_device *dev); -}; - -#endif /* _NB8800_H_ */ diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 7b79528d6eed..4bdf8fbe75a6 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -174,7 +174,6 @@ config BGMAC_BCMA config BGMAC_PLATFORM tristate "Broadcom iProc GBit platform support" depends on ARCH_BCM_IPROC || COMPILE_TEST - depends on OF select BGMAC select PHYLIB select FIXED_PHY diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 916824cca3fd..fd8767213165 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -220,7 +220,7 @@ static void bcm_enet_mdio_write_mii(struct net_device *dev, int mii_id, /* * refill rx queue */ -static int bcm_enet_refill_rx(struct net_device *dev) +static int bcm_enet_refill_rx(struct net_device *dev, bool napi_mode) { struct bcm_enet_priv *priv; @@ -228,26 +228,29 @@ static int bcm_enet_refill_rx(struct net_device *dev) while (priv->rx_desc_count < priv->rx_ring_size) { struct bcm_enet_desc *desc; - struct sk_buff *skb; - dma_addr_t p; int desc_idx; u32 len_stat; desc_idx = priv->rx_dirty_desc; desc = &priv->rx_desc_cpu[desc_idx]; - if (!priv->rx_skb[desc_idx]) { - skb = netdev_alloc_skb(dev, priv->rx_skb_size); - if (!skb) + if (!priv->rx_buf[desc_idx]) { + void *buf; + + if (likely(napi_mode)) + buf = napi_alloc_frag(priv->rx_frag_size); + else + buf = netdev_alloc_frag(priv->rx_frag_size); + if (unlikely(!buf)) break; - priv->rx_skb[desc_idx] = skb; - p = dma_map_single(&priv->pdev->dev, skb->data, - priv->rx_skb_size, - DMA_FROM_DEVICE); - desc->address = p; + priv->rx_buf[desc_idx] = buf; + desc->address = dma_map_single(&priv->pdev->dev, + buf + priv->rx_buf_offset, + priv->rx_buf_size, + DMA_FROM_DEVICE); } - len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT; + len_stat = priv->rx_buf_size << DMADESC_LENGTH_SHIFT; len_stat |= DMADESC_OWNER_MASK; if (priv->rx_dirty_desc == priv->rx_ring_size - 1) { len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift); @@ -287,7 +290,7 @@ static void bcm_enet_refill_rx_timer(struct timer_list *t) struct net_device *dev = priv->net_dev; spin_lock(&priv->rx_lock); - bcm_enet_refill_rx(dev); + bcm_enet_refill_rx(dev, false); spin_unlock(&priv->rx_lock); } @@ -297,10 +300,12 @@ static void bcm_enet_refill_rx_timer(struct timer_list *t) static int bcm_enet_receive_queue(struct net_device *dev, int budget) { struct bcm_enet_priv *priv; + struct list_head rx_list; struct device *kdev; int processed; priv = netdev_priv(dev); + INIT_LIST_HEAD(&rx_list); kdev = &priv->pdev->dev; processed = 0; @@ -315,6 +320,7 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) int desc_idx; u32 len_stat; unsigned int len; + void *buf; desc_idx = priv->rx_curr_desc; desc = &priv->rx_desc_cpu[desc_idx]; @@ -333,7 +339,6 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) priv->rx_curr_desc++; if (priv->rx_curr_desc == priv->rx_ring_size) priv->rx_curr_desc = 0; - priv->rx_desc_count--; /* if the packet does not have start of packet _and_ * end of packet flag set, then just recycle it */ @@ -360,16 +365,14 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) } /* valid packet */ - skb = priv->rx_skb[desc_idx]; + buf = priv->rx_buf[desc_idx]; len = (len_stat & DMADESC_LENGTH_MASK) >> DMADESC_LENGTH_SHIFT; /* don't include FCS */ len -= 4; if (len < copybreak) { - struct sk_buff *nskb; - - nskb = napi_alloc_skb(&priv->napi, len); - if (!nskb) { + skb = napi_alloc_skb(&priv->napi, len); + if (unlikely(!skb)) { /* forget packet, just rearm desc */ dev->stats.rx_dropped++; continue; @@ -377,26 +380,36 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) dma_sync_single_for_cpu(kdev, desc->address, len, DMA_FROM_DEVICE); - memcpy(nskb->data, skb->data, len); + memcpy(skb->data, buf + priv->rx_buf_offset, len); dma_sync_single_for_device(kdev, desc->address, len, DMA_FROM_DEVICE); - skb = nskb; } else { - dma_unmap_single(&priv->pdev->dev, desc->address, - priv->rx_skb_size, DMA_FROM_DEVICE); - priv->rx_skb[desc_idx] = NULL; + dma_unmap_single(kdev, desc->address, + priv->rx_buf_size, DMA_FROM_DEVICE); + priv->rx_buf[desc_idx] = NULL; + + skb = build_skb(buf, priv->rx_frag_size); + if (unlikely(!skb)) { + skb_free_frag(buf); + dev->stats.rx_dropped++; + continue; + } + skb_reserve(skb, priv->rx_buf_offset); } skb_put(skb, len); skb->protocol = eth_type_trans(skb, dev); dev->stats.rx_packets++; dev->stats.rx_bytes += len; - netif_receive_skb(skb); + list_add_tail(&skb->list, &rx_list); - } while (--budget > 0); + } while (processed < budget); + + netif_receive_skb_list(&rx_list); + priv->rx_desc_count -= processed; if (processed || !priv->rx_desc_count) { - bcm_enet_refill_rx(dev); + bcm_enet_refill_rx(dev, true); /* kick rx dma */ enet_dmac_writel(priv, priv->dma_chan_en_mask, @@ -413,9 +426,11 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) static int bcm_enet_tx_reclaim(struct net_device *dev, int force) { struct bcm_enet_priv *priv; + unsigned int bytes; int released; priv = netdev_priv(dev); + bytes = 0; released = 0; while (priv->tx_desc_count < priv->tx_ring_size) { @@ -452,10 +467,13 @@ static int bcm_enet_tx_reclaim(struct net_device *dev, int force) if (desc->len_stat & DMADESC_UNDER_MASK) dev->stats.tx_errors++; + bytes += skb->len; dev_kfree_skb(skb); released++; } + netdev_completed_queue(dev, released, bytes); + if (netif_queue_stopped(dev) && released) netif_wake_queue(dev); @@ -622,8 +640,11 @@ bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) desc->len_stat = len_stat; wmb(); + netdev_sent_queue(dev, skb->len); + /* kick tx dma */ - enet_dmac_writel(priv, priv->dma_chan_en_mask, + if (!netdev_xmit_more() || !priv->tx_desc_count) + enet_dmac_writel(priv, priv->dma_chan_en_mask, ENETDMAC_CHANCFG, priv->tx_chan); /* stop queue if no more desc available */ @@ -845,6 +866,24 @@ static void bcm_enet_adjust_link(struct net_device *dev) priv->pause_tx ? "tx" : "off"); } +static void bcm_enet_free_rx_buf_ring(struct device *kdev, struct bcm_enet_priv *priv) +{ + int i; + + for (i = 0; i < priv->rx_ring_size; i++) { + struct bcm_enet_desc *desc; + + if (!priv->rx_buf[i]) + continue; + + desc = &priv->rx_desc_cpu[i]; + dma_unmap_single(kdev, desc->address, priv->rx_buf_size, + DMA_FROM_DEVICE); + skb_free_frag(priv->rx_buf[i]); + } + kfree(priv->rx_buf); +} + /* * open callback, allocate dma rings & buffers and start rx operation */ @@ -954,10 +993,10 @@ static int bcm_enet_open(struct net_device *dev) priv->tx_curr_desc = 0; spin_lock_init(&priv->tx_lock); - /* init & fill rx ring with skbs */ - priv->rx_skb = kcalloc(priv->rx_ring_size, sizeof(struct sk_buff *), + /* init & fill rx ring with buffers */ + priv->rx_buf = kcalloc(priv->rx_ring_size, sizeof(void *), GFP_KERNEL); - if (!priv->rx_skb) { + if (!priv->rx_buf) { ret = -ENOMEM; goto out_free_tx_skb; } @@ -974,8 +1013,8 @@ static int bcm_enet_open(struct net_device *dev) enet_dmac_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, ENETDMAC_BUFALLOC, priv->rx_chan); - if (bcm_enet_refill_rx(dev)) { - dev_err(kdev, "cannot allocate rx skb queue\n"); + if (bcm_enet_refill_rx(dev, false)) { + dev_err(kdev, "cannot allocate rx buffer queue\n"); ret = -ENOMEM; goto out; } @@ -1069,18 +1108,7 @@ static int bcm_enet_open(struct net_device *dev) return 0; out: - for (i = 0; i < priv->rx_ring_size; i++) { - struct bcm_enet_desc *desc; - - if (!priv->rx_skb[i]) - continue; - - desc = &priv->rx_desc_cpu[i]; - dma_unmap_single(kdev, desc->address, priv->rx_skb_size, - DMA_FROM_DEVICE); - kfree_skb(priv->rx_skb[i]); - } - kfree(priv->rx_skb); + bcm_enet_free_rx_buf_ring(kdev, priv); out_free_tx_skb: kfree(priv->tx_skb); @@ -1159,12 +1187,12 @@ static int bcm_enet_stop(struct net_device *dev) { struct bcm_enet_priv *priv; struct device *kdev; - int i; priv = netdev_priv(dev); kdev = &priv->pdev->dev; netif_stop_queue(dev); + netdev_reset_queue(dev); napi_disable(&priv->napi); if (priv->has_phy) phy_stop(dev->phydev); @@ -1186,21 +1214,10 @@ static int bcm_enet_stop(struct net_device *dev) /* force reclaim of all tx buffers */ bcm_enet_tx_reclaim(dev, 1); - /* free the rx skb ring */ - for (i = 0; i < priv->rx_ring_size; i++) { - struct bcm_enet_desc *desc; - - if (!priv->rx_skb[i]) - continue; - - desc = &priv->rx_desc_cpu[i]; - dma_unmap_single(kdev, desc->address, priv->rx_skb_size, - DMA_FROM_DEVICE); - kfree_skb(priv->rx_skb[i]); - } + /* free the rx buffer ring */ + bcm_enet_free_rx_buf_ring(kdev, priv); /* free remaining allocated memory */ - kfree(priv->rx_skb); kfree(priv->tx_skb); dma_free_coherent(kdev, priv->rx_desc_alloc_size, priv->rx_desc_cpu, priv->rx_desc_dma); @@ -1622,9 +1639,12 @@ static int bcm_enet_change_mtu(struct net_device *dev, int new_mtu) * align rx buffer size to dma burst len, account FCS since * it's appended */ - priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN, + priv->rx_buf_size = ALIGN(actual_mtu + ETH_FCS_LEN, priv->dma_maxburst * 4); + priv->rx_frag_size = SKB_DATA_ALIGN(priv->rx_buf_offset + priv->rx_buf_size) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + dev->mtu = new_mtu; return 0; } @@ -1709,6 +1729,7 @@ static int bcm_enet_probe(struct platform_device *pdev) priv->enet_is_sw = false; priv->dma_maxburst = BCMENET_DMA_MAXBURST; + priv->rx_buf_offset = NET_SKB_PAD; ret = bcm_enet_change_mtu(dev, dev->mtu); if (ret) @@ -2126,7 +2147,7 @@ static int bcm_enetsw_open(struct net_device *dev) priv->tx_skb = kcalloc(priv->tx_ring_size, sizeof(struct sk_buff *), GFP_KERNEL); if (!priv->tx_skb) { - dev_err(kdev, "cannot allocate rx skb queue\n"); + dev_err(kdev, "cannot allocate tx skb queue\n"); ret = -ENOMEM; goto out_free_tx_ring; } @@ -2136,11 +2157,11 @@ static int bcm_enetsw_open(struct net_device *dev) priv->tx_curr_desc = 0; spin_lock_init(&priv->tx_lock); - /* init & fill rx ring with skbs */ - priv->rx_skb = kcalloc(priv->rx_ring_size, sizeof(struct sk_buff *), + /* init & fill rx ring with buffers */ + priv->rx_buf = kcalloc(priv->rx_ring_size, sizeof(void *), GFP_KERNEL); - if (!priv->rx_skb) { - dev_err(kdev, "cannot allocate rx skb queue\n"); + if (!priv->rx_buf) { + dev_err(kdev, "cannot allocate rx buffer queue\n"); ret = -ENOMEM; goto out_free_tx_skb; } @@ -2187,8 +2208,8 @@ static int bcm_enetsw_open(struct net_device *dev) enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, ENETDMA_BUFALLOC_REG(priv->rx_chan)); - if (bcm_enet_refill_rx(dev)) { - dev_err(kdev, "cannot allocate rx skb queue\n"); + if (bcm_enet_refill_rx(dev, false)) { + dev_err(kdev, "cannot allocate rx buffer queue\n"); ret = -ENOMEM; goto out; } @@ -2287,18 +2308,7 @@ static int bcm_enetsw_open(struct net_device *dev) return 0; out: - for (i = 0; i < priv->rx_ring_size; i++) { - struct bcm_enet_desc *desc; - - if (!priv->rx_skb[i]) - continue; - - desc = &priv->rx_desc_cpu[i]; - dma_unmap_single(kdev, desc->address, priv->rx_skb_size, - DMA_FROM_DEVICE); - kfree_skb(priv->rx_skb[i]); - } - kfree(priv->rx_skb); + bcm_enet_free_rx_buf_ring(kdev, priv); out_free_tx_skb: kfree(priv->tx_skb); @@ -2327,13 +2337,13 @@ static int bcm_enetsw_stop(struct net_device *dev) { struct bcm_enet_priv *priv; struct device *kdev; - int i; priv = netdev_priv(dev); kdev = &priv->pdev->dev; del_timer_sync(&priv->swphy_poll); netif_stop_queue(dev); + netdev_reset_queue(dev); napi_disable(&priv->napi); del_timer_sync(&priv->rx_timeout); @@ -2348,21 +2358,10 @@ static int bcm_enetsw_stop(struct net_device *dev) /* force reclaim of all tx buffers */ bcm_enet_tx_reclaim(dev, 1); - /* free the rx skb ring */ - for (i = 0; i < priv->rx_ring_size; i++) { - struct bcm_enet_desc *desc; - - if (!priv->rx_skb[i]) - continue; - - desc = &priv->rx_desc_cpu[i]; - dma_unmap_single(kdev, desc->address, priv->rx_skb_size, - DMA_FROM_DEVICE); - kfree_skb(priv->rx_skb[i]); - } + /* free the rx buffer ring */ + bcm_enet_free_rx_buf_ring(kdev, priv); /* free remaining allocated memory */ - kfree(priv->rx_skb); kfree(priv->tx_skb); dma_free_coherent(kdev, priv->rx_desc_alloc_size, priv->rx_desc_cpu, priv->rx_desc_dma); @@ -2659,6 +2658,7 @@ static int bcm_enetsw_probe(struct platform_device *pdev) priv->rx_ring_size = BCMENET_DEF_RX_DESC; priv->tx_ring_size = BCMENET_DEF_TX_DESC; priv->dma_maxburst = BCMENETSW_DMA_MAXBURST; + priv->rx_buf_offset = NET_SKB_PAD + NET_IP_ALIGN; pd = dev_get_platdata(&pdev->dev); if (pd) { diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.h b/drivers/net/ethernet/broadcom/bcm63xx_enet.h index 1d3c917eb830..78f1830fb3cb 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.h +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.h @@ -230,11 +230,17 @@ struct bcm_enet_priv { /* next dirty rx descriptor to refill */ int rx_dirty_desc; - /* size of allocated rx skbs */ - unsigned int rx_skb_size; + /* size of allocated rx buffers */ + unsigned int rx_buf_size; - /* list of skb given to hw for rx */ - struct sk_buff **rx_skb; + /* allocated rx buffer offset */ + unsigned int rx_buf_offset; + + /* size of allocated rx frag */ + unsigned int rx_frag_size; + + /* list of buffer given to hw for rx */ + void **rx_buf; /* used when rx skb allocation failed, so we defer rx queue * refill */ diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 0404aafd5ce5..777bbf6d2586 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/dsa/brcm.h> #include <linux/etherdevice.h> #include <linux/platform_device.h> #include <linux/of.h> @@ -2310,33 +2311,22 @@ static const struct net_device_ops bcm_sysport_netdev_ops = { .ndo_select_queue = bcm_sysport_select_queue, }; -static int bcm_sysport_map_queues(struct notifier_block *nb, - struct dsa_notifier_register_info *info) +static int bcm_sysport_map_queues(struct net_device *dev, + struct net_device *slave_dev) { + struct dsa_port *dp = dsa_port_from_netdev(slave_dev); + struct bcm_sysport_priv *priv = netdev_priv(dev); struct bcm_sysport_tx_ring *ring; - struct bcm_sysport_priv *priv; - struct net_device *slave_dev; unsigned int num_tx_queues; unsigned int q, qp, port; - struct net_device *dev; - - priv = container_of(nb, struct bcm_sysport_priv, dsa_notifier); - if (priv->netdev != info->master) - return 0; - - dev = info->master; /* We can't be setting up queue inspection for non directly attached * switches */ - if (info->switch_number) + if (dp->ds->index) return 0; - if (dev->netdev_ops != &bcm_sysport_netdev_ops) - return 0; - - port = info->port_number; - slave_dev = info->info.dev; + port = dp->index; /* On SYSTEMPORT Lite we have twice as less queues, so we cannot do a * 1:1 mapping, we can only do a 2:1 mapping. By reducing the number of @@ -2376,27 +2366,16 @@ static int bcm_sysport_map_queues(struct notifier_block *nb, return 0; } -static int bcm_sysport_unmap_queues(struct notifier_block *nb, - struct dsa_notifier_register_info *info) +static int bcm_sysport_unmap_queues(struct net_device *dev, + struct net_device *slave_dev) { + struct dsa_port *dp = dsa_port_from_netdev(slave_dev); + struct bcm_sysport_priv *priv = netdev_priv(dev); struct bcm_sysport_tx_ring *ring; - struct bcm_sysport_priv *priv; - struct net_device *slave_dev; unsigned int num_tx_queues; - struct net_device *dev; unsigned int q, qp, port; - priv = container_of(nb, struct bcm_sysport_priv, dsa_notifier); - if (priv->netdev != info->master) - return 0; - - dev = info->master; - - if (dev->netdev_ops != &bcm_sysport_netdev_ops) - return 0; - - port = info->port_number; - slave_dev = info->info.dev; + port = dp->index; num_tx_queues = slave_dev->real_num_tx_queues; @@ -2417,17 +2396,30 @@ static int bcm_sysport_unmap_queues(struct notifier_block *nb, return 0; } -static int bcm_sysport_dsa_notifier(struct notifier_block *nb, - unsigned long event, void *ptr) +static int bcm_sysport_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr) { - int ret = NOTIFY_DONE; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info = ptr; + struct bcm_sysport_priv *priv; + int ret = 0; + + priv = container_of(nb, struct bcm_sysport_priv, netdev_notifier); + if (priv->netdev != dev) + return NOTIFY_DONE; switch (event) { - case DSA_PORT_REGISTER: - ret = bcm_sysport_map_queues(nb, ptr); - break; - case DSA_PORT_UNREGISTER: - ret = bcm_sysport_unmap_queues(nb, ptr); + case NETDEV_CHANGEUPPER: + if (dev->netdev_ops != &bcm_sysport_netdev_ops) + return NOTIFY_DONE; + + if (!dsa_slave_dev_check(info->upper_dev)) + return NOTIFY_DONE; + + if (info->linking) + ret = bcm_sysport_map_queues(dev, info->upper_dev); + else + ret = bcm_sysport_unmap_queues(dev, info->upper_dev); break; } @@ -2602,9 +2594,9 @@ static int bcm_sysport_probe(struct platform_device *pdev) priv->rx_max_coalesced_frames = 1; u64_stats_init(&priv->syncp); - priv->dsa_notifier.notifier_call = bcm_sysport_dsa_notifier; + priv->netdev_notifier.notifier_call = bcm_sysport_netdevice_event; - ret = register_dsa_notifier(&priv->dsa_notifier); + ret = register_netdevice_notifier(&priv->netdev_notifier); if (ret) { dev_err(&pdev->dev, "failed to register DSA notifier\n"); goto err_deregister_fixed_link; @@ -2631,7 +2623,7 @@ static int bcm_sysport_probe(struct platform_device *pdev) return 0; err_deregister_notifier: - unregister_dsa_notifier(&priv->dsa_notifier); + unregister_netdevice_notifier(&priv->netdev_notifier); err_deregister_fixed_link: if (of_phy_is_fixed_link(dn)) of_phy_deregister_fixed_link(dn); @@ -2649,7 +2641,7 @@ static int bcm_sysport_remove(struct platform_device *pdev) /* Not much to do, ndo_close has been called * and we use managed allocations */ - unregister_dsa_notifier(&priv->dsa_notifier); + unregister_netdevice_notifier(&priv->netdev_notifier); unregister_netdev(dev); if (of_phy_is_fixed_link(dn)) of_phy_deregister_fixed_link(dn); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index 3a5cb6f128f5..984f76e74b43 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -13,6 +13,8 @@ #include <linux/if_vlan.h> #include <linux/dim.h> +#include "unimac.h" + /* Receive/transmit descriptor format */ #define DESC_ADDR_HI_STATUS_LEN 0x00 #define DESC_ADDR_HI_SHIFT 0 @@ -213,39 +215,6 @@ struct bcm_rsb { /* UniMAC offset and defines */ #define SYS_PORT_UMAC_OFFSET 0x800 -#define UMAC_CMD 0x008 -#define CMD_TX_EN (1 << 0) -#define CMD_RX_EN (1 << 1) -#define CMD_SPEED_SHIFT 2 -#define CMD_SPEED_10 0 -#define CMD_SPEED_100 1 -#define CMD_SPEED_1000 2 -#define CMD_SPEED_2500 3 -#define CMD_SPEED_MASK 3 -#define CMD_PROMISC (1 << 4) -#define CMD_PAD_EN (1 << 5) -#define CMD_CRC_FWD (1 << 6) -#define CMD_PAUSE_FWD (1 << 7) -#define CMD_RX_PAUSE_IGNORE (1 << 8) -#define CMD_TX_ADDR_INS (1 << 9) -#define CMD_HD_EN (1 << 10) -#define CMD_SW_RESET (1 << 13) -#define CMD_LCL_LOOP_EN (1 << 15) -#define CMD_AUTO_CONFIG (1 << 22) -#define CMD_CNTL_FRM_EN (1 << 23) -#define CMD_NO_LEN_CHK (1 << 24) -#define CMD_RMT_LOOP_EN (1 << 25) -#define CMD_PRBL_EN (1 << 27) -#define CMD_TX_PAUSE_IGNORE (1 << 28) -#define CMD_TX_RX_EN (1 << 29) -#define CMD_RUNT_FILTER_DIS (1 << 30) - -#define UMAC_MAC0 0x00c -#define UMAC_MAC1 0x010 -#define UMAC_MAX_FRAME_LEN 0x014 - -#define UMAC_TX_FLUSH 0x334 - #define UMAC_MIB_START 0x400 /* There is a 0xC gap between the end of RX and beginning of TX stats and then @@ -787,7 +756,7 @@ struct bcm_sysport_priv { struct u64_stats_sync syncp; /* map information between switch port queues and local queues */ - struct notifier_block dsa_notifier; + struct notifier_block netdev_notifier; unsigned int per_port_num_tx_queues; struct bcm_sysport_tx_ring *ring_map[DSA_MAX_PORTS * 8]; diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 98ec1b8a7d8e..075f6e146b29 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -746,25 +746,25 @@ error: /* TODO: can we just drop @force? Can we don't reset MAC at all if there is * nothing to change? Try if after stabilizng driver. */ -static void bgmac_cmdcfg_maskset(struct bgmac *bgmac, u32 mask, u32 set, - bool force) +static void bgmac_umac_cmd_maskset(struct bgmac *bgmac, u32 mask, u32 set, + bool force) { - u32 cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); + u32 cmdcfg = bgmac_umac_read(bgmac, UMAC_CMD); u32 new_val = (cmdcfg & mask) | set; u32 cmdcfg_sr; if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4) - cmdcfg_sr = BGMAC_CMDCFG_SR_REV4; + cmdcfg_sr = CMD_SW_RESET; else - cmdcfg_sr = BGMAC_CMDCFG_SR_REV0; + cmdcfg_sr = CMD_SW_RESET_OLD; - bgmac_set(bgmac, BGMAC_CMDCFG, cmdcfg_sr); + bgmac_umac_maskset(bgmac, UMAC_CMD, ~0, cmdcfg_sr); udelay(2); if (new_val != cmdcfg || force) - bgmac_write(bgmac, BGMAC_CMDCFG, new_val); + bgmac_umac_write(bgmac, UMAC_CMD, new_val); - bgmac_mask(bgmac, BGMAC_CMDCFG, ~cmdcfg_sr); + bgmac_umac_maskset(bgmac, UMAC_CMD, ~cmdcfg_sr, 0); udelay(2); } @@ -773,9 +773,9 @@ static void bgmac_write_mac_address(struct bgmac *bgmac, u8 *addr) u32 tmp; tmp = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; - bgmac_write(bgmac, BGMAC_MACADDR_HIGH, tmp); + bgmac_umac_write(bgmac, UMAC_MAC0, tmp); tmp = (addr[4] << 8) | addr[5]; - bgmac_write(bgmac, BGMAC_MACADDR_LOW, tmp); + bgmac_umac_write(bgmac, UMAC_MAC1, tmp); } static void bgmac_set_rx_mode(struct net_device *net_dev) @@ -783,9 +783,9 @@ static void bgmac_set_rx_mode(struct net_device *net_dev) struct bgmac *bgmac = netdev_priv(net_dev); if (net_dev->flags & IFF_PROMISC) - bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_PROM, true); + bgmac_umac_cmd_maskset(bgmac, ~0, CMD_PROMISC, true); else - bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_PROM, 0, true); + bgmac_umac_cmd_maskset(bgmac, ~CMD_PROMISC, 0, true); } #if 0 /* We don't use that regs yet */ @@ -825,21 +825,21 @@ static void bgmac_clear_mib(struct bgmac *bgmac) /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_speed */ static void bgmac_mac_speed(struct bgmac *bgmac) { - u32 mask = ~(BGMAC_CMDCFG_ES_MASK | BGMAC_CMDCFG_HD); + u32 mask = ~(CMD_SPEED_MASK << CMD_SPEED_SHIFT | CMD_HD_EN); u32 set = 0; switch (bgmac->mac_speed) { case SPEED_10: - set |= BGMAC_CMDCFG_ES_10; + set |= CMD_SPEED_10 << CMD_SPEED_SHIFT; break; case SPEED_100: - set |= BGMAC_CMDCFG_ES_100; + set |= CMD_SPEED_100 << CMD_SPEED_SHIFT; break; case SPEED_1000: - set |= BGMAC_CMDCFG_ES_1000; + set |= CMD_SPEED_1000 << CMD_SPEED_SHIFT; break; case SPEED_2500: - set |= BGMAC_CMDCFG_ES_2500; + set |= CMD_SPEED_2500 << CMD_SPEED_SHIFT; break; default: dev_err(bgmac->dev, "Unsupported speed: %d\n", @@ -847,9 +847,9 @@ static void bgmac_mac_speed(struct bgmac *bgmac) } if (bgmac->mac_duplex == DUPLEX_HALF) - set |= BGMAC_CMDCFG_HD; + set |= CMD_HD_EN; - bgmac_cmdcfg_maskset(bgmac, mask, set, true); + bgmac_umac_cmd_maskset(bgmac, mask, set, true); } static void bgmac_miiconfig(struct bgmac *bgmac) @@ -917,7 +917,7 @@ static void bgmac_chip_reset(struct bgmac *bgmac) for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) bgmac_dma_tx_reset(bgmac, &bgmac->tx_ring[i]); - bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false); + bgmac_umac_cmd_maskset(bgmac, ~0, CMD_LCL_LOOP_EN, false); udelay(1); for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) @@ -986,34 +986,34 @@ static void bgmac_chip_reset(struct bgmac *bgmac) } /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_reset - * Specs don't say about using BGMAC_CMDCFG_SR, but in this routine - * BGMAC_CMDCFG is read _after_ putting chip in a reset. So it has to + * Specs don't say about using UMAC_CMD_SR, but in this routine + * UMAC_CMD is read _after_ putting chip in a reset. So it has to * be keps until taking MAC out of the reset. */ if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4) - cmdcfg_sr = BGMAC_CMDCFG_SR_REV4; + cmdcfg_sr = CMD_SW_RESET; else - cmdcfg_sr = BGMAC_CMDCFG_SR_REV0; - - bgmac_cmdcfg_maskset(bgmac, - ~(BGMAC_CMDCFG_TE | - BGMAC_CMDCFG_RE | - BGMAC_CMDCFG_RPI | - BGMAC_CMDCFG_TAI | - BGMAC_CMDCFG_HD | - BGMAC_CMDCFG_ML | - BGMAC_CMDCFG_CFE | - BGMAC_CMDCFG_RL | - BGMAC_CMDCFG_RED | - BGMAC_CMDCFG_PE | - BGMAC_CMDCFG_TPI | - BGMAC_CMDCFG_PAD_EN | - BGMAC_CMDCFG_PF), - BGMAC_CMDCFG_PROM | - BGMAC_CMDCFG_NLC | - BGMAC_CMDCFG_CFE | - cmdcfg_sr, - false); + cmdcfg_sr = CMD_SW_RESET_OLD; + + bgmac_umac_cmd_maskset(bgmac, + ~(CMD_TX_EN | + CMD_RX_EN | + CMD_RX_PAUSE_IGNORE | + CMD_TX_ADDR_INS | + CMD_HD_EN | + CMD_LCL_LOOP_EN | + CMD_CNTL_FRM_EN | + CMD_RMT_LOOP_EN | + CMD_RX_ERR_DISC | + CMD_PRBL_EN | + CMD_TX_PAUSE_IGNORE | + CMD_PAD_EN | + CMD_PAUSE_FWD), + CMD_PROMISC | + CMD_NO_LEN_CHK | + CMD_CNTL_FRM_EN | + cmdcfg_sr, + false); bgmac->mac_speed = SPEED_UNKNOWN; bgmac->mac_duplex = DUPLEX_UNKNOWN; @@ -1049,16 +1049,16 @@ static void bgmac_enable(struct bgmac *bgmac) u32 mode; if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4) - cmdcfg_sr = BGMAC_CMDCFG_SR_REV4; + cmdcfg_sr = CMD_SW_RESET; else - cmdcfg_sr = BGMAC_CMDCFG_SR_REV0; + cmdcfg_sr = CMD_SW_RESET_OLD; - cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); - bgmac_cmdcfg_maskset(bgmac, ~(BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE), - cmdcfg_sr, true); + cmdcfg = bgmac_umac_read(bgmac, UMAC_CMD); + bgmac_umac_cmd_maskset(bgmac, ~(CMD_TX_EN | CMD_RX_EN), + cmdcfg_sr, true); udelay(2); - cmdcfg |= BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE; - bgmac_write(bgmac, BGMAC_CMDCFG, cmdcfg); + cmdcfg |= CMD_TX_EN | CMD_RX_EN; + bgmac_umac_write(bgmac, UMAC_CMD, cmdcfg); mode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >> BGMAC_DS_MM_SHIFT; @@ -1078,7 +1078,7 @@ static void bgmac_enable(struct bgmac *bgmac) fl_ctl = 0x03cb04cb; bgmac_write(bgmac, BGMAC_FLOW_CTL_THRESH, fl_ctl); - bgmac_write(bgmac, BGMAC_PAUSE_CTL, 0x27fff); + bgmac_umac_write(bgmac, UMAC_PAUSE_CTRL, 0x27fff); } if (bgmac->feature_flags & BGMAC_FEAT_SET_RXQ_CLK) { @@ -1105,18 +1105,18 @@ static void bgmac_chip_init(struct bgmac *bgmac) bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT); /* Enable 802.3x tx flow control (honor received PAUSE frames) */ - bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_RPI, 0, true); + bgmac_umac_cmd_maskset(bgmac, ~CMD_RX_PAUSE_IGNORE, 0, true); bgmac_set_rx_mode(bgmac->net_dev); bgmac_write_mac_address(bgmac, bgmac->net_dev->dev_addr); if (bgmac->loopback) - bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false); + bgmac_umac_cmd_maskset(bgmac, ~0, CMD_LCL_LOOP_EN, false); else - bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_ML, 0, false); + bgmac_umac_cmd_maskset(bgmac, ~CMD_LCL_LOOP_EN, 0, false); - bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + ETHER_MAX_LEN); + bgmac_umac_write(bgmac, UMAC_MAX_FRAME_LEN, 32 + ETHER_MAX_LEN); bgmac_chip_intrs_on(bgmac); @@ -1252,7 +1252,7 @@ static int bgmac_change_mtu(struct net_device *net_dev, int mtu) { struct bgmac *bgmac = netdev_priv(net_dev); - bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + mtu); + bgmac_umac_write(bgmac, UMAC_MAX_FRAME_LEN, 32 + mtu); return 0; } diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index 351c598a3ec6..110088e662ea 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -4,6 +4,8 @@ #include <linux/netdevice.h> +#include "unimac.h" + #define BGMAC_DEV_CTL 0x000 #define BGMAC_DC_TSM 0x00000002 #define BGMAC_DC_CFCO 0x00000004 @@ -169,47 +171,7 @@ #define BGMAC_RX_NONPAUSE_PKTS 0x420 #define BGMAC_RX_SACHANGES 0x424 #define BGMAC_RX_UNI_PKTS 0x428 -#define BGMAC_UNIMAC_VERSION 0x800 -#define BGMAC_HDBKP_CTL 0x804 -#define BGMAC_CMDCFG 0x808 /* Configuration */ -#define BGMAC_CMDCFG_TE 0x00000001 /* Set to activate TX */ -#define BGMAC_CMDCFG_RE 0x00000002 /* Set to activate RX */ -#define BGMAC_CMDCFG_ES_MASK 0x0000000c /* Ethernet speed see gmac_speed */ -#define BGMAC_CMDCFG_ES_10 0x00000000 -#define BGMAC_CMDCFG_ES_100 0x00000004 -#define BGMAC_CMDCFG_ES_1000 0x00000008 -#define BGMAC_CMDCFG_ES_2500 0x0000000C -#define BGMAC_CMDCFG_PROM 0x00000010 /* Set to activate promiscuous mode */ -#define BGMAC_CMDCFG_PAD_EN 0x00000020 -#define BGMAC_CMDCFG_CF 0x00000040 -#define BGMAC_CMDCFG_PF 0x00000080 -#define BGMAC_CMDCFG_RPI 0x00000100 /* Unset to enable 802.3x tx flow control */ -#define BGMAC_CMDCFG_TAI 0x00000200 -#define BGMAC_CMDCFG_HD 0x00000400 /* Set if in half duplex mode */ -#define BGMAC_CMDCFG_HD_SHIFT 10 -#define BGMAC_CMDCFG_SR_REV0 0x00000800 /* Set to reset mode, for core rev 0-3 */ -#define BGMAC_CMDCFG_SR_REV4 0x00002000 /* Set to reset mode, for core rev >= 4 */ -#define BGMAC_CMDCFG_ML 0x00008000 /* Set to activate mac loopback mode */ -#define BGMAC_CMDCFG_AE 0x00400000 -#define BGMAC_CMDCFG_CFE 0x00800000 -#define BGMAC_CMDCFG_NLC 0x01000000 -#define BGMAC_CMDCFG_RL 0x02000000 -#define BGMAC_CMDCFG_RED 0x04000000 -#define BGMAC_CMDCFG_PE 0x08000000 -#define BGMAC_CMDCFG_TPI 0x10000000 -#define BGMAC_CMDCFG_AT 0x20000000 -#define BGMAC_MACADDR_HIGH 0x80c /* High 4 octets of own mac address */ -#define BGMAC_MACADDR_LOW 0x810 /* Low 2 octets of own mac address */ -#define BGMAC_RXMAX_LENGTH 0x814 /* Max receive frame length with vlan tag */ -#define BGMAC_PAUSEQUANTA 0x818 -#define BGMAC_MAC_MODE 0x844 -#define BGMAC_OUTERTAG 0x848 -#define BGMAC_INNERTAG 0x84c -#define BGMAC_TXIPG 0x85c -#define BGMAC_PAUSE_CTL 0xb30 -#define BGMAC_TX_FLUSH 0xb34 -#define BGMAC_RX_STATUS 0xb38 -#define BGMAC_TX_STATUS 0xb3c +#define BGMAC_UNIMAC 0x800 /* BCMA GMAC core specific IO Control (BCMA_IOCTL) flags */ #define BGMAC_BCMA_IOCTL_SW_CLKEN 0x00000004 /* PHY Clock Enable */ @@ -556,6 +518,16 @@ static inline void bgmac_write(struct bgmac *bgmac, u16 offset, u32 value) bgmac->write(bgmac, offset, value); } +static inline u32 bgmac_umac_read(struct bgmac *bgmac, u16 offset) +{ + return bgmac_read(bgmac, BGMAC_UNIMAC + offset); +} + +static inline void bgmac_umac_write(struct bgmac *bgmac, u16 offset, u32 value) +{ + bgmac_write(bgmac, BGMAC_UNIMAC + offset, value); +} + static inline u32 bgmac_idm_read(struct bgmac *bgmac, u16 offset) { return bgmac->idm_read(bgmac, offset); @@ -609,6 +581,11 @@ static inline void bgmac_set(struct bgmac *bgmac, u16 offset, u32 set) bgmac_maskset(bgmac, offset, ~0, set); } +static inline void bgmac_umac_maskset(struct bgmac *bgmac, u16 offset, u32 mask, u32 set) +{ + bgmac_maskset(bgmac, BGMAC_UNIMAC + offset, mask, set); +} + static inline int bgmac_phy_connect(struct bgmac *bgmac) { return bgmac->phy_connect(bgmac); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 28069b290862..b652ed72a621 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -13071,8 +13071,6 @@ static const struct net_device_ops bnx2x_netdev_ops = { .ndo_get_phys_port_id = bnx2x_get_phys_port_id, .ndo_set_vf_link_state = bnx2x_set_vf_link_state, .ndo_features_check = bnx2x_features_check, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, }; static int bnx2x_set_coherency_mask(struct bnx2x *bp) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index d10e4f85dd11..f508c5c61a30 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -255,6 +255,7 @@ static const u16 bnxt_async_events_arr[] = { ASYNC_EVENT_CMPL_EVENT_ID_PORT_PHY_CFG_CHANGE, ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY, ASYNC_EVENT_CMPL_EVENT_ID_ERROR_RECOVERY, + ASYNC_EVENT_CMPL_EVENT_ID_DEBUG_NOTIFICATION, ASYNC_EVENT_CMPL_EVENT_ID_RING_MONITOR_MSG, }; @@ -1265,8 +1266,7 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, } else { tpa_info->hash_type = PKT_HASH_TYPE_NONE; tpa_info->gso_type = 0; - if (netif_msg_rx_err(bp)) - netdev_warn(bp->dev, "TPA packet without valid hash\n"); + netif_warn(bp, rx_err, bp->dev, "TPA packet without valid hash\n"); } tpa_info->flags2 = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_flags2); tpa_info->metadata = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_metadata); @@ -2021,10 +2021,9 @@ static int bnxt_async_event_process(struct bnxt *bp, goto async_event_process_exit; set_bit(BNXT_RESET_TASK_SILENT_SP_EVENT, &bp->sp_event); break; - case ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY: - if (netif_msg_hw(bp)) - netdev_warn(bp->dev, "Received RESET_NOTIFY event, data1: 0x%x, data2: 0x%x\n", - data1, data2); + case ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY: { + char *fatal_str = "non-fatal"; + if (!bp->fw_health) goto async_event_process_exit; @@ -2036,14 +2035,17 @@ static int bnxt_async_event_process(struct bnxt *bp, if (!bp->fw_reset_max_dsecs) bp->fw_reset_max_dsecs = BNXT_DFLT_FW_RST_MAX_DSECS; if (EVENT_DATA1_RESET_NOTIFY_FATAL(data1)) { - netdev_warn(bp->dev, "Firmware fatal reset event received\n"); + fatal_str = "fatal"; set_bit(BNXT_STATE_FW_FATAL_COND, &bp->state); - } else { - netdev_warn(bp->dev, "Firmware non-fatal reset event received, max wait time %d msec\n", - bp->fw_reset_max_dsecs * 100); } + netif_warn(bp, hw, bp->dev, + "Firmware %s reset event, data1: 0x%x, data2: 0x%x, min wait %u ms, max wait %u ms\n", + fatal_str, data1, data2, + bp->fw_reset_min_dsecs * 100, + bp->fw_reset_max_dsecs * 100); set_bit(BNXT_FW_RESET_NOTIFY_SP_EVENT, &bp->sp_event); break; + } case ASYNC_EVENT_CMPL_EVENT_ID_ERROR_RECOVERY: { struct bnxt_fw_health *fw_health = bp->fw_health; @@ -2055,13 +2057,11 @@ static int bnxt_async_event_process(struct bnxt *bp, if (!fw_health->enabled) break; - if (netif_msg_drv(bp)) - netdev_info(bp->dev, "Error recovery info: error recovery[%d], master[%d], reset count[0x%x], health status: 0x%x\n", - fw_health->enabled, fw_health->master, - bnxt_fw_health_readl(bp, - BNXT_FW_RESET_CNT_REG), - bnxt_fw_health_readl(bp, - BNXT_FW_HEALTH_REG)); + netif_info(bp, drv, bp->dev, + "Error recovery info: error recovery[%d], master[%d], reset count[0x%x], health status: 0x%x\n", + fw_health->enabled, fw_health->master, + bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG), + bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG)); fw_health->tmr_multiplier = DIV_ROUND_UP(fw_health->polling_dsecs * HZ, bp->current_interval * 10); @@ -2072,6 +2072,11 @@ static int bnxt_async_event_process(struct bnxt *bp, bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG); goto async_event_process_exit; } + case ASYNC_EVENT_CMPL_EVENT_ID_DEBUG_NOTIFICATION: + netif_notice(bp, hw, bp->dev, + "Received firmware debug notification, data1: 0x%x, data2: 0x%x\n", + data1, data2); + goto async_event_process_exit; case ASYNC_EVENT_CMPL_EVENT_ID_RING_MONITOR_MSG: { struct bnxt_rx_ring_info *rxr; u16 grp_idx; @@ -2394,6 +2399,10 @@ static int bnxt_poll(struct napi_struct *napi, int budget) struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; int work_done = 0; + if (unlikely(test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))) { + napi_complete(napi); + return 0; + } while (1) { work_done += bnxt_poll_work(bp, cpr, budget - work_done); @@ -2468,6 +2477,10 @@ static int bnxt_poll_p5(struct napi_struct *napi, int budget) int work_done = 0; u32 cons; + if (unlikely(test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))) { + napi_complete(napi); + return 0; + } if (cpr->has_more_work) { cpr->has_more_work = 0; work_done = __bnxt_poll_cqs(bp, bnapi, budget); @@ -4272,6 +4285,9 @@ static void bnxt_disable_int_sync(struct bnxt *bp) { int i; + if (!bp->irq_tbl) + return; + atomic_inc(&bp->intr_sem); bnxt_disable_int(bp); @@ -4425,6 +4441,8 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len, if (!timeout) timeout = DFLT_HWRM_CMD_TIMEOUT; + /* Limit timeout to an upper limit */ + timeout = min(timeout, HWRM_CMD_MAX_TIMEOUT); /* convert timeout to usec */ timeout *= 1000; @@ -6845,6 +6863,7 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables) struct hwrm_func_backing_store_cfg_input req = {0}; struct bnxt_ctx_mem_info *ctx = bp->ctx; struct bnxt_ctx_pg_info *ctx_pg; + u32 req_len = sizeof(req); __le32 *num_entries; __le64 *pg_dir; u32 flags = 0; @@ -6855,6 +6874,8 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables) if (!ctx) return 0; + if (req_len > bp->hwrm_max_ext_req_len) + req_len = BNXT_BACKING_STORE_CFG_LEGACY_LEN; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_BACKING_STORE_CFG, -1, -1); req.enables = cpu_to_le32(enables); @@ -6938,7 +6959,7 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables) bnxt_hwrm_set_pg_attr(&ctx_pg->ring_mem, pg_attr, pg_dir); } req.flags = cpu_to_le32(flags); - return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + return hwrm_send_message(bp, &req, req_len, HWRM_CMD_TIMEOUT); } static int bnxt_alloc_ctx_mem_blk(struct bnxt *bp, @@ -7438,9 +7459,22 @@ static void bnxt_try_map_fw_health_reg(struct bnxt *bp) sig = readl(hs + offsetof(struct hcomm_status, sig_ver)); if ((sig & HCOMM_STATUS_SIGNATURE_MASK) != HCOMM_STATUS_SIGNATURE_VAL) { - if (bp->fw_health) - bp->fw_health->status_reliable = false; - return; + if (!bp->chip_num) { + __bnxt_map_fw_health_reg(bp, BNXT_GRC_REG_BASE); + bp->chip_num = readl(bp->bar0 + + BNXT_FW_HEALTH_WIN_BASE + + BNXT_GRC_REG_CHIP_NUM); + } + if (!BNXT_CHIP_P5(bp)) { + if (bp->fw_health) + bp->fw_health->status_reliable = false; + return; + } + status_loc = BNXT_GRC_REG_STATUS_P5 | + BNXT_FW_HEALTH_REG_TYPE_BAR0; + } else { + status_loc = readl(hs + offsetof(struct hcomm_status, + fw_status_loc)); } if (__bnxt_alloc_fw_health(bp)) { @@ -7448,7 +7482,6 @@ static void bnxt_try_map_fw_health_reg(struct bnxt *bp) return; } - status_loc = readl(hs + offsetof(struct hcomm_status, fw_status_loc)); bp->fw_health->regs[BNXT_FW_HEALTH_REG] = status_loc; reg_type = BNXT_FW_HEALTH_REG_TYPE(status_loc); if (reg_type == BNXT_FW_HEALTH_REG_TYPE_GRC) { @@ -8603,7 +8636,7 @@ msix_setup_exit: static int bnxt_init_inta(struct bnxt *bp) { - bp->irq_tbl = kcalloc(1, sizeof(struct bnxt_irq), GFP_KERNEL); + bp->irq_tbl = kzalloc(sizeof(struct bnxt_irq), GFP_KERNEL); if (!bp->irq_tbl) return -ENOMEM; @@ -8811,7 +8844,8 @@ static void bnxt_disable_napi(struct bnxt *bp) { int i; - if (!bp->bnapi) + if (!bp->bnapi || + test_and_set_bit(BNXT_STATE_NAPI_DISABLED, &bp->state)) return; for (i = 0; i < bp->cp_nr_rings; i++) { @@ -8828,6 +8862,7 @@ static void bnxt_enable_napi(struct bnxt *bp) { int i; + clear_bit(BNXT_STATE_NAPI_DISABLED, &bp->state); for (i = 0; i < bp->cp_nr_rings; i++) { struct bnxt_napi *bnapi = bp->bnapi[i]; struct bnxt_cp_ring_info *cpr; @@ -9334,13 +9369,60 @@ static int bnxt_hwrm_shutdown_link(struct bnxt *bp) static int bnxt_fw_init_one(struct bnxt *bp); +static int bnxt_fw_reset_via_optee(struct bnxt *bp) +{ +#ifdef CONFIG_TEE_BNXT_FW + int rc = tee_bnxt_fw_load(); + + if (rc) + netdev_err(bp->dev, "Failed FW reset via OP-TEE, rc=%d\n", rc); + + return rc; +#else + netdev_err(bp->dev, "OP-TEE not supported\n"); + return -ENODEV; +#endif +} + +static int bnxt_try_recover_fw(struct bnxt *bp) +{ + if (bp->fw_health && bp->fw_health->status_reliable) { + int retry = 0, rc; + u32 sts; + + mutex_lock(&bp->hwrm_cmd_lock); + do { + rc = __bnxt_hwrm_ver_get(bp, true); + sts = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG); + if (!sts || !BNXT_FW_IS_BOOTING(sts)) + break; + retry++; + } while (rc == -EBUSY && retry < BNXT_FW_RETRY); + mutex_unlock(&bp->hwrm_cmd_lock); + + if (!BNXT_FW_IS_HEALTHY(sts)) { + netdev_err(bp->dev, + "Firmware not responding, status: 0x%x\n", + sts); + rc = -ENODEV; + } + if (sts & FW_STATUS_REG_CRASHED_NO_MASTER) { + netdev_warn(bp->dev, "Firmware recover via OP-TEE requested\n"); + return bnxt_fw_reset_via_optee(bp); + } + return rc; + } + + return -ENODEV; +} + static int bnxt_hwrm_if_change(struct bnxt *bp, bool up) { struct hwrm_func_drv_if_change_output *resp = bp->hwrm_cmd_resp_addr; struct hwrm_func_drv_if_change_input req = {0}; bool resc_reinit = false, fw_reset = false; + int rc, retry = 0; u32 flags = 0; - int rc; if (!(bp->fw_cap & BNXT_FW_CAP_IF_CHANGE)) return 0; @@ -9349,10 +9431,25 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up) if (up) req.flags = cpu_to_le32(FUNC_DRV_IF_CHANGE_REQ_FLAGS_UP); mutex_lock(&bp->hwrm_cmd_lock); - rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + while (retry < BNXT_FW_IF_RETRY) { + rc = _hwrm_send_message(bp, &req, sizeof(req), + HWRM_CMD_TIMEOUT); + if (rc != -EAGAIN) + break; + + msleep(50); + retry++; + } if (!rc) flags = le32_to_cpu(resp->flags); mutex_unlock(&bp->hwrm_cmd_lock); + + if (rc == -EAGAIN) + return rc; + if (rc && up) { + rc = bnxt_try_recover_fw(bp); + fw_reset = true; + } if (rc) return rc; @@ -9692,6 +9789,25 @@ static void bnxt_preset_reg_win(struct bnxt *bp) static int bnxt_init_dflt_ring_mode(struct bnxt *bp); +static int bnxt_reinit_after_abort(struct bnxt *bp) +{ + int rc; + + if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) + return -EBUSY; + + rc = bnxt_fw_init_one(bp); + if (!rc) { + bnxt_clear_int_mode(bp); + rc = bnxt_init_int_mode(bp); + if (!rc) { + clear_bit(BNXT_STATE_ABORT_ERR, &bp->state); + set_bit(BNXT_STATE_FW_RESET_DET, &bp->state); + } + } + return rc; +} + static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) { int rc = 0; @@ -9850,8 +9966,14 @@ static int bnxt_open(struct net_device *dev) int rc; if (test_bit(BNXT_STATE_ABORT_ERR, &bp->state)) { - netdev_err(bp->dev, "A previous firmware reset did not complete, aborting\n"); - return -ENODEV; + rc = bnxt_reinit_after_abort(bp); + if (rc) { + if (rc == -EBUSY) + netdev_err(bp->dev, "A previous firmware reset has not completed, aborting\n"); + else + netdev_err(bp->dev, "Failed to reinitialize after aborted firmware reset\n"); + return -ENODEV; + } } rc = bnxt_hwrm_if_change(bp, true); @@ -10788,11 +10910,18 @@ static void bnxt_rx_ring_reset(struct bnxt *bp) static void bnxt_fw_reset_close(struct bnxt *bp) { bnxt_ulp_stop(bp); - /* When firmware is fatal state, disable PCI device to prevent - * any potential bad DMAs before freeing kernel memory. + /* When firmware is in fatal state, quiesce device and disable + * bus master to prevent any potential bad DMAs before freeing + * kernel memory. */ - if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state)) + if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state)) { + bnxt_tx_disable(bp); + bnxt_disable_napi(bp); + bnxt_disable_int_sync(bp); + bnxt_free_irq(bp); + bnxt_clear_int_mode(bp); pci_disable_device(bp->pdev); + } __bnxt_close_nic(bp, true, false); bnxt_clear_int_mode(bp); bnxt_hwrm_func_drv_unrgtr(bp); @@ -11180,21 +11309,6 @@ static void bnxt_init_dflt_coal(struct bnxt *bp) bp->stats_coal_ticks = BNXT_DEF_STATS_COAL_TICKS; } -static int bnxt_fw_reset_via_optee(struct bnxt *bp) -{ -#ifdef CONFIG_TEE_BNXT_FW - int rc = tee_bnxt_fw_load(); - - if (rc) - netdev_err(bp->dev, "Failed FW reset via OP-TEE, rc=%d\n", rc); - - return rc; -#else - netdev_err(bp->dev, "OP-TEE not supported\n"); - return -ENODEV; -#endif -} - static int bnxt_fw_init_one_p1(struct bnxt *bp) { int rc; @@ -11203,19 +11317,10 @@ static int bnxt_fw_init_one_p1(struct bnxt *bp) rc = bnxt_hwrm_ver_get(bp); bnxt_try_map_fw_health_reg(bp); if (rc) { - if (bp->fw_health && bp->fw_health->status_reliable) { - u32 sts = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG); - - netdev_err(bp->dev, - "Firmware not responding, status: 0x%x\n", - sts); - if (sts & FW_STATUS_REG_CRASHED_NO_MASTER) { - netdev_warn(bp->dev, "Firmware recover via OP-TEE requested\n"); - rc = bnxt_fw_reset_via_optee(bp); - if (!rc) - rc = bnxt_hwrm_ver_get(bp); - } - } + rc = bnxt_try_recover_fw(bp); + if (rc) + return rc; + rc = bnxt_hwrm_ver_get(bp); if (rc) return rc; } @@ -11415,6 +11520,12 @@ static void bnxt_reset_all(struct bnxt *bp) bp->fw_reset_timestamp = jiffies; } +static bool bnxt_fw_reset_timeout(struct bnxt *bp) +{ + return time_after(jiffies, bp->fw_reset_timestamp + + (bp->fw_reset_max_dsecs * HZ / 10)); +} + static void bnxt_fw_reset_task(struct work_struct *work) { struct bnxt *bp = container_of(work, struct bnxt, fw_reset_task.work); @@ -11436,8 +11547,7 @@ static void bnxt_fw_reset_task(struct work_struct *work) bp->fw_reset_timestamp)); goto fw_reset_abort; } else if (n > 0) { - if (time_after(jiffies, bp->fw_reset_timestamp + - (bp->fw_reset_max_dsecs * HZ / 10))) { + if (bnxt_fw_reset_timeout(bp)) { clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state); bp->fw_reset_state = 0; netdev_err(bp->dev, "Firmware reset aborted, bnxt_get_registered_vfs() returns %d\n", @@ -11466,8 +11576,7 @@ static void bnxt_fw_reset_task(struct work_struct *work) val = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG); if (!(val & BNXT_FW_STATUS_SHUTDOWN) && - !time_after(jiffies, bp->fw_reset_timestamp + - (bp->fw_reset_max_dsecs * HZ / 10))) { + !bnxt_fw_reset_timeout(bp)) { bnxt_queue_fw_reset_work(bp, HZ / 5); return; } @@ -11509,8 +11618,7 @@ static void bnxt_fw_reset_task(struct work_struct *work) bp->hwrm_cmd_timeout = SHORT_HWRM_CMD_TIMEOUT; rc = __bnxt_hwrm_ver_get(bp, true); if (rc) { - if (time_after(jiffies, bp->fw_reset_timestamp + - (bp->fw_reset_max_dsecs * HZ / 10))) { + if (bnxt_fw_reset_timeout(bp)) { netdev_err(bp->dev, "Firmware reset aborted\n"); goto fw_reset_abort_status; } @@ -12091,8 +12199,6 @@ static const struct net_device_ops bnxt_netdev_ops = { #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = bnxt_rx_flow_steer, #endif - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_bpf = bnxt_xdp, .ndo_xdp_xmit = bnxt_xdp_xmit, .ndo_bridge_getlink = bnxt_bridge_getlink, @@ -12544,9 +12650,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->ethtool_ops = &bnxt_ethtool_ops; pci_set_drvdata(pdev, dev); - if (BNXT_PF(bp)) - bnxt_vpd_read_info(bp); - rc = bnxt_alloc_hwrm_resources(bp); if (rc) goto init_err_pci_clean; @@ -12558,6 +12661,9 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_pci_clean; + if (BNXT_PF(bp)) + bnxt_vpd_read_info(bp); + if (BNXT_CHIP_P5(bp)) { bp->flags |= BNXT_FLAG_CHIP_P5; if (BNXT_CHIP_SR2(bp)) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 51996c85547e..4ef6888acdc6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -656,6 +656,7 @@ struct nqe_cn { #define BNXT_HWRM_MAX_REQ_LEN (bp->hwrm_max_req_len) #define BNXT_HWRM_SHORT_REQ_LEN sizeof(struct hwrm_short_input) #define DFLT_HWRM_CMD_TIMEOUT 500 +#define HWRM_CMD_MAX_TIMEOUT 40000 #define SHORT_HWRM_CMD_TIMEOUT 20 #define HWRM_CMD_TIMEOUT (bp->hwrm_cmd_timeout) #define HWRM_RESET_TIMEOUT ((HWRM_CMD_TIMEOUT) * 4) @@ -1345,9 +1346,14 @@ struct bnxt_test_info { #define BNXT_CAG_REG_LEGACY_INT_STATUS 0x4014 #define BNXT_CAG_REG_BASE 0x300000 +#define BNXT_GRC_REG_STATUS_P5 0x520 + #define BNXT_GRCPF_REG_KONG_COMM 0xA00 #define BNXT_GRCPF_REG_KONG_COMM_TRIGGER 0xB00 +#define BNXT_GRC_REG_CHIP_NUM 0x48 +#define BNXT_GRC_REG_BASE 0x260000 + #define BNXT_GRC_BASE_MASK 0xfffff000 #define BNXT_GRC_OFFSET_MASK 0x00000ffc @@ -1441,6 +1447,8 @@ struct bnxt_ctx_pg_info { #define BNXT_MAX_TQM_RINGS \ (BNXT_MAX_TQM_SP_RINGS + BNXT_MAX_TQM_FP_RINGS) +#define BNXT_BACKING_STORE_CFG_LEGACY_LEN 256 + struct bnxt_ctx_mem_info { u32 qp_max_entries; u16 qp_min_qp1_entries; @@ -1532,9 +1540,22 @@ struct bnxt_fw_reporter_ctx { #define BNXT_FW_HEALTH_WIN_OFF(reg) (BNXT_FW_HEALTH_WIN_BASE + \ ((reg) & BNXT_GRC_OFFSET_MASK)) +#define BNXT_FW_STATUS_HEALTH_MSK 0xffff #define BNXT_FW_STATUS_HEALTHY 0x8000 #define BNXT_FW_STATUS_SHUTDOWN 0x100000 +#define BNXT_FW_IS_HEALTHY(sts) (((sts) & BNXT_FW_STATUS_HEALTH_MSK) ==\ + BNXT_FW_STATUS_HEALTHY) + +#define BNXT_FW_IS_BOOTING(sts) (((sts) & BNXT_FW_STATUS_HEALTH_MSK) < \ + BNXT_FW_STATUS_HEALTHY) + +#define BNXT_FW_IS_ERR(sts) (((sts) & BNXT_FW_STATUS_HEALTH_MSK) > \ + BNXT_FW_STATUS_HEALTHY) + +#define BNXT_FW_RETRY 5 +#define BNXT_FW_IF_RETRY 10 + struct bnxt { void __iomem *bar0; void __iomem *bar1; @@ -1788,6 +1809,7 @@ struct bnxt { #define BNXT_STATE_FW_FATAL_COND 6 #define BNXT_STATE_DRV_REGISTERED 7 #define BNXT_STATE_PCI_CHANNEL_IO_FROZEN 8 +#define BNXT_STATE_NAPI_DISABLED 9 #define BNXT_NO_FW_ACCESS(bp) \ (test_bit(BNXT_STATE_FW_FATAL_COND, &(bp)->state) || \ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 6b7b69ed62db..90a31b4a3020 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -44,21 +44,20 @@ static int bnxt_fw_reporter_diagnose(struct devlink_health_reporter *reporter, struct netlink_ext_ack *extack) { struct bnxt *bp = devlink_health_reporter_priv(reporter); - u32 val, health_status; + u32 val; int rc; if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) return 0; val = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG); - health_status = val & 0xffff; - if (health_status < BNXT_FW_STATUS_HEALTHY) { + if (BNXT_FW_IS_BOOTING(val)) { rc = devlink_fmsg_string_pair_put(fmsg, "Description", "Not yet completed initialization"); if (rc) return rc; - } else if (health_status > BNXT_FW_STATUS_HEALTHY) { + } else if (BNXT_FW_IS_ERR(val)) { rc = devlink_fmsg_string_pair_put(fmsg, "Description", "Encountered fatal error and cannot recover"); if (rc) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 2d3e962bdac3..d5c6e6a3d22d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -2,7 +2,7 @@ * * Copyright (c) 2014-2016 Broadcom Corporation * Copyright (c) 2014-2018 Broadcom Limited - * Copyright (c) 2018-2020 Broadcom Inc. + * Copyright (c) 2018-2021 Broadcom Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -164,6 +164,7 @@ struct cmd_nums { #define HWRM_VNIC_PLCMODES_CFG 0x48UL #define HWRM_VNIC_PLCMODES_QCFG 0x49UL #define HWRM_VNIC_QCAPS 0x4aUL + #define HWRM_VNIC_UPDATE 0x4bUL #define HWRM_RING_ALLOC 0x50UL #define HWRM_RING_FREE 0x51UL #define HWRM_RING_CMPL_RING_QAGGINT_PARAMS 0x52UL @@ -184,6 +185,9 @@ struct cmd_nums { #define HWRM_QUEUE_MPLS_QCAPS 0x80UL #define HWRM_QUEUE_MPLSTC2PRI_QCFG 0x81UL #define HWRM_QUEUE_MPLSTC2PRI_CFG 0x82UL + #define HWRM_QUEUE_VLANPRI_QCAPS 0x83UL + #define HWRM_QUEUE_VLANPRI2PRI_QCFG 0x84UL + #define HWRM_QUEUE_VLANPRI2PRI_CFG 0x85UL #define HWRM_CFA_L2_FILTER_ALLOC 0x90UL #define HWRM_CFA_L2_FILTER_FREE 0x91UL #define HWRM_CFA_L2_FILTER_CFG 0x92UL @@ -217,6 +221,8 @@ struct cmd_nums { #define HWRM_PORT_TX_FIR_CFG 0xbbUL #define HWRM_PORT_TX_FIR_QCFG 0xbcUL #define HWRM_PORT_ECN_QSTATS 0xbdUL + #define HWRM_FW_LIVEPATCH_QUERY 0xbeUL + #define HWRM_FW_LIVEPATCH 0xbfUL #define HWRM_FW_RESET 0xc0UL #define HWRM_FW_QSTATUS 0xc1UL #define HWRM_FW_HEALTH_CHECK 0xc2UL @@ -347,6 +353,8 @@ struct cmd_nums { #define HWRM_FUNC_HOST_PF_IDS_QUERY 0x197UL #define HWRM_FUNC_QSTATS_EXT 0x198UL #define HWRM_STAT_EXT_CTX_QUERY 0x199UL + #define HWRM_FUNC_SPD_CFG 0x19aUL + #define HWRM_FUNC_SPD_QCFG 0x19bUL #define HWRM_SELFTEST_QLIST 0x200UL #define HWRM_SELFTEST_EXEC 0x201UL #define HWRM_SELFTEST_IRQ 0x202UL @@ -359,6 +367,11 @@ struct cmd_nums { #define HWRM_MFG_HDMA_TEST 0x209UL #define HWRM_MFG_FRU_EEPROM_WRITE 0x20aUL #define HWRM_MFG_FRU_EEPROM_READ 0x20bUL + #define HWRM_MFG_SOC_IMAGE 0x20cUL + #define HWRM_MFG_SOC_QSTATUS 0x20dUL + #define HWRM_MFG_PARAM_SEEPROM_SYNC 0x20eUL + #define HWRM_MFG_PARAM_SEEPROM_READ 0x20fUL + #define HWRM_MFG_PARAM_SEEPROM_HEALTH 0x210UL #define HWRM_TF 0x2bcUL #define HWRM_TF_VERSION_GET 0x2bdUL #define HWRM_TF_SESSION_OPEN 0x2c6UL @@ -384,6 +397,7 @@ struct cmd_nums { #define HWRM_TF_EXT_EM_QCFG 0x2e9UL #define HWRM_TF_EM_INSERT 0x2eaUL #define HWRM_TF_EM_DELETE 0x2ebUL + #define HWRM_TF_EM_HASH_INSERT 0x2ecUL #define HWRM_TF_TCAM_SET 0x2f8UL #define HWRM_TF_TCAM_GET 0x2f9UL #define HWRM_TF_TCAM_MOVE 0x2faUL @@ -486,9 +500,9 @@ struct hwrm_err_output { #define HWRM_TARGET_ID_TOOLS 0xFFFD #define HWRM_VERSION_MAJOR 1 #define HWRM_VERSION_MINOR 10 -#define HWRM_VERSION_UPDATE 1 -#define HWRM_VERSION_RSVD 68 -#define HWRM_VERSION_STR "1.10.1.68" +#define HWRM_VERSION_UPDATE 2 +#define HWRM_VERSION_RSVD 11 +#define HWRM_VERSION_STR "1.10.2.11" /* hwrm_ver_get_input (size:192b/24B) */ struct hwrm_ver_get_input { @@ -563,8 +577,9 @@ struct hwrm_ver_get_output { __le16 max_resp_len; __le16 def_req_timeout; u8 flags; - #define VER_GET_RESP_FLAGS_DEV_NOT_RDY 0x1UL - #define VER_GET_RESP_FLAGS_EXT_VER_AVAIL 0x2UL + #define VER_GET_RESP_FLAGS_DEV_NOT_RDY 0x1UL + #define VER_GET_RESP_FLAGS_EXT_VER_AVAIL 0x2UL + #define VER_GET_RESP_FLAGS_DEV_NOT_RDY_BACKING_STORE 0x4UL u8 unused_0[2]; u8 always_1; __le16 hwrm_intf_major; @@ -708,6 +723,7 @@ struct hwrm_async_event_cmpl { #define ASYNC_EVENT_CMPL_EVENT_ID_QUIESCE_DONE 0x3fUL #define ASYNC_EVENT_CMPL_EVENT_ID_DEFERRED_RESPONSE 0x40UL #define ASYNC_EVENT_CMPL_EVENT_ID_PFC_WATCHDOG_CFG_CHANGE 0x41UL + #define ASYNC_EVENT_CMPL_EVENT_ID_MAX_RGTR_EVENT_ID 0x42UL #define ASYNC_EVENT_CMPL_EVENT_ID_FW_TRACE_MSG 0xfeUL #define ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR 0xffUL #define ASYNC_EVENT_CMPL_EVENT_ID_LAST ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR @@ -815,6 +831,8 @@ struct hwrm_async_event_cmpl_reset_notify { #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_RESET_NOTIFY 0x8UL #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_LAST ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_RESET_NOTIFY __le32 event_data2; + #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA2_FW_STATUS_CODE_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA2_FW_STATUS_CODE_SFT 0 u8 opaque_v; #define ASYNC_EVENT_CMPL_RESET_NOTIFY_V 0x1UL #define ASYNC_EVENT_CMPL_RESET_NOTIFY_OPAQUE_MASK 0xfeUL @@ -832,7 +850,8 @@ struct hwrm_async_event_cmpl_reset_notify { #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_MANAGEMENT_RESET_REQUEST (0x1UL << 8) #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_FATAL (0x2UL << 8) #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_NON_FATAL (0x3UL << 8) - #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_LAST ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_NON_FATAL + #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FAST_RESET (0x4UL << 8) + #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_LAST ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FAST_RESET #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DELAY_IN_100MS_TICKS_MASK 0xffff0000UL #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DELAY_IN_100MS_TICKS_SFT 16 }; @@ -1271,6 +1290,10 @@ struct hwrm_func_qcaps_output { #define FUNC_QCAPS_RESP_FLAGS_EXT_TX_PROXY_SRC_INTF_OVERRIDE_SUPPORT 0x20UL #define FUNC_QCAPS_RESP_FLAGS_EXT_SCHQ_SUPPORTED 0x40UL #define FUNC_QCAPS_RESP_FLAGS_EXT_PPP_PUSH_MODE_SUPPORTED 0x80UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_EVB_MODE_CFG_NOT_SUPPORTED 0x100UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_SOC_SPD_SUPPORTED 0x200UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_FW_LIVEPATCH_SUPPORTED 0x400UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_FAST_RESET_CAPABLE 0x800UL u8 max_schqs; u8 mpc_chnls_cap; #define FUNC_QCAPS_RESP_MPC_CHNLS_CAP_TCE 0x1UL @@ -1315,6 +1338,7 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_FLAGS_HOT_RESET_ALLOWED 0x200UL #define FUNC_QCFG_RESP_FLAGS_PPP_PUSH_MODE_ENABLED 0x400UL #define FUNC_QCFG_RESP_FLAGS_RING_MONITOR_ENABLED 0x800UL + #define FUNC_QCFG_RESP_FLAGS_FAST_RESET_ALLOWED 0x1000UL u8 mac_address[6]; __le16 pci_id; __le16 alloc_rsscos_ctx; @@ -1731,6 +1755,7 @@ struct hwrm_func_drv_rgtr_input { #define FUNC_DRV_RGTR_REQ_FLAGS_HOT_RESET_SUPPORT 0x10UL #define FUNC_DRV_RGTR_REQ_FLAGS_ERROR_RECOVERY_SUPPORT 0x20UL #define FUNC_DRV_RGTR_REQ_FLAGS_MASTER_SUPPORT 0x40UL + #define FUNC_DRV_RGTR_REQ_FLAGS_FAST_RESET_SUPPORT 0x80UL __le32 enables; #define FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE 0x1UL #define FUNC_DRV_RGTR_REQ_ENABLES_VER 0x2UL @@ -1993,7 +2018,7 @@ struct hwrm_func_backing_store_qcaps_input { __le64 resp_addr; }; -/* hwrm_func_backing_store_qcaps_output (size:640b/80B) */ +/* hwrm_func_backing_store_qcaps_output (size:704b/88B) */ struct hwrm_func_backing_store_qcaps_output { __le16 error_code; __le16 req_type; @@ -2024,13 +2049,25 @@ struct hwrm_func_backing_store_qcaps_output { __le16 mrav_num_entries_units; u8 tqm_entries_multiple; u8 ctx_kind_initializer; - __le32 rsvd; - __le16 rsvd1; + __le16 ctx_init_mask; + #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_QP 0x1UL + #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_SRQ 0x2UL + #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_CQ 0x4UL + #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_VNIC 0x8UL + #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_STAT 0x10UL + #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_MRAV 0x20UL + u8 qp_init_offset; + u8 srq_init_offset; + u8 cq_init_offset; + u8 vnic_init_offset; u8 tqm_fp_rings_count; + u8 stat_init_offset; + u8 mrav_init_offset; + u8 rsvd[6]; u8 valid; }; -/* hwrm_func_backing_store_cfg_input (size:2048b/256B) */ +/* hwrm_func_backing_store_cfg_input (size:2432b/304B) */ struct hwrm_func_backing_store_cfg_input { __le16 req_type; __le16 cmpl_ring; @@ -2041,22 +2078,25 @@ struct hwrm_func_backing_store_cfg_input { #define FUNC_BACKING_STORE_CFG_REQ_FLAGS_PREBOOT_MODE 0x1UL #define FUNC_BACKING_STORE_CFG_REQ_FLAGS_MRAV_RESERVATION_SPLIT 0x2UL __le32 enables; - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP 0x1UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_SRQ 0x2UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_CQ 0x4UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_VNIC 0x8UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_STAT 0x10UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP 0x20UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING0 0x40UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING1 0x80UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING2 0x100UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING3 0x200UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING4 0x400UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING5 0x800UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING6 0x1000UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING7 0x2000UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV 0x4000UL - #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM 0x8000UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP 0x1UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_SRQ 0x2UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_CQ 0x4UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_VNIC 0x8UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_STAT 0x10UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP 0x20UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING0 0x40UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING1 0x80UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING2 0x100UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING3 0x200UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING4 0x400UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING5 0x800UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING6 0x1000UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING7 0x2000UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV 0x4000UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM 0x8000UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING8 0x10000UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING9 0x20000UL + #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING10 0x40000UL u8 qpc_pg_size_qpc_lvl; #define FUNC_BACKING_STORE_CFG_REQ_QPC_LVL_MASK 0xfUL #define FUNC_BACKING_STORE_CFG_REQ_QPC_LVL_SFT 0 @@ -2358,6 +2398,63 @@ struct hwrm_func_backing_store_cfg_input { __le16 tqm_entry_size; __le16 mrav_entry_size; __le16 tim_entry_size; + u8 tqm_ring8_pg_size_tqm_ring_lvl; + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_MASK 0xfUL + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_SFT 0 + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_0 0x0UL + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_1 0x1UL + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_2 0x2UL + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LAST FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_2 + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_MASK 0xf0UL + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_SFT 4 + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_4K (0x0UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_8K (0x1UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_64K (0x2UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_2M (0x3UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_8M (0x4UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_1G (0x5UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_LAST FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_1G + u8 ring8_unused[3]; + __le32 tqm_ring8_num_entries; + __le64 tqm_ring8_page_dir; + u8 tqm_ring9_pg_size_tqm_ring_lvl; + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_MASK 0xfUL + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_SFT 0 + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_0 0x0UL + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_1 0x1UL + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_2 0x2UL + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LAST FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_2 + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_MASK 0xf0UL + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_SFT 4 + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_4K (0x0UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_8K (0x1UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_64K (0x2UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_2M (0x3UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_8M (0x4UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_1G (0x5UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_LAST FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_1G + u8 ring9_unused[3]; + __le32 tqm_ring9_num_entries; + __le64 tqm_ring9_page_dir; + u8 tqm_ring10_pg_size_tqm_ring_lvl; + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_MASK 0xfUL + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_SFT 0 + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_0 0x0UL + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_1 0x1UL + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_2 0x2UL + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LAST FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_2 + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_MASK 0xf0UL + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_SFT 4 + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_4K (0x0UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_8K (0x1UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_64K (0x2UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_2M (0x3UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_8M (0x4UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_1G (0x5UL << 4) + #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_LAST FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_1G + u8 ring10_unused[3]; + __le32 tqm_ring10_num_entries; + __le64 tqm_ring10_page_dir; }; /* hwrm_func_backing_store_cfg_output (size:128b/16B) */ @@ -2930,6 +3027,7 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_DUPLEX_STATE_LAST PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL u8 option_flags; #define PORT_PHY_QCFG_RESP_OPTION_FLAGS_MEDIA_AUTO_DETECT 0x1UL + #define PORT_PHY_QCFG_RESP_OPTION_FLAGS_SIGNAL_MODE_KNOWN 0x2UL char phy_vendor_name[16]; char phy_vendor_partnumber[16]; __le16 support_pam4_speeds; @@ -3528,8 +3626,8 @@ struct hwrm_port_phy_qcaps_output { #define PORT_PHY_QCAPS_RESP_FLAGS_SHARED_PHY_CFG_SUPPORTED 0x8UL #define PORT_PHY_QCAPS_RESP_FLAGS_CUMULATIVE_COUNTERS_ON_RESET 0x10UL #define PORT_PHY_QCAPS_RESP_FLAGS_LOCAL_LPBK_NOT_SUPPORTED 0x20UL - #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_MASK 0xc0UL - #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_SFT 6 + #define PORT_PHY_QCAPS_RESP_FLAGS_FW_MANAGED_LINK_DOWN 0x40UL + #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1 0x80UL u8 port_cnt; #define PORT_PHY_QCAPS_RESP_PORT_CNT_UNKNOWN 0x0UL #define PORT_PHY_QCAPS_RESP_PORT_CNT_1 0x1UL @@ -4119,7 +4217,10 @@ struct hwrm_queue_qportcfg_output { #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN 0xffUL #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN - u8 unused_0; + u8 queue_id0_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_TYPE_CNP 0x4UL char qid0_name[16]; char qid1_name[16]; char qid2_name[16]; @@ -4128,7 +4229,34 @@ struct hwrm_queue_qportcfg_output { char qid5_name[16]; char qid6_name[16]; char qid7_name[16]; - u8 unused_1[7]; + u8 queue_id1_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_TYPE_CNP 0x4UL + u8 queue_id2_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_TYPE_CNP 0x4UL + u8 queue_id3_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_TYPE_CNP 0x4UL + u8 queue_id4_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_TYPE_CNP 0x4UL + u8 queue_id5_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_TYPE_CNP 0x4UL + u8 queue_id6_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_TYPE_CNP 0x4UL + u8 queue_id7_service_profile_type; + #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_TYPE_ROCE 0x1UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_TYPE_NIC 0x2UL + #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_TYPE_CNP 0x4UL u8 valid; }; @@ -5142,8 +5270,10 @@ struct hwrm_vnic_alloc_input { __le16 target_id; __le64 resp_addr; __le32 flags; - #define VNIC_ALLOC_REQ_FLAGS_DEFAULT 0x1UL - u8 unused_0[4]; + #define VNIC_ALLOC_REQ_FLAGS_DEFAULT 0x1UL + #define VNIC_ALLOC_REQ_FLAGS_VIRTIO_NET_FID_VALID 0x2UL + __le16 virtio_net_fid; + u8 unused_0[2]; }; /* hwrm_vnic_alloc_output (size:128b/16B) */ @@ -5260,6 +5390,8 @@ struct hwrm_vnic_qcaps_output { #define VNIC_QCAPS_RESP_FLAGS_OUTERMOST_RSS_CAP 0x80UL #define VNIC_QCAPS_RESP_FLAGS_COS_ASSIGNMENT_CAP 0x100UL #define VNIC_QCAPS_RESP_FLAGS_RX_CMPL_V2_CAP 0x200UL + #define VNIC_QCAPS_RESP_FLAGS_VNIC_STATE_CAP 0x400UL + #define VNIC_QCAPS_RESP_FLAGS_VIRTIO_NET_VNIC_ALLOC_CAP 0x800UL __le16 max_aggs_supported; u8 unused_1[5]; u8 valid; @@ -5585,7 +5717,11 @@ struct hwrm_ring_alloc_output { __le16 resp_len; __le16 ring_id; __le16 logical_ring_id; - u8 unused_0[3]; + u8 push_buffer_index; + #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL + #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL + #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_LAST RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER + u8 unused_0[2]; u8 valid; }; @@ -5644,7 +5780,11 @@ struct hwrm_ring_reset_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - u8 unused_0[4]; + u8 push_buffer_index; + #define RING_RESET_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL + #define RING_RESET_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL + #define RING_RESET_RESP_PUSH_BUFFER_INDEX_LAST RING_RESET_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER + u8 unused_0[3]; u8 consumer_idx[3]; u8 valid; }; @@ -6988,21 +7128,23 @@ struct hwrm_cfa_adv_flow_mgnt_qcaps_output { __le16 seq_id; __le16 resp_len; __le32 flags; - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_16BIT_SUPPORTED 0x1UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_64BIT_SUPPORTED 0x2UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_BATCH_DELETE_SUPPORTED 0x4UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_RESET_ALL_SUPPORTED 0x8UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_DEST_FUNC_SUPPORTED 0x10UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TX_EEM_FLOW_SUPPORTED 0x20UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RX_EEM_FLOW_SUPPORTED 0x40UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_COUNTER_ALLOC_SUPPORTED 0x80UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_SUPPORTED 0x100UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_UNTAGGED_VLAN_SUPPORTED 0x200UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_XDP_SUPPORTED 0x400UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_HEADER_SOURCE_FIELDS_SUPPORTED 0x800UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ARP_SUPPORTED 0x1000UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V2_SUPPORTED 0x2000UL - #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ETHERTYPE_IP_SUPPORTED 0x4000UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_16BIT_SUPPORTED 0x1UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_64BIT_SUPPORTED 0x2UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_BATCH_DELETE_SUPPORTED 0x4UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_RESET_ALL_SUPPORTED 0x8UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_DEST_FUNC_SUPPORTED 0x10UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TX_EEM_FLOW_SUPPORTED 0x20UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RX_EEM_FLOW_SUPPORTED 0x40UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_COUNTER_ALLOC_SUPPORTED 0x80UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_SUPPORTED 0x100UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_UNTAGGED_VLAN_SUPPORTED 0x200UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_XDP_SUPPORTED 0x400UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_HEADER_SOURCE_FIELDS_SUPPORTED 0x800UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ARP_SUPPORTED 0x1000UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V2_SUPPORTED 0x2000UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ETHERTYPE_IP_SUPPORTED 0x4000UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TRUFLOW_CAPABLE 0x8000UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_FILTER_TRAFFIC_TYPE_L2_ROCE_SUPPORTED 0x10000UL u8 unused_0[3]; u8 valid; }; @@ -7472,7 +7614,8 @@ struct hwrm_struct_hdr { #define STRUCT_HDR_STRUCT_ID_AFM_OPAQUE 0x1UL #define STRUCT_HDR_STRUCT_ID_PORT_DESCRIPTION 0xaUL #define STRUCT_HDR_STRUCT_ID_RSS_V2 0x64UL - #define STRUCT_HDR_STRUCT_ID_LAST STRUCT_HDR_STRUCT_ID_RSS_V2 + #define STRUCT_HDR_STRUCT_ID_MSIX_PER_VF 0xc8UL + #define STRUCT_HDR_STRUCT_ID_LAST STRUCT_HDR_STRUCT_ID_MSIX_PER_VF __le16 len; u8 version; u8 count; @@ -8000,6 +8143,9 @@ struct hwrm_dbg_coredump_initiate_output { struct coredump_data_hdr { __le32 address; __le32 flags_length; + #define COREDUMP_DATA_HDR_FLAGS_LENGTH_ACTUAL_LEN_MASK 0xffffffUL + #define COREDUMP_DATA_HDR_FLAGS_LENGTH_ACTUAL_LEN_SFT 0 + #define COREDUMP_DATA_HDR_FLAGS_LENGTH_INDIRECT_ACCESS 0x1000000UL __le32 instance; __le32 next_offset; }; @@ -8669,7 +8815,6 @@ struct hcomm_status { #define HCOMM_STATUS_TRUE_OFFSET_MASK 0xfffffffcUL #define HCOMM_STATUS_TRUE_OFFSET_SFT 2 }; - #define HCOMM_STATUS_STRUCT_LOC 0x31001F0UL #endif /* _BNXT_HSI_H_ */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index fcc262064766..641303894341 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -133,12 +133,9 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, dma_sync_single_for_cpu(&pdev->dev, mapping + offset, *len, bp->rx_dir); txr = rxr->bnapi->tx_ring; - xdp.data_hard_start = *data_ptr - offset; - xdp.data = *data_ptr; - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = *data_ptr + *len; - xdp.rxq = &rxr->xdp_rxq; - xdp.frame_sz = PAGE_SIZE; /* BNXT_RX_PAGE_MODE(bp) when XDP enabled */ + /* BNXT_RX_PAGE_MODE(bp) when XDP enabled */ + xdp_init_buff(&xdp, PAGE_SIZE, &rxr->xdp_rxq); + xdp_prepare_buff(&xdp, *data_ptr - offset, offset, *len, false); orig_data = xdp.data; rcu_read_lock(); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index f6ca01da141d..0a6d91b0f0aa 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -16,6 +16,8 @@ #include <linux/dim.h> #include <linux/ethtool.h> +#include "../unimac.h" + /* total number of Buffer Descriptors, same for Rx/Tx */ #define TOTAL_DESC 256 @@ -150,63 +152,6 @@ struct bcmgenet_mib_counters { u32 tx_realloc_tsb_failed; }; -#define UMAC_HD_BKP_CTRL 0x004 -#define HD_FC_EN (1 << 0) -#define HD_FC_BKOFF_OK (1 << 1) -#define IPG_CONFIG_RX_SHIFT 2 -#define IPG_CONFIG_RX_MASK 0x1F - -#define UMAC_CMD 0x008 -#define CMD_TX_EN (1 << 0) -#define CMD_RX_EN (1 << 1) -#define UMAC_SPEED_10 0 -#define UMAC_SPEED_100 1 -#define UMAC_SPEED_1000 2 -#define UMAC_SPEED_2500 3 -#define CMD_SPEED_SHIFT 2 -#define CMD_SPEED_MASK 3 -#define CMD_PROMISC (1 << 4) -#define CMD_PAD_EN (1 << 5) -#define CMD_CRC_FWD (1 << 6) -#define CMD_PAUSE_FWD (1 << 7) -#define CMD_RX_PAUSE_IGNORE (1 << 8) -#define CMD_TX_ADDR_INS (1 << 9) -#define CMD_HD_EN (1 << 10) -#define CMD_SW_RESET (1 << 13) -#define CMD_LCL_LOOP_EN (1 << 15) -#define CMD_AUTO_CONFIG (1 << 22) -#define CMD_CNTL_FRM_EN (1 << 23) -#define CMD_NO_LEN_CHK (1 << 24) -#define CMD_RMT_LOOP_EN (1 << 25) -#define CMD_PRBL_EN (1 << 27) -#define CMD_TX_PAUSE_IGNORE (1 << 28) -#define CMD_TX_RX_EN (1 << 29) -#define CMD_RUNT_FILTER_DIS (1 << 30) - -#define UMAC_MAC0 0x00C -#define UMAC_MAC1 0x010 -#define UMAC_MAX_FRAME_LEN 0x014 - -#define UMAC_MODE 0x44 -#define MODE_LINK_STATUS (1 << 5) - -#define UMAC_EEE_CTRL 0x064 -#define EN_LPI_RX_PAUSE (1 << 0) -#define EN_LPI_TX_PFC (1 << 1) -#define EN_LPI_TX_PAUSE (1 << 2) -#define EEE_EN (1 << 3) -#define RX_FIFO_CHECK (1 << 4) -#define EEE_TX_CLK_DIS (1 << 5) -#define DIS_EEE_10M (1 << 6) -#define LP_IDLE_PREDICTION_MODE (1 << 7) - -#define UMAC_EEE_LPI_TIMER 0x068 -#define UMAC_EEE_WAKE_TIMER 0x06C -#define UMAC_EEE_REF_COUNT 0x070 -#define EEE_REFERENCE_COUNT_MASK 0xffff - -#define UMAC_TX_FLUSH 0x334 - #define UMAC_MIB_START 0x400 #define UMAC_MDIO_CMD 0x614 diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 6fb6c3556285..17f997ef950f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -63,11 +63,11 @@ void bcmgenet_mii_setup(struct net_device *dev) /* speed */ if (phydev->speed == SPEED_1000) - cmd_bits = UMAC_SPEED_1000; + cmd_bits = CMD_SPEED_1000; else if (phydev->speed == SPEED_100) - cmd_bits = UMAC_SPEED_100; + cmd_bits = CMD_SPEED_100; else - cmd_bits = UMAC_SPEED_10; + cmd_bits = CMD_SPEED_10; cmd_bits <<= CMD_SPEED_SHIFT; /* duplex */ diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 5143cdd0eeca..8936c2bc6286 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -12826,11 +12826,13 @@ static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen) offset = tg3_nvram_logical_addr(tp, offset); } - } - if (!offset || !len) { - offset = TG3_NVM_VPD_OFF; - len = TG3_NVM_VPD_LEN; + if (!offset || !len) { + offset = TG3_NVM_VPD_OFF; + len = TG3_NVM_VPD_LEN; + } + } else { + len = TG3_NVM_PCI_VPD_MAX_LEN; } buf = kmalloc(len, GFP_KERNEL); @@ -12846,26 +12848,16 @@ static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen) if (tg3_nvram_read_be32(tp, offset + i, &buf[i/4])) goto error; } + *vpdlen = len; } else { - u8 *ptr; ssize_t cnt; - unsigned int pos = 0; - - ptr = (u8 *)&buf[0]; - for (i = 0; pos < len && i < 3; i++, pos += cnt, ptr += cnt) { - cnt = pci_read_vpd(tp->pdev, pos, - len - pos, ptr); - if (cnt == -ETIMEDOUT || cnt == -EINTR) - cnt = 0; - else if (cnt < 0) - goto error; - } - if (pos != len) + + cnt = pci_read_vpd(tp->pdev, 0, len, (u8 *)buf); + if (cnt < 0) goto error; + *vpdlen = cnt; } - *vpdlen = len; - return buf; error: diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index 1000c894064f..46ec4fdfd16a 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -2101,6 +2101,7 @@ /* Hardware Legacy NVRAM layout */ #define TG3_NVM_VPD_OFF 0x100 #define TG3_NVM_VPD_LEN 256 +#define TG3_NVM_PCI_VPD_MAX_LEN 512 /* Hardware Selfboot NVRAM layout */ #define TG3_NVM_HWSB_CFG1 0x00000004 diff --git a/drivers/net/ethernet/broadcom/unimac.h b/drivers/net/ethernet/broadcom/unimac.h new file mode 100644 index 000000000000..585a85286257 --- /dev/null +++ b/drivers/net/ethernet/broadcom/unimac.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __UNIMAC_H +#define __UNIMAC_H + +#define UMAC_HD_BKP_CTRL 0x004 +#define HD_FC_EN (1 << 0) +#define HD_FC_BKOFF_OK (1 << 1) +#define IPG_CONFIG_RX_SHIFT 2 +#define IPG_CONFIG_RX_MASK 0x1F +#define UMAC_CMD 0x008 +#define CMD_TX_EN (1 << 0) +#define CMD_RX_EN (1 << 1) +#define CMD_SPEED_10 0 +#define CMD_SPEED_100 1 +#define CMD_SPEED_1000 2 +#define CMD_SPEED_2500 3 +#define CMD_SPEED_SHIFT 2 +#define CMD_SPEED_MASK 3 +#define CMD_PROMISC (1 << 4) +#define CMD_PAD_EN (1 << 5) +#define CMD_CRC_FWD (1 << 6) +#define CMD_PAUSE_FWD (1 << 7) +#define CMD_RX_PAUSE_IGNORE (1 << 8) +#define CMD_TX_ADDR_INS (1 << 9) +#define CMD_HD_EN (1 << 10) +#define CMD_SW_RESET_OLD (1 << 11) +#define CMD_SW_RESET (1 << 13) +#define CMD_LCL_LOOP_EN (1 << 15) +#define CMD_AUTO_CONFIG (1 << 22) +#define CMD_CNTL_FRM_EN (1 << 23) +#define CMD_NO_LEN_CHK (1 << 24) +#define CMD_RMT_LOOP_EN (1 << 25) +#define CMD_RX_ERR_DISC (1 << 26) +#define CMD_PRBL_EN (1 << 27) +#define CMD_TX_PAUSE_IGNORE (1 << 28) +#define CMD_TX_RX_EN (1 << 29) +#define CMD_RUNT_FILTER_DIS (1 << 30) +#define UMAC_MAC0 0x00c +#define UMAC_MAC1 0x010 +#define UMAC_MAX_FRAME_LEN 0x014 +#define UMAC_PAUSE_QUANTA 0x018 +#define UMAC_MODE 0x044 +#define MODE_LINK_STATUS (1 << 5) +#define UMAC_FRM_TAG0 0x048 /* outer tag */ +#define UMAC_FRM_TAG1 0x04c /* inner tag */ +#define UMAC_TX_IPG_LEN 0x05c +#define UMAC_EEE_CTRL 0x064 +#define EN_LPI_RX_PAUSE (1 << 0) +#define EN_LPI_TX_PFC (1 << 1) +#define EN_LPI_TX_PAUSE (1 << 2) +#define EEE_EN (1 << 3) +#define RX_FIFO_CHECK (1 << 4) +#define EEE_TX_CLK_DIS (1 << 5) +#define DIS_EEE_10M (1 << 6) +#define LP_IDLE_PREDICTION_MODE (1 << 7) +#define UMAC_EEE_LPI_TIMER 0x068 +#define UMAC_EEE_WAKE_TIMER 0x06C +#define UMAC_EEE_REF_COUNT 0x070 +#define EEE_REFERENCE_COUNT_MASK 0xffff +#define UMAC_RX_IPG_INV 0x078 +#define UMAC_MACSEC_PROG_TX_CRC 0x310 +#define UMAC_MACSEC_CTRL 0x314 +#define UMAC_PAUSE_CTRL 0x330 +#define UMAC_TX_FLUSH 0x334 +#define UMAC_RX_FIFO_STATUS 0x338 +#define UMAC_TX_FIFO_STATUS 0x33c + +#endif diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 814a5b10141d..472bf8f220bc 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -470,6 +470,10 @@ static void macb_set_tx_clk(struct macb *bp, int speed) if (!bp->tx_clk || (bp->caps & MACB_CAPS_CLK_HW_CHG)) return; + /* In case of MII the PHY is the clock master */ + if (bp->phy_interface == PHY_INTERFACE_MODE_MII) + return; + switch (speed) { case SPEED_10: rate = 2500000; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 37d064193f0f..2a0d64e5797c 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -1163,7 +1163,7 @@ int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs) oct->flags |= LIO_FLAG_MSI_ENABLED; /* allocate storage for the names assigned to the irq */ - oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL); + oct->irq_name_storage = kzalloc(INTRNAMSIZ, GFP_KERNEL); if (!oct->irq_name_storage) return -ENOMEM; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 7d00d3a8ded4..7c5af4beedc6 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3219,8 +3219,6 @@ static const struct net_device_ops lionetdevops = { .ndo_do_ioctl = liquidio_ioctl, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_set_vf_mac = liquidio_set_vf_mac, .ndo_set_vf_vlan = liquidio_set_vf_vlan, .ndo_get_vf_config = liquidio_get_vf_config, diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 103440f97bc8..516f166ceff8 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1879,8 +1879,6 @@ static const struct net_device_ops lionetdevops = { .ndo_do_ioctl = liquidio_ioctl, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, }; static int lio_nic_info(struct octeon_recv_info *recv_info, void *buf) diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 387a57cbfb73..e159194d0aef 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -545,7 +545,7 @@ static atomic_t adapter_fw_states[MAX_OCTEON_DEVICES]; static u32 octeon_device_count; /* locks device array (i.e. octeon_device[]) */ -static spinlock_t octeon_devices_lock; +static DEFINE_SPINLOCK(octeon_devices_lock); static struct octeon_core_setup core_setup[MAX_OCTEON_DEVICES]; @@ -563,7 +563,6 @@ void octeon_init_device_list(int conf_type) memset(octeon_device, 0, (sizeof(void *) * MAX_OCTEON_DEVICES)); for (i = 0; i < MAX_OCTEON_DEVICES; i++) oct_set_config_info(i, conf_type); - spin_lock_init(&octeon_devices_lock); } static void *__retrieve_octeon_config_info(struct octeon_device *oct, diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index f3b7b443f964..c33b4e837515 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -530,6 +530,7 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, struct cqe_rx_t *cqe_rx, struct snd_queue *sq, struct rcv_queue *rq, struct sk_buff **skb) { + unsigned char *hard_start, *data; struct xdp_buff xdp; struct page *page; u32 action; @@ -547,12 +548,11 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, cpu_addr = (u64)phys_to_virt(cpu_addr); page = virt_to_page((void *)cpu_addr); - xdp.data_hard_start = page_address(page); - xdp.data = (void *)cpu_addr; - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = xdp.data + len; - xdp.rxq = &rq->xdp_rxq; - xdp.frame_sz = RCV_FRAG_LEN + XDP_PACKET_HEADROOM; + xdp_init_buff(&xdp, RCV_FRAG_LEN + XDP_PACKET_HEADROOM, + &rq->xdp_rxq); + hard_start = page_address(page); + data = (unsigned char *)cpu_addr; + xdp_prepare_buff(&xdp, hard_start, data - hard_start, len, false); orig_data = xdp.data; rcu_read_lock(); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 7fd264a6d085..9f1965c80fb1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -3882,8 +3882,6 @@ static const struct net_device_ops cxgb4_netdev_ops = { #endif /* CONFIG_CHELSIO_T4_FCOE */ .ndo_set_tx_maxrate = cxgb_set_tx_maxrate, .ndo_setup_tc = cxgb_setup_tc, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = cxgb_features_check, .ndo_fix_features = cxgb_fix_features, }; @@ -5139,7 +5137,7 @@ static int adap_init0(struct adapter *adap, int vpd_skip) /* See if FW supports FW_FILTER2 work request */ if (is_t4(adap->params.chip)) { - adap->params.filter2_wr_support = 0; + adap->params.filter2_wr_support = false; } else { params[0] = FW_PARAM_DEV(FILTER2_WR); ret = t4_query_params(adap, adap->mbox, adap->pf, 0, diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 196652a114c5..550cc065649f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1600,7 +1600,8 @@ static netdev_tx_t cxgb4_eth_xmit(struct sk_buff *skb, struct net_device *dev) * has opened up. */ eth_txq_stop(q); - wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F; + if (chip_ver > CHELSIO_T5) + wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F; } wr = (void *)&q->q.desc[q->q.pidx]; @@ -1832,6 +1833,7 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb, struct adapter *adapter; int qidx, credits, ret; size_t fw_hdr_copy_len; + unsigned int chip_ver; u64 cntrl, *end; u32 wr_mid; @@ -1896,6 +1898,7 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb, goto out_free; } + chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip); wr_mid = FW_WR_LEN16_V(DIV_ROUND_UP(flits, 2)); if (unlikely(credits < ETHTXQ_STOP_THRES)) { /* After we're done injecting the Work Request for this @@ -1907,7 +1910,8 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb, * has opened up. */ eth_txq_stop(txq); - wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F; + if (chip_ver > CHELSIO_T5) + wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F; } /* Start filling in our Work Request. Note that we do _not_ handle @@ -1960,7 +1964,7 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb, */ cpl = (void *)(lso + 1); - if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) + if (chip_ver <= CHELSIO_T5) cntrl = TXPKT_ETHHDR_LEN_V(eth_xtra_len); else cntrl = T6_TXPKT_ETHHDR_LEN_V(eth_xtra_len); @@ -3598,6 +3602,25 @@ static void t4_tx_completion_handler(struct sge_rspq *rspq, } txq = &s->ethtxq[pi->first_qset + rspq->idx]; + + /* We've got the Hardware Consumer Index Update in the Egress Update + * message. These Egress Update messages will be our sole CIDX Updates + * we get since we don't want to chew up PCIe bandwidth for both Ingress + * Messages and Status Page writes. However, The code which manages + * reclaiming successfully DMA'ed TX Work Requests uses the CIDX value + * stored in the Status Page at the end of the TX Queue. It's easiest + * to simply copy the CIDX Update value from the Egress Update message + * to the Status Page. Also note that no Endian issues need to be + * considered here since both are Big Endian and we're just copying + * bytes consistently ... + */ + if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) { + struct cpl_sge_egr_update *egr; + + egr = (struct cpl_sge_egr_update *)rsp; + WRITE_ONCE(txq->q.stat->cidx, egr->cidx); + } + t4_sge_eth_txq_egress_update(adapter, txq, -1); } @@ -4583,11 +4606,15 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, * write the CIDX Updates into the Status Page at the end of the * TX Queue. */ - c.autoequiqe_to_viid = htonl(FW_EQ_ETH_CMD_AUTOEQUEQE_F | + c.autoequiqe_to_viid = htonl(((chip_ver <= CHELSIO_T5) ? + FW_EQ_ETH_CMD_AUTOEQUIQE_F : + FW_EQ_ETH_CMD_AUTOEQUEQE_F) | FW_EQ_ETH_CMD_VIID_V(pi->viid)); c.fetchszm_to_iqid = - htonl(FW_EQ_ETH_CMD_HOSTFCMODE_V(HOSTFCMODE_STATUS_PAGE_X) | + htonl(FW_EQ_ETH_CMD_HOSTFCMODE_V((chip_ver <= CHELSIO_T5) ? + HOSTFCMODE_INGRESS_QUEUE_X : + HOSTFCMODE_STATUS_PAGE_X) | FW_EQ_ETH_CMD_PCIECHN_V(pi->tx_chan) | FW_EQ_ETH_CMD_FETCHRO_F | FW_EQ_ETH_CMD_IQID_V(iqid)); @@ -4598,6 +4625,7 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, : FETCHBURSTMIN_64B_T6_X) | FW_EQ_ETH_CMD_FBMAX_V(FETCHBURSTMAX_512B_X) | FW_EQ_ETH_CMD_CIDXFTHRESH_V(CIDXFLUSHTHRESH_32_X) | + FW_EQ_ETH_CMD_CIDXFTHRESHO_V(chip_ver == CHELSIO_T5) | FW_EQ_ETH_CMD_EQSIZE_V(nentries)); c.eqaddr = cpu_to_be64(txq->q.phys_addr); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 98d01a7497ec..98829e482bfa 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -2689,7 +2689,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) #define VPD_BASE 0x400 #define VPD_BASE_OLD 0 #define VPD_LEN 1024 -#define CHELSIO_VPD_UNIQUE_ID 0x82 /** * t4_eeprom_ptov - translate a physical EEPROM address to virtual @@ -2745,7 +2744,7 @@ int t4_get_raw_vpd_params(struct adapter *adapter, struct vpd_params *p) { int i, ret = 0, addr; int ec, sn, pn, na; - u8 *vpd, csum; + u8 *vpd, csum, base_val = 0; unsigned int vpdr_len, kw_offset, id_len; vpd = vmalloc(VPD_LEN); @@ -2755,17 +2754,11 @@ int t4_get_raw_vpd_params(struct adapter *adapter, struct vpd_params *p) /* Card information normally starts at VPD_BASE but early cards had * it at 0. */ - ret = pci_read_vpd(adapter->pdev, VPD_BASE, sizeof(u32), vpd); + ret = pci_read_vpd(adapter->pdev, VPD_BASE, 1, &base_val); if (ret < 0) goto out; - /* The VPD shall have a unique identifier specified by the PCI SIG. - * For chelsio adapters, the identifier is 0x82. The first byte of a VPD - * shall be CHELSIO_VPD_UNIQUE_ID (0x82). The VPD programming software - * is expected to automatically put this entry at the - * beginning of the VPD. - */ - addr = *vpd == CHELSIO_VPD_UNIQUE_ID ? VPD_BASE : VPD_BASE_OLD; + addr = base_val == PCI_VPD_LRDT_ID_STRING ? VPD_BASE : VPD_BASE_OLD; ret = pci_read_vpd(adapter->pdev, addr, VPD_LEN, vpd); if (ret < 0) diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c index 47d9268a7e3c..585590520076 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c @@ -92,9 +92,6 @@ static const struct xfrmdev_ops ch_ipsec_xfrmdev_ops = { static struct cxgb4_uld_info ch_ipsec_uld_info = { .name = CHIPSEC_DRV_MODULE_NAME, - .nrxq = MAX_ULD_QSETS, - /* Max ntxq will be derived from fw config file*/ - .rxq_size = 1024, .add = ch_ipsec_uld_add, .state_change = ch_ipsec_uld_state_change, .tx_handler = ch_ipsec_xmit, diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index fb269d587b74..f04ec53544ae 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2509,8 +2509,6 @@ static const struct net_device_ops enic_netdev_dynamic_ops = { #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = enic_rx_flow_steer, #endif - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = enic_features_check, }; @@ -2535,8 +2533,6 @@ static const struct net_device_ops enic_netdev_ops = { #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = enic_rx_flow_steer, #endif - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = enic_features_check, }; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index d402d83d9edd..b6eba29d8e99 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -5179,8 +5179,6 @@ static const struct net_device_ops be_netdev_ops = { #endif .ndo_bridge_setlink = be_ndo_bridge_setlink, .ndo_bridge_getlink = be_ndo_bridge_getlink, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = be_features_check, .ndo_get_phys_port_id = be_get_phys_port_id, }; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 4360ce4d3fb6..d8e568f6caf3 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -2532,12 +2532,10 @@ static u32 dpaa_run_xdp(struct dpaa_priv *priv, struct qm_fd *fd, void *vaddr, return XDP_PASS; } - xdp.data = vaddr + fd_off; - xdp.data_meta = xdp.data; - xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM; - xdp.data_end = xdp.data + qm_fd_get_length(fd); - xdp.frame_sz = DPAA_BP_RAW_SIZE - DPAA_TX_PRIV_DATA_SIZE; - xdp.rxq = &dpaa_fq->xdp_rxq; + xdp_init_buff(&xdp, DPAA_BP_RAW_SIZE - DPAA_TX_PRIV_DATA_SIZE, + &dpaa_fq->xdp_rxq); + xdp_prepare_buff(&xdp, vaddr + fd_off - XDP_PACKET_HEADROOM, + XDP_PACKET_HEADROOM, qm_fd_get_length(fd), true); /* We reserve a fixed headroom of 256 bytes under the erratum and we * offer it all to XDP programs to use. If no room is left for the diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index fb0bcd18ec0c..41e225baf571 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -350,7 +350,7 @@ static u32 dpaa2_eth_run_xdp(struct dpaa2_eth_priv *priv, struct bpf_prog *xdp_prog; struct xdp_buff xdp; u32 xdp_act = XDP_PASS; - int err; + int err, offset; rcu_read_lock(); @@ -358,14 +358,10 @@ static u32 dpaa2_eth_run_xdp(struct dpaa2_eth_priv *priv, if (!xdp_prog) goto out; - xdp.data = vaddr + dpaa2_fd_get_offset(fd); - xdp.data_end = xdp.data + dpaa2_fd_get_len(fd); - xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM; - xdp_set_data_meta_invalid(&xdp); - xdp.rxq = &ch->xdp_rxq; - - xdp.frame_sz = DPAA2_ETH_RX_BUF_RAW_SIZE - - (dpaa2_fd_get_offset(fd) - XDP_PACKET_HEADROOM); + offset = dpaa2_fd_get_offset(fd) - XDP_PACKET_HEADROOM; + xdp_init_buff(&xdp, DPAA2_ETH_RX_BUF_RAW_SIZE - offset, &ch->xdp_rxq); + xdp_prepare_buff(&xdp, vaddr + offset, XDP_PACKET_HEADROOM, + dpaa2_fd_get_len(fd), false); xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp); @@ -1262,6 +1258,22 @@ static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv, percpu_stats->tx_errors++; } +static int dpaa2_eth_set_rx_vlan_filtering(struct dpaa2_eth_priv *priv, + bool enable) +{ + int err; + + err = dpni_enable_vlan_filter(priv->mc_io, 0, priv->mc_token, enable); + + if (err) { + netdev_err(priv->net_dev, + "dpni_enable_vlan_filter failed\n"); + return err; + } + + return 0; +} + static int dpaa2_eth_set_rx_csum(struct dpaa2_eth_priv *priv, bool enable) { int err; @@ -1691,7 +1703,7 @@ static int dpaa2_eth_link_state_update(struct dpaa2_eth_priv *priv) /* When we manage the MAC/PHY using phylink there is no need * to manually update the netif_carrier. */ - if (priv->mac) + if (dpaa2_eth_is_type_phy(priv)) goto out; /* Chech link state; speed / duplex changes are not treated yet */ @@ -1730,7 +1742,7 @@ static int dpaa2_eth_open(struct net_device *net_dev) priv->dpbp_dev->obj_desc.id, priv->bpid); } - if (!priv->mac) { + if (!dpaa2_eth_is_type_phy(priv)) { /* We'll only start the txqs when the link is actually ready; * make sure we don't race against the link up notification, * which may come immediately after dpni_enable(); @@ -1752,7 +1764,7 @@ static int dpaa2_eth_open(struct net_device *net_dev) goto enable_err; } - if (priv->mac) + if (dpaa2_eth_is_type_phy(priv)) phylink_start(priv->mac->phylink); return 0; @@ -1826,11 +1838,11 @@ static int dpaa2_eth_stop(struct net_device *net_dev) int dpni_enabled = 0; int retries = 10; - if (!priv->mac) { + if (dpaa2_eth_is_type_phy(priv)) { + phylink_stop(priv->mac->phylink); + } else { netif_tx_stop_all_queues(net_dev); netif_carrier_off(net_dev); - } else { - phylink_stop(priv->mac->phylink); } /* On dpni_disable(), the MC firmware will: @@ -1952,6 +1964,43 @@ static void dpaa2_eth_add_mc_hw_addr(const struct net_device *net_dev, } } +static int dpaa2_eth_rx_add_vid(struct net_device *net_dev, + __be16 vlan_proto, u16 vid) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int err; + + err = dpni_add_vlan_id(priv->mc_io, 0, priv->mc_token, + vid, 0, 0, 0); + + if (err) { + netdev_warn(priv->net_dev, + "Could not add the vlan id %u\n", + vid); + return err; + } + + return 0; +} + +static int dpaa2_eth_rx_kill_vid(struct net_device *net_dev, + __be16 vlan_proto, u16 vid) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int err; + + err = dpni_remove_vlan_id(priv->mc_io, 0, priv->mc_token, vid); + + if (err) { + netdev_warn(priv->net_dev, + "Could not remove the vlan id %u\n", + vid); + return err; + } + + return 0; +} + static void dpaa2_eth_set_rx_mode(struct net_device *net_dev) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); @@ -2058,6 +2107,13 @@ static int dpaa2_eth_set_features(struct net_device *net_dev, bool enable; int err; + if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { + enable = !!(features & NETIF_F_HW_VLAN_CTAG_FILTER); + err = dpaa2_eth_set_rx_vlan_filtering(priv, enable); + if (err) + return err; + } + if (changed & NETIF_F_RXCSUM) { enable = !!(features & NETIF_F_RXCSUM); err = dpaa2_eth_set_rx_csum(priv, enable); @@ -2115,7 +2171,7 @@ static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if (cmd == SIOCSHWTSTAMP) return dpaa2_eth_ts_ioctl(dev, rq, cmd); - if (priv->mac) + if (dpaa2_eth_is_type_phy(priv)) return phylink_mii_ioctl(priv->mac->phylink, rq, cmd); return -EOPNOTSUPP; @@ -2507,6 +2563,8 @@ static const struct net_device_ops dpaa2_eth_ops = { .ndo_bpf = dpaa2_eth_xdp, .ndo_xdp_xmit = dpaa2_eth_xdp_xmit, .ndo_setup_tc = dpaa2_eth_setup_tc, + .ndo_vlan_rx_add_vid = dpaa2_eth_rx_add_vid, + .ndo_vlan_rx_kill_vid = dpaa2_eth_rx_kill_vid }; static void dpaa2_eth_cdan_cb(struct dpaa2_io_notification_ctx *ctx) @@ -4015,6 +4073,9 @@ static int dpaa2_eth_netdev_init(struct net_device *net_dev) NETIF_F_LLTX | NETIF_F_HW_TC; net_dev->hw_features = net_dev->features; + if (priv->dpni_attrs.vlan_filter_entries) + net_dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + return 0; } @@ -4042,10 +4103,11 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) dpni_dev = to_fsl_mc_device(priv->net_dev->dev.parent); dpmac_dev = fsl_mc_get_endpoint(dpni_dev); - if (IS_ERR_OR_NULL(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type) - return 0; - if (dpaa2_mac_is_type_fixed(dpmac_dev, priv->mc_io)) + if (PTR_ERR(dpmac_dev) == -EPROBE_DEFER) + return PTR_ERR(dpmac_dev); + + if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type) return 0; mac = kzalloc(sizeof(struct dpaa2_mac), GFP_KERNEL); @@ -4056,23 +4118,38 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) mac->mc_io = priv->mc_io; mac->net_dev = priv->net_dev; - err = dpaa2_mac_connect(mac); - if (err) { - netdev_err(priv->net_dev, "Error connecting to the MAC endpoint\n"); - kfree(mac); - return err; - } + err = dpaa2_mac_open(mac); + if (err) + goto err_free_mac; priv->mac = mac; + if (dpaa2_eth_is_type_phy(priv)) { + err = dpaa2_mac_connect(mac); + if (err) { + netdev_err(priv->net_dev, "Error connecting to the MAC endpoint\n"); + goto err_close_mac; + } + } + return 0; + +err_close_mac: + dpaa2_mac_close(mac); + priv->mac = NULL; +err_free_mac: + kfree(mac); + return err; } static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) { - if (!priv->mac) + if (dpaa2_eth_is_type_phy(priv)) + dpaa2_mac_disconnect(priv->mac); + + if (!dpaa2_eth_has_mac(priv)) return; - dpaa2_mac_disconnect(priv->mac); + dpaa2_mac_close(priv->mac); kfree(priv->mac); priv->mac = NULL; } @@ -4101,7 +4178,7 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) dpaa2_eth_update_tx_fqids(priv); rtnl_lock(); - if (priv->mac) + if (dpaa2_eth_has_mac(priv)) dpaa2_eth_disconnect_mac(priv); else dpaa2_eth_connect_mac(priv); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index d236b8695c39..c3d456c45102 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -693,6 +693,19 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv) return priv->tx_data_offset - DPAA2_ETH_RX_HWA_SIZE; } +static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv) +{ + if (priv->mac && priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY) + return true; + + return false; +} + +static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv) +{ + return priv->mac ? true : false; +} + int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags); int dpaa2_eth_set_cls(struct net_device *net_dev, u64 key); int dpaa2_eth_cls_key_size(u64 key); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index f981a523e13a..bf59708b869e 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -85,7 +85,7 @@ static int dpaa2_eth_nway_reset(struct net_device *net_dev) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - if (priv->mac) + if (dpaa2_eth_is_type_phy(priv)) return phylink_ethtool_nway_reset(priv->mac->phylink); return -EOPNOTSUPP; @@ -97,7 +97,7 @@ dpaa2_eth_get_link_ksettings(struct net_device *net_dev, { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - if (priv->mac) + if (dpaa2_eth_is_type_phy(priv)) return phylink_ethtool_ksettings_get(priv->mac->phylink, link_settings); @@ -115,7 +115,7 @@ dpaa2_eth_set_link_ksettings(struct net_device *net_dev, { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - if (!priv->mac) + if (!dpaa2_eth_is_type_phy(priv)) return -ENOTSUPP; return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings); @@ -127,7 +127,7 @@ static void dpaa2_eth_get_pauseparam(struct net_device *net_dev, struct dpaa2_eth_priv *priv = netdev_priv(net_dev); u64 link_options = priv->link_state.options; - if (priv->mac) { + if (dpaa2_eth_is_type_phy(priv)) { phylink_ethtool_get_pauseparam(priv->mac->phylink, pause); return; } @@ -150,7 +150,7 @@ static int dpaa2_eth_set_pauseparam(struct net_device *net_dev, return -EOPNOTSUPP; } - if (priv->mac) + if (dpaa2_eth_is_type_phy(priv)) return phylink_ethtool_set_pauseparam(priv->mac->phylink, pause); if (pause->autoneg) @@ -198,7 +198,7 @@ static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset, strlcpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - if (priv->mac) + if (dpaa2_eth_has_mac(priv)) dpaa2_mac_get_strings(p); break; } @@ -211,7 +211,7 @@ static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset) switch (sset) { case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */ - if (priv->mac) + if (dpaa2_eth_has_mac(priv)) num_ss_stats += dpaa2_mac_get_sset_count(); return num_ss_stats; default: @@ -313,7 +313,7 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, } *(data + i++) = buf_cnt; - if (priv->mac) + if (dpaa2_eth_has_mac(priv)) dpaa2_mac_get_ethtool_stats(priv->mac, data + i); } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 828c177df03d..69ad869446cf 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -174,30 +174,22 @@ static void dpaa2_mac_link_up(struct phylink_config *config, dpmac_state->up = 1; - if (mac->if_link_type == DPMAC_LINK_TYPE_PHY) { - /* If the DPMAC is configured for PHY mode, we need - * to pass the link parameters to the MC firmware. - */ - dpmac_state->rate = speed; - - if (duplex == DUPLEX_HALF) - dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX; - else if (duplex == DUPLEX_FULL) - dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX; - - /* This is lossy; the firmware really should take the pause - * enablement status rather than pause/asym pause status. - */ - if (rx_pause) - dpmac_state->options |= DPMAC_LINK_OPT_PAUSE; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE; - - if (rx_pause ^ tx_pause) - dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE; - } + dpmac_state->rate = speed; + + if (duplex == DUPLEX_HALF) + dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX; + else if (duplex == DUPLEX_FULL) + dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX; + + if (rx_pause) + dpmac_state->options |= DPMAC_LINK_OPT_PAUSE; + else + dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE; + + if (rx_pause ^ tx_pause) + dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE; + else + dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE; err = dpmac_set_link_state(mac->mc_io, 0, mac->mc_dev->mc_handle, dpmac_state); @@ -228,32 +220,6 @@ static const struct phylink_mac_ops dpaa2_mac_phylink_ops = { .mac_link_down = dpaa2_mac_link_down, }; -bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, - struct fsl_mc_io *mc_io) -{ - struct dpmac_attr attr; - bool fixed = false; - u16 mc_handle = 0; - int err; - - err = dpmac_open(mc_io, 0, dpmac_dev->obj_desc.id, - &mc_handle); - if (err || !mc_handle) - return false; - - err = dpmac_get_attributes(mc_io, 0, mc_handle, &attr); - if (err) - goto out; - - if (attr.link_type == DPMAC_LINK_TYPE_FIXED) - fixed = true; - -out: - dpmac_close(mc_io, 0, mc_handle); - - return fixed; -} - static int dpaa2_pcs_create(struct dpaa2_mac *mac, struct device_node *dpmac_node, int id) { @@ -302,36 +268,20 @@ static void dpaa2_pcs_destroy(struct dpaa2_mac *mac) int dpaa2_mac_connect(struct dpaa2_mac *mac) { - struct fsl_mc_device *dpmac_dev = mac->mc_dev; struct net_device *net_dev = mac->net_dev; struct device_node *dpmac_node; struct phylink *phylink; - struct dpmac_attr attr; int err; - err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id, - &dpmac_dev->mc_handle); - if (err || !dpmac_dev->mc_handle) { - netdev_err(net_dev, "dpmac_open() = %d\n", err); - return -ENODEV; - } - - err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle, &attr); - if (err) { - netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err); - goto err_close_dpmac; - } + mac->if_link_type = mac->attr.link_type; - mac->if_link_type = attr.link_type; - - dpmac_node = dpaa2_mac_get_node(attr.id); + dpmac_node = dpaa2_mac_get_node(mac->attr.id); if (!dpmac_node) { - netdev_err(net_dev, "No dpmac@%d node found.\n", attr.id); - err = -ENODEV; - goto err_close_dpmac; + netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id); + return -ENODEV; } - err = dpaa2_mac_get_if_mode(dpmac_node, attr); + err = dpaa2_mac_get_if_mode(dpmac_node, mac->attr); if (err < 0) { err = -EINVAL; goto err_put_node; @@ -351,9 +301,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) goto err_put_node; } - if (attr.link_type == DPMAC_LINK_TYPE_PHY && - attr.eth_if != DPMAC_ETH_IF_RGMII) { - err = dpaa2_pcs_create(mac, dpmac_node, attr.id); + if (mac->attr.link_type == DPMAC_LINK_TYPE_PHY && + mac->attr.eth_if != DPMAC_ETH_IF_RGMII) { + err = dpaa2_pcs_create(mac, dpmac_node, mac->attr.id); if (err) goto err_put_node; } @@ -389,8 +339,7 @@ err_pcs_destroy: dpaa2_pcs_destroy(mac); err_put_node: of_node_put(dpmac_node); -err_close_dpmac: - dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle); + return err; } @@ -402,8 +351,40 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac) phylink_disconnect_phy(mac->phylink); phylink_destroy(mac->phylink); dpaa2_pcs_destroy(mac); +} + +int dpaa2_mac_open(struct dpaa2_mac *mac) +{ + struct fsl_mc_device *dpmac_dev = mac->mc_dev; + struct net_device *net_dev = mac->net_dev; + int err; - dpmac_close(mac->mc_io, 0, mac->mc_dev->mc_handle); + err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id, + &dpmac_dev->mc_handle); + if (err || !dpmac_dev->mc_handle) { + netdev_err(net_dev, "dpmac_open() = %d\n", err); + return -ENODEV; + } + + err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle, + &mac->attr); + if (err) { + netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err); + goto err_close_dpmac; + } + + return 0; + +err_close_dpmac: + dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle); + return err; +} + +void dpaa2_mac_close(struct dpaa2_mac *mac) +{ + struct fsl_mc_device *dpmac_dev = mac->mc_dev; + + dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle); } static char dpaa2_mac_ethtool_stats[][ETH_GSTRING_LEN] = { diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h index 955a52856210..13d42dd58ec9 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h @@ -17,6 +17,7 @@ struct dpaa2_mac { struct dpmac_link_state state; struct net_device *net_dev; struct fsl_mc_io *mc_io; + struct dpmac_attr attr; struct phylink_config phylink_config; struct phylink *phylink; @@ -28,6 +29,10 @@ struct dpaa2_mac { bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, struct fsl_mc_io *mc_io); +int dpaa2_mac_open(struct dpaa2_mac *mac); + +void dpaa2_mac_close(struct dpaa2_mac *mac); + int dpaa2_mac_connect(struct dpaa2_mac *mac); void dpaa2_mac_disconnect(struct dpaa2_mac *mac); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h index 90453dc7baef..9f80bdfeedec 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h @@ -62,6 +62,10 @@ #define DPNI_CMDID_SET_RX_TC_DIST DPNI_CMD(0x235) +#define DPNI_CMDID_ENABLE_VLAN_FILTER DPNI_CMD(0x230) +#define DPNI_CMDID_ADD_VLAN_ID DPNI_CMD_V2(0x231) +#define DPNI_CMDID_REMOVE_VLAN_ID DPNI_CMD(0x232) + #define DPNI_CMDID_SET_QOS_TBL DPNI_CMD(0x240) #define DPNI_CMDID_ADD_QOS_ENT DPNI_CMD(0x241) #define DPNI_CMDID_REMOVE_QOS_ENT DPNI_CMD(0x242) @@ -662,4 +666,17 @@ struct dpni_rsp_single_step_cfg { __le32 peer_delay; }; +struct dpni_cmd_enable_vlan_filter { + /* only the LSB */ + u8 en; +}; + +struct dpni_cmd_vlan_id { + u8 flags; + u8 tc_id; + u8 flow_id; + u8 pad; + __le16 vlan_id; +}; + #endif /* _FSL_DPNI_CMD_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c index 6ea7db66a632..aa429c17c343 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c @@ -1225,6 +1225,99 @@ int dpni_get_port_mac_addr(struct fsl_mc_io *mc_io, } /** + * dpni_enable_vlan_filter() - Enable/disable VLAN filtering mode + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @en: Set to '1' to enable; '0' to disable + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_enable_vlan_filter(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u32 en) +{ + struct dpni_cmd_enable_vlan_filter *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE_VLAN_FILTER, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_enable_vlan_filter *)cmd.params; + dpni_set_field(cmd_params->en, ENABLE, en); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_add_vlan_id() - Add VLAN ID filter + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @vlan_id: VLAN ID to add + * @flags: 0 - tc_id and flow_id will be ignored. + * Pkt with this vlan_id will be passed to the next + * classification stages + * DPNI_VLAN_SET_QUEUE_ACTION + * Pkt with this vlan_id will be forward directly to + * queue defined by the tc_id and flow_id + * + * @tc_id: Traffic class selection (0-7) + * @flow_id: Selects the specific queue out of the set allocated for the + * same as tc_id. Value must be in range 0 to NUM_QUEUES - 1 + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_add_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + u16 vlan_id, u8 flags, u8 tc_id, u8 flow_id) +{ + struct dpni_cmd_vlan_id *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_VLAN_ID, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_vlan_id *)cmd.params; + cmd_params->flags = flags; + cmd_params->tc_id = tc_id; + cmd_params->flow_id = flow_id; + cmd_params->vlan_id = cpu_to_le16(vlan_id); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_remove_vlan_id() - Remove VLAN ID filter + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @vlan_id: VLAN ID to remove + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + u16 vlan_id) +{ + struct dpni_cmd_vlan_id *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_VLAN_ID, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_vlan_id *)cmd.params; + cmd_params->vlan_id = cpu_to_le16(vlan_id); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** * dpni_add_mac_addr() - Add MAC address filter * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h index e7b9e195b534..4e96d9362dd2 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h @@ -1114,4 +1114,13 @@ int dpni_get_single_step_cfg(struct fsl_mc_io *mc_io, u16 token, struct dpni_single_step_cfg *ptp_cfg); +int dpni_enable_vlan_filter(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + u32 en); + +int dpni_add_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + u16 vlan_id, u8 flags, u8 tc_id, u8 flow_id); + +int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + u16 vlan_id); + #endif /* __FSL_DPNI_H */ diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c index ee0116ed4738..70e6d97b380f 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c @@ -14,23 +14,6 @@ #define ENETC_MDIO_DATA 0x8 /* MDIO data */ #define ENETC_MDIO_ADDR 0xc /* MDIO address */ -static inline u32 _enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off) -{ - return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off); -} - -static inline void _enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off, - u32 val) -{ - enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val); -} - -#define enetc_mdio_rd(mdio_priv, off) \ - _enetc_mdio_rd(mdio_priv, ENETC_##off) -#define enetc_mdio_wr(mdio_priv, off, val) \ - _enetc_mdio_wr(mdio_priv, ENETC_##off, val) -#define enetc_mdio_rd_reg(off) enetc_mdio_rd(mdio_priv, off) - #define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8) #define MDIO_CFG_BSY BIT(0) #define MDIO_CFG_RD_ER BIT(1) @@ -47,15 +30,29 @@ static inline void _enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off, #define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f) #define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5) #define MDIO_CTL_READ BIT(15) -#define MDIO_DATA(x) ((x) & 0xffff) -#define TIMEOUT 1000 +static inline u32 enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off) +{ + return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off); +} + +static inline void enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off, + u32 val) +{ + enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val); +} + +static bool enetc_mdio_is_busy(struct enetc_mdio_priv *mdio_priv) +{ + return enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_BSY; +} + static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv) { - u32 val; + bool is_busy; - return readx_poll_timeout(enetc_mdio_rd_reg, MDIO_CFG, val, - !(val & MDIO_CFG_BSY), 10, 10 * TIMEOUT); + return readx_poll_timeout(enetc_mdio_is_busy, mdio_priv, + is_busy, !is_busy, 10, 10 * 1000); } int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) @@ -75,7 +72,7 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) mdio_cfg &= ~MDIO_CFG_ENC45; } - enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg); ret = enetc_mdio_wait_complete(mdio_priv); if (ret) @@ -83,11 +80,11 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) /* set port and dev addr */ mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); - enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl); /* set the register address */ if (regnum & MII_ADDR_C45) { - enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff); ret = enetc_mdio_wait_complete(mdio_priv); if (ret) @@ -95,7 +92,7 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) } /* write the value */ - enetc_mdio_wr(mdio_priv, MDIO_DATA, MDIO_DATA(value)); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value); ret = enetc_mdio_wait_complete(mdio_priv); if (ret) @@ -121,7 +118,7 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) mdio_cfg &= ~MDIO_CFG_ENC45; } - enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg); ret = enetc_mdio_wait_complete(mdio_priv); if (ret) @@ -129,11 +126,11 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) /* set port and device addr */ mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); - enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl); /* set the register address */ if (regnum & MII_ADDR_C45) { - enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff); ret = enetc_mdio_wait_complete(mdio_priv); if (ret) @@ -141,21 +138,21 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) } /* initiate the read */ - enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl | MDIO_CTL_READ); + enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ); ret = enetc_mdio_wait_complete(mdio_priv); if (ret) return ret; /* return all Fs if nothing was there */ - if (enetc_mdio_rd(mdio_priv, MDIO_CFG) & MDIO_CFG_RD_ER) { + if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) { dev_dbg(&bus->dev, "Error while reading PHY%d reg at %d.%hhu\n", phy_id, dev_addr, regnum); return 0xffff; } - value = enetc_mdio_rd(mdio_priv, MDIO_DATA) & 0xffff; + value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff; return value; } diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index bb9887f98841..62f42921933d 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -111,6 +111,7 @@ do { \ #define IF_MODE_MASK 0x00000003 /* 30-31 Mask on i/f mode bits */ #define IF_MODE_10G 0x00000000 /* 30-31 10G interface */ +#define IF_MODE_MII 0x00000001 /* 30-31 MII interface */ #define IF_MODE_GMII 0x00000002 /* 30-31 GMII (1G) interface */ #define IF_MODE_RGMII 0x00000004 #define IF_MODE_RGMII_AUTO 0x00008000 @@ -442,6 +443,9 @@ static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, case PHY_INTERFACE_MODE_XGMII: tmp |= IF_MODE_10G; break; + case PHY_INTERFACE_MODE_MII: + tmp |= IF_MODE_MII; + break; default: tmp |= IF_MODE_GMII; if (phy_if == PHY_INTERFACE_MODE_RGMII || diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index d391a45cebb6..541de32ea662 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -58,7 +58,6 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define DEBUG #include <linux/kernel.h> #include <linux/string.h> diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 6d853f018d53..ef4e2febeb5b 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -70,9 +70,32 @@ static struct { module_param_named(debug, debug.msg_enable, int, 0); MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 0xffff=all)"); -static struct ucc_geth_info ugeth_primary_info = { +static int ucc_geth_thread_count(enum ucc_geth_num_of_threads idx) +{ + static const u8 count[] = { + [UCC_GETH_NUM_OF_THREADS_1] = 1, + [UCC_GETH_NUM_OF_THREADS_2] = 2, + [UCC_GETH_NUM_OF_THREADS_4] = 4, + [UCC_GETH_NUM_OF_THREADS_6] = 6, + [UCC_GETH_NUM_OF_THREADS_8] = 8, + }; + if (idx >= ARRAY_SIZE(count)) + return 0; + return count[idx]; +} + +static inline int ucc_geth_tx_queues(const struct ucc_geth_info *info) +{ + return 1; +} + +static inline int ucc_geth_rx_queues(const struct ucc_geth_info *info) +{ + return 1; +} + +static const struct ucc_geth_info ugeth_primary_info = { .uf_info = { - .bd_mem_part = MEM_PART_SYSTEM, .rtsm = UCC_FAST_SEND_IDLES_BETWEEN_FRAMES, .max_rx_buf_length = 1536, /* adjusted at startup if max-speed 1000 */ @@ -90,8 +113,6 @@ static struct ucc_geth_info ugeth_primary_info = { .tcrc = UCC_FAST_16_BIT_CRC, .synl = UCC_FAST_SYNC_LEN_NOT_USED, }, - .numQueuesTx = 1, - .numQueuesRx = 1, .extendedFilteringChainPointer = ((uint32_t) NULL), .typeorlen = 3072 /*1536 */ , .nonBackToBackIfgPart1 = 0x40, @@ -157,8 +178,6 @@ static struct ucc_geth_info ugeth_primary_info = { .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, }; -static struct ucc_geth_info ugeth_info[8]; - #ifdef DEBUG static void mem_disp(u8 *addr, int size) { @@ -558,7 +577,7 @@ static void dump_bds(struct ucc_geth_private *ugeth) int i; int length; - for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { + for (i = 0; i < ucc_geth_tx_queues(ugeth->ug_info); i++) { if (ugeth->p_tx_bd_ring[i]) { length = (ugeth->ug_info->bdRingLenTx[i] * @@ -567,7 +586,7 @@ static void dump_bds(struct ucc_geth_private *ugeth) mem_disp(ugeth->p_tx_bd_ring[i], length); } } - for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { + for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) { if (ugeth->p_rx_bd_ring[i]) { length = (ugeth->ug_info->bdRingLenRx[i] * @@ -671,32 +690,12 @@ static void dump_regs(struct ucc_geth_private *ugeth) in_be32(&ugeth->ug_regs->scam)); if (ugeth->p_thread_data_tx) { - int numThreadsTxNumerical; - switch (ugeth->ug_info->numThreadsTx) { - case UCC_GETH_NUM_OF_THREADS_1: - numThreadsTxNumerical = 1; - break; - case UCC_GETH_NUM_OF_THREADS_2: - numThreadsTxNumerical = 2; - break; - case UCC_GETH_NUM_OF_THREADS_4: - numThreadsTxNumerical = 4; - break; - case UCC_GETH_NUM_OF_THREADS_6: - numThreadsTxNumerical = 6; - break; - case UCC_GETH_NUM_OF_THREADS_8: - numThreadsTxNumerical = 8; - break; - default: - numThreadsTxNumerical = 0; - break; - } + int count = ucc_geth_thread_count(ugeth->ug_info->numThreadsTx); pr_info("Thread data TXs:\n"); pr_info("Base address: 0x%08x\n", (u32)ugeth->p_thread_data_tx); - for (i = 0; i < numThreadsTxNumerical; i++) { + for (i = 0; i < count; i++) { pr_info("Thread data TX[%d]:\n", i); pr_info("Base address: 0x%08x\n", (u32)&ugeth->p_thread_data_tx[i]); @@ -705,32 +704,12 @@ static void dump_regs(struct ucc_geth_private *ugeth) } } if (ugeth->p_thread_data_rx) { - int numThreadsRxNumerical; - switch (ugeth->ug_info->numThreadsRx) { - case UCC_GETH_NUM_OF_THREADS_1: - numThreadsRxNumerical = 1; - break; - case UCC_GETH_NUM_OF_THREADS_2: - numThreadsRxNumerical = 2; - break; - case UCC_GETH_NUM_OF_THREADS_4: - numThreadsRxNumerical = 4; - break; - case UCC_GETH_NUM_OF_THREADS_6: - numThreadsRxNumerical = 6; - break; - case UCC_GETH_NUM_OF_THREADS_8: - numThreadsRxNumerical = 8; - break; - default: - numThreadsRxNumerical = 0; - break; - } + int count = ucc_geth_thread_count(ugeth->ug_info->numThreadsRx); pr_info("Thread data RX:\n"); pr_info("Base address: 0x%08x\n", (u32)ugeth->p_thread_data_rx); - for (i = 0; i < numThreadsRxNumerical; i++) { + for (i = 0; i < count; i++) { pr_info("Thread data RX[%d]:\n", i); pr_info("Base address: 0x%08x\n", (u32)&ugeth->p_thread_data_rx[i]); @@ -905,7 +884,7 @@ static void dump_regs(struct ucc_geth_private *ugeth) if (ugeth->p_send_q_mem_reg) { pr_info("Send Q memory registers:\n"); pr_info("Base address: 0x%08x\n", (u32)ugeth->p_send_q_mem_reg); - for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { + for (i = 0; i < ucc_geth_tx_queues(ugeth->ug_info); i++) { pr_info("SQQD[%d]:\n", i); pr_info("Base address: 0x%08x\n", (u32)&ugeth->p_send_q_mem_reg->sqqd[i]); @@ -937,7 +916,7 @@ static void dump_regs(struct ucc_geth_private *ugeth) pr_info("RX IRQ coalescing tables:\n"); pr_info("Base address: 0x%08x\n", (u32)ugeth->p_rx_irq_coalescing_tbl); - for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { + for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) { pr_info("RX IRQ coalescing table entry[%d]:\n", i); pr_info("Base address: 0x%08x\n", (u32)&ugeth->p_rx_irq_coalescing_tbl-> @@ -959,7 +938,7 @@ static void dump_regs(struct ucc_geth_private *ugeth) if (ugeth->p_rx_bd_qs_tbl) { pr_info("RX BD QS tables:\n"); pr_info("Base address: 0x%08x\n", (u32)ugeth->p_rx_bd_qs_tbl); - for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { + for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) { pr_info("RX BD QS table[%d]:\n", i); pr_info("Base address: 0x%08x\n", (u32)&ugeth->p_rx_bd_qs_tbl[i]); @@ -1835,7 +1814,7 @@ static void ucc_geth_free_rx(struct ucc_geth_private *ugeth) ug_info = ugeth->ug_info; uf_info = &ug_info->uf_info; - for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { + for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) { if (ugeth->p_rx_bd_ring[i]) { /* Return existing data buffers in ring */ bd = ugeth->p_rx_bd_ring[i]; @@ -1856,12 +1835,7 @@ static void ucc_geth_free_rx(struct ucc_geth_private *ugeth) kfree(ugeth->rx_skbuff[i]); - if (ugeth->ug_info->uf_info.bd_mem_part == - MEM_PART_SYSTEM) - kfree((void *)ugeth->rx_bd_ring_offset[i]); - else if (ugeth->ug_info->uf_info.bd_mem_part == - MEM_PART_MURAM) - qe_muram_free(ugeth->rx_bd_ring_offset[i]); + kfree(ugeth->p_rx_bd_ring[i]); ugeth->p_rx_bd_ring[i] = NULL; } } @@ -1880,7 +1854,7 @@ static void ucc_geth_free_tx(struct ucc_geth_private *ugeth) ug_info = ugeth->ug_info; uf_info = &ug_info->uf_info; - for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { + for (i = 0; i < ucc_geth_tx_queues(ugeth->ug_info); i++) { bd = ugeth->p_tx_bd_ring[i]; if (!bd) continue; @@ -1898,15 +1872,8 @@ static void ucc_geth_free_tx(struct ucc_geth_private *ugeth) kfree(ugeth->tx_skbuff[i]); - if (ugeth->p_tx_bd_ring[i]) { - if (ugeth->ug_info->uf_info.bd_mem_part == - MEM_PART_SYSTEM) - kfree((void *)ugeth->tx_bd_ring_offset[i]); - else if (ugeth->ug_info->uf_info.bd_mem_part == - MEM_PART_MURAM) - qe_muram_free(ugeth->tx_bd_ring_offset[i]); - ugeth->p_tx_bd_ring[i] = NULL; - } + kfree(ugeth->p_tx_bd_ring[i]); + ugeth->p_tx_bd_ring[i] = NULL; } } @@ -1921,50 +1888,39 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth) ugeth->uccf = NULL; } - if (ugeth->p_thread_data_tx) { - qe_muram_free(ugeth->thread_dat_tx_offset); - ugeth->p_thread_data_tx = NULL; - } - if (ugeth->p_thread_data_rx) { - qe_muram_free(ugeth->thread_dat_rx_offset); - ugeth->p_thread_data_rx = NULL; - } - if (ugeth->p_exf_glbl_param) { - qe_muram_free(ugeth->exf_glbl_param_offset); - ugeth->p_exf_glbl_param = NULL; - } - if (ugeth->p_rx_glbl_pram) { - qe_muram_free(ugeth->rx_glbl_pram_offset); - ugeth->p_rx_glbl_pram = NULL; - } - if (ugeth->p_tx_glbl_pram) { - qe_muram_free(ugeth->tx_glbl_pram_offset); - ugeth->p_tx_glbl_pram = NULL; - } - if (ugeth->p_send_q_mem_reg) { - qe_muram_free(ugeth->send_q_mem_reg_offset); - ugeth->p_send_q_mem_reg = NULL; - } - if (ugeth->p_scheduler) { - qe_muram_free(ugeth->scheduler_offset); - ugeth->p_scheduler = NULL; - } - if (ugeth->p_tx_fw_statistics_pram) { - qe_muram_free(ugeth->tx_fw_statistics_pram_offset); - ugeth->p_tx_fw_statistics_pram = NULL; - } - if (ugeth->p_rx_fw_statistics_pram) { - qe_muram_free(ugeth->rx_fw_statistics_pram_offset); - ugeth->p_rx_fw_statistics_pram = NULL; - } - if (ugeth->p_rx_irq_coalescing_tbl) { - qe_muram_free(ugeth->rx_irq_coalescing_tbl_offset); - ugeth->p_rx_irq_coalescing_tbl = NULL; - } - if (ugeth->p_rx_bd_qs_tbl) { - qe_muram_free(ugeth->rx_bd_qs_tbl_offset); - ugeth->p_rx_bd_qs_tbl = NULL; - } + qe_muram_free_addr(ugeth->p_thread_data_tx); + ugeth->p_thread_data_tx = NULL; + + qe_muram_free_addr(ugeth->p_thread_data_rx); + ugeth->p_thread_data_rx = NULL; + + qe_muram_free_addr(ugeth->p_exf_glbl_param); + ugeth->p_exf_glbl_param = NULL; + + qe_muram_free_addr(ugeth->p_rx_glbl_pram); + ugeth->p_rx_glbl_pram = NULL; + + qe_muram_free_addr(ugeth->p_tx_glbl_pram); + ugeth->p_tx_glbl_pram = NULL; + + qe_muram_free_addr(ugeth->p_send_q_mem_reg); + ugeth->p_send_q_mem_reg = NULL; + + qe_muram_free_addr(ugeth->p_scheduler); + ugeth->p_scheduler = NULL; + + qe_muram_free_addr(ugeth->p_tx_fw_statistics_pram); + ugeth->p_tx_fw_statistics_pram = NULL; + + qe_muram_free_addr(ugeth->p_rx_fw_statistics_pram); + ugeth->p_rx_fw_statistics_pram = NULL; + + qe_muram_free_addr(ugeth->p_rx_irq_coalescing_tbl); + ugeth->p_rx_irq_coalescing_tbl = NULL; + + qe_muram_free_addr(ugeth->p_rx_bd_qs_tbl); + ugeth->p_rx_bd_qs_tbl = NULL; + if (ugeth->p_init_enet_param_shadow) { return_init_enet_entries(ugeth, &(ugeth->p_init_enet_param_shadow-> @@ -2073,15 +2029,8 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) ug_info = ugeth->ug_info; uf_info = &ug_info->uf_info; - if (!((uf_info->bd_mem_part == MEM_PART_SYSTEM) || - (uf_info->bd_mem_part == MEM_PART_MURAM))) { - if (netif_msg_probe(ugeth)) - pr_err("Bad memory partition value\n"); - return -EINVAL; - } - /* Rx BD lengths */ - for (i = 0; i < ug_info->numQueuesRx; i++) { + for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) { if ((ug_info->bdRingLenRx[i] < UCC_GETH_RX_BD_RING_SIZE_MIN) || (ug_info->bdRingLenRx[i] % UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT)) { @@ -2092,7 +2041,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) } /* Tx BD lengths */ - for (i = 0; i < ug_info->numQueuesTx; i++) { + for (i = 0; i < ucc_geth_tx_queues(ug_info); i++) { if (ug_info->bdRingLenTx[i] < UCC_GETH_TX_BD_RING_SIZE_MIN) { if (netif_msg_probe(ugeth)) pr_err("Tx BD ring length must be no smaller than 2\n"); @@ -2109,14 +2058,14 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) } /* num Tx queues */ - if (ug_info->numQueuesTx > NUM_TX_QUEUES) { + if (ucc_geth_tx_queues(ug_info) > NUM_TX_QUEUES) { if (netif_msg_probe(ugeth)) pr_err("number of tx queues too large\n"); return -EINVAL; } /* num Rx queues */ - if (ug_info->numQueuesRx > NUM_RX_QUEUES) { + if (ucc_geth_rx_queues(ug_info) > NUM_RX_QUEUES) { if (netif_msg_probe(ugeth)) pr_err("number of rx queues too large\n"); return -EINVAL; @@ -2124,7 +2073,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) /* l2qt */ for (i = 0; i < UCC_GETH_VLAN_PRIORITY_MAX; i++) { - if (ug_info->l2qt[i] >= ug_info->numQueuesRx) { + if (ug_info->l2qt[i] >= ucc_geth_rx_queues(ug_info)) { if (netif_msg_probe(ugeth)) pr_err("VLAN priority table entry must not be larger than number of Rx queues\n"); return -EINVAL; @@ -2133,7 +2082,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) /* l3qt */ for (i = 0; i < UCC_GETH_IP_PRIORITY_MAX; i++) { - if (ug_info->l3qt[i] >= ug_info->numQueuesRx) { + if (ug_info->l3qt[i] >= ucc_geth_rx_queues(ug_info)) { if (netif_msg_probe(ugeth)) pr_err("IP priority table entry must not be larger than number of Rx queues\n"); return -EINVAL; @@ -2156,10 +2105,10 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) /* Generate uccm_mask for receive */ uf_info->uccm_mask = ug_info->eventRegMask & UCCE_OTHER;/* Errors */ - for (i = 0; i < ug_info->numQueuesRx; i++) + for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) uf_info->uccm_mask |= (UCC_GETH_UCCE_RXF0 << i); - for (i = 0; i < ug_info->numQueuesTx; i++) + for (i = 0; i < ucc_geth_tx_queues(ug_info); i++) uf_info->uccm_mask |= (UCC_GETH_UCCE_TXB0 << i); /* Initialize the general fast UCC block. */ if (ucc_fast_init(uf_info, &ugeth->uccf)) { @@ -2198,53 +2147,32 @@ static int ucc_geth_alloc_tx(struct ucc_geth_private *ugeth) uf_info = &ug_info->uf_info; /* Allocate Tx bds */ - for (j = 0; j < ug_info->numQueuesTx; j++) { - /* Allocate in multiple of - UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT, - according to spec */ - length = ((ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)) - / UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) - * UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; - if ((ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)) % - UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) - length += UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; - if (uf_info->bd_mem_part == MEM_PART_SYSTEM) { - u32 align = 4; - if (UCC_GETH_TX_BD_RING_ALIGNMENT > 4) - align = UCC_GETH_TX_BD_RING_ALIGNMENT; - ugeth->tx_bd_ring_offset[j] = - (u32) kmalloc((u32) (length + align), GFP_KERNEL); - - if (ugeth->tx_bd_ring_offset[j] != 0) - ugeth->p_tx_bd_ring[j] = - (u8 __iomem *)((ugeth->tx_bd_ring_offset[j] + - align) & ~(align - 1)); - } else if (uf_info->bd_mem_part == MEM_PART_MURAM) { - ugeth->tx_bd_ring_offset[j] = - qe_muram_alloc(length, - UCC_GETH_TX_BD_RING_ALIGNMENT); - if (!IS_ERR_VALUE(ugeth->tx_bd_ring_offset[j])) - ugeth->p_tx_bd_ring[j] = - (u8 __iomem *) qe_muram_addr(ugeth-> - tx_bd_ring_offset[j]); - } + for (j = 0; j < ucc_geth_tx_queues(ug_info); j++) { + u32 align = max(UCC_GETH_TX_BD_RING_ALIGNMENT, + UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT); + u32 alloc; + + length = ug_info->bdRingLenTx[j] * sizeof(struct qe_bd); + alloc = round_up(length, align); + alloc = roundup_pow_of_two(alloc); + + ugeth->p_tx_bd_ring[j] = kmalloc(alloc, GFP_KERNEL); + if (!ugeth->p_tx_bd_ring[j]) { if (netif_msg_ifup(ugeth)) pr_err("Can not allocate memory for Tx bd rings\n"); return -ENOMEM; } /* Zero unused end of bd ring, according to spec */ - memset_io((void __iomem *)(ugeth->p_tx_bd_ring[j] + - ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)), 0, - length - ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)); + memset(ugeth->p_tx_bd_ring[j] + length, 0, alloc - length); } /* Init Tx bds */ - for (j = 0; j < ug_info->numQueuesTx; j++) { + for (j = 0; j < ucc_geth_tx_queues(ug_info); j++) { /* Setup the skbuff rings */ ugeth->tx_skbuff[j] = - kmalloc_array(ugeth->ug_info->bdRingLenTx[j], - sizeof(struct sk_buff *), GFP_KERNEL); + kcalloc(ugeth->ug_info->bdRingLenTx[j], + sizeof(struct sk_buff *), GFP_KERNEL); if (ugeth->tx_skbuff[j] == NULL) { if (netif_msg_ifup(ugeth)) @@ -2252,9 +2180,6 @@ static int ucc_geth_alloc_tx(struct ucc_geth_private *ugeth) return -ENOMEM; } - for (i = 0; i < ugeth->ug_info->bdRingLenTx[j]; i++) - ugeth->tx_skbuff[j][i] = NULL; - ugeth->skb_curtx[j] = ugeth->skb_dirtytx[j] = 0; bd = ugeth->confBd[j] = ugeth->txBd[j] = ugeth->p_tx_bd_ring[j]; for (i = 0; i < ug_info->bdRingLenTx[j]; i++) { @@ -2284,27 +2209,15 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth) uf_info = &ug_info->uf_info; /* Allocate Rx bds */ - for (j = 0; j < ug_info->numQueuesRx; j++) { + for (j = 0; j < ucc_geth_rx_queues(ug_info); j++) { + u32 align = UCC_GETH_RX_BD_RING_ALIGNMENT; + u32 alloc; + length = ug_info->bdRingLenRx[j] * sizeof(struct qe_bd); - if (uf_info->bd_mem_part == MEM_PART_SYSTEM) { - u32 align = 4; - if (UCC_GETH_RX_BD_RING_ALIGNMENT > 4) - align = UCC_GETH_RX_BD_RING_ALIGNMENT; - ugeth->rx_bd_ring_offset[j] = - (u32) kmalloc((u32) (length + align), GFP_KERNEL); - if (ugeth->rx_bd_ring_offset[j] != 0) - ugeth->p_rx_bd_ring[j] = - (u8 __iomem *)((ugeth->rx_bd_ring_offset[j] + - align) & ~(align - 1)); - } else if (uf_info->bd_mem_part == MEM_PART_MURAM) { - ugeth->rx_bd_ring_offset[j] = - qe_muram_alloc(length, - UCC_GETH_RX_BD_RING_ALIGNMENT); - if (!IS_ERR_VALUE(ugeth->rx_bd_ring_offset[j])) - ugeth->p_rx_bd_ring[j] = - (u8 __iomem *) qe_muram_addr(ugeth-> - rx_bd_ring_offset[j]); - } + alloc = round_up(length, align); + alloc = roundup_pow_of_two(alloc); + + ugeth->p_rx_bd_ring[j] = kmalloc(alloc, GFP_KERNEL); if (!ugeth->p_rx_bd_ring[j]) { if (netif_msg_ifup(ugeth)) pr_err("Can not allocate memory for Rx bd rings\n"); @@ -2313,11 +2226,11 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth) } /* Init Rx bds */ - for (j = 0; j < ug_info->numQueuesRx; j++) { + for (j = 0; j < ucc_geth_rx_queues(ug_info); j++) { /* Setup the skbuff rings */ ugeth->rx_skbuff[j] = - kmalloc_array(ugeth->ug_info->bdRingLenRx[j], - sizeof(struct sk_buff *), GFP_KERNEL); + kcalloc(ugeth->ug_info->bdRingLenRx[j], + sizeof(struct sk_buff *), GFP_KERNEL); if (ugeth->rx_skbuff[j] == NULL) { if (netif_msg_ifup(ugeth)) @@ -2325,9 +2238,6 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth) return -ENOMEM; } - for (i = 0; i < ugeth->ug_info->bdRingLenRx[j]; i++) - ugeth->rx_skbuff[j][i] = NULL; - ugeth->skb_currx[j] = 0; bd = ugeth->rxBd[j] = ugeth->p_rx_bd_ring[j]; for (i = 0; i < ug_info->bdRingLenRx[j]; i++) { @@ -2359,10 +2269,10 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) u32 init_enet_pram_offset, cecr_subblock, command; u32 ifstat, i, j, size, l2qt, l3qt; u16 temoder = UCC_GETH_TEMODER_INIT; - u16 test; u8 function_code = 0; u8 __iomem *endOfRing; u8 numThreadsRxNumerical, numThreadsTxNumerical; + s32 rx_glbl_pram_offset, tx_glbl_pram_offset; ugeth_vdbg("%s: IN", __func__); uccf = ugeth->uccf; @@ -2371,45 +2281,15 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) uf_regs = uccf->uf_regs; ug_regs = ugeth->ug_regs; - switch (ug_info->numThreadsRx) { - case UCC_GETH_NUM_OF_THREADS_1: - numThreadsRxNumerical = 1; - break; - case UCC_GETH_NUM_OF_THREADS_2: - numThreadsRxNumerical = 2; - break; - case UCC_GETH_NUM_OF_THREADS_4: - numThreadsRxNumerical = 4; - break; - case UCC_GETH_NUM_OF_THREADS_6: - numThreadsRxNumerical = 6; - break; - case UCC_GETH_NUM_OF_THREADS_8: - numThreadsRxNumerical = 8; - break; - default: + numThreadsRxNumerical = ucc_geth_thread_count(ug_info->numThreadsRx); + if (!numThreadsRxNumerical) { if (netif_msg_ifup(ugeth)) pr_err("Bad number of Rx threads value\n"); return -EINVAL; } - switch (ug_info->numThreadsTx) { - case UCC_GETH_NUM_OF_THREADS_1: - numThreadsTxNumerical = 1; - break; - case UCC_GETH_NUM_OF_THREADS_2: - numThreadsTxNumerical = 2; - break; - case UCC_GETH_NUM_OF_THREADS_4: - numThreadsTxNumerical = 4; - break; - case UCC_GETH_NUM_OF_THREADS_6: - numThreadsTxNumerical = 6; - break; - case UCC_GETH_NUM_OF_THREADS_8: - numThreadsTxNumerical = 8; - break; - default: + numThreadsTxNumerical = ucc_geth_thread_count(ug_info->numThreadsTx); + if (!numThreadsTxNumerical) { if (netif_msg_ifup(ugeth)) pr_err("Bad number of Tx threads value\n"); return -EINVAL; @@ -2507,20 +2387,15 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) */ /* Tx global PRAM */ /* Allocate global tx parameter RAM page */ - ugeth->tx_glbl_pram_offset = + tx_glbl_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_tx_global_pram), UCC_GETH_TX_GLOBAL_PRAM_ALIGNMENT); - if (IS_ERR_VALUE(ugeth->tx_glbl_pram_offset)) { + if (tx_glbl_pram_offset < 0) { if (netif_msg_ifup(ugeth)) pr_err("Can not allocate DPRAM memory for p_tx_glbl_pram\n"); return -ENOMEM; } - ugeth->p_tx_glbl_pram = - (struct ucc_geth_tx_global_pram __iomem *) qe_muram_addr(ugeth-> - tx_glbl_pram_offset); - /* Zero out p_tx_glbl_pram */ - memset_io((void __iomem *)ugeth->p_tx_glbl_pram, 0, sizeof(struct ucc_geth_tx_global_pram)); - + ugeth->p_tx_glbl_pram = qe_muram_addr(tx_glbl_pram_offset); /* Fill global PRAM */ /* TQPTR */ @@ -2554,7 +2429,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) /* SQPTR */ /* Size varies with number of Tx queues */ ugeth->send_q_mem_reg_offset = - qe_muram_alloc(ug_info->numQueuesTx * + qe_muram_alloc(ucc_geth_tx_queues(ug_info) * sizeof(struct ucc_geth_send_queue_qd), UCC_GETH_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT); if (IS_ERR_VALUE(ugeth->send_q_mem_reg_offset)) { @@ -2570,29 +2445,20 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) /* Setup the table */ /* Assume BD rings are already established */ - for (i = 0; i < ug_info->numQueuesTx; i++) { + for (i = 0; i < ucc_geth_tx_queues(ug_info); i++) { endOfRing = ugeth->p_tx_bd_ring[i] + (ug_info->bdRingLenTx[i] - 1) * sizeof(struct qe_bd); - if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) { - out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base, - (u32) virt_to_phys(ugeth->p_tx_bd_ring[i])); - out_be32(&ugeth->p_send_q_mem_reg->sqqd[i]. - last_bd_completed_address, - (u32) virt_to_phys(endOfRing)); - } else if (ugeth->ug_info->uf_info.bd_mem_part == - MEM_PART_MURAM) { - out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base, - (u32)qe_muram_dma(ugeth->p_tx_bd_ring[i])); - out_be32(&ugeth->p_send_q_mem_reg->sqqd[i]. - last_bd_completed_address, - (u32)qe_muram_dma(endOfRing)); - } + out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base, + (u32) virt_to_phys(ugeth->p_tx_bd_ring[i])); + out_be32(&ugeth->p_send_q_mem_reg->sqqd[i]. + last_bd_completed_address, + (u32) virt_to_phys(endOfRing)); } /* schedulerbasepointer */ - if (ug_info->numQueuesTx > 1) { + if (ucc_geth_tx_queues(ug_info) > 1) { /* scheduler exists only if more than 1 tx queue */ ugeth->scheduler_offset = qe_muram_alloc(sizeof(struct ucc_geth_scheduler), @@ -2608,8 +2474,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) scheduler_offset); out_be32(&ugeth->p_tx_glbl_pram->schedulerbasepointer, ugeth->scheduler_offset); - /* Zero out p_scheduler */ - memset_io((void __iomem *)ugeth->p_scheduler, 0, sizeof(struct ucc_geth_scheduler)); /* Set values in scheduler */ out_be32(&ugeth->p_scheduler->mblinterval, @@ -2652,23 +2516,18 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->p_tx_fw_statistics_pram = (struct ucc_geth_tx_firmware_statistics_pram __iomem *) qe_muram_addr(ugeth->tx_fw_statistics_pram_offset); - /* Zero out p_tx_fw_statistics_pram */ - memset_io((void __iomem *)ugeth->p_tx_fw_statistics_pram, - 0, sizeof(struct ucc_geth_tx_firmware_statistics_pram)); } /* temoder */ /* Already has speed set */ - if (ug_info->numQueuesTx > 1) + if (ucc_geth_tx_queues(ug_info) > 1) temoder |= TEMODER_SCHEDULER_ENABLE; if (ug_info->ipCheckSumGenerate) temoder |= TEMODER_IP_CHECKSUM_GENERATE; - temoder |= ((ug_info->numQueuesTx - 1) << TEMODER_NUM_OF_QUEUES_SHIFT); + temoder |= ((ucc_geth_tx_queues(ug_info) - 1) << TEMODER_NUM_OF_QUEUES_SHIFT); out_be16(&ugeth->p_tx_glbl_pram->temoder, temoder); - test = in_be16(&ugeth->p_tx_glbl_pram->temoder); - /* Function code register value to be used later */ function_code = UCC_BMR_BO_BE | UCC_BMR_GBL; /* Required for QE */ @@ -2678,20 +2537,15 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) /* Rx global PRAM */ /* Allocate global rx parameter RAM page */ - ugeth->rx_glbl_pram_offset = + rx_glbl_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_rx_global_pram), UCC_GETH_RX_GLOBAL_PRAM_ALIGNMENT); - if (IS_ERR_VALUE(ugeth->rx_glbl_pram_offset)) { + if (rx_glbl_pram_offset < 0) { if (netif_msg_ifup(ugeth)) pr_err("Can not allocate DPRAM memory for p_rx_glbl_pram\n"); return -ENOMEM; } - ugeth->p_rx_glbl_pram = - (struct ucc_geth_rx_global_pram __iomem *) qe_muram_addr(ugeth-> - rx_glbl_pram_offset); - /* Zero out p_rx_glbl_pram */ - memset_io((void __iomem *)ugeth->p_rx_glbl_pram, 0, sizeof(struct ucc_geth_rx_global_pram)); - + ugeth->p_rx_glbl_pram = qe_muram_addr(rx_glbl_pram_offset); /* Fill global PRAM */ /* RQPTR */ @@ -2729,16 +2583,13 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->p_rx_fw_statistics_pram = (struct ucc_geth_rx_firmware_statistics_pram __iomem *) qe_muram_addr(ugeth->rx_fw_statistics_pram_offset); - /* Zero out p_rx_fw_statistics_pram */ - memset_io((void __iomem *)ugeth->p_rx_fw_statistics_pram, 0, - sizeof(struct ucc_geth_rx_firmware_statistics_pram)); } /* intCoalescingPtr */ /* Size varies with number of Rx queues */ ugeth->rx_irq_coalescing_tbl_offset = - qe_muram_alloc(ug_info->numQueuesRx * + qe_muram_alloc(ucc_geth_rx_queues(ug_info) * sizeof(struct ucc_geth_rx_interrupt_coalescing_entry) + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT); if (IS_ERR_VALUE(ugeth->rx_irq_coalescing_tbl_offset)) { @@ -2754,7 +2605,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->rx_irq_coalescing_tbl_offset); /* Fill interrupt coalescing table */ - for (i = 0; i < ug_info->numQueuesRx; i++) { + for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) { out_be32(&ugeth->p_rx_irq_coalescing_tbl->coalescingentry[i]. interruptcoalescingmaxvalue, ug_info->interruptcoalescingmaxvalue[i]); @@ -2803,7 +2654,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) /* RBDQPTR */ /* Size varies with number of Rx queues */ ugeth->rx_bd_qs_tbl_offset = - qe_muram_alloc(ug_info->numQueuesRx * + qe_muram_alloc(ucc_geth_rx_queues(ug_info) * (sizeof(struct ucc_geth_rx_bd_queues_entry) + sizeof(struct ucc_geth_rx_prefetched_bds)), UCC_GETH_RX_BD_QUEUES_ALIGNMENT); @@ -2817,23 +2668,12 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) (struct ucc_geth_rx_bd_queues_entry __iomem *) qe_muram_addr(ugeth-> rx_bd_qs_tbl_offset); out_be32(&ugeth->p_rx_glbl_pram->rbdqptr, ugeth->rx_bd_qs_tbl_offset); - /* Zero out p_rx_bd_qs_tbl */ - memset_io((void __iomem *)ugeth->p_rx_bd_qs_tbl, - 0, - ug_info->numQueuesRx * (sizeof(struct ucc_geth_rx_bd_queues_entry) + - sizeof(struct ucc_geth_rx_prefetched_bds))); /* Setup the table */ /* Assume BD rings are already established */ - for (i = 0; i < ug_info->numQueuesRx; i++) { - if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) { - out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr, - (u32) virt_to_phys(ugeth->p_rx_bd_ring[i])); - } else if (ugeth->ug_info->uf_info.bd_mem_part == - MEM_PART_MURAM) { - out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr, - (u32)qe_muram_dma(ugeth->p_rx_bd_ring[i])); - } + for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) { + out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr, + (u32) virt_to_phys(ugeth->p_rx_bd_ring[i])); /* rest of fields handled by QE */ } @@ -2854,7 +2694,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ug_info-> vlanOperationNonTagged << REMODER_VLAN_OPERATION_NON_TAGGED_SHIFT; remoder |= ug_info->rxQoSMode << REMODER_RX_QOS_MODE_SHIFT; - remoder |= ((ug_info->numQueuesRx - 1) << REMODER_NUM_OF_QUEUES_SHIFT); + remoder |= ((ucc_geth_rx_queues(ug_info) - 1) << REMODER_NUM_OF_QUEUES_SHIFT); if (ug_info->ipCheckSumCheck) remoder |= REMODER_IP_CHECKSUM_CHECK; if (ug_info->ipAddressAlignment) @@ -2937,14 +2777,11 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) * allocated resources can be released when the channel is freed. */ if (!(ugeth->p_init_enet_param_shadow = - kmalloc(sizeof(struct ucc_geth_init_pram), GFP_KERNEL))) { + kzalloc(sizeof(struct ucc_geth_init_pram), GFP_KERNEL))) { if (netif_msg_ifup(ugeth)) pr_err("Can not allocate memory for p_UccInitEnetParamShadows\n"); return -ENOMEM; } - /* Zero out *p_init_enet_param_shadow */ - memset((char *)ugeth->p_init_enet_param_shadow, - 0, sizeof(struct ucc_geth_init_pram)); /* Fill shadow InitEnet command parameter structure */ @@ -2964,7 +2801,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ((u32) ug_info->numThreadsTx) << ENET_INIT_PARAM_TGF_SHIFT; ugeth->p_init_enet_param_shadow->rgftgfrxglobal |= - ugeth->rx_glbl_pram_offset | ug_info->riscRx; + rx_glbl_pram_offset | ug_info->riscRx; if ((ug_info->largestexternallookupkeysize != QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE) && (ug_info->largestexternallookupkeysize != @@ -3002,7 +2839,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) } ugeth->p_init_enet_param_shadow->txglobal = - ugeth->tx_glbl_pram_offset | ug_info->riscTx; + tx_glbl_pram_offset | ug_info->riscTx; if ((ret_val = fill_init_enet_entries(ugeth, &(ugeth->p_init_enet_param_shadow-> @@ -3016,7 +2853,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) } /* Load Rx bds with buffers */ - for (i = 0; i < ug_info->numQueuesRx; i++) { + for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) { if ((ret_val = rx_bd_buffer_set(ugeth, (u8) i)) != 0) { if (netif_msg_ifup(ugeth)) pr_err("Can not fill Rx bds with buffers\n"); @@ -3287,12 +3124,12 @@ static int ucc_geth_poll(struct napi_struct *napi, int budget) /* Tx event processing */ spin_lock(&ugeth->lock); - for (i = 0; i < ug_info->numQueuesTx; i++) + for (i = 0; i < ucc_geth_tx_queues(ug_info); i++) ucc_geth_tx(ugeth->ndev, i); spin_unlock(&ugeth->lock); howmany = 0; - for (i = 0; i < ug_info->numQueuesRx; i++) + for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) howmany += ucc_geth_rx(ugeth, i, budget - howmany); if (howmany < budget) { @@ -3685,6 +3522,36 @@ static const struct net_device_ops ucc_geth_netdev_ops = { #endif }; +static int ucc_geth_parse_clock(struct device_node *np, const char *which, + enum qe_clock *out) +{ + const char *sprop; + char buf[24]; + + snprintf(buf, sizeof(buf), "%s-clock-name", which); + sprop = of_get_property(np, buf, NULL); + if (sprop) { + *out = qe_clock_source(sprop); + } else { + u32 val; + + snprintf(buf, sizeof(buf), "%s-clock", which); + if (of_property_read_u32(np, buf, &val)) { + /* If both *-clock-name and *-clock are missing, + * we want to tell people to use *-clock-name. + */ + pr_err("missing %s-clock-name property\n", buf); + return -EINVAL; + } + *out = val; + } + if (*out < QE_CLK_NONE || *out > QE_CLK24) { + pr_err("invalid %s property\n", buf); + return -EINVAL; + } + return 0; +} + static int ucc_geth_probe(struct platform_device* ofdev) { struct device *device = &ofdev->dev; @@ -3695,7 +3562,6 @@ static int ucc_geth_probe(struct platform_device* ofdev) struct resource res; int err, ucc_num, max_speed = 0; const unsigned int *prop; - const char *sprop; const void *mac_addr; phy_interface_t phy_interface; static const int enet_to_speed[] = { @@ -3725,62 +3591,23 @@ static int ucc_geth_probe(struct platform_device* ofdev) if ((ucc_num < 0) || (ucc_num > 7)) return -ENODEV; - ug_info = &ugeth_info[ucc_num]; - if (ug_info == NULL) { - if (netif_msg_probe(&debug)) - pr_err("[%d] Missing additional data!\n", ucc_num); - return -ENODEV; - } + ug_info = kmalloc(sizeof(*ug_info), GFP_KERNEL); + if (ug_info == NULL) + return -ENOMEM; + memcpy(ug_info, &ugeth_primary_info, sizeof(*ug_info)); ug_info->uf_info.ucc_num = ucc_num; - sprop = of_get_property(np, "rx-clock-name", NULL); - if (sprop) { - ug_info->uf_info.rx_clock = qe_clock_source(sprop); - if ((ug_info->uf_info.rx_clock < QE_CLK_NONE) || - (ug_info->uf_info.rx_clock > QE_CLK24)) { - pr_err("invalid rx-clock-name property\n"); - return -EINVAL; - } - } else { - prop = of_get_property(np, "rx-clock", NULL); - if (!prop) { - /* If both rx-clock-name and rx-clock are missing, - we want to tell people to use rx-clock-name. */ - pr_err("missing rx-clock-name property\n"); - return -EINVAL; - } - if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) { - pr_err("invalid rx-clock property\n"); - return -EINVAL; - } - ug_info->uf_info.rx_clock = *prop; - } - - sprop = of_get_property(np, "tx-clock-name", NULL); - if (sprop) { - ug_info->uf_info.tx_clock = qe_clock_source(sprop); - if ((ug_info->uf_info.tx_clock < QE_CLK_NONE) || - (ug_info->uf_info.tx_clock > QE_CLK24)) { - pr_err("invalid tx-clock-name property\n"); - return -EINVAL; - } - } else { - prop = of_get_property(np, "tx-clock", NULL); - if (!prop) { - pr_err("missing tx-clock-name property\n"); - return -EINVAL; - } - if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) { - pr_err("invalid tx-clock property\n"); - return -EINVAL; - } - ug_info->uf_info.tx_clock = *prop; - } + err = ucc_geth_parse_clock(np, "rx", &ug_info->uf_info.rx_clock); + if (err) + goto err_free_info; + err = ucc_geth_parse_clock(np, "tx", &ug_info->uf_info.tx_clock); + if (err) + goto err_free_info; err = of_address_to_resource(np, 0, &res); if (err) - return -EINVAL; + goto err_free_info; ug_info->uf_info.regs = res.start; ug_info->uf_info.irq = irq_of_parse_and_map(np, 0); @@ -3793,7 +3620,7 @@ static int ucc_geth_probe(struct platform_device* ofdev) */ err = of_phy_register_fixed_link(np); if (err) - return err; + goto err_free_info; ug_info->phy_node = of_node_get(np); } @@ -3924,6 +3751,8 @@ err_deregister_fixed_link: of_phy_deregister_fixed_link(np); of_node_put(ug_info->tbi_node); of_node_put(ug_info->phy_node); +err_free_info: + kfree(ug_info); return err; } @@ -3940,6 +3769,7 @@ static int ucc_geth_remove(struct platform_device* ofdev) of_phy_deregister_fixed_link(np); of_node_put(ugeth->ug_info->tbi_node); of_node_put(ugeth->ug_info->phy_node); + kfree(ugeth->ug_info); free_netdev(dev); return 0; @@ -3968,17 +3798,10 @@ static struct platform_driver ucc_geth_driver = { static int __init ucc_geth_init(void) { - int i, ret; - if (netif_msg_drv(&debug)) pr_info(DRV_DESC "\n"); - for (i = 0; i < 8; i++) - memcpy(&(ugeth_info[i]), &ugeth_primary_info, - sizeof(ugeth_primary_info)); - - ret = platform_driver_register(&ucc_geth_driver); - return ret; + return platform_driver_register(&ucc_geth_driver); } static void __exit ucc_geth_exit(void) diff --git a/drivers/net/ethernet/freescale/ucc_geth.h b/drivers/net/ethernet/freescale/ucc_geth.h index 11d4bf5dc21f..4294ed096ebb 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.h +++ b/drivers/net/ethernet/freescale/ucc_geth.h @@ -1076,8 +1076,6 @@ struct ucc_geth_tad_params { /* GETH protocol initialization structure */ struct ucc_geth_info { struct ucc_fast_info uf_info; - u8 numQueuesTx; - u8 numQueuesRx; int ipCheckSumCheck; int ipCheckSumGenerate; int rxExtendedFiltering; @@ -1165,9 +1163,7 @@ struct ucc_geth_private { struct ucc_geth_exf_global_pram __iomem *p_exf_glbl_param; u32 exf_glbl_param_offset; struct ucc_geth_rx_global_pram __iomem *p_rx_glbl_pram; - u32 rx_glbl_pram_offset; struct ucc_geth_tx_global_pram __iomem *p_tx_glbl_pram; - u32 tx_glbl_pram_offset; struct ucc_geth_send_queue_mem_region __iomem *p_send_q_mem_reg; u32 send_q_mem_reg_offset; struct ucc_geth_thread_data_tx __iomem *p_thread_data_tx; @@ -1185,9 +1181,7 @@ struct ucc_geth_private { struct ucc_geth_rx_bd_queues_entry __iomem *p_rx_bd_qs_tbl; u32 rx_bd_qs_tbl_offset; u8 __iomem *p_tx_bd_ring[NUM_TX_QUEUES]; - u32 tx_bd_ring_offset[NUM_TX_QUEUES]; u8 __iomem *p_rx_bd_ring[NUM_RX_QUEUES]; - u32 rx_bd_ring_offset[NUM_RX_QUEUES]; u8 __iomem *confBd[NUM_TX_QUEUES]; u8 __iomem *txBd[NUM_TX_QUEUES]; u8 __iomem *rxBd[NUM_RX_QUEUES]; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 858cb293152a..5d7824d2b4d4 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1502,7 +1502,7 @@ static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, { struct hns_nic_priv *priv = netdev_priv(ndev); - assert(skb->queue_mapping < ndev->ae_handle->q_num); + assert(skb->queue_mapping < priv->ae_handle->q_num); return hns_nic_net_xmit_hw(ndev, skb, &tx_ring_data(priv, skb->queue_mapping)); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 405e49033417..512080640cbc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -1070,7 +1070,7 @@ static bool hns3_check_hw_tx_csum(struct sk_buff *skb) * HW checksum of the non-IP packets and GSO packets is handled at * different place in the following code */ - if (skb->csum_not_inet || skb_is_gso(skb) || + if (skb_csum_is_sctp(skb) || skb_is_gso(skb) || !test_bit(HNS3_NIC_STATE_HW_TX_CSUM_ENABLE, &priv->state)) return false; diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 8820c98ea891..a40af510018a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1384,10 +1384,10 @@ static int ibmvnic_close(struct net_device *netdev) /** * build_hdr_data - creates L2/L3/L4 header data buffer - * @hdr_field - bitfield determining needed headers - * @skb - socket buffer - * @hdr_len - array of header lengths - * @tot_len - total length of data + * @hdr_field: bitfield determining needed headers + * @skb: socket buffer + * @hdr_len: array of header lengths + * @hdr_data: buffer to write the header to * * Reads hdr_field to determine which headers are needed by firmware. * Builds a buffer containing these headers. Saves individual header @@ -1444,11 +1444,11 @@ static int build_hdr_data(u8 hdr_field, struct sk_buff *skb, /** * create_hdr_descs - create header and header extension descriptors - * @hdr_field - bitfield determining needed headers - * @data - buffer containing header data - * @len - length of data buffer - * @hdr_len - array of individual header lengths - * @scrq_arr - descriptor array + * @hdr_field: bitfield determining needed headers + * @hdr_data: buffer containing header data + * @len: length of data buffer + * @hdr_len: array of individual header lengths + * @scrq_arr: descriptor array * * Creates header and, if needed, header extension descriptors and * places them in a descriptor array, scrq_arr @@ -1496,10 +1496,9 @@ static int create_hdr_descs(u8 hdr_field, u8 *hdr_data, int len, int *hdr_len, /** * build_hdr_descs_arr - build a header descriptor array - * @skb - socket buffer - * @num_entries - number of descriptors to be sent - * @subcrq - first TX descriptor - * @hdr_field - bit field determining which headers will be sent + * @txbuff: tx buffer + * @num_entries: number of descriptors to be sent + * @hdr_field: bit field determining which headers will be sent * * This function will build a TX descriptor array with applicable * L2/L3/L4 packet header descriptors to be sent by send_subcrq_indirect. @@ -1925,93 +1924,7 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p) return rc; } -/** - * do_change_param_reset returns zero if we are able to keep processing reset - * events, or non-zero if we hit a fatal error and must halt. - */ -static int do_change_param_reset(struct ibmvnic_adapter *adapter, - struct ibmvnic_rwi *rwi, - u32 reset_state) -{ - struct net_device *netdev = adapter->netdev; - int i, rc; - - netdev_dbg(adapter->netdev, "Change param resetting driver (%d)\n", - rwi->reset_reason); - - netif_carrier_off(netdev); - adapter->reset_reason = rwi->reset_reason; - - ibmvnic_cleanup(netdev); - - if (reset_state == VNIC_OPEN) { - rc = __ibmvnic_close(netdev); - if (rc) - goto out; - } - - release_resources(adapter); - release_sub_crqs(adapter, 1); - release_crq_queue(adapter); - - adapter->state = VNIC_PROBED; - - rc = init_crq_queue(adapter); - - if (rc) { - netdev_err(adapter->netdev, - "Couldn't initialize crq. rc=%d\n", rc); - return rc; - } - - rc = ibmvnic_reset_init(adapter, true); - if (rc) { - rc = IBMVNIC_INIT_FAILED; - goto out; - } - - /* If the adapter was in PROBE state prior to the reset, - * exit here. - */ - if (reset_state == VNIC_PROBED) - goto out; - - rc = ibmvnic_login(netdev); - if (rc) { - goto out; - } - - rc = init_resources(adapter); - if (rc) - goto out; - - ibmvnic_disable_irqs(adapter); - - adapter->state = VNIC_CLOSED; - - if (reset_state == VNIC_CLOSED) - return 0; - - rc = __ibmvnic_open(netdev); - if (rc) { - rc = IBMVNIC_OPEN_FAILED; - goto out; - } - - /* refresh device's multicast list */ - ibmvnic_set_multi(netdev); - - /* kick napi */ - for (i = 0; i < adapter->req_rx_queues; i++) - napi_schedule(&adapter->napi[i]); - -out: - if (rc) - adapter->state = reset_state; - return rc; -} - -/** +/* * do_reset returns zero if we are able to keep processing reset events, or * non-zero if we hit a fatal error and must halt. */ @@ -2028,7 +1941,11 @@ static int do_reset(struct ibmvnic_adapter *adapter, adapter->state, adapter->failover_pending, rwi->reset_reason, reset_state); - rtnl_lock(); + adapter->reset_reason = rwi->reset_reason; + /* requestor of VNIC_RESET_CHANGE_PARAM already has the rtnl lock */ + if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM)) + rtnl_lock(); + /* * Now that we have the rtnl lock, clear any pending failover. * This will ensure ibmvnic_open() has either completed or will @@ -2038,7 +1955,6 @@ static int do_reset(struct ibmvnic_adapter *adapter, adapter->failover_pending = false; netif_carrier_off(netdev); - adapter->reset_reason = rwi->reset_reason; old_num_rx_queues = adapter->req_rx_queues; old_num_tx_queues = adapter->req_tx_queues; @@ -2050,25 +1966,37 @@ static int do_reset(struct ibmvnic_adapter *adapter, if (reset_state == VNIC_OPEN && adapter->reset_reason != VNIC_RESET_MOBILITY && adapter->reset_reason != VNIC_RESET_FAILOVER) { - adapter->state = VNIC_CLOSING; + if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { + rc = __ibmvnic_close(netdev); + if (rc) + goto out; + } else { + adapter->state = VNIC_CLOSING; - /* Release the RTNL lock before link state change and - * re-acquire after the link state change to allow - * linkwatch_event to grab the RTNL lock and run during - * a reset. - */ - rtnl_unlock(); - rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); - rtnl_lock(); - if (rc) - goto out; + /* Release the RTNL lock before link state change and + * re-acquire after the link state change to allow + * linkwatch_event to grab the RTNL lock and run during + * a reset. + */ + rtnl_unlock(); + rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); + rtnl_lock(); + if (rc) + goto out; - if (adapter->state != VNIC_CLOSING) { - rc = -1; - goto out; + if (adapter->state != VNIC_CLOSING) { + rc = -1; + goto out; + } + + adapter->state = VNIC_CLOSED; } + } - adapter->state = VNIC_CLOSED; + if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { + release_resources(adapter); + release_sub_crqs(adapter, 1); + release_crq_queue(adapter); } if (adapter->reset_reason != VNIC_RESET_NON_FATAL) { @@ -2077,7 +2005,9 @@ static int do_reset(struct ibmvnic_adapter *adapter, */ adapter->state = VNIC_PROBED; - if (adapter->reset_reason == VNIC_RESET_MOBILITY) { + if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { + rc = init_crq_queue(adapter); + } else if (adapter->reset_reason == VNIC_RESET_MOBILITY) { rc = ibmvnic_reenable_crq_queue(adapter); release_sub_crqs(adapter, 1); } else { @@ -2116,7 +2046,11 @@ static int do_reset(struct ibmvnic_adapter *adapter, goto out; } - if (adapter->req_rx_queues != old_num_rx_queues || + if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) { + rc = init_resources(adapter); + if (rc) + goto out; + } else if (adapter->req_rx_queues != old_num_rx_queues || adapter->req_tx_queues != old_num_tx_queues || adapter->req_rx_add_entries_per_subcrq != old_num_rx_slots || @@ -2181,7 +2115,9 @@ out: /* restore the adapter state if reset failed */ if (rc) adapter->state = reset_state; - rtnl_unlock(); + /* requestor of VNIC_RESET_CHANGE_PARAM should still hold the rtnl lock */ + if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM)) + rtnl_unlock(); netdev_dbg(adapter->netdev, "[S:%d FOP:%d] Reset done, rc %d\n", adapter->state, adapter->failover_pending, rc); @@ -2312,10 +2248,7 @@ static void __ibmvnic_reset(struct work_struct *work) } spin_unlock_irqrestore(&adapter->state_lock, flags); - if (rwi->reset_reason == VNIC_RESET_CHANGE_PARAM) { - /* CHANGE_PARAM requestor holds rtnl_lock */ - rc = do_change_param_reset(adapter, rwi, reset_state); - } else if (adapter->force_reset_recovery) { + if (adapter->force_reset_recovery) { /* * Since we are doing a hard reset now, clear the * failover_pending flag so we don't ignore any diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 5c19ff452558..2fb52bd6fc0e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1531,8 +1531,6 @@ static const struct net_device_ops fm10k_netdev_ops = { .ndo_set_vf_rate = fm10k_ndo_set_vf_bw, .ndo_get_vf_config = fm10k_ndo_get_vf_config, .ndo_get_vf_stats = fm10k_ndo_get_vf_stats, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_dfwd_add_station = fm10k_dfwd_add_station, .ndo_dfwd_del_station = fm10k_dfwd_del_station, .ndo_features_check = fm10k_features_check, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1db482d310c2..521ea9df38d5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -12804,8 +12804,6 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, .ndo_set_vf_spoofchk = i40e_ndo_set_vf_spoofchk, .ndo_set_vf_trust = i40e_ndo_set_vf_trust, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_get_phys_port_id = i40e_get_phys_port_id, .ndo_fdb_add = i40e_ndo_fdb_add, .ndo_features_check = i40e_features_check, diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 4aca637d4a23..2574e78f7597 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2344,7 +2344,7 @@ static void i40e_inc_ntc(struct i40e_ring *rx_ring) **/ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) { - unsigned int total_rx_bytes = 0, total_rx_packets = 0; + unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0; struct sk_buff *skb = rx_ring->skb; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); unsigned int xdp_xmit = 0; @@ -2352,9 +2352,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) struct xdp_buff xdp; #if (PAGE_SIZE < 8192) - xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + frame_sz = i40e_rx_frame_truesize(rx_ring, 0); #endif - xdp.rxq = &rx_ring->xdp_rxq; + xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); while (likely(total_rx_packets < (unsigned int)budget)) { struct i40e_rx_buffer *rx_buffer; @@ -2406,12 +2406,12 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* retrieve a buffer from the ring */ if (!skb) { - xdp.data = page_address(rx_buffer->page) + - rx_buffer->page_offset; - xdp.data_meta = xdp.data; - xdp.data_hard_start = xdp.data - - i40e_rx_offset(rx_ring); - xdp.data_end = xdp.data + size; + unsigned int offset = i40e_rx_offset(rx_ring); + unsigned char *hard_start; + + hard_start = page_address(rx_buffer->page) + + rx_buffer->page_offset - offset; + xdp_prepare_buff(&xdp, hard_start, offset, size, true); #if (PAGE_SIZE > 4096) /* At larger PAGE_SIZE, frame_sz depend on len size */ xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, size); diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c index ed08ace4f05a..647e7fde11b4 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c +++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c @@ -911,7 +911,7 @@ static void iavf_print_link_message(struct iavf_adapter *adapter) return; } - speed = kcalloc(1, IAVF_MAX_SPEED_STRLEN, GFP_KERNEL); + speed = kzalloc(IAVF_MAX_SPEED_STRLEN, GFP_KERNEL); if (!speed) return; diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index e10ca8929f85..06aa37549c04 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -6792,6 +6792,4 @@ static const struct net_device_ops ice_netdev_ops = { .ndo_bpf = ice_xdp, .ndo_xdp_xmit = ice_xdp_xmit, .ndo_xsk_wakeup = ice_xsk_wakeup, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, }; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index b6fa83c619dd..2c2de56e2824 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -1089,23 +1089,25 @@ ice_is_non_eop(struct ice_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc, */ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) { - unsigned int total_rx_bytes = 0, total_rx_pkts = 0; + unsigned int total_rx_bytes = 0, total_rx_pkts = 0, frame_sz = 0; u16 cleaned_count = ICE_DESC_UNUSED(rx_ring); unsigned int xdp_res, xdp_xmit = 0; struct bpf_prog *xdp_prog = NULL; struct xdp_buff xdp; bool failure; - xdp.rxq = &rx_ring->xdp_rxq; /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ #if (PAGE_SIZE < 8192) - xdp.frame_sz = ice_rx_frame_truesize(rx_ring, 0); + frame_sz = ice_rx_frame_truesize(rx_ring, 0); #endif + xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); /* start the loop to process Rx packets bounded by 'budget' */ while (likely(total_rx_pkts < (unsigned int)budget)) { + unsigned int offset = ice_rx_offset(rx_ring); union ice_32b_rx_flex_desc *rx_desc; struct ice_rx_buf *rx_buf; + unsigned char *hard_start; struct sk_buff *skb; unsigned int size; u16 stat_err_bits; @@ -1151,10 +1153,9 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) goto construct_skb; } - xdp.data = page_address(rx_buf->page) + rx_buf->page_offset; - xdp.data_hard_start = xdp.data - ice_rx_offset(rx_ring); - xdp.data_meta = xdp.data; - xdp.data_end = xdp.data + size; + hard_start = page_address(rx_buf->page) + rx_buf->page_offset - + offset; + xdp_prepare_buff(&xdp, hard_start, offset, size, true); #if (PAGE_SIZE > 4096) /* At larger PAGE_SIZE, frame_sz depend on len size */ xdp.frame_sz = ice_rx_frame_truesize(rx_ring, size); diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c index bc2f4390b51d..02b12736ea80 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c @@ -191,12 +191,7 @@ ice_receive_skb(struct ice_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag) if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && (vlan_tag & VLAN_VID_MASK)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); - if (napi_gro_receive(&rx_ring->q_vector->napi, skb) == GRO_DROP) { - /* this is tracked separately to help us debug stack drops */ - rx_ring->rx_stats.gro_dropped++; - netdev_dbg(rx_ring->netdev, "Receive Queue %d: Dropped packet from GRO\n", - rx_ring->q_index); - } + napi_gro_receive(&rx_ring->q_vector->napi, skb); } /** diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 03f78fdb0dcd..84d4284b8b32 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -5959,15 +5959,6 @@ static int igb_tso(struct igb_ring *tx_ring, return 1; } -static inline bool igb_ipv6_csum_is_sctp(struct sk_buff *skb) -{ - unsigned int offset = 0; - - ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL); - - return offset == skb_checksum_start_offset(skb); -} - static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first) { struct sk_buff *skb = first->skb; @@ -5990,10 +5981,7 @@ csum_failed: break; case offsetof(struct sctphdr, checksum): /* validate that this is actually an SCTP request */ - if (((first->protocol == htons(ETH_P_IP)) && - (ip_hdr(skb)->protocol == IPPROTO_SCTP)) || - ((first->protocol == htons(ETH_P_IPV6)) && - igb_ipv6_csum_is_sctp(skb))) { + if (skb_csum_is_sctp(skb)) { type_tucmd = E1000_ADVTXD_TUCMD_L4T_SCTP; break; } @@ -8681,13 +8669,13 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) u16 cleaned_count = igb_desc_unused(rx_ring); unsigned int xdp_xmit = 0; struct xdp_buff xdp; - - xdp.rxq = &rx_ring->xdp_rxq; + u32 frame_sz = 0; /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ #if (PAGE_SIZE < 8192) - xdp.frame_sz = igb_rx_frame_truesize(rx_ring, 0); + frame_sz = igb_rx_frame_truesize(rx_ring, 0); #endif + xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); while (likely(total_packets < budget)) { union e1000_adv_rx_desc *rx_desc; @@ -8715,12 +8703,12 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) /* retrieve a buffer from the ring */ if (!skb) { - xdp.data = page_address(rx_buffer->page) + - rx_buffer->page_offset; - xdp.data_meta = xdp.data; - xdp.data_hard_start = xdp.data - - igb_rx_offset(rx_ring); - xdp.data_end = xdp.data + size; + unsigned int offset = igb_rx_offset(rx_ring); + unsigned char *hard_start; + + hard_start = page_address(rx_buffer->page) + + rx_buffer->page_offset - offset; + xdp_prepare_buff(&xdp, hard_start, offset, size, true); #if (PAGE_SIZE > 4096) /* At larger PAGE_SIZE, frame_sz depend on len size */ xdp.frame_sz = igb_rx_frame_truesize(rx_ring, size); diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 30fdea24e94a..fb3fbcb13331 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2072,15 +2072,6 @@ static int igbvf_tso(struct igbvf_ring *tx_ring, return 1; } -static inline bool igbvf_ipv6_csum_is_sctp(struct sk_buff *skb) -{ - unsigned int offset = 0; - - ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL); - - return offset == skb_checksum_start_offset(skb); -} - static bool igbvf_tx_csum(struct igbvf_ring *tx_ring, struct sk_buff *skb, u32 tx_flags, __be16 protocol) { @@ -2102,10 +2093,7 @@ csum_failed: break; case offsetof(struct sctphdr, checksum): /* validate that this is actually an SCTP request */ - if (((protocol == htons(ETH_P_IP)) && - (ip_hdr(skb)->protocol == IPPROTO_SCTP)) || - ((protocol == htons(ETH_P_IPV6)) && - igbvf_ipv6_csum_is_sctp(skb))) { + if (skb_csum_is_sctp(skb)) { type_tucmd = E1000_ADVTXD_TUCMD_L4T_SCTP; break; } diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index afd6a62da29d..43aec42e6d9d 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -949,15 +949,6 @@ static void igc_tx_ctxtdesc(struct igc_ring *tx_ring, } } -static inline bool igc_ipv6_csum_is_sctp(struct sk_buff *skb) -{ - unsigned int offset = 0; - - ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL); - - return offset == skb_checksum_start_offset(skb); -} - static void igc_tx_csum(struct igc_ring *tx_ring, struct igc_tx_buffer *first) { struct sk_buff *skb = first->skb; @@ -980,10 +971,7 @@ csum_failed: break; case offsetof(struct sctphdr, checksum): /* validate that this is actually an SCTP request */ - if ((first->protocol == htons(ETH_P_IP) && - (ip_hdr(skb)->protocol == IPPROTO_SCTP)) || - (first->protocol == htons(ETH_P_IPV6) && - igc_ipv6_csum_is_sctp(skb))) { + if (skb_csum_is_sctp(skb)) { type_tucmd = IGC_ADVTXD_TUCMD_L4T_SCTP; break; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 393d1c2cd853..e08c01525fd2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2291,7 +2291,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, struct ixgbe_ring *rx_ring, const int budget) { - unsigned int total_rx_bytes = 0, total_rx_packets = 0; + unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0; struct ixgbe_adapter *adapter = q_vector->adapter; #ifdef IXGBE_FCOE int ddp_bytes; @@ -2301,12 +2301,11 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, unsigned int xdp_xmit = 0; struct xdp_buff xdp; - xdp.rxq = &rx_ring->xdp_rxq; - /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ #if (PAGE_SIZE < 8192) - xdp.frame_sz = ixgbe_rx_frame_truesize(rx_ring, 0); + frame_sz = ixgbe_rx_frame_truesize(rx_ring, 0); #endif + xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); while (likely(total_rx_packets < budget)) { union ixgbe_adv_rx_desc *rx_desc; @@ -2336,12 +2335,12 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, /* retrieve a buffer from the ring */ if (!skb) { - xdp.data = page_address(rx_buffer->page) + - rx_buffer->page_offset; - xdp.data_meta = xdp.data; - xdp.data_hard_start = xdp.data - - ixgbe_rx_offset(rx_ring); - xdp.data_end = xdp.data + size; + unsigned int offset = ixgbe_rx_offset(rx_ring); + unsigned char *hard_start; + + hard_start = page_address(rx_buffer->page) + + rx_buffer->page_offset - offset; + xdp_prepare_buff(&xdp, hard_start, offset, size, true); #if (PAGE_SIZE > 4096) /* At larger PAGE_SIZE, frame_sz depend on len size */ xdp.frame_sz = ixgbe_rx_frame_truesize(rx_ring, size); @@ -8040,15 +8039,6 @@ static int ixgbe_tso(struct ixgbe_ring *tx_ring, return 1; } -static inline bool ixgbe_ipv6_csum_is_sctp(struct sk_buff *skb) -{ - unsigned int offset = 0; - - ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL); - - return offset == skb_checksum_start_offset(skb); -} - static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring, struct ixgbe_tx_buffer *first, struct ixgbe_ipsec_tx_data *itd) @@ -8074,10 +8064,7 @@ csum_failed: break; case offsetof(struct sctphdr, checksum): /* validate that this is actually an SCTP request */ - if (((first->protocol == htons(ETH_P_IP)) && - (ip_hdr(skb)->protocol == IPPROTO_SCTP)) || - ((first->protocol == htons(ETH_P_IPV6)) && - ixgbe_ipv6_csum_is_sctp(skb))) { + if (skb_csum_is_sctp(skb)) { type_tucmd = IXGBE_ADVTXD_TUCMD_L4T_SCTP; break; } @@ -10278,8 +10265,6 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_bridge_getlink = ixgbe_ndo_bridge_getlink, .ndo_dfwd_add_station = ixgbe_fwd_add, .ndo_dfwd_del_station = ixgbe_fwd_del, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = ixgbe_features_check, .ndo_bpf = ixgbe_xdp, .ndo_xdp_xmit = ixgbe_xdp_xmit, diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 4061cd7db5dd..a14e55e7fce8 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1121,19 +1121,18 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, struct ixgbevf_ring *rx_ring, int budget) { - unsigned int total_rx_bytes = 0, total_rx_packets = 0; + unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0; struct ixgbevf_adapter *adapter = q_vector->adapter; u16 cleaned_count = ixgbevf_desc_unused(rx_ring); struct sk_buff *skb = rx_ring->skb; bool xdp_xmit = false; struct xdp_buff xdp; - xdp.rxq = &rx_ring->xdp_rxq; - /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ #if (PAGE_SIZE < 8192) - xdp.frame_sz = ixgbevf_rx_frame_truesize(rx_ring, 0); + frame_sz = ixgbevf_rx_frame_truesize(rx_ring, 0); #endif + xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); while (likely(total_rx_packets < budget)) { struct ixgbevf_rx_buffer *rx_buffer; @@ -1161,12 +1160,12 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, /* retrieve a buffer from the ring */ if (!skb) { - xdp.data = page_address(rx_buffer->page) + - rx_buffer->page_offset; - xdp.data_meta = xdp.data; - xdp.data_hard_start = xdp.data - - ixgbevf_rx_offset(rx_ring); - xdp.data_end = xdp.data + size; + unsigned int offset = ixgbevf_rx_offset(rx_ring); + unsigned char *hard_start; + + hard_start = page_address(rx_buffer->page) + + rx_buffer->page_offset - offset; + xdp_prepare_buff(&xdp, hard_start, offset, size, true); #if (PAGE_SIZE > 4096) /* At larger PAGE_SIZE, frame_sz depend on len size */ xdp.frame_sz = ixgbevf_rx_frame_truesize(rx_ring, size); @@ -3844,15 +3843,6 @@ static int ixgbevf_tso(struct ixgbevf_ring *tx_ring, return 1; } -static inline bool ixgbevf_ipv6_csum_is_sctp(struct sk_buff *skb) -{ - unsigned int offset = 0; - - ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL); - - return offset == skb_checksum_start_offset(skb); -} - static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring, struct ixgbevf_tx_buffer *first, struct ixgbevf_ipsec_tx_data *itd) @@ -3873,10 +3863,7 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring, break; case offsetof(struct sctphdr, checksum): /* validate that this is actually an SCTP request */ - if (((first->protocol == htons(ETH_P_IP)) && - (ip_hdr(skb)->protocol == IPPROTO_SCTP)) || - ((first->protocol == htons(ETH_P_IPV6)) && - ixgbevf_ipv6_csum_is_sctp(skb))) { + if (skb_csum_is_sctp(skb)) { type_tucmd = IXGBE_ADVTXD_TUCMD_L4T_SCTP; break; } diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 41815b609569..7fe15a3286f4 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -94,7 +94,6 @@ config MVPP2 config MVPP2_PTP bool "Marvell Armada 8K Enable PTP support" - depends on NETWORK_PHY_TIMESTAMPING depends on (PTP_1588_CLOCK = y && MVPP2 = y) || \ (PTP_1588_CLOCK && MVPP2 = m) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index bc4d8d144401..6290bfb6494e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2263,11 +2263,8 @@ mvneta_swbm_rx_frame(struct mvneta_port *pp, /* Prefetch header */ prefetch(data); - - xdp->data_hard_start = data; - xdp->data = data + pp->rx_offset_correction + MVNETA_MH_SIZE; - xdp->data_end = xdp->data + data_len; - xdp_set_data_meta_invalid(xdp); + xdp_prepare_buff(xdp, data, pp->rx_offset_correction + MVNETA_MH_SIZE, + data_len, false); sinfo = xdp_get_shared_info_from_buff(xdp); sinfo->nr_frags = 0; @@ -2363,9 +2360,8 @@ static int mvneta_rx_swbm(struct napi_struct *napi, u32 desc_status, frame_sz; struct xdp_buff xdp_buf; + xdp_init_buff(&xdp_buf, PAGE_SIZE, &rxq->xdp_rxq); xdp_buf.data_hard_start = NULL; - xdp_buf.frame_sz = PAGE_SIZE; - xdp_buf.rxq = &rxq->xdp_rxq; sinfo.nr_frags = 0; diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.h index 8867f25afab4..663157dc8062 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.h +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.h @@ -143,7 +143,7 @@ struct mvpp2_cls_c2_entry { /* Number of per-port dedicated entries in the C2 TCAM */ #define MVPP22_CLS_C2_PORT_N_FLOWS MVPP2_N_RFS_ENTRIES_PER_FLOW -/* Each port has oen range per flow type + one entry controling the global RSS +/* Each port has one range per flow type + one entry controlling the global RSS * setting and the default rx queue */ #define MVPP22_CLS_C2_PORT_RANGE (MVPP22_CLS_C2_PORT_N_FLOWS + 1) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 358119d98358..143522908477 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3563,17 +3563,17 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, frag_size = bm_pool->frag_size; if (xdp_prog) { - xdp.data_hard_start = data; - xdp.data = data + MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM; - xdp.data_end = xdp.data + rx_bytes; - xdp.frame_sz = PAGE_SIZE; + struct xdp_rxq_info *xdp_rxq; if (bm_pool->pkt_size == MVPP2_BM_SHORT_PKT_SIZE) - xdp.rxq = &rxq->xdp_rxq_short; + xdp_rxq = &rxq->xdp_rxq_short; else - xdp.rxq = &rxq->xdp_rxq_long; + xdp_rxq = &rxq->xdp_rxq_long; - xdp_set_data_meta_invalid(&xdp); + xdp_init_buff(&xdp, PAGE_SIZE, xdp_rxq); + xdp_prepare_buff(&xdp, data, + MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM, + rx_bytes, false); ret = mvpp2_run_xdp(port, rxq, xdp_prog, &xdp, pp, &ps); diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c index a30eb90ba3d2..6ee53c52627f 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c @@ -914,15 +914,15 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto, mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = tid; - /* Set next lu to IPv4 */ - mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); - mvpp2_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + /* Finished: go to flowid generation */ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + /* Set L4 offset */ mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, sizeof(struct iphdr) - 4, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); - mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, - MVPP2_PRS_IPV4_DIP_AI_BIT); + mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_sram_ri_update(&pe, ri, ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); mvpp2_prs_tcam_data_byte_set(&pe, 2, 0x00, @@ -931,7 +931,8 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto, MVPP2_PRS_TCAM_PROTO_MASK); mvpp2_prs_tcam_data_byte_set(&pe, 5, proto, MVPP2_PRS_TCAM_PROTO_MASK); - mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); + mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, + MVPP2_PRS_IPV4_DIP_AI_BIT); /* Unmask all ports */ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); @@ -999,12 +1000,17 @@ static int mvpp2_prs_ip4_cast(struct mvpp2 *priv, unsigned short l3_cast) return -EINVAL; } - /* Finished: go to flowid generation */ - mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); - mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + /* Go again to ipv4 */ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); - mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, + mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, MVPP2_PRS_IPV4_DIP_AI_BIT); + + /* Shift back to IPv4 proto */ + mvpp2_prs_sram_shift_set(&pe, -12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + + mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); + /* Unmask all ports */ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); @@ -1425,8 +1431,9 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, MVPP2_PRS_RI_L3_PROTO_MASK); - /* Skip eth_type + 4 bytes of IP header */ - mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, + /* goto ipv4 dest-address (skip eth_type + IP-header-size - 4) */ + mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + + sizeof(struct iphdr) - 4, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); /* Set L3 offset */ mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, @@ -1630,8 +1637,9 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT, MVPP2_PRS_RI_L3_PROTO_MASK); - /* Skip eth_type + 4 bytes of IP header */ - mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, + /* goto ipv4 dest-address (skip eth_type + IP-header-size - 4) */ + mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + + sizeof(struct iphdr) - 4, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); /* Set L3 offset */ mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, @@ -1761,19 +1769,20 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv) mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = MVPP2_PE_IP4_PROTO_UN; - /* Set next lu to IPv4 */ - mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); - mvpp2_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + /* Finished: go to flowid generation */ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + /* Set L4 offset */ mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, sizeof(struct iphdr) - 4, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); - mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, - MVPP2_PRS_IPV4_DIP_AI_BIT); + mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER, MVPP2_PRS_RI_L4_PROTO_MASK); - mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); + mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, + MVPP2_PRS_IPV4_DIP_AI_BIT); /* Unmask all ports */ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); @@ -1786,14 +1795,19 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv) mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = MVPP2_PE_IP4_ADDR_UN; - /* Finished: go to flowid generation */ - mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); - mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + /* Go again to ipv4 */ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); + + mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, + MVPP2_PRS_IPV4_DIP_AI_BIT); + + /* Shift back to IPv4 proto */ + mvpp2_prs_sram_shift_set(&pe, -12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UCAST, MVPP2_PRS_RI_L3_ADDR_MASK); + mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); - mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, - MVPP2_PRS_IPV4_DIP_AI_BIT); /* Unmask all ports */ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 1a8f5a039d50..84a91234ba8e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -867,7 +867,7 @@ static int cgx_lmac_init(struct cgx *cgx) cgx->lmac_count = MAX_LMAC_PER_CGX; for (i = 0; i < cgx->lmac_count; i++) { - lmac = kcalloc(1, sizeof(struct lmac), GFP_KERNEL); + lmac = kzalloc(sizeof(struct lmac), GFP_KERNEL); if (!lmac) return -ENOMEM; lmac->name = kcalloc(1, sizeof("cgx_fwi_xxx_yyy"), GFP_KERNEL); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index f919283ddc34..89e93eb46ab7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -717,6 +717,8 @@ struct nix_rss_flowkey_cfg { #define NIX_FLOW_KEY_TYPE_INNR_ETH_DMAC BIT(17) #define NIX_FLOW_KEY_TYPE_VLAN BIT(20) #define NIX_FLOW_KEY_TYPE_IPV4_PROTO BIT(21) +#define NIX_FLOW_KEY_TYPE_AH BIT(22) +#define NIX_FLOW_KEY_TYPE_ESP BIT(23) u32 flowkey_cfg; /* Flowkey types selected */ u8 group; /* RSS context or group */ }; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h index a1f79445db71..3c640f6aba92 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h @@ -162,6 +162,11 @@ enum key_fields { NPC_DIP_IPV4, NPC_SIP_IPV6, NPC_DIP_IPV6, + NPC_IPPROTO_TCP, + NPC_IPPROTO_UDP, + NPC_IPPROTO_SCTP, + NPC_IPPROTO_AH, + NPC_IPPROTO_ESP, NPC_SPORT_TCP, NPC_DPORT_TCP, NPC_SPORT_UDP, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index e8fd712860a1..0b6bf9f0c6f0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -1323,7 +1323,7 @@ static int rvu_get_attach_blkaddr(struct rvu *rvu, int blktype, break; default: return rvu_get_blkaddr(rvu, blktype, 0); - }; + } if (is_block_implemented(rvu->hw, blkaddr)) return blkaddr; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index d27543c1a166..f60499562d2e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -1757,6 +1757,7 @@ static void rvu_dbg_npc_mcam_show_flows(struct seq_file *s, seq_printf(s, "mask 0x%x\n", ntohs(rule->mask.dport)); break; default: + seq_puts(s, "\n"); break; } } @@ -1785,7 +1786,7 @@ static void rvu_dbg_npc_mcam_show_action(struct seq_file *s, break; default: break; - }; + } } else { switch (rule->rx_action.op) { case NIX_RX_ACTIONOP_DROP: @@ -1806,7 +1807,7 @@ static void rvu_dbg_npc_mcam_show_action(struct seq_file *s, break; default: break; - }; + } } } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c index bc0e4113370e..10a98bcb7c54 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c @@ -52,6 +52,650 @@ static bool rvu_common_request_irq(struct rvu *rvu, int offset, return rvu->irq_allocated[offset]; } +static void rvu_nix_intr_work(struct work_struct *work) +{ + struct rvu_nix_health_reporters *rvu_nix_health_reporter; + + rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, intr_work); + devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_intr_reporter, + "NIX_AF_RVU Error", + rvu_nix_health_reporter->nix_event_ctx); +} + +static irqreturn_t rvu_nix_af_rvu_intr_handler(int irq, void *rvu_irq) +{ + struct rvu_nix_event_ctx *nix_event_context; + struct rvu_devlink *rvu_dl = rvu_irq; + struct rvu *rvu; + int blkaddr; + u64 intr; + + rvu = rvu_dl->rvu; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return IRQ_NONE; + + nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + intr = rvu_read64(rvu, blkaddr, NIX_AF_RVU_INT); + nix_event_context->nix_af_rvu_int = intr; + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT, intr); + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1C, ~0ULL); + queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->intr_work); + + return IRQ_HANDLED; +} + +static void rvu_nix_gen_work(struct work_struct *work) +{ + struct rvu_nix_health_reporters *rvu_nix_health_reporter; + + rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, gen_work); + devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_gen_reporter, + "NIX_AF_GEN Error", + rvu_nix_health_reporter->nix_event_ctx); +} + +static irqreturn_t rvu_nix_af_rvu_gen_handler(int irq, void *rvu_irq) +{ + struct rvu_nix_event_ctx *nix_event_context; + struct rvu_devlink *rvu_dl = rvu_irq; + struct rvu *rvu; + int blkaddr; + u64 intr; + + rvu = rvu_dl->rvu; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return IRQ_NONE; + + nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + intr = rvu_read64(rvu, blkaddr, NIX_AF_GEN_INT); + nix_event_context->nix_af_rvu_gen = intr; + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT, intr); + rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1C, ~0ULL); + queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->gen_work); + + return IRQ_HANDLED; +} + +static void rvu_nix_err_work(struct work_struct *work) +{ + struct rvu_nix_health_reporters *rvu_nix_health_reporter; + + rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, err_work); + devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_err_reporter, + "NIX_AF_ERR Error", + rvu_nix_health_reporter->nix_event_ctx); +} + +static irqreturn_t rvu_nix_af_rvu_err_handler(int irq, void *rvu_irq) +{ + struct rvu_nix_event_ctx *nix_event_context; + struct rvu_devlink *rvu_dl = rvu_irq; + struct rvu *rvu; + int blkaddr; + u64 intr; + + rvu = rvu_dl->rvu; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return IRQ_NONE; + + nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + intr = rvu_read64(rvu, blkaddr, NIX_AF_ERR_INT); + nix_event_context->nix_af_rvu_err = intr; + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT, intr); + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1C, ~0ULL); + queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->err_work); + + return IRQ_HANDLED; +} + +static void rvu_nix_ras_work(struct work_struct *work) +{ + struct rvu_nix_health_reporters *rvu_nix_health_reporter; + + rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, ras_work); + devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_ras_reporter, + "NIX_AF_RAS Error", + rvu_nix_health_reporter->nix_event_ctx); +} + +static irqreturn_t rvu_nix_af_rvu_ras_handler(int irq, void *rvu_irq) +{ + struct rvu_nix_event_ctx *nix_event_context; + struct rvu_devlink *rvu_dl = rvu_irq; + struct rvu *rvu; + int blkaddr; + u64 intr; + + rvu = rvu_dl->rvu; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return IRQ_NONE; + + nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + intr = rvu_read64(rvu, blkaddr, NIX_AF_ERR_INT); + nix_event_context->nix_af_rvu_ras = intr; + + /* Clear interrupts */ + rvu_write64(rvu, blkaddr, NIX_AF_RAS, intr); + rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1C, ~0ULL); + queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->ras_work); + + return IRQ_HANDLED; +} + +static void rvu_nix_unregister_interrupts(struct rvu *rvu) +{ + struct rvu_devlink *rvu_dl = rvu->rvu_dl; + int offs, i, blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return; + + offs = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff; + if (!offs) + return; + + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1C, ~0ULL); + rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1C, ~0ULL); + + if (rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU]) { + free_irq(pci_irq_vector(rvu->pdev, offs + NIX_AF_INT_VEC_RVU), + rvu_dl); + rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU] = false; + } + + for (i = NIX_AF_INT_VEC_AF_ERR; i < NIX_AF_INT_VEC_CNT; i++) + if (rvu->irq_allocated[offs + i]) { + free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu_dl); + rvu->irq_allocated[offs + i] = false; + } +} + +static int rvu_nix_register_interrupts(struct rvu *rvu) +{ + int blkaddr, base; + bool rc; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return blkaddr; + + /* Get NIX AF MSIX vectors offset. */ + base = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff; + if (!base) { + dev_warn(rvu->dev, + "Failed to get NIX%d NIX_AF_INT vector offsets\n", + blkaddr - BLKADDR_NIX0); + return 0; + } + /* Register and enable NIX_AF_RVU_INT interrupt */ + rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_RVU, + "NIX_AF_RVU_INT", + rvu_nix_af_rvu_intr_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1S, ~0ULL); + + /* Register and enable NIX_AF_GEN_INT interrupt */ + rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_GEN, + "NIX_AF_GEN_INT", + rvu_nix_af_rvu_gen_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1S, ~0ULL); + + /* Register and enable NIX_AF_ERR_INT interrupt */ + rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_AF_ERR, + "NIX_AF_ERR_INT", + rvu_nix_af_rvu_err_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1S, ~0ULL); + + /* Register and enable NIX_AF_RAS interrupt */ + rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_POISON, + "NIX_AF_RAS", + rvu_nix_af_rvu_ras_handler); + if (!rc) + goto err; + rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1S, ~0ULL); + + return 0; +err: + rvu_nix_unregister_interrupts(rvu); + return rc; +} + +static int rvu_nix_report_show(struct devlink_fmsg *fmsg, void *ctx, + enum nix_af_rvu_health health_reporter) +{ + struct rvu_nix_event_ctx *nix_event_context; + u64 intr_val; + int err; + + nix_event_context = ctx; + switch (health_reporter) { + case NIX_AF_RVU_INTR: + intr_val = nix_event_context->nix_af_rvu_int; + err = rvu_report_pair_start(fmsg, "NIX_AF_RVU"); + if (err) + return err; + err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX RVU Interrupt Reg ", + nix_event_context->nix_af_rvu_int); + if (err) + return err; + if (intr_val & BIT_ULL(0)) { + err = devlink_fmsg_string_put(fmsg, "\n\tUnmap Slot Error"); + if (err) + return err; + } + err = rvu_report_pair_end(fmsg); + if (err) + return err; + break; + case NIX_AF_RVU_GEN: + intr_val = nix_event_context->nix_af_rvu_gen; + err = rvu_report_pair_start(fmsg, "NIX_AF_GENERAL"); + if (err) + return err; + err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX General Interrupt Reg ", + nix_event_context->nix_af_rvu_gen); + if (err) + return err; + if (intr_val & BIT_ULL(0)) { + err = devlink_fmsg_string_put(fmsg, "\n\tRx multicast pkt drop"); + if (err) + return err; + } + if (intr_val & BIT_ULL(1)) { + err = devlink_fmsg_string_put(fmsg, "\n\tRx mirror pkt drop"); + if (err) + return err; + } + if (intr_val & BIT_ULL(4)) { + err = devlink_fmsg_string_put(fmsg, "\n\tSMQ flush done"); + if (err) + return err; + } + err = rvu_report_pair_end(fmsg); + if (err) + return err; + break; + case NIX_AF_RVU_ERR: + intr_val = nix_event_context->nix_af_rvu_err; + err = rvu_report_pair_start(fmsg, "NIX_AF_ERR"); + if (err) + return err; + err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX Error Interrupt Reg ", + nix_event_context->nix_af_rvu_err); + if (err) + return err; + if (intr_val & BIT_ULL(14)) { + err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_AQ_INST_S read"); + if (err) + return err; + } + if (intr_val & BIT_ULL(13)) { + err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_AQ_RES_S write"); + if (err) + return err; + } + if (intr_val & BIT_ULL(12)) { + err = devlink_fmsg_string_put(fmsg, "\n\tAQ Doorbell Error"); + if (err) + return err; + } + if (intr_val & BIT_ULL(6)) { + err = devlink_fmsg_string_put(fmsg, "\n\tRx on unmapped PF_FUNC"); + if (err) + return err; + } + if (intr_val & BIT_ULL(5)) { + err = devlink_fmsg_string_put(fmsg, "\n\tRx multicast replication error"); + if (err) + return err; + } + if (intr_val & BIT_ULL(4)) { + err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_RX_MCE_S read"); + if (err) + return err; + } + if (intr_val & BIT_ULL(3)) { + err = devlink_fmsg_string_put(fmsg, "\n\tFault on multicast WQE read"); + if (err) + return err; + } + if (intr_val & BIT_ULL(2)) { + err = devlink_fmsg_string_put(fmsg, "\n\tFault on mirror WQE read"); + if (err) + return err; + } + if (intr_val & BIT_ULL(1)) { + err = devlink_fmsg_string_put(fmsg, "\n\tFault on mirror pkt write"); + if (err) + return err; + } + if (intr_val & BIT_ULL(0)) { + err = devlink_fmsg_string_put(fmsg, "\n\tFault on multicast pkt write"); + if (err) + return err; + } + err = rvu_report_pair_end(fmsg); + if (err) + return err; + break; + case NIX_AF_RVU_RAS: + intr_val = nix_event_context->nix_af_rvu_err; + err = rvu_report_pair_start(fmsg, "NIX_AF_RAS"); + if (err) + return err; + err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX RAS Interrupt Reg ", + nix_event_context->nix_af_rvu_err); + if (err) + return err; + err = devlink_fmsg_string_put(fmsg, "\n\tPoison Data on:"); + if (err) + return err; + if (intr_val & BIT_ULL(34)) { + err = devlink_fmsg_string_put(fmsg, "\n\tNIX_AQ_INST_S"); + if (err) + return err; + } + if (intr_val & BIT_ULL(33)) { + err = devlink_fmsg_string_put(fmsg, "\n\tNIX_AQ_RES_S"); + if (err) + return err; + } + if (intr_val & BIT_ULL(32)) { + err = devlink_fmsg_string_put(fmsg, "\n\tHW ctx"); + if (err) + return err; + } + if (intr_val & BIT_ULL(4)) { + err = devlink_fmsg_string_put(fmsg, "\n\tPacket from mirror buffer"); + if (err) + return err; + } + if (intr_val & BIT_ULL(3)) { + err = devlink_fmsg_string_put(fmsg, "\n\tPacket from multicast buffer"); + + if (err) + return err; + } + if (intr_val & BIT_ULL(2)) { + err = devlink_fmsg_string_put(fmsg, "\n\tWQE read from mirror buffer"); + if (err) + return err; + } + if (intr_val & BIT_ULL(1)) { + err = devlink_fmsg_string_put(fmsg, "\n\tWQE read from multicast buffer"); + if (err) + return err; + } + if (intr_val & BIT_ULL(0)) { + err = devlink_fmsg_string_put(fmsg, "\n\tNIX_RX_MCE_S read"); + if (err) + return err; + } + err = rvu_report_pair_end(fmsg); + if (err) + return err; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rvu_hw_nix_intr_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *ctx, + struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_devlink *rvu_dl = rvu->rvu_dl; + struct rvu_nix_event_ctx *nix_ctx; + + nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + + return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_INTR) : + rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_INTR); +} + +static int rvu_hw_nix_intr_recover(struct devlink_health_reporter *reporter, + void *ctx, struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_nix_event_ctx *nix_event_ctx = ctx; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return blkaddr; + + if (nix_event_ctx->nix_af_rvu_int) + rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1S, ~0ULL); + + return 0; +} + +static int rvu_hw_nix_gen_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *ctx, + struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_devlink *rvu_dl = rvu->rvu_dl; + struct rvu_nix_event_ctx *nix_ctx; + + nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + + return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_GEN) : + rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_GEN); +} + +static int rvu_hw_nix_gen_recover(struct devlink_health_reporter *reporter, + void *ctx, struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_nix_event_ctx *nix_event_ctx = ctx; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return blkaddr; + + if (nix_event_ctx->nix_af_rvu_gen) + rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1S, ~0ULL); + + return 0; +} + +static int rvu_hw_nix_err_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *ctx, + struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_devlink *rvu_dl = rvu->rvu_dl; + struct rvu_nix_event_ctx *nix_ctx; + + nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + + return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_ERR) : + rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_ERR); +} + +static int rvu_hw_nix_err_recover(struct devlink_health_reporter *reporter, + void *ctx, struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_nix_event_ctx *nix_event_ctx = ctx; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return blkaddr; + + if (nix_event_ctx->nix_af_rvu_err) + rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1S, ~0ULL); + + return 0; +} + +static int rvu_hw_nix_ras_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *ctx, + struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_devlink *rvu_dl = rvu->rvu_dl; + struct rvu_nix_event_ctx *nix_ctx; + + nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; + + return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_RAS) : + rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_RAS); +} + +static int rvu_hw_nix_ras_recover(struct devlink_health_reporter *reporter, + void *ctx, struct netlink_ext_ack *netlink_extack) +{ + struct rvu *rvu = devlink_health_reporter_priv(reporter); + struct rvu_nix_event_ctx *nix_event_ctx = ctx; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); + if (blkaddr < 0) + return blkaddr; + + if (nix_event_ctx->nix_af_rvu_int) + rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1S, ~0ULL); + + return 0; +} + +RVU_REPORTERS(hw_nix_intr); +RVU_REPORTERS(hw_nix_gen); +RVU_REPORTERS(hw_nix_err); +RVU_REPORTERS(hw_nix_ras); + +static void rvu_nix_health_reporters_destroy(struct rvu_devlink *rvu_dl); + +static int rvu_nix_register_reporters(struct rvu_devlink *rvu_dl) +{ + struct rvu_nix_health_reporters *rvu_reporters; + struct rvu_nix_event_ctx *nix_event_context; + struct rvu *rvu = rvu_dl->rvu; + + rvu_reporters = kzalloc(sizeof(*rvu_reporters), GFP_KERNEL); + if (!rvu_reporters) + return -ENOMEM; + + rvu_dl->rvu_nix_health_reporter = rvu_reporters; + nix_event_context = kzalloc(sizeof(*nix_event_context), GFP_KERNEL); + if (!nix_event_context) + return -ENOMEM; + + rvu_reporters->nix_event_ctx = nix_event_context; + rvu_reporters->rvu_hw_nix_intr_reporter = + devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_intr_reporter_ops, 0, rvu); + if (IS_ERR(rvu_reporters->rvu_hw_nix_intr_reporter)) { + dev_warn(rvu->dev, "Failed to create hw_nix_intr reporter, err=%ld\n", + PTR_ERR(rvu_reporters->rvu_hw_nix_intr_reporter)); + return PTR_ERR(rvu_reporters->rvu_hw_nix_intr_reporter); + } + + rvu_reporters->rvu_hw_nix_gen_reporter = + devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_gen_reporter_ops, 0, rvu); + if (IS_ERR(rvu_reporters->rvu_hw_nix_gen_reporter)) { + dev_warn(rvu->dev, "Failed to create hw_nix_gen reporter, err=%ld\n", + PTR_ERR(rvu_reporters->rvu_hw_nix_gen_reporter)); + return PTR_ERR(rvu_reporters->rvu_hw_nix_gen_reporter); + } + + rvu_reporters->rvu_hw_nix_err_reporter = + devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_err_reporter_ops, 0, rvu); + if (IS_ERR(rvu_reporters->rvu_hw_nix_err_reporter)) { + dev_warn(rvu->dev, "Failed to create hw_nix_err reporter, err=%ld\n", + PTR_ERR(rvu_reporters->rvu_hw_nix_err_reporter)); + return PTR_ERR(rvu_reporters->rvu_hw_nix_err_reporter); + } + + rvu_reporters->rvu_hw_nix_ras_reporter = + devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_ras_reporter_ops, 0, rvu); + if (IS_ERR(rvu_reporters->rvu_hw_nix_ras_reporter)) { + dev_warn(rvu->dev, "Failed to create hw_nix_ras reporter, err=%ld\n", + PTR_ERR(rvu_reporters->rvu_hw_nix_ras_reporter)); + return PTR_ERR(rvu_reporters->rvu_hw_nix_ras_reporter); + } + + rvu_dl->devlink_wq = create_workqueue("rvu_devlink_wq"); + if (!rvu_dl->devlink_wq) + goto err; + + INIT_WORK(&rvu_reporters->intr_work, rvu_nix_intr_work); + INIT_WORK(&rvu_reporters->gen_work, rvu_nix_gen_work); + INIT_WORK(&rvu_reporters->err_work, rvu_nix_err_work); + INIT_WORK(&rvu_reporters->ras_work, rvu_nix_ras_work); + + return 0; +err: + rvu_nix_health_reporters_destroy(rvu_dl); + return -ENOMEM; +} + +static int rvu_nix_health_reporters_create(struct rvu_devlink *rvu_dl) +{ + struct rvu *rvu = rvu_dl->rvu; + int err; + + err = rvu_nix_register_reporters(rvu_dl); + if (err) { + dev_warn(rvu->dev, "Failed to create nix reporter, err =%d\n", + err); + return err; + } + rvu_nix_register_interrupts(rvu); + + return 0; +} + +static void rvu_nix_health_reporters_destroy(struct rvu_devlink *rvu_dl) +{ + struct rvu_nix_health_reporters *nix_reporters; + struct rvu *rvu = rvu_dl->rvu; + + nix_reporters = rvu_dl->rvu_nix_health_reporter; + + if (!nix_reporters->rvu_hw_nix_ras_reporter) + return; + if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_intr_reporter)) + devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_intr_reporter); + + if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_gen_reporter)) + devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_gen_reporter); + + if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_err_reporter)) + devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_err_reporter); + + if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_ras_reporter)) + devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_ras_reporter); + + rvu_nix_unregister_interrupts(rvu); + kfree(rvu_dl->rvu_nix_health_reporter->nix_event_ctx); + kfree(rvu_dl->rvu_nix_health_reporter); +} + static void rvu_npa_intr_work(struct work_struct *work) { struct rvu_npa_health_reporters *rvu_npa_health_reporter; @@ -698,9 +1342,14 @@ static void rvu_npa_health_reporters_destroy(struct rvu_devlink *rvu_dl) static int rvu_health_reporters_create(struct rvu *rvu) { struct rvu_devlink *rvu_dl; + int err; rvu_dl = rvu->rvu_dl; - return rvu_npa_health_reporters_create(rvu_dl); + err = rvu_npa_health_reporters_create(rvu_dl); + if (err) + return err; + + return rvu_nix_health_reporters_create(rvu_dl); } static void rvu_health_reporters_destroy(struct rvu *rvu) @@ -712,6 +1361,7 @@ static void rvu_health_reporters_destroy(struct rvu *rvu) rvu_dl = rvu->rvu_dl; rvu_npa_health_reporters_destroy(rvu_dl); + rvu_nix_health_reporters_destroy(rvu_dl); } static int rvu_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.h index d7578fa92ac1..471e57dedb20 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.h @@ -41,11 +41,38 @@ struct rvu_npa_health_reporters { struct work_struct ras_work; }; +enum nix_af_rvu_health { + NIX_AF_RVU_INTR, + NIX_AF_RVU_GEN, + NIX_AF_RVU_ERR, + NIX_AF_RVU_RAS, +}; + +struct rvu_nix_event_ctx { + u64 nix_af_rvu_int; + u64 nix_af_rvu_gen; + u64 nix_af_rvu_err; + u64 nix_af_rvu_ras; +}; + +struct rvu_nix_health_reporters { + struct rvu_nix_event_ctx *nix_event_ctx; + struct devlink_health_reporter *rvu_hw_nix_intr_reporter; + struct work_struct intr_work; + struct devlink_health_reporter *rvu_hw_nix_gen_reporter; + struct work_struct gen_work; + struct devlink_health_reporter *rvu_hw_nix_err_reporter; + struct work_struct err_work; + struct devlink_health_reporter *rvu_hw_nix_ras_reporter; + struct work_struct ras_work; +}; + struct rvu_devlink { struct devlink *dl; struct rvu *rvu; struct workqueue_struct *devlink_wq; struct rvu_npa_health_reporters *rvu_npa_health_reporter; + struct rvu_nix_health_reporters *rvu_nix_health_reporter; }; /* Devlink APIs */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index a8dfbb6d1774..b54753ef7d94 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -2580,6 +2580,7 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) struct nix_rx_flowkey_alg *field; struct nix_rx_flowkey_alg tmp; u32 key_type, valid_key; + int l4_key_offset; if (!alg) return -EINVAL; @@ -2712,6 +2713,12 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) field_marker = false; keyoff_marker = false; } + + /* TCP/UDP/SCTP and ESP/AH falls at same offset so + * remember the TCP key offset of 40 byte hash key. + */ + if (key_type == NIX_FLOW_KEY_TYPE_TCP) + l4_key_offset = key_off; break; case NIX_FLOW_KEY_TYPE_NVGRE: field->lid = NPC_LID_LD; @@ -2783,11 +2790,31 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) field->ltype_mask = 0xF; field->fn_mask = 1; /* Mask out the first nibble */ break; + case NIX_FLOW_KEY_TYPE_AH: + case NIX_FLOW_KEY_TYPE_ESP: + field->hdr_offset = 0; + field->bytesm1 = 7; /* SPI + sequence number */ + field->ltype_mask = 0xF; + field->lid = NPC_LID_LE; + field->ltype_match = NPC_LT_LE_ESP; + if (key_type == NIX_FLOW_KEY_TYPE_AH) { + field->lid = NPC_LID_LD; + field->ltype_match = NPC_LT_LD_AH; + field->hdr_offset = 4; + keyoff_marker = false; + } + break; } field->ena = 1; /* Found a valid flow key type */ if (valid_key) { + /* Use the key offset of TCP/UDP/SCTP fields + * for ESP/AH fields. + */ + if (key_type == NIX_FLOW_KEY_TYPE_ESP || + key_type == NIX_FLOW_KEY_TYPE_AH) + key_off = l4_key_offset; field->key_offset = key_off; memcpy(&alg[nr_field], field, sizeof(*field)); max_key_off = max(max_key_off, field->bytesm1 + 1); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 14832b66d1fe..4ba9d54ce4e3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -26,6 +26,11 @@ static const char * const npc_flow_names[] = { [NPC_DIP_IPV4] = "ipv4 destination ip", [NPC_SIP_IPV6] = "ipv6 source ip", [NPC_DIP_IPV6] = "ipv6 destination ip", + [NPC_IPPROTO_TCP] = "ip proto tcp", + [NPC_IPPROTO_UDP] = "ip proto udp", + [NPC_IPPROTO_SCTP] = "ip proto sctp", + [NPC_IPPROTO_AH] = "ip proto AH", + [NPC_IPPROTO_ESP] = "ip proto ESP", [NPC_SPORT_TCP] = "tcp source port", [NPC_DPORT_TCP] = "tcp destination port", [NPC_SPORT_UDP] = "udp source port", @@ -212,13 +217,13 @@ static bool npc_check_overlap(struct rvu *rvu, int blkaddr, return false; } -static int npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type, - u8 intf) +static bool npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type, + u8 intf) { if (!npc_is_field_present(rvu, type, intf) || npc_check_overlap(rvu, blkaddr, type, 0, intf)) - return -EOPNOTSUPP; - return 0; + return false; + return true; } static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number, @@ -269,7 +274,7 @@ static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number, break; default: return; - }; + } npc_set_kw_masks(mcam, type, nr_bits, kwi, offset, intf); } @@ -448,14 +453,13 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf) struct npc_mcam *mcam = &rvu->hw->mcam; u64 *features = &mcam->rx_features; u64 tcp_udp_sctp; - int err, hdr; + int hdr; if (is_npc_intf_tx(intf)) features = &mcam->tx_features; for (hdr = NPC_DMAC; hdr < NPC_HEADER_FIELDS_MAX; hdr++) { - err = npc_check_field(rvu, blkaddr, hdr, intf); - if (!err) + if (npc_check_field(rvu, blkaddr, hdr, intf)) *features |= BIT_ULL(hdr); } @@ -464,13 +468,26 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf) BIT_ULL(NPC_SPORT_SCTP) | BIT_ULL(NPC_DPORT_SCTP); /* for tcp/udp/sctp corresponding layer type should be in the key */ - if (*features & tcp_udp_sctp) - if (npc_check_field(rvu, blkaddr, NPC_LD, intf)) + if (*features & tcp_udp_sctp) { + if (!npc_check_field(rvu, blkaddr, NPC_LD, intf)) *features &= ~tcp_udp_sctp; + else + *features |= BIT_ULL(NPC_IPPROTO_TCP) | + BIT_ULL(NPC_IPPROTO_UDP) | + BIT_ULL(NPC_IPPROTO_SCTP); + } + + /* for AH, check if corresponding layer type is present in the key */ + if (npc_check_field(rvu, blkaddr, NPC_LD, intf)) + *features |= BIT_ULL(NPC_IPPROTO_AH); + + /* for ESP, check if corresponding layer type is present in the key */ + if (npc_check_field(rvu, blkaddr, NPC_LE, intf)) + *features |= BIT_ULL(NPC_IPPROTO_ESP); /* for vlan corresponding layer type should be in the key */ if (*features & BIT_ULL(NPC_OUTER_VID)) - if (npc_check_field(rvu, blkaddr, NPC_LB, intf)) + if (!npc_check_field(rvu, blkaddr, NPC_LB, intf)) *features &= ~BIT_ULL(NPC_OUTER_VID); } @@ -743,13 +760,13 @@ static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry, return; /* For tcp/udp/sctp LTYPE should be present in entry */ - if (features & (BIT_ULL(NPC_SPORT_TCP) | BIT_ULL(NPC_DPORT_TCP))) + if (features & BIT_ULL(NPC_IPPROTO_TCP)) npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_TCP, 0, ~0ULL, 0, intf); - if (features & (BIT_ULL(NPC_SPORT_UDP) | BIT_ULL(NPC_DPORT_UDP))) + if (features & BIT_ULL(NPC_IPPROTO_UDP)) npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_UDP, 0, ~0ULL, 0, intf); - if (features & (BIT_ULL(NPC_SPORT_SCTP) | BIT_ULL(NPC_DPORT_SCTP))) + if (features & BIT_ULL(NPC_IPPROTO_SCTP)) npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_SCTP, 0, ~0ULL, 0, intf); @@ -758,6 +775,15 @@ static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry, NPC_LT_LB_STAG_QINQ | NPC_LT_LB_CTAG, 0, NPC_LT_LB_STAG_QINQ & NPC_LT_LB_CTAG, 0, intf); + /* For AH, LTYPE should be present in entry */ + if (features & BIT_ULL(NPC_IPPROTO_AH)) + npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_AH, + 0, ~0ULL, 0, intf); + /* For ESP, LTYPE should be present in entry */ + if (features & BIT_ULL(NPC_IPPROTO_ESP)) + npc_update_entry(rvu, NPC_LE, entry, NPC_LT_LE_ESP, + 0, ~0ULL, 0, intf); + #define NPC_WRITE_FLOW(field, member, val_lo, val_hi, mask_lo, mask_hi) \ do { \ if (features & BIT_ULL((field))) { \ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h index e2153d47c373..5e15f4fc11e3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h @@ -74,6 +74,16 @@ enum npa_af_int_vec_e { NPA_AF_INT_VEC_CNT = 0x5, }; +/* NIX Admin function Interrupt Vector Enumeration */ +enum nix_af_int_vec_e { + NIX_AF_INT_VEC_RVU = 0x0, + NIX_AF_INT_VEC_GEN = 0x1, + NIX_AF_INT_VEC_AQ_DONE = 0x2, + NIX_AF_INT_VEC_AF_ERR = 0x3, + NIX_AF_INT_VEC_POISON = 0x4, + NIX_AF_INT_VEC_CNT = 0x5, +}; + /** * RVU PF Interrupt Vector Enumeration */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index e6869435e1f3..5ddedc3b754d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -270,14 +270,17 @@ int otx2_set_flowkey_cfg(struct otx2_nic *pfvf) return err; } -int otx2_set_rss_table(struct otx2_nic *pfvf) +int otx2_set_rss_table(struct otx2_nic *pfvf, int ctx_id) { struct otx2_rss_info *rss = &pfvf->hw.rss_info; + const int index = rss->rss_size * ctx_id; struct mbox *mbox = &pfvf->mbox; + struct otx2_rss_ctx *rss_ctx; struct nix_aq_enq_req *aq; int idx, err; mutex_lock(&mbox->lock); + rss_ctx = rss->rss_ctx[ctx_id]; /* Get memory to put this msg */ for (idx = 0; idx < rss->rss_size; idx++) { aq = otx2_mbox_alloc_msg_nix_aq_enq(mbox); @@ -297,10 +300,10 @@ int otx2_set_rss_table(struct otx2_nic *pfvf) } } - aq->rss.rq = rss->ind_tbl[idx]; + aq->rss.rq = rss_ctx->ind_tbl[idx]; /* Fill AQ info */ - aq->qidx = idx; + aq->qidx = index + idx; aq->ctype = NIX_AQ_CTYPE_RSS; aq->op = NIX_AQ_INSTOP_INIT; } @@ -335,9 +338,10 @@ void otx2_set_rss_key(struct otx2_nic *pfvf) int otx2_rss_init(struct otx2_nic *pfvf) { struct otx2_rss_info *rss = &pfvf->hw.rss_info; + struct otx2_rss_ctx *rss_ctx; int idx, ret = 0; - rss->rss_size = sizeof(rss->ind_tbl); + rss->rss_size = sizeof(*rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP]); /* Init RSS key if it is not setup already */ if (!rss->enable) @@ -345,13 +349,19 @@ int otx2_rss_init(struct otx2_nic *pfvf) otx2_set_rss_key(pfvf); if (!netif_is_rxfh_configured(pfvf->netdev)) { - /* Default indirection table */ + /* Set RSS group 0 as default indirection table */ + rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP] = kzalloc(rss->rss_size, + GFP_KERNEL); + if (!rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP]) + return -ENOMEM; + + rss_ctx = rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP]; for (idx = 0; idx < rss->rss_size; idx++) - rss->ind_tbl[idx] = + rss_ctx->ind_tbl[idx] = ethtool_rxfh_indir_default(idx, pfvf->hw.rx_queues); } - ret = otx2_set_rss_table(pfvf); + ret = otx2_set_rss_table(pfvf, DEFAULT_RSS_CONTEXT_GROUP); if (ret) return ret; @@ -987,7 +997,7 @@ int otx2_config_nix(struct otx2_nic *pfvf) nixlf->sq_cnt = pfvf->hw.tx_queues; nixlf->cq_cnt = pfvf->qset.cq_cnt; nixlf->rss_sz = MAX_RSS_INDIR_TBL_SIZE; - nixlf->rss_grps = 1; /* Single RSS indir table supported, for now */ + nixlf->rss_grps = MAX_RSS_GROUPS; nixlf->xqe_sz = NIX_XQESZ_W16; /* We don't know absolute NPA LF idx attached. * AF will replace 'RVU_DEFAULT_PF_FUNC' with diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 103430400a8a..143ae04c8ad5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -51,13 +51,17 @@ enum arua_mapped_qtypes { #define NIX_LF_POISON_VEC 0x82 /* RSS configuration */ +struct otx2_rss_ctx { + u8 ind_tbl[MAX_RSS_INDIR_TBL_SIZE]; +}; + struct otx2_rss_info { u8 enable; u32 flowkey_cfg; u16 rss_size; - u8 ind_tbl[MAX_RSS_INDIR_TBL_SIZE]; #define RSS_HASH_KEY_SIZE 44 /* 352 bit key */ u8 key[RSS_HASH_KEY_SIZE]; + struct otx2_rss_ctx *rss_ctx[MAX_RSS_GROUPS]; }; /* NIX (or NPC) RX errors */ @@ -643,7 +647,7 @@ void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); int otx2_rss_init(struct otx2_nic *pfvf); int otx2_set_flowkey_cfg(struct otx2_nic *pfvf); void otx2_set_rss_key(struct otx2_nic *pfvf); -int otx2_set_rss_table(struct otx2_nic *pfvf); +int otx2_set_rss_table(struct otx2_nic *pfvf, int ctx_id); /* Mbox handlers */ void mbox_handler_msix_offset(struct otx2_nic *pfvf, @@ -684,10 +688,11 @@ int otx2_get_flow(struct otx2_nic *pfvf, int otx2_get_all_flows(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc, u32 *rule_locs); int otx2_add_flow(struct otx2_nic *pfvf, - struct ethtool_rx_flow_spec *fsp); + struct ethtool_rxnfc *nfc); int otx2_remove_flow(struct otx2_nic *pfvf, u32 location); int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, struct npc_install_flow_req *req); +void otx2_rss_ctx_flow_del(struct otx2_nic *pfvf, int ctx_id); int otx2_del_macfilter(struct net_device *netdev, const u8 *mac); int otx2_add_macfilter(struct net_device *netdev, const u8 *mac); int otx2_enable_rxvlan(struct otx2_nic *pf, bool enable); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index 67171b66a56c..e0199f0e4a6c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -448,10 +448,14 @@ static int otx2_get_rss_hash_opts(struct otx2_nic *pfvf, nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; break; case AH_ESP_V4_FLOW: + case AH_ESP_V6_FLOW: + if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_ESP) + nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; case AH_V4_FLOW: case ESP_V4_FLOW: case IPV4_FLOW: - case AH_ESP_V6_FLOW: + break; case AH_V6_FLOW: case ESP_V6_FLOW: case IPV6_FLOW: @@ -459,6 +463,7 @@ static int otx2_get_rss_hash_opts(struct otx2_nic *pfvf, default: return -EINVAL; } + return 0; } @@ -527,6 +532,36 @@ static int otx2_set_rss_hash_opts(struct otx2_nic *pfvf, return -EINVAL; } break; + case AH_ESP_V4_FLOW: + case AH_ESP_V6_FLOW: + switch (nfc->data & rxh_l4) { + case 0: + rss_cfg &= ~(NIX_FLOW_KEY_TYPE_ESP | + NIX_FLOW_KEY_TYPE_AH); + rss_cfg |= NIX_FLOW_KEY_TYPE_VLAN | + NIX_FLOW_KEY_TYPE_IPV4_PROTO; + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + /* If VLAN hashing is also requested for ESP then do not + * allow because of hardware 40 bytes flow key limit. + */ + if (rss_cfg & NIX_FLOW_KEY_TYPE_VLAN) { + netdev_err(pfvf->netdev, + "RSS hash of ESP or AH with VLAN is not supported\n"); + return -EOPNOTSUPP; + } + + rss_cfg |= NIX_FLOW_KEY_TYPE_ESP | NIX_FLOW_KEY_TYPE_AH; + /* Disable IPv4 proto hashing since IPv6 SA+DA(32 bytes) + * and ESP SPI+sequence(8 bytes) uses hardware maximum + * limit of 40 byte flow key. + */ + rss_cfg &= ~NIX_FLOW_KEY_TYPE_IPV4_PROTO; + break; + default: + return -EINVAL; + } + break; case IPV4_FLOW: case IPV6_FLOW: rss_cfg = NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6; @@ -581,7 +616,7 @@ static int otx2_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *nfc) break; case ETHTOOL_SRXCLSRLINS: if (netif_running(dev) && ntuple) - ret = otx2_add_flow(pfvf, &nfc->fs); + ret = otx2_add_flow(pfvf, nfc); break; case ETHTOOL_SRXCLSRLDEL: if (netif_running(dev) && ntuple) @@ -641,42 +676,50 @@ static u32 otx2_get_rxfh_key_size(struct net_device *netdev) static u32 otx2_get_rxfh_indir_size(struct net_device *dev) { - struct otx2_nic *pfvf = netdev_priv(dev); - - return pfvf->hw.rss_info.rss_size; + return MAX_RSS_INDIR_TBL_SIZE; } -/* Get RSS configuration */ -static int otx2_get_rxfh(struct net_device *dev, u32 *indir, - u8 *hkey, u8 *hfunc) +static int otx2_rss_ctx_delete(struct otx2_nic *pfvf, int ctx_id) { - struct otx2_nic *pfvf = netdev_priv(dev); - struct otx2_rss_info *rss; - int idx; + struct otx2_rss_info *rss = &pfvf->hw.rss_info; - rss = &pfvf->hw.rss_info; + otx2_rss_ctx_flow_del(pfvf, ctx_id); + kfree(rss->rss_ctx[ctx_id]); + rss->rss_ctx[ctx_id] = NULL; - if (indir) { - for (idx = 0; idx < rss->rss_size; idx++) - indir[idx] = rss->ind_tbl[idx]; - } + return 0; +} - if (hkey) - memcpy(hkey, rss->key, sizeof(rss->key)); +static int otx2_rss_ctx_create(struct otx2_nic *pfvf, + u32 *rss_context) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + u8 ctx; - if (hfunc) - *hfunc = ETH_RSS_HASH_TOP; + for (ctx = 0; ctx < MAX_RSS_GROUPS; ctx++) { + if (!rss->rss_ctx[ctx]) + break; + } + if (ctx == MAX_RSS_GROUPS) + return -EINVAL; + + rss->rss_ctx[ctx] = kzalloc(sizeof(*rss->rss_ctx[ctx]), GFP_KERNEL); + if (!rss->rss_ctx[ctx]) + return -ENOMEM; + *rss_context = ctx; return 0; } -/* Configure RSS table and hash key */ -static int otx2_set_rxfh(struct net_device *dev, const u32 *indir, - const u8 *hkey, const u8 hfunc) +/* RSS context configuration */ +static int otx2_set_rxfh_context(struct net_device *dev, const u32 *indir, + const u8 *hkey, const u8 hfunc, + u32 *rss_context, bool delete) { struct otx2_nic *pfvf = netdev_priv(dev); + struct otx2_rss_ctx *rss_ctx; struct otx2_rss_info *rss; - int idx; + int ret, idx; if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; @@ -688,20 +731,85 @@ static int otx2_set_rxfh(struct net_device *dev, const u32 *indir, return -EIO; } + if (hkey) { + memcpy(rss->key, hkey, sizeof(rss->key)); + otx2_set_rss_key(pfvf); + } + if (delete) + return otx2_rss_ctx_delete(pfvf, *rss_context); + + if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) { + ret = otx2_rss_ctx_create(pfvf, rss_context); + if (ret) + return ret; + } if (indir) { + rss_ctx = rss->rss_ctx[*rss_context]; for (idx = 0; idx < rss->rss_size; idx++) - rss->ind_tbl[idx] = indir[idx]; + rss_ctx->ind_tbl[idx] = indir[idx]; } + otx2_set_rss_table(pfvf, *rss_context); - if (hkey) { - memcpy(rss->key, hkey, sizeof(rss->key)); - otx2_set_rss_key(pfvf); + return 0; +} + +static int otx2_get_rxfh_context(struct net_device *dev, u32 *indir, + u8 *hkey, u8 *hfunc, u32 rss_context) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + struct otx2_rss_ctx *rss_ctx; + struct otx2_rss_info *rss; + int idx, rx_queues; + + rss = &pfvf->hw.rss_info; + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + if (!indir) + return 0; + + if (!rss->enable && rss_context == DEFAULT_RSS_CONTEXT_GROUP) { + rx_queues = pfvf->hw.rx_queues; + for (idx = 0; idx < MAX_RSS_INDIR_TBL_SIZE; idx++) + indir[idx] = ethtool_rxfh_indir_default(idx, rx_queues); + return 0; } + if (rss_context >= MAX_RSS_GROUPS) + return -ENOENT; + + rss_ctx = rss->rss_ctx[rss_context]; + if (!rss_ctx) + return -ENOENT; + + if (indir) { + for (idx = 0; idx < rss->rss_size; idx++) + indir[idx] = rss_ctx->ind_tbl[idx]; + } + if (hkey) + memcpy(hkey, rss->key, sizeof(rss->key)); - otx2_set_rss_table(pfvf); return 0; } +/* Get RSS configuration */ +static int otx2_get_rxfh(struct net_device *dev, u32 *indir, + u8 *hkey, u8 *hfunc) +{ + return otx2_get_rxfh_context(dev, indir, hkey, hfunc, + DEFAULT_RSS_CONTEXT_GROUP); +} + +/* Configure RSS table and hash key */ +static int otx2_set_rxfh(struct net_device *dev, const u32 *indir, + const u8 *hkey, const u8 hfunc) +{ + + u32 rss_context = DEFAULT_RSS_CONTEXT_GROUP; + + return otx2_set_rxfh_context(dev, indir, hkey, hfunc, &rss_context, 0); +} + static u32 otx2_get_msglevel(struct net_device *netdev) { struct otx2_nic *pfvf = netdev_priv(netdev); @@ -771,6 +879,8 @@ static const struct ethtool_ops otx2_ethtool_ops = { .get_rxfh_indir_size = otx2_get_rxfh_indir_size, .get_rxfh = otx2_get_rxfh, .set_rxfh = otx2_set_rxfh, + .get_rxfh_context = otx2_get_rxfh_context, + .set_rxfh_context = otx2_set_rxfh_context, .get_msglevel = otx2_get_msglevel, .set_msglevel = otx2_set_msglevel, .get_pauseparam = otx2_get_pauseparam, @@ -866,6 +976,8 @@ static const struct ethtool_ops otx2vf_ethtool_ops = { .get_rxfh_indir_size = otx2_get_rxfh_indir_size, .get_rxfh = otx2_get_rxfh, .set_rxfh = otx2_set_rxfh, + .get_rxfh_context = otx2_get_rxfh_context, + .set_rxfh_context = otx2_set_rxfh_context, .get_ringparam = otx2_get_ringparam, .set_ringparam = otx2_set_ringparam, .get_coalesce = otx2_get_coalesce, diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index be8ccfce1848..d6b5bf247e31 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -16,6 +16,7 @@ struct otx2_flow { u32 location; u16 entry; bool is_vf; + u8 rss_ctx_id; int vf; }; @@ -245,6 +246,7 @@ int otx2_get_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc, list_for_each_entry(iter, &pfvf->flow_cfg->flow_list, list) { if (iter->location == location) { nfc->fs = iter->flow_spec; + nfc->rss_context = iter->rss_ctx_id; return 0; } } @@ -270,14 +272,16 @@ int otx2_get_all_flows(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc, return err; } -static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp, - struct npc_install_flow_req *req, - u32 flow_type) +static int otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp, + struct npc_install_flow_req *req, + u32 flow_type) { struct ethtool_usrip4_spec *ipv4_usr_mask = &fsp->m_u.usr_ip4_spec; struct ethtool_usrip4_spec *ipv4_usr_hdr = &fsp->h_u.usr_ip4_spec; struct ethtool_tcpip4_spec *ipv4_l4_mask = &fsp->m_u.tcp_ip4_spec; struct ethtool_tcpip4_spec *ipv4_l4_hdr = &fsp->h_u.tcp_ip4_spec; + struct ethtool_ah_espip4_spec *ah_esp_hdr = &fsp->h_u.ah_ip4_spec; + struct ethtool_ah_espip4_spec *ah_esp_mask = &fsp->m_u.ah_ip4_spec; struct flow_msg *pmask = &req->mask; struct flow_msg *pkt = &req->packet; @@ -297,10 +301,16 @@ static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp, sizeof(pmask->ip4dst)); req->features |= BIT_ULL(NPC_DIP_IPV4); } + pkt->etype = cpu_to_be16(ETH_P_IP); + pmask->etype = cpu_to_be16(0xFFFF); + req->features |= BIT_ULL(NPC_ETYPE); break; case TCP_V4_FLOW: case UDP_V4_FLOW: case SCTP_V4_FLOW: + pkt->etype = cpu_to_be16(ETH_P_IP); + pmask->etype = cpu_to_be16(0xFFFF); + req->features |= BIT_ULL(NPC_ETYPE); if (ipv4_l4_mask->ip4src) { memcpy(&pkt->ip4src, &ipv4_l4_hdr->ip4src, sizeof(pkt->ip4src)); @@ -339,20 +349,60 @@ static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp, else req->features |= BIT_ULL(NPC_DPORT_SCTP); } + if (flow_type == UDP_V4_FLOW) + req->features |= BIT_ULL(NPC_IPPROTO_UDP); + else if (flow_type == TCP_V4_FLOW) + req->features |= BIT_ULL(NPC_IPPROTO_TCP); + else + req->features |= BIT_ULL(NPC_IPPROTO_SCTP); + break; + case AH_V4_FLOW: + case ESP_V4_FLOW: + pkt->etype = cpu_to_be16(ETH_P_IP); + pmask->etype = cpu_to_be16(0xFFFF); + req->features |= BIT_ULL(NPC_ETYPE); + if (ah_esp_mask->ip4src) { + memcpy(&pkt->ip4src, &ah_esp_hdr->ip4src, + sizeof(pkt->ip4src)); + memcpy(&pmask->ip4src, &ah_esp_mask->ip4src, + sizeof(pmask->ip4src)); + req->features |= BIT_ULL(NPC_SIP_IPV4); + } + if (ah_esp_mask->ip4dst) { + memcpy(&pkt->ip4dst, &ah_esp_hdr->ip4dst, + sizeof(pkt->ip4dst)); + memcpy(&pmask->ip4dst, &ah_esp_mask->ip4dst, + sizeof(pmask->ip4dst)); + req->features |= BIT_ULL(NPC_DIP_IPV4); + } + + /* NPC profile doesn't extract AH/ESP header fields */ + if ((ah_esp_mask->spi & ah_esp_hdr->spi) || + (ah_esp_mask->tos & ah_esp_mask->tos)) + return -EOPNOTSUPP; + + if (flow_type == AH_V4_FLOW) + req->features |= BIT_ULL(NPC_IPPROTO_AH); + else + req->features |= BIT_ULL(NPC_IPPROTO_ESP); break; default: break; } + + return 0; } -static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp, - struct npc_install_flow_req *req, - u32 flow_type) +static int otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp, + struct npc_install_flow_req *req, + u32 flow_type) { struct ethtool_usrip6_spec *ipv6_usr_mask = &fsp->m_u.usr_ip6_spec; struct ethtool_usrip6_spec *ipv6_usr_hdr = &fsp->h_u.usr_ip6_spec; struct ethtool_tcpip6_spec *ipv6_l4_mask = &fsp->m_u.tcp_ip6_spec; struct ethtool_tcpip6_spec *ipv6_l4_hdr = &fsp->h_u.tcp_ip6_spec; + struct ethtool_ah_espip6_spec *ah_esp_hdr = &fsp->h_u.ah_ip6_spec; + struct ethtool_ah_espip6_spec *ah_esp_mask = &fsp->m_u.ah_ip6_spec; struct flow_msg *pmask = &req->mask; struct flow_msg *pkt = &req->packet; @@ -372,10 +422,16 @@ static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp, sizeof(pmask->ip6dst)); req->features |= BIT_ULL(NPC_DIP_IPV6); } + pkt->etype = cpu_to_be16(ETH_P_IPV6); + pmask->etype = cpu_to_be16(0xFFFF); + req->features |= BIT_ULL(NPC_ETYPE); break; case TCP_V6_FLOW: case UDP_V6_FLOW: case SCTP_V6_FLOW: + pkt->etype = cpu_to_be16(ETH_P_IPV6); + pmask->etype = cpu_to_be16(0xFFFF); + req->features |= BIT_ULL(NPC_ETYPE); if (!ipv6_addr_any((struct in6_addr *)ipv6_l4_mask->ip6src)) { memcpy(&pkt->ip6src, &ipv6_l4_hdr->ip6src, sizeof(pkt->ip6src)); @@ -414,10 +470,47 @@ static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp, else req->features |= BIT_ULL(NPC_DPORT_SCTP); } + if (flow_type == UDP_V6_FLOW) + req->features |= BIT_ULL(NPC_IPPROTO_UDP); + else if (flow_type == TCP_V6_FLOW) + req->features |= BIT_ULL(NPC_IPPROTO_TCP); + else + req->features |= BIT_ULL(NPC_IPPROTO_SCTP); break; + case AH_V6_FLOW: + case ESP_V6_FLOW: + pkt->etype = cpu_to_be16(ETH_P_IPV6); + pmask->etype = cpu_to_be16(0xFFFF); + req->features |= BIT_ULL(NPC_ETYPE); + if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6src)) { + memcpy(&pkt->ip6src, &ah_esp_hdr->ip6src, + sizeof(pkt->ip6src)); + memcpy(&pmask->ip6src, &ah_esp_mask->ip6src, + sizeof(pmask->ip6src)); + req->features |= BIT_ULL(NPC_SIP_IPV6); + } + if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6dst)) { + memcpy(&pkt->ip6dst, &ah_esp_hdr->ip6dst, + sizeof(pkt->ip6dst)); + memcpy(&pmask->ip6dst, &ah_esp_mask->ip6dst, + sizeof(pmask->ip6dst)); + req->features |= BIT_ULL(NPC_DIP_IPV6); + } + + /* NPC profile doesn't extract AH/ESP header fields */ + if ((ah_esp_mask->spi & ah_esp_hdr->spi) || + (ah_esp_mask->tclass & ah_esp_mask->tclass)) + return -EOPNOTSUPP; + + if (flow_type == AH_V6_FLOW) + req->features |= BIT_ULL(NPC_IPPROTO_AH); + else + req->features |= BIT_ULL(NPC_IPPROTO_ESP); default: break; } + + return 0; } int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, @@ -428,8 +521,9 @@ int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, struct flow_msg *pmask = &req->mask; struct flow_msg *pkt = &req->packet; u32 flow_type; + int ret; - flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); + flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS); switch (flow_type) { /* bits not set in mask are don't care */ case ETHER_FLOW: @@ -455,13 +549,21 @@ int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, case TCP_V4_FLOW: case UDP_V4_FLOW: case SCTP_V4_FLOW: - otx2_prepare_ipv4_flow(fsp, req, flow_type); + case AH_V4_FLOW: + case ESP_V4_FLOW: + ret = otx2_prepare_ipv4_flow(fsp, req, flow_type); + if (ret) + return ret; break; case IPV6_USER_FLOW: case TCP_V6_FLOW: case UDP_V6_FLOW: case SCTP_V6_FLOW: - otx2_prepare_ipv6_flow(fsp, req, flow_type); + case AH_V6_FLOW: + case ESP_V6_FLOW: + ret = otx2_prepare_ipv6_flow(fsp, req, flow_type); + if (ret) + return ret; break; default: return -EOPNOTSUPP; @@ -532,9 +634,13 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) /* change to unicast only if action of default entry is not * requested by user */ - if (req->op != NIX_RX_ACTION_DEFAULT) + if (flow->flow_spec.flow_type & FLOW_RSS) { + req->op = NIX_RX_ACTIONOP_RSS; + req->index = flow->rss_ctx_id; + } else { req->op = NIX_RX_ACTIONOP_UCAST; - req->index = ethtool_get_flow_spec_ring(ring_cookie); + req->index = ethtool_get_flow_spec_ring(ring_cookie); + } vf = ethtool_get_flow_spec_ring_vf(ring_cookie); if (vf > pci_num_vf(pfvf->pdev)) { mutex_unlock(&pfvf->mbox.lock); @@ -555,14 +661,16 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) return err; } -int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rx_flow_spec *fsp) +int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) { struct otx2_flow_config *flow_cfg = pfvf->flow_cfg; - u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie); + struct ethtool_rx_flow_spec *fsp = &nfc->fs; struct otx2_flow *flow; bool new = false; + u32 ring; int err; + ring = ethtool_get_flow_spec_ring(fsp->ring_cookie); if (!(pfvf->flags & OTX2_FLAG_NTUPLE_SUPPORT)) return -ENOMEM; @@ -585,6 +693,9 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rx_flow_spec *fsp) /* struct copy */ flow->flow_spec = *fsp; + if (fsp->flow_type & FLOW_RSS) + flow->rss_ctx_id = nfc->rss_context; + err = otx2_add_flow_msg(pfvf, flow); if (err) { if (new) @@ -647,6 +758,22 @@ int otx2_remove_flow(struct otx2_nic *pfvf, u32 location) return 0; } +void otx2_rss_ctx_flow_del(struct otx2_nic *pfvf, int ctx_id) +{ + struct otx2_flow *flow, *tmp; + int err; + + list_for_each_entry_safe(flow, tmp, &pfvf->flow_cfg->flow_list, list) { + if (flow->rss_ctx_id != ctx_id) + continue; + err = otx2_remove_flow(pfvf, flow->location); + if (err) + netdev_warn(pfvf->netdev, + "Can't delete the rule %d associated with this rss group err:%d", + flow->location, err); + } +} + int otx2_destroy_ntuple_flows(struct otx2_nic *pfvf) { struct otx2_flow_config *flow_cfg = pfvf->flow_cfg; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c index 7d83e1f91ef1..8c2b03151736 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c @@ -580,16 +580,12 @@ int prestera_bridge_port_event(struct net_device *dev, unsigned long event, } static int prestera_port_attr_br_flags_set(struct prestera_port *port, - struct switchdev_trans *trans, struct net_device *dev, unsigned long flags) { struct prestera_bridge_port *br_port; int err; - if (switchdev_trans_ph_prepare(trans)) - return 0; - br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); if (!br_port) return 0; @@ -608,35 +604,26 @@ static int prestera_port_attr_br_flags_set(struct prestera_port *port, } static int prestera_port_attr_br_ageing_set(struct prestera_port *port, - struct switchdev_trans *trans, unsigned long ageing_clock_t) { unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); u32 ageing_time_ms = jiffies_to_msecs(ageing_jiffies); struct prestera_switch *sw = port->sw; - if (switchdev_trans_ph_prepare(trans)) { - if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS || - ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS) - return -ERANGE; - else - return 0; - } + if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS || + ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS) + return -ERANGE; return prestera_hw_switch_ageing_set(sw, ageing_time_ms); } static int prestera_port_attr_br_vlan_set(struct prestera_port *port, - struct switchdev_trans *trans, struct net_device *dev, bool vlan_enabled) { struct prestera_switch *sw = port->sw; struct prestera_bridge *bridge; - if (!switchdev_trans_ph_prepare(trans)) - return 0; - bridge = prestera_bridge_by_dev(sw->swdev, dev); if (WARN_ON(!bridge)) return -EINVAL; @@ -665,19 +652,15 @@ static int prestera_port_bridge_vlan_stp_set(struct prestera_port *port, return 0; } -static int presterar_port_attr_stp_state_set(struct prestera_port *port, - struct switchdev_trans *trans, - struct net_device *dev, - u8 state) +static int prestera_port_attr_stp_state_set(struct prestera_port *port, + struct net_device *dev, + u8 state) { struct prestera_bridge_port *br_port; struct prestera_bridge_vlan *br_vlan; int err; u16 vid; - if (switchdev_trans_ph_prepare(trans)) - return 0; - br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); if (!br_port) return 0; @@ -712,17 +695,15 @@ err_port_stp_set: } static int prestera_port_obj_attr_set(struct net_device *dev, - const struct switchdev_attr *attr, - struct switchdev_trans *trans) + const struct switchdev_attr *attr) { struct prestera_port *port = netdev_priv(dev); int err = 0; switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - err = presterar_port_attr_stp_state_set(port, trans, - attr->orig_dev, - attr->u.stp_state); + err = prestera_port_attr_stp_state_set(port, attr->orig_dev, + attr->u.stp_state); break; case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: if (attr->u.brport_flags & @@ -730,17 +711,15 @@ static int prestera_port_obj_attr_set(struct net_device *dev, err = -EINVAL; break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: - err = prestera_port_attr_br_flags_set(port, trans, - attr->orig_dev, + err = prestera_port_attr_br_flags_set(port, attr->orig_dev, attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: - err = prestera_port_attr_br_ageing_set(port, trans, + err = prestera_port_attr_br_ageing_set(port, attr->u.ageing_time); break; case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: - err = prestera_port_attr_br_vlan_set(port, trans, - attr->orig_dev, + err = prestera_port_attr_br_vlan_set(port, attr->orig_dev, attr->u.vlan_filtering); break; default: @@ -1020,7 +999,6 @@ prestera_bridge_port_vlan_del(struct prestera_port *port, static int prestera_port_vlans_add(struct prestera_port *port, const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans, struct netlink_ext_ack *extack) { bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; @@ -1029,14 +1007,10 @@ static int prestera_port_vlans_add(struct prestera_port *port, struct prestera_bridge_port *br_port; struct prestera_switch *sw = port->sw; struct prestera_bridge *bridge; - u16 vid; if (netif_is_bridge_master(dev)) return 0; - if (switchdev_trans_ph_commit(trans)) - return 0; - br_port = prestera_bridge_port_by_dev(sw->swdev, dev); if (WARN_ON(!br_port)) return -EINVAL; @@ -1045,22 +1019,13 @@ static int prestera_port_vlans_add(struct prestera_port *port, if (!bridge->vlan_enabled) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - int err; - - err = prestera_bridge_port_vlan_add(port, br_port, - vid, flag_untagged, - flag_pvid, extack); - if (err) - return err; - } - - return 0; + return prestera_bridge_port_vlan_add(port, br_port, + vlan->vid, flag_untagged, + flag_pvid, extack); } static int prestera_port_obj_add(struct net_device *dev, const struct switchdev_obj *obj, - struct switchdev_trans *trans, struct netlink_ext_ack *extack) { struct prestera_port *port = netdev_priv(dev); @@ -1069,7 +1034,7 @@ static int prestera_port_obj_add(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); - return prestera_port_vlans_add(port, vlan, trans, extack); + return prestera_port_vlans_add(port, vlan, extack); default: return -EOPNOTSUPP; } @@ -1081,7 +1046,6 @@ static int prestera_port_vlans_del(struct prestera_port *port, struct net_device *dev = vlan->obj.orig_dev; struct prestera_bridge_port *br_port; struct prestera_switch *sw = port->sw; - u16 vid; if (netif_is_bridge_master(dev)) return -EOPNOTSUPP; @@ -1093,8 +1057,7 @@ static int prestera_port_vlans_del(struct prestera_port *port, if (!br_port->bridge->vlan_enabled) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) - prestera_bridge_port_vlan_del(port, br_port, vid); + prestera_bridge_port_vlan_del(port, br_port, vlan->vid); return 0; } diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 6d2d60675ffd..01d3ee4b5829 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -353,7 +353,7 @@ 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_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | + mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; /* Only update control register when needed! */ @@ -759,8 +759,8 @@ static void mtk_get_stats64(struct net_device *dev, static inline int mtk_max_frag_size(int mtu) { /* make sure buf_size will be at least MTK_MAX_RX_LENGTH */ - if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH) - mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; + if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH_2K) + mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN; return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); @@ -771,7 +771,7 @@ static inline int mtk_max_buf_size(int frag_size) int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - WARN_ON(buf_size < MTK_MAX_RX_LENGTH); + WARN_ON(buf_size < MTK_MAX_RX_LENGTH_2K); return buf_size; } @@ -2499,6 +2499,35 @@ static void mtk_uninit(struct net_device *dev) mtk_rx_irq_disable(eth, ~0); } +static int mtk_change_mtu(struct net_device *dev, int new_mtu) +{ + int length = new_mtu + MTK_RX_ETH_HLEN; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + u32 mcr_cur, mcr_new; + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK; + + if (length <= 1518) + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518); + else if (length <= 1536) + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536); + else if (length <= 1552) + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552); + else + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048); + + if (mcr_new != mcr_cur) + mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); + } + + dev->mtu = new_mtu; + + return 0; +} + static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mtk_mac *mac = netdev_priv(dev); @@ -2795,6 +2824,7 @@ static const struct net_device_ops mtk_netdev_ops = { .ndo_set_mac_address = mtk_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = mtk_do_ioctl, + .ndo_change_mtu = mtk_change_mtu, .ndo_tx_timeout = mtk_tx_timeout, .ndo_get_stats64 = mtk_get_stats64, .ndo_fix_features = mtk_fix_features, @@ -2896,7 +2926,10 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) eth->netdev[id]->irq = eth->irq[0]; eth->netdev[id]->dev.of_node = np; - eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; + else + eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN; return 0; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 454cfcd465fd..fd3cec8f06ba 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -17,12 +17,13 @@ #include <linux/phylink.h> #define MTK_QDMA_PAGE_SIZE 2048 -#define MTK_MAX_RX_LENGTH 1536 +#define MTK_MAX_RX_LENGTH 1536 +#define MTK_MAX_RX_LENGTH_2K 2048 #define MTK_TX_DMA_BUF_LEN 0x3fff #define MTK_DMA_SIZE 256 #define MTK_NAPI_WEIGHT 64 #define MTK_MAC_COUNT 2 -#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN) +#define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN) #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN) #define MTK_DMA_DUMMY_DESC 0xffffffff #define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \ @@ -320,7 +321,12 @@ /* Mac control registers */ #define MTK_MAC_MCR(x) (0x10100 + (x * 0x100)) -#define MAC_MCR_MAX_RX_1536 BIT(24) +#define MAC_MCR_MAX_RX_MASK GENMASK(25, 24) +#define MAC_MCR_MAX_RX(_x) (MAC_MCR_MAX_RX_MASK & ((_x) << 24)) +#define MAC_MCR_MAX_RX_1518 0x0 +#define MAC_MCR_MAX_RX_1536 0x1 +#define MAC_MCR_MAX_RX_1552 0x2 +#define MAC_MCR_MAX_RX_2048 0x3 #define MAC_MCR_IPG_CFG (BIT(18) | BIT(16)) #define MAC_MCR_FORCE_MODE BIT(15) #define MAC_MCR_TX_EN BIT(14) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 32aad4d32b88..51b9700fce83 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2839,8 +2839,6 @@ static const struct net_device_ops mlx4_netdev_ops = { .ndo_rx_flow_steer = mlx4_en_filter_rfs, #endif .ndo_get_phys_port_id = mlx4_en_get_phys_port_id, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = mlx4_en_features_check, .ndo_set_tx_maxrate = mlx4_en_set_tx_maxrate, .ndo_bpf = mlx4_xdp, @@ -2873,8 +2871,6 @@ static const struct net_device_ops mlx4_netdev_ops_master = { .ndo_rx_flow_steer = mlx4_en_filter_rfs, #endif .ndo_get_phys_port_id = mlx4_en_get_phys_port_id, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = mlx4_en_features_check, .ndo_set_tx_maxrate = mlx4_en_set_tx_maxrate, .ndo_bpf = mlx4_xdp, diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index c1c9118a66c9..e35e4d7ef4d1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -682,8 +682,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud /* Protect accesses to: ring->xdp_prog, priv->mac_hash list */ rcu_read_lock(); xdp_prog = rcu_dereference(ring->xdp_prog); - xdp.rxq = &ring->xdp_rxq; - xdp.frame_sz = priv->frag_info[0].frag_stride; + xdp_init_buff(&xdp, priv->frag_info[0].frag_stride, &ring->xdp_rxq); doorbell_pending = false; /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx @@ -777,10 +776,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud priv->frag_info[0].frag_size, DMA_FROM_DEVICE); - xdp.data_hard_start = va - frags[0].page_offset; - xdp.data = va; - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = xdp.data + length; + xdp_prepare_buff(&xdp, va - frags[0].page_offset, + frags[0].page_offset, length, false); orig_data = xdp.data; act = bpf_prog_run_xdp(xdp_prog, &xdp); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index 6e4d7bb7fea2..ad45d20f9d44 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -203,3 +203,22 @@ config MLX5_SW_STEERING default y help Build support for software-managed steering in the NIC. + +config MLX5_SF + bool "Mellanox Technologies subfunction device support using auxiliary device" + depends on MLX5_CORE && MLX5_CORE_EN + default n + help + Build support for subfuction device in the NIC. A Mellanox subfunction + device can support RDMA, netdevice and vdpa device. + It is similar to a SRIOV VF but it doesn't require SRIOV support. + +config MLX5_SF_MANAGER + bool + depends on MLX5_SF && MLX5_ESWITCH + default y + help + Build support for subfuction port in the NIC. A Mellanox subfunction + port is managed through devlink. A subfunction supports RDMA, netdevice + and vdpa device. It is similar to a SRIOV VF but it doesn't require + SRIOV support. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 77961643d5a9..f61ee7110243 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -16,7 +16,8 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \ lib/devcom.o lib/pci_vsc.o lib/dm.o diag/fs_tracepoint.o \ - diag/fw_tracer.o diag/crdump.o devlink.o diag/rsc_dump.o fw_reset.o + diag/fw_tracer.o diag/crdump.o devlink.o diag/rsc_dump.o \ + fw_reset.o qos.o # # Netdev basic @@ -25,7 +26,8 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o \ en_selftest.o en/port.o en/monitor_stats.o en/health.o \ en/reporter_tx.o en/reporter_rx.o en/params.o en/xsk/pool.o \ - en/xsk/setup.o en/xsk/rx.o en/xsk/tx.o en/devlink.o en/ptp.o + en/xsk/setup.o en/xsk/rx.o en/xsk/tx.o en/devlink.o en/ptp.o \ + en/qos.o en/trap.o # # Netdev extra @@ -83,5 +85,15 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o steering/dr_matcher.o steering/dr_rule.o \ steering/dr_icm_pool.o steering/dr_buddy.o \ steering/dr_ste.o steering/dr_send.o \ + steering/dr_ste_v0.o \ steering/dr_cmd.o steering/dr_fw.o \ steering/dr_action.o steering/fs_dr.o +# +# SF device +# +mlx5_core-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o + +# +# SF manager +# +mlx5_core-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 50c7b9ee80c3..e8cecd50558d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -333,6 +333,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_DEALLOC_MEMIC: case MLX5_CMD_OP_PAGE_FAULT_RESUME: case MLX5_CMD_OP_QUERY_ESW_FUNCTIONS: + case MLX5_CMD_OP_DEALLOC_SF: return MLX5_CMD_STAT_OK; case MLX5_CMD_OP_QUERY_HCA_CAP: @@ -464,6 +465,9 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_ALLOC_MEMIC: case MLX5_CMD_OP_MODIFY_XRQ: case MLX5_CMD_OP_RELEASE_XRQ_ERROR: + case MLX5_CMD_OP_QUERY_VHCA_STATE: + case MLX5_CMD_OP_MODIFY_VHCA_STATE: + case MLX5_CMD_OP_ALLOC_SF: *status = MLX5_DRIVER_STATUS_ABORTED; *synd = MLX5_DRIVER_SYND; return -EIO; @@ -657,6 +661,10 @@ const char *mlx5_command_str(int command) MLX5_COMMAND_STR_CASE(DESTROY_UMEM); MLX5_COMMAND_STR_CASE(RELEASE_XRQ_ERROR); MLX5_COMMAND_STR_CASE(MODIFY_XRQ); + MLX5_COMMAND_STR_CASE(QUERY_VHCA_STATE); + MLX5_COMMAND_STR_CASE(MODIFY_VHCA_STATE); + MLX5_COMMAND_STR_CASE(ALLOC_SF); + MLX5_COMMAND_STR_CASE(DEALLOC_SF); default: return "unknown command opcode"; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 3261d0dc1104..aa76a6e0dae8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -7,6 +7,8 @@ #include "fw_reset.h" #include "fs_core.h" #include "eswitch.h" +#include "sf/dev/dev.h" +#include "sf/sf.h" static int mlx5_devlink_flash_update(struct devlink *devlink, struct devlink_flash_update_params *params, @@ -127,6 +129,17 @@ static int mlx5_devlink_reload_down(struct devlink *devlink, bool netns_change, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); + bool sf_dev_allocated; + + sf_dev_allocated = mlx5_sf_dev_allocated(dev); + if (sf_dev_allocated) { + /* Reload results in deleting SF device which further results in + * unregistering devlink instance while holding devlink_mutext. + * Hence, do not support reload. + */ + NL_SET_ERR_MSG_MOD(extack, "reload is unsupported when SFs are allocated\n"); + return -EOPNOTSUPP; + } switch (action) { case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: @@ -168,6 +181,91 @@ static int mlx5_devlink_reload_up(struct devlink *devlink, enum devlink_reload_a return 0; } +static struct mlx5_devlink_trap *mlx5_find_trap_by_id(struct mlx5_core_dev *dev, int trap_id) +{ + struct mlx5_devlink_trap *dl_trap; + + list_for_each_entry(dl_trap, &dev->priv.traps, list) + if (dl_trap->trap.id == trap_id) + return dl_trap; + + return NULL; +} + +static int mlx5_devlink_trap_init(struct devlink *devlink, const struct devlink_trap *trap, + void *trap_ctx) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_devlink_trap *dl_trap; + + dl_trap = kzalloc(sizeof(*dl_trap), GFP_KERNEL); + if (!dl_trap) + return -ENOMEM; + + dl_trap->trap.id = trap->id; + dl_trap->trap.action = DEVLINK_TRAP_ACTION_DROP; + dl_trap->item = trap_ctx; + + if (mlx5_find_trap_by_id(dev, trap->id)) { + kfree(dl_trap); + mlx5_core_err(dev, "Devlink trap: Trap 0x%x already found", trap->id); + return -EEXIST; + } + + list_add_tail(&dl_trap->list, &dev->priv.traps); + return 0; +} + +static void mlx5_devlink_trap_fini(struct devlink *devlink, const struct devlink_trap *trap, + void *trap_ctx) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_devlink_trap *dl_trap; + + dl_trap = mlx5_find_trap_by_id(dev, trap->id); + if (!dl_trap) { + mlx5_core_err(dev, "Devlink trap: Missing trap id 0x%x", trap->id); + return; + } + list_del(&dl_trap->list); + kfree(dl_trap); +} + +static int mlx5_devlink_trap_action_set(struct devlink *devlink, + const struct devlink_trap *trap, + enum devlink_trap_action action, + struct netlink_ext_ack *extack) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + enum devlink_trap_action action_orig; + struct mlx5_devlink_trap *dl_trap; + int err = 0; + + dl_trap = mlx5_find_trap_by_id(dev, trap->id); + if (!dl_trap) { + mlx5_core_err(dev, "Devlink trap: Set action on invalid trap id 0x%x", trap->id); + err = -EINVAL; + goto out; + } + + if (action != DEVLINK_TRAP_ACTION_DROP && action != DEVLINK_TRAP_ACTION_TRAP) { + err = -EOPNOTSUPP; + goto out; + } + + if (action == dl_trap->trap.action) + goto out; + + action_orig = dl_trap->trap.action; + dl_trap->trap.action = action; + err = mlx5_blocking_notifier_call_chain(dev, MLX5_DRIVER_EVENT_TYPE_TRAP, + &dl_trap->trap); + if (err) + dl_trap->trap.action = action_orig; +out: + return err; +} + static const struct devlink_ops mlx5_devlink_ops = { #ifdef CONFIG_MLX5_ESWITCH .eswitch_mode_set = mlx5_devlink_eswitch_mode_set, @@ -179,6 +277,12 @@ static const struct devlink_ops mlx5_devlink_ops = { .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 +#ifdef CONFIG_MLX5_SF_MANAGER + .port_new = mlx5_devlink_sf_port_new, + .port_del = mlx5_devlink_sf_port_del, + .port_fn_state_get = mlx5_devlink_sf_port_fn_state_get, + .port_fn_state_set = mlx5_devlink_sf_port_fn_state_set, +#endif .flash_update = mlx5_devlink_flash_update, .info_get = mlx5_devlink_info_get, .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) | @@ -186,8 +290,59 @@ static const struct devlink_ops mlx5_devlink_ops = { .reload_limits = BIT(DEVLINK_RELOAD_LIMIT_NO_RESET), .reload_down = mlx5_devlink_reload_down, .reload_up = mlx5_devlink_reload_up, + .trap_init = mlx5_devlink_trap_init, + .trap_fini = mlx5_devlink_trap_fini, + .trap_action_set = mlx5_devlink_trap_action_set, }; +void mlx5_devlink_trap_report(struct mlx5_core_dev *dev, int trap_id, struct sk_buff *skb, + struct devlink_port *dl_port) +{ + struct devlink *devlink = priv_to_devlink(dev); + struct mlx5_devlink_trap *dl_trap; + + dl_trap = mlx5_find_trap_by_id(dev, trap_id); + if (!dl_trap) { + mlx5_core_err(dev, "Devlink trap: Report on invalid trap id 0x%x", trap_id); + return; + } + + if (dl_trap->trap.action != DEVLINK_TRAP_ACTION_TRAP) { + mlx5_core_dbg(dev, "Devlink trap: Trap id %d has action %d", trap_id, + dl_trap->trap.action); + return; + } + devlink_trap_report(devlink, skb, dl_trap->item, dl_port, NULL); +} + +int mlx5_devlink_trap_get_num_active(struct mlx5_core_dev *dev) +{ + struct mlx5_devlink_trap *dl_trap; + int count = 0; + + list_for_each_entry(dl_trap, &dev->priv.traps, list) + if (dl_trap->trap.action == DEVLINK_TRAP_ACTION_TRAP) + count++; + + return count; +} + +int mlx5_devlink_traps_get_action(struct mlx5_core_dev *dev, int trap_id, + enum devlink_trap_action *action) +{ + struct mlx5_devlink_trap *dl_trap; + + dl_trap = mlx5_find_trap_by_id(dev, trap_id); + if (!dl_trap) { + mlx5_core_err(dev, "Devlink trap: Get action on invalid trap id 0x%x", + trap_id); + return -EINVAL; + } + + *action = dl_trap->trap.action; + return 0; +} + struct devlink *mlx5_devlink_alloc(void) { return devlink_alloc(&mlx5_devlink_ops, sizeof(struct mlx5_core_dev)); @@ -358,6 +513,49 @@ static void mlx5_devlink_set_params_init_values(struct devlink *devlink) #endif } +#define MLX5_TRAP_DROP(_id, _group_id) \ + DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ + DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ + DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT) + +static const struct devlink_trap mlx5_traps_arr[] = { + MLX5_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), + MLX5_TRAP_DROP(DMAC_FILTER, L2_DROPS), +}; + +static const struct devlink_trap_group mlx5_trap_groups_arr[] = { + DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0), +}; + +static int mlx5_devlink_traps_register(struct devlink *devlink) +{ + struct mlx5_core_dev *core_dev = devlink_priv(devlink); + int err; + + err = devlink_trap_groups_register(devlink, mlx5_trap_groups_arr, + ARRAY_SIZE(mlx5_trap_groups_arr)); + if (err) + return err; + + err = devlink_traps_register(devlink, mlx5_traps_arr, ARRAY_SIZE(mlx5_traps_arr), + &core_dev->priv); + if (err) + goto err_trap_group; + return 0; + +err_trap_group: + devlink_trap_groups_unregister(devlink, mlx5_trap_groups_arr, + ARRAY_SIZE(mlx5_trap_groups_arr)); + return err; +} + +static void mlx5_devlink_traps_unregister(struct devlink *devlink) +{ + devlink_traps_unregister(devlink, mlx5_traps_arr, ARRAY_SIZE(mlx5_traps_arr)); + devlink_trap_groups_unregister(devlink, mlx5_trap_groups_arr, + ARRAY_SIZE(mlx5_trap_groups_arr)); +} + int mlx5_devlink_register(struct devlink *devlink, struct device *dev) { int err; @@ -372,8 +570,16 @@ int mlx5_devlink_register(struct devlink *devlink, struct device *dev) goto params_reg_err; mlx5_devlink_set_params_init_values(devlink); devlink_params_publish(devlink); + + err = mlx5_devlink_traps_register(devlink); + if (err) + goto traps_reg_err; + return 0; +traps_reg_err: + devlink_params_unregister(devlink, mlx5_devlink_params, + ARRAY_SIZE(mlx5_devlink_params)); params_reg_err: devlink_unregister(devlink); return err; @@ -381,6 +587,7 @@ params_reg_err: void mlx5_devlink_unregister(struct devlink *devlink) { + mlx5_devlink_traps_unregister(devlink); devlink_params_unregister(devlink, mlx5_devlink_params, ARRAY_SIZE(mlx5_devlink_params)); devlink_unregister(devlink); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h index f0de327a59be..eff107dad922 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h @@ -12,6 +12,24 @@ enum mlx5_devlink_param_id { MLX5_DEVLINK_PARAM_ID_ESW_LARGE_GROUP_NUM, }; +struct mlx5_trap_ctx { + int id; + int action; +}; + +struct mlx5_devlink_trap { + struct mlx5_trap_ctx trap; + void *item; + struct list_head list; +}; + +struct mlx5_core_dev; +void mlx5_devlink_trap_report(struct mlx5_core_dev *dev, int trap_id, struct sk_buff *skb, + struct devlink_port *dl_port); +int mlx5_devlink_trap_get_num_active(struct mlx5_core_dev *dev); +int mlx5_devlink_traps_get_action(struct mlx5_core_dev *dev, int trap_id, + enum devlink_trap_action *action); + struct devlink *mlx5_devlink_alloc(void); void mlx5_devlink_free(struct devlink *devlink); int mlx5_devlink_register(struct devlink *devlink, struct device *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 055baf3b6cb1..39f389cc40fc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -55,6 +55,7 @@ #include "en_stats.h" #include "en/dcbnl.h" #include "en/fs.h" +#include "en/qos.h" #include "lib/hv_vhca.h" extern const struct net_device_ops mlx5e_netdev_ops; @@ -161,6 +162,9 @@ do { \ ##__VA_ARGS__); \ } while (0) +#define mlx5e_state_dereference(priv, p) \ + rcu_dereference_protected((p), lockdep_is_held(&(priv)->state_lock)) + enum mlx5e_rq_group { MLX5E_RQ_GROUP_REGULAR, MLX5E_RQ_GROUP_XSK, @@ -560,6 +564,7 @@ typedef bool (*mlx5e_fp_post_rx_wqes)(struct mlx5e_rq *rq); typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq*, u16); int mlx5e_rq_set_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params, bool xsk); +void mlx5e_rq_set_trap_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params); enum mlx5e_rq_flag { MLX5E_RQ_FLAG_XDP_XMIT, @@ -663,11 +668,13 @@ struct mlx5e_channel { struct mlx5e_xdpsq rq_xdpsq; struct mlx5e_txqsq sq[MLX5E_MAX_NUM_TC]; struct mlx5e_icosq icosq; /* internal control operations */ + struct mlx5e_txqsq __rcu * __rcu *qos_sqs; bool xdp; struct napi_struct napi; struct device *pdev; struct net_device *netdev; __be32 mkey_be; + u16 qos_sqs_size; u8 num_tc; u8 lag_port; @@ -756,6 +763,8 @@ struct mlx5e_modify_sq_param { int next_state; int rl_update; int rl_index; + bool qos_update; + u16 qos_queue_group_id; }; #if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE) @@ -788,10 +797,22 @@ struct mlx5e_scratchpad { cpumask_var_t cpumask; }; +struct mlx5e_htb { + DECLARE_HASHTABLE(qos_tc2node, order_base_2(MLX5E_QOS_MAX_LEAF_NODES)); + DECLARE_BITMAP(qos_used_qids, MLX5E_QOS_MAX_LEAF_NODES); + struct mlx5e_sq_stats **qos_sq_stats; + u16 max_qos_sqs; + u16 maj_id; + u16 defcls; +}; + +struct mlx5e_trap; + struct mlx5e_priv { /* priv data path fields - start */ /* +1 for port ptp ts */ - struct mlx5e_txqsq *txq2sq[(MLX5E_MAX_NUM_CHANNELS + 1) * MLX5E_MAX_NUM_TC]; + struct mlx5e_txqsq *txq2sq[(MLX5E_MAX_NUM_CHANNELS + 1) * MLX5E_MAX_NUM_TC + + MLX5E_QOS_MAX_LEAF_NODES]; int channel_tc2realtxq[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC]; int port_ptp_tc2realtxq[MLX5E_MAX_NUM_TC]; #ifdef CONFIG_MLX5_CORE_EN_DCB @@ -826,8 +847,10 @@ struct mlx5e_priv { struct mlx5_core_dev *mdev; struct net_device *netdev; + struct mlx5e_trap *en_trap; struct mlx5e_stats stats; struct mlx5e_channel_stats channel_stats[MLX5E_MAX_NUM_CHANNELS]; + struct mlx5e_channel_stats trap_stats; struct mlx5e_port_ptp_stats port_ptp_stats; u16 max_nch; u8 max_opened_tc; @@ -836,6 +859,7 @@ struct mlx5e_priv { u16 q_counter; u16 drop_rq_q_counter; struct notifier_block events_nb; + struct notifier_block blocking_events_nb; int num_tc_x_num_ch; struct udp_tunnel_nic_info nic_info; @@ -859,6 +883,7 @@ struct mlx5e_priv { struct mlx5e_hv_vhca_stats_agent stats_agent; #endif struct mlx5e_scratchpad scratchpad; + struct mlx5e_htb htb; }; struct mlx5e_rx_handlers { @@ -942,6 +967,8 @@ int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_params *params, int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time); void mlx5e_deactivate_rq(struct mlx5e_rq *rq); void mlx5e_close_rq(struct mlx5e_rq *rq); +int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param); +void mlx5e_destroy_rq(struct mlx5e_rq *rq); struct mlx5e_sq_param; int mlx5e_open_icosq(struct mlx5e_channel *c, struct mlx5e_params *params, @@ -986,6 +1013,7 @@ int mlx5e_safe_switch_channels(struct mlx5e_priv *priv, struct mlx5e_channels *new_chs, mlx5e_fp_preactivate preactivate, void *context); +int mlx5e_update_tx_netdev_queues(struct mlx5e_priv *priv); int mlx5e_num_channels_changed(struct mlx5e_priv *priv); int mlx5e_num_channels_changed_ctx(struct mlx5e_priv *priv, void *context); void mlx5e_activate_priv_channels(struct mlx5e_priv *priv); @@ -1010,6 +1038,9 @@ void mlx5e_deactivate_icosq(struct mlx5e_icosq *icosq); int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn, struct mlx5e_modify_sq_param *p); +int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix, + struct mlx5e_params *params, struct mlx5e_sq_param *param, + struct mlx5e_txqsq *sq, int tc, u16 qos_queue_group_id, u16 qos_qid); void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq); void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq); void mlx5e_free_txqsq(struct mlx5e_txqsq *sq); @@ -1020,8 +1051,10 @@ struct mlx5e_create_sq_param; int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev, struct mlx5e_sq_param *param, struct mlx5e_create_sq_param *csp, + u16 qos_queue_group_id, u32 *sqn); void mlx5e_tx_err_cqe_work(struct work_struct *recover_work); +void mlx5e_close_txqsq(struct mlx5e_txqsq *sq); static inline bool mlx5_tx_swp_supported(struct mlx5_core_dev *mdev) { @@ -1047,6 +1080,8 @@ void mlx5e_destroy_q_counters(struct mlx5e_priv *priv); int mlx5e_open_drop_rq(struct mlx5e_priv *priv, struct mlx5e_rq *drop_rq); void mlx5e_close_drop_rq(struct mlx5e_rq *drop_rq); +int mlx5e_init_di_list(struct mlx5e_rq *rq, int wq_sz, int node); +void mlx5e_free_di_list(struct mlx5e_rq *rq); int mlx5e_create_indirect_rqt(struct mlx5e_priv *priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h index 5749557749b0..a16297e7e2ac 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h @@ -44,6 +44,11 @@ struct mlx5e_l2_rule { #define MLX5E_L2_ADDR_HASH_SIZE BIT(BITS_PER_BYTE) +struct mlx5e_promisc_table { + struct mlx5e_flow_table ft; + struct mlx5_flow_handle *rule; +}; + struct mlx5e_vlan_table { struct mlx5e_flow_table ft; DECLARE_BITMAP(active_cvlans, VLAN_N_VID); @@ -53,6 +58,7 @@ struct mlx5e_vlan_table { struct mlx5_flow_handle *untagged_rule; struct mlx5_flow_handle *any_cvlan_rule; struct mlx5_flow_handle *any_svlan_rule; + struct mlx5_flow_handle *trap_rule; bool cvlan_filter_disabled; }; @@ -62,7 +68,7 @@ struct mlx5e_l2_table { struct hlist_head netdev_mc[MLX5E_L2_ADDR_HASH_SIZE]; struct mlx5e_l2_rule broadcast; struct mlx5e_l2_rule allmulti; - struct mlx5e_l2_rule promisc; + struct mlx5_flow_handle *trap_rule; bool broadcast_enabled; bool allmulti_enabled; bool promisc_enabled; @@ -126,7 +132,8 @@ struct mlx5e_ttc_table { /* NIC prio FTS */ enum { - MLX5E_VLAN_FT_LEVEL = 0, + MLX5E_PROMISC_FT_LEVEL, + MLX5E_VLAN_FT_LEVEL, MLX5E_L2_FT_LEVEL, MLX5E_TTC_FT_LEVEL, MLX5E_INNER_TTC_FT_LEVEL, @@ -241,6 +248,7 @@ struct mlx5e_flow_steering { struct mlx5e_ethtool_steering ethtool; #endif struct mlx5e_tc_table tc; + struct mlx5e_promisc_table promisc; struct mlx5e_vlan_table vlan; struct mlx5e_l2_table l2; struct mlx5e_ttc_table ttc; @@ -288,6 +296,10 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv); void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv); u8 mlx5e_get_proto_by_tunnel_type(enum mlx5e_tunnel_types tt); +int mlx5e_add_vlan_trap(struct mlx5e_priv *priv, int trap_id, int tir_num); +void mlx5e_remove_vlan_trap(struct mlx5e_priv *priv); +int mlx5e_add_mac_trap(struct mlx5e_priv *priv, int trap_id, int tir_num); +void mlx5e_remove_mac_trap(struct mlx5e_priv *priv); #endif /* __MLX5E_FLOW_STEER_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h index 807147d97a0f..ea2cfb04b31a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h @@ -118,6 +118,8 @@ void mlx5e_build_rq_param(struct mlx5e_priv *priv, struct mlx5e_rq_param *param); void mlx5e_build_sq_param_common(struct mlx5e_priv *priv, struct mlx5e_sq_param *param); +void mlx5e_build_sq_param(struct mlx5e_priv *priv, struct mlx5e_params *params, + struct mlx5e_sq_param *param); void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv, struct mlx5e_params *params, struct mlx5e_xsk_param *xsk, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index 2a2bac30daaa..eeddd1137dda 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -261,7 +261,7 @@ static int mlx5e_ptp_open_txqsq(struct mlx5e_port_ptp *c, u32 tisn, csp.min_inline_mode = txqsq->min_inline_mode; csp.ts_cqe_to_dest_cqn = ptpsq->ts_cq.mcq.cqn; - err = mlx5e_create_sq_rdy(c->mdev, sqp, &csp, &txqsq->sqn); + err = mlx5e_create_sq_rdy(c->mdev, sqp, &csp, 0, &txqsq->sqn); if (err) goto err_free_txqsq; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c new file mode 100644 index 000000000000..12d7ad061237 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c @@ -0,0 +1,984 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */ + +#include "en.h" +#include "params.h" +#include "../qos.h" + +#define BYTES_IN_MBIT 125000 + +int mlx5e_qos_max_leaf_nodes(struct mlx5_core_dev *mdev) +{ + return min(MLX5E_QOS_MAX_LEAF_NODES, mlx5_qos_max_leaf_nodes(mdev)); +} + +int mlx5e_qos_cur_leaf_nodes(struct mlx5e_priv *priv) +{ + int last = find_last_bit(priv->htb.qos_used_qids, mlx5e_qos_max_leaf_nodes(priv->mdev)); + + return last == mlx5e_qos_max_leaf_nodes(priv->mdev) ? 0 : last + 1; +} + +/* Software representation of the QoS tree (internal to this file) */ + +static int mlx5e_find_unused_qos_qid(struct mlx5e_priv *priv) +{ + int size = mlx5e_qos_max_leaf_nodes(priv->mdev); + int res; + + WARN_ONCE(!mutex_is_locked(&priv->state_lock), "%s: state_lock is not held\n", __func__); + res = find_first_zero_bit(priv->htb.qos_used_qids, size); + + return res == size ? -ENOSPC : res; +} + +struct mlx5e_qos_node { + struct hlist_node hnode; + struct rcu_head rcu; + struct mlx5e_qos_node *parent; + u64 rate; + u32 bw_share; + u32 max_average_bw; + u32 hw_id; + u32 classid; /* 16-bit, except root. */ + u16 qid; +}; + +#define MLX5E_QOS_QID_INNER 0xffff +#define MLX5E_HTB_CLASSID_ROOT 0xffffffff + +static struct mlx5e_qos_node * +mlx5e_sw_node_create_leaf(struct mlx5e_priv *priv, u16 classid, u16 qid, + struct mlx5e_qos_node *parent) +{ + struct mlx5e_qos_node *node; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return ERR_PTR(-ENOMEM); + + node->parent = parent; + + node->qid = qid; + __set_bit(qid, priv->htb.qos_used_qids); + + node->classid = classid; + hash_add_rcu(priv->htb.qos_tc2node, &node->hnode, classid); + + mlx5e_update_tx_netdev_queues(priv); + + return node; +} + +static struct mlx5e_qos_node *mlx5e_sw_node_create_root(struct mlx5e_priv *priv) +{ + struct mlx5e_qos_node *node; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return ERR_PTR(-ENOMEM); + + node->qid = MLX5E_QOS_QID_INNER; + node->classid = MLX5E_HTB_CLASSID_ROOT; + hash_add_rcu(priv->htb.qos_tc2node, &node->hnode, node->classid); + + return node; +} + +static struct mlx5e_qos_node *mlx5e_sw_node_find(struct mlx5e_priv *priv, u32 classid) +{ + struct mlx5e_qos_node *node = NULL; + + hash_for_each_possible(priv->htb.qos_tc2node, node, hnode, classid) { + if (node->classid == classid) + break; + } + + return node; +} + +static struct mlx5e_qos_node *mlx5e_sw_node_find_rcu(struct mlx5e_priv *priv, u32 classid) +{ + struct mlx5e_qos_node *node = NULL; + + hash_for_each_possible_rcu(priv->htb.qos_tc2node, node, hnode, classid) { + if (node->classid == classid) + break; + } + + return node; +} + +static void mlx5e_sw_node_delete(struct mlx5e_priv *priv, struct mlx5e_qos_node *node) +{ + hash_del_rcu(&node->hnode); + if (node->qid != MLX5E_QOS_QID_INNER) { + __clear_bit(node->qid, priv->htb.qos_used_qids); + mlx5e_update_tx_netdev_queues(priv); + } + kfree_rcu(node, rcu); +} + +/* TX datapath API */ + +static u16 mlx5e_qid_from_qos(struct mlx5e_channels *chs, u16 qid) +{ + /* These channel params are safe to access from the datapath, because: + * 1. This function is called only after checking priv->htb.maj_id != 0, + * and the number of queues can't change while HTB offload is active. + * 2. When priv->htb.maj_id becomes 0, synchronize_rcu waits for + * mlx5e_select_queue to finish while holding priv->state_lock, + * preventing other code from changing the number of queues. + */ + bool is_ptp = MLX5E_GET_PFLAG(&chs->params, MLX5E_PFLAG_TX_PORT_TS); + + return (chs->params.num_channels + is_ptp) * chs->params.num_tc + qid; +} + +int mlx5e_get_txq_by_classid(struct mlx5e_priv *priv, u16 classid) +{ + struct mlx5e_qos_node *node; + u16 qid; + int res; + + rcu_read_lock(); + + node = mlx5e_sw_node_find_rcu(priv, classid); + if (!node) { + res = -ENOENT; + goto out; + } + qid = READ_ONCE(node->qid); + if (qid == MLX5E_QOS_QID_INNER) { + res = -EINVAL; + goto out; + } + res = mlx5e_qid_from_qos(&priv->channels, qid); + +out: + rcu_read_unlock(); + return res; +} + +static struct mlx5e_txqsq *mlx5e_get_qos_sq(struct mlx5e_priv *priv, int qid) +{ + struct mlx5e_params *params = &priv->channels.params; + struct mlx5e_txqsq __rcu **qos_sqs; + struct mlx5e_channel *c; + int ix; + + ix = qid % params->num_channels; + qid /= params->num_channels; + c = priv->channels.c[ix]; + + qos_sqs = mlx5e_state_dereference(priv, c->qos_sqs); + return mlx5e_state_dereference(priv, qos_sqs[qid]); +} + +/* SQ lifecycle */ + +static int mlx5e_open_qos_sq(struct mlx5e_priv *priv, struct mlx5e_channels *chs, + struct mlx5e_qos_node *node) +{ + struct mlx5e_create_cq_param ccp = {}; + struct mlx5e_txqsq __rcu **qos_sqs; + struct mlx5e_sq_param param_sq; + struct mlx5e_cq_param param_cq; + int txq_ix, ix, qid, err = 0; + struct mlx5e_params *params; + struct mlx5e_channel *c; + struct mlx5e_txqsq *sq; + + params = &chs->params; + + txq_ix = mlx5e_qid_from_qos(chs, node->qid); + + WARN_ON(node->qid > priv->htb.max_qos_sqs); + if (node->qid == priv->htb.max_qos_sqs) { + struct mlx5e_sq_stats *stats, **stats_list = NULL; + + if (priv->htb.max_qos_sqs == 0) { + stats_list = kvcalloc(mlx5e_qos_max_leaf_nodes(priv->mdev), + sizeof(*stats_list), + GFP_KERNEL); + if (!stats_list) + return -ENOMEM; + } + stats = kzalloc(sizeof(*stats), GFP_KERNEL); + if (!stats) { + kvfree(stats_list); + return -ENOMEM; + } + if (stats_list) + WRITE_ONCE(priv->htb.qos_sq_stats, stats_list); + WRITE_ONCE(priv->htb.qos_sq_stats[node->qid], stats); + /* Order max_qos_sqs increment after writing the array pointer. + * Pairs with smp_load_acquire in en_stats.c. + */ + smp_store_release(&priv->htb.max_qos_sqs, priv->htb.max_qos_sqs + 1); + } + + ix = node->qid % params->num_channels; + qid = node->qid / params->num_channels; + c = chs->c[ix]; + + qos_sqs = mlx5e_state_dereference(priv, c->qos_sqs); + sq = kzalloc(sizeof(*sq), GFP_KERNEL); + + if (!sq) + return -ENOMEM; + + mlx5e_build_create_cq_param(&ccp, c); + + memset(¶m_sq, 0, sizeof(param_sq)); + memset(¶m_cq, 0, sizeof(param_cq)); + mlx5e_build_sq_param(priv, params, ¶m_sq); + mlx5e_build_tx_cq_param(priv, params, ¶m_cq); + err = mlx5e_open_cq(priv, params->tx_cq_moderation, ¶m_cq, &ccp, &sq->cq); + if (err) + goto err_free_sq; + err = mlx5e_open_txqsq(c, priv->tisn[c->lag_port][0], txq_ix, params, + ¶m_sq, sq, 0, node->hw_id, node->qid); + if (err) + goto err_close_cq; + + rcu_assign_pointer(qos_sqs[qid], sq); + + return 0; + +err_close_cq: + mlx5e_close_cq(&sq->cq); +err_free_sq: + kfree(sq); + return err; +} + +static void mlx5e_activate_qos_sq(struct mlx5e_priv *priv, struct mlx5e_qos_node *node) +{ + struct mlx5e_txqsq *sq; + + sq = mlx5e_get_qos_sq(priv, node->qid); + + WRITE_ONCE(priv->txq2sq[mlx5e_qid_from_qos(&priv->channels, node->qid)], sq); + + /* Make the change to txq2sq visible before the queue is started. + * As mlx5e_xmit runs under a spinlock, there is an implicit ACQUIRE, + * which pairs with this barrier. + */ + smp_wmb(); + + qos_dbg(priv->mdev, "Activate QoS SQ qid %u\n", node->qid); + mlx5e_activate_txqsq(sq); +} + +static void mlx5e_deactivate_qos_sq(struct mlx5e_priv *priv, u16 qid) +{ + struct mlx5e_txqsq *sq; + + sq = mlx5e_get_qos_sq(priv, qid); + if (!sq) /* Handle the case when the SQ failed to open. */ + return; + + qos_dbg(priv->mdev, "Deactivate QoS SQ qid %u\n", qid); + mlx5e_deactivate_txqsq(sq); + + /* The queue is disabled, no synchronization with datapath is needed. */ + priv->txq2sq[mlx5e_qid_from_qos(&priv->channels, qid)] = NULL; +} + +static void mlx5e_close_qos_sq(struct mlx5e_priv *priv, u16 qid) +{ + struct mlx5e_txqsq __rcu **qos_sqs; + struct mlx5e_params *params; + struct mlx5e_channel *c; + struct mlx5e_txqsq *sq; + int ix; + + params = &priv->channels.params; + + ix = qid % params->num_channels; + qid /= params->num_channels; + c = priv->channels.c[ix]; + qos_sqs = mlx5e_state_dereference(priv, c->qos_sqs); + sq = rcu_replace_pointer(qos_sqs[qid], NULL, lockdep_is_held(&priv->state_lock)); + if (!sq) /* Handle the case when the SQ failed to open. */ + return; + + synchronize_rcu(); /* Sync with NAPI. */ + + mlx5e_close_txqsq(sq); + mlx5e_close_cq(&sq->cq); + kfree(sq); +} + +void mlx5e_qos_close_queues(struct mlx5e_channel *c) +{ + struct mlx5e_txqsq __rcu **qos_sqs; + int i; + + qos_sqs = rcu_replace_pointer(c->qos_sqs, NULL, lockdep_is_held(&c->priv->state_lock)); + if (!qos_sqs) + return; + synchronize_rcu(); /* Sync with NAPI. */ + + for (i = 0; i < c->qos_sqs_size; i++) { + struct mlx5e_txqsq *sq; + + sq = mlx5e_state_dereference(c->priv, qos_sqs[i]); + if (!sq) /* Handle the case when the SQ failed to open. */ + continue; + + mlx5e_close_txqsq(sq); + mlx5e_close_cq(&sq->cq); + kfree(sq); + } + + kvfree(qos_sqs); +} + +static void mlx5e_qos_close_all_queues(struct mlx5e_channels *chs) +{ + int i; + + for (i = 0; i < chs->num; i++) + mlx5e_qos_close_queues(chs->c[i]); +} + +static int mlx5e_qos_alloc_queues(struct mlx5e_priv *priv, struct mlx5e_channels *chs) +{ + u16 qos_sqs_size; + int i; + + qos_sqs_size = DIV_ROUND_UP(mlx5e_qos_max_leaf_nodes(priv->mdev), chs->num); + + for (i = 0; i < chs->num; i++) { + struct mlx5e_txqsq **sqs; + + sqs = kvcalloc(qos_sqs_size, sizeof(struct mlx5e_txqsq *), GFP_KERNEL); + if (!sqs) + goto err_free; + + WRITE_ONCE(chs->c[i]->qos_sqs_size, qos_sqs_size); + smp_wmb(); /* Pairs with mlx5e_napi_poll. */ + rcu_assign_pointer(chs->c[i]->qos_sqs, sqs); + } + + return 0; + +err_free: + while (--i >= 0) { + struct mlx5e_txqsq **sqs; + + sqs = rcu_replace_pointer(chs->c[i]->qos_sqs, NULL, + lockdep_is_held(&priv->state_lock)); + + synchronize_rcu(); /* Sync with NAPI. */ + kvfree(sqs); + } + return -ENOMEM; +} + +int mlx5e_qos_open_queues(struct mlx5e_priv *priv, struct mlx5e_channels *chs) +{ + struct mlx5e_qos_node *node = NULL; + int bkt, err; + + if (!priv->htb.maj_id) + return 0; + + err = mlx5e_qos_alloc_queues(priv, chs); + if (err) + return err; + + hash_for_each(priv->htb.qos_tc2node, bkt, node, hnode) { + if (node->qid == MLX5E_QOS_QID_INNER) + continue; + err = mlx5e_open_qos_sq(priv, chs, node); + if (err) { + mlx5e_qos_close_all_queues(chs); + return err; + } + } + + return 0; +} + +void mlx5e_qos_activate_queues(struct mlx5e_priv *priv) +{ + struct mlx5e_qos_node *node = NULL; + int bkt; + + hash_for_each(priv->htb.qos_tc2node, bkt, node, hnode) { + if (node->qid == MLX5E_QOS_QID_INNER) + continue; + mlx5e_activate_qos_sq(priv, node); + } +} + +void mlx5e_qos_deactivate_queues(struct mlx5e_channel *c) +{ + struct mlx5e_params *params = &c->priv->channels.params; + struct mlx5e_txqsq __rcu **qos_sqs; + int i; + + qos_sqs = mlx5e_state_dereference(c->priv, c->qos_sqs); + if (!qos_sqs) + return; + + for (i = 0; i < c->qos_sqs_size; i++) { + u16 qid = params->num_channels * i + c->ix; + struct mlx5e_txqsq *sq; + + sq = mlx5e_state_dereference(c->priv, qos_sqs[i]); + if (!sq) /* Handle the case when the SQ failed to open. */ + continue; + + qos_dbg(c->mdev, "Deactivate QoS SQ qid %u\n", qid); + mlx5e_deactivate_txqsq(sq); + + /* The queue is disabled, no synchronization with datapath is needed. */ + c->priv->txq2sq[mlx5e_qid_from_qos(&c->priv->channels, qid)] = NULL; + } +} + +static void mlx5e_qos_deactivate_all_queues(struct mlx5e_channels *chs) +{ + int i; + + for (i = 0; i < chs->num; i++) + mlx5e_qos_deactivate_queues(chs->c[i]); +} + +/* HTB API */ + +int mlx5e_htb_root_add(struct mlx5e_priv *priv, u16 htb_maj_id, u16 htb_defcls, + struct netlink_ext_ack *extack) +{ + struct mlx5e_qos_node *root; + bool opened; + int err; + + qos_dbg(priv->mdev, "TC_HTB_CREATE handle %04x:, default :%04x\n", htb_maj_id, htb_defcls); + + if (!mlx5_qos_is_supported(priv->mdev)) { + NL_SET_ERR_MSG_MOD(extack, + "Missing QoS capabilities. Try disabling SRIOV or use a supported device."); + return -EOPNOTSUPP; + } + + opened = test_bit(MLX5E_STATE_OPENED, &priv->state); + if (opened) { + err = mlx5e_qos_alloc_queues(priv, &priv->channels); + if (err) + return err; + } + + root = mlx5e_sw_node_create_root(priv); + if (IS_ERR(root)) { + err = PTR_ERR(root); + goto err_free_queues; + } + + err = mlx5_qos_create_root_node(priv->mdev, &root->hw_id); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Firmware error. Try upgrading firmware."); + goto err_sw_node_delete; + } + + WRITE_ONCE(priv->htb.defcls, htb_defcls); + /* Order maj_id after defcls - pairs with + * mlx5e_select_queue/mlx5e_select_htb_queues. + */ + smp_store_release(&priv->htb.maj_id, htb_maj_id); + + return 0; + +err_sw_node_delete: + mlx5e_sw_node_delete(priv, root); + +err_free_queues: + if (opened) + mlx5e_qos_close_all_queues(&priv->channels); + return err; +} + +int mlx5e_htb_root_del(struct mlx5e_priv *priv) +{ + struct mlx5e_qos_node *root; + int err; + + qos_dbg(priv->mdev, "TC_HTB_DESTROY\n"); + + WRITE_ONCE(priv->htb.maj_id, 0); + synchronize_rcu(); /* Sync with mlx5e_select_htb_queue and TX data path. */ + + root = mlx5e_sw_node_find(priv, MLX5E_HTB_CLASSID_ROOT); + if (!root) { + qos_err(priv->mdev, "Failed to find the root node in the QoS tree\n"); + return -ENOENT; + } + err = mlx5_qos_destroy_node(priv->mdev, root->hw_id); + if (err) + qos_err(priv->mdev, "Failed to destroy root node %u, err = %d\n", + root->hw_id, err); + mlx5e_sw_node_delete(priv, root); + + mlx5e_qos_deactivate_all_queues(&priv->channels); + mlx5e_qos_close_all_queues(&priv->channels); + + return err; +} + +static int mlx5e_htb_convert_rate(struct mlx5e_priv *priv, u64 rate, + struct mlx5e_qos_node *parent, u32 *bw_share) +{ + u64 share = 0; + + while (parent->classid != MLX5E_HTB_CLASSID_ROOT && !parent->max_average_bw) + parent = parent->parent; + + if (parent->max_average_bw) + share = div64_u64(div_u64(rate * 100, BYTES_IN_MBIT), + parent->max_average_bw); + else + share = 101; + + *bw_share = share == 0 ? 1 : share > 100 ? 0 : share; + + qos_dbg(priv->mdev, "Convert: rate %llu, parent ceil %llu -> bw_share %u\n", + rate, (u64)parent->max_average_bw * BYTES_IN_MBIT, *bw_share); + + return 0; +} + +static void mlx5e_htb_convert_ceil(struct mlx5e_priv *priv, u64 ceil, u32 *max_average_bw) +{ + *max_average_bw = div_u64(ceil, BYTES_IN_MBIT); + + qos_dbg(priv->mdev, "Convert: ceil %llu -> max_average_bw %u\n", + ceil, *max_average_bw); +} + +int mlx5e_htb_leaf_alloc_queue(struct mlx5e_priv *priv, u16 classid, + u32 parent_classid, u64 rate, u64 ceil, + struct netlink_ext_ack *extack) +{ + struct mlx5e_qos_node *node, *parent; + int qid; + int err; + + qos_dbg(priv->mdev, "TC_HTB_LEAF_ALLOC_QUEUE classid %04x, parent %04x, rate %llu, ceil %llu\n", + classid, parent_classid, rate, ceil); + + qid = mlx5e_find_unused_qos_qid(priv); + if (qid < 0) { + NL_SET_ERR_MSG_MOD(extack, "Maximum amount of leaf classes is reached."); + return qid; + } + + parent = mlx5e_sw_node_find(priv, parent_classid); + if (!parent) + return -EINVAL; + + node = mlx5e_sw_node_create_leaf(priv, classid, qid, parent); + if (IS_ERR(node)) + return PTR_ERR(node); + + node->rate = rate; + mlx5e_htb_convert_rate(priv, rate, node->parent, &node->bw_share); + mlx5e_htb_convert_ceil(priv, ceil, &node->max_average_bw); + + err = mlx5_qos_create_leaf_node(priv->mdev, node->parent->hw_id, + node->bw_share, node->max_average_bw, + &node->hw_id); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating a leaf node."); + qos_err(priv->mdev, "Failed to create a leaf node (class %04x), err = %d\n", + classid, err); + mlx5e_sw_node_delete(priv, node); + return err; + } + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + err = mlx5e_open_qos_sq(priv, &priv->channels, node); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ."); + qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x), err = %d\n", + classid, err); + } else { + mlx5e_activate_qos_sq(priv, node); + } + } + + return mlx5e_qid_from_qos(&priv->channels, node->qid); +} + +int mlx5e_htb_leaf_to_inner(struct mlx5e_priv *priv, u16 classid, u16 child_classid, + u64 rate, u64 ceil, struct netlink_ext_ack *extack) +{ + struct mlx5e_qos_node *node, *child; + int err, tmp_err; + u32 new_hw_id; + u16 qid; + + qos_dbg(priv->mdev, "TC_HTB_LEAF_TO_INNER classid %04x, upcoming child %04x, rate %llu, ceil %llu\n", + classid, child_classid, rate, ceil); + + node = mlx5e_sw_node_find(priv, classid); + if (!node) + return -ENOENT; + + err = mlx5_qos_create_inner_node(priv->mdev, node->parent->hw_id, + node->bw_share, node->max_average_bw, + &new_hw_id); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating an inner node."); + qos_err(priv->mdev, "Failed to create an inner node (class %04x), err = %d\n", + classid, err); + return err; + } + + /* Intentionally reuse the qid for the upcoming first child. */ + child = mlx5e_sw_node_create_leaf(priv, child_classid, node->qid, node); + if (IS_ERR(child)) { + err = PTR_ERR(child); + goto err_destroy_hw_node; + } + + child->rate = rate; + mlx5e_htb_convert_rate(priv, rate, node, &child->bw_share); + mlx5e_htb_convert_ceil(priv, ceil, &child->max_average_bw); + + err = mlx5_qos_create_leaf_node(priv->mdev, new_hw_id, child->bw_share, + child->max_average_bw, &child->hw_id); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating a leaf node."); + qos_err(priv->mdev, "Failed to create a leaf node (class %04x), err = %d\n", + classid, err); + goto err_delete_sw_node; + } + + /* No fail point. */ + + qid = node->qid; + /* Pairs with mlx5e_get_txq_by_classid. */ + WRITE_ONCE(node->qid, MLX5E_QOS_QID_INNER); + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + mlx5e_deactivate_qos_sq(priv, qid); + mlx5e_close_qos_sq(priv, qid); + } + + err = mlx5_qos_destroy_node(priv->mdev, node->hw_id); + if (err) /* Not fatal. */ + qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n", + node->hw_id, classid, err); + + node->hw_id = new_hw_id; + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + err = mlx5e_open_qos_sq(priv, &priv->channels, child); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ."); + qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x), err = %d\n", + classid, err); + } else { + mlx5e_activate_qos_sq(priv, child); + } + } + + return 0; + +err_delete_sw_node: + child->qid = MLX5E_QOS_QID_INNER; + mlx5e_sw_node_delete(priv, child); + +err_destroy_hw_node: + tmp_err = mlx5_qos_destroy_node(priv->mdev, new_hw_id); + if (tmp_err) /* Not fatal. */ + qos_warn(priv->mdev, "Failed to roll back creation of an inner node %u (class %04x), err = %d\n", + new_hw_id, classid, tmp_err); + return err; +} + +static struct mlx5e_qos_node *mlx5e_sw_node_find_by_qid(struct mlx5e_priv *priv, u16 qid) +{ + struct mlx5e_qos_node *node = NULL; + int bkt; + + hash_for_each(priv->htb.qos_tc2node, bkt, node, hnode) + if (node->qid == qid) + break; + + return node; +} + +static void mlx5e_reactivate_qos_sq(struct mlx5e_priv *priv, u16 qid, struct netdev_queue *txq) +{ + qos_dbg(priv->mdev, "Reactivate QoS SQ qid %u\n", qid); + netdev_tx_reset_queue(txq); + netif_tx_start_queue(txq); +} + +static void mlx5e_reset_qdisc(struct net_device *dev, u16 qid) +{ + struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, qid); + struct Qdisc *qdisc = dev_queue->qdisc_sleeping; + + if (!qdisc) + return; + + spin_lock_bh(qdisc_lock(qdisc)); + qdisc_reset(qdisc); + spin_unlock_bh(qdisc_lock(qdisc)); +} + +int mlx5e_htb_leaf_del(struct mlx5e_priv *priv, u16 classid, u16 *old_qid, + u16 *new_qid, struct netlink_ext_ack *extack) +{ + struct mlx5e_qos_node *node; + struct netdev_queue *txq; + u16 qid, moved_qid; + bool opened; + int err; + + qos_dbg(priv->mdev, "TC_HTB_LEAF_DEL classid %04x\n", classid); + + *old_qid = *new_qid = 0; + + node = mlx5e_sw_node_find(priv, classid); + if (!node) + return -ENOENT; + + /* Store qid for reuse. */ + qid = node->qid; + + opened = test_bit(MLX5E_STATE_OPENED, &priv->state); + if (opened) { + txq = netdev_get_tx_queue(priv->netdev, + mlx5e_qid_from_qos(&priv->channels, qid)); + mlx5e_deactivate_qos_sq(priv, qid); + mlx5e_close_qos_sq(priv, qid); + } + + err = mlx5_qos_destroy_node(priv->mdev, node->hw_id); + if (err) /* Not fatal. */ + qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n", + node->hw_id, classid, err); + + mlx5e_sw_node_delete(priv, node); + + moved_qid = mlx5e_qos_cur_leaf_nodes(priv); + + if (moved_qid == 0) { + /* The last QoS SQ was just destroyed. */ + if (opened) + mlx5e_reactivate_qos_sq(priv, qid, txq); + return 0; + } + moved_qid--; + + if (moved_qid < qid) { + /* The highest QoS SQ was just destroyed. */ + WARN(moved_qid != qid - 1, "Gaps in queue numeration: destroyed queue %u, the highest queue is %u", + qid, moved_qid); + if (opened) + mlx5e_reactivate_qos_sq(priv, qid, txq); + return 0; + } + + WARN(moved_qid == qid, "Can't move node with qid %u to itself", qid); + qos_dbg(priv->mdev, "Moving QoS SQ %u to %u\n", moved_qid, qid); + + node = mlx5e_sw_node_find_by_qid(priv, moved_qid); + WARN(!node, "Could not find a node with qid %u to move to queue %u", + moved_qid, qid); + + /* Stop traffic to the old queue. */ + WRITE_ONCE(node->qid, MLX5E_QOS_QID_INNER); + __clear_bit(moved_qid, priv->htb.qos_used_qids); + + if (opened) { + txq = netdev_get_tx_queue(priv->netdev, + mlx5e_qid_from_qos(&priv->channels, moved_qid)); + mlx5e_deactivate_qos_sq(priv, moved_qid); + mlx5e_close_qos_sq(priv, moved_qid); + } + + /* Prevent packets from the old class from getting into the new one. */ + mlx5e_reset_qdisc(priv->netdev, moved_qid); + + __set_bit(qid, priv->htb.qos_used_qids); + WRITE_ONCE(node->qid, qid); + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + err = mlx5e_open_qos_sq(priv, &priv->channels, node); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ."); + qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x) while moving qid %u to %u, err = %d\n", + node->classid, moved_qid, qid, err); + } else { + mlx5e_activate_qos_sq(priv, node); + } + } + + mlx5e_update_tx_netdev_queues(priv); + if (opened) + mlx5e_reactivate_qos_sq(priv, moved_qid, txq); + + *old_qid = mlx5e_qid_from_qos(&priv->channels, moved_qid); + *new_qid = mlx5e_qid_from_qos(&priv->channels, qid); + return 0; +} + +int mlx5e_htb_leaf_del_last(struct mlx5e_priv *priv, u16 classid, bool force, + struct netlink_ext_ack *extack) +{ + struct mlx5e_qos_node *node, *parent; + u32 old_hw_id, new_hw_id; + int err, saved_err = 0; + u16 qid; + + qos_dbg(priv->mdev, "TC_HTB_LEAF_DEL_LAST%s classid %04x\n", + force ? "_FORCE" : "", classid); + + node = mlx5e_sw_node_find(priv, classid); + if (!node) + return -ENOENT; + + err = mlx5_qos_create_leaf_node(priv->mdev, node->parent->parent->hw_id, + node->parent->bw_share, + node->parent->max_average_bw, + &new_hw_id); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating a leaf node."); + qos_err(priv->mdev, "Failed to create a leaf node (class %04x), err = %d\n", + classid, err); + if (!force) + return err; + saved_err = err; + } + + /* Store qid for reuse and prevent clearing the bit. */ + qid = node->qid; + /* Pairs with mlx5e_get_txq_by_classid. */ + WRITE_ONCE(node->qid, MLX5E_QOS_QID_INNER); + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + mlx5e_deactivate_qos_sq(priv, qid); + mlx5e_close_qos_sq(priv, qid); + } + + /* Prevent packets from the old class from getting into the new one. */ + mlx5e_reset_qdisc(priv->netdev, qid); + + err = mlx5_qos_destroy_node(priv->mdev, node->hw_id); + if (err) /* Not fatal. */ + qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n", + node->hw_id, classid, err); + + parent = node->parent; + mlx5e_sw_node_delete(priv, node); + + node = parent; + WRITE_ONCE(node->qid, qid); + + /* Early return on error in force mode. Parent will still be an inner + * node to be deleted by a following delete operation. + */ + if (saved_err) + return saved_err; + + old_hw_id = node->hw_id; + node->hw_id = new_hw_id; + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + err = mlx5e_open_qos_sq(priv, &priv->channels, node); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ."); + qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x), err = %d\n", + classid, err); + } else { + mlx5e_activate_qos_sq(priv, node); + } + } + + err = mlx5_qos_destroy_node(priv->mdev, old_hw_id); + if (err) /* Not fatal. */ + qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n", + node->hw_id, classid, err); + + return 0; +} + +static int mlx5e_qos_update_children(struct mlx5e_priv *priv, struct mlx5e_qos_node *node, + struct netlink_ext_ack *extack) +{ + struct mlx5e_qos_node *child; + int err = 0; + int bkt; + + hash_for_each(priv->htb.qos_tc2node, bkt, child, hnode) { + u32 old_bw_share = child->bw_share; + int err_one; + + if (child->parent != node) + continue; + + mlx5e_htb_convert_rate(priv, child->rate, node, &child->bw_share); + if (child->bw_share == old_bw_share) + continue; + + err_one = mlx5_qos_update_node(priv->mdev, child->hw_id, child->bw_share, + child->max_average_bw, child->hw_id); + if (!err && err_one) { + err = err_one; + + NL_SET_ERR_MSG_MOD(extack, "Firmware error when modifying a child node."); + qos_err(priv->mdev, "Failed to modify a child node (class %04x), err = %d\n", + node->classid, err); + } + } + + return err; +} + +int mlx5e_htb_node_modify(struct mlx5e_priv *priv, u16 classid, u64 rate, u64 ceil, + struct netlink_ext_ack *extack) +{ + u32 bw_share, max_average_bw; + struct mlx5e_qos_node *node; + bool ceil_changed = false; + int err; + + qos_dbg(priv->mdev, "TC_HTB_LEAF_MODIFY classid %04x, rate %llu, ceil %llu\n", + classid, rate, ceil); + + node = mlx5e_sw_node_find(priv, classid); + if (!node) + return -ENOENT; + + node->rate = rate; + mlx5e_htb_convert_rate(priv, rate, node->parent, &bw_share); + mlx5e_htb_convert_ceil(priv, ceil, &max_average_bw); + + err = mlx5_qos_update_node(priv->mdev, node->parent->hw_id, bw_share, + max_average_bw, node->hw_id); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Firmware error when modifying a node."); + qos_err(priv->mdev, "Failed to modify a node (class %04x), err = %d\n", + classid, err); + return err; + } + + if (max_average_bw != node->max_average_bw) + ceil_changed = true; + + node->bw_share = bw_share; + node->max_average_bw = max_average_bw; + + if (ceil_changed) + err = mlx5e_qos_update_children(priv, node, extack); + + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h new file mode 100644 index 000000000000..5af7991fcd19 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */ + +#ifndef __MLX5E_EN_QOS_H +#define __MLX5E_EN_QOS_H + +#include <linux/mlx5/driver.h> + +#define MLX5E_QOS_MAX_LEAF_NODES 256 + +struct mlx5e_priv; +struct mlx5e_channels; +struct mlx5e_channel; + +int mlx5e_qos_max_leaf_nodes(struct mlx5_core_dev *mdev); +int mlx5e_qos_cur_leaf_nodes(struct mlx5e_priv *priv); + +/* TX datapath API */ +int mlx5e_get_txq_by_classid(struct mlx5e_priv *priv, u16 classid); +struct mlx5e_txqsq *mlx5e_get_sq(struct mlx5e_priv *priv, int qid); + +/* SQ lifecycle */ +int mlx5e_qos_open_queues(struct mlx5e_priv *priv, struct mlx5e_channels *chs); +void mlx5e_qos_activate_queues(struct mlx5e_priv *priv); +void mlx5e_qos_deactivate_queues(struct mlx5e_channel *c); +void mlx5e_qos_close_queues(struct mlx5e_channel *c); + +/* HTB API */ +int mlx5e_htb_root_add(struct mlx5e_priv *priv, u16 htb_maj_id, u16 htb_defcls, + struct netlink_ext_ack *extack); +int mlx5e_htb_root_del(struct mlx5e_priv *priv); +int mlx5e_htb_leaf_alloc_queue(struct mlx5e_priv *priv, u16 classid, + u32 parent_classid, u64 rate, u64 ceil, + struct netlink_ext_ack *extack); +int mlx5e_htb_leaf_to_inner(struct mlx5e_priv *priv, u16 classid, u16 child_classid, + u64 rate, u64 ceil, struct netlink_ext_ack *extack); +int mlx5e_htb_leaf_del(struct mlx5e_priv *priv, u16 classid, u16 *old_qid, + u16 *new_qid, struct netlink_ext_ack *extack); +int mlx5e_htb_leaf_del_last(struct mlx5e_priv *priv, u16 classid, bool force, + struct netlink_ext_ack *extack); +int mlx5e_htb_node_modify(struct mlx5e_priv *priv, u16 classid, u64 rate, u64 ceil, + struct netlink_ext_ack *extack); + +#endif 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 6bc6b48a56dc..a359c3c73106 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c @@ -711,9 +711,7 @@ mlx5_tc_ct_entry_add_rule(struct mlx5_tc_ct_priv *ct_priv, attr->flags |= MLX5_ESW_ATTR_FLAG_NO_IN_PORT; mlx5_tc_ct_set_tuple_match(netdev_priv(ct_priv->netdev), spec, flow_rule); - mlx5e_tc_match_to_reg_match(spec, ZONE_TO_REG, - entry->tuple.zone & MLX5_CT_ZONE_MASK, - MLX5_CT_ZONE_MASK); + mlx5e_tc_match_to_reg_match(spec, ZONE_TO_REG, entry->tuple.zone, MLX5_CT_ZONE_MASK); zone_rule->rule = mlx5_tc_rule_insert(priv, spec, attr); if (IS_ERR(zone_rule->rule)) { @@ -1247,9 +1245,8 @@ static int tc_ct_pre_ct_add_rules(struct mlx5_ct_ft *ct_ft, pre_ct->flow_rule = rule; /* add miss rule */ - memset(spec, 0, sizeof(*spec)); dest.ft = nat ? ct_priv->ct_nat : ct_priv->ct; - rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); + rule = mlx5_add_flow_rules(ft, NULL, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); ct_dbg("Failed to add pre ct miss rule zone %d", zone); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_mplsoudp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_mplsoudp.c index 1f9526244222..3479672e84cf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_mplsoudp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_mplsoudp.c @@ -81,8 +81,8 @@ static int parse_tunnel(struct mlx5e_priv *priv, if (!enc_keyid.mask->keyid) return 0; - if (!(MLX5_CAP_GEN(priv->mdev, flex_parser_protocols) & - MLX5_FLEX_PROTO_CW_MPLS_UDP)) + if (!MLX5_CAP_ETH(priv->mdev, tunnel_stateless_mpls_over_udp) && + !(MLX5_CAP_GEN(priv->mdev, flex_parser_protocols) & MLX5_FLEX_PROTO_CW_MPLS_UDP)) return -EOPNOTSUPP; flow_rule_match_mpls(rule, &match); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c new file mode 100644 index 000000000000..37fc1d77ded7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 Mellanox Technologies */ + +#include <net/page_pool.h> +#include "en/txrx.h" +#include "en/params.h" +#include "en/trap.h" + +static int mlx5e_trap_napi_poll(struct napi_struct *napi, int budget) +{ + struct mlx5e_trap *trap_ctx = container_of(napi, struct mlx5e_trap, napi); + struct mlx5e_ch_stats *ch_stats = trap_ctx->stats; + struct mlx5e_rq *rq = &trap_ctx->rq; + bool busy = false; + int work_done = 0; + + ch_stats->poll++; + + work_done = mlx5e_poll_rx_cq(&rq->cq, budget); + busy |= work_done == budget; + busy |= rq->post_wqes(rq); + + if (busy) + return budget; + + if (unlikely(!napi_complete_done(napi, work_done))) + return work_done; + + mlx5e_cq_arm(&rq->cq); + return work_done; +} + +static int mlx5e_alloc_trap_rq(struct mlx5e_priv *priv, struct mlx5e_rq_param *rqp, + struct mlx5e_rq_stats *stats, struct mlx5e_params *params, + struct mlx5e_ch_stats *ch_stats, + struct mlx5e_rq *rq) +{ + void *rqc_wq = MLX5_ADDR_OF(rqc, rqp->rqc, wq); + struct mlx5_core_dev *mdev = priv->mdev; + struct page_pool_params pp_params = {}; + int node = dev_to_node(mdev->device); + u32 pool_size; + int wq_sz; + int err; + int i; + + rqp->wq.db_numa_node = node; + + rq->wq_type = params->rq_wq_type; + rq->pdev = mdev->device; + rq->netdev = priv->netdev; + rq->mdev = mdev; + rq->priv = priv; + rq->stats = stats; + rq->clock = &mdev->clock; + rq->tstamp = &priv->tstamp; + rq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); + + xdp_rxq_info_unused(&rq->xdp_rxq); + + rq->buff.map_dir = DMA_FROM_DEVICE; + rq->buff.headroom = mlx5e_get_rq_headroom(mdev, params, NULL); + pool_size = 1 << params->log_rq_mtu_frames; + + err = mlx5_wq_cyc_create(mdev, &rqp->wq, rqc_wq, &rq->wqe.wq, &rq->wq_ctrl); + if (err) + return err; + + rq->wqe.wq.db = &rq->wqe.wq.db[MLX5_RCV_DBR]; + + wq_sz = mlx5_wq_cyc_get_size(&rq->wqe.wq); + + rq->wqe.info = rqp->frags_info; + rq->buff.frame0_sz = rq->wqe.info.arr[0].frag_stride; + rq->wqe.frags = kvzalloc_node(array_size(sizeof(*rq->wqe.frags), + (wq_sz << rq->wqe.info.log_num_frags)), + GFP_KERNEL, node); + if (!rq->wqe.frags) { + err = -ENOMEM; + goto err_wq_cyc_destroy; + } + + err = mlx5e_init_di_list(rq, wq_sz, node); + if (err) + goto err_free_frags; + + rq->mkey_be = cpu_to_be32(priv->mdev->mlx5e_res.mkey.key); + + mlx5e_rq_set_trap_handlers(rq, params); + + /* Create a page_pool and register it with rxq */ + pp_params.order = 0; + pp_params.flags = 0; /* No-internal DMA mapping in page_pool */ + pp_params.pool_size = pool_size; + pp_params.nid = node; + pp_params.dev = mdev->device; + pp_params.dma_dir = rq->buff.map_dir; + + /* page_pool can be used even when there is no rq->xdp_prog, + * given page_pool does not handle DMA mapping there is no + * required state to clear. And page_pool gracefully handle + * elevated refcnt. + */ + rq->page_pool = page_pool_create(&pp_params); + if (IS_ERR(rq->page_pool)) { + err = PTR_ERR(rq->page_pool); + rq->page_pool = NULL; + goto err_free_di_list; + } + for (i = 0; i < wq_sz; i++) { + struct mlx5e_rx_wqe_cyc *wqe = + mlx5_wq_cyc_get_wqe(&rq->wqe.wq, i); + int f; + + for (f = 0; f < rq->wqe.info.num_frags; f++) { + u32 frag_size = rq->wqe.info.arr[f].frag_size | + MLX5_HW_START_PADDING; + + wqe->data[f].byte_count = cpu_to_be32(frag_size); + wqe->data[f].lkey = rq->mkey_be; + } + /* check if num_frags is not a pow of two */ + if (rq->wqe.info.num_frags < (1 << rq->wqe.info.log_num_frags)) { + wqe->data[f].byte_count = 0; + wqe->data[f].lkey = cpu_to_be32(MLX5_INVALID_LKEY); + wqe->data[f].addr = 0; + } + } + return 0; + +err_free_di_list: + mlx5e_free_di_list(rq); +err_free_frags: + kvfree(rq->wqe.frags); +err_wq_cyc_destroy: + mlx5_wq_destroy(&rq->wq_ctrl); + + return err; +} + +static void mlx5e_free_trap_rq(struct mlx5e_rq *rq) +{ + page_pool_destroy(rq->page_pool); + mlx5e_free_di_list(rq); + kvfree(rq->wqe.frags); + mlx5_wq_destroy(&rq->wq_ctrl); +} + +static int mlx5e_open_trap_rq(struct mlx5e_priv *priv, struct napi_struct *napi, + struct mlx5e_rq_stats *stats, struct mlx5e_params *params, + struct mlx5e_rq_param *rq_param, + struct mlx5e_ch_stats *ch_stats, + struct mlx5e_rq *rq) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5e_create_cq_param ccp = {}; + struct dim_cq_moder trap_moder = {}; + struct mlx5e_cq *cq = &rq->cq; + int err; + + ccp.node = dev_to_node(mdev->device); + ccp.ch_stats = ch_stats; + ccp.napi = napi; + ccp.ix = 0; + err = mlx5e_open_cq(priv, trap_moder, &rq_param->cqp, &ccp, cq); + if (err) + return err; + + err = mlx5e_alloc_trap_rq(priv, rq_param, stats, params, ch_stats, rq); + if (err) + goto err_destroy_cq; + + err = mlx5e_create_rq(rq, rq_param); + if (err) + goto err_free_rq; + + err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY); + if (err) + goto err_destroy_rq; + + return 0; + +err_destroy_rq: + mlx5e_destroy_rq(rq); + mlx5e_free_rx_descs(rq); +err_free_rq: + mlx5e_free_trap_rq(rq); +err_destroy_cq: + mlx5e_close_cq(cq); + + return err; +} + +static void mlx5e_close_trap_rq(struct mlx5e_rq *rq) +{ + mlx5e_destroy_rq(rq); + mlx5e_free_rx_descs(rq); + mlx5e_free_trap_rq(rq); + mlx5e_close_cq(&rq->cq); +} + +static int mlx5e_create_trap_direct_rq_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir, + u32 rqn) +{ + void *tirc; + int inlen; + u32 *in; + int err; + + inlen = MLX5_ST_SZ_BYTES(create_tir_in); + in = kvzalloc(inlen, GFP_KERNEL); + if (!in) + return -ENOMEM; + + tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); + MLX5_SET(tirc, tirc, transport_domain, mdev->mlx5e_res.td.tdn); + MLX5_SET(tirc, tirc, rx_hash_fn, MLX5_RX_HASH_FN_NONE); + MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_DIRECT); + MLX5_SET(tirc, tirc, inline_rqn, rqn); + err = mlx5e_create_tir(mdev, tir, in); + kvfree(in); + + return err; +} + +static void mlx5e_destroy_trap_direct_rq_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir) +{ + mlx5e_destroy_tir(mdev, tir); +} + +static void mlx5e_activate_trap_rq(struct mlx5e_rq *rq) +{ + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); +} + +static void mlx5e_deactivate_trap_rq(struct mlx5e_rq *rq) +{ + clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); +} + +static void mlx5e_build_trap_params(struct mlx5e_priv *priv, struct mlx5e_trap *t) +{ + struct mlx5e_params *params = &t->params; + + params->rq_wq_type = MLX5_WQ_TYPE_CYCLIC; + mlx5e_init_rq_type_params(priv->mdev, params); + params->sw_mtu = priv->netdev->max_mtu; + mlx5e_build_rq_param(priv, params, NULL, &t->rq_param); +} + +static struct mlx5e_trap *mlx5e_open_trap(struct mlx5e_priv *priv) +{ + int cpu = cpumask_first(mlx5_comp_irq_get_affinity_mask(priv->mdev, 0)); + struct net_device *netdev = priv->netdev; + struct mlx5e_trap *t; + int err; + + t = kvzalloc_node(sizeof(*t), GFP_KERNEL, cpu_to_node(cpu)); + if (!t) + return ERR_PTR(-ENOMEM); + + mlx5e_build_trap_params(priv, t); + + t->priv = priv; + t->mdev = priv->mdev; + t->tstamp = &priv->tstamp; + t->pdev = mlx5_core_dma_dev(priv->mdev); + t->netdev = priv->netdev; + t->mkey_be = cpu_to_be32(priv->mdev->mlx5e_res.mkey.key); + t->stats = &priv->trap_stats.ch; + + netif_napi_add(netdev, &t->napi, mlx5e_trap_napi_poll, 64); + + err = mlx5e_open_trap_rq(priv, &t->napi, + &priv->trap_stats.rq, + &t->params, &t->rq_param, + &priv->trap_stats.ch, + &t->rq); + if (unlikely(err)) + goto err_napi_del; + + err = mlx5e_create_trap_direct_rq_tir(t->mdev, &t->tir, t->rq.rqn); + if (err) + goto err_close_trap_rq; + + return t; + +err_close_trap_rq: + mlx5e_close_trap_rq(&t->rq); +err_napi_del: + netif_napi_del(&t->napi); + kvfree(t); + return ERR_PTR(err); +} + +void mlx5e_close_trap(struct mlx5e_trap *trap) +{ + mlx5e_destroy_trap_direct_rq_tir(trap->mdev, &trap->tir); + mlx5e_close_trap_rq(&trap->rq); + netif_napi_del(&trap->napi); + kvfree(trap); +} + +static void mlx5e_activate_trap(struct mlx5e_trap *trap) +{ + napi_enable(&trap->napi); + mlx5e_activate_trap_rq(&trap->rq); + napi_schedule(&trap->napi); +} + +void mlx5e_deactivate_trap(struct mlx5e_priv *priv) +{ + struct mlx5e_trap *trap = priv->en_trap; + + mlx5e_deactivate_trap_rq(&trap->rq); + napi_disable(&trap->napi); +} + +static struct mlx5e_trap *mlx5e_add_trap_queue(struct mlx5e_priv *priv) +{ + struct mlx5e_trap *trap; + + trap = mlx5e_open_trap(priv); + if (IS_ERR(trap)) + goto out; + + mlx5e_activate_trap(trap); +out: + return trap; +} + +static void mlx5e_del_trap_queue(struct mlx5e_priv *priv) +{ + mlx5e_deactivate_trap(priv); + mlx5e_close_trap(priv->en_trap); + priv->en_trap = NULL; +} + +static int mlx5e_trap_get_tirn(struct mlx5e_trap *en_trap) +{ + return en_trap->tir.tirn; +} + +static int mlx5e_handle_action_trap(struct mlx5e_priv *priv, int trap_id) +{ + bool open_queue = !priv->en_trap; + struct mlx5e_trap *trap; + int err; + + if (open_queue) { + trap = mlx5e_add_trap_queue(priv); + if (IS_ERR(trap)) + return PTR_ERR(trap); + priv->en_trap = trap; + } + + switch (trap_id) { + case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER: + err = mlx5e_add_vlan_trap(priv, trap_id, mlx5e_trap_get_tirn(priv->en_trap)); + if (err) + goto err_out; + break; + case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER: + err = mlx5e_add_mac_trap(priv, trap_id, mlx5e_trap_get_tirn(priv->en_trap)); + if (err) + goto err_out; + break; + default: + netdev_warn(priv->netdev, "%s: Unknown trap id %d\n", __func__, trap_id); + err = -EINVAL; + goto err_out; + } + return 0; + +err_out: + if (open_queue) + mlx5e_del_trap_queue(priv); + return err; +} + +static int mlx5e_handle_action_drop(struct mlx5e_priv *priv, int trap_id) +{ + switch (trap_id) { + case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER: + mlx5e_remove_vlan_trap(priv); + break; + case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER: + mlx5e_remove_mac_trap(priv); + break; + default: + netdev_warn(priv->netdev, "%s: Unknown trap id %d\n", __func__, trap_id); + return -EINVAL; + } + if (priv->en_trap && !mlx5_devlink_trap_get_num_active(priv->mdev)) + mlx5e_del_trap_queue(priv); + + return 0; +} + +int mlx5e_handle_trap_event(struct mlx5e_priv *priv, struct mlx5_trap_ctx *trap_ctx) +{ + int err = 0; + + /* Traps are unarmed when interface is down, no need to update + * them. The configuration is saved in the core driver, + * queried and applied upon interface up operation in + * mlx5e_open_locked(). + */ + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) + return 0; + + switch (trap_ctx->action) { + case DEVLINK_TRAP_ACTION_TRAP: + err = mlx5e_handle_action_trap(priv, trap_ctx->id); + break; + case DEVLINK_TRAP_ACTION_DROP: + err = mlx5e_handle_action_drop(priv, trap_ctx->id); + break; + default: + netdev_warn(priv->netdev, "%s: Unsupported action %d\n", __func__, + trap_ctx->action); + err = -EINVAL; + } + return err; +} + +static int mlx5e_apply_trap(struct mlx5e_priv *priv, int trap_id, bool enable) +{ + enum devlink_trap_action action; + int err; + + err = mlx5_devlink_traps_get_action(priv->mdev, trap_id, &action); + if (err) + return err; + if (action == DEVLINK_TRAP_ACTION_TRAP) + err = enable ? mlx5e_handle_action_trap(priv, trap_id) : + mlx5e_handle_action_drop(priv, trap_id); + return err; +} + +static const int mlx5e_traps_arr[] = { + DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER, + DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER, +}; + +int mlx5e_apply_traps(struct mlx5e_priv *priv, bool enable) +{ + int err; + int i; + + for (i = 0; i < ARRAY_SIZE(mlx5e_traps_arr); i++) { + err = mlx5e_apply_trap(priv, mlx5e_traps_arr[i], enable); + if (err) + return err; + } + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h new file mode 100644 index 000000000000..aa3f17658c6d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020, Mellanox Technologies */ + +#ifndef __MLX5E_TRAP_H__ +#define __MLX5E_TRAP_H__ + +#include "../en.h" +#include "../devlink.h" + +struct mlx5e_trap { + /* data path */ + struct mlx5e_rq rq; + struct mlx5e_tir tir; + struct napi_struct napi; + struct device *pdev; + struct net_device *netdev; + __be32 mkey_be; + + /* data path - accessed per napi poll */ + struct mlx5e_ch_stats *stats; + + /* control */ + struct mlx5e_priv *priv; + struct mlx5_core_dev *mdev; + struct hwtstamp_config *tstamp; + DECLARE_BITMAP(state, MLX5E_CHANNEL_NUM_STATES); + + struct mlx5e_params params; + struct mlx5e_rq_param rq_param; +}; + +void mlx5e_close_trap(struct mlx5e_trap *trap); +void mlx5e_deactivate_trap(struct mlx5e_priv *priv); +int mlx5e_handle_trap_event(struct mlx5e_priv *priv, struct mlx5_trap_ctx *trap_ctx); +int mlx5e_apply_traps(struct mlx5e_priv *priv, bool enable); + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h index 1fae7fab8297..6488098d2700 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h @@ -144,9 +144,9 @@ static inline bool mlx5e_accel_tx_is_ipsec_flow(struct mlx5e_accel_tx_state *sta { #ifdef CONFIG_MLX5_EN_IPSEC return mlx5e_ipsec_is_tx_flow(&state->ipsec); -#endif - +#else return false; +#endif } static inline unsigned int mlx5e_accel_tx_ids_len(struct mlx5e_txqsq *sq, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c index a9b45606dbdb..a97e8d205094 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c @@ -497,20 +497,6 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, } } -bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev, - netdev_features_t features) -{ - struct sec_path *sp = skb_sec_path(skb); - struct xfrm_state *x; - - if (sp && sp->len) { - x = sp->xvec[0]; - if (x && x->xso.offload_handle) - return true; - } - return false; -} - void mlx5e_ipsec_build_inverse_table(void) { u16 mss_inv; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h index 9df9b9a8e09b..3e80742a3caf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h @@ -57,8 +57,6 @@ struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb, u32 *cqe_bcnt); void mlx5e_ipsec_inverse_table_init(void); -bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev, - netdev_features_t features); void mlx5e_ipsec_set_iv_esn(struct sk_buff *skb, struct xfrm_state *x, struct xfrm_offload *xo); void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_state *x, @@ -87,8 +85,28 @@ static inline bool mlx5e_ipsec_is_tx_flow(struct mlx5e_accel_tx_ipsec_state *ips return ipsec_st->x; } +static inline bool mlx5e_ipsec_eseg_meta(struct mlx5_wqe_eth_seg *eseg) +{ + return eseg->flow_table_metadata & cpu_to_be32(MLX5_ETH_WQE_FT_META_IPSEC); +} + void mlx5e_ipsec_tx_build_eseg(struct mlx5e_priv *priv, struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg); + +static inline bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev, + netdev_features_t features) +{ + struct sec_path *sp = skb_sec_path(skb); + + if (sp && sp->len) { + struct xfrm_state *x = sp->xvec[0]; + + if (x && x->xso.offload_handle) + return true; + } + return false; +} + #else static inline void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, @@ -96,7 +114,14 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, struct mlx5_cqe64 *cqe) {} +static inline bool mlx5e_ipsec_eseg_meta(struct mlx5_wqe_eth_seg *eseg) +{ + return false; +} + static inline bool mlx5_ipsec_is_rx_flow(struct mlx5_cqe64 *cqe) { return false; } +static inline bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev, + netdev_features_t features) { return false; } #endif /* CONFIG_MLX5_EN_IPSEC */ #endif /* __MLX5E_IPSEC_RXTX_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 302001d6661e..5e9474dba4e5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -447,6 +447,17 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, goto out; } + /* Don't allow changing the number of channels if HTB offload is active, + * because the numeration of the QoS SQs will change, while per-queue + * qdiscs are attached. + */ + if (priv->htb.maj_id) { + err = -EINVAL; + netdev_err(priv->netdev, "%s: HTB offload is active, cannot change the number of channels\n", + __func__); + goto out; + } + new_channels.params = *cur_params; new_channels.params.num_channels = count; @@ -1972,6 +1983,16 @@ static int set_pflag_tx_port_ts(struct net_device *netdev, bool enable) if (!MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn)) return -EOPNOTSUPP; + /* Don't allow changing the PTP state if HTB offload is active, because + * the numeration of the QoS SQs will change, while per-queue qdiscs are + * attached. + */ + if (priv->htb.maj_id) { + netdev_err(priv->netdev, "%s: HTB offload is active, cannot change the PTP state\n", + __func__); + return -EINVAL; + } + new_channels.params = priv->channels.params; MLX5E_SET_PFLAG(&new_channels.params, MLX5E_PFLAG_TX_PORT_TS, enable); /* No need to verify SQ stop room as diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index e02e5895703d..16ce7756ac43 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -46,7 +46,6 @@ static void mlx5e_del_l2_flow_rule(struct mlx5e_priv *priv, enum { MLX5E_FULLMATCH = 0, MLX5E_ALLMULTI = 1, - MLX5E_PROMISC = 2, }; enum { @@ -306,6 +305,79 @@ static int mlx5e_add_any_vid_rules(struct mlx5e_priv *priv) return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_STAG_VID, 0); } +static struct mlx5_flow_handle * +mlx5e_add_trap_rule(struct mlx5_flow_table *ft, int trap_id, int tir_num) +{ + struct mlx5_flow_destination dest = {}; + MLX5_DECLARE_FLOW_ACT(flow_act); + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return ERR_PTR(-ENOMEM); + spec->flow_context.flags |= FLOW_CONTEXT_HAS_TAG; + spec->flow_context.flow_tag = trap_id; + dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; + dest.tir_num = tir_num; + + rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); + kvfree(spec); + return rule; +} + +int mlx5e_add_vlan_trap(struct mlx5e_priv *priv, int trap_id, int tir_num) +{ + struct mlx5_flow_table *ft = priv->fs.vlan.ft.t; + struct mlx5_flow_handle *rule; + int err; + + rule = mlx5e_add_trap_rule(ft, trap_id, tir_num); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + priv->fs.vlan.trap_rule = NULL; + netdev_err(priv->netdev, "%s: add VLAN trap rule failed, err %d\n", + __func__, err); + return err; + } + priv->fs.vlan.trap_rule = rule; + return 0; +} + +void mlx5e_remove_vlan_trap(struct mlx5e_priv *priv) +{ + if (priv->fs.vlan.trap_rule) { + mlx5_del_flow_rules(priv->fs.vlan.trap_rule); + priv->fs.vlan.trap_rule = NULL; + } +} + +int mlx5e_add_mac_trap(struct mlx5e_priv *priv, int trap_id, int tir_num) +{ + struct mlx5_flow_table *ft = priv->fs.l2.ft.t; + struct mlx5_flow_handle *rule; + int err; + + rule = mlx5e_add_trap_rule(ft, trap_id, tir_num); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + priv->fs.l2.trap_rule = NULL; + netdev_err(priv->netdev, "%s: add MAC trap rule failed, err %d\n", + __func__, err); + return err; + } + priv->fs.l2.trap_rule = rule; + return 0; +} + +void mlx5e_remove_mac_trap(struct mlx5e_priv *priv) +{ + if (priv->fs.l2.trap_rule) { + mlx5_del_flow_rules(priv->fs.l2.trap_rule); + priv->fs.l2.trap_rule = NULL; + } +} + void mlx5e_enable_cvlan_filter(struct mlx5e_priv *priv) { if (!priv->fs.vlan.cvlan_filter_disabled) @@ -419,6 +491,8 @@ static void mlx5e_del_vlan_rules(struct mlx5e_priv *priv) WARN_ON_ONCE(!(test_bit(MLX5E_STATE_DESTROYING, &priv->state))); + mlx5e_remove_vlan_trap(priv); + /* must be called after DESTROY bit is set and * set_rx_mode is called and flushed */ @@ -596,6 +670,83 @@ static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv) mlx5e_apply_netdev_addr(priv); } +#define MLX5E_PROMISC_GROUP0_SIZE BIT(0) +#define MLX5E_PROMISC_TABLE_SIZE MLX5E_PROMISC_GROUP0_SIZE + +static int mlx5e_add_promisc_rule(struct mlx5e_priv *priv) +{ + struct mlx5_flow_table *ft = priv->fs.promisc.ft.t; + struct mlx5_flow_destination dest = {}; + struct mlx5_flow_handle **rule_p; + MLX5_DECLARE_FLOW_ACT(flow_act); + struct mlx5_flow_spec *spec; + int err = 0; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fs.ttc.ft.t; + + rule_p = &priv->fs.promisc.rule; + *rule_p = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); + if (IS_ERR(*rule_p)) { + err = PTR_ERR(*rule_p); + *rule_p = NULL; + netdev_err(priv->netdev, "%s: add promiscuous rule failed\n", __func__); + } + kvfree(spec); + return err; +} + +static int mlx5e_create_promisc_table(struct mlx5e_priv *priv) +{ + struct mlx5e_flow_table *ft = &priv->fs.promisc.ft; + struct mlx5_flow_table_attr ft_attr = {}; + int err; + + ft_attr.max_fte = MLX5E_PROMISC_TABLE_SIZE; + ft_attr.autogroup.max_num_groups = 1; + ft_attr.level = MLX5E_PROMISC_FT_LEVEL; + ft_attr.prio = MLX5E_NIC_PRIO; + + ft->t = mlx5_create_auto_grouped_flow_table(priv->fs.ns, &ft_attr); + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + netdev_err(priv->netdev, "fail to create promisc table err=%d\n", err); + return err; + } + + err = mlx5e_add_promisc_rule(priv); + if (err) + goto err_destroy_promisc_table; + + return 0; + +err_destroy_promisc_table: + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; + + return err; +} + +static void mlx5e_del_promisc_rule(struct mlx5e_priv *priv) +{ + if (WARN(!priv->fs.promisc.rule, "Trying to remove non-existing promiscuous rule")) + return; + mlx5_del_flow_rules(priv->fs.promisc.rule); + priv->fs.promisc.rule = NULL; +} + +static void mlx5e_destroy_promisc_table(struct mlx5e_priv *priv) +{ + if (WARN(!priv->fs.promisc.ft.t, "Trying to remove non-existing promiscuous table")) + return; + mlx5e_del_promisc_rule(priv); + mlx5_destroy_flow_table(priv->fs.promisc.ft.t); + priv->fs.promisc.ft.t = NULL; +} + void mlx5e_set_rx_mode_work(struct work_struct *work) { struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv, @@ -615,14 +766,15 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) bool disable_allmulti = ea->allmulti_enabled && !allmulti_enabled; bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled; bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled; + int err; if (enable_promisc) { - if (!priv->channels.params.vlan_strip_disable) + err = mlx5e_create_promisc_table(priv); + if (err) + enable_promisc = false; + if (!priv->channels.params.vlan_strip_disable && !err) netdev_warn_once(ndev, "S-tagged traffic will be dropped while C-tag vlan stripping is enabled\n"); - mlx5e_add_l2_flow_rule(priv, &ea->promisc, MLX5E_PROMISC); - if (!priv->fs.vlan.cvlan_filter_disabled) - mlx5e_add_any_vid_rules(priv); } if (enable_allmulti) mlx5e_add_l2_flow_rule(priv, &ea->allmulti, MLX5E_ALLMULTI); @@ -635,11 +787,8 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) mlx5e_del_l2_flow_rule(priv, &ea->broadcast); if (disable_allmulti) mlx5e_del_l2_flow_rule(priv, &ea->allmulti); - if (disable_promisc) { - if (!priv->fs.vlan.cvlan_filter_disabled) - mlx5e_del_any_vid_rules(priv); - mlx5e_del_l2_flow_rule(priv, &ea->promisc); - } + if (disable_promisc) + mlx5e_destroy_promisc_table(priv); ea->promisc_enabled = promisc_enabled; ea->allmulti_enabled = allmulti_enabled; @@ -1306,9 +1455,6 @@ static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, mc_dmac[0] = 0x01; mv_dmac[0] = 0x01; break; - - case MLX5E_PROMISC: - break; } ai->rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); @@ -1325,12 +1471,12 @@ static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, } #define MLX5E_NUM_L2_GROUPS 3 -#define MLX5E_L2_GROUP1_SIZE BIT(0) -#define MLX5E_L2_GROUP2_SIZE BIT(15) -#define MLX5E_L2_GROUP3_SIZE BIT(0) +#define MLX5E_L2_GROUP1_SIZE BIT(15) +#define MLX5E_L2_GROUP2_SIZE BIT(0) +#define MLX5E_L2_GROUP_TRAP_SIZE BIT(0) /* must be last */ #define MLX5E_L2_TABLE_SIZE (MLX5E_L2_GROUP1_SIZE +\ MLX5E_L2_GROUP2_SIZE +\ - MLX5E_L2_GROUP3_SIZE) + MLX5E_L2_GROUP_TRAP_SIZE) static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); @@ -1353,7 +1499,9 @@ static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table) mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); mc_dmac = MLX5_ADDR_OF(fte_match_param, mc, outer_headers.dmac_47_16); - /* Flow Group for promiscuous */ + /* Flow Group for full match */ + eth_broadcast_addr(mc_dmac); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); MLX5_SET_CFG(in, start_flow_index, ix); ix += MLX5E_L2_GROUP1_SIZE; MLX5_SET_CFG(in, end_flow_index, ix - 1); @@ -1362,9 +1510,9 @@ static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table) goto err_destroy_groups; ft->num_groups++; - /* Flow Group for full match */ - eth_broadcast_addr(mc_dmac); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + /* Flow Group for allmulti */ + eth_zero_addr(mc_dmac); + mc_dmac[0] = 0x01; MLX5_SET_CFG(in, start_flow_index, ix); ix += MLX5E_L2_GROUP2_SIZE; MLX5_SET_CFG(in, end_flow_index, ix - 1); @@ -1373,11 +1521,10 @@ static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table) goto err_destroy_groups; ft->num_groups++; - /* Flow Group for allmulti */ - eth_zero_addr(mc_dmac); - mc_dmac[0] = 0x01; + /* Flow Group for l2 traps */ + memset(in, 0, inlen); MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_L2_GROUP3_SIZE; + ix += MLX5E_L2_GROUP_TRAP_SIZE; MLX5_SET_CFG(in, end_flow_index, ix - 1); ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); if (IS_ERR(ft->g[ft->num_groups])) @@ -1435,15 +1582,17 @@ err_destroy_flow_table: return err; } -#define MLX5E_NUM_VLAN_GROUPS 4 +#define MLX5E_NUM_VLAN_GROUPS 5 #define MLX5E_VLAN_GROUP0_SIZE BIT(12) #define MLX5E_VLAN_GROUP1_SIZE BIT(12) #define MLX5E_VLAN_GROUP2_SIZE BIT(1) #define MLX5E_VLAN_GROUP3_SIZE BIT(0) +#define MLX5E_VLAN_GROUP_TRAP_SIZE BIT(0) /* must be last */ #define MLX5E_VLAN_TABLE_SIZE (MLX5E_VLAN_GROUP0_SIZE +\ MLX5E_VLAN_GROUP1_SIZE +\ MLX5E_VLAN_GROUP2_SIZE +\ - MLX5E_VLAN_GROUP3_SIZE) + MLX5E_VLAN_GROUP3_SIZE +\ + MLX5E_VLAN_GROUP_TRAP_SIZE) static int __mlx5e_create_vlan_table_groups(struct mlx5e_flow_table *ft, u32 *in, int inlen) @@ -1498,6 +1647,15 @@ static int __mlx5e_create_vlan_table_groups(struct mlx5e_flow_table *ft, u32 *in goto err_destroy_groups; ft->num_groups++; + memset(in, 0, inlen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_VLAN_GROUP_TRAP_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + return 0; err_destroy_groups: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a9d824a9cb05..aad3887e3c1a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -65,6 +65,8 @@ #include "en/devlink.h" #include "lib/mlx5.h" #include "en/ptp.h" +#include "qos.h" +#include "en/trap.h" bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) { @@ -211,6 +213,33 @@ static void mlx5e_disable_async_events(struct mlx5e_priv *priv) mlx5_notifier_unregister(priv->mdev, &priv->events_nb); } +static int blocking_event(struct notifier_block *nb, unsigned long event, void *data) +{ + struct mlx5e_priv *priv = container_of(nb, struct mlx5e_priv, blocking_events_nb); + int err; + + switch (event) { + case MLX5_DRIVER_EVENT_TYPE_TRAP: + err = mlx5e_handle_trap_event(priv, data); + break; + default: + netdev_warn(priv->netdev, "Sync event: Unknouwn event %ld\n", event); + err = -EINVAL; + } + return err; +} + +static void mlx5e_enable_blocking_events(struct mlx5e_priv *priv) +{ + priv->blocking_events_nb.notifier_call = blocking_event; + mlx5_blocking_notifier_register(priv->mdev, &priv->blocking_events_nb); +} + +static void mlx5e_disable_blocking_events(struct mlx5e_priv *priv) +{ + mlx5_blocking_notifier_unregister(priv->mdev, &priv->blocking_events_nb); +} + static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq, struct mlx5e_icosq *sq, struct mlx5e_umr_wqe *wqe) @@ -342,13 +371,11 @@ static void mlx5e_init_frags_partition(struct mlx5e_rq *rq) prev->last_in_page = true; } -static int mlx5e_init_di_list(struct mlx5e_rq *rq, - int wq_sz, int cpu) +int mlx5e_init_di_list(struct mlx5e_rq *rq, int wq_sz, int node) { int len = wq_sz << rq->wqe.info.log_num_frags; - rq->wqe.di = kvzalloc_node(array_size(len, sizeof(*rq->wqe.di)), - GFP_KERNEL, cpu_to_node(cpu)); + rq->wqe.di = kvzalloc_node(array_size(len, sizeof(*rq->wqe.di)), GFP_KERNEL, node); if (!rq->wqe.di) return -ENOMEM; @@ -357,7 +384,7 @@ static int mlx5e_init_di_list(struct mlx5e_rq *rq, return 0; } -static void mlx5e_free_di_list(struct mlx5e_rq *rq) +void mlx5e_free_di_list(struct mlx5e_rq *rq) { kvfree(rq->wqe.di); } @@ -499,7 +526,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, goto err_rq_wq_destroy; } - err = mlx5e_init_di_list(rq, wq_sz, c->cpu); + err = mlx5e_init_di_list(rq, wq_sz, cpu_to_node(c->cpu)); if (err) goto err_rq_frags; @@ -650,8 +677,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) mlx5_wq_destroy(&rq->wq_ctrl); } -static int mlx5e_create_rq(struct mlx5e_rq *rq, - struct mlx5e_rq_param *param) +int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param) { struct mlx5_core_dev *mdev = rq->mdev; @@ -774,7 +800,7 @@ static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd) return err; } -static void mlx5e_destroy_rq(struct mlx5e_rq *rq) +void mlx5e_destroy_rq(struct mlx5e_rq *rq) { mlx5_core_destroy_rq(rq->mdev, rq->rqn); } @@ -1143,7 +1169,6 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, sq->uar_map = mdev->mlx5e_res.bfreg.map; sq->min_inline_mode = params->tx_min_inline_mode; sq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); - sq->stats = &c->priv->channel_stats[c->ix].sq[tc]; INIT_WORK(&sq->recover_work, mlx5e_tx_err_cqe_work); if (!MLX5_CAP_ETH(mdev, wqe_vlan_insert)) set_bit(MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE, &sq->state); @@ -1233,6 +1258,7 @@ static int mlx5e_create_sq(struct mlx5_core_dev *mdev, int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn, struct mlx5e_modify_sq_param *p) { + u64 bitmask = 0; void *in; void *sqc; int inlen; @@ -1248,9 +1274,14 @@ int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn, MLX5_SET(modify_sq_in, in, sq_state, p->curr_state); MLX5_SET(sqc, sqc, state, p->next_state); if (p->rl_update && p->next_state == MLX5_SQC_STATE_RDY) { - MLX5_SET64(modify_sq_in, in, modify_bitmask, 1); - MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, p->rl_index); + bitmask |= 1; + MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, p->rl_index); + } + if (p->qos_update && p->next_state == MLX5_SQC_STATE_RDY) { + bitmask |= 1 << 2; + MLX5_SET(sqc, sqc, qos_queue_group_id, p->qos_queue_group_id); } + MLX5_SET64(modify_sq_in, in, modify_bitmask, bitmask); err = mlx5_core_modify_sq(mdev, sqn, in); @@ -1267,6 +1298,7 @@ static void mlx5e_destroy_sq(struct mlx5_core_dev *mdev, u32 sqn) int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev, struct mlx5e_sq_param *param, struct mlx5e_create_sq_param *csp, + u16 qos_queue_group_id, u32 *sqn) { struct mlx5e_modify_sq_param msp = {0}; @@ -1278,6 +1310,10 @@ int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev, msp.curr_state = MLX5_SQC_STATE_RST; msp.next_state = MLX5_SQC_STATE_RDY; + if (qos_queue_group_id) { + msp.qos_update = true; + msp.qos_queue_group_id = qos_queue_group_id; + } err = mlx5e_modify_sq(mdev, *sqn, &msp); if (err) mlx5e_destroy_sq(mdev, *sqn); @@ -1288,13 +1324,9 @@ int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev, static int mlx5e_set_sq_maxrate(struct net_device *dev, struct mlx5e_txqsq *sq, u32 rate); -static int mlx5e_open_txqsq(struct mlx5e_channel *c, - u32 tisn, - int txq_ix, - struct mlx5e_params *params, - struct mlx5e_sq_param *param, - struct mlx5e_txqsq *sq, - int tc) +int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix, + struct mlx5e_params *params, struct mlx5e_sq_param *param, + struct mlx5e_txqsq *sq, int tc, u16 qos_queue_group_id, u16 qos_qid) { struct mlx5e_create_sq_param csp = {}; u32 tx_rate; @@ -1304,12 +1336,17 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c, if (err) return err; + if (qos_queue_group_id) + sq->stats = c->priv->htb.qos_sq_stats[qos_qid]; + else + sq->stats = &c->priv->channel_stats[c->ix].sq[tc]; + csp.tisn = tisn; csp.tis_lst_sz = 1; csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = sq->min_inline_mode; - err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn); + err = mlx5e_create_sq_rdy(c->mdev, param, &csp, qos_queue_group_id, &sq->sqn); if (err) goto err_free_txqsq; @@ -1366,7 +1403,7 @@ void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) } } -static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) +void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) { struct mlx5_core_dev *mdev = sq->mdev; struct mlx5_rate_limit rl = {0}; @@ -1403,7 +1440,7 @@ int mlx5e_open_icosq(struct mlx5e_channel *c, struct mlx5e_params *params, csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = params->tx_min_inline_mode; - err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn); + err = mlx5e_create_sq_rdy(c->mdev, param, &csp, 0, &sq->sqn); if (err) goto err_free_icosq; @@ -1452,7 +1489,7 @@ int mlx5e_open_xdpsq(struct mlx5e_channel *c, struct mlx5e_params *params, csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = sq->min_inline_mode; set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn); + err = mlx5e_create_sq_rdy(c->mdev, param, &csp, 0, &sq->sqn); if (err) goto err_free_xdpsq; @@ -1703,7 +1740,7 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c, int txq_ix = c->ix + tc * params->num_channels; err = mlx5e_open_txqsq(c, c->priv->tisn[c->lag_port][tc], txq_ix, - params, &cparam->txq_sq, &c->sq[tc], tc); + params, &cparam->txq_sq, &c->sq[tc], tc, 0, 0); if (err) goto err_close_sqs; } @@ -2044,6 +2081,8 @@ static void mlx5e_deactivate_channel(struct mlx5e_channel *c) mlx5e_deactivate_icosq(&c->icosq); for (tc = 0; tc < c->num_tc; tc++) mlx5e_deactivate_txqsq(&c->sq[tc]); + + mlx5e_qos_deactivate_queues(c); } static void mlx5e_close_channel(struct mlx5e_channel *c) @@ -2051,6 +2090,7 @@ static void mlx5e_close_channel(struct mlx5e_channel *c) if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)) mlx5e_close_xsk(c); mlx5e_close_queues(c); + mlx5e_qos_close_queues(c); netif_napi_del(&c->napi); kvfree(c); @@ -2068,10 +2108,8 @@ static void mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev, u32 buf_size = 0; int i; -#ifdef CONFIG_MLX5_EN_IPSEC if (MLX5_IPSEC_DEV(mdev)) byte_count += MLX5E_METADATA_ETHER_LEN; -#endif if (mlx5e_rx_is_linear_skb(params, xsk)) { int frag_stride; @@ -2200,9 +2238,8 @@ void mlx5e_build_sq_param_common(struct mlx5e_priv *priv, param->wq.buf_numa_node = dev_to_node(mlx5_core_dma_dev(priv->mdev)); } -static void mlx5e_build_sq_param(struct mlx5e_priv *priv, - struct mlx5e_params *params, - struct mlx5e_sq_param *param) +void mlx5e_build_sq_param(struct mlx5e_priv *priv, struct mlx5e_params *params, + struct mlx5e_sq_param *param) { void *sqc = param->sqc; void *wq = MLX5_ADDR_OF(sqc, sqc, wq); @@ -2381,10 +2418,18 @@ int mlx5e_open_channels(struct mlx5e_priv *priv, goto err_close_channels; } + err = mlx5e_qos_open_queues(priv, chs); + if (err) + goto err_close_ptp; + mlx5e_health_channels_update(priv); kvfree(cparam); return 0; +err_close_ptp: + if (chs->port_ptp) + mlx5e_port_ptp_close(chs->port_ptp); + err_close_channels: for (i--; i >= 0; i--) mlx5e_close_channel(chs->c[i]); @@ -2917,11 +2962,31 @@ static void mlx5e_netdev_set_tcs(struct net_device *netdev, u16 nch, u8 ntc) netdev_set_tc_queue(netdev, tc, nch, 0); } +int mlx5e_update_tx_netdev_queues(struct mlx5e_priv *priv) +{ + int qos_queues, nch, ntc, num_txqs, err; + + qos_queues = mlx5e_qos_cur_leaf_nodes(priv); + + nch = priv->channels.params.num_channels; + ntc = priv->channels.params.num_tc; + num_txqs = nch * ntc + qos_queues; + if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_TX_PORT_TS)) + num_txqs += ntc; + + mlx5e_dbg(DRV, priv, "Setting num_txqs %d\n", num_txqs); + err = netif_set_real_num_tx_queues(priv->netdev, num_txqs); + if (err) + netdev_warn(priv->netdev, "netif_set_real_num_tx_queues failed, %d\n", err); + + return err; +} + static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv) { struct net_device *netdev = priv->netdev; - int num_txqs, num_rxqs, nch, ntc; int old_num_txqs, old_ntc; + int num_rxqs, nch, ntc; int err; old_num_txqs = netdev->real_num_tx_queues; @@ -2929,18 +2994,13 @@ static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv) nch = priv->channels.params.num_channels; ntc = priv->channels.params.num_tc; - num_txqs = nch * ntc; - if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_TX_PORT_TS)) - num_txqs += ntc; num_rxqs = nch * priv->profile->rq_groups; mlx5e_netdev_set_tcs(netdev, nch, ntc); - err = netif_set_real_num_tx_queues(netdev, num_txqs); - if (err) { - netdev_warn(netdev, "netif_set_real_num_tx_queues failed, %d\n", err); + err = mlx5e_update_tx_netdev_queues(priv); + if (err) goto err_tcs; - } err = netif_set_real_num_rx_queues(netdev, num_rxqs); if (err) { netdev_warn(netdev, "netif_set_real_num_rx_queues failed, %d\n", err); @@ -3044,6 +3104,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) mlx5e_update_num_tc_x_num_ch(priv); mlx5e_build_txq_maps(priv); mlx5e_activate_channels(&priv->channels); + mlx5e_qos_activate_queues(priv); mlx5e_xdp_tx_enable(priv); netif_tx_start_all_queues(priv->netdev); @@ -3186,6 +3247,7 @@ int mlx5e_open_locked(struct net_device *netdev) priv->profile->update_rx(priv); mlx5e_activate_priv_channels(priv); + mlx5e_apply_traps(priv, true); if (priv->profile->update_carrier) priv->profile->update_carrier(priv); @@ -3221,6 +3283,7 @@ int mlx5e_close_locked(struct net_device *netdev) if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) return 0; + mlx5e_apply_traps(priv, false); clear_bit(MLX5E_STATE_OPENED, &priv->state); netif_carrier_off(priv->netdev); @@ -3610,6 +3673,14 @@ static int mlx5e_setup_tc_mqprio(struct mlx5e_priv *priv, mutex_lock(&priv->state_lock); + /* MQPRIO is another toplevel qdisc that can't be attached + * simultaneously with the offloaded HTB. + */ + if (WARN_ON(priv->htb.maj_id)) { + err = -EINVAL; + goto out; + } + new_channels.params = priv->channels.params; new_channels.params.num_tc = tc ? tc : 1; @@ -3637,12 +3708,55 @@ out: return err; } +static int mlx5e_setup_tc_htb(struct mlx5e_priv *priv, struct tc_htb_qopt_offload *htb) +{ + int res; + + switch (htb->command) { + case TC_HTB_CREATE: + return mlx5e_htb_root_add(priv, htb->parent_classid, htb->classid, + htb->extack); + case TC_HTB_DESTROY: + return mlx5e_htb_root_del(priv); + case TC_HTB_LEAF_ALLOC_QUEUE: + res = mlx5e_htb_leaf_alloc_queue(priv, htb->classid, htb->parent_classid, + htb->rate, htb->ceil, htb->extack); + if (res < 0) + return res; + htb->qid = res; + return 0; + case TC_HTB_LEAF_TO_INNER: + return mlx5e_htb_leaf_to_inner(priv, htb->parent_classid, htb->classid, + htb->rate, htb->ceil, htb->extack); + case TC_HTB_LEAF_DEL: + return mlx5e_htb_leaf_del(priv, htb->classid, &htb->moved_qid, &htb->qid, + htb->extack); + case TC_HTB_LEAF_DEL_LAST: + case TC_HTB_LEAF_DEL_LAST_FORCE: + return mlx5e_htb_leaf_del_last(priv, htb->classid, + htb->command == TC_HTB_LEAF_DEL_LAST_FORCE, + htb->extack); + case TC_HTB_NODE_MODIFY: + return mlx5e_htb_node_modify(priv, htb->classid, htb->rate, htb->ceil, + htb->extack); + case TC_HTB_LEAF_QUERY_QUEUE: + res = mlx5e_get_txq_by_classid(priv, htb->classid); + if (res < 0) + return res; + htb->qid = res; + return 0; + default: + return -EOPNOTSUPP; + } +} + static LIST_HEAD(mlx5e_block_cb_list); static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data) { struct mlx5e_priv *priv = netdev_priv(dev); + int err; switch (type) { case TC_SETUP_BLOCK: { @@ -3656,6 +3770,11 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, } case TC_SETUP_QDISC_MQPRIO: return mlx5e_setup_tc_mqprio(priv, type_data); + case TC_SETUP_QDISC_HTB: + mutex_lock(&priv->state_lock); + err = mlx5e_setup_tc_htb(priv, type_data); + mutex_unlock(&priv->state_lock); + return err; default: return -EOPNOTSUPP; } @@ -3825,20 +3944,25 @@ static int set_feature_cvlan_filter(struct net_device *netdev, bool enable) return 0; } -#if IS_ENABLED(CONFIG_MLX5_CLS_ACT) -static int set_feature_tc_num_filters(struct net_device *netdev, bool enable) +static int set_feature_hw_tc(struct net_device *netdev, bool enable) { struct mlx5e_priv *priv = netdev_priv(netdev); +#if IS_ENABLED(CONFIG_MLX5_CLS_ACT) if (!enable && mlx5e_tc_num_filters(priv, MLX5_TC_FLAG(NIC_OFFLOAD))) { netdev_err(netdev, "Active offloaded tc filters, can't turn hw_tc_offload off\n"); return -EINVAL; } +#endif + + if (!enable && priv->htb.maj_id) { + netdev_err(netdev, "Active HTB offload, can't turn hw_tc_offload off\n"); + return -EINVAL; + } return 0; } -#endif static int set_feature_rx_all(struct net_device *netdev, bool enable) { @@ -3936,9 +4060,7 @@ int mlx5e_set_features(struct net_device *netdev, netdev_features_t features) err |= MLX5E_HANDLE_FEATURE(NETIF_F_LRO, set_feature_lro); err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_FILTER, set_feature_cvlan_filter); -#if IS_ENABLED(CONFIG_MLX5_CLS_ACT) - err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_tc_num_filters); -#endif + err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_hw_tc); err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXALL, set_feature_rx_all); err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXFCS, set_feature_rx_fcs); err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan); @@ -4395,10 +4517,8 @@ netdev_features_t mlx5e_features_check(struct sk_buff *skb, features = vlan_features_check(skb, features); features = vxlan_features_check(skb, features); -#ifdef CONFIG_MLX5_EN_IPSEC if (mlx5e_ipsec_feature_check(skb, netdev, features)) return features; -#endif /* Validate if the tunneled packet is being offloaded by HW */ if (skb->encapsulation && @@ -4641,8 +4761,6 @@ const struct net_device_ops mlx5e_netdev_ops = { .ndo_change_mtu = mlx5e_change_nic_mtu, .ndo_do_ioctl = mlx5e_ioctl, .ndo_set_tx_maxrate = mlx5e_set_tx_maxrate, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = mlx5e_features_check, .ndo_tx_timeout = mlx5e_tx_timeout, .ndo_bpf = mlx5e_xdp, @@ -5053,6 +5171,8 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) netdev->hw_features |= NETIF_F_NTUPLE; #endif } + if (mlx5_qos_is_supported(mdev)) + netdev->features |= NETIF_F_HW_TC; netdev->features |= NETIF_F_HIGHDMA; netdev->features |= NETIF_F_HW_VLAN_STAG_FILTER; @@ -5270,6 +5390,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5_lag_add(mdev, netdev); mlx5e_enable_async_events(priv); + mlx5e_enable_blocking_events(priv); if (mlx5e_monitor_counter_supported(priv)) mlx5e_monitor_counter_init(priv); @@ -5307,6 +5428,12 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) if (mlx5e_monitor_counter_supported(priv)) mlx5e_monitor_counter_cleanup(priv); + mlx5e_disable_blocking_events(priv); + if (priv->en_trap) { + mlx5e_deactivate_trap(priv); + mlx5e_close_trap(priv->en_trap); + priv->en_trap = NULL; + } mlx5e_disable_async_events(priv); mlx5_lag_remove(mdev); mlx5_vxlan_reset_to_default(mdev->vxlan); @@ -5358,6 +5485,7 @@ int mlx5e_netdev_init(struct net_device *netdev, return -ENOMEM; mutex_init(&priv->state_lock); + hash_init(priv->htb.qos_tc2node); INIT_WORK(&priv->update_carrier_work, mlx5e_update_carrier_work); INIT_WORK(&priv->set_rx_mode_work, mlx5e_set_rx_mode_work); INIT_WORK(&priv->tx_timeout_work, mlx5e_tx_timeout_work); @@ -5380,8 +5508,14 @@ err_free_cpumask: void mlx5e_netdev_cleanup(struct net_device *netdev, struct mlx5e_priv *priv) { + int i; + destroy_workqueue(priv->wq); free_cpumask_var(priv->scratchpad.cpumask); + + for (i = 0; i < priv->htb.max_qos_sqs; i++) + kfree(priv->htb.qos_sq_stats[i]); + kvfree(priv->htb.qos_sq_stats); } struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev, @@ -5391,13 +5525,17 @@ struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev, { struct net_device *netdev; unsigned int ptp_txqs = 0; + int qos_sqs = 0; int err; if (MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn)) ptp_txqs = profile->max_tc; + if (mlx5_qos_is_supported(mdev)) + qos_sqs = mlx5e_qos_max_leaf_nodes(mdev); + netdev = alloc_etherdev_mqs(sizeof(struct mlx5e_priv), - nch * profile->max_tc + ptp_txqs, + nch * profile->max_tc + ptp_txqs + qos_sqs, nch * profile->rq_groups); if (!netdev) { mlx5_core_err(mdev, "alloc_etherdev_mqs() failed\n"); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index f0ceae65f6cf..629ae6ccb4cd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -653,8 +653,6 @@ static const struct net_device_ops mlx5e_netdev_ops_uplink_rep = { .ndo_has_offload_stats = mlx5e_rep_has_offload_stats, .ndo_get_offload_stats = mlx5e_rep_get_offload_stats, .ndo_change_mtu = mlx5e_uplink_rep_change_mtu, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = mlx5e_features_check, .ndo_set_vf_mac = mlx5e_set_vf_mac, .ndo_set_vf_rate = mlx5e_set_vf_rate, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 7f5851c61218..98b56f495b32 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -52,6 +52,7 @@ #include "en/xsk/rx.h" #include "en/health.h" #include "en/params.h" +#include "devlink.h" static struct sk_buff * mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, @@ -1126,12 +1127,8 @@ struct sk_buff *mlx5e_build_linear_skb(struct mlx5e_rq *rq, void *va, static void mlx5e_fill_xdp_buff(struct mlx5e_rq *rq, void *va, u16 headroom, u32 len, struct xdp_buff *xdp) { - xdp->data_hard_start = va; - xdp->data = va + headroom; - xdp_set_data_meta_invalid(xdp); - xdp->data_end = xdp->data + len; - xdp->rxq = &rq->xdp_rxq; - xdp->frame_sz = rq->buff.frame0_sz; + xdp_init_buff(xdp, rq->buff.frame0_sz, &rq->xdp_rxq); + xdp_prepare_buff(xdp, va, headroom, len, false); } static struct sk_buff * @@ -1786,12 +1783,10 @@ int mlx5e_rq_set_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params, bool rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe; rq->handle_rx_cqe = priv->profile->rx_handlers->handle_rx_cqe_mpwqe; -#ifdef CONFIG_MLX5_EN_IPSEC if (MLX5_IPSEC_DEV(mdev)) { netdev_err(netdev, "MPWQE RQ with IPSec offload not supported\n"); return -EINVAL; } -#endif if (!rq->handle_rx_cqe) { netdev_err(netdev, "RX handler of MPWQE RQ is not set\n"); return -EINVAL; @@ -1821,3 +1816,48 @@ int mlx5e_rq_set_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params, bool return 0; } + +static void mlx5e_trap_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) +{ + struct mlx5e_priv *priv = netdev_priv(rq->netdev); + struct mlx5_wq_cyc *wq = &rq->wqe.wq; + struct mlx5e_wqe_frag_info *wi; + struct sk_buff *skb; + u32 cqe_bcnt; + u16 trap_id; + u16 ci; + + trap_id = get_cqe_flow_tag(cqe); + ci = mlx5_wq_cyc_ctr2ix(wq, be16_to_cpu(cqe->wqe_counter)); + wi = get_frag(rq, ci); + cqe_bcnt = be32_to_cpu(cqe->byte_cnt); + + if (unlikely(MLX5E_RX_ERR_CQE(cqe))) { + rq->stats->wqe_err++; + goto free_wqe; + } + + skb = mlx5e_skb_from_cqe_nonlinear(rq, cqe, wi, cqe_bcnt); + if (!skb) + goto free_wqe; + + mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb); + skb_push(skb, ETH_HLEN); + + mlx5_devlink_trap_report(rq->mdev, trap_id, skb, &priv->dl_port); + dev_kfree_skb_any(skb); + +free_wqe: + mlx5e_free_rx_wqe(rq, wi, false); + mlx5_wq_cyc_pop(wq); +} + +void mlx5e_rq_set_trap_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params) +{ + rq->wqe.skb_from_cqe = mlx5e_rx_is_linear_skb(params, NULL) ? + mlx5e_skb_from_cqe_linear : + mlx5e_skb_from_cqe_nonlinear; + rq->post_wqes = mlx5e_post_rx_wqes; + rq->dealloc_wqe = mlx5e_dealloc_rx_wqe; + rq->handle_rx_cqe = mlx5e_trap_handle_rx_cqe; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 2cf2042b37c7..92c5b81427b9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -420,6 +420,25 @@ static void mlx5e_stats_grp_sw_update_stats_ptp(struct mlx5e_priv *priv, } } +static void mlx5e_stats_grp_sw_update_stats_qos(struct mlx5e_priv *priv, + struct mlx5e_sw_stats *s) +{ + struct mlx5e_sq_stats **stats; + u16 max_qos_sqs; + int i; + + /* Pairs with smp_store_release in mlx5e_open_qos_sq. */ + max_qos_sqs = smp_load_acquire(&priv->htb.max_qos_sqs); + stats = READ_ONCE(priv->htb.qos_sq_stats); + + for (i = 0; i < max_qos_sqs; i++) { + mlx5e_stats_grp_sw_update_stats_sq(s, READ_ONCE(stats[i])); + + /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92657 */ + barrier(); + } +} + static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) { struct mlx5e_sw_stats *s = &priv->stats.sw; @@ -449,6 +468,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) } } mlx5e_stats_grp_sw_update_stats_ptp(priv, s); + mlx5e_stats_grp_sw_update_stats_qos(priv, s); } static const struct counter_desc q_stats_desc[] = { @@ -1740,6 +1760,41 @@ static const struct counter_desc ptp_cq_stats_desc[] = { { MLX5E_DECLARE_PTP_CQ_STAT(struct mlx5e_ptp_cq_stats, abort_abs_diff_ns) }, }; +static const struct counter_desc qos_sq_stats_desc[] = { + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, packets) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, bytes) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_packets) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_bytes) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_inner_packets) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_inner_bytes) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, csum_partial) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, csum_partial_inner) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, added_vlan_packets) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, nop) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, mpwqe_blks) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, mpwqe_pkts) }, +#ifdef CONFIG_MLX5_EN_TLS + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_encrypted_packets) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_encrypted_bytes) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_ctx) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_ooo) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_dump_packets) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_dump_bytes) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_resync_bytes) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_skip_no_sync_data) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_drop_no_sync_data) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_drop_bypass_req) }, +#endif + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, csum_none) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, stopped) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, dropped) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, xmit_more) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, recover) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, cqes) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, wake) }, + { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, cqe_err) }, +}; + #define NUM_RQ_STATS ARRAY_SIZE(rq_stats_desc) #define NUM_SQ_STATS ARRAY_SIZE(sq_stats_desc) #define NUM_XDPSQ_STATS ARRAY_SIZE(xdpsq_stats_desc) @@ -1750,6 +1805,49 @@ static const struct counter_desc ptp_cq_stats_desc[] = { #define NUM_PTP_SQ_STATS ARRAY_SIZE(ptp_sq_stats_desc) #define NUM_PTP_CH_STATS ARRAY_SIZE(ptp_ch_stats_desc) #define NUM_PTP_CQ_STATS ARRAY_SIZE(ptp_cq_stats_desc) +#define NUM_QOS_SQ_STATS ARRAY_SIZE(qos_sq_stats_desc) + +static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(qos) +{ + /* Pairs with smp_store_release in mlx5e_open_qos_sq. */ + return NUM_QOS_SQ_STATS * smp_load_acquire(&priv->htb.max_qos_sqs); +} + +static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(qos) +{ + /* Pairs with smp_store_release in mlx5e_open_qos_sq. */ + u16 max_qos_sqs = smp_load_acquire(&priv->htb.max_qos_sqs); + int i, qid; + + for (qid = 0; qid < max_qos_sqs; qid++) + for (i = 0; i < NUM_QOS_SQ_STATS; i++) + sprintf(data + (idx++) * ETH_GSTRING_LEN, + qos_sq_stats_desc[i].format, qid); + + return idx; +} + +static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(qos) +{ + struct mlx5e_sq_stats **stats; + u16 max_qos_sqs; + int i, qid; + + /* Pairs with smp_store_release in mlx5e_open_qos_sq. */ + max_qos_sqs = smp_load_acquire(&priv->htb.max_qos_sqs); + stats = READ_ONCE(priv->htb.qos_sq_stats); + + for (qid = 0; qid < max_qos_sqs; qid++) { + struct mlx5e_sq_stats *s = READ_ONCE(stats[qid]); + + for (i = 0; i < NUM_QOS_SQ_STATS; i++) + data[idx++] = MLX5E_READ_CTR64_CPU(s, qos_sq_stats_desc, i); + } + + return idx; +} + +static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(qos) { return; } static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ptp) { @@ -1932,6 +2030,7 @@ MLX5E_DEFINE_STATS_GRP(per_port_buff_congest, 0); MLX5E_DEFINE_STATS_GRP(eth_ext, 0); static MLX5E_DEFINE_STATS_GRP(tls, 0); static MLX5E_DEFINE_STATS_GRP(ptp, 0); +static MLX5E_DEFINE_STATS_GRP(qos, 0); /* The stats groups order is opposite to the update_stats() order calls */ mlx5e_stats_grp_t mlx5e_nic_stats_grps[] = { @@ -1955,6 +2054,7 @@ mlx5e_stats_grp_t mlx5e_nic_stats_grps[] = { &MLX5E_STATS_GRP(channels), &MLX5E_STATS_GRP(per_port_buff_congest), &MLX5E_STATS_GRP(ptp), + &MLX5E_STATS_GRP(qos), }; unsigned int mlx5e_nic_stats_grps_num(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index e41fc11f2ce7..93c41312fb03 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -55,6 +55,8 @@ #define MLX5E_DECLARE_PTP_CH_STAT(type, fld) "ptp_ch_"#fld, offsetof(type, fld) #define MLX5E_DECLARE_PTP_CQ_STAT(type, fld) "ptp_cq%d_"#fld, offsetof(type, fld) +#define MLX5E_DECLARE_QOS_TX_STAT(type, fld) "qos_tx%d_"#fld, offsetof(type, fld) + struct counter_desc { char format[ETH_GSTRING_LEN]; size_t offset; /* Byte offset */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index dd0bfbacad47..8fd38ad8113b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1325,12 +1325,6 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, int err = 0; int out_index; - if (!mlx5_chains_prios_supported(esw_chains(esw)) && attr->prio != 1) { - NL_SET_ERR_MSG_MOD(extack, - "E-switch priorities unsupported, upgrade FW"); - return -EOPNOTSUPP; - } - /* We check chain range only for tc flows. * For ft flows, we checked attr->chain was originally 0 and set it to * FDB_FT_CHAIN which is outside tc range. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 61ed671fe741..da6a358a8a10 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -106,28 +106,53 @@ return_txq: return priv->port_ptp_tc2realtxq[up]; } +static int mlx5e_select_htb_queue(struct mlx5e_priv *priv, struct sk_buff *skb, + u16 htb_maj_id) +{ + u16 classid; + + if ((TC_H_MAJ(skb->priority) >> 16) == htb_maj_id) + classid = TC_H_MIN(skb->priority); + else + classid = READ_ONCE(priv->htb.defcls); + + if (!classid) + return 0; + + return mlx5e_get_txq_by_classid(priv, classid); +} + u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, struct net_device *sb_dev) { struct mlx5e_priv *priv = netdev_priv(dev); + int num_tc_x_num_ch; int txq_ix; int up = 0; int ch_ix; - if (unlikely(priv->channels.port_ptp)) { - int num_tc_x_num_ch; + /* Sync with mlx5e_update_num_tc_x_num_ch - avoid refetching. */ + num_tc_x_num_ch = READ_ONCE(priv->num_tc_x_num_ch); + if (unlikely(dev->real_num_tx_queues > num_tc_x_num_ch)) { + /* Order maj_id before defcls - pairs with mlx5e_htb_root_add. */ + u16 htb_maj_id = smp_load_acquire(&priv->htb.maj_id); - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && - mlx5e_use_ptpsq(skb)) - return mlx5e_select_ptpsq(dev, skb); + if (unlikely(htb_maj_id)) { + txq_ix = mlx5e_select_htb_queue(priv, skb, htb_maj_id); + if (txq_ix > 0) + return txq_ix; + } - /* Sync with mlx5e_update_num_tc_x_num_ch - avoid refetching. */ - num_tc_x_num_ch = READ_ONCE(priv->num_tc_x_num_ch); + if (unlikely(priv->channels.port_ptp)) + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + mlx5e_use_ptpsq(skb)) + return mlx5e_select_ptpsq(dev, skb); txq_ix = netdev_pick_tx(dev, skb, NULL); - /* Fix netdev_pick_tx() not to choose ptp_channel txqs. + /* Fix netdev_pick_tx() not to choose ptp_channel and HTB txqs. * If they are selected, switch to regular queues. - * Driver to select these queues only at mlx5e_select_ptpsq(). + * Driver to select these queues only at mlx5e_select_ptpsq() + * and mlx5e_select_htb_queue(). */ if (unlikely(txq_ix >= num_tc_x_num_ch)) txq_ix %= num_tc_x_num_ch; @@ -241,9 +266,8 @@ mlx5e_txwqe_build_eseg_csum(struct mlx5e_txqsq *sq, struct sk_buff *skb, eseg->cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM; sq->stats->csum_partial++; #endif - } else if (unlikely(eseg->flow_table_metadata & cpu_to_be32(MLX5_ETH_WQE_FT_META_IPSEC))) { + } else if (unlikely(mlx5e_ipsec_eseg_meta(eseg))) { ipsec_txwqe_build_eseg_csum(sq, skb, eseg); - } else sq->stats->csum_none++; } @@ -703,6 +727,10 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) u16 pi; sq = priv->txq2sq[skb_get_queue_mapping(skb)]; + if (unlikely(!sq)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } /* May send SKBs and WQEs. */ if (unlikely(!mlx5e_accel_tx_begin(dev, sq, skb, &accel))) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index a3cfe06d5116..d54da3797c30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -115,17 +115,21 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) napi); struct mlx5e_ch_stats *ch_stats = c->stats; struct mlx5e_xdpsq *xsksq = &c->xsksq; + struct mlx5e_txqsq __rcu **qos_sqs; struct mlx5e_rq *xskrq = &c->xskrq; struct mlx5e_rq *rq = &c->rq; bool aff_change = false; bool busy_xsk = false; bool busy = false; int work_done = 0; + u16 qos_sqs_size; bool xsk_open; int i; rcu_read_lock(); + qos_sqs = rcu_dereference(c->qos_sqs); + xsk_open = test_bit(MLX5E_CHANNEL_STATE_XSK, c->state); ch_stats->poll++; @@ -133,6 +137,18 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) for (i = 0; i < c->num_tc; i++) busy |= mlx5e_poll_tx_cq(&c->sq[i].cq, budget); + if (unlikely(qos_sqs)) { + smp_rmb(); /* Pairs with mlx5e_qos_alloc_queues. */ + qos_sqs_size = READ_ONCE(c->qos_sqs_size); + + for (i = 0; i < qos_sqs_size; i++) { + struct mlx5e_txqsq *sq = rcu_dereference(qos_sqs[i]); + + if (sq) + busy |= mlx5e_poll_tx_cq(&sq->cq, budget); + } + } + busy |= mlx5e_poll_xdpsq_cq(&c->xdpsq.cq); if (c->xdp) @@ -186,6 +202,16 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) mlx5e_handle_tx_dim(&c->sq[i]); mlx5e_cq_arm(&c->sq[i].cq); } + if (unlikely(qos_sqs)) { + for (i = 0; i < qos_sqs_size; i++) { + struct mlx5e_txqsq *sq = rcu_dereference(qos_sqs[i]); + + if (sq) { + mlx5e_handle_tx_dim(sq); + mlx5e_cq_arm(&sq->cq); + } + } + } mlx5e_handle_rx_dim(rq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index fc0afa03d407..174dfbc996c6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -467,7 +467,7 @@ int mlx5_eq_table_init(struct mlx5_core_dev *dev) for (i = 0; i < MLX5_EVENT_TYPE_MAX; i++) ATOMIC_INIT_NOTIFIER_HEAD(&eq_table->nh[i]); - eq_table->irq_table = dev->priv.irq_table; + eq_table->irq_table = mlx5_irq_table_get(dev); return 0; } @@ -595,6 +595,9 @@ static void gather_async_events_mask(struct mlx5_core_dev *dev, u64 mask[4]) async_event_mask |= (1ull << MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED); + if (MLX5_CAP_GEN_MAX(dev, vhca_state)) + async_event_mask |= (1ull << MLX5_EVENT_TYPE_VHCA_STATE_CHANGE); + mask[0] = async_event_mask; if (MLX5_CAP_GEN(dev, event_cap)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_ofld.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_ofld.c index 4c74e2690d57..26b37a0f8762 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_ofld.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_ofld.c @@ -150,7 +150,7 @@ static void esw_acl_egress_ofld_groups_destroy(struct mlx5_vport *vport) static bool esw_acl_egress_needed(const struct mlx5_eswitch *esw, u16 vport_num) { - return mlx5_eswitch_is_vf_vport(esw, vport_num); + return mlx5_eswitch_is_vf_vport(esw, vport_num) || mlx5_esw_is_sf_vport(esw, vport_num); } int esw_acl_egress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c index ffff11baa3d0..cb1e181f4c6a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c @@ -122,3 +122,44 @@ struct devlink_port *mlx5_esw_offloads_devlink_port(struct mlx5_eswitch *esw, u1 vport = mlx5_eswitch_get_vport(esw, vport_num); return vport->dl_port; } + +int mlx5_esw_devlink_sf_port_register(struct mlx5_eswitch *esw, struct devlink_port *dl_port, + u16 vport_num, u32 sfnum) +{ + struct mlx5_core_dev *dev = esw->dev; + struct netdev_phys_item_id ppid = {}; + unsigned int dl_port_index; + struct mlx5_vport *vport; + struct devlink *devlink; + u16 pfnum; + int err; + + vport = mlx5_eswitch_get_vport(esw, vport_num); + if (IS_ERR(vport)) + return PTR_ERR(vport); + + pfnum = PCI_FUNC(dev->pdev->devfn); + mlx5_esw_get_port_parent_id(dev, &ppid); + memcpy(dl_port->attrs.switch_id.id, &ppid.id[0], ppid.id_len); + dl_port->attrs.switch_id.id_len = ppid.id_len; + devlink_port_attrs_pci_sf_set(dl_port, 0, pfnum, sfnum); + devlink = priv_to_devlink(dev); + dl_port_index = mlx5_esw_vport_to_devlink_port_index(dev, vport_num); + err = devlink_port_register(devlink, dl_port, dl_port_index); + if (err) + return err; + + vport->dl_port = dl_port; + return 0; +} + +void mlx5_esw_devlink_sf_port_unregister(struct mlx5_eswitch *esw, u16 vport_num) +{ + struct mlx5_vport *vport; + + vport = mlx5_eswitch_get_vport(esw, vport_num); + if (IS_ERR(vport)) + return; + devlink_port_unregister(vport->dl_port); + vport->dl_port = NULL; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index da901e364656..820305b1664e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1042,8 +1042,7 @@ static int esw_vport_enable_qos(struct mlx5_eswitch *esw, void *vport_elem; int err = 0; - if (!esw->qos.enabled || !MLX5_CAP_GEN(dev, qos) || - !MLX5_CAP_QOS(dev, esw_scheduling)) + if (!esw->qos.enabled) return 0; if (vport->qos.enabled) @@ -1273,8 +1272,8 @@ static void esw_vport_cleanup(struct mlx5_eswitch *esw, struct mlx5_vport *vport esw_vport_cleanup_acl(esw, vport); } -static int esw_enable_vport(struct mlx5_eswitch *esw, u16 vport_num, - enum mlx5_eswitch_vport_event enabled_events) +int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, u16 vport_num, + enum mlx5_eswitch_vport_event enabled_events) { struct mlx5_vport *vport; int ret; @@ -1310,7 +1309,7 @@ done: return ret; } -static void esw_disable_vport(struct mlx5_eswitch *esw, u16 vport_num) +void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, u16 vport_num) { struct mlx5_vport *vport; @@ -1366,9 +1365,15 @@ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) { int outlen = MLX5_ST_SZ_BYTES(query_esw_functions_out); u32 in[MLX5_ST_SZ_DW(query_esw_functions_in)] = {}; + u16 max_sf_vports; u32 *out; int err; + max_sf_vports = mlx5_sf_max_functions(dev); + /* Device interface is array of 64-bits */ + if (max_sf_vports) + outlen += DIV_ROUND_UP(max_sf_vports, BITS_PER_TYPE(__be64)) * sizeof(__be64); + out = kvzalloc(outlen, GFP_KERNEL); if (!out) return ERR_PTR(-ENOMEM); @@ -1376,7 +1381,7 @@ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) MLX5_SET(query_esw_functions_in, in, opcode, MLX5_CMD_OP_QUERY_ESW_FUNCTIONS); - err = mlx5_cmd_exec_inout(dev, query_esw_functions, in, out); + err = mlx5_cmd_exec(dev, in, sizeof(in), out, outlen); if (!err) return out; @@ -1426,7 +1431,7 @@ int mlx5_eswitch_load_vport(struct mlx5_eswitch *esw, u16 vport_num, { int err; - err = esw_enable_vport(esw, vport_num, enabled_events); + err = mlx5_esw_vport_enable(esw, vport_num, enabled_events); if (err) return err; @@ -1437,14 +1442,14 @@ int mlx5_eswitch_load_vport(struct mlx5_eswitch *esw, u16 vport_num, return err; err_rep: - esw_disable_vport(esw, vport_num); + mlx5_esw_vport_disable(esw, vport_num); return err; } void mlx5_eswitch_unload_vport(struct mlx5_eswitch *esw, u16 vport_num) { esw_offloads_unload_rep(esw, vport_num); - esw_disable_vport(esw, vport_num); + mlx5_esw_vport_disable(esw, vport_num); } void mlx5_eswitch_unload_vf_vports(struct mlx5_eswitch *esw, u16 num_vfs) @@ -1594,6 +1599,15 @@ mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, int num_vfs) kvfree(out); } +static void mlx5_esw_mode_change_notify(struct mlx5_eswitch *esw, u16 mode) +{ + struct mlx5_esw_event_info info = {}; + + info.new_mode = mode; + + blocking_notifier_call_chain(&esw->n_head, 0, &info); +} + /** * mlx5_eswitch_enable_locked - Enable eswitch * @esw: Pointer to eswitch @@ -1654,6 +1668,8 @@ int mlx5_eswitch_enable_locked(struct mlx5_eswitch *esw, int mode, int num_vfs) mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", esw->esw_funcs.num_vfs, esw->enabled_vports); + mlx5_esw_mode_change_notify(esw, mode); + return 0; abort: @@ -1710,6 +1726,11 @@ void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw, bool clear_vf) esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", esw->esw_funcs.num_vfs, esw->enabled_vports); + /* Notify eswitch users that it is exiting from current mode. + * So that it can do necessary cleanup before the eswitch is disabled. + */ + mlx5_esw_mode_change_notify(esw, MLX5_ESWITCH_NONE); + mlx5_eswitch_event_handlers_unregister(esw); if (esw->mode == MLX5_ESWITCH_LEGACY) @@ -1810,6 +1831,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE; dev->priv.eswitch = esw; + BLOCKING_INIT_NOTIFIER_HEAD(&esw->n_head); return 0; abort: if (esw->work_queue) @@ -1899,7 +1921,8 @@ 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); + mlx5_eswitch_is_vf_vport(esw, vport_num) || + mlx5_esw_is_sf_vport(esw, vport_num); } int mlx5_devlink_port_function_hw_addr_get(struct devlink *devlink, @@ -2500,4 +2523,12 @@ bool mlx5_esw_multipath_prereq(struct mlx5_core_dev *dev0, dev1->priv.eswitch->mode == MLX5_ESWITCH_OFFLOADS); } +int mlx5_esw_event_notifier_register(struct mlx5_eswitch *esw, struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&esw->n_head, nb); +} +void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&esw->n_head, nb); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index cf87de94418f..479d2ac2cd85 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -43,6 +43,7 @@ #include <linux/mlx5/fs.h> #include "lib/mpfs.h" #include "lib/fs_chains.h" +#include "sf/sf.h" #include "en/tc_ct.h" #ifdef CONFIG_MLX5_ESWITCH @@ -277,6 +278,7 @@ struct mlx5_eswitch { struct { u32 large_group_num; } params; + struct blocking_notifier_head n_head; }; void esw_offloads_disable(struct mlx5_eswitch *esw); @@ -499,6 +501,40 @@ static inline u16 mlx5_eswitch_first_host_vport_num(struct mlx5_core_dev *dev) MLX5_VPORT_PF : MLX5_VPORT_FIRST_VF; } +static inline int mlx5_esw_sf_start_idx(const struct mlx5_eswitch *esw) +{ + /* PF and VF vports indices start from 0 to max_vfs */ + return MLX5_VPORT_PF_PLACEHOLDER + mlx5_core_max_vfs(esw->dev); +} + +static inline int mlx5_esw_sf_end_idx(const struct mlx5_eswitch *esw) +{ + return mlx5_esw_sf_start_idx(esw) + mlx5_sf_max_functions(esw->dev); +} + +static inline int +mlx5_esw_sf_vport_num_to_index(const struct mlx5_eswitch *esw, u16 vport_num) +{ + return vport_num - mlx5_sf_start_function_id(esw->dev) + + MLX5_VPORT_PF_PLACEHOLDER + mlx5_core_max_vfs(esw->dev); +} + +static inline u16 +mlx5_esw_sf_vport_index_to_num(const struct mlx5_eswitch *esw, int idx) +{ + return mlx5_sf_start_function_id(esw->dev) + idx - + (MLX5_VPORT_PF_PLACEHOLDER + mlx5_core_max_vfs(esw->dev)); +} + +static inline bool +mlx5_esw_is_sf_vport(const struct mlx5_eswitch *esw, u16 vport_num) +{ + return mlx5_sf_supported(esw->dev) && + vport_num >= mlx5_sf_start_function_id(esw->dev) && + (vport_num < (mlx5_sf_start_function_id(esw->dev) + + mlx5_sf_max_functions(esw->dev))); +} + static inline bool mlx5_eswitch_is_funcs_handler(const struct mlx5_core_dev *dev) { return mlx5_core_is_ecpf_esw_manager(dev); @@ -527,6 +563,10 @@ static inline int mlx5_eswitch_vport_num_to_index(struct mlx5_eswitch *esw, if (vport_num == MLX5_VPORT_UPLINK) return mlx5_eswitch_uplink_idx(esw); + if (mlx5_esw_is_sf_vport(esw, vport_num)) + return mlx5_esw_sf_vport_num_to_index(esw, vport_num); + + /* PF and VF vports start from 0 to max_vfs */ return vport_num; } @@ -540,6 +580,12 @@ static inline u16 mlx5_eswitch_index_to_vport_num(struct mlx5_eswitch *esw, if (index == mlx5_eswitch_uplink_idx(esw)) return MLX5_VPORT_UPLINK; + /* SF vports indices are after VFs and before ECPF */ + if (mlx5_sf_supported(esw->dev) && + index > mlx5_core_max_vfs(esw->dev)) + return mlx5_esw_sf_vport_index_to_num(esw, index); + + /* PF and VF vports start from 0 to max_vfs */ return index; } @@ -625,6 +671,11 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw); for ((vport) = (nvfs); \ (vport) >= (esw)->first_host_vport; (vport)--) +#define mlx5_esw_for_each_sf_rep(esw, i, rep) \ + for ((i) = mlx5_esw_sf_start_idx(esw); \ + (rep) = &(esw)->offloads.vport_reps[(i)], \ + (i) < mlx5_esw_sf_end_idx(esw); (i++)) + 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); @@ -638,6 +689,10 @@ mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, enum mlx5_eswitch_vport_event enabled_events); void mlx5_eswitch_disable_pf_vf_vports(struct mlx5_eswitch *esw); +int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, u16 vport_num, + enum mlx5_eswitch_vport_event enabled_events); +void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, u16 vport_num); + int esw_vport_create_offloads_acl_tables(struct mlx5_eswitch *esw, struct mlx5_vport *vport); @@ -656,6 +711,9 @@ esw_get_max_restore_tag(struct mlx5_eswitch *esw); int esw_offloads_load_rep(struct mlx5_eswitch *esw, u16 vport_num); void esw_offloads_unload_rep(struct mlx5_eswitch *esw, u16 vport_num); +int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num); +void mlx5_esw_offloads_rep_unload(struct mlx5_eswitch *esw, u16 vport_num); + int mlx5_eswitch_load_vport(struct mlx5_eswitch *esw, u16 vport_num, enum mlx5_eswitch_vport_event enabled_events); void mlx5_eswitch_unload_vport(struct mlx5_eswitch *esw, u16 vport_num); @@ -667,6 +725,26 @@ void mlx5_eswitch_unload_vf_vports(struct mlx5_eswitch *esw, u16 num_vfs); int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, u16 vport_num); void mlx5_esw_offloads_devlink_port_unregister(struct mlx5_eswitch *esw, u16 vport_num); struct devlink_port *mlx5_esw_offloads_devlink_port(struct mlx5_eswitch *esw, u16 vport_num); + +int mlx5_esw_devlink_sf_port_register(struct mlx5_eswitch *esw, struct devlink_port *dl_port, + u16 vport_num, u32 sfnum); +void mlx5_esw_devlink_sf_port_unregister(struct mlx5_eswitch *esw, u16 vport_num); + +int mlx5_esw_offloads_sf_vport_enable(struct mlx5_eswitch *esw, struct devlink_port *dl_port, + u16 vport_num, u32 sfnum); +void mlx5_esw_offloads_sf_vport_disable(struct mlx5_eswitch *esw, u16 vport_num); + +/** + * mlx5_esw_event_info - Indicates eswitch mode changed/changing. + * + * @new_mode: New mode of eswitch. + */ +struct mlx5_esw_event_info { + u16 new_mode; +}; + +int mlx5_esw_event_notifier_register(struct mlx5_eswitch *esw, struct notifier_block *n); +void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifier_block *n); #else /* CONFIG_MLX5_ESWITCH */ /* eswitch API stubs */ static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 2f6a0ae20650..7f09f2bbf7c1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -1800,11 +1800,22 @@ static void __esw_offloads_unload_rep(struct mlx5_eswitch *esw, esw->offloads.rep_ops[rep_type]->unload(rep); } +static void __unload_reps_sf_vport(struct mlx5_eswitch *esw, u8 rep_type) +{ + struct mlx5_eswitch_rep *rep; + int i; + + mlx5_esw_for_each_sf_rep(esw, i, rep) + __esw_offloads_unload_rep(esw, rep, rep_type); +} + static void __unload_reps_all_vport(struct mlx5_eswitch *esw, u8 rep_type) { struct mlx5_eswitch_rep *rep; int i; + __unload_reps_sf_vport(esw, rep_type); + mlx5_esw_for_each_vf_rep_reverse(esw, i, rep, esw->esw_funcs.num_vfs) __esw_offloads_unload_rep(esw, rep, rep_type); @@ -1822,7 +1833,7 @@ static void __unload_reps_all_vport(struct mlx5_eswitch *esw, u8 rep_type) __esw_offloads_unload_rep(esw, rep, rep_type); } -static int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num) +int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num) { struct mlx5_eswitch_rep *rep; int rep_type; @@ -1846,7 +1857,7 @@ err_reps: return err; } -static void mlx5_esw_offloads_rep_unload(struct mlx5_eswitch *esw, u16 vport_num) +void mlx5_esw_offloads_rep_unload(struct mlx5_eswitch *esw, u16 vport_num) { struct mlx5_eswitch_rep *rep; int rep_type; @@ -2824,3 +2835,35 @@ u32 mlx5_eswitch_get_vport_metadata_for_match(struct mlx5_eswitch *esw, return vport->metadata << (32 - ESW_SOURCE_PORT_METADATA_BITS); } EXPORT_SYMBOL(mlx5_eswitch_get_vport_metadata_for_match); + +int mlx5_esw_offloads_sf_vport_enable(struct mlx5_eswitch *esw, struct devlink_port *dl_port, + u16 vport_num, u32 sfnum) +{ + int err; + + err = mlx5_esw_vport_enable(esw, vport_num, MLX5_VPORT_UC_ADDR_CHANGE); + if (err) + return err; + + err = mlx5_esw_devlink_sf_port_register(esw, dl_port, vport_num, sfnum); + if (err) + goto devlink_err; + + err = mlx5_esw_offloads_rep_load(esw, vport_num); + if (err) + goto rep_err; + return 0; + +rep_err: + mlx5_esw_devlink_sf_port_unregister(esw, vport_num); +devlink_err: + mlx5_esw_vport_disable(esw, vport_num); + return err; +} + +void mlx5_esw_offloads_sf_vport_disable(struct mlx5_eswitch *esw, u16 vport_num) +{ + mlx5_esw_offloads_rep_unload(esw, vport_num); + mlx5_esw_devlink_sf_port_unregister(esw, vport_num); + mlx5_esw_vport_disable(esw, vport_num); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/events.c b/drivers/net/ethernet/mellanox/mlx5/core/events.c index 3ce17c3d7a00..d713ae24d6b6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/events.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/events.c @@ -23,7 +23,7 @@ static int temp_warn(struct notifier_block *, unsigned long, void *); static int port_module(struct notifier_block *, unsigned long, void *); static int pcie_core(struct notifier_block *, unsigned long, void *); -/* handler which forwards the event to events->nh, driver notifiers */ +/* handler which forwards the event to events->fw_nh, driver notifiers */ static int forward_event(struct notifier_block *, unsigned long, void *); static struct mlx5_nb events_nbs_ref[] = { @@ -55,12 +55,14 @@ struct mlx5_events { struct mlx5_core_dev *dev; struct workqueue_struct *wq; struct mlx5_event_nb notifiers[ARRAY_SIZE(events_nbs_ref)]; - /* driver notifier chain */ - struct atomic_notifier_head nh; + /* driver notifier chain for fw events */ + struct atomic_notifier_head fw_nh; /* port module events stats */ struct mlx5_pme_stats pme_stats; /*pcie_core*/ struct work_struct pcie_core_work; + /* driver notifier chain for sw events */ + struct blocking_notifier_head sw_nh; }; static const char *eqe_type_str(u8 type) @@ -110,6 +112,8 @@ static const char *eqe_type_str(u8 type) return "MLX5_EVENT_TYPE_CMD"; case MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED: return "MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED"; + case MLX5_EVENT_TYPE_VHCA_STATE_CHANGE: + return "MLX5_EVENT_TYPE_VHCA_STATE_CHANGE"; case MLX5_EVENT_TYPE_PAGE_REQUEST: return "MLX5_EVENT_TYPE_PAGE_REQUEST"; case MLX5_EVENT_TYPE_PAGE_FAULT: @@ -331,7 +335,7 @@ static int forward_event(struct notifier_block *nb, unsigned long event, void *d mlx5_core_dbg(events->dev, "Async eqe type %s, subtype (%d) forward to interfaces\n", eqe_type_str(eqe->type), eqe->sub_type); - atomic_notifier_call_chain(&events->nh, event, data); + atomic_notifier_call_chain(&events->fw_nh, event, data); return NOTIFY_OK; } @@ -342,7 +346,7 @@ int mlx5_events_init(struct mlx5_core_dev *dev) if (!events) return -ENOMEM; - ATOMIC_INIT_NOTIFIER_HEAD(&events->nh); + ATOMIC_INIT_NOTIFIER_HEAD(&events->fw_nh); events->dev = dev; dev->priv.events = events; events->wq = create_singlethread_workqueue("mlx5_events"); @@ -351,6 +355,7 @@ int mlx5_events_init(struct mlx5_core_dev *dev) return -ENOMEM; } INIT_WORK(&events->pcie_core_work, mlx5_pcie_event); + BLOCKING_INIT_NOTIFIER_HEAD(&events->sw_nh); return 0; } @@ -383,11 +388,14 @@ void mlx5_events_stop(struct mlx5_core_dev *dev) flush_workqueue(events->wq); } +/* This API is used only for processing and forwarding firmware + * events to mlx5 consumer. + */ int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb) { struct mlx5_events *events = dev->priv.events; - return atomic_notifier_chain_register(&events->nh, nb); + return atomic_notifier_chain_register(&events->fw_nh, nb); } EXPORT_SYMBOL(mlx5_notifier_register); @@ -395,11 +403,41 @@ int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *n { struct mlx5_events *events = dev->priv.events; - return atomic_notifier_chain_unregister(&events->nh, nb); + return atomic_notifier_chain_unregister(&events->fw_nh, nb); } EXPORT_SYMBOL(mlx5_notifier_unregister); int mlx5_notifier_call_chain(struct mlx5_events *events, unsigned int event, void *data) { - return atomic_notifier_call_chain(&events->nh, event, data); + return atomic_notifier_call_chain(&events->fw_nh, event, data); +} + +/* This API is used only for processing and forwarding driver-specific + * events to mlx5 consumers. + */ +int mlx5_blocking_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb) +{ + struct mlx5_events *events = dev->priv.events; + + return blocking_notifier_chain_register(&events->sw_nh, nb); +} + +int mlx5_blocking_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb) +{ + struct mlx5_events *events = dev->priv.events; + + return blocking_notifier_chain_unregister(&events->sw_nh, nb); +} + +int mlx5_blocking_notifier_call_chain(struct mlx5_core_dev *dev, unsigned int event, + void *data) +{ + struct mlx5_events *events = dev->priv.events; + + return blocking_notifier_call_chain(&events->sw_nh, event, data); +} + +void mlx5_events_work_enqueue(struct mlx5_core_dev *dev, struct work_struct *work) +{ + queue_work(dev->priv.events->wq, work); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 0fcee702b808..07b4dbd61920 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -105,8 +105,8 @@ #define ETHTOOL_PRIO_NUM_LEVELS 1 #define ETHTOOL_NUM_PRIOS 11 #define ETHTOOL_MIN_LEVEL (KERNEL_MIN_LEVEL + ETHTOOL_NUM_PRIOS) -/* Vlan, mac, ttc, inner ttc, {aRFS/accel and esp/esp_err} */ -#define KERNEL_NIC_PRIO_NUM_LEVELS 6 +/* Promiscuous, Vlan, mac, ttc, inner ttc, {aRFS/accel and esp/esp_err} */ +#define KERNEL_NIC_PRIO_NUM_LEVELS 7 #define KERNEL_NIC_NUM_PRIOS 1 /* One more level for tc */ #define KERNEL_MIN_LEVEL (KERNEL_NIC_PRIO_NUM_LEVELS + 1) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c index 947f346bdc2d..381325b4a863 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c @@ -141,9 +141,6 @@ u32 mlx5_chains_get_nf_ft_chain(struct mlx5_fs_chains *chains) u32 mlx5_chains_get_prio_range(struct mlx5_fs_chains *chains) { - if (!mlx5_chains_prios_supported(chains)) - return 1; - if (mlx5_chains_ignore_flow_level_supported(chains)) return UINT_MAX; @@ -541,13 +538,13 @@ mlx5_chains_create_prio(struct mlx5_fs_chains *chains, u32 chain, u32 prio, u32 level) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); - struct mlx5_flow_handle *miss_rule = NULL; + struct mlx5_flow_handle *miss_rule; struct mlx5_flow_group *miss_group; struct mlx5_flow_table *next_ft; struct mlx5_flow_table *ft; - struct prio *prio_s = NULL; struct fs_chain *chain_s; struct list_head *pos; + struct prio *prio_s; u32 *flow_group_in; int err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index ca6f2fc39ea0..e4c9627485aa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -73,6 +73,9 @@ #include "ecpf.h" #include "lib/hv_vhca.h" #include "diag/rsc_dump.h" +#include "sf/vhca_event.h" +#include "sf/dev/dev.h" +#include "sf/sf.h" MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>"); MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) core driver"); @@ -82,7 +85,6 @@ unsigned int mlx5_core_debug_mask; module_param_named(debug_mask, mlx5_core_debug_mask, uint, 0644); MODULE_PARM_DESC(debug_mask, "debug mask: 1 = dump cmd data, 2 = dump cmd exec time, 3 = both. Default=0"); -#define MLX5_DEFAULT_PROF 2 static unsigned int prof_sel = MLX5_DEFAULT_PROF; module_param_named(prof_sel, prof_sel, uint, 0444); MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2"); @@ -567,6 +569,8 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) if (MLX5_CAP_GEN_MAX(dev, mkey_by_name)) MLX5_SET(cmd_hca_cap, set_hca_cap, mkey_by_name, 1); + mlx5_vhca_state_cap_handle(dev, set_hca_cap); + return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE); } @@ -884,6 +888,24 @@ static int mlx5_init_once(struct mlx5_core_dev *dev) goto err_eswitch_cleanup; } + err = mlx5_vhca_event_init(dev); + if (err) { + mlx5_core_err(dev, "Failed to init vhca event notifier %d\n", err); + goto err_fpga_cleanup; + } + + err = mlx5_sf_hw_table_init(dev); + if (err) { + mlx5_core_err(dev, "Failed to init SF HW table %d\n", err); + goto err_sf_hw_table_cleanup; + } + + err = mlx5_sf_table_init(dev); + if (err) { + mlx5_core_err(dev, "Failed to init SF table %d\n", err); + goto err_sf_table_cleanup; + } + dev->dm = mlx5_dm_create(dev); if (IS_ERR(dev->dm)) mlx5_core_warn(dev, "Failed to init device memory%d\n", err); @@ -894,6 +916,12 @@ static int mlx5_init_once(struct mlx5_core_dev *dev) return 0; +err_sf_table_cleanup: + mlx5_sf_hw_table_cleanup(dev); +err_sf_hw_table_cleanup: + mlx5_vhca_event_cleanup(dev); +err_fpga_cleanup: + mlx5_fpga_cleanup(dev); err_eswitch_cleanup: mlx5_eswitch_cleanup(dev->priv.eswitch); err_sriov_cleanup: @@ -925,6 +953,9 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev) mlx5_hv_vhca_destroy(dev->hv_vhca); mlx5_fw_tracer_destroy(dev->tracer); mlx5_dm_cleanup(dev); + mlx5_sf_table_cleanup(dev); + mlx5_sf_hw_table_cleanup(dev); + mlx5_vhca_event_cleanup(dev); mlx5_fpga_cleanup(dev); mlx5_eswitch_cleanup(dev->priv.eswitch); mlx5_sriov_cleanup(dev); @@ -1129,6 +1160,14 @@ static int mlx5_load(struct mlx5_core_dev *dev) goto err_sriov; } + mlx5_vhca_event_start(dev); + + err = mlx5_sf_hw_table_create(dev); + if (err) { + mlx5_core_err(dev, "sf table create failed %d\n", err); + goto err_vhca; + } + err = mlx5_ec_init(dev); if (err) { mlx5_core_err(dev, "Failed to init embedded CPU\n"); @@ -1141,11 +1180,16 @@ static int mlx5_load(struct mlx5_core_dev *dev) goto err_sriov; } + mlx5_sf_dev_table_create(dev); + return 0; err_sriov: mlx5_ec_cleanup(dev); err_ec: + mlx5_sf_hw_table_destroy(dev); +err_vhca: + mlx5_vhca_event_stop(dev); mlx5_cleanup_fs(dev); err_fs: mlx5_accel_tls_cleanup(dev); @@ -1171,8 +1215,11 @@ err_irq_table: static void mlx5_unload(struct mlx5_core_dev *dev) { + mlx5_sf_dev_table_destroy(dev); mlx5_sriov_detach(dev); mlx5_ec_cleanup(dev); + mlx5_sf_hw_table_destroy(dev); + mlx5_vhca_event_stop(dev); mlx5_cleanup_fs(dev); mlx5_accel_ipsec_cleanup(dev); mlx5_accel_tls_cleanup(dev); @@ -1283,7 +1330,7 @@ out: mutex_unlock(&dev->intf_state_mutex); } -static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) +int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) { struct mlx5_priv *priv = &dev->priv; int err; @@ -1305,6 +1352,8 @@ static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) priv->dbg_root = debugfs_create_dir(dev_name(dev->device), mlx5_debugfs_root); + INIT_LIST_HEAD(&priv->traps); + err = mlx5_health_init(dev); if (err) goto err_health_init; @@ -1333,7 +1382,7 @@ err_health_init: return err; } -static void mlx5_mdev_uninit(struct mlx5_core_dev *dev) +void mlx5_mdev_uninit(struct mlx5_core_dev *dev) { struct mlx5_priv *priv = &dev->priv; @@ -1676,6 +1725,10 @@ static int __init init(void) if (err) goto err_debug; + err = mlx5_sf_driver_register(); + if (err) + goto err_sf; + #ifdef CONFIG_MLX5_CORE_EN err = mlx5e_init(); if (err) { @@ -1686,6 +1739,8 @@ static int __init init(void) return 0; +err_sf: + pci_unregister_driver(&mlx5_core_driver); err_debug: mlx5_unregister_debugfs(); return err; @@ -1696,6 +1751,7 @@ static void __exit cleanup(void) #ifdef CONFIG_MLX5_CORE_EN mlx5e_cleanup(); #endif + mlx5_sf_driver_unregister(); pci_unregister_driver(&mlx5_core_driver); mlx5_unregister_debugfs(); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 0a0302ce7144..3754ef98554f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -117,6 +117,8 @@ enum mlx5_semaphore_space_address { MLX5_SEMAPHORE_SW_RESET = 0x20, }; +#define MLX5_DEFAULT_PROF 2 + int mlx5_query_hca_caps(struct mlx5_core_dev *dev); int mlx5_query_board_id(struct mlx5_core_dev *dev); int mlx5_cmd_init(struct mlx5_core_dev *dev); @@ -176,6 +178,7 @@ struct cpumask * mlx5_irq_get_affinity_mask(struct mlx5_irq_table *irq_table, int vecidx); struct cpu_rmap *mlx5_irq_get_rmap(struct mlx5_irq_table *table); int mlx5_irq_get_num_comp(struct mlx5_irq_table *table); +struct mlx5_irq_table *mlx5_irq_table_get(struct mlx5_core_dev *dev); int mlx5_events_init(struct mlx5_core_dev *dev); void mlx5_events_cleanup(struct mlx5_core_dev *dev); @@ -257,6 +260,15 @@ enum { u8 mlx5_get_nic_state(struct mlx5_core_dev *dev); void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state); +static inline bool mlx5_core_is_sf(const struct mlx5_core_dev *dev) +{ + return dev->coredev_type == MLX5_COREDEV_SF; +} + +int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx); +void mlx5_mdev_uninit(struct mlx5_core_dev *dev); void mlx5_unload_one(struct mlx5_core_dev *dev, bool cleanup); int mlx5_load_one(struct mlx5_core_dev *dev, bool boot); + +void mlx5_events_work_enqueue(struct mlx5_core_dev *dev, struct work_struct *work); #endif /* __MLX5_CORE_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c index 6fd974920394..a61e09aff152 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c @@ -30,6 +30,9 @@ int mlx5_irq_table_init(struct mlx5_core_dev *dev) { struct mlx5_irq_table *irq_table; + if (mlx5_core_is_sf(dev)) + return 0; + irq_table = kvzalloc(sizeof(*irq_table), GFP_KERNEL); if (!irq_table) return -ENOMEM; @@ -40,6 +43,9 @@ int mlx5_irq_table_init(struct mlx5_core_dev *dev) void mlx5_irq_table_cleanup(struct mlx5_core_dev *dev) { + if (mlx5_core_is_sf(dev)) + return; + kvfree(dev->priv.irq_table); } @@ -268,6 +274,9 @@ int mlx5_irq_table_create(struct mlx5_core_dev *dev) int nvec; int err; + if (mlx5_core_is_sf(dev)) + return 0; + nvec = MLX5_CAP_GEN(dev, num_ports) * num_online_cpus() + MLX5_IRQ_VEC_COMP_BASE; nvec = min_t(int, nvec, num_eqs); @@ -319,6 +328,9 @@ void mlx5_irq_table_destroy(struct mlx5_core_dev *dev) struct mlx5_irq_table *table = dev->priv.irq_table; int i; + if (mlx5_core_is_sf(dev)) + return; + /* free_irq requires that affinity and rmap will be cleared * before calling it. This is why there is asymmetry with set_rmap * which should be called after alloc_irq but before request_irq. @@ -332,3 +344,11 @@ void mlx5_irq_table_destroy(struct mlx5_core_dev *dev) kfree(table->irq); } +struct mlx5_irq_table *mlx5_irq_table_get(struct mlx5_core_dev *dev) +{ +#ifdef CONFIG_MLX5_SF + if (mlx5_core_is_sf(dev)) + return dev->priv.parent_mdev->priv.irq_table; +#endif + return dev->priv.irq_table; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/qos.c new file mode 100644 index 000000000000..0777be24a307 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/qos.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */ + +#include "qos.h" + +#define MLX5_QOS_DEFAULT_DWRR_UID 0 + +bool mlx5_qos_is_supported(struct mlx5_core_dev *mdev) +{ + if (!MLX5_CAP_GEN(mdev, qos)) + return false; + if (!MLX5_CAP_QOS(mdev, nic_sq_scheduling)) + return false; + if (!MLX5_CAP_QOS(mdev, nic_bw_share)) + return false; + if (!MLX5_CAP_QOS(mdev, nic_rate_limit)) + return false; + return true; +} + +int mlx5_qos_max_leaf_nodes(struct mlx5_core_dev *mdev) +{ + return 1 << MLX5_CAP_QOS(mdev, log_max_qos_nic_queue_group); +} + +int mlx5_qos_create_leaf_node(struct mlx5_core_dev *mdev, u32 parent_id, + u32 bw_share, u32 max_avg_bw, u32 *id) +{ + u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0}; + + MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_id); + MLX5_SET(scheduling_context, sched_ctx, element_type, + SCHEDULING_CONTEXT_ELEMENT_TYPE_QUEUE_GROUP); + MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share); + MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_avg_bw); + + return mlx5_create_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC, + sched_ctx, id); +} + +int mlx5_qos_create_inner_node(struct mlx5_core_dev *mdev, u32 parent_id, + u32 bw_share, u32 max_avg_bw, u32 *id) +{ + u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0}; + void *attr; + + MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_id); + MLX5_SET(scheduling_context, sched_ctx, element_type, + SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR); + MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share); + MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_avg_bw); + + attr = MLX5_ADDR_OF(scheduling_context, sched_ctx, element_attributes); + MLX5_SET(tsar_element, attr, tsar_type, TSAR_ELEMENT_TSAR_TYPE_DWRR); + + return mlx5_create_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC, + sched_ctx, id); +} + +int mlx5_qos_create_root_node(struct mlx5_core_dev *mdev, u32 *id) +{ + return mlx5_qos_create_inner_node(mdev, MLX5_QOS_DEFAULT_DWRR_UID, 0, 0, id); +} + +int mlx5_qos_update_node(struct mlx5_core_dev *mdev, u32 parent_id, + u32 bw_share, u32 max_avg_bw, u32 id) +{ + u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0}; + u32 bitmask = 0; + + MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_id); + MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share); + MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_avg_bw); + + bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_BW_SHARE; + bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW; + + return mlx5_modify_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC, + sched_ctx, id, bitmask); +} + +int mlx5_qos_destroy_node(struct mlx5_core_dev *mdev, u32 id) +{ + return mlx5_destroy_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC, id); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qos.h b/drivers/net/ethernet/mellanox/mlx5/core/qos.h new file mode 100644 index 000000000000..125e4e47e6f7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/qos.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */ + +#ifndef __MLX5_QOS_H +#define __MLX5_QOS_H + +#include "mlx5_core.h" + +#define MLX5_DEBUG_QOS_MASK BIT(4) + +#define qos_err(mdev, fmt, ...) \ + mlx5_core_err(mdev, "QoS: " fmt, ##__VA_ARGS__) +#define qos_warn(mdev, fmt, ...) \ + mlx5_core_warn(mdev, "QoS: " fmt, ##__VA_ARGS__) +#define qos_dbg(mdev, fmt, ...) \ + mlx5_core_dbg_mask(mdev, MLX5_DEBUG_QOS_MASK, "QoS: " fmt, ##__VA_ARGS__) + +bool mlx5_qos_is_supported(struct mlx5_core_dev *mdev); +int mlx5_qos_max_leaf_nodes(struct mlx5_core_dev *mdev); + +int mlx5_qos_create_leaf_node(struct mlx5_core_dev *mdev, u32 parent_id, + u32 bw_share, u32 max_avg_bw, u32 *id); +int mlx5_qos_create_inner_node(struct mlx5_core_dev *mdev, u32 parent_id, + u32 bw_share, u32 max_avg_bw, u32 *id); +int mlx5_qos_create_root_node(struct mlx5_core_dev *mdev, u32 *id); +int mlx5_qos_update_node(struct mlx5_core_dev *mdev, u32 parent_id, u32 bw_share, + u32 max_avg_bw, u32 id); +int mlx5_qos_destroy_node(struct mlx5_core_dev *mdev, u32 id); + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/cmd.c new file mode 100644 index 000000000000..a8d75c2f0275 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/cmd.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#include <linux/mlx5/driver.h> +#include "priv.h" + +int mlx5_cmd_alloc_sf(struct mlx5_core_dev *dev, u16 function_id) +{ + u32 out[MLX5_ST_SZ_DW(alloc_sf_out)] = {}; + u32 in[MLX5_ST_SZ_DW(alloc_sf_in)] = {}; + + MLX5_SET(alloc_sf_in, in, opcode, MLX5_CMD_OP_ALLOC_SF); + MLX5_SET(alloc_sf_in, in, function_id, function_id); + + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + +int mlx5_cmd_dealloc_sf(struct mlx5_core_dev *dev, u16 function_id) +{ + u32 out[MLX5_ST_SZ_DW(dealloc_sf_out)] = {}; + u32 in[MLX5_ST_SZ_DW(dealloc_sf_in)] = {}; + + MLX5_SET(dealloc_sf_in, in, opcode, MLX5_CMD_OP_DEALLOC_SF); + MLX5_SET(dealloc_sf_in, in, function_id, function_id); + + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + +int mlx5_cmd_sf_enable_hca(struct mlx5_core_dev *dev, u16 func_id) +{ + u32 out[MLX5_ST_SZ_DW(enable_hca_out)] = {}; + u32 in[MLX5_ST_SZ_DW(enable_hca_in)] = {}; + + MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA); + MLX5_SET(enable_hca_in, in, function_id, func_id); + MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0); + return mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +} + +int mlx5_cmd_sf_disable_hca(struct mlx5_core_dev *dev, u16 func_id) +{ + u32 out[MLX5_ST_SZ_DW(disable_hca_out)] = {}; + u32 in[MLX5_ST_SZ_DW(disable_hca_in)] = {}; + + MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA); + MLX5_SET(disable_hca_in, in, function_id, func_id); + MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0); + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c new file mode 100644 index 000000000000..b265f27b2166 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#include <linux/mlx5/driver.h> +#include <linux/mlx5/device.h> +#include "mlx5_core.h" +#include "dev.h" +#include "sf/vhca_event.h" +#include "sf/sf.h" +#include "sf/mlx5_ifc_vhca_event.h" +#include "ecpf.h" + +struct mlx5_sf_dev_table { + struct xarray devices; + unsigned int max_sfs; + phys_addr_t base_address; + u64 sf_bar_length; + struct notifier_block nb; + struct mlx5_core_dev *dev; +}; + +static bool mlx5_sf_dev_supported(const struct mlx5_core_dev *dev) +{ + return MLX5_CAP_GEN(dev, sf) && mlx5_vhca_event_supported(dev); +} + +bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev) +{ + struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table; + + if (!mlx5_sf_dev_supported(dev)) + return false; + + return !xa_empty(&table->devices); +} + +static ssize_t sfnum_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct auxiliary_device *adev = container_of(dev, struct auxiliary_device, dev); + struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", sf_dev->sfnum); +} +static DEVICE_ATTR_RO(sfnum); + +static struct attribute *sf_device_attrs[] = { + &dev_attr_sfnum.attr, + NULL, +}; + +static const struct attribute_group sf_attr_group = { + .attrs = sf_device_attrs, +}; + +static const struct attribute_group *sf_attr_groups[2] = { + &sf_attr_group, + NULL +}; + +static void mlx5_sf_dev_release(struct device *device) +{ + struct auxiliary_device *adev = container_of(device, struct auxiliary_device, dev); + struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev); + + mlx5_adev_idx_free(adev->id); + kfree(sf_dev); +} + +static void mlx5_sf_dev_remove(struct mlx5_sf_dev *sf_dev) +{ + auxiliary_device_delete(&sf_dev->adev); + auxiliary_device_uninit(&sf_dev->adev); +} + +static void mlx5_sf_dev_add(struct mlx5_core_dev *dev, u16 sf_index, u32 sfnum) +{ + struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table; + struct mlx5_sf_dev *sf_dev; + struct pci_dev *pdev; + int err; + int id; + + id = mlx5_adev_idx_alloc(); + if (id < 0) { + err = id; + goto add_err; + } + + sf_dev = kzalloc(sizeof(*sf_dev), GFP_KERNEL); + if (!sf_dev) { + mlx5_adev_idx_free(id); + err = -ENOMEM; + goto add_err; + } + pdev = dev->pdev; + sf_dev->adev.id = id; + sf_dev->adev.name = MLX5_SF_DEV_ID_NAME; + sf_dev->adev.dev.release = mlx5_sf_dev_release; + sf_dev->adev.dev.parent = &pdev->dev; + sf_dev->adev.dev.groups = sf_attr_groups; + sf_dev->sfnum = sfnum; + sf_dev->parent_mdev = dev; + + if (!table->max_sfs) { + mlx5_adev_idx_free(id); + kfree(sf_dev); + err = -EOPNOTSUPP; + goto add_err; + } + sf_dev->bar_base_addr = table->base_address + (sf_index * table->sf_bar_length); + + err = auxiliary_device_init(&sf_dev->adev); + if (err) { + mlx5_adev_idx_free(id); + kfree(sf_dev); + goto add_err; + } + + err = auxiliary_device_add(&sf_dev->adev); + if (err) { + put_device(&sf_dev->adev.dev); + goto add_err; + } + + err = xa_insert(&table->devices, sf_index, sf_dev, GFP_KERNEL); + if (err) + goto xa_err; + return; + +xa_err: + mlx5_sf_dev_remove(sf_dev); +add_err: + mlx5_core_err(dev, "SF DEV: fail device add for index=%d sfnum=%d err=%d\n", + sf_index, sfnum, err); +} + +static void mlx5_sf_dev_del(struct mlx5_core_dev *dev, struct mlx5_sf_dev *sf_dev, u16 sf_index) +{ + struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table; + + xa_erase(&table->devices, sf_index); + mlx5_sf_dev_remove(sf_dev); +} + +static int +mlx5_sf_dev_state_change_handler(struct notifier_block *nb, unsigned long event_code, void *data) +{ + struct mlx5_sf_dev_table *table = container_of(nb, struct mlx5_sf_dev_table, nb); + const struct mlx5_vhca_state_event *event = data; + struct mlx5_sf_dev *sf_dev; + u16 sf_index; + + sf_index = event->function_id - MLX5_CAP_GEN(table->dev, sf_base_id); + sf_dev = xa_load(&table->devices, sf_index); + switch (event->new_vhca_state) { + case MLX5_VHCA_STATE_ALLOCATED: + if (sf_dev) + mlx5_sf_dev_del(table->dev, sf_dev, sf_index); + break; + case MLX5_VHCA_STATE_TEARDOWN_REQUEST: + if (sf_dev) + mlx5_sf_dev_del(table->dev, sf_dev, sf_index); + else + mlx5_core_err(table->dev, + "SF DEV: teardown state for invalid dev index=%d fn_id=0x%x\n", + sf_index, event->sw_function_id); + break; + case MLX5_VHCA_STATE_ACTIVE: + if (!sf_dev) + mlx5_sf_dev_add(table->dev, sf_index, event->sw_function_id); + break; + default: + break; + } + return 0; +} + +static int mlx5_sf_dev_vhca_arm_all(struct mlx5_sf_dev_table *table) +{ + struct mlx5_core_dev *dev = table->dev; + u16 max_functions; + u16 function_id; + int err = 0; + bool ecpu; + int i; + + max_functions = mlx5_sf_max_functions(dev); + function_id = MLX5_CAP_GEN(dev, sf_base_id); + ecpu = mlx5_read_embedded_cpu(dev); + /* Arm the vhca context as the vhca event notifier */ + for (i = 0; i < max_functions; i++) { + err = mlx5_vhca_event_arm(dev, function_id, ecpu); + if (err) + return err; + + function_id++; + } + return 0; +} + +void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_dev_table *table; + unsigned int max_sfs; + int err; + + if (!mlx5_sf_dev_supported(dev) || !mlx5_vhca_event_supported(dev)) + return; + + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) { + err = -ENOMEM; + goto table_err; + } + + table->nb.notifier_call = mlx5_sf_dev_state_change_handler; + table->dev = dev; + if (MLX5_CAP_GEN(dev, max_num_sf)) + max_sfs = MLX5_CAP_GEN(dev, max_num_sf); + else + max_sfs = 1 << MLX5_CAP_GEN(dev, log_max_sf); + table->sf_bar_length = 1 << (MLX5_CAP_GEN(dev, log_min_sf_size) + 12); + table->base_address = pci_resource_start(dev->pdev, 2); + table->max_sfs = max_sfs; + xa_init(&table->devices); + dev->priv.sf_dev_table = table; + + err = mlx5_vhca_event_notifier_register(dev, &table->nb); + if (err) + goto vhca_err; + err = mlx5_sf_dev_vhca_arm_all(table); + if (err) + goto arm_err; + mlx5_core_dbg(dev, "SF DEV: max sf devices=%d\n", max_sfs); + return; + +arm_err: + mlx5_vhca_event_notifier_unregister(dev, &table->nb); +vhca_err: + table->max_sfs = 0; + kfree(table); + dev->priv.sf_dev_table = NULL; +table_err: + mlx5_core_err(dev, "SF DEV table create err = %d\n", err); +} + +static void mlx5_sf_dev_destroy_all(struct mlx5_sf_dev_table *table) +{ + struct mlx5_sf_dev *sf_dev; + unsigned long index; + + xa_for_each(&table->devices, index, sf_dev) { + xa_erase(&table->devices, index); + mlx5_sf_dev_remove(sf_dev); + } +} + +void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table; + + if (!table) + return; + + mlx5_vhca_event_notifier_unregister(dev, &table->nb); + + /* Now that event handler is not running, it is safe to destroy + * the sf device without race. + */ + mlx5_sf_dev_destroy_all(table); + + WARN_ON(!xa_empty(&table->devices)); + kfree(table); + dev->priv.sf_dev_table = NULL; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h new file mode 100644 index 000000000000..4de02902aef1 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#ifndef __MLX5_SF_DEV_H__ +#define __MLX5_SF_DEV_H__ + +#ifdef CONFIG_MLX5_SF + +#include <linux/auxiliary_bus.h> + +#define MLX5_SF_DEV_ID_NAME "sf" + +struct mlx5_sf_dev { + struct auxiliary_device adev; + struct mlx5_core_dev *parent_mdev; + struct mlx5_core_dev *mdev; + phys_addr_t bar_base_addr; + u32 sfnum; +}; + +void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev); +void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev); + +int mlx5_sf_driver_register(void); +void mlx5_sf_driver_unregister(void); + +bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev); + +#else + +static inline void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev) +{ +} + +static inline void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev) +{ +} + +static inline int mlx5_sf_driver_register(void) +{ + return 0; +} + +static inline void mlx5_sf_driver_unregister(void) +{ +} + +static inline bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev) +{ + return 0; +} + +#endif + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c new file mode 100644 index 000000000000..daf63a8115e0 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#include <linux/mlx5/driver.h> +#include <linux/mlx5/device.h> +#include "mlx5_core.h" +#include "dev.h" +#include "devlink.h" + +static int mlx5_sf_dev_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) +{ + struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev); + struct mlx5_core_dev *mdev; + struct devlink *devlink; + int err; + + devlink = mlx5_devlink_alloc(); + if (!devlink) + return -ENOMEM; + + mdev = devlink_priv(devlink); + mdev->device = &adev->dev; + mdev->pdev = sf_dev->parent_mdev->pdev; + mdev->bar_addr = sf_dev->bar_base_addr; + mdev->iseg_base = sf_dev->bar_base_addr; + mdev->coredev_type = MLX5_COREDEV_SF; + mdev->priv.parent_mdev = sf_dev->parent_mdev; + mdev->priv.adev_idx = adev->id; + sf_dev->mdev = mdev; + + err = mlx5_mdev_init(mdev, MLX5_DEFAULT_PROF); + if (err) { + mlx5_core_warn(mdev, "mlx5_mdev_init on err=%d\n", err); + goto mdev_err; + } + + mdev->iseg = ioremap(mdev->iseg_base, sizeof(*mdev->iseg)); + if (!mdev->iseg) { + mlx5_core_warn(mdev, "remap error\n"); + goto remap_err; + } + + err = mlx5_load_one(mdev, true); + if (err) { + mlx5_core_warn(mdev, "mlx5_load_one err=%d\n", err); + goto load_one_err; + } + return 0; + +load_one_err: + iounmap(mdev->iseg); +remap_err: + mlx5_mdev_uninit(mdev); +mdev_err: + mlx5_devlink_free(devlink); + return err; +} + +static void mlx5_sf_dev_remove(struct auxiliary_device *adev) +{ + struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev); + struct devlink *devlink; + + devlink = priv_to_devlink(sf_dev->mdev); + mlx5_unload_one(sf_dev->mdev, true); + iounmap(sf_dev->mdev->iseg); + mlx5_mdev_uninit(sf_dev->mdev); + mlx5_devlink_free(devlink); +} + +static void mlx5_sf_dev_shutdown(struct auxiliary_device *adev) +{ + struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev); + + mlx5_unload_one(sf_dev->mdev, false); +} + +static const struct auxiliary_device_id mlx5_sf_dev_id_table[] = { + { .name = MLX5_ADEV_NAME "." MLX5_SF_DEV_ID_NAME, }, + { }, +}; + +MODULE_DEVICE_TABLE(auxiliary, mlx5_sf_dev_id_table); + +static struct auxiliary_driver mlx5_sf_driver = { + .name = MLX5_SF_DEV_ID_NAME, + .probe = mlx5_sf_dev_probe, + .remove = mlx5_sf_dev_remove, + .shutdown = mlx5_sf_dev_shutdown, + .id_table = mlx5_sf_dev_id_table, +}; + +int mlx5_sf_driver_register(void) +{ + return auxiliary_driver_register(&mlx5_sf_driver); +} + +void mlx5_sf_driver_unregister(void) +{ + auxiliary_driver_unregister(&mlx5_sf_driver); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c new file mode 100644 index 000000000000..c2ba41bb7a70 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#include <linux/mlx5/driver.h> +#include "eswitch.h" +#include "priv.h" +#include "sf/dev/dev.h" +#include "mlx5_ifc_vhca_event.h" +#include "vhca_event.h" +#include "ecpf.h" + +struct mlx5_sf { + struct devlink_port dl_port; + unsigned int port_index; + u16 id; + u16 hw_fn_id; + u16 hw_state; +}; + +struct mlx5_sf_table { + struct mlx5_core_dev *dev; /* To refer from notifier context. */ + struct xarray port_indices; /* port index based lookup. */ + refcount_t refcount; + struct completion disable_complete; + struct mutex sf_state_lock; /* Serializes sf state among user cmds & vhca event handler. */ + struct notifier_block esw_nb; + struct notifier_block vhca_nb; + u8 ecpu: 1; +}; + +static struct mlx5_sf * +mlx5_sf_lookup_by_index(struct mlx5_sf_table *table, unsigned int port_index) +{ + return xa_load(&table->port_indices, port_index); +} + +static struct mlx5_sf * +mlx5_sf_lookup_by_function_id(struct mlx5_sf_table *table, unsigned int fn_id) +{ + unsigned long index; + struct mlx5_sf *sf; + + xa_for_each(&table->port_indices, index, sf) { + if (sf->hw_fn_id == fn_id) + return sf; + } + return NULL; +} + +static int mlx5_sf_id_insert(struct mlx5_sf_table *table, struct mlx5_sf *sf) +{ + return xa_insert(&table->port_indices, sf->port_index, sf, GFP_KERNEL); +} + +static void mlx5_sf_id_erase(struct mlx5_sf_table *table, struct mlx5_sf *sf) +{ + xa_erase(&table->port_indices, sf->port_index); +} + +static struct mlx5_sf * +mlx5_sf_alloc(struct mlx5_sf_table *table, u32 sfnum, struct netlink_ext_ack *extack) +{ + unsigned int dl_port_index; + struct mlx5_sf *sf; + u16 hw_fn_id; + int id_err; + int err; + + id_err = mlx5_sf_hw_table_sf_alloc(table->dev, sfnum); + if (id_err < 0) { + err = id_err; + goto id_err; + } + + sf = kzalloc(sizeof(*sf), GFP_KERNEL); + if (!sf) { + err = -ENOMEM; + goto alloc_err; + } + sf->id = id_err; + hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, sf->id); + dl_port_index = mlx5_esw_vport_to_devlink_port_index(table->dev, hw_fn_id); + sf->port_index = dl_port_index; + sf->hw_fn_id = hw_fn_id; + sf->hw_state = MLX5_VHCA_STATE_ALLOCATED; + + err = mlx5_sf_id_insert(table, sf); + if (err) + goto insert_err; + + return sf; + +insert_err: + kfree(sf); +alloc_err: + mlx5_sf_hw_table_sf_free(table->dev, id_err); +id_err: + if (err == -EEXIST) + NL_SET_ERR_MSG_MOD(extack, "SF already exist. Choose different sfnum"); + return ERR_PTR(err); +} + +static void mlx5_sf_free(struct mlx5_sf_table *table, struct mlx5_sf *sf) +{ + mlx5_sf_id_erase(table, sf); + mlx5_sf_hw_table_sf_free(table->dev, sf->id); + kfree(sf); +} + +static struct mlx5_sf_table *mlx5_sf_table_try_get(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_table *table = dev->priv.sf_table; + + if (!table) + return NULL; + + return refcount_inc_not_zero(&table->refcount) ? table : NULL; +} + +static void mlx5_sf_table_put(struct mlx5_sf_table *table) +{ + if (refcount_dec_and_test(&table->refcount)) + complete(&table->disable_complete); +} + +static enum devlink_port_fn_state mlx5_sf_to_devlink_state(u8 hw_state) +{ + switch (hw_state) { + case MLX5_VHCA_STATE_ACTIVE: + case MLX5_VHCA_STATE_IN_USE: + case MLX5_VHCA_STATE_TEARDOWN_REQUEST: + return DEVLINK_PORT_FN_STATE_ACTIVE; + case MLX5_VHCA_STATE_INVALID: + case MLX5_VHCA_STATE_ALLOCATED: + default: + return DEVLINK_PORT_FN_STATE_INACTIVE; + } +} + +static enum devlink_port_fn_opstate mlx5_sf_to_devlink_opstate(u8 hw_state) +{ + switch (hw_state) { + case MLX5_VHCA_STATE_IN_USE: + case MLX5_VHCA_STATE_TEARDOWN_REQUEST: + return DEVLINK_PORT_FN_OPSTATE_ATTACHED; + case MLX5_VHCA_STATE_INVALID: + case MLX5_VHCA_STATE_ALLOCATED: + case MLX5_VHCA_STATE_ACTIVE: + default: + return DEVLINK_PORT_FN_OPSTATE_DETACHED; + } +} + +static bool mlx5_sf_is_active(const struct mlx5_sf *sf) +{ + return sf->hw_state == MLX5_VHCA_STATE_ACTIVE || sf->hw_state == MLX5_VHCA_STATE_IN_USE; +} + +int mlx5_devlink_sf_port_fn_state_get(struct devlink *devlink, struct devlink_port *dl_port, + enum devlink_port_fn_state *state, + enum devlink_port_fn_opstate *opstate, + struct netlink_ext_ack *extack) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_sf_table *table; + struct mlx5_sf *sf; + int err = 0; + + table = mlx5_sf_table_try_get(dev); + if (!table) + return -EOPNOTSUPP; + + sf = mlx5_sf_lookup_by_index(table, dl_port->index); + if (!sf) { + err = -EOPNOTSUPP; + goto sf_err; + } + mutex_lock(&table->sf_state_lock); + *state = mlx5_sf_to_devlink_state(sf->hw_state); + *opstate = mlx5_sf_to_devlink_opstate(sf->hw_state); + mutex_unlock(&table->sf_state_lock); +sf_err: + mlx5_sf_table_put(table); + return err; +} + +static int mlx5_sf_activate(struct mlx5_core_dev *dev, struct mlx5_sf *sf) +{ + int err; + + if (mlx5_sf_is_active(sf)) + return 0; + if (sf->hw_state != MLX5_VHCA_STATE_ALLOCATED) + return -EINVAL; + + err = mlx5_cmd_sf_enable_hca(dev, sf->hw_fn_id); + if (err) + return err; + + sf->hw_state = MLX5_VHCA_STATE_ACTIVE; + return 0; +} + +static int mlx5_sf_deactivate(struct mlx5_core_dev *dev, struct mlx5_sf *sf) +{ + int err; + + if (!mlx5_sf_is_active(sf)) + return 0; + + err = mlx5_cmd_sf_disable_hca(dev, sf->hw_fn_id); + if (err) + return err; + + sf->hw_state = MLX5_VHCA_STATE_TEARDOWN_REQUEST; + return 0; +} + +static int mlx5_sf_state_set(struct mlx5_core_dev *dev, struct mlx5_sf_table *table, + struct mlx5_sf *sf, + enum devlink_port_fn_state state) +{ + int err = 0; + + mutex_lock(&table->sf_state_lock); + if (state == mlx5_sf_to_devlink_state(sf->hw_state)) + goto out; + if (state == DEVLINK_PORT_FN_STATE_ACTIVE) + err = mlx5_sf_activate(dev, sf); + else if (state == DEVLINK_PORT_FN_STATE_INACTIVE) + err = mlx5_sf_deactivate(dev, sf); + else + err = -EINVAL; +out: + mutex_unlock(&table->sf_state_lock); + return err; +} + +int mlx5_devlink_sf_port_fn_state_set(struct devlink *devlink, struct devlink_port *dl_port, + enum devlink_port_fn_state state, + struct netlink_ext_ack *extack) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_sf_table *table; + struct mlx5_sf *sf; + int err; + + table = mlx5_sf_table_try_get(dev); + if (!table) { + NL_SET_ERR_MSG_MOD(extack, + "Port state set is only supported in eswitch switchdev mode or SF ports are disabled."); + return -EOPNOTSUPP; + } + sf = mlx5_sf_lookup_by_index(table, dl_port->index); + if (!sf) { + err = -ENODEV; + goto out; + } + + err = mlx5_sf_state_set(dev, table, sf, state); +out: + mlx5_sf_table_put(table); + return err; +} + +static int mlx5_sf_add(struct mlx5_core_dev *dev, struct mlx5_sf_table *table, + const struct devlink_port_new_attrs *new_attr, + struct netlink_ext_ack *extack, + unsigned int *new_port_index) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_sf *sf; + u16 hw_fn_id; + int err; + + sf = mlx5_sf_alloc(table, new_attr->sfnum, extack); + if (IS_ERR(sf)) + return PTR_ERR(sf); + + hw_fn_id = mlx5_sf_sw_to_hw_id(dev, sf->id); + err = mlx5_esw_offloads_sf_vport_enable(esw, &sf->dl_port, hw_fn_id, new_attr->sfnum); + if (err) + goto esw_err; + *new_port_index = sf->port_index; + return 0; + +esw_err: + mlx5_sf_free(table, sf); + return err; +} + +static int +mlx5_sf_new_check_attr(struct mlx5_core_dev *dev, const struct devlink_port_new_attrs *new_attr, + struct netlink_ext_ack *extack) +{ + if (new_attr->flavour != DEVLINK_PORT_FLAVOUR_PCI_SF) { + NL_SET_ERR_MSG_MOD(extack, "Driver supports only SF port addition"); + return -EOPNOTSUPP; + } + if (new_attr->port_index_valid) { + NL_SET_ERR_MSG_MOD(extack, + "Driver does not support user defined port index assignment"); + return -EOPNOTSUPP; + } + if (!new_attr->sfnum_valid) { + NL_SET_ERR_MSG_MOD(extack, + "User must provide unique sfnum. Driver does not support auto assignment"); + return -EOPNOTSUPP; + } + if (new_attr->controller_valid && new_attr->controller) { + NL_SET_ERR_MSG_MOD(extack, "External controller is unsupported"); + return -EOPNOTSUPP; + } + if (new_attr->pfnum != PCI_FUNC(dev->pdev->devfn)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid pfnum supplied"); + return -EOPNOTSUPP; + } + return 0; +} + +int mlx5_devlink_sf_port_new(struct devlink *devlink, + const struct devlink_port_new_attrs *new_attr, + struct netlink_ext_ack *extack, + unsigned int *new_port_index) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_sf_table *table; + int err; + + err = mlx5_sf_new_check_attr(dev, new_attr, extack); + if (err) + return err; + + table = mlx5_sf_table_try_get(dev); + if (!table) { + NL_SET_ERR_MSG_MOD(extack, + "Port add is only supported in eswitch switchdev mode or SF ports are disabled."); + return -EOPNOTSUPP; + } + err = mlx5_sf_add(dev, table, new_attr, extack, new_port_index); + mlx5_sf_table_put(table); + return err; +} + +static void mlx5_sf_dealloc(struct mlx5_sf_table *table, struct mlx5_sf *sf) +{ + if (sf->hw_state == MLX5_VHCA_STATE_ALLOCATED) { + mlx5_sf_free(table, sf); + } else if (mlx5_sf_is_active(sf)) { + /* Even if its active, it is treated as in_use because by the time, + * it is disabled here, it may getting used. So it is safe to + * always look for the event to ensure that it is recycled only after + * firmware gives confirmation that it is detached by the driver. + */ + mlx5_cmd_sf_disable_hca(table->dev, sf->hw_fn_id); + mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->id); + kfree(sf); + } else { + mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->id); + kfree(sf); + } +} + +int mlx5_devlink_sf_port_del(struct devlink *devlink, unsigned int port_index, + struct netlink_ext_ack *extack) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_sf_table *table; + struct mlx5_sf *sf; + int err = 0; + + table = mlx5_sf_table_try_get(dev); + if (!table) { + NL_SET_ERR_MSG_MOD(extack, + "Port del is only supported in eswitch switchdev mode or SF ports are disabled."); + return -EOPNOTSUPP; + } + sf = mlx5_sf_lookup_by_index(table, port_index); + if (!sf) { + err = -ENODEV; + goto sf_err; + } + + mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id); + mlx5_sf_id_erase(table, sf); + + mutex_lock(&table->sf_state_lock); + mlx5_sf_dealloc(table, sf); + mutex_unlock(&table->sf_state_lock); +sf_err: + mlx5_sf_table_put(table); + return err; +} + +static bool mlx5_sf_state_update_check(const struct mlx5_sf *sf, u8 new_state) +{ + if (sf->hw_state == MLX5_VHCA_STATE_ACTIVE && new_state == MLX5_VHCA_STATE_IN_USE) + return true; + + if (sf->hw_state == MLX5_VHCA_STATE_IN_USE && new_state == MLX5_VHCA_STATE_ACTIVE) + return true; + + if (sf->hw_state == MLX5_VHCA_STATE_TEARDOWN_REQUEST && + new_state == MLX5_VHCA_STATE_ALLOCATED) + return true; + + return false; +} + +static int mlx5_sf_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data) +{ + struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, vhca_nb); + const struct mlx5_vhca_state_event *event = data; + bool update = false; + struct mlx5_sf *sf; + + table = mlx5_sf_table_try_get(table->dev); + if (!table) + return 0; + + mutex_lock(&table->sf_state_lock); + sf = mlx5_sf_lookup_by_function_id(table, event->function_id); + if (!sf) + goto sf_err; + + /* When driver is attached or detached to a function, an event + * notifies such state change. + */ + update = mlx5_sf_state_update_check(sf, event->new_vhca_state); + if (update) + sf->hw_state = event->new_vhca_state; +sf_err: + mutex_unlock(&table->sf_state_lock); + mlx5_sf_table_put(table); + return 0; +} + +static void mlx5_sf_table_enable(struct mlx5_sf_table *table) +{ + if (!mlx5_sf_max_functions(table->dev)) + return; + + init_completion(&table->disable_complete); + refcount_set(&table->refcount, 1); +} + +static void mlx5_sf_deactivate_all(struct mlx5_sf_table *table) +{ + struct mlx5_eswitch *esw = table->dev->priv.eswitch; + unsigned long index; + struct mlx5_sf *sf; + + /* At this point, no new user commands can start and no vhca event can + * arrive. It is safe to destroy all user created SFs. + */ + xa_for_each(&table->port_indices, index, sf) { + mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id); + mlx5_sf_id_erase(table, sf); + mlx5_sf_dealloc(table, sf); + } +} + +static void mlx5_sf_table_disable(struct mlx5_sf_table *table) +{ + if (!mlx5_sf_max_functions(table->dev)) + return; + + if (!refcount_read(&table->refcount)) + return; + + /* Balances with refcount_set; drop the reference so that new user cmd cannot start + * and new vhca event handler cannnot run. + */ + mlx5_sf_table_put(table); + wait_for_completion(&table->disable_complete); + + mlx5_sf_deactivate_all(table); +} + +static int mlx5_sf_esw_event(struct notifier_block *nb, unsigned long event, void *data) +{ + struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, esw_nb); + const struct mlx5_esw_event_info *mode = data; + + switch (mode->new_mode) { + case MLX5_ESWITCH_OFFLOADS: + mlx5_sf_table_enable(table); + break; + case MLX5_ESWITCH_NONE: + mlx5_sf_table_disable(table); + break; + default: + break; + }; + + return 0; +} + +static bool mlx5_sf_table_supported(const struct mlx5_core_dev *dev) +{ + return dev->priv.eswitch && MLX5_ESWITCH_MANAGER(dev) && mlx5_sf_supported(dev); +} + +int mlx5_sf_table_init(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_table *table; + int err; + + if (!mlx5_sf_table_supported(dev) || !mlx5_vhca_event_supported(dev)) + return 0; + + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + mutex_init(&table->sf_state_lock); + table->dev = dev; + xa_init(&table->port_indices); + dev->priv.sf_table = table; + refcount_set(&table->refcount, 0); + table->esw_nb.notifier_call = mlx5_sf_esw_event; + err = mlx5_esw_event_notifier_register(dev->priv.eswitch, &table->esw_nb); + if (err) + goto reg_err; + + table->vhca_nb.notifier_call = mlx5_sf_vhca_event; + err = mlx5_vhca_event_notifier_register(table->dev, &table->vhca_nb); + if (err) + goto vhca_err; + + return 0; + +vhca_err: + mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb); +reg_err: + mutex_destroy(&table->sf_state_lock); + kfree(table); + dev->priv.sf_table = NULL; + return err; +} + +void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_table *table = dev->priv.sf_table; + + if (!table) + return; + + mlx5_vhca_event_notifier_unregister(table->dev, &table->vhca_nb); + mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb); + WARN_ON(refcount_read(&table->refcount)); + mutex_destroy(&table->sf_state_lock); + WARN_ON(!xa_empty(&table->port_indices)); + kfree(table); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c new file mode 100644 index 000000000000..58b6be0b03d7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 Mellanox Technologies Ltd */ +#include <linux/mlx5/driver.h> +#include "vhca_event.h" +#include "priv.h" +#include "sf.h" +#include "mlx5_ifc_vhca_event.h" +#include "vhca_event.h" +#include "ecpf.h" + +struct mlx5_sf_hw { + u32 usr_sfnum; + u8 allocated: 1; + u8 pending_delete: 1; +}; + +struct mlx5_sf_hw_table { + struct mlx5_core_dev *dev; + struct mlx5_sf_hw *sfs; + int max_local_functions; + u8 ecpu: 1; + struct mutex table_lock; /* Serializes sf deletion and vhca state change handler. */ + struct notifier_block vhca_nb; +}; + +u16 mlx5_sf_sw_to_hw_id(const struct mlx5_core_dev *dev, u16 sw_id) +{ + return sw_id + mlx5_sf_start_function_id(dev); +} + +static u16 mlx5_sf_hw_to_sw_id(const struct mlx5_core_dev *dev, u16 hw_id) +{ + return hw_id - mlx5_sf_start_function_id(dev); +} + +int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 usr_sfnum) +{ + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + int sw_id = -ENOSPC; + u16 hw_fn_id; + int err; + int i; + + if (!table->max_local_functions) + return -EOPNOTSUPP; + + mutex_lock(&table->table_lock); + /* Check if sf with same sfnum already exists or not. */ + for (i = 0; i < table->max_local_functions; i++) { + if (table->sfs[i].allocated && table->sfs[i].usr_sfnum == usr_sfnum) { + err = -EEXIST; + goto exist_err; + } + } + + /* Find the free entry and allocate the entry from the array */ + for (i = 0; i < table->max_local_functions; i++) { + if (!table->sfs[i].allocated) { + table->sfs[i].usr_sfnum = usr_sfnum; + table->sfs[i].allocated = true; + sw_id = i; + break; + } + } + if (sw_id == -ENOSPC) { + err = -ENOSPC; + goto err; + } + + hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, sw_id); + err = mlx5_cmd_alloc_sf(table->dev, hw_fn_id); + if (err) + goto err; + + err = mlx5_modify_vhca_sw_id(dev, hw_fn_id, table->ecpu, usr_sfnum); + if (err) + goto vhca_err; + + mutex_unlock(&table->table_lock); + return sw_id; + +vhca_err: + mlx5_cmd_dealloc_sf(table->dev, hw_fn_id); +err: + table->sfs[i].allocated = false; +exist_err: + mutex_unlock(&table->table_lock); + return err; +} + +static void _mlx5_sf_hw_id_free(struct mlx5_core_dev *dev, u16 id) +{ + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + u16 hw_fn_id; + + hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, id); + mlx5_cmd_dealloc_sf(table->dev, hw_fn_id); + table->sfs[id].allocated = false; + table->sfs[id].pending_delete = false; +} + +void mlx5_sf_hw_table_sf_free(struct mlx5_core_dev *dev, u16 id) +{ + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + + mutex_lock(&table->table_lock); + _mlx5_sf_hw_id_free(dev, id); + mutex_unlock(&table->table_lock); +} + +void mlx5_sf_hw_table_sf_deferred_free(struct mlx5_core_dev *dev, u16 id) +{ + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {}; + u16 hw_fn_id; + u8 state; + int err; + + hw_fn_id = mlx5_sf_sw_to_hw_id(dev, id); + mutex_lock(&table->table_lock); + err = mlx5_cmd_query_vhca_state(dev, hw_fn_id, table->ecpu, out, sizeof(out)); + if (err) + goto err; + state = MLX5_GET(query_vhca_state_out, out, vhca_state_context.vhca_state); + if (state == MLX5_VHCA_STATE_ALLOCATED) { + mlx5_cmd_dealloc_sf(table->dev, hw_fn_id); + table->sfs[id].allocated = false; + } else { + table->sfs[id].pending_delete = true; + } +err: + mutex_unlock(&table->table_lock); +} + +static void mlx5_sf_hw_dealloc_all(struct mlx5_sf_hw_table *table) +{ + int i; + + for (i = 0; i < table->max_local_functions; i++) { + if (table->sfs[i].allocated) + _mlx5_sf_hw_id_free(table->dev, i); + } +} + +int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_hw_table *table; + struct mlx5_sf_hw *sfs; + int max_functions; + + if (!mlx5_sf_supported(dev) || !mlx5_vhca_event_supported(dev)) + return 0; + + max_functions = mlx5_sf_max_functions(dev); + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + sfs = kcalloc(max_functions, sizeof(*sfs), GFP_KERNEL); + if (!sfs) + goto table_err; + + mutex_init(&table->table_lock); + table->dev = dev; + table->sfs = sfs; + table->max_local_functions = max_functions; + table->ecpu = mlx5_read_embedded_cpu(dev); + dev->priv.sf_hw_table = table; + mlx5_core_dbg(dev, "SF HW table: max sfs = %d\n", max_functions); + return 0; + +table_err: + kfree(table); + return -ENOMEM; +} + +void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + + if (!table) + return; + + mutex_destroy(&table->table_lock); + kfree(table->sfs); + kfree(table); +} + +static int mlx5_sf_hw_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data) +{ + struct mlx5_sf_hw_table *table = container_of(nb, struct mlx5_sf_hw_table, vhca_nb); + const struct mlx5_vhca_state_event *event = data; + struct mlx5_sf_hw *sf_hw; + u16 sw_id; + + if (event->new_vhca_state != MLX5_VHCA_STATE_ALLOCATED) + return 0; + + sw_id = mlx5_sf_hw_to_sw_id(table->dev, event->function_id); + sf_hw = &table->sfs[sw_id]; + + mutex_lock(&table->table_lock); + /* SF driver notified through firmware that SF is finally detached. + * Hence recycle the sf hardware id for reuse. + */ + if (sf_hw->allocated && sf_hw->pending_delete) + _mlx5_sf_hw_id_free(table->dev, sw_id); + mutex_unlock(&table->table_lock); + return 0; +} + +int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + + if (!table) + return 0; + + table->vhca_nb.notifier_call = mlx5_sf_hw_vhca_event; + return mlx5_vhca_event_notifier_register(table->dev, &table->vhca_nb); +} + +void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + + if (!table) + return; + + mlx5_vhca_event_notifier_unregister(table->dev, &table->vhca_nb); + /* Dealloc SFs whose firmware event has been missed. */ + mlx5_sf_hw_dealloc_all(table); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h new file mode 100644 index 000000000000..1daf5a122ba3 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#ifndef __MLX5_IFC_VHCA_EVENT_H__ +#define __MLX5_IFC_VHCA_EVENT_H__ + +enum mlx5_ifc_vhca_state { + MLX5_VHCA_STATE_INVALID = 0x0, + MLX5_VHCA_STATE_ALLOCATED = 0x1, + MLX5_VHCA_STATE_ACTIVE = 0x2, + MLX5_VHCA_STATE_IN_USE = 0x3, + MLX5_VHCA_STATE_TEARDOWN_REQUEST = 0x4, +}; + +struct mlx5_ifc_vhca_state_context_bits { + u8 arm_change_event[0x1]; + u8 reserved_at_1[0xb]; + u8 vhca_state[0x4]; + u8 reserved_at_10[0x10]; + + u8 sw_function_id[0x20]; + + u8 reserved_at_40[0x80]; +}; + +struct mlx5_ifc_query_vhca_state_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x40]; + + struct mlx5_ifc_vhca_state_context_bits vhca_state_context; +}; + +struct mlx5_ifc_query_vhca_state_in_bits { + u8 opcode[0x10]; + u8 uid[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 embedded_cpu_function[0x1]; + u8 reserved_at_41[0xf]; + u8 function_id[0x10]; + + u8 reserved_at_60[0x20]; +}; + +struct mlx5_ifc_vhca_state_field_select_bits { + u8 reserved_at_0[0x1e]; + u8 sw_function_id[0x1]; + u8 arm_change_event[0x1]; +}; + +struct mlx5_ifc_modify_vhca_state_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x40]; +}; + +struct mlx5_ifc_modify_vhca_state_in_bits { + u8 opcode[0x10]; + u8 uid[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 embedded_cpu_function[0x1]; + u8 reserved_at_41[0xf]; + u8 function_id[0x10]; + + struct mlx5_ifc_vhca_state_field_select_bits vhca_state_field_select; + + struct mlx5_ifc_vhca_state_context_bits vhca_state_context; +}; + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/priv.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/priv.h new file mode 100644 index 000000000000..cb02a51d0986 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/priv.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#ifndef __MLX5_SF_PRIV_H__ +#define __MLX5_SF_PRIV_H__ + +#include <linux/mlx5/driver.h> + +int mlx5_cmd_alloc_sf(struct mlx5_core_dev *dev, u16 function_id); +int mlx5_cmd_dealloc_sf(struct mlx5_core_dev *dev, u16 function_id); + +int mlx5_cmd_sf_enable_hca(struct mlx5_core_dev *dev, u16 func_id); +int mlx5_cmd_sf_disable_hca(struct mlx5_core_dev *dev, u16 func_id); + +u16 mlx5_sf_sw_to_hw_id(const struct mlx5_core_dev *dev, u16 sw_id); + +int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 usr_sfnum); +void mlx5_sf_hw_table_sf_free(struct mlx5_core_dev *dev, u16 id); +void mlx5_sf_hw_table_sf_deferred_free(struct mlx5_core_dev *dev, u16 id); + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h new file mode 100644 index 000000000000..0b6aea1e6a94 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#ifndef __MLX5_SF_H__ +#define __MLX5_SF_H__ + +#include <linux/mlx5/driver.h> + +static inline u16 mlx5_sf_start_function_id(const struct mlx5_core_dev *dev) +{ + return MLX5_CAP_GEN(dev, sf_base_id); +} + +#ifdef CONFIG_MLX5_SF + +static inline bool mlx5_sf_supported(const struct mlx5_core_dev *dev) +{ + return MLX5_CAP_GEN(dev, sf); +} + +static inline u16 mlx5_sf_max_functions(const struct mlx5_core_dev *dev) +{ + if (!mlx5_sf_supported(dev)) + return 0; + if (MLX5_CAP_GEN(dev, max_num_sf)) + return MLX5_CAP_GEN(dev, max_num_sf); + else + return 1 << MLX5_CAP_GEN(dev, log_max_sf); +} + +#else + +static inline bool mlx5_sf_supported(const struct mlx5_core_dev *dev) +{ + return false; +} + +static inline u16 mlx5_sf_max_functions(const struct mlx5_core_dev *dev) +{ + return 0; +} + +#endif + +#ifdef CONFIG_MLX5_SF_MANAGER + +int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev); +void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev); + +int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev); +void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev); + +int mlx5_sf_table_init(struct mlx5_core_dev *dev); +void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev); + +int mlx5_devlink_sf_port_new(struct devlink *devlink, + const struct devlink_port_new_attrs *add_attr, + struct netlink_ext_ack *extack, + unsigned int *new_port_index); +int mlx5_devlink_sf_port_del(struct devlink *devlink, unsigned int port_index, + struct netlink_ext_ack *extack); +int mlx5_devlink_sf_port_fn_state_get(struct devlink *devlink, struct devlink_port *dl_port, + enum devlink_port_fn_state *state, + enum devlink_port_fn_opstate *opstate, + struct netlink_ext_ack *extack); +int mlx5_devlink_sf_port_fn_state_set(struct devlink *devlink, struct devlink_port *dl_port, + enum devlink_port_fn_state state, + struct netlink_ext_ack *extack); +#else + +static inline int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) +{ + return 0; +} + +static inline void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev) +{ +} + +static inline int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev) +{ + return 0; +} + +static inline void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev) +{ +} + +static inline int mlx5_sf_table_init(struct mlx5_core_dev *dev) +{ + return 0; +} + +static inline void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev) +{ +} + +#endif + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c new file mode 100644 index 000000000000..af2f2dd9db25 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#include <linux/mlx5/driver.h> +#include "mlx5_ifc_vhca_event.h" +#include "mlx5_core.h" +#include "vhca_event.h" +#include "ecpf.h" + +struct mlx5_vhca_state_notifier { + struct mlx5_core_dev *dev; + struct mlx5_nb nb; + struct blocking_notifier_head n_head; +}; + +struct mlx5_vhca_event_work { + struct work_struct work; + struct mlx5_vhca_state_notifier *notifier; + struct mlx5_vhca_state_event event; +}; + +int mlx5_cmd_query_vhca_state(struct mlx5_core_dev *dev, u16 function_id, + bool ecpu, u32 *out, u32 outlen) +{ + u32 in[MLX5_ST_SZ_DW(query_vhca_state_in)] = {}; + + MLX5_SET(query_vhca_state_in, in, opcode, MLX5_CMD_OP_QUERY_VHCA_STATE); + MLX5_SET(query_vhca_state_in, in, function_id, function_id); + MLX5_SET(query_vhca_state_in, in, embedded_cpu_function, ecpu); + + return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen); +} + +static int mlx5_cmd_modify_vhca_state(struct mlx5_core_dev *dev, u16 function_id, + bool ecpu, u32 *in, u32 inlen) +{ + u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {}; + + MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE); + MLX5_SET(modify_vhca_state_in, in, function_id, function_id); + MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, ecpu); + + return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); +} + +int mlx5_modify_vhca_sw_id(struct mlx5_core_dev *dev, u16 function_id, bool ecpu, u32 sw_fn_id) +{ + u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {}; + u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {}; + + MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE); + MLX5_SET(modify_vhca_state_in, in, function_id, function_id); + MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, ecpu); + MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.sw_function_id, 1); + MLX5_SET(modify_vhca_state_in, in, vhca_state_context.sw_function_id, sw_fn_id); + + return mlx5_cmd_exec_inout(dev, modify_vhca_state, in, out); +} + +int mlx5_vhca_event_arm(struct mlx5_core_dev *dev, u16 function_id, bool ecpu) +{ + u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {}; + + MLX5_SET(modify_vhca_state_in, in, vhca_state_context.arm_change_event, 1); + MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.arm_change_event, 1); + + return mlx5_cmd_modify_vhca_state(dev, function_id, ecpu, in, sizeof(in)); +} + +static void +mlx5_vhca_event_notify(struct mlx5_core_dev *dev, struct mlx5_vhca_state_event *event) +{ + u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {}; + int err; + + err = mlx5_cmd_query_vhca_state(dev, event->function_id, event->ecpu, out, sizeof(out)); + if (err) + return; + + event->sw_function_id = MLX5_GET(query_vhca_state_out, out, + vhca_state_context.sw_function_id); + event->new_vhca_state = MLX5_GET(query_vhca_state_out, out, + vhca_state_context.vhca_state); + + mlx5_vhca_event_arm(dev, event->function_id, event->ecpu); + + blocking_notifier_call_chain(&dev->priv.vhca_state_notifier->n_head, 0, event); +} + +static void mlx5_vhca_state_work_handler(struct work_struct *_work) +{ + struct mlx5_vhca_event_work *work = container_of(_work, struct mlx5_vhca_event_work, work); + struct mlx5_vhca_state_notifier *notifier = work->notifier; + struct mlx5_core_dev *dev = notifier->dev; + + mlx5_vhca_event_notify(dev, &work->event); +} + +static int +mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, void *data) +{ + struct mlx5_vhca_state_notifier *notifier = + mlx5_nb_cof(nb, struct mlx5_vhca_state_notifier, nb); + struct mlx5_vhca_event_work *work; + struct mlx5_eqe *eqe = data; + + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return NOTIFY_DONE; + INIT_WORK(&work->work, &mlx5_vhca_state_work_handler); + work->notifier = notifier; + work->event.function_id = be16_to_cpu(eqe->data.vhca_state.function_id); + work->event.ecpu = be16_to_cpu(eqe->data.vhca_state.ec_function); + mlx5_events_work_enqueue(notifier->dev, &work->work); + return NOTIFY_OK; +} + +void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap) +{ + if (!mlx5_vhca_event_supported(dev)) + return; + + MLX5_SET(cmd_hca_cap, set_hca_cap, vhca_state, 1); + MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_allocated, 1); + MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_active, 1); + MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_in_use, 1); + MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_teardown_request, 1); +} + +int mlx5_vhca_event_init(struct mlx5_core_dev *dev) +{ + struct mlx5_vhca_state_notifier *notifier; + + if (!mlx5_vhca_event_supported(dev)) + return 0; + + notifier = kzalloc(sizeof(*notifier), GFP_KERNEL); + if (!notifier) + return -ENOMEM; + + dev->priv.vhca_state_notifier = notifier; + notifier->dev = dev; + BLOCKING_INIT_NOTIFIER_HEAD(¬ifier->n_head); + MLX5_NB_INIT(¬ifier->nb, mlx5_vhca_state_change_notifier, VHCA_STATE_CHANGE); + return 0; +} + +void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev) +{ + if (!mlx5_vhca_event_supported(dev)) + return; + + kfree(dev->priv.vhca_state_notifier); + dev->priv.vhca_state_notifier = NULL; +} + +void mlx5_vhca_event_start(struct mlx5_core_dev *dev) +{ + struct mlx5_vhca_state_notifier *notifier; + + if (!dev->priv.vhca_state_notifier) + return; + + notifier = dev->priv.vhca_state_notifier; + mlx5_eq_notifier_register(dev, ¬ifier->nb); +} + +void mlx5_vhca_event_stop(struct mlx5_core_dev *dev) +{ + struct mlx5_vhca_state_notifier *notifier; + + if (!dev->priv.vhca_state_notifier) + return; + + notifier = dev->priv.vhca_state_notifier; + mlx5_eq_notifier_unregister(dev, ¬ifier->nb); +} + +int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb) +{ + if (!dev->priv.vhca_state_notifier) + return -EOPNOTSUPP; + return blocking_notifier_chain_register(&dev->priv.vhca_state_notifier->n_head, nb); +} + +void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&dev->priv.vhca_state_notifier->n_head, nb); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h new file mode 100644 index 000000000000..1fe1ec6f4d4b --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020 Mellanox Technologies Ltd */ + +#ifndef __MLX5_VHCA_EVENT_H__ +#define __MLX5_VHCA_EVENT_H__ + +#ifdef CONFIG_MLX5_SF + +struct mlx5_vhca_state_event { + u16 function_id; + u16 sw_function_id; + u8 new_vhca_state; + bool ecpu; +}; + +static inline bool mlx5_vhca_event_supported(const struct mlx5_core_dev *dev) +{ + return MLX5_CAP_GEN_MAX(dev, vhca_state); +} + +void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap); +int mlx5_vhca_event_init(struct mlx5_core_dev *dev); +void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev); +void mlx5_vhca_event_start(struct mlx5_core_dev *dev); +void mlx5_vhca_event_stop(struct mlx5_core_dev *dev); +int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb); +void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb); +int mlx5_modify_vhca_sw_id(struct mlx5_core_dev *dev, u16 function_id, bool ecpu, u32 sw_fn_id); +int mlx5_vhca_event_arm(struct mlx5_core_dev *dev, u16 function_id, bool ecpu); +int mlx5_cmd_query_vhca_state(struct mlx5_core_dev *dev, u16 function_id, + bool ecpu, u32 *out, u32 outlen); +#else + +static inline void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap) +{ +} + +static inline int mlx5_vhca_event_init(struct mlx5_core_dev *dev) +{ + return 0; +} + +static inline void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev) +{ +} + +static inline void mlx5_vhca_event_start(struct mlx5_core_dev *dev) +{ +} + +static inline void mlx5_vhca_event_stop(struct mlx5_core_dev *dev) +{ +} + +#endif + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index df1363a34a42..27c2b8416d02 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -218,158 +218,6 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] }, }; -struct dr_action_modify_field_conv { - u16 hw_field; - u8 start; - u8 end; - u8 l3_type; - u8 l4_type; -}; - -static const struct dr_action_modify_field_conv dr_action_conv_arr[] = { - [MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_1, .start = 16, .end = 47, - }, - [MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_1, .start = 0, .end = 15, - }, - [MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_2, .start = 32, .end = 47, - }, - [MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_0, .start = 16, .end = 47, - }, - [MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_0, .start = 0, .end = 15, - }, - [MLX5_ACTION_IN_FIELD_OUT_IP_DSCP] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_1, .start = 0, .end = 5, - }, - [MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 48, .end = 56, - .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP, - }, - [MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 0, .end = 15, - .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP, - }, - [MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 16, .end = 31, - .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP, - }, - [MLX5_ACTION_IN_FIELD_OUT_IP_TTL] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_1, .start = 8, .end = 15, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4, - }, - [MLX5_ACTION_IN_FIELD_OUT_IPV6_HOPLIMIT] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_1, .start = 8, .end = 15, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 0, .end = 15, - .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_UDP, - }, - [MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 16, .end = 31, - .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_UDP, - }, - [MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_3, .start = 32, .end = 63, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_3, .start = 0, .end = 31, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_4, .start = 32, .end = 63, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_4, .start = 0, .end = 31, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 32, .end = 63, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 0, .end = 31, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_2, .start = 32, .end = 63, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_2, .start = 0, .end = 31, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6, - }, - [MLX5_ACTION_IN_FIELD_OUT_SIPV4] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 0, .end = 31, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4, - }, - [MLX5_ACTION_IN_FIELD_OUT_DIPV4] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 32, .end = 63, - .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_A] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_METADATA, .start = 0, .end = 31, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_B] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_METADATA, .start = 32, .end = 63, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_C_0] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_0, .start = 32, .end = 63, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_C_1] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_0, .start = 0, .end = 31, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_C_2] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_1, .start = 32, .end = 63, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_C_3] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_1, .start = 0, .end = 31, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_C_4] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_2, .start = 32, .end = 63, - }, - [MLX5_ACTION_IN_FIELD_METADATA_REG_C_5] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_2, .start = 0, .end = 31, - }, - [MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_1, .start = 32, .end = 63, - }, - [MLX5_ACTION_IN_FIELD_OUT_TCP_ACK_NUM] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_1, .start = 0, .end = 31, - }, - [MLX5_ACTION_IN_FIELD_OUT_FIRST_VID] = { - .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_2, .start = 0, .end = 15, - }, -}; - -#define MAX_VLANS 2 -struct dr_action_vlan_info { - int count; - u32 headers[MAX_VLANS]; -}; - -struct dr_action_apply_attr { - u32 modify_index; - u16 modify_actions; - u32 decap_index; - u16 decap_actions; - u8 decap_with_vlan:1; - u64 final_icm_addr; - u32 flow_tag; - u32 ctr_id; - u16 gvmi; - u16 hit_gvmi; - u32 reformat_id; - u32 reformat_size; - struct dr_action_vlan_info vlans; -}; - static int dr_action_reformat_to_action_type(enum mlx5dr_action_reformat_type reformat_type, enum mlx5dr_action_type *action_type) @@ -394,141 +242,6 @@ dr_action_reformat_to_action_type(enum mlx5dr_action_reformat_type reformat_type return 0; } -static void dr_actions_init_next_ste(u8 **last_ste, - u32 *added_stes, - enum mlx5dr_ste_entry_type entry_type, - u16 gvmi) -{ - (*added_stes)++; - *last_ste += DR_STE_SIZE; - mlx5dr_ste_init(*last_ste, MLX5DR_STE_LU_TYPE_DONT_CARE, entry_type, gvmi); -} - -static void dr_actions_apply_tx(struct mlx5dr_domain *dmn, - u8 *action_type_set, - u8 *last_ste, - struct dr_action_apply_attr *attr, - u32 *added_stes) -{ - bool encap = action_type_set[DR_ACTION_TYP_L2_TO_TNL_L2] || - action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]; - - /* We want to make sure the modify header comes before L2 - * encapsulation. The reason for that is that we support - * modify headers for outer headers only - */ - if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) { - mlx5dr_ste_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT); - mlx5dr_ste_set_rewrite_actions(last_ste, - attr->modify_actions, - attr->modify_index); - } - - if (action_type_set[DR_ACTION_TYP_PUSH_VLAN]) { - int i; - - for (i = 0; i < attr->vlans.count; i++) { - if (i || action_type_set[DR_ACTION_TYP_MODIFY_HDR]) - dr_actions_init_next_ste(&last_ste, - added_stes, - MLX5DR_STE_TYPE_TX, - attr->gvmi); - - mlx5dr_ste_set_tx_push_vlan(last_ste, - attr->vlans.headers[i], - encap); - } - } - - if (encap) { - /* Modify header and encapsulation require a different STEs. - * Since modify header STE format doesn't support encapsulation - * tunneling_action. - */ - if (action_type_set[DR_ACTION_TYP_MODIFY_HDR] || - action_type_set[DR_ACTION_TYP_PUSH_VLAN]) - dr_actions_init_next_ste(&last_ste, - added_stes, - MLX5DR_STE_TYPE_TX, - attr->gvmi); - - mlx5dr_ste_set_tx_encap(last_ste, - attr->reformat_id, - attr->reformat_size, - action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]); - /* Whenever prio_tag_required enabled, we can be sure that the - * previous table (ACL) already push vlan to our packet, - * And due to HW limitation we need to set this bit, otherwise - * push vlan + reformat will not work. - */ - if (MLX5_CAP_GEN(dmn->mdev, prio_tag_required)) - mlx5dr_ste_set_go_back_bit(last_ste); - } - - if (action_type_set[DR_ACTION_TYP_CTR]) - mlx5dr_ste_set_counter_id(last_ste, attr->ctr_id); -} - -static void dr_actions_apply_rx(u8 *action_type_set, - u8 *last_ste, - struct dr_action_apply_attr *attr, - u32 *added_stes) -{ - if (action_type_set[DR_ACTION_TYP_CTR]) - mlx5dr_ste_set_counter_id(last_ste, attr->ctr_id); - - if (action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2]) { - mlx5dr_ste_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT); - mlx5dr_ste_set_rx_decap_l3(last_ste, attr->decap_with_vlan); - mlx5dr_ste_set_rewrite_actions(last_ste, - attr->decap_actions, - attr->decap_index); - } - - if (action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2]) - mlx5dr_ste_set_rx_decap(last_ste); - - if (action_type_set[DR_ACTION_TYP_POP_VLAN]) { - int i; - - for (i = 0; i < attr->vlans.count; i++) { - if (i || - action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2] || - action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2]) - dr_actions_init_next_ste(&last_ste, - added_stes, - MLX5DR_STE_TYPE_RX, - attr->gvmi); - - mlx5dr_ste_set_rx_pop_vlan(last_ste); - } - } - - if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) { - if (mlx5dr_ste_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT) - dr_actions_init_next_ste(&last_ste, - added_stes, - MLX5DR_STE_TYPE_MODIFY_PKT, - attr->gvmi); - else - mlx5dr_ste_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT); - - mlx5dr_ste_set_rewrite_actions(last_ste, - attr->modify_actions, - attr->modify_index); - } - - if (action_type_set[DR_ACTION_TYP_TAG]) { - if (mlx5dr_ste_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT) - dr_actions_init_next_ste(&last_ste, - added_stes, - MLX5DR_STE_TYPE_RX, - attr->gvmi); - - mlx5dr_ste_rx_set_flow_tag(last_ste, attr->flow_tag); - } -} - /* Apply the actions on the rule STE array starting from the last_ste. * Actions might require more than one STE, new_num_stes will return * the new size of the STEs array, rule with actions. @@ -537,21 +250,20 @@ static void dr_actions_apply(struct mlx5dr_domain *dmn, enum mlx5dr_ste_entry_type ste_type, u8 *action_type_set, u8 *last_ste, - struct dr_action_apply_attr *attr, + struct mlx5dr_ste_actions_attr *attr, u32 *new_num_stes) { + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; u32 added_stes = 0; if (ste_type == MLX5DR_STE_TYPE_RX) - dr_actions_apply_rx(action_type_set, last_ste, attr, &added_stes); + mlx5dr_ste_set_actions_rx(ste_ctx, dmn, action_type_set, + last_ste, attr, &added_stes); else - dr_actions_apply_tx(dmn, action_type_set, last_ste, attr, &added_stes); + mlx5dr_ste_set_actions_tx(ste_ctx, dmn, action_type_set, + last_ste, attr, &added_stes); - last_ste += added_stes * DR_STE_SIZE; *new_num_stes += added_stes; - - mlx5dr_ste_set_hit_gvmi(last_ste, attr->hit_gvmi); - mlx5dr_ste_set_hit_addr(last_ste, attr->final_icm_addr, 1); } static enum dr_action_domain @@ -643,9 +355,9 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, bool rx_rule = nic_dmn->ste_type == MLX5DR_STE_TYPE_RX; struct mlx5dr_domain *dmn = matcher->tbl->dmn; u8 action_type_set[DR_ACTION_TYP_MAX] = {}; + struct mlx5dr_ste_actions_attr attr = {}; struct mlx5dr_action *dest_action = NULL; u32 state = DR_ACTION_STATE_NO_ACTION; - struct dr_action_apply_attr attr = {}; enum dr_action_domain action_domain; bool recalc_cs_required = false; u8 *last_ste; @@ -756,12 +468,12 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, } break; case DR_ACTION_TYP_POP_VLAN: - max_actions_type = MAX_VLANS; + max_actions_type = MLX5DR_MAX_VLANS; attr.vlans.count++; break; case DR_ACTION_TYP_PUSH_VLAN: - max_actions_type = MAX_VLANS; - if (attr.vlans.count == MAX_VLANS) + max_actions_type = MLX5DR_MAX_VLANS; + if (attr.vlans.count == MLX5DR_MAX_VLANS) return -EINVAL; attr.vlans.headers[attr.vlans.count++] = action->push_vlan.vlan_hdr; @@ -817,132 +529,6 @@ out_invalid_arg: return -EINVAL; } -#define CVLAN_ETHERTYPE 0x8100 -#define SVLAN_ETHERTYPE 0x88a8 -#define HDR_LEN_L2_ONLY 14 -#define HDR_LEN_L2_VLAN 18 -#define REWRITE_HW_ACTION_NUM 6 - -static int dr_actions_l2_rewrite(struct mlx5dr_domain *dmn, - struct mlx5dr_action *action, - void *data, size_t data_sz) -{ - struct mlx5_ifc_l2_hdr_bits *l2_hdr = data; - u64 ops[REWRITE_HW_ACTION_NUM] = {}; - u32 hdr_fld_4b; - u16 hdr_fld_2b; - u16 vlan_type; - bool vlan; - int i = 0; - int ret; - - vlan = (data_sz != HDR_LEN_L2_ONLY); - - /* dmac_47_16 */ - MLX5_SET(dr_action_hw_set, ops + i, - opcode, MLX5DR_ACTION_MDFY_HW_OP_SET); - MLX5_SET(dr_action_hw_set, ops + i, - destination_length, 0); - MLX5_SET(dr_action_hw_set, ops + i, - destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_0); - MLX5_SET(dr_action_hw_set, ops + i, - destination_left_shifter, 16); - hdr_fld_4b = MLX5_GET(l2_hdr, l2_hdr, dmac_47_16); - MLX5_SET(dr_action_hw_set, ops + i, - inline_data, hdr_fld_4b); - i++; - - /* smac_47_16 */ - MLX5_SET(dr_action_hw_set, ops + i, - opcode, MLX5DR_ACTION_MDFY_HW_OP_SET); - MLX5_SET(dr_action_hw_set, ops + i, - destination_length, 0); - MLX5_SET(dr_action_hw_set, ops + i, - destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_1); - MLX5_SET(dr_action_hw_set, ops + i, - destination_left_shifter, 16); - hdr_fld_4b = (MLX5_GET(l2_hdr, l2_hdr, smac_31_0) >> 16 | - MLX5_GET(l2_hdr, l2_hdr, smac_47_32) << 16); - MLX5_SET(dr_action_hw_set, ops + i, - inline_data, hdr_fld_4b); - i++; - - /* dmac_15_0 */ - MLX5_SET(dr_action_hw_set, ops + i, - opcode, MLX5DR_ACTION_MDFY_HW_OP_SET); - MLX5_SET(dr_action_hw_set, ops + i, - destination_length, 16); - MLX5_SET(dr_action_hw_set, ops + i, - destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_0); - MLX5_SET(dr_action_hw_set, ops + i, - destination_left_shifter, 0); - hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, dmac_15_0); - MLX5_SET(dr_action_hw_set, ops + i, - inline_data, hdr_fld_2b); - i++; - - /* ethertype + (optional) vlan */ - MLX5_SET(dr_action_hw_set, ops + i, - opcode, MLX5DR_ACTION_MDFY_HW_OP_SET); - MLX5_SET(dr_action_hw_set, ops + i, - destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_2); - MLX5_SET(dr_action_hw_set, ops + i, - destination_left_shifter, 32); - if (!vlan) { - hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype); - MLX5_SET(dr_action_hw_set, ops + i, inline_data, hdr_fld_2b); - MLX5_SET(dr_action_hw_set, ops + i, destination_length, 16); - } else { - hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype); - vlan_type = hdr_fld_2b == SVLAN_ETHERTYPE ? DR_STE_SVLAN : DR_STE_CVLAN; - hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan); - hdr_fld_4b = (vlan_type << 16) | hdr_fld_2b; - MLX5_SET(dr_action_hw_set, ops + i, inline_data, hdr_fld_4b); - MLX5_SET(dr_action_hw_set, ops + i, destination_length, 18); - } - i++; - - /* smac_15_0 */ - MLX5_SET(dr_action_hw_set, ops + i, - opcode, MLX5DR_ACTION_MDFY_HW_OP_SET); - MLX5_SET(dr_action_hw_set, ops + i, - destination_length, 16); - MLX5_SET(dr_action_hw_set, ops + i, - destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_1); - MLX5_SET(dr_action_hw_set, ops + i, - destination_left_shifter, 0); - hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, smac_31_0); - MLX5_SET(dr_action_hw_set, ops + i, - inline_data, hdr_fld_2b); - i++; - - if (vlan) { - MLX5_SET(dr_action_hw_set, ops + i, - opcode, MLX5DR_ACTION_MDFY_HW_OP_SET); - hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan_type); - MLX5_SET(dr_action_hw_set, ops + i, - inline_data, hdr_fld_2b); - MLX5_SET(dr_action_hw_set, ops + i, - destination_length, 16); - MLX5_SET(dr_action_hw_set, ops + i, - destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_2); - MLX5_SET(dr_action_hw_set, ops + i, - destination_left_shifter, 0); - i++; - } - - action->rewrite.data = (void *)ops; - action->rewrite.num_of_actions = i; - - ret = mlx5dr_send_postsend_action(dmn, action); - if (ret) { - mlx5dr_dbg(dmn, "Writing encapsulation action to ICM failed\n"); - return ret; - } - - return 0; -} - static struct mlx5dr_action * dr_action_create_generic(enum mlx5dr_action_type action_type) { @@ -1217,21 +803,34 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, } case DR_ACTION_TYP_TNL_L3_TO_L2: { - /* Only Ethernet frame is supported, with VLAN (18) or without (14) */ - if (data_sz != HDR_LEN_L2_ONLY && data_sz != HDR_LEN_L2_VLAN) - return -EINVAL; + u8 hw_actions[ACTION_CACHE_LINE_SIZE] = {}; + int ret; + + ret = mlx5dr_ste_set_action_decap_l3_list(dmn->ste_ctx, + data, data_sz, + hw_actions, + ACTION_CACHE_LINE_SIZE, + &action->rewrite.num_of_actions); + if (ret) { + mlx5dr_dbg(dmn, "Failed creating decap l3 action list\n"); + return ret; + } action->rewrite.chunk = mlx5dr_icm_alloc_chunk(dmn->action_icm_pool, DR_CHUNK_SIZE_8); - if (!action->rewrite.chunk) + if (!action->rewrite.chunk) { + mlx5dr_dbg(dmn, "Failed allocating modify header chunk\n"); return -ENOMEM; + } + action->rewrite.data = (void *)hw_actions; action->rewrite.index = (action->rewrite.chunk->icm_addr - dmn->info.caps.hdr_modify_icm_addr) / ACTION_CACHE_LINE_SIZE; - ret = dr_actions_l2_rewrite(dmn, action, data, data_sz); + ret = mlx5dr_send_postsend_action(dmn, action); if (ret) { + mlx5dr_dbg(dmn, "Writing decap l3 actions to ICM failed\n"); mlx5dr_icm_free_chunk(action->rewrite.chunk); return ret; } @@ -1243,6 +842,9 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, } } +#define CVLAN_ETHERTYPE 0x8100 +#define SVLAN_ETHERTYPE 0x88a8 + struct mlx5dr_action *mlx5dr_action_create_pop_vlan(void) { return dr_action_create_generic(DR_ACTION_TYP_POP_VLAN); @@ -1315,31 +917,13 @@ dec_ref: return NULL; } -static const struct dr_action_modify_field_conv * -dr_action_modify_get_hw_info(u16 sw_field) -{ - const struct dr_action_modify_field_conv *hw_action_info; - - if (sw_field >= ARRAY_SIZE(dr_action_conv_arr)) - goto not_found; - - hw_action_info = &dr_action_conv_arr[sw_field]; - if (!hw_action_info->end && !hw_action_info->start) - goto not_found; - - return hw_action_info; - -not_found: - return NULL; -} - static int dr_action_modify_sw_to_hw_add(struct mlx5dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, - const struct dr_action_modify_field_conv **ret_hw_info) + const struct mlx5dr_ste_action_modify_field **ret_hw_info) { - const struct dr_action_modify_field_conv *hw_action_info; + const struct mlx5dr_ste_action_modify_field *hw_action_info; u8 max_length; u16 sw_field; u32 data; @@ -1349,7 +933,7 @@ dr_action_modify_sw_to_hw_add(struct mlx5dr_domain *dmn, data = MLX5_GET(set_action_in, sw_action, data); /* Convert SW data to HW modify action format */ - hw_action_info = dr_action_modify_get_hw_info(sw_field); + hw_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, sw_field); if (!hw_action_info) { mlx5dr_dbg(dmn, "Modify add action invalid field given\n"); return -EINVAL; @@ -1357,20 +941,12 @@ dr_action_modify_sw_to_hw_add(struct mlx5dr_domain *dmn, max_length = hw_action_info->end - hw_action_info->start + 1; - MLX5_SET(dr_action_hw_set, hw_action, - opcode, MLX5DR_ACTION_MDFY_HW_OP_ADD); - - MLX5_SET(dr_action_hw_set, hw_action, destination_field_code, - hw_action_info->hw_field); - - MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, - hw_action_info->start); - - /* PRM defines that length zero specific length of 32bits */ - MLX5_SET(dr_action_hw_set, hw_action, destination_length, - max_length == 32 ? 0 : max_length); - - MLX5_SET(dr_action_hw_set, hw_action, inline_data, data); + mlx5dr_ste_set_action_add(dmn->ste_ctx, + hw_action, + hw_action_info->hw_field, + hw_action_info->start, + max_length, + data); *ret_hw_info = hw_action_info; @@ -1381,9 +957,9 @@ static int dr_action_modify_sw_to_hw_set(struct mlx5dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, - const struct dr_action_modify_field_conv **ret_hw_info) + const struct mlx5dr_ste_action_modify_field **ret_hw_info) { - const struct dr_action_modify_field_conv *hw_action_info; + const struct mlx5dr_ste_action_modify_field *hw_action_info; u8 offset, length, max_length; u16 sw_field; u32 data; @@ -1395,7 +971,7 @@ dr_action_modify_sw_to_hw_set(struct mlx5dr_domain *dmn, data = MLX5_GET(set_action_in, sw_action, data); /* Convert SW data to HW modify action format */ - hw_action_info = dr_action_modify_get_hw_info(sw_field); + hw_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, sw_field); if (!hw_action_info) { mlx5dr_dbg(dmn, "Modify set action invalid field given\n"); return -EINVAL; @@ -1411,19 +987,12 @@ dr_action_modify_sw_to_hw_set(struct mlx5dr_domain *dmn, return -EINVAL; } - MLX5_SET(dr_action_hw_set, hw_action, - opcode, MLX5DR_ACTION_MDFY_HW_OP_SET); - - MLX5_SET(dr_action_hw_set, hw_action, destination_field_code, - hw_action_info->hw_field); - - MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, - hw_action_info->start + offset); - - MLX5_SET(dr_action_hw_set, hw_action, destination_length, - length == 32 ? 0 : length); - - MLX5_SET(dr_action_hw_set, hw_action, inline_data, data); + mlx5dr_ste_set_action_set(dmn->ste_ctx, + hw_action, + hw_action_info->hw_field, + hw_action_info->start + offset, + length, + data); *ret_hw_info = hw_action_info; @@ -1434,12 +1003,12 @@ static int dr_action_modify_sw_to_hw_copy(struct mlx5dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, - const struct dr_action_modify_field_conv **ret_dst_hw_info, - const struct dr_action_modify_field_conv **ret_src_hw_info) + const struct mlx5dr_ste_action_modify_field **ret_dst_hw_info, + const struct mlx5dr_ste_action_modify_field **ret_src_hw_info) { u8 src_offset, dst_offset, src_max_length, dst_max_length, length; - const struct dr_action_modify_field_conv *hw_dst_action_info; - const struct dr_action_modify_field_conv *hw_src_action_info; + const struct mlx5dr_ste_action_modify_field *hw_dst_action_info; + const struct mlx5dr_ste_action_modify_field *hw_src_action_info; u16 src_field, dst_field; /* Get SW modify action data */ @@ -1450,8 +1019,8 @@ dr_action_modify_sw_to_hw_copy(struct mlx5dr_domain *dmn, length = MLX5_GET(copy_action_in, sw_action, length); /* Convert SW data to HW modify action format */ - hw_src_action_info = dr_action_modify_get_hw_info(src_field); - hw_dst_action_info = dr_action_modify_get_hw_info(dst_field); + hw_src_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, src_field); + hw_dst_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, dst_field); if (!hw_src_action_info || !hw_dst_action_info) { mlx5dr_dbg(dmn, "Modify copy action invalid field given\n"); return -EINVAL; @@ -1471,23 +1040,13 @@ dr_action_modify_sw_to_hw_copy(struct mlx5dr_domain *dmn, return -EINVAL; } - MLX5_SET(dr_action_hw_copy, hw_action, - opcode, MLX5DR_ACTION_MDFY_HW_OP_COPY); - - MLX5_SET(dr_action_hw_copy, hw_action, destination_field_code, - hw_dst_action_info->hw_field); - - MLX5_SET(dr_action_hw_copy, hw_action, destination_left_shifter, - hw_dst_action_info->start + dst_offset); - - MLX5_SET(dr_action_hw_copy, hw_action, destination_length, - length == 32 ? 0 : length); - - MLX5_SET(dr_action_hw_copy, hw_action, source_field_code, - hw_src_action_info->hw_field); - - MLX5_SET(dr_action_hw_copy, hw_action, source_left_shifter, - hw_src_action_info->start + dst_offset); + mlx5dr_ste_set_action_copy(dmn->ste_ctx, + hw_action, + hw_dst_action_info->hw_field, + hw_dst_action_info->start + dst_offset, + length, + hw_src_action_info->hw_field, + hw_src_action_info->start + src_offset); *ret_dst_hw_info = hw_dst_action_info; *ret_src_hw_info = hw_src_action_info; @@ -1499,8 +1058,8 @@ static int dr_action_modify_sw_to_hw(struct mlx5dr_domain *dmn, __be64 *sw_action, __be64 *hw_action, - const struct dr_action_modify_field_conv **ret_dst_hw_info, - const struct dr_action_modify_field_conv **ret_src_hw_info) + const struct mlx5dr_ste_action_modify_field **ret_dst_hw_info, + const struct mlx5dr_ste_action_modify_field **ret_src_hw_info) { u8 action; int ret; @@ -1677,15 +1236,15 @@ static int dr_actions_convert_modify_header(struct mlx5dr_action *action, u32 *num_hw_actions, bool *modify_ttl) { - const struct dr_action_modify_field_conv *hw_dst_action_info; - const struct dr_action_modify_field_conv *hw_src_action_info; - u16 hw_field = MLX5DR_ACTION_MDFY_HW_FLD_RESERVED; - u32 l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_NONE; - u32 l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_NONE; + const struct mlx5dr_ste_action_modify_field *hw_dst_action_info; + const struct mlx5dr_ste_action_modify_field *hw_src_action_info; struct mlx5dr_domain *dmn = action->rewrite.dmn; int ret, i, hw_idx = 0; __be64 *sw_action; __be64 hw_action; + u16 hw_field = 0; + u32 l3_type = 0; + u32 l4_type = 0; *modify_ttl = false; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index aa2c2d6c44e6..47ec88964bf3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -57,6 +57,12 @@ static int dr_domain_init_resources(struct mlx5dr_domain *dmn) { int ret; + dmn->ste_ctx = mlx5dr_ste_get_ctx(dmn->info.caps.sw_format_ver); + if (!dmn->ste_ctx) { + mlx5dr_err(dmn, "SW Steering on this device is unsupported\n"); + return -EOPNOTSUPP; + } + ret = mlx5_core_alloc_pd(dmn->mdev, &dmn->pdn); if (ret) { mlx5dr_err(dmn, "Couldn't allocate PD, ret: %d", ret); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c index 6527eb4df153..e3a002983c26 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c @@ -221,6 +221,7 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher, { struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn; struct mlx5dr_domain *dmn = matcher->tbl->dmn; + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_match_param mask = {}; struct mlx5dr_ste_build *sb; bool inner, rx; @@ -259,80 +260,89 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher, inner = false; if (dr_mask_is_wqe_metadata_set(&mask.misc2)) - mlx5dr_ste_build_general_purpose(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_general_purpose(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_reg_c_0_3_set(&mask.misc2)) - mlx5dr_ste_build_register_0(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_register_0(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_reg_c_4_7_set(&mask.misc2)) - mlx5dr_ste_build_register_1(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_register_1(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_gvmi_or_qpn_set(&mask.misc) && (dmn->type == MLX5DR_DOMAIN_TYPE_FDB || dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX)) { - mlx5dr_ste_build_src_gvmi_qpn(&sb[idx++], &mask, - dmn, inner, rx); + mlx5dr_ste_build_src_gvmi_qpn(ste_ctx, &sb[idx++], + &mask, dmn, inner, rx); } if (dr_mask_is_smac_set(&mask.outer) && dr_mask_is_dmac_set(&mask.outer)) { - mlx5dr_ste_build_eth_l2_src_dst(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l2_src_dst(ste_ctx, &sb[idx++], + &mask, inner, rx); } if (dr_mask_is_smac_set(&mask.outer)) - mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_eth_l2_src(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_L2_DST(mask.outer, mask.misc, outer)) - mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_eth_l2_dst(ste_ctx, &sb[idx++], + &mask, inner, rx); if (outer_ipv == DR_RULE_IPV6) { if (dr_mask_is_dst_addr_set(&mask.outer)) - mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv6_dst(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_src_addr_set(&mask.outer)) - mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv6_src(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_ETH_L4_SET(mask.outer, mask.misc, outer)) - mlx5dr_ste_build_eth_ipv6_l3_l4(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_ipv6_l3_l4(ste_ctx, &sb[idx++], + &mask, inner, rx); } else { if (dr_mask_is_ipv4_5_tuple_set(&mask.outer)) - mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv4_5_tuple(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_ttl_set(&mask.outer)) - mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv4_misc(ste_ctx, &sb[idx++], + &mask, inner, rx); } if (dr_mask_is_tnl_vxlan_gpe(&mask, dmn)) - mlx5dr_ste_build_tnl_vxlan_gpe(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_tnl_vxlan_gpe(ste_ctx, &sb[idx++], + &mask, inner, rx); else if (dr_mask_is_tnl_geneve(&mask, dmn)) - mlx5dr_ste_build_tnl_geneve(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_tnl_geneve(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, outer)) - mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_eth_l4_misc(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, outer)) - mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_mpls(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_TNL_MPLS_SET(mask.misc2)) - mlx5dr_ste_build_tnl_mpls(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_tnl_mpls(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_icmp(&mask, dmn)) { - ret = mlx5dr_ste_build_icmp(&sb[idx++], + ret = mlx5dr_ste_build_icmp(ste_ctx, &sb[idx++], &mask, &dmn->info.caps, inner, rx); if (ret) return ret; } if (dr_mask_is_tnl_gre_set(&mask.misc)) - mlx5dr_ste_build_tnl_gre(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_tnl_gre(ste_ctx, &sb[idx++], + &mask, inner, rx); } /* Inner */ @@ -343,50 +353,56 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher, inner = true; if (dr_mask_is_eth_l2_tnl_set(&mask.misc)) - mlx5dr_ste_build_eth_l2_tnl(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_eth_l2_tnl(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_smac_set(&mask.inner) && dr_mask_is_dmac_set(&mask.inner)) { - mlx5dr_ste_build_eth_l2_src_dst(&sb[idx++], + mlx5dr_ste_build_eth_l2_src_dst(ste_ctx, &sb[idx++], &mask, inner, rx); } if (dr_mask_is_smac_set(&mask.inner)) - mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_eth_l2_src(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_L2_DST(mask.inner, mask.misc, inner)) - mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_eth_l2_dst(ste_ctx, &sb[idx++], + &mask, inner, rx); if (inner_ipv == DR_RULE_IPV6) { if (dr_mask_is_dst_addr_set(&mask.inner)) - mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv6_dst(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_src_addr_set(&mask.inner)) - mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv6_src(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_ETH_L4_SET(mask.inner, mask.misc, inner)) - mlx5dr_ste_build_eth_ipv6_l3_l4(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_ipv6_l3_l4(ste_ctx, &sb[idx++], + &mask, inner, rx); } else { if (dr_mask_is_ipv4_5_tuple_set(&mask.inner)) - mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv4_5_tuple(ste_ctx, &sb[idx++], + &mask, inner, rx); if (dr_mask_is_ttl_set(&mask.inner)) - mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask, - inner, rx); + mlx5dr_ste_build_eth_l3_ipv4_misc(ste_ctx, &sb[idx++], + &mask, inner, rx); } if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, inner)) - mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_eth_l4_misc(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, inner)) - mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_mpls(ste_ctx, &sb[idx++], + &mask, inner, rx); if (DR_MASK_IS_TNL_MPLS_SET(mask.misc2)) - mlx5dr_ste_build_tnl_mpls(&sb[idx++], &mask, inner, rx); + mlx5dr_ste_build_tnl_mpls(ste_ctx, &sb[idx++], + &mask, inner, rx); } /* Empty matcher, takes all */ if (matcher->match_criteria == DR_MATCHER_CRITERIA_EMPTY) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c index 6d73719db1f4..ddcb7017e121 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c @@ -10,7 +10,8 @@ struct mlx5dr_rule_action_member { struct list_head list; }; -static int dr_rule_append_to_miss_list(struct mlx5dr_ste *new_last_ste, +static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste *new_last_ste, struct list_head *miss_list, struct list_head *send_list) { @@ -25,7 +26,7 @@ static int dr_rule_append_to_miss_list(struct mlx5dr_ste *new_last_ste, if (!ste_info_last) return -ENOMEM; - mlx5dr_ste_set_miss_addr(last_ste->hw_ste, + mlx5dr_ste_set_miss_addr(ste_ctx, last_ste->hw_ste, mlx5dr_ste_get_icm_addr(new_last_ste)); list_add_tail(&new_last_ste->miss_list_node, miss_list); @@ -42,6 +43,7 @@ dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher, u8 *hw_ste) { struct mlx5dr_domain *dmn = matcher->tbl->dmn; + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_htbl *new_htbl; struct mlx5dr_ste *ste; @@ -57,7 +59,8 @@ dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher, /* One and only entry, never grows */ ste = new_htbl->ste_arr; - mlx5dr_ste_set_miss_addr(hw_ste, nic_matcher->e_anchor->chunk->icm_addr); + mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste, + nic_matcher->e_anchor->chunk->icm_addr); mlx5dr_htbl_get(new_htbl); return ste; @@ -169,6 +172,7 @@ dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher, struct mlx5dr_ste *col_ste, u8 *hw_ste) { + struct mlx5dr_domain *dmn = matcher->tbl->dmn; struct mlx5dr_ste *new_ste; int ret; @@ -180,11 +184,11 @@ dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher, new_ste->htbl->miss_list = mlx5dr_ste_get_miss_list(col_ste); /* Update the previous from the list */ - ret = dr_rule_append_to_miss_list(new_ste, + ret = dr_rule_append_to_miss_list(dmn->ste_ctx, new_ste, mlx5dr_ste_get_miss_list(col_ste), update_list); if (ret) { - mlx5dr_dbg(matcher->tbl->dmn, "Failed update dup entry\n"); + mlx5dr_dbg(dmn, "Failed update dup entry\n"); goto err_exit; } @@ -224,6 +228,7 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, struct mlx5dr_ste_htbl *new_htbl, struct list_head *update_list) { + struct mlx5dr_domain *dmn = matcher->tbl->dmn; struct mlx5dr_ste_send_info *ste_info; bool use_update_list = false; u8 hw_ste[DR_STE_SIZE] = {}; @@ -237,7 +242,8 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, /* Copy STE control and tag */ memcpy(hw_ste, cur_ste->hw_ste, DR_STE_SIZE_REDUCED); - mlx5dr_ste_set_miss_addr(hw_ste, nic_matcher->e_anchor->chunk->icm_addr); + mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, + nic_matcher->e_anchor->chunk->icm_addr); new_idx = mlx5dr_ste_calc_hash_index(hw_ste, new_htbl); new_ste = &new_htbl->ste_arr[new_idx]; @@ -253,7 +259,7 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, new_ste, hw_ste); if (!new_ste) { - mlx5dr_dbg(matcher->tbl->dmn, "Failed adding collision entry, index: %d\n", + mlx5dr_dbg(dmn, "Failed adding collision entry, index: %d\n", new_idx); return NULL; } @@ -391,7 +397,8 @@ dr_rule_rehash_htbl(struct mlx5dr_rule *rule, /* Write new table to HW */ info.type = CONNECT_MISS; info.miss_icm_addr = nic_matcher->e_anchor->chunk->icm_addr; - mlx5dr_ste_set_formatted_ste(dmn->info.caps.gvmi, + mlx5dr_ste_set_formatted_ste(dmn->ste_ctx, + dmn->info.caps.gvmi, nic_dmn, new_htbl, formatted_ste, @@ -436,13 +443,15 @@ dr_rule_rehash_htbl(struct mlx5dr_rule *rule, /* It is safe to operate dr_ste_set_hit_addr on the hw_ste here * (48B len) which works only on first 32B */ - mlx5dr_ste_set_hit_addr(prev_htbl->ste_arr[0].hw_ste, + mlx5dr_ste_set_hit_addr(dmn->ste_ctx, + prev_htbl->ste_arr[0].hw_ste, new_htbl->chunk->icm_addr, new_htbl->chunk->num_of_entries); ste_to_update = &prev_htbl->ste_arr[0]; } else { - mlx5dr_ste_set_hit_addr_by_next_htbl(cur_htbl->pointing_ste->hw_ste, + mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx, + cur_htbl->pointing_ste->hw_ste, new_htbl); ste_to_update = cur_htbl->pointing_ste; } @@ -496,6 +505,8 @@ dr_rule_handle_collision(struct mlx5dr_matcher *matcher, struct list_head *miss_list, struct list_head *send_list) { + struct mlx5dr_domain *dmn = matcher->tbl->dmn; + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_send_info *ste_info; struct mlx5dr_ste *new_ste; @@ -507,8 +518,9 @@ dr_rule_handle_collision(struct mlx5dr_matcher *matcher, if (!new_ste) goto free_send_info; - if (dr_rule_append_to_miss_list(new_ste, miss_list, send_list)) { - mlx5dr_dbg(matcher->tbl->dmn, "Failed to update prev miss_list\n"); + if (dr_rule_append_to_miss_list(ste_ctx, new_ste, + miss_list, send_list)) { + mlx5dr_dbg(dmn, "Failed to update prev miss_list\n"); goto err_exit; } @@ -659,6 +671,7 @@ static int dr_rule_handle_action_stes(struct mlx5dr_rule *rule, struct mlx5dr_ste_send_info *ste_info_arr[DR_ACTION_MAX_STES]; u8 num_of_builders = nic_matcher->num_of_builders; struct mlx5dr_matcher *matcher = rule->matcher; + struct mlx5dr_domain *dmn = matcher->tbl->dmn; u8 *curr_hw_ste, *prev_hw_ste; struct mlx5dr_ste *action_ste; int i, k, ret; @@ -692,10 +705,12 @@ static int dr_rule_handle_action_stes(struct mlx5dr_rule *rule, goto err_exit; /* Point current ste to the new action */ - mlx5dr_ste_set_hit_addr_by_next_htbl(prev_hw_ste, action_ste->htbl); + mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx, + prev_hw_ste, + action_ste->htbl); ret = dr_rule_add_member(nic_rule, action_ste); if (ret) { - mlx5dr_dbg(matcher->tbl->dmn, "Failed adding rule member\n"); + mlx5dr_dbg(dmn, "Failed adding rule member\n"); goto free_ste_info; } mlx5dr_send_fill_and_append_ste_send_info(action_ste, DR_STE_SIZE, 0, @@ -722,6 +737,7 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, struct list_head *miss_list, struct list_head *send_list) { + struct mlx5dr_domain *dmn = matcher->tbl->dmn; struct mlx5dr_ste_send_info *ste_info; /* Take ref on table, only on first time this ste is used */ @@ -730,7 +746,8 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, /* new entry -> new branch */ list_add_tail(&ste->miss_list_node, miss_list); - mlx5dr_ste_set_miss_addr(hw_ste, nic_matcher->e_anchor->chunk->icm_addr); + mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, + nic_matcher->e_anchor->chunk->icm_addr); ste->ste_chain_location = ste_location; @@ -743,7 +760,7 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, ste, hw_ste, DR_CHUNK_SIZE_1)) { - mlx5dr_dbg(matcher->tbl->dmn, "Failed allocating table\n"); + mlx5dr_dbg(dmn, "Failed allocating table\n"); goto clean_ste_info; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c index d275823bff2f..1614481fdf8d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c @@ -3,104 +3,7 @@ #include <linux/types.h> #include <linux/crc32.h> -#include "dr_types.h" - -#define DR_STE_CRC_POLY 0xEDB88320L -#define STE_IPV4 0x1 -#define STE_IPV6 0x2 -#define STE_TCP 0x1 -#define STE_UDP 0x2 -#define STE_SPI 0x3 -#define IP_VERSION_IPV4 0x4 -#define IP_VERSION_IPV6 0x6 -#define STE_SVLAN 0x1 -#define STE_CVLAN 0x2 - -#define DR_STE_ENABLE_FLOW_TAG BIT(31) - -/* Set to STE a specific value using DR_STE_SET */ -#define DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, value) do { \ - if ((spec)->s_fname) { \ - MLX5_SET(ste_##lookup_type, tag, t_fname, value); \ - (spec)->s_fname = 0; \ - } \ -} while (0) - -/* Set to STE spec->s_fname to tag->t_fname */ -#define DR_STE_SET_TAG(lookup_type, tag, t_fname, spec, s_fname) \ - DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, spec->s_fname) - -/* Set to STE -1 to bit_mask->bm_fname and set spec->s_fname as used */ -#define DR_STE_SET_MASK(lookup_type, bit_mask, bm_fname, spec, s_fname) \ - DR_STE_SET_VAL(lookup_type, bit_mask, bm_fname, spec, s_fname, -1) - -/* Set to STE spec->s_fname to bit_mask->bm_fname and set spec->s_fname as used */ -#define DR_STE_SET_MASK_V(lookup_type, bit_mask, bm_fname, spec, s_fname) \ - DR_STE_SET_VAL(lookup_type, bit_mask, bm_fname, spec, s_fname, (spec)->s_fname) - -#define DR_STE_SET_TCP_FLAGS(lookup_type, tag, spec) do { \ - MLX5_SET(ste_##lookup_type, tag, tcp_ns, !!((spec)->tcp_flags & (1 << 8))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_cwr, !!((spec)->tcp_flags & (1 << 7))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_ece, !!((spec)->tcp_flags & (1 << 6))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_urg, !!((spec)->tcp_flags & (1 << 5))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_ack, !!((spec)->tcp_flags & (1 << 4))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_psh, !!((spec)->tcp_flags & (1 << 3))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_rst, !!((spec)->tcp_flags & (1 << 2))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_syn, !!((spec)->tcp_flags & (1 << 1))); \ - MLX5_SET(ste_##lookup_type, tag, tcp_fin, !!((spec)->tcp_flags & (1 << 0))); \ -} while (0) - -#define DR_STE_SET_MPLS_MASK(lookup_type, mask, in_out, bit_mask) do { \ - DR_STE_SET_MASK_V(lookup_type, mask, mpls0_label, mask, \ - in_out##_first_mpls_label);\ - DR_STE_SET_MASK_V(lookup_type, mask, mpls0_s_bos, mask, \ - in_out##_first_mpls_s_bos); \ - DR_STE_SET_MASK_V(lookup_type, mask, mpls0_exp, mask, \ - in_out##_first_mpls_exp); \ - DR_STE_SET_MASK_V(lookup_type, mask, mpls0_ttl, mask, \ - in_out##_first_mpls_ttl); \ -} while (0) - -#define DR_STE_SET_MPLS_TAG(lookup_type, mask, in_out, tag) do { \ - DR_STE_SET_TAG(lookup_type, tag, mpls0_label, mask, \ - in_out##_first_mpls_label);\ - DR_STE_SET_TAG(lookup_type, tag, mpls0_s_bos, mask, \ - in_out##_first_mpls_s_bos); \ - DR_STE_SET_TAG(lookup_type, tag, mpls0_exp, mask, \ - in_out##_first_mpls_exp); \ - DR_STE_SET_TAG(lookup_type, tag, mpls0_ttl, mask, \ - in_out##_first_mpls_ttl); \ -} while (0) - -#define DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(_misc) (\ - (_misc)->outer_first_mpls_over_gre_label || \ - (_misc)->outer_first_mpls_over_gre_exp || \ - (_misc)->outer_first_mpls_over_gre_s_bos || \ - (_misc)->outer_first_mpls_over_gre_ttl) -#define DR_STE_IS_OUTER_MPLS_OVER_UDP_SET(_misc) (\ - (_misc)->outer_first_mpls_over_udp_label || \ - (_misc)->outer_first_mpls_over_udp_exp || \ - (_misc)->outer_first_mpls_over_udp_s_bos || \ - (_misc)->outer_first_mpls_over_udp_ttl) - -#define DR_STE_CALC_LU_TYPE(lookup_type, rx, inner) \ - ((inner) ? MLX5DR_STE_LU_TYPE_##lookup_type##_I : \ - (rx) ? MLX5DR_STE_LU_TYPE_##lookup_type##_D : \ - MLX5DR_STE_LU_TYPE_##lookup_type##_O) - -enum dr_ste_tunl_action { - DR_STE_TUNL_ACTION_NONE = 0, - DR_STE_TUNL_ACTION_ENABLE = 1, - DR_STE_TUNL_ACTION_DECAP = 2, - DR_STE_TUNL_ACTION_L3_DECAP = 3, - DR_STE_TUNL_ACTION_POP_VLAN = 4, -}; - -enum dr_ste_action_type { - DR_STE_ACTION_TYPE_PUSH_VLAN = 1, - DR_STE_ACTION_TYPE_ENCAP_L3 = 3, - DR_STE_ACTION_TYPE_ENCAP = 4, -}; +#include "dr_ste.h" struct dr_hw_ste_format { u8 ctrl[DR_STE_SIZE_CTRL]; @@ -142,7 +45,7 @@ u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl) return index; } -static u16 dr_ste_conv_bit_to_byte_mask(u8 *bit_mask) +u16 mlx5dr_ste_conv_bit_to_byte_mask(u8 *bit_mask) { u16 byte_mask = 0; int i; @@ -155,7 +58,7 @@ static u16 dr_ste_conv_bit_to_byte_mask(u8 *bit_mask) return byte_mask; } -static u8 *mlx5dr_ste_get_tag(u8 *hw_ste_p) +static u8 *dr_ste_get_tag(u8 *hw_ste_p) { struct dr_hw_ste_format *hw_ste = (struct dr_hw_ste_format *)hw_ste_p; @@ -169,104 +72,6 @@ void mlx5dr_ste_set_bit_mask(u8 *hw_ste_p, u8 *bit_mask) memcpy(hw_ste->mask, bit_mask, DR_STE_SIZE_MASK); } -void mlx5dr_ste_rx_set_flow_tag(u8 *hw_ste_p, u32 flow_tag) -{ - MLX5_SET(ste_rx_steering_mult, hw_ste_p, qp_list_pointer, - DR_STE_ENABLE_FLOW_TAG | flow_tag); -} - -void mlx5dr_ste_set_counter_id(u8 *hw_ste_p, u32 ctr_id) -{ - /* This can be used for both rx_steering_mult and for sx_transmit */ - MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_15_0, ctr_id); - MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_23_16, ctr_id >> 16); -} - -void mlx5dr_ste_set_go_back_bit(u8 *hw_ste_p) -{ - MLX5_SET(ste_sx_transmit, hw_ste_p, go_back, 1); -} - -void mlx5dr_ste_set_tx_push_vlan(u8 *hw_ste_p, u32 vlan_hdr, - bool go_back) -{ - MLX5_SET(ste_sx_transmit, hw_ste_p, action_type, - DR_STE_ACTION_TYPE_PUSH_VLAN); - MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, vlan_hdr); - /* Due to HW limitation we need to set this bit, otherwise reforamt + - * push vlan will not work. - */ - if (go_back) - mlx5dr_ste_set_go_back_bit(hw_ste_p); -} - -void mlx5dr_ste_set_tx_encap(void *hw_ste_p, u32 reformat_id, int size, bool encap_l3) -{ - MLX5_SET(ste_sx_transmit, hw_ste_p, action_type, - encap_l3 ? DR_STE_ACTION_TYPE_ENCAP_L3 : DR_STE_ACTION_TYPE_ENCAP); - /* The hardware expects here size in words (2 byte) */ - MLX5_SET(ste_sx_transmit, hw_ste_p, action_description, size / 2); - MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, reformat_id); -} - -void mlx5dr_ste_set_rx_decap(u8 *hw_ste_p) -{ - MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action, - DR_STE_TUNL_ACTION_DECAP); -} - -void mlx5dr_ste_set_rx_pop_vlan(u8 *hw_ste_p) -{ - MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action, - DR_STE_TUNL_ACTION_POP_VLAN); -} - -void mlx5dr_ste_set_rx_decap_l3(u8 *hw_ste_p, bool vlan) -{ - MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action, - DR_STE_TUNL_ACTION_L3_DECAP); - MLX5_SET(ste_modify_packet, hw_ste_p, action_description, vlan ? 1 : 0); -} - -void mlx5dr_ste_set_entry_type(u8 *hw_ste_p, u8 entry_type) -{ - MLX5_SET(ste_general, hw_ste_p, entry_type, entry_type); -} - -u8 mlx5dr_ste_get_entry_type(u8 *hw_ste_p) -{ - return MLX5_GET(ste_general, hw_ste_p, entry_type); -} - -void mlx5dr_ste_set_rewrite_actions(u8 *hw_ste_p, u16 num_of_actions, - u32 re_write_index) -{ - MLX5_SET(ste_modify_packet, hw_ste_p, number_of_re_write_actions, - num_of_actions); - MLX5_SET(ste_modify_packet, hw_ste_p, header_re_write_actions_pointer, - re_write_index); -} - -void mlx5dr_ste_set_hit_gvmi(u8 *hw_ste_p, u16 gvmi) -{ - MLX5_SET(ste_general, hw_ste_p, next_table_base_63_48, gvmi); -} - -void mlx5dr_ste_init(u8 *hw_ste_p, u8 lu_type, u8 entry_type, - u16 gvmi) -{ - MLX5_SET(ste_general, hw_ste_p, entry_type, entry_type); - MLX5_SET(ste_general, hw_ste_p, entry_sub_type, lu_type); - MLX5_SET(ste_general, hw_ste_p, next_lu_type, MLX5DR_STE_LU_TYPE_DONT_CARE); - - /* Set GVMI once, this is the same for RX/TX - * bits 63_48 of next table base / miss address encode the next GVMI - */ - MLX5_SET(ste_rx_steering_mult, hw_ste_p, gvmi, gvmi); - MLX5_SET(ste_rx_steering_mult, hw_ste_p, next_table_base_63_48, gvmi); - MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_63_48, gvmi); -} - static void dr_ste_set_always_hit(struct dr_hw_ste_format *hw_ste) { memset(&hw_ste->tag, 0, sizeof(hw_ste->tag)); @@ -279,21 +84,26 @@ static void dr_ste_set_always_miss(struct dr_hw_ste_format *hw_ste) hw_ste->mask[0] = 0; } -u64 mlx5dr_ste_get_miss_addr(u8 *hw_ste) +void mlx5dr_ste_set_miss_addr(struct mlx5dr_ste_ctx *ste_ctx, + u8 *hw_ste_p, u64 miss_addr) { - u64 index = - (MLX5_GET(ste_rx_steering_mult, hw_ste, miss_address_31_6) | - MLX5_GET(ste_rx_steering_mult, hw_ste, miss_address_39_32) << 26); - - return index << 6; + ste_ctx->set_miss_addr(hw_ste_p, miss_addr); } -void mlx5dr_ste_set_hit_addr(u8 *hw_ste, u64 icm_addr, u32 ht_size) +static void dr_ste_always_miss_addr(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste *ste, u64 miss_addr) { - u64 index = (icm_addr >> 5) | ht_size; + u8 *hw_ste_p = ste->hw_ste; - MLX5_SET(ste_general, hw_ste, next_table_base_39_32_size, index >> 27); - MLX5_SET(ste_general, hw_ste, next_table_base_31_5_size, index); + ste_ctx->set_next_lu_type(hw_ste_p, MLX5DR_STE_LU_TYPE_DONT_CARE); + ste_ctx->set_miss_addr(hw_ste_p, miss_addr); + dr_ste_set_always_miss((struct dr_hw_ste_format *)ste->hw_ste); +} + +void mlx5dr_ste_set_hit_addr(struct mlx5dr_ste_ctx *ste_ctx, + u8 *hw_ste, u64 icm_addr, u32 ht_size) +{ + ste_ctx->set_hit_addr(hw_ste, icm_addr, ht_size); } u64 mlx5dr_ste_get_icm_addr(struct mlx5dr_ste *ste) @@ -317,15 +127,16 @@ struct list_head *mlx5dr_ste_get_miss_list(struct mlx5dr_ste *ste) return &ste->htbl->miss_list[index]; } -static void dr_ste_always_hit_htbl(struct mlx5dr_ste *ste, +static void dr_ste_always_hit_htbl(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste *ste, struct mlx5dr_ste_htbl *next_htbl) { struct mlx5dr_icm_chunk *chunk = next_htbl->chunk; u8 *hw_ste = ste->hw_ste; - MLX5_SET(ste_general, hw_ste, byte_mask, next_htbl->byte_mask); - MLX5_SET(ste_general, hw_ste, next_lu_type, next_htbl->lu_type); - mlx5dr_ste_set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries); + ste_ctx->set_byte_mask(hw_ste, next_htbl->byte_mask); + ste_ctx->set_next_lu_type(hw_ste, next_htbl->lu_type); + ste_ctx->set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries); dr_ste_set_always_hit((struct dr_hw_ste_format *)ste->hw_ste); } @@ -363,7 +174,8 @@ static void dr_ste_replace(struct mlx5dr_ste *dst, struct mlx5dr_ste *src) /* Free ste which is the head and the only one in miss_list */ static void -dr_ste_remove_head_ste(struct mlx5dr_ste *ste, +dr_ste_remove_head_ste(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste *ste, struct mlx5dr_matcher_rx_tx *nic_matcher, struct mlx5dr_ste_send_info *ste_info_head, struct list_head *send_ste_list, @@ -380,7 +192,7 @@ dr_ste_remove_head_ste(struct mlx5dr_ste *ste, */ memcpy(tmp_ste.hw_ste, ste->hw_ste, DR_STE_SIZE_REDUCED); miss_addr = nic_matcher->e_anchor->chunk->icm_addr; - mlx5dr_ste_always_miss_addr(&tmp_ste, miss_addr); + dr_ste_always_miss_addr(ste_ctx, &tmp_ste, miss_addr); memcpy(ste->hw_ste, tmp_ste.hw_ste, DR_STE_SIZE_REDUCED); list_del_init(&ste->miss_list_node); @@ -436,7 +248,8 @@ dr_ste_replace_head_ste(struct mlx5dr_ste *ste, struct mlx5dr_ste *next_ste, /* Free ste that is located in the middle of the miss list: * |__| -->|_prev_ste_|->|_ste_|-->|_next_ste_| */ -static void dr_ste_remove_middle_ste(struct mlx5dr_ste *ste, +static void dr_ste_remove_middle_ste(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste *ste, struct mlx5dr_ste_send_info *ste_info, struct list_head *send_ste_list, struct mlx5dr_ste_htbl *stats_tbl) @@ -448,8 +261,8 @@ static void dr_ste_remove_middle_ste(struct mlx5dr_ste *ste, if (WARN_ON(!prev_ste)) return; - miss_addr = mlx5dr_ste_get_miss_addr(ste->hw_ste); - mlx5dr_ste_set_miss_addr(prev_ste->hw_ste, miss_addr); + miss_addr = ste_ctx->get_miss_addr(ste->hw_ste); + ste_ctx->set_miss_addr(prev_ste->hw_ste, miss_addr); mlx5dr_send_fill_and_append_ste_send_info(prev_ste, DR_STE_SIZE_REDUCED, 0, prev_ste->hw_ste, ste_info, @@ -467,6 +280,7 @@ void mlx5dr_ste_free(struct mlx5dr_ste *ste, { struct mlx5dr_ste_send_info *cur_ste_info, *tmp_ste_info; struct mlx5dr_domain *dmn = matcher->tbl->dmn; + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_send_info ste_info_head; struct mlx5dr_ste *next_ste, *first_ste; bool put_on_origin_table = true; @@ -495,7 +309,8 @@ void mlx5dr_ste_free(struct mlx5dr_ste *ste, if (!next_ste) { /* One and only entry in the list */ - dr_ste_remove_head_ste(ste, nic_matcher, + dr_ste_remove_head_ste(ste_ctx, ste, + nic_matcher, &ste_info_head, &send_ste_list, stats_tbl); @@ -506,7 +321,9 @@ void mlx5dr_ste_free(struct mlx5dr_ste *ste, put_on_origin_table = false; } } else { /* Ste in the middle of the list */ - dr_ste_remove_middle_ste(ste, &ste_info_head, &send_ste_list, stats_tbl); + dr_ste_remove_middle_ste(ste_ctx, ste, + &ste_info_head, &send_ste_list, + stats_tbl); } /* Update HW */ @@ -530,34 +347,18 @@ bool mlx5dr_ste_equal_tag(void *src, void *dst) return !memcmp(s_hw_ste->tag, d_hw_ste->tag, DR_STE_SIZE_TAG); } -void mlx5dr_ste_set_hit_addr_by_next_htbl(u8 *hw_ste, +void mlx5dr_ste_set_hit_addr_by_next_htbl(struct mlx5dr_ste_ctx *ste_ctx, + u8 *hw_ste, struct mlx5dr_ste_htbl *next_htbl) { struct mlx5dr_icm_chunk *chunk = next_htbl->chunk; - mlx5dr_ste_set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries); -} - -void mlx5dr_ste_set_miss_addr(u8 *hw_ste_p, u64 miss_addr) -{ - u64 index = miss_addr >> 6; - - /* Miss address for TX and RX STEs located in the same offsets */ - MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_39_32, index >> 26); - MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_31_6, index); -} - -void mlx5dr_ste_always_miss_addr(struct mlx5dr_ste *ste, u64 miss_addr) -{ - u8 *hw_ste = ste->hw_ste; - - MLX5_SET(ste_rx_steering_mult, hw_ste, next_lu_type, MLX5DR_STE_LU_TYPE_DONT_CARE); - mlx5dr_ste_set_miss_addr(hw_ste, miss_addr); - dr_ste_set_always_miss((struct dr_hw_ste_format *)ste->hw_ste); + ste_ctx->set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries); } /* Init one ste as a pattern for ste data array */ -void mlx5dr_ste_set_formatted_ste(u16 gvmi, +void mlx5dr_ste_set_formatted_ste(struct mlx5dr_ste_ctx *ste_ctx, + u16 gvmi, struct mlx5dr_domain_rx_tx *nic_dmn, struct mlx5dr_ste_htbl *htbl, u8 *formatted_ste, @@ -565,13 +366,13 @@ void mlx5dr_ste_set_formatted_ste(u16 gvmi, { struct mlx5dr_ste ste = {}; - mlx5dr_ste_init(formatted_ste, htbl->lu_type, nic_dmn->ste_type, gvmi); + ste_ctx->ste_init(formatted_ste, htbl->lu_type, nic_dmn->ste_type, gvmi); ste.hw_ste = formatted_ste; if (connect_info->type == CONNECT_HIT) - dr_ste_always_hit_htbl(&ste, connect_info->hit_next_htbl); + dr_ste_always_hit_htbl(ste_ctx, &ste, connect_info->hit_next_htbl); else - mlx5dr_ste_always_miss_addr(&ste, connect_info->miss_icm_addr); + dr_ste_always_miss_addr(ste_ctx, &ste, connect_info->miss_icm_addr); } int mlx5dr_ste_htbl_init_and_postsend(struct mlx5dr_domain *dmn, @@ -582,7 +383,8 @@ int mlx5dr_ste_htbl_init_and_postsend(struct mlx5dr_domain *dmn, { u8 formatted_ste[DR_STE_SIZE] = {}; - mlx5dr_ste_set_formatted_ste(dmn->info.caps.gvmi, + mlx5dr_ste_set_formatted_ste(dmn->ste_ctx, + dmn->info.caps.gvmi, nic_dmn, htbl, formatted_ste, @@ -597,18 +399,18 @@ int mlx5dr_ste_create_next_htbl(struct mlx5dr_matcher *matcher, u8 *cur_hw_ste, enum mlx5dr_icm_chunk_size log_table_size) { - struct dr_hw_ste_format *hw_ste = (struct dr_hw_ste_format *)cur_hw_ste; struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn; struct mlx5dr_domain *dmn = matcher->tbl->dmn; + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_htbl_connect_info info; struct mlx5dr_ste_htbl *next_htbl; if (!mlx5dr_ste_is_last_in_rule(nic_matcher, ste->ste_chain_location)) { - u8 next_lu_type; + u16 next_lu_type; u16 byte_mask; - next_lu_type = MLX5_GET(ste_general, hw_ste, next_lu_type); - byte_mask = MLX5_GET(ste_general, hw_ste, byte_mask); + next_lu_type = ste_ctx->get_next_lu_type(cur_hw_ste); + byte_mask = ste_ctx->get_byte_mask(cur_hw_ste); next_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool, log_table_size, @@ -628,7 +430,8 @@ int mlx5dr_ste_create_next_htbl(struct mlx5dr_matcher *matcher, goto free_table; } - mlx5dr_ste_set_hit_addr_by_next_htbl(cur_hw_ste, next_htbl); + mlx5dr_ste_set_hit_addr_by_next_htbl(ste_ctx, + cur_hw_ste, next_htbl); ste->next_htbl = next_htbl; next_htbl->pointing_ste = ste; } @@ -657,7 +460,7 @@ static void dr_ste_set_ctrl(struct mlx5dr_ste_htbl *htbl) struct mlx5dr_ste_htbl *mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool, enum mlx5dr_icm_chunk_size chunk_size, - u8 lu_type, u16 byte_mask) + u16 lu_type, u16 byte_mask) { struct mlx5dr_icm_chunk *chunk; struct mlx5dr_ste_htbl *htbl; @@ -709,6 +512,92 @@ int mlx5dr_ste_htbl_free(struct mlx5dr_ste_htbl *htbl) return 0; } +void mlx5dr_ste_set_actions_tx(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *hw_ste_arr, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes) +{ + ste_ctx->set_actions_tx(dmn, action_type_set, hw_ste_arr, + attr, added_stes); +} + +void mlx5dr_ste_set_actions_rx(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *hw_ste_arr, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes) +{ + ste_ctx->set_actions_rx(dmn, action_type_set, hw_ste_arr, + attr, added_stes); +} + +const struct mlx5dr_ste_action_modify_field * +mlx5dr_ste_conv_modify_hdr_sw_field(struct mlx5dr_ste_ctx *ste_ctx, u16 sw_field) +{ + const struct mlx5dr_ste_action_modify_field *hw_field; + + if (sw_field >= ste_ctx->modify_field_arr_sz) + return NULL; + + hw_field = &ste_ctx->modify_field_arr[sw_field]; + if (!hw_field->end && !hw_field->start) + return NULL; + + return hw_field; +} + +void mlx5dr_ste_set_action_set(struct mlx5dr_ste_ctx *ste_ctx, + __be64 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data) +{ + ste_ctx->set_action_set((u8 *)hw_action, + hw_field, shifter, length, data); +} + +void mlx5dr_ste_set_action_add(struct mlx5dr_ste_ctx *ste_ctx, + __be64 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data) +{ + ste_ctx->set_action_add((u8 *)hw_action, + hw_field, shifter, length, data); +} + +void mlx5dr_ste_set_action_copy(struct mlx5dr_ste_ctx *ste_ctx, + __be64 *hw_action, + u8 dst_hw_field, + u8 dst_shifter, + u8 dst_len, + u8 src_hw_field, + u8 src_shifter) +{ + ste_ctx->set_action_copy((u8 *)hw_action, + dst_hw_field, dst_shifter, dst_len, + src_hw_field, src_shifter); +} + +int mlx5dr_ste_set_action_decap_l3_list(struct mlx5dr_ste_ctx *ste_ctx, + void *data, u32 data_sz, + u8 *hw_action, u32 hw_action_sz, + u16 *used_hw_action_num) +{ + /* Only Ethernet frame is supported, with VLAN (18) or without (14) */ + if (data_sz != HDR_LEN_L2 && data_sz != HDR_LEN_L2_W_VLAN) + return -EINVAL; + + return ste_ctx->set_action_decap_l3_list(data, data_sz, + hw_action, hw_action_sz, + used_hw_action_num); +} + int mlx5dr_ste_build_pre_check(struct mlx5dr_domain *dmn, u8 match_criteria, struct mlx5dr_match_param *mask, @@ -738,6 +627,7 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher, { struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn; struct mlx5dr_domain *dmn = matcher->tbl->dmn; + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_build *sb; int ret, i; @@ -748,14 +638,14 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher, sb = nic_matcher->ste_builder; for (i = 0; i < nic_matcher->num_of_builders; i++) { - mlx5dr_ste_init(ste_arr, - sb->lu_type, - nic_dmn->ste_type, - dmn->info.caps.gvmi); + ste_ctx->ste_init(ste_arr, + sb->lu_type, + nic_dmn->ste_type, + dmn->info.caps.gvmi); mlx5dr_ste_set_bit_mask(ste_arr, sb->bit_mask); - ret = sb->ste_build_tag_func(value, sb, mlx5dr_ste_get_tag(ste_arr)); + ret = sb->ste_build_tag_func(value, sb, dr_ste_get_tag(ste_arr)); if (ret) return ret; @@ -765,45 +655,14 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher, * not relevant for the last ste in the chain. */ sb++; - MLX5_SET(ste_general, ste_arr, next_lu_type, sb->lu_type); - MLX5_SET(ste_general, ste_arr, byte_mask, sb->byte_mask); + ste_ctx->set_next_lu_type(ste_arr, sb->lu_type); + ste_ctx->set_byte_mask(ste_arr, sb->byte_mask); } ste_arr += DR_STE_SIZE; } return 0; } -static void dr_ste_build_eth_l2_src_des_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, dmac_47_16, mask, dmac_47_16); - DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, dmac_15_0, mask, dmac_15_0); - - if (mask->smac_47_16 || mask->smac_15_0) { - MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_47_32, - mask->smac_47_16 >> 16); - MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_31_0, - mask->smac_47_16 << 16 | mask->smac_15_0); - mask->smac_47_16 = 0; - mask->smac_15_0 = 0; - } - - DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, first_vlan_id, mask, first_vid); - DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, first_cfi, mask, first_cfi); - DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, first_priority, mask, first_prio); - DR_STE_SET_MASK(eth_l2_src_dst, bit_mask, l3_type, mask, ip_version); - - if (mask->cvlan_tag) { - MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1); - mask->cvlan_tag = 0; - } else if (mask->svlan_tag) { - MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1); - mask->svlan_tag = 0; - } -} - static void dr_ste_copy_mask_misc(char *mask, struct mlx5dr_match_misc *spec) { spec->gre_c_present = MLX5_GET(fte_match_set_misc, mask, gre_c_present); @@ -1045,566 +904,93 @@ void mlx5dr_ste_copy_param(u8 match_criteria, } } -static int dr_ste_build_eth_l2_src_des_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_47_16, spec, dmac_47_16); - DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_15_0, spec, dmac_15_0); - - if (spec->smac_47_16 || spec->smac_15_0) { - MLX5_SET(ste_eth_l2_src_dst, tag, smac_47_32, - spec->smac_47_16 >> 16); - MLX5_SET(ste_eth_l2_src_dst, tag, smac_31_0, - spec->smac_47_16 << 16 | spec->smac_15_0); - spec->smac_47_16 = 0; - spec->smac_15_0 = 0; - } - - if (spec->ip_version) { - if (spec->ip_version == IP_VERSION_IPV4) { - MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV4); - spec->ip_version = 0; - } else if (spec->ip_version == IP_VERSION_IPV6) { - MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV6); - spec->ip_version = 0; - } else { - pr_info("Unsupported ip_version value\n"); - return -EINVAL; - } - } - - DR_STE_SET_TAG(eth_l2_src_dst, tag, first_vlan_id, spec, first_vid); - DR_STE_SET_TAG(eth_l2_src_dst, tag, first_cfi, spec, first_cfi); - DR_STE_SET_TAG(eth_l2_src_dst, tag, first_priority, spec, first_prio); - - if (spec->cvlan_tag) { - MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_CVLAN); - spec->cvlan_tag = 0; - } else if (spec->svlan_tag) { - MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_SVLAN); - spec->svlan_tag = 0; - } - return 0; -} - -void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l2_src_des_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC_DST, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l2_src_des_tag; -} - -static void dr_ste_build_eth_l3_ipv6_dst_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_127_96, mask, dst_ip_127_96); - DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_95_64, mask, dst_ip_95_64); - DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_63_32, mask, dst_ip_63_32); - DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_31_0, mask, dst_ip_31_0); -} - -static int dr_ste_build_eth_l3_ipv6_dst_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_127_96, spec, dst_ip_127_96); - DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_95_64, spec, dst_ip_95_64); - DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_63_32, spec, dst_ip_63_32); - DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_31_0, spec, dst_ip_31_0); - - return 0; + ste_ctx->build_eth_l2_src_dst_init(sb, mask); } -void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l3_ipv6_dst_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_DST, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv6_dst_tag; -} - -static void dr_ste_build_eth_l3_ipv6_src_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_127_96, mask, src_ip_127_96); - DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_95_64, mask, src_ip_95_64); - DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_63_32, mask, src_ip_63_32); - DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_31_0, mask, src_ip_31_0); -} - -static int dr_ste_build_eth_l3_ipv6_src_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_127_96, spec, src_ip_127_96); - DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_95_64, spec, src_ip_95_64); - DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_63_32, spec, src_ip_63_32); - DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_31_0, spec, src_ip_31_0); - - return 0; + ste_ctx->build_eth_l3_ipv6_dst_init(sb, mask); } -void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l3_ipv6_src_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_SRC, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv6_src_tag; -} - -static void dr_ste_build_eth_l3_ipv4_5_tuple_bit_mask(struct mlx5dr_match_param *value, - bool inner, - u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - destination_address, mask, dst_ip_31_0); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - source_address, mask, src_ip_31_0); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - destination_port, mask, tcp_dport); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - destination_port, mask, udp_dport); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - source_port, mask, tcp_sport); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - source_port, mask, udp_sport); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - protocol, mask, ip_protocol); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - fragmented, mask, frag); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - dscp, mask, ip_dscp); - DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask, - ecn, mask, ip_ecn); - - if (mask->tcp_flags) { - DR_STE_SET_TCP_FLAGS(eth_l3_ipv4_5_tuple, bit_mask, mask); - mask->tcp_flags = 0; - } -} - -static int dr_ste_build_eth_l3_ipv4_5_tuple_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_address, spec, dst_ip_31_0); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_address, spec, src_ip_31_0); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, tcp_dport); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, udp_dport); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, tcp_sport); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, udp_sport); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, protocol, spec, ip_protocol); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, fragmented, spec, frag); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, dscp, spec, ip_dscp); - DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, ecn, spec, ip_ecn); - - if (spec->tcp_flags) { - DR_STE_SET_TCP_FLAGS(eth_l3_ipv4_5_tuple, tag, spec); - spec->tcp_flags = 0; - } - - return 0; + ste_ctx->build_eth_l3_ipv6_src_init(sb, mask); } -void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l3_ipv4_5_tuple_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_5_TUPLE, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv4_5_tuple_tag; -} - -static void -dr_ste_build_eth_l2_src_or_dst_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - struct mlx5dr_match_misc *misc_mask = &value->misc; - - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, first_vlan_id, mask, first_vid); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, first_cfi, mask, first_cfi); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, first_priority, mask, first_prio); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, ip_fragmented, mask, frag); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, l3_ethertype, mask, ethertype); - DR_STE_SET_MASK(eth_l2_src, bit_mask, l3_type, mask, ip_version); - - if (mask->svlan_tag || mask->cvlan_tag) { - MLX5_SET(ste_eth_l2_src, bit_mask, first_vlan_qualifier, -1); - mask->cvlan_tag = 0; - mask->svlan_tag = 0; - } - - if (inner) { - if (misc_mask->inner_second_cvlan_tag || - misc_mask->inner_second_svlan_tag) { - MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1); - misc_mask->inner_second_cvlan_tag = 0; - misc_mask->inner_second_svlan_tag = 0; - } - - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, - second_vlan_id, misc_mask, inner_second_vid); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, - second_cfi, misc_mask, inner_second_cfi); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, - second_priority, misc_mask, inner_second_prio); - } else { - if (misc_mask->outer_second_cvlan_tag || - misc_mask->outer_second_svlan_tag) { - MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1); - misc_mask->outer_second_cvlan_tag = 0; - misc_mask->outer_second_svlan_tag = 0; - } - - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, - second_vlan_id, misc_mask, outer_second_vid); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, - second_cfi, misc_mask, outer_second_cfi); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, - second_priority, misc_mask, outer_second_prio); - } -} - -static int dr_ste_build_eth_l2_src_or_dst_tag(struct mlx5dr_match_param *value, - bool inner, u8 *tag) -{ - struct mlx5dr_match_spec *spec = inner ? &value->inner : &value->outer; - struct mlx5dr_match_misc *misc_spec = &value->misc; - - DR_STE_SET_TAG(eth_l2_src, tag, first_vlan_id, spec, first_vid); - DR_STE_SET_TAG(eth_l2_src, tag, first_cfi, spec, first_cfi); - DR_STE_SET_TAG(eth_l2_src, tag, first_priority, spec, first_prio); - DR_STE_SET_TAG(eth_l2_src, tag, ip_fragmented, spec, frag); - DR_STE_SET_TAG(eth_l2_src, tag, l3_ethertype, spec, ethertype); - - if (spec->ip_version) { - if (spec->ip_version == IP_VERSION_IPV4) { - MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV4); - spec->ip_version = 0; - } else if (spec->ip_version == IP_VERSION_IPV6) { - MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV6); - spec->ip_version = 0; - } else { - pr_info("Unsupported ip_version value\n"); - return -EINVAL; - } - } - - if (spec->cvlan_tag) { - MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_CVLAN); - spec->cvlan_tag = 0; - } else if (spec->svlan_tag) { - MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_SVLAN); - spec->svlan_tag = 0; - } - - if (inner) { - if (misc_spec->inner_second_cvlan_tag) { - MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN); - misc_spec->inner_second_cvlan_tag = 0; - } else if (misc_spec->inner_second_svlan_tag) { - MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN); - misc_spec->inner_second_svlan_tag = 0; - } - - DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, inner_second_vid); - DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, inner_second_cfi); - DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, inner_second_prio); - } else { - if (misc_spec->outer_second_cvlan_tag) { - MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN); - misc_spec->outer_second_cvlan_tag = 0; - } else if (misc_spec->outer_second_svlan_tag) { - MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN); - misc_spec->outer_second_svlan_tag = 0; - } - DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, outer_second_vid); - DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, outer_second_cfi); - DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, outer_second_prio); - } - - return 0; -} - -static void dr_ste_build_eth_l2_src_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, smac_47_16, mask, smac_47_16); - DR_STE_SET_MASK_V(eth_l2_src, bit_mask, smac_15_0, mask, smac_15_0); - - dr_ste_build_eth_l2_src_or_dst_bit_mask(value, inner, bit_mask); -} - -static int dr_ste_build_eth_l2_src_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l2_src, tag, smac_47_16, spec, smac_47_16); - DR_STE_SET_TAG(eth_l2_src, tag, smac_15_0, spec, smac_15_0); - - return dr_ste_build_eth_l2_src_or_dst_tag(value, sb->inner, tag); + ste_ctx->build_eth_l3_ipv4_5_tuple_init(sb, mask); } -void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l2_src_bit_mask(mask, inner, sb->bit_mask); sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l2_src_tag; -} - -static void dr_ste_build_eth_l2_dst_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l2_dst, bit_mask, dmac_47_16, mask, dmac_47_16); - DR_STE_SET_MASK_V(eth_l2_dst, bit_mask, dmac_15_0, mask, dmac_15_0); - - dr_ste_build_eth_l2_src_or_dst_bit_mask(value, inner, bit_mask); -} - -static int dr_ste_build_eth_l2_dst_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l2_dst, tag, dmac_47_16, spec, dmac_47_16); - DR_STE_SET_TAG(eth_l2_dst, tag, dmac_15_0, spec, dmac_15_0); - - return dr_ste_build_eth_l2_src_or_dst_tag(value, sb->inner, tag); + ste_ctx->build_eth_l2_src_init(sb, mask); } -void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l2_dst_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_DST, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l2_dst_tag; -} - -static void dr_ste_build_eth_l2_tnl_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - struct mlx5dr_match_misc *misc = &value->misc; - - DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, dmac_47_16, mask, dmac_47_16); - DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, dmac_15_0, mask, dmac_15_0); - DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, first_vlan_id, mask, first_vid); - DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, first_cfi, mask, first_cfi); - DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, first_priority, mask, first_prio); - DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, ip_fragmented, mask, frag); - DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, l3_ethertype, mask, ethertype); - DR_STE_SET_MASK(eth_l2_tnl, bit_mask, l3_type, mask, ip_version); - - if (misc->vxlan_vni) { - MLX5_SET(ste_eth_l2_tnl, bit_mask, - l2_tunneling_network_id, (misc->vxlan_vni << 8)); - misc->vxlan_vni = 0; - } - - if (mask->svlan_tag || mask->cvlan_tag) { - MLX5_SET(ste_eth_l2_tnl, bit_mask, first_vlan_qualifier, -1); - mask->cvlan_tag = 0; - mask->svlan_tag = 0; - } + ste_ctx->build_eth_l2_dst_init(sb, mask); } -static int dr_ste_build_eth_l2_tnl_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - struct mlx5dr_match_misc *misc = &value->misc; - - DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_47_16, spec, dmac_47_16); - DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_15_0, spec, dmac_15_0); - DR_STE_SET_TAG(eth_l2_tnl, tag, first_vlan_id, spec, first_vid); - DR_STE_SET_TAG(eth_l2_tnl, tag, first_cfi, spec, first_cfi); - DR_STE_SET_TAG(eth_l2_tnl, tag, ip_fragmented, spec, frag); - DR_STE_SET_TAG(eth_l2_tnl, tag, first_priority, spec, first_prio); - DR_STE_SET_TAG(eth_l2_tnl, tag, l3_ethertype, spec, ethertype); - - if (misc->vxlan_vni) { - MLX5_SET(ste_eth_l2_tnl, tag, l2_tunneling_network_id, - (misc->vxlan_vni << 8)); - misc->vxlan_vni = 0; - } - - if (spec->cvlan_tag) { - MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_CVLAN); - spec->cvlan_tag = 0; - } else if (spec->svlan_tag) { - MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_SVLAN); - spec->svlan_tag = 0; - } - - if (spec->ip_version) { - if (spec->ip_version == IP_VERSION_IPV4) { - MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV4); - spec->ip_version = 0; - } else if (spec->ip_version == IP_VERSION_IPV6) { - MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV6); - spec->ip_version = 0; - } else { - return -EINVAL; - } - } - - return 0; -} - -void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l2_tnl_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_ETHL2_TUNNELING_I; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l2_tnl_tag; -} - -static void dr_ste_build_eth_l3_ipv4_misc_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l3_ipv4_misc, bit_mask, time_to_live, mask, ttl_hoplimit); -} - -static int dr_ste_build_eth_l3_ipv4_misc_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l3_ipv4_misc, tag, time_to_live, spec, ttl_hoplimit); - - return 0; + ste_ctx->build_eth_l2_tnl_init(sb, mask); } -void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l3_ipv4_misc_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_MISC, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv4_misc_tag; -} - -static void dr_ste_build_ipv6_l3_l4_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; - - DR_STE_SET_MASK_V(eth_l4, bit_mask, dst_port, mask, tcp_dport); - DR_STE_SET_MASK_V(eth_l4, bit_mask, src_port, mask, tcp_sport); - DR_STE_SET_MASK_V(eth_l4, bit_mask, dst_port, mask, udp_dport); - DR_STE_SET_MASK_V(eth_l4, bit_mask, src_port, mask, udp_sport); - DR_STE_SET_MASK_V(eth_l4, bit_mask, protocol, mask, ip_protocol); - DR_STE_SET_MASK_V(eth_l4, bit_mask, fragmented, mask, frag); - DR_STE_SET_MASK_V(eth_l4, bit_mask, dscp, mask, ip_dscp); - DR_STE_SET_MASK_V(eth_l4, bit_mask, ecn, mask, ip_ecn); - DR_STE_SET_MASK_V(eth_l4, bit_mask, ipv6_hop_limit, mask, ttl_hoplimit); - - if (mask->tcp_flags) { - DR_STE_SET_TCP_FLAGS(eth_l4, bit_mask, mask); - mask->tcp_flags = 0; - } + ste_ctx->build_eth_l3_ipv4_misc_init(sb, mask); } -static int dr_ste_build_ipv6_l3_l4_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; - - DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, tcp_dport); - DR_STE_SET_TAG(eth_l4, tag, src_port, spec, tcp_sport); - DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, udp_dport); - DR_STE_SET_TAG(eth_l4, tag, src_port, spec, udp_sport); - DR_STE_SET_TAG(eth_l4, tag, protocol, spec, ip_protocol); - DR_STE_SET_TAG(eth_l4, tag, fragmented, spec, frag); - DR_STE_SET_TAG(eth_l4, tag, dscp, spec, ip_dscp); - DR_STE_SET_TAG(eth_l4, tag, ecn, spec, ip_ecn); - DR_STE_SET_TAG(eth_l4, tag, ipv6_hop_limit, spec, ttl_hoplimit); - - if (spec->tcp_flags) { - DR_STE_SET_TCP_FLAGS(eth_l4, tag, spec); - spec->tcp_flags = 0; - } - - return 0; -} - -void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_ipv6_l3_l4_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_ipv6_l3_l4_tag; + ste_ctx->build_eth_ipv6_l3_l4_init(sb, mask); } static int dr_ste_build_empty_always_hit_tag(struct mlx5dr_match_param *value, @@ -1622,653 +1008,110 @@ void mlx5dr_ste_build_empty_always_hit(struct mlx5dr_ste_build *sb, bool rx) sb->ste_build_tag_func = &dr_ste_build_empty_always_hit_tag; } -static void dr_ste_build_mpls_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_misc2 *misc2_mask = &value->misc2; - - if (inner) - DR_STE_SET_MPLS_MASK(mpls, misc2_mask, inner, bit_mask); - else - DR_STE_SET_MPLS_MASK(mpls, misc2_mask, outer, bit_mask); -} - -static int dr_ste_build_mpls_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc2 *misc2_mask = &value->misc2; - - if (sb->inner) - DR_STE_SET_MPLS_TAG(mpls, misc2_mask, inner, tag); - else - DR_STE_SET_MPLS_TAG(mpls, misc2_mask, outer, tag); - - return 0; -} - -void mlx5dr_ste_build_mpls(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_mpls(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_mpls_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(MPLS_FIRST, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_mpls_tag; -} - -static void dr_ste_build_gre_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_misc *misc_mask = &value->misc; - - DR_STE_SET_MASK_V(gre, bit_mask, gre_protocol, misc_mask, gre_protocol); - DR_STE_SET_MASK_V(gre, bit_mask, gre_k_present, misc_mask, gre_k_present); - DR_STE_SET_MASK_V(gre, bit_mask, gre_key_h, misc_mask, gre_key_h); - DR_STE_SET_MASK_V(gre, bit_mask, gre_key_l, misc_mask, gre_key_l); - - DR_STE_SET_MASK_V(gre, bit_mask, gre_c_present, misc_mask, gre_c_present); - DR_STE_SET_MASK_V(gre, bit_mask, gre_s_present, misc_mask, gre_s_present); + ste_ctx->build_mpls_init(sb, mask); } -static int dr_ste_build_gre_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) +void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask, + bool inner, bool rx) { - struct mlx5dr_match_misc *misc = &value->misc; - - DR_STE_SET_TAG(gre, tag, gre_protocol, misc, gre_protocol); - - DR_STE_SET_TAG(gre, tag, gre_k_present, misc, gre_k_present); - DR_STE_SET_TAG(gre, tag, gre_key_h, misc, gre_key_h); - DR_STE_SET_TAG(gre, tag, gre_key_l, misc, gre_key_l); - - DR_STE_SET_TAG(gre, tag, gre_c_present, misc, gre_c_present); - - DR_STE_SET_TAG(gre, tag, gre_s_present, misc, gre_s_present); - - return 0; -} - -void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask, bool inner, bool rx) -{ - dr_ste_build_gre_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_GRE; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_gre_tag; -} - -static void dr_ste_build_flex_parser_0_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2; - - if (DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(misc_2_mask)) { - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_label, - misc_2_mask, outer_first_mpls_over_gre_label); - - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_exp, - misc_2_mask, outer_first_mpls_over_gre_exp); - - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_s_bos, - misc_2_mask, outer_first_mpls_over_gre_s_bos); - - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_ttl, - misc_2_mask, outer_first_mpls_over_gre_ttl); - } else { - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_label, - misc_2_mask, outer_first_mpls_over_udp_label); - - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_exp, - misc_2_mask, outer_first_mpls_over_udp_exp); - - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_s_bos, - misc_2_mask, outer_first_mpls_over_udp_s_bos); - - DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_ttl, - misc_2_mask, outer_first_mpls_over_udp_ttl); - } -} - -static int dr_ste_build_flex_parser_0_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2; - - if (DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(misc_2_mask)) { - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label, - misc_2_mask, outer_first_mpls_over_gre_label); - - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp, - misc_2_mask, outer_first_mpls_over_gre_exp); - - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos, - misc_2_mask, outer_first_mpls_over_gre_s_bos); - - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl, - misc_2_mask, outer_first_mpls_over_gre_ttl); - } else { - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label, - misc_2_mask, outer_first_mpls_over_udp_label); - - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp, - misc_2_mask, outer_first_mpls_over_udp_exp); - - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos, - misc_2_mask, outer_first_mpls_over_udp_s_bos); - - DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl, - misc_2_mask, outer_first_mpls_over_udp_ttl); - } - return 0; + ste_ctx->build_tnl_gre_init(sb, mask); } -void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_flex_parser_0_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_0; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_flex_parser_0_tag; -} - -#define ICMP_TYPE_OFFSET_FIRST_DW 24 -#define ICMP_CODE_OFFSET_FIRST_DW 16 -#define ICMP_HEADER_DATA_OFFSET_SECOND_DW 0 - -static int dr_ste_build_flex_parser_1_bit_mask(struct mlx5dr_match_param *mask, - struct mlx5dr_cmd_caps *caps, - u8 *bit_mask) -{ - bool is_ipv4_mask = DR_MASK_IS_ICMPV4_SET(&mask->misc3); - struct mlx5dr_match_misc3 *misc_3_mask = &mask->misc3; - u32 icmp_header_data_mask; - u32 icmp_type_mask; - u32 icmp_code_mask; - int dw0_location; - int dw1_location; - - if (is_ipv4_mask) { - icmp_header_data_mask = misc_3_mask->icmpv4_header_data; - icmp_type_mask = misc_3_mask->icmpv4_type; - icmp_code_mask = misc_3_mask->icmpv4_code; - dw0_location = caps->flex_parser_id_icmp_dw0; - dw1_location = caps->flex_parser_id_icmp_dw1; - } else { - icmp_header_data_mask = misc_3_mask->icmpv6_header_data; - icmp_type_mask = misc_3_mask->icmpv6_type; - icmp_code_mask = misc_3_mask->icmpv6_code; - dw0_location = caps->flex_parser_id_icmpv6_dw0; - dw1_location = caps->flex_parser_id_icmpv6_dw1; - } - - switch (dw0_location) { - case 4: - if (icmp_type_mask) { - MLX5_SET(ste_flex_parser_1, bit_mask, flex_parser_4, - (icmp_type_mask << ICMP_TYPE_OFFSET_FIRST_DW)); - if (is_ipv4_mask) - misc_3_mask->icmpv4_type = 0; - else - misc_3_mask->icmpv6_type = 0; - } - if (icmp_code_mask) { - u32 cur_val = MLX5_GET(ste_flex_parser_1, bit_mask, - flex_parser_4); - MLX5_SET(ste_flex_parser_1, bit_mask, flex_parser_4, - cur_val | (icmp_code_mask << ICMP_CODE_OFFSET_FIRST_DW)); - if (is_ipv4_mask) - misc_3_mask->icmpv4_code = 0; - else - misc_3_mask->icmpv6_code = 0; - } - break; - default: - return -EINVAL; - } - - switch (dw1_location) { - case 5: - if (icmp_header_data_mask) { - MLX5_SET(ste_flex_parser_1, bit_mask, flex_parser_5, - (icmp_header_data_mask << ICMP_HEADER_DATA_OFFSET_SECOND_DW)); - if (is_ipv4_mask) - misc_3_mask->icmpv4_header_data = 0; - else - misc_3_mask->icmpv6_header_data = 0; - } - break; - default: - return -EINVAL; - } - - return 0; + ste_ctx->build_tnl_mpls_init(sb, mask); } -static int dr_ste_build_flex_parser_1_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc3 *misc_3 = &value->misc3; - u32 icmp_header_data; - int dw0_location; - int dw1_location; - u32 icmp_type; - u32 icmp_code; - bool is_ipv4; - - is_ipv4 = DR_MASK_IS_ICMPV4_SET(misc_3); - if (is_ipv4) { - icmp_header_data = misc_3->icmpv4_header_data; - icmp_type = misc_3->icmpv4_type; - icmp_code = misc_3->icmpv4_code; - dw0_location = sb->caps->flex_parser_id_icmp_dw0; - dw1_location = sb->caps->flex_parser_id_icmp_dw1; - } else { - icmp_header_data = misc_3->icmpv6_header_data; - icmp_type = misc_3->icmpv6_type; - icmp_code = misc_3->icmpv6_code; - dw0_location = sb->caps->flex_parser_id_icmpv6_dw0; - dw1_location = sb->caps->flex_parser_id_icmpv6_dw1; - } - - switch (dw0_location) { - case 4: - if (icmp_type) { - MLX5_SET(ste_flex_parser_1, tag, flex_parser_4, - (icmp_type << ICMP_TYPE_OFFSET_FIRST_DW)); - if (is_ipv4) - misc_3->icmpv4_type = 0; - else - misc_3->icmpv6_type = 0; - } - - if (icmp_code) { - u32 cur_val = MLX5_GET(ste_flex_parser_1, tag, - flex_parser_4); - MLX5_SET(ste_flex_parser_1, tag, flex_parser_4, - cur_val | (icmp_code << ICMP_CODE_OFFSET_FIRST_DW)); - if (is_ipv4) - misc_3->icmpv4_code = 0; - else - misc_3->icmpv6_code = 0; - } - break; - default: - return -EINVAL; - } - - switch (dw1_location) { - case 5: - if (icmp_header_data) { - MLX5_SET(ste_flex_parser_1, tag, flex_parser_5, - (icmp_header_data << ICMP_HEADER_DATA_OFFSET_SECOND_DW)); - if (is_ipv4) - misc_3->icmpv4_header_data = 0; - else - misc_3->icmpv6_header_data = 0; - } - break; - default: - return -EINVAL; - } - - return 0; -} - -int mlx5dr_ste_build_icmp(struct mlx5dr_ste_build *sb, +int mlx5dr_ste_build_icmp(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, struct mlx5dr_cmd_caps *caps, bool inner, bool rx) { - int ret; - - ret = dr_ste_build_flex_parser_1_bit_mask(mask, caps, sb->bit_mask); - if (ret) - return ret; - sb->rx = rx; sb->inner = inner; sb->caps = caps; - sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_1; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_flex_parser_1_tag; - - return 0; -} - -static void dr_ste_build_general_purpose_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2; - - DR_STE_SET_MASK_V(general_purpose, bit_mask, - general_purpose_lookup_field, misc_2_mask, - metadata_reg_a); -} - -static int dr_ste_build_general_purpose_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2; - - DR_STE_SET_TAG(general_purpose, tag, general_purpose_lookup_field, - misc_2_mask, metadata_reg_a); - - return 0; + return ste_ctx->build_icmp_init(sb, mask); } -void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_general_purpose_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_GENERAL_PURPOSE; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_general_purpose_tag; -} - -static void dr_ste_build_eth_l4_misc_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_misc3 *misc_3_mask = &value->misc3; - - if (inner) { - DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, seq_num, misc_3_mask, - inner_tcp_seq_num); - DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, ack_num, misc_3_mask, - inner_tcp_ack_num); - } else { - DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, seq_num, misc_3_mask, - outer_tcp_seq_num); - DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, ack_num, misc_3_mask, - outer_tcp_ack_num); - } + ste_ctx->build_general_purpose_init(sb, mask); } -static int dr_ste_build_eth_l4_misc_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc3 *misc3 = &value->misc3; - - if (sb->inner) { - DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, inner_tcp_seq_num); - DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, inner_tcp_ack_num); - } else { - DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, outer_tcp_seq_num); - DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, outer_tcp_ack_num); - } - - return 0; -} - -void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_eth_l4_misc_bit_mask(mask, inner, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4_MISC, rx, inner); - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_eth_l4_misc_tag; + ste_ctx->build_eth_l4_misc_init(sb, mask); } -static void -dr_ste_build_flex_parser_tnl_vxlan_gpe_bit_mask(struct mlx5dr_match_param *value, - bool inner, u8 *bit_mask) -{ - struct mlx5dr_match_misc3 *misc_3_mask = &value->misc3; - - DR_STE_SET_MASK_V(flex_parser_tnl_vxlan_gpe, bit_mask, - outer_vxlan_gpe_flags, - misc_3_mask, outer_vxlan_gpe_flags); - DR_STE_SET_MASK_V(flex_parser_tnl_vxlan_gpe, bit_mask, - outer_vxlan_gpe_next_protocol, - misc_3_mask, outer_vxlan_gpe_next_protocol); - DR_STE_SET_MASK_V(flex_parser_tnl_vxlan_gpe, bit_mask, - outer_vxlan_gpe_vni, - misc_3_mask, outer_vxlan_gpe_vni); -} - -static int -dr_ste_build_flex_parser_tnl_vxlan_gpe_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc3 *misc3 = &value->misc3; - - DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag, - outer_vxlan_gpe_flags, misc3, - outer_vxlan_gpe_flags); - DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag, - outer_vxlan_gpe_next_protocol, misc3, - outer_vxlan_gpe_next_protocol); - DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag, - outer_vxlan_gpe_vni, misc3, - outer_vxlan_gpe_vni); - - return 0; -} - -void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_flex_parser_tnl_vxlan_gpe_bit_mask(mask, inner, - sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_TNL_HEADER; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_flex_parser_tnl_vxlan_gpe_tag; + ste_ctx->build_tnl_vxlan_gpe_init(sb, mask); } -static void -dr_ste_build_flex_parser_tnl_geneve_bit_mask(struct mlx5dr_match_param *value, - u8 *bit_mask) -{ - struct mlx5dr_match_misc *misc_mask = &value->misc; - - DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask, - geneve_protocol_type, - misc_mask, geneve_protocol_type); - DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask, - geneve_oam, - misc_mask, geneve_oam); - DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask, - geneve_opt_len, - misc_mask, geneve_opt_len); - DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask, - geneve_vni, - misc_mask, geneve_vni); -} - -static int -dr_ste_build_flex_parser_tnl_geneve_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc *misc = &value->misc; - - DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, - geneve_protocol_type, misc, geneve_protocol_type); - DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, - geneve_oam, misc, geneve_oam); - DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, - geneve_opt_len, misc, geneve_opt_len); - DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, - geneve_vni, misc, geneve_vni); - - return 0; -} - -void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_flex_parser_tnl_geneve_bit_mask(mask, sb->bit_mask); sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_TNL_HEADER; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_flex_parser_tnl_geneve_tag; + ste_ctx->build_tnl_geneve_init(sb, mask); } -static void dr_ste_build_register_0_bit_mask(struct mlx5dr_match_param *value, - u8 *bit_mask) -{ - struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2; - - DR_STE_SET_MASK_V(register_0, bit_mask, register_0_h, - misc_2_mask, metadata_reg_c_0); - DR_STE_SET_MASK_V(register_0, bit_mask, register_0_l, - misc_2_mask, metadata_reg_c_1); - DR_STE_SET_MASK_V(register_0, bit_mask, register_1_h, - misc_2_mask, metadata_reg_c_2); - DR_STE_SET_MASK_V(register_0, bit_mask, register_1_l, - misc_2_mask, metadata_reg_c_3); -} - -static int dr_ste_build_register_0_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc2 *misc2 = &value->misc2; - - DR_STE_SET_TAG(register_0, tag, register_0_h, misc2, metadata_reg_c_0); - DR_STE_SET_TAG(register_0, tag, register_0_l, misc2, metadata_reg_c_1); - DR_STE_SET_TAG(register_0, tag, register_1_h, misc2, metadata_reg_c_2); - DR_STE_SET_TAG(register_0, tag, register_1_l, misc2, metadata_reg_c_3); - - return 0; -} - -void mlx5dr_ste_build_register_0(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_register_0(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_register_0_bit_mask(mask, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_0; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_register_0_tag; -} - -static void dr_ste_build_register_1_bit_mask(struct mlx5dr_match_param *value, - u8 *bit_mask) -{ - struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2; - - DR_STE_SET_MASK_V(register_1, bit_mask, register_2_h, - misc_2_mask, metadata_reg_c_4); - DR_STE_SET_MASK_V(register_1, bit_mask, register_2_l, - misc_2_mask, metadata_reg_c_5); - DR_STE_SET_MASK_V(register_1, bit_mask, register_3_h, - misc_2_mask, metadata_reg_c_6); - DR_STE_SET_MASK_V(register_1, bit_mask, register_3_l, - misc_2_mask, metadata_reg_c_7); + ste_ctx->build_register_0_init(sb, mask); } -static int dr_ste_build_register_1_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc2 *misc2 = &value->misc2; - - DR_STE_SET_TAG(register_1, tag, register_2_h, misc2, metadata_reg_c_4); - DR_STE_SET_TAG(register_1, tag, register_2_l, misc2, metadata_reg_c_5); - DR_STE_SET_TAG(register_1, tag, register_3_h, misc2, metadata_reg_c_6); - DR_STE_SET_TAG(register_1, tag, register_3_l, misc2, metadata_reg_c_7); - - return 0; -} - -void mlx5dr_ste_build_register_1(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_register_1(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx) { - dr_ste_build_register_1_bit_mask(mask, sb->bit_mask); - sb->rx = rx; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_1; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_register_1_tag; + ste_ctx->build_register_1_init(sb, mask); } -static void dr_ste_build_src_gvmi_qpn_bit_mask(struct mlx5dr_match_param *value, - u8 *bit_mask) -{ - struct mlx5dr_match_misc *misc_mask = &value->misc; - - DR_STE_SET_MASK(src_gvmi_qp, bit_mask, source_gvmi, misc_mask, source_port); - DR_STE_SET_MASK(src_gvmi_qp, bit_mask, source_qp, misc_mask, source_sqn); - misc_mask->source_eswitch_owner_vhca_id = 0; -} - -static int dr_ste_build_src_gvmi_qpn_tag(struct mlx5dr_match_param *value, - struct mlx5dr_ste_build *sb, - u8 *tag) -{ - struct mlx5dr_match_misc *misc = &value->misc; - struct mlx5dr_cmd_vport_cap *vport_cap; - struct mlx5dr_domain *dmn = sb->dmn; - struct mlx5dr_cmd_caps *caps; - u8 *bit_mask = sb->bit_mask; - bool source_gvmi_set; - - DR_STE_SET_TAG(src_gvmi_qp, tag, source_qp, misc, source_sqn); - - if (sb->vhca_id_valid) { - /* Find port GVMI based on the eswitch_owner_vhca_id */ - if (misc->source_eswitch_owner_vhca_id == dmn->info.caps.gvmi) - caps = &dmn->info.caps; - else if (dmn->peer_dmn && (misc->source_eswitch_owner_vhca_id == - dmn->peer_dmn->info.caps.gvmi)) - caps = &dmn->peer_dmn->info.caps; - else - return -EINVAL; - } else { - caps = &dmn->info.caps; - } - - vport_cap = mlx5dr_get_vport_cap(caps, misc->source_port); - if (!vport_cap) - return -EINVAL; - - source_gvmi_set = MLX5_GET(ste_src_gvmi_qp, bit_mask, source_gvmi); - if (vport_cap->vport_gvmi && source_gvmi_set) - MLX5_SET(ste_src_gvmi_qp, tag, source_gvmi, vport_cap->vport_gvmi); - - misc->source_eswitch_owner_vhca_id = 0; - misc->source_port = 0; - - return 0; -} - -void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, struct mlx5dr_domain *dmn, bool inner, bool rx) @@ -2276,12 +1119,21 @@ void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_build *sb, /* Set vhca_id_valid before we reset source_eswitch_owner_vhca_id */ sb->vhca_id_valid = mask->misc.source_eswitch_owner_vhca_id; - dr_ste_build_src_gvmi_qpn_bit_mask(mask, sb->bit_mask); - sb->rx = rx; sb->dmn = dmn; sb->inner = inner; - sb->lu_type = MLX5DR_STE_LU_TYPE_SRC_GVMI_AND_QP; - sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask); - sb->ste_build_tag_func = &dr_ste_build_src_gvmi_qpn_tag; + ste_ctx->build_src_gvmi_qpn_init(sb, mask); +} + +static struct mlx5dr_ste_ctx *mlx5dr_ste_ctx_arr[] = { + [MLX5_STEERING_FORMAT_CONNECTX_5] = &ste_ctx_v0, + [MLX5_STEERING_FORMAT_CONNECTX_6DX] = NULL, +}; + +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx(u8 version) +{ + if (version > MLX5_STEERING_FORMAT_CONNECTX_6DX) + return NULL; + + return mlx5dr_ste_ctx_arr[version]; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h new file mode 100644 index 000000000000..4a3d6a849991 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. */ + +#ifndef _DR_STE_ +#define _DR_STE_ + +#include "dr_types.h" + +#define STE_IPV4 0x1 +#define STE_IPV6 0x2 +#define STE_TCP 0x1 +#define STE_UDP 0x2 +#define STE_SPI 0x3 +#define IP_VERSION_IPV4 0x4 +#define IP_VERSION_IPV6 0x6 +#define STE_SVLAN 0x1 +#define STE_CVLAN 0x2 +#define HDR_LEN_L2_MACS 0xC +#define HDR_LEN_L2_VLAN 0x4 +#define HDR_LEN_L2_ETHER 0x2 +#define HDR_LEN_L2 (HDR_LEN_L2_MACS + HDR_LEN_L2_ETHER) +#define HDR_LEN_L2_W_VLAN (HDR_LEN_L2 + HDR_LEN_L2_VLAN) + +/* Set to STE a specific value using DR_STE_SET */ +#define DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, value) do { \ + if ((spec)->s_fname) { \ + MLX5_SET(ste_##lookup_type, tag, t_fname, value); \ + (spec)->s_fname = 0; \ + } \ +} while (0) + +/* Set to STE spec->s_fname to tag->t_fname set spec->s_fname as used */ +#define DR_STE_SET_TAG(lookup_type, tag, t_fname, spec, s_fname) \ + DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, spec->s_fname) + +/* Set to STE -1 to tag->t_fname and set spec->s_fname as used */ +#define DR_STE_SET_ONES(lookup_type, tag, t_fname, spec, s_fname) \ + DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, -1) + +#define DR_STE_SET_TCP_FLAGS(lookup_type, tag, spec) do { \ + MLX5_SET(ste_##lookup_type, tag, tcp_ns, !!((spec)->tcp_flags & (1 << 8))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_cwr, !!((spec)->tcp_flags & (1 << 7))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_ece, !!((spec)->tcp_flags & (1 << 6))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_urg, !!((spec)->tcp_flags & (1 << 5))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_ack, !!((spec)->tcp_flags & (1 << 4))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_psh, !!((spec)->tcp_flags & (1 << 3))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_rst, !!((spec)->tcp_flags & (1 << 2))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_syn, !!((spec)->tcp_flags & (1 << 1))); \ + MLX5_SET(ste_##lookup_type, tag, tcp_fin, !!((spec)->tcp_flags & (1 << 0))); \ +} while (0) + +#define DR_STE_SET_MPLS(lookup_type, mask, in_out, tag) do { \ + struct mlx5dr_match_misc2 *_mask = mask; \ + u8 *_tag = tag; \ + DR_STE_SET_TAG(lookup_type, _tag, mpls0_label, _mask, \ + in_out##_first_mpls_label);\ + DR_STE_SET_TAG(lookup_type, _tag, mpls0_s_bos, _mask, \ + in_out##_first_mpls_s_bos); \ + DR_STE_SET_TAG(lookup_type, _tag, mpls0_exp, _mask, \ + in_out##_first_mpls_exp); \ + DR_STE_SET_TAG(lookup_type, _tag, mpls0_ttl, _mask, \ + in_out##_first_mpls_ttl); \ +} while (0) + +#define DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(_misc) (\ + (_misc)->outer_first_mpls_over_gre_label || \ + (_misc)->outer_first_mpls_over_gre_exp || \ + (_misc)->outer_first_mpls_over_gre_s_bos || \ + (_misc)->outer_first_mpls_over_gre_ttl) + +#define DR_STE_IS_OUTER_MPLS_OVER_UDP_SET(_misc) (\ + (_misc)->outer_first_mpls_over_udp_label || \ + (_misc)->outer_first_mpls_over_udp_exp || \ + (_misc)->outer_first_mpls_over_udp_s_bos || \ + (_misc)->outer_first_mpls_over_udp_ttl) + +enum dr_ste_action_modify_type_l3 { + DR_STE_ACTION_MDFY_TYPE_L3_NONE = 0x0, + DR_STE_ACTION_MDFY_TYPE_L3_IPV4 = 0x1, + DR_STE_ACTION_MDFY_TYPE_L3_IPV6 = 0x2, +}; + +enum dr_ste_action_modify_type_l4 { + DR_STE_ACTION_MDFY_TYPE_L4_NONE = 0x0, + DR_STE_ACTION_MDFY_TYPE_L4_TCP = 0x1, + DR_STE_ACTION_MDFY_TYPE_L4_UDP = 0x2, +}; + +u16 mlx5dr_ste_conv_bit_to_byte_mask(u8 *bit_mask); + +#define DR_STE_CTX_BUILDER(fname) \ + ((*build_##fname##_init)(struct mlx5dr_ste_build *sb, \ + struct mlx5dr_match_param *mask)) + +struct mlx5dr_ste_ctx { + /* Builders */ + void DR_STE_CTX_BUILDER(eth_l2_src_dst); + void DR_STE_CTX_BUILDER(eth_l3_ipv6_src); + void DR_STE_CTX_BUILDER(eth_l3_ipv6_dst); + void DR_STE_CTX_BUILDER(eth_l3_ipv4_5_tuple); + void DR_STE_CTX_BUILDER(eth_l2_src); + void DR_STE_CTX_BUILDER(eth_l2_dst); + void DR_STE_CTX_BUILDER(eth_l2_tnl); + void DR_STE_CTX_BUILDER(eth_l3_ipv4_misc); + void DR_STE_CTX_BUILDER(eth_ipv6_l3_l4); + void DR_STE_CTX_BUILDER(mpls); + void DR_STE_CTX_BUILDER(tnl_gre); + void DR_STE_CTX_BUILDER(tnl_mpls); + int DR_STE_CTX_BUILDER(icmp); + void DR_STE_CTX_BUILDER(general_purpose); + void DR_STE_CTX_BUILDER(eth_l4_misc); + void DR_STE_CTX_BUILDER(tnl_vxlan_gpe); + void DR_STE_CTX_BUILDER(tnl_geneve); + void DR_STE_CTX_BUILDER(register_0); + void DR_STE_CTX_BUILDER(register_1); + void DR_STE_CTX_BUILDER(src_gvmi_qpn); + + /* Getters and Setters */ + void (*ste_init)(u8 *hw_ste_p, u16 lu_type, + u8 entry_type, u16 gvmi); + void (*set_next_lu_type)(u8 *hw_ste_p, u16 lu_type); + u16 (*get_next_lu_type)(u8 *hw_ste_p); + void (*set_miss_addr)(u8 *hw_ste_p, u64 miss_addr); + u64 (*get_miss_addr)(u8 *hw_ste_p); + void (*set_hit_addr)(u8 *hw_ste_p, u64 icm_addr, u32 ht_size); + void (*set_byte_mask)(u8 *hw_ste_p, u16 byte_mask); + u16 (*get_byte_mask)(u8 *hw_ste_p); + + /* Actions */ + void (*set_actions_rx)(struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *hw_ste_arr, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes); + void (*set_actions_tx)(struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *hw_ste_arr, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes); + u32 modify_field_arr_sz; + const struct mlx5dr_ste_action_modify_field *modify_field_arr; + void (*set_action_set)(u8 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data); + void (*set_action_add)(u8 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data); + void (*set_action_copy)(u8 *hw_action, + u8 dst_hw_field, + u8 dst_shifter, + u8 dst_len, + u8 src_hw_field, + u8 src_shifter); + int (*set_action_decap_l3_list)(void *data, + u32 data_sz, + u8 *hw_action, + u32 hw_action_sz, + u16 *used_hw_action_num); +}; + +extern struct mlx5dr_ste_ctx ste_ctx_v0; + +#endif /* _DR_STE_ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c new file mode 100644 index 000000000000..b76fdff08890 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c @@ -0,0 +1,1640 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. */ + +#include <linux/types.h> +#include <linux/crc32.h> +#include "dr_ste.h" + +#define SVLAN_ETHERTYPE 0x88a8 +#define DR_STE_ENABLE_FLOW_TAG BIT(31) + +enum dr_ste_v0_action_tunl { + DR_STE_TUNL_ACTION_NONE = 0, + DR_STE_TUNL_ACTION_ENABLE = 1, + DR_STE_TUNL_ACTION_DECAP = 2, + DR_STE_TUNL_ACTION_L3_DECAP = 3, + DR_STE_TUNL_ACTION_POP_VLAN = 4, +}; + +enum dr_ste_v0_action_type { + DR_STE_ACTION_TYPE_PUSH_VLAN = 1, + DR_STE_ACTION_TYPE_ENCAP_L3 = 3, + DR_STE_ACTION_TYPE_ENCAP = 4, +}; + +enum dr_ste_v0_action_mdfy_op { + DR_STE_ACTION_MDFY_OP_COPY = 0x1, + DR_STE_ACTION_MDFY_OP_SET = 0x2, + DR_STE_ACTION_MDFY_OP_ADD = 0x3, +}; + +#define DR_STE_CALC_LU_TYPE(lookup_type, rx, inner) \ + ((inner) ? DR_STE_V0_LU_TYPE_##lookup_type##_I : \ + (rx) ? DR_STE_V0_LU_TYPE_##lookup_type##_D : \ + DR_STE_V0_LU_TYPE_##lookup_type##_O) + +enum { + DR_STE_V0_LU_TYPE_NOP = 0x00, + DR_STE_V0_LU_TYPE_SRC_GVMI_AND_QP = 0x05, + DR_STE_V0_LU_TYPE_ETHL2_TUNNELING_I = 0x0a, + DR_STE_V0_LU_TYPE_ETHL2_DST_O = 0x06, + DR_STE_V0_LU_TYPE_ETHL2_DST_I = 0x07, + DR_STE_V0_LU_TYPE_ETHL2_DST_D = 0x1b, + DR_STE_V0_LU_TYPE_ETHL2_SRC_O = 0x08, + DR_STE_V0_LU_TYPE_ETHL2_SRC_I = 0x09, + DR_STE_V0_LU_TYPE_ETHL2_SRC_D = 0x1c, + DR_STE_V0_LU_TYPE_ETHL2_SRC_DST_O = 0x36, + DR_STE_V0_LU_TYPE_ETHL2_SRC_DST_I = 0x37, + DR_STE_V0_LU_TYPE_ETHL2_SRC_DST_D = 0x38, + DR_STE_V0_LU_TYPE_ETHL3_IPV6_DST_O = 0x0d, + DR_STE_V0_LU_TYPE_ETHL3_IPV6_DST_I = 0x0e, + DR_STE_V0_LU_TYPE_ETHL3_IPV6_DST_D = 0x1e, + DR_STE_V0_LU_TYPE_ETHL3_IPV6_SRC_O = 0x0f, + DR_STE_V0_LU_TYPE_ETHL3_IPV6_SRC_I = 0x10, + DR_STE_V0_LU_TYPE_ETHL3_IPV6_SRC_D = 0x1f, + DR_STE_V0_LU_TYPE_ETHL3_IPV4_5_TUPLE_O = 0x11, + DR_STE_V0_LU_TYPE_ETHL3_IPV4_5_TUPLE_I = 0x12, + DR_STE_V0_LU_TYPE_ETHL3_IPV4_5_TUPLE_D = 0x20, + DR_STE_V0_LU_TYPE_ETHL3_IPV4_MISC_O = 0x29, + DR_STE_V0_LU_TYPE_ETHL3_IPV4_MISC_I = 0x2a, + DR_STE_V0_LU_TYPE_ETHL3_IPV4_MISC_D = 0x2b, + DR_STE_V0_LU_TYPE_ETHL4_O = 0x13, + DR_STE_V0_LU_TYPE_ETHL4_I = 0x14, + DR_STE_V0_LU_TYPE_ETHL4_D = 0x21, + DR_STE_V0_LU_TYPE_ETHL4_MISC_O = 0x2c, + DR_STE_V0_LU_TYPE_ETHL4_MISC_I = 0x2d, + DR_STE_V0_LU_TYPE_ETHL4_MISC_D = 0x2e, + DR_STE_V0_LU_TYPE_MPLS_FIRST_O = 0x15, + DR_STE_V0_LU_TYPE_MPLS_FIRST_I = 0x24, + DR_STE_V0_LU_TYPE_MPLS_FIRST_D = 0x25, + DR_STE_V0_LU_TYPE_GRE = 0x16, + DR_STE_V0_LU_TYPE_FLEX_PARSER_0 = 0x22, + DR_STE_V0_LU_TYPE_FLEX_PARSER_1 = 0x23, + DR_STE_V0_LU_TYPE_FLEX_PARSER_TNL_HEADER = 0x19, + DR_STE_V0_LU_TYPE_GENERAL_PURPOSE = 0x18, + DR_STE_V0_LU_TYPE_STEERING_REGISTERS_0 = 0x2f, + DR_STE_V0_LU_TYPE_STEERING_REGISTERS_1 = 0x30, + DR_STE_V0_LU_TYPE_DONT_CARE = MLX5DR_STE_LU_TYPE_DONT_CARE, +}; + +enum { + DR_STE_V0_ACTION_MDFY_FLD_L2_0 = 0, + DR_STE_V0_ACTION_MDFY_FLD_L2_1 = 1, + DR_STE_V0_ACTION_MDFY_FLD_L2_2 = 2, + DR_STE_V0_ACTION_MDFY_FLD_L3_0 = 3, + DR_STE_V0_ACTION_MDFY_FLD_L3_1 = 4, + DR_STE_V0_ACTION_MDFY_FLD_L3_2 = 5, + DR_STE_V0_ACTION_MDFY_FLD_L3_3 = 6, + DR_STE_V0_ACTION_MDFY_FLD_L3_4 = 7, + DR_STE_V0_ACTION_MDFY_FLD_L4_0 = 8, + DR_STE_V0_ACTION_MDFY_FLD_L4_1 = 9, + DR_STE_V0_ACTION_MDFY_FLD_MPLS = 10, + DR_STE_V0_ACTION_MDFY_FLD_L2_TNL_0 = 11, + DR_STE_V0_ACTION_MDFY_FLD_REG_0 = 12, + DR_STE_V0_ACTION_MDFY_FLD_REG_1 = 13, + DR_STE_V0_ACTION_MDFY_FLD_REG_2 = 14, + DR_STE_V0_ACTION_MDFY_FLD_REG_3 = 15, + DR_STE_V0_ACTION_MDFY_FLD_L4_2 = 16, + DR_STE_V0_ACTION_MDFY_FLD_FLEX_0 = 17, + DR_STE_V0_ACTION_MDFY_FLD_FLEX_1 = 18, + DR_STE_V0_ACTION_MDFY_FLD_FLEX_2 = 19, + DR_STE_V0_ACTION_MDFY_FLD_FLEX_3 = 20, + DR_STE_V0_ACTION_MDFY_FLD_L2_TNL_1 = 21, + DR_STE_V0_ACTION_MDFY_FLD_METADATA = 22, + DR_STE_V0_ACTION_MDFY_FLD_RESERVED = 23, +}; + +static const struct mlx5dr_ste_action_modify_field dr_ste_v0_action_modify_field_arr[] = { + [MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_1, .start = 16, .end = 47, + }, + [MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_1, .start = 0, .end = 15, + }, + [MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_2, .start = 32, .end = 47, + }, + [MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_0, .start = 16, .end = 47, + }, + [MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_0, .start = 0, .end = 15, + }, + [MLX5_ACTION_IN_FIELD_OUT_IP_DSCP] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_1, .start = 0, .end = 5, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 48, .end = 56, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 0, .end = 15, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 16, .end = 31, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP, + }, + [MLX5_ACTION_IN_FIELD_OUT_IP_TTL] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_1, .start = 8, .end = 15, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4, + }, + [MLX5_ACTION_IN_FIELD_OUT_IPV6_HOPLIMIT] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_1, .start = 8, .end = 15, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 0, .end = 15, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_UDP, + }, + [MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 16, .end = 31, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_UDP, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_3, .start = 32, .end = 63, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_3, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_4, .start = 32, .end = 63, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_4, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 32, .end = 63, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_2, .start = 32, .end = 63, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_2, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV4] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV4] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 32, .end = 63, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_A] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_METADATA, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_B] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_METADATA, .start = 32, .end = 63, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_0] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_0, .start = 32, .end = 63, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_1] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_0, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_2] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_1, .start = 32, .end = 63, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_3] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_1, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_4] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_2, .start = 32, .end = 63, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_5] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_2, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_1, .start = 32, .end = 63, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_ACK_NUM] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_1, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_FIRST_VID] = { + .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_2, .start = 0, .end = 15, + }, +}; + +static void dr_ste_v0_set_entry_type(u8 *hw_ste_p, u8 entry_type) +{ + MLX5_SET(ste_general, hw_ste_p, entry_type, entry_type); +} + +static u8 dr_ste_v0_get_entry_type(u8 *hw_ste_p) +{ + return MLX5_GET(ste_general, hw_ste_p, entry_type); +} + +static void dr_ste_v0_set_miss_addr(u8 *hw_ste_p, u64 miss_addr) +{ + u64 index = miss_addr >> 6; + + /* Miss address for TX and RX STEs located in the same offsets */ + MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_39_32, index >> 26); + MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_31_6, index); +} + +static u64 dr_ste_v0_get_miss_addr(u8 *hw_ste_p) +{ + u64 index = + (MLX5_GET(ste_rx_steering_mult, hw_ste_p, miss_address_31_6) | + MLX5_GET(ste_rx_steering_mult, hw_ste_p, miss_address_39_32) << 26); + + return index << 6; +} + +static void dr_ste_v0_set_byte_mask(u8 *hw_ste_p, u16 byte_mask) +{ + MLX5_SET(ste_general, hw_ste_p, byte_mask, byte_mask); +} + +static u16 dr_ste_v0_get_byte_mask(u8 *hw_ste_p) +{ + return MLX5_GET(ste_general, hw_ste_p, byte_mask); +} + +static void dr_ste_v0_set_lu_type(u8 *hw_ste_p, u16 lu_type) +{ + MLX5_SET(ste_general, hw_ste_p, entry_sub_type, lu_type); +} + +static void dr_ste_v0_set_next_lu_type(u8 *hw_ste_p, u16 lu_type) +{ + MLX5_SET(ste_general, hw_ste_p, next_lu_type, lu_type); +} + +static u16 dr_ste_v0_get_next_lu_type(u8 *hw_ste_p) +{ + return MLX5_GET(ste_general, hw_ste_p, next_lu_type); +} + +static void dr_ste_v0_set_hit_gvmi(u8 *hw_ste_p, u16 gvmi) +{ + MLX5_SET(ste_general, hw_ste_p, next_table_base_63_48, gvmi); +} + +static void dr_ste_v0_set_hit_addr(u8 *hw_ste_p, u64 icm_addr, u32 ht_size) +{ + u64 index = (icm_addr >> 5) | ht_size; + + MLX5_SET(ste_general, hw_ste_p, next_table_base_39_32_size, index >> 27); + MLX5_SET(ste_general, hw_ste_p, next_table_base_31_5_size, index); +} + +static void dr_ste_v0_init(u8 *hw_ste_p, u16 lu_type, + u8 entry_type, u16 gvmi) +{ + dr_ste_v0_set_entry_type(hw_ste_p, entry_type); + dr_ste_v0_set_lu_type(hw_ste_p, lu_type); + dr_ste_v0_set_next_lu_type(hw_ste_p, MLX5DR_STE_LU_TYPE_DONT_CARE); + + /* Set GVMI once, this is the same for RX/TX + * bits 63_48 of next table base / miss address encode the next GVMI + */ + MLX5_SET(ste_rx_steering_mult, hw_ste_p, gvmi, gvmi); + MLX5_SET(ste_rx_steering_mult, hw_ste_p, next_table_base_63_48, gvmi); + MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_63_48, gvmi); +} + +static void dr_ste_v0_rx_set_flow_tag(u8 *hw_ste_p, u32 flow_tag) +{ + MLX5_SET(ste_rx_steering_mult, hw_ste_p, qp_list_pointer, + DR_STE_ENABLE_FLOW_TAG | flow_tag); +} + +static void dr_ste_v0_set_counter_id(u8 *hw_ste_p, u32 ctr_id) +{ + /* This can be used for both rx_steering_mult and for sx_transmit */ + MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_15_0, ctr_id); + MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_23_16, ctr_id >> 16); +} + +static void dr_ste_v0_set_go_back_bit(u8 *hw_ste_p) +{ + MLX5_SET(ste_sx_transmit, hw_ste_p, go_back, 1); +} + +static void dr_ste_v0_set_tx_push_vlan(u8 *hw_ste_p, u32 vlan_hdr, + bool go_back) +{ + MLX5_SET(ste_sx_transmit, hw_ste_p, action_type, + DR_STE_ACTION_TYPE_PUSH_VLAN); + MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, vlan_hdr); + /* Due to HW limitation we need to set this bit, otherwise reforamt + + * push vlan will not work. + */ + if (go_back) + dr_ste_v0_set_go_back_bit(hw_ste_p); +} + +static void dr_ste_v0_set_tx_encap(void *hw_ste_p, u32 reformat_id, + int size, bool encap_l3) +{ + MLX5_SET(ste_sx_transmit, hw_ste_p, action_type, + encap_l3 ? DR_STE_ACTION_TYPE_ENCAP_L3 : DR_STE_ACTION_TYPE_ENCAP); + /* The hardware expects here size in words (2 byte) */ + MLX5_SET(ste_sx_transmit, hw_ste_p, action_description, size / 2); + MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, reformat_id); +} + +static void dr_ste_v0_set_rx_decap(u8 *hw_ste_p) +{ + MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action, + DR_STE_TUNL_ACTION_DECAP); +} + +static void dr_ste_v0_set_rx_pop_vlan(u8 *hw_ste_p) +{ + MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action, + DR_STE_TUNL_ACTION_POP_VLAN); +} + +static void dr_ste_v0_set_rx_decap_l3(u8 *hw_ste_p, bool vlan) +{ + MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action, + DR_STE_TUNL_ACTION_L3_DECAP); + MLX5_SET(ste_modify_packet, hw_ste_p, action_description, vlan ? 1 : 0); +} + +static void dr_ste_v0_set_rewrite_actions(u8 *hw_ste_p, u16 num_of_actions, + u32 re_write_index) +{ + MLX5_SET(ste_modify_packet, hw_ste_p, number_of_re_write_actions, + num_of_actions); + MLX5_SET(ste_modify_packet, hw_ste_p, header_re_write_actions_pointer, + re_write_index); +} + +static void dr_ste_v0_arr_init_next(u8 **last_ste, + u32 *added_stes, + enum mlx5dr_ste_entry_type entry_type, + u16 gvmi) +{ + (*added_stes)++; + *last_ste += DR_STE_SIZE; + dr_ste_v0_init(*last_ste, MLX5DR_STE_LU_TYPE_DONT_CARE, + entry_type, gvmi); +} + +static void +dr_ste_v0_set_actions_tx(struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes) +{ + bool encap = action_type_set[DR_ACTION_TYP_L2_TO_TNL_L2] || + action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]; + + /* We want to make sure the modify header comes before L2 + * encapsulation. The reason for that is that we support + * modify headers for outer headers only + */ + if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) { + dr_ste_v0_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT); + dr_ste_v0_set_rewrite_actions(last_ste, + attr->modify_actions, + attr->modify_index); + } + + if (action_type_set[DR_ACTION_TYP_PUSH_VLAN]) { + int i; + + for (i = 0; i < attr->vlans.count; i++) { + if (i || action_type_set[DR_ACTION_TYP_MODIFY_HDR]) + dr_ste_v0_arr_init_next(&last_ste, + added_stes, + MLX5DR_STE_TYPE_TX, + attr->gvmi); + + dr_ste_v0_set_tx_push_vlan(last_ste, + attr->vlans.headers[i], + encap); + } + } + + if (encap) { + /* Modify header and encapsulation require a different STEs. + * Since modify header STE format doesn't support encapsulation + * tunneling_action. + */ + if (action_type_set[DR_ACTION_TYP_MODIFY_HDR] || + action_type_set[DR_ACTION_TYP_PUSH_VLAN]) + dr_ste_v0_arr_init_next(&last_ste, + added_stes, + MLX5DR_STE_TYPE_TX, + attr->gvmi); + + dr_ste_v0_set_tx_encap(last_ste, + attr->reformat_id, + attr->reformat_size, + action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]); + /* Whenever prio_tag_required enabled, we can be sure that the + * previous table (ACL) already push vlan to our packet, + * And due to HW limitation we need to set this bit, otherwise + * push vlan + reformat will not work. + */ + if (MLX5_CAP_GEN(dmn->mdev, prio_tag_required)) + dr_ste_v0_set_go_back_bit(last_ste); + } + + if (action_type_set[DR_ACTION_TYP_CTR]) + dr_ste_v0_set_counter_id(last_ste, attr->ctr_id); + + dr_ste_v0_set_hit_gvmi(last_ste, attr->hit_gvmi); + dr_ste_v0_set_hit_addr(last_ste, attr->final_icm_addr, 1); +} + +static void +dr_ste_v0_set_actions_rx(struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes) +{ + if (action_type_set[DR_ACTION_TYP_CTR]) + dr_ste_v0_set_counter_id(last_ste, attr->ctr_id); + + if (action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2]) { + dr_ste_v0_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT); + dr_ste_v0_set_rx_decap_l3(last_ste, attr->decap_with_vlan); + dr_ste_v0_set_rewrite_actions(last_ste, + attr->decap_actions, + attr->decap_index); + } + + if (action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2]) + dr_ste_v0_set_rx_decap(last_ste); + + if (action_type_set[DR_ACTION_TYP_POP_VLAN]) { + int i; + + for (i = 0; i < attr->vlans.count; i++) { + if (i || + action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2] || + action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2]) + dr_ste_v0_arr_init_next(&last_ste, + added_stes, + MLX5DR_STE_TYPE_RX, + attr->gvmi); + + dr_ste_v0_set_rx_pop_vlan(last_ste); + } + } + + if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) { + if (dr_ste_v0_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT) + dr_ste_v0_arr_init_next(&last_ste, + added_stes, + MLX5DR_STE_TYPE_MODIFY_PKT, + attr->gvmi); + else + dr_ste_v0_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT); + + dr_ste_v0_set_rewrite_actions(last_ste, + attr->modify_actions, + attr->modify_index); + } + + if (action_type_set[DR_ACTION_TYP_TAG]) { + if (dr_ste_v0_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT) + dr_ste_v0_arr_init_next(&last_ste, + added_stes, + MLX5DR_STE_TYPE_RX, + attr->gvmi); + + dr_ste_v0_rx_set_flow_tag(last_ste, attr->flow_tag); + } + + dr_ste_v0_set_hit_gvmi(last_ste, attr->hit_gvmi); + dr_ste_v0_set_hit_addr(last_ste, attr->final_icm_addr, 1); +} + +static void dr_ste_v0_set_action_set(u8 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data) +{ + length = (length == 32) ? 0 : length; + MLX5_SET(dr_action_hw_set, hw_action, opcode, DR_STE_ACTION_MDFY_OP_SET); + MLX5_SET(dr_action_hw_set, hw_action, destination_field_code, hw_field); + MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, shifter); + MLX5_SET(dr_action_hw_set, hw_action, destination_length, length); + MLX5_SET(dr_action_hw_set, hw_action, inline_data, data); +} + +static void dr_ste_v0_set_action_add(u8 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data) +{ + length = (length == 32) ? 0 : length; + MLX5_SET(dr_action_hw_set, hw_action, opcode, DR_STE_ACTION_MDFY_OP_ADD); + MLX5_SET(dr_action_hw_set, hw_action, destination_field_code, hw_field); + MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, shifter); + MLX5_SET(dr_action_hw_set, hw_action, destination_length, length); + MLX5_SET(dr_action_hw_set, hw_action, inline_data, data); +} + +static void dr_ste_v0_set_action_copy(u8 *hw_action, + u8 dst_hw_field, + u8 dst_shifter, + u8 dst_len, + u8 src_hw_field, + u8 src_shifter) +{ + MLX5_SET(dr_action_hw_copy, hw_action, opcode, DR_STE_ACTION_MDFY_OP_COPY); + MLX5_SET(dr_action_hw_copy, hw_action, destination_field_code, dst_hw_field); + MLX5_SET(dr_action_hw_copy, hw_action, destination_left_shifter, dst_shifter); + MLX5_SET(dr_action_hw_copy, hw_action, destination_length, dst_len); + MLX5_SET(dr_action_hw_copy, hw_action, source_field_code, src_hw_field); + MLX5_SET(dr_action_hw_copy, hw_action, source_left_shifter, src_shifter); +} + +#define DR_STE_DECAP_L3_MIN_ACTION_NUM 5 + +static int +dr_ste_v0_set_action_decap_l3_list(void *data, u32 data_sz, + u8 *hw_action, u32 hw_action_sz, + u16 *used_hw_action_num) +{ + struct mlx5_ifc_l2_hdr_bits *l2_hdr = data; + u32 hw_action_num; + int required_actions; + u32 hdr_fld_4b; + u16 hdr_fld_2b; + u16 vlan_type; + bool vlan; + + vlan = (data_sz != HDR_LEN_L2); + hw_action_num = hw_action_sz / MLX5_ST_SZ_BYTES(dr_action_hw_set); + required_actions = DR_STE_DECAP_L3_MIN_ACTION_NUM + !!vlan; + + if (hw_action_num < required_actions) + return -ENOMEM; + + /* dmac_47_16 */ + MLX5_SET(dr_action_hw_set, hw_action, + opcode, DR_STE_ACTION_MDFY_OP_SET); + MLX5_SET(dr_action_hw_set, hw_action, + destination_length, 0); + MLX5_SET(dr_action_hw_set, hw_action, + destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_0); + MLX5_SET(dr_action_hw_set, hw_action, + destination_left_shifter, 16); + hdr_fld_4b = MLX5_GET(l2_hdr, l2_hdr, dmac_47_16); + MLX5_SET(dr_action_hw_set, hw_action, + inline_data, hdr_fld_4b); + hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set); + + /* smac_47_16 */ + MLX5_SET(dr_action_hw_set, hw_action, + opcode, DR_STE_ACTION_MDFY_OP_SET); + MLX5_SET(dr_action_hw_set, hw_action, + destination_length, 0); + MLX5_SET(dr_action_hw_set, hw_action, + destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_1); + MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, 16); + hdr_fld_4b = (MLX5_GET(l2_hdr, l2_hdr, smac_31_0) >> 16 | + MLX5_GET(l2_hdr, l2_hdr, smac_47_32) << 16); + MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_4b); + hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set); + + /* dmac_15_0 */ + MLX5_SET(dr_action_hw_set, hw_action, + opcode, DR_STE_ACTION_MDFY_OP_SET); + MLX5_SET(dr_action_hw_set, hw_action, + destination_length, 16); + MLX5_SET(dr_action_hw_set, hw_action, + destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_0); + MLX5_SET(dr_action_hw_set, hw_action, + destination_left_shifter, 0); + hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, dmac_15_0); + MLX5_SET(dr_action_hw_set, hw_action, + inline_data, hdr_fld_2b); + hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set); + + /* ethertype + (optional) vlan */ + MLX5_SET(dr_action_hw_set, hw_action, + opcode, DR_STE_ACTION_MDFY_OP_SET); + MLX5_SET(dr_action_hw_set, hw_action, + destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_2); + MLX5_SET(dr_action_hw_set, hw_action, + destination_left_shifter, 32); + if (!vlan) { + hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype); + MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_2b); + MLX5_SET(dr_action_hw_set, hw_action, destination_length, 16); + } else { + hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype); + vlan_type = hdr_fld_2b == SVLAN_ETHERTYPE ? DR_STE_SVLAN : DR_STE_CVLAN; + hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan); + hdr_fld_4b = (vlan_type << 16) | hdr_fld_2b; + MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_4b); + MLX5_SET(dr_action_hw_set, hw_action, destination_length, 18); + } + hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set); + + /* smac_15_0 */ + MLX5_SET(dr_action_hw_set, hw_action, + opcode, DR_STE_ACTION_MDFY_OP_SET); + MLX5_SET(dr_action_hw_set, hw_action, + destination_length, 16); + MLX5_SET(dr_action_hw_set, hw_action, + destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_1); + MLX5_SET(dr_action_hw_set, hw_action, + destination_left_shifter, 0); + hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, smac_31_0); + MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_2b); + hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set); + + if (vlan) { + MLX5_SET(dr_action_hw_set, hw_action, + opcode, DR_STE_ACTION_MDFY_OP_SET); + hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan_type); + MLX5_SET(dr_action_hw_set, hw_action, + inline_data, hdr_fld_2b); + MLX5_SET(dr_action_hw_set, hw_action, + destination_length, 16); + MLX5_SET(dr_action_hw_set, hw_action, + destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_2); + MLX5_SET(dr_action_hw_set, hw_action, + destination_left_shifter, 0); + } + + *used_hw_action_num = required_actions; + + return 0; +} + +static void +dr_ste_v0_build_eth_l2_src_dst_bit_mask(struct mlx5dr_match_param *value, + bool inner, u8 *bit_mask) +{ + struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, dmac_47_16, mask, dmac_47_16); + DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, dmac_15_0, mask, dmac_15_0); + + if (mask->smac_47_16 || mask->smac_15_0) { + MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_47_32, + mask->smac_47_16 >> 16); + MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_31_0, + mask->smac_47_16 << 16 | mask->smac_15_0); + mask->smac_47_16 = 0; + mask->smac_15_0 = 0; + } + + DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, first_vlan_id, mask, first_vid); + DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, first_cfi, mask, first_cfi); + DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, first_priority, mask, first_prio); + DR_STE_SET_ONES(eth_l2_src_dst, bit_mask, l3_type, mask, ip_version); + + if (mask->cvlan_tag) { + MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1); + mask->cvlan_tag = 0; + } else if (mask->svlan_tag) { + MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1); + mask->svlan_tag = 0; + } +} + +static int +dr_ste_v0_build_eth_l2_src_dst_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_47_16, spec, dmac_47_16); + DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_15_0, spec, dmac_15_0); + + if (spec->smac_47_16 || spec->smac_15_0) { + MLX5_SET(ste_eth_l2_src_dst, tag, smac_47_32, + spec->smac_47_16 >> 16); + MLX5_SET(ste_eth_l2_src_dst, tag, smac_31_0, + spec->smac_47_16 << 16 | spec->smac_15_0); + spec->smac_47_16 = 0; + spec->smac_15_0 = 0; + } + + if (spec->ip_version) { + if (spec->ip_version == IP_VERSION_IPV4) { + MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV4); + spec->ip_version = 0; + } else if (spec->ip_version == IP_VERSION_IPV6) { + MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV6); + spec->ip_version = 0; + } else { + return -EINVAL; + } + } + + DR_STE_SET_TAG(eth_l2_src_dst, tag, first_vlan_id, spec, first_vid); + DR_STE_SET_TAG(eth_l2_src_dst, tag, first_cfi, spec, first_cfi); + DR_STE_SET_TAG(eth_l2_src_dst, tag, first_priority, spec, first_prio); + + if (spec->cvlan_tag) { + MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_CVLAN); + spec->cvlan_tag = 0; + } else if (spec->svlan_tag) { + MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_SVLAN); + spec->svlan_tag = 0; + } + return 0; +} + +static void +dr_ste_v0_build_eth_l2_src_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l2_src_dst_bit_mask(mask, sb->inner, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC_DST, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_src_dst_tag; +} + +static int +dr_ste_v0_build_eth_l3_ipv6_dst_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_127_96, spec, dst_ip_127_96); + DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_95_64, spec, dst_ip_95_64); + DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_63_32, spec, dst_ip_63_32); + DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_31_0, spec, dst_ip_31_0); + + return 0; +} + +static void +dr_ste_v0_build_eth_l3_ipv6_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l3_ipv6_dst_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_DST, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv6_dst_tag; +} + +static int +dr_ste_v0_build_eth_l3_ipv6_src_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_127_96, spec, src_ip_127_96); + DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_95_64, spec, src_ip_95_64); + DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_63_32, spec, src_ip_63_32); + DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_31_0, spec, src_ip_31_0); + + return 0; +} + +static void +dr_ste_v0_build_eth_l3_ipv6_src_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l3_ipv6_src_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_SRC, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv6_src_tag; +} + +static int +dr_ste_v0_build_eth_l3_ipv4_5_tuple_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_address, spec, dst_ip_31_0); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_address, spec, src_ip_31_0); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, tcp_dport); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, udp_dport); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, tcp_sport); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, udp_sport); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, protocol, spec, ip_protocol); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, fragmented, spec, frag); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, dscp, spec, ip_dscp); + DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, ecn, spec, ip_ecn); + + if (spec->tcp_flags) { + DR_STE_SET_TCP_FLAGS(eth_l3_ipv4_5_tuple, tag, spec); + spec->tcp_flags = 0; + } + + return 0; +} + +static void +dr_ste_v0_build_eth_l3_ipv4_5_tuple_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l3_ipv4_5_tuple_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_5_TUPLE, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv4_5_tuple_tag; +} + +static void +dr_ste_v0_build_eth_l2_src_or_dst_bit_mask(struct mlx5dr_match_param *value, + bool inner, u8 *bit_mask) +{ + struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; + struct mlx5dr_match_misc *misc_mask = &value->misc; + + DR_STE_SET_TAG(eth_l2_src, bit_mask, first_vlan_id, mask, first_vid); + DR_STE_SET_TAG(eth_l2_src, bit_mask, first_cfi, mask, first_cfi); + DR_STE_SET_TAG(eth_l2_src, bit_mask, first_priority, mask, first_prio); + DR_STE_SET_TAG(eth_l2_src, bit_mask, ip_fragmented, mask, frag); + DR_STE_SET_TAG(eth_l2_src, bit_mask, l3_ethertype, mask, ethertype); + DR_STE_SET_ONES(eth_l2_src, bit_mask, l3_type, mask, ip_version); + + if (mask->svlan_tag || mask->cvlan_tag) { + MLX5_SET(ste_eth_l2_src, bit_mask, first_vlan_qualifier, -1); + mask->cvlan_tag = 0; + mask->svlan_tag = 0; + } + + if (inner) { + if (misc_mask->inner_second_cvlan_tag || + misc_mask->inner_second_svlan_tag) { + MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1); + misc_mask->inner_second_cvlan_tag = 0; + misc_mask->inner_second_svlan_tag = 0; + } + + DR_STE_SET_TAG(eth_l2_src, bit_mask, + second_vlan_id, misc_mask, inner_second_vid); + DR_STE_SET_TAG(eth_l2_src, bit_mask, + second_cfi, misc_mask, inner_second_cfi); + DR_STE_SET_TAG(eth_l2_src, bit_mask, + second_priority, misc_mask, inner_second_prio); + } else { + if (misc_mask->outer_second_cvlan_tag || + misc_mask->outer_second_svlan_tag) { + MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1); + misc_mask->outer_second_cvlan_tag = 0; + misc_mask->outer_second_svlan_tag = 0; + } + + DR_STE_SET_TAG(eth_l2_src, bit_mask, + second_vlan_id, misc_mask, outer_second_vid); + DR_STE_SET_TAG(eth_l2_src, bit_mask, + second_cfi, misc_mask, outer_second_cfi); + DR_STE_SET_TAG(eth_l2_src, bit_mask, + second_priority, misc_mask, outer_second_prio); + } +} + +static int +dr_ste_v0_build_eth_l2_src_or_dst_tag(struct mlx5dr_match_param *value, + bool inner, u8 *tag) +{ + struct mlx5dr_match_spec *spec = inner ? &value->inner : &value->outer; + struct mlx5dr_match_misc *misc_spec = &value->misc; + + DR_STE_SET_TAG(eth_l2_src, tag, first_vlan_id, spec, first_vid); + DR_STE_SET_TAG(eth_l2_src, tag, first_cfi, spec, first_cfi); + DR_STE_SET_TAG(eth_l2_src, tag, first_priority, spec, first_prio); + DR_STE_SET_TAG(eth_l2_src, tag, ip_fragmented, spec, frag); + DR_STE_SET_TAG(eth_l2_src, tag, l3_ethertype, spec, ethertype); + + if (spec->ip_version) { + if (spec->ip_version == IP_VERSION_IPV4) { + MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV4); + spec->ip_version = 0; + } else if (spec->ip_version == IP_VERSION_IPV6) { + MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV6); + spec->ip_version = 0; + } else { + return -EINVAL; + } + } + + if (spec->cvlan_tag) { + MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_CVLAN); + spec->cvlan_tag = 0; + } else if (spec->svlan_tag) { + MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_SVLAN); + spec->svlan_tag = 0; + } + + if (inner) { + if (misc_spec->inner_second_cvlan_tag) { + MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN); + misc_spec->inner_second_cvlan_tag = 0; + } else if (misc_spec->inner_second_svlan_tag) { + MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN); + misc_spec->inner_second_svlan_tag = 0; + } + + DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, inner_second_vid); + DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, inner_second_cfi); + DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, inner_second_prio); + } else { + if (misc_spec->outer_second_cvlan_tag) { + MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN); + misc_spec->outer_second_cvlan_tag = 0; + } else if (misc_spec->outer_second_svlan_tag) { + MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN); + misc_spec->outer_second_svlan_tag = 0; + } + DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, outer_second_vid); + DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, outer_second_cfi); + DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, outer_second_prio); + } + + return 0; +} + +static void +dr_ste_v0_build_eth_l2_src_bit_mask(struct mlx5dr_match_param *value, + bool inner, u8 *bit_mask) +{ + struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l2_src, bit_mask, smac_47_16, mask, smac_47_16); + DR_STE_SET_TAG(eth_l2_src, bit_mask, smac_15_0, mask, smac_15_0); + + dr_ste_v0_build_eth_l2_src_or_dst_bit_mask(value, inner, bit_mask); +} + +static int +dr_ste_v0_build_eth_l2_src_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l2_src, tag, smac_47_16, spec, smac_47_16); + DR_STE_SET_TAG(eth_l2_src, tag, smac_15_0, spec, smac_15_0); + + return dr_ste_v0_build_eth_l2_src_or_dst_tag(value, sb->inner, tag); +} + +static void +dr_ste_v0_build_eth_l2_src_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l2_src_bit_mask(mask, sb->inner, sb->bit_mask); + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_src_tag; +} + +static void +dr_ste_v0_build_eth_l2_dst_bit_mask(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *bit_mask) +{ + struct mlx5dr_match_spec *mask = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l2_dst, bit_mask, dmac_47_16, mask, dmac_47_16); + DR_STE_SET_TAG(eth_l2_dst, bit_mask, dmac_15_0, mask, dmac_15_0); + + dr_ste_v0_build_eth_l2_src_or_dst_bit_mask(value, sb->inner, bit_mask); +} + +static int +dr_ste_v0_build_eth_l2_dst_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l2_dst, tag, dmac_47_16, spec, dmac_47_16); + DR_STE_SET_TAG(eth_l2_dst, tag, dmac_15_0, spec, dmac_15_0); + + return dr_ste_v0_build_eth_l2_src_or_dst_tag(value, sb->inner, tag); +} + +static void +dr_ste_v0_build_eth_l2_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l2_dst_bit_mask(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_DST, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_dst_tag; +} + +static void +dr_ste_v0_build_eth_l2_tnl_bit_mask(struct mlx5dr_match_param *value, + bool inner, u8 *bit_mask) +{ + struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer; + struct mlx5dr_match_misc *misc = &value->misc; + + DR_STE_SET_TAG(eth_l2_tnl, bit_mask, dmac_47_16, mask, dmac_47_16); + DR_STE_SET_TAG(eth_l2_tnl, bit_mask, dmac_15_0, mask, dmac_15_0); + DR_STE_SET_TAG(eth_l2_tnl, bit_mask, first_vlan_id, mask, first_vid); + DR_STE_SET_TAG(eth_l2_tnl, bit_mask, first_cfi, mask, first_cfi); + DR_STE_SET_TAG(eth_l2_tnl, bit_mask, first_priority, mask, first_prio); + DR_STE_SET_TAG(eth_l2_tnl, bit_mask, ip_fragmented, mask, frag); + DR_STE_SET_TAG(eth_l2_tnl, bit_mask, l3_ethertype, mask, ethertype); + DR_STE_SET_ONES(eth_l2_tnl, bit_mask, l3_type, mask, ip_version); + + if (misc->vxlan_vni) { + MLX5_SET(ste_eth_l2_tnl, bit_mask, + l2_tunneling_network_id, (misc->vxlan_vni << 8)); + misc->vxlan_vni = 0; + } + + if (mask->svlan_tag || mask->cvlan_tag) { + MLX5_SET(ste_eth_l2_tnl, bit_mask, first_vlan_qualifier, -1); + mask->cvlan_tag = 0; + mask->svlan_tag = 0; + } +} + +static int +dr_ste_v0_build_eth_l2_tnl_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + struct mlx5dr_match_misc *misc = &value->misc; + + DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_47_16, spec, dmac_47_16); + DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_15_0, spec, dmac_15_0); + DR_STE_SET_TAG(eth_l2_tnl, tag, first_vlan_id, spec, first_vid); + DR_STE_SET_TAG(eth_l2_tnl, tag, first_cfi, spec, first_cfi); + DR_STE_SET_TAG(eth_l2_tnl, tag, ip_fragmented, spec, frag); + DR_STE_SET_TAG(eth_l2_tnl, tag, first_priority, spec, first_prio); + DR_STE_SET_TAG(eth_l2_tnl, tag, l3_ethertype, spec, ethertype); + + if (misc->vxlan_vni) { + MLX5_SET(ste_eth_l2_tnl, tag, l2_tunneling_network_id, + (misc->vxlan_vni << 8)); + misc->vxlan_vni = 0; + } + + if (spec->cvlan_tag) { + MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_CVLAN); + spec->cvlan_tag = 0; + } else if (spec->svlan_tag) { + MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_SVLAN); + spec->svlan_tag = 0; + } + + if (spec->ip_version) { + if (spec->ip_version == IP_VERSION_IPV4) { + MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV4); + spec->ip_version = 0; + } else if (spec->ip_version == IP_VERSION_IPV6) { + MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV6); + spec->ip_version = 0; + } else { + return -EINVAL; + } + } + + return 0; +} + +static void +dr_ste_v0_build_eth_l2_tnl_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l2_tnl_bit_mask(mask, sb->inner, sb->bit_mask); + + sb->lu_type = DR_STE_V0_LU_TYPE_ETHL2_TUNNELING_I; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_tnl_tag; +} + +static int +dr_ste_v0_build_eth_l3_ipv4_misc_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l3_ipv4_misc, tag, time_to_live, spec, ttl_hoplimit); + + return 0; +} + +static void +dr_ste_v0_build_eth_l3_ipv4_misc_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l3_ipv4_misc_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_MISC, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv4_misc_tag; +} + +static int +dr_ste_v0_build_eth_ipv6_l3_l4_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; + + DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, tcp_dport); + DR_STE_SET_TAG(eth_l4, tag, src_port, spec, tcp_sport); + DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, udp_dport); + DR_STE_SET_TAG(eth_l4, tag, src_port, spec, udp_sport); + DR_STE_SET_TAG(eth_l4, tag, protocol, spec, ip_protocol); + DR_STE_SET_TAG(eth_l4, tag, fragmented, spec, frag); + DR_STE_SET_TAG(eth_l4, tag, dscp, spec, ip_dscp); + DR_STE_SET_TAG(eth_l4, tag, ecn, spec, ip_ecn); + DR_STE_SET_TAG(eth_l4, tag, ipv6_hop_limit, spec, ttl_hoplimit); + + if (spec->tcp_flags) { + DR_STE_SET_TCP_FLAGS(eth_l4, tag, spec); + spec->tcp_flags = 0; + } + + return 0; +} + +static void +dr_ste_v0_build_eth_ipv6_l3_l4_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_ipv6_l3_l4_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_ipv6_l3_l4_tag; +} + +static int +dr_ste_v0_build_mpls_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc2 *misc2 = &value->misc2; + + if (sb->inner) + DR_STE_SET_MPLS(mpls, misc2, inner, tag); + else + DR_STE_SET_MPLS(mpls, misc2, outer, tag); + + return 0; +} + +static void +dr_ste_v0_build_mpls_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_mpls_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(MPLS_FIRST, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_mpls_tag; +} + +static int +dr_ste_v0_build_tnl_gre_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc *misc = &value->misc; + + DR_STE_SET_TAG(gre, tag, gre_protocol, misc, gre_protocol); + + DR_STE_SET_TAG(gre, tag, gre_k_present, misc, gre_k_present); + DR_STE_SET_TAG(gre, tag, gre_key_h, misc, gre_key_h); + DR_STE_SET_TAG(gre, tag, gre_key_l, misc, gre_key_l); + + DR_STE_SET_TAG(gre, tag, gre_c_present, misc, gre_c_present); + + DR_STE_SET_TAG(gre, tag, gre_s_present, misc, gre_s_present); + + return 0; +} + +static void +dr_ste_v0_build_tnl_gre_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_tnl_gre_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_V0_LU_TYPE_GRE; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_tnl_gre_tag; +} + +static int +dr_ste_v0_build_tnl_mpls_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc2 *misc_2 = &value->misc2; + + if (DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(misc_2)) { + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label, + misc_2, outer_first_mpls_over_gre_label); + + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp, + misc_2, outer_first_mpls_over_gre_exp); + + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos, + misc_2, outer_first_mpls_over_gre_s_bos); + + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl, + misc_2, outer_first_mpls_over_gre_ttl); + } else { + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label, + misc_2, outer_first_mpls_over_udp_label); + + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp, + misc_2, outer_first_mpls_over_udp_exp); + + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos, + misc_2, outer_first_mpls_over_udp_s_bos); + + DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl, + misc_2, outer_first_mpls_over_udp_ttl); + } + return 0; +} + +static void +dr_ste_v0_build_tnl_mpls_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_tnl_mpls_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_0; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_tnl_mpls_tag; +} + +#define ICMP_TYPE_OFFSET_FIRST_DW 24 +#define ICMP_CODE_OFFSET_FIRST_DW 16 + +static int +dr_ste_v0_build_icmp_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc3 *misc_3 = &value->misc3; + u32 *icmp_header_data; + int dw0_location; + int dw1_location; + u8 *icmp_type; + u8 *icmp_code; + bool is_ipv4; + + is_ipv4 = DR_MASK_IS_ICMPV4_SET(misc_3); + if (is_ipv4) { + icmp_header_data = &misc_3->icmpv4_header_data; + icmp_type = &misc_3->icmpv4_type; + icmp_code = &misc_3->icmpv4_code; + dw0_location = sb->caps->flex_parser_id_icmp_dw0; + dw1_location = sb->caps->flex_parser_id_icmp_dw1; + } else { + icmp_header_data = &misc_3->icmpv6_header_data; + icmp_type = &misc_3->icmpv6_type; + icmp_code = &misc_3->icmpv6_code; + dw0_location = sb->caps->flex_parser_id_icmpv6_dw0; + dw1_location = sb->caps->flex_parser_id_icmpv6_dw1; + } + + switch (dw0_location) { + case 4: + MLX5_SET(ste_flex_parser_1, tag, flex_parser_4, + (*icmp_type << ICMP_TYPE_OFFSET_FIRST_DW) | + (*icmp_code << ICMP_TYPE_OFFSET_FIRST_DW)); + + *icmp_type = 0; + *icmp_code = 0; + break; + default: + return -EINVAL; + } + + switch (dw1_location) { + case 5: + MLX5_SET(ste_flex_parser_1, tag, flex_parser_5, + *icmp_header_data); + *icmp_header_data = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +dr_ste_v0_build_icmp_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + int ret; + + ret = dr_ste_v0_build_icmp_tag(mask, sb, sb->bit_mask); + if (ret) + return ret; + + sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_1; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_icmp_tag; + + return 0; +} + +static int +dr_ste_v0_build_general_purpose_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc2 *misc_2 = &value->misc2; + + DR_STE_SET_TAG(general_purpose, tag, general_purpose_lookup_field, + misc_2, metadata_reg_a); + + return 0; +} + +static void +dr_ste_v0_build_general_purpose_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_general_purpose_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_V0_LU_TYPE_GENERAL_PURPOSE; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_general_purpose_tag; +} + +static int +dr_ste_v0_build_eth_l4_misc_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc3 *misc3 = &value->misc3; + + if (sb->inner) { + DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, inner_tcp_seq_num); + DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, inner_tcp_ack_num); + } else { + DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, outer_tcp_seq_num); + DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, outer_tcp_ack_num); + } + + return 0; +} + +static void +dr_ste_v0_build_eth_l4_misc_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_eth_l4_misc_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4_MISC, sb->rx, sb->inner); + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_eth_l4_misc_tag; +} + +static int +dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc3 *misc3 = &value->misc3; + + DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag, + outer_vxlan_gpe_flags, misc3, + outer_vxlan_gpe_flags); + DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag, + outer_vxlan_gpe_next_protocol, misc3, + outer_vxlan_gpe_next_protocol); + DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag, + outer_vxlan_gpe_vni, misc3, + outer_vxlan_gpe_vni); + + return 0; +} + +static void +dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_tag(mask, sb, sb->bit_mask); + sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_TNL_HEADER; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_tag; +} + +static int +dr_ste_v0_build_flex_parser_tnl_geneve_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc *misc = &value->misc; + + DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, + geneve_protocol_type, misc, geneve_protocol_type); + DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, + geneve_oam, misc, geneve_oam); + DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, + geneve_opt_len, misc, geneve_opt_len); + DR_STE_SET_TAG(flex_parser_tnl_geneve, tag, + geneve_vni, misc, geneve_vni); + + return 0; +} + +static void +dr_ste_v0_build_flex_parser_tnl_geneve_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_flex_parser_tnl_geneve_tag(mask, sb, sb->bit_mask); + sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_TNL_HEADER; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_flex_parser_tnl_geneve_tag; +} + +static int +dr_ste_v0_build_register_0_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc2 *misc2 = &value->misc2; + + DR_STE_SET_TAG(register_0, tag, register_0_h, misc2, metadata_reg_c_0); + DR_STE_SET_TAG(register_0, tag, register_0_l, misc2, metadata_reg_c_1); + DR_STE_SET_TAG(register_0, tag, register_1_h, misc2, metadata_reg_c_2); + DR_STE_SET_TAG(register_0, tag, register_1_l, misc2, metadata_reg_c_3); + + return 0; +} + +static void +dr_ste_v0_build_register_0_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_register_0_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_V0_LU_TYPE_STEERING_REGISTERS_0; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_register_0_tag; +} + +static int +dr_ste_v0_build_register_1_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc2 *misc2 = &value->misc2; + + DR_STE_SET_TAG(register_1, tag, register_2_h, misc2, metadata_reg_c_4); + DR_STE_SET_TAG(register_1, tag, register_2_l, misc2, metadata_reg_c_5); + DR_STE_SET_TAG(register_1, tag, register_3_h, misc2, metadata_reg_c_6); + DR_STE_SET_TAG(register_1, tag, register_3_l, misc2, metadata_reg_c_7); + + return 0; +} + +static void +dr_ste_v0_build_register_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_register_1_tag(mask, sb, sb->bit_mask); + + sb->lu_type = DR_STE_V0_LU_TYPE_STEERING_REGISTERS_1; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_register_1_tag; +} + +static void +dr_ste_v0_build_src_gvmi_qpn_bit_mask(struct mlx5dr_match_param *value, + u8 *bit_mask) +{ + struct mlx5dr_match_misc *misc_mask = &value->misc; + + DR_STE_SET_ONES(src_gvmi_qp, bit_mask, source_gvmi, misc_mask, source_port); + DR_STE_SET_ONES(src_gvmi_qp, bit_mask, source_qp, misc_mask, source_sqn); + misc_mask->source_eswitch_owner_vhca_id = 0; +} + +static int +dr_ste_v0_build_src_gvmi_qpn_tag(struct mlx5dr_match_param *value, + struct mlx5dr_ste_build *sb, + u8 *tag) +{ + struct mlx5dr_match_misc *misc = &value->misc; + struct mlx5dr_cmd_vport_cap *vport_cap; + struct mlx5dr_domain *dmn = sb->dmn; + struct mlx5dr_cmd_caps *caps; + u8 *bit_mask = sb->bit_mask; + bool source_gvmi_set; + + DR_STE_SET_TAG(src_gvmi_qp, tag, source_qp, misc, source_sqn); + + if (sb->vhca_id_valid) { + /* Find port GVMI based on the eswitch_owner_vhca_id */ + if (misc->source_eswitch_owner_vhca_id == dmn->info.caps.gvmi) + caps = &dmn->info.caps; + else if (dmn->peer_dmn && (misc->source_eswitch_owner_vhca_id == + dmn->peer_dmn->info.caps.gvmi)) + caps = &dmn->peer_dmn->info.caps; + else + return -EINVAL; + + misc->source_eswitch_owner_vhca_id = 0; + } else { + caps = &dmn->info.caps; + } + + source_gvmi_set = MLX5_GET(ste_src_gvmi_qp, bit_mask, source_gvmi); + if (source_gvmi_set) { + vport_cap = mlx5dr_get_vport_cap(caps, misc->source_port); + if (!vport_cap) { + mlx5dr_err(dmn, "Vport 0x%x is invalid\n", + misc->source_port); + return -EINVAL; + } + + if (vport_cap->vport_gvmi) + MLX5_SET(ste_src_gvmi_qp, tag, source_gvmi, vport_cap->vport_gvmi); + + misc->source_port = 0; + } + + return 0; +} + +static void +dr_ste_v0_build_src_gvmi_qpn_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) +{ + dr_ste_v0_build_src_gvmi_qpn_bit_mask(mask, sb->bit_mask); + + sb->lu_type = DR_STE_V0_LU_TYPE_SRC_GVMI_AND_QP; + sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask); + sb->ste_build_tag_func = &dr_ste_v0_build_src_gvmi_qpn_tag; +} + +struct mlx5dr_ste_ctx ste_ctx_v0 = { + /* Builders */ + .build_eth_l2_src_dst_init = &dr_ste_v0_build_eth_l2_src_dst_init, + .build_eth_l3_ipv6_src_init = &dr_ste_v0_build_eth_l3_ipv6_src_init, + .build_eth_l3_ipv6_dst_init = &dr_ste_v0_build_eth_l3_ipv6_dst_init, + .build_eth_l3_ipv4_5_tuple_init = &dr_ste_v0_build_eth_l3_ipv4_5_tuple_init, + .build_eth_l2_src_init = &dr_ste_v0_build_eth_l2_src_init, + .build_eth_l2_dst_init = &dr_ste_v0_build_eth_l2_dst_init, + .build_eth_l2_tnl_init = &dr_ste_v0_build_eth_l2_tnl_init, + .build_eth_l3_ipv4_misc_init = &dr_ste_v0_build_eth_l3_ipv4_misc_init, + .build_eth_ipv6_l3_l4_init = &dr_ste_v0_build_eth_ipv6_l3_l4_init, + .build_mpls_init = &dr_ste_v0_build_mpls_init, + .build_tnl_gre_init = &dr_ste_v0_build_tnl_gre_init, + .build_tnl_mpls_init = &dr_ste_v0_build_tnl_mpls_init, + .build_icmp_init = &dr_ste_v0_build_icmp_init, + .build_general_purpose_init = &dr_ste_v0_build_general_purpose_init, + .build_eth_l4_misc_init = &dr_ste_v0_build_eth_l4_misc_init, + .build_tnl_vxlan_gpe_init = &dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_init, + .build_tnl_geneve_init = &dr_ste_v0_build_flex_parser_tnl_geneve_init, + .build_register_0_init = &dr_ste_v0_build_register_0_init, + .build_register_1_init = &dr_ste_v0_build_register_1_init, + .build_src_gvmi_qpn_init = &dr_ste_v0_build_src_gvmi_qpn_init, + + /* Getters and Setters */ + .ste_init = &dr_ste_v0_init, + .set_next_lu_type = &dr_ste_v0_set_next_lu_type, + .get_next_lu_type = &dr_ste_v0_get_next_lu_type, + .set_miss_addr = &dr_ste_v0_set_miss_addr, + .get_miss_addr = &dr_ste_v0_get_miss_addr, + .set_hit_addr = &dr_ste_v0_set_hit_addr, + .set_byte_mask = &dr_ste_v0_set_byte_mask, + .get_byte_mask = &dr_ste_v0_get_byte_mask, + + /* Actions */ + .set_actions_rx = &dr_ste_v0_set_actions_rx, + .set_actions_tx = &dr_ste_v0_set_actions_tx, + .modify_field_arr_sz = ARRAY_SIZE(dr_ste_v0_action_modify_field_arr), + .modify_field_arr = dr_ste_v0_action_modify_field_arr, + .set_action_set = &dr_ste_v0_set_action_set, + .set_action_add = &dr_ste_v0_set_action_add, + .set_action_copy = &dr_ste_v0_set_action_copy, + .set_action_decap_l3_list = &dr_ste_v0_set_action_decap_l3_list, +}; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 51880df26724..8d2c3b6e2755 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -120,6 +120,7 @@ struct mlx5dr_ste_htbl; struct mlx5dr_match_param; struct mlx5dr_cmd_caps; struct mlx5dr_matcher_rx_tx; +struct mlx5dr_ste_ctx; struct mlx5dr_ste { u8 *hw_ste; @@ -154,7 +155,7 @@ struct mlx5dr_ste_htbl_ctrl { }; struct mlx5dr_ste_htbl { - u8 lu_type; + u16 lu_type; u16 byte_mask; u32 refcount; struct mlx5dr_icm_chunk *chunk; @@ -190,7 +191,7 @@ struct mlx5dr_ste_build { u8 vhca_id_valid:1; struct mlx5dr_domain *dmn; struct mlx5dr_cmd_caps *caps; - u8 lu_type; + u16 lu_type; u16 byte_mask; u8 bit_mask[DR_STE_SIZE_MASK]; int (*ste_build_tag_func)(struct mlx5dr_match_param *spec, @@ -201,7 +202,7 @@ struct mlx5dr_ste_build { struct mlx5dr_ste_htbl * mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool, enum mlx5dr_icm_chunk_size chunk_size, - u8 lu_type, u16 byte_mask); + u16 lu_type, u16 byte_mask); int mlx5dr_ste_htbl_free(struct mlx5dr_ste_htbl *htbl); @@ -219,35 +220,84 @@ static inline void mlx5dr_htbl_get(struct mlx5dr_ste_htbl *htbl) /* STE utils */ u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl); -void mlx5dr_ste_init(u8 *hw_ste_p, u8 lu_type, u8 entry_type, u16 gvmi); -void mlx5dr_ste_always_hit_htbl(struct mlx5dr_ste *ste, - struct mlx5dr_ste_htbl *next_htbl); -void mlx5dr_ste_set_miss_addr(u8 *hw_ste, u64 miss_addr); -u64 mlx5dr_ste_get_miss_addr(u8 *hw_ste); -void mlx5dr_ste_set_hit_gvmi(u8 *hw_ste_p, u16 gvmi); -void mlx5dr_ste_set_hit_addr(u8 *hw_ste, u64 icm_addr, u32 ht_size); -void mlx5dr_ste_always_miss_addr(struct mlx5dr_ste *ste, u64 miss_addr); +void mlx5dr_ste_set_miss_addr(struct mlx5dr_ste_ctx *ste_ctx, + u8 *hw_ste, u64 miss_addr); +void mlx5dr_ste_set_hit_addr(struct mlx5dr_ste_ctx *ste_ctx, + u8 *hw_ste, u64 icm_addr, u32 ht_size); +void mlx5dr_ste_set_hit_addr_by_next_htbl(struct mlx5dr_ste_ctx *ste_ctx, + u8 *hw_ste, + struct mlx5dr_ste_htbl *next_htbl); void mlx5dr_ste_set_bit_mask(u8 *hw_ste_p, u8 *bit_mask); bool mlx5dr_ste_is_last_in_rule(struct mlx5dr_matcher_rx_tx *nic_matcher, u8 ste_location); -void mlx5dr_ste_rx_set_flow_tag(u8 *hw_ste_p, u32 flow_tag); -void mlx5dr_ste_set_counter_id(u8 *hw_ste_p, u32 ctr_id); -void mlx5dr_ste_set_tx_encap(void *hw_ste_p, u32 reformat_id, - int size, bool encap_l3); -void mlx5dr_ste_set_rx_decap(u8 *hw_ste_p); -void mlx5dr_ste_set_rx_decap_l3(u8 *hw_ste_p, bool vlan); -void mlx5dr_ste_set_rx_pop_vlan(u8 *hw_ste_p); -void mlx5dr_ste_set_tx_push_vlan(u8 *hw_ste_p, u32 vlan_tpid_pcp_dei_vid, - bool go_back); -void mlx5dr_ste_set_entry_type(u8 *hw_ste_p, u8 entry_type); -u8 mlx5dr_ste_get_entry_type(u8 *hw_ste_p); -void mlx5dr_ste_set_rewrite_actions(u8 *hw_ste_p, u16 num_of_actions, - u32 re_write_index); -void mlx5dr_ste_set_go_back_bit(u8 *hw_ste_p); u64 mlx5dr_ste_get_icm_addr(struct mlx5dr_ste *ste); u64 mlx5dr_ste_get_mr_addr(struct mlx5dr_ste *ste); struct list_head *mlx5dr_ste_get_miss_list(struct mlx5dr_ste *ste); +#define MLX5DR_MAX_VLANS 2 + +struct mlx5dr_ste_actions_attr { + u32 modify_index; + u16 modify_actions; + u32 decap_index; + u16 decap_actions; + u8 decap_with_vlan:1; + u64 final_icm_addr; + u32 flow_tag; + u32 ctr_id; + u16 gvmi; + u16 hit_gvmi; + u32 reformat_id; + u32 reformat_size; + struct { + int count; + u32 headers[MLX5DR_MAX_VLANS]; + } vlans; +}; + +void mlx5dr_ste_set_actions_rx(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes); +void mlx5dr_ste_set_actions_tx(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_domain *dmn, + u8 *action_type_set, + u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes); + +void mlx5dr_ste_set_action_set(struct mlx5dr_ste_ctx *ste_ctx, + __be64 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data); +void mlx5dr_ste_set_action_add(struct mlx5dr_ste_ctx *ste_ctx, + __be64 *hw_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data); +void mlx5dr_ste_set_action_copy(struct mlx5dr_ste_ctx *ste_ctx, + __be64 *hw_action, + u8 dst_hw_field, + u8 dst_shifter, + u8 dst_len, + u8 src_hw_field, + u8 src_shifter); +int mlx5dr_ste_set_action_decap_l3_list(struct mlx5dr_ste_ctx *ste_ctx, + void *data, + u32 data_sz, + u8 *hw_action, + u32 hw_action_sz, + u16 *used_hw_action_num); + +const struct mlx5dr_ste_action_modify_field * +mlx5dr_ste_conv_modify_hdr_sw_field(struct mlx5dr_ste_ctx *ste_ctx, u16 sw_field); + +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx(u8 version); void mlx5dr_ste_free(struct mlx5dr_ste *ste, struct mlx5dr_matcher *matcher, struct mlx5dr_matcher_rx_tx *nic_matcher); @@ -271,8 +321,6 @@ static inline bool mlx5dr_ste_is_not_used(struct mlx5dr_ste *ste) return !ste->refcount; } -void mlx5dr_ste_set_hit_addr_by_next_htbl(u8 *hw_ste, - struct mlx5dr_ste_htbl *next_htbl); bool mlx5dr_ste_equal_tag(void *src, void *dst); int mlx5dr_ste_create_next_htbl(struct mlx5dr_matcher *matcher, struct mlx5dr_matcher_rx_tx *nic_matcher, @@ -289,65 +337,85 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher, struct mlx5dr_matcher_rx_tx *nic_matcher, struct mlx5dr_match_param *value, u8 *ste_arr); -void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_build *builder, +void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *builder, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_mpls(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_mpls(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -int mlx5dr_ste_build_icmp(struct mlx5dr_ste_build *sb, +int mlx5dr_ste_build_icmp(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, struct mlx5dr_cmd_caps *caps, bool inner, bool rx); -void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_register_0(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_register_0(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_register_1(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_register_1(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, bool inner, bool rx); -void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_build *sb, +void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_ctx *ste_ctx, + struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask, struct mlx5dr_domain *dmn, bool inner, bool rx); @@ -574,10 +642,10 @@ struct mlx5dr_match_misc3 { u32 outer_vxlan_gpe_next_protocol:8; u32 icmpv4_header_data; u32 icmpv6_header_data; - u32 icmpv6_code:8; - u32 icmpv6_type:8; - u32 icmpv4_code:8; - u32 icmpv4_type:8; + u8 icmpv6_code; + u8 icmpv6_type; + u8 icmpv4_code; + u8 icmpv4_type; u8 reserved_auto3[0x1c]; }; @@ -671,6 +739,7 @@ struct mlx5dr_domain { struct mlx5dr_send_ring *send_ring; struct mlx5dr_domain_info info; struct mlx5dr_domain_cache cache; + struct mlx5dr_ste_ctx *ste_ctx; }; struct mlx5dr_table_rx_tx { @@ -725,6 +794,14 @@ struct mlx5dr_rule_member { struct list_head use_ste_list; }; +struct mlx5dr_ste_action_modify_field { + u16 hw_field; + u8 start; + u8 end; + u8 l3_type; + u8 l4_type; +}; + struct mlx5dr_action { enum mlx5dr_action_type action_type; refcount_t refcount; @@ -1000,7 +1077,8 @@ int mlx5dr_ste_htbl_init_and_postsend(struct mlx5dr_domain *dmn, struct mlx5dr_ste_htbl *htbl, struct mlx5dr_htbl_connect_info *connect_info, bool update_hw_ste); -void mlx5dr_ste_set_formatted_ste(u16 gvmi, +void mlx5dr_ste_set_formatted_ste(struct mlx5dr_ste_ctx *ste_ctx, + u16 gvmi, struct mlx5dr_domain_rx_tx *nic_dmn, struct mlx5dr_ste_htbl *htbl, u8 *formatted_ste, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr.h index e01c3766c7de..83df6df6b459 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr.h @@ -5,91 +5,6 @@ #define MLX5_IFC_DR_H enum { - MLX5DR_ACTION_MDFY_HW_FLD_L2_0 = 0, - MLX5DR_ACTION_MDFY_HW_FLD_L2_1 = 1, - MLX5DR_ACTION_MDFY_HW_FLD_L2_2 = 2, - MLX5DR_ACTION_MDFY_HW_FLD_L3_0 = 3, - MLX5DR_ACTION_MDFY_HW_FLD_L3_1 = 4, - MLX5DR_ACTION_MDFY_HW_FLD_L3_2 = 5, - MLX5DR_ACTION_MDFY_HW_FLD_L3_3 = 6, - MLX5DR_ACTION_MDFY_HW_FLD_L3_4 = 7, - MLX5DR_ACTION_MDFY_HW_FLD_L4_0 = 8, - MLX5DR_ACTION_MDFY_HW_FLD_L4_1 = 9, - MLX5DR_ACTION_MDFY_HW_FLD_MPLS = 10, - MLX5DR_ACTION_MDFY_HW_FLD_L2_TNL_0 = 11, - MLX5DR_ACTION_MDFY_HW_FLD_REG_0 = 12, - MLX5DR_ACTION_MDFY_HW_FLD_REG_1 = 13, - MLX5DR_ACTION_MDFY_HW_FLD_REG_2 = 14, - MLX5DR_ACTION_MDFY_HW_FLD_REG_3 = 15, - MLX5DR_ACTION_MDFY_HW_FLD_L4_2 = 16, - MLX5DR_ACTION_MDFY_HW_FLD_FLEX_0 = 17, - MLX5DR_ACTION_MDFY_HW_FLD_FLEX_1 = 18, - MLX5DR_ACTION_MDFY_HW_FLD_FLEX_2 = 19, - MLX5DR_ACTION_MDFY_HW_FLD_FLEX_3 = 20, - MLX5DR_ACTION_MDFY_HW_FLD_L2_TNL_1 = 21, - MLX5DR_ACTION_MDFY_HW_FLD_METADATA = 22, - MLX5DR_ACTION_MDFY_HW_FLD_RESERVED = 23, -}; - -enum { - MLX5DR_ACTION_MDFY_HW_OP_COPY = 0x1, - MLX5DR_ACTION_MDFY_HW_OP_SET = 0x2, - MLX5DR_ACTION_MDFY_HW_OP_ADD = 0x3, -}; - -enum { - MLX5DR_ACTION_MDFY_HW_HDR_L3_NONE = 0x0, - MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4 = 0x1, - MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6 = 0x2, -}; - -enum { - MLX5DR_ACTION_MDFY_HW_HDR_L4_NONE = 0x0, - MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP = 0x1, - MLX5DR_ACTION_MDFY_HW_HDR_L4_UDP = 0x2, -}; - -enum { - MLX5DR_STE_LU_TYPE_NOP = 0x00, - MLX5DR_STE_LU_TYPE_SRC_GVMI_AND_QP = 0x05, - MLX5DR_STE_LU_TYPE_ETHL2_TUNNELING_I = 0x0a, - MLX5DR_STE_LU_TYPE_ETHL2_DST_O = 0x06, - MLX5DR_STE_LU_TYPE_ETHL2_DST_I = 0x07, - MLX5DR_STE_LU_TYPE_ETHL2_DST_D = 0x1b, - MLX5DR_STE_LU_TYPE_ETHL2_SRC_O = 0x08, - MLX5DR_STE_LU_TYPE_ETHL2_SRC_I = 0x09, - MLX5DR_STE_LU_TYPE_ETHL2_SRC_D = 0x1c, - MLX5DR_STE_LU_TYPE_ETHL2_SRC_DST_O = 0x36, - MLX5DR_STE_LU_TYPE_ETHL2_SRC_DST_I = 0x37, - MLX5DR_STE_LU_TYPE_ETHL2_SRC_DST_D = 0x38, - MLX5DR_STE_LU_TYPE_ETHL3_IPV6_DST_O = 0x0d, - MLX5DR_STE_LU_TYPE_ETHL3_IPV6_DST_I = 0x0e, - MLX5DR_STE_LU_TYPE_ETHL3_IPV6_DST_D = 0x1e, - MLX5DR_STE_LU_TYPE_ETHL3_IPV6_SRC_O = 0x0f, - MLX5DR_STE_LU_TYPE_ETHL3_IPV6_SRC_I = 0x10, - MLX5DR_STE_LU_TYPE_ETHL3_IPV6_SRC_D = 0x1f, - MLX5DR_STE_LU_TYPE_ETHL3_IPV4_5_TUPLE_O = 0x11, - MLX5DR_STE_LU_TYPE_ETHL3_IPV4_5_TUPLE_I = 0x12, - MLX5DR_STE_LU_TYPE_ETHL3_IPV4_5_TUPLE_D = 0x20, - MLX5DR_STE_LU_TYPE_ETHL3_IPV4_MISC_O = 0x29, - MLX5DR_STE_LU_TYPE_ETHL3_IPV4_MISC_I = 0x2a, - MLX5DR_STE_LU_TYPE_ETHL3_IPV4_MISC_D = 0x2b, - MLX5DR_STE_LU_TYPE_ETHL4_O = 0x13, - MLX5DR_STE_LU_TYPE_ETHL4_I = 0x14, - MLX5DR_STE_LU_TYPE_ETHL4_D = 0x21, - MLX5DR_STE_LU_TYPE_ETHL4_MISC_O = 0x2c, - MLX5DR_STE_LU_TYPE_ETHL4_MISC_I = 0x2d, - MLX5DR_STE_LU_TYPE_ETHL4_MISC_D = 0x2e, - MLX5DR_STE_LU_TYPE_MPLS_FIRST_O = 0x15, - MLX5DR_STE_LU_TYPE_MPLS_FIRST_I = 0x24, - MLX5DR_STE_LU_TYPE_MPLS_FIRST_D = 0x25, - MLX5DR_STE_LU_TYPE_GRE = 0x16, - MLX5DR_STE_LU_TYPE_FLEX_PARSER_0 = 0x22, - MLX5DR_STE_LU_TYPE_FLEX_PARSER_1 = 0x23, - MLX5DR_STE_LU_TYPE_FLEX_PARSER_TNL_HEADER = 0x19, - MLX5DR_STE_LU_TYPE_GENERAL_PURPOSE = 0x18, - MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_0 = 0x2f, - MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_1 = 0x30, MLX5DR_STE_LU_TYPE_DONT_CARE = 0x0f, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index bdafc85fd874..ba78e0660523 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -36,6 +36,7 @@ #include <linux/mlx5/vport.h> #include <linux/mlx5/eswitch.h> #include "mlx5_core.h" +#include "sf/sf.h" /* Mutex to hold while enabling or disabling RoCE */ static DEFINE_MUTEX(mlx5_roce_en_lock); @@ -1160,6 +1161,6 @@ EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid); */ u16 mlx5_eswitch_get_total_vports(const struct mlx5_core_dev *dev) { - return MLX5_SPECIAL_VPORTS(dev) + mlx5_core_max_vfs(dev); + return MLX5_SPECIAL_VPORTS(dev) + mlx5_core_max_vfs(dev) + mlx5_sf_max_functions(dev); } EXPORT_SYMBOL_GPL(mlx5_eswitch_get_total_vports); diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c index 5d9ddf36fb4e..e6f677e42007 100644 --- a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c +++ b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c @@ -267,7 +267,7 @@ struct mlxfw_mfa2_file *mlxfw_mfa2_file_init(const struct firmware *fw) const void *first_tlv_ptr; const void *cb_top_ptr; - mfa2_file = kcalloc(1, sizeof(*mfa2_file), GFP_KERNEL); + mfa2_file = kzalloc(sizeof(*mfa2_file), GFP_KERNEL); if (!mfa2_file) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 685037e052af..52fdc34251ba 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -84,6 +84,7 @@ struct mlxsw_core { struct mlxsw_thermal *thermal; struct mlxsw_core_port *ports; unsigned int max_ports; + atomic_t active_ports_count; bool fw_flash_in_progress; struct { struct devlink_health_reporter *fw_fatal; @@ -96,8 +97,36 @@ struct mlxsw_core { #define MLXSW_PORT_MAX_PORTS_DEFAULT 0x40 -static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core) +static u64 mlxsw_ports_occ_get(void *priv) { + struct mlxsw_core *mlxsw_core = priv; + + return atomic_read(&mlxsw_core->active_ports_count); +} + +static int mlxsw_core_resources_ports_register(struct mlxsw_core *mlxsw_core) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + struct devlink_resource_size_params ports_num_params; + u32 max_ports; + + max_ports = mlxsw_core->max_ports - 1; + devlink_resource_size_params_init(&ports_num_params, max_ports, + max_ports, 1, + DEVLINK_RESOURCE_UNIT_ENTRY); + + return devlink_resource_register(devlink, + DEVLINK_RESOURCE_GENERIC_NAME_PORTS, + max_ports, MLXSW_CORE_RESOURCE_PORTS, + DEVLINK_RESOURCE_ID_PARENT_TOP, + &ports_num_params); +} + +static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core, bool reload) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + int err; + /* Switch ports are numbered from 1 to queried value */ if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SYSTEM_PORT)) mlxsw_core->max_ports = MLXSW_CORE_RES_GET(mlxsw_core, @@ -110,11 +139,30 @@ static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core) if (!mlxsw_core->ports) return -ENOMEM; + if (!reload) { + err = mlxsw_core_resources_ports_register(mlxsw_core); + if (err) + goto err_resources_ports_register; + } + atomic_set(&mlxsw_core->active_ports_count, 0); + devlink_resource_occ_get_register(devlink, MLXSW_CORE_RESOURCE_PORTS, + mlxsw_ports_occ_get, mlxsw_core); + return 0; + +err_resources_ports_register: + kfree(mlxsw_core->ports); + return err; } -static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core) +static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core, bool reload) { + struct devlink *devlink = priv_to_devlink(mlxsw_core); + + devlink_resource_occ_get_unregister(devlink, MLXSW_CORE_RESOURCE_PORTS); + if (!reload) + devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL); + kfree(mlxsw_core->ports); } @@ -1897,7 +1945,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, goto err_register_resources; } - err = mlxsw_ports_init(mlxsw_core); + err = mlxsw_ports_init(mlxsw_core, reload); if (err) goto err_ports_init; @@ -1986,7 +2034,7 @@ err_devlink_register: err_emad_init: kfree(mlxsw_core->lag.mapping); err_alloc_lag_mapping: - mlxsw_ports_fini(mlxsw_core); + mlxsw_ports_fini(mlxsw_core, reload); err_ports_init: if (!reload) devlink_resources_unregister(devlink, NULL); @@ -2056,7 +2104,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, devlink_unregister(devlink); mlxsw_emad_fini(mlxsw_core); kfree(mlxsw_core->lag.mapping); - mlxsw_ports_fini(mlxsw_core); + mlxsw_ports_fini(mlxsw_core, reload); if (!reload) devlink_resources_unregister(devlink, NULL); mlxsw_core->bus->fini(mlxsw_core->bus_priv); @@ -2755,16 +2803,25 @@ int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port, const unsigned char *switch_id, unsigned char switch_id_len) { - return __mlxsw_core_port_init(mlxsw_core, local_port, - DEVLINK_PORT_FLAVOUR_PHYSICAL, - port_number, split, split_port_subnumber, - splittable, lanes, - switch_id, switch_id_len); + int err; + + err = __mlxsw_core_port_init(mlxsw_core, local_port, + DEVLINK_PORT_FLAVOUR_PHYSICAL, + port_number, split, split_port_subnumber, + splittable, lanes, + switch_id, switch_id_len); + if (err) + return err; + + atomic_inc(&mlxsw_core->active_ports_count); + return 0; } EXPORT_SYMBOL(mlxsw_core_port_init); void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port) { + atomic_dec(&mlxsw_core->active_ports_count); + __mlxsw_core_port_fini(mlxsw_core, local_port); } EXPORT_SYMBOL(mlxsw_core_port_fini); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 6b3ccbf6b238..8af7d9d03475 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -19,6 +19,11 @@ #include "cmd.h" #include "resources.h" +enum mlxsw_core_resource_id { + MLXSW_CORE_RESOURCE_PORTS = 1, + MLXSW_CORE_RESOURCE_MAX, +}; + struct mlxsw_core; struct mlxsw_core_port; struct mlxsw_driver; diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 4eeae8d78006..d0052537e627 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -323,8 +323,8 @@ static int mlxsw_pci_wqe_frag_map(struct mlxsw_pci *mlxsw_pci, char *wqe, struct pci_dev *pdev = mlxsw_pci->pdev; dma_addr_t mapaddr; - mapaddr = pci_map_single(pdev, frag_data, frag_len, direction); - if (unlikely(pci_dma_mapping_error(pdev, mapaddr))) { + mapaddr = dma_map_single(&pdev->dev, frag_data, frag_len, direction); + if (unlikely(dma_mapping_error(&pdev->dev, mapaddr))) { dev_err_ratelimited(&pdev->dev, "failed to dma map tx frag\n"); return -EIO; } @@ -342,7 +342,7 @@ static void mlxsw_pci_wqe_frag_unmap(struct mlxsw_pci *mlxsw_pci, char *wqe, if (!frag_len) return; - pci_unmap_single(pdev, mapaddr, frag_len, direction); + dma_unmap_single(&pdev->dev, mapaddr, frag_len, direction); } static int mlxsw_pci_rdq_skb_alloc(struct mlxsw_pci *mlxsw_pci, @@ -858,9 +858,9 @@ static int mlxsw_pci_queue_init(struct mlxsw_pci *mlxsw_pci, char *mbox, tasklet_setup(&q->tasklet, q_ops->tasklet); mem_item->size = MLXSW_PCI_AQ_SIZE; - mem_item->buf = pci_alloc_consistent(mlxsw_pci->pdev, - mem_item->size, - &mem_item->mapaddr); + mem_item->buf = dma_alloc_coherent(&mlxsw_pci->pdev->dev, + mem_item->size, &mem_item->mapaddr, + GFP_KERNEL); if (!mem_item->buf) return -ENOMEM; @@ -890,8 +890,8 @@ static int mlxsw_pci_queue_init(struct mlxsw_pci *mlxsw_pci, char *mbox, err_q_ops_init: kfree(q->elem_info); err_elem_info_alloc: - pci_free_consistent(mlxsw_pci->pdev, mem_item->size, - mem_item->buf, mem_item->mapaddr); + dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size, + mem_item->buf, mem_item->mapaddr); return err; } @@ -903,8 +903,8 @@ static void mlxsw_pci_queue_fini(struct mlxsw_pci *mlxsw_pci, q_ops->fini(mlxsw_pci, q); kfree(q->elem_info); - pci_free_consistent(mlxsw_pci->pdev, mem_item->size, - mem_item->buf, mem_item->mapaddr); + dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size, + mem_item->buf, mem_item->mapaddr); } static int mlxsw_pci_queue_group_init(struct mlxsw_pci *mlxsw_pci, char *mbox, @@ -1273,9 +1273,9 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox, mem_item = &mlxsw_pci->fw_area.items[i]; mem_item->size = MLXSW_PCI_PAGE_SIZE; - mem_item->buf = pci_alloc_consistent(mlxsw_pci->pdev, - mem_item->size, - &mem_item->mapaddr); + mem_item->buf = dma_alloc_coherent(&mlxsw_pci->pdev->dev, + mem_item->size, + &mem_item->mapaddr, GFP_KERNEL); if (!mem_item->buf) { err = -ENOMEM; goto err_alloc; @@ -1304,8 +1304,8 @@ err_alloc: for (i--; i >= 0; i--) { mem_item = &mlxsw_pci->fw_area.items[i]; - pci_free_consistent(mlxsw_pci->pdev, mem_item->size, - mem_item->buf, mem_item->mapaddr); + dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size, + mem_item->buf, mem_item->mapaddr); } kfree(mlxsw_pci->fw_area.items); return err; @@ -1321,8 +1321,8 @@ static void mlxsw_pci_fw_area_fini(struct mlxsw_pci *mlxsw_pci) for (i = 0; i < mlxsw_pci->fw_area.count; i++) { mem_item = &mlxsw_pci->fw_area.items[i]; - pci_free_consistent(mlxsw_pci->pdev, mem_item->size, - mem_item->buf, mem_item->mapaddr); + dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size, + mem_item->buf, mem_item->mapaddr); } kfree(mlxsw_pci->fw_area.items); } @@ -1347,8 +1347,8 @@ static int mlxsw_pci_mbox_alloc(struct mlxsw_pci *mlxsw_pci, int err = 0; mbox->size = MLXSW_CMD_MBOX_SIZE; - mbox->buf = pci_alloc_consistent(pdev, MLXSW_CMD_MBOX_SIZE, - &mbox->mapaddr); + mbox->buf = dma_alloc_coherent(&pdev->dev, MLXSW_CMD_MBOX_SIZE, + &mbox->mapaddr, GFP_KERNEL); if (!mbox->buf) { dev_err(&pdev->dev, "Failed allocating memory for mailbox\n"); err = -ENOMEM; @@ -1362,8 +1362,8 @@ static void mlxsw_pci_mbox_free(struct mlxsw_pci *mlxsw_pci, { struct pci_dev *pdev = mlxsw_pci->pdev; - pci_free_consistent(pdev, MLXSW_CMD_MBOX_SIZE, mbox->buf, - mbox->mapaddr); + dma_free_coherent(&pdev->dev, MLXSW_CMD_MBOX_SIZE, mbox->buf, + mbox->mapaddr); } static int mlxsw_pci_sys_ready_wait(struct mlxsw_pci *mlxsw_pci, @@ -1848,17 +1848,11 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_pci_request_regions; } - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); - if (!err) { - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); - if (err) { - dev_err(&pdev->dev, "pci_set_consistent_dma_mask failed\n"); - goto err_pci_set_dma_mask; - } - } else { - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (err) { + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (err) { - dev_err(&pdev->dev, "pci_set_dma_mask failed\n"); + dev_err(&pdev->dev, "dma_set_mask failed\n"); goto err_pci_set_dma_mask; } } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index a6956cfc9cb1..a3769f95a182 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -52,7 +52,7 @@ #define MLXSW_SP_RESOURCE_NAME_COUNTERS_RIF "rif" enum mlxsw_sp_resource_id { - MLXSW_SP_RESOURCE_KVD = 1, + MLXSW_SP_RESOURCE_KVD = MLXSW_CORE_RESOURCE_MAX, MLXSW_SP_RESOURCE_KVD_LINEAR, MLXSW_SP_RESOURCE_KVD_HASH_SINGLE, MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index cea42f6ed89b..20c4f3c2cf23 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -527,7 +527,6 @@ mlxsw_sp_port_bridge_vlan_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, struct net_device *orig_dev, u8 state) { @@ -535,9 +534,6 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_vlan *bridge_vlan; int err; - if (switchdev_trans_ph_prepare(trans)) - return 0; - /* It's possible we failed to enslave the port, yet this * operation is executed due to it being deferred. */ @@ -659,7 +655,6 @@ err_port_bridge_vlan_learning_set: static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, unsigned long brport_flags) { if (brport_flags & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) @@ -669,16 +664,12 @@ static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port } static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, struct net_device *orig_dev, unsigned long brport_flags) { struct mlxsw_sp_bridge_port *bridge_port; int err; - if (switchdev_trans_ph_prepare(trans)) - return 0; - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, orig_dev); if (!bridge_port) @@ -724,35 +715,26 @@ static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time) } static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, unsigned long ageing_clock_t) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; - if (switchdev_trans_ph_prepare(trans)) { - if (ageing_time < MLXSW_SP_MIN_AGEING_TIME || - ageing_time > MLXSW_SP_MAX_AGEING_TIME) - return -ERANGE; - else - return 0; - } + if (ageing_time < MLXSW_SP_MIN_AGEING_TIME || + ageing_time > MLXSW_SP_MAX_AGEING_TIME) + return -ERANGE; return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time); } static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, struct net_device *orig_dev, bool vlan_enabled) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_bridge_device *bridge_device; - if (!switchdev_trans_ph_prepare(trans)) - return 0; - bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); if (WARN_ON(!bridge_device)) return -EINVAL; @@ -765,16 +747,12 @@ static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_port_attr_br_vlan_proto_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, struct net_device *orig_dev, u16 vlan_proto) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_bridge_device *bridge_device; - if (!switchdev_trans_ph_prepare(trans)) - return 0; - bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); if (WARN_ON(!bridge_device)) return -EINVAL; @@ -784,16 +762,12 @@ static int mlxsw_sp_port_attr_br_vlan_proto_set(struct mlxsw_sp_port *mlxsw_sp_p } static int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, struct net_device *orig_dev, bool is_port_mrouter) { struct mlxsw_sp_bridge_port *bridge_port; int err; - if (switchdev_trans_ph_prepare(trans)) - return 0; - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, orig_dev); if (!bridge_port) @@ -825,7 +799,6 @@ static bool mlxsw_sp_mc_flood(const struct mlxsw_sp_bridge_port *bridge_port) } static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, struct net_device *orig_dev, bool mc_disabled) { @@ -834,9 +807,6 @@ static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_port *bridge_port; int err; - if (switchdev_trans_ph_prepare(trans)) - return 0; - /* It's possible we failed to enslave the port, yet this * operation is executed due to it being deferred. */ @@ -896,16 +866,12 @@ mlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_trans *trans, struct net_device *orig_dev, bool is_mrouter) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_bridge_device *bridge_device; - if (switchdev_trans_ph_prepare(trans)) - return 0; - /* It's possible we failed to enslave the port, yet this * operation is executed due to it being deferred. */ @@ -921,54 +887,52 @@ mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr, - struct switchdev_trans *trans) + const struct switchdev_attr *attr) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); int err; switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, attr->orig_dev, attr->u.stp_state); break; case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port, - trans, attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: - err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, attr->orig_dev, attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: - err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, attr->u.ageing_time); break; case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: - err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, attr->orig_dev, attr->u.vlan_filtering); break; case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL: - err = mlxsw_sp_port_attr_br_vlan_proto_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_attr_br_vlan_proto_set(mlxsw_sp_port, attr->orig_dev, attr->u.vlan_protocol); break; case SWITCHDEV_ATTR_ID_PORT_MROUTER: - err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port, attr->orig_dev, attr->u.mrouter); break; case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: - err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, attr->orig_dev, attr->u.mc_disabled); break; case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: - err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port, trans, + err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port, attr->orig_dev, attr->u.mrouter); break; @@ -977,8 +941,7 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, break; } - if (switchdev_trans_ph_commit(trans)) - mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); + mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); return err; } @@ -1211,23 +1174,20 @@ mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp, const struct switchdev_obj_port_vlan *vlan) { u16 pvid; - u16 vid; pvid = mlxsw_sp_rif_vid(mlxsw_sp, br_dev); if (!pvid) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { - if (vid != pvid) { - netdev_err(br_dev, "Can't change PVID, it's used by router interface\n"); - return -EBUSY; - } - } else { - if (vid == pvid) { - netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n"); - return -EBUSY; - } + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { + if (vlan->vid != pvid) { + netdev_err(br_dev, "Can't change PVID, it's used by router interface\n"); + return -EBUSY; + } + } else { + if (vlan->vid == pvid) { + netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n"); + return -EBUSY; } } @@ -1236,7 +1196,6 @@ mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans, struct netlink_ext_ack *extack) { bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; @@ -1244,14 +1203,12 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *orig_dev = vlan->obj.orig_dev; struct mlxsw_sp_bridge_port *bridge_port; - u16 vid; if (netif_is_bridge_master(orig_dev)) { int err = 0; if ((vlan->flags & BRIDGE_VLAN_INFO_BRENTRY) && - br_vlan_enabled(orig_dev) && - switchdev_trans_ph_prepare(trans)) + br_vlan_enabled(orig_dev)) err = mlxsw_sp_br_ban_rif_pvid_change(mlxsw_sp, orig_dev, vlan); if (!err) @@ -1259,9 +1216,6 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, return err; } - if (switchdev_trans_ph_commit(trans)) - return 0; - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); if (WARN_ON(!bridge_port)) return -EINVAL; @@ -1269,17 +1223,9 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, if (!bridge_port->bridge_device->vlan_enabled) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - int err; - - err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port, - vid, flag_untagged, - flag_pvid, extack); - if (err) - return err; - } - - return 0; + return mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port, + vlan->vid, flag_untagged, + flag_pvid, extack); } static enum mlxsw_reg_sfdf_flush_type mlxsw_sp_fdb_flush_type(bool lagged) @@ -1716,8 +1662,7 @@ static int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, - const struct switchdev_obj_port_mdb *mdb, - struct switchdev_trans *trans) + const struct switchdev_obj_port_mdb *mdb) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *orig_dev = mdb->obj.orig_dev; @@ -1729,9 +1674,6 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid_index; int err = 0; - if (switchdev_trans_ph_commit(trans)) - return 0; - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); if (!bridge_port) return 0; @@ -1813,7 +1755,6 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_port_obj_add(struct net_device *dev, const struct switchdev_obj *obj, - struct switchdev_trans *trans, struct netlink_ext_ack *extack) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); @@ -1823,22 +1764,19 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); - err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans, - extack); - if (switchdev_trans_ph_prepare(trans)) { - /* The event is emitted before the changes are actually - * applied to the bridge. Therefore schedule the respin - * call for later, so that the respin logic sees the - * updated bridge state. - */ - mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); - } + err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, extack); + + /* The event is emitted before the changes are actually + * applied to the bridge. Therefore schedule the respin + * call for later, so that the respin logic sees the + * updated bridge state. + */ + mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = mlxsw_sp_port_mdb_add(mlxsw_sp_port, - SWITCHDEV_OBJ_PORT_MDB(obj), - trans); + SWITCHDEV_OBJ_PORT_MDB(obj)); break; default: err = -EOPNOTSUPP; @@ -1873,7 +1811,6 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *orig_dev = vlan->obj.orig_dev; struct mlxsw_sp_bridge_port *bridge_port; - u16 vid; if (netif_is_bridge_master(orig_dev)) return -EOPNOTSUPP; @@ -1885,8 +1822,7 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, if (!bridge_port->bridge_device->vlan_enabled) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) - mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vid); + mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vlan->vid); return 0; } @@ -3406,12 +3342,10 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev, SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj); bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; - struct switchdev_trans *trans = port_obj_info->trans; struct mlxsw_sp_bridge_device *bridge_device; struct netlink_ext_ack *extack; struct mlxsw_sp *mlxsw_sp; struct net_device *br_dev; - u16 vid; extack = switchdev_notifier_info_to_extack(&port_obj_info->info); br_dev = netdev_master_upper_dev_get(vxlan_dev); @@ -3424,9 +3358,6 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev, port_obj_info->handled = true; - if (switchdev_trans_ph_commit(trans)) - return 0; - bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); if (!bridge_device) return -EINVAL; @@ -3434,18 +3365,10 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev, if (!bridge_device->vlan_enabled) return 0; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - int err; - - err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device, - vxlan_dev, vid, - flag_untagged, - flag_pvid, extack); - if (err) - return err; - } - - return 0; + return mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device, + vxlan_dev, vlan->vid, + flag_untagged, + flag_pvid, extack); } static void @@ -3458,7 +3381,6 @@ mlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev, struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp *mlxsw_sp; struct net_device *br_dev; - u16 vid; br_dev = netdev_master_upper_dev_get(vxlan_dev); if (!br_dev) @@ -3477,9 +3399,8 @@ mlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev, if (!bridge_device->vlan_enabled) return; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) - mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device, - vxlan_dev, vid); + mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device, vxlan_dev, + vlan->vid); } static int diff --git a/drivers/net/ethernet/micrel/Kconfig b/drivers/net/ethernet/micrel/Kconfig index 42bc014136fe..93df3049cdc0 100644 --- a/drivers/net/ethernet/micrel/Kconfig +++ b/drivers/net/ethernet/micrel/Kconfig @@ -31,6 +31,8 @@ config KS8851 select MII select CRC32 select EEPROM_93CX6 + select PHYLIB + select MICREL_PHY help SPI driver for Micrel KS8851 SPI attached network chip. @@ -40,6 +42,8 @@ config KS8851_MLL select MII select CRC32 select EEPROM_93CX6 + select PHYLIB + select MICREL_PHY help This platform driver is for Micrel KS8851 Address/data bus multiplexed network chip. diff --git a/drivers/net/ethernet/micrel/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h index 2b319e451121..e2eb0caeac82 100644 --- a/drivers/net/ethernet/micrel/ks8851.h +++ b/drivers/net/ethernet/micrel/ks8851.h @@ -358,6 +358,7 @@ union ks8851_tx_hdr { * @vdd_reg: Optional regulator supplying the chip * @vdd_io: Optional digital power supply for IO * @gpio: Optional reset_n gpio + * @mii_bus: Pointer to MII bus structure * @lock: Bus access lock callback * @unlock: Bus access unlock callback * @rdreg16: 16bit register read callback @@ -403,6 +404,7 @@ struct ks8851_net { struct regulator *vdd_reg; struct regulator *vdd_io; int gpio; + struct mii_bus *mii_bus; void (*lock)(struct ks8851_net *ks, unsigned long *flags); diff --git a/drivers/net/ethernet/micrel/ks8851_common.c b/drivers/net/ethernet/micrel/ks8851_common.c index 6fc7483aea03..2feed6ce19d3 100644 --- a/drivers/net/ethernet/micrel/ks8851_common.c +++ b/drivers/net/ethernet/micrel/ks8851_common.c @@ -8,8 +8,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define DEBUG - #include <linux/interrupt.h> #include <linux/module.h> #include <linux/kernel.h> @@ -23,6 +21,7 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> +#include <linux/of_mdio.h> #include <linux/of_net.h> #include "ks8851.h" @@ -932,7 +931,25 @@ static int ks8851_phy_reg(int reg) return KS_P1ANLPR; } - return 0x0; + return -EOPNOTSUPP; +} + +static int ks8851_phy_read_common(struct net_device *dev, int phy_addr, int reg) +{ + struct ks8851_net *ks = netdev_priv(dev); + unsigned long flags; + int result; + int ksreg; + + ksreg = ks8851_phy_reg(reg); + if (ksreg < 0) + return ksreg; + + ks8851_lock(ks, &flags); + result = ks8851_rdreg16(ks, ksreg); + ks8851_unlock(ks, &flags); + + return result; } /** @@ -952,20 +969,13 @@ static int ks8851_phy_reg(int reg) */ static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg) { - struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; - int ksreg; - int result; + int ret; - ksreg = ks8851_phy_reg(reg); - if (!ksreg) + ret = ks8851_phy_read_common(dev, phy_addr, reg); + if (ret < 0) return 0x0; /* no error return allowed, so use zero */ - ks8851_lock(ks, &flags); - result = ks8851_rdreg16(ks, ksreg); - ks8851_unlock(ks, &flags); - - return result; + return ret; } static void ks8851_phy_write(struct net_device *dev, @@ -976,13 +986,37 @@ static void ks8851_phy_write(struct net_device *dev, int ksreg; ksreg = ks8851_phy_reg(reg); - if (ksreg) { + if (ksreg >= 0) { ks8851_lock(ks, &flags); ks8851_wrreg16(ks, ksreg, value); ks8851_unlock(ks, &flags); } } +static int ks8851_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct ks8851_net *ks = bus->priv; + + if (phy_id != 0) + return -EOPNOTSUPP; + + /* KS8851 PHY ID registers are swapped in HW, swap them back. */ + if (reg == MII_PHYSID1) + reg = MII_PHYSID2; + else if (reg == MII_PHYSID2) + reg = MII_PHYSID1; + + return ks8851_phy_read_common(ks->netdev, phy_id, reg); +} + +static int ks8851_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) +{ + struct ks8851_net *ks = bus->priv; + + ks8851_phy_write(ks->netdev, phy_id, reg, val); + return 0; +} + /** * ks8851_read_selftest - read the selftest memory info. * @ks: The device state @@ -1046,6 +1080,42 @@ int ks8851_resume(struct device *dev) } #endif +static int ks8851_register_mdiobus(struct ks8851_net *ks, struct device *dev) +{ + struct mii_bus *mii_bus; + int ret; + + mii_bus = mdiobus_alloc(); + if (!mii_bus) + return -ENOMEM; + + mii_bus->name = "ks8851_eth_mii"; + mii_bus->read = ks8851_mdio_read; + mii_bus->write = ks8851_mdio_write; + mii_bus->priv = ks; + mii_bus->parent = dev; + mii_bus->phy_mask = ~((u32)BIT(0)); + snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + + ret = mdiobus_register(mii_bus); + if (ret) + goto err_mdiobus_register; + + ks->mii_bus = mii_bus; + + return 0; + +err_mdiobus_register: + mdiobus_free(mii_bus); + return ret; +} + +static void ks8851_unregister_mdiobus(struct ks8851_net *ks) +{ + mdiobus_unregister(ks->mii_bus); + mdiobus_free(ks->mii_bus); +} + int ks8851_probe_common(struct net_device *netdev, struct device *dev, int msg_en) { @@ -1104,6 +1174,8 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work); + SET_NETDEV_DEV(netdev, dev); + /* setup EEPROM state */ ks->eeprom.data = ks; ks->eeprom.width = PCI_EEPROM_WIDTH_93C46; @@ -1120,6 +1192,10 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, dev_info(dev, "message enable is %d\n", msg_en); + ret = ks8851_register_mdiobus(ks, dev); + if (ret) + goto err_mdio; + /* set the default message enable */ ks->msg_enable = netif_msg_init(msg_en, NETIF_MSG_DRV | NETIF_MSG_PROBE | @@ -1128,7 +1204,6 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, skb_queue_head_init(&ks->txq); netdev->ethtool_ops = &ks8851_ethtool_ops; - SET_NETDEV_DEV(netdev, dev); dev_set_drvdata(dev, ks); @@ -1156,7 +1231,7 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, ret = register_netdev(netdev); if (ret) { dev_err(dev, "failed to register network device\n"); - goto err_netdev; + goto err_id; } netdev_info(netdev, "revision %d, MAC %pM, IRQ %d, %s EEPROM\n", @@ -1165,8 +1240,9 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, return 0; -err_netdev: err_id: + ks8851_unregister_mdiobus(ks); +err_mdio: if (gpio_is_valid(gpio)) gpio_set_value(gpio, 0); regulator_disable(ks->vdd_reg); @@ -1180,6 +1256,8 @@ int ks8851_remove_common(struct device *dev) { struct ks8851_net *priv = dev_get_drvdata(dev); + ks8851_unregister_mdiobus(priv); + if (netif_msg_drv(priv)) dev_info(dev, "remove\n"); diff --git a/drivers/net/ethernet/micrel/ks8851_par.c b/drivers/net/ethernet/micrel/ks8851_par.c index 3bab0cb2b1a5..2e8fcce50f9d 100644 --- a/drivers/net/ethernet/micrel/ks8851_par.c +++ b/drivers/net/ethernet/micrel/ks8851_par.c @@ -8,8 +8,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define DEBUG - #include <linux/interrupt.h> #include <linux/module.h> #include <linux/kernel.h> diff --git a/drivers/net/ethernet/micrel/ks8851_spi.c b/drivers/net/ethernet/micrel/ks8851_spi.c index 4ec7f1615977..479406ecbaa3 100644 --- a/drivers/net/ethernet/micrel/ks8851_spi.c +++ b/drivers/net/ethernet/micrel/ks8851_spi.c @@ -8,8 +8,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define DEBUG - #include <linux/interrupt.h> #include <linux/module.h> #include <linux/kernel.h> diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile index 58f94c3d80f9..346bba2730ad 100644 --- a/drivers/net/ethernet/mscc/Makefile +++ b/drivers/net/ethernet/mscc/Makefile @@ -6,7 +6,8 @@ mscc_ocelot_switch_lib-y := \ ocelot_police.o \ ocelot_vcap.o \ ocelot_flower.o \ - ocelot_ptp.o + ocelot_ptp.o \ + ocelot_devlink.o obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o mscc_ocelot-y := \ ocelot_vsc7514.o \ diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index ff87a0bc089c..5b2c0cea49ea 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -221,25 +221,20 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, } int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, - bool vlan_aware, struct switchdev_trans *trans) + bool vlan_aware) { + struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1]; struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct ocelot_vcap_filter *filter; u32 val; - if (switchdev_trans_ph_prepare(trans)) { - struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1]; - struct ocelot_vcap_filter *filter; - - list_for_each_entry(filter, &block->rules, list) { - if (filter->ingress_port_mask & BIT(port) && - filter->action.vid_replace_ena) { - dev_err(ocelot->dev, - "Cannot change VLAN state with vlan modify rules active\n"); - return -EBUSY; - } + list_for_each_entry(filter, &block->rules, list) { + if (filter->ingress_port_mask & BIT(port) && + filter->action.vid_replace_ena) { + dev_err(ocelot->dev, + "Cannot change VLAN state with vlan modify rules active\n"); + return -EBUSY; } - - return 0; } ocelot_port->vlan_aware = vlan_aware; @@ -1192,7 +1187,6 @@ int ocelot_port_bridge_leave(struct ocelot *ocelot, int port, struct net_device *bridge) { struct ocelot_vlan pvid = {0}, native_vlan = {0}; - struct switchdev_trans trans; int ret; ocelot->bridge_mask &= ~BIT(port); @@ -1200,13 +1194,7 @@ int ocelot_port_bridge_leave(struct ocelot *ocelot, int port, if (!ocelot->bridge_mask) ocelot->hw_bridge_dev = NULL; - trans.ph_prepare = true; - ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans); - if (ret) - return ret; - - trans.ph_prepare = false; - ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans); + ret = ocelot_port_vlan_filtering(ocelot, port, false); if (ret) return ret; @@ -1379,7 +1367,7 @@ void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) pause_stop); /* Tail dropping watermarks */ - atop_tot = (ocelot->shared_queue_sz - 9 * maxlen) / + atop_tot = (ocelot->packet_buffer_size - 9 * maxlen) / OCELOT_BUFFER_CELL_SZ; atop = (9 * maxlen) / OCELOT_BUFFER_CELL_SZ; ocelot_write_rix(ocelot, ocelot->ops->wm_enc(atop), SYS_ATOP, port); @@ -1492,6 +1480,21 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot) ANA_PORT_VLAN_CFG, cpu); } +static void ocelot_detect_features(struct ocelot *ocelot) +{ + int mmgt, eq_ctrl; + + /* For Ocelot, Felix, Seville, Serval etc, SYS:MMGT:MMGT:FREECNT holds + * the number of 240-byte free memory words (aka 4-cell chunks) and not + * 192 bytes as the documentation incorrectly says. + */ + mmgt = ocelot_read(ocelot, SYS_MMGT); + ocelot->packet_buffer_size = 240 * SYS_MMGT_FREECNT(mmgt); + + eq_ctrl = ocelot_read(ocelot, QSYS_EQ_CTRL); + ocelot->num_frame_refs = QSYS_MMGT_EQ_CTRL_FP_FREE_CNT(eq_ctrl); +} + int ocelot_init(struct ocelot *ocelot) { char queue_name[32]; @@ -1534,6 +1537,7 @@ int ocelot_init(struct ocelot *ocelot) INIT_LIST_HEAD(&ocelot->multicast); INIT_LIST_HEAD(&ocelot->pgids); + ocelot_detect_features(ocelot); ocelot_mact_init(ocelot); ocelot_vlan_init(ocelot); ocelot_vcap_init(ocelot); diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index 291d39d49c4e..e8621dbc14f7 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -121,13 +121,15 @@ void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg); int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, struct phy_device *phy); - -void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, - enum ocelot_tag_prefix injection, - enum ocelot_tag_prefix extraction); +int ocelot_devlink_init(struct ocelot *ocelot); +void ocelot_devlink_teardown(struct ocelot *ocelot); +int ocelot_port_devlink_init(struct ocelot *ocelot, int port, + enum devlink_port_flavour flavour); +void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port); extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_switchdev_nb; extern struct notifier_block ocelot_switchdev_blocking_nb; +extern const struct devlink_ops ocelot_devlink_ops; #endif diff --git a/drivers/net/ethernet/mscc/ocelot_devlink.c b/drivers/net/ethernet/mscc/ocelot_devlink.c new file mode 100644 index 000000000000..edafbd37d12c --- /dev/null +++ b/drivers/net/ethernet/mscc/ocelot_devlink.c @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright 2020-2021 NXP Semiconductors + */ +#include <net/devlink.h> +#include "ocelot.h" + +/* The queue system tracks four resource consumptions: + * Resource 0: Memory tracked per source port + * Resource 1: Frame references tracked per source port + * Resource 2: Memory tracked per destination port + * Resource 3: Frame references tracked per destination port + */ +#define OCELOT_RESOURCE_SZ 256 +#define OCELOT_NUM_RESOURCES 4 + +#define BUF_xxxx_I (0 * OCELOT_RESOURCE_SZ) +#define REF_xxxx_I (1 * OCELOT_RESOURCE_SZ) +#define BUF_xxxx_E (2 * OCELOT_RESOURCE_SZ) +#define REF_xxxx_E (3 * OCELOT_RESOURCE_SZ) + +/* For each resource type there are 4 types of watermarks: + * Q_RSRV: reservation per QoS class per port + * PRIO_SHR: sharing watermark per QoS class across all ports + * P_RSRV: reservation per port + * COL_SHR: sharing watermark per color (drop precedence) across all ports + */ +#define xxx_Q_RSRV_x 0 +#define xxx_PRIO_SHR_x 216 +#define xxx_P_RSRV_x 224 +#define xxx_COL_SHR_x 254 + +/* Reservation Watermarks + * ---------------------- + * + * For setting up the reserved areas, egress watermarks exist per port and per + * QoS class for both ingress and egress. + */ + +/* Amount of packet buffer + * | per QoS class + * | | reserved + * | | | per egress port + * | | | | + * V V v v + * BUF_Q_RSRV_E + */ +#define BUF_Q_RSRV_E(port, prio) \ + (BUF_xxxx_E + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio)) + +/* Amount of packet buffer + * | for all port's traffic classes + * | | reserved + * | | | per egress port + * | | | | + * V V v v + * BUF_P_RSRV_E + */ +#define BUF_P_RSRV_E(port) \ + (BUF_xxxx_E + xxx_P_RSRV_x + (port)) + +/* Amount of packet buffer + * | per QoS class + * | | reserved + * | | | per ingress port + * | | | | + * V V v v + * BUF_Q_RSRV_I + */ +#define BUF_Q_RSRV_I(port, prio) \ + (BUF_xxxx_I + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio)) + +/* Amount of packet buffer + * | for all port's traffic classes + * | | reserved + * | | | per ingress port + * | | | | + * V V v v + * BUF_P_RSRV_I + */ +#define BUF_P_RSRV_I(port) \ + (BUF_xxxx_I + xxx_P_RSRV_x + (port)) + +/* Amount of frame references + * | per QoS class + * | | reserved + * | | | per egress port + * | | | | + * V V v v + * REF_Q_RSRV_E + */ +#define REF_Q_RSRV_E(port, prio) \ + (REF_xxxx_E + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio)) + +/* Amount of frame references + * | for all port's traffic classes + * | | reserved + * | | | per egress port + * | | | | + * V V v v + * REF_P_RSRV_E + */ +#define REF_P_RSRV_E(port) \ + (REF_xxxx_E + xxx_P_RSRV_x + (port)) + +/* Amount of frame references + * | per QoS class + * | | reserved + * | | | per ingress port + * | | | | + * V V v v + * REF_Q_RSRV_I + */ +#define REF_Q_RSRV_I(port, prio) \ + (REF_xxxx_I + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio)) + +/* Amount of frame references + * | for all port's traffic classes + * | | reserved + * | | | per ingress port + * | | | | + * V V v v + * REF_P_RSRV_I + */ +#define REF_P_RSRV_I(port) \ + (REF_xxxx_I + xxx_P_RSRV_x + (port)) + +/* Sharing Watermarks + * ------------------ + * + * The shared memory area is shared between all ports. + */ + +/* Amount of buffer + * | per QoS class + * | | from the shared memory area + * | | | for egress traffic + * | | | | + * V V v v + * BUF_PRIO_SHR_E + */ +#define BUF_PRIO_SHR_E(prio) \ + (BUF_xxxx_E + xxx_PRIO_SHR_x + (prio)) + +/* Amount of buffer + * | per color (drop precedence level) + * | | from the shared memory area + * | | | for egress traffic + * | | | | + * V V v v + * BUF_COL_SHR_E + */ +#define BUF_COL_SHR_E(dp) \ + (BUF_xxxx_E + xxx_COL_SHR_x + (1 - (dp))) + +/* Amount of buffer + * | per QoS class + * | | from the shared memory area + * | | | for ingress traffic + * | | | | + * V V v v + * BUF_PRIO_SHR_I + */ +#define BUF_PRIO_SHR_I(prio) \ + (BUF_xxxx_I + xxx_PRIO_SHR_x + (prio)) + +/* Amount of buffer + * | per color (drop precedence level) + * | | from the shared memory area + * | | | for ingress traffic + * | | | | + * V V v v + * BUF_COL_SHR_I + */ +#define BUF_COL_SHR_I(dp) \ + (BUF_xxxx_I + xxx_COL_SHR_x + (1 - (dp))) + +/* Amount of frame references + * | per QoS class + * | | from the shared area + * | | | for egress traffic + * | | | | + * V V v v + * REF_PRIO_SHR_E + */ +#define REF_PRIO_SHR_E(prio) \ + (REF_xxxx_E + xxx_PRIO_SHR_x + (prio)) + +/* Amount of frame references + * | per color (drop precedence level) + * | | from the shared area + * | | | for egress traffic + * | | | | + * V V v v + * REF_COL_SHR_E + */ +#define REF_COL_SHR_E(dp) \ + (REF_xxxx_E + xxx_COL_SHR_x + (1 - (dp))) + +/* Amount of frame references + * | per QoS class + * | | from the shared area + * | | | for ingress traffic + * | | | | + * V V v v + * REF_PRIO_SHR_I + */ +#define REF_PRIO_SHR_I(prio) \ + (REF_xxxx_I + xxx_PRIO_SHR_x + (prio)) + +/* Amount of frame references + * | per color (drop precedence level) + * | | from the shared area + * | | | for ingress traffic + * | | | | + * V V v v + * REF_COL_SHR_I + */ +#define REF_COL_SHR_I(dp) \ + (REF_xxxx_I + xxx_COL_SHR_x + (1 - (dp))) + +static u32 ocelot_wm_read(struct ocelot *ocelot, int index) +{ + int wm = ocelot_read_gix(ocelot, QSYS_RES_CFG, index); + + return ocelot->ops->wm_dec(wm); +} + +static void ocelot_wm_write(struct ocelot *ocelot, int index, u32 val) +{ + u32 wm = ocelot->ops->wm_enc(val); + + ocelot_write_gix(ocelot, wm, QSYS_RES_CFG, index); +} + +static void ocelot_wm_status(struct ocelot *ocelot, int index, u32 *inuse, + u32 *maxuse) +{ + int res_stat = ocelot_read_gix(ocelot, QSYS_RES_STAT, index); + + return ocelot->ops->wm_stat(res_stat, inuse, maxuse); +} + +/* The hardware comes out of reset with strange defaults: the sum of all + * reservations for frame memory is larger than the total buffer size. + * One has to wonder how can the reservation watermarks still guarantee + * anything under congestion. + * Bring some sense into the hardware by changing the defaults to disable all + * reservations and rely only on the sharing watermark for frames with drop + * precedence 0. The user can still explicitly request reservations per port + * and per port-tc through devlink-sb. + */ +static void ocelot_disable_reservation_watermarks(struct ocelot *ocelot, + int port) +{ + int prio; + + for (prio = 0; prio < OCELOT_NUM_TC; prio++) { + ocelot_wm_write(ocelot, BUF_Q_RSRV_I(port, prio), 0); + ocelot_wm_write(ocelot, BUF_Q_RSRV_E(port, prio), 0); + ocelot_wm_write(ocelot, REF_Q_RSRV_I(port, prio), 0); + ocelot_wm_write(ocelot, REF_Q_RSRV_E(port, prio), 0); + } + + ocelot_wm_write(ocelot, BUF_P_RSRV_I(port), 0); + ocelot_wm_write(ocelot, BUF_P_RSRV_E(port), 0); + ocelot_wm_write(ocelot, REF_P_RSRV_I(port), 0); + ocelot_wm_write(ocelot, REF_P_RSRV_E(port), 0); +} + +/* We want the sharing watermarks to consume all nonreserved resources, for + * efficient resource utilization (a single traffic flow should be able to use + * up the entire buffer space and frame resources as long as there's no + * interference). + * The switch has 10 sharing watermarks per lookup: 8 per traffic class and 2 + * per color (drop precedence). + * The trouble with configuring these sharing watermarks is that: + * (1) There's a risk that we overcommit the resources if we configure + * (a) all 8 per-TC sharing watermarks to the max + * (b) all 2 per-color sharing watermarks to the max + * (2) There's a risk that we undercommit the resources if we configure + * (a) all 8 per-TC sharing watermarks to "max / 8" + * (b) all 2 per-color sharing watermarks to "max / 2" + * So for Linux, let's just disable the sharing watermarks per traffic class + * (setting them to 0 will make them always exceeded), and rely only on the + * sharing watermark for drop priority 0. So frames with drop priority set to 1 + * by QoS classification or policing will still be allowed, but only as long as + * the port and port-TC reservations are not exceeded. + */ +static void ocelot_disable_tc_sharing_watermarks(struct ocelot *ocelot) +{ + int prio; + + for (prio = 0; prio < OCELOT_NUM_TC; prio++) { + ocelot_wm_write(ocelot, BUF_PRIO_SHR_I(prio), 0); + ocelot_wm_write(ocelot, BUF_PRIO_SHR_E(prio), 0); + ocelot_wm_write(ocelot, REF_PRIO_SHR_I(prio), 0); + ocelot_wm_write(ocelot, REF_PRIO_SHR_E(prio), 0); + } +} + +static void ocelot_get_buf_rsrv(struct ocelot *ocelot, u32 *buf_rsrv_i, + u32 *buf_rsrv_e) +{ + int port, prio; + + *buf_rsrv_i = 0; + *buf_rsrv_e = 0; + + for (port = 0; port <= ocelot->num_phys_ports; port++) { + for (prio = 0; prio < OCELOT_NUM_TC; prio++) { + *buf_rsrv_i += ocelot_wm_read(ocelot, + BUF_Q_RSRV_I(port, prio)); + *buf_rsrv_e += ocelot_wm_read(ocelot, + BUF_Q_RSRV_E(port, prio)); + } + + *buf_rsrv_i += ocelot_wm_read(ocelot, BUF_P_RSRV_I(port)); + *buf_rsrv_e += ocelot_wm_read(ocelot, BUF_P_RSRV_E(port)); + } + + *buf_rsrv_i *= OCELOT_BUFFER_CELL_SZ; + *buf_rsrv_e *= OCELOT_BUFFER_CELL_SZ; +} + +static void ocelot_get_ref_rsrv(struct ocelot *ocelot, u32 *ref_rsrv_i, + u32 *ref_rsrv_e) +{ + int port, prio; + + *ref_rsrv_i = 0; + *ref_rsrv_e = 0; + + for (port = 0; port <= ocelot->num_phys_ports; port++) { + for (prio = 0; prio < OCELOT_NUM_TC; prio++) { + *ref_rsrv_i += ocelot_wm_read(ocelot, + REF_Q_RSRV_I(port, prio)); + *ref_rsrv_e += ocelot_wm_read(ocelot, + REF_Q_RSRV_E(port, prio)); + } + + *ref_rsrv_i += ocelot_wm_read(ocelot, REF_P_RSRV_I(port)); + *ref_rsrv_e += ocelot_wm_read(ocelot, REF_P_RSRV_E(port)); + } +} + +/* Calculate all reservations, then set up the sharing watermark for DP=0 to + * consume the remaining resources up to the pool's configured size. + */ +static void ocelot_setup_sharing_watermarks(struct ocelot *ocelot) +{ + u32 buf_rsrv_i, buf_rsrv_e; + u32 ref_rsrv_i, ref_rsrv_e; + u32 buf_shr_i, buf_shr_e; + u32 ref_shr_i, ref_shr_e; + + ocelot_get_buf_rsrv(ocelot, &buf_rsrv_i, &buf_rsrv_e); + ocelot_get_ref_rsrv(ocelot, &ref_rsrv_i, &ref_rsrv_e); + + buf_shr_i = ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_ING] - + buf_rsrv_i; + buf_shr_e = ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_EGR] - + buf_rsrv_e; + ref_shr_i = ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_ING] - + ref_rsrv_i; + ref_shr_e = ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_EGR] - + ref_rsrv_e; + + buf_shr_i /= OCELOT_BUFFER_CELL_SZ; + buf_shr_e /= OCELOT_BUFFER_CELL_SZ; + + ocelot_wm_write(ocelot, BUF_COL_SHR_I(0), buf_shr_i); + ocelot_wm_write(ocelot, BUF_COL_SHR_E(0), buf_shr_e); + ocelot_wm_write(ocelot, REF_COL_SHR_E(0), ref_shr_e); + ocelot_wm_write(ocelot, REF_COL_SHR_I(0), ref_shr_i); + ocelot_wm_write(ocelot, BUF_COL_SHR_I(1), 0); + ocelot_wm_write(ocelot, BUF_COL_SHR_E(1), 0); + ocelot_wm_write(ocelot, REF_COL_SHR_E(1), 0); + ocelot_wm_write(ocelot, REF_COL_SHR_I(1), 0); +} + +/* Ensure that all reservations can be enforced */ +static int ocelot_watermark_validate(struct ocelot *ocelot, + struct netlink_ext_ack *extack) +{ + u32 buf_rsrv_i, buf_rsrv_e; + u32 ref_rsrv_i, ref_rsrv_e; + + ocelot_get_buf_rsrv(ocelot, &buf_rsrv_i, &buf_rsrv_e); + ocelot_get_ref_rsrv(ocelot, &ref_rsrv_i, &ref_rsrv_e); + + if (buf_rsrv_i > ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_ING]) { + NL_SET_ERR_MSG_MOD(extack, + "Ingress frame reservations exceed pool size"); + return -ERANGE; + } + if (buf_rsrv_e > ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_EGR]) { + NL_SET_ERR_MSG_MOD(extack, + "Egress frame reservations exceed pool size"); + return -ERANGE; + } + if (ref_rsrv_i > ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_ING]) { + NL_SET_ERR_MSG_MOD(extack, + "Ingress reference reservations exceed pool size"); + return -ERANGE; + } + if (ref_rsrv_e > ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_EGR]) { + NL_SET_ERR_MSG_MOD(extack, + "Egress reference reservations exceed pool size"); + return -ERANGE; + } + + return 0; +} + +/* The hardware works like this: + * + * Frame forwarding decision taken + * | + * v + * +--------------------+--------------------+--------------------+ + * | | | | + * v v v v + * Ingress memory Egress memory Ingress frame Egress frame + * check check reference check reference check + * | | | | + * v v v v + * BUF_Q_RSRV_I ok BUF_Q_RSRV_E ok REF_Q_RSRV_I ok REF_Q_RSRV_E ok + *(src port, prio) -+ (dst port, prio) -+ (src port, prio) -+ (dst port, prio) -+ + * | | | | | | | | + * |exceeded | |exceeded | |exceeded | |exceeded | + * v | v | v | v | + * BUF_P_RSRV_I ok| BUF_P_RSRV_E ok| REF_P_RSRV_I ok| REF_P_RSRV_E ok| + * (src port) ----+ (dst port) ----+ (src port) ----+ (dst port) -----+ + * | | | | | | | | + * |exceeded | |exceeded | |exceeded | |exceeded | + * v | v | v | v | + * BUF_PRIO_SHR_I ok| BUF_PRIO_SHR_E ok| REF_PRIO_SHR_I ok| REF_PRIO_SHR_E ok| + * (prio) ------+ (prio) ------+ (prio) ------+ (prio) -------+ + * | | | | | | | | + * |exceeded | |exceeded | |exceeded | |exceeded | + * v | v | v | v | + * BUF_COL_SHR_I ok| BUF_COL_SHR_E ok| REF_COL_SHR_I ok| REF_COL_SHR_E ok| + * (dp) -------+ (dp) -------+ (dp) -------+ (dp) --------+ + * | | | | | | | | + * |exceeded | |exceeded | |exceeded | |exceeded | + * v v v v v v v v + * fail success fail success fail success fail success + * | | | | | | | | + * v v v v v v v v + * +-----+----+ +-----+----+ +-----+----+ +-----+-----+ + * | | | | + * +-------> OR <-------+ +-------> OR <-------+ + * | | + * v v + * +----------------> AND <-----------------+ + * | + * v + * FIFO drop / accept + * + * We are modeling each of the 4 parallel lookups as a devlink-sb pool. + * At least one (ingress or egress) memory pool and one (ingress or egress) + * frame reference pool need to have resources for frame acceptance to succeed. + * + * The following watermarks are controlled explicitly through devlink-sb: + * BUF_Q_RSRV_I, BUF_Q_RSRV_E, REF_Q_RSRV_I, REF_Q_RSRV_E + * BUF_P_RSRV_I, BUF_P_RSRV_E, REF_P_RSRV_I, REF_P_RSRV_E + * The following watermarks are controlled implicitly through devlink-sb: + * BUF_COL_SHR_I, BUF_COL_SHR_E, REF_COL_SHR_I, REF_COL_SHR_E + * The following watermarks are unused and disabled: + * BUF_PRIO_SHR_I, BUF_PRIO_SHR_E, REF_PRIO_SHR_I, REF_PRIO_SHR_E + * + * This function overrides the hardware defaults with more sane ones (no + * reservations by default, let sharing use all resources) and disables the + * unused watermarks. + */ +static void ocelot_watermark_init(struct ocelot *ocelot) +{ + int all_tcs = GENMASK(OCELOT_NUM_TC - 1, 0); + int port; + + ocelot_write(ocelot, all_tcs, QSYS_RES_QOS_MODE); + + for (port = 0; port <= ocelot->num_phys_ports; port++) + ocelot_disable_reservation_watermarks(ocelot, port); + + ocelot_disable_tc_sharing_watermarks(ocelot); + ocelot_setup_sharing_watermarks(ocelot); +} + +/* Pool size and type are fixed up at runtime. Keeping this structure to + * look up the cell size multipliers. + */ +static const struct devlink_sb_pool_info ocelot_sb_pool[] = { + [OCELOT_SB_BUF] = { + .cell_size = OCELOT_BUFFER_CELL_SZ, + .threshold_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC, + }, + [OCELOT_SB_REF] = { + .cell_size = 1, + .threshold_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC, + }, +}; + +/* Returns the pool size configured through ocelot_sb_pool_set */ +int ocelot_sb_pool_get(struct ocelot *ocelot, unsigned int sb_index, + u16 pool_index, + struct devlink_sb_pool_info *pool_info) +{ + if (sb_index >= OCELOT_SB_NUM) + return -ENODEV; + if (pool_index >= OCELOT_SB_POOL_NUM) + return -ENODEV; + + *pool_info = ocelot_sb_pool[sb_index]; + pool_info->size = ocelot->pool_size[sb_index][pool_index]; + if (pool_index) + pool_info->pool_type = DEVLINK_SB_POOL_TYPE_INGRESS; + else + pool_info->pool_type = DEVLINK_SB_POOL_TYPE_EGRESS; + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_pool_get); + +/* The pool size received here configures the total amount of resources used on + * ingress (or on egress, depending upon the pool index). The pool size, minus + * the values for the port and port-tc reservations, is written into the + * COL_SHR(dp=0) sharing watermark. + */ +int ocelot_sb_pool_set(struct ocelot *ocelot, unsigned int sb_index, + u16 pool_index, u32 size, + enum devlink_sb_threshold_type threshold_type, + struct netlink_ext_ack *extack) +{ + u32 old_pool_size; + int err; + + if (sb_index >= OCELOT_SB_NUM) { + NL_SET_ERR_MSG_MOD(extack, + "Invalid sb, use 0 for buffers and 1 for frame references"); + return -ENODEV; + } + if (pool_index >= OCELOT_SB_POOL_NUM) { + NL_SET_ERR_MSG_MOD(extack, + "Invalid pool, use 0 for ingress and 1 for egress"); + return -ENODEV; + } + if (threshold_type != DEVLINK_SB_THRESHOLD_TYPE_STATIC) { + NL_SET_ERR_MSG_MOD(extack, + "Only static threshold supported"); + return -EOPNOTSUPP; + } + + old_pool_size = ocelot->pool_size[sb_index][pool_index]; + ocelot->pool_size[sb_index][pool_index] = size; + + err = ocelot_watermark_validate(ocelot, extack); + if (err) { + ocelot->pool_size[sb_index][pool_index] = old_pool_size; + return err; + } + + ocelot_setup_sharing_watermarks(ocelot); + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_pool_set); + +/* This retrieves the configuration made with ocelot_sb_port_pool_set */ +int ocelot_sb_port_pool_get(struct ocelot *ocelot, int port, + unsigned int sb_index, u16 pool_index, + u32 *p_threshold) +{ + int wm_index; + + switch (sb_index) { + case OCELOT_SB_BUF: + if (pool_index == OCELOT_SB_POOL_ING) + wm_index = BUF_P_RSRV_I(port); + else + wm_index = BUF_P_RSRV_E(port); + break; + case OCELOT_SB_REF: + if (pool_index == OCELOT_SB_POOL_ING) + wm_index = REF_P_RSRV_I(port); + else + wm_index = REF_P_RSRV_E(port); + break; + default: + return -ENODEV; + } + + *p_threshold = ocelot_wm_read(ocelot, wm_index); + *p_threshold *= ocelot_sb_pool[sb_index].cell_size; + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_port_pool_get); + +/* This configures the P_RSRV per-port reserved resource watermark */ +int ocelot_sb_port_pool_set(struct ocelot *ocelot, int port, + unsigned int sb_index, u16 pool_index, + u32 threshold, struct netlink_ext_ack *extack) +{ + int wm_index, err; + u32 old_thr; + + switch (sb_index) { + case OCELOT_SB_BUF: + if (pool_index == OCELOT_SB_POOL_ING) + wm_index = BUF_P_RSRV_I(port); + else + wm_index = BUF_P_RSRV_E(port); + break; + case OCELOT_SB_REF: + if (pool_index == OCELOT_SB_POOL_ING) + wm_index = REF_P_RSRV_I(port); + else + wm_index = REF_P_RSRV_E(port); + break; + default: + NL_SET_ERR_MSG_MOD(extack, "Invalid shared buffer"); + return -ENODEV; + } + + threshold /= ocelot_sb_pool[sb_index].cell_size; + + old_thr = ocelot_wm_read(ocelot, wm_index); + ocelot_wm_write(ocelot, wm_index, threshold); + + err = ocelot_watermark_validate(ocelot, extack); + if (err) { + ocelot_wm_write(ocelot, wm_index, old_thr); + return err; + } + + ocelot_setup_sharing_watermarks(ocelot); + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_port_pool_set); + +/* This retrieves the configuration done by ocelot_sb_tc_pool_bind_set */ +int ocelot_sb_tc_pool_bind_get(struct ocelot *ocelot, int port, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 *p_pool_index, u32 *p_threshold) +{ + int wm_index; + + switch (sb_index) { + case OCELOT_SB_BUF: + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + wm_index = BUF_Q_RSRV_I(port, tc_index); + else + wm_index = BUF_Q_RSRV_E(port, tc_index); + break; + case OCELOT_SB_REF: + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + wm_index = REF_Q_RSRV_I(port, tc_index); + else + wm_index = REF_Q_RSRV_E(port, tc_index); + break; + default: + return -ENODEV; + } + + *p_threshold = ocelot_wm_read(ocelot, wm_index); + *p_threshold *= ocelot_sb_pool[sb_index].cell_size; + + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + *p_pool_index = 0; + else + *p_pool_index = 1; + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_tc_pool_bind_get); + +/* This configures the Q_RSRV per-port-tc reserved resource watermark */ +int ocelot_sb_tc_pool_bind_set(struct ocelot *ocelot, int port, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 pool_index, u32 threshold, + struct netlink_ext_ack *extack) +{ + int wm_index, err; + u32 old_thr; + + /* Paranoid check? */ + if (pool_index == OCELOT_SB_POOL_ING && + pool_type != DEVLINK_SB_POOL_TYPE_INGRESS) + return -EINVAL; + if (pool_index == OCELOT_SB_POOL_EGR && + pool_type != DEVLINK_SB_POOL_TYPE_EGRESS) + return -EINVAL; + + switch (sb_index) { + case OCELOT_SB_BUF: + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + wm_index = BUF_Q_RSRV_I(port, tc_index); + else + wm_index = BUF_Q_RSRV_E(port, tc_index); + break; + case OCELOT_SB_REF: + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + wm_index = REF_Q_RSRV_I(port, tc_index); + else + wm_index = REF_Q_RSRV_E(port, tc_index); + break; + default: + NL_SET_ERR_MSG_MOD(extack, "Invalid shared buffer"); + return -ENODEV; + } + + threshold /= ocelot_sb_pool[sb_index].cell_size; + + old_thr = ocelot_wm_read(ocelot, wm_index); + ocelot_wm_write(ocelot, wm_index, threshold); + err = ocelot_watermark_validate(ocelot, extack); + if (err) { + ocelot_wm_write(ocelot, wm_index, old_thr); + return err; + } + + ocelot_setup_sharing_watermarks(ocelot); + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_tc_pool_bind_set); + +/* The hardware does not support atomic snapshots, we'll read out the + * occupancy registers individually and have this as just a stub. + */ +int ocelot_sb_occ_snapshot(struct ocelot *ocelot, unsigned int sb_index) +{ + return 0; +} +EXPORT_SYMBOL(ocelot_sb_occ_snapshot); + +/* The watermark occupancy registers are cleared upon read, + * so let's read them. + */ +int ocelot_sb_occ_max_clear(struct ocelot *ocelot, unsigned int sb_index) +{ + u32 inuse, maxuse; + int port, prio; + + switch (sb_index) { + case OCELOT_SB_BUF: + for (port = 0; port <= ocelot->num_phys_ports; port++) { + for (prio = 0; prio < OCELOT_NUM_TC; prio++) { + ocelot_wm_status(ocelot, BUF_Q_RSRV_I(port, prio), + &inuse, &maxuse); + ocelot_wm_status(ocelot, BUF_Q_RSRV_E(port, prio), + &inuse, &maxuse); + } + ocelot_wm_status(ocelot, BUF_P_RSRV_I(port), + &inuse, &maxuse); + ocelot_wm_status(ocelot, BUF_P_RSRV_E(port), + &inuse, &maxuse); + } + break; + case OCELOT_SB_REF: + for (port = 0; port <= ocelot->num_phys_ports; port++) { + for (prio = 0; prio < OCELOT_NUM_TC; prio++) { + ocelot_wm_status(ocelot, REF_Q_RSRV_I(port, prio), + &inuse, &maxuse); + ocelot_wm_status(ocelot, REF_Q_RSRV_E(port, prio), + &inuse, &maxuse); + } + ocelot_wm_status(ocelot, REF_P_RSRV_I(port), + &inuse, &maxuse); + ocelot_wm_status(ocelot, REF_P_RSRV_E(port), + &inuse, &maxuse); + } + break; + default: + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_occ_max_clear); + +/* This retrieves the watermark occupancy for per-port P_RSRV watermarks */ +int ocelot_sb_occ_port_pool_get(struct ocelot *ocelot, int port, + unsigned int sb_index, u16 pool_index, + u32 *p_cur, u32 *p_max) +{ + int wm_index; + + switch (sb_index) { + case OCELOT_SB_BUF: + if (pool_index == OCELOT_SB_POOL_ING) + wm_index = BUF_P_RSRV_I(port); + else + wm_index = BUF_P_RSRV_E(port); + break; + case OCELOT_SB_REF: + if (pool_index == OCELOT_SB_POOL_ING) + wm_index = REF_P_RSRV_I(port); + else + wm_index = REF_P_RSRV_E(port); + break; + default: + return -ENODEV; + } + + ocelot_wm_status(ocelot, wm_index, p_cur, p_max); + *p_cur *= ocelot_sb_pool[sb_index].cell_size; + *p_max *= ocelot_sb_pool[sb_index].cell_size; + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_occ_port_pool_get); + +/* This retrieves the watermark occupancy for per-port-tc Q_RSRV watermarks */ +int ocelot_sb_occ_tc_port_bind_get(struct ocelot *ocelot, int port, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u32 *p_cur, u32 *p_max) +{ + int wm_index; + + switch (sb_index) { + case OCELOT_SB_BUF: + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + wm_index = BUF_Q_RSRV_I(port, tc_index); + else + wm_index = BUF_Q_RSRV_E(port, tc_index); + break; + case OCELOT_SB_REF: + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + wm_index = REF_Q_RSRV_I(port, tc_index); + else + wm_index = REF_Q_RSRV_E(port, tc_index); + break; + default: + return -ENODEV; + } + + ocelot_wm_status(ocelot, wm_index, p_cur, p_max); + *p_cur *= ocelot_sb_pool[sb_index].cell_size; + *p_max *= ocelot_sb_pool[sb_index].cell_size; + + return 0; +} +EXPORT_SYMBOL(ocelot_sb_occ_tc_port_bind_get); + +int ocelot_devlink_sb_register(struct ocelot *ocelot) +{ + int err; + + err = devlink_sb_register(ocelot->devlink, OCELOT_SB_BUF, + ocelot->packet_buffer_size, 1, 1, + OCELOT_NUM_TC, OCELOT_NUM_TC); + if (err) + return err; + + err = devlink_sb_register(ocelot->devlink, OCELOT_SB_REF, + ocelot->num_frame_refs, 1, 1, + OCELOT_NUM_TC, OCELOT_NUM_TC); + if (err) { + devlink_sb_unregister(ocelot->devlink, OCELOT_SB_BUF); + return err; + } + + ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_ING] = ocelot->packet_buffer_size; + ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_EGR] = ocelot->packet_buffer_size; + ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_ING] = ocelot->num_frame_refs; + ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_EGR] = ocelot->num_frame_refs; + + ocelot_watermark_init(ocelot); + + return 0; +} +EXPORT_SYMBOL(ocelot_devlink_sb_register); + +void ocelot_devlink_sb_unregister(struct ocelot *ocelot) +{ + devlink_sb_unregister(ocelot->devlink, OCELOT_SB_BUF); + devlink_sb_unregister(ocelot->devlink, OCELOT_SB_REF); +} +EXPORT_SYMBOL(ocelot_devlink_sb_unregister); diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 42230f92ca9c..9553eb3e441c 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -1,13 +1,190 @@ // SPDX-License-Identifier: (GPL-2.0 OR MIT) /* Microsemi Ocelot Switch driver * + * This contains glue logic between the switchdev driver operations and the + * mscc_ocelot_switch_lib. + * * Copyright (c) 2017, 2019 Microsemi Corporation + * Copyright 2020-2021 NXP Semiconductors */ #include <linux/if_bridge.h> #include "ocelot.h" #include "ocelot_vcap.h" +static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp) +{ + return devlink_priv(dlp->devlink); +} + +static int devlink_port_to_port(struct devlink_port *dlp) +{ + struct ocelot *ocelot = devlink_port_to_ocelot(dlp); + + return dlp - ocelot->devlink_ports; +} + +static int ocelot_devlink_sb_pool_get(struct devlink *dl, + unsigned int sb_index, u16 pool_index, + struct devlink_sb_pool_info *pool_info) +{ + struct ocelot *ocelot = devlink_priv(dl); + + return ocelot_sb_pool_get(ocelot, sb_index, pool_index, pool_info); +} + +static int ocelot_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index, + u16 pool_index, u32 size, + enum devlink_sb_threshold_type threshold_type, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = devlink_priv(dl); + + return ocelot_sb_pool_set(ocelot, sb_index, pool_index, size, + threshold_type, extack); +} + +static int ocelot_devlink_sb_port_pool_get(struct devlink_port *dlp, + unsigned int sb_index, u16 pool_index, + u32 *p_threshold) +{ + struct ocelot *ocelot = devlink_port_to_ocelot(dlp); + int port = devlink_port_to_port(dlp); + + return ocelot_sb_port_pool_get(ocelot, port, sb_index, pool_index, + p_threshold); +} + +static int ocelot_devlink_sb_port_pool_set(struct devlink_port *dlp, + unsigned int sb_index, u16 pool_index, + u32 threshold, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = devlink_port_to_ocelot(dlp); + int port = devlink_port_to_port(dlp); + + return ocelot_sb_port_pool_set(ocelot, port, sb_index, pool_index, + threshold, extack); +} + +static int +ocelot_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 *p_pool_index, u32 *p_threshold) +{ + struct ocelot *ocelot = devlink_port_to_ocelot(dlp); + int port = devlink_port_to_port(dlp); + + return ocelot_sb_tc_pool_bind_get(ocelot, port, sb_index, tc_index, + pool_type, p_pool_index, + p_threshold); +} + +static int +ocelot_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 pool_index, u32 threshold, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = devlink_port_to_ocelot(dlp); + int port = devlink_port_to_port(dlp); + + return ocelot_sb_tc_pool_bind_set(ocelot, port, sb_index, tc_index, + pool_type, pool_index, threshold, + extack); +} + +static int ocelot_devlink_sb_occ_snapshot(struct devlink *dl, + unsigned int sb_index) +{ + struct ocelot *ocelot = devlink_priv(dl); + + return ocelot_sb_occ_snapshot(ocelot, sb_index); +} + +static int ocelot_devlink_sb_occ_max_clear(struct devlink *dl, + unsigned int sb_index) +{ + struct ocelot *ocelot = devlink_priv(dl); + + return ocelot_sb_occ_max_clear(ocelot, sb_index); +} + +static int ocelot_devlink_sb_occ_port_pool_get(struct devlink_port *dlp, + unsigned int sb_index, + u16 pool_index, u32 *p_cur, + u32 *p_max) +{ + struct ocelot *ocelot = devlink_port_to_ocelot(dlp); + int port = devlink_port_to_port(dlp); + + return ocelot_sb_occ_port_pool_get(ocelot, port, sb_index, pool_index, + p_cur, p_max); +} + +static int +ocelot_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u32 *p_cur, u32 *p_max) +{ + struct ocelot *ocelot = devlink_port_to_ocelot(dlp); + int port = devlink_port_to_port(dlp); + + return ocelot_sb_occ_tc_port_bind_get(ocelot, port, sb_index, + tc_index, pool_type, + p_cur, p_max); +} + +const struct devlink_ops ocelot_devlink_ops = { + .sb_pool_get = ocelot_devlink_sb_pool_get, + .sb_pool_set = ocelot_devlink_sb_pool_set, + .sb_port_pool_get = ocelot_devlink_sb_port_pool_get, + .sb_port_pool_set = ocelot_devlink_sb_port_pool_set, + .sb_tc_pool_bind_get = ocelot_devlink_sb_tc_pool_bind_get, + .sb_tc_pool_bind_set = ocelot_devlink_sb_tc_pool_bind_set, + .sb_occ_snapshot = ocelot_devlink_sb_occ_snapshot, + .sb_occ_max_clear = ocelot_devlink_sb_occ_max_clear, + .sb_occ_port_pool_get = ocelot_devlink_sb_occ_port_pool_get, + .sb_occ_tc_port_bind_get = ocelot_devlink_sb_occ_tc_port_bind_get, +}; + +int ocelot_port_devlink_init(struct ocelot *ocelot, int port, + enum devlink_port_flavour flavour) +{ + struct devlink_port *dlp = &ocelot->devlink_ports[port]; + int id_len = sizeof(ocelot->base_mac); + struct devlink *dl = ocelot->devlink; + struct devlink_port_attrs attrs = {}; + + memcpy(attrs.switch_id.id, &ocelot->base_mac, id_len); + attrs.switch_id.id_len = id_len; + attrs.phys.port_number = port; + attrs.flavour = flavour; + + devlink_port_attrs_set(dlp, &attrs); + + return devlink_port_register(dl, dlp, port); +} + +void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port) +{ + struct devlink_port *dlp = &ocelot->devlink_ports[port]; + + devlink_port_unregister(dlp); +} + +static struct devlink_port *ocelot_get_devlink_port(struct net_device *dev) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + return &ocelot->devlink_ports[port]; +} + int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, struct flow_cls_offload *f, bool ingress) @@ -457,7 +634,7 @@ static void ocelot_mact_work(struct work_struct *work) break; default: break; - }; + } kfree(w); } @@ -525,20 +702,6 @@ static void ocelot_set_rx_mode(struct net_device *dev) __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); @@ -689,18 +852,6 @@ static int ocelot_set_features(struct net_device *dev, 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); @@ -727,7 +878,6 @@ static const struct net_device_ops ocelot_port_netdev_ops = { .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, @@ -736,9 +886,9 @@ static const struct net_device_ops ocelot_port_netdev_ops = { .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, + .ndo_get_devlink_port = ocelot_get_devlink_port, }; struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port) @@ -825,12 +975,8 @@ static const struct ethtool_ops ocelot_ethtool_ops = { }; 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); } @@ -858,8 +1004,7 @@ static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc) } static int ocelot_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr, - struct switchdev_trans *trans) + const struct switchdev_attr *attr) { struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot *ocelot = priv->port.ocelot; @@ -868,15 +1013,13 @@ static int ocelot_port_attr_set(struct net_device *dev, switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - ocelot_port_attr_stp_state_set(ocelot, port, trans, - attr->u.stp_state); + ocelot_port_attr_stp_state_set(ocelot, port, 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, trans); + 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); @@ -890,56 +1033,27 @@ static int ocelot_port_attr_set(struct net_device *dev, } static int ocelot_port_obj_add_vlan(struct net_device *dev, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) + const struct switchdev_obj_port_vlan *vlan) { + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; int ret; - u16 vid; - - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; - bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; - - if (switchdev_trans_ph_prepare(trans)) - ret = ocelot_vlan_vid_prepare(dev, vid, pvid, - untagged); - else - ret = ocelot_vlan_vid_add(dev, vid, pvid, 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; - } + ret = ocelot_vlan_vid_prepare(dev, vlan->vid, pvid, untagged); + if (ret) + return ret; - return 0; + return ocelot_vlan_vid_add(dev, vlan->vid, pvid, untagged); } static int ocelot_port_obj_add_mdb(struct net_device *dev, - const struct switchdev_obj_port_mdb *mdb, - struct switchdev_trans *trans) + 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; - if (switchdev_trans_ph_prepare(trans)) - return 0; - return ocelot_port_mdb_add(ocelot, port, mdb); } @@ -956,7 +1070,6 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, 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; @@ -964,12 +1077,10 @@ static int ocelot_port_obj_add(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: ret = ocelot_port_obj_add_vlan(dev, - SWITCHDEV_OBJ_PORT_VLAN(obj), - trans); + SWITCHDEV_OBJ_PORT_VLAN(obj)); break; case SWITCHDEV_OBJ_ID_PORT_MDB: - ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj), - trans); + ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj)); break; default: return -EOPNOTSUPP; @@ -985,8 +1096,8 @@ static int ocelot_port_obj_del(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: - ret = ocelot_port_vlan_del_vlan(dev, - SWITCHDEV_OBJ_PORT_VLAN(obj)); + ret = ocelot_vlan_vid_del(dev, + SWITCHDEV_OBJ_PORT_VLAN(obj)->vid); break; case SWITCHDEV_OBJ_ID_PORT_MDB: ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj)); diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c index 9cf2bc5f4289..30a38df08a21 100644 --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c @@ -517,7 +517,6 @@ static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops) 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; @@ -764,9 +763,25 @@ static u16 ocelot_wm_enc(u16 value) return value; } +static u16 ocelot_wm_dec(u16 wm) +{ + if (wm & BIT(8)) + return (wm & GENMASK(7, 0)) * 16; + + return wm; +} + +static void ocelot_wm_stat(u32 val, u32 *inuse, u32 *maxuse) +{ + *inuse = (val & GENMASK(23, 12)) >> 12; + *maxuse = val & GENMASK(11, 0); +} + static const struct ocelot_ops ocelot_ops = { .reset = ocelot_reset, .wm_enc = ocelot_wm_enc, + .wm_dec = ocelot_wm_dec, + .wm_stat = ocelot_wm_stat, .port_to_netdev = ocelot_port_to_netdev, .netdev_to_port = ocelot_netdev_to_port, }; @@ -1036,6 +1051,14 @@ static struct ptp_clock_info ocelot_ptp_clock_info = { .enable = ocelot_ptp_enable, }; +static void mscc_ocelot_teardown_devlink_ports(struct ocelot *ocelot) +{ + int port; + + for (port = 0; port < ocelot->num_phys_ports; port++) + ocelot_port_devlink_teardown(ocelot, port); +} + static void mscc_ocelot_release_ports(struct ocelot *ocelot) { int port; @@ -1063,28 +1086,44 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev, { struct ocelot *ocelot = platform_get_drvdata(pdev); struct device_node *portnp; - int err; + bool *registered_ports; + int port, err; + u32 reg; ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports, sizeof(struct ocelot_port *), GFP_KERNEL); if (!ocelot->ports) return -ENOMEM; + ocelot->devlink_ports = devm_kcalloc(ocelot->dev, + ocelot->num_phys_ports, + sizeof(*ocelot->devlink_ports), + GFP_KERNEL); + if (!ocelot->devlink_ports) + return -ENOMEM; + + registered_ports = kcalloc(ocelot->num_phys_ports, sizeof(bool), + GFP_KERNEL); + if (!registered_ports) + return -ENOMEM; + for_each_available_child_of_node(ports, portnp) { struct ocelot_port_private *priv; struct ocelot_port *ocelot_port; struct device_node *phy_node; + struct devlink_port *dlp; phy_interface_t phy_mode; struct phy_device *phy; struct regmap *target; struct resource *res; struct phy *serdes; char res_name[8]; - u32 port; - if (of_property_read_u32(portnp, "reg", &port)) + if (of_property_read_u32(portnp, "reg", ®)) continue; + port = reg; + snprintf(res_name, sizeof(res_name), "port%d", port); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -1102,15 +1141,26 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev, if (!phy) continue; + err = ocelot_port_devlink_init(ocelot, port, + DEVLINK_PORT_FLAVOUR_PHYSICAL); + if (err) { + of_node_put(portnp); + goto out_teardown; + } + err = ocelot_probe_port(ocelot, port, target, phy); if (err) { of_node_put(portnp); - return err; + goto out_teardown; } + registered_ports[port] = true; + ocelot_port = ocelot->ports[port]; priv = container_of(ocelot_port, struct ocelot_port_private, port); + dlp = &ocelot->devlink_ports[port]; + devlink_port_type_eth_set(dlp, priv->dev); of_get_phy_mode(portnp, &phy_mode); @@ -1135,7 +1185,8 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev, "invalid phy mode for port%d, (Q)SGMII only\n", port); of_node_put(portnp); - return -EINVAL; + err = -EINVAL; + goto out_teardown; } serdes = devm_of_phy_get(ocelot->dev, portnp, NULL); @@ -1149,13 +1200,46 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev, port); of_node_put(portnp); - return err; + goto out_teardown; } priv->serdes = serdes; } + /* Initialize unused devlink ports at the end */ + for (port = 0; port < ocelot->num_phys_ports; port++) { + if (registered_ports[port]) + continue; + + err = ocelot_port_devlink_init(ocelot, port, + DEVLINK_PORT_FLAVOUR_UNUSED); + if (err) { + while (port-- >= 0) { + if (!registered_ports[port]) + continue; + ocelot_port_devlink_teardown(ocelot, port); + } + + goto out_teardown; + } + } + + kfree(registered_ports); + return 0; + +out_teardown: + /* Unregister the network interfaces */ + mscc_ocelot_release_ports(ocelot); + /* Tear down devlink ports for the registered network interfaces */ + for (port = 0; port < ocelot->num_phys_ports; port++) { + if (!registered_ports[port]) + continue; + + ocelot_port_devlink_teardown(ocelot, port); + } + kfree(registered_ports); + return err; } static int mscc_ocelot_probe(struct platform_device *pdev) @@ -1163,6 +1247,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; int err, irq_xtr, irq_ptp_rdy; struct device_node *ports; + struct devlink *devlink; struct ocelot *ocelot; struct regmap *hsio; unsigned int i; @@ -1186,10 +1271,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev) if (!np && !pdev->dev.platform_data) return -ENODEV; - ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL); - if (!ocelot) + devlink = devlink_alloc(&ocelot_devlink_ops, sizeof(*ocelot)); + if (!devlink) return -ENOMEM; + ocelot = devlink_priv(devlink); + ocelot->devlink = priv_to_devlink(ocelot); platform_set_drvdata(pdev, ocelot); ocelot->dev = &pdev->dev; @@ -1206,7 +1293,8 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ocelot->targets[io_target[i].id] = NULL; continue; } - return PTR_ERR(target); + err = PTR_ERR(target); + goto out_free_devlink; } ocelot->targets[io_target[i].id] = target; @@ -1215,24 +1303,25 @@ static int mscc_ocelot_probe(struct platform_device *pdev) 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); + err = PTR_ERR(hsio); + goto out_free_devlink; } ocelot->targets[HSIO] = hsio; err = ocelot_chip_init(ocelot, &ocelot_ops); if (err) - return err; + goto out_free_devlink; irq_xtr = platform_get_irq_byname(pdev, "xtr"); if (irq_xtr < 0) - return -ENODEV; + goto out_free_devlink; err = devm_request_threaded_irq(&pdev->dev, irq_xtr, NULL, ocelot_xtr_irq_handler, IRQF_ONESHOT, "frame extraction", ocelot); if (err) - return err; + goto out_free_devlink; irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy"); if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) { @@ -1241,7 +1330,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev) IRQF_ONESHOT, "ptp ready", ocelot); if (err) - return err; + goto out_free_devlink; /* Both the PTP interrupt and the PTP bank are available */ ocelot->ptp = 1; @@ -1250,7 +1339,8 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ports = of_get_child_by_name(np, "ethernet-ports"); if (!ports) { dev_err(ocelot->dev, "no ethernet-ports child node found\n"); - return -ENODEV; + err = -ENODEV; + goto out_free_devlink; } ocelot->num_phys_ports = of_get_child_count(ports); @@ -1265,10 +1355,18 @@ static int mscc_ocelot_probe(struct platform_device *pdev) if (err) goto out_put_ports; - err = mscc_ocelot_init_ports(pdev, ports); + err = devlink_register(devlink, ocelot->dev); if (err) goto out_ocelot_deinit; + err = mscc_ocelot_init_ports(pdev, ports); + if (err) + goto out_ocelot_devlink_unregister; + + err = ocelot_devlink_sb_register(ocelot); + if (err) + goto out_ocelot_release_ports; + if (ocelot->ptp) { err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info); if (err) { @@ -1288,10 +1386,17 @@ static int mscc_ocelot_probe(struct platform_device *pdev) return 0; +out_ocelot_release_ports: + mscc_ocelot_release_ports(ocelot); + mscc_ocelot_teardown_devlink_ports(ocelot); +out_ocelot_devlink_unregister: + devlink_unregister(devlink); out_ocelot_deinit: ocelot_deinit(ocelot); out_put_ports: of_node_put(ports); +out_free_devlink: + devlink_free(devlink); return err; } @@ -1300,11 +1405,15 @@ static int mscc_ocelot_remove(struct platform_device *pdev) struct ocelot *ocelot = platform_get_drvdata(pdev); ocelot_deinit_timestamp(ocelot); + ocelot_devlink_sb_unregister(ocelot); mscc_ocelot_release_ports(ocelot); + mscc_ocelot_teardown_devlink_ports(ocelot); + devlink_unregister(ocelot->devlink); ocelot_deinit(ocelot); unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); unregister_switchdev_notifier(&ocelot_switchdev_nb); unregister_netdevice_notifier(&ocelot_netdevice_nb); + devlink_free(ocelot->devlink); return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index 0a721f6e8676..e31f8fbbc696 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -3109,13 +3109,19 @@ mem_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, bool is64) return 0; } -static int mem_xadd4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +static int mem_atomic4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { + if (meta->insn.imm != BPF_ADD) + return -EOPNOTSUPP; + return mem_xadd(nfp_prog, meta, false); } -static int mem_xadd8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +static int mem_atomic8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { + if (meta->insn.imm != BPF_ADD) + return -EOPNOTSUPP; + return mem_xadd(nfp_prog, meta, true); } @@ -3475,8 +3481,8 @@ static const instr_cb_t instr_cb[256] = { [BPF_STX | BPF_MEM | BPF_H] = mem_stx2, [BPF_STX | BPF_MEM | BPF_W] = mem_stx4, [BPF_STX | BPF_MEM | BPF_DW] = mem_stx8, - [BPF_STX | BPF_XADD | BPF_W] = mem_xadd4, - [BPF_STX | BPF_XADD | BPF_DW] = mem_xadd8, + [BPF_STX | BPF_ATOMIC | BPF_W] = mem_atomic4, + [BPF_STX | BPF_ATOMIC | BPF_DW] = mem_atomic8, [BPF_ST | BPF_MEM | BPF_B] = mem_st1, [BPF_ST | BPF_MEM | BPF_H] = mem_st2, [BPF_ST | BPF_MEM | BPF_W] = mem_st4, diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h index fac9c6f9e197..d0e17eebddd9 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.h +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h @@ -428,9 +428,9 @@ static inline bool is_mbpf_classic_store_pkt(const struct nfp_insn_meta *meta) return is_mbpf_classic_store(meta) && meta->ptr.type == PTR_TO_PACKET; } -static inline bool is_mbpf_xadd(const struct nfp_insn_meta *meta) +static inline bool is_mbpf_atomic(const struct nfp_insn_meta *meta) { - return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_XADD); + return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_ATOMIC); } static inline bool is_mbpf_mul(const struct nfp_insn_meta *meta) diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c index e92ee510fd52..9d235c0ce46a 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c @@ -479,7 +479,7 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, pr_vlog(env, "map writes not supported\n"); return -EOPNOTSUPP; } - if (is_mbpf_xadd(meta)) { + if (is_mbpf_atomic(meta)) { err = nfp_bpf_map_mark_used(env, meta, reg, NFP_MAP_USE_ATOMIC_CNT); if (err) @@ -523,12 +523,17 @@ exit_check_ptr: } static int -nfp_bpf_check_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, - struct bpf_verifier_env *env) +nfp_bpf_check_atomic(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, + struct bpf_verifier_env *env) { const struct bpf_reg_state *sreg = cur_regs(env) + meta->insn.src_reg; const struct bpf_reg_state *dreg = cur_regs(env) + meta->insn.dst_reg; + if (meta->insn.imm != BPF_ADD) { + pr_vlog(env, "atomic op not implemented: %d\n", meta->insn.imm); + return -EOPNOTSUPP; + } + if (dreg->type != PTR_TO_MAP_VALUE) { pr_vlog(env, "atomic add not to a map value pointer: %d\n", dreg->type); @@ -655,8 +660,8 @@ int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, if (is_mbpf_store(meta)) return nfp_bpf_check_store(nfp_prog, meta, env); - if (is_mbpf_xadd(meta)) - return nfp_bpf_check_xadd(nfp_prog, meta, env); + if (is_mbpf_atomic(meta)) + return nfp_bpf_check_atomic(nfp_prog, meta, env); if (is_mbpf_alu(meta)) return nfp_bpf_check_alu(nfp_prog, meta, env); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index f21fb573ea3e..eeb30680b4dc 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1822,8 +1822,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) rcu_read_lock(); xdp_prog = READ_ONCE(dp->xdp_prog); true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; - xdp.frame_sz = PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM; - xdp.rxq = &rx_ring->xdp_rxq; + xdp_init_buff(&xdp, PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM, + &rx_ring->xdp_rxq); tx_ring = r_vec->xdp_ring; while (pkts_polled < budget) { @@ -1914,10 +1914,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) unsigned int dma_off; int act; - xdp.data_hard_start = rxbuf->frag + NFP_NET_RX_BUF_HEADROOM; - xdp.data = orig_data; - xdp.data_meta = orig_data; - xdp.data_end = orig_data + pkt_len; + xdp_prepare_buff(&xdp, + rxbuf->frag + NFP_NET_RX_BUF_HEADROOM, + pkt_off - NFP_NET_RX_BUF_HEADROOM, + pkt_len, true); act = bpf_prog_run_xdp(xdp_prog, &xdp); @@ -3656,8 +3656,6 @@ const struct net_device_ops nfp_net_netdev_ops = { .ndo_set_features = nfp_net_set_features, .ndo_features_check = nfp_net_features_check, .ndo_get_phys_port_name = nfp_net_get_phys_port_name, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_bpf = nfp_net_xdp, .ndo_get_devlink_port = nfp_devlink_get_devlink_port, }; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index ac4cd5d82e69..162a1ff1e9d2 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -979,7 +979,7 @@ static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb) stats->vlan_inserted++; } - if (skb->csum_not_inet) + if (skb_csum_is_sctp(skb)) stats->crc32_csum++; else stats->csum++; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c index 5e9f8ee99800..2fcbcecb41d1 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c @@ -113,7 +113,8 @@ netxen_get_minidump_template(struct netxen_adapter *adapter) return NX_RCODE_INVALID_ARGS; } - addr = pci_zalloc_consistent(adapter->pdev, size, &md_template_addr); + addr = dma_alloc_coherent(&adapter->pdev->dev, size, + &md_template_addr, GFP_KERNEL); if (!addr) { dev_err(&adapter->pdev->dev, "Unable to allocate dmable memory for template.\n"); return -ENOMEM; @@ -133,7 +134,7 @@ netxen_get_minidump_template(struct netxen_adapter *adapter) dev_err(&adapter->pdev->dev, "Failed to get minidump template, err_code : %d, requested_size : %d, actual_size : %d\n", cmd.rsp.cmd, size, cmd.rsp.arg2); } - pci_free_consistent(adapter->pdev, size, addr, md_template_addr); + dma_free_coherent(&adapter->pdev->dev, size, addr, md_template_addr); return 0; } @@ -281,14 +282,14 @@ nx_fw_cmd_create_rx_ctx(struct netxen_adapter *adapter) rsp_size = SIZEOF_CARDRSP_RX(nx_cardrsp_rx_ctx_t, nrds_rings, nsds_rings); - addr = pci_alloc_consistent(adapter->pdev, - rq_size, &hostrq_phys_addr); + addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size, + &hostrq_phys_addr, GFP_KERNEL); if (addr == NULL) return -ENOMEM; prq = addr; - addr = pci_alloc_consistent(adapter->pdev, - rsp_size, &cardrsp_phys_addr); + addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size, + &cardrsp_phys_addr, GFP_KERNEL); if (addr == NULL) { err = -ENOMEM; goto out_free_rq; @@ -387,9 +388,10 @@ nx_fw_cmd_create_rx_ctx(struct netxen_adapter *adapter) recv_ctx->virt_port = prsp->virt_port; out_free_rsp: - pci_free_consistent(adapter->pdev, rsp_size, prsp, cardrsp_phys_addr); + dma_free_coherent(&adapter->pdev->dev, rsp_size, prsp, + cardrsp_phys_addr); out_free_rq: - pci_free_consistent(adapter->pdev, rq_size, prq, hostrq_phys_addr); + dma_free_coherent(&adapter->pdev->dev, rq_size, prq, hostrq_phys_addr); return err; } @@ -429,14 +431,14 @@ nx_fw_cmd_create_tx_ctx(struct netxen_adapter *adapter) struct netxen_cmd_args cmd; rq_size = SIZEOF_HOSTRQ_TX(nx_hostrq_tx_ctx_t); - rq_addr = pci_alloc_consistent(adapter->pdev, - rq_size, &rq_phys_addr); + rq_addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size, + &rq_phys_addr, GFP_KERNEL); if (!rq_addr) return -ENOMEM; rsp_size = SIZEOF_CARDRSP_TX(nx_cardrsp_tx_ctx_t); - rsp_addr = pci_alloc_consistent(adapter->pdev, - rsp_size, &rsp_phys_addr); + rsp_addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size, + &rsp_phys_addr, GFP_KERNEL); if (!rsp_addr) { err = -ENOMEM; goto out_free_rq; @@ -491,10 +493,11 @@ nx_fw_cmd_create_tx_ctx(struct netxen_adapter *adapter) err = -EIO; } - pci_free_consistent(adapter->pdev, rsp_size, rsp_addr, rsp_phys_addr); + dma_free_coherent(&adapter->pdev->dev, rsp_size, rsp_addr, + rsp_phys_addr); out_free_rq: - pci_free_consistent(adapter->pdev, rq_size, rq_addr, rq_phys_addr); + dma_free_coherent(&adapter->pdev->dev, rq_size, rq_addr, rq_phys_addr); return err; } @@ -745,9 +748,9 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter) recv_ctx = &adapter->recv_ctx; tx_ring = adapter->tx_ring; - addr = pci_alloc_consistent(pdev, - sizeof(struct netxen_ring_ctx) + sizeof(uint32_t), - &recv_ctx->phys_addr); + addr = dma_alloc_coherent(&pdev->dev, + sizeof(struct netxen_ring_ctx) + sizeof(uint32_t), + &recv_ctx->phys_addr, GFP_KERNEL); if (addr == NULL) { dev_err(&pdev->dev, "failed to allocate hw context\n"); return -ENOMEM; @@ -762,8 +765,8 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter) (__le32 *)(((char *)addr) + sizeof(struct netxen_ring_ctx)); /* cmd desc ring */ - addr = pci_alloc_consistent(pdev, TX_DESC_RINGSIZE(tx_ring), - &tx_ring->phys_addr); + addr = dma_alloc_coherent(&pdev->dev, TX_DESC_RINGSIZE(tx_ring), + &tx_ring->phys_addr, GFP_KERNEL); if (addr == NULL) { dev_err(&pdev->dev, "%s: failed to allocate tx desc ring\n", @@ -776,9 +779,9 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter) for (ring = 0; ring < adapter->max_rds_rings; ring++) { rds_ring = &recv_ctx->rds_rings[ring]; - addr = pci_alloc_consistent(adapter->pdev, - RCV_DESC_RINGSIZE(rds_ring), - &rds_ring->phys_addr); + addr = dma_alloc_coherent(&adapter->pdev->dev, + RCV_DESC_RINGSIZE(rds_ring), + &rds_ring->phys_addr, GFP_KERNEL); if (addr == NULL) { dev_err(&pdev->dev, "%s: failed to allocate rds ring [%d]\n", @@ -797,9 +800,9 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter) for (ring = 0; ring < adapter->max_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; - addr = pci_alloc_consistent(adapter->pdev, - STATUS_DESC_RINGSIZE(sds_ring), - &sds_ring->phys_addr); + addr = dma_alloc_coherent(&adapter->pdev->dev, + STATUS_DESC_RINGSIZE(sds_ring), + &sds_ring->phys_addr, GFP_KERNEL); if (addr == NULL) { dev_err(&pdev->dev, "%s: failed to allocate sds ring [%d]\n", @@ -874,19 +877,17 @@ done: recv_ctx = &adapter->recv_ctx; if (recv_ctx->hwctx != NULL) { - pci_free_consistent(adapter->pdev, - sizeof(struct netxen_ring_ctx) + - sizeof(uint32_t), - recv_ctx->hwctx, - recv_ctx->phys_addr); + dma_free_coherent(&adapter->pdev->dev, + sizeof(struct netxen_ring_ctx) + sizeof(uint32_t), + recv_ctx->hwctx, recv_ctx->phys_addr); recv_ctx->hwctx = NULL; } tx_ring = adapter->tx_ring; if (tx_ring->desc_head != NULL) { - pci_free_consistent(adapter->pdev, - TX_DESC_RINGSIZE(tx_ring), - tx_ring->desc_head, tx_ring->phys_addr); + dma_free_coherent(&adapter->pdev->dev, + TX_DESC_RINGSIZE(tx_ring), + tx_ring->desc_head, tx_ring->phys_addr); tx_ring->desc_head = NULL; } @@ -894,10 +895,10 @@ done: rds_ring = &recv_ctx->rds_rings[ring]; if (rds_ring->desc_head != NULL) { - pci_free_consistent(adapter->pdev, - RCV_DESC_RINGSIZE(rds_ring), - rds_ring->desc_head, - rds_ring->phys_addr); + dma_free_coherent(&adapter->pdev->dev, + RCV_DESC_RINGSIZE(rds_ring), + rds_ring->desc_head, + rds_ring->phys_addr); rds_ring->desc_head = NULL; } } @@ -906,10 +907,10 @@ done: sds_ring = &recv_ctx->sds_rings[ring]; if (sds_ring->desc_head != NULL) { - pci_free_consistent(adapter->pdev, - STATUS_DESC_RINGSIZE(sds_ring), - sds_ring->desc_head, - sds_ring->phys_addr); + dma_free_coherent(&adapter->pdev->dev, + STATUS_DESC_RINGSIZE(sds_ring), + sds_ring->desc_head, + sds_ring->phys_addr); sds_ring->desc_head = NULL; } } diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c index 94546ed5f867..08f9477d2ee8 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c @@ -102,10 +102,8 @@ void netxen_release_rx_buffers(struct netxen_adapter *adapter) rx_buf = &(rds_ring->rx_buf_arr[i]); if (rx_buf->state == NETXEN_BUFFER_FREE) continue; - pci_unmap_single(adapter->pdev, - rx_buf->dma, - rds_ring->dma_size, - PCI_DMA_FROMDEVICE); + dma_unmap_single(&adapter->pdev->dev, rx_buf->dma, + rds_ring->dma_size, DMA_FROM_DEVICE); if (rx_buf->skb != NULL) dev_kfree_skb_any(rx_buf->skb); } @@ -124,16 +122,16 @@ void netxen_release_tx_buffers(struct netxen_adapter *adapter) for (i = 0; i < tx_ring->num_desc; i++) { buffrag = cmd_buf->frag_array; if (buffrag->dma) { - pci_unmap_single(adapter->pdev, buffrag->dma, - buffrag->length, PCI_DMA_TODEVICE); + dma_unmap_single(&adapter->pdev->dev, buffrag->dma, + buffrag->length, DMA_TO_DEVICE); buffrag->dma = 0ULL; } for (j = 1; j < cmd_buf->frag_count; j++) { buffrag++; if (buffrag->dma) { - pci_unmap_page(adapter->pdev, buffrag->dma, - buffrag->length, - PCI_DMA_TODEVICE); + dma_unmap_page(&adapter->pdev->dev, + buffrag->dma, buffrag->length, + DMA_TO_DEVICE); buffrag->dma = 0ULL; } } @@ -1250,9 +1248,10 @@ int netxen_init_dummy_dma(struct netxen_adapter *adapter) if (!NX_IS_REVISION_P2(adapter->ahw.revision_id)) return 0; - adapter->dummy_dma.addr = pci_alloc_consistent(adapter->pdev, - NETXEN_HOST_DUMMY_DMA_SIZE, - &adapter->dummy_dma.phys_addr); + adapter->dummy_dma.addr = dma_alloc_coherent(&adapter->pdev->dev, + NETXEN_HOST_DUMMY_DMA_SIZE, + &adapter->dummy_dma.phys_addr, + GFP_KERNEL); if (adapter->dummy_dma.addr == NULL) { dev_err(&adapter->pdev->dev, "ERROR: Could not allocate dummy DMA memory\n"); @@ -1304,10 +1303,10 @@ void netxen_free_dummy_dma(struct netxen_adapter *adapter) } if (i) { - pci_free_consistent(adapter->pdev, - NETXEN_HOST_DUMMY_DMA_SIZE, - adapter->dummy_dma.addr, - adapter->dummy_dma.phys_addr); + dma_free_coherent(&adapter->pdev->dev, + NETXEN_HOST_DUMMY_DMA_SIZE, + adapter->dummy_dma.addr, + adapter->dummy_dma.phys_addr); adapter->dummy_dma.addr = NULL; } else dev_err(&adapter->pdev->dev, "dma_watchdog_shutdown failed\n"); @@ -1467,10 +1466,10 @@ netxen_alloc_rx_skb(struct netxen_adapter *adapter, if (!adapter->ahw.cut_through) skb_reserve(skb, 2); - dma = pci_map_single(pdev, skb->data, - rds_ring->dma_size, PCI_DMA_FROMDEVICE); + dma = dma_map_single(&pdev->dev, skb->data, rds_ring->dma_size, + DMA_FROM_DEVICE); - if (pci_dma_mapping_error(pdev, dma)) { + if (dma_mapping_error(&pdev->dev, dma)) { dev_kfree_skb_any(skb); buffer->skb = NULL; return 1; @@ -1491,8 +1490,8 @@ static struct sk_buff *netxen_process_rxbuf(struct netxen_adapter *adapter, buffer = &rds_ring->rx_buf_arr[index]; - pci_unmap_single(adapter->pdev, buffer->dma, rds_ring->dma_size, - PCI_DMA_FROMDEVICE); + dma_unmap_single(&adapter->pdev->dev, buffer->dma, rds_ring->dma_size, + DMA_FROM_DEVICE); skb = buffer->skb; if (!skb) @@ -1754,13 +1753,13 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter) buffer = &tx_ring->cmd_buf_arr[sw_consumer]; if (buffer->skb) { frag = &buffer->frag_array[0]; - pci_unmap_single(pdev, frag->dma, frag->length, - PCI_DMA_TODEVICE); + dma_unmap_single(&pdev->dev, frag->dma, frag->length, + DMA_TO_DEVICE); frag->dma = 0ULL; for (i = 1; i < buffer->frag_count; i++) { frag++; /* Get the next frag */ - pci_unmap_page(pdev, frag->dma, frag->length, - PCI_DMA_TODEVICE); + dma_unmap_page(&pdev->dev, frag->dma, + frag->length, DMA_TO_DEVICE); frag->dma = 0ULL; } diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index d258e0ccf946..7e6bac85495d 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -243,8 +243,8 @@ static int nx_set_dma_mask(struct netxen_adapter *adapter) cmask = mask; } - if (pci_set_dma_mask(pdev, mask) == 0 && - pci_set_consistent_dma_mask(pdev, cmask) == 0) { + if (dma_set_mask(&pdev->dev, mask) == 0 && + dma_set_coherent_mask(&pdev->dev, cmask) == 0) { adapter->pci_using_dac = 1; return 0; } @@ -277,13 +277,13 @@ nx_update_dma_mask(struct netxen_adapter *adapter) mask = DMA_BIT_MASK(32+shift); - err = pci_set_dma_mask(pdev, mask); + err = dma_set_mask(&pdev->dev, mask); if (err) goto err_out; if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { - err = pci_set_consistent_dma_mask(pdev, mask); + err = dma_set_coherent_mask(&pdev->dev, mask); if (err) goto err_out; } @@ -293,8 +293,8 @@ nx_update_dma_mask(struct netxen_adapter *adapter) return 0; err_out: - pci_set_dma_mask(pdev, old_mask); - pci_set_consistent_dma_mask(pdev, old_cmask); + dma_set_mask(&pdev->dev, old_mask); + dma_set_coherent_mask(&pdev->dev, old_cmask); return err; } @@ -1978,9 +1978,9 @@ netxen_map_tx_skb(struct pci_dev *pdev, nr_frags = skb_shinfo(skb)->nr_frags; nf = &pbuf->frag_array[0]; - map = pci_map_single(pdev, skb->data, - skb_headlen(skb), PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(pdev, map)) + map = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, map)) goto out_err; nf->dma = map; @@ -2004,12 +2004,12 @@ netxen_map_tx_skb(struct pci_dev *pdev, unwind: while (--i >= 0) { nf = &pbuf->frag_array[i+1]; - pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); + dma_unmap_page(&pdev->dev, nf->dma, nf->length, DMA_TO_DEVICE); nf->dma = 0ULL; } nf = &pbuf->frag_array[0]; - pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); + dma_unmap_single(&pdev->dev, nf->dma, skb_headlen(skb), DMA_TO_DEVICE); nf->dma = 0ULL; out_err: diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index ca0ee29a57b5..70c8d3cd85c0 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -1090,12 +1090,9 @@ static bool qede_rx_xdp(struct qede_dev *edev, struct xdp_buff xdp; enum xdp_action act; - xdp.data_hard_start = page_address(bd->data); - xdp.data = xdp.data_hard_start + *data_offset; - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = xdp.data + *len; - xdp.rxq = &rxq->xdp_rxq; - xdp.frame_sz = rxq->rx_buf_seg_size; /* PAGE_SIZE when XDP enabled */ + xdp_init_buff(&xdp, rxq->rx_buf_seg_size, &rxq->xdp_rxq); + xdp_prepare_buff(&xdp, page_address(bd->data), *data_offset, + *len, false); /* Queues always have a full reset currently, so for the time * being until there's atomic program replace just mark read diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 9cf960a6d007..4bf94797aac5 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -663,8 +663,6 @@ static const struct net_device_ops qede_netdev_ops = { .ndo_get_vf_config = qede_get_vf_config, .ndo_set_vf_rate = qede_set_vf_rate, #endif - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = qede_features_check, .ndo_bpf = qede_xdp, #ifdef CONFIG_RFS_ACCEL @@ -688,8 +686,6 @@ static const struct net_device_ops qede_netdev_vf_ops = { .ndo_fix_features = qede_fix_features, .ndo_set_features = qede_set_features, .ndo_get_stats64 = qede_get_stats64, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = qede_features_check, }; @@ -707,8 +703,6 @@ static const struct net_device_ops qede_netdev_vf_xdp_ops = { .ndo_fix_features = qede_fix_features, .ndo_set_features = qede_set_features, .ndo_get_stats64 = qede_get_stats64, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = qede_features_check, .ndo_bpf = qede_xdp, .ndo_xdp_xmit = qede_xdp_transmit, diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 27740c027681..214e347097a7 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -315,12 +315,11 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev, * buffer */ skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE); - map = pci_map_single(qdev->pdev, + map = dma_map_single(&qdev->pdev->dev, lrg_buf_cb->skb->data, - qdev->lrg_buffer_len - - QL_HEADER_SPACE, - PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(qdev->pdev, map); + qdev->lrg_buffer_len - QL_HEADER_SPACE, + DMA_FROM_DEVICE); + err = dma_mapping_error(&qdev->pdev->dev, map); if (err) { netdev_err(qdev->ndev, "PCI mapping failed with error: %d\n", @@ -1802,13 +1801,12 @@ static int ql_populate_free_queue(struct ql3_adapter *qdev) * first buffer */ skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE); - map = pci_map_single(qdev->pdev, + map = dma_map_single(&qdev->pdev->dev, lrg_buf_cb->skb->data, - qdev->lrg_buffer_len - - QL_HEADER_SPACE, - PCI_DMA_FROMDEVICE); + qdev->lrg_buffer_len - QL_HEADER_SPACE, + DMA_FROM_DEVICE); - err = pci_dma_mapping_error(qdev->pdev, map); + err = dma_mapping_error(&qdev->pdev->dev, map); if (err) { netdev_err(qdev->ndev, "PCI mapping failed with error: %d\n", @@ -1943,18 +1941,16 @@ static void ql_process_mac_tx_intr(struct ql3_adapter *qdev, goto invalid_seg_count; } - pci_unmap_single(qdev->pdev, + dma_unmap_single(&qdev->pdev->dev, dma_unmap_addr(&tx_cb->map[0], mapaddr), - dma_unmap_len(&tx_cb->map[0], maplen), - PCI_DMA_TODEVICE); + dma_unmap_len(&tx_cb->map[0], maplen), DMA_TO_DEVICE); tx_cb->seg_count--; if (tx_cb->seg_count) { for (i = 1; i < tx_cb->seg_count; i++) { - pci_unmap_page(qdev->pdev, - dma_unmap_addr(&tx_cb->map[i], - mapaddr), + dma_unmap_page(&qdev->pdev->dev, + dma_unmap_addr(&tx_cb->map[i], mapaddr), dma_unmap_len(&tx_cb->map[i], maplen), - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); } } qdev->ndev->stats.tx_packets++; @@ -2021,10 +2017,9 @@ static void ql_process_mac_rx_intr(struct ql3_adapter *qdev, qdev->ndev->stats.rx_bytes += length; skb_put(skb, length); - pci_unmap_single(qdev->pdev, + dma_unmap_single(&qdev->pdev->dev, dma_unmap_addr(lrg_buf_cb2, mapaddr), - dma_unmap_len(lrg_buf_cb2, maplen), - PCI_DMA_FROMDEVICE); + dma_unmap_len(lrg_buf_cb2, maplen), DMA_FROM_DEVICE); prefetch(skb->data); skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, qdev->ndev); @@ -2067,10 +2062,9 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev, skb2 = lrg_buf_cb2->skb; skb_put(skb2, length); /* Just the second buffer length here. */ - pci_unmap_single(qdev->pdev, + dma_unmap_single(&qdev->pdev->dev, dma_unmap_addr(lrg_buf_cb2, mapaddr), - dma_unmap_len(lrg_buf_cb2, maplen), - PCI_DMA_FROMDEVICE); + dma_unmap_len(lrg_buf_cb2, maplen), DMA_FROM_DEVICE); prefetch(skb2->data); skb_checksum_none_assert(skb2); @@ -2319,9 +2313,9 @@ static int ql_send_map(struct ql3_adapter *qdev, /* * Map the skb buffer first. */ - map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE); + map = dma_map_single(&qdev->pdev->dev, skb->data, len, DMA_TO_DEVICE); - err = pci_dma_mapping_error(qdev->pdev, map); + err = dma_mapping_error(&qdev->pdev->dev, map); if (err) { netdev_err(qdev->ndev, "PCI mapping failed with error: %d\n", err); @@ -2357,11 +2351,11 @@ static int ql_send_map(struct ql3_adapter *qdev, (seg == 7 && seg_cnt > 8) || (seg == 12 && seg_cnt > 13) || (seg == 17 && seg_cnt > 18)) { - map = pci_map_single(qdev->pdev, oal, + map = dma_map_single(&qdev->pdev->dev, oal, sizeof(struct oal), - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); - err = pci_dma_mapping_error(qdev->pdev, map); + err = dma_mapping_error(&qdev->pdev->dev, map); if (err) { netdev_err(qdev->ndev, "PCI mapping outbound address list with error: %d\n", @@ -2423,24 +2417,24 @@ map_error: (seg == 7 && seg_cnt > 8) || (seg == 12 && seg_cnt > 13) || (seg == 17 && seg_cnt > 18)) { - pci_unmap_single(qdev->pdev, - dma_unmap_addr(&tx_cb->map[seg], mapaddr), - dma_unmap_len(&tx_cb->map[seg], maplen), - PCI_DMA_TODEVICE); + dma_unmap_single(&qdev->pdev->dev, + dma_unmap_addr(&tx_cb->map[seg], mapaddr), + dma_unmap_len(&tx_cb->map[seg], maplen), + DMA_TO_DEVICE); oal++; seg++; } - pci_unmap_page(qdev->pdev, + dma_unmap_page(&qdev->pdev->dev, dma_unmap_addr(&tx_cb->map[seg], mapaddr), dma_unmap_len(&tx_cb->map[seg], maplen), - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); } - pci_unmap_single(qdev->pdev, + dma_unmap_single(&qdev->pdev->dev, dma_unmap_addr(&tx_cb->map[0], mapaddr), dma_unmap_addr(&tx_cb->map[0], maplen), - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); return NETDEV_TX_BUSY; @@ -2525,9 +2519,8 @@ static int ql_alloc_net_req_rsp_queues(struct ql3_adapter *qdev) wmb(); qdev->req_q_virt_addr = - pci_alloc_consistent(qdev->pdev, - (size_t) qdev->req_q_size, - &qdev->req_q_phy_addr); + dma_alloc_coherent(&qdev->pdev->dev, (size_t)qdev->req_q_size, + &qdev->req_q_phy_addr, GFP_KERNEL); if ((qdev->req_q_virt_addr == NULL) || LS_64BITS(qdev->req_q_phy_addr) & (qdev->req_q_size - 1)) { @@ -2536,16 +2529,14 @@ static int ql_alloc_net_req_rsp_queues(struct ql3_adapter *qdev) } qdev->rsp_q_virt_addr = - pci_alloc_consistent(qdev->pdev, - (size_t) qdev->rsp_q_size, - &qdev->rsp_q_phy_addr); + dma_alloc_coherent(&qdev->pdev->dev, (size_t)qdev->rsp_q_size, + &qdev->rsp_q_phy_addr, GFP_KERNEL); if ((qdev->rsp_q_virt_addr == NULL) || LS_64BITS(qdev->rsp_q_phy_addr) & (qdev->rsp_q_size - 1)) { netdev_err(qdev->ndev, "rspQ allocation failed\n"); - pci_free_consistent(qdev->pdev, (size_t) qdev->req_q_size, - qdev->req_q_virt_addr, - qdev->req_q_phy_addr); + dma_free_coherent(&qdev->pdev->dev, (size_t)qdev->req_q_size, + qdev->req_q_virt_addr, qdev->req_q_phy_addr); return -ENOMEM; } @@ -2561,15 +2552,13 @@ static void ql_free_net_req_rsp_queues(struct ql3_adapter *qdev) return; } - pci_free_consistent(qdev->pdev, - qdev->req_q_size, - qdev->req_q_virt_addr, qdev->req_q_phy_addr); + dma_free_coherent(&qdev->pdev->dev, qdev->req_q_size, + qdev->req_q_virt_addr, qdev->req_q_phy_addr); qdev->req_q_virt_addr = NULL; - pci_free_consistent(qdev->pdev, - qdev->rsp_q_size, - qdev->rsp_q_virt_addr, qdev->rsp_q_phy_addr); + dma_free_coherent(&qdev->pdev->dev, qdev->rsp_q_size, + qdev->rsp_q_virt_addr, qdev->rsp_q_phy_addr); qdev->rsp_q_virt_addr = NULL; @@ -2593,9 +2582,9 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev) return -ENOMEM; qdev->lrg_buf_q_alloc_virt_addr = - pci_alloc_consistent(qdev->pdev, - qdev->lrg_buf_q_alloc_size, - &qdev->lrg_buf_q_alloc_phy_addr); + dma_alloc_coherent(&qdev->pdev->dev, + qdev->lrg_buf_q_alloc_size, + &qdev->lrg_buf_q_alloc_phy_addr, GFP_KERNEL); if (qdev->lrg_buf_q_alloc_virt_addr == NULL) { netdev_err(qdev->ndev, "lBufQ failed\n"); @@ -2613,15 +2602,16 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev) qdev->small_buf_q_alloc_size = qdev->small_buf_q_size * 2; qdev->small_buf_q_alloc_virt_addr = - pci_alloc_consistent(qdev->pdev, - qdev->small_buf_q_alloc_size, - &qdev->small_buf_q_alloc_phy_addr); + dma_alloc_coherent(&qdev->pdev->dev, + qdev->small_buf_q_alloc_size, + &qdev->small_buf_q_alloc_phy_addr, GFP_KERNEL); if (qdev->small_buf_q_alloc_virt_addr == NULL) { netdev_err(qdev->ndev, "Small Buffer Queue allocation failed\n"); - pci_free_consistent(qdev->pdev, qdev->lrg_buf_q_alloc_size, - qdev->lrg_buf_q_alloc_virt_addr, - qdev->lrg_buf_q_alloc_phy_addr); + dma_free_coherent(&qdev->pdev->dev, + qdev->lrg_buf_q_alloc_size, + qdev->lrg_buf_q_alloc_virt_addr, + qdev->lrg_buf_q_alloc_phy_addr); return -ENOMEM; } @@ -2638,17 +2628,15 @@ static void ql_free_buffer_queues(struct ql3_adapter *qdev) return; } kfree(qdev->lrg_buf); - pci_free_consistent(qdev->pdev, - qdev->lrg_buf_q_alloc_size, - qdev->lrg_buf_q_alloc_virt_addr, - qdev->lrg_buf_q_alloc_phy_addr); + dma_free_coherent(&qdev->pdev->dev, qdev->lrg_buf_q_alloc_size, + qdev->lrg_buf_q_alloc_virt_addr, + qdev->lrg_buf_q_alloc_phy_addr); qdev->lrg_buf_q_virt_addr = NULL; - pci_free_consistent(qdev->pdev, - qdev->small_buf_q_alloc_size, - qdev->small_buf_q_alloc_virt_addr, - qdev->small_buf_q_alloc_phy_addr); + dma_free_coherent(&qdev->pdev->dev, qdev->small_buf_q_alloc_size, + qdev->small_buf_q_alloc_virt_addr, + qdev->small_buf_q_alloc_phy_addr); qdev->small_buf_q_virt_addr = NULL; @@ -2666,9 +2654,9 @@ static int ql_alloc_small_buffers(struct ql3_adapter *qdev) QL_SMALL_BUFFER_SIZE); qdev->small_buf_virt_addr = - pci_alloc_consistent(qdev->pdev, - qdev->small_buf_total_size, - &qdev->small_buf_phy_addr); + dma_alloc_coherent(&qdev->pdev->dev, + qdev->small_buf_total_size, + &qdev->small_buf_phy_addr, GFP_KERNEL); if (qdev->small_buf_virt_addr == NULL) { netdev_err(qdev->ndev, "Failed to get small buffer memory\n"); @@ -2701,10 +2689,10 @@ static void ql_free_small_buffers(struct ql3_adapter *qdev) return; } if (qdev->small_buf_virt_addr != NULL) { - pci_free_consistent(qdev->pdev, - qdev->small_buf_total_size, - qdev->small_buf_virt_addr, - qdev->small_buf_phy_addr); + dma_free_coherent(&qdev->pdev->dev, + qdev->small_buf_total_size, + qdev->small_buf_virt_addr, + qdev->small_buf_phy_addr); qdev->small_buf_virt_addr = NULL; } @@ -2719,10 +2707,10 @@ static void ql_free_large_buffers(struct ql3_adapter *qdev) lrg_buf_cb = &qdev->lrg_buf[i]; if (lrg_buf_cb->skb) { dev_kfree_skb(lrg_buf_cb->skb); - pci_unmap_single(qdev->pdev, + dma_unmap_single(&qdev->pdev->dev, dma_unmap_addr(lrg_buf_cb, mapaddr), dma_unmap_len(lrg_buf_cb, maplen), - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb)); } else { break; @@ -2774,13 +2762,11 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) * buffer */ skb_reserve(skb, QL_HEADER_SPACE); - map = pci_map_single(qdev->pdev, - skb->data, - qdev->lrg_buffer_len - - QL_HEADER_SPACE, - PCI_DMA_FROMDEVICE); + map = dma_map_single(&qdev->pdev->dev, skb->data, + qdev->lrg_buffer_len - QL_HEADER_SPACE, + DMA_FROM_DEVICE); - err = pci_dma_mapping_error(qdev->pdev, map); + err = dma_mapping_error(&qdev->pdev->dev, map); if (err) { netdev_err(qdev->ndev, "PCI mapping failed with error: %d\n", @@ -2865,8 +2851,8 @@ static int ql_alloc_mem_resources(struct ql3_adapter *qdev) * Network Completion Queue Producer Index Register */ qdev->shadow_reg_virt_addr = - pci_alloc_consistent(qdev->pdev, - PAGE_SIZE, &qdev->shadow_reg_phy_addr); + dma_alloc_coherent(&qdev->pdev->dev, PAGE_SIZE, + &qdev->shadow_reg_phy_addr, GFP_KERNEL); if (qdev->shadow_reg_virt_addr != NULL) { qdev->preq_consumer_index = qdev->shadow_reg_virt_addr; @@ -2921,10 +2907,9 @@ err_small_buffers: err_buffer_queues: ql_free_net_req_rsp_queues(qdev); err_req_rsp: - pci_free_consistent(qdev->pdev, - PAGE_SIZE, - qdev->shadow_reg_virt_addr, - qdev->shadow_reg_phy_addr); + dma_free_coherent(&qdev->pdev->dev, PAGE_SIZE, + qdev->shadow_reg_virt_addr, + qdev->shadow_reg_phy_addr); return -ENOMEM; } @@ -2937,10 +2922,9 @@ static void ql_free_mem_resources(struct ql3_adapter *qdev) ql_free_buffer_queues(qdev); ql_free_net_req_rsp_queues(qdev); if (qdev->shadow_reg_virt_addr != NULL) { - pci_free_consistent(qdev->pdev, - PAGE_SIZE, - qdev->shadow_reg_virt_addr, - qdev->shadow_reg_phy_addr); + dma_free_coherent(&qdev->pdev->dev, PAGE_SIZE, + qdev->shadow_reg_virt_addr, + qdev->shadow_reg_phy_addr); qdev->shadow_reg_virt_addr = NULL; } } @@ -3641,18 +3625,15 @@ static void ql_reset_work(struct work_struct *work) if (tx_cb->skb) { netdev_printk(KERN_DEBUG, ndev, "Freeing lost SKB\n"); - pci_unmap_single(qdev->pdev, - dma_unmap_addr(&tx_cb->map[0], - mapaddr), - dma_unmap_len(&tx_cb->map[0], maplen), - PCI_DMA_TODEVICE); + dma_unmap_single(&qdev->pdev->dev, + dma_unmap_addr(&tx_cb->map[0], mapaddr), + dma_unmap_len(&tx_cb->map[0], maplen), + DMA_TO_DEVICE); for (j = 1; j < tx_cb->seg_count; j++) { - pci_unmap_page(qdev->pdev, - dma_unmap_addr(&tx_cb->map[j], - mapaddr), - dma_unmap_len(&tx_cb->map[j], - maplen), - PCI_DMA_TODEVICE); + dma_unmap_page(&qdev->pdev->dev, + dma_unmap_addr(&tx_cb->map[j], mapaddr), + dma_unmap_len(&tx_cb->map[j], maplen), + DMA_TO_DEVICE); } dev_kfree_skb(tx_cb->skb); tx_cb->skb = NULL; @@ -3784,13 +3765,10 @@ static int ql3xxx_probe(struct pci_dev *pdev, pci_set_master(pdev); - if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) pci_using_dac = 1; - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); - } else if (!(err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) { + else if (!(err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))) pci_using_dac = 0; - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - } if (err) { pr_err("%s no usable DMA configuration\n", pci_name(pdev)); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index c2faf96fcade..96b947fde646 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -520,8 +520,6 @@ static const struct net_device_ops qlcnic_netdev_ops = { .ndo_fdb_del = qlcnic_fdb_del, .ndo_fdb_dump = qlcnic_fdb_dump, .ndo_get_phys_port_id = qlcnic_get_phys_port_id, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_features_check = qlcnic_features_check, #ifdef CONFIG_QLCNIC_SRIOV .ndo_set_vf_mac = qlcnic_sriov_set_vf_mac, diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index a569abe7f5ef..475e6f01ea10 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -28,6 +28,7 @@ #include <linux/bitfield.h> #include <linux/prefetch.h> #include <linux/ipv6.h> +#include <asm/unaligned.h> #include <net/ip6_checksum.h> #include "r8169.h" @@ -260,6 +261,9 @@ enum rtl8168_8101_registers { #define CSIAR_BYTE_ENABLE 0x0000f000 #define CSIAR_ADDR_MASK 0x00000fff PMCH = 0x6f, +#define D3COLD_NO_PLL_DOWN BIT(7) +#define D3HOT_NO_PLL_DOWN BIT(6) +#define D3_NO_PLL_DOWN (BIT(7) | BIT(6)) EPHYAR = 0x80, #define EPHYAR_FLAG 0x80000000 #define EPHYAR_WRITE_CMD 0x80000000 @@ -529,6 +533,9 @@ enum rtl_rx_desc_bit { IPFail = (1 << 16), /* IP checksum failed */ UDPFail = (1 << 15), /* UDP/IP checksum failed */ TCPFail = (1 << 14), /* TCP/IP checksum failed */ + +#define RxCSFailMask (IPFail | UDPFail | TCPFail) + RxVlanTag = (1 << 16), /* VLAN tag available */ }; @@ -584,6 +591,12 @@ enum rtl_flag { RTL_FLAG_MAX }; +enum rtl_dash_type { + RTL_DASH_NONE, + RTL_DASH_DP, + RTL_DASH_EP, +}; + struct rtl8169_private { void __iomem *mmio_addr; /* memory map physical address */ struct pci_dev *pci_dev; @@ -591,6 +604,7 @@ struct rtl8169_private { struct phy_device *phydev; struct napi_struct napi; enum mac_version mac_version; + enum rtl_dash_type dash_type; u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ u32 dirty_tx; @@ -746,14 +760,75 @@ static const struct rtl_cond name = { \ \ static bool name ## _check(struct rtl8169_private *tp) -static bool rtl_ocp_reg_failure(struct rtl8169_private *tp, u32 reg) +static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type) { - if (reg & 0xffff0001) { - if (net_ratelimit()) - netdev_err(tp->dev, "Invalid ocp reg %x!\n", reg); - return true; - } - return false; + /* based on RTL8168FP_OOBMAC_BASE in vendor driver */ + if (tp->mac_version == RTL_GIGA_MAC_VER_52 && type == ERIAR_OOB) + *cmd |= 0x7f0 << 18; +} + +DECLARE_RTL_COND(rtl_eriar_cond) +{ + return RTL_R32(tp, ERIAR) & ERIAR_FLAG; +} + +static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, + u32 val, int type) +{ + u32 cmd = ERIAR_WRITE_CMD | type | mask | addr; + + if (WARN(addr & 3 || !mask, "addr: 0x%x, mask: 0x%08x\n", addr, mask)) + return; + + RTL_W32(tp, ERIDR, val); + r8168fp_adjust_ocp_cmd(tp, &cmd, type); + RTL_W32(tp, ERIAR, cmd); + + rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100); +} + +static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, + u32 val) +{ + _rtl_eri_write(tp, addr, mask, val, ERIAR_EXGMAC); +} + +static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type) +{ + u32 cmd = ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr; + + r8168fp_adjust_ocp_cmd(tp, &cmd, type); + RTL_W32(tp, ERIAR, cmd); + + return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ? + RTL_R32(tp, ERIDR) : ~0; +} + +static u32 rtl_eri_read(struct rtl8169_private *tp, int addr) +{ + return _rtl_eri_read(tp, addr, ERIAR_EXGMAC); +} + +static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 p, u32 m) +{ + u32 val = rtl_eri_read(tp, addr); + + rtl_eri_write(tp, addr, ERIAR_MASK_1111, (val & ~m) | p); +} + +static void rtl_eri_set_bits(struct rtl8169_private *tp, int addr, u32 p) +{ + rtl_w0w1_eri(tp, addr, p, 0); +} + +static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 m) +{ + rtl_w0w1_eri(tp, addr, 0, m); +} + +static bool rtl_ocp_reg_failure(u32 reg) +{ + return WARN_ONCE(reg & 0xffff0001, "Invalid ocp reg %x!\n", reg); } DECLARE_RTL_COND(rtl_ocp_gphy_cond) @@ -763,7 +838,7 @@ DECLARE_RTL_COND(rtl_ocp_gphy_cond) static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) { - if (rtl_ocp_reg_failure(tp, reg)) + if (rtl_ocp_reg_failure(reg)) return; RTL_W32(tp, GPHY_OCP, OCPAR_FLAG | (reg << 15) | data); @@ -773,7 +848,7 @@ static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg) { - if (rtl_ocp_reg_failure(tp, reg)) + if (rtl_ocp_reg_failure(reg)) return 0; RTL_W32(tp, GPHY_OCP, reg << 15); @@ -784,7 +859,7 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg) static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) { - if (rtl_ocp_reg_failure(tp, reg)) + if (rtl_ocp_reg_failure(reg)) return; RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data); @@ -792,7 +867,7 @@ static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) { - if (rtl_ocp_reg_failure(tp, reg)) + if (rtl_ocp_reg_failure(reg)) return 0; RTL_W32(tp, OCPDR, reg << 15); @@ -808,6 +883,25 @@ static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask, r8168_mac_ocp_write(tp, reg, (data & ~mask) | set); } +/* Work around a hw issue with RTL8168g PHY, the quirk disables + * PHY MCU interrupts before PHY power-down. + */ +static void rtl8168g_phy_suspend_quirk(struct rtl8169_private *tp, int value) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_40: + case RTL_GIGA_MAC_VER_41: + case RTL_GIGA_MAC_VER_49: + if (value & BMCR_RESET || !(value & BMCR_PDOWN)) + rtl_eri_set_bits(tp, 0x1a8, 0xfc000000); + else + rtl_eri_clear_bits(tp, 0x1a8, 0xfc000000); + break; + default: + break; + } +}; + static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value) { if (reg == 0x1f) { @@ -818,6 +912,9 @@ static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value) if (tp->ocp_base != OCP_STD_PHY_BASE) reg -= 0x10; + if (tp->ocp_base == OCP_STD_PHY_BASE && reg == MII_BMCR) + rtl8168g_phy_suspend_quirk(tp, value); + r8168_phy_ocp_write(tp, tp->ocp_base + reg * 2, value); } @@ -1009,70 +1106,6 @@ static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr) RTL_R32(tp, EPHYAR) & EPHYAR_DATA_MASK : ~0; } -static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type) -{ - /* based on RTL8168FP_OOBMAC_BASE in vendor driver */ - if (tp->mac_version == RTL_GIGA_MAC_VER_52 && type == ERIAR_OOB) - *cmd |= 0x7f0 << 18; -} - -DECLARE_RTL_COND(rtl_eriar_cond) -{ - return RTL_R32(tp, ERIAR) & ERIAR_FLAG; -} - -static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, - u32 val, int type) -{ - u32 cmd = ERIAR_WRITE_CMD | type | mask | addr; - - BUG_ON((addr & 3) || (mask == 0)); - RTL_W32(tp, ERIDR, val); - r8168fp_adjust_ocp_cmd(tp, &cmd, type); - RTL_W32(tp, ERIAR, cmd); - - rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100); -} - -static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, - u32 val) -{ - _rtl_eri_write(tp, addr, mask, val, ERIAR_EXGMAC); -} - -static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type) -{ - u32 cmd = ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr; - - r8168fp_adjust_ocp_cmd(tp, &cmd, type); - RTL_W32(tp, ERIAR, cmd); - - return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ? - RTL_R32(tp, ERIDR) : ~0; -} - -static u32 rtl_eri_read(struct rtl8169_private *tp, int addr) -{ - return _rtl_eri_read(tp, addr, ERIAR_EXGMAC); -} - -static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 p, u32 m) -{ - u32 val = rtl_eri_read(tp, addr); - - rtl_eri_write(tp, addr, ERIAR_MASK_1111, (val & ~m) | p); -} - -static void rtl_eri_set_bits(struct rtl8169_private *tp, int addr, u32 p) -{ - rtl_w0w1_eri(tp, addr, p, 0); -} - -static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 m) -{ - rtl_w0w1_eri(tp, addr, 0, m); -} - static u32 r8168dp_ocp_read(struct rtl8169_private *tp, u16 reg) { RTL_W32(tp, OCPAR, 0x0fu << 12 | (reg & 0x0fff)); @@ -1158,19 +1191,10 @@ static void rtl8168ep_driver_start(struct rtl8169_private *tp) static void rtl8168_driver_start(struct rtl8169_private *tp) { - switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_27: - case RTL_GIGA_MAC_VER_28: - case RTL_GIGA_MAC_VER_31: + if (tp->dash_type == RTL_DASH_DP) rtl8168dp_driver_start(tp); - break; - case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_52: + else rtl8168ep_driver_start(tp); - break; - default: - BUG(); - break; - } } static void rtl8168dp_driver_stop(struct rtl8169_private *tp) @@ -1189,44 +1213,51 @@ static void rtl8168ep_driver_stop(struct rtl8169_private *tp) static void rtl8168_driver_stop(struct rtl8169_private *tp) { - switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_27: - case RTL_GIGA_MAC_VER_28: - case RTL_GIGA_MAC_VER_31: + if (tp->dash_type == RTL_DASH_DP) rtl8168dp_driver_stop(tp); - break; - case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_52: + else rtl8168ep_driver_stop(tp); - break; - default: - BUG(); - break; - } } static bool r8168dp_check_dash(struct rtl8169_private *tp) { u16 reg = rtl8168_get_ocp_reg(tp); - return !!(r8168dp_ocp_read(tp, reg) & 0x00008000); + return r8168dp_ocp_read(tp, reg) & BIT(15); } static bool r8168ep_check_dash(struct rtl8169_private *tp) { - return r8168ep_ocp_read(tp, 0x128) & 0x00000001; + return r8168ep_ocp_read(tp, 0x128) & BIT(0); } -static bool r8168_check_dash(struct rtl8169_private *tp) +static enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp) { switch (tp->mac_version) { case RTL_GIGA_MAC_VER_27: case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: - return r8168dp_check_dash(tp); + return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE; case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_52: - return r8168ep_check_dash(tp); + return r8168ep_check_dash(tp) ? RTL_DASH_EP : RTL_DASH_NONE; default: - return false; + return RTL_DASH_NONE; + } +} + +static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26: + case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37: + case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63: + if (enable) + RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN); + else + RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | D3_NO_PLL_DOWN); + break; + default: + break; } } @@ -1396,6 +1427,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) rtl_lock_config_regs(tp); device_set_wakeup_enable(tp_to_dev(tp), wolopts); + rtl_set_d3_pll_down(tp, !wolopts); tp->dev->wol_enabled = wolopts ? 1 : 0; } @@ -1962,7 +1994,11 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 }, /* 8168DP family. */ - { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 }, + /* It seems this early RTL8168dp version never made it to + * the wild. Let's see whether somebody complains, if not + * we'll remove support for this chip version completely. + * { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 }, + */ { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 }, { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 }, @@ -2081,18 +2117,12 @@ static void rtl8125b_config_eee_mac(struct rtl8169_private *tp) r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0)); } -static void rtl_rar_exgmac_set(struct rtl8169_private *tp, u8 *addr) +static void rtl_rar_exgmac_set(struct rtl8169_private *tp, const u8 *addr) { - const u16 w[] = { - addr[0] | (addr[1] << 8), - addr[2] | (addr[3] << 8), - addr[4] | (addr[5] << 8) - }; - - rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, w[0] | (w[1] << 16)); - rtl_eri_write(tp, 0xe4, ERIAR_MASK_1111, w[2]); - rtl_eri_write(tp, 0xf0, ERIAR_MASK_1111, w[0] << 16); - rtl_eri_write(tp, 0xf4, ERIAR_MASK_1111, w[1] | (w[2] << 16)); + rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, get_unaligned_le32(addr)); + rtl_eri_write(tp, 0xe4, ERIAR_MASK_1111, get_unaligned_le16(addr + 4)); + rtl_eri_write(tp, 0xf0, ERIAR_MASK_1111, get_unaligned_le16(addr) << 16); + rtl_eri_write(tp, 0xf4, ERIAR_MASK_1111, get_unaligned_le32(addr + 2)); } u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp) @@ -2142,14 +2172,14 @@ static void rtl8169_init_phy(struct rtl8169_private *tp) genphy_soft_reset(tp->phydev); } -static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr) +static void rtl_rar_set(struct rtl8169_private *tp, const u8 *addr) { rtl_unlock_config_regs(tp); - RTL_W32(tp, MAC4, addr[4] | addr[5] << 8); + RTL_W32(tp, MAC4, get_unaligned_le16(addr + 4)); rtl_pci_commit(tp); - RTL_W32(tp, MAC0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + RTL_W32(tp, MAC0, get_unaligned_le32(addr)); rtl_pci_commit(tp); if (tp->mac_version == RTL_GIGA_MAC_VER_34) @@ -2172,28 +2202,16 @@ static int rtl_set_mac_address(struct net_device *dev, void *p) return 0; } -static void rtl_wol_suspend_quirk(struct rtl8169_private *tp) +static void rtl_wol_enable_rx(struct rtl8169_private *tp) { - switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_25: - case RTL_GIGA_MAC_VER_26: - case RTL_GIGA_MAC_VER_29: - case RTL_GIGA_MAC_VER_30: - case RTL_GIGA_MAC_VER_32: - case RTL_GIGA_MAC_VER_33: - case RTL_GIGA_MAC_VER_34: - case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_63: + if (tp->mac_version >= RTL_GIGA_MAC_VER_25) RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) | AcceptBroadcast | AcceptMulticast | AcceptMyPhys); - break; - default: - break; - } } -static void rtl_pll_power_down(struct rtl8169_private *tp) +static void rtl_prepare_power_down(struct rtl8169_private *tp) { - if (r8168_check_dash(tp)) + if (tp->dash_type != RTL_DASH_NONE) return; if (tp->mac_version == RTL_GIGA_MAC_VER_32 || @@ -2202,66 +2220,10 @@ static void rtl_pll_power_down(struct rtl8169_private *tp) if (device_may_wakeup(tp_to_dev(tp))) { phy_speed_down(tp->phydev, false); - rtl_wol_suspend_quirk(tp); - return; - } - - switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26: - case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_33: - case RTL_GIGA_MAC_VER_37: - case RTL_GIGA_MAC_VER_39: - case RTL_GIGA_MAC_VER_43: - case RTL_GIGA_MAC_VER_44: - case RTL_GIGA_MAC_VER_45: - case RTL_GIGA_MAC_VER_46: - case RTL_GIGA_MAC_VER_47: - case RTL_GIGA_MAC_VER_48: - case RTL_GIGA_MAC_VER_50 ... RTL_GIGA_MAC_VER_63: - RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~0x80); - break; - case RTL_GIGA_MAC_VER_40: - case RTL_GIGA_MAC_VER_41: - case RTL_GIGA_MAC_VER_49: - rtl_eri_clear_bits(tp, 0x1a8, 0xfc000000); - RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~0x80); - break; - default: - break; + rtl_wol_enable_rx(tp); } } -static void rtl_pll_power_up(struct rtl8169_private *tp) -{ - switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26: - case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_33: - case RTL_GIGA_MAC_VER_37: - case RTL_GIGA_MAC_VER_39: - case RTL_GIGA_MAC_VER_43: - RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0x80); - break; - case RTL_GIGA_MAC_VER_44: - case RTL_GIGA_MAC_VER_45: - case RTL_GIGA_MAC_VER_46: - case RTL_GIGA_MAC_VER_47: - case RTL_GIGA_MAC_VER_48: - case RTL_GIGA_MAC_VER_50 ... RTL_GIGA_MAC_VER_63: - RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0xc0); - break; - case RTL_GIGA_MAC_VER_40: - case RTL_GIGA_MAC_VER_41: - case RTL_GIGA_MAC_VER_49: - RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0xc0); - rtl_eri_set_bits(tp, 0x1a8, 0xfc000000); - break; - default: - break; - } - - phy_resume(tp->phydev); -} - static void rtl_init_rxcfg(struct rtl8169_private *tp) { switch (tp->mac_version) { @@ -2340,13 +2302,14 @@ static void r8168b_1_hw_jumbo_disable(struct rtl8169_private *tp) static void rtl_jumbo_config(struct rtl8169_private *tp) { bool jumbo = tp->dev->mtu > ETH_DATA_LEN; + int readrq = 4096; rtl_unlock_config_regs(tp); switch (tp->mac_version) { case RTL_GIGA_MAC_VER_12: case RTL_GIGA_MAC_VER_17: if (jumbo) { - pcie_set_readrq(tp->pci_dev, 512); + readrq = 512; r8168b_1_hw_jumbo_enable(tp); } else { r8168b_1_hw_jumbo_disable(tp); @@ -2354,7 +2317,7 @@ static void rtl_jumbo_config(struct rtl8169_private *tp) break; case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_26: if (jumbo) { - pcie_set_readrq(tp->pci_dev, 512); + readrq = 512; r8168c_hw_jumbo_enable(tp); } else { r8168c_hw_jumbo_disable(tp); @@ -2367,20 +2330,18 @@ static void rtl_jumbo_config(struct rtl8169_private *tp) r8168dp_hw_jumbo_disable(tp); break; case RTL_GIGA_MAC_VER_31 ... RTL_GIGA_MAC_VER_33: - if (jumbo) { - pcie_set_readrq(tp->pci_dev, 512); + if (jumbo) r8168e_hw_jumbo_enable(tp); - } else { + else r8168e_hw_jumbo_disable(tp); - } break; default: break; } rtl_lock_config_regs(tp); - if (!jumbo && pci_is_pcie(tp->pci_dev) && tp->supports_gmii) - pcie_set_readrq(tp->pci_dev, 4096); + if (pci_is_pcie(tp->pci_dev) && tp->supports_gmii) + pcie_set_readrq(tp->pci_dev, readrq); } DECLARE_RTL_COND(rtl_chipcmd_cond) @@ -4408,10 +4369,9 @@ static inline int rtl8169_fragmented_frame(u32 status) static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1) { - u32 status = opts1 & RxProtoMask; + u32 status = opts1 & (RxProtoMask | RxCSFailMask); - if (((status == RxProtoTCP) && !(opts1 & TCPFail)) || - ((status == RxProtoUDP) && !(opts1 & UDPFail))) + if (status == RxProtoTCP || status == RxProtoUDP) skb->ip_summed = CHECKSUM_UNNECESSARY; else skb_checksum_none_assert(skb); @@ -4618,12 +4578,12 @@ static void rtl8169_down(struct rtl8169_private *tp) rtl8169_cleanup(tp, true); - rtl_pll_power_down(tp); + rtl_prepare_power_down(tp); } static void rtl8169_up(struct rtl8169_private *tp) { - rtl_pll_power_up(tp); + phy_resume(tp->phydev); rtl8169_init_phy(tp); napi_enable(&tp->napi); set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags); @@ -4890,12 +4850,10 @@ static void rtl_shutdown(struct pci_dev *pdev) rtl_rar_set(tp, tp->dev->perm_addr); if (system_state == SYSTEM_POWER_OFF) { - if (tp->saved_wolopts) { - rtl_wol_suspend_quirk(tp); + if (tp->saved_wolopts) rtl_wol_shutdown_quirk(tp); - } - pci_wake_from_d3(pdev, true); + pci_wake_from_d3(pdev, tp->saved_wolopts); pci_set_power_state(pdev, PCI_D3hot); } } @@ -4909,7 +4867,7 @@ static void rtl_remove_one(struct pci_dev *pdev) unregister_netdev(tp->dev); - if (r8168_check_dash(tp)) + if (tp->dash_type != RTL_DASH_NONE) rtl8168_driver_stop(tp); rtl_release_firmware(tp); @@ -4977,16 +4935,12 @@ static void rtl_read_mac_address(struct rtl8169_private *tp, { /* Get MAC address */ if (rtl_is_8168evl_up(tp) && tp->mac_version != RTL_GIGA_MAC_VER_34) { - u32 value = rtl_eri_read(tp, 0xe0); - - mac_addr[0] = (value >> 0) & 0xff; - mac_addr[1] = (value >> 8) & 0xff; - mac_addr[2] = (value >> 16) & 0xff; - mac_addr[3] = (value >> 24) & 0xff; + u32 value; + value = rtl_eri_read(tp, 0xe0); + put_unaligned_le32(value, mac_addr); value = rtl_eri_read(tp, 0xe4); - mac_addr[4] = (value >> 0) & 0xff; - mac_addr[5] = (value >> 8) & 0xff; + put_unaligned_le16(value, mac_addr + 4); } else if (rtl_is_8125(tp)) { rtl_read_mac_from_reg(tp, mac_addr, MAC0_BKP); } @@ -5267,12 +5221,14 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* Identify chip attached to board */ chipset = rtl8169_get_mac_version(xid, tp->supports_gmii); if (chipset == RTL_GIGA_MAC_NONE) { - dev_err(&pdev->dev, "unknown chip XID %03x\n", xid); + dev_err(&pdev->dev, "unknown chip XID %03x, contact r8169 maintainers (see MAINTAINERS file)\n", xid); return -ENODEV; } tp->mac_version = chipset; + tp->dash_type = rtl_check_dash(tp); + tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK; if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 && @@ -5342,6 +5298,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* configure chip for default features */ rtl8169_set_features(dev, dev->features); + rtl_set_d3_pll_down(tp, true); + jumbo_max = rtl_jumbo_max(tp); if (jumbo_max) dev->max_mtu = jumbo_max; @@ -5362,9 +5320,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - /* chip gets powered up in rtl_open() */ - rtl_pll_power_down(tp); - rc = register_netdev(dev); if (rc) return rc; @@ -5378,7 +5333,7 @@ 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 (tp->dash_type != RTL_DASH_NONE) { netdev_info(dev, "DASH enabled\n"); rtl8168_driver_start(tp); } diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index 7453b17a37a2..cb47e68c1a3e 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -165,7 +165,7 @@ enum ravb_reg { GTO2 = 0x03A8, GIC = 0x03AC, GIS = 0x03B0, - GCPT = 0x03B4, /* Undocumented? */ + GCPT = 0x03B4, /* Documented for R-Car Gen3 only */ GCT0 = 0x03B8, GCT1 = 0x03BC, GCT2 = 0x03C0, @@ -225,7 +225,7 @@ enum CSR_BIT { CSR_OPS_RESET = 0x00000001, CSR_OPS_CONFIG = 0x00000002, CSR_OPS_OPERATION = 0x00000004, - CSR_OPS_STANDBY = 0x00000008, /* Undocumented? */ + CSR_OPS_STANDBY = 0x00000008, /* Documented for R-Car Gen3 only */ CSR_DTS = 0x00000100, CSR_TPO0 = 0x00010000, CSR_TPO1 = 0x00020000, @@ -241,13 +241,12 @@ enum ESR_BIT { ESR_EIL = 0x00001000, }; -/* APSR */ +/* APSR (R-Car Gen3 only) */ enum APSR_BIT { - APSR_MEMS = 0x00000002, - APSR_CMSW = 0x00000010, - APSR_DM = 0x00006000, /* Undocumented? */ - APSR_DM_RDM = 0x00002000, - APSR_DM_TDM = 0x00004000, + APSR_MEMS = 0x00000002, /* Undocumented */ + APSR_CMSW = 0x00000010, + APSR_RDM = 0x00002000, + APSR_TDM = 0x00004000, }; /* RCR */ @@ -530,16 +529,16 @@ enum RIS2_BIT { /* TIC */ enum TIC_BIT { - TIC_FTE0 = 0x00000001, /* Undocumented? */ - TIC_FTE1 = 0x00000002, /* Undocumented? */ + TIC_FTE0 = 0x00000001, /* Documented for R-Car Gen3 only */ + TIC_FTE1 = 0x00000002, /* Documented for R-Car Gen3 only */ TIC_TFUE = 0x00000100, TIC_TFWE = 0x00000200, }; /* TIS */ enum TIS_BIT { - TIS_FTF0 = 0x00000001, /* Undocumented? */ - TIS_FTF1 = 0x00000002, /* Undocumented? */ + TIS_FTF0 = 0x00000001, /* Documented for R-Car Gen3 only */ + TIS_FTF1 = 0x00000002, /* Documented for R-Car Gen3 only */ TIS_TFUF = 0x00000100, TIS_TFWF = 0x00000200, TIS_RESERVED = (GENMASK(31, 20) | GENMASK(15, 12) | GENMASK(7, 4)) @@ -547,8 +546,8 @@ enum TIS_BIT { /* ISS */ enum ISS_BIT { - ISS_FRS = 0x00000001, /* Undocumented? */ - ISS_FTS = 0x00000004, /* Undocumented? */ + ISS_FRS = 0x00000001, /* Documented for R-Car Gen3 only */ + ISS_FTS = 0x00000004, /* Documented for R-Car Gen3 only */ ISS_ES = 0x00000040, ISS_MS = 0x00000080, ISS_TFUS = 0x00000100, @@ -608,13 +607,13 @@ enum GTI_BIT { /* GIC */ enum GIC_BIT { - GIC_PTCE = 0x00000001, /* Undocumented? */ + GIC_PTCE = 0x00000001, /* Documented for R-Car Gen3 only */ GIC_PTME = 0x00000004, }; /* GIS */ enum GIS_BIT { - GIS_PTCF = 0x00000001, /* Undocumented? */ + GIS_PTCF = 0x00000001, /* Documented for R-Car Gen3 only */ GIS_PTMF = 0x00000004, GIS_RESERVED = GENMASK(15, 10), }; @@ -808,10 +807,10 @@ enum ECMR_BIT { ECMR_TE = 0x00000020, ECMR_RE = 0x00000040, ECMR_MPDE = 0x00000200, - ECMR_TXF = 0x00010000, /* Undocumented? */ + ECMR_TXF = 0x00010000, /* Documented for R-Car Gen3 only */ ECMR_RXF = 0x00020000, ECMR_PFR = 0x00040000, - ECMR_ZPF = 0x00080000, /* Undocumented? */ + ECMR_ZPF = 0x00080000, /* Documented for R-Car Gen3 only */ ECMR_RZPF = 0x00100000, ECMR_DPAD = 0x00200000, ECMR_RCSC = 0x00800000, @@ -830,7 +829,7 @@ enum ECSR_BIT { enum ECSIPR_BIT { ECSIPR_ICDIP = 0x00000001, ECSIPR_MPDIP = 0x00000002, - ECSIPR_LCHNGIP = 0x00000004, /* Undocumented? */ + ECSIPR_LCHNGIP = 0x00000004, }; /* PIR */ diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index bd30505fbc57..eb0c03bdb12d 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -2034,10 +2034,10 @@ static void ravb_set_delay_mode(struct net_device *ndev) u32 set = 0; if (priv->rxcidm) - set |= APSR_DM_RDM; + set |= APSR_RDM; if (priv->txcidm) - set |= APSR_DM_TDM; - ravb_modify(ndev, APSR, APSR_DM, set); + set |= APSR_TDM; + ravb_modify(ndev, APSR, APSR_RDM | APSR_TDM, set); } static int ravb_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index 6fad25321dc5..315a6e5c0f59 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -103,15 +103,13 @@ struct rocker_world_ops { int (*port_attr_stp_state_set)(struct rocker_port *rocker_port, u8 state); int (*port_attr_bridge_flags_set)(struct rocker_port *rocker_port, - unsigned long brport_flags, - struct switchdev_trans *trans); + unsigned long brport_flags); int (*port_attr_bridge_flags_support_get)(const struct rocker_port * rocker_port, unsigned long * p_brport_flags); int (*port_attr_bridge_ageing_time_set)(struct rocker_port *rocker_port, - u32 ageing_time, - struct switchdev_trans *trans); + u32 ageing_time); int (*port_obj_vlan_add)(struct rocker_port *rocker_port, const struct switchdev_obj_port_vlan *vlan); int (*port_obj_vlan_del)(struct rocker_port *rocker_port, diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index dd0bc7f0aaee..740a715c49c6 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1550,17 +1550,13 @@ static void rocker_world_port_stop(struct rocker_port *rocker_port) } static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port, - u8 state, - struct switchdev_trans *trans) + u8 state) { struct rocker_world_ops *wops = rocker_port->rocker->wops; if (!wops->port_attr_stp_state_set) return -EOPNOTSUPP; - if (switchdev_trans_ph_prepare(trans)) - return 0; - return wops->port_attr_stp_state_set(rocker_port, state); } @@ -1580,8 +1576,7 @@ rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port * static int rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port, - unsigned long brport_flags, - struct switchdev_trans *trans) + unsigned long brport_flags) { struct rocker_world_ops *wops = rocker_port->rocker->wops; unsigned long brport_flags_s; @@ -1603,52 +1598,37 @@ rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port, static int rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, - unsigned long brport_flags, - struct switchdev_trans *trans) + unsigned long brport_flags) { struct rocker_world_ops *wops = rocker_port->rocker->wops; if (!wops->port_attr_bridge_flags_set) return -EOPNOTSUPP; - if (switchdev_trans_ph_prepare(trans)) - return 0; - - return wops->port_attr_bridge_flags_set(rocker_port, brport_flags, - trans); + return wops->port_attr_bridge_flags_set(rocker_port, brport_flags); } static int rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, - u32 ageing_time, - struct switchdev_trans *trans) - + u32 ageing_time) { struct rocker_world_ops *wops = rocker_port->rocker->wops; if (!wops->port_attr_bridge_ageing_time_set) return -EOPNOTSUPP; - if (switchdev_trans_ph_prepare(trans)) - return 0; - - return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time, - trans); + return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time); } static int rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) + const struct switchdev_obj_port_vlan *vlan) { struct rocker_world_ops *wops = rocker_port->rocker->wops; if (!wops->port_obj_vlan_add) return -EOPNOTSUPP; - if (switchdev_trans_ph_prepare(trans)) - return 0; - return wops->port_obj_vlan_add(rocker_port, vlan); } @@ -2066,8 +2046,7 @@ static const struct net_device_ops rocker_port_netdev_ops = { ********************/ static int rocker_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr, - struct switchdev_trans *trans) + const struct switchdev_attr *attr) { struct rocker_port *rocker_port = netdev_priv(dev); int err = 0; @@ -2075,23 +2054,19 @@ static int rocker_port_attr_set(struct net_device *dev, switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: err = rocker_world_port_attr_stp_state_set(rocker_port, - attr->u.stp_state, - trans); + attr->u.stp_state); break; case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: err = rocker_world_port_attr_pre_bridge_flags_set(rocker_port, - attr->u.brport_flags, - trans); + attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: err = rocker_world_port_attr_bridge_flags_set(rocker_port, - attr->u.brport_flags, - trans); + attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: err = rocker_world_port_attr_bridge_ageing_time_set(rocker_port, - attr->u.ageing_time, - trans); + attr->u.ageing_time); break; default: err = -EOPNOTSUPP; @@ -2102,8 +2077,7 @@ static int rocker_port_attr_set(struct net_device *dev, } static int rocker_port_obj_add(struct net_device *dev, - const struct switchdev_obj *obj, - struct switchdev_trans *trans) + const struct switchdev_obj *obj) { struct rocker_port *rocker_port = netdev_priv(dev); int err = 0; @@ -2111,8 +2085,7 @@ static int rocker_port_obj_add(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: err = rocker_world_port_obj_vlan_add(rocker_port, - SWITCHDEV_OBJ_PORT_VLAN(obj), - trans); + SWITCHDEV_OBJ_PORT_VLAN(obj)); break; default: err = -EOPNOTSUPP; @@ -2725,8 +2698,7 @@ rocker_switchdev_port_attr_set_event(struct net_device *netdev, { int err; - err = rocker_port_attr_set(netdev, port_attr_info->attr, - port_attr_info->trans); + err = rocker_port_attr_set(netdev, port_attr_info->attr); port_attr_info->handled = true; return notifier_from_errno(err); @@ -2847,8 +2819,7 @@ rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev, switch (event) { case SWITCHDEV_PORT_OBJ_ADD: - err = rocker_port_obj_add(netdev, port_obj_info->obj, - port_obj_info->trans); + err = rocker_port_obj_add(netdev, port_obj_info->obj); break; case SWITCHDEV_PORT_OBJ_DEL: err = rocker_port_obj_del(netdev, port_obj_info->obj); diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 7072b249c8bd..967a634ee9ac 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -923,7 +923,7 @@ static int ofdpa_flow_tbl_bridge(struct ofdpa_port *ofdpa_port, struct ofdpa_flow_tbl_entry *entry; u32 priority; bool vlan_bridging = !!vlan_id; - bool dflt = !eth_dst || (eth_dst && eth_dst_mask); + bool dflt = !eth_dst || eth_dst_mask; bool wild = false; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); @@ -2488,8 +2488,7 @@ static int ofdpa_port_attr_stp_state_set(struct rocker_port *rocker_port, } static int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port, - unsigned long brport_flags, - struct switchdev_trans *trans) + unsigned long brport_flags) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; unsigned long orig_flags; @@ -2497,14 +2496,11 @@ static int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port, orig_flags = ofdpa_port->brport_flags; ofdpa_port->brport_flags = brport_flags; - if ((orig_flags ^ ofdpa_port->brport_flags) & BR_LEARNING && - !switchdev_trans_ph_prepare(trans)) + + if ((orig_flags ^ ofdpa_port->brport_flags) & BR_LEARNING) err = rocker_port_set_learning(ofdpa_port->rocker_port, !!(ofdpa_port->brport_flags & BR_LEARNING)); - if (switchdev_trans_ph_prepare(trans)) - ofdpa_port->brport_flags = orig_flags; - return err; } @@ -2520,18 +2516,15 @@ ofdpa_port_attr_bridge_flags_support_get(const struct rocker_port * static int ofdpa_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, - u32 ageing_time, - struct switchdev_trans *trans) + u32 ageing_time) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; struct ofdpa *ofdpa = ofdpa_port->ofdpa; - if (!switchdev_trans_ph_prepare(trans)) { - ofdpa_port->ageing_time = clock_t_to_jiffies(ageing_time); - if (ofdpa_port->ageing_time < ofdpa->ageing_time) - ofdpa->ageing_time = ofdpa_port->ageing_time; - mod_timer(&ofdpa_port->ofdpa->fdb_cleanup_timer, jiffies); - } + ofdpa_port->ageing_time = clock_t_to_jiffies(ageing_time); + if (ofdpa_port->ageing_time < ofdpa->ageing_time) + ofdpa->ageing_time = ofdpa_port->ageing_time; + mod_timer(&ofdpa_port->ofdpa->fdb_cleanup_timer, jiffies); return 0; } @@ -2540,32 +2533,16 @@ static int ofdpa_port_obj_vlan_add(struct rocker_port *rocker_port, const struct switchdev_obj_port_vlan *vlan) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - u16 vid; - int err; - - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - err = ofdpa_port_vlan_add(ofdpa_port, vid, vlan->flags); - if (err) - return err; - } - return 0; + return ofdpa_port_vlan_add(ofdpa_port, vlan->vid, vlan->flags); } static int ofdpa_port_obj_vlan_del(struct rocker_port *rocker_port, const struct switchdev_obj_port_vlan *vlan) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - u16 vid; - int err; - - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - err = ofdpa_port_vlan_del(ofdpa_port, vid, vlan->flags); - if (err) - return err; - } - return 0; + return ofdpa_port_vlan_del(ofdpa_port, vlan->vid, vlan->flags); } static int ofdpa_port_obj_fdb_add(struct rocker_port *rocker_port, diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 718308076341..36c8625a6fd7 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -612,8 +612,6 @@ static const struct net_device_ops efx_netdev_ops = { #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = efx_filter_rfs, #endif - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_xdp_xmit = efx_xdp_xmit, .ndo_bpf = efx_xdp }; diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index a4a626e9cd9a..1bfeee283ea9 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -17,6 +17,7 @@ #include "rx_common.h" #include "nic.h" #include "sriov.h" +#include "workarounds.h" /* This is the first interrupt mode to try out of: * 0 => MSI-X @@ -137,6 +138,7 @@ static int efx_allocate_msix_channels(struct efx_nic *efx, { unsigned int n_channels = parallelism; int vec_count; + int tx_per_ev; int n_xdp_tx; int n_xdp_ev; @@ -149,9 +151,9 @@ static int efx_allocate_msix_channels(struct efx_nic *efx, * multiple tx queues, assuming tx and ev queues are both * maximum size. */ - + tx_per_ev = EFX_MAX_EVQ_SIZE / EFX_TXQ_MAX_ENT(efx); n_xdp_tx = num_possible_cpus(); - n_xdp_ev = DIV_ROUND_UP(n_xdp_tx, EFX_MAX_TXQ_PER_CHANNEL); + n_xdp_ev = DIV_ROUND_UP(n_xdp_tx, tx_per_ev); vec_count = pci_msix_vec_count(efx->pci_dev); if (vec_count < 0) diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index aaa112877561..89c5c75f479f 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -293,14 +293,10 @@ static bool efx_do_xdp(struct efx_nic *efx, struct efx_channel *channel, memcpy(rx_prefix, *ehp - efx->rx_prefix_size, efx->rx_prefix_size); - xdp.data = *ehp; - xdp.data_hard_start = xdp.data - EFX_XDP_HEADROOM; - + xdp_init_buff(&xdp, efx->rx_page_buf_step, &rx_queue->xdp_rxq_info); /* No support yet for XDP metadata */ - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = xdp.data + rx_buf->len; - xdp.rxq = &rx_queue->xdp_rxq_info; - xdp.frame_sz = efx->rx_page_buf_step; + xdp_prepare_buff(&xdp, *ehp - EFX_XDP_HEADROOM, EFX_XDP_HEADROOM, + rx_buf->len, false); xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp); rcu_read_unlock(); diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 742a1f7a838c..891b49281bc6 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2191,7 +2191,7 @@ static const struct of_device_id smc91x_match[] = { MODULE_DEVICE_TABLE(of, smc91x_match); /** - * of_try_set_control_gpio - configure a gpio if it exists + * try_toggle_control_gpio - configure a gpio if it exists * @dev: net device * @desc: where to store the GPIO descriptor, if it exists * @name: name of the GPIO in DT diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 823d9a7184fe..606c79de93a6 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -557,6 +557,7 @@ static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx) unsigned int addr; int i, reg; + pm_runtime_get_sync(bus->parent); spin_lock_irqsave(&pdata->mac_lock, flags); /* Confirm MII not busy */ @@ -582,6 +583,7 @@ static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx) out: spin_unlock_irqrestore(&pdata->mac_lock, flags); + pm_runtime_put(bus->parent); return reg; } @@ -594,6 +596,7 @@ static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx, unsigned int addr; int i, reg; + pm_runtime_get_sync(bus->parent); spin_lock_irqsave(&pdata->mac_lock, flags); /* Confirm MII not busy */ @@ -623,6 +626,7 @@ static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx, out: spin_unlock_irqrestore(&pdata->mac_lock, flags); + pm_runtime_put(bus->parent); return reg; } @@ -1589,6 +1593,8 @@ static int smsc911x_open(struct net_device *dev) int retval; int irq_flags; + pm_runtime_get_sync(dev->dev.parent); + /* find and start the given phy */ if (!dev->phydev) { retval = smsc911x_mii_probe(dev); @@ -1735,6 +1741,7 @@ mii_free_out: phy_disconnect(dev->phydev); dev->phydev = NULL; out: + pm_runtime_put(dev->dev.parent); return retval; } @@ -1766,6 +1773,7 @@ static int smsc911x_stop(struct net_device *dev) dev->phydev = NULL; } netif_carrier_off(dev); + pm_runtime_put(dev->dev.parent); SMSC_TRACE(pdata, ifdown, "Interface stopped"); return 0; @@ -2334,7 +2342,6 @@ static int smsc911x_drv_remove(struct platform_device *pdev) free_netdev(dev); - pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; @@ -2540,6 +2547,7 @@ static int smsc911x_drv_probe(struct platform_device *pdev) } spin_unlock_irq(&pdata->mac_lock); + pm_runtime_put(&pdev->dev); netdev_info(dev, "MAC Address: %pM\n", dev->dev_addr); diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 19d20a6d0d44..3c53051bdacf 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -956,8 +956,7 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) u32 xdp_act = 0; int done = 0; - xdp.rxq = &dring->xdp_rxq; - xdp.frame_sz = PAGE_SIZE; + xdp_init_buff(&xdp, PAGE_SIZE, &dring->xdp_rxq); rcu_read_lock(); xdp_prog = READ_ONCE(priv->xdp_prog); @@ -1016,10 +1015,8 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) dma_dir); prefetch(desc->addr); - xdp.data_hard_start = desc->addr; - xdp.data = desc->addr + NETSEC_RXBUF_HEADROOM; - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = xdp.data + pkt_len; + xdp_prepare_buff(&xdp, desc->addr, NETSEC_RXBUF_HEADROOM, + pkt_len, false); if (xdp_prog) { xdp_result = netsec_run_xdp(priv, xdp_prog, &xdp); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 103d2448e9e0..1c9c67b641a2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -459,6 +459,21 @@ static struct stmmac_pci_info tgl_sgmii1g_info = { .setup = tgl_sgmii_data, }; +static int adls_sgmii_data(struct pci_dev *pdev, + struct plat_stmmacenet_data *plat) +{ + plat->bus_id = 1; + plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + + /* SerDes power up and power down are done in BIOS for ADL */ + + return tgl_common_data(pdev, plat); +} + +static struct stmmac_pci_info adls_sgmii1g_info = { + .setup = adls_sgmii_data, +}; + static const struct stmmac_pci_func_data galileo_stmmac_func_data[] = { { .func = 6, @@ -726,6 +741,8 @@ static SIMPLE_DEV_PM_OPS(intel_eth_pm_ops, intel_eth_pci_suspend, #define PCI_DEVICE_ID_INTEL_TGLH_SGMII1G_0_ID 0x43ac #define PCI_DEVICE_ID_INTEL_TGLH_SGMII1G_1_ID 0x43a2 #define PCI_DEVICE_ID_INTEL_TGL_SGMII1G_ID 0xa0ac +#define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_0_ID 0x7aac +#define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_1_ID 0x7aad static const struct pci_device_id intel_eth_pci_id_table[] = { { PCI_DEVICE_DATA(INTEL, QUARK_ID, &quark_info) }, @@ -741,6 +758,8 @@ static const struct pci_device_id intel_eth_pci_id_table[] = { { PCI_DEVICE_DATA(INTEL, TGL_SGMII1G_ID, &tgl_sgmii1g_info) }, { PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_0_ID, &tgl_sgmii1g_info) }, { PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_1_ID, &tgl_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_0_ID, &adls_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_1_ID, &adls_sgmii1g_info) }, {} }; MODULE_DEVICE_TABLE(pci, intel_eth_pci_id_table); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index f184b00f5116..848e5c37746b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -68,10 +68,21 @@ */ #define PRG_ETH0_ADJ_SKEW GENMASK(24, 20) +#define PRG_ETH1 0x4 + +/* Defined for adding a delay to the input RX_CLK for better timing. + * Each step is 200ps. These bits are used with external RGMII PHYs + * because RGMII RX only has the small window. cfg_rxclk_dly can + * adjust the window between RX_CLK and RX_DATA and improve the stability + * of "rx data valid". + */ +#define PRG_ETH1_CFG_RXCLK_DLY GENMASK(19, 16) + struct meson8b_dwmac; struct meson8b_dwmac_data { int (*set_phy_mode)(struct meson8b_dwmac *dwmac); + bool has_prg_eth1_rgmii_rx_delay; }; struct meson8b_dwmac { @@ -82,7 +93,7 @@ struct meson8b_dwmac { phy_interface_t phy_mode; struct clk *rgmii_tx_clk; u32 tx_delay_ns; - u32 rx_delay_ns; + u32 rx_delay_ps; struct clk *timing_adj_clk; }; @@ -268,32 +279,37 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, return 0; } -static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) +static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac) { - u32 tx_dly_config, rx_dly_config, delay_config; + u32 tx_dly_config, rx_adj_config, cfg_rxclk_dly, delay_config; int ret; + rx_adj_config = 0; + cfg_rxclk_dly = 0; tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK, dwmac->tx_delay_ns >> 1); - if (dwmac->rx_delay_ns == 2) - rx_dly_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP; - else - rx_dly_config = 0; + if (dwmac->data->has_prg_eth1_rgmii_rx_delay) + cfg_rxclk_dly = FIELD_PREP(PRG_ETH1_CFG_RXCLK_DLY, + dwmac->rx_delay_ps / 200); + else if (dwmac->rx_delay_ps == 2000) + rx_adj_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP; switch (dwmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: - delay_config = tx_dly_config | rx_dly_config; + delay_config = tx_dly_config | rx_adj_config; break; case PHY_INTERFACE_MODE_RGMII_RXID: delay_config = tx_dly_config; + cfg_rxclk_dly = 0; break; case PHY_INTERFACE_MODE_RGMII_TXID: - delay_config = rx_dly_config; + delay_config = rx_adj_config; break; case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RMII: delay_config = 0; + cfg_rxclk_dly = 0; break; default: dev_err(dwmac->dev, "unsupported phy-mode %s\n", @@ -301,7 +317,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) return -EINVAL; } - if (rx_dly_config & PRG_ETH0_ADJ_ENABLE) { + if (delay_config & PRG_ETH0_ADJ_ENABLE) { if (!dwmac->timing_adj_clk) { dev_err(dwmac->dev, "The timing-adjustment clock is mandatory for the RX delay re-timing\n"); @@ -323,6 +339,16 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW, delay_config); + meson8b_dwmac_mask_bits(dwmac, PRG_ETH1, PRG_ETH1_CFG_RXCLK_DLY, + cfg_rxclk_dly); + + return 0; +} + +static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) +{ + int ret; + if (phy_interface_mode_is_rgmii(dwmac->phy_mode)) { /* only relevant for RMII mode -> disable in RGMII mode */ meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, @@ -406,16 +432,30 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) &dwmac->tx_delay_ns)) dwmac->tx_delay_ns = 2; - /* use 0ns as fallback since this is what most boards actually use */ - if (of_property_read_u32(pdev->dev.of_node, "amlogic,rx-delay-ns", - &dwmac->rx_delay_ns)) - dwmac->rx_delay_ns = 0; + /* RX delay defaults to 0ps since this is what many boards use */ + if (of_property_read_u32(pdev->dev.of_node, "rx-internal-delay-ps", + &dwmac->rx_delay_ps)) { + if (!of_property_read_u32(pdev->dev.of_node, + "amlogic,rx-delay-ns", + &dwmac->rx_delay_ps)) + /* convert ns to ps */ + dwmac->rx_delay_ps *= 1000; + } - if (dwmac->rx_delay_ns != 0 && dwmac->rx_delay_ns != 2) { - dev_err(&pdev->dev, - "The only allowed RX delays values are: 0ns, 2ns"); - ret = -EINVAL; - goto err_remove_config_dt; + if (dwmac->data->has_prg_eth1_rgmii_rx_delay) { + if (dwmac->rx_delay_ps > 3000 || dwmac->rx_delay_ps % 200) { + dev_err(dwmac->dev, + "The RGMII RX delay range is 0..3000ps in 200ps steps"); + ret = -EINVAL; + goto err_remove_config_dt; + } + } else { + if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) { + dev_err(dwmac->dev, + "The only allowed RGMII RX delays values are: 0ps, 2000ps"); + ret = -EINVAL; + goto err_remove_config_dt; + } } dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev, @@ -425,6 +465,10 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) goto err_remove_config_dt; } + ret = meson8b_init_rgmii_delays(dwmac); + if (ret) + goto err_remove_config_dt; + ret = meson8b_init_rgmii_tx_clk(dwmac); if (ret) goto err_remove_config_dt; @@ -453,10 +497,17 @@ err_remove_config_dt: static const struct meson8b_dwmac_data meson8b_dwmac_data = { .set_phy_mode = meson8b_set_phy_mode, + .has_prg_eth1_rgmii_rx_delay = false, }; static const struct meson8b_dwmac_data meson_axg_dwmac_data = { .set_phy_mode = meson_axg_set_phy_mode, + .has_prg_eth1_rgmii_rx_delay = false, +}; + +static const struct meson8b_dwmac_data meson_g12a_dwmac_data = { + .set_phy_mode = meson_axg_set_phy_mode, + .has_prg_eth1_rgmii_rx_delay = true, }; static const struct of_device_id meson8b_dwmac_match[] = { @@ -478,7 +529,7 @@ static const struct of_device_id meson8b_dwmac_match[] = { }, { .compatible = "amlogic,meson-g12a-dwmac", - .data = &meson_axg_dwmac_data, + .data = &meson_g12a_dwmac_data, }, { } }; diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 766e8866bbef..1850743c04da 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -366,8 +366,9 @@ static int am65_cpsw_nuss_rx_push(struct am65_cpsw_common *common, } desc_dma = k3_cppi_desc_pool_virt2dma(rx_chn->desc_pool, desc_rx); - buf_dma = dma_map_single(dev, skb->data, pkt_len, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(dev, buf_dma))) { + buf_dma = dma_map_single(rx_chn->dma_dev, skb->data, pkt_len, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(rx_chn->dma_dev, buf_dma))) { k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); dev_err(dev, "Failed to map rx skb buffer\n"); return -EINVAL; @@ -375,6 +376,7 @@ static int am65_cpsw_nuss_rx_push(struct am65_cpsw_common *common, cppi5_hdesc_init(desc_rx, CPPI5_INFO0_HDESC_EPIB_PRESENT, AM65_CPSW_NAV_PS_DATA_SIZE); + k3_udma_glue_rx_dma_to_cppi5_addr(rx_chn->rx_chn, &buf_dma); cppi5_hdesc_attach_buf(desc_rx, buf_dma, skb_tailroom(skb), buf_dma, skb_tailroom(skb)); swdata = cppi5_hdesc_get_swdata(desc_rx); *((void **)swdata) = skb; @@ -691,8 +693,9 @@ static void am65_cpsw_nuss_rx_cleanup(void *data, dma_addr_t desc_dma) swdata = cppi5_hdesc_get_swdata(desc_rx); skb = *swdata; cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); - dma_unmap_single(rx_chn->dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); dev_kfree_skb_any(skb); @@ -779,6 +782,7 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_common *common, swdata = cppi5_hdesc_get_swdata(desc_rx); skb = *swdata; cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len); + k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); pkt_len = cppi5_hdesc_get_pktlen(desc_rx); cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL); dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id); @@ -793,7 +797,7 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_common *common, csum_info = psdata[2]; dev_dbg(dev, "%s rx csum_info:%#x\n", __func__, csum_info); - dma_unmap_single(dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); + dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE); k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx); @@ -864,7 +868,6 @@ static int am65_cpsw_nuss_rx_poll(struct napi_struct *napi_rx, int budget) } static void am65_cpsw_nuss_xmit_free(struct am65_cpsw_tx_chn *tx_chn, - struct device *dev, struct cppi5_host_desc_t *desc) { struct cppi5_host_desc_t *first_desc, *next_desc; @@ -875,20 +878,23 @@ static void am65_cpsw_nuss_xmit_free(struct am65_cpsw_tx_chn *tx_chn, next_desc = first_desc; cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma); - dma_unmap_single(dev, buf_dma, buf_dma_len, - DMA_TO_DEVICE); + dma_unmap_single(tx_chn->dma_dev, buf_dma, buf_dma_len, DMA_TO_DEVICE); next_desc_dma = cppi5_hdesc_get_next_hbdesc(first_desc); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma); while (next_desc_dma) { next_desc = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, next_desc_dma); cppi5_hdesc_get_obuf(next_desc, &buf_dma, &buf_dma_len); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma); - dma_unmap_page(dev, buf_dma, buf_dma_len, + dma_unmap_page(tx_chn->dma_dev, buf_dma, buf_dma_len, DMA_TO_DEVICE); next_desc_dma = cppi5_hdesc_get_next_hbdesc(next_desc); + k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma); k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); } @@ -906,7 +912,7 @@ static void am65_cpsw_nuss_tx_cleanup(void *data, dma_addr_t desc_dma) desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, desc_dma); swdata = cppi5_hdesc_get_swdata(desc_tx); skb = *(swdata); - am65_cpsw_nuss_xmit_free(tx_chn, tx_chn->common->dev, desc_tx); + am65_cpsw_nuss_xmit_free(tx_chn, desc_tx); dev_kfree_skb_any(skb); } @@ -926,7 +932,7 @@ am65_cpsw_nuss_tx_compl_packet(struct am65_cpsw_tx_chn *tx_chn, desc_dma); swdata = cppi5_hdesc_get_swdata(desc_tx); skb = *(swdata); - am65_cpsw_nuss_xmit_free(tx_chn, tx_chn->common->dev, desc_tx); + am65_cpsw_nuss_xmit_free(tx_chn, desc_tx); ndev = skb->dev; @@ -1119,9 +1125,9 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb, netif_txq = netdev_get_tx_queue(ndev, q_idx); /* Map the linear buffer */ - buf_dma = dma_map_single(dev, skb->data, pkt_len, + buf_dma = dma_map_single(tx_chn->dma_dev, skb->data, pkt_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(dev, buf_dma))) { + if (unlikely(dma_mapping_error(tx_chn->dma_dev, buf_dma))) { dev_err(dev, "Failed to map tx skb buffer\n"); ndev->stats.tx_errors++; goto err_free_skb; @@ -1130,7 +1136,8 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb, first_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool); if (!first_desc) { dev_dbg(dev, "Failed to allocate descriptor\n"); - dma_unmap_single(dev, buf_dma, pkt_len, DMA_TO_DEVICE); + dma_unmap_single(tx_chn->dma_dev, buf_dma, pkt_len, + DMA_TO_DEVICE); goto busy_stop_q; } @@ -1140,6 +1147,7 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb, cppi5_hdesc_set_pkttype(first_desc, 0x7); cppi5_desc_set_tags_ids(&first_desc->hdr, 0, port->port_id); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); cppi5_hdesc_attach_buf(first_desc, buf_dma, pkt_len, buf_dma, pkt_len); swdata = cppi5_hdesc_get_swdata(first_desc); *(swdata) = skb; @@ -1175,9 +1183,9 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb, goto busy_free_descs; } - buf_dma = skb_frag_dma_map(dev, frag, 0, frag_size, + buf_dma = skb_frag_dma_map(tx_chn->dma_dev, frag, 0, frag_size, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(dev, buf_dma))) { + if (unlikely(dma_mapping_error(tx_chn->dma_dev, buf_dma))) { dev_err(dev, "Failed to map tx skb page\n"); k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc); ndev->stats.tx_errors++; @@ -1185,11 +1193,13 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb, } cppi5_hdesc_reset_hbdesc(next_desc); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); cppi5_hdesc_attach_buf(next_desc, buf_dma, frag_size, buf_dma, frag_size); desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool, next_desc); + k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &desc_dma); cppi5_hdesc_link_hbdesc(cur_desc, desc_dma); pkt_len += frag_size; @@ -1237,14 +1247,14 @@ done_tx: return NETDEV_TX_OK; err_free_descs: - am65_cpsw_nuss_xmit_free(tx_chn, dev, first_desc); + am65_cpsw_nuss_xmit_free(tx_chn, first_desc); err_free_skb: ndev->stats.tx_dropped++; dev_kfree_skb_any(skb); return NETDEV_TX_OK; busy_free_descs: - am65_cpsw_nuss_xmit_free(tx_chn, dev, first_desc); + am65_cpsw_nuss_xmit_free(tx_chn, first_desc); busy_stop_q: netif_tx_stop_queue(netif_txq); return NETDEV_TX_BUSY; @@ -1545,16 +1555,6 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) tx_chn->common = common; tx_chn->id = i; tx_chn->descs_num = max_desc_num; - tx_chn->desc_pool = - k3_cppi_desc_pool_create_name(dev, - tx_chn->descs_num, - hdesc_size, - tx_chn->tx_chn_name); - if (IS_ERR(tx_chn->desc_pool)) { - ret = PTR_ERR(tx_chn->desc_pool); - dev_err(dev, "Failed to create poll %d\n", ret); - goto err; - } tx_chn->tx_chn = k3_udma_glue_request_tx_chn(dev, @@ -1565,6 +1565,17 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) "Failed to request tx dma channel\n"); goto err; } + tx_chn->dma_dev = k3_udma_glue_tx_get_dma_device(tx_chn->tx_chn); + + tx_chn->desc_pool = k3_cppi_desc_pool_create_name(tx_chn->dma_dev, + tx_chn->descs_num, + hdesc_size, + tx_chn->tx_chn_name); + if (IS_ERR(tx_chn->desc_pool)) { + ret = PTR_ERR(tx_chn->desc_pool); + dev_err(dev, "Failed to create poll %d\n", ret); + goto err; + } tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); if (tx_chn->irq <= 0) { @@ -1622,14 +1633,6 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) /* init all flows */ rx_chn->dev = dev; rx_chn->descs_num = max_desc_num; - rx_chn->desc_pool = k3_cppi_desc_pool_create_name(dev, - rx_chn->descs_num, - hdesc_size, "rx"); - if (IS_ERR(rx_chn->desc_pool)) { - ret = PTR_ERR(rx_chn->desc_pool); - dev_err(dev, "Failed to create rx poll %d\n", ret); - goto err; - } rx_chn->rx_chn = k3_udma_glue_request_rx_chn(dev, "rx", &rx_cfg); if (IS_ERR(rx_chn->rx_chn)) { @@ -1637,6 +1640,16 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) "Failed to request rx dma channel\n"); goto err; } + rx_chn->dma_dev = k3_udma_glue_rx_get_dma_device(rx_chn->rx_chn); + + rx_chn->desc_pool = k3_cppi_desc_pool_create_name(rx_chn->dma_dev, + rx_chn->descs_num, + hdesc_size, "rx"); + if (IS_ERR(rx_chn->desc_pool)) { + ret = PTR_ERR(rx_chn->desc_pool); + dev_err(dev, "Failed to create rx poll %d\n", ret); + goto err; + } common->rx_flow_id_base = k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn); @@ -2102,9 +2115,16 @@ static const struct am65_cpsw_pdata j721e_pdata = { .fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE, }; +static const struct am65_cpsw_pdata am64x_cpswxg_pdata = { + .quirks = 0, + .ale_dev_id = "am64-cpswxg", + .fdqring_mode = K3_RINGACC_RING_MODE_RING, +}; + static const struct of_device_id am65_cpsw_nuss_of_mtable[] = { { .compatible = "ti,am654-cpsw-nuss", .data = &am65x_sr1_0}, { .compatible = "ti,j721e-cpsw-nuss", .data = &j721e_pdata}, + { .compatible = "ti,am642-cpsw-nuss", .data = &am64x_cpswxg_pdata}, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, am65_cpsw_nuss_of_mtable); @@ -2164,12 +2184,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) common->tx_ch_num = 1; common->pf_p0_rx_ptype_rrobin = false; - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(48)); - if (ret) { - dev_err(dev, "error setting dma mask: %d\n", ret); - return ret; - } - common->ports = devm_kcalloc(dev, common->port_num, sizeof(*common->ports), GFP_KERNEL); diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index 02aed4c0ceba..d7f8a0f76fdc 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -56,6 +56,7 @@ struct am65_cpsw_host { }; struct am65_cpsw_tx_chn { + struct device *dma_dev; struct napi_struct napi_tx; struct am65_cpsw_common *common; struct k3_cppi_desc_pool *desc_pool; @@ -69,6 +70,7 @@ struct am65_cpsw_tx_chn { struct am65_cpsw_rx_chn { struct device *dev; + struct device *dma_dev; struct k3_cppi_desc_pool *desc_pool; struct k3_udma_glue_rx_channel *rx_chn; u32 descs_num; diff --git a/drivers/net/ethernet/ti/am65-cpsw-qos.c b/drivers/net/ethernet/ti/am65-cpsw-qos.c index 3bdd4dbcd2ff..ebcc6386cc34 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-qos.c +++ b/drivers/net/ethernet/ti/am65-cpsw-qos.c @@ -356,7 +356,7 @@ static void am65_cpsw_est_set_sched_list(struct net_device *ndev, writel(~all_fetch_allow & AM65_CPSW_FETCH_ALLOW_MSK, ram_addr); } -/** +/* * Enable ESTf periodic output, set cycle start time and interval. */ static int am65_cpsw_timer_set(struct net_device *ndev, diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c index 5dc60ecabe56..9caaae79fc95 100644 --- a/drivers/net/ethernet/ti/am65-cpts.c +++ b/drivers/net/ethernet/ti/am65-cpts.c @@ -727,7 +727,7 @@ static long am65_cpts_ts_work(struct ptp_clock_info *ptp) /** * am65_cpts_rx_enable - enable rx timestamping * @cpts: cpts handle - * @skb: packet + * @en: enable * * This functions enables rx packets timestamping. The CPTS can timestamp all * rx packets. diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index b0f00b4edd94..5239318e9686 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -392,21 +392,15 @@ static void cpsw_rx_handler(void *token, int len, int status) } if (priv->xdp_prog) { + int headroom = CPSW_HEADROOM, size = len; + + xdp_init_buff(&xdp, PAGE_SIZE, &priv->xdp_rxq[ch]); if (status & CPDMA_RX_VLAN_ENCAP) { - xdp.data = pa + CPSW_HEADROOM + - CPSW_RX_VLAN_ENCAP_HDR_SIZE; - xdp.data_end = xdp.data + len - - CPSW_RX_VLAN_ENCAP_HDR_SIZE; - } else { - xdp.data = pa + CPSW_HEADROOM; - xdp.data_end = xdp.data + len; + headroom += CPSW_RX_VLAN_ENCAP_HDR_SIZE; + size -= CPSW_RX_VLAN_ENCAP_HDR_SIZE; } - xdp_set_data_meta_invalid(&xdp); - - xdp.data_hard_start = pa; - xdp.rxq = &priv->xdp_rxq[ch]; - xdp.frame_sz = PAGE_SIZE; + xdp_prepare_buff(&xdp, pa, headroom, size, false); port = priv->emac_port + cpsw->data.dual_emac; ret = cpsw_run_xdp(priv, ch, &xdp, page, port); diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index cdc308a2aa3e..d828f856237a 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -1256,6 +1256,13 @@ static const struct cpsw_ale_dev_id cpsw_ale_id_match[] = { .major_ver_mask = 0x7, .vlan_entry_tbl = vlan_entry_k3_cpswxg, }, + { + .dev_id = "am64-cpswxg", + .features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING, + .major_ver_mask = 0x7, + .vlan_entry_tbl = vlan_entry_k3_cpswxg, + .tbl_entries = 512, + }, { }, }; diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c index 2f5e0ad23ad7..94747f82c60b 100644 --- a/drivers/net/ethernet/ti/cpsw_new.c +++ b/drivers/net/ethernet/ti/cpsw_new.c @@ -335,21 +335,15 @@ static void cpsw_rx_handler(void *token, int len, int status) } if (priv->xdp_prog) { + int headroom = CPSW_HEADROOM, size = len; + + xdp_init_buff(&xdp, PAGE_SIZE, &priv->xdp_rxq[ch]); if (status & CPDMA_RX_VLAN_ENCAP) { - xdp.data = pa + CPSW_HEADROOM + - CPSW_RX_VLAN_ENCAP_HDR_SIZE; - xdp.data_end = xdp.data + len - - CPSW_RX_VLAN_ENCAP_HDR_SIZE; - } else { - xdp.data = pa + CPSW_HEADROOM; - xdp.data_end = xdp.data + len; + headroom += CPSW_RX_VLAN_ENCAP_HDR_SIZE; + size -= CPSW_RX_VLAN_ENCAP_HDR_SIZE; } - xdp_set_data_meta_invalid(&xdp); - - xdp.data_hard_start = pa; - xdp.rxq = &priv->xdp_rxq[ch]; - xdp.frame_sz = PAGE_SIZE; + xdp_prepare_buff(&xdp, pa, headroom, size, false); ret = cpsw_run_xdp(priv, ch, &xdp, page, priv->emac_port); if (ret != CPSW_XDP_PASS) diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c index 29747da5c514..9967cf985728 100644 --- a/drivers/net/ethernet/ti/cpsw_switchdev.c +++ b/drivers/net/ethernet/ti/cpsw_switchdev.c @@ -24,16 +24,12 @@ struct cpsw_switchdev_event_work { unsigned long event; }; -static int cpsw_port_stp_state_set(struct cpsw_priv *priv, - struct switchdev_trans *trans, u8 state) +static int cpsw_port_stp_state_set(struct cpsw_priv *priv, u8 state) { struct cpsw_common *cpsw = priv->cpsw; u8 cpsw_state; int ret = 0; - if (switchdev_trans_ph_prepare(trans)) - return 0; - switch (state) { case BR_STATE_FORWARDING: cpsw_state = ALE_PORT_STATE_FORWARD; @@ -60,16 +56,12 @@ static int cpsw_port_stp_state_set(struct cpsw_priv *priv, } static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv, - struct switchdev_trans *trans, struct net_device *orig_dev, unsigned long brport_flags) { struct cpsw_common *cpsw = priv->cpsw; bool unreg_mcast_add = false; - if (switchdev_trans_ph_prepare(trans)) - return 0; - if (brport_flags & BR_MCAST_FLOOD) unreg_mcast_add = true; dev_dbg(priv->dev, "BR_MCAST_FLOOD: %d port %u\n", @@ -82,7 +74,6 @@ static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv, } static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, - struct switchdev_trans *trans, unsigned long flags) { if (flags & ~(BR_LEARNING | BR_MCAST_FLOOD)) @@ -92,8 +83,7 @@ static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, } static int cpsw_port_attr_set(struct net_device *ndev, - const struct switchdev_attr *attr, - struct switchdev_trans *trans) + const struct switchdev_attr *attr) { struct cpsw_priv *priv = netdev_priv(ndev); int ret; @@ -102,15 +92,15 @@ static int cpsw_port_attr_set(struct net_device *ndev, switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - ret = cpsw_port_attr_br_flags_pre_set(ndev, trans, + ret = cpsw_port_attr_br_flags_pre_set(ndev, attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - ret = cpsw_port_stp_state_set(priv, trans, attr->u.stp_state); + ret = cpsw_port_stp_state_set(priv, attr->u.stp_state); dev_dbg(priv->dev, "stp state: %u\n", attr->u.stp_state); break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: - ret = cpsw_port_attr_br_flags_set(priv, trans, attr->orig_dev, + ret = cpsw_port_attr_br_flags_set(priv, attr->orig_dev, attr->u.brport_flags); break; default: @@ -253,56 +243,24 @@ static int cpsw_port_vlan_del(struct cpsw_priv *priv, u16 vid, } static int cpsw_port_vlans_add(struct cpsw_priv *priv, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) + const struct switchdev_obj_port_vlan *vlan) { bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct net_device *orig_dev = vlan->obj.orig_dev; bool cpu_port = netif_is_bridge_master(orig_dev); bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; - u16 vid; dev_dbg(priv->dev, "VID add: %s: vid:%u flags:%X\n", - priv->ndev->name, vlan->vid_begin, vlan->flags); + priv->ndev->name, vlan->vid, vlan->flags); if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY)) return 0; - if (switchdev_trans_ph_prepare(trans)) - return 0; - - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - int err; - - err = cpsw_port_vlan_add(priv, untag, pvid, vid, orig_dev); - if (err) - return err; - } - - return 0; -} - -static int cpsw_port_vlans_del(struct cpsw_priv *priv, - const struct switchdev_obj_port_vlan *vlan) - -{ - struct net_device *orig_dev = vlan->obj.orig_dev; - u16 vid; - - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - int err; - - err = cpsw_port_vlan_del(priv, vid, orig_dev); - if (err) - return err; - } - - return 0; + return cpsw_port_vlan_add(priv, untag, pvid, vlan->vid, orig_dev); } static int cpsw_port_mdb_add(struct cpsw_priv *priv, - struct switchdev_obj_port_mdb *mdb, - struct switchdev_trans *trans) + struct switchdev_obj_port_mdb *mdb) { struct net_device *orig_dev = mdb->obj.orig_dev; @@ -311,9 +269,6 @@ static int cpsw_port_mdb_add(struct cpsw_priv *priv, int port_mask; int err; - if (switchdev_trans_ph_prepare(trans)) - return 0; - if (cpu_port) port_mask = BIT(HOST_PORT_NUM); else @@ -352,7 +307,6 @@ static int cpsw_port_mdb_del(struct cpsw_priv *priv, static int cpsw_port_obj_add(struct net_device *ndev, const struct switchdev_obj *obj, - struct switchdev_trans *trans, struct netlink_ext_ack *extack) { struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); @@ -365,11 +319,11 @@ static int cpsw_port_obj_add(struct net_device *ndev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = cpsw_port_vlans_add(priv, vlan, trans); + err = cpsw_port_vlans_add(priv, vlan); break; case SWITCHDEV_OBJ_ID_PORT_MDB: case SWITCHDEV_OBJ_ID_HOST_MDB: - err = cpsw_port_mdb_add(priv, mdb, trans); + err = cpsw_port_mdb_add(priv, mdb); break; default: err = -EOPNOTSUPP; @@ -392,7 +346,7 @@ static int cpsw_port_obj_del(struct net_device *ndev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = cpsw_port_vlans_del(priv, vlan); + err = cpsw_port_vlan_del(priv, vlan->vid, vlan->obj.orig_dev); break; case SWITCHDEV_OBJ_ID_PORT_MDB: case SWITCHDEV_OBJ_ID_HOST_MDB: diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c index 3d1fc8d2ca66..55e652624bd7 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -1100,7 +1100,7 @@ static int gelic_net_poll(struct napi_struct *napi, int budget) return packets_done; } -/** +/* * gelic_card_interrupt - event handler for gelic_net */ static irqreturn_t gelic_card_interrupt(int irq, void *ptr) @@ -1400,6 +1400,7 @@ out: /** * gelic_net_tx_timeout - called when the tx timeout watchdog kicks in. * @netdev: interface device structure + * @txqueue: unused * * called, if tx hangs. Schedules a task that resets the interface */ @@ -1431,6 +1432,7 @@ static const struct net_device_ops gelic_netdevice_ops = { /** * gelic_ether_setup_netdev_ops - initialization of net_device operations * @netdev: net_device structure + * @napi: napi structure * * fills out function pointers in the net_device structure */ @@ -1632,7 +1634,7 @@ static void gelic_card_get_vlan_info(struct gelic_card *card) dev_info(ctodev(card), "internal vlan %s\n", card->vlan_required? "enabled" : "disabled"); } -/** +/* * ps3_gelic_driver_probe - add a device to the control of this driver */ static int ps3_gelic_driver_probe(struct ps3_system_bus_device *dev) @@ -1787,7 +1789,7 @@ fail_open: return result; } -/** +/* * ps3_gelic_driver_remove - remove a device from the control of this driver */ diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c index 5f5b33e6653b..d5a75ef7e3ca 100644 --- a/drivers/net/ethernet/toshiba/spider_net.c +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -254,7 +254,7 @@ spider_net_set_promisc(struct spider_net_card *card) /** * spider_net_get_descr_status -- returns the status of a descriptor - * @descr: descriptor to look at + * @hwdescr: descriptor to look at * * returns the status as in the dmac_cmd_status field of the descriptor */ @@ -542,6 +542,7 @@ error: /** * spider_net_get_multicast_hash - generates hash for multicast filter table + * @netdev: interface device structure * @addr: multicast address * * returns the hash value. @@ -890,7 +891,7 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev) /** * spider_net_cleanup_tx_ring - cleans up the TX ring - * @card: card structure + * @t: timer context used to obtain the pointer to net card data structure * * spider_net_cleanup_tx_ring is called by either the tx_timer * or from the NAPI polling routine. @@ -1063,6 +1064,7 @@ static void show_rx_chain(struct spider_net_card *card) /** * spider_net_resync_head_ptr - Advance head ptr past empty descrs + * @card: card structure * * If the driver fails to keep up and empty the queue, then the * hardware wil run out of room to put incoming packets. This @@ -1220,7 +1222,7 @@ bad_desc: /** * spider_net_poll - NAPI poll function called by the stack to return packets - * @netdev: interface device structure + * @napi: napi device structure * @budget: number of packets we can pass to the stack at most * * returns 0 if no more packets available to the driver/stack. Returns 1, @@ -1268,7 +1270,7 @@ static int spider_net_poll(struct napi_struct *napi, int budget) /** * spider_net_set_mac - sets the MAC of an interface * @netdev: interface device structure - * @ptr: pointer to new MAC address + * @p: pointer to new MAC address * * Returns 0 on success, <0 on failure. Currently, we don't support this * and will always return EOPNOTSUPP. @@ -1340,6 +1342,8 @@ spider_net_link_reset(struct net_device *netdev) * spider_net_handle_error_irq - handles errors raised by an interrupt * @card: card structure * @status_reg: interrupt status register 0 (GHIINT0STS) + * @error_reg1: interrupt status register 1 (GHIINT1STS) + * @error_reg2: interrupt status register 2 (GHIINT2STS) * * spider_net_handle_error_irq treats or ignores all error conditions * found when an interrupt is presented @@ -1961,8 +1965,7 @@ init_firmware_failed: /** * spider_net_link_phy - * @data: used for pointer to card structure - * + * @t: timer context used to obtain the pointer to net card data structure */ static void spider_net_link_phy(struct timer_list *t) { @@ -2140,7 +2143,7 @@ spider_net_stop(struct net_device *netdev) /** * spider_net_tx_timeout_task - task scheduled by the watchdog timeout * function (to be called not under interrupt status) - * @data: data, is interface device structure + * @work: work context used to obtain the pointer to net card data structure * * called as task when tx hangs, resets interface (if interface is up) */ @@ -2174,6 +2177,7 @@ out: /** * spider_net_tx_timeout - called when the tx timeout watchdog kicks in. * @netdev: interface device structure + * @txqueue: unused * * called, if tx hangs. Schedules a task that resets the interface */ diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 2e5202923510..0152f1e70783 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -247,7 +247,7 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt) } #endif -static spinlock_t mdio_lock; +static DEFINE_SPINLOCK(mdio_lock); static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */ static struct mii_bus *mdio_bus; static int ports_open; @@ -528,7 +528,6 @@ static int ixp4xx_mdio_register(struct eth_regs __iomem *regs) mdio_regs = regs; __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control); - spin_lock_init(&mdio_lock); mdio_bus->name = "IXP4xx MII Bus"; mdio_bus->read = &ixp4xx_mdio_read; mdio_bus->write = &ixp4xx_mdio_write; diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 5523f069b9a5..4ac0373326ef 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1197,11 +1197,12 @@ static void geneve_setup(struct net_device *dev) SET_NETDEV_DEVTYPE(dev, &geneve_type); dev->features |= NETIF_F_LLTX; - dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; - dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; + dev->hw_features |= NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; /* MTU range: 68 - (something less than 65535) */ @@ -1851,16 +1852,10 @@ static int geneve_netdevice_event(struct notifier_block *unused, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (event == NETDEV_UDP_TUNNEL_PUSH_INFO || - event == NETDEV_UDP_TUNNEL_DROP_INFO) { - geneve_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO); - } else if (event == NETDEV_UNREGISTER) { - if (!dev->udp_tunnel_nic_info) - geneve_offload_rx_ports(dev, false); - } else if (event == NETDEV_REGISTER) { - if (!dev->udp_tunnel_nic_info) - geneve_offload_rx_ports(dev, true); - } + if (event == NETDEV_UDP_TUNNEL_PUSH_INFO) + geneve_offload_rx_ports(dev, true); + else if (event == NETDEV_UDP_TUNNEL_DROP_INFO) + geneve_offload_rx_ports(dev, false); return NOTIFY_DONE; } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 4c04e271f184..851364314ecc 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -21,6 +21,7 @@ #include <linux/file.h> #include <linux/gtp.h> +#include <net/dst_metadata.h> #include <net/net_namespace.h> #include <net/protocol.h> #include <net/ip.h> @@ -73,6 +74,9 @@ struct gtp_dev { unsigned int hash_size; struct hlist_head *tid_hash; struct hlist_head *addr_hash; + /* Used by LWT tunnel. */ + bool collect_md; + struct socket *collect_md_sock; }; static unsigned int gtp_net_id __read_mostly; @@ -179,33 +183,121 @@ static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx, return false; } -static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, - unsigned int hdrlen, unsigned int role) +static int gtp_set_tun_dst(struct gtp_dev *gtp, struct sk_buff *skb, + unsigned int hdrlen, u8 gtp_version, + __be64 tid, u8 flags) { - if (!gtp_check_ms(skb, pctx, hdrlen, role)) { - netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); - return 1; + struct metadata_dst *tun_dst; + int opts_len = 0; + + if (unlikely(flags & GTP1_F_MASK)) + opts_len = sizeof(struct gtpu_metadata); + + tun_dst = udp_tun_rx_dst(skb, gtp->sk1u->sk_family, TUNNEL_KEY, tid, opts_len); + if (!tun_dst) { + netdev_dbg(gtp->dev, "Failed to allocate tun_dst"); + goto err; } + netdev_dbg(gtp->dev, "attaching metadata_dst to skb, gtp ver %d hdrlen %d\n", + gtp_version, hdrlen); + if (unlikely(opts_len)) { + struct gtpu_metadata *opts; + struct gtp1_header *gtp1; + + opts = ip_tunnel_info_opts(&tun_dst->u.tun_info); + gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); + opts->ver = GTP_METADATA_V1; + opts->flags = gtp1->flags; + opts->type = gtp1->type; + netdev_dbg(gtp->dev, "recved control pkt: flag %x type: %d\n", + opts->flags, opts->type); + tun_dst->u.tun_info.key.tun_flags |= TUNNEL_GTPU_OPT; + tun_dst->u.tun_info.options_len = opts_len; + skb->protocol = htons(0xffff); /* Unknown */ + } /* Get rid of the GTP + UDP headers. */ if (iptunnel_pull_header(skb, hdrlen, skb->protocol, - !net_eq(sock_net(pctx->sk), dev_net(pctx->dev)))) - return -1; + !net_eq(sock_net(gtp->sk1u), dev_net(gtp->dev)))) { + gtp->dev->stats.rx_length_errors++; + goto err; + } + + skb_dst_set(skb, &tun_dst->dst); + return 0; +err: + return -1; +} + +static int gtp_rx(struct gtp_dev *gtp, struct sk_buff *skb, + unsigned int hdrlen, u8 gtp_version, unsigned int role, + __be64 tid, u8 flags, u8 type) +{ + if (ip_tunnel_collect_metadata() || gtp->collect_md) { + int err; + + err = gtp_set_tun_dst(gtp, skb, hdrlen, gtp_version, tid, flags); + if (err) + goto err; + } else { + struct pdp_ctx *pctx; + + if (flags & GTP1_F_MASK) + hdrlen += 4; - netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); + if (type != GTP_TPDU) + return 1; + + if (gtp_version == GTP_V0) + pctx = gtp0_pdp_find(gtp, be64_to_cpu(tid)); + else + pctx = gtp1_pdp_find(gtp, be64_to_cpu(tid)); + if (!pctx) { + netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); + return 1; + } + + if (!gtp_check_ms(skb, pctx, hdrlen, role)) { + netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); + return 1; + } + /* Get rid of the GTP + UDP headers. */ + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, + !net_eq(sock_net(pctx->sk), dev_net(gtp->dev)))) { + gtp->dev->stats.rx_length_errors++; + goto err; + } + } + netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); /* Now that the UDP and the GTP header have been removed, set up the * new network header. This is required by the upper layer to * calculate the transport header. */ skb_reset_network_header(skb); + if (pskb_may_pull(skb, sizeof(struct iphdr))) { + struct iphdr *iph; + + iph = ip_hdr(skb); + if (iph->version == 4) { + netdev_dbg(gtp->dev, "inner pkt: ipv4"); + skb->protocol = htons(ETH_P_IP); + } else if (iph->version == 6) { + netdev_dbg(gtp->dev, "inner pkt: ipv6"); + skb->protocol = htons(ETH_P_IPV6); + } else { + netdev_dbg(gtp->dev, "inner pkt error: Unknown type"); + } + } - skb->dev = pctx->dev; - - dev_sw_netstats_rx_add(pctx->dev, skb->len); - + skb->dev = gtp->dev; + dev_sw_netstats_rx_add(gtp->dev, skb->len); netif_rx(skb); return 0; + +err: + gtp->dev->stats.rx_dropped++; + return -1; } /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ @@ -214,7 +306,6 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp0_header); struct gtp0_header *gtp0; - struct pdp_ctx *pctx; if (!pskb_may_pull(skb, hdrlen)) return -1; @@ -224,16 +315,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) if ((gtp0->flags >> 5) != GTP_V0) return 1; - if (gtp0->type != GTP_TPDU) - return 1; - - pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid)); - if (!pctx) { - netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); - return 1; - } - - return gtp_rx(pctx, skb, hdrlen, gtp->role); + return gtp_rx(gtp, skb, hdrlen, GTP_V0, gtp->role, gtp0->tid, gtp0->flags, gtp0->type); } static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) @@ -241,41 +323,30 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp1_header); struct gtp1_header *gtp1; - struct pdp_ctx *pctx; if (!pskb_may_pull(skb, hdrlen)) return -1; gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); + netdev_dbg(gtp->dev, "GTPv1 recv: flags %x\n", gtp1->flags); if ((gtp1->flags >> 5) != GTP_V1) return 1; - if (gtp1->type != GTP_TPDU) - return 1; - /* From 29.060: "This field shall be present if and only if any one or * more of the S, PN and E flags are set.". * * If any of the bit is set, then the remaining ones also have to be * set. */ - if (gtp1->flags & GTP1_F_MASK) - hdrlen += 4; - /* Make sure the header is larger enough, including extensions. */ if (!pskb_may_pull(skb, hdrlen)) return -1; gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); - pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid)); - if (!pctx) { - netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); - return 1; - } - - return gtp_rx(pctx, skb, hdrlen, gtp->role); + return gtp_rx(gtp, skb, hdrlen, GTP_V1, gtp->role, + key32_to_tunnel_id(gtp1->tid), gtp1->flags, gtp1->type); } static void __gtp_encap_destroy(struct sock *sk) @@ -315,6 +386,11 @@ static void gtp_encap_disable(struct gtp_dev *gtp) { gtp_encap_disable_sock(gtp->sk0); gtp_encap_disable_sock(gtp->sk1u); + if (gtp->collect_md_sock) { + udp_tunnel_sock_release(gtp->collect_md_sock); + gtp->collect_md_sock = NULL; + netdev_dbg(gtp->dev, "GTP socket released.\n"); + } } /* UDP encapsulation receive handler. See net/ipv4/udp.c. @@ -329,7 +405,8 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) if (!gtp) return 1; - netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); + netdev_dbg(gtp->dev, "encap_recv sk=%p type %d\n", + sk, udp_sk(sk)->encap_type); switch (udp_sk(sk)->encap_type) { case UDP_ENCAP_GTP0: @@ -383,12 +460,13 @@ static void gtp_dev_uninit(struct net_device *dev) static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4, const struct sock *sk, - __be32 daddr) + __be32 daddr, + __be32 saddr) { memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_oif = sk->sk_bound_dev_if; fl4->daddr = daddr; - fl4->saddr = inet_sk(sk)->inet_saddr; + fl4->saddr = saddr; fl4->flowi4_tos = RT_CONN_FLAGS(sk); fl4->flowi4_proto = sk->sk_protocol; @@ -412,7 +490,7 @@ static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) gtp0->tid = cpu_to_be64(pctx->u.v0.tid); } -static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) +static inline void gtp1_push_header(struct sk_buff *skb, __be32 tid) { int payload_len = skb->len; struct gtp1_header *gtp1; @@ -428,46 +506,63 @@ static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) gtp1->flags = 0x30; /* v1, GTP-non-prime. */ gtp1->type = GTP_TPDU; gtp1->length = htons(payload_len); - gtp1->tid = htonl(pctx->u.v1.o_tei); + gtp1->tid = tid; /* TODO: Suppport for extension header, sequence number and N-PDU. * Update the length field if any of them is available. */ } -struct gtp_pktinfo { - struct sock *sk; - struct iphdr *iph; - struct flowi4 fl4; - struct rtable *rt; - struct pdp_ctx *pctx; - struct net_device *dev; - __be16 gtph_port; -}; - -static void gtp_push_header(struct sk_buff *skb, struct gtp_pktinfo *pktinfo) +static inline int gtp1_push_control_header(struct sk_buff *skb, + __be32 tid, + struct gtpu_metadata *opts, + struct net_device *dev) { - switch (pktinfo->pctx->gtp_version) { - case GTP_V0: - pktinfo->gtph_port = htons(GTP0_PORT); - gtp0_push_header(skb, pktinfo->pctx); - break; - case GTP_V1: - pktinfo->gtph_port = htons(GTP1U_PORT); - gtp1_push_header(skb, pktinfo->pctx); - break; + struct gtp1_header *gtp1c; + int payload_len; + + if (opts->ver != GTP_METADATA_V1) + return -ENOENT; + + if (opts->type == 0xFE) { + /* for end marker ignore skb data. */ + netdev_dbg(dev, "xmit pkt with null data"); + pskb_trim(skb, 0); } + if (skb_cow_head(skb, sizeof(*gtp1c)) < 0) + return -ENOMEM; + + payload_len = skb->len; + + gtp1c = skb_push(skb, sizeof(*gtp1c)); + + gtp1c->flags = opts->flags; + gtp1c->type = opts->type; + gtp1c->length = htons(payload_len); + gtp1c->tid = tid; + netdev_dbg(dev, "xmit control pkt: ver %d flags %x type %x pkt len %d tid %x", + opts->ver, opts->flags, opts->type, skb->len, tid); + return 0; } +struct gtp_pktinfo { + struct sock *sk; + __u8 tos; + struct flowi4 fl4; + struct rtable *rt; + struct net_device *dev; + __be16 gtph_port; +}; + static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo, - struct sock *sk, struct iphdr *iph, - struct pdp_ctx *pctx, struct rtable *rt, + struct sock *sk, + __u8 tos, + struct rtable *rt, struct flowi4 *fl4, struct net_device *dev) { pktinfo->sk = sk; - pktinfo->iph = iph; - pktinfo->pctx = pctx; + pktinfo->tos = tos; pktinfo->rt = rt; pktinfo->fl4 = *fl4; pktinfo->dev = dev; @@ -477,40 +572,99 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, struct gtp_pktinfo *pktinfo) { struct gtp_dev *gtp = netdev_priv(dev); + struct gtpu_metadata *opts = NULL; + struct sock *sk = NULL; struct pdp_ctx *pctx; struct rtable *rt; struct flowi4 fl4; - struct iphdr *iph; - __be16 df; + u8 gtp_version; + __be16 df = 0; + __be32 tun_id; + __be32 daddr; + __be32 saddr; + __u8 tos; int mtu; - /* Read the IP destination address and resolve the PDP context. - * Prepend PDP header with TEI/TID from PDP ctx. - */ - iph = ip_hdr(skb); - if (gtp->role == GTP_ROLE_SGSN) - pctx = ipv4_pdp_find(gtp, iph->saddr); - else - pctx = ipv4_pdp_find(gtp, iph->daddr); + if (gtp->collect_md) { + /* LWT GTP1U encap */ + struct ip_tunnel_info *info = NULL; - if (!pctx) { - netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n", - &iph->daddr); - return -ENOENT; + info = skb_tunnel_info(skb); + if (!info) { + netdev_dbg(dev, "missing tunnel info"); + return -ENOENT; + } + if (info->key.tp_dst && ntohs(info->key.tp_dst) != GTP1U_PORT) { + netdev_dbg(dev, "unexpected GTP dst port: %d", ntohs(info->key.tp_dst)); + return -EOPNOTSUPP; + } + pctx = NULL; + gtp_version = GTP_V1; + tun_id = tunnel_id_to_key32(info->key.tun_id); + daddr = info->key.u.ipv4.dst; + saddr = info->key.u.ipv4.src; + sk = gtp->sk1u; + if (!sk) { + netdev_dbg(dev, "missing tunnel sock"); + return -EOPNOTSUPP; + } + tos = info->key.tos; + if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) + df = htons(IP_DF); + + if (info->options_len != 0) { + if (info->key.tun_flags & TUNNEL_GTPU_OPT) { + opts = ip_tunnel_info_opts(info); + } else { + netdev_dbg(dev, "missing tunnel metadata for control pkt"); + return -EOPNOTSUPP; + } + } + netdev_dbg(dev, "flow-based GTP1U encap: tunnel id %d\n", + be32_to_cpu(tun_id)); + } else { + struct iphdr *iph; + + if (ntohs(skb->protocol) != ETH_P_IP) + return -EOPNOTSUPP; + + iph = ip_hdr(skb); + + /* Read the IP destination address and resolve the PDP context. + * Prepend PDP header with TEI/TID from PDP ctx. + */ + if (gtp->role == GTP_ROLE_SGSN) + pctx = ipv4_pdp_find(gtp, iph->saddr); + else + pctx = ipv4_pdp_find(gtp, iph->daddr); + + if (!pctx) { + netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n", + &iph->daddr); + return -ENOENT; + } + sk = pctx->sk; + netdev_dbg(dev, "found PDP context %p\n", pctx); + + gtp_version = pctx->gtp_version; + tun_id = htonl(pctx->u.v1.o_tei); + daddr = pctx->peer_addr_ip4.s_addr; + saddr = inet_sk(sk)->inet_saddr; + tos = iph->tos; + df = iph->frag_off; + netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n", + &iph->saddr, &iph->daddr); } - netdev_dbg(dev, "found PDP context %p\n", pctx); - rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr); + rt = ip4_route_output_gtp(&fl4, sk, daddr, saddr); if (IS_ERR(rt)) { - netdev_dbg(dev, "no route to SSGN %pI4\n", - &pctx->peer_addr_ip4.s_addr); + netdev_dbg(dev, "no route to SSGN %pI4\n", &daddr); dev->stats.tx_carrier_errors++; goto err; } if (rt->dst.dev == dev) { - netdev_dbg(dev, "circular route to SSGN %pI4\n", - &pctx->peer_addr_ip4.s_addr); + netdev_dbg(dev, "circular route to SSGN %pI4\n", &daddr); dev->stats.collisions++; goto err_rt; } @@ -518,11 +672,10 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, skb_dst_drop(skb); /* This is similar to tnl_update_pmtu(). */ - df = iph->frag_off; if (df) { mtu = dst_mtu(&rt->dst) - dev->hard_header_len - sizeof(struct iphdr) - sizeof(struct udphdr); - switch (pctx->gtp_version) { + switch (gtp_version) { case GTP_V0: mtu -= sizeof(struct gtp0_header); break; @@ -536,17 +689,38 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu, false); - if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) && - mtu < ntohs(iph->tot_len)) { - netdev_dbg(dev, "packet too big, fragmentation needed\n"); + if (!skb_is_gso(skb) && (df & htons(IP_DF)) && mtu < skb->len) { + netdev_dbg(dev, "packet too big, fragmentation needed"); memset(IPCB(skb), 0, sizeof(*IPCB(skb))); icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); goto err_rt; } - gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev); - gtp_push_header(skb, pktinfo); + gtp_set_pktinfo_ipv4(pktinfo, sk, tos, rt, &fl4, dev); + + if (unlikely(opts)) { + int err; + + pktinfo->gtph_port = htons(GTP1U_PORT); + err = gtp1_push_control_header(skb, tun_id, opts, dev); + if (err) { + netdev_info(dev, "cntr pkt error %d", err); + goto err_rt; + } + return 0; + } + + switch (gtp_version) { + case GTP_V0: + pktinfo->gtph_port = htons(GTP0_PORT); + gtp0_push_header(skb, pctx); + break; + case GTP_V1: + pktinfo->gtph_port = htons(GTP1U_PORT); + gtp1_push_header(skb, tun_id); + break; + } return 0; err_rt: @@ -557,7 +731,6 @@ err: static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) { - unsigned int proto = ntohs(skb->protocol); struct gtp_pktinfo pktinfo; int err; @@ -569,32 +742,22 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) /* PDP context lookups in gtp_build_skb_*() need rcu read-side lock. */ rcu_read_lock(); - switch (proto) { - case ETH_P_IP: - err = gtp_build_skb_ip4(skb, dev, &pktinfo); - break; - default: - err = -EOPNOTSUPP; - break; - } + err = gtp_build_skb_ip4(skb, dev, &pktinfo); rcu_read_unlock(); if (err < 0) goto tx_err; - switch (proto) { - case ETH_P_IP: - netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI4 dst: %pI4\n", - &pktinfo.iph->saddr, &pktinfo.iph->daddr); - udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb, - pktinfo.fl4.saddr, pktinfo.fl4.daddr, - pktinfo.iph->tos, - ip4_dst_hoplimit(&pktinfo.rt->dst), - 0, - pktinfo.gtph_port, pktinfo.gtph_port, - true, false); - break; - } + udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb, + pktinfo.fl4.saddr, + pktinfo.fl4.daddr, + pktinfo.tos, + ip4_dst_hoplimit(&pktinfo.rt->dst), + 0, + pktinfo.gtph_port, + pktinfo.gtph_port, + true, + false); return NETDEV_TX_OK; tx_err: @@ -610,6 +773,19 @@ static const struct net_device_ops gtp_netdev_ops = { .ndo_get_stats64 = dev_get_tstats64, }; +static struct gtp_dev *gtp_find_flow_based_dev(struct net *net) +{ + struct gtp_net *gn = net_generic(net, gtp_net_id); + struct gtp_dev *gtp; + + list_for_each_entry(gtp, &gn->gtp_dev_list, list) { + if (gtp->collect_md) + return gtp; + } + + return NULL; +} + static void gtp_link_setup(struct net_device *dev) { dev->netdev_ops = >p_netdev_ops; @@ -634,7 +810,7 @@ static void gtp_link_setup(struct net_device *dev) } static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); -static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); +static int gtp_encap_enable(struct gtp_dev *gtp, struct net_device *dev, struct nlattr *data[]); static void gtp_destructor(struct net_device *dev) { @@ -652,11 +828,24 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, struct gtp_net *gn; int hashsize, err; - if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) + if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1] && + !data[IFLA_GTP_COLLECT_METADATA]) return -EINVAL; gtp = netdev_priv(dev); + if (data[IFLA_GTP_COLLECT_METADATA]) { + if (data[IFLA_GTP_FD0]) { + netdev_dbg(dev, "LWT device does not support setting v0 socket"); + return -EINVAL; + } + if (gtp_find_flow_based_dev(src_net)) { + netdev_dbg(dev, "LWT device already exist"); + return -EBUSY; + } + gtp->collect_md = true; + } + if (!data[IFLA_GTP_PDP_HASHSIZE]) { hashsize = 1024; } else { @@ -669,7 +858,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, if (err < 0) return err; - err = gtp_encap_enable(gtp, data); + err = gtp_encap_enable(gtp, dev, data); if (err < 0) goto out_hashtable; @@ -683,7 +872,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, list_add_rcu(>p->list, &gn->gtp_dev_list); dev->priv_destructor = gtp_destructor; - netdev_dbg(dev, "registered new GTP interface\n"); + netdev_dbg(dev, "registered new GTP interface %s\n", dev->name); return 0; @@ -714,6 +903,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { [IFLA_GTP_FD1] = { .type = NLA_U32 }, [IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 }, [IFLA_GTP_ROLE] = { .type = NLA_U32 }, + [IFLA_GTP_COLLECT_METADATA] = { .type = NLA_FLAG }, }; static int gtp_validate(struct nlattr *tb[], struct nlattr *data[], @@ -737,6 +927,9 @@ static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) if (nla_put_u32(skb, IFLA_GTP_PDP_HASHSIZE, gtp->hash_size)) goto nla_put_failure; + if (gtp->collect_md && nla_put_flag(skb, IFLA_GTP_COLLECT_METADATA)) + goto nla_put_failure; + return 0; nla_put_failure: @@ -782,35 +975,24 @@ err1: return -ENOMEM; } -static struct sock *gtp_encap_enable_socket(int fd, int type, - struct gtp_dev *gtp) +static int __gtp_encap_enable_socket(struct socket *sock, int type, + struct gtp_dev *gtp) { struct udp_tunnel_sock_cfg tuncfg = {NULL}; - struct socket *sock; struct sock *sk; - int err; - - pr_debug("enable gtp on %d, %d\n", fd, type); - - sock = sockfd_lookup(fd, &err); - if (!sock) { - pr_debug("gtp socket fd=%d not found\n", fd); - return NULL; - } sk = sock->sk; if (sk->sk_protocol != IPPROTO_UDP || sk->sk_type != SOCK_DGRAM || (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)) { - pr_debug("socket fd=%d not UDP\n", fd); - sk = ERR_PTR(-EINVAL); - goto out_sock; + pr_debug("socket not UDP\n"); + return -EINVAL; } lock_sock(sk); if (sk->sk_user_data) { - sk = ERR_PTR(-EBUSY); - goto out_rel_sock; + release_sock(sock->sk); + return -EBUSY; } sock_hold(sk); @@ -821,15 +1003,58 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, tuncfg.encap_destroy = gtp_encap_destroy; setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); - -out_rel_sock: release_sock(sock->sk); -out_sock: + return 0; +} + +static struct sock *gtp_encap_enable_socket(int fd, int type, + struct gtp_dev *gtp) +{ + struct socket *sock; + int err; + + pr_debug("enable gtp on %d, %d\n", fd, type); + + sock = sockfd_lookup(fd, &err); + if (!sock) { + pr_debug("gtp socket fd=%d not found\n", fd); + return NULL; + } + err = __gtp_encap_enable_socket(sock, type, gtp); sockfd_put(sock); - return sk; + if (err) + return ERR_PTR(err); + + return sock->sk; } -static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) +static struct socket *gtp_create_gtp_socket(struct gtp_dev *gtp, struct net_device *dev) +{ + struct udp_port_cfg udp_conf; + struct socket *sock; + int err; + + memset(&udp_conf, 0, sizeof(udp_conf)); + udp_conf.family = AF_INET; + udp_conf.local_ip.s_addr = htonl(INADDR_ANY); + udp_conf.local_udp_port = htons(GTP1U_PORT); + + err = udp_sock_create(dev_net(dev), &udp_conf, &sock); + if (err < 0) { + pr_debug("create gtp sock failed: %d\n", err); + return ERR_PTR(err); + } + err = __gtp_encap_enable_socket(sock, UDP_ENCAP_GTP1U, gtp); + if (err) { + pr_debug("enable gtp sock encap failed: %d\n", err); + udp_tunnel_sock_release(sock); + return ERR_PTR(err); + } + pr_debug("create gtp sock done\n"); + return sock; +} + +static int gtp_encap_enable(struct gtp_dev *gtp, struct net_device *dev, struct nlattr *data[]) { struct sock *sk1u = NULL; struct sock *sk0 = NULL; @@ -853,11 +1078,25 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) } } + if (data[IFLA_GTP_COLLECT_METADATA]) { + struct socket *sock; + + if (!sk1u) { + sock = gtp_create_gtp_socket(gtp, dev); + if (IS_ERR(sock)) + return PTR_ERR(sock); + + gtp->collect_md_sock = sock; + sk1u = sock->sk; + } else { + gtp->collect_md_sock = NULL; + } + } + if (data[IFLA_GTP_ROLE]) { role = nla_get_u32(data[IFLA_GTP_ROLE]); if (role > GTP_ROLE_SGSN) { - gtp_encap_disable_sock(sk0); - gtp_encap_disable_sock(sk1u); + gtp_encap_disable(gtp); return -EINVAL; } } @@ -1416,7 +1655,7 @@ static int __init gtp_init(void) if (err < 0) goto unreg_genl_family; - pr_info("GTP module loaded (pdp ctx size %zd bytes)\n", + pr_info("GTP module loaded (pdp ctx size %zd bytes) with tnl-md support\n", sizeof(struct pdp_ctx)); return 0; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 2350342b961f..6184e99c7f31 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -37,6 +37,10 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf) struct netvsc_device *nv_dev = rtnl_dereference(net_device_ctx->nvdev); struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt; + /* Block sending traffic to VF if it's about to be gone */ + if (!vf) + net_device_ctx->data_path_is_vf = vf; + memset(init_pkt, 0, sizeof(struct nvsp_message)); init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH; if (vf) @@ -50,8 +54,11 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf) vmbus_sendpacket(dev->channel, init_pkt, sizeof(struct nvsp_message), - VMBUS_RQST_ID_NO_RESPONSE, - VM_PKT_DATA_INBAND, 0); + (unsigned long)init_pkt, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + wait_for_completion(&nv_dev->channel_init_wait); + net_device_ctx->data_path_is_vf = vf; } /* Worker to setup sub channels on initial setup @@ -754,8 +761,31 @@ static void netvsc_send_completion(struct net_device *ndev, const struct vmpacket_descriptor *desc, int budget) { - const struct nvsp_message *nvsp_packet = hv_pkt_data(desc); + const struct nvsp_message *nvsp_packet; u32 msglen = hv_pkt_datalen(desc); + struct nvsp_message *pkt_rqst; + u64 cmd_rqst; + + /* First check if this is a VMBUS completion without data payload */ + if (!msglen) { + cmd_rqst = vmbus_request_addr(&incoming_channel->requestor, + (u64)desc->trans_id); + if (cmd_rqst == VMBUS_RQST_ERROR) { + netdev_err(ndev, "Invalid transaction id\n"); + return; + } + + pkt_rqst = (struct nvsp_message *)(uintptr_t)cmd_rqst; + switch (pkt_rqst->hdr.msg_type) { + case NVSP_MSG4_TYPE_SWITCH_DATA_PATH: + complete(&net_device->channel_init_wait); + break; + + default: + netdev_err(ndev, "Unexpected VMBUS completion!!\n"); + } + return; + } /* Ensure packet is big enough to read header fields */ if (msglen < sizeof(struct nvsp_message_header)) { @@ -763,6 +793,7 @@ static void netvsc_send_completion(struct net_device *ndev, return; } + nvsp_packet = hv_pkt_data(desc); switch (nvsp_packet->hdr.msg_type) { case NVSP_MSG_TYPE_INIT_COMPLETE: if (msglen < sizeof(struct nvsp_message_header) + @@ -887,6 +918,7 @@ static inline int netvsc_send_pkt( int ret; u32 ring_avail = hv_get_avail_to_write_percent(&out_channel->outbound); + memset(&nvmsg, 0, sizeof(struct nvsp_message)); nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; if (skb) rpkt->channel_type = 0; /* 0 is RMC_DATA */ @@ -1306,7 +1338,7 @@ static void netvsc_send_table(struct net_device *ndev, sizeof(union nvsp_6_message_uber); /* Boundary check for all versions */ - if (offset > msglen - count * sizeof(u32)) { + if (msglen < count * sizeof(u32) || offset > msglen - count * sizeof(u32)) { netdev_err(ndev, "Received send-table offset too big:%u\n", offset); return; diff --git a/drivers/net/hyperv/netvsc_bpf.c b/drivers/net/hyperv/netvsc_bpf.c index 440486d9c999..aa877da113f8 100644 --- a/drivers/net/hyperv/netvsc_bpf.c +++ b/drivers/net/hyperv/netvsc_bpf.c @@ -37,6 +37,12 @@ u32 netvsc_run_xdp(struct net_device *ndev, struct netvsc_channel *nvchan, if (!prog) goto out; + /* Ensure that the below memcpy() won't overflow the page buffer. */ + if (len > ndev->mtu + ETH_HLEN) { + act = XDP_DROP; + goto out; + } + /* allocate page buffer for data */ page = alloc_page(GFP_ATOMIC); if (!page) { @@ -44,12 +50,8 @@ u32 netvsc_run_xdp(struct net_device *ndev, struct netvsc_channel *nvchan, goto out; } - xdp->data_hard_start = page_address(page); - xdp->data = xdp->data_hard_start + NETVSC_XDP_HDRM; - xdp_set_data_meta_invalid(xdp); - xdp->data_end = xdp->data + len; - xdp->rxq = &nvchan->xdp_rxq; - xdp->frame_sz = PAGE_SIZE; + xdp_init_buff(xdp, PAGE_SIZE, &nvchan->xdp_rxq); + xdp_prepare_buff(xdp, page_address(page), NETVSC_XDP_HDRM, len, false); memcpy(xdp->data, data, len); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f32f28311d57..ac20c432d4d8 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -539,7 +539,8 @@ static int netvsc_xmit(struct sk_buff *skb, struct net_device *net, bool xdp_tx) */ vf_netdev = rcu_dereference_bh(net_device_ctx->vf_netdev); if (vf_netdev && netif_running(vf_netdev) && - netif_carrier_ok(vf_netdev) && !netpoll_tx_running(net)) + netif_carrier_ok(vf_netdev) && !netpoll_tx_running(net) && + net_device_ctx->data_path_is_vf) return netvsc_vf_xmit(net, vf_netdev, skb); /* We will atmost need two pages to describe the rndis @@ -760,6 +761,16 @@ void netvsc_linkstatus_callback(struct net_device *net, if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) { u32 speed; + /* Validate status_buf_offset */ + if (indicate->status_buflen < sizeof(speed) || + indicate->status_buf_offset < sizeof(*indicate) || + resp->msg_len - RNDIS_HEADER_SIZE < indicate->status_buf_offset || + resp->msg_len - RNDIS_HEADER_SIZE - indicate->status_buf_offset + < indicate->status_buflen) { + netdev_err(net, "invalid rndis_indicate_status packet\n"); + return; + } + speed = *(u32 *)((void *)indicate + indicate->status_buf_offset) / 10000; ndev_ctx->speed = speed; @@ -865,8 +876,14 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net, */ if (csum_info && csum_info->receive.ip_checksum_value_invalid && csum_info->receive.ip_checksum_succeeded && - skb->protocol == htons(ETH_P_IP)) + skb->protocol == htons(ETH_P_IP)) { + /* Check that there is enough space to hold the IP header. */ + if (skb_headlen(skb) < sizeof(struct iphdr)) { + kfree_skb(skb); + return NULL; + } netvsc_comp_ipcsum(skb); + } /* Do L4 checksum offload if enabled and present. */ if (csum_info && (net->features & NETIF_F_RXCSUM)) { @@ -2381,12 +2398,15 @@ static int netvsc_register_vf(struct net_device *vf_netdev) * During hibernation, if a VF NIC driver (e.g. mlx5) preserves the network * interface, there is only the CHANGE event and no UP or DOWN event. */ -static int netvsc_vf_changed(struct net_device *vf_netdev) +static int netvsc_vf_changed(struct net_device *vf_netdev, unsigned long event) { struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; struct net_device *ndev; - bool vf_is_up = netif_running(vf_netdev); + bool vf_is_up = false; + + if (event != NETDEV_GOING_DOWN) + vf_is_up = netif_running(vf_netdev); ndev = get_netvsc_byref(vf_netdev); if (!ndev) @@ -2399,7 +2419,6 @@ static int netvsc_vf_changed(struct net_device *vf_netdev) if (net_device_ctx->data_path_is_vf == vf_is_up) return NOTIFY_OK; - net_device_ctx->data_path_is_vf = vf_is_up; netvsc_switch_datapath(ndev, vf_is_up); netdev_info(ndev, "Data path switched %s VF: %s\n", @@ -2716,7 +2735,8 @@ static int netvsc_netdev_event(struct notifier_block *this, case NETDEV_UP: case NETDEV_DOWN: case NETDEV_CHANGE: - return netvsc_vf_changed(event_dev); + case NETDEV_GOING_DOWN: + return netvsc_vf_changed(event_dev, event); default: return NOTIFY_DONE; } diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 598713c0d5a8..c8534b6619b8 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -131,66 +131,84 @@ static void dump_rndis_message(struct net_device *netdev, { switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: - netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, " - "data offset %u data len %u, # oob %u, " - "oob offset %u, oob len %u, pkt offset %u, " - "pkt len %u\n", - rndis_msg->msg_len, - rndis_msg->msg.pkt.data_offset, - rndis_msg->msg.pkt.data_len, - rndis_msg->msg.pkt.num_oob_data_elements, - rndis_msg->msg.pkt.oob_data_offset, - rndis_msg->msg.pkt.oob_data_len, - rndis_msg->msg.pkt.per_pkt_info_offset, - rndis_msg->msg.pkt.per_pkt_info_len); + if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= sizeof(struct rndis_packet)) { + const struct rndis_packet *pkt = &rndis_msg->msg.pkt; + netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, " + "data offset %u data len %u, # oob %u, " + "oob offset %u, oob len %u, pkt offset %u, " + "pkt len %u\n", + rndis_msg->msg_len, + pkt->data_offset, + pkt->data_len, + pkt->num_oob_data_elements, + pkt->oob_data_offset, + pkt->oob_data_len, + pkt->per_pkt_info_offset, + pkt->per_pkt_info_len); + } break; case RNDIS_MSG_INIT_C: - netdev_dbg(netdev, "RNDIS_MSG_INIT_C " - "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " - "device flags %d, max xfer size 0x%x, max pkts %u, " - "pkt aligned %u)\n", - rndis_msg->msg_len, - rndis_msg->msg.init_complete.req_id, - rndis_msg->msg.init_complete.status, - rndis_msg->msg.init_complete.major_ver, - rndis_msg->msg.init_complete.minor_ver, - rndis_msg->msg.init_complete.dev_flags, - rndis_msg->msg.init_complete.max_xfer_size, - rndis_msg->msg.init_complete. - max_pkt_per_msg, - rndis_msg->msg.init_complete. - pkt_alignment_factor); + if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= + sizeof(struct rndis_initialize_complete)) { + const struct rndis_initialize_complete *init_complete = + &rndis_msg->msg.init_complete; + netdev_dbg(netdev, "RNDIS_MSG_INIT_C " + "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " + "device flags %d, max xfer size 0x%x, max pkts %u, " + "pkt aligned %u)\n", + rndis_msg->msg_len, + init_complete->req_id, + init_complete->status, + init_complete->major_ver, + init_complete->minor_ver, + init_complete->dev_flags, + init_complete->max_xfer_size, + init_complete->max_pkt_per_msg, + init_complete->pkt_alignment_factor); + } break; case RNDIS_MSG_QUERY_C: - netdev_dbg(netdev, "RNDIS_MSG_QUERY_C " - "(len %u, id 0x%x, status 0x%x, buf len %u, " - "buf offset %u)\n", - rndis_msg->msg_len, - rndis_msg->msg.query_complete.req_id, - rndis_msg->msg.query_complete.status, - rndis_msg->msg.query_complete. - info_buflen, - rndis_msg->msg.query_complete. - info_buf_offset); + if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= + sizeof(struct rndis_query_complete)) { + const struct rndis_query_complete *query_complete = + &rndis_msg->msg.query_complete; + netdev_dbg(netdev, "RNDIS_MSG_QUERY_C " + "(len %u, id 0x%x, status 0x%x, buf len %u, " + "buf offset %u)\n", + rndis_msg->msg_len, + query_complete->req_id, + query_complete->status, + query_complete->info_buflen, + query_complete->info_buf_offset); + } break; case RNDIS_MSG_SET_C: - netdev_dbg(netdev, - "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n", - rndis_msg->msg_len, - rndis_msg->msg.set_complete.req_id, - rndis_msg->msg.set_complete.status); + if (rndis_msg->msg_len - RNDIS_HEADER_SIZE + sizeof(struct rndis_set_complete)) { + const struct rndis_set_complete *set_complete = + &rndis_msg->msg.set_complete; + netdev_dbg(netdev, + "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n", + rndis_msg->msg_len, + set_complete->req_id, + set_complete->status); + } break; case RNDIS_MSG_INDICATE: - netdev_dbg(netdev, "RNDIS_MSG_INDICATE " - "(len %u, status 0x%x, buf len %u, buf offset %u)\n", - rndis_msg->msg_len, - rndis_msg->msg.indicate_status.status, - rndis_msg->msg.indicate_status.status_buflen, - rndis_msg->msg.indicate_status.status_buf_offset); + if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= + sizeof(struct rndis_indicate_status)) { + const struct rndis_indicate_status *indicate_status = + &rndis_msg->msg.indicate_status; + netdev_dbg(netdev, "RNDIS_MSG_INDICATE " + "(len %u, status 0x%x, buf len %u, buf offset %u)\n", + rndis_msg->msg_len, + indicate_status->status, + indicate_status->status_buflen, + indicate_status->status_buf_offset); + } break; default: @@ -246,11 +264,20 @@ static void rndis_set_link_state(struct rndis_device *rdev, { u32 link_status; struct rndis_query_complete *query_complete; + u32 msg_len = request->response_msg.msg_len; + + /* Ensure the packet is big enough to access its fields */ + if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete)) + return; query_complete = &request->response_msg.msg.query_complete; if (query_complete->status == RNDIS_STATUS_SUCCESS && - query_complete->info_buflen == sizeof(u32)) { + query_complete->info_buflen >= sizeof(u32) && + query_complete->info_buf_offset >= sizeof(*query_complete) && + msg_len - RNDIS_HEADER_SIZE >= query_complete->info_buf_offset && + msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset + >= query_complete->info_buflen) { memcpy(&link_status, (void *)((unsigned long)query_complete + query_complete->info_buf_offset), sizeof(u32)); rdev->link_state = link_status != 0; @@ -343,7 +370,8 @@ static void rndis_filter_receive_response(struct net_device *ndev, */ static inline void *rndis_get_ppi(struct net_device *ndev, struct rndis_packet *rpkt, - u32 rpkt_len, u32 type, u8 internal) + u32 rpkt_len, u32 type, u8 internal, + u32 ppi_size) { struct rndis_per_packet_info *ppi; int len; @@ -359,7 +387,8 @@ static inline void *rndis_get_ppi(struct net_device *ndev, return NULL; } - if (rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) { + if (rpkt->per_pkt_info_len < sizeof(*ppi) || + rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) { netdev_err(ndev, "Invalid per_pkt_info_len: %u\n", rpkt->per_pkt_info_len); return NULL; @@ -381,8 +410,15 @@ static inline void *rndis_get_ppi(struct net_device *ndev, continue; } - if (ppi->type == type && ppi->internal == internal) + if (ppi->type == type && ppi->internal == internal) { + /* ppi->size should be big enough to hold the returned object. */ + if (ppi->size - ppi->ppi_offset < ppi_size) { + netdev_err(ndev, "Invalid ppi: size %u ppi_offset %u\n", + ppi->size, ppi->ppi_offset); + continue; + } return (void *)((ulong)ppi + ppi->ppi_offset); + } len -= ppi->size; ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size); } @@ -461,13 +497,16 @@ static int rndis_filter_receive_data(struct net_device *ndev, return NVSP_STAT_FAIL; } - vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0); + vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0, sizeof(*vlan)); - csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0); + csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0, + sizeof(*csum_info)); - hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0); + hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0, + sizeof(*hash_info)); - pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1); + pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1, + sizeof(*pktinfo_id)); data = (void *)msg + data_offset; @@ -522,9 +561,6 @@ int rndis_filter_receive(struct net_device *ndev, struct net_device_context *net_device_ctx = netdev_priv(ndev); struct rndis_message *rndis_msg = data; - if (netif_msg_rx_status(net_device_ctx)) - dump_rndis_message(ndev, rndis_msg); - /* Validate incoming rndis_message packet */ if (buflen < RNDIS_HEADER_SIZE || rndis_msg->msg_len < RNDIS_HEADER_SIZE || buflen < rndis_msg->msg_len) { @@ -533,6 +569,9 @@ int rndis_filter_receive(struct net_device *ndev, return NVSP_STAT_FAIL; } + if (netif_msg_rx_status(net_device_ctx)) + dump_rndis_message(ndev, rndis_msg); + switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: return rndis_filter_receive_data(ndev, net_dev, nvchan, @@ -567,6 +606,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 inresult_size = *result_size; struct rndis_query_request *query; struct rndis_query_complete *query_complete; + u32 msg_len; int ret = 0; if (!result) @@ -634,8 +674,19 @@ static int rndis_filter_query_device(struct rndis_device *dev, /* Copy the response back */ query_complete = &request->response_msg.msg.query_complete; + msg_len = request->response_msg.msg_len; + + /* Ensure the packet is big enough to access its fields */ + if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete)) { + ret = -1; + goto cleanup; + } - if (query_complete->info_buflen > inresult_size) { + if (query_complete->info_buflen > inresult_size || + query_complete->info_buf_offset < sizeof(*query_complete) || + msg_len - RNDIS_HEADER_SIZE < query_complete->info_buf_offset || + msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset + < query_complete->info_buflen) { ret = -1; goto cleanup; } diff --git a/drivers/net/ipa/Kconfig b/drivers/net/ipa/Kconfig index 9f0d2a93379c..b68f1289b89e 100644 --- a/drivers/net/ipa/Kconfig +++ b/drivers/net/ipa/Kconfig @@ -1,9 +1,10 @@ config QCOM_IPA tristate "Qualcomm IPA support" - depends on ARCH_QCOM && 64BIT && NET - depends on QCOM_Q6V5_MSS + depends on 64BIT && NET && QCOM_SMEM + depends on ARCH_QCOM || COMPILE_TEST + depends on QCOM_RPROC_COMMON || (QCOM_RPROC_COMMON=n && COMPILE_TEST) + select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_QMI_HELPERS - select QCOM_MDT_LOADER help Choose Y or M here to include support for the Qualcomm IP Accelerator (IPA), a hardware block present in some @@ -11,7 +12,8 @@ config QCOM_IPA that is capable of generic hardware handling of IP packets, including routing, filtering, and NAT. Currently the IPA driver supports only basic transport of network traffic - between the AP and modem, on the Qualcomm SDM845 SoC. + between the AP and modem, on the Qualcomm SDM845 and SC7180 + SoCs. Note that if selected, the selection type must match that of QCOM_Q6V5_COMMON (Y or M). diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c index 14d9a791924b..f79cf3c327c1 100644 --- a/drivers/net/ipa/gsi.c +++ b/drivers/net/ipa/gsi.c @@ -89,9 +89,9 @@ /* Delay period for interrupt moderation (in 32KHz IPA internal timer ticks) */ #define GSI_EVT_RING_INT_MODT (32 * 1) /* 1ms under 32KHz clock */ -#define GSI_CMD_TIMEOUT 5 /* seconds */ +#define GSI_CMD_TIMEOUT 50 /* milliseconds */ -#define GSI_CHANNEL_STOP_RX_RETRIES 10 +#define GSI_CHANNEL_STOP_RETRIES 10 #define GSI_CHANNEL_MODEM_HALT_RETRIES 10 #define GSI_MHI_EVENT_ID_START 10 /* 1st reserved event id */ @@ -220,7 +220,59 @@ static void gsi_irq_teardown(struct gsi *gsi) /* Nothing to do */ } -static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id) +/* Event ring commands are performed one at a time. Their completion + * is signaled by the event ring control GSI interrupt type, which is + * only enabled when we issue an event ring command. Only the event + * ring being operated on has this interrupt enabled. + */ +static void gsi_irq_ev_ctrl_enable(struct gsi *gsi, u32 evt_ring_id) +{ + u32 val = BIT(evt_ring_id); + + /* There's a small chance that a previous command completed + * after the interrupt was disabled, so make sure we have no + * pending interrupts before we enable them. + */ + iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_CLR_OFFSET); + + iowrite32(val, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET); + gsi_irq_type_enable(gsi, GSI_EV_CTRL); +} + +/* Disable event ring control interrupts */ +static void gsi_irq_ev_ctrl_disable(struct gsi *gsi) +{ + gsi_irq_type_disable(gsi, GSI_EV_CTRL); + iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET); +} + +/* Channel commands are performed one at a time. Their completion is + * signaled by the channel control GSI interrupt type, which is only + * enabled when we issue a channel command. Only the channel being + * operated on has this interrupt enabled. + */ +static void gsi_irq_ch_ctrl_enable(struct gsi *gsi, u32 channel_id) +{ + u32 val = BIT(channel_id); + + /* There's a small chance that a previous command completed + * after the interrupt was disabled, so make sure we have no + * pending interrupts before we enable them. + */ + iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_CLR_OFFSET); + + iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET); + gsi_irq_type_enable(gsi, GSI_CH_CTRL); +} + +/* Disable channel control interrupts */ +static void gsi_irq_ch_ctrl_disable(struct gsi *gsi) +{ + gsi_irq_type_disable(gsi, GSI_CH_CTRL); + iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET); +} + +static void gsi_irq_ieob_enable_one(struct gsi *gsi, u32 evt_ring_id) { bool enable_ieob = !gsi->ieob_enabled_bitmap; u32 val; @@ -234,11 +286,11 @@ static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id) gsi_irq_type_enable(gsi, GSI_IEOB); } -static void gsi_irq_ieob_disable(struct gsi *gsi, u32 evt_ring_id) +static void gsi_irq_ieob_disable(struct gsi *gsi, u32 event_mask) { u32 val; - gsi->ieob_enabled_bitmap &= ~BIT(evt_ring_id); + gsi->ieob_enabled_bitmap &= ~event_mask; /* Disable the interrupt type if this was the last enabled channel */ if (!gsi->ieob_enabled_bitmap) @@ -248,6 +300,11 @@ static void gsi_irq_ieob_disable(struct gsi *gsi, u32 evt_ring_id) iowrite32(val, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET); } +static void gsi_irq_ieob_disable_one(struct gsi *gsi, u32 evt_ring_id) +{ + gsi_irq_ieob_disable(gsi, BIT(evt_ring_id)); +} + /* Enable all GSI_interrupt types */ static void gsi_irq_enable(struct gsi *gsi) { @@ -307,11 +364,13 @@ static u32 gsi_ring_index(struct gsi_ring *ring, u32 offset) static bool gsi_command(struct gsi *gsi, u32 reg, u32 val, struct completion *completion) { + unsigned long timeout = msecs_to_jiffies(GSI_CMD_TIMEOUT); + reinit_completion(completion); iowrite32(val, gsi->virt + reg); - return !!wait_for_completion_timeout(completion, GSI_CMD_TIMEOUT * HZ); + return !!wait_for_completion_timeout(completion, timeout); } /* Return the hardware's notion of the current state of an event ring */ @@ -326,41 +385,26 @@ gsi_evt_ring_state(struct gsi *gsi, u32 evt_ring_id) } /* Issue an event ring command and wait for it to complete */ -static void evt_ring_command(struct gsi *gsi, u32 evt_ring_id, - enum gsi_evt_cmd_opcode opcode) +static void gsi_evt_ring_command(struct gsi *gsi, u32 evt_ring_id, + enum gsi_evt_cmd_opcode opcode) { struct gsi_evt_ring *evt_ring = &gsi->evt_ring[evt_ring_id]; struct completion *completion = &evt_ring->completion; struct device *dev = gsi->dev; - bool success; + bool timeout; u32 val; - /* We only perform one event ring command at a time, and event - * control interrupts should only occur when such a command - * is issued here. Only permit *this* event ring to trigger - * an interrupt, and only enable the event control IRQ type - * when we expect it to occur. - * - * There's a small chance that a previous command completed - * after the interrupt was disabled, so make sure we have no - * pending interrupts before we enable them. - */ - iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_CLR_OFFSET); - - val = BIT(evt_ring_id); - iowrite32(val, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET); - gsi_irq_type_enable(gsi, GSI_EV_CTRL); + /* Enable the completion interrupt for the command */ + gsi_irq_ev_ctrl_enable(gsi, evt_ring_id); val = u32_encode_bits(evt_ring_id, EV_CHID_FMASK); val |= u32_encode_bits(opcode, EV_OPCODE_FMASK); - success = gsi_command(gsi, GSI_EV_CH_CMD_OFFSET, val, completion); + timeout = !gsi_command(gsi, GSI_EV_CH_CMD_OFFSET, val, completion); - /* Disable the interrupt again */ - gsi_irq_type_disable(gsi, GSI_EV_CTRL); - iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET); + gsi_irq_ev_ctrl_disable(gsi); - if (success) + if (!timeout) return; dev_err(dev, "GSI command %u for event ring %u timed out, state %u\n", @@ -380,7 +424,7 @@ static int gsi_evt_ring_alloc_command(struct gsi *gsi, u32 evt_ring_id) return -EINVAL; } - evt_ring_command(gsi, evt_ring_id, GSI_EVT_ALLOCATE); + gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_ALLOCATE); /* If successful the event ring state will have changed */ if (evt_ring->state == GSI_EVT_RING_STATE_ALLOCATED) @@ -405,7 +449,7 @@ static void gsi_evt_ring_reset_command(struct gsi *gsi, u32 evt_ring_id) return; } - evt_ring_command(gsi, evt_ring_id, GSI_EVT_RESET); + gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_RESET); /* If successful the event ring state will have changed */ if (evt_ring->state == GSI_EVT_RING_STATE_ALLOCATED) @@ -426,7 +470,7 @@ static void gsi_evt_ring_de_alloc_command(struct gsi *gsi, u32 evt_ring_id) return; } - evt_ring_command(gsi, evt_ring_id, GSI_EVT_DE_ALLOC); + gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_DE_ALLOC); /* If successful the event ring state will have changed */ if (evt_ring->state == GSI_EVT_RING_STATE_NOT_ALLOCATED) @@ -456,34 +500,19 @@ gsi_channel_command(struct gsi_channel *channel, enum gsi_ch_cmd_opcode opcode) u32 channel_id = gsi_channel_id(channel); struct gsi *gsi = channel->gsi; struct device *dev = gsi->dev; - bool success; + bool timeout; u32 val; - /* We only perform one channel command at a time, and channel - * control interrupts should only occur when such a command is - * issued here. So we only permit *this* channel to trigger - * an interrupt and only enable the channel control IRQ type - * when we expect it to occur. - * - * There's a small chance that a previous command completed - * after the interrupt was disabled, so make sure we have no - * pending interrupts before we enable them. - */ - iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_CLR_OFFSET); - - val = BIT(channel_id); - iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET); - gsi_irq_type_enable(gsi, GSI_CH_CTRL); + /* Enable the completion interrupt for the command */ + gsi_irq_ch_ctrl_enable(gsi, channel_id); val = u32_encode_bits(channel_id, CH_CHID_FMASK); val |= u32_encode_bits(opcode, CH_OPCODE_FMASK); - success = gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion); + timeout = !gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion); - /* Disable the interrupt again */ - gsi_irq_type_disable(gsi, GSI_CH_CTRL); - iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET); + gsi_irq_ch_ctrl_disable(gsi); - if (success) + if (!timeout) return; dev_err(dev, "GSI command %u for channel %u timed out, state %u\n", @@ -589,7 +618,8 @@ static void gsi_channel_reset_command(struct gsi_channel *channel) struct device *dev = channel->gsi->dev; enum gsi_channel_state state; - msleep(1); /* A short delay is required before a RESET command */ + /* A short delay is required before a RESET command */ + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); state = gsi_channel_state(channel); if (state != GSI_CHANNEL_STATE_STOPPED && @@ -741,13 +771,13 @@ static void gsi_channel_freeze(struct gsi_channel *channel) napi_disable(&channel->napi); - gsi_irq_ieob_disable(channel->gsi, channel->evt_ring_id); + gsi_irq_ieob_disable_one(channel->gsi, channel->evt_ring_id); } /* Allow transactions to be used on the channel again. */ static void gsi_channel_thaw(struct gsi_channel *channel) { - gsi_irq_ieob_enable(channel->gsi, channel->evt_ring_id); + gsi_irq_ieob_enable_one(channel->gsi, channel->evt_ring_id); napi_enable(&channel->napi); } @@ -864,21 +894,18 @@ int gsi_channel_start(struct gsi *gsi, u32 channel_id) int gsi_channel_stop(struct gsi *gsi, u32 channel_id) { struct gsi_channel *channel = &gsi->channel[channel_id]; - u32 retries; + u32 retries = GSI_CHANNEL_STOP_RETRIES; int ret; gsi_channel_freeze(channel); - /* RX channels might require a little time to enter STOPPED state */ - retries = channel->toward_ipa ? 0 : GSI_CHANNEL_STOP_RX_RETRIES; - mutex_lock(&gsi->mutex); do { ret = gsi_channel_stop_command(channel); if (ret != -EAGAIN) break; - msleep(1); + usleep_range(3 * USEC_PER_MSEC, 5 * USEC_PER_MSEC); } while (retries--); mutex_unlock(&gsi->mutex); @@ -1178,6 +1205,7 @@ static void gsi_isr_ieob(struct gsi *gsi) u32 event_mask; event_mask = ioread32(gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_OFFSET); + gsi_irq_ieob_disable(gsi, event_mask); iowrite32(event_mask, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_CLR_OFFSET); while (event_mask) { @@ -1185,7 +1213,6 @@ static void gsi_isr_ieob(struct gsi *gsi) event_mask ^= BIT(evt_ring_id); - gsi_irq_ieob_disable(gsi, evt_ring_id); napi_schedule(&gsi->evt_ring[evt_ring_id].channel->napi); } } @@ -1430,7 +1457,7 @@ void gsi_channel_doorbell(struct gsi_channel *channel) } /* Consult hardware, move any newly completed transactions to completed list */ -static void gsi_channel_update(struct gsi_channel *channel) +static struct gsi_trans *gsi_channel_update(struct gsi_channel *channel) { u32 evt_ring_id = channel->evt_ring_id; struct gsi *gsi = channel->gsi; @@ -1449,7 +1476,7 @@ static void gsi_channel_update(struct gsi_channel *channel) offset = GSI_EV_CH_E_CNTXT_4_OFFSET(evt_ring_id); index = gsi_ring_index(ring, ioread32(gsi->virt + offset)); if (index == ring->index % ring->count) - return; + return NULL; /* Get the transaction for the latest completed event. Take a * reference to keep it from completing before we give the events @@ -1474,6 +1501,8 @@ static void gsi_channel_update(struct gsi_channel *channel) gsi_evt_ring_doorbell(channel->gsi, channel->evt_ring_id, index); gsi_trans_free(trans); + + return gsi_channel_trans_complete(channel); } /** @@ -1494,11 +1523,8 @@ static struct gsi_trans *gsi_channel_poll_one(struct gsi_channel *channel) /* Get the first transaction from the completed list */ trans = gsi_channel_trans_complete(channel); - if (!trans) { - /* List is empty; see if there's more to do */ - gsi_channel_update(channel); - trans = gsi_channel_trans_complete(channel); - } + if (!trans) /* List is empty; see if there's more to do */ + trans = gsi_channel_update(channel); if (trans) gsi_trans_move_polled(trans); @@ -1521,23 +1547,20 @@ static struct gsi_trans *gsi_channel_poll_one(struct gsi_channel *channel) static int gsi_channel_poll(struct napi_struct *napi, int budget) { struct gsi_channel *channel; - int count = 0; + int count; channel = container_of(napi, struct gsi_channel, napi); - while (count < budget) { + for (count = 0; count < budget; count++) { struct gsi_trans *trans; - count++; trans = gsi_channel_poll_one(channel); if (!trans) break; gsi_trans_complete(trans); } - if (count < budget) { - napi_complete(&channel->napi); - gsi_irq_ieob_enable(channel->gsi, channel->evt_ring_id); - } + if (count < budget && napi_complete(napi)) + gsi_irq_ieob_enable_one(channel->gsi, channel->evt_ring_id); return count; } @@ -1627,7 +1650,7 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id, enum gsi_generic_cmd_opcode opcode) { struct completion *completion = &gsi->completion; - bool success; + bool timeout; u32 val; /* The error global interrupt type is always enabled (until we @@ -1650,12 +1673,12 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id, val |= u32_encode_bits(channel_id, GENERIC_CHID_FMASK); val |= u32_encode_bits(GSI_EE_MODEM, GENERIC_EE_FMASK); - success = gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion); + timeout = !gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion); /* Disable the GP_INT1 IRQ type again */ iowrite32(BIT(ERROR_INT), gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET); - if (success) + if (!timeout) return gsi->result; dev_err(gsi->dev, "GSI generic command %u to channel %u timed out\n", diff --git a/drivers/net/ipa/gsi_trans.h b/drivers/net/ipa/gsi_trans.h index 4d4606b5fa95..3a4ab8a94d82 100644 --- a/drivers/net/ipa/gsi_trans.h +++ b/drivers/net/ipa/gsi_trans.h @@ -13,6 +13,7 @@ #include "ipa_cmd.h" +struct page; struct scatterlist; struct device; struct sk_buff; diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index 6c2371084c55..c6c6a7f6909c 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -43,7 +43,6 @@ enum ipa_flag { * @flags: Boolean state flags * @version: IPA hardware version * @pdev: Platform device - * @modem_rproc: Remoteproc handle for modem subsystem * @smp2p: SMP2P information * @clock: IPA clocking information * @table_addr: DMA address of filter/route table content @@ -83,7 +82,6 @@ struct ipa { DECLARE_BITMAP(flags, IPA_FLAG_COUNT); enum ipa_version version; struct platform_device *pdev; - struct rproc *modem_rproc; struct notifier_block nb; void *notifier; struct ipa_smp2p *smp2p; diff --git a/drivers/net/ipa/ipa_clock.c b/drivers/net/ipa/ipa_clock.c index 135c393437f1..354675a643db 100644 --- a/drivers/net/ipa/ipa_clock.c +++ b/drivers/net/ipa/ipa_clock.c @@ -31,142 +31,154 @@ */ /** + * struct ipa_interconnect - IPA interconnect information + * @path: Interconnect path + * @average_bandwidth: Average interconnect bandwidth (KB/second) + * @peak_bandwidth: Peak interconnect bandwidth (KB/second) + */ +struct ipa_interconnect { + struct icc_path *path; + u32 average_bandwidth; + u32 peak_bandwidth; +}; + +/** * struct ipa_clock - IPA clocking information * @count: Clocking reference count * @mutex: Protects clock enable/disable * @core: IPA core clock - * @memory_path: Memory interconnect - * @imem_path: Internal memory interconnect - * @config_path: Configuration space interconnect - * @interconnect_data: Interconnect configuration data + * @interconnect_count: Number of elements in interconnect[] + * @interconnect: Interconnect array */ struct ipa_clock { refcount_t count; struct mutex mutex; /* protects clock enable/disable */ struct clk *core; - struct icc_path *memory_path; - struct icc_path *imem_path; - struct icc_path *config_path; - const struct ipa_interconnect_data *interconnect_data; + u32 interconnect_count; + struct ipa_interconnect *interconnect; }; -static struct icc_path * -ipa_interconnect_init_one(struct device *dev, const char *name) +static int ipa_interconnect_init_one(struct device *dev, + struct ipa_interconnect *interconnect, + const struct ipa_interconnect_data *data) { struct icc_path *path; - path = of_icc_get(dev, name); - if (IS_ERR(path)) - dev_err(dev, "error %ld getting %s interconnect\n", - PTR_ERR(path), name); + path = of_icc_get(dev, data->name); + if (IS_ERR(path)) { + int ret = PTR_ERR(path); + + dev_err(dev, "error %d getting %s interconnect\n", ret, + data->name); - return path; + return ret; + } + + interconnect->path = path; + interconnect->average_bandwidth = data->average_bandwidth; + interconnect->peak_bandwidth = data->peak_bandwidth; + + return 0; } -/* Initialize interconnects required for IPA operation */ -static int ipa_interconnect_init(struct ipa_clock *clock, struct device *dev) +static void ipa_interconnect_exit_one(struct ipa_interconnect *interconnect) { - struct icc_path *path; - - path = ipa_interconnect_init_one(dev, "memory"); - if (IS_ERR(path)) - goto err_return; - clock->memory_path = path; + icc_put(interconnect->path); + memset(interconnect, 0, sizeof(*interconnect)); +} - path = ipa_interconnect_init_one(dev, "imem"); - if (IS_ERR(path)) - goto err_memory_path_put; - clock->imem_path = path; +/* Initialize interconnects required for IPA operation */ +static int ipa_interconnect_init(struct ipa_clock *clock, struct device *dev, + const struct ipa_interconnect_data *data) +{ + struct ipa_interconnect *interconnect; + u32 count; + int ret; - path = ipa_interconnect_init_one(dev, "config"); - if (IS_ERR(path)) - goto err_imem_path_put; - clock->config_path = path; + count = clock->interconnect_count; + interconnect = kcalloc(count, sizeof(*interconnect), GFP_KERNEL); + if (!interconnect) + return -ENOMEM; + clock->interconnect = interconnect; + + while (count--) { + ret = ipa_interconnect_init_one(dev, interconnect, data++); + if (ret) + goto out_unwind; + interconnect++; + } return 0; -err_imem_path_put: - icc_put(clock->imem_path); -err_memory_path_put: - icc_put(clock->memory_path); -err_return: - return PTR_ERR(path); +out_unwind: + while (interconnect-- > clock->interconnect) + ipa_interconnect_exit_one(interconnect); + kfree(clock->interconnect); + clock->interconnect = NULL; + + return ret; } /* Inverse of ipa_interconnect_init() */ static void ipa_interconnect_exit(struct ipa_clock *clock) { - icc_put(clock->config_path); - icc_put(clock->imem_path); - icc_put(clock->memory_path); + struct ipa_interconnect *interconnect; + + interconnect = clock->interconnect + clock->interconnect_count; + while (interconnect-- > clock->interconnect) + ipa_interconnect_exit_one(interconnect); + kfree(clock->interconnect); + clock->interconnect = NULL; } /* Currently we only use one bandwidth level, so just "enable" interconnects */ static int ipa_interconnect_enable(struct ipa *ipa) { - const struct ipa_interconnect_data *data; + struct ipa_interconnect *interconnect; struct ipa_clock *clock = ipa->clock; int ret; - - data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY]; - ret = icc_set_bw(clock->memory_path, data->average_rate, - data->peak_rate); - if (ret) - return ret; - - data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM]; - ret = icc_set_bw(clock->imem_path, data->average_rate, - data->peak_rate); - if (ret) - goto err_memory_path_disable; - - data = &clock->interconnect_data[IPA_INTERCONNECT_CONFIG]; - ret = icc_set_bw(clock->config_path, data->average_rate, - data->peak_rate); - if (ret) - goto err_imem_path_disable; + u32 i; + + interconnect = clock->interconnect; + for (i = 0; i < clock->interconnect_count; i++) { + ret = icc_set_bw(interconnect->path, + interconnect->average_bandwidth, + interconnect->peak_bandwidth); + if (ret) + goto out_unwind; + interconnect++; + } return 0; -err_imem_path_disable: - (void)icc_set_bw(clock->imem_path, 0, 0); -err_memory_path_disable: - (void)icc_set_bw(clock->memory_path, 0, 0); +out_unwind: + while (interconnect-- > clock->interconnect) + (void)icc_set_bw(interconnect->path, 0, 0); return ret; } /* To disable an interconnect, we just its bandwidth to 0 */ -static int ipa_interconnect_disable(struct ipa *ipa) +static void ipa_interconnect_disable(struct ipa *ipa) { - const struct ipa_interconnect_data *data; + struct ipa_interconnect *interconnect; struct ipa_clock *clock = ipa->clock; + int result = 0; + u32 count; int ret; - ret = icc_set_bw(clock->memory_path, 0, 0); - if (ret) - return ret; - - ret = icc_set_bw(clock->imem_path, 0, 0); - if (ret) - goto err_memory_path_reenable; - - ret = icc_set_bw(clock->config_path, 0, 0); - if (ret) - goto err_imem_path_reenable; - - return 0; - -err_imem_path_reenable: - data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM]; - (void)icc_set_bw(clock->imem_path, data->average_rate, - data->peak_rate); -err_memory_path_reenable: - data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY]; - (void)icc_set_bw(clock->memory_path, data->average_rate, - data->peak_rate); + count = clock->interconnect_count; + interconnect = clock->interconnect + count; + while (count--) { + interconnect--; + ret = icc_set_bw(interconnect->path, 0, 0); + if (ret && !result) + result = ret; + } - return ret; + if (result) + dev_err(&ipa->pdev->dev, + "error %d disabling IPA interconnects\n", ret); } /* Turn on IPA clocks, including interconnects */ @@ -189,7 +201,7 @@ static int ipa_clock_enable(struct ipa *ipa) static void ipa_clock_disable(struct ipa *ipa) { clk_disable_unprepare(ipa->clock->core); - (void)ipa_interconnect_disable(ipa); + ipa_interconnect_disable(ipa); } /* Get an IPA clock reference, but only if the reference count is @@ -286,9 +298,9 @@ ipa_clock_init(struct device *dev, const struct ipa_clock_data *data) goto err_clk_put; } clock->core = clk; - clock->interconnect_data = data->interconnect; + clock->interconnect_count = data->interconnect_count; - ret = ipa_interconnect_init(clock, dev); + ret = ipa_interconnect_init(clock, dev, data->interconnect_data); if (ret) goto err_kfree; diff --git a/drivers/net/ipa/ipa_data-sc7180.c b/drivers/net/ipa/ipa_data-sc7180.c index 5cc0ed77edb9..997b51ceb7d7 100644 --- a/drivers/net/ipa/ipa_data-sc7180.c +++ b/drivers/net/ipa/ipa_data-sc7180.c @@ -309,24 +309,30 @@ static struct ipa_mem_data ipa_mem_data = { .smem_size = 0x00002000, }; +/* Interconnect bandwidths are in 1000 byte/second units */ +static struct ipa_interconnect_data ipa_interconnect_data[] = { + { + .name = "memory", + .peak_bandwidth = 465000, /* 465 MBps */ + .average_bandwidth = 80000, /* 80 MBps */ + }, + /* Average bandwidth is unused for the next two interconnects */ + { + .name = "imem", + .peak_bandwidth = 68570, /* 68.570 MBps */ + .average_bandwidth = 0, /* unused */ + }, + { + .name = "config", + .peak_bandwidth = 30000, /* 30 MBps */ + .average_bandwidth = 0, /* unused */ + }, +}; + static struct ipa_clock_data ipa_clock_data = { .core_clock_rate = 100 * 1000 * 1000, /* Hz */ - /* Interconnect rates are in 1000 byte/second units */ - .interconnect = { - [IPA_INTERCONNECT_MEMORY] = { - .peak_rate = 465000, /* 465 MBps */ - .average_rate = 80000, /* 80 MBps */ - }, - /* Average rate is unused for the next two interconnects */ - [IPA_INTERCONNECT_IMEM] = { - .peak_rate = 68570, /* 68.570 MBps */ - .average_rate = 0, /* unused */ - }, - [IPA_INTERCONNECT_CONFIG] = { - .peak_rate = 30000, /* 30 MBps */ - .average_rate = 0, /* unused */ - }, - }, + .interconnect_count = ARRAY_SIZE(ipa_interconnect_data), + .interconnect_data = ipa_interconnect_data, }; /* Configuration data for the SC7180 SoC. */ diff --git a/drivers/net/ipa/ipa_data-sdm845.c b/drivers/net/ipa/ipa_data-sdm845.c index f8fee8d3ca42..88c9c3562ab7 100644 --- a/drivers/net/ipa/ipa_data-sdm845.c +++ b/drivers/net/ipa/ipa_data-sdm845.c @@ -329,24 +329,30 @@ static struct ipa_mem_data ipa_mem_data = { .smem_size = 0x00002000, }; +/* Interconnect bandwidths are in 1000 byte/second units */ +static struct ipa_interconnect_data ipa_interconnect_data[] = { + { + .name = "memory", + .peak_bandwidth = 600000, /* 600 MBps */ + .average_bandwidth = 80000, /* 80 MBps */ + }, + /* Average bandwidth is unused for the next two interconnects */ + { + .name = "imem", + .peak_bandwidth = 350000, /* 350 MBps */ + .average_bandwidth = 0, /* unused */ + }, + { + .name = "config", + .peak_bandwidth = 40000, /* 40 MBps */ + .average_bandwidth = 0, /* unused */ + }, +}; + static struct ipa_clock_data ipa_clock_data = { .core_clock_rate = 75 * 1000 * 1000, /* Hz */ - /* Interconnect rates are in 1000 byte/second units */ - .interconnect = { - [IPA_INTERCONNECT_MEMORY] = { - .peak_rate = 600000, /* 600 MBps */ - .average_rate = 80000, /* 80 MBps */ - }, - /* Average rate is unused for the next two interconnects */ - [IPA_INTERCONNECT_IMEM] = { - .peak_rate = 350000, /* 350 MBps */ - .average_rate = 0, /* unused */ - }, - [IPA_INTERCONNECT_CONFIG] = { - .peak_rate = 40000, /* 40 MBps */ - .average_rate = 0, /* unused */ - }, - }, + .interconnect_count = ARRAY_SIZE(ipa_interconnect_data), + .interconnect_data = ipa_interconnect_data, }; /* Configuration data for the SDM845 SoC. */ diff --git a/drivers/net/ipa/ipa_data.h b/drivers/net/ipa/ipa_data.h index 0ed5ffe2b8da..b476fc373f7f 100644 --- a/drivers/net/ipa/ipa_data.h +++ b/drivers/net/ipa/ipa_data.h @@ -258,32 +258,28 @@ struct ipa_mem_data { u32 smem_size; }; -/** enum ipa_interconnect_id - IPA interconnect identifier */ -enum ipa_interconnect_id { - IPA_INTERCONNECT_MEMORY, - IPA_INTERCONNECT_IMEM, - IPA_INTERCONNECT_CONFIG, - IPA_INTERCONNECT_COUNT, /* Last; not an interconnect */ -}; - /** - * struct ipa_interconnect_data - description of IPA interconnect rates - * @peak_rate: Peak interconnect bandwidth (in 1000 byte/sec units) - * @average_rate: Average interconnect bandwidth (in 1000 byte/sec units) + * struct ipa_interconnect_data - description of IPA interconnect bandwidths + * @name: Interconnect name (matches interconnect-name in DT) + * @peak_bandwidth: Peak interconnect bandwidth (in 1000 byte/sec units) + * @average_bandwidth: Average interconnect bandwidth (in 1000 byte/sec units) */ struct ipa_interconnect_data { - u32 peak_rate; - u32 average_rate; + const char *name; + u32 peak_bandwidth; + u32 average_bandwidth; }; /** * struct ipa_clock_data - description of IPA clock and interconnect rates * @core_clock_rate: Core clock rate (Hz) - * @interconnect: Array of interconnect bandwidth parameters + * @interconnect_count: Number of entries in the interconnect_data array + * @interconnect_data: IPA interconnect configuration data */ struct ipa_clock_data { u32 core_clock_rate; - struct ipa_interconnect_data interconnect[IPA_INTERCONNECT_COUNT]; + u32 interconnect_count; /* # entries in interconnect_data[] */ + const struct ipa_interconnect_data *interconnect_data; }; /** diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 9f4be9812a1f..688a3dd40510 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -1378,7 +1378,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint) do { if (!ipa_endpoint_aggr_active(endpoint)) break; - msleep(1); + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); } while (retries--); /* Check one last time */ @@ -1399,7 +1399,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint) */ gsi_channel_reset(gsi, endpoint->channel_id, true); - msleep(1); + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); goto out_suspend_again; diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 84bb8ae92725..ab0fd5cb4927 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -15,7 +15,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_address.h> -#include <linux/remoteproc.h> #include <linux/qcom_scm.h> #include <linux/soc/qcom/mdt_loader.h> @@ -729,19 +728,6 @@ static const struct of_device_id ipa_match[] = { }; MODULE_DEVICE_TABLE(of, ipa_match); -static phandle of_property_read_phandle(const struct device_node *np, - const char *name) -{ - struct property *prop; - int len = 0; - - prop = of_find_property(np, name, &len); - if (!prop || len != sizeof(__be32)) - return 0; - - return be32_to_cpup(prop->value); -} - /* Check things that can be validated at build time. This just * groups these things BUILD_BUG_ON() calls don't clutter the rest * of the code. @@ -807,10 +793,8 @@ static int ipa_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct ipa_data *data; struct ipa_clock *clock; - struct rproc *rproc; bool modem_init; struct ipa *ipa; - phandle ph; int ret; ipa_validate_build(); @@ -829,25 +813,12 @@ static int ipa_probe(struct platform_device *pdev) if (!qcom_scm_is_available()) return -EPROBE_DEFER; - /* We rely on remoteproc to tell us about modem state changes */ - ph = of_property_read_phandle(dev->of_node, "modem-remoteproc"); - if (!ph) { - dev_err(dev, "DT missing \"modem-remoteproc\" property\n"); - return -EINVAL; - } - - rproc = rproc_get_by_phandle(ph); - if (!rproc) - return -EPROBE_DEFER; - /* The clock and interconnects might not be ready when we're * probed, so might return -EPROBE_DEFER. */ clock = ipa_clock_init(dev, data->clock_data); - if (IS_ERR(clock)) { - ret = PTR_ERR(clock); - goto err_rproc_put; - } + if (IS_ERR(clock)) + return PTR_ERR(clock); /* No more EPROBE_DEFER. Allocate and initialize the IPA structure */ ipa = kzalloc(sizeof(*ipa), GFP_KERNEL); @@ -858,7 +829,6 @@ static int ipa_probe(struct platform_device *pdev) ipa->pdev = pdev; dev_set_drvdata(dev, ipa); - ipa->modem_rproc = rproc; ipa->clock = clock; ipa->version = data->version; @@ -935,8 +905,6 @@ err_kfree_ipa: kfree(ipa); err_clock_exit: ipa_clock_exit(clock); -err_rproc_put: - rproc_put(rproc); return ret; } @@ -944,7 +912,6 @@ err_rproc_put: static int ipa_remove(struct platform_device *pdev) { struct ipa *ipa = dev_get_drvdata(&pdev->dev); - struct rproc *rproc = ipa->modem_rproc; struct ipa_clock *clock = ipa->clock; int ret; @@ -970,7 +937,6 @@ static int ipa_remove(struct platform_device *pdev) ipa_reg_exit(ipa); kfree(ipa); ipa_clock_exit(clock); - rproc_put(rproc); return 0; } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index fb51329f8964..9a9a5cf36a4b 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1385,7 +1385,7 @@ static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode, return ret; } - if (!data || !data[IFLA_MACVLAN_MACADDR_DATA]) + if (!data[IFLA_MACVLAN_MACADDR_DATA]) return 0; head = nla_data(data[IFLA_MACVLAN_MACADDR_DATA]); diff --git a/drivers/net/mhi_net.c b/drivers/net/mhi_net.c index fa41d8c42f05..a5a214d29849 100644 --- a/drivers/net/mhi_net.c +++ b/drivers/net/mhi_net.c @@ -122,7 +122,7 @@ static const struct net_device_ops mhi_netdev_ops = { static void mhi_net_setup(struct net_device *ndev) { ndev->header_ops = NULL; /* No header */ - ndev->type = ARPHRD_NONE; /* QMAP... */ + ndev->type = ARPHRD_RAWIP; ndev->hard_header_len = 0; ndev->addr_len = 0; ndev->flags = IFF_POINTOPOINT | IFF_NOARP; @@ -158,7 +158,18 @@ static void mhi_net_dl_callback(struct mhi_device *mhi_dev, u64_stats_add(&mhi_netdev->stats.rx_bytes, mhi_res->bytes_xferd); u64_stats_update_end(&mhi_netdev->stats.rx_syncp); - skb->protocol = htons(ETH_P_MAP); + switch (skb->data[0] & 0xf0) { + case 0x40: + skb->protocol = htons(ETH_P_IP); + break; + case 0x60: + skb->protocol = htons(ETH_P_IPV6); + break; + default: + skb->protocol = htons(ETH_P_MAP); + break; + } + skb_put(skb, mhi_res->bytes_xferd); netif_rx(skb); } @@ -237,6 +248,10 @@ static void mhi_net_rx_refill_work(struct work_struct *work) schedule_delayed_work(&mhi_netdev->rx_refill, HZ / 2); } +static struct device_type wwan_type = { + .name = "wwan", +}; + static int mhi_net_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) { @@ -256,6 +271,7 @@ static int mhi_net_probe(struct mhi_device *mhi_dev, mhi_netdev->ndev = ndev; mhi_netdev->mdev = mhi_dev; SET_NETDEV_DEV(ndev, &mhi_dev->dev); + SET_NETDEV_DEVTYPE(ndev, &wwan_type); /* All MHI net channels have 128 ring elements (at least for now) */ mhi_netdev->rx_queue_sz = 128; diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 7178468302c8..aec92440eef1 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -258,8 +258,6 @@ static const struct net_device_ops nsim_netdev_ops = { .ndo_setup_tc = nsim_setup_tc, .ndo_set_features = nsim_set_features, .ndo_bpf = nsim_bpf, - .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, - .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_get_devlink_port = nsim_get_devlink_port, }; diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index d0b36fd6c265..d67bddc111e3 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -132,6 +132,11 @@ #define AT803X_MIN_DOWNSHIFT 2 #define AT803X_MAX_DOWNSHIFT 9 +#define AT803X_MMD3_SMARTEEE_CTL1 0x805b +#define AT803X_MMD3_SMARTEEE_CTL2 0x805c +#define AT803X_MMD3_SMARTEEE_CTL3 0x805d +#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) + #define ATH9331_PHY_ID 0x004dd041 #define ATH8030_PHY_ID 0x004dd076 #define ATH8031_PHY_ID 0x004dd074 @@ -146,8 +151,11 @@ MODULE_LICENSE("GPL"); struct at803x_priv { int flags; #define AT803X_KEEP_PLL_ENABLED BIT(0) /* don't turn off internal PLL */ +#define AT803X_DISABLE_SMARTEEE BIT(1) u16 clk_25m_reg; u16 clk_25m_mask; + u8 smarteee_lpi_tw_1g; + u8 smarteee_lpi_tw_100m; struct regulator_dev *vddio_rdev; struct regulator_dev *vddh_rdev; struct regulator *vddio; @@ -411,13 +419,32 @@ static int at803x_parse_dt(struct phy_device *phydev) { struct device_node *node = phydev->mdio.dev.of_node; struct at803x_priv *priv = phydev->priv; - u32 freq, strength; + u32 freq, strength, tw; unsigned int sel; int ret; if (!IS_ENABLED(CONFIG_OF_MDIO)) return 0; + if (of_property_read_bool(node, "qca,disable-smarteee")) + priv->flags |= AT803X_DISABLE_SMARTEEE; + + if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) { + if (!tw || tw > 255) { + phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n"); + return -EINVAL; + } + priv->smarteee_lpi_tw_1g = tw; + } + + if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) { + if (!tw || tw > 255) { + phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n"); + return -EINVAL; + } + priv->smarteee_lpi_tw_100m = tw; + } + ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); if (!ret) { switch (freq) { @@ -526,22 +553,47 @@ static void at803x_remove(struct phy_device *phydev) regulator_disable(priv->vddio); } -static int at803x_clk_out_config(struct phy_device *phydev) +static int at803x_smarteee_config(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; - int val; + u16 mask = 0, val = 0; + int ret; - if (!priv->clk_25m_mask) + if (priv->flags & AT803X_DISABLE_SMARTEEE) + return phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_MMD3_SMARTEEE_CTL3, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); + + if (priv->smarteee_lpi_tw_1g) { + mask |= 0xff00; + val |= priv->smarteee_lpi_tw_1g << 8; + } + if (priv->smarteee_lpi_tw_100m) { + mask |= 0x00ff; + val |= priv->smarteee_lpi_tw_100m; + } + if (!mask) return 0; - val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M); - if (val < 0) - return val; + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, + mask, val); + if (ret) + return ret; - val &= ~priv->clk_25m_mask; - val |= priv->clk_25m_reg; + return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN); +} + +static int at803x_clk_out_config(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; - return phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val); + if (!priv->clk_25m_mask) + return 0; + + return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, + priv->clk_25m_mask, priv->clk_25m_reg); } static int at8031_pll_config(struct phy_device *phydev) @@ -584,6 +636,10 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; + ret = at803x_smarteee_config(phydev); + if (ret < 0) + return ret; + ret = at803x_clk_out_config(phydev); if (ret < 0) return ret; @@ -594,7 +650,13 @@ static int at803x_config_init(struct phy_device *phydev) return ret; } - return 0; + /* Ar803x extended next page bit is enabled by default. Cisco + * multigig switches read this bit and attempt to negotiate 10Gbps + * rates even if the next page bit is disabled. This is incorrect + * behaviour but we still need to accommodate it. XNP is only needed + * for 10Gbps support, so disable XNP. + */ + return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); } static int at803x_ack_interrupt(struct phy_device *phydev) diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 15812001b3ff..e79297a4bae8 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -612,6 +612,7 @@ static void bcm7xxx_28nm_remove(struct phy_device *phydev) static struct phy_driver bcm7xxx_driver[] = { BCM7XXX_28NM_EPHY(PHY_ID_BCM72113, "Broadcom BCM72113"), + BCM7XXX_28NM_EPHY(PHY_ID_BCM72116, "Broadcom BCM72116"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255"), BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"), @@ -633,6 +634,7 @@ static struct phy_driver bcm7xxx_driver[] = { static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { { PHY_ID_BCM72113, 0xfffffff0 }, + { PHY_ID_BCM72116, 0xfffffff0, }, { PHY_ID_BCM7250, 0xfffffff0, }, { PHY_ID_BCM7255, 0xfffffff0, }, { PHY_ID_BCM7260, 0xfffffff0, }, diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 2b42e46066b4..040509b81f02 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -740,7 +740,7 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) { int retval; - WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock)); + lockdep_assert_held_once(&bus->mdio_lock); retval = bus->read(bus, addr, regnum); @@ -766,7 +766,7 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) { int err; - WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock)); + lockdep_assert_held_once(&bus->mdio_lock); err = bus->write(bus, addr, regnum, val); diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 54e0d75203da..39c7c786a912 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -1389,7 +1389,7 @@ static struct phy_driver ksphy_driver[] = { }, { .phy_id = PHY_ID_KSZ886X, .phy_id_mask = MICREL_PHY_ID_MASK, - .name = "Micrel KSZ886X Switch", + .name = "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch", /* PHY_BASIC_FEATURES */ .config_init = kszphy_config_init, .suspend = genphy_suspend, diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c index 5a8c8eb18582..46160baaafe3 100644 --- a/drivers/net/phy/national.c +++ b/drivers/net/phy/national.c @@ -19,8 +19,6 @@ #include <linux/phy.h> #include <linux/netdevice.h> -#define DEBUG - /* DP83865 phy identifier values */ #define DP83865_PHY_ID 0x20005c7a diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 45f75533c47c..9cb7e4dbf8f4 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -724,7 +724,7 @@ static int phy_check_link_status(struct phy_device *phydev) { int err; - WARN_ON(!mutex_is_locked(&phydev->lock)); + lockdep_assert_held(&phydev->lock); /* Keep previous state if loopback is enabled because some PHYs * report that Link is Down when loopback is enabled. diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 80c2e646c093..8447e56ba572 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1740,7 +1740,7 @@ int __phy_resume(struct phy_device *phydev) struct phy_driver *phydrv = phydev->drv; int ret; - WARN_ON(!mutex_is_locked(&phydev->lock)); + lockdep_assert_held(&phydev->lock); if (!phydrv || !phydrv->resume) return 0; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 99ecd6c4c15a..821e85a97367 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -60,6 +60,9 @@ #define RTL_LPADV_5000FULL BIT(6) #define RTL_LPADV_2500FULL BIT(5) +#define RTL9000A_GINMR 0x14 +#define RTL9000A_GINMR_LINK_STATUS BIT(4) + #define RTLGEN_SPEED_MASK 0x0630 #define RTL_GENERIC_PHYID 0x001cc800 @@ -655,6 +658,122 @@ static int rtlgen_resume(struct phy_device *phydev) return ret; } +static int rtl9000a_config_init(struct phy_device *phydev) +{ + phydev->autoneg = AUTONEG_DISABLE; + phydev->speed = SPEED_100; + phydev->duplex = DUPLEX_FULL; + + return 0; +} + +static int rtl9000a_config_aneg(struct phy_device *phydev) +{ + int ret; + u16 ctl = 0; + + switch (phydev->master_slave_set) { + case MASTER_SLAVE_CFG_MASTER_FORCE: + ctl |= CTL1000_AS_MASTER; + break; + case MASTER_SLAVE_CFG_SLAVE_FORCE: + break; + case MASTER_SLAVE_CFG_UNKNOWN: + case MASTER_SLAVE_CFG_UNSUPPORTED: + return 0; + default: + phydev_warn(phydev, "Unsupported Master/Slave mode\n"); + return -EOPNOTSUPP; + } + + ret = phy_modify_changed(phydev, MII_CTRL1000, CTL1000_AS_MASTER, ctl); + if (ret == 1) + ret = genphy_soft_reset(phydev); + + return ret; +} + +static int rtl9000a_read_status(struct phy_device *phydev) +{ + int ret; + + phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + ret = phy_read(phydev, MII_CTRL1000); + if (ret < 0) + return ret; + if (ret & CTL1000_AS_MASTER) + phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE; + else + phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE; + + ret = phy_read(phydev, MII_STAT1000); + if (ret < 0) + return ret; + if (ret & LPA_1000MSRES) + phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; + else + phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; + + return 0; +} + +static int rtl9000a_ack_interrupt(struct phy_device *phydev) +{ + int err; + + err = phy_read(phydev, RTL8211F_INSR); + + return (err < 0) ? err : 0; +} + +static int rtl9000a_config_intr(struct phy_device *phydev) +{ + u16 val; + int err; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + err = rtl9000a_ack_interrupt(phydev); + if (err) + return err; + + val = (u16)~RTL9000A_GINMR_LINK_STATUS; + err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); + } else { + val = ~0; + err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); + if (err) + return err; + + err = rtl9000a_ack_interrupt(phydev); + } + + return phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); +} + +static irqreturn_t rtl9000a_handle_interrupt(struct phy_device *phydev) +{ + int irq_status; + + irq_status = phy_read(phydev, RTL8211F_INSR); + if (irq_status < 0) { + phy_error(phydev); + return IRQ_NONE; + } + + if (!(irq_status & RTL8211F_INER_LINK_STATUS)) + return IRQ_NONE; + + phy_trigger_machine(phydev); + + return IRQ_HANDLED; +} + static struct phy_driver realtek_drvs[] = { { PHY_ID_MATCH_EXACT(0x00008201), @@ -823,6 +942,19 @@ static struct phy_driver realtek_drvs[] = { .handle_interrupt = genphy_handle_interrupt_no_ack, .suspend = genphy_suspend, .resume = genphy_resume, + }, { + PHY_ID_MATCH_EXACT(0x001ccb00), + .name = "RTL9000AA_RTL9000AN Ethernet", + .features = PHY_BASIC_T1_FEATURES, + .config_init = rtl9000a_config_init, + .config_aneg = rtl9000a_config_aneg, + .read_status = rtl9000a_read_status, + .config_intr = rtl9000a_config_intr, + .handle_interrupt = rtl9000a_handle_interrupt, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, }, }; diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 20b91f5dfc6e..3cfd773ae5f4 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -44,6 +44,17 @@ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, phylink_set(modes, 2500baseX_Full); } +static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, + unsigned long *modes) +{ + /* Ubiquiti U-Fiber Instant module claims that support all transceiver + * types including 10G Ethernet which is not truth. So clear all claimed + * modes and set only one mode which module supports: 1000baseX_Full. + */ + phylink_zero(modes); + phylink_set(modes, 1000baseX_Full); +} + static const struct sfp_quirk sfp_quirks[] = { { // Alcatel Lucent G-010S-P can operate at 2500base-X, but @@ -63,6 +74,10 @@ static const struct sfp_quirk sfp_quirks[] = { .vendor = "HUAWEI", .part = "MA5671A", .modes = sfp_quirk_2500basex, + }, { + .vendor = "UBNT", + .part = "UF-INSTANT", + .modes = sfp_quirk_ubnt_uf_instant, }, }; @@ -265,6 +280,12 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, br_min <= 1300 && br_max >= 1200) phylink_set(modes, 1000baseX_Full); + /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */ + if (id->base.e100_base_fx || id->base.e100_base_lx) + phylink_set(modes, 100baseFX_Full); + if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100) + phylink_set(modes, 100baseFX_Full); + /* For active or passive cables, select the link modes * based on the bit rates and the cable compliance bytes. */ @@ -337,11 +358,16 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, * the bitrate to determine supported modes. Some BiDi modules (eg, * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing * wavelengths, so do not set any transceiver bits. + * + * Do the same for modules supporting 2500BASE-X. Note that some + * modules use 2500Mbaud rather than 3100 or 3200Mbaud for + * 2500BASE-X, so we allow some slack here. */ - if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) { - /* If the bit rate allows 1000baseX */ - if (br_nom && br_min <= 1300 && br_max >= 1200) + if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { + if (br_min <= 1300 && br_max >= 1200) phylink_set(modes, 1000baseX_Full); + if (br_min <= 3200 && br_max >= 2500) + phylink_set(modes, 2500baseX_Full); } if (bus->sfp_quirk) @@ -384,6 +410,9 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus, if (phylink_test(link_modes, 1000baseX_Full)) return PHY_INTERFACE_MODE_1000BASEX; + if (phylink_test(link_modes, 100baseFX_Full)) + return PHY_INTERFACE_MODE_100BASEX; + dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n"); return PHY_INTERFACE_MODE_NA; diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 91d74c1a920a..7998acc689b7 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/acpi.h> #include <linux/ctype.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/hwmon.h> @@ -258,6 +259,9 @@ struct sfp { char *hwmon_name; #endif +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *debugfs_dir; +#endif }; static bool sff_module_supported(const struct sfp_eeprom_id *id) @@ -273,8 +277,21 @@ static const struct sff_data sff_data = { static bool sfp_module_supported(const struct sfp_eeprom_id *id) { - return id->base.phys_id == SFF8024_ID_SFP && - id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP; + if (id->base.phys_id == SFF8024_ID_SFP && + id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP) + return true; + + /* SFP GPON module Ubiquiti U-Fiber Instant has in its EEPROM stored + * phys id SFF instead of SFP. Therefore mark this module explicitly + * as supported based on vendor name and pn match. + */ + if (id->base.phys_id == SFF8024_ID_SFF_8472 && + id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP && + !memcmp(id->base.vendor_name, "UBNT ", 16) && + !memcmp(id->base.vendor_pn, "UF-INSTANT ", 16)) + return true; + + return false; } static const struct sff_data sfp_data = { @@ -336,19 +353,11 @@ static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, size_t len) { struct i2c_msg msgs[2]; - size_t block_size; + u8 bus_addr = a2 ? 0x51 : 0x50; + size_t block_size = sfp->i2c_block_size; size_t this_len; - u8 bus_addr; int ret; - if (a2) { - block_size = 16; - bus_addr = 0x51; - } else { - block_size = sfp->i2c_block_size; - bus_addr = 0x50; - } - msgs[0].addr = bus_addr; msgs[0].flags = 0; msgs[0].len = 1; @@ -1282,6 +1291,20 @@ static void sfp_hwmon_probe(struct work_struct *work) struct sfp *sfp = container_of(work, struct sfp, hwmon_probe.work); int err, i; + /* hwmon interface needs to access 16bit registers in atomic way to + * guarantee coherency of the diagnostic monitoring data. If it is not + * possible to guarantee coherency because EEPROM is broken in such way + * that does not support atomic 16bit read operation then we have to + * skip registration of hwmon device. + */ + if (sfp->i2c_block_size < 2) { + dev_info(sfp->dev, + "skipping hwmon device registration due to broken EEPROM\n"); + dev_info(sfp->dev, + "diagnostic EEPROM area cannot be read atomically to guarantee data coherency\n"); + return; + } + err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag)); if (err < 0) { if (sfp->hwmon_tries--) { @@ -1390,6 +1413,54 @@ static void sfp_module_tx_enable(struct sfp *sfp) sfp_set_state(sfp, sfp->state); } +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int sfp_debug_state_show(struct seq_file *s, void *data) +{ + struct sfp *sfp = s->private; + + seq_printf(s, "Module state: %s\n", + mod_state_to_str(sfp->sm_mod_state)); + seq_printf(s, "Module probe attempts: %d %d\n", + R_PROBE_RETRY_INIT - sfp->sm_mod_tries_init, + R_PROBE_RETRY_SLOW - sfp->sm_mod_tries); + seq_printf(s, "Device state: %s\n", + dev_state_to_str(sfp->sm_dev_state)); + seq_printf(s, "Main state: %s\n", + sm_state_to_str(sfp->sm_state)); + seq_printf(s, "Fault recovery remaining retries: %d\n", + sfp->sm_fault_retries); + seq_printf(s, "PHY probe remaining retries: %d\n", + sfp->sm_phy_retries); + seq_printf(s, "moddef0: %d\n", !!(sfp->state & SFP_F_PRESENT)); + seq_printf(s, "rx_los: %d\n", !!(sfp->state & SFP_F_LOS)); + seq_printf(s, "tx_fault: %d\n", !!(sfp->state & SFP_F_TX_FAULT)); + seq_printf(s, "tx_disable: %d\n", !!(sfp->state & SFP_F_TX_DISABLE)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(sfp_debug_state); + +static void sfp_debugfs_init(struct sfp *sfp) +{ + sfp->debugfs_dir = debugfs_create_dir(dev_name(sfp->dev), NULL); + + debugfs_create_file("state", 0600, sfp->debugfs_dir, sfp, + &sfp_debug_state_fops); +} + +static void sfp_debugfs_exit(struct sfp *sfp) +{ + debugfs_remove_recursive(sfp->debugfs_dir); +} +#else +static void sfp_debugfs_init(struct sfp *sfp) +{ +} + +static void sfp_debugfs_exit(struct sfp *sfp) +{ +} +#endif + static void sfp_module_tx_fault_reset(struct sfp *sfp) { unsigned int state = sfp->state; @@ -1482,15 +1553,19 @@ static void sfp_sm_link_down(struct sfp *sfp) static void sfp_sm_link_check_los(struct sfp *sfp) { - unsigned int los = sfp->state & SFP_F_LOS; + const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); + const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); + __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); + bool los = false; /* If neither SFP_OPTIONS_LOS_INVERTED nor SFP_OPTIONS_LOS_NORMAL - * are set, we assume that no LOS signal is available. + * are set, we assume that no LOS signal is available. If both are + * set, we assume LOS is not implemented (and is meaningless.) */ - if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED)) - los ^= SFP_F_LOS; - else if (!(sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL))) - los = 0; + if (los_options == los_inverted) + los = !(sfp->state & SFP_F_LOS); + else if (los_options == los_normal) + los = !!(sfp->state & SFP_F_LOS); if (los) sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); @@ -1500,18 +1575,22 @@ static void sfp_sm_link_check_los(struct sfp *sfp) static bool sfp_los_event_active(struct sfp *sfp, unsigned int event) { - return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) && - event == SFP_E_LOS_LOW) || - (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) && - event == SFP_E_LOS_HIGH); + const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); + const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); + __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); + + return (los_options == los_inverted && event == SFP_E_LOS_LOW) || + (los_options == los_normal && event == SFP_E_LOS_HIGH); } static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event) { - return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) && - event == SFP_E_LOS_HIGH) || - (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) && - event == SFP_E_LOS_LOW); + const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); + const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); + __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); + + return (los_options == los_inverted && event == SFP_E_LOS_HIGH) || + (los_options == los_normal && event == SFP_E_LOS_LOW); } static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) @@ -1642,26 +1721,30 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) return 0; } -/* Some modules (Nokia 3FE46541AA) lock up if byte 0x51 is read as a - * single read. Switch back to reading 16 byte blocks unless we have - * a CarlitoxxPro module (rebranded VSOL V2801F). Even more annoyingly, - * some VSOL V2801F have the vendor name changed to OEM. +/* GPON modules based on Realtek RTL8672 and RTL9601C chips (e.g. V-SOL + * V2801F, CarlitoxxPro CPGOS03-0490, Ubiquiti U-Fiber Instant, ...) do + * not support multibyte reads from the EEPROM. Each multi-byte read + * operation returns just one byte of EEPROM followed by zeros. There is + * no way to identify which modules are using Realtek RTL8672 and RTL9601C + * chips. Moreover every OEM of V-SOL V2801F module puts its own vendor + * name and vendor id into EEPROM, so there is even no way to detect if + * module is V-SOL V2801F. Therefore check for those zeros in the read + * data and then based on check switch to reading EEPROM to one byte + * at a time. */ -static int sfp_quirk_i2c_block_size(const struct sfp_eeprom_base *base) +static bool sfp_id_needs_byte_io(struct sfp *sfp, void *buf, size_t len) { - if (!memcmp(base->vendor_name, "VSOL ", 16)) - return 1; - if (!memcmp(base->vendor_name, "OEM ", 16) && - !memcmp(base->vendor_pn, "V2801F ", 16)) - return 1; + size_t i, block_size = sfp->i2c_block_size; - /* Some modules can't cope with long reads */ - return 16; -} + /* Already using byte IO */ + if (block_size == 1) + return false; -static void sfp_quirks_base(struct sfp *sfp, const struct sfp_eeprom_base *base) -{ - sfp->i2c_block_size = sfp_quirk_i2c_block_size(base); + for (i = 1; i < len; i += block_size) { + if (memchr_inv(buf + i, '\0', min(block_size - 1, len - i))) + return false; + } + return true; } static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) @@ -1705,11 +1788,11 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) u8 check; int ret; - /* Some modules (CarlitoxxPro CPGOS03-0490) do not support multibyte - * reads from the EEPROM, so start by reading the base identifying - * information one byte at a time. + /* Some SFP modules and also some Linux I2C drivers do not like reads + * longer than 16 bytes, so read the EEPROM in chunks of 16 bytes at + * a time. */ - sfp->i2c_block_size = 1; + sfp->i2c_block_size = 16; ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base)); if (ret < 0) { @@ -1723,6 +1806,33 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) return -EAGAIN; } + /* Some SFP modules (e.g. Nokia 3FE46541AA) lock up if read from + * address 0x51 is just one byte at a time. Also SFF-8472 requires + * that EEPROM supports atomic 16bit read operation for diagnostic + * fields, so do not switch to one byte reading at a time unless it + * is really required and we have no other option. + */ + if (sfp_id_needs_byte_io(sfp, &id.base, sizeof(id.base))) { + dev_info(sfp->dev, + "Detected broken RTL8672/RTL9601C emulated EEPROM\n"); + dev_info(sfp->dev, + "Switching to reading EEPROM to one byte at a time\n"); + sfp->i2c_block_size = 1; + + ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base)); + if (ret < 0) { + if (report) + dev_err(sfp->dev, "failed to read EEPROM: %d\n", + ret); + return -EAGAIN; + } + + if (ret != sizeof(id.base)) { + dev_err(sfp->dev, "EEPROM short read: %d\n", ret); + return -EAGAIN; + } + } + /* Cotsworks do not seem to update the checksums when they * do the final programming with the final module part number, * serial number and date code. @@ -1757,9 +1867,6 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) } } - /* Apply any early module-specific quirks */ - sfp_quirks_base(sfp, &id.base); - ret = sfp_read(sfp, false, SFP_CC_BASE + 1, &id.ext, sizeof(id.ext)); if (ret < 0) { if (report) @@ -2483,6 +2590,8 @@ static int sfp_probe(struct platform_device *pdev) if (!sfp->sfp_bus) return -ENOMEM; + sfp_debugfs_init(sfp); + return 0; } @@ -2490,6 +2599,7 @@ static int sfp_remove(struct platform_device *pdev) { struct sfp *sfp = platform_get_drvdata(pdev); + sfp_debugfs_exit(sfp); sfp_unregister_socket(sfp->sfp_bus); rtnl_lock(); diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index ee5058445d06..0fe78826c8fa 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -278,10 +278,8 @@ static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb) header = (struct pptp_gre_header *)(skb->data); /* ack in different place if S = 0 */ - ack = GRE_IS_SEQ(header->gre_hd.flags) ? header->ack : header->seq; - - ack = ntohl(ack); - + ack = GRE_IS_SEQ(header->gre_hd.flags) ? ntohl(header->ack) : + ntohl(header->seq); if (ack > opt->ack_recv) opt->ack_recv = ack; /* also handle sequence number wrap-around */ @@ -355,7 +353,7 @@ static int pptp_rcv(struct sk_buff *skb) /* if invalid, discard this packet */ goto drop; - po = lookup_chan(htons(header->call_id), iph->saddr); + po = lookup_chan(ntohs(header->call_id), iph->saddr); if (po) { skb_dst_drop(skb); nf_reset_ct(skb); diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 1f4bdd94407a..ff4aa35979a1 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -713,8 +713,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, skb_probe_transport_header(skb); /* Move network header to the right position for VLAN tagged packets */ - if ((skb->protocol == htons(ETH_P_8021Q) || - skb->protocol == htons(ETH_P_8021AD)) && + if (eth_type_vlan(skb->protocol) && __vlan_get_protocol(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); @@ -722,12 +721,10 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, tap = rcu_dereference(q->tap); /* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) { - skb_shinfo(skb)->destructor_arg = msg_control; - skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; - skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; + skb_zcopy_init(skb, msg_control); } else if (msg_control) { struct ubuf_info *uarg = msg_control; - uarg->callback(uarg, false); + uarg->callback(NULL, uarg, false); } if (tap) { @@ -1166,8 +1163,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) } /* Move network header to the right position for VLAN tagged packets */ - if ((skb->protocol == htons(ETH_P_8021Q) || - skb->protocol == htons(ETH_P_8021AD)) && + if (eth_type_vlan(skb->protocol) && __vlan_get_protocol(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 978ac0981d16..62690baa19bc 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1599,12 +1599,8 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, struct xdp_buff xdp; u32 act; - xdp.data_hard_start = buf; - xdp.data = buf + pad; - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = xdp.data + len; - xdp.rxq = &tfile->xdp_rxq; - xdp.frame_sz = buflen; + xdp_init_buff(&xdp, buflen, &tfile->xdp_rxq); + xdp_prepare_buff(&xdp, buf, pad, len, false); act = bpf_prog_run_xdp(xdp_prog, &xdp); if (act == XDP_REDIRECT || act == XDP_TX) { @@ -1814,12 +1810,10 @@ drop: /* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) { - skb_shinfo(skb)->destructor_arg = msg_control; - skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; - skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; + skb_zcopy_init(skb, msg_control); } else if (msg_control) { struct ubuf_info *uarg = msg_control; - uarg->callback(uarg, false); + uarg->callback(NULL, uarg, false); } skb_reset_network_header(skb); @@ -2344,9 +2338,9 @@ static int tun_xdp_one(struct tun_struct *tun, skb_xdp = true; goto build; } + + xdp_init_buff(xdp, buflen, &tfile->xdp_rxq); xdp_set_data_meta_invalid(xdp); - xdp->rxq = &tfile->xdp_rxq; - xdp->frame_sz = buflen; act = bpf_prog_run_xdp(xdp_prog, xdp); err = tun_xdp_act(tun, xdp_prog, xdp, act); @@ -2741,7 +2735,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) err = register_netdevice(tun->dev); if (err < 0) goto err_detach; - /* free_netdev() won't check refcnt, to aovid race + /* free_netdev() won't check refcnt, to avoid race * with dev_put() we need publish tun after registration. */ rcu_assign_pointer(tfile->tun, tun); diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 2bb28db89432..ef6dd012b8c4 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -370,7 +370,7 @@ static struct usb_driver hso_driver; static struct tty_driver *tty_drv; static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS]; static struct hso_device *network_table[HSO_MAX_NET_DEVICES]; -static spinlock_t serial_table_lock; +static DEFINE_SPINLOCK(serial_table_lock); static const s32 default_port_spec[] = { HSO_INTF_MUX | HSO_PORT_NETWORK, @@ -3236,7 +3236,6 @@ static int __init hso_init(void) pr_info("%s\n", version); /* Initialise the serial table semaphore and table */ - spin_lock_init(&serial_table_lock); for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) serial_table[i] = NULL; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index cc4819282820..b96a3dc775a4 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -186,7 +186,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb) net = qmimux_find_dev(dev, hdr->mux_id); if (!net) goto skip; - skbn = netdev_alloc_skb(net, pkt_len); + skbn = netdev_alloc_skb(net, pkt_len + LL_MAX_HEADER); if (!skbn) return 0; skbn->dev = net; @@ -203,6 +203,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb) goto skip; } + skb_reserve(skbn, LL_MAX_HEADER); skb_put_data(skbn, skb->data + offset + qmimux_hdr_sz, pkt_len); if (netif_rx(skbn) != NET_RX_SUCCESS) { net->stats.rx_errors++; diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 1447da1d5729..b4c8080e6f87 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1539,11 +1539,11 @@ static void usbnet_bh (struct timer_list *t) } } -static void usbnet_bh_tasklet(unsigned long data) +static void usbnet_bh_tasklet(struct tasklet_struct *t) { - struct timer_list *t = (struct timer_list *)data; + struct usbnet *dev = from_tasklet(dev, t, bh); - usbnet_bh(t); + usbnet_bh(&dev->delay); } @@ -1673,8 +1673,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) skb_queue_head_init (&dev->txq); skb_queue_head_init (&dev->done); skb_queue_head_init(&dev->rxq_pause); - dev->bh.func = usbnet_bh_tasklet; - dev->bh.data = (unsigned long)&dev->delay; + tasklet_setup(&dev->bh, usbnet_bh_tasklet); INIT_WORK (&dev->kevent, usbnet_deferred_kevent); init_usb_anchor(&dev->deferred); timer_setup(&dev->delay, usbnet_bh, 0); @@ -1964,12 +1963,12 @@ static int __usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, cmd, reqtype, value, index, buf, size, USB_CTRL_GET_TIMEOUT); if (err > 0 && err <= size) { - if (data) - memcpy(data, buf, err); - else - netdev_dbg(dev->net, - "Huh? Data requested but thrown away.\n"); - } + if (data) + memcpy(data, buf, err); + else + netdev_dbg(dev->net, + "Huh? Data requested but thrown away.\n"); + } kfree(buf); out: return err; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 02bfcdf50a7a..99caae7d1641 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -654,7 +654,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, struct veth_xdp_tx_bq *bq, struct veth_stats *stats) { - u32 pktlen, headroom, act, metalen; + u32 pktlen, headroom, act, metalen, frame_sz; void *orig_data, *orig_data_end; struct bpf_prog *xdp_prog; int mac_len, delta, off; @@ -710,15 +710,11 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, skb = nskb; } - xdp.data_hard_start = skb->head; - xdp.data = skb_mac_header(skb); - xdp.data_end = xdp.data + pktlen; - xdp.data_meta = xdp.data; - xdp.rxq = &rq->xdp_rxq; - /* SKB "head" area always have tailroom for skb_shared_info */ - xdp.frame_sz = (void *)skb_end_pointer(skb) - xdp.data_hard_start; - xdp.frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + frame_sz = skb_end_pointer(skb) - skb->head; + frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + xdp_init_buff(&xdp, frame_sz, &rq->xdp_rxq); + xdp_prepare_buff(&xdp, skb->head, skb->mac_header, pktlen, true); orig_data = xdp.data; orig_data_end = xdp.data_end; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 508408fbe78f..ba8e63792549 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -689,12 +689,9 @@ static struct sk_buff *receive_small(struct net_device *dev, page = xdp_page; } - xdp.data_hard_start = buf + VIRTNET_RX_PAD + vi->hdr_len; - xdp.data = xdp.data_hard_start + xdp_headroom; - xdp.data_end = xdp.data + len; - xdp.data_meta = xdp.data; - xdp.rxq = &rq->xdp_rxq; - xdp.frame_sz = buflen; + xdp_init_buff(&xdp, buflen, &rq->xdp_rxq); + xdp_prepare_buff(&xdp, buf + VIRTNET_RX_PAD + vi->hdr_len, + xdp_headroom, len, true); orig_data = xdp.data; act = bpf_prog_run_xdp(xdp_prog, &xdp); stats->xdp_packets++; @@ -859,12 +856,9 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, * the descriptor on if we get an XDP_TX return code. */ data = page_address(xdp_page) + offset; - xdp.data_hard_start = data - VIRTIO_XDP_HEADROOM + vi->hdr_len; - xdp.data = data + vi->hdr_len; - xdp.data_end = xdp.data + (len - vi->hdr_len); - xdp.data_meta = xdp.data; - xdp.rxq = &rq->xdp_rxq; - xdp.frame_sz = frame_sz - vi->hdr_len; + xdp_init_buff(&xdp, frame_sz - vi->hdr_len, &rq->xdp_rxq); + xdp_prepare_buff(&xdp, data - VIRTIO_XDP_HEADROOM + vi->hdr_len, + VIRTIO_XDP_HEADROOM, len - vi->hdr_len, true); act = bpf_prog_run_xdp(xdp_prog, &xdp); stats->xdp_packets++; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index a8ad710629e6..3929e437382b 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -3283,12 +3283,13 @@ static void vxlan_setup(struct net_device *dev) SET_NETDEV_DEVTYPE(dev, &vxlan_type); dev->features |= NETIF_F_LLTX; - dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; dev->vlan_features = dev->features; - dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; + dev->hw_features |= NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; netif_keep_dst(dev); dev->priv_flags |= IFF_NO_QUEUE; @@ -4521,17 +4522,12 @@ static int vxlan_netdevice_event(struct notifier_block *unused, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); - if (event == NETDEV_UNREGISTER) { - if (!dev->udp_tunnel_nic_info) - vxlan_offload_rx_ports(dev, false); + if (event == NETDEV_UNREGISTER) vxlan_handle_lowerdev_unregister(vn, dev); - } else if (event == NETDEV_REGISTER) { - if (!dev->udp_tunnel_nic_info) - vxlan_offload_rx_ports(dev, true); - } else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO || - event == NETDEV_UDP_TUNNEL_DROP_INFO) { - vxlan_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO); - } + else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO) + vxlan_offload_rx_ports(dev, true); + else if (event == NETDEV_UDP_TUNNEL_DROP_INFO) + vxlan_offload_rx_ports(dev, false); return NOTIFY_DONE; } diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 7c5cf77e9ef1..ecea09fd21cb 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -323,7 +323,7 @@ struct desc { static int ports_open; static struct dma_pool *dma_pool; -static spinlock_t npe_lock; +static DEFINE_SPINLOCK(npe_lock); static const struct { int tx, txdone, rx, rxfree; @@ -1402,8 +1402,6 @@ static int __init hss_init_module(void) (IXP4XX_FEATURE_HDLC | IXP4XX_FEATURE_HSS)) return -ENODEV; - spin_lock_init(&npe_lock); - return platform_driver_register(&ixp4xx_hss_driver); } diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 2fde439543fb..3092a09d3eaa 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -1535,7 +1535,7 @@ sbni_setup( char *p ) goto bad_param; for( n = 0, parm = 0; *p && n < 8; ) { - (*dest[ parm ])[ n ] = simple_strtol( p, &p, 0 ); + (*dest[ parm ])[ n ] = simple_strtoul( p, &p, 0 ); if( !*p || *p == ')' ) return 1; if( *p == ';' ) { diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index b876fec7fa1b..e1a1df169034 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -247,7 +247,9 @@ int ath11k_regd_update(struct ath11k *ar, bool init) } rtnl_lock(); - ret = regulatory_set_wiphy_regd_sync_rtnl(ar->hw->wiphy, regd_copy); + wiphy_lock(ar->hw->wiphy); + ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy); + wiphy_unlock(ar->hw->wiphy); rtnl_unlock(); kfree(regd_copy); diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 9c83e9a4299b..29527e8dcced 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3648,7 +3648,7 @@ void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif) kfree(mc_filter); } - unregister_netdevice(vif->ndev); + cfg80211_unregister_netdevice(vif->ndev); ar->num_vif--; } @@ -3821,7 +3821,7 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, netdev_set_default_ethtool_ops(ndev, &ath6kl_ethtool_ops); - if (register_netdevice(ndev)) + if (cfg80211_register_netdevice(ndev)) goto err; ar->avail_idx_map &= ~BIT(fw_vif_idx); diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index ebb9f163710f..4f0a7a185fc9 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -212,11 +212,13 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ar->avail_idx_map |= BIT(i); rtnl_lock(); + wiphy_lock(ar->wiphy); /* Add an initial station interface */ wdev = ath6kl_interface_add(ar, "wlan%d", NET_NAME_ENUM, NL80211_IFTYPE_STATION, 0, INFRA_NETWORK); + wiphy_unlock(ar->wiphy); rtnl_unlock(); if (!wdev) { diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 39bf19686175..9b5c7d8f2b95 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1904,7 +1904,9 @@ void ath6kl_stop_txrx(struct ath6kl *ar) spin_unlock_bh(&ar->list_lock); ath6kl_cfg80211_vif_stop(vif, test_bit(WMI_READY, &ar->flag)); rtnl_lock(); + wiphy_lock(ar->wiphy); ath6kl_cfg80211_vif_cleanup(vif); + wiphy_unlock(ar->wiphy); rtnl_unlock(); spin_lock_bh(&ar->list_lock); } diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 1c42410d68e1..60bba5b491e0 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2820,7 +2820,9 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil) wil->radio_wdev = wil->main_ndev->ieee80211_ptr; mutex_unlock(&wil->vif_mutex); if (p2p_wdev) { + wiphy_lock(wil->wiphy); cfg80211_unregister_wdev(p2p_wdev); + wiphy_unlock(wil->wiphy); kfree(p2p_wdev); } } diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 07b4a252a23c..0913f0bf60e7 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -424,7 +424,7 @@ int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif) if (rc) return rc; } - rc = register_netdevice(ndev); + rc = cfg80211_register_netdevice(ndev); if (rc < 0) { dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); if (any_active && vif->mid != 0) @@ -473,7 +473,9 @@ int wil_if_add(struct wil6210_priv *wil) wil_update_net_queues_bh(wil, vif, NULL, true); rtnl_lock(); + wiphy_lock(wiphy); rc = wil_vif_add(wil, vif); + wiphy_unlock(wiphy); rtnl_unlock(); if (rc < 0) goto out_wiphy; @@ -511,7 +513,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid) /* during unregister_netdevice cfg80211_leave may perform operations * such as stop AP, disconnect, so we only clear the VIF afterwards */ - unregister_netdevice(ndev); + cfg80211_unregister_netdevice(ndev); if (any_active && vif->mid != 0) wmi_port_delete(wil, vif->mid); @@ -543,15 +545,18 @@ void wil_if_remove(struct wil6210_priv *wil) { struct net_device *ndev = wil->main_ndev; struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; wil_dbg_misc(wil, "if_remove\n"); rtnl_lock(); + wiphy_lock(wiphy); wil_vif_remove(wil, 0); + wiphy_unlock(wiphy); rtnl_unlock(); netif_napi_del(&wil->napi_tx); netif_napi_del(&wil->napi_rx); - wiphy_unregister(wdev->wiphy); + wiphy_unregister(wiphy); } diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index c174323c5c0b..ce40d94909ad 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -473,8 +473,10 @@ static void wil_pcie_remove(struct pci_dev *pdev) wil6210_debugfs_remove(wil); rtnl_lock(); + wiphy_lock(wil->wiphy); wil_p2p_wdev_free(wil); wil_remove_all_additional_vifs(wil); + wiphy_unlock(wil->wiphy); rtnl_unlock(); wil_if_remove(wil); wil_if_pcie_disable(wil); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 3dd28f5fef19..ea78fe527c5d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -633,7 +633,7 @@ static const struct net_device_ops brcmf_netdev_ops_pri = { .ndo_set_rx_mode = brcmf_netdev_set_multicast_list }; -int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) +int brcmf_net_attach(struct brcmf_if *ifp, bool locked) { struct brcmf_pub *drvr = ifp->drvr; struct net_device *ndev; @@ -656,8 +656,8 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable); - if (rtnl_locked) - err = register_netdevice(ndev); + if (locked) + err = cfg80211_register_netdevice(ndev); else err = register_netdev(ndev); if (err != 0) { @@ -677,11 +677,11 @@ fail: return -EBADE; } -void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked) +void brcmf_net_detach(struct net_device *ndev, bool locked) { if (ndev->reg_state == NETREG_REGISTERED) { - if (rtnl_locked) - unregister_netdevice(ndev); + if (locked) + cfg80211_unregister_netdevice(ndev); else unregister_netdev(ndev); } else { @@ -758,7 +758,7 @@ int brcmf_net_mon_attach(struct brcmf_if *ifp) ndev = ifp->ndev; ndev->netdev_ops = &brcmf_netdev_ops_mon; - err = register_netdevice(ndev); + err = cfg80211_register_netdevice(ndev); if (err) bphy_err(drvr, "Failed to register %s device\n", ndev->name); @@ -909,7 +909,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, } static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx, - bool rtnl_locked) + bool locked) { struct brcmf_if *ifp; int ifidx; @@ -938,7 +938,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx, cancel_work_sync(&ifp->multicast_work); cancel_work_sync(&ifp->ndoffload_work); } - brcmf_net_detach(ifp->ndev, rtnl_locked); + brcmf_net_detach(ifp->ndev, locked); } else { /* Only p2p device interfaces which get dynamically created * end up here. In this case the p2p module should be informed @@ -947,7 +947,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx, * serious troublesome side effects. The p2p module will clean * up the ifp if needed. */ - brcmf_p2p_ifp_removed(ifp, rtnl_locked); + brcmf_p2p_ifp_removed(ifp, locked); kfree(ifp); } @@ -956,14 +956,14 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx, drvr->if2bss[ifidx] = BRCMF_BSSIDX_INVALID; } -void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked) +void brcmf_remove_interface(struct brcmf_if *ifp, bool locked) { if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp)) return; brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx, ifp->ifidx); brcmf_proto_del_if(ifp->drvr, ifp); - brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked); + brcmf_del_if(ifp->drvr, ifp->bsscfgidx, locked); } static int brcmf_psm_watchdog_notify(struct brcmf_if *ifp, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 5767d665cee5..8212c9de14f1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -201,16 +201,16 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); char *brcmf_ifname(struct brcmf_if *ifp); struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); void brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable); -int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); +int brcmf_net_attach(struct brcmf_if *ifp, bool locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, bool is_p2pdev, const char *name, u8 *mac_addr); -void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked); +void brcmf_remove_interface(struct brcmf_if *ifp, bool locked); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb); -void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked); +void brcmf_net_detach(struct net_device *ndev, bool locked); int brcmf_net_mon_attach(struct brcmf_if *ifp); void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); int __init brcmf_core_init(void); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index ec6fc7a150a6..6d30a0fcecea 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -2430,7 +2430,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) return err; } -void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool rtnl_locked) +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool locked) { struct brcmf_cfg80211_info *cfg; struct brcmf_cfg80211_vif *vif; @@ -2439,11 +2439,15 @@ void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool rtnl_locked) vif = ifp->vif; cfg = wdev_to_cfg(&vif->wdev); cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; - if (!rtnl_locked) + if (locked) { rtnl_lock(); - cfg80211_unregister_wdev(&vif->wdev); - if (!rtnl_locked) + wiphy_lock(cfg->wiphy); + cfg80211_unregister_wdev(&vif->wdev); + wiphy_unlock(cfg->wiphy); rtnl_unlock(); + } else { + cfg80211_unregister_wdev(&vif->wdev); + } brcmf_free_vif(vif); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index df018972a46b..64c10ca0d31e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2141,7 +2141,7 @@ err: out_iterate: if (!test) - ieee80211_iterate_active_interfaces_rtnl(mvm->hw, + ieee80211_iterate_active_interfaces_mtx(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 43ff0407916a..bcbd77ef68df 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -260,7 +260,7 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) int ret; bool changed; const struct ieee80211_regdomain *r = - rtnl_dereference(mvm->hw->wiphy->regd); + wiphy_dereference(mvm->hw->wiphy, mvm->hw->wiphy->regd); if (!r) return -ENOENT; @@ -282,7 +282,7 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) /* update cfg80211 if the regdomain was changed */ if (changed) - ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); + ret = regulatory_set_wiphy_regd_sync(mvm->hw->wiphy, regd); else ret = 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index abb8c1088c2f..7fb4e618f76e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -545,7 +545,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm) return -EIO; } - retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); + retval = regulatory_set_wiphy_regd_sync(mvm->hw->wiphy, regd); kfree(regd); return retval; } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 3b3fc7c9c91d..fa7d4c20dc13 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -311,6 +311,12 @@ static struct net_device *hwsim_mon; /* global monitor netdev */ .hw_value = (_freq), \ } +#define CHAN6G(_freq) { \ + .band = NL80211_BAND_6GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_freq), \ +} + static const struct ieee80211_channel hwsim_channels_2ghz[] = { CHAN2G(2412), /* Channel 1 */ CHAN2G(2417), /* Channel 2 */ @@ -377,6 +383,68 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = { CHAN5G(5925), /* Channel 185 */ }; +static const struct ieee80211_channel hwsim_channels_6ghz[] = { + CHAN6G(5955), /* Channel 1 */ + CHAN6G(5975), /* Channel 5 */ + CHAN6G(5995), /* Channel 9 */ + CHAN6G(6015), /* Channel 13 */ + CHAN6G(6035), /* Channel 17 */ + CHAN6G(6055), /* Channel 21 */ + CHAN6G(6075), /* Channel 25 */ + CHAN6G(6095), /* Channel 29 */ + CHAN6G(6115), /* Channel 33 */ + CHAN6G(6135), /* Channel 37 */ + CHAN6G(6155), /* Channel 41 */ + CHAN6G(6175), /* Channel 45 */ + CHAN6G(6195), /* Channel 49 */ + CHAN6G(6215), /* Channel 53 */ + CHAN6G(6235), /* Channel 57 */ + CHAN6G(6255), /* Channel 61 */ + CHAN6G(6275), /* Channel 65 */ + CHAN6G(6295), /* Channel 69 */ + CHAN6G(6315), /* Channel 73 */ + CHAN6G(6335), /* Channel 77 */ + CHAN6G(6355), /* Channel 81 */ + CHAN6G(6375), /* Channel 85 */ + CHAN6G(6395), /* Channel 89 */ + CHAN6G(6415), /* Channel 93 */ + CHAN6G(6435), /* Channel 97 */ + CHAN6G(6455), /* Channel 181 */ + CHAN6G(6475), /* Channel 105 */ + CHAN6G(6495), /* Channel 109 */ + CHAN6G(6515), /* Channel 113 */ + CHAN6G(6535), /* Channel 117 */ + CHAN6G(6555), /* Channel 121 */ + CHAN6G(6575), /* Channel 125 */ + CHAN6G(6595), /* Channel 129 */ + CHAN6G(6615), /* Channel 133 */ + CHAN6G(6635), /* Channel 137 */ + CHAN6G(6655), /* Channel 141 */ + CHAN6G(6675), /* Channel 145 */ + CHAN6G(6695), /* Channel 149 */ + CHAN6G(6715), /* Channel 153 */ + CHAN6G(6735), /* Channel 157 */ + CHAN6G(6755), /* Channel 161 */ + CHAN6G(6775), /* Channel 165 */ + CHAN6G(6795), /* Channel 169 */ + CHAN6G(6815), /* Channel 173 */ + CHAN6G(6835), /* Channel 177 */ + CHAN6G(6855), /* Channel 181 */ + CHAN6G(6875), /* Channel 185 */ + CHAN6G(6895), /* Channel 189 */ + CHAN6G(6915), /* Channel 193 */ + CHAN6G(6935), /* Channel 197 */ + CHAN6G(6955), /* Channel 201 */ + CHAN6G(6975), /* Channel 205 */ + CHAN6G(6995), /* Channel 209 */ + CHAN6G(7015), /* Channel 213 */ + CHAN6G(7035), /* Channel 217 */ + CHAN6G(7055), /* Channel 221 */ + CHAN6G(7075), /* Channel 225 */ + CHAN6G(7095), /* Channel 229 */ + CHAN6G(7115), /* Channel 233 */ +}; + #define NUM_S1G_CHANS_US 51 static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US]; @@ -548,6 +616,7 @@ struct mac80211_hwsim_data { struct ieee80211_supported_band bands[NUM_NL80211_BANDS]; struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; + struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)]; struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)]; struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; struct ieee80211_iface_combination if_combination; @@ -578,7 +647,8 @@ struct mac80211_hwsim_data { struct ieee80211_channel *channel; unsigned long next_start, start, end; } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) + - ARRAY_SIZE(hwsim_channels_5ghz)]; + ARRAY_SIZE(hwsim_channels_5ghz) + + ARRAY_SIZE(hwsim_channels_6ghz)]; struct ieee80211_channel *channel; u64 beacon_int /* beacon interval in us */; @@ -3149,6 +3219,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, sizeof(hwsim_channels_2ghz)); memcpy(data->channels_5ghz, hwsim_channels_5ghz, sizeof(hwsim_channels_5ghz)); + memcpy(data->channels_6ghz, hwsim_channels_6ghz, + sizeof(hwsim_channels_6ghz)); memcpy(data->channels_s1g, hwsim_channels_s1g, sizeof(hwsim_channels_s1g)); memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index a6b9dc6700b1..5553df913290 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -2097,7 +2097,7 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (!mwifiex_stop_bg_scan(priv)) - cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0); + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; @@ -2366,7 +2366,7 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, (int)sme->ssid_len, (char *)sme->ssid, sme->bssid); if (!mwifiex_stop_bg_scan(priv)) - cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0); + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); @@ -2576,7 +2576,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, priv->scan_block = false; if (!mwifiex_stop_bg_scan(priv)) - cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0); + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); if (!user_scan_cfg) @@ -3081,7 +3081,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, mutex_init(&priv->async_mutex); /* Register network device */ - if (register_netdevice(dev)) { + if (cfg80211_register_netdevice(dev)) { mwifiex_dbg(adapter, ERROR, "cannot register network device\n"); ret = -EFAULT; goto err_reg_netdev; @@ -3160,7 +3160,7 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) netif_carrier_off(priv->netdev); if (wdev->netdev->reg_state == NETREG_REGISTERED) - unregister_netdevice(wdev->netdev); + cfg80211_unregister_netdevice(wdev->netdev); if (priv->dfs_cac_workqueue) { flush_workqueue(priv->dfs_cac_workqueue); diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index ee52fb839ef7..529dfd8b7ae8 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -598,12 +598,14 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) } rtnl_lock(); + wiphy_lock(adapter->wiphy); /* Create station interface by default */ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, NL80211_IFTYPE_STATION, NULL); if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create default STA interface\n"); + wiphy_unlock(adapter->wiphy); rtnl_unlock(); goto err_add_intf; } @@ -614,6 +616,7 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create AP interface\n"); + wiphy_unlock(adapter->wiphy); rtnl_unlock(); goto err_add_intf; } @@ -625,10 +628,12 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create p2p client interface\n"); + wiphy_unlock(adapter->wiphy); rtnl_unlock(); goto err_add_intf; } } + wiphy_unlock(adapter->wiphy); rtnl_unlock(); mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); @@ -1440,9 +1445,11 @@ static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter) if (!priv) continue; rtnl_lock(); + wiphy_lock(adapter->wiphy); if (priv->netdev && priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); + wiphy_unlock(adapter->wiphy); rtnl_unlock(); } diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index e3dd205cbbe5..96973ec7bd9a 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -1538,7 +1538,7 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) wilc_wfi_deinit_mon_interface(wl, true); vif = netdev_priv(wdev->netdev); cfg80211_stop_iface(wiphy, wdev, GFP_KERNEL); - unregister_netdevice(vif->ndev); + cfg80211_unregister_netdevice(vif->ndev); vif->monitor_flag = 0; wilc_set_operation_mode(vif, 0, 0, 0); diff --git a/drivers/net/wireless/microchip/wilc1000/mon.c b/drivers/net/wireless/microchip/wilc1000/mon.c index b5a1b65c087c..6bd63934c2d8 100644 --- a/drivers/net/wireless/microchip/wilc1000/mon.c +++ b/drivers/net/wireless/microchip/wilc1000/mon.c @@ -233,7 +233,7 @@ struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl, wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops; wl->monitor_dev->needs_free_netdev = true; - if (register_netdevice(wl->monitor_dev)) { + if (cfg80211_register_netdevice(wl->monitor_dev)) { netdev_err(real_dev, "register_netdevice failed\n"); free_netdev(wl->monitor_dev); return NULL; @@ -251,7 +251,7 @@ void wilc_wfi_deinit_mon_interface(struct wilc *wl, bool rtnl_locked) return; if (rtnl_locked) - unregister_netdevice(wl->monitor_dev); + cfg80211_unregister_netdevice(wl->monitor_dev); else unregister_netdev(wl->monitor_dev); wl->monitor_dev = NULL; diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 2a1fbbdd6a4b..643cbb155439 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -950,7 +950,7 @@ struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, vif->priv.dev = ndev; if (rtnl_locked) - ret = register_netdevice(ndev); + ret = cfg80211_register_netdevice(ndev); else ret = register_netdev(ndev); diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 54cdf3ad09d7..504b4d0b98c4 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -180,7 +180,7 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) cancel_work_sync(&vif->high_pri_tx_work); if (netdev->reg_state == NETREG_REGISTERED) - unregister_netdevice(netdev); + cfg80211_unregister_netdevice(netdev); if (qtnf_cmd_send_del_intf(vif)) pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, @@ -267,7 +267,7 @@ static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy, if (qtnf_hwcap_is_set(&mac->bus->hw_info, QLINK_HW_CAPAB_HW_BRIDGE)) { ret = qtnf_cmd_netdev_changeupper(vif, vif->netdev->ifindex); if (ret) { - unregister_netdevice(vif->netdev); + cfg80211_unregister_netdevice(vif->netdev); vif->netdev = NULL; goto error_del_vif; } diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c index ad726bd100ec..b4dd60b2ebc9 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.c +++ b/drivers/net/wireless/quantenna/qtnfmac/core.c @@ -492,7 +492,7 @@ int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif, SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); - ret = register_netdevice(dev); + ret = cfg80211_register_netdevice(dev); if (ret) { free_netdev(dev); vif->netdev = NULL; @@ -611,8 +611,9 @@ static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid) mac->wiphy_registered = 1; rtnl_lock(); - + wiphy_lock(priv_to_wiphy(mac)); ret = qtnf_core_net_attach(mac, vif, "wlan%d", NET_NAME_ENUM); + wiphy_unlock(priv_to_wiphy(mac)); rtnl_unlock(); if (ret) { diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 8ee24e351bdc..4a16d6e33c09 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -399,7 +399,8 @@ void xenvif_rx_queue_tail(struct xenvif_queue *queue, struct sk_buff *skb); void xenvif_carrier_on(struct xenvif *vif); /* Callback from stack when TX packet can be released */ -void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success); +void xenvif_zerocopy_callback(struct sk_buff *skb, struct ubuf_info *ubuf, + bool zerocopy_success); /* Unmap a pending page and release it back to the guest */ void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index acb786d8b1d8..08b0e3d0b7eb 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -47,7 +47,7 @@ /* Number of bytes allowed on the internal guest Rx queue. */ #define XENVIF_RX_QUEUE_BYTES (XEN_NETIF_RX_RING_SIZE/2 * PAGE_SIZE) -/* This function is used to set SKBTX_DEV_ZEROCOPY as well as +/* This function is used to set SKBFL_ZEROCOPY_ENABLE as well as * increasing the inflight counter. We need to increase the inflight * counter because core driver calls into xenvif_zerocopy_callback * which calls xenvif_skb_zerocopy_complete. @@ -55,7 +55,7 @@ void xenvif_skb_zerocopy_prepare(struct xenvif_queue *queue, struct sk_buff *skb) { - skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; + skb_shinfo(skb)->flags |= SKBFL_ZEROCOPY_ENABLE; atomic_inc(&queue->inflight_packets); } diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index bc3421d14576..19a27dce79d2 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1091,7 +1091,7 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s uarg = skb_shinfo(skb)->destructor_arg; /* increase inflight counter to offset decrement in callback */ atomic_inc(&queue->inflight_packets); - uarg->callback(uarg, true); + uarg->callback(NULL, uarg, true); skb_shinfo(skb)->destructor_arg = NULL; /* Fill the skb with the new (local) frags. */ @@ -1228,7 +1228,8 @@ static int xenvif_tx_submit(struct xenvif_queue *queue) return work_done; } -void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success) +void xenvif_zerocopy_callback(struct sk_buff *skb, struct ubuf_info *ubuf, + bool zerocopy_success) { unsigned long flags; pending_ring_idx_t index; diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 6f10e0998f1c..a5439c130130 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -411,7 +411,7 @@ static void read_xenbus_frontend_xdp(struct backend_info *be, vif->xdp_headroom = headroom; } -/** +/* * Callback received when the frontend's state changes. */ static void frontend_changed(struct xenbus_device *dev, @@ -996,7 +996,7 @@ static int netback_remove(struct xenbus_device *dev) return 0; } -/** +/* * Entry point to this code when a new device is created. Allocate the basic * structures and switch to InitWait. */ diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index b01848ef4649..6ef2adbd283a 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -864,12 +864,10 @@ static u32 xennet_run_xdp(struct netfront_queue *queue, struct page *pdata, u32 act; int err; - xdp->data_hard_start = page_address(pdata); - xdp->data = xdp->data_hard_start + XDP_PACKET_HEADROOM; - xdp_set_data_meta_invalid(xdp); - xdp->data_end = xdp->data + len; - xdp->rxq = &queue->xdp_rxq; - xdp->frame_sz = XEN_PAGE_SIZE - XDP_PACKET_HEADROOM; + xdp_init_buff(xdp, XEN_PAGE_SIZE - XDP_PACKET_HEADROOM, + &queue->xdp_rxq); + xdp_prepare_buff(xdp, page_address(pdata), XDP_PACKET_HEADROOM, + len, false); act = bpf_prog_run_xdp(prog, xdp); switch (act) { @@ -1582,7 +1580,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) return ERR_PTR(err); } -/** +/* * Entry point to this code when a new device is created. Allocate the basic * structures and the ring buffers for communication with the backend, and * inform the backend of the appropriate details for those. @@ -1659,7 +1657,7 @@ static void xennet_disconnect_backend(struct netfront_info *info) } } -/** +/* * We are reconnecting to the backend, due to a suspend/resume, or a backend * driver restart. We tear down our netif structure and recreate it, but * leave the device-layer structures intact so that this is transparent to the @@ -2305,7 +2303,7 @@ static int xennet_connect(struct net_device *dev) return 0; } -/** +/* * Callback received when the backend's state changes. */ static void netback_changed(struct xenbus_device *dev, |