diff options
Diffstat (limited to 'drivers/net')
418 files changed, 22894 insertions, 8635 deletions
diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c index 8c651fdee039..57f1729066f2 100644 --- a/drivers/net/arcnet/arc-rawmode.c +++ b/drivers/net/arcnet/arc-rawmode.c @@ -186,4 +186,5 @@ static void __exit arcnet_raw_exit(void) module_init(arcnet_raw_init); module_exit(arcnet_raw_exit); +MODULE_DESCRIPTION("ARCnet raw mode packet interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index 8c3ccc7c83cd..53d10a04d1bd 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -312,6 +312,7 @@ module_param(node, int, 0); module_param(io, int, 0); module_param(irq, int, 0); module_param_string(device, device, sizeof(device), 0); +MODULE_DESCRIPTION("ARCnet COM90xx RIM I chipset driver"); MODULE_LICENSE("GPL"); static struct net_device *my_dev; diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index d9e052c49ba1..166bfc3c8e6c 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -108,6 +108,7 @@ static int go_tx(struct net_device *dev); static int debug = ARCNET_DEBUG; module_param(debug, int, 0); +MODULE_DESCRIPTION("ARCnet core driver"); MODULE_LICENSE("GPL"); static int __init arcnet_init(void) diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c index c09b567845e1..7a0a79973769 100644 --- a/drivers/net/arcnet/capmode.c +++ b/drivers/net/arcnet/capmode.c @@ -265,4 +265,5 @@ static void __exit capmode_module_exit(void) module_init(capmode_module_init); module_exit(capmode_module_exit); +MODULE_DESCRIPTION("ARCnet CAP mode packet interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 7b5c8bb02f11..c5e571ec94c9 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -61,6 +61,7 @@ module_param(timeout, int, 0); module_param(backplane, int, 0); module_param(clockp, int, 0); module_param(clockm, int, 0); +MODULE_DESCRIPTION("ARCnet COM20020 chipset PCI driver"); MODULE_LICENSE("GPL"); static void led_tx_set(struct led_classdev *led_cdev, diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index 06e1651b594b..a0053e3992a3 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -399,6 +399,7 @@ EXPORT_SYMBOL(com20020_found); EXPORT_SYMBOL(com20020_netdev_ops); #endif +MODULE_DESCRIPTION("ARCnet COM20020 chipset core driver"); MODULE_LICENSE("GPL"); #ifdef MODULE diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c index dc3253b318da..75f08aa7528b 100644 --- a/drivers/net/arcnet/com20020_cs.c +++ b/drivers/net/arcnet/com20020_cs.c @@ -97,6 +97,7 @@ module_param(backplane, int, 0); module_param(clockp, int, 0); module_param(clockm, int, 0); +MODULE_DESCRIPTION("ARCnet COM20020 chipset PCMCIA driver"); MODULE_LICENSE("GPL"); /*====================================================================*/ diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index 37b47749fc8b..3b463fbc6402 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -350,6 +350,7 @@ static char device[9]; /* use eg. device=arc1 to change name */ module_param_hw(io, int, ioport, 0); module_param_hw(irq, int, irq, 0); module_param_string(device, device, sizeof(device), 0); +MODULE_DESCRIPTION("ARCnet COM90xx IO mapped chipset driver"); MODULE_LICENSE("GPL"); #ifndef MODULE diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index f49dae194284..b3b287c16561 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -645,6 +645,7 @@ static void com90xx_copy_from_card(struct net_device *dev, int bufnum, TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); } +MODULE_DESCRIPTION("ARCnet COM90xx normal chipset driver"); MODULE_LICENSE("GPL"); static int __init com90xx_init(void) diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c index a7752a5b647f..46519ca63a0a 100644 --- a/drivers/net/arcnet/rfc1051.c +++ b/drivers/net/arcnet/rfc1051.c @@ -78,6 +78,7 @@ static void __exit arcnet_rfc1051_exit(void) module_init(arcnet_rfc1051_init); module_exit(arcnet_rfc1051_exit); +MODULE_DESCRIPTION("ARCNet packet format (RFC 1051) module"); MODULE_LICENSE("GPL"); /* Determine a packet's protocol ID. diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c index a4c856282674..0edf35d971c5 100644 --- a/drivers/net/arcnet/rfc1201.c +++ b/drivers/net/arcnet/rfc1201.c @@ -35,6 +35,7 @@ #include "arcdevice.h" +MODULE_DESCRIPTION("ARCNet packet format (RFC 1201) module"); MODULE_LICENSE("GPL"); static __be16 type_trans(struct sk_buff *skb, struct net_device *dev); diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index 31377bb1cc97..4db6122c9b43 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -760,23 +760,18 @@ static void bareudp_destroy_tunnels(struct net *net, struct list_head *head) unregister_netdevice_queue(bareudp->dev, head); } -static void __net_exit bareudp_exit_batch_net(struct list_head *net_list) +static void __net_exit bareudp_exit_batch_rtnl(struct list_head *net_list, + struct list_head *dev_kill_list) { struct net *net; - LIST_HEAD(list); - rtnl_lock(); list_for_each_entry(net, net_list, exit_list) - bareudp_destroy_tunnels(net, &list); - - /* unregister the devices gathered above */ - unregister_netdevice_many(&list); - rtnl_unlock(); + bareudp_destroy_tunnels(net, dev_kill_list); } static struct pernet_operations bareudp_net_ops = { .init = bareudp_init_net, - .exit_batch = bareudp_exit_batch_net, + .exit_batch_rtnl = bareudp_exit_batch_rtnl, .id = &bareudp_net_id, .size = sizeof(struct bareudp_net), }; diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index c99ffe6c683a..f2942e8c6c91 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -106,6 +106,9 @@ static void ad_agg_selection_logic(struct aggregator *aggregator, static void ad_clear_agg(struct aggregator *aggregator); static void ad_initialize_agg(struct aggregator *aggregator); static void ad_initialize_port(struct port *port, int lacp_fast); +static void ad_enable_collecting(struct port *port); +static void ad_disable_distributing(struct port *port, + bool *update_slave_arr); static void ad_enable_collecting_distributing(struct port *port, bool *update_slave_arr); static void ad_disable_collecting_distributing(struct port *port, @@ -172,8 +175,37 @@ static inline int __agg_has_partner(struct aggregator *agg) } /** + * __disable_distributing_port - disable the port's slave for distributing. + * Port will still be able to collect. + * @port: the port we're looking at + * + * This will disable only distributing on the port's slave. + */ +static void __disable_distributing_port(struct port *port) +{ + bond_set_slave_tx_disabled_flags(port->slave, BOND_SLAVE_NOTIFY_LATER); +} + +/** + * __enable_collecting_port - enable the port's slave for collecting, + * if it's up + * @port: the port we're looking at + * + * This will enable only collecting on the port's slave. + */ +static void __enable_collecting_port(struct port *port) +{ + struct slave *slave = port->slave; + + if (slave->link == BOND_LINK_UP && bond_slave_is_up(slave)) + bond_set_slave_rx_enabled_flags(slave, BOND_SLAVE_NOTIFY_LATER); +} + +/** * __disable_port - disable the port's slave * @port: the port we're looking at + * + * This will disable both collecting and distributing on the port's slave. */ static inline void __disable_port(struct port *port) { @@ -183,6 +215,8 @@ static inline void __disable_port(struct port *port) /** * __enable_port - enable the port's slave, if it's up * @port: the port we're looking at + * + * This will enable both collecting and distributing on the port's slave. */ static inline void __enable_port(struct port *port) { @@ -193,10 +227,27 @@ static inline void __enable_port(struct port *port) } /** - * __port_is_enabled - check if the port's slave is in active state + * __port_move_to_attached_state - check if port should transition back to attached + * state. + * @port: the port we're looking at + */ +static bool __port_move_to_attached_state(struct port *port) +{ + if (!(port->sm_vars & AD_PORT_SELECTED) || + (port->sm_vars & AD_PORT_STANDBY) || + !(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) || + !(port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) + port->sm_mux_state = AD_MUX_ATTACHED; + + return port->sm_mux_state == AD_MUX_ATTACHED; +} + +/** + * __port_is_collecting_distributing - check if the port's slave is in the + * combined collecting/distributing state * @port: the port we're looking at */ -static inline int __port_is_enabled(struct port *port) +static int __port_is_collecting_distributing(struct port *port) { return bond_is_active_slave(port->slave); } @@ -942,6 +993,7 @@ static int ad_marker_send(struct port *port, struct bond_marker *marker) */ static void ad_mux_machine(struct port *port, bool *update_slave_arr) { + struct bonding *bond = __get_bond_by_port(port); mux_states_t last_state; /* keep current State Machine state to compare later if it was @@ -999,9 +1051,13 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) { - if (port->aggregator->is_active) - port->sm_mux_state = - AD_MUX_COLLECTING_DISTRIBUTING; + if (port->aggregator->is_active) { + int state = AD_MUX_COLLECTING_DISTRIBUTING; + + if (!bond->params.coupled_control) + state = AD_MUX_COLLECTING; + port->sm_mux_state = state; + } } else if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY)) { /* if UNSELECTED or STANDBY */ @@ -1019,11 +1075,45 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) } break; case AD_MUX_COLLECTING_DISTRIBUTING: + if (!__port_move_to_attached_state(port)) { + /* if port state hasn't changed make + * sure that a collecting distributing + * port in an active aggregator is enabled + */ + if (port->aggregator->is_active && + !__port_is_collecting_distributing(port)) { + __enable_port(port); + *update_slave_arr = true; + } + } + break; + case AD_MUX_COLLECTING: + if (!__port_move_to_attached_state(port)) { + if ((port->sm_vars & AD_PORT_SELECTED) && + (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) && + (port->partner_oper.port_state & LACP_STATE_COLLECTING)) { + port->sm_mux_state = AD_MUX_DISTRIBUTING; + } else { + /* If port state hasn't changed, make sure that a collecting + * port is enabled for an active aggregator. + */ + struct slave *slave = port->slave; + + if (port->aggregator->is_active && + bond_is_slave_rx_disabled(slave)) { + ad_enable_collecting(port); + *update_slave_arr = true; + } + } + } + break; + case AD_MUX_DISTRIBUTING: if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY) || + !(port->partner_oper.port_state & LACP_STATE_COLLECTING) || !(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) || !(port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) { - port->sm_mux_state = AD_MUX_ATTACHED; + port->sm_mux_state = AD_MUX_COLLECTING; } else { /* if port state hasn't changed make * sure that a collecting distributing @@ -1031,7 +1121,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) */ if (port->aggregator && port->aggregator->is_active && - !__port_is_enabled(port)) { + !__port_is_collecting_distributing(port)) { __enable_port(port); *update_slave_arr = true; } @@ -1082,6 +1172,20 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) update_slave_arr); port->ntt = true; break; + case AD_MUX_COLLECTING: + port->actor_oper_port_state |= LACP_STATE_COLLECTING; + port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; + port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; + ad_enable_collecting(port); + ad_disable_distributing(port, update_slave_arr); + port->ntt = true; + break; + case AD_MUX_DISTRIBUTING: + port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING; + port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; + ad_enable_collecting_distributing(port, + update_slave_arr); + break; default: break; } @@ -1907,6 +2011,45 @@ static void ad_initialize_port(struct port *port, int lacp_fast) } /** + * ad_enable_collecting - enable a port's receive + * @port: the port we're looking at + * + * Enable @port if it's in an active aggregator + */ +static void ad_enable_collecting(struct port *port) +{ + if (port->aggregator->is_active) { + struct slave *slave = port->slave; + + slave_dbg(slave->bond->dev, slave->dev, + "Enabling collecting on port %d (LAG %d)\n", + port->actor_port_number, + port->aggregator->aggregator_identifier); + __enable_collecting_port(port); + } +} + +/** + * ad_disable_distributing - disable a port's transmit + * @port: the port we're looking at + * @update_slave_arr: Does slave array need update? + */ +static void ad_disable_distributing(struct port *port, bool *update_slave_arr) +{ + if (port->aggregator && + !MAC_ADDRESS_EQUAL(&port->aggregator->partner_system, + &(null_mac_addr))) { + slave_dbg(port->slave->bond->dev, port->slave->dev, + "Disabling distributing on port %d (LAG %d)\n", + port->actor_port_number, + port->aggregator->aggregator_identifier); + __disable_distributing_port(port); + /* Slave array needs an update */ + *update_slave_arr = true; + } +} + +/** * ad_enable_collecting_distributing - enable a port's transmit/receive * @port: the port we're looking at * @update_slave_arr: Does slave array need update? diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 4e0600c7b050..a8a6c5309518 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1819,6 +1819,8 @@ void bond_xdp_set_features(struct net_device *bond_dev) bond_for_each_slave(bond, slave, iter) val &= slave->dev->xdp_features; + val &= ~NETDEV_XDP_ACT_XSK_ZEROCOPY; + xdp_set_features_flag(bond_dev, val); } @@ -5909,9 +5911,6 @@ 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 (bond_xdp_check(bond)) - bond_dev->xdp_features = NETDEV_XDP_ACT_MASK; } /* Destroy a bonding device. @@ -6306,6 +6305,7 @@ static int __init bond_check_params(struct bond_params *params) params->ad_actor_sys_prio = ad_actor_sys_prio; eth_zero_addr(params->ad_actor_system); params->ad_user_port_key = ad_user_port_key; + params->coupled_control = 1; if (packets_per_slave > 0) { params->reciprocal_packets_per_slave = reciprocal_value(packets_per_slave); @@ -6415,28 +6415,41 @@ static int __net_init bond_net_init(struct net *net) return 0; } -static void __net_exit bond_net_exit_batch(struct list_head *net_list) +/* According to commit 69b0216ac255 ("bonding: fix bonding_masters + * race condition in bond unloading") we need to remove sysfs files + * before we remove our devices (done later in bond_net_exit_batch_rtnl()) + */ +static void __net_exit bond_net_pre_exit(struct net *net) +{ + struct bond_net *bn = net_generic(net, bond_net_id); + + bond_destroy_sysfs(bn); +} + +static void __net_exit bond_net_exit_batch_rtnl(struct list_head *net_list, + struct list_head *dev_kill_list) { struct bond_net *bn; struct net *net; - LIST_HEAD(list); - - list_for_each_entry(net, net_list, exit_list) { - bn = net_generic(net, bond_net_id); - bond_destroy_sysfs(bn); - } /* Kill off any bonds created after unregistering bond rtnl ops */ - rtnl_lock(); list_for_each_entry(net, net_list, exit_list) { struct bonding *bond, *tmp_bond; bn = net_generic(net, bond_net_id); list_for_each_entry_safe(bond, tmp_bond, &bn->dev_list, bond_list) - unregister_netdevice_queue(bond->dev, &list); + unregister_netdevice_queue(bond->dev, dev_kill_list); } - unregister_netdevice_many(&list); - rtnl_unlock(); +} + +/* According to commit 23fa5c2caae0 ("bonding: destroy proc directory + * only after all bonds are gone") bond_destroy_proc_dir() is called + * after bond_net_exit_batch_rtnl() has completed. + */ +static void __net_exit bond_net_exit_batch(struct list_head *net_list) +{ + struct bond_net *bn; + struct net *net; list_for_each_entry(net, net_list, exit_list) { bn = net_generic(net, bond_net_id); @@ -6446,6 +6459,8 @@ static void __net_exit bond_net_exit_batch(struct list_head *net_list) static struct pernet_operations bond_net_ops = { .init = bond_net_init, + .pre_exit = bond_net_pre_exit, + .exit_batch_rtnl = bond_net_exit_batch_rtnl, .exit_batch = bond_net_exit_batch, .id = &bond_net_id, .size = sizeof(struct bond_net), diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index cfa74cf8bb1a..29b4c3d1b9b6 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -122,6 +122,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_PEER_NOTIF_DELAY] = NLA_POLICY_FULL_RANGE(NLA_U32, &delay_range), [IFLA_BOND_MISSED_MAX] = { .type = NLA_U8 }, [IFLA_BOND_NS_IP6_TARGET] = { .type = NLA_NESTED }, + [IFLA_BOND_COUPLED_CONTROL] = { .type = NLA_U8 }, }; static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = { @@ -549,6 +550,16 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[], return err; } + if (data[IFLA_BOND_COUPLED_CONTROL]) { + int coupled_control = nla_get_u8(data[IFLA_BOND_COUPLED_CONTROL]); + + bond_opt_initval(&newval, coupled_control); + err = __bond_opt_set(bond, BOND_OPT_COUPLED_CONTROL, &newval, + data[IFLA_BOND_COUPLED_CONTROL], extack); + if (err) + return err; + } + return 0; } @@ -615,6 +626,7 @@ static size_t bond_get_size(const struct net_device *bond_dev) /* IFLA_BOND_NS_IP6_TARGET */ nla_total_size(sizeof(struct nlattr)) + nla_total_size(sizeof(struct in6_addr)) * BOND_MAX_NS_TARGETS + + nla_total_size(sizeof(u8)) + /* IFLA_BOND_COUPLED_CONTROL */ 0; } @@ -774,6 +786,10 @@ static int bond_fill_info(struct sk_buff *skb, bond->params.missed_max)) goto nla_put_failure; + if (nla_put_u8(skb, IFLA_BOND_COUPLED_CONTROL, + bond->params.coupled_control)) + goto nla_put_failure; + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info info; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index f3f27f0bd2a6..4cdbc7e084f4 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -84,7 +84,8 @@ static int bond_option_ad_user_port_key_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_missed_max_set(struct bonding *bond, const struct bond_opt_value *newval); - +static int bond_option_coupled_control_set(struct bonding *bond, + const struct bond_opt_value *newval); static const struct bond_opt_value bond_mode_tbl[] = { { "balance-rr", BOND_MODE_ROUNDROBIN, BOND_VALFLAG_DEFAULT}, @@ -232,6 +233,12 @@ static const struct bond_opt_value bond_missed_max_tbl[] = { { NULL, -1, 0}, }; +static const struct bond_opt_value bond_coupled_control_tbl[] = { + { "on", 1, BOND_VALFLAG_DEFAULT}, + { "off", 0, 0}, + { NULL, -1, 0}, +}; + static const struct bond_option bond_opts[BOND_OPT_LAST] = { [BOND_OPT_MODE] = { .id = BOND_OPT_MODE, @@ -496,6 +503,15 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = { .desc = "Delay between each peer notification on failover event, in milliseconds", .values = bond_peer_notif_delay_tbl, .set = bond_option_peer_notif_delay_set + }, + [BOND_OPT_COUPLED_CONTROL] = { + .id = BOND_OPT_COUPLED_CONTROL, + .name = "coupled_control", + .desc = "Opt into using coupled control MUX for LACP states", + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_8023AD)), + .flags = BOND_OPTFLAG_IFDOWN, + .values = bond_coupled_control_tbl, + .set = bond_option_coupled_control_set, } }; @@ -1692,3 +1708,13 @@ static int bond_option_ad_user_port_key_set(struct bonding *bond, bond->params.ad_user_port_key = newval->value; return 0; } + +static int bond_option_coupled_control_set(struct bonding *bond, + const struct bond_opt_value *newval) +{ + netdev_info(bond->dev, "Setting coupled_control to %s (%llu)\n", + newval->string, newval->value); + + bond->params.coupled_control = newval->value; + return 0; +} diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index eb410714afc2..620766eb6bc1 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -168,6 +168,7 @@ config CAN_KVASER_PCIEFD Kvaser Mini PCI Express 2xHS v2 Kvaser Mini PCI Express 1xCAN v3 Kvaser Mini PCI Express 2xCAN v3 + Kvaser M.2 PCIe 4xCAN config CAN_SLCAN tristate "Serial / USB serial CAN Adaptors (slcan)" @@ -218,6 +219,7 @@ config CAN_XILINXCAN source "drivers/net/can/c_can/Kconfig" source "drivers/net/can/cc770/Kconfig" source "drivers/net/can/ctucanfd/Kconfig" +source "drivers/net/can/esd/Kconfig" source "drivers/net/can/ifi_canfd/Kconfig" source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/mscan/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index ff8f76295d13..4669cd51e7bf 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_CAN_VXCAN) += vxcan.o obj-$(CONFIG_CAN_SLCAN) += slcan/ obj-y += dev/ +obj-y += esd/ obj-y += rcar/ obj-y += spi/ obj-y += usb/ diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c index 036d85ef07f5..dfdc039d92a6 100644 --- a/drivers/net/can/dev/netlink.c +++ b/drivers/net/can/dev/netlink.c @@ -346,7 +346,7 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[], /* Neither of TDC parameters nor TDC flags are * provided: do calculation */ - can_calc_tdco(&priv->tdc, priv->tdc_const, &priv->data_bittiming, + can_calc_tdco(&priv->tdc, priv->tdc_const, &dbt, &priv->ctrlmode, priv->ctrlmode_supported); } /* else: both CAN_CTRLMODE_TDC_{AUTO,MANUAL} are explicitly * turned off. TDC is disabled: do nothing diff --git a/drivers/net/can/esd/Kconfig b/drivers/net/can/esd/Kconfig new file mode 100644 index 000000000000..54bfc366634c --- /dev/null +++ b/drivers/net/can/esd/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +config CAN_ESD_402_PCI + tristate "esd electronics gmbh CAN-PCI(e)/402 family" + depends on PCI && HAS_DMA + help + Support for C402 card family from esd electronics gmbh. + This card family is based on the ESDACC CAN controller and + available in several form factors: PCI, PCIe, PCIe Mini, + M.2 PCIe, CPCIserial, PMC, XMC (see https://esd.eu/en) + + This driver can also be built as a module. In this case the + module will be called esd_402_pci. diff --git a/drivers/net/can/esd/Makefile b/drivers/net/can/esd/Makefile new file mode 100644 index 000000000000..5dd2d470c286 --- /dev/null +++ b/drivers/net/can/esd/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for esd gmbh ESDACC controller driver +# +esd_402_pci-objs := esdacc.o esd_402_pci-core.o + +obj-$(CONFIG_CAN_ESD_402_PCI) += esd_402_pci.o diff --git a/drivers/net/can/esd/esd_402_pci-core.c b/drivers/net/can/esd/esd_402_pci-core.c new file mode 100644 index 000000000000..b7cdcffd0e45 --- /dev/null +++ b/drivers/net/can/esd/esd_402_pci-core.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2015 - 2016 Thomas Körper, esd electronic system design gmbh + * Copyright (C) 2017 - 2023 Stefan Mätje, esd electronics gmbh + */ + +#include <linux/can/dev.h> +#include <linux/can.h> +#include <linux/can/netlink.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/ethtool.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/pci.h> + +#include "esdacc.h" + +#define ESD_PCI_DEVICE_ID_PCIE402 0x0402 + +#define PCI402_FPGA_VER_MIN 0x003d +#define PCI402_MAX_CORES 6 +#define PCI402_BAR 0 +#define PCI402_IO_OV_OFFS 0 +#define PCI402_IO_PCIEP_OFFS 0x10000 +#define PCI402_IO_LEN_TOTAL 0x20000 +#define PCI402_IO_LEN_CORE 0x2000 +#define PCI402_PCICFG_MSICAP 0x50 + +#define PCI402_DMA_MASK DMA_BIT_MASK(32) +#define PCI402_DMA_SIZE ALIGN(0x10000, PAGE_SIZE) + +#define PCI402_PCIEP_OF_INT_ENABLE 0x0050 +#define PCI402_PCIEP_OF_BM_ADDR_LO 0x1000 +#define PCI402_PCIEP_OF_BM_ADDR_HI 0x1004 +#define PCI402_PCIEP_OF_MSI_ADDR_LO 0x1008 +#define PCI402_PCIEP_OF_MSI_ADDR_HI 0x100c + +struct pci402_card { + /* Actually mapped io space, all other iomem derived from this */ + void __iomem *addr; + void __iomem *addr_pciep; + + void *dma_buf; + dma_addr_t dma_hnd; + + struct acc_ov ov; + struct acc_core *cores; + + bool msi_enabled; +}; + +/* The BTR register capabilities described by the can_bittiming_const structures + * below are valid since esdACC version 0x0032. + */ + +/* Used if the esdACC FPGA is built as CAN-Classic version. */ +static const struct can_bittiming_const pci402_bittiming_const = { + .name = "esd_402", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 512, + .brp_inc = 1, +}; + +/* Used if the esdACC FPGA is built as CAN-FD version. */ +static const struct can_bittiming_const pci402_bittiming_const_canfd = { + .name = "esd_402fd", + .tseg1_min = 1, + .tseg1_max = 256, + .tseg2_min = 1, + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +static const struct net_device_ops pci402_acc_netdev_ops = { + .ndo_open = acc_open, + .ndo_stop = acc_close, + .ndo_start_xmit = acc_start_xmit, + .ndo_change_mtu = can_change_mtu, + .ndo_eth_ioctl = can_eth_ioctl_hwts, +}; + +static const struct ethtool_ops pci402_acc_ethtool_ops = { + .get_ts_info = can_ethtool_op_get_ts_info_hwts, +}; + +static irqreturn_t pci402_interrupt(int irq, void *dev_id) +{ + struct pci_dev *pdev = dev_id; + struct pci402_card *card = pci_get_drvdata(pdev); + irqreturn_t irq_status; + + irq_status = acc_card_interrupt(&card->ov, card->cores); + + return irq_status; +} + +static int pci402_set_msiconfig(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + u32 addr_lo_offs = 0; + u32 addr_lo = 0; + u32 addr_hi = 0; + u32 data = 0; + u16 csr = 0; + int err; + + /* The FPGA hard IP PCIe core implements a 64-bit MSI Capability + * Register Format + */ + err = pci_read_config_word(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_FLAGS, &csr); + if (err) + goto failed; + + err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_ADDRESS_LO, + &addr_lo); + if (err) + goto failed; + err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_ADDRESS_HI, + &addr_hi); + if (err) + goto failed; + + err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_DATA_64, + &data); + if (err) + goto failed; + + addr_lo_offs = addr_lo & 0x0000ffff; + addr_lo &= 0xffff0000; + + if (addr_hi) + addr_lo |= 1; /* To enable 64-Bit addressing in PCIe endpoint */ + + if (!(csr & PCI_MSI_FLAGS_ENABLE)) { + err = -EINVAL; + goto failed; + } + + iowrite32(addr_lo, card->addr_pciep + PCI402_PCIEP_OF_MSI_ADDR_LO); + iowrite32(addr_hi, card->addr_pciep + PCI402_PCIEP_OF_MSI_ADDR_HI); + acc_ov_write32(&card->ov, ACC_OV_OF_MSI_ADDRESSOFFSET, addr_lo_offs); + acc_ov_write32(&card->ov, ACC_OV_OF_MSI_DATA, data); + + return 0; + +failed: + pci_warn(pdev, "Error while setting MSI configuration:\n" + "CSR: 0x%.4x, addr: 0x%.8x%.8x, offs: 0x%.4x, data: 0x%.8x\n", + csr, addr_hi, addr_lo, addr_lo_offs, data); + + return err; +} + +static int pci402_init_card(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + + card->ov.addr = card->addr + PCI402_IO_OV_OFFS; + card->addr_pciep = card->addr + PCI402_IO_PCIEP_OFFS; + + acc_reset_fpga(&card->ov); + acc_init_ov(&card->ov, &pdev->dev); + + if (card->ov.version < PCI402_FPGA_VER_MIN) { + pci_err(pdev, + "esdACC version (0x%.4x) outdated, please update\n", + card->ov.version); + return -EINVAL; + } + + if (card->ov.timestamp_frequency != ACC_TS_FREQ_80MHZ) { + pci_err(pdev, + "esdACC timestamp frequency of %uHz not supported by driver. Aborted.\n", + card->ov.timestamp_frequency); + return -EINVAL; + } + + if (card->ov.active_cores > PCI402_MAX_CORES) { + pci_err(pdev, + "Card with %u active cores not supported by driver. Aborted.\n", + card->ov.active_cores); + return -EINVAL; + } + card->cores = devm_kcalloc(&pdev->dev, card->ov.active_cores, + sizeof(struct acc_core), GFP_KERNEL); + if (!card->cores) + return -ENOMEM; + + if (card->ov.features & ACC_OV_REG_FEAT_MASK_CANFD) { + pci_warn(pdev, + "esdACC with CAN-FD feature detected. This driver doesn't support CAN-FD yet.\n"); + } + +#ifdef __LITTLE_ENDIAN + /* So card converts all busmastered data to LE for us: */ + acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE, + ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE); +#endif + + return 0; +} + +static int pci402_init_interrupt(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + int err; + + err = pci_enable_msi(pdev); + if (!err) { + err = pci402_set_msiconfig(pdev); + if (!err) { + card->msi_enabled = true; + acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE, + ACC_OV_REG_MODE_MASK_MSI_ENABLE); + pci_dbg(pdev, "MSI preparation done\n"); + } + } + + err = devm_request_irq(&pdev->dev, pdev->irq, pci402_interrupt, + IRQF_SHARED, dev_name(&pdev->dev), pdev); + if (err) + goto failure_msidis; + + iowrite32(1, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE); + + return 0; + +failure_msidis: + if (card->msi_enabled) { + acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE, + ACC_OV_REG_MODE_MASK_MSI_ENABLE); + pci_disable_msi(pdev); + card->msi_enabled = false; + } + + return err; +} + +static void pci402_finish_interrupt(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + + iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE); + devm_free_irq(&pdev->dev, pdev->irq, pdev); + + if (card->msi_enabled) { + acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE, + ACC_OV_REG_MODE_MASK_MSI_ENABLE); + pci_disable_msi(pdev); + card->msi_enabled = false; + } +} + +static int pci402_init_dma(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + int err; + + err = dma_set_coherent_mask(&pdev->dev, PCI402_DMA_MASK); + if (err) { + pci_err(pdev, "DMA set mask failed!\n"); + return err; + } + + /* The esdACC DMA engine needs the DMA buffer aligned to a 64k + * boundary. The DMA API guarantees to align the returned buffer to the + * smallest PAGE_SIZE order which is greater than or equal to the + * requested size. With PCI402_DMA_SIZE == 64kB this suffices here. + */ + card->dma_buf = dma_alloc_coherent(&pdev->dev, PCI402_DMA_SIZE, + &card->dma_hnd, GFP_KERNEL); + if (!card->dma_buf) + return -ENOMEM; + + acc_init_bm_ptr(&card->ov, card->cores, card->dma_buf); + + iowrite32(card->dma_hnd, + card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO); + iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI); + + pci_set_master(pdev); + + acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE, + ACC_OV_REG_MODE_MASK_BM_ENABLE); + + return 0; +} + +static void pci402_finish_dma(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + int i; + + acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE, + ACC_OV_REG_MODE_MASK_BM_ENABLE); + + pci_clear_master(pdev); + + iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO); + iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI); + + card->ov.bmfifo.messages = NULL; + card->ov.bmfifo.irq_cnt = NULL; + for (i = 0; i < card->ov.active_cores; i++) { + struct acc_core *core = &card->cores[i]; + + core->bmfifo.messages = NULL; + core->bmfifo.irq_cnt = NULL; + } + + dma_free_coherent(&pdev->dev, PCI402_DMA_SIZE, card->dma_buf, + card->dma_hnd); + card->dma_buf = NULL; +} + +static void pci402_unregister_core(struct acc_core *core) +{ + netdev_info(core->netdev, "unregister\n"); + unregister_candev(core->netdev); + + free_candev(core->netdev); + core->netdev = NULL; +} + +static int pci402_init_cores(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + int err; + int i; + + for (i = 0; i < card->ov.active_cores; i++) { + struct acc_core *core = &card->cores[i]; + struct acc_net_priv *priv; + struct net_device *netdev; + u32 fifo_config; + + core->addr = card->ov.addr + (i + 1) * PCI402_IO_LEN_CORE; + + fifo_config = acc_read32(core, ACC_CORE_OF_TXFIFO_CONFIG); + core->tx_fifo_size = (fifo_config >> 24); + if (core->tx_fifo_size <= 1) { + pci_err(pdev, "Invalid tx_fifo_size!\n"); + err = -EINVAL; + goto failure; + } + + netdev = alloc_candev(sizeof(*priv), core->tx_fifo_size); + if (!netdev) { + err = -ENOMEM; + goto failure; + } + core->netdev = netdev; + + netdev->flags |= IFF_ECHO; + netdev->dev_port = i; + netdev->netdev_ops = &pci402_acc_netdev_ops; + netdev->ethtool_ops = &pci402_acc_ethtool_ops; + SET_NETDEV_DEV(netdev, &pdev->dev); + + priv = netdev_priv(netdev); + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_CC_LEN8_DLC; + + priv->can.clock.freq = card->ov.core_frequency; + if (card->ov.features & ACC_OV_REG_FEAT_MASK_CANFD) + priv->can.bittiming_const = &pci402_bittiming_const_canfd; + else + priv->can.bittiming_const = &pci402_bittiming_const; + priv->can.do_set_bittiming = acc_set_bittiming; + priv->can.do_set_mode = acc_set_mode; + priv->can.do_get_berr_counter = acc_get_berr_counter; + + priv->core = core; + priv->ov = &card->ov; + + err = register_candev(netdev); + if (err) { + free_candev(core->netdev); + core->netdev = NULL; + goto failure; + } + + netdev_info(netdev, "registered\n"); + } + + return 0; + +failure: + for (i--; i >= 0; i--) + pci402_unregister_core(&card->cores[i]); + + return err; +} + +static void pci402_finish_cores(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + int i; + + for (i = 0; i < card->ov.active_cores; i++) + pci402_unregister_core(&card->cores[i]); +} + +static int pci402_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct pci402_card *card = NULL; + int err; + + err = pci_enable_device(pdev); + if (err) + return err; + + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + if (!card) { + err = -ENOMEM; + goto failure_disable_pci; + } + + pci_set_drvdata(pdev, card); + + err = pci_request_regions(pdev, pci_name(pdev)); + if (err) + goto failure_disable_pci; + + card->addr = pci_iomap(pdev, PCI402_BAR, PCI402_IO_LEN_TOTAL); + if (!card->addr) { + err = -ENOMEM; + goto failure_release_regions; + } + + err = pci402_init_card(pdev); + if (err) + goto failure_unmap; + + err = pci402_init_dma(pdev); + if (err) + goto failure_unmap; + + err = pci402_init_interrupt(pdev); + if (err) + goto failure_finish_dma; + + err = pci402_init_cores(pdev); + if (err) + goto failure_finish_interrupt; + + return 0; + +failure_finish_interrupt: + pci402_finish_interrupt(pdev); + +failure_finish_dma: + pci402_finish_dma(pdev); + +failure_unmap: + pci_iounmap(pdev, card->addr); + +failure_release_regions: + pci_release_regions(pdev); + +failure_disable_pci: + pci_disable_device(pdev); + + return err; +} + +static void pci402_remove(struct pci_dev *pdev) +{ + struct pci402_card *card = pci_get_drvdata(pdev); + + pci402_finish_interrupt(pdev); + pci402_finish_cores(pdev); + pci402_finish_dma(pdev); + pci_iounmap(pdev, card->addr); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static const struct pci_device_id pci402_tbl[] = { + { + .vendor = PCI_VENDOR_ID_ESDGMBH, + .device = ESD_PCI_DEVICE_ID_PCIE402, + .subvendor = PCI_VENDOR_ID_ESDGMBH, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, pci402_tbl); + +static struct pci_driver pci402_driver = { + .name = KBUILD_MODNAME, + .id_table = pci402_tbl, + .probe = pci402_probe, + .remove = pci402_remove, +}; +module_pci_driver(pci402_driver); + +MODULE_DESCRIPTION("Socket-CAN driver for esd CAN 402 card family with esdACC core on PCIe"); +MODULE_AUTHOR("Thomas Körper <socketcan@esd.eu>"); +MODULE_AUTHOR("Stefan Mätje <stefan.maetje@esd.eu>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/can/esd/esdacc.c b/drivers/net/can/esd/esdacc.c new file mode 100644 index 000000000000..121cbbf81458 --- /dev/null +++ b/drivers/net/can/esd/esdacc.c @@ -0,0 +1,764 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2015 - 2016 Thomas Körper, esd electronic system design gmbh + * Copyright (C) 2017 - 2023 Stefan Mätje, esd electronics gmbh + */ + +#include "esdacc.h" + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/ktime.h> + +/* esdACC ID register layout */ +#define ACC_ID_ID_MASK GENMASK(28, 0) +#define ACC_ID_EFF_FLAG BIT(29) + +/* esdACC DLC register layout */ +#define ACC_DLC_DLC_MASK GENMASK(3, 0) +#define ACC_DLC_RTR_FLAG BIT(4) +#define ACC_DLC_TXD_FLAG BIT(5) + +/* ecc value of esdACC equals SJA1000's ECC register */ +#define ACC_ECC_SEG 0x1f +#define ACC_ECC_DIR 0x20 +#define ACC_ECC_BIT 0x00 +#define ACC_ECC_FORM 0x40 +#define ACC_ECC_STUFF 0x80 +#define ACC_ECC_MASK 0xc0 + +/* esdACC Status Register bits. Unused bits not documented. */ +#define ACC_REG_STATUS_MASK_STATUS_ES BIT(17) +#define ACC_REG_STATUS_MASK_STATUS_EP BIT(18) +#define ACC_REG_STATUS_MASK_STATUS_BS BIT(19) + +/* esdACC Overview Module BM_IRQ_Mask register related defines */ +/* Two bit wide command masks to mask or unmask a single core IRQ */ +#define ACC_BM_IRQ_UNMASK BIT(0) +#define ACC_BM_IRQ_MASK (ACC_BM_IRQ_UNMASK << 1) +/* Command to unmask all IRQ sources. Created by shifting + * and oring the two bit wide ACC_BM_IRQ_UNMASK 16 times. + */ +#define ACC_BM_IRQ_UNMASK_ALL 0x55555555U + +static void acc_resetmode_enter(struct acc_core *core) +{ + acc_set_bits(core, ACC_CORE_OF_CTRL_MODE, + ACC_REG_CONTROL_MASK_MODE_RESETMODE); + + /* Read back reset mode bit to flush PCI write posting */ + acc_resetmode_entered(core); +} + +static void acc_resetmode_leave(struct acc_core *core) +{ + acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE, + ACC_REG_CONTROL_MASK_MODE_RESETMODE); + + /* Read back reset mode bit to flush PCI write posting */ + acc_resetmode_entered(core); +} + +static void acc_txq_put(struct acc_core *core, u32 acc_id, u8 acc_dlc, + const void *data) +{ + acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_1, + *((const u32 *)(data + 4))); + acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_0, + *((const u32 *)data)); + acc_write32(core, ACC_CORE_OF_TXFIFO_DLC, acc_dlc); + /* CAN id must be written at last. This write starts TX. */ + acc_write32(core, ACC_CORE_OF_TXFIFO_ID, acc_id); +} + +static u8 acc_tx_fifo_next(struct acc_core *core, u8 tx_fifo_idx) +{ + ++tx_fifo_idx; + if (tx_fifo_idx >= core->tx_fifo_size) + tx_fifo_idx = 0U; + return tx_fifo_idx; +} + +/* Convert timestamp from esdACC time stamp ticks to ns + * + * The conversion factor ts2ns from time stamp counts to ns is basically + * ts2ns = NSEC_PER_SEC / timestamp_frequency + * + * We handle here only a fixed timestamp frequency of 80MHz. The + * resulting ts2ns factor would be 12.5. + * + * At the end we multiply by 12 and add the half of the HW timestamp + * to get a multiplication by 12.5. This way any overflow is + * avoided until ktime_t itself overflows. + */ +#define ACC_TS_FACTOR (NSEC_PER_SEC / ACC_TS_FREQ_80MHZ) +#define ACC_TS_80MHZ_SHIFT 1 + +static ktime_t acc_ts2ktime(struct acc_ov *ov, u64 ts) +{ + u64 ns; + + ns = (ts * ACC_TS_FACTOR) + (ts >> ACC_TS_80MHZ_SHIFT); + + return ns_to_ktime(ns); +} + +#undef ACC_TS_FACTOR +#undef ACC_TS_80MHZ_SHIFT + +void acc_init_ov(struct acc_ov *ov, struct device *dev) +{ + u32 temp; + + temp = acc_ov_read32(ov, ACC_OV_OF_VERSION); + ov->version = temp; + ov->features = (temp >> 16); + + temp = acc_ov_read32(ov, ACC_OV_OF_INFO); + ov->total_cores = temp; + ov->active_cores = (temp >> 8); + + ov->core_frequency = acc_ov_read32(ov, ACC_OV_OF_CANCORE_FREQ); + ov->timestamp_frequency = acc_ov_read32(ov, ACC_OV_OF_TS_FREQ_LO); + + /* Depending on esdACC feature NEW_PSC enable the new prescaler + * or adjust core_frequency according to the implicit division by 2. + */ + if (ov->features & ACC_OV_REG_FEAT_MASK_NEW_PSC) { + acc_ov_set_bits(ov, ACC_OV_OF_MODE, + ACC_OV_REG_MODE_MASK_NEW_PSC_ENABLE); + } else { + ov->core_frequency /= 2; + } + + dev_dbg(dev, + "esdACC v%u, freq: %u/%u, feat/strap: 0x%x/0x%x, cores: %u/%u\n", + ov->version, ov->core_frequency, ov->timestamp_frequency, + ov->features, acc_ov_read32(ov, ACC_OV_OF_INFO) >> 16, + ov->active_cores, ov->total_cores); +} + +void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores, const void *mem) +{ + unsigned int u; + + /* DMA buffer layout as follows where N is the number of CAN cores + * implemented in the FPGA, i.e. N = ov->total_cores + * + * Section Layout Section size + * ---------------------------------------------- + * FIFO Card/Overview ACC_CORE_DMABUF_SIZE + * FIFO Core0 ACC_CORE_DMABUF_SIZE + * ... ... + * FIFO CoreN ACC_CORE_DMABUF_SIZE + * irq_cnt Card/Overview sizeof(u32) + * irq_cnt Core0 sizeof(u32) + * ... ... + * irq_cnt CoreN sizeof(u32) + */ + ov->bmfifo.messages = mem; + ov->bmfifo.irq_cnt = mem + (ov->total_cores + 1U) * ACC_CORE_DMABUF_SIZE; + + for (u = 0U; u < ov->active_cores; u++) { + struct acc_core *core = &cores[u]; + + core->bmfifo.messages = mem + (u + 1U) * ACC_CORE_DMABUF_SIZE; + core->bmfifo.irq_cnt = ov->bmfifo.irq_cnt + (u + 1U); + } +} + +int acc_open(struct net_device *netdev) +{ + struct acc_net_priv *priv = netdev_priv(netdev); + struct acc_core *core = priv->core; + u32 tx_fifo_status; + u32 ctrl_mode; + int err; + + /* Retry to enter RESET mode if out of sync. */ + if (priv->can.state != CAN_STATE_STOPPED) { + netdev_warn(netdev, "Entered %s() with bad can.state: %s\n", + __func__, can_get_state_str(priv->can.state)); + acc_resetmode_enter(core); + priv->can.state = CAN_STATE_STOPPED; + } + + err = open_candev(netdev); + if (err) + return err; + + ctrl_mode = ACC_REG_CONTROL_MASK_IE_RXTX | + ACC_REG_CONTROL_MASK_IE_TXERROR | + ACC_REG_CONTROL_MASK_IE_ERRWARN | + ACC_REG_CONTROL_MASK_IE_OVERRUN | + ACC_REG_CONTROL_MASK_IE_ERRPASS; + + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + ctrl_mode |= ACC_REG_CONTROL_MASK_IE_BUSERR; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + ctrl_mode |= ACC_REG_CONTROL_MASK_MODE_LOM; + + acc_set_bits(core, ACC_CORE_OF_CTRL_MODE, ctrl_mode); + + acc_resetmode_leave(core); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Resync TX FIFO indices to HW state after (re-)start. */ + tx_fifo_status = acc_read32(core, ACC_CORE_OF_TXFIFO_STATUS); + core->tx_fifo_head = tx_fifo_status & 0xff; + core->tx_fifo_tail = (tx_fifo_status >> 8) & 0xff; + + netif_start_queue(netdev); + return 0; +} + +int acc_close(struct net_device *netdev) +{ + struct acc_net_priv *priv = netdev_priv(netdev); + struct acc_core *core = priv->core; + + acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE, + ACC_REG_CONTROL_MASK_IE_RXTX | + ACC_REG_CONTROL_MASK_IE_TXERROR | + ACC_REG_CONTROL_MASK_IE_ERRWARN | + ACC_REG_CONTROL_MASK_IE_OVERRUN | + ACC_REG_CONTROL_MASK_IE_ERRPASS | + ACC_REG_CONTROL_MASK_IE_BUSERR); + + netif_stop_queue(netdev); + acc_resetmode_enter(core); + priv->can.state = CAN_STATE_STOPPED; + + /* Mark pending TX requests to be aborted after controller restart. */ + acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff); + + /* ACC_REG_CONTROL_MASK_MODE_LOM is only accessible in RESET mode */ + acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE, + ACC_REG_CONTROL_MASK_MODE_LOM); + + close_candev(netdev); + return 0; +} + +netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct acc_net_priv *priv = netdev_priv(netdev); + struct acc_core *core = priv->core; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 tx_fifo_head = core->tx_fifo_head; + int fifo_usage; + u32 acc_id; + u8 acc_dlc; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* Access core->tx_fifo_tail only once because it may be changed + * from the interrupt level. + */ + fifo_usage = tx_fifo_head - core->tx_fifo_tail; + if (fifo_usage < 0) + fifo_usage += core->tx_fifo_size; + + if (fifo_usage >= core->tx_fifo_size - 1) { + netdev_err(core->netdev, + "BUG: TX ring full when queue awake!\n"); + netif_stop_queue(netdev); + return NETDEV_TX_BUSY; + } + + if (fifo_usage == core->tx_fifo_size - 2) + netif_stop_queue(netdev); + + acc_dlc = can_get_cc_dlc(cf, priv->can.ctrlmode); + if (cf->can_id & CAN_RTR_FLAG) + acc_dlc |= ACC_DLC_RTR_FLAG; + + if (cf->can_id & CAN_EFF_FLAG) { + acc_id = cf->can_id & CAN_EFF_MASK; + acc_id |= ACC_ID_EFF_FLAG; + } else { + acc_id = cf->can_id & CAN_SFF_MASK; + } + + can_put_echo_skb(skb, netdev, core->tx_fifo_head, 0); + + core->tx_fifo_head = acc_tx_fifo_next(core, tx_fifo_head); + + acc_txq_put(core, acc_id, acc_dlc, cf->data); + + return NETDEV_TX_OK; +} + +int acc_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct acc_net_priv *priv = netdev_priv(netdev); + u32 core_status = acc_read32(priv->core, ACC_CORE_OF_STATUS); + + bec->txerr = (core_status >> 8) & 0xff; + bec->rxerr = core_status & 0xff; + + return 0; +} + +int acc_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct acc_net_priv *priv = netdev_priv(netdev); + + switch (mode) { + case CAN_MODE_START: + /* Paranoid FIFO index check. */ + { + const u32 tx_fifo_status = + acc_read32(priv->core, ACC_CORE_OF_TXFIFO_STATUS); + const u8 hw_fifo_head = tx_fifo_status; + + if (hw_fifo_head != priv->core->tx_fifo_head || + hw_fifo_head != priv->core->tx_fifo_tail) { + netdev_warn(netdev, + "TX FIFO mismatch: T %2u H %2u; TFHW %#08x\n", + priv->core->tx_fifo_tail, + priv->core->tx_fifo_head, + tx_fifo_status); + } + } + acc_resetmode_leave(priv->core); + /* To leave the bus-off state the esdACC controller begins + * here a grace period where it counts 128 "idle conditions" (each + * of 11 consecutive recessive bits) on the bus as required + * by the CAN spec. + * + * During this time the TX FIFO may still contain already + * aborted "zombie" frames that are only drained from the FIFO + * at the end of the grace period. + * + * To not to interfere with this drain process we don't + * call netif_wake_queue() here. When the controller reaches + * the error-active state again, it informs us about that + * with an acc_bmmsg_errstatechange message. Then + * netif_wake_queue() is called from + * handle_core_msg_errstatechange() instead. + */ + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +int acc_set_bittiming(struct net_device *netdev) +{ + struct acc_net_priv *priv = netdev_priv(netdev); + const struct can_bittiming *bt = &priv->can.bittiming; + u32 brp; + u32 btr; + + if (priv->ov->features & ACC_OV_REG_FEAT_MASK_CANFD) { + u32 fbtr = 0; + + netdev_dbg(netdev, "bit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n", + bt->brp, bt->prop_seg, + bt->phase_seg1, bt->phase_seg2, bt->sjw); + + brp = FIELD_PREP(ACC_REG_BRP_FD_MASK_BRP, bt->brp - 1); + + btr = FIELD_PREP(ACC_REG_BTR_FD_MASK_TSEG1, bt->phase_seg1 + bt->prop_seg - 1); + btr |= FIELD_PREP(ACC_REG_BTR_FD_MASK_TSEG2, bt->phase_seg2 - 1); + btr |= FIELD_PREP(ACC_REG_BTR_FD_MASK_SJW, bt->sjw - 1); + + /* Keep order of accesses to ACC_CORE_OF_BRP and ACC_CORE_OF_BTR. */ + acc_write32(priv->core, ACC_CORE_OF_BRP, brp); + acc_write32(priv->core, ACC_CORE_OF_BTR, btr); + + netdev_dbg(netdev, "esdACC: BRP %u, NBTR 0x%08x, DBTR 0x%08x", + brp, btr, fbtr); + } else { + netdev_dbg(netdev, "bit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n", + bt->brp, bt->prop_seg, + bt->phase_seg1, bt->phase_seg2, bt->sjw); + + brp = FIELD_PREP(ACC_REG_BRP_CL_MASK_BRP, bt->brp - 1); + + btr = FIELD_PREP(ACC_REG_BTR_CL_MASK_TSEG1, bt->phase_seg1 + bt->prop_seg - 1); + btr |= FIELD_PREP(ACC_REG_BTR_CL_MASK_TSEG2, bt->phase_seg2 - 1); + btr |= FIELD_PREP(ACC_REG_BTR_CL_MASK_SJW, bt->sjw - 1); + + /* Keep order of accesses to ACC_CORE_OF_BRP and ACC_CORE_OF_BTR. */ + acc_write32(priv->core, ACC_CORE_OF_BRP, brp); + acc_write32(priv->core, ACC_CORE_OF_BTR, btr); + + netdev_dbg(netdev, "esdACC: BRP %u, BTR 0x%08x", brp, btr); + } + + return 0; +} + +static void handle_core_msg_rxtxdone(struct acc_core *core, + const struct acc_bmmsg_rxtxdone *msg) +{ + struct acc_net_priv *priv = netdev_priv(core->netdev); + struct net_device_stats *stats = &core->netdev->stats; + struct sk_buff *skb; + + if (msg->acc_dlc.len & ACC_DLC_TXD_FLAG) { + u8 tx_fifo_tail = core->tx_fifo_tail; + + if (core->tx_fifo_head == tx_fifo_tail) { + netdev_warn(core->netdev, + "TX interrupt, but queue is empty!?\n"); + return; + } + + /* Direct access echo skb to attach HW time stamp. */ + skb = priv->can.echo_skb[tx_fifo_tail]; + if (skb) { + skb_hwtstamps(skb)->hwtstamp = + acc_ts2ktime(priv->ov, msg->ts); + } + + stats->tx_packets++; + stats->tx_bytes += can_get_echo_skb(core->netdev, tx_fifo_tail, + NULL); + + core->tx_fifo_tail = acc_tx_fifo_next(core, tx_fifo_tail); + + netif_wake_queue(core->netdev); + + } else { + struct can_frame *cf; + + skb = alloc_can_skb(core->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + cf->can_id = msg->id & ACC_ID_ID_MASK; + if (msg->id & ACC_ID_EFF_FLAG) + cf->can_id |= CAN_EFF_FLAG; + + can_frame_set_cc_len(cf, msg->acc_dlc.len & ACC_DLC_DLC_MASK, + priv->can.ctrlmode); + + if (msg->acc_dlc.len & ACC_DLC_RTR_FLAG) { + cf->can_id |= CAN_RTR_FLAG; + } else { + memcpy(cf->data, msg->data, cf->len); + stats->rx_bytes += cf->len; + } + stats->rx_packets++; + + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); + + netif_rx(skb); + } +} + +static void handle_core_msg_txabort(struct acc_core *core, + const struct acc_bmmsg_txabort *msg) +{ + struct net_device_stats *stats = &core->netdev->stats; + u8 tx_fifo_tail = core->tx_fifo_tail; + u32 abort_mask = msg->abort_mask; /* u32 extend to avoid warnings later */ + + /* The abort_mask shows which frames were aborted in esdACC's FIFO. */ + while (tx_fifo_tail != core->tx_fifo_head && (abort_mask)) { + const u32 tail_mask = (1U << tx_fifo_tail); + + if (!(abort_mask & tail_mask)) + break; + abort_mask &= ~tail_mask; + + can_free_echo_skb(core->netdev, tx_fifo_tail, NULL); + stats->tx_dropped++; + stats->tx_aborted_errors++; + + tx_fifo_tail = acc_tx_fifo_next(core, tx_fifo_tail); + } + core->tx_fifo_tail = tx_fifo_tail; + if (abort_mask) + netdev_warn(core->netdev, "Unhandled aborted messages\n"); + + if (!acc_resetmode_entered(core)) + netif_wake_queue(core->netdev); +} + +static void handle_core_msg_overrun(struct acc_core *core, + const struct acc_bmmsg_overrun *msg) +{ + struct acc_net_priv *priv = netdev_priv(core->netdev); + struct net_device_stats *stats = &core->netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* lost_cnt may be 0 if not supported by esdACC version */ + if (msg->lost_cnt) { + stats->rx_errors += msg->lost_cnt; + stats->rx_over_errors += msg->lost_cnt; + } else { + stats->rx_errors++; + stats->rx_over_errors++; + } + + skb = alloc_can_err_skb(core->netdev, &cf); + if (!skb) + return; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); + + netif_rx(skb); +} + +static void handle_core_msg_buserr(struct acc_core *core, + const struct acc_bmmsg_buserr *msg) +{ + struct acc_net_priv *priv = netdev_priv(core->netdev); + struct net_device_stats *stats = &core->netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + const u32 reg_status = msg->reg_status; + const u8 rxerr = reg_status; + const u8 txerr = (reg_status >> 8); + u8 can_err_prot_type = 0U; + + priv->can.can_stats.bus_error++; + + /* Error occurred during transmission? */ + if (msg->ecc & ACC_ECC_DIR) { + stats->rx_errors++; + } else { + can_err_prot_type |= CAN_ERR_PROT_TX; + stats->tx_errors++; + } + /* Determine error type */ + switch (msg->ecc & ACC_ECC_MASK) { + case ACC_ECC_BIT: + can_err_prot_type |= CAN_ERR_PROT_BIT; + break; + case ACC_ECC_FORM: + can_err_prot_type |= CAN_ERR_PROT_FORM; + break; + case ACC_ECC_STUFF: + can_err_prot_type |= CAN_ERR_PROT_STUFF; + break; + default: + can_err_prot_type |= CAN_ERR_PROT_UNSPEC; + break; + } + + skb = alloc_can_err_skb(core->netdev, &cf); + if (!skb) + return; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_CNT; + + /* Set protocol error type */ + cf->data[2] = can_err_prot_type; + /* Set error location */ + cf->data[3] = msg->ecc & ACC_ECC_SEG; + + /* Insert CAN TX and RX error counters. */ + cf->data[6] = txerr; + cf->data[7] = rxerr; + + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); + + netif_rx(skb); +} + +static void +handle_core_msg_errstatechange(struct acc_core *core, + const struct acc_bmmsg_errstatechange *msg) +{ + struct acc_net_priv *priv = netdev_priv(core->netdev); + struct can_frame *cf = NULL; + struct sk_buff *skb; + const u32 reg_status = msg->reg_status; + const u8 rxerr = reg_status; + const u8 txerr = (reg_status >> 8); + enum can_state new_state; + + if (reg_status & ACC_REG_STATUS_MASK_STATUS_BS) { + new_state = CAN_STATE_BUS_OFF; + } else if (reg_status & ACC_REG_STATUS_MASK_STATUS_EP) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (reg_status & ACC_REG_STATUS_MASK_STATUS_ES) { + new_state = CAN_STATE_ERROR_WARNING; + } else { + new_state = CAN_STATE_ERROR_ACTIVE; + if (priv->can.state == CAN_STATE_BUS_OFF) { + /* See comment in acc_set_mode() for CAN_MODE_START */ + netif_wake_queue(core->netdev); + } + } + + skb = alloc_can_err_skb(core->netdev, &cf); + + if (new_state != priv->can.state) { + enum can_state tx_state, rx_state; + + tx_state = (txerr >= rxerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + rx_state = (rxerr >= txerr) ? + new_state : CAN_STATE_ERROR_ACTIVE; + + /* Always call can_change_state() to update the state + * even if alloc_can_err_skb() may have failed. + * can_change_state() can cope with a NULL cf pointer. + */ + can_change_state(core->netdev, cf, tx_state, rx_state); + } + + if (skb) { + cf->can_id |= CAN_ERR_CNT; + cf->data[6] = txerr; + cf->data[7] = rxerr; + + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); + + netif_rx(skb); + } + + if (new_state == CAN_STATE_BUS_OFF) { + acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff); + can_bus_off(core->netdev); + } +} + +static void handle_core_interrupt(struct acc_core *core) +{ + u32 msg_fifo_head = core->bmfifo.local_irq_cnt & 0xff; + + while (core->bmfifo.msg_fifo_tail != msg_fifo_head) { + const union acc_bmmsg *msg = + &core->bmfifo.messages[core->bmfifo.msg_fifo_tail]; + + switch (msg->msg_id) { + case BM_MSG_ID_RXTXDONE: + handle_core_msg_rxtxdone(core, &msg->rxtxdone); + break; + + case BM_MSG_ID_TXABORT: + handle_core_msg_txabort(core, &msg->txabort); + break; + + case BM_MSG_ID_OVERRUN: + handle_core_msg_overrun(core, &msg->overrun); + break; + + case BM_MSG_ID_BUSERR: + handle_core_msg_buserr(core, &msg->buserr); + break; + + case BM_MSG_ID_ERRPASSIVE: + case BM_MSG_ID_ERRWARN: + handle_core_msg_errstatechange(core, + &msg->errstatechange); + break; + + default: + /* Ignore all other BM messages (like the CAN-FD messages) */ + break; + } + + core->bmfifo.msg_fifo_tail = + (core->bmfifo.msg_fifo_tail + 1) & 0xff; + } +} + +/** + * acc_card_interrupt() - handle the interrupts of an esdACC FPGA + * + * @ov: overview module structure + * @cores: array of core structures + * + * This function handles all interrupts pending for the overview module and the + * CAN cores of the esdACC FPGA. + * + * It examines for all cores (the overview module core and the CAN cores) + * the bmfifo.irq_cnt and compares it with the previously saved + * bmfifo.local_irq_cnt. An IRQ is pending if they differ. The esdACC FPGA + * updates the bmfifo.irq_cnt values by DMA. + * + * The pending interrupts are masked by writing to the IRQ mask register at + * ACC_OV_OF_BM_IRQ_MASK. This register has for each core a two bit command + * field evaluated as follows: + * + * Define, bit pattern: meaning + * 00: no action + * ACC_BM_IRQ_UNMASK, 01: unmask interrupt + * ACC_BM_IRQ_MASK, 10: mask interrupt + * 11: no action + * + * For each CAN core with a pending IRQ handle_core_interrupt() handles all + * busmaster messages from the message FIFO. The last handled message (FIFO + * index) is written to the CAN core to acknowledge its handling. + * + * Last step is to unmask all interrupts in the FPGA using + * ACC_BM_IRQ_UNMASK_ALL. + * + * Return: + * IRQ_HANDLED, if card generated an interrupt that was handled + * IRQ_NONE, if the interrupt is not ours + */ +irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores) +{ + u32 irqmask; + int i; + + /* First we look for whom interrupts are pending, card/overview + * or any of the cores. Two bits in irqmask are used for each; + * Each two bit field is set to ACC_BM_IRQ_MASK if an IRQ is + * pending. + */ + irqmask = 0U; + if (READ_ONCE(*ov->bmfifo.irq_cnt) != ov->bmfifo.local_irq_cnt) { + irqmask |= ACC_BM_IRQ_MASK; + ov->bmfifo.local_irq_cnt = READ_ONCE(*ov->bmfifo.irq_cnt); + } + + for (i = 0; i < ov->active_cores; i++) { + struct acc_core *core = &cores[i]; + + if (READ_ONCE(*core->bmfifo.irq_cnt) != core->bmfifo.local_irq_cnt) { + irqmask |= (ACC_BM_IRQ_MASK << (2 * (i + 1))); + core->bmfifo.local_irq_cnt = READ_ONCE(*core->bmfifo.irq_cnt); + } + } + + if (!irqmask) + return IRQ_NONE; + + /* At second we tell the card we're working on them by writing irqmask, + * call handle_{ov|core}_interrupt and then acknowledge the + * interrupts by writing irq_cnt: + */ + acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, irqmask); + + if (irqmask & ACC_BM_IRQ_MASK) { + /* handle_ov_interrupt(); - no use yet. */ + acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_COUNTER, + ov->bmfifo.local_irq_cnt); + } + + for (i = 0; i < ov->active_cores; i++) { + struct acc_core *core = &cores[i]; + + if (irqmask & (ACC_BM_IRQ_MASK << (2 * (i + 1)))) { + handle_core_interrupt(core); + acc_write32(core, ACC_OV_OF_BM_IRQ_COUNTER, + core->bmfifo.local_irq_cnt); + } + } + + acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, ACC_BM_IRQ_UNMASK_ALL); + + return IRQ_HANDLED; +} diff --git a/drivers/net/can/esd/esdacc.h b/drivers/net/can/esd/esdacc.h new file mode 100644 index 000000000000..a70488b25d39 --- /dev/null +++ b/drivers/net/can/esd/esdacc.h @@ -0,0 +1,356 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2015 - 2016 Thomas Körper, esd electronic system design gmbh + * Copyright (C) 2017 - 2023 Stefan Mätje, esd electronics gmbh + */ + +#include <linux/bits.h> +#include <linux/can/dev.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/units.h> + +#define ACC_TS_FREQ_80MHZ (80 * HZ_PER_MHZ) +#define ACC_I2C_ADDON_DETECT_DELAY_MS 10 + +/* esdACC Overview Module */ +#define ACC_OV_OF_PROBE 0x0000 +#define ACC_OV_OF_VERSION 0x0004 +#define ACC_OV_OF_INFO 0x0008 +#define ACC_OV_OF_CANCORE_FREQ 0x000c +#define ACC_OV_OF_TS_FREQ_LO 0x0010 +#define ACC_OV_OF_TS_FREQ_HI 0x0014 +#define ACC_OV_OF_IRQ_STATUS_CORES 0x0018 +#define ACC_OV_OF_TS_CURR_LO 0x001c +#define ACC_OV_OF_TS_CURR_HI 0x0020 +#define ACC_OV_OF_IRQ_STATUS 0x0028 +#define ACC_OV_OF_MODE 0x002c +#define ACC_OV_OF_BM_IRQ_COUNTER 0x0070 +#define ACC_OV_OF_BM_IRQ_MASK 0x0074 +#define ACC_OV_OF_MSI_DATA 0x0080 +#define ACC_OV_OF_MSI_ADDRESSOFFSET 0x0084 + +/* Feature flags are contained in the upper 16 bit of the version + * register at ACC_OV_OF_VERSION but only used with these masks after + * extraction into an extra variable => (xx - 16). + */ +#define ACC_OV_REG_FEAT_MASK_CANFD BIT(27 - 16) +#define ACC_OV_REG_FEAT_MASK_NEW_PSC BIT(28 - 16) + +#define ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE BIT(0) +#define ACC_OV_REG_MODE_MASK_BM_ENABLE BIT(1) +#define ACC_OV_REG_MODE_MASK_MODE_LED BIT(2) +#define ACC_OV_REG_MODE_MASK_TIMER_ENABLE BIT(4) +#define ACC_OV_REG_MODE_MASK_TIMER_ONE_SHOT BIT(5) +#define ACC_OV_REG_MODE_MASK_TIMER_ABSOLUTE BIT(6) +#define ACC_OV_REG_MODE_MASK_TIMER GENMASK(6, 4) +#define ACC_OV_REG_MODE_MASK_TS_SRC GENMASK(8, 7) +#define ACC_OV_REG_MODE_MASK_I2C_ENABLE BIT(11) +#define ACC_OV_REG_MODE_MASK_MSI_ENABLE BIT(14) +#define ACC_OV_REG_MODE_MASK_NEW_PSC_ENABLE BIT(15) +#define ACC_OV_REG_MODE_MASK_FPGA_RESET BIT(31) + +/* esdACC CAN Core Module */ +#define ACC_CORE_OF_CTRL_MODE 0x0000 +#define ACC_CORE_OF_STATUS_IRQ 0x0008 +#define ACC_CORE_OF_BRP 0x000c +#define ACC_CORE_OF_BTR 0x0010 +#define ACC_CORE_OF_FBTR 0x0014 +#define ACC_CORE_OF_STATUS 0x0030 +#define ACC_CORE_OF_TXFIFO_CONFIG 0x0048 +#define ACC_CORE_OF_TXFIFO_STATUS 0x004c +#define ACC_CORE_OF_TX_STATUS_IRQ 0x0050 +#define ACC_CORE_OF_TX_ABORT_MASK 0x0054 +#define ACC_CORE_OF_BM_IRQ_COUNTER 0x0070 +#define ACC_CORE_OF_TXFIFO_ID 0x00c0 +#define ACC_CORE_OF_TXFIFO_DLC 0x00c4 +#define ACC_CORE_OF_TXFIFO_DATA_0 0x00c8 +#define ACC_CORE_OF_TXFIFO_DATA_1 0x00cc + +#define ACC_REG_CONTROL_MASK_MODE_RESETMODE BIT(0) +#define ACC_REG_CONTROL_MASK_MODE_LOM BIT(1) +#define ACC_REG_CONTROL_MASK_MODE_STM BIT(2) +#define ACC_REG_CONTROL_MASK_MODE_TRANSEN BIT(5) +#define ACC_REG_CONTROL_MASK_MODE_TS BIT(6) +#define ACC_REG_CONTROL_MASK_MODE_SCHEDULE BIT(7) + +#define ACC_REG_CONTROL_MASK_IE_RXTX BIT(8) +#define ACC_REG_CONTROL_MASK_IE_TXERROR BIT(9) +#define ACC_REG_CONTROL_MASK_IE_ERRWARN BIT(10) +#define ACC_REG_CONTROL_MASK_IE_OVERRUN BIT(11) +#define ACC_REG_CONTROL_MASK_IE_TSI BIT(12) +#define ACC_REG_CONTROL_MASK_IE_ERRPASS BIT(13) +#define ACC_REG_CONTROL_MASK_IE_ALI BIT(14) +#define ACC_REG_CONTROL_MASK_IE_BUSERR BIT(15) + +/* BRP and BTR register layout for CAN-Classic version */ +#define ACC_REG_BRP_CL_MASK_BRP GENMASK(8, 0) +#define ACC_REG_BTR_CL_MASK_TSEG1 GENMASK(3, 0) +#define ACC_REG_BTR_CL_MASK_TSEG2 GENMASK(18, 16) +#define ACC_REG_BTR_CL_MASK_SJW GENMASK(25, 24) + +/* BRP and BTR register layout for CAN-FD version */ +#define ACC_REG_BRP_FD_MASK_BRP GENMASK(7, 0) +#define ACC_REG_BTR_FD_MASK_TSEG1 GENMASK(7, 0) +#define ACC_REG_BTR_FD_MASK_TSEG2 GENMASK(22, 16) +#define ACC_REG_BTR_FD_MASK_SJW GENMASK(30, 24) + +/* 256 BM_MSGs of 32 byte size */ +#define ACC_CORE_DMAMSG_SIZE 32U +#define ACC_CORE_DMABUF_SIZE (256U * ACC_CORE_DMAMSG_SIZE) + +enum acc_bmmsg_id { + BM_MSG_ID_RXTXDONE = 0x01, + BM_MSG_ID_TXABORT = 0x02, + BM_MSG_ID_OVERRUN = 0x03, + BM_MSG_ID_BUSERR = 0x04, + BM_MSG_ID_ERRPASSIVE = 0x05, + BM_MSG_ID_ERRWARN = 0x06, + BM_MSG_ID_TIMESLICE = 0x07, + BM_MSG_ID_HWTIMER = 0x08, + BM_MSG_ID_HOTPLUG = 0x09, +}; + +/* The struct acc_bmmsg_* structure declarations that follow here provide + * access to the ring buffer of bus master messages maintained by the FPGA + * bus master engine. All bus master messages have the same size of + * ACC_CORE_DMAMSG_SIZE and a minimum alignment of ACC_CORE_DMAMSG_SIZE in + * memory. + * + * All structure members are natural aligned. Therefore we should not need + * a __packed attribute. All struct acc_bmmsg_* declarations have at least + * reserved* members to fill the structure to the full ACC_CORE_DMAMSG_SIZE. + * + * A failure of this property due padding will be detected at compile time + * by static_assert(sizeof(union acc_bmmsg) == ACC_CORE_DMAMSG_SIZE). + */ + +struct acc_bmmsg_rxtxdone { + u8 msg_id; + u8 txfifo_level; + u8 reserved1[2]; + u8 txtsfifo_level; + u8 reserved2[3]; + u32 id; + struct { + u8 len; + u8 txdfifo_idx; + u8 zeroes8; + u8 reserved; + } acc_dlc; + u8 data[CAN_MAX_DLEN]; + /* Time stamps in struct acc_ov::timestamp_frequency ticks. */ + u64 ts; +}; + +struct acc_bmmsg_txabort { + u8 msg_id; + u8 txfifo_level; + u16 abort_mask; + u8 txtsfifo_level; + u8 reserved2[1]; + u16 abort_mask_txts; + u64 ts; + u32 reserved3[4]; +}; + +struct acc_bmmsg_overrun { + u8 msg_id; + u8 txfifo_level; + u8 lost_cnt; + u8 reserved1; + u8 txtsfifo_level; + u8 reserved2[3]; + u64 ts; + u32 reserved3[4]; +}; + +struct acc_bmmsg_buserr { + u8 msg_id; + u8 txfifo_level; + u8 ecc; + u8 reserved1; + u8 txtsfifo_level; + u8 reserved2[3]; + u64 ts; + u32 reg_status; + u32 reg_btr; + u32 reserved3[2]; +}; + +struct acc_bmmsg_errstatechange { + u8 msg_id; + u8 txfifo_level; + u8 reserved1[2]; + u8 txtsfifo_level; + u8 reserved2[3]; + u64 ts; + u32 reg_status; + u32 reserved3[3]; +}; + +struct acc_bmmsg_timeslice { + u8 msg_id; + u8 txfifo_level; + u8 reserved1[2]; + u8 txtsfifo_level; + u8 reserved2[3]; + u64 ts; + u32 reserved3[4]; +}; + +struct acc_bmmsg_hwtimer { + u8 msg_id; + u8 reserved1[3]; + u32 reserved2[1]; + u64 timer; + u32 reserved3[4]; +}; + +struct acc_bmmsg_hotplug { + u8 msg_id; + u8 reserved1[3]; + u32 reserved2[7]; +}; + +union acc_bmmsg { + u8 msg_id; + struct acc_bmmsg_rxtxdone rxtxdone; + struct acc_bmmsg_txabort txabort; + struct acc_bmmsg_overrun overrun; + struct acc_bmmsg_buserr buserr; + struct acc_bmmsg_errstatechange errstatechange; + struct acc_bmmsg_timeslice timeslice; + struct acc_bmmsg_hwtimer hwtimer; +}; + +/* Check size of union acc_bmmsg to be of expected size. */ +static_assert(sizeof(union acc_bmmsg) == ACC_CORE_DMAMSG_SIZE); + +struct acc_bmfifo { + const union acc_bmmsg *messages; + /* irq_cnt points to an u32 value where the esdACC FPGA deposits + * the bm_fifo head index in coherent DMA memory. Only bits 7..0 + * are valid. Use READ_ONCE() to access this memory location. + */ + const u32 *irq_cnt; + u32 local_irq_cnt; + u32 msg_fifo_tail; +}; + +struct acc_core { + void __iomem *addr; + struct net_device *netdev; + struct acc_bmfifo bmfifo; + u8 tx_fifo_size; + u8 tx_fifo_head; + u8 tx_fifo_tail; +}; + +struct acc_ov { + void __iomem *addr; + struct acc_bmfifo bmfifo; + u32 timestamp_frequency; + u32 core_frequency; + u16 version; + u16 features; + u8 total_cores; + u8 active_cores; +}; + +struct acc_net_priv { + struct can_priv can; /* must be the first member! */ + struct acc_core *core; + struct acc_ov *ov; +}; + +static inline u32 acc_read32(struct acc_core *core, unsigned short offs) +{ + return ioread32be(core->addr + offs); +} + +static inline void acc_write32(struct acc_core *core, + unsigned short offs, u32 v) +{ + iowrite32be(v, core->addr + offs); +} + +static inline void acc_write32_noswap(struct acc_core *core, + unsigned short offs, u32 v) +{ + iowrite32(v, core->addr + offs); +} + +static inline void acc_set_bits(struct acc_core *core, + unsigned short offs, u32 mask) +{ + u32 v = acc_read32(core, offs); + + v |= mask; + acc_write32(core, offs, v); +} + +static inline void acc_clear_bits(struct acc_core *core, + unsigned short offs, u32 mask) +{ + u32 v = acc_read32(core, offs); + + v &= ~mask; + acc_write32(core, offs, v); +} + +static inline int acc_resetmode_entered(struct acc_core *core) +{ + u32 ctrl = acc_read32(core, ACC_CORE_OF_CTRL_MODE); + + return (ctrl & ACC_REG_CONTROL_MASK_MODE_RESETMODE) != 0; +} + +static inline u32 acc_ov_read32(struct acc_ov *ov, unsigned short offs) +{ + return ioread32be(ov->addr + offs); +} + +static inline void acc_ov_write32(struct acc_ov *ov, + unsigned short offs, u32 v) +{ + iowrite32be(v, ov->addr + offs); +} + +static inline void acc_ov_set_bits(struct acc_ov *ov, + unsigned short offs, u32 b) +{ + u32 v = acc_ov_read32(ov, offs); + + v |= b; + acc_ov_write32(ov, offs, v); +} + +static inline void acc_ov_clear_bits(struct acc_ov *ov, + unsigned short offs, u32 b) +{ + u32 v = acc_ov_read32(ov, offs); + + v &= ~b; + acc_ov_write32(ov, offs, v); +} + +static inline void acc_reset_fpga(struct acc_ov *ov) +{ + acc_ov_write32(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_FPGA_RESET); + + /* (Re-)start and wait for completion of addon detection on the I^2C bus */ + acc_ov_set_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE); + mdelay(ACC_I2C_ADDON_DETECT_DELAY_MS); +} + +void acc_init_ov(struct acc_ov *ov, struct device *dev); +void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores, + const void *mem); +int acc_open(struct net_device *netdev); +int acc_close(struct net_device *netdev); +netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev); +int acc_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec); +int acc_set_mode(struct net_device *netdev, enum can_mode mode); +int acc_set_bittiming(struct net_device *netdev); +irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores); diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index a57005faa04f..416f10480b40 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -47,12 +47,18 @@ MODULE_DESCRIPTION("CAN driver for Kvaser CAN/PCIe devices"); #define KVASER_PCIEFD_MINIPCIE_2CAN_V3_DEVICE_ID 0x0015 #define KVASER_PCIEFD_MINIPCIE_1CAN_V3_DEVICE_ID 0x0016 +/* Xilinx based devices */ +#define KVASER_PCIEFD_M2_4CAN_DEVICE_ID 0x0017 + /* Altera SerDes Enable 64-bit DMA address translation */ #define KVASER_PCIEFD_ALTERA_DMA_64BIT BIT(0) /* SmartFusion2 SerDes LSB address translation mask */ #define KVASER_PCIEFD_SF2_DMA_LSB_MASK GENMASK(31, 12) +/* Xilinx SerDes LSB address translation mask */ +#define KVASER_PCIEFD_XILINX_DMA_LSB_MASK GENMASK(31, 12) + /* Kvaser KCAN CAN controller registers */ #define KVASER_PCIEFD_KCAN_FIFO_REG 0x100 #define KVASER_PCIEFD_KCAN_FIFO_LAST_REG 0x180 @@ -281,6 +287,8 @@ static void kvaser_pciefd_write_dma_map_altera(struct kvaser_pciefd *pcie, dma_addr_t addr, int index); static void kvaser_pciefd_write_dma_map_sf2(struct kvaser_pciefd *pcie, dma_addr_t addr, int index); +static void kvaser_pciefd_write_dma_map_xilinx(struct kvaser_pciefd *pcie, + dma_addr_t addr, int index); struct kvaser_pciefd_address_offset { u32 serdes; @@ -335,6 +343,18 @@ static const struct kvaser_pciefd_address_offset kvaser_pciefd_sf2_address_offse .kcan_ch1 = 0x142000, }; +static const struct kvaser_pciefd_address_offset kvaser_pciefd_xilinx_address_offset = { + .serdes = 0x00208, + .pci_ien = 0x102004, + .pci_irq = 0x102008, + .sysid = 0x100000, + .loopback = 0x103000, + .kcan_srb_fifo = 0x120000, + .kcan_srb = 0x121000, + .kcan_ch0 = 0x140000, + .kcan_ch1 = 0x142000, +}; + static const struct kvaser_pciefd_irq_mask kvaser_pciefd_altera_irq_mask = { .kcan_rx0 = BIT(4), .kcan_tx = { BIT(0), BIT(1), BIT(2), BIT(3) }, @@ -347,6 +367,12 @@ static const struct kvaser_pciefd_irq_mask kvaser_pciefd_sf2_irq_mask = { .all = GENMASK(19, 16) | BIT(4), }; +static const struct kvaser_pciefd_irq_mask kvaser_pciefd_xilinx_irq_mask = { + .kcan_rx0 = BIT(4), + .kcan_tx = { BIT(16), BIT(17), BIT(18), BIT(19) }, + .all = GENMASK(19, 16) | BIT(4), +}; + static const struct kvaser_pciefd_dev_ops kvaser_pciefd_altera_dev_ops = { .kvaser_pciefd_write_dma_map = kvaser_pciefd_write_dma_map_altera, }; @@ -355,6 +381,10 @@ static const struct kvaser_pciefd_dev_ops kvaser_pciefd_sf2_dev_ops = { .kvaser_pciefd_write_dma_map = kvaser_pciefd_write_dma_map_sf2, }; +static const struct kvaser_pciefd_dev_ops kvaser_pciefd_xilinx_dev_ops = { + .kvaser_pciefd_write_dma_map = kvaser_pciefd_write_dma_map_xilinx, +}; + static const struct kvaser_pciefd_driver_data kvaser_pciefd_altera_driver_data = { .address_offset = &kvaser_pciefd_altera_address_offset, .irq_mask = &kvaser_pciefd_altera_irq_mask, @@ -367,6 +397,12 @@ static const struct kvaser_pciefd_driver_data kvaser_pciefd_sf2_driver_data = { .ops = &kvaser_pciefd_sf2_dev_ops, }; +static const struct kvaser_pciefd_driver_data kvaser_pciefd_xilinx_driver_data = { + .address_offset = &kvaser_pciefd_xilinx_address_offset, + .irq_mask = &kvaser_pciefd_xilinx_irq_mask, + .ops = &kvaser_pciefd_xilinx_dev_ops, +}; + struct kvaser_pciefd_can { struct can_priv can; struct kvaser_pciefd *kv_pcie; @@ -457,6 +493,10 @@ static struct pci_device_id kvaser_pciefd_id_table[] = { .driver_data = (kernel_ulong_t)&kvaser_pciefd_sf2_driver_data, }, { + PCI_DEVICE(KVASER_PCIEFD_VENDOR, KVASER_PCIEFD_M2_4CAN_DEVICE_ID), + .driver_data = (kernel_ulong_t)&kvaser_pciefd_xilinx_driver_data, + }, + { 0, }, }; @@ -1035,6 +1075,21 @@ static void kvaser_pciefd_write_dma_map_sf2(struct kvaser_pciefd *pcie, iowrite32(msb, serdes_base + 0x4); } +static void kvaser_pciefd_write_dma_map_xilinx(struct kvaser_pciefd *pcie, + dma_addr_t addr, int index) +{ + void __iomem *serdes_base; + u32 lsb = addr & KVASER_PCIEFD_XILINX_DMA_LSB_MASK; + u32 msb = 0x0; + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + msb = addr >> 32; +#endif + serdes_base = KVASER_PCIEFD_SERDES_ADDR(pcie) + 0x8 * index; + iowrite32(msb, serdes_base); + iowrite32(lsb, serdes_base + 0x4); +} + static int kvaser_pciefd_setup_dma(struct kvaser_pciefd *pcie) { int i; diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 16ecc11c7f62..14b231c4d7ec 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -255,6 +255,7 @@ enum m_can_reg { #define TXESC_TBDS_64B 0x7 /* Tx Event FIFO Configuration (TXEFC) */ +#define TXEFC_EFWM_MASK GENMASK(29, 24) #define TXEFC_EFS_MASK GENMASK(21, 16) /* Tx Event FIFO Status (TXEFS) */ @@ -320,6 +321,12 @@ struct id_and_dlc { u32 dlc; }; +struct m_can_fifo_element { + u32 id; + u32 dlc; + u8 data[CANFD_MAX_DLEN]; +}; + static inline u32 m_can_read(struct m_can_classdev *cdev, enum m_can_reg reg) { return cdev->ops->read_reg(cdev, reg); @@ -372,16 +379,6 @@ m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val) return cdev->ops->read_fifo(cdev, addr_offset, val, 1); } -static inline bool _m_can_tx_fifo_full(u32 txfqs) -{ - return !!(txfqs & TXFQS_TFQF); -} - -static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev) -{ - return _m_can_tx_fifo_full(m_can_read(cdev, M_CAN_TXFQS)); -} - static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable) { u32 cccr = m_can_read(cdev, M_CAN_CCCR); @@ -416,15 +413,48 @@ static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable) } } +static void m_can_interrupt_enable(struct m_can_classdev *cdev, u32 interrupts) +{ + if (cdev->active_interrupts == interrupts) + return; + cdev->ops->write_reg(cdev, M_CAN_IE, interrupts); + cdev->active_interrupts = interrupts; +} + +static void m_can_coalescing_disable(struct m_can_classdev *cdev) +{ + u32 new_interrupts = cdev->active_interrupts | IR_RF0N | IR_TEFN; + + if (!cdev->net->irq) + return; + + hrtimer_cancel(&cdev->hrtimer); + m_can_interrupt_enable(cdev, new_interrupts); +} + static inline void m_can_enable_all_interrupts(struct m_can_classdev *cdev) { + if (!cdev->net->irq) { + dev_dbg(cdev->dev, "Start hrtimer\n"); + hrtimer_start(&cdev->hrtimer, + ms_to_ktime(HRTIMER_POLL_INTERVAL_MS), + HRTIMER_MODE_REL_PINNED); + } + /* Only interrupt line 0 is used in this driver */ m_can_write(cdev, M_CAN_ILE, ILE_EINT0); } static inline void m_can_disable_all_interrupts(struct m_can_classdev *cdev) { + m_can_coalescing_disable(cdev); m_can_write(cdev, M_CAN_ILE, 0x0); + cdev->active_interrupts = 0x0; + + if (!cdev->net->irq) { + dev_dbg(cdev->dev, "Stop hrtimer\n"); + hrtimer_cancel(&cdev->hrtimer); + } } /* Retrieve internal timestamp counter from TSCV.TSC, and shift it to 32-bit @@ -444,18 +474,26 @@ static u32 m_can_get_timestamp(struct m_can_classdev *cdev) static void m_can_clean(struct net_device *net) { struct m_can_classdev *cdev = netdev_priv(net); + unsigned long irqflags; - if (cdev->tx_skb) { - int putidx = 0; + if (cdev->tx_ops) { + for (int i = 0; i != cdev->tx_fifo_size; ++i) { + if (!cdev->tx_ops[i].skb) + continue; - net->stats.tx_errors++; - if (cdev->version > 30) - putidx = FIELD_GET(TXFQS_TFQPI_MASK, - m_can_read(cdev, M_CAN_TXFQS)); - - can_free_echo_skb(cdev->net, putidx, NULL); - cdev->tx_skb = NULL; + net->stats.tx_errors++; + cdev->tx_ops[i].skb = NULL; + } } + + for (int i = 0; i != cdev->can.echo_skb_max; ++i) + can_free_echo_skb(cdev->net, i, NULL); + + netdev_reset_queue(cdev->net); + + spin_lock_irqsave(&cdev->tx_handling_spinlock, irqflags); + cdev->tx_fifo_in_flight = 0; + spin_unlock_irqrestore(&cdev->tx_handling_spinlock, irqflags); } /* For peripherals, pass skb to rx-offload, which will push skb from @@ -1007,23 +1045,60 @@ static int m_can_poll(struct napi_struct *napi, int quota) * echo. timestamp is used for peripherals to ensure correct ordering * by rx-offload, and is ignored for non-peripherals. */ -static void m_can_tx_update_stats(struct m_can_classdev *cdev, - unsigned int msg_mark, - u32 timestamp) +static unsigned int m_can_tx_update_stats(struct m_can_classdev *cdev, + unsigned int msg_mark, u32 timestamp) { struct net_device *dev = cdev->net; struct net_device_stats *stats = &dev->stats; + unsigned int frame_len; if (cdev->is_peripheral) stats->tx_bytes += can_rx_offload_get_echo_skb_queue_timestamp(&cdev->offload, msg_mark, timestamp, - NULL); + &frame_len); else - stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); + stats->tx_bytes += can_get_echo_skb(dev, msg_mark, &frame_len); stats->tx_packets++; + + return frame_len; +} + +static void m_can_finish_tx(struct m_can_classdev *cdev, int transmitted, + unsigned int transmitted_frame_len) +{ + unsigned long irqflags; + + netdev_completed_queue(cdev->net, transmitted, transmitted_frame_len); + + spin_lock_irqsave(&cdev->tx_handling_spinlock, irqflags); + if (cdev->tx_fifo_in_flight >= cdev->tx_fifo_size && transmitted > 0) + netif_wake_queue(cdev->net); + cdev->tx_fifo_in_flight -= transmitted; + spin_unlock_irqrestore(&cdev->tx_handling_spinlock, irqflags); +} + +static netdev_tx_t m_can_start_tx(struct m_can_classdev *cdev) +{ + unsigned long irqflags; + int tx_fifo_in_flight; + + spin_lock_irqsave(&cdev->tx_handling_spinlock, irqflags); + tx_fifo_in_flight = cdev->tx_fifo_in_flight + 1; + if (tx_fifo_in_flight >= cdev->tx_fifo_size) { + netif_stop_queue(cdev->net); + if (tx_fifo_in_flight > cdev->tx_fifo_size) { + netdev_err_once(cdev->net, "hard_xmit called while TX FIFO full\n"); + spin_unlock_irqrestore(&cdev->tx_handling_spinlock, irqflags); + return NETDEV_TX_BUSY; + } + } + cdev->tx_fifo_in_flight = tx_fifo_in_flight; + spin_unlock_irqrestore(&cdev->tx_handling_spinlock, irqflags); + + return NETDEV_TX_OK; } static int m_can_echo_tx_event(struct net_device *dev) @@ -1035,6 +1110,8 @@ static int m_can_echo_tx_event(struct net_device *dev) int i = 0; int err = 0; unsigned int msg_mark; + int processed = 0; + unsigned int processed_frame_len = 0; struct m_can_classdev *cdev = netdev_priv(dev); @@ -1063,25 +1140,62 @@ static int m_can_echo_tx_event(struct net_device *dev) fgi = (++fgi >= cdev->mcfg[MRAM_TXE].num ? 0 : fgi); /* update stats */ - m_can_tx_update_stats(cdev, msg_mark, timestamp); + processed_frame_len += m_can_tx_update_stats(cdev, msg_mark, + timestamp); + + ++processed; } if (ack_fgi != -1) m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK, ack_fgi)); + m_can_finish_tx(cdev, processed, processed_frame_len); + return err; } +static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir) +{ + u32 new_interrupts = cdev->active_interrupts; + bool enable_rx_timer = false; + bool enable_tx_timer = false; + + if (!cdev->net->irq) + return; + + if (cdev->rx_coalesce_usecs_irq > 0 && (ir & (IR_RF0N | IR_RF0W))) { + enable_rx_timer = true; + new_interrupts &= ~IR_RF0N; + } + if (cdev->tx_coalesce_usecs_irq > 0 && (ir & (IR_TEFN | IR_TEFW))) { + enable_tx_timer = true; + new_interrupts &= ~IR_TEFN; + } + if (!enable_rx_timer && !hrtimer_active(&cdev->hrtimer)) + new_interrupts |= IR_RF0N; + if (!enable_tx_timer && !hrtimer_active(&cdev->hrtimer)) + new_interrupts |= IR_TEFN; + + m_can_interrupt_enable(cdev, new_interrupts); + if (enable_rx_timer | enable_tx_timer) + hrtimer_start(&cdev->hrtimer, cdev->irq_timer_wait, + HRTIMER_MODE_REL); +} + static irqreturn_t m_can_isr(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct m_can_classdev *cdev = netdev_priv(dev); u32 ir; - if (pm_runtime_suspended(cdev->dev)) + if (pm_runtime_suspended(cdev->dev)) { + m_can_coalescing_disable(cdev); return IRQ_NONE; + } + ir = m_can_read(cdev, M_CAN_IR); + m_can_coalescing_update(cdev, ir); if (!ir) return IRQ_NONE; @@ -1096,13 +1210,17 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) * - state change IRQ * - bus error IRQ and bus error reporting */ - if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) { + if (ir & (IR_RF0N | IR_RF0W | IR_ERR_ALL_30X)) { cdev->irqstatus = ir; if (!cdev->is_peripheral) { m_can_disable_all_interrupts(cdev); napi_schedule(&cdev->napi); - } else if (m_can_rx_peripheral(dev, ir) < 0) { - goto out_fail; + } else { + int pkts; + + pkts = m_can_rx_peripheral(dev, ir); + if (pkts < 0) + goto out_fail; } } @@ -1110,21 +1228,18 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) if (ir & IR_TC) { /* Transmission Complete Interrupt*/ u32 timestamp = 0; + unsigned int frame_len; if (cdev->is_peripheral) timestamp = m_can_get_timestamp(cdev); - m_can_tx_update_stats(cdev, 0, timestamp); - netif_wake_queue(dev); + frame_len = m_can_tx_update_stats(cdev, 0, timestamp); + m_can_finish_tx(cdev, 1, frame_len); } } else { - if (ir & IR_TEFN) { + if (ir & (IR_TEFN | IR_TEFW)) { /* New TX FIFO Element arrived */ if (m_can_echo_tx_event(dev) != 0) goto out_fail; - - if (netif_queue_stopped(dev) && - !m_can_tx_fifo_full(cdev)) - netif_wake_queue(dev); } } @@ -1138,6 +1253,15 @@ out_fail: return IRQ_HANDLED; } +static enum hrtimer_restart m_can_coalescing_timer(struct hrtimer *timer) +{ + struct m_can_classdev *cdev = container_of(timer, struct m_can_classdev, hrtimer); + + irq_wake_thread(cdev->net->irq, cdev->net); + + return HRTIMER_NORESTART; +} + static const struct can_bittiming_const m_can_bittiming_const_30X = { .name = KBUILD_MODNAME, .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ @@ -1276,9 +1400,8 @@ static int m_can_chip_config(struct net_device *dev) } /* Disable unused interrupts */ - interrupts &= ~(IR_ARA | IR_ELO | IR_DRX | IR_TEFF | IR_TEFW | IR_TFE | - IR_TCF | IR_HPM | IR_RF1F | IR_RF1W | IR_RF1N | - IR_RF0F | IR_RF0W); + interrupts &= ~(IR_ARA | IR_ELO | IR_DRX | IR_TEFF | IR_TFE | IR_TCF | + IR_HPM | IR_RF1F | IR_RF1W | IR_RF1N | IR_RF0F); m_can_config_endisable(cdev, true); @@ -1315,6 +1438,8 @@ static int m_can_chip_config(struct net_device *dev) } else { /* Full TX Event FIFO is used */ m_can_write(cdev, M_CAN_TXEFC, + FIELD_PREP(TXEFC_EFWM_MASK, + cdev->tx_max_coalesced_frames_irq) | FIELD_PREP(TXEFC_EFS_MASK, cdev->mcfg[MRAM_TXE].num) | cdev->mcfg[MRAM_TXE].off); @@ -1322,6 +1447,7 @@ static int m_can_chip_config(struct net_device *dev) /* rx fifo configuration, blocking mode, fifo size 1 */ m_can_write(cdev, M_CAN_RXF0C, + FIELD_PREP(RXFC_FWM_MASK, cdev->rx_max_coalesced_frames_irq) | FIELD_PREP(RXFC_FS_MASK, cdev->mcfg[MRAM_RXF0].num) | cdev->mcfg[MRAM_RXF0].off); @@ -1380,7 +1506,7 @@ static int m_can_chip_config(struct net_device *dev) else interrupts &= ~(IR_ERR_LEC_31X); } - m_can_write(cdev, M_CAN_IE, interrupts); + m_can_interrupt_enable(cdev, interrupts); /* route all interrupts to INT0 */ m_can_write(cdev, M_CAN_ILS, ILS_ALL_INT0); @@ -1413,15 +1539,16 @@ static int m_can_start(struct net_device *dev) if (ret) return ret; + netdev_queue_set_dql_min_limit(netdev_get_tx_queue(cdev->net, 0), + cdev->tx_max_coalesced_frames); + cdev->can.state = CAN_STATE_ERROR_ACTIVE; m_can_enable_all_interrupts(cdev); - if (!dev->irq) { - dev_dbg(cdev->dev, "Start hrtimer\n"); - hrtimer_start(&cdev->hrtimer, ms_to_ktime(HRTIMER_POLL_INTERVAL_MS), - HRTIMER_MODE_REL_PINNED); - } + if (cdev->version > 30) + cdev->tx_fifo_putidx = FIELD_GET(TXFQS_TFQPI_MASK, + m_can_read(cdev, M_CAN_TXFQS)); return 0; } @@ -1577,11 +1704,6 @@ static void m_can_stop(struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); - if (!dev->irq) { - dev_dbg(cdev->dev, "Stop hrtimer\n"); - hrtimer_cancel(&cdev->hrtimer); - } - /* disable all interrupts */ m_can_disable_all_interrupts(cdev); @@ -1605,8 +1727,9 @@ static int m_can_close(struct net_device *dev) m_can_clk_stop(cdev); free_irq(dev->irq, dev); + m_can_clean(dev); + if (cdev->is_peripheral) { - cdev->tx_skb = NULL; destroy_workqueue(cdev->tx_wq); cdev->tx_wq = NULL; can_rx_offload_disable(&cdev->offload); @@ -1619,57 +1742,42 @@ static int m_can_close(struct net_device *dev) return 0; } -static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx) -{ - struct m_can_classdev *cdev = netdev_priv(dev); - /*get wrap around for loopback skb index */ - unsigned int wrap = cdev->can.echo_skb_max; - int next_idx; - - /* calculate next index */ - next_idx = (++putidx >= wrap ? 0 : putidx); - - /* check if occupied */ - return !!cdev->can.echo_skb[next_idx]; -} - -static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) +static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev, + struct sk_buff *skb) { - struct canfd_frame *cf = (struct canfd_frame *)cdev->tx_skb->data; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u8 len_padded = DIV_ROUND_UP(cf->len, 4); + struct m_can_fifo_element fifo_element; struct net_device *dev = cdev->net; - struct sk_buff *skb = cdev->tx_skb; - struct id_and_dlc fifo_header; u32 cccr, fdflags; - u32 txfqs; int err; - int putidx; - - cdev->tx_skb = NULL; + u32 putidx; + unsigned int frame_len = can_skb_get_frame_len(skb); /* Generate ID field for TX buffer Element */ /* Common to all supported M_CAN versions */ if (cf->can_id & CAN_EFF_FLAG) { - fifo_header.id = cf->can_id & CAN_EFF_MASK; - fifo_header.id |= TX_BUF_XTD; + fifo_element.id = cf->can_id & CAN_EFF_MASK; + fifo_element.id |= TX_BUF_XTD; } else { - fifo_header.id = ((cf->can_id & CAN_SFF_MASK) << 18); + fifo_element.id = ((cf->can_id & CAN_SFF_MASK) << 18); } if (cf->can_id & CAN_RTR_FLAG) - fifo_header.id |= TX_BUF_RTR; + fifo_element.id |= TX_BUF_RTR; if (cdev->version == 30) { netif_stop_queue(dev); - fifo_header.dlc = can_fd_len2dlc(cf->len) << 16; + fifo_element.dlc = can_fd_len2dlc(cf->len) << 16; /* Write the frame ID, DLC, and payload to the FIFO element. */ - err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, &fifo_header, 2); + err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, &fifo_element, 2); if (err) goto out_fail; err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_DATA, - cf->data, DIV_ROUND_UP(cf->len, 4)); + cf->data, len_padded); if (err) goto out_fail; @@ -1690,33 +1798,15 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) } m_can_write(cdev, M_CAN_TXBTIE, 0x1); - can_put_echo_skb(skb, dev, 0, 0); + can_put_echo_skb(skb, dev, 0, frame_len); m_can_write(cdev, M_CAN_TXBAR, 0x1); /* End of xmit function for version 3.0.x */ } else { /* Transmit routine for version >= v3.1.x */ - txfqs = m_can_read(cdev, M_CAN_TXFQS); - - /* Check if FIFO full */ - if (_m_can_tx_fifo_full(txfqs)) { - /* This shouldn't happen */ - netif_stop_queue(dev); - netdev_warn(dev, - "TX queue active although FIFO is full."); - - if (cdev->is_peripheral) { - kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } else { - return NETDEV_TX_BUSY; - } - } - /* get put index for frame */ - putidx = FIELD_GET(TXFQS_TFQPI_MASK, txfqs); + putidx = cdev->tx_fifo_putidx; /* Construct DLC Field, with CAN-FD configuration. * Use the put index of the fifo as the message marker, @@ -1731,30 +1821,32 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) fdflags |= TX_BUF_BRS; } - fifo_header.dlc = FIELD_PREP(TX_BUF_MM_MASK, putidx) | + fifo_element.dlc = FIELD_PREP(TX_BUF_MM_MASK, putidx) | FIELD_PREP(TX_BUF_DLC_MASK, can_fd_len2dlc(cf->len)) | fdflags | TX_BUF_EFC; - err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, &fifo_header, 2); - if (err) - goto out_fail; - err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DATA, - cf->data, DIV_ROUND_UP(cf->len, 4)); + memcpy_and_pad(fifo_element.data, CANFD_MAX_DLEN, &cf->data, + cf->len, 0); + + err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, + &fifo_element, 2 + len_padded); if (err) goto out_fail; /* Push loopback echo. * Will be looped back on TX interrupt based on message marker */ - can_put_echo_skb(skb, dev, putidx, 0); + can_put_echo_skb(skb, dev, putidx, frame_len); - /* Enable TX FIFO element to start transfer */ - m_can_write(cdev, M_CAN_TXBAR, (1 << putidx)); - - /* stop network queue if fifo full */ - if (m_can_tx_fifo_full(cdev) || - m_can_next_echo_skb_occupied(dev, putidx)) - netif_stop_queue(dev); + if (cdev->is_peripheral) { + /* Delay enabling TX FIFO element */ + cdev->tx_peripheral_submit |= BIT(putidx); + } else { + /* Enable TX FIFO element to start transfer */ + m_can_write(cdev, M_CAN_TXBAR, BIT(putidx)); + } + cdev->tx_fifo_putidx = (++cdev->tx_fifo_putidx >= cdev->can.echo_skb_max ? + 0 : cdev->tx_fifo_putidx); } return NETDEV_TX_OK; @@ -1765,46 +1857,91 @@ out_fail: return NETDEV_TX_BUSY; } +static void m_can_tx_submit(struct m_can_classdev *cdev) +{ + if (cdev->version == 30) + return; + if (!cdev->is_peripheral) + return; + + m_can_write(cdev, M_CAN_TXBAR, cdev->tx_peripheral_submit); + cdev->tx_peripheral_submit = 0; +} + static void m_can_tx_work_queue(struct work_struct *ws) { - struct m_can_classdev *cdev = container_of(ws, struct m_can_classdev, - tx_work); + struct m_can_tx_op *op = container_of(ws, struct m_can_tx_op, work); + struct m_can_classdev *cdev = op->cdev; + struct sk_buff *skb = op->skb; + + op->skb = NULL; + m_can_tx_handler(cdev, skb); + if (op->submit) + m_can_tx_submit(cdev); +} + +static void m_can_tx_queue_skb(struct m_can_classdev *cdev, struct sk_buff *skb, + bool submit) +{ + cdev->tx_ops[cdev->next_tx_op].skb = skb; + cdev->tx_ops[cdev->next_tx_op].submit = submit; + queue_work(cdev->tx_wq, &cdev->tx_ops[cdev->next_tx_op].work); + + ++cdev->next_tx_op; + if (cdev->next_tx_op >= cdev->tx_fifo_size) + cdev->next_tx_op = 0; +} + +static netdev_tx_t m_can_start_peripheral_xmit(struct m_can_classdev *cdev, + struct sk_buff *skb) +{ + bool submit; + + ++cdev->nr_txs_without_submit; + if (cdev->nr_txs_without_submit >= cdev->tx_max_coalesced_frames || + !netdev_xmit_more()) { + cdev->nr_txs_without_submit = 0; + submit = true; + } else { + submit = false; + } + m_can_tx_queue_skb(cdev, skb, submit); - m_can_tx_handler(cdev); + return NETDEV_TX_OK; } static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); + unsigned int frame_len; + netdev_tx_t ret; if (can_dev_dropped_skb(dev, skb)) return NETDEV_TX_OK; - if (cdev->is_peripheral) { - if (cdev->tx_skb) { - netdev_err(dev, "hard_xmit called while tx busy\n"); - return NETDEV_TX_BUSY; - } + frame_len = can_skb_get_frame_len(skb); - if (cdev->can.state == CAN_STATE_BUS_OFF) { - m_can_clean(dev); - } else { - /* Need to stop the queue to avoid numerous requests - * from being sent. Suggested improvement is to create - * a queueing mechanism that will queue the skbs and - * process them in order. - */ - cdev->tx_skb = skb; - netif_stop_queue(cdev->net); - queue_work(cdev->tx_wq, &cdev->tx_work); - } - } else { - cdev->tx_skb = skb; - return m_can_tx_handler(cdev); + if (cdev->can.state == CAN_STATE_BUS_OFF) { + m_can_clean(cdev->net); + return NETDEV_TX_OK; } - return NETDEV_TX_OK; + ret = m_can_start_tx(cdev); + if (ret != NETDEV_TX_OK) + return ret; + + netdev_sent_queue(dev, frame_len); + + if (cdev->is_peripheral) + ret = m_can_start_peripheral_xmit(cdev, skb); + else + ret = m_can_tx_handler(cdev, skb); + + if (ret != NETDEV_TX_OK) + netdev_completed_queue(dev, 1, frame_len); + + return ret; } static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer) @@ -1844,15 +1981,17 @@ static int m_can_open(struct net_device *dev) /* register interrupt handler */ if (cdev->is_peripheral) { - cdev->tx_skb = NULL; - cdev->tx_wq = alloc_workqueue("mcan_wq", - WQ_FREEZABLE | WQ_MEM_RECLAIM, 0); + cdev->tx_wq = alloc_ordered_workqueue("mcan_wq", + WQ_FREEZABLE | WQ_MEM_RECLAIM); if (!cdev->tx_wq) { err = -ENOMEM; goto out_wq_fail; } - INIT_WORK(&cdev->tx_work, m_can_tx_work_queue); + for (int i = 0; i != cdev->tx_fifo_size; ++i) { + cdev->tx_ops[i].cdev = cdev; + INIT_WORK(&cdev->tx_ops[i].work, m_can_tx_work_queue); + } err = request_threaded_irq(dev->irq, NULL, m_can_isr, IRQF_ONESHOT, @@ -1900,7 +2039,108 @@ static const struct net_device_ops m_can_netdev_ops = { .ndo_change_mtu = can_change_mtu, }; +static int m_can_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct m_can_classdev *cdev = netdev_priv(dev); + + ec->rx_max_coalesced_frames_irq = cdev->rx_max_coalesced_frames_irq; + ec->rx_coalesce_usecs_irq = cdev->rx_coalesce_usecs_irq; + ec->tx_max_coalesced_frames = cdev->tx_max_coalesced_frames; + ec->tx_max_coalesced_frames_irq = cdev->tx_max_coalesced_frames_irq; + ec->tx_coalesce_usecs_irq = cdev->tx_coalesce_usecs_irq; + + return 0; +} + +static int m_can_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct m_can_classdev *cdev = netdev_priv(dev); + + if (cdev->can.state != CAN_STATE_STOPPED) { + netdev_err(dev, "Device is in use, please shut it down first\n"); + return -EBUSY; + } + + if (ec->rx_max_coalesced_frames_irq > cdev->mcfg[MRAM_RXF0].num) { + netdev_err(dev, "rx-frames-irq %u greater than the RX FIFO %u\n", + ec->rx_max_coalesced_frames_irq, + cdev->mcfg[MRAM_RXF0].num); + return -EINVAL; + } + if ((ec->rx_max_coalesced_frames_irq == 0) != (ec->rx_coalesce_usecs_irq == 0)) { + netdev_err(dev, "rx-frames-irq and rx-usecs-irq can only be set together\n"); + return -EINVAL; + } + if (ec->tx_max_coalesced_frames_irq > cdev->mcfg[MRAM_TXE].num) { + netdev_err(dev, "tx-frames-irq %u greater than the TX event FIFO %u\n", + ec->tx_max_coalesced_frames_irq, + cdev->mcfg[MRAM_TXE].num); + return -EINVAL; + } + if (ec->tx_max_coalesced_frames_irq > cdev->mcfg[MRAM_TXB].num) { + netdev_err(dev, "tx-frames-irq %u greater than the TX FIFO %u\n", + ec->tx_max_coalesced_frames_irq, + cdev->mcfg[MRAM_TXB].num); + return -EINVAL; + } + if ((ec->tx_max_coalesced_frames_irq == 0) != (ec->tx_coalesce_usecs_irq == 0)) { + netdev_err(dev, "tx-frames-irq and tx-usecs-irq can only be set together\n"); + return -EINVAL; + } + if (ec->tx_max_coalesced_frames > cdev->mcfg[MRAM_TXE].num) { + netdev_err(dev, "tx-frames %u greater than the TX event FIFO %u\n", + ec->tx_max_coalesced_frames, + cdev->mcfg[MRAM_TXE].num); + return -EINVAL; + } + if (ec->tx_max_coalesced_frames > cdev->mcfg[MRAM_TXB].num) { + netdev_err(dev, "tx-frames %u greater than the TX FIFO %u\n", + ec->tx_max_coalesced_frames, + cdev->mcfg[MRAM_TXB].num); + return -EINVAL; + } + if (ec->rx_coalesce_usecs_irq != 0 && ec->tx_coalesce_usecs_irq != 0 && + ec->rx_coalesce_usecs_irq != ec->tx_coalesce_usecs_irq) { + netdev_err(dev, "rx-usecs-irq %u needs to be equal to tx-usecs-irq %u if both are enabled\n", + ec->rx_coalesce_usecs_irq, + ec->tx_coalesce_usecs_irq); + return -EINVAL; + } + + cdev->rx_max_coalesced_frames_irq = ec->rx_max_coalesced_frames_irq; + cdev->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq; + cdev->tx_max_coalesced_frames = ec->tx_max_coalesced_frames; + cdev->tx_max_coalesced_frames_irq = ec->tx_max_coalesced_frames_irq; + cdev->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq; + + if (cdev->rx_coalesce_usecs_irq) + cdev->irq_timer_wait = + ns_to_ktime(cdev->rx_coalesce_usecs_irq * NSEC_PER_USEC); + else + cdev->irq_timer_wait = + ns_to_ktime(cdev->tx_coalesce_usecs_irq * NSEC_PER_USEC); + + return 0; +} + static const struct ethtool_ops m_can_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ | + ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ | + ETHTOOL_COALESCE_TX_USECS_IRQ | + ETHTOOL_COALESCE_TX_MAX_FRAMES | + ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ, + .get_ts_info = ethtool_op_get_ts_info, + .get_coalesce = m_can_get_coalesce, + .set_coalesce = m_can_set_coalesce, +}; + +static const struct ethtool_ops m_can_ethtool_ops_polling = { .get_ts_info = ethtool_op_get_ts_info, }; @@ -1908,7 +2148,10 @@ static int register_m_can_dev(struct net_device *dev) { dev->flags |= IFF_ECHO; /* we support local echo */ dev->netdev_ops = &m_can_netdev_ops; - dev->ethtool_ops = &m_can_ethtool_ops; + if (dev->irq) + dev->ethtool_ops = &m_can_ethtool_ops; + else + dev->ethtool_ops = &m_can_ethtool_ops_polling; return register_candev(dev); } @@ -2056,12 +2299,23 @@ int m_can_class_register(struct m_can_classdev *cdev) { int ret; - if (cdev->pm_clock_support) { - ret = m_can_clk_start(cdev); - if (ret) - return ret; + cdev->tx_fifo_size = max(1, min(cdev->mcfg[MRAM_TXB].num, + cdev->mcfg[MRAM_TXE].num)); + if (cdev->is_peripheral) { + cdev->tx_ops = + devm_kzalloc(cdev->dev, + cdev->tx_fifo_size * sizeof(*cdev->tx_ops), + GFP_KERNEL); + if (!cdev->tx_ops) { + dev_err(cdev->dev, "Failed to allocate tx_ops for workqueue\n"); + return -ENOMEM; + } } + ret = m_can_clk_start(cdev); + if (ret) + return ret; + if (cdev->is_peripheral) { ret = can_rx_offload_add_manual(cdev->net, &cdev->offload, NAPI_POLL_WEIGHT); @@ -2069,8 +2323,15 @@ int m_can_class_register(struct m_can_classdev *cdev) goto clk_disable; } - if (!cdev->net->irq) + if (!cdev->net->irq) { + dev_dbg(cdev->dev, "Polling enabled, initialize hrtimer"); + hrtimer_init(&cdev->hrtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_PINNED); cdev->hrtimer.function = &hrtimer_callback; + } else { + hrtimer_init(&cdev->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + cdev->hrtimer.function = m_can_coalescing_timer; + } ret = m_can_dev_setup(cdev); if (ret) @@ -2121,7 +2382,15 @@ int m_can_class_suspend(struct device *dev) if (netif_running(ndev)) { netif_stop_queue(ndev); netif_device_detach(ndev); - m_can_stop(ndev); + + /* leave the chip running with rx interrupt enabled if it is + * used as a wake-up source. + */ + if (cdev->pm_wake_source) + m_can_write(cdev, M_CAN_IE, IR_RF0N); + else + m_can_stop(ndev); + m_can_clk_stop(cdev); } @@ -2148,11 +2417,15 @@ int m_can_class_resume(struct device *dev) ret = m_can_clk_start(cdev); if (ret) return ret; - ret = m_can_start(ndev); - if (ret) { - m_can_clk_stop(cdev); - return ret; + if (cdev->pm_wake_source) { + m_can_write(cdev, M_CAN_IE, cdev->active_interrupts); + } else { + ret = m_can_start(ndev); + if (ret) { + m_can_clk_stop(cdev); + return ret; + } } netif_device_attach(ndev); diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index 520e14277dff..3a9edc292593 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -70,6 +70,13 @@ struct m_can_ops { int (*init)(struct m_can_classdev *cdev); }; +struct m_can_tx_op { + struct m_can_classdev *cdev; + struct work_struct work; + struct sk_buff *skb; + bool submit; +}; + struct m_can_classdev { struct can_priv can; struct can_rx_offload offload; @@ -80,18 +87,42 @@ struct m_can_classdev { struct clk *cclk; struct workqueue_struct *tx_wq; - struct work_struct tx_work; - struct sk_buff *tx_skb; struct phy *transceiver; + ktime_t irq_timer_wait; + struct m_can_ops *ops; int version; u32 irqstatus; int pm_clock_support; + int pm_wake_source; int is_peripheral; + // Cached M_CAN_IE register content + u32 active_interrupts; + u32 rx_max_coalesced_frames_irq; + u32 rx_coalesce_usecs_irq; + u32 tx_max_coalesced_frames; + u32 tx_max_coalesced_frames_irq; + u32 tx_coalesce_usecs_irq; + + // Store this internally to avoid fetch delays on peripheral chips + u32 tx_fifo_putidx; + + /* Protects shared state between start_xmit and m_can_isr */ + spinlock_t tx_handling_spinlock; + int tx_fifo_in_flight; + + struct m_can_tx_op *tx_ops; + int tx_fifo_size; + int next_tx_op; + + int nr_txs_without_submit; + /* bitfield of fifo elements that will be submitted together */ + u32 tx_peripheral_submit; + struct mram_cfg mcfg[MRAM_CFG_NUM]; struct hrtimer hrtimer; diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c index f2219aa2824b..45400de4163d 100644 --- a/drivers/net/can/m_can/m_can_pci.c +++ b/drivers/net/can/m_can/m_can_pci.c @@ -125,6 +125,7 @@ static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) mcan_class->dev = &pci->dev; mcan_class->net->irq = pci_irq_vector(pci, 0); mcan_class->pm_clock_support = 1; + mcan_class->pm_wake_source = 0; mcan_class->can.clock.freq = id->driver_data; mcan_class->ops = &m_can_pci_ops; diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c index cdb28d6a092c..df0367124b4c 100644 --- a/drivers/net/can/m_can/m_can_platform.c +++ b/drivers/net/can/m_can/m_can_platform.c @@ -109,10 +109,6 @@ static int m_can_plat_probe(struct platform_device *pdev) ret = irq; goto probe_fail; } - } else { - dev_dbg(mcan_class->dev, "Polling enabled, initialize hrtimer"); - hrtimer_init(&mcan_class->hrtimer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_PINNED); } /* message ram could be shared */ @@ -143,6 +139,7 @@ static int m_can_plat_probe(struct platform_device *pdev) mcan_class->net->irq = irq; mcan_class->pm_clock_support = 1; + mcan_class->pm_wake_source = 0; mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk); mcan_class->dev = &pdev->dev; mcan_class->transceiver = transceiver; diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index ae8c42f5debd..a42600dac70d 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -411,6 +411,7 @@ static int tcan4x5x_can_probe(struct spi_device *spi) priv->spi = spi; mcan_class->pm_clock_support = 0; + mcan_class->pm_wake_source = device_property_read_bool(&spi->dev, "wakeup-source"); mcan_class->can.clock.freq = freq; mcan_class->dev = &spi->dev; mcan_class->ops = &tcan4x5x_ops; @@ -459,6 +460,9 @@ static int tcan4x5x_can_probe(struct spi_device *spi) goto out_power; } + if (mcan_class->pm_wake_source) + device_init_wakeup(&spi->dev, true); + ret = m_can_class_register(mcan_class); if (ret) { dev_err(&spi->dev, "Failed registering m_can device %pe\n", @@ -487,6 +491,29 @@ static void tcan4x5x_can_remove(struct spi_device *spi) m_can_class_free_dev(priv->cdev.net); } +static int __maybe_unused tcan4x5x_suspend(struct device *dev) +{ + struct m_can_classdev *cdev = dev_get_drvdata(dev); + struct spi_device *spi = to_spi_device(dev); + + if (cdev->pm_wake_source) + enable_irq_wake(spi->irq); + + return m_can_class_suspend(dev); +} + +static int __maybe_unused tcan4x5x_resume(struct device *dev) +{ + struct m_can_classdev *cdev = dev_get_drvdata(dev); + struct spi_device *spi = to_spi_device(dev); + int ret = m_can_class_resume(dev); + + if (cdev->pm_wake_source) + disable_irq_wake(spi->irq); + + return ret; +} + static const struct of_device_id tcan4x5x_of_match[] = { { .compatible = "ti,tcan4x5x", @@ -505,11 +532,15 @@ static const struct spi_device_id tcan4x5x_id_table[] = { }; MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table); +static const struct dev_pm_ops tcan4x5x_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tcan4x5x_suspend, tcan4x5x_resume) +}; + static struct spi_driver tcan4x5x_can_driver = { .driver = { .name = KBUILD_MODNAME, .of_match_table = tcan4x5x_of_match, - .pm = NULL, + .pm = &tcan4x5x_pm_ops, }, .id_table = tcan4x5x_id_table, .probe = tcan4x5x_can_probe, diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c index 32286f861a19..721df91cdbfb 100644 --- a/drivers/net/can/softing/softing_fw.c +++ b/drivers/net/can/softing/softing_fw.c @@ -436,7 +436,7 @@ int softing_startstop(struct net_device *dev, int up) return ret; bus_bitmask_start = 0; - if (dev && up) + if (up) /* prepare to start this bus as well */ bus_bitmask_start |= (1 << priv->index); /* bring netdevs down */ diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index 3722eaa84234..fae0120473f8 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -31,6 +31,7 @@ #include <linux/phy/phy.h> #include <linux/pm_runtime.h> #include <linux/reset.h> +#include <linux/u64_stats_sync.h> #define DRIVER_NAME "xilinx_can" @@ -58,6 +59,13 @@ enum xcan_reg { */ XCAN_F_BTR_OFFSET = 0x08C, /* Data Phase Bit Timing */ XCAN_TRR_OFFSET = 0x0090, /* TX Buffer Ready Request */ + + /* only on AXI CAN cores */ + XCAN_ECC_CFG_OFFSET = 0xC8, /* ECC Configuration */ + XCAN_TXTLFIFO_ECC_OFFSET = 0xCC, /* TXTL FIFO ECC error counter */ + XCAN_TXOLFIFO_ECC_OFFSET = 0xD0, /* TXOL FIFO ECC error counter */ + XCAN_RXFIFO_ECC_OFFSET = 0xD4, /* RX FIFO ECC error counter */ + XCAN_AFR_EXT_OFFSET = 0x00E0, /* Acceptance Filter */ XCAN_FSR_OFFSET = 0x00E8, /* RX FIFO Status */ XCAN_TXMSG_BASE_OFFSET = 0x0100, /* TX Message Space */ @@ -124,6 +132,18 @@ enum xcan_reg { #define XCAN_IXR_TXFLL_MASK 0x00000004 /* Tx FIFO Full intr */ #define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */ #define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */ +#define XCAN_IXR_E2BERX_MASK BIT(23) /* RX FIFO two bit ECC error */ +#define XCAN_IXR_E1BERX_MASK BIT(22) /* RX FIFO one bit ECC error */ +#define XCAN_IXR_E2BETXOL_MASK BIT(21) /* TXOL FIFO two bit ECC error */ +#define XCAN_IXR_E1BETXOL_MASK BIT(20) /* TXOL FIFO One bit ECC error */ +#define XCAN_IXR_E2BETXTL_MASK BIT(19) /* TXTL FIFO Two bit ECC error */ +#define XCAN_IXR_E1BETXTL_MASK BIT(18) /* TXTL FIFO One bit ECC error */ +#define XCAN_IXR_ECC_MASK (XCAN_IXR_E2BERX_MASK | \ + XCAN_IXR_E1BERX_MASK | \ + XCAN_IXR_E2BETXOL_MASK | \ + XCAN_IXR_E1BETXOL_MASK | \ + XCAN_IXR_E2BETXTL_MASK | \ + XCAN_IXR_E1BETXTL_MASK) #define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */ #define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */ #define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */ @@ -137,6 +157,11 @@ enum xcan_reg { #define XCAN_2_FSR_RI_MASK 0x0000003F /* RX Read Index */ #define XCAN_DLCR_EDL_MASK 0x08000000 /* EDL Mask in DLC */ #define XCAN_DLCR_BRS_MASK 0x04000000 /* BRS Mask in DLC */ +#define XCAN_ECC_CFG_REECRX_MASK BIT(2) /* Reset RX FIFO ECC error counters */ +#define XCAN_ECC_CFG_REECTXOL_MASK BIT(1) /* Reset TXOL FIFO ECC error counters */ +#define XCAN_ECC_CFG_REECTXTL_MASK BIT(0) /* Reset TXTL FIFO ECC error counters */ +#define XCAN_ECC_1BIT_CNT_MASK GENMASK(15, 0) /* FIFO ECC 1bit count mask */ +#define XCAN_ECC_2BIT_CNT_MASK GENMASK(31, 16) /* FIFO ECC 2bit count mask */ /* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */ #define XCAN_BRPR_TDC_ENABLE BIT(16) /* Transmitter Delay Compensation (TDC) Enable */ @@ -202,6 +227,14 @@ struct xcan_devtype_data { * @devtype: Device type specific constants * @transceiver: Optional pointer to associated CAN transceiver * @rstc: Pointer to reset control + * @ecc_enable: ECC enable flag + * @syncp: synchronization for ECC error stats + * @ecc_rx_2_bit_errors: RXFIFO 2bit ECC count + * @ecc_rx_1_bit_errors: RXFIFO 1bit ECC count + * @ecc_txol_2_bit_errors: TXOLFIFO 2bit ECC count + * @ecc_txol_1_bit_errors: TXOLFIFO 1bit ECC count + * @ecc_txtl_2_bit_errors: TXTLFIFO 2bit ECC count + * @ecc_txtl_1_bit_errors: TXTLFIFO 1bit ECC count */ struct xcan_priv { struct can_priv can; @@ -221,6 +254,14 @@ struct xcan_priv { struct xcan_devtype_data devtype; struct phy *transceiver; struct reset_control *rstc; + bool ecc_enable; + struct u64_stats_sync syncp; + u64_stats_t ecc_rx_2_bit_errors; + u64_stats_t ecc_rx_1_bit_errors; + u64_stats_t ecc_txol_2_bit_errors; + u64_stats_t ecc_txol_1_bit_errors; + u64_stats_t ecc_txtl_2_bit_errors; + u64_stats_t ecc_txtl_1_bit_errors; }; /* CAN Bittiming constants as per Xilinx CAN specs */ @@ -308,6 +349,24 @@ static const struct can_tdc_const xcan_tdc_const_canfd2 = { .tdcf_max = 0, }; +enum xcan_stats_type { + XCAN_ECC_RX_2_BIT_ERRORS, + XCAN_ECC_RX_1_BIT_ERRORS, + XCAN_ECC_TXOL_2_BIT_ERRORS, + XCAN_ECC_TXOL_1_BIT_ERRORS, + XCAN_ECC_TXTL_2_BIT_ERRORS, + XCAN_ECC_TXTL_1_BIT_ERRORS, +}; + +static const char xcan_priv_flags_strings[][ETH_GSTRING_LEN] = { + [XCAN_ECC_RX_2_BIT_ERRORS] = "ecc_rx_2_bit_errors", + [XCAN_ECC_RX_1_BIT_ERRORS] = "ecc_rx_1_bit_errors", + [XCAN_ECC_TXOL_2_BIT_ERRORS] = "ecc_txol_2_bit_errors", + [XCAN_ECC_TXOL_1_BIT_ERRORS] = "ecc_txol_1_bit_errors", + [XCAN_ECC_TXTL_2_BIT_ERRORS] = "ecc_txtl_2_bit_errors", + [XCAN_ECC_TXTL_1_BIT_ERRORS] = "ecc_txtl_1_bit_errors", +}; + /** * xcan_write_reg_le - Write a value to the device register little endian * @priv: Driver private data structure @@ -523,6 +582,9 @@ static int xcan_chip_start(struct net_device *ndev) XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | XCAN_IXR_ARBLST_MASK | xcan_rx_int_mask(priv); + if (priv->ecc_enable) + ier |= XCAN_IXR_ECC_MASK; + if (priv->devtype.flags & XCAN_FLAG_RXMNF) ier |= XCAN_IXR_RXMNF_MASK; @@ -1127,6 +1189,54 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr) priv->can.can_stats.bus_error++; } + if (priv->ecc_enable && isr & XCAN_IXR_ECC_MASK) { + u32 reg_rx_ecc, reg_txol_ecc, reg_txtl_ecc; + + reg_rx_ecc = priv->read_reg(priv, XCAN_RXFIFO_ECC_OFFSET); + reg_txol_ecc = priv->read_reg(priv, XCAN_TXOLFIFO_ECC_OFFSET); + reg_txtl_ecc = priv->read_reg(priv, XCAN_TXTLFIFO_ECC_OFFSET); + + /* The counter reaches its maximum at 0xffff and does not overflow. + * Accept the small race window between reading and resetting ECC counters. + */ + priv->write_reg(priv, XCAN_ECC_CFG_OFFSET, XCAN_ECC_CFG_REECRX_MASK | + XCAN_ECC_CFG_REECTXOL_MASK | XCAN_ECC_CFG_REECTXTL_MASK); + + u64_stats_update_begin(&priv->syncp); + + if (isr & XCAN_IXR_E2BERX_MASK) { + u64_stats_add(&priv->ecc_rx_2_bit_errors, + FIELD_GET(XCAN_ECC_2BIT_CNT_MASK, reg_rx_ecc)); + } + + if (isr & XCAN_IXR_E1BERX_MASK) { + u64_stats_add(&priv->ecc_rx_1_bit_errors, + FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_rx_ecc)); + } + + if (isr & XCAN_IXR_E2BETXOL_MASK) { + u64_stats_add(&priv->ecc_txol_2_bit_errors, + FIELD_GET(XCAN_ECC_2BIT_CNT_MASK, reg_txol_ecc)); + } + + if (isr & XCAN_IXR_E1BETXOL_MASK) { + u64_stats_add(&priv->ecc_txol_1_bit_errors, + FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_txol_ecc)); + } + + if (isr & XCAN_IXR_E2BETXTL_MASK) { + u64_stats_add(&priv->ecc_txtl_2_bit_errors, + FIELD_GET(XCAN_ECC_2BIT_CNT_MASK, reg_txtl_ecc)); + } + + if (isr & XCAN_IXR_E1BETXTL_MASK) { + u64_stats_add(&priv->ecc_txtl_1_bit_errors, + FIELD_GET(XCAN_ECC_1BIT_CNT_MASK, reg_txtl_ecc)); + } + + u64_stats_update_end(&priv->syncp); + } + if (cf.can_id) { struct can_frame *skb_cf; struct sk_buff *skb = alloc_can_err_skb(ndev, &skb_cf); @@ -1354,8 +1464,8 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id) { struct net_device *ndev = (struct net_device *)dev_id; struct xcan_priv *priv = netdev_priv(ndev); + u32 isr_errors, mask; u32 isr, ier; - u32 isr_errors; u32 rx_int_mask = xcan_rx_int_mask(priv); /* Get the interrupt status from Xilinx CAN */ @@ -1374,10 +1484,15 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id) if (isr & XCAN_IXR_TXOK_MASK) xcan_tx_interrupt(ndev, isr); + mask = XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK | + XCAN_IXR_RXMNF_MASK; + + if (priv->ecc_enable) + mask |= XCAN_IXR_ECC_MASK; + /* Check for the type of error interrupt and Processing it */ - isr_errors = isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | - XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK | - XCAN_IXR_RXMNF_MASK); + isr_errors = isr & mask; if (isr_errors) { priv->write_reg(priv, XCAN_ICR_OFFSET, isr_errors); xcan_err_interrupt(ndev, isr); @@ -1546,6 +1661,43 @@ static int xcan_get_auto_tdcv(const struct net_device *ndev, u32 *tdcv) return 0; } +static void xcan_get_strings(struct net_device *ndev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, &xcan_priv_flags_strings, + sizeof(xcan_priv_flags_strings)); + } +} + +static int xcan_get_sset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(xcan_priv_flags_strings); + default: + return -EOPNOTSUPP; + } +} + +static void xcan_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, u64 *data) +{ + struct xcan_priv *priv = netdev_priv(ndev); + unsigned int start; + + do { + start = u64_stats_fetch_begin(&priv->syncp); + + data[XCAN_ECC_RX_2_BIT_ERRORS] = u64_stats_read(&priv->ecc_rx_2_bit_errors); + data[XCAN_ECC_RX_1_BIT_ERRORS] = u64_stats_read(&priv->ecc_rx_1_bit_errors); + data[XCAN_ECC_TXOL_2_BIT_ERRORS] = u64_stats_read(&priv->ecc_txol_2_bit_errors); + data[XCAN_ECC_TXOL_1_BIT_ERRORS] = u64_stats_read(&priv->ecc_txol_1_bit_errors); + data[XCAN_ECC_TXTL_2_BIT_ERRORS] = u64_stats_read(&priv->ecc_txtl_2_bit_errors); + data[XCAN_ECC_TXTL_1_BIT_ERRORS] = u64_stats_read(&priv->ecc_txtl_1_bit_errors); + } while (u64_stats_fetch_retry(&priv->syncp, start)); +} + static const struct net_device_ops xcan_netdev_ops = { .ndo_open = xcan_open, .ndo_stop = xcan_close, @@ -1555,6 +1707,9 @@ static const struct net_device_ops xcan_netdev_ops = { static const struct ethtool_ops xcan_ethtool_ops = { .get_ts_info = ethtool_op_get_ts_info, + .get_strings = xcan_get_strings, + .get_sset_count = xcan_get_sset_count, + .get_ethtool_stats = xcan_get_ethtool_stats, }; /** @@ -1793,6 +1948,7 @@ static int xcan_probe(struct platform_device *pdev) return -ENOMEM; priv = netdev_priv(ndev); + priv->ecc_enable = of_property_read_bool(pdev->dev.of_node, "xlnx,has-ecc"); priv->dev = &pdev->dev; priv->can.bittiming_const = devtype->bittiming_const; priv->can.do_set_mode = xcan_do_set_mode; @@ -1909,6 +2065,11 @@ static int xcan_probe(struct platform_device *pdev) priv->reg_base, ndev->irq, priv->can.clock.freq, hw_tx_max, priv->tx_max); + if (priv->ecc_enable) { + /* Reset FIFO ECC counters */ + priv->write_reg(priv, XCAN_ECC_CFG_OFFSET, XCAN_ECC_CFG_REECRX_MASK | + XCAN_ECC_CFG_REECTXOL_MASK | XCAN_ECC_CFG_REECTXTL_MASK); + } return 0; err_disableclks: diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index f8c1d73b251d..3092b391031a 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -48,7 +48,7 @@ config NET_DSA_MT7530 config NET_DSA_MT7530_MDIO tristate "MediaTek MT7530 MDIO interface driver" depends on NET_DSA_MT7530 - imply MEDIATEK_GE_PHY + select MEDIATEK_GE_PHY select PCS_MTK_LYNXI help This enables support for the MediaTek MT7530 and MT7531 switch diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 0d628b35fd5c..b2eeff04f4c8 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -559,6 +559,19 @@ static void b53_port_set_learning(struct b53_device *dev, int port, b53_write16(dev, B53_CTRL_PAGE, B53_DIS_LEARNING, reg); } +static void b53_eee_enable_set(struct dsa_switch *ds, int port, bool enable) +{ + struct b53_device *dev = ds->priv; + u16 reg; + + b53_read16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, ®); + if (enable) + reg |= BIT(port); + else + reg &= ~BIT(port); + b53_write16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, reg); +} + int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) { struct b53_device *dev = ds->priv; @@ -1257,7 +1270,7 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct b53_device *dev = ds->priv; - struct ethtool_eee *p = &dev->ports[port].eee; + struct ethtool_keee *p = &dev->ports[port].eee; u8 rgmii_ctrl = 0, reg = 0, off; bool tx_pause = false; bool rx_pause = false; @@ -2193,21 +2206,6 @@ void b53_mirror_del(struct dsa_switch *ds, int port, } EXPORT_SYMBOL(b53_mirror_del); -void b53_eee_enable_set(struct dsa_switch *ds, int port, bool enable) -{ - struct b53_device *dev = ds->priv; - u16 reg; - - b53_read16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, ®); - if (enable) - reg |= BIT(port); - else - reg &= ~BIT(port); - b53_write16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, reg); -} -EXPORT_SYMBOL(b53_eee_enable_set); - - /* Returns 0 if EEE was not enabled, or 1 otherwise */ int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) @@ -2224,27 +2222,21 @@ int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) } EXPORT_SYMBOL(b53_eee_init); -int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) { struct b53_device *dev = ds->priv; - struct ethtool_eee *p = &dev->ports[port].eee; - u16 reg; if (is5325(dev) || is5365(dev)) return -EOPNOTSUPP; - b53_read16(dev, B53_EEE_PAGE, B53_EEE_LPI_INDICATE, ®); - e->eee_enabled = p->eee_enabled; - e->eee_active = !!(reg & BIT(port)); - return 0; } EXPORT_SYMBOL(b53_get_mac_eee); -int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) { struct b53_device *dev = ds->priv; - struct ethtool_eee *p = &dev->ports[port].eee; + struct ethtool_keee *p = &dev->ports[port].eee; if (is5325(dev) || is5365(dev)) return -EOPNOTSUPP; diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index fdcfd5081c28..c13a907947f1 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -95,7 +95,7 @@ struct b53_pcs { struct b53_port { u16 vlan_ctl_mask; - struct ethtool_eee eee; + struct ethtool_keee eee; }; struct b53_vlan { @@ -395,9 +395,8 @@ void b53_mirror_del(struct dsa_switch *ds, int port, int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); void b53_disable_port(struct dsa_switch *ds, int port); void b53_brcm_hdr_setup(struct dsa_switch *ds, int port); -void b53_eee_enable_set(struct dsa_switch *ds, int port, bool enable); int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy); -int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); -int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); +int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e); +int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e); #endif diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 4a52ccbe393f..bc77ee9e6d0a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -835,7 +835,7 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, bool tx_pause, bool rx_pause) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - struct ethtool_eee *p = &priv->dev->ports[port].eee; + struct ethtool_keee *p = &priv->dev->ports[port].eee; u32 reg_rgmii_ctrl = 0; u32 reg, offset; diff --git a/drivers/net/dsa/dsa_loop_bdinfo.c b/drivers/net/dsa/dsa_loop_bdinfo.c index 237066d30704..14ca42491512 100644 --- a/drivers/net/dsa/dsa_loop_bdinfo.c +++ b/drivers/net/dsa/dsa_loop_bdinfo.c @@ -32,4 +32,5 @@ static int __init dsa_loop_bdinfo_init(void) } arch_initcall(dsa_loop_bdinfo_init) +MODULE_DESCRIPTION("DSA mock-up switch driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 61b71bcfe396..50351cef6ca5 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -633,6 +633,57 @@ static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan) } /** + * ksz879x_get_loopback - KSZ879x specific function to get loopback + * configuration status for a specific port + * @dev: Pointer to the device structure + * @port: Port number to query + * @val: Pointer to store the result + * + * This function reads the SMI registers to determine whether loopback mode + * is enabled for a specific port. + * + * Return: 0 on success, error code on failure. + */ +static int ksz879x_get_loopback(struct ksz_device *dev, u16 port, + u16 *val) +{ + u8 stat3; + int ret; + + ret = ksz_pread8(dev, port, REG_PORT_STATUS_3, &stat3); + if (ret) + return ret; + + if (stat3 & PORT_PHY_LOOPBACK) + *val |= BMCR_LOOPBACK; + + return 0; +} + +/** + * ksz879x_set_loopback - KSZ879x specific function to set loopback mode for + * a specific port + * @dev: Pointer to the device structure. + * @port: Port number to modify. + * @val: Value indicating whether to enable or disable loopback mode. + * + * This function translates loopback bit of the BMCR register into the + * corresponding hardware register bit value and writes it to the SMI interface. + * + * Return: 0 on success, error code on failure. + */ +static int ksz879x_set_loopback(struct ksz_device *dev, u16 port, u16 val) +{ + u8 stat3 = 0; + + if (val & BMCR_LOOPBACK) + stat3 |= PORT_PHY_LOOPBACK; + + return ksz_prmw8(dev, port, REG_PORT_STATUS_3, PORT_PHY_LOOPBACK, + stat3); +} + +/** * ksz8_r_phy_ctrl - Translates and reads from the SMI interface to a MIIM PHY * Control register (Reg. 31). * @dev: The KSZ device instance. @@ -676,59 +727,122 @@ static int ksz8_r_phy_ctrl(struct ksz_device *dev, int port, u16 *val) return 0; } +/** + * ksz8_r_phy_bmcr - Translates and reads from the SMI interface to a MIIM PHY + * Basic mode control register (Reg. 0). + * @dev: The KSZ device instance. + * @port: The port number to be read. + * @val: The value read from the SMI interface. + * + * This function reads the SMI interface and translates the hardware register + * bit values into their corresponding control settings for a MIIM PHY Basic + * mode control register. + * + * MIIM Bit Mapping Comparison between KSZ8794 and KSZ8873 + * ------------------------------------------------------------------- + * MIIM Bit | KSZ8794 Reg/Bit | KSZ8873 Reg/Bit + * ----------------------------+-----------------------------+---------------- + * Bit 15 - Soft Reset | 0xF/4 | Not supported + * Bit 14 - Loopback | 0xD/0 (MAC), 0xF/7 (PHY) ~ 0xD/0 (PHY) + * Bit 13 - Force 100 | 0xC/6 = 0xC/6 + * Bit 12 - AN Enable | 0xC/7 (reverse logic) ~ 0xC/7 + * Bit 11 - Power Down | 0xD/3 = 0xD/3 + * Bit 10 - PHY Isolate | 0xF/5 | Not supported + * Bit 9 - Restart AN | 0xD/5 = 0xD/5 + * Bit 8 - Force Full-Duplex | 0xC/5 = 0xC/5 + * Bit 7 - Collision Test/Res. | Not supported | Not supported + * Bit 6 - Reserved | Not supported | Not supported + * Bit 5 - Hp_mdix | 0x9/7 ~ 0xF/7 + * Bit 4 - Force MDI | 0xD/1 = 0xD/1 + * Bit 3 - Disable MDIX | 0xD/2 = 0xD/2 + * Bit 2 - Disable Far-End F. | ???? | 0xD/4 + * Bit 1 - Disable Transmit | 0xD/6 = 0xD/6 + * Bit 0 - Disable LED | 0xD/7 = 0xD/7 + * ------------------------------------------------------------------- + * + * Return: 0 on success, error code on failure. + */ +static int ksz8_r_phy_bmcr(struct ksz_device *dev, u16 port, u16 *val) +{ + const u16 *regs = dev->info->regs; + u8 restart, speed, ctrl; + int ret; + + *val = 0; + + ret = ksz_pread8(dev, port, regs[P_NEG_RESTART_CTRL], &restart); + if (ret) + return ret; + + ret = ksz_pread8(dev, port, regs[P_SPEED_STATUS], &speed); + if (ret) + return ret; + + ret = ksz_pread8(dev, port, regs[P_FORCE_CTRL], &ctrl); + if (ret) + return ret; + + if (ctrl & PORT_FORCE_100_MBIT) + *val |= BMCR_SPEED100; + + if (ksz_is_ksz88x3(dev)) { + if (restart & KSZ8873_PORT_PHY_LOOPBACK) + *val |= BMCR_LOOPBACK; + + if ((ctrl & PORT_AUTO_NEG_ENABLE)) + *val |= BMCR_ANENABLE; + } else { + ret = ksz879x_get_loopback(dev, port, val); + if (ret) + return ret; + + if (!(ctrl & PORT_AUTO_NEG_DISABLE)) + *val |= BMCR_ANENABLE; + } + + if (restart & PORT_POWER_DOWN) + *val |= BMCR_PDOWN; + + if (restart & PORT_AUTO_NEG_RESTART) + *val |= BMCR_ANRESTART; + + if (ctrl & PORT_FORCE_FULL_DUPLEX) + *val |= BMCR_FULLDPLX; + + if (speed & PORT_HP_MDIX) + *val |= KSZ886X_BMCR_HP_MDIX; + + if (restart & PORT_FORCE_MDIX) + *val |= KSZ886X_BMCR_FORCE_MDI; + + if (restart & PORT_AUTO_MDIX_DISABLE) + *val |= KSZ886X_BMCR_DISABLE_AUTO_MDIX; + + if (restart & PORT_TX_DISABLE) + *val |= KSZ886X_BMCR_DISABLE_TRANSMIT; + + if (restart & PORT_LED_OFF) + *val |= KSZ886X_BMCR_DISABLE_LED; + + return 0; +} + int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) { - u8 restart, speed, ctrl, link; + u8 ctrl, link, val1, val2; int processed = true; const u16 *regs; - u8 val1, val2; u16 data = 0; - u8 p = phy; + u16 p = phy; int ret; regs = dev->info->regs; switch (reg) { case MII_BMCR: - ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); - if (ret) - return ret; - - ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); + ret = ksz8_r_phy_bmcr(dev, p, &data); if (ret) return ret; - - ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); - if (ret) - return ret; - - if (restart & PORT_PHY_LOOPBACK) - data |= BMCR_LOOPBACK; - if (ctrl & PORT_FORCE_100_MBIT) - data |= BMCR_SPEED100; - if (ksz_is_ksz88x3(dev)) { - if ((ctrl & PORT_AUTO_NEG_ENABLE)) - data |= BMCR_ANENABLE; - } else { - if (!(ctrl & PORT_AUTO_NEG_DISABLE)) - data |= BMCR_ANENABLE; - } - if (restart & PORT_POWER_DOWN) - data |= BMCR_PDOWN; - if (restart & PORT_AUTO_NEG_RESTART) - data |= BMCR_ANRESTART; - if (ctrl & PORT_FORCE_FULL_DUPLEX) - data |= BMCR_FULLDPLX; - if (speed & PORT_HP_MDIX) - data |= KSZ886X_BMCR_HP_MDIX; - if (restart & PORT_FORCE_MDIX) - data |= KSZ886X_BMCR_FORCE_MDI; - if (restart & PORT_AUTO_MDIX_DISABLE) - data |= KSZ886X_BMCR_DISABLE_AUTO_MDIX; - if (restart & PORT_TX_DISABLE) - data |= KSZ886X_BMCR_DISABLE_TRANSMIT; - if (restart & PORT_LED_OFF) - data |= KSZ886X_BMCR_DISABLE_LED; break; case MII_BMSR: ret = ksz_pread8(dev, p, regs[P_LINK_STATUS], &link); @@ -860,113 +974,137 @@ static int ksz8_w_phy_ctrl(struct ksz_device *dev, int port, u16 val) return ret; } -int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) +/** + * ksz8_w_phy_bmcr - Translates and writes to the SMI interface from a MIIM PHY + * Basic mode control register (Reg. 0). + * @dev: The KSZ device instance. + * @port: The port number to be configured. + * @val: The register value to be written. + * + * This function translates control settings from a MIIM PHY Basic mode control + * register into their corresponding hardware register bit values for the SMI + * interface. + * + * MIIM Bit Mapping Comparison between KSZ8794 and KSZ8873 + * ------------------------------------------------------------------- + * MIIM Bit | KSZ8794 Reg/Bit | KSZ8873 Reg/Bit + * ----------------------------+-----------------------------+---------------- + * Bit 15 - Soft Reset | 0xF/4 | Not supported + * Bit 14 - Loopback | 0xD/0 (MAC), 0xF/7 (PHY) ~ 0xD/0 (PHY) + * Bit 13 - Force 100 | 0xC/6 = 0xC/6 + * Bit 12 - AN Enable | 0xC/7 (reverse logic) ~ 0xC/7 + * Bit 11 - Power Down | 0xD/3 = 0xD/3 + * Bit 10 - PHY Isolate | 0xF/5 | Not supported + * Bit 9 - Restart AN | 0xD/5 = 0xD/5 + * Bit 8 - Force Full-Duplex | 0xC/5 = 0xC/5 + * Bit 7 - Collision Test/Res. | Not supported | Not supported + * Bit 6 - Reserved | Not supported | Not supported + * Bit 5 - Hp_mdix | 0x9/7 ~ 0xF/7 + * Bit 4 - Force MDI | 0xD/1 = 0xD/1 + * Bit 3 - Disable MDIX | 0xD/2 = 0xD/2 + * Bit 2 - Disable Far-End F. | ???? | 0xD/4 + * Bit 1 - Disable Transmit | 0xD/6 = 0xD/6 + * Bit 0 - Disable LED | 0xD/7 = 0xD/7 + * ------------------------------------------------------------------- + * + * Return: 0 on success, error code on failure. + */ +static int ksz8_w_phy_bmcr(struct ksz_device *dev, u16 port, u16 val) { - u8 restart, speed, ctrl, data; - const u16 *regs; - u8 p = phy; + u8 restart, speed, ctrl, restart_mask; + const u16 *regs = dev->info->regs; int ret; - regs = dev->info->regs; + /* Do not support PHY reset function. */ + if (val & BMCR_RESET) + return 0; - switch (reg) { - case MII_BMCR: + speed = 0; + if (val & KSZ886X_BMCR_HP_MDIX) + speed |= PORT_HP_MDIX; - /* Do not support PHY reset function. */ - if (val & BMCR_RESET) - break; - ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); - if (ret) - return ret; + ret = ksz_prmw8(dev, port, regs[P_SPEED_STATUS], PORT_HP_MDIX, speed); + if (ret) + return ret; - data = speed; - if (val & KSZ886X_BMCR_HP_MDIX) - data |= PORT_HP_MDIX; - else - data &= ~PORT_HP_MDIX; + ctrl = 0; + if (ksz_is_ksz88x3(dev)) { + if ((val & BMCR_ANENABLE)) + ctrl |= PORT_AUTO_NEG_ENABLE; + } else { + if (!(val & BMCR_ANENABLE)) + ctrl |= PORT_AUTO_NEG_DISABLE; - if (data != speed) { - ret = ksz_pwrite8(dev, p, regs[P_SPEED_STATUS], data); - if (ret) - return ret; - } + /* Fiber port does not support auto-negotiation. */ + if (dev->ports[port].fiber) + ctrl |= PORT_AUTO_NEG_DISABLE; + } - ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); - if (ret) - return ret; + if (val & BMCR_SPEED100) + ctrl |= PORT_FORCE_100_MBIT; - data = ctrl; - if (ksz_is_ksz88x3(dev)) { - if ((val & BMCR_ANENABLE)) - data |= PORT_AUTO_NEG_ENABLE; - else - data &= ~PORT_AUTO_NEG_ENABLE; - } else { - if (!(val & BMCR_ANENABLE)) - data |= PORT_AUTO_NEG_DISABLE; - else - data &= ~PORT_AUTO_NEG_DISABLE; - - /* Fiber port does not support auto-negotiation. */ - if (dev->ports[p].fiber) - data |= PORT_AUTO_NEG_DISABLE; - } + if (val & BMCR_FULLDPLX) + ctrl |= PORT_FORCE_FULL_DUPLEX; - if (val & BMCR_SPEED100) - data |= PORT_FORCE_100_MBIT; - else - data &= ~PORT_FORCE_100_MBIT; - if (val & BMCR_FULLDPLX) - data |= PORT_FORCE_FULL_DUPLEX; - else - data &= ~PORT_FORCE_FULL_DUPLEX; + ret = ksz_prmw8(dev, port, regs[P_FORCE_CTRL], PORT_FORCE_100_MBIT | + /* PORT_AUTO_NEG_ENABLE and PORT_AUTO_NEG_DISABLE are the same + * bits + */ + PORT_FORCE_FULL_DUPLEX | PORT_AUTO_NEG_ENABLE, ctrl); + if (ret) + return ret; - if (data != ctrl) { - ret = ksz_pwrite8(dev, p, regs[P_FORCE_CTRL], data); - if (ret) - return ret; - } + restart = 0; + restart_mask = PORT_LED_OFF | PORT_TX_DISABLE | PORT_AUTO_NEG_RESTART | + PORT_POWER_DOWN | PORT_AUTO_MDIX_DISABLE | PORT_FORCE_MDIX; + + if (val & KSZ886X_BMCR_DISABLE_LED) + restart |= PORT_LED_OFF; + + if (val & KSZ886X_BMCR_DISABLE_TRANSMIT) + restart |= PORT_TX_DISABLE; + + if (val & BMCR_ANRESTART) + restart |= PORT_AUTO_NEG_RESTART; + + if (val & BMCR_PDOWN) + restart |= PORT_POWER_DOWN; + + if (val & KSZ886X_BMCR_DISABLE_AUTO_MDIX) + restart |= PORT_AUTO_MDIX_DISABLE; + + if (val & KSZ886X_BMCR_FORCE_MDI) + restart |= PORT_FORCE_MDIX; - ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); + if (ksz_is_ksz88x3(dev)) { + restart_mask |= KSZ8873_PORT_PHY_LOOPBACK; + + if (val & BMCR_LOOPBACK) + restart |= KSZ8873_PORT_PHY_LOOPBACK; + } else { + ret = ksz879x_set_loopback(dev, port, val); if (ret) return ret; + } - data = restart; - if (val & KSZ886X_BMCR_DISABLE_LED) - data |= PORT_LED_OFF; - else - data &= ~PORT_LED_OFF; - if (val & KSZ886X_BMCR_DISABLE_TRANSMIT) - data |= PORT_TX_DISABLE; - else - data &= ~PORT_TX_DISABLE; - if (val & BMCR_ANRESTART) - data |= PORT_AUTO_NEG_RESTART; - else - data &= ~(PORT_AUTO_NEG_RESTART); - if (val & BMCR_PDOWN) - data |= PORT_POWER_DOWN; - else - data &= ~PORT_POWER_DOWN; - if (val & KSZ886X_BMCR_DISABLE_AUTO_MDIX) - data |= PORT_AUTO_MDIX_DISABLE; - else - data &= ~PORT_AUTO_MDIX_DISABLE; - if (val & KSZ886X_BMCR_FORCE_MDI) - data |= PORT_FORCE_MDIX; - else - data &= ~PORT_FORCE_MDIX; - if (val & BMCR_LOOPBACK) - data |= PORT_PHY_LOOPBACK; - else - data &= ~PORT_PHY_LOOPBACK; + return ksz_prmw8(dev, port, regs[P_NEG_RESTART_CTRL], restart_mask, + restart); +} - if (data != restart) { - ret = ksz_pwrite8(dev, p, regs[P_NEG_RESTART_CTRL], - data); - if (ret) - return ret; - } +int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) +{ + const u16 *regs; + u8 ctrl, data; + u16 p = phy; + int ret; + + regs = dev->info->regs; + + switch (reg) { + case MII_BMCR: + ret = ksz8_w_phy_bmcr(dev, p, val); + if (ret) + return ret; break; case MII_ADVERTISE: ret = ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl); diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index beca974e0171..7c9341ef73b0 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -265,6 +265,7 @@ #define PORT_AUTO_MDIX_DISABLE BIT(2) #define PORT_FORCE_MDIX BIT(1) #define PORT_MAC_LOOPBACK BIT(0) +#define KSZ8873_PORT_PHY_LOOPBACK BIT(0) #define REG_PORT_1_STATUS_2 0x1E #define REG_PORT_2_STATUS_2 0x2E diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index cac4a607e54a..82bebee4615c 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -104,6 +104,10 @@ static const struct of_device_id ksz9477_dt_ids[] = { .data = &ksz_switch_chips[KSZ8563] }, { + .compatible = "microchip,ksz8567", + .data = &ksz_switch_chips[KSZ8567] + }, + { .compatible = "microchip,ksz9567", .data = &ksz_switch_chips[KSZ9567] }, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 245dfb7a7a31..9ff5132f3ac6 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -1476,6 +1476,39 @@ const struct ksz_chip_data ksz_switch_chips[] = { .gbit_capable = {true, true, true}, }, + [KSZ8567] = { + .chip_id = KSZ8567_CHIP_ID, + .dev_name = "KSZ8567", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x7F, /* can be configured as cpu port */ + .port_cnt = 7, /* total port count */ + .port_nirqs = 3, + .num_tx_queues = 4, + .tc_cbs_supported = true, + .tc_ets_supported = true, + .ops = &ksz9477_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = ksz9477_masks, + .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, + false, true, true}, + .supports_rmii = {false, false, false, false, + false, true, true}, + .supports_rgmii = {false, false, false, false, + false, true, true}, + .internal_phy = {true, true, true, true, + true, false, false}, + .gbit_capable = {false, false, false, false, false, + true, true}, + }, + [KSZ9567] = { .chip_id = KSZ9567_CHIP_ID, .dev_name = "KSZ9567", @@ -2649,6 +2682,7 @@ static void ksz_port_teardown(struct dsa_switch *ds, int port) switch (dev->chip_id) { case KSZ8563_CHIP_ID: + case KSZ8567_CHIP_ID: case KSZ9477_CHIP_ID: case KSZ9563_CHIP_ID: case KSZ9567_CHIP_ID: @@ -2705,7 +2739,8 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, dev->chip_id == KSZ9563_CHIP_ID) proto = DSA_TAG_PROTO_KSZ9893; - if (dev->chip_id == KSZ9477_CHIP_ID || + if (dev->chip_id == KSZ8567_CHIP_ID || + dev->chip_id == KSZ9477_CHIP_ID || dev->chip_id == KSZ9896_CHIP_ID || dev->chip_id == KSZ9897_CHIP_ID || dev->chip_id == KSZ9567_CHIP_ID) @@ -2813,6 +2848,7 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port) case KSZ8830_CHIP_ID: return KSZ8863_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; case KSZ8563_CHIP_ID: + case KSZ8567_CHIP_ID: case KSZ9477_CHIP_ID: case KSZ9563_CHIP_ID: case KSZ9567_CHIP_ID: @@ -2839,6 +2875,7 @@ static int ksz_validate_eee(struct dsa_switch *ds, int port) switch (dev->chip_id) { case KSZ8563_CHIP_ID: + case KSZ8567_CHIP_ID: case KSZ9477_CHIP_ID: case KSZ9563_CHIP_ID: case KSZ9567_CHIP_ID: @@ -2852,7 +2889,7 @@ static int ksz_validate_eee(struct dsa_switch *ds, int port) } static int ksz_get_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) + struct ethtool_keee *e) { int ret; @@ -2872,7 +2909,7 @@ static int ksz_get_mac_eee(struct dsa_switch *ds, int port, } static int ksz_set_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) + struct ethtool_keee *e) { struct ksz_device *dev = ds->priv; int ret; @@ -3183,6 +3220,7 @@ static int ksz_switch_detect(struct ksz_device *dev) case KSZ9896_CHIP_ID: case KSZ9897_CHIP_ID: case KSZ9567_CHIP_ID: + case KSZ8567_CHIP_ID: case LAN9370_CHIP_ID: case LAN9371_CHIP_ID: case LAN9372_CHIP_ID: @@ -3220,6 +3258,7 @@ static int ksz_cls_flower_add(struct dsa_switch *ds, int port, switch (dev->chip_id) { case KSZ8563_CHIP_ID: + case KSZ8567_CHIP_ID: case KSZ9477_CHIP_ID: case KSZ9563_CHIP_ID: case KSZ9567_CHIP_ID: @@ -3239,6 +3278,7 @@ static int ksz_cls_flower_del(struct dsa_switch *ds, int port, switch (dev->chip_id) { case KSZ8563_CHIP_ID: + case KSZ8567_CHIP_ID: case KSZ9477_CHIP_ID: case KSZ9563_CHIP_ID: case KSZ9567_CHIP_ID: @@ -4142,6 +4182,7 @@ static int ksz_parse_drive_strength(struct ksz_device *dev) case KSZ8794_CHIP_ID: case KSZ8765_CHIP_ID: case KSZ8563_CHIP_ID: + case KSZ8567_CHIP_ID: case KSZ9477_CHIP_ID: case KSZ9563_CHIP_ID: case KSZ9567_CHIP_ID: diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 15612101a155..060c5de9aa05 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -187,6 +187,7 @@ struct ksz_device { /* List of supported models */ enum ksz_model { KSZ8563, + KSZ8567, KSZ8795, KSZ8794, KSZ8765, diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 6f6d878e742c..c8166fb440ab 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -165,6 +165,10 @@ static const struct of_device_id ksz_dt_ids[] = { .data = &ksz_switch_chips[KSZ8563] }, { + .compatible = "microchip,ksz8567", + .data = &ksz_switch_chips[KSZ8567] + }, + { .compatible = "microchip,ksz9567", .data = &ksz_switch_chips[KSZ9567] }, @@ -204,6 +208,7 @@ static const struct spi_device_id ksz_spi_ids[] = { { "ksz9893" }, { "ksz9563" }, { "ksz8563" }, + { "ksz8567" }, { "ksz9567" }, { "lan9370" }, { "lan9371" }, diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c index 088533663b83..fa3ee85a99c1 100644 --- a/drivers/net/dsa/mt7530-mdio.c +++ b/drivers/net/dsa/mt7530-mdio.c @@ -81,17 +81,14 @@ static const struct regmap_bus mt7530_regmap_bus = { }; static int -mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii) +mt7531_create_sgmii(struct mt7530_priv *priv) { struct regmap_config *mt7531_pcs_config[2] = {}; struct phylink_pcs *pcs; struct regmap *regmap; int i, ret = 0; - /* MT7531AE has two SGMII units for port 5 and port 6 - * MT7531BE has only one SGMII unit for port 6 - */ - for (i = dual_sgmii ? 0 : 1; i < 2; i++) { + for (i = priv->p5_sgmii ? 0 : 1; i < 2; i++) { mt7531_pcs_config[i] = devm_kzalloc(priv->dev, sizeof(struct regmap_config), GFP_KERNEL); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index cf2ff7680c15..03d966fa67b2 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -414,92 +414,57 @@ mt753x_preferred_default_local_cpu_port(struct dsa_switch *ds) } /* Setup port 6 interface mode and TRGMII TX circuit */ -static int -mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface) +static void +mt7530_setup_port6(struct dsa_switch *ds, phy_interface_t interface) { struct mt7530_priv *priv = ds->priv; - u32 ncpo1, ssc_delta, trgint, xtal; - - xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK; + u32 ncpo1, ssc_delta, xtal; - if (xtal == HWTRAP_XTAL_20MHZ) { - dev_err(priv->dev, - "%s: MT7530 with a 20MHz XTAL is not supported!\n", - __func__); - return -EINVAL; - } + /* Disable the MT7530 TRGMII clocks */ + core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); - switch (interface) { - case PHY_INTERFACE_MODE_RGMII: - trgint = 0; - break; - case PHY_INTERFACE_MODE_TRGMII: - trgint = 1; - if (xtal == HWTRAP_XTAL_25MHZ) - ssc_delta = 0x57; - else - ssc_delta = 0x87; - if (priv->id == ID_MT7621) { - /* PLL frequency: 125MHz: 1.0GBit */ - if (xtal == HWTRAP_XTAL_40MHZ) - ncpo1 = 0x0640; - if (xtal == HWTRAP_XTAL_25MHZ) - ncpo1 = 0x0a00; - } else { /* PLL frequency: 250MHz: 2.0Gbit */ - if (xtal == HWTRAP_XTAL_40MHZ) - ncpo1 = 0x0c80; - if (xtal == HWTRAP_XTAL_25MHZ) - ncpo1 = 0x1400; - } - break; - default: - dev_err(priv->dev, "xMII interface %d not supported\n", - interface); - return -EINVAL; + if (interface == PHY_INTERFACE_MODE_RGMII) { + mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, + P6_INTF_MODE(0)); + return; } - mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, - P6_INTF_MODE(trgint)); + mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1)); - if (trgint) { - /* Disable the MT7530 TRGMII clocks */ - core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); + xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK; - /* Setup the MT7530 TRGMII Tx Clock */ - core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); - core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); - core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); - core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); - core_write(priv, CORE_PLL_GROUP4, - RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN | - RG_SYSPLL_BIAS_LPF_EN); - core_write(priv, CORE_PLL_GROUP2, - RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | - RG_SYSPLL_POSDIV(1)); - core_write(priv, CORE_PLL_GROUP7, - RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) | - RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); + if (xtal == HWTRAP_XTAL_25MHZ) + ssc_delta = 0x57; + else + ssc_delta = 0x87; - /* Enable the MT7530 TRGMII clocks */ - core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); + if (priv->id == ID_MT7621) { + /* PLL frequency: 125MHz: 1.0GBit */ + if (xtal == HWTRAP_XTAL_40MHZ) + ncpo1 = 0x0640; + if (xtal == HWTRAP_XTAL_25MHZ) + ncpo1 = 0x0a00; + } else { /* PLL frequency: 250MHz: 2.0Gbit */ + if (xtal == HWTRAP_XTAL_40MHZ) + ncpo1 = 0x0c80; + if (xtal == HWTRAP_XTAL_25MHZ) + ncpo1 = 0x1400; } - return 0; -} - -static bool mt7531_dual_sgmii_supported(struct mt7530_priv *priv) -{ - u32 val; - - val = mt7530_read(priv, MT7531_TOP_SIG_SR); - - return (val & PAD_DUAL_SGMII_EN) != 0; -} + /* Setup the MT7530 TRGMII Tx Clock */ + core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); + core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); + core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); + core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); + core_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN | + RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN); + core_write(priv, CORE_PLL_GROUP2, RG_SYSPLL_EN_NORMAL | + RG_SYSPLL_VODEN | RG_SYSPLL_POSDIV(1)); + core_write(priv, CORE_PLL_GROUP7, RG_LCDDS_PCW_NCPO_CHG | + RG_LCCDS_C(3) | RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); -static int -mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface) -{ - return 0; + /* Enable the MT7530 TRGMII clocks */ + core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); } static void @@ -510,9 +475,6 @@ mt7531_pll_setup(struct mt7530_priv *priv) u32 xtal; u32 val; - if (mt7531_dual_sgmii_supported(priv)) - return; - val = mt7530_read(priv, MT7531_CREV); top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR); hwstrap = mt7530_read(priv, MT7531_HWTRAP); @@ -920,8 +882,6 @@ static const char *p5_intf_modes(unsigned int p5_interface) return "PHY P4"; case P5_INTF_SEL_GMAC5: return "GMAC5"; - case P5_INTF_SEL_GMAC5_SGMII: - return "GMAC5_SGMII"; default: return "unknown"; } @@ -956,13 +916,8 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */ val &= ~MHWTRAP_P5_DIS; break; - case P5_DISABLED: - interface = PHY_INTERFACE_MODE_NA; - break; default: - dev_err(ds->dev, "Unsupported p5_intf_sel %d\n", - priv->p5_intf_sel); - goto unlock_exit; + break; } /* Setup RGMII settings */ @@ -992,9 +947,6 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n", val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface)); - priv->p5_interface = interface; - -unlock_exit: mutex_unlock(&priv->reg_mutex); } @@ -1035,10 +987,6 @@ mt753x_cpu_port_enable(struct dsa_switch *ds, int port) mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | UNU_FFP(BIT(port))); - /* Set CPU port number */ - if (priv->id == ID_MT7530 || priv->id == ID_MT7621) - mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port)); - /* Add the CPU port to the CPU port bitmap for MT7531 and the switch on * the MT7988 SoC. Trapped frames will be forwarded to the CPU port that * is affine to the inbound user port. @@ -2285,6 +2233,12 @@ mt7530_setup(struct dsa_switch *ds) return -ENODEV; } + if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_20MHZ) { + dev_err(priv->dev, + "MT7530 with a 20MHz XTAL is not supported!\n"); + return -EINVAL; + } + /* Reset the switch through internal reset */ mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | @@ -2344,16 +2298,13 @@ mt7530_setup(struct dsa_switch *ds) return ret; /* Setup port 5 */ - priv->p5_intf_sel = P5_DISABLED; - interface = PHY_INTERFACE_MODE_NA; - if (!dsa_is_unused_port(ds, 5)) { priv->p5_intf_sel = P5_INTF_SEL_GMAC5; - ret = of_get_phy_mode(dsa_to_port(ds, 5)->dn, &interface); - if (ret && ret != -ENODEV) - return ret; } else { - /* Scan the ethernet nodes. look for GMAC1, lookup used phy */ + /* Scan the ethernet nodes. Look for GMAC1, lookup the used PHY. + * Set priv->p5_intf_sel to the appropriate value if PHY muxing + * is detected. + */ for_each_child_of_node(dn, mac_np) { if (!of_device_is_compatible(mac_np, "mediatek,eth-mac")) @@ -2384,6 +2335,10 @@ mt7530_setup(struct dsa_switch *ds) of_node_put(phy_node); break; } + + if (priv->p5_intf_sel == P5_INTF_SEL_PHY_P0 || + priv->p5_intf_sel == P5_INTF_SEL_PHY_P4) + mt7530_setup_port5(ds, interface); } #ifdef CONFIG_GPIOLIB @@ -2394,8 +2349,6 @@ mt7530_setup(struct dsa_switch *ds) } #endif /* CONFIG_GPIOLIB */ - mt7530_setup_port5(ds, interface); - /* Flush the FDB table */ ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); if (ret < 0) @@ -2492,6 +2445,12 @@ mt7531_setup(struct dsa_switch *ds) return -ENODEV; } + /* MT7531AE has got two SGMII units. One for port 5, one for port 6. + * MT7531BE has got only one SGMII unit which is for port 6. + */ + val = mt7530_read(priv, MT7531_TOP_SIG_SR); + priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN); + /* all MACs must be forced link-down before sw reset */ for (i = 0; i < MT7530_NUM_PORTS; i++) mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK); @@ -2501,21 +2460,18 @@ mt7531_setup(struct dsa_switch *ds) SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); - mt7531_pll_setup(priv); - - if (mt7531_dual_sgmii_supported(priv)) { - priv->p5_intf_sel = P5_INTF_SEL_GMAC5_SGMII; - + if (!priv->p5_sgmii) { + mt7531_pll_setup(priv); + } else { /* Let ds->user_mii_bus be able to access external phy. */ mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK, MT7531_EXT_P_MDC_11); mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK, MT7531_EXT_P_MDIO_12); - } else { - priv->p5_intf_sel = P5_INTF_SEL_GMAC5; } - dev_dbg(ds->dev, "P5 support %s interface\n", - p5_intf_modes(priv->p5_intf_sel)); + + if (!dsa_is_unused_port(ds, 5)) + priv->p5_intf_sel = P5_INTF_SEL_GMAC5; mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK, MT7531_GPIO0_INTERRUPT); @@ -2553,12 +2509,14 @@ static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { switch (port) { - case 0 ... 4: /* Internal phy */ + /* Ports which are connected to switch PHYs. There is no MII pinout. */ + case 0 ... 4: __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); break; - case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ + /* Port 5 supports rgmii with delays, mii, and gmii. */ + case 5: phy_interface_set_rgmii(config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); @@ -2566,7 +2524,8 @@ static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port, config->supported_interfaces); break; - case 6: /* 1st cpu port */ + /* Port 6 supports rgmii and trgmii. */ + case 6: __set_bit(PHY_INTERFACE_MODE_RGMII, config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_TRGMII, @@ -2575,30 +2534,30 @@ static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port, } } -static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port) -{ - return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); -} - static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { struct mt7530_priv *priv = ds->priv; switch (port) { - case 0 ... 4: /* Internal phy */ + /* Ports which are connected to switch PHYs. There is no MII pinout. */ + case 0 ... 4: __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); break; - case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */ - if (mt7531_is_rgmii_port(priv, port)) { + /* Port 5 supports rgmii with delays on MT7531BE, sgmii/802.3z on + * MT7531AE. + */ + case 5: + if (!priv->p5_sgmii) { phy_interface_set_rgmii(config->supported_interfaces); break; } fallthrough; - case 6: /* 1st cpu port supports sgmii/8023z only */ + /* Port 6 supports sgmii/802.3z. */ + case 6: __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_1000BASEX, @@ -2614,14 +2573,14 @@ static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port, static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { - phy_interface_zero(config->supported_interfaces); - switch (port) { - case 0 ... 4: /* Internal phy */ + /* Ports which are connected to switch PHYs. There is no MII pinout. */ + case 0 ... 3: __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); break; + /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */ case 6: __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); @@ -2631,24 +2590,15 @@ static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port, } static int -mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) -{ - struct mt7530_priv *priv = ds->priv; - - return priv->info->pad_setup(ds, state->interface); -} - -static int mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { struct mt7530_priv *priv = ds->priv; - /* Only need to setup port5. */ - if (port != 5) - return 0; - - mt7530_setup_port5(priv->ds, interface); + if (port == 5) + mt7530_setup_port5(priv->ds, interface); + else if (port == 6) + mt7530_setup_port6(priv->ds, interface); return 0; } @@ -2659,7 +2609,7 @@ static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port, { u32 val; - if (!mt7531_is_rgmii_port(priv, port)) { + if (priv->p5_sgmii) { dev_err(priv->dev, "RGMII mode is not available for port %d\n", port); return -EINVAL; @@ -2785,12 +2735,12 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, u32 mcr_cur, mcr_new; switch (port) { - case 0 ... 4: /* Internal phy */ + case 0 ... 4: if (state->interface != PHY_INTERFACE_MODE_GMII && state->interface != PHY_INTERFACE_MODE_INTERNAL) goto unsupported; break; - case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ + case 5: if (priv->p5_interface == state->interface) break; @@ -2800,12 +2750,10 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, if (priv->p5_intf_sel != P5_DISABLED) priv->p5_interface = state->interface; break; - case 6: /* 1st cpu port */ + case 6: if (priv->p6_interface == state->interface) break; - mt753x_pad_setup(ds, state); - if (mt753x_mac_config(ds, port, mode, state) < 0) goto unsupported; @@ -2856,8 +2804,7 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, /* MT753x MAC works in 1G full duplex mode for all up-clocked * variants. */ - if (interface == PHY_INTERFACE_MODE_INTERNAL || - interface == PHY_INTERFACE_MODE_TRGMII || + if (interface == PHY_INTERFACE_MODE_TRGMII || (phy_interface_mode_is_8023z(interface))) { speed = SPEED_1000; duplex = DUPLEX_FULL; @@ -2903,7 +2850,7 @@ mt7531_cpu_port_config(struct dsa_switch *ds, int port) switch (port) { case 5: - if (mt7531_is_rgmii_port(priv, port)) + if (!priv->p5_sgmii) interface = PHY_INTERFACE_MODE_RGMII; else interface = PHY_INTERFACE_MODE_2500BASEX; @@ -3055,7 +3002,7 @@ mt753x_setup(struct dsa_switch *ds) mt7530_free_irq_common(priv); if (priv->create_sgmii) { - ret = priv->create_sgmii(priv, mt7531_dual_sgmii_supported(priv)); + ret = priv->create_sgmii(priv); if (ret && priv->irq) mt7530_free_irq(priv); } @@ -3064,7 +3011,7 @@ mt753x_setup(struct dsa_switch *ds) } static int mt753x_get_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) + struct ethtool_keee *e) { struct mt7530_priv *priv = ds->priv; u32 eeecr = mt7530_read(priv, MT7530_PMEEECR_P(port)); @@ -3076,7 +3023,7 @@ static int mt753x_get_mac_eee(struct dsa_switch *ds, int port, } static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) + struct ethtool_keee *e) { struct mt7530_priv *priv = ds->priv; u32 set, mask = LPI_THRESH_MASK | LPI_MODE_EN; @@ -3093,9 +3040,34 @@ static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, return 0; } -static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface) +static void +mt753x_conduit_state_change(struct dsa_switch *ds, + const struct net_device *conduit, + bool operational) { - return 0; + struct dsa_port *cpu_dp = conduit->dsa_ptr; + struct mt7530_priv *priv = ds->priv; + int val = 0; + u8 mask; + + /* Set the CPU port to trap frames to for MT7530. Trapped frames will be + * forwarded to the numerically smallest CPU port whose conduit + * interface is up. + */ + if (priv->id != ID_MT7530 && priv->id != ID_MT7621) + return; + + mask = BIT(cpu_dp->index); + + if (operational) + priv->active_cpu_ports |= mask; + else + priv->active_cpu_ports &= ~mask; + + if (priv->active_cpu_ports) + val = CPU_EN | CPU_PORT(__ffs(priv->active_cpu_ports)); + + mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val); } static int mt7988_setup(struct dsa_switch *ds) @@ -3148,6 +3120,7 @@ const struct dsa_switch_ops mt7530_switch_ops = { .phylink_mac_link_up = mt753x_phylink_mac_link_up, .get_mac_eee = mt753x_get_mac_eee, .set_mac_eee = mt753x_set_mac_eee, + .conduit_state_change = mt753x_conduit_state_change, }; EXPORT_SYMBOL_GPL(mt7530_switch_ops); @@ -3160,7 +3133,6 @@ const struct mt753x_info mt753x_table[] = { .phy_write_c22 = mt7530_phy_write_c22, .phy_read_c45 = mt7530_phy_read_c45, .phy_write_c45 = mt7530_phy_write_c45, - .pad_setup = mt7530_pad_clk_setup, .mac_port_get_caps = mt7530_mac_port_get_caps, .mac_port_config = mt7530_mac_config, }, @@ -3172,7 +3144,6 @@ const struct mt753x_info mt753x_table[] = { .phy_write_c22 = mt7530_phy_write_c22, .phy_read_c45 = mt7530_phy_read_c45, .phy_write_c45 = mt7530_phy_write_c45, - .pad_setup = mt7530_pad_clk_setup, .mac_port_get_caps = mt7530_mac_port_get_caps, .mac_port_config = mt7530_mac_config, }, @@ -3184,7 +3155,6 @@ const struct mt753x_info mt753x_table[] = { .phy_write_c22 = mt7531_ind_c22_phy_write, .phy_read_c45 = mt7531_ind_c45_phy_read, .phy_write_c45 = mt7531_ind_c45_phy_write, - .pad_setup = mt7531_pad_setup, .cpu_port_config = mt7531_cpu_port_config, .mac_port_get_caps = mt7531_mac_port_get_caps, .mac_port_config = mt7531_mac_config, @@ -3197,7 +3167,6 @@ const struct mt753x_info mt753x_table[] = { .phy_write_c22 = mt7531_ind_c22_phy_write, .phy_read_c45 = mt7531_ind_c45_phy_read, .phy_write_c45 = mt7531_ind_c45_phy_write, - .pad_setup = mt7988_pad_setup, .cpu_port_config = mt7988_cpu_port_config, .mac_port_get_caps = mt7988_mac_port_get_caps, .mac_port_config = mt7988_mac_config, @@ -3227,9 +3196,8 @@ mt7530_probe_common(struct mt7530_priv *priv) /* Sanity check if these required device operations are filled * properly. */ - if (!priv->info->sw_setup || !priv->info->pad_setup || - !priv->info->phy_read_c22 || !priv->info->phy_write_c22 || - !priv->info->mac_port_get_caps || + if (!priv->info->sw_setup || !priv->info->phy_read_c22 || + !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps || !priv->info->mac_port_config) return -EINVAL; diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 17e42d30fff4..26a6d2160c08 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -41,8 +41,8 @@ enum mt753x_id { #define UNU_FFP(x) (((x) & 0xff) << 8) #define UNU_FFP_MASK UNU_FFP(~0) #define CPU_EN BIT(7) -#define CPU_PORT(x) ((x) << 4) -#define CPU_MASK (0xf << 4) +#define CPU_PORT_MASK GENMASK(6, 4) +#define CPU_PORT(x) FIELD_PREP(CPU_PORT_MASK, x) #define MIRROR_EN BIT(3) #define MIRROR_PORT(x) ((x) & 0x7) #define MIRROR_MASK 0x7 @@ -683,11 +683,10 @@ struct mt7530_port { /* Port 5 interface select definitions */ enum p5_interface_select { - P5_DISABLED = 0, + P5_DISABLED, P5_INTF_SEL_PHY_P0, P5_INTF_SEL_PHY_P4, P5_INTF_SEL_GMAC5, - P5_INTF_SEL_GMAC5_SGMII, }; struct mt7530_priv; @@ -705,8 +704,6 @@ struct mt753x_pcs { * @phy_write_c22: Holding the way writing PHY port using C22 * @phy_read_c45: Holding the way reading PHY port using C45 * @phy_write_c45: Holding the way writing PHY port using C45 - * @pad_setup: Holding the way setting up the bus pad for a certain - * MAC port * @phy_mode_supported: Check if the PHY type is being supported on a certain * port * @mac_port_validate: Holding the way to set addition validate type for a @@ -727,7 +724,6 @@ struct mt753x_info { int regnum); int (*phy_write_c45)(struct mt7530_priv *priv, int port, int devad, int regnum, u16 val); - int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface); int (*cpu_port_config)(struct dsa_switch *ds, int port); void (*mac_port_get_caps)(struct dsa_switch *ds, int port, struct phylink_config *config); @@ -756,10 +752,13 @@ struct mt753x_info { * registers * @p6_interface Holding the current port 6 interface * @p5_intf_sel: Holding the current port 5 interface select + * @p5_sgmii: Flag for distinguishing if port 5 of the MT7531 switch + * has got SGMII * @irq: IRQ number of the switch * @irq_domain: IRQ domain of the switch irq_chip * @irq_enable: IRQ enable bits, synced to SYS_INT_EN * @create_sgmii: Pointer to function creating SGMII PCS instance(s) + * @active_cpu_ports: Holding the active CPU ports */ struct mt7530_priv { struct device *dev; @@ -775,7 +774,8 @@ struct mt7530_priv { bool mcm; phy_interface_t p6_interface; phy_interface_t p5_interface; - unsigned int p5_intf_sel; + enum p5_interface_select p5_intf_sel; + bool p5_sgmii; u8 mirror_rx; u8 mirror_tx; struct mt7530_port ports[MT7530_NUM_PORTS]; @@ -785,7 +785,8 @@ struct mt7530_priv { int irq; struct irq_domain *irq_domain; u32 irq_enable; - int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii); + int (*create_sgmii)(struct mt7530_priv *priv); + u8 active_cpu_ports; }; struct mt7530_hw_vlan_entry { diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 383b3c4d6f59..9caecb4dfbfa 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1451,14 +1451,14 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, } static int mv88e6xxx_get_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) + struct ethtool_keee *e) { /* Nothing to do on the port's MAC */ return 0; } static int mv88e6xxx_set_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) + struct ethtool_keee *e) { /* Nothing to do on the port's MAC */ return 0; @@ -3659,7 +3659,7 @@ static int mv88e6xxx_mdio_read_c45(struct mii_bus *bus, int phy, int devad, int err; if (!chip->info->ops->phy_read_c45) - return -EOPNOTSUPP; + return -ENODEV; mv88e6xxx_reg_lock(chip); err = chip->info->ops->phy_read_c45(chip, bus, phy, devad, reg, &val); diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index c51f40960961..dab66c0c6f64 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -950,15 +950,15 @@ qca8k_mdio_register(struct qca8k_priv *priv) struct device *dev = ds->dev; struct device_node *mdio; struct mii_bus *bus; - int err = 0; + int ret = 0; mdio = of_get_child_by_name(dev->of_node, "mdio"); if (mdio && !of_device_is_available(mdio)) - goto out; + goto out_put_node; bus = devm_mdiobus_alloc(dev); if (!bus) { - err = -ENOMEM; + ret = -ENOMEM; goto out_put_node; } @@ -984,12 +984,11 @@ qca8k_mdio_register(struct qca8k_priv *priv) bus->write = qca8k_legacy_mdio_write; } - err = devm_of_mdiobus_register(dev, bus, mdio); + ret = devm_of_mdiobus_register(dev, bus, mdio); out_put_node: of_node_put(mdio); -out: - return err; + return ret; } static int @@ -998,7 +997,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv) u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; struct device_node *ports, *port; phy_interface_t mode; - int err; + int ret; ports = of_get_child_by_name(priv->dev->of_node, "ports"); if (!ports) @@ -1008,11 +1007,11 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv) return -EINVAL; for_each_available_child_of_node(ports, port) { - err = of_property_read_u32(port, "reg", ®); - if (err) { + ret = of_property_read_u32(port, "reg", ®); + if (ret) { of_node_put(port); of_node_put(ports); - return err; + return ret; } if (!dsa_is_user_port(priv->ds, reg)) @@ -2051,12 +2050,11 @@ qca8k_sw_probe(struct mdio_device *mdiodev) priv->info = of_device_get_match_data(priv->dev); priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", - GPIOD_ASIS); + GPIOD_OUT_HIGH); if (IS_ERR(priv->reset_gpio)) return PTR_ERR(priv->reset_gpio); if (priv->reset_gpio) { - gpiod_set_value_cansleep(priv->reset_gpio, 1); /* The active low duration must be greater than 10 ms * and checkpatch.pl wants 20 ms. */ diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index 2358cd399c7e..7f80035c5441 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -534,7 +534,7 @@ int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) } int qca8k_set_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *eee) + struct ethtool_keee *eee) { u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); struct qca8k_priv *priv = ds->priv; @@ -558,7 +558,7 @@ exit: } int qca8k_get_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) + struct ethtool_keee *e) { /* Nothing to do on the port's MAC */ return 0; diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index c8785c36c54e..2184d8d2d5a9 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -518,8 +518,8 @@ void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset); /* Common eee function */ -int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee); -int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); +int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *eee); +int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e); /* Common bridge function */ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig index 060165a85fb7..6989972eebc3 100644 --- a/drivers/net/dsa/realtek/Kconfig +++ b/drivers/net/dsa/realtek/Kconfig @@ -16,37 +16,29 @@ menuconfig NET_DSA_REALTEK if NET_DSA_REALTEK config NET_DSA_REALTEK_MDIO - tristate "Realtek MDIO interface driver" + bool "Realtek MDIO interface support" depends on OF - depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB - depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB - depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB help Select to enable support for registering switches configured through MDIO. config NET_DSA_REALTEK_SMI - tristate "Realtek SMI interface driver" + bool "Realtek SMI interface support" depends on OF - depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB - depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB - depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB help Select to enable support for registering switches connected through SMI. config NET_DSA_REALTEK_RTL8365MB - tristate "Realtek RTL8365MB switch subdriver" - imply NET_DSA_REALTEK_SMI - imply NET_DSA_REALTEK_MDIO + tristate "Realtek RTL8365MB switch driver" + depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO select NET_DSA_TAG_RTL8_4 help Select to enable support for Realtek RTL8365MB-VC and RTL8367S. config NET_DSA_REALTEK_RTL8366RB - tristate "Realtek RTL8366RB switch subdriver" - imply NET_DSA_REALTEK_SMI - imply NET_DSA_REALTEK_MDIO + tristate "Realtek RTL8366RB switch driver" + depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO select NET_DSA_TAG_RTL4_A help Select to enable support for Realtek RTL8366RB. diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile index 0aab57252a7c..35491dc20d6d 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -1,6 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o -obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o +obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o +realtek_dsa-objs := rtl83xx.o + +ifdef CONFIG_NET_DSA_REALTEK_MDIO +realtek_dsa-objs += realtek-mdio.o +endif + +ifdef CONFIG_NET_DSA_REALTEK_SMI +realtek_dsa-objs += realtek-smi.o +endif + obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o rtl8366-objs := rtl8366-core.o rtl8366rb.o obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 292e6d087e8b..04b758e5a680 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -25,6 +25,8 @@ #include <linux/regmap.h> #include "realtek.h" +#include "realtek-mdio.h" +#include "rtl83xx.h" /* Read/write via mdiobus */ #define REALTEK_MDIO_CTRL0_REG 31 @@ -99,192 +101,87 @@ out_unlock: return ret; } -static void realtek_mdio_lock(void *ctx) -{ - struct realtek_priv *priv = ctx; - - mutex_lock(&priv->map_lock); -} - -static void realtek_mdio_unlock(void *ctx) -{ - struct realtek_priv *priv = ctx; - - mutex_unlock(&priv->map_lock); -} - -static const struct regmap_config realtek_mdio_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, +static const struct realtek_interface_info realtek_mdio_info = { .reg_read = realtek_mdio_read, .reg_write = realtek_mdio_write, - .cache_type = REGCACHE_NONE, - .lock = realtek_mdio_lock, - .unlock = realtek_mdio_unlock, }; -static const struct regmap_config realtek_mdio_nolock_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .reg_read = realtek_mdio_read, - .reg_write = realtek_mdio_write, - .cache_type = REGCACHE_NONE, - .disable_locking = true, -}; - -static int realtek_mdio_probe(struct mdio_device *mdiodev) +/** + * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch + * @mdiodev: mdio_device to probe on. + * + * This function should be used as the .probe in an mdio_driver. After + * calling the common probe function for both interfaces, it initializes the + * values specific for MDIO-connected devices. Finally, it calls a common + * function to register the DSA switch. + * + * Context: Can sleep. Takes and releases priv->map_lock. + * Return: Returns 0 on success, a negative error on failure. + */ +int realtek_mdio_probe(struct mdio_device *mdiodev) { - struct realtek_priv *priv; struct device *dev = &mdiodev->dev; - const struct realtek_variant *var; - struct regmap_config rc; - struct device_node *np; + struct realtek_priv *priv; int ret; - var = of_device_get_match_data(dev); - if (!var) - return -EINVAL; - - priv = devm_kzalloc(&mdiodev->dev, - size_add(sizeof(*priv), var->chip_data_sz), - GFP_KERNEL); - if (!priv) - return -ENOMEM; - - mutex_init(&priv->map_lock); + priv = rtl83xx_probe(dev, &realtek_mdio_info); + if (IS_ERR(priv)) + return PTR_ERR(priv); - rc = realtek_mdio_regmap_config; - rc.lock_arg = priv; - priv->map = devm_regmap_init(dev, NULL, priv, &rc); - if (IS_ERR(priv->map)) { - ret = PTR_ERR(priv->map); - dev_err(dev, "regmap init failed: %d\n", ret); - return ret; - } - - rc = realtek_mdio_nolock_regmap_config; - priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); - if (IS_ERR(priv->map_nolock)) { - ret = PTR_ERR(priv->map_nolock); - dev_err(dev, "regmap init failed: %d\n", ret); - return ret; - } - - priv->mdio_addr = mdiodev->addr; priv->bus = mdiodev->bus; - priv->dev = &mdiodev->dev; - priv->chip_data = (void *)priv + sizeof(*priv); - - priv->clk_delay = var->clk_delay; - priv->cmd_read = var->cmd_read; - priv->cmd_write = var->cmd_write; - priv->ops = var->ops; - + priv->mdio_addr = mdiodev->addr; priv->write_reg_noack = realtek_mdio_write; - np = dev->of_node; - - dev_set_drvdata(dev, priv); - - /* TODO: if power is software controlled, set up any regulators here */ - priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); - - priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(priv->reset)) { - dev_err(dev, "failed to get RESET GPIO\n"); - return PTR_ERR(priv->reset); - } - - if (priv->reset) { - gpiod_set_value(priv->reset, 1); - dev_dbg(dev, "asserted RESET\n"); - msleep(REALTEK_HW_STOP_DELAY); - gpiod_set_value(priv->reset, 0); - msleep(REALTEK_HW_START_DELAY); - dev_dbg(dev, "deasserted RESET\n"); - } - - ret = priv->ops->detect(priv); - if (ret) { - dev_err(dev, "unable to detect switch\n"); - return ret; - } - - priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); - if (!priv->ds) - return -ENOMEM; - - priv->ds->dev = dev; - priv->ds->num_ports = priv->num_ports; - priv->ds->priv = priv; - priv->ds->ops = var->ds_ops_mdio; - - ret = dsa_register_switch(priv->ds); + ret = rtl83xx_register_switch(priv); if (ret) { - dev_err(priv->dev, "unable to register switch ret = %d\n", ret); + rtl83xx_remove(priv); return ret; } return 0; } +EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA); -static void realtek_mdio_remove(struct mdio_device *mdiodev) +/** + * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch + * @mdiodev: mdio_device to be removed. + * + * This function should be used as the .remove_new in an mdio_driver. First + * it unregisters the DSA switch and then it calls the common remove function. + * + * Context: Can sleep. + * Return: Nothing. + */ +void realtek_mdio_remove(struct mdio_device *mdiodev) { struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); if (!priv) return; - dsa_unregister_switch(priv->ds); + rtl83xx_unregister_switch(priv); - /* leave the device reset asserted */ - if (priv->reset) - gpiod_set_value(priv->reset, 1); + rtl83xx_remove(priv); } +EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA); -static void realtek_mdio_shutdown(struct mdio_device *mdiodev) +/** + * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch + * @mdiodev: mdio_device shutting down. + * + * This function should be used as the .shutdown in a platform_driver. It calls + * the common shutdown function. + * + * Context: Can sleep. + * Return: Nothing. + */ +void realtek_mdio_shutdown(struct mdio_device *mdiodev) { struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); if (!priv) return; - dsa_switch_shutdown(priv->ds); - - dev_set_drvdata(&mdiodev->dev, NULL); + rtl83xx_shutdown(priv); } - -static const struct of_device_id realtek_mdio_of_match[] = { -#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) - { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, -#endif -#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) - { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, -#endif - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); - -static struct mdio_driver realtek_mdio_driver = { - .mdiodrv.driver = { - .name = "realtek-mdio", - .of_match_table = realtek_mdio_of_match, - }, - .probe = realtek_mdio_probe, - .remove = realtek_mdio_remove, - .shutdown = realtek_mdio_shutdown, -}; - -mdio_module_driver(realtek_mdio_driver); - -MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>"); -MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); -MODULE_LICENSE("GPL"); +EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); diff --git a/drivers/net/dsa/realtek/realtek-mdio.h b/drivers/net/dsa/realtek/realtek-mdio.h new file mode 100644 index 000000000000..ee70f6a5b8ff --- /dev/null +++ b/drivers/net/dsa/realtek/realtek-mdio.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _REALTEK_MDIO_H +#define _REALTEK_MDIO_H + +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) + +static inline int realtek_mdio_driver_register(struct mdio_driver *drv) +{ + return mdio_driver_register(drv); +} + +static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv) +{ + mdio_driver_unregister(drv); +} + +int realtek_mdio_probe(struct mdio_device *mdiodev); +void realtek_mdio_remove(struct mdio_device *mdiodev); +void realtek_mdio_shutdown(struct mdio_device *mdiodev); + +#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */ + +static inline int realtek_mdio_driver_register(struct mdio_driver *drv) +{ + return 0; +} + +static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv) +{ +} + +static inline int realtek_mdio_probe(struct mdio_device *mdiodev) +{ + return -ENOENT; +} + +static inline void realtek_mdio_remove(struct mdio_device *mdiodev) +{ +} + +static inline void realtek_mdio_shutdown(struct mdio_device *mdiodev) +{ +} + +#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */ + +#endif /* _REALTEK_MDIO_H */ diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 755546ed8db6..88590ae95a75 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -31,7 +31,6 @@ #include <linux/spinlock.h> #include <linux/skbuff.h> #include <linux/of.h> -#include <linux/of_mdio.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/platform_device.h> @@ -40,12 +39,14 @@ #include <linux/if_bridge.h> #include "realtek.h" +#include "realtek-smi.h" +#include "rtl83xx.h" #define REALTEK_SMI_ACK_RETRY_COUNT 5 static inline void realtek_smi_clk_delay(struct realtek_priv *priv) { - ndelay(priv->clk_delay); + ndelay(priv->variant->clk_delay); } static void realtek_smi_start(struct realtek_priv *priv) @@ -208,7 +209,7 @@ static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) realtek_smi_start(priv); /* Send READ command */ - ret = realtek_smi_write_byte(priv, priv->cmd_read); + ret = realtek_smi_write_byte(priv, priv->variant->cmd_read); if (ret) goto out; @@ -249,7 +250,7 @@ static int realtek_smi_write_reg(struct realtek_priv *priv, realtek_smi_start(priv); /* Send WRITE command */ - ret = realtek_smi_write_byte(priv, priv->cmd_write); + ret = realtek_smi_write_byte(priv, priv->variant->cmd_write); if (ret) goto out; @@ -310,258 +311,98 @@ static int realtek_smi_read(void *ctx, u32 reg, u32 *val) return realtek_smi_read_reg(priv, reg, val); } -static void realtek_smi_lock(void *ctx) -{ - struct realtek_priv *priv = ctx; - - mutex_lock(&priv->map_lock); -} - -static void realtek_smi_unlock(void *ctx) -{ - struct realtek_priv *priv = ctx; - - mutex_unlock(&priv->map_lock); -} - -static const struct regmap_config realtek_smi_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, +static const struct realtek_interface_info realtek_smi_info = { .reg_read = realtek_smi_read, .reg_write = realtek_smi_write, - .cache_type = REGCACHE_NONE, - .lock = realtek_smi_lock, - .unlock = realtek_smi_unlock, }; -static const struct regmap_config realtek_smi_nolock_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .reg_read = realtek_smi_read, - .reg_write = realtek_smi_write, - .cache_type = REGCACHE_NONE, - .disable_locking = true, -}; - -static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) -{ - struct realtek_priv *priv = bus->priv; - - return priv->ops->phy_read(priv, addr, regnum); -} - -static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) -{ - struct realtek_priv *priv = bus->priv; - - return priv->ops->phy_write(priv, addr, regnum, val); -} - -static int realtek_smi_setup_mdio(struct dsa_switch *ds) -{ - struct realtek_priv *priv = ds->priv; - struct device_node *mdio_np; - int ret; - - mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio"); - if (!mdio_np) { - dev_err(priv->dev, "no MDIO bus node\n"); - return -ENODEV; - } - - priv->user_mii_bus = devm_mdiobus_alloc(priv->dev); - if (!priv->user_mii_bus) { - ret = -ENOMEM; - goto err_put_node; - } - priv->user_mii_bus->priv = priv; - priv->user_mii_bus->name = "SMI user MII"; - priv->user_mii_bus->read = realtek_smi_mdio_read; - priv->user_mii_bus->write = realtek_smi_mdio_write; - snprintf(priv->user_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", - ds->index); - priv->user_mii_bus->dev.of_node = mdio_np; - priv->user_mii_bus->parent = priv->dev; - ds->user_mii_bus = priv->user_mii_bus; - - ret = devm_of_mdiobus_register(priv->dev, priv->user_mii_bus, mdio_np); - if (ret) { - dev_err(priv->dev, "unable to register MDIO bus %s\n", - priv->user_mii_bus->id); - goto err_put_node; - } - - return 0; - -err_put_node: - of_node_put(mdio_np); - - return ret; -} - -static int realtek_smi_probe(struct platform_device *pdev) +/** + * realtek_smi_probe() - Probe a platform device for an SMI-connected switch + * @pdev: platform_device to probe on. + * + * This function should be used as the .probe in a platform_driver. After + * calling the common probe function for both interfaces, it initializes the + * values specific for SMI-connected devices. Finally, it calls a common + * function to register the DSA switch. + * + * Context: Can sleep. Takes and releases priv->map_lock. + * Return: Returns 0 on success, a negative error on failure. + */ +int realtek_smi_probe(struct platform_device *pdev) { - const struct realtek_variant *var; struct device *dev = &pdev->dev; struct realtek_priv *priv; - struct regmap_config rc; - struct device_node *np; int ret; - var = of_device_get_match_data(dev); - np = dev->of_node; - - priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->chip_data = (void *)priv + sizeof(*priv); - - mutex_init(&priv->map_lock); - - rc = realtek_smi_regmap_config; - rc.lock_arg = priv; - priv->map = devm_regmap_init(dev, NULL, priv, &rc); - if (IS_ERR(priv->map)) { - ret = PTR_ERR(priv->map); - dev_err(dev, "regmap init failed: %d\n", ret); - return ret; - } - - rc = realtek_smi_nolock_regmap_config; - priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); - if (IS_ERR(priv->map_nolock)) { - ret = PTR_ERR(priv->map_nolock); - dev_err(dev, "regmap init failed: %d\n", ret); - return ret; - } - - /* Link forward and backward */ - priv->dev = dev; - priv->clk_delay = var->clk_delay; - priv->cmd_read = var->cmd_read; - priv->cmd_write = var->cmd_write; - priv->ops = var->ops; - - priv->setup_interface = realtek_smi_setup_mdio; - priv->write_reg_noack = realtek_smi_write_reg_noack; - - dev_set_drvdata(dev, priv); - spin_lock_init(&priv->lock); - - /* TODO: if power is software controlled, set up any regulators here */ - - priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(priv->reset)) { - dev_err(dev, "failed to get RESET GPIO\n"); - return PTR_ERR(priv->reset); - } - if (priv->reset) { - gpiod_set_value(priv->reset, 1); - dev_dbg(dev, "asserted RESET\n"); - msleep(REALTEK_HW_STOP_DELAY); - gpiod_set_value(priv->reset, 0); - msleep(REALTEK_HW_START_DELAY); - dev_dbg(dev, "deasserted RESET\n"); - } + priv = rtl83xx_probe(dev, &realtek_smi_info); + if (IS_ERR(priv)) + return PTR_ERR(priv); /* Fetch MDIO pins */ priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); - if (IS_ERR(priv->mdc)) + if (IS_ERR(priv->mdc)) { + rtl83xx_remove(priv); return PTR_ERR(priv->mdc); + } + priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); - if (IS_ERR(priv->mdio)) + if (IS_ERR(priv->mdio)) { + rtl83xx_remove(priv); return PTR_ERR(priv->mdio); - - priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); - - ret = priv->ops->detect(priv); - if (ret) { - dev_err(dev, "unable to detect switch\n"); - return ret; } - priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); - if (!priv->ds) - return -ENOMEM; - - priv->ds->dev = dev; - priv->ds->num_ports = priv->num_ports; - priv->ds->priv = priv; + priv->write_reg_noack = realtek_smi_write_reg_noack; - priv->ds->ops = var->ds_ops_smi; - ret = dsa_register_switch(priv->ds); + ret = rtl83xx_register_switch(priv); if (ret) { - dev_err_probe(dev, ret, "unable to register switch\n"); + rtl83xx_remove(priv); return ret; } + return 0; } +EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA); -static void realtek_smi_remove(struct platform_device *pdev) +/** + * realtek_smi_remove() - Remove the driver of a SMI-connected switch + * @pdev: platform_device to be removed. + * + * This function should be used as the .remove_new in a platform_driver. First + * it unregisters the DSA switch and then it calls the common remove function. + * + * Context: Can sleep. + * Return: Nothing. + */ +void realtek_smi_remove(struct platform_device *pdev) { struct realtek_priv *priv = platform_get_drvdata(pdev); if (!priv) return; - dsa_unregister_switch(priv->ds); - if (priv->user_mii_bus) - of_node_put(priv->user_mii_bus->dev.of_node); + rtl83xx_unregister_switch(priv); - /* leave the device reset asserted */ - if (priv->reset) - gpiod_set_value(priv->reset, 1); + rtl83xx_remove(priv); } +EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); -static void realtek_smi_shutdown(struct platform_device *pdev) +/** + * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch + * @pdev: platform_device shutting down. + * + * This function should be used as the .shutdown in a platform_driver. It calls + * the common shutdown function. + * + * Context: Can sleep. + * Return: Nothing. + */ +void realtek_smi_shutdown(struct platform_device *pdev) { struct realtek_priv *priv = platform_get_drvdata(pdev); if (!priv) return; - dsa_switch_shutdown(priv->ds); - - platform_set_drvdata(pdev, NULL); + rtl83xx_shutdown(priv); } - -static const struct of_device_id realtek_smi_of_match[] = { -#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) - { - .compatible = "realtek,rtl8366rb", - .data = &rtl8366rb_variant, - }, -#endif -#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) - { - .compatible = "realtek,rtl8365mb", - .data = &rtl8365mb_variant, - }, -#endif - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, realtek_smi_of_match); - -static struct platform_driver realtek_smi_driver = { - .driver = { - .name = "realtek-smi", - .of_match_table = realtek_smi_of_match, - }, - .probe = realtek_smi_probe, - .remove_new = realtek_smi_remove, - .shutdown = realtek_smi_shutdown, -}; -module_platform_driver(realtek_smi_driver); - -MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); -MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); -MODULE_LICENSE("GPL"); +EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); diff --git a/drivers/net/dsa/realtek/realtek-smi.h b/drivers/net/dsa/realtek/realtek-smi.h new file mode 100644 index 000000000000..ea49a2edd3c8 --- /dev/null +++ b/drivers/net/dsa/realtek/realtek-smi.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _REALTEK_SMI_H +#define _REALTEK_SMI_H + +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) + +static inline int realtek_smi_driver_register(struct platform_driver *drv) +{ + return platform_driver_register(drv); +} + +static inline void realtek_smi_driver_unregister(struct platform_driver *drv) +{ + platform_driver_unregister(drv); +} + +int realtek_smi_probe(struct platform_device *pdev); +void realtek_smi_remove(struct platform_device *pdev); +void realtek_smi_shutdown(struct platform_device *pdev); + +#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */ + +static inline int realtek_smi_driver_register(struct platform_driver *drv) +{ + return 0; +} + +static inline void realtek_smi_driver_unregister(struct platform_driver *drv) +{ +} + +static inline int realtek_smi_probe(struct platform_device *pdev) +{ + return -ENOENT; +} + +static inline void realtek_smi_remove(struct platform_device *pdev) +{ +} + +static inline void realtek_smi_shutdown(struct platform_device *pdev) +{ +} + +#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */ + +#endif /* _REALTEK_SMI_H */ diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index 790488e9c667..b80bfde1ad04 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -58,11 +58,10 @@ struct realtek_priv { struct mii_bus *bus; int mdio_addr; - unsigned int clk_delay; - u8 cmd_read; - u8 cmd_write; + const struct realtek_variant *variant; + spinlock_t lock; /* Locks around command writes */ - struct dsa_switch *ds; + struct dsa_switch ds; struct irq_domain *irqdomain; bool leds_disabled; @@ -73,7 +72,6 @@ struct realtek_priv { struct rtl8366_mib_counter *mib_counters; const struct realtek_ops *ops; - int (*setup_interface)(struct dsa_switch *ds); int (*write_reg_noack)(void *ctx, u32 addr, u32 data); int vlan_enabled; @@ -91,7 +89,6 @@ struct realtek_ops { int (*detect)(struct realtek_priv *priv); int (*reset_chip)(struct realtek_priv *priv); int (*setup)(struct realtek_priv *priv); - void (*cleanup)(struct realtek_priv *priv); int (*get_mib_counter)(struct realtek_priv *priv, int port, struct rtl8366_mib_counter *mib, @@ -116,8 +113,7 @@ struct realtek_ops { }; struct realtek_variant { - const struct dsa_switch_ops *ds_ops_smi; - const struct dsa_switch_ops *ds_ops_mdio; + const struct dsa_switch_ops *ds_ops; const struct realtek_ops *ops; unsigned int clk_delay; u8 cmd_read; diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index b072045eb154..12665a8a3412 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -101,6 +101,9 @@ #include <linux/if_vlan.h> #include "realtek.h" +#include "realtek-smi.h" +#include "realtek-mdio.h" +#include "rtl83xx.h" /* Family-specific data and limits */ #define RTL8365MB_PHYADDRMAX 7 @@ -206,10 +209,10 @@ #define RTL8365MB_EXT_PORT_MODE_100FX 13 /* External interface mode configuration registers 0~1 */ -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT0,EXT1 */ #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ - ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ + ((_extint) <= 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ 0x0) #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ @@ -689,7 +692,7 @@ static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, u32 val; int ret; - mutex_lock(&priv->map_lock); + rtl83xx_lock(priv); ret = rtl8365mb_phy_poll_busy(priv); if (ret) @@ -722,7 +725,7 @@ static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, *data = val & 0xFFFF; out: - mutex_unlock(&priv->map_lock); + rtl83xx_unlock(priv); return ret; } @@ -733,7 +736,7 @@ static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, u32 val; int ret; - mutex_lock(&priv->map_lock); + rtl83xx_lock(priv); ret = rtl8365mb_phy_poll_busy(priv); if (ret) @@ -764,7 +767,7 @@ static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, goto out; out: - mutex_unlock(&priv->map_lock); + rtl83xx_unlock(priv); return 0; } @@ -825,17 +828,6 @@ static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, return 0; } -static int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) -{ - return rtl8365mb_phy_read(ds->priv, phy, regnum); -} - -static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, - u16 val) -{ - return rtl8365mb_phy_write(ds->priv, phy, regnum, val); -} - static const struct rtl8365mb_extint * rtl8365mb_get_port_extint(struct realtek_priv *priv, int port) { @@ -878,6 +870,7 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port, { const struct rtl8365mb_extint *extint = rtl8365mb_get_port_extint(priv, port); + struct dsa_switch *ds = &priv->ds; struct device_node *dn; struct dsa_port *dp; int tx_delay = 0; @@ -888,7 +881,7 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port, if (!extint) return -ENODEV; - dp = dsa_to_port(priv->ds, port); + dp = dsa_to_port(ds, port); dn = dp->dn; /* Set the RGMII TX/RX delay @@ -1541,6 +1534,7 @@ static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, static void rtl8365mb_stats_setup(struct realtek_priv *priv) { struct rtl8365mb *mb = priv->chip_data; + struct dsa_switch *ds = &priv->ds; int i; /* Per-chip global mutex to protect MIB counter access, since doing @@ -1551,7 +1545,7 @@ static void rtl8365mb_stats_setup(struct realtek_priv *priv) for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(priv->ds, i)) + if (dsa_is_unused_port(ds, i)) continue; /* Per-port spinlock to protect the stats64 data */ @@ -1567,12 +1561,13 @@ static void rtl8365mb_stats_setup(struct realtek_priv *priv) static void rtl8365mb_stats_teardown(struct realtek_priv *priv) { struct rtl8365mb *mb = priv->chip_data; + struct dsa_switch *ds = &priv->ds; int i; for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(priv->ds, i)) + if (dsa_is_unused_port(ds, i)) continue; cancel_delayed_work_sync(&p->mib_work); @@ -1971,7 +1966,7 @@ static int rtl8365mb_setup(struct dsa_switch *ds) dev_info(priv->dev, "no interrupt support\n"); /* Configure CPU tagging */ - dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) { + dsa_switch_for_each_cpu_port(cpu_dp, ds) { cpu->mask |= BIT(cpu_dp->index); if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) @@ -1986,7 +1981,7 @@ static int rtl8365mb_setup(struct dsa_switch *ds) for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(priv->ds, i)) + if (dsa_is_unused_port(ds, i)) continue; /* Forward only to the CPU */ @@ -2003,7 +1998,7 @@ static int rtl8365mb_setup(struct dsa_switch *ds) * ports will still forward frames to the CPU despite being * administratively down by default. */ - rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED); + rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED); /* Set up per-port private data */ p->priv = priv; @@ -2014,12 +2009,10 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; - if (priv->setup_interface) { - ret = priv->setup_interface(ds); - if (ret) { - dev_err(priv->dev, "could not set up MDIO bus\n"); - goto out_teardown_irq; - } + ret = rtl83xx_setup_user_mdio(ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + goto out_teardown_irq; } /* Start statistics counter polling */ @@ -2113,7 +2106,7 @@ static int rtl8365mb_detect(struct realtek_priv *priv) return 0; } -static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { +static const struct dsa_switch_ops rtl8365mb_switch_ops = { .get_tag_protocol = rtl8365mb_get_tag_protocol, .change_tag_protocol = rtl8365mb_change_tag_protocol, .setup = rtl8365mb_setup, @@ -2134,29 +2127,6 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { .port_max_mtu = rtl8365mb_port_max_mtu, }; -static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { - .get_tag_protocol = rtl8365mb_get_tag_protocol, - .change_tag_protocol = rtl8365mb_change_tag_protocol, - .setup = rtl8365mb_setup, - .teardown = rtl8365mb_teardown, - .phylink_get_caps = rtl8365mb_phylink_get_caps, - .phylink_mac_config = rtl8365mb_phylink_mac_config, - .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, - .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, - .phy_read = rtl8365mb_dsa_phy_read, - .phy_write = rtl8365mb_dsa_phy_write, - .port_stp_state_set = rtl8365mb_port_stp_state_set, - .get_strings = rtl8365mb_get_strings, - .get_ethtool_stats = rtl8365mb_get_ethtool_stats, - .get_sset_count = rtl8365mb_get_sset_count, - .get_eth_phy_stats = rtl8365mb_get_phy_stats, - .get_eth_mac_stats = rtl8365mb_get_mac_stats, - .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, - .get_stats64 = rtl8365mb_get_stats64, - .port_change_mtu = rtl8365mb_port_change_mtu, - .port_max_mtu = rtl8365mb_port_max_mtu, -}; - static const struct realtek_ops rtl8365mb_ops = { .detect = rtl8365mb_detect, .phy_read = rtl8365mb_phy_read, @@ -2164,16 +2134,66 @@ static const struct realtek_ops rtl8365mb_ops = { }; const struct realtek_variant rtl8365mb_variant = { - .ds_ops_smi = &rtl8365mb_switch_ops_smi, - .ds_ops_mdio = &rtl8365mb_switch_ops_mdio, + .ds_ops = &rtl8365mb_switch_ops, .ops = &rtl8365mb_ops, .clk_delay = 10, .cmd_read = 0xb9, .cmd_write = 0xb8, .chip_data_sz = sizeof(struct rtl8365mb), }; -EXPORT_SYMBOL_GPL(rtl8365mb_variant); + +static const struct of_device_id rtl8365mb_of_match[] = { + { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, rtl8365mb_of_match); + +static struct platform_driver rtl8365mb_smi_driver = { + .driver = { + .name = "rtl8365mb-smi", + .of_match_table = rtl8365mb_of_match, + }, + .probe = realtek_smi_probe, + .remove_new = realtek_smi_remove, + .shutdown = realtek_smi_shutdown, +}; + +static struct mdio_driver rtl8365mb_mdio_driver = { + .mdiodrv.driver = { + .name = "rtl8365mb-mdio", + .of_match_table = rtl8365mb_of_match, + }, + .probe = realtek_mdio_probe, + .remove = realtek_mdio_remove, + .shutdown = realtek_mdio_shutdown, +}; + +static int rtl8365mb_init(void) +{ + int ret; + + ret = realtek_mdio_driver_register(&rtl8365mb_mdio_driver); + if (ret) + return ret; + + ret = realtek_smi_driver_register(&rtl8365mb_smi_driver); + if (ret) { + realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); + return ret; + } + + return 0; +} +module_init(rtl8365mb_init); + +static void __exit rtl8365mb_exit(void) +{ + realtek_smi_driver_unregister(&rtl8365mb_smi_driver); + realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); +} +module_exit(rtl8365mb_exit); MODULE_AUTHOR("Alvin Å ipraga <alsi@bang-olufsen.dk>"); MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(REALTEK_DSA); diff --git a/drivers/net/dsa/realtek/rtl8366-core.c b/drivers/net/dsa/realtek/rtl8366-core.c index 59f98d2c8769..7c6520ba3a26 100644 --- a/drivers/net/dsa/realtek/rtl8366-core.c +++ b/drivers/net/dsa/realtek/rtl8366-core.c @@ -34,7 +34,7 @@ int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used) return 0; } -EXPORT_SYMBOL_GPL(rtl8366_mc_is_used); +EXPORT_SYMBOL_NS_GPL(rtl8366_mc_is_used, REALTEK_DSA); /** * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration @@ -187,7 +187,7 @@ int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member, return ret; } -EXPORT_SYMBOL_GPL(rtl8366_set_vlan); +EXPORT_SYMBOL_NS_GPL(rtl8366_set_vlan, REALTEK_DSA); int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, unsigned int vid) @@ -217,7 +217,7 @@ int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, return 0; } -EXPORT_SYMBOL_GPL(rtl8366_set_pvid); +EXPORT_SYMBOL_NS_GPL(rtl8366_set_pvid, REALTEK_DSA); int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) { @@ -243,7 +243,7 @@ int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) priv->vlan4k_enabled = enable; return 0; } -EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k); +EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan4k, REALTEK_DSA); int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) { @@ -265,7 +265,7 @@ int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) return ret; } -EXPORT_SYMBOL_GPL(rtl8366_enable_vlan); +EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan, REALTEK_DSA); int rtl8366_reset_vlan(struct realtek_priv *priv) { @@ -290,7 +290,7 @@ int rtl8366_reset_vlan(struct realtek_priv *priv) return 0; } -EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); +EXPORT_SYMBOL_NS_GPL(rtl8366_reset_vlan, REALTEK_DSA); int rtl8366_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan, @@ -345,7 +345,7 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, return 0; } -EXPORT_SYMBOL_GPL(rtl8366_vlan_add); +EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_add, REALTEK_DSA); int rtl8366_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) @@ -389,7 +389,7 @@ int rtl8366_vlan_del(struct dsa_switch *ds, int port, return 0; } -EXPORT_SYMBOL_GPL(rtl8366_vlan_del); +EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_del, REALTEK_DSA); void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) @@ -403,7 +403,7 @@ void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, for (i = 0; i < priv->num_mib_counters; i++) ethtool_puts(&data, priv->mib_counters[i].name); } -EXPORT_SYMBOL_GPL(rtl8366_get_strings); +EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, REALTEK_DSA); int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) { @@ -417,7 +417,7 @@ int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) return priv->num_mib_counters; } -EXPORT_SYMBOL_GPL(rtl8366_get_sset_count); +EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, REALTEK_DSA); void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { @@ -441,4 +441,4 @@ void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) data[i] = mibvalue; } } -EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats); +EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, REALTEK_DSA); diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index e3b6a470ca67..e10ae94cf771 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -23,6 +23,9 @@ #include <linux/regmap.h> #include "realtek.h" +#include "realtek-smi.h" +#include "realtek-mdio.h" +#include "rtl83xx.h" #define RTL8366RB_PORT_NUM_CPU 5 #define RTL8366RB_NUM_PORTS 6 @@ -1030,12 +1033,10 @@ static int rtl8366rb_setup(struct dsa_switch *ds) if (ret) dev_info(priv->dev, "no interrupt support\n"); - if (priv->setup_interface) { - ret = priv->setup_interface(ds); - if (ret) { - dev_err(priv->dev, "could not set up MDIO bus\n"); - return -ENODEV; - } + ret = rtl83xx_setup_user_mdio(ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + return -ENODEV; } return 0; @@ -1650,6 +1651,7 @@ static int rtl8366rb_get_mc_index(struct realtek_priv *priv, int port, int *val) static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index) { + struct dsa_switch *ds = &priv->ds; struct rtl8366rb *rb; bool pvid_enabled; int ret; @@ -1674,7 +1676,7 @@ static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index * not drop any untagged or C-tagged frames. Make sure to update the * filtering setting. */ - if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port))) + if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled); return ret; @@ -1718,7 +1720,7 @@ static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum) if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - mutex_lock(&priv->map_lock); + rtl83xx_lock(priv); ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_READ); @@ -1746,7 +1748,7 @@ static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum) phy, regnum, reg, val); out: - mutex_unlock(&priv->map_lock); + rtl83xx_unlock(priv); return ret; } @@ -1760,7 +1762,7 @@ static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - mutex_lock(&priv->map_lock); + rtl83xx_lock(priv); ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_WRITE); @@ -1777,22 +1779,11 @@ static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, goto out; out: - mutex_unlock(&priv->map_lock); + rtl83xx_unlock(priv); return ret; } -static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) -{ - return rtl8366rb_phy_read(ds->priv, phy, regnum); -} - -static int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, - u16 val) -{ - return rtl8366rb_phy_write(ds->priv, phy, regnum, val); -} - static int rtl8366rb_reset_chip(struct realtek_priv *priv) { int timeout = 10; @@ -1858,7 +1849,7 @@ static int rtl8366rb_detect(struct realtek_priv *priv) return 0; } -static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { +static const struct dsa_switch_ops rtl8366rb_switch_ops = { .get_tag_protocol = rtl8366_get_tag_protocol, .setup = rtl8366rb_setup, .phylink_get_caps = rtl8366rb_phylink_get_caps, @@ -1882,32 +1873,6 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { .port_max_mtu = rtl8366rb_max_mtu, }; -static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = { - .get_tag_protocol = rtl8366_get_tag_protocol, - .setup = rtl8366rb_setup, - .phy_read = rtl8366rb_dsa_phy_read, - .phy_write = rtl8366rb_dsa_phy_write, - .phylink_get_caps = rtl8366rb_phylink_get_caps, - .phylink_mac_link_up = rtl8366rb_mac_link_up, - .phylink_mac_link_down = rtl8366rb_mac_link_down, - .get_strings = rtl8366_get_strings, - .get_ethtool_stats = rtl8366_get_ethtool_stats, - .get_sset_count = rtl8366_get_sset_count, - .port_bridge_join = rtl8366rb_port_bridge_join, - .port_bridge_leave = rtl8366rb_port_bridge_leave, - .port_vlan_filtering = rtl8366rb_vlan_filtering, - .port_vlan_add = rtl8366_vlan_add, - .port_vlan_del = rtl8366_vlan_del, - .port_enable = rtl8366rb_port_enable, - .port_disable = rtl8366rb_port_disable, - .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, - .port_bridge_flags = rtl8366rb_port_bridge_flags, - .port_stp_state_set = rtl8366rb_port_stp_state_set, - .port_fast_age = rtl8366rb_port_fast_age, - .port_change_mtu = rtl8366rb_change_mtu, - .port_max_mtu = rtl8366rb_max_mtu, -}; - static const struct realtek_ops rtl8366rb_ops = { .detect = rtl8366rb_detect, .get_vlan_mc = rtl8366rb_get_vlan_mc, @@ -1925,16 +1890,66 @@ static const struct realtek_ops rtl8366rb_ops = { }; const struct realtek_variant rtl8366rb_variant = { - .ds_ops_smi = &rtl8366rb_switch_ops_smi, - .ds_ops_mdio = &rtl8366rb_switch_ops_mdio, + .ds_ops = &rtl8366rb_switch_ops, .ops = &rtl8366rb_ops, .clk_delay = 10, .cmd_read = 0xa9, .cmd_write = 0xa8, .chip_data_sz = sizeof(struct rtl8366rb), }; -EXPORT_SYMBOL_GPL(rtl8366rb_variant); + +static const struct of_device_id rtl8366rb_of_match[] = { + { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, rtl8366rb_of_match); + +static struct platform_driver rtl8366rb_smi_driver = { + .driver = { + .name = "rtl8366rb-smi", + .of_match_table = rtl8366rb_of_match, + }, + .probe = realtek_smi_probe, + .remove_new = realtek_smi_remove, + .shutdown = realtek_smi_shutdown, +}; + +static struct mdio_driver rtl8366rb_mdio_driver = { + .mdiodrv.driver = { + .name = "rtl8366rb-mdio", + .of_match_table = rtl8366rb_of_match, + }, + .probe = realtek_mdio_probe, + .remove = realtek_mdio_remove, + .shutdown = realtek_mdio_shutdown, +}; + +static int rtl8366rb_init(void) +{ + int ret; + + ret = realtek_mdio_driver_register(&rtl8366rb_mdio_driver); + if (ret) + return ret; + + ret = realtek_smi_driver_register(&rtl8366rb_smi_driver); + if (ret) { + realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver); + return ret; + } + + return 0; +} +module_init(rtl8366rb_init); + +static void __exit rtl8366rb_exit(void) +{ + realtek_smi_driver_unregister(&rtl8366rb_smi_driver); + realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver); +} +module_exit(rtl8366rb_exit); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(REALTEK_DSA); diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c new file mode 100644 index 000000000000..801873754df2 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/of_mdio.h> + +#include "realtek.h" +#include "rtl83xx.h" + +/** + * rtl83xx_lock() - Locks the mutex used by regmaps + * @ctx: realtek_priv pointer + * + * This function is passed to regmap to be used as the lock function. + * It is also used externally to block regmap before executing multiple + * operations that must happen in sequence (which will use + * realtek_priv.map_nolock instead). + * + * Context: Can sleep. Holds priv->map_lock lock. + * Return: nothing + */ +void rtl83xx_lock(void *ctx) +{ + struct realtek_priv *priv = ctx; + + mutex_lock(&priv->map_lock); +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, REALTEK_DSA); + +/** + * rtl83xx_unlock() - Unlocks the mutex used by regmaps + * @ctx: realtek_priv pointer + * + * This function unlocks the lock acquired by rtl83xx_lock. + * + * Context: Releases priv->map_lock lock. + * Return: nothing + */ +void rtl83xx_unlock(void *ctx) +{ + struct realtek_priv *priv = ctx; + + mutex_unlock(&priv->map_lock); +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA); + +static int rtl83xx_user_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_read(priv, addr, regnum); +} + +static int rtl83xx_user_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_write(priv, addr, regnum, val); +} + +/** + * rtl83xx_setup_user_mdio() - register the user mii bus driver + * @ds: DSA switch associated with this user_mii_bus + * + * Registers the MDIO bus for built-in Ethernet PHYs, and associates it with + * the mandatory 'mdio' child OF node of the switch. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_setup_user_mdio(struct dsa_switch *ds) +{ + struct realtek_priv *priv = ds->priv; + struct device_node *mdio_np; + struct mii_bus *bus; + int ret = 0; + + mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (!mdio_np) { + dev_err(priv->dev, "no MDIO bus node\n"); + return -ENODEV; + } + + bus = devm_mdiobus_alloc(priv->dev); + if (!bus) { + ret = -ENOMEM; + goto err_put_node; + } + + bus->priv = priv; + bus->name = "Realtek user MII"; + bus->read = rtl83xx_user_mdio_read; + bus->write = rtl83xx_user_mdio_write; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s:user_mii", dev_name(priv->dev)); + bus->parent = priv->dev; + + ret = devm_of_mdiobus_register(priv->dev, bus, mdio_np); + if (ret) { + dev_err(priv->dev, "unable to register MDIO bus %s\n", + bus->id); + goto err_put_node; + } + + priv->user_mii_bus = bus; + +err_put_node: + of_node_put(mdio_np); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_user_mdio, REALTEK_DSA); + +/** + * rtl83xx_probe() - probe a Realtek switch + * @dev: the device being probed + * @interface_info: specific management interface info. + * + * This function initializes realtek_priv and reads data from the device tree + * node. The switch is hard resetted if a method is provided. + * + * Context: Can sleep. + * Return: Pointer to the realtek_priv or ERR_PTR() in case of failure. + * + * The realtek_priv pointer does not need to be freed as it is controlled by + * devres. + */ +struct realtek_priv * +rtl83xx_probe(struct device *dev, + const struct realtek_interface_info *interface_info) +{ + const struct realtek_variant *var; + struct realtek_priv *priv; + struct regmap_config rc = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = interface_info->reg_read, + .reg_write = interface_info->reg_write, + .cache_type = REGCACHE_NONE, + .lock = rtl83xx_lock, + .unlock = rtl83xx_unlock, + }; + int ret; + + var = of_device_get_match_data(dev); + if (!var) + return ERR_PTR(-EINVAL); + + priv = devm_kzalloc(dev, size_add(sizeof(*priv), var->chip_data_sz), + GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + mutex_init(&priv->map_lock); + + rc.lock_arg = priv; + priv->map = devm_regmap_init(dev, NULL, priv, &rc); + if (IS_ERR(priv->map)) { + ret = PTR_ERR(priv->map); + dev_err(dev, "regmap init failed: %d\n", ret); + return ERR_PTR(ret); + } + + rc.disable_locking = true; + priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); + if (IS_ERR(priv->map_nolock)) { + ret = PTR_ERR(priv->map_nolock); + dev_err(dev, "regmap init failed: %d\n", ret); + return ERR_PTR(ret); + } + + /* Link forward and backward */ + priv->dev = dev; + priv->variant = var; + priv->ops = var->ops; + priv->chip_data = (void *)priv + sizeof(*priv); + + spin_lock_init(&priv->lock); + + priv->leds_disabled = of_property_read_bool(dev->of_node, + "realtek,disable-leds"); + + /* TODO: if power is software controlled, set up any regulators here */ + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(dev, "failed to get RESET GPIO\n"); + return ERR_CAST(priv->reset); + } + + dev_set_drvdata(dev, priv); + + if (priv->reset) { + gpiod_set_value(priv->reset, 1); + dev_dbg(dev, "asserted RESET\n"); + msleep(REALTEK_HW_STOP_DELAY); + gpiod_set_value(priv->reset, 0); + msleep(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } + + return priv; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REALTEK_DSA); + +/** + * rtl83xx_register_switch() - detects and register a switch + * @priv: realtek_priv pointer + * + * This function first checks the switch chip ID and register a DSA + * switch. + * + * Context: Can sleep. Takes and releases priv->map_lock. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_register_switch(struct realtek_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + int ret; + + ret = priv->ops->detect(priv); + if (ret) { + dev_err_probe(priv->dev, ret, "unable to detect switch\n"); + return ret; + } + + ds->priv = priv; + ds->dev = priv->dev; + ds->ops = priv->variant->ds_ops; + ds->num_ports = priv->num_ports; + + ret = dsa_register_switch(ds); + if (ret) { + dev_err_probe(priv->dev, ret, "unable to register switch\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, REALTEK_DSA); + +/** + * rtl83xx_unregister_switch() - unregister a switch + * @priv: realtek_priv pointer + * + * This function unregister a DSA switch. + * + * Context: Can sleep. + * Return: Nothing. + */ +void rtl83xx_unregister_switch(struct realtek_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + + dsa_unregister_switch(ds); +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA); + +/** + * rtl83xx_shutdown() - shutdown a switch + * @priv: realtek_priv pointer + * + * This function shuts down the DSA switch and cleans the platform driver data, + * to prevent realtek_{smi,mdio}_remove() from running afterwards, which is + * possible if the parent bus implements its own .shutdown() as .remove(). + * + * Context: Can sleep. + * Return: Nothing. + */ +void rtl83xx_shutdown(struct realtek_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + + dsa_switch_shutdown(ds); + + dev_set_drvdata(priv->dev, NULL); +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, REALTEK_DSA); + +/** + * rtl83xx_remove() - Cleanup a realtek switch driver + * @priv: realtek_priv pointer + * + * If a method is provided, this function asserts the hard reset of the switch + * in order to avoid leaking traffic when the driver is gone. + * + * Context: Might sleep if priv->gdev->chip->can_sleep. + * Return: nothing + */ +void rtl83xx_remove(struct realtek_priv *priv) +{ + /* leave the device reset asserted */ + if (priv->reset) + gpiod_set_value(priv->reset, 1); +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); + +MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>"); +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("Realtek DSA switches common module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/realtek/rtl83xx.h b/drivers/net/dsa/realtek/rtl83xx.h new file mode 100644 index 000000000000..0ddff33df6b0 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl83xx.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _RTL83XX_H +#define _RTL83XX_H + +struct realtek_interface_info { + int (*reg_read)(void *ctx, u32 reg, u32 *val); + int (*reg_write)(void *ctx, u32 reg, u32 val); +}; + +void rtl83xx_lock(void *ctx); +void rtl83xx_unlock(void *ctx); +int rtl83xx_setup_user_mdio(struct dsa_switch *ds); +struct realtek_priv * +rtl83xx_probe(struct device *dev, + const struct realtek_interface_info *interface_info); +int rtl83xx_register_switch(struct realtek_priv *priv); +void rtl83xx_unregister_switch(struct realtek_priv *priv); +void rtl83xx_shutdown(struct realtek_priv *priv); +void rtl83xx_remove(struct realtek_priv *priv); + +#endif /* _RTL83XX_H */ diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index 768454aa36d6..d29b5d7af0d7 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -67,18 +67,12 @@ static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev) static int dummy_dev_init(struct net_device *dev) { - dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); - if (!dev->lstats) - return -ENOMEM; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_LSTATS; + netdev_lockdep_set_classes(dev); return 0; } -static void dummy_dev_uninit(struct net_device *dev) -{ - free_percpu(dev->lstats); -} - static int dummy_change_carrier(struct net_device *dev, bool new_carrier) { if (new_carrier) @@ -90,7 +84,6 @@ static int dummy_change_carrier(struct net_device *dev, bool new_carrier) static const struct net_device_ops dummy_netdev_ops = { .ndo_init = dummy_dev_init, - .ndo_uninit = dummy_dev_uninit, .ndo_start_xmit = dummy_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = set_multicast_list, diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 5a274b99f299..6a19b5393ed1 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -15,9 +15,6 @@ if ETHERNET config MDIO tristate -config SUNGEM_PHY - tristate - source "drivers/net/ethernet/3com/Kconfig" source "drivers/net/ethernet/actions/Kconfig" source "drivers/net/ethernet/adaptec/Kconfig" diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index 633b321d7fdd..9e9e4a03f1a8 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -90,8 +90,7 @@ static int ena_com_admin_init_sq(struct ena_com_admin_queue *admin_queue) struct ena_com_admin_sq *sq = &admin_queue->sq; u16 size = ADMIN_SQ_SIZE(admin_queue->q_depth); - sq->entries = dma_alloc_coherent(admin_queue->q_dmadev, size, - &sq->dma_addr, GFP_KERNEL); + sq->entries = dma_alloc_coherent(admin_queue->q_dmadev, size, &sq->dma_addr, GFP_KERNEL); if (!sq->entries) { netdev_err(ena_dev->net_device, "Memory allocation failed\n"); @@ -113,8 +112,7 @@ static int ena_com_admin_init_cq(struct ena_com_admin_queue *admin_queue) struct ena_com_admin_cq *cq = &admin_queue->cq; u16 size = ADMIN_CQ_SIZE(admin_queue->q_depth); - cq->entries = dma_alloc_coherent(admin_queue->q_dmadev, size, - &cq->dma_addr, GFP_KERNEL); + cq->entries = dma_alloc_coherent(admin_queue->q_dmadev, size, &cq->dma_addr, GFP_KERNEL); if (!cq->entries) { netdev_err(ena_dev->net_device, "Memory allocation failed\n"); @@ -136,8 +134,7 @@ static int ena_com_admin_init_aenq(struct ena_com_dev *ena_dev, ena_dev->aenq.q_depth = ENA_ASYNC_QUEUE_DEPTH; size = ADMIN_AENQ_SIZE(ENA_ASYNC_QUEUE_DEPTH); - aenq->entries = dma_alloc_coherent(ena_dev->dmadev, size, - &aenq->dma_addr, GFP_KERNEL); + aenq->entries = dma_alloc_coherent(ena_dev->dmadev, size, &aenq->dma_addr, GFP_KERNEL); if (!aenq->entries) { netdev_err(ena_dev->net_device, "Memory allocation failed\n"); @@ -155,14 +152,13 @@ static int ena_com_admin_init_aenq(struct ena_com_dev *ena_dev, aenq_caps = 0; aenq_caps |= ena_dev->aenq.q_depth & ENA_REGS_AENQ_CAPS_AENQ_DEPTH_MASK; - aenq_caps |= (sizeof(struct ena_admin_aenq_entry) - << ENA_REGS_AENQ_CAPS_AENQ_ENTRY_SIZE_SHIFT) & - ENA_REGS_AENQ_CAPS_AENQ_ENTRY_SIZE_MASK; + aenq_caps |= + (sizeof(struct ena_admin_aenq_entry) << ENA_REGS_AENQ_CAPS_AENQ_ENTRY_SIZE_SHIFT) & + ENA_REGS_AENQ_CAPS_AENQ_ENTRY_SIZE_MASK; writel(aenq_caps, ena_dev->reg_bar + ENA_REGS_AENQ_CAPS_OFF); if (unlikely(!aenq_handlers)) { - netdev_err(ena_dev->net_device, - "AENQ handlers pointer is NULL\n"); + netdev_err(ena_dev->net_device, "AENQ handlers pointer is NULL\n"); return -EINVAL; } @@ -189,14 +185,12 @@ static struct ena_comp_ctx *get_comp_ctxt(struct ena_com_admin_queue *admin_queu } if (unlikely(!admin_queue->comp_ctx)) { - netdev_err(admin_queue->ena_dev->net_device, - "Completion context is NULL\n"); + netdev_err(admin_queue->ena_dev->net_device, "Completion context is NULL\n"); return NULL; } if (unlikely(admin_queue->comp_ctx[command_id].occupied && capture)) { - netdev_err(admin_queue->ena_dev->net_device, - "Completion context is occupied\n"); + netdev_err(admin_queue->ena_dev->net_device, "Completion context is occupied\n"); return NULL; } @@ -226,8 +220,7 @@ static struct ena_comp_ctx *__ena_com_submit_admin_cmd(struct ena_com_admin_queu /* In case of queue FULL */ cnt = (u16)atomic_read(&admin_queue->outstanding_cmds); if (cnt >= admin_queue->q_depth) { - netdev_dbg(admin_queue->ena_dev->net_device, - "Admin queue is full.\n"); + netdev_dbg(admin_queue->ena_dev->net_device, "Admin queue is full.\n"); admin_queue->stats.out_of_space++; return ERR_PTR(-ENOSPC); } @@ -274,8 +267,7 @@ static int ena_com_init_comp_ctxt(struct ena_com_admin_queue *admin_queue) struct ena_comp_ctx *comp_ctx; u16 i; - admin_queue->comp_ctx = - devm_kzalloc(admin_queue->q_dmadev, size, GFP_KERNEL); + admin_queue->comp_ctx = devm_kzalloc(admin_queue->q_dmadev, size, GFP_KERNEL); if (unlikely(!admin_queue->comp_ctx)) { netdev_err(ena_dev->net_device, "Memory allocation failed\n"); return -ENOMEM; @@ -336,20 +328,17 @@ static int ena_com_init_io_sq(struct ena_com_dev *ena_dev, dev_node = dev_to_node(ena_dev->dmadev); set_dev_node(ena_dev->dmadev, ctx->numa_node); io_sq->desc_addr.virt_addr = - dma_alloc_coherent(ena_dev->dmadev, size, - &io_sq->desc_addr.phys_addr, + dma_alloc_coherent(ena_dev->dmadev, size, &io_sq->desc_addr.phys_addr, GFP_KERNEL); set_dev_node(ena_dev->dmadev, dev_node); if (!io_sq->desc_addr.virt_addr) { io_sq->desc_addr.virt_addr = dma_alloc_coherent(ena_dev->dmadev, size, - &io_sq->desc_addr.phys_addr, - GFP_KERNEL); + &io_sq->desc_addr.phys_addr, GFP_KERNEL); } if (!io_sq->desc_addr.virt_addr) { - netdev_err(ena_dev->net_device, - "Memory allocation failed\n"); + netdev_err(ena_dev->net_device, "Memory allocation failed\n"); return -ENOMEM; } } @@ -367,16 +356,14 @@ static int ena_com_init_io_sq(struct ena_com_dev *ena_dev, dev_node = dev_to_node(ena_dev->dmadev); set_dev_node(ena_dev->dmadev, ctx->numa_node); - io_sq->bounce_buf_ctrl.base_buffer = - devm_kzalloc(ena_dev->dmadev, size, GFP_KERNEL); + io_sq->bounce_buf_ctrl.base_buffer = devm_kzalloc(ena_dev->dmadev, size, GFP_KERNEL); set_dev_node(ena_dev->dmadev, dev_node); if (!io_sq->bounce_buf_ctrl.base_buffer) io_sq->bounce_buf_ctrl.base_buffer = devm_kzalloc(ena_dev->dmadev, size, GFP_KERNEL); if (!io_sq->bounce_buf_ctrl.base_buffer) { - netdev_err(ena_dev->net_device, - "Bounce buffer memory allocation failed\n"); + netdev_err(ena_dev->net_device, "Bounce buffer memory allocation failed\n"); return -ENOMEM; } @@ -425,13 +412,11 @@ static int ena_com_init_io_cq(struct ena_com_dev *ena_dev, prev_node = dev_to_node(ena_dev->dmadev); set_dev_node(ena_dev->dmadev, ctx->numa_node); io_cq->cdesc_addr.virt_addr = - dma_alloc_coherent(ena_dev->dmadev, size, - &io_cq->cdesc_addr.phys_addr, GFP_KERNEL); + dma_alloc_coherent(ena_dev->dmadev, size, &io_cq->cdesc_addr.phys_addr, GFP_KERNEL); set_dev_node(ena_dev->dmadev, prev_node); if (!io_cq->cdesc_addr.virt_addr) { io_cq->cdesc_addr.virt_addr = - dma_alloc_coherent(ena_dev->dmadev, size, - &io_cq->cdesc_addr.phys_addr, + dma_alloc_coherent(ena_dev->dmadev, size, &io_cq->cdesc_addr.phys_addr, GFP_KERNEL); } @@ -514,8 +499,8 @@ static int ena_com_comp_status_to_errno(struct ena_com_admin_queue *admin_queue, u8 comp_status) { if (unlikely(comp_status != 0)) - netdev_err(admin_queue->ena_dev->net_device, - "Admin command failed[%u]\n", comp_status); + netdev_err(admin_queue->ena_dev->net_device, "Admin command failed[%u]\n", + comp_status); switch (comp_status) { case ENA_ADMIN_SUCCESS: @@ -580,8 +565,7 @@ static int ena_com_wait_and_process_admin_cq_polling(struct ena_comp_ctx *comp_c } if (unlikely(comp_ctx->status == ENA_CMD_ABORTED)) { - netdev_err(admin_queue->ena_dev->net_device, - "Command was aborted\n"); + netdev_err(admin_queue->ena_dev->net_device, "Command was aborted\n"); spin_lock_irqsave(&admin_queue->q_lock, flags); admin_queue->stats.aborted_cmd++; spin_unlock_irqrestore(&admin_queue->q_lock, flags); @@ -589,8 +573,7 @@ static int ena_com_wait_and_process_admin_cq_polling(struct ena_comp_ctx *comp_c goto err; } - WARN(comp_ctx->status != ENA_CMD_COMPLETED, "Invalid comp status %d\n", - comp_ctx->status); + WARN(comp_ctx->status != ENA_CMD_COMPLETED, "Invalid comp status %d\n", comp_ctx->status); ret = ena_com_comp_status_to_errno(admin_queue, comp_ctx->comp_status); err: @@ -634,8 +617,7 @@ static int ena_com_set_llq(struct ena_com_dev *ena_dev) sizeof(resp)); if (unlikely(ret)) - netdev_err(ena_dev->net_device, - "Failed to set LLQ configurations: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to set LLQ configurations: %d\n", ret); return ret; } @@ -658,8 +640,7 @@ static int ena_com_config_llq_info(struct ena_com_dev *ena_dev, llq_default_cfg->llq_header_location; } else { netdev_err(ena_dev->net_device, - "Invalid header location control, supported: 0x%x\n", - supported_feat); + "Invalid header location control, supported: 0x%x\n", supported_feat); return -EINVAL; } @@ -681,8 +662,8 @@ static int ena_com_config_llq_info(struct ena_com_dev *ena_dev, netdev_err(ena_dev->net_device, "Default llq stride ctrl is not supported, performing fallback, default: 0x%x, supported: 0x%x, used: 0x%x\n", - llq_default_cfg->llq_stride_ctrl, - supported_feat, llq_info->desc_stride_ctrl); + llq_default_cfg->llq_stride_ctrl, supported_feat, + llq_info->desc_stride_ctrl); } } else { llq_info->desc_stride_ctrl = 0; @@ -704,8 +685,7 @@ static int ena_com_config_llq_info(struct ena_com_dev *ena_dev, llq_info->desc_list_entry_size = 256; } else { netdev_err(ena_dev->net_device, - "Invalid entry_size_ctrl, supported: 0x%x\n", - supported_feat); + "Invalid entry_size_ctrl, supported: 0x%x\n", supported_feat); return -EINVAL; } @@ -750,8 +730,8 @@ static int ena_com_config_llq_info(struct ena_com_dev *ena_dev, netdev_err(ena_dev->net_device, "Default llq num descs before header is not supported, performing fallback, default: 0x%x, supported: 0x%x, used: 0x%x\n", - llq_default_cfg->llq_num_decs_before_header, - supported_feat, llq_info->descs_num_before_header); + llq_default_cfg->llq_num_decs_before_header, supported_feat, + llq_info->descs_num_before_header); } /* Check for accelerated queue supported */ llq_accel_mode_get = llq_features->accel_mode.u.get; @@ -767,8 +747,7 @@ static int ena_com_config_llq_info(struct ena_com_dev *ena_dev, rc = ena_com_set_llq(ena_dev); if (rc) - netdev_err(ena_dev->net_device, - "Cannot set LLQ configuration: %d\n", rc); + netdev_err(ena_dev->net_device, "Cannot set LLQ configuration: %d\n", rc); return rc; } @@ -780,8 +759,7 @@ static int ena_com_wait_and_process_admin_cq_interrupts(struct ena_comp_ctx *com int ret; wait_for_completion_timeout(&comp_ctx->wait_event, - usecs_to_jiffies( - admin_queue->completion_timeout)); + usecs_to_jiffies(admin_queue->completion_timeout)); /* In case the command wasn't completed find out the root cause. * There might be 2 kinds of errors @@ -797,8 +775,7 @@ static int ena_com_wait_and_process_admin_cq_interrupts(struct ena_comp_ctx *com if (comp_ctx->status == ENA_CMD_COMPLETED) { netdev_err(admin_queue->ena_dev->net_device, "The ena device sent a completion but the driver didn't receive a MSI-X interrupt (cmd %d), autopolling mode is %s\n", - comp_ctx->cmd_opcode, - admin_queue->auto_polling ? "ON" : "OFF"); + comp_ctx->cmd_opcode, admin_queue->auto_polling ? "ON" : "OFF"); /* Check if fallback to polling is enabled */ if (admin_queue->auto_polling) admin_queue->polling = true; @@ -867,15 +844,13 @@ static u32 ena_com_reg_bar_read32(struct ena_com_dev *ena_dev, u16 offset) if (unlikely(i == timeout)) { netdev_err(ena_dev->net_device, "Reading reg failed for timeout. expected: req id[%u] offset[%u] actual: req id[%u] offset[%u]\n", - mmio_read->seq_num, offset, read_resp->req_id, - read_resp->reg_off); + mmio_read->seq_num, offset, read_resp->req_id, read_resp->reg_off); ret = ENA_MMIO_READ_TIMEOUT; goto err; } if (read_resp->reg_off != offset) { - netdev_err(ena_dev->net_device, - "Read failure: wrong offset provided\n"); + netdev_err(ena_dev->net_device, "Read failure: wrong offset provided\n"); ret = ENA_MMIO_READ_TIMEOUT; } else { ret = read_resp->reg_val; @@ -934,8 +909,7 @@ static int ena_com_destroy_io_sq(struct ena_com_dev *ena_dev, sizeof(destroy_resp)); if (unlikely(ret && (ret != -ENODEV))) - netdev_err(ena_dev->net_device, - "Failed to destroy io sq error: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to destroy io sq error: %d\n", ret); return ret; } @@ -949,8 +923,7 @@ static void ena_com_io_queue_free(struct ena_com_dev *ena_dev, if (io_cq->cdesc_addr.virt_addr) { size = io_cq->cdesc_entry_size_in_bytes * io_cq->q_depth; - dma_free_coherent(ena_dev->dmadev, size, - io_cq->cdesc_addr.virt_addr, + dma_free_coherent(ena_dev->dmadev, size, io_cq->cdesc_addr.virt_addr, io_cq->cdesc_addr.phys_addr); io_cq->cdesc_addr.virt_addr = NULL; @@ -959,8 +932,7 @@ static void ena_com_io_queue_free(struct ena_com_dev *ena_dev, if (io_sq->desc_addr.virt_addr) { size = io_sq->desc_entry_size * io_sq->q_depth; - dma_free_coherent(ena_dev->dmadev, size, - io_sq->desc_addr.virt_addr, + dma_free_coherent(ena_dev->dmadev, size, io_sq->desc_addr.virt_addr, io_sq->desc_addr.phys_addr); io_sq->desc_addr.virt_addr = NULL; @@ -985,8 +957,7 @@ static int wait_for_reset_state(struct ena_com_dev *ena_dev, u32 timeout, val = ena_com_reg_bar_read32(ena_dev, ENA_REGS_DEV_STS_OFF); if (unlikely(val == ENA_MMIO_READ_TIMEOUT)) { - netdev_err(ena_dev->net_device, - "Reg read timeout occurred\n"); + netdev_err(ena_dev->net_device, "Reg read timeout occurred\n"); return -ETIME; } @@ -1026,8 +997,7 @@ static int ena_com_get_feature_ex(struct ena_com_dev *ena_dev, int ret; if (!ena_com_check_supported_feature_id(ena_dev, feature_id)) { - netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", - feature_id); + netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", feature_id); return -EOPNOTSUPP; } @@ -1064,8 +1034,7 @@ static int ena_com_get_feature_ex(struct ena_com_dev *ena_dev, if (unlikely(ret)) netdev_err(ena_dev->net_device, - "Failed to submit get_feature command %d error: %d\n", - feature_id, ret); + "Failed to submit get_feature command %d error: %d\n", feature_id, ret); return ret; } @@ -1104,13 +1073,11 @@ static int ena_com_hash_key_allocate(struct ena_com_dev *ena_dev) { struct ena_rss *rss = &ena_dev->rss; - if (!ena_com_check_supported_feature_id(ena_dev, - ENA_ADMIN_RSS_HASH_FUNCTION)) + if (!ena_com_check_supported_feature_id(ena_dev, ENA_ADMIN_RSS_HASH_FUNCTION)) return -EOPNOTSUPP; - rss->hash_key = - dma_alloc_coherent(ena_dev->dmadev, sizeof(*rss->hash_key), - &rss->hash_key_dma_addr, GFP_KERNEL); + rss->hash_key = dma_alloc_coherent(ena_dev->dmadev, sizeof(*rss->hash_key), + &rss->hash_key_dma_addr, GFP_KERNEL); if (unlikely(!rss->hash_key)) return -ENOMEM; @@ -1123,8 +1090,8 @@ static void ena_com_hash_key_destroy(struct ena_com_dev *ena_dev) struct ena_rss *rss = &ena_dev->rss; if (rss->hash_key) - dma_free_coherent(ena_dev->dmadev, sizeof(*rss->hash_key), - rss->hash_key, rss->hash_key_dma_addr); + dma_free_coherent(ena_dev->dmadev, sizeof(*rss->hash_key), rss->hash_key, + rss->hash_key_dma_addr); rss->hash_key = NULL; } @@ -1132,9 +1099,8 @@ static int ena_com_hash_ctrl_init(struct ena_com_dev *ena_dev) { struct ena_rss *rss = &ena_dev->rss; - rss->hash_ctrl = - dma_alloc_coherent(ena_dev->dmadev, sizeof(*rss->hash_ctrl), - &rss->hash_ctrl_dma_addr, GFP_KERNEL); + rss->hash_ctrl = dma_alloc_coherent(ena_dev->dmadev, sizeof(*rss->hash_ctrl), + &rss->hash_ctrl_dma_addr, GFP_KERNEL); if (unlikely(!rss->hash_ctrl)) return -ENOMEM; @@ -1147,8 +1113,8 @@ static void ena_com_hash_ctrl_destroy(struct ena_com_dev *ena_dev) struct ena_rss *rss = &ena_dev->rss; if (rss->hash_ctrl) - dma_free_coherent(ena_dev->dmadev, sizeof(*rss->hash_ctrl), - rss->hash_ctrl, rss->hash_ctrl_dma_addr); + dma_free_coherent(ena_dev->dmadev, sizeof(*rss->hash_ctrl), rss->hash_ctrl, + rss->hash_ctrl_dma_addr); rss->hash_ctrl = NULL; } @@ -1177,15 +1143,13 @@ static int ena_com_indirect_table_allocate(struct ena_com_dev *ena_dev, tbl_size = (1ULL << log_size) * sizeof(struct ena_admin_rss_ind_table_entry); - rss->rss_ind_tbl = - dma_alloc_coherent(ena_dev->dmadev, tbl_size, - &rss->rss_ind_tbl_dma_addr, GFP_KERNEL); + rss->rss_ind_tbl = dma_alloc_coherent(ena_dev->dmadev, tbl_size, &rss->rss_ind_tbl_dma_addr, + GFP_KERNEL); if (unlikely(!rss->rss_ind_tbl)) goto mem_err1; tbl_size = (1ULL << log_size) * sizeof(u16); - rss->host_rss_ind_tbl = - devm_kzalloc(ena_dev->dmadev, tbl_size, GFP_KERNEL); + rss->host_rss_ind_tbl = devm_kzalloc(ena_dev->dmadev, tbl_size, GFP_KERNEL); if (unlikely(!rss->host_rss_ind_tbl)) goto mem_err2; @@ -1197,8 +1161,7 @@ mem_err2: tbl_size = (1ULL << log_size) * sizeof(struct ena_admin_rss_ind_table_entry); - dma_free_coherent(ena_dev->dmadev, tbl_size, rss->rss_ind_tbl, - rss->rss_ind_tbl_dma_addr); + dma_free_coherent(ena_dev->dmadev, tbl_size, rss->rss_ind_tbl, rss->rss_ind_tbl_dma_addr); rss->rss_ind_tbl = NULL; mem_err1: rss->tbl_log_size = 0; @@ -1261,8 +1224,7 @@ static int ena_com_create_io_sq(struct ena_com_dev *ena_dev, &create_cmd.sq_ba, io_sq->desc_addr.phys_addr); if (unlikely(ret)) { - netdev_err(ena_dev->net_device, - "Memory address set failed\n"); + netdev_err(ena_dev->net_device, "Memory address set failed\n"); return ret; } } @@ -1273,8 +1235,7 @@ static int ena_com_create_io_sq(struct ena_com_dev *ena_dev, (struct ena_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (unlikely(ret)) { - netdev_err(ena_dev->net_device, - "Failed to create IO SQ. error: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to create IO SQ. error: %d\n", ret); return ret; } @@ -1284,16 +1245,12 @@ static int ena_com_create_io_sq(struct ena_com_dev *ena_dev, (uintptr_t)cmd_completion.sq_doorbell_offset); if (io_sq->mem_queue_type == ENA_ADMIN_PLACEMENT_POLICY_DEV) { - io_sq->header_addr = (u8 __iomem *)((uintptr_t)ena_dev->mem_bar - + cmd_completion.llq_headers_offset); - io_sq->desc_addr.pbuf_dev_addr = (u8 __iomem *)((uintptr_t)ena_dev->mem_bar + cmd_completion.llq_descriptors_offset); } - netdev_dbg(ena_dev->net_device, "Created sq[%u], depth[%u]\n", - io_sq->idx, io_sq->q_depth); + netdev_dbg(ena_dev->net_device, "Created sq[%u], depth[%u]\n", io_sq->idx, io_sq->q_depth); return ret; } @@ -1420,8 +1377,7 @@ int ena_com_create_io_cq(struct ena_com_dev *ena_dev, (struct ena_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (unlikely(ret)) { - netdev_err(ena_dev->net_device, - "Failed to create IO CQ. error: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to create IO CQ. error: %d\n", ret); return ret; } @@ -1430,18 +1386,12 @@ int ena_com_create_io_cq(struct ena_com_dev *ena_dev, io_cq->unmask_reg = (u32 __iomem *)((uintptr_t)ena_dev->reg_bar + cmd_completion.cq_interrupt_unmask_register_offset); - if (cmd_completion.cq_head_db_register_offset) - io_cq->cq_head_db_reg = - (u32 __iomem *)((uintptr_t)ena_dev->reg_bar + - cmd_completion.cq_head_db_register_offset); - if (cmd_completion.numa_node_register_offset) io_cq->numa_node_cfg_reg = (u32 __iomem *)((uintptr_t)ena_dev->reg_bar + cmd_completion.numa_node_register_offset); - netdev_dbg(ena_dev->net_device, "Created cq[%u], depth[%u]\n", - io_cq->idx, io_cq->q_depth); + netdev_dbg(ena_dev->net_device, "Created cq[%u], depth[%u]\n", io_cq->idx, io_cq->q_depth); return ret; } @@ -1451,8 +1401,7 @@ int ena_com_get_io_handlers(struct ena_com_dev *ena_dev, u16 qid, struct ena_com_io_cq **io_cq) { if (qid >= ENA_TOTAL_NUM_QUEUES) { - netdev_err(ena_dev->net_device, - "Invalid queue number %d but the max is %d\n", qid, + netdev_err(ena_dev->net_device, "Invalid queue number %d but the max is %d\n", qid, ENA_TOTAL_NUM_QUEUES); return -EINVAL; } @@ -1492,8 +1441,7 @@ void ena_com_wait_for_abort_completion(struct ena_com_dev *ena_dev) spin_lock_irqsave(&admin_queue->q_lock, flags); while (atomic_read(&admin_queue->outstanding_cmds) != 0) { spin_unlock_irqrestore(&admin_queue->q_lock, flags); - ena_delay_exponential_backoff_us(exp++, - ena_dev->ena_min_poll_delay_us); + ena_delay_exponential_backoff_us(exp++, ena_dev->ena_min_poll_delay_us); spin_lock_irqsave(&admin_queue->q_lock, flags); } spin_unlock_irqrestore(&admin_queue->q_lock, flags); @@ -1519,8 +1467,7 @@ int ena_com_destroy_io_cq(struct ena_com_dev *ena_dev, sizeof(destroy_resp)); if (unlikely(ret && (ret != -ENODEV))) - netdev_err(ena_dev->net_device, - "Failed to destroy IO CQ. error: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to destroy IO CQ. error: %d\n", ret); return ret; } @@ -1588,8 +1535,7 @@ int ena_com_set_aenq_config(struct ena_com_dev *ena_dev, u32 groups_flag) sizeof(resp)); if (unlikely(ret)) - netdev_err(ena_dev->net_device, - "Failed to config AENQ ret: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to config AENQ ret: %d\n", ret); return ret; } @@ -1610,8 +1556,7 @@ int ena_com_get_dma_width(struct ena_com_dev *ena_dev) netdev_dbg(ena_dev->net_device, "ENA dma width: %d\n", width); if ((width < 32) || width > ENA_MAX_PHYS_ADDR_SIZE_BITS) { - netdev_err(ena_dev->net_device, "DMA width illegal value: %d\n", - width); + netdev_err(ena_dev->net_device, "DMA width illegal value: %d\n", width); return -EINVAL; } @@ -1633,19 +1578,16 @@ int ena_com_validate_version(struct ena_com_dev *ena_dev) ctrl_ver = ena_com_reg_bar_read32(ena_dev, ENA_REGS_CONTROLLER_VERSION_OFF); - if (unlikely((ver == ENA_MMIO_READ_TIMEOUT) || - (ctrl_ver == ENA_MMIO_READ_TIMEOUT))) { + if (unlikely((ver == ENA_MMIO_READ_TIMEOUT) || (ctrl_ver == ENA_MMIO_READ_TIMEOUT))) { netdev_err(ena_dev->net_device, "Reg read timeout occurred\n"); return -ETIME; } dev_info(ena_dev->dmadev, "ENA device version: %d.%d\n", - (ver & ENA_REGS_VERSION_MAJOR_VERSION_MASK) >> - ENA_REGS_VERSION_MAJOR_VERSION_SHIFT, + (ver & ENA_REGS_VERSION_MAJOR_VERSION_MASK) >> ENA_REGS_VERSION_MAJOR_VERSION_SHIFT, ver & ENA_REGS_VERSION_MINOR_VERSION_MASK); - dev_info(ena_dev->dmadev, - "ENA controller version: %d.%d.%d implementation version %d\n", + dev_info(ena_dev->dmadev, "ENA controller version: %d.%d.%d implementation version %d\n", (ctrl_ver & ENA_REGS_CONTROLLER_VERSION_MAJOR_VERSION_MASK) >> ENA_REGS_CONTROLLER_VERSION_MAJOR_VERSION_SHIFT, (ctrl_ver & ENA_REGS_CONTROLLER_VERSION_MINOR_VERSION_MASK) >> @@ -1694,20 +1636,17 @@ void ena_com_admin_destroy(struct ena_com_dev *ena_dev) size = ADMIN_SQ_SIZE(admin_queue->q_depth); if (sq->entries) - dma_free_coherent(ena_dev->dmadev, size, sq->entries, - sq->dma_addr); + dma_free_coherent(ena_dev->dmadev, size, sq->entries, sq->dma_addr); sq->entries = NULL; size = ADMIN_CQ_SIZE(admin_queue->q_depth); if (cq->entries) - dma_free_coherent(ena_dev->dmadev, size, cq->entries, - cq->dma_addr); + dma_free_coherent(ena_dev->dmadev, size, cq->entries, cq->dma_addr); cq->entries = NULL; size = ADMIN_AENQ_SIZE(aenq->q_depth); if (ena_dev->aenq.entries) - dma_free_coherent(ena_dev->dmadev, size, aenq->entries, - aenq->dma_addr); + dma_free_coherent(ena_dev->dmadev, size, aenq->entries, aenq->dma_addr); aenq->entries = NULL; } @@ -1733,10 +1672,8 @@ int ena_com_mmio_reg_read_request_init(struct ena_com_dev *ena_dev) struct ena_com_mmio_read *mmio_read = &ena_dev->mmio_read; spin_lock_init(&mmio_read->lock); - mmio_read->read_resp = - dma_alloc_coherent(ena_dev->dmadev, - sizeof(*mmio_read->read_resp), - &mmio_read->read_resp_dma_addr, GFP_KERNEL); + mmio_read->read_resp = dma_alloc_coherent(ena_dev->dmadev, sizeof(*mmio_read->read_resp), + &mmio_read->read_resp_dma_addr, GFP_KERNEL); if (unlikely(!mmio_read->read_resp)) goto err; @@ -1767,8 +1704,8 @@ void ena_com_mmio_reg_read_request_destroy(struct ena_com_dev *ena_dev) writel(0x0, ena_dev->reg_bar + ENA_REGS_MMIO_RESP_LO_OFF); writel(0x0, ena_dev->reg_bar + ENA_REGS_MMIO_RESP_HI_OFF); - dma_free_coherent(ena_dev->dmadev, sizeof(*mmio_read->read_resp), - mmio_read->read_resp, mmio_read->read_resp_dma_addr); + dma_free_coherent(ena_dev->dmadev, sizeof(*mmio_read->read_resp), mmio_read->read_resp, + mmio_read->read_resp_dma_addr); mmio_read->read_resp = NULL; } @@ -1800,8 +1737,7 @@ int ena_com_admin_init(struct ena_com_dev *ena_dev, } if (!(dev_sts & ENA_REGS_DEV_STS_READY_MASK)) { - netdev_err(ena_dev->net_device, - "Device isn't ready, abort com init\n"); + netdev_err(ena_dev->net_device, "Device isn't ready, abort com init\n"); return -ENODEV; } @@ -1878,8 +1814,7 @@ int ena_com_create_io_queue(struct ena_com_dev *ena_dev, int ret; if (ctx->qid >= ENA_TOTAL_NUM_QUEUES) { - netdev_err(ena_dev->net_device, - "Qid (%d) is bigger than max num of queues (%d)\n", + netdev_err(ena_dev->net_device, "Qid (%d) is bigger than max num of queues (%d)\n", ctx->qid, ENA_TOTAL_NUM_QUEUES); return -EINVAL; } @@ -1905,8 +1840,7 @@ int ena_com_create_io_queue(struct ena_com_dev *ena_dev, if (ctx->direction == ENA_COM_IO_QUEUE_DIRECTION_TX) /* header length is limited to 8 bits */ - io_sq->tx_max_header_size = - min_t(u32, ena_dev->tx_max_header_size, SZ_256); + io_sq->tx_max_header_size = min_t(u32, ena_dev->tx_max_header_size, SZ_256); ret = ena_com_init_io_sq(ena_dev, ctx, io_sq); if (ret) @@ -1938,8 +1872,7 @@ void ena_com_destroy_io_queue(struct ena_com_dev *ena_dev, u16 qid) struct ena_com_io_cq *io_cq; if (qid >= ENA_TOTAL_NUM_QUEUES) { - netdev_err(ena_dev->net_device, - "Qid (%d) is bigger than max num of queues (%d)\n", + netdev_err(ena_dev->net_device, "Qid (%d) is bigger than max num of queues (%d)\n", qid, ENA_TOTAL_NUM_QUEUES); return; } @@ -1983,8 +1916,7 @@ int ena_com_get_dev_attr_feat(struct ena_com_dev *ena_dev, if (rc) return rc; - if (get_resp.u.max_queue_ext.version != - ENA_FEATURE_MAX_QUEUE_EXT_VER) + if (get_resp.u.max_queue_ext.version != ENA_FEATURE_MAX_QUEUE_EXT_VER) return -EINVAL; memcpy(&get_feat_ctx->max_queue_ext, &get_resp.u.max_queue_ext, @@ -2025,18 +1957,15 @@ int ena_com_get_dev_attr_feat(struct ena_com_dev *ena_dev, rc = ena_com_get_feature(ena_dev, &get_resp, ENA_ADMIN_HW_HINTS, 0); if (!rc) - memcpy(&get_feat_ctx->hw_hints, &get_resp.u.hw_hints, - sizeof(get_resp.u.hw_hints)); + memcpy(&get_feat_ctx->hw_hints, &get_resp.u.hw_hints, sizeof(get_resp.u.hw_hints)); else if (rc == -EOPNOTSUPP) - memset(&get_feat_ctx->hw_hints, 0x0, - sizeof(get_feat_ctx->hw_hints)); + memset(&get_feat_ctx->hw_hints, 0x0, sizeof(get_feat_ctx->hw_hints)); else return rc; rc = ena_com_get_feature(ena_dev, &get_resp, ENA_ADMIN_LLQ, 0); if (!rc) - memcpy(&get_feat_ctx->llq, &get_resp.u.llq, - sizeof(get_resp.u.llq)); + memcpy(&get_feat_ctx->llq, &get_resp.u.llq, sizeof(get_resp.u.llq)); else if (rc == -EOPNOTSUPP) memset(&get_feat_ctx->llq, 0x0, sizeof(get_feat_ctx->llq)); else @@ -2084,8 +2013,7 @@ void ena_com_aenq_intr_handler(struct ena_com_dev *ena_dev, void *data) aenq_common = &aenq_e->aenq_common_desc; /* Go over all the events */ - while ((READ_ONCE(aenq_common->flags) & - ENA_ADMIN_AENQ_COMMON_DESC_PHASE_MASK) == phase) { + while ((READ_ONCE(aenq_common->flags) & ENA_ADMIN_AENQ_COMMON_DESC_PHASE_MASK) == phase) { /* Make sure the phase bit (ownership) is as expected before * reading the rest of the descriptor. */ @@ -2094,8 +2022,7 @@ void ena_com_aenq_intr_handler(struct ena_com_dev *ena_dev, void *data) timestamp = (u64)aenq_common->timestamp_low | ((u64)aenq_common->timestamp_high << 32); - netdev_dbg(ena_dev->net_device, - "AENQ! Group[%x] Syndrome[%x] timestamp: [%llus]\n", + netdev_dbg(ena_dev->net_device, "AENQ! Group[%x] Syndrome[%x] timestamp: [%llus]\n", aenq_common->group, aenq_common->syndrome, timestamp); /* Handle specific event*/ @@ -2124,8 +2051,7 @@ void ena_com_aenq_intr_handler(struct ena_com_dev *ena_dev, void *data) /* write the aenq doorbell after all AENQ descriptors were read */ mb(); - writel_relaxed((u32)aenq->head, - ena_dev->reg_bar + ENA_REGS_AENQ_HEAD_DB_OFF); + writel_relaxed((u32)aenq->head, ena_dev->reg_bar + ENA_REGS_AENQ_HEAD_DB_OFF); } int ena_com_dev_reset(struct ena_com_dev *ena_dev, @@ -2137,15 +2063,13 @@ int ena_com_dev_reset(struct ena_com_dev *ena_dev, stat = ena_com_reg_bar_read32(ena_dev, ENA_REGS_DEV_STS_OFF); cap = ena_com_reg_bar_read32(ena_dev, ENA_REGS_CAPS_OFF); - if (unlikely((stat == ENA_MMIO_READ_TIMEOUT) || - (cap == ENA_MMIO_READ_TIMEOUT))) { + if (unlikely((stat == ENA_MMIO_READ_TIMEOUT) || (cap == ENA_MMIO_READ_TIMEOUT))) { netdev_err(ena_dev->net_device, "Reg read32 timeout occurred\n"); return -ETIME; } if ((stat & ENA_REGS_DEV_STS_READY_MASK) == 0) { - netdev_err(ena_dev->net_device, - "Device isn't ready, can't reset device\n"); + netdev_err(ena_dev->net_device, "Device isn't ready, can't reset device\n"); return -EINVAL; } @@ -2168,8 +2092,7 @@ int ena_com_dev_reset(struct ena_com_dev *ena_dev, rc = wait_for_reset_state(ena_dev, timeout, ENA_REGS_DEV_STS_RESET_IN_PROGRESS_MASK); if (rc != 0) { - netdev_err(ena_dev->net_device, - "Reset indication didn't turn on\n"); + netdev_err(ena_dev->net_device, "Reset indication didn't turn on\n"); return rc; } @@ -2177,8 +2100,7 @@ int ena_com_dev_reset(struct ena_com_dev *ena_dev, writel(0, ena_dev->reg_bar + ENA_REGS_DEV_CTL_OFF); rc = wait_for_reset_state(ena_dev, timeout, 0); if (rc != 0) { - netdev_err(ena_dev->net_device, - "Reset indication didn't turn off\n"); + netdev_err(ena_dev->net_device, "Reset indication didn't turn off\n"); return rc; } @@ -2215,8 +2137,7 @@ static int ena_get_dev_stats(struct ena_com_dev *ena_dev, sizeof(*get_resp)); if (unlikely(ret)) - netdev_err(ena_dev->net_device, - "Failed to get stats. error: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to get stats. error: %d\n", ret); return ret; } @@ -2228,8 +2149,7 @@ int ena_com_get_eni_stats(struct ena_com_dev *ena_dev, int ret; if (!ena_com_get_cap(ena_dev, ENA_ADMIN_ENI_STATS)) { - netdev_err(ena_dev->net_device, - "Capability %d isn't supported\n", + netdev_err(ena_dev->net_device, "Capability %d isn't supported\n", ENA_ADMIN_ENI_STATS); return -EOPNOTSUPP; } @@ -2266,8 +2186,7 @@ int ena_com_set_dev_mtu(struct ena_com_dev *ena_dev, u32 mtu) int ret; if (!ena_com_check_supported_feature_id(ena_dev, ENA_ADMIN_MTU)) { - netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", - ENA_ADMIN_MTU); + netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", ENA_ADMIN_MTU); return -EOPNOTSUPP; } @@ -2286,8 +2205,7 @@ int ena_com_set_dev_mtu(struct ena_com_dev *ena_dev, u32 mtu) sizeof(resp)); if (unlikely(ret)) - netdev_err(ena_dev->net_device, - "Failed to set mtu %d. error: %d\n", mtu, ret); + netdev_err(ena_dev->net_device, "Failed to set mtu %d. error: %d\n", mtu, ret); return ret; } @@ -2301,8 +2219,7 @@ int ena_com_get_offload_settings(struct ena_com_dev *ena_dev, ret = ena_com_get_feature(ena_dev, &resp, ENA_ADMIN_STATELESS_OFFLOAD_CONFIG, 0); if (unlikely(ret)) { - netdev_err(ena_dev->net_device, - "Failed to get offload capabilities %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to get offload capabilities %d\n", ret); return ret; } @@ -2320,8 +2237,7 @@ int ena_com_set_hash_function(struct ena_com_dev *ena_dev) struct ena_admin_get_feat_resp get_resp; int ret; - if (!ena_com_check_supported_feature_id(ena_dev, - ENA_ADMIN_RSS_HASH_FUNCTION)) { + if (!ena_com_check_supported_feature_id(ena_dev, ENA_ADMIN_RSS_HASH_FUNCTION)) { netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", ENA_ADMIN_RSS_HASH_FUNCTION); return -EOPNOTSUPP; @@ -2334,8 +2250,7 @@ int ena_com_set_hash_function(struct ena_com_dev *ena_dev) return ret; if (!(get_resp.u.flow_hash_func.supported_func & BIT(rss->hash_func))) { - netdev_err(ena_dev->net_device, - "Func hash %d isn't supported by device, abort\n", + netdev_err(ena_dev->net_device, "Func hash %d isn't supported by device, abort\n", rss->hash_func); return -EOPNOTSUPP; } @@ -2365,8 +2280,7 @@ int ena_com_set_hash_function(struct ena_com_dev *ena_dev) (struct ena_admin_acq_entry *)&resp, sizeof(resp)); if (unlikely(ret)) { - netdev_err(ena_dev->net_device, - "Failed to set hash function %d. error: %d\n", + netdev_err(ena_dev->net_device, "Failed to set hash function %d. error: %d\n", rss->hash_func, ret); return -EINVAL; } @@ -2398,16 +2312,15 @@ int ena_com_fill_hash_function(struct ena_com_dev *ena_dev, return rc; if (!(BIT(func) & get_resp.u.flow_hash_func.supported_func)) { - netdev_err(ena_dev->net_device, - "Flow hash function %d isn't supported\n", func); + netdev_err(ena_dev->net_device, "Flow hash function %d isn't supported\n", func); return -EOPNOTSUPP; } if ((func == ENA_ADMIN_TOEPLITZ) && key) { if (key_len != sizeof(hash_key->key)) { netdev_err(ena_dev->net_device, - "key len (%u) doesn't equal the supported size (%zu)\n", - key_len, sizeof(hash_key->key)); + "key len (%u) doesn't equal the supported size (%zu)\n", key_len, + sizeof(hash_key->key)); return -EINVAL; } memcpy(hash_key->key, key, key_len); @@ -2495,8 +2408,7 @@ int ena_com_set_hash_ctrl(struct ena_com_dev *ena_dev) struct ena_admin_set_feat_resp resp; int ret; - if (!ena_com_check_supported_feature_id(ena_dev, - ENA_ADMIN_RSS_HASH_INPUT)) { + if (!ena_com_check_supported_feature_id(ena_dev, ENA_ADMIN_RSS_HASH_INPUT)) { netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", ENA_ADMIN_RSS_HASH_INPUT); return -EOPNOTSUPP; @@ -2527,8 +2439,7 @@ int ena_com_set_hash_ctrl(struct ena_com_dev *ena_dev) (struct ena_admin_acq_entry *)&resp, sizeof(resp)); if (unlikely(ret)) - netdev_err(ena_dev->net_device, - "Failed to set hash input. error: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to set hash input. error: %d\n", ret); return ret; } @@ -2605,8 +2516,7 @@ int ena_com_fill_hash_ctrl(struct ena_com_dev *ena_dev, int rc; if (proto >= ENA_ADMIN_RSS_PROTO_NUM) { - netdev_err(ena_dev->net_device, "Invalid proto num (%u)\n", - proto); + netdev_err(ena_dev->net_device, "Invalid proto num (%u)\n", proto); return -EINVAL; } @@ -2658,8 +2568,7 @@ int ena_com_indirect_table_set(struct ena_com_dev *ena_dev) struct ena_admin_set_feat_resp resp; int ret; - if (!ena_com_check_supported_feature_id( - ena_dev, ENA_ADMIN_RSS_INDIRECTION_TABLE_CONFIG)) { + if (!ena_com_check_supported_feature_id(ena_dev, ENA_ADMIN_RSS_INDIRECTION_TABLE_CONFIG)) { netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", ENA_ADMIN_RSS_INDIRECTION_TABLE_CONFIG); return -EOPNOTSUPP; @@ -2699,8 +2608,7 @@ int ena_com_indirect_table_set(struct ena_com_dev *ena_dev) sizeof(resp)); if (unlikely(ret)) - netdev_err(ena_dev->net_device, - "Failed to set indirect table. error: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to set indirect table. error: %d\n", ret); return ret; } @@ -2779,9 +2687,8 @@ int ena_com_allocate_host_info(struct ena_com_dev *ena_dev) { struct ena_host_attribute *host_attr = &ena_dev->host_attr; - host_attr->host_info = - dma_alloc_coherent(ena_dev->dmadev, SZ_4K, - &host_attr->host_info_dma_addr, GFP_KERNEL); + host_attr->host_info = dma_alloc_coherent(ena_dev->dmadev, SZ_4K, + &host_attr->host_info_dma_addr, GFP_KERNEL); if (unlikely(!host_attr->host_info)) return -ENOMEM; @@ -2827,8 +2734,7 @@ void ena_com_delete_debug_area(struct ena_com_dev *ena_dev) if (host_attr->debug_area_virt_addr) { dma_free_coherent(ena_dev->dmadev, host_attr->debug_area_size, - host_attr->debug_area_virt_addr, - host_attr->debug_area_dma_addr); + host_attr->debug_area_virt_addr, host_attr->debug_area_dma_addr); host_attr->debug_area_virt_addr = NULL; } } @@ -2877,8 +2783,7 @@ int ena_com_set_host_attributes(struct ena_com_dev *ena_dev) sizeof(resp)); if (unlikely(ret)) - netdev_err(ena_dev->net_device, - "Failed to set host attributes: %d\n", ret); + netdev_err(ena_dev->net_device, "Failed to set host attributes: %d\n", ret); return ret; } @@ -2896,8 +2801,7 @@ static int ena_com_update_nonadaptive_moderation_interval(struct ena_com_dev *en u32 *intr_moder_interval) { if (!intr_delay_resolution) { - netdev_err(ena_dev->net_device, - "Illegal interrupt delay granularity value\n"); + netdev_err(ena_dev->net_device, "Illegal interrupt delay granularity value\n"); return -EFAULT; } @@ -2935,14 +2839,12 @@ int ena_com_init_interrupt_moderation(struct ena_com_dev *ena_dev) if (rc) { if (rc == -EOPNOTSUPP) { - netdev_dbg(ena_dev->net_device, - "Feature %d isn't supported\n", + netdev_dbg(ena_dev->net_device, "Feature %d isn't supported\n", ENA_ADMIN_INTERRUPT_MODERATION); rc = 0; } else { netdev_err(ena_dev->net_device, - "Failed to get interrupt moderation admin cmd. rc: %d\n", - rc); + "Failed to get interrupt moderation admin cmd. rc: %d\n", rc); } /* no moderation supported, disable adaptive support */ @@ -2990,8 +2892,7 @@ int ena_com_config_dev_mode(struct ena_com_dev *ena_dev, (llq_info->descs_num_before_header * sizeof(struct ena_eth_io_tx_desc)); if (unlikely(ena_dev->tx_max_header_size == 0)) { - netdev_err(ena_dev->net_device, - "The size of the LLQ entry is smaller than needed\n"); + netdev_err(ena_dev->net_device, "The size of the LLQ entry is smaller than needed\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/amazon/ena/ena_com.h b/drivers/net/ethernet/amazon/ena/ena_com.h index 3c5081d9d25d..fea57eb8e58b 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.h +++ b/drivers/net/ethernet/amazon/ena/ena_com.h @@ -109,16 +109,13 @@ struct ena_com_io_cq { /* Interrupt unmask register */ u32 __iomem *unmask_reg; - /* The completion queue head doorbell register */ - u32 __iomem *cq_head_db_reg; - /* numa configuration register (for TPH) */ u32 __iomem *numa_node_cfg_reg; /* The value to write to the above register to unmask * the interrupt of this queue */ - u32 msix_vector; + u32 msix_vector ____cacheline_aligned; enum queue_direction direction; @@ -134,7 +131,6 @@ struct ena_com_io_cq { /* Device queue index */ u16 idx; u16 head; - u16 last_head_update; u8 phase; u8 cdesc_entry_size_in_bytes; @@ -158,7 +154,6 @@ struct ena_com_io_sq { struct ena_com_io_desc_addr desc_addr; u32 __iomem *db_addr; - u8 __iomem *header_addr; enum queue_direction direction; enum ena_admin_placement_policy_type mem_queue_type; diff --git a/drivers/net/ethernet/amazon/ena/ena_eth_com.c b/drivers/net/ethernet/amazon/ena/ena_eth_com.c index f9f886289b97..933e619b3a31 100644 --- a/drivers/net/ethernet/amazon/ena/ena_eth_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_eth_com.c @@ -18,8 +18,7 @@ static struct ena_eth_io_rx_cdesc_base *ena_com_get_next_rx_cdesc( cdesc = (struct ena_eth_io_rx_cdesc_base *)(io_cq->cdesc_addr.virt_addr + (head_masked * io_cq->cdesc_entry_size_in_bytes)); - desc_phase = (READ_ONCE(cdesc->status) & - ENA_ETH_IO_RX_CDESC_BASE_PHASE_MASK) >> + desc_phase = (READ_ONCE(cdesc->status) & ENA_ETH_IO_RX_CDESC_BASE_PHASE_MASK) >> ENA_ETH_IO_RX_CDESC_BASE_PHASE_SHIFT; if (desc_phase != expected_phase) @@ -65,8 +64,8 @@ static int ena_com_write_bounce_buffer_to_dev(struct ena_com_io_sq *io_sq, io_sq->entries_in_tx_burst_left--; netdev_dbg(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "Decreasing entries_in_tx_burst_left of queue %d to %d\n", - io_sq->qid, io_sq->entries_in_tx_burst_left); + "Decreasing entries_in_tx_burst_left of queue %d to %d\n", io_sq->qid, + io_sq->entries_in_tx_burst_left); } /* Make sure everything was written into the bounce buffer before @@ -75,8 +74,8 @@ static int ena_com_write_bounce_buffer_to_dev(struct ena_com_io_sq *io_sq, wmb(); /* The line is completed. Copy it to dev */ - __iowrite64_copy(io_sq->desc_addr.pbuf_dev_addr + dst_offset, - bounce_buffer, (llq_info->desc_list_entry_size) / 8); + __iowrite64_copy(io_sq->desc_addr.pbuf_dev_addr + dst_offset, bounce_buffer, + (llq_info->desc_list_entry_size) / 8); io_sq->tail++; @@ -102,16 +101,14 @@ static int ena_com_write_header_to_bounce(struct ena_com_io_sq *io_sq, header_offset = llq_info->descs_num_before_header * io_sq->desc_entry_size; - if (unlikely((header_offset + header_len) > - llq_info->desc_list_entry_size)) { + if (unlikely((header_offset + header_len) > llq_info->desc_list_entry_size)) { netdev_err(ena_com_io_sq_to_ena_dev(io_sq)->net_device, "Trying to write header larger than llq entry can accommodate\n"); return -EFAULT; } if (unlikely(!bounce_buffer)) { - netdev_err(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "Bounce buffer is NULL\n"); + netdev_err(ena_com_io_sq_to_ena_dev(io_sq)->net_device, "Bounce buffer is NULL\n"); return -EFAULT; } @@ -129,8 +126,7 @@ static void *get_sq_desc_llq(struct ena_com_io_sq *io_sq) bounce_buffer = pkt_ctrl->curr_bounce_buf; if (unlikely(!bounce_buffer)) { - netdev_err(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "Bounce buffer is NULL\n"); + netdev_err(ena_com_io_sq_to_ena_dev(io_sq)->net_device, "Bounce buffer is NULL\n"); return NULL; } @@ -247,8 +243,7 @@ static u16 ena_com_cdesc_rx_pkt_get(struct ena_com_io_cq *io_cq, ena_com_cq_inc_head(io_cq); count++; - last = (READ_ONCE(cdesc->status) & - ENA_ETH_IO_RX_CDESC_BASE_LAST_MASK) >> + last = (READ_ONCE(cdesc->status) & ENA_ETH_IO_RX_CDESC_BASE_LAST_MASK) >> ENA_ETH_IO_RX_CDESC_BASE_LAST_SHIFT; } while (!last); @@ -369,9 +364,8 @@ static void ena_com_rx_set_flags(struct ena_com_io_cq *io_cq, netdev_dbg(ena_com_io_cq_to_ena_dev(io_cq)->net_device, "l3_proto %d l4_proto %d l3_csum_err %d l4_csum_err %d hash %d frag %d cdesc_status %x\n", - ena_rx_ctx->l3_proto, ena_rx_ctx->l4_proto, - ena_rx_ctx->l3_csum_err, ena_rx_ctx->l4_csum_err, - ena_rx_ctx->hash, ena_rx_ctx->frag, cdesc->status); + ena_rx_ctx->l3_proto, ena_rx_ctx->l4_proto, ena_rx_ctx->l3_csum_err, + ena_rx_ctx->l4_csum_err, ena_rx_ctx->hash, ena_rx_ctx->frag, cdesc->status); } /*****************************************************************************/ @@ -403,13 +397,12 @@ int ena_com_prepare_tx(struct ena_com_io_sq *io_sq, if (unlikely(header_len > io_sq->tx_max_header_size)) { netdev_err(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "Header size is too large %d max header: %d\n", - header_len, io_sq->tx_max_header_size); + "Header size is too large %d max header: %d\n", header_len, + io_sq->tx_max_header_size); return -EINVAL; } - if (unlikely(io_sq->mem_queue_type == ENA_ADMIN_PLACEMENT_POLICY_DEV && - !buffer_to_push)) { + if (unlikely(io_sq->mem_queue_type == ENA_ADMIN_PLACEMENT_POLICY_DEV && !buffer_to_push)) { netdev_err(ena_com_io_sq_to_ena_dev(io_sq)->net_device, "Push header wasn't provided in LLQ mode\n"); return -EINVAL; @@ -556,13 +549,11 @@ int ena_com_rx_pkt(struct ena_com_io_cq *io_cq, } netdev_dbg(ena_com_io_cq_to_ena_dev(io_cq)->net_device, - "Fetch rx packet: queue %d completed desc: %d\n", io_cq->qid, - nb_hw_desc); + "Fetch rx packet: queue %d completed desc: %d\n", io_cq->qid, nb_hw_desc); if (unlikely(nb_hw_desc > ena_rx_ctx->max_bufs)) { netdev_err(ena_com_io_cq_to_ena_dev(io_cq)->net_device, - "Too many RX cdescs (%d) > MAX(%d)\n", nb_hw_desc, - ena_rx_ctx->max_bufs); + "Too many RX cdescs (%d) > MAX(%d)\n", nb_hw_desc, ena_rx_ctx->max_bufs); return -ENOSPC; } @@ -586,8 +577,8 @@ int ena_com_rx_pkt(struct ena_com_io_cq *io_cq, io_sq->next_to_comp += nb_hw_desc; netdev_dbg(ena_com_io_cq_to_ena_dev(io_cq)->net_device, - "[%s][QID#%d] Updating SQ head to: %d\n", __func__, - io_sq->qid, io_sq->next_to_comp); + "[%s][QID#%d] Updating SQ head to: %d\n", __func__, io_sq->qid, + io_sq->next_to_comp); /* Get rx flags from the last pkt */ ena_com_rx_set_flags(io_cq, ena_rx_ctx, cdesc); @@ -624,8 +615,8 @@ int ena_com_add_single_rx_desc(struct ena_com_io_sq *io_sq, desc->req_id = req_id; netdev_dbg(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "[%s] Adding single RX desc, Queue: %u, req_id: %u\n", - __func__, io_sq->qid, req_id); + "[%s] Adding single RX desc, Queue: %u, req_id: %u\n", __func__, io_sq->qid, + req_id); desc->buff_addr_lo = (u32)ena_buf->paddr; desc->buff_addr_hi = diff --git a/drivers/net/ethernet/amazon/ena/ena_eth_com.h b/drivers/net/ethernet/amazon/ena/ena_eth_com.h index 372b259279ec..72b019758caa 100644 --- a/drivers/net/ethernet/amazon/ena/ena_eth_com.h +++ b/drivers/net/ethernet/amazon/ena/ena_eth_com.h @@ -8,8 +8,6 @@ #include "ena_com.h" -/* head update threshold in units of (queue size / ENA_COMP_HEAD_THRESH) */ -#define ENA_COMP_HEAD_THRESH 4 /* we allow 2 DMA descriptors per LLQ entry */ #define ENA_LLQ_ENTRY_DESC_CHUNK_SIZE (2 * sizeof(struct ena_eth_io_tx_desc)) #define ENA_LLQ_HEADER (128UL - ENA_LLQ_ENTRY_DESC_CHUNK_SIZE) @@ -145,8 +143,8 @@ static inline bool ena_com_is_doorbell_needed(struct ena_com_io_sq *io_sq, } netdev_dbg(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "Queue: %d num_descs: %d num_entries_needed: %d\n", - io_sq->qid, num_descs, num_entries_needed); + "Queue: %d num_descs: %d num_entries_needed: %d\n", io_sq->qid, num_descs, + num_entries_needed); return num_entries_needed > io_sq->entries_in_tx_burst_left; } @@ -157,43 +155,20 @@ static inline int ena_com_write_sq_doorbell(struct ena_com_io_sq *io_sq) u16 tail = io_sq->tail; netdev_dbg(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "Write submission queue doorbell for queue: %d tail: %d\n", - io_sq->qid, tail); + "Write submission queue doorbell for queue: %d tail: %d\n", io_sq->qid, tail); writel(tail, io_sq->db_addr); if (is_llq_max_tx_burst_exists(io_sq)) { netdev_dbg(ena_com_io_sq_to_ena_dev(io_sq)->net_device, - "Reset available entries in tx burst for queue %d to %d\n", - io_sq->qid, max_entries_in_tx_burst); + "Reset available entries in tx burst for queue %d to %d\n", io_sq->qid, + max_entries_in_tx_burst); io_sq->entries_in_tx_burst_left = max_entries_in_tx_burst; } return 0; } -static inline int ena_com_update_dev_comp_head(struct ena_com_io_cq *io_cq) -{ - u16 unreported_comp, head; - bool need_update; - - if (unlikely(io_cq->cq_head_db_reg)) { - head = io_cq->head; - unreported_comp = head - io_cq->last_head_update; - need_update = unreported_comp > (io_cq->q_depth / ENA_COMP_HEAD_THRESH); - - if (unlikely(need_update)) { - netdev_dbg(ena_com_io_cq_to_ena_dev(io_cq)->net_device, - "Write completion queue doorbell for queue %d: head: %d\n", - io_cq->qid, head); - writel(head, io_cq->cq_head_db_reg); - io_cq->last_head_update = head; - } - } - - return 0; -} - static inline void ena_com_update_numa_node(struct ena_com_io_cq *io_cq, u8 numa_node) { @@ -248,8 +223,8 @@ static inline int ena_com_tx_comp_req_id_get(struct ena_com_io_cq *io_cq, *req_id = READ_ONCE(cdesc->req_id); if (unlikely(*req_id >= io_cq->q_depth)) { - netdev_err(ena_com_io_cq_to_ena_dev(io_cq)->net_device, - "Invalid req id %d\n", cdesc->req_id); + netdev_err(ena_com_io_cq_to_ena_dev(io_cq)->net_device, "Invalid req id %d\n", + cdesc->req_id); return -EINVAL; } diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 1c0a7828d397..09e7da1a69c9 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -32,7 +32,7 @@ MODULE_LICENSE("GPL"); #define ENA_MAX_RINGS min_t(unsigned int, ENA_MAX_NUM_IO_QUEUES, num_possible_cpus()) #define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | \ - NETIF_MSG_TX_DONE | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR) + NETIF_MSG_IFDOWN | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR) static struct ena_aenq_handlers aenq_handlers; @@ -47,19 +47,44 @@ static int ena_restore_device(struct ena_adapter *adapter); static void ena_tx_timeout(struct net_device *dev, unsigned int txqueue) { + enum ena_regs_reset_reason_types reset_reason = ENA_REGS_RESET_OS_NETDEV_WD; struct ena_adapter *adapter = netdev_priv(dev); + unsigned int time_since_last_napi, threshold; + struct ena_ring *tx_ring; + int napi_scheduled; + + if (txqueue >= adapter->num_io_queues) { + netdev_err(dev, "TX timeout on invalid queue %u\n", txqueue); + goto schedule_reset; + } + + threshold = jiffies_to_usecs(dev->watchdog_timeo); + tx_ring = &adapter->tx_ring[txqueue]; + time_since_last_napi = jiffies_to_usecs(jiffies - tx_ring->tx_stats.last_napi_jiffies); + napi_scheduled = !!(tx_ring->napi->state & NAPIF_STATE_SCHED); + + netdev_err(dev, + "TX q %d is paused for too long (threshold %u). Time since last napi %u usec. napi scheduled: %d\n", + txqueue, + threshold, + time_since_last_napi, + napi_scheduled); + + if (threshold < time_since_last_napi && napi_scheduled) { + netdev_err(dev, + "napi handler hasn't been called for a long time but is scheduled\n"); + reset_reason = ENA_REGS_RESET_SUSPECTED_POLL_STARVATION; + } +schedule_reset: /* Change the state of the device to trigger reset * Check that we are not in the middle or a trigger already */ - if (test_and_set_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags)) return; - ena_reset_device(adapter, ENA_REGS_RESET_OS_NETDEV_WD); + ena_reset_device(adapter, reset_reason); ena_increase_stat(&adapter->dev_stats.tx_timeout, 1, &adapter->syncp); - - netif_err(adapter, tx_err, dev, "Transmit time out\n"); } static void update_rx_ring_mtu(struct ena_adapter *adapter, int mtu) @@ -116,11 +141,9 @@ int ena_xmit_common(struct ena_adapter *adapter, if (unlikely(rc)) { netif_err(adapter, tx_queued, adapter->netdev, "Failed to prepare tx bufs\n"); - ena_increase_stat(&ring->tx_stats.prepare_ctx_err, 1, - &ring->syncp); + ena_increase_stat(&ring->tx_stats.prepare_ctx_err, 1, &ring->syncp); if (rc != -ENOMEM) - ena_reset_device(adapter, - ENA_REGS_RESET_DRIVER_INVALID_STATE); + ena_reset_device(adapter, ENA_REGS_RESET_DRIVER_INVALID_STATE); return rc; } @@ -485,8 +508,7 @@ static struct page *ena_alloc_map_page(struct ena_ring *rx_ring, */ page = dev_alloc_page(); if (!page) { - ena_increase_stat(&rx_ring->rx_stats.page_alloc_fail, 1, - &rx_ring->syncp); + ena_increase_stat(&rx_ring->rx_stats.page_alloc_fail, 1, &rx_ring->syncp); return ERR_PTR(-ENOSPC); } @@ -523,7 +545,7 @@ static int ena_alloc_rx_buffer(struct ena_ring *rx_ring, /* We handle DMA here */ page = ena_alloc_map_page(rx_ring, &dma); - if (unlikely(IS_ERR(page))) + if (IS_ERR(page)) return PTR_ERR(page); netif_dbg(rx_ring->adapter, rx_status, rx_ring->netdev, @@ -545,8 +567,8 @@ static void ena_unmap_rx_buff_attrs(struct ena_ring *rx_ring, struct ena_rx_buffer *rx_info, unsigned long attrs) { - dma_unmap_page_attrs(rx_ring->dev, rx_info->dma_addr, ENA_PAGE_SIZE, - DMA_BIDIRECTIONAL, attrs); + dma_unmap_page_attrs(rx_ring->dev, rx_info->dma_addr, ENA_PAGE_SIZE, DMA_BIDIRECTIONAL, + attrs); } static void ena_free_rx_page(struct ena_ring *rx_ring, @@ -819,8 +841,7 @@ static int ena_clean_tx_irq(struct ena_ring *tx_ring, u32 budget) &req_id); if (rc) { if (unlikely(rc == -EINVAL)) - handle_invalid_req_id(tx_ring, req_id, NULL, - false); + handle_invalid_req_id(tx_ring, req_id, NULL, false); break; } @@ -856,7 +877,6 @@ static int ena_clean_tx_irq(struct ena_ring *tx_ring, u32 budget) tx_ring->next_to_clean = next_to_clean; ena_com_comp_ack(tx_ring->ena_com_io_sq, total_done); - ena_com_update_dev_comp_head(tx_ring->ena_com_io_cq); netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); @@ -1046,8 +1066,7 @@ static struct sk_buff *ena_rx_skb(struct ena_ring *rx_ring, DMA_FROM_DEVICE); if (!reuse_rx_buf_page) - ena_unmap_rx_buff_attrs(rx_ring, rx_info, - DMA_ATTR_SKIP_CPU_SYNC); + ena_unmap_rx_buff_attrs(rx_ring, rx_info, DMA_ATTR_SKIP_CPU_SYNC); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_info->page, page_offset + buf_offset, len, buf_len); @@ -1303,10 +1322,8 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi, ENA_RX_REFILL_THRESH_PACKET); /* Optimization, try to batch new rx buffers */ - if (refill_required > refill_threshold) { - ena_com_update_dev_comp_head(rx_ring->ena_com_io_cq); + if (refill_required > refill_threshold) ena_refill_rx_bufs(rx_ring, refill_required); - } if (xdp_flags & ENA_XDP_REDIRECT) xdp_do_flush(); @@ -1320,8 +1337,7 @@ error: adapter = netdev_priv(rx_ring->netdev); if (rc == -ENOSPC) { - ena_increase_stat(&rx_ring->rx_stats.bad_desc_num, 1, - &rx_ring->syncp); + ena_increase_stat(&rx_ring->rx_stats.bad_desc_num, 1, &rx_ring->syncp); ena_reset_device(adapter, ENA_REGS_RESET_TOO_MANY_RX_DESCS); } else { ena_increase_stat(&rx_ring->rx_stats.bad_req_id, 1, @@ -1811,8 +1827,7 @@ static int ena_rss_configure(struct ena_adapter *adapter) if (!ena_dev->rss.tbl_log_size) { rc = ena_rss_init_default(adapter); if (rc && (rc != -EOPNOTSUPP)) { - netif_err(adapter, ifup, adapter->netdev, - "Failed to init RSS rc: %d\n", rc); + netif_err(adapter, ifup, adapter->netdev, "Failed to init RSS rc: %d\n", rc); return rc; } } @@ -2134,6 +2149,12 @@ int ena_up(struct ena_adapter *adapter) */ ena_init_napi_in_range(adapter, 0, io_queue_count); + /* Enabling DIM needs to happen before enabling IRQs since DIM + * is run from napi routine + */ + if (ena_com_interrupt_moderation_supported(adapter->ena_dev)) + ena_com_enable_adaptive_moderation(adapter->ena_dev); + rc = ena_request_io_irq(adapter); if (rc) goto err_req_irq; @@ -2184,7 +2205,7 @@ void ena_down(struct ena_adapter *adapter) { int io_queue_count = adapter->num_io_queues + adapter->xdp_num_queues; - netif_info(adapter, ifdown, adapter->netdev, "%s\n", __func__); + netif_dbg(adapter, ifdown, adapter->netdev, "%s\n", __func__); clear_bit(ENA_FLAG_DEV_UP, &adapter->flags); @@ -2197,8 +2218,6 @@ void ena_down(struct ena_adapter *adapter) /* After this point the napi handler won't enable the tx queue */ ena_napi_disable_in_range(adapter, 0, io_queue_count); - /* After destroy the queue there won't be any new interrupts */ - if (test_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags)) { int rc; @@ -2588,8 +2607,6 @@ static netdev_tx_t ena_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(rc)) goto error_drop_packet; - skb_tx_timestamp(skb); - next_to_use = tx_ring->next_to_use; req_id = tx_ring->free_ids[next_to_use]; tx_info = &tx_ring->tx_buffer_info[req_id]; @@ -2653,6 +2670,8 @@ static netdev_tx_t ena_start_xmit(struct sk_buff *skb, struct net_device *dev) } } + skb_tx_timestamp(skb); + if (netif_xmit_stopped(txq) || !netdev_xmit_more()) /* trigger the dma engine. ena_ring_tx_doorbell() * calls a memory barrier inside it. @@ -2670,22 +2689,6 @@ error_drop_packet: return NETDEV_TX_OK; } -static u16 ena_select_queue(struct net_device *dev, struct sk_buff *skb, - struct net_device *sb_dev) -{ - u16 qid; - /* we suspect that this is good for in--kernel network services that - * want to loop incoming skb rx to tx in normal user generated traffic, - * most probably we will not get to this - */ - if (skb_rx_queue_recorded(skb)) - qid = skb_get_rx_queue(skb); - else - qid = netdev_pick_tx(dev, skb, NULL); - - return qid; -} - static void ena_config_host_info(struct ena_com_dev *ena_dev, struct pci_dev *pdev) { struct device *dev = &pdev->dev; @@ -2764,8 +2767,7 @@ static void ena_config_debug_area(struct ena_adapter *adapter) rc = ena_com_set_host_attributes(adapter->ena_dev); if (rc) { if (rc == -EOPNOTSUPP) - netif_warn(adapter, drv, adapter->netdev, - "Cannot set host attributes\n"); + netif_warn(adapter, drv, adapter->netdev, "Cannot set host attributes\n"); else netif_err(adapter, drv, adapter->netdev, "Cannot set host attributes\n"); @@ -2863,18 +2865,16 @@ static const struct net_device_ops ena_netdev_ops = { .ndo_open = ena_open, .ndo_stop = ena_close, .ndo_start_xmit = ena_start_xmit, - .ndo_select_queue = ena_select_queue, .ndo_get_stats64 = ena_get_stats64, .ndo_tx_timeout = ena_tx_timeout, .ndo_change_mtu = ena_change_mtu, - .ndo_set_mac_address = NULL, .ndo_validate_addr = eth_validate_addr, .ndo_bpf = ena_xdp, .ndo_xdp_xmit = ena_xdp_xmit, }; -static void ena_calc_io_queue_size(struct ena_adapter *adapter, - struct ena_com_dev_get_features_ctx *get_feat_ctx) +static int ena_calc_io_queue_size(struct ena_adapter *adapter, + struct ena_com_dev_get_features_ctx *get_feat_ctx) { struct ena_admin_feature_llq_desc *llq = &get_feat_ctx->llq; struct ena_com_dev *ena_dev = adapter->ena_dev; @@ -2933,6 +2933,18 @@ static void ena_calc_io_queue_size(struct ena_adapter *adapter, max_tx_queue_size = rounddown_pow_of_two(max_tx_queue_size); max_rx_queue_size = rounddown_pow_of_two(max_rx_queue_size); + if (max_tx_queue_size < ENA_MIN_RING_SIZE) { + netdev_err(adapter->netdev, "Device max TX queue size: %d < minimum: %d\n", + max_tx_queue_size, ENA_MIN_RING_SIZE); + return -EINVAL; + } + + if (max_rx_queue_size < ENA_MIN_RING_SIZE) { + netdev_err(adapter->netdev, "Device max RX queue size: %d < minimum: %d\n", + max_rx_queue_size, ENA_MIN_RING_SIZE); + return -EINVAL; + } + /* When forcing large headers, we multiply the entry size by 2, and therefore divide * the queue size by 2, leaving the amount of memory used by the queues unchanged. */ @@ -2963,6 +2975,8 @@ static void ena_calc_io_queue_size(struct ena_adapter *adapter, adapter->max_rx_ring_size = max_rx_queue_size; adapter->requested_tx_ring_size = tx_queue_size; adapter->requested_rx_ring_size = rx_queue_size; + + return 0; } static int ena_device_validate_params(struct ena_adapter *adapter, @@ -3070,6 +3084,7 @@ static int ena_device_init(struct ena_adapter *adapter, struct pci_dev *pdev, bool *wd_state) { struct ena_com_dev *ena_dev = adapter->ena_dev; + struct net_device *netdev = adapter->netdev; struct ena_llq_configurations llq_config; struct device *dev = &pdev->dev; bool readless_supported; @@ -3159,15 +3174,19 @@ static int ena_device_init(struct ena_adapter *adapter, struct pci_dev *pdev, rc = ena_set_queues_placement_policy(pdev, ena_dev, &get_feat_ctx->llq, &llq_config); if (rc) { - dev_err(dev, "ENA device init failed\n"); + netdev_err(netdev, "Cannot set queues placement policy rc= %d\n", rc); goto err_admin_init; } - ena_calc_io_queue_size(adapter, get_feat_ctx); + rc = ena_calc_io_queue_size(adapter, get_feat_ctx); + if (unlikely(rc)) + goto err_admin_init; return 0; err_admin_init: + ena_com_abort_admin_commands(ena_dev); + ena_com_wait_for_abort_completion(ena_dev); ena_com_delete_host_info(ena_dev); ena_com_admin_destroy(ena_dev); err_mmio_read_less: @@ -3226,7 +3245,7 @@ static void ena_destroy_device(struct ena_adapter *adapter, bool graceful) if (!graceful) ena_com_set_admin_running_state(ena_dev, false); - if (test_bit(ENA_FLAG_DEV_UP, &adapter->flags)) + if (dev_up) ena_down(adapter); /* Stop the device from sending AENQ events (in case reset flag is set @@ -3372,14 +3391,18 @@ static int check_missing_comp_in_tx_queue(struct ena_adapter *adapter, struct ena_ring *tx_ring) { struct ena_napi *ena_napi = container_of(tx_ring->napi, struct ena_napi, napi); + enum ena_regs_reset_reason_types reset_reason = ENA_REGS_RESET_MISS_TX_CMPL; unsigned int time_since_last_napi; unsigned int missing_tx_comp_to; bool is_tx_comp_time_expired; struct ena_tx_buffer *tx_buf; unsigned long last_jiffies; + int napi_scheduled; u32 missed_tx = 0; int i, rc = 0; + missing_tx_comp_to = jiffies_to_msecs(adapter->missing_tx_completion_to); + for (i = 0; i < tx_ring->ring_size; i++) { tx_buf = &tx_ring->tx_buffer_info[i]; last_jiffies = tx_buf->last_jiffies; @@ -3406,25 +3429,45 @@ static int check_missing_comp_in_tx_queue(struct ena_adapter *adapter, adapter->missing_tx_completion_to); if (unlikely(is_tx_comp_time_expired)) { - if (!tx_buf->print_once) { - time_since_last_napi = jiffies_to_usecs(jiffies - tx_ring->tx_stats.last_napi_jiffies); - missing_tx_comp_to = jiffies_to_msecs(adapter->missing_tx_completion_to); - netif_notice(adapter, tx_err, adapter->netdev, - "Found a Tx that wasn't completed on time, qid %d, index %d. %u usecs have passed since last napi execution. Missing Tx timeout value %u msecs\n", - tx_ring->qid, i, time_since_last_napi, missing_tx_comp_to); + time_since_last_napi = + jiffies_to_usecs(jiffies - tx_ring->tx_stats.last_napi_jiffies); + napi_scheduled = !!(ena_napi->napi.state & NAPIF_STATE_SCHED); + + if (missing_tx_comp_to < time_since_last_napi && napi_scheduled) { + /* We suspect napi isn't called because the + * bottom half is not run. Require a bigger + * timeout for these cases + */ + if (!time_is_before_jiffies(last_jiffies + + 2 * adapter->missing_tx_completion_to)) + continue; + + reset_reason = ENA_REGS_RESET_SUSPECTED_POLL_STARVATION; } - tx_buf->print_once = 1; missed_tx++; + + if (tx_buf->print_once) + continue; + + netif_notice(adapter, tx_err, adapter->netdev, + "TX hasn't completed, qid %d, index %d. %u usecs from last napi execution, napi scheduled: %d\n", + tx_ring->qid, i, time_since_last_napi, napi_scheduled); + + tx_buf->print_once = 1; } } if (unlikely(missed_tx > adapter->missing_tx_completion_threshold)) { netif_err(adapter, tx_err, adapter->netdev, - "The number of lost tx completions is above the threshold (%d > %d). Reset the device\n", + "Lost TX completions are above the threshold (%d > %d). Completion transmission timeout: %u.\n", missed_tx, - adapter->missing_tx_completion_threshold); - ena_reset_device(adapter, ENA_REGS_RESET_MISS_TX_CMPL); + adapter->missing_tx_completion_threshold, + missing_tx_comp_to); + netif_err(adapter, tx_err, adapter->netdev, + "Resetting the device\n"); + + ena_reset_device(adapter, reset_reason); rc = -EIO; } @@ -3762,8 +3805,8 @@ static int ena_rss_init_default(struct ena_adapter *adapter) } } - rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_TOEPLITZ, NULL, - ENA_HASH_KEY_SIZE, 0xFFFFFFFF); + rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_TOEPLITZ, NULL, ENA_HASH_KEY_SIZE, + 0xFFFFFFFF); if (unlikely(rc && (rc != -EOPNOTSUPP))) { dev_err(dev, "Cannot fill hash function\n"); goto err_fill_indir; @@ -4040,8 +4083,8 @@ static void __ena_shutoff(struct pci_dev *pdev, bool shutdown) free_irq_cpu_rmap(netdev->rx_cpu_rmap); netdev->rx_cpu_rmap = NULL; } -#endif /* CONFIG_RFS_ACCEL */ +#endif /* CONFIG_RFS_ACCEL */ /* Make sure timer and reset routine won't be called after * freeing device resources. */ diff --git a/drivers/net/ethernet/amazon/ena/ena_regs_defs.h b/drivers/net/ethernet/amazon/ena/ena_regs_defs.h index 1e007a41a525..2c3d6a77ea79 100644 --- a/drivers/net/ethernet/amazon/ena/ena_regs_defs.h +++ b/drivers/net/ethernet/amazon/ena/ena_regs_defs.h @@ -21,6 +21,7 @@ enum ena_regs_reset_reason_types { ENA_REGS_RESET_USER_TRIGGER = 12, ENA_REGS_RESET_GENERIC = 13, ENA_REGS_RESET_MISS_INTERRUPT = 14, + ENA_REGS_RESET_SUSPECTED_POLL_STARVATION = 15, }; /* ena_registers offsets */ diff --git a/drivers/net/ethernet/amazon/ena/ena_xdp.c b/drivers/net/ethernet/amazon/ena/ena_xdp.c index fc1c4ef73ba3..337c435d3ce9 100644 --- a/drivers/net/ethernet/amazon/ena/ena_xdp.c +++ b/drivers/net/ethernet/amazon/ena/ena_xdp.c @@ -412,7 +412,6 @@ static int ena_clean_xdp_irq(struct ena_ring *tx_ring, u32 budget) tx_ring->next_to_clean = next_to_clean; ena_com_comp_ack(tx_ring->ena_com_io_sq, total_done); - ena_com_update_dev_comp_head(tx_ring->ena_com_io_cq); netif_dbg(tx_ring->adapter, tx_done, tx_ring->netdev, "tx_poll: q %d done. total pkts: %d\n", diff --git a/drivers/net/ethernet/amd/pds_core/adminq.c b/drivers/net/ethernet/amd/pds_core/adminq.c index 5beadabc2136..c83a0a80d533 100644 --- a/drivers/net/ethernet/amd/pds_core/adminq.c +++ b/drivers/net/ethernet/amd/pds_core/adminq.c @@ -63,6 +63,15 @@ static int pdsc_process_notifyq(struct pdsc_qcq *qcq) return nq_work; } +static bool pdsc_adminq_inc_if_up(struct pdsc *pdsc) +{ + if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER) || + pdsc->state & BIT_ULL(PDSC_S_FW_DEAD)) + return false; + + return refcount_inc_not_zero(&pdsc->adminq_refcnt); +} + void pdsc_process_adminq(struct pdsc_qcq *qcq) { union pds_core_adminq_comp *comp; @@ -73,11 +82,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq) unsigned long irqflags; int nq_work = 0; int aq_work = 0; - int credits; - /* Don't process AdminQ when shutting down */ - if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) { - dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n", + /* Don't process AdminQ when it's not up */ + if (!pdsc_adminq_inc_if_up(pdsc)) { + dev_err(pdsc->dev, "%s: called while adminq is unavailable\n", __func__); return; } @@ -119,11 +127,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq) credits: /* Return the interrupt credits, one for each completion */ - credits = nq_work + aq_work; - if (credits) - pds_core_intr_credits(&pdsc->intr_ctrl[qcq->intx], - credits, - PDS_CORE_INTR_CRED_REARM); + pds_core_intr_credits(&pdsc->intr_ctrl[qcq->intx], + nq_work + aq_work, + PDS_CORE_INTR_CRED_REARM); + refcount_dec(&pdsc->adminq_refcnt); } void pdsc_work_thread(struct work_struct *work) @@ -135,18 +142,19 @@ void pdsc_work_thread(struct work_struct *work) irqreturn_t pdsc_adminq_isr(int irq, void *data) { - struct pdsc_qcq *qcq = data; - struct pdsc *pdsc = qcq->pdsc; + struct pdsc *pdsc = data; + struct pdsc_qcq *qcq; - /* Don't process AdminQ when shutting down */ - if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) { - dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n", + /* Don't process AdminQ when it's not up */ + if (!pdsc_adminq_inc_if_up(pdsc)) { + dev_err(pdsc->dev, "%s: called while adminq is unavailable\n", __func__); return IRQ_HANDLED; } + qcq = &pdsc->adminqcq; queue_work(pdsc->wq, &qcq->work); - pds_core_intr_mask(&pdsc->intr_ctrl[qcq->intx], PDS_CORE_INTR_MASK_CLEAR); + refcount_dec(&pdsc->adminq_refcnt); return IRQ_HANDLED; } @@ -179,10 +187,16 @@ static int __pdsc_adminq_post(struct pdsc *pdsc, /* Check that the FW is running */ if (!pdsc_is_fw_running(pdsc)) { - u8 fw_status = ioread8(&pdsc->info_regs->fw_status); - - dev_info(pdsc->dev, "%s: post failed - fw not running %#02x:\n", - __func__, fw_status); + if (pdsc->info_regs) { + u8 fw_status = + ioread8(&pdsc->info_regs->fw_status); + + dev_info(pdsc->dev, "%s: post failed - fw not running %#02x:\n", + __func__, fw_status); + } else { + dev_info(pdsc->dev, "%s: post failed - BARs not setup\n", + __func__); + } ret = -ENXIO; goto err_out_unlock; @@ -230,6 +244,12 @@ int pdsc_adminq_post(struct pdsc *pdsc, int err = 0; int index; + if (!pdsc_adminq_inc_if_up(pdsc)) { + dev_dbg(pdsc->dev, "%s: preventing adminq cmd %u\n", + __func__, cmd->opcode); + return -ENXIO; + } + wc.qcq = &pdsc->adminqcq; index = __pdsc_adminq_post(pdsc, &pdsc->adminqcq, cmd, comp, &wc); if (index < 0) { @@ -248,10 +268,16 @@ int pdsc_adminq_post(struct pdsc *pdsc, break; if (!pdsc_is_fw_running(pdsc)) { - u8 fw_status = ioread8(&pdsc->info_regs->fw_status); - - dev_dbg(pdsc->dev, "%s: post wait failed - fw not running %#02x:\n", - __func__, fw_status); + if (pdsc->info_regs) { + u8 fw_status = + ioread8(&pdsc->info_regs->fw_status); + + dev_dbg(pdsc->dev, "%s: post wait failed - fw not running %#02x:\n", + __func__, fw_status); + } else { + dev_dbg(pdsc->dev, "%s: post wait failed - BARs not setup\n", + __func__); + } err = -ENXIO; break; } @@ -285,6 +311,8 @@ err_out: queue_work(pdsc->wq, &pdsc->health_work); } + refcount_dec(&pdsc->adminq_refcnt); + return err; } EXPORT_SYMBOL_GPL(pdsc_adminq_post); diff --git a/drivers/net/ethernet/amd/pds_core/auxbus.c b/drivers/net/ethernet/amd/pds_core/auxbus.c index 11c23a7f3172..a3c79848a69a 100644 --- a/drivers/net/ethernet/amd/pds_core/auxbus.c +++ b/drivers/net/ethernet/amd/pds_core/auxbus.c @@ -184,6 +184,9 @@ int pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf) struct pds_auxiliary_dev *padev; int err = 0; + if (!cf) + return -ENODEV; + mutex_lock(&pf->config_lock); padev = pf->vfs[cf->vf_id].padev; @@ -202,14 +205,27 @@ int pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf) int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf) { struct pds_auxiliary_dev *padev; - enum pds_core_vif_types vt; char devname[PDS_DEVNAME_LEN]; + enum pds_core_vif_types vt; + unsigned long mask; u16 vt_support; int client_id; int err = 0; + if (!cf) + return -ENODEV; + mutex_lock(&pf->config_lock); + mask = BIT_ULL(PDSC_S_FW_DEAD) | + BIT_ULL(PDSC_S_STOPPING_DRIVER); + if (cf->state & mask) { + dev_err(pf->dev, "%s: can't add dev, VF client in bad state %#lx\n", + __func__, cf->state); + err = -ENXIO; + goto out_unlock; + } + /* We only support vDPA so far, so it is the only one to * be verified that it is available in the Core device and * enabled in the devlink param. In the future this might diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 0d2091e9eb28..9662ee72814c 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -125,10 +125,11 @@ static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq) snprintf(name, sizeof(name), "%s-%d-%s", PDS_CORE_DRV_NAME, pdsc->pdev->bus->number, qcq->q.name); - index = pdsc_intr_alloc(pdsc, name, pdsc_adminq_isr, qcq); + index = pdsc_intr_alloc(pdsc, name, pdsc_adminq_isr, pdsc); if (index < 0) return index; qcq->intx = index; + qcq->cq.bound_intr = &pdsc->intr_info[index]; return 0; } @@ -222,7 +223,6 @@ int pdsc_qcq_alloc(struct pdsc *pdsc, unsigned int type, unsigned int index, goto err_out_free_irq; } - qcq->cq.bound_intr = &pdsc->intr_info[qcq->intx]; qcq->cq.num_descs = num_descs; qcq->cq.desc_size = cq_desc_size; qcq->cq.tail_idx = 0; @@ -300,6 +300,17 @@ err_out: return err; } +static void pdsc_core_uninit(struct pdsc *pdsc) +{ + pdsc_qcq_free(pdsc, &pdsc->notifyqcq); + pdsc_qcq_free(pdsc, &pdsc->adminqcq); + + if (pdsc->kern_dbpage) { + iounmap(pdsc->kern_dbpage); + pdsc->kern_dbpage = NULL; + } +} + static int pdsc_core_init(struct pdsc *pdsc) { union pds_core_dev_comp comp = {}; @@ -310,9 +321,32 @@ static int pdsc_core_init(struct pdsc *pdsc) struct pds_core_dev_init_data_in cidi; u32 dbid_count; u32 dbpage_num; + int numdescs; size_t sz; int err; + /* Scale the descriptor ring length based on number of CPUs and VFs */ + numdescs = max_t(int, PDSC_ADMINQ_MIN_LENGTH, num_online_cpus()); + numdescs += 2 * pci_sriov_get_totalvfs(pdsc->pdev); + numdescs = roundup_pow_of_two(numdescs); + err = pdsc_qcq_alloc(pdsc, PDS_CORE_QTYPE_ADMINQ, 0, "adminq", + PDS_CORE_QCQ_F_CORE | PDS_CORE_QCQ_F_INTR, + numdescs, + sizeof(union pds_core_adminq_cmd), + sizeof(union pds_core_adminq_comp), + 0, &pdsc->adminqcq); + if (err) + return err; + + err = pdsc_qcq_alloc(pdsc, PDS_CORE_QTYPE_NOTIFYQ, 0, "notifyq", + PDS_CORE_QCQ_F_NOTIFYQ, + PDSC_NOTIFYQ_LENGTH, + sizeof(struct pds_core_notifyq_cmd), + sizeof(union pds_core_notifyq_comp), + 0, &pdsc->notifyqcq); + if (err) + goto err_out_uninit; + cidi.adminq_q_base = cpu_to_le64(pdsc->adminqcq.q_base_pa); cidi.adminq_cq_base = cpu_to_le64(pdsc->adminqcq.cq_base_pa); cidi.notifyq_cq_base = cpu_to_le64(pdsc->notifyqcq.cq.base_pa); @@ -336,7 +370,7 @@ static int pdsc_core_init(struct pdsc *pdsc) if (err) { dev_err(pdsc->dev, "Device init command failed: %pe\n", ERR_PTR(err)); - return err; + goto err_out_uninit; } pdsc->hw_index = le32_to_cpu(cido.core_hw_index); @@ -346,7 +380,8 @@ static int pdsc_core_init(struct pdsc *pdsc) pdsc->kern_dbpage = pdsc_map_dbpage(pdsc, dbpage_num); if (!pdsc->kern_dbpage) { dev_err(pdsc->dev, "Cannot map dbpage, aborting\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_out_uninit; } pdsc->adminqcq.q.hw_type = cido.adminq_hw_type; @@ -359,6 +394,10 @@ static int pdsc_core_init(struct pdsc *pdsc) pdsc->last_eid = 0; + return 0; + +err_out_uninit: + pdsc_core_uninit(pdsc); return err; } @@ -401,41 +440,12 @@ static int pdsc_viftypes_init(struct pdsc *pdsc) int pdsc_setup(struct pdsc *pdsc, bool init) { - int numdescs; int err; - if (init) - err = pdsc_dev_init(pdsc); - else - err = pdsc_dev_reinit(pdsc); + err = pdsc_dev_init(pdsc); if (err) return err; - /* Scale the descriptor ring length based on number of CPUs and VFs */ - numdescs = max_t(int, PDSC_ADMINQ_MIN_LENGTH, num_online_cpus()); - numdescs += 2 * pci_sriov_get_totalvfs(pdsc->pdev); - numdescs = roundup_pow_of_two(numdescs); - err = pdsc_qcq_alloc(pdsc, PDS_CORE_QTYPE_ADMINQ, 0, "adminq", - PDS_CORE_QCQ_F_CORE | PDS_CORE_QCQ_F_INTR, - numdescs, - sizeof(union pds_core_adminq_cmd), - sizeof(union pds_core_adminq_comp), - 0, &pdsc->adminqcq); - if (err) - goto err_out_teardown; - - err = pdsc_qcq_alloc(pdsc, PDS_CORE_QTYPE_NOTIFYQ, 0, "notifyq", - PDS_CORE_QCQ_F_NOTIFYQ, - PDSC_NOTIFYQ_LENGTH, - sizeof(struct pds_core_notifyq_cmd), - sizeof(union pds_core_notifyq_comp), - 0, &pdsc->notifyqcq); - if (err) - goto err_out_teardown; - - /* NotifyQ rides on the AdminQ interrupt */ - pdsc->notifyqcq.intx = pdsc->adminqcq.intx; - /* Set up the Core with the AdminQ and NotifyQ info */ err = pdsc_core_init(pdsc); if (err) @@ -450,6 +460,7 @@ int pdsc_setup(struct pdsc *pdsc, bool init) pdsc_debugfs_add_viftype(pdsc); } + refcount_set(&pdsc->adminq_refcnt, 1); clear_bit(PDSC_S_FW_DEAD, &pdsc->state); return 0; @@ -460,32 +471,19 @@ err_out_teardown: void pdsc_teardown(struct pdsc *pdsc, bool removing) { - int i; - if (!pdsc->pdev->is_virtfn) pdsc_devcmd_reset(pdsc); - pdsc_qcq_free(pdsc, &pdsc->notifyqcq); - pdsc_qcq_free(pdsc, &pdsc->adminqcq); + if (pdsc->adminqcq.work.func) + cancel_work_sync(&pdsc->adminqcq.work); + + pdsc_core_uninit(pdsc); if (removing) { kfree(pdsc->viftype_status); pdsc->viftype_status = NULL; } - if (pdsc->intr_info) { - for (i = 0; i < pdsc->nintrs; i++) - pdsc_intr_free(pdsc, i); - - if (removing) { - kfree(pdsc->intr_info); - pdsc->intr_info = NULL; - } - } - - if (pdsc->kern_dbpage) { - iounmap(pdsc->kern_dbpage); - pdsc->kern_dbpage = NULL; - } + pdsc_dev_uninit(pdsc); set_bit(PDSC_S_FW_DEAD, &pdsc->state); } @@ -512,6 +510,24 @@ void pdsc_stop(struct pdsc *pdsc) PDS_CORE_INTR_MASK_SET); } +static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc) +{ + /* The driver initializes the adminq_refcnt to 1 when the adminq is + * allocated and ready for use. Other users/requesters will increment + * the refcnt while in use. If the refcnt is down to 1 then the adminq + * is not in use and the refcnt can be cleared and adminq freed. Before + * calling this function the driver will set PDSC_S_FW_DEAD, which + * prevent subsequent attempts to use the adminq and increment the + * refcnt to fail. This guarantees that this function will eventually + * exit. + */ + while (!refcount_dec_if_one(&pdsc->adminq_refcnt)) { + dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n", + __func__); + cpu_relax(); + } +} + void pdsc_fw_down(struct pdsc *pdsc) { union pds_core_notifyq_comp reset_event = { @@ -527,6 +543,8 @@ void pdsc_fw_down(struct pdsc *pdsc) if (pdsc->pdev->is_virtfn) return; + pdsc_adminq_wait_and_dec_once_unused(pdsc); + /* Notify clients of fw_down */ if (pdsc->fw_reporter) devlink_health_report(pdsc->fw_reporter, "FW down reported", pdsc); @@ -577,14 +595,19 @@ err_out: static void pdsc_check_pci_health(struct pdsc *pdsc) { - u8 fw_status = ioread8(&pdsc->info_regs->fw_status); + u8 fw_status; + + /* some sort of teardown already in progress */ + if (!pdsc->info_regs) + return; + + fw_status = ioread8(&pdsc->info_regs->fw_status); /* is PCI broken? */ if (fw_status != PDS_RC_BAD_PCI) return; - pdsc_reset_prepare(pdsc->pdev); - pdsc_reset_done(pdsc->pdev); + pci_reset_function(pdsc->pdev); } void pdsc_health_thread(struct work_struct *work) diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h index e35d3e7006bf..92d7657dd614 100644 --- a/drivers/net/ethernet/amd/pds_core/core.h +++ b/drivers/net/ethernet/amd/pds_core/core.h @@ -184,6 +184,7 @@ struct pdsc { struct mutex devcmd_lock; /* lock for dev_cmd operations */ struct mutex config_lock; /* lock for configuration operations */ spinlock_t adminq_lock; /* lock for adminq operations */ + refcount_t adminq_refcnt; struct pds_core_dev_info_regs __iomem *info_regs; struct pds_core_dev_cmd_regs __iomem *cmd_regs; struct pds_core_intr __iomem *intr_ctrl; @@ -280,11 +281,8 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd, union pds_core_dev_comp *comp, int max_seconds); int pdsc_devcmd_init(struct pdsc *pdsc); int pdsc_devcmd_reset(struct pdsc *pdsc); -int pdsc_dev_reinit(struct pdsc *pdsc); int pdsc_dev_init(struct pdsc *pdsc); - -void pdsc_reset_prepare(struct pci_dev *pdev); -void pdsc_reset_done(struct pci_dev *pdev); +void pdsc_dev_uninit(struct pdsc *pdsc); int pdsc_intr_alloc(struct pdsc *pdsc, char *name, irq_handler_t handler, void *data); diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c index 8ec392299b7d..6bdd02b7aa6d 100644 --- a/drivers/net/ethernet/amd/pds_core/debugfs.c +++ b/drivers/net/ethernet/amd/pds_core/debugfs.c @@ -32,8 +32,8 @@ void pdsc_debugfs_del_dev(struct pdsc *pdsc) static int identity_show(struct seq_file *seq, void *v) { - struct pdsc *pdsc = seq->private; struct pds_core_dev_identity *ident; + struct pdsc *pdsc = seq->private; int vt; ident = &pdsc->dev_ident; @@ -64,6 +64,10 @@ DEFINE_SHOW_ATTRIBUTE(identity); void pdsc_debugfs_add_ident(struct pdsc *pdsc) { + /* This file will already exist in the reset flow */ + if (debugfs_lookup("identity", pdsc->dentry)) + return; + debugfs_create_file("identity", 0400, pdsc->dentry, pdsc, &identity_fops); } @@ -102,10 +106,8 @@ static const struct debugfs_reg32 intr_ctrl_regs[] = { void pdsc_debugfs_add_qcq(struct pdsc *pdsc, struct pdsc_qcq *qcq) { - struct dentry *qcq_dentry, *q_dentry, *cq_dentry; - struct dentry *intr_dentry; + struct dentry *qcq_dentry, *q_dentry, *cq_dentry, *intr_dentry; struct debugfs_regset32 *intr_ctrl_regset; - struct pdsc_intr_info *intr = &pdsc->intr_info[qcq->intx]; struct pdsc_queue *q = &qcq->q; struct pdsc_cq *cq = &qcq->cq; @@ -143,6 +145,8 @@ void pdsc_debugfs_add_qcq(struct pdsc *pdsc, struct pdsc_qcq *qcq) debugfs_create_u16("tail", 0400, cq_dentry, &cq->tail_idx); if (qcq->flags & PDS_CORE_QCQ_F_INTR) { + struct pdsc_intr_info *intr = &pdsc->intr_info[qcq->intx]; + intr_dentry = debugfs_create_dir("intr", qcq->dentry); if (IS_ERR_OR_NULL(intr_dentry)) return; diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c index 31940b857e0e..e494e1298dc9 100644 --- a/drivers/net/ethernet/amd/pds_core/dev.c +++ b/drivers/net/ethernet/amd/pds_core/dev.c @@ -57,6 +57,9 @@ int pdsc_err_to_errno(enum pds_core_status_code code) bool pdsc_is_fw_running(struct pdsc *pdsc) { + if (!pdsc->info_regs) + return false; + pdsc->fw_status = ioread8(&pdsc->info_regs->fw_status); pdsc->last_fw_time = jiffies; pdsc->last_hb = ioread32(&pdsc->info_regs->fw_heartbeat); @@ -182,13 +185,17 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd, { int err; + if (!pdsc->cmd_regs) + return -ENXIO; + memcpy_toio(&pdsc->cmd_regs->cmd, cmd, sizeof(*cmd)); pdsc_devcmd_dbell(pdsc); err = pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds); - memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp)); if ((err == -ENXIO || err == -ETIMEDOUT) && pdsc->wq) queue_work(pdsc->wq, &pdsc->health_work); + else + memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp)); return err; } @@ -309,11 +316,20 @@ static int pdsc_identify(struct pdsc *pdsc) return 0; } -int pdsc_dev_reinit(struct pdsc *pdsc) +void pdsc_dev_uninit(struct pdsc *pdsc) { - pdsc_init_devinfo(pdsc); + if (pdsc->intr_info) { + int i; + + for (i = 0; i < pdsc->nintrs; i++) + pdsc_intr_free(pdsc, i); - return pdsc_identify(pdsc); + kfree(pdsc->intr_info); + pdsc->intr_info = NULL; + pdsc->nintrs = 0; + } + + pci_free_irq_vectors(pdsc->pdev); } int pdsc_dev_init(struct pdsc *pdsc) @@ -341,10 +357,8 @@ int pdsc_dev_init(struct pdsc *pdsc) /* Get intr_info struct array for tracking */ pdsc->intr_info = kcalloc(nintrs, sizeof(*pdsc->intr_info), GFP_KERNEL); - if (!pdsc->intr_info) { - err = -ENOMEM; - goto err_out; - } + if (!pdsc->intr_info) + return -ENOMEM; err = pci_alloc_irq_vectors(pdsc->pdev, nintrs, nintrs, PCI_IRQ_MSIX); if (err != nintrs) { diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index e9948ea5bbcd..54864f27c87a 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -111,7 +111,8 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, mutex_lock(&pdsc->devcmd_lock); err = pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2); - memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list)); + if (!err) + memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list)); mutex_unlock(&pdsc->devcmd_lock); if (err && err != -EIO) return err; diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c index 90a811f3878a..fa626719e68d 100644 --- a/drivers/net/ethernet/amd/pds_core/fw.c +++ b/drivers/net/ethernet/amd/pds_core/fw.c @@ -107,6 +107,9 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw, dev_info(pdsc->dev, "Installing firmware\n"); + if (!pdsc->cmd_regs) + return -ENXIO; + dl = priv_to_devlink(pdsc); devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0); diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 3080898d7b95..ab6133e7db42 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -37,9 +37,15 @@ static void pdsc_unmap_bars(struct pdsc *pdsc) struct pdsc_dev_bar *bars = pdsc->bars; unsigned int i; + pdsc->info_regs = NULL; + pdsc->cmd_regs = NULL; + pdsc->intr_status = NULL; + pdsc->intr_ctrl = NULL; + for (i = 0; i < PDS_CORE_BARS_MAX; i++) { if (bars[i].vaddr) pci_iounmap(pdsc->pdev, bars[i].vaddr); + bars[i].vaddr = NULL; } } @@ -293,7 +299,7 @@ err_out_stop: err_out_teardown: pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); err_out_unmap_bars: - del_timer_sync(&pdsc->wdtimer); + timer_shutdown_sync(&pdsc->wdtimer); if (pdsc->wq) destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); @@ -420,7 +426,7 @@ static void pdsc_remove(struct pci_dev *pdev) */ pdsc_sriov_configure(pdev, 0); - del_timer_sync(&pdsc->wdtimer); + timer_shutdown_sync(&pdsc->wdtimer); if (pdsc->wq) destroy_workqueue(pdsc->wq); @@ -433,7 +439,6 @@ static void pdsc_remove(struct pci_dev *pdev) mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); - pci_free_irq_vectors(pdev); pdsc_unmap_bars(pdsc); pci_release_regions(pdev); } @@ -445,19 +450,47 @@ static void pdsc_remove(struct pci_dev *pdev) devlink_free(dl); } -void pdsc_reset_prepare(struct pci_dev *pdev) +static void pdsc_stop_health_thread(struct pdsc *pdsc) +{ + if (pdsc->pdev->is_virtfn) + return; + + timer_shutdown_sync(&pdsc->wdtimer); + if (pdsc->health_work.func) + cancel_work_sync(&pdsc->health_work); +} + +static void pdsc_restart_health_thread(struct pdsc *pdsc) +{ + if (pdsc->pdev->is_virtfn) + return; + + timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0); + mod_timer(&pdsc->wdtimer, jiffies + 1); +} + +static void pdsc_reset_prepare(struct pci_dev *pdev) { struct pdsc *pdsc = pci_get_drvdata(pdev); + pdsc_stop_health_thread(pdsc); pdsc_fw_down(pdsc); - pci_free_irq_vectors(pdev); + if (pdev->is_virtfn) { + struct pdsc *pf; + + pf = pdsc_get_pf_struct(pdsc->pdev); + if (!IS_ERR(pf)) + pdsc_auxbus_dev_del(pdsc, pf); + } + pdsc_unmap_bars(pdsc); pci_release_regions(pdev); - pci_disable_device(pdev); + if (pci_is_enabled(pdev)) + pci_disable_device(pdev); } -void pdsc_reset_done(struct pci_dev *pdev) +static void pdsc_reset_done(struct pci_dev *pdev) { struct pdsc *pdsc = pci_get_drvdata(pdev); struct device *dev = pdsc->dev; @@ -486,12 +519,44 @@ void pdsc_reset_done(struct pci_dev *pdev) } pdsc_fw_up(pdsc); + pdsc_restart_health_thread(pdsc); + + if (pdev->is_virtfn) { + struct pdsc *pf; + + pf = pdsc_get_pf_struct(pdsc->pdev); + if (!IS_ERR(pf)) + pdsc_auxbus_dev_add(pdsc, pf); + } +} + +static pci_ers_result_t pdsc_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t error) +{ + if (error == pci_channel_io_frozen) { + pdsc_reset_prepare(pdev); + return PCI_ERS_RESULT_NEED_RESET; + } + + return PCI_ERS_RESULT_NONE; +} + +static void pdsc_pci_error_resume(struct pci_dev *pdev) +{ + struct pdsc *pdsc = pci_get_drvdata(pdev); + + if (test_bit(PDSC_S_FW_DEAD, &pdsc->state)) + pci_reset_function_locked(pdev); } static const struct pci_error_handlers pdsc_err_handler = { /* FLR handling */ .reset_prepare = pdsc_reset_prepare, .reset_done = pdsc_reset_done, + + /* AER handling */ + .error_detected = pdsc_pci_error_detected, + .resume = pdsc_pci_error_resume, }; static struct pci_driver pdsc_driver = { diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c index 18a6c8d99fa0..a2606ee3b0a5 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c @@ -15,6 +15,7 @@ #include "aq_macsec.h" #include "aq_main.h" +#include <linux/linkmode.h> #include <linux/ptp_clock_kernel.h> static void aq_ethtool_get_regs(struct net_device *ndev, @@ -681,23 +682,19 @@ static int aq_ethtool_get_ts_info(struct net_device *ndev, return 0; } -static u32 eee_mask_to_ethtool_mask(u32 speed) +static void eee_mask_to_ethtool_mask(unsigned long *mode, u32 speed) { - u32 rate = 0; - if (speed & AQ_NIC_RATE_EEE_10G) - rate |= SUPPORTED_10000baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode); if (speed & AQ_NIC_RATE_EEE_1G) - rate |= SUPPORTED_1000baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode); if (speed & AQ_NIC_RATE_EEE_100M) - rate |= SUPPORTED_100baseT_Full; - - return rate; + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode); } -static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee) +static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_keee *eee) { struct aq_nic_s *aq_nic = netdev_priv(ndev); u32 rate, supported_rates; @@ -713,14 +710,14 @@ static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee) if (err < 0) return err; - eee->supported = eee_mask_to_ethtool_mask(supported_rates); + eee_mask_to_ethtool_mask(eee->supported, supported_rates); if (aq_nic->aq_nic_cfg.eee_speeds) - eee->advertised = eee->supported; + linkmode_copy(eee->advertised, eee->supported); - eee->lp_advertised = eee_mask_to_ethtool_mask(rate); + eee_mask_to_ethtool_mask(eee->lp_advertised, rate); - eee->eee_enabled = !!eee->advertised; + eee->eee_enabled = !linkmode_empty(eee->advertised); eee->tx_lpi_enabled = eee->eee_enabled; if ((supported_rates & rate) & AQ_NIC_RATE_EEE_MSK) @@ -729,7 +726,7 @@ static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee) return 0; } -static int aq_ethtool_set_eee(struct net_device *ndev, struct ethtool_eee *eee) +static int aq_ethtool_set_eee(struct net_device *ndev, struct ethtool_keee *eee) { struct aq_nic_s *aq_nic = netdev_priv(ndev); u32 rate, supported_rates; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c index abd4832e4ed2..5acb3e16b567 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c @@ -993,7 +993,7 @@ int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic) return 0; err_exit_hwts_rx: - aq_ring_free(&aq_ptp->hwts_rx); + aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); err_exit_ptp_rx: aq_ring_free(&aq_ptp->ptp_rx); err_exit_ptp_tx: @@ -1011,7 +1011,7 @@ void aq_ptp_ring_free(struct aq_nic_s *aq_nic) aq_ring_free(&aq_ptp->ptp_tx); aq_ring_free(&aq_ptp->ptp_rx); - aq_ring_free(&aq_ptp->hwts_rx); + aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); aq_ptp_skb_ring_release(&aq_ptp->skb_ring); } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index cda8597b4e14..f7433abd6591 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -919,6 +919,19 @@ void aq_ring_free(struct aq_ring_s *self) } } +void aq_ring_hwts_rx_free(struct aq_ring_s *self) +{ + if (!self) + return; + + if (self->dx_ring) { + dma_free_coherent(aq_nic_get_dev(self->aq_nic), + self->size * self->dx_size + AQ_CFG_RXDS_DEF, + self->dx_ring, self->dx_ring_pa); + self->dx_ring = NULL; + } +} + unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data) { unsigned int count; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index 52847310740a..d627ace850ff 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -210,6 +210,7 @@ int aq_ring_rx_fill(struct aq_ring_s *self); int aq_ring_hwts_rx_alloc(struct aq_ring_s *self, struct aq_nic_s *aq_nic, unsigned int idx, unsigned int size, unsigned int dx_size); +void aq_ring_hwts_rx_free(struct aq_ring_s *self); void aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic); unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data); diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.h b/drivers/net/ethernet/broadcom/asp2/bcmasp.h index ec90add6b03e..312bf9b6576e 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.h +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.h @@ -337,7 +337,7 @@ struct bcmasp_intf { int wol_irq; unsigned int wol_irq_enabled:1; - struct ethtool_eee eee; + struct ethtool_keee eee; }; #define NUM_NET_FILTERS 32 diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c index ce6a3d56fb23..484fc2b5626f 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c @@ -360,29 +360,26 @@ void bcmasp_eee_enable_set(struct bcmasp_intf *intf, bool enable) umac_wl(intf, reg, UMC_EEE_CTRL); intf->eee.eee_enabled = enable; - intf->eee.eee_active = enable; } -static int bcmasp_get_eee(struct net_device *dev, struct ethtool_eee *e) +static int bcmasp_get_eee(struct net_device *dev, struct ethtool_keee *e) { struct bcmasp_intf *intf = netdev_priv(dev); - struct ethtool_eee *p = &intf->eee; + struct ethtool_keee *p = &intf->eee; if (!dev->phydev) return -ENODEV; - e->eee_enabled = p->eee_enabled; - e->eee_active = p->eee_active; e->tx_lpi_enabled = p->tx_lpi_enabled; e->tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); return phy_ethtool_get_eee(dev->phydev, e); } -static int bcmasp_set_eee(struct net_device *dev, struct ethtool_eee *e) +static int bcmasp_set_eee(struct net_device *dev, struct ethtool_keee *e) { struct bcmasp_intf *intf = netdev_priv(dev); - struct ethtool_eee *p = &intf->eee; + struct ethtool_keee *p = &intf->eee; int ret; if (!dev->phydev) @@ -399,7 +396,6 @@ static int bcmasp_set_eee(struct net_device *dev, struct ethtool_eee *e) } umac_wl(intf, e->tx_lpi_timer, UMC_EEE_LPI_TIMER); - intf->eee.eee_active = ret >= 0; intf->eee.tx_lpi_enabled = e->tx_lpi_enabled; bcmasp_eee_enable_set(intf, true); } diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c index 53e542881255..0420f17e53a9 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c @@ -607,6 +607,7 @@ static void bcmasp_adj_link(struct net_device *dev) struct phy_device *phydev = dev->phydev; u32 cmd_bits = 0, reg; int changed = 0; + bool active; if (intf->old_link != phydev->link) { changed = 1; @@ -658,8 +659,8 @@ static void bcmasp_adj_link(struct net_device *dev) reg |= cmd_bits; umac_wl(intf, reg, UMC_CMD); - intf->eee.eee_active = phy_init_eee(phydev, 0) >= 0; - bcmasp_eee_enable_set(intf, intf->eee.eee_active); + active = phy_init_eee(phydev, 0) >= 0; + bcmasp_eee_enable_set(intf, active); } reg = rgmii_rl(intf, RGMII_OOB_CNTRL); @@ -684,6 +685,8 @@ static int bcmasp_init_rx(struct bcmasp_intf *intf) intf->rx_buf_order = get_order(RING_BUFFER_SIZE); buffer_pg = alloc_pages(GFP_KERNEL, intf->rx_buf_order); + if (!buffer_pg) + return -ENOMEM; dma = dma_map_page(kdev, buffer_pg, 0, RING_BUFFER_SIZE, DMA_FROM_DEVICE); @@ -1092,6 +1095,7 @@ static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) return 0; err_reclaim_tx: + netif_napi_del(&intf->tx_napi); bcmasp_reclaim_free_all_tx(intf); err_phy_disconnect: if (phydev) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index e9c1e1bb5580..528441b28c4e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -147,10 +147,11 @@ void bnx2x_fill_fw_str(struct bnx2x *bp, char *buf, size_t buf_len) phy_fw_ver[0] = '\0'; bnx2x_get_ext_phy_fw_version(&bp->link_params, - phy_fw_ver, PHY_FW_VER_LEN); - strscpy(buf, bp->fw_ver, buf_len); - snprintf(buf + strlen(bp->fw_ver), 32 - strlen(bp->fw_ver), - "bc %d.%d.%d%s%s", + phy_fw_ver, sizeof(phy_fw_ver)); + /* This may become truncated. */ + scnprintf(buf, buf_len, + "%sbc %d.%d.%d%s%s", + bp->fw_ver, (bp->common.bc_ver & 0xff0000) >> 16, (bp->common.bc_ver & 0xff00) >> 8, (bp->common.bc_ver & 0xff), diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 81d232e6d05f..58956ed8f531 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1132,7 +1132,7 @@ static void bnx2x_get_drvinfo(struct net_device *dev, } memset(version, 0, sizeof(version)); - bnx2x_fill_fw_str(bp, version, ETHTOOL_FWVERS_LEN); + bnx2x_fill_fw_str(bp, version, sizeof(version)); strlcat(info->fw_version, version, sizeof(info->fw_version)); strscpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); @@ -2081,34 +2081,31 @@ static const char bnx2x_private_arr[BNX2X_PRI_FLAG_LEN][ETH_GSTRING_LEN] = { "Storage only interface" }; -static u32 bnx2x_eee_to_adv(u32 eee_adv) +static void bnx2x_eee_to_linkmode(unsigned long *mode, u32 eee_adv) { - u32 modes = 0; - if (eee_adv & SHMEM_EEE_100M_ADV) - modes |= ADVERTISED_100baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode); if (eee_adv & SHMEM_EEE_1G_ADV) - modes |= ADVERTISED_1000baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode); if (eee_adv & SHMEM_EEE_10G_ADV) - modes |= ADVERTISED_10000baseT_Full; - - return modes; + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode); } -static u32 bnx2x_adv_to_eee(u32 modes, u32 shift) +static u32 bnx2x_linkmode_to_eee(const unsigned long *mode, u32 shift) { u32 eee_adv = 0; - if (modes & ADVERTISED_100baseT_Full) + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode)) eee_adv |= SHMEM_EEE_100M_ADV; - if (modes & ADVERTISED_1000baseT_Full) + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode)) eee_adv |= SHMEM_EEE_1G_ADV; - if (modes & ADVERTISED_10000baseT_Full) + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode)) eee_adv |= SHMEM_EEE_10G_ADV; return eee_adv << shift; } -static int bnx2x_get_eee(struct net_device *dev, struct ethtool_eee *edata) +static int bnx2x_get_eee(struct net_device *dev, struct ethtool_keee *edata) { struct bnx2x *bp = netdev_priv(dev); u32 eee_cfg; @@ -2120,16 +2117,17 @@ static int bnx2x_get_eee(struct net_device *dev, struct ethtool_eee *edata) eee_cfg = bp->link_vars.eee_status; - edata->supported = - bnx2x_eee_to_adv((eee_cfg & SHMEM_EEE_SUPPORTED_MASK) >> - SHMEM_EEE_SUPPORTED_SHIFT); + bnx2x_eee_to_linkmode(edata->supported, + (eee_cfg & SHMEM_EEE_SUPPORTED_MASK) >> + SHMEM_EEE_SUPPORTED_SHIFT); + + bnx2x_eee_to_linkmode(edata->advertised, + (eee_cfg & SHMEM_EEE_ADV_STATUS_MASK) >> + SHMEM_EEE_ADV_STATUS_SHIFT); - edata->advertised = - bnx2x_eee_to_adv((eee_cfg & SHMEM_EEE_ADV_STATUS_MASK) >> - SHMEM_EEE_ADV_STATUS_SHIFT); - edata->lp_advertised = - bnx2x_eee_to_adv((eee_cfg & SHMEM_EEE_LP_ADV_STATUS_MASK) >> - SHMEM_EEE_LP_ADV_STATUS_SHIFT); + bnx2x_eee_to_linkmode(edata->lp_advertised, + (eee_cfg & SHMEM_EEE_LP_ADV_STATUS_MASK) >> + SHMEM_EEE_LP_ADV_STATUS_SHIFT); /* SHMEM value is in 16u units --> Convert to 1u units. */ edata->tx_lpi_timer = (eee_cfg & SHMEM_EEE_TIMER_MASK) << 4; @@ -2141,7 +2139,7 @@ static int bnx2x_get_eee(struct net_device *dev, struct ethtool_eee *edata) return 0; } -static int bnx2x_set_eee(struct net_device *dev, struct ethtool_eee *edata) +static int bnx2x_set_eee(struct net_device *dev, struct ethtool_keee *edata) { struct bnx2x *bp = netdev_priv(dev); u32 eee_cfg; @@ -2162,8 +2160,8 @@ static int bnx2x_set_eee(struct net_device *dev, struct ethtool_eee *edata) return -EOPNOTSUPP; } - advertised = bnx2x_adv_to_eee(edata->advertised, - SHMEM_EEE_ADV_STATUS_SHIFT); + advertised = bnx2x_linkmode_to_eee(edata->advertised, + SHMEM_EEE_ADV_STATUS_SHIFT); if ((advertised != (eee_cfg & SHMEM_EEE_ADV_STATUS_MASK))) { DP(BNX2X_MSG_ETHTOOL, "Direct manipulation of EEE advertisement is not supported\n"); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 02808513ffe4..ea310057fe3a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -6163,8 +6163,8 @@ static void bnx2x_link_int_ack(struct link_params *params, static int bnx2x_null_format_ver(u32 spirom_ver, u8 *str, u16 *len) { - str[0] = '\0'; - (*len)--; + if (*len) + str[0] = '\0'; return 0; } @@ -6173,7 +6173,7 @@ static int bnx2x_format_ver(u32 num, u8 *str, u16 *len) u16 ret; if (*len < 10) { - /* Need more than 10chars for this format */ + /* Need more than 10 chars for this format */ bnx2x_null_format_ver(num, str, len); return -EINVAL; } @@ -6188,8 +6188,8 @@ static int bnx2x_3_seq_format_ver(u32 num, u8 *str, u16 *len) { u16 ret; - if (*len < 10) { - /* Need more than 10chars for this format */ + if (*len < 9) { + /* Need more than 9 chars for this format */ bnx2x_null_format_ver(num, str, len); return -EINVAL; } @@ -6208,7 +6208,7 @@ int bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 *version, int status = 0; u8 *ver_p = version; u16 remain_len = len; - if (version == NULL || params == NULL) + if (version == NULL || params == NULL || len == 0) return -EINVAL; bp = params->bp; @@ -11546,7 +11546,7 @@ static int bnx2x_7101_format_ver(u32 spirom_ver, u8 *str, u16 *len) str[2] = (spirom_ver & 0xFF0000) >> 16; str[3] = (spirom_ver & 0xFF000000) >> 24; str[4] = '\0'; - *len -= 5; + *len -= 4; return 0; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 39845d556baf..6f415425dc14 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -246,6 +246,49 @@ static const u16 bnxt_async_events_arr[] = { static struct workqueue_struct *bnxt_pf_wq; +#define BNXT_IPV6_MASK_ALL {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}} +#define BNXT_IPV6_MASK_NONE {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}} + +const struct bnxt_flow_masks BNXT_FLOW_MASK_NONE = { + .ports = { + .src = 0, + .dst = 0, + }, + .addrs = { + .v6addrs = { + .src = BNXT_IPV6_MASK_NONE, + .dst = BNXT_IPV6_MASK_NONE, + }, + }, +}; + +const struct bnxt_flow_masks BNXT_FLOW_IPV6_MASK_ALL = { + .ports = { + .src = cpu_to_be16(0xffff), + .dst = cpu_to_be16(0xffff), + }, + .addrs = { + .v6addrs = { + .src = BNXT_IPV6_MASK_ALL, + .dst = BNXT_IPV6_MASK_ALL, + }, + }, +}; + +const struct bnxt_flow_masks BNXT_FLOW_IPV4_MASK_ALL = { + .ports = { + .src = cpu_to_be16(0xffff), + .dst = cpu_to_be16(0xffff), + }, + .addrs = { + .v4addrs = { + .src = cpu_to_be32(0xffffffff), + .dst = cpu_to_be32(0xffffffff), + }, + }, +}; + static bool bnxt_vf_pciid(enum board_idx idx) { return (idx == NETXTREME_C_VF || idx == NETXTREME_E_VF || @@ -4203,9 +4246,23 @@ static void bnxt_init_vnics(struct bnxt *bp) u8 *key = (void *)vnic->rss_hash_key; int k; + if (!bp->rss_hash_key_valid && + !bp->rss_hash_key_updated) { + get_random_bytes(bp->rss_hash_key, + HW_HASH_KEY_SIZE); + bp->rss_hash_key_updated = true; + } + + memcpy(vnic->rss_hash_key, bp->rss_hash_key, + HW_HASH_KEY_SIZE); + + if (!bp->rss_hash_key_updated) + continue; + + bp->rss_hash_key_updated = false; + bp->rss_hash_key_valid = true; + bp->toeplitz_prefix = 0; - get_random_bytes(vnic->rss_hash_key, - HW_HASH_KEY_SIZE); for (k = 0; k < 8; k++) { bp->toeplitz_prefix <<= 8; bp->toeplitz_prefix |= key[k]; @@ -4798,6 +4855,44 @@ static void bnxt_clear_ring_indices(struct bnxt *bp) } } +void bnxt_insert_usr_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr) +{ + u8 type = fltr->type, flags = fltr->flags; + + INIT_LIST_HEAD(&fltr->list); + if ((type == BNXT_FLTR_TYPE_L2 && flags & BNXT_ACT_RING_DST) || + (type == BNXT_FLTR_TYPE_NTUPLE && flags & BNXT_ACT_NO_AGING)) + list_add_tail(&fltr->list, &bp->usr_fltr_list); +} + +void bnxt_del_one_usr_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr) +{ + if (!list_empty(&fltr->list)) + list_del_init(&fltr->list); +} + +void bnxt_clear_usr_fltrs(struct bnxt *bp, bool all) +{ + struct bnxt_filter_base *usr_fltr, *tmp; + + list_for_each_entry_safe(usr_fltr, tmp, &bp->usr_fltr_list, list) { + if (!all && usr_fltr->type == BNXT_FLTR_TYPE_L2) + continue; + bnxt_del_one_usr_fltr(bp, usr_fltr); + } +} + +static void bnxt_del_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr) +{ + hlist_del(&fltr->hash); + bnxt_del_one_usr_fltr(bp, fltr); + if (fltr->flags) { + clear_bit(fltr->sw_id, bp->ntp_fltr_bmap); + bp->ntp_fltr_count--; + } + kfree(fltr); +} + static void bnxt_free_ntp_fltrs(struct bnxt *bp, bool all) { int i; @@ -4813,12 +4908,10 @@ static void bnxt_free_ntp_fltrs(struct bnxt *bp, bool all) head = &bp->ntp_fltr_hash_tbl[i]; hlist_for_each_entry_safe(fltr, tmp, head, base.hash) { bnxt_del_l2_filter(bp, fltr->l2_fltr); - if (!all && (fltr->base.flags & BNXT_ACT_FUNC_DST)) + if (!all && ((fltr->base.flags & BNXT_ACT_FUNC_DST) || + !list_empty(&fltr->base.list))) continue; - hlist_del(&fltr->base.hash); - clear_bit(fltr->base.sw_id, bp->ntp_fltr_bmap); - bp->ntp_fltr_count--; - kfree(fltr); + bnxt_del_fltr(bp, &fltr->base); } } if (!all) @@ -4840,7 +4933,7 @@ static int bnxt_alloc_ntp_fltrs(struct bnxt *bp) INIT_HLIST_HEAD(&bp->ntp_fltr_hash_tbl[i]); bp->ntp_fltr_count = 0; - bp->ntp_fltr_bmap = bitmap_zalloc(BNXT_MAX_FLTR, GFP_KERNEL); + bp->ntp_fltr_bmap = bitmap_zalloc(bp->max_fltr, GFP_KERNEL); if (!bp->ntp_fltr_bmap) rc = -ENOMEM; @@ -4859,14 +4952,10 @@ static void bnxt_free_l2_filters(struct bnxt *bp, bool all) head = &bp->l2_fltr_hash_tbl[i]; hlist_for_each_entry_safe(fltr, tmp, head, base.hash) { - if (!all && (fltr->base.flags & BNXT_ACT_FUNC_DST)) + if (!all && ((fltr->base.flags & BNXT_ACT_FUNC_DST) || + !list_empty(&fltr->base.list))) continue; - hlist_del(&fltr->base.hash); - if (fltr->base.flags) { - clear_bit(fltr->base.sw_id, bp->ntp_fltr_bmap); - bp->ntp_fltr_count--; - } - kfree(fltr); + bnxt_del_fltr(bp, &fltr->base); } } } @@ -5342,6 +5431,7 @@ void bnxt_del_l2_filter(struct bnxt *bp, struct bnxt_l2_filter *fltr) return; } hlist_del_rcu(&fltr->base.hash); + bnxt_del_one_usr_fltr(bp, &fltr->base); if (fltr->base.flags) { clear_bit(fltr->base.sw_id, bp->ntp_fltr_bmap); bp->ntp_fltr_count--; @@ -5480,13 +5570,15 @@ static int bnxt_init_l2_filter(struct bnxt *bp, struct bnxt_l2_filter *fltr, int bit_id; bit_id = bitmap_find_free_region(bp->ntp_fltr_bmap, - BNXT_MAX_FLTR, 0); + bp->max_fltr, 0); if (bit_id < 0) return -ENOMEM; fltr->base.sw_id = (u16)bit_id; + bp->ntp_fltr_count++; } head = &bp->l2_fltr_hash_tbl[idx]; hlist_add_head_rcu(&fltr->base.hash, head); + bnxt_insert_usr_fltr(bp, &fltr->base); set_bit(BNXT_FLTR_INSERTED, &fltr->base.state); atomic_set(&fltr->refcnt, 1); return 0; @@ -5519,6 +5611,40 @@ static struct bnxt_l2_filter *bnxt_alloc_l2_filter(struct bnxt *bp, return fltr; } +struct bnxt_l2_filter *bnxt_alloc_new_l2_filter(struct bnxt *bp, + struct bnxt_l2_key *key, + u16 flags) +{ + struct bnxt_l2_filter *fltr; + u32 idx; + int rc; + + idx = jhash2(&key->filter_key, BNXT_L2_KEY_SIZE, bp->hash_seed) & + BNXT_L2_FLTR_HASH_MASK; + spin_lock_bh(&bp->ntp_fltr_lock); + fltr = __bnxt_lookup_l2_filter(bp, key, idx); + if (fltr) { + fltr = ERR_PTR(-EEXIST); + goto l2_filter_exit; + } + fltr = kzalloc(sizeof(*fltr), GFP_ATOMIC); + if (!fltr) { + fltr = ERR_PTR(-ENOMEM); + goto l2_filter_exit; + } + fltr->base.flags = flags; + rc = bnxt_init_l2_filter(bp, fltr, key, idx); + if (rc) { + spin_unlock_bh(&bp->ntp_fltr_lock); + bnxt_del_l2_filter(bp, fltr); + return ERR_PTR(rc); + } + +l2_filter_exit: + spin_unlock_bh(&bp->ntp_fltr_lock); + return fltr; +} + static u16 bnxt_vf_target_id(struct bnxt_pf_info *pf, u16 vf_idx) { #ifdef CONFIG_BNXT_SRIOV @@ -5655,6 +5781,7 @@ int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp, { struct hwrm_cfa_ntuple_filter_alloc_output *resp; struct hwrm_cfa_ntuple_filter_alloc_input *req; + struct bnxt_flow_masks *masks = &fltr->fmasks; struct flow_keys *keys = &fltr->fkeys; struct bnxt_l2_filter *l2_fltr; struct bnxt_vnic_info *vnic; @@ -5668,8 +5795,9 @@ int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp, l2_fltr = fltr->l2_fltr; req->l2_filter_id = l2_fltr->base.filter_id; - - if (bp->fw_cap & BNXT_FW_CAP_CFA_RFS_RING_TBL_IDX_V2) { + if (fltr->base.flags & BNXT_ACT_DROP) { + flags = CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DROP; + } else if (bp->fw_cap & BNXT_FW_CAP_CFA_RFS_RING_TBL_IDX_V2) { flags = CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DEST_RFS_RING_IDX; req->dst_id = cpu_to_le16(fltr->base.rxq); } else { @@ -5687,25 +5815,15 @@ int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp, req->ethertype = htons(ETH_P_IPV6); req->ip_addr_type = CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6; - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) { - *(struct in6_addr *)&req->src_ipaddr[0] = - keys->addrs.v6addrs.src; - bnxt_fill_ipv6_mask(req->src_ipaddr_mask); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) { - *(struct in6_addr *)&req->dst_ipaddr[0] = - keys->addrs.v6addrs.dst; - bnxt_fill_ipv6_mask(req->dst_ipaddr_mask); - } + *(struct in6_addr *)&req->src_ipaddr[0] = keys->addrs.v6addrs.src; + *(struct in6_addr *)&req->src_ipaddr_mask[0] = masks->addrs.v6addrs.src; + *(struct in6_addr *)&req->dst_ipaddr[0] = keys->addrs.v6addrs.dst; + *(struct in6_addr *)&req->dst_ipaddr_mask[0] = masks->addrs.v6addrs.dst; } else { - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) { - req->src_ipaddr[0] = keys->addrs.v4addrs.src; - req->src_ipaddr_mask[0] = cpu_to_be32(0xffffffff); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) { - req->dst_ipaddr[0] = keys->addrs.v4addrs.dst; - req->dst_ipaddr_mask[0] = cpu_to_be32(0xffffffff); - } + req->src_ipaddr[0] = keys->addrs.v4addrs.src; + req->src_ipaddr_mask[0] = masks->addrs.v4addrs.src; + req->dst_ipaddr[0] = keys->addrs.v4addrs.dst; + req->dst_ipaddr_mask[0] = masks->addrs.v4addrs.dst; } if (keys->control.flags & FLOW_DIS_ENCAPSULATION) { req->enables |= cpu_to_le32(BNXT_NTP_TUNNEL_FLTR_FLAG); @@ -5713,14 +5831,10 @@ int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp, CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL; } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_PORT) { - req->src_port = keys->ports.src; - req->src_port_mask = cpu_to_be16(0xffff); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_PORT) { - req->dst_port = keys->ports.dst; - req->dst_port_mask = cpu_to_be16(0xffff); - } + req->src_port = keys->ports.src; + req->src_port_mask = masks->ports.src; + req->dst_port = keys->ports.dst; + req->dst_port_mask = masks->ports.dst; resp = hwrm_req_hold(bp, req); rc = hwrm_req_send(bp, req); @@ -5985,10 +6099,13 @@ static void __bnxt_hwrm_vnic_set_rss(struct bnxt *bp, struct hwrm_vnic_rss_cfg_input *req, struct bnxt_vnic_info *vnic) { - if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) + if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { bnxt_fill_hw_rss_tbl_p5(bp, vnic); - else + if (bp->flags & BNXT_FLAG_CHIP_P7) + req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT; + } else { bnxt_fill_hw_rss_tbl(bp, vnic); + } if (bp->rss_hash_delta) { req->hash_type = cpu_to_le32(bp->rss_hash_delta); @@ -6351,6 +6468,14 @@ static int bnxt_hwrm_vnic_qcaps(struct bnxt *bp) } if (flags & VNIC_QCAPS_RESP_FLAGS_HW_TUNNEL_TPA_CAP) bp->fw_cap |= BNXT_FW_CAP_VNIC_TUNNEL_TPA; + if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_AH_SPI_IPV4_CAP) + bp->rss_cap |= BNXT_RSS_CAP_AH_V4_RSS_CAP; + if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_AH_SPI_IPV6_CAP) + bp->rss_cap |= BNXT_RSS_CAP_AH_V6_RSS_CAP; + if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_ESP_SPI_IPV4_CAP) + bp->rss_cap |= BNXT_RSS_CAP_ESP_V4_RSS_CAP; + if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_ESP_SPI_IPV6_CAP) + bp->rss_cap |= BNXT_RSS_CAP_ESP_V6_RSS_CAP; } hwrm_req_drop(bp, req); return rc; @@ -8709,6 +8834,13 @@ static int __bnxt_hwrm_func_qcaps(struct bnxt *bp) hw_resc->max_vnics = le16_to_cpu(resp->max_vnics); hw_resc->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx); + hw_resc->max_encap_records = le32_to_cpu(resp->max_encap_records); + hw_resc->max_decap_records = le32_to_cpu(resp->max_decap_records); + hw_resc->max_tx_em_flows = le32_to_cpu(resp->max_tx_em_flows); + hw_resc->max_tx_wm_flows = le32_to_cpu(resp->max_tx_wm_flows); + hw_resc->max_rx_em_flows = le32_to_cpu(resp->max_rx_em_flows); + hw_resc->max_rx_wm_flows = le32_to_cpu(resp->max_rx_wm_flows); + if (BNXT_PF(bp)) { struct bnxt_pf_info *pf = &bp->pf; @@ -8717,12 +8849,6 @@ static int __bnxt_hwrm_func_qcaps(struct bnxt *bp) memcpy(pf->mac_addr, resp->mac_address, ETH_ALEN); pf->first_vf_id = le16_to_cpu(resp->first_vf_id); pf->max_vfs = le16_to_cpu(resp->max_vfs); - pf->max_encap_records = le32_to_cpu(resp->max_encap_records); - pf->max_decap_records = le32_to_cpu(resp->max_decap_records); - pf->max_tx_em_flows = le32_to_cpu(resp->max_tx_em_flows); - pf->max_tx_wm_flows = le32_to_cpu(resp->max_tx_wm_flows); - pf->max_rx_em_flows = le32_to_cpu(resp->max_rx_em_flows); - pf->max_rx_wm_flows = le32_to_cpu(resp->max_rx_wm_flows); bp->flags &= ~BNXT_FLAG_WOL_CAP; if (flags & FUNC_QCAPS_RESP_FLAGS_WOL_MAGICPKT_SUPPORTED) bp->flags |= BNXT_FLAG_WOL_CAP; @@ -8825,6 +8951,10 @@ static int bnxt_hwrm_cfa_adv_flow_mgnt_qcaps(struct bnxt *bp) CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V2_SUPPORTED) bp->fw_cap |= BNXT_FW_CAP_CFA_RFS_RING_TBL_IDX_V2; + if (flags & + CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_EXT_IP_PROTO_SUPPORTED) + bp->fw_cap |= BNXT_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO; + hwrm_cfa_adv_qcaps_exit: hwrm_req_drop(bp, req); return rc; @@ -10621,10 +10751,10 @@ static int bnxt_hwrm_phy_qcaps(struct bnxt *bp) bp->phy_flags = resp->flags | (le16_to_cpu(resp->flags2) << 8); if (resp->flags & PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED) { - struct ethtool_eee *eee = &bp->eee; + struct ethtool_keee *eee = &bp->eee; u16 fw_speeds = le16_to_cpu(resp->supported_speeds_eee_mode); - eee->supported = _bnxt_fw_to_ethtool_adv_spds(fw_speeds, 0); + _bnxt_fw_to_linkmode(eee->supported, fw_speeds); bp->lpi_tmr_lo = le32_to_cpu(resp->tx_lpi_timer_low) & PORT_PHY_QCAPS_RESP_TX_LPI_TIMER_LOW_MASK; bp->lpi_tmr_hi = le32_to_cpu(resp->valid_tx_lpi_timer_high) & @@ -10766,7 +10896,7 @@ int bnxt_update_link(struct bnxt *bp, bool chng_link_state) link_info->module_status = resp->module_status; if (bp->phy_flags & BNXT_PHY_FL_EEE_CAP) { - struct ethtool_eee *eee = &bp->eee; + struct ethtool_keee *eee = &bp->eee; u16 fw_speeds; eee->eee_active = 0; @@ -10775,8 +10905,7 @@ int bnxt_update_link(struct bnxt *bp, bool chng_link_state) eee->eee_active = 1; fw_speeds = le16_to_cpu( resp->link_partner_adv_eee_link_speed_mask); - eee->lp_advertised = - _bnxt_fw_to_ethtool_adv_spds(fw_speeds, 0); + _bnxt_fw_to_linkmode(eee->lp_advertised, fw_speeds); } /* Pull initial EEE config */ @@ -10786,8 +10915,7 @@ int bnxt_update_link(struct bnxt *bp, bool chng_link_state) eee->eee_enabled = 1; fw_speeds = le16_to_cpu(resp->adv_eee_link_speed_mask); - eee->advertised = - _bnxt_fw_to_ethtool_adv_spds(fw_speeds, 0); + _bnxt_fw_to_linkmode(eee->advertised, fw_speeds); if (resp->eee_config_phy_addr & PORT_PHY_QCFG_RESP_EEE_CONFIG_EEE_TX_LPI) { @@ -10957,7 +11085,7 @@ int bnxt_hwrm_set_pause(struct bnxt *bp) static void bnxt_hwrm_set_eee(struct bnxt *bp, struct hwrm_port_phy_cfg_input *req) { - struct ethtool_eee *eee = &bp->eee; + struct ethtool_keee *eee = &bp->eee; if (eee->eee_enabled) { u16 eee_speeds; @@ -11322,22 +11450,25 @@ static void bnxt_get_wol_settings(struct bnxt *bp) static bool bnxt_eee_config_ok(struct bnxt *bp) { - struct ethtool_eee *eee = &bp->eee; + struct ethtool_keee *eee = &bp->eee; struct bnxt_link_info *link_info = &bp->link_info; if (!(bp->phy_flags & BNXT_PHY_FL_EEE_CAP)) return true; if (eee->eee_enabled) { - u32 advertising = - _bnxt_fw_to_ethtool_adv_spds(link_info->advertising, 0); + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp); + + _bnxt_fw_to_linkmode(advertising, link_info->advertising); if (!(link_info->autoneg & BNXT_AUTONEG_SPEED)) { eee->eee_enabled = 0; return false; } - if (eee->advertised & ~advertising) { - eee->advertised = advertising & eee->supported; + if (linkmode_andnot(tmp, eee->advertised, advertising)) { + linkmode_and(eee->advertised, advertising, + eee->supported); return false; } } @@ -11442,6 +11573,42 @@ static int bnxt_reinit_after_abort(struct bnxt *bp) return rc; } +static void bnxt_cfg_one_usr_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr) +{ + struct bnxt_ntuple_filter *ntp_fltr; + struct bnxt_l2_filter *l2_fltr; + + if (list_empty(&fltr->list)) + return; + + if (fltr->type == BNXT_FLTR_TYPE_NTUPLE) { + ntp_fltr = container_of(fltr, struct bnxt_ntuple_filter, base); + l2_fltr = bp->vnic_info[0].l2_filters[0]; + atomic_inc(&l2_fltr->refcnt); + ntp_fltr->l2_fltr = l2_fltr; + if (bnxt_hwrm_cfa_ntuple_filter_alloc(bp, ntp_fltr)) { + bnxt_del_ntp_filter(bp, ntp_fltr); + netdev_err(bp->dev, "restoring previously configured ntuple filter id %d failed\n", + fltr->sw_id); + } + } else if (fltr->type == BNXT_FLTR_TYPE_L2) { + l2_fltr = container_of(fltr, struct bnxt_l2_filter, base); + if (bnxt_hwrm_l2_filter_alloc(bp, l2_fltr)) { + bnxt_del_l2_filter(bp, l2_fltr); + netdev_err(bp->dev, "restoring previously configured l2 filter id %d failed\n", + fltr->sw_id); + } + } +} + +static void bnxt_cfg_usr_fltrs(struct bnxt *bp) +{ + struct bnxt_filter_base *usr_fltr, *tmp; + + list_for_each_entry_safe(usr_fltr, tmp, &bp->usr_fltr_list, list) + bnxt_cfg_one_usr_fltr(bp, usr_fltr); +} + static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) { int rc = 0; @@ -11528,6 +11695,7 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) bnxt_vf_reps_open(bp); bnxt_ptp_init_rtc(bp, true); bnxt_ptp_cfg_tstamp_filters(bp); + bnxt_cfg_usr_fltrs(bp); return 0; open_err_irq: @@ -12269,6 +12437,8 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features) if (features & NETIF_F_NTUPLE) flags |= BNXT_FLAG_RFS; + else + bnxt_clear_usr_fltrs(bp, true); changes = flags ^ bp->flags; if (changes & BNXT_FLAG_TPA) { @@ -13766,6 +13936,7 @@ static int bnxt_change_mac_addr(struct net_device *dev, void *p) return rc; eth_hw_addr_set(dev, addr->sa_data); + bnxt_clear_usr_fltrs(bp, true); if (netif_running(dev)) { bnxt_close_nic(bp, false, false); rc = bnxt_open_nic(bp, false, false); @@ -13899,7 +14070,7 @@ int bnxt_insert_ntp_filter(struct bnxt *bp, struct bnxt_ntuple_filter *fltr, int bit_id; spin_lock_bh(&bp->ntp_fltr_lock); - bit_id = bitmap_find_free_region(bp->ntp_fltr_bmap, BNXT_MAX_FLTR, 0); + bit_id = bitmap_find_free_region(bp->ntp_fltr_bmap, bp->max_fltr, 0); if (bit_id < 0) { spin_unlock_bh(&bp->ntp_fltr_lock); return -ENOMEM; @@ -13911,6 +14082,7 @@ int bnxt_insert_ntp_filter(struct bnxt *bp, struct bnxt_ntuple_filter *fltr, head = &bp->ntp_fltr_hash_tbl[idx]; hlist_add_head_rcu(&fltr->base.hash, head); set_bit(BNXT_FLTR_INSERTED, &fltr->base.state); + bnxt_insert_usr_fltr(bp, &fltr->base); bp->ntp_fltr_count++; spin_unlock_bh(&bp->ntp_fltr_lock); return 0; @@ -13919,45 +14091,39 @@ int bnxt_insert_ntp_filter(struct bnxt *bp, struct bnxt_ntuple_filter *fltr, static bool bnxt_fltr_match(struct bnxt_ntuple_filter *f1, struct bnxt_ntuple_filter *f2) { + struct bnxt_flow_masks *masks1 = &f1->fmasks; + struct bnxt_flow_masks *masks2 = &f2->fmasks; struct flow_keys *keys1 = &f1->fkeys; struct flow_keys *keys2 = &f2->fkeys; - if (f1->ntuple_flags != f2->ntuple_flags) - return false; - if (keys1->basic.n_proto != keys2->basic.n_proto || keys1->basic.ip_proto != keys2->basic.ip_proto) return false; if (keys1->basic.n_proto == htons(ETH_P_IP)) { - if (((f1->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) && - keys1->addrs.v4addrs.src != keys2->addrs.v4addrs.src) || - ((f1->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) && - keys1->addrs.v4addrs.dst != keys2->addrs.v4addrs.dst)) + if (keys1->addrs.v4addrs.src != keys2->addrs.v4addrs.src || + masks1->addrs.v4addrs.src != masks2->addrs.v4addrs.src || + keys1->addrs.v4addrs.dst != keys2->addrs.v4addrs.dst || + masks1->addrs.v4addrs.dst != masks2->addrs.v4addrs.dst) return false; } else { - if (((f1->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) && - memcmp(&keys1->addrs.v6addrs.src, - &keys2->addrs.v6addrs.src, - sizeof(keys1->addrs.v6addrs.src))) || - ((f1->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) && - memcmp(&keys1->addrs.v6addrs.dst, - &keys2->addrs.v6addrs.dst, - sizeof(keys1->addrs.v6addrs.dst)))) + if (!ipv6_addr_equal(&keys1->addrs.v6addrs.src, + &keys2->addrs.v6addrs.src) || + !ipv6_addr_equal(&masks1->addrs.v6addrs.src, + &masks2->addrs.v6addrs.src) || + !ipv6_addr_equal(&keys1->addrs.v6addrs.dst, + &keys2->addrs.v6addrs.dst) || + !ipv6_addr_equal(&masks1->addrs.v6addrs.dst, + &masks2->addrs.v6addrs.dst)) return false; } - if (((f1->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_PORT) && - keys1->ports.src != keys2->ports.src) || - ((f1->ntuple_flags & BNXT_NTUPLE_MATCH_DST_PORT) && - keys1->ports.dst != keys2->ports.dst)) - return false; - - if (keys1->control.flags == keys2->control.flags && - f1->l2_fltr == f2->l2_fltr) - return true; - - return false; + return keys1->ports.src == keys2->ports.src && + masks1->ports.src == masks2->ports.src && + keys1->ports.dst == keys2->ports.dst && + masks1->ports.dst == masks2->ports.dst && + keys1->control.flags == keys2->control.flags && + f1->l2_fltr == f2->l2_fltr; } struct bnxt_ntuple_filter * @@ -14022,10 +14188,13 @@ static int bnxt_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, rc = -EPROTONOSUPPORT; goto err_free; } - if (fkeys->basic.n_proto == htons(ETH_P_IPV6) && - bp->hwrm_spec_code < 0x10601) { - rc = -EPROTONOSUPPORT; - goto err_free; + new_fltr->fmasks = BNXT_FLOW_IPV4_MASK_ALL; + if (fkeys->basic.n_proto == htons(ETH_P_IPV6)) { + if (bp->hwrm_spec_code < 0x10601) { + rc = -EPROTONOSUPPORT; + goto err_free; + } + new_fltr->fmasks = BNXT_FLOW_IPV6_MASK_ALL; } flags = fkeys->control.flags; if (((flags & FLOW_DIS_ENCAPSULATION) && @@ -14033,9 +14202,7 @@ static int bnxt_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, rc = -EPROTONOSUPPORT; goto err_free; } - new_fltr->l2_fltr = l2_fltr; - new_fltr->ntuple_flags = BNXT_NTUPLE_MATCH_ALL; idx = bnxt_get_ntp_filter_idx(bp, fkeys, skb); rcu_read_lock(); @@ -14070,6 +14237,7 @@ void bnxt_del_ntp_filter(struct bnxt *bp, struct bnxt_ntuple_filter *fltr) return; } hlist_del_rcu(&fltr->base.hash); + bnxt_del_one_usr_fltr(bp, &fltr->base); bp->ntp_fltr_count--; spin_unlock_bh(&bp->ntp_fltr_lock); bnxt_del_l2_filter(bp, fltr->l2_fltr); @@ -14669,6 +14837,7 @@ void bnxt_print_device_info(struct bnxt *bp) static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct bnxt_hw_resc *hw_resc; struct net_device *dev; struct bnxt *bp; int rc, max_irqs; @@ -14827,6 +14996,12 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_pci_clean; + hw_resc = &bp->hw_resc; + bp->max_fltr = hw_resc->max_rx_em_flows + hw_resc->max_rx_wm_flows + + BNXT_L2_FLTR_MAX_FLTR; + /* Older firmware may not report these filters properly */ + if (bp->max_fltr < BNXT_MAX_FLTR) + bp->max_fltr = BNXT_MAX_FLTR; bnxt_init_l2_fltr_tbl(bp); bnxt_set_rx_skb_mode(bp, false); bnxt_set_tpa_flags(bp); @@ -14879,6 +15054,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_dl; + INIT_LIST_HEAD(&bp->usr_fltr_list); + rc = register_netdev(dev); if (rc) goto init_err_cleanup; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 47338b48ca20..60bdd0673ec8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1281,6 +1281,12 @@ struct bnxt_hw_resc { u16 max_nqs; u16 max_irqs; u16 resv_irqs; + u32 max_encap_records; + u32 max_decap_records; + u32 max_tx_em_flows; + u32 max_tx_wm_flows; + u32 max_rx_em_flows; + u32 max_rx_wm_flows; }; #if defined(CONFIG_BNXT_SRIOV) @@ -1315,12 +1321,6 @@ struct bnxt_pf_info { u16 active_vfs; u16 registered_vfs; u16 max_vfs; - u32 max_encap_records; - u32 max_decap_records; - u32 max_tx_em_flows; - u32 max_tx_wm_flows; - u32 max_rx_em_flows; - u32 max_rx_wm_flows; unsigned long *vf_event_bmap; u16 hwrm_cmd_req_pages; u8 vf_resv_strategy; @@ -1334,6 +1334,7 @@ struct bnxt_pf_info { struct bnxt_filter_base { struct hlist_node hash; + struct list_head list; __le64 filter_id; u8 type; #define BNXT_FLTR_TYPE_NTUPLE 1 @@ -1355,19 +1356,21 @@ struct bnxt_filter_base { struct rcu_head rcu; }; +struct bnxt_flow_masks { + struct flow_dissector_key_ports ports; + struct flow_dissector_key_addrs addrs; +}; + +extern const struct bnxt_flow_masks BNXT_FLOW_MASK_NONE; +extern const struct bnxt_flow_masks BNXT_FLOW_IPV6_MASK_ALL; +extern const struct bnxt_flow_masks BNXT_FLOW_IPV4_MASK_ALL; + struct bnxt_ntuple_filter { + /* base filter must be the first member */ struct bnxt_filter_base base; struct flow_keys fkeys; + struct bnxt_flow_masks fmasks; struct bnxt_l2_filter *l2_fltr; - u32 ntuple_flags; -#define BNXT_NTUPLE_MATCH_SRC_IP 1 -#define BNXT_NTUPLE_MATCH_DST_IP 2 -#define BNXT_NTUPLE_MATCH_SRC_PORT 4 -#define BNXT_NTUPLE_MATCH_DST_PORT 8 -#define BNXT_NTUPLE_MATCH_ALL (BNXT_NTUPLE_MATCH_SRC_IP | \ - BNXT_NTUPLE_MATCH_DST_IP | \ - BNXT_NTUPLE_MATCH_SRC_PORT | \ - BNXT_NTUPLE_MATCH_DST_PORT) u32 flow_id; }; @@ -1394,6 +1397,7 @@ struct bnxt_ipv6_tuple { #define BNXT_L2_KEY_SIZE (sizeof(struct bnxt_l2_key) / 4) struct bnxt_l2_filter { + /* base filter must be the first member */ struct bnxt_filter_base base; struct bnxt_l2_key l2_key; atomic_t refcnt; @@ -2217,6 +2221,14 @@ struct bnxt { #define BNXT_RSS_CAP_UDP_RSS_CAP BIT(1) #define BNXT_RSS_CAP_NEW_RSS_CAP BIT(2) #define BNXT_RSS_CAP_RSS_TCAM BIT(3) +#define BNXT_RSS_CAP_AH_V4_RSS_CAP BIT(4) +#define BNXT_RSS_CAP_AH_V6_RSS_CAP BIT(5) +#define BNXT_RSS_CAP_ESP_V4_RSS_CAP BIT(6) +#define BNXT_RSS_CAP_ESP_V6_RSS_CAP BIT(7) + + u8 rss_hash_key[HW_HASH_KEY_SIZE]; + u8 rss_hash_key_valid:1; + u8 rss_hash_key_updated:1; u16 max_mtu; u8 max_tc; @@ -2301,6 +2313,7 @@ struct bnxt { #define BNXT_FW_CAP_PRE_RESV_VNICS BIT_ULL(35) #define BNXT_FW_CAP_BACKING_STORE_V2 BIT_ULL(36) #define BNXT_FW_CAP_VNIC_TUNNEL_TPA BIT_ULL(37) + #define BNXT_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO BIT_ULL(38) u32 fw_dbg_cap; @@ -2428,6 +2441,7 @@ struct bnxt { unsigned long *ntp_fltr_bmap; int ntp_fltr_count; + int max_fltr; #define BNXT_L2_FLTR_MAX_FLTR 1024 #define BNXT_L2_FLTR_HASH_SIZE 32 @@ -2437,12 +2451,14 @@ struct bnxt { u32 hash_seed; u64 toeplitz_prefix; + struct list_head usr_fltr_list; + /* To protect link related settings during link changes and * ethtool settings changes. */ struct mutex link_lock; struct bnxt_link_info link_info; - struct ethtool_eee eee; + struct ethtool_keee eee; u32 lpi_tmr_lo; u32 lpi_tmr_hi; @@ -2641,10 +2657,16 @@ u32 bnxt_fw_health_readl(struct bnxt *bp, int reg_idx); void bnxt_set_tpa_flags(struct bnxt *bp); void bnxt_set_ring_params(struct bnxt *); int bnxt_set_rx_skb_mode(struct bnxt *bp, bool page_mode); +void bnxt_insert_usr_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr); +void bnxt_del_one_usr_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr); +void bnxt_clear_usr_fltrs(struct bnxt *bp, bool all); int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp, unsigned long *bmap, int bmap_size, bool async_only); int bnxt_hwrm_func_drv_unrgtr(struct bnxt *bp); void bnxt_del_l2_filter(struct bnxt *bp, struct bnxt_l2_filter *fltr); +struct bnxt_l2_filter *bnxt_alloc_new_l2_filter(struct bnxt *bp, + struct bnxt_l2_key *key, + u16 flags); int bnxt_hwrm_l2_filter_free(struct bnxt *bp, struct bnxt_l2_filter *fltr); int bnxt_hwrm_l2_filter_alloc(struct bnxt *bp, struct bnxt_l2_filter *fltr); int bnxt_hwrm_cfa_ntuple_filter_free(struct bnxt *bp, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index dc4ca706b0e2..dfb5944683f8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -968,6 +968,7 @@ static int bnxt_set_channels(struct net_device *dev, return -EINVAL; } + bnxt_clear_usr_fltrs(bp, true); if (netif_running(dev)) { if (BNXT_PF(bp)) { /* TODO CHIMP_FW: Send message to all VF's @@ -1058,11 +1059,17 @@ static struct bnxt_filter_base *bnxt_get_one_fltr_rcu(struct bnxt *bp, static int bnxt_grxclsrlall(struct bnxt *bp, struct ethtool_rxnfc *cmd, u32 *rule_locs) { + u32 count; + cmd->data = bp->ntp_fltr_count; rcu_read_lock(); + count = bnxt_get_all_fltr_ids_rcu(bp, bp->l2_fltr_hash_tbl, + BNXT_L2_FLTR_HASH_SIZE, rule_locs, 0, + cmd->rule_cnt); cmd->rule_cnt = bnxt_get_all_fltr_ids_rcu(bp, bp->ntp_fltr_hash_tbl, BNXT_NTP_FLTR_HASH_SIZE, - rule_locs, 0, cmd->rule_cnt); + rule_locs, count, + cmd->rule_cnt); rcu_read_unlock(); return 0; @@ -1074,13 +1081,44 @@ static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd) (struct ethtool_rx_flow_spec *)&cmd->fs; struct bnxt_filter_base *fltr_base; struct bnxt_ntuple_filter *fltr; + struct bnxt_flow_masks *fmasks; struct flow_keys *fkeys; int rc = -EINVAL; - if (fs->location >= BNXT_NTP_FLTR_MAX_FLTR) + if (fs->location >= bp->max_fltr) return rc; rcu_read_lock(); + fltr_base = bnxt_get_one_fltr_rcu(bp, bp->l2_fltr_hash_tbl, + BNXT_L2_FLTR_HASH_SIZE, + fs->location); + if (fltr_base) { + struct ethhdr *h_ether = &fs->h_u.ether_spec; + struct ethhdr *m_ether = &fs->m_u.ether_spec; + struct bnxt_l2_filter *l2_fltr; + struct bnxt_l2_key *l2_key; + + l2_fltr = container_of(fltr_base, struct bnxt_l2_filter, base); + l2_key = &l2_fltr->l2_key; + fs->flow_type = ETHER_FLOW; + ether_addr_copy(h_ether->h_dest, l2_key->dst_mac_addr); + eth_broadcast_addr(m_ether->h_dest); + if (l2_key->vlan) { + struct ethtool_flow_ext *m_ext = &fs->m_ext; + struct ethtool_flow_ext *h_ext = &fs->h_ext; + + fs->flow_type |= FLOW_EXT; + m_ext->vlan_tci = htons(0xfff); + h_ext->vlan_tci = htons(l2_key->vlan); + } + if (fltr_base->flags & BNXT_ACT_RING_DST) + fs->ring_cookie = fltr_base->rxq; + if (fltr_base->flags & BNXT_ACT_FUNC_DST) + fs->ring_cookie = (u64)(fltr_base->vf_idx + 1) << + ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; + rcu_read_unlock(); + return 0; + } fltr_base = bnxt_get_one_fltr_rcu(bp, bp->ntp_fltr_hash_tbl, BNXT_NTP_FLTR_HASH_SIZE, fs->location); @@ -1091,59 +1129,74 @@ static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd) fltr = container_of(fltr_base, struct bnxt_ntuple_filter, base); fkeys = &fltr->fkeys; + fmasks = &fltr->fmasks; if (fkeys->basic.n_proto == htons(ETH_P_IP)) { - if (fkeys->basic.ip_proto == IPPROTO_TCP) + if (fkeys->basic.ip_proto == IPPROTO_ICMP || + fkeys->basic.ip_proto == IPPROTO_RAW) { + fs->flow_type = IP_USER_FLOW; + fs->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4; + if (fkeys->basic.ip_proto == IPPROTO_ICMP) + fs->h_u.usr_ip4_spec.proto = IPPROTO_ICMP; + else + fs->h_u.usr_ip4_spec.proto = IPPROTO_RAW; + fs->m_u.usr_ip4_spec.proto = BNXT_IP_PROTO_FULL_MASK; + } else if (fkeys->basic.ip_proto == IPPROTO_TCP) { fs->flow_type = TCP_V4_FLOW; - else if (fkeys->basic.ip_proto == IPPROTO_UDP) + } else if (fkeys->basic.ip_proto == IPPROTO_UDP) { fs->flow_type = UDP_V4_FLOW; - else + } else { goto fltr_err; - - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) { - fs->h_u.tcp_ip4_spec.ip4src = fkeys->addrs.v4addrs.src; - fs->m_u.tcp_ip4_spec.ip4src = cpu_to_be32(~0); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) { - fs->h_u.tcp_ip4_spec.ip4dst = fkeys->addrs.v4addrs.dst; - fs->m_u.tcp_ip4_spec.ip4dst = cpu_to_be32(~0); } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_PORT) { + + fs->h_u.tcp_ip4_spec.ip4src = fkeys->addrs.v4addrs.src; + fs->m_u.tcp_ip4_spec.ip4src = fmasks->addrs.v4addrs.src; + fs->h_u.tcp_ip4_spec.ip4dst = fkeys->addrs.v4addrs.dst; + fs->m_u.tcp_ip4_spec.ip4dst = fmasks->addrs.v4addrs.dst; + if (fs->flow_type == TCP_V4_FLOW || + fs->flow_type == UDP_V4_FLOW) { fs->h_u.tcp_ip4_spec.psrc = fkeys->ports.src; - fs->m_u.tcp_ip4_spec.psrc = cpu_to_be16(~0); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_PORT) { + fs->m_u.tcp_ip4_spec.psrc = fmasks->ports.src; fs->h_u.tcp_ip4_spec.pdst = fkeys->ports.dst; - fs->m_u.tcp_ip4_spec.pdst = cpu_to_be16(~0); + fs->m_u.tcp_ip4_spec.pdst = fmasks->ports.dst; } } else { - if (fkeys->basic.ip_proto == IPPROTO_TCP) + if (fkeys->basic.ip_proto == IPPROTO_ICMPV6 || + fkeys->basic.ip_proto == IPPROTO_RAW) { + fs->flow_type = IPV6_USER_FLOW; + if (fkeys->basic.ip_proto == IPPROTO_ICMPV6) + fs->h_u.usr_ip6_spec.l4_proto = IPPROTO_ICMPV6; + else + fs->h_u.usr_ip6_spec.l4_proto = IPPROTO_RAW; + fs->m_u.usr_ip6_spec.l4_proto = BNXT_IP_PROTO_FULL_MASK; + } else if (fkeys->basic.ip_proto == IPPROTO_TCP) { fs->flow_type = TCP_V6_FLOW; - else if (fkeys->basic.ip_proto == IPPROTO_UDP) + } else if (fkeys->basic.ip_proto == IPPROTO_UDP) { fs->flow_type = UDP_V6_FLOW; - else + } else { goto fltr_err; - - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) { - *(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6src[0] = - fkeys->addrs.v6addrs.src; - bnxt_fill_ipv6_mask(fs->m_u.tcp_ip6_spec.ip6src); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) { - *(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6dst[0] = - fkeys->addrs.v6addrs.dst; - bnxt_fill_ipv6_mask(fs->m_u.tcp_ip6_spec.ip6dst); } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_PORT) { + + *(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6src[0] = + fkeys->addrs.v6addrs.src; + *(struct in6_addr *)&fs->m_u.tcp_ip6_spec.ip6src[0] = + fmasks->addrs.v6addrs.src; + *(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6dst[0] = + fkeys->addrs.v6addrs.dst; + *(struct in6_addr *)&fs->m_u.tcp_ip6_spec.ip6dst[0] = + fmasks->addrs.v6addrs.dst; + if (fs->flow_type == TCP_V6_FLOW || + fs->flow_type == UDP_V6_FLOW) { fs->h_u.tcp_ip6_spec.psrc = fkeys->ports.src; - fs->m_u.tcp_ip6_spec.psrc = cpu_to_be16(~0); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_PORT) { + fs->m_u.tcp_ip6_spec.psrc = fmasks->ports.src; fs->h_u.tcp_ip6_spec.pdst = fkeys->ports.dst; - fs->m_u.tcp_ip6_spec.pdst = cpu_to_be16(~0); + fs->m_u.tcp_ip6_spec.pdst = fmasks->ports.dst; } } - fs->ring_cookie = fltr->base.rxq; + if (fltr->base.flags & BNXT_ACT_DROP) + fs->ring_cookie = RX_CLS_FLOW_DISC; + else + fs->ring_cookie = fltr->base.rxq; rc = 0; fltr_err: @@ -1152,17 +1205,78 @@ fltr_err: return rc; } -#define IPV4_ALL_MASK ((__force __be32)~0) -#define L4_PORT_ALL_MASK ((__force __be16)~0) +static int bnxt_add_l2_cls_rule(struct bnxt *bp, + struct ethtool_rx_flow_spec *fs) +{ + u32 ring = ethtool_get_flow_spec_ring(fs->ring_cookie); + u8 vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie); + struct ethhdr *h_ether = &fs->h_u.ether_spec; + struct ethhdr *m_ether = &fs->m_u.ether_spec; + struct bnxt_l2_filter *fltr; + struct bnxt_l2_key key; + u16 vnic_id; + u8 flags; + int rc; + + if (BNXT_CHIP_P5_PLUS(bp)) + return -EOPNOTSUPP; -static bool ipv6_mask_is_full(__be32 mask[4]) + if (!is_broadcast_ether_addr(m_ether->h_dest)) + return -EINVAL; + ether_addr_copy(key.dst_mac_addr, h_ether->h_dest); + key.vlan = 0; + if (fs->flow_type & FLOW_EXT) { + struct ethtool_flow_ext *m_ext = &fs->m_ext; + struct ethtool_flow_ext *h_ext = &fs->h_ext; + + if (m_ext->vlan_tci != htons(0xfff) || !h_ext->vlan_tci) + return -EINVAL; + key.vlan = ntohs(h_ext->vlan_tci); + } + + if (vf) { + flags = BNXT_ACT_FUNC_DST; + vnic_id = 0xffff; + vf--; + } else { + flags = BNXT_ACT_RING_DST; + vnic_id = bp->vnic_info[ring + 1].fw_vnic_id; + } + fltr = bnxt_alloc_new_l2_filter(bp, &key, flags); + if (IS_ERR(fltr)) + return PTR_ERR(fltr); + + fltr->base.fw_vnic_id = vnic_id; + fltr->base.rxq = ring; + fltr->base.vf_idx = vf; + rc = bnxt_hwrm_l2_filter_alloc(bp, fltr); + if (rc) + bnxt_del_l2_filter(bp, fltr); + else + fs->location = fltr->base.sw_id; + return rc; +} + +static bool bnxt_verify_ntuple_ip4_flow(struct ethtool_usrip4_spec *ip_spec, + struct ethtool_usrip4_spec *ip_mask) { - return (mask[0] & mask[1] & mask[2] & mask[3]) == IPV4_ALL_MASK; + if (ip_mask->l4_4_bytes || ip_mask->tos || + ip_spec->ip_ver != ETH_RX_NFC_IP4 || + ip_mask->proto != BNXT_IP_PROTO_FULL_MASK || + (ip_spec->proto != IPPROTO_RAW && ip_spec->proto != IPPROTO_ICMP)) + return false; + return true; } -static bool ipv6_mask_is_zero(__be32 mask[4]) +static bool bnxt_verify_ntuple_ip6_flow(struct ethtool_usrip6_spec *ip_spec, + struct ethtool_usrip6_spec *ip_mask) { - return !(mask[0] | mask[1] | mask[2] | mask[3]); + if (ip_mask->l4_4_bytes || ip_mask->tclass || + ip_mask->l4_proto != BNXT_IP_PROTO_FULL_MASK || + (ip_spec->l4_proto != IPPROTO_RAW && + ip_spec->l4_proto != IPPROTO_ICMPV6)) + return false; + return true; } static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, @@ -1172,6 +1286,7 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, u32 ring = ethtool_get_flow_spec_ring(fs->ring_cookie); struct bnxt_ntuple_filter *new_fltr, *fltr; struct bnxt_l2_filter *l2_fltr; + struct bnxt_flow_masks *fmasks; u32 flow_type = fs->flow_type; struct flow_keys *fkeys; u32 idx; @@ -1183,6 +1298,18 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, if ((flow_type & (FLOW_MAC_EXT | FLOW_EXT)) || vf) return -EOPNOTSUPP; + if (flow_type == IP_USER_FLOW) { + if (!bnxt_verify_ntuple_ip4_flow(&fs->h_u.usr_ip4_spec, + &fs->m_u.usr_ip4_spec)) + return -EOPNOTSUPP; + } + + if (flow_type == IPV6_USER_FLOW) { + if (!bnxt_verify_ntuple_ip6_flow(&fs->h_u.usr_ip6_spec, + &fs->m_u.usr_ip6_spec)) + return -EOPNOTSUPP; + } + new_fltr = kzalloc(sizeof(*new_fltr), GFP_KERNEL); if (!new_fltr) return -ENOMEM; @@ -1190,10 +1317,23 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, l2_fltr = bp->vnic_info[0].l2_filters[0]; atomic_inc(&l2_fltr->refcnt); new_fltr->l2_fltr = l2_fltr; + fmasks = &new_fltr->fmasks; fkeys = &new_fltr->fkeys; rc = -EOPNOTSUPP; switch (flow_type) { + case IP_USER_FLOW: { + struct ethtool_usrip4_spec *ip_spec = &fs->h_u.usr_ip4_spec; + struct ethtool_usrip4_spec *ip_mask = &fs->m_u.usr_ip4_spec; + + fkeys->basic.ip_proto = ip_spec->proto; + fkeys->basic.n_proto = htons(ETH_P_IP); + fkeys->addrs.v4addrs.src = ip_spec->ip4src; + fmasks->addrs.v4addrs.src = ip_mask->ip4src; + fkeys->addrs.v4addrs.dst = ip_spec->ip4dst; + fmasks->addrs.v4addrs.dst = ip_mask->ip4dst; + break; + } case TCP_V4_FLOW: case UDP_V4_FLOW: { struct ethtool_tcpip4_spec *ip_spec = &fs->h_u.tcp_ip4_spec; @@ -1203,32 +1343,26 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, if (flow_type == UDP_V4_FLOW) fkeys->basic.ip_proto = IPPROTO_UDP; fkeys->basic.n_proto = htons(ETH_P_IP); + fkeys->addrs.v4addrs.src = ip_spec->ip4src; + fmasks->addrs.v4addrs.src = ip_mask->ip4src; + fkeys->addrs.v4addrs.dst = ip_spec->ip4dst; + fmasks->addrs.v4addrs.dst = ip_mask->ip4dst; + fkeys->ports.src = ip_spec->psrc; + fmasks->ports.src = ip_mask->psrc; + fkeys->ports.dst = ip_spec->pdst; + fmasks->ports.dst = ip_mask->pdst; + break; + } + case IPV6_USER_FLOW: { + struct ethtool_usrip6_spec *ip_spec = &fs->h_u.usr_ip6_spec; + struct ethtool_usrip6_spec *ip_mask = &fs->m_u.usr_ip6_spec; - if (ip_mask->ip4src == IPV4_ALL_MASK) { - fkeys->addrs.v4addrs.src = ip_spec->ip4src; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_IP; - } else if (ip_mask->ip4src) { - goto ntuple_err; - } - if (ip_mask->ip4dst == IPV4_ALL_MASK) { - fkeys->addrs.v4addrs.dst = ip_spec->ip4dst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_IP; - } else if (ip_mask->ip4dst) { - goto ntuple_err; - } - - if (ip_mask->psrc == L4_PORT_ALL_MASK) { - fkeys->ports.src = ip_spec->psrc; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_PORT; - } else if (ip_mask->psrc) { - goto ntuple_err; - } - if (ip_mask->pdst == L4_PORT_ALL_MASK) { - fkeys->ports.dst = ip_spec->pdst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_PORT; - } else if (ip_mask->pdst) { - goto ntuple_err; - } + fkeys->basic.ip_proto = ip_spec->l4_proto; + fkeys->basic.n_proto = htons(ETH_P_IPV6); + fkeys->addrs.v6addrs.src = *(struct in6_addr *)&ip_spec->ip6src; + fmasks->addrs.v6addrs.src = *(struct in6_addr *)&ip_mask->ip6src; + fkeys->addrs.v6addrs.dst = *(struct in6_addr *)&ip_spec->ip6dst; + fmasks->addrs.v6addrs.dst = *(struct in6_addr *)&ip_mask->ip6dst; break; } case TCP_V6_FLOW: @@ -1241,40 +1375,21 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, fkeys->basic.ip_proto = IPPROTO_UDP; fkeys->basic.n_proto = htons(ETH_P_IPV6); - if (ipv6_mask_is_full(ip_mask->ip6src)) { - fkeys->addrs.v6addrs.src = - *(struct in6_addr *)&ip_spec->ip6src; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_IP; - } else if (!ipv6_mask_is_zero(ip_mask->ip6src)) { - goto ntuple_err; - } - if (ipv6_mask_is_full(ip_mask->ip6dst)) { - fkeys->addrs.v6addrs.dst = - *(struct in6_addr *)&ip_spec->ip6dst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_IP; - } else if (!ipv6_mask_is_zero(ip_mask->ip6dst)) { - goto ntuple_err; - } - - if (ip_mask->psrc == L4_PORT_ALL_MASK) { - fkeys->ports.src = ip_spec->psrc; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_PORT; - } else if (ip_mask->psrc) { - goto ntuple_err; - } - if (ip_mask->pdst == L4_PORT_ALL_MASK) { - fkeys->ports.dst = ip_spec->pdst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_PORT; - } else if (ip_mask->pdst) { - goto ntuple_err; - } + fkeys->addrs.v6addrs.src = *(struct in6_addr *)&ip_spec->ip6src; + fmasks->addrs.v6addrs.src = *(struct in6_addr *)&ip_mask->ip6src; + fkeys->addrs.v6addrs.dst = *(struct in6_addr *)&ip_spec->ip6dst; + fmasks->addrs.v6addrs.dst = *(struct in6_addr *)&ip_mask->ip6dst; + fkeys->ports.src = ip_spec->psrc; + fmasks->ports.src = ip_mask->psrc; + fkeys->ports.dst = ip_spec->pdst; + fmasks->ports.dst = ip_mask->pdst; break; } default: rc = -EOPNOTSUPP; goto ntuple_err; } - if (!new_fltr->ntuple_flags) + if (!memcmp(&BNXT_FLOW_MASK_NONE, fmasks, sizeof(*fmasks))) goto ntuple_err; idx = bnxt_get_ntp_filter_idx(bp, fkeys, NULL); @@ -1287,8 +1402,11 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, } rcu_read_unlock(); - new_fltr->base.rxq = ring; new_fltr->base.flags = BNXT_ACT_NO_AGING; + if (fs->ring_cookie == RX_CLS_FLOW_DISC) + new_fltr->base.flags |= BNXT_ACT_DROP; + else + new_fltr->base.rxq = ring; __set_bit(BNXT_FLTR_VALID, &new_fltr->base.state); rc = bnxt_insert_ntp_filter(bp, new_fltr, idx); if (!rc) { @@ -1321,6 +1439,18 @@ static int bnxt_srxclsrlins(struct bnxt *bp, struct ethtool_rxnfc *cmd) if (fs->location != RX_CLS_LOC_ANY) return -EINVAL; + flow_type = fs->flow_type; + if ((flow_type == IP_USER_FLOW || + flow_type == IPV6_USER_FLOW) && + !(bp->fw_cap & BNXT_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO)) + return -EOPNOTSUPP; + if (flow_type & (FLOW_MAC_EXT | FLOW_RSS)) + return -EINVAL; + flow_type &= ~FLOW_EXT; + + if (fs->ring_cookie == RX_CLS_FLOW_DISC && flow_type != ETHER_FLOW) + return bnxt_add_ntuple_cls_rule(bp, fs); + ring = ethtool_get_flow_spec_ring(fs->ring_cookie); vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie); if (BNXT_VF(bp) && vf) @@ -1330,12 +1460,8 @@ static int bnxt_srxclsrlins(struct bnxt *bp, struct ethtool_rxnfc *cmd) if (!vf && ring >= bp->rx_nr_rings) return -EINVAL; - flow_type = fs->flow_type; - if (flow_type & (FLOW_MAC_EXT | FLOW_RSS)) - return -EINVAL; - flow_type &= ~FLOW_EXT; if (flow_type == ETHER_FLOW) - rc = -EOPNOTSUPP; + rc = bnxt_add_l2_cls_rule(bp, fs); else rc = bnxt_add_ntuple_cls_rule(bp, fs); return rc; @@ -1346,11 +1472,22 @@ static int bnxt_srxclsrldel(struct bnxt *bp, struct ethtool_rxnfc *cmd) struct ethtool_rx_flow_spec *fs = &cmd->fs; struct bnxt_filter_base *fltr_base; struct bnxt_ntuple_filter *fltr; + u32 id = fs->location; rcu_read_lock(); + fltr_base = bnxt_get_one_fltr_rcu(bp, bp->l2_fltr_hash_tbl, + BNXT_L2_FLTR_HASH_SIZE, id); + if (fltr_base) { + struct bnxt_l2_filter *l2_fltr; + + l2_fltr = container_of(fltr_base, struct bnxt_l2_filter, base); + rcu_read_unlock(); + bnxt_hwrm_l2_filter_free(bp, l2_fltr); + bnxt_del_l2_filter(bp, l2_fltr); + return 0; + } fltr_base = bnxt_get_one_fltr_rcu(bp, bp->ntp_fltr_hash_tbl, - BNXT_NTP_FLTR_HASH_SIZE, - fs->location); + BNXT_NTP_FLTR_HASH_SIZE, id); if (!fltr_base) { rcu_read_unlock(); return -ENOENT; @@ -1396,8 +1533,14 @@ static int bnxt_grxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) cmd->data |= RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3; fallthrough; - case SCTP_V4_FLOW: case AH_ESP_V4_FLOW: + if (bp->rss_hash_cfg & + (VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 | + VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4)) + cmd->data |= RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + fallthrough; + case SCTP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case IPV4_FLOW: @@ -1415,8 +1558,14 @@ static int bnxt_grxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) cmd->data |= RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3; fallthrough; - case SCTP_V6_FLOW: case AH_ESP_V6_FLOW: + if (bp->rss_hash_cfg & + (VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 | + VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6)) + cmd->data |= RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + fallthrough; + case SCTP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case IPV6_FLOW: @@ -1463,6 +1612,24 @@ static int bnxt_srxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6; if (tuple == 4) rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6; + } else if (cmd->flow_type == AH_ESP_V4_FLOW) { + if (tuple == 4 && (!(bp->rss_cap & BNXT_RSS_CAP_AH_V4_RSS_CAP) || + !(bp->rss_cap & BNXT_RSS_CAP_ESP_V4_RSS_CAP))) + return -EINVAL; + rss_hash_cfg &= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 | + VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4); + if (tuple == 4) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 | + VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4; + } else if (cmd->flow_type == AH_ESP_V6_FLOW) { + if (tuple == 4 && (!(bp->rss_cap & BNXT_RSS_CAP_AH_V6_RSS_CAP) || + !(bp->rss_cap & BNXT_RSS_CAP_ESP_V6_RSS_CAP))) + return -EINVAL; + rss_hash_cfg &= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 | + VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6); + if (tuple == 4) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 | + VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6; } else if (tuple == 4) { return -EINVAL; } @@ -1521,7 +1688,7 @@ static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, case ETHTOOL_GRXCLSRLCNT: cmd->rule_cnt = bp->ntp_fltr_count; - cmd->data = BNXT_NTP_FLTR_MAX_FLTR | RX_CLS_LOC_SPECIAL; + cmd->data = bp->max_fltr | RX_CLS_LOC_SPECIAL; break; case ETHTOOL_GRXCLSRLALL: @@ -1619,8 +1786,10 @@ static int bnxt_set_rxfh(struct net_device *dev, if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; - if (rxfh->key) - return -EOPNOTSUPP; + if (rxfh->key) { + memcpy(bp->rss_hash_key, rxfh->key, HW_HASH_KEY_SIZE); + bp->rss_hash_key_updated = true; + } if (rxfh->indir) { u32 i, pad, tbl_size = bnxt_get_rxfh_indir_size(dev); @@ -1631,7 +1800,7 @@ static int bnxt_set_rxfh(struct net_device *dev, if (pad) memset(&bp->rss_indir_tbl[i], 0, pad * sizeof(u16)); } - + bnxt_clear_usr_fltrs(bp, false); if (netif_running(bp->dev)) { bnxt_close_nic(bp, false, false); rc = bnxt_open_nic(bp, false, false); @@ -1751,31 +1920,21 @@ static int bnxt_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) return 0; } -u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause) +/* TODO: support 25GB, 40GB, 50GB with different cable type */ +void _bnxt_fw_to_linkmode(unsigned long *mode, u16 fw_speeds) { - u32 speed_mask = 0; + linkmode_zero(mode); - /* TODO: support 25GB, 40GB, 50GB with different cable type */ - /* set the advertised speeds */ if (fw_speeds & BNXT_LINK_SPEED_MSK_100MB) - speed_mask |= ADVERTISED_100baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_1GB) - speed_mask |= ADVERTISED_1000baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_2_5GB) - speed_mask |= ADVERTISED_2500baseX_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_10GB) - speed_mask |= ADVERTISED_10000baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_40GB) - speed_mask |= ADVERTISED_40000baseCR4_Full; - - if ((fw_pause & BNXT_LINK_PAUSE_BOTH) == BNXT_LINK_PAUSE_BOTH) - speed_mask |= ADVERTISED_Pause; - else if (fw_pause & BNXT_LINK_PAUSE_TX) - speed_mask |= ADVERTISED_Asym_Pause; - else if (fw_pause & BNXT_LINK_PAUSE_RX) - speed_mask |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; - - return speed_mask; + linkmode_set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, mode); } enum bnxt_media_type { @@ -2643,23 +2802,22 @@ bnxt_force_link_speed(struct net_device *dev, u32 ethtool_speed, u32 lanes) return 0; } -u16 bnxt_get_fw_auto_link_speeds(u32 advertising) +u16 bnxt_get_fw_auto_link_speeds(const unsigned long *mode) { u16 fw_speed_mask = 0; - /* only support autoneg at speed 100, 1000, and 10000 */ - if (advertising & (ADVERTISED_100baseT_Full | - ADVERTISED_100baseT_Half)) { + if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode) || + linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_100MB; - } - if (advertising & (ADVERTISED_1000baseT_Full | - ADVERTISED_1000baseT_Half)) { + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode) || + linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_1GB; - } - if (advertising & ADVERTISED_10000baseT_Full) + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_10GB; - if (advertising & ADVERTISED_40000baseCR4_Full) + if (linkmode_test_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_40GB; return fw_speed_mask; @@ -3884,12 +4042,13 @@ static int bnxt_set_eeprom(struct net_device *dev, eeprom->len); } -static int bnxt_set_eee(struct net_device *dev, struct ethtool_eee *edata) +static int bnxt_set_eee(struct net_device *dev, struct ethtool_keee *edata) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp); struct bnxt *bp = netdev_priv(dev); - struct ethtool_eee *eee = &bp->eee; + struct ethtool_keee *eee = &bp->eee; struct bnxt_link_info *link_info = &bp->link_info; - u32 advertising; int rc = 0; if (!BNXT_PHY_CFG_ABLE(bp)) @@ -3899,7 +4058,7 @@ static int bnxt_set_eee(struct net_device *dev, struct ethtool_eee *edata) return -EOPNOTSUPP; mutex_lock(&bp->link_lock); - advertising = _bnxt_fw_to_ethtool_adv_spds(link_info->advertising, 0); + _bnxt_fw_to_linkmode(advertising, link_info->advertising); if (!edata->eee_enabled) goto eee_ok; @@ -3919,16 +4078,15 @@ static int bnxt_set_eee(struct net_device *dev, struct ethtool_eee *edata) edata->tx_lpi_timer = eee->tx_lpi_timer; } } - if (!edata->advertised) { - edata->advertised = advertising & eee->supported; - } else if (edata->advertised & ~advertising) { - netdev_warn(dev, "EEE advertised %x must be a subset of autoneg advertised speeds %x\n", - edata->advertised, advertising); + if (linkmode_empty(edata->advertised)) { + linkmode_and(edata->advertised, advertising, eee->supported); + } else if (linkmode_andnot(tmp, edata->advertised, advertising)) { + netdev_warn(dev, "EEE advertised must be a subset of autoneg advertised speeds\n"); rc = -EINVAL; goto eee_exit; } - eee->advertised = edata->advertised; + linkmode_copy(eee->advertised, edata->advertised); eee->tx_lpi_enabled = edata->tx_lpi_enabled; eee->tx_lpi_timer = edata->tx_lpi_timer; eee_ok: @@ -3942,7 +4100,7 @@ eee_exit: return rc; } -static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata) +static int bnxt_get_eee(struct net_device *dev, struct ethtool_keee *edata) { struct bnxt *bp = netdev_priv(dev); @@ -3954,12 +4112,12 @@ static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata) /* Preserve tx_lpi_timer so that the last value will be used * by default when it is re-enabled. */ - edata->advertised = 0; + linkmode_zero(edata->advertised); edata->tx_lpi_enabled = 0; } if (!bp->eee.eee_active) - edata->lp_advertised = 0; + linkmode_zero(edata->lp_advertised); return 0; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h index a8ecef8ab82c..e2ee030237d4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h @@ -43,12 +43,14 @@ struct bnxt_led_cfg { #define BNXT_PXP_REG_LEN 0x3110 +#define BNXT_IP_PROTO_FULL_MASK 0xFF + extern const struct ethtool_ops bnxt_ethtool_ops; u32 bnxt_get_rxfh_indir_size(struct net_device *dev); -u32 _bnxt_fw_to_ethtool_adv_spds(u16, u8); +void _bnxt_fw_to_linkmode(unsigned long *mode, u16 fw_speeds); u32 bnxt_fw_to_ethtool_speed(u16); -u16 bnxt_get_fw_auto_link_speeds(u32); +u16 bnxt_get_fw_auto_link_speeds(const unsigned long *mode); int bnxt_hwrm_nvm_get_dev_info(struct bnxt *bp, struct hwrm_nvm_get_dev_info_output *nvm_dev_info); int bnxt_hwrm_firmware_reset(struct net_device *dev, u8 proc_type, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index adad188e38b8..cc07660330f5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -684,7 +684,7 @@ static void bnxt_stamp_tx_skb(struct bnxt *bp, struct sk_buff *skb) timestamp.hwtstamp = ns_to_ktime(ns); skb_tstamp_tx(ptp->tx_skb, ×tamp); } else { - netdev_WARN_ONCE(bp->dev, + netdev_warn_once(bp->dev, "TS query for TX timer failed rc = %x\n", rc); } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 2d7ae71287b1..7396e2823e32 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1313,14 +1313,13 @@ void bcmgenet_eee_enable_set(struct net_device *dev, bool enable, } priv->eee.eee_enabled = enable; - priv->eee.eee_active = enable; priv->eee.tx_lpi_enabled = tx_lpi_enabled; } -static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_eee *e) +static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_keee *e) { struct bcmgenet_priv *priv = netdev_priv(dev); - struct ethtool_eee *p = &priv->eee; + struct ethtool_keee *p = &priv->eee; if (GENET_IS_V1(priv)) return -EOPNOTSUPP; @@ -1328,18 +1327,17 @@ static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_eee *e) if (!dev->phydev) return -ENODEV; - e->eee_enabled = p->eee_enabled; - e->eee_active = p->eee_active; e->tx_lpi_enabled = p->tx_lpi_enabled; e->tx_lpi_timer = bcmgenet_umac_readl(priv, UMAC_EEE_LPI_TIMER); return phy_ethtool_get_eee(dev->phydev, e); } -static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e) +static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_keee *e) { struct bcmgenet_priv *priv = netdev_priv(dev); - struct ethtool_eee *p = &priv->eee; + struct ethtool_keee *p = &priv->eee; + bool active; if (GENET_IS_V1(priv)) return -EOPNOTSUPP; @@ -1352,9 +1350,9 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e) if (!p->eee_enabled) { bcmgenet_eee_enable_set(dev, false, false); } else { - p->eee_active = phy_init_eee(dev->phydev, false) >= 0; + active = phy_init_eee(dev->phydev, false) >= 0; bcmgenet_umac_writel(priv, e->tx_lpi_timer, UMAC_EEE_LPI_TIMER); - bcmgenet_eee_enable_set(dev, p->eee_active, e->tx_lpi_enabled); + bcmgenet_eee_enable_set(dev, active, e->tx_lpi_enabled); } return phy_ethtool_set_eee(dev->phydev, e); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 1985c0ec4da2..7523b60b3c1c 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -645,7 +645,7 @@ struct bcmgenet_priv { struct bcmgenet_mib_counters mib; - struct ethtool_eee eee; + struct ethtool_keee eee; }; #define GENET_IO_MACRO(name, offset) \ diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 97ea76d443ab..cbbe004621bc 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -30,6 +30,7 @@ static void bcmgenet_mac_config(struct net_device *dev) struct bcmgenet_priv *priv = netdev_priv(dev); struct phy_device *phydev = dev->phydev; u32 reg, cmd_bits = 0; + bool active; /* speed */ if (phydev->speed == SPEED_1000) @@ -88,9 +89,9 @@ static void bcmgenet_mac_config(struct net_device *dev) } bcmgenet_umac_writel(priv, reg, UMAC_CMD); - priv->eee.eee_active = phy_init_eee(phydev, 0) >= 0; + active = phy_init_eee(phydev, 0) >= 0; bcmgenet_eee_enable_set(dev, - priv->eee.eee_enabled && priv->eee.eee_active, + priv->eee.eee_enabled && active, priv->eee.tx_lpi_enabled); } diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 04964bbe08cf..eee759054aad 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -2338,10 +2338,10 @@ static void tg3_phy_apply_otp(struct tg3 *tp) tg3_phy_toggle_auxctl_smdsp(tp, false); } -static void tg3_eee_pull_config(struct tg3 *tp, struct ethtool_eee *eee) +static void tg3_eee_pull_config(struct tg3 *tp, struct ethtool_keee *eee) { u32 val; - struct ethtool_eee *dest = &tp->eee; + struct ethtool_keee *dest = &tp->eee; if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) return; @@ -2362,13 +2362,13 @@ static void tg3_eee_pull_config(struct tg3 *tp, struct ethtool_eee *eee) /* Pull lp advertised settings */ if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE, &val)) return; - dest->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); + mii_eee_cap1_mod_linkmode_t(dest->lp_advertised, val); /* Pull advertised and eee_enabled settings */ if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val)) return; dest->eee_enabled = !!val; - dest->advertised = mmd_eee_adv_to_ethtool_adv_t(val); + mii_eee_cap1_mod_linkmode_t(dest->advertised, val); /* Pull tx_lpi_enabled */ val = tr32(TG3_CPMU_EEE_MODE); @@ -4354,23 +4354,12 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl) if (!err) { u32 err2; - val = 0; - /* Advertise 100-BaseTX EEE ability */ - if (advertise & ADVERTISED_100baseT_Full) - val |= MDIO_AN_EEE_ADV_100TX; - /* Advertise 1000-BaseT EEE ability */ - if (advertise & ADVERTISED_1000baseT_Full) - val |= MDIO_AN_EEE_ADV_1000T; - - if (!tp->eee.eee_enabled) { + if (!tp->eee.eee_enabled) val = 0; - tp->eee.advertised = 0; - } else { - tp->eee.advertised = advertise & - (ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Full); - } + else + val = ethtool_adv_to_mmd_eee_adv_t(advertise); + mii_eee_cap1_mod_linkmode_t(tp->eee.advertised, val); err = tg3_phy_cl45_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); if (err) val = 0; @@ -4618,7 +4607,7 @@ static int tg3_init_5401phy_dsp(struct tg3 *tp) static bool tg3_phy_eee_config_ok(struct tg3 *tp) { - struct ethtool_eee eee; + struct ethtool_keee eee = {}; if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) return true; @@ -4626,13 +4615,13 @@ static bool tg3_phy_eee_config_ok(struct tg3 *tp) tg3_eee_pull_config(tp, &eee); if (tp->eee.eee_enabled) { - if (tp->eee.advertised != eee.advertised || + if (!linkmode_equal(tp->eee.advertised, eee.advertised) || tp->eee.tx_lpi_timer != eee.tx_lpi_timer || tp->eee.tx_lpi_enabled != eee.tx_lpi_enabled) return false; } else { /* EEE is disabled but we're advertising */ - if (eee.advertised) + if (!linkmode_empty(eee.advertised)) return false; } @@ -14180,7 +14169,7 @@ static int tg3_set_coalesce(struct net_device *dev, return 0; } -static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata) +static int tg3_set_eee(struct net_device *dev, struct ethtool_keee *edata) { struct tg3 *tp = netdev_priv(dev); @@ -14189,7 +14178,7 @@ static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata) return -EOPNOTSUPP; } - if (edata->advertised != tp->eee.advertised) { + if (!linkmode_equal(edata->advertised, tp->eee.advertised)) { netdev_warn(tp->dev, "Direct manipulation of EEE advertisement is not supported\n"); return -EINVAL; @@ -14202,7 +14191,9 @@ static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata) return -EINVAL; } - tp->eee = *edata; + tp->eee.eee_enabled = edata->eee_enabled; + tp->eee.tx_lpi_enabled = edata->tx_lpi_enabled; + tp->eee.tx_lpi_timer = edata->tx_lpi_timer; tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED; tg3_warn_mgmt_link_flap(tp); @@ -14217,7 +14208,7 @@ static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata) return 0; } -static int tg3_get_eee(struct net_device *dev, struct ethtool_eee *edata) +static int tg3_get_eee(struct net_device *dev, struct ethtool_keee *edata) { struct tg3 *tp = netdev_priv(dev); @@ -15655,10 +15646,13 @@ static int tg3_phy_probe(struct tg3 *tp) tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0))) { tp->phy_flags |= TG3_PHYFLG_EEE_CAP; - tp->eee.supported = SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full; - tp->eee.advertised = ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Full; + linkmode_zero(tp->eee.supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + tp->eee.supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + tp->eee.supported); + linkmode_copy(tp->eee.advertised, tp->eee.supported); + tp->eee.eee_enabled = 1; tp->eee.tx_lpi_enabled = 1; tp->eee.tx_lpi_timer = TG3_CPMU_DBTMR1_LNKIDLE_2047US; diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index 5016475e5005..cf1b2b123c7e 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -3419,7 +3419,7 @@ struct tg3 { unsigned int irq_cnt; struct ethtool_coalesce coal; - struct ethtool_eee eee; + struct ethtool_keee eee; /* firmware info */ const char *fw_needed; diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 31191b520b58..c32174484a96 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -1091,10 +1091,10 @@ bnad_cb_tx_resume(struct bnad *bnad, struct bna_tx *tx) * Free all TxQs buffers and then notify TX_E_CLEANUP_DONE to Tx fsm. */ static void -bnad_tx_cleanup(struct delayed_work *work) +bnad_tx_cleanup(struct work_struct *work) { struct bnad_tx_info *tx_info = - container_of(work, struct bnad_tx_info, tx_cleanup_work); + container_of(work, struct bnad_tx_info, tx_cleanup_work.work); struct bnad *bnad = NULL; struct bna_tcb *tcb; unsigned long flags; @@ -1170,7 +1170,7 @@ bnad_cb_rx_stall(struct bnad *bnad, struct bna_rx *rx) * Free all RxQs buffers and then notify RX_E_CLEANUP_DONE to Rx fsm. */ static void -bnad_rx_cleanup(void *work) +bnad_rx_cleanup(struct work_struct *work) { struct bnad_rx_info *rx_info = container_of(work, struct bnad_rx_info, rx_cleanup_work); @@ -1991,8 +1991,7 @@ bnad_setup_tx(struct bnad *bnad, u32 tx_id) } tx_info->tx = tx; - INIT_DELAYED_WORK(&tx_info->tx_cleanup_work, - (work_func_t)bnad_tx_cleanup); + INIT_DELAYED_WORK(&tx_info->tx_cleanup_work, bnad_tx_cleanup); /* Register ISR for the Tx object */ if (intr_info->intr_type == BNA_INTR_T_MSIX) { @@ -2248,8 +2247,7 @@ bnad_setup_rx(struct bnad *bnad, u32 rx_id) rx_info->rx = rx; spin_unlock_irqrestore(&bnad->bna_lock, flags); - INIT_WORK(&rx_info->rx_cleanup_work, - (work_func_t)(bnad_rx_cleanup)); + INIT_WORK(&rx_info->rx_cleanup_work, bnad_rx_cleanup); /* * Init NAPI, so that state is set to NAPI_STATE_SCHED, diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 37bd38d772e8..d266a87297a5 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -872,7 +872,7 @@ error: return NETDEV_TX_OK; } -/* dev_base_lock rwlock held, nominally process context */ +/* rcu_read_lock potentially held, nominally process context */ static void enic_get_stats(struct net_device *netdev, struct rtnl_link_stats64 *net_stats) { diff --git a/drivers/net/ethernet/ec_bhf.c b/drivers/net/ethernet/ec_bhf.c index c2c5c589a5e3..44af1d13d931 100644 --- a/drivers/net/ethernet/ec_bhf.c +++ b/drivers/net/ethernet/ec_bhf.c @@ -590,5 +590,6 @@ module_pci_driver(pci_driver); module_param(polling_frequency, long, 0444); MODULE_PARM_DESC(polling_frequency, "Polling timer frequency in ns"); +MODULE_DESCRIPTION("Beckhoff CX5020 EtherCAT Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dariusz Marcinkiewicz <reksio@newterm.pl>"); diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index ae0b8b37b9bf..4b15af6b7122 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -240,7 +240,7 @@ static int tsnep_phy_loopback(struct tsnep_adapter *adapter, bool enable) static int tsnep_phy_open(struct tsnep_adapter *adapter) { struct phy_device *phydev; - struct ethtool_eee ethtool_eee; + struct ethtool_keee ethtool_keee; int retval; retval = phy_connect_direct(adapter->netdev, adapter->phydev, @@ -259,8 +259,8 @@ static int tsnep_phy_open(struct tsnep_adapter *adapter) phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); /* disable EEE autoneg, EEE not supported by TSNEP */ - memset(ðtool_eee, 0, sizeof(ethtool_eee)); - phy_ethtool_set_eee(adapter->phydev, ðtool_eee); + memset(ðtool_keee, 0, sizeof(ethtool_keee)); + phy_ethtool_set_eee(adapter->phydev, ðtool_keee); adapter->phydev->irq = PHY_MAC_INTERRUPT; phy_start(adapter->phydev); @@ -721,17 +721,25 @@ static void tsnep_xdp_xmit_flush(struct tsnep_tx *tx) static bool tsnep_xdp_xmit_back(struct tsnep_adapter *adapter, struct xdp_buff *xdp, - struct netdev_queue *tx_nq, struct tsnep_tx *tx) + struct netdev_queue *tx_nq, struct tsnep_tx *tx, + bool zc) { struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp); bool xmit; + u32 type; if (unlikely(!xdpf)) return false; + /* no page pool for zero copy */ + if (zc) + type = TSNEP_TX_TYPE_XDP_NDO; + else + type = TSNEP_TX_TYPE_XDP_TX; + __netif_tx_lock(tx_nq, smp_processor_id()); - xmit = tsnep_xdp_xmit_frame_ring(xdpf, tx, TSNEP_TX_TYPE_XDP_TX); + xmit = tsnep_xdp_xmit_frame_ring(xdpf, tx, type); /* Avoid transmit queue timeout since we share it with the slow path */ if (xmit) @@ -1260,6 +1268,14 @@ static int tsnep_rx_refill_zc(struct tsnep_rx *rx, int count, bool reuse) return desc_refilled; } +static void tsnep_xsk_rx_need_wakeup(struct tsnep_rx *rx, int desc_available) +{ + if (desc_available) + xsk_set_rx_need_wakeup(rx->xsk_pool); + else + xsk_clear_rx_need_wakeup(rx->xsk_pool); +} + static bool tsnep_xdp_run_prog(struct tsnep_rx *rx, struct bpf_prog *prog, struct xdp_buff *xdp, int *status, struct netdev_queue *tx_nq, struct tsnep_tx *tx) @@ -1275,7 +1291,7 @@ static bool tsnep_xdp_run_prog(struct tsnep_rx *rx, struct bpf_prog *prog, case XDP_PASS: return false; case XDP_TX: - if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx)) + if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, false)) goto out_failure; *status |= TSNEP_XDP_TX; return true; @@ -1325,7 +1341,7 @@ static bool tsnep_xdp_run_prog_zc(struct tsnep_rx *rx, struct bpf_prog *prog, case XDP_PASS: return false; case XDP_TX: - if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx)) + if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, true)) goto out_failure; *status |= TSNEP_XDP_TX; return true; @@ -1621,10 +1637,7 @@ static int tsnep_rx_poll_zc(struct tsnep_rx *rx, struct napi_struct *napi, desc_available -= tsnep_rx_refill_zc(rx, desc_available, false); if (xsk_uses_need_wakeup(rx->xsk_pool)) { - if (desc_available) - xsk_set_rx_need_wakeup(rx->xsk_pool); - else - xsk_clear_rx_need_wakeup(rx->xsk_pool); + tsnep_xsk_rx_need_wakeup(rx, desc_available); return done; } @@ -1769,14 +1782,8 @@ static void tsnep_rx_reopen_xsk(struct tsnep_rx *rx) * first polling would be too late as need wakeup signalisation would * be delayed for an indefinite time */ - if (xsk_uses_need_wakeup(rx->xsk_pool)) { - int desc_available = tsnep_rx_desc_available(rx); - - if (desc_available) - xsk_set_rx_need_wakeup(rx->xsk_pool); - else - xsk_clear_rx_need_wakeup(rx->xsk_pool); - } + if (xsk_uses_need_wakeup(rx->xsk_pool)) + tsnep_xsk_rx_need_wakeup(rx, tsnep_rx_desc_available(rx)); } static bool tsnep_pending(struct tsnep_queue *queue) @@ -2564,8 +2571,7 @@ static int tsnep_probe(struct platform_device *pdev) mutex_init(&adapter->rxnfc_lock); INIT_LIST_HEAD(&adapter->rxnfc_rules); - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); - adapter->addr = devm_ioremap_resource(&pdev->dev, io); + adapter->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &io); if (IS_ERR(adapter->addr)) return PTR_ERR(adapter->addr); netdev->mem_start = io->start; diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index bfdbdab443ae..9f07f4947b63 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -2402,7 +2402,7 @@ static void enetc_clear_interrupts(struct enetc_ndev_priv *priv) static int enetc_phylink_connect(struct net_device *ndev) { struct enetc_ndev_priv *priv = netdev_priv(ndev); - struct ethtool_eee edata; + struct ethtool_keee edata; int err; if (!priv->phylink) { @@ -2418,7 +2418,7 @@ static int enetc_phylink_connect(struct net_device *ndev) } /* disable EEE autoneg, until ENETC driver supports it */ - memset(&edata, 0, sizeof(struct ethtool_eee)); + memset(&edata, 0, sizeof(struct ethtool_keee)); phylink_ethtool_set_eee(priv->phylink, &edata); phylink_start(priv->phylink); diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index a8fbcada6b01..a19cb2a786fd 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -672,7 +672,7 @@ struct fec_enet_private { unsigned int itr_clk_rate; /* tx lpi eee mode */ - struct ethtool_eee eee; + struct ethtool_keee eee; unsigned int clk_ref_rate; /* ptp clock period in ns*/ diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 432523b2c789..207f1f66c117 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -85,8 +85,6 @@ static int fec_enet_xdp_tx_xmit(struct fec_enet_private *fep, static const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; -/* Pause frame feild and FIFO threshold */ -#define FEC_ENET_FCE (1 << 5) #define FEC_ENET_RSEM_V 0x84 #define FEC_ENET_RSFL_V 16 #define FEC_ENET_RAEM_V 0x8 @@ -240,8 +238,8 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define PKT_MINBUF_SIZE 64 /* FEC receive acceleration */ -#define FEC_RACC_IPDIS (1 << 1) -#define FEC_RACC_PRODIS (1 << 2) +#define FEC_RACC_IPDIS BIT(1) +#define FEC_RACC_PRODIS BIT(2) #define FEC_RACC_SHIFT16 BIT(7) #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) @@ -273,8 +271,23 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_MMFR_TA (2 << 16) #define FEC_MMFR_DATA(v) (v & 0xffff) /* FEC ECR bits definition */ -#define FEC_ECR_MAGICEN (1 << 2) -#define FEC_ECR_SLEEP (1 << 3) +#define FEC_ECR_RESET BIT(0) +#define FEC_ECR_ETHEREN BIT(1) +#define FEC_ECR_MAGICEN BIT(2) +#define FEC_ECR_SLEEP BIT(3) +#define FEC_ECR_EN1588 BIT(4) +#define FEC_ECR_BYTESWP BIT(8) +/* FEC RCR bits definition */ +#define FEC_RCR_LOOP BIT(0) +#define FEC_RCR_HALFDPX BIT(1) +#define FEC_RCR_MII BIT(2) +#define FEC_RCR_PROMISC BIT(3) +#define FEC_RCR_BC_REJ BIT(4) +#define FEC_RCR_FLOWCTL BIT(5) +#define FEC_RCR_RMII BIT(8) +#define FEC_RCR_10BASET BIT(9) +/* TX WMARK bits */ +#define FEC_TXWMRK_STRFWD BIT(8) #define FEC_MII_TIMEOUT 30000 /* us */ @@ -1062,7 +1075,7 @@ fec_restart(struct net_device *ndev) struct fec_enet_private *fep = netdev_priv(ndev); u32 temp_mac[2]; u32 rcntl = OPT_FRAME_SIZE | 0x04; - u32 ecntl = 0x2; /* ETHEREN */ + u32 ecntl = FEC_ECR_ETHEREN; /* Whack a reset. We should wait for this. * For i.MX6SX SOC, enet use AXI bus, we use disable MAC @@ -1137,18 +1150,18 @@ fec_restart(struct net_device *ndev) fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) rcntl |= (1 << 6); else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) - rcntl |= (1 << 8); + rcntl |= FEC_RCR_RMII; else - rcntl &= ~(1 << 8); + rcntl &= ~FEC_RCR_RMII; /* 1G, 100M or 10M */ if (ndev->phydev) { if (ndev->phydev->speed == SPEED_1000) ecntl |= (1 << 5); else if (ndev->phydev->speed == SPEED_100) - rcntl &= ~(1 << 9); + rcntl &= ~FEC_RCR_10BASET; else - rcntl |= (1 << 9); + rcntl |= FEC_RCR_10BASET; } } else { #ifdef FEC_MIIGSK_ENR @@ -1181,7 +1194,7 @@ fec_restart(struct net_device *ndev) if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && ndev->phydev && ndev->phydev->pause)) { - rcntl |= FEC_ENET_FCE; + rcntl |= FEC_RCR_FLOWCTL; /* set FIFO threshold parameter to reduce overrun */ writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); @@ -1192,7 +1205,7 @@ fec_restart(struct net_device *ndev) /* OPD */ writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); } else { - rcntl &= ~FEC_ENET_FCE; + rcntl &= ~FEC_RCR_FLOWCTL; } #endif /* !defined(CONFIG_M5272) */ @@ -1207,13 +1220,13 @@ fec_restart(struct net_device *ndev) if (fep->quirks & FEC_QUIRK_ENET_MAC) { /* enable ENET endian swap */ - ecntl |= (1 << 8); + ecntl |= FEC_ECR_BYTESWP; /* enable ENET store and forward mode */ - writel(1 << 8, fep->hwp + FEC_X_WMRK); + writel(FEC_TXWMRK_STRFWD, fep->hwp + FEC_X_WMRK); } if (fep->bufdesc_ex) - ecntl |= (1 << 4); + ecntl |= FEC_ECR_EN1588; if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && fep->rgmii_txc_dly) @@ -1312,7 +1325,7 @@ static void fec_stop(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); - u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); + u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & FEC_RCR_RMII; u32 val; /* We cannot expect a graceful transmit stop without link !!! */ @@ -1331,7 +1344,7 @@ fec_stop(struct net_device *ndev) if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { writel(0, fep->hwp + FEC_ECNTRL); } else { - writel(1, fep->hwp + FEC_ECNTRL); + writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL); udelay(10); } } else { @@ -1345,12 +1358,11 @@ fec_stop(struct net_device *ndev) /* We have to keep ENET enabled to have MII interrupt stay working */ if (fep->quirks & FEC_QUIRK_ENET_MAC && !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { - writel(2, fep->hwp + FEC_ECNTRL); + writel(FEC_ECR_ETHEREN, fep->hwp + FEC_ECNTRL); writel(rmii_mode, fep->hwp + FEC_R_CNTRL); } } - static void fec_timeout(struct net_device *ndev, unsigned int txqueue) { @@ -3122,7 +3134,7 @@ static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) { struct fec_enet_private *fep = netdev_priv(ndev); - struct ethtool_eee *p = &fep->eee; + struct ethtool_keee *p = &fep->eee; unsigned int sleep_cycle, wake_cycle; int ret = 0; @@ -3139,8 +3151,6 @@ static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) } p->tx_lpi_enabled = enable; - p->eee_enabled = enable; - p->eee_active = enable; writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); @@ -3149,10 +3159,10 @@ static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) } static int -fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +fec_enet_get_eee(struct net_device *ndev, struct ethtool_keee *edata) { struct fec_enet_private *fep = netdev_priv(ndev); - struct ethtool_eee *p = &fep->eee; + struct ethtool_keee *p = &fep->eee; if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) return -EOPNOTSUPP; @@ -3160,8 +3170,6 @@ fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) if (!netif_running(ndev)) return -ENETDOWN; - edata->eee_enabled = p->eee_enabled; - edata->eee_active = p->eee_active; edata->tx_lpi_timer = p->tx_lpi_timer; edata->tx_lpi_enabled = p->tx_lpi_enabled; @@ -3169,10 +3177,10 @@ fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) } static int -fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +fec_enet_set_eee(struct net_device *ndev, struct ethtool_keee *edata) { struct fec_enet_private *fep = netdev_priv(ndev); - struct ethtool_eee *p = &fep->eee; + struct ethtool_keee *p = &fep->eee; int ret = 0; if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index e3dfbd7a4236..a811238c018d 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1649,7 +1649,7 @@ static int init_phy(struct net_device *dev) struct gfar_private *priv = netdev_priv(dev); phy_interface_t interface = priv->interface; struct phy_device *phydev; - struct ethtool_eee edata; + struct ethtool_keee edata; linkmode_set_bit_array(phy_10_100_features_array, ARRAY_SIZE(phy_10_100_features_array), @@ -1681,7 +1681,7 @@ static int init_phy(struct net_device *dev) phy_support_asym_pause(phydev); /* disable EEE autoneg, EEE not supported by eTSEC */ - memset(&edata, 0, sizeof(struct ethtool_eee)); + memset(&edata, 0, sizeof(struct ethtool_keee)); phy_ethtool_set_eee(phydev, &edata); return 0; diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index c32b3d40beb0..20f5a9e7fae9 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -402,7 +402,7 @@ static enum pkt_hash_types gve_rss_type(__be16 pkt_flags) static struct sk_buff *gve_rx_add_frags(struct napi_struct *napi, struct gve_rx_slot_page_info *page_info, - u16 packet_buffer_size, u16 len, + unsigned int truesize, u16 len, struct gve_rx_ctx *ctx) { u32 offset = page_info->page_offset + page_info->pad; @@ -435,10 +435,10 @@ static struct sk_buff *gve_rx_add_frags(struct napi_struct *napi, if (skb != ctx->skb_head) { ctx->skb_head->len += len; ctx->skb_head->data_len += len; - ctx->skb_head->truesize += packet_buffer_size; + ctx->skb_head->truesize += truesize; } skb_add_rx_frag(skb, num_frags, page_info->page, - offset, len, packet_buffer_size); + offset, len, truesize); return ctx->skb_head; } @@ -532,7 +532,7 @@ static struct sk_buff *gve_rx_copy_to_pool(struct gve_rx_ring *rx, memcpy(alloc_page_info.page_address, src, page_info->pad + len); skb = gve_rx_add_frags(napi, &alloc_page_info, - rx->packet_buffer_size, + PAGE_SIZE, len, ctx); u64_stats_update_begin(&rx->statss); diff --git a/drivers/net/ethernet/i825xx/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c index 5e27470c6b1e..f2d4669c81cf 100644 --- a/drivers/net/ethernet/i825xx/sun3_82586.c +++ b/drivers/net/ethernet/i825xx/sun3_82586.c @@ -987,7 +987,7 @@ static void sun3_82586_timeout(struct net_device *dev, unsigned int txqueue) { #ifdef DEBUG printk("%s: xmitter timed out, try to restart! stat: %02x\n",dev->name,p->scb->cus); - printk("%s: command-stats: %04x %04x\n",dev->name,swab16(p->xmit_cmds[0]->cmd_status),swab16(p->xmit_cmds[1]->cmd_status)); + printk("%s: command-stats: %04x\n", dev->name, swab16(p->xmit_cmds[0]->cmd_status)); printk("%s: check, whether you set the right interrupt number!\n",dev->name); #endif sun3_82586_close(dev); diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index d55638ad8704..767358b60507 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -368,6 +368,14 @@ config IGC To compile this driver as a module, choose M here. The module will be called igc. + +config IGC_LEDS + def_bool LEDS_TRIGGER_NETDEV + depends on IGC && LEDS_CLASS + help + Optional support for controlling the NIC LED's with the netdev + LED trigger. + config IDPF tristate "Intel(R) Infrastructure Data Path Function Support" depends on PCI_MSI diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index a187582d2299..ba9c19e6994c 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -360,23 +360,43 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca); * As a result, a shift of INCVALUE_SHIFT_n is used to fit a value of * INCVALUE_n into the TIMINCA register allowing 32+8+(24-INCVALUE_SHIFT_n) * bits to count nanoseconds leaving the rest for fractional nonseconds. + * + * Any given INCVALUE also has an associated maximum adjustment value. This + * maximum adjustment value is the largest increase (or decrease) which can be + * safely applied without overflowing the INCVALUE. Since INCVALUE has + * a maximum range of 24 bits, its largest value is 0xFFFFFF. + * + * To understand where the maximum value comes from, consider the following + * equation: + * + * new_incval = base_incval + (base_incval * adjustment) / 1billion + * + * To avoid overflow that means: + * max_incval = base_incval + (base_incval * max_adj) / billion + * + * Re-arranging: + * max_adj = floor(((max_incval - base_incval) * 1billion) / 1billion) */ #define INCVALUE_96MHZ 125 #define INCVALUE_SHIFT_96MHZ 17 #define INCPERIOD_SHIFT_96MHZ 2 #define INCPERIOD_96MHZ (12 >> INCPERIOD_SHIFT_96MHZ) +#define MAX_PPB_96MHZ 23999900 /* 23,999,900 ppb */ #define INCVALUE_25MHZ 40 #define INCVALUE_SHIFT_25MHZ 18 #define INCPERIOD_25MHZ 1 +#define MAX_PPB_25MHZ 599999900 /* 599,999,900 ppb */ #define INCVALUE_24MHZ 125 #define INCVALUE_SHIFT_24MHZ 14 #define INCPERIOD_24MHZ 3 +#define MAX_PPB_24MHZ 999999999 /* 999,999,999 ppb */ #define INCVALUE_38400KHZ 26 #define INCVALUE_SHIFT_38400KHZ 19 #define INCPERIOD_38400KHZ 1 +#define MAX_PPB_38400KHZ 230769100 /* 230,769,100 ppb */ /* Another drawback of scaling the incvalue by a large factor is the * 64-bit SYSTIM register overflows more quickly. This is dealt with diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index fc0f98ea6133..ff243ae71b78 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -2186,7 +2186,7 @@ static int e1000_get_rxnfc(struct net_device *netdev, } } -static int e1000e_get_eee(struct net_device *netdev, struct ethtool_eee *edata) +static int e1000e_get_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -2223,16 +2223,16 @@ static int e1000e_get_eee(struct net_device *netdev, struct ethtool_eee *edata) ret_val = e1000_read_emi_reg_locked(hw, cap_addr, &phy_data); if (ret_val) goto release; - edata->supported = mmd_eee_cap_to_ethtool_sup_t(phy_data); + edata->supported_u32 = mmd_eee_cap_to_ethtool_sup_t(phy_data); /* EEE Advertised */ - edata->advertised = mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert); + edata->advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert); /* EEE Link Partner Advertised */ ret_val = e1000_read_emi_reg_locked(hw, lpa_addr, &phy_data); if (ret_val) goto release; - edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data); + edata->lp_advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(phy_data); /* EEE PCS Status */ ret_val = e1000_read_emi_reg_locked(hw, pcs_stat_addr, &phy_data); @@ -2262,11 +2262,11 @@ release: return ret_val; } -static int e1000e_set_eee(struct net_device *netdev, struct ethtool_eee *edata) +static int e1000e_set_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - struct ethtool_eee eee_curr; + struct ethtool_keee eee_curr; s32 ret_val; ret_val = e1000e_get_eee(netdev, &eee_curr); @@ -2283,12 +2283,12 @@ static int e1000e_set_eee(struct net_device *netdev, struct ethtool_eee *edata) return -EINVAL; } - if (edata->advertised & ~(ADVERTISE_100_FULL | ADVERTISE_1000_FULL)) { + if (edata->advertised_u32 & ~(ADVERTISE_100_FULL | ADVERTISE_1000_FULL)) { e_err("EEE advertisement supports only 100TX and/or 1000T full-duplex\n"); return -EINVAL; } - adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised); + adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised_u32); hw->dev_spec.ich8lan.eee_disable = !edata->eee_enabled; diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index 02d871bc112a..bbcfd529399b 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -280,8 +280,17 @@ void e1000e_ptp_init(struct e1000_adapter *adapter) switch (hw->mac.type) { case e1000_pch2lan: + adapter->ptp_clock_info.max_adj = MAX_PPB_96MHZ; + break; case e1000_pch_lpt: + if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) + adapter->ptp_clock_info.max_adj = MAX_PPB_96MHZ; + else + adapter->ptp_clock_info.max_adj = MAX_PPB_25MHZ; + break; case e1000_pch_spt: + adapter->ptp_clock_info.max_adj = MAX_PPB_24MHZ; + break; case e1000_pch_cnp: case e1000_pch_tgp: case e1000_pch_adp: @@ -289,15 +298,14 @@ void e1000e_ptp_init(struct e1000_adapter *adapter) case e1000_pch_lnp: case e1000_pch_ptp: case e1000_pch_nvp: - if ((hw->mac.type < e1000_pch_lpt) || - (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI)) { - adapter->ptp_clock_info.max_adj = 24000000 - 1; - break; - } - fallthrough; + if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) + adapter->ptp_clock_info.max_adj = MAX_PPB_24MHZ; + else + adapter->ptp_clock_info.max_adj = MAX_PPB_38400KHZ; + break; case e1000_82574: case e1000_82583: - adapter->ptp_clock_info.max_adj = 600000000 - 1; + adapter->ptp_clock_info.max_adj = MAX_PPB_25MHZ; break; default: break; diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 9b701615c7c6..ba24f3fa92c3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -687,6 +687,54 @@ struct i40e_pf { }; /** + * __i40e_pf_next_vsi - get next valid VSI + * @pf: pointer to the PF struct + * @idx: pointer to start position number + * + * Find and return next non-NULL VSI pointer in pf->vsi array and + * updates idx position. Returns NULL if no VSI is found. + **/ +static __always_inline struct i40e_vsi * +__i40e_pf_next_vsi(struct i40e_pf *pf, int *idx) +{ + while (*idx < pf->num_alloc_vsi) { + if (pf->vsi[*idx]) + return pf->vsi[*idx]; + (*idx)++; + } + return NULL; +} + +#define i40e_pf_for_each_vsi(_pf, _i, _vsi) \ + for (_i = 0, _vsi = __i40e_pf_next_vsi(_pf, &_i); \ + _vsi; \ + _i++, _vsi = __i40e_pf_next_vsi(_pf, &_i)) + +/** + * __i40e_pf_next_veb - get next valid VEB + * @pf: pointer to the PF struct + * @idx: pointer to start position number + * + * Find and return next non-NULL VEB pointer in pf->veb array and + * updates idx position. Returns NULL if no VEB is found. + **/ +static __always_inline struct i40e_veb * +__i40e_pf_next_veb(struct i40e_pf *pf, int *idx) +{ + while (*idx < I40E_MAX_VEB) { + if (pf->veb[*idx]) + return pf->veb[*idx]; + (*idx)++; + } + return NULL; +} + +#define i40e_pf_for_each_veb(_pf, _i, _veb) \ + for (_i = 0, _veb = __i40e_pf_next_veb(_pf, &_i); \ + _veb; \ + _i++, _veb = __i40e_pf_next_veb(_pf, &_i)) + +/** * i40e_mac_to_hkey - Convert a 6-byte MAC Address to a u64 hash key * @macaddr: the MAC Address as the base key * @@ -735,7 +783,6 @@ struct i40e_new_mac_filter { struct i40e_veb { struct i40e_pf *pf; u16 idx; - u16 veb_idx; /* index of VEB parent */ u16 seid; u16 uplink_seid; u16 stats_idx; /* index of VEB parent */ @@ -1120,14 +1167,12 @@ struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id); static inline struct i40e_vsi * i40e_find_vsi_by_type(struct i40e_pf *pf, u16 type) { + struct i40e_vsi *vsi; int i; - for (i = 0; i < pf->num_alloc_vsi; i++) { - struct i40e_vsi *vsi = pf->vsi[i]; - - if (vsi && vsi->type == type) + i40e_pf_for_each_vsi(pf, i, vsi) + if (vsi->type == type) return vsi; - } return NULL; } @@ -1309,4 +1354,40 @@ static inline struct i40e_pf *i40e_hw_to_pf(struct i40e_hw *hw) struct device *i40e_hw_to_dev(struct i40e_hw *hw); +/** + * i40e_pf_get_vsi_by_seid - find VSI by SEID + * @pf: pointer to a PF + * @seid: SEID of the VSI + **/ +static inline struct i40e_vsi * +i40e_pf_get_vsi_by_seid(struct i40e_pf *pf, u16 seid) +{ + struct i40e_vsi *vsi; + int i; + + i40e_pf_for_each_vsi(pf, i, vsi) + if (vsi->seid == seid) + return vsi; + + return NULL; +} + +/** + * i40e_pf_get_veb_by_seid - find VEB by SEID + * @pf: pointer to a PF + * @seid: SEID of the VSI + **/ +static inline struct i40e_veb * +i40e_pf_get_veb_by_seid(struct i40e_pf *pf, u16 seid) +{ + struct i40e_veb *veb; + int i; + + i40e_pf_for_each_veb(pf, i, veb) + if (veb->seid == seid) + return veb; + + return NULL; +} + #endif /* _I40E_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 9d88ed6105fd..8db1eb0c1768 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -1523,7 +1523,7 @@ void i40e_dcb_hw_rx_ets_bw_config(struct i40e_hw *hw, u8 *bw_share, reg = rd32(hw, I40E_PRTDCB_RETSTCC(i)); reg &= ~(I40E_PRTDCB_RETSTCC_BWSHARE_MASK | I40E_PRTDCB_RETSTCC_UPINTC_MODE_MASK | - I40E_PRTDCB_RETSTCC_ETSTC_SHIFT); + I40E_PRTDCB_RETSTCC_ETSTC_MASK); reg |= FIELD_PREP(I40E_PRTDCB_RETSTCC_BWSHARE_MASK, bw_share[i]); reg |= FIELD_PREP(I40E_PRTDCB_RETSTCC_UPINTC_MODE_MASK, diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h index 6b60dc9b7736..d76497566e40 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h @@ -43,7 +43,7 @@ #define I40E_LLDP_TLV_SUBTYPE_SHIFT 0 #define I40E_LLDP_TLV_SUBTYPE_MASK (0xFF << I40E_LLDP_TLV_SUBTYPE_SHIFT) #define I40E_LLDP_TLV_OUI_SHIFT 8 -#define I40E_LLDP_TLV_OUI_MASK (0xFFFFFF << I40E_LLDP_TLV_OUI_SHIFT) +#define I40E_LLDP_TLV_OUI_MASK (0xFFFFFFU << I40E_LLDP_TLV_OUI_SHIFT) /* Defines for IEEE ETS TLV */ #define I40E_IEEE_ETS_MAXTC_SHIFT 0 diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index b96a92187ab3..8aa43aefe84c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -947,16 +947,16 @@ static int i40e_dcbnl_vsi_del_app(struct i40e_vsi *vsi, static void i40e_dcbnl_del_app(struct i40e_pf *pf, struct i40e_dcb_app_priority_table *app) { + struct i40e_vsi *vsi; int v, err; - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (pf->vsi[v] && pf->vsi[v]->netdev) { - err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); + i40e_pf_for_each_vsi(pf, v, vsi) + if (vsi->netdev) { + err = i40e_dcbnl_vsi_del_app(vsi, app); dev_dbg(&pf->pdev->dev, "Deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", - pf->vsi[v]->seid, err, app->selector, + vsi->seid, err, app->selector, app->protocolid, app->priority); } - } } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index ef70ddbe9c2f..f9ba45f596c9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -24,31 +24,13 @@ enum ring_type { **/ static struct i40e_vsi *i40e_dbg_find_vsi(struct i40e_pf *pf, int seid) { - int i; - - if (seid < 0) + if (seid < 0) { dev_info(&pf->pdev->dev, "%d: bad seid\n", seid); - else - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i] && (pf->vsi[i]->seid == seid)) - return pf->vsi[i]; - - return NULL; -} -/** - * i40e_dbg_find_veb - searches for the veb with the given seid - * @pf: the PF structure to search for the veb - * @seid: seid of the veb it is searching for - **/ -static struct i40e_veb *i40e_dbg_find_veb(struct i40e_pf *pf, int seid) -{ - int i; + return NULL; + } - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i] && pf->veb[i]->seid == seid) - return pf->veb[i]; - return NULL; + return i40e_pf_get_vsi_by_seid(pf, seid); } /************************************************************** @@ -653,12 +635,11 @@ out: **/ static void i40e_dbg_dump_vsi_no_seid(struct i40e_pf *pf) { + struct i40e_vsi *vsi; int i; - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i]) - dev_info(&pf->pdev->dev, "dump vsi[%d]: %d\n", - i, pf->vsi[i]->seid); + i40e_pf_for_each_vsi(pf, i, vsi) + dev_info(&pf->pdev->dev, "dump vsi[%d]: %d\n", i, vsi->seid); } /** @@ -696,15 +677,14 @@ static void i40e_dbg_dump_veb_seid(struct i40e_pf *pf, int seid) { struct i40e_veb *veb; - veb = i40e_dbg_find_veb(pf, seid); + veb = i40e_pf_get_veb_by_seid(pf, seid); if (!veb) { dev_info(&pf->pdev->dev, "can't find veb %d\n", seid); return; } dev_info(&pf->pdev->dev, - "veb idx=%d,%d stats_ic=%d seid=%d uplink=%d mode=%s\n", - veb->idx, veb->veb_idx, veb->stats_idx, veb->seid, - veb->uplink_seid, + "veb idx=%d stats_ic=%d seid=%d uplink=%d mode=%s\n", + veb->idx, veb->stats_idx, veb->seid, veb->uplink_seid, veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); i40e_dbg_dump_eth_stats(pf, &veb->stats); } @@ -718,11 +698,8 @@ static void i40e_dbg_dump_veb_all(struct i40e_pf *pf) struct i40e_veb *veb; int i; - for (i = 0; i < I40E_MAX_VEB; i++) { - veb = pf->veb[i]; - if (veb) - i40e_dbg_dump_veb_seid(pf, veb->seid); - } + i40e_pf_for_each_veb(pf, i, veb) + i40e_dbg_dump_veb_seid(pf, veb->seid); } /** @@ -851,10 +828,14 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "add relay", 9) == 0) { struct i40e_veb *veb; - int uplink_seid, i; + u8 enabled_tc = 0x1; + int uplink_seid; cnt = sscanf(&cmd_buf[9], "%i %i", &uplink_seid, &vsi_seid); - if (cnt != 2) { + if (cnt == 0) { + uplink_seid = 0; + vsi_seid = 0; + } else if (cnt != 2) { dev_info(&pf->pdev->dev, "add relay: bad command string, cnt=%d\n", cnt); @@ -866,33 +847,36 @@ static ssize_t i40e_dbg_command_write(struct file *filp, goto command_write_done; } - vsi = i40e_dbg_find_vsi(pf, vsi_seid); - if (!vsi) { - dev_info(&pf->pdev->dev, - "add relay: VSI %d not found\n", vsi_seid); - goto command_write_done; - } - - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i] && pf->veb[i]->seid == uplink_seid) - break; - if (i >= I40E_MAX_VEB && uplink_seid != 0 && - uplink_seid != pf->mac_seid) { + if (uplink_seid != 0 && uplink_seid != pf->mac_seid) { dev_info(&pf->pdev->dev, "add relay: relay uplink %d not found\n", uplink_seid); goto command_write_done; + } else if (uplink_seid) { + vsi = i40e_pf_get_vsi_by_seid(pf, vsi_seid); + if (!vsi) { + dev_info(&pf->pdev->dev, + "add relay: VSI %d not found\n", + vsi_seid); + goto command_write_done; + } + enabled_tc = vsi->tc_config.enabled_tc; + } else if (vsi_seid) { + dev_info(&pf->pdev->dev, + "add relay: VSI must be 0 for floating relay\n"); + goto command_write_done; } - veb = i40e_veb_setup(pf, 0, uplink_seid, vsi_seid, - vsi->tc_config.enabled_tc); + veb = i40e_veb_setup(pf, 0, uplink_seid, vsi_seid, enabled_tc); if (veb) dev_info(&pf->pdev->dev, "added relay %d\n", veb->seid); else dev_info(&pf->pdev->dev, "add relay failed\n"); } else if (strncmp(cmd_buf, "del relay", 9) == 0) { + struct i40e_veb *veb; int i; + cnt = sscanf(&cmd_buf[9], "%i", &veb_seid); if (cnt != 1) { dev_info(&pf->pdev->dev, @@ -906,9 +890,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } /* find the veb */ - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i] && pf->veb[i]->seid == veb_seid) + i40e_pf_for_each_veb(pf, i, veb) + if (veb->seid == veb_seid) break; + if (i >= I40E_MAX_VEB) { dev_info(&pf->pdev->dev, "del relay: relay %d not found\n", veb_seid); @@ -916,7 +901,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } dev_info(&pf->pdev->dev, "deleting relay %d\n", veb_seid); - i40e_veb_release(pf->veb[i]); + i40e_veb_release(veb); } else if (strncmp(cmd_buf, "add pvid", 8) == 0) { unsigned int v; int ret; @@ -1251,8 +1236,8 @@ static ssize_t i40e_dbg_command_write(struct file *filp, if (cnt == 0) { int i; - for (i = 0; i < pf->num_alloc_vsi; i++) - i40e_vsi_reset_stats(pf->vsi[i]); + i40e_pf_for_each_vsi(pf, i, vsi) + i40e_vsi_reset_stats(vsi); dev_info(&pf->pdev->dev, "vsi clear stats called for all vsi's\n"); } else if (cnt == 1) { vsi = i40e_dbg_find_vsi(pf, vsi_seid); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index c841779713f6..1b5473358e1a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -5644,7 +5644,7 @@ static int i40e_get_module_eeprom(struct net_device *netdev, return 0; } -static int i40e_get_eee(struct net_device *netdev, struct ethtool_eee *edata) +static int i40e_get_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_aq_get_phy_abilities_resp phy_cfg; @@ -5664,16 +5664,16 @@ static int i40e_get_eee(struct net_device *netdev, struct ethtool_eee *edata) if (phy_cfg.eee_capability == 0) return -EOPNOTSUPP; - edata->supported = SUPPORTED_Autoneg; - edata->lp_advertised = edata->supported; + edata->supported_u32 = SUPPORTED_Autoneg; + edata->lp_advertised_u32 = edata->supported_u32; /* Get current configuration */ status = i40e_aq_get_phy_capabilities(hw, false, false, &phy_cfg, NULL); if (status) return -EAGAIN; - edata->advertised = phy_cfg.eee_capability ? SUPPORTED_Autoneg : 0U; - edata->eee_enabled = !!edata->advertised; + edata->advertised_u32 = phy_cfg.eee_capability ? SUPPORTED_Autoneg : 0U; + edata->eee_enabled = !!edata->advertised_u32; edata->tx_lpi_enabled = pf->stats.tx_lpi_status; edata->eee_active = pf->stats.tx_lpi_status && pf->stats.rx_lpi_status; @@ -5682,7 +5682,7 @@ static int i40e_get_eee(struct net_device *netdev, struct ethtool_eee *edata) } static int i40e_is_eee_param_supported(struct net_device *netdev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -5691,7 +5691,7 @@ static int i40e_is_eee_param_supported(struct net_device *netdev, u32 value; const char *name; } param[] = { - {edata->advertised & ~SUPPORTED_Autoneg, "advertise"}, + {edata->advertised_u32 & ~SUPPORTED_Autoneg, "advertise"}, {edata->tx_lpi_timer, "tx-timer"}, {edata->tx_lpi_enabled != pf->stats.tx_lpi_status, "tx-lpi"} }; @@ -5709,7 +5709,7 @@ static int i40e_is_eee_param_supported(struct net_device *netdev, return 0; } -static int i40e_set_eee(struct net_device *netdev, struct ethtool_eee *edata) +static int i40e_set_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_aq_get_phy_abilities_resp abilities; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 6e7fd473abfd..f12092cdb1f0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -310,11 +310,12 @@ static int i40e_put_lump(struct i40e_lump_tracking *pile, u16 index, u16 id) **/ struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id) { + struct i40e_vsi *vsi; int i; - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i] && (pf->vsi[i]->id == id)) - return pf->vsi[i]; + i40e_pf_for_each_vsi(pf, i, vsi) + if (vsi->id == id) + return vsi; return NULL; } @@ -552,24 +553,19 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi) **/ void i40e_pf_reset_stats(struct i40e_pf *pf) { + struct i40e_veb *veb; int i; memset(&pf->stats, 0, sizeof(pf->stats)); memset(&pf->stats_offsets, 0, sizeof(pf->stats_offsets)); pf->stat_offsets_loaded = false; - for (i = 0; i < I40E_MAX_VEB; i++) { - if (pf->veb[i]) { - memset(&pf->veb[i]->stats, 0, - sizeof(pf->veb[i]->stats)); - memset(&pf->veb[i]->stats_offsets, 0, - sizeof(pf->veb[i]->stats_offsets)); - memset(&pf->veb[i]->tc_stats, 0, - sizeof(pf->veb[i]->tc_stats)); - memset(&pf->veb[i]->tc_stats_offsets, 0, - sizeof(pf->veb[i]->tc_stats_offsets)); - pf->veb[i]->stat_offsets_loaded = false; - } + i40e_pf_for_each_veb(pf, i, veb) { + memset(&veb->stats, 0, sizeof(veb->stats)); + memset(&veb->stats_offsets, 0, sizeof(veb->stats_offsets)); + memset(&veb->tc_stats, 0, sizeof(veb->tc_stats)); + memset(&veb->tc_stats_offsets, 0, sizeof(veb->tc_stats_offsets)); + veb->stat_offsets_loaded = false; } pf->hw_csum_rx_error = 0; } @@ -2879,6 +2875,7 @@ err_no_memory_locked: **/ static void i40e_sync_filters_subtask(struct i40e_pf *pf) { + struct i40e_vsi *vsi; int v; if (!pf) @@ -2890,11 +2887,10 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) return; } - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (pf->vsi[v] && - (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED) && - !test_bit(__I40E_VSI_RELEASING, pf->vsi[v]->state)) { - int ret = i40e_sync_vsi_filters(pf->vsi[v]); + i40e_pf_for_each_vsi(pf, v, vsi) { + if ((vsi->flags & I40E_VSI_FLAG_FILTER_CHANGED) && + !test_bit(__I40E_VSI_RELEASING, vsi->state)) { + int ret = i40e_sync_vsi_filters(vsi); if (ret) { /* come back and try again later */ @@ -4926,27 +4922,23 @@ int i40e_vsi_start_rings(struct i40e_vsi *vsi) void i40e_vsi_stop_rings(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; - int pf_q, err, q_end; + u32 pf_q, tx_q_end, rx_q_end; /* When port TX is suspended, don't wait */ if (test_bit(__I40E_PORT_SUSPENDED, vsi->back->state)) return i40e_vsi_stop_rings_no_wait(vsi); - q_end = vsi->base_queue + vsi->num_queue_pairs; - for (pf_q = vsi->base_queue; pf_q < q_end; pf_q++) - i40e_pre_tx_queue_cfg(&pf->hw, (u32)pf_q, false); + tx_q_end = vsi->base_queue + + vsi->alloc_queue_pairs * (i40e_enabled_xdp_vsi(vsi) ? 2 : 1); + for (pf_q = vsi->base_queue; pf_q < tx_q_end; pf_q++) + i40e_pre_tx_queue_cfg(&pf->hw, pf_q, false); - for (pf_q = vsi->base_queue; pf_q < q_end; pf_q++) { - err = i40e_control_wait_rx_q(pf, pf_q, false); - if (err) - dev_info(&pf->pdev->dev, - "VSI seid %d Rx ring %d disable timeout\n", - vsi->seid, pf_q); - } + rx_q_end = vsi->base_queue + vsi->num_queue_pairs; + for (pf_q = vsi->base_queue; pf_q < rx_q_end; pf_q++) + i40e_control_rx_q(pf, pf_q, false); msleep(I40E_DISABLE_TX_GAP_MSEC); - pf_q = vsi->base_queue; - for (pf_q = vsi->base_queue; pf_q < q_end; pf_q++) + for (pf_q = vsi->base_queue; pf_q < tx_q_end; pf_q++) wr32(&pf->hw, I40E_QTX_ENA(pf_q), 0); i40e_vsi_wait_queues_disabled(vsi); @@ -5170,6 +5162,7 @@ static void i40e_reset_interrupt_capability(struct i40e_pf *pf) **/ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf) { + struct i40e_vsi *vsi; int i; if (test_bit(__I40E_MISC_IRQ_REQUESTED, pf->state)) @@ -5179,9 +5172,10 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf) I40E_IWARP_IRQ_PILE_ID); i40e_put_lump(pf->irq_pile, 0, I40E_PILE_VALID_BIT-1); - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i]) - i40e_vsi_free_q_vectors(pf->vsi[i]); + + i40e_pf_for_each_vsi(pf, i, vsi) + i40e_vsi_free_q_vectors(vsi); + i40e_reset_interrupt_capability(pf); } @@ -5278,12 +5272,11 @@ static void i40e_unquiesce_vsi(struct i40e_vsi *vsi) **/ static void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf) { + struct i40e_vsi *vsi; int v; - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (pf->vsi[v]) - i40e_quiesce_vsi(pf->vsi[v]); - } + i40e_pf_for_each_vsi(pf, v, vsi) + i40e_quiesce_vsi(vsi); } /** @@ -5292,12 +5285,11 @@ static void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf) **/ static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf) { + struct i40e_vsi *vsi; int v; - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (pf->vsi[v]) - i40e_unquiesce_vsi(pf->vsi[v]); - } + i40e_pf_for_each_vsi(pf, v, vsi) + i40e_unquiesce_vsi(vsi); } /** @@ -5358,14 +5350,13 @@ wait_rx: **/ static int i40e_pf_wait_queues_disabled(struct i40e_pf *pf) { + struct i40e_vsi *vsi; int v, ret = 0; - for (v = 0; v < pf->hw.func_caps.num_vsis; v++) { - if (pf->vsi[v]) { - ret = i40e_vsi_wait_queues_disabled(pf->vsi[v]); - if (ret) - break; - } + i40e_pf_for_each_vsi(pf, v, vsi) { + ret = i40e_vsi_wait_queues_disabled(vsi); + if (ret) + break; } return ret; @@ -6782,32 +6773,29 @@ out: **/ static void i40e_dcb_reconfigure(struct i40e_pf *pf) { + struct i40e_vsi *vsi; + struct i40e_veb *veb; u8 tc_map = 0; int ret; - u8 v; + int v; /* Enable the TCs available on PF to all VEBs */ tc_map = i40e_pf_get_tc_map(pf); if (tc_map == I40E_DEFAULT_TRAFFIC_CLASS) return; - for (v = 0; v < I40E_MAX_VEB; v++) { - if (!pf->veb[v]) - continue; - ret = i40e_veb_config_tc(pf->veb[v], tc_map); + i40e_pf_for_each_veb(pf, v, veb) { + ret = i40e_veb_config_tc(veb, tc_map); if (ret) { dev_info(&pf->pdev->dev, "Failed configuring TC for VEB seid=%d\n", - pf->veb[v]->seid); + veb->seid); /* Will try to configure as many components */ } } /* Update each VSI */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (!pf->vsi[v]) - continue; - + i40e_pf_for_each_vsi(pf, v, vsi) { /* - Enable all TCs for the LAN VSI * - For all others keep them at TC0 for now */ @@ -6816,17 +6804,17 @@ static void i40e_dcb_reconfigure(struct i40e_pf *pf) else tc_map = I40E_DEFAULT_TRAFFIC_CLASS; - ret = i40e_vsi_config_tc(pf->vsi[v], tc_map); + ret = i40e_vsi_config_tc(vsi, tc_map); if (ret) { dev_info(&pf->pdev->dev, "Failed configuring TC for VSI seid=%d\n", - pf->vsi[v]->seid); + vsi->seid); /* Will try to configure as many components */ } else { /* Re-configure VSI vectors based on updated TC map */ - i40e_vsi_map_rings_to_vectors(pf->vsi[v]); - if (pf->vsi[v]->netdev) - i40e_dcbnl_set_all(pf->vsi[v]); + i40e_vsi_map_rings_to_vectors(vsi); + if (vsi->netdev) + i40e_dcbnl_set_all(vsi); } } } @@ -9261,7 +9249,9 @@ int i40e_close(struct net_device *netdev) **/ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) { + struct i40e_vsi *vsi; u32 val; + int i; /* do the biggest reset indicated */ if (reset_flags & BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED)) { @@ -9317,29 +9307,20 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) "FW LLDP is enabled\n"); } else if (reset_flags & BIT_ULL(__I40E_REINIT_REQUESTED)) { - int v; - /* Find the VSI(s) that requested a re-init */ - dev_info(&pf->pdev->dev, - "VSI reinit requested\n"); - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; + dev_info(&pf->pdev->dev, "VSI reinit requested\n"); - if (vsi != NULL && - test_and_clear_bit(__I40E_VSI_REINIT_REQUESTED, + i40e_pf_for_each_vsi(pf, i, vsi) { + if (test_and_clear_bit(__I40E_VSI_REINIT_REQUESTED, vsi->state)) - i40e_vsi_reinit_locked(pf->vsi[v]); + i40e_vsi_reinit_locked(vsi); } } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { - int v; - /* Find the VSI(s) that needs to be brought down */ dev_info(&pf->pdev->dev, "VSI down requested\n"); - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - if (vsi != NULL && - test_and_clear_bit(__I40E_VSI_DOWN_REQUESTED, + i40e_pf_for_each_vsi(pf, i, vsi) { + if (test_and_clear_bit(__I40E_VSI_DOWN_REQUESTED, vsi->state)) { set_bit(__I40E_VSI_DOWN, vsi->state); i40e_down(vsi); @@ -9892,6 +9873,7 @@ static void i40e_vsi_link_event(struct i40e_vsi *vsi, bool link_up) **/ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up) { + struct i40e_vsi *vsi; struct i40e_pf *pf; int i; @@ -9899,15 +9881,10 @@ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up) return; pf = veb->pf; - /* depth first... */ - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i] && (pf->veb[i]->uplink_seid == veb->seid)) - i40e_veb_link_event(pf->veb[i], link_up); - - /* ... now the local VSIs */ - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i] && (pf->vsi[i]->uplink_seid == veb->seid)) - i40e_vsi_link_event(pf->vsi[i], link_up); + /* Send link event to contained VSIs */ + i40e_pf_for_each_vsi(pf, i, vsi) + if (vsi->uplink_seid == veb->seid) + i40e_vsi_link_event(vsi, link_up); } /** @@ -9999,6 +9976,8 @@ static void i40e_link_event(struct i40e_pf *pf) **/ static void i40e_watchdog_subtask(struct i40e_pf *pf) { + struct i40e_vsi *vsi; + struct i40e_veb *veb; int i; /* if interface is down do nothing */ @@ -10019,15 +9998,14 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf) /* Update the stats for active netdevs so the network stack * can look at updated numbers whenever it cares to */ - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i] && pf->vsi[i]->netdev) - i40e_update_stats(pf->vsi[i]); + i40e_pf_for_each_vsi(pf, i, vsi) + if (vsi->netdev) + i40e_update_stats(vsi); if (test_bit(I40E_FLAG_VEB_STATS_ENA, pf->flags)) { /* Update the stats for the active switching components */ - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i]) - i40e_update_veb_stats(pf->veb[i]); + i40e_pf_for_each_veb(pf, i, veb) + i40e_update_veb_stats(veb); } i40e_ptp_rx_hang(pf); @@ -10372,89 +10350,84 @@ static void i40e_config_bridge_mode(struct i40e_veb *veb) } /** - * i40e_reconstitute_veb - rebuild the VEB and anything connected to it + * i40e_reconstitute_veb - rebuild the VEB and VSIs connected to it * @veb: pointer to the VEB instance * - * This is a recursive function that first builds the attached VSIs then - * recurses in to build the next layer of VEB. We track the connections - * through our own index numbers because the seid's from the HW could - * change across the reset. + * This is a function that builds the attached VSIs. We track the connections + * through our own index numbers because the seid's from the HW could change + * across the reset. **/ static int i40e_reconstitute_veb(struct i40e_veb *veb) { struct i40e_vsi *ctl_vsi = NULL; struct i40e_pf *pf = veb->pf; - int v, veb_idx; - int ret; + struct i40e_vsi *vsi; + int v, ret; - /* build VSI that owns this VEB, temporarily attached to base VEB */ - for (v = 0; v < pf->num_alloc_vsi && !ctl_vsi; v++) { - if (pf->vsi[v] && - pf->vsi[v]->veb_idx == veb->idx && - pf->vsi[v]->flags & I40E_VSI_FLAG_VEB_OWNER) { - ctl_vsi = pf->vsi[v]; - break; - } - } - if (!ctl_vsi) { - dev_info(&pf->pdev->dev, - "missing owner VSI for veb_idx %d\n", veb->idx); - ret = -ENOENT; - goto end_reconstitute; + /* As we do not maintain PV (port virtualizer) switch element then + * there can be only one non-floating VEB that have uplink to MAC SEID + * and its control VSI is the main one. + */ + if (WARN_ON(veb->uplink_seid && veb->uplink_seid != pf->mac_seid)) { + dev_err(&pf->pdev->dev, + "Invalid uplink SEID for VEB %d\n", veb->idx); + return -ENOENT; } - if (ctl_vsi != pf->vsi[pf->lan_vsi]) - ctl_vsi->uplink_seid = pf->vsi[pf->lan_vsi]->uplink_seid; - ret = i40e_add_vsi(ctl_vsi); - if (ret) { - dev_info(&pf->pdev->dev, - "rebuild of veb_idx %d owner VSI failed: %d\n", - veb->idx, ret); - goto end_reconstitute; + + if (veb->uplink_seid == pf->mac_seid) { + /* Check that the LAN VSI has VEB owning flag set */ + ctl_vsi = pf->vsi[pf->lan_vsi]; + + if (WARN_ON(ctl_vsi->veb_idx != veb->idx || + !(ctl_vsi->flags & I40E_VSI_FLAG_VEB_OWNER))) { + dev_err(&pf->pdev->dev, + "Invalid control VSI for VEB %d\n", veb->idx); + return -ENOENT; + } + + /* Add the control VSI to switch */ + ret = i40e_add_vsi(ctl_vsi); + if (ret) { + dev_err(&pf->pdev->dev, + "Rebuild of owner VSI for VEB %d failed: %d\n", + veb->idx, ret); + return ret; + } + + i40e_vsi_reset_stats(ctl_vsi); } - i40e_vsi_reset_stats(ctl_vsi); /* create the VEB in the switch and move the VSI onto the VEB */ ret = i40e_add_veb(veb, ctl_vsi); if (ret) - goto end_reconstitute; + return ret; - if (test_bit(I40E_FLAG_VEB_MODE_ENA, pf->flags)) - veb->bridge_mode = BRIDGE_MODE_VEB; - else - veb->bridge_mode = BRIDGE_MODE_VEPA; - i40e_config_bridge_mode(veb); + if (veb->uplink_seid) { + if (test_bit(I40E_FLAG_VEB_MODE_ENA, pf->flags)) + veb->bridge_mode = BRIDGE_MODE_VEB; + else + veb->bridge_mode = BRIDGE_MODE_VEPA; + i40e_config_bridge_mode(veb); + } /* create the remaining VSIs attached to this VEB */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (!pf->vsi[v] || pf->vsi[v] == ctl_vsi) + i40e_pf_for_each_vsi(pf, v, vsi) { + if (vsi == ctl_vsi) continue; - if (pf->vsi[v]->veb_idx == veb->idx) { - struct i40e_vsi *vsi = pf->vsi[v]; - + if (vsi->veb_idx == veb->idx) { vsi->uplink_seid = veb->seid; ret = i40e_add_vsi(vsi); if (ret) { dev_info(&pf->pdev->dev, "rebuild of vsi_idx %d failed: %d\n", v, ret); - goto end_reconstitute; + return ret; } i40e_vsi_reset_stats(vsi); } } - /* create any VEBs attached to this VEB - RECURSION */ - for (veb_idx = 0; veb_idx < I40E_MAX_VEB; veb_idx++) { - if (pf->veb[veb_idx] && pf->veb[veb_idx]->veb_idx == veb->idx) { - pf->veb[veb_idx]->uplink_seid = veb->seid; - ret = i40e_reconstitute_veb(pf->veb[veb_idx]); - if (ret) - break; - } - } - -end_reconstitute: return ret; } @@ -10722,6 +10695,7 @@ static void i40e_clean_xps_state(struct i40e_vsi *vsi) static void i40e_prep_for_reset(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; + struct i40e_vsi *vsi; int ret = 0; u32 v; @@ -10736,11 +10710,9 @@ static void i40e_prep_for_reset(struct i40e_pf *pf) /* quiesce the VSIs and their queues that are not already DOWN */ i40e_pf_quiesce_all_vsi(pf); - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (pf->vsi[v]) { - i40e_clean_xps_state(pf->vsi[v]); - pf->vsi[v]->seid = 0; - } + i40e_pf_for_each_vsi(pf, v, vsi) { + i40e_clean_xps_state(vsi); + vsi->seid = 0; } i40e_shutdown_adminq(&pf->hw); @@ -10854,6 +10826,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) const bool is_recovery_mode_reported = i40e_check_recovery_mode(pf); struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; struct i40e_hw *hw = &pf->hw; + struct i40e_veb *veb; int ret; u32 val; int v; @@ -10995,35 +10968,29 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) */ if (vsi->uplink_seid != pf->mac_seid) { dev_dbg(&pf->pdev->dev, "attempting to rebuild switch\n"); - /* find the one VEB connected to the MAC, and find orphans */ - for (v = 0; v < I40E_MAX_VEB; v++) { - if (!pf->veb[v]) - continue; - - if (pf->veb[v]->uplink_seid == pf->mac_seid || - pf->veb[v]->uplink_seid == 0) { - ret = i40e_reconstitute_veb(pf->veb[v]); - if (!ret) - continue; + /* Rebuild VEBs */ + i40e_pf_for_each_veb(pf, v, veb) { + ret = i40e_reconstitute_veb(veb); + if (!ret) + continue; - /* If Main VEB failed, we're in deep doodoo, - * so give up rebuilding the switch and set up - * for minimal rebuild of PF VSI. - * If orphan failed, we'll report the error - * but try to keep going. - */ - if (pf->veb[v]->uplink_seid == pf->mac_seid) { - dev_info(&pf->pdev->dev, - "rebuild of switch failed: %d, will try to set up simple PF connection\n", - ret); - vsi->uplink_seid = pf->mac_seid; - break; - } else if (pf->veb[v]->uplink_seid == 0) { - dev_info(&pf->pdev->dev, - "rebuild of orphan VEB failed: %d\n", - ret); - } + /* If Main VEB failed, we're in deep doodoo, + * so give up rebuilding the switch and set up + * for minimal rebuild of PF VSI. + * If orphan failed, we'll report the error + * but try to keep going. + */ + if (veb->uplink_seid == pf->mac_seid) { + dev_info(&pf->pdev->dev, + "rebuild of switch failed: %d, will try to set up simple PF connection\n", + ret); + vsi->uplink_seid = pf->mac_seid; + break; + } else if (veb->uplink_seid == 0) { + dev_info(&pf->pdev->dev, + "rebuild of orphan VEB failed: %d\n", + ret); } } } @@ -12102,6 +12069,7 @@ static int i40e_init_interrupt_scheme(struct i40e_pf *pf) */ static int i40e_restore_interrupt_scheme(struct i40e_pf *pf) { + struct i40e_vsi *vsi; int err, i; /* We cleared the MSI and MSI-X flags when disabling the old interrupt @@ -12118,13 +12086,12 @@ static int i40e_restore_interrupt_scheme(struct i40e_pf *pf) /* Now that we've re-acquired IRQs, we need to remap the vectors and * rings together again. */ - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i]) { - err = i40e_vsi_alloc_q_vectors(pf->vsi[i]); - if (err) - goto err_unwind; - i40e_vsi_map_rings_to_vectors(pf->vsi[i]); - } + i40e_pf_for_each_vsi(pf, i, vsi) { + err = i40e_vsi_alloc_q_vectors(vsi); + if (err) + goto err_unwind; + + i40e_vsi_map_rings_to_vectors(vsi); } err = i40e_setup_misc_vector(pf); @@ -13126,19 +13093,16 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev, struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - struct i40e_veb *veb = NULL; struct nlattr *attr, *br_spec; - int i, rem; + struct i40e_veb *veb; + int rem; /* Only for PF VSI for now */ if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) return -EOPNOTSUPP; /* Find the HW bridge for PF VSI */ - for (i = 0; i < I40E_MAX_VEB && !veb; i++) { - if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid) - veb = pf->veb[i]; - } + veb = i40e_pf_get_veb_by_seid(pf, vsi->uplink_seid); br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); if (!br_spec) @@ -13203,19 +13167,14 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - struct i40e_veb *veb = NULL; - int i; + struct i40e_veb *veb; /* Only for PF VSI for now */ if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) return -EOPNOTSUPP; /* Find the HW bridge for the PF VSI */ - for (i = 0; i < I40E_MAX_VEB && !veb; i++) { - if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid) - veb = pf->veb[i]; - } - + veb = i40e_pf_get_veb_by_seid(pf, vsi->uplink_seid); if (!veb) return 0; @@ -14149,7 +14108,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) { struct i40e_mac_filter *f; struct hlist_node *h; - struct i40e_veb *veb = NULL; + struct i40e_veb *veb; struct i40e_pf *pf; u16 uplink_seid; int i, n, bkt; @@ -14213,29 +14172,28 @@ int i40e_vsi_release(struct i40e_vsi *vsi) /* If this was the last thing on the VEB, except for the * controlling VSI, remove the VEB, which puts the controlling - * VSI onto the next level down in the switch. + * VSI onto the uplink port. * * Well, okay, there's one more exception here: don't remove - * the orphan VEBs yet. We'll wait for an explicit remove request + * the floating VEBs yet. We'll wait for an explicit remove request * from up the network stack. */ - for (n = 0, i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i] && - pf->vsi[i]->uplink_seid == uplink_seid && - (pf->vsi[i]->flags & I40E_VSI_FLAG_VEB_OWNER) == 0) { - n++; /* count the VSIs */ - } - } - for (i = 0; i < I40E_MAX_VEB; i++) { - if (!pf->veb[i]) - continue; - if (pf->veb[i]->uplink_seid == uplink_seid) - n++; /* count the VEBs */ - if (pf->veb[i]->seid == uplink_seid) - veb = pf->veb[i]; + veb = i40e_pf_get_veb_by_seid(pf, uplink_seid); + if (veb && veb->uplink_seid) { + n = 0; + + /* Count non-controlling VSIs present on the VEB */ + i40e_pf_for_each_vsi(pf, i, vsi) + if (vsi->uplink_seid == uplink_seid && + (vsi->flags & I40E_VSI_FLAG_VEB_OWNER) == 0) + n++; + + /* If there is no VSI except the control one then release + * the VEB and put the control VSI onto VEB uplink. + */ + if (!n) + i40e_veb_release(veb); } - if (n == 0 && veb && veb->uplink_seid != 0) - i40e_veb_release(veb); return 0; } @@ -14393,8 +14351,8 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, struct i40e_vsi *vsi = NULL; struct i40e_veb *veb = NULL; u16 alloc_queue_pairs; - int ret, i; int v_idx; + int ret; /* The requested uplink_seid must be either * - the PF's port seid @@ -14409,21 +14367,9 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, * * Find which uplink_seid we were given and create a new VEB if needed */ - for (i = 0; i < I40E_MAX_VEB; i++) { - if (pf->veb[i] && pf->veb[i]->seid == uplink_seid) { - veb = pf->veb[i]; - break; - } - } - + veb = i40e_pf_get_veb_by_seid(pf, uplink_seid); if (!veb && uplink_seid != pf->mac_seid) { - - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i] && pf->vsi[i]->seid == uplink_seid) { - vsi = pf->vsi[i]; - break; - } - } + vsi = i40e_pf_get_vsi_by_seid(pf, uplink_seid); if (!vsi) { dev_info(&pf->pdev->dev, "no such uplink_seid %d\n", uplink_seid); @@ -14452,10 +14398,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, } i40e_config_bridge_mode(veb); } - for (i = 0; i < I40E_MAX_VEB && !veb; i++) { - if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid) - veb = pf->veb[i]; - } + veb = i40e_pf_get_veb_by_seid(pf, vsi->uplink_seid); if (!veb) { dev_info(&pf->pdev->dev, "couldn't add VEB\n"); return NULL; @@ -14685,29 +14628,24 @@ static void i40e_switch_branch_release(struct i40e_veb *branch) struct i40e_pf *pf = branch->pf; u16 branch_seid = branch->seid; u16 veb_idx = branch->idx; + struct i40e_vsi *vsi; + struct i40e_veb *veb; int i; /* release any VEBs on this VEB - RECURSION */ - for (i = 0; i < I40E_MAX_VEB; i++) { - if (!pf->veb[i]) - continue; - if (pf->veb[i]->uplink_seid == branch->seid) - i40e_switch_branch_release(pf->veb[i]); - } + i40e_pf_for_each_veb(pf, i, veb) + if (veb->uplink_seid == branch->seid) + i40e_switch_branch_release(veb); /* Release the VSIs on this VEB, but not the owner VSI. * * NOTE: Removing the last VSI on a VEB has the SIDE EFFECT of removing * the VEB itself, so don't use (*branch) after this loop. */ - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (!pf->vsi[i]) - continue; - if (pf->vsi[i]->uplink_seid == branch_seid && - (pf->vsi[i]->flags & I40E_VSI_FLAG_VEB_OWNER) == 0) { - i40e_vsi_release(pf->vsi[i]); - } - } + i40e_pf_for_each_vsi(pf, i, vsi) + if (vsi->uplink_seid == branch_seid && + (vsi->flags & I40E_VSI_FLAG_VEB_OWNER) == 0) + i40e_vsi_release(vsi); /* There's one corner case where the VEB might not have been * removed, so double check it here and remove it if needed. @@ -14745,38 +14683,35 @@ static void i40e_veb_clear(struct i40e_veb *veb) **/ void i40e_veb_release(struct i40e_veb *veb) { - struct i40e_vsi *vsi = NULL; + struct i40e_vsi *vsi, *vsi_it; struct i40e_pf *pf; int i, n = 0; pf = veb->pf; /* find the remaining VSI and check for extras */ - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i] && pf->vsi[i]->uplink_seid == veb->seid) { + i40e_pf_for_each_vsi(pf, i, vsi_it) + if (vsi_it->uplink_seid == veb->seid) { + if (vsi_it->flags & I40E_VSI_FLAG_VEB_OWNER) + vsi = vsi_it; n++; - vsi = pf->vsi[i]; } - } - if (n != 1) { + + /* Floating VEB has to be empty and regular one must have + * single owner VSI. + */ + if ((veb->uplink_seid && n != 1) || (!veb->uplink_seid && n != 0)) { dev_info(&pf->pdev->dev, "can't remove VEB %d with %d VSIs left\n", veb->seid, n); return; } - /* move the remaining VSI to uplink veb */ - vsi->flags &= ~I40E_VSI_FLAG_VEB_OWNER; + /* For regular VEB move the owner VSI to uplink port */ if (veb->uplink_seid) { + vsi->flags &= ~I40E_VSI_FLAG_VEB_OWNER; vsi->uplink_seid = veb->uplink_seid; - if (veb->uplink_seid == pf->mac_seid) - vsi->veb_idx = I40E_NO_VEB; - else - vsi->veb_idx = veb->veb_idx; - } else { - /* floating VEB */ - vsi->uplink_seid = pf->vsi[pf->lan_vsi]->uplink_seid; - vsi->veb_idx = pf->vsi[pf->lan_vsi]->veb_idx; + vsi->veb_idx = I40E_NO_VEB; } i40e_aq_delete_element(&pf->hw, veb->seid, NULL); @@ -14794,8 +14729,8 @@ static int i40e_add_veb(struct i40e_veb *veb, struct i40e_vsi *vsi) bool enable_stats = !!test_bit(I40E_FLAG_VEB_STATS_ENA, pf->flags); int ret; - ret = i40e_aq_add_veb(&pf->hw, veb->uplink_seid, vsi->seid, - veb->enabled_tc, false, + ret = i40e_aq_add_veb(&pf->hw, veb->uplink_seid, vsi ? vsi->seid : 0, + veb->enabled_tc, vsi ? false : true, &veb->seid, enable_stats, NULL); /* get a VEB from the hardware */ @@ -14827,9 +14762,11 @@ static int i40e_add_veb(struct i40e_veb *veb, struct i40e_vsi *vsi) return -ENOENT; } - vsi->uplink_seid = veb->seid; - vsi->veb_idx = veb->idx; - vsi->flags |= I40E_VSI_FLAG_VEB_OWNER; + if (vsi) { + vsi->uplink_seid = veb->seid; + vsi->veb_idx = veb->idx; + vsi->flags |= I40E_VSI_FLAG_VEB_OWNER; + } return 0; } @@ -14854,8 +14791,9 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, u16 uplink_seid, u16 vsi_seid, u8 enabled_tc) { - struct i40e_veb *veb, *uplink_veb = NULL; - int vsi_idx, veb_idx; + struct i40e_vsi *vsi = NULL; + struct i40e_veb *veb; + int veb_idx; int ret; /* if one seid is 0, the other must be 0 to create a floating relay */ @@ -14868,26 +14806,11 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, } /* make sure there is such a vsi and uplink */ - for (vsi_idx = 0; vsi_idx < pf->num_alloc_vsi; vsi_idx++) - if (pf->vsi[vsi_idx] && pf->vsi[vsi_idx]->seid == vsi_seid) - break; - if (vsi_idx == pf->num_alloc_vsi && vsi_seid != 0) { - dev_info(&pf->pdev->dev, "vsi seid %d not found\n", - vsi_seid); - return NULL; - } - - if (uplink_seid && uplink_seid != pf->mac_seid) { - for (veb_idx = 0; veb_idx < I40E_MAX_VEB; veb_idx++) { - if (pf->veb[veb_idx] && - pf->veb[veb_idx]->seid == uplink_seid) { - uplink_veb = pf->veb[veb_idx]; - break; - } - } - if (!uplink_veb) { - dev_info(&pf->pdev->dev, - "uplink seid %d not found\n", uplink_seid); + if (vsi_seid) { + vsi = i40e_pf_get_vsi_by_seid(pf, vsi_seid); + if (!vsi) { + dev_err(&pf->pdev->dev, "vsi seid %d not found\n", + vsi_seid); return NULL; } } @@ -14899,14 +14822,14 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, veb = pf->veb[veb_idx]; veb->flags = flags; veb->uplink_seid = uplink_seid; - veb->veb_idx = (uplink_veb ? uplink_veb->idx : I40E_NO_VEB); veb->enabled_tc = (enabled_tc ? enabled_tc : 0x1); /* create the VEB in the switch */ - ret = i40e_add_veb(veb, pf->vsi[vsi_idx]); + ret = i40e_add_veb(veb, vsi); if (ret) goto err_veb; - if (vsi_idx == pf->lan_vsi) + + if (vsi && vsi->idx == pf->lan_vsi) pf->lan_veb = veb->idx; return veb; @@ -14934,6 +14857,7 @@ static void i40e_setup_pf_switch_element(struct i40e_pf *pf, u16 uplink_seid = le16_to_cpu(ele->uplink_seid); u8 element_type = ele->element_type; u16 seid = le16_to_cpu(ele->seid); + struct i40e_veb *veb; if (printconfig) dev_info(&pf->pdev->dev, @@ -14952,13 +14876,10 @@ static void i40e_setup_pf_switch_element(struct i40e_pf *pf, int v; /* find existing or else empty VEB */ - for (v = 0; v < I40E_MAX_VEB; v++) { - if (pf->veb[v] && (pf->veb[v]->seid == seid)) { - pf->lan_veb = v; - break; - } - } - if (pf->lan_veb >= I40E_MAX_VEB) { + veb = i40e_pf_get_veb_by_seid(pf, seid); + if (veb) { + pf->lan_veb = veb->idx; + } else { v = i40e_veb_mem_alloc(pf); if (v < 0) break; @@ -14971,7 +14892,6 @@ static void i40e_setup_pf_switch_element(struct i40e_pf *pf, pf->veb[pf->lan_veb]->seid = seid; pf->veb[pf->lan_veb]->uplink_seid = pf->mac_seid; pf->veb[pf->lan_veb]->pf = pf; - pf->veb[pf->lan_veb]->veb_idx = I40E_NO_VEB; break; case I40E_SWITCH_ELEMENT_TYPE_VSI: if (num_reported != 1) @@ -15634,6 +15554,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef CONFIG_I40E_DCB enum i40e_get_fw_lldp_status_resp lldp_status; #endif /* CONFIG_I40E_DCB */ + struct i40e_vsi *vsi; struct i40e_pf *pf; struct i40e_hw *hw; u16 wol_nvm_bits; @@ -15644,7 +15565,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #endif /* CONFIG_I40E_DCB */ int err; u32 val; - u32 i; err = pci_enable_device_mem(pdev); if (err) @@ -15994,12 +15914,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_LIST_HEAD(&pf->vsi[pf->lan_vsi]->ch_list); /* if FDIR VSI was set up, start it now */ - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) { - i40e_vsi_open(pf->vsi[i]); - break; - } - } + vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); + if (vsi) + i40e_vsi_open(vsi); /* The driver only wants link up/down and module qualification * reports from firmware. Note the negative logic. @@ -16245,6 +16162,8 @@ static void i40e_remove(struct pci_dev *pdev) { struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_hw *hw = &pf->hw; + struct i40e_vsi *vsi; + struct i40e_veb *veb; int ret_code; int i; @@ -16302,24 +16221,19 @@ static void i40e_remove(struct pci_dev *pdev) /* If there is a switch structure or any orphans, remove them. * This will leave only the PF's VSI remaining. */ - for (i = 0; i < I40E_MAX_VEB; i++) { - if (!pf->veb[i]) - continue; - - if (pf->veb[i]->uplink_seid == pf->mac_seid || - pf->veb[i]->uplink_seid == 0) - i40e_switch_branch_release(pf->veb[i]); - } + i40e_pf_for_each_veb(pf, i, veb) + if (veb->uplink_seid == pf->mac_seid || + veb->uplink_seid == 0) + i40e_switch_branch_release(veb); /* Now we can shutdown the PF's VSIs, just before we kill * adminq and hmc. */ - for (i = pf->num_alloc_vsi; i--;) - if (pf->vsi[i]) { - i40e_vsi_close(pf->vsi[i]); - i40e_vsi_release(pf->vsi[i]); - pf->vsi[i] = NULL; - } + i40e_pf_for_each_vsi(pf, i, vsi) { + i40e_vsi_close(vsi); + i40e_vsi_release(vsi); + pf->vsi[i] = NULL; + } i40e_cloud_filter_exit(pf); @@ -16356,18 +16270,17 @@ unmap: /* Clear all dynamic memory lists of rings, q_vectors, and VSIs */ rtnl_lock(); i40e_clear_interrupt_scheme(pf); - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i]) { - if (!test_bit(__I40E_RECOVERY_MODE, pf->state)) - i40e_vsi_clear_rings(pf->vsi[i]); - i40e_vsi_clear(pf->vsi[i]); - pf->vsi[i] = NULL; - } + i40e_pf_for_each_vsi(pf, i, vsi) { + if (!test_bit(__I40E_RECOVERY_MODE, pf->state)) + i40e_vsi_clear_rings(vsi); + + i40e_vsi_clear(vsi); + pf->vsi[i] = NULL; } rtnl_unlock(); - for (i = 0; i < I40E_MAX_VEB; i++) { - kfree(pf->veb[i]); + i40e_pf_for_each_veb(pf, i, veb) { + kfree(veb); pf->veb[i] = NULL; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 908cdbd3ec5d..b34c71770887 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2848,6 +2848,24 @@ error_param: (u8 *)&stats, sizeof(stats)); } +/** + * i40e_can_vf_change_mac + * @vf: pointer to the VF info + * + * Return true if the VF is allowed to change its MAC filters, false otherwise + */ +static bool i40e_can_vf_change_mac(struct i40e_vf *vf) +{ + /* If the VF MAC address has been set administratively (via the + * ndo_set_vf_mac command), then deny permission to the VF to + * add/delete unicast MAC addresses, unless the VF is trusted + */ + if (vf->pf_set_mac && !vf->trusted) + return false; + + return true; +} + #define I40E_MAX_MACVLAN_PER_HW 3072 #define I40E_MAX_MACVLAN_PER_PF(num_ports) (I40E_MAX_MACVLAN_PER_HW / \ (num_ports)) @@ -2907,8 +2925,8 @@ static inline int i40e_check_vf_permission(struct i40e_vf *vf, * The VF may request to set the MAC address filter already * assigned to it so do not return an error in that case. */ - if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) && - !is_multicast_ether_addr(addr) && vf->pf_set_mac && + if (!i40e_can_vf_change_mac(vf) && + !is_multicast_ether_addr(addr) && !ether_addr_equal(addr, vf->default_lan_addr.addr)) { dev_err(&pf->pdev->dev, "VF attempting to override administratively set MAC address, bring down and up the VF interface to resume normal operation\n"); @@ -3114,19 +3132,29 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg) ret = -EINVAL; goto error_param; } - if (ether_addr_equal(al->list[i].addr, vf->default_lan_addr.addr)) - was_unimac_deleted = true; } vsi = pf->vsi[vf->lan_vsi_idx]; spin_lock_bh(&vsi->mac_filter_hash_lock); /* delete addresses from the list */ - for (i = 0; i < al->num_elements; i++) + for (i = 0; i < al->num_elements; i++) { + const u8 *addr = al->list[i].addr; + + /* Allow to delete VF primary MAC only if it was not set + * administratively by PF or if VF is trusted. + */ + if (ether_addr_equal(addr, vf->default_lan_addr.addr) && + i40e_can_vf_change_mac(vf)) + was_unimac_deleted = true; + else + continue; + if (i40e_del_mac_filter(vsi, al->list[i].addr)) { ret = -EINVAL; spin_unlock_bh(&vsi->mac_filter_hash_lock); goto error_param; } + } spin_unlock_bh(&vsi->mac_filter_hash_lock); diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 367b613d92c0..365c03d1c462 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -493,7 +493,6 @@ enum ice_pf_flags { ICE_FLAG_DCB_ENA, ICE_FLAG_FD_ENA, ICE_FLAG_PTP_SUPPORTED, /* PTP is supported by NVM */ - ICE_FLAG_PTP, /* PTP is enabled by software */ ICE_FLAG_ADV_FEATURES, ICE_FLAG_TC_MQPRIO, /* support for Multi queue TC */ ICE_FLAG_CLS_FLOWER, @@ -606,6 +605,7 @@ struct ice_pf { wait_queue_head_t reset_wait_queue; u32 hw_csum_rx_error; + u32 hw_rx_eipe_error; u32 oicr_err_reg; struct msi_map oicr_irq; /* Other interrupt cause MSIX vector */ struct msi_map ll_ts_irq; /* LL_TS interrupt MSIX vector */ @@ -896,6 +896,7 @@ static inline bool ice_is_adq_active(struct ice_pf *pf) } void ice_debugfs_fwlog_init(struct ice_pf *pf); +void ice_debugfs_pf_deinit(struct ice_pf *pf); void ice_debugfs_init(void); void ice_debugfs_exit(void); void ice_pf_fwlog_update_module(struct ice_pf *pf, int log_level, int module); @@ -983,6 +984,8 @@ void ice_service_task_schedule(struct ice_pf *pf); int ice_load(struct ice_pf *pf); void ice_unload(struct ice_pf *pf); void ice_adv_lnk_speed_maps_init(void); +int ice_init_dev(struct ice_pf *pf); +void ice_deinit_dev(struct ice_pf *pf); /** * ice_set_rdma_cap - enable RDMA support diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index 7ac847718882..ad953208f582 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -538,7 +538,7 @@ static void ice_xsk_pool_fill_cb(struct ice_rx_ring *ring) * * Return 0 on success and a negative value on error. */ -int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) +static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) { struct device *dev = ice_pf_to_dev(ring->vsi->back); u32 num_bufs = ICE_RX_DESC_UNUSED(ring); @@ -633,6 +633,62 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) return 0; } +int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx) +{ + if (q_idx >= vsi->num_rxq) + return -EINVAL; + + return ice_vsi_cfg_rxq(vsi->rx_rings[q_idx]); +} + +/** + * ice_vsi_cfg_frame_size - setup max frame size and Rx buffer length + * @vsi: VSI + */ +static void ice_vsi_cfg_frame_size(struct ice_vsi *vsi) +{ + if (!vsi->netdev || test_bit(ICE_FLAG_LEGACY_RX, vsi->back->flags)) { + vsi->max_frame = ICE_MAX_FRAME_LEGACY_RX; + vsi->rx_buf_len = ICE_RXBUF_1664; +#if (PAGE_SIZE < 8192) + } else if (!ICE_2K_TOO_SMALL_WITH_PADDING && + (vsi->netdev->mtu <= ETH_DATA_LEN)) { + vsi->max_frame = ICE_RXBUF_1536 - NET_IP_ALIGN; + vsi->rx_buf_len = ICE_RXBUF_1536 - NET_IP_ALIGN; +#endif + } else { + vsi->max_frame = ICE_AQ_SET_MAC_FRAME_SIZE_MAX; + vsi->rx_buf_len = ICE_RXBUF_3072; + } +} + +/** + * ice_vsi_cfg_rxqs - Configure the VSI for Rx + * @vsi: the VSI being configured + * + * Return 0 on success and a negative value on error + * Configure the Rx VSI for operation. + */ +int ice_vsi_cfg_rxqs(struct ice_vsi *vsi) +{ + u16 i; + + if (vsi->type == ICE_VSI_VF) + goto setup_rings; + + ice_vsi_cfg_frame_size(vsi); +setup_rings: + /* set up individual rings */ + ice_for_each_rxq(vsi, i) { + int err = ice_vsi_cfg_rxq(vsi->rx_rings[i]); + + if (err) + return err; + } + + return 0; +} + /** * __ice_vsi_get_qs - helper function for assigning queues from PF to VSI * @qs_cfg: gathered variables needed for pf->vsi queues assignment @@ -828,7 +884,7 @@ void ice_vsi_free_q_vectors(struct ice_vsi *vsi) * @ring: Tx ring to be configured * @qg_buf: queue group buffer */ -int +static int ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, struct ice_aqc_add_tx_qgrp *qg_buf) { @@ -899,6 +955,80 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, return 0; } +int ice_vsi_cfg_single_txq(struct ice_vsi *vsi, struct ice_tx_ring **tx_rings, + u16 q_idx) +{ + DEFINE_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); + + if (q_idx >= vsi->alloc_txq || !tx_rings || !tx_rings[q_idx]) + return -EINVAL; + + qg_buf->num_txqs = 1; + + return ice_vsi_cfg_txq(vsi, tx_rings[q_idx], qg_buf); +} + +/** + * ice_vsi_cfg_txqs - Configure the VSI for Tx + * @vsi: the VSI being configured + * @rings: Tx ring array to be configured + * @count: number of Tx ring array elements + * + * Return 0 on success and a negative value on error + * Configure the Tx VSI for operation. + */ +static int +ice_vsi_cfg_txqs(struct ice_vsi *vsi, struct ice_tx_ring **rings, u16 count) +{ + DEFINE_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); + int err = 0; + u16 q_idx; + + qg_buf->num_txqs = 1; + + for (q_idx = 0; q_idx < count; q_idx++) { + err = ice_vsi_cfg_txq(vsi, rings[q_idx], qg_buf); + if (err) + break; + } + + return err; +} + +/** + * ice_vsi_cfg_lan_txqs - Configure the VSI for Tx + * @vsi: the VSI being configured + * + * Return 0 on success and a negative value on error + * Configure the Tx VSI for operation. + */ +int ice_vsi_cfg_lan_txqs(struct ice_vsi *vsi) +{ + return ice_vsi_cfg_txqs(vsi, vsi->tx_rings, vsi->num_txq); +} + +/** + * ice_vsi_cfg_xdp_txqs - Configure Tx queues dedicated for XDP in given VSI + * @vsi: the VSI being configured + * + * Return 0 on success and a negative value on error + * Configure the Tx queues dedicated for XDP in given VSI for operation. + */ +int ice_vsi_cfg_xdp_txqs(struct ice_vsi *vsi) +{ + int ret; + int i; + + ret = ice_vsi_cfg_txqs(vsi, vsi->xdp_rings, vsi->num_xdp_txq); + if (ret) + return ret; + + ice_for_each_rxq(vsi, i) + ice_tx_xsk_pool(vsi, i); + + return 0; +} + /** * ice_cfg_itr - configure the initial interrupt throttle values * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/ice/ice_base.h b/drivers/net/ethernet/intel/ice/ice_base.h index 17321ba75602..b711bc921928 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.h +++ b/drivers/net/ethernet/intel/ice/ice_base.h @@ -6,7 +6,8 @@ #include "ice.h" -int ice_vsi_cfg_rxq(struct ice_rx_ring *ring); +int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx); +int ice_vsi_cfg_rxqs(struct ice_vsi *vsi); int __ice_vsi_get_qs(struct ice_qs_cfg *qs_cfg); int ice_vsi_ctrl_one_rx_ring(struct ice_vsi *vsi, bool ena, u16 rxq_idx, bool wait); @@ -14,9 +15,10 @@ int ice_vsi_wait_one_rx_ring(struct ice_vsi *vsi, bool ena, u16 rxq_idx); int ice_vsi_alloc_q_vectors(struct ice_vsi *vsi); void ice_vsi_map_rings_to_vectors(struct ice_vsi *vsi); void ice_vsi_free_q_vectors(struct ice_vsi *vsi); -int -ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, - struct ice_aqc_add_tx_qgrp *qg_buf); +int ice_vsi_cfg_single_txq(struct ice_vsi *vsi, struct ice_tx_ring **tx_rings, + u16 q_idx); +int ice_vsi_cfg_lan_txqs(struct ice_vsi *vsi); +int ice_vsi_cfg_xdp_txqs(struct ice_vsi *vsi); void ice_cfg_itr(struct ice_hw *hw, struct ice_q_vector *q_vector); void ice_cfg_txq_interrupt(struct ice_vsi *vsi, u16 txq, u16 msix_idx, u16 itr_idx); diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 10c32cd80fff..9266f25a9978 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -154,6 +154,12 @@ static int ice_set_mac_type(struct ice_hw *hw) case ICE_DEV_ID_E823L_SFP: hw->mac_type = ICE_MAC_GENERIC; break; + case ICE_DEV_ID_E825C_BACKPLANE: + case ICE_DEV_ID_E825C_QSFP: + case ICE_DEV_ID_E825C_SFP: + case ICE_DEV_ID_E825C_SGMII: + hw->mac_type = ICE_MAC_GENERIC_3K_E825; + break; case ICE_DEV_ID_E830_BACKPLANE: case ICE_DEV_ID_E830_QSFP56: case ICE_DEV_ID_E830_SFP: @@ -170,6 +176,18 @@ static int ice_set_mac_type(struct ice_hw *hw) } /** + * ice_is_generic_mac - check if device's mac_type is generic + * @hw: pointer to the hardware structure + * + * Return: true if mac_type is generic (with SBQ support), false if not + */ +bool ice_is_generic_mac(struct ice_hw *hw) +{ + return (hw->mac_type == ICE_MAC_GENERIC || + hw->mac_type == ICE_MAC_GENERIC_3K_E825); +} + +/** * ice_is_e810 * @hw: pointer to the hardware structure * @@ -241,6 +259,25 @@ bool ice_is_e823(struct ice_hw *hw) } /** + * ice_is_e825c - Check if a device is E825C family device + * @hw: pointer to the hardware structure + * + * Return: true if the device is E825-C based, false if not. + */ +bool ice_is_e825c(struct ice_hw *hw) +{ + switch (hw->device_id) { + case ICE_DEV_ID_E825C_BACKPLANE: + case ICE_DEV_ID_E825C_QSFP: + case ICE_DEV_ID_E825C_SFP: + case ICE_DEV_ID_E825C_SGMII: + return true; + default: + return false; + } +} + +/** * ice_clear_pf_cfg - Clear PF configuration * @hw: pointer to the hardware structure * diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h index 3e933f75e948..32fd10de620c 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.h +++ b/drivers/net/ethernet/intel/ice/ice_common.h @@ -112,6 +112,7 @@ ice_update_phy_type(u64 *phy_type_low, u64 *phy_type_high, int ice_aq_manage_mac_write(struct ice_hw *hw, const u8 *mac_addr, u8 flags, struct ice_sq_cd *cd); +bool ice_is_generic_mac(struct ice_hw *hw); bool ice_is_e810(struct ice_hw *hw); int ice_clear_pf_cfg(struct ice_hw *hw); int @@ -251,6 +252,7 @@ ice_stat_update32(struct ice_hw *hw, u32 reg, bool prev_stat_loaded, u64 *prev_stat, u64 *cur_stat); bool ice_is_e810t(struct ice_hw *hw); bool ice_is_e823(struct ice_hw *hw); +bool ice_is_e825c(struct ice_hw *hw); int ice_sched_query_elem(struct ice_hw *hw, u32 node_teid, struct ice_aqc_txsched_elem_data *buf); diff --git a/drivers/net/ethernet/intel/ice/ice_controlq.c b/drivers/net/ethernet/intel/ice/ice_controlq.c index e7d2474c431c..ffe660f34992 100644 --- a/drivers/net/ethernet/intel/ice/ice_controlq.c +++ b/drivers/net/ethernet/intel/ice/ice_controlq.c @@ -666,7 +666,7 @@ bool ice_is_sbq_supported(struct ice_hw *hw) /* The device sideband queue is only supported on devices with the * generic MAC type. */ - return hw->mac_type == ICE_MAC_GENERIC; + return ice_is_generic_mac(hw); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_ddp.c b/drivers/net/ethernet/intel/ice/ice_ddp.c index 8b7504a9df31..7532d11ad7f3 100644 --- a/drivers/net/ethernet/intel/ice/ice_ddp.c +++ b/drivers/net/ethernet/intel/ice/ice_ddp.c @@ -1825,6 +1825,7 @@ static u32 ice_get_pkg_segment_id(enum ice_mac_type mac_type) seg_id = SEGMENT_TYPE_ICE_E830; break; case ICE_MAC_GENERIC: + case ICE_MAC_GENERIC_3K_E825: default: seg_id = SEGMENT_TYPE_ICE_E810; break; @@ -1845,6 +1846,9 @@ static u32 ice_get_pkg_sign_type(enum ice_mac_type mac_type) case ICE_MAC_E830: sign_type = SEGMENT_SIGN_TYPE_RSA3K_SBB; break; + case ICE_MAC_GENERIC_3K_E825: + sign_type = SEGMENT_SIGN_TYPE_RSA3K_E825; + break; case ICE_MAC_GENERIC: default: sign_type = SEGMENT_SIGN_TYPE_RSA2K; diff --git a/drivers/net/ethernet/intel/ice/ice_debugfs.c b/drivers/net/ethernet/intel/ice/ice_debugfs.c index c2bfba6b9ead..d252d98218d0 100644 --- a/drivers/net/ethernet/intel/ice/ice_debugfs.c +++ b/drivers/net/ethernet/intel/ice/ice_debugfs.c @@ -64,9 +64,6 @@ static const char * const ice_fwlog_level_string[] = { "verbose", }; -/* the order in this array is important. it matches the ordering of the - * values in the FW so the index is the same value as in ice_fwlog_level - */ static const char * const ice_fwlog_log_size[] = { "128K", "256K", @@ -648,6 +645,16 @@ err_create_module_files: } /** + * ice_debugfs_pf_deinit - cleanup PF's debugfs + * @pf: pointer to the PF struct + */ +void ice_debugfs_pf_deinit(struct ice_pf *pf) +{ + debugfs_remove_recursive(pf->ice_debugfs_pf); + pf->ice_debugfs_pf = NULL; +} + +/** * ice_debugfs_init - create root directory for debugfs entries */ void ice_debugfs_init(void) diff --git a/drivers/net/ethernet/intel/ice/ice_devids.h b/drivers/net/ethernet/intel/ice/ice_devids.h index a2d384dbfc76..9dfae9bce758 100644 --- a/drivers/net/ethernet/intel/ice/ice_devids.h +++ b/drivers/net/ethernet/intel/ice/ice_devids.h @@ -71,5 +71,13 @@ #define ICE_DEV_ID_E822L_10G_BASE_T 0x1899 /* Intel(R) Ethernet Connection E822-L 1GbE */ #define ICE_DEV_ID_E822L_SGMII 0x189A +/* Intel(R) Ethernet Connection E825-C for backplane */ +#define ICE_DEV_ID_E825C_BACKPLANE 0x579c +/* Intel(R) Ethernet Connection E825-C for QSFP */ +#define ICE_DEV_ID_E825C_QSFP 0x579d +/* Intel(R) Ethernet Connection E825-C for SFP */ +#define ICE_DEV_ID_E825C_SFP 0x579e +/* Intel(R) Ethernet Connection E825-C 1GbE */ +#define ICE_DEV_ID_E825C_SGMII 0x579f #endif /* _ICE_DEVIDS_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index 65be56f2af9e..b516e42b41f0 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -445,6 +445,20 @@ ice_devlink_reload_empr_start(struct ice_pf *pf, } /** + * ice_devlink_reinit_down - unload given PF + * @pf: pointer to the PF struct + */ +static void ice_devlink_reinit_down(struct ice_pf *pf) +{ + /* No need to take devl_lock, it's already taken by devlink API */ + ice_unload(pf); + rtnl_lock(); + ice_vsi_decfg(ice_get_main_vsi(pf)); + rtnl_unlock(); + ice_deinit_dev(pf); +} + +/** * ice_devlink_reload_down - prepare for reload * @devlink: pointer to the devlink instance to reload * @netns_change: if true, the network namespace is changing @@ -477,7 +491,7 @@ ice_devlink_reload_down(struct devlink *devlink, bool netns_change, "Remove all VFs before doing reinit\n"); return -EOPNOTSUPP; } - ice_unload(pf); + ice_devlink_reinit_down(pf); return 0; case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: return ice_devlink_reload_empr_start(pf, extack); @@ -1270,6 +1284,45 @@ static int ice_devlink_set_parent(struct devlink_rate *devlink_rate, } /** + * ice_devlink_reinit_up - do reinit of the given PF + * @pf: pointer to the PF struct + */ +static int ice_devlink_reinit_up(struct ice_pf *pf) +{ + struct ice_vsi *vsi = ice_get_main_vsi(pf); + struct ice_vsi_cfg_params params; + int err; + + err = ice_init_dev(pf); + if (err) + return err; + + params = ice_vsi_to_params(vsi); + params.flags = ICE_VSI_FLAG_INIT; + + rtnl_lock(); + err = ice_vsi_cfg(vsi, ¶ms); + rtnl_unlock(); + if (err) + goto err_vsi_cfg; + + /* No need to take devl_lock, it's already taken by devlink API */ + err = ice_load(pf); + if (err) + goto err_load; + + return 0; + +err_load: + rtnl_lock(); + ice_vsi_decfg(vsi); + rtnl_unlock(); +err_vsi_cfg: + ice_deinit_dev(pf); + return err; +} + +/** * ice_devlink_reload_up - do reload up after reinit * @devlink: pointer to the devlink instance reloading * @action: the action requested @@ -1289,7 +1342,7 @@ ice_devlink_reload_up(struct devlink *devlink, switch (action) { case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT); - return ice_load(pf); + return ice_devlink_reinit_up(pf); case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: *actions_performed = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE); return ice_devlink_reload_empr_finish(pf, extack); @@ -1569,6 +1622,7 @@ static const struct devlink_port_ops ice_devlink_port_ops = { * @pf: the PF to create a devlink port for * * Create and register a devlink_port for this PF. + * This function has to be called under devl_lock. * * Return: zero on success or an error code on failure. */ @@ -1581,6 +1635,8 @@ int ice_devlink_create_pf_port(struct ice_pf *pf) struct device *dev; int err; + devlink = priv_to_devlink(pf); + dev = ice_pf_to_dev(pf); devlink_port = &pf->devlink_port; @@ -1601,10 +1657,9 @@ int ice_devlink_create_pf_port(struct ice_pf *pf) ice_devlink_set_switch_id(pf, &attrs.switch_id); devlink_port_attrs_set(devlink_port, &attrs); - devlink = priv_to_devlink(pf); - err = devlink_port_register_with_ops(devlink, devlink_port, vsi->idx, - &ice_devlink_port_ops); + err = devl_port_register_with_ops(devlink, devlink_port, vsi->idx, + &ice_devlink_port_ops); if (err) { dev_err(dev, "Failed to create devlink port for PF %d, error %d\n", pf->hw.pf_id, err); @@ -1619,10 +1674,11 @@ int ice_devlink_create_pf_port(struct ice_pf *pf) * @pf: the PF to cleanup * * Unregisters the devlink_port structure associated with this PF. + * This function has to be called under devl_lock. */ void ice_devlink_destroy_pf_port(struct ice_pf *pf) { - devlink_port_unregister(&pf->devlink_port); + devl_port_unregister(&pf->devlink_port); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index b9c5eced6326..c0256564e998 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -488,6 +488,7 @@ ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll, * @dpll: registered dpll pointer * @dpll_priv: private data pointer passed on dpll registration * @status: on success holds dpll's lock status + * @status_error: status error value * @extack: error reporting * * Dpll subsystem callback, provides dpll's lock status. @@ -500,6 +501,7 @@ ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll, static int ice_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv, enum dpll_lock_status *status, + enum dpll_lock_status_error *status_error, struct netlink_ext_ack *extack) { struct ice_dpll *d = dpll_priv; diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index a19b06f18e40..3cc364a4d682 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -129,6 +129,7 @@ static const struct ice_stats ice_gstrings_pf_stats[] = { ICE_PF_STAT("rx_oversize.nic", stats.rx_oversize), ICE_PF_STAT("rx_jabber.nic", stats.rx_jabber), ICE_PF_STAT("rx_csum_bad.nic", hw_csum_rx_error), + ICE_PF_STAT("rx_eipe_error.nic", hw_rx_eipe_error), ICE_PF_STAT("rx_dropped.nic", stats.eth.rx_discards), ICE_PF_STAT("rx_crc_errors.nic", stats.crc_errors), ICE_PF_STAT("illegal_bytes.nic", stats.illegal_bytes), @@ -3360,7 +3361,7 @@ ice_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) struct ice_pf *pf = ice_netdev_to_pf(dev); /* only report timestamping if PTP is enabled */ - if (!test_bit(ICE_FLAG_PTP, pf->flags)) + if (pf->ptp.state != ICE_PTP_READY) return ethtool_op_get_ts_info(dev, info); info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | diff --git a/drivers/net/ethernet/intel/ice/ice_fwlog.c b/drivers/net/ethernet/intel/ice/ice_fwlog.c index 92b5dac481cd..4fd15387a7e5 100644 --- a/drivers/net/ethernet/intel/ice/ice_fwlog.c +++ b/drivers/net/ethernet/intel/ice/ice_fwlog.c @@ -188,6 +188,8 @@ void ice_fwlog_deinit(struct ice_hw *hw) if (hw->bus.func) return; + ice_debugfs_pf_deinit(hw->back); + /* make sure FW logging is disabled to not put the FW in a weird state * for the next driver load */ diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c index 2a25323105e5..467372d541d2 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.c +++ b/drivers/net/ethernet/intel/ice/ice_lag.c @@ -152,6 +152,27 @@ ice_lag_find_hw_by_lport(struct ice_lag *lag, u8 lport) } /** + * ice_pkg_has_lport_extract - check if lport extraction supported + * @hw: HW struct + */ +static bool ice_pkg_has_lport_extract(struct ice_hw *hw) +{ + int i; + + for (i = 0; i < hw->blk[ICE_BLK_SW].es.count; i++) { + u16 offset; + u8 fv_prot; + + ice_find_prot_off(hw, ICE_BLK_SW, ICE_SW_DEFAULT_PROFILE, i, + &fv_prot, &offset); + if (fv_prot == ICE_FV_PROT_MDID && + offset == ICE_LP_EXT_BUF_OFFSET) + return true; + } + return false; +} + +/** * ice_lag_find_primary - returns pointer to primary interfaces lag struct * @lag: local interfaces lag struct */ @@ -1206,7 +1227,7 @@ static void ice_lag_del_prune_list(struct ice_lag *lag, struct ice_pf *event_pf) } /** - * ice_lag_init_feature_support_flag - Check for NVM support for LAG + * ice_lag_init_feature_support_flag - Check for package and NVM support for LAG * @pf: PF struct */ static void ice_lag_init_feature_support_flag(struct ice_pf *pf) @@ -1219,7 +1240,7 @@ static void ice_lag_init_feature_support_flag(struct ice_pf *pf) else ice_clear_feature_support(pf, ICE_F_ROCE_LAG); - if (caps->sriov_lag) + if (caps->sriov_lag && ice_pkg_has_lport_extract(&pf->hw)) ice_set_feature_support(pf, ICE_F_SRIOV_LAG); else ice_clear_feature_support(pf, ICE_F_SRIOV_LAG); diff --git a/drivers/net/ethernet/intel/ice/ice_lag.h b/drivers/net/ethernet/intel/ice/ice_lag.h index ede833dfa658..183b38792ef2 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.h +++ b/drivers/net/ethernet/intel/ice/ice_lag.h @@ -17,6 +17,9 @@ enum ice_lag_role { #define ICE_LAG_INVALID_PORT 0xFF #define ICE_LAG_RESET_RETRIES 5 +#define ICE_SW_DEFAULT_PROFILE 0 +#define ICE_FV_PROT_MDID 255 +#define ICE_LP_EXT_BUF_OFFSET 32 struct ice_pf; struct ice_vf; diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 9be724291ef8..60e0d824195e 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -1672,27 +1672,6 @@ static void ice_vsi_set_rss_flow_fld(struct ice_vsi *vsi) } /** - * ice_vsi_cfg_frame_size - setup max frame size and Rx buffer length - * @vsi: VSI - */ -static void ice_vsi_cfg_frame_size(struct ice_vsi *vsi) -{ - if (!vsi->netdev || test_bit(ICE_FLAG_LEGACY_RX, vsi->back->flags)) { - vsi->max_frame = ICE_MAX_FRAME_LEGACY_RX; - vsi->rx_buf_len = ICE_RXBUF_1664; -#if (PAGE_SIZE < 8192) - } else if (!ICE_2K_TOO_SMALL_WITH_PADDING && - (vsi->netdev->mtu <= ETH_DATA_LEN)) { - vsi->max_frame = ICE_RXBUF_1536 - NET_IP_ALIGN; - vsi->rx_buf_len = ICE_RXBUF_1536 - NET_IP_ALIGN; -#endif - } else { - vsi->max_frame = ICE_AQ_SET_MAC_FRAME_SIZE_MAX; - vsi->rx_buf_len = ICE_RXBUF_3072; - } -} - -/** * ice_pf_state_is_nominal - checks the PF for nominal state * @pf: pointer to PF to check * @@ -1795,114 +1774,6 @@ ice_write_qrxflxp_cntxt(struct ice_hw *hw, u16 pf_q, u32 rxdid, u32 prio, wr32(hw, QRXFLXP_CNTXT(pf_q), regval); } -int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx) -{ - if (q_idx >= vsi->num_rxq) - return -EINVAL; - - return ice_vsi_cfg_rxq(vsi->rx_rings[q_idx]); -} - -int ice_vsi_cfg_single_txq(struct ice_vsi *vsi, struct ice_tx_ring **tx_rings, u16 q_idx) -{ - DEFINE_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); - - if (q_idx >= vsi->alloc_txq || !tx_rings || !tx_rings[q_idx]) - return -EINVAL; - - qg_buf->num_txqs = 1; - - return ice_vsi_cfg_txq(vsi, tx_rings[q_idx], qg_buf); -} - -/** - * ice_vsi_cfg_rxqs - Configure the VSI for Rx - * @vsi: the VSI being configured - * - * Return 0 on success and a negative value on error - * Configure the Rx VSI for operation. - */ -int ice_vsi_cfg_rxqs(struct ice_vsi *vsi) -{ - u16 i; - - if (vsi->type == ICE_VSI_VF) - goto setup_rings; - - ice_vsi_cfg_frame_size(vsi); -setup_rings: - /* set up individual rings */ - ice_for_each_rxq(vsi, i) { - int err = ice_vsi_cfg_rxq(vsi->rx_rings[i]); - - if (err) - return err; - } - - return 0; -} - -/** - * ice_vsi_cfg_txqs - Configure the VSI for Tx - * @vsi: the VSI being configured - * @rings: Tx ring array to be configured - * @count: number of Tx ring array elements - * - * Return 0 on success and a negative value on error - * Configure the Tx VSI for operation. - */ -static int -ice_vsi_cfg_txqs(struct ice_vsi *vsi, struct ice_tx_ring **rings, u16 count) -{ - DEFINE_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); - int err = 0; - u16 q_idx; - - qg_buf->num_txqs = 1; - - for (q_idx = 0; q_idx < count; q_idx++) { - err = ice_vsi_cfg_txq(vsi, rings[q_idx], qg_buf); - if (err) - break; - } - - return err; -} - -/** - * ice_vsi_cfg_lan_txqs - Configure the VSI for Tx - * @vsi: the VSI being configured - * - * Return 0 on success and a negative value on error - * Configure the Tx VSI for operation. - */ -int ice_vsi_cfg_lan_txqs(struct ice_vsi *vsi) -{ - return ice_vsi_cfg_txqs(vsi, vsi->tx_rings, vsi->num_txq); -} - -/** - * ice_vsi_cfg_xdp_txqs - Configure Tx queues dedicated for XDP in given VSI - * @vsi: the VSI being configured - * - * Return 0 on success and a negative value on error - * Configure the Tx queues dedicated for XDP in given VSI for operation. - */ -int ice_vsi_cfg_xdp_txqs(struct ice_vsi *vsi) -{ - int ret; - int i; - - ret = ice_vsi_cfg_txqs(vsi, vsi->xdp_rings, vsi->num_xdp_txq); - if (ret) - return ret; - - ice_for_each_rxq(vsi, i) - ice_tx_xsk_pool(vsi, i); - - return 0; -} - /** * ice_intrl_usec_to_reg - convert interrupt rate limit to register value * @intrl: interrupt rate limit in usecs diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h b/drivers/net/ethernet/intel/ice/ice_lib.h index 71bd27244941..0c77d581416a 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_lib.h @@ -54,14 +54,6 @@ bool ice_pf_state_is_nominal(struct ice_pf *pf); void ice_update_eth_stats(struct ice_vsi *vsi); -int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx); - -int ice_vsi_cfg_single_txq(struct ice_vsi *vsi, struct ice_tx_ring **tx_rings, u16 q_idx); - -int ice_vsi_cfg_rxqs(struct ice_vsi *vsi); - -int ice_vsi_cfg_lan_txqs(struct ice_vsi *vsi); - void ice_vsi_cfg_msix(struct ice_vsi *vsi); int ice_vsi_start_all_rx_rings(struct ice_vsi *vsi); @@ -72,8 +64,6 @@ int ice_vsi_stop_lan_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src, u16 rel_vmvf_num); -int ice_vsi_cfg_xdp_txqs(struct ice_vsi *vsi); - int ice_vsi_stop_xdp_tx_rings(struct ice_vsi *vsi); void ice_cfg_sw_lldp(struct ice_vsi *vsi, bool tx, bool create); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index dd4a9bc0dfdc..9c2c8637b4a7 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -613,7 +613,7 @@ skip: ice_pf_dis_all_vsi(pf, false); if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) - ice_ptp_prepare_for_reset(pf); + ice_ptp_prepare_for_reset(pf, reset_type); if (ice_is_feature_supported(pf, ICE_F_GNSS)) ice_gnss_exit(pf); @@ -1649,8 +1649,10 @@ static void ice_clean_sbq_subtask(struct ice_pf *pf) { struct ice_hw *hw = &pf->hw; - /* Nothing to do here if sideband queue is not supported */ - if (!ice_is_sbq_supported(hw)) { + /* if mac_type is not generic, sideband is not supported + * and there's nothing to do here + */ + if (!ice_is_generic_mac(hw)) { clear_bit(ICE_SIDEBANDQ_EVENT_PENDING, pf->state); return; } @@ -4572,90 +4574,6 @@ static void ice_decfg_netdev(struct ice_vsi *vsi) vsi->netdev = NULL; } -static int ice_start_eth(struct ice_vsi *vsi) -{ - int err; - - err = ice_init_mac_fltr(vsi->back); - if (err) - return err; - - err = ice_vsi_open(vsi); - if (err) - ice_fltr_remove_all(vsi); - - return err; -} - -static void ice_stop_eth(struct ice_vsi *vsi) -{ - ice_fltr_remove_all(vsi); - ice_vsi_close(vsi); -} - -static int ice_init_eth(struct ice_pf *pf) -{ - struct ice_vsi *vsi = ice_get_main_vsi(pf); - int err; - - if (!vsi) - return -EINVAL; - - /* init channel list */ - INIT_LIST_HEAD(&vsi->ch_list); - - err = ice_cfg_netdev(vsi); - if (err) - return err; - /* Setup DCB netlink interface */ - ice_dcbnl_setup(vsi); - - err = ice_init_mac_fltr(pf); - if (err) - goto err_init_mac_fltr; - - err = ice_devlink_create_pf_port(pf); - if (err) - goto err_devlink_create_pf_port; - - SET_NETDEV_DEVLINK_PORT(vsi->netdev, &pf->devlink_port); - - err = ice_register_netdev(vsi); - if (err) - goto err_register_netdev; - - err = ice_tc_indir_block_register(vsi); - if (err) - goto err_tc_indir_block_register; - - ice_napi_add(vsi); - - return 0; - -err_tc_indir_block_register: - ice_unregister_netdev(vsi); -err_register_netdev: - ice_devlink_destroy_pf_port(pf); -err_devlink_create_pf_port: -err_init_mac_fltr: - ice_decfg_netdev(vsi); - return err; -} - -static void ice_deinit_eth(struct ice_pf *pf) -{ - struct ice_vsi *vsi = ice_get_main_vsi(pf); - - if (!vsi) - return; - - ice_vsi_close(vsi); - ice_unregister_netdev(vsi); - ice_devlink_destroy_pf_port(pf); - ice_tc_indir_block_unregister(vsi); - ice_decfg_netdev(vsi); -} - /** * ice_wait_for_fw - wait for full FW readiness * @hw: pointer to the hardware structure @@ -4681,7 +4599,7 @@ static int ice_wait_for_fw(struct ice_hw *hw, u32 timeout) return -ETIMEDOUT; } -static int ice_init_dev(struct ice_pf *pf) +int ice_init_dev(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; @@ -4774,7 +4692,7 @@ err_init_pf: return err; } -static void ice_deinit_dev(struct ice_pf *pf) +void ice_deinit_dev(struct ice_pf *pf) { ice_free_irq_msix_misc(pf); ice_deinit_pf(pf); @@ -5079,31 +4997,47 @@ static void ice_deinit(struct ice_pf *pf) /** * ice_load - load pf by init hw and starting VSI * @pf: pointer to the pf instance + * + * This function has to be called under devl_lock. */ int ice_load(struct ice_pf *pf) { - struct ice_vsi_cfg_params params = {}; struct ice_vsi *vsi; int err; - err = ice_init_dev(pf); + devl_assert_locked(priv_to_devlink(pf)); + + vsi = ice_get_main_vsi(pf); + + /* init channel list */ + INIT_LIST_HEAD(&vsi->ch_list); + + err = ice_cfg_netdev(vsi); if (err) return err; - vsi = ice_get_main_vsi(pf); + /* Setup DCB netlink interface */ + ice_dcbnl_setup(vsi); - params = ice_vsi_to_params(vsi); - params.flags = ICE_VSI_FLAG_INIT; + err = ice_init_mac_fltr(pf); + if (err) + goto err_init_mac_fltr; + + err = ice_devlink_create_pf_port(pf); + if (err) + goto err_devlink_create_pf_port; + + SET_NETDEV_DEVLINK_PORT(vsi->netdev, &pf->devlink_port); - rtnl_lock(); - err = ice_vsi_cfg(vsi, ¶ms); + err = ice_register_netdev(vsi); if (err) - goto err_vsi_cfg; + goto err_register_netdev; - err = ice_start_eth(ice_get_main_vsi(pf)); + err = ice_tc_indir_block_register(vsi); if (err) - goto err_start_eth; - rtnl_unlock(); + goto err_tc_indir_block_register; + + ice_napi_add(vsi); err = ice_init_rdma(pf); if (err) @@ -5117,29 +5051,35 @@ int ice_load(struct ice_pf *pf) return 0; err_init_rdma: - ice_vsi_close(ice_get_main_vsi(pf)); - rtnl_lock(); -err_start_eth: - ice_vsi_decfg(ice_get_main_vsi(pf)); -err_vsi_cfg: - rtnl_unlock(); - ice_deinit_dev(pf); + ice_tc_indir_block_unregister(vsi); +err_tc_indir_block_register: + ice_unregister_netdev(vsi); +err_register_netdev: + ice_devlink_destroy_pf_port(pf); +err_devlink_create_pf_port: +err_init_mac_fltr: + ice_decfg_netdev(vsi); return err; } /** * ice_unload - unload pf by stopping VSI and deinit hw * @pf: pointer to the pf instance + * + * This function has to be called under devl_lock. */ void ice_unload(struct ice_pf *pf) { + struct ice_vsi *vsi = ice_get_main_vsi(pf); + + devl_assert_locked(priv_to_devlink(pf)); + ice_deinit_features(pf); ice_deinit_rdma(pf); - rtnl_lock(); - ice_stop_eth(ice_get_main_vsi(pf)); - ice_vsi_decfg(ice_get_main_vsi(pf)); - rtnl_unlock(); - ice_deinit_dev(pf); + ice_tc_indir_block_unregister(vsi); + ice_unregister_netdev(vsi); + ice_devlink_destroy_pf_port(pf); + ice_decfg_netdev(vsi); } /** @@ -5237,27 +5177,23 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) if (err) goto err_init; - err = ice_init_eth(pf); + devl_lock(priv_to_devlink(pf)); + err = ice_load(pf); + devl_unlock(priv_to_devlink(pf)); if (err) - goto err_init_eth; - - err = ice_init_rdma(pf); - if (err) - goto err_init_rdma; + goto err_load; err = ice_init_devlink(pf); if (err) goto err_init_devlink; - ice_init_features(pf); - return 0; err_init_devlink: - ice_deinit_rdma(pf); -err_init_rdma: - ice_deinit_eth(pf); -err_init_eth: + devl_lock(priv_to_devlink(pf)); + ice_unload(pf); + devl_unlock(priv_to_devlink(pf)); +err_load: ice_deinit(pf); err_init: pci_disable_device(pdev); @@ -5340,8 +5276,6 @@ static void ice_remove(struct pci_dev *pdev) msleep(100); } - ice_debugfs_exit(); - if (test_bit(ICE_FLAG_SRIOV_ENA, pf->flags)) { set_bit(ICE_VF_RESETS_DISABLED, pf->state); ice_free_vfs(pf); @@ -5355,12 +5289,14 @@ static void ice_remove(struct pci_dev *pdev) if (!ice_is_safe_mode(pf)) ice_remove_arfs(pf); - ice_deinit_features(pf); + ice_deinit_devlink(pf); - ice_deinit_rdma(pf); - ice_deinit_eth(pf); - ice_deinit(pf); + devl_lock(priv_to_devlink(pf)); + ice_unload(pf); + devl_unlock(priv_to_devlink(pf)); + + ice_deinit(pf); ice_vsi_release_all(pf); ice_setup_mc_magic_wake(pf); @@ -5752,6 +5688,10 @@ static const struct pci_device_id ice_pci_tbl[] = { { PCI_VDEVICE(INTEL, ICE_DEV_ID_E823L_1GBE) }, { PCI_VDEVICE(INTEL, ICE_DEV_ID_E823L_QSFP) }, { PCI_VDEVICE(INTEL, ICE_DEV_ID_E822_SI_DFLT) }, + { PCI_VDEVICE(INTEL, ICE_DEV_ID_E825C_BACKPLANE), }, + { PCI_VDEVICE(INTEL, ICE_DEV_ID_E825C_QSFP), }, + { PCI_VDEVICE(INTEL, ICE_DEV_ID_E825C_SFP), }, + { PCI_VDEVICE(INTEL, ICE_DEV_ID_E825C_SGMII), }, { PCI_VDEVICE(INTEL, ICE_DEV_ID_E830_BACKPLANE) }, { PCI_VDEVICE(INTEL, ICE_DEV_ID_E830_QSFP56) }, { PCI_VDEVICE(INTEL, ICE_DEV_ID_E830_SFP) }, @@ -5841,6 +5781,7 @@ module_init(ice_module_init); static void __exit ice_module_exit(void) { pci_unregister_driver(&ice_driver); + ice_debugfs_exit(); destroy_workqueue(ice_wq); destroy_workqueue(ice_lag_wq); pr_info("module unloaded\n"); @@ -7548,7 +7489,7 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) * fail. */ if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) - ice_ptp_reset(pf); + ice_ptp_rebuild(pf, reset_type); if (ice_is_feature_supported(pf, ICE_F_GNSS)) ice_gnss_init(pf); diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h b/drivers/net/ethernet/intel/ice/ice_osdep.h index 82bc54fec7f3..a2562f04267f 100644 --- a/drivers/net/ethernet/intel/ice/ice_osdep.h +++ b/drivers/net/ethernet/intel/ice/ice_osdep.h @@ -24,7 +24,7 @@ #define rd64(a, reg) readq((a)->hw_addr + (reg)) #define ice_flush(a) rd32((a), GLGEN_STAT) -#define ICE_M(m, s) ((m) << (s)) +#define ICE_M(m, s) ((m ## U) << (s)) struct ice_dma_mem { void *va; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 3b6605c8585e..c11eba07283c 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -601,17 +601,13 @@ void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx) /* Read the low 32 bit value */ raw_tstamp |= (u64)rd32(&pf->hw, PF_SB_ATQBAH); - /* For PHYs which don't implement a proper timestamp ready bitmap, - * verify that the timestamp value is different from the last cached - * timestamp. If it is not, skip this for now assuming it hasn't yet - * been captured by hardware. + /* Devices using this interface always verify the timestamp differs + * relative to the last cached timestamp value. */ - if (!drop_ts && tx->verify_cached && - raw_tstamp == tx->tstamps[idx].cached_tstamp) + if (raw_tstamp == tx->tstamps[idx].cached_tstamp) return; - if (tx->verify_cached && raw_tstamp) - tx->tstamps[idx].cached_tstamp = raw_tstamp; + tx->tstamps[idx].cached_tstamp = raw_tstamp; clear_bit(idx, tx->in_use); skb = tx->tstamps[idx].skb; tx->tstamps[idx].skb = NULL; @@ -701,9 +697,11 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx) hw = &pf->hw; /* Read the Tx ready status first */ - err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready); - if (err) - return; + if (tx->has_ready_bitmap) { + err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready); + if (err) + return; + } /* Drop packets if the link went down */ link_up = ptp_port->link_up; @@ -731,7 +729,8 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx) * If we do not, the hardware logic for generating a new * interrupt can get stuck on some devices. */ - if (!(tstamp_ready & BIT_ULL(phy_idx))) { + if (tx->has_ready_bitmap && + !(tstamp_ready & BIT_ULL(phy_idx))) { if (drop_ts) goto skip_ts_read; @@ -751,7 +750,7 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx) * from the last cached timestamp. If it is not, skip this for * now assuming it hasn't yet been captured by hardware. */ - if (!drop_ts && tx->verify_cached && + if (!drop_ts && !tx->has_ready_bitmap && raw_tstamp == tx->tstamps[idx].cached_tstamp) continue; @@ -761,7 +760,7 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx) skip_ts_read: spin_lock_irqsave(&tx->lock, flags); - if (tx->verify_cached && raw_tstamp) + if (!tx->has_ready_bitmap && raw_tstamp) tx->tstamps[idx].cached_tstamp = raw_tstamp; clear_bit(idx, tx->in_use); skb = tx->tstamps[idx].skb; @@ -965,6 +964,22 @@ ice_ptp_mark_tx_tracker_stale(struct ice_ptp_tx *tx) } /** + * ice_ptp_flush_all_tx_tracker - Flush all timestamp trackers on this clock + * @pf: Board private structure + * + * Called by the clock owner to flush all the Tx timestamp trackers associated + * with the clock. + */ +static void +ice_ptp_flush_all_tx_tracker(struct ice_pf *pf) +{ + struct ice_ptp_port *port; + + list_for_each_entry(port, &pf->ptp.ports_owner.ports, list_member) + ice_ptp_flush_tx_tracker(ptp_port_to_pf(port), &port->tx); +} + +/** * ice_ptp_release_tx_tracker - Release allocated memory for Tx tracker * @pf: Board private structure * @tx: Tx tracking structure to release @@ -1014,7 +1029,7 @@ ice_ptp_init_tx_e82x(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port) tx->block = port / ICE_PORTS_PER_QUAD; tx->offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT_E82X; tx->len = INDEX_PER_PORT_E82X; - tx->verify_cached = 0; + tx->has_ready_bitmap = 1; return ice_ptp_alloc_tx_tracker(tx); } @@ -1037,7 +1052,7 @@ ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx) * verify new timestamps against cached copy of the last read * timestamp. */ - tx->verify_cached = 1; + tx->has_ready_bitmap = 0; return ice_ptp_alloc_tx_tracker(tx); } @@ -1430,7 +1445,7 @@ void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) struct ice_ptp_port *ptp_port; struct ice_hw *hw = &pf->hw; - if (!test_bit(ICE_FLAG_PTP, pf->flags)) + if (pf->ptp.state != ICE_PTP_READY) return; if (WARN_ON_ONCE(port >= ICE_NUM_EXTERNAL_PORTS)) @@ -1456,14 +1471,14 @@ void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) } /** - * ice_ptp_tx_ena_intr - Enable or disable the Tx timestamp interrupt + * ice_ptp_cfg_phy_interrupt - Configure PHY interrupt settings * @pf: PF private structure * @ena: bool value to enable or disable interrupt * @threshold: Minimum number of packets at which intr is triggered * * Utility function to enable or disable Tx timestamp interrupt and threshold */ -static int ice_ptp_tx_ena_intr(struct ice_pf *pf, bool ena, u32 threshold) +static int ice_ptp_cfg_phy_interrupt(struct ice_pf *pf, bool ena, u32 threshold) { struct ice_hw *hw = &pf->hw; int err = 0; @@ -2162,7 +2177,7 @@ int ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr) { struct hwtstamp_config *config; - if (!test_bit(ICE_FLAG_PTP, pf->flags)) + if (pf->ptp.state != ICE_PTP_READY) return -EIO; config = &pf->ptp.tstamp_config; @@ -2232,7 +2247,7 @@ int ice_ptp_set_ts_config(struct ice_pf *pf, struct ifreq *ifr) struct hwtstamp_config config; int err; - if (!test_bit(ICE_FLAG_PTP, pf->flags)) + if (pf->ptp.state != ICE_PTP_READY) return -EAGAIN; if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) @@ -2616,7 +2631,7 @@ static void ice_ptp_periodic_work(struct kthread_work *work) struct ice_pf *pf = container_of(ptp, struct ice_pf, ptp); int err; - if (!test_bit(ICE_FLAG_PTP, pf->flags)) + if (pf->ptp.state != ICE_PTP_READY) return; err = ice_ptp_update_cached_phctime(pf); @@ -2629,36 +2644,72 @@ static void ice_ptp_periodic_work(struct kthread_work *work) } /** - * ice_ptp_reset - Initialize PTP hardware clock support after reset + * ice_ptp_prepare_for_reset - Prepare PTP for reset + * @pf: Board private structure + * @reset_type: the reset type being performed + */ +void ice_ptp_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type) +{ + struct ice_ptp *ptp = &pf->ptp; + u8 src_tmr; + + if (ptp->state != ICE_PTP_READY) + return; + + ptp->state = ICE_PTP_RESETTING; + + /* Disable timestamping for both Tx and Rx */ + ice_ptp_disable_timestamp_mode(pf); + + kthread_cancel_delayed_work_sync(&ptp->work); + + if (reset_type == ICE_RESET_PFR) + return; + + ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx); + + /* Disable periodic outputs */ + ice_ptp_disable_all_clkout(pf); + + src_tmr = ice_get_ptp_src_clock_index(&pf->hw); + + /* Disable source clock */ + wr32(&pf->hw, GLTSYN_ENA(src_tmr), (u32)~GLTSYN_ENA_TSYN_ENA_M); + + /* Acquire PHC and system timer to restore after reset */ + ptp->reset_time = ktime_get_real_ns(); +} + +/** + * ice_ptp_rebuild_owner - Initialize PTP clock owner after reset * @pf: Board private structure + * + * Companion function for ice_ptp_rebuild() which handles tasks that only the + * PTP clock owner instance should perform. */ -void ice_ptp_reset(struct ice_pf *pf) +static int ice_ptp_rebuild_owner(struct ice_pf *pf) { struct ice_ptp *ptp = &pf->ptp; struct ice_hw *hw = &pf->hw; struct timespec64 ts; - int err, itr = 1; u64 time_diff; - - if (test_bit(ICE_PFR_REQ, pf->state) || - !ice_pf_src_tmr_owned(pf)) - goto pfr; + int err; err = ice_ptp_init_phc(hw); if (err) - goto err; + return err; /* Acquire the global hardware lock */ if (!ice_ptp_lock(hw)) { err = -EBUSY; - goto err; + return err; } /* Write the increment time value to PHY and LAN */ err = ice_ptp_write_incval(hw, ice_base_incval(pf)); if (err) { ice_ptp_unlock(hw); - goto err; + return err; } /* Write the initial Time value to PHY and LAN using the cached PHC @@ -2674,38 +2725,54 @@ void ice_ptp_reset(struct ice_pf *pf) err = ice_ptp_write_init(pf, &ts); if (err) { ice_ptp_unlock(hw); - goto err; + return err; } /* Release the global hardware lock */ ice_ptp_unlock(hw); + /* Flush software tracking of any outstanding timestamps since we're + * about to flush the PHY timestamp block. + */ + ice_ptp_flush_all_tx_tracker(pf); + if (!ice_is_e810(hw)) { /* Enable quad interrupts */ - err = ice_ptp_tx_ena_intr(pf, true, itr); + err = ice_ptp_cfg_phy_interrupt(pf, true, 1); if (err) - goto err; - } + return err; -pfr: - /* Init Tx structures */ - if (ice_is_e810(&pf->hw)) { - err = ice_ptp_init_tx_e810(pf, &ptp->port.tx); - } else { - kthread_init_delayed_work(&ptp->port.ov_work, - ice_ptp_wait_for_offsets); - err = ice_ptp_init_tx_e82x(pf, &ptp->port.tx, - ptp->port.port_num); + ice_ptp_restart_all_phy(pf); } - if (err) + + return 0; +} + +/** + * ice_ptp_rebuild - Initialize PTP hardware clock support after reset + * @pf: Board private structure + * @reset_type: the reset type being performed + */ +void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) +{ + struct ice_ptp *ptp = &pf->ptp; + int err; + + if (ptp->state == ICE_PTP_READY) { + ice_ptp_prepare_for_reset(pf, reset_type); + } else if (ptp->state != ICE_PTP_RESETTING) { + err = -EINVAL; + dev_err(ice_pf_to_dev(pf), "PTP was not initialized\n"); goto err; + } - set_bit(ICE_FLAG_PTP, pf->flags); + if (ice_pf_src_tmr_owned(pf) && reset_type != ICE_RESET_PFR) { + err = ice_ptp_rebuild_owner(pf); + if (err) + goto err; + } - /* Restart the PHY timestamping block */ - if (!test_bit(ICE_PFR_REQ, pf->state) && - ice_pf_src_tmr_owned(pf)) - ice_ptp_restart_all_phy(pf); + ptp->state = ICE_PTP_READY; /* Start periodic work going */ kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0); @@ -2714,6 +2781,7 @@ pfr: return; err: + ptp->state = ICE_PTP_ERROR; dev_err(ice_pf_to_dev(pf), "PTP reset failed %d\n", err); } @@ -2923,39 +2991,6 @@ int ice_ptp_clock_index(struct ice_pf *pf) } /** - * ice_ptp_prepare_for_reset - Prepare PTP for reset - * @pf: Board private structure - */ -void ice_ptp_prepare_for_reset(struct ice_pf *pf) -{ - struct ice_ptp *ptp = &pf->ptp; - u8 src_tmr; - - clear_bit(ICE_FLAG_PTP, pf->flags); - - /* Disable timestamping for both Tx and Rx */ - ice_ptp_disable_timestamp_mode(pf); - - kthread_cancel_delayed_work_sync(&ptp->work); - - if (test_bit(ICE_PFR_REQ, pf->state)) - return; - - ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx); - - /* Disable periodic outputs */ - ice_ptp_disable_all_clkout(pf); - - src_tmr = ice_get_ptp_src_clock_index(&pf->hw); - - /* Disable source clock */ - wr32(&pf->hw, GLTSYN_ENA(src_tmr), (u32)~GLTSYN_ENA_TSYN_ENA_M); - - /* Acquire PHC and system timer to restore after reset */ - ptp->reset_time = ktime_get_real_ns(); -} - -/** * ice_ptp_init_owner - Initialize PTP_1588_CLOCK device * @pf: Board private structure * @@ -2967,7 +3002,7 @@ static int ice_ptp_init_owner(struct ice_pf *pf) { struct ice_hw *hw = &pf->hw; struct timespec64 ts; - int err, itr = 1; + int err; err = ice_ptp_init_phc(hw); if (err) { @@ -3002,7 +3037,7 @@ static int ice_ptp_init_owner(struct ice_pf *pf) if (!ice_is_e810(hw)) { /* Enable quad interrupts */ - err = ice_ptp_tx_ena_intr(pf, true, itr); + err = ice_ptp_cfg_phy_interrupt(pf, true, 1); if (err) goto err_exit; } @@ -3195,6 +3230,8 @@ void ice_ptp_init(struct ice_pf *pf) struct ice_hw *hw = &pf->hw; int err; + ptp->state = ICE_PTP_INITIALIZING; + ice_ptp_init_phy_model(hw); ice_ptp_init_tx_interrupt_mode(pf); @@ -3219,12 +3256,13 @@ void ice_ptp_init(struct ice_pf *pf) /* Configure initial Tx interrupt settings */ ice_ptp_cfg_tx_interrupt(pf); - set_bit(ICE_FLAG_PTP, pf->flags); - err = ice_ptp_init_work(pf, ptp); + err = ice_ptp_create_auxbus_device(pf); if (err) goto err; - err = ice_ptp_create_auxbus_device(pf); + ptp->state = ICE_PTP_READY; + + err = ice_ptp_init_work(pf, ptp); if (err) goto err; @@ -3237,7 +3275,7 @@ err: ptp_clock_unregister(ptp->clock); pf->ptp.clock = NULL; } - clear_bit(ICE_FLAG_PTP, pf->flags); + ptp->state = ICE_PTP_ERROR; dev_err(ice_pf_to_dev(pf), "PTP failed %d\n", err); } @@ -3250,9 +3288,11 @@ err: */ void ice_ptp_release(struct ice_pf *pf) { - if (!test_bit(ICE_FLAG_PTP, pf->flags)) + if (pf->ptp.state != ICE_PTP_READY) return; + pf->ptp.state = ICE_PTP_UNINIT; + /* Disable timestamping for both Tx and Rx */ ice_ptp_disable_timestamp_mode(pf); @@ -3260,8 +3300,6 @@ void ice_ptp_release(struct ice_pf *pf) ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx); - clear_bit(ICE_FLAG_PTP, pf->flags); - kthread_cancel_delayed_work_sync(&pf->ptp.work); ice_ptp_port_phy_stop(&pf->ptp.port); @@ -3271,6 +3309,9 @@ void ice_ptp_release(struct ice_pf *pf) pf->ptp.kworker = NULL; } + if (ice_pf_src_tmr_owned(pf)) + ice_ptp_unregister_auxbus_driver(pf); + if (!pf->ptp.clock) return; @@ -3280,7 +3321,5 @@ void ice_ptp_release(struct ice_pf *pf) ptp_clock_unregister(pf->ptp.clock); pf->ptp.clock = NULL; - ice_ptp_unregister_auxbus_driver(pf); - dev_info(ice_pf_to_dev(pf), "Removed PTP clock\n"); } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 087dd32d8762..3af20025043a 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -100,7 +100,7 @@ struct ice_perout_channel { * the last timestamp we read for a given index. If the current timestamp * value is the same as the cached value, we assume a new timestamp hasn't * been captured. This avoids reporting stale timestamps to the stack. This is - * only done if the verify_cached flag is set in ice_ptp_tx structure. + * only done if the has_ready_bitmap flag is not set in ice_ptp_tx structure. */ struct ice_tx_tstamp { struct sk_buff *skb; @@ -130,7 +130,9 @@ enum ice_tx_tstamp_work { * @init: if true, the tracker is initialized; * @calibrating: if true, the PHY is calibrating the Tx offset. During this * window, timestamps are temporarily disabled. - * @verify_cached: if true, verify new timestamp differs from last read value + * @has_ready_bitmap: if true, the hardware has a valid Tx timestamp ready + * bitmap register. If false, fall back to verifying new + * timestamp values against previously cached copy. * @last_ll_ts_idx_read: index of the last LL TS read by the FW */ struct ice_ptp_tx { @@ -143,7 +145,7 @@ struct ice_ptp_tx { u8 len; u8 init : 1; u8 calibrating : 1; - u8 verify_cached : 1; + u8 has_ready_bitmap : 1; s8 last_ll_ts_idx_read; }; @@ -203,8 +205,17 @@ struct ice_ptp_port_owner { #define GLTSYN_TGT_H_IDX_MAX 4 +enum ice_ptp_state { + ICE_PTP_UNINIT = 0, + ICE_PTP_INITIALIZING, + ICE_PTP_READY, + ICE_PTP_RESETTING, + ICE_PTP_ERROR, +}; + /** * struct ice_ptp - data used for integrating with CONFIG_PTP_1588_CLOCK + * @state: current state of PTP state machine * @tx_interrupt_mode: the TX interrupt mode for the PTP clock * @port: data for the PHY port initialization procedure * @ports_owner: data for the auxiliary driver owner @@ -227,6 +238,7 @@ struct ice_ptp_port_owner { * @late_cached_phc_updates: number of times cached PHC update is late */ struct ice_ptp { + enum ice_ptp_state state; enum ice_ptp_tx_interrupt tx_interrupt_mode; struct ice_ptp_port port; struct ice_ptp_port_owner ports_owner; @@ -304,8 +316,9 @@ enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf); u64 ice_ptp_get_rx_hwts(const union ice_32b_rx_flex_desc *rx_desc, const struct ice_pkt_ctx *pkt_ctx); -void ice_ptp_reset(struct ice_pf *pf); -void ice_ptp_prepare_for_reset(struct ice_pf *pf); +void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type); +void ice_ptp_prepare_for_reset(struct ice_pf *pf, + enum ice_reset_req reset_type); void ice_ptp_init(struct ice_pf *pf); void ice_ptp_release(struct ice_pf *pf); void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup); @@ -345,8 +358,15 @@ ice_ptp_get_rx_hwts(const union ice_32b_rx_flex_desc *rx_desc, return 0; } -static inline void ice_ptp_reset(struct ice_pf *pf) { } -static inline void ice_ptp_prepare_for_reset(struct ice_pf *pf) { } +static inline void ice_ptp_rebuild(struct ice_pf *pf, + enum ice_reset_req reset_type) +{ +} + +static inline void ice_ptp_prepare_for_reset(struct ice_pf *pf, + enum ice_reset_req reset_type) +{ +} static inline void ice_ptp_init(struct ice_pf *pf) { } static inline void ice_ptp_release(struct ice_pf *pf) { } static inline void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c index 839e5da24ad5..f8f1d2bdc1be 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c @@ -143,8 +143,12 @@ ice_rx_csum(struct ice_rx_ring *ring, struct sk_buff *skb, ipv6 = (decoded.outer_ip == ICE_RX_PTYPE_OUTER_IP) && (decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV6); - if (ipv4 && (rx_status0 & (BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_IPE_S) | - BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S)))) + if (ipv4 && (rx_status0 & (BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S)))) { + ring->vsi->back->hw_rx_eipe_error++; + return; + } + + if (ipv4 && (rx_status0 & (BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_IPE_S)))) goto checksum_fail; if (ipv6 && (rx_status0 & (BIT(ICE_RX_FLEX_DESC_STATUS0_IPV6EXADD_S)))) diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index 41ab6d7bbd9e..9ff92dba5823 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -132,6 +132,7 @@ enum ice_mac_type { ICE_MAC_E810, ICE_MAC_E830, ICE_MAC_GENERIC, + ICE_MAC_GENERIC_3K_E825, }; /* Media Types */ @@ -1072,7 +1073,7 @@ struct ice_aq_get_set_rss_lut_params { #define ICE_OROM_VER_BUILD_SHIFT 8 #define ICE_OROM_VER_BUILD_MASK (0xffff << ICE_OROM_VER_BUILD_SHIFT) #define ICE_OROM_VER_SHIFT 24 -#define ICE_OROM_VER_MASK (0xff << ICE_OROM_VER_SHIFT) +#define ICE_OROM_VER_MASK (0xffU << ICE_OROM_VER_SHIFT) #define ICE_SR_PFA_PTR 0x40 #define ICE_SR_1ST_NVM_BANK_PTR 0x42 #define ICE_SR_NVM_BANK_SIZE 0x43 diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index 8b81a1677045..8a051420fa19 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -217,42 +217,28 @@ static int ice_qp_dis(struct ice_vsi *vsi, u16 q_idx) */ static int ice_qp_ena(struct ice_vsi *vsi, u16 q_idx) { - DEFINE_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); - u16 size = __struct_size(qg_buf); struct ice_q_vector *q_vector; - struct ice_tx_ring *tx_ring; - struct ice_rx_ring *rx_ring; int err; - if (q_idx >= vsi->num_rxq || q_idx >= vsi->num_txq) - return -EINVAL; - - qg_buf->num_txqs = 1; - - tx_ring = vsi->tx_rings[q_idx]; - rx_ring = vsi->rx_rings[q_idx]; - q_vector = rx_ring->q_vector; - - err = ice_vsi_cfg_txq(vsi, tx_ring, qg_buf); + err = ice_vsi_cfg_single_txq(vsi, vsi->tx_rings, q_idx); if (err) return err; if (ice_is_xdp_ena_vsi(vsi)) { struct ice_tx_ring *xdp_ring = vsi->xdp_rings[q_idx]; - memset(qg_buf, 0, size); - qg_buf->num_txqs = 1; - err = ice_vsi_cfg_txq(vsi, xdp_ring, qg_buf); + err = ice_vsi_cfg_single_txq(vsi, vsi->xdp_rings, q_idx); if (err) return err; ice_set_ring_xdp(xdp_ring); ice_tx_xsk_pool(vsi, q_idx); } - err = ice_vsi_cfg_rxq(rx_ring); + err = ice_vsi_cfg_single_rxq(vsi, q_idx); if (err) return err; + q_vector = vsi->rx_rings[q_idx]->q_vector; ice_qvec_cfg_msix(vsi, q_vector); err = ice_vsi_ctrl_one_rx_ring(vsi, true, q_idx, true); diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/drivers/net/ethernet/intel/idpf/virtchnl2.h index 8dc837889723..4a3c4454d25a 100644 --- a/drivers/net/ethernet/intel/idpf/virtchnl2.h +++ b/drivers/net/ethernet/intel/idpf/virtchnl2.h @@ -978,7 +978,7 @@ struct virtchnl2_ptype { u8 proto_id_count; __le16 pad; __le16 proto_id[]; -}; +} __packed __aligned(2); VIRTCHNL2_CHECK_STRUCT_LEN(6, virtchnl2_ptype); /** diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index a2b759531cb7..3c2dc7bdebb5 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -637,7 +637,7 @@ struct igb_adapter { struct timespec64 period; } perout[IGB_N_PEROUT]; - char fw_version[32]; + char fw_version[48]; #ifdef CONFIG_IGB_HWMON struct hwmon_buff *igb_hwmon_buff; bool ets; diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index b66199c9bb3a..b87b23d2151c 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -3027,7 +3027,7 @@ static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) return ret; } -static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata) +static int igb_get_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -3038,10 +3038,10 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata) (hw->phy.media_type != e1000_media_type_copper)) return -EOPNOTSUPP; - edata->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full); + edata->supported_u32 = (SUPPORTED_1000baseT_Full | + SUPPORTED_100baseT_Full); if (!hw->dev_spec._82575.eee_disable) - edata->advertised = + edata->advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert); /* The IPCNFG and EEER registers are not supported on I354. */ @@ -3068,7 +3068,7 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata) if (ret_val) return -ENODATA; - edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data); + edata->lp_advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(phy_data); break; case e1000_i354: case e1000_i210: @@ -3079,7 +3079,7 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata) if (ret_val) return -ENODATA; - edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data); + edata->lp_advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(phy_data); break; default: @@ -3099,18 +3099,18 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata) edata->eee_enabled = false; edata->eee_active = false; edata->tx_lpi_enabled = false; - edata->advertised &= ~edata->advertised; + edata->advertised_u32 &= ~edata->advertised_u32; } return 0; } static int igb_set_eee(struct net_device *netdev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - struct ethtool_eee eee_curr; + struct ethtool_keee eee_curr; bool adv1g_eee = true, adv100m_eee = true; s32 ret_val; @@ -3118,7 +3118,7 @@ static int igb_set_eee(struct net_device *netdev, (hw->phy.media_type != e1000_media_type_copper)) return -EOPNOTSUPP; - memset(&eee_curr, 0, sizeof(struct ethtool_eee)); + memset(&eee_curr, 0, sizeof(struct ethtool_keee)); ret_val = igb_get_eee(netdev, &eee_curr); if (ret_val) @@ -3138,14 +3138,14 @@ static int igb_set_eee(struct net_device *netdev, return -EINVAL; } - if (!edata->advertised || (edata->advertised & + if (!edata->advertised_u32 || (edata->advertised_u32 & ~(ADVERTISE_100_FULL | ADVERTISE_1000_FULL))) { dev_err(&adapter->pdev->dev, "EEE Advertisement supports only 100Tx and/or 100T full duplex\n"); return -EINVAL; } - adv100m_eee = !!(edata->advertised & ADVERTISE_100_FULL); - adv1g_eee = !!(edata->advertised & ADVERTISE_1000_FULL); + adv100m_eee = !!(edata->advertised_u32 & ADVERTISE_100_FULL); + adv1g_eee = !!(edata->advertised_u32 & ADVERTISE_1000_FULL); } else if (!edata->eee_enabled) { dev_err(&adapter->pdev->dev, @@ -3153,7 +3153,7 @@ static int igb_set_eee(struct net_device *netdev, return -EINVAL; } - adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised); + adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised_u32); if (hw->dev_spec._82575.eee_disable != !edata->eee_enabled) { hw->dev_spec._82575.eee_disable = !edata->eee_enabled; adapter->flags |= IGB_FLAG_EEE; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 4df8d4153aa5..cebb44f51d5f 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3069,7 +3069,6 @@ void igb_set_fw_version(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct e1000_fw_version fw; - char *lbuf; igb_get_fw_version(hw, &fw); @@ -3077,34 +3076,36 @@ void igb_set_fw_version(struct igb_adapter *adapter) case e1000_i210: case e1000_i211: if (!(igb_get_flash_presence_i210(hw))) { - lbuf = kasprintf(GFP_KERNEL, "%2d.%2d-%d", - fw.invm_major, fw.invm_minor, - fw.invm_img_type); + snprintf(adapter->fw_version, + sizeof(adapter->fw_version), + "%2d.%2d-%d", + fw.invm_major, fw.invm_minor, + fw.invm_img_type); break; } fallthrough; default: /* if option rom is valid, display its version too */ if (fw.or_valid) { - lbuf = kasprintf(GFP_KERNEL, "%d.%d, 0x%08x, %d.%d.%d", - fw.eep_major, fw.eep_minor, - fw.etrack_id, fw.or_major, fw.or_build, - fw.or_patch); + snprintf(adapter->fw_version, + sizeof(adapter->fw_version), + "%d.%d, 0x%08x, %d.%d.%d", + fw.eep_major, fw.eep_minor, fw.etrack_id, + fw.or_major, fw.or_build, fw.or_patch); /* no option rom */ } else if (fw.etrack_id != 0X0000) { - lbuf = kasprintf(GFP_KERNEL, "%d.%d, 0x%08x", - fw.eep_major, fw.eep_minor, - fw.etrack_id); + snprintf(adapter->fw_version, + sizeof(adapter->fw_version), + "%d.%d, 0x%08x", + fw.eep_major, fw.eep_minor, fw.etrack_id); } else { - lbuf = kasprintf(GFP_KERNEL, "%d.%d.%d", fw.eep_major, - fw.eep_minor, fw.eep_build); + snprintf(adapter->fw_version, + sizeof(adapter->fw_version), + "%d.%d.%d", + fw.eep_major, fw.eep_minor, fw.eep_build); } break; } - - /* the truncate happens here if it doesn't fit */ - strscpy(adapter->fw_version, lbuf, sizeof(adapter->fw_version)); - kfree(lbuf); } /** diff --git a/drivers/net/ethernet/intel/igc/Makefile b/drivers/net/ethernet/intel/igc/Makefile index 95d1e8c490a4..ebffd3054285 100644 --- a/drivers/net/ethernet/intel/igc/Makefile +++ b/drivers/net/ethernet/intel/igc/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_IGC) += igc.o +igc-$(CONFIG_IGC_LEDS) += igc_leds.o igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \ igc_diag.o igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o igc_xdp.o diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 45430e246e9c..cfa6baccec55 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -168,7 +168,7 @@ struct igc_ring { struct igc_adapter { struct net_device *netdev; - struct ethtool_eee eee; + struct ethtool_keee eee; u16 eee_advert; unsigned long state; @@ -295,6 +295,9 @@ struct igc_adapter { struct timespec64 start; struct timespec64 period; } perout[IGC_N_PEROUT]; + + /* LEDs */ + struct mutex led_mutex; }; void igc_up(struct igc_adapter *adapter); @@ -585,7 +588,7 @@ enum igc_filter_match_flags { struct igc_nfc_filter { u8 match_flags; u16 etype; - __be16 vlan_etype; + u16 vlan_etype; u16 vlan_tci; u16 vlan_tci_mask; u8 src_addr[ETH_ALEN]; @@ -720,6 +723,8 @@ void igc_ptp_tx_hang(struct igc_adapter *adapter); void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); +int igc_led_setup(struct igc_adapter *adapter); + #define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring)) #define IGC_TXD_DCMD (IGC_ADVTXD_DCMD_EOP | IGC_ADVTXD_DCMD_RS) diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index b95d2c86e803..47c797dd2cd9 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -981,7 +981,7 @@ static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter, if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) { fsp->flow_type |= FLOW_EXT; - fsp->h_ext.vlan_etype = rule->filter.vlan_etype; + fsp->h_ext.vlan_etype = htons(rule->filter.vlan_etype); fsp->m_ext.vlan_etype = ETHER_TYPE_FULL_MASK; } @@ -1249,7 +1249,7 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule, /* VLAN etype matching */ if ((fsp->flow_type & FLOW_EXT) && fsp->h_ext.vlan_etype) { - rule->filter.vlan_etype = fsp->h_ext.vlan_etype; + rule->filter.vlan_etype = ntohs(fsp->h_ext.vlan_etype); rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_ETYPE; } @@ -1623,18 +1623,18 @@ static int igc_ethtool_set_priv_flags(struct net_device *netdev, u32 priv_flags) } static int igc_ethtool_get_eee(struct net_device *netdev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct igc_adapter *adapter = netdev_priv(netdev); struct igc_hw *hw = &adapter->hw; u32 eeer; if (hw->dev_spec._base.eee_enable) - edata->advertised = + edata->advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert); *edata = adapter->eee; - edata->supported = SUPPORTED_Autoneg; + edata->supported_u32 = SUPPORTED_Autoneg; eeer = rd32(IGC_EEER); @@ -1647,8 +1647,8 @@ static int igc_ethtool_get_eee(struct net_device *netdev, edata->eee_enabled = hw->dev_spec._base.eee_enable; - edata->advertised = SUPPORTED_Autoneg; - edata->lp_advertised = SUPPORTED_Autoneg; + edata->advertised_u32 = SUPPORTED_Autoneg; + edata->lp_advertised_u32 = SUPPORTED_Autoneg; /* Report correct negotiated EEE status for devices that * wrongly report EEE at half-duplex @@ -1657,21 +1657,21 @@ static int igc_ethtool_get_eee(struct net_device *netdev, edata->eee_enabled = false; edata->eee_active = false; edata->tx_lpi_enabled = false; - edata->advertised &= ~edata->advertised; + edata->advertised_u32 &= ~edata->advertised_u32; } return 0; } static int igc_ethtool_set_eee(struct net_device *netdev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct igc_adapter *adapter = netdev_priv(netdev); struct igc_hw *hw = &adapter->hw; - struct ethtool_eee eee_curr; + struct ethtool_keee eee_curr; s32 ret_val; - memset(&eee_curr, 0, sizeof(struct ethtool_eee)); + memset(&eee_curr, 0, sizeof(struct ethtool_keee)); ret_val = igc_ethtool_get_eee(netdev, &eee_curr); if (ret_val) { @@ -1699,7 +1699,7 @@ static int igc_ethtool_set_eee(struct net_device *netdev, return -EINVAL; } - adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised); + adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised_u32); if (hw->dev_spec._base.eee_enable != edata->eee_enabled) { hw->dev_spec._base.eee_enable = edata->eee_enabled; adapter->flags |= IGC_FLAG_EEE; diff --git a/drivers/net/ethernet/intel/igc/igc_leds.c b/drivers/net/ethernet/intel/igc/igc_leds.c new file mode 100644 index 000000000000..bf240c5daf86 --- /dev/null +++ b/drivers/net/ethernet/intel/igc/igc_leds.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2024 Linutronix GmbH */ + +#include <linux/bits.h> +#include <linux/leds.h> +#include <linux/netdevice.h> +#include <linux/pm_runtime.h> +#include <uapi/linux/uleds.h> + +#include "igc.h" + +#define IGC_NUM_LEDS 3 + +#define IGC_LEDCTL_LED0_MODE_SHIFT 0 +#define IGC_LEDCTL_LED0_MODE_MASK GENMASK(3, 0) +#define IGC_LEDCTL_LED0_BLINK BIT(7) +#define IGC_LEDCTL_LED1_MODE_SHIFT 8 +#define IGC_LEDCTL_LED1_MODE_MASK GENMASK(11, 8) +#define IGC_LEDCTL_LED1_BLINK BIT(15) +#define IGC_LEDCTL_LED2_MODE_SHIFT 16 +#define IGC_LEDCTL_LED2_MODE_MASK GENMASK(19, 16) +#define IGC_LEDCTL_LED2_BLINK BIT(23) + +#define IGC_LEDCTL_MODE_ON 0x00 +#define IGC_LEDCTL_MODE_OFF 0x01 +#define IGC_LEDCTL_MODE_LINK_10 0x05 +#define IGC_LEDCTL_MODE_LINK_100 0x06 +#define IGC_LEDCTL_MODE_LINK_1000 0x07 +#define IGC_LEDCTL_MODE_LINK_2500 0x08 +#define IGC_LEDCTL_MODE_ACTIVITY 0x0b + +#define IGC_SUPPORTED_MODES \ + (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK_1000) | \ + BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_10) | \ + BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX)) + +#define IGC_ACTIVITY_MODES \ + (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX)) + +struct igc_led_classdev { + struct net_device *netdev; + struct led_classdev led; + int index; +}; + +#define lcdev_to_igc_ldev(lcdev) \ + container_of(lcdev, struct igc_led_classdev, led) + +static void igc_led_select(struct igc_adapter *adapter, int led, + u32 *mask, u32 *shift, u32 *blink) +{ + switch (led) { + case 0: + *mask = IGC_LEDCTL_LED0_MODE_MASK; + *shift = IGC_LEDCTL_LED0_MODE_SHIFT; + *blink = IGC_LEDCTL_LED0_BLINK; + break; + case 1: + *mask = IGC_LEDCTL_LED1_MODE_MASK; + *shift = IGC_LEDCTL_LED1_MODE_SHIFT; + *blink = IGC_LEDCTL_LED1_BLINK; + break; + case 2: + *mask = IGC_LEDCTL_LED2_MODE_MASK; + *shift = IGC_LEDCTL_LED2_MODE_SHIFT; + *blink = IGC_LEDCTL_LED2_BLINK; + break; + default: + *mask = *shift = *blink = 0; + netdev_err(adapter->netdev, "Unknown LED %d selected!\n", led); + } +} + +static void igc_led_set(struct igc_adapter *adapter, int led, u32 mode, + bool blink) +{ + u32 shift, mask, blink_bit, ledctl; + struct igc_hw *hw = &adapter->hw; + + igc_led_select(adapter, led, &mask, &shift, &blink_bit); + + pm_runtime_get_sync(&adapter->pdev->dev); + mutex_lock(&adapter->led_mutex); + + /* Set mode */ + ledctl = rd32(IGC_LEDCTL); + ledctl &= ~mask; + ledctl |= mode << shift; + + /* Configure blinking */ + if (blink) + ledctl |= blink_bit; + else + ledctl &= ~blink_bit; + wr32(IGC_LEDCTL, ledctl); + + mutex_unlock(&adapter->led_mutex); + pm_runtime_put(&adapter->pdev->dev); +} + +static u32 igc_led_get(struct igc_adapter *adapter, int led) +{ + u32 shift, mask, blink_bit, ledctl; + struct igc_hw *hw = &adapter->hw; + + igc_led_select(adapter, led, &mask, &shift, &blink_bit); + + pm_runtime_get_sync(&adapter->pdev->dev); + mutex_lock(&adapter->led_mutex); + ledctl = rd32(IGC_LEDCTL); + mutex_unlock(&adapter->led_mutex); + pm_runtime_put(&adapter->pdev->dev); + + return (ledctl & mask) >> shift; +} + +static int igc_led_brightness_set_blocking(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev); + struct igc_adapter *adapter = netdev_priv(ldev->netdev); + u32 mode; + + if (brightness) + mode = IGC_LEDCTL_MODE_ON; + else + mode = IGC_LEDCTL_MODE_OFF; + + netdev_dbg(adapter->netdev, "Set brightness for LED %d to mode %u!\n", + ldev->index, mode); + + igc_led_set(adapter, ldev->index, mode, false); + + return 0; +} + +static int igc_led_hw_control_is_supported(struct led_classdev *led_cdev, + unsigned long flags) +{ + if (flags & ~IGC_SUPPORTED_MODES) + return -EOPNOTSUPP; + + /* If Tx and Rx selected, activity can be offloaded unless some other + * mode is selected as well. + */ + if ((flags & BIT(TRIGGER_NETDEV_TX)) && + (flags & BIT(TRIGGER_NETDEV_RX)) && + !(flags & ~IGC_ACTIVITY_MODES)) + return 0; + + /* Single Rx or Tx activity is not supported. */ + if (flags & IGC_ACTIVITY_MODES) + return -EOPNOTSUPP; + + /* Only one mode can be active at a given time. */ + if (flags & (flags - 1)) + return -EOPNOTSUPP; + + return 0; +} + +static int igc_led_hw_control_set(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev); + struct igc_adapter *adapter = netdev_priv(ldev->netdev); + u32 mode = IGC_LEDCTL_MODE_OFF; + bool blink = false; + + if (flags & BIT(TRIGGER_NETDEV_LINK_10)) + mode = IGC_LEDCTL_MODE_LINK_10; + if (flags & BIT(TRIGGER_NETDEV_LINK_100)) + mode = IGC_LEDCTL_MODE_LINK_100; + if (flags & BIT(TRIGGER_NETDEV_LINK_1000)) + mode = IGC_LEDCTL_MODE_LINK_1000; + if (flags & BIT(TRIGGER_NETDEV_LINK_2500)) + mode = IGC_LEDCTL_MODE_LINK_2500; + if ((flags & BIT(TRIGGER_NETDEV_TX)) && + (flags & BIT(TRIGGER_NETDEV_RX))) + mode = IGC_LEDCTL_MODE_ACTIVITY; + + netdev_dbg(adapter->netdev, "Set HW control for LED %d to mode %u!\n", + ldev->index, mode); + + /* blink is recommended for activity */ + if (mode == IGC_LEDCTL_MODE_ACTIVITY) + blink = true; + + igc_led_set(adapter, ldev->index, mode, blink); + + return 0; +} + +static int igc_led_hw_control_get(struct led_classdev *led_cdev, + unsigned long *flags) +{ + struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev); + struct igc_adapter *adapter = netdev_priv(ldev->netdev); + u32 mode; + + mode = igc_led_get(adapter, ldev->index); + + switch (mode) { + case IGC_LEDCTL_MODE_ACTIVITY: + *flags = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); + break; + case IGC_LEDCTL_MODE_LINK_10: + *flags = BIT(TRIGGER_NETDEV_LINK_10); + break; + case IGC_LEDCTL_MODE_LINK_100: + *flags = BIT(TRIGGER_NETDEV_LINK_100); + break; + case IGC_LEDCTL_MODE_LINK_1000: + *flags = BIT(TRIGGER_NETDEV_LINK_1000); + break; + case IGC_LEDCTL_MODE_LINK_2500: + *flags = BIT(TRIGGER_NETDEV_LINK_2500); + break; + } + + return 0; +} + +static struct device *igc_led_hw_control_get_device(struct led_classdev *led_cdev) +{ + struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev); + + return &ldev->netdev->dev; +} + +static void igc_led_get_name(struct igc_adapter *adapter, int index, char *buf, + size_t buf_len) +{ + snprintf(buf, buf_len, "igc-%x%x-led%d", + pci_domain_nr(adapter->pdev->bus), + pci_dev_id(adapter->pdev), index); +} + +static void igc_setup_ldev(struct igc_led_classdev *ldev, + struct net_device *netdev, int index) +{ + struct igc_adapter *adapter = netdev_priv(netdev); + struct led_classdev *led_cdev = &ldev->led; + char led_name[LED_MAX_NAME_SIZE]; + + ldev->netdev = netdev; + ldev->index = index; + + igc_led_get_name(adapter, index, led_name, LED_MAX_NAME_SIZE); + led_cdev->name = led_name; + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + led_cdev->max_brightness = 1; + led_cdev->brightness_set_blocking = igc_led_brightness_set_blocking; + led_cdev->hw_control_trigger = "netdev"; + led_cdev->hw_control_is_supported = igc_led_hw_control_is_supported; + led_cdev->hw_control_set = igc_led_hw_control_set; + led_cdev->hw_control_get = igc_led_hw_control_get; + led_cdev->hw_control_get_device = igc_led_hw_control_get_device; + + devm_led_classdev_register(&netdev->dev, led_cdev); +} + +int igc_led_setup(struct igc_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct device *dev = &netdev->dev; + struct igc_led_classdev *leds; + int i; + + mutex_init(&adapter->led_mutex); + + leds = devm_kcalloc(dev, IGC_NUM_LEDS, sizeof(*leds), GFP_KERNEL); + if (!leds) + return -ENOMEM; + + for (i = 0; i < IGC_NUM_LEDS; i++) + igc_setup_ldev(leds + i, netdev, i); + + return 0; +} diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index ba8d3fe186ae..3af52d238f3b 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -3385,7 +3385,7 @@ static int igc_flex_filter_select(struct igc_adapter *adapter, u32 fhftsl; if (input->index >= MAX_FLEX_FILTER) { - dev_err(&adapter->pdev->dev, "Wrong Flex Filter index selected!\n"); + netdev_err(adapter->netdev, "Wrong Flex Filter index selected!\n"); return -EINVAL; } @@ -3420,7 +3420,6 @@ static int igc_flex_filter_select(struct igc_adapter *adapter, static int igc_write_flex_filter_ll(struct igc_adapter *adapter, struct igc_flex_filter *input) { - struct device *dev = &adapter->pdev->dev; struct igc_hw *hw = &adapter->hw; u8 *data = input->data; u8 *mask = input->mask; @@ -3434,7 +3433,7 @@ static int igc_write_flex_filter_ll(struct igc_adapter *adapter, * out early to avoid surprises later. */ if (input->length % 8 != 0) { - dev_err(dev, "The length of a flex filter has to be 8 byte aligned!\n"); + netdev_err(adapter->netdev, "The length of a flex filter has to be 8 byte aligned!\n"); return -EINVAL; } @@ -3504,8 +3503,8 @@ static int igc_write_flex_filter_ll(struct igc_adapter *adapter, } wr32(IGC_WUFC, wufc); - dev_dbg(&adapter->pdev->dev, "Added flex filter %u to HW.\n", - input->index); + netdev_dbg(adapter->netdev, "Added flex filter %u to HW.\n", + input->index); return 0; } @@ -3577,9 +3576,9 @@ static bool igc_flex_filter_in_use(struct igc_adapter *adapter) static int igc_add_flex_filter(struct igc_adapter *adapter, struct igc_nfc_rule *rule) { - struct igc_flex_filter flex = { }; struct igc_nfc_filter *filter = &rule->filter; unsigned int eth_offset, user_offset; + struct igc_flex_filter flex = { }; int ret, index; bool vlan; @@ -3615,10 +3614,12 @@ static int igc_add_flex_filter(struct igc_adapter *adapter, ETH_ALEN, NULL); /* Add VLAN etype */ - if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) - igc_flex_filter_add_field(&flex, &filter->vlan_etype, 12, - sizeof(filter->vlan_etype), - NULL); + if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) { + __be16 vlan_etype = cpu_to_be16(filter->vlan_etype); + + igc_flex_filter_add_field(&flex, &vlan_etype, 12, + sizeof(vlan_etype), NULL); + } /* Add VLAN TCI */ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) @@ -6977,6 +6978,12 @@ static int igc_probe(struct pci_dev *pdev, pm_runtime_put_noidle(&pdev->dev); + if (IS_ENABLED(CONFIG_IGC_LEDS)) { + err = igc_led_setup(adapter); + if (err) + goto err_register; + } + return 0; err_register: diff --git a/drivers/net/ethernet/intel/igc/igc_phy.c b/drivers/net/ethernet/intel/igc/igc_phy.c index 7cd8716d2ffa..861f37076861 100644 --- a/drivers/net/ethernet/intel/igc/igc_phy.c +++ b/drivers/net/ethernet/intel/igc/igc_phy.c @@ -130,11 +130,7 @@ void igc_power_down_phy_copper(struct igc_hw *hw) /* The PHY will retain its settings across a power down/up cycle */ hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); mii_reg |= MII_CR_POWER_DOWN; - - /* Temporary workaround - should be removed when PHY will implement - * IEEE registers as properly - */ - /* hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);*/ + hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); usleep_range(1000, 2000); } diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index d38c87d7e5e8..e5b893fc5b66 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -12,6 +12,7 @@ #define IGC_MDIC 0x00020 /* MDI Control - RW */ #define IGC_CONNSW 0x00034 /* Copper/Fiber switch control - RW */ #define IGC_VET 0x00038 /* VLAN Ether Type - RW */ +#define IGC_LEDCTL 0x00E00 /* LED Control - RW */ #define IGC_I225_PHPM 0x00E14 /* I225 PHY Power Management */ #define IGC_GPHY_VERSION 0x0001E /* I225 gPHY Firmware Version */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index b6f0376e42f4..559b443c409f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -949,19 +949,19 @@ void ixgbe_alloc_rx_buffers(struct ixgbe_ring *, u16); void ixgbe_write_eitr(struct ixgbe_q_vector *); int ixgbe_poll(struct napi_struct *napi, int budget); int ethtool_ioctl(struct ifreq *ifr); -s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw); -s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl); -s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl); -s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, +int ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw); +int ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl); +int ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl); +int ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, union ixgbe_atr_hash_dword input, union ixgbe_atr_hash_dword common, u8 queue); -s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, +int ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, union ixgbe_atr_input *input_mask); -s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw, +int ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw, union ixgbe_atr_input *input, u16 soft_id, u8 queue); -s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw, +int ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw, union ixgbe_atr_input *input, u16 soft_id); void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input, @@ -1059,7 +1059,7 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, u32 ixgbe_rss_indir_tbl_entries(struct ixgbe_adapter *adapter); void ixgbe_store_key(struct ixgbe_adapter *adapter); void ixgbe_store_reta(struct ixgbe_adapter *adapter); -s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, +int ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm); #ifdef CONFIG_IXGBE_IPSEC void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index 6835d5f18753..283a23150a4d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -15,10 +15,10 @@ #define IXGBE_82598_VFT_TBL_SIZE 128 #define IXGBE_82598_RX_PB_SIZE 512 -static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, +static int ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); -static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset, u8 *eeprom_data); /** @@ -66,7 +66,7 @@ out: IXGBE_WRITE_REG(hw, IXGBE_GCR, gcr); } -static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw) +static int ixgbe_get_invariants_82598(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; @@ -93,12 +93,12 @@ static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw) * not known. Perform the SFP init if necessary. * **/ -static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw) +static int ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; struct ixgbe_phy_info *phy = &hw->phy; - s32 ret_val; u16 list_offset, data_offset; + int ret_val; /* Identify the PHY */ phy->ops.identify(hw); @@ -148,9 +148,9 @@ static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw) * Then set pcie completion timeout * **/ -static s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw) +static int ixgbe_start_hw_82598(struct ixgbe_hw *hw) { - s32 ret_val; + int ret_val; ret_val = ixgbe_start_hw_generic(hw); if (ret_val) @@ -170,7 +170,7 @@ static s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw) * * Determines the link capabilities by reading the AUTOC register. **/ -static s32 ixgbe_get_link_capabilities_82598(struct ixgbe_hw *hw, +static int ixgbe_get_link_capabilities_82598(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *autoneg) { @@ -271,7 +271,7 @@ static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw) * * Enable flow control according to the current settings. **/ -static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw) +static int ixgbe_fc_enable_82598(struct ixgbe_hw *hw) { u32 fctrl_reg; u32 rmcs_reg; @@ -411,13 +411,13 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw) * Configures link settings based on values in the ixgbe_hw struct. * Restarts the link. Performs autonegotiation if needed. **/ -static s32 ixgbe_start_mac_link_82598(struct ixgbe_hw *hw, +static int ixgbe_start_mac_link_82598(struct ixgbe_hw *hw, bool autoneg_wait_to_complete) { + int status = 0; u32 autoc_reg; u32 links_reg; u32 i; - s32 status = 0; /* Restart link */ autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC); @@ -457,7 +457,7 @@ static s32 ixgbe_start_mac_link_82598(struct ixgbe_hw *hw, * Function indicates success when phy link is available. If phy is not ready * within 5 seconds of MAC indicating link, the function returns error. **/ -static s32 ixgbe_validate_link_ready(struct ixgbe_hw *hw) +static int ixgbe_validate_link_ready(struct ixgbe_hw *hw) { u32 timeout; u16 an_reg; @@ -493,7 +493,7 @@ static s32 ixgbe_validate_link_ready(struct ixgbe_hw *hw) * * Reads the links register to determine if link is up and the current speed **/ -static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, +static int ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *link_up, bool link_up_wait_to_complete) { @@ -579,7 +579,7 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, * * Set the link speed in the AUTOC register and restarts link. **/ -static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw, +static int ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete) { @@ -624,11 +624,11 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw, * * Sets the link speed in the AUTOC register in the MAC and restarts link. **/ -static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) +static int ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) { - s32 status; + int status; /* Setup the PHY according to input speed */ status = hw->phy.ops.setup_link_speed(hw, speed, @@ -647,15 +647,15 @@ static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, * clears all interrupts, performing a PHY reset, and performing a link (MAC) * reset. **/ -static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw) +static int ixgbe_reset_hw_82598(struct ixgbe_hw *hw) { - s32 status; - s32 phy_status = 0; - u32 ctrl; + int phy_status = 0; + u8 analog_val; u32 gheccr; - u32 i; + int status; u32 autoc; - u8 analog_val; + u32 ctrl; + u32 i; /* Call adapter stop to disable tx/rx and clear interrupts */ status = hw->mac.ops.stop_adapter(hw); @@ -781,7 +781,7 @@ mac_reset_top: * @rar: receive address register index to associate with a VMDq index * @vmdq: VMDq set index **/ -static s32 ixgbe_set_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) +static int ixgbe_set_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) { u32 rar_high; u32 rar_entries = hw->mac.num_rar_entries; @@ -805,7 +805,7 @@ static s32 ixgbe_set_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) * @rar: receive address register index to associate with a VMDq index * @vmdq: VMDq clear index (not used in 82598, but elsewhere) **/ -static s32 ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) +static int ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) { u32 rar_high; u32 rar_entries = hw->mac.num_rar_entries; @@ -836,7 +836,7 @@ static s32 ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) * * Turn on/off specified VLAN in the VLAN filter table. **/ -static s32 ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind, +static int ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on, bool vlvf_bypass) { u32 regindex; @@ -881,7 +881,7 @@ static s32 ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind, * * Clears the VLAN filter table, and the VMDq index associated with the filter **/ -static s32 ixgbe_clear_vfta_82598(struct ixgbe_hw *hw) +static int ixgbe_clear_vfta_82598(struct ixgbe_hw *hw) { u32 offset; u32 vlanbyte; @@ -905,7 +905,7 @@ static s32 ixgbe_clear_vfta_82598(struct ixgbe_hw *hw) * * Performs read operation to Atlas analog register specified. **/ -static s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val) +static int ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val) { u32 atlas_ctl; @@ -927,7 +927,7 @@ static s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val) * * Performs write operation to Atlas analog register specified. **/ -static s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val) +static int ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val) { u32 atlas_ctl; @@ -948,13 +948,13 @@ static s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val) * * Performs 8 byte read operation to SFP module's data over I2C interface. **/ -static s32 ixgbe_read_i2c_phy_82598(struct ixgbe_hw *hw, u8 dev_addr, +static int ixgbe_read_i2c_phy_82598(struct ixgbe_hw *hw, u8 dev_addr, u8 byte_offset, u8 *eeprom_data) { - s32 status = 0; u16 sfp_addr = 0; u16 sfp_data = 0; u16 sfp_stat = 0; + int status = 0; u16 gssr; u32 i; @@ -1019,7 +1019,7 @@ out: * * Performs 8 byte read operation to SFP module's EEPROM over I2C interface. **/ -static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset, u8 *eeprom_data) { return ixgbe_read_i2c_phy_82598(hw, IXGBE_I2C_EEPROM_DEV_ADDR, @@ -1034,8 +1034,8 @@ static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset, * * Performs 8 byte read operation to SFP module's SFF-8472 data over I2C **/ -static s32 ixgbe_read_i2c_sff8472_82598(struct ixgbe_hw *hw, u8 byte_offset, - u8 *sff8472_data) +static int ixgbe_read_i2c_sff8472_82598(struct ixgbe_hw *hw, u8 byte_offset, + u8 *sff8472_data) { return ixgbe_read_i2c_phy_82598(hw, IXGBE_I2C_EEPROM_DEV_ADDR2, byte_offset, sff8472_data); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index 339e106a5732..e0c300fe5cee 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -21,24 +21,24 @@ static void ixgbe_enable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); static void ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *, ixgbe_link_speed); -static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, +static int ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw); -static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, +static int ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, bool autoneg_wait_to_complete); -static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); -static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw, +static int ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); +static int ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); -static s32 ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw); -static s32 ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw); +static int ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data); -static s32 ixgbe_write_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_write_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data); -static s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw); +static int ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw); static bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw); bool ixgbe_mng_enabled(struct ixgbe_hw *hw) @@ -98,10 +98,10 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw) } } -static s32 ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw) +static int ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw) { - s32 ret_val; u16 list_offset, data_offset, data_value; + int ret_val; if (hw->phy.sfp_type != ixgbe_sfp_type_unknown) { ixgbe_init_mac_link_ops_82599(hw); @@ -173,10 +173,10 @@ setup_sfp_err: * prot_autoc_write_82599(). Note, that locked can only be true in cases * where this function doesn't return an error. **/ -static s32 prot_autoc_read_82599(struct ixgbe_hw *hw, bool *locked, +static int prot_autoc_read_82599(struct ixgbe_hw *hw, bool *locked, u32 *reg_val) { - s32 ret_val; + int ret_val; *locked = false; /* If LESM is on then we need to hold the SW/FW semaphore. */ @@ -203,9 +203,9 @@ static s32 prot_autoc_read_82599(struct ixgbe_hw *hw, bool *locked, * This part (82599) may need to hold a the SW/FW lock around all writes to * AUTOC. Likewise after a write we need to do a pipeline reset. **/ -static s32 prot_autoc_write_82599(struct ixgbe_hw *hw, u32 autoc, bool locked) +static int prot_autoc_write_82599(struct ixgbe_hw *hw, u32 autoc, bool locked) { - s32 ret_val = 0; + int ret_val = 0; /* Blocked by MNG FW so bail */ if (ixgbe_check_reset_blocked(hw)) @@ -237,7 +237,7 @@ out: return ret_val; } -static s32 ixgbe_get_invariants_82599(struct ixgbe_hw *hw) +static int ixgbe_get_invariants_82599(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; @@ -263,11 +263,11 @@ static s32 ixgbe_get_invariants_82599(struct ixgbe_hw *hw) * not known. Perform the SFP init if necessary. * **/ -static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw) +static int ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; struct ixgbe_phy_info *phy = &hw->phy; - s32 ret_val; + int ret_val; u32 esdp; if (hw->device_id == IXGBE_DEV_ID_82599_QSFP_SF_QP) { @@ -322,7 +322,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw) * * Determines the link capabilities by reading the AUTOC register. **/ -static s32 ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw, +static int ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *autoneg) { @@ -500,14 +500,14 @@ static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw) * Configures link settings based on values in the ixgbe_hw struct. * Restarts the link. Performs autonegotiation if needed. **/ -static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, - bool autoneg_wait_to_complete) +static int ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, + bool autoneg_wait_to_complete) { + bool got_lock = false; + int status = 0; u32 autoc_reg; u32 links_reg; u32 i; - s32 status = 0; - bool got_lock = false; if (ixgbe_verify_lesm_fw_enabled_82599(hw)) { status = hw->mac.ops.acquire_swfw_sync(hw, @@ -657,15 +657,15 @@ ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *hw, ixgbe_link_speed speed) * * Implements the Intel SmartSpeed algorithm. **/ -static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) +static int ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) { - s32 status = 0; ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; - s32 i, j; - bool link_up = false; u32 autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC); + bool link_up = false; + int status = 0; + s32 i, j; /* Set autoneg_advertised value based on input link speed */ hw->phy.autoneg_advertised = 0; @@ -767,16 +767,15 @@ out: * * Set the link speed in the AUTOC register and restarts link. **/ -static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, +static int ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete) { - bool autoneg = false; - s32 status; - u32 pma_pmd_1g, link_mode, links_reg, i; - u32 autoc2 = IXGBE_READ_REG(hw, IXGBE_AUTOC2); - u32 pma_pmd_10g_serial = autoc2 & IXGBE_AUTOC2_10G_SERIAL_PMA_PMD_MASK; ixgbe_link_speed link_capabilities = IXGBE_LINK_SPEED_UNKNOWN; + u32 pma_pmd_10g_serial, pma_pmd_1g, link_mode, links_reg, i; + u32 autoc2 = IXGBE_READ_REG(hw, IXGBE_AUTOC2); + bool autoneg = false; + int status; /* holds the value of AUTOC register at this current point in time */ u32 current_autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC); @@ -785,6 +784,8 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, /* temporary variable used for comparison purposes */ u32 autoc = current_autoc; + pma_pmd_10g_serial = autoc2 & IXGBE_AUTOC2_10G_SERIAL_PMA_PMD_MASK; + /* Check to see if speed passed in is supported. */ status = hw->mac.ops.get_link_capabilities(hw, &link_capabilities, &autoneg); @@ -882,11 +883,11 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, * * Restarts link on PHY and MAC based on settings passed in. **/ -static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw, +static int ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete) { - s32 status; + int status; /* Setup the PHY according to input speed */ status = hw->phy.ops.setup_link_speed(hw, speed, @@ -905,13 +906,13 @@ static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw, * and clears all interrupts, perform a PHY reset, and perform a link (MAC) * reset. **/ -static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw) +static int ixgbe_reset_hw_82599(struct ixgbe_hw *hw) { ixgbe_link_speed link_speed; - s32 status; u32 ctrl, i, autoc, autoc2; - u32 curr_lms; bool link_up = false; + u32 curr_lms; + int status; /* Call adapter stop to disable tx/rx and clear interrupts */ status = hw->mac.ops.stop_adapter(hw); @@ -1081,7 +1082,7 @@ mac_reset_top: * @hw: pointer to hardware structure * @fdircmd: current value of FDIRCMD register */ -static s32 ixgbe_fdir_check_cmd_complete(struct ixgbe_hw *hw, u32 *fdircmd) +static int ixgbe_fdir_check_cmd_complete(struct ixgbe_hw *hw, u32 *fdircmd) { int i; @@ -1099,12 +1100,12 @@ static s32 ixgbe_fdir_check_cmd_complete(struct ixgbe_hw *hw, u32 *fdircmd) * ixgbe_reinit_fdir_tables_82599 - Reinitialize Flow Director tables. * @hw: pointer to hardware structure **/ -s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw) +int ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw) { - int i; u32 fdirctrl = IXGBE_READ_REG(hw, IXGBE_FDIRCTRL); u32 fdircmd; - s32 err; + int err; + int i; fdirctrl &= ~IXGBE_FDIRCTRL_INIT_DONE; @@ -1212,7 +1213,7 @@ static void ixgbe_fdir_enable_82599(struct ixgbe_hw *hw, u32 fdirctrl) * @fdirctrl: value to write to flow director control register, initially * contains just the value of the Rx packet buffer allocation **/ -s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl) +int ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl) { /* * Continue setup of fdirctrl register bits: @@ -1236,7 +1237,7 @@ s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl) * @fdirctrl: value to write to flow director control register, initially * contains just the value of the Rx packet buffer allocation **/ -s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl) +int ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl) { /* * Continue setup of fdirctrl register bits: @@ -1359,7 +1360,7 @@ static u32 ixgbe_atr_compute_sig_hash_82599(union ixgbe_atr_hash_dword input, * Note that the tunnel bit in input must not be set when the hardware * tunneling support does not exist. **/ -s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, +int ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, union ixgbe_atr_hash_dword input, union ixgbe_atr_hash_dword common, u8 queue) @@ -1515,7 +1516,7 @@ static u32 ixgbe_get_fdirtcpm_82599(union ixgbe_atr_input *input_mask) #define IXGBE_STORE_AS_BE16(_value) __swab16(ntohs((_value))) -s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, +int ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, union ixgbe_atr_input *input_mask) { /* mask IPv6 since it is currently not supported */ @@ -1627,12 +1628,12 @@ s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, return 0; } -s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw, +int ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw, union ixgbe_atr_input *input, u16 soft_id, u8 queue) { u32 fdirport, fdirvlan, fdirhash, fdircmd; - s32 err; + int err; /* currently IPv6 is not supported, must be programmed with 0 */ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIPv6(0), @@ -1690,13 +1691,13 @@ s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw, return 0; } -s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw, +int ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw, union ixgbe_atr_input *input, u16 soft_id) { u32 fdirhash; u32 fdircmd; - s32 err; + int err; /* configure FDIRHASH register */ fdirhash = (__force u32)input->formatted.bkt_hash; @@ -1734,7 +1735,7 @@ s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw, * * Performs read operation to Omer analog register specified. **/ -static s32 ixgbe_read_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 *val) +static int ixgbe_read_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 *val) { u32 core_ctl; @@ -1756,7 +1757,7 @@ static s32 ixgbe_read_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 *val) * * Performs write operation to Omer analog register specified. **/ -static s32 ixgbe_write_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 val) +static int ixgbe_write_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 val) { u32 core_ctl; @@ -1776,9 +1777,9 @@ static s32 ixgbe_write_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 val) * and the generation start_hw function. * Then performs revision-specific operations, if any. **/ -static s32 ixgbe_start_hw_82599(struct ixgbe_hw *hw) +static int ixgbe_start_hw_82599(struct ixgbe_hw *hw) { - s32 ret_val = 0; + int ret_val = 0; ret_val = ixgbe_start_hw_generic(hw); if (ret_val) @@ -1802,9 +1803,9 @@ static s32 ixgbe_start_hw_82599(struct ixgbe_hw *hw) * If PHY already detected, maintains current PHY type in hw struct, * otherwise executes the PHY detection routine. **/ -static s32 ixgbe_identify_phy_82599(struct ixgbe_hw *hw) +static int ixgbe_identify_phy_82599(struct ixgbe_hw *hw) { - s32 status; + int status; /* Detect PHY if not unknown - returns success if already detected. */ status = ixgbe_identify_phy_generic(hw); @@ -1835,7 +1836,7 @@ static s32 ixgbe_identify_phy_82599(struct ixgbe_hw *hw) * * Enables the Rx DMA unit for 82599 **/ -static s32 ixgbe_enable_rx_dma_82599(struct ixgbe_hw *hw, u32 regval) +static int ixgbe_enable_rx_dma_82599(struct ixgbe_hw *hw, u32 regval) { /* * Workaround for 82599 silicon errata when enabling the Rx datapath. @@ -1865,12 +1866,12 @@ static s32 ixgbe_enable_rx_dma_82599(struct ixgbe_hw *hw, u32 regval) * Return: -EACCES if the FW is not present or if the FW version is * not supported. **/ -static s32 ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw) +static int ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw) { u16 fw_offset, fw_ptp_cfg_offset; - s32 status = -EACCES; - u16 offset; + int status = -EACCES; u16 fw_version = 0; + u16 offset; /* firmware check is only necessary for SFI devices */ if (hw->phy.media_type != ixgbe_media_type_fiber) @@ -1917,7 +1918,7 @@ fw_version_err: static bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw) { u16 fw_offset, fw_lesm_param_offset, fw_lesm_state; - s32 status; + int status; /* get the offset to the Firmware Module block */ status = hw->eeprom.ops.read(hw, IXGBE_FW_PTR, &fw_offset); @@ -1956,7 +1957,7 @@ static bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw) * * Retrieves 16 bit word(s) read from EEPROM **/ -static s32 ixgbe_read_eeprom_buffer_82599(struct ixgbe_hw *hw, u16 offset, +static int ixgbe_read_eeprom_buffer_82599(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { struct ixgbe_eeprom_info *eeprom = &hw->eeprom; @@ -1982,7 +1983,7 @@ static s32 ixgbe_read_eeprom_buffer_82599(struct ixgbe_hw *hw, u16 offset, * * Reads a 16 bit word from the EEPROM **/ -static s32 ixgbe_read_eeprom_82599(struct ixgbe_hw *hw, +static int ixgbe_read_eeprom_82599(struct ixgbe_hw *hw, u16 offset, u16 *data) { struct ixgbe_eeprom_info *eeprom = &hw->eeprom; @@ -2006,11 +2007,11 @@ static s32 ixgbe_read_eeprom_82599(struct ixgbe_hw *hw, * full pipeline reset. Note - We must hold the SW/FW semaphore before writing * to AUTOC, so this function assumes the semaphore is held. **/ -static s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw) +static int ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw) { - s32 ret_val; - u32 anlp1_reg = 0; u32 i, autoc_reg, autoc2_reg; + u32 anlp1_reg = 0; + int ret_val; /* Enable link if disabled in NVM */ autoc2_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC2); @@ -2061,12 +2062,12 @@ reset_pipeline_out: * Performs byte read operation to SFP module's EEPROM over I2C interface at * a specified device address. **/ -static s32 ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data) { - u32 esdp; - s32 status; s32 timeout = 200; + int status; + u32 esdp; if (hw->phy.qsfp_shared_i2c_bus == true) { /* Acquire I2C bus ownership. */ @@ -2115,12 +2116,12 @@ release_i2c_access: * Performs byte write operation to SFP module's EEPROM over I2C interface at * a specified device address. **/ -static s32 ixgbe_write_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_write_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data) { - u32 esdp; - s32 status; s32 timeout = 200; + int status; + u32 esdp; if (hw->phy.qsfp_shared_i2c_bus == true) { /* Acquire I2C bus ownership. */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 2e6e0365154a..3be1bfb16498 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -10,10 +10,10 @@ #include "ixgbe_common.h" #include "ixgbe_phy.h" -static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw); -static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw); +static int ixgbe_acquire_eeprom(struct ixgbe_hw *hw); +static int ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw); static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw); -static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw); +static int ixgbe_ready_eeprom(struct ixgbe_hw *hw); static void ixgbe_standby_eeprom(struct ixgbe_hw *hw); static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data, u16 count); @@ -22,15 +22,15 @@ static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec); static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec); static void ixgbe_release_eeprom(struct ixgbe_hw *hw); -static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr); -static s32 ixgbe_poll_eerd_eewr_done(struct ixgbe_hw *hw, u32 ee_reg); -static s32 ixgbe_read_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, +static int ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr); +static int ixgbe_poll_eerd_eewr_done(struct ixgbe_hw *hw, u32 ee_reg); +static int ixgbe_read_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); -static s32 ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, - u16 words, u16 *data); -static s32 ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw, +static int ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, + u16 words, u16 *data); +static int ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw, u16 offset); -static s32 ixgbe_disable_pcie_primary(struct ixgbe_hw *hw); +static int ixgbe_disable_pcie_primary(struct ixgbe_hw *hw); /* Base table for registers values that change by MAC */ const u32 ixgbe_mvals_8259X[IXGBE_MVALS_IDX_LIMIT] = { @@ -111,12 +111,12 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) * * Called at init time to set up flow control. **/ -s32 ixgbe_setup_fc_generic(struct ixgbe_hw *hw) +int ixgbe_setup_fc_generic(struct ixgbe_hw *hw) { - s32 ret_val = 0; u32 reg = 0, reg_bp = 0; - u16 reg_cu = 0; bool locked = false; + int ret_val = 0; + u16 reg_cu = 0; /* * Validate the requested mode. Strict IEEE mode does not allow @@ -267,11 +267,11 @@ s32 ixgbe_setup_fc_generic(struct ixgbe_hw *hw) * table, VLAN filter table, calls routine to set up link and flow control * settings, and leaves transmit and receive units disabled and uninitialized **/ -s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw) +int ixgbe_start_hw_generic(struct ixgbe_hw *hw) { - s32 ret_val; - u32 ctrl_ext; u16 device_caps; + u32 ctrl_ext; + int ret_val; /* Set the media type */ hw->phy.media_type = hw->mac.ops.get_media_type(hw); @@ -330,7 +330,7 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw) * 82599 * X540 **/ -s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw) +int ixgbe_start_hw_gen2(struct ixgbe_hw *hw) { u32 i; @@ -354,9 +354,9 @@ s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw) * up link and flow control settings, and leaves transmit and receive units * disabled and uninitialized **/ -s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw) +int ixgbe_init_hw_generic(struct ixgbe_hw *hw) { - s32 status; + int status; /* Reset the hardware */ status = hw->mac.ops.reset_hw(hw); @@ -380,7 +380,7 @@ s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw) * Clears all hardware statistics counters by reading them from the hardware * Statistics counters are clear on read. **/ -s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw) +int ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw) { u16 i = 0; @@ -489,14 +489,14 @@ s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw) * * Reads the part number string from the EEPROM. **/ -s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num, +int ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num, u32 pba_num_size) { - s32 ret_val; - u16 data; + int ret_val; u16 pba_ptr; u16 offset; u16 length; + u16 data; if (pba_num == NULL) { hw_dbg(hw, "PBA string buffer was null\n"); @@ -599,7 +599,7 @@ s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num, * A reset of the adapter must be performed prior to calling this function * in order for the MAC address to have been loaded from the EEPROM into RAR0 **/ -s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr) +int ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr) { u32 rar_high; u32 rar_low; @@ -653,7 +653,7 @@ enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status) * * Sets the PCI bus info (speed, width, type) within the ixgbe_hw structure **/ -s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw) +int ixgbe_get_bus_info_generic(struct ixgbe_hw *hw) { u16 link_status; @@ -709,7 +709,7 @@ void ixgbe_set_lan_id_multi_port_pcie(struct ixgbe_hw *hw) * the shared code and drivers to determine if the adapter is in a stopped * state and should not touch the hardware. **/ -s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw) +int ixgbe_stop_adapter_generic(struct ixgbe_hw *hw) { u32 reg_val; u16 i; @@ -759,7 +759,7 @@ s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw) * Store the index for the link active LED. This will be used to support * blinking the LED. **/ -s32 ixgbe_init_led_link_act_generic(struct ixgbe_hw *hw) +int ixgbe_init_led_link_act_generic(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; u32 led_reg, led_mode; @@ -800,7 +800,7 @@ s32 ixgbe_init_led_link_act_generic(struct ixgbe_hw *hw) * @hw: pointer to hardware structure * @index: led number to turn on **/ -s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index) +int ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index) { u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL); @@ -821,7 +821,7 @@ s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index) * @hw: pointer to hardware structure * @index: led number to turn off **/ -s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index) +int ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index) { u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL); @@ -844,7 +844,7 @@ s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index) * Initializes the EEPROM parameters ixgbe_eeprom_info within the * ixgbe_hw struct in order to set up EEPROM access. **/ -s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw) +int ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw) { struct ixgbe_eeprom_info *eeprom = &hw->eeprom; u32 eec; @@ -895,11 +895,11 @@ s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw) * * Reads 16 bit word(s) from EEPROM through bit-bang method **/ -s32 ixgbe_write_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_write_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { - s32 status; u16 i, count; + int status; hw->eeprom.ops.init_params(hw); @@ -942,14 +942,14 @@ s32 ixgbe_write_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, * If ixgbe_eeprom_update_checksum is not called after this function, the * EEPROM will most likely contain an invalid checksum. **/ -static s32 ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, +static int ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { - s32 status; - u16 word; + u8 write_opcode = IXGBE_EEPROM_WRITE_OPCODE_SPI; u16 page_size; + int status; + u16 word; u16 i; - u8 write_opcode = IXGBE_EEPROM_WRITE_OPCODE_SPI; /* Prepare the EEPROM for writing */ status = ixgbe_acquire_eeprom(hw); @@ -1019,7 +1019,7 @@ static s32 ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, * If ixgbe_eeprom_update_checksum is not called after this function, the * EEPROM will most likely contain an invalid checksum. **/ -s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset, u16 data) +int ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset, u16 data) { hw->eeprom.ops.init_params(hw); @@ -1038,11 +1038,11 @@ s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset, u16 data) * * Reads 16 bit word(s) from EEPROM through bit-bang method **/ -s32 ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { - s32 status; u16 i, count; + int status; hw->eeprom.ops.init_params(hw); @@ -1077,12 +1077,12 @@ s32 ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, * * Reads 16 bit word(s) from EEPROM through bit-bang method **/ -static s32 ixgbe_read_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, +static int ixgbe_read_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { - s32 status; - u16 word_in; u8 read_opcode = IXGBE_EEPROM_READ_OPCODE_SPI; + u16 word_in; + int status; u16 i; /* Prepare the EEPROM for reading */ @@ -1129,7 +1129,7 @@ static s32 ixgbe_read_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, * * Reads 16 bit value from EEPROM through bit-bang method **/ -s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, u16 *data) { hw->eeprom.ops.init_params(hw); @@ -1149,11 +1149,11 @@ s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, * * Reads a 16 bit word(s) from the EEPROM using the EERD register. **/ -s32 ixgbe_read_eerd_buffer_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_read_eerd_buffer_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { + int status; u32 eerd; - s32 status; u32 i; hw->eeprom.ops.init_params(hw); @@ -1189,11 +1189,11 @@ s32 ixgbe_read_eerd_buffer_generic(struct ixgbe_hw *hw, u16 offset, * This function is called only when we are writing a new large buffer * at given offset so the data would be overwritten anyway. **/ -static s32 ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw, +static int ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw, u16 offset) { u16 data[IXGBE_EEPROM_PAGE_SIZE_MAX]; - s32 status; + int status; u16 i; for (i = 0; i < IXGBE_EEPROM_PAGE_SIZE_MAX; i++) @@ -1229,7 +1229,7 @@ static s32 ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw, * * Reads a 16 bit word from the EEPROM using the EERD register. **/ -s32 ixgbe_read_eerd_generic(struct ixgbe_hw *hw, u16 offset, u16 *data) +int ixgbe_read_eerd_generic(struct ixgbe_hw *hw, u16 offset, u16 *data) { return ixgbe_read_eerd_buffer_generic(hw, offset, 1, data); } @@ -1243,11 +1243,11 @@ s32 ixgbe_read_eerd_generic(struct ixgbe_hw *hw, u16 offset, u16 *data) * * Write a 16 bit word(s) to the EEPROM using the EEWR register. **/ -s32 ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { + int status; u32 eewr; - s32 status; u16 i; hw->eeprom.ops.init_params(hw); @@ -1286,7 +1286,7 @@ s32 ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset, * * Write a 16 bit word to the EEPROM using the EEWR register. **/ -s32 ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data) +int ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data) { return ixgbe_write_eewr_buffer_generic(hw, offset, 1, &data); } @@ -1299,7 +1299,7 @@ s32 ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data) * Polls the status bit (bit 1) of the EERD or EEWR to determine when the * read or write is done respectively. **/ -static s32 ixgbe_poll_eerd_eewr_done(struct ixgbe_hw *hw, u32 ee_reg) +static int ixgbe_poll_eerd_eewr_done(struct ixgbe_hw *hw, u32 ee_reg) { u32 i; u32 reg; @@ -1325,7 +1325,7 @@ static s32 ixgbe_poll_eerd_eewr_done(struct ixgbe_hw *hw, u32 ee_reg) * Prepares EEPROM for access using bit-bang method. This function should * be called before issuing a command to the EEPROM. **/ -static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw) +static int ixgbe_acquire_eeprom(struct ixgbe_hw *hw) { u32 eec; u32 i; @@ -1371,7 +1371,7 @@ static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw) * * Sets the hardware semaphores so EEPROM access can occur for bit-bang method **/ -static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw) +static int ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw) { u32 timeout = 2000; u32 i; @@ -1462,7 +1462,7 @@ static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw) * ixgbe_ready_eeprom - Polls for EEPROM ready * @hw: pointer to hardware structure **/ -static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw) +static int ixgbe_ready_eeprom(struct ixgbe_hw *hw) { u16 i; u8 spi_stat_reg; @@ -1680,7 +1680,7 @@ static void ixgbe_release_eeprom(struct ixgbe_hw *hw) * ixgbe_calc_eeprom_checksum_generic - Calculates and returns the checksum * @hw: pointer to hardware structure **/ -s32 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw) +int ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw) { u16 i; u16 j; @@ -1728,7 +1728,7 @@ s32 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw) checksum = (u16)IXGBE_EEPROM_SUM - checksum; - return (s32)checksum; + return (int)checksum; } /** @@ -1739,12 +1739,12 @@ s32 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw) * Performs checksum calculation and validates the EEPROM checksum. If the * caller does not need checksum_val, the value can be NULL. **/ -s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw, +int ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw, u16 *checksum_val) { - s32 status; - u16 checksum; u16 read_checksum = 0; + u16 checksum; + int status; /* * Read the first word from the EEPROM. If this times out or fails, do @@ -1786,10 +1786,10 @@ s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw, * ixgbe_update_eeprom_checksum_generic - Updates the EEPROM checksum * @hw: pointer to hardware structure **/ -s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw) +int ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw) { - s32 status; u16 checksum; + int status; /* * Read the first word from the EEPROM. If this times out or fails, do @@ -1823,7 +1823,7 @@ s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw) * * Puts an ethernet address into a receive address register. **/ -s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, +int ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, u32 enable_addr) { u32 rar_low, rar_high; @@ -1876,7 +1876,7 @@ s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, * * Clears an ethernet address from a receive address register. **/ -s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index) +int ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index) { u32 rar_high; u32 rar_entries = hw->mac.num_rar_entries; @@ -1917,7 +1917,7 @@ s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index) * of the receive address registers. Clears the multicast table. Assumes * the receiver is in reset when the routine is called. **/ -s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw) +int ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw) { u32 i; u32 rar_entries = hw->mac.num_rar_entries; @@ -1980,7 +1980,7 @@ s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw) * by the MO field of the MCSTCTRL. The MO field is set during initialization * to mc_filter_type. **/ -static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr) +static int ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr) { u32 vector = 0; @@ -2049,7 +2049,7 @@ static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr) * registers for the first multicast addresses, and hashes the rest into the * multicast table. **/ -s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, +int ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, struct net_device *netdev) { struct netdev_hw_addr *ha; @@ -2091,7 +2091,7 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, * * Enables multicast address in RAR and the use of the multicast hash table. **/ -s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw) +int ixgbe_enable_mc_generic(struct ixgbe_hw *hw) { struct ixgbe_addr_filter_info *a = &hw->addr_ctrl; @@ -2108,7 +2108,7 @@ s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw) * * Disables multicast address in RAR and the use of the multicast hash table. **/ -s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw) +int ixgbe_disable_mc_generic(struct ixgbe_hw *hw) { struct ixgbe_addr_filter_info *a = &hw->addr_ctrl; @@ -2124,7 +2124,7 @@ s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw) * * Enable flow control according to the current settings. **/ -s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw) +int ixgbe_fc_enable_generic(struct ixgbe_hw *hw) { u32 mflcn_reg, fccfg_reg; u32 reg; @@ -2252,7 +2252,7 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw) * Find the intersection between advertised settings and link partner's * advertised settings **/ -s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, +int ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm) { if ((!(adv_reg)) || (!(lp_reg))) @@ -2294,10 +2294,10 @@ s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, * * Enable flow control according on 1 gig fiber. **/ -static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw) +static int ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw) { u32 pcs_anadv_reg, pcs_lpab_reg, linkstat; - s32 ret_val; + int ret_val; /* * On multispeed fiber at 1g, bail out if @@ -2328,10 +2328,10 @@ static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw) * * Enable flow control according to IEEE clause 37. **/ -static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw) +static int ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw) { u32 links2, anlp1_reg, autoc_reg, links; - s32 ret_val; + int ret_val; /* * On backplane, bail out if @@ -2367,7 +2367,7 @@ static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw) * * Enable flow control according to IEEE clause 37. **/ -static s32 ixgbe_fc_autoneg_copper(struct ixgbe_hw *hw) +static int ixgbe_fc_autoneg_copper(struct ixgbe_hw *hw) { u16 technology_ability_reg = 0; u16 lp_technology_ability_reg = 0; @@ -2395,7 +2395,7 @@ static s32 ixgbe_fc_autoneg_copper(struct ixgbe_hw *hw) void ixgbe_fc_autoneg(struct ixgbe_hw *hw) { ixgbe_link_speed speed; - s32 ret_val = -EIO; + int ret_val = -EIO; bool link_up; /* @@ -2501,7 +2501,7 @@ static u32 ixgbe_pcie_timeout_poll(struct ixgbe_hw *hw) * bit hasn't caused the primary requests to be disabled, else 0 * is returned signifying primary requests disabled. **/ -static s32 ixgbe_disable_pcie_primary(struct ixgbe_hw *hw) +static int ixgbe_disable_pcie_primary(struct ixgbe_hw *hw) { u32 i, poll; u16 value; @@ -2573,7 +2573,7 @@ gio_disable_fail: * Acquires the SWFW semaphore through the GSSR register for the specified * function (CSR, PHY0, PHY1, EEPROM, Flash) **/ -s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u32 mask) +int ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u32 mask) { u32 gssr = 0; u32 swmask = mask; @@ -2641,7 +2641,7 @@ void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u32 mask) * * The default case requires no protection so just to the register read. **/ -s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *locked, u32 *reg_val) +int prot_autoc_read_generic(struct ixgbe_hw *hw, bool *locked, u32 *reg_val) { *locked = false; *reg_val = IXGBE_READ_REG(hw, IXGBE_AUTOC); @@ -2655,7 +2655,7 @@ s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *locked, u32 *reg_val) * @locked: bool to indicate whether the SW/FW lock was already taken by * previous read. **/ -s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked) +int prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked) { IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_val); return 0; @@ -2668,7 +2668,7 @@ s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked) * Stops the receive data path and waits for the HW to internally * empty the Rx security block. **/ -s32 ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw) +int ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw) { #define IXGBE_MAX_SECRX_POLL 40 int i; @@ -2700,7 +2700,7 @@ s32 ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw) * * Enables the receive data path **/ -s32 ixgbe_enable_rx_buff_generic(struct ixgbe_hw *hw) +int ixgbe_enable_rx_buff_generic(struct ixgbe_hw *hw) { u32 secrxreg; @@ -2719,7 +2719,7 @@ s32 ixgbe_enable_rx_buff_generic(struct ixgbe_hw *hw) * * Enables the Rx DMA unit **/ -s32 ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval) +int ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval) { if (regval & IXGBE_RXCTRL_RXEN) hw->mac.ops.enable_rx(hw); @@ -2734,14 +2734,14 @@ s32 ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval) * @hw: pointer to hardware structure * @index: led number to blink **/ -s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index) +int ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index) { - ixgbe_link_speed speed = 0; - bool link_up = false; u32 autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC); u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL); + ixgbe_link_speed speed = 0; + bool link_up = false; bool locked = false; - s32 ret_val; + int ret_val; if (index > 3) return -EINVAL; @@ -2782,12 +2782,12 @@ s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index) * @hw: pointer to hardware structure * @index: led number to stop blinking **/ -s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index) +int ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index) { - u32 autoc_reg = 0; u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL); bool locked = false; - s32 ret_val; + u32 autoc_reg = 0; + int ret_val; if (index > 3) return -EINVAL; @@ -2821,10 +2821,10 @@ s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index) * pointer, and returns the value at that location. This is used in both * get and set mac_addr routines. **/ -static s32 ixgbe_get_san_mac_addr_offset(struct ixgbe_hw *hw, - u16 *san_mac_offset) +static int ixgbe_get_san_mac_addr_offset(struct ixgbe_hw *hw, + u16 *san_mac_offset) { - s32 ret_val; + int ret_val; /* * First read the EEPROM pointer to see if the MAC addresses are @@ -2849,11 +2849,11 @@ static s32 ixgbe_get_san_mac_addr_offset(struct ixgbe_hw *hw, * set_lan_id() is called by identify_sfp(), but this cannot be relied * upon for non-SFP connections, so we must call it here. **/ -s32 ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr) +int ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr) { u16 san_mac_data, san_mac_offset; + int ret_val; u8 i; - s32 ret_val; /* * First read the EEPROM pointer to see if the MAC addresses are @@ -2942,7 +2942,7 @@ u16 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw) * @rar: receive address register index to disassociate * @vmdq: VMDq pool index to remove from the rar **/ -s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) +int ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) { u32 mpsar_lo, mpsar_hi; u32 rar_entries = hw->mac.num_rar_entries; @@ -2993,7 +2993,7 @@ s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) * @rar: receive address register index to associate with a VMDq index * @vmdq: VMDq pool index **/ -s32 ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) +int ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) { u32 mpsar; u32 rar_entries = hw->mac.num_rar_entries; @@ -3026,7 +3026,7 @@ s32 ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) * VFs advertized and not 0. * MPSAR table needs to be updated for SAN_MAC RAR [hw->mac.san_mac_rar_index] **/ -s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq) +int ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq) { u32 rar = hw->mac.san_mac_rar_index; @@ -3045,7 +3045,7 @@ s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq) * ixgbe_init_uta_tables_generic - Initialize the Unicast Table Array * @hw: pointer to hardware structure **/ -s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw) +int ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw) { int i; @@ -3065,9 +3065,9 @@ s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw) * return the VLVF index where this VLAN id should be placed * **/ -static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass) +static int ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass) { - s32 regindex, first_empty_slot; + int regindex, first_empty_slot; u32 bits; /* short cut the special case */ @@ -3115,11 +3115,11 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass) * * Turn on/off specified VLAN in the VLAN filter table. **/ -s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, +int ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on, bool vlvf_bypass) { u32 regidx, vfta_delta, vfta, bits; - s32 vlvf_index; + int vlvf_index; if ((vlan > 4095) || (vind > 63)) return -EINVAL; @@ -3226,7 +3226,7 @@ vfta_update: * * Clears the VLAN filter table, and the VMDq index associated with the filter **/ -s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw) +int ixgbe_clear_vfta_generic(struct ixgbe_hw *hw) { u32 offset; @@ -3276,7 +3276,7 @@ static bool ixgbe_need_crosstalk_fix(struct ixgbe_hw *hw) * * Reads the links register to determine if link is up and the current speed **/ -s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, +int ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *link_up, bool link_up_wait_to_complete) { bool crosstalk_fix_active = ixgbe_need_crosstalk_fix(hw); @@ -3396,8 +3396,8 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, * This function will read the EEPROM from the alternative SAN MAC address * block to check the support for the alternative WWNN/WWPN prefix support. **/ -s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix, - u16 *wwpn_prefix) +int ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix, + u16 *wwpn_prefix) { u16 offset, caps; u16 alt_san_mac_blk_offset; @@ -3494,7 +3494,7 @@ void ixgbe_set_vlan_anti_spoofing(struct ixgbe_hw *hw, bool enable, int vf) * This function will read the EEPROM location for the device capabilities, * and return the word through device_caps. **/ -s32 ixgbe_get_device_caps_generic(struct ixgbe_hw *hw, u16 *device_caps) +int ixgbe_get_device_caps_generic(struct ixgbe_hw *hw, u16 *device_caps) { hw->eeprom.ops.read(hw, IXGBE_DEVICE_CAPS, device_caps); @@ -3604,7 +3604,7 @@ u8 ixgbe_calculate_checksum(u8 *buffer, u32 length) * This function assumes that the IXGBE_GSSR_SW_MNG_SM semaphore is held * by the caller. **/ -s32 ixgbe_hic_unlocked(struct ixgbe_hw *hw, u32 *buffer, u32 length, +int ixgbe_hic_unlocked(struct ixgbe_hw *hw, u32 *buffer, u32 length, u32 timeout) { u32 hicr, i, fwsts; @@ -3676,15 +3676,15 @@ s32 ixgbe_hic_unlocked(struct ixgbe_hw *hw, u32 *buffer, u32 length, * Communicates with the manageability block. On success return 0 * else return -EIO or -EINVAL. **/ -s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, void *buffer, +int ixgbe_host_interface_command(struct ixgbe_hw *hw, void *buffer, u32 length, u32 timeout, bool return_data) { u32 hdr_size = sizeof(struct ixgbe_hic_hdr); struct ixgbe_hic_hdr *hdr = buffer; - u32 *u32arr = buffer; u16 buf_len, dword_len; - s32 status; + u32 *u32arr = buffer; + int status; u32 bi; if (!length || length > IXGBE_HI_MAX_BLOCK_BYTE_LENGTH) { @@ -3753,13 +3753,13 @@ rel_out: * else returns -EBUSY when encountering an error acquiring * semaphore or -EIO when command fails. **/ -s32 ixgbe_set_fw_drv_ver_generic(struct ixgbe_hw *hw, u8 maj, u8 min, +int ixgbe_set_fw_drv_ver_generic(struct ixgbe_hw *hw, u8 maj, u8 min, u8 build, u8 sub, __always_unused u16 len, __always_unused const char *driver_ver) { struct ixgbe_hic_drv_info fw_cmd; + int ret_val; int i; - s32 ret_val; fw_cmd.hdr.cmd = FW_CEM_CMD_DRIVER_INFO; fw_cmd.hdr.buf_len = FW_CEM_CMD_DRIVER_INFO_LEN; @@ -3875,10 +3875,10 @@ static const u8 ixgbe_emc_therm_limit[4] = { * * Returns error code. **/ -static s32 ixgbe_get_ets_data(struct ixgbe_hw *hw, u16 *ets_cfg, +static int ixgbe_get_ets_data(struct ixgbe_hw *hw, u16 *ets_cfg, u16 *ets_offset) { - s32 status; + int status; status = hw->eeprom.ops.read(hw, IXGBE_ETS_CFG, ets_offset); if (status) @@ -3903,13 +3903,13 @@ static s32 ixgbe_get_ets_data(struct ixgbe_hw *hw, u16 *ets_cfg, * * Returns the thermal sensor data structure **/ -s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw) +int ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw) { - s32 status; u16 ets_offset; - u16 ets_cfg; u16 ets_sensor; u8 num_sensors; + u16 ets_cfg; + int status; u8 i; struct ixgbe_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; @@ -3959,17 +3959,17 @@ s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw) * Inits the thermal sensor thresholds according to the NVM map * and save off the threshold and location values into mac.thermal_sensor_data **/ -s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw) +int ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw) { - s32 status; - u16 ets_offset; - u16 ets_cfg; - u16 ets_sensor; + struct ixgbe_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; u8 low_thresh_delta; u8 num_sensors; u8 therm_limit; + u16 ets_sensor; + u16 ets_offset; + u16 ets_cfg; + int status; u8 i; - struct ixgbe_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; memset(data, 0, sizeof(struct ixgbe_thermal_sensor_data)); @@ -4192,16 +4192,16 @@ bool ixgbe_mng_present(struct ixgbe_hw *hw) * * Set the link speed in the MAC and/or PHY register and restarts link. */ -s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, +int ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete) { - ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; ixgbe_link_speed highest_link_speed = IXGBE_LINK_SPEED_UNKNOWN; - s32 status = 0; + ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; + bool autoneg, link_up = false; u32 speedcnt = 0; + int status = 0; u32 i = 0; - bool autoneg, link_up = false; /* Mask off requested but non-supported speeds */ status = hw->mac.ops.get_link_capabilities(hw, &link_speed, &autoneg); @@ -4340,8 +4340,8 @@ out: void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw, ixgbe_link_speed speed) { - s32 status; u8 rs, eeprom_data; + int status; switch (speed) { case IXGBE_LINK_SPEED_10GB_FULL: diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index 34761e691d52..6493abf189de 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -8,89 +8,89 @@ #include "ixgbe.h" u16 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw); -s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw); -s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw); -s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw); -s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw); -s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num, +int ixgbe_init_hw_generic(struct ixgbe_hw *hw); +int ixgbe_start_hw_generic(struct ixgbe_hw *hw); +int ixgbe_start_hw_gen2(struct ixgbe_hw *hw); +int ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw); +int ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num, u32 pba_num_size); -s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr); +int ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr); enum ixgbe_bus_width ixgbe_convert_bus_width(u16 link_status); enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status); -s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw); +int ixgbe_get_bus_info_generic(struct ixgbe_hw *hw); void ixgbe_set_lan_id_multi_port_pcie(struct ixgbe_hw *hw); -s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw); +int ixgbe_stop_adapter_generic(struct ixgbe_hw *hw); -s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index); -s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index); -s32 ixgbe_init_led_link_act_generic(struct ixgbe_hw *hw); +int ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index); +int ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index); +int ixgbe_init_led_link_act_generic(struct ixgbe_hw *hw); -s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw); -s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset, u16 data); -s32 ixgbe_write_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw); +int ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset, u16 data); +int ixgbe_write_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); -s32 ixgbe_read_eerd_generic(struct ixgbe_hw *hw, u16 offset, u16 *data); -s32 ixgbe_read_eerd_buffer_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_read_eerd_generic(struct ixgbe_hw *hw, u16 offset, u16 *data); +int ixgbe_read_eerd_buffer_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); -s32 ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data); -s32 ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data); +int ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); -s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, u16 *data); -s32 ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, +int ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); -s32 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw); -s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw, +int ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw); +int ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw, u16 *checksum_val); -s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw); +int ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw); -s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, +int ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, u32 enable_addr); -s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index); -s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw); -s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, +int ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index); +int ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw); +int ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, struct net_device *netdev); -s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw); -s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw); -s32 ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw); -s32 ixgbe_enable_rx_buff_generic(struct ixgbe_hw *hw); -s32 ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval); -s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw); -s32 ixgbe_setup_fc_generic(struct ixgbe_hw *); +int ixgbe_enable_mc_generic(struct ixgbe_hw *hw); +int ixgbe_disable_mc_generic(struct ixgbe_hw *hw); +int ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw); +int ixgbe_enable_rx_buff_generic(struct ixgbe_hw *hw); +int ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval); +int ixgbe_fc_enable_generic(struct ixgbe_hw *hw); +int ixgbe_setup_fc_generic(struct ixgbe_hw *); bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw); void ixgbe_fc_autoneg(struct ixgbe_hw *hw); -s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u32 mask); +int ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u32 mask); void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u32 mask); -s32 ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr); -s32 ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); -s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq); -s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); -s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw); -s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, +int ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr); +int ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); +int ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq); +int ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); +int ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw); +int ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on, bool vlvf_bypass); -s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw); -s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, +int ixgbe_clear_vfta_generic(struct ixgbe_hw *hw); +int ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *link_up, bool link_up_wait_to_complete); -s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix, +int ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix, u16 *wwpn_prefix); -s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *, u32 *reg_val); -s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked); +int prot_autoc_read_generic(struct ixgbe_hw *hw, bool *, u32 *reg_val); +int prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked); -s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index); -s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index); +int ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index); +int ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index); void ixgbe_set_mac_anti_spoofing(struct ixgbe_hw *hw, bool enable, int vf); void ixgbe_set_vlan_anti_spoofing(struct ixgbe_hw *hw, bool enable, int vf); -s32 ixgbe_get_device_caps_generic(struct ixgbe_hw *hw, u16 *device_caps); -s32 ixgbe_set_fw_drv_ver_generic(struct ixgbe_hw *hw, u8 maj, u8 min, +int ixgbe_get_device_caps_generic(struct ixgbe_hw *hw, u16 *device_caps); +int ixgbe_set_fw_drv_ver_generic(struct ixgbe_hw *hw, u8 maj, u8 min, u8 build, u8 ver, u16 len, const char *str); u8 ixgbe_calculate_checksum(u8 *buffer, u32 length); -s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, void *, u32 length, +int ixgbe_host_interface_command(struct ixgbe_hw *hw, void *, u32 length, u32 timeout, bool return_data); -s32 ixgbe_hic_unlocked(struct ixgbe_hw *hw, u32 *buffer, u32 len, u32 timeout); -s32 ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity, +int ixgbe_hic_unlocked(struct ixgbe_hw *hw, u32 *buffer, u32 len, u32 timeout); +int ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity, u32 (*data)[FW_PHY_ACT_DATA_COUNT]); void ixgbe_clear_tx_pending(struct ixgbe_hw *hw); bool ixgbe_mng_present(struct ixgbe_hw *hw); @@ -111,8 +111,8 @@ extern const u32 ixgbe_mvals_8259X[IXGBE_MVALS_IDX_LIMIT]; #define IXGBE_EMC_DIODE3_DATA 0x2A #define IXGBE_EMC_DIODE3_THERM_LIMIT 0x30 -s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw); -s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw); +int ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw); +int ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw); void ixgbe_get_etk_id(struct ixgbe_hw *hw, struct ixgbe_nvm_version *nvm_ver); void ixgbe_get_oem_prod_version(struct ixgbe_hw *hw, @@ -121,7 +121,7 @@ void ixgbe_get_orom_version(struct ixgbe_hw *hw, struct ixgbe_nvm_version *nvm_ver); void ixgbe_disable_rx_generic(struct ixgbe_hw *hw); void ixgbe_enable_rx_generic(struct ixgbe_hw *hw); -s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, +int ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c index d26cea5b43bd..502666f28124 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c @@ -18,7 +18,7 @@ * @max: max credits by traffic class * @max_frame: maximum frame size */ -static s32 ixgbe_ieee_credits(__u8 *bw, __u16 *refill, +static int ixgbe_ieee_credits(__u8 *bw, __u16 *refill, __u16 *max, int max_frame) { int min_percent = 100; @@ -59,7 +59,7 @@ static s32 ixgbe_ieee_credits(__u8 *bw, __u16 *refill, * It should be called only after the rules are checked by * ixgbe_dcb_check_config(). */ -s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw, +int ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw, struct ixgbe_dcb_config *dcb_config, int max_frame, u8 direction) { @@ -247,7 +247,7 @@ void ixgbe_dcb_unpack_map(struct ixgbe_dcb_config *cfg, int direction, u8 *map) * * Configure dcb settings and enable dcb mode. */ -s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw, +int ixgbe_dcb_hw_config(struct ixgbe_hw *hw, struct ixgbe_dcb_config *dcb_config) { u8 pfc_en; @@ -283,7 +283,7 @@ s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw, } /* Helper routines to abstract HW specifics from DCB netlink ops */ -s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) +int ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) { switch (hw->mac.type) { case ixgbe_mac_82598EB: @@ -300,7 +300,7 @@ s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) return -EINVAL; } -s32 ixgbe_dcb_hw_ets(struct ixgbe_hw *hw, struct ieee_ets *ets, int max_frame) +int ixgbe_dcb_hw_ets(struct ixgbe_hw *hw, struct ieee_ets *ets, int max_frame) { __u16 refill[IEEE_8021QAZ_MAX_TCS], max[IEEE_8021QAZ_MAX_TCS]; __u8 prio_type[IEEE_8021QAZ_MAX_TCS]; @@ -333,7 +333,7 @@ s32 ixgbe_dcb_hw_ets(struct ixgbe_hw *hw, struct ieee_ets *ets, int max_frame) bwg_id, prio_type, ets->prio_tc); } -s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, +int ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, u16 *refill, u16 *max, u8 *bwg_id, u8 *prio_type, u8 *prio_tc) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h index 60cd5863bf5e..91788e4c4e19 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h @@ -124,15 +124,15 @@ void ixgbe_dcb_unpack_map(struct ixgbe_dcb_config *, int, u8 *); u8 ixgbe_dcb_get_tc_from_up(struct ixgbe_dcb_config *, int, u8); /* DCB credits calculation */ -s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *, +int ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *, struct ixgbe_dcb_config *, int, u8); /* DCB hw initialization */ -s32 ixgbe_dcb_hw_ets(struct ixgbe_hw *hw, struct ieee_ets *ets, int max); -s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, u16 *refill, u16 *max, +int ixgbe_dcb_hw_ets(struct ixgbe_hw *hw, struct ieee_ets *ets, int max); +int ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, u16 *refill, u16 *max, u8 *bwg_id, u8 *prio_type, u8 *tc_prio); -s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *tc_prio); -s32 ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *); +int ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *tc_prio); +int ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *); void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c index 379ae747cdce..185c3e5f9837 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c @@ -15,10 +15,8 @@ * * Configure Rx Data Arbiter and credits for each traffic class. */ -s32 ixgbe_dcb_config_rx_arbiter_82598(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *prio_type) +int ixgbe_dcb_config_rx_arbiter_82598(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *prio_type) { u32 reg = 0; u32 credit_refill = 0; @@ -75,11 +73,8 @@ s32 ixgbe_dcb_config_rx_arbiter_82598(struct ixgbe_hw *hw, * * Configure Tx Descriptor Arbiter and credits for each traffic class. */ -s32 ixgbe_dcb_config_tx_desc_arbiter_82598(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *bwg_id, - u8 *prio_type) +int ixgbe_dcb_config_tx_desc_arbiter_82598(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *bwg_id, u8 *prio_type) { u32 reg, max_credits; u8 i; @@ -124,11 +119,8 @@ s32 ixgbe_dcb_config_tx_desc_arbiter_82598(struct ixgbe_hw *hw, * * Configure Tx Data Arbiter and credits for each traffic class. */ -s32 ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *bwg_id, - u8 *prio_type) +int ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *bwg_id, u8 *prio_type) { u32 reg; u8 i; @@ -171,7 +163,7 @@ s32 ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw, * * Configure Priority Flow Control for each traffic class. */ -s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en) +int ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en) { u32 fcrtl, reg; u8 i; @@ -224,7 +216,7 @@ s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en) * Configure queue statistics registers, all queues belonging to same traffic * class uses a single set of queue statistics counters. */ -static s32 ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *hw) +static int ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *hw) { u32 reg = 0; u8 i = 0; @@ -260,7 +252,7 @@ static s32 ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *hw) * * Configure dcb settings and enable dcb mode. */ -s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, +int ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, u16 *max, u8 *bwg_id, u8 *prio_type) { ixgbe_dcb_config_rx_arbiter_82598(hw, refill, max, prio_type); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h index fdca41abb44c..5bf3f13c6953 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h @@ -46,27 +46,19 @@ /* DCB hardware-specific driver APIs */ /* DCB PFC functions */ -s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *, u8 pfc_en); +int ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *, u8 pfc_en); /* DCB hw initialization */ -s32 ixgbe_dcb_config_rx_arbiter_82598(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *prio_type); - -s32 ixgbe_dcb_config_tx_desc_arbiter_82598(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *bwg_id, - u8 *prio_type); - -s32 ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *bwg_id, - u8 *prio_type); - -s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, +int ixgbe_dcb_config_rx_arbiter_82598(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *prio_type); + +int ixgbe_dcb_config_tx_desc_arbiter_82598(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *bwg_id, u8 *prio_type); + +int ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *bwg_id, u8 *prio_type); + +int ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, u16 *max, u8 *bwg_id, u8 *prio_type); #endif /* _DCB_82598_CONFIG_H */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c index 7948849840a5..c61bd9059541 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c @@ -17,7 +17,7 @@ * * Configure Rx Packet Arbiter and credits for each traffic class. */ -s32 ixgbe_dcb_config_rx_arbiter_82599(struct ixgbe_hw *hw, +int ixgbe_dcb_config_rx_arbiter_82599(struct ixgbe_hw *hw, u16 *refill, u16 *max, u8 *bwg_id, @@ -76,7 +76,7 @@ s32 ixgbe_dcb_config_rx_arbiter_82599(struct ixgbe_hw *hw, * * Configure Tx Descriptor Arbiter and credits for each traffic class. */ -s32 ixgbe_dcb_config_tx_desc_arbiter_82599(struct ixgbe_hw *hw, +int ixgbe_dcb_config_tx_desc_arbiter_82599(struct ixgbe_hw *hw, u16 *refill, u16 *max, u8 *bwg_id, @@ -128,7 +128,7 @@ s32 ixgbe_dcb_config_tx_desc_arbiter_82599(struct ixgbe_hw *hw, * * Configure Tx Packet Arbiter and credits for each traffic class. */ -s32 ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw, +int ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw, u16 *refill, u16 *max, u8 *bwg_id, @@ -187,7 +187,7 @@ s32 ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw, * * Configure Priority Flow Control (PFC) for each traffic class. */ -s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) +int ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) { u32 i, j, fcrtl, reg; u8 max_tc = 0; @@ -272,7 +272,7 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) * Configure queue statistics registers, all queues belonging to same traffic * class uses a single set of queue statistics counters. */ -static s32 ixgbe_dcb_config_tc_stats_82599(struct ixgbe_hw *hw) +static int ixgbe_dcb_config_tc_stats_82599(struct ixgbe_hw *hw) { u32 reg = 0; u8 i = 0; @@ -330,7 +330,7 @@ static s32 ixgbe_dcb_config_tc_stats_82599(struct ixgbe_hw *hw) * * Configure dcb settings and enable dcb mode. */ -s32 ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, +int ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, u16 *max, u8 *bwg_id, u8 *prio_type, u8 *prio_tc) { ixgbe_dcb_config_rx_arbiter_82599(hw, refill, max, bwg_id, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h index c6f084883cab..f6e5a87c03e3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h @@ -70,30 +70,21 @@ /* DCB hardware-specific driver APIs */ /* DCB PFC functions */ -s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc); +int ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc); /* DCB hw initialization */ -s32 ixgbe_dcb_config_rx_arbiter_82599(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *bwg_id, - u8 *prio_type, - u8 *prio_tc); - -s32 ixgbe_dcb_config_tx_desc_arbiter_82599(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *bwg_id, - u8 *prio_type); - -s32 ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw, - u16 *refill, - u16 *max, - u8 *bwg_id, - u8 *prio_type, - u8 *prio_tc); - -s32 ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, +int ixgbe_dcb_config_rx_arbiter_82599(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *bwg_id, u8 *prio_type, + u8 *prio_tc); + +int ixgbe_dcb_config_tx_desc_arbiter_82599(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *bwg_id, u8 *prio_type); + +int ixgbe_dcb_config_tx_data_arbiter_82599(struct ixgbe_hw *hw, u16 *refill, + u16 *max, u8 *bwg_id, u8 *prio_type, + u8 *prio_tc); + +int ixgbe_dcb_hw_config_82599(struct ixgbe_hw *hw, u8 pfc_en, u16 *refill, u16 *max, u8 *bwg_id, u8 *prio_type, u8 *prio_tc); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 9a63457712c7..b1e7338a4ed1 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -459,7 +459,7 @@ static int ixgbe_set_link_ksettings(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; u32 advertised, old; - s32 err = 0; + int err = 0; if ((hw->phy.media_type == ixgbe_media_type_copper) || (hw->phy.multispeed_fiber)) { @@ -3326,9 +3326,9 @@ static int ixgbe_get_module_info(struct net_device *dev, { struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_hw *hw = &adapter->hw; - s32 status; u8 sff8472_rev, addr_mode; bool page_swap = false; + int status; if (hw->phy.type == ixgbe_phy_fw) return -ENXIO; @@ -3372,7 +3372,7 @@ static int ixgbe_get_module_eeprom(struct net_device *dev, { struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_hw *hw = &adapter->hw; - s32 status = -EFAULT; + int status = -EFAULT; u8 databyte = 0xFF; int i = 0; @@ -3425,44 +3425,44 @@ static const struct { }; static int -ixgbe_get_eee_fw(struct ixgbe_adapter *adapter, struct ethtool_eee *edata) +ixgbe_get_eee_fw(struct ixgbe_adapter *adapter, struct ethtool_keee *edata) { u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 }; struct ixgbe_hw *hw = &adapter->hw; - s32 rc; + int rc; u16 i; rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_UD_2, &info); if (rc) return rc; - edata->lp_advertised = 0; + edata->lp_advertised_u32 = 0; for (i = 0; i < ARRAY_SIZE(ixgbe_lp_map); ++i) { if (info[0] & ixgbe_lp_map[i].lp_advertised) - edata->lp_advertised |= ixgbe_lp_map[i].mac_speed; + edata->lp_advertised_u32 |= ixgbe_lp_map[i].mac_speed; } - edata->supported = 0; + edata->supported_u32 = 0; for (i = 0; i < ARRAY_SIZE(ixgbe_ls_map); ++i) { if (hw->phy.eee_speeds_supported & ixgbe_ls_map[i].mac_speed) - edata->supported |= ixgbe_ls_map[i].supported; + edata->supported_u32 |= ixgbe_ls_map[i].supported; } - edata->advertised = 0; + edata->advertised_u32 = 0; for (i = 0; i < ARRAY_SIZE(ixgbe_ls_map); ++i) { if (hw->phy.eee_speeds_advertised & ixgbe_ls_map[i].mac_speed) - edata->advertised |= ixgbe_ls_map[i].supported; + edata->advertised_u32 |= ixgbe_ls_map[i].supported; } - edata->eee_enabled = !!edata->advertised; + edata->eee_enabled = !!edata->advertised_u32; edata->tx_lpi_enabled = edata->eee_enabled; - if (edata->advertised & edata->lp_advertised) + if (edata->advertised_u32 & edata->lp_advertised_u32) edata->eee_active = true; return 0; } -static int ixgbe_get_eee(struct net_device *netdev, struct ethtool_eee *edata) +static int ixgbe_get_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; @@ -3476,17 +3476,17 @@ static int ixgbe_get_eee(struct net_device *netdev, struct ethtool_eee *edata) return -EOPNOTSUPP; } -static int ixgbe_set_eee(struct net_device *netdev, struct ethtool_eee *edata) +static int ixgbe_set_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; - struct ethtool_eee eee_data; - s32 ret_val; + struct ethtool_keee eee_data; + int ret_val; if (!(adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE)) return -EOPNOTSUPP; - memset(&eee_data, 0, sizeof(struct ethtool_eee)); + memset(&eee_data, 0, sizeof(struct ethtool_keee)); ret_val = ixgbe_get_eee(netdev, &eee_data); if (ret_val) @@ -3504,7 +3504,7 @@ static int ixgbe_set_eee(struct net_device *netdev, struct ethtool_eee *edata) return -EINVAL; } - if (eee_data.advertised != edata->advertised) { + if (eee_data.advertised_u32 != edata->advertised_u32) { e_err(drv, "Setting EEE advertised speeds is not supported\n"); return -EINVAL; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index bd541527c8c7..e23c3614fb10 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -205,7 +205,7 @@ static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter, return 0; } -static s32 ixgbe_get_parent_bus_info(struct ixgbe_adapter *adapter) +static int ixgbe_get_parent_bus_info(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; u16 link_status = 0; @@ -7809,7 +7809,7 @@ static void ixgbe_watchdog_subtask(struct ixgbe_adapter *adapter) static void ixgbe_sfp_detection_subtask(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; - s32 err; + int err; /* not searching for SFP so there is nothing to do here */ if (!(adapter->flags2 & IXGBE_FLAG2_SEARCH_FOR_SFP) && diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c index fe7ef5773369..d67d77e5dacc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c @@ -15,7 +15,7 @@ * * returns SUCCESS if it successfully read message from buffer **/ -s32 ixgbe_read_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) +int ixgbe_read_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; @@ -38,7 +38,7 @@ s32 ixgbe_read_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) * * returns SUCCESS if it successfully copied message into the buffer **/ -s32 ixgbe_write_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) +int ixgbe_write_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; @@ -58,7 +58,7 @@ s32 ixgbe_write_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) * * returns SUCCESS if the Status bit was found or else ERR_MBX **/ -s32 ixgbe_check_for_msg(struct ixgbe_hw *hw, u16 mbx_id) +int ixgbe_check_for_msg(struct ixgbe_hw *hw, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; @@ -75,7 +75,7 @@ s32 ixgbe_check_for_msg(struct ixgbe_hw *hw, u16 mbx_id) * * returns SUCCESS if the Status bit was found or else ERR_MBX **/ -s32 ixgbe_check_for_ack(struct ixgbe_hw *hw, u16 mbx_id) +int ixgbe_check_for_ack(struct ixgbe_hw *hw, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; @@ -92,7 +92,7 @@ s32 ixgbe_check_for_ack(struct ixgbe_hw *hw, u16 mbx_id) * * returns SUCCESS if the Status bit was found or else ERR_MBX **/ -s32 ixgbe_check_for_rst(struct ixgbe_hw *hw, u16 mbx_id) +int ixgbe_check_for_rst(struct ixgbe_hw *hw, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; @@ -109,7 +109,7 @@ s32 ixgbe_check_for_rst(struct ixgbe_hw *hw, u16 mbx_id) * * returns SUCCESS if it successfully received a message notification **/ -static s32 ixgbe_poll_for_msg(struct ixgbe_hw *hw, u16 mbx_id) +static int ixgbe_poll_for_msg(struct ixgbe_hw *hw, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; int countdown = mbx->timeout; @@ -134,7 +134,7 @@ static s32 ixgbe_poll_for_msg(struct ixgbe_hw *hw, u16 mbx_id) * * returns SUCCESS if it successfully received a message acknowledgement **/ -static s32 ixgbe_poll_for_ack(struct ixgbe_hw *hw, u16 mbx_id) +static int ixgbe_poll_for_ack(struct ixgbe_hw *hw, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; int countdown = mbx->timeout; @@ -162,11 +162,11 @@ static s32 ixgbe_poll_for_ack(struct ixgbe_hw *hw, u16 mbx_id) * returns SUCCESS if it successfully received a message notification and * copied it into the receive buffer. **/ -static s32 ixgbe_read_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, +static int ixgbe_read_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; - s32 ret_val; + int ret_val; if (!mbx->ops) return -EIO; @@ -189,11 +189,11 @@ static s32 ixgbe_read_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, * returns SUCCESS if it successfully copied message into the buffer and * received an ack to that message within delay * timeout period **/ -static s32 ixgbe_write_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, - u16 mbx_id) +static int ixgbe_write_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, + u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; - s32 ret_val; + int ret_val; /* exit if either we can't write or there isn't a defined timeout */ if (!mbx->ops || !mbx->timeout) @@ -208,7 +208,7 @@ static s32 ixgbe_write_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, return ixgbe_poll_for_ack(hw, mbx_id); } -static s32 ixgbe_check_for_bit_pf(struct ixgbe_hw *hw, u32 mask, s32 index) +static int ixgbe_check_for_bit_pf(struct ixgbe_hw *hw, u32 mask, s32 index) { u32 mbvficr = IXGBE_READ_REG(hw, IXGBE_MBVFICR(index)); @@ -227,9 +227,9 @@ static s32 ixgbe_check_for_bit_pf(struct ixgbe_hw *hw, u32 mask, s32 index) * * returns SUCCESS if the VF has set the Status bit or else ERR_MBX **/ -static s32 ixgbe_check_for_msg_pf(struct ixgbe_hw *hw, u16 vf_number) +static int ixgbe_check_for_msg_pf(struct ixgbe_hw *hw, u16 vf_number) { - s32 index = IXGBE_MBVFICR_INDEX(vf_number); + int index = IXGBE_MBVFICR_INDEX(vf_number); u32 vf_bit = vf_number % 16; if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFREQ_VF1 << vf_bit, @@ -248,9 +248,9 @@ static s32 ixgbe_check_for_msg_pf(struct ixgbe_hw *hw, u16 vf_number) * * returns SUCCESS if the VF has set the Status bit or else ERR_MBX **/ -static s32 ixgbe_check_for_ack_pf(struct ixgbe_hw *hw, u16 vf_number) +static int ixgbe_check_for_ack_pf(struct ixgbe_hw *hw, u16 vf_number) { - s32 index = IXGBE_MBVFICR_INDEX(vf_number); + int index = IXGBE_MBVFICR_INDEX(vf_number); u32 vf_bit = vf_number % 16; if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFACK_VF1 << vf_bit, @@ -269,7 +269,7 @@ static s32 ixgbe_check_for_ack_pf(struct ixgbe_hw *hw, u16 vf_number) * * returns SUCCESS if the VF has set the Status bit or else ERR_MBX **/ -static s32 ixgbe_check_for_rst_pf(struct ixgbe_hw *hw, u16 vf_number) +static int ixgbe_check_for_rst_pf(struct ixgbe_hw *hw, u16 vf_number) { u32 reg_offset = (vf_number < 32) ? 0 : 1; u32 vf_shift = vf_number % 32; @@ -305,7 +305,7 @@ static s32 ixgbe_check_for_rst_pf(struct ixgbe_hw *hw, u16 vf_number) * * return SUCCESS if we obtained the mailbox lock **/ -static s32 ixgbe_obtain_mbx_lock_pf(struct ixgbe_hw *hw, u16 vf_number) +static int ixgbe_obtain_mbx_lock_pf(struct ixgbe_hw *hw, u16 vf_number) { u32 p2v_mailbox; @@ -329,10 +329,10 @@ static s32 ixgbe_obtain_mbx_lock_pf(struct ixgbe_hw *hw, u16 vf_number) * * returns SUCCESS if it successfully copied message into the buffer **/ -static s32 ixgbe_write_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size, +static int ixgbe_write_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 vf_number) { - s32 ret_val; + int ret_val; u16 i; /* lock the mailbox to prevent pf/vf race condition */ @@ -368,10 +368,10 @@ static s32 ixgbe_write_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size, * memory buffer. The presumption is that the caller knows that there was * a message due to a VF request so no polling for message is needed. **/ -static s32 ixgbe_read_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size, +static int ixgbe_read_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 vf_number) { - s32 ret_val; + int ret_val; u16 i; /* lock the mailbox to prevent pf/vf race condition */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h index 6434c190e7a4..bd205306934b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h @@ -96,11 +96,11 @@ enum ixgbe_pfvf_api_rev { #define IXGBE_VF_MBX_INIT_TIMEOUT 2000 /* number of retries on mailbox */ #define IXGBE_VF_MBX_INIT_DELAY 500 /* microseconds between retries */ -s32 ixgbe_read_mbx(struct ixgbe_hw *, u32 *, u16, u16); -s32 ixgbe_write_mbx(struct ixgbe_hw *, u32 *, u16, u16); -s32 ixgbe_check_for_msg(struct ixgbe_hw *, u16); -s32 ixgbe_check_for_ack(struct ixgbe_hw *, u16); -s32 ixgbe_check_for_rst(struct ixgbe_hw *, u16); +int ixgbe_read_mbx(struct ixgbe_hw *, u32 *, u16, u16); +int ixgbe_write_mbx(struct ixgbe_hw *, u32 *, u16, u16); +int ixgbe_check_for_msg(struct ixgbe_hw *, u16); +int ixgbe_check_for_ack(struct ixgbe_hw *, u16); +int ixgbe_check_for_rst(struct ixgbe_hw *, u16); #ifdef CONFIG_PCI_IOV void ixgbe_init_mbx_params_pf(struct ixgbe_hw *); #endif /* CONFIG_PCI_IOV */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index f28140a05f09..75e9453331ed 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -11,19 +11,19 @@ static void ixgbe_i2c_start(struct ixgbe_hw *hw); static void ixgbe_i2c_stop(struct ixgbe_hw *hw); -static s32 ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data); -static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data); -static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw); -static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data); -static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data); +static int ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data); +static int ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data); +static int ixgbe_get_i2c_ack(struct ixgbe_hw *hw); +static int ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data); +static int ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data); static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl); static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl); -static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data); +static int ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data); static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl); static void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw); static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id); -static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw); -static s32 ixgbe_identify_qsfp_module_generic(struct ixgbe_hw *hw); +static int ixgbe_get_phy_id(struct ixgbe_hw *hw); +static int ixgbe_identify_qsfp_module_generic(struct ixgbe_hw *hw); /** * ixgbe_out_i2c_byte_ack - Send I2C byte with ack @@ -32,9 +32,9 @@ static s32 ixgbe_identify_qsfp_module_generic(struct ixgbe_hw *hw); * * Returns an error code on error. **/ -static s32 ixgbe_out_i2c_byte_ack(struct ixgbe_hw *hw, u8 byte) +static int ixgbe_out_i2c_byte_ack(struct ixgbe_hw *hw, u8 byte) { - s32 status; + int status; status = ixgbe_clock_out_i2c_byte(hw, byte); if (status) @@ -49,9 +49,9 @@ static s32 ixgbe_out_i2c_byte_ack(struct ixgbe_hw *hw, u8 byte) * * Returns an error code on error. **/ -static s32 ixgbe_in_i2c_byte_ack(struct ixgbe_hw *hw, u8 *byte) +static int ixgbe_in_i2c_byte_ack(struct ixgbe_hw *hw, u8 *byte) { - s32 status; + int status; status = ixgbe_clock_in_i2c_byte(hw, byte); if (status) @@ -85,7 +85,7 @@ static u8 ixgbe_ones_comp_byte_add(u8 add1, u8 add2) * * Returns an error code on error. */ -s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, +int ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 *val, bool lock) { u32 swfw_mask = hw->phy.phy_semaphore_mask; @@ -163,7 +163,7 @@ fail: * * Returns an error code on error. */ -s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, +int ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 val, bool lock) { u32 swfw_mask = hw->phy.phy_semaphore_mask; @@ -260,7 +260,7 @@ static bool ixgbe_probe_phy(struct ixgbe_hw *hw, u16 phy_addr) * * Determines the physical layer module found on the current adapter. **/ -s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw) +int ixgbe_identify_phy_generic(struct ixgbe_hw *hw) { u32 status = -EFAULT; u32 phy_addr; @@ -332,11 +332,11 @@ bool ixgbe_check_reset_blocked(struct ixgbe_hw *hw) * @hw: pointer to hardware structure * **/ -static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw) +static int ixgbe_get_phy_id(struct ixgbe_hw *hw) { - s32 status; u16 phy_id_high = 0; u16 phy_id_low = 0; + int status; status = hw->phy.ops.read_reg(hw, MDIO_DEVID1, MDIO_MMD_PMAPMD, &phy_id_high); @@ -394,11 +394,11 @@ static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) * ixgbe_reset_phy_generic - Performs a PHY reset * @hw: pointer to hardware structure **/ -s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw) +int ixgbe_reset_phy_generic(struct ixgbe_hw *hw) { u32 i; u16 ctrl = 0; - s32 status = 0; + int status = 0; if (hw->phy.type == ixgbe_phy_unknown) status = ixgbe_identify_phy_generic(hw); @@ -470,8 +470,8 @@ s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw) * * Reads a value from a specified PHY register without the SWFW lock **/ -s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, - u16 *phy_data) +int ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, + u16 *phy_data) { u32 i, data, command; @@ -546,11 +546,11 @@ s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, * @device_type: 5 bit device type * @phy_data: Pointer to read data from PHY register **/ -s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, +int ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data) { - s32 status; u32 gssr = hw->phy.phy_semaphore_mask; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, gssr) == 0) { status = ixgbe_read_phy_reg_mdi(hw, reg_addr, device_type, @@ -571,8 +571,8 @@ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, * @device_type: 5 bit device type * @phy_data: Data to write to the PHY register **/ -s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, - u32 device_type, u16 phy_data) +int ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, + u16 phy_data) { u32 i, command; @@ -644,11 +644,11 @@ s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, * @device_type: 5 bit device type * @phy_data: Data to write to the PHY register **/ -s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, +int ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data) { - s32 status; u32 gssr = hw->phy.phy_semaphore_mask; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, gssr) == 0) { status = ixgbe_write_phy_reg_mdi(hw, reg_addr, device_type, @@ -668,7 +668,7 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, * @hw: pointer to hardware structure * @cmd: command register value to write **/ -static s32 ixgbe_msca_cmd(struct ixgbe_hw *hw, u32 cmd) +static int ixgbe_msca_cmd(struct ixgbe_hw *hw, u32 cmd) { IXGBE_WRITE_REG(hw, IXGBE_MSCA, cmd); @@ -684,11 +684,11 @@ static s32 ixgbe_msca_cmd(struct ixgbe_hw *hw, u32 cmd) * @regnum: register number * @gssr: semaphore flags to acquire **/ -static s32 ixgbe_mii_bus_read_generic_c22(struct ixgbe_hw *hw, int addr, +static int ixgbe_mii_bus_read_generic_c22(struct ixgbe_hw *hw, int addr, int regnum, u32 gssr) { u32 hwaddr, cmd; - s32 data; + int data; if (hw->mac.ops.acquire_swfw_sync(hw, gssr)) return -EBUSY; @@ -718,11 +718,11 @@ mii_bus_read_done: * @regnum: register number * @gssr: semaphore flags to acquire **/ -static s32 ixgbe_mii_bus_read_generic_c45(struct ixgbe_hw *hw, int addr, +static int ixgbe_mii_bus_read_generic_c45(struct ixgbe_hw *hw, int addr, int devad, int regnum, u32 gssr) { u32 hwaddr, cmd; - s32 data; + int data; if (hw->mac.ops.acquire_swfw_sync(hw, gssr)) return -EBUSY; @@ -756,11 +756,11 @@ mii_bus_read_done: * @val: value to write * @gssr: semaphore flags to acquire **/ -static s32 ixgbe_mii_bus_write_generic_c22(struct ixgbe_hw *hw, int addr, +static int ixgbe_mii_bus_write_generic_c22(struct ixgbe_hw *hw, int addr, int regnum, u16 val, u32 gssr) { u32 hwaddr, cmd; - s32 err; + int err; if (hw->mac.ops.acquire_swfw_sync(hw, gssr)) return -EBUSY; @@ -787,12 +787,12 @@ static s32 ixgbe_mii_bus_write_generic_c22(struct ixgbe_hw *hw, int addr, * @val: value to write * @gssr: semaphore flags to acquire **/ -static s32 ixgbe_mii_bus_write_generic_c45(struct ixgbe_hw *hw, int addr, +static int ixgbe_mii_bus_write_generic_c45(struct ixgbe_hw *hw, int addr, int devad, int regnum, u16 val, u32 gssr) { u32 hwaddr, cmd; - s32 err; + int err; if (hw->mac.ops.acquire_swfw_sync(hw, gssr)) return -EBUSY; @@ -821,7 +821,7 @@ mii_bus_write_done: * @addr: address * @regnum: register number **/ -static s32 ixgbe_mii_bus_read_c22(struct mii_bus *bus, int addr, int regnum) +static int ixgbe_mii_bus_read_c22(struct mii_bus *bus, int addr, int regnum) { struct ixgbe_adapter *adapter = bus->priv; struct ixgbe_hw *hw = &adapter->hw; @@ -837,7 +837,7 @@ static s32 ixgbe_mii_bus_read_c22(struct mii_bus *bus, int addr, int regnum) * @addr: address * @regnum: register number **/ -static s32 ixgbe_mii_bus_read_c45(struct mii_bus *bus, int devad, int addr, +static int ixgbe_mii_bus_read_c45(struct mii_bus *bus, int devad, int addr, int regnum) { struct ixgbe_adapter *adapter = bus->priv; @@ -854,7 +854,7 @@ static s32 ixgbe_mii_bus_read_c45(struct mii_bus *bus, int devad, int addr, * @regnum: register number * @val: value to write **/ -static s32 ixgbe_mii_bus_write_c22(struct mii_bus *bus, int addr, int regnum, +static int ixgbe_mii_bus_write_c22(struct mii_bus *bus, int addr, int regnum, u16 val) { struct ixgbe_adapter *adapter = bus->priv; @@ -872,7 +872,7 @@ static s32 ixgbe_mii_bus_write_c22(struct mii_bus *bus, int addr, int regnum, * @regnum: register number * @val: value to write **/ -static s32 ixgbe_mii_bus_write_c45(struct mii_bus *bus, int addr, int devad, +static int ixgbe_mii_bus_write_c45(struct mii_bus *bus, int addr, int devad, int regnum, u16 val) { struct ixgbe_adapter *adapter = bus->priv; @@ -889,7 +889,7 @@ static s32 ixgbe_mii_bus_write_c45(struct mii_bus *bus, int addr, int devad, * @addr: address * @regnum: register number **/ -static s32 ixgbe_x550em_a_mii_bus_read_c22(struct mii_bus *bus, int addr, +static int ixgbe_x550em_a_mii_bus_read_c22(struct mii_bus *bus, int addr, int regnum) { struct ixgbe_adapter *adapter = bus->priv; @@ -907,7 +907,7 @@ static s32 ixgbe_x550em_a_mii_bus_read_c22(struct mii_bus *bus, int addr, * @devad: device address to read * @regnum: register number **/ -static s32 ixgbe_x550em_a_mii_bus_read_c45(struct mii_bus *bus, int addr, +static int ixgbe_x550em_a_mii_bus_read_c45(struct mii_bus *bus, int addr, int devad, int regnum) { struct ixgbe_adapter *adapter = bus->priv; @@ -925,7 +925,7 @@ static s32 ixgbe_x550em_a_mii_bus_read_c45(struct mii_bus *bus, int addr, * @regnum: register number * @val: value to write **/ -static s32 ixgbe_x550em_a_mii_bus_write_c22(struct mii_bus *bus, int addr, +static int ixgbe_x550em_a_mii_bus_write_c22(struct mii_bus *bus, int addr, int regnum, u16 val) { struct ixgbe_adapter *adapter = bus->priv; @@ -944,7 +944,7 @@ static s32 ixgbe_x550em_a_mii_bus_write_c22(struct mii_bus *bus, int addr, * @regnum: register number * @val: value to write **/ -static s32 ixgbe_x550em_a_mii_bus_write_c45(struct mii_bus *bus, int addr, +static int ixgbe_x550em_a_mii_bus_write_c45(struct mii_bus *bus, int addr, int devad, int regnum, u16 val) { struct ixgbe_adapter *adapter = bus->priv; @@ -1023,13 +1023,13 @@ out: * * ixgbe_mii_bus_init initializes a mii_bus structure in adapter **/ -s32 ixgbe_mii_bus_init(struct ixgbe_hw *hw) +int ixgbe_mii_bus_init(struct ixgbe_hw *hw) { - s32 (*write_c22)(struct mii_bus *bus, int addr, int regnum, u16 val); - s32 (*read_c22)(struct mii_bus *bus, int addr, int regnum); - s32 (*write_c45)(struct mii_bus *bus, int addr, int devad, int regnum, + int (*write_c22)(struct mii_bus *bus, int addr, int regnum, u16 val); + int (*read_c22)(struct mii_bus *bus, int addr, int regnum); + int (*write_c45)(struct mii_bus *bus, int addr, int devad, int regnum, u16 val); - s32 (*read_c45)(struct mii_bus *bus, int addr, int devad, int regnum); + int (*read_c45)(struct mii_bus *bus, int addr, int devad, int regnum); struct ixgbe_adapter *adapter = hw->back; struct pci_dev *pdev = adapter->pdev; struct device *dev = &adapter->netdev->dev; @@ -1095,12 +1095,12 @@ s32 ixgbe_mii_bus_init(struct ixgbe_hw *hw) * * Restart autonegotiation and PHY and waits for completion. **/ -s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw) +int ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw) { - s32 status = 0; u16 autoneg_reg = IXGBE_MII_AUTONEG_REG; - bool autoneg = false; ixgbe_link_speed speed; + bool autoneg = false; + int status = 0; ixgbe_get_copper_link_capabilities_generic(hw, &speed, &autoneg); @@ -1173,7 +1173,7 @@ s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw) * @speed: new link speed * @autoneg_wait_to_complete: unused **/ -s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, +int ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete) { @@ -1214,10 +1214,10 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, * Determines the supported link capabilities by reading the PHY auto * negotiation register. */ -static s32 ixgbe_get_copper_speeds_supported(struct ixgbe_hw *hw) +static int ixgbe_get_copper_speeds_supported(struct ixgbe_hw *hw) { u16 speed_ability; - s32 status; + int status; status = hw->phy.ops.read_reg(hw, MDIO_SPEED, MDIO_MMD_PMAPMD, &speed_ability); @@ -1253,11 +1253,11 @@ static s32 ixgbe_get_copper_speeds_supported(struct ixgbe_hw *hw) * @speed: pointer to link speed * @autoneg: boolean auto-negotiation value */ -s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, +int ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *autoneg) { - s32 status = 0; + int status = 0; *autoneg = true; if (!hw->phy.speeds_supported) @@ -1276,15 +1276,15 @@ s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, * Reads the VS1 register to determine if link is up and the current speed for * the PHY. **/ -s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed, +int ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *link_up) { - s32 status; - u32 time_out; u32 max_time_out = 10; - u16 phy_link = 0; u16 phy_speed = 0; + u16 phy_link = 0; u16 phy_data = 0; + u32 time_out; + int status; /* Initialize speed and link to default case */ *link_up = false; @@ -1326,7 +1326,7 @@ s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed, * it is called via a function pointer that could call other * functions that could return an error. **/ -s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw) +int ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw) { u16 autoneg_reg = IXGBE_MII_AUTONEG_REG; bool autoneg = false; @@ -1399,13 +1399,13 @@ s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw) * ixgbe_reset_phy_nl - Performs a PHY reset * @hw: pointer to hardware structure **/ -s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw) +int ixgbe_reset_phy_nl(struct ixgbe_hw *hw) { u16 phy_offset, control, eword, edata, block_crc; - bool end_data = false; u16 list_offset, data_offset; + bool end_data = false; u16 phy_data = 0; - s32 ret_val; + int ret_val; u32 i; /* Blocked by MNG FW so bail */ @@ -1506,7 +1506,7 @@ err_eeprom: * * Determines HW type and calls appropriate function. **/ -s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw) +int ixgbe_identify_module_generic(struct ixgbe_hw *hw) { switch (hw->mac.ops.get_media_type(hw)) { case ixgbe_media_type_fiber: @@ -1527,19 +1527,19 @@ s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw) * * Searches for and identifies the SFP module and assigns appropriate PHY type. **/ -s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) +int ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) { + enum ixgbe_sfp_type stored_sfp_type = hw->phy.sfp_type; struct ixgbe_adapter *adapter = hw->back; - s32 status; + u8 oui_bytes[3] = {0, 0, 0}; + u8 comp_codes_10g = 0; + u8 comp_codes_1g = 0; + u16 enforce_sfp = 0; u32 vendor_oui = 0; - enum ixgbe_sfp_type stored_sfp_type = hw->phy.sfp_type; u8 identifier = 0; - u8 comp_codes_1g = 0; - u8 comp_codes_10g = 0; - u8 oui_bytes[3] = {0, 0, 0}; u8 cable_tech = 0; u8 cable_spec = 0; - u16 enforce_sfp = 0; + int status; if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_fiber) { hw->phy.sfp_type = ixgbe_sfp_type_not_present; @@ -1792,10 +1792,10 @@ err_read_i2c_eeprom: * * Searches for and identifies the QSFP module and assigns appropriate PHY type **/ -static s32 ixgbe_identify_qsfp_module_generic(struct ixgbe_hw *hw) +static int ixgbe_identify_qsfp_module_generic(struct ixgbe_hw *hw) { struct ixgbe_adapter *adapter = hw->back; - s32 status; + int status; u32 vendor_oui = 0; enum ixgbe_sfp_type stored_sfp_type = hw->phy.sfp_type; u8 identifier = 0; @@ -1975,7 +1975,7 @@ err_read_i2c_eeprom: * Checks the MAC's EEPROM to see if it supports a given SFP+ module type, if * so it returns the offsets to the phy init sequence block. **/ -s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, +int ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, u16 *list_offset, u16 *data_offset) { @@ -2065,7 +2065,7 @@ err_phy: * * Performs byte read operation to SFP module's EEPROM over I2C interface. **/ -s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *eeprom_data) { return hw->phy.ops.read_i2c_byte(hw, byte_offset, @@ -2081,7 +2081,7 @@ s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, * * Performs byte read operation to SFP module's SFF-8472 data over I2C **/ -s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *sff8472_data) { return hw->phy.ops.read_i2c_byte(hw, byte_offset, @@ -2097,7 +2097,7 @@ s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, * * Performs byte write operation to SFP module's EEPROM over I2C interface. **/ -s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 eeprom_data) { return hw->phy.ops.write_i2c_byte(hw, byte_offset, @@ -2131,14 +2131,14 @@ static bool ixgbe_is_sfp_probe(struct ixgbe_hw *hw, u8 offset, u8 addr) * Performs byte read operation to SFP module's EEPROM over I2C interface at * a specified device address. */ -static s32 ixgbe_read_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_read_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data, bool lock) { - s32 status; - u32 max_retry = 10; - u32 retry = 0; u32 swfw_mask = hw->phy.phy_semaphore_mask; + u32 max_retry = 10; bool nack = true; + u32 retry = 0; + int status; if (hw->mac.type >= ixgbe_mac_X550) max_retry = 3; @@ -2221,7 +2221,7 @@ fail: * Performs byte read operation to SFP module's EEPROM over I2C interface at * a specified device address. */ -s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data) { return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr, @@ -2238,7 +2238,7 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, * Performs byte read operation to SFP module's EEPROM over I2C interface at * a specified device address. */ -s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data) { return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr, @@ -2256,13 +2256,13 @@ s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, * Performs byte write operation to SFP module's EEPROM over I2C interface at * a specified device address. */ -static s32 ixgbe_write_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, +static int ixgbe_write_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data, bool lock) { - s32 status; + u32 swfw_mask = hw->phy.phy_semaphore_mask; u32 max_retry = 1; u32 retry = 0; - u32 swfw_mask = hw->phy.phy_semaphore_mask; + int status; if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) return -EBUSY; @@ -2324,7 +2324,7 @@ fail: * Performs byte write operation to SFP module's EEPROM over I2C interface at * a specified device address. */ -s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data) { return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr, @@ -2341,7 +2341,7 @@ s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, * Performs byte write operation to SFP module's EEPROM over I2C interface at * a specified device address. */ -s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data) { return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr, @@ -2422,10 +2422,10 @@ static void ixgbe_i2c_stop(struct ixgbe_hw *hw) * * Clocks in one byte data via I2C data/clock **/ -static s32 ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data) +static int ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data) { - s32 i; bool bit = false; + int i; *data = 0; for (i = 7; i >= 0; i--) { @@ -2443,12 +2443,12 @@ static s32 ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data) * * Clocks out one byte data via I2C data/clock **/ -static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data) +static int ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data) { - s32 status; - s32 i; - u32 i2cctl; bool bit = false; + int status; + u32 i2cctl; + int i; for (i = 7; i >= 0; i--) { bit = (data >> i) & 0x1; @@ -2474,14 +2474,14 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data) * * Clocks in/out one bit via I2C data/clock **/ -static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw) +static int ixgbe_get_i2c_ack(struct ixgbe_hw *hw) { - u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); - s32 status = 0; - u32 i = 0; u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); u32 timeout = 10; bool ack = true; + int status = 0; + u32 i = 0; if (data_oe_bit) { i2cctl |= IXGBE_I2C_DATA_OUT(hw); @@ -2525,7 +2525,7 @@ static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw) * * Clocks in one bit via I2C data/clock **/ -static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data) +static int ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data) { u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); @@ -2559,10 +2559,10 @@ static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data) * * Clocks out one bit via I2C data/clock **/ -static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data) +static int ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data) { - s32 status; u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + int status; status = ixgbe_set_i2c_data(hw, &i2cctl, data); if (status == 0) { @@ -2647,7 +2647,7 @@ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) * Sets the I2C data bit * Asserts the I2C data output enable on X550 hardware. **/ -static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) +static int ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) { u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); @@ -2769,7 +2769,7 @@ bool ixgbe_tn_check_overtemp(struct ixgbe_hw *hw) * @hw: pointer to hardware structure * @on: true for on, false for off **/ -s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on) +int ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on) { u32 status; u16 reg; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index ef72729d7c93..beedcb7bec0d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -121,57 +121,57 @@ /* SFP+ SFF-8472 Compliance code */ #define IXGBE_SFF_SFF_8472_UNSUP 0x00 -s32 ixgbe_mii_bus_init(struct ixgbe_hw *hw); +int ixgbe_mii_bus_init(struct ixgbe_hw *hw); -s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw); -s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw); -s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, +int ixgbe_identify_phy_generic(struct ixgbe_hw *hw); +int ixgbe_reset_phy_generic(struct ixgbe_hw *hw); +int ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data); -s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, +int ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data); -s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, +int ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data); -s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, +int ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data); -s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw); -s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, +int ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw); +int ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); -s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, +int ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *autoneg); bool ixgbe_check_reset_blocked(struct ixgbe_hw *hw); /* PHY specific */ -s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, +int ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *link_up); -s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw); +int ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw); -s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw); -s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on); -s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw); -s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw); -s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, +int ixgbe_reset_phy_nl(struct ixgbe_hw *hw); +int ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on); +int ixgbe_identify_module_generic(struct ixgbe_hw *hw); +int ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw); +int ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, u16 *list_offset, u16 *data_offset); bool ixgbe_tn_check_overtemp(struct ixgbe_hw *hw); -s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data); -s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data); -s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data); -s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data); -s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *eeprom_data); -s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *sff8472_data); -s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, +int ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 eeprom_data); -s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *, u8 addr, u16 reg, +int ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *, u8 addr, u16 reg, u16 *val, bool lock); -s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *, u8 addr, u16 reg, +int ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *, u8 addr, u16 reg, u16 val, bool lock); #endif /* _IXGBE_PHY_H_ */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 7299a830f6e4..fcfd0a075eee 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -492,7 +492,7 @@ static int ixgbe_set_vf_lpe(struct ixgbe_adapter *adapter, u32 max_frame, u32 vf struct net_device *dev = adapter->netdev; int pf_max_frame = dev->mtu + ETH_HLEN; u32 reg_offset, vf_shift, vfre; - s32 err = 0; + int err = 0; #ifdef CONFIG_FCOE if (dev->features & NETIF_F_FCOE_MTU) @@ -775,7 +775,7 @@ static void ixgbe_vf_clear_mbx(struct ixgbe_adapter *adapter, u32 vf) static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter, int vf, unsigned char *mac_addr) { - s32 retval; + int retval; ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf); retval = ixgbe_add_mac_filter(adapter, mac_addr, vf); @@ -1254,7 +1254,7 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf) u32 mbx_size = IXGBE_VFMAILBOX_SIZE; u32 msgbuf[IXGBE_VFMAILBOX_SIZE]; struct ixgbe_hw *hw = &adapter->hw; - s32 retval; + int retval; retval = ixgbe_read_mbx(hw, msgbuf, mbx_size, vf); @@ -1418,7 +1418,7 @@ void ixgbe_set_all_vfs(struct ixgbe_adapter *adapter) int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - s32 retval; + int retval; if (vf >= adapter->num_vfs) return -EINVAL; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 61b9774b3d31..d44c58130be2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3393,50 +3393,50 @@ struct ixgbe_hw; /* Function pointer table */ struct ixgbe_eeprom_operations { - s32 (*init_params)(struct ixgbe_hw *); - s32 (*read)(struct ixgbe_hw *, u16, u16 *); - s32 (*read_buffer)(struct ixgbe_hw *, u16, u16, u16 *); - s32 (*write)(struct ixgbe_hw *, u16, u16); - s32 (*write_buffer)(struct ixgbe_hw *, u16, u16, u16 *); - s32 (*validate_checksum)(struct ixgbe_hw *, u16 *); - s32 (*update_checksum)(struct ixgbe_hw *); - s32 (*calc_checksum)(struct ixgbe_hw *); + int (*init_params)(struct ixgbe_hw *); + int (*read)(struct ixgbe_hw *, u16, u16 *); + int (*read_buffer)(struct ixgbe_hw *, u16, u16, u16 *); + int (*write)(struct ixgbe_hw *, u16, u16); + int (*write_buffer)(struct ixgbe_hw *, u16, u16, u16 *); + int (*validate_checksum)(struct ixgbe_hw *, u16 *); + int (*update_checksum)(struct ixgbe_hw *); + int (*calc_checksum)(struct ixgbe_hw *); }; struct ixgbe_mac_operations { - s32 (*init_hw)(struct ixgbe_hw *); - s32 (*reset_hw)(struct ixgbe_hw *); - s32 (*start_hw)(struct ixgbe_hw *); - s32 (*clear_hw_cntrs)(struct ixgbe_hw *); + int (*init_hw)(struct ixgbe_hw *); + int (*reset_hw)(struct ixgbe_hw *); + int (*start_hw)(struct ixgbe_hw *); + int (*clear_hw_cntrs)(struct ixgbe_hw *); enum ixgbe_media_type (*get_media_type)(struct ixgbe_hw *); - s32 (*get_mac_addr)(struct ixgbe_hw *, u8 *); - s32 (*get_san_mac_addr)(struct ixgbe_hw *, u8 *); - s32 (*get_device_caps)(struct ixgbe_hw *, u16 *); - s32 (*get_wwn_prefix)(struct ixgbe_hw *, u16 *, u16 *); - s32 (*stop_adapter)(struct ixgbe_hw *); - s32 (*get_bus_info)(struct ixgbe_hw *); + int (*get_mac_addr)(struct ixgbe_hw *, u8 *); + int (*get_san_mac_addr)(struct ixgbe_hw *, u8 *); + int (*get_device_caps)(struct ixgbe_hw *, u16 *); + int (*get_wwn_prefix)(struct ixgbe_hw *, u16 *, u16 *); + int (*stop_adapter)(struct ixgbe_hw *); + int (*get_bus_info)(struct ixgbe_hw *); void (*set_lan_id)(struct ixgbe_hw *); - s32 (*read_analog_reg8)(struct ixgbe_hw*, u32, u8*); - s32 (*write_analog_reg8)(struct ixgbe_hw*, u32, u8); - s32 (*setup_sfp)(struct ixgbe_hw *); - s32 (*disable_rx_buff)(struct ixgbe_hw *); - s32 (*enable_rx_buff)(struct ixgbe_hw *); - s32 (*enable_rx_dma)(struct ixgbe_hw *, u32); - s32 (*acquire_swfw_sync)(struct ixgbe_hw *, u32); + int (*read_analog_reg8)(struct ixgbe_hw*, u32, u8*); + int (*write_analog_reg8)(struct ixgbe_hw*, u32, u8); + int (*setup_sfp)(struct ixgbe_hw *); + int (*disable_rx_buff)(struct ixgbe_hw *); + int (*enable_rx_buff)(struct ixgbe_hw *); + int (*enable_rx_dma)(struct ixgbe_hw *, u32); + int (*acquire_swfw_sync)(struct ixgbe_hw *, u32); void (*release_swfw_sync)(struct ixgbe_hw *, u32); void (*init_swfw_sync)(struct ixgbe_hw *); - s32 (*prot_autoc_read)(struct ixgbe_hw *, bool *, u32 *); - s32 (*prot_autoc_write)(struct ixgbe_hw *, u32, bool); + int (*prot_autoc_read)(struct ixgbe_hw *, bool *, u32 *); + int (*prot_autoc_write)(struct ixgbe_hw *, u32, bool); /* Link */ void (*disable_tx_laser)(struct ixgbe_hw *); void (*enable_tx_laser)(struct ixgbe_hw *); void (*flap_tx_laser)(struct ixgbe_hw *); void (*stop_link_on_d3)(struct ixgbe_hw *); - s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); - s32 (*setup_mac_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); - s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool); - s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *, + int (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); + int (*setup_mac_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); + int (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool); + int (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *, bool *); void (*set_rate_select_speed)(struct ixgbe_hw *, ixgbe_link_speed); @@ -3444,38 +3444,38 @@ struct ixgbe_mac_operations { void (*set_rxpba)(struct ixgbe_hw *, int, u32, int); /* LED */ - s32 (*led_on)(struct ixgbe_hw *, u32); - s32 (*led_off)(struct ixgbe_hw *, u32); - s32 (*blink_led_start)(struct ixgbe_hw *, u32); - s32 (*blink_led_stop)(struct ixgbe_hw *, u32); - s32 (*init_led_link_act)(struct ixgbe_hw *); + int (*led_on)(struct ixgbe_hw *, u32); + int (*led_off)(struct ixgbe_hw *, u32); + int (*blink_led_start)(struct ixgbe_hw *, u32); + int (*blink_led_stop)(struct ixgbe_hw *, u32); + int (*init_led_link_act)(struct ixgbe_hw *); /* RAR, Multicast, VLAN */ - s32 (*set_rar)(struct ixgbe_hw *, u32, u8 *, u32, u32); - s32 (*clear_rar)(struct ixgbe_hw *, u32); - s32 (*set_vmdq)(struct ixgbe_hw *, u32, u32); - s32 (*set_vmdq_san_mac)(struct ixgbe_hw *, u32); - s32 (*clear_vmdq)(struct ixgbe_hw *, u32, u32); - s32 (*init_rx_addrs)(struct ixgbe_hw *); - s32 (*update_mc_addr_list)(struct ixgbe_hw *, struct net_device *); - s32 (*enable_mc)(struct ixgbe_hw *); - s32 (*disable_mc)(struct ixgbe_hw *); - s32 (*clear_vfta)(struct ixgbe_hw *); - s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool, bool); - s32 (*init_uta_tables)(struct ixgbe_hw *); + int (*set_rar)(struct ixgbe_hw *, u32, u8 *, u32, u32); + int (*clear_rar)(struct ixgbe_hw *, u32); + int (*set_vmdq)(struct ixgbe_hw *, u32, u32); + int (*set_vmdq_san_mac)(struct ixgbe_hw *, u32); + int (*clear_vmdq)(struct ixgbe_hw *, u32, u32); + int (*init_rx_addrs)(struct ixgbe_hw *); + int (*update_mc_addr_list)(struct ixgbe_hw *, struct net_device *); + int (*enable_mc)(struct ixgbe_hw *); + int (*disable_mc)(struct ixgbe_hw *); + int (*clear_vfta)(struct ixgbe_hw *); + int (*set_vfta)(struct ixgbe_hw *, u32, u32, bool, bool); + int (*init_uta_tables)(struct ixgbe_hw *); void (*set_mac_anti_spoofing)(struct ixgbe_hw *, bool, int); void (*set_vlan_anti_spoofing)(struct ixgbe_hw *, bool, int); /* Flow Control */ - s32 (*fc_enable)(struct ixgbe_hw *); - s32 (*setup_fc)(struct ixgbe_hw *); + int (*fc_enable)(struct ixgbe_hw *); + int (*setup_fc)(struct ixgbe_hw *); void (*fc_autoneg)(struct ixgbe_hw *); /* Manageability interface */ - s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8, u16, + int (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8, u16, const char *); - s32 (*get_thermal_sensor_data)(struct ixgbe_hw *); - s32 (*init_thermal_sensor_thresh)(struct ixgbe_hw *hw); + int (*get_thermal_sensor_data)(struct ixgbe_hw *); + int (*init_thermal_sensor_thresh)(struct ixgbe_hw *hw); bool (*fw_recovery_mode)(struct ixgbe_hw *hw); void (*disable_rx)(struct ixgbe_hw *hw); void (*enable_rx)(struct ixgbe_hw *hw); @@ -3484,47 +3484,47 @@ struct ixgbe_mac_operations { void (*set_ethertype_anti_spoofing)(struct ixgbe_hw *, bool, int); /* DMA Coalescing */ - s32 (*dmac_config)(struct ixgbe_hw *hw); - s32 (*dmac_update_tcs)(struct ixgbe_hw *hw); - s32 (*dmac_config_tcs)(struct ixgbe_hw *hw); - s32 (*read_iosf_sb_reg)(struct ixgbe_hw *, u32, u32, u32 *); - s32 (*write_iosf_sb_reg)(struct ixgbe_hw *, u32, u32, u32); + int (*dmac_config)(struct ixgbe_hw *hw); + int (*dmac_update_tcs)(struct ixgbe_hw *hw); + int (*dmac_config_tcs)(struct ixgbe_hw *hw); + int (*read_iosf_sb_reg)(struct ixgbe_hw *, u32, u32, u32 *); + int (*write_iosf_sb_reg)(struct ixgbe_hw *, u32, u32, u32); }; struct ixgbe_phy_operations { - s32 (*identify)(struct ixgbe_hw *); - s32 (*identify_sfp)(struct ixgbe_hw *); - s32 (*init)(struct ixgbe_hw *); - s32 (*reset)(struct ixgbe_hw *); - s32 (*read_reg)(struct ixgbe_hw *, u32, u32, u16 *); - s32 (*write_reg)(struct ixgbe_hw *, u32, u32, u16); - s32 (*read_reg_mdi)(struct ixgbe_hw *, u32, u32, u16 *); - s32 (*write_reg_mdi)(struct ixgbe_hw *, u32, u32, u16); - s32 (*setup_link)(struct ixgbe_hw *); - s32 (*setup_internal_link)(struct ixgbe_hw *); - s32 (*setup_link_speed)(struct ixgbe_hw *, ixgbe_link_speed, bool); - s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *); - s32 (*read_i2c_byte)(struct ixgbe_hw *, u8, u8, u8 *); - s32 (*write_i2c_byte)(struct ixgbe_hw *, u8, u8, u8); - s32 (*read_i2c_sff8472)(struct ixgbe_hw *, u8 , u8 *); - s32 (*read_i2c_eeprom)(struct ixgbe_hw *, u8 , u8 *); - s32 (*write_i2c_eeprom)(struct ixgbe_hw *, u8, u8); + int (*identify)(struct ixgbe_hw *); + int (*identify_sfp)(struct ixgbe_hw *); + int (*init)(struct ixgbe_hw *); + int (*reset)(struct ixgbe_hw *); + int (*read_reg)(struct ixgbe_hw *, u32, u32, u16 *); + int (*write_reg)(struct ixgbe_hw *, u32, u32, u16); + int (*read_reg_mdi)(struct ixgbe_hw *, u32, u32, u16 *); + int (*write_reg_mdi)(struct ixgbe_hw *, u32, u32, u16); + int (*setup_link)(struct ixgbe_hw *); + int (*setup_internal_link)(struct ixgbe_hw *); + int (*setup_link_speed)(struct ixgbe_hw *, ixgbe_link_speed, bool); + int (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *); + int (*read_i2c_byte)(struct ixgbe_hw *, u8, u8, u8 *); + int (*write_i2c_byte)(struct ixgbe_hw *, u8, u8, u8); + int (*read_i2c_sff8472)(struct ixgbe_hw *, u8, u8 *); + int (*read_i2c_eeprom)(struct ixgbe_hw *, u8, u8 *); + int (*write_i2c_eeprom)(struct ixgbe_hw *, u8, u8); bool (*check_overtemp)(struct ixgbe_hw *); - s32 (*set_phy_power)(struct ixgbe_hw *, bool on); - s32 (*enter_lplu)(struct ixgbe_hw *); - s32 (*handle_lasi)(struct ixgbe_hw *hw, bool *); - s32 (*read_i2c_byte_unlocked)(struct ixgbe_hw *, u8 offset, u8 addr, + int (*set_phy_power)(struct ixgbe_hw *, bool on); + int (*enter_lplu)(struct ixgbe_hw *); + int (*handle_lasi)(struct ixgbe_hw *hw, bool *); + int (*read_i2c_byte_unlocked)(struct ixgbe_hw *, u8 offset, u8 addr, u8 *value); - s32 (*write_i2c_byte_unlocked)(struct ixgbe_hw *, u8 offset, u8 addr, + int (*write_i2c_byte_unlocked)(struct ixgbe_hw *, u8 offset, u8 addr, u8 value); }; struct ixgbe_link_operations { - s32 (*read_link)(struct ixgbe_hw *, u8 addr, u16 reg, u16 *val); - s32 (*read_link_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + int (*read_link)(struct ixgbe_hw *, u8 addr, u16 reg, u16 *val); + int (*read_link_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, u16 *val); - s32 (*write_link)(struct ixgbe_hw *, u8 addr, u16 reg, u16 val); - s32 (*write_link_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + int (*write_link)(struct ixgbe_hw *, u8 addr, u16 reg, u16 val); + int (*write_link_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, u16 val); }; @@ -3602,14 +3602,14 @@ struct ixgbe_phy_info { #include "ixgbe_mbx.h" struct ixgbe_mbx_operations { - s32 (*init_params)(struct ixgbe_hw *hw); - s32 (*read)(struct ixgbe_hw *, u32 *, u16, u16); - s32 (*write)(struct ixgbe_hw *, u32 *, u16, u16); - s32 (*read_posted)(struct ixgbe_hw *, u32 *, u16, u16); - s32 (*write_posted)(struct ixgbe_hw *, u32 *, u16, u16); - s32 (*check_for_msg)(struct ixgbe_hw *, u16); - s32 (*check_for_ack)(struct ixgbe_hw *, u16); - s32 (*check_for_rst)(struct ixgbe_hw *, u16); + int (*init_params)(struct ixgbe_hw *hw); + int (*read)(struct ixgbe_hw *, u32 *, u16, u16); + int (*write)(struct ixgbe_hw *, u32 *, u16, u16); + int (*read_posted)(struct ixgbe_hw *, u32 *, u16, u16); + int (*write_posted)(struct ixgbe_hw *, u32 *, u16, u16); + int (*check_for_msg)(struct ixgbe_hw *, u16); + int (*check_for_ack)(struct ixgbe_hw *, u16); + int (*check_for_rst)(struct ixgbe_hw *, u16); }; struct ixgbe_mbx_stats { @@ -3656,7 +3656,7 @@ struct ixgbe_hw { struct ixgbe_info { enum ixgbe_mac_type mac; - s32 (*get_invariants)(struct ixgbe_hw *); + int (*get_invariants)(struct ixgbe_hw *); const struct ixgbe_mac_operations *mac_ops; const struct ixgbe_eeprom_operations *eeprom_ops; const struct ixgbe_phy_operations *phy_ops; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 57a912e4653f..f1ffa398f6df 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -16,9 +16,9 @@ #define IXGBE_X540_VFT_TBL_SIZE 128 #define IXGBE_X540_RX_PB_SIZE 384 -static s32 ixgbe_update_flash_X540(struct ixgbe_hw *hw); -static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw); -static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw); +static int ixgbe_update_flash_X540(struct ixgbe_hw *hw); +static int ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw); +static int ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw); static void ixgbe_release_swfw_sync_semaphore(struct ixgbe_hw *hw); enum ixgbe_media_type ixgbe_get_media_type_X540(struct ixgbe_hw *hw) @@ -26,7 +26,7 @@ enum ixgbe_media_type ixgbe_get_media_type_X540(struct ixgbe_hw *hw) return ixgbe_media_type_copper; } -s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw) +int ixgbe_get_invariants_X540(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; struct ixgbe_phy_info *phy = &hw->phy; @@ -51,7 +51,7 @@ s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw) * @speed: new link speed * @autoneg_wait_to_complete: true when waiting for completion is needed **/ -s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, ixgbe_link_speed speed, +int ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete) { return hw->phy.ops.setup_link_speed(hw, speed, @@ -66,11 +66,11 @@ s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, ixgbe_link_speed speed, * and clears all interrupts, perform a PHY reset, and perform a link (MAC) * reset. **/ -s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw) +int ixgbe_reset_hw_X540(struct ixgbe_hw *hw) { - s32 status; - u32 ctrl, i; u32 swfw_mask = hw->phy.phy_semaphore_mask; + u32 ctrl, i; + int status; /* Call adapter stop to disable tx/rx and clear interrupts */ status = hw->mac.ops.stop_adapter(hw); @@ -166,9 +166,9 @@ mac_reset_top: * and the generation start_hw function. * Then performs revision-specific operations, if any. **/ -s32 ixgbe_start_hw_X540(struct ixgbe_hw *hw) +int ixgbe_start_hw_X540(struct ixgbe_hw *hw) { - s32 ret_val; + int ret_val; ret_val = ixgbe_start_hw_generic(hw); if (ret_val) @@ -184,7 +184,7 @@ s32 ixgbe_start_hw_X540(struct ixgbe_hw *hw) * Initializes the EEPROM parameters ixgbe_eeprom_info within the * ixgbe_hw struct in order to set up EEPROM access. **/ -s32 ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw) +int ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw) { struct ixgbe_eeprom_info *eeprom = &hw->eeprom; @@ -215,9 +215,9 @@ s32 ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw) * * Reads a 16 bit word from the EEPROM using the EERD register. **/ -static s32 ixgbe_read_eerd_X540(struct ixgbe_hw *hw, u16 offset, u16 *data) +static int ixgbe_read_eerd_X540(struct ixgbe_hw *hw, u16 offset, u16 *data) { - s32 status; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM)) return -EBUSY; @@ -237,10 +237,10 @@ static s32 ixgbe_read_eerd_X540(struct ixgbe_hw *hw, u16 offset, u16 *data) * * Reads a 16 bit word(s) from the EEPROM using the EERD register. **/ -static s32 ixgbe_read_eerd_buffer_X540(struct ixgbe_hw *hw, +static int ixgbe_read_eerd_buffer_X540(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { - s32 status; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM)) return -EBUSY; @@ -259,9 +259,9 @@ static s32 ixgbe_read_eerd_buffer_X540(struct ixgbe_hw *hw, * * Write a 16 bit word to the EEPROM using the EEWR register. **/ -static s32 ixgbe_write_eewr_X540(struct ixgbe_hw *hw, u16 offset, u16 data) +static int ixgbe_write_eewr_X540(struct ixgbe_hw *hw, u16 offset, u16 data) { - s32 status; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM)) return -EBUSY; @@ -281,10 +281,10 @@ static s32 ixgbe_write_eewr_X540(struct ixgbe_hw *hw, u16 offset, u16 data) * * Write a 16 bit word(s) to the EEPROM using the EEWR register. **/ -static s32 ixgbe_write_eewr_buffer_X540(struct ixgbe_hw *hw, +static int ixgbe_write_eewr_buffer_X540(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { - s32 status; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM)) return -EBUSY; @@ -303,7 +303,7 @@ static s32 ixgbe_write_eewr_buffer_X540(struct ixgbe_hw *hw, * * @hw: pointer to hardware structure **/ -static s32 ixgbe_calc_eeprom_checksum_X540(struct ixgbe_hw *hw) +static int ixgbe_calc_eeprom_checksum_X540(struct ixgbe_hw *hw) { u16 i; u16 j; @@ -368,7 +368,7 @@ static s32 ixgbe_calc_eeprom_checksum_X540(struct ixgbe_hw *hw) checksum = (u16)IXGBE_EEPROM_SUM - checksum; - return (s32)checksum; + return (int)checksum; } /** @@ -379,12 +379,12 @@ static s32 ixgbe_calc_eeprom_checksum_X540(struct ixgbe_hw *hw) * Performs checksum calculation and validates the EEPROM checksum. If the * caller does not need checksum_val, the value can be NULL. **/ -static s32 ixgbe_validate_eeprom_checksum_X540(struct ixgbe_hw *hw, +static int ixgbe_validate_eeprom_checksum_X540(struct ixgbe_hw *hw, u16 *checksum_val) { - s32 status; - u16 checksum; u16 read_checksum = 0; + u16 checksum; + int status; /* Read the first word from the EEPROM. If this times out or fails, do * not continue or we could be in for a very long wait while every @@ -439,10 +439,10 @@ out: * checksum and updates the EEPROM and instructs the hardware to update * the flash. **/ -static s32 ixgbe_update_eeprom_checksum_X540(struct ixgbe_hw *hw) +static int ixgbe_update_eeprom_checksum_X540(struct ixgbe_hw *hw) { - s32 status; u16 checksum; + int status; /* Read the first word from the EEPROM. If this times out or fails, do * not continue or we could be in for a very long wait while every @@ -484,10 +484,10 @@ out: * Set FLUP (bit 23) of the EEC register to instruct Hardware to copy * EEPROM from shadow RAM to the flash device. **/ -static s32 ixgbe_update_flash_X540(struct ixgbe_hw *hw) +static int ixgbe_update_flash_X540(struct ixgbe_hw *hw) { + int status; u32 flup; - s32 status; status = ixgbe_poll_flash_update_done_X540(hw); if (status == -EIO) { @@ -529,7 +529,7 @@ static s32 ixgbe_update_flash_X540(struct ixgbe_hw *hw) * Polls the FLUDONE (bit 26) of the EEC Register to determine when the * flash update is done. **/ -static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw) +static int ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw) { u32 i; u32 reg; @@ -551,7 +551,7 @@ static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw) * Acquires the SWFW semaphore thought the SW_FW_SYNC register for * the specified function (CSR, PHY0, PHY1, NVM, Flash) **/ -s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) +int ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) { u32 swmask = mask & IXGBE_GSSR_NVM_PHY_MASK; u32 swi2c_mask = mask & IXGBE_GSSR_I2C_MASK; @@ -660,7 +660,7 @@ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) * * Sets the hardware semaphores so SW/FW can gain control of shared resources */ -static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw) +static int ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw) { u32 timeout = 2000; u32 i; @@ -760,7 +760,7 @@ void ixgbe_init_swfw_sync_X540(struct ixgbe_hw *hw) * Devices that implement the version 2 interface: * X540 **/ -s32 ixgbe_blink_led_start_X540(struct ixgbe_hw *hw, u32 index) +int ixgbe_blink_led_start_X540(struct ixgbe_hw *hw, u32 index) { u32 macc_reg; u32 ledctl_reg; @@ -798,7 +798,7 @@ s32 ixgbe_blink_led_start_X540(struct ixgbe_hw *hw, u32 index) * Devices that implement the version 2 interface: * X540 **/ -s32 ixgbe_blink_led_stop_X540(struct ixgbe_hw *hw, u32 index) +int ixgbe_blink_led_stop_X540(struct ixgbe_hw *hw, u32 index) { u32 macc_reg; u32 ledctl_reg; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h index e246c0d2a427..b69a680d3ab5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h @@ -3,17 +3,17 @@ #include "ixgbe_type.h" -s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw); -s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, ixgbe_link_speed speed, +int ixgbe_get_invariants_X540(struct ixgbe_hw *hw); +int ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); -s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw); -s32 ixgbe_start_hw_X540(struct ixgbe_hw *hw); +int ixgbe_reset_hw_X540(struct ixgbe_hw *hw); +int ixgbe_start_hw_X540(struct ixgbe_hw *hw); enum ixgbe_media_type ixgbe_get_media_type_X540(struct ixgbe_hw *hw); -s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, ixgbe_link_speed speed, +int ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); -s32 ixgbe_blink_led_start_X540(struct ixgbe_hw *hw, u32 index); -s32 ixgbe_blink_led_stop_X540(struct ixgbe_hw *hw, u32 index); -s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask); +int ixgbe_blink_led_start_X540(struct ixgbe_hw *hw, u32 index); +int ixgbe_blink_led_stop_X540(struct ixgbe_hw *hw, u32 index); +int ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask); void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask); void ixgbe_init_swfw_sync_X540(struct ixgbe_hw *hw); -s32 ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw); +int ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 6208923e29a2..2decb0710b6e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -6,13 +6,13 @@ #include "ixgbe_common.h" #include "ixgbe_phy.h" -static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *, ixgbe_link_speed); -static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *); +static int ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *, ixgbe_link_speed); +static int ixgbe_setup_fc_x550em(struct ixgbe_hw *); static void ixgbe_fc_autoneg_fiber_x550em_a(struct ixgbe_hw *); static void ixgbe_fc_autoneg_backplane_x550em_a(struct ixgbe_hw *); -static s32 ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *); +static int ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *); -static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw) +static int ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; struct ixgbe_phy_info *phy = &hw->phy; @@ -29,7 +29,7 @@ static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw) return 0; } -static s32 ixgbe_get_invariants_X550_x_fw(struct ixgbe_hw *hw) +static int ixgbe_get_invariants_X550_x_fw(struct ixgbe_hw *hw) { struct ixgbe_phy_info *phy = &hw->phy; @@ -41,7 +41,7 @@ static s32 ixgbe_get_invariants_X550_x_fw(struct ixgbe_hw *hw) return 0; } -static s32 ixgbe_get_invariants_X550_a(struct ixgbe_hw *hw) +static int ixgbe_get_invariants_X550_a(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; struct ixgbe_phy_info *phy = &hw->phy; @@ -55,7 +55,7 @@ static s32 ixgbe_get_invariants_X550_a(struct ixgbe_hw *hw) return 0; } -static s32 ixgbe_get_invariants_X550_a_fw(struct ixgbe_hw *hw) +static int ixgbe_get_invariants_X550_a_fw(struct ixgbe_hw *hw) { struct ixgbe_phy_info *phy = &hw->phy; @@ -91,7 +91,7 @@ static void ixgbe_setup_mux_ctl(struct ixgbe_hw *hw) * * Returns status code */ -static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value) +static int ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value) { return hw->link.ops.read_link_unlocked(hw, hw->link.addr, reg, value); } @@ -104,7 +104,7 @@ static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value) * * Returns status code */ -static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value) +static int ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value) { return hw->link.ops.write_link_unlocked(hw, hw->link.addr, reg, value); } @@ -117,9 +117,9 @@ static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value) * * Returns status code */ -static s32 ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value) +static int ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value) { - s32 status; + int status; status = ixgbe_read_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, value); if (status) @@ -135,9 +135,9 @@ static s32 ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value) * * Returns status code */ -static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value) +static int ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value) { - s32 status; + int status; status = ixgbe_write_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, value); @@ -153,9 +153,9 @@ static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value) * This function assumes that the caller has acquired the proper semaphore. * Returns error code */ -static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw) +static int ixgbe_reset_cs4227(struct ixgbe_hw *hw) { - s32 status; + int status; u32 retry; u16 value; u8 reg; @@ -225,7 +225,7 @@ static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw) static void ixgbe_check_cs4227(struct ixgbe_hw *hw) { u32 swfw_mask = hw->phy.phy_semaphore_mask; - s32 status; + int status; u16 value; u8 retry; @@ -292,7 +292,7 @@ out: * * Returns error code */ -static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) +static int ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) { switch (hw->device_id) { case IXGBE_DEV_ID_X550EM_A_SFP: @@ -347,13 +347,13 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) return 0; } -static s32 ixgbe_read_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_read_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data) { return -EOPNOTSUPP; } -static s32 ixgbe_write_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_write_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data) { return -EOPNOTSUPP; @@ -368,7 +368,7 @@ static s32 ixgbe_write_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr, * * Returns an error code on error. **/ -static s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, +static int ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 *val) { return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, true); @@ -383,7 +383,7 @@ static s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, * * Returns an error code on error. **/ -static s32 +static int ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 *val) { @@ -399,7 +399,7 @@ ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, * * Returns an error code on error. **/ -static s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, +static int ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 val) { return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, true); @@ -414,7 +414,7 @@ static s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, * * Returns an error code on error. **/ -static s32 +static int ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 val) { @@ -427,7 +427,7 @@ ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, * @activity: activity to perform * @data: Pointer to 4 32-bit words of data */ -s32 ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity, +int ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity, u32 (*data)[FW_PHY_ACT_DATA_COUNT]) { union { @@ -435,7 +435,7 @@ s32 ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity, struct ixgbe_hic_phy_activity_resp rsp; } hic; u16 retries = FW_PHY_ACT_RETRIES; - s32 rc; + int rc; u32 i; do { @@ -484,12 +484,12 @@ static const struct { * * Returns error code */ -static s32 ixgbe_get_phy_id_fw(struct ixgbe_hw *hw) +static int ixgbe_get_phy_id_fw(struct ixgbe_hw *hw) { u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 }; u16 phy_speeds; u16 phy_id_lo; - s32 rc; + int rc; u16 i; if (hw->phy.id) @@ -526,7 +526,7 @@ static s32 ixgbe_get_phy_id_fw(struct ixgbe_hw *hw) * * Returns error code */ -static s32 ixgbe_identify_phy_fw(struct ixgbe_hw *hw) +static int ixgbe_identify_phy_fw(struct ixgbe_hw *hw) { if (hw->bus.lan_id) hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY1_SM; @@ -545,7 +545,7 @@ static s32 ixgbe_identify_phy_fw(struct ixgbe_hw *hw) * * Returns error code */ -static s32 ixgbe_shutdown_fw_phy(struct ixgbe_hw *hw) +static int ixgbe_shutdown_fw_phy(struct ixgbe_hw *hw) { u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 }; @@ -557,10 +557,10 @@ static s32 ixgbe_shutdown_fw_phy(struct ixgbe_hw *hw) * ixgbe_setup_fw_link - Setup firmware-controlled PHYs * @hw: pointer to hardware structure */ -static s32 ixgbe_setup_fw_link(struct ixgbe_hw *hw) +static int ixgbe_setup_fw_link(struct ixgbe_hw *hw) { u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 }; - s32 rc; + int rc; u16 i; if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw)) @@ -613,7 +613,7 @@ static s32 ixgbe_setup_fw_link(struct ixgbe_hw *hw) * * Called at init time to set up flow control. */ -static s32 ixgbe_fc_autoneg_fw(struct ixgbe_hw *hw) +static int ixgbe_fc_autoneg_fw(struct ixgbe_hw *hw) { if (hw->fc.requested_mode == ixgbe_fc_default) hw->fc.requested_mode = ixgbe_fc_full; @@ -627,7 +627,7 @@ static s32 ixgbe_fc_autoneg_fw(struct ixgbe_hw *hw) * Initializes the EEPROM parameters ixgbe_eeprom_info within the * ixgbe_hw struct in order to set up EEPROM access. **/ -static s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw) +static int ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw) { struct ixgbe_eeprom_info *eeprom = &hw->eeprom; @@ -659,7 +659,7 @@ static s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw) * * Note: ctrl can be NULL if the IOSF control register value is not needed */ -static s32 ixgbe_iosf_wait(struct ixgbe_hw *hw, u32 *ctrl) +static int ixgbe_iosf_wait(struct ixgbe_hw *hw, u32 *ctrl) { u32 i, command; @@ -690,12 +690,12 @@ static s32 ixgbe_iosf_wait(struct ixgbe_hw *hw, u32 *ctrl) * @device_type: 3 bit device type * @phy_data: Pointer to read data from the register **/ -static s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u32 *data) { u32 gssr = IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_PHY0_SM; u32 command, error; - s32 ret; + int ret; ret = hw->mac.ops.acquire_swfw_sync(hw, gssr); if (ret) @@ -716,7 +716,8 @@ static s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, if ((command & IXGBE_SB_IOSF_CTRL_RESP_STAT_MASK) != 0) { error = FIELD_GET(IXGBE_SB_IOSF_CTRL_CMPL_ERR_MASK, command); hw_dbg(hw, "Failed to read, error %x\n", error); - return -EIO; + ret = -EIO; + goto out; } if (!ret) @@ -731,10 +732,10 @@ out: * ixgbe_get_phy_token - Get the token for shared PHY access * @hw: Pointer to hardware structure */ -static s32 ixgbe_get_phy_token(struct ixgbe_hw *hw) +static int ixgbe_get_phy_token(struct ixgbe_hw *hw) { struct ixgbe_hic_phy_token_req token_cmd; - s32 status; + int status; token_cmd.hdr.cmd = FW_PHY_TOKEN_REQ_CMD; token_cmd.hdr.buf_len = FW_PHY_TOKEN_REQ_LEN; @@ -760,10 +761,10 @@ static s32 ixgbe_get_phy_token(struct ixgbe_hw *hw) * ixgbe_put_phy_token - Put the token for shared PHY access * @hw: Pointer to hardware structure */ -static s32 ixgbe_put_phy_token(struct ixgbe_hw *hw) +static int ixgbe_put_phy_token(struct ixgbe_hw *hw) { struct ixgbe_hic_phy_token_req token_cmd; - s32 status; + int status; token_cmd.hdr.cmd = FW_PHY_TOKEN_REQ_CMD; token_cmd.hdr.buf_len = FW_PHY_TOKEN_REQ_LEN; @@ -789,7 +790,7 @@ static s32 ixgbe_put_phy_token(struct ixgbe_hw *hw) * @device_type: 3 bit device type * @data: Data to write to the register **/ -static s32 ixgbe_write_iosf_sb_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_write_iosf_sb_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, __always_unused u32 device_type, u32 data) { @@ -815,7 +816,7 @@ static s32 ixgbe_write_iosf_sb_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, * @device_type: 3 bit device type * @data: Pointer to read data from the register **/ -static s32 ixgbe_read_iosf_sb_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_read_iosf_sb_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, __always_unused u32 device_type, u32 *data) { @@ -823,7 +824,7 @@ static s32 ixgbe_read_iosf_sb_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, struct ixgbe_hic_internal_phy_req cmd; struct ixgbe_hic_internal_phy_resp rsp; } hic; - s32 status; + int status; memset(&hic, 0, sizeof(hic)); hic.cmd.hdr.cmd = FW_INT_PHY_REQ_CMD; @@ -850,14 +851,14 @@ static s32 ixgbe_read_iosf_sb_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, * * Reads a 16 bit word(s) from the EEPROM using the hostif. **/ -static s32 ixgbe_read_ee_hostif_buffer_X550(struct ixgbe_hw *hw, +static int ixgbe_read_ee_hostif_buffer_X550(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { const u32 mask = IXGBE_GSSR_SW_MNG_SM | IXGBE_GSSR_EEP_SM; struct ixgbe_hic_read_shadow_ram buffer; u32 current_word = 0; u16 words_to_read; - s32 status; + int status; u32 i; /* Take semaphore for the entire operation. */ @@ -922,14 +923,14 @@ out: * * Returns error status for any failure **/ -static s32 ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr, +static int ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr, u16 size, u16 *csum, u16 *buffer, u32 buffer_size) { - u16 buf[256]; - s32 status; u16 length, bufsz, i, start; u16 *local_buffer; + u16 buf[256]; + int status; bufsz = ARRAY_SIZE(buf); @@ -990,14 +991,14 @@ static s32 ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr, * * Returns a negative error code on error, or the 16-bit checksum **/ -static s32 ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, +static int ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, u32 buffer_size) { u16 eeprom_ptrs[IXGBE_EEPROM_LAST_WORD + 1]; + u16 pointer, i, size; u16 *local_buffer; - s32 status; u16 checksum = 0; - u16 pointer, i, size; + int status; hw->eeprom.ops.init_params(hw); @@ -1059,7 +1060,7 @@ static s32 ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, checksum = (u16)IXGBE_EEPROM_SUM - checksum; - return (s32)checksum; + return (int)checksum; } /** ixgbe_calc_eeprom_checksum_X550 - Calculates and returns the checksum @@ -1067,7 +1068,7 @@ static s32 ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, * * Returns a negative error code on error, or the 16-bit checksum **/ -static s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw) +static int ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw) { return ixgbe_calc_checksum_X550(hw, NULL, 0); } @@ -1079,11 +1080,11 @@ static s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw) * * Reads a 16 bit word from the EEPROM using the hostif. **/ -static s32 ixgbe_read_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) +static int ixgbe_read_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) { const u32 mask = IXGBE_GSSR_SW_MNG_SM | IXGBE_GSSR_EEP_SM; struct ixgbe_hic_read_shadow_ram buffer; - s32 status; + int status; buffer.hdr.req.cmd = FW_READ_SHADOW_RAM_CMD; buffer.hdr.req.buf_lenh = 0; @@ -1117,12 +1118,12 @@ static s32 ixgbe_read_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) * Performs checksum calculation and validates the EEPROM checksum. If the * caller does not need checksum_val, the value can be NULL. **/ -static s32 ixgbe_validate_eeprom_checksum_X550(struct ixgbe_hw *hw, +static int ixgbe_validate_eeprom_checksum_X550(struct ixgbe_hw *hw, u16 *checksum_val) { - s32 status; - u16 checksum; u16 read_checksum = 0; + u16 checksum; + int status; /* Read the first word from the EEPROM. If this times out or fails, do * not continue or we could be in for a very long wait while every @@ -1167,11 +1168,11 @@ static s32 ixgbe_validate_eeprom_checksum_X550(struct ixgbe_hw *hw, * * Write a 16 bit word to the EEPROM using the hostif. **/ -static s32 ixgbe_write_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, +static int ixgbe_write_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, u16 data) { - s32 status; struct ixgbe_hic_write_shadow_ram buffer; + int status; buffer.hdr.req.cmd = FW_WRITE_SHADOW_RAM_CMD; buffer.hdr.req.buf_lenh = 0; @@ -1195,9 +1196,9 @@ static s32 ixgbe_write_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, * * Write a 16 bit word to the EEPROM using the hostif. **/ -static s32 ixgbe_write_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 data) +static int ixgbe_write_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 data) { - s32 status = 0; + int status = 0; if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM) == 0) { status = ixgbe_write_ee_hostif_data_X550(hw, offset, data); @@ -1215,10 +1216,10 @@ static s32 ixgbe_write_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 data) * * Issue a shadow RAM dump to FW to copy EEPROM from shadow RAM to the flash. **/ -static s32 ixgbe_update_flash_X550(struct ixgbe_hw *hw) +static int ixgbe_update_flash_X550(struct ixgbe_hw *hw) { - s32 status = 0; union ixgbe_hic_hdr2 buffer; + int status = 0; buffer.req.cmd = FW_SHADOW_RAM_DUMP_CMD; buffer.req.buf_lenh = 0; @@ -1237,7 +1238,7 @@ static s32 ixgbe_update_flash_X550(struct ixgbe_hw *hw) * Sets bus link width and speed to unknown because X550em is * not a PCI device. **/ -static s32 ixgbe_get_bus_info_X550em(struct ixgbe_hw *hw) +static int ixgbe_get_bus_info_X550em(struct ixgbe_hw *hw) { hw->bus.type = ixgbe_bus_type_internal; hw->bus.width = ixgbe_bus_width_unknown; @@ -1268,9 +1269,9 @@ static bool ixgbe_fw_recovery_mode_X550(struct ixgbe_hw *hw) **/ static void ixgbe_disable_rx_x550(struct ixgbe_hw *hw) { - u32 rxctrl, pfdtxgswc; - s32 status; struct ixgbe_hic_disable_rxen fw_cmd; + u32 rxctrl, pfdtxgswc; + int status; rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL); if (rxctrl & IXGBE_RXCTRL_RXEN) { @@ -1310,10 +1311,10 @@ static void ixgbe_disable_rx_x550(struct ixgbe_hw *hw) * checksum and updates the EEPROM and instructs the hardware to update * the flash. **/ -static s32 ixgbe_update_eeprom_checksum_X550(struct ixgbe_hw *hw) +static int ixgbe_update_eeprom_checksum_X550(struct ixgbe_hw *hw) { - s32 status; u16 checksum = 0; + int status; /* Read the first word from the EEPROM. If this times out or fails, do * not continue or we could be in for a very long wait while every @@ -1350,11 +1351,11 @@ static s32 ixgbe_update_eeprom_checksum_X550(struct ixgbe_hw *hw) * * Write a 16 bit word(s) to the EEPROM using the hostif. **/ -static s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw, +static int ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data) { - s32 status = 0; + int status = 0; u32 i = 0; /* Take semaphore for the entire operation. */ @@ -1386,12 +1387,12 @@ static s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw, * @device_type: 3 bit device type * @data: Data to write to the register **/ -static s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u32 data) { u32 gssr = IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_PHY0_SM; u32 command, error; - s32 ret; + int ret; ret = hw->mac.ops.acquire_swfw_sync(hw, gssr); if (ret) @@ -1429,10 +1430,10 @@ out: * * iXfI configuration needed for ixgbe_mac_X550EM_x devices. **/ -static s32 ixgbe_setup_ixfi_x550em_x(struct ixgbe_hw *hw) +static int ixgbe_setup_ixfi_x550em_x(struct ixgbe_hw *hw) { - s32 status; u32 reg_val; + int status; /* Disable training protocol FSM. */ status = ixgbe_read_iosf_sb_reg_x550(hw, @@ -1501,10 +1502,10 @@ static s32 ixgbe_setup_ixfi_x550em_x(struct ixgbe_hw *hw) * internal PHY * @hw: pointer to hardware structure **/ -static s32 ixgbe_restart_an_internal_phy_x550em(struct ixgbe_hw *hw) +static int ixgbe_restart_an_internal_phy_x550em(struct ixgbe_hw *hw) { - s32 status; u32 link_ctrl; + int status; /* Restart auto-negotiation. */ status = hw->mac.ops.read_iosf_sb_reg(hw, @@ -1550,11 +1551,11 @@ static s32 ixgbe_restart_an_internal_phy_x550em(struct ixgbe_hw *hw) * Configures the integrated KR PHY to use iXFI mode. Used to connect an * internal and external PHY at a specific speed, without autonegotiation. **/ -static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) +static int ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) { struct ixgbe_mac_info *mac = &hw->mac; - s32 status; u32 reg_val; + int status; /* iXFI is only supported with X552 */ if (mac->type != ixgbe_mac_X550EM_x) @@ -1607,7 +1608,7 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) * @hw: pointer to hardware structure * @linear: true if SFP module is linear */ -static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear) +static int ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear) { switch (hw->phy.sfp_type) { case ixgbe_sfp_type_not_present: @@ -1644,14 +1645,14 @@ static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear) * * Configures the extern PHY and the integrated KR PHY for SFP support. */ -static s32 +static int ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, ixgbe_link_speed speed, __always_unused bool autoneg_wait_to_complete) { - s32 status; - u16 reg_slice, reg_val; bool setup_linear = false; + u16 reg_slice, reg_val; + int status; /* Check if SFP module is supported and linear */ status = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); @@ -1690,11 +1691,11 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, * Configures the integrated PHY for native SFI mode. Used to connect the * internal PHY directly to an SFP cage, without autonegotiation. **/ -static s32 ixgbe_setup_sfi_x550a(struct ixgbe_hw *hw, ixgbe_link_speed *speed) +static int ixgbe_setup_sfi_x550a(struct ixgbe_hw *hw, ixgbe_link_speed *speed) { struct ixgbe_mac_info *mac = &hw->mac; - s32 status; u32 reg_val; + int status; /* Disable all AN and force speed to 10G Serial. */ status = mac->ops.read_iosf_sb_reg(hw, @@ -1789,13 +1790,13 @@ static s32 ixgbe_setup_sfi_x550a(struct ixgbe_hw *hw, ixgbe_link_speed *speed) * * Configure the integrated PHY for native SFP support. */ -static s32 +static int ixgbe_setup_mac_link_sfp_n(struct ixgbe_hw *hw, ixgbe_link_speed speed, __always_unused bool autoneg_wait_to_complete) { bool setup_linear = false; u32 reg_phy_int; - s32 ret_val; + int ret_val; /* Check if SFP module is supported and linear */ ret_val = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); @@ -1838,14 +1839,14 @@ ixgbe_setup_mac_link_sfp_n(struct ixgbe_hw *hw, ixgbe_link_speed speed, * * Configure the integrated PHY for SFP support. */ -static s32 +static int ixgbe_setup_mac_link_sfp_x550a(struct ixgbe_hw *hw, ixgbe_link_speed speed, __always_unused bool autoneg_wait_to_complete) { u32 reg_slice, slice_offset; bool setup_linear = false; u16 reg_phy_ext; - s32 ret_val; + int ret_val; /* Check if SFP module is supported and linear */ ret_val = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); @@ -1917,12 +1918,12 @@ ixgbe_setup_mac_link_sfp_x550a(struct ixgbe_hw *hw, ixgbe_link_speed speed, * * Returns error status for any failure **/ -static s32 ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw, +static int ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait) { - s32 status; ixgbe_link_speed force_speed; + int status; /* Setup internal/external PHY link speed to iXFI (10G), unless * only 1G is auto advertised then setup KX link. @@ -1953,7 +1954,7 @@ static s32 ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw, * * Check that both the MAC and X557 external PHY have link. **/ -static s32 ixgbe_check_link_t_X550em(struct ixgbe_hw *hw, +static int ixgbe_check_link_t_X550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *link_up, bool link_up_wait_to_complete) @@ -1997,13 +1998,13 @@ static s32 ixgbe_check_link_t_X550em(struct ixgbe_hw *hw, * @speed: unused * @autoneg_wait_to_complete: unused */ -static s32 +static int ixgbe_setup_sgmii(struct ixgbe_hw *hw, __always_unused ixgbe_link_speed speed, __always_unused bool autoneg_wait_to_complete) { struct ixgbe_mac_info *mac = &hw->mac; u32 lval, sval, flx_val; - s32 rc; + int rc; rc = mac->ops.read_iosf_sb_reg(hw, IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), @@ -2070,12 +2071,12 @@ ixgbe_setup_sgmii(struct ixgbe_hw *hw, __always_unused ixgbe_link_speed speed, * @speed: the link speed to force * @autoneg_wait: true when waiting for completion is needed */ -static s32 ixgbe_setup_sgmii_fw(struct ixgbe_hw *hw, ixgbe_link_speed speed, +static int ixgbe_setup_sgmii_fw(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait) { struct ixgbe_mac_info *mac = &hw->mac; u32 lval, sval, flx_val; - s32 rc; + int rc; rc = mac->ops.read_iosf_sb_reg(hw, IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), @@ -2147,7 +2148,7 @@ static void ixgbe_fc_autoneg_sgmii_x550em_a(struct ixgbe_hw *hw) { u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 }; ixgbe_link_speed speed; - s32 status = -EIO; + int status = -EIO; bool link_up; /* AN should have completed when the cable was plugged in. @@ -2275,10 +2276,10 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) /** ixgbe_setup_sfp_modules_X550em - Setup SFP module * @hw: pointer to hardware structure */ -static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) +static int ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) { - s32 status; bool linear; + int status; /* Check if SFP module is supported */ status = ixgbe_supported_sfp_modules_X550em(hw, &linear); @@ -2296,7 +2297,7 @@ static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) * @speed: pointer to link speed * @autoneg: true when autoneg or autotry is enabled **/ -static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, +static int ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed, bool *autoneg) { @@ -2374,7 +2375,7 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, * Determime if external Base T PHY interrupt cause is high temperature * failure alarm or link status change. **/ -static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc, +static int ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc, bool *is_overtemp) { u32 status; @@ -2462,7 +2463,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc, * * Returns PHY access status **/ -static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) +static int ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) { bool lsc, overtemp; u32 status; @@ -2554,7 +2555,7 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) * failure alarm then return error, else if link status change * then setup internal/external PHY link **/ -static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw, +static int ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *is_overtemp) { struct ixgbe_phy_info *phy = &hw->phy; @@ -2578,11 +2579,11 @@ static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw, * * Configures the integrated KR PHY. **/ -static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *hw, +static int ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *hw, ixgbe_link_speed speed) { - s32 status; u32 reg_val; + int status; status = hw->mac.ops.read_iosf_sb_reg(hw, IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), @@ -2633,7 +2634,7 @@ static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *hw, * ixgbe_setup_kr_x550em - Configure the KR PHY * @hw: pointer to hardware structure **/ -static s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) +static int ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) { /* leave link alone for 2.5G */ if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_2_5GB_FULL) @@ -2651,7 +2652,7 @@ static s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) * * Returns error code if unable to get link status. **/ -static s32 ixgbe_ext_phy_t_x550em_get_link(struct ixgbe_hw *hw, bool *link_up) +static int ixgbe_ext_phy_t_x550em_get_link(struct ixgbe_hw *hw, bool *link_up) { u32 ret; u16 autoneg_status; @@ -2685,7 +2686,7 @@ static s32 ixgbe_ext_phy_t_x550em_get_link(struct ixgbe_hw *hw, bool *link_up) * A return of a non-zero value indicates an error, and the base driver should * not report link up. **/ -static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw) +static int ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw) { ixgbe_link_speed force_speed; bool link_up; @@ -2745,9 +2746,9 @@ static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw) /** ixgbe_reset_phy_t_X550em - Performs X557 PHY reset and enables LASI * @hw: pointer to hardware structure **/ -static s32 ixgbe_reset_phy_t_X550em(struct ixgbe_hw *hw) +static int ixgbe_reset_phy_t_X550em(struct ixgbe_hw *hw) { - s32 status; + int status; status = ixgbe_reset_phy_generic(hw); @@ -2763,7 +2764,7 @@ static s32 ixgbe_reset_phy_t_X550em(struct ixgbe_hw *hw) * @hw: pointer to hardware structure * @led_idx: led number to turn on **/ -static s32 ixgbe_led_on_t_x550em(struct ixgbe_hw *hw, u32 led_idx) +static int ixgbe_led_on_t_x550em(struct ixgbe_hw *hw, u32 led_idx) { u16 phy_data; @@ -2785,7 +2786,7 @@ static s32 ixgbe_led_on_t_x550em(struct ixgbe_hw *hw, u32 led_idx) * @hw: pointer to hardware structure * @led_idx: led number to turn off **/ -static s32 ixgbe_led_off_t_x550em(struct ixgbe_hw *hw, u32 led_idx) +static int ixgbe_led_off_t_x550em(struct ixgbe_hw *hw, u32 led_idx) { u16 phy_data; @@ -2818,12 +2819,12 @@ static s32 ixgbe_led_off_t_x550em(struct ixgbe_hw *hw, u32 led_idx) * semaphore, -EIO when command fails or -ENIVAL when incorrect * params passed. **/ -static s32 ixgbe_set_fw_drv_ver_x550(struct ixgbe_hw *hw, u8 maj, u8 min, +static int ixgbe_set_fw_drv_ver_x550(struct ixgbe_hw *hw, u8 maj, u8 min, u8 build, u8 sub, u16 len, const char *driver_ver) { struct ixgbe_hic_drv_info2 fw_cmd; - s32 ret_val; + int ret_val; int i; if (!len || !driver_ver || (len > sizeof(fw_cmd.driver_string))) @@ -2865,12 +2866,12 @@ static s32 ixgbe_set_fw_drv_ver_x550(struct ixgbe_hw *hw, u8 maj, u8 min, * * Determine lowest common link speed with link partner. **/ -static s32 ixgbe_get_lcd_t_x550em(struct ixgbe_hw *hw, +static int ixgbe_get_lcd_t_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *lcd_speed) { - u16 an_lp_status; - s32 status; u16 word = hw->eeprom.ctrl_word_3; + u16 an_lp_status; + int status; *lcd_speed = IXGBE_LINK_SPEED_UNKNOWN; @@ -2883,28 +2884,28 @@ static s32 ixgbe_get_lcd_t_x550em(struct ixgbe_hw *hw, /* If link partner advertised 1G, return 1G */ if (an_lp_status & IXGBE_AUTO_NEG_LP_1000BASE_CAP) { *lcd_speed = IXGBE_LINK_SPEED_1GB_FULL; - return status; + return 0; } /* If 10G disabled for LPLU via NVM D10GMP, then return no valid LCD */ if ((hw->bus.lan_id && (word & NVM_INIT_CTRL_3_D10GMP_PORT1)) || (word & NVM_INIT_CTRL_3_D10GMP_PORT0)) - return status; + return 0; /* Link partner not capable of lower speeds, return 10G */ *lcd_speed = IXGBE_LINK_SPEED_10GB_FULL; - return status; + return 0; } /** * ixgbe_setup_fc_x550em - Set up flow control * @hw: pointer to hardware structure */ -static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *hw) +static int ixgbe_setup_fc_x550em(struct ixgbe_hw *hw) { bool pause, asm_dir; u32 reg_val; - s32 rc = 0; + int rc = 0; /* Validate the requested mode */ if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) { @@ -2989,7 +2990,7 @@ static void ixgbe_fc_autoneg_backplane_x550em_a(struct ixgbe_hw *hw) { u32 link_s1, lp_an_page_low, an_cntl_1; ixgbe_link_speed speed; - s32 status = -EIO; + int status = -EIO; bool link_up; /* AN should have completed when the cable was plugged in. @@ -3072,13 +3073,13 @@ static void ixgbe_fc_autoneg_fiber_x550em_a(struct ixgbe_hw *hw) * (from D0 to non-D0). Link is required to enter LPLU so avoid resetting * the X557 PHY immediately prior to entering LPLU. **/ -static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) +static int ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) { u16 an_10g_cntl_reg, autoneg_reg, speed; - s32 status; ixgbe_link_speed lcd_speed; u32 save_autoneg; bool link_up; + int status; /* If blocked by MNG FW, then don't restart AN */ if (ixgbe_check_reset_blocked(hw)) @@ -3129,7 +3130,7 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) (lcd_speed == IXGBE_LINK_SPEED_1GB_FULL)) || ((speed == IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_10GB) && (lcd_speed == IXGBE_LINK_SPEED_10GB_FULL))) - return status; + return 0; /* Clear AN completed indication */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM, @@ -3166,10 +3167,10 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) * ixgbe_reset_phy_fw - Reset firmware-controlled PHYs * @hw: pointer to hardware structure */ -static s32 ixgbe_reset_phy_fw(struct ixgbe_hw *hw) +static int ixgbe_reset_phy_fw(struct ixgbe_hw *hw) { u32 store[FW_PHY_ACT_DATA_COUNT] = { 0 }; - s32 rc; + int rc; if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw)) return 0; @@ -3195,7 +3196,7 @@ static s32 ixgbe_reset_phy_fw(struct ixgbe_hw *hw) static bool ixgbe_check_overtemp_fw(struct ixgbe_hw *hw) { u32 store[FW_PHY_ACT_DATA_COUNT] = { 0 }; - s32 rc; + int rc; rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_LINK_INFO, &store); if (rc) @@ -3238,10 +3239,10 @@ static void ixgbe_read_mng_if_sel_x550em(struct ixgbe_hw *hw) * set during init_shared_code because the PHY/SFP type was * not known. Perform the SFP init if necessary. **/ -static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) +static int ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) { struct ixgbe_phy_info *phy = &hw->phy; - s32 ret_val; + int ret_val; hw->mac.ops.set_lan_id(hw); @@ -3366,9 +3367,9 @@ static enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw) /** ixgbe_init_ext_t_x550em - Start (unstall) the external Base T PHY. ** @hw: pointer to hardware structure **/ -static s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) +static int ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) { - s32 status; + int status; u16 reg; status = hw->phy.ops.read_reg(hw, @@ -3440,14 +3441,14 @@ static void ixgbe_set_mdio_speed(struct ixgbe_hw *hw) ** and clears all interrupts, perform a PHY reset, and perform a link (MAC) ** reset. **/ -static s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw) +static int ixgbe_reset_hw_X550em(struct ixgbe_hw *hw) { + u32 swfw_mask = hw->phy.phy_semaphore_mask; ixgbe_link_speed link_speed; - s32 status; + bool link_up = false; u32 ctrl = 0; + int status; u32 i; - bool link_up = false; - u32 swfw_mask = hw->phy.phy_semaphore_mask; /* Call adapter stop to disable Tx/Rx and clear interrupts */ status = hw->mac.ops.stop_adapter(hw); @@ -3608,10 +3609,10 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw, * * Called at init time to set up flow control. **/ -static s32 ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *hw) +static int ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *hw) { - s32 status = 0; u32 an_cntl = 0; + int status = 0; /* Validate the requested mode */ if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) { @@ -3713,9 +3714,9 @@ static void ixgbe_set_mux(struct ixgbe_hw *hw, u8 state) * * Acquires the SWFW semaphore and sets the I2C MUX */ -static s32 ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask) +static int ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask) { - s32 status; + int status; status = ixgbe_acquire_swfw_sync_X540(hw, mask); if (status) @@ -3749,11 +3750,11 @@ static void ixgbe_release_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask) * * Acquires the SWFW semaphore and get the shared PHY token as needed */ -static s32 ixgbe_acquire_swfw_sync_x550em_a(struct ixgbe_hw *hw, u32 mask) +static int ixgbe_acquire_swfw_sync_x550em_a(struct ixgbe_hw *hw, u32 mask) { u32 hmask = mask & ~IXGBE_GSSR_TOKEN_SM; int retries = FW_PHY_TOKEN_RETRIES; - s32 status; + int status; while (--retries) { status = 0; @@ -3806,11 +3807,11 @@ static void ixgbe_release_swfw_sync_x550em_a(struct ixgbe_hw *hw, u32 mask) * Token. The PHY Token is needed since the MDIO is shared between to MAC * instances. */ -static s32 ixgbe_read_phy_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_read_phy_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data) { u32 mask = hw->phy.phy_semaphore_mask | IXGBE_GSSR_TOKEN_SM; - s32 status; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, mask)) return -EBUSY; @@ -3832,11 +3833,11 @@ static s32 ixgbe_read_phy_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, * Writes a value to specified PHY register using the SWFW lock and PHY Token. * The PHY Token is needed since the MDIO is shared between to MAC instances. */ -static s32 ixgbe_write_phy_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, +static int ixgbe_write_phy_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data) { u32 mask = hw->phy.phy_semaphore_mask | IXGBE_GSSR_TOKEN_SM; - s32 status; + int status; if (hw->mac.ops.acquire_swfw_sync(hw, mask)) return -EBUSY; diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 884d64114bff..837295fecd17 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -180,6 +180,7 @@ config SKY2_DEBUG source "drivers/net/ethernet/marvell/octeontx2/Kconfig" source "drivers/net/ethernet/marvell/octeon_ep/Kconfig" +source "drivers/net/ethernet/marvell/octeon_ep_vf/Kconfig" source "drivers/net/ethernet/marvell/prestera/Kconfig" endif # NET_VENDOR_MARVELL diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile index ceba4aa4f026..a399defe25fd 100644 --- a/drivers/net/ethernet/marvell/Makefile +++ b/drivers/net/ethernet/marvell/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SKY2) += sky2.o obj-y += octeon_ep/ +obj-y += octeon_ep_vf/ obj-y += octeontx2/ obj-y += prestera/ diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index a641b3534ca3..40a5f1431e4e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5097,7 +5097,7 @@ static int mvneta_ethtool_set_wol(struct net_device *dev, } static int mvneta_ethtool_get_eee(struct net_device *dev, - struct ethtool_eee *eee) + struct ethtool_keee *eee) { struct mvneta_port *pp = netdev_priv(dev); u32 lpi_ctl0; @@ -5113,7 +5113,7 @@ static int mvneta_ethtool_get_eee(struct net_device *dev, } static int mvneta_ethtool_set_eee(struct net_device *dev, - struct ethtool_eee *eee) + struct ethtool_keee *eee) { struct mvneta_port *pp = netdev_priv(dev); u32 lpi_ctl0; diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/Kconfig b/drivers/net/ethernet/marvell/octeon_ep_vf/Kconfig new file mode 100644 index 000000000000..dbd1267bda0c --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Marvell's Octeon PCI Endpoint NIC VF Driver Configuration +# + +config OCTEON_EP_VF + tristate "Marvell Octeon PCI Endpoint NIC VF Driver" + depends on 64BIT + depends on PCI + help + This driver supports networking functionality of Marvell's + Octeon PCI Endpoint NIC VF. + + To know the list of devices supported by this driver, refer + documentation in + <file:Documentation/networking/device_drivers/ethernet/marvell/octeon_ep_vf.rst>. + + To compile this drivers as a module, choose M here. Name of the + module is octeon_ep_vf. diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/Makefile b/drivers/net/ethernet/marvell/octeon_ep_vf/Makefile new file mode 100644 index 000000000000..4a5f9fcb0b40 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Network driver for Marvell's Octeon PCI Endpoint NIC VF +# + +obj-$(CONFIG_OCTEON_EP_VF) += octeon_ep_vf.o + +octeon_ep_vf-y := octep_vf_main.o octep_vf_cn9k.o octep_vf_cnxk.o \ + octep_vf_tx.o octep_vf_rx.o octep_vf_mbox.o \ + octep_vf_ethtool.o diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c new file mode 100644 index 000000000000..88937fce75f1 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include "octep_vf_config.h" +#include "octep_vf_main.h" +#include "octep_vf_regs_cn9k.h" + +/* Dump useful hardware IQ/OQ CSRs for debug purpose */ +static void cn93_vf_dump_q_regs(struct octep_vf_device *oct, int qno) +{ + struct device *dev = &oct->pdev->dev; + + dev_info(dev, "IQ-%d register dump\n", qno); + dev_info(dev, "R[%d]_IN_INSTR_DBELL[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_INSTR_DBELL(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INSTR_DBELL(qno))); + dev_info(dev, "R[%d]_IN_CONTROL[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_CONTROL(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_CONTROL(qno))); + dev_info(dev, "R[%d]_IN_ENABLE[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_ENABLE(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_ENABLE(qno))); + dev_info(dev, "R[%d]_IN_INSTR_BADDR[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_INSTR_BADDR(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INSTR_BADDR(qno))); + dev_info(dev, "R[%d]_IN_INSTR_RSIZE[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_INSTR_RSIZE(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INSTR_RSIZE(qno))); + dev_info(dev, "R[%d]_IN_CNTS[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_CNTS(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_CNTS(qno))); + dev_info(dev, "R[%d]_IN_INT_LEVELS[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_INT_LEVELS(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(qno))); + dev_info(dev, "R[%d]_IN_PKT_CNT[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_PKT_CNT(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_PKT_CNT(qno))); + dev_info(dev, "R[%d]_IN_BYTE_CNT[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_IN_BYTE_CNT(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_BYTE_CNT(qno))); + + dev_info(dev, "OQ-%d register dump\n", qno); + dev_info(dev, "R[%d]_OUT_SLIST_DBELL[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_SLIST_DBELL(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_SLIST_DBELL(qno))); + dev_info(dev, "R[%d]_OUT_CONTROL[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_CONTROL(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_CONTROL(qno))); + dev_info(dev, "R[%d]_OUT_ENABLE[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_ENABLE(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_ENABLE(qno))); + dev_info(dev, "R[%d]_OUT_SLIST_BADDR[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_SLIST_BADDR(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_SLIST_BADDR(qno))); + dev_info(dev, "R[%d]_OUT_SLIST_RSIZE[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_SLIST_RSIZE(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_SLIST_RSIZE(qno))); + dev_info(dev, "R[%d]_OUT_CNTS[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_CNTS(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_CNTS(qno))); + dev_info(dev, "R[%d]_OUT_INT_LEVELS[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_INT_LEVELS(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(qno))); + dev_info(dev, "R[%d]_OUT_PKT_CNT[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_PKT_CNT(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_PKT_CNT(qno))); + dev_info(dev, "R[%d]_OUT_BYTE_CNT[0x%llx]: 0x%016llx\n", + qno, CN93_VF_SDP_R_OUT_BYTE_CNT(qno), + octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_BYTE_CNT(qno))); +} + +/* Reset Hardware Tx queue */ +static void cn93_vf_reset_iq(struct octep_vf_device *oct, int q_no) +{ + u64 val = ULL(0); + + dev_dbg(&oct->pdev->dev, "Reset VF IQ-%d\n", q_no); + + /* Disable the Tx/Instruction Ring */ + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_ENABLE(q_no), val); + + /* clear the Instruction Ring packet/byte counts and doorbell CSRs */ + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(q_no), val); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_PKT_CNT(q_no), val); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_BYTE_CNT(q_no), val); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INSTR_BADDR(q_no), val); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INSTR_RSIZE(q_no), val); + + val = GENMASK_ULL(31, 0); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INSTR_DBELL(q_no), val); + + val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_CNTS(q_no)); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_CNTS(q_no), + val & GENMASK_ULL(31, 0)); +} + +/* Reset Hardware Rx queue */ +static void cn93_vf_reset_oq(struct octep_vf_device *oct, int q_no) +{ + u64 val = ULL(0); + + /* Disable Output (Rx) Ring */ + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_ENABLE(q_no), val); + + /* Clear count CSRs */ + val = octep_vf_read_csr(oct, CN93_VF_SDP_R_OUT_CNTS(q_no)); + octep_vf_write_csr(oct, CN93_VF_SDP_R_OUT_CNTS(q_no), val); + + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_PKT_CNT(q_no), GENMASK_ULL(35, 0)); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_SLIST_DBELL(q_no), GENMASK_ULL(31, 0)); +} + +/* Reset all hardware Tx/Rx queues */ +static void octep_vf_reset_io_queues_cn93(struct octep_vf_device *oct) +{ + struct pci_dev *pdev = oct->pdev; + int q; + + dev_dbg(&pdev->dev, "Reset OCTEP_CN93 VF IO Queues\n"); + + for (q = 0; q < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); q++) { + cn93_vf_reset_iq(oct, q); + cn93_vf_reset_oq(oct, q); + } +} + +/* Initialize configuration limits and initial active config */ +static void octep_vf_init_config_cn93_vf(struct octep_vf_device *oct) +{ + struct octep_vf_config *conf = oct->conf; + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_CONTROL(0)); + conf->ring_cfg.max_io_rings = (reg_val >> CN93_VF_R_IN_CTL_RPVF_POS) & + CN93_VF_R_IN_CTL_RPVF_MASK; + conf->ring_cfg.active_io_rings = conf->ring_cfg.max_io_rings; + + conf->iq.num_descs = OCTEP_VF_IQ_MAX_DESCRIPTORS; + conf->iq.instr_type = OCTEP_VF_64BYTE_INSTR; + conf->iq.db_min = OCTEP_VF_DB_MIN; + conf->iq.intr_threshold = OCTEP_VF_IQ_INTR_THRESHOLD; + + conf->oq.num_descs = OCTEP_VF_OQ_MAX_DESCRIPTORS; + conf->oq.buf_size = OCTEP_VF_OQ_BUF_SIZE; + conf->oq.refill_threshold = OCTEP_VF_OQ_REFILL_THRESHOLD; + conf->oq.oq_intr_pkt = OCTEP_VF_OQ_INTR_PKT_THRESHOLD; + conf->oq.oq_intr_time = OCTEP_VF_OQ_INTR_TIME_THRESHOLD; + + conf->msix_cfg.ioq_msix = conf->ring_cfg.active_io_rings; +} + +/* Setup registers for a hardware Tx Queue */ +static void octep_vf_setup_iq_regs_cn93(struct octep_vf_device *oct, int iq_no) +{ + struct octep_vf_iq *iq = oct->iq[iq_no]; + u32 reset_instr_cnt; + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_CONTROL(iq_no)); + + /* wait for IDLE to set to 1 */ + if (!(reg_val & CN93_VF_R_IN_CTL_IDLE)) { + do { + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_CONTROL(iq_no)); + } while (!(reg_val & CN93_VF_R_IN_CTL_IDLE)); + } + reg_val |= CN93_VF_R_IN_CTL_RDSIZE; + reg_val |= CN93_VF_R_IN_CTL_IS_64B; + reg_val |= CN93_VF_R_IN_CTL_ESR; + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_CONTROL(iq_no), reg_val); + + /* Write the start of the input queue's ring and its size */ + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INSTR_BADDR(iq_no), iq->desc_ring_dma); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INSTR_RSIZE(iq_no), iq->max_count); + + /* Remember the doorbell & instruction count register addr for this queue */ + iq->doorbell_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_IN_INSTR_DBELL(iq_no); + iq->inst_cnt_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_IN_CNTS(iq_no); + iq->intr_lvl_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_IN_INT_LEVELS(iq_no); + + /* Store the current instruction counter (used in flush_iq calculation) */ + reset_instr_cnt = readl(iq->inst_cnt_reg); + writel(reset_instr_cnt, iq->inst_cnt_reg); + + /* INTR_THRESHOLD is set to max(FFFFFFFF) to disable the INTR */ + reg_val = CFG_GET_IQ_INTR_THRESHOLD(oct->conf) & GENMASK_ULL(31, 0); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(iq_no), reg_val); +} + +/* Setup registers for a hardware Rx Queue */ +static void octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no) +{ + struct octep_vf_oq *oq = oct->oq[oq_no]; + u32 time_threshold = 0; + u64 oq_ctl = ULL(0); + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_CONTROL(oq_no)); + + /* wait for IDLE to set to 1 */ + if (!(reg_val & CN93_VF_R_OUT_CTL_IDLE)) { + do { + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_CONTROL(oq_no)); + } while (!(reg_val & CN93_VF_R_OUT_CTL_IDLE)); + } + + reg_val &= ~(CN93_VF_R_OUT_CTL_IMODE); + reg_val &= ~(CN93_VF_R_OUT_CTL_ROR_P); + reg_val &= ~(CN93_VF_R_OUT_CTL_NSR_P); + reg_val &= ~(CN93_VF_R_OUT_CTL_ROR_I); + reg_val &= ~(CN93_VF_R_OUT_CTL_NSR_I); + reg_val &= ~(CN93_VF_R_OUT_CTL_ES_I); + reg_val &= ~(CN93_VF_R_OUT_CTL_ROR_D); + reg_val &= ~(CN93_VF_R_OUT_CTL_NSR_D); + reg_val &= ~(CN93_VF_R_OUT_CTL_ES_D); + reg_val |= (CN93_VF_R_OUT_CTL_ES_P); + + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_CONTROL(oq_no), reg_val); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_SLIST_BADDR(oq_no), oq->desc_ring_dma); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_SLIST_RSIZE(oq_no), oq->max_count); + + oq_ctl = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_CONTROL(oq_no)); + oq_ctl &= ~GENMASK_ULL(22, 0); //clear the ISIZE and BSIZE (22-0) + oq_ctl |= (oq->buffer_size & GENMASK_ULL(15, 0)); //populate the BSIZE (15-0) + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_CONTROL(oq_no), oq_ctl); + + /* Get the mapped address of the pkt_sent and pkts_credit regs */ + oq->pkts_sent_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_OUT_CNTS(oq_no); + oq->pkts_credit_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_OUT_SLIST_DBELL(oq_no); + + time_threshold = CFG_GET_OQ_INTR_TIME(oct->conf); + reg_val = ((u64)time_threshold << 32) | CFG_GET_OQ_INTR_PKT(oct->conf); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(oq_no), reg_val); +} + +/* Setup registers for a VF mailbox */ +static void octep_vf_setup_mbox_regs_cn93(struct octep_vf_device *oct, int q_no) +{ + struct octep_vf_mbox *mbox = oct->mbox; + + /* PF to VF DATA reg. VF reads from this reg */ + mbox->mbox_read_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_MBOX_PF_VF_DATA(q_no); + + /* VF mbox interrupt reg */ + mbox->mbox_int_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_MBOX_PF_VF_INT(q_no); + + /* VF to PF DATA reg. VF writes into this reg */ + mbox->mbox_write_reg = oct->mmio.hw_addr + CN93_VF_SDP_R_MBOX_VF_PF_DATA(q_no); +} + +/* Mailbox Interrupt handler */ +static void cn93_handle_vf_mbox_intr(struct octep_vf_device *oct) +{ + if (oct->mbox) + schedule_work(&oct->mbox->wk.work); + else + dev_err(&oct->pdev->dev, "cannot schedule work on invalid mbox\n"); +} + +/* Tx/Rx queue interrupt handler */ +static irqreturn_t octep_vf_ioq_intr_handler_cn93(void *data) +{ + struct octep_vf_ioq_vector *vector = data; + struct octep_vf_device *oct; + struct octep_vf_oq *oq; + u64 reg_val; + + oct = vector->octep_vf_dev; + oq = vector->oq; + /* Mailbox interrupt arrives along with interrupt of tx/rx ring pair 0 */ + if (oq->q_no == 0) { + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_MBOX_PF_VF_INT(0)); + if (reg_val & CN93_VF_SDP_R_MBOX_PF_VF_INT_STATUS) { + cn93_handle_vf_mbox_intr(oct); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_MBOX_PF_VF_INT(0), reg_val); + } + } + napi_schedule_irqoff(oq->napi); + return IRQ_HANDLED; +} + +/* Re-initialize Octeon hardware registers */ +static void octep_vf_reinit_regs_cn93(struct octep_vf_device *oct) +{ + u32 i; + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) + oct->hw_ops.setup_iq_regs(oct, i); + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) + oct->hw_ops.setup_oq_regs(oct, i); + + oct->hw_ops.enable_interrupts(oct); + oct->hw_ops.enable_io_queues(oct); + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) + writel(oct->oq[i]->max_count, oct->oq[i]->pkts_credit_reg); +} + +/* Enable all interrupts */ +static void octep_vf_enable_interrupts_cn93(struct octep_vf_device *oct) +{ + int num_rings, q; + u64 reg_val; + + num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + for (q = 0; q < num_rings; q++) { + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(q)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(q), reg_val); + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(q)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(q), reg_val); + } + /* Enable PF to VF mbox interrupt by setting 2nd bit*/ + octep_vf_write_csr64(oct, CN93_VF_SDP_R_MBOX_PF_VF_INT(0), + CN93_VF_SDP_R_MBOX_PF_VF_INT_ENAB); +} + +/* Disable all interrupts */ +static void octep_vf_disable_interrupts_cn93(struct octep_vf_device *oct) +{ + int num_rings, q; + u64 reg_val; + + /* Disable PF to VF mbox interrupt by setting 2nd bit*/ + if (oct->mbox) + octep_vf_write_csr64(oct, CN93_VF_SDP_R_MBOX_PF_VF_INT(0), 0x0); + + num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + for (q = 0; q < num_rings; q++) { + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(q)); + reg_val &= ~BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(q), reg_val); + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(q)); + reg_val &= ~BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(q), reg_val); + } +} + +/* Get new Octeon Read Index: index of descriptor that Octeon reads next. */ +static u32 octep_vf_update_iq_read_index_cn93(struct octep_vf_iq *iq) +{ + u32 pkt_in_done = readl(iq->inst_cnt_reg); + u32 last_done, new_idx; + + last_done = pkt_in_done - iq->pkt_in_done; + iq->pkt_in_done = pkt_in_done; + + new_idx = (iq->octep_vf_read_index + last_done) % iq->max_count; + + return new_idx; +} + +/* Enable a hardware Tx Queue */ +static void octep_vf_enable_iq_cn93(struct octep_vf_device *oct, int iq_no) +{ + u64 loop = HZ; + u64 reg_val; + + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INSTR_DBELL(iq_no), GENMASK_ULL(31, 0)); + + while (octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INSTR_DBELL(iq_no)) && + loop--) { + schedule_timeout_interruptible(1); + } + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(iq_no)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_INT_LEVELS(iq_no), reg_val); + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_ENABLE(iq_no)); + reg_val |= ULL(1); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_ENABLE(iq_no), reg_val); +} + +/* Enable a hardware Rx Queue */ +static void octep_vf_enable_oq_cn93(struct octep_vf_device *oct, int oq_no) +{ + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(oq_no)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(oq_no), reg_val); + + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_SLIST_DBELL(oq_no), GENMASK_ULL(31, 0)); + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_ENABLE(oq_no)); + reg_val |= ULL(1); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_ENABLE(oq_no), reg_val); +} + +/* Enable all hardware Tx/Rx Queues assigned to VF */ +static void octep_vf_enable_io_queues_cn93(struct octep_vf_device *oct) +{ + u8 q; + + for (q = 0; q < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); q++) { + octep_vf_enable_iq_cn93(oct, q); + octep_vf_enable_oq_cn93(oct, q); + } +} + +/* Disable a hardware Tx Queue assigned to VF */ +static void octep_vf_disable_iq_cn93(struct octep_vf_device *oct, int iq_no) +{ + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_IN_ENABLE(iq_no)); + reg_val &= ~ULL(1); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_IN_ENABLE(iq_no), reg_val); +} + +/* Disable a hardware Rx Queue assigned to VF */ +static void octep_vf_disable_oq_cn93(struct octep_vf_device *oct, int oq_no) +{ + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CN93_VF_SDP_R_OUT_ENABLE(oq_no)); + reg_val &= ~ULL(1); + octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_ENABLE(oq_no), reg_val); +} + +/* Disable all hardware Tx/Rx Queues assigned to VF */ +static void octep_vf_disable_io_queues_cn93(struct octep_vf_device *oct) +{ + int q; + + for (q = 0; q < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); q++) { + octep_vf_disable_iq_cn93(oct, q); + octep_vf_disable_oq_cn93(oct, q); + } +} + +/* Dump hardware registers (including Tx/Rx queues) for debugging. */ +static void octep_vf_dump_registers_cn93(struct octep_vf_device *oct) +{ + u8 num_rings, q; + + num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + for (q = 0; q < num_rings; q++) + cn93_vf_dump_q_regs(oct, q); +} + +/** + * octep_vf_device_setup_cn93() - Setup Octeon device. + * + * @oct: Octeon device private data structure. + * + * - initialize hardware operations. + * - get target side pcie port number for the device. + * - set initial configuration and max limits. + */ +void octep_vf_device_setup_cn93(struct octep_vf_device *oct) +{ + oct->hw_ops.setup_iq_regs = octep_vf_setup_iq_regs_cn93; + oct->hw_ops.setup_oq_regs = octep_vf_setup_oq_regs_cn93; + oct->hw_ops.setup_mbox_regs = octep_vf_setup_mbox_regs_cn93; + + oct->hw_ops.ioq_intr_handler = octep_vf_ioq_intr_handler_cn93; + oct->hw_ops.reinit_regs = octep_vf_reinit_regs_cn93; + + oct->hw_ops.enable_interrupts = octep_vf_enable_interrupts_cn93; + oct->hw_ops.disable_interrupts = octep_vf_disable_interrupts_cn93; + + oct->hw_ops.update_iq_read_idx = octep_vf_update_iq_read_index_cn93; + + oct->hw_ops.enable_iq = octep_vf_enable_iq_cn93; + oct->hw_ops.enable_oq = octep_vf_enable_oq_cn93; + oct->hw_ops.enable_io_queues = octep_vf_enable_io_queues_cn93; + + oct->hw_ops.disable_iq = octep_vf_disable_iq_cn93; + oct->hw_ops.disable_oq = octep_vf_disable_oq_cn93; + oct->hw_ops.disable_io_queues = octep_vf_disable_io_queues_cn93; + oct->hw_ops.reset_io_queues = octep_vf_reset_io_queues_cn93; + + oct->hw_ops.dump_registers = octep_vf_dump_registers_cn93; + octep_vf_init_config_cn93_vf(oct); +} diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c new file mode 100644 index 000000000000..1f79dfad42c6 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include "octep_vf_config.h" +#include "octep_vf_main.h" +#include "octep_vf_regs_cnxk.h" + +/* Dump useful hardware IQ/OQ CSRs for debug purpose */ +static void cnxk_vf_dump_q_regs(struct octep_vf_device *oct, int qno) +{ + struct device *dev = &oct->pdev->dev; + + dev_info(dev, "IQ-%d register dump\n", qno); + dev_info(dev, "R[%d]_IN_INSTR_DBELL[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_INSTR_DBELL(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_DBELL(qno))); + dev_info(dev, "R[%d]_IN_CONTROL[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_CONTROL(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_CONTROL(qno))); + dev_info(dev, "R[%d]_IN_ENABLE[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_ENABLE(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_ENABLE(qno))); + dev_info(dev, "R[%d]_IN_INSTR_BADDR[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_INSTR_BADDR(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_BADDR(qno))); + dev_info(dev, "R[%d]_IN_INSTR_RSIZE[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_INSTR_RSIZE(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_RSIZE(qno))); + dev_info(dev, "R[%d]_IN_CNTS[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_CNTS(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_CNTS(qno))); + dev_info(dev, "R[%d]_IN_INT_LEVELS[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_INT_LEVELS(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(qno))); + dev_info(dev, "R[%d]_IN_PKT_CNT[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_PKT_CNT(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_PKT_CNT(qno))); + dev_info(dev, "R[%d]_IN_BYTE_CNT[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_IN_BYTE_CNT(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_BYTE_CNT(qno))); + + dev_info(dev, "OQ-%d register dump\n", qno); + dev_info(dev, "R[%d]_OUT_SLIST_DBELL[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_SLIST_DBELL(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_DBELL(qno))); + dev_info(dev, "R[%d]_OUT_CONTROL[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_CONTROL(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(qno))); + dev_info(dev, "R[%d]_OUT_ENABLE[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_ENABLE(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_ENABLE(qno))); + dev_info(dev, "R[%d]_OUT_SLIST_BADDR[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_SLIST_BADDR(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_BADDR(qno))); + dev_info(dev, "R[%d]_OUT_SLIST_RSIZE[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_SLIST_RSIZE(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_RSIZE(qno))); + dev_info(dev, "R[%d]_OUT_CNTS[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_CNTS(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CNTS(qno))); + dev_info(dev, "R[%d]_OUT_INT_LEVELS[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_INT_LEVELS(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(qno))); + dev_info(dev, "R[%d]_OUT_PKT_CNT[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_PKT_CNT(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_PKT_CNT(qno))); + dev_info(dev, "R[%d]_OUT_BYTE_CNT[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_OUT_BYTE_CNT(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_BYTE_CNT(qno))); + dev_info(dev, "R[%d]_ERR_TYPE[0x%llx]: 0x%016llx\n", + qno, CNXK_VF_SDP_R_ERR_TYPE(qno), + octep_vf_read_csr64(oct, CNXK_VF_SDP_R_ERR_TYPE(qno))); +} + +/* Reset Hardware Tx queue */ +static void cnxk_vf_reset_iq(struct octep_vf_device *oct, int q_no) +{ + u64 val = ULL(0); + + dev_dbg(&oct->pdev->dev, "Reset VF IQ-%d\n", q_no); + + /* Disable the Tx/Instruction Ring */ + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_ENABLE(q_no), val); + + /* clear the Instruction Ring packet/byte counts and doorbell CSRs */ + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(q_no), val); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_PKT_CNT(q_no), val); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_BYTE_CNT(q_no), val); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_BADDR(q_no), val); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_RSIZE(q_no), val); + + val = GENMASK_ULL(31, 0); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_DBELL(q_no), val); + + val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_CNTS(q_no)); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_CNTS(q_no), val & GENMASK_ULL(31, 0)); +} + +/* Reset Hardware Rx queue */ +static void cnxk_vf_reset_oq(struct octep_vf_device *oct, int q_no) +{ + u64 val = ULL(0); + + /* Disable Output (Rx) Ring */ + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_ENABLE(q_no), val); + + /* Clear count CSRs */ + val = octep_vf_read_csr(oct, CNXK_VF_SDP_R_OUT_CNTS(q_no)); + octep_vf_write_csr(oct, CNXK_VF_SDP_R_OUT_CNTS(q_no), val); + + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_PKT_CNT(q_no), GENMASK_ULL(35, 0)); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_DBELL(q_no), GENMASK_ULL(31, 0)); +} + +/* Reset all hardware Tx/Rx queues */ +static void octep_vf_reset_io_queues_cnxk(struct octep_vf_device *oct) +{ + struct pci_dev *pdev = oct->pdev; + int q; + + dev_dbg(&pdev->dev, "Reset OCTEP_CNXK VF IO Queues\n"); + + for (q = 0; q < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); q++) { + cnxk_vf_reset_iq(oct, q); + cnxk_vf_reset_oq(oct, q); + } +} + +/* Initialize configuration limits and initial active config */ +static void octep_vf_init_config_cnxk_vf(struct octep_vf_device *oct) +{ + struct octep_vf_config *conf = oct->conf; + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_CONTROL(0)); + conf->ring_cfg.max_io_rings = (reg_val >> CNXK_VF_R_IN_CTL_RPVF_POS) & + CNXK_VF_R_IN_CTL_RPVF_MASK; + conf->ring_cfg.active_io_rings = conf->ring_cfg.max_io_rings; + + conf->iq.num_descs = OCTEP_VF_IQ_MAX_DESCRIPTORS; + conf->iq.instr_type = OCTEP_VF_64BYTE_INSTR; + conf->iq.db_min = OCTEP_VF_DB_MIN; + conf->iq.intr_threshold = OCTEP_VF_IQ_INTR_THRESHOLD; + + conf->oq.num_descs = OCTEP_VF_OQ_MAX_DESCRIPTORS; + conf->oq.buf_size = OCTEP_VF_OQ_BUF_SIZE; + conf->oq.refill_threshold = OCTEP_VF_OQ_REFILL_THRESHOLD; + conf->oq.oq_intr_pkt = OCTEP_VF_OQ_INTR_PKT_THRESHOLD; + conf->oq.oq_intr_time = OCTEP_VF_OQ_INTR_TIME_THRESHOLD; + conf->oq.wmark = OCTEP_VF_OQ_WMARK_MIN; + + conf->msix_cfg.ioq_msix = conf->ring_cfg.active_io_rings; +} + +/* Setup registers for a hardware Tx Queue */ +static void octep_vf_setup_iq_regs_cnxk(struct octep_vf_device *oct, int iq_no) +{ + struct octep_vf_iq *iq = oct->iq[iq_no]; + u32 reset_instr_cnt; + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_CONTROL(iq_no)); + + /* wait for IDLE to set to 1 */ + if (!(reg_val & CNXK_VF_R_IN_CTL_IDLE)) { + do { + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_CONTROL(iq_no)); + } while (!(reg_val & CNXK_VF_R_IN_CTL_IDLE)); + } + reg_val |= CNXK_VF_R_IN_CTL_RDSIZE; + reg_val |= CNXK_VF_R_IN_CTL_IS_64B; + reg_val |= CNXK_VF_R_IN_CTL_ESR; + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_CONTROL(iq_no), reg_val); + + /* Write the start of the input queue's ring and its size */ + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_BADDR(iq_no), iq->desc_ring_dma); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_RSIZE(iq_no), iq->max_count); + + /* Remember the doorbell & instruction count register addr for this queue */ + iq->doorbell_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_IN_INSTR_DBELL(iq_no); + iq->inst_cnt_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_IN_CNTS(iq_no); + iq->intr_lvl_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_IN_INT_LEVELS(iq_no); + + /* Store the current instruction counter (used in flush_iq calculation) */ + reset_instr_cnt = readl(iq->inst_cnt_reg); + writel(reset_instr_cnt, iq->inst_cnt_reg); + + /* INTR_THRESHOLD is set to max(FFFFFFFF) to disable the INTR */ + reg_val = CFG_GET_IQ_INTR_THRESHOLD(oct->conf) & GENMASK_ULL(31, 0); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(iq_no), reg_val); +} + +/* Setup registers for a hardware Rx Queue */ +static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) +{ + struct octep_vf_oq *oq = oct->oq[oq_no]; + u32 time_threshold = 0; + u64 oq_ctl = ULL(0); + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no)); + + /* wait for IDLE to set to 1 */ + if (!(reg_val & CNXK_VF_R_OUT_CTL_IDLE)) { + do { + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no)); + } while (!(reg_val & CNXK_VF_R_OUT_CTL_IDLE)); + } + + reg_val &= ~(CNXK_VF_R_OUT_CTL_IMODE); + reg_val &= ~(CNXK_VF_R_OUT_CTL_ROR_P); + reg_val &= ~(CNXK_VF_R_OUT_CTL_NSR_P); + reg_val &= ~(CNXK_VF_R_OUT_CTL_ROR_I); + reg_val &= ~(CNXK_VF_R_OUT_CTL_NSR_I); + reg_val &= ~(CNXK_VF_R_OUT_CTL_ES_I); + reg_val &= ~(CNXK_VF_R_OUT_CTL_ROR_D); + reg_val &= ~(CNXK_VF_R_OUT_CTL_NSR_D); + reg_val &= ~(CNXK_VF_R_OUT_CTL_ES_D); + reg_val |= (CNXK_VF_R_OUT_CTL_ES_P); + + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no), reg_val); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_BADDR(oq_no), oq->desc_ring_dma); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_RSIZE(oq_no), oq->max_count); + + oq_ctl = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no)); + /* Clear the ISIZE and BSIZE (22-0) */ + oq_ctl &= ~GENMASK_ULL(22, 0); + /* Populate the BSIZE (15-0) */ + oq_ctl |= (oq->buffer_size & GENMASK_ULL(15, 0)); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no), oq_ctl); + + /* Get the mapped address of the pkt_sent and pkts_credit regs */ + oq->pkts_sent_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_OUT_CNTS(oq_no); + oq->pkts_credit_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_OUT_SLIST_DBELL(oq_no); + + time_threshold = CFG_GET_OQ_INTR_TIME(oct->conf); + reg_val = ((u64)time_threshold << 32) | CFG_GET_OQ_INTR_PKT(oct->conf); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(oq_no), reg_val); + + /* set watermark for backpressure */ + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_WMARK(oq_no)); + reg_val &= ~GENMASK_ULL(31, 0); + reg_val |= CFG_GET_OQ_WMARK(oct->conf); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_WMARK(oq_no), reg_val); +} + +/* Setup registers for a VF mailbox */ +static void octep_vf_setup_mbox_regs_cnxk(struct octep_vf_device *oct, int q_no) +{ + struct octep_vf_mbox *mbox = oct->mbox; + + /* PF to VF DATA reg. VF reads from this reg */ + mbox->mbox_read_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_MBOX_PF_VF_DATA(q_no); + + /* VF mbox interrupt reg */ + mbox->mbox_int_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_MBOX_PF_VF_INT(q_no); + + /* VF to PF DATA reg. VF writes into this reg */ + mbox->mbox_write_reg = oct->mmio.hw_addr + CNXK_VF_SDP_R_MBOX_VF_PF_DATA(q_no); +} + +/* Mailbox Interrupt handler */ +static void cnxk_handle_vf_mbox_intr(struct octep_vf_device *oct) +{ + if (oct->mbox) + schedule_work(&oct->mbox->wk.work); + else + dev_err(&oct->pdev->dev, "cannot schedule work on invalid mbox\n"); +} + +/* Tx/Rx queue interrupt handler */ +static irqreturn_t octep_vf_ioq_intr_handler_cnxk(void *data) +{ + struct octep_vf_ioq_vector *vector = data; + struct octep_vf_device *oct; + struct octep_vf_oq *oq; + u64 reg_val; + + oct = vector->octep_vf_dev; + oq = vector->oq; + /* Mailbox interrupt arrives along with interrupt of tx/rx ring pair 0 */ + if (oq->q_no == 0) { + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_MBOX_PF_VF_INT(0)); + if (reg_val & CNXK_VF_SDP_R_MBOX_PF_VF_INT_STATUS) { + cnxk_handle_vf_mbox_intr(oct); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_MBOX_PF_VF_INT(0), reg_val); + } + } + napi_schedule_irqoff(oq->napi); + return IRQ_HANDLED; +} + +/* Re-initialize Octeon hardware registers */ +static void octep_vf_reinit_regs_cnxk(struct octep_vf_device *oct) +{ + u32 i; + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) + oct->hw_ops.setup_iq_regs(oct, i); + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) + oct->hw_ops.setup_oq_regs(oct, i); + + oct->hw_ops.enable_interrupts(oct); + oct->hw_ops.enable_io_queues(oct); + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) + writel(oct->oq[i]->max_count, oct->oq[i]->pkts_credit_reg); +} + +/* Enable all interrupts */ +static void octep_vf_enable_interrupts_cnxk(struct octep_vf_device *oct) +{ + int num_rings, q; + u64 reg_val; + + num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + for (q = 0; q < num_rings; q++) { + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(q)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(q), reg_val); + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(q)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(q), reg_val); + } + /* Enable PF to VF mbox interrupt by setting 2nd bit*/ + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_MBOX_PF_VF_INT(0), + CNXK_VF_SDP_R_MBOX_PF_VF_INT_ENAB); +} + +/* Disable all interrupts */ +static void octep_vf_disable_interrupts_cnxk(struct octep_vf_device *oct) +{ + int num_rings, q; + u64 reg_val; + + /* Disable PF to VF mbox interrupt by setting 2nd bit*/ + if (oct->mbox) + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_MBOX_PF_VF_INT(0), 0x0); + + num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + for (q = 0; q < num_rings; q++) { + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(q)); + reg_val &= ~BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(q), reg_val); + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(q)); + reg_val &= ~BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(q), reg_val); + } +} + +/* Get new Octeon Read Index: index of descriptor that Octeon reads next. */ +static u32 octep_vf_update_iq_read_index_cnxk(struct octep_vf_iq *iq) +{ + u32 pkt_in_done = readl(iq->inst_cnt_reg); + u32 last_done, new_idx; + + last_done = pkt_in_done - iq->pkt_in_done; + iq->pkt_in_done = pkt_in_done; + + new_idx = (iq->octep_vf_read_index + last_done) % iq->max_count; + + return new_idx; +} + +/* Enable a hardware Tx Queue */ +static void octep_vf_enable_iq_cnxk(struct octep_vf_device *oct, int iq_no) +{ + u64 loop = HZ; + u64 reg_val; + + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_DBELL(iq_no), GENMASK_ULL(31, 0)); + + while (octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INSTR_DBELL(iq_no)) && + loop--) { + schedule_timeout_interruptible(1); + } + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(iq_no)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_INT_LEVELS(iq_no), reg_val); + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_ENABLE(iq_no)); + reg_val |= ULL(1); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_ENABLE(iq_no), reg_val); +} + +/* Enable a hardware Rx Queue */ +static void octep_vf_enable_oq_cnxk(struct octep_vf_device *oct, int oq_no) +{ + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(oq_no)); + reg_val |= BIT_ULL_MASK(62); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_INT_LEVELS(oq_no), reg_val); + + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_DBELL(oq_no), GENMASK_ULL(31, 0)); + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_ENABLE(oq_no)); + reg_val |= ULL(1); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_ENABLE(oq_no), reg_val); +} + +/* Enable all hardware Tx/Rx Queues assigned to VF */ +static void octep_vf_enable_io_queues_cnxk(struct octep_vf_device *oct) +{ + u8 q; + + for (q = 0; q < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); q++) { + octep_vf_enable_iq_cnxk(oct, q); + octep_vf_enable_oq_cnxk(oct, q); + } +} + +/* Disable a hardware Tx Queue assigned to VF */ +static void octep_vf_disable_iq_cnxk(struct octep_vf_device *oct, int iq_no) +{ + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_IN_ENABLE(iq_no)); + reg_val &= ~ULL(1); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_IN_ENABLE(iq_no), reg_val); +} + +/* Disable a hardware Rx Queue assigned to VF */ +static void octep_vf_disable_oq_cnxk(struct octep_vf_device *oct, int oq_no) +{ + u64 reg_val; + + reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_ENABLE(oq_no)); + reg_val &= ~ULL(1); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_ENABLE(oq_no), reg_val); +} + +/* Disable all hardware Tx/Rx Queues assigned to VF */ +static void octep_vf_disable_io_queues_cnxk(struct octep_vf_device *oct) +{ + int q; + + for (q = 0; q < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); q++) { + octep_vf_disable_iq_cnxk(oct, q); + octep_vf_disable_oq_cnxk(oct, q); + } +} + +/* Dump hardware registers (including Tx/Rx queues) for debugging. */ +static void octep_vf_dump_registers_cnxk(struct octep_vf_device *oct) +{ + u8 num_rings, q; + + num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + for (q = 0; q < num_rings; q++) + cnxk_vf_dump_q_regs(oct, q); +} + +/** + * octep_vf_device_setup_cnxk() - Setup Octeon device. + * + * @oct: Octeon device private data structure. + * + * - initialize hardware operations. + * - get target side pcie port number for the device. + * - set initial configuration and max limits. + */ +void octep_vf_device_setup_cnxk(struct octep_vf_device *oct) +{ + oct->hw_ops.setup_iq_regs = octep_vf_setup_iq_regs_cnxk; + oct->hw_ops.setup_oq_regs = octep_vf_setup_oq_regs_cnxk; + oct->hw_ops.setup_mbox_regs = octep_vf_setup_mbox_regs_cnxk; + + oct->hw_ops.ioq_intr_handler = octep_vf_ioq_intr_handler_cnxk; + oct->hw_ops.reinit_regs = octep_vf_reinit_regs_cnxk; + + oct->hw_ops.enable_interrupts = octep_vf_enable_interrupts_cnxk; + oct->hw_ops.disable_interrupts = octep_vf_disable_interrupts_cnxk; + + oct->hw_ops.update_iq_read_idx = octep_vf_update_iq_read_index_cnxk; + + oct->hw_ops.enable_iq = octep_vf_enable_iq_cnxk; + oct->hw_ops.enable_oq = octep_vf_enable_oq_cnxk; + oct->hw_ops.enable_io_queues = octep_vf_enable_io_queues_cnxk; + + oct->hw_ops.disable_iq = octep_vf_disable_iq_cnxk; + oct->hw_ops.disable_oq = octep_vf_disable_oq_cnxk; + oct->hw_ops.disable_io_queues = octep_vf_disable_io_queues_cnxk; + oct->hw_ops.reset_io_queues = octep_vf_reset_io_queues_cnxk; + + oct->hw_ops.dump_registers = octep_vf_dump_registers_cnxk; + octep_vf_init_config_cnxk_vf(oct); +} diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_config.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_config.h new file mode 100644 index 000000000000..e03a647b0110 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_config.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#ifndef _OCTEP_VF_CONFIG_H_ +#define _OCTEP_VF_CONFIG_H_ + +/* Tx instruction types by length */ +#define OCTEP_VF_32BYTE_INSTR 32 +#define OCTEP_VF_64BYTE_INSTR 64 + +/* Tx Queue: maximum descriptors per ring */ +#define OCTEP_VF_IQ_MAX_DESCRIPTORS 1024 +/* Minimum input (Tx) requests to be enqueued to ring doorbell */ +#define OCTEP_VF_DB_MIN 8 +/* Packet threshold for Tx queue interrupt */ +#define OCTEP_VF_IQ_INTR_THRESHOLD 0x0 + +/* Minimum watermark for backpressure */ +#define OCTEP_VF_OQ_WMARK_MIN 256 + +/* Rx Queue: maximum descriptors per ring */ +#define OCTEP_VF_OQ_MAX_DESCRIPTORS 1024 + +/* Rx buffer size: Use page size buffers. + * Build skb from allocated page buffer once the packet is received. + * When a gathered packet is received, make head page as skb head and + * page buffers in consecutive Rx descriptors as fragments. + */ +#define OCTEP_VF_OQ_BUF_SIZE (SKB_WITH_OVERHEAD(PAGE_SIZE)) +#define OCTEP_VF_OQ_PKTS_PER_INTR 128 +#define OCTEP_VF_OQ_REFILL_THRESHOLD (OCTEP_VF_OQ_MAX_DESCRIPTORS / 4) + +#define OCTEP_VF_OQ_INTR_PKT_THRESHOLD 1 +#define OCTEP_VF_OQ_INTR_TIME_THRESHOLD 10 + +#define OCTEP_VF_MSIX_NAME_SIZE (IFNAMSIZ + 32) + +/* Tx Queue wake threshold + * wakeup a stopped Tx queue if minimum 2 descriptors are available. + * Even a skb with fragments consume only one Tx queue descriptor entry. + */ +#define OCTEP_VF_WAKE_QUEUE_THRESHOLD 2 + +/* Minimum MTU supported by Octeon network interface */ +#define OCTEP_VF_MIN_MTU ETH_MIN_MTU +/* Maximum MTU supported by Octeon interface*/ +#define OCTEP_VF_MAX_MTU (10000 - (ETH_HLEN + ETH_FCS_LEN)) +/* Default MTU */ +#define OCTEP_VF_DEFAULT_MTU 1500 + +/* Macros to get octeon config params */ +#define CFG_GET_IQ_CFG(cfg) ((cfg)->iq) +#define CFG_GET_IQ_NUM_DESC(cfg) ((cfg)->iq.num_descs) +#define CFG_GET_IQ_INSTR_TYPE(cfg) ((cfg)->iq.instr_type) +#define CFG_GET_IQ_INSTR_SIZE(cfg) (64) +#define CFG_GET_IQ_DB_MIN(cfg) ((cfg)->iq.db_min) +#define CFG_GET_IQ_INTR_THRESHOLD(cfg) ((cfg)->iq.intr_threshold) + +#define CFG_GET_OQ_NUM_DESC(cfg) ((cfg)->oq.num_descs) +#define CFG_GET_OQ_BUF_SIZE(cfg) ((cfg)->oq.buf_size) +#define CFG_GET_OQ_REFILL_THRESHOLD(cfg) ((cfg)->oq.refill_threshold) +#define CFG_GET_OQ_INTR_PKT(cfg) ((cfg)->oq.oq_intr_pkt) +#define CFG_GET_OQ_INTR_TIME(cfg) ((cfg)->oq.oq_intr_time) +#define CFG_GET_OQ_WMARK(cfg) ((cfg)->oq.wmark) + +#define CFG_GET_PORTS_ACTIVE_IO_RINGS(cfg) ((cfg)->ring_cfg.active_io_rings) +#define CFG_GET_PORTS_MAX_IO_RINGS(cfg) ((cfg)->ring_cfg.max_io_rings) + +#define CFG_GET_CORE_TICS_PER_US(cfg) ((cfg)->core_cfg.core_tics_per_us) +#define CFG_GET_COPROC_TICS_PER_US(cfg) ((cfg)->core_cfg.coproc_tics_per_us) + +#define CFG_GET_IOQ_MSIX(cfg) ((cfg)->msix_cfg.ioq_msix) + +/* Hardware Tx Queue configuration. */ +struct octep_vf_iq_config { + /* Size of the Input queue (number of commands) */ + u16 num_descs; + + /* Command size - 32 or 64 bytes */ + u16 instr_type; + + /* Minimum number of commands pending to be posted to Octeon before driver + * hits the Input queue doorbell. + */ + u16 db_min; + + /* Trigger the IQ interrupt when processed cmd count reaches + * this level. + */ + u32 intr_threshold; +}; + +/* Hardware Rx Queue configuration. */ +struct octep_vf_oq_config { + /* Size of Output queue (number of descriptors) */ + u16 num_descs; + + /* Size of buffer in this Output queue. */ + u16 buf_size; + + /* The number of buffers that were consumed during packet processing + * by the driver on this Output queue before the driver attempts to + * replenish the descriptor ring with new buffers. + */ + u16 refill_threshold; + + /* Interrupt Coalescing (Packet Count). Octeon will interrupt the host + * only if it sent as many packets as specified by this field. + * The driver usually does not use packet count interrupt coalescing. + */ + u32 oq_intr_pkt; + + /* Interrupt Coalescing (Time Interval). Octeon will interrupt the host + * if at least one packet was sent in the time interval specified by + * this field. The driver uses time interval interrupt coalescing by + * default. The time is specified in microseconds. + */ + u32 oq_intr_time; + + /* Water mark for backpressure. + * Output queue sends backpressure signal to source when + * free buffer count falls below wmark. + */ + u32 wmark; +}; + +/* Tx/Rx configuration */ +struct octep_vf_ring_config { + /* Max number of IOQs */ + u16 max_io_rings; + + /* Number of active IOQs */ + u16 active_io_rings; +}; + +/* Octeon MSI-x config. */ +struct octep_vf_msix_config { + /* Number of IOQ interrupts */ + u16 ioq_msix; +}; + +/* Data Structure to hold configuration limits and active config */ +struct octep_vf_config { + /* Input Queue attributes. */ + struct octep_vf_iq_config iq; + + /* Output Queue attributes. */ + struct octep_vf_oq_config oq; + + /* MSI-X interrupt config */ + struct octep_vf_msix_config msix_cfg; + + /* NIC VF ring Configuration */ + struct octep_vf_ring_config ring_cfg; +}; +#endif /* _OCTEP_VF_CONFIG_H_ */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_ethtool.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_ethtool.c new file mode 100644 index 000000000000..a1979b45e355 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_ethtool.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/ethtool.h> + +#include "octep_vf_config.h" +#include "octep_vf_main.h" + +static const char octep_vf_gstrings_global_stats[][ETH_GSTRING_LEN] = { + "rx_alloc_errors", + "tx_busy_errors", + "tx_hw_pkts", + "tx_hw_octs", + "tx_hw_bcast", + "tx_hw_mcast", + "rx_hw_pkts", + "rx_hw_bytes", + "rx_hw_bcast", + "rx_dropped_bytes_fifo_full", +}; + +#define OCTEP_VF_GLOBAL_STATS_CNT (sizeof(octep_vf_gstrings_global_stats) / ETH_GSTRING_LEN) + +static const char octep_vf_gstrings_tx_q_stats[][ETH_GSTRING_LEN] = { + "tx_packets_posted[Q-%u]", + "tx_packets_completed[Q-%u]", + "tx_bytes[Q-%u]", + "tx_busy[Q-%u]", +}; + +#define OCTEP_VF_TX_Q_STATS_CNT (sizeof(octep_vf_gstrings_tx_q_stats) / ETH_GSTRING_LEN) + +static const char octep_vf_gstrings_rx_q_stats[][ETH_GSTRING_LEN] = { + "rx_packets[Q-%u]", + "rx_bytes[Q-%u]", + "rx_alloc_errors[Q-%u]", +}; + +#define OCTEP_VF_RX_Q_STATS_CNT (sizeof(octep_vf_gstrings_rx_q_stats) / ETH_GSTRING_LEN) + +static void octep_vf_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + + strscpy(info->driver, OCTEP_VF_DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(oct->pdev), sizeof(info->bus_info)); +} + +static void octep_vf_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + u16 num_queues = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + char *strings = (char *)data; + int i, j; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < OCTEP_VF_GLOBAL_STATS_CNT; i++) { + snprintf(strings, ETH_GSTRING_LEN, + octep_vf_gstrings_global_stats[i]); + strings += ETH_GSTRING_LEN; + } + + for (i = 0; i < num_queues; i++) { + for (j = 0; j < OCTEP_VF_TX_Q_STATS_CNT; j++) { + snprintf(strings, ETH_GSTRING_LEN, + octep_vf_gstrings_tx_q_stats[j], i); + strings += ETH_GSTRING_LEN; + } + } + + for (i = 0; i < num_queues; i++) { + for (j = 0; j < OCTEP_VF_RX_Q_STATS_CNT; j++) { + snprintf(strings, ETH_GSTRING_LEN, + octep_vf_gstrings_rx_q_stats[j], i); + strings += ETH_GSTRING_LEN; + } + } + break; + default: + break; + } +} + +static int octep_vf_get_sset_count(struct net_device *netdev, int sset) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + u16 num_queues = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); + + switch (sset) { + case ETH_SS_STATS: + return OCTEP_VF_GLOBAL_STATS_CNT + (num_queues * + (OCTEP_VF_TX_Q_STATS_CNT + OCTEP_VF_RX_Q_STATS_CNT)); + break; + default: + return -EOPNOTSUPP; + } +} + +static void octep_vf_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + struct octep_vf_iface_tx_stats *iface_tx_stats; + struct octep_vf_iface_rx_stats *iface_rx_stats; + u64 rx_alloc_errors, tx_busy_errors; + int q, i; + + rx_alloc_errors = 0; + tx_busy_errors = 0; + + octep_vf_get_if_stats(oct); + iface_tx_stats = &oct->iface_tx_stats; + iface_rx_stats = &oct->iface_rx_stats; + + for (q = 0; q < oct->num_oqs; q++) { + struct octep_vf_iq *iq = oct->iq[q]; + struct octep_vf_oq *oq = oct->oq[q]; + + tx_busy_errors += iq->stats.tx_busy; + rx_alloc_errors += oq->stats.alloc_failures; + } + i = 0; + data[i++] = rx_alloc_errors; + data[i++] = tx_busy_errors; + data[i++] = iface_tx_stats->pkts; + data[i++] = iface_tx_stats->octs; + data[i++] = iface_tx_stats->bcst; + data[i++] = iface_tx_stats->mcst; + data[i++] = iface_rx_stats->pkts; + data[i++] = iface_rx_stats->octets; + data[i++] = iface_rx_stats->bcast_pkts; + data[i++] = iface_rx_stats->dropped_octets_fifo_full; + + /* Per Tx Queue stats */ + for (q = 0; q < oct->num_iqs; q++) { + struct octep_vf_iq *iq = oct->iq[q]; + + data[i++] = iq->stats.instr_posted; + data[i++] = iq->stats.instr_completed; + data[i++] = iq->stats.bytes_sent; + data[i++] = iq->stats.tx_busy; + } + + /* Per Rx Queue stats */ + for (q = 0; q < oct->num_oqs; q++) { + struct octep_vf_oq *oq = oct->oq[q]; + + data[i++] = oq->stats.packets; + data[i++] = oq->stats.bytes; + data[i++] = oq->stats.alloc_failures; + } +} + +#define OCTEP_VF_SET_ETHTOOL_LINK_MODES_BITMAP(octep_vf_speeds, ksettings, name) \ +{ \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_T)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseT_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_R)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseR_FEC); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_CR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseCR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_KR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseKR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_LR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseLR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_10GBASE_SR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 10000baseSR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_25GBASE_CR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 25000baseCR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_25GBASE_KR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 25000baseKR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_25GBASE_SR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 25000baseSR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_CR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseCR4_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_KR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseKR4_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_LR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseLR4_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_40GBASE_SR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 40000baseSR4_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_CR2)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseCR2_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_KR2)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseKR2_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_SR2)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseSR2_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_CR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseCR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_KR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseKR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_LR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseLR_ER_FR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_50GBASE_SR)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 50000baseSR_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_CR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseCR4_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_KR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseKR4_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_LR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseLR4_ER4_Full); \ + if ((octep_vf_speeds) & BIT(OCTEP_VF_LINK_MODE_100GBASE_SR4)) \ + ethtool_link_ksettings_add_link_mode(ksettings, name, 100000baseSR4_Full); \ +} + +static int octep_vf_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + struct octep_vf_iface_link_info *link_info; + u32 advertised_modes, supported_modes; + + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + + octep_vf_get_link_info(oct); + + advertised_modes = oct->link_info.advertised_modes; + supported_modes = oct->link_info.supported_modes; + link_info = &oct->link_info; + + OCTEP_VF_SET_ETHTOOL_LINK_MODES_BITMAP(supported_modes, cmd, supported); + OCTEP_VF_SET_ETHTOOL_LINK_MODES_BITMAP(advertised_modes, cmd, advertising); + + if (link_info->autoneg) { + if (link_info->autoneg & OCTEP_VF_LINK_MODE_AUTONEG_SUPPORTED) + ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); + if (link_info->autoneg & OCTEP_VF_LINK_MODE_AUTONEG_ADVERTISED) { + ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); + cmd->base.autoneg = AUTONEG_ENABLE; + } else { + cmd->base.autoneg = AUTONEG_DISABLE; + } + } else { + cmd->base.autoneg = AUTONEG_DISABLE; + } + + cmd->base.port = PORT_FIBRE; + ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); + ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); + + if (netif_carrier_ok(netdev)) { + cmd->base.speed = link_info->speed; + cmd->base.duplex = DUPLEX_FULL; + } else { + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + } + return 0; +} + +static const struct ethtool_ops octep_vf_ethtool_ops = { + .get_drvinfo = octep_vf_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = octep_vf_get_strings, + .get_sset_count = octep_vf_get_sset_count, + .get_ethtool_stats = octep_vf_get_ethtool_stats, + .get_link_ksettings = octep_vf_get_link_ksettings, +}; + +void octep_vf_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &octep_vf_ethtool_ops; +} diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c new file mode 100644 index 000000000000..dd49d0b8b494 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c @@ -0,0 +1,1231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/aer.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/rtnetlink.h> +#include <linux/vmalloc.h> +#include <net/netdev_queues.h> + +#include "octep_vf_config.h" +#include "octep_vf_main.h" + +struct workqueue_struct *octep_vf_wq; + +/* Supported Devices */ +static const struct pci_device_id octep_vf_pci_id_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CN93_VF)}, + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CNF95N_VF)}, + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CN98_VF)}, + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CN10KA_VF)}, + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CNF10KA_VF)}, + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CNF10KB_VF)}, + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CN10KB_VF)}, + {0, }, +}; +MODULE_DEVICE_TABLE(pci, octep_vf_pci_id_tbl); + +MODULE_AUTHOR("Veerasenareddy Burru <vburru@marvell.com>"); +MODULE_DESCRIPTION(OCTEP_VF_DRV_STRING); +MODULE_LICENSE("GPL"); + +/** + * octep_vf_alloc_ioq_vectors() - Allocate Tx/Rx Queue interrupt info. + * + * @oct: Octeon device private data structure. + * + * Allocate resources to hold per Tx/Rx queue interrupt info. + * This is the information passed to interrupt handler, from which napi poll + * is scheduled and includes quick access to private data of Tx/Rx queue + * corresponding to the interrupt being handled. + * + * Return: 0, on successful allocation of resources for all queue interrupts. + * -1, if failed to allocate any resource. + */ +static int octep_vf_alloc_ioq_vectors(struct octep_vf_device *oct) +{ + struct octep_vf_ioq_vector *ioq_vector; + int i; + + for (i = 0; i < oct->num_oqs; i++) { + oct->ioq_vector[i] = vzalloc(sizeof(*oct->ioq_vector[i])); + if (!oct->ioq_vector[i]) + goto free_ioq_vector; + + ioq_vector = oct->ioq_vector[i]; + ioq_vector->iq = oct->iq[i]; + ioq_vector->oq = oct->oq[i]; + ioq_vector->octep_vf_dev = oct; + } + + dev_info(&oct->pdev->dev, "Allocated %d IOQ vectors\n", oct->num_oqs); + return 0; + +free_ioq_vector: + while (i) { + i--; + vfree(oct->ioq_vector[i]); + oct->ioq_vector[i] = NULL; + } + return -1; +} + +/** + * octep_vf_free_ioq_vectors() - Free Tx/Rx Queue interrupt vector info. + * + * @oct: Octeon device private data structure. + */ +static void octep_vf_free_ioq_vectors(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_oqs; i++) { + if (oct->ioq_vector[i]) { + vfree(oct->ioq_vector[i]); + oct->ioq_vector[i] = NULL; + } + } + netdev_info(oct->netdev, "Freed IOQ Vectors\n"); +} + +/** + * octep_vf_enable_msix_range() - enable MSI-x interrupts. + * + * @oct: Octeon device private data structure. + * + * Allocate and enable all MSI-x interrupts (queue and non-queue interrupts) + * for the Octeon device. + * + * Return: 0, on successfully enabling all MSI-x interrupts. + * -1, if failed to enable any MSI-x interrupt. + */ +static int octep_vf_enable_msix_range(struct octep_vf_device *oct) +{ + int num_msix, msix_allocated; + int i; + + /* Generic interrupts apart from input/output queues */ + //num_msix = oct->num_oqs + CFG_GET_NON_IOQ_MSIX(oct->conf); + num_msix = oct->num_oqs; + oct->msix_entries = kcalloc(num_msix, sizeof(struct msix_entry), GFP_KERNEL); + if (!oct->msix_entries) + goto msix_alloc_err; + + for (i = 0; i < num_msix; i++) + oct->msix_entries[i].entry = i; + + msix_allocated = pci_enable_msix_range(oct->pdev, oct->msix_entries, + num_msix, num_msix); + if (msix_allocated != num_msix) { + dev_err(&oct->pdev->dev, + "Failed to enable %d msix irqs; got only %d\n", + num_msix, msix_allocated); + goto enable_msix_err; + } + oct->num_irqs = msix_allocated; + dev_info(&oct->pdev->dev, "MSI-X enabled successfully\n"); + + return 0; + +enable_msix_err: + if (msix_allocated > 0) + pci_disable_msix(oct->pdev); + kfree(oct->msix_entries); + oct->msix_entries = NULL; +msix_alloc_err: + return -1; +} + +/** + * octep_vf_disable_msix() - disable MSI-x interrupts. + * + * @oct: Octeon device private data structure. + * + * Disable MSI-x on the Octeon device. + */ +static void octep_vf_disable_msix(struct octep_vf_device *oct) +{ + pci_disable_msix(oct->pdev); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + dev_info(&oct->pdev->dev, "Disabled MSI-X\n"); +} + +/** + * octep_vf_ioq_intr_handler() - handler for all Tx/Rx queue interrupts. + * + * @irq: Interrupt number. + * @data: interrupt data contains pointers to Tx/Rx queue private data + * and correspong NAPI context. + * + * this is common handler for all non-queue (generic) interrupts. + */ +static irqreturn_t octep_vf_ioq_intr_handler(int irq, void *data) +{ + struct octep_vf_ioq_vector *ioq_vector = data; + struct octep_vf_device *oct = ioq_vector->octep_vf_dev; + + return oct->hw_ops.ioq_intr_handler(ioq_vector); +} + +/** + * octep_vf_request_irqs() - Register interrupt handlers. + * + * @oct: Octeon device private data structure. + * + * Register handlers for all queue and non-queue interrupts. + * + * Return: 0, on successful registration of all interrupt handlers. + * -1, on any error. + */ +static int octep_vf_request_irqs(struct octep_vf_device *oct) +{ + struct net_device *netdev = oct->netdev; + struct octep_vf_ioq_vector *ioq_vector; + struct msix_entry *msix_entry; + int ret, i; + + /* Request IRQs for Tx/Rx queues */ + for (i = 0; i < oct->num_oqs; i++) { + ioq_vector = oct->ioq_vector[i]; + msix_entry = &oct->msix_entries[i]; + + snprintf(ioq_vector->name, sizeof(ioq_vector->name), + "%s-q%d", netdev->name, i); + ret = request_irq(msix_entry->vector, + octep_vf_ioq_intr_handler, 0, + ioq_vector->name, ioq_vector); + if (ret) { + netdev_err(netdev, + "request_irq failed for Q-%d; err=%d", + i, ret); + goto ioq_irq_err; + } + + cpumask_set_cpu(i % num_online_cpus(), + &ioq_vector->affinity_mask); + irq_set_affinity_hint(msix_entry->vector, + &ioq_vector->affinity_mask); + } + + return 0; +ioq_irq_err: + while (i) { + --i; + free_irq(oct->msix_entries[i].vector, oct); + } + return -1; +} + +/** + * octep_vf_free_irqs() - free all registered interrupts. + * + * @oct: Octeon device private data structure. + * + * Free all queue and non-queue interrupts of the Octeon device. + */ +static void octep_vf_free_irqs(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_irqs; i++) { + irq_set_affinity_hint(oct->msix_entries[i].vector, NULL); + free_irq(oct->msix_entries[i].vector, oct->ioq_vector[i]); + } + netdev_info(oct->netdev, "IRQs freed\n"); +} + +/** + * octep_vf_setup_irqs() - setup interrupts for the Octeon device. + * + * @oct: Octeon device private data structure. + * + * Allocate data structures to hold per interrupt information, allocate/enable + * MSI-x interrupt and register interrupt handlers. + * + * Return: 0, on successful allocation and registration of all interrupts. + * -1, on any error. + */ +static int octep_vf_setup_irqs(struct octep_vf_device *oct) +{ + if (octep_vf_alloc_ioq_vectors(oct)) + goto ioq_vector_err; + + if (octep_vf_enable_msix_range(oct)) + goto enable_msix_err; + + if (octep_vf_request_irqs(oct)) + goto request_irq_err; + + return 0; + +request_irq_err: + octep_vf_disable_msix(oct); +enable_msix_err: + octep_vf_free_ioq_vectors(oct); +ioq_vector_err: + return -1; +} + +/** + * octep_vf_clean_irqs() - free all interrupts and its resources. + * + * @oct: Octeon device private data structure. + */ +static void octep_vf_clean_irqs(struct octep_vf_device *oct) +{ + octep_vf_free_irqs(oct); + octep_vf_disable_msix(oct); + octep_vf_free_ioq_vectors(oct); +} + +/** + * octep_vf_enable_ioq_irq() - Enable MSI-x interrupt of a Tx/Rx queue. + * + * @iq: Octeon Tx queue data structure. + * @oq: Octeon Rx queue data structure. + */ +static void octep_vf_enable_ioq_irq(struct octep_vf_iq *iq, struct octep_vf_oq *oq) +{ + u32 pkts_pend = oq->pkts_pending; + + netdev_dbg(iq->netdev, "enabling intr for Q-%u\n", iq->q_no); + if (iq->pkts_processed) { + writel(iq->pkts_processed, iq->inst_cnt_reg); + iq->pkt_in_done -= iq->pkts_processed; + iq->pkts_processed = 0; + } + if (oq->last_pkt_count - pkts_pend) { + writel(oq->last_pkt_count - pkts_pend, oq->pkts_sent_reg); + oq->last_pkt_count = pkts_pend; + } + + /* Flush the previous wrties before writing to RESEND bit */ + smp_wmb(); + writeq(1UL << OCTEP_VF_OQ_INTR_RESEND_BIT, oq->pkts_sent_reg); + writeq(1UL << OCTEP_VF_IQ_INTR_RESEND_BIT, iq->inst_cnt_reg); +} + +/** + * octep_vf_napi_poll() - NAPI poll function for Tx/Rx. + * + * @napi: pointer to napi context. + * @budget: max number of packets to be processed in single invocation. + */ +static int octep_vf_napi_poll(struct napi_struct *napi, int budget) +{ + struct octep_vf_ioq_vector *ioq_vector = + container_of(napi, struct octep_vf_ioq_vector, napi); + u32 tx_pending, rx_done; + + tx_pending = octep_vf_iq_process_completions(ioq_vector->iq, 64); + rx_done = octep_vf_oq_process_rx(ioq_vector->oq, budget); + + /* need more polling if tx completion processing is still pending or + * processed at least 'budget' number of rx packets. + */ + if (tx_pending || rx_done >= budget) + return budget; + + if (likely(napi_complete_done(napi, rx_done))) + octep_vf_enable_ioq_irq(ioq_vector->iq, ioq_vector->oq); + + return rx_done; +} + +/** + * octep_vf_napi_add() - Add NAPI poll for all Tx/Rx queues. + * + * @oct: Octeon device private data structure. + */ +static void octep_vf_napi_add(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_oqs; i++) { + netdev_dbg(oct->netdev, "Adding NAPI on Q-%d\n", i); + netif_napi_add(oct->netdev, &oct->ioq_vector[i]->napi, octep_vf_napi_poll); + oct->oq[i]->napi = &oct->ioq_vector[i]->napi; + } +} + +/** + * octep_vf_napi_delete() - delete NAPI poll callback for all Tx/Rx queues. + * + * @oct: Octeon device private data structure. + */ +static void octep_vf_napi_delete(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_oqs; i++) { + netdev_dbg(oct->netdev, "Deleting NAPI on Q-%d\n", i); + netif_napi_del(&oct->ioq_vector[i]->napi); + oct->oq[i]->napi = NULL; + } +} + +/** + * octep_vf_napi_enable() - enable NAPI for all Tx/Rx queues. + * + * @oct: Octeon device private data structure. + */ +static void octep_vf_napi_enable(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_oqs; i++) { + netdev_dbg(oct->netdev, "Enabling NAPI on Q-%d\n", i); + napi_enable(&oct->ioq_vector[i]->napi); + } +} + +/** + * octep_vf_napi_disable() - disable NAPI for all Tx/Rx queues. + * + * @oct: Octeon device private data structure. + */ +static void octep_vf_napi_disable(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_oqs; i++) { + netdev_dbg(oct->netdev, "Disabling NAPI on Q-%d\n", i); + napi_disable(&oct->ioq_vector[i]->napi); + } +} + +static void octep_vf_link_up(struct net_device *netdev) +{ + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); +} + +static void octep_vf_set_rx_state(struct octep_vf_device *oct, bool up) +{ + int err; + + err = octep_vf_mbox_set_rx_state(oct, up); + if (err) + netdev_err(oct->netdev, "Set Rx state to %d failed with err:%d\n", up, err); +} + +static int octep_vf_get_link_status(struct octep_vf_device *oct) +{ + int err; + + err = octep_vf_mbox_get_link_status(oct, &oct->link_info.oper_up); + if (err) + netdev_err(oct->netdev, "Get link status failed with err:%d\n", err); + return oct->link_info.oper_up; +} + +static void octep_vf_set_link_status(struct octep_vf_device *oct, bool up) +{ + int err; + + err = octep_vf_mbox_set_link_status(oct, up); + if (err) { + netdev_err(oct->netdev, "Set link status to %d failed with err:%d\n", up, err); + return; + } + oct->link_info.oper_up = up; +} + +/** + * octep_vf_open() - start the octeon network device. + * + * @netdev: pointer to kernel network device. + * + * setup Tx/Rx queues, interrupts and enable hardware operation of Tx/Rx queues + * and interrupts.. + * + * Return: 0, on successfully setting up device and bring it up. + * -1, on any error. + */ +static int octep_vf_open(struct net_device *netdev) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + int err, ret; + + netdev_info(netdev, "Starting netdev ...\n"); + netif_carrier_off(netdev); + + oct->hw_ops.reset_io_queues(oct); + + if (octep_vf_setup_iqs(oct)) + goto setup_iq_err; + if (octep_vf_setup_oqs(oct)) + goto setup_oq_err; + if (octep_vf_setup_irqs(oct)) + goto setup_irq_err; + + err = netif_set_real_num_tx_queues(netdev, oct->num_oqs); + if (err) + goto set_queues_err; + err = netif_set_real_num_rx_queues(netdev, oct->num_iqs); + if (err) + goto set_queues_err; + + octep_vf_napi_add(oct); + octep_vf_napi_enable(oct); + + oct->link_info.admin_up = 1; + octep_vf_set_rx_state(oct, true); + + ret = octep_vf_get_link_status(oct); + if (!ret) + octep_vf_set_link_status(oct, true); + + /* Enable the input and output queues for this Octeon device */ + oct->hw_ops.enable_io_queues(oct); + + /* Enable Octeon device interrupts */ + oct->hw_ops.enable_interrupts(oct); + + octep_vf_oq_dbell_init(oct); + + ret = octep_vf_get_link_status(oct); + if (ret) + octep_vf_link_up(netdev); + + return 0; + +set_queues_err: + octep_vf_napi_disable(oct); + octep_vf_napi_delete(oct); + octep_vf_clean_irqs(oct); +setup_irq_err: + octep_vf_free_oqs(oct); +setup_oq_err: + octep_vf_free_iqs(oct); +setup_iq_err: + return -1; +} + +/** + * octep_vf_stop() - stop the octeon network device. + * + * @netdev: pointer to kernel network device. + * + * stop the device Tx/Rx operations, bring down the link and + * free up all resources allocated for Tx/Rx queues and interrupts. + */ +static int octep_vf_stop(struct net_device *netdev) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + + netdev_info(netdev, "Stopping the device ...\n"); + + /* Stop Tx from stack */ + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + octep_vf_set_link_status(oct, false); + octep_vf_set_rx_state(oct, false); + + oct->link_info.admin_up = 0; + oct->link_info.oper_up = 0; + + oct->hw_ops.disable_interrupts(oct); + octep_vf_napi_disable(oct); + octep_vf_napi_delete(oct); + + octep_vf_clean_irqs(oct); + octep_vf_clean_iqs(oct); + + oct->hw_ops.disable_io_queues(oct); + oct->hw_ops.reset_io_queues(oct); + octep_vf_free_oqs(oct); + octep_vf_free_iqs(oct); + netdev_info(netdev, "Device stopped !!\n"); + return 0; +} + +/** + * octep_vf_iq_full_check() - check if a Tx queue is full. + * + * @iq: Octeon Tx queue data structure. + * + * Return: 0, if the Tx queue is not full. + * 1, if the Tx queue is full. + */ +static int octep_vf_iq_full_check(struct octep_vf_iq *iq) +{ + int ret; + + ret = netif_subqueue_maybe_stop(iq->netdev, iq->q_no, IQ_INSTR_SPACE(iq), + OCTEP_VF_WAKE_QUEUE_THRESHOLD, + OCTEP_VF_WAKE_QUEUE_THRESHOLD); + switch (ret) { + case 0: /* Stopped the queue, since IQ is full */ + return 1; + case -1: /* + * Pending updates in write index from + * iq_process_completion in other cpus + * caused queues to get re-enabled after + * being stopped + */ + iq->stats.restart_cnt++; + fallthrough; + case 1: /* Queue left enabled, since IQ is not yet full*/ + return 0; + } + + return 1; +} + +/** + * octep_vf_start_xmit() - Enqueue packet to Octoen hardware Tx Queue. + * + * @skb: packet skbuff pointer. + * @netdev: kernel network device. + * + * Return: NETDEV_TX_BUSY, if Tx Queue is full. + * NETDEV_TX_OK, if successfully enqueued to hardware Tx queue. + */ +static netdev_tx_t octep_vf_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + netdev_features_t feat = netdev->features; + struct octep_vf_tx_sglist_desc *sglist; + struct octep_vf_tx_buffer *tx_buffer; + struct octep_vf_tx_desc_hw *hw_desc; + struct skb_shared_info *shinfo; + struct octep_vf_instr_hdr *ih; + struct octep_vf_iq *iq; + skb_frag_t *frag; + u16 nr_frags, si; + int xmit_more; + u16 q_no, wi; + + if (skb_put_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + + q_no = skb_get_queue_mapping(skb); + if (q_no >= oct->num_iqs) { + netdev_err(netdev, "Invalid Tx skb->queue_mapping=%d\n", q_no); + q_no = q_no % oct->num_iqs; + } + + iq = oct->iq[q_no]; + + shinfo = skb_shinfo(skb); + nr_frags = shinfo->nr_frags; + + wi = iq->host_write_index; + hw_desc = &iq->desc_ring[wi]; + hw_desc->ih64 = 0; + + tx_buffer = iq->buff_info + wi; + tx_buffer->skb = skb; + + ih = &hw_desc->ih; + ih->tlen = skb->len; + ih->pkind = oct->fw_info.pkind; + ih->fsz = oct->fw_info.fsz; + ih->tlen = skb->len + ih->fsz; + + if (!nr_frags) { + tx_buffer->gather = 0; + tx_buffer->dma = dma_map_single(iq->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(iq->dev, tx_buffer->dma)) + goto dma_map_err; + hw_desc->dptr = tx_buffer->dma; + } else { + /* Scatter/Gather */ + dma_addr_t dma; + u16 len; + + sglist = tx_buffer->sglist; + + ih->gsz = nr_frags + 1; + ih->gather = 1; + tx_buffer->gather = 1; + + len = skb_headlen(skb); + dma = dma_map_single(iq->dev, skb->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(iq->dev, dma)) + goto dma_map_err; + + memset(sglist, 0, OCTEP_VF_SGLIST_SIZE_PER_PKT); + sglist[0].len[3] = len; + sglist[0].dma_ptr[0] = dma; + + si = 1; /* entry 0 is main skb, mapped above */ + frag = &shinfo->frags[0]; + while (nr_frags--) { + len = skb_frag_size(frag); + dma = skb_frag_dma_map(iq->dev, frag, 0, + len, DMA_TO_DEVICE); + if (dma_mapping_error(iq->dev, dma)) + goto dma_map_sg_err; + + sglist[si >> 2].len[3 - (si & 3)] = len; + sglist[si >> 2].dma_ptr[si & 3] = dma; + + frag++; + si++; + } + hw_desc->dptr = tx_buffer->sglist_dma; + } + if (oct->fw_info.tx_ol_flags) { + if ((feat & (NETIF_F_TSO)) && (skb_is_gso(skb))) { + hw_desc->txm.ol_flags = OCTEP_VF_TX_OFFLOAD_CKSUM; + hw_desc->txm.ol_flags |= OCTEP_VF_TX_OFFLOAD_TSO; + hw_desc->txm.gso_size = skb_shinfo(skb)->gso_size; + hw_desc->txm.gso_segs = skb_shinfo(skb)->gso_segs; + } else if (feat & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { + hw_desc->txm.ol_flags = OCTEP_VF_TX_OFFLOAD_CKSUM; + } + /* due to ESR txm will be swapped by hw */ + hw_desc->txm64[0] = (__force u64)cpu_to_be64(hw_desc->txm64[0]); + } + + xmit_more = netdev_xmit_more(); + + netdev_tx_sent_queue(iq->netdev_q, skb->len); + + skb_tx_timestamp(skb); + iq->fill_cnt++; + wi++; + iq->host_write_index = wi & iq->ring_size_mask; + + /* octep_iq_full_check stops the queue and returns + * true if so, in case the queue has become full + * by inserting current packet. If so, we can + * go ahead and ring doorbell. + */ + if (!octep_vf_iq_full_check(iq) && xmit_more && + iq->fill_cnt < iq->fill_threshold) + return NETDEV_TX_OK; + + goto ring_dbell; + +dma_map_sg_err: + if (si > 0) { + dma_unmap_single(iq->dev, sglist[0].dma_ptr[0], + sglist[0].len[0], DMA_TO_DEVICE); + sglist[0].len[0] = 0; + } + while (si > 1) { + dma_unmap_page(iq->dev, sglist[si >> 2].dma_ptr[si & 3], + sglist[si >> 2].len[si & 3], DMA_TO_DEVICE); + sglist[si >> 2].len[si & 3] = 0; + si--; + } + tx_buffer->gather = 0; +dma_map_err: + dev_kfree_skb_any(skb); +ring_dbell: + /* Flush the hw descriptors before writing to doorbell */ + smp_wmb(); + writel(iq->fill_cnt, iq->doorbell_reg); + iq->stats.instr_posted += iq->fill_cnt; + iq->fill_cnt = 0; + return NETDEV_TX_OK; +} + +int octep_vf_get_if_stats(struct octep_vf_device *oct) +{ + struct octep_vf_iface_rxtx_stats vf_stats; + int ret, size; + + memset(&vf_stats, 0, sizeof(struct octep_vf_iface_rxtx_stats)); + ret = octep_vf_mbox_bulk_read(oct, OCTEP_PFVF_MBOX_CMD_GET_STATS, + (u8 *)&vf_stats, &size); + + if (ret) + return ret; + + memcpy(&oct->iface_rx_stats, &vf_stats.iface_rx_stats, + sizeof(struct octep_vf_iface_rx_stats)); + memcpy(&oct->iface_tx_stats, &vf_stats.iface_tx_stats, + sizeof(struct octep_vf_iface_tx_stats)); + + return 0; +} + +int octep_vf_get_link_info(struct octep_vf_device *oct) +{ + int ret, size; + + ret = octep_vf_mbox_bulk_read(oct, OCTEP_PFVF_MBOX_CMD_GET_LINK_INFO, + (u8 *)&oct->link_info, &size); + if (ret) { + dev_err(&oct->pdev->dev, "Get VF link info failed via VF Mbox\n"); + return ret; + } + return 0; +} + +/** + * octep_vf_get_stats64() - Get Octeon network device statistics. + * + * @netdev: kernel network device. + * @stats: pointer to stats structure to be filled in. + */ +static void octep_vf_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + u64 tx_packets, tx_bytes, rx_packets, rx_bytes; + int q; + + tx_packets = 0; + tx_bytes = 0; + rx_packets = 0; + rx_bytes = 0; + for (q = 0; q < oct->num_oqs; q++) { + struct octep_vf_iq *iq = oct->iq[q]; + struct octep_vf_oq *oq = oct->oq[q]; + + tx_packets += iq->stats.instr_completed; + tx_bytes += iq->stats.bytes_sent; + rx_packets += oq->stats.packets; + rx_bytes += oq->stats.bytes; + } + stats->tx_packets = tx_packets; + stats->tx_bytes = tx_bytes; + stats->rx_packets = rx_packets; + stats->rx_bytes = rx_bytes; + if (!octep_vf_get_if_stats(oct)) { + stats->multicast = oct->iface_rx_stats.mcast_pkts; + stats->rx_errors = oct->iface_rx_stats.err_pkts; + stats->rx_dropped = oct->iface_rx_stats.dropped_pkts_fifo_full + + oct->iface_rx_stats.err_pkts; + stats->rx_missed_errors = oct->iface_rx_stats.dropped_pkts_fifo_full; + stats->tx_dropped = oct->iface_tx_stats.dropped; + } +} + +/** + * octep_vf_tx_timeout_task - work queue task to Handle Tx queue timeout. + * + * @work: pointer to Tx queue timeout work_struct + * + * Stop and start the device so that it frees up all queue resources + * and restarts the queues, that potentially clears a Tx queue timeout + * condition. + **/ +static void octep_vf_tx_timeout_task(struct work_struct *work) +{ + struct octep_vf_device *oct = container_of(work, struct octep_vf_device, + tx_timeout_task); + struct net_device *netdev = oct->netdev; + + rtnl_lock(); + if (netif_running(netdev)) { + octep_vf_stop(netdev); + octep_vf_open(netdev); + } + rtnl_unlock(); + netdev_put(netdev, NULL); +} + +/** + * octep_vf_tx_timeout() - Handle Tx Queue timeout. + * + * @netdev: pointer to kernel network device. + * @txqueue: Timed out Tx queue number. + * + * Schedule a work to handle Tx queue timeout. + */ +static void octep_vf_tx_timeout(struct net_device *netdev, unsigned int txqueue) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + + netdev_hold(netdev, NULL, GFP_ATOMIC); + schedule_work(&oct->tx_timeout_task); +} + +static int octep_vf_set_mac(struct net_device *netdev, void *p) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + struct sockaddr *addr = (struct sockaddr *)p; + int err; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + err = octep_vf_mbox_set_mac_addr(oct, addr->sa_data); + if (err) + return err; + + memcpy(oct->mac_addr, addr->sa_data, ETH_ALEN); + eth_hw_addr_set(netdev, addr->sa_data); + + return 0; +} + +static int octep_vf_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + struct octep_vf_iface_link_info *link_info; + int err; + + link_info = &oct->link_info; + if (link_info->mtu == new_mtu) + return 0; + + err = octep_vf_mbox_set_mtu(oct, new_mtu); + if (!err) { + oct->link_info.mtu = new_mtu; + netdev->mtu = new_mtu; + } + return err; +} + +static int octep_vf_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct octep_vf_device *oct = netdev_priv(netdev); + u16 rx_offloads = 0, tx_offloads = 0; + int err; + + /* We only support features received from firmware */ + if ((features & netdev->hw_features) != features) + return -EINVAL; + + if (features & NETIF_F_TSO) + tx_offloads |= OCTEP_VF_TX_OFFLOAD_TSO; + + if (features & NETIF_F_TSO6) + tx_offloads |= OCTEP_VF_TX_OFFLOAD_TSO; + + if (features & NETIF_F_IP_CSUM) + tx_offloads |= OCTEP_VF_TX_OFFLOAD_CKSUM; + + if (features & NETIF_F_IPV6_CSUM) + tx_offloads |= OCTEP_VF_TX_OFFLOAD_CKSUM; + + if (features & NETIF_F_RXCSUM) + rx_offloads |= OCTEP_VF_RX_OFFLOAD_CKSUM; + + err = octep_vf_mbox_set_offloads(oct, tx_offloads, rx_offloads); + if (!err) + netdev->features = features; + + return err; +} + +static const struct net_device_ops octep_vf_netdev_ops = { + .ndo_open = octep_vf_open, + .ndo_stop = octep_vf_stop, + .ndo_start_xmit = octep_vf_start_xmit, + .ndo_get_stats64 = octep_vf_get_stats64, + .ndo_tx_timeout = octep_vf_tx_timeout, + .ndo_set_mac_address = octep_vf_set_mac, + .ndo_change_mtu = octep_vf_change_mtu, + .ndo_set_features = octep_vf_set_features, +}; + +static const char *octep_vf_devid_to_str(struct octep_vf_device *oct) +{ + switch (oct->chip_id) { + case OCTEP_PCI_DEVICE_ID_CN93_VF: + return "CN93XX"; + case OCTEP_PCI_DEVICE_ID_CNF95N_VF: + return "CNF95N"; + case OCTEP_PCI_DEVICE_ID_CN10KA_VF: + return "CN10KA"; + case OCTEP_PCI_DEVICE_ID_CNF10KA_VF: + return "CNF10KA"; + case OCTEP_PCI_DEVICE_ID_CNF10KB_VF: + return "CNF10KB"; + case OCTEP_PCI_DEVICE_ID_CN10KB_VF: + return "CN10KB"; + default: + return "Unsupported"; + } +} + +/** + * octep_vf_device_setup() - Setup Octeon Device. + * + * @oct: Octeon device private data structure. + * + * Setup Octeon device hardware operations, configuration, etc ... + */ +int octep_vf_device_setup(struct octep_vf_device *oct) +{ + struct pci_dev *pdev = oct->pdev; + + /* allocate memory for oct->conf */ + oct->conf = kzalloc(sizeof(*oct->conf), GFP_KERNEL); + if (!oct->conf) + return -ENOMEM; + + /* Map BAR region 0 */ + oct->mmio.hw_addr = ioremap(pci_resource_start(oct->pdev, 0), + pci_resource_len(oct->pdev, 0)); + if (!oct->mmio.hw_addr) { + dev_err(&pdev->dev, + "Failed to remap BAR0; start=0x%llx len=0x%llx\n", + pci_resource_start(oct->pdev, 0), + pci_resource_len(oct->pdev, 0)); + goto ioremap_err; + } + oct->mmio.mapped = 1; + + oct->chip_id = pdev->device; + oct->rev_id = pdev->revision; + dev_info(&pdev->dev, "chip_id = 0x%x\n", pdev->device); + + switch (oct->chip_id) { + case OCTEP_PCI_DEVICE_ID_CN93_VF: + case OCTEP_PCI_DEVICE_ID_CNF95N_VF: + case OCTEP_PCI_DEVICE_ID_CN98_VF: + dev_info(&pdev->dev, "Setting up OCTEON %s VF PASS%d.%d\n", + octep_vf_devid_to_str(oct), OCTEP_VF_MAJOR_REV(oct), + OCTEP_VF_MINOR_REV(oct)); + octep_vf_device_setup_cn93(oct); + break; + case OCTEP_PCI_DEVICE_ID_CNF10KA_VF: + case OCTEP_PCI_DEVICE_ID_CN10KA_VF: + case OCTEP_PCI_DEVICE_ID_CNF10KB_VF: + case OCTEP_PCI_DEVICE_ID_CN10KB_VF: + dev_info(&pdev->dev, "Setting up OCTEON %s VF PASS%d.%d\n", + octep_vf_devid_to_str(oct), OCTEP_VF_MAJOR_REV(oct), + OCTEP_VF_MINOR_REV(oct)); + octep_vf_device_setup_cnxk(oct); + break; + default: + dev_err(&pdev->dev, "Unsupported device\n"); + goto unsupported_dev; + } + + return 0; + +unsupported_dev: + iounmap(oct->mmio.hw_addr); +ioremap_err: + kfree(oct->conf); + return -EOPNOTSUPP; +} + +/** + * octep_vf_device_cleanup() - Cleanup Octeon Device. + * + * @oct: Octeon device private data structure. + * + * Cleanup Octeon device allocated resources. + */ +static void octep_vf_device_cleanup(struct octep_vf_device *oct) +{ + dev_info(&oct->pdev->dev, "Cleaning up Octeon Device ...\n"); + + if (oct->mmio.mapped) + iounmap(oct->mmio.hw_addr); + + kfree(oct->conf); + oct->conf = NULL; +} + +static int octep_vf_get_mac_addr(struct octep_vf_device *oct, u8 *addr) +{ + return octep_vf_mbox_get_mac_addr(oct, addr); +} + +/** + * octep_vf_probe() - Octeon PCI device probe handler. + * + * @pdev: PCI device structure. + * @ent: entry in Octeon PCI device ID table. + * + * Initializes and enables the Octeon PCI device for network operations. + * Initializes Octeon private data structure and registers a network device. + */ +static int octep_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct octep_vf_device *octep_vf_dev; + struct net_device *netdev; + int err; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to enable PCI device\n"); + return err; + } + + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (err) { + dev_err(&pdev->dev, "Failed to set DMA mask !!\n"); + goto disable_pci_device; + } + + err = pci_request_mem_regions(pdev, OCTEP_VF_DRV_NAME); + if (err) { + dev_err(&pdev->dev, "Failed to map PCI memory regions\n"); + goto disable_pci_device; + } + + pci_set_master(pdev); + + netdev = alloc_etherdev_mq(sizeof(struct octep_vf_device), + OCTEP_VF_MAX_QUEUES); + if (!netdev) { + dev_err(&pdev->dev, "Failed to allocate netdev\n"); + err = -ENOMEM; + goto mem_regions_release; + } + SET_NETDEV_DEV(netdev, &pdev->dev); + + octep_vf_dev = netdev_priv(netdev); + octep_vf_dev->netdev = netdev; + octep_vf_dev->pdev = pdev; + octep_vf_dev->dev = &pdev->dev; + pci_set_drvdata(pdev, octep_vf_dev); + + err = octep_vf_device_setup(octep_vf_dev); + if (err) { + dev_err(&pdev->dev, "Device setup failed\n"); + goto netdevice_free; + } + INIT_WORK(&octep_vf_dev->tx_timeout_task, octep_vf_tx_timeout_task); + + netdev->netdev_ops = &octep_vf_netdev_ops; + octep_vf_set_ethtool_ops(netdev); + netif_carrier_off(netdev); + + if (octep_vf_setup_mbox(octep_vf_dev)) { + dev_err(&pdev->dev, "VF Mailbox setup failed\n"); + err = -ENOMEM; + goto device_cleanup; + } + + if (octep_vf_mbox_version_check(octep_vf_dev)) { + dev_err(&pdev->dev, "PF VF Mailbox version mismatch\n"); + err = -EINVAL; + goto delete_mbox; + } + + if (octep_vf_mbox_get_fw_info(octep_vf_dev)) { + dev_err(&pdev->dev, "unable to get fw info\n"); + err = -EINVAL; + goto delete_mbox; + } + + netdev->hw_features = NETIF_F_SG; + if (OCTEP_VF_TX_IP_CSUM(octep_vf_dev->fw_info.tx_ol_flags)) + netdev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); + + if (OCTEP_VF_RX_IP_CSUM(octep_vf_dev->fw_info.rx_ol_flags)) + netdev->hw_features |= NETIF_F_RXCSUM; + + netdev->min_mtu = OCTEP_VF_MIN_MTU; + netdev->max_mtu = OCTEP_VF_MAX_MTU; + netdev->mtu = OCTEP_VF_DEFAULT_MTU; + + if (OCTEP_VF_TX_TSO(octep_vf_dev->fw_info.tx_ol_flags)) { + netdev->hw_features |= NETIF_F_TSO; + netif_set_tso_max_size(netdev, netdev->max_mtu); + } + + netdev->features |= netdev->hw_features; + octep_vf_get_mac_addr(octep_vf_dev, octep_vf_dev->mac_addr); + eth_hw_addr_set(netdev, octep_vf_dev->mac_addr); + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "Failed to register netdev\n"); + goto delete_mbox; + } + dev_info(&pdev->dev, "Device probe successful\n"); + return 0; + +delete_mbox: + octep_vf_delete_mbox(octep_vf_dev); +device_cleanup: + octep_vf_device_cleanup(octep_vf_dev); +netdevice_free: + free_netdev(netdev); +mem_regions_release: + pci_release_mem_regions(pdev); +disable_pci_device: + pci_disable_device(pdev); + dev_err(&pdev->dev, "Device probe failed\n"); + return err; +} + +/** + * octep_vf_remove() - Remove Octeon PCI device from driver control. + * + * @pdev: PCI device structure of the Octeon device. + * + * Cleanup all resources allocated for the Octeon device. + * Unregister from network device and disable the PCI device. + */ +static void octep_vf_remove(struct pci_dev *pdev) +{ + struct octep_vf_device *oct = pci_get_drvdata(pdev); + struct net_device *netdev; + + if (!oct) + return; + + octep_vf_mbox_dev_remove(oct); + cancel_work_sync(&oct->tx_timeout_task); + netdev = oct->netdev; + if (netdev->reg_state == NETREG_REGISTERED) + unregister_netdev(netdev); + octep_vf_delete_mbox(oct); + octep_vf_device_cleanup(oct); + pci_release_mem_regions(pdev); + free_netdev(netdev); + pci_disable_device(pdev); +} + +static struct pci_driver octep_vf_driver = { + .name = OCTEP_VF_DRV_NAME, + .id_table = octep_vf_pci_id_tbl, + .probe = octep_vf_probe, + .remove = octep_vf_remove, +}; + +/** + * octep_vf_init_module() - Module initialization. + * + * create common resource for the driver and register PCI driver. + */ +static int __init octep_vf_init_module(void) +{ + int ret; + + pr_info("%s: Loading %s ...\n", OCTEP_VF_DRV_NAME, OCTEP_VF_DRV_STRING); + + ret = pci_register_driver(&octep_vf_driver); + if (ret < 0) { + pr_err("%s: Failed to register PCI driver; err=%d\n", + OCTEP_VF_DRV_NAME, ret); + return ret; + } + + return ret; +} + +/** + * octep_vf_exit_module() - Module exit routine. + * + * unregister the driver with PCI subsystem and cleanup common resources. + */ +static void __exit octep_vf_exit_module(void) +{ + pr_info("%s: Unloading ...\n", OCTEP_VF_DRV_NAME); + + pci_unregister_driver(&octep_vf_driver); + + pr_info("%s: Unloading complete\n", OCTEP_VF_DRV_NAME); +} + +module_init(octep_vf_init_module); +module_exit(octep_vf_exit_module); diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h new file mode 100644 index 000000000000..5769f62545cd --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h @@ -0,0 +1,334 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#ifndef _OCTEP_VF_MAIN_H_ +#define _OCTEP_VF_MAIN_H_ + +#include "octep_vf_tx.h" +#include "octep_vf_rx.h" +#include "octep_vf_mbox.h" + +#define OCTEP_VF_DRV_NAME "octeon_ep_vf" +#define OCTEP_VF_DRV_STRING "Marvell Octeon EndPoint NIC VF Driver" + +#define OCTEP_PCI_DEVICE_ID_CN93_VF 0xB203 //93xx VF +#define OCTEP_PCI_DEVICE_ID_CNF95N_VF 0xB403 //95N VF +#define OCTEP_PCI_DEVICE_ID_CN98_VF 0xB103 +#define OCTEP_PCI_DEVICE_ID_CN10KA_VF 0xB903 +#define OCTEP_PCI_DEVICE_ID_CNF10KA_VF 0xBA03 +#define OCTEP_PCI_DEVICE_ID_CNF10KB_VF 0xBC03 +#define OCTEP_PCI_DEVICE_ID_CN10KB_VF 0xBD03 + +#define OCTEP_VF_MAX_QUEUES 63 +#define OCTEP_VF_MAX_IQ OCTEP_VF_MAX_QUEUES +#define OCTEP_VF_MAX_OQ OCTEP_VF_MAX_QUEUES + +#define OCTEP_VF_MAX_MSIX_VECTORS OCTEP_VF_MAX_OQ + +#define OCTEP_VF_IQ_INTR_RESEND_BIT 59 +#define OCTEP_VF_OQ_INTR_RESEND_BIT 59 + +#define IQ_INSTR_PENDING(iq) ({ typeof(iq) iq__ = (iq); \ + ((iq__)->host_write_index - (iq__)->flush_index) & \ + (iq__)->ring_size_mask; \ + }) +#define IQ_INSTR_SPACE(iq) ({ typeof(iq) iq_ = (iq); \ + (iq_)->max_count - IQ_INSTR_PENDING(iq_); \ + }) + +/* PCI address space mapping information. + * Each of the 3 address spaces given by BAR0, BAR2 and BAR4 of + * Octeon gets mapped to different physical address spaces in + * the kernel. + */ +struct octep_vf_mmio { + /* The physical address to which the PCI address space is mapped. */ + u8 __iomem *hw_addr; + + /* Flag indicating the mapping was successful. */ + int mapped; +}; + +struct octep_vf_hw_ops { + void (*setup_iq_regs)(struct octep_vf_device *oct, int q); + void (*setup_oq_regs)(struct octep_vf_device *oct, int q); + void (*setup_mbox_regs)(struct octep_vf_device *oct, int mbox); + + irqreturn_t (*non_ioq_intr_handler)(void *ioq_vector); + irqreturn_t (*ioq_intr_handler)(void *ioq_vector); + void (*reinit_regs)(struct octep_vf_device *oct); + u32 (*update_iq_read_idx)(struct octep_vf_iq *iq); + + void (*enable_interrupts)(struct octep_vf_device *oct); + void (*disable_interrupts)(struct octep_vf_device *oct); + + void (*enable_io_queues)(struct octep_vf_device *oct); + void (*disable_io_queues)(struct octep_vf_device *oct); + void (*enable_iq)(struct octep_vf_device *oct, int q); + void (*disable_iq)(struct octep_vf_device *oct, int q); + void (*enable_oq)(struct octep_vf_device *oct, int q); + void (*disable_oq)(struct octep_vf_device *oct, int q); + void (*reset_io_queues)(struct octep_vf_device *oct); + void (*dump_registers)(struct octep_vf_device *oct); +}; + +/* Octeon mailbox data */ +struct octep_vf_mbox_data { + /* Holds the offset of received data via mailbox. */ + u32 data_index; + + /* Holds the received data via mailbox. */ + u8 recv_data[OCTEP_PFVF_MBOX_MAX_DATA_BUF_SIZE]; +}; + +/* wrappers around work structs */ +struct octep_vf_mbox_wk { + struct work_struct work; + void *ctxptr; +}; + +/* Octeon device mailbox */ +struct octep_vf_mbox { + /* A mutex to protect access to this q_mbox. */ + struct mutex lock; + + u32 state; + + /* SLI_MAC_PF_MBOX_INT for PF, SLI_PKT_MBOX_INT for VF. */ + u8 __iomem *mbox_int_reg; + + /* SLI_PKT_PF_VF_MBOX_SIG(0) for PF, + * SLI_PKT_PF_VF_MBOX_SIG(1) for VF. + */ + u8 __iomem *mbox_write_reg; + + /* SLI_PKT_PF_VF_MBOX_SIG(1) for PF, + * SLI_PKT_PF_VF_MBOX_SIG(0) for VF. + */ + u8 __iomem *mbox_read_reg; + + /* Octeon mailbox data */ + struct octep_vf_mbox_data mbox_data; + + /* Octeon mailbox work handler to process Mbox messages */ + struct octep_vf_mbox_wk wk; +}; + +/* Tx/Rx queue vector per interrupt. */ +struct octep_vf_ioq_vector { + char name[OCTEP_VF_MSIX_NAME_SIZE]; + struct napi_struct napi; + struct octep_vf_device *octep_vf_dev; + struct octep_vf_iq *iq; + struct octep_vf_oq *oq; + cpumask_t affinity_mask; +}; + +/* Octeon hardware/firmware offload capability flags. */ +#define OCTEP_VF_CAP_TX_CHECKSUM BIT(0) +#define OCTEP_VF_CAP_RX_CHECKSUM BIT(1) +#define OCTEP_VF_CAP_TSO BIT(2) + +/* Link modes */ +enum octep_vf_link_mode_bit_indices { + OCTEP_VF_LINK_MODE_10GBASE_T = 0, + OCTEP_VF_LINK_MODE_10GBASE_R, + OCTEP_VF_LINK_MODE_10GBASE_CR, + OCTEP_VF_LINK_MODE_10GBASE_KR, + OCTEP_VF_LINK_MODE_10GBASE_LR, + OCTEP_VF_LINK_MODE_10GBASE_SR, + OCTEP_VF_LINK_MODE_25GBASE_CR, + OCTEP_VF_LINK_MODE_25GBASE_KR, + OCTEP_VF_LINK_MODE_25GBASE_SR, + OCTEP_VF_LINK_MODE_40GBASE_CR4, + OCTEP_VF_LINK_MODE_40GBASE_KR4, + OCTEP_VF_LINK_MODE_40GBASE_LR4, + OCTEP_VF_LINK_MODE_40GBASE_SR4, + OCTEP_VF_LINK_MODE_50GBASE_CR2, + OCTEP_VF_LINK_MODE_50GBASE_KR2, + OCTEP_VF_LINK_MODE_50GBASE_SR2, + OCTEP_VF_LINK_MODE_50GBASE_CR, + OCTEP_VF_LINK_MODE_50GBASE_KR, + OCTEP_VF_LINK_MODE_50GBASE_LR, + OCTEP_VF_LINK_MODE_50GBASE_SR, + OCTEP_VF_LINK_MODE_100GBASE_CR4, + OCTEP_VF_LINK_MODE_100GBASE_KR4, + OCTEP_VF_LINK_MODE_100GBASE_LR4, + OCTEP_VF_LINK_MODE_100GBASE_SR4, + OCTEP_VF_LINK_MODE_NBITS +}; + +/* Hardware interface link state information. */ +struct octep_vf_iface_link_info { + /* Bitmap of Supported link speeds/modes. */ + u64 supported_modes; + + /* Bitmap of Advertised link speeds/modes. */ + u64 advertised_modes; + + /* Negotiated link speed in Mbps. */ + u32 speed; + + /* MTU */ + u16 mtu; + + /* Autonegotiation state. */ +#define OCTEP_VF_LINK_MODE_AUTONEG_SUPPORTED BIT(0) +#define OCTEP_VF_LINK_MODE_AUTONEG_ADVERTISED BIT(1) + u8 autoneg; + + /* Pause frames setting. */ +#define OCTEP_VF_LINK_MODE_PAUSE_SUPPORTED BIT(0) +#define OCTEP_VF_LINK_MODE_PAUSE_ADVERTISED BIT(1) + u8 pause; + + /* Admin state of the link (ifconfig <iface> up/down */ + u8 admin_up; + + /* Operational state of the link: physical link is up down */ + u8 oper_up; +}; + +/* Hardware interface stats information. */ +struct octep_vf_iface_rxtx_stats { + /* Hardware Interface Rx statistics */ + struct octep_vf_iface_rx_stats iface_rx_stats; + + /* Hardware Interface Tx statistics */ + struct octep_vf_iface_tx_stats iface_tx_stats; +}; + +struct octep_vf_fw_info { + /* pkind value to be used in every Tx hardware descriptor */ + u8 pkind; + /* front size data */ + u8 fsz; + /* supported rx offloads OCTEP_VF_RX_OFFLOAD_* */ + u16 rx_ol_flags; + /* supported tx offloads OCTEP_VF_TX_OFFLOAD_* */ + u16 tx_ol_flags; +}; + +/* The Octeon device specific private data structure. + * Each Octeon device has this structure to represent all its components. + */ +struct octep_vf_device { + struct octep_vf_config *conf; + + /* Octeon Chip type. */ + u16 chip_id; + u16 rev_id; + + /* Device capabilities enabled */ + u64 caps_enabled; + /* Device capabilities supported */ + u64 caps_supported; + + /* Pointer to basic Linux device */ + struct device *dev; + /* Linux PCI device pointer */ + struct pci_dev *pdev; + /* Netdev corresponding to the Octeon device */ + struct net_device *netdev; + + /* memory mapped io range */ + struct octep_vf_mmio mmio; + + /* MAC address */ + u8 mac_addr[ETH_ALEN]; + + /* Tx queues (IQ: Instruction Queue) */ + u16 num_iqs; + /* Pointers to Octeon Tx queues */ + struct octep_vf_iq *iq[OCTEP_VF_MAX_IQ]; + + /* Rx queues (OQ: Output Queue) */ + u16 num_oqs; + /* Pointers to Octeon Rx queues */ + struct octep_vf_oq *oq[OCTEP_VF_MAX_OQ]; + + /* Hardware port number of the PCIe interface */ + u16 pcie_port; + + /* Hardware operations */ + struct octep_vf_hw_ops hw_ops; + + /* IRQ info */ + u16 num_irqs; + u16 num_non_ioq_irqs; + char *non_ioq_irq_names; + struct msix_entry *msix_entries; + /* IOq information of it's corresponding MSI-X interrupt. */ + struct octep_vf_ioq_vector *ioq_vector[OCTEP_VF_MAX_QUEUES]; + + /* Hardware Interface Tx statistics */ + struct octep_vf_iface_tx_stats iface_tx_stats; + /* Hardware Interface Rx statistics */ + struct octep_vf_iface_rx_stats iface_rx_stats; + + /* Hardware Interface Link info like supported modes, aneg support */ + struct octep_vf_iface_link_info link_info; + + /* Mailbox to talk to VFs */ + struct octep_vf_mbox *mbox; + + /* Work entry to handle Tx timeout */ + struct work_struct tx_timeout_task; + + /* offset for iface stats */ + u32 ctrl_mbox_ifstats_offset; + + /* Negotiated Mbox version */ + u32 mbox_neg_ver; + + /* firmware info */ + struct octep_vf_fw_info fw_info; +}; + +static inline u16 OCTEP_VF_MAJOR_REV(struct octep_vf_device *oct) +{ + u16 rev = (oct->rev_id & 0xC) >> 2; + + return (rev == 0) ? 1 : rev; +} + +static inline u16 OCTEP_VF_MINOR_REV(struct octep_vf_device *oct) +{ + return (oct->rev_id & 0x3); +} + +/* Octeon CSR read/write access APIs */ +#define octep_vf_write_csr(octep_vf_dev, reg_off, value) \ + writel(value, (octep_vf_dev)->mmio.hw_addr + (reg_off)) + +#define octep_vf_write_csr64(octep_vf_dev, reg_off, val64) \ + writeq(val64, (octep_vf_dev)->mmio.hw_addr + (reg_off)) + +#define octep_vf_read_csr(octep_vf_dev, reg_off) \ + readl((octep_vf_dev)->mmio.hw_addr + (reg_off)) + +#define octep_vf_read_csr64(octep_vf_dev, reg_off) \ + readq((octep_vf_dev)->mmio.hw_addr + (reg_off)) + +extern struct workqueue_struct *octep_vf_wq; + +int octep_vf_device_setup(struct octep_vf_device *oct); +int octep_vf_setup_iqs(struct octep_vf_device *oct); +void octep_vf_free_iqs(struct octep_vf_device *oct); +void octep_vf_clean_iqs(struct octep_vf_device *oct); +int octep_vf_setup_oqs(struct octep_vf_device *oct); +void octep_vf_free_oqs(struct octep_vf_device *oct); +void octep_vf_oq_dbell_init(struct octep_vf_device *oct); +void octep_vf_device_setup_cn93(struct octep_vf_device *oct); +void octep_vf_device_setup_cnxk(struct octep_vf_device *oct); +int octep_vf_iq_process_completions(struct octep_vf_iq *iq, u16 budget); +int octep_vf_oq_process_rx(struct octep_vf_oq *oq, int budget); +void octep_vf_set_ethtool_ops(struct net_device *netdev); +int octep_vf_get_link_info(struct octep_vf_device *oct); +int octep_vf_get_if_stats(struct octep_vf_device *oct); +void octep_vf_mbox_work(struct work_struct *work); +#endif /* _OCTEP_VF_MAIN_H_ */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.c new file mode 100644 index 000000000000..2eab21e43048 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include "octep_vf_config.h" +#include "octep_vf_main.h" + +/* When a new command is implemented, the below table should be updated + * with new command and it's version info. + */ +static u32 pfvf_cmd_versions[OCTEP_PFVF_MBOX_CMD_MAX] = { + [0 ... OCTEP_PFVF_MBOX_CMD_DEV_REMOVE] = OCTEP_PFVF_MBOX_VERSION_V1, + [OCTEP_PFVF_MBOX_CMD_GET_FW_INFO ... OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS] = + OCTEP_PFVF_MBOX_VERSION_V2 +}; + +int octep_vf_setup_mbox(struct octep_vf_device *oct) +{ + int ring = 0; + + oct->mbox = vzalloc(sizeof(*oct->mbox)); + if (!oct->mbox) + return -1; + + mutex_init(&oct->mbox->lock); + + oct->hw_ops.setup_mbox_regs(oct, ring); + INIT_WORK(&oct->mbox->wk.work, octep_vf_mbox_work); + oct->mbox->wk.ctxptr = oct; + oct->mbox_neg_ver = OCTEP_PFVF_MBOX_VERSION_CURRENT; + dev_info(&oct->pdev->dev, "setup vf mbox successfully\n"); + return 0; +} + +void octep_vf_delete_mbox(struct octep_vf_device *oct) +{ + if (oct->mbox) { + if (work_pending(&oct->mbox->wk.work)) + cancel_work_sync(&oct->mbox->wk.work); + + mutex_destroy(&oct->mbox->lock); + vfree(oct->mbox); + oct->mbox = NULL; + dev_info(&oct->pdev->dev, "Deleted vf mbox successfully\n"); + } +} + +int octep_vf_mbox_version_check(struct octep_vf_device *oct) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int ret; + + cmd.u64 = 0; + cmd.s_version.opcode = OCTEP_PFVF_MBOX_CMD_VERSION; + cmd.s_version.version = OCTEP_PFVF_MBOX_VERSION_CURRENT; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret == OCTEP_PFVF_MBOX_CMD_STATUS_NACK) { + dev_err(&oct->pdev->dev, + "VF Mbox version is incompatible with PF\n"); + return -EINVAL; + } + oct->mbox_neg_ver = (u32)rsp.s_version.version; + dev_dbg(&oct->pdev->dev, + "VF Mbox version:%u Negotiated VF version with PF:%u\n", + (u32)cmd.s_version.version, + (u32)rsp.s_version.version); + return 0; +} + +void octep_vf_mbox_work(struct work_struct *work) +{ + struct octep_vf_mbox_wk *wk = container_of(work, struct octep_vf_mbox_wk, work); + struct octep_vf_iface_link_info *link_info; + struct octep_vf_device *oct = NULL; + struct octep_vf_mbox *mbox = NULL; + union octep_pfvf_mbox_word *notif; + u64 pf_vf_data; + + oct = (struct octep_vf_device *)wk->ctxptr; + link_info = &oct->link_info; + mbox = oct->mbox; + pf_vf_data = readq(mbox->mbox_read_reg); + + notif = (union octep_pfvf_mbox_word *)&pf_vf_data; + + switch (notif->s.opcode) { + case OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS: + if (notif->s_link_status.status) { + link_info->oper_up = OCTEP_PFVF_LINK_STATUS_UP; + netif_carrier_on(oct->netdev); + dev_info(&oct->pdev->dev, "netif_carrier_on\n"); + } else { + link_info->oper_up = OCTEP_PFVF_LINK_STATUS_DOWN; + netif_carrier_off(oct->netdev); + dev_info(&oct->pdev->dev, "netif_carrier_off\n"); + } + break; + default: + dev_err(&oct->pdev->dev, + "Received unsupported notif %d\n", notif->s.opcode); + break; + } +} + +static int __octep_vf_mbox_send_cmd(struct octep_vf_device *oct, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + struct octep_vf_mbox *mbox = oct->mbox; + u64 reg_val = 0ull; + int count; + + if (!mbox) + return OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP; + + cmd.s.type = OCTEP_PFVF_MBOX_TYPE_CMD; + writeq(cmd.u64, mbox->mbox_write_reg); + + /* No response for notification messages */ + if (!rsp) + return 0; + + for (count = 0; count < OCTEP_PFVF_MBOX_TIMEOUT_WAIT_COUNT; count++) { + usleep_range(1000, 1500); + reg_val = readq(mbox->mbox_write_reg); + if (reg_val != cmd.u64) { + rsp->u64 = reg_val; + break; + } + } + if (count == OCTEP_PFVF_MBOX_TIMEOUT_WAIT_COUNT) { + dev_err(&oct->pdev->dev, "mbox send command timed out\n"); + return OCTEP_PFVF_MBOX_CMD_STATUS_TIMEDOUT; + } + if (rsp->s.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "mbox_send: Received NACK\n"); + return OCTEP_PFVF_MBOX_CMD_STATUS_NACK; + } + rsp->u64 = reg_val; + return 0; +} + +int octep_vf_mbox_send_cmd(struct octep_vf_device *oct, union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + struct octep_vf_mbox *mbox = oct->mbox; + int ret; + + if (!mbox) + return OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP; + mutex_lock(&mbox->lock); + if (pfvf_cmd_versions[cmd.s.opcode] > oct->mbox_neg_ver) { + dev_dbg(&oct->pdev->dev, "CMD:%d not supported in Version:%d\n", + cmd.s.opcode, oct->mbox_neg_ver); + mutex_unlock(&mbox->lock); + return -EOPNOTSUPP; + } + ret = __octep_vf_mbox_send_cmd(oct, cmd, rsp); + mutex_unlock(&mbox->lock); + return ret; +} + +int octep_vf_mbox_bulk_read(struct octep_vf_device *oct, enum octep_pfvf_mbox_opcode opcode, + u8 *data, int *size) +{ + struct octep_vf_mbox *mbox = oct->mbox; + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int data_len = 0, tmp_len = 0; + int read_cnt, i = 0, ret; + + if (!mbox) + return OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP; + + mutex_lock(&mbox->lock); + cmd.u64 = 0; + cmd.s_data.opcode = opcode; + cmd.s_data.frag = 0; + /* Send cmd to read data from PF */ + ret = __octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "send mbox cmd fail for data request\n"); + mutex_unlock(&mbox->lock); + return ret; + } + /* PF sends the data length of requested CMD + * in ACK + */ + data_len = *((int32_t *)rsp.s_data.data); + tmp_len = data_len; + cmd.u64 = 0; + rsp.u64 = 0; + cmd.s_data.opcode = opcode; + cmd.s_data.frag = 1; + while (data_len) { + ret = __octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "send mbox cmd fail for data request\n"); + mutex_unlock(&mbox->lock); + mbox->mbox_data.data_index = 0; + memset(mbox->mbox_data.recv_data, 0, OCTEP_PFVF_MBOX_MAX_DATA_BUF_SIZE); + return ret; + } + if (data_len > OCTEP_PFVF_MBOX_MAX_DATA_SIZE) { + data_len -= OCTEP_PFVF_MBOX_MAX_DATA_SIZE; + read_cnt = OCTEP_PFVF_MBOX_MAX_DATA_SIZE; + } else { + read_cnt = data_len; + data_len = 0; + } + for (i = 0; i < read_cnt; i++) { + mbox->mbox_data.recv_data[mbox->mbox_data.data_index] = + rsp.s_data.data[i]; + mbox->mbox_data.data_index++; + } + cmd.u64 = 0; + rsp.u64 = 0; + cmd.s_data.opcode = opcode; + cmd.s_data.frag = 1; + } + memcpy(data, mbox->mbox_data.recv_data, tmp_len); + *size = tmp_len; + mbox->mbox_data.data_index = 0; + memset(mbox->mbox_data.recv_data, 0, OCTEP_PFVF_MBOX_MAX_DATA_BUF_SIZE); + mutex_unlock(&mbox->lock); + return 0; +} + +int octep_vf_mbox_set_mtu(struct octep_vf_device *oct, int mtu) +{ + int frame_size = mtu + ETH_HLEN + ETH_FCS_LEN; + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int ret = 0; + + if (mtu < ETH_MIN_MTU || frame_size > ETH_MAX_MTU) { + dev_err(&oct->pdev->dev, + "Failed to set MTU to %d MIN MTU:%d MAX MTU:%d\n", + mtu, ETH_MIN_MTU, ETH_MAX_MTU); + return -EINVAL; + } + + cmd.u64 = 0; + cmd.s_set_mtu.opcode = OCTEP_PFVF_MBOX_CMD_SET_MTU; + cmd.s_set_mtu.mtu = mtu; + + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "Mbox send failed; err=%d\n", ret); + return ret; + } + if (rsp.s_set_mtu.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "Received Mbox NACK from PF for MTU:%d\n", mtu); + return -EINVAL; + } + + return 0; +} + +int octep_vf_mbox_set_mac_addr(struct octep_vf_device *oct, char *mac_addr) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int i, ret; + + cmd.u64 = 0; + cmd.s_set_mac.opcode = OCTEP_PFVF_MBOX_CMD_SET_MAC_ADDR; + for (i = 0; i < ETH_ALEN; i++) + cmd.s_set_mac.mac_addr[i] = mac_addr[i]; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "Mbox send failed; err = %d\n", ret); + return ret; + } + if (rsp.s_set_mac.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "received NACK\n"); + return -EINVAL; + } + return 0; +} + +int octep_vf_mbox_get_mac_addr(struct octep_vf_device *oct, char *mac_addr) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int i, ret; + + cmd.u64 = 0; + cmd.s_set_mac.opcode = OCTEP_PFVF_MBOX_CMD_GET_MAC_ADDR; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "get_mac: mbox send failed; err = %d\n", ret); + return ret; + } + if (rsp.s_set_mac.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "get_mac: received NACK\n"); + return -EINVAL; + } + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = rsp.s_set_mac.mac_addr[i]; + return 0; +} + +int octep_vf_mbox_set_rx_state(struct octep_vf_device *oct, bool state) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int ret; + + cmd.u64 = 0; + cmd.s_link_state.opcode = OCTEP_PFVF_MBOX_CMD_SET_RX_STATE; + cmd.s_link_state.state = state; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "Set Rx state via VF Mbox send failed\n"); + return ret; + } + if (rsp.s_link_state.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "Set Rx state received NACK\n"); + return -EINVAL; + } + return 0; +} + +int octep_vf_mbox_set_link_status(struct octep_vf_device *oct, bool status) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int ret; + + cmd.u64 = 0; + cmd.s_link_status.opcode = OCTEP_PFVF_MBOX_CMD_SET_LINK_STATUS; + cmd.s_link_status.status = status; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "Set link status via VF Mbox send failed\n"); + return ret; + } + if (rsp.s_link_status.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "Set link status received NACK\n"); + return -EINVAL; + } + return 0; +} + +int octep_vf_mbox_get_link_status(struct octep_vf_device *oct, u8 *oper_up) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int ret; + + cmd.u64 = 0; + cmd.s_link_status.opcode = OCTEP_PFVF_MBOX_CMD_GET_LINK_STATUS; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "Get link status via VF Mbox send failed\n"); + return ret; + } + if (rsp.s_link_status.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "Get link status received NACK\n"); + return -EINVAL; + } + *oper_up = rsp.s_link_status.status; + return 0; +} + +int octep_vf_mbox_dev_remove(struct octep_vf_device *oct) +{ + union octep_pfvf_mbox_word cmd; + int ret; + + cmd.u64 = 0; + cmd.s.opcode = OCTEP_PFVF_MBOX_CMD_DEV_REMOVE; + ret = octep_vf_mbox_send_cmd(oct, cmd, NULL); + return ret; +} + +int octep_vf_mbox_get_fw_info(struct octep_vf_device *oct) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int ret; + + cmd.u64 = 0; + cmd.s_fw_info.opcode = OCTEP_PFVF_MBOX_CMD_GET_FW_INFO; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "Get link status via VF Mbox send failed\n"); + return ret; + } + if (rsp.s_fw_info.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "Get link status received NACK\n"); + return -EINVAL; + } + oct->fw_info.pkind = rsp.s_fw_info.pkind; + oct->fw_info.fsz = rsp.s_fw_info.fsz; + oct->fw_info.rx_ol_flags = rsp.s_fw_info.rx_ol_flags; + oct->fw_info.tx_ol_flags = rsp.s_fw_info.tx_ol_flags; + + return 0; +} + +int octep_vf_mbox_set_offloads(struct octep_vf_device *oct, u16 tx_offloads, + u16 rx_offloads) +{ + union octep_pfvf_mbox_word cmd; + union octep_pfvf_mbox_word rsp; + int ret; + + cmd.u64 = 0; + cmd.s_offloads.opcode = OCTEP_PFVF_MBOX_CMD_SET_OFFLOADS; + cmd.s_offloads.rx_ol_flags = rx_offloads; + cmd.s_offloads.tx_ol_flags = tx_offloads; + ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp); + if (ret) { + dev_err(&oct->pdev->dev, "Set offloads via VF Mbox send failed\n"); + return ret; + } + if (rsp.s_link_state.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) { + dev_err(&oct->pdev->dev, "Set offloads received NACK\n"); + return -EINVAL; + } + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.h new file mode 100644 index 000000000000..9b5efad37eab --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell Octeon EP (EndPoint) Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ +#ifndef _OCTEP_VF_MBOX_H_ +#define _OCTEP_VF_MBOX_H_ + +/* When a new command is implemented, VF Mbox version should be bumped. + */ +enum octep_pfvf_mbox_version { + OCTEP_PFVF_MBOX_VERSION_V0, + OCTEP_PFVF_MBOX_VERSION_V1, + OCTEP_PFVF_MBOX_VERSION_V2 +}; + +#define OCTEP_PFVF_MBOX_VERSION_CURRENT OCTEP_PFVF_MBOX_VERSION_V2 + +enum octep_pfvf_mbox_opcode { + OCTEP_PFVF_MBOX_CMD_VERSION, + OCTEP_PFVF_MBOX_CMD_SET_MTU, + OCTEP_PFVF_MBOX_CMD_SET_MAC_ADDR, + OCTEP_PFVF_MBOX_CMD_GET_MAC_ADDR, + OCTEP_PFVF_MBOX_CMD_GET_LINK_INFO, + OCTEP_PFVF_MBOX_CMD_GET_STATS, + OCTEP_PFVF_MBOX_CMD_SET_RX_STATE, + OCTEP_PFVF_MBOX_CMD_SET_LINK_STATUS, + OCTEP_PFVF_MBOX_CMD_GET_LINK_STATUS, + OCTEP_PFVF_MBOX_CMD_GET_MTU, + OCTEP_PFVF_MBOX_CMD_DEV_REMOVE, + OCTEP_PFVF_MBOX_CMD_GET_FW_INFO, + OCTEP_PFVF_MBOX_CMD_SET_OFFLOADS, + OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS, + OCTEP_PFVF_MBOX_CMD_MAX, +}; + +enum octep_pfvf_mbox_word_type { + OCTEP_PFVF_MBOX_TYPE_CMD, + OCTEP_PFVF_MBOX_TYPE_RSP_ACK, + OCTEP_PFVF_MBOX_TYPE_RSP_NACK, +}; + +enum octep_pfvf_mbox_cmd_status { + OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP = 1, + OCTEP_PFVF_MBOX_CMD_STATUS_TIMEDOUT = 2, + OCTEP_PFVF_MBOX_CMD_STATUS_NACK = 3, + OCTEP_PFVF_MBOX_CMD_STATUS_BUSY = 4, + OCTEP_PFVF_MBOX_CMD_STATUS_ERR = 5 +}; + +enum octep_pfvf_link_status { + OCTEP_PFVF_LINK_STATUS_DOWN, + OCTEP_PFVF_LINK_STATUS_UP, +}; + +enum octep_pfvf_link_speed { + OCTEP_PFVF_LINK_SPEED_NONE, + OCTEP_PFVF_LINK_SPEED_1000, + OCTEP_PFVF_LINK_SPEED_10000, + OCTEP_PFVF_LINK_SPEED_25000, + OCTEP_PFVF_LINK_SPEED_40000, + OCTEP_PFVF_LINK_SPEED_50000, + OCTEP_PFVF_LINK_SPEED_100000, + OCTEP_PFVF_LINK_SPEED_LAST, +}; + +enum octep_pfvf_link_duplex { + OCTEP_PFVF_LINK_HALF_DUPLEX, + OCTEP_PFVF_LINK_FULL_DUPLEX, +}; + +enum octep_pfvf_link_autoneg { + OCTEP_PFVF_LINK_AUTONEG, + OCTEP_PFVF_LINK_FIXED, +}; + +#define OCTEP_PFVF_MBOX_TIMEOUT_WAIT_COUNT 8000 +#define OCTEP_PFVF_MBOX_TIMEOUT_WAIT_UDELAY 1000 +#define OCTEP_PFVF_MBOX_MAX_RETRIES 2 +#define OCTEP_PFVF_MBOX_VERSION 0 +#define OCTEP_PFVF_MBOX_MAX_DATA_SIZE 6 +#define OCTEP_PFVF_MBOX_MAX_DATA_BUF_SIZE 320 +#define OCTEP_PFVF_MBOX_MORE_FRAG_FLAG 1 + +union octep_pfvf_mbox_word { + u64 u64; + struct { + u64 opcode:8; + u64 type:2; + u64 rsvd:6; + u64 data:48; + } s; + struct { + u64 opcode:8; + u64 type:2; + u64 frag:1; + u64 rsvd:5; + u8 data[6]; + } s_data; + struct { + u64 opcode:8; + u64 type:2; + u64 rsvd:6; + u64 version:48; + } s_version; + struct { + u64 opcode:8; + u64 type:2; + u64 rsvd:6; + u8 mac_addr[6]; + } s_set_mac; + struct { + u64 opcode:8; + u64 type:2; + u64 rsvd:6; + u64 mtu:48; + } s_set_mtu; + struct { + u64 opcode:8; + u64 type:2; + u64 state:1; + u64 rsvd:53; + } s_link_state; + struct { + u64 opcode:8; + u64 type:2; + u64 status:1; + u64 rsvd:53; + } s_link_status; + struct { + u64 opcode:8; + u64 type:2; + u64 pkind:8; + u64 fsz:8; + u64 rx_ol_flags:16; + u64 tx_ol_flags:16; + u64 rsvd:6; + } s_fw_info; + struct { + u64 opcode:8; + u64 type:2; + u64 rsvd:22; + u64 rx_ol_flags:16; + u64 tx_ol_flags:16; + } s_offloads; +} __packed; + +int octep_vf_setup_mbox(struct octep_vf_device *oct); +void octep_vf_delete_mbox(struct octep_vf_device *oct); +int octep_vf_mbox_send_cmd(struct octep_vf_device *oct, union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp); +int octep_vf_mbox_bulk_read(struct octep_vf_device *oct, enum octep_pfvf_mbox_opcode opcode, + u8 *data, int *size); +int octep_vf_mbox_set_mtu(struct octep_vf_device *oct, int mtu); +int octep_vf_mbox_set_mac_addr(struct octep_vf_device *oct, char *mac_addr); +int octep_vf_mbox_get_mac_addr(struct octep_vf_device *oct, char *mac_addr); +int octep_vf_mbox_version_check(struct octep_vf_device *oct); +int octep_vf_mbox_set_rx_state(struct octep_vf_device *oct, bool state); +int octep_vf_mbox_set_link_status(struct octep_vf_device *oct, bool status); +int octep_vf_mbox_get_link_status(struct octep_vf_device *oct, u8 *oper_up); +int octep_vf_mbox_dev_remove(struct octep_vf_device *oct); +int octep_vf_mbox_get_fw_info(struct octep_vf_device *oct); +int octep_vf_mbox_set_offloads(struct octep_vf_device *oct, u16 tx_offloads, u16 rx_offloads); + +#endif diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cn9k.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cn9k.h new file mode 100644 index 000000000000..25e2a876ebba --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cn9k.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ +#ifndef _OCTEP_VF_REGS_CN9K_H_ +#define _OCTEP_VF_REGS_CN9K_H_ + +/*############################ RST #########################*/ +#define CN93_VF_CONFIG_XPANSION_BAR 0x38 +#define CN93_VF_CONFIG_PCIE_CAP 0x70 +#define CN93_VF_CONFIG_PCIE_DEVCAP 0x74 +#define CN93_VF_CONFIG_PCIE_DEVCTL 0x78 +#define CN93_VF_CONFIG_PCIE_LINKCAP 0x7C +#define CN93_VF_CONFIG_PCIE_LINKCTL 0x80 +#define CN93_VF_CONFIG_PCIE_SLOTCAP 0x84 +#define CN93_VF_CONFIG_PCIE_SLOTCTL 0x88 + +#define CN93_VF_RING_OFFSET BIT_ULL(17) + +/*###################### RING IN REGISTERS #########################*/ +#define CN93_VF_SDP_R_IN_CONTROL_START 0x10000 +#define CN93_VF_SDP_R_IN_ENABLE_START 0x10010 +#define CN93_VF_SDP_R_IN_INSTR_BADDR_START 0x10020 +#define CN93_VF_SDP_R_IN_INSTR_RSIZE_START 0x10030 +#define CN93_VF_SDP_R_IN_INSTR_DBELL_START 0x10040 +#define CN93_VF_SDP_R_IN_CNTS_START 0x10050 +#define CN93_VF_SDP_R_IN_INT_LEVELS_START 0x10060 +#define CN93_VF_SDP_R_IN_PKT_CNT_START 0x10080 +#define CN93_VF_SDP_R_IN_BYTE_CNT_START 0x10090 + +#define CN93_VF_SDP_R_IN_CONTROL(ring) \ + (CN93_VF_SDP_R_IN_CONTROL_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_ENABLE(ring) \ + (CN93_VF_SDP_R_IN_ENABLE_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_INSTR_BADDR(ring) \ + (CN93_VF_SDP_R_IN_INSTR_BADDR_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_INSTR_RSIZE(ring) \ + (CN93_VF_SDP_R_IN_INSTR_RSIZE_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_INSTR_DBELL(ring) \ + (CN93_VF_SDP_R_IN_INSTR_DBELL_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_CNTS(ring) \ + (CN93_VF_SDP_R_IN_CNTS_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_INT_LEVELS(ring) \ + (CN93_VF_SDP_R_IN_INT_LEVELS_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_PKT_CNT(ring) \ + (CN93_VF_SDP_R_IN_PKT_CNT_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_IN_BYTE_CNT(ring) \ + (CN93_VF_SDP_R_IN_BYTE_CNT_START + ((ring) * CN93_VF_RING_OFFSET)) + +/*------------------ R_IN Masks ----------------*/ + +/** Rings per Virtual Function **/ +#define CN93_VF_R_IN_CTL_RPVF_MASK (0xF) +#define CN93_VF_R_IN_CTL_RPVF_POS (48) + +/* Number of instructions to be read in one MAC read request. + * setting to Max value(4) + **/ +#define CN93_VF_R_IN_CTL_IDLE BIT_ULL(28) +#define CN93_VF_R_IN_CTL_RDSIZE (0x3ULL << 25) +#define CN93_VF_R_IN_CTL_IS_64B BIT_ULL(24) +#define CN93_VF_R_IN_CTL_D_NSR BIT_ULL(8) +#define CN93_VF_R_IN_CTL_D_ESR BIT_ULL(6) +#define CN93_VF_R_IN_CTL_D_ROR BIT_ULL(5) +#define CN93_VF_R_IN_CTL_NSR BIT_ULL(3) +#define CN93_VF_R_IN_CTL_ESR BIT_ULL(1) +#define CN93_VF_R_IN_CTL_ROR BIT_ULL(0) + +#define CN93_VF_R_IN_CTL_MASK (CN93_VF_R_IN_CTL_RDSIZE | CN93_VF_R_IN_CTL_IS_64B) + +/*###################### RING OUT REGISTERS #########################*/ +#define CN93_VF_SDP_R_OUT_CNTS_START 0x10100 +#define CN93_VF_SDP_R_OUT_INT_LEVELS_START 0x10110 +#define CN93_VF_SDP_R_OUT_SLIST_BADDR_START 0x10120 +#define CN93_VF_SDP_R_OUT_SLIST_RSIZE_START 0x10130 +#define CN93_VF_SDP_R_OUT_SLIST_DBELL_START 0x10140 +#define CN93_VF_SDP_R_OUT_CONTROL_START 0x10150 +#define CN93_VF_SDP_R_OUT_ENABLE_START 0x10160 +#define CN93_VF_SDP_R_OUT_PKT_CNT_START 0x10180 +#define CN93_VF_SDP_R_OUT_BYTE_CNT_START 0x10190 + +#define CN93_VF_SDP_R_OUT_CONTROL(ring) \ + (CN93_VF_SDP_R_OUT_CONTROL_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_ENABLE(ring) \ + (CN93_VF_SDP_R_OUT_ENABLE_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_SLIST_BADDR(ring) \ + (CN93_VF_SDP_R_OUT_SLIST_BADDR_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_SLIST_RSIZE(ring) \ + (CN93_VF_SDP_R_OUT_SLIST_RSIZE_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_SLIST_DBELL(ring) \ + (CN93_VF_SDP_R_OUT_SLIST_DBELL_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_CNTS(ring) \ + (CN93_VF_SDP_R_OUT_CNTS_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_INT_LEVELS(ring) \ + (CN93_VF_SDP_R_OUT_INT_LEVELS_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_PKT_CNT(ring) \ + (CN93_VF_SDP_R_OUT_PKT_CNT_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_OUT_BYTE_CNT(ring) \ + (CN93_VF_SDP_R_OUT_BYTE_CNT_START + ((ring) * CN93_VF_RING_OFFSET)) + +/*------------------ R_OUT Masks ----------------*/ +#define CN93_VF_R_OUT_INT_LEVELS_BMODE BIT_ULL(63) +#define CN93_VF_R_OUT_INT_LEVELS_TIMET (32) + +#define CN93_VF_R_OUT_CTL_IDLE BIT_ULL(40) +#define CN93_VF_R_OUT_CTL_ES_I BIT_ULL(34) +#define CN93_VF_R_OUT_CTL_NSR_I BIT_ULL(33) +#define CN93_VF_R_OUT_CTL_ROR_I BIT_ULL(32) +#define CN93_VF_R_OUT_CTL_ES_D BIT_ULL(30) +#define CN93_VF_R_OUT_CTL_NSR_D BIT_ULL(29) +#define CN93_VF_R_OUT_CTL_ROR_D BIT_ULL(28) +#define CN93_VF_R_OUT_CTL_ES_P BIT_ULL(26) +#define CN93_VF_R_OUT_CTL_NSR_P BIT_ULL(25) +#define CN93_VF_R_OUT_CTL_ROR_P BIT_ULL(24) +#define CN93_VF_R_OUT_CTL_IMODE BIT_ULL(23) + +/* ##################### Mail Box Registers ########################## */ +/* SDP PF to VF Mailbox Data Register */ +#define CN93_VF_SDP_R_MBOX_PF_VF_DATA_START 0x10210 +/* SDP Packet PF to VF Mailbox Interrupt Register */ +#define CN93_VF_SDP_R_MBOX_PF_VF_INT_START 0x10220 +/* SDP VF to PF Mailbox Data Register */ +#define CN93_VF_SDP_R_MBOX_VF_PF_DATA_START 0x10230 + +#define CN93_VF_SDP_R_MBOX_PF_VF_INT_ENAB BIT_ULL(1) +#define CN93_VF_SDP_R_MBOX_PF_VF_INT_STATUS BIT_ULL(0) + +#define CN93_VF_SDP_R_MBOX_PF_VF_DATA(ring) \ + (CN93_VF_SDP_R_MBOX_PF_VF_DATA_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_MBOX_PF_VF_INT(ring) \ + (CN93_VF_SDP_R_MBOX_PF_VF_INT_START + ((ring) * CN93_VF_RING_OFFSET)) + +#define CN93_VF_SDP_R_MBOX_VF_PF_DATA(ring) \ + (CN93_VF_SDP_R_MBOX_VF_PF_DATA_START + ((ring) * CN93_VF_RING_OFFSET)) +#endif /* _OCTEP_VF_REGS_CN9K_H_ */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cnxk.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cnxk.h new file mode 100644 index 000000000000..2e156745ef64 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cnxk.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ +#ifndef _OCTEP_VF_REGS_CNXK_H_ +#define _OCTEP_VF_REGS_CNXK_H_ + +/*############################ RST #########################*/ +#define CNXK_VF_CONFIG_XPANSION_BAR 0x38 +#define CNXK_VF_CONFIG_PCIE_CAP 0x70 +#define CNXK_VF_CONFIG_PCIE_DEVCAP 0x74 +#define CNXK_VF_CONFIG_PCIE_DEVCTL 0x78 +#define CNXK_VF_CONFIG_PCIE_LINKCAP 0x7C +#define CNXK_VF_CONFIG_PCIE_LINKCTL 0x80 +#define CNXK_VF_CONFIG_PCIE_SLOTCAP 0x84 +#define CNXK_VF_CONFIG_PCIE_SLOTCTL 0x88 + +#define CNXK_VF_RING_OFFSET (0x1ULL << 17) + +/*###################### RING IN REGISTERS #########################*/ +#define CNXK_VF_SDP_R_IN_CONTROL_START 0x10000 +#define CNXK_VF_SDP_R_IN_ENABLE_START 0x10010 +#define CNXK_VF_SDP_R_IN_INSTR_BADDR_START 0x10020 +#define CNXK_VF_SDP_R_IN_INSTR_RSIZE_START 0x10030 +#define CNXK_VF_SDP_R_IN_INSTR_DBELL_START 0x10040 +#define CNXK_VF_SDP_R_IN_CNTS_START 0x10050 +#define CNXK_VF_SDP_R_IN_INT_LEVELS_START 0x10060 +#define CNXK_VF_SDP_R_IN_PKT_CNT_START 0x10080 +#define CNXK_VF_SDP_R_IN_BYTE_CNT_START 0x10090 +#define CNXK_VF_SDP_R_ERR_TYPE_START 0x10400 + +#define CNXK_VF_SDP_R_ERR_TYPE(ring) \ + (CNXK_VF_SDP_R_ERR_TYPE_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_CONTROL(ring) \ + (CNXK_VF_SDP_R_IN_CONTROL_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_ENABLE(ring) \ + (CNXK_VF_SDP_R_IN_ENABLE_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_INSTR_BADDR(ring) \ + (CNXK_VF_SDP_R_IN_INSTR_BADDR_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_INSTR_RSIZE(ring) \ + (CNXK_VF_SDP_R_IN_INSTR_RSIZE_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_INSTR_DBELL(ring) \ + (CNXK_VF_SDP_R_IN_INSTR_DBELL_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_CNTS(ring) \ + (CNXK_VF_SDP_R_IN_CNTS_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_INT_LEVELS(ring) \ + (CNXK_VF_SDP_R_IN_INT_LEVELS_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_PKT_CNT(ring) \ + (CNXK_VF_SDP_R_IN_PKT_CNT_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_IN_BYTE_CNT(ring) \ + (CNXK_VF_SDP_R_IN_BYTE_CNT_START + ((ring) * CNXK_VF_RING_OFFSET)) + +/*------------------ R_IN Masks ----------------*/ + +/** Rings per Virtual Function **/ +#define CNXK_VF_R_IN_CTL_RPVF_MASK (0xF) +#define CNXK_VF_R_IN_CTL_RPVF_POS (48) + +/* Number of instructions to be read in one MAC read request. + * setting to Max value(4) + **/ +#define CNXK_VF_R_IN_CTL_IDLE (0x1ULL << 28) +#define CNXK_VF_R_IN_CTL_RDSIZE (0x3ULL << 25) +#define CNXK_VF_R_IN_CTL_IS_64B (0x1ULL << 24) +#define CNXK_VF_R_IN_CTL_D_NSR (0x1ULL << 8) +#define CNXK_VF_R_IN_CTL_D_ESR (0x1ULL << 6) +#define CNXK_VF_R_IN_CTL_D_ROR (0x1ULL << 5) +#define CNXK_VF_R_IN_CTL_NSR (0x1ULL << 3) +#define CNXK_VF_R_IN_CTL_ESR (0x1ULL << 1) +#define CNXK_VF_R_IN_CTL_ROR (0x1ULL << 0) + +#define CNXK_VF_R_IN_CTL_MASK (CNXK_VF_R_IN_CTL_RDSIZE | CNXK_VF_R_IN_CTL_IS_64B) + +/*###################### RING OUT REGISTERS #########################*/ +#define CNXK_VF_SDP_R_OUT_CNTS_START 0x10100 +#define CNXK_VF_SDP_R_OUT_INT_LEVELS_START 0x10110 +#define CNXK_VF_SDP_R_OUT_SLIST_BADDR_START 0x10120 +#define CNXK_VF_SDP_R_OUT_SLIST_RSIZE_START 0x10130 +#define CNXK_VF_SDP_R_OUT_SLIST_DBELL_START 0x10140 +#define CNXK_VF_SDP_R_OUT_CONTROL_START 0x10150 +#define CNXK_VF_SDP_R_OUT_WMARK_START 0x10160 +#define CNXK_VF_SDP_R_OUT_ENABLE_START 0x10170 +#define CNXK_VF_SDP_R_OUT_PKT_CNT_START 0x10180 +#define CNXK_VF_SDP_R_OUT_BYTE_CNT_START 0x10190 + +#define CNXK_VF_SDP_R_OUT_CONTROL(ring) \ + (CNXK_VF_SDP_R_OUT_CONTROL_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_ENABLE(ring) \ + (CNXK_VF_SDP_R_OUT_ENABLE_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_SLIST_BADDR(ring) \ + (CNXK_VF_SDP_R_OUT_SLIST_BADDR_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_SLIST_RSIZE(ring) \ + (CNXK_VF_SDP_R_OUT_SLIST_RSIZE_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_SLIST_DBELL(ring) \ + (CNXK_VF_SDP_R_OUT_SLIST_DBELL_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_WMARK(ring) \ + (CNXK_VF_SDP_R_OUT_WMARK_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_CNTS(ring) \ + (CNXK_VF_SDP_R_OUT_CNTS_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_INT_LEVELS(ring) \ + (CNXK_VF_SDP_R_OUT_INT_LEVELS_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_PKT_CNT(ring) \ + (CNXK_VF_SDP_R_OUT_PKT_CNT_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_OUT_BYTE_CNT(ring) \ + (CNXK_VF_SDP_R_OUT_BYTE_CNT_START + ((ring) * CNXK_VF_RING_OFFSET)) + +/*------------------ R_OUT Masks ----------------*/ +#define CNXK_VF_R_OUT_INT_LEVELS_BMODE BIT_ULL(63) +#define CNXK_VF_R_OUT_INT_LEVELS_TIMET (32) + +#define CNXK_VF_R_OUT_CTL_IDLE BIT_ULL(40) +#define CNXK_VF_R_OUT_CTL_ES_I BIT_ULL(34) +#define CNXK_VF_R_OUT_CTL_NSR_I BIT_ULL(33) +#define CNXK_VF_R_OUT_CTL_ROR_I BIT_ULL(32) +#define CNXK_VF_R_OUT_CTL_ES_D BIT_ULL(30) +#define CNXK_VF_R_OUT_CTL_NSR_D BIT_ULL(29) +#define CNXK_VF_R_OUT_CTL_ROR_D BIT_ULL(28) +#define CNXK_VF_R_OUT_CTL_ES_P BIT_ULL(26) +#define CNXK_VF_R_OUT_CTL_NSR_P BIT_ULL(25) +#define CNXK_VF_R_OUT_CTL_ROR_P BIT_ULL(24) +#define CNXK_VF_R_OUT_CTL_IMODE BIT_ULL(23) + +/* ##################### Mail Box Registers ########################## */ +/* SDP PF to VF Mailbox Data Register */ +#define CNXK_VF_SDP_R_MBOX_PF_VF_DATA_START 0x10210 +/* SDP Packet PF to VF Mailbox Interrupt Register */ +#define CNXK_VF_SDP_R_MBOX_PF_VF_INT_START 0x10220 +/* SDP VF to PF Mailbox Data Register */ +#define CNXK_VF_SDP_R_MBOX_VF_PF_DATA_START 0x10230 + +#define CNXK_VF_SDP_R_MBOX_PF_VF_INT_ENAB BIT_ULL(1) +#define CNXK_VF_SDP_R_MBOX_PF_VF_INT_STATUS BIT_ULL(0) + +#define CNXK_VF_SDP_R_MBOX_PF_VF_DATA(ring) \ + (CNXK_VF_SDP_R_MBOX_PF_VF_DATA_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_MBOX_PF_VF_INT(ring) \ + (CNXK_VF_SDP_R_MBOX_PF_VF_INT_START + ((ring) * CNXK_VF_RING_OFFSET)) + +#define CNXK_VF_SDP_R_MBOX_VF_PF_DATA(ring) \ + (CNXK_VF_SDP_R_MBOX_VF_PF_DATA_START + ((ring) * CNXK_VF_RING_OFFSET)) +#endif /* _OCTEP_VF_REGS_CNXK_H_ */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c new file mode 100644 index 000000000000..82821bc28634 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c @@ -0,0 +1,510 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#include <linux/pci.h> +#include <linux/etherdevice.h> +#include <linux/vmalloc.h> + +#include "octep_vf_config.h" +#include "octep_vf_main.h" + +static void octep_vf_oq_reset_indices(struct octep_vf_oq *oq) +{ + oq->host_read_idx = 0; + oq->host_refill_idx = 0; + oq->refill_count = 0; + oq->last_pkt_count = 0; + oq->pkts_pending = 0; +} + +/** + * octep_vf_oq_fill_ring_buffers() - fill initial receive buffers for Rx ring. + * + * @oq: Octeon Rx queue data structure. + * + * Return: 0, if successfully filled receive buffers for all descriptors. + * -ENOMEM, if failed to allocate a buffer or failed to map for DMA. + */ +static int octep_vf_oq_fill_ring_buffers(struct octep_vf_oq *oq) +{ + struct octep_vf_oq_desc_hw *desc_ring = oq->desc_ring; + struct page *page; + u32 i; + + for (i = 0; i < oq->max_count; i++) { + page = dev_alloc_page(); + if (unlikely(!page)) { + dev_err(oq->dev, "Rx buffer alloc failed\n"); + goto rx_buf_alloc_err; + } + desc_ring[i].buffer_ptr = dma_map_page(oq->dev, page, 0, + PAGE_SIZE, + DMA_FROM_DEVICE); + if (dma_mapping_error(oq->dev, desc_ring[i].buffer_ptr)) { + dev_err(oq->dev, + "OQ-%d buffer alloc: DMA mapping error!\n", + oq->q_no); + goto dma_map_err; + } + oq->buff_info[i].page = page; + } + + return 0; + +dma_map_err: + put_page(page); +rx_buf_alloc_err: + while (i) { + i--; + dma_unmap_page(oq->dev, desc_ring[i].buffer_ptr, PAGE_SIZE, DMA_FROM_DEVICE); + put_page(oq->buff_info[i].page); + oq->buff_info[i].page = NULL; + } + + return -ENOMEM; +} + +/** + * octep_vf_oq_refill() - refill buffers for used Rx ring descriptors. + * + * @oct: Octeon device private data structure. + * @oq: Octeon Rx queue data structure. + * + * Return: number of descriptors successfully refilled with receive buffers. + */ +static int octep_vf_oq_refill(struct octep_vf_device *oct, struct octep_vf_oq *oq) +{ + struct octep_vf_oq_desc_hw *desc_ring = oq->desc_ring; + struct page *page; + u32 refill_idx, i; + + refill_idx = oq->host_refill_idx; + for (i = 0; i < oq->refill_count; i++) { + page = dev_alloc_page(); + if (unlikely(!page)) { + dev_err(oq->dev, "refill: rx buffer alloc failed\n"); + oq->stats.alloc_failures++; + break; + } + + desc_ring[refill_idx].buffer_ptr = dma_map_page(oq->dev, page, 0, + PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(oq->dev, desc_ring[refill_idx].buffer_ptr)) { + dev_err(oq->dev, + "OQ-%d buffer refill: DMA mapping error!\n", + oq->q_no); + put_page(page); + oq->stats.alloc_failures++; + break; + } + oq->buff_info[refill_idx].page = page; + refill_idx++; + if (refill_idx == oq->max_count) + refill_idx = 0; + } + oq->host_refill_idx = refill_idx; + oq->refill_count -= i; + + return i; +} + +/** + * octep_vf_setup_oq() - Setup a Rx queue. + * + * @oct: Octeon device private data structure. + * @q_no: Rx queue number to be setup. + * + * Allocate resources for a Rx queue. + */ +static int octep_vf_setup_oq(struct octep_vf_device *oct, int q_no) +{ + struct octep_vf_oq *oq; + u32 desc_ring_size; + + oq = vzalloc(sizeof(*oq)); + if (!oq) + goto create_oq_fail; + oct->oq[q_no] = oq; + + oq->octep_vf_dev = oct; + oq->netdev = oct->netdev; + oq->dev = &oct->pdev->dev; + oq->q_no = q_no; + oq->max_count = CFG_GET_OQ_NUM_DESC(oct->conf); + oq->ring_size_mask = oq->max_count - 1; + oq->buffer_size = CFG_GET_OQ_BUF_SIZE(oct->conf); + oq->max_single_buffer_size = oq->buffer_size - OCTEP_VF_OQ_RESP_HW_SIZE; + + /* When the hardware/firmware supports additional capabilities, + * additional header is filled-in by Octeon after length field in + * Rx packets. this header contains additional packet information. + */ + if (oct->fw_info.rx_ol_flags) + oq->max_single_buffer_size -= OCTEP_VF_OQ_RESP_HW_EXT_SIZE; + + oq->refill_threshold = CFG_GET_OQ_REFILL_THRESHOLD(oct->conf); + + desc_ring_size = oq->max_count * OCTEP_VF_OQ_DESC_SIZE; + oq->desc_ring = dma_alloc_coherent(oq->dev, desc_ring_size, + &oq->desc_ring_dma, GFP_KERNEL); + + if (unlikely(!oq->desc_ring)) { + dev_err(oq->dev, + "Failed to allocate DMA memory for OQ-%d !!\n", q_no); + goto desc_dma_alloc_err; + } + + oq->buff_info = vzalloc(oq->max_count * OCTEP_VF_OQ_RECVBUF_SIZE); + + if (unlikely(!oq->buff_info)) { + dev_err(&oct->pdev->dev, + "Failed to allocate buffer info for OQ-%d\n", q_no); + goto buf_list_err; + } + + if (octep_vf_oq_fill_ring_buffers(oq)) + goto oq_fill_buff_err; + + octep_vf_oq_reset_indices(oq); + oct->hw_ops.setup_oq_regs(oct, q_no); + oct->num_oqs++; + + return 0; + +oq_fill_buff_err: + vfree(oq->buff_info); + oq->buff_info = NULL; +buf_list_err: + dma_free_coherent(oq->dev, desc_ring_size, + oq->desc_ring, oq->desc_ring_dma); + oq->desc_ring = NULL; +desc_dma_alloc_err: + vfree(oq); + oct->oq[q_no] = NULL; +create_oq_fail: + return -ENOMEM; +} + +/** + * octep_vf_oq_free_ring_buffers() - Free ring buffers. + * + * @oq: Octeon Rx queue data structure. + * + * Free receive buffers in unused Rx queue descriptors. + */ +static void octep_vf_oq_free_ring_buffers(struct octep_vf_oq *oq) +{ + struct octep_vf_oq_desc_hw *desc_ring = oq->desc_ring; + int i; + + if (!oq->desc_ring || !oq->buff_info) + return; + + for (i = 0; i < oq->max_count; i++) { + if (oq->buff_info[i].page) { + dma_unmap_page(oq->dev, desc_ring[i].buffer_ptr, + PAGE_SIZE, DMA_FROM_DEVICE); + put_page(oq->buff_info[i].page); + oq->buff_info[i].page = NULL; + desc_ring[i].buffer_ptr = 0; + } + } + octep_vf_oq_reset_indices(oq); +} + +/** + * octep_vf_free_oq() - Free Rx queue resources. + * + * @oq: Octeon Rx queue data structure. + * + * Free all resources of a Rx queue. + */ +static int octep_vf_free_oq(struct octep_vf_oq *oq) +{ + struct octep_vf_device *oct = oq->octep_vf_dev; + int q_no = oq->q_no; + + octep_vf_oq_free_ring_buffers(oq); + + vfree(oq->buff_info); + + if (oq->desc_ring) + dma_free_coherent(oq->dev, + oq->max_count * OCTEP_VF_OQ_DESC_SIZE, + oq->desc_ring, oq->desc_ring_dma); + + vfree(oq); + oct->oq[q_no] = NULL; + oct->num_oqs--; + return 0; +} + +/** + * octep_vf_setup_oqs() - setup resources for all Rx queues. + * + * @oct: Octeon device private data structure. + */ +int octep_vf_setup_oqs(struct octep_vf_device *oct) +{ + int i, retval = 0; + + oct->num_oqs = 0; + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) { + retval = octep_vf_setup_oq(oct, i); + if (retval) { + dev_err(&oct->pdev->dev, + "Failed to setup OQ(RxQ)-%d.\n", i); + goto oq_setup_err; + } + dev_dbg(&oct->pdev->dev, "Successfully setup OQ(RxQ)-%d.\n", i); + } + + return 0; + +oq_setup_err: + while (i) { + i--; + octep_vf_free_oq(oct->oq[i]); + } + return retval; +} + +/** + * octep_vf_oq_dbell_init() - Initialize Rx queue doorbell. + * + * @oct: Octeon device private data structure. + * + * Write number of descriptors to Rx queue doorbell register. + */ +void octep_vf_oq_dbell_init(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_oqs; i++) + writel(oct->oq[i]->max_count, oct->oq[i]->pkts_credit_reg); +} + +/** + * octep_vf_free_oqs() - Free resources of all Rx queues. + * + * @oct: Octeon device private data structure. + */ +void octep_vf_free_oqs(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) { + if (!oct->oq[i]) + continue; + octep_vf_free_oq(oct->oq[i]); + dev_dbg(&oct->pdev->dev, + "Successfully freed OQ(RxQ)-%d.\n", i); + } +} + +/** + * octep_vf_oq_check_hw_for_pkts() - Check for new Rx packets. + * + * @oct: Octeon device private data structure. + * @oq: Octeon Rx queue data structure. + * + * Return: packets received after previous check. + */ +static int octep_vf_oq_check_hw_for_pkts(struct octep_vf_device *oct, + struct octep_vf_oq *oq) +{ + u32 pkt_count, new_pkts; + + pkt_count = readl(oq->pkts_sent_reg); + new_pkts = pkt_count - oq->last_pkt_count; + + /* Clear the hardware packets counter register if the rx queue is + * being processed continuously with-in a single interrupt and + * reached half its max value. + * this counter is not cleared every time read, to save write cycles. + */ + if (unlikely(pkt_count > 0xF0000000U)) { + writel(pkt_count, oq->pkts_sent_reg); + pkt_count = readl(oq->pkts_sent_reg); + new_pkts += pkt_count; + } + oq->last_pkt_count = pkt_count; + oq->pkts_pending += new_pkts; + return new_pkts; +} + +/** + * __octep_vf_oq_process_rx() - Process hardware Rx queue and push to stack. + * + * @oct: Octeon device private data structure. + * @oq: Octeon Rx queue data structure. + * @pkts_to_process: number of packets to be processed. + * + * Process the new packets in Rx queue. + * Packets larger than single Rx buffer arrive in consecutive descriptors. + * But, count returned by the API only accounts full packets, not fragments. + * + * Return: number of packets processed and pushed to stack. + */ +static int __octep_vf_oq_process_rx(struct octep_vf_device *oct, + struct octep_vf_oq *oq, u16 pkts_to_process) +{ + struct octep_vf_oq_resp_hw_ext *resp_hw_ext = NULL; + netdev_features_t feat = oq->netdev->features; + struct octep_vf_rx_buffer *buff_info; + struct octep_vf_oq_resp_hw *resp_hw; + u32 pkt, rx_bytes, desc_used; + u16 data_offset, rx_ol_flags; + struct sk_buff *skb; + u32 read_idx; + + read_idx = oq->host_read_idx; + rx_bytes = 0; + desc_used = 0; + for (pkt = 0; pkt < pkts_to_process; pkt++) { + buff_info = (struct octep_vf_rx_buffer *)&oq->buff_info[read_idx]; + dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr, + PAGE_SIZE, DMA_FROM_DEVICE); + resp_hw = page_address(buff_info->page); + buff_info->page = NULL; + + /* Swap the length field that is in Big-Endian to CPU */ + buff_info->len = be64_to_cpu(resp_hw->length); + if (oct->fw_info.rx_ol_flags) { + /* Extended response header is immediately after + * response header (resp_hw) + */ + resp_hw_ext = (struct octep_vf_oq_resp_hw_ext *) + (resp_hw + 1); + buff_info->len -= OCTEP_VF_OQ_RESP_HW_EXT_SIZE; + /* Packet Data is immediately after + * extended response header. + */ + data_offset = OCTEP_VF_OQ_RESP_HW_SIZE + + OCTEP_VF_OQ_RESP_HW_EXT_SIZE; + rx_ol_flags = resp_hw_ext->rx_ol_flags; + } else { + /* Data is immediately after + * Hardware Rx response header. + */ + data_offset = OCTEP_VF_OQ_RESP_HW_SIZE; + rx_ol_flags = 0; + } + rx_bytes += buff_info->len; + + if (buff_info->len <= oq->max_single_buffer_size) { + skb = napi_build_skb((void *)resp_hw, PAGE_SIZE); + skb_reserve(skb, data_offset); + skb_put(skb, buff_info->len); + read_idx++; + desc_used++; + if (read_idx == oq->max_count) + read_idx = 0; + } else { + struct skb_shared_info *shinfo; + u16 data_len; + + skb = napi_build_skb((void *)resp_hw, PAGE_SIZE); + skb_reserve(skb, data_offset); + /* Head fragment includes response header(s); + * subsequent fragments contains only data. + */ + skb_put(skb, oq->max_single_buffer_size); + read_idx++; + desc_used++; + if (read_idx == oq->max_count) + read_idx = 0; + + shinfo = skb_shinfo(skb); + data_len = buff_info->len - oq->max_single_buffer_size; + while (data_len) { + dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr, + PAGE_SIZE, DMA_FROM_DEVICE); + buff_info = (struct octep_vf_rx_buffer *) + &oq->buff_info[read_idx]; + if (data_len < oq->buffer_size) { + buff_info->len = data_len; + data_len = 0; + } else { + buff_info->len = oq->buffer_size; + data_len -= oq->buffer_size; + } + + skb_add_rx_frag(skb, shinfo->nr_frags, + buff_info->page, 0, + buff_info->len, + buff_info->len); + buff_info->page = NULL; + read_idx++; + desc_used++; + if (read_idx == oq->max_count) + read_idx = 0; + } + } + + skb->dev = oq->netdev; + skb->protocol = eth_type_trans(skb, skb->dev); + if (feat & NETIF_F_RXCSUM && + OCTEP_VF_RX_CSUM_VERIFIED(rx_ol_flags)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + napi_gro_receive(oq->napi, skb); + } + + oq->host_read_idx = read_idx; + oq->refill_count += desc_used; + oq->stats.packets += pkt; + oq->stats.bytes += rx_bytes; + + return pkt; +} + +/** + * octep_vf_oq_process_rx() - Process Rx queue. + * + * @oq: Octeon Rx queue data structure. + * @budget: max number of packets can be processed in one invocation. + * + * Check for newly received packets and process them. + * Keeps checking for new packets until budget is used or no new packets seen. + * + * Return: number of packets processed. + */ +int octep_vf_oq_process_rx(struct octep_vf_oq *oq, int budget) +{ + u32 pkts_available, pkts_processed, total_pkts_processed; + struct octep_vf_device *oct = oq->octep_vf_dev; + + pkts_available = 0; + pkts_processed = 0; + total_pkts_processed = 0; + while (total_pkts_processed < budget) { + /* update pending count only when current one exhausted */ + if (oq->pkts_pending == 0) + octep_vf_oq_check_hw_for_pkts(oct, oq); + pkts_available = min(budget - total_pkts_processed, + oq->pkts_pending); + if (!pkts_available) + break; + + pkts_processed = __octep_vf_oq_process_rx(oct, oq, + pkts_available); + oq->pkts_pending -= pkts_processed; + total_pkts_processed += pkts_processed; + } + + if (oq->refill_count >= oq->refill_threshold) { + u32 desc_refilled = octep_vf_oq_refill(oct, oq); + + /* flush pending writes before updating credits */ + smp_wmb(); + writel(desc_refilled, oq->pkts_credit_reg); + } + + return total_pkts_processed; +} diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.h new file mode 100644 index 000000000000..fe46838b5200 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#ifndef _OCTEP_VF_RX_H_ +#define _OCTEP_VF_RX_H_ + +/* struct octep_vf_oq_desc_hw - Octeon Hardware OQ descriptor format. + * + * The descriptor ring is made of descriptors which have 2 64-bit values: + * + * @buffer_ptr: DMA address of the skb->data + * @info_ptr: DMA address of host memory, used to update pkt count by hw. + * This is currently unused to save pci writes. + */ +struct octep_vf_oq_desc_hw { + dma_addr_t buffer_ptr; + u64 info_ptr; +}; + +static_assert(sizeof(struct octep_vf_oq_desc_hw) == 16); + +#define OCTEP_VF_OQ_DESC_SIZE (sizeof(struct octep_vf_oq_desc_hw)) + +/* Rx offload flags */ +#define OCTEP_VF_RX_OFFLOAD_VLAN_STRIP BIT(0) +#define OCTEP_VF_RX_OFFLOAD_IPV4_CKSUM BIT(1) +#define OCTEP_VF_RX_OFFLOAD_UDP_CKSUM BIT(2) +#define OCTEP_VF_RX_OFFLOAD_TCP_CKSUM BIT(3) + +#define OCTEP_VF_RX_OFFLOAD_CKSUM (OCTEP_VF_RX_OFFLOAD_IPV4_CKSUM | \ + OCTEP_VF_RX_OFFLOAD_UDP_CKSUM | \ + OCTEP_VF_RX_OFFLOAD_TCP_CKSUM) + +#define OCTEP_VF_RX_IP_CSUM(flags) ((flags) & \ + (OCTEP_VF_RX_OFFLOAD_IPV4_CKSUM | \ + OCTEP_VF_RX_OFFLOAD_TCP_CKSUM | \ + OCTEP_VF_RX_OFFLOAD_UDP_CKSUM)) + +/* bit 0 is vlan strip */ +#define OCTEP_VF_RX_CSUM_IP_VERIFIED BIT(1) +#define OCTEP_VF_RX_CSUM_L4_VERIFIED BIT(2) + +#define OCTEP_VF_RX_CSUM_VERIFIED(flags) ((flags) & \ + (OCTEP_VF_RX_CSUM_L4_VERIFIED | \ + OCTEP_VF_RX_CSUM_IP_VERIFIED)) + +/* Extended Response Header in packet data received from Hardware. + * Includes metadata like checksum status. + * this is valid only if hardware/firmware published support for this. + * This is at offset 0 of packet data (skb->data). + */ +struct octep_vf_oq_resp_hw_ext { + /* Reserved. */ + u64 rsvd:48; + + /* rx offload flags */ + u16 rx_ol_flags; +}; + +static_assert(sizeof(struct octep_vf_oq_resp_hw_ext) == 8); + +#define OCTEP_VF_OQ_RESP_HW_EXT_SIZE (sizeof(struct octep_vf_oq_resp_hw_ext)) + +/* Length of Rx packet DMA'ed by Octeon to Host. + * this is in bigendian; so need to be converted to cpu endian. + * Octeon writes this at the beginning of Rx buffer (skb->data). + */ +struct octep_vf_oq_resp_hw { + /* The Length of the packet. */ + __be64 length; +}; + +static_assert(sizeof(struct octep_vf_oq_resp_hw) == 8); + +#define OCTEP_VF_OQ_RESP_HW_SIZE (sizeof(struct octep_vf_oq_resp_hw)) + +/* Pointer to data buffer. + * Driver keeps a pointer to the data buffer that it made available to + * the Octeon device. Since the descriptor ring keeps physical (bus) + * addresses, this field is required for the driver to keep track of + * the virtual address pointers. The fields are operated by + * OS-dependent routines. + */ +struct octep_vf_rx_buffer { + struct page *page; + + /* length from rx hardware descriptor after converting to cpu endian */ + u64 len; +}; + +#define OCTEP_VF_OQ_RECVBUF_SIZE (sizeof(struct octep_vf_rx_buffer)) + +/* Output Queue statistics. Each output queue has four stats fields. */ +struct octep_vf_oq_stats { + /* Number of packets received from the Device. */ + u64 packets; + + /* Number of bytes received from the Device. */ + u64 bytes; + + /* Number of times failed to allocate buffers. */ + u64 alloc_failures; +}; + +#define OCTEP_VF_OQ_STATS_SIZE (sizeof(struct octep_vf_oq_stats)) + +/* Hardware interface Rx statistics */ +struct octep_vf_iface_rx_stats { + /* Received packets */ + u64 pkts; + + /* Octets of received packets */ + u64 octets; + + /* Received PAUSE and Control packets */ + u64 pause_pkts; + + /* Received PAUSE and Control octets */ + u64 pause_octets; + + /* Filtered DMAC0 packets */ + u64 dmac0_pkts; + + /* Filtered DMAC0 octets */ + u64 dmac0_octets; + + /* Packets dropped due to RX FIFO full */ + u64 dropped_pkts_fifo_full; + + /* Octets dropped due to RX FIFO full */ + u64 dropped_octets_fifo_full; + + /* Error packets */ + u64 err_pkts; + + /* Filtered DMAC1 packets */ + u64 dmac1_pkts; + + /* Filtered DMAC1 octets */ + u64 dmac1_octets; + + /* NCSI-bound packets dropped */ + u64 ncsi_dropped_pkts; + + /* NCSI-bound octets dropped */ + u64 ncsi_dropped_octets; + + /* Multicast packets received. */ + u64 mcast_pkts; + + /* Broadcast packets received. */ + u64 bcast_pkts; + +}; + +/* The Descriptor Ring Output Queue structure. + * This structure has all the information required to implement a + * Octeon OQ. + */ +struct octep_vf_oq { + u32 q_no; + + struct octep_vf_device *octep_vf_dev; + struct net_device *netdev; + struct device *dev; + + struct napi_struct *napi; + + /* The receive buffer list. This list has the virtual addresses + * of the buffers. + */ + struct octep_vf_rx_buffer *buff_info; + + /* Pointer to the mapped packet credit register. + * Host writes number of info/buffer ptrs available to this register + */ + u8 __iomem *pkts_credit_reg; + + /* Pointer to the mapped packet sent register. + * Octeon writes the number of packets DMA'ed to host memory + * in this register. + */ + u8 __iomem *pkts_sent_reg; + + /* Statistics for this OQ. */ + struct octep_vf_oq_stats stats; + + /* Packets pending to be processed */ + u32 pkts_pending; + u32 last_pkt_count; + + /* Index in the ring where the driver should read the next packet */ + u32 host_read_idx; + + /* Number of descriptors in this ring. */ + u32 max_count; + u32 ring_size_mask; + + /* The number of descriptors pending refill. */ + u32 refill_count; + + /* Index in the ring where the driver will refill the + * descriptor's buffer + */ + u32 host_refill_idx; + u32 refill_threshold; + + /* The size of each buffer pointed by the buffer pointer. */ + u32 buffer_size; + u32 max_single_buffer_size; + + /* The 8B aligned descriptor ring starts at this address. */ + struct octep_vf_oq_desc_hw *desc_ring; + + /* DMA mapped address of the OQ descriptor ring. */ + dma_addr_t desc_ring_dma; +}; + +#define OCTEP_VF_OQ_SIZE (sizeof(struct octep_vf_oq)) +#endif /* _OCTEP_VF_RX_H_ */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.c new file mode 100644 index 000000000000..47a5c054fdb6 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#include <linux/pci.h> +#include <linux/etherdevice.h> +#include <linux/vmalloc.h> +#include <net/netdev_queues.h> + +#include "octep_vf_config.h" +#include "octep_vf_main.h" + +/* Reset various index of Tx queue data structure. */ +static void octep_vf_iq_reset_indices(struct octep_vf_iq *iq) +{ + iq->fill_cnt = 0; + iq->host_write_index = 0; + iq->octep_vf_read_index = 0; + iq->flush_index = 0; + iq->pkts_processed = 0; + iq->pkt_in_done = 0; +} + +/** + * octep_vf_iq_process_completions() - Process Tx queue completions. + * + * @iq: Octeon Tx queue data structure. + * @budget: max number of completions to be processed in one invocation. + */ +int octep_vf_iq_process_completions(struct octep_vf_iq *iq, u16 budget) +{ + u32 compl_pkts, compl_bytes, compl_sg; + struct octep_vf_device *oct = iq->octep_vf_dev; + struct octep_vf_tx_buffer *tx_buffer; + struct skb_shared_info *shinfo; + u32 fi = iq->flush_index; + struct sk_buff *skb; + u8 frags, i; + + compl_pkts = 0; + compl_sg = 0; + compl_bytes = 0; + iq->octep_vf_read_index = oct->hw_ops.update_iq_read_idx(iq); + + while (likely(budget && (fi != iq->octep_vf_read_index))) { + tx_buffer = iq->buff_info + fi; + skb = tx_buffer->skb; + + fi++; + if (unlikely(fi == iq->max_count)) + fi = 0; + compl_bytes += skb->len; + compl_pkts++; + budget--; + + if (!tx_buffer->gather) { + dma_unmap_single(iq->dev, tx_buffer->dma, + tx_buffer->skb->len, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + continue; + } + + /* Scatter/Gather */ + shinfo = skb_shinfo(skb); + frags = shinfo->nr_frags; + compl_sg++; + + dma_unmap_single(iq->dev, tx_buffer->sglist[0].dma_ptr[0], + tx_buffer->sglist[0].len[3], DMA_TO_DEVICE); + + i = 1; /* entry 0 is main skb, unmapped above */ + while (frags--) { + dma_unmap_page(iq->dev, tx_buffer->sglist[i >> 2].dma_ptr[i & 3], + tx_buffer->sglist[i >> 2].len[3 - (i & 3)], DMA_TO_DEVICE); + i++; + } + + dev_kfree_skb_any(skb); + } + + iq->pkts_processed += compl_pkts; + iq->stats.instr_completed += compl_pkts; + iq->stats.bytes_sent += compl_bytes; + iq->stats.sgentry_sent += compl_sg; + iq->flush_index = fi; + + netif_subqueue_completed_wake(iq->netdev, iq->q_no, compl_pkts, + compl_bytes, IQ_INSTR_SPACE(iq), + OCTEP_VF_WAKE_QUEUE_THRESHOLD); + + return !budget; +} + +/** + * octep_vf_iq_free_pending() - Free Tx buffers for pending completions. + * + * @iq: Octeon Tx queue data structure. + */ +static void octep_vf_iq_free_pending(struct octep_vf_iq *iq) +{ + struct octep_vf_tx_buffer *tx_buffer; + struct skb_shared_info *shinfo; + u32 fi = iq->flush_index; + struct sk_buff *skb; + u8 frags, i; + + while (fi != iq->host_write_index) { + tx_buffer = iq->buff_info + fi; + skb = tx_buffer->skb; + + fi++; + if (unlikely(fi == iq->max_count)) + fi = 0; + + if (!tx_buffer->gather) { + dma_unmap_single(iq->dev, tx_buffer->dma, + tx_buffer->skb->len, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + continue; + } + + /* Scatter/Gather */ + shinfo = skb_shinfo(skb); + frags = shinfo->nr_frags; + + dma_unmap_single(iq->dev, + tx_buffer->sglist[0].dma_ptr[0], + tx_buffer->sglist[0].len[0], + DMA_TO_DEVICE); + + i = 1; /* entry 0 is main skb, unmapped above */ + while (frags--) { + dma_unmap_page(iq->dev, tx_buffer->sglist[i >> 2].dma_ptr[i & 3], + tx_buffer->sglist[i >> 2].len[i & 3], DMA_TO_DEVICE); + i++; + } + + dev_kfree_skb_any(skb); + } + + iq->flush_index = fi; + netdev_tx_reset_queue(netdev_get_tx_queue(iq->netdev, iq->q_no)); +} + +/** + * octep_vf_clean_iqs() - Clean Tx queues to shutdown the device. + * + * @oct: Octeon device private data structure. + * + * Free the buffers in Tx queue descriptors pending completion and + * reset queue indices + */ +void octep_vf_clean_iqs(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < oct->num_iqs; i++) { + octep_vf_iq_free_pending(oct->iq[i]); + octep_vf_iq_reset_indices(oct->iq[i]); + } +} + +/** + * octep_vf_setup_iq() - Setup a Tx queue. + * + * @oct: Octeon device private data structure. + * @q_no: Tx queue number to be setup. + * + * Allocate resources for a Tx queue. + */ +static int octep_vf_setup_iq(struct octep_vf_device *oct, int q_no) +{ + u32 desc_ring_size, buff_info_size, sglist_size; + struct octep_vf_iq *iq; + int i; + + iq = vzalloc(sizeof(*iq)); + if (!iq) + goto iq_alloc_err; + oct->iq[q_no] = iq; + + iq->octep_vf_dev = oct; + iq->netdev = oct->netdev; + iq->dev = &oct->pdev->dev; + iq->q_no = q_no; + iq->max_count = CFG_GET_IQ_NUM_DESC(oct->conf); + iq->ring_size_mask = iq->max_count - 1; + iq->fill_threshold = CFG_GET_IQ_DB_MIN(oct->conf); + iq->netdev_q = netdev_get_tx_queue(iq->netdev, q_no); + + /* Allocate memory for hardware queue descriptors */ + desc_ring_size = OCTEP_VF_IQ_DESC_SIZE * CFG_GET_IQ_NUM_DESC(oct->conf); + iq->desc_ring = dma_alloc_coherent(iq->dev, desc_ring_size, + &iq->desc_ring_dma, GFP_KERNEL); + if (unlikely(!iq->desc_ring)) { + dev_err(iq->dev, + "Failed to allocate DMA memory for IQ-%d\n", q_no); + goto desc_dma_alloc_err; + } + + /* Allocate memory for hardware SGLIST descriptors */ + sglist_size = OCTEP_VF_SGLIST_SIZE_PER_PKT * + CFG_GET_IQ_NUM_DESC(oct->conf); + iq->sglist = dma_alloc_coherent(iq->dev, sglist_size, + &iq->sglist_dma, GFP_KERNEL); + if (unlikely(!iq->sglist)) { + dev_err(iq->dev, + "Failed to allocate DMA memory for IQ-%d SGLIST\n", + q_no); + goto sglist_alloc_err; + } + + /* allocate memory to manage Tx packets pending completion */ + buff_info_size = OCTEP_VF_IQ_TXBUFF_INFO_SIZE * iq->max_count; + iq->buff_info = vzalloc(buff_info_size); + if (!iq->buff_info) { + dev_err(iq->dev, + "Failed to allocate buff info for IQ-%d\n", q_no); + goto buff_info_err; + } + + /* Setup sglist addresses in tx_buffer entries */ + for (i = 0; i < CFG_GET_IQ_NUM_DESC(oct->conf); i++) { + struct octep_vf_tx_buffer *tx_buffer; + + tx_buffer = &iq->buff_info[i]; + tx_buffer->sglist = + &iq->sglist[i * OCTEP_VF_SGLIST_ENTRIES_PER_PKT]; + tx_buffer->sglist_dma = + iq->sglist_dma + (i * OCTEP_VF_SGLIST_SIZE_PER_PKT); + } + + octep_vf_iq_reset_indices(iq); + oct->hw_ops.setup_iq_regs(oct, q_no); + + oct->num_iqs++; + return 0; + +buff_info_err: + dma_free_coherent(iq->dev, sglist_size, iq->sglist, iq->sglist_dma); +sglist_alloc_err: + dma_free_coherent(iq->dev, desc_ring_size, + iq->desc_ring, iq->desc_ring_dma); +desc_dma_alloc_err: + vfree(iq); + oct->iq[q_no] = NULL; +iq_alloc_err: + return -1; +} + +/** + * octep_vf_free_iq() - Free Tx queue resources. + * + * @iq: Octeon Tx queue data structure. + * + * Free all the resources allocated for a Tx queue. + */ +static void octep_vf_free_iq(struct octep_vf_iq *iq) +{ + struct octep_vf_device *oct = iq->octep_vf_dev; + u64 desc_ring_size, sglist_size; + int q_no = iq->q_no; + + desc_ring_size = OCTEP_VF_IQ_DESC_SIZE * CFG_GET_IQ_NUM_DESC(oct->conf); + + vfree(iq->buff_info); + + if (iq->desc_ring) + dma_free_coherent(iq->dev, desc_ring_size, + iq->desc_ring, iq->desc_ring_dma); + + sglist_size = OCTEP_VF_SGLIST_SIZE_PER_PKT * + CFG_GET_IQ_NUM_DESC(oct->conf); + if (iq->sglist) + dma_free_coherent(iq->dev, sglist_size, + iq->sglist, iq->sglist_dma); + + vfree(iq); + oct->iq[q_no] = NULL; + oct->num_iqs--; +} + +/** + * octep_vf_setup_iqs() - setup resources for all Tx queues. + * + * @oct: Octeon device private data structure. + */ +int octep_vf_setup_iqs(struct octep_vf_device *oct) +{ + int i; + + oct->num_iqs = 0; + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) { + if (octep_vf_setup_iq(oct, i)) { + dev_err(&oct->pdev->dev, + "Failed to setup IQ(TxQ)-%d.\n", i); + goto iq_setup_err; + } + dev_dbg(&oct->pdev->dev, "Successfully setup IQ(TxQ)-%d.\n", i); + } + + return 0; + +iq_setup_err: + while (i) { + i--; + octep_vf_free_iq(oct->iq[i]); + } + return -1; +} + +/** + * octep_vf_free_iqs() - Free resources of all Tx queues. + * + * @oct: Octeon device private data structure. + */ +void octep_vf_free_iqs(struct octep_vf_device *oct) +{ + int i; + + for (i = 0; i < CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); i++) { + octep_vf_free_iq(oct->iq[i]); + dev_dbg(&oct->pdev->dev, + "Successfully destroyed IQ(TxQ)-%d.\n", i); + } + oct->num_iqs = 0; +} diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.h new file mode 100644 index 000000000000..f338b975103c --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.h @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell Octeon EP (EndPoint) VF Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#ifndef _OCTEP_VF_TX_H_ +#define _OCTEP_VF_TX_H_ + +#define IQ_SEND_OK 0 +#define IQ_SEND_STOP 1 +#define IQ_SEND_FAILED -1 + +#define TX_BUFTYPE_NONE 0 +#define TX_BUFTYPE_NET 1 +#define TX_BUFTYPE_NET_SG 2 +#define NUM_TX_BUFTYPES 3 + +/* Hardware format for Scatter/Gather list + * + * 63 48|47 32|31 16|15 0 + * ----------------------------------------- + * | Len 0 | Len 1 | Len 2 | Len 3 | + * ----------------------------------------- + * | Ptr 0 | + * ----------------------------------------- + * | Ptr 1 | + * ----------------------------------------- + * | Ptr 2 | + * ----------------------------------------- + * | Ptr 3 | + * ----------------------------------------- + */ +struct octep_vf_tx_sglist_desc { + u16 len[4]; + dma_addr_t dma_ptr[4]; +}; + +static_assert(sizeof(struct octep_vf_tx_sglist_desc) == 40); + +/* Each Scatter/Gather entry sent to hardwar hold four pointers. + * So, number of entries required is (MAX_SKB_FRAGS + 1)/4, where '+1' + * is for main skb which also goes as a gather buffer to Octeon hardware. + * To allocate sufficient SGLIST entries for a packet with max fragments, + * align by adding 3 before calcuating max SGLIST entries per packet. + */ +#define OCTEP_VF_SGLIST_ENTRIES_PER_PKT ((MAX_SKB_FRAGS + 1 + 3) / 4) +#define OCTEP_VF_SGLIST_SIZE_PER_PKT \ + (OCTEP_VF_SGLIST_ENTRIES_PER_PKT * sizeof(struct octep_vf_tx_sglist_desc)) + +struct octep_vf_tx_buffer { + struct sk_buff *skb; + dma_addr_t dma; + struct octep_vf_tx_sglist_desc *sglist; + dma_addr_t sglist_dma; + u8 gather; +}; + +#define OCTEP_VF_IQ_TXBUFF_INFO_SIZE (sizeof(struct octep_vf_tx_buffer)) + +/* VF Hardware interface Tx statistics */ +struct octep_vf_iface_tx_stats { + /* Total frames sent on the interface */ + u64 pkts; + + /* Total octets sent on the interface */ + u64 octs; + + /* Packets sent to a broadcast DMAC */ + u64 bcst; + + /* Packets sent to the multicast DMAC */ + u64 mcst; + + /* Packets dropped */ + u64 dropped; + + /* Reserved */ + u64 reserved[13]; +}; + +/* VF Input Queue statistics */ +struct octep_vf_iq_stats { + /* Instructions posted to this queue. */ + u64 instr_posted; + + /* Instructions copied by hardware for processing. */ + u64 instr_completed; + + /* Instructions that could not be processed. */ + u64 instr_dropped; + + /* Bytes sent through this queue. */ + u64 bytes_sent; + + /* Gather entries sent through this queue. */ + u64 sgentry_sent; + + /* Number of transmit failures due to TX_BUSY */ + u64 tx_busy; + + /* Number of times the queue is restarted */ + u64 restart_cnt; +}; + +/* The instruction (input) queue. + * The input queue is used to post raw (instruction) mode data or packet + * data to Octeon device from the host. Each input queue (up to 4) for + * a Octeon device has one such structure to represent it. + */ +struct octep_vf_iq { + u32 q_no; + + struct octep_vf_device *octep_vf_dev; + struct net_device *netdev; + struct device *dev; + struct netdev_queue *netdev_q; + + /* Index in input ring where driver should write the next packet */ + u16 host_write_index; + + /* Index in input ring where Octeon is expected to read next packet */ + u16 octep_vf_read_index; + + /* This index aids in finding the window in the queue where Octeon + * has read the commands. + */ + u16 flush_index; + + /* Statistics for this input queue. */ + struct octep_vf_iq_stats stats; + + /* Pointer to the Virtual Base addr of the input ring. */ + struct octep_vf_tx_desc_hw *desc_ring; + + /* DMA mapped base address of the input descriptor ring. */ + dma_addr_t desc_ring_dma; + + /* Info of Tx buffers pending completion. */ + struct octep_vf_tx_buffer *buff_info; + + /* Base pointer to Scatter/Gather lists for all ring descriptors. */ + struct octep_vf_tx_sglist_desc *sglist; + + /* DMA mapped addr of Scatter Gather Lists */ + dma_addr_t sglist_dma; + + /* Octeon doorbell register for the ring. */ + u8 __iomem *doorbell_reg; + + /* Octeon instruction count register for this ring. */ + u8 __iomem *inst_cnt_reg; + + /* interrupt level register for this ring */ + u8 __iomem *intr_lvl_reg; + + /* Maximum no. of instructions in this queue. */ + u32 max_count; + u32 ring_size_mask; + + u32 pkt_in_done; + u32 pkts_processed; + + u32 status; + + /* Number of instructions pending to be posted to Octeon. */ + u32 fill_cnt; + + /* The max. number of instructions that can be held pending by the + * driver before ringing doorbell. + */ + u32 fill_threshold; +}; + +/* Hardware Tx Instruction Header */ +struct octep_vf_instr_hdr { + /* Data Len */ + u64 tlen:16; + + /* Reserved */ + u64 rsvd:20; + + /* PKIND for SDP */ + u64 pkind:6; + + /* Front Data size */ + u64 fsz:6; + + /* No. of entries in gather list */ + u64 gsz:14; + + /* Gather indicator 1=gather*/ + u64 gather:1; + + /* Reserved3 */ + u64 reserved3:1; +}; + +static_assert(sizeof(struct octep_vf_instr_hdr) == 8); + +/* Tx offload flags */ +#define OCTEP_VF_TX_OFFLOAD_VLAN_INSERT BIT(0) +#define OCTEP_VF_TX_OFFLOAD_IPV4_CKSUM BIT(1) +#define OCTEP_VF_TX_OFFLOAD_UDP_CKSUM BIT(2) +#define OCTEP_VF_TX_OFFLOAD_TCP_CKSUM BIT(3) +#define OCTEP_VF_TX_OFFLOAD_SCTP_CKSUM BIT(4) +#define OCTEP_VF_TX_OFFLOAD_TCP_TSO BIT(5) +#define OCTEP_VF_TX_OFFLOAD_UDP_TSO BIT(6) + +#define OCTEP_VF_TX_OFFLOAD_CKSUM (OCTEP_VF_TX_OFFLOAD_IPV4_CKSUM | \ + OCTEP_VF_TX_OFFLOAD_UDP_CKSUM | \ + OCTEP_VF_TX_OFFLOAD_TCP_CKSUM) + +#define OCTEP_VF_TX_OFFLOAD_TSO (OCTEP_VF_TX_OFFLOAD_TCP_TSO | \ + OCTEP_VF_TX_OFFLOAD_UDP_TSO) + +#define OCTEP_VF_TX_IP_CSUM(flags) ((flags) & \ + (OCTEP_VF_TX_OFFLOAD_IPV4_CKSUM | \ + OCTEP_VF_TX_OFFLOAD_TCP_CKSUM | \ + OCTEP_VF_TX_OFFLOAD_UDP_CKSUM)) + +#define OCTEP_VF_TX_TSO(flags) ((flags) & \ + (OCTEP_VF_TX_OFFLOAD_TCP_TSO | \ + OCTEP_VF_TX_OFFLOAD_UDP_TSO)) + +struct tx_mdata { + /* offload flags */ + u16 ol_flags; + + /* gso size */ + u16 gso_size; + + /* gso flags */ + u16 gso_segs; + + /* reserved */ + u16 rsvd1; + + /* reserved */ + u64 rsvd2; +}; + +static_assert(sizeof(struct tx_mdata) == 16); + +/* 64-byte Tx instruction format. + * Format of instruction for a 64-byte mode input queue. + * + * only first 16-bytes (dptr and ih) are mandatory; rest are optional + * and filled by the driver based on firmware/hardware capabilities. + * These optional headers together called Front Data and its size is + * described by ih->fsz. + */ +struct octep_vf_tx_desc_hw { + /* Pointer where the input data is available. */ + u64 dptr; + + /* Instruction Header. */ + union { + struct octep_vf_instr_hdr ih; + u64 ih64; + }; + + union { + u64 txm64[2]; + struct tx_mdata txm; + }; + + /* Additional headers available in a 64-byte instruction. */ + u64 exhdr[4]; +}; + +static_assert(sizeof(struct octep_vf_tx_desc_hw) == 64); + +#define OCTEP_VF_IQ_DESC_SIZE (sizeof(struct octep_vf_tx_desc_hw)) +#endif /* _OCTEP_VF_TX_H_ */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index edeb0f737312..d5c4f810da61 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -837,6 +837,8 @@ enum nix_af_status { NIX_AF_ERR_CQ_CTX_WRITE_ERR = -429, NIX_AF_ERR_AQ_CTX_RETRY_WRITE = -430, NIX_AF_ERR_LINK_CREDITS = -431, + NIX_AF_ERR_INVALID_BPID = -434, + NIX_AF_ERR_INVALID_BPID_REQ = -435, NIX_AF_ERR_INVALID_MCAST_GRP = -436, NIX_AF_ERR_INVALID_MCAST_DEL_REQ = -437, NIX_AF_ERR_NON_CONTIG_MCE_LIST = -438, @@ -1114,6 +1116,7 @@ struct nix_rss_flowkey_cfg { #define NIX_FLOW_KEY_TYPE_INNR_UDP BIT(15) #define NIX_FLOW_KEY_TYPE_INNR_SCTP BIT(16) #define NIX_FLOW_KEY_TYPE_INNR_ETH_DMAC BIT(17) +#define NIX_FLOW_KEY_TYPE_CUSTOM0 BIT(19) #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) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h index b0b4dea548e1..3e6de9d7dde3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h @@ -85,8 +85,7 @@ enum npc_kpu_lc_ltype { enum npc_kpu_ld_ltype { NPC_LT_LD_TCP = 1, NPC_LT_LD_UDP, - NPC_LT_LD_ICMP, - NPC_LT_LD_SCTP, + NPC_LT_LD_SCTP = 4, NPC_LT_LD_ICMP6, NPC_LT_LD_CUSTOM0, NPC_LT_LD_CUSTOM1, @@ -97,6 +96,7 @@ enum npc_kpu_ld_ltype { NPC_LT_LD_NSH, NPC_LT_LD_TU_MPLS_IN_NSH, NPC_LT_LD_TU_MPLS_IN_IP, + NPC_LT_LD_ICMP, }; enum npc_kpu_le_ltype { @@ -140,14 +140,14 @@ enum npc_kpu_lg_ltype { enum npc_kpu_lh_ltype { NPC_LT_LH_TU_TCP = 1, NPC_LT_LH_TU_UDP, - NPC_LT_LH_TU_ICMP, - NPC_LT_LH_TU_SCTP, + NPC_LT_LH_TU_SCTP = 4, NPC_LT_LH_TU_ICMP6, + NPC_LT_LH_CUSTOM0, + NPC_LT_LH_CUSTOM1, NPC_LT_LH_TU_IGMP = 8, NPC_LT_LH_TU_ESP, NPC_LT_LH_TU_AH, - NPC_LT_LH_CUSTOM0 = 0xE, - NPC_LT_LH_CUSTOM1 = 0xF, + NPC_LT_LH_TU_ICMP = 0xF, }; /* NPC port kind defines how the incoming or outgoing packets @@ -155,10 +155,11 @@ enum npc_kpu_lh_ltype { * Software assigns pkind for each incoming port such as CGX * Ethernet interfaces, LBK interfaces, etc. */ -#define NPC_UNRESERVED_PKIND_COUNT NPC_RX_CUSTOM_PRE_L2_PKIND +#define NPC_UNRESERVED_PKIND_COUNT NPC_RX_CPT_HDR_PTP_PKIND enum npc_pkind_type { NPC_RX_LBK_PKIND = 0ULL, + NPC_RX_CPT_HDR_PTP_PKIND = 54ULL, NPC_RX_CUSTOM_PRE_L2_PKIND = 55ULL, NPC_RX_VLAN_EXDSA_PKIND = 56ULL, NPC_RX_CHLEN24B_PKIND = 57ULL, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h index a820bad3abb2..41de72c8607f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h @@ -35,6 +35,7 @@ #define NPC_ETYPE_NSH 0x894f #define NPC_ETYPE_DSA 0xdada #define NPC_ETYPE_PPPOE 0x8864 +#define NPC_ETYPE_ERSPA 0x88be #define NPC_PPP_IP 0x0021 #define NPC_PPP_IP6 0x0057 @@ -59,6 +60,9 @@ #define NPC_IPNH_MPLS 137 #define NPC_IPNH_HOSTID 139 #define NPC_IPNH_SHIM6 140 +#define NPC_IPNH_CUSTOM 253 + +#define NPC_IP6_ROUTE_TYPE 4 #define NPC_UDP_PORT_PTP_E 319 #define NPC_UDP_PORT_PTP_G 320 @@ -187,6 +191,7 @@ enum npc_kpu_parser_state { NPC_S_KPU2_EXDSA, NPC_S_KPU2_CPT_CTAG, NPC_S_KPU2_CPT_QINQ, + NPC_S_KPU2_MT, NPC_S_KPU3_CTAG, NPC_S_KPU3_STAG, NPC_S_KPU3_QINQ, @@ -231,6 +236,7 @@ enum npc_kpu_parser_state { NPC_S_KPU8_ICMP6, NPC_S_KPU8_GRE, NPC_S_KPU8_AH, + NPC_S_KPU8_CUSTOM, NPC_S_KPU9_TU_MPLS_IN_GRE, NPC_S_KPU9_TU_MPLS_IN_NSH, NPC_S_KPU9_TU_MPLS_IN_IP, @@ -242,6 +248,7 @@ enum npc_kpu_parser_state { NPC_S_KPU9_GTPC, NPC_S_KPU9_GTPU, NPC_S_KPU9_ESP, + NPC_S_KPU9_CUSTOM, NPC_S_KPU10_TU_MPLS_IN_VXLANGPE, NPC_S_KPU10_TU_MPLS_PL, NPC_S_KPU10_TU_MPLS, @@ -318,10 +325,10 @@ enum npc_kpu_lc_uflag { NPC_F_LC_U_UNK_PROTO = 0x10, NPC_F_LC_U_IP_FRAG = 0x20, NPC_F_LC_U_IP6_FRAG = 0x40, + NPC_F_LC_L_6TO4 = 0x80, }; enum npc_kpu_lc_lflag { NPC_F_LC_L_IP_IN_IP = 1, - NPC_F_LC_L_6TO4, NPC_F_LC_L_MPLS_IN_IP, NPC_F_LC_L_IP6_TUN_IP6, NPC_F_LC_L_IP6_MPLS_IN_IP, @@ -334,6 +341,8 @@ enum npc_kpu_lc_lflag { NPC_F_LC_L_EXT_MOBILITY, NPC_F_LC_L_EXT_HOSTID, NPC_F_LC_L_EXT_SHIM6, + NPC_F_LC_L_IP6_SRH_SEG_1, + NPC_F_LC_L_IP6_SRH_SEG_2, }; enum npc_kpu_ld_lflag { @@ -970,10 +979,10 @@ static struct npc_kpu_profile_action ikpu_action_entries[] = { { NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, 0, 0, - NPC_S_KPU1_ETHER, 0, 0, + NPC_S_KPU1_CPT_HDR, 48, 0, NPC_LID_LA, NPC_LT_NA, 0, - 0, 0, 0, 0, + 0, 7, 0, 0, }, { @@ -2786,6 +2795,24 @@ static struct npc_kpu_profile_cam kpu2_cam_entries[] = { 0x0000, }, { + NPC_S_KPU2_MT, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_MT, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { NPC_S_NA, 0X00, 0x0000, 0x0000, @@ -4501,6 +4528,24 @@ static struct npc_kpu_profile_cam kpu5_cam_entries[] = { 0xff00, NPC_IP_VER_6, NPC_IP_VER_MASK, + (NPC_IP6_ROUTE_TYPE << 8) | 1, + 0xffff, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_ROUT << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + (NPC_IP6_ROUTE_TYPE << 8) | 2, + 0xffff, + }, + { + NPC_S_KPU5_IP6, 0xff, + NPC_IPNH_ROUT << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, 0x0000, 0x0000, }, @@ -4776,6 +4821,15 @@ static struct npc_kpu_profile_cam kpu5_cam_entries[] = { }, { NPC_S_KPU5_CPT_IP, 0xff, + NPC_IPNH_CUSTOM, + 0x00ff, + NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_CPT_IP, 0xff, 0x0000, 0x0000, NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, @@ -4884,6 +4938,15 @@ static struct npc_kpu_profile_cam kpu5_cam_entries[] = { }, { NPC_S_KPU5_CPT_IP, 0xff, + NPC_IPNH_CUSTOM, + 0x00ff, + NPC_IP_VER_4, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_CPT_IP, 0xff, 0x0000, 0x0000, NPC_IP_VER_4, @@ -5064,6 +5127,15 @@ static struct npc_kpu_profile_cam kpu5_cam_entries[] = { }, { NPC_S_KPU5_CPT_IP6, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + NPC_IP_VER_6, + NPC_IP_VER_MASK, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU5_CPT_IP6, 0xff, 0x0000, 0x0000, NPC_IP_VER_6, @@ -5208,6 +5280,15 @@ static struct npc_kpu_profile_cam kpu6_cam_entries[] = { }, { NPC_S_KPU6_IP6_FRAG, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_FRAG, 0xff, 0x0000, 0x0000, 0x0000, @@ -5325,6 +5406,15 @@ static struct npc_kpu_profile_cam kpu6_cam_entries[] = { }, { NPC_S_KPU6_IP6_HOP_DEST, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_HOP_DEST, 0xff, 0x0000, 0x0000, 0x0000, @@ -5433,6 +5523,15 @@ static struct npc_kpu_profile_cam kpu6_cam_entries[] = { }, { NPC_S_KPU6_IP6_ROUT, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_ROUT, 0xff, 0x0000, 0x0000, 0x0000, @@ -5532,6 +5631,15 @@ static struct npc_kpu_profile_cam kpu6_cam_entries[] = { }, { NPC_S_KPU6_IP6_CPT_FRAG, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_CPT_FRAG, 0xff, 0x0000, 0x0000, 0x0000, @@ -5649,6 +5757,15 @@ static struct npc_kpu_profile_cam kpu6_cam_entries[] = { }, { NPC_S_KPU6_IP6_CPT_HOP_DEST, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_CPT_HOP_DEST, 0xff, 0x0000, 0x0000, 0x0000, @@ -5757,6 +5874,15 @@ static struct npc_kpu_profile_cam kpu6_cam_entries[] = { }, { NPC_S_KPU6_IP6_CPT_ROUT, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU6_IP6_CPT_ROUT, 0xff, 0x0000, 0x0000, 0x0000, @@ -5883,6 +6009,15 @@ static struct npc_kpu_profile_cam kpu7_cam_entries[] = { }, { NPC_S_KPU7_IP6_ROUT, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_ROUT, 0xff, 0x0000, 0x0000, 0x0000, @@ -5982,6 +6117,15 @@ static struct npc_kpu_profile_cam kpu7_cam_entries[] = { }, { NPC_S_KPU7_IP6_FRAG, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + NPC_IP6_FRAG_FRAGOFF, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_IP6_FRAG, 0xff, 0x0000, 0x0000, 0x0000, @@ -6081,6 +6225,15 @@ static struct npc_kpu_profile_cam kpu7_cam_entries[] = { }, { NPC_S_KPU7_CPT_IP6_FRAG, 0xff, + NPC_IPNH_CUSTOM << 8, + 0xff00, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU7_CPT_IP6_FRAG, 0xff, 0x0000, 0x0000, 0x0000, @@ -6310,6 +6463,15 @@ static struct npc_kpu_profile_cam kpu8_cam_entries[] = { 0xffff, 0x0000, 0x0000, + 0x0009, + 0xffff, + }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_ESP, + 0xffff, + 0x0000, + 0x0000, 0x0000, 0x0000, }, @@ -6756,6 +6918,78 @@ static struct npc_kpu_profile_cam kpu8_cam_entries[] = { }, { NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + 0x0000, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + NPC_GRE_F_CSUM, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + NPC_GRE_F_CSUM | NPC_GRE_F_KEY, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + NPC_GRE_F_CSUM | NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + NPC_GRE_F_KEY | NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, + NPC_ETYPE_ERSPA, + 0xffff, + NPC_GRE_F_CSUM | NPC_GRE_F_KEY | NPC_GRE_F_SEQ, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU8_GRE, 0xff, 0x0000, 0xffff, NPC_GRE_F_ROUTE, @@ -6836,6 +7070,15 @@ static struct npc_kpu_profile_cam kpu8_cam_entries[] = { 0x0000, }, { + NPC_S_KPU8_CUSTOM, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { NPC_S_NA, 0X00, 0x0000, 0x0000, @@ -7304,6 +7547,24 @@ static struct npc_kpu_profile_cam kpu9_cam_entries[] = { 0x0000, }, { + NPC_S_KPU9_CUSTOM, 0xff, + 0x4000, + 0xf000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU9_CUSTOM, 0xff, + 0x6000, + 0xf000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { NPC_S_NA, 0X00, 0x0000, 0x0000, @@ -8384,7 +8645,7 @@ static struct npc_kpu_profile_action kpu1_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 3, 0, + 6, 0, 42, 3, 0, NPC_S_KPU5_IP6, 14, 1, NPC_LID_LA, NPC_LT_LA_ETHER, 0, @@ -8536,7 +8797,7 @@ static struct npc_kpu_profile_action kpu1_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 3, 0, + 6, 0, 42, 3, 0, NPC_S_KPU5_IP6, 22, 1, NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, NPC_F_LA_U_HAS_IH_NIX, @@ -8693,7 +8954,7 @@ static struct npc_kpu_profile_action kpu1_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 3, 0, + 6, 0, 42, 3, 0, NPC_S_KPU5_IP6, 30, 1, NPC_LID_LA, NPC_LT_LA_HIGIG2_ETHER, NPC_F_LA_U_HAS_HIGIG2, @@ -8818,7 +9079,7 @@ static struct npc_kpu_profile_action kpu1_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 3, 0, + 6, 0, 42, 3, 0, NPC_S_KPU5_IP6, 38, 1, NPC_LID_LA, NPC_LT_LA_IH_NIX_HIGIG2_ETHER, NPC_F_LA_U_HAS_IH_NIX | NPC_F_LA_U_HAS_HIGIG2, @@ -8947,7 +9208,7 @@ static struct npc_kpu_profile_action kpu1_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 3, 0, + 6, 0, 42, 3, 0, NPC_S_KPU5_IP6, 14, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -9124,7 +9385,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 6, 1, NPC_LID_LB, NPC_LT_LB_CTAG, 0, @@ -9204,7 +9465,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 14, 1, NPC_LID_LB, NPC_LT_LB_PPPOE, 0, @@ -9213,7 +9474,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, - NPC_S_NA, 0, 1, + NPC_S_NA, 6, 1, NPC_LID_LB, NPC_LT_LB_CTAG, NPC_F_LB_U_UNK_ETYPE, 0, 0, 0, 0, @@ -9228,7 +9489,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, @@ -9324,7 +9585,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 24, 1, NPC_LID_LB, NPC_LT_LB_BTAG, NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_ITAG, @@ -9428,7 +9689,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, @@ -9532,7 +9793,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_ETAG, 0, @@ -9628,7 +9889,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 28, 1, NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_ITAG, @@ -9684,7 +9945,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 0, @@ -9757,7 +10018,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, - NPC_S_NA, 0, 1, + NPC_S_NA, 8, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, NPC_F_LB_U_UNK_ETYPE, 0, 0, 0, 0, @@ -9772,7 +10033,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 18, 1, NPC_LID_LB, NPC_LT_LB_EDSA, NPC_F_LB_L_EDSA, @@ -9836,7 +10097,7 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 2, 0, + 6, 0, 42, 2, 0, NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_EXDSA, NPC_F_LB_L_EXDSA, @@ -9923,6 +10184,22 @@ static struct npc_kpu_profile_action kpu2_action_entries[] = { 0, 0, 0, 0, }, { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 0, 0, 0, 0, + NPC_S_KPU3_CTAG, 0, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU3_CTAG_C, 0, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { NPC_ERRLEV_LB, NPC_EC_L2_K3, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, @@ -9949,7 +10226,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 6, 0, NPC_LID_LB, NPC_LT_NA, 0, @@ -10029,7 +10306,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 8, 0, NPC_LID_LB, NPC_LT_NA, 0, @@ -10101,7 +10378,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 4, 0, NPC_LID_LB, NPC_LT_NA, 0, @@ -10165,7 +10442,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 8, 0, NPC_LID_LB, NPC_LT_NA, 0, @@ -10237,7 +10514,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 4, 0, NPC_LID_LB, NPC_LT_NA, 0, @@ -10310,80 +10587,80 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { { NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 6, 1, 0, - NPC_S_KPU5_IP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU5_IP, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, - NPC_S_KPU5_IP6, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + 6, 0, 42, 1, 0, + NPC_S_KPU5_IP6, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 1, 0, - NPC_S_KPU5_ARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU5_ARP, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 1, 0, - NPC_S_KPU5_RARP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU5_RARP, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 1, 0, - NPC_S_KPU5_PTP, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU5_PTP, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 1, 0, - NPC_S_KPU5_FCOE, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU5_FCOE, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, 0, 0, - NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU4_MPLS, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 6, 10, 0, 0, - NPC_S_KPU4_MPLS, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU4_MPLS, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 0, 0, 0, 0, - NPC_S_KPU4_NSH, 4, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_KPU4_NSH, 2, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, 0, 0, 0, 0, 1, - NPC_S_NA, 0, 1, - NPC_LID_LB, NPC_LT_LB_CTAG, + NPC_S_NA, 0, 0, + NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, @@ -10397,7 +10674,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 8, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 0, @@ -10469,7 +10746,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 4, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 0, @@ -10533,7 +10810,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 8, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 0, @@ -10605,7 +10882,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 4, 1, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 0, @@ -10685,7 +10962,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_DSA, NPC_F_LB_L_DSA, @@ -10733,7 +11010,7 @@ static struct npc_kpu_profile_action kpu3_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 1, 0, + 6, 0, 42, 1, 0, NPC_S_KPU5_IP6, 14, 1, NPC_LID_LB, NPC_LT_LB_DSA_VLAN, NPC_F_LB_L_DSA_VLAN, @@ -10894,7 +11171,7 @@ static struct npc_kpu_profile_action kpu4_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 0, 0, + 6, 0, 42, 0, 0, NPC_S_KPU5_IP6, 6, 1, NPC_LID_LB, NPC_LT_LB_FDSA, NPC_F_LB_L_FDSA, @@ -10942,7 +11219,7 @@ static struct npc_kpu_profile_action kpu4_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 0, 0, + 6, 0, 42, 0, 0, NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_FDSA, NPC_F_LB_L_FDSA, @@ -10990,7 +11267,7 @@ static struct npc_kpu_profile_action kpu4_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 0, 0, + 6, 0, 42, 0, 0, NPC_S_KPU5_IP6, 14, 1, NPC_LID_LB, NPC_LT_LB_PPPOE, 0, @@ -11014,7 +11291,7 @@ static struct npc_kpu_profile_action kpu4_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 0, 0, + 6, 0, 42, 0, 0, NPC_S_KPU5_IP6, 2, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -11063,15 +11340,15 @@ static struct npc_kpu_profile_action kpu4_action_entries[] = { { NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 6, 0, 0, - NPC_S_KPU5_IP, 10, 0, + NPC_S_KPU5_IP, 10, 1, NPC_LID_LB, NPC_LT_LB_PPPOE, 0, 0, 0, 0, 0, }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 6, 0, 0, 0, 0, - NPC_S_KPU5_IP6, 10, 0, + 6, 0, 42, 0, 0, + NPC_S_KPU5_IP6, 10, 1, NPC_LID_LB, NPC_LT_LB_PPPOE, 0, 0, 0, 0, 0, @@ -11119,7 +11396,7 @@ static struct npc_kpu_profile_action kpu5_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 2, 0, 0, 2, 0, + 2, 0, 4, 2, 0, NPC_S_KPU8_UDP, 20, 1, NPC_LID_LC, NPC_LT_LC_IP, 0, @@ -11223,7 +11500,7 @@ static struct npc_kpu_profile_action kpu5_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 2, 8, 10, 2, 0, + 2, 8, 4, 2, 0, NPC_S_KPU8_UDP, 0, 1, NPC_LID_LC, NPC_LT_LC_IP_OPT, 0, @@ -11450,6 +11727,22 @@ static struct npc_kpu_profile_action kpu5_action_entries[] = { 0, 0, 0, 0, 0, NPC_S_KPU6_IP6_ROUT, 40, 1, NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_IP6_SRH_SEG_1, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU6_IP6_ROUT, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, + NPC_F_LC_L_IP6_SRH_SEG_2, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU6_IP6_ROUT, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6_EXT, NPC_F_LC_L_EXT_ROUT, 0, 0, 0, 0, }, @@ -11695,6 +11988,14 @@ static struct npc_kpu_profile_action kpu5_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_CUSTOM, 20, 1, + NPC_LID_LC, NPC_LT_LC_IP, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 1, NPC_LID_LC, NPC_LT_LC_IP, @@ -11791,6 +12092,14 @@ static struct npc_kpu_profile_action kpu5_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_CUSTOM, 0, 1, + NPC_LID_LC, NPC_LT_LC_IP_OPT, + 0, + 0, 0xf, 0, 2, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 1, NPC_LID_LC, NPC_LT_LC_IP_OPT, @@ -11951,6 +12260,14 @@ static struct npc_kpu_profile_action kpu5_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 2, 0, + NPC_S_KPU8_CUSTOM, 40, 1, + NPC_LID_LC, NPC_LT_LC_IP6, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 1, NPC_LID_LC, NPC_LT_LC_IP6, @@ -12080,6 +12397,14 @@ static struct npc_kpu_profile_action kpu6_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12184,6 +12509,14 @@ static struct npc_kpu_profile_action kpu6_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12280,6 +12613,14 @@ static struct npc_kpu_profile_action kpu6_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12368,6 +12709,14 @@ static struct npc_kpu_profile_action kpu6_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12472,6 +12821,14 @@ static struct npc_kpu_profile_action kpu6_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12568,6 +12925,14 @@ static struct npc_kpu_profile_action kpu6_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 1, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12681,6 +13046,14 @@ static struct npc_kpu_profile_action kpu7_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 1, 0xff, 0, 3, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12769,6 +13142,14 @@ static struct npc_kpu_profile_action kpu7_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -12857,6 +13238,14 @@ static struct npc_kpu_profile_action kpu7_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU8_CUSTOM, 8, 0, + NPC_LID_LC, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LC, NPC_LT_NA, @@ -13058,6 +13447,14 @@ static struct npc_kpu_profile_action kpu8_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 0, NPC_S_KPU9_ESP, 8, 1, NPC_LID_LD, NPC_LT_LD_UDP, @@ -13458,6 +13855,70 @@ static struct npc_kpu_profile_action kpu8_action_entries[] = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 12, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 16, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_SEQ, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 20, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 20, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_SEQ, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 20, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_KEY_SEQ, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 24, 1, + NPC_LID_LD, NPC_LT_LD_GRE, + NPC_F_LD_L_GRE_HAS_CSUM_KEY_SEQ, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 1, NPC_LID_LD, NPC_LT_LD_GRE, @@ -13529,6 +13990,14 @@ static struct npc_kpu_profile_action kpu8_action_entries[] = { 0, 0, 0, 0, }, { + NPC_ERRLEV_LD, NPC_EC_NOERR, + 0, 0, 0, 0, 0, + NPC_S_KPU9_CUSTOM, 0, 1, + NPC_LID_LF, NPC_LT_LF_CUSTOM0, + 0, + 0, 0xff, 0, 0, + }, + { NPC_ERRLEV_LD, NPC_EC_UNK, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, @@ -13946,6 +14415,22 @@ static struct npc_kpu_profile_action kpu9_action_entries[] = { 0, 0, 0, 0, }, { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU12_TU_IP, 0, 0, + NPC_LID_LE, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 0, 2, 0, + NPC_S_KPU12_TU_IP6, 0, 0, + NPC_LID_LE, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { NPC_ERRLEV_LE, NPC_EC_UNK, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, @@ -15105,7 +15590,9 @@ static struct npc_lt_def_cfg npc_lt_defaults = { }, .rx_et = { { - .lid = NPC_LID_LB, + .offset = -2, + .valid = 1, + .lid = NPC_LID_LC, .ltype_match = NPC_LT_NA, .ltype_mask = 0x0, }, @@ -15139,6 +15626,12 @@ static struct npc_mcam_kex npc_mkex_default = { /* Ethertype: 2 bytes, KW0[55:40] */ KEX_LD_CFG(0x01, 0xc, 0x1, 0x0, 0x5), }, + [NPC_LT_LA_CPT_HDR] = { + /* DMAC: 6 bytes, KW1[55:8] */ + KEX_LD_CFG(0x05, 0x0, 0x1, 0x0, NPC_KEXOF_DMAC), + /* Ethertype: 2 bytes, KW0[55:40] */ + KEX_LD_CFG(0x01, 0xc, 0x1, 0x0, 0x5), + }, /* Layer A: HiGig2: */ [NPC_LT_LA_HIGIG2_ETHER] = { /* Classification: 2 bytes, KW1[23:8] */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 5c1d04a3c559..edd12d09dc89 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -1484,7 +1484,7 @@ int rvu_get_nix_blkaddr(struct rvu *rvu, u16 pcifunc) /* All CGX mapped PFs are set with assigned NIX block during init */ if (is_pf_cgxmapped(rvu, rvu_get_pf(pcifunc))) { blkaddr = pf->nix_blkaddr; - } else if (is_afvf(pcifunc)) { + } else if (is_lbk_vf(rvu, pcifunc)) { vf = pcifunc - 1; /* Assign NIX based on VF number. All even numbered VFs get * NIX0 and odd numbered gets NIX1 @@ -2034,7 +2034,7 @@ int rvu_mbox_handler_set_vf_perm(struct rvu *rvu, struct set_vf_perm *req, u16 target; /* Only PF can add VF permissions */ - if ((pcifunc & RVU_PFVF_FUNC_MASK) || is_afvf(pcifunc)) + if ((pcifunc & RVU_PFVF_FUNC_MASK) || is_lbk_vf(rvu, pcifunc)) return -EOPNOTSUPP; target = (pcifunc & ~RVU_PFVF_FUNC_MASK) | (req->vf + 1); @@ -2618,6 +2618,9 @@ static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc) * 3. Cleanup pools (NPA) */ + /* Free allocated BPIDs */ + rvu_nix_flr_free_bpids(rvu, pcifunc); + /* Free multicast/mirror node associated with the 'pcifunc' */ rvu_nix_mcast_flr_free_entries(rvu, pcifunc); @@ -3151,6 +3154,7 @@ static int rvu_enable_sriov(struct rvu *rvu) { struct pci_dev *pdev = rvu->pdev; int err, chans, vfs; + int pos = 0; if (!rvu_afvf_msix_vectors_num_ok(rvu)) { dev_warn(&pdev->dev, @@ -3158,6 +3162,12 @@ static int rvu_enable_sriov(struct rvu *rvu) return 0; } + /* Get RVU VFs device id */ + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) + return 0; + pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &rvu->vf_devid); + chans = rvu_get_num_lbk_chans(); if (chans < 0) return chans; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 43be37dd1f32..de8eba902276 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -288,6 +288,16 @@ enum rvu_pfvf_flags { #define RVU_CLEAR_VF_PERM ~GENMASK(PF_SET_VF_TRUSTED, PF_SET_VF_MAC) +struct nix_bp { + struct rsrc_bmap bpids; /* free bpids bitmap */ + u16 cgx_bpid_cnt; + u16 sdp_bpid_cnt; + u16 free_pool_base; + u16 *fn_map; /* pcifunc mapping */ + u8 *intf_map; /* interface type map */ + u8 *ref_cnt; +}; + struct nix_txsch { struct rsrc_bmap schq; u8 lvl; @@ -363,6 +373,7 @@ struct nix_hw { struct nix_lso lso; struct nix_txvlan txvlan; struct nix_ipolicer *ipolicer; + struct nix_bp bp; u64 *tx_credits; u8 cc_mcs_cnt; }; @@ -432,6 +443,13 @@ struct mbox_wq_info { struct workqueue_struct *mbox_wq; }; +struct channel_fwdata { + struct sdp_node_info info; + u8 valid; +#define RVU_CHANL_INFO_RESERVED 379 + u8 reserved[RVU_CHANL_INFO_RESERVED]; +}; + struct rvu_fwdata { #define RVU_FWDATA_HEADER_MAGIC 0xCFDA /* Custom Firmware Data*/ #define RVU_FWDATA_VERSION 0x0001 @@ -450,7 +468,8 @@ struct rvu_fwdata { u64 msixtr_base; u32 ptp_ext_clk_rate; u32 ptp_ext_tstamp; -#define FWDATA_RESERVED_MEM 1022 + struct channel_fwdata channel_data; +#define FWDATA_RESERVED_MEM 1014 u64 reserved[FWDATA_RESERVED_MEM]; #define CGX_MAX 9 #define CGX_LMACS_MAX 4 @@ -503,6 +522,7 @@ struct rvu { struct mutex rsrc_lock; /* Serialize resource alloc/free */ struct mutex alias_lock; /* Serialize bar2 alias access */ int vfs; /* Number of VFs attached to RVU */ + u16 vf_devid; /* VF devices id */ int nix_blkaddr[MAX_NIX_BLKS]; /* Mbox */ @@ -732,9 +752,11 @@ static inline bool is_rvu_supports_nix1(struct rvu *rvu) /* Function Prototypes * RVU */ -static inline bool is_afvf(u16 pcifunc) +#define RVU_LBK_VF_DEVID 0xA0F8 +static inline bool is_lbk_vf(struct rvu *rvu, u16 pcifunc) { - return !(pcifunc & ~RVU_PFVF_FUNC_MASK); + return (!(pcifunc & ~RVU_PFVF_FUNC_MASK) && + (rvu->vf_devid == RVU_LBK_VF_DEVID)); } static inline bool is_vf(u16 pcifunc) @@ -794,7 +816,7 @@ void rvu_aq_free(struct rvu *rvu, struct admin_queue *aq); int rvu_sdp_init(struct rvu *rvu); bool is_sdp_pfvf(u16 pcifunc); bool is_sdp_pf(u16 pcifunc); -bool is_sdp_vf(u16 pcifunc); +bool is_sdp_vf(struct rvu *rvu, u16 pcifunc); /* CGX APIs */ static inline bool is_pf_cgxmapped(struct rvu *rvu, u8 pf) @@ -873,6 +895,7 @@ int rvu_nix_mcast_get_mce_index(struct rvu *rvu, u16 pcifunc, u32 mcast_grp_idx); int rvu_nix_mcast_update_mcam_entry(struct rvu *rvu, u16 pcifunc, u32 mcast_grp_idx, u16 mcam_index); +void rvu_nix_flr_free_bpids(struct rvu *rvu, u16 pcifunc); /* NPC APIs */ void rvu_npc_freemem(struct rvu *rvu); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 66203a90f052..d39001cdc707 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -499,29 +499,115 @@ static void nix_interface_deinit(struct rvu *rvu, u16 pcifunc, u8 nixlf) rvu_cgx_disable_dmac_entries(rvu, pcifunc); } +#define NIX_BPIDS_PER_LMAC 8 +#define NIX_BPIDS_PER_CPT 1 +static int nix_setup_bpids(struct rvu *rvu, struct nix_hw *hw, int blkaddr) +{ + struct nix_bp *bp = &hw->bp; + int err, max_bpids; + u64 cfg; + + cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST1); + max_bpids = FIELD_GET(NIX_CONST_MAX_BPIDS, cfg); + + /* Reserve the BPIds for CGX and SDP */ + bp->cgx_bpid_cnt = rvu->hw->cgx_links * NIX_BPIDS_PER_LMAC; + bp->sdp_bpid_cnt = rvu->hw->sdp_links * FIELD_GET(NIX_CONST_SDP_CHANS, cfg); + bp->free_pool_base = bp->cgx_bpid_cnt + bp->sdp_bpid_cnt + + NIX_BPIDS_PER_CPT; + bp->bpids.max = max_bpids - bp->free_pool_base; + + err = rvu_alloc_bitmap(&bp->bpids); + if (err) + return err; + + bp->fn_map = devm_kcalloc(rvu->dev, bp->bpids.max, + sizeof(u16), GFP_KERNEL); + if (!bp->fn_map) + return -ENOMEM; + + bp->intf_map = devm_kcalloc(rvu->dev, bp->bpids.max, + sizeof(u8), GFP_KERNEL); + if (!bp->intf_map) + return -ENOMEM; + + bp->ref_cnt = devm_kcalloc(rvu->dev, bp->bpids.max, + sizeof(u8), GFP_KERNEL); + if (!bp->ref_cnt) + return -ENOMEM; + + return 0; +} + +void rvu_nix_flr_free_bpids(struct rvu *rvu, u16 pcifunc) +{ + int blkaddr, bpid, err; + struct nix_hw *nix_hw; + struct nix_bp *bp; + + if (!is_lbk_vf(rvu, pcifunc)) + return; + + err = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); + if (err) + return; + + bp = &nix_hw->bp; + + mutex_lock(&rvu->rsrc_lock); + for (bpid = 0; bpid < bp->bpids.max; bpid++) { + if (bp->fn_map[bpid] == pcifunc) { + bp->ref_cnt[bpid]--; + if (bp->ref_cnt[bpid]) + continue; + rvu_free_rsrc(&bp->bpids, bpid); + bp->fn_map[bpid] = 0; + } + } + mutex_unlock(&rvu->rsrc_lock); +} + int rvu_mbox_handler_nix_bp_disable(struct rvu *rvu, struct nix_bp_cfg_req *req, struct msg_rsp *rsp) { u16 pcifunc = req->hdr.pcifunc; + int blkaddr, pf, type, err; + u16 chan_base, chan, bpid; struct rvu_pfvf *pfvf; - int blkaddr, pf, type; - u16 chan_base, chan; + struct nix_hw *nix_hw; + struct nix_bp *bp; u64 cfg; pf = rvu_get_pf(pcifunc); - type = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; + type = is_lbk_vf(rvu, pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; if (!is_pf_cgxmapped(rvu, pf) && type != NIX_INTF_TYPE_LBK) return 0; pfvf = rvu_get_pfvf(rvu, pcifunc); - blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + err = nix_get_struct_ptrs(rvu, pcifunc, &nix_hw, &blkaddr); + if (err) + return err; + bp = &nix_hw->bp; chan_base = pfvf->rx_chan_base + req->chan_base; for (chan = chan_base; chan < (chan_base + req->chan_cnt); chan++) { cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan)); rvu_write64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan), cfg & ~BIT_ULL(16)); + + if (type == NIX_INTF_TYPE_LBK) { + bpid = cfg & GENMASK(8, 0); + mutex_lock(&rvu->rsrc_lock); + rvu_free_rsrc(&bp->bpids, bpid - bp->free_pool_base); + for (bpid = 0; bpid < bp->bpids.max; bpid++) { + if (bp->fn_map[bpid] == pcifunc) { + bp->fn_map[bpid] = 0; + bp->ref_cnt[bpid] = 0; + } + } + mutex_unlock(&rvu->rsrc_lock); + } } return 0; } @@ -529,25 +615,20 @@ int rvu_mbox_handler_nix_bp_disable(struct rvu *rvu, static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, int type, int chan_id) { - int bpid, blkaddr, lmac_chan_cnt, sdp_chan_cnt; - u16 cgx_bpid_cnt, lbk_bpid_cnt, sdp_bpid_cnt; + int bpid, blkaddr, sdp_chan_base, err; struct rvu_hwinfo *hw = rvu->hw; struct rvu_pfvf *pfvf; + struct nix_hw *nix_hw; u8 cgx_id, lmac_id; - u64 cfg; - - blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, req->hdr.pcifunc); - cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST); - lmac_chan_cnt = cfg & 0xFF; + struct nix_bp *bp; - cgx_bpid_cnt = hw->cgx_links * lmac_chan_cnt; - lbk_bpid_cnt = hw->lbk_links * ((cfg >> 16) & 0xFF); + pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); - cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST1); - sdp_chan_cnt = cfg & 0xFFF; - sdp_bpid_cnt = hw->sdp_links * sdp_chan_cnt; + err = nix_get_struct_ptrs(rvu, req->hdr.pcifunc, &nix_hw, &blkaddr); + if (err) + return err; - pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + bp = &nix_hw->bp; /* Backpressure IDs range division * CGX channles are mapped to (0 - 191) BPIDs @@ -561,38 +642,48 @@ static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, */ switch (type) { case NIX_INTF_TYPE_CGX: - if ((req->chan_base + req->chan_cnt) > 16) - return -EINVAL; + if ((req->chan_base + req->chan_cnt) > NIX_BPIDS_PER_LMAC) + return NIX_AF_ERR_INVALID_BPID_REQ; rvu_get_cgx_lmac_id(pfvf->cgx_lmac, &cgx_id, &lmac_id); /* Assign bpid based on cgx, lmac and chan id */ - bpid = (cgx_id * hw->lmac_per_cgx * lmac_chan_cnt) + - (lmac_id * lmac_chan_cnt) + req->chan_base; + bpid = (cgx_id * hw->lmac_per_cgx * NIX_BPIDS_PER_LMAC) + + (lmac_id * NIX_BPIDS_PER_LMAC) + req->chan_base; if (req->bpid_per_chan) bpid += chan_id; - if (bpid > cgx_bpid_cnt) - return -EINVAL; + if (bpid > bp->cgx_bpid_cnt) + return NIX_AF_ERR_INVALID_BPID; break; case NIX_INTF_TYPE_LBK: - if ((req->chan_base + req->chan_cnt) > 63) - return -EINVAL; - bpid = cgx_bpid_cnt + req->chan_base; - if (req->bpid_per_chan) - bpid += chan_id; - if (bpid > (cgx_bpid_cnt + lbk_bpid_cnt)) - return -EINVAL; + /* Alloc bpid from the free pool */ + mutex_lock(&rvu->rsrc_lock); + bpid = rvu_alloc_rsrc(&bp->bpids); + if (bpid < 0) { + mutex_unlock(&rvu->rsrc_lock); + return NIX_AF_ERR_INVALID_BPID; + } + bp->fn_map[bpid] = req->hdr.pcifunc; + bp->ref_cnt[bpid]++; + bpid += bp->free_pool_base; + mutex_unlock(&rvu->rsrc_lock); break; case NIX_INTF_TYPE_SDP: - if ((req->chan_base + req->chan_cnt) > 255) - return -EINVAL; + if ((req->chan_base + req->chan_cnt) > bp->sdp_bpid_cnt) + return NIX_AF_ERR_INVALID_BPID_REQ; - bpid = sdp_bpid_cnt + req->chan_base; + /* Handle usecase of 2 SDP blocks */ + if (!hw->cap.programmable_chans) + sdp_chan_base = pfvf->rx_chan_base - NIX_CHAN_SDP_CH_START; + else + sdp_chan_base = pfvf->rx_chan_base - hw->sdp_chan_base; + + bpid = bp->cgx_bpid_cnt + req->chan_base + sdp_chan_base; if (req->bpid_per_chan) bpid += chan_id; - if (bpid > (cgx_bpid_cnt + lbk_bpid_cnt + sdp_bpid_cnt)) - return -EINVAL; + if (bpid > (bp->cgx_bpid_cnt + bp->sdp_bpid_cnt)) + return NIX_AF_ERR_INVALID_BPID; break; default: return -EINVAL; @@ -612,7 +703,7 @@ int rvu_mbox_handler_nix_bp_enable(struct rvu *rvu, u64 cfg; pf = rvu_get_pf(pcifunc); - type = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; + type = is_lbk_vf(rvu, pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; if (is_sdp_pfvf(pcifunc)) type = NIX_INTF_TYPE_SDP; @@ -1523,7 +1614,7 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, cfg = NPC_TX_DEF_PKIND; rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); - intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; + intf = is_lbk_vf(rvu, pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX; if (is_sdp_pfvf(pcifunc)) intf = NIX_INTF_TYPE_SDP; @@ -1899,7 +1990,7 @@ static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc) int pf = rvu_get_pf(pcifunc); u8 cgx_id = 0, lmac_id = 0; - if (is_afvf(pcifunc)) {/* LBK links */ + if (is_lbk_vf(rvu, pcifunc)) {/* LBK links */ return hw->cgx_links; } else if (is_pf_cgxmapped(rvu, pf)) { rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); @@ -1916,7 +2007,7 @@ static void nix_get_txschq_range(struct rvu *rvu, u16 pcifunc, struct rvu_hwinfo *hw = rvu->hw; int pf = rvu_get_pf(pcifunc); - if (is_afvf(pcifunc)) { /* LBK links */ + if (is_lbk_vf(rvu, pcifunc)) { /* LBK links */ *start = hw->cap.nix_txsch_per_cgx_lmac * link; *end = *start + hw->cap.nix_txsch_per_lbk_lmac; } else if (is_pf_cgxmapped(rvu, pf)) { /* CGX links */ @@ -3356,7 +3447,7 @@ static int nix_update_mce_rule(struct rvu *rvu, u16 pcifunc, int pf; /* skip multicast pkt replication for AF's VFs & SDP links */ - if (is_afvf(pcifunc) || is_sdp_pfvf(pcifunc)) + if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(pcifunc)) return 0; if (!hw->cap.nix_rx_multicast) @@ -3703,7 +3794,7 @@ int rvu_mbox_handler_nix_get_hw_info(struct rvu *rvu, struct msg_req *req, if (blkaddr < 0) return NIX_AF_ERR_AF_LF_INVALID; - if (is_afvf(pcifunc)) + if (is_lbk_vf(rvu, pcifunc)) rvu_get_lbk_link_max_frs(rvu, &rsp->max_mtu); else rvu_get_lmac_link_max_frs(rvu, &rsp->max_mtu); @@ -4039,6 +4130,13 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) field->ltype_match = NPC_LT_LE_GTPU; field->ltype_mask = 0xF; break; + case NIX_FLOW_KEY_TYPE_CUSTOM0: + field->lid = NPC_LID_LC; + field->hdr_offset = 6; + field->bytesm1 = 1; /* 2 Bytes*/ + field->ltype_match = NPC_LT_LC_CUSTOM0; + field->ltype_mask = 0xF; + break; case NIX_FLOW_KEY_TYPE_VLAN: field->lid = NPC_LID_LB; field->hdr_offset = 2; /* Skip TPID (2-bytes) */ @@ -4420,7 +4518,7 @@ int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req, if (!nix_hw) return NIX_AF_ERR_INVALID_NIXBLK; - if (is_afvf(pcifunc)) + if (is_lbk_vf(rvu, pcifunc)) rvu_get_lbk_link_max_frs(rvu, &max_mtu); else rvu_get_lmac_link_max_frs(rvu, &max_mtu); @@ -4784,6 +4882,10 @@ static int rvu_nix_block_init(struct rvu *rvu, struct nix_hw *nix_hw) if (err) return err; + err = nix_setup_bpids(rvu, nix_hw, blkaddr); + if (err) + return err; + /* Configure segmentation offload formats */ nix_setup_lso(rvu, nix_hw, blkaddr); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 167145bdcb75..8ec0b9764649 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -61,28 +61,6 @@ int rvu_npc_get_tx_nibble_cfg(struct rvu *rvu, u64 nibble_ena) return 0; } -static int npc_mcam_verify_pf_func(struct rvu *rvu, - struct mcam_entry *entry_data, u8 intf, - u16 pcifunc) -{ - u16 pf_func, pf_func_mask; - - if (is_npc_intf_rx(intf)) - return 0; - - pf_func_mask = (entry_data->kw_mask[0] >> 32) & - NPC_KEX_PF_FUNC_MASK; - pf_func = (entry_data->kw[0] >> 32) & NPC_KEX_PF_FUNC_MASK; - - pf_func = be16_to_cpu((__force __be16)pf_func); - if (pf_func_mask != NPC_KEX_PF_FUNC_MASK || - ((pf_func & ~RVU_PFVF_FUNC_MASK) != - (pcifunc & ~RVU_PFVF_FUNC_MASK))) - return -EINVAL; - - return 0; -} - void rvu_npc_set_pkind(struct rvu *rvu, int pkind, struct rvu_pfvf *pfvf) { int blkaddr; @@ -417,7 +395,7 @@ static void npc_fixup_vf_rule(struct rvu *rvu, struct npc_mcam *mcam, owner = mcam->entry2pfvf_map[index]; target_func = (entry->action >> 4) & 0xffff; /* do nothing when target is LBK/PF or owner is not PF */ - if (is_pffunc_af(owner) || is_afvf(target_func) || + if (is_pffunc_af(owner) || is_lbk_vf(rvu, target_func) || (owner & RVU_PFVF_FUNC_MASK) || !(target_func & RVU_PFVF_FUNC_MASK)) return; @@ -626,7 +604,7 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, int blkaddr, index; /* AF's and SDP VFs work in promiscuous mode */ - if (is_afvf(pcifunc) || is_sdp_vf(pcifunc)) + if (is_lbk_vf(rvu, pcifunc) || is_sdp_vf(rvu, pcifunc)) return; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -791,7 +769,7 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, return; /* Skip LBK VFs */ - if (is_afvf(pcifunc)) + if (is_lbk_vf(rvu, pcifunc)) return; /* If pkt replication is not supported, @@ -871,7 +849,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u16 vf_func; /* Only CGX PF/VF can add allmulticast entry */ - if (is_afvf(pcifunc) && is_sdp_vf(pcifunc)) + if (is_lbk_vf(rvu, pcifunc) && is_sdp_vf(rvu, pcifunc)) return; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -1850,8 +1828,8 @@ void npc_mcam_rsrcs_deinit(struct rvu *rvu) { struct npc_mcam *mcam = &rvu->hw->mcam; - kfree(mcam->bmap); - kfree(mcam->bmap_reverse); + bitmap_free(mcam->bmap); + bitmap_free(mcam->bmap_reverse); kfree(mcam->entry2pfvf_map); kfree(mcam->cntr2pfvf_map); kfree(mcam->entry2cntr_map); @@ -1904,21 +1882,20 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) mcam->pf_offset = mcam->nixlf_offset + nixlf_count; /* Allocate bitmaps for managing MCAM entries */ - mcam->bmap = kmalloc_array(BITS_TO_LONGS(mcam->bmap_entries), - sizeof(long), GFP_KERNEL); + mcam->bmap = bitmap_zalloc(mcam->bmap_entries, GFP_KERNEL); if (!mcam->bmap) return -ENOMEM; - mcam->bmap_reverse = kmalloc_array(BITS_TO_LONGS(mcam->bmap_entries), - sizeof(long), GFP_KERNEL); + mcam->bmap_reverse = bitmap_zalloc(mcam->bmap_entries, GFP_KERNEL); if (!mcam->bmap_reverse) goto free_bmap; mcam->bmap_fcnt = mcam->bmap_entries; /* Alloc memory for saving entry to RVU PFFUNC allocation mapping */ - mcam->entry2pfvf_map = kmalloc_array(mcam->bmap_entries, - sizeof(u16), GFP_KERNEL); + mcam->entry2pfvf_map = kcalloc(mcam->bmap_entries, sizeof(u16), + GFP_KERNEL); + if (!mcam->entry2pfvf_map) goto free_bmap_reverse; @@ -1941,21 +1918,21 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) if (err) goto free_entry_map; - mcam->cntr2pfvf_map = kmalloc_array(mcam->counters.max, - sizeof(u16), GFP_KERNEL); + mcam->cntr2pfvf_map = kcalloc(mcam->counters.max, sizeof(u16), + GFP_KERNEL); if (!mcam->cntr2pfvf_map) goto free_cntr_bmap; /* Alloc memory for MCAM entry to counter mapping and for tracking * counter's reference count. */ - mcam->entry2cntr_map = kmalloc_array(mcam->bmap_entries, - sizeof(u16), GFP_KERNEL); + mcam->entry2cntr_map = kcalloc(mcam->bmap_entries, sizeof(u16), + GFP_KERNEL); if (!mcam->entry2cntr_map) goto free_cntr_map; - mcam->cntr_refcnt = kmalloc_array(mcam->counters.max, - sizeof(u16), GFP_KERNEL); + mcam->cntr_refcnt = kcalloc(mcam->counters.max, sizeof(u16), + GFP_KERNEL); if (!mcam->cntr_refcnt) goto free_entry_cntr_map; @@ -1988,9 +1965,9 @@ free_cntr_bmap: free_entry_map: kfree(mcam->entry2pfvf_map); free_bmap_reverse: - kfree(mcam->bmap_reverse); + bitmap_free(mcam->bmap_reverse); free_bmap: - kfree(mcam->bmap); + bitmap_free(mcam->bmap); return -ENOMEM; } @@ -2852,12 +2829,6 @@ int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu, else nix_intf = pfvf->nix_rx_intf; - if (!is_pffunc_af(pcifunc) && - npc_mcam_verify_pf_func(rvu, &req->entry_data, req->intf, pcifunc)) { - rc = NPC_MCAM_INVALID_REQ; - goto exit; - } - /* For AF installed rules, the nix_intf should be set to target NIX */ if (is_pffunc_af(req->hdr.pcifunc)) nix_intf = req->intf; @@ -3209,10 +3180,6 @@ int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu, if (!is_npc_interface_valid(rvu, req->intf)) return NPC_MCAM_INVALID_REQ; - if (npc_mcam_verify_pf_func(rvu, &req->entry_data, req->intf, - req->hdr.pcifunc)) - return NPC_MCAM_INVALID_REQ; - /* Try to allocate a MCAM entry */ entry_req.hdr.pcifunc = req->hdr.pcifunc; entry_req.contig = true; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h index 6f73ad9807f0..086f05c0376f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h @@ -439,6 +439,9 @@ #define NIX_AF_LINKX_RANGE_MASK GENMASK_ULL(19, 16) #define NIX_AF_LINKX_MCS_CNT_MASK GENMASK_ULL(33, 32) +#define NIX_CONST_MAX_BPIDS GENMASK_ULL(23, 12) +#define NIX_CONST_SDP_CHANS GENMASK_ULL(11, 0) + /* SSO */ #define SSO_AF_CONST (0x1000) #define SSO_AF_CONST1 (0x1008) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c index ae50d56258ec..38cfe148f4b7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c @@ -40,8 +40,12 @@ bool is_sdp_pf(u16 pcifunc) !(pcifunc & RVU_PFVF_FUNC_MASK)); } -bool is_sdp_vf(u16 pcifunc) +#define RVU_SDP_VF_DEVID 0xA0F7 +bool is_sdp_vf(struct rvu *rvu, u16 pcifunc) { + if (!(pcifunc & ~RVU_PFVF_FUNC_MASK)) + return (rvu->vf_devid == RVU_SDP_VF_DEVID); + return (is_sdp_pfvf(pcifunc) && !!(pcifunc & RVU_PFVF_FUNC_MASK)); } @@ -52,6 +56,14 @@ int rvu_sdp_init(struct rvu *rvu) struct rvu_pfvf *pfvf; u32 i = 0; + if (rvu->fwdata->channel_data.valid) { + sdp_pf_num[0] = 0; + pfvf = &rvu->pf[sdp_pf_num[0]]; + pfvf->sdp_info = &rvu->fwdata->channel_data.info; + + return 0; + } + while ((i < MAX_SDP) && (pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OTX2_SDP_PF, pdev)) != NULL) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 7ca6941ea0b9..02d0b707aea5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -951,8 +951,11 @@ int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) if (pfvf->ptp && qidx < pfvf->hw.tx_queues) { err = qmem_alloc(pfvf->dev, &sq->timestamps, qset->sqe_cnt, sizeof(*sq->timestamps)); - if (err) + if (err) { + kfree(sq->sg); + sq->sg = NULL; return err; + } } sq->head = 0; @@ -968,7 +971,14 @@ int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) sq->stats.bytes = 0; sq->stats.pkts = 0; - return pfvf->hw_ops->sq_aq_init(pfvf, qidx, sqb_aura); + err = pfvf->hw_ops->sq_aq_init(pfvf, qidx, sqb_aura); + if (err) { + kfree(sq->sg); + sq->sg = NULL; + return err; + } + + return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index 2928898c7f8d..7f786de61014 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -314,7 +314,6 @@ static int otx2_set_channels(struct net_device *dev, pfvf->hw.tx_queues = channel->tx_count; if (pfvf->xdp_prog) pfvf->hw.xdp_queues = channel->rx_count; - pfvf->hw.non_qos_queues = pfvf->hw.tx_queues + pfvf->hw.xdp_queues; if (if_up) err = dev->netdev_ops->ndo_open(dev); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index a57455aebff6..e5fe67e73865 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1744,6 +1744,7 @@ int otx2_open(struct net_device *netdev) /* RQ and SQs are mapped to different CQs, * so find out max CQ IRQs (i.e CINTs) needed. */ + pf->hw.non_qos_queues = pf->hw.tx_queues + pf->hw.xdp_queues; pf->hw.cint_cnt = max3(pf->hw.rx_queues, pf->hw.tx_queues, pf->hw.tc_tx_queues); @@ -2643,8 +2644,6 @@ static int otx2_xdp_setup(struct otx2_nic *pf, struct bpf_prog *prog) xdp_features_clear_redirect_target(dev); } - pf->hw.non_qos_queues += pf->hw.xdp_queues; - if (if_up) otx2_open(pf->netdev); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 4d519ea833b2..f828d32737af 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -1403,7 +1403,7 @@ static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, struct otx2_cq_queue *cq, bool *need_xdp_flush) { - unsigned char *hard_start, *data; + unsigned char *hard_start; int qidx = cq->cq_idx; struct xdp_buff xdp; struct page *page; @@ -1417,9 +1417,8 @@ static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, xdp_init_buff(&xdp, pfvf->rbsize, &cq->xdp_rxq); - data = (unsigned char *)phys_to_virt(pa); - hard_start = page_address(page); - xdp_prepare_buff(&xdp, hard_start, data - hard_start, + hard_start = (unsigned char *)phys_to_virt(pa); + xdp_prepare_buff(&xdp, hard_start, OTX2_HEAD_ROOM, cqe->sg.seg_size, false); act = bpf_prog_run_xdp(prog, &xdp); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index a6e91573f8da..de123350bd46 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4761,7 +4761,10 @@ static int mtk_probe(struct platform_device *pdev) } if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); + if (!err) + err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (err) { dev_err(&pdev->dev, "Wrong DMA config\n"); return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index f5b1f8c7834f..7f20813456e2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2199,8 +2199,9 @@ reset_slave: if (cmd != MLX4_COMM_CMD_RESET) { mlx4_warn(dev, "Turn on internal error to force reset, slave=%d, cmd=0x%x\n", slave, cmd); - /* Turn on internal error letting slave reset itself immeditaly, - * otherwise it might take till timeout on command is passed + /* Turn on internal error letting slave reset itself + * immediately, otherwise it might take till timeout on + * command is passed */ reply |= ((u32)COMM_CHAN_EVENT_INTERNAL_ERR); } @@ -2954,7 +2955,7 @@ static bool mlx4_valid_vf_state_change(struct mlx4_dev *dev, int port, dummy_admin.default_vlan = vlan; /* VF wants to move to other VST state which is valid with current - * rate limit. Either differnt default vlan in VST or other + * rate limit. Either different default vlan in VST or other * supported QoS priority. Otherwise we don't allow this change when * the TX rate is still configured. */ diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 4d4f9cf9facb..e130e7259275 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -115,7 +115,7 @@ void mlx4_cq_completion(struct mlx4_dev *dev, u32 cqn) return; } - /* Acessing the CQ outside of rcu_read_lock is safe, because + /* Accessing the CQ outside of rcu_read_lock is safe, because * the CQ is freed only after interrupt handling is completed. */ ++cq->arm_sn; @@ -137,7 +137,7 @@ void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type) return; } - /* Acessing the CQ outside of rcu_read_lock is safe, because + /* Accessing the CQ outside of rcu_read_lock is safe, because * the CQ is freed only after interrupt handling is completed. */ cq->event(cq, event_type); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index 9e3b76182088..cd754cd76bde 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -96,8 +96,8 @@ void mlx4_en_remove_timestamp(struct mlx4_en_dev *mdev) #define MLX4_EN_WRAP_AROUND_SEC 10UL /* By scheduling the overflow check every 5 seconds, we have a reasonably - * good chance we wont miss a wrap around. - * TOTO: Use a timer instead of a work queue to increase the guarantee. + * good chance we won't miss a wrap around. + * TODO: Use a timer instead of a work queue to increase the guarantee. */ #define MLX4_EN_OVERFLOW_PERIOD (MLX4_EN_WRAP_AROUND_SEC * HZ / 2) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 33bbcced8105..d7da62cda821 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1072,7 +1072,8 @@ static void mlx4_en_do_multicast(struct mlx4_en_priv *priv, 1, MLX4_MCAST_CONFIG); /* Update multicast list - we cache all addresses so they won't - * change while HW is updated holding the command semaphor */ + * change while HW is updated holding the command semaphore + */ netif_addr_lock_bh(dev); mlx4_en_cache_mclist(dev); netif_addr_unlock_bh(dev); @@ -1817,7 +1818,7 @@ int mlx4_en_start_port(struct net_device *dev) mlx4_en_set_rss_steer_rules(priv)) mlx4_warn(mdev, "Failed setting steering rules\n"); - /* Attach rx QP to bradcast address */ + /* Attach rx QP to broadcast address */ eth_broadcast_addr(&mc_list[10]); mc_list[5] = priv->port; /* needed for B0 steering support */ if (mlx4_multicast_attach(mdev->dev, priv->rss_map.indir_qp, mc_list, diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index a09b6e05337d..eac49657bd07 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -762,7 +762,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud /* Drop packet on bad receive or bad checksum */ if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) == MLX4_CQE_OPCODE_ERROR)) { - en_err(priv, "CQE completed in error - vendor syndrom:%d syndrom:%d\n", + en_err(priv, "CQE completed in error - vendor syndrome:%d syndrome:%d\n", ((struct mlx4_err_cqe *)cqe)->vendor_err_syndrome, ((struct mlx4_err_cqe *)cqe)->syndrome); goto next; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 65cb63f6c465..1ddb11cb25f9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -992,7 +992,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) tx_info->ts_requested = 1; } - /* Prepare ctrl segement apart opcode+ownership, which depends on + /* Prepare ctrl segment apart opcode+ownership, which depends on * whether LSO is used */ tx_desc->ctrl.srcrb_flags = priv->ctrl_flags; if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 6598b10a9ff4..9572a45f6143 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -210,7 +210,7 @@ static void slave_event(struct mlx4_dev *dev, u8 slave, struct mlx4_eqe *eqe) memcpy(s_eqe, eqe, sizeof(struct mlx4_eqe) - 1); s_eqe->slave_id = slave; - /* ensure all information is written before setting the ownersip bit */ + /* ensure all information is written before setting the ownership bit */ dma_wmb(); s_eqe->owner = !!(slave_eq->prod & SLAVE_EVENT_EQ_SIZE) ? 0x0 : 0x80; ++slave_eq->prod; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h index 954b86faac29..40ca29bb928c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -44,7 +44,7 @@ /* Default supported priorities for VPP allocation */ #define MLX4_DEFAULT_QOS_PRIO (0) -/* Derived from FW feature definition, 0 is the default vport fo all QPs */ +/* Derived from FW feature definition, 0 is the default vport for all QPs */ #define MLX4_VPP_DEFAULT_VPORT (0) struct mlx4_vport_qos_param { @@ -98,7 +98,7 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, u16 *available_vpp, u8 *vpp_p_up); /** - * mlx4_ALLOCATE_VPP_set - Distribution of VPPs among differnt priorities. + * mlx4_ALLOCATE_VPP_set - Distribution of VPPs among different priorities. * The total number of VPPs assigned to all for a port must not exceed * the value reported by available_vpp in mlx4_ALLOCATE_VPP_get. * VPP allocation is allowed only after the port type has been set, @@ -113,7 +113,7 @@ int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up); /** - * mlx4_SET_VPORT_QOS_get - Query QoS proporties of a Vport. + * mlx4_SET_VPORT_QOS_get - Query QoS properties of a Vport. * Each priority allowed for the Vport is assigned with a share of the BW, * and a BW limitation. This commands query the current QoS values. * @@ -128,7 +128,7 @@ int mlx4_SET_VPORT_QOS_get(struct mlx4_dev *dev, u8 port, u8 vport, struct mlx4_vport_qos_param *out_param); /** - * mlx4_SET_VPORT_QOS_set - Set QoS proporties of a Vport. + * mlx4_SET_VPORT_QOS_set - Set QoS properties of a Vport. * QoS parameters can be modified at any time, but must be initialized * before any QP is associated with the VPort. * diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 2581226836b5..7b02ff61126d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -129,7 +129,7 @@ static const struct mlx4_profile default_profile = { .num_cq = 1 << 16, .num_mcg = 1 << 13, .num_mpt = 1 << 19, - .num_mtt = 1 << 20, /* It is really num mtt segements */ + .num_mtt = 1 << 20, /* It is really num mtt segments */ }; static const struct mlx4_profile low_mem_profile = { @@ -1508,7 +1508,7 @@ static int mlx4_port_map_set(struct mlx4_dev *dev, struct mlx4_port_map *v2p) priv->v2p.port1 = port1; priv->v2p.port2 = port2; } else { - mlx4_err(dev, "Failed to change port mape: %d\n", err); + mlx4_err(dev, "Failed to change port map: %d\n", err); } } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h index e9cd4bb6f83d..d3d9ec042d2c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h @@ -112,7 +112,7 @@ struct mlx4_en_stat_out_flow_control_mbox { __be64 tx_pause_duration; /* Number of transmitter transitions from XOFF state to XON state */ __be64 tx_pause_transition; - /* Reserverd */ + /* Reserved */ __be64 reserved[2]; }; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 256a06b3c096..4e43f4a7d246 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -2118,7 +2118,7 @@ static void mlx4_qsfp_eeprom_params_set(u8 *i2c_addr, u8 *page_num, u16 *offset) * @data: output buffer to put the requested data into. * * Reads cable module eeprom data, puts the outcome data into - * data pointer paramer. + * data pointer parameter. * Returns num of read bytes on success or a negative error * code. */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c index cf0477f53dc4..47e7c2639774 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c @@ -210,7 +210,7 @@ static bool is_dpll_supported(struct mlx5_core_dev *dev) return false; if (!MLX5_CAP_MCAM_REG2(dev, synce_registers)) { - mlx5_core_warn(dev, "Missing SyncE capability\n"); + mlx5_core_dbg(dev, "Missing SyncE capability\n"); return false; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c index 18fed2b34fb1..c9c7fddb246f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c @@ -41,6 +41,7 @@ struct mlx5_dpll_synce_status { enum mlx5_msees_oper_status oper_status; bool ho_acq; bool oper_freq_measure; + enum mlx5_msees_failure_reason failure_reason; s32 frequency_diff; }; @@ -60,6 +61,7 @@ mlx5_dpll_synce_status_get(struct mlx5_core_dev *mdev, synce_status->oper_status = MLX5_GET(msees_reg, out, oper_status); synce_status->ho_acq = MLX5_GET(msees_reg, out, ho_acq); synce_status->oper_freq_measure = MLX5_GET(msees_reg, out, oper_freq_measure); + synce_status->failure_reason = MLX5_GET(msees_reg, out, failure_reason); synce_status->frequency_diff = MLX5_GET(msees_reg, out, frequency_diff); return 0; } @@ -99,6 +101,26 @@ mlx5_dpll_lock_status_get(struct mlx5_dpll_synce_status *synce_status) } } +static enum dpll_lock_status_error +mlx5_dpll_lock_status_error_get(struct mlx5_dpll_synce_status *synce_status) +{ + switch (synce_status->oper_status) { + case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER: + fallthrough; + case MLX5_MSEES_OPER_STATUS_FAIL_FREE_RUNNING: + switch (synce_status->failure_reason) { + case MLX5_MSEES_FAILURE_REASON_PORT_DOWN: + return DPLL_LOCK_STATUS_ERROR_MEDIA_DOWN; + case MLX5_MSEES_FAILURE_REASON_TOO_HIGH_FREQUENCY_DIFF: + return DPLL_LOCK_STATUS_ERROR_FRACTIONAL_FREQUENCY_OFFSET_TOO_HIGH; + default: + return DPLL_LOCK_STATUS_ERROR_UNDEFINED; + } + default: + return DPLL_LOCK_STATUS_ERROR_NONE; + } +} + static enum dpll_pin_state mlx5_dpll_pin_state_get(struct mlx5_dpll_synce_status *synce_status) { @@ -118,10 +140,11 @@ mlx5_dpll_pin_ffo_get(struct mlx5_dpll_synce_status *synce_status, return 0; } -static int mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll, - void *priv, - enum dpll_lock_status *status, - struct netlink_ext_ack *extack) +static int +mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll, void *priv, + enum dpll_lock_status *status, + enum dpll_lock_status_error *status_error, + struct netlink_ext_ack *extack) { struct mlx5_dpll_synce_status synce_status; struct mlx5_dpll *mdpll = priv; @@ -131,6 +154,7 @@ static int mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll, if (err) return err; *status = mlx5_dpll_lock_status_get(&synce_status); + *status_error = mlx5_dpll_lock_status_error_get(&synce_status); return 0; } @@ -389,7 +413,7 @@ static void mlx5_dpll_remove(struct auxiliary_device *adev) struct mlx5_dpll *mdpll = auxiliary_get_drvdata(adev); struct mlx5_core_dev *mdev = mdpll->mdev; - cancel_delayed_work(&mdpll->work); + cancel_delayed_work_sync(&mdpll->work); mlx5_dpll_mdev_netdev_untrack(mdpll, mdev); destroy_workqueue(mdpll->wq); dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index 5d213a9886f1..5757f4f10c12 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -240,11 +240,14 @@ static u32 mlx5e_rx_get_linear_sz_xsk(struct mlx5e_params *params, return xsk->headroom + hw_mtu; } -static u32 mlx5e_rx_get_linear_sz_skb(struct mlx5e_params *params, bool xsk) +static u32 mlx5e_rx_get_linear_sz_skb(struct mlx5e_params *params, bool no_head_tail_room) { - /* SKBs built on XDP_PASS on XSK RQs don't have headroom. */ - u16 headroom = xsk ? 0 : mlx5e_get_linear_rq_headroom(params, NULL); u32 hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); + u16 headroom; + + if (no_head_tail_room) + return SKB_DATA_ALIGN(hw_mtu); + headroom = mlx5e_get_linear_rq_headroom(params, NULL); return MLX5_SKB_FRAG_SZ(headroom + hw_mtu); } @@ -254,6 +257,7 @@ static u32 mlx5e_rx_get_linear_stride_sz(struct mlx5_core_dev *mdev, struct mlx5e_xsk_param *xsk, bool mpwqe) { + bool no_head_tail_room; u32 sz; /* XSK frames are mapped as individual pages, because frames may come in @@ -262,7 +266,13 @@ static u32 mlx5e_rx_get_linear_stride_sz(struct mlx5_core_dev *mdev, if (xsk) return mpwqe ? 1 << mlx5e_mpwrq_page_shift(mdev, xsk) : PAGE_SIZE; - sz = roundup_pow_of_two(mlx5e_rx_get_linear_sz_skb(params, false)); + no_head_tail_room = params->xdp_prog && mpwqe && !mlx5e_rx_is_linear_skb(mdev, params, xsk); + + /* When no_head_tail_room is set, headroom and tailroom are excluded from skb calculations. + * no_head_tail_room should be set in the case of XDP with Striding RQ + * when SKB is not linear. This is because another page is allocated for the linear part. + */ + sz = roundup_pow_of_two(mlx5e_rx_get_linear_sz_skb(params, no_head_tail_room)); /* XDP in mlx5e doesn't support multiple packets per page. * Do not assume sz <= PAGE_SIZE if params->xdp_prog is set. @@ -289,7 +299,11 @@ bool mlx5e_rx_is_linear_skb(struct mlx5_core_dev *mdev, if (params->packet_merge.type != MLX5E_PACKET_MERGE_NONE) return false; - /* Both XSK and non-XSK cases allocate an SKB on XDP_PASS. Packet data + /* Call mlx5e_rx_get_linear_sz_skb with the no_head_tail_room parameter set + * to exclude headroom and tailroom from calculations. + * no_head_tail_room is true when SKB is built on XDP_PASS on XSK RQs + * since packet data buffers don't have headroom and tailroom resreved for the SKB. + * Both XSK and non-XSK cases allocate an SKB on XDP_PASS. Packet data * must fit into a CPU page. */ if (mlx5e_rx_get_linear_sz_skb(params, xsk) > PAGE_SIZE) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index 078f56a3cbb2..fd4ef6431142 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -935,6 +935,7 @@ void mlx5e_ptp_activate_channel(struct mlx5e_ptp *c) if (test_bit(MLX5E_PTP_STATE_RX, c->state)) { mlx5e_ptp_rx_set_fs(c->priv); mlx5e_activate_rq(&c->rq); + netif_queue_set_napi(c->netdev, c->rq.ix, NETDEV_QUEUE_TYPE_RX, &c->napi); } mlx5e_trigger_napi_sched(&c->napi); } @@ -943,8 +944,10 @@ void mlx5e_ptp_deactivate_channel(struct mlx5e_ptp *c) { int tc; - if (test_bit(MLX5E_PTP_STATE_RX, c->state)) + if (test_bit(MLX5E_PTP_STATE_RX, c->state)) { + netif_queue_set_napi(c->netdev, c->rq.ix, NETDEV_QUEUE_TYPE_RX, NULL); mlx5e_deactivate_rq(&c->rq); + } if (test_bit(MLX5E_PTP_STATE_TX, c->state)) { for (tc = 0; tc < c->num_tc; tc++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 05612d9c6080..c54fd01ea635 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -984,21 +984,41 @@ static void mlx5e_xfrm_advance_esn_state(struct xfrm_state *x) queue_work(sa_entry->ipsec->wq, &work->work); } -static void mlx5e_xfrm_update_curlft(struct xfrm_state *x) +static void mlx5e_xfrm_update_stats(struct xfrm_state *x) { struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x); struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; + struct net *net = dev_net(x->xso.dev); u64 packets, bytes, lastuse; lockdep_assert(lockdep_is_held(&x->lock) || - lockdep_is_held(&dev_net(x->xso.real_dev)->xfrm.xfrm_cfg_mutex)); + lockdep_is_held(&dev_net(x->xso.real_dev)->xfrm.xfrm_cfg_mutex) || + lockdep_is_held(&dev_net(x->xso.real_dev)->xfrm.xfrm_state_lock)); if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) return; + if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_IN) { + mlx5_fc_query_cached(ipsec_rule->auth.fc, &bytes, &packets, &lastuse); + x->stats.integrity_failed += packets; + XFRM_ADD_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR, packets); + + mlx5_fc_query_cached(ipsec_rule->trailer.fc, &bytes, &packets, &lastuse); + XFRM_ADD_STATS(net, LINUX_MIB_XFRMINHDRERROR, packets); + } + + if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) + return; + mlx5_fc_query_cached(ipsec_rule->fc, &bytes, &packets, &lastuse); x->curlft.packets += packets; x->curlft.bytes += bytes; + + if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_IN) { + mlx5_fc_query_cached(ipsec_rule->replay.fc, &bytes, &packets, &lastuse); + x->stats.replay += packets; + XFRM_ADD_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR, packets); + } } static int mlx5e_xfrm_validate_policy(struct mlx5_core_dev *mdev, @@ -1156,7 +1176,7 @@ static const struct xfrmdev_ops mlx5e_ipsec_xfrmdev_ops = { .xdo_dev_offload_ok = mlx5e_ipsec_offload_ok, .xdo_dev_state_advance_esn = mlx5e_xfrm_advance_esn_state, - .xdo_dev_state_update_curlft = mlx5e_xfrm_update_curlft, + .xdo_dev_state_update_stats = mlx5e_xfrm_update_stats, .xdo_dev_policy_add = mlx5e_xfrm_add_policy, .xdo_dev_policy_delete = mlx5e_xfrm_del_policy, .xdo_dev_policy_free = mlx5e_xfrm_free_policy, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index adaea3493193..7d943e93cf6d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -137,7 +137,6 @@ struct mlx5e_ipsec_hw_stats { struct mlx5e_ipsec_sw_stats { atomic64_t ipsec_rx_drop_sp_alloc; atomic64_t ipsec_rx_drop_sadb_miss; - atomic64_t ipsec_rx_drop_syndrome; atomic64_t ipsec_tx_drop_bundle; atomic64_t ipsec_tx_drop_no_state; atomic64_t ipsec_tx_drop_not_ip; 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 51a144246ea6..727fa7c18523 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 @@ -304,12 +304,6 @@ drop: return false; } -enum { - MLX5E_IPSEC_OFFLOAD_RX_SYNDROME_DECRYPTED, - MLX5E_IPSEC_OFFLOAD_RX_SYNDROME_AUTH_FAILED, - MLX5E_IPSEC_OFFLOAD_RX_SYNDROME_BAD_TRAILER, -}; - void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb, u32 ipsec_meta_data) @@ -343,20 +337,7 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, xo = xfrm_offload(skb); xo->flags = CRYPTO_DONE; - - switch (MLX5_IPSEC_METADATA_SYNDROM(ipsec_meta_data)) { - case MLX5E_IPSEC_OFFLOAD_RX_SYNDROME_DECRYPTED: - xo->status = CRYPTO_SUCCESS; - break; - case MLX5E_IPSEC_OFFLOAD_RX_SYNDROME_AUTH_FAILED: - xo->status = CRYPTO_TUNNEL_ESP_AUTH_FAILED; - break; - case MLX5E_IPSEC_OFFLOAD_RX_SYNDROME_BAD_TRAILER: - xo->status = CRYPTO_INVALID_PACKET_SYNTAX; - break; - default: - atomic64_inc(&ipsec->sw_stats.ipsec_rx_drop_syndrome); - } + xo->status = CRYPTO_SUCCESS; } int mlx5_esw_ipsec_rx_make_metadata(struct mlx5e_priv *priv, u32 id, u32 *metadata) @@ -374,8 +355,6 @@ int mlx5_esw_ipsec_rx_make_metadata(struct mlx5e_priv *priv, u32 id, u32 *metada return err; } - *metadata = MLX5_IPSEC_METADATA_CREATE(ipsec_obj_id, - MLX5E_IPSEC_OFFLOAD_RX_SYNDROME_DECRYPTED); - + *metadata = ipsec_obj_id; return 0; } 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 2ed99772f168..82064614846f 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 @@ -43,7 +43,6 @@ #define MLX5_IPSEC_METADATA_MARKER(metadata) (((metadata) >> 31) & 0x1) #define MLX5_IPSEC_METADATA_SYNDROM(metadata) (((metadata) >> 24) & GENMASK(5, 0)) #define MLX5_IPSEC_METADATA_HANDLE(metadata) ((metadata) & GENMASK(23, 0)) -#define MLX5_IPSEC_METADATA_CREATE(id, syndrome) ((id) | ((syndrome) << 24)) struct mlx5e_accel_tx_ipsec_state { struct xfrm_offload *xo; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c index e0e36a09721c..dd36b04e30a0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c @@ -51,7 +51,6 @@ static const struct counter_desc mlx5e_ipsec_hw_stats_desc[] = { static const struct counter_desc mlx5e_ipsec_sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_rx_drop_sp_alloc) }, { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_rx_drop_sadb_miss) }, - { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_rx_drop_syndrome) }, { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_tx_drop_bundle) }, { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_tx_drop_no_state) }, { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_tx_drop_not_ip) }, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index c8e8f512803e..be809556b2e1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1806,6 +1806,7 @@ void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq) set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); netdev_tx_reset_queue(sq->txq); netif_tx_start_queue(sq->txq); + netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, sq->cq.napi); } void mlx5e_tx_disable_queue(struct netdev_queue *txq) @@ -1819,6 +1820,7 @@ void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) { struct mlx5_wq_cyc *wq = &sq->wq; + netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, NULL); clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); synchronize_net(); /* Sync with NAPI to prevent netif_tx_wake_queue. */ @@ -2560,6 +2562,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, c->lag_port = mlx5e_enumerate_lag_port(priv->mdev, ix); netif_napi_add(netdev, &c->napi, mlx5e_napi_poll); + netif_napi_set_irq(&c->napi, irq); err = mlx5e_open_queues(c, params, cparam); if (unlikely(err)) @@ -2602,12 +2605,16 @@ static void mlx5e_activate_channel(struct mlx5e_channel *c) mlx5e_activate_xsk(c); else mlx5e_activate_rq(&c->rq); + + netif_queue_set_napi(c->netdev, c->ix, NETDEV_QUEUE_TYPE_RX, &c->napi); } static void mlx5e_deactivate_channel(struct mlx5e_channel *c) { int tc; + netif_queue_set_napi(c->netdev, c->ix, NETDEV_QUEUE_TYPE_RX, NULL); + if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)) mlx5e_deactivate_xsk(c); else diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 58f4c0d0fafa..e7faf7e73ca4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -366,18 +366,18 @@ int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev) return -EIO; } - mlx5_set_nic_state(dev, MLX5_NIC_IFC_DISABLED); + mlx5_set_nic_state(dev, MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED); /* Loop until device state turns to disable */ end = jiffies + msecs_to_jiffies(delay_ms); do { - if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED) + if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) break; cond_resched(); } while (!time_after(jiffies, end)); - if (mlx5_get_nic_state(dev) != MLX5_NIC_IFC_DISABLED) { + if (mlx5_get_nic_state(dev) != MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) { dev_err(&dev->pdev->dev, "NIC IFC still %d after %lums.\n", mlx5_get_nic_state(dev), delay_ms); return -EIO; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 8ff6dc9bc803..9463ede84d8d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -116,9 +116,9 @@ u32 mlx5_health_check_fatal_sensors(struct mlx5_core_dev *dev) return MLX5_SENSOR_PCI_COMM_ERR; if (pci_channel_offline(dev->pdev)) return MLX5_SENSOR_PCI_ERR; - if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED) + if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) return MLX5_SENSOR_NIC_DISABLED; - if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_SW_RESET) + if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_SW_RESET) return MLX5_SENSOR_NIC_SW_RESET; if (sensor_fw_synd_rfr(dev)) return MLX5_SENSOR_FW_SYND_RFR; @@ -185,7 +185,7 @@ static bool reset_fw_if_needed(struct mlx5_core_dev *dev) /* Write the NIC interface field to initiate the reset, the command * interface address also resides here, don't overwrite it. */ - mlx5_set_nic_state(dev, MLX5_NIC_IFC_SW_RESET); + mlx5_set_nic_state(dev, MLX5_INITIAL_SEG_NIC_INTERFACE_SW_RESET); return true; } @@ -246,13 +246,13 @@ recover_from_sw_reset: /* Recover from SW reset */ end = jiffies + msecs_to_jiffies(delay_ms); do { - if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED) + if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) break; msleep(20); } while (!time_after(jiffies, end)); - if (mlx5_get_nic_state(dev) != MLX5_NIC_IFC_DISABLED) { + if (mlx5_get_nic_state(dev) != MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) { dev_err(&dev->pdev->dev, "NIC IFC still %d after %lums.\n", mlx5_get_nic_state(dev), delay_ms); } @@ -272,26 +272,26 @@ static void mlx5_handle_bad_state(struct mlx5_core_dev *dev) u8 nic_interface = mlx5_get_nic_state(dev); switch (nic_interface) { - case MLX5_NIC_IFC_FULL: + case MLX5_INITIAL_SEG_NIC_INTERFACE_FULL_DRIVER: mlx5_core_warn(dev, "Expected to see disabled NIC but it is full driver\n"); break; - case MLX5_NIC_IFC_DISABLED: + case MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED: mlx5_core_warn(dev, "starting teardown\n"); break; - case MLX5_NIC_IFC_NO_DRAM_NIC: + case MLX5_INITIAL_SEG_NIC_INTERFACE_NO_DRAM_NIC: mlx5_core_warn(dev, "Expected to see disabled NIC but it is no dram nic\n"); break; - case MLX5_NIC_IFC_SW_RESET: + case MLX5_INITIAL_SEG_NIC_INTERFACE_SW_RESET: /* The IFC mode field is 3 bits, so it will read 0x7 in 2 cases: * 1. PCI has been disabled (ie. PCI-AER, PF driver unloaded * and this is a VF), this is not recoverable by SW reset. * Logging of this is handled elsewhere. * 2. FW reset has been issued by another function, driver can * be reloaded to recover after the mode switches to - * MLX5_NIC_IFC_DISABLED. + * MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED. */ if (dev->priv.health.fatal_error != MLX5_SENSOR_PCI_COMM_ERR) mlx5_core_warn(dev, "NIC SW reset in progress\n"); @@ -555,12 +555,17 @@ static void mlx5_fw_reporter_err_work(struct work_struct *work) &fw_reporter_ctx); } -static const struct devlink_health_reporter_ops mlx5_fw_reporter_ops = { +static const struct devlink_health_reporter_ops mlx5_fw_reporter_pf_ops = { .name = "fw", .diagnose = mlx5_fw_reporter_diagnose, .dump = mlx5_fw_reporter_dump, }; +static const struct devlink_health_reporter_ops mlx5_fw_reporter_ops = { + .name = "fw", + .diagnose = mlx5_fw_reporter_diagnose, +}; + static int mlx5_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter, void *priv_ctx, @@ -646,12 +651,17 @@ static void mlx5_fw_fatal_reporter_err_work(struct work_struct *work) } } -static const struct devlink_health_reporter_ops mlx5_fw_fatal_reporter_ops = { +static const struct devlink_health_reporter_ops mlx5_fw_fatal_reporter_pf_ops = { .name = "fw_fatal", .recover = mlx5_fw_fatal_reporter_recover, .dump = mlx5_fw_fatal_reporter_dump, }; +static const struct devlink_health_reporter_ops mlx5_fw_fatal_reporter_ops = { + .name = "fw_fatal", + .recover = mlx5_fw_fatal_reporter_recover, +}; + #define MLX5_FW_REPORTER_ECPF_GRACEFUL_PERIOD 180000 #define MLX5_FW_REPORTER_PF_GRACEFUL_PERIOD 60000 #define MLX5_FW_REPORTER_VF_GRACEFUL_PERIOD 30000 @@ -659,10 +669,14 @@ static const struct devlink_health_reporter_ops mlx5_fw_fatal_reporter_ops = { void mlx5_fw_reporters_create(struct mlx5_core_dev *dev) { + const struct devlink_health_reporter_ops *fw_fatal_ops; struct mlx5_core_health *health = &dev->priv.health; + const struct devlink_health_reporter_ops *fw_ops; struct devlink *devlink = priv_to_devlink(dev); u64 grace_period; + fw_fatal_ops = &mlx5_fw_fatal_reporter_pf_ops; + fw_ops = &mlx5_fw_reporter_pf_ops; if (mlx5_core_is_ecpf(dev)) { grace_period = MLX5_FW_REPORTER_ECPF_GRACEFUL_PERIOD; } else if (mlx5_core_is_pf(dev)) { @@ -670,18 +684,19 @@ void mlx5_fw_reporters_create(struct mlx5_core_dev *dev) } else { /* VF or SF */ grace_period = MLX5_FW_REPORTER_DEFAULT_GRACEFUL_PERIOD; + fw_fatal_ops = &mlx5_fw_fatal_reporter_ops; + fw_ops = &mlx5_fw_reporter_ops; } health->fw_reporter = - devl_health_reporter_create(devlink, &mlx5_fw_reporter_ops, - 0, dev); + devl_health_reporter_create(devlink, fw_ops, 0, dev); if (IS_ERR(health->fw_reporter)) mlx5_core_warn(dev, "Failed to create fw reporter, err = %ld\n", PTR_ERR(health->fw_reporter)); health->fw_fatal_reporter = devl_health_reporter_create(devlink, - &mlx5_fw_fatal_reporter_ops, + fw_fatal_ops, grace_period, dev); if (IS_ERR(health->fw_fatal_reporter)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index bccf6e53556c..c2593625c09a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -187,31 +187,36 @@ static struct mlx5_profile profile[] = { }; static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili, - u32 warn_time_mili) + u32 warn_time_mili, const char *init_state) { unsigned long warn = jiffies + msecs_to_jiffies(warn_time_mili); unsigned long end = jiffies + msecs_to_jiffies(max_wait_mili); u32 fw_initializing; - int err = 0; do { fw_initializing = ioread32be(&dev->iseg->initializing); if (!(fw_initializing >> 31)) break; - if (time_after(jiffies, end) || - test_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state)) { - err = -EBUSY; - break; + if (time_after(jiffies, end)) { + mlx5_core_err(dev, "Firmware over %u MS in %s state, aborting\n", + max_wait_mili, init_state); + return -ETIMEDOUT; + } + if (test_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state)) { + mlx5_core_warn(dev, "device is being removed, stop waiting for FW %s\n", + init_state); + return -ENODEV; } if (warn_time_mili && time_after(jiffies, warn)) { - mlx5_core_warn(dev, "Waiting for FW initialization, timeout abort in %ds (0x%x)\n", - jiffies_to_msecs(end - warn) / 1000, fw_initializing); + mlx5_core_warn(dev, "Waiting for FW %s, timeout abort in %ds (0x%x)\n", + init_state, jiffies_to_msecs(end - warn) / 1000, + fw_initializing); warn = jiffies + msecs_to_jiffies(warn_time_mili); } msleep(mlx5_tout_ms(dev, FW_PRE_INIT_WAIT)); } while (true); - return err; + return 0; } static void mlx5_set_driver_version(struct mlx5_core_dev *dev) @@ -1151,12 +1156,10 @@ static int mlx5_function_enable(struct mlx5_core_dev *dev, bool boot, u64 timeou /* wait for firmware to accept initialization segments configurations */ err = wait_fw_init(dev, timeout, - mlx5_tout_ms(dev, FW_PRE_INIT_WARN_MESSAGE_INTERVAL)); - if (err) { - mlx5_core_err(dev, "Firmware over %llu MS in pre-initializing state, aborting\n", - timeout); + mlx5_tout_ms(dev, FW_PRE_INIT_WARN_MESSAGE_INTERVAL), + "pre-initializing"); + if (err) return err; - } err = mlx5_cmd_enable(dev); if (err) { @@ -1166,12 +1169,9 @@ static int mlx5_function_enable(struct mlx5_core_dev *dev, bool boot, u64 timeou mlx5_tout_query_iseg(dev); - err = wait_fw_init(dev, mlx5_tout_ms(dev, FW_INIT), 0); - if (err) { - mlx5_core_err(dev, "Firmware over %llu MS in initializing state, aborting\n", - mlx5_tout_ms(dev, FW_INIT)); + err = wait_fw_init(dev, mlx5_tout_ms(dev, FW_INIT), 0, "initializing"); + if (err) goto err_cmd_cleanup; - } dev->caps.embedded_cpu = mlx5_read_embedded_cpu(dev); mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_UP); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index a79b7959361b..58732f44940f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -312,13 +312,6 @@ static inline int mlx5_rescan_drivers(struct mlx5_core_dev *dev) return ret; } -enum { - MLX5_NIC_IFC_FULL = 0, - MLX5_NIC_IFC_DISABLED = 1, - MLX5_NIC_IFC_NO_DRAM_NIC = 2, - MLX5_NIC_IFC_SW_RESET = 7 -}; - u8 mlx5_get_nic_state(struct mlx5_core_dev *dev); void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c index c93492b67788..99219ea52c4b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c @@ -74,7 +74,8 @@ static void mlx5_sf_dev_release(struct device *device) kfree(sf_dev); } -static void mlx5_sf_dev_remove(struct mlx5_core_dev *dev, struct mlx5_sf_dev *sf_dev) +static void mlx5_sf_dev_remove_aux(struct mlx5_core_dev *dev, + struct mlx5_sf_dev *sf_dev) { int id; @@ -138,7 +139,7 @@ static void mlx5_sf_dev_add(struct mlx5_core_dev *dev, u16 sf_index, u16 fn_id, return; xa_err: - mlx5_sf_dev_remove(dev, sf_dev); + mlx5_sf_dev_remove_aux(dev, 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); @@ -149,7 +150,7 @@ static void mlx5_sf_dev_del(struct mlx5_core_dev *dev, struct mlx5_sf_dev *sf_de struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table; xa_erase(&table->devices, sf_index); - mlx5_sf_dev_remove(dev, sf_dev); + mlx5_sf_dev_remove_aux(dev, sf_dev); } static int @@ -367,7 +368,7 @@ static void mlx5_sf_dev_destroy_all(struct mlx5_sf_dev_table *table) xa_for_each(&table->devices, index, sf_dev) { xa_erase(&table->devices, index); - mlx5_sf_dev_remove(table->dev, sf_dev); + mlx5_sf_dev_remove_aux(table->dev, sf_dev); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c index 169c2c68ed5c..bc863e1f062e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c @@ -95,24 +95,29 @@ mdev_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 = priv_to_devlink(sf_dev->mdev); + struct mlx5_core_dev *mdev = sf_dev->mdev; + struct devlink *devlink; - mlx5_drain_health_wq(sf_dev->mdev); + devlink = priv_to_devlink(mdev); + set_bit(MLX5_BREAK_FW_WAIT, &mdev->intf_state); + mlx5_drain_health_wq(mdev); devlink_unregister(devlink); - if (mlx5_dev_is_lightweight(sf_dev->mdev)) - mlx5_uninit_one_light(sf_dev->mdev); + if (mlx5_dev_is_lightweight(mdev)) + mlx5_uninit_one_light(mdev); else - mlx5_uninit_one(sf_dev->mdev); - iounmap(sf_dev->mdev->iseg); - mlx5_mdev_uninit(sf_dev->mdev); + mlx5_uninit_one(mdev); + iounmap(mdev->iseg); + mlx5_mdev_uninit(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); + struct mlx5_core_dev *mdev = sf_dev->mdev; - mlx5_unload_one(sf_dev->mdev, false); + set_bit(MLX5_BREAK_FW_WAIT, &mdev->intf_state); + mlx5_unload_one(mdev, false); } static const struct auxiliary_device_id mlx5_sf_dev_id_table[] = { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c index 7e36e1062139..64f4cc284aea 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c @@ -54,6 +54,107 @@ enum dr_dump_rec_type { DR_DUMP_REC_TYPE_ACTION_MATCH_RANGE = 3425, }; +static struct mlx5dr_dbg_dump_buff * +mlx5dr_dbg_dump_data_init_new_buff(struct mlx5dr_dbg_dump_data *dump_data) +{ + struct mlx5dr_dbg_dump_buff *new_buff; + + new_buff = kzalloc(sizeof(*new_buff), GFP_KERNEL); + if (!new_buff) + return NULL; + + new_buff->buff = kvzalloc(MLX5DR_DEBUG_DUMP_BUFF_SIZE, GFP_KERNEL); + if (!new_buff->buff) { + kfree(new_buff); + return NULL; + } + + INIT_LIST_HEAD(&new_buff->node); + list_add_tail(&new_buff->node, &dump_data->buff_list); + + return new_buff; +} + +static struct mlx5dr_dbg_dump_data * +mlx5dr_dbg_create_dump_data(void) +{ + struct mlx5dr_dbg_dump_data *dump_data; + + dump_data = kzalloc(sizeof(*dump_data), GFP_KERNEL); + if (!dump_data) + return NULL; + + INIT_LIST_HEAD(&dump_data->buff_list); + + if (!mlx5dr_dbg_dump_data_init_new_buff(dump_data)) { + kfree(dump_data); + return NULL; + } + + return dump_data; +} + +static void +mlx5dr_dbg_destroy_dump_data(struct mlx5dr_dbg_dump_data *dump_data) +{ + struct mlx5dr_dbg_dump_buff *dump_buff, *tmp_buff; + + if (!dump_data) + return; + + list_for_each_entry_safe(dump_buff, tmp_buff, &dump_data->buff_list, node) { + kvfree(dump_buff->buff); + list_del(&dump_buff->node); + kfree(dump_buff); + } + + kfree(dump_data); +} + +static int +mlx5dr_dbg_dump_data_print(struct seq_file *file, char *str, u32 size) +{ + struct mlx5dr_domain *dmn = file->private; + struct mlx5dr_dbg_dump_data *dump_data; + struct mlx5dr_dbg_dump_buff *buff; + u32 buff_capacity, write_size; + int remain_size, ret; + + if (size >= MLX5DR_DEBUG_DUMP_BUFF_SIZE) + return -EINVAL; + + dump_data = dmn->dump_info.dump_data; + buff = list_last_entry(&dump_data->buff_list, + struct mlx5dr_dbg_dump_buff, node); + + buff_capacity = (MLX5DR_DEBUG_DUMP_BUFF_SIZE - 1) - buff->index; + remain_size = buff_capacity - size; + write_size = (remain_size > 0) ? size : buff_capacity; + + if (likely(write_size)) { + ret = snprintf(buff->buff + buff->index, write_size + 1, "%s", str); + if (ret < 0) + return ret; + + buff->index += write_size; + } + + if (remain_size < 0) { + remain_size *= -1; + buff = mlx5dr_dbg_dump_data_init_new_buff(dump_data); + if (!buff) + return -ENOMEM; + + ret = snprintf(buff->buff, remain_size + 1, "%s", str + write_size); + if (ret < 0) + return ret; + + buff->index += remain_size; + } + + return 0; +} + void mlx5dr_dbg_tbl_add(struct mlx5dr_table *tbl) { mutex_lock(&tbl->dmn->dump_info.dbg_mutex); @@ -109,36 +210,68 @@ dr_dump_rule_action_mem(struct seq_file *file, const u64 rule_id, { struct mlx5dr_action *action = action_mem->action; const u64 action_id = DR_DBG_PTR_TO_ID(action); + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; u64 hit_tbl_ptr, miss_tbl_ptr; u32 hit_tbl_id, miss_tbl_id; + int ret; switch (action->action_type) { case DR_ACTION_TYP_DROP: - seq_printf(file, "%d,0x%llx,0x%llx\n", - DR_DUMP_REC_TYPE_ACTION_DROP, action_id, rule_id); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx\n", + DR_DUMP_REC_TYPE_ACTION_DROP, action_id, + rule_id); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_FT: if (action->dest_tbl->is_fw_tbl) - seq_printf(file, "%d,0x%llx,0x%llx,0x%x,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_FT, action_id, - rule_id, action->dest_tbl->fw_tbl.id, - -1); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_FT, action_id, + rule_id, action->dest_tbl->fw_tbl.id, + -1); else - seq_printf(file, "%d,0x%llx,0x%llx,0x%x,0x%llx\n", - DR_DUMP_REC_TYPE_ACTION_FT, action_id, - rule_id, action->dest_tbl->tbl->table_id, - DR_DBG_PTR_TO_ID(action->dest_tbl->tbl)); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,0x%llx\n", + DR_DUMP_REC_TYPE_ACTION_FT, action_id, + rule_id, action->dest_tbl->tbl->table_id, + DR_DBG_PTR_TO_ID(action->dest_tbl->tbl)); + + if (ret < 0) + return ret; + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_CTR: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_CTR, action_id, rule_id, - action->ctr->ctr_id + action->ctr->offset); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_CTR, action_id, rule_id, + action->ctr->ctr_id + action->ctr->offset); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_TAG: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_TAG, action_id, rule_id, - action->flow_tag->flow_tag); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_TAG, action_id, rule_id, + action->flow_tag->flow_tag); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_MODIFY_HDR: { @@ -150,83 +283,171 @@ dr_dump_rule_action_mem(struct seq_file *file, const u64 rule_id, ptrn_arg = !action->rewrite->single_action_opt && ptrn && arg; - seq_printf(file, "%d,0x%llx,0x%llx,0x%x,%d,0x%x,0x%x,0x%x", - DR_DUMP_REC_TYPE_ACTION_MODIFY_HDR, action_id, - rule_id, action->rewrite->index, - action->rewrite->single_action_opt, - ptrn_arg ? action->rewrite->num_of_actions : 0, - ptrn_arg ? ptrn->index : 0, - ptrn_arg ? mlx5dr_arg_get_obj_id(arg) : 0); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,%d,0x%x,0x%x,0x%x", + DR_DUMP_REC_TYPE_ACTION_MODIFY_HDR, action_id, + rule_id, action->rewrite->index, + action->rewrite->single_action_opt, + ptrn_arg ? action->rewrite->num_of_actions : 0, + ptrn_arg ? ptrn->index : 0, + ptrn_arg ? mlx5dr_arg_get_obj_id(arg) : 0); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; if (ptrn_arg) { for (i = 0; i < action->rewrite->num_of_actions; i++) { - seq_printf(file, ",0x%016llx", - be64_to_cpu(((__be64 *)rewrite_data)[i])); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + ",0x%016llx", + be64_to_cpu(((__be64 *)rewrite_data)[i])); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; } } - seq_puts(file, "\n"); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, "\n"); + if (ret < 0) + return ret; + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; } case DR_ACTION_TYP_VPORT: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_VPORT, action_id, rule_id, - action->vport->caps->num); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_VPORT, action_id, rule_id, + action->vport->caps->num); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_TNL_L2_TO_L2: - seq_printf(file, "%d,0x%llx,0x%llx\n", - DR_DUMP_REC_TYPE_ACTION_DECAP_L2, action_id, - rule_id); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx\n", + DR_DUMP_REC_TYPE_ACTION_DECAP_L2, action_id, + rule_id); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_TNL_L3_TO_L2: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_DECAP_L3, action_id, - rule_id, - (action->rewrite->ptrn && action->rewrite->arg) ? - mlx5dr_arg_get_obj_id(action->rewrite->arg) : - action->rewrite->index); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_DECAP_L3, action_id, + rule_id, + (action->rewrite->ptrn && action->rewrite->arg) ? + mlx5dr_arg_get_obj_id(action->rewrite->arg) : + action->rewrite->index); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_L2_TO_TNL_L2: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_ENCAP_L2, action_id, - rule_id, action->reformat->id); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_ENCAP_L2, action_id, + rule_id, action->reformat->id); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_L2_TO_TNL_L3: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_ENCAP_L3, action_id, - rule_id, action->reformat->id); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_ENCAP_L3, action_id, + rule_id, action->reformat->id); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_POP_VLAN: - seq_printf(file, "%d,0x%llx,0x%llx\n", - DR_DUMP_REC_TYPE_ACTION_POP_VLAN, action_id, - rule_id); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx\n", + DR_DUMP_REC_TYPE_ACTION_POP_VLAN, action_id, + rule_id); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_PUSH_VLAN: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_PUSH_VLAN, action_id, - rule_id, action->push_vlan->vlan_hdr); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_PUSH_VLAN, action_id, + rule_id, action->push_vlan->vlan_hdr); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_INSERT_HDR: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_INSERT_HDR, action_id, - rule_id, action->reformat->id, - action->reformat->param_0, - action->reformat->param_1); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_INSERT_HDR, action_id, + rule_id, action->reformat->id, + action->reformat->param_0, + action->reformat->param_1); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_REMOVE_HDR: - seq_printf(file, "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_REMOVE_HDR, action_id, - rule_id, action->reformat->id, - action->reformat->param_0, - action->reformat->param_1); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_REMOVE_HDR, action_id, + rule_id, action->reformat->id, + action->reformat->param_0, + action->reformat->param_1); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_SAMPLER: - seq_printf(file, - "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x,0x%llx,0x%llx\n", - DR_DUMP_REC_TYPE_ACTION_SAMPLER, action_id, rule_id, - 0, 0, action->sampler->sampler_id, - action->sampler->rx_icm_addr, - action->sampler->tx_icm_addr); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x,0x%llx,0x%llx\n", + DR_DUMP_REC_TYPE_ACTION_SAMPLER, action_id, + rule_id, 0, 0, action->sampler->sampler_id, + action->sampler->rx_icm_addr, + action->sampler->tx_icm_addr); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; case DR_ACTION_TYP_RANGE: if (action->range->hit_tbl_action->dest_tbl->is_fw_tbl) { @@ -247,10 +468,17 @@ dr_dump_rule_action_mem(struct seq_file *file, const u64 rule_id, DR_DBG_PTR_TO_ID(action->range->miss_tbl_action->dest_tbl->tbl); } - seq_printf(file, "%d,0x%llx,0x%llx,0x%x,0x%llx,0x%x,0x%llx,0x%x\n", - DR_DUMP_REC_TYPE_ACTION_MATCH_RANGE, action_id, rule_id, - hit_tbl_id, hit_tbl_ptr, miss_tbl_id, miss_tbl_ptr, - action->range->definer_id); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,0x%llx,0x%x,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_MATCH_RANGE, action_id, + rule_id, hit_tbl_id, hit_tbl_ptr, miss_tbl_id, + miss_tbl_ptr, action->range->definer_id); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; break; default: return 0; @@ -263,8 +491,10 @@ static int dr_dump_rule_mem(struct seq_file *file, struct mlx5dr_ste *ste, bool is_rx, const u64 rule_id, u8 format_ver) { + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; char hw_ste_dump[DR_HEX_SIZE]; u32 mem_rec_type; + int ret; if (format_ver == MLX5_STEERING_FORMAT_CONNECTX_5) { mem_rec_type = is_rx ? DR_DUMP_REC_TYPE_RULE_RX_ENTRY_V0 : @@ -277,9 +507,16 @@ dr_dump_rule_mem(struct seq_file *file, struct mlx5dr_ste *ste, dr_dump_hex_print(hw_ste_dump, (char *)mlx5dr_ste_get_hw_ste(ste), DR_STE_SIZE_REDUCED); - seq_printf(file, "%d,0x%llx,0x%llx,%s\n", mem_rec_type, - dr_dump_icm_to_idx(mlx5dr_ste_get_icm_addr(ste)), rule_id, - hw_ste_dump); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,%s\n", mem_rec_type, + dr_dump_icm_to_idx(mlx5dr_ste_get_icm_addr(ste)), + rule_id, hw_ste_dump); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; return 0; } @@ -309,6 +546,7 @@ static int dr_dump_rule(struct seq_file *file, struct mlx5dr_rule *rule) { struct mlx5dr_rule_action_member *action_mem; const u64 rule_id = DR_DBG_PTR_TO_ID(rule); + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; struct mlx5dr_rule_rx_tx *rx = &rule->rx; struct mlx5dr_rule_rx_tx *tx = &rule->tx; u8 format_ver; @@ -316,8 +554,15 @@ static int dr_dump_rule(struct seq_file *file, struct mlx5dr_rule *rule) format_ver = rule->matcher->tbl->dmn->info.caps.sw_format_ver; - seq_printf(file, "%d,0x%llx,0x%llx\n", DR_DUMP_REC_TYPE_RULE, rule_id, - DR_DBG_PTR_TO_ID(rule->matcher)); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx\n", DR_DUMP_REC_TYPE_RULE, + rule_id, DR_DBG_PTR_TO_ID(rule->matcher)); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; if (rx->nic_matcher) { ret = dr_dump_rule_rx_tx(file, rx, true, rule_id, format_ver); @@ -344,46 +589,94 @@ static int dr_dump_matcher_mask(struct seq_file *file, struct mlx5dr_match_param *mask, u8 criteria, const u64 matcher_id) { + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; char dump[DR_HEX_SIZE]; + int ret; - seq_printf(file, "%d,0x%llx,", DR_DUMP_REC_TYPE_MATCHER_MASK, - matcher_id); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, "%d,0x%llx,", + DR_DUMP_REC_TYPE_MATCHER_MASK, matcher_id); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; if (criteria & DR_MATCHER_CRITERIA_OUTER) { dr_dump_hex_print(dump, (char *)&mask->outer, sizeof(mask->outer)); - seq_printf(file, "%s,", dump); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%s,", dump); } else { - seq_puts(file, ","); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ","); } + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; + if (criteria & DR_MATCHER_CRITERIA_INNER) { dr_dump_hex_print(dump, (char *)&mask->inner, sizeof(mask->inner)); - seq_printf(file, "%s,", dump); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%s,", dump); } else { - seq_puts(file, ","); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ","); } + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; + if (criteria & DR_MATCHER_CRITERIA_MISC) { dr_dump_hex_print(dump, (char *)&mask->misc, sizeof(mask->misc)); - seq_printf(file, "%s,", dump); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%s,", dump); } else { - seq_puts(file, ","); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ","); } + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; + if (criteria & DR_MATCHER_CRITERIA_MISC2) { dr_dump_hex_print(dump, (char *)&mask->misc2, sizeof(mask->misc2)); - seq_printf(file, "%s,", dump); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%s,", dump); } else { - seq_puts(file, ","); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ","); } + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; + if (criteria & DR_MATCHER_CRITERIA_MISC3) { dr_dump_hex_print(dump, (char *)&mask->misc3, sizeof(mask->misc3)); - seq_printf(file, "%s\n", dump); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%s\n", dump); } else { - seq_puts(file, ",\n"); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ",\n"); } + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; + return 0; } @@ -391,9 +684,19 @@ static int dr_dump_matcher_builder(struct seq_file *file, struct mlx5dr_ste_build *builder, u32 index, bool is_rx, const u64 matcher_id) { - seq_printf(file, "%d,0x%llx,%d,%d,0x%x\n", - DR_DUMP_REC_TYPE_MATCHER_BUILDER, matcher_id, index, is_rx, - builder->lu_type); + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; + int ret; + + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,%d,%d,0x%x\n", + DR_DUMP_REC_TYPE_MATCHER_BUILDER, matcher_id, index, + is_rx, builder->lu_type); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; return 0; } @@ -403,6 +706,7 @@ dr_dump_matcher_rx_tx(struct seq_file *file, bool is_rx, struct mlx5dr_matcher_rx_tx *matcher_rx_tx, const u64 matcher_id) { + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; enum dr_dump_rec_type rec_type; u64 s_icm_addr, e_icm_addr; int i, ret; @@ -412,11 +716,19 @@ dr_dump_matcher_rx_tx(struct seq_file *file, bool is_rx, s_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(matcher_rx_tx->s_htbl->chunk); e_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(matcher_rx_tx->e_anchor->chunk); - seq_printf(file, "%d,0x%llx,0x%llx,%d,0x%llx,0x%llx\n", - rec_type, DR_DBG_PTR_TO_ID(matcher_rx_tx), - matcher_id, matcher_rx_tx->num_of_builders, - dr_dump_icm_to_idx(s_icm_addr), - dr_dump_icm_to_idx(e_icm_addr)); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,%d,0x%llx,0x%llx\n", + rec_type, DR_DBG_PTR_TO_ID(matcher_rx_tx), + matcher_id, matcher_rx_tx->num_of_builders, + dr_dump_icm_to_idx(s_icm_addr), + dr_dump_icm_to_idx(e_icm_addr)); + + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; for (i = 0; i < matcher_rx_tx->num_of_builders; i++) { ret = dr_dump_matcher_builder(file, @@ -434,13 +746,22 @@ dr_dump_matcher(struct seq_file *file, struct mlx5dr_matcher *matcher) { struct mlx5dr_matcher_rx_tx *rx = &matcher->rx; struct mlx5dr_matcher_rx_tx *tx = &matcher->tx; + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; u64 matcher_id; int ret; matcher_id = DR_DBG_PTR_TO_ID(matcher); - seq_printf(file, "%d,0x%llx,0x%llx,%d\n", DR_DUMP_REC_TYPE_MATCHER, - matcher_id, DR_DBG_PTR_TO_ID(matcher->tbl), matcher->prio); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,%d\n", DR_DUMP_REC_TYPE_MATCHER, + matcher_id, DR_DBG_PTR_TO_ID(matcher->tbl), + matcher->prio); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; ret = dr_dump_matcher_mask(file, &matcher->mask, matcher->match_criteria, matcher_id); @@ -486,15 +807,24 @@ dr_dump_table_rx_tx(struct seq_file *file, bool is_rx, struct mlx5dr_table_rx_tx *table_rx_tx, const u64 table_id) { + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; enum dr_dump_rec_type rec_type; u64 s_icm_addr; + int ret; rec_type = is_rx ? DR_DUMP_REC_TYPE_TABLE_RX : DR_DUMP_REC_TYPE_TABLE_TX; s_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(table_rx_tx->s_anchor->chunk); - seq_printf(file, "%d,0x%llx,0x%llx\n", rec_type, table_id, - dr_dump_icm_to_idx(s_icm_addr)); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx\n", rec_type, table_id, + dr_dump_icm_to_idx(s_icm_addr)); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; return 0; } @@ -503,11 +833,19 @@ static int dr_dump_table(struct seq_file *file, struct mlx5dr_table *table) { struct mlx5dr_table_rx_tx *rx = &table->rx; struct mlx5dr_table_rx_tx *tx = &table->tx; + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; int ret; - seq_printf(file, "%d,0x%llx,0x%llx,%d,%d\n", DR_DUMP_REC_TYPE_TABLE, - DR_DBG_PTR_TO_ID(table), DR_DBG_PTR_TO_ID(table->dmn), - table->table_type, table->level); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,%d,%d\n", DR_DUMP_REC_TYPE_TABLE, + DR_DBG_PTR_TO_ID(table), DR_DBG_PTR_TO_ID(table->dmn), + table->table_type, table->level); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; if (rx->nic_dmn) { ret = dr_dump_table_rx_tx(file, true, rx, @@ -546,46 +884,86 @@ static int dr_dump_send_ring(struct seq_file *file, struct mlx5dr_send_ring *ring, const u64 domain_id) { - seq_printf(file, "%d,0x%llx,0x%llx,0x%x,0x%x\n", - DR_DUMP_REC_TYPE_DOMAIN_SEND_RING, DR_DBG_PTR_TO_ID(ring), - domain_id, ring->cq->mcq.cqn, ring->qp->qpn); + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; + int ret; + + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%llx,0x%x,0x%x\n", + DR_DUMP_REC_TYPE_DOMAIN_SEND_RING, + DR_DBG_PTR_TO_ID(ring), domain_id, + ring->cq->mcq.cqn, ring->qp->qpn); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; + return 0; } -static int +static noinline_for_stack int dr_dump_domain_info_flex_parser(struct seq_file *file, const char *flex_parser_name, const u8 flex_parser_value, const u64 domain_id) { - seq_printf(file, "%d,0x%llx,%s,0x%x\n", - DR_DUMP_REC_TYPE_DOMAIN_INFO_FLEX_PARSER, domain_id, - flex_parser_name, flex_parser_value); + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; + int ret; + + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,%s,0x%x\n", + DR_DUMP_REC_TYPE_DOMAIN_INFO_FLEX_PARSER, domain_id, + flex_parser_name, flex_parser_value); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; + return 0; } -static int +static noinline_for_stack int dr_dump_domain_info_caps(struct seq_file *file, struct mlx5dr_cmd_caps *caps, const u64 domain_id) { + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; struct mlx5dr_cmd_vport_cap *vport_caps; unsigned long i, vports_num; + int ret; xa_for_each(&caps->vports.vports_caps_xa, vports_num, vport_caps) ; /* count the number of vports in xarray */ - seq_printf(file, "%d,0x%llx,0x%x,0x%llx,0x%llx,0x%x,%lu,%d\n", - DR_DUMP_REC_TYPE_DOMAIN_INFO_CAPS, domain_id, caps->gvmi, - caps->nic_rx_drop_address, caps->nic_tx_drop_address, - caps->flex_protocols, vports_num, caps->eswitch_manager); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,0x%x,0x%llx,0x%llx,0x%x,%lu,%d\n", + DR_DUMP_REC_TYPE_DOMAIN_INFO_CAPS, domain_id, caps->gvmi, + caps->nic_rx_drop_address, caps->nic_tx_drop_address, + caps->flex_protocols, vports_num, caps->eswitch_manager); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; xa_for_each(&caps->vports.vports_caps_xa, i, vport_caps) { vport_caps = xa_load(&caps->vports.vports_caps_xa, i); - seq_printf(file, "%d,0x%llx,%lu,0x%x,0x%llx,0x%llx\n", - DR_DUMP_REC_TYPE_DOMAIN_INFO_VPORT, domain_id, i, - vport_caps->vport_gvmi, vport_caps->icm_address_rx, - vport_caps->icm_address_tx); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,%lu,0x%x,0x%llx,0x%llx\n", + DR_DUMP_REC_TYPE_DOMAIN_INFO_VPORT, + domain_id, i, vport_caps->vport_gvmi, + vport_caps->icm_address_rx, + vport_caps->icm_address_tx); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; } return 0; } @@ -627,24 +1005,32 @@ dr_dump_domain_info(struct seq_file *file, struct mlx5dr_domain_info *info, return 0; } -static int +static noinline_for_stack int dr_dump_domain(struct seq_file *file, struct mlx5dr_domain *dmn) { + char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH]; u64 domain_id = DR_DBG_PTR_TO_ID(dmn); int ret; - seq_printf(file, "%d,0x%llx,%d,0%x,%d,%u.%u.%u,%s,%d,%u,%u,%u\n", - DR_DUMP_REC_TYPE_DOMAIN, - domain_id, dmn->type, dmn->info.caps.gvmi, - dmn->info.supp_sw_steering, - /* package version */ - LINUX_VERSION_MAJOR, LINUX_VERSION_PATCHLEVEL, - LINUX_VERSION_SUBLEVEL, - pci_name(dmn->mdev->pdev), - 0, /* domain flags */ - dmn->num_buddies[DR_ICM_TYPE_STE], - dmn->num_buddies[DR_ICM_TYPE_MODIFY_ACTION], - dmn->num_buddies[DR_ICM_TYPE_MODIFY_HDR_PTRN]); + ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, + "%d,0x%llx,%d,0%x,%d,%u.%u.%u,%s,%d,%u,%u,%u\n", + DR_DUMP_REC_TYPE_DOMAIN, + domain_id, dmn->type, dmn->info.caps.gvmi, + dmn->info.supp_sw_steering, + /* package version */ + LINUX_VERSION_MAJOR, LINUX_VERSION_PATCHLEVEL, + LINUX_VERSION_SUBLEVEL, + pci_name(dmn->mdev->pdev), + 0, /* domain flags */ + dmn->num_buddies[DR_ICM_TYPE_STE], + dmn->num_buddies[DR_ICM_TYPE_MODIFY_ACTION], + dmn->num_buddies[DR_ICM_TYPE_MODIFY_HDR_PTRN]); + if (ret < 0) + return ret; + + ret = mlx5dr_dbg_dump_data_print(file, buff, ret); + if (ret) + return ret; ret = dr_dump_domain_info(file, &dmn->info, domain_id); if (ret < 0) @@ -683,11 +1069,91 @@ unlock_mutex: return ret; } -static int dr_dump_show(struct seq_file *file, void *priv) +static void * +dr_dump_start(struct seq_file *file, loff_t *pos) { - return dr_dump_domain_all(file, file->private); + struct mlx5dr_domain *dmn = file->private; + struct mlx5dr_dbg_dump_data *dump_data; + + if (atomic_read(&dmn->dump_info.state) != MLX5DR_DEBUG_DUMP_STATE_FREE) { + mlx5_core_warn(dmn->mdev, "Dump already in progress\n"); + return ERR_PTR(-EBUSY); + } + + atomic_set(&dmn->dump_info.state, MLX5DR_DEBUG_DUMP_STATE_IN_PROGRESS); + dump_data = dmn->dump_info.dump_data; + + if (dump_data) { + return seq_list_start(&dump_data->buff_list, *pos); + } else if (*pos == 0) { + dump_data = mlx5dr_dbg_create_dump_data(); + if (!dump_data) + goto exit; + + dmn->dump_info.dump_data = dump_data; + if (dr_dump_domain_all(file, dmn)) { + mlx5dr_dbg_destroy_dump_data(dump_data); + dmn->dump_info.dump_data = NULL; + goto exit; + } + + return seq_list_start(&dump_data->buff_list, *pos); + } + +exit: + atomic_set(&dmn->dump_info.state, MLX5DR_DEBUG_DUMP_STATE_FREE); + return NULL; } -DEFINE_SHOW_ATTRIBUTE(dr_dump); + +static void * +dr_dump_next(struct seq_file *file, void *v, loff_t *pos) +{ + struct mlx5dr_domain *dmn = file->private; + struct mlx5dr_dbg_dump_data *dump_data; + + dump_data = dmn->dump_info.dump_data; + + return seq_list_next(v, &dump_data->buff_list, pos); +} + +static void +dr_dump_stop(struct seq_file *file, void *v) +{ + struct mlx5dr_domain *dmn = file->private; + struct mlx5dr_dbg_dump_data *dump_data; + + if (v && IS_ERR(v)) + return; + + if (!v) { + dump_data = dmn->dump_info.dump_data; + if (dump_data) { + mlx5dr_dbg_destroy_dump_data(dump_data); + dmn->dump_info.dump_data = NULL; + } + } + + atomic_set(&dmn->dump_info.state, MLX5DR_DEBUG_DUMP_STATE_FREE); +} + +static int +dr_dump_show(struct seq_file *file, void *v) +{ + struct mlx5dr_dbg_dump_buff *entry; + + entry = list_entry(v, struct mlx5dr_dbg_dump_buff, node); + seq_printf(file, "%s", entry->buff); + + return 0; +} + +static const struct seq_operations dr_dump_sops = { + .start = dr_dump_start, + .next = dr_dump_next, + .stop = dr_dump_stop, + .show = dr_dump_show, +}; +DEFINE_SEQ_ATTRIBUTE(dr_dump); void mlx5dr_dbg_init_dump(struct mlx5dr_domain *dmn) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.h index def6cf853eea..57c6b363b870 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.h @@ -1,10 +1,30 @@ /* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ /* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ +#define MLX5DR_DEBUG_DUMP_BUFF_SIZE (64 * 1024 * 1024) +#define MLX5DR_DEBUG_DUMP_BUFF_LENGTH 512 + +enum { + MLX5DR_DEBUG_DUMP_STATE_FREE, + MLX5DR_DEBUG_DUMP_STATE_IN_PROGRESS, +}; + +struct mlx5dr_dbg_dump_buff { + char *buff; + u32 index; + struct list_head node; +}; + +struct mlx5dr_dbg_dump_data { + struct list_head buff_list; +}; + struct mlx5dr_dbg_dump_info { struct mutex dbg_mutex; /* protect dbg lists */ struct dentry *steering_debugfs; struct dentry *fdb_debugfs; + struct mlx5dr_dbg_dump_data *dump_data; + atomic_t state; }; void mlx5dr_dbg_init_dump(struct mlx5dr_domain *dmn); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c index faa63ea9b83e..1915fa41c622 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c @@ -95,7 +95,7 @@ struct mlxsw_afa_set { */ has_trap:1, has_police:1; - unsigned int ref_count; + refcount_t ref_count; struct mlxsw_afa_set *next; /* Pointer to the next set. */ struct mlxsw_afa_set *prev; /* Pointer to the previous set, * note that set may have multiple @@ -120,7 +120,7 @@ struct mlxsw_afa_fwd_entry { struct rhash_head ht_node; struct mlxsw_afa_fwd_entry_ht_key ht_key; u32 kvdl_index; - unsigned int ref_count; + refcount_t ref_count; }; static const struct rhashtable_params mlxsw_afa_fwd_entry_ht_params = { @@ -282,7 +282,7 @@ static struct mlxsw_afa_set *mlxsw_afa_set_create(bool is_first) /* Need to initialize the set to pass by default */ mlxsw_afa_set_goto_set(set, MLXSW_AFA_SET_GOTO_BINDING_CMD_TERM, 0); set->ht_key.is_first = is_first; - set->ref_count = 1; + refcount_set(&set->ref_count, 1); return set; } @@ -330,7 +330,7 @@ static void mlxsw_afa_set_unshare(struct mlxsw_afa *mlxsw_afa, static void mlxsw_afa_set_put(struct mlxsw_afa *mlxsw_afa, struct mlxsw_afa_set *set) { - if (--set->ref_count) + if (!refcount_dec_and_test(&set->ref_count)) return; if (set->shared) mlxsw_afa_set_unshare(mlxsw_afa, set); @@ -350,7 +350,7 @@ static struct mlxsw_afa_set *mlxsw_afa_set_get(struct mlxsw_afa *mlxsw_afa, set = rhashtable_lookup_fast(&mlxsw_afa->set_ht, &orig_set->ht_key, mlxsw_afa_set_ht_params); if (set) { - set->ref_count++; + refcount_inc(&set->ref_count); mlxsw_afa_set_put(mlxsw_afa, orig_set); } else { set = orig_set; @@ -564,7 +564,7 @@ mlxsw_afa_fwd_entry_create(struct mlxsw_afa *mlxsw_afa, u16 local_port) if (!fwd_entry) return ERR_PTR(-ENOMEM); fwd_entry->ht_key.local_port = local_port; - fwd_entry->ref_count = 1; + refcount_set(&fwd_entry->ref_count, 1); err = rhashtable_insert_fast(&mlxsw_afa->fwd_entry_ht, &fwd_entry->ht_node, @@ -607,7 +607,7 @@ mlxsw_afa_fwd_entry_get(struct mlxsw_afa *mlxsw_afa, u16 local_port) fwd_entry = rhashtable_lookup_fast(&mlxsw_afa->fwd_entry_ht, &ht_key, mlxsw_afa_fwd_entry_ht_params); if (fwd_entry) { - fwd_entry->ref_count++; + refcount_inc(&fwd_entry->ref_count); return fwd_entry; } return mlxsw_afa_fwd_entry_create(mlxsw_afa, local_port); @@ -616,7 +616,7 @@ mlxsw_afa_fwd_entry_get(struct mlxsw_afa *mlxsw_afa, u16 local_port) static void mlxsw_afa_fwd_entry_put(struct mlxsw_afa *mlxsw_afa, struct mlxsw_afa_fwd_entry *fwd_entry) { - if (--fwd_entry->ref_count) + if (!refcount_dec_and_test(&fwd_entry->ref_count)) return; mlxsw_afa_fwd_entry_destroy(mlxsw_afa, fwd_entry); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.c index 0d5e6f9b466e..947500f8ed71 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.c @@ -5,6 +5,7 @@ #include <linux/slab.h> #include <linux/list.h> #include <linux/errno.h> +#include <linux/refcount.h> #include "item.h" #include "core_acl_flex_keys.h" @@ -107,7 +108,7 @@ EXPORT_SYMBOL(mlxsw_afk_destroy); struct mlxsw_afk_key_info { struct list_head list; - unsigned int ref_count; + refcount_t ref_count; unsigned int blocks_count; int element_to_block[MLXSW_AFK_ELEMENT_MAX]; /* index is element, value * is index inside "blocks" @@ -334,7 +335,7 @@ mlxsw_afk_key_info_create(struct mlxsw_afk *mlxsw_afk, if (err) goto err_picker; list_add(&key_info->list, &mlxsw_afk->key_info_list); - key_info->ref_count = 1; + refcount_set(&key_info->ref_count, 1); return key_info; err_picker: @@ -356,7 +357,7 @@ mlxsw_afk_key_info_get(struct mlxsw_afk *mlxsw_afk, key_info = mlxsw_afk_key_info_find(mlxsw_afk, elusage); if (key_info) { - key_info->ref_count++; + refcount_inc(&key_info->ref_count); return key_info; } return mlxsw_afk_key_info_create(mlxsw_afk, elusage); @@ -365,7 +366,7 @@ EXPORT_SYMBOL(mlxsw_afk_key_info_get); void mlxsw_afk_key_info_put(struct mlxsw_afk_key_info *key_info) { - if (--key_info->ref_count) + if (!refcount_dec_and_test(&key_info->ref_count)) return; mlxsw_afk_key_info_destroy(key_info); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 6b98c3287b49..f0ceb196a6ce 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -708,7 +708,6 @@ static const struct i2c_device_id mlxsw_m_i2c_id[] = { static struct i2c_driver mlxsw_m_i2c_driver = { .driver.name = "mlxsw_minimal", - .class = I2C_CLASS_HWMON, .id_table = mlxsw_m_i2c_id, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 5d3413636a62..ecde2086c703 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2695,23 +2695,18 @@ static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp) static int mlxsw_sp_lag_pgt_init(struct mlxsw_sp *mlxsw_sp) { char sgcr_pl[MLXSW_REG_SGCR_LEN]; - u16 max_lag; int err; if (mlxsw_core_lag_mode(mlxsw_sp->core) != MLXSW_CMD_MBOX_CONFIG_PROFILE_LAG_MODE_SW) return 0; - err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag); - if (err) - return err; - /* In DDD mode, which we by default use, each LAG entry is 8 PGT * entries. The LAG table address needs to be 8-aligned, but that ought * to be the case, since the LAG table is allocated first. */ err = mlxsw_sp_pgt_mid_alloc_range(mlxsw_sp, &mlxsw_sp->lag_pgt_base, - max_lag * 8); + mlxsw_sp->max_lag * 8); if (err) return err; if (WARN_ON_ONCE(mlxsw_sp->lag_pgt_base % 8)) { @@ -2728,33 +2723,31 @@ static int mlxsw_sp_lag_pgt_init(struct mlxsw_sp *mlxsw_sp) err_mid_alloc_range: mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mlxsw_sp->lag_pgt_base, - max_lag * 8); + mlxsw_sp->max_lag * 8); return err; } static void mlxsw_sp_lag_pgt_fini(struct mlxsw_sp *mlxsw_sp) { - u16 max_lag; - int err; - if (mlxsw_core_lag_mode(mlxsw_sp->core) != MLXSW_CMD_MBOX_CONFIG_PROFILE_LAG_MODE_SW) return; - err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag); - if (err) - return; - mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mlxsw_sp->lag_pgt_base, - max_lag * 8); + mlxsw_sp->max_lag * 8); } #define MLXSW_SP_LAG_SEED_INIT 0xcafecafe +struct mlxsw_sp_lag { + struct net_device *dev; + refcount_t ref_count; + u16 lag_id; +}; + static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) { char slcr_pl[MLXSW_REG_SLCR_LEN]; - u16 max_lag; u32 seed; int err; @@ -2773,7 +2766,7 @@ static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) if (err) return err; - err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag); + err = mlxsw_core_max_lag(mlxsw_sp->core, &mlxsw_sp->max_lag); if (err) return err; @@ -2784,7 +2777,7 @@ static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) if (err) return err; - mlxsw_sp->lags = kcalloc(max_lag, sizeof(struct mlxsw_sp_upper), + mlxsw_sp->lags = kcalloc(mlxsw_sp->max_lag, sizeof(struct mlxsw_sp_lag), GFP_KERNEL); if (!mlxsw_sp->lags) { err = -ENOMEM; @@ -4269,19 +4262,48 @@ mlxsw_sp_port_lag_uppers_cleanup(struct mlxsw_sp_port *mlxsw_sp_port, } } -static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id) +static struct mlxsw_sp_lag * +mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, struct net_device *lag_dev, + struct netlink_ext_ack *extack) { char sldr_pl[MLXSW_REG_SLDR_LEN]; + struct mlxsw_sp_lag *lag; + u16 lag_id; + int i, err; + + for (i = 0; i < mlxsw_sp->max_lag; i++) { + if (!mlxsw_sp->lags[i].dev) + break; + } + + if (i == mlxsw_sp->max_lag) { + NL_SET_ERR_MSG_MOD(extack, + "Exceeded number of supported LAG devices"); + return ERR_PTR(-EBUSY); + } + lag_id = i; mlxsw_reg_sldr_lag_create_pack(sldr_pl, lag_id); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl); + if (err) + return ERR_PTR(err); + + lag = &mlxsw_sp->lags[lag_id]; + lag->lag_id = lag_id; + lag->dev = lag_dev; + refcount_set(&lag->ref_count, 1); + + return lag; } -static int mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, u16 lag_id) +static int +mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_lag *lag) { char sldr_pl[MLXSW_REG_SLDR_LEN]; - mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag_id); + lag->dev = NULL; + + mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag->lag_id); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl); } @@ -4329,34 +4351,44 @@ static int mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl); } -static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp, - struct net_device *lag_dev, - u16 *p_lag_id) +static struct mlxsw_sp_lag * +mlxsw_sp_lag_find(struct mlxsw_sp *mlxsw_sp, struct net_device *lag_dev) { - struct mlxsw_sp_upper *lag; - int free_lag_id = -1; - u16 max_lag; - int err, i; + int i; - err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag); - if (err) - return err; + for (i = 0; i < mlxsw_sp->max_lag; i++) { + if (!mlxsw_sp->lags[i].dev) + continue; - for (i = 0; i < max_lag; i++) { - lag = mlxsw_sp_lag_get(mlxsw_sp, i); - if (lag->ref_count) { - if (lag->dev == lag_dev) { - *p_lag_id = i; - return 0; - } - } else if (free_lag_id < 0) { - free_lag_id = i; - } + if (mlxsw_sp->lags[i].dev == lag_dev) + return &mlxsw_sp->lags[i]; } - if (free_lag_id < 0) - return -EBUSY; - *p_lag_id = free_lag_id; - return 0; + + return NULL; +} + +static struct mlxsw_sp_lag * +mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, struct net_device *lag_dev, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_lag *lag; + + lag = mlxsw_sp_lag_find(mlxsw_sp, lag_dev); + if (lag) { + refcount_inc(&lag->ref_count); + return lag; + } + + return mlxsw_sp_lag_create(mlxsw_sp, lag_dev, extack); +} + +static void +mlxsw_sp_lag_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_lag *lag) +{ + if (!refcount_dec_and_test(&lag->ref_count)) + return; + + mlxsw_sp_lag_destroy(mlxsw_sp, lag); } static bool @@ -4365,12 +4397,6 @@ mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp, struct netdev_lag_upper_info *lag_upper_info, struct netlink_ext_ack *extack) { - u16 lag_id; - - if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0) { - NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported LAG devices"); - return false; - } if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type"); return false; @@ -4482,22 +4508,16 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, struct netlink_ext_ack *extack) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - struct mlxsw_sp_upper *lag; + struct mlxsw_sp_lag *lag; u16 lag_id; u8 port_index; int err; - err = mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id); - if (err) - return err; - lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id); - if (!lag->ref_count) { - err = mlxsw_sp_lag_create(mlxsw_sp, lag_id); - if (err) - return err; - lag->dev = lag_dev; - } + lag = mlxsw_sp_lag_get(mlxsw_sp, lag_dev, extack); + if (IS_ERR(lag)) + return PTR_ERR(lag); + lag_id = lag->lag_id; err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index); if (err) return err; @@ -4515,7 +4535,6 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_port->local_port); mlxsw_sp_port->lag_id = lag_id; mlxsw_sp_port->lagged = 1; - lag->ref_count++; err = mlxsw_sp_fid_port_join_lag(mlxsw_sp_port); if (err) @@ -4542,7 +4561,6 @@ err_replay: err_router_join: mlxsw_sp_fid_port_leave_lag(mlxsw_sp_port); err_fid_port_join_lag: - lag->ref_count--; mlxsw_sp_port->lagged = 0; mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id, mlxsw_sp_port->local_port); @@ -4550,8 +4568,7 @@ err_fid_port_join_lag: err_col_port_add: mlxsw_sp_lag_uppers_bridge_leave(mlxsw_sp_port, lag_dev); err_lag_uppers_bridge_join: - if (!lag->ref_count) - mlxsw_sp_lag_destroy(mlxsw_sp, lag_id); + mlxsw_sp_lag_put(mlxsw_sp, lag); return err; } @@ -4560,12 +4577,11 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port, { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; u16 lag_id = mlxsw_sp_port->lag_id; - struct mlxsw_sp_upper *lag; + struct mlxsw_sp_lag *lag; if (!mlxsw_sp_port->lagged) return; - lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id); - WARN_ON(lag->ref_count == 0); + lag = &mlxsw_sp->lags[lag_id]; mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id); @@ -4579,13 +4595,11 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_fid_port_leave_lag(mlxsw_sp_port); - if (lag->ref_count == 1) - mlxsw_sp_lag_destroy(mlxsw_sp, lag_id); + mlxsw_sp_lag_put(mlxsw_sp, lag); mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id, mlxsw_sp_port->local_port); mlxsw_sp_port->lagged = 0; - lag->ref_count--; /* Make sure untagged frames are allowed to ingress */ mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index a0c9775fa955..898d24232935 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -78,11 +78,6 @@ struct mlxsw_sp_span_entry; enum mlxsw_sp_l3proto; union mlxsw_sp_l3addr; -struct mlxsw_sp_upper { - struct net_device *dev; - unsigned int ref_count; -}; - enum mlxsw_sp_rif_type { MLXSW_SP_RIF_TYPE_SUBPORT, MLXSW_SP_RIF_TYPE_VLAN, @@ -136,6 +131,7 @@ struct mlxsw_sp_span_ops; struct mlxsw_sp_qdisc_state; struct mlxsw_sp_mall_entry; struct mlxsw_sp_pgt; +struct mlxsw_sp_lag; struct mlxsw_sp_port_mapping { u8 module; @@ -164,7 +160,8 @@ struct mlxsw_sp { const struct mlxsw_bus_info *bus_info; unsigned char base_mac[ETH_ALEN]; const unsigned char *mac_mask; - struct mlxsw_sp_upper *lags; + struct mlxsw_sp_lag *lags; + u16 max_lag; struct mlxsw_sp_port_mapping *port_mapping; struct mlxsw_sp_port_mapping_events port_mapping_events; struct rhashtable sample_trigger_ht; @@ -257,12 +254,6 @@ struct mlxsw_sp_fid_core_ops { void (*fini)(struct mlxsw_sp *mlxsw_sp); }; -static inline struct mlxsw_sp_upper * -mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id) -{ - return &mlxsw_sp->lags[lag_id]; -} - struct mlxsw_sp_port_pcpu_stats { u64 rx_packets; u64 rx_bytes; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 7c59c8a13584..b01b000bc71c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -9,6 +9,7 @@ #include <linux/rhashtable.h> #include <linux/netdevice.h> #include <linux/mutex.h> +#include <linux/refcount.h> #include <net/net_namespace.h> #include <net/tc_act/tc_vlan.h> @@ -55,7 +56,7 @@ struct mlxsw_sp_acl_ruleset { struct rhash_head ht_node; /* Member of acl HT */ struct mlxsw_sp_acl_ruleset_ht_key ht_key; struct rhashtable rule_ht; - unsigned int ref_count; + refcount_t ref_count; unsigned int min_prio; unsigned int max_prio; unsigned long priv[]; @@ -99,7 +100,7 @@ static bool mlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset) { /* We hold a reference on ruleset ourselves */ - return ruleset->ref_count == 2; + return refcount_read(&ruleset->ref_count) == 2; } int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, @@ -176,7 +177,7 @@ mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp, ruleset = kzalloc(alloc_size, GFP_KERNEL); if (!ruleset) return ERR_PTR(-ENOMEM); - ruleset->ref_count = 1; + refcount_set(&ruleset->ref_count, 1); ruleset->ht_key.block = block; ruleset->ht_key.chain_index = chain_index; ruleset->ht_key.ops = ops; @@ -222,13 +223,13 @@ static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_acl_ruleset_ref_inc(struct mlxsw_sp_acl_ruleset *ruleset) { - ruleset->ref_count++; + refcount_inc(&ruleset->ref_count); } static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset) { - if (--ruleset->ref_count) + if (!refcount_dec_and_test(&ruleset->ref_count)) return; mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index 50ea1eff02b2..f20052776b3f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -9,6 +9,7 @@ #include <linux/rhashtable.h> #include <linux/netdevice.h> #include <linux/mutex.h> +#include <linux/refcount.h> #include <net/devlink.h> #include <trace/events/mlxsw.h> @@ -155,7 +156,7 @@ struct mlxsw_sp_acl_tcam_vregion { struct mlxsw_sp_acl_tcam_rehash_ctx ctx; } rehash; struct mlxsw_sp *mlxsw_sp; - unsigned int ref_count; + refcount_t ref_count; }; struct mlxsw_sp_acl_tcam_vchunk; @@ -176,7 +177,7 @@ struct mlxsw_sp_acl_tcam_vchunk { unsigned int priority; /* Priority within the vregion and group */ struct mlxsw_sp_acl_tcam_vgroup *vgroup; struct mlxsw_sp_acl_tcam_vregion *vregion; - unsigned int ref_count; + refcount_t ref_count; }; struct mlxsw_sp_acl_tcam_entry { @@ -769,7 +770,7 @@ mlxsw_sp_acl_tcam_vregion_create(struct mlxsw_sp *mlxsw_sp, vregion->tcam = tcam; vregion->mlxsw_sp = mlxsw_sp; vregion->vgroup = vgroup; - vregion->ref_count = 1; + refcount_set(&vregion->ref_count, 1); vregion->key_info = mlxsw_afk_key_info_get(afk, elusage); if (IS_ERR(vregion->key_info)) { @@ -856,7 +857,7 @@ mlxsw_sp_acl_tcam_vregion_get(struct mlxsw_sp *mlxsw_sp, */ return ERR_PTR(-EOPNOTSUPP); } - vregion->ref_count++; + refcount_inc(&vregion->ref_count); return vregion; } @@ -871,7 +872,7 @@ static void mlxsw_sp_acl_tcam_vregion_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_tcam_vregion *vregion) { - if (--vregion->ref_count) + if (!refcount_dec_and_test(&vregion->ref_count)) return; mlxsw_sp_acl_tcam_vregion_destroy(mlxsw_sp, vregion); } @@ -924,7 +925,7 @@ mlxsw_sp_acl_tcam_vchunk_create(struct mlxsw_sp *mlxsw_sp, INIT_LIST_HEAD(&vchunk->ventry_list); vchunk->priority = priority; vchunk->vgroup = vgroup; - vchunk->ref_count = 1; + refcount_set(&vchunk->ref_count, 1); vregion = mlxsw_sp_acl_tcam_vregion_get(mlxsw_sp, vgroup, priority, elusage); @@ -1008,7 +1009,7 @@ mlxsw_sp_acl_tcam_vchunk_get(struct mlxsw_sp *mlxsw_sp, if (WARN_ON(!mlxsw_afk_key_info_subset(vchunk->vregion->key_info, elusage))) return ERR_PTR(-EINVAL); - vchunk->ref_count++; + refcount_inc(&vchunk->ref_count); return vchunk; } return mlxsw_sp_acl_tcam_vchunk_create(mlxsw_sp, vgroup, @@ -1019,7 +1020,7 @@ static void mlxsw_sp_acl_tcam_vchunk_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_tcam_vchunk *vchunk) { - if (--vchunk->ref_count) + if (!refcount_dec_and_test(&vchunk->ref_count)) return; mlxsw_sp_acl_tcam_vchunk_destroy(mlxsw_sp, vchunk); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 7164f9e6370f..87617df694ab 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -501,7 +501,7 @@ struct mlxsw_sp_rt6 { struct mlxsw_sp_lpm_tree { u8 id; /* tree ID */ - unsigned int ref_count; + refcount_t ref_count; enum mlxsw_sp_l3proto proto; unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT]; struct mlxsw_sp_prefix_usage prefix_usage; @@ -578,7 +578,7 @@ mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp) for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) { lpm_tree = &mlxsw_sp->router->lpm.trees[i]; - if (lpm_tree->ref_count == 0) + if (refcount_read(&lpm_tree->ref_count) == 0) return lpm_tree; } return NULL; @@ -654,7 +654,7 @@ mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp, sizeof(lpm_tree->prefix_usage)); memset(&lpm_tree->prefix_ref_count, 0, sizeof(lpm_tree->prefix_ref_count)); - lpm_tree->ref_count = 1; + refcount_set(&lpm_tree->ref_count, 1); return lpm_tree; err_left_struct_set: @@ -678,7 +678,7 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) { lpm_tree = &mlxsw_sp->router->lpm.trees[i]; - if (lpm_tree->ref_count != 0 && + if (refcount_read(&lpm_tree->ref_count) && lpm_tree->proto == proto && mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage, prefix_usage)) { @@ -691,14 +691,15 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree) { - lpm_tree->ref_count++; + refcount_inc(&lpm_tree->ref_count); } static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_lpm_tree *lpm_tree) { - if (--lpm_tree->ref_count == 0) - mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree); + if (!refcount_dec_and_test(&lpm_tree->ref_count)) + return; + mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree); } #define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 6c749c148148..6397ff0dc951 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -61,7 +61,7 @@ struct mlxsw_sp_bridge_port { struct mlxsw_sp_bridge_device *bridge_device; struct list_head list; struct list_head vlans_list; - unsigned int ref_count; + refcount_t ref_count; u8 stp_state; unsigned long flags; bool mrouter; @@ -495,7 +495,7 @@ mlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device, BR_MCAST_FLOOD; INIT_LIST_HEAD(&bridge_port->vlans_list); list_add(&bridge_port->list, &bridge_device->ports_list); - bridge_port->ref_count = 1; + refcount_set(&bridge_port->ref_count, 1); err = switchdev_bridge_port_offload(brport_dev, mlxsw_sp_port->dev, NULL, NULL, NULL, false, extack); @@ -531,7 +531,7 @@ mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge, bridge_port = mlxsw_sp_bridge_port_find(bridge, brport_dev); if (bridge_port) { - bridge_port->ref_count++; + refcount_inc(&bridge_port->ref_count); return bridge_port; } @@ -558,7 +558,7 @@ static void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge, { struct mlxsw_sp_bridge_device *bridge_device; - if (--bridge_port->ref_count != 0) + if (!refcount_dec_and_test(&bridge_port->ref_count)) return; bridge_device = bridge_port->bridge_device; mlxsw_sp_bridge_port_destroy(bridge_port); diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c index 5693784eec5b..443128adbcb6 100644 --- a/drivers/net/ethernet/microchip/encx24j600-regmap.c +++ b/drivers/net/ethernet/microchip/encx24j600-regmap.c @@ -464,7 +464,7 @@ static struct regmap_config regcfg = { .val_bits = 16, .max_register = 0xee, .reg_stride = 2, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .val_format_endian = REGMAP_ENDIAN_LITTLE, .readable_reg = encx24j600_regmap_readable, .writeable_reg = encx24j600_regmap_writeable, @@ -485,7 +485,7 @@ static struct regmap_config phycfg = { .reg_bits = 8, .val_bits = 16, .max_register = 0x1f, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .val_format_endian = REGMAP_ENDIAN_LITTLE, .readable_reg = encx24j600_phymap_readable, .writeable_reg = encx24j600_phymap_writeable, @@ -513,4 +513,5 @@ int devm_regmap_init_encx24j600(struct device *dev, } EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600); +MODULE_DESCRIPTION("Microchip ENCX24J600 helpers"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index a2b3f4433ca8..8a6ae171e375 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -1055,7 +1055,7 @@ static int lan743x_ethtool_get_ts_info(struct net_device *netdev, } static int lan743x_ethtool_get_eee(struct net_device *netdev, - struct ethtool_eee *eee) + struct ethtool_keee *eee) { struct lan743x_adapter *adapter = netdev_priv(netdev); struct phy_device *phydev = netdev->phydev; @@ -1092,7 +1092,7 @@ static int lan743x_ethtool_get_eee(struct net_device *netdev, } static int lan743x_ethtool_set_eee(struct net_device *netdev, - struct ethtool_eee *eee) + struct ethtool_keee *eee) { struct lan743x_adapter *adapter; struct phy_device *phydev; diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c index 2f04bc77a118..2801f08bf1c9 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.c +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -1712,13 +1712,13 @@ bool lan743x_ptp_request_tx_timestamp(struct lan743x_adapter *adapter) struct lan743x_ptp *ptp = &adapter->ptp; bool result = false; - spin_lock_bh(&ptp->tx_ts_lock); + spin_lock(&ptp->tx_ts_lock); if (ptp->pending_tx_timestamps < LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS) { /* request granted */ ptp->pending_tx_timestamps++; result = true; } - spin_unlock_bh(&ptp->tx_ts_lock); + spin_unlock(&ptp->tx_ts_lock); return result; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_lag.c b/drivers/net/ethernet/microchip/lan966x/lan966x_lag.c index 41fa2523d91d..5f2cd9a8cf8f 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_lag.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_lag.c @@ -37,19 +37,24 @@ static void lan966x_lag_set_aggr_pgids(struct lan966x *lan966x) /* Now, set PGIDs for each active LAG */ for (lag = 0; lag < lan966x->num_phys_ports; ++lag) { - struct net_device *bond = lan966x->ports[lag]->bond; + struct lan966x_port *port = lan966x->ports[lag]; int num_active_ports = 0; + struct net_device *bond; unsigned long bond_mask; u8 aggr_idx[16]; - if (!bond || (visited & BIT(lag))) + if (!port || !port->bond || (visited & BIT(lag))) continue; + bond = port->bond; bond_mask = lan966x_lag_get_mask(lan966x, bond); for_each_set_bit(p, &bond_mask, lan966x->num_phys_ports) { struct lan966x_port *port = lan966x->ports[p]; + if (!port) + continue; + lan_wr(ANA_PGID_PGID_SET(bond_mask), lan966x, ANA_PGID(p)); if (port->lag_tx_active) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c index 92108d354051..2e83bbb9477e 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c @@ -168,9 +168,10 @@ static void lan966x_port_link_up(struct lan966x_port *port) lan966x_taprio_speed_set(port, config->speed); /* Also the GIGA_MODE_ENA(1) needs to be set regardless of the - * port speed for QSGMII ports. + * port speed for QSGMII or SGMII ports. */ - if (phy_interface_num_ports(config->portmode) == 4) + if (phy_interface_num_ports(config->portmode) == 4 || + config->portmode == PHY_INTERFACE_MODE_SGMII) mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1); lan_wr(config->duplex | mode, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c index ac525ff1503e..3a01e13bd10b 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c @@ -25,6 +25,8 @@ static void lan966x_vcap_is1_port_keys(struct lan966x_port *port, for (int l = 0; l < admin->lookups; ++l) { out->prf(out->dst, "\n Lookup %d: ", l); + val = lan_rd(lan966x, ANA_VCAP_S1_CFG(port->chip_port, l)); + out->prf(out->dst, "\n other: "); switch (ANA_VCAP_S1_CFG_KEY_OTHER_CFG_GET(val)) { case VCAP_IS1_PS_OTHER_NORMAL: diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index d33b27214539..1332db9a08eb 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -1249,15 +1249,47 @@ void mana_gd_free_res_map(struct gdma_resource *r) r->size = 0; } +static int irq_setup(unsigned int *irqs, unsigned int len, int node) +{ + const struct cpumask *next, *prev = cpu_none_mask; + cpumask_var_t cpus __free(free_cpumask_var); + int cpu, weight; + + if (!alloc_cpumask_var(&cpus, GFP_KERNEL)) + return -ENOMEM; + + rcu_read_lock(); + for_each_numa_hop_mask(next, node) { + weight = cpumask_weight_andnot(next, prev); + while (weight > 0) { + cpumask_andnot(cpus, next, prev); + for_each_cpu(cpu, cpus) { + if (len-- == 0) + goto done; + irq_set_affinity_and_hint(*irqs++, topology_sibling_cpumask(cpu)); + cpumask_andnot(cpus, cpus, topology_sibling_cpumask(cpu)); + --weight; + } + } + prev = next; + } +done: + rcu_read_unlock(); + return 0; +} + static int mana_gd_setup_irqs(struct pci_dev *pdev) { - unsigned int max_queues_per_port = num_online_cpus(); struct gdma_context *gc = pci_get_drvdata(pdev); + unsigned int max_queues_per_port; struct gdma_irq_context *gic; unsigned int max_irqs, cpu; - int nvec, irq; + int start_irq_index = 1; + int nvec, *irqs, irq; int err, i = 0, j; + cpus_read_lock(); + max_queues_per_port = num_online_cpus(); if (max_queues_per_port > MANA_MAX_NUM_QUEUES) max_queues_per_port = MANA_MAX_NUM_QUEUES; @@ -1265,8 +1297,18 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev) max_irqs = max_queues_per_port + 1; nvec = pci_alloc_irq_vectors(pdev, 2, max_irqs, PCI_IRQ_MSIX); - if (nvec < 0) + if (nvec < 0) { + cpus_read_unlock(); return nvec; + } + if (nvec <= num_online_cpus()) + start_irq_index = 0; + + irqs = kmalloc_array((nvec - start_irq_index), sizeof(int), GFP_KERNEL); + if (!irqs) { + err = -ENOMEM; + goto free_irq_vector; + } gc->irq_contexts = kcalloc(nvec, sizeof(struct gdma_irq_context), GFP_KERNEL); @@ -1294,17 +1336,41 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev) goto free_irq; } - err = request_irq(irq, mana_gd_intr, 0, gic->name, gic); - if (err) - goto free_irq; - - cpu = cpumask_local_spread(i, gc->numa_node); - irq_set_affinity_and_hint(irq, cpumask_of(cpu)); + if (!i) { + err = request_irq(irq, mana_gd_intr, 0, gic->name, gic); + if (err) + goto free_irq; + + /* If number of IRQ is one extra than number of online CPUs, + * then we need to assign IRQ0 (hwc irq) and IRQ1 to + * same CPU. + * Else we will use different CPUs for IRQ0 and IRQ1. + * Also we are using cpumask_local_spread instead of + * cpumask_first for the node, because the node can be + * mem only. + */ + if (start_irq_index) { + cpu = cpumask_local_spread(i, gc->numa_node); + irq_set_affinity_and_hint(irq, cpumask_of(cpu)); + } else { + irqs[start_irq_index] = irq; + } + } else { + irqs[i - start_irq_index] = irq; + err = request_irq(irqs[i - start_irq_index], mana_gd_intr, 0, + gic->name, gic); + if (err) + goto free_irq; + } } + err = irq_setup(irqs, (nvec - start_irq_index), gc->numa_node); + if (err) + goto free_irq; + gc->max_num_msix = nvec; gc->num_msix_usable = nvec; - + cpus_read_unlock(); return 0; free_irq: @@ -1317,8 +1383,10 @@ free_irq: } kfree(gc->irq_contexts); + kfree(irqs); gc->irq_contexts = NULL; free_irq_vector: + cpus_read_unlock(); pci_free_irq_vectors(pdev); return err; } diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 56ccbd4c37fe..ed2fb44500b0 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -3078,4 +3078,5 @@ void ocelot_deinit_port(struct ocelot *ocelot, int port) } EXPORT_SYMBOL(ocelot_deinit_port); +MODULE_DESCRIPTION("Microsemi Ocelot switch family library"); MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index 2967bab72505..15180538b80a 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -1424,10 +1424,30 @@ static void nfp_nft_ct_translate_mangle_action(struct flow_action_entry *mangle_ mangle_action->mangle.mask = (__force u32)cpu_to_be32(mangle_action->mangle.mask); return; + /* Both struct tcphdr and struct udphdr start with + * __be16 source; + * __be16 dest; + * so we can use the same code for both. + */ case FLOW_ACT_MANGLE_HDR_TYPE_TCP: case FLOW_ACT_MANGLE_HDR_TYPE_UDP: - mangle_action->mangle.val = (__force u16)cpu_to_be16(mangle_action->mangle.val); - mangle_action->mangle.mask = (__force u16)cpu_to_be16(mangle_action->mangle.mask); + if (mangle_action->mangle.offset == offsetof(struct tcphdr, source)) { + mangle_action->mangle.val = + (__force u32)cpu_to_be32(mangle_action->mangle.val << 16); + /* The mask of mangle action is inverse mask, + * so clear the dest tp port with 0xFFFF to + * instead of rotate-left operation. + */ + mangle_action->mangle.mask = + (__force u32)cpu_to_be32(mangle_action->mangle.mask << 16 | 0xFFFF); + } + if (mangle_action->mangle.offset == offsetof(struct tcphdr, dest)) { + mangle_action->mangle.offset = 0; + mangle_action->mangle.val = + (__force u32)cpu_to_be32(mangle_action->mangle.val); + mangle_action->mangle.mask = + (__force u32)cpu_to_be32(mangle_action->mangle.mask); + } return; default: @@ -1864,10 +1884,30 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv, { struct flow_rule *rule = flow_cls_offload_flow_rule(flow); struct nfp_fl_ct_flow_entry *ct_entry; + struct flow_action_entry *ct_goto; struct nfp_fl_ct_zone_entry *zt; + struct flow_action_entry *act; bool wildcarded = false; struct flow_match_ct ct; - struct flow_action_entry *ct_goto; + int i; + + flow_action_for_each(i, act, &rule->action) { + switch (act->id) { + case FLOW_ACTION_REDIRECT: + case FLOW_ACTION_REDIRECT_INGRESS: + case FLOW_ACTION_MIRRED: + case FLOW_ACTION_MIRRED_INGRESS: + if (act->dev->rtnl_link_ops && + !strcmp(act->dev->rtnl_link_ops->kind, "openvswitch")) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload: out port is openvswitch internal port"); + return -EOPNOTSUPP; + } + break; + default: + break; + } + } flow_rule_match_ct(rule, &ct); if (!ct.mask->ct_zone) { diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index e522845c7c21..0d7d138d6e0d 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -1084,7 +1084,7 @@ nfp_tunnel_add_shared_mac(struct nfp_app *app, struct net_device *netdev, u16 nfp_mac_idx = 0; entry = nfp_tunnel_lookup_offloaded_macs(app, netdev->dev_addr); - if (entry && nfp_tunnel_is_mac_idx_global(entry->index)) { + if (entry && (nfp_tunnel_is_mac_idx_global(entry->index) || netif_is_lag_port(netdev))) { if (entry->bridge_count || !nfp_flower_is_supported_bridge(netdev)) { nfp_tunnel_offloaded_macs_inc_ref_and_link(entry, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 3b3210d823e8..f28e769e6fda 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2776,6 +2776,7 @@ static void nfp_net_netdev_init(struct nfp_net *nn) case NFP_NFD_VER_NFD3: netdev->netdev_ops = &nfp_nfd3_netdev_ops; netdev->xdp_features |= NETDEV_XDP_ACT_XSK_ZEROCOPY; + netdev->xdp_features |= NETDEV_XDP_ACT_REDIRECT; break; case NFP_NFD_VER_NFDK: netdev->netdev_ops = &nfp_nfdk_netdev_ops; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c index 33b4c2856316..3f10c5365c80 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c @@ -537,11 +537,13 @@ static int enable_bars(struct nfp6000_pcie *nfp, u16 interface) const u32 barcfg_msix_general = NFP_PCIE_BAR_PCIE2CPP_MapType( NFP_PCIE_BAR_PCIE2CPP_MapType_GENERAL) | - NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT; + NFP_PCIE_BAR_PCIE2CPP_LengthSelect( + NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT); const u32 barcfg_msix_xpb = NFP_PCIE_BAR_PCIE2CPP_MapType( NFP_PCIE_BAR_PCIE2CPP_MapType_BULK) | - NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT | + NFP_PCIE_BAR_PCIE2CPP_LengthSelect( + NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT) | NFP_PCIE_BAR_PCIE2CPP_Target_BaseAddress( NFP_CPP_TARGET_ISLAND_XPB); const u32 barcfg_explicit[4] = { diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 7a549b834e97..31f896c4aa26 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -1761,7 +1761,7 @@ static void nv_get_stats(int cpu, struct fe_priv *np, /* * nv_get_stats64: dev->ndo_get_stats64 function * Get latest stats value from the nic. - * Called with read_lock(&dev_base_lock) held for read - + * Called with rcu_read_lock() held - * only synchronized against unregister_netdevice. */ static void @@ -3090,7 +3090,7 @@ static void set_bufsize(struct net_device *dev) /* * nv_change_mtu: dev->change_mtu function - * Called with dev_base_lock held for read. + * Called with RTNL held for read. */ static int nv_change_mtu(struct net_device *dev, int new_mtu) { diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index 2667e1cde16b..bfcfc2d7bcbd 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -8,6 +8,7 @@ #include <linux/mutex.h> #include <linux/workqueue.h> #include <linux/skbuff.h> +#include <linux/bpf_trace.h> #include "ionic_if.h" #include "ionic_regs.h" @@ -195,6 +196,11 @@ typedef void (*ionic_desc_cb)(struct ionic_queue *q, #define IONIC_PAGE_GFP_MASK (GFP_ATOMIC | __GFP_NOWARN |\ __GFP_COMP | __GFP_MEMALLOC) +#define IONIC_XDP_MAX_LINEAR_MTU (IONIC_PAGE_SIZE - \ + (VLAN_ETH_HLEN + \ + XDP_PACKET_HEADROOM + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))) + struct ionic_buf_info { struct page *page; dma_addr_t dma_addr; @@ -222,6 +228,8 @@ struct ionic_desc_info { struct ionic_buf_info bufs[MAX_SKB_FRAGS + 1]; ionic_desc_cb cb; void *cb_arg; + struct xdp_frame *xdpf; + enum xdp_action act; }; #define IONIC_QUEUE_NAME_MAX_SZ 16 @@ -256,6 +264,9 @@ struct ionic_queue { struct ionic_txq_sg_desc *txq_sgl; struct ionic_rxq_sg_desc *rxq_sgl; }; + struct xdp_rxq_info *xdp_rxq_info; + struct ionic_queue *partner; + bool xdp_flush; dma_addr_t base_pa; dma_addr_t cmb_base_pa; dma_addr_t sg_base_pa; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index cd3c0b01402e..98df2ee11c51 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -721,6 +721,11 @@ static int ionic_set_channels(struct net_device *netdev, ionic_init_queue_params(lif, &qparam); + if ((ch->rx_count || ch->tx_count) && lif->xdp_prog) { + netdev_info(lif->netdev, "Split Tx/Rx interrupts not available when using XDP\n"); + return -EOPNOTSUPP; + } + if (ch->rx_count != ch->tx_count) { netdev_info(netdev, "The rx and tx count must be equal\n"); return -EINVAL; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index cf2d5ad7b68c..5cfc784f1227 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -46,6 +46,9 @@ static int ionic_start_queues(struct ionic_lif *lif); static void ionic_stop_queues(struct ionic_lif *lif); static void ionic_lif_queue_identify(struct ionic_lif *lif); +static int ionic_xdp_queues_config(struct ionic_lif *lif); +static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q); + static void ionic_dim_work(struct work_struct *work) { struct dim *dim = container_of(work, struct dim, work); @@ -422,6 +425,7 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq) qcq->sg_base_pa = 0; } + ionic_xdp_unregister_rxq_info(&qcq->q); ionic_qcq_intr_free(lif, qcq); vfree(qcq->cq.info); @@ -862,8 +866,7 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) .type = q->type, .ver = lif->qtype_info[q->type].version, .index = cpu_to_le32(q->index), - .flags = cpu_to_le16(IONIC_QINIT_F_IRQ | - IONIC_QINIT_F_SG), + .flags = cpu_to_le16(IONIC_QINIT_F_IRQ), .intr_index = cpu_to_le16(cq->bound_intr->index), .pid = cpu_to_le16(q->pid), .ring_size = ilog2(q->num_descs), @@ -875,6 +878,13 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) }; int err; + q->partner = &lif->txqcqs[q->index]->q; + q->partner->partner = q; + + if (!lif->xdp_prog || + (lif->xdp_prog->aux && lif->xdp_prog->aux->xdp_has_frags)) + ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_SG); + if (qcq->flags & IONIC_QCQ_F_CMB_RINGS) { ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_CMB); ctx.cmd.q_init.ring_base = cpu_to_le64(qcq->cmb_q_base_pa); @@ -1640,6 +1650,12 @@ static int ionic_init_nic_features(struct ionic_lif *lif) netdev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; + netdev->xdp_features = NETDEV_XDP_ACT_BASIC | + NETDEV_XDP_ACT_REDIRECT | + NETDEV_XDP_ACT_RX_SG | + NETDEV_XDP_ACT_NDO_XMIT | + NETDEV_XDP_ACT_NDO_XMIT_SG; + return 0; } @@ -1777,6 +1793,21 @@ static int ionic_start_queues_reconfig(struct ionic_lif *lif) return err; } +static bool ionic_xdp_is_valid_mtu(struct ionic_lif *lif, u32 mtu, + struct bpf_prog *xdp_prog) +{ + if (!xdp_prog) + return true; + + if (mtu <= IONIC_XDP_MAX_LINEAR_MTU) + return true; + + if (xdp_prog->aux && xdp_prog->aux->xdp_has_frags) + return true; + + return false; +} + static int ionic_change_mtu(struct net_device *netdev, int new_mtu) { struct ionic_lif *lif = netdev_priv(netdev); @@ -1789,8 +1820,13 @@ static int ionic_change_mtu(struct net_device *netdev, int new_mtu) .mtu = cpu_to_le32(new_mtu), }, }; + struct bpf_prog *xdp_prog; int err; + xdp_prog = READ_ONCE(lif->xdp_prog); + if (!ionic_xdp_is_valid_mtu(lif, new_mtu, xdp_prog)) + return -EINVAL; + err = ionic_adminq_post_wait(lif, &ctx); if (err) return err; @@ -2166,6 +2202,10 @@ static int ionic_txrx_enable(struct ionic_lif *lif) int derr = 0; int i, err; + err = ionic_xdp_queues_config(lif); + if (err) + return err; + for (i = 0; i < lif->nxqs; i++) { if (!(lif->rxqcqs[i] && lif->txqcqs[i])) { dev_err(lif->ionic->dev, "%s: bad qcq %d\n", __func__, i); @@ -2211,6 +2251,8 @@ err_out: derr = ionic_qcq_disable(lif, lif->rxqcqs[i], derr); } + ionic_xdp_queues_config(lif); + return err; } @@ -2668,11 +2710,151 @@ static void ionic_vf_attr_replay(struct ionic_lif *lif) ionic_vf_start(ionic); } +static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q) +{ + struct xdp_rxq_info *xi; + + if (!q->xdp_rxq_info) + return; + + xi = q->xdp_rxq_info; + q->xdp_rxq_info = NULL; + + xdp_rxq_info_unreg(xi); + kfree(xi); +} + +static int ionic_xdp_register_rxq_info(struct ionic_queue *q, unsigned int napi_id) +{ + struct xdp_rxq_info *rxq_info; + int err; + + rxq_info = kzalloc(sizeof(*rxq_info), GFP_KERNEL); + if (!rxq_info) + return -ENOMEM; + + err = xdp_rxq_info_reg(rxq_info, q->lif->netdev, q->index, napi_id); + if (err) { + dev_err(q->dev, "Queue %d xdp_rxq_info_reg failed, err %d\n", + q->index, err); + goto err_out; + } + + err = xdp_rxq_info_reg_mem_model(rxq_info, MEM_TYPE_PAGE_ORDER0, NULL); + if (err) { + dev_err(q->dev, "Queue %d xdp_rxq_info_reg_mem_model failed, err %d\n", + q->index, err); + xdp_rxq_info_unreg(rxq_info); + goto err_out; + } + + q->xdp_rxq_info = rxq_info; + + return 0; + +err_out: + kfree(rxq_info); + return err; +} + +static int ionic_xdp_queues_config(struct ionic_lif *lif) +{ + unsigned int i; + int err; + + if (!lif->rxqcqs) + return 0; + + /* There's no need to rework memory if not going to/from NULL program. + * If there is no lif->xdp_prog, there should also be no q.xdp_rxq_info + * This way we don't need to keep an *xdp_prog in every queue struct. + */ + if (!lif->xdp_prog == !lif->rxqcqs[0]->q.xdp_rxq_info) + return 0; + + for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) { + struct ionic_queue *q = &lif->rxqcqs[i]->q; + + if (q->xdp_rxq_info) { + ionic_xdp_unregister_rxq_info(q); + continue; + } + + err = ionic_xdp_register_rxq_info(q, lif->rxqcqs[i]->napi.napi_id); + if (err) { + dev_err(lif->ionic->dev, "failed to register RX queue %d info for XDP, err %d\n", + i, err); + goto err_out; + } + } + + return 0; + +err_out: + for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) + ionic_xdp_unregister_rxq_info(&lif->rxqcqs[i]->q); + + return err; +} + +static int ionic_xdp_config(struct net_device *netdev, struct netdev_bpf *bpf) +{ + struct ionic_lif *lif = netdev_priv(netdev); + struct bpf_prog *old_prog; + u32 maxfs; + + if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) { +#define XDP_ERR_SPLIT "XDP not available with split Tx/Rx interrupts" + NL_SET_ERR_MSG_MOD(bpf->extack, XDP_ERR_SPLIT); + netdev_info(lif->netdev, XDP_ERR_SPLIT); + return -EOPNOTSUPP; + } + + if (!ionic_xdp_is_valid_mtu(lif, netdev->mtu, bpf->prog)) { +#define XDP_ERR_MTU "MTU is too large for XDP without frags support" + NL_SET_ERR_MSG_MOD(bpf->extack, XDP_ERR_MTU); + netdev_info(lif->netdev, XDP_ERR_MTU); + return -EINVAL; + } + + maxfs = __le32_to_cpu(lif->identity->eth.max_frame_size) - VLAN_ETH_HLEN; + if (bpf->prog && !(bpf->prog->aux && bpf->prog->aux->xdp_has_frags)) + maxfs = min_t(u32, maxfs, IONIC_XDP_MAX_LINEAR_MTU); + netdev->max_mtu = maxfs; + + if (!netif_running(netdev)) { + old_prog = xchg(&lif->xdp_prog, bpf->prog); + } else { + mutex_lock(&lif->queue_lock); + ionic_stop_queues_reconfig(lif); + old_prog = xchg(&lif->xdp_prog, bpf->prog); + ionic_start_queues_reconfig(lif); + mutex_unlock(&lif->queue_lock); + } + + if (old_prog) + bpf_prog_put(old_prog); + + return 0; +} + +static int ionic_xdp(struct net_device *netdev, struct netdev_bpf *bpf) +{ + switch (bpf->command) { + case XDP_SETUP_PROG: + return ionic_xdp_config(netdev, bpf); + default: + return -EINVAL; + } +} + static const struct net_device_ops ionic_netdev_ops = { .ndo_open = ionic_open, .ndo_stop = ionic_stop, .ndo_eth_ioctl = ionic_eth_ioctl, .ndo_start_xmit = ionic_start_xmit, + .ndo_bpf = ionic_xdp, + .ndo_xdp_xmit = ionic_xdp_xmit, .ndo_get_stats64 = ionic_get_stats64, .ndo_set_rx_mode = ionic_ndo_set_rx_mode, .ndo_set_features = ionic_set_features, @@ -2755,6 +2937,8 @@ static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b) swap(a->q.base, b->q.base); swap(a->q.base_pa, b->q.base_pa); swap(a->q.info, b->q.info); + swap(a->q.xdp_rxq_info, b->q.xdp_rxq_info); + swap(a->q.partner, b->q.partner); swap(a->q_base, b->q_base); swap(a->q_base_pa, b->q_base_pa); swap(a->q_size, b->q_size); @@ -3391,9 +3575,12 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif) napi_enable(&qcq->napi); - if (qcq->flags & IONIC_QCQ_F_INTR) + if (qcq->flags & IONIC_QCQ_F_INTR) { + irq_set_affinity_hint(qcq->intr.vector, + &qcq->intr.affinity_mask); ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, IONIC_INTR_MASK_CLEAR); + } qcq->flags |= IONIC_QCQ_F_INITED; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h index 61548b3eea93..42006de8069d 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h @@ -37,6 +37,7 @@ struct ionic_tx_stats { u64 dma_map_err; u64 hwstamp_valid; u64 hwstamp_invalid; + u64 xdp_frames; }; struct ionic_rx_stats { @@ -51,6 +52,11 @@ struct ionic_rx_stats { u64 alloc_err; u64 hwstamp_valid; u64 hwstamp_invalid; + u64 xdp_drop; + u64 xdp_aborted; + u64 xdp_pass; + u64 xdp_tx; + u64 xdp_redirect; }; #define IONIC_QCQ_F_INITED BIT(0) @@ -135,6 +141,12 @@ struct ionic_lif_sw_stats { u64 hw_rx_over_errors; u64 hw_rx_missed_errors; u64 hw_tx_aborted_errors; + u64 xdp_drop; + u64 xdp_aborted; + u64 xdp_pass; + u64 xdp_tx; + u64 xdp_redirect; + u64 xdp_frames; }; enum ionic_lif_state_flags { @@ -230,6 +242,7 @@ struct ionic_lif { struct ionic_phc *phc; struct dentry *dentry; + struct bpf_prog *xdp_prog; }; struct ionic_phc { diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c index 1f6022fb7679..0107599a9dd4 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c @@ -27,6 +27,12 @@ static const struct ionic_stat_desc ionic_lif_stats_desc[] = { IONIC_LIF_STAT_DESC(hw_rx_over_errors), IONIC_LIF_STAT_DESC(hw_rx_missed_errors), IONIC_LIF_STAT_DESC(hw_tx_aborted_errors), + IONIC_LIF_STAT_DESC(xdp_drop), + IONIC_LIF_STAT_DESC(xdp_aborted), + IONIC_LIF_STAT_DESC(xdp_pass), + IONIC_LIF_STAT_DESC(xdp_tx), + IONIC_LIF_STAT_DESC(xdp_redirect), + IONIC_LIF_STAT_DESC(xdp_frames), }; static const struct ionic_stat_desc ionic_port_stats_desc[] = { @@ -135,6 +141,7 @@ static const struct ionic_stat_desc ionic_tx_stats_desc[] = { IONIC_TX_STAT_DESC(csum_none), IONIC_TX_STAT_DESC(csum), IONIC_TX_STAT_DESC(vlan_inserted), + IONIC_TX_STAT_DESC(xdp_frames), }; static const struct ionic_stat_desc ionic_rx_stats_desc[] = { @@ -149,6 +156,11 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = { IONIC_RX_STAT_DESC(hwstamp_invalid), IONIC_RX_STAT_DESC(dropped), IONIC_RX_STAT_DESC(vlan_stripped), + IONIC_RX_STAT_DESC(xdp_drop), + IONIC_RX_STAT_DESC(xdp_aborted), + IONIC_RX_STAT_DESC(xdp_pass), + IONIC_RX_STAT_DESC(xdp_tx), + IONIC_RX_STAT_DESC(xdp_redirect), }; #define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc) @@ -171,6 +183,7 @@ static void ionic_add_lif_txq_stats(struct ionic_lif *lif, int q_num, stats->tx_csum += txstats->csum; stats->tx_hwstamp_valid += txstats->hwstamp_valid; stats->tx_hwstamp_invalid += txstats->hwstamp_invalid; + stats->xdp_frames += txstats->xdp_frames; } static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num, @@ -185,6 +198,11 @@ static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num, stats->rx_csum_error += rxstats->csum_error; stats->rx_hwstamp_valid += rxstats->hwstamp_valid; stats->rx_hwstamp_invalid += rxstats->hwstamp_invalid; + stats->xdp_drop += rxstats->xdp_drop; + stats->xdp_aborted += rxstats->xdp_aborted; + stats->xdp_pass += rxstats->xdp_pass; + stats->xdp_tx += rxstats->xdp_tx; + stats->xdp_redirect += rxstats->xdp_redirect; } static void ionic_get_lif_stats(struct ionic_lif *lif, diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 54cd96b035d6..56a7ad5bff17 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -10,6 +10,23 @@ #include "ionic_lif.h" #include "ionic_txrx.h" +static int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs); + +static dma_addr_t ionic_tx_map_single(struct ionic_queue *q, + void *data, size_t len); + +static dma_addr_t ionic_tx_map_frag(struct ionic_queue *q, + const skb_frag_t *frag, + size_t offset, size_t len); + +static void ionic_tx_desc_unmap_bufs(struct ionic_queue *q, + struct ionic_desc_info *desc_info); + +static void ionic_tx_clean(struct ionic_queue *q, + struct ionic_desc_info *desc_info, + struct ionic_cq_info *cq_info, + void *cb_arg); + static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell, ionic_desc_cb cb_func, void *cb_arg) { @@ -88,6 +105,21 @@ static inline struct netdev_queue *q_to_ndq(struct ionic_queue *q) return netdev_get_tx_queue(q->lif->netdev, q->index); } +static void *ionic_rx_buf_va(struct ionic_buf_info *buf_info) +{ + return page_address(buf_info->page) + buf_info->page_offset; +} + +static dma_addr_t ionic_rx_buf_pa(struct ionic_buf_info *buf_info) +{ + return buf_info->dma_addr + buf_info->page_offset; +} + +static unsigned int ionic_rx_buf_size(struct ionic_buf_info *buf_info) +{ + return min_t(u32, IONIC_MAX_BUF_LEN, IONIC_PAGE_SIZE - buf_info->page_offset); +} + static int ionic_rx_page_alloc(struct ionic_queue *q, struct ionic_buf_info *buf_info) { @@ -162,7 +194,7 @@ static bool ionic_rx_buf_recycle(struct ionic_queue *q, if (page_to_nid(buf_info->page) != numa_mem_id()) return false; - size = ALIGN(used, IONIC_PAGE_SPLIT_SZ); + size = ALIGN(used, q->xdp_rxq_info ? IONIC_PAGE_SIZE : IONIC_PAGE_SPLIT_SZ); buf_info->page_offset += size; if (buf_info->page_offset >= IONIC_PAGE_SIZE) return false; @@ -174,7 +206,10 @@ static bool ionic_rx_buf_recycle(struct ionic_queue *q, static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, struct ionic_desc_info *desc_info, - struct ionic_rxq_comp *comp) + unsigned int headroom, + unsigned int len, + unsigned int num_sg_elems, + bool synced) { struct net_device *netdev = q->lif->netdev; struct ionic_buf_info *buf_info; @@ -183,12 +218,10 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, struct sk_buff *skb; unsigned int i; u16 frag_len; - u16 len; stats = q_to_rx_stats(q); buf_info = &desc_info->bufs[0]; - len = le16_to_cpu(comp->len); prefetchw(buf_info->page); @@ -200,24 +233,26 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, return NULL; } - i = comp->num_sg_elems + 1; + i = num_sg_elems + 1; do { if (unlikely(!buf_info->page)) { dev_kfree_skb(skb); return NULL; } - frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, - IONIC_PAGE_SIZE - buf_info->page_offset)); + if (headroom) + frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN); + else + frag_len = min_t(u16, len, ionic_rx_buf_size(buf_info)); len -= frag_len; - dma_sync_single_for_cpu(dev, - buf_info->dma_addr + buf_info->page_offset, - frag_len, DMA_FROM_DEVICE); + if (!synced) + dma_sync_single_range_for_cpu(dev, ionic_rx_buf_pa(buf_info), + headroom, frag_len, DMA_FROM_DEVICE); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - buf_info->page, buf_info->page_offset, frag_len, - IONIC_PAGE_SIZE); + buf_info->page, buf_info->page_offset + headroom, + frag_len, IONIC_PAGE_SIZE); if (!ionic_rx_buf_recycle(q, buf_info, frag_len)) { dma_unmap_page(dev, buf_info->dma_addr, @@ -225,6 +260,10 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, buf_info->page = NULL; } + /* only needed on the first buffer */ + if (headroom) + headroom = 0; + buf_info++; i--; @@ -235,19 +274,19 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, struct ionic_desc_info *desc_info, - struct ionic_rxq_comp *comp) + unsigned int headroom, + unsigned int len, + bool synced) { struct net_device *netdev = q->lif->netdev; struct ionic_buf_info *buf_info; struct ionic_rx_stats *stats; struct device *dev = q->dev; struct sk_buff *skb; - u16 len; stats = q_to_rx_stats(q); buf_info = &desc_info->bufs[0]; - len = le16_to_cpu(comp->len); skb = napi_alloc_skb(&q_to_qcq(q)->napi, len); if (unlikely(!skb)) { @@ -262,11 +301,12 @@ static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, return NULL; } - dma_sync_single_for_cpu(dev, buf_info->dma_addr + buf_info->page_offset, - len, DMA_FROM_DEVICE); - skb_copy_to_linear_data(skb, page_address(buf_info->page) + buf_info->page_offset, len); - dma_sync_single_for_device(dev, buf_info->dma_addr + buf_info->page_offset, - len, DMA_FROM_DEVICE); + if (!synced) + dma_sync_single_range_for_cpu(dev, ionic_rx_buf_pa(buf_info), + headroom, len, DMA_FROM_DEVICE); + skb_copy_to_linear_data(skb, ionic_rx_buf_va(buf_info) + headroom, len); + dma_sync_single_range_for_device(dev, ionic_rx_buf_pa(buf_info), + headroom, len, DMA_FROM_DEVICE); skb_put(skb, len); skb->protocol = eth_type_trans(skb, q->lif->netdev); @@ -274,6 +314,315 @@ static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, return skb; } +static void ionic_xdp_tx_desc_clean(struct ionic_queue *q, + struct ionic_desc_info *desc_info) +{ + unsigned int nbufs = desc_info->nbufs; + struct ionic_buf_info *buf_info; + struct device *dev = q->dev; + int i; + + if (!nbufs) + return; + + buf_info = desc_info->bufs; + dma_unmap_single(dev, buf_info->dma_addr, + buf_info->len, DMA_TO_DEVICE); + if (desc_info->act == XDP_TX) + __free_pages(buf_info->page, 0); + buf_info->page = NULL; + + buf_info++; + for (i = 1; i < nbufs + 1 && buf_info->page; i++, buf_info++) { + dma_unmap_page(dev, buf_info->dma_addr, + buf_info->len, DMA_TO_DEVICE); + if (desc_info->act == XDP_TX) + __free_pages(buf_info->page, 0); + buf_info->page = NULL; + } + + if (desc_info->act == XDP_REDIRECT) + xdp_return_frame(desc_info->xdpf); + + desc_info->nbufs = 0; + desc_info->xdpf = NULL; + desc_info->act = 0; +} + +static int ionic_xdp_post_frame(struct net_device *netdev, + struct ionic_queue *q, struct xdp_frame *frame, + enum xdp_action act, struct page *page, int off, + bool ring_doorbell) +{ + struct ionic_desc_info *desc_info; + struct ionic_buf_info *buf_info; + struct ionic_tx_stats *stats; + struct ionic_txq_desc *desc; + size_t len = frame->len; + dma_addr_t dma_addr; + u64 cmd; + + desc_info = &q->info[q->head_idx]; + desc = desc_info->txq_desc; + buf_info = desc_info->bufs; + stats = q_to_tx_stats(q); + + dma_addr = ionic_tx_map_single(q, frame->data, len); + if (dma_mapping_error(q->dev, dma_addr)) { + stats->dma_map_err++; + return -EIO; + } + buf_info->dma_addr = dma_addr; + buf_info->len = len; + buf_info->page = page; + buf_info->page_offset = off; + + desc_info->nbufs = 1; + desc_info->xdpf = frame; + desc_info->act = act; + + if (xdp_frame_has_frags(frame)) { + struct ionic_txq_sg_elem *elem; + struct skb_shared_info *sinfo; + struct ionic_buf_info *bi; + skb_frag_t *frag; + int i; + + bi = &buf_info[1]; + sinfo = xdp_get_shared_info_from_frame(frame); + frag = sinfo->frags; + elem = desc_info->txq_sg_desc->elems; + for (i = 0; i < sinfo->nr_frags; i++, frag++, bi++) { + dma_addr = ionic_tx_map_frag(q, frag, 0, skb_frag_size(frag)); + if (dma_mapping_error(q->dev, dma_addr)) { + stats->dma_map_err++; + ionic_tx_desc_unmap_bufs(q, desc_info); + return -EIO; + } + bi->dma_addr = dma_addr; + bi->len = skb_frag_size(frag); + bi->page = skb_frag_page(frag); + + elem->addr = cpu_to_le64(bi->dma_addr); + elem->len = cpu_to_le16(bi->len); + elem++; + + desc_info->nbufs++; + } + } + + cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_NONE, + 0, (desc_info->nbufs - 1), buf_info->dma_addr); + desc->cmd = cpu_to_le64(cmd); + desc->len = cpu_to_le16(len); + desc->csum_start = 0; + desc->csum_offset = 0; + + stats->xdp_frames++; + stats->pkts++; + stats->bytes += len; + + ionic_txq_post(q, ring_doorbell, ionic_tx_clean, NULL); + + return 0; +} + +int ionic_xdp_xmit(struct net_device *netdev, int n, + struct xdp_frame **xdp_frames, u32 flags) +{ + struct ionic_lif *lif = netdev_priv(netdev); + struct ionic_queue *txq; + struct netdev_queue *nq; + int nxmit; + int space; + int cpu; + int qi; + + if (unlikely(!test_bit(IONIC_LIF_F_UP, lif->state))) + return -ENETDOWN; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + /* AdminQ is assumed on cpu 0, while we attempt to affinitize the + * TxRx queue pairs 0..n-1 on cpus 1..n. We try to keep with that + * affinitization here, but of course irqbalance and friends might + * have juggled things anyway, so we have to check for the 0 case. + */ + cpu = smp_processor_id(); + qi = cpu ? (cpu - 1) % lif->nxqs : cpu; + + txq = &lif->txqcqs[qi]->q; + nq = netdev_get_tx_queue(netdev, txq->index); + __netif_tx_lock(nq, cpu); + txq_trans_cond_update(nq); + + if (netif_tx_queue_stopped(nq) || + unlikely(ionic_maybe_stop_tx(txq, 1))) { + __netif_tx_unlock(nq); + return -EIO; + } + + space = min_t(int, n, ionic_q_space_avail(txq)); + for (nxmit = 0; nxmit < space ; nxmit++) { + if (ionic_xdp_post_frame(netdev, txq, xdp_frames[nxmit], + XDP_REDIRECT, + virt_to_page(xdp_frames[nxmit]->data), + 0, false)) { + nxmit--; + break; + } + } + + if (flags & XDP_XMIT_FLUSH) + ionic_dbell_ring(lif->kern_dbpage, txq->hw_type, + txq->dbval | txq->head_idx); + + ionic_maybe_stop_tx(txq, 4); + __netif_tx_unlock(nq); + + return nxmit; +} + +static bool ionic_run_xdp(struct ionic_rx_stats *stats, + struct net_device *netdev, + struct bpf_prog *xdp_prog, + struct ionic_queue *rxq, + struct ionic_buf_info *buf_info, + int len) +{ + u32 xdp_action = XDP_ABORTED; + struct xdp_buff xdp_buf; + struct ionic_queue *txq; + struct netdev_queue *nq; + struct xdp_frame *xdpf; + int remain_len; + int frag_len; + int err = 0; + + xdp_init_buff(&xdp_buf, IONIC_PAGE_SIZE, rxq->xdp_rxq_info); + frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN); + xdp_prepare_buff(&xdp_buf, ionic_rx_buf_va(buf_info), + XDP_PACKET_HEADROOM, frag_len, false); + + dma_sync_single_range_for_cpu(rxq->dev, ionic_rx_buf_pa(buf_info), + XDP_PACKET_HEADROOM, len, + DMA_FROM_DEVICE); + + prefetchw(&xdp_buf.data_hard_start); + + /* We limit MTU size to one buffer if !xdp_has_frags, so + * if the recv len is bigger than one buffer + * then we know we have frag info to gather + */ + remain_len = len - frag_len; + if (remain_len) { + struct skb_shared_info *sinfo; + struct ionic_buf_info *bi; + skb_frag_t *frag; + + bi = buf_info; + sinfo = xdp_get_shared_info_from_buff(&xdp_buf); + sinfo->nr_frags = 0; + sinfo->xdp_frags_size = 0; + xdp_buff_set_frags_flag(&xdp_buf); + + do { + if (unlikely(sinfo->nr_frags >= MAX_SKB_FRAGS)) { + err = -ENOSPC; + goto out_xdp_abort; + } + + frag = &sinfo->frags[sinfo->nr_frags]; + sinfo->nr_frags++; + bi++; + frag_len = min_t(u16, remain_len, ionic_rx_buf_size(bi)); + dma_sync_single_range_for_cpu(rxq->dev, ionic_rx_buf_pa(bi), + 0, frag_len, DMA_FROM_DEVICE); + skb_frag_fill_page_desc(frag, bi->page, 0, frag_len); + sinfo->xdp_frags_size += frag_len; + remain_len -= frag_len; + + if (page_is_pfmemalloc(bi->page)) + xdp_buff_set_frag_pfmemalloc(&xdp_buf); + } while (remain_len > 0); + } + + xdp_action = bpf_prog_run_xdp(xdp_prog, &xdp_buf); + + switch (xdp_action) { + case XDP_PASS: + stats->xdp_pass++; + return false; /* false = we didn't consume the packet */ + + case XDP_DROP: + ionic_rx_page_free(rxq, buf_info); + stats->xdp_drop++; + break; + + case XDP_TX: + xdpf = xdp_convert_buff_to_frame(&xdp_buf); + if (!xdpf) + goto out_xdp_abort; + + txq = rxq->partner; + nq = netdev_get_tx_queue(netdev, txq->index); + __netif_tx_lock(nq, smp_processor_id()); + txq_trans_cond_update(nq); + + if (netif_tx_queue_stopped(nq) || + unlikely(ionic_maybe_stop_tx(txq, 1))) { + __netif_tx_unlock(nq); + goto out_xdp_abort; + } + + dma_unmap_page(rxq->dev, buf_info->dma_addr, + IONIC_PAGE_SIZE, DMA_FROM_DEVICE); + + err = ionic_xdp_post_frame(netdev, txq, xdpf, XDP_TX, + buf_info->page, + buf_info->page_offset, + true); + __netif_tx_unlock(nq); + if (err) { + netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err); + goto out_xdp_abort; + } + stats->xdp_tx++; + + /* the Tx completion will free the buffers */ + break; + + case XDP_REDIRECT: + /* unmap the pages before handing them to a different device */ + dma_unmap_page(rxq->dev, buf_info->dma_addr, + IONIC_PAGE_SIZE, DMA_FROM_DEVICE); + + err = xdp_do_redirect(netdev, &xdp_buf, xdp_prog); + if (err) { + netdev_dbg(netdev, "xdp_do_redirect err %d\n", err); + goto out_xdp_abort; + } + buf_info->page = NULL; + rxq->xdp_flush = true; + stats->xdp_redirect++; + break; + + case XDP_ABORTED: + default: + goto out_xdp_abort; + } + + return true; + +out_xdp_abort: + trace_xdp_exception(netdev, xdp_prog, xdp_action); + ionic_rx_page_free(rxq, buf_info); + stats->xdp_aborted++; + + return true; +} + static void ionic_rx_clean(struct ionic_queue *q, struct ionic_desc_info *desc_info, struct ionic_cq_info *cq_info, @@ -283,7 +632,10 @@ static void ionic_rx_clean(struct ionic_queue *q, struct ionic_qcq *qcq = q_to_qcq(q); struct ionic_rx_stats *stats; struct ionic_rxq_comp *comp; + struct bpf_prog *xdp_prog; + unsigned int headroom; struct sk_buff *skb; + u16 len; comp = cq_info->cq_desc + qcq->cq.desc_size - sizeof(*comp); @@ -294,13 +646,21 @@ static void ionic_rx_clean(struct ionic_queue *q, return; } + len = le16_to_cpu(comp->len); stats->pkts++; - stats->bytes += le16_to_cpu(comp->len); + stats->bytes += len; + + xdp_prog = READ_ONCE(q->lif->xdp_prog); + if (xdp_prog && + ionic_run_xdp(stats, netdev, xdp_prog, q, desc_info->bufs, len)) + return; - if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) - skb = ionic_rx_copybreak(q, desc_info, comp); + headroom = q->xdp_rxq_info ? XDP_PACKET_HEADROOM : 0; + if (len <= q->lif->rx_copybreak) + skb = ionic_rx_copybreak(q, desc_info, headroom, len, !!xdp_prog); else - skb = ionic_rx_frags(q, desc_info, comp); + skb = ionic_rx_frags(q, desc_info, headroom, len, + comp->num_sg_elems, !!xdp_prog); if (unlikely(!skb)) { stats->dropped++; @@ -367,7 +727,7 @@ static void ionic_rx_clean(struct ionic_queue *q, } } - if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) + if (len <= q->lif->rx_copybreak) napi_gro_receive(&qcq->napi, skb); else napi_gro_frags(&qcq->napi); @@ -424,8 +784,9 @@ void ionic_rx_fill(struct ionic_queue *q) unsigned int frag_len; unsigned int nfrags; unsigned int n_fill; - unsigned int i, j; unsigned int len; + unsigned int i; + unsigned int j; n_fill = ionic_q_space_avail(q); @@ -434,9 +795,12 @@ void ionic_rx_fill(struct ionic_queue *q) if (n_fill < fill_threshold) return; - len = netdev->mtu + ETH_HLEN + VLAN_HLEN; + len = netdev->mtu + VLAN_ETH_HLEN; for (i = n_fill; i; i--) { + unsigned int headroom; + unsigned int buf_len; + nfrags = 0; remain_len = len; desc_info = &q->info[q->head_idx]; @@ -451,10 +815,18 @@ void ionic_rx_fill(struct ionic_queue *q) } } - /* fill main descriptor - buf[0] */ - desc->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); - frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, - IONIC_PAGE_SIZE - buf_info->page_offset)); + /* fill main descriptor - buf[0] + * XDP uses space in the first buffer, so account for + * head room, tail room, and ip header in the first frag size. + */ + headroom = q->xdp_rxq_info ? XDP_PACKET_HEADROOM : 0; + if (q->xdp_rxq_info) + buf_len = IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN; + else + buf_len = ionic_rx_buf_size(buf_info); + frag_len = min_t(u16, len, buf_len); + + desc->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info) + headroom); desc->len = cpu_to_le16(frag_len); remain_len -= frag_len; buf_info++; @@ -472,10 +844,8 @@ void ionic_rx_fill(struct ionic_queue *q) } } - sg_elem->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); - frag_len = min_t(u16, remain_len, min_t(u32, IONIC_MAX_BUF_LEN, - IONIC_PAGE_SIZE - - buf_info->page_offset)); + sg_elem->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info)); + frag_len = min_t(u16, remain_len, ionic_rx_buf_size(buf_info)); sg_elem->len = cpu_to_le16(frag_len); remain_len -= frag_len; buf_info++; @@ -579,6 +949,9 @@ int ionic_tx_napi(struct napi_struct *napi, int budget) work_done = ionic_cq_service(cq, budget, ionic_tx_service, NULL, NULL); + if (unlikely(!budget)) + return budget; + if (work_done < budget && napi_complete_done(napi, work_done)) { ionic_dim_update(qcq, IONIC_LIF_F_TX_DIM_INTR); flags |= IONIC_INTR_CRED_UNMASK; @@ -598,6 +971,14 @@ int ionic_tx_napi(struct napi_struct *napi, int budget) return work_done; } +static void ionic_xdp_do_flush(struct ionic_cq *cq) +{ + if (cq->bound_q->xdp_flush) { + xdp_do_flush(); + cq->bound_q->xdp_flush = false; + } +} + int ionic_rx_napi(struct napi_struct *napi, int budget) { struct ionic_qcq *qcq = napi_to_qcq(napi); @@ -607,6 +988,9 @@ int ionic_rx_napi(struct napi_struct *napi, int budget) u32 work_done = 0; u32 flags = 0; + if (unlikely(!budget)) + return budget; + lif = cq->bound_q->lif; idev = &lif->ionic->idev; @@ -615,6 +999,7 @@ int ionic_rx_napi(struct napi_struct *napi, int budget) ionic_rx_fill(cq->bound_q); + ionic_xdp_do_flush(cq); if (work_done < budget && napi_complete_done(napi, work_done)) { ionic_dim_update(qcq, IONIC_LIF_F_RX_DIM_INTR); flags |= IONIC_INTR_CRED_UNMASK; @@ -656,11 +1041,15 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) tx_work_done = ionic_cq_service(txcq, IONIC_TX_BUDGET_DEFAULT, ionic_tx_service, NULL, NULL); + if (unlikely(!budget)) + return budget; + rx_work_done = ionic_cq_service(rxcq, budget, ionic_rx_service, NULL, NULL); ionic_rx_fill(rxcq->bound_q); + ionic_xdp_do_flush(rxcq); if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) { ionic_dim_update(rxqcq, 0); flags |= IONIC_INTR_CRED_UNMASK; @@ -796,6 +1185,16 @@ static void ionic_tx_clean(struct ionic_queue *q, struct sk_buff *skb = cb_arg; u16 qi; + if (desc_info->xdpf) { + ionic_xdp_tx_desc_clean(q->partner, desc_info); + stats->clean++; + + if (unlikely(__netif_subqueue_stopped(q->lif->netdev, q->index))) + netif_wake_subqueue(q->lif->netdev, q->index); + + return; + } + ionic_tx_desc_unmap_bufs(q, desc_info); if (!skb) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h index d7cbaad8a6fb..82fc38e0f573 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h @@ -17,4 +17,5 @@ netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev); bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info); bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info); +int ionic_xdp_xmit(struct net_device *netdev, int n, struct xdp_frame **xdp, u32 flags); #endif /* _IONIC_TXRX_H_ */ diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c index 35ec9aab3dc7..51fa880eaf6c 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c @@ -1186,7 +1186,6 @@ static int netxen_p3_has_mn(struct netxen_adapter *adapter) { u32 capability, flashed_ver; - capability = 0; /* NX2031 always had MN */ if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) @@ -1197,7 +1196,6 @@ netxen_p3_has_mn(struct netxen_adapter *adapter) flashed_ver = NETXEN_DECODE_VERSION(flashed_ver); if (flashed_ver >= NETXEN_VERSION_CODE(4, 0, 220)) { - capability = NXRD32(adapter, NX_PEG_TUNE_CAPABILITY); if (capability & NX_PEG_TUNE_MN_PRESENT) return 1; diff --git a/drivers/net/ethernet/qlogic/qed/qed_rdma.c b/drivers/net/ethernet/qlogic/qed/qed_rdma.c index 5a5dbbb8d8aa..9a1660a12c57 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_rdma.c +++ b/drivers/net/ethernet/qlogic/qed/qed_rdma.c @@ -1794,8 +1794,6 @@ qed_rdma_create_srq(void *rdma_cxt, goto err; opaque_fid = p_hwfn->hw_info.opaque_fid; - - opaque_fid = p_hwfn->hw_info.opaque_fid; init_data.opaque_fid = opaque_fid; init_data.comp_mode = QED_SPQ_MODE_EBLOCK; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 0e240b5ab8d4..dfa15619fd78 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -1776,7 +1776,7 @@ static int qede_get_tunable(struct net_device *dev, return 0; } -static int qede_get_eee(struct net_device *dev, struct ethtool_eee *edata) +static int qede_get_eee(struct net_device *dev, struct ethtool_keee *edata) { struct qede_dev *edev = netdev_priv(dev); struct qed_link_output current_link; @@ -1790,17 +1790,17 @@ static int qede_get_eee(struct net_device *dev, struct ethtool_eee *edata) } if (current_link.eee.adv_caps & QED_EEE_1G_ADV) - edata->advertised = ADVERTISED_1000baseT_Full; + edata->advertised_u32 = ADVERTISED_1000baseT_Full; if (current_link.eee.adv_caps & QED_EEE_10G_ADV) - edata->advertised |= ADVERTISED_10000baseT_Full; + edata->advertised_u32 |= ADVERTISED_10000baseT_Full; if (current_link.sup_caps & QED_EEE_1G_ADV) - edata->supported = ADVERTISED_1000baseT_Full; + edata->supported_u32 = ADVERTISED_1000baseT_Full; if (current_link.sup_caps & QED_EEE_10G_ADV) - edata->supported |= ADVERTISED_10000baseT_Full; + edata->supported_u32 |= ADVERTISED_10000baseT_Full; if (current_link.eee.lp_adv_caps & QED_EEE_1G_ADV) - edata->lp_advertised = ADVERTISED_1000baseT_Full; + edata->lp_advertised_u32 = ADVERTISED_1000baseT_Full; if (current_link.eee.lp_adv_caps & QED_EEE_10G_ADV) - edata->lp_advertised |= ADVERTISED_10000baseT_Full; + edata->lp_advertised_u32 |= ADVERTISED_10000baseT_Full; edata->tx_lpi_timer = current_link.eee.tx_lpi_timer; edata->eee_enabled = current_link.eee.enable; @@ -1810,7 +1810,7 @@ static int qede_get_eee(struct net_device *dev, struct ethtool_eee *edata) return 0; } -static int qede_set_eee(struct net_device *dev, struct ethtool_eee *edata) +static int qede_set_eee(struct net_device *dev, struct ethtool_keee *edata) { struct qede_dev *edev = netdev_priv(dev); struct qed_link_output current_link; @@ -1832,20 +1832,20 @@ static int qede_set_eee(struct net_device *dev, struct ethtool_eee *edata) memset(¶ms, 0, sizeof(params)); params.override_flags |= QED_LINK_OVERRIDE_EEE_CONFIG; - if (!(edata->advertised & (ADVERTISED_1000baseT_Full | - ADVERTISED_10000baseT_Full)) || - ((edata->advertised & (ADVERTISED_1000baseT_Full | - ADVERTISED_10000baseT_Full)) != - edata->advertised)) { + if (!(edata->advertised_u32 & (ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full)) || + ((edata->advertised_u32 & (ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full)) != + edata->advertised_u32)) { DP_VERBOSE(edev, QED_MSG_DEBUG, "Invalid advertised capabilities %d\n", - edata->advertised); + edata->advertised_u32); return -EINVAL; } - if (edata->advertised & ADVERTISED_1000baseT_Full) + if (edata->advertised_u32 & ADVERTISED_1000baseT_Full) params.eee.adv_caps = QED_EEE_1G_ADV; - if (edata->advertised & ADVERTISED_10000baseT_Full) + if (edata->advertised_u32 & ADVERTISED_10000baseT_Full) params.eee.adv_caps |= QED_EEE_10G_ADV; params.eee.enable = edata->eee_enabled; params.eee.tx_lpi_enable = edata->tx_lpi_enabled; diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c index 3270df72541b..4c06f55878de 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac.c +++ b/drivers/net/ethernet/qualcomm/emac/emac.c @@ -771,5 +771,6 @@ static struct platform_driver emac_platform_driver = { module_platform_driver(emac_platform_driver); +MODULE_DESCRIPTION("Qualcomm EMAC Gigabit Ethernet driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:qcom-emac"); diff --git a/drivers/net/ethernet/qualcomm/qca_7k.c b/drivers/net/ethernet/qualcomm/qca_7k.c index 4292c89bd35c..6263e4cf47fa 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k.c +++ b/drivers/net/ethernet/qualcomm/qca_7k.c @@ -1,22 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* - * * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * */ /* This module implements the Qualcomm Atheros SPI protocol for diff --git a/drivers/net/ethernet/qualcomm/qca_7k.h b/drivers/net/ethernet/qualcomm/qca_7k.h index 356de8ec5d48..828ee9c27578 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k.h +++ b/drivers/net/ethernet/qualcomm/qca_7k.h @@ -1,21 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * */ /* Qualcomm Atheros SPI register definition. diff --git a/drivers/net/ethernet/qualcomm/qca_7k_common.c b/drivers/net/ethernet/qualcomm/qca_7k_common.c index 6b511f05df61..5302da587620 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k_common.c +++ b/drivers/net/ethernet/qualcomm/qca_7k_common.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* * Copyright (c) 2011, 2012, Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Atheros ethernet framing. Every Ethernet frame is surrounded @@ -162,5 +149,5 @@ EXPORT_SYMBOL_GPL(qcafrm_fsm_decode); MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 common"); MODULE_AUTHOR("Qualcomm Atheros Communications"); -MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); +MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/qualcomm/qca_7k_common.h b/drivers/net/ethernet/qualcomm/qca_7k_common.h index 928554f11e35..44ed66fdb407 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k_common.h +++ b/drivers/net/ethernet/qualcomm/qca_7k_common.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* * Copyright (c) 2011, 2012, Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Atheros Ethernet framing. Every Ethernet frame is surrounded by an atheros @@ -107,9 +94,6 @@ struct qcafrm_handle { /* Offset in buffer (borrowed for length too) */ u16 offset; - - /* Frame length as kept by this module */ - u16 len; }; u16 qcafrm_create_header(u8 *buf, u16 len); @@ -128,17 +112,6 @@ static inline void qcafrm_fsm_init_uart(struct qcafrm_handle *handle) handle->state = handle->init; } -/* Gather received bytes and try to extract a full Ethernet frame - * by following a simple state machine. - * - * Return: QCAFRM_GATHER No Ethernet frame fully received yet. - * QCAFRM_NOHEAD Header expected but not found. - * QCAFRM_INVLEN QCA7K frame length is invalid - * QCAFRM_NOTAIL Footer expected but not found. - * > 0 Number of byte in the fully received - * Ethernet frame - */ - s32 qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_byte); #endif /* _QCA_FRAMING_H */ diff --git a/drivers/net/ethernet/qualcomm/qca_debug.c b/drivers/net/ethernet/qualcomm/qca_debug.c index 1822f2ad8f0d..ff3b89e9028e 100644 --- a/drivers/net/ethernet/qualcomm/qca_debug.c +++ b/drivers/net/ethernet/qualcomm/qca_debug.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* This file contains debugging routines for use in the QCA7K driver. @@ -255,7 +242,7 @@ qcaspi_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring, struct qcaspi *qca = netdev_priv(dev); ring->rx_max_pending = QCASPI_RX_MAX_FRAMES; - ring->tx_max_pending = TX_RING_MAX_LEN; + ring->tx_max_pending = QCASPI_TX_RING_MAX_LEN; ring->rx_pending = QCASPI_RX_MAX_FRAMES; ring->tx_pending = qca->txr.count; } @@ -275,8 +262,8 @@ qcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring, if (qca->spi_thread) kthread_park(qca->spi_thread); - qca->txr.count = max_t(u32, ring->tx_pending, TX_RING_MIN_LEN); - qca->txr.count = min_t(u16, qca->txr.count, TX_RING_MAX_LEN); + qca->txr.count = max_t(u32, ring->tx_pending, QCASPI_TX_RING_MIN_LEN); + qca->txr.count = min_t(u16, qca->txr.count, QCASPI_TX_RING_MAX_LEN); if (qca->spi_thread) kthread_unpark(qca->spi_thread); diff --git a/drivers/net/ethernet/qualcomm/qca_debug.h b/drivers/net/ethernet/qualcomm/qca_debug.h index 46a785844421..0d98cef3abc4 100644 --- a/drivers/net/ethernet/qualcomm/qca_debug.h +++ b/drivers/net/ethernet/qualcomm/qca_debug.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* This file contains debugging routines for use in the QCA7K driver. diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 5f3c11fb3fa2..5799ecc88a87 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* This module implements the Qualcomm Atheros SPI protocol for @@ -359,7 +346,7 @@ qcaspi_receive(struct qcaspi *qca) /* Read the packet size. */ qcaspi_read_register(qca, SPI_REG_RDBUF_BYTE_AVA, &available); - netdev_dbg(net_dev, "qcaspi_receive: SPI_REG_RDBUF_BYTE_AVA: Value: %08x\n", + netdev_dbg(net_dev, "qcaspi_receive: SPI_REG_RDBUF_BYTE_AVA: Value: %04x\n", available); if (available > QCASPI_HW_BUF_LEN + QCASPI_HW_PKT_LEN) { @@ -476,7 +463,7 @@ qcaspi_flush_tx_ring(struct qcaspi *qca) * has been replaced by netif_tx_lock_bh() and so on. */ netif_tx_lock_bh(qca->net_dev); - for (i = 0; i < TX_RING_MAX_LEN; i++) { + for (i = 0; i < QCASPI_TX_RING_MAX_LEN; i++) { if (qca->txr.skb[i]) { dev_kfree_skb(qca->txr.skb[i]); qca->txr.skb[i] = NULL; @@ -687,7 +674,7 @@ static int qcaspi_netdev_open(struct net_device *dev) { struct qcaspi *qca = netdev_priv(dev); - int ret = 0; + struct task_struct *thread; if (!qca) return -EINVAL; @@ -697,23 +684,18 @@ qcaspi_netdev_open(struct net_device *dev) qca->sync = QCASPI_SYNC_UNKNOWN; qcafrm_fsm_init_spi(&qca->frm_handle); - qca->spi_thread = kthread_run((void *)qcaspi_spi_thread, - qca, "%s", dev->name); + thread = kthread_run((void *)qcaspi_spi_thread, + qca, "%s", dev->name); - if (IS_ERR(qca->spi_thread)) { + if (IS_ERR(thread)) { netdev_err(dev, "%s: unable to start kernel thread.\n", QCASPI_DRV_NAME); - return PTR_ERR(qca->spi_thread); + return PTR_ERR(thread); } - ret = request_irq(qca->spi_dev->irq, qcaspi_intr_handler, 0, - dev->name, qca); - if (ret) { - netdev_err(dev, "%s: unable to get IRQ %d (irqval=%d).\n", - QCASPI_DRV_NAME, qca->spi_dev->irq, ret); - kthread_stop(qca->spi_thread); - return ret; - } + qca->spi_thread = thread; + + enable_irq(qca->spi_dev->irq); /* SPI thread takes care of TX queue */ @@ -728,10 +710,12 @@ qcaspi_netdev_close(struct net_device *dev) netif_stop_queue(dev); qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0, wr_verify); - free_irq(qca->spi_dev->irq, qca); + disable_irq(qca->spi_dev->irq); - kthread_stop(qca->spi_thread); - qca->spi_thread = NULL; + if (qca->spi_thread) { + kthread_stop(qca->spi_thread); + qca->spi_thread = NULL; + } qcaspi_flush_tx_ring(qca); return 0; @@ -831,8 +815,8 @@ qcaspi_netdev_init(struct net_device *dev) qca->clkspeed = qcaspi_clkspeed; qca->burst_len = qcaspi_burst_len; qca->spi_thread = NULL; - qca->buffer_size = (dev->mtu + VLAN_ETH_HLEN + QCAFRM_HEADER_LEN + - QCAFRM_FOOTER_LEN + 4) * 4; + qca->buffer_size = (QCAFRM_MAX_MTU + VLAN_ETH_HLEN + QCAFRM_HEADER_LEN + + QCAFRM_FOOTER_LEN + QCASPI_HW_PKT_LEN) * QCASPI_RX_MAX_FRAMES; memset(&qca->stats, 0, sizeof(struct qcaspi_stats)); @@ -881,6 +865,8 @@ qcaspi_netdev_setup(struct net_device *dev) qcaspi_set_ethtool_ops(dev); dev->watchdog_timeo = QCASPI_TX_TIMEOUT; dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->needed_tailroom = ALIGN(QCAFRM_FOOTER_LEN + QCAFRM_MIN_LEN, 4); + dev->needed_headroom = ALIGN(QCAFRM_HEADER_LEN, 4); dev->tx_queue_len = 100; /* MTU range: 46 - 1500 */ @@ -891,7 +877,7 @@ qcaspi_netdev_setup(struct net_device *dev) memset(qca, 0, sizeof(struct qcaspi)); memset(&qca->txr, 0, sizeof(qca->txr)); - qca->txr.count = TX_RING_MAX_LEN; + qca->txr.count = QCASPI_TX_RING_MAX_LEN; } static const struct of_device_id qca_spi_of_match[] = { @@ -984,6 +970,15 @@ qca_spi_probe(struct spi_device *spi) spi_set_drvdata(spi, qcaspi_devs); + ret = devm_request_irq(&spi->dev, spi->irq, qcaspi_intr_handler, + IRQF_NO_AUTOEN, qca->net_dev->name, qca); + if (ret) { + dev_err(&spi->dev, "Unable to get IRQ %d (irqval=%d).\n", + spi->irq, ret); + free_netdev(qcaspi_devs); + return ret; + } + ret = of_get_ethdev_address(spi->dev.of_node, qca->net_dev); if (ret) { eth_hw_addr_random(qca->net_dev); @@ -998,8 +993,8 @@ qca_spi_probe(struct spi_device *spi) qcaspi_read_register(qca, SPI_REG_SIGNATURE, &signature); if (signature != QCASPI_GOOD_SIGNATURE) { - dev_err(&spi->dev, "Invalid signature (0x%04X)\n", - signature); + dev_err(&spi->dev, "Invalid signature (expected 0x%04x, read 0x%04x)\n", + QCASPI_GOOD_SIGNATURE, signature); free_netdev(qcaspi_devs); return -EFAULT; } @@ -1048,6 +1043,6 @@ module_spi_driver(qca_spi_driver); MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 SPI Driver"); MODULE_AUTHOR("Qualcomm Atheros Communications"); -MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); +MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(QCASPI_DRV_VERSION); diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h index 3067356106f0..d59cb2352cee 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.h +++ b/drivers/net/ethernet/qualcomm/qca_spi.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. * Copyright (c) 2014, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Qualcomm Atheros SPI register definition. @@ -39,8 +26,9 @@ #define QCASPI_GOOD_SIGNATURE 0xAA55 -#define TX_RING_MAX_LEN 10 -#define TX_RING_MIN_LEN 2 +#define QCASPI_TX_RING_MAX_LEN 10 +#define QCASPI_TX_RING_MIN_LEN 2 +#define QCASPI_RX_MAX_FRAMES 4 /* sync related constants */ #define QCASPI_SYNC_UNKNOWN 0 @@ -54,7 +42,7 @@ #define QCASPI_EVENT_CPUON 1 struct tx_ring { - struct sk_buff *skb[TX_RING_MAX_LEN]; + struct sk_buff *skb[QCASPI_TX_RING_MAX_LEN]; u16 head; u16 tail; u16 size; diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c index 223321897b96..321fd8d00730 100644 --- a/drivers/net/ethernet/qualcomm/qca_uart.c +++ b/drivers/net/ethernet/qualcomm/qca_uart.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. * Copyright (c) 2017, I2SE GmbH - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice appear - * in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* This module implements the Qualcomm Atheros UART protocol for @@ -410,6 +397,6 @@ module_serdev_device_driver(qca_uart_driver); MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 UART Driver"); MODULE_AUTHOR("Qualcomm Atheros Communications"); -MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); +MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(QCAUART_DRV_VERSION); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 5b69b9268c75..f3bea196a8f9 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -520,4 +520,5 @@ static void __exit rmnet_exit(void) module_init(rmnet_init) module_exit(rmnet_exit) MODULE_ALIAS_RTNL_LINK("rmnet"); +MODULE_DESCRIPTION("Qualcomm RmNet MAP driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/realtek/r8169.h b/drivers/net/ethernet/realtek/r8169.h index 81567fcf3957..4c043052198d 100644 --- a/drivers/net/ethernet/realtek/r8169.h +++ b/drivers/net/ethernet/realtek/r8169.h @@ -68,6 +68,7 @@ enum mac_version { /* support for RTL_GIGA_MAC_VER_60 has been removed */ RTL_GIGA_MAC_VER_61, RTL_GIGA_MAC_VER_63, + RTL_GIGA_MAC_VER_65, RTL_GIGA_MAC_NONE }; @@ -84,3 +85,6 @@ void r8169_get_led_name(struct rtl8169_private *tp, int idx, int rtl8168_get_led_mode(struct rtl8169_private *tp); int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val); void rtl8168_init_leds(struct net_device *ndev); +int rtl8125_get_led_mode(struct rtl8169_private *tp, int index); +int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode); +void rtl8125_init_leds(struct net_device *ndev); diff --git a/drivers/net/ethernet/realtek/r8169_leds.c b/drivers/net/ethernet/realtek/r8169_leds.c index 007d077edcad..7c5dc9d0df85 100644 --- a/drivers/net/ethernet/realtek/r8169_leds.c +++ b/drivers/net/ethernet/realtek/r8169_leds.c @@ -18,12 +18,14 @@ #define RTL8168_LED_CTRL_LINK_100 BIT(1) #define RTL8168_LED_CTRL_LINK_10 BIT(0) -#define RTL8168_NUM_LEDS 3 +#define RTL8125_LED_CTRL_ACT BIT(9) +#define RTL8125_LED_CTRL_LINK_2500 BIT(5) +#define RTL8125_LED_CTRL_LINK_1000 BIT(3) +#define RTL8125_LED_CTRL_LINK_100 BIT(1) +#define RTL8125_LED_CTRL_LINK_10 BIT(0) -#define RTL8168_SUPPORTED_MODES \ - (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK_100) | \ - BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_RX) | \ - BIT(TRIGGER_NETDEV_TX)) +#define RTL8168_NUM_LEDS 3 +#define RTL8125_NUM_LEDS 4 struct r8169_led_classdev { struct led_classdev led; @@ -33,28 +35,35 @@ struct r8169_led_classdev { #define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led) +static bool r8169_trigger_mode_is_valid(unsigned long flags) +{ + bool rx, tx; + + if (flags & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) + return false; + if (flags & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) + return false; + + rx = flags & BIT(TRIGGER_NETDEV_RX); + tx = flags & BIT(TRIGGER_NETDEV_TX); + + return rx == tx; +} + static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev, unsigned long flags) { struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); struct rtl8169_private *tp = netdev_priv(ldev->ndev); int shift = ldev->index * 4; - bool rx, tx; - - if (flags & ~RTL8168_SUPPORTED_MODES) - goto nosupp; - rx = flags & BIT(TRIGGER_NETDEV_RX); - tx = flags & BIT(TRIGGER_NETDEV_TX); - if (rx != tx) - goto nosupp; + if (!r8169_trigger_mode_is_valid(flags)) { + /* Switch LED off to indicate that mode isn't supported */ + rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0); + return -EOPNOTSUPP; + } return 0; - -nosupp: - /* Switch LED off to indicate that mode isn't supported */ - rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0); - return -EOPNOTSUPP; } static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev, @@ -129,7 +138,6 @@ static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev, r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE); led_cdev->name = led_name; - led_cdev->default_trigger = "netdev"; led_cdev->hw_control_trigger = "netdev"; led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; led_cdev->hw_control_is_supported = rtl8168_led_hw_control_is_supported; @@ -155,3 +163,102 @@ void rtl8168_init_leds(struct net_device *ndev) for (i = 0; i < RTL8168_NUM_LEDS; i++) rtl8168_setup_ldev(leds + i, ndev, i); } + +static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + + if (!r8169_trigger_mode_is_valid(flags)) { + /* Switch LED off to indicate that mode isn't supported */ + rtl8125_set_led_mode(tp, ldev->index, 0); + return -EOPNOTSUPP; + } + + return 0; +} + +static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + u16 mode = 0; + + if (flags & BIT(TRIGGER_NETDEV_LINK_10)) + mode |= RTL8125_LED_CTRL_LINK_10; + if (flags & BIT(TRIGGER_NETDEV_LINK_100)) + mode |= RTL8125_LED_CTRL_LINK_100; + if (flags & BIT(TRIGGER_NETDEV_LINK_1000)) + mode |= RTL8125_LED_CTRL_LINK_1000; + if (flags & BIT(TRIGGER_NETDEV_LINK_2500)) + mode |= RTL8125_LED_CTRL_LINK_2500; + if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX))) + mode |= RTL8125_LED_CTRL_ACT; + + return rtl8125_set_led_mode(tp, ldev->index, mode); +} + +static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev, + unsigned long *flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + int mode; + + mode = rtl8125_get_led_mode(tp, ldev->index); + if (mode < 0) + return mode; + + if (mode & RTL8125_LED_CTRL_LINK_10) + *flags |= BIT(TRIGGER_NETDEV_LINK_10); + if (mode & RTL8125_LED_CTRL_LINK_100) + *flags |= BIT(TRIGGER_NETDEV_LINK_100); + if (mode & RTL8125_LED_CTRL_LINK_1000) + *flags |= BIT(TRIGGER_NETDEV_LINK_1000); + if (mode & RTL8125_LED_CTRL_LINK_2500) + *flags |= BIT(TRIGGER_NETDEV_LINK_2500); + if (mode & RTL8125_LED_CTRL_ACT) + *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); + + return 0; +} + +static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev, + struct net_device *ndev, int index) +{ + struct rtl8169_private *tp = netdev_priv(ndev); + struct led_classdev *led_cdev = &ldev->led; + char led_name[LED_MAX_NAME_SIZE]; + + ldev->ndev = ndev; + ldev->index = index; + + r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE); + led_cdev->name = led_name; + led_cdev->hw_control_trigger = "netdev"; + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported; + led_cdev->hw_control_set = rtl8125_led_hw_control_set; + led_cdev->hw_control_get = rtl8125_led_hw_control_get; + led_cdev->hw_control_get_device = r8169_led_hw_control_get_device; + + /* ignore errors */ + devm_led_classdev_register(&ndev->dev, led_cdev); +} + +void rtl8125_init_leds(struct net_device *ndev) +{ + /* bind resource mgmt to netdev */ + struct device *dev = &ndev->dev; + struct r8169_led_classdev *leds; + int i; + + leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL); + if (!leds) + return; + + for (i = 0; i < RTL8125_NUM_LEDS; i++) + rtl8125_setup_led_ldev(leds + i, ndev, i); +} diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index dd73df6b17b0..0d2cbb32c870 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -55,6 +55,7 @@ #define FIRMWARE_8107E_2 "rtl_nic/rtl8107e-2.fw" #define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw" #define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw" +#define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw" #define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */ #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ @@ -136,6 +137,7 @@ static const struct { [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3}, /* reserve 62 for CFG_METHOD_4 in the vendor driver */ [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2}, + [RTL_GIGA_MAC_VER_65] = {"RTL8126A", FIRMWARE_8126A_2}, }; static const struct pci_device_id rtl8169_pci_tbl[] = { @@ -158,6 +160,7 @@ static const struct pci_device_id rtl8169_pci_tbl[] = { { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0024 }, { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 }, { PCI_VDEVICE(REALTEK, 0x8125) }, + { PCI_VDEVICE(REALTEK, 0x8126) }, { PCI_VDEVICE(REALTEK, 0x3000) }, {} }; @@ -327,13 +330,23 @@ enum rtl8168_registers { }; enum rtl8125_registers { + LEDSEL0 = 0x18, + INT_CFG0_8125 = 0x34, +#define INT_CFG0_ENABLE_8125 BIT(0) +#define INT_CFG0_CLKREQEN BIT(3) IntrMask_8125 = 0x38, IntrStatus_8125 = 0x3c, + INT_CFG1_8125 = 0x7a, + LEDSEL2 = 0x84, + LEDSEL1 = 0x86, TxPoll_8125 = 0x90, + LEDSEL3 = 0x96, MAC0_BKP = 0x19e0, EEE_TXIDLE_TIMER_8125 = 0x6048, }; +#define LEDSEL_MASK_8125 0x23f + #define RX_VLAN_INNER_8125 BIT(22) #define RX_VLAN_OUTER_8125 BIT(23) #define RX_VLAN_8125 (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125) @@ -606,6 +619,7 @@ struct rtl8169_private { struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */ struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */ u16 cp_cmd; + u16 tx_lpi_timer; u32 irq_mask; int irq; struct clk *clk; @@ -629,7 +643,6 @@ struct rtl8169_private { struct rtl8169_counters *counters; struct rtl8169_tc_offsets tc_offset; u32 saved_wolopts; - int eee_adv; const char *fw_name; struct rtl_fw *rtl_fw; @@ -663,6 +676,7 @@ MODULE_FIRMWARE(FIRMWARE_8168FP_3); MODULE_FIRMWARE(FIRMWARE_8107E_2); MODULE_FIRMWARE(FIRMWARE_8125A_3); MODULE_FIRMWARE(FIRMWARE_8125B_2); +MODULE_FIRMWARE(FIRMWARE_8126A_2); static inline struct device *tp_to_dev(struct rtl8169_private *tp) { @@ -824,6 +838,51 @@ int rtl8168_get_led_mode(struct rtl8169_private *tp) return ret; } +static int rtl8125_get_led_reg(int index) +{ + static const int led_regs[] = { LEDSEL0, LEDSEL1, LEDSEL2, LEDSEL3 }; + + return led_regs[index]; +} + +int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode) +{ + int reg = rtl8125_get_led_reg(index); + struct device *dev = tp_to_dev(tp); + int ret; + u16 val; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + mutex_lock(&tp->led_lock); + val = RTL_R16(tp, reg) & ~LEDSEL_MASK_8125; + RTL_W16(tp, reg, val | mode); + mutex_unlock(&tp->led_lock); + + pm_runtime_put_sync(dev); + + return 0; +} + +int rtl8125_get_led_mode(struct rtl8169_private *tp, int index) +{ + int reg = rtl8125_get_led_reg(index); + struct device *dev = tp_to_dev(tp); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = RTL_R16(tp, reg); + + pm_runtime_put_sync(dev); + + return ret; +} + void r8169_get_led_name(struct rtl8169_private *tp, int idx, char *buf, int buf_len) { @@ -1140,7 +1199,7 @@ static void rtl_writephy(struct rtl8169_private *tp, int location, int val) case RTL_GIGA_MAC_VER_31: r8168dp_2_mdio_write(tp, location, val); break; - case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65: r8168g_mdio_write(tp, location, val); break; default: @@ -1155,7 +1214,7 @@ static int rtl_readphy(struct rtl8169_private *tp, int location) case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: return r8168dp_2_mdio_read(tp, location); - case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65: return r8168g_mdio_read(tp, location); default: return r8169_mdio_read(tp, location); @@ -1341,7 +1400,7 @@ static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable) case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26: case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30: case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37: - case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65: if (enable) RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN); else @@ -1508,7 +1567,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) break; case RTL_GIGA_MAC_VER_34: case RTL_GIGA_MAC_VER_37: - case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65: if (wolopts) rtl_mod_config2(tp, 0, PME_SIGNAL); else @@ -1974,30 +2033,65 @@ static int rtl_set_coalesce(struct net_device *dev, return 0; } -static int rtl8169_get_eee(struct net_device *dev, struct ethtool_eee *data) +static void rtl_set_eee_txidle_timer(struct rtl8169_private *tp) +{ + unsigned int timer_val = READ_ONCE(tp->dev->mtu) + ETH_HLEN + 0x20; + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46: + case RTL_GIGA_MAC_VER_48: + tp->tx_lpi_timer = timer_val; + r8168_mac_ocp_write(tp, 0xe048, timer_val); + break; + case RTL_GIGA_MAC_VER_61: + case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_65: + tp->tx_lpi_timer = timer_val; + RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val); + break; + default: + break; + } +} + +static unsigned int r8169_get_tx_lpi_timer_us(struct rtl8169_private *tp) +{ + unsigned int speed = tp->phydev->speed; + unsigned int timer = tp->tx_lpi_timer; + + if (!timer || speed == SPEED_UNKNOWN) + return 0; + + /* tx_lpi_timer value is in bytes */ + return DIV_ROUND_CLOSEST(timer * BITS_PER_BYTE, speed); +} + +static int rtl8169_get_eee(struct net_device *dev, struct ethtool_keee *data) { struct rtl8169_private *tp = netdev_priv(dev); + int ret; if (!rtl_supports_eee(tp)) return -EOPNOTSUPP; - return phy_ethtool_get_eee(tp->phydev, data); + ret = phy_ethtool_get_eee(tp->phydev, data); + if (ret) + return ret; + + data->tx_lpi_timer = r8169_get_tx_lpi_timer_us(tp); + data->tx_lpi_enabled = data->tx_lpi_timer ? data->eee_enabled : false; + + return 0; } -static int rtl8169_set_eee(struct net_device *dev, struct ethtool_eee *data) +static int rtl8169_set_eee(struct net_device *dev, struct ethtool_keee *data) { struct rtl8169_private *tp = netdev_priv(dev); - int ret; if (!rtl_supports_eee(tp)) return -EOPNOTSUPP; - ret = phy_ethtool_set_eee(tp->phydev, data); - - if (!ret) - tp->eee_adv = phy_read_mmd(dev->phydev, MDIO_MMD_AN, - MDIO_AN_EEE_ADV); - return ret; + return phy_ethtool_set_eee(tp->phydev, data); } static void rtl8169_get_ringparam(struct net_device *dev, @@ -2062,21 +2156,6 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .set_pauseparam = rtl8169_set_pauseparam, }; -static void rtl_enable_eee(struct rtl8169_private *tp) -{ - struct phy_device *phydev = tp->phydev; - int adv; - - /* respect EEE advertisement the user may have set */ - if (tp->eee_adv >= 0) - adv = tp->eee_adv; - else - adv = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); - - if (adv >= 0) - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); -} - static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) { /* @@ -2095,6 +2174,9 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) u16 val; enum mac_version ver; } mac_info[] = { + /* 8126A family. */ + { 0x7cf, 0x649, RTL_GIGA_MAC_VER_65 }, + /* 8125B family. */ { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63 }, @@ -2250,14 +2332,8 @@ static void rtl8125a_config_eee_mac(struct rtl8169_private *tp) r8168_mac_ocp_modify(tp, 0xeb62, 0, BIT(2) | BIT(1)); } -static void rtl8125_set_eee_txidle_timer(struct rtl8169_private *tp) -{ - RTL_W16(tp, EEE_TXIDLE_TIMER_8125, tp->dev->mtu + ETH_HLEN + 0x20); -} - static void rtl8125b_config_eee_mac(struct rtl8169_private *tp) { - rtl8125_set_eee_txidle_timer(tp); r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0)); } @@ -2313,9 +2389,6 @@ static void rtl8169_init_phy(struct rtl8169_private *tp) /* We may have called phy_speed_down before */ phy_speed_up(tp->phydev); - if (rtl_supports_eee(tp)) - rtl_enable_eee(tp); - genphy_soft_reset(tp->phydev); } @@ -2368,6 +2441,7 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp) RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST); break; case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_65: RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST | RX_PAUSE_SLOT_ON); break; @@ -2554,7 +2628,7 @@ static void rtl_wait_txrx_fifo_empty(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61: rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); break; - case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_65: RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42); @@ -2797,7 +2871,7 @@ static void rtl_enable_exit_l1(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38: rtl_eri_set_bits(tp, 0xd4, 0x0c00); break; - case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65: r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80); break; default: @@ -2811,7 +2885,7 @@ static void rtl_disable_exit_l1(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: rtl_eri_clear_bits(tp, 0xd4, 0x1f00); break; - case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65: r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0); break; default: @@ -2821,6 +2895,8 @@ static void rtl_disable_exit_l1(struct rtl8169_private *tp) static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) { + u8 val8; + if (tp->mac_version < RTL_GIGA_MAC_VER_32) return; @@ -2834,11 +2910,19 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) return; rtl_mod_config5(tp, 0, ASPM_en); - rtl_mod_config2(tp, 0, ClkReqEn); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_65: + val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; + default: + rtl_mod_config2(tp, 0, ClkReqEn); + break; + } switch (tp->mac_version) { case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: - case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65: /* reset ephy tx/rx disable timer */ r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0); /* chip can trigger L1.2 */ @@ -2850,14 +2934,22 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) } else { switch (tp->mac_version) { case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: - case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65: r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0); break; default: break; } - rtl_mod_config2(tp, ClkReqEn, 0); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_65: + val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; + default: + rtl_mod_config2(tp, ClkReqEn, 0); + break; + } rtl_mod_config5(tp, ASPM_en, 0); } } @@ -3570,10 +3662,15 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp) /* disable new tx descriptor format */ r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000); - if (tp->mac_version == RTL_GIGA_MAC_VER_63) + if (tp->mac_version == RTL_GIGA_MAC_VER_65) + RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02); + + if (tp->mac_version == RTL_GIGA_MAC_VER_65) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400); + else if (tp->mac_version == RTL_GIGA_MAC_VER_63) r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200); else - r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400); + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0300); if (tp->mac_version == RTL_GIGA_MAC_VER_63) r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0000); @@ -3586,6 +3683,10 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp) r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0030); r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000); r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001); + if (tp->mac_version == RTL_GIGA_MAC_VER_65) + r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000); + else + r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000); r8168_mac_ocp_modify(tp, 0xe0c0, 0x4f0f, 0x4403); r8168_mac_ocp_modify(tp, 0xe052, 0x0080, 0x0068); r8168_mac_ocp_modify(tp, 0xd430, 0x0fff, 0x047f); @@ -3600,10 +3701,10 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp) rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10); - if (tp->mac_version == RTL_GIGA_MAC_VER_63) - rtl8125b_config_eee_mac(tp); - else + if (tp->mac_version == RTL_GIGA_MAC_VER_61) rtl8125a_config_eee_mac(tp); + else + rtl8125b_config_eee_mac(tp); rtl_disable_rxdvgate(tp); } @@ -3647,6 +3748,12 @@ static void rtl_hw_start_8125b(struct rtl8169_private *tp) rtl_hw_start_8125_common(tp); } +static void rtl_hw_start_8126a(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + rtl_hw_start_8125_common(tp); +} + static void rtl_hw_config(struct rtl8169_private *tp) { static const rtl_generic_fct hw_configs[] = { @@ -3689,6 +3796,7 @@ static void rtl_hw_config(struct rtl8169_private *tp) [RTL_GIGA_MAC_VER_53] = rtl_hw_start_8117, [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8126a, }; if (hw_configs[tp->mac_version]) @@ -3699,9 +3807,23 @@ static void rtl_hw_start_8125(struct rtl8169_private *tp) { int i; + RTL_W8(tp, INT_CFG0_8125, 0x00); + /* disable interrupt coalescing */ - for (i = 0xa00; i < 0xb00; i += 4) - RTL_W32(tp, i, 0); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_61: + for (i = 0xa00; i < 0xb00; i += 4) + RTL_W32(tp, i, 0); + break; + case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_65: + for (i = 0xa00; i < 0xa80; i += 4) + RTL_W32(tp, i, 0); + RTL_W16(tp, INT_CFG1_8125, 0x0000); + break; + default: + break; + } rtl_hw_config(tp); } @@ -3744,6 +3866,8 @@ static void rtl_hw_start(struct rtl8169_private *tp) rtl_hw_aspm_clkreq_enable(tp, false); RTL_W16(tp, CPlusCmd, tp->cp_cmd); + rtl_set_eee_txidle_timer(tp); + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) rtl_hw_start_8169(tp); else if (rtl_is_8125(tp)) @@ -3777,15 +3901,7 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu) dev->mtu = new_mtu; netdev_update_features(dev); rtl_jumbo_config(tp); - - switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_61: - case RTL_GIGA_MAC_VER_63: - rtl8125_set_eee_txidle_timer(tp); - break; - default: - break; - } + rtl_set_eee_txidle_timer(tp); return 0; } @@ -3929,7 +4045,7 @@ static void rtl8169_cleanup(struct rtl8169_private *tp) RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666); break; - case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65: rtl_enable_rxdvgate(tp); fsleep(2000); break; @@ -4080,8 +4196,7 @@ static unsigned int rtl_quirk_packet_padto(struct rtl8169_private *tp, switch (tp->mac_version) { case RTL_GIGA_MAC_VER_34: - case RTL_GIGA_MAC_VER_61: - case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65: padto = max_t(unsigned int, padto, ETH_ZLEN); break; default: @@ -5058,7 +5173,8 @@ static int r8169_mdio_register(struct rtl8169_private *tp) } tp->phydev->mac_managed_pm = true; - + if (rtl_supports_eee(tp)) + phy_advertise_eee_all(tp->phydev); phy_support_asym_pause(tp->phydev); /* PHY will be woken up in rtl_open() */ @@ -5108,7 +5224,7 @@ static void rtl_hw_initialize(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48: rtl_hw_init_8168g(tp); break; - case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65: rtl_hw_init_8125(tp); break; default: @@ -5193,7 +5309,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->dev = dev; tp->pci_dev = pdev; tp->supports_gmii = ent->driver_data == RTL_CFG_NO_GBIT ? 0 : 1; - tp->eee_adv = -1; tp->ocp_base = OCP_STD_PHY_BASE; raw_spin_lock_init(&tp->cfg9346_usage_lock); @@ -5201,11 +5316,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) raw_spin_lock_init(&tp->mac_ocp_lock); mutex_init(&tp->led_lock); - dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev, - struct pcpu_sw_netstats); - if (!dev->tstats) - return -ENOMEM; - /* Get the *optional* external "ether_clk" used on some boards */ tp->clk = devm_clk_get_optional_enabled(&pdev->dev, "ether_clk"); if (IS_ERR(tp->clk)) @@ -5320,6 +5430,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->hw_features |= NETIF_F_RXALL; dev->hw_features |= NETIF_F_RXFCS; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + netdev_sw_irq_coalesce_default_on(dev); /* configure chip for default features */ @@ -5356,10 +5468,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - if (IS_ENABLED(CONFIG_R8169_LEDS) && - tp->mac_version > RTL_GIGA_MAC_VER_06 && - tp->mac_version < RTL_GIGA_MAC_VER_61) - rtl8168_init_leds(dev); + if (IS_ENABLED(CONFIG_R8169_LEDS)) { + if (rtl_is_8125(tp)) + rtl8125_init_leds(dev); + else if (tp->mac_version > RTL_GIGA_MAC_VER_06) + rtl8168_init_leds(dev); + } netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n", rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq); diff --git a/drivers/net/ethernet/realtek/r8169_phy_config.c b/drivers/net/ethernet/realtek/r8169_phy_config.c index b50f16786c24..1f74317beb88 100644 --- a/drivers/net/ethernet/realtek/r8169_phy_config.c +++ b/drivers/net/ethernet/realtek/r8169_phy_config.c @@ -1102,6 +1102,12 @@ static void rtl8125b_hw_phy_config(struct rtl8169_private *tp, rtl8125b_config_eee_phy(phydev); } +static void rtl8126a_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); +} + void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, enum mac_version ver) { @@ -1152,6 +1158,7 @@ void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, [RTL_GIGA_MAC_VER_53] = rtl8117_hw_phy_config, [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + [RTL_GIGA_MAC_VER_65] = rtl8126a_hw_phy_config, }; if (phy_configs[ver]) diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig index d6136fe5c206..b03fae7a0f72 100644 --- a/drivers/net/ethernet/renesas/Kconfig +++ b/drivers/net/ethernet/renesas/Kconfig @@ -34,6 +34,7 @@ config RAVB select MII select MDIO_BITBANG select PHYLIB + select RESET_CONTROLLER help Renesas Ethernet AVB device driver. diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index e0f8276cffed..35e642fc4b2a 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -205,7 +205,11 @@ enum ravb_reg { TLFRCR = 0x0758, RFCR = 0x0760, MAFCR = 0x0778, - CSR0 = 0x0800, /* RZ/G2L only */ + + /* TOE registers (RZ/G2L only) */ + CSR0 = 0x0800, + CSR1 = 0x0804, + CSR2 = 0x0808, }; @@ -978,6 +982,34 @@ enum CSR0_BIT { CSR0_RPE = 0x00000020, }; +enum CSR1_BIT { + CSR1_TIP4 = 0x00000001, + CSR1_TTCP4 = 0x00000010, + CSR1_TUDP4 = 0x00000020, + CSR1_TICMP4 = 0x00000040, + CSR1_TTCP6 = 0x00100000, + CSR1_TUDP6 = 0x00200000, + CSR1_TICMP6 = 0x00400000, + CSR1_THOP = 0x01000000, + CSR1_TROUT = 0x02000000, + CSR1_TAHD = 0x04000000, + CSR1_TDHD = 0x08000000, +}; + +enum CSR2_BIT { + CSR2_RIP4 = 0x00000001, + CSR2_RTCP4 = 0x00000010, + CSR2_RUDP4 = 0x00000020, + CSR2_RICMP4 = 0x00000040, + CSR2_RTCP6 = 0x00100000, + CSR2_RUDP6 = 0x00200000, + CSR2_RICMP6 = 0x00400000, + CSR2_RHOP = 0x01000000, + CSR2_RROUT = 0x02000000, + CSR2_RAHD = 0x04000000, + CSR2_RDHD = 0x08000000, +}; + #define DBAT_ENTRY_NUM 22 #define RX_QUEUE_OFFSET 4 #define NUM_RX_QUEUE 2 @@ -1089,10 +1121,6 @@ struct ravb_private { int msg_enable; int speed; int emac_irq; - int erra_irq; - int mgmta_irq; - int rx_irqs[NUM_RX_QUEUE]; - int tx_irqs[NUM_TX_QUEUE]; unsigned no_avb_link:1; unsigned avb_link_active_low:1; @@ -1106,6 +1134,8 @@ struct ravb_private { const struct ravb_hw_info *info; struct reset_control *rstc; + + u32 gti_tiv; }; static inline u32 ravb_read(struct net_device *ndev, enum ravb_reg reg) diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 0e3731f50fc2..f9fb772b05c7 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -29,6 +29,7 @@ #include <linux/spinlock.h> #include <linux/reset.h> #include <linux/math64.h> +#include <net/ip.h> #include "ravb.h" @@ -38,16 +39,6 @@ NETIF_MSG_RX_ERR | \ NETIF_MSG_TX_ERR) -static const char *ravb_rx_irqs[NUM_RX_QUEUE] = { - "ch0", /* RAVB_BE */ - "ch1", /* RAVB_NC */ -}; - -static const char *ravb_tx_irqs[NUM_TX_QUEUE] = { - "ch18", /* RAVB_BE */ - "ch19", /* RAVB_NC */ -}; - void ravb_modify(struct net_device *ndev, enum ravb_reg reg, u32 clear, u32 set) { @@ -96,13 +87,13 @@ static void ravb_set_rate_gbeth(struct net_device *ndev) struct ravb_private *priv = netdev_priv(ndev); switch (priv->speed) { - case 10: /* 10BASE */ + case 10: /* 10BASE */ ravb_write(ndev, GBETH_GECMR_SPEED_10, GECMR); break; - case 100: /* 100BASE */ + case 100: /* 100BASE */ ravb_write(ndev, GBETH_GECMR_SPEED_100, GECMR); break; - case 1000: /* 1000BASE */ + case 1000: /* 1000BASE */ ravb_write(ndev, GBETH_GECMR_SPEED_1000, GECMR); break; } @@ -522,6 +513,36 @@ error: return -ENOMEM; } +static void ravb_csum_init_gbeth(struct net_device *ndev) +{ + bool tx_enable = ndev->features & NETIF_F_HW_CSUM; + bool rx_enable = ndev->features & NETIF_F_RXCSUM; + + if (!(tx_enable || rx_enable)) + goto done; + + ravb_write(ndev, 0, CSR0); + if (ravb_wait(ndev, CSR0, CSR0_TPE | CSR0_RPE, 0)) { + netdev_err(ndev, "Timeout enabling hardware checksum\n"); + + if (tx_enable) + ndev->features &= ~NETIF_F_HW_CSUM; + + if (rx_enable) + ndev->features &= ~NETIF_F_RXCSUM; + } else { + if (tx_enable) + ravb_write(ndev, CSR1_TIP4 | CSR1_TTCP4 | CSR1_TUDP4, CSR1); + + if (rx_enable) + ravb_write(ndev, CSR2_RIP4 | CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4, + CSR2); + } + +done: + ravb_write(ndev, CSR0_TPE | CSR0_RPE, CSR0); +} + static void ravb_emac_init_gbeth(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); @@ -553,7 +574,8 @@ static void ravb_emac_init_gbeth(struct net_device *ndev) /* E-MAC status register clear */ ravb_write(ndev, ECSR_ICD | ECSR_LCHNG | ECSR_PFRI, ECSR); - ravb_write(ndev, CSR0_TPE | CSR0_RPE, CSR0); + + ravb_csum_init_gbeth(ndev); /* E-MAC interrupt enable register */ ravb_write(ndev, ECSIPR_ICDIP, ECSIPR); @@ -734,6 +756,30 @@ static void ravb_get_tx_tstamp(struct net_device *ndev) } } +static void ravb_rx_csum_gbeth(struct sk_buff *skb) +{ + __wsum csum_ip_hdr, csum_proto; + u8 *hw_csum; + + /* The hardware checksum status is contained in sizeof(__sum16) * 2 = 4 + * bytes appended to packet data. First 2 bytes is ip header checksum + * and last 2 bytes is protocol checksum. + */ + if (unlikely(skb->len < sizeof(__sum16) * 2)) + return; + + hw_csum = skb_tail_pointer(skb) - sizeof(__sum16); + csum_proto = csum_unfold((__force __sum16)get_unaligned_le16(hw_csum)); + + hw_csum -= sizeof(__sum16); + csum_ip_hdr = csum_unfold((__force __sum16)get_unaligned_le16(hw_csum)); + skb_trim(skb, skb->len - 2 * sizeof(__sum16)); + + /* TODO: IPV6 Rx checksum */ + if (skb->protocol == htons(ETH_P_IP) && !csum_ip_hdr && !csum_proto) + skb->ip_summed = CHECKSUM_UNNECESSARY; +} + static void ravb_rx_csum(struct sk_buff *skb) { u8 *hw_csum; @@ -772,29 +818,25 @@ static bool ravb_rx_gbeth(struct net_device *ndev, int *quota, int q) struct ravb_rx_desc *desc; struct sk_buff *skb; dma_addr_t dma_addr; + int rx_packets = 0; u8 desc_status; - int boguscnt; u16 pkt_len; u8 die_dt; int entry; int limit; + int i; entry = priv->cur_rx[q] % priv->num_rx_ring[q]; - boguscnt = priv->dirty_rx[q] + priv->num_rx_ring[q] - priv->cur_rx[q]; + limit = priv->dirty_rx[q] + priv->num_rx_ring[q] - priv->cur_rx[q]; stats = &priv->stats[q]; - boguscnt = min(boguscnt, *quota); - limit = boguscnt; desc = &priv->gbeth_rx_ring[entry]; - while (desc->die_dt != DT_FEMPTY) { + for (i = 0; i < limit && rx_packets < *quota && desc->die_dt != DT_FEMPTY; i++) { /* Descriptor type must be checked before all other reads */ dma_rmb(); desc_status = desc->msc; pkt_len = le16_to_cpu(desc->ds_cc) & RX_DS; - if (--boguscnt < 0) - break; - /* We use 0-byte descriptors to mark the DMA mapping errors */ if (!pkt_len) continue; @@ -819,8 +861,10 @@ static bool ravb_rx_gbeth(struct net_device *ndev, int *quota, int q) skb = ravb_get_skb_gbeth(ndev, entry, desc); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, ndev); + if (ndev->features & NETIF_F_RXCSUM) + ravb_rx_csum_gbeth(skb); napi_gro_receive(&priv->napi[q], skb); - stats->rx_packets++; + rx_packets++; stats->rx_bytes += pkt_len; break; case DT_FSTART: @@ -846,9 +890,11 @@ static bool ravb_rx_gbeth(struct net_device *ndev, int *quota, int q) dev_kfree_skb(skb); priv->rx_1st_skb->protocol = eth_type_trans(priv->rx_1st_skb, ndev); + if (ndev->features & NETIF_F_RXCSUM) + ravb_rx_csum_gbeth(skb); napi_gro_receive(&priv->napi[q], priv->rx_1st_skb); - stats->rx_packets++; + rx_packets++; stats->rx_bytes += pkt_len; break; } @@ -887,9 +933,9 @@ static bool ravb_rx_gbeth(struct net_device *ndev, int *quota, int q) desc->die_dt = DT_FEMPTY; } - *quota -= limit - (++boguscnt); - - return boguscnt <= 0; + stats->rx_packets += rx_packets; + *quota -= rx_packets; + return *quota == 0; } /* Packet receive function for Ethernet AVB */ @@ -1092,11 +1138,23 @@ static irqreturn_t ravb_emac_interrupt(int irq, void *dev_id) { struct net_device *ndev = dev_id; struct ravb_private *priv = netdev_priv(ndev); + struct device *dev = &priv->pdev->dev; + irqreturn_t result = IRQ_HANDLED; + + pm_runtime_get_noresume(dev); + + if (unlikely(!pm_runtime_active(dev))) { + result = IRQ_NONE; + goto out_rpm_put; + } spin_lock(&priv->lock); ravb_emac_interrupt_unlocked(ndev); spin_unlock(&priv->lock); - return IRQ_HANDLED; + +out_rpm_put: + pm_runtime_put_noidle(dev); + return result; } /* Error interrupt handler */ @@ -1176,9 +1234,15 @@ static irqreturn_t ravb_interrupt(int irq, void *dev_id) struct net_device *ndev = dev_id; struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; + struct device *dev = &priv->pdev->dev; irqreturn_t result = IRQ_NONE; u32 iss; + pm_runtime_get_noresume(dev); + + if (unlikely(!pm_runtime_active(dev))) + goto out_rpm_put; + spin_lock(&priv->lock); /* Get interrupt status */ iss = ravb_read(ndev, ISS); @@ -1222,6 +1286,9 @@ static irqreturn_t ravb_interrupt(int irq, void *dev_id) } spin_unlock(&priv->lock); + +out_rpm_put: + pm_runtime_put_noidle(dev); return result; } @@ -1230,9 +1297,15 @@ static irqreturn_t ravb_multi_interrupt(int irq, void *dev_id) { struct net_device *ndev = dev_id; struct ravb_private *priv = netdev_priv(ndev); + struct device *dev = &priv->pdev->dev; irqreturn_t result = IRQ_NONE; u32 iss; + pm_runtime_get_noresume(dev); + + if (unlikely(!pm_runtime_active(dev))) + goto out_rpm_put; + spin_lock(&priv->lock); /* Get interrupt status */ iss = ravb_read(ndev, ISS); @@ -1254,6 +1327,9 @@ static irqreturn_t ravb_multi_interrupt(int irq, void *dev_id) } spin_unlock(&priv->lock); + +out_rpm_put: + pm_runtime_put_noidle(dev); return result; } @@ -1261,8 +1337,14 @@ static irqreturn_t ravb_dma_interrupt(int irq, void *dev_id, int q) { struct net_device *ndev = dev_id; struct ravb_private *priv = netdev_priv(ndev); + struct device *dev = &priv->pdev->dev; irqreturn_t result = IRQ_NONE; + pm_runtime_get_noresume(dev); + + if (unlikely(!pm_runtime_active(dev))) + goto out_rpm_put; + spin_lock(&priv->lock); /* Network control/Best effort queue RX/TX */ @@ -1270,6 +1352,9 @@ static irqreturn_t ravb_dma_interrupt(int irq, void *dev_id, int q) result = IRQ_HANDLED; spin_unlock(&priv->lock); + +out_rpm_put: + pm_runtime_put_noidle(dev); return result; } @@ -1288,25 +1373,16 @@ static int ravb_poll(struct napi_struct *napi, int budget) struct net_device *ndev = napi->dev; struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; - bool gptp = info->gptp || info->ccc_gac; - struct ravb_rx_desc *desc; unsigned long flags; int q = napi - priv->napi; int mask = BIT(q); int quota = budget; - unsigned int entry; - if (!gptp) { - entry = priv->cur_rx[q] % priv->num_rx_ring[q]; - desc = &priv->gbeth_rx_ring[entry]; - } /* Processing RX Descriptor Ring */ /* Clear RX interrupt */ ravb_write(ndev, ~(mask | RIS0_RESERVED), RIS0); - if (gptp || desc->die_dt != DT_FEMPTY) { - if (ravb_rx(ndev, "a, q)) - goto out; - } + if (ravb_rx(ndev, "a, q)) + goto out; /* Processing TX Descriptor Ring */ spin_lock_irqsave(&priv->lock, flags); @@ -1736,89 +1812,159 @@ static const struct ethtool_ops ravb_ethtool_ops = { .set_wol = ravb_set_wol, }; -static inline int ravb_hook_irq(unsigned int irq, irq_handler_t handler, - struct net_device *ndev, struct device *dev, - const char *ch) +static int ravb_set_config_mode(struct net_device *ndev) { - char *name; + struct ravb_private *priv = netdev_priv(ndev); + const struct ravb_hw_info *info = priv->info; int error; - name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", ndev->name, ch); - if (!name) - return -ENOMEM; - error = request_irq(irq, handler, 0, name, ndev); - if (error) - netdev_err(ndev, "cannot request IRQ %s\n", name); + if (info->gptp) { + error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); + if (error) + return error; + /* Set CSEL value */ + ravb_modify(ndev, CCC, CCC_CSEL, CCC_CSEL_HPB); + } else if (info->ccc_gac) { + error = ravb_set_opmode(ndev, CCC_OPC_CONFIG | CCC_GAC | CCC_CSEL_HPB); + } else { + error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); + } return error; } +static void ravb_set_gti(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + const struct ravb_hw_info *info = priv->info; + + if (!(info->gptp || info->ccc_gac)) + return; + + ravb_write(ndev, priv->gti_tiv, GTI); + + /* Request GTI loading */ + ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI); +} + +static int ravb_compute_gti(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + const struct ravb_hw_info *info = priv->info; + struct device *dev = ndev->dev.parent; + unsigned long rate; + u64 inc; + + if (!(info->gptp || info->ccc_gac)) + return 0; + + if (info->gptp_ref_clk) + rate = clk_get_rate(priv->gptp_clk); + else + rate = clk_get_rate(priv->clk); + if (!rate) + return -EINVAL; + + inc = div64_ul(1000000000ULL << 20, rate); + + if (inc < GTI_TIV_MIN || inc > GTI_TIV_MAX) { + dev_err(dev, "gti.tiv increment 0x%llx is outside the range 0x%x - 0x%x\n", + inc, GTI_TIV_MIN, GTI_TIV_MAX); + return -EINVAL; + } + priv->gti_tiv = inc; + + return 0; +} + +/* Set tx and rx clock internal delay modes */ +static void ravb_parse_delay_mode(struct device_node *np, struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + bool explicit_delay = false; + u32 delay; + + if (!priv->info->internal_delay) + return; + + if (!of_property_read_u32(np, "rx-internal-delay-ps", &delay)) { + /* Valid values are 0 and 1800, according to DT bindings */ + priv->rxcidm = !!delay; + explicit_delay = true; + } + if (!of_property_read_u32(np, "tx-internal-delay-ps", &delay)) { + /* Valid values are 0 and 2000, according to DT bindings */ + priv->txcidm = !!delay; + explicit_delay = true; + } + + if (explicit_delay) + return; + + /* Fall back to legacy rgmii-*id behavior */ + if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + priv->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID) { + priv->rxcidm = 1; + priv->rgmii_override = 1; + } + + if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + priv->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) { + priv->txcidm = 1; + priv->rgmii_override = 1; + } +} + +static void ravb_set_delay_mode(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + u32 set = 0; + + if (!priv->info->internal_delay) + return; + + if (priv->rxcidm) + set |= APSR_RDM; + if (priv->txcidm) + set |= APSR_TDM; + ravb_modify(ndev, APSR, APSR_RDM | APSR_TDM, set); +} + /* Network device open function for Ethernet AVB */ static int ravb_open(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; - struct platform_device *pdev = priv->pdev; - struct device *dev = &pdev->dev; + struct device *dev = &priv->pdev->dev; int error; napi_enable(&priv->napi[RAVB_BE]); if (info->nc_queues) napi_enable(&priv->napi[RAVB_NC]); - if (!info->multi_irqs) { - error = request_irq(ndev->irq, ravb_interrupt, IRQF_SHARED, - ndev->name, ndev); - if (error) { - netdev_err(ndev, "cannot request IRQ\n"); - goto out_napi_off; - } - } else { - error = ravb_hook_irq(ndev->irq, ravb_multi_interrupt, ndev, - dev, "ch22:multi"); - if (error) - goto out_napi_off; - error = ravb_hook_irq(priv->emac_irq, ravb_emac_interrupt, ndev, - dev, "ch24:emac"); - if (error) - goto out_free_irq; - error = ravb_hook_irq(priv->rx_irqs[RAVB_BE], ravb_be_interrupt, - ndev, dev, "ch0:rx_be"); - if (error) - goto out_free_irq_emac; - error = ravb_hook_irq(priv->tx_irqs[RAVB_BE], ravb_be_interrupt, - ndev, dev, "ch18:tx_be"); - if (error) - goto out_free_irq_be_rx; - error = ravb_hook_irq(priv->rx_irqs[RAVB_NC], ravb_nc_interrupt, - ndev, dev, "ch1:rx_nc"); - if (error) - goto out_free_irq_be_tx; - error = ravb_hook_irq(priv->tx_irqs[RAVB_NC], ravb_nc_interrupt, - ndev, dev, "ch19:tx_nc"); - if (error) - goto out_free_irq_nc_rx; - - if (info->err_mgmt_irqs) { - error = ravb_hook_irq(priv->erra_irq, ravb_multi_interrupt, - ndev, dev, "err_a"); - if (error) - goto out_free_irq_nc_tx; - error = ravb_hook_irq(priv->mgmta_irq, ravb_multi_interrupt, - ndev, dev, "mgmt_a"); - if (error) - goto out_free_irq_erra; - } - } + error = pm_runtime_resume_and_get(dev); + if (error < 0) + goto out_napi_off; + + /* Set AVB config mode */ + error = ravb_set_config_mode(ndev); + if (error) + goto out_rpm_put; + + ravb_set_delay_mode(ndev); + ravb_write(ndev, priv->desc_bat_dma, DBAT); /* Device init */ error = ravb_dmac_init(ndev); if (error) - goto out_free_irq_mgmta; + goto out_set_reset; + ravb_emac_init(ndev); + ravb_set_gti(ndev); + /* Initialise PTP Clock driver */ - if (info->gptp) + if (info->gptp || info->ccc_gac) ravb_ptp_init(ndev, priv->pdev); /* PHY control start */ @@ -1832,29 +1978,14 @@ static int ravb_open(struct net_device *ndev) out_ptp_stop: /* Stop PTP Clock driver */ - if (info->gptp) + if (info->gptp || info->ccc_gac) ravb_ptp_stop(ndev); ravb_stop_dma(ndev); -out_free_irq_mgmta: - if (!info->multi_irqs) - goto out_free_irq; - if (info->err_mgmt_irqs) - free_irq(priv->mgmta_irq, ndev); -out_free_irq_erra: - if (info->err_mgmt_irqs) - free_irq(priv->erra_irq, ndev); -out_free_irq_nc_tx: - free_irq(priv->tx_irqs[RAVB_NC], ndev); -out_free_irq_nc_rx: - free_irq(priv->rx_irqs[RAVB_NC], ndev); -out_free_irq_be_tx: - free_irq(priv->tx_irqs[RAVB_BE], ndev); -out_free_irq_be_rx: - free_irq(priv->rx_irqs[RAVB_BE], ndev); -out_free_irq_emac: - free_irq(priv->emac_irq, ndev); -out_free_irq: - free_irq(ndev->irq, ndev); +out_set_reset: + ravb_set_opmode(ndev, CCC_OPC_RESET); +out_rpm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); out_napi_off: if (info->nc_queues) napi_disable(&priv->napi[RAVB_NC]); @@ -1939,6 +2070,36 @@ out_unlock: rtnl_unlock(); } +static bool ravb_can_tx_csum_gbeth(struct sk_buff *skb) +{ + struct iphdr *ip = ip_hdr(skb); + + /* TODO: Need to add support for VLAN tag 802.1Q */ + if (skb_vlan_tag_present(skb)) + return false; + + /* TODO: Need to add hardware checksum for IPv6 */ + if (skb->protocol != htons(ETH_P_IP)) + return false; + + switch (ip->protocol) { + case IPPROTO_TCP: + break; + case IPPROTO_UDP: + /* If the checksum value in the UDP header field is 0, TOE does + * not calculate checksum for UDP part of this frame as it is + * optional function as per standards. + */ + if (udp_hdr(skb)->check == 0) + return false; + break; + default: + return false; + } + + return true; +} + /* Packet transmit function for Ethernet AVB */ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) { @@ -1954,6 +2115,9 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) u32 entry; u32 len; + if (skb->ip_summed == CHECKSUM_PARTIAL && !ravb_can_tx_csum_gbeth(skb)) + skb_checksum_help(skb); + spin_lock_irqsave(&priv->lock, flags); if (priv->cur_tx[q] - priv->dirty_tx[q] > (priv->num_tx_ring[q] - 1) * num_tx_desc) { @@ -2088,8 +2252,15 @@ static struct net_device_stats *ravb_get_stats(struct net_device *ndev) struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; struct net_device_stats *nstats, *stats0, *stats1; + struct device *dev = &priv->pdev->dev; nstats = &ndev->stats; + + pm_runtime_get_noresume(dev); + + if (!pm_runtime_active(dev)) + goto out_rpm_put; + stats0 = &priv->stats[RAVB_BE]; if (info->tx_counters) { @@ -2131,6 +2302,8 @@ static struct net_device_stats *ravb_get_stats(struct net_device *ndev) nstats->rx_over_errors += stats1->rx_over_errors; } +out_rpm_put: + pm_runtime_put_noidle(dev); return nstats; } @@ -2153,6 +2326,8 @@ static int ravb_close(struct net_device *ndev) struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; struct ravb_tstamp_skb *ts_skb, *ts_skb2; + struct device *dev = &priv->pdev->dev; + int error; netif_tx_stop_all_queues(ndev); @@ -2161,8 +2336,16 @@ static int ravb_close(struct net_device *ndev) ravb_write(ndev, 0, RIC2); ravb_write(ndev, 0, TIC); + /* PHY disconnect */ + if (ndev->phydev) { + phy_stop(ndev->phydev); + phy_disconnect(ndev->phydev); + if (of_phy_is_fixed_link(np)) + of_phy_deregister_fixed_link(np); + } + /* Stop PTP Clock driver */ - if (info->gptp) + if (info->gptp || info->ccc_gac) ravb_ptp_stop(ndev); /* Set the config mode to stop the AVB-DMAC's processes */ @@ -2179,29 +2362,8 @@ static int ravb_close(struct net_device *ndev) } } - /* PHY disconnect */ - if (ndev->phydev) { - phy_stop(ndev->phydev); - phy_disconnect(ndev->phydev); - if (of_phy_is_fixed_link(np)) - of_phy_deregister_fixed_link(np); - } - cancel_work_sync(&priv->work); - if (info->multi_irqs) { - free_irq(priv->tx_irqs[RAVB_NC], ndev); - free_irq(priv->rx_irqs[RAVB_NC], ndev); - free_irq(priv->tx_irqs[RAVB_BE], ndev); - free_irq(priv->rx_irqs[RAVB_BE], ndev); - free_irq(priv->emac_irq, ndev); - if (info->err_mgmt_irqs) { - free_irq(priv->erra_irq, ndev); - free_irq(priv->mgmta_irq, ndev); - } - } - free_irq(ndev->irq, ndev); - if (info->nc_queues) napi_disable(&priv->napi[RAVB_NC]); napi_disable(&priv->napi[RAVB_BE]); @@ -2211,6 +2373,17 @@ static int ravb_close(struct net_device *ndev) if (info->nc_queues) ravb_ring_free(ndev, RAVB_NC); + /* Update statistics. */ + ravb_get_stats(ndev); + + /* Set reset mode. */ + error = ravb_set_opmode(ndev, CCC_OPC_RESET); + if (error) + return error; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; } @@ -2334,11 +2507,58 @@ static void ravb_set_rx_csum(struct net_device *ndev, bool enable) spin_unlock_irqrestore(&priv->lock, flags); } +static int ravb_endisable_csum_gbeth(struct net_device *ndev, enum ravb_reg reg, + u32 val, u32 mask) +{ + u32 csr0 = CSR0_TPE | CSR0_RPE; + int ret; + + ravb_write(ndev, csr0 & ~mask, CSR0); + ret = ravb_wait(ndev, CSR0, mask, 0); + if (!ret) + ravb_write(ndev, val, reg); + + ravb_write(ndev, csr0, CSR0); + + return ret; +} + static int ravb_set_features_gbeth(struct net_device *ndev, netdev_features_t features) { - /* Place holder */ - return 0; + netdev_features_t changed = ndev->features ^ features; + struct ravb_private *priv = netdev_priv(ndev); + unsigned long flags; + int ret = 0; + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + if (changed & NETIF_F_RXCSUM) { + if (features & NETIF_F_RXCSUM) + val = CSR2_RIP4 | CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4; + else + val = 0; + + ret = ravb_endisable_csum_gbeth(ndev, CSR2, val, CSR0_RPE); + if (ret) + goto done; + } + + if (changed & NETIF_F_HW_CSUM) { + if (features & NETIF_F_HW_CSUM) + val = CSR1_TIP4 | CSR1_TTCP4 | CSR1_TUDP4; + else + val = 0; + + ret = ravb_endisable_csum_gbeth(ndev, CSR1, val, CSR0_TPE); + if (ret) + goto done; + } + +done: + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; } static int ravb_set_features_rcar(struct net_device *ndev, @@ -2349,8 +2569,6 @@ static int ravb_set_features_rcar(struct net_device *ndev, if (changed & NETIF_F_RXCSUM) ravb_set_rx_csum(ndev, features & NETIF_F_RXCSUM); - ndev->features = features; - return 0; } @@ -2359,8 +2577,24 @@ static int ravb_set_features(struct net_device *ndev, { struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; + struct device *dev = &priv->pdev->dev; + int ret; + + pm_runtime_get_noresume(dev); + + if (pm_runtime_active(dev)) + ret = info->set_feature(ndev, features); + else + ret = 0; + + pm_runtime_put_noidle(dev); + + if (ret) + return ret; - return info->set_feature(ndev, features); + ndev->features = features; + + return 0; } static const struct net_device_ops ravb_netdev_ops = { @@ -2518,6 +2752,8 @@ static const struct ravb_hw_info gbeth_hw_info = { .emac_init = ravb_emac_init_gbeth, .gstrings_stats = ravb_gstrings_stats_gbeth, .gstrings_size = sizeof(ravb_gstrings_stats_gbeth), + .net_hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM, + .net_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM, .stats_len = ARRAY_SIZE(ravb_gstrings_stats_gbeth), .max_rx_len = ALIGN(GBETH_RX_BUFF_MAX, RAVB_ALIGN), .tccr_mask = TCCR_TSRQ0, @@ -2541,100 +2777,91 @@ static const struct of_device_id ravb_match_table[] = { }; MODULE_DEVICE_TABLE(of, ravb_match_table); -static int ravb_set_gti(struct net_device *ndev) +static int ravb_setup_irq(struct ravb_private *priv, const char *irq_name, + const char *ch, int *irq, irq_handler_t handler) { - struct ravb_private *priv = netdev_priv(ndev); - const struct ravb_hw_info *info = priv->info; - struct device *dev = ndev->dev.parent; - unsigned long rate; - uint64_t inc; - - if (info->gptp_ref_clk) - rate = clk_get_rate(priv->gptp_clk); - else - rate = clk_get_rate(priv->clk); - if (!rate) - return -EINVAL; + struct platform_device *pdev = priv->pdev; + struct net_device *ndev = priv->ndev; + struct device *dev = &pdev->dev; + const char *dev_name; + unsigned long flags; + int error, irq_num; - inc = div64_ul(1000000000ULL << 20, rate); + if (irq_name) { + dev_name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", ndev->name, ch); + if (!dev_name) + return -ENOMEM; - if (inc < GTI_TIV_MIN || inc > GTI_TIV_MAX) { - dev_err(dev, "gti.tiv increment 0x%llx is outside the range 0x%x - 0x%x\n", - inc, GTI_TIV_MIN, GTI_TIV_MAX); - return -EINVAL; + irq_num = platform_get_irq_byname(pdev, irq_name); + flags = 0; + } else { + dev_name = ndev->name; + irq_num = platform_get_irq(pdev, 0); + flags = IRQF_SHARED; } + if (irq_num < 0) + return irq_num; - ravb_write(ndev, inc, GTI); + if (irq) + *irq = irq_num; - return 0; + error = devm_request_irq(dev, irq_num, handler, flags, dev_name, ndev); + if (error) + netdev_err(ndev, "cannot request IRQ %s\n", dev_name); + + return error; } -static int ravb_set_config_mode(struct net_device *ndev) +static int ravb_setup_irqs(struct ravb_private *priv) { - struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; + struct net_device *ndev = priv->ndev; + const char *irq_name, *emac_irq_name; int error; - if (info->gptp) { - error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); - if (error) - return error; - /* Set CSEL value */ - ravb_modify(ndev, CCC, CCC_CSEL, CCC_CSEL_HPB); - } else if (info->ccc_gac) { - error = ravb_set_opmode(ndev, CCC_OPC_CONFIG | CCC_GAC | CCC_CSEL_HPB); + if (!info->multi_irqs) + return ravb_setup_irq(priv, NULL, NULL, &ndev->irq, ravb_interrupt); + + if (info->err_mgmt_irqs) { + irq_name = "dia"; + emac_irq_name = "line3"; } else { - error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); + irq_name = "ch22"; + emac_irq_name = "ch24"; } - return error; -} - -/* Set tx and rx clock internal delay modes */ -static void ravb_parse_delay_mode(struct device_node *np, struct net_device *ndev) -{ - struct ravb_private *priv = netdev_priv(ndev); - bool explicit_delay = false; - u32 delay; + error = ravb_setup_irq(priv, irq_name, "ch22:multi", &ndev->irq, ravb_multi_interrupt); + if (error) + return error; - if (!of_property_read_u32(np, "rx-internal-delay-ps", &delay)) { - /* Valid values are 0 and 1800, according to DT bindings */ - priv->rxcidm = !!delay; - explicit_delay = true; - } - if (!of_property_read_u32(np, "tx-internal-delay-ps", &delay)) { - /* Valid values are 0 and 2000, according to DT bindings */ - priv->txcidm = !!delay; - explicit_delay = true; - } + error = ravb_setup_irq(priv, emac_irq_name, "ch24:emac", &priv->emac_irq, + ravb_emac_interrupt); + if (error) + return error; - if (explicit_delay) - return; + if (info->err_mgmt_irqs) { + error = ravb_setup_irq(priv, "err_a", "err_a", NULL, ravb_multi_interrupt); + if (error) + return error; - /* Fall back to legacy rgmii-*id behavior */ - if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || - priv->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID) { - priv->rxcidm = 1; - priv->rgmii_override = 1; + error = ravb_setup_irq(priv, "mgmt_a", "mgmt_a", NULL, ravb_multi_interrupt); + if (error) + return error; } - if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || - priv->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) { - priv->txcidm = 1; - priv->rgmii_override = 1; - } -} + error = ravb_setup_irq(priv, "ch0", "ch0:rx_be", NULL, ravb_be_interrupt); + if (error) + return error; -static void ravb_set_delay_mode(struct net_device *ndev) -{ - struct ravb_private *priv = netdev_priv(ndev); - u32 set = 0; + error = ravb_setup_irq(priv, "ch1", "ch1:rx_nc", NULL, ravb_nc_interrupt); + if (error) + return error; - if (priv->rxcidm) - set |= APSR_RDM; - if (priv->txcidm) - set |= APSR_TDM; - ravb_modify(ndev, APSR, APSR_RDM | APSR_TDM, set); + error = ravb_setup_irq(priv, "ch18", "ch18:tx_be", NULL, ravb_be_interrupt); + if (error) + return error; + + return ravb_setup_irq(priv, "ch19", "ch19:tx_nc", NULL, ravb_nc_interrupt); } static int ravb_probe(struct platform_device *pdev) @@ -2644,9 +2871,8 @@ static int ravb_probe(struct platform_device *pdev) struct reset_control *rstc; struct ravb_private *priv; struct net_device *ndev; - int error, irq, q; struct resource *res; - int i; + int error, q; if (!np) { dev_err(&pdev->dev, @@ -2654,7 +2880,7 @@ static int ravb_probe(struct platform_device *pdev) return -EINVAL; } - rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(&pdev->dev, PTR_ERR(rstc), "failed to get cpg reset\n"); @@ -2673,25 +2899,6 @@ static int ravb_probe(struct platform_device *pdev) if (error) goto out_free_netdev; - pm_runtime_enable(&pdev->dev); - error = pm_runtime_resume_and_get(&pdev->dev); - if (error < 0) - goto out_rpm_disable; - - if (info->multi_irqs) { - if (info->err_mgmt_irqs) - irq = platform_get_irq_byname(pdev, "dia"); - else - irq = platform_get_irq_byname(pdev, "ch22"); - } else { - irq = platform_get_irq(pdev, 0); - } - if (irq < 0) { - error = irq; - goto out_release; - } - ndev->irq = irq; - SET_NETDEV_DEV(ndev, &pdev->dev); priv = netdev_priv(ndev); @@ -2706,10 +2913,43 @@ static int ravb_probe(struct platform_device *pdev) priv->num_rx_ring[RAVB_NC] = NC_RX_RING_SIZE; } + error = ravb_setup_irqs(priv); + if (error) + goto out_reset_assert; + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + error = PTR_ERR(priv->clk); + goto out_reset_assert; + } + + if (info->gptp_ref_clk) { + priv->gptp_clk = devm_clk_get(&pdev->dev, "gptp"); + if (IS_ERR(priv->gptp_clk)) { + error = PTR_ERR(priv->gptp_clk); + goto out_reset_assert; + } + } + + priv->refclk = devm_clk_get_optional(&pdev->dev, "refclk"); + if (IS_ERR(priv->refclk)) { + error = PTR_ERR(priv->refclk); + goto out_reset_assert; + } + clk_prepare(priv->refclk); + + platform_set_drvdata(pdev, ndev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 100); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + error = pm_runtime_resume_and_get(&pdev->dev); + if (error < 0) + goto out_rpm_disable; + priv->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->addr)) { error = PTR_ERR(priv->addr); - goto out_release; + goto out_rpm_put; } /* The Ether-specific entries in the device structure. */ @@ -2720,78 +2960,12 @@ static int ravb_probe(struct platform_device *pdev) error = of_get_phy_mode(np, &priv->phy_interface); if (error && error != -ENODEV) - goto out_release; + goto out_rpm_put; priv->no_avb_link = of_property_read_bool(np, "renesas,no-ether-link"); priv->avb_link_active_low = of_property_read_bool(np, "renesas,ether-link-active-low"); - if (info->multi_irqs) { - if (info->err_mgmt_irqs) - irq = platform_get_irq_byname(pdev, "line3"); - else - irq = platform_get_irq_byname(pdev, "ch24"); - if (irq < 0) { - error = irq; - goto out_release; - } - priv->emac_irq = irq; - for (i = 0; i < NUM_RX_QUEUE; i++) { - irq = platform_get_irq_byname(pdev, ravb_rx_irqs[i]); - if (irq < 0) { - error = irq; - goto out_release; - } - priv->rx_irqs[i] = irq; - } - for (i = 0; i < NUM_TX_QUEUE; i++) { - irq = platform_get_irq_byname(pdev, ravb_tx_irqs[i]); - if (irq < 0) { - error = irq; - goto out_release; - } - priv->tx_irqs[i] = irq; - } - - if (info->err_mgmt_irqs) { - irq = platform_get_irq_byname(pdev, "err_a"); - if (irq < 0) { - error = irq; - goto out_release; - } - priv->erra_irq = irq; - - irq = platform_get_irq_byname(pdev, "mgmt_a"); - if (irq < 0) { - error = irq; - goto out_release; - } - priv->mgmta_irq = irq; - } - } - - priv->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(priv->clk)) { - error = PTR_ERR(priv->clk); - goto out_release; - } - - priv->refclk = devm_clk_get_optional(&pdev->dev, "refclk"); - if (IS_ERR(priv->refclk)) { - error = PTR_ERR(priv->refclk); - goto out_release; - } - clk_prepare_enable(priv->refclk); - - if (info->gptp_ref_clk) { - priv->gptp_clk = devm_clk_get(&pdev->dev, "gptp"); - if (IS_ERR(priv->gptp_clk)) { - error = PTR_ERR(priv->gptp_clk); - goto out_disable_refclk; - } - clk_prepare_enable(priv->gptp_clk); - } - ndev->max_mtu = info->rx_max_buf_size - (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN); ndev->min_mtu = ETH_MIN_MTU; @@ -2806,25 +2980,11 @@ static int ravb_probe(struct platform_device *pdev) ndev->netdev_ops = &ravb_netdev_ops; ndev->ethtool_ops = &ravb_ethtool_ops; - /* Set AVB config mode */ - error = ravb_set_config_mode(ndev); + error = ravb_compute_gti(ndev); if (error) - goto out_disable_gptp_clk; + goto out_rpm_put; - if (info->gptp || info->ccc_gac) { - /* Set GTI value */ - error = ravb_set_gti(ndev); - if (error) - goto out_disable_gptp_clk; - - /* Request GTI loading */ - ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI); - } - - if (info->internal_delay) { - ravb_parse_delay_mode(np, ndev); - ravb_set_delay_mode(ndev); - } + ravb_parse_delay_mode(np, ndev); /* Allocate descriptor base address table */ priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM; @@ -2835,22 +2995,22 @@ static int ravb_probe(struct platform_device *pdev) "Cannot allocate desc base address table (size %d bytes)\n", priv->desc_bat_size); error = -ENOMEM; - goto out_disable_gptp_clk; + goto out_rpm_put; } for (q = RAVB_BE; q < DBAT_ENTRY_NUM; q++) priv->desc_bat[q].die_dt = DT_EOS; - ravb_write(ndev, priv->desc_bat_dma, DBAT); /* Initialise HW timestamp list */ INIT_LIST_HEAD(&priv->ts_skb_list); - /* Initialise PTP Clock driver */ - if (info->ccc_gac) - ravb_ptp_init(ndev, pdev); - /* Debug message level */ priv->msg_enable = RAVB_DEF_MSG_ENABLE; + /* Set config mode as this is needed for PHY initialization. */ + error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); + if (error) + goto out_rpm_put; + /* Read and set MAC address */ ravb_read_mac_address(np, ndev); if (!is_valid_ether_addr(ndev->dev_addr)) { @@ -2863,9 +3023,14 @@ static int ravb_probe(struct platform_device *pdev) error = ravb_mdio_init(priv); if (error) { dev_err(&pdev->dev, "failed to initialize MDIO\n"); - goto out_dma_free; + goto out_reset_mode; } + /* Undo previous switch to config opmode. */ + error = ravb_set_opmode(ndev, CCC_OPC_RESET); + if (error) + goto out_mdio_release; + netif_napi_add(ndev, &priv->napi[RAVB_BE], ravb_poll); if (info->nc_queues) netif_napi_add(ndev, &priv->napi[RAVB_NC], ravb_poll); @@ -2881,7 +3046,8 @@ static int ravb_probe(struct platform_device *pdev) netdev_info(ndev, "Base address at %#x, %pM, IRQ %d.\n", (u32)ndev->base_addr, ndev->dev_addr, ndev->irq); - platform_set_drvdata(pdev, ndev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); return 0; @@ -2890,22 +3056,19 @@ out_napi_del: netif_napi_del(&priv->napi[RAVB_NC]); netif_napi_del(&priv->napi[RAVB_BE]); +out_mdio_release: ravb_mdio_release(priv); -out_dma_free: +out_reset_mode: + ravb_set_opmode(ndev, CCC_OPC_RESET); dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); - - /* Stop PTP Clock driver */ - if (info->ccc_gac) - ravb_ptp_stop(ndev); -out_disable_gptp_clk: - clk_disable_unprepare(priv->gptp_clk); -out_disable_refclk: - clk_disable_unprepare(priv->refclk); -out_release: +out_rpm_put: pm_runtime_put(&pdev->dev); out_rpm_disable: pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + clk_unprepare(priv->refclk); +out_reset_assert: reset_control_assert(rstc); out_free_netdev: free_netdev(ndev); @@ -2917,6 +3080,12 @@ static void ravb_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; + struct device *dev = &priv->pdev->dev; + int error; + + error = pm_runtime_resume_and_get(dev); + if (error < 0) + return; unregister_netdev(ndev); if (info->nc_queues) @@ -2925,20 +3094,13 @@ static void ravb_remove(struct platform_device *pdev) ravb_mdio_release(priv); - /* Stop PTP Clock driver */ - if (info->ccc_gac) - ravb_ptp_stop(ndev); - dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); - ravb_set_opmode(ndev, CCC_OPC_RESET); - - clk_disable_unprepare(priv->gptp_clk); - clk_disable_unprepare(priv->refclk); - - pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync_suspend(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(dev); + clk_unprepare(priv->refclk); reset_control_assert(priv->rstc); free_netdev(ndev); platform_set_drvdata(pdev, NULL); @@ -2964,6 +3126,9 @@ static int ravb_wol_setup(struct net_device *ndev) /* Enable MagicPacket */ ravb_modify(ndev, ECMR, ECMR_MPDE, ECMR_MPDE); + if (priv->info->ccc_gac) + ravb_ptp_stop(ndev); + return enable_irq_wake(priv->emac_irq); } @@ -2971,6 +3136,20 @@ static int ravb_wol_restore(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; + int error; + + /* Set reset mode to rearm the WoL logic. */ + error = ravb_set_opmode(ndev, CCC_OPC_RESET); + if (error) + return error; + + /* Set AVB config mode. */ + error = ravb_set_config_mode(ndev); + if (error) + return error; + + if (priv->info->ccc_gac) + ravb_ptp_init(ndev, priv->pdev); if (info->nc_queues) napi_enable(&priv->napi[RAVB_NC]); @@ -2984,102 +3163,96 @@ static int ravb_wol_restore(struct net_device *ndev) return disable_irq_wake(priv->emac_irq); } -static int __maybe_unused ravb_suspend(struct device *dev) +static int ravb_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct ravb_private *priv = netdev_priv(ndev); int ret; if (!netif_running(ndev)) - return 0; + goto reset_assert; netif_device_detach(ndev); if (priv->wol_enabled) - ret = ravb_wol_setup(ndev); - else - ret = ravb_close(ndev); + return ravb_wol_setup(ndev); - if (priv->info->ccc_gac) - ravb_ptp_stop(ndev); + ret = ravb_close(ndev); + if (ret) + return ret; - return ret; + ret = pm_runtime_force_suspend(&priv->pdev->dev); + if (ret) + return ret; + +reset_assert: + return reset_control_assert(priv->rstc); } -static int __maybe_unused ravb_resume(struct device *dev) +static int ravb_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct ravb_private *priv = netdev_priv(ndev); - const struct ravb_hw_info *info = priv->info; - int ret = 0; - - /* If WoL is enabled set reset mode to rearm the WoL logic */ - if (priv->wol_enabled) { - ret = ravb_set_opmode(ndev, CCC_OPC_RESET); - if (ret) - return ret; - } - - /* All register have been reset to default values. - * Restore all registers which where setup at probe time and - * reopen device if it was running before system suspended. - */ + int ret; - /* Set AVB config mode */ - ret = ravb_set_config_mode(ndev); + ret = reset_control_deassert(priv->rstc); if (ret) return ret; - if (info->gptp || info->ccc_gac) { - /* Set GTI value */ - ret = ravb_set_gti(ndev); + if (!netif_running(ndev)) + return 0; + + /* If WoL is enabled restore the interface. */ + if (priv->wol_enabled) { + ret = ravb_wol_restore(ndev); + if (ret) + return ret; + } else { + ret = pm_runtime_force_resume(dev); if (ret) return ret; - - /* Request GTI loading */ - ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI); } - if (info->internal_delay) - ravb_set_delay_mode(ndev); + /* Reopening the interface will restore the device to the working state. */ + ret = ravb_open(ndev); + if (ret < 0) + goto out_rpm_put; - /* Restore descriptor base address table */ - ravb_write(ndev, priv->desc_bat_dma, DBAT); + ravb_set_rx_mode(ndev); + netif_device_attach(ndev); - if (priv->info->ccc_gac) - ravb_ptp_init(ndev, priv->pdev); + return 0; - if (netif_running(ndev)) { - if (priv->wol_enabled) { - ret = ravb_wol_restore(ndev); - if (ret) - return ret; - } - ret = ravb_open(ndev); - if (ret < 0) - return ret; - ravb_set_rx_mode(ndev); - netif_device_attach(ndev); +out_rpm_put: + if (!priv->wol_enabled) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } return ret; } -static int __maybe_unused ravb_runtime_nop(struct device *dev) +static int ravb_runtime_suspend(struct device *dev) { - /* Runtime PM callback shared between ->runtime_suspend() - * and ->runtime_resume(). Simply returns success. - * - * This driver re-initializes all registers after - * pm_runtime_get_sync() anyway so there is no need - * to save and restore registers here. - */ + struct net_device *ndev = dev_get_drvdata(dev); + struct ravb_private *priv = netdev_priv(ndev); + + clk_disable(priv->refclk); + return 0; } +static int ravb_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct ravb_private *priv = netdev_priv(ndev); + + return clk_enable(priv->refclk); +} + static const struct dev_pm_ops ravb_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ravb_suspend, ravb_resume) - SET_RUNTIME_PM_OPS(ravb_runtime_nop, ravb_runtime_nop, NULL) + SYSTEM_SLEEP_PM_OPS(ravb_suspend, ravb_resume) + RUNTIME_PM_OPS(ravb_runtime_suspend, ravb_runtime_resume, NULL) }; static struct platform_driver ravb_driver = { @@ -3087,7 +3260,7 @@ static struct platform_driver ravb_driver = { .remove_new = ravb_remove, .driver = { .name = "ravb", - .pm = &ravb_dev_pm_ops, + .pm = pm_ptr(&ravb_dev_pm_ops), .of_match_table = ravb_match_table, }, }; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h index d14e0cfc3a6b..1458939c3bf5 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h @@ -503,7 +503,6 @@ struct sxgbe_priv_data { bool tx_path_in_lpi_mode; int lpi_irq; int eee_enabled; - int eee_active; int tx_lpi_timer; }; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c index 8ba017ec9849..4a439b34114d 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c @@ -133,22 +133,20 @@ static const struct sxgbe_stats sxgbe_gstrings_stats[] = { #define SXGBE_STATS_LEN ARRAY_SIZE(sxgbe_gstrings_stats) static int sxgbe_get_eee(struct net_device *dev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct sxgbe_priv_data *priv = netdev_priv(dev); if (!priv->hw_cap.eee) return -EOPNOTSUPP; - edata->eee_enabled = priv->eee_enabled; - edata->eee_active = priv->eee_active; edata->tx_lpi_timer = priv->tx_lpi_timer; return phy_ethtool_get_eee(dev->phydev, edata); } static int sxgbe_set_eee(struct net_device *dev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct sxgbe_priv_data *priv = netdev_priv(dev); diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 71439825ea4e..ecbe3994f2b1 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -130,7 +130,6 @@ bool sxgbe_eee_init(struct sxgbe_priv_data * const priv) if (phy_init_eee(ndev->phydev, true)) return false; - priv->eee_active = 1; timer_setup(&priv->eee_ctrl_timer, sxgbe_eee_ctrl_timer, 0); priv->eee_ctrl_timer.expires = SXGBE_LPI_TIMER(eee_timer); add_timer(&priv->eee_ctrl_timer); diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index 175bd9cdfdac..551f890db90a 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -595,7 +595,7 @@ void efx_stop_all(struct efx_nic *efx) efx_stop_datapath(efx); } -/* Context: process, dev_base_lock or RTNL held, non-blocking. */ +/* Context: process, rcu_read_lock or RTNL held, non-blocking. */ void efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats) { struct efx_nic *efx = efx_netdev_priv(net_dev); diff --git a/drivers/net/ethernet/sfc/falcon/efx.c b/drivers/net/ethernet/sfc/falcon/efx.c index e001f27085c6..1cb32aedd89c 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.c +++ b/drivers/net/ethernet/sfc/falcon/efx.c @@ -2085,7 +2085,7 @@ int ef4_net_stop(struct net_device *net_dev) return 0; } -/* Context: process, dev_base_lock or RTNL held, non-blocking. */ +/* Context: process, rcu_read_lock or RTNL held, non-blocking. */ static void ef4_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats) { diff --git a/drivers/net/ethernet/sfc/siena/efx_common.c b/drivers/net/ethernet/sfc/siena/efx_common.c index e4b294b8e9ac..88e5bc347a44 100644 --- a/drivers/net/ethernet/sfc/siena/efx_common.c +++ b/drivers/net/ethernet/sfc/siena/efx_common.c @@ -605,7 +605,7 @@ static size_t efx_siena_update_stats_atomic(struct efx_nic *efx, u64 *full_stats return efx->type->update_stats(efx, full_stats, core_stats); } -/* Context: process, dev_base_lock or RTNL held, non-blocking. */ +/* Context: process, rcu_read_lock or RTNL held, non-blocking. */ void efx_siena_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats) { diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 758347616535..78ff3af7911a 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -98,6 +98,7 @@ static int watchdog = 1000; module_param(watchdog, int, 0400); MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); +MODULE_DESCRIPTION("SMC 91C9x/91C1xxx Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:smc91x"); diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 31cb7d0166f0..74f1ccc96459 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -56,6 +56,7 @@ #define SMSC_MDIONAME "smsc911x-mdio" #define SMSC_DRV_VERSION "2008-10-21" +MODULE_DESCRIPTION("SMSC LAN911x/LAN921x Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(SMSC_DRV_VERSION); MODULE_ALIAS("platform:smsc911x"); diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index e1c4a11c1f18..15cb96c2506d 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -26,6 +26,7 @@ #define DRV_DESCRIPTION "SMSC LAN9420 driver" #define DRV_VERSION "1.01" +MODULE_DESCRIPTION("SMSC LAN9420 Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 85dcda51df05..4ec61f1ee71a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -165,9 +165,9 @@ config DWMAC_STARFIVE help Support for ethernet controllers on StarFive RISC-V SoCs - This selects the StarFive platform specific glue layer support for - the stmmac device driver. This driver is used for StarFive JH7110 - ethernet controller. + This selects the StarFive platform specific glue layer support + for the stmmac device driver. This driver is used for the + StarFive JH7100 and JH7110 ethernet controllers. config DWMAC_STI tristate "STi GMAC support" diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 721c1f8e892f..a6fefe675ef1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -59,28 +59,51 @@ #undef FRAME_FILTER_DEBUG /* #define FRAME_FILTER_DEBUG */ +struct stmmac_q_tx_stats { + u64_stats_t tx_bytes; + u64_stats_t tx_set_ic_bit; + u64_stats_t tx_tso_frames; + u64_stats_t tx_tso_nfrags; +}; + +struct stmmac_napi_tx_stats { + u64_stats_t tx_packets; + u64_stats_t tx_pkt_n; + u64_stats_t poll; + u64_stats_t tx_clean; + u64_stats_t tx_set_ic_bit; +}; + struct stmmac_txq_stats { - u64 tx_bytes; - u64 tx_packets; - u64 tx_pkt_n; - u64 tx_normal_irq_n; - u64 napi_poll; - u64 tx_clean; - u64 tx_set_ic_bit; - u64 tx_tso_frames; - u64 tx_tso_nfrags; - struct u64_stats_sync syncp; + /* Updates protected by tx queue lock. */ + struct u64_stats_sync q_syncp; + struct stmmac_q_tx_stats q; + + /* Updates protected by NAPI poll logic. */ + struct u64_stats_sync napi_syncp; + struct stmmac_napi_tx_stats napi; } ____cacheline_aligned_in_smp; +struct stmmac_napi_rx_stats { + u64_stats_t rx_bytes; + u64_stats_t rx_packets; + u64_stats_t rx_pkt_n; + u64_stats_t poll; +}; + struct stmmac_rxq_stats { - u64 rx_bytes; - u64 rx_packets; - u64 rx_pkt_n; - u64 rx_normal_irq_n; - u64 napi_poll; - struct u64_stats_sync syncp; + /* Updates protected by NAPI poll logic. */ + struct u64_stats_sync napi_syncp; + struct stmmac_napi_rx_stats napi; } ____cacheline_aligned_in_smp; +/* Updates on each CPU protected by not allowing nested irqs. */ +struct stmmac_pcpu_stats { + struct u64_stats_sync syncp; + u64_stats_t rx_normal_irq_n[MTL_MAX_TX_QUEUES]; + u64_stats_t tx_normal_irq_n[MTL_MAX_RX_QUEUES]; +}; + /* Extra statistic and debug information exposed by ethtool */ struct stmmac_extra_stats { /* Transmit errors */ @@ -202,9 +225,12 @@ struct stmmac_extra_stats { unsigned long mtl_est_hlbf; unsigned long mtl_est_btre; unsigned long mtl_est_btrlm; + unsigned long max_sdu_txq_drop[MTL_MAX_TX_QUEUES]; + unsigned long mtl_est_txq_hlbf[MTL_MAX_TX_QUEUES]; /* per queue statistics */ struct stmmac_txq_stats txq_stats[MTL_MAX_TX_QUEUES]; struct stmmac_rxq_stats rxq_stats[MTL_MAX_RX_QUEUES]; + struct stmmac_pcpu_stats __percpu *pcpu_stats; unsigned long rx_dropped; unsigned long rx_errors; unsigned long tx_dropped; @@ -216,6 +242,7 @@ struct stmmac_safety_stats { unsigned long mac_errors[32]; unsigned long mtl_errors[32]; unsigned long dma_errors[32]; + unsigned long dma_dpp_errors[32]; }; /* Number of fields in Safety Stats */ @@ -344,6 +371,7 @@ enum request_irq_err { REQ_IRQ_ERR_ALL, REQ_IRQ_ERR_TX, REQ_IRQ_ERR_RX, + REQ_IRQ_ERR_SFTY, REQ_IRQ_ERR_SFTY_UE, REQ_IRQ_ERR_SFTY_CE, REQ_IRQ_ERR_LPI, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c index 8f730ada71f9..6b65420e11b5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c @@ -353,6 +353,10 @@ static int imx_dwmac_probe(struct platform_device *pdev) if (data->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY) plat_dat->flags |= STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY; + /* Default TX Q0 to use TSO and rest TXQ for TBS */ + for (int i = 1; i < plat_dat->tx_queues_to_use; i++) + plat_dat->tx_queues_cfg[i].tbs_en = 1; + plat_dat->host_dma_width = dwmac->ops->addr_width; plat_dat->init = imx_dwmac_init; plat_dat->exit = imx_dwmac_exit; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 31631e3f89d0..2691a250a5a7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -728,7 +728,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) struct stmmac_resources stmmac_res; struct device *dev = &pdev->dev; struct qcom_ethqos *ethqos; - int ret; + int ret, i; ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) @@ -822,6 +822,10 @@ static int qcom_ethqos_probe(struct platform_device *pdev) plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown; } + /* Enable TSO on queue0 and enable TBS on rest of the queues */ + for (i = 1; i < plat_dat->tx_queues_to_use; i++) + plat_dat->tx_queues_cfg[i].tbs_en = 1; + return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index ba2ce776bd4d..68f85e4605cb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -585,4 +585,5 @@ static struct platform_driver socfpga_dwmac_driver = { }; module_platform_driver(socfpga_dwmac_driver); +MODULE_DESCRIPTION("Altera SOC DWMAC Specific Glue layer"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c index 5d630affb4d1..4e1076faee0c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c @@ -15,13 +15,20 @@ #include "stmmac_platform.h" -#define STARFIVE_DWMAC_PHY_INFT_RGMII 0x1 -#define STARFIVE_DWMAC_PHY_INFT_RMII 0x4 -#define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U +#define STARFIVE_DWMAC_PHY_INFT_RGMII 0x1 +#define STARFIVE_DWMAC_PHY_INFT_RMII 0x4 +#define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U + +#define JH7100_SYSMAIN_REGISTER49_DLYCHAIN 0xc8 + +struct starfive_dwmac_data { + unsigned int gtxclk_dlychain; +}; struct starfive_dwmac { struct device *dev; struct clk *clk_tx; + const struct starfive_dwmac_data *data; }; static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode) @@ -67,6 +74,8 @@ static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: mode = STARFIVE_DWMAC_PHY_INFT_RGMII; break; @@ -89,6 +98,14 @@ static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) if (err) return dev_err_probe(dwmac->dev, err, "error setting phy mode\n"); + if (dwmac->data) { + err = regmap_write(regmap, JH7100_SYSMAIN_REGISTER49_DLYCHAIN, + dwmac->data->gtxclk_dlychain); + if (err) + return dev_err_probe(dwmac->dev, err, + "error selecting gtxclk delay chain\n"); + } + return 0; } @@ -114,6 +131,8 @@ static int starfive_dwmac_probe(struct platform_device *pdev) if (!dwmac) return -ENOMEM; + dwmac->data = device_get_match_data(&pdev->dev); + dwmac->clk_tx = devm_clk_get_enabled(&pdev->dev, "tx"); if (IS_ERR(dwmac->clk_tx)) return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->clk_tx), @@ -144,8 +163,13 @@ static int starfive_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); } +static const struct starfive_dwmac_data jh7100_data = { + .gtxclk_dlychain = 4, +}; + static const struct of_device_id starfive_dwmac_match[] = { - { .compatible = "starfive,jh7110-dwmac" }, + { .compatible = "starfive,jh7100-dwmac", .data = &jh7100_data }, + { .compatible = "starfive,jh7110-dwmac" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, starfive_dwmac_match); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 137741b94122..b21d99faa2d0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -441,8 +441,7 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); int ret = 0; u32 v; @@ -455,9 +454,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, if (v & EMAC_TX_INT) { ret |= handle_tx; - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); } if (v & EMAC_TX_DMA_STOP_INT) @@ -479,9 +478,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, if (v & EMAC_RX_INT) { ret |= handle_rx; - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); } if (v & EMAC_RX_BUF_UA_INT) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index 9470d3fd2ded..0d185e54eb7e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -171,8 +171,7 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan)); u32 intr_en = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); int ret = 0; if (dir == DMA_DIR_RX) @@ -201,15 +200,15 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, } /* TX/RX NORMAL interrupts */ if (likely(intr_status & DMA_CHAN_STATUS_RI)) { - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_rx; } if (likely(intr_status & DMA_CHAN_STATUS_TI)) { - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_tx; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 7907d62d3437..85e18f9a22f9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -162,8 +162,7 @@ static void show_rx_process_state(unsigned int status) int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); int ret = 0; /* read the status register (CSR5) */ u32 intr_status = readl(ioaddr + DMA_STATUS); @@ -215,16 +214,16 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, u32 value = readl(ioaddr + DMA_INTR_ENA); /* to schedule NAPI on real RIE event. */ if (likely(value & DMA_INTR_ENA_RIE)) { - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_rx; } } if (likely(intr_status & DMA_STATUS_TI)) { - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_tx; } if (unlikely(intr_status & DMA_STATUS_ERI)) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 207ff1799f2c..6a2c7d22df1e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -303,6 +303,8 @@ #define XGMAC_RXCEIE BIT(4) #define XGMAC_TXCEIE BIT(0) #define XGMAC_MTL_ECC_INT_STATUS 0x000010cc +#define XGMAC_MTL_DPP_CONTROL 0x000010e0 +#define XGMAC_DPP_DISABLE BIT(0) #define XGMAC_MTL_TXQ_OPMODE(x) (0x00001100 + (0x80 * (x))) #define XGMAC_TQS GENMASK(25, 16) #define XGMAC_TQS_SHIFT 16 @@ -385,6 +387,7 @@ #define XGMAC_DCEIE BIT(1) #define XGMAC_TCEIE BIT(0) #define XGMAC_DMA_ECC_INT_STATUS 0x0000306c +#define XGMAC_DMA_DPP_INT_STATUS 0x00003074 #define XGMAC_DMA_CH_CONTROL(x) (0x00003100 + (0x80 * (x))) #define XGMAC_SPH BIT(24) #define XGMAC_PBLx8 BIT(16) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index eb48211d9b0e..1af2f89a0504 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -830,6 +830,44 @@ static const struct dwxgmac3_error_desc dwxgmac3_dma_errors[32]= { { false, "UNKNOWN", "Unknown Error" }, /* 31 */ }; +#define DPP_RX_ERR "Read Rx Descriptor Parity checker Error" +#define DPP_TX_ERR "Read Tx Descriptor Parity checker Error" + +static const struct dwxgmac3_error_desc dwxgmac3_dma_dpp_errors[32] = { + { true, "TDPES0", DPP_TX_ERR }, + { true, "TDPES1", DPP_TX_ERR }, + { true, "TDPES2", DPP_TX_ERR }, + { true, "TDPES3", DPP_TX_ERR }, + { true, "TDPES4", DPP_TX_ERR }, + { true, "TDPES5", DPP_TX_ERR }, + { true, "TDPES6", DPP_TX_ERR }, + { true, "TDPES7", DPP_TX_ERR }, + { true, "TDPES8", DPP_TX_ERR }, + { true, "TDPES9", DPP_TX_ERR }, + { true, "TDPES10", DPP_TX_ERR }, + { true, "TDPES11", DPP_TX_ERR }, + { true, "TDPES12", DPP_TX_ERR }, + { true, "TDPES13", DPP_TX_ERR }, + { true, "TDPES14", DPP_TX_ERR }, + { true, "TDPES15", DPP_TX_ERR }, + { true, "RDPES0", DPP_RX_ERR }, + { true, "RDPES1", DPP_RX_ERR }, + { true, "RDPES2", DPP_RX_ERR }, + { true, "RDPES3", DPP_RX_ERR }, + { true, "RDPES4", DPP_RX_ERR }, + { true, "RDPES5", DPP_RX_ERR }, + { true, "RDPES6", DPP_RX_ERR }, + { true, "RDPES7", DPP_RX_ERR }, + { true, "RDPES8", DPP_RX_ERR }, + { true, "RDPES9", DPP_RX_ERR }, + { true, "RDPES10", DPP_RX_ERR }, + { true, "RDPES11", DPP_RX_ERR }, + { true, "RDPES12", DPP_RX_ERR }, + { true, "RDPES13", DPP_RX_ERR }, + { true, "RDPES14", DPP_RX_ERR }, + { true, "RDPES15", DPP_RX_ERR }, +}; + static void dwxgmac3_handle_dma_err(struct net_device *ndev, void __iomem *ioaddr, bool correctable, struct stmmac_safety_stats *stats) @@ -841,6 +879,13 @@ static void dwxgmac3_handle_dma_err(struct net_device *ndev, dwxgmac3_log_error(ndev, value, correctable, "DMA", dwxgmac3_dma_errors, STAT_OFF(dma_errors), stats); + + value = readl(ioaddr + XGMAC_DMA_DPP_INT_STATUS); + writel(value, ioaddr + XGMAC_DMA_DPP_INT_STATUS); + + dwxgmac3_log_error(ndev, value, false, "DMA_DPP", + dwxgmac3_dma_dpp_errors, + STAT_OFF(dma_dpp_errors), stats); } static int @@ -881,6 +926,12 @@ dwxgmac3_safety_feat_config(void __iomem *ioaddr, unsigned int asp, value |= XGMAC_TMOUTEN; /* FSM Timeout Feature */ writel(value, ioaddr + XGMAC_MAC_FSM_CONTROL); + /* 5. Enable Data Path Parity Protection */ + value = readl(ioaddr + XGMAC_MTL_DPP_CONTROL); + /* already enabled by default, explicit enable it again */ + value &= ~XGMAC_DPP_DISABLE; + writel(value, ioaddr + XGMAC_MTL_DPP_CONTROL); + return 0; } @@ -914,7 +965,11 @@ static int dwxgmac3_safety_feat_irq_status(struct net_device *ndev, ret |= !corr; } - err = dma & (XGMAC_DEUIS | XGMAC_DECIS); + /* DMA_DPP_Interrupt_Status is indicated by MCSIS bit in + * DMA_Safety_Interrupt_Status, so we handle DMA Data Path + * Parity Errors here + */ + err = dma & (XGMAC_DEUIS | XGMAC_DECIS | XGMAC_MCSIS); corr = dma & XGMAC_DECIS; if (err) { dwxgmac3_handle_dma_err(ndev, ioaddr, corr, stats); @@ -930,6 +985,7 @@ static const struct dwxgmac3_error { { dwxgmac3_mac_errors }, { dwxgmac3_mtl_errors }, { dwxgmac3_dma_errors }, + { dwxgmac3_dma_dpp_errors }, }; static int dwxgmac3_safety_feat_dump(struct stmmac_safety_stats *stats, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index 3cde695fec91..dd2ab6185c40 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -337,8 +337,7 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); u32 intr_status = readl(ioaddr + XGMAC_DMA_CH_STATUS(chan)); u32 intr_en = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); int ret = 0; @@ -367,15 +366,15 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, /* TX/RX NORMAL interrupts */ if (likely(intr_status & XGMAC_NIS)) { if (likely(intr_status & XGMAC_RI)) { - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_rx; } if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) { - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_tx; } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index f155e4841c62..dddcaa9220cc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -31,6 +31,7 @@ struct stmmac_resources { int wol_irq; int lpi_irq; int irq; + int sfty_irq; int sfty_ce_irq; int sfty_ue_irq; int rx_irq[MTL_MAX_RX_QUEUES]; @@ -298,6 +299,7 @@ struct stmmac_priv { void __iomem *ptpaddr; void __iomem *estaddr; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + int sfty_irq; int sfty_ce_irq; int sfty_ue_irq; int rx_irq[MTL_MAX_RX_QUEUES]; @@ -306,6 +308,7 @@ struct stmmac_priv { char int_name_mac[IFNAMSIZ + 9]; char int_name_wol[IFNAMSIZ + 9]; char int_name_lpi[IFNAMSIZ + 9]; + char int_name_sfty[IFNAMSIZ + 10]; char int_name_sfty_ce[IFNAMSIZ + 10]; char int_name_sfty_ue[IFNAMSIZ + 10]; char int_name_rx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 14]; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c index 4da6ccc17c20..c9693f77e1f6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c @@ -81,6 +81,7 @@ static void est_irq_status(struct stmmac_priv *priv, struct net_device *dev, u32 status, value, feqn, hbfq, hbfs, btrl, btrl_max; void __iomem *est_addr = priv->estaddr; u32 txqcnt_mask = BIT(txqcnt) - 1; + int i; status = readl(est_addr + EST_STATUS); @@ -125,6 +126,11 @@ static void est_irq_status(struct stmmac_priv *priv, struct net_device *dev, x->mtl_est_hlbf++; + for (i = 0; i < txqcnt; i++) { + if (feqn & BIT(i)) + x->mtl_est_txq_hlbf[i]++; + } + /* Clear Interrupt */ writel(feqn, est_addr + EST_FRM_SZ_ERR); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 42d27b97dd1d..0e44b84fb7e7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -549,44 +549,79 @@ stmmac_set_pauseparam(struct net_device *netdev, } } +static u64 stmmac_get_rx_normal_irq_n(struct stmmac_priv *priv, int q) +{ + u64 total; + int cpu; + + total = 0; + for_each_possible_cpu(cpu) { + struct stmmac_pcpu_stats *pcpu; + unsigned int start; + u64 irq_n; + + pcpu = per_cpu_ptr(priv->xstats.pcpu_stats, cpu); + do { + start = u64_stats_fetch_begin(&pcpu->syncp); + irq_n = u64_stats_read(&pcpu->rx_normal_irq_n[q]); + } while (u64_stats_fetch_retry(&pcpu->syncp, start)); + total += irq_n; + } + return total; +} + +static u64 stmmac_get_tx_normal_irq_n(struct stmmac_priv *priv, int q) +{ + u64 total; + int cpu; + + total = 0; + for_each_possible_cpu(cpu) { + struct stmmac_pcpu_stats *pcpu; + unsigned int start; + u64 irq_n; + + pcpu = per_cpu_ptr(priv->xstats.pcpu_stats, cpu); + do { + start = u64_stats_fetch_begin(&pcpu->syncp); + irq_n = u64_stats_read(&pcpu->tx_normal_irq_n[q]); + } while (u64_stats_fetch_retry(&pcpu->syncp, start)); + total += irq_n; + } + return total; +} + static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data) { u32 tx_cnt = priv->plat->tx_queues_to_use; u32 rx_cnt = priv->plat->rx_queues_to_use; unsigned int start; - int q, stat; - char *p; + int q; for (q = 0; q < tx_cnt; q++) { struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[q]; - struct stmmac_txq_stats snapshot; + u64 pkt_n; do { - start = u64_stats_fetch_begin(&txq_stats->syncp); - snapshot = *txq_stats; - } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); + start = u64_stats_fetch_begin(&txq_stats->napi_syncp); + pkt_n = u64_stats_read(&txq_stats->napi.tx_pkt_n); + } while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start)); - p = (char *)&snapshot + offsetof(struct stmmac_txq_stats, tx_pkt_n); - for (stat = 0; stat < STMMAC_TXQ_STATS; stat++) { - *data++ = (*(u64 *)p); - p += sizeof(u64); - } + *data++ = pkt_n; + *data++ = stmmac_get_tx_normal_irq_n(priv, q); } for (q = 0; q < rx_cnt; q++) { struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[q]; - struct stmmac_rxq_stats snapshot; + u64 pkt_n; do { - start = u64_stats_fetch_begin(&rxq_stats->syncp); - snapshot = *rxq_stats; - } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); + start = u64_stats_fetch_begin(&rxq_stats->napi_syncp); + pkt_n = u64_stats_read(&rxq_stats->napi.rx_pkt_n); + } while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start)); - p = (char *)&snapshot + offsetof(struct stmmac_rxq_stats, rx_pkt_n); - for (stat = 0; stat < STMMAC_RXQ_STATS; stat++) { - *data++ = (*(u64 *)p); - p += sizeof(u64); - } + *data++ = pkt_n; + *data++ = stmmac_get_rx_normal_irq_n(priv, q); } } @@ -645,39 +680,49 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, pos = j; for (i = 0; i < rx_queues_count; i++) { struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[i]; - struct stmmac_rxq_stats snapshot; + struct stmmac_napi_rx_stats snapshot; + u64 n_irq; j = pos; do { - start = u64_stats_fetch_begin(&rxq_stats->syncp); - snapshot = *rxq_stats; - } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); - - data[j++] += snapshot.rx_pkt_n; - data[j++] += snapshot.rx_normal_irq_n; - normal_irq_n += snapshot.rx_normal_irq_n; - napi_poll += snapshot.napi_poll; + start = u64_stats_fetch_begin(&rxq_stats->napi_syncp); + snapshot = rxq_stats->napi; + } while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start)); + + data[j++] += u64_stats_read(&snapshot.rx_pkt_n); + n_irq = stmmac_get_rx_normal_irq_n(priv, i); + data[j++] += n_irq; + normal_irq_n += n_irq; + napi_poll += u64_stats_read(&snapshot.poll); } pos = j; for (i = 0; i < tx_queues_count; i++) { struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[i]; - struct stmmac_txq_stats snapshot; + struct stmmac_napi_tx_stats napi_snapshot; + struct stmmac_q_tx_stats q_snapshot; + u64 n_irq; j = pos; do { - start = u64_stats_fetch_begin(&txq_stats->syncp); - snapshot = *txq_stats; - } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); - - data[j++] += snapshot.tx_pkt_n; - data[j++] += snapshot.tx_normal_irq_n; - normal_irq_n += snapshot.tx_normal_irq_n; - data[j++] += snapshot.tx_clean; - data[j++] += snapshot.tx_set_ic_bit; - data[j++] += snapshot.tx_tso_frames; - data[j++] += snapshot.tx_tso_nfrags; - napi_poll += snapshot.napi_poll; + start = u64_stats_fetch_begin(&txq_stats->q_syncp); + q_snapshot = txq_stats->q; + } while (u64_stats_fetch_retry(&txq_stats->q_syncp, start)); + do { + start = u64_stats_fetch_begin(&txq_stats->napi_syncp); + napi_snapshot = txq_stats->napi; + } while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start)); + + data[j++] += u64_stats_read(&napi_snapshot.tx_pkt_n); + n_irq = stmmac_get_tx_normal_irq_n(priv, i); + data[j++] += n_irq; + normal_irq_n += n_irq; + data[j++] += u64_stats_read(&napi_snapshot.tx_clean); + data[j++] += u64_stats_read(&q_snapshot.tx_set_ic_bit) + + u64_stats_read(&napi_snapshot.tx_set_ic_bit); + data[j++] += u64_stats_read(&q_snapshot.tx_tso_frames); + data[j++] += u64_stats_read(&q_snapshot.tx_tso_nfrags); + napi_poll += u64_stats_read(&napi_snapshot.poll); } normal_irq_n += priv->xstats.rx_early_irq; data[j++] = normal_irq_n; @@ -852,15 +897,13 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) } static int stmmac_ethtool_op_get_eee(struct net_device *dev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct stmmac_priv *priv = netdev_priv(dev); if (!priv->dma_cap.eee) return -EOPNOTSUPP; - edata->eee_enabled = priv->eee_enabled; - edata->eee_active = priv->eee_active; edata->tx_lpi_timer = priv->tx_lpi_timer; edata->tx_lpi_enabled = priv->tx_lpi_enabled; @@ -868,7 +911,7 @@ static int stmmac_ethtool_op_get_eee(struct net_device *dev, } static int stmmac_ethtool_op_set_eee(struct net_device *dev, - struct ethtool_eee *edata) + struct ethtool_keee *edata) { struct stmmac_priv *priv = netdev_priv(dev); int ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b334eb16da23..ae2ffa9595d6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2482,7 +2482,6 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) struct xdp_desc xdp_desc; bool work_done = true; u32 tx_set_ic_bit = 0; - unsigned long flags; /* Avoids TX time-out as we are sharing with slow path */ txq_trans_cond_update(nq); @@ -2507,6 +2506,13 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) if (!xsk_tx_peek_desc(pool, &xdp_desc)) break; + if (priv->plat->est && priv->plat->est->enable && + priv->plat->est->max_sdu[queue] && + xdp_desc.len > priv->plat->est->max_sdu[queue]) { + priv->xstats.max_sdu_txq_drop[queue]++; + continue; + } + if (likely(priv->extend_desc)) tx_desc = (struct dma_desc *)(tx_q->dma_etx + entry); else if (tx_q->tbs & STMMAC_TBS_AVAIL) @@ -2566,9 +2572,9 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); entry = tx_q->cur_tx; } - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_set_ic_bit += tx_set_ic_bit; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_add(&txq_stats->napi.tx_set_ic_bit, tx_set_ic_bit); + u64_stats_update_end(&txq_stats->napi_syncp); if (tx_desc) { stmmac_flush_tx_descriptors(priv, queue); @@ -2616,7 +2622,6 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, unsigned int bytes_compl = 0, pkts_compl = 0; unsigned int entry, xmits = 0, count = 0; u32 tx_packets = 0, tx_errors = 0; - unsigned long flags; __netif_tx_lock_bh(netdev_get_tx_queue(priv->dev, queue)); @@ -2782,11 +2787,11 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, if (tx_q->dirty_tx != tx_q->cur_tx) *pending_packets = true; - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_packets += tx_packets; - txq_stats->tx_pkt_n += tx_packets; - txq_stats->tx_clean++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_add(&txq_stats->napi.tx_packets, tx_packets); + u64_stats_add(&txq_stats->napi.tx_pkt_n, tx_packets); + u64_stats_inc(&txq_stats->napi.tx_clean); + u64_stats_update_end(&txq_stats->napi_syncp); priv->xstats.tx_errors += tx_errors; @@ -3592,6 +3597,10 @@ static void stmmac_free_irq(struct net_device *dev, if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) free_irq(priv->wol_irq, dev); fallthrough; + case REQ_IRQ_ERR_SFTY: + if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq) + free_irq(priv->sfty_irq, dev); + fallthrough; case REQ_IRQ_ERR_WOL: free_irq(dev->irq, dev); fallthrough; @@ -3662,6 +3671,23 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev) } } + /* Request the common Safety Feature Correctible/Uncorrectible + * Error line in case of another line is used + */ + if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq) { + int_name = priv->int_name_sfty; + sprintf(int_name, "%s:%s", dev->name, "safety"); + ret = request_irq(priv->sfty_irq, stmmac_safety_interrupt, + 0, int_name, dev); + if (unlikely(ret < 0)) { + netdev_err(priv->dev, + "%s: alloc sfty MSI %d (error: %d)\n", + __func__, priv->sfty_irq, ret); + irq_err = REQ_IRQ_ERR_SFTY; + goto irq_error; + } + } + /* Request the Safety Feature Correctible Error line in * case of another line is used */ @@ -3799,6 +3825,21 @@ static int stmmac_request_irq_single(struct net_device *dev) } } + /* Request the common Safety Feature Correctible/Uncorrectible + * Error line in case of another line is used + */ + if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq) { + ret = request_irq(priv->sfty_irq, stmmac_safety_interrupt, + IRQF_SHARED, dev->name, dev); + if (unlikely(ret < 0)) { + netdev_err(priv->dev, + "%s: ERROR: allocating the sfty IRQ %d (%d)\n", + __func__, priv->sfty_irq, ret); + irq_err = REQ_IRQ_ERR_SFTY; + goto irq_error; + } + } + return 0; irq_error: @@ -3932,6 +3973,9 @@ static int __stmmac_open(struct net_device *dev, priv->rx_copybreak = STMMAC_RX_COPYBREAK; buf_sz = dma_conf->dma_buf_sz; + for (int i = 0; i < MTL_MAX_TX_QUEUES; i++) + if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN) + dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs; memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); stmmac_reset_queues_param(priv); @@ -4210,7 +4254,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) struct stmmac_tx_queue *tx_q; bool has_vlan, set_ic; u8 proto_hdr_len, hdr; - unsigned long flags; u32 pay_len, mss; dma_addr_t des; int i; @@ -4375,13 +4418,13 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_bytes += skb->len; - txq_stats->tx_tso_frames++; - txq_stats->tx_tso_nfrags += nfrags; + u64_stats_update_begin(&txq_stats->q_syncp); + u64_stats_add(&txq_stats->q.tx_bytes, skb->len); + u64_stats_inc(&txq_stats->q.tx_tso_frames); + u64_stats_add(&txq_stats->q.tx_tso_nfrags, nfrags); if (set_ic) - txq_stats->tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_inc(&txq_stats->q.tx_set_ic_bit); + u64_stats_update_end(&txq_stats->q_syncp); if (priv->sarc_type) stmmac_set_desc_sarc(priv, first, priv->sarc_type); @@ -4480,7 +4523,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) struct stmmac_tx_queue *tx_q; bool has_vlan, set_ic; int entry, first_tx; - unsigned long flags; dma_addr_t des; tx_q = &priv->dma_conf.tx_queue[queue]; @@ -4498,6 +4540,13 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) return stmmac_tso_xmit(skb, dev); } + if (priv->plat->est && priv->plat->est->enable && + priv->plat->est->max_sdu[queue] && + skb->len > priv->plat->est->max_sdu[queue]){ + priv->xstats.max_sdu_txq_drop[queue]++; + goto max_sdu_err; + } + if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) { if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, @@ -4650,11 +4699,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_bytes += skb->len; + u64_stats_update_begin(&txq_stats->q_syncp); + u64_stats_add(&txq_stats->q.tx_bytes, skb->len); if (set_ic) - txq_stats->tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_inc(&txq_stats->q.tx_set_ic_bit); + u64_stats_update_end(&txq_stats->q_syncp); if (priv->sarc_type) stmmac_set_desc_sarc(priv, first, priv->sarc_type); @@ -4715,6 +4764,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) dma_map_err: netdev_err(priv->dev, "Tx DMA map failed\n"); +max_sdu_err: dev_kfree_skb(skb); priv->xstats.tx_dropped++; return NETDEV_TX_OK; @@ -4871,6 +4921,13 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, if (stmmac_tx_avail(priv, queue) < STMMAC_TX_THRESH(priv)) return STMMAC_XDP_CONSUMED; + if (priv->plat->est && priv->plat->est->enable && + priv->plat->est->max_sdu[queue] && + xdpf->len > priv->plat->est->max_sdu[queue]) { + priv->xstats.max_sdu_txq_drop[queue]++; + return STMMAC_XDP_CONSUMED; + } + if (likely(priv->extend_desc)) tx_desc = (struct dma_desc *)(tx_q->dma_etx + entry); else if (tx_q->tbs & STMMAC_TBS_AVAIL) @@ -4918,12 +4975,11 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, set_ic = false; if (set_ic) { - unsigned long flags; tx_q->tx_count_frames = 0; stmmac_set_tx_ic(priv, tx_desc); - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->q_syncp); + u64_stats_inc(&txq_stats->q.tx_set_ic_bit); + u64_stats_update_end(&txq_stats->q_syncp); } stmmac_enable_dma_transmission(priv, priv->ioaddr); @@ -5073,7 +5129,6 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, unsigned int len = xdp->data_end - xdp->data; enum pkt_hash_types hash_type; int coe = priv->hw->rx_csum; - unsigned long flags; struct sk_buff *skb; u32 hash; @@ -5103,10 +5158,10 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, skb_record_rx_queue(skb, queue); napi_gro_receive(&ch->rxtx_napi, skb); - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->rx_pkt_n++; - rxq_stats->rx_bytes += len; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_inc(&rxq_stats->napi.rx_pkt_n); + u64_stats_add(&rxq_stats->napi.rx_bytes, len); + u64_stats_update_end(&rxq_stats->napi_syncp); } static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) @@ -5188,7 +5243,6 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) unsigned int desc_size; struct bpf_prog *prog; bool failure = false; - unsigned long flags; int xdp_status = 0; int status = 0; @@ -5343,9 +5397,9 @@ read_again: stmmac_finalize_xdp_rx(priv, xdp_status); - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->rx_pkt_n += count; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_add(&rxq_stats->napi.rx_pkt_n, count); + u64_stats_update_end(&rxq_stats->napi_syncp); priv->xstats.rx_dropped += rx_dropped; priv->xstats.rx_errors += rx_errors; @@ -5383,7 +5437,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) unsigned int desc_size; struct sk_buff *skb = NULL; struct stmmac_xdp_buff ctx; - unsigned long flags; int xdp_status = 0; int buf_sz; @@ -5643,11 +5696,11 @@ drain_data: stmmac_rx_refill(priv, queue); - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->rx_packets += rx_packets; - rxq_stats->rx_bytes += rx_bytes; - rxq_stats->rx_pkt_n += count; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_add(&rxq_stats->napi.rx_packets, rx_packets); + u64_stats_add(&rxq_stats->napi.rx_bytes, rx_bytes); + u64_stats_add(&rxq_stats->napi.rx_pkt_n, count); + u64_stats_update_end(&rxq_stats->napi_syncp); priv->xstats.rx_dropped += rx_dropped; priv->xstats.rx_errors += rx_errors; @@ -5662,13 +5715,12 @@ static int stmmac_napi_poll_rx(struct napi_struct *napi, int budget) struct stmmac_priv *priv = ch->priv_data; struct stmmac_rxq_stats *rxq_stats; u32 chan = ch->index; - unsigned long flags; int work_done; rxq_stats = &priv->xstats.rxq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_inc(&rxq_stats->napi.poll); + u64_stats_update_end(&rxq_stats->napi_syncp); work_done = stmmac_rx(priv, budget, chan); if (work_done < budget && napi_complete_done(napi, work_done)) { @@ -5690,13 +5742,12 @@ static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget) struct stmmac_txq_stats *txq_stats; bool pending_packets = false; u32 chan = ch->index; - unsigned long flags; int work_done; txq_stats = &priv->xstats.txq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_inc(&txq_stats->napi.poll); + u64_stats_update_end(&txq_stats->napi_syncp); work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets); work_done = min(work_done, budget); @@ -5726,17 +5777,16 @@ static int stmmac_napi_poll_rxtx(struct napi_struct *napi, int budget) struct stmmac_rxq_stats *rxq_stats; struct stmmac_txq_stats *txq_stats; u32 chan = ch->index; - unsigned long flags; rxq_stats = &priv->xstats.rxq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_inc(&rxq_stats->napi.poll); + u64_stats_update_end(&rxq_stats->napi_syncp); txq_stats = &priv->xstats.txq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_inc(&txq_stats->napi.poll); + u64_stats_update_end(&txq_stats->napi_syncp); tx_done = stmmac_tx_clean(priv, budget, chan, &tx_pending_packets); tx_done = min(tx_done, budget); @@ -6011,10 +6061,8 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv) priv->tx_path_in_lpi_mode = false; } - for (queue = 0; queue < queues_count; queue++) { - status = stmmac_host_mtl_irq_status(priv, priv->hw, - queue); - } + for (queue = 0; queue < queues_count; queue++) + stmmac_host_mtl_irq_status(priv, priv->hw, queue); /* PCS link status */ if (priv->hw->pcs && @@ -6049,8 +6097,8 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; - /* Check if a fatal error happened */ - if (stmmac_safety_feat_interrupt(priv)) + /* Check ASP error if it isn't delivered via an individual IRQ */ + if (priv->sfty_irq <= 0 && stmmac_safety_feat_interrupt(priv)) return IRQ_HANDLED; /* To handle Common interrupts */ @@ -7062,10 +7110,13 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 u64 tx_bytes; do { - start = u64_stats_fetch_begin(&txq_stats->syncp); - tx_packets = txq_stats->tx_packets; - tx_bytes = txq_stats->tx_bytes; - } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); + start = u64_stats_fetch_begin(&txq_stats->q_syncp); + tx_bytes = u64_stats_read(&txq_stats->q.tx_bytes); + } while (u64_stats_fetch_retry(&txq_stats->q_syncp, start)); + do { + start = u64_stats_fetch_begin(&txq_stats->napi_syncp); + tx_packets = u64_stats_read(&txq_stats->napi.tx_packets); + } while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start)); stats->tx_packets += tx_packets; stats->tx_bytes += tx_bytes; @@ -7077,10 +7128,10 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 u64 rx_bytes; do { - start = u64_stats_fetch_begin(&rxq_stats->syncp); - rx_packets = rxq_stats->rx_packets; - rx_bytes = rxq_stats->rx_bytes; - } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); + start = u64_stats_fetch_begin(&rxq_stats->napi_syncp); + rx_packets = u64_stats_read(&rxq_stats->napi.rx_packets); + rx_bytes = u64_stats_read(&rxq_stats->napi.rx_bytes); + } while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; @@ -7474,9 +7525,16 @@ int stmmac_dvr_probe(struct device *device, priv->dev = ndev; for (i = 0; i < MTL_MAX_RX_QUEUES; i++) - u64_stats_init(&priv->xstats.rxq_stats[i].syncp); - for (i = 0; i < MTL_MAX_TX_QUEUES; i++) - u64_stats_init(&priv->xstats.txq_stats[i].syncp); + u64_stats_init(&priv->xstats.rxq_stats[i].napi_syncp); + for (i = 0; i < MTL_MAX_TX_QUEUES; i++) { + u64_stats_init(&priv->xstats.txq_stats[i].q_syncp); + u64_stats_init(&priv->xstats.txq_stats[i].napi_syncp); + } + + priv->xstats.pcpu_stats = + devm_netdev_alloc_pcpu_stats(device, struct stmmac_pcpu_stats); + if (!priv->xstats.pcpu_stats) + return -ENOMEM; stmmac_set_ethtool_ops(ndev); priv->pause = pause; @@ -7489,6 +7547,7 @@ int stmmac_dvr_probe(struct device *device, priv->dev->irq = res->irq; priv->wol_irq = res->wol_irq; priv->lpi_irq = res->lpi_irq; + priv->sfty_irq = res->sfty_irq; priv->sfty_ce_irq = res->sfty_ce_irq; priv->sfty_ue_irq = res->sfty_ue_irq; for (i = 0; i < MTL_MAX_RX_QUEUES; i++) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 70eadc83ca68..54797edc9b38 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -743,6 +743,14 @@ int stmmac_get_platform_resources(struct platform_device *pdev, dev_info(&pdev->dev, "IRQ eth_lpi not found\n"); } + stmmac_res->sfty_irq = + platform_get_irq_byname_optional(pdev, "sfty"); + if (stmmac_res->sfty_irq < 0) { + if (stmmac_res->sfty_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(&pdev->dev, "IRQ sfty not found\n"); + } + stmmac_res->addr = devm_platform_ioremap_resource(pdev, 0); return PTR_ERR_OR_ZERO(stmmac_res->addr); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 26fa33e5ec34..cce00719937d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -915,8 +915,30 @@ struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time, return time; } -static int tc_setup_taprio(struct stmmac_priv *priv, - struct tc_taprio_qopt_offload *qopt) +static void tc_taprio_map_maxsdu_txq(struct stmmac_priv *priv, + struct tc_taprio_qopt_offload *qopt) +{ + struct plat_stmmacenet_data *plat = priv->plat; + u32 num_tc = qopt->mqprio.qopt.num_tc; + u32 offset, count, i, j; + + /* QueueMaxSDU received from the driver corresponds to the Linux traffic + * class. Map queueMaxSDU per Linux traffic class to DWMAC Tx queues. + */ + for (i = 0; i < num_tc; i++) { + if (!qopt->max_sdu[i]) + continue; + + offset = qopt->mqprio.qopt.offset[i]; + count = qopt->mqprio.qopt.count[i]; + + for (j = offset; j < offset + count; j++) + plat->est->max_sdu[j] = qopt->max_sdu[i] + ETH_HLEN - ETH_TLEN; + } +} + +static int tc_taprio_configure(struct stmmac_priv *priv, + struct tc_taprio_qopt_offload *qopt) { u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep; struct plat_stmmacenet_data *plat = priv->plat; @@ -968,8 +990,6 @@ static int tc_setup_taprio(struct stmmac_priv *priv, if (qopt->cmd == TAPRIO_CMD_DESTROY) goto disable; - else if (qopt->cmd != TAPRIO_CMD_REPLACE) - return -EOPNOTSUPP; if (qopt->num_entries >= dep) return -EINVAL; @@ -1045,6 +1065,8 @@ static int tc_setup_taprio(struct stmmac_priv *priv, priv->plat->est->ter = qopt->cycle_time_extension; + tc_taprio_map_maxsdu_txq(priv, qopt); + if (fpe && !priv->dma_cap.fpesel) { mutex_unlock(&priv->plat->est->lock); return -EOPNOTSUPP; @@ -1078,6 +1100,11 @@ disable: priv->plat->est->enable = false; stmmac_est_configure(priv, priv, priv->plat->est, priv->plat->clk_ptp_rate); + /* Reset taprio status */ + for (i = 0; i < priv->plat->tx_queues_to_use; i++) { + priv->xstats.max_sdu_txq_drop[i] = 0; + priv->xstats.mtl_est_txq_hlbf[i] = 0; + } mutex_unlock(&priv->plat->est->lock); } @@ -1095,6 +1122,57 @@ disable: return ret; } +static void tc_taprio_stats(struct stmmac_priv *priv, + struct tc_taprio_qopt_offload *qopt) +{ + u64 window_drops = 0; + int i = 0; + + for (i = 0; i < priv->plat->tx_queues_to_use; i++) + window_drops += priv->xstats.max_sdu_txq_drop[i] + + priv->xstats.mtl_est_txq_hlbf[i]; + qopt->stats.window_drops = window_drops; + + /* Transmission overrun doesn't happen for stmmac, hence always 0 */ + qopt->stats.tx_overruns = 0; +} + +static void tc_taprio_queue_stats(struct stmmac_priv *priv, + struct tc_taprio_qopt_offload *qopt) +{ + struct tc_taprio_qopt_queue_stats *q_stats = &qopt->queue_stats; + int queue = qopt->queue_stats.queue; + + q_stats->stats.window_drops = priv->xstats.max_sdu_txq_drop[queue] + + priv->xstats.mtl_est_txq_hlbf[queue]; + + /* Transmission overrun doesn't happen for stmmac, hence always 0 */ + q_stats->stats.tx_overruns = 0; +} + +static int tc_setup_taprio(struct stmmac_priv *priv, + struct tc_taprio_qopt_offload *qopt) +{ + int err = 0; + + switch (qopt->cmd) { + case TAPRIO_CMD_REPLACE: + case TAPRIO_CMD_DESTROY: + err = tc_taprio_configure(priv, qopt); + break; + case TAPRIO_CMD_STATS: + tc_taprio_stats(priv, qopt); + break; + case TAPRIO_CMD_QUEUE_STATS: + tc_taprio_queue_stats(priv, qopt); + break; + default: + err = -EOPNOTSUPP; + } + + return err; +} + static int tc_setup_etf(struct stmmac_priv *priv, struct tc_etf_qopt_offload *qopt) { @@ -1126,6 +1204,7 @@ static int tc_query_caps(struct stmmac_priv *priv, return -EOPNOTSUPP; caps->gate_mask_per_txq = true; + caps->supports_queue_max_sdu = true; return 0; } diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index be01450c20dc..1530d13984d4 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -189,6 +189,7 @@ config TI_ICSSG_PRUETH select TI_K3_CPPI_DESC_POOL depends on PRU_REMOTEPROC depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER + depends on PTP_1588_CLOCK_OPTIONAL help Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem. This subsystem is available starting with the AM65 platform. diff --git a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c index 35fceba01ea4..d6ce2c9f0a8d 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c +++ b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c @@ -514,14 +514,14 @@ am65_cpsw_set_link_ksettings(struct net_device *ndev, return phylink_ethtool_ksettings_set(salve->phylink, ecmd); } -static int am65_cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +static int am65_cpsw_get_eee(struct net_device *ndev, struct ethtool_keee *edata) { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); return phylink_ethtool_get_eee(salve->phylink, edata); } -static int am65_cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +static int am65_cpsw_set_eee(struct net_device *ndev, struct ethtool_keee *edata) { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); diff --git a/drivers/net/ethernet/ti/cpsw-common.c b/drivers/net/ethernet/ti/cpsw-common.c index 26dc906eae90..57fe936bb177 100644 --- a/drivers/net/ethernet/ti/cpsw-common.c +++ b/drivers/net/ethernet/ti/cpsw-common.c @@ -90,4 +90,5 @@ int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr) } EXPORT_SYMBOL_GPL(ti_cm_get_macid); +MODULE_DESCRIPTION("TI CPSW Switch common module"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index ea85c6dd5484..c0a5abd8d9a8 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -631,6 +631,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) } } + phy->mac_managed_pm = true; + slave->phy = phy; phy_attached_info(slave->phy); diff --git a/drivers/net/ethernet/ti/cpsw_ethtool.c b/drivers/net/ethernet/ti/cpsw_ethtool.c index a557a477d039..f7b283353ba2 100644 --- a/drivers/net/ethernet/ti/cpsw_ethtool.c +++ b/drivers/net/ethernet/ti/cpsw_ethtool.c @@ -422,7 +422,7 @@ int cpsw_set_link_ksettings(struct net_device *ndev, return phy_ethtool_ksettings_set(cpsw->slaves[slave_no].phy, ecmd); } -int cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +int cpsw_get_eee(struct net_device *ndev, struct ethtool_keee *edata) { struct cpsw_priv *priv = netdev_priv(ndev); struct cpsw_common *cpsw = priv->cpsw; @@ -434,7 +434,7 @@ int cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata) return -EOPNOTSUPP; } -int cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +int cpsw_set_eee(struct net_device *ndev, struct ethtool_keee *edata) { struct cpsw_priv *priv = netdev_priv(ndev); struct cpsw_common *cpsw = priv->cpsw; diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c index 498c50c6d1a7..087dcb67505a 100644 --- a/drivers/net/ethernet/ti/cpsw_new.c +++ b/drivers/net/ethernet/ti/cpsw_new.c @@ -773,6 +773,9 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) slave->slave_num); return; } + + phy->mac_managed_pm = true; + slave->phy = phy; phy_attached_info(slave->phy); diff --git a/drivers/net/ethernet/ti/cpsw_priv.h b/drivers/net/ethernet/ti/cpsw_priv.h index 0e27c433098d..7efa72502c86 100644 --- a/drivers/net/ethernet/ti/cpsw_priv.h +++ b/drivers/net/ethernet/ti/cpsw_priv.h @@ -496,8 +496,8 @@ int cpsw_get_link_ksettings(struct net_device *ndev, struct ethtool_link_ksettings *ecmd); int cpsw_set_link_ksettings(struct net_device *ndev, const struct ethtool_link_ksettings *ecmd); -int cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata); -int cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata); +int cpsw_get_eee(struct net_device *ndev, struct ethtool_keee *edata); +int cpsw_set_eee(struct net_device *ndev, struct ethtool_keee *edata); int cpsw_nway_reset(struct net_device *ndev); void cpsw_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *ering, diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index bcccf43d368b..dbbea9146040 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -638,6 +638,16 @@ static void cpts_calc_mult_shift(struct cpts *cpts) freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); } +static void cpts_clk_unregister(void *clk) +{ + clk_hw_unregister_mux(clk); +} + +static void cpts_clk_del_provider(void *np) +{ + of_clk_del_provider(np); +} + static int cpts_of_mux_clk_setup(struct cpts *cpts, struct device_node *node) { struct device_node *refclk_np; @@ -687,9 +697,7 @@ static int cpts_of_mux_clk_setup(struct cpts *cpts, struct device_node *node) goto mux_fail; } - ret = devm_add_action_or_reset(cpts->dev, - (void(*)(void *))clk_hw_unregister_mux, - clk_hw); + ret = devm_add_action_or_reset(cpts->dev, cpts_clk_unregister, clk_hw); if (ret) { dev_err(cpts->dev, "add clkmux unreg action %d", ret); goto mux_fail; @@ -699,8 +707,7 @@ static int cpts_of_mux_clk_setup(struct cpts *cpts, struct device_node *node) if (ret) goto mux_fail; - ret = devm_add_action_or_reset(cpts->dev, - (void(*)(void *))of_clk_del_provider, + ret = devm_add_action_or_reset(cpts->dev, cpts_clk_del_provider, refclk_np); if (ret) { dev_err(cpts->dev, "add clkmux provider unreg action %d", ret); diff --git a/drivers/net/ethernet/ti/icssg/icssg_ethtool.c b/drivers/net/ethernet/ti/icssg/icssg_ethtool.c index a27ec1dcc8d5..9a7dd7efcf69 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_ethtool.c +++ b/drivers/net/ethernet/ti/icssg/icssg_ethtool.c @@ -45,7 +45,7 @@ static int emac_set_link_ksettings(struct net_device *ndev, return phy_ethtool_set_link_ksettings(ndev, ecmd); } -static int emac_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +static int emac_get_eee(struct net_device *ndev, struct ethtool_keee *edata) { if (!ndev->phydev) return -EOPNOTSUPP; @@ -53,7 +53,7 @@ static int emac_get_eee(struct net_device *ndev, struct ethtool_eee *edata) return phy_ethtool_get_eee(ndev->phydev, edata); } -static int emac_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +static int emac_set_eee(struct net_device *ndev, struct ethtool_keee *edata) { if (!ndev->phydev) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c index 411898a4f38c..cf7b73f8f450 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c @@ -1489,9 +1489,6 @@ static int emac_ndo_stop(struct net_device *ndev) /* Destroying the queued work in ndo_stop() */ cancel_delayed_work_sync(&emac->stats_work); - /* stop PRUs */ - prueth_emac_stop(emac); - if (prueth->emacs_initialized == 1) icss_iep_exit(emac->iep); @@ -1502,7 +1499,6 @@ static int emac_ndo_stop(struct net_device *ndev) free_irq(emac->rx_chns.irq[rx_flow], emac); prueth_ndev_del_tx_napi(emac, emac->tx_ch_num); - prueth_cleanup_tx_chns(emac); prueth_cleanup_rx_chns(emac, &emac->rx_chns, max_rx_flows); prueth_cleanup_tx_chns(emac); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 1db754615cca..945c13d1a982 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -1958,8 +1958,6 @@ int wx_sw_init(struct wx *wx) return -ENOMEM; } - wx->msix_in_use = false; - return 0; } EXPORT_SYMBOL(wx_sw_init); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 8706223a6e5a..7cf02ab6de68 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1614,14 +1614,12 @@ static int wx_acquire_msix_vectors(struct wx *wx) /* One for non-queue interrupts */ nvecs += 1; - if (!wx->msix_in_use) { - wx->msix_entry = kcalloc(1, sizeof(struct msix_entry), - GFP_KERNEL); - if (!wx->msix_entry) { - kfree(wx->msix_q_entries); - wx->msix_q_entries = NULL; - return -ENOMEM; - } + wx->msix_entry = kcalloc(1, sizeof(struct msix_entry), + GFP_KERNEL); + if (!wx->msix_entry) { + kfree(wx->msix_q_entries); + wx->msix_q_entries = NULL; + return -ENOMEM; } nvecs = pci_alloc_irq_vectors_affinity(wx->pdev, nvecs, @@ -1931,10 +1929,8 @@ void wx_reset_interrupt_capability(struct wx *wx) if (pdev->msix_enabled) { kfree(wx->msix_q_entries); wx->msix_q_entries = NULL; - if (!wx->msix_in_use) { - kfree(wx->msix_entry); - wx->msix_entry = NULL; - } + kfree(wx->msix_entry); + wx->msix_entry = NULL; } pci_free_irq_vectors(wx->pdev); } diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index b4dc4f341117..1fdeb464d5f4 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1047,7 +1047,6 @@ struct wx { unsigned int queues_per_pool; struct msix_entry *msix_q_entries; struct msix_entry *msix_entry; - bool msix_in_use; struct wx_ring_feature ring_feature[RING_F_ARRAY_SIZE]; /* misc interrupt status block */ diff --git a/drivers/net/ethernet/wangxun/txgbe/Makefile b/drivers/net/ethernet/wangxun/txgbe/Makefile index 7507f762edfe..42718875277c 100644 --- a/drivers/net/ethernet/wangxun/txgbe/Makefile +++ b/drivers/net/ethernet/wangxun/txgbe/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_TXGBE) += txgbe.o txgbe-objs := txgbe_main.o \ txgbe_hw.o \ txgbe_phy.o \ + txgbe_irq.o \ txgbe_ethtool.o diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c new file mode 100644 index 000000000000..b3e3605d1edb --- /dev/null +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */ + +#include <linux/irqdomain.h> +#include <linux/pci.h> + +#include "../libwx/wx_type.h" +#include "../libwx/wx_lib.h" +#include "../libwx/wx_hw.h" +#include "txgbe_type.h" +#include "txgbe_phy.h" +#include "txgbe_irq.h" + +/** + * txgbe_irq_enable - Enable default interrupt generation settings + * @wx: pointer to private structure + * @queues: enable irqs for queues + **/ +void txgbe_irq_enable(struct wx *wx, bool queues) +{ + wr32(wx, WX_PX_MISC_IEN, TXGBE_PX_MISC_IEN_MASK); + + /* unmask interrupt */ + wx_intr_enable(wx, TXGBE_INTR_MISC); + if (queues) + wx_intr_enable(wx, TXGBE_INTR_QALL(wx)); +} + +/** + * txgbe_intr - msi/legacy mode Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + **/ +static irqreturn_t txgbe_intr(int __always_unused irq, void *data) +{ + struct wx_q_vector *q_vector; + struct wx *wx = data; + struct pci_dev *pdev; + u32 eicr; + + q_vector = wx->q_vector[0]; + pdev = wx->pdev; + + eicr = wx_misc_isb(wx, WX_ISB_VEC0); + if (!eicr) { + /* shared interrupt alert! + * the interrupt that we masked before the ICR read. + */ + if (netif_running(wx->netdev)) + txgbe_irq_enable(wx, true); + return IRQ_NONE; /* Not our interrupt */ + } + wx->isb_mem[WX_ISB_VEC0] = 0; + if (!(pdev->msi_enabled)) + wr32(wx, WX_PX_INTA, 1); + + wx->isb_mem[WX_ISB_MISC] = 0; + /* would disable interrupts here but it is auto disabled */ + napi_schedule_irqoff(&q_vector->napi); + + /* re-enable link(maybe) and non-queue interrupts, no flush. + * txgbe_poll will re-enable the queue interrupts + */ + if (netif_running(wx->netdev)) + txgbe_irq_enable(wx, false); + + return IRQ_HANDLED; +} + +/** + * txgbe_request_msix_irqs - Initialize MSI-X interrupts + * @wx: board private structure + * + * Allocate MSI-X vectors and request interrupts from the kernel. + **/ +static int txgbe_request_msix_irqs(struct wx *wx) +{ + struct net_device *netdev = wx->netdev; + int vector, err; + + for (vector = 0; vector < wx->num_q_vectors; vector++) { + struct wx_q_vector *q_vector = wx->q_vector[vector]; + struct msix_entry *entry = &wx->msix_q_entries[vector]; + + if (q_vector->tx.ring && q_vector->rx.ring) + snprintf(q_vector->name, sizeof(q_vector->name) - 1, + "%s-TxRx-%d", netdev->name, entry->entry); + else + /* skip this unused q_vector */ + continue; + + err = request_irq(entry->vector, wx_msix_clean_rings, 0, + q_vector->name, q_vector); + if (err) { + wx_err(wx, "request_irq failed for MSIX interrupt %s Error: %d\n", + q_vector->name, err); + goto free_queue_irqs; + } + } + + return 0; + +free_queue_irqs: + while (vector) { + vector--; + free_irq(wx->msix_q_entries[vector].vector, + wx->q_vector[vector]); + } + wx_reset_interrupt_capability(wx); + return err; +} + +/** + * txgbe_request_irq - initialize interrupts + * @wx: board private structure + * + * Attempt to configure interrupts using the best available + * capabilities of the hardware and kernel. + **/ +int txgbe_request_irq(struct wx *wx) +{ + struct net_device *netdev = wx->netdev; + struct pci_dev *pdev = wx->pdev; + int err; + + if (pdev->msix_enabled) + err = txgbe_request_msix_irqs(wx); + else if (pdev->msi_enabled) + err = request_irq(wx->pdev->irq, &txgbe_intr, 0, + netdev->name, wx); + else + err = request_irq(wx->pdev->irq, &txgbe_intr, IRQF_SHARED, + netdev->name, wx); + + if (err) + wx_err(wx, "request_irq failed, Error %d\n", err); + + return err; +} + +static int txgbe_request_gpio_irq(struct txgbe *txgbe) +{ + txgbe->gpio_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO); + return request_threaded_irq(txgbe->gpio_irq, NULL, + txgbe_gpio_irq_handler, + IRQF_ONESHOT, "txgbe-gpio-irq", txgbe); +} + +static int txgbe_request_link_irq(struct txgbe *txgbe) +{ + txgbe->link_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_LINK); + return request_threaded_irq(txgbe->link_irq, NULL, + txgbe_link_irq_handler, + IRQF_ONESHOT, "txgbe-link-irq", txgbe); +} + +static const struct irq_chip txgbe_irq_chip = { + .name = "txgbe-misc-irq", +}; + +static int txgbe_misc_irq_domain_map(struct irq_domain *d, + unsigned int irq, + irq_hw_number_t hwirq) +{ + struct txgbe *txgbe = d->host_data; + + irq_set_chip_data(irq, txgbe); + irq_set_chip(irq, &txgbe->misc.chip); + irq_set_nested_thread(irq, true); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops txgbe_misc_irq_domain_ops = { + .map = txgbe_misc_irq_domain_map, +}; + +static irqreturn_t txgbe_misc_irq_handle(int irq, void *data) +{ + struct txgbe *txgbe = data; + struct wx *wx = txgbe->wx; + unsigned int nhandled = 0; + unsigned int sub_irq; + u32 eicr; + + eicr = wx_misc_isb(wx, WX_ISB_MISC); + if (eicr & TXGBE_PX_MISC_GPIO) { + sub_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO); + handle_nested_irq(sub_irq); + nhandled++; + } + if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN | + TXGBE_PX_MISC_ETH_AN)) { + sub_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_LINK); + handle_nested_irq(sub_irq); + nhandled++; + } + + wx_intr_enable(wx, TXGBE_INTR_MISC); + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void txgbe_del_irq_domain(struct txgbe *txgbe) +{ + int hwirq, virq; + + for (hwirq = 0; hwirq < txgbe->misc.nirqs; hwirq++) { + virq = irq_find_mapping(txgbe->misc.domain, hwirq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(txgbe->misc.domain); +} + +void txgbe_free_misc_irq(struct txgbe *txgbe) +{ + free_irq(txgbe->gpio_irq, txgbe); + free_irq(txgbe->link_irq, txgbe); + free_irq(txgbe->misc.irq, txgbe); + txgbe_del_irq_domain(txgbe); +} + +int txgbe_setup_misc_irq(struct txgbe *txgbe) +{ + struct wx *wx = txgbe->wx; + int hwirq, err; + + txgbe->misc.nirqs = 2; + txgbe->misc.domain = irq_domain_add_simple(NULL, txgbe->misc.nirqs, 0, + &txgbe_misc_irq_domain_ops, txgbe); + if (!txgbe->misc.domain) + return -ENOMEM; + + for (hwirq = 0; hwirq < txgbe->misc.nirqs; hwirq++) + irq_create_mapping(txgbe->misc.domain, hwirq); + + txgbe->misc.chip = txgbe_irq_chip; + if (wx->pdev->msix_enabled) + txgbe->misc.irq = wx->msix_entry->vector; + else + txgbe->misc.irq = wx->pdev->irq; + + err = request_threaded_irq(txgbe->misc.irq, NULL, + txgbe_misc_irq_handle, + IRQF_ONESHOT, + wx->netdev->name, txgbe); + if (err) + goto del_misc_irq; + + err = txgbe_request_gpio_irq(txgbe); + if (err) + goto free_msic_irq; + + err = txgbe_request_link_irq(txgbe); + if (err) + goto free_gpio_irq; + + return 0; + +free_gpio_irq: + free_irq(txgbe->gpio_irq, txgbe); +free_msic_irq: + free_irq(txgbe->misc.irq, txgbe); +del_misc_irq: + txgbe_del_irq_domain(txgbe); + + return err; +} diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.h new file mode 100644 index 000000000000..b77945e7a0f2 --- /dev/null +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */ + +void txgbe_irq_enable(struct wx *wx, bool queues); +int txgbe_request_irq(struct wx *wx); +void txgbe_free_misc_irq(struct txgbe *txgbe); +int txgbe_setup_misc_irq(struct txgbe *txgbe); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 3b151c410a5c..e67a21294158 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -17,6 +17,7 @@ #include "txgbe_type.h" #include "txgbe_hw.h" #include "txgbe_phy.h" +#include "txgbe_irq.h" #include "txgbe_ethtool.h" char txgbe_driver_name[] = "txgbe"; @@ -76,133 +77,6 @@ static int txgbe_enumerate_functions(struct wx *wx) return physfns; } -/** - * txgbe_irq_enable - Enable default interrupt generation settings - * @wx: pointer to private structure - * @queues: enable irqs for queues - **/ -static void txgbe_irq_enable(struct wx *wx, bool queues) -{ - wr32(wx, WX_PX_MISC_IEN, TXGBE_PX_MISC_IEN_MASK); - - /* unmask interrupt */ - wx_intr_enable(wx, TXGBE_INTR_MISC); - if (queues) - wx_intr_enable(wx, TXGBE_INTR_QALL(wx)); -} - -/** - * txgbe_intr - msi/legacy mode Interrupt Handler - * @irq: interrupt number - * @data: pointer to a network interface device structure - **/ -static irqreturn_t txgbe_intr(int __always_unused irq, void *data) -{ - struct wx_q_vector *q_vector; - struct wx *wx = data; - struct pci_dev *pdev; - u32 eicr; - - q_vector = wx->q_vector[0]; - pdev = wx->pdev; - - eicr = wx_misc_isb(wx, WX_ISB_VEC0); - if (!eicr) { - /* shared interrupt alert! - * the interrupt that we masked before the ICR read. - */ - if (netif_running(wx->netdev)) - txgbe_irq_enable(wx, true); - return IRQ_NONE; /* Not our interrupt */ - } - wx->isb_mem[WX_ISB_VEC0] = 0; - if (!(pdev->msi_enabled)) - wr32(wx, WX_PX_INTA, 1); - - wx->isb_mem[WX_ISB_MISC] = 0; - /* would disable interrupts here but it is auto disabled */ - napi_schedule_irqoff(&q_vector->napi); - - /* re-enable link(maybe) and non-queue interrupts, no flush. - * txgbe_poll will re-enable the queue interrupts - */ - if (netif_running(wx->netdev)) - txgbe_irq_enable(wx, false); - - return IRQ_HANDLED; -} - -/** - * txgbe_request_msix_irqs - Initialize MSI-X interrupts - * @wx: board private structure - * - * Allocate MSI-X vectors and request interrupts from the kernel. - **/ -static int txgbe_request_msix_irqs(struct wx *wx) -{ - struct net_device *netdev = wx->netdev; - int vector, err; - - for (vector = 0; vector < wx->num_q_vectors; vector++) { - struct wx_q_vector *q_vector = wx->q_vector[vector]; - struct msix_entry *entry = &wx->msix_q_entries[vector]; - - if (q_vector->tx.ring && q_vector->rx.ring) - snprintf(q_vector->name, sizeof(q_vector->name) - 1, - "%s-TxRx-%d", netdev->name, entry->entry); - else - /* skip this unused q_vector */ - continue; - - err = request_irq(entry->vector, wx_msix_clean_rings, 0, - q_vector->name, q_vector); - if (err) { - wx_err(wx, "request_irq failed for MSIX interrupt %s Error: %d\n", - q_vector->name, err); - goto free_queue_irqs; - } - } - - return 0; - -free_queue_irqs: - while (vector) { - vector--; - free_irq(wx->msix_q_entries[vector].vector, - wx->q_vector[vector]); - } - wx_reset_interrupt_capability(wx); - return err; -} - -/** - * txgbe_request_irq - initialize interrupts - * @wx: board private structure - * - * Attempt to configure interrupts using the best available - * capabilities of the hardware and kernel. - **/ -static int txgbe_request_irq(struct wx *wx) -{ - struct net_device *netdev = wx->netdev; - struct pci_dev *pdev = wx->pdev; - int err; - - if (pdev->msix_enabled) - err = txgbe_request_msix_irqs(wx); - else if (pdev->msi_enabled) - err = request_irq(wx->pdev->irq, &txgbe_intr, 0, - netdev->name, wx); - else - err = request_irq(wx->pdev->irq, &txgbe_intr, IRQF_SHARED, - netdev->name, wx); - - if (err) - wx_err(wx, "request_irq failed, Error %d\n", err); - - return err; -} - static void txgbe_up_complete(struct wx *wx) { struct net_device *netdev = wx->netdev; @@ -518,6 +392,7 @@ static void txgbe_shutdown(struct pci_dev *pdev) int txgbe_setup_tc(struct net_device *dev, u8 tc) { struct wx *wx = netdev_priv(dev); + struct txgbe *txgbe = wx->priv; /* Hardware has to reinitialize queues and interrupts to * match packet buffer alignment. Unfortunately, the @@ -528,6 +403,7 @@ int txgbe_setup_tc(struct net_device *dev, u8 tc) else txgbe_reset(wx); + txgbe_free_misc_irq(txgbe); wx_clear_interrupt_scheme(wx); if (tc) @@ -536,6 +412,7 @@ int txgbe_setup_tc(struct net_device *dev, u8 tc) netdev_reset_tc(dev); wx_init_interrupt_scheme(wx); + txgbe_setup_misc_irq(txgbe); if (netif_running(dev)) txgbe_open(dev); @@ -751,10 +628,14 @@ static int txgbe_probe(struct pci_dev *pdev, txgbe->wx = wx; wx->priv = txgbe; - err = txgbe_init_phy(txgbe); + err = txgbe_setup_misc_irq(txgbe); if (err) goto err_release_hw; + err = txgbe_init_phy(txgbe); + if (err) + goto err_free_misc_irq; + err = register_netdev(netdev); if (err) goto err_remove_phy; @@ -781,6 +662,8 @@ static int txgbe_probe(struct pci_dev *pdev, err_remove_phy: txgbe_remove_phy(txgbe); +err_free_misc_irq: + txgbe_free_misc_irq(txgbe); err_release_hw: wx_clear_interrupt_scheme(wx); wx_control_hw(wx, false); @@ -813,6 +696,7 @@ static void txgbe_remove(struct pci_dev *pdev) unregister_netdev(netdev); txgbe_remove_phy(txgbe); + txgbe_free_misc_irq(txgbe); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index 1b84d495d14e..bae0a8ee7014 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c @@ -292,6 +292,21 @@ static int txgbe_phylink_init(struct txgbe *txgbe) return 0; } +irqreturn_t txgbe_link_irq_handler(int irq, void *data) +{ + struct txgbe *txgbe = data; + struct wx *wx = txgbe->wx; + u32 status; + bool up; + + status = rd32(wx, TXGBE_CFG_PORT_ST); + up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP); + + phylink_mac_change(wx->phylink, up); + + return IRQ_HANDLED; +} + static int txgbe_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct wx *wx = gpiochip_get_data(chip); @@ -437,7 +452,7 @@ static int txgbe_gpio_set_type(struct irq_data *d, unsigned int type) } static const struct irq_chip txgbe_gpio_irq_chip = { - .name = "txgbe_gpio_irq", + .name = "txgbe-gpio-irq", .irq_ack = txgbe_gpio_irq_ack, .irq_mask = txgbe_gpio_irq_mask, .irq_unmask = txgbe_gpio_irq_unmask, @@ -446,20 +461,14 @@ static const struct irq_chip txgbe_gpio_irq_chip = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; -static void txgbe_irq_handler(struct irq_desc *desc) +irqreturn_t txgbe_gpio_irq_handler(int irq, void *data) { - struct irq_chip *chip = irq_desc_get_chip(desc); - struct wx *wx = irq_desc_get_handler_data(desc); - struct txgbe *txgbe = wx->priv; + struct txgbe *txgbe = data; + struct wx *wx = txgbe->wx; irq_hw_number_t hwirq; unsigned long gpioirq; struct gpio_chip *gc; unsigned long flags; - u32 eicr; - - eicr = wx_misc_isb(wx, WX_ISB_MISC); - - chained_irq_enter(chip, desc); gpioirq = rd32(wx, WX_GPIO_INTSTATUS); @@ -468,7 +477,7 @@ static void txgbe_irq_handler(struct irq_desc *desc) int gpio = irq_find_mapping(gc->irq.domain, hwirq); u32 irq_type = irq_get_trigger_type(gpio); - generic_handle_domain_irq(gc->irq.domain, hwirq); + handle_nested_irq(gpio); if ((irq_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { raw_spin_lock_irqsave(&wx->gpio_lock, flags); @@ -477,17 +486,7 @@ static void txgbe_irq_handler(struct irq_desc *desc) } } - chained_irq_exit(chip, desc); - - if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN | - TXGBE_PX_MISC_ETH_AN)) { - u32 reg = rd32(wx, TXGBE_CFG_PORT_ST); - - phylink_mac_change(wx->phylink, !!(reg & TXGBE_CFG_PORT_ST_LINK_UP)); - } - - /* unmask interrupt */ - wx_intr_enable(wx, TXGBE_INTR_MISC); + return IRQ_HANDLED; } static int txgbe_gpio_init(struct txgbe *txgbe) @@ -524,19 +523,6 @@ static int txgbe_gpio_init(struct txgbe *txgbe) girq = &gc->irq; gpio_irq_chip_set_chip(girq, &txgbe_gpio_irq_chip); - girq->parent_handler = txgbe_irq_handler; - girq->parent_handler_data = wx; - girq->num_parents = 1; - girq->parents = devm_kcalloc(dev, girq->num_parents, - sizeof(*girq->parents), GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; - - /* now only suuported on MSI-X interrupt */ - if (!wx->msix_entry) - return -EPERM; - - girq->parents[0] = wx->msix_entry->vector; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; @@ -754,8 +740,6 @@ int txgbe_init_phy(struct txgbe *txgbe) goto err_unregister_i2c; } - wx->msix_in_use = true; - return 0; err_unregister_i2c: @@ -788,5 +772,4 @@ void txgbe_remove_phy(struct txgbe *txgbe) phylink_destroy(txgbe->wx->phylink); xpcs_destroy(txgbe->xpcs); software_node_unregister_node_group(txgbe->nodes.group); - txgbe->wx->msix_in_use = false; } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h index 1ab592124986..9855d44076cb 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h @@ -4,6 +4,8 @@ #ifndef _TXGBE_PHY_H_ #define _TXGBE_PHY_H_ +irqreturn_t txgbe_gpio_irq_handler(int irq, void *data); +irqreturn_t txgbe_link_irq_handler(int irq, void *data); int txgbe_init_phy(struct txgbe *txgbe); void txgbe_remove_phy(struct txgbe *txgbe); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 270a6fd9ad0b..1b4ff50d5857 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -5,6 +5,7 @@ #define _TXGBE_TYPE_H_ #include <linux/property.h> +#include <linux/irq.h> /* Device IDs */ #define TXGBE_DEV_ID_SP1000 0x1001 @@ -169,15 +170,31 @@ struct txgbe_nodes { const struct software_node *group[SWNODE_MAX + 1]; }; +enum txgbe_misc_irqs { + TXGBE_IRQ_GPIO = 0, + TXGBE_IRQ_LINK, + TXGBE_IRQ_MAX +}; + +struct txgbe_irq { + struct irq_chip chip; + struct irq_domain *domain; + int nirqs; + int irq; +}; + struct txgbe { struct wx *wx; struct txgbe_nodes nodes; + struct txgbe_irq misc; struct dw_xpcs *xpcs; struct platform_device *sfp_dev; struct platform_device *i2c_dev; struct clk_lookup *clock; struct clk *clk; struct gpio_chip *gpio; + unsigned int gpio_irq; + unsigned int link_irq; }; #endif /* _TXGBE_TYPE_H_ */ diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index 3318b50a5911..f165616f36fe 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -539,8 +539,7 @@ static int w5300_hw_probe(struct platform_device *pdev) eth_hw_addr_random(ndev); } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, mem); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 765aa516aada..940452d0a4d2 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -1114,8 +1114,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev) ndev->irq = rc; - res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - lp->base_addr = devm_ioremap_resource(&ofdev->dev, res); + lp->base_addr = devm_platform_get_and_ioremap_resource(ofdev, 0, &res); if (IS_ERR(lp->base_addr)) { rc = PTR_ERR(lp->base_addr); goto error; diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index 9f505cf02d96..e9bc38fd2025 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1240,9 +1240,7 @@ do_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); SelectPage(0); PutWord(XIRCREG0_TRS, (u_short)pktlen+2); - freespace = GetWord(XIRCREG0_TSO); - okay = freespace & 0x8000; - freespace &= 0x7fff; + freespace = GetWord(XIRCREG0_TSO) & 0x7fff; /* TRS doesn't work - (indeed it is eliminated with sil-rev 1) */ okay = pktlen +2 < freespace; pr_debug("%s: avail. tx space=%u%s\n", diff --git a/drivers/net/fddi/skfp/skfddi.c b/drivers/net/fddi/skfp/skfddi.c index 2b6a607ac0b7..a273362c9e70 100644 --- a/drivers/net/fddi/skfp/skfddi.c +++ b/drivers/net/fddi/skfp/skfddi.c @@ -153,6 +153,7 @@ static const struct pci_device_id skfddi_pci_tbl[] = { { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(pci, skfddi_pci_tbl); +MODULE_DESCRIPTION("SysKonnect FDDI PCI driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mirko Lindner <mlindner@syskonnect.de>"); diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 32c51c244153..6f3f9b446b1d 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -335,6 +335,7 @@ static int geneve_init(struct net_device *dev) gro_cells_destroy(&geneve->gro_cells); return err; } + netdev_lockdep_set_classes(dev); return 0; } @@ -1141,7 +1142,7 @@ static const struct ethtool_ops geneve_ethtool_ops = { }; /* Info for udev, that this is a virtual tunnel endpoint */ -static struct device_type geneve_type = { +static const struct device_type geneve_type = { .name = "geneve", }; @@ -1900,29 +1901,26 @@ static void geneve_destroy_tunnels(struct net *net, struct list_head *head) } } -static void __net_exit geneve_exit_batch_net(struct list_head *net_list) +static void __net_exit geneve_exit_batch_rtnl(struct list_head *net_list, + struct list_head *dev_to_kill) { struct net *net; - LIST_HEAD(list); - rtnl_lock(); list_for_each_entry(net, net_list, exit_list) - geneve_destroy_tunnels(net, &list); - - /* unregister the devices gathered above */ - unregister_netdevice_many(&list); - rtnl_unlock(); + geneve_destroy_tunnels(net, dev_to_kill); +} - list_for_each_entry(net, net_list, exit_list) { - const struct geneve_net *gn = net_generic(net, geneve_net_id); +static void __net_exit geneve_exit_net(struct net *net) +{ + const struct geneve_net *gn = net_generic(net, geneve_net_id); - WARN_ON_ONCE(!list_empty(&gn->sock_list)); - } + WARN_ON_ONCE(!list_empty(&gn->sock_list)); } static struct pernet_operations geneve_net_ops = { .init = geneve_init_net, - .exit_batch = geneve_exit_batch_net, + .exit_batch_rtnl = geneve_exit_batch_rtnl, + .exit = geneve_exit_net, .id = &geneve_net_id, .size = sizeof(struct geneve_net), }; diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index b1919278e931..62c601d9f752 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1876,23 +1876,23 @@ static int __net_init gtp_net_init(struct net *net) return 0; } -static void __net_exit gtp_net_exit(struct net *net) +static void __net_exit gtp_net_exit_batch_rtnl(struct list_head *net_list, + struct list_head *dev_to_kill) { - struct gtp_net *gn = net_generic(net, gtp_net_id); - struct gtp_dev *gtp; - LIST_HEAD(list); + struct net *net; - rtnl_lock(); - list_for_each_entry(gtp, &gn->gtp_dev_list, list) - gtp_dellink(gtp->dev, &list); + list_for_each_entry(net, net_list, exit_list) { + struct gtp_net *gn = net_generic(net, gtp_net_id); + struct gtp_dev *gtp; - unregister_netdevice_many(&list); - rtnl_unlock(); + list_for_each_entry(gtp, &gn->gtp_dev_list, list) + gtp_dellink(gtp->dev, dev_to_kill); + } } static struct pernet_operations gtp_net_ops = { .init = gtp_net_init, - .exit = gtp_net_exit, + .exit_batch_rtnl = gtp_net_exit_batch_rtnl, .id = >p_net_id, .size = sizeof(struct gtp_net), }; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 1dafa44155d0..a6fcbda64ecc 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -708,7 +708,10 @@ void netvsc_device_remove(struct hv_device *device) /* Disable NAPI and disassociate its context from the device. */ for (i = 0; i < net_device->num_chn; i++) { /* See also vmbus_reset_channel_cb(). */ - napi_disable(&net_device->chan_table[i].napi); + /* only disable enabled NAPI channel */ + if (i < ndev->real_num_rx_queues) + napi_disable(&net_device->chan_table[i].napi); + netif_napi_del(&net_device->chan_table[i].napi); } diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 273bd8a20122..11831a1c9762 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -42,6 +42,10 @@ #define LINKCHANGE_INT (2 * HZ) #define VF_TAKEOVER_INT (HZ / 10) +/* Macros to define the context of vf registration */ +#define VF_REG_IN_PROBE 1 +#define VF_REG_IN_NOTIFIER 2 + static unsigned int ring_size __ro_after_init = 128; module_param(ring_size, uint, 0444); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of 4K pages)"); @@ -2185,7 +2189,7 @@ static rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb) } static int netvsc_vf_join(struct net_device *vf_netdev, - struct net_device *ndev) + struct net_device *ndev, int context) { struct net_device_context *ndev_ctx = netdev_priv(ndev); int ret; @@ -2208,7 +2212,11 @@ static int netvsc_vf_join(struct net_device *vf_netdev, goto upper_link_failed; } - schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); + /* If this registration is called from probe context vf_takeover + * is taken care of later in probe itself. + */ + if (context == VF_REG_IN_NOTIFIER) + schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); @@ -2346,7 +2354,7 @@ static int netvsc_prepare_bonding(struct net_device *vf_netdev) return NOTIFY_DONE; } -static int netvsc_register_vf(struct net_device *vf_netdev) +static int netvsc_register_vf(struct net_device *vf_netdev, int context) { struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; @@ -2386,7 +2394,7 @@ static int netvsc_register_vf(struct net_device *vf_netdev) netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); - if (netvsc_vf_join(vf_netdev, ndev) != 0) + if (netvsc_vf_join(vf_netdev, ndev, context) != 0) return NOTIFY_DONE; dev_hold(vf_netdev); @@ -2484,10 +2492,31 @@ static int netvsc_unregister_vf(struct net_device *vf_netdev) return NOTIFY_OK; } +static int check_dev_is_matching_vf(struct net_device *event_ndev) +{ + /* Skip NetVSC interfaces */ + if (event_ndev->netdev_ops == &device_ops) + return -ENODEV; + + /* Avoid non-Ethernet type devices */ + if (event_ndev->type != ARPHRD_ETHER) + return -ENODEV; + + /* Avoid Vlan dev with same MAC registering as VF */ + if (is_vlan_dev(event_ndev)) + return -ENODEV; + + /* Avoid Bonding master dev with same MAC registering as VF */ + if (netif_is_bond_master(event_ndev)) + return -ENODEV; + + return 0; +} + static int netvsc_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) { - struct net_device *net = NULL; + struct net_device *net = NULL, *vf_netdev; struct net_device_context *net_device_ctx; struct netvsc_device_info *device_info = NULL; struct netvsc_device *nvdev; @@ -2599,6 +2628,30 @@ static int netvsc_probe(struct hv_device *dev, } list_add(&net_device_ctx->list, &netvsc_dev_list); + + /* When the hv_netvsc driver is unloaded and reloaded, the + * NET_DEVICE_REGISTER for the vf device is replayed before probe + * is complete. This is because register_netdevice_notifier() gets + * registered before vmbus_driver_register() so that callback func + * is set before probe and we don't miss events like NETDEV_POST_INIT + * So, in this section we try to register the matching vf device that + * is present as a netdevice, knowing that its register call is not + * processed in the netvsc_netdev_notifier(as probing is progress and + * get_netvsc_byslot fails). + */ + for_each_netdev(dev_net(net), vf_netdev) { + ret = check_dev_is_matching_vf(vf_netdev); + if (ret != 0) + continue; + + if (net != get_netvsc_byslot(vf_netdev)) + continue; + + netvsc_prepare_bonding(vf_netdev); + netvsc_register_vf(vf_netdev, VF_REG_IN_PROBE); + __netvsc_vf_setup(net, vf_netdev); + break; + } rtnl_unlock(); netvsc_devinfo_put(device_info); @@ -2754,28 +2807,17 @@ static int netvsc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); + int ret = 0; - /* Skip our own events */ - if (event_dev->netdev_ops == &device_ops) - return NOTIFY_DONE; - - /* Avoid non-Ethernet type devices */ - if (event_dev->type != ARPHRD_ETHER) - return NOTIFY_DONE; - - /* Avoid Vlan dev with same MAC registering as VF */ - if (is_vlan_dev(event_dev)) - return NOTIFY_DONE; - - /* Avoid Bonding master dev with same MAC registering as VF */ - if (netif_is_bond_master(event_dev)) + ret = check_dev_is_matching_vf(event_dev); + if (ret != 0) return NOTIFY_DONE; switch (event) { case NETDEV_POST_INIT: return netvsc_prepare_bonding(event_dev); case NETDEV_REGISTER: - return netvsc_register_vf(event_dev); + return netvsc_register_vf(event_dev, VF_REG_IN_NOTIFIER); case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c index 35e55f198e05..2930141d7dd2 100644 --- a/drivers/net/ieee802154/fakelb.c +++ b/drivers/net/ieee802154/fakelb.c @@ -259,4 +259,5 @@ static __exit void fake_remove_module(void) module_init(fakelb_init_module); module_exit(fake_remove_module); +MODULE_DESCRIPTION("IEEE 802.15.4 loopback driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ipa/ipa_modem.c b/drivers/net/ipa/ipa_modem.c index 423422a2a445..1d1be92fbebc 100644 --- a/drivers/net/ipa/ipa_modem.c +++ b/drivers/net/ipa/ipa_modem.c @@ -39,10 +39,14 @@ enum ipa_modem_state { /** * struct ipa_priv - IPA network device private data * @ipa: IPA pointer + * @tx: Transmit endpoint pointer + * @rx: Receive endpoint pointer * @work: Work structure used to wake the modem netdev TX queue */ struct ipa_priv { struct ipa *ipa; + struct ipa_endpoint *tx; + struct ipa_endpoint *rx; struct work_struct work; }; @@ -59,11 +63,11 @@ static int ipa_open(struct net_device *netdev) if (ret < 0) goto err_power_put; - ret = ipa_endpoint_enable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); + ret = ipa_endpoint_enable_one(priv->tx); if (ret) goto err_power_put; - ret = ipa_endpoint_enable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); + ret = ipa_endpoint_enable_one(priv->rx); if (ret) goto err_disable_tx; @@ -75,7 +79,7 @@ static int ipa_open(struct net_device *netdev) return 0; err_disable_tx: - ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); + ipa_endpoint_disable_one(priv->tx); err_power_put: pm_runtime_put_noidle(dev); @@ -97,8 +101,8 @@ static int ipa_stop(struct net_device *netdev) netif_stop_queue(netdev); - ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); - ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); + ipa_endpoint_disable_one(priv->rx); + ipa_endpoint_disable_one(priv->tx); out_power_put: pm_runtime_mark_last_busy(dev); (void)pm_runtime_put_autosuspend(dev); @@ -106,13 +110,16 @@ out_power_put: return 0; } -/** ipa_start_xmit() - Transmits an skb. - * @skb: skb to be transmitted - * @dev: network device +/** ipa_start_xmit() - Transmit an skb + * @skb: Socket buffer to be transmitted + * @netdev: Network device * - * Return codes: - * NETDEV_TX_OK: Success - * NETDEV_TX_BUSY: Error while transmitting the skb. Try again later + * Return: NETDEV_TX_OK if successful (or dropped), NETDEV_TX_BUSY otherwise + + * Normally NETDEV_TX_OK indicates the buffer was successfully transmitted. + * If the buffer has an unexpected protocol or its size is out of range it + * is quietly dropped, returning NETDEV_TX_OK. NETDEV_TX_BUSY indicates + * the buffer cannot be sent at this time and should retried later. */ static netdev_tx_t ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev) @@ -132,29 +139,41 @@ ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev) if (endpoint->config.qmap && skb->protocol != htons(ETH_P_MAP)) goto err_drop_skb; - /* The hardware must be powered for us to transmit */ + /* The hardware must be powered for us to transmit, so if we're not + * ready we want the network stack to stop queueing until power is + * ACTIVE. Once runtime resume has completed, we inform the network + * stack it's OK to try transmitting again. + * + * We learn from pm_runtime_get() whether the hardware is powered. + * If it was not, powering up is either started or already underway. + * And in that case we want to disable queueing, expecting it to be + * re-enabled once power is ACTIVE. But runtime PM and network + * transmit run concurrently, and if we're not careful the requests + * to stop and start queueing could occur in the wrong order. + * + * For that reason we *always* stop queueing here, *before* the call + * to pm_runtime_get(). If we determine here that power is ACTIVE, + * we restart queueing before transmitting the SKB. Otherwise + * queueing will eventually be enabled after resume completes. + */ + netif_stop_queue(netdev); + dev = &ipa->pdev->dev; ret = pm_runtime_get(dev); if (ret < 1) { /* If a resume won't happen, just drop the packet */ if (ret < 0 && ret != -EINPROGRESS) { - ipa_power_modem_queue_active(ipa); + netif_wake_queue(netdev); pm_runtime_put_noidle(dev); goto err_drop_skb; } - /* No power (yet). Stop the network stack from transmitting - * until we're resumed; ipa_modem_resume() arranges for the - * TX queue to be started again. - */ - ipa_power_modem_queue_stop(ipa); - pm_runtime_put_noidle(dev); return NETDEV_TX_BUSY; } - ipa_power_modem_queue_active(ipa); + netif_wake_queue(netdev); ret = ipa_endpoint_skb_tx(endpoint, skb); @@ -233,14 +252,14 @@ static void ipa_modem_netdev_setup(struct net_device *netdev) */ void ipa_modem_suspend(struct net_device *netdev) { - struct ipa_priv *priv = netdev_priv(netdev); - struct ipa *ipa = priv->ipa; + struct ipa_priv *priv; if (!(netdev->flags & IFF_UP)) return; - ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); - ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); + priv = netdev_priv(netdev); + ipa_endpoint_suspend_one(priv->rx); + ipa_endpoint_suspend_one(priv->tx); } /** @@ -258,7 +277,7 @@ static void ipa_modem_wake_queue_work(struct work_struct *work) { struct ipa_priv *priv = container_of(work, struct ipa_priv, work); - ipa_power_modem_queue_wake(priv->ipa); + netif_wake_queue(priv->tx->netdev); } /** ipa_modem_resume() - resume callback for runtime_pm @@ -268,14 +287,14 @@ static void ipa_modem_wake_queue_work(struct work_struct *work) */ void ipa_modem_resume(struct net_device *netdev) { - struct ipa_priv *priv = netdev_priv(netdev); - struct ipa *ipa = priv->ipa; + struct ipa_priv *priv; if (!(netdev->flags & IFF_UP)) return; - ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); - ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); + priv = netdev_priv(netdev); + ipa_endpoint_resume_one(priv->tx); + ipa_endpoint_resume_one(priv->rx); /* Arrange for the TX queue to be restarted */ (void)queue_pm_work(&priv->work); @@ -306,16 +325,21 @@ int ipa_modem_start(struct ipa *ipa) SET_NETDEV_DEV(netdev, &ipa->pdev->dev); priv = netdev_priv(netdev); priv->ipa = ipa; + priv->tx = ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]; + priv->rx = ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]; INIT_WORK(&priv->work, ipa_modem_wake_queue_work); - ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = netdev; - ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = netdev; + + priv->tx->netdev = netdev; + priv->rx->netdev = netdev; + ipa->modem_netdev = netdev; ret = register_netdev(netdev); if (ret) { ipa->modem_netdev = NULL; - ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = NULL; - ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = NULL; + priv->rx->netdev = NULL; + priv->tx->netdev = NULL; + free_netdev(netdev); } @@ -355,9 +379,11 @@ int ipa_modem_stop(struct ipa *ipa) if (netdev->flags & IFF_UP) (void)ipa_stop(netdev); unregister_netdev(netdev); + ipa->modem_netdev = NULL; - ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = NULL; - ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = NULL; + priv->rx->netdev = NULL; + priv->tx->netdev = NULL; + free_netdev(netdev); } diff --git a/drivers/net/ipa/ipa_power.c b/drivers/net/ipa/ipa_power.c index e223886123ce..128a816f6523 100644 --- a/drivers/net/ipa/ipa_power.c +++ b/drivers/net/ipa/ipa_power.c @@ -38,15 +38,11 @@ * enum ipa_power_flag - IPA power flags * @IPA_POWER_FLAG_RESUMED: Whether resume from suspend has been signaled * @IPA_POWER_FLAG_SYSTEM: Hardware is system (not runtime) suspended - * @IPA_POWER_FLAG_STOPPED: Modem TX is disabled by ipa_start_xmit() - * @IPA_POWER_FLAG_STARTED: Modem TX was enabled by ipa_runtime_resume() * @IPA_POWER_FLAG_COUNT: Number of defined power flags */ enum ipa_power_flag { IPA_POWER_FLAG_RESUMED, IPA_POWER_FLAG_SYSTEM, - IPA_POWER_FLAG_STOPPED, - IPA_POWER_FLAG_STARTED, IPA_POWER_FLAG_COUNT, /* Last; not a flag */ }; @@ -55,7 +51,6 @@ enum ipa_power_flag { * @dev: IPA device pointer * @core: IPA core clock * @qmp: QMP handle for AOSS communication - * @spinlock: Protects modem TX queue enable/disable * @flags: Boolean state flags * @interconnect_count: Number of elements in interconnect[] * @interconnect: Interconnect array @@ -64,7 +59,6 @@ struct ipa_power { struct device *dev; struct clk *core; struct qmp *qmp; - spinlock_t spinlock; /* used with STOPPED/STARTED power flags */ DECLARE_BITMAP(flags, IPA_POWER_FLAG_COUNT); u32 interconnect_count; struct icc_bulk_data interconnect[] __counted_by(interconnect_count); @@ -233,70 +227,6 @@ void ipa_power_suspend_handler(struct ipa *ipa, enum ipa_irq_id irq_id) ipa_interrupt_suspend_clear_all(ipa->interrupt); } -/* The next few functions coordinate stopping and starting the modem - * network device transmit queue. - * - * Transmit can be running concurrent with power resume, and there's a - * chance the resume completes before the transmit path stops the queue, - * leaving the queue in a stopped state. The next two functions are used - * to avoid this: ipa_power_modem_queue_stop() is used by ipa_start_xmit() - * to conditionally stop the TX queue; and ipa_power_modem_queue_start() - * is used by ipa_runtime_resume() to conditionally restart it. - * - * Two flags and a spinlock are used. If the queue is stopped, the STOPPED - * power flag is set. And if the queue is started, the STARTED flag is set. - * The queue is only started on resume if the STOPPED flag is set. And the - * queue is only started in ipa_start_xmit() if the STARTED flag is *not* - * set. As a result, the queue remains operational if the two activites - * happen concurrently regardless of the order they complete. The spinlock - * ensures the flag and TX queue operations are done atomically. - * - * The first function stops the modem netdev transmit queue, but only if - * the STARTED flag is *not* set. That flag is cleared if it was set. - * If the queue is stopped, the STOPPED flag is set. This is called only - * from the power ->runtime_resume operation. - */ -void ipa_power_modem_queue_stop(struct ipa *ipa) -{ - struct ipa_power *power = ipa->power; - unsigned long flags; - - spin_lock_irqsave(&power->spinlock, flags); - - if (!__test_and_clear_bit(IPA_POWER_FLAG_STARTED, power->flags)) { - netif_stop_queue(ipa->modem_netdev); - __set_bit(IPA_POWER_FLAG_STOPPED, power->flags); - } - - spin_unlock_irqrestore(&power->spinlock, flags); -} - -/* This function starts the modem netdev transmit queue, but only if the - * STOPPED flag is set. That flag is cleared if it was set. If the queue - * was restarted, the STARTED flag is set; this allows ipa_start_xmit() - * to skip stopping the queue in the event of a race. - */ -void ipa_power_modem_queue_wake(struct ipa *ipa) -{ - struct ipa_power *power = ipa->power; - unsigned long flags; - - spin_lock_irqsave(&power->spinlock, flags); - - if (__test_and_clear_bit(IPA_POWER_FLAG_STOPPED, power->flags)) { - __set_bit(IPA_POWER_FLAG_STARTED, power->flags); - netif_wake_queue(ipa->modem_netdev); - } - - spin_unlock_irqrestore(&power->spinlock, flags); -} - -/* This function clears the STARTED flag once the TX queue is operating */ -void ipa_power_modem_queue_active(struct ipa *ipa) -{ - clear_bit(IPA_POWER_FLAG_STARTED, ipa->power->flags); -} - static int ipa_power_retention_init(struct ipa_power *power) { struct qmp *qmp = qmp_get(power->dev); @@ -385,7 +315,6 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data) } power->dev = dev; power->core = clk; - spin_lock_init(&power->spinlock); power->interconnect_count = data->interconnect_count; ret = ipa_interconnect_init(power, data->interconnect_data); diff --git a/drivers/net/ipa/ipa_power.h b/drivers/net/ipa/ipa_power.h index 3a4c59ea1222..718aacf5e2b2 100644 --- a/drivers/net/ipa/ipa_power.h +++ b/drivers/net/ipa/ipa_power.h @@ -24,24 +24,6 @@ extern const struct dev_pm_ops ipa_pm_ops; u32 ipa_core_clock_rate(struct ipa *ipa); /** - * ipa_power_modem_queue_stop() - Possibly stop the modem netdev TX queue - * @ipa: IPA pointer - */ -void ipa_power_modem_queue_stop(struct ipa *ipa); - -/** - * ipa_power_modem_queue_wake() - Possibly wake the modem netdev TX queue - * @ipa: IPA pointer - */ -void ipa_power_modem_queue_wake(struct ipa *ipa); - -/** - * ipa_power_modem_queue_active() - Report modem netdev TX queue active - * @ipa: IPA pointer - */ -void ipa_power_modem_queue_active(struct ipa *ipa); - -/** * ipa_power_retention() - Control register retention on power collapse * @ipa: IPA pointer * @enable: Whether retention should be enabled or disabled diff --git a/drivers/net/ipvlan/ipvtap.c b/drivers/net/ipvlan/ipvtap.c index 60944a4beada..1afc4c47be73 100644 --- a/drivers/net/ipvlan/ipvtap.c +++ b/drivers/net/ipvlan/ipvtap.c @@ -237,4 +237,5 @@ static void __exit ipvtap_exit(void) module_exit(ipvtap_exit); MODULE_ALIAS_RTNL_LINK("ipvtap"); MODULE_AUTHOR("Sainath Grandhi <sainath.grandhi@intel.com>"); +MODULE_DESCRIPTION("IP-VLAN based tap driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index f6d53e63ef4e..f6eab66c2660 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -144,6 +144,7 @@ static int loopback_dev_init(struct net_device *dev) dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); if (!dev->lstats) return -ENOMEM; + netdev_lockdep_set_classes(dev); return 0; } diff --git a/drivers/net/mdio/mdio-ipq4019.c b/drivers/net/mdio/mdio-ipq4019.c index abd8b508ec16..9d8f43b28aac 100644 --- a/drivers/net/mdio/mdio-ipq4019.c +++ b/drivers/net/mdio/mdio-ipq4019.c @@ -14,6 +14,20 @@ #include <linux/clk.h> #define MDIO_MODE_REG 0x40 +#define MDIO_MODE_MDC_MODE BIT(12) +/* 0 = Clause 22, 1 = Clause 45 */ +#define MDIO_MODE_C45 BIT(8) +#define MDIO_MODE_DIV_MASK GENMASK(7, 0) +#define MDIO_MODE_DIV(x) FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1) +#define MDIO_MODE_DIV_1 0x0 +#define MDIO_MODE_DIV_2 0x1 +#define MDIO_MODE_DIV_4 0x3 +#define MDIO_MODE_DIV_8 0x7 +#define MDIO_MODE_DIV_16 0xf +#define MDIO_MODE_DIV_32 0x1f +#define MDIO_MODE_DIV_64 0x3f +#define MDIO_MODE_DIV_128 0x7f +#define MDIO_MODE_DIV_256 0xff #define MDIO_ADDR_REG 0x44 #define MDIO_DATA_WRITE_REG 0x48 #define MDIO_DATA_READ_REG 0x4c @@ -26,9 +40,6 @@ #define MDIO_CMD_ACCESS_CODE_C45_WRITE 1 #define MDIO_CMD_ACCESS_CODE_C45_READ 2 -/* 0 = Clause 22, 1 = Clause 45 */ -#define MDIO_MODE_C45 BIT(8) - #define IPQ4019_MDIO_TIMEOUT 10000 #define IPQ4019_MDIO_SLEEP 10 @@ -41,6 +52,7 @@ struct ipq4019_mdio_data { void __iomem *membase; void __iomem *eth_ldo_rdy; struct clk *mdio_clk; + unsigned int mdc_rate; }; static int ipq4019_mdio_wait_busy(struct mii_bus *bus) @@ -203,6 +215,38 @@ static int ipq4019_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum, return 0; } +static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv) +{ + unsigned long ahb_rate; + int div; + u32 val; + + /* If we don't have a clock for AHB use the fixed value */ + ahb_rate = IPQ_MDIO_CLK_RATE; + if (priv->mdio_clk) + ahb_rate = clk_get_rate(priv->mdio_clk); + + /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1) + * While supported, internal documentation doesn't + * assure correct functionality of the MDIO bus + * with divider of 1, 2 or 4. + */ + for (div = 8; div <= 256; div *= 2) { + /* The requested rate is supported by the div */ + if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) { + val = readl(priv->membase + MDIO_MODE_REG); + val &= ~MDIO_MODE_DIV_MASK; + val |= MDIO_MODE_DIV(div); + writel(val, priv->membase + MDIO_MODE_REG); + + return 0; + } + } + + /* The requested rate is not supported */ + return -EINVAL; +} + static int ipq_mdio_reset(struct mii_bus *bus) { struct ipq4019_mdio_data *priv = bus->priv; @@ -225,10 +269,58 @@ static int ipq_mdio_reset(struct mii_bus *bus) return ret; ret = clk_prepare_enable(priv->mdio_clk); - if (ret == 0) - mdelay(10); + if (ret) + return ret; - return ret; + mdelay(10); + + /* Restore MDC rate */ + return ipq4019_mdio_set_div(priv); +} + +static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev, + struct ipq4019_mdio_data *priv) +{ + unsigned long ahb_rate; + int div; + u32 val; + + /* MDC rate defined in DT, we don't have to decide a default value */ + if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &priv->mdc_rate)) + return; + + /* If we don't have a clock for AHB use the fixed value */ + ahb_rate = IPQ_MDIO_CLK_RATE; + if (priv->mdio_clk) + ahb_rate = clk_get_rate(priv->mdio_clk); + + /* Check what is the current div set */ + val = readl(priv->membase + MDIO_MODE_REG); + div = FIELD_GET(MDIO_MODE_DIV_MASK, val); + + /* div is not set to the default value of /256 + * Probably someone changed that (bootloader, other drivers) + * Keep this and don't overwrite it. + */ + if (div != MDIO_MODE_DIV_256) { + priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1); + return; + } + + /* If div is /256 assume nobody have set this value and + * try to find one MDC rate that is close the 802.3 spec of + * 2.5MHz + */ + for (div = 256; div >= 8; div /= 2) { + /* Stop as soon as we found a divider that + * reached the closest value to 2.5MHz + */ + if (DIV_ROUND_UP(ahb_rate, div) > 2500000) + break; + + priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div); + } } static int ipq4019_mdio_probe(struct platform_device *pdev) @@ -252,6 +344,11 @@ static int ipq4019_mdio_probe(struct platform_device *pdev) if (IS_ERR(priv->mdio_clk)) return PTR_ERR(priv->mdio_clk); + ipq4019_mdio_select_mdc_rate(pdev, priv); + ret = ipq4019_mdio_set_div(priv); + if (ret) + return ret; + /* The platform resource is provided on the chipset IPQ5018 */ /* This resource is optional */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c index 64ebcb6d235c..08e607f62e10 100644 --- a/drivers/net/mdio/of_mdio.c +++ b/drivers/net/mdio/of_mdio.c @@ -139,6 +139,53 @@ bool of_mdiobus_child_is_phy(struct device_node *child) } EXPORT_SYMBOL(of_mdiobus_child_is_phy); +static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np, + bool *scanphys) +{ + struct device_node *child; + int addr, rc = 0; + + /* Loop over the child nodes and register a phy_device for each phy */ + for_each_available_child_of_node(np, child) { + if (of_node_name_eq(child, "ethernet-phy-package")) { + /* Ignore invalid ethernet-phy-package node */ + if (!of_property_present(child, "reg")) + continue; + + rc = __of_mdiobus_parse_phys(mdio, child, NULL); + if (rc && rc != -ENODEV) + goto exit; + + continue; + } + + addr = of_mdio_parse_addr(&mdio->dev, child); + if (addr < 0) { + /* Skip scanning for invalid ethernet-phy-package node */ + if (scanphys) + *scanphys = true; + continue; + } + + if (of_mdiobus_child_is_phy(child)) + rc = of_mdiobus_register_phy(mdio, child, addr); + else + rc = of_mdiobus_register_device(mdio, child, addr); + + if (rc == -ENODEV) + dev_err(&mdio->dev, + "MDIO device at address %d is missing.\n", + addr); + else if (rc) + goto exit; + } + + return 0; +exit: + of_node_put(child); + return rc; +} + /** * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree * @mdio: pointer to mii_bus structure @@ -180,33 +227,18 @@ int __of_mdiobus_register(struct mii_bus *mdio, struct device_node *np, return rc; /* Loop over the child nodes and register a phy_device for each phy */ - for_each_available_child_of_node(np, child) { - addr = of_mdio_parse_addr(&mdio->dev, child); - if (addr < 0) { - scanphys = true; - continue; - } - - if (of_mdiobus_child_is_phy(child)) - rc = of_mdiobus_register_phy(mdio, child, addr); - else - rc = of_mdiobus_register_device(mdio, child, addr); - - if (rc == -ENODEV) - dev_err(&mdio->dev, - "MDIO device at address %d is missing.\n", - addr); - else if (rc) - goto unregister; - } + rc = __of_mdiobus_parse_phys(mdio, np, &scanphys); + if (rc) + goto unregister; if (!scanphys) return 0; /* auto scan for PHYs with empty reg property */ for_each_available_child_of_node(np, child) { - /* Skip PHYs with reg property set */ - if (of_property_present(child, "reg")) + /* Skip PHYs with reg property set or ethernet-phy-package node */ + if (of_property_present(child, "reg") || + of_node_name_eq(child, "ethernet-phy-package")) continue; for (addr = 0; addr < PHY_MAX_ADDR; addr++) { @@ -227,15 +259,16 @@ int __of_mdiobus_register(struct mii_bus *mdio, struct device_node *np, if (!rc) break; if (rc != -ENODEV) - goto unregister; + goto put_unregister; } } } return 0; -unregister: +put_unregister: of_node_put(child); +unregister: mdiobus_unregister(mdio); return rc; } diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 6e14ba5e06c8..0de108a1c0c8 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -43,13 +43,17 @@ MODULE_DESCRIPTION("Console driver for network interfaces"); MODULE_LICENSE("GPL"); #define MAX_PARAM_LENGTH 256 +#define MAX_USERDATA_NAME_LENGTH 54 +#define MAX_USERDATA_VALUE_LENGTH 200 +#define MAX_USERDATA_ENTRY_LENGTH 256 +#define MAX_USERDATA_ITEMS 16 #define MAX_PRINT_CHUNK 1000 static char config[MAX_PARAM_LENGTH]; module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0); MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]"); -static bool oops_only = false; +static bool oops_only; module_param(oops_only, bool, 0600); MODULE_PARM_DESC(oops_only, "Only log oops messages"); @@ -79,7 +83,10 @@ static struct console netconsole_ext; /** * struct netconsole_target - Represents a configured netconsole target. * @list: Links this target into the target_list. - * @item: Links us into the configfs subsystem hierarchy. + * @group: Links us into the configfs subsystem hierarchy. + * @userdata_group: Links to the userdata configfs hierarchy + * @userdata_complete: Cached, formatted string of append + * @userdata_length: String length of userdata_complete * @enabled: On / off knob to enable / disable target. * Visible from userspace (read-write). * We maintain a strict 1:1 correspondence between this and @@ -102,7 +109,10 @@ static struct console netconsole_ext; struct netconsole_target { struct list_head list; #ifdef CONFIG_NETCONSOLE_DYNAMIC - struct config_item item; + struct config_group group; + struct config_group userdata_group; + char userdata_complete[MAX_USERDATA_ENTRY_LENGTH * MAX_USERDATA_ITEMS]; + size_t userdata_length; #endif bool enabled; bool extended; @@ -134,14 +144,14 @@ static void __exit dynamic_netconsole_exit(void) */ static void netconsole_target_get(struct netconsole_target *nt) { - if (config_item_name(&nt->item)) - config_item_get(&nt->item); + if (config_item_name(&nt->group.cg_item)) + config_group_get(&nt->group); } static void netconsole_target_put(struct netconsole_target *nt) { - if (config_item_name(&nt->item)) - config_item_put(&nt->item); + if (config_item_name(&nt->group.cg_item)) + config_group_put(&nt->group); } #else /* !CONFIG_NETCONSOLE_DYNAMIC */ @@ -215,15 +225,33 @@ static struct netconsole_target *alloc_and_init(void) * | remote_ip * | local_mac * | remote_mac + * | userdata/ + * | <key>/ + * | value + * | ... * | * <target>/... */ static struct netconsole_target *to_target(struct config_item *item) { - return item ? - container_of(item, struct netconsole_target, item) : - NULL; + struct config_group *cfg_group; + + cfg_group = to_config_group(item); + if (!cfg_group) + return NULL; + return container_of(to_config_group(item), + struct netconsole_target, group); +} + +/* Get rid of possible trailing newline, returning the new length */ +static void trim_newline(char *s, size_t maxlen) +{ + size_t len; + + len = strnlen(s, maxlen); + if (s[len - 1] == '\n') + s[len - 1] = '\0'; } /* @@ -370,7 +398,7 @@ static ssize_t release_store(struct config_item *item, const char *buf, mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); err = -EINVAL; goto out_unlock; } @@ -398,7 +426,7 @@ static ssize_t extended_store(struct config_item *item, const char *buf, mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); err = -EINVAL; goto out_unlock; } @@ -420,22 +448,17 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf, size_t count) { struct netconsole_target *nt = to_target(item); - size_t len; mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); mutex_unlock(&dynamic_netconsole_mutex); return -EINVAL; } strscpy(nt->np.dev_name, buf, IFNAMSIZ); - - /* Get rid of possible trailing newline from echo(1) */ - len = strnlen(nt->np.dev_name, IFNAMSIZ); - if (nt->np.dev_name[len - 1] == '\n') - nt->np.dev_name[len - 1] = '\0'; + trim_newline(nt->np.dev_name, IFNAMSIZ); mutex_unlock(&dynamic_netconsole_mutex); return strnlen(buf, count); @@ -450,7 +473,7 @@ static ssize_t local_port_store(struct config_item *item, const char *buf, mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); goto out_unlock; } @@ -473,7 +496,7 @@ static ssize_t remote_port_store(struct config_item *item, mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); goto out_unlock; } @@ -495,12 +518,13 @@ static ssize_t local_ip_store(struct config_item *item, const char *buf, mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); goto out_unlock; } if (strnchr(buf, count, ':')) { const char *end; + if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) { if (*end && *end != '\n') { pr_err("invalid IPv6 address at: <%c>\n", *end); @@ -510,9 +534,9 @@ static ssize_t local_ip_store(struct config_item *item, const char *buf, } else goto out_unlock; } else { - if (!nt->np.ipv6) { + if (!nt->np.ipv6) nt->np.local_ip.ip = in_aton(buf); - } else + else goto out_unlock; } @@ -531,12 +555,13 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf, mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); goto out_unlock; } if (strnchr(buf, count, ':')) { const char *end; + if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) { if (*end && *end != '\n') { pr_err("invalid IPv6 address at: <%c>\n", *end); @@ -546,9 +571,9 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf, } else goto out_unlock; } else { - if (!nt->np.ipv6) { + if (!nt->np.ipv6) nt->np.remote_ip.ip = in_aton(buf); - } else + else goto out_unlock; } @@ -568,7 +593,7 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf, mutex_lock(&dynamic_netconsole_mutex); if (nt->enabled) { pr_err("target (%s) is enabled, disable to update parameters\n", - config_item_name(&nt->item)); + config_item_name(&nt->group.cg_item)); goto out_unlock; } @@ -585,6 +610,180 @@ out_unlock: return -EINVAL; } +struct userdatum { + struct config_item item; + char value[MAX_USERDATA_VALUE_LENGTH]; +}; + +static struct userdatum *to_userdatum(struct config_item *item) +{ + return container_of(item, struct userdatum, item); +} + +struct userdata { + struct config_group group; +}; + +static struct userdata *to_userdata(struct config_item *item) +{ + return container_of(to_config_group(item), struct userdata, group); +} + +static struct netconsole_target *userdata_to_target(struct userdata *ud) +{ + struct config_group *netconsole_group; + + netconsole_group = to_config_group(ud->group.cg_item.ci_parent); + return to_target(&netconsole_group->cg_item); +} + +static ssize_t userdatum_value_show(struct config_item *item, char *buf) +{ + return sysfs_emit(buf, "%s\n", &(to_userdatum(item)->value[0])); +} + +static void update_userdata(struct netconsole_target *nt) +{ + int complete_idx = 0, child_count = 0; + struct list_head *entry; + + /* Clear the current string in case the last userdatum was deleted */ + nt->userdata_length = 0; + nt->userdata_complete[0] = 0; + + list_for_each(entry, &nt->userdata_group.cg_children) { + struct userdatum *udm_item; + struct config_item *item; + + if (child_count >= MAX_USERDATA_ITEMS) + break; + child_count++; + + item = container_of(entry, struct config_item, ci_entry); + udm_item = to_userdatum(item); + + /* Skip userdata with no value set */ + if (strnlen(udm_item->value, MAX_USERDATA_VALUE_LENGTH) == 0) + continue; + + /* This doesn't overflow userdata_complete since it will write + * one entry length (1/MAX_USERDATA_ITEMS long), entry count is + * checked to not exceed MAX items with child_count above + */ + complete_idx += scnprintf(&nt->userdata_complete[complete_idx], + MAX_USERDATA_ENTRY_LENGTH, "%s=%s\n", + item->ci_name, udm_item->value); + } + nt->userdata_length = strnlen(nt->userdata_complete, + sizeof(nt->userdata_complete)); +} + +static ssize_t userdatum_value_store(struct config_item *item, const char *buf, + size_t count) +{ + struct userdatum *udm = to_userdatum(item); + struct netconsole_target *nt; + struct userdata *ud; + int ret; + + if (count > MAX_USERDATA_VALUE_LENGTH) + return -EMSGSIZE; + + mutex_lock(&dynamic_netconsole_mutex); + + ret = strscpy(udm->value, buf, sizeof(udm->value)); + if (ret < 0) + goto out_unlock; + trim_newline(udm->value, sizeof(udm->value)); + + ud = to_userdata(item->ci_parent); + nt = userdata_to_target(ud); + update_userdata(nt); + + mutex_unlock(&dynamic_netconsole_mutex); + return count; +out_unlock: + mutex_unlock(&dynamic_netconsole_mutex); + return ret; +} + +CONFIGFS_ATTR(userdatum_, value); + +static struct configfs_attribute *userdatum_attrs[] = { + &userdatum_attr_value, + NULL, +}; + +static void userdatum_release(struct config_item *item) +{ + kfree(to_userdatum(item)); +} + +static struct configfs_item_operations userdatum_ops = { + .release = userdatum_release, +}; + +static const struct config_item_type userdatum_type = { + .ct_item_ops = &userdatum_ops, + .ct_attrs = userdatum_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *userdatum_make_item(struct config_group *group, + const char *name) +{ + struct netconsole_target *nt; + struct userdatum *udm; + struct userdata *ud; + size_t child_count; + + if (strlen(name) > MAX_USERDATA_NAME_LENGTH) + return ERR_PTR(-ENAMETOOLONG); + + ud = to_userdata(&group->cg_item); + nt = userdata_to_target(ud); + child_count = list_count_nodes(&nt->userdata_group.cg_children); + if (child_count >= MAX_USERDATA_ITEMS) + return ERR_PTR(-ENOSPC); + + udm = kzalloc(sizeof(*udm), GFP_KERNEL); + if (!udm) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&udm->item, name, &userdatum_type); + return &udm->item; +} + +static void userdatum_drop(struct config_group *group, struct config_item *item) +{ + struct netconsole_target *nt; + struct userdata *ud; + + ud = to_userdata(&group->cg_item); + nt = userdata_to_target(ud); + + mutex_lock(&dynamic_netconsole_mutex); + update_userdata(nt); + config_item_put(item); + mutex_unlock(&dynamic_netconsole_mutex); +} + +static struct configfs_attribute *userdata_attrs[] = { + NULL, +}; + +static struct configfs_group_operations userdata_ops = { + .make_item = userdatum_make_item, + .drop_item = userdatum_drop, +}; + +static struct config_item_type userdata_type = { + .ct_item_ops = &userdatum_ops, + .ct_group_ops = &userdata_ops, + .ct_attrs = userdata_attrs, + .ct_owner = THIS_MODULE, +}; + CONFIGFS_ATTR(, enabled); CONFIGFS_ATTR(, extended); CONFIGFS_ATTR(, dev_name); @@ -629,6 +828,15 @@ static const struct config_item_type netconsole_target_type = { .ct_owner = THIS_MODULE, }; +static void init_target_config_group(struct netconsole_target *nt, + const char *name) +{ + config_group_init_type_name(&nt->group, name, &netconsole_target_type); + config_group_init_type_name(&nt->userdata_group, "userdata", + &userdata_type); + configfs_add_default_group(&nt->userdata_group, &nt->group); +} + static struct netconsole_target *find_cmdline_target(const char *name) { struct netconsole_target *nt, *ret = NULL; @@ -636,7 +844,7 @@ static struct netconsole_target *find_cmdline_target(const char *name) spin_lock_irqsave(&target_list_lock, flags); list_for_each_entry(nt, &target_list, list) { - if (!strcmp(nt->item.ci_name, name)) { + if (!strcmp(nt->group.cg_item.ci_name, name)) { ret = nt; break; } @@ -650,8 +858,8 @@ static struct netconsole_target *find_cmdline_target(const char *name) * Group operations and type for netconsole_subsys. */ -static struct config_item *make_netconsole_target(struct config_group *group, - const char *name) +static struct config_group *make_netconsole_target(struct config_group *group, + const char *name) { struct netconsole_target *nt; unsigned long flags; @@ -663,23 +871,25 @@ static struct config_item *make_netconsole_target(struct config_group *group, if (!strncmp(name, NETCONSOLE_PARAM_TARGET_PREFIX, strlen(NETCONSOLE_PARAM_TARGET_PREFIX))) { nt = find_cmdline_target(name); - if (nt) - return &nt->item; + if (nt) { + init_target_config_group(nt, name); + return &nt->group; + } } nt = alloc_and_init(); if (!nt) return ERR_PTR(-ENOMEM); - /* Initialize the config_item member */ - config_item_init_type_name(&nt->item, name, &netconsole_target_type); + /* Initialize the config_group member */ + init_target_config_group(nt, name); /* Adding, but it is disabled */ spin_lock_irqsave(&target_list_lock, flags); list_add(&nt->list, &target_list); spin_unlock_irqrestore(&target_list_lock, flags); - return &nt->item; + return &nt->group; } static void drop_netconsole_target(struct config_group *group, @@ -699,11 +909,11 @@ static void drop_netconsole_target(struct config_group *group, if (nt->enabled) netpoll_cleanup(&nt->np); - config_item_put(&nt->item); + config_item_put(&nt->group.cg_item); } static struct configfs_group_operations netconsole_subsys_group_ops = { - .make_item = make_netconsole_target, + .make_group = make_netconsole_target, .drop_item = drop_netconsole_target, }; @@ -729,8 +939,7 @@ static void populate_configfs_item(struct netconsole_target *nt, snprintf(target_name, sizeof(target_name), "%s%d", NETCONSOLE_PARAM_TARGET_PREFIX, cmdline_count); - config_item_init_type_name(&nt->item, target_name, - &netconsole_target_type); + init_target_config_group(nt, target_name); } #endif /* CONFIG_NETCONSOLE_DYNAMIC */ @@ -781,6 +990,7 @@ restart: spin_unlock_irqrestore(&target_list_lock, flags); if (stopped) { const char *msg = "had an event"; + switch (event) { case NETDEV_UNREGISTER: msg = "unregistered"; @@ -824,19 +1034,34 @@ static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, const char *msg_ready = msg; const char *release; int release_len = 0; + int userdata_len = 0; + char *userdata = NULL; + +#ifdef CONFIG_NETCONSOLE_DYNAMIC + userdata = nt->userdata_complete; + userdata_len = nt->userdata_length; +#endif if (nt->release) { release = init_utsname()->release; release_len = strlen(release) + 1; } - if (msg_len + release_len <= MAX_PRINT_CHUNK) { + if (msg_len + release_len + userdata_len <= MAX_PRINT_CHUNK) { /* No fragmentation needed */ if (nt->release) { scnprintf(buf, MAX_PRINT_CHUNK, "%s,%s", release, msg); msg_len += release_len; - msg_ready = buf; + } else { + memcpy(buf, msg, msg_len); } + + if (userdata) + msg_len += scnprintf(&buf[msg_len], + MAX_PRINT_CHUNK - msg_len, + "%s", userdata); + + msg_ready = buf; netpoll_send_udp(&nt->np, msg_ready, msg_len); return; } @@ -860,24 +1085,48 @@ static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, memcpy(buf + release_len, header, header_len); header_len += release_len; - while (offset < body_len) { + while (offset < body_len + userdata_len) { int this_header = header_len; - int this_chunk; + int this_offset = 0; + int this_chunk = 0; this_header += scnprintf(buf + this_header, sizeof(buf) - this_header, - ",ncfrag=%d/%d;", offset, body_len); - - this_chunk = min(body_len - offset, - MAX_PRINT_CHUNK - this_header); - if (WARN_ON_ONCE(this_chunk <= 0)) - return; - - memcpy(buf + this_header, body + offset, this_chunk); - - netpoll_send_udp(&nt->np, buf, this_header + this_chunk); + ",ncfrag=%d/%d;", offset, + body_len + userdata_len); + + /* Not all body data has been written yet */ + if (offset < body_len) { + this_chunk = min(body_len - offset, + MAX_PRINT_CHUNK - this_header); + if (WARN_ON_ONCE(this_chunk <= 0)) + return; + memcpy(buf + this_header, body + offset, this_chunk); + this_offset += this_chunk; + } + /* Body is fully written and there is pending userdata to write, + * append userdata in this chunk + */ + if (offset + this_offset >= body_len && + offset + this_offset < userdata_len + body_len) { + int sent_userdata = (offset + this_offset) - body_len; + int preceding_bytes = this_chunk + this_header; + + if (WARN_ON_ONCE(sent_userdata < 0)) + return; + + this_chunk = min(userdata_len - sent_userdata, + MAX_PRINT_CHUNK - preceding_bytes); + if (WARN_ON_ONCE(this_chunk <= 0)) + return; + memcpy(buf + this_header + this_offset, + userdata + sent_userdata, + this_chunk); + this_offset += this_chunk; + } - offset += this_chunk; + netpoll_send_udp(&nt->np, buf, this_header + this_offset); + offset += this_offset; } } diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c index bcbc1e19edde..0c5aff63d242 100644 --- a/drivers/net/netdevsim/bus.c +++ b/drivers/net/netdevsim/bus.c @@ -129,7 +129,7 @@ static void nsim_bus_dev_release(struct device *dev) complete(&nsim_bus_devs_released); } -static struct device_type nsim_bus_dev_type = { +static const struct device_type nsim_bus_dev_type = { .groups = nsim_bus_dev_attr_groups, .release = nsim_bus_dev_release, }; @@ -260,7 +260,7 @@ static int nsim_num_vf(struct device *dev) return nsim_bus_dev->num_vfs; } -static struct bus_type nsim_bus = { +static const struct bus_type nsim_bus = { .name = DRV_NAME, .dev_name = DRV_NAME, .bus_groups = nsim_bus_groups, diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index b4d3b9cde8bd..92a7a36b93ac 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -835,14 +835,14 @@ static void nsim_dev_trap_report_work(struct work_struct *work) trap_report_dw.work); nsim_dev = nsim_trap_data->nsim_dev; - /* For each running port and enabled packet trap, generate a UDP - * packet with a random 5-tuple and report it. - */ if (!devl_trylock(priv_to_devlink(nsim_dev))) { - schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 0); + schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 1); return; } + /* For each running port and enabled packet trap, generate a UDP + * packet with a random 5-tuple and report it. + */ list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { if (!netif_running(nsim_dev_port->ns->netdev)) continue; diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c index dc3962b2aa6b..853b8c138718 100644 --- a/drivers/net/pcs/pcs-lynx.c +++ b/drivers/net/pcs/pcs-lynx.c @@ -398,4 +398,5 @@ void lynx_pcs_destroy(struct phylink_pcs *pcs) } EXPORT_SYMBOL(lynx_pcs_destroy); +MODULE_DESCRIPTION("NXP Lynx PCS phylink library"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c index 8501dd365279..4f63abe638c4 100644 --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c @@ -303,4 +303,5 @@ void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs) } EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); +MODULE_DESCRIPTION("MediaTek SGMII library for LynxI"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 31f0beba638a..52a7757ee419 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -1456,4 +1456,5 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, } EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); +MODULE_DESCRIPTION("Synopsys DesignWare XPCS library"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 9e2672800f0b..e261e58bf158 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -335,12 +335,7 @@ config NCN26000_PHY Currently supports the NCN26000 10BASE-T1S Industrial PHY with MII interface. -config AT803X_PHY - tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" - depends on REGULATOR - help - Currently supports the AR8030, AR8031, AR8033, AR8035 and internal - QCA8337(Internal qca8k PHY) model +source "drivers/net/phy/qcom/Kconfig" config QSEMI_PHY tristate "Quality Semiconductor PHYs" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 6097afd44392..197acfa0b412 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -36,7 +36,6 @@ obj-$(CONFIG_ADIN_PHY) += adin.o obj-$(CONFIG_ADIN1100_PHY) += adin1100.o obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ -obj-$(CONFIG_AT803X_PHY) += at803x.o ifdef CONFIG_AX88796B_RUST_PHY obj-$(CONFIG_AX88796B_PHY) += ax88796b_rust.o else @@ -91,6 +90,7 @@ endif obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja.o obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o +obj-y += qcom/ obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_RENESAS_PHY) += uPD60620.o diff --git a/drivers/net/phy/adin1100.c b/drivers/net/phy/adin1100.c index 7619d6185801..85f910e2d4fb 100644 --- a/drivers/net/phy/adin1100.c +++ b/drivers/net/phy/adin1100.c @@ -18,6 +18,12 @@ #define PHY_ID_ADIN1110 0x0283bc91 #define PHY_ID_ADIN2111 0x0283bca1 +#define ADIN_PHY_SUBSYS_IRQ_MASK 0x0021 +#define ADIN_LINK_STAT_CHNG_IRQ_EN BIT(1) + +#define ADIN_PHY_SUBSYS_IRQ_STATUS 0x0011 +#define ADIN_LINK_STAT_CHNG BIT(1) + #define ADIN_FORCED_MODE 0x8000 #define ADIN_FORCED_MODE_EN BIT(0) @@ -136,6 +142,53 @@ static int adin_config_aneg(struct phy_device *phydev) return genphy_c45_config_aneg(phydev); } +static int adin_phy_ack_intr(struct phy_device *phydev) +{ + /* Clear pending interrupts */ + int rc = phy_read_mmd(phydev, MDIO_MMD_VEND2, + ADIN_PHY_SUBSYS_IRQ_STATUS); + + return rc < 0 ? rc : 0; +} + +static int adin_config_intr(struct phy_device *phydev) +{ + u16 irq_mask; + int ret; + + ret = adin_phy_ack_intr(phydev); + if (ret) + return ret; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + irq_mask = ADIN_LINK_STAT_CHNG_IRQ_EN; + else + irq_mask = 0; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + ADIN_PHY_SUBSYS_IRQ_MASK, + ADIN_LINK_STAT_CHNG_IRQ_EN, irq_mask); +} + +static irqreturn_t adin_phy_handle_interrupt(struct phy_device *phydev) +{ + int irq_status; + + irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND2, + ADIN_PHY_SUBSYS_IRQ_STATUS); + if (irq_status < 0) { + phy_error(phydev); + return IRQ_NONE; + } + + if (!(irq_status & ADIN_LINK_STAT_CHNG)) + return IRQ_NONE; + + phy_trigger_machine(phydev); + + return IRQ_HANDLED; +} + static int adin_set_powerdown_mode(struct phy_device *phydev, bool en) { int ret; @@ -275,6 +328,8 @@ static struct phy_driver adin_driver[] = { .probe = adin_probe, .config_aneg = adin_config_aneg, .read_status = adin_read_status, + .config_intr = adin_config_intr, + .handle_interrupt = adin_phy_handle_interrupt, .set_loopback = adin_set_loopback, .suspend = adin_suspend, .resume = adin_resume, diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 97a2fafa15ca..71bfddb8f453 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -22,9 +22,13 @@ #define PHY_ID_AQR107 0x03a1b4e0 #define PHY_ID_AQCS109 0x03a1b5c2 #define PHY_ID_AQR405 0x03a1b4b0 +#define PHY_ID_AQR111 0x03a1b610 +#define PHY_ID_AQR111B0 0x03a1b612 #define PHY_ID_AQR112 0x03a1b662 #define PHY_ID_AQR412 0x03a1b712 +#define PHY_ID_AQR113 0x31c31c40 #define PHY_ID_AQR113C 0x31c31c12 +#define PHY_ID_AQR813 0x31c31cb2 #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) @@ -727,6 +731,15 @@ static int aqr113c_config_init(struct phy_device *phydev) if (ret < 0) return ret; + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, + MDIO_PMD_TXDIS_GLOBAL); + if (ret) + return ret; + + ret = aqr107_wait_processor_intensive_op(phydev); + if (ret) + return ret; + return aqr107_fill_interface_modes(phydev); } @@ -746,6 +759,16 @@ static int aqr107_probe(struct phy_device *phydev) return aqr_hwmon_probe(phydev); } +static int aqr111_config_init(struct phy_device *phydev) +{ + /* AQR111 reports supporting speed up to 10G, + * however only speeds up to 5G are supported. + */ + phy_set_max_speed(phydev, SPEED_5000); + + return aqr107_config_init(phydev); +} + static struct phy_driver aqr_driver[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), @@ -820,6 +843,44 @@ static struct phy_driver aqr_driver[] = { .link_change_notify = aqr107_link_change_notify, }, { + PHY_ID_MATCH_MODEL(PHY_ID_AQR111), + .name = "Aquantia AQR111", + .probe = aqr107_probe, + .get_rate_matching = aqr107_get_rate_matching, + .config_init = aqr111_config_init, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr107_read_status, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr107_suspend, + .resume = aqr107_resume, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, +}, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0), + .name = "Aquantia AQR111B0", + .probe = aqr107_probe, + .get_rate_matching = aqr107_get_rate_matching, + .config_init = aqr111_config_init, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr107_read_status, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr107_suspend, + .resume = aqr107_resume, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, +}, +{ PHY_ID_MATCH_MODEL(PHY_ID_AQR405), .name = "Aquantia AQR405", .config_aneg = aqr_config_aneg, @@ -864,6 +925,25 @@ static struct phy_driver aqr_driver[] = { .link_change_notify = aqr107_link_change_notify, }, { + PHY_ID_MATCH_MODEL(PHY_ID_AQR113), + .name = "Aquantia AQR113", + .probe = aqr107_probe, + .get_rate_matching = aqr107_get_rate_matching, + .config_init = aqr113c_config_init, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr107_read_status, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr107_suspend, + .resume = aqr107_resume, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, +}, +{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), .name = "Aquantia AQR113C", .probe = aqr107_probe, @@ -882,6 +962,25 @@ static struct phy_driver aqr_driver[] = { .get_stats = aqr107_get_stats, .link_change_notify = aqr107_link_change_notify, }, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR813), + .name = "Aquantia AQR813", + .probe = aqr107_probe, + .get_rate_matching = aqr107_get_rate_matching, + .config_init = aqr107_config_init, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr107_read_status, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr107_suspend, + .resume = aqr107_resume, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, +}, }; module_phy_driver(aqr_driver); @@ -894,9 +993,13 @@ static struct mdio_device_id __maybe_unused aqr_tbl[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, { } }; diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c deleted file mode 100644 index a62442a55774..000000000000 --- a/drivers/net/phy/at803x.c +++ /dev/null @@ -1,2432 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * drivers/net/phy/at803x.c - * - * Driver for Qualcomm Atheros AR803x PHY - * - * Author: Matus Ujhelyi <ujhelyi.m@gmail.com> - */ - -#include <linux/phy.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/ethtool_netlink.h> -#include <linux/bitfield.h> -#include <linux/regulator/of_regulator.h> -#include <linux/regulator/driver.h> -#include <linux/regulator/consumer.h> -#include <linux/of.h> -#include <linux/phylink.h> -#include <linux/sfp.h> -#include <dt-bindings/net/qca-ar803x.h> - -#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 -#define AT803X_SFC_ASSERT_CRS BIT(11) -#define AT803X_SFC_FORCE_LINK BIT(10) -#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) -#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 -#define AT803X_SFC_MANUAL_MDIX 0x1 -#define AT803X_SFC_MANUAL_MDI 0x0 -#define AT803X_SFC_SQE_TEST BIT(2) -#define AT803X_SFC_POLARITY_REVERSAL BIT(1) -#define AT803X_SFC_DISABLE_JABBER BIT(0) - -#define AT803X_SPECIFIC_STATUS 0x11 -#define AT803X_SS_SPEED_MASK GENMASK(15, 14) -#define AT803X_SS_SPEED_1000 2 -#define AT803X_SS_SPEED_100 1 -#define AT803X_SS_SPEED_10 0 -#define AT803X_SS_DUPLEX BIT(13) -#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) -#define AT803X_SS_MDIX BIT(6) - -#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) -#define QCA808X_SS_SPEED_2500 4 - -#define AT803X_INTR_ENABLE 0x12 -#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) -#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) -#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) -#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) -#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) -#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) -#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) -#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) -#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) -#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) -#define AT803X_INTR_ENABLE_WOL BIT(0) - -#define AT803X_INTR_STATUS 0x13 - -#define AT803X_SMART_SPEED 0x14 -#define AT803X_SMART_SPEED_ENABLE BIT(5) -#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) -#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) -#define AT803X_CDT 0x16 -#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) -#define AT803X_CDT_ENABLE_TEST BIT(0) -#define AT803X_CDT_STATUS 0x1c -#define AT803X_CDT_STATUS_STAT_NORMAL 0 -#define AT803X_CDT_STATUS_STAT_SHORT 1 -#define AT803X_CDT_STATUS_STAT_OPEN 2 -#define AT803X_CDT_STATUS_STAT_FAIL 3 -#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) -#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) -#define AT803X_LED_CONTROL 0x18 - -#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 -#define AT803X_WOL_EN BIT(5) -#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C -#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B -#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A -#define AT803X_REG_CHIP_CONFIG 0x1f -#define AT803X_BT_BX_REG_SEL 0x8000 - -#define AT803X_DEBUG_ADDR 0x1D -#define AT803X_DEBUG_DATA 0x1E - -#define AT803X_MODE_CFG_MASK 0x0F -#define AT803X_MODE_CFG_BASET_RGMII 0x00 -#define AT803X_MODE_CFG_BASET_SGMII 0x01 -#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02 -#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03 -#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04 -#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05 -#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06 -#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07 -#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B -#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E -#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F - -#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ -#define AT803X_PSSR_MR_AN_COMPLETE 0x0200 - -#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00 -#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) -#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) -#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) - -#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05 -#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) - -#define AT803X_DEBUG_REG_HIB_CTRL 0x0b -#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) -#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) -#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) - -#define AT803X_DEBUG_REG_3C 0x3C - -#define AT803X_DEBUG_REG_GREEN 0x3D -#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) - -#define AT803X_DEBUG_REG_1F 0x1F -#define AT803X_DEBUG_PLL_ON BIT(2) -#define AT803X_DEBUG_RGMII_1V8 BIT(3) - -#define MDIO_AZ_DEBUG 0x800D - -/* AT803x supports either the XTAL input pad, an internal PLL or the - * DSP as clock reference for the clock output pad. The XTAL reference - * is only used for 25 MHz output, all other frequencies need the PLL. - * The DSP as a clock reference is used in synchronous ethernet - * applications. - * - * By default the PLL is only enabled if there is a link. Otherwise - * the PHY will go into low power state and disabled the PLL. You can - * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always - * enabled. - */ -#define AT803X_MMD7_CLK25M 0x8016 -#define AT803X_CLK_OUT_MASK GENMASK(4, 2) -#define AT803X_CLK_OUT_25MHZ_XTAL 0 -#define AT803X_CLK_OUT_25MHZ_DSP 1 -#define AT803X_CLK_OUT_50MHZ_PLL 2 -#define AT803X_CLK_OUT_50MHZ_DSP 3 -#define AT803X_CLK_OUT_62_5MHZ_PLL 4 -#define AT803X_CLK_OUT_62_5MHZ_DSP 5 -#define AT803X_CLK_OUT_125MHZ_PLL 6 -#define AT803X_CLK_OUT_125MHZ_DSP 7 - -/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask - * but doesn't support choosing between XTAL/PLL and DSP. - */ -#define AT8035_CLK_OUT_MASK GENMASK(4, 3) - -#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) -#define AT803X_CLK_OUT_STRENGTH_FULL 0 -#define AT803X_CLK_OUT_STRENGTH_HALF 1 -#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 - -#define AT803X_DEFAULT_DOWNSHIFT 5 -#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 -#define ATH8032_PHY_ID 0x004dd023 -#define ATH8035_PHY_ID 0x004dd072 -#define AT8030_PHY_ID_MASK 0xffffffef - -#define QCA8081_PHY_ID 0x004dd101 - -#define QCA8327_A_PHY_ID 0x004dd033 -#define QCA8327_B_PHY_ID 0x004dd034 -#define QCA8337_PHY_ID 0x004dd036 -#define QCA9561_PHY_ID 0x004dd042 -#define QCA8K_PHY_ID_MASK 0xffffffff - -#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) - -#define AT803X_PAGE_FIBER 0 -#define AT803X_PAGE_COPPER 1 - -/* don't turn off internal PLL */ -#define AT803X_KEEP_PLL_ENABLED BIT(0) -#define AT803X_DISABLE_SMARTEEE BIT(1) - -/* disable hibernation mode */ -#define AT803X_DISABLE_HIBERNATION_MODE BIT(2) - -/* ADC threshold */ -#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 -#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) -#define QCA808X_ADC_THRESHOLD_80MV 0 -#define QCA808X_ADC_THRESHOLD_100MV 0xf0 -#define QCA808X_ADC_THRESHOLD_200MV 0x0f -#define QCA808X_ADC_THRESHOLD_300MV 0xff - -/* CLD control */ -#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 -#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) -#define QCA808X_8023AZ_AFE_EN 0x90 - -/* AZ control */ -#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 -#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 - -#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 -#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 - -#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E -#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 - -#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E -#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 - -#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 -#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 - -#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c -#define QCA808X_TOP_OPTION1_DATA 0x0 - -#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 -#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 -#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 -#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad -#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 -#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 -#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 -#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 -#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 -#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 -#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 -#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 - -/* master/slave seed config */ -#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 -#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) -#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) -#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 - -/* Hibernation yields lower power consumpiton in contrast with normal operation mode. - * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. - */ -#define QCA808X_DBG_AN_TEST 0xb -#define QCA808X_HIBERNATION_EN BIT(15) - -#define QCA808X_CDT_ENABLE_TEST BIT(15) -#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) -#define QCA808X_CDT_STATUS BIT(11) -#define QCA808X_CDT_LENGTH_UNIT BIT(10) - -#define QCA808X_MMD3_CDT_STATUS 0x8064 -#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 -#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 -#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 -#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 -#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) -#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) - -#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) -#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) -#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) -#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) - -#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) -#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) -#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) -#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) -#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) - -#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) -#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) -#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) -#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) - -/* NORMAL are MDI with type set to 0 */ -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ - QCA808X_CDT_STATUS_STAT_MDI1) -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ - QCA808X_CDT_STATUS_STAT_MDI1) -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ - QCA808X_CDT_STATUS_STAT_MDI2) -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ - QCA808X_CDT_STATUS_STAT_MDI2) -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ - QCA808X_CDT_STATUS_STAT_MDI3) -#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ - QCA808X_CDT_STATUS_STAT_MDI3) - -/* Added for reference of existence but should be handled by wait_for_completion already */ -#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) - -/* QCA808X 1G chip type */ -#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d -#define QCA808X_PHY_CHIP_TYPE_1G BIT(0) - -#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072 -#define QCA8081_PHY_FIFO_RSTN BIT(11) - -MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver"); -MODULE_AUTHOR("Matus Ujhelyi"); -MODULE_LICENSE("GPL"); - -enum stat_access_type { - PHY, - MMD -}; - -struct at803x_hw_stat { - const char *string; - u8 reg; - u32 mask; - enum stat_access_type access_type; -}; - -static struct at803x_hw_stat qca83xx_hw_stats[] = { - { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, - { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, - { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, -}; - -struct at803x_ss_mask { - u16 speed_mask; - u8 speed_shift; -}; - -struct at803x_priv { - int flags; - u16 clk_25m_reg; - u16 clk_25m_mask; - u8 smarteee_lpi_tw_1g; - u8 smarteee_lpi_tw_100m; - bool is_fiber; - bool is_1000basex; - struct regulator_dev *vddio_rdev; - struct regulator_dev *vddh_rdev; - u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; -}; - -struct at803x_context { - u16 bmcr; - u16 advertise; - u16 control1000; - u16 int_enable; - u16 smart_speed; - u16 led_control; -}; - -static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) -{ - int ret; - - ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); - if (ret < 0) - return ret; - - return phy_write(phydev, AT803X_DEBUG_DATA, data); -} - -static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) -{ - int ret; - - ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); - if (ret < 0) - return ret; - - return phy_read(phydev, AT803X_DEBUG_DATA); -} - -static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, - u16 clear, u16 set) -{ - u16 val; - int ret; - - ret = at803x_debug_reg_read(phydev, reg); - if (ret < 0) - return ret; - - val = ret & 0xffff; - val &= ~clear; - val |= set; - - return phy_write(phydev, AT803X_DEBUG_DATA, val); -} - -static int at803x_write_page(struct phy_device *phydev, int page) -{ - int mask; - int set; - - if (page == AT803X_PAGE_COPPER) { - set = AT803X_BT_BX_REG_SEL; - mask = 0; - } else { - set = 0; - mask = AT803X_BT_BX_REG_SEL; - } - - return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); -} - -static int at803x_read_page(struct phy_device *phydev) -{ - int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); - - if (ccr < 0) - return ccr; - - if (ccr & AT803X_BT_BX_REG_SEL) - return AT803X_PAGE_COPPER; - - return AT803X_PAGE_FIBER; -} - -static int at803x_enable_rx_delay(struct phy_device *phydev) -{ - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0, - AT803X_DEBUG_RX_CLK_DLY_EN); -} - -static int at803x_enable_tx_delay(struct phy_device *phydev) -{ - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0, - AT803X_DEBUG_TX_CLK_DLY_EN); -} - -static int at803x_disable_rx_delay(struct phy_device *phydev) -{ - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, - AT803X_DEBUG_RX_CLK_DLY_EN, 0); -} - -static int at803x_disable_tx_delay(struct phy_device *phydev) -{ - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, - AT803X_DEBUG_TX_CLK_DLY_EN, 0); -} - -/* save relevant PHY registers to private copy */ -static void at803x_context_save(struct phy_device *phydev, - struct at803x_context *context) -{ - context->bmcr = phy_read(phydev, MII_BMCR); - context->advertise = phy_read(phydev, MII_ADVERTISE); - context->control1000 = phy_read(phydev, MII_CTRL1000); - context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); - context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); - context->led_control = phy_read(phydev, AT803X_LED_CONTROL); -} - -/* restore relevant PHY registers from private copy */ -static void at803x_context_restore(struct phy_device *phydev, - const struct at803x_context *context) -{ - phy_write(phydev, MII_BMCR, context->bmcr); - phy_write(phydev, MII_ADVERTISE, context->advertise); - phy_write(phydev, MII_CTRL1000, context->control1000); - phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); - phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); - phy_write(phydev, AT803X_LED_CONTROL, context->led_control); -} - -static int at803x_set_wol(struct phy_device *phydev, - struct ethtool_wolinfo *wol) -{ - int ret, irq_enabled; - - if (wol->wolopts & WAKE_MAGIC) { - struct net_device *ndev = phydev->attached_dev; - const u8 *mac; - unsigned int i; - static const unsigned int offsets[] = { - AT803X_LOC_MAC_ADDR_32_47_OFFSET, - AT803X_LOC_MAC_ADDR_16_31_OFFSET, - AT803X_LOC_MAC_ADDR_0_15_OFFSET, - }; - - if (!ndev) - return -ENODEV; - - mac = (const u8 *)ndev->dev_addr; - - if (!is_valid_ether_addr(mac)) - return -EINVAL; - - for (i = 0; i < 3; i++) - phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], - mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); - - /* Enable WOL interrupt */ - ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); - if (ret) - return ret; - } else { - /* Disable WOL interrupt */ - ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); - if (ret) - return ret; - } - - /* Clear WOL status */ - ret = phy_read(phydev, AT803X_INTR_STATUS); - if (ret < 0) - return ret; - - /* Check if there are other interrupts except for WOL triggered when PHY is - * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can - * be passed up to the interrupt PIN. - */ - irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); - if (irq_enabled < 0) - return irq_enabled; - - irq_enabled &= ~AT803X_INTR_ENABLE_WOL; - if (ret & irq_enabled && !phy_polling_mode(phydev)) - phy_trigger_machine(phydev); - - return 0; -} - -static void at803x_get_wol(struct phy_device *phydev, - struct ethtool_wolinfo *wol) -{ - int value; - - wol->supported = WAKE_MAGIC; - wol->wolopts = 0; - - value = phy_read(phydev, AT803X_INTR_ENABLE); - if (value < 0) - return; - - if (value & AT803X_INTR_ENABLE_WOL) - wol->wolopts |= WAKE_MAGIC; -} - -static int qca83xx_get_sset_count(struct phy_device *phydev) -{ - return ARRAY_SIZE(qca83xx_hw_stats); -} - -static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { - strscpy(data + i * ETH_GSTRING_LEN, - qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); - } -} - -static u64 qca83xx_get_stat(struct phy_device *phydev, int i) -{ - struct at803x_hw_stat stat = qca83xx_hw_stats[i]; - struct at803x_priv *priv = phydev->priv; - int val; - u64 ret; - - if (stat.access_type == MMD) - val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); - else - val = phy_read(phydev, stat.reg); - - if (val < 0) { - ret = U64_MAX; - } else { - val = val & stat.mask; - priv->stats[i] += val; - ret = priv->stats[i]; - } - - return ret; -} - -static void qca83xx_get_stats(struct phy_device *phydev, - struct ethtool_stats *stats, u64 *data) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) - data[i] = qca83xx_get_stat(phydev, i); -} - -static int at803x_suspend(struct phy_device *phydev) -{ - int value; - int wol_enabled; - - value = phy_read(phydev, AT803X_INTR_ENABLE); - wol_enabled = value & AT803X_INTR_ENABLE_WOL; - - if (wol_enabled) - value = BMCR_ISOLATE; - else - value = BMCR_PDOWN; - - phy_modify(phydev, MII_BMCR, 0, value); - - return 0; -} - -static int at803x_resume(struct phy_device *phydev) -{ - return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); -} - -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, 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_bool(node, "qca,disable-hibernation-mode")) - priv->flags |= AT803X_DISABLE_HIBERNATION_MODE; - - 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) { - case 25000000: - sel = AT803X_CLK_OUT_25MHZ_XTAL; - break; - case 50000000: - sel = AT803X_CLK_OUT_50MHZ_PLL; - break; - case 62500000: - sel = AT803X_CLK_OUT_62_5MHZ_PLL; - break; - case 125000000: - sel = AT803X_CLK_OUT_125MHZ_PLL; - break; - default: - phydev_err(phydev, "invalid qca,clk-out-frequency\n"); - return -EINVAL; - } - - priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); - priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; - } - - ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); - if (!ret) { - priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; - switch (strength) { - case AR803X_STRENGTH_FULL: - priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; - break; - case AR803X_STRENGTH_HALF: - priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; - break; - case AR803X_STRENGTH_QUARTER: - priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; - break; - default: - phydev_err(phydev, "invalid qca,clk-out-strength\n"); - return -EINVAL; - } - } - - return 0; -} - -static int at803x_probe(struct phy_device *phydev) -{ - struct device *dev = &phydev->mdio.dev; - struct at803x_priv *priv; - int ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - phydev->priv = priv; - - ret = at803x_parse_dt(phydev); - if (ret) - return ret; - - return 0; -} - -static int at803x_get_features(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - int err; - - err = genphy_read_abilities(phydev); - if (err) - return err; - - if (phydev->drv->phy_id != ATH8031_PHY_ID) - return 0; - - /* AR8031/AR8033 have different status registers - * for copper and fiber operation. However, the - * extended status register is the same for both - * operation modes. - * - * As a result of that, ESTATUS_1000_XFULL is set - * to 1 even when operating in copper TP mode. - * - * Remove this mode from the supported link modes - * when not operating in 1000BaseX mode. - */ - if (!priv->is_1000basex) - linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, - phydev->supported); - - return 0; -} - -static int at803x_smarteee_config(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - u16 mask = 0, val = 0; - int ret; - - 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; - - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, - mask, val); - if (ret) - return ret; - - 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; - - 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) -{ - struct at803x_priv *priv = phydev->priv; - - /* The default after hardware reset is PLL OFF. After a soft reset, the - * values are retained. - */ - if (priv->flags & AT803X_KEEP_PLL_ENABLED) - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, - 0, AT803X_DEBUG_PLL_ON); - else - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, - AT803X_DEBUG_PLL_ON, 0); -} - -static int at803x_hibernation_mode_config(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - - /* The default after hardware reset is hibernation mode enabled. After - * software reset, the value is retained. - */ - if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE)) - return 0; - - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, - AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); -} - -static int at803x_config_init(struct phy_device *phydev) -{ - int ret; - - /* The RX and TX delay default is: - * after HW reset: RX delay enabled and TX delay disabled - * after SW reset: RX delay enabled, while TX delay retains the - * value before reset. - */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) - ret = at803x_enable_rx_delay(phydev); - else - ret = at803x_disable_rx_delay(phydev); - if (ret < 0) - return ret; - - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) - ret = at803x_enable_tx_delay(phydev); - else - ret = at803x_disable_tx_delay(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; - - ret = at803x_hibernation_mode_config(phydev); - if (ret < 0) - return ret; - - /* 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) -{ - int err; - - err = phy_read(phydev, AT803X_INTR_STATUS); - - return (err < 0) ? err : 0; -} - -static int at803x_config_intr(struct phy_device *phydev) -{ - int err; - int value; - - value = phy_read(phydev, AT803X_INTR_ENABLE); - - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { - /* Clear any pending interrupts */ - err = at803x_ack_interrupt(phydev); - if (err) - return err; - - value |= AT803X_INTR_ENABLE_AUTONEG_ERR; - value |= AT803X_INTR_ENABLE_SPEED_CHANGED; - value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; - value |= AT803X_INTR_ENABLE_LINK_FAIL; - value |= AT803X_INTR_ENABLE_LINK_SUCCESS; - - err = phy_write(phydev, AT803X_INTR_ENABLE, value); - } else { - err = phy_write(phydev, AT803X_INTR_ENABLE, 0); - if (err) - return err; - - /* Clear any pending interrupts */ - err = at803x_ack_interrupt(phydev); - } - - return err; -} - -static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) -{ - int irq_status, int_enabled; - - irq_status = phy_read(phydev, AT803X_INTR_STATUS); - if (irq_status < 0) { - phy_error(phydev); - return IRQ_NONE; - } - - /* Read the current enabled interrupts */ - int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); - if (int_enabled < 0) { - phy_error(phydev); - return IRQ_NONE; - } - - /* See if this was one of our enabled interrupts */ - if (!(irq_status & int_enabled)) - return IRQ_NONE; - - phy_trigger_machine(phydev); - - return IRQ_HANDLED; -} - -static void at803x_link_change_notify(struct phy_device *phydev) -{ - /* - * Conduct a hardware reset for AT8030 every time a link loss is - * signalled. This is necessary to circumvent a hardware bug that - * occurs when the cable is unplugged while TX packets are pending - * in the FIFO. In such cases, the FIFO enters an error mode it - * cannot recover from by software. - */ - if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { - struct at803x_context context; - - at803x_context_save(phydev, &context); - - phy_device_reset(phydev, 1); - usleep_range(1000, 2000); - phy_device_reset(phydev, 0); - usleep_range(1000, 2000); - - at803x_context_restore(phydev, &context); - - phydev_dbg(phydev, "%s(): phy was reset\n", __func__); - } -} - -static int at803x_read_specific_status(struct phy_device *phydev, - struct at803x_ss_mask ss_mask) -{ - int ss; - - /* Read the AT8035 PHY-Specific Status register, which indicates the - * speed and duplex that the PHY is actually using, irrespective of - * whether we are in autoneg mode or not. - */ - ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); - if (ss < 0) - return ss; - - if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { - int sfc, speed; - - sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); - if (sfc < 0) - return sfc; - - speed = ss & ss_mask.speed_mask; - speed >>= ss_mask.speed_shift; - - switch (speed) { - case AT803X_SS_SPEED_10: - phydev->speed = SPEED_10; - break; - case AT803X_SS_SPEED_100: - phydev->speed = SPEED_100; - break; - case AT803X_SS_SPEED_1000: - phydev->speed = SPEED_1000; - break; - case QCA808X_SS_SPEED_2500: - phydev->speed = SPEED_2500; - break; - } - if (ss & AT803X_SS_DUPLEX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - if (ss & AT803X_SS_MDIX) - phydev->mdix = ETH_TP_MDI_X; - else - phydev->mdix = ETH_TP_MDI; - - switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { - case AT803X_SFC_MANUAL_MDI: - phydev->mdix_ctrl = ETH_TP_MDI; - break; - case AT803X_SFC_MANUAL_MDIX: - phydev->mdix_ctrl = ETH_TP_MDI_X; - break; - case AT803X_SFC_AUTOMATIC_CROSSOVER: - phydev->mdix_ctrl = ETH_TP_MDI_AUTO; - break; - } - } - - return 0; -} - -static int at803x_read_status(struct phy_device *phydev) -{ - struct at803x_ss_mask ss_mask = { 0 }; - int err, old_link = phydev->link; - - /* Update the link, but return if there was an error */ - err = genphy_update_link(phydev); - if (err) - return err; - - /* why bother the PHY if nothing can have changed */ - if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) - return 0; - - phydev->speed = SPEED_UNKNOWN; - phydev->duplex = DUPLEX_UNKNOWN; - phydev->pause = 0; - phydev->asym_pause = 0; - - err = genphy_read_lpa(phydev); - if (err < 0) - return err; - - ss_mask.speed_mask = AT803X_SS_SPEED_MASK; - ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); - err = at803x_read_specific_status(phydev, ss_mask); - if (err < 0) - return err; - - if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) - phy_resolve_aneg_pause(phydev); - - return 0; -} - -static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) -{ - u16 val; - - switch (ctrl) { - case ETH_TP_MDI: - val = AT803X_SFC_MANUAL_MDI; - break; - case ETH_TP_MDI_X: - val = AT803X_SFC_MANUAL_MDIX; - break; - case ETH_TP_MDI_AUTO: - val = AT803X_SFC_AUTOMATIC_CROSSOVER; - break; - default: - return 0; - } - - return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, - AT803X_SFC_MDI_CROSSOVER_MODE_M, - FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); -} - -static int at803x_prepare_config_aneg(struct phy_device *phydev) -{ - int ret; - - ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); - if (ret < 0) - return ret; - - /* Changes of the midx bits are disruptive to the normal operation; - * therefore any changes to these registers must be followed by a - * software reset to take effect. - */ - if (ret == 1) { - ret = genphy_soft_reset(phydev); - if (ret < 0) - return ret; - } - - return 0; -} - -static int at803x_config_aneg(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - int ret; - - ret = at803x_prepare_config_aneg(phydev); - if (ret) - return ret; - - if (priv->is_1000basex) - return genphy_c37_config_aneg(phydev); - - return genphy_config_aneg(phydev); -} - -static int at803x_get_downshift(struct phy_device *phydev, u8 *d) -{ - int val; - - val = phy_read(phydev, AT803X_SMART_SPEED); - if (val < 0) - return val; - - if (val & AT803X_SMART_SPEED_ENABLE) - *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; - else - *d = DOWNSHIFT_DEV_DISABLE; - - return 0; -} - -static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) -{ - u16 mask, set; - int ret; - - switch (cnt) { - case DOWNSHIFT_DEV_DEFAULT_COUNT: - cnt = AT803X_DEFAULT_DOWNSHIFT; - fallthrough; - case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: - set = AT803X_SMART_SPEED_ENABLE | - AT803X_SMART_SPEED_BYPASS_TIMER | - FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); - mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; - break; - case DOWNSHIFT_DEV_DISABLE: - set = 0; - mask = AT803X_SMART_SPEED_ENABLE | - AT803X_SMART_SPEED_BYPASS_TIMER; - break; - default: - return -EINVAL; - } - - ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); - - /* After changing the smart speed settings, we need to perform a - * software reset, use phy_init_hw() to make sure we set the - * reapply any values which might got lost during software reset. - */ - if (ret == 1) - ret = phy_init_hw(phydev); - - return ret; -} - -static int at803x_get_tunable(struct phy_device *phydev, - struct ethtool_tunable *tuna, void *data) -{ - switch (tuna->id) { - case ETHTOOL_PHY_DOWNSHIFT: - return at803x_get_downshift(phydev, data); - default: - return -EOPNOTSUPP; - } -} - -static int at803x_set_tunable(struct phy_device *phydev, - struct ethtool_tunable *tuna, const void *data) -{ - switch (tuna->id) { - case ETHTOOL_PHY_DOWNSHIFT: - return at803x_set_downshift(phydev, *(const u8 *)data); - default: - return -EOPNOTSUPP; - } -} - -static int at803x_cable_test_result_trans(u16 status) -{ - switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { - case AT803X_CDT_STATUS_STAT_NORMAL: - return ETHTOOL_A_CABLE_RESULT_CODE_OK; - case AT803X_CDT_STATUS_STAT_SHORT: - return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; - case AT803X_CDT_STATUS_STAT_OPEN: - return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; - case AT803X_CDT_STATUS_STAT_FAIL: - default: - return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; - } -} - -static bool at803x_cdt_test_failed(u16 status) -{ - return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) == - AT803X_CDT_STATUS_STAT_FAIL; -} - -static bool at803x_cdt_fault_length_valid(u16 status) -{ - switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { - case AT803X_CDT_STATUS_STAT_OPEN: - case AT803X_CDT_STATUS_STAT_SHORT: - return true; - } - return false; -} - -static int at803x_cdt_fault_length(int dt) -{ - /* According to the datasheet the distance to the fault is - * DELTA_TIME * 0.824 meters. - * - * The author suspect the correct formula is: - * - * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 - * - * where c is the speed of light, VF is the velocity factor of - * the twisted pair cable, 125MHz the counter frequency and - * we need to divide by 2 because the hardware will measure the - * round trip time to the fault and back to the PHY. - * - * With a VF of 0.69 we get the factor 0.824 mentioned in the - * datasheet. - */ - return (dt * 824) / 10; -} - -static int at803x_cdt_start(struct phy_device *phydev, - u32 cdt_start) -{ - return phy_write(phydev, AT803X_CDT, cdt_start); -} - -static int at803x_cdt_wait_for_completion(struct phy_device *phydev, - u32 cdt_en) -{ - int val, ret; - - /* One test run takes about 25ms */ - ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, - !(val & cdt_en), - 30000, 100000, true); - - return ret < 0 ? ret : 0; -} - -static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) -{ - static const int ethtool_pair[] = { - ETHTOOL_A_CABLE_PAIR_A, - ETHTOOL_A_CABLE_PAIR_B, - ETHTOOL_A_CABLE_PAIR_C, - ETHTOOL_A_CABLE_PAIR_D, - }; - int ret, val; - - val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | - AT803X_CDT_ENABLE_TEST; - ret = at803x_cdt_start(phydev, val); - if (ret) - return ret; - - ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); - if (ret) - return ret; - - val = phy_read(phydev, AT803X_CDT_STATUS); - if (val < 0) - return val; - - if (at803x_cdt_test_failed(val)) - return 0; - - ethnl_cable_test_result(phydev, ethtool_pair[pair], - at803x_cable_test_result_trans(val)); - - if (at803x_cdt_fault_length_valid(val)) { - val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); - ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], - at803x_cdt_fault_length(val)); - } - - return 1; -} - -static int at803x_cable_test_get_status(struct phy_device *phydev, - bool *finished, unsigned long pair_mask) -{ - int retries = 20; - int pair, ret; - - *finished = false; - - /* According to the datasheet the CDT can be performed when - * there is no link partner or when the link partner is - * auto-negotiating. Starting the test will restart the AN - * automatically. It seems that doing this repeatedly we will - * get a slot where our link partner won't disturb our - * measurement. - */ - while (pair_mask && retries--) { - for_each_set_bit(pair, &pair_mask, 4) { - ret = at803x_cable_test_one_pair(phydev, pair); - if (ret < 0) - return ret; - if (ret) - clear_bit(pair, &pair_mask); - } - if (pair_mask) - msleep(250); - } - - *finished = true; - - return 0; -} - -static void at803x_cable_test_autoneg(struct phy_device *phydev) -{ - /* Enable auto-negotiation, but advertise no capabilities, no link - * will be established. A restart of the auto-negotiation is not - * required, because the cable test will automatically break the link. - */ - phy_write(phydev, MII_BMCR, BMCR_ANENABLE); - phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); -} - -static int at803x_cable_test_start(struct phy_device *phydev) -{ - at803x_cable_test_autoneg(phydev); - /* we do all the (time consuming) work later */ - return 0; -} - -static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, - unsigned int selector) -{ - struct phy_device *phydev = rdev_get_drvdata(rdev); - - if (selector) - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, - 0, AT803X_DEBUG_RGMII_1V8); - else - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, - AT803X_DEBUG_RGMII_1V8, 0); -} - -static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) -{ - struct phy_device *phydev = rdev_get_drvdata(rdev); - int val; - - val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); - if (val < 0) - return val; - - return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; -} - -static const struct regulator_ops vddio_regulator_ops = { - .list_voltage = regulator_list_voltage_table, - .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, - .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, -}; - -static const unsigned int vddio_voltage_table[] = { - 1500000, - 1800000, -}; - -static const struct regulator_desc vddio_desc = { - .name = "vddio", - .of_match = of_match_ptr("vddio-regulator"), - .n_voltages = ARRAY_SIZE(vddio_voltage_table), - .volt_table = vddio_voltage_table, - .ops = &vddio_regulator_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, -}; - -static const struct regulator_ops vddh_regulator_ops = { -}; - -static const struct regulator_desc vddh_desc = { - .name = "vddh", - .of_match = of_match_ptr("vddh-regulator"), - .n_voltages = 1, - .fixed_uV = 2500000, - .ops = &vddh_regulator_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, -}; - -static int at8031_register_regulators(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - struct device *dev = &phydev->mdio.dev; - struct regulator_config config = { }; - - config.dev = dev; - config.driver_data = phydev; - - priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); - if (IS_ERR(priv->vddio_rdev)) { - phydev_err(phydev, "failed to register VDDIO regulator\n"); - return PTR_ERR(priv->vddio_rdev); - } - - priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); - if (IS_ERR(priv->vddh_rdev)) { - phydev_err(phydev, "failed to register VDDH regulator\n"); - return PTR_ERR(priv->vddh_rdev); - } - - return 0; -} - -static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) -{ - struct phy_device *phydev = upstream; - __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); - __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); - DECLARE_PHY_INTERFACE_MASK(interfaces); - phy_interface_t iface; - - linkmode_zero(phy_support); - phylink_set(phy_support, 1000baseX_Full); - phylink_set(phy_support, 1000baseT_Full); - phylink_set(phy_support, Autoneg); - phylink_set(phy_support, Pause); - phylink_set(phy_support, Asym_Pause); - - linkmode_zero(sfp_support); - sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); - /* Some modules support 10G modes as well as others we support. - * Mask out non-supported modes so the correct interface is picked. - */ - linkmode_and(sfp_support, phy_support, sfp_support); - - if (linkmode_empty(sfp_support)) { - dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); - return -EINVAL; - } - - iface = sfp_select_interface(phydev->sfp_bus, sfp_support); - - /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes - * interface for use with SFP modules. - * However, some copper modules detected as having a preferred SGMII - * interface do default to and function in 1000Base-X mode, so just - * print a warning and allow such modules, as they may have some chance - * of working. - */ - if (iface == PHY_INTERFACE_MODE_SGMII) - dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); - else if (iface != PHY_INTERFACE_MODE_1000BASEX) - return -EINVAL; - - return 0; -} - -static const struct sfp_upstream_ops at8031_sfp_ops = { - .attach = phy_sfp_attach, - .detach = phy_sfp_detach, - .module_insert = at8031_sfp_insert, -}; - -static int at8031_parse_dt(struct phy_device *phydev) -{ - struct device_node *node = phydev->mdio.dev.of_node; - struct at803x_priv *priv = phydev->priv; - int ret; - - if (of_property_read_bool(node, "qca,keep-pll-enabled")) - priv->flags |= AT803X_KEEP_PLL_ENABLED; - - ret = at8031_register_regulators(phydev); - if (ret < 0) - return ret; - - ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, - "vddio"); - if (ret) { - phydev_err(phydev, "failed to get VDDIO regulator\n"); - return ret; - } - - /* Only AR8031/8033 support 1000Base-X for SFP modules */ - return phy_sfp_probe(phydev, &at8031_sfp_ops); -} - -static int at8031_probe(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - int mode_cfg; - int ccr; - int ret; - - ret = at803x_probe(phydev); - if (ret) - return ret; - - /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping - * options. - */ - ret = at8031_parse_dt(phydev); - if (ret) - return ret; - - ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); - if (ccr < 0) - return ccr; - mode_cfg = ccr & AT803X_MODE_CFG_MASK; - - switch (mode_cfg) { - case AT803X_MODE_CFG_BX1000_RGMII_50OHM: - case AT803X_MODE_CFG_BX1000_RGMII_75OHM: - priv->is_1000basex = true; - fallthrough; - case AT803X_MODE_CFG_FX100_RGMII_50OHM: - case AT803X_MODE_CFG_FX100_RGMII_75OHM: - priv->is_fiber = true; - break; - } - - /* Disable WoL in 1588 register which is enabled - * by default - */ - return phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - AT803X_WOL_EN, 0); -} - -static int at8031_config_init(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - int ret; - - /* Some bootloaders leave the fiber page selected. - * Switch to the appropriate page (fiber or copper), as otherwise we - * read the PHY capabilities from the wrong page. - */ - phy_lock_mdio_bus(phydev); - ret = at803x_write_page(phydev, - priv->is_fiber ? AT803X_PAGE_FIBER : - AT803X_PAGE_COPPER); - phy_unlock_mdio_bus(phydev); - if (ret) - return ret; - - ret = at8031_pll_config(phydev); - if (ret < 0) - return ret; - - return at803x_config_init(phydev); -} - -static int at8031_set_wol(struct phy_device *phydev, - struct ethtool_wolinfo *wol) -{ - int ret; - - /* First setup MAC address and enable WOL interrupt */ - ret = at803x_set_wol(phydev, wol); - if (ret) - return ret; - - if (wol->wolopts & WAKE_MAGIC) - /* Enable WOL function for 1588 */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - 0, AT803X_WOL_EN); - else - /* Disable WoL function for 1588 */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - AT803X_WOL_EN, 0); - - return ret; -} - -static int at8031_config_intr(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - int err, value = 0; - - if (phydev->interrupts == PHY_INTERRUPT_ENABLED && - priv->is_fiber) { - /* Clear any pending interrupts */ - err = at803x_ack_interrupt(phydev); - if (err) - return err; - - value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; - value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; - - err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); - if (err) - return err; - } - - return at803x_config_intr(phydev); -} - -/* AR8031 and AR8033 share the same read status logic */ -static int at8031_read_status(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - - if (priv->is_1000basex) - return genphy_c37_read_status(phydev); - - return at803x_read_status(phydev); -} - -/* AR8031 and AR8035 share the same cable test get status reg */ -static int at8031_cable_test_get_status(struct phy_device *phydev, - bool *finished) -{ - return at803x_cable_test_get_status(phydev, finished, 0xf); -} - -/* AR8031 and AR8035 share the same cable test start logic */ -static int at8031_cable_test_start(struct phy_device *phydev) -{ - at803x_cable_test_autoneg(phydev); - phy_write(phydev, MII_CTRL1000, 0); - /* we do all the (time consuming) work later */ - return 0; -} - -/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ -static int at8032_cable_test_get_status(struct phy_device *phydev, - bool *finished) -{ - return at803x_cable_test_get_status(phydev, finished, 0x3); -} - -static int at8035_parse_dt(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - - /* Mask is set by the generic at803x_parse_dt - * if property is set. Assume property is set - * with the mask not zero. - */ - if (priv->clk_25m_mask) { - /* Fixup for the AR8030/AR8035. This chip has another mask and - * doesn't support the DSP reference. Eg. the lowest bit of the - * mask. The upper two bits select the same frequencies. Mask - * the lowest bit here. - * - * Warning: - * There was no datasheet for the AR8030 available so this is - * just a guess. But the AR8035 is listed as pin compatible - * to the AR8030 so there might be a good chance it works on - * the AR8030 too. - */ - priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; - priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; - } - - return 0; -} - -/* AR8030 and AR8035 shared the same special mask for clk_25m */ -static int at8035_probe(struct phy_device *phydev) -{ - int ret; - - ret = at803x_probe(phydev); - if (ret) - return ret; - - return at8035_parse_dt(phydev); -} - -static int qca83xx_config_init(struct phy_device *phydev) -{ - u8 switch_revision; - - switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; - - switch (switch_revision) { - case 1: - /* For 100M waveform */ - at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); - /* Turn on Gigabit clock */ - at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); - break; - - case 2: - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); - fallthrough; - case 4: - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); - at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); - at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); - at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); - break; - } - - /* Following original QCA sourcecode set port to prefer master */ - phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); - - return 0; -} - -static int qca8327_config_init(struct phy_device *phydev) -{ - /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. - * Disable on init and enable only with 100m speed following - * qca original source code. - */ - at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, - QCA8327_DEBUG_MANU_CTRL_EN, 0); - - return qca83xx_config_init(phydev); -} - -static void qca83xx_link_change_notify(struct phy_device *phydev) -{ - /* Set DAC Amplitude adjustment to +6% for 100m on link running */ - if (phydev->state == PHY_RUNNING) { - if (phydev->speed == SPEED_100) - at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, - QCA8327_DEBUG_MANU_CTRL_EN, - QCA8327_DEBUG_MANU_CTRL_EN); - } else { - /* Reset DAC Amplitude adjustment */ - at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, - QCA8327_DEBUG_MANU_CTRL_EN, 0); - } -} - -static int qca83xx_resume(struct phy_device *phydev) -{ - int ret, val; - - /* Skip reset if not suspended */ - if (!phydev->suspended) - return 0; - - /* Reinit the port, reset values set by suspend */ - qca83xx_config_init(phydev); - - /* Reset the port on port resume */ - phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); - - /* On resume from suspend the switch execute a reset and - * restart auto-negotiation. Wait for reset to complete. - */ - ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), - 50000, 600000, true); - if (ret) - return ret; - - usleep_range(1000, 2000); - - return 0; -} - -static int qca83xx_suspend(struct phy_device *phydev) -{ - at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, - AT803X_DEBUG_GATE_CLK_IN1000, 0); - - at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, - AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | - AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); - - return 0; -} - -static int qca8337_suspend(struct phy_device *phydev) -{ - /* Only QCA8337 support actual suspend. */ - genphy_suspend(phydev); - - return qca83xx_suspend(phydev); -} - -static int qca8327_suspend(struct phy_device *phydev) -{ - u16 mask = 0; - - /* QCA8327 cause port unreliability when phy suspend - * is set. - */ - mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); - phy_modify(phydev, MII_BMCR, mask, 0); - - return qca83xx_suspend(phydev); -} - -static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) -{ - int ret; - - /* Enable fast retrain */ - ret = genphy_c45_fast_retrain(phydev, true); - if (ret) - return ret; - - phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, - QCA808X_TOP_OPTION1_DATA); - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, - QCA808X_MSE_THRESHOLD_20DB_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, - QCA808X_MSE_THRESHOLD_17DB_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, - QCA808X_MSE_THRESHOLD_27DB_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, - QCA808X_MSE_THRESHOLD_28DB_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, - QCA808X_MMD3_DEBUG_1_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, - QCA808X_MMD3_DEBUG_4_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, - QCA808X_MMD3_DEBUG_5_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, - QCA808X_MMD3_DEBUG_3_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, - QCA808X_MMD3_DEBUG_6_VALUE); - phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, - QCA808X_MMD3_DEBUG_2_VALUE); - - return 0; -} - -static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) -{ - u16 seed_value; - - if (!enable) - return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, - QCA808X_MASTER_SLAVE_SEED_ENABLE, 0); - - seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE); - return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, - QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, - FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | - QCA808X_MASTER_SLAVE_SEED_ENABLE); -} - -static bool qca808x_is_prefer_master(struct phy_device *phydev) -{ - return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) || - (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); -} - -static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev) -{ - return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); -} - -static int qca808x_config_init(struct phy_device *phydev) -{ - int ret; - - /* Active adc&vga on 802.3az for the link 1000M and 100M */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, - QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); - if (ret) - return ret; - - /* Adjust the threshold on 802.3az for the link 1000M */ - ret = phy_write_mmd(phydev, MDIO_MMD_PCS, - QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, - QCA808X_MMD3_AZ_TRAINING_VAL); - if (ret) - return ret; - - if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { - /* Config the fast retrain for the link 2500M */ - ret = qca808x_phy_fast_retrain_config(phydev); - if (ret) - return ret; - - ret = genphy_read_master_slave(phydev); - if (ret < 0) - return ret; - - if (!qca808x_is_prefer_master(phydev)) { - /* Enable seed and configure lower ramdom seed to make phy - * linked as slave mode. - */ - ret = qca808x_phy_ms_seed_enable(phydev, true); - if (ret) - return ret; - } - } - - /* Configure adc threshold as 100mv for the link 10M */ - return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, - QCA808X_ADC_THRESHOLD_MASK, - QCA808X_ADC_THRESHOLD_100MV); -} - -static int qca808x_read_status(struct phy_device *phydev) -{ - struct at803x_ss_mask ss_mask = { 0 }; - int ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); - if (ret < 0) - return ret; - - linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, - ret & MDIO_AN_10GBT_STAT_LP2_5G); - - ret = genphy_read_status(phydev); - if (ret) - return ret; - - /* qca8081 takes the different bits for speed value from at803x */ - ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; - ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); - ret = at803x_read_specific_status(phydev, ss_mask); - if (ret < 0) - return ret; - - if (phydev->link) { - if (phydev->speed == SPEED_2500) - phydev->interface = PHY_INTERFACE_MODE_2500BASEX; - else - phydev->interface = PHY_INTERFACE_MODE_SGMII; - } else { - /* generate seed as a lower random value to make PHY linked as SLAVE easily, - * except for master/slave configuration fault detected or the master mode - * preferred. - * - * the reason for not putting this code into the function link_change_notify is - * the corner case where the link partner is also the qca8081 PHY and the seed - * value is configured as the same value, the link can't be up and no link change - * occurs. - */ - if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { - if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || - qca808x_is_prefer_master(phydev)) { - qca808x_phy_ms_seed_enable(phydev, false); - } else { - qca808x_phy_ms_seed_enable(phydev, true); - } - } - } - - return 0; -} - -static int qca808x_soft_reset(struct phy_device *phydev) -{ - int ret; - - ret = genphy_soft_reset(phydev); - if (ret < 0) - return ret; - - if (qca808x_has_fast_retrain_or_slave_seed(phydev)) - ret = qca808x_phy_ms_seed_enable(phydev, true); - - return ret; -} - -static bool qca808x_cdt_fault_length_valid(int cdt_code) -{ - switch (cdt_code) { - case QCA808X_CDT_STATUS_STAT_SAME_SHORT: - case QCA808X_CDT_STATUS_STAT_SAME_OPEN: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: - return true; - default: - return false; - } -} - -static int qca808x_cable_test_result_trans(int cdt_code) -{ - switch (cdt_code) { - case QCA808X_CDT_STATUS_STAT_NORMAL: - return ETHTOOL_A_CABLE_RESULT_CODE_OK; - case QCA808X_CDT_STATUS_STAT_SAME_SHORT: - return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; - case QCA808X_CDT_STATUS_STAT_SAME_OPEN: - return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: - case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: - return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; - case QCA808X_CDT_STATUS_STAT_FAIL: - default: - return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; - } -} - -static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, - int result) -{ - int val; - u32 cdt_length_reg = 0; - - switch (pair) { - case ETHTOOL_A_CABLE_PAIR_A: - cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; - break; - case ETHTOOL_A_CABLE_PAIR_B: - cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; - break; - case ETHTOOL_A_CABLE_PAIR_C: - cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; - break; - case ETHTOOL_A_CABLE_PAIR_D: - cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; - break; - default: - return -EINVAL; - } - - val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); - if (val < 0) - return val; - - if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) - val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); - else - val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); - - return at803x_cdt_fault_length(val); -} - -static int qca808x_cable_test_start(struct phy_device *phydev) -{ - int ret; - - /* perform CDT with the following configs: - * 1. disable hibernation. - * 2. force PHY working in MDI mode. - * 3. for PHY working in 1000BaseT. - * 4. configure the threshold. - */ - - ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); - if (ret < 0) - return ret; - - ret = at803x_config_mdix(phydev, ETH_TP_MDI); - if (ret < 0) - return ret; - - /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ - phydev->duplex = DUPLEX_FULL; - phydev->speed = SPEED_1000; - ret = genphy_c45_pma_setup_forced(phydev); - if (ret < 0) - return ret; - - ret = genphy_setup_forced(phydev); - if (ret < 0) - return ret; - - /* configure the thresholds for open, short, pair ok test */ - phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); - phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); - phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); - phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); - phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); - phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); - - return 0; -} - -static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, - u16 status) -{ - int length, result; - u16 pair_code; - - switch (pair) { - case ETHTOOL_A_CABLE_PAIR_A: - pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); - break; - case ETHTOOL_A_CABLE_PAIR_B: - pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); - break; - case ETHTOOL_A_CABLE_PAIR_C: - pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); - break; - case ETHTOOL_A_CABLE_PAIR_D: - pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); - break; - default: - return -EINVAL; - } - - result = qca808x_cable_test_result_trans(pair_code); - ethnl_cable_test_result(phydev, pair, result); - - if (qca808x_cdt_fault_length_valid(pair_code)) { - length = qca808x_cdt_fault_length(phydev, pair, result); - ethnl_cable_test_fault_length(phydev, pair, length); - } - - return 0; -} - -static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) -{ - int ret, val; - - *finished = false; - - val = QCA808X_CDT_ENABLE_TEST | - QCA808X_CDT_LENGTH_UNIT; - ret = at803x_cdt_start(phydev, val); - if (ret) - return ret; - - ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); - if (ret) - return ret; - - val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); - if (val < 0) - return val; - - ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); - if (ret) - return ret; - - ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); - if (ret) - return ret; - - ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); - if (ret) - return ret; - - ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); - if (ret) - return ret; - - *finished = true; - - return 0; -} - -static int qca808x_get_features(struct phy_device *phydev) -{ - int ret; - - ret = genphy_c45_pma_read_abilities(phydev); - if (ret) - return ret; - - /* The autoneg ability is not existed in bit3 of MMD7.1, - * but it is supported by qca808x PHY, so we add it here - * manually. - */ - linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); - - /* As for the qca8081 1G version chip, the 2500baseT ability is also - * existed in the bit0 of MMD1.21, we need to remove it manually if - * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. - */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); - if (ret < 0) - return ret; - - if (QCA808X_PHY_CHIP_TYPE_1G & ret) - linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); - - return 0; -} - -static int qca808x_config_aneg(struct phy_device *phydev) -{ - int phy_ctrl = 0; - int ret; - - ret = at803x_prepare_config_aneg(phydev); - if (ret) - return ret; - - /* The reg MII_BMCR also needs to be configured for force mode, the - * genphy_config_aneg is also needed. - */ - if (phydev->autoneg == AUTONEG_DISABLE) - genphy_c45_pma_setup_forced(phydev); - - if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) - phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; - - ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, - MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); - if (ret < 0) - return ret; - - return __genphy_config_aneg(phydev, ret); -} - -static void qca808x_link_change_notify(struct phy_device *phydev) -{ - /* Assert interface sgmii fifo on link down, deassert it on link up, - * the interface device address is always phy address added by 1. - */ - mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, - MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, - QCA8081_PHY_FIFO_RSTN, - phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); -} - -static struct phy_driver at803x_driver[] = { -{ - /* Qualcomm Atheros AR8035 */ - PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), - .name = "Qualcomm Atheros AR8035", - .flags = PHY_POLL_CABLE_TEST, - .probe = at8035_probe, - .config_aneg = at803x_config_aneg, - .config_init = at803x_config_init, - .soft_reset = genphy_soft_reset, - .set_wol = at803x_set_wol, - .get_wol = at803x_get_wol, - .suspend = at803x_suspend, - .resume = at803x_resume, - /* PHY_GBIT_FEATURES */ - .read_status = at803x_read_status, - .config_intr = at803x_config_intr, - .handle_interrupt = at803x_handle_interrupt, - .get_tunable = at803x_get_tunable, - .set_tunable = at803x_set_tunable, - .cable_test_start = at8031_cable_test_start, - .cable_test_get_status = at8031_cable_test_get_status, -}, { - /* Qualcomm Atheros AR8030 */ - .phy_id = ATH8030_PHY_ID, - .name = "Qualcomm Atheros AR8030", - .phy_id_mask = AT8030_PHY_ID_MASK, - .probe = at8035_probe, - .config_init = at803x_config_init, - .link_change_notify = at803x_link_change_notify, - .set_wol = at803x_set_wol, - .get_wol = at803x_get_wol, - .suspend = at803x_suspend, - .resume = at803x_resume, - /* PHY_BASIC_FEATURES */ - .config_intr = at803x_config_intr, - .handle_interrupt = at803x_handle_interrupt, -}, { - /* Qualcomm Atheros AR8031/AR8033 */ - PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), - .name = "Qualcomm Atheros AR8031/AR8033", - .flags = PHY_POLL_CABLE_TEST, - .probe = at8031_probe, - .config_init = at8031_config_init, - .config_aneg = at803x_config_aneg, - .soft_reset = genphy_soft_reset, - .set_wol = at8031_set_wol, - .get_wol = at803x_get_wol, - .suspend = at803x_suspend, - .resume = at803x_resume, - .read_page = at803x_read_page, - .write_page = at803x_write_page, - .get_features = at803x_get_features, - .read_status = at8031_read_status, - .config_intr = at8031_config_intr, - .handle_interrupt = at803x_handle_interrupt, - .get_tunable = at803x_get_tunable, - .set_tunable = at803x_set_tunable, - .cable_test_start = at8031_cable_test_start, - .cable_test_get_status = at8031_cable_test_get_status, -}, { - /* Qualcomm Atheros AR8032 */ - PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), - .name = "Qualcomm Atheros AR8032", - .probe = at803x_probe, - .flags = PHY_POLL_CABLE_TEST, - .config_init = at803x_config_init, - .link_change_notify = at803x_link_change_notify, - .suspend = at803x_suspend, - .resume = at803x_resume, - /* PHY_BASIC_FEATURES */ - .config_intr = at803x_config_intr, - .handle_interrupt = at803x_handle_interrupt, - .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at8032_cable_test_get_status, -}, { - /* ATHEROS AR9331 */ - PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), - .name = "Qualcomm Atheros AR9331 built-in PHY", - .probe = at803x_probe, - .suspend = at803x_suspend, - .resume = at803x_resume, - .flags = PHY_POLL_CABLE_TEST, - /* PHY_BASIC_FEATURES */ - .config_intr = at803x_config_intr, - .handle_interrupt = at803x_handle_interrupt, - .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at8032_cable_test_get_status, - .read_status = at803x_read_status, - .soft_reset = genphy_soft_reset, - .config_aneg = at803x_config_aneg, -}, { - /* Qualcomm Atheros QCA9561 */ - PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), - .name = "Qualcomm Atheros QCA9561 built-in PHY", - .probe = at803x_probe, - .suspend = at803x_suspend, - .resume = at803x_resume, - .flags = PHY_POLL_CABLE_TEST, - /* PHY_BASIC_FEATURES */ - .config_intr = at803x_config_intr, - .handle_interrupt = at803x_handle_interrupt, - .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at8032_cable_test_get_status, - .read_status = at803x_read_status, - .soft_reset = genphy_soft_reset, - .config_aneg = at803x_config_aneg, -}, { - /* QCA8337 */ - .phy_id = QCA8337_PHY_ID, - .phy_id_mask = QCA8K_PHY_ID_MASK, - .name = "Qualcomm Atheros 8337 internal PHY", - /* PHY_GBIT_FEATURES */ - .probe = at803x_probe, - .flags = PHY_IS_INTERNAL, - .config_init = qca83xx_config_init, - .soft_reset = genphy_soft_reset, - .get_sset_count = qca83xx_get_sset_count, - .get_strings = qca83xx_get_strings, - .get_stats = qca83xx_get_stats, - .suspend = qca8337_suspend, - .resume = qca83xx_resume, -}, { - /* QCA8327-A from switch QCA8327-AL1A */ - .phy_id = QCA8327_A_PHY_ID, - .phy_id_mask = QCA8K_PHY_ID_MASK, - .name = "Qualcomm Atheros 8327-A internal PHY", - /* PHY_GBIT_FEATURES */ - .link_change_notify = qca83xx_link_change_notify, - .probe = at803x_probe, - .flags = PHY_IS_INTERNAL, - .config_init = qca8327_config_init, - .soft_reset = genphy_soft_reset, - .get_sset_count = qca83xx_get_sset_count, - .get_strings = qca83xx_get_strings, - .get_stats = qca83xx_get_stats, - .suspend = qca8327_suspend, - .resume = qca83xx_resume, -}, { - /* QCA8327-B from switch QCA8327-BL1A */ - .phy_id = QCA8327_B_PHY_ID, - .phy_id_mask = QCA8K_PHY_ID_MASK, - .name = "Qualcomm Atheros 8327-B internal PHY", - /* PHY_GBIT_FEATURES */ - .link_change_notify = qca83xx_link_change_notify, - .probe = at803x_probe, - .flags = PHY_IS_INTERNAL, - .config_init = qca8327_config_init, - .soft_reset = genphy_soft_reset, - .get_sset_count = qca83xx_get_sset_count, - .get_strings = qca83xx_get_strings, - .get_stats = qca83xx_get_stats, - .suspend = qca8327_suspend, - .resume = qca83xx_resume, -}, { - /* Qualcomm QCA8081 */ - PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), - .name = "Qualcomm QCA8081", - .flags = PHY_POLL_CABLE_TEST, - .probe = at803x_probe, - .config_intr = at803x_config_intr, - .handle_interrupt = at803x_handle_interrupt, - .get_tunable = at803x_get_tunable, - .set_tunable = at803x_set_tunable, - .set_wol = at803x_set_wol, - .get_wol = at803x_get_wol, - .get_features = qca808x_get_features, - .config_aneg = qca808x_config_aneg, - .suspend = genphy_suspend, - .resume = genphy_resume, - .read_status = qca808x_read_status, - .config_init = qca808x_config_init, - .soft_reset = qca808x_soft_reset, - .cable_test_start = qca808x_cable_test_start, - .cable_test_get_status = qca808x_cable_test_get_status, - .link_change_notify = qca808x_link_change_notify, -}, }; - -module_phy_driver(at803x_driver); - -static struct mdio_device_id __maybe_unused atheros_tbl[] = { - { ATH8030_PHY_ID, AT8030_PHY_ID_MASK }, - { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) }, - { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, - { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, - { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, - { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, - { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, - { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, - { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, - { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, - { } -}; - -MODULE_DEVICE_TABLE(mdio, atheros_tbl); diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 312a8bb35d78..370e4ed45098 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -665,10 +665,11 @@ static int bcm54616s_config_aneg(struct phy_device *phydev) static int bcm54616s_read_status(struct phy_device *phydev) { struct bcm54616s_phy_priv *priv = phydev->priv; + bool changed; int err; if (priv->mode_1000bx_en) - err = genphy_c37_read_status(phydev); + err = genphy_c37_read_status(phydev, &changed); else err = genphy_read_status(phydev); diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c index b7cb71817780..30f2616ab1c2 100644 --- a/drivers/net/phy/dp83822.c +++ b/drivers/net/phy/dp83822.c @@ -12,6 +12,7 @@ #include <linux/of.h> #include <linux/phy.h> #include <linux/netdevice.h> +#include <linux/bitfield.h> #define DP83822_PHY_ID 0x2000a240 #define DP83825S_PHY_ID 0x2000a140 @@ -34,6 +35,10 @@ #define MII_DP83822_GENCFG 0x465 #define MII_DP83822_SOR1 0x467 +/* DP83826 specific registers */ +#define MII_DP83826_VOD_CFG1 0x30b +#define MII_DP83826_VOD_CFG2 0x30c + /* GENCFG */ #define DP83822_SIG_DET_LOW BIT(0) @@ -110,6 +115,19 @@ #define DP83822_RX_ER_STR_MASK GENMASK(9, 8) #define DP83822_RX_ER_SHIFT 8 +/* DP83826: VOD_CFG1 & VOD_CFG2 */ +#define DP83826_VOD_CFG1_MINUS_MDIX_MASK GENMASK(13, 12) +#define DP83826_VOD_CFG1_MINUS_MDI_MASK GENMASK(11, 6) +#define DP83826_VOD_CFG2_MINUS_MDIX_MASK GENMASK(15, 12) +#define DP83826_VOD_CFG2_PLUS_MDIX_MASK GENMASK(11, 6) +#define DP83826_VOD_CFG2_PLUS_MDI_MASK GENMASK(5, 0) +#define DP83826_CFG_DAC_MINUS_MDIX_5_TO_4 GENMASK(5, 4) +#define DP83826_CFG_DAC_MINUS_MDIX_3_TO_0 GENMASK(3, 0) +#define DP83826_CFG_DAC_PERCENT_PER_STEP 625 +#define DP83826_CFG_DAC_PERCENT_DEFAULT 10000 +#define DP83826_CFG_DAC_MINUS_DEFAULT 0x30 +#define DP83826_CFG_DAC_PLUS_DEFAULT 0x10 + #define MII_DP83822_FIBER_ADVERTISE (ADVERTISED_TP | ADVERTISED_MII | \ ADVERTISED_FIBRE | \ ADVERTISED_Pause | ADVERTISED_Asym_Pause) @@ -118,6 +136,8 @@ struct dp83822_private { bool fx_signal_det_low; int fx_enabled; u16 fx_sd_enable; + u8 cfg_dac_minus; + u8 cfg_dac_plus; }; static int dp83822_set_wol(struct phy_device *phydev, @@ -233,7 +253,7 @@ static int dp83822_config_intr(struct phy_device *phydev) DP83822_ENERGY_DET_INT_EN | DP83822_LINK_QUAL_INT_EN); - /* Private data pointer is NULL on DP83825/26 */ + /* Private data pointer is NULL on DP83825 */ if (!dp83822 || !dp83822->fx_enabled) misr_status |= DP83822_ANEG_COMPLETE_INT_EN | DP83822_DUP_MODE_CHANGE_INT_EN | @@ -254,7 +274,7 @@ static int dp83822_config_intr(struct phy_device *phydev) DP83822_PAGE_RX_INT_EN | DP83822_EEE_ERROR_CHANGE_INT_EN); - /* Private data pointer is NULL on DP83825/26 */ + /* Private data pointer is NULL on DP83825 */ if (!dp83822 || !dp83822->fx_enabled) misr_status |= DP83822_ANEG_ERR_INT_EN | DP83822_WOL_PKT_INT_EN; @@ -474,6 +494,43 @@ static int dp83822_config_init(struct phy_device *phydev) return dp8382x_disable_wol(phydev); } +static int dp83826_config_init(struct phy_device *phydev) +{ + struct dp83822_private *dp83822 = phydev->priv; + u16 val, mask; + int ret; + + if (dp83822->cfg_dac_minus != DP83826_CFG_DAC_MINUS_DEFAULT) { + val = FIELD_PREP(DP83826_VOD_CFG1_MINUS_MDI_MASK, dp83822->cfg_dac_minus) | + FIELD_PREP(DP83826_VOD_CFG1_MINUS_MDIX_MASK, + FIELD_GET(DP83826_CFG_DAC_MINUS_MDIX_5_TO_4, + dp83822->cfg_dac_minus)); + mask = DP83826_VOD_CFG1_MINUS_MDIX_MASK | DP83826_VOD_CFG1_MINUS_MDI_MASK; + ret = phy_modify_mmd(phydev, DP83822_DEVADDR, MII_DP83826_VOD_CFG1, mask, val); + if (ret) + return ret; + + val = FIELD_PREP(DP83826_VOD_CFG2_MINUS_MDIX_MASK, + FIELD_GET(DP83826_CFG_DAC_MINUS_MDIX_3_TO_0, + dp83822->cfg_dac_minus)); + mask = DP83826_VOD_CFG2_MINUS_MDIX_MASK; + ret = phy_modify_mmd(phydev, DP83822_DEVADDR, MII_DP83826_VOD_CFG2, mask, val); + if (ret) + return ret; + } + + if (dp83822->cfg_dac_plus != DP83826_CFG_DAC_PLUS_DEFAULT) { + val = FIELD_PREP(DP83826_VOD_CFG2_PLUS_MDIX_MASK, dp83822->cfg_dac_plus) | + FIELD_PREP(DP83826_VOD_CFG2_PLUS_MDI_MASK, dp83822->cfg_dac_plus); + mask = DP83826_VOD_CFG2_PLUS_MDIX_MASK | DP83826_VOD_CFG2_PLUS_MDI_MASK; + ret = phy_modify_mmd(phydev, DP83822_DEVADDR, MII_DP83826_VOD_CFG2, mask, val); + if (ret) + return ret; + } + + return 0; +} + static int dp8382x_config_init(struct phy_device *phydev) { return dp8382x_disable_wol(phydev); @@ -509,11 +566,44 @@ static int dp83822_of_init(struct phy_device *phydev) return 0; } + +static int dp83826_to_dac_minus_one_regval(int percent) +{ + int tmp = DP83826_CFG_DAC_PERCENT_DEFAULT - percent; + + return tmp / DP83826_CFG_DAC_PERCENT_PER_STEP; +} + +static int dp83826_to_dac_plus_one_regval(int percent) +{ + int tmp = percent - DP83826_CFG_DAC_PERCENT_DEFAULT; + + return tmp / DP83826_CFG_DAC_PERCENT_PER_STEP; +} + +static void dp83826_of_init(struct phy_device *phydev) +{ + struct dp83822_private *dp83822 = phydev->priv; + struct device *dev = &phydev->mdio.dev; + u32 val; + + dp83822->cfg_dac_minus = DP83826_CFG_DAC_MINUS_DEFAULT; + if (!device_property_read_u32(dev, "ti,cfg-dac-minus-one-bp", &val)) + dp83822->cfg_dac_minus += dp83826_to_dac_minus_one_regval(val); + + dp83822->cfg_dac_plus = DP83826_CFG_DAC_PLUS_DEFAULT; + if (!device_property_read_u32(dev, "ti,cfg-dac-plus-one-bp", &val)) + dp83822->cfg_dac_plus += dp83826_to_dac_plus_one_regval(val); +} #else static int dp83822_of_init(struct phy_device *phydev) { return 0; } + +static void dp83826_of_init(struct phy_device *phydev) +{ +} #endif /* CONFIG_OF_MDIO */ static int dp83822_read_straps(struct phy_device *phydev) @@ -567,6 +657,22 @@ static int dp83822_probe(struct phy_device *phydev) return 0; } +static int dp83826_probe(struct phy_device *phydev) +{ + struct dp83822_private *dp83822; + + dp83822 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83822), + GFP_KERNEL); + if (!dp83822) + return -ENOMEM; + + phydev->priv = dp83822; + + dp83826_of_init(phydev); + + return 0; +} + static int dp83822_suspend(struct phy_device *phydev) { int value; @@ -610,6 +716,22 @@ static int dp83822_resume(struct phy_device *phydev) .resume = dp83822_resume, \ } +#define DP83826_PHY_DRIVER(_id, _name) \ + { \ + PHY_ID_MATCH_MODEL(_id), \ + .name = (_name), \ + /* PHY_BASIC_FEATURES */ \ + .probe = dp83826_probe, \ + .soft_reset = dp83822_phy_reset, \ + .config_init = dp83826_config_init, \ + .get_wol = dp83822_get_wol, \ + .set_wol = dp83822_set_wol, \ + .config_intr = dp83822_config_intr, \ + .handle_interrupt = dp83822_handle_interrupt, \ + .suspend = dp83822_suspend, \ + .resume = dp83822_resume, \ + } + #define DP8382X_PHY_DRIVER(_id, _name) \ { \ PHY_ID_MATCH_MODEL(_id), \ @@ -628,8 +750,8 @@ static int dp83822_resume(struct phy_device *phydev) static struct phy_driver dp83822_driver[] = { DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"), DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), - DP8382X_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), - DP8382X_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), + DP83826_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), + DP83826_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 5f08f9d38bd7..4120385c5a79 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -158,6 +158,7 @@ /* LED_DRV bits */ #define DP83867_LED_DRV_EN(x) BIT((x) * 4) #define DP83867_LED_DRV_VAL(x) BIT((x) * 4 + 1) +#define DP83867_LED_POLARITY(x) BIT((x) * 4 + 2) #define DP83867_LED_FN(idx, val) (((val) & 0xf) << ((idx) * 4)) #define DP83867_LED_FN_MASK(idx) (0xf << ((idx) * 4)) @@ -1152,6 +1153,26 @@ static int dp83867_led_hw_control_get(struct phy_device *phydev, u8 index, return 0; } +static int dp83867_led_polarity_set(struct phy_device *phydev, int index, + unsigned long modes) +{ + /* Default active high */ + u16 polarity = DP83867_LED_POLARITY(index); + u32 mode; + + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { + switch (mode) { + case PHY_LED_ACTIVE_LOW: + polarity = 0; + break; + default: + return -EINVAL; + } + } + return phy_modify(phydev, DP83867_LEDCR2, + DP83867_LED_POLARITY(index), polarity); +} + static struct phy_driver dp83867_driver[] = { { .phy_id = DP83867_PHY_ID, @@ -1184,6 +1205,7 @@ static struct phy_driver dp83867_driver[] = { .led_hw_is_supported = dp83867_led_hw_is_supported, .led_hw_control_set = dp83867_led_hw_control_set, .led_hw_control_get = dp83867_led_hw_control_get, + .led_polarity_set = dp83867_led_polarity_set, }, }; module_phy_driver(dp83867_driver); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index eba652a4c1d8..1faa22f58366 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1461,7 +1461,7 @@ static int m88e1540_get_fld(struct phy_device *phydev, u8 *msecs) static int m88e1540_set_fld(struct phy_device *phydev, const u8 *msecs) { - struct ethtool_eee eee; + struct ethtool_keee eee; int val, ret; if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index afbad1ad8683..b765466e767f 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -459,19 +459,34 @@ EXPORT_SYMBOL(of_mdio_find_bus); * found, set the of_node pointer for the mdio device. This allows * auto-probed phy devices to be supplied with information passed in * via DT. + * If a PHY package is found, PHY is searched also there. */ -static void of_mdiobus_link_mdiodev(struct mii_bus *bus, - struct mdio_device *mdiodev) +static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev, + struct device_node *np) { - struct device *dev = &mdiodev->dev; struct device_node *child; - if (dev->of_node || !bus->dev.of_node) - return; - - for_each_available_child_of_node(bus->dev.of_node, child) { + for_each_available_child_of_node(np, child) { int addr; + if (of_node_name_eq(child, "ethernet-phy-package")) { + /* Validate PHY package reg presence */ + if (!of_property_present(child, "reg")) { + of_node_put(child); + return -EINVAL; + } + + if (!of_mdiobus_find_phy(dev, mdiodev, child)) { + /* The refcount for the PHY package will be + * incremented later when PHY join the Package. + */ + of_node_put(child); + return 0; + } + + continue; + } + addr = of_mdio_parse_addr(dev, child); if (addr < 0) continue; @@ -481,9 +496,22 @@ static void of_mdiobus_link_mdiodev(struct mii_bus *bus, /* The refcount on "child" is passed to the mdio * device. Do _not_ use of_node_put(child) here. */ - return; + return 0; } } + + return -ENODEV; +} + +static void of_mdiobus_link_mdiodev(struct mii_bus *bus, + struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + + if (dev->of_node || !bus->dev.of_node) + return; + + of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node); } #else /* !IS_ENABLED(CONFIG_OF_MDIO) */ static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, @@ -1398,7 +1426,7 @@ static const struct attribute_group *mdio_bus_dev_groups[] = { NULL, }; -struct bus_type mdio_bus_type = { +const struct bus_type mdio_bus_type = { .name = "mdio_bus", .dev_groups = mdio_bus_dev_groups, .match = mdio_bus_match, diff --git a/drivers/net/phy/mdio_devres.c b/drivers/net/phy/mdio_devres.c index 69b829e6ab35..7fd3377dbd79 100644 --- a/drivers/net/phy/mdio_devres.c +++ b/drivers/net/phy/mdio_devres.c @@ -131,4 +131,5 @@ int __devm_of_mdiobus_register(struct device *dev, struct mii_bus *mdio, EXPORT_SYMBOL(__devm_of_mdiobus_register); #endif /* CONFIG_OF_MDIO */ +MODULE_DESCRIPTION("Network MDIO bus devres helpers"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/mediatek-ge-soc.c b/drivers/net/phy/mediatek-ge-soc.c index 8a20d9889f10..0f3a1538a8b8 100644 --- a/drivers/net/phy/mediatek-ge-soc.c +++ b/drivers/net/phy/mediatek-ge-soc.c @@ -489,7 +489,7 @@ static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, u16 reg, val; if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) - bias = -2; + bias = -1; val = clamp_val(bias + tx_r50_cal_val, 0, 63); @@ -705,6 +705,11 @@ restore: static void mt798x_phy_common_finetune(struct phy_device *phydev) { phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ + __phy_write(phydev, 0x11, 0xc71); + __phy_write(phydev, 0x12, 0xc); + __phy_write(phydev, 0x10, 0x8fae); + /* EnabRandUpdTrig = 1 */ __phy_write(phydev, 0x11, 0x2f00); __phy_write(phydev, 0x12, 0xe); @@ -715,15 +720,56 @@ static void mt798x_phy_common_finetune(struct phy_device *phydev) __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x83aa); - /* TrFreeze = 0 */ + /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ + __phy_write(phydev, 0x11, 0x240); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9680); + + /* TrFreeze = 0 (mt7988 default) */ __phy_write(phydev, 0x11, 0x0); __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x9686); + /* SSTrKp100 = 5 */ + /* SSTrKf100 = 6 */ + /* SSTrKp1000Mas = 5 */ + /* SSTrKf1000Mas = 6 */ /* SSTrKp1000Slv = 5 */ + /* SSTrKf1000Slv = 6 */ __phy_write(phydev, 0x11, 0xbaef); __phy_write(phydev, 0x12, 0x2e); __phy_write(phydev, 0x10, 0x968c); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +} + +static void mt7981_phy_finetune(struct phy_device *phydev) +{ + u16 val[8] = { 0x01ce, 0x01c1, + 0x020f, 0x0202, + 0x03d0, 0x03c0, + 0x0013, 0x0005 }; + int i, k; + + /* 100M eye finetune: + * Keep middle level of TX MLT3 shapper as default. + * Only change TX MLT3 overshoot level here. + */ + for (k = 0, i = 1; i < 12; i++) { + if (i % 3 == 0) + continue; + phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); + } + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* ResetSyncOffset = 6 */ + __phy_write(phydev, 0x11, 0x600); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8fc0); + + /* VgaDecRate = 1 */ + __phy_write(phydev, 0x11, 0x4c2a); + __phy_write(phydev, 0x12, 0x3e); + __phy_write(phydev, 0x10, 0x8fa4); /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 @@ -738,7 +784,7 @@ static void mt798x_phy_common_finetune(struct phy_device *phydev) __phy_write(phydev, 0x10, 0x8ec0); phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); - /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); @@ -771,48 +817,6 @@ static void mt798x_phy_common_finetune(struct phy_device *phydev) phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); } -static void mt7981_phy_finetune(struct phy_device *phydev) -{ - u16 val[8] = { 0x01ce, 0x01c1, - 0x020f, 0x0202, - 0x03d0, 0x03c0, - 0x0013, 0x0005 }; - int i, k; - - /* 100M eye finetune: - * Keep middle level of TX MLT3 shapper as default. - * Only change TX MLT3 overshoot level here. - */ - for (k = 0, i = 1; i < 12; i++) { - if (i % 3 == 0) - continue; - phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); - } - - phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); - /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ - __phy_write(phydev, 0x11, 0xc71); - __phy_write(phydev, 0x12, 0xc); - __phy_write(phydev, 0x10, 0x8fae); - - /* ResetSyncOffset = 6 */ - __phy_write(phydev, 0x11, 0x600); - __phy_write(phydev, 0x12, 0x0); - __phy_write(phydev, 0x10, 0x8fc0); - - /* VgaDecRate = 1 */ - __phy_write(phydev, 0x11, 0x4c2a); - __phy_write(phydev, 0x12, 0x3e); - __phy_write(phydev, 0x10, 0x8fa4); - - /* FfeUpdGainForce = 4 */ - __phy_write(phydev, 0x11, 0x240); - __phy_write(phydev, 0x12, 0x0); - __phy_write(phydev, 0x10, 0x9680); - - phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); -} - static void mt7988_phy_finetune(struct phy_device *phydev) { u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, @@ -827,17 +831,7 @@ static void mt7988_phy_finetune(struct phy_device *phydev) /* TCT finetune */ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); - /* Disable TX power saving */ - phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, - MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); - phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); - - /* SlvDSPreadyTime = 24, MasDSPreadyTime = 12 */ - __phy_write(phydev, 0x11, 0x671); - __phy_write(phydev, 0x12, 0xc); - __phy_write(phydev, 0x10, 0x8fae); - /* ResetSyncOffset = 5 */ __phy_write(phydev, 0x11, 0x500); __phy_write(phydev, 0x12, 0x0); @@ -845,13 +839,27 @@ static void mt7988_phy_finetune(struct phy_device *phydev) /* VgaDecRate is 1 at default on mt7988 */ - phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, + * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 + */ + __phy_write(phydev, 0x11, 0xb90a); + __phy_write(phydev, 0x12, 0x6f); + __phy_write(phydev, 0x10, 0x8f82); + + /* RemAckCntLimitCtrl = 1 */ + __phy_write(phydev, 0x11, 0xfbba); + __phy_write(phydev, 0x12, 0xc3); + __phy_write(phydev, 0x10, 0x87f8); - phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_2A30); - /* TxClkOffset = 2 */ - __phy_modify(phydev, MTK_PHY_ANARG_RG, MTK_PHY_TCLKOFFSET_MASK, - FIELD_PREP(MTK_PHY_TCLKOFFSET_MASK, 0x2)); phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, + MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); + + /* rg_tr_lpf_cnt_val = 1023 */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x3ff); } static void mt798x_phy_eee(struct phy_device *phydev) @@ -884,11 +892,11 @@ static void mt798x_phy_eee(struct phy_device *phydev) MTK_PHY_LPI_SLV_SEND_TX_EN, FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); - phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, - MTK_PHY_LPI_SEND_LOC_TIMER_MASK | - MTK_PHY_LPI_TXPCS_LOC_RCV, - FIELD_PREP(MTK_PHY_LPI_SEND_LOC_TIMER_MASK, 0x117)); + /* Keep MTK_PHY_LPI_SEND_LOC_TIMER as 375 */ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, + MTK_PHY_LPI_TXPCS_LOC_RCV); + /* This also fixes some IoT issues, such as CH340 */ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | @@ -922,7 +930,7 @@ static void mt798x_phy_eee(struct phy_device *phydev) __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x9690); - /* REG_EEE_st2TrKf1000 = 3 */ + /* REG_EEE_st2TrKf1000 = 2 */ __phy_write(phydev, 0x11, 0x114f); __phy_write(phydev, 0x12, 0x2); __phy_write(phydev, 0x10, 0x969a); @@ -947,7 +955,7 @@ static void mt798x_phy_eee(struct phy_device *phydev) __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x96b8); - /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 1 */ + /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ __phy_write(phydev, 0x11, 0x1463); __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x96ca); @@ -1459,6 +1467,13 @@ static int mt7988_phy_probe(struct phy_device *phydev) if (err) return err; + /* Disable TX power saving at probing to: + * 1. Meet common mode compliance test criteria + * 2. Make sure that TX-VCM calibration works fine + */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, + MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); + return mt798x_phy_calibration(phydev); } diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index dad720138baa..9b6973581989 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -120,6 +120,12 @@ */ #define LAN8814_1PPM_FORMAT 17179 +/* Represents 1ppm adjustment in 2^32 format with + * each nsec contains 8 clock cycles. + * The value is calculated as following: (1/1000000)/((2^-32)/8) + */ +#define LAN8841_1PPM_FORMAT 34360 + #define PTP_RX_VERSION 0x0248 #define PTP_TX_VERSION 0x0288 #define PTP_MAX_VERSION(x) (((x) & GENMASK(7, 0)) << 8) @@ -154,11 +160,13 @@ #define PTP_CMD_CTL_PTP_LTC_STEP_SEC_ BIT(5) #define PTP_CMD_CTL_PTP_LTC_STEP_NSEC_ BIT(6) +#define PTP_CLOCK_SET_SEC_HI 0x0205 #define PTP_CLOCK_SET_SEC_MID 0x0206 #define PTP_CLOCK_SET_SEC_LO 0x0207 #define PTP_CLOCK_SET_NS_HI 0x0208 #define PTP_CLOCK_SET_NS_LO 0x0209 +#define PTP_CLOCK_READ_SEC_HI 0x0229 #define PTP_CLOCK_READ_SEC_MID 0x022A #define PTP_CLOCK_READ_SEC_LO 0x022B #define PTP_CLOCK_READ_NS_HI 0x022C @@ -2592,35 +2600,31 @@ static bool lan8814_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb } static void lan8814_ptp_clock_set(struct phy_device *phydev, - u32 seconds, u32 nano_seconds) + time64_t sec, u32 nsec) { - u32 sec_low, sec_high, nsec_low, nsec_high; - - sec_low = seconds & 0xffff; - sec_high = (seconds >> 16) & 0xffff; - nsec_low = nano_seconds & 0xffff; - nsec_high = (nano_seconds >> 16) & 0x3fff; - - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, sec_low); - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, sec_high); - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, nsec_low); - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, nsec_high); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, lower_16_bits(sec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, upper_16_bits(sec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_HI, upper_32_bits(sec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, lower_16_bits(nsec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, upper_16_bits(nsec)); lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_LOAD_); } static void lan8814_ptp_clock_get(struct phy_device *phydev, - u32 *seconds, u32 *nano_seconds) + time64_t *sec, u32 *nsec) { lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_); - *seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID); - *seconds = (*seconds << 16) | - lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO); + *sec = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_HI); + *sec <<= 16; + *sec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID); + *sec <<= 16; + *sec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO); - *nano_seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI); - *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | - lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO); + *nsec = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI); + *nsec <<= 16; + *nsec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO); } static int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci, @@ -2630,7 +2634,7 @@ static int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci, ptp_clock_info); struct phy_device *phydev = shared->phydev; u32 nano_seconds; - u32 seconds; + time64_t seconds; mutex_lock(&shared->shared_lock); lan8814_ptp_clock_get(phydev, &seconds, &nano_seconds); @@ -2660,38 +2664,37 @@ static void lan8814_ptp_clock_step(struct phy_device *phydev, { u32 nano_seconds_step; u64 abs_time_step_ns; - u32 unsigned_seconds; + time64_t set_seconds; u32 nano_seconds; u32 remainder; s32 seconds; if (time_step_ns > 15000000000LL) { /* convert to clock set */ - lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); - unsigned_seconds += div_u64_rem(time_step_ns, 1000000000LL, - &remainder); + lan8814_ptp_clock_get(phydev, &set_seconds, &nano_seconds); + set_seconds += div_u64_rem(time_step_ns, 1000000000LL, + &remainder); nano_seconds += remainder; if (nano_seconds >= 1000000000) { - unsigned_seconds++; + set_seconds++; nano_seconds -= 1000000000; } - lan8814_ptp_clock_set(phydev, unsigned_seconds, nano_seconds); + lan8814_ptp_clock_set(phydev, set_seconds, nano_seconds); return; } else if (time_step_ns < -15000000000LL) { /* convert to clock set */ time_step_ns = -time_step_ns; - lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); - unsigned_seconds -= div_u64_rem(time_step_ns, 1000000000LL, - &remainder); + lan8814_ptp_clock_get(phydev, &set_seconds, &nano_seconds); + set_seconds -= div_u64_rem(time_step_ns, 1000000000LL, + &remainder); nano_seconds_step = remainder; if (nano_seconds < nano_seconds_step) { - unsigned_seconds--; + set_seconds--; nano_seconds += 1000000000; } nano_seconds -= nano_seconds_step; - lan8814_ptp_clock_set(phydev, unsigned_seconds, - nano_seconds); + lan8814_ptp_clock_set(phydev, set_seconds, nano_seconds); return; } @@ -4118,8 +4121,8 @@ static int lan8841_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) faster = false; } - rate = LAN8814_1PPM_FORMAT * (upper_16_bits(scaled_ppm)); - rate += (LAN8814_1PPM_FORMAT * (lower_16_bits(scaled_ppm))) >> 16; + rate = LAN8841_1PPM_FORMAT * (upper_16_bits(scaled_ppm)); + rate += (LAN8841_1PPM_FORMAT * (lower_16_bits(scaled_ppm))) >> 16; mutex_lock(&ptp_priv->ptp_lock); phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_RATE_ADJ_HI, diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 747d14bf152c..c69568e7695e 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -706,6 +706,22 @@ int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv) changed = 1; } + if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP2_FEATURES)) { + val = linkmode_to_mii_eee_cap2_t(adv); + + /* IEEE 802.3-2022 45.2.7.16 EEE advertisement 2 + * (Register 7.62) + */ + val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, + MDIO_AN_EEE_ADV2, + MDIO_EEE_2_5GT | MDIO_EEE_5GT, + val); + if (val < 0) + return val; + if (val > 0) + changed = 1; + } + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, phydev->supported_eee)) { val = linkmode_adv_to_mii_10base_t1_t(adv); @@ -745,6 +761,17 @@ int genphy_c45_read_eee_adv(struct phy_device *phydev, unsigned long *adv) mii_eee_cap1_mod_linkmode_t(adv, val); } + if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP2_FEATURES)) { + /* IEEE 802.3-2022 45.2.7.16 EEE advertisement 2 + * (Register 7.62) + */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV2); + if (val < 0) + return val; + + mii_eee_cap2_mod_linkmode_adv_t(adv, val); + } + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, phydev->supported_eee)) { /* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register @@ -781,6 +808,17 @@ static int genphy_c45_read_eee_lpa(struct phy_device *phydev, mii_eee_cap1_mod_linkmode_t(lpa, val); } + if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP2_FEATURES)) { + /* IEEE 802.3-2022 45.2.7.17 EEE link partner ability 2 + * (Register 7.63) + */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE2); + if (val < 0) + return val; + + mii_eee_cap2_mod_linkmode_adv_t(lpa, val); + } + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, phydev->supported_eee)) { /* IEEE 802.3cg-2019 45.2.7.26 10BASE-T1 AN status register @@ -831,6 +869,30 @@ static int genphy_c45_read_eee_cap1(struct phy_device *phydev) } /** + * genphy_c45_read_eee_cap2 - read supported EEE link modes from register 3.21 + * @phydev: target phy_device struct + */ +static int genphy_c45_read_eee_cap2(struct phy_device *phydev) +{ + int val; + + /* IEEE 802.3-2022 45.2.3.11 EEE control and capability 2 + * (Register 3.21) + */ + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE2); + if (val < 0) + return val; + + /* IEEE 802.3-2022 45.2.3.11 says 9 bits are reserved. */ + if (val == 0xffff) + return 0; + + mii_eee_cap2_mod_linkmode_sup_t(phydev->supported_eee, val); + + return 0; +} + +/** * genphy_c45_read_eee_abilities - read supported EEE link modes * @phydev: target phy_device struct */ @@ -848,6 +910,13 @@ int genphy_c45_read_eee_abilities(struct phy_device *phydev) return val; } + /* Same for cap2 (3.21) */ + if (linkmode_intersects(phydev->supported, PHY_EEE_CAP2_FEATURES)) { + val = genphy_c45_read_eee_cap2(phydev); + if (val) + return val; + } + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, phydev->supported)) { /* IEEE 802.3cg-2019 45.2.1.186b 10BASE-T1L PMA status register @@ -1443,17 +1512,17 @@ EXPORT_SYMBOL(genphy_c45_eee_is_active); /** * genphy_c45_ethtool_get_eee - get EEE supported and status * @phydev: target phy_device struct - * @data: ethtool_eee data + * @data: ethtool_keee data * * Description: it reports the Supported/Advertisement/LP Advertisement * capabilities. */ int genphy_c45_ethtool_get_eee(struct phy_device *phydev, - struct ethtool_eee *data) + struct ethtool_keee *data) { __ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {}; __ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {}; - bool overflow = false, is_enabled; + bool is_enabled; int ret; ret = genphy_c45_eee_is_active(phydev, adv, lp, &is_enabled); @@ -1462,17 +1531,9 @@ int genphy_c45_ethtool_get_eee(struct phy_device *phydev, data->eee_enabled = is_enabled; data->eee_active = ret; - - if (!ethtool_convert_link_mode_to_legacy_u32(&data->supported, - phydev->supported_eee)) - overflow = true; - if (!ethtool_convert_link_mode_to_legacy_u32(&data->advertised, adv)) - overflow = true; - if (!ethtool_convert_link_mode_to_legacy_u32(&data->lp_advertised, lp)) - overflow = true; - - if (overflow) - phydev_warn(phydev, "Not all supported or advertised EEE link modes were passed to the user space\n"); + linkmode_copy(data->supported, phydev->supported_eee); + linkmode_copy(data->advertised, adv); + linkmode_copy(data->lp_advertised, lp); return 0; } @@ -1481,7 +1542,7 @@ EXPORT_SYMBOL(genphy_c45_ethtool_get_eee); /** * genphy_c45_ethtool_set_eee - set EEE supported and status * @phydev: target phy_device struct - * @data: ethtool_eee data + * @data: ethtool_keee data * * Description: sets the Supported/Advertisement/LP Advertisement * capabilities. If eee_enabled is false, no links modes are @@ -1490,29 +1551,27 @@ EXPORT_SYMBOL(genphy_c45_ethtool_get_eee); * non-destructive way. */ int genphy_c45_ethtool_set_eee(struct phy_device *phydev, - struct ethtool_eee *data) + struct ethtool_keee *data) { int ret; if (data->eee_enabled) { - if (data->advertised) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(adv); + unsigned long *adv = data->advertised; - ethtool_convert_legacy_u32_to_link_mode(adv, - data->advertised); - linkmode_andnot(adv, adv, phydev->supported_eee); - if (!linkmode_empty(adv)) { + if (!linkmode_empty(adv)) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp); + bool unsupp; + + unsupp = linkmode_andnot(tmp, adv, phydev->supported_eee); + if (unsupp) { phydev_warn(phydev, "At least some EEE link modes are not supported.\n"); return -EINVAL; } - - ethtool_convert_legacy_u32_to_link_mode(phydev->advertising_eee, - data->advertised); } else { - linkmode_copy(phydev->advertising_eee, - phydev->supported_eee); + adv = phydev->supported_eee; } + linkmode_copy(phydev->advertising_eee, adv); phydev->eee_enabled = true; } else { phydev->eee_enabled = false; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 3376e58e2b88..14224e06d69f 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1290,7 +1290,6 @@ int phy_disable_interrupts(struct phy_device *phydev) static irqreturn_t phy_interrupt(int irq, void *phy_dat) { struct phy_device *phydev = phy_dat; - struct phy_driver *drv = phydev->drv; irqreturn_t ret; /* Wakeup interrupts may occur during a system sleep transition. @@ -1316,7 +1315,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) } mutex_lock(&phydev->lock); - ret = drv->handle_interrupt(phydev); + ret = phydev->drv->handle_interrupt(phydev); mutex_unlock(&phydev->lock); return ret; @@ -1632,12 +1631,12 @@ EXPORT_SYMBOL(phy_get_eee_err); /** * phy_ethtool_get_eee - get EEE supported and status * @phydev: target phy_device struct - * @data: ethtool_eee data + * @data: ethtool_keee data * * Description: it reportes the Supported/Advertisement/LP Advertisement * capabilities. */ -int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) +int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_keee *data) { int ret; @@ -1655,11 +1654,11 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); /** * phy_ethtool_set_eee - set EEE supported and status * @phydev: target phy_device struct - * @data: ethtool_eee data + * @data: ethtool_keee data * * Description: it is to program the Advertisement EEE register. */ -int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) +int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_keee *data) { int ret; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3611ea64875e..2eefee970851 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -148,6 +148,14 @@ static const int phy_eee_cap1_features_array[] = { __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init; EXPORT_SYMBOL_GPL(phy_eee_cap1_features); +static const int phy_eee_cap2_features_array[] = { + ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + ETHTOOL_LINK_MODE_5000baseT_Full_BIT, +}; + +__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap2_features) __ro_after_init; +EXPORT_SYMBOL_GPL(phy_eee_cap2_features); + static void features_init(void) { /* 10/100 half/full*/ @@ -232,6 +240,9 @@ static void features_init(void) linkmode_set_bit_array(phy_eee_cap1_features_array, ARRAY_SIZE(phy_eee_cap1_features_array), phy_eee_cap1_features); + linkmode_set_bit_array(phy_eee_cap2_features_array, + ARRAY_SIZE(phy_eee_cap2_features_array), + phy_eee_cap2_features); } @@ -780,7 +791,7 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, * and identifiers in @c45_ids. * * Returns zero on success, %-EIO on bus access error, or %-ENODEV if - * the "devices in package" is invalid. + * the "devices in package" is invalid or no device responds. */ static int get_phy_c45_ids(struct mii_bus *bus, int addr, struct phy_c45_device_ids *c45_ids) @@ -803,7 +814,11 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, */ ret = phy_c45_probe_present(bus, addr, i); if (ret < 0) - return -EIO; + /* returning -ENODEV doesn't stop bus + * scanning + */ + return (phy_reg == -EIO || + phy_reg == -ENODEV) ? -ENODEV : -EIO; if (!ret) continue; @@ -1413,6 +1428,11 @@ int phy_sfp_probe(struct phy_device *phydev, } EXPORT_SYMBOL(phy_sfp_probe); +static bool phy_drv_supports_irq(const struct phy_driver *phydrv) +{ + return phydrv->config_intr && phydrv->handle_interrupt; +} + /** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach @@ -1527,6 +1547,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, if (phydev->dev_flags & PHY_F_NO_IRQ) phydev->irq = PHY_POLL; + if (!phy_drv_supports_irq(phydev->drv) && phy_interrupt_is_valid(phydev)) + phydev->irq = PHY_POLL; + /* Port is set to PORT_TP by default and the actual PHY driver will set * it to different value depending on the PHY configuration. If we have * the generic PHY driver we can't figure it out, thus set the old @@ -1592,7 +1615,6 @@ EXPORT_SYMBOL(phy_attach_direct); struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, phy_interface_t interface) { - struct bus_type *bus = &mdio_bus_type; struct phy_device *phydev; struct device *d; int rc; @@ -1603,7 +1625,7 @@ struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, /* Search the list of PHY devices on the mdio bus for the * PHY with the requested name */ - d = bus_find_device_by_name(bus, NULL, bus_id); + d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id); if (!d) { pr_err("PHY %s not found\n", bus_id); return ERR_PTR(-ENODEV); @@ -1700,6 +1722,7 @@ int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size) shared->priv_size = priv_size; } shared->base_addr = base_addr; + shared->np = NULL; refcount_set(&shared->refcnt, 1); bus->shared[base_addr] = shared; } else { @@ -1723,6 +1746,63 @@ err_unlock: EXPORT_SYMBOL_GPL(phy_package_join); /** + * of_phy_package_join - join a common PHY group in PHY package + * @phydev: target phy_device struct + * @priv_size: if non-zero allocate this amount of bytes for private data + * + * This is a variant of phy_package_join for PHY package defined in DT. + * + * The parent node of the @phydev is checked as a valid PHY package node + * structure (by matching the node name "ethernet-phy-package") and the + * base_addr for the PHY package is passed to phy_package_join. + * + * With this configuration the shared struct will also have the np value + * filled to use additional DT defined properties in PHY specific + * probe_once and config_init_once PHY package OPs. + * + * Returns < 0 on error, 0 on success. Esp. calling phy_package_join() + * with the same cookie but a different priv_size is an error. Or a parent + * node is not detected or is not valid or doesn't match the expected node + * name for PHY package. + */ +int of_phy_package_join(struct phy_device *phydev, size_t priv_size) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct device_node *package_node; + u32 base_addr; + int ret; + + if (!node) + return -EINVAL; + + package_node = of_get_parent(node); + if (!package_node) + return -EINVAL; + + if (!of_node_name_eq(package_node, "ethernet-phy-package")) { + ret = -EINVAL; + goto exit; + } + + if (of_property_read_u32(package_node, "reg", &base_addr)) { + ret = -EINVAL; + goto exit; + } + + ret = phy_package_join(phydev, base_addr, priv_size); + if (ret) + goto exit; + + phydev->shared->np = package_node; + + return 0; +exit: + of_node_put(package_node); + return ret; +} +EXPORT_SYMBOL_GPL(of_phy_package_join); + +/** * phy_package_leave - leave a common PHY group * @phydev: target phy_device struct * @@ -1738,6 +1818,10 @@ void phy_package_leave(struct phy_device *phydev) if (!shared) return; + /* Decrease the node refcount on leave if present */ + if (shared->np) + of_node_put(shared->np); + if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { bus->shared[shared->base_addr] = NULL; mutex_unlock(&bus->shared_lock); @@ -1791,6 +1875,40 @@ int devm_phy_package_join(struct device *dev, struct phy_device *phydev, EXPORT_SYMBOL_GPL(devm_phy_package_join); /** + * devm_of_phy_package_join - resource managed of_phy_package_join() + * @dev: device that is registering this PHY package + * @phydev: target phy_device struct + * @priv_size: if non-zero allocate this amount of bytes for private data + * + * Managed of_phy_package_join(). Shared storage fetched by this function, + * phy_package_leave() is automatically called on driver detach. See + * of_phy_package_join() for more information. + */ +int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev, + size_t priv_size) +{ + struct phy_device **ptr; + int ret; + + ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = of_phy_package_join(phydev, priv_size); + + if (!ret) { + *ptr = phydev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_of_phy_package_join); + +/** * phy_detach - detach a PHY device from its network device * @phydev: target phy_device struct * @@ -1859,7 +1977,7 @@ int phy_suspend(struct phy_device *phydev) { struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct net_device *netdev = phydev->attached_dev; - struct phy_driver *phydrv = phydev->drv; + const struct phy_driver *phydrv = phydev->drv; int ret; if (phydev->suspended) @@ -1884,7 +2002,7 @@ EXPORT_SYMBOL(phy_suspend); int __phy_resume(struct phy_device *phydev) { - struct phy_driver *phydrv = phydev->drv; + const struct phy_driver *phydrv = phydev->drv; int ret; lockdep_assert_held(&phydev->lock); @@ -2513,12 +2631,15 @@ EXPORT_SYMBOL(genphy_read_status); /** * genphy_c37_read_status - check the link status and update current link state * @phydev: target phy_device struct + * @changed: pointer where to store if link changed * * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. This function is for Clause 37 1000Base-X mode. + * + * If link has changed, @changed is set to true, false otherwise. */ -int genphy_c37_read_status(struct phy_device *phydev) +int genphy_c37_read_status(struct phy_device *phydev, bool *changed) { int lpa, err, old_link = phydev->link; @@ -2528,9 +2649,13 @@ int genphy_c37_read_status(struct phy_device *phydev) return err; /* why bother the PHY if nothing can have changed */ - if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) { + *changed = false; return 0; + } + /* Signal link has changed */ + *changed = true; phydev->duplex = DUPLEX_UNKNOWN; phydev->pause = 0; phydev->asym_pause = 0; @@ -2770,6 +2895,22 @@ void phy_advertise_supported(struct phy_device *phydev) EXPORT_SYMBOL(phy_advertise_supported); /** + * phy_advertise_eee_all - Advertise all supported EEE modes + * @phydev: target phy_device struct + * + * Description: Per default phylib preserves the EEE advertising at the time of + * phy probing, which might be a subset of the supported EEE modes. Use this + * function when all supported EEE modes should be advertised. This does not + * trigger auto-negotiation, so must be called before phy_start()/ + * phylink_start() which will start auto-negotiation. + */ +void phy_advertise_eee_all(struct phy_device *phydev) +{ + linkmode_copy(phydev->advertising_eee, phydev->supported_eee); +} +EXPORT_SYMBOL_GPL(phy_advertise_eee_all); + +/** * phy_support_sym_pause - Enable support of symmetrical pause * @phydev: target phy_device struct * @@ -2992,11 +3133,6 @@ s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, } EXPORT_SYMBOL(phy_get_internal_delay); -static bool phy_drv_supports_irq(struct phy_driver *phydrv) -{ - return phydrv->config_intr && phydrv->handle_interrupt; -} - static int phy_led_set_brightness(struct led_classdev *led_cdev, enum led_brightness value) { @@ -3097,6 +3233,7 @@ static int of_phy_led(struct phy_device *phydev, struct device *dev = &phydev->mdio.dev; struct led_init_data init_data = {}; struct led_classdev *cdev; + unsigned long modes = 0; struct phy_led *phyled; u32 index; int err; @@ -3114,6 +3251,21 @@ static int of_phy_led(struct phy_device *phydev, if (index > U8_MAX) return -EINVAL; + if (of_property_read_bool(led, "active-low")) + set_bit(PHY_LED_ACTIVE_LOW, &modes); + if (of_property_read_bool(led, "inactive-high-impedance")) + set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes); + + if (modes) { + /* Return error if asked to set polarity modes but not supported */ + if (!phydev->drv->led_polarity_set) + return -EINVAL; + + err = phydev->drv->led_polarity_set(phydev, index, modes); + if (err) + return err; + } + phyled->index = index; if (phydev->drv->led_brightness_set) cdev->brightness_set_blocking = phy_led_set_brightness; diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index ed0b4ccaa6a6..503fd7c40523 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2764,9 +2764,9 @@ EXPORT_SYMBOL_GPL(phylink_init_eee); /** * phylink_ethtool_get_eee() - read the energy efficient ethernet parameters * @pl: a pointer to a &struct phylink returned from phylink_create() - * @eee: a pointer to a &struct ethtool_eee for the read parameters + * @eee: a pointer to a &struct ethtool_keee for the read parameters */ -int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee) +int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_keee *eee) { int ret = -EOPNOTSUPP; @@ -2782,9 +2782,9 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); /** * phylink_ethtool_set_eee() - set the energy efficient ethernet parameters * @pl: a pointer to a &struct phylink returned from phylink_create() - * @eee: a pointer to a &struct ethtool_eee for the desired parameters + * @eee: a pointer to a &struct ethtool_keee for the desired parameters */ -int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee) +int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee) { int ret = -EOPNOTSUPP; diff --git a/drivers/net/phy/qcom/Kconfig b/drivers/net/phy/qcom/Kconfig new file mode 100644 index 000000000000..570626cc8e14 --- /dev/null +++ b/drivers/net/phy/qcom/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-only +config QCOM_NET_PHYLIB + tristate + +config AT803X_PHY + tristate "Qualcomm Atheros AR803X PHYs" + select QCOM_NET_PHYLIB + depends on REGULATOR + help + Currently supports the AR8030, AR8031, AR8033, AR8035 model + +config QCA83XX_PHY + tristate "Qualcomm Atheros QCA833x PHYs" + select QCOM_NET_PHYLIB + help + Currently supports the internal QCA8337(Internal qca8k PHY) model + +config QCA808X_PHY + tristate "Qualcomm QCA808x PHYs" + select QCOM_NET_PHYLIB + help + Currently supports the QCA8081 model + +config QCA807X_PHY + tristate "Qualcomm QCA807x PHYs" + select QCOM_NET_PHYLIB + depends on OF_MDIO + help + Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII + control PHY. diff --git a/drivers/net/phy/qcom/Makefile b/drivers/net/phy/qcom/Makefile new file mode 100644 index 000000000000..f24fb550babd --- /dev/null +++ b/drivers/net/phy/qcom/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o +obj-$(CONFIG_AT803X_PHY) += at803x.o +obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o +obj-$(CONFIG_QCA808X_PHY) += qca808x.o +obj-$(CONFIG_QCA807X_PHY) += qca807x.o diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c new file mode 100644 index 000000000000..4717c59d51d0 --- /dev/null +++ b/drivers/net/phy/qcom/at803x.c @@ -0,0 +1,1106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * drivers/net/phy/at803x.c + * + * Driver for Qualcomm Atheros AR803x PHY + * + * Author: Matus Ujhelyi <ujhelyi.m@gmail.com> + */ + +#include <linux/phy.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool_netlink.h> +#include <linux/bitfield.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/consumer.h> +#include <linux/of.h> +#include <linux/phylink.h> +#include <linux/sfp.h> +#include <dt-bindings/net/qca-ar803x.h> + +#include "qcom.h" + +#define AT803X_LED_CONTROL 0x18 + +#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 +#define AT803X_WOL_EN BIT(5) + +#define AT803X_REG_CHIP_CONFIG 0x1f +#define AT803X_BT_BX_REG_SEL 0x8000 + +#define AT803X_MODE_CFG_MASK 0x0F +#define AT803X_MODE_CFG_BASET_RGMII 0x00 +#define AT803X_MODE_CFG_BASET_SGMII 0x01 +#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02 +#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03 +#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04 +#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05 +#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06 +#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07 +#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B +#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E +#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F + +#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ +#define AT803X_PSSR_MR_AN_COMPLETE 0x0200 + +#define AT803X_DEBUG_REG_1F 0x1F +#define AT803X_DEBUG_PLL_ON BIT(2) +#define AT803X_DEBUG_RGMII_1V8 BIT(3) + +/* AT803x supports either the XTAL input pad, an internal PLL or the + * DSP as clock reference for the clock output pad. The XTAL reference + * is only used for 25 MHz output, all other frequencies need the PLL. + * The DSP as a clock reference is used in synchronous ethernet + * applications. + * + * By default the PLL is only enabled if there is a link. Otherwise + * the PHY will go into low power state and disabled the PLL. You can + * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always + * enabled. + */ +#define AT803X_MMD7_CLK25M 0x8016 +#define AT803X_CLK_OUT_MASK GENMASK(4, 2) +#define AT803X_CLK_OUT_25MHZ_XTAL 0 +#define AT803X_CLK_OUT_25MHZ_DSP 1 +#define AT803X_CLK_OUT_50MHZ_PLL 2 +#define AT803X_CLK_OUT_50MHZ_DSP 3 +#define AT803X_CLK_OUT_62_5MHZ_PLL 4 +#define AT803X_CLK_OUT_62_5MHZ_DSP 5 +#define AT803X_CLK_OUT_125MHZ_PLL 6 +#define AT803X_CLK_OUT_125MHZ_DSP 7 + +/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask + * but doesn't support choosing between XTAL/PLL and DSP. + */ +#define AT8035_CLK_OUT_MASK GENMASK(4, 3) + +#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) +#define AT803X_CLK_OUT_STRENGTH_FULL 0 +#define AT803X_CLK_OUT_STRENGTH_HALF 1 +#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 + +#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 +#define ATH8032_PHY_ID 0x004dd023 +#define ATH8035_PHY_ID 0x004dd072 +#define AT8030_PHY_ID_MASK 0xffffffef + +#define QCA9561_PHY_ID 0x004dd042 + +#define AT803X_PAGE_FIBER 0 +#define AT803X_PAGE_COPPER 1 + +/* don't turn off internal PLL */ +#define AT803X_KEEP_PLL_ENABLED BIT(0) +#define AT803X_DISABLE_SMARTEEE BIT(1) + +/* disable hibernation mode */ +#define AT803X_DISABLE_HIBERNATION_MODE BIT(2) + +MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); +MODULE_AUTHOR("Matus Ujhelyi"); +MODULE_LICENSE("GPL"); + +struct at803x_priv { + int flags; + u16 clk_25m_reg; + u16 clk_25m_mask; + u8 smarteee_lpi_tw_1g; + u8 smarteee_lpi_tw_100m; + bool is_fiber; + bool is_1000basex; + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; +}; + +struct at803x_context { + u16 bmcr; + u16 advertise; + u16 control1000; + u16 int_enable; + u16 smart_speed; + u16 led_control; +}; + +static int at803x_write_page(struct phy_device *phydev, int page) +{ + int mask; + int set; + + if (page == AT803X_PAGE_COPPER) { + set = AT803X_BT_BX_REG_SEL; + mask = 0; + } else { + set = 0; + mask = AT803X_BT_BX_REG_SEL; + } + + return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); +} + +static int at803x_read_page(struct phy_device *phydev) +{ + int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); + + if (ccr < 0) + return ccr; + + if (ccr & AT803X_BT_BX_REG_SEL) + return AT803X_PAGE_COPPER; + + return AT803X_PAGE_FIBER; +} + +static int at803x_enable_rx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0, + AT803X_DEBUG_RX_CLK_DLY_EN); +} + +static int at803x_enable_tx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0, + AT803X_DEBUG_TX_CLK_DLY_EN); +} + +static int at803x_disable_rx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + AT803X_DEBUG_RX_CLK_DLY_EN, 0); +} + +static int at803x_disable_tx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, + AT803X_DEBUG_TX_CLK_DLY_EN, 0); +} + +/* save relevant PHY registers to private copy */ +static void at803x_context_save(struct phy_device *phydev, + struct at803x_context *context) +{ + context->bmcr = phy_read(phydev, MII_BMCR); + context->advertise = phy_read(phydev, MII_ADVERTISE); + context->control1000 = phy_read(phydev, MII_CTRL1000); + context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); + context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); + context->led_control = phy_read(phydev, AT803X_LED_CONTROL); +} + +/* restore relevant PHY registers from private copy */ +static void at803x_context_restore(struct phy_device *phydev, + const struct at803x_context *context) +{ + phy_write(phydev, MII_BMCR, context->bmcr); + phy_write(phydev, MII_ADVERTISE, context->advertise); + phy_write(phydev, MII_CTRL1000, context->control1000); + phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); + phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); + phy_write(phydev, AT803X_LED_CONTROL, context->led_control); +} + +static int at803x_suspend(struct phy_device *phydev) +{ + int value; + int wol_enabled; + + value = phy_read(phydev, AT803X_INTR_ENABLE); + wol_enabled = value & AT803X_INTR_ENABLE_WOL; + + if (wol_enabled) + value = BMCR_ISOLATE; + else + value = BMCR_PDOWN; + + phy_modify(phydev, MII_BMCR, 0, value); + + return 0; +} + +static int at803x_resume(struct phy_device *phydev) +{ + return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); +} + +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, 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_bool(node, "qca,disable-hibernation-mode")) + priv->flags |= AT803X_DISABLE_HIBERNATION_MODE; + + 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) { + case 25000000: + sel = AT803X_CLK_OUT_25MHZ_XTAL; + break; + case 50000000: + sel = AT803X_CLK_OUT_50MHZ_PLL; + break; + case 62500000: + sel = AT803X_CLK_OUT_62_5MHZ_PLL; + break; + case 125000000: + sel = AT803X_CLK_OUT_125MHZ_PLL; + break; + default: + phydev_err(phydev, "invalid qca,clk-out-frequency\n"); + return -EINVAL; + } + + priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); + priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; + } + + ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); + if (!ret) { + priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; + switch (strength) { + case AR803X_STRENGTH_FULL: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; + break; + case AR803X_STRENGTH_HALF: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; + break; + case AR803X_STRENGTH_QUARTER: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; + break; + default: + phydev_err(phydev, "invalid qca,clk-out-strength\n"); + return -EINVAL; + } + } + + return 0; +} + +static int at803x_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct at803x_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + ret = at803x_parse_dt(phydev); + if (ret) + return ret; + + return 0; +} + +static int at803x_get_features(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int err; + + err = genphy_read_abilities(phydev); + if (err) + return err; + + if (phydev->drv->phy_id != ATH8031_PHY_ID) + return 0; + + /* AR8031/AR8033 have different status registers + * for copper and fiber operation. However, the + * extended status register is the same for both + * operation modes. + * + * As a result of that, ESTATUS_1000_XFULL is set + * to 1 even when operating in copper TP mode. + * + * Remove this mode from the supported link modes + * when not operating in 1000BaseX mode. + */ + if (!priv->is_1000basex) + linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->supported); + + return 0; +} + +static int at803x_smarteee_config(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + u16 mask = 0, val = 0; + int ret; + + 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; + + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, + mask, val); + if (ret) + return ret; + + 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; + + 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) +{ + struct at803x_priv *priv = phydev->priv; + + /* The default after hardware reset is PLL OFF. After a soft reset, the + * values are retained. + */ + if (priv->flags & AT803X_KEEP_PLL_ENABLED) + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, + 0, AT803X_DEBUG_PLL_ON); + else + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, + AT803X_DEBUG_PLL_ON, 0); +} + +static int at803x_hibernation_mode_config(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + + /* The default after hardware reset is hibernation mode enabled. After + * software reset, the value is retained. + */ + if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE)) + return 0; + + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, + AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); +} + +static int at803x_config_init(struct phy_device *phydev) +{ + int ret; + + /* The RX and TX delay default is: + * after HW reset: RX delay enabled and TX delay disabled + * after SW reset: RX delay enabled, while TX delay retains the + * value before reset. + */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + ret = at803x_enable_rx_delay(phydev); + else + ret = at803x_disable_rx_delay(phydev); + if (ret < 0) + return ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + ret = at803x_enable_tx_delay(phydev); + else + ret = at803x_disable_tx_delay(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; + + ret = at803x_hibernation_mode_config(phydev); + if (ret < 0) + return ret; + + /* 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 void at803x_link_change_notify(struct phy_device *phydev) +{ + /* + * Conduct a hardware reset for AT8030 every time a link loss is + * signalled. This is necessary to circumvent a hardware bug that + * occurs when the cable is unplugged while TX packets are pending + * in the FIFO. In such cases, the FIFO enters an error mode it + * cannot recover from by software. + */ + if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { + struct at803x_context context; + + at803x_context_save(phydev, &context); + + phy_device_reset(phydev, 1); + usleep_range(1000, 2000); + phy_device_reset(phydev, 0); + usleep_range(1000, 2000); + + at803x_context_restore(phydev, &context); + + phydev_dbg(phydev, "%s(): phy was reset\n", __func__); + } +} + +static int at803x_config_aneg(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int ret; + + ret = at803x_prepare_config_aneg(phydev); + if (ret) + return ret; + + if (priv->is_1000basex) + return genphy_c37_config_aneg(phydev); + + return genphy_config_aneg(phydev); +} + +static int at803x_cable_test_result_trans(u16 status) +{ + switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { + case AT803X_CDT_STATUS_STAT_NORMAL: + return ETHTOOL_A_CABLE_RESULT_CODE_OK; + case AT803X_CDT_STATUS_STAT_SHORT: + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; + case AT803X_CDT_STATUS_STAT_OPEN: + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; + case AT803X_CDT_STATUS_STAT_FAIL: + default: + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; + } +} + +static bool at803x_cdt_test_failed(u16 status) +{ + return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) == + AT803X_CDT_STATUS_STAT_FAIL; +} + +static bool at803x_cdt_fault_length_valid(u16 status) +{ + switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { + case AT803X_CDT_STATUS_STAT_OPEN: + case AT803X_CDT_STATUS_STAT_SHORT: + return true; + } + return false; +} + +static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) +{ + static const int ethtool_pair[] = { + ETHTOOL_A_CABLE_PAIR_A, + ETHTOOL_A_CABLE_PAIR_B, + ETHTOOL_A_CABLE_PAIR_C, + ETHTOOL_A_CABLE_PAIR_D, + }; + int ret, val; + + val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | + AT803X_CDT_ENABLE_TEST; + ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; + + ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); + if (ret) + return ret; + + val = phy_read(phydev, AT803X_CDT_STATUS); + if (val < 0) + return val; + + if (at803x_cdt_test_failed(val)) + return 0; + + ethnl_cable_test_result(phydev, ethtool_pair[pair], + at803x_cable_test_result_trans(val)); + + if (at803x_cdt_fault_length_valid(val)) { + val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); + ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], + at803x_cdt_fault_length(val)); + } + + return 1; +} + +static int at803x_cable_test_get_status(struct phy_device *phydev, + bool *finished, unsigned long pair_mask) +{ + int retries = 20; + int pair, ret; + + *finished = false; + + /* According to the datasheet the CDT can be performed when + * there is no link partner or when the link partner is + * auto-negotiating. Starting the test will restart the AN + * automatically. It seems that doing this repeatedly we will + * get a slot where our link partner won't disturb our + * measurement. + */ + while (pair_mask && retries--) { + for_each_set_bit(pair, &pair_mask, 4) { + ret = at803x_cable_test_one_pair(phydev, pair); + if (ret < 0) + return ret; + if (ret) + clear_bit(pair, &pair_mask); + } + if (pair_mask) + msleep(250); + } + + *finished = true; + + return 0; +} + +static void at803x_cable_test_autoneg(struct phy_device *phydev) +{ + /* Enable auto-negotiation, but advertise no capabilities, no link + * will be established. A restart of the auto-negotiation is not + * required, because the cable test will automatically break the link. + */ + phy_write(phydev, MII_BMCR, BMCR_ANENABLE); + phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); +} + +static int at803x_cable_test_start(struct phy_device *phydev) +{ + at803x_cable_test_autoneg(phydev); + /* we do all the (time consuming) work later */ + return 0; +} + +static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct phy_device *phydev = rdev_get_drvdata(rdev); + + if (selector) + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, + 0, AT803X_DEBUG_RGMII_1V8); + else + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, + AT803X_DEBUG_RGMII_1V8, 0); +} + +static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + struct phy_device *phydev = rdev_get_drvdata(rdev); + int val; + + val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); + if (val < 0) + return val; + + return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; +} + +static const struct regulator_ops vddio_regulator_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, + .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, +}; + +static const unsigned int vddio_voltage_table[] = { + 1500000, + 1800000, +}; + +static const struct regulator_desc vddio_desc = { + .name = "vddio", + .of_match = of_match_ptr("vddio-regulator"), + .n_voltages = ARRAY_SIZE(vddio_voltage_table), + .volt_table = vddio_voltage_table, + .ops = &vddio_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static const struct regulator_ops vddh_regulator_ops = { +}; + +static const struct regulator_desc vddh_desc = { + .name = "vddh", + .of_match = of_match_ptr("vddh-regulator"), + .n_voltages = 1, + .fixed_uV = 2500000, + .ops = &vddh_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int at8031_register_regulators(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + struct regulator_config config = { }; + + config.dev = dev; + config.driver_data = phydev; + + priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); + if (IS_ERR(priv->vddio_rdev)) { + phydev_err(phydev, "failed to register VDDIO regulator\n"); + return PTR_ERR(priv->vddio_rdev); + } + + priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); + if (IS_ERR(priv->vddh_rdev)) { + phydev_err(phydev, "failed to register VDDH regulator\n"); + return PTR_ERR(priv->vddh_rdev); + } + + return 0; +} + +static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + DECLARE_PHY_INTERFACE_MASK(interfaces); + phy_interface_t iface; + + linkmode_zero(phy_support); + phylink_set(phy_support, 1000baseX_Full); + phylink_set(phy_support, 1000baseT_Full); + phylink_set(phy_support, Autoneg); + phylink_set(phy_support, Pause); + phylink_set(phy_support, Asym_Pause); + + linkmode_zero(sfp_support); + sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); + /* Some modules support 10G modes as well as others we support. + * Mask out non-supported modes so the correct interface is picked. + */ + linkmode_and(sfp_support, phy_support, sfp_support); + + if (linkmode_empty(sfp_support)) { + dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); + return -EINVAL; + } + + iface = sfp_select_interface(phydev->sfp_bus, sfp_support); + + /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes + * interface for use with SFP modules. + * However, some copper modules detected as having a preferred SGMII + * interface do default to and function in 1000Base-X mode, so just + * print a warning and allow such modules, as they may have some chance + * of working. + */ + if (iface == PHY_INTERFACE_MODE_SGMII) + dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); + else if (iface != PHY_INTERFACE_MODE_1000BASEX) + return -EINVAL; + + return 0; +} + +static const struct sfp_upstream_ops at8031_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, + .module_insert = at8031_sfp_insert, +}; + +static int at8031_parse_dt(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct at803x_priv *priv = phydev->priv; + int ret; + + if (of_property_read_bool(node, "qca,keep-pll-enabled")) + priv->flags |= AT803X_KEEP_PLL_ENABLED; + + ret = at8031_register_regulators(phydev); + if (ret < 0) + return ret; + + ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, + "vddio"); + if (ret) { + phydev_err(phydev, "failed to get VDDIO regulator\n"); + return ret; + } + + /* Only AR8031/8033 support 1000Base-X for SFP modules */ + return phy_sfp_probe(phydev, &at8031_sfp_ops); +} + +static int at8031_probe(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int mode_cfg; + int ccr; + int ret; + + ret = at803x_probe(phydev); + if (ret) + return ret; + + /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping + * options. + */ + ret = at8031_parse_dt(phydev); + if (ret) + return ret; + + ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); + if (ccr < 0) + return ccr; + mode_cfg = ccr & AT803X_MODE_CFG_MASK; + + switch (mode_cfg) { + case AT803X_MODE_CFG_BX1000_RGMII_50OHM: + case AT803X_MODE_CFG_BX1000_RGMII_75OHM: + priv->is_1000basex = true; + fallthrough; + case AT803X_MODE_CFG_FX100_RGMII_50OHM: + case AT803X_MODE_CFG_FX100_RGMII_75OHM: + priv->is_fiber = true; + break; + } + + /* Disable WoL in 1588 register which is enabled + * by default + */ + return phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + AT803X_WOL_EN, 0); +} + +static int at8031_config_init(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int ret; + + /* Some bootloaders leave the fiber page selected. + * Switch to the appropriate page (fiber or copper), as otherwise we + * read the PHY capabilities from the wrong page. + */ + phy_lock_mdio_bus(phydev); + ret = at803x_write_page(phydev, + priv->is_fiber ? AT803X_PAGE_FIBER : + AT803X_PAGE_COPPER); + phy_unlock_mdio_bus(phydev); + if (ret) + return ret; + + ret = at8031_pll_config(phydev); + if (ret < 0) + return ret; + + return at803x_config_init(phydev); +} + +static int at8031_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int ret; + + /* First setup MAC address and enable WOL interrupt */ + ret = at803x_set_wol(phydev, wol); + if (ret) + return ret; + + if (wol->wolopts & WAKE_MAGIC) + /* Enable WOL function for 1588 */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + 0, AT803X_WOL_EN); + else + /* Disable WoL function for 1588 */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + AT803X_WOL_EN, 0); + + return ret; +} + +static int at8031_config_intr(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int err, value = 0; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED && + priv->is_fiber) { + /* Clear any pending interrupts */ + err = at803x_ack_interrupt(phydev); + if (err) + return err; + + value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; + value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; + + err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); + if (err) + return err; + } + + return at803x_config_intr(phydev); +} + +/* AR8031 and AR8033 share the same read status logic */ +static int at8031_read_status(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + bool changed; + + if (priv->is_1000basex) + return genphy_c37_read_status(phydev, &changed); + + return at803x_read_status(phydev); +} + +/* AR8031 and AR8035 share the same cable test get status reg */ +static int at8031_cable_test_get_status(struct phy_device *phydev, + bool *finished) +{ + return at803x_cable_test_get_status(phydev, finished, 0xf); +} + +/* AR8031 and AR8035 share the same cable test start logic */ +static int at8031_cable_test_start(struct phy_device *phydev) +{ + at803x_cable_test_autoneg(phydev); + phy_write(phydev, MII_CTRL1000, 0); + /* we do all the (time consuming) work later */ + return 0; +} + +/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ +static int at8032_cable_test_get_status(struct phy_device *phydev, + bool *finished) +{ + return at803x_cable_test_get_status(phydev, finished, 0x3); +} + +static int at8035_parse_dt(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + + /* Mask is set by the generic at803x_parse_dt + * if property is set. Assume property is set + * with the mask not zero. + */ + if (priv->clk_25m_mask) { + /* Fixup for the AR8030/AR8035. This chip has another mask and + * doesn't support the DSP reference. Eg. the lowest bit of the + * mask. The upper two bits select the same frequencies. Mask + * the lowest bit here. + * + * Warning: + * There was no datasheet for the AR8030 available so this is + * just a guess. But the AR8035 is listed as pin compatible + * to the AR8030 so there might be a good chance it works on + * the AR8030 too. + */ + priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; + priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; + } + + return 0; +} + +/* AR8030 and AR8035 shared the same special mask for clk_25m */ +static int at8035_probe(struct phy_device *phydev) +{ + int ret; + + ret = at803x_probe(phydev); + if (ret) + return ret; + + return at8035_parse_dt(phydev); +} + +static struct phy_driver at803x_driver[] = { +{ + /* Qualcomm Atheros AR8035 */ + PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), + .name = "Qualcomm Atheros AR8035", + .flags = PHY_POLL_CABLE_TEST, + .probe = at8035_probe, + .config_aneg = at803x_config_aneg, + .config_init = at803x_config_init, + .soft_reset = genphy_soft_reset, + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, + /* PHY_GBIT_FEATURES */ + .read_status = at803x_read_status, + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, + .cable_test_start = at8031_cable_test_start, + .cable_test_get_status = at8031_cable_test_get_status, +}, { + /* Qualcomm Atheros AR8030 */ + .phy_id = ATH8030_PHY_ID, + .name = "Qualcomm Atheros AR8030", + .phy_id_mask = AT8030_PHY_ID_MASK, + .probe = at8035_probe, + .config_init = at803x_config_init, + .link_change_notify = at803x_link_change_notify, + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, + /* PHY_BASIC_FEATURES */ + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, +}, { + /* Qualcomm Atheros AR8031/AR8033 */ + PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), + .name = "Qualcomm Atheros AR8031/AR8033", + .flags = PHY_POLL_CABLE_TEST, + .probe = at8031_probe, + .config_init = at8031_config_init, + .config_aneg = at803x_config_aneg, + .soft_reset = genphy_soft_reset, + .set_wol = at8031_set_wol, + .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, + .read_page = at803x_read_page, + .write_page = at803x_write_page, + .get_features = at803x_get_features, + .read_status = at8031_read_status, + .config_intr = at8031_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, + .cable_test_start = at8031_cable_test_start, + .cable_test_get_status = at8031_cable_test_get_status, +}, { + /* Qualcomm Atheros AR8032 */ + PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), + .name = "Qualcomm Atheros AR8032", + .probe = at803x_probe, + .flags = PHY_POLL_CABLE_TEST, + .config_init = at803x_config_init, + .link_change_notify = at803x_link_change_notify, + .suspend = at803x_suspend, + .resume = at803x_resume, + /* PHY_BASIC_FEATURES */ + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, + .cable_test_get_status = at8032_cable_test_get_status, +}, { + /* ATHEROS AR9331 */ + PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), + .name = "Qualcomm Atheros AR9331 built-in PHY", + .probe = at803x_probe, + .suspend = at803x_suspend, + .resume = at803x_resume, + .flags = PHY_POLL_CABLE_TEST, + /* PHY_BASIC_FEATURES */ + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, + .cable_test_get_status = at8032_cable_test_get_status, + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +}, { + /* Qualcomm Atheros QCA9561 */ + PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), + .name = "Qualcomm Atheros QCA9561 built-in PHY", + .probe = at803x_probe, + .suspend = at803x_suspend, + .resume = at803x_resume, + .flags = PHY_POLL_CABLE_TEST, + /* PHY_BASIC_FEATURES */ + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, + .cable_test_get_status = at8032_cable_test_get_status, + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +}, }; + +module_phy_driver(at803x_driver); + +static struct mdio_device_id __maybe_unused atheros_tbl[] = { + { ATH8030_PHY_ID, AT8030_PHY_ID_MASK }, + { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, atheros_tbl); diff --git a/drivers/net/phy/qcom/qca807x.c b/drivers/net/phy/qcom/qca807x.c new file mode 100644 index 000000000000..780c28e2e4aa --- /dev/null +++ b/drivers/net/phy/qcom/qca807x.c @@ -0,0 +1,849 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Sartura Ltd. + * + * Author: Robert Marko <robert.marko@sartura.hr> + * Christian Marangi <ansuelsmth@gmail.com> + * + * Qualcomm QCA8072 and QCA8075 PHY driver + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy.h> +#include <linux/bitfield.h> +#include <linux/gpio/driver.h> +#include <linux/sfp.h> + +#include "qcom.h" + +#define QCA807X_CHIP_CONFIGURATION 0x1f +#define QCA807X_BT_BX_REG_SEL BIT(15) +#define QCA807X_BT_BX_REG_SEL_FIBER 0 +#define QCA807X_BT_BX_REG_SEL_COPPER 1 +#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0) +#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4 +#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3 +#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0 + +#define QCA807X_MEDIA_SELECT_STATUS 0x1a +#define QCA807X_MEDIA_DETECTED_COPPER BIT(5) +#define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4) +#define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3) + +#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e +#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0) + +#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a +#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0) +/* List of tweaks enabled by this bit: + * - With both FULL amplitude and FULL bias current: bias current + * is set to half. + * - With only DSP amplitude: bias current is set to half and + * is set to 1/4 with cable < 10m. + * - With DSP bias current (included both DSP amplitude and + * DSP bias current): bias current is half the detected current + * with cable < 10m. + */ +#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2) +#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1) +#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0) + +#define QCA807X_MMD7_LED_100N_1 0x8074 +#define QCA807X_MMD7_LED_100N_2 0x8075 +#define QCA807X_MMD7_LED_1000N_1 0x8076 +#define QCA807X_MMD7_LED_1000N_2 0x8077 + +#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2)) +#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2)) + +/* LED hw control pattern for fiber port */ +#define QCA807X_LED_FIBER_PATTERN_MASK GENMASK(11, 1) +#define QCA807X_LED_FIBER_TXACT_BLK_EN BIT(10) +#define QCA807X_LED_FIBER_RXACT_BLK_EN BIT(9) +#define QCA807X_LED_FIBER_FDX_ON_EN BIT(6) +#define QCA807X_LED_FIBER_HDX_ON_EN BIT(5) +#define QCA807X_LED_FIBER_1000BX_ON_EN BIT(2) +#define QCA807X_LED_FIBER_100FX_ON_EN BIT(1) + +/* Some device repurpose the LED as GPIO out */ +#define QCA807X_GPIO_FORCE_EN QCA808X_LED_FORCE_EN +#define QCA807X_GPIO_FORCE_MODE_MASK QCA808X_LED_FORCE_MODE_MASK + +#define QCA807X_FUNCTION_CONTROL 0x10 +#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5) +#define QCA807X_FC_MDI_CROSSOVER_AUTO 3 +#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1 +#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0 + +/* PQSGMII Analog PHY specific */ +#define PQSGMII_CTRL_REG 0x0 +#define PQSGMII_ANALOG_SW_RESET BIT(6) +#define PQSGMII_DRIVE_CONTROL_1 0xb +#define PQSGMII_TX_DRIVER_MASK GENMASK(7, 4) +#define PQSGMII_TX_DRIVER_140MV 0x0 +#define PQSGMII_TX_DRIVER_160MV 0x1 +#define PQSGMII_TX_DRIVER_180MV 0x2 +#define PQSGMII_TX_DRIVER_200MV 0x3 +#define PQSGMII_TX_DRIVER_220MV 0x4 +#define PQSGMII_TX_DRIVER_240MV 0x5 +#define PQSGMII_TX_DRIVER_260MV 0x6 +#define PQSGMII_TX_DRIVER_280MV 0x7 +#define PQSGMII_TX_DRIVER_300MV 0x8 +#define PQSGMII_TX_DRIVER_320MV 0x9 +#define PQSGMII_TX_DRIVER_400MV 0xa +#define PQSGMII_TX_DRIVER_500MV 0xb +#define PQSGMII_TX_DRIVER_600MV 0xc +#define PQSGMII_MODE_CTRL 0x6d +#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0) +#define PQSGMII_MMD3_SERDES_CONTROL 0x805a + +#define PHY_ID_QCA8072 0x004dd0b2 +#define PHY_ID_QCA8075 0x004dd0b1 + +#define QCA807X_COMBO_ADDR_OFFSET 4 +#define QCA807X_PQSGMII_ADDR_OFFSET 5 +#define SERDES_RESET_SLEEP 100 + +enum qca807x_global_phy { + QCA807X_COMBO_ADDR = 4, + QCA807X_PQSGMII_ADDR = 5, +}; + +struct qca807x_shared_priv { + unsigned int package_mode; + u32 tx_drive_strength; +}; + +struct qca807x_gpio_priv { + struct phy_device *phy; +}; + +struct qca807x_priv { + bool dac_full_amplitude; + bool dac_full_bias_current; + bool dac_disable_bias_current_tweak; +}; + +static int qca807x_cable_test_start(struct phy_device *phydev) +{ + /* we do all the (time consuming) work later */ + return 0; +} + +static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, + u16 *offload_trigger) +{ + /* Parsing specific to netdev trigger */ + switch (phydev->port) { + case PORT_TP: + if (test_bit(TRIGGER_NETDEV_TX, &rules)) + *offload_trigger |= QCA808X_LED_TX_BLINK; + if (test_bit(TRIGGER_NETDEV_RX, &rules)) + *offload_trigger |= QCA808X_LED_RX_BLINK; + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) + *offload_trigger |= QCA808X_LED_SPEED10_ON; + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) + *offload_trigger |= QCA808X_LED_SPEED100_ON; + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) + *offload_trigger |= QCA808X_LED_SPEED1000_ON; + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) + *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; + if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) + *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; + break; + case PORT_FIBRE: + if (test_bit(TRIGGER_NETDEV_TX, &rules)) + *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN; + if (test_bit(TRIGGER_NETDEV_RX, &rules)) + *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN; + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) + *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN; + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) + *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN; + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) + *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN; + if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) + *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN; + break; + default: + return -EOPNOTSUPP; + } + + if (rules && !*offload_trigger) + return -EOPNOTSUPP; + + return 0; +} + +static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index) +{ + u16 reg; + + if (index > 1) + return -EINVAL; + + reg = QCA807X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_hw_control_enable(phydev, reg); +} + +static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 offload_trigger = 0; + + if (index > 1) + return -EINVAL; + + return qca807x_led_parse_netdev(phydev, rules, &offload_trigger); +} + +static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 reg, mask, offload_trigger = 0; + int ret; + + if (index > 1) + return -EINVAL; + + ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger); + if (ret) + return ret; + + ret = qca807x_led_hw_control_enable(phydev, index); + if (ret) + return ret; + + switch (phydev->port) { + case PORT_TP: + reg = QCA807X_MMD7_LED_CTRL(index); + mask = QCA808X_LED_PATTERN_MASK; + break; + case PORT_FIBRE: + /* HW control pattern bits are in LED FORCE reg */ + reg = QCA807X_MMD7_LED_FORCE_CTRL(index); + mask = QCA807X_LED_FIBER_PATTERN_MASK; + break; + default: + return -EINVAL; + } + + return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask, + offload_trigger); +} + +static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index) +{ + u16 reg; + + if (index > 1) + return false; + + reg = QCA807X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_hw_control_status(phydev, reg); +} + +static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) +{ + u16 reg; + int val; + + if (index > 1) + return -EINVAL; + + /* Check if we have hw control enabled */ + if (qca807x_led_hw_control_status(phydev, index)) + return -EINVAL; + + /* Parsing specific to netdev trigger */ + switch (phydev->port) { + case PORT_TP: + reg = QCA807X_MMD7_LED_CTRL(index); + val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); + if (val & QCA808X_LED_TX_BLINK) + set_bit(TRIGGER_NETDEV_TX, rules); + if (val & QCA808X_LED_RX_BLINK) + set_bit(TRIGGER_NETDEV_RX, rules); + if (val & QCA808X_LED_SPEED10_ON) + set_bit(TRIGGER_NETDEV_LINK_10, rules); + if (val & QCA808X_LED_SPEED100_ON) + set_bit(TRIGGER_NETDEV_LINK_100, rules); + if (val & QCA808X_LED_SPEED1000_ON) + set_bit(TRIGGER_NETDEV_LINK_1000, rules); + if (val & QCA808X_LED_HALF_DUPLEX_ON) + set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); + if (val & QCA808X_LED_FULL_DUPLEX_ON) + set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); + break; + case PORT_FIBRE: + /* HW control pattern bits are in LED FORCE reg */ + reg = QCA807X_MMD7_LED_FORCE_CTRL(index); + val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); + if (val & QCA807X_LED_FIBER_TXACT_BLK_EN) + set_bit(TRIGGER_NETDEV_TX, rules); + if (val & QCA807X_LED_FIBER_RXACT_BLK_EN) + set_bit(TRIGGER_NETDEV_RX, rules); + if (val & QCA807X_LED_FIBER_100FX_ON_EN) + set_bit(TRIGGER_NETDEV_LINK_100, rules); + if (val & QCA807X_LED_FIBER_1000BX_ON_EN) + set_bit(TRIGGER_NETDEV_LINK_1000, rules); + if (val & QCA807X_LED_FIBER_HDX_ON_EN) + set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); + if (val & QCA807X_LED_FIBER_FDX_ON_EN) + set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index) +{ + u16 reg, mask; + + if (index > 1) + return -EINVAL; + + switch (phydev->port) { + case PORT_TP: + reg = QCA807X_MMD7_LED_CTRL(index); + mask = QCA808X_LED_PATTERN_MASK; + break; + case PORT_FIBRE: + /* HW control pattern bits are in LED FORCE reg */ + reg = QCA807X_MMD7_LED_FORCE_CTRL(index); + mask = QCA807X_LED_FIBER_PATTERN_MASK; + break; + default: + return -EINVAL; + } + + return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask); +} + +static int qca807x_led_brightness_set(struct phy_device *phydev, + u8 index, enum led_brightness value) +{ + u16 reg; + int ret; + + if (index > 1) + return -EINVAL; + + /* If we are setting off the LED reset any hw control rule */ + if (!value) { + ret = qca807x_led_hw_control_reset(phydev, index); + if (ret) + return ret; + } + + reg = QCA807X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_brightness_set(phydev, reg, value); +} + +static int qca807x_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u16 reg; + + if (index > 1) + return -EINVAL; + + reg = QCA807X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off); +} + +#ifdef CONFIG_GPIOLIB +static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct qca807x_gpio_priv *priv = gpiochip_get_data(gc); + u16 reg; + int val; + + reg = QCA807X_MMD7_LED_FORCE_CTRL(offset); + val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg); + + return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val); +} + +static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct qca807x_gpio_priv *priv = gpiochip_get_data(gc); + u16 reg; + int val; + + reg = QCA807X_MMD7_LED_FORCE_CTRL(offset); + + val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg); + val &= ~QCA807X_GPIO_FORCE_MODE_MASK; + val |= QCA807X_GPIO_FORCE_EN; + val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value); + + phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val); +} + +static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value) +{ + qca807x_gpio_set(gc, offset, value); + + return 0; +} + +static int qca807x_gpio(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct qca807x_gpio_priv *priv; + struct gpio_chip *gc; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->phy = phydev; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->label = dev_name(dev); + gc->base = -1; + gc->ngpio = 2; + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->can_sleep = true; + gc->get_direction = qca807x_gpio_get_direction; + gc->direction_output = qca807x_gpio_dir_out; + gc->get = qca807x_gpio_get; + gc->set = qca807x_gpio_set; + + return devm_gpiochip_add_data(dev, gc, priv); +} +#endif + +static int qca807x_read_fiber_status(struct phy_device *phydev) +{ + bool changed; + int ss, err; + + err = genphy_c37_read_status(phydev, &changed); + if (err || !changed) + return err; + + /* Read the QCA807x PHY-Specific Status register fiber page, + * which indicates the speed and duplex that the PHY is actually + * using, irrespective of whether we are in autoneg mode or not. + */ + ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); + if (ss < 0) + return ss; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { + switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) { + case AT803X_SS_SPEED_100: + phydev->speed = SPEED_100; + break; + case AT803X_SS_SPEED_1000: + phydev->speed = SPEED_1000; + break; + } + + if (ss & AT803X_SS_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + } + + return 0; +} + +static int qca807x_read_status(struct phy_device *phydev) +{ + if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { + switch (phydev->port) { + case PORT_FIBRE: + return qca807x_read_fiber_status(phydev); + case PORT_TP: + return at803x_read_status(phydev); + default: + return -EINVAL; + } + } + + return at803x_read_status(phydev); +} + +static int qca807x_phy_package_probe_once(struct phy_device *phydev) +{ + struct phy_package_shared *shared = phydev->shared; + struct qca807x_shared_priv *priv = shared->priv; + unsigned int tx_drive_strength; + const char *package_mode_name; + + /* Default to 600mw if not defined */ + if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt", + &tx_drive_strength)) + tx_drive_strength = 600; + + switch (tx_drive_strength) { + case 140: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV; + break; + case 160: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV; + break; + case 180: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV; + break; + case 200: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV; + break; + case 220: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV; + break; + case 240: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV; + break; + case 260: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV; + break; + case 280: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV; + break; + case 300: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV; + break; + case 320: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV; + break; + case 400: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV; + break; + case 500: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV; + break; + case 600: + priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV; + break; + default: + return -EINVAL; + } + + priv->package_mode = PHY_INTERFACE_MODE_NA; + if (!of_property_read_string(shared->np, "qcom,package-mode", + &package_mode_name)) { + if (!strcasecmp(package_mode_name, + phy_modes(PHY_INTERFACE_MODE_PSGMII))) + priv->package_mode = PHY_INTERFACE_MODE_PSGMII; + else if (!strcasecmp(package_mode_name, + phy_modes(PHY_INTERFACE_MODE_QSGMII))) + priv->package_mode = PHY_INTERFACE_MODE_QSGMII; + else + return -EINVAL; + } + + return 0; +} + +static int qca807x_phy_package_config_init_once(struct phy_device *phydev) +{ + struct phy_package_shared *shared = phydev->shared; + struct qca807x_shared_priv *priv = shared->priv; + int val, ret; + + /* Make sure PHY follow PHY package mode if enforced */ + if (priv->package_mode != PHY_INTERFACE_MODE_NA && + phydev->interface != priv->package_mode) + return -EINVAL; + + phy_lock_mdio_bus(phydev); + + /* Set correct PHY package mode */ + val = __phy_package_read(phydev, QCA807X_COMBO_ADDR, + QCA807X_CHIP_CONFIGURATION); + val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK; + /* package_mode can be QSGMII or PSGMII and we validate + * this in probe_once. + * With package_mode to NA, we default to PSGMII. + */ + switch (priv->package_mode) { + case PHY_INTERFACE_MODE_QSGMII: + val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII; + break; + case PHY_INTERFACE_MODE_PSGMII: + default: + val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER; + } + ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR, + QCA807X_CHIP_CONFIGURATION, val); + if (ret) + goto exit; + + /* After mode change Serdes reset is required */ + val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, + PQSGMII_CTRL_REG); + val &= ~PQSGMII_ANALOG_SW_RESET; + ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, + PQSGMII_CTRL_REG, val); + if (ret) + goto exit; + + msleep(SERDES_RESET_SLEEP); + + val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, + PQSGMII_CTRL_REG); + val |= PQSGMII_ANALOG_SW_RESET; + ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, + PQSGMII_CTRL_REG, val); + if (ret) + goto exit; + + /* Workaround to enable AZ transmitting ability */ + val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR, + MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL); + val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK; + ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR, + MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val); + if (ret) + goto exit; + + /* Set PQSGMII TX AMP strength */ + val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, + PQSGMII_DRIVE_CONTROL_1); + val &= ~PQSGMII_TX_DRIVER_MASK; + val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength); + ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, + PQSGMII_DRIVE_CONTROL_1, val); + if (ret) + goto exit; + + /* Prevent PSGMII going into hibernation via PSGMII self test */ + val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR, + MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL); + val &= ~BIT(1); + ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR, + MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val); + +exit: + phy_unlock_mdio_bus(phydev); + + return ret; +} + +static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; + phy_interface_t iface; + int ret; + DECLARE_PHY_INTERFACE_MASK(interfaces); + + sfp_parse_support(phydev->sfp_bus, id, support, interfaces); + iface = sfp_select_interface(phydev->sfp_bus, support); + + dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface)); + + switch (iface) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_100BASEX: + /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */ + ret = phy_modify(phydev, + QCA807X_CHIP_CONFIGURATION, + QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK, + QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER); + /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */ + ret = phy_set_bits_mmd(phydev, + MDIO_MMD_AN, + QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION, + QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN); + /* Select fiber page */ + ret = phy_clear_bits(phydev, + QCA807X_CHIP_CONFIGURATION, + QCA807X_BT_BX_REG_SEL); + + phydev->port = PORT_FIBRE; + break; + default: + dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n"); + return -EINVAL; + } + + return ret; +} + +static void qca807x_sfp_remove(void *upstream) +{ + struct phy_device *phydev = upstream; + + /* Select copper page */ + phy_set_bits(phydev, + QCA807X_CHIP_CONFIGURATION, + QCA807X_BT_BX_REG_SEL); + + phydev->port = PORT_TP; +} + +static const struct sfp_upstream_ops qca807x_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, + .module_insert = qca807x_sfp_insert, + .module_remove = qca807x_sfp_remove, +}; + +static int qca807x_probe(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct qca807x_shared_priv *shared_priv; + struct device *dev = &phydev->mdio.dev; + struct phy_package_shared *shared; + struct qca807x_priv *priv; + int ret; + + ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv)); + if (ret) + return ret; + + if (phy_package_probe_once(phydev)) { + ret = qca807x_phy_package_probe_once(phydev); + if (ret) + return ret; + } + + shared = phydev->shared; + shared_priv = shared->priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude"); + priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current"); + priv->dac_disable_bias_current_tweak = of_property_read_bool(node, + "qcom,dac-disable-bias-current-tweak"); + + if (IS_ENABLED(CONFIG_GPIOLIB)) { + /* Make sure we don't have mixed leds node and gpio-controller + * to prevent registering leds and having gpio-controller usage + * conflicting with them. + */ + if (of_find_property(node, "leds", NULL) && + of_find_property(node, "gpio-controller", NULL)) { + phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive."); + return -EINVAL; + } + + /* Do not register a GPIO controller unless flagged for it */ + if (of_property_read_bool(node, "gpio-controller")) { + ret = qca807x_gpio(phydev); + if (ret) + return ret; + } + } + + /* Attach SFP bus on combo port*/ + if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) { + ret = phy_sfp_probe(phydev, &qca807x_sfp_ops); + if (ret) + return ret; + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising); + } + + phydev->priv = priv; + + return 0; +} + +static int qca807x_config_init(struct phy_device *phydev) +{ + struct qca807x_priv *priv = phydev->priv; + u16 control_dac; + int ret; + + if (phy_package_init_once(phydev)) { + ret = qca807x_phy_package_config_init_once(phydev); + if (ret) + return ret; + } + + control_dac = phy_read_mmd(phydev, MDIO_MMD_AN, + QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH); + control_dac &= ~QCA807X_CONTROL_DAC_MASK; + if (!priv->dac_full_amplitude) + control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE; + if (!priv->dac_full_amplitude) + control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT; + if (!priv->dac_disable_bias_current_tweak) + control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK; + return phy_write_mmd(phydev, MDIO_MMD_AN, + QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH, + control_dac); +} + +static struct phy_driver qca807x_drivers[] = { + { + PHY_ID_MATCH_EXACT(PHY_ID_QCA8072), + .name = "Qualcomm QCA8072", + .flags = PHY_POLL_CABLE_TEST, + /* PHY_GBIT_FEATURES */ + .probe = qca807x_probe, + .config_init = qca807x_config_init, + .read_status = qca807x_read_status, + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .soft_reset = genphy_soft_reset, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, + .resume = genphy_resume, + .suspend = genphy_suspend, + .cable_test_start = qca807x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_QCA8075), + .name = "Qualcomm QCA8075", + .flags = PHY_POLL_CABLE_TEST, + /* PHY_GBIT_FEATURES */ + .probe = qca807x_probe, + .config_init = qca807x_config_init, + .read_status = qca807x_read_status, + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .soft_reset = genphy_soft_reset, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, + .resume = genphy_resume, + .suspend = genphy_suspend, + .cable_test_start = qca807x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, + .led_brightness_set = qca807x_led_brightness_set, + .led_blink_set = qca807x_led_blink_set, + .led_hw_is_supported = qca807x_led_hw_is_supported, + .led_hw_control_set = qca807x_led_hw_control_set, + .led_hw_control_get = qca807x_led_hw_control_get, + }, +}; +module_phy_driver(qca807x_drivers); + +static struct mdio_device_id __maybe_unused qca807x_tbl[] = { + { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) }, + { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) }, + { } +}; + +MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); +MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver"); +MODULE_DEVICE_TABLE(mdio, qca807x_tbl); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/qcom/qca808x.c b/drivers/net/phy/qcom/qca808x.c new file mode 100644 index 000000000000..2acf852fb4de --- /dev/null +++ b/drivers/net/phy/qcom/qca808x.c @@ -0,0 +1,644 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/phy.h> +#include <linux/module.h> + +#include "qcom.h" + +/* ADC threshold */ +#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 +#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) +#define QCA808X_ADC_THRESHOLD_80MV 0 +#define QCA808X_ADC_THRESHOLD_100MV 0xf0 +#define QCA808X_ADC_THRESHOLD_200MV 0x0f +#define QCA808X_ADC_THRESHOLD_300MV 0xff + +/* CLD control */ +#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 +#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) +#define QCA808X_8023AZ_AFE_EN 0x90 + +/* AZ control */ +#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 +#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 + +#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 +#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 + +#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E +#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 + +#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E +#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 + +#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 +#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 + +#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c +#define QCA808X_TOP_OPTION1_DATA 0x0 + +#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 +#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 +#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 +#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad +#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 +#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 +#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 +#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 +#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 +#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 +#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 +#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 + +/* master/slave seed config */ +#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 +#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) +#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) +#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 + +/* Hibernation yields lower power consumpiton in contrast with normal operation mode. + * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. + */ +#define QCA808X_DBG_AN_TEST 0xb +#define QCA808X_HIBERNATION_EN BIT(15) + +#define QCA808X_MMD7_LED2_CTRL 0x8074 +#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 +#define QCA808X_MMD7_LED1_CTRL 0x8076 +#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 +#define QCA808X_MMD7_LED0_CTRL 0x8078 +#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) + +#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 +#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) + +#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a +/* QSDK sets by default 0x46 to this reg that sets BIT 6 for + * LED to active high. It's not clear what BIT 3 and BIT 4 does. + */ +#define QCA808X_LED_ACTIVE_HIGH BIT(6) + +/* QCA808X 1G chip type */ +#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d +#define QCA808X_PHY_CHIP_TYPE_1G BIT(0) + +#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072 +#define QCA8081_PHY_FIFO_RSTN BIT(11) + +#define QCA8081_PHY_ID 0x004dd101 + +MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver"); +MODULE_AUTHOR("Matus Ujhelyi"); +MODULE_LICENSE("GPL"); + +struct qca808x_priv { + int led_polarity_mode; +}; + +static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) +{ + int ret; + + /* Enable fast retrain */ + ret = genphy_c45_fast_retrain(phydev, true); + if (ret) + return ret; + + phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, + QCA808X_TOP_OPTION1_DATA); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, + QCA808X_MSE_THRESHOLD_20DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, + QCA808X_MSE_THRESHOLD_17DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, + QCA808X_MSE_THRESHOLD_27DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, + QCA808X_MSE_THRESHOLD_28DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, + QCA808X_MMD3_DEBUG_1_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, + QCA808X_MMD3_DEBUG_4_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, + QCA808X_MMD3_DEBUG_5_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, + QCA808X_MMD3_DEBUG_3_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, + QCA808X_MMD3_DEBUG_6_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, + QCA808X_MMD3_DEBUG_2_VALUE); + + return 0; +} + +static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) +{ + u16 seed_value; + + if (!enable) + return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, + QCA808X_MASTER_SLAVE_SEED_ENABLE, 0); + + seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE); + return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, + QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, + FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | + QCA808X_MASTER_SLAVE_SEED_ENABLE); +} + +static bool qca808x_is_prefer_master(struct phy_device *phydev) +{ + return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) || + (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); +} + +static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev) +{ + return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); +} + +static int qca808x_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct qca808x_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Init LED polarity mode to -1 */ + priv->led_polarity_mode = -1; + + phydev->priv = priv; + + return 0; +} + +static int qca808x_config_init(struct phy_device *phydev) +{ + struct qca808x_priv *priv = phydev->priv; + int ret; + + /* Default to LED Active High if active-low not in DT */ + if (priv->led_polarity_mode == -1) { + ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, + QCA808X_MMD7_LED_POLARITY_CTRL, + QCA808X_LED_ACTIVE_HIGH); + if (ret) + return ret; + } + + /* Active adc&vga on 802.3az for the link 1000M and 100M */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, + QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); + if (ret) + return ret; + + /* Adjust the threshold on 802.3az for the link 1000M */ + ret = phy_write_mmd(phydev, MDIO_MMD_PCS, + QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, + QCA808X_MMD3_AZ_TRAINING_VAL); + if (ret) + return ret; + + if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { + /* Config the fast retrain for the link 2500M */ + ret = qca808x_phy_fast_retrain_config(phydev); + if (ret) + return ret; + + ret = genphy_read_master_slave(phydev); + if (ret < 0) + return ret; + + if (!qca808x_is_prefer_master(phydev)) { + /* Enable seed and configure lower ramdom seed to make phy + * linked as slave mode. + */ + ret = qca808x_phy_ms_seed_enable(phydev, true); + if (ret) + return ret; + } + } + + /* Configure adc threshold as 100mv for the link 10M */ + return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, + QCA808X_ADC_THRESHOLD_MASK, + QCA808X_ADC_THRESHOLD_100MV); +} + +static int qca808x_read_status(struct phy_device *phydev) +{ + struct at803x_ss_mask ss_mask = { 0 }; + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); + if (ret < 0) + return ret; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, + ret & MDIO_AN_10GBT_STAT_LP2_5G); + + ret = genphy_read_status(phydev); + if (ret) + return ret; + + /* qca8081 takes the different bits for speed value from at803x */ + ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; + ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); + ret = at803x_read_specific_status(phydev, ss_mask); + if (ret < 0) + return ret; + + if (phydev->link) { + if (phydev->speed == SPEED_2500) + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + else + phydev->interface = PHY_INTERFACE_MODE_SGMII; + } else { + /* generate seed as a lower random value to make PHY linked as SLAVE easily, + * except for master/slave configuration fault detected or the master mode + * preferred. + * + * the reason for not putting this code into the function link_change_notify is + * the corner case where the link partner is also the qca8081 PHY and the seed + * value is configured as the same value, the link can't be up and no link change + * occurs. + */ + if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { + if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || + qca808x_is_prefer_master(phydev)) { + qca808x_phy_ms_seed_enable(phydev, false); + } else { + qca808x_phy_ms_seed_enable(phydev, true); + } + } + } + + return 0; +} + +static int qca808x_soft_reset(struct phy_device *phydev) +{ + int ret; + + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + + if (qca808x_has_fast_retrain_or_slave_seed(phydev)) + ret = qca808x_phy_ms_seed_enable(phydev, true); + + return ret; +} + +static int qca808x_cable_test_start(struct phy_device *phydev) +{ + int ret; + + /* perform CDT with the following configs: + * 1. disable hibernation. + * 2. force PHY working in MDI mode. + * 3. for PHY working in 1000BaseT. + * 4. configure the threshold. + */ + + ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); + if (ret < 0) + return ret; + + ret = at803x_config_mdix(phydev, ETH_TP_MDI); + if (ret < 0) + return ret; + + /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_1000; + ret = genphy_c45_pma_setup_forced(phydev); + if (ret < 0) + return ret; + + ret = genphy_setup_forced(phydev); + if (ret < 0) + return ret; + + /* configure the thresholds for open, short, pair ok test */ + phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); + phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); + phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); + phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); + phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); + phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); + + return 0; +} + +static int qca808x_get_features(struct phy_device *phydev) +{ + int ret; + + ret = genphy_c45_pma_read_abilities(phydev); + if (ret) + return ret; + + /* The autoneg ability is not existed in bit3 of MMD7.1, + * but it is supported by qca808x PHY, so we add it here + * manually. + */ + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); + + /* As for the qca8081 1G version chip, the 2500baseT ability is also + * existed in the bit0 of MMD1.21, we need to remove it manually if + * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. + */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); + if (ret < 0) + return ret; + + if (QCA808X_PHY_CHIP_TYPE_1G & ret) + linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); + + return 0; +} + +static int qca808x_config_aneg(struct phy_device *phydev) +{ + int phy_ctrl = 0; + int ret; + + ret = at803x_prepare_config_aneg(phydev); + if (ret) + return ret; + + /* The reg MII_BMCR also needs to be configured for force mode, the + * genphy_config_aneg is also needed. + */ + if (phydev->autoneg == AUTONEG_DISABLE) + genphy_c45_pma_setup_forced(phydev); + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) + phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; + + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); + if (ret < 0) + return ret; + + return __genphy_config_aneg(phydev, ret); +} + +static void qca808x_link_change_notify(struct phy_device *phydev) +{ + /* Assert interface sgmii fifo on link down, deassert it on link up, + * the interface device address is always phy address added by 1. + */ + mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, + MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, + QCA8081_PHY_FIFO_RSTN, + phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); +} + +static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, + u16 *offload_trigger) +{ + /* Parsing specific to netdev trigger */ + if (test_bit(TRIGGER_NETDEV_TX, &rules)) + *offload_trigger |= QCA808X_LED_TX_BLINK; + if (test_bit(TRIGGER_NETDEV_RX, &rules)) + *offload_trigger |= QCA808X_LED_RX_BLINK; + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) + *offload_trigger |= QCA808X_LED_SPEED10_ON; + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) + *offload_trigger |= QCA808X_LED_SPEED100_ON; + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) + *offload_trigger |= QCA808X_LED_SPEED1000_ON; + if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) + *offload_trigger |= QCA808X_LED_SPEED2500_ON; + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) + *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; + if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) + *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; + + if (rules && !*offload_trigger) + return -EOPNOTSUPP; + + /* Enable BLINK_CHECK_BYPASS by default to make the LED + * blink even with duplex or speed mode not enabled. + */ + *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; + + return 0; +} + +static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) +{ + u16 reg; + + if (index > 2) + return -EINVAL; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_hw_control_enable(phydev, reg); +} + +static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 offload_trigger = 0; + + if (index > 2) + return -EINVAL; + + return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +} + +static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 reg, offload_trigger = 0; + int ret; + + if (index > 2) + return -EINVAL; + + reg = QCA808X_MMD7_LED_CTRL(index); + + ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); + if (ret) + return ret; + + ret = qca808x_led_hw_control_enable(phydev, index); + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, + QCA808X_LED_PATTERN_MASK, + offload_trigger); +} + +static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) +{ + u16 reg; + + if (index > 2) + return false; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_hw_control_status(phydev, reg); +} + +static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) +{ + u16 reg; + int val; + + if (index > 2) + return -EINVAL; + + /* Check if we have hw control enabled */ + if (qca808x_led_hw_control_status(phydev, index)) + return -EINVAL; + + reg = QCA808X_MMD7_LED_CTRL(index); + + val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); + if (val & QCA808X_LED_TX_BLINK) + set_bit(TRIGGER_NETDEV_TX, rules); + if (val & QCA808X_LED_RX_BLINK) + set_bit(TRIGGER_NETDEV_RX, rules); + if (val & QCA808X_LED_SPEED10_ON) + set_bit(TRIGGER_NETDEV_LINK_10, rules); + if (val & QCA808X_LED_SPEED100_ON) + set_bit(TRIGGER_NETDEV_LINK_100, rules); + if (val & QCA808X_LED_SPEED1000_ON) + set_bit(TRIGGER_NETDEV_LINK_1000, rules); + if (val & QCA808X_LED_SPEED2500_ON) + set_bit(TRIGGER_NETDEV_LINK_2500, rules); + if (val & QCA808X_LED_HALF_DUPLEX_ON) + set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); + if (val & QCA808X_LED_FULL_DUPLEX_ON) + set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); + + return 0; +} + +static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) +{ + u16 reg; + + if (index > 2) + return -EINVAL; + + reg = QCA808X_MMD7_LED_CTRL(index); + + return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, + QCA808X_LED_PATTERN_MASK); +} + +static int qca808x_led_brightness_set(struct phy_device *phydev, + u8 index, enum led_brightness value) +{ + u16 reg; + int ret; + + if (index > 2) + return -EINVAL; + + if (!value) { + ret = qca808x_led_hw_control_reset(phydev, index); + if (ret) + return ret; + } + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_brightness_set(phydev, reg, value); +} + +static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u16 reg; + + if (index > 2) + return -EINVAL; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); + return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off); +} + +static int qca808x_led_polarity_set(struct phy_device *phydev, int index, + unsigned long modes) +{ + struct qca808x_priv *priv = phydev->priv; + bool active_low = false; + u32 mode; + + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { + switch (mode) { + case PHY_LED_ACTIVE_LOW: + active_low = true; + break; + default: + return -EINVAL; + } + } + + /* PHY polarity is global and can't be set per LED. + * To detect this, check if last requested polarity mode + * match the new one. + */ + if (priv->led_polarity_mode >= 0 && + priv->led_polarity_mode != active_low) { + phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); + return -EINVAL; + } + + /* Save the last PHY polarity mode */ + priv->led_polarity_mode = active_low; + + return phy_modify_mmd(phydev, MDIO_MMD_AN, + QCA808X_MMD7_LED_POLARITY_CTRL, + QCA808X_LED_ACTIVE_HIGH, + active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); +} + +static struct phy_driver qca808x_driver[] = { +{ + /* Qualcomm QCA8081 */ + PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), + .name = "Qualcomm QCA8081", + .flags = PHY_POLL_CABLE_TEST, + .probe = qca808x_probe, + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, + .get_features = qca808x_get_features, + .config_aneg = qca808x_config_aneg, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_status = qca808x_read_status, + .config_init = qca808x_config_init, + .soft_reset = qca808x_soft_reset, + .cable_test_start = qca808x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, + .link_change_notify = qca808x_link_change_notify, + .led_brightness_set = qca808x_led_brightness_set, + .led_blink_set = qca808x_led_blink_set, + .led_hw_is_supported = qca808x_led_hw_is_supported, + .led_hw_control_set = qca808x_led_hw_control_set, + .led_hw_control_get = qca808x_led_hw_control_get, + .led_polarity_set = qca808x_led_polarity_set, +}, }; + +module_phy_driver(qca808x_driver); + +static struct mdio_device_id __maybe_unused qca808x_tbl[] = { + { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, qca808x_tbl); diff --git a/drivers/net/phy/qcom/qca83xx.c b/drivers/net/phy/qcom/qca83xx.c new file mode 100644 index 000000000000..5d083ef0250e --- /dev/null +++ b/drivers/net/phy/qcom/qca83xx.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/phy.h> +#include <linux/module.h> + +#include "qcom.h" + +#define AT803X_DEBUG_REG_3C 0x3C + +#define AT803X_DEBUG_REG_GREEN 0x3D +#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) + +#define MDIO_AZ_DEBUG 0x800D + +#define QCA8327_A_PHY_ID 0x004dd033 +#define QCA8327_B_PHY_ID 0x004dd034 +#define QCA8337_PHY_ID 0x004dd036 +#define QCA8K_PHY_ID_MASK 0xffffffff + +#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) + +static struct at803x_hw_stat qca83xx_hw_stats[] = { + { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, + { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, + { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, +}; + +struct qca83xx_priv { + u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; +}; + +MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver"); +MODULE_AUTHOR("Matus Ujhelyi"); +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); +MODULE_LICENSE("GPL"); + +static int qca83xx_get_sset_count(struct phy_device *phydev) +{ + return ARRAY_SIZE(qca83xx_hw_stats); +} + +static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { + strscpy(data + i * ETH_GSTRING_LEN, + qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); + } +} + +static u64 qca83xx_get_stat(struct phy_device *phydev, int i) +{ + struct at803x_hw_stat stat = qca83xx_hw_stats[i]; + struct qca83xx_priv *priv = phydev->priv; + int val; + u64 ret; + + if (stat.access_type == MMD) + val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); + else + val = phy_read(phydev, stat.reg); + + if (val < 0) { + ret = U64_MAX; + } else { + val = val & stat.mask; + priv->stats[i] += val; + ret = priv->stats[i]; + } + + return ret; +} + +static void qca83xx_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) + data[i] = qca83xx_get_stat(phydev, i); +} + +static int qca83xx_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct qca83xx_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + return 0; +} + +static int qca83xx_config_init(struct phy_device *phydev) +{ + u8 switch_revision; + + switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; + + switch (switch_revision) { + case 1: + /* For 100M waveform */ + at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); + /* Turn on Gigabit clock */ + at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); + break; + + case 2: + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); + fallthrough; + case 4: + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); + at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); + at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); + at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); + break; + } + + /* Following original QCA sourcecode set port to prefer master */ + phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); + + return 0; +} + +static int qca8327_config_init(struct phy_device *phydev) +{ + /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. + * Disable on init and enable only with 100m speed following + * qca original source code. + */ + at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + QCA8327_DEBUG_MANU_CTRL_EN, 0); + + return qca83xx_config_init(phydev); +} + +static void qca83xx_link_change_notify(struct phy_device *phydev) +{ + /* Set DAC Amplitude adjustment to +6% for 100m on link running */ + if (phydev->state == PHY_RUNNING) { + if (phydev->speed == SPEED_100) + at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + QCA8327_DEBUG_MANU_CTRL_EN, + QCA8327_DEBUG_MANU_CTRL_EN); + } else { + /* Reset DAC Amplitude adjustment */ + at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + QCA8327_DEBUG_MANU_CTRL_EN, 0); + } +} + +static int qca83xx_resume(struct phy_device *phydev) +{ + int ret, val; + + /* Skip reset if not suspended */ + if (!phydev->suspended) + return 0; + + /* Reinit the port, reset values set by suspend */ + qca83xx_config_init(phydev); + + /* Reset the port on port resume */ + phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); + + /* On resume from suspend the switch execute a reset and + * restart auto-negotiation. Wait for reset to complete. + */ + ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), + 50000, 600000, true); + if (ret) + return ret; + + usleep_range(1000, 2000); + + return 0; +} + +static int qca83xx_suspend(struct phy_device *phydev) +{ + at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, + AT803X_DEBUG_GATE_CLK_IN1000, 0); + + at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, + AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | + AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); + + return 0; +} + +static int qca8337_suspend(struct phy_device *phydev) +{ + /* Only QCA8337 support actual suspend. */ + genphy_suspend(phydev); + + return qca83xx_suspend(phydev); +} + +static int qca8327_suspend(struct phy_device *phydev) +{ + u16 mask = 0; + + /* QCA8327 cause port unreliability when phy suspend + * is set. + */ + mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); + phy_modify(phydev, MII_BMCR, mask, 0); + + return qca83xx_suspend(phydev); +} + +static struct phy_driver qca83xx_driver[] = { +{ + /* QCA8337 */ + .phy_id = QCA8337_PHY_ID, + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "Qualcomm Atheros 8337 internal PHY", + /* PHY_GBIT_FEATURES */ + .probe = qca83xx_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, + .suspend = qca8337_suspend, + .resume = qca83xx_resume, +}, { + /* QCA8327-A from switch QCA8327-AL1A */ + .phy_id = QCA8327_A_PHY_ID, + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "Qualcomm Atheros 8327-A internal PHY", + /* PHY_GBIT_FEATURES */ + .link_change_notify = qca83xx_link_change_notify, + .probe = qca83xx_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca8327_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, + .suspend = qca8327_suspend, + .resume = qca83xx_resume, +}, { + /* QCA8327-B from switch QCA8327-BL1A */ + .phy_id = QCA8327_B_PHY_ID, + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "Qualcomm Atheros 8327-B internal PHY", + /* PHY_GBIT_FEATURES */ + .link_change_notify = qca83xx_link_change_notify, + .probe = qca83xx_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca8327_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, + .suspend = qca8327_suspend, + .resume = qca83xx_resume, +}, }; + +module_phy_driver(qca83xx_driver); + +static struct mdio_device_id __maybe_unused qca83xx_tbl[] = { + { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, qca83xx_tbl); diff --git a/drivers/net/phy/qcom/qcom-phy-lib.c b/drivers/net/phy/qcom/qcom-phy-lib.c new file mode 100644 index 000000000000..d28815ef56bb --- /dev/null +++ b/drivers/net/phy/qcom/qcom-phy-lib.c @@ -0,0 +1,676 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/phy.h> +#include <linux/module.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool_netlink.h> + +#include "qcom.h" + +MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions"); +MODULE_AUTHOR("Matus Ujhelyi"); +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); +MODULE_LICENSE("GPL"); + +int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) +{ + int ret; + + ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); + if (ret < 0) + return ret; + + return phy_read(phydev, AT803X_DEBUG_DATA); +} +EXPORT_SYMBOL_GPL(at803x_debug_reg_read); + +int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, + u16 clear, u16 set) +{ + u16 val; + int ret; + + ret = at803x_debug_reg_read(phydev, reg); + if (ret < 0) + return ret; + + val = ret & 0xffff; + val &= ~clear; + val |= set; + + return phy_write(phydev, AT803X_DEBUG_DATA, val); +} +EXPORT_SYMBOL_GPL(at803x_debug_reg_mask); + +int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) +{ + int ret; + + ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); + if (ret < 0) + return ret; + + return phy_write(phydev, AT803X_DEBUG_DATA, data); +} +EXPORT_SYMBOL_GPL(at803x_debug_reg_write); + +int at803x_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int ret, irq_enabled; + + if (wol->wolopts & WAKE_MAGIC) { + struct net_device *ndev = phydev->attached_dev; + const u8 *mac; + unsigned int i; + static const unsigned int offsets[] = { + AT803X_LOC_MAC_ADDR_32_47_OFFSET, + AT803X_LOC_MAC_ADDR_16_31_OFFSET, + AT803X_LOC_MAC_ADDR_0_15_OFFSET, + }; + + if (!ndev) + return -ENODEV; + + mac = (const u8 *)ndev->dev_addr; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; + + for (i = 0; i < 3; i++) + phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], + mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); + + /* Enable WOL interrupt */ + ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); + if (ret) + return ret; + } else { + /* Disable WOL interrupt */ + ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); + if (ret) + return ret; + } + + /* Clear WOL status */ + ret = phy_read(phydev, AT803X_INTR_STATUS); + if (ret < 0) + return ret; + + /* Check if there are other interrupts except for WOL triggered when PHY is + * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can + * be passed up to the interrupt PIN. + */ + irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); + if (irq_enabled < 0) + return irq_enabled; + + irq_enabled &= ~AT803X_INTR_ENABLE_WOL; + if (ret & irq_enabled && !phy_polling_mode(phydev)) + phy_trigger_machine(phydev); + + return 0; +} +EXPORT_SYMBOL_GPL(at803x_set_wol); + +void at803x_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int value; + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + value = phy_read(phydev, AT803X_INTR_ENABLE); + if (value < 0) + return; + + if (value & AT803X_INTR_ENABLE_WOL) + wol->wolopts |= WAKE_MAGIC; +} +EXPORT_SYMBOL_GPL(at803x_get_wol); + +int at803x_ack_interrupt(struct phy_device *phydev) +{ + int err; + + err = phy_read(phydev, AT803X_INTR_STATUS); + + return (err < 0) ? err : 0; +} +EXPORT_SYMBOL_GPL(at803x_ack_interrupt); + +int at803x_config_intr(struct phy_device *phydev) +{ + int err; + int value; + + value = phy_read(phydev, AT803X_INTR_ENABLE); + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + /* Clear any pending interrupts */ + err = at803x_ack_interrupt(phydev); + if (err) + return err; + + value |= AT803X_INTR_ENABLE_AUTONEG_ERR; + value |= AT803X_INTR_ENABLE_SPEED_CHANGED; + value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; + value |= AT803X_INTR_ENABLE_LINK_FAIL; + value |= AT803X_INTR_ENABLE_LINK_SUCCESS; + + err = phy_write(phydev, AT803X_INTR_ENABLE, value); + } else { + err = phy_write(phydev, AT803X_INTR_ENABLE, 0); + if (err) + return err; + + /* Clear any pending interrupts */ + err = at803x_ack_interrupt(phydev); + } + + return err; +} +EXPORT_SYMBOL_GPL(at803x_config_intr); + +irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) +{ + int irq_status, int_enabled; + + irq_status = phy_read(phydev, AT803X_INTR_STATUS); + if (irq_status < 0) { + phy_error(phydev); + return IRQ_NONE; + } + + /* Read the current enabled interrupts */ + int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); + if (int_enabled < 0) { + phy_error(phydev); + return IRQ_NONE; + } + + /* See if this was one of our enabled interrupts */ + if (!(irq_status & int_enabled)) + return IRQ_NONE; + + phy_trigger_machine(phydev); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(at803x_handle_interrupt); + +int at803x_read_specific_status(struct phy_device *phydev, + struct at803x_ss_mask ss_mask) +{ + int ss; + + /* Read the AT8035 PHY-Specific Status register, which indicates the + * speed and duplex that the PHY is actually using, irrespective of + * whether we are in autoneg mode or not. + */ + ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); + if (ss < 0) + return ss; + + if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { + int sfc, speed; + + sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); + if (sfc < 0) + return sfc; + + speed = ss & ss_mask.speed_mask; + speed >>= ss_mask.speed_shift; + + switch (speed) { + case AT803X_SS_SPEED_10: + phydev->speed = SPEED_10; + break; + case AT803X_SS_SPEED_100: + phydev->speed = SPEED_100; + break; + case AT803X_SS_SPEED_1000: + phydev->speed = SPEED_1000; + break; + case QCA808X_SS_SPEED_2500: + phydev->speed = SPEED_2500; + break; + } + if (ss & AT803X_SS_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (ss & AT803X_SS_MDIX) + phydev->mdix = ETH_TP_MDI_X; + else + phydev->mdix = ETH_TP_MDI; + + switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { + case AT803X_SFC_MANUAL_MDI: + phydev->mdix_ctrl = ETH_TP_MDI; + break; + case AT803X_SFC_MANUAL_MDIX: + phydev->mdix_ctrl = ETH_TP_MDI_X; + break; + case AT803X_SFC_AUTOMATIC_CROSSOVER: + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + break; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(at803x_read_specific_status); + +int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) +{ + u16 val; + + switch (ctrl) { + case ETH_TP_MDI: + val = AT803X_SFC_MANUAL_MDI; + break; + case ETH_TP_MDI_X: + val = AT803X_SFC_MANUAL_MDIX; + break; + case ETH_TP_MDI_AUTO: + val = AT803X_SFC_AUTOMATIC_CROSSOVER; + break; + default: + return 0; + } + + return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, + AT803X_SFC_MDI_CROSSOVER_MODE_M, + FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); +} +EXPORT_SYMBOL_GPL(at803x_config_mdix); + +int at803x_prepare_config_aneg(struct phy_device *phydev) +{ + int ret; + + ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); + if (ret < 0) + return ret; + + /* Changes of the midx bits are disruptive to the normal operation; + * therefore any changes to these registers must be followed by a + * software reset to take effect. + */ + if (ret == 1) { + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg); + +int at803x_read_status(struct phy_device *phydev) +{ + struct at803x_ss_mask ss_mask = { 0 }; + int err, old_link = phydev->link; + + /* Update the link, but return if there was an error */ + err = genphy_update_link(phydev); + if (err) + return err; + + /* why bother the PHY if nothing can have changed */ + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + return 0; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + + err = genphy_read_lpa(phydev); + if (err < 0) + return err; + + ss_mask.speed_mask = AT803X_SS_SPEED_MASK; + ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); + err = at803x_read_specific_status(phydev, ss_mask); + if (err < 0) + return err; + + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) + phy_resolve_aneg_pause(phydev); + + return 0; +} +EXPORT_SYMBOL_GPL(at803x_read_status); + +static int at803x_get_downshift(struct phy_device *phydev, u8 *d) +{ + int val; + + val = phy_read(phydev, AT803X_SMART_SPEED); + if (val < 0) + return val; + + if (val & AT803X_SMART_SPEED_ENABLE) + *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; + else + *d = DOWNSHIFT_DEV_DISABLE; + + return 0; +} + +static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) +{ + u16 mask, set; + int ret; + + switch (cnt) { + case DOWNSHIFT_DEV_DEFAULT_COUNT: + cnt = AT803X_DEFAULT_DOWNSHIFT; + fallthrough; + case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: + set = AT803X_SMART_SPEED_ENABLE | + AT803X_SMART_SPEED_BYPASS_TIMER | + FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); + mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; + break; + case DOWNSHIFT_DEV_DISABLE: + set = 0; + mask = AT803X_SMART_SPEED_ENABLE | + AT803X_SMART_SPEED_BYPASS_TIMER; + break; + default: + return -EINVAL; + } + + ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); + + /* After changing the smart speed settings, we need to perform a + * software reset, use phy_init_hw() to make sure we set the + * reapply any values which might got lost during software reset. + */ + if (ret == 1) + ret = phy_init_hw(phydev); + + return ret; +} + +int at803x_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return at803x_get_downshift(phydev, data); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(at803x_get_tunable); + +int at803x_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return at803x_set_downshift(phydev, *(const u8 *)data); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(at803x_set_tunable); + +int at803x_cdt_fault_length(int dt) +{ + /* According to the datasheet the distance to the fault is + * DELTA_TIME * 0.824 meters. + * + * The author suspect the correct formula is: + * + * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 + * + * where c is the speed of light, VF is the velocity factor of + * the twisted pair cable, 125MHz the counter frequency and + * we need to divide by 2 because the hardware will measure the + * round trip time to the fault and back to the PHY. + * + * With a VF of 0.69 we get the factor 0.824 mentioned in the + * datasheet. + */ + return (dt * 824) / 10; +} +EXPORT_SYMBOL_GPL(at803x_cdt_fault_length); + +int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start) +{ + return phy_write(phydev, AT803X_CDT, cdt_start); +} +EXPORT_SYMBOL_GPL(at803x_cdt_start); + +int at803x_cdt_wait_for_completion(struct phy_device *phydev, + u32 cdt_en) +{ + int val, ret; + + /* One test run takes about 25ms */ + ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, + !(val & cdt_en), + 30000, 100000, true); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion); + +static bool qca808x_cdt_fault_length_valid(int cdt_code) +{ + switch (cdt_code) { + case QCA808X_CDT_STATUS_STAT_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: + return true; + default: + return false; + } +} + +static int qca808x_cable_test_result_trans(int cdt_code) +{ + switch (cdt_code) { + case QCA808X_CDT_STATUS_STAT_NORMAL: + return ETHTOOL_A_CABLE_RESULT_CODE_OK; + case QCA808X_CDT_STATUS_STAT_SAME_SHORT: + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; + case QCA808X_CDT_STATUS_STAT_SAME_OPEN: + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: + return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; + case QCA808X_CDT_STATUS_STAT_FAIL: + default: + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; + } +} + +static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, + int result) +{ + int val; + u32 cdt_length_reg = 0; + + switch (pair) { + case ETHTOOL_A_CABLE_PAIR_A: + cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; + break; + case ETHTOOL_A_CABLE_PAIR_B: + cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; + break; + case ETHTOOL_A_CABLE_PAIR_C: + cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; + break; + case ETHTOOL_A_CABLE_PAIR_D: + cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; + break; + default: + return -EINVAL; + } + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); + if (val < 0) + return val; + + if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) + val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); + else + val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); + + return at803x_cdt_fault_length(val); +} + +static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, + u16 status) +{ + int length, result; + u16 pair_code; + + switch (pair) { + case ETHTOOL_A_CABLE_PAIR_A: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); + break; + case ETHTOOL_A_CABLE_PAIR_B: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); + break; + case ETHTOOL_A_CABLE_PAIR_C: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); + break; + case ETHTOOL_A_CABLE_PAIR_D: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); + break; + default: + return -EINVAL; + } + + result = qca808x_cable_test_result_trans(pair_code); + ethnl_cable_test_result(phydev, pair, result); + + if (qca808x_cdt_fault_length_valid(pair_code)) { + length = qca808x_cdt_fault_length(phydev, pair, result); + ethnl_cable_test_fault_length(phydev, pair, length); + } + + return 0; +} + +int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) +{ + int ret, val; + + *finished = false; + + val = QCA808X_CDT_ENABLE_TEST | + QCA808X_CDT_LENGTH_UNIT; + ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; + + ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); + if (ret) + return ret; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); + if (val < 0) + return val; + + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); + if (ret) + return ret; + + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); + if (ret) + return ret; + + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); + if (ret) + return ret; + + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); + if (ret) + return ret; + + *finished = true; + + return 0; +} +EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status); + +int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg) +{ + return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, + QCA808X_LED_FORCE_EN); +} +EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable); + +bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); + return !(val & QCA808X_LED_FORCE_EN); +} +EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status); + +int qca808x_led_reg_brightness_set(struct phy_device *phydev, + u16 reg, enum led_brightness value) +{ + return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, + QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON : + QCA808X_LED_FORCE_OFF)); +} +EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set); + +int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, + unsigned long *delay_on, + unsigned long *delay_off) +{ + int ret; + + /* Set blink to 50% off, 50% on at 4Hz by default */ + ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, + QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, + QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); + if (ret) + return ret; + + /* We use BLINK_1 for normal blinking */ + ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); + if (ret) + return ret; + + /* We set blink to 4Hz, aka 250ms */ + *delay_on = 250 / 2; + *delay_off = 250 / 2; + + return 0; +} +EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set); diff --git a/drivers/net/phy/qcom/qcom.h b/drivers/net/phy/qcom/qcom.h new file mode 100644 index 000000000000..4bb541728846 --- /dev/null +++ b/drivers/net/phy/qcom/qcom.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 +#define AT803X_SFC_ASSERT_CRS BIT(11) +#define AT803X_SFC_FORCE_LINK BIT(10) +#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) +#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 +#define AT803X_SFC_MANUAL_MDIX 0x1 +#define AT803X_SFC_MANUAL_MDI 0x0 +#define AT803X_SFC_SQE_TEST BIT(2) +#define AT803X_SFC_POLARITY_REVERSAL BIT(1) +#define AT803X_SFC_DISABLE_JABBER BIT(0) + +#define AT803X_SPECIFIC_STATUS 0x11 +#define AT803X_SS_SPEED_MASK GENMASK(15, 14) +#define AT803X_SS_SPEED_1000 2 +#define AT803X_SS_SPEED_100 1 +#define AT803X_SS_SPEED_10 0 +#define AT803X_SS_DUPLEX BIT(13) +#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) +#define AT803X_SS_MDIX BIT(6) + +#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) +#define QCA808X_SS_SPEED_2500 4 + +#define AT803X_INTR_ENABLE 0x12 +#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) +#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) +#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) +#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) +#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) +#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) +#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) +#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) +#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) +#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) +#define AT803X_INTR_ENABLE_WOL BIT(0) + +#define AT803X_INTR_STATUS 0x13 + +#define AT803X_SMART_SPEED 0x14 +#define AT803X_SMART_SPEED_ENABLE BIT(5) +#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) +#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) + +#define AT803X_CDT 0x16 +#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) +#define AT803X_CDT_ENABLE_TEST BIT(0) +#define AT803X_CDT_STATUS 0x1c +#define AT803X_CDT_STATUS_STAT_NORMAL 0 +#define AT803X_CDT_STATUS_STAT_SHORT 1 +#define AT803X_CDT_STATUS_STAT_OPEN 2 +#define AT803X_CDT_STATUS_STAT_FAIL 3 +#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) +#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) + +#define QCA808X_CDT_ENABLE_TEST BIT(15) +#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +#define QCA808X_CDT_STATUS BIT(11) +#define QCA808X_CDT_LENGTH_UNIT BIT(10) + +#define QCA808X_MMD3_CDT_STATUS 0x8064 +#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 +#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 +#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 +#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) + +#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) +#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) +#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) +#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) + +#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) + +#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) + +/* NORMAL are MDI with type set to 0 */ +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ + QCA808X_CDT_STATUS_STAT_MDI1) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ + QCA808X_CDT_STATUS_STAT_MDI1) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ + QCA808X_CDT_STATUS_STAT_MDI2) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ + QCA808X_CDT_STATUS_STAT_MDI2) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ + QCA808X_CDT_STATUS_STAT_MDI3) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ + QCA808X_CDT_STATUS_STAT_MDI3) + +/* Added for reference of existence but should be handled by wait_for_completion already */ +#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) + +#define QCA808X_MMD7_LED_GLOBAL 0x8073 +#define QCA808X_LED_BLINK_1 GENMASK(11, 6) +#define QCA808X_LED_BLINK_2 GENMASK(5, 0) +/* Values are the same for both BLINK_1 and BLINK_2 */ +#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) +#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) +#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) +#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) +#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) +#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) +#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) +#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) +#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) +#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) +#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) +#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) +#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) +#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) +#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) +#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) +#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) +#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) + +/* LED hw control pattern is the same for every LED */ +#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) +#define QCA808X_LED_SPEED2500_ON BIT(15) +#define QCA808X_LED_SPEED2500_BLINK BIT(14) +/* Follow blink trigger even if duplex or speed condition doesn't match */ +#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) +#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) +#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) +#define QCA808X_LED_TX_BLINK BIT(10) +#define QCA808X_LED_RX_BLINK BIT(9) +#define QCA808X_LED_TX_ON_10MS BIT(8) +#define QCA808X_LED_RX_ON_10MS BIT(7) +#define QCA808X_LED_SPEED1000_ON BIT(6) +#define QCA808X_LED_SPEED100_ON BIT(5) +#define QCA808X_LED_SPEED10_ON BIT(4) +#define QCA808X_LED_COLLISION_BLINK BIT(3) +#define QCA808X_LED_SPEED1000_BLINK BIT(2) +#define QCA808X_LED_SPEED100_BLINK BIT(1) +#define QCA808X_LED_SPEED10_BLINK BIT(0) + +/* LED force ctrl is the same for every LED + * No documentation exist for this, not even internal one + * with NDA as QCOM gives only info about configuring + * hw control pattern rules and doesn't indicate any way + * to force the LED to specific mode. + * These define comes from reverse and testing and maybe + * lack of some info or some info are not entirely correct. + * For the basic LED control and hw control these finding + * are enough to support LED control in all the required APIs. + * + * On doing some comparison with implementation with qca807x, + * it was found that it's 1:1 equal to it and confirms all the + * reverse done. It was also found further specification with the + * force mode and the blink modes. + */ +#define QCA808X_LED_FORCE_EN BIT(15) +#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) +#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) +#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) +#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) +#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) + +#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C +#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B +#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A + +#define AT803X_DEBUG_ADDR 0x1D +#define AT803X_DEBUG_DATA 0x1E + +#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00 +#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) +#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) +#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) + +#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05 +#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) + +#define AT803X_DEBUG_REG_HIB_CTRL 0x0b +#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) +#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) +#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) + +#define AT803X_DEFAULT_DOWNSHIFT 5 +#define AT803X_MIN_DOWNSHIFT 2 +#define AT803X_MAX_DOWNSHIFT 9 + +enum stat_access_type { + PHY, + MMD +}; + +struct at803x_hw_stat { + const char *string; + u8 reg; + u32 mask; + enum stat_access_type access_type; +}; + +struct at803x_ss_mask { + u16 speed_mask; + u8 speed_shift; +}; + +int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); +int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, + u16 clear, u16 set); +int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); +int at803x_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol); +void at803x_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol); +int at803x_ack_interrupt(struct phy_device *phydev); +int at803x_config_intr(struct phy_device *phydev); +irqreturn_t at803x_handle_interrupt(struct phy_device *phydev); +int at803x_read_specific_status(struct phy_device *phydev, + struct at803x_ss_mask ss_mask); +int at803x_config_mdix(struct phy_device *phydev, u8 ctrl); +int at803x_prepare_config_aneg(struct phy_device *phydev); +int at803x_read_status(struct phy_device *phydev); +int at803x_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data); +int at803x_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data); +int at803x_cdt_fault_length(int dt); +int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start); +int at803x_cdt_wait_for_completion(struct phy_device *phydev, + u32 cdt_en); +int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished); +int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg); +bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg); +int qca808x_led_reg_brightness_set(struct phy_device *phydev, + u16 reg, enum led_brightness value); +int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, + unsigned long *delay_on, + unsigned long *delay_off); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 894172a3e15f..481c79fbd6eb 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -57,14 +57,6 @@ #define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE_ON BIT(12) -#define RTL_SUPPORTS_5000FULL BIT(14) -#define RTL_SUPPORTS_2500FULL BIT(13) -#define RTL_SUPPORTS_10000FULL BIT(0) -#define RTL_ADV_2500FULL BIT(7) -#define RTL_LPADV_10000FULL BIT(11) -#define RTL_LPADV_5000FULL BIT(6) -#define RTL_LPADV_2500FULL BIT(5) - #define RTL9000A_GINMR 0x14 #define RTL9000A_GINMR_LINK_STATUS BIT(4) @@ -674,11 +666,11 @@ static int rtl822x_get_features(struct phy_device *phydev) return val; linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, - phydev->supported, val & RTL_SUPPORTS_2500FULL); + phydev->supported, val & MDIO_PMA_SPEED_2_5G); linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, - phydev->supported, val & RTL_SUPPORTS_5000FULL); + phydev->supported, val & MDIO_PMA_SPEED_5G); linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, - phydev->supported, val & RTL_SUPPORTS_10000FULL); + phydev->supported, val & MDIO_SPEED_10G); return genphy_read_abilities(phydev); } @@ -688,14 +680,12 @@ static int rtl822x_config_aneg(struct phy_device *phydev) int ret = 0; if (phydev->autoneg == AUTONEG_ENABLE) { - u16 adv2500 = 0; - - if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, - phydev->advertising)) - adv2500 = RTL_ADV_2500FULL; + u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12, - RTL_ADV_2500FULL, adv2500); + MDIO_AN_10GBT_CTRL_ADV2_5G | + MDIO_AN_10GBT_CTRL_ADV5G, + adv); if (ret < 0) return ret; } @@ -713,12 +703,8 @@ static int rtl822x_read_status(struct phy_device *phydev) if (lpadv < 0) return lpadv; - linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, - phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL); - linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, - phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL); - linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, - phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL); + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, + lpadv); } ret = genphy_read_status(phydev); @@ -736,7 +722,7 @@ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) val = phy_read(phydev, 0x13); phy_write(phydev, RTL821x_PAGE_SELECT, 0); - return val >= 0 && val & RTL_SUPPORTS_2500FULL; + return val >= 0 && val & MDIO_PMA_SPEED_2_5G; } static int rtlgen_match_phy_device(struct phy_device *phydev) @@ -1048,6 +1034,16 @@ static struct phy_driver realtek_drvs[] = { .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, }, { + PHY_ID_MATCH_EXACT(0x001cc862), + .name = "RTL8251B 5Gbps PHY", + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + }, { PHY_ID_MATCH_EXACT(0x001cc961), .name = "RTL8366RB Gigabit Ethernet", .config_init = &rtl8366rb_config_init, diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c index 7fd9fe6a602b..7b1bc5fcef9b 100644 --- a/drivers/net/phy/xilinx_gmii2rgmii.c +++ b/drivers/net/phy/xilinx_gmii2rgmii.c @@ -22,7 +22,7 @@ struct gmii2rgmii { struct phy_device *phy_dev; - struct phy_driver *phy_drv; + const struct phy_driver *phy_drv; struct phy_driver conv_phy_drv; struct mdio_device *mdio; }; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index 40ce8abe6999..cc7d1113ece0 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -1437,4 +1437,5 @@ static int __init plip_init (void) module_init(plip_init); module_exit(plip_cleanup_module); +MODULE_DESCRIPTION("PLIP (parallel port) network module"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ppp/bsd_comp.c b/drivers/net/ppp/bsd_comp.c index db0dc36d12e3..55954594e157 100644 --- a/drivers/net/ppp/bsd_comp.c +++ b/drivers/net/ppp/bsd_comp.c @@ -1166,5 +1166,6 @@ static void __exit bsdcomp_cleanup(void) module_init(bsdcomp_init); module_exit(bsdcomp_cleanup); +MODULE_DESCRIPTION("PPP BSD-Compress compression module"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("ppp-compress-" __stringify(CI_BSD_COMPRESS)); diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index 840da924708b..c33c3db3cc08 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -87,6 +87,7 @@ struct asyncppp { static int flag_time = HZ; module_param(flag_time, int, 0); MODULE_PARM_DESC(flag_time, "ppp_async: interval between flagged packets (in clock ticks)"); +MODULE_DESCRIPTION("PPP async serial channel module"); MODULE_LICENSE("GPL"); MODULE_ALIAS_LDISC(N_PPP); @@ -460,6 +461,10 @@ ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) case PPPIOCSMRU: if (get_user(val, p)) break; + if (val > U16_MAX) { + err = -EINVAL; + break; + } if (val < PPP_MRU) val = PPP_MRU; ap->mru = val; diff --git a/drivers/net/ppp/ppp_deflate.c b/drivers/net/ppp/ppp_deflate.c index e6d48e5c65a3..4d2ff63f2ee2 100644 --- a/drivers/net/ppp/ppp_deflate.c +++ b/drivers/net/ppp/ppp_deflate.c @@ -630,6 +630,7 @@ static void __exit deflate_cleanup(void) module_init(deflate_init); module_exit(deflate_cleanup); +MODULE_DESCRIPTION("PPP Deflate compression module"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE)); MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT)); diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 0193af2d31c9..db1d11ae817b 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1607,7 +1607,7 @@ static const struct net_device_ops ppp_netdev_ops = { .ndo_fill_forward_path = ppp_fill_forward_path, }; -static struct device_type ppp_type = { +static const struct device_type ppp_type = { .name = "ppp", }; @@ -3604,6 +3604,7 @@ EXPORT_SYMBOL(ppp_input_error); EXPORT_SYMBOL(ppp_output_wakeup); EXPORT_SYMBOL(ppp_register_compressor); EXPORT_SYMBOL(ppp_unregister_compressor); +MODULE_DESCRIPTION("Generic PPP layer driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); MODULE_ALIAS_RTNL_LINK("ppp"); diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index 52d05ce4a281..45bf59ac8f57 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -724,5 +724,6 @@ ppp_sync_cleanup(void) module_init(ppp_sync_init); module_exit(ppp_sync_cleanup); +MODULE_DESCRIPTION("PPP synchronous TTY channel module"); MODULE_LICENSE("GPL"); MODULE_ALIAS_LDISC(N_SYNC_PPP); diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 8e7238e97d0a..2ea4f4890d23 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -1007,26 +1007,21 @@ static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, struct sk_buff *skb; int error = 0; - if (sk->sk_state & PPPOX_BOUND) { - error = -EIO; - goto end; - } + if (sk->sk_state & PPPOX_BOUND) + return -EIO; skb = skb_recv_datagram(sk, flags, &error); - if (error < 0) - goto end; + if (!skb) + return error; - if (skb) { - total_len = min_t(size_t, total_len, skb->len); - error = skb_copy_datagram_msg(skb, 0, m, total_len); - if (error == 0) { - consume_skb(skb); - return total_len; - } + total_len = min_t(size_t, total_len, skb->len); + error = skb_copy_datagram_msg(skb, 0, m, total_len); + if (error == 0) { + consume_skb(skb); + return total_len; } kfree_skb(skb); -end: return error; } diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 4a4f8c8e79fa..bc80fc1d576e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -54,6 +54,7 @@ #include <linux/if_tun.h> #include <linux/if_vlan.h> #include <linux/crc32.h> +#include <linux/math.h> #include <linux/nsproxy.h> #include <linux/virtio_net.h> #include <linux/rcupdate.h> @@ -523,8 +524,7 @@ static inline void tun_flow_save_rps_rxhash(struct tun_flow_entry *e, u32 hash) static u16 tun_automq_select_queue(struct tun_struct *tun, struct sk_buff *skb) { struct tun_flow_entry *e; - u32 txq = 0; - u32 numqueues = 0; + u32 txq, numqueues; numqueues = READ_ONCE(tun->numqueues); @@ -534,8 +534,7 @@ static u16 tun_automq_select_queue(struct tun_struct *tun, struct sk_buff *skb) tun_flow_save_rps_rxhash(e, txq); txq = e->queue_index; } else { - /* use multiply and shift instead of expensive divide */ - txq = ((u64)txq * numqueues) >> 32; + txq = reciprocal_scale(txq, numqueues); } return txq; @@ -1927,7 +1926,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, rcu_read_lock(); xdp_prog = rcu_dereference(tun->xdp_prog); if (xdp_prog) { - ret = do_xdp_generic(xdp_prog, skb); + ret = do_xdp_generic(xdp_prog, &skb); if (ret != XDP_PASS) { rcu_read_unlock(); local_bh_enable(); @@ -2517,7 +2516,7 @@ build: skb_record_rx_queue(skb, tfile->queue_index); if (skb_xdp) { - ret = do_xdp_generic(xdp_prog, skb); + ret = do_xdp_generic(xdp_prog, &skb); if (ret != XDP_PASS) { ret = 0; goto out; @@ -3644,12 +3643,22 @@ static int tun_set_coalesce(struct net_device *dev, return 0; } +static void tun_get_channels(struct net_device *dev, + struct ethtool_channels *channels) +{ + struct tun_struct *tun = netdev_priv(dev); + + channels->combined_count = tun->numqueues; + channels->max_combined = tun->flags & IFF_MULTI_QUEUE ? MAX_TAP_QUEUES : 1; +} + static const struct ethtool_ops tun_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_RX_MAX_FRAMES, .get_drvinfo = tun_get_drvinfo, .get_msglevel = tun_get_msglevel, .set_msglevel = tun_set_msglevel, .get_link = ethtool_op_get_link, + .get_channels = tun_get_channels, .get_ts_info = ethtool_op_get_ts_info, .get_coalesce = tun_get_coalesce, .set_coalesce = tun_set_coalesce, diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index d837c1887416..d6168eaa286f 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -667,7 +667,7 @@ static int ax88179_set_link_ksettings(struct net_device *net, } static int -ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data) +ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_keee *data) { int val; @@ -676,29 +676,29 @@ ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data) MDIO_MMD_PCS); if (val < 0) return val; - data->supported = mmd_eee_cap_to_ethtool_sup_t(val); + data->supported_u32 = mmd_eee_cap_to_ethtool_sup_t(val); /* Get advertisement EEE */ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); if (val < 0) return val; - data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); + data->advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(val); /* Get LP advertisement EEE */ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN); if (val < 0) return val; - data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); + data->lp_advertised_u32 = mmd_eee_adv_to_ethtool_adv_t(val); return 0; } static int -ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data) +ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_keee *data) { - u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised); + u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised_u32); return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, tmp16); @@ -807,7 +807,7 @@ static void ax88179_enable_eee(struct usbnet *dev) GMII_PHY_PAGE_SELECT, 2, &tmp16); } -static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata) +static int ax88179_get_eee(struct net_device *net, struct ethtool_keee *edata) { struct usbnet *dev = netdev_priv(net); struct ax88179_data *priv = dev->driver_priv; @@ -818,7 +818,7 @@ static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata) return ax88179_ethtool_get_eee(dev, edata); } -static int ax88179_set_eee(struct net_device *net, struct ethtool_eee *edata) +static int ax88179_set_eee(struct net_device *net, struct ethtool_keee *edata) { struct usbnet *dev = netdev_priv(net); struct ax88179_data *priv = dev->driver_priv; @@ -1587,7 +1587,7 @@ static int ax88179_reset(struct usbnet *dev) u16 *tmp16; u8 *tmp; struct ax88179_data *ax179_data = dev->driver_priv; - struct ethtool_eee eee_data; + struct ethtool_keee eee_data; tmp16 = (u16 *)buf; tmp = (u8 *)buf; @@ -1663,7 +1663,7 @@ static int ax88179_reset(struct usbnet *dev) ax88179_disable_eee(dev); ax88179_ethtool_get_eee(dev, &eee_data); - eee_data.advertised = 0; + eee_data.advertised_u32 = 0; ax88179_ethtool_set_eee(dev, &eee_data); /* Restart autoneg */ diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index f088ea2ba6f3..1aeb36119d3f 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2465,7 +2465,7 @@ static void hso_create_rfkill(struct hso_device *hso_dev, } } -static struct device_type hso_type = { +static const struct device_type hso_type = { .name = "wwan", }; diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index a6d653ff552a..106282612bc2 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1673,7 +1673,7 @@ static int lan78xx_set_wol(struct net_device *netdev, return ret; } -static int lan78xx_get_eee(struct net_device *net, struct ethtool_eee *edata) +static int lan78xx_get_eee(struct net_device *net, struct ethtool_keee *edata) { struct lan78xx_net *dev = netdev_priv(net); struct phy_device *phydev = net->phydev; @@ -1709,7 +1709,7 @@ exit: return ret; } -static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata) +static int lan78xx_set_eee(struct net_device *net, struct ethtool_keee *edata) { struct lan78xx_net *dev = netdev_priv(net); int ret; diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 0d0672d2a654..3d806b3ff425 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -891,8 +891,8 @@ struct r8152 { void (*up)(struct r8152 *tp); void (*down)(struct r8152 *tp); void (*unload)(struct r8152 *tp); - int (*eee_get)(struct r8152 *tp, struct ethtool_eee *eee); - int (*eee_set)(struct r8152 *tp, struct ethtool_eee *eee); + int (*eee_get)(struct r8152 *tp, struct ethtool_keee *eee); + int (*eee_set)(struct r8152 *tp, struct ethtool_keee *eee); bool (*in_nway)(struct r8152 *tp); void (*hw_phy_cfg)(struct r8152 *tp); void (*autosuspend_en)(struct r8152 *tp, bool enable); @@ -8922,7 +8922,7 @@ static void rtl8152_get_strings(struct net_device *dev, u32 stringset, u8 *data) } } -static int r8152_get_eee(struct r8152 *tp, struct ethtool_eee *eee) +static int r8152_get_eee(struct r8152 *tp, struct ethtool_keee *eee) { u32 lp, adv, supported = 0; u16 val; @@ -8938,16 +8938,16 @@ static int r8152_get_eee(struct r8152 *tp, struct ethtool_eee *eee) eee->eee_enabled = tp->eee_en; eee->eee_active = !!(supported & adv & lp); - eee->supported = supported; - eee->advertised = tp->eee_adv; - eee->lp_advertised = lp; + eee->supported_u32 = supported; + eee->advertised_u32 = tp->eee_adv; + eee->lp_advertised_u32 = lp; return 0; } -static int r8152_set_eee(struct r8152 *tp, struct ethtool_eee *eee) +static int r8152_set_eee(struct r8152 *tp, struct ethtool_keee *eee) { - u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); + u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised_u32); tp->eee_en = eee->eee_enabled; tp->eee_adv = val; @@ -8957,7 +8957,7 @@ static int r8152_set_eee(struct r8152 *tp, struct ethtool_eee *eee) return 0; } -static int r8153_get_eee(struct r8152 *tp, struct ethtool_eee *eee) +static int r8153_get_eee(struct r8152 *tp, struct ethtool_keee *eee) { u32 lp, adv, supported = 0; u16 val; @@ -8973,15 +8973,15 @@ static int r8153_get_eee(struct r8152 *tp, struct ethtool_eee *eee) eee->eee_enabled = tp->eee_en; eee->eee_active = !!(supported & adv & lp); - eee->supported = supported; - eee->advertised = tp->eee_adv; - eee->lp_advertised = lp; + eee->supported_u32 = supported; + eee->advertised_u32 = tp->eee_adv; + eee->lp_advertised_u32 = lp; return 0; } static int -rtl_ethtool_get_eee(struct net_device *net, struct ethtool_eee *edata) +rtl_ethtool_get_eee(struct net_device *net, struct ethtool_keee *edata) { struct r8152 *tp = netdev_priv(net); int ret; @@ -9008,7 +9008,7 @@ out: } static int -rtl_ethtool_set_eee(struct net_device *net, struct ethtool_eee *edata) +rtl_ethtool_set_eee(struct net_device *net, struct ethtool_keee *edata) { struct r8152 *tp = netdev_priv(net); int ret; diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 2d14b0d78541..b21ebe24057f 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1654,11 +1654,11 @@ static const struct net_device_ops usbnet_netdev_ops = { // precondition: never called in_interrupt -static struct device_type wlan_type = { +static const struct device_type wlan_type = { .name = "wlan", }; -static struct device_type wwan_type = { +static const struct device_type wwan_type = { .name = "wwan", }; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 578e36ea1589..500b9dfccd08 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -729,80 +729,10 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, if (skb_shared(skb) || skb_head_is_locked(skb) || skb_shinfo(skb)->nr_frags || skb_headroom(skb) < XDP_PACKET_HEADROOM) { - u32 size, len, max_head_size, off, truesize, page_offset; - struct sk_buff *nskb; - struct page *page; - int i, head_off; - void *va; - - /* We need a private copy of the skb and data buffers since - * the ebpf program can modify it. We segment the original skb - * into order-0 pages without linearize it. - * - * Make sure we have enough space for linear and paged area - */ - max_head_size = SKB_WITH_OVERHEAD(PAGE_SIZE - - VETH_XDP_HEADROOM); - if (skb->len > PAGE_SIZE * MAX_SKB_FRAGS + max_head_size) - goto drop; - - size = min_t(u32, skb->len, max_head_size); - truesize = SKB_HEAD_ALIGN(size) + VETH_XDP_HEADROOM; - - /* Allocate skb head */ - va = page_pool_dev_alloc_va(rq->page_pool, &truesize); - if (!va) - goto drop; - - nskb = napi_build_skb(va, truesize); - if (!nskb) { - page_pool_free_va(rq->page_pool, va, true); + if (skb_pp_cow_data(rq->page_pool, pskb, XDP_PACKET_HEADROOM)) goto drop; - } - - skb_reserve(nskb, VETH_XDP_HEADROOM); - skb_copy_header(nskb, skb); - skb_mark_for_recycle(nskb); - - if (skb_copy_bits(skb, 0, nskb->data, size)) { - consume_skb(nskb); - goto drop; - } - skb_put(nskb, size); - head_off = skb_headroom(nskb) - skb_headroom(skb); - skb_headers_offset_update(nskb, head_off); - - /* Allocate paged area of new skb */ - off = size; - len = skb->len - off; - - for (i = 0; i < MAX_SKB_FRAGS && off < skb->len; i++) { - size = min_t(u32, len, PAGE_SIZE); - truesize = size; - - page = page_pool_dev_alloc(rq->page_pool, &page_offset, - &truesize); - if (!page) { - consume_skb(nskb); - goto drop; - } - - skb_add_rx_frag(nskb, i, page, page_offset, size, - truesize); - if (skb_copy_bits(skb, off, - page_address(page) + page_offset, - size)) { - consume_skb(nskb); - goto drop; - } - - len -= size; - off += size; - } - - consume_skb(skb); - skb = nskb; + skb = *pskb; } /* SKB "head" area always have tailroom for skb_shared_info */ @@ -1499,6 +1429,7 @@ static void veth_free_queues(struct net_device *dev) static int veth_dev_init(struct net_device *dev) { + netdev_lockdep_set_classes(dev); return veth_alloc_queues(dev); } diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 16106e088c63..386cbe4d3327 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2855,6 +2855,7 @@ static int vxlan_init(struct net_device *dev) if (err) goto err_gro_cells_destroy; + netdev_lockdep_set_classes(dev); return 0; err_gro_cells_destroy: @@ -3253,7 +3254,7 @@ static const struct net_device_ops vxlan_netdev_raw_ops = { }; /* Info for udev, that this is a virtual tunnel endpoint */ -static struct device_type vxlan_type = { +static const struct device_type vxlan_type = { .name = "vxlan", }; @@ -4826,55 +4827,43 @@ static __net_init int vxlan_init_net(struct net *net) NULL); } -static void vxlan_destroy_tunnels(struct net *net, struct list_head *head) +static void __net_exit vxlan_destroy_tunnels(struct vxlan_net *vn, + struct list_head *dev_to_kill) { - struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan, *next; - struct net_device *dev, *aux; - - for_each_netdev_safe(net, dev, aux) - if (dev->rtnl_link_ops == &vxlan_link_ops) - unregister_netdevice_queue(dev, head); - - list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) { - /* If vxlan->dev is in the same netns, it has already been added - * to the list by the previous loop. - */ - if (!net_eq(dev_net(vxlan->dev), net)) - unregister_netdevice_queue(vxlan->dev, head); - } + list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) + vxlan_dellink(vxlan->dev, dev_to_kill); } -static void __net_exit vxlan_exit_batch_net(struct list_head *net_list) +static void __net_exit vxlan_exit_batch_rtnl(struct list_head *net_list, + struct list_head *dev_to_kill) { struct net *net; - LIST_HEAD(list); - unsigned int h; + ASSERT_RTNL(); list_for_each_entry(net, net_list, exit_list) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); - unregister_nexthop_notifier(net, &vn->nexthop_notifier_block); - } - rtnl_lock(); - list_for_each_entry(net, net_list, exit_list) - vxlan_destroy_tunnels(net, &list); + __unregister_nexthop_notifier(net, &vn->nexthop_notifier_block); - unregister_netdevice_many(&list); - rtnl_unlock(); + vxlan_destroy_tunnels(vn, dev_to_kill); + } +} - list_for_each_entry(net, net_list, exit_list) { - struct vxlan_net *vn = net_generic(net, vxlan_net_id); +static void __net_exit vxlan_exit_net(struct net *net) +{ + struct vxlan_net *vn = net_generic(net, vxlan_net_id); + unsigned int h; - for (h = 0; h < PORT_HASH_SIZE; ++h) - WARN_ON_ONCE(!hlist_empty(&vn->sock_list[h])); - } + for (h = 0; h < PORT_HASH_SIZE; ++h) + WARN_ON_ONCE(!hlist_empty(&vn->sock_list[h])); } static struct pernet_operations vxlan_net_ops = { .init = vxlan_init_net, - .exit_batch = vxlan_exit_batch_net, + .exit_batch_rtnl = vxlan_exit_batch_rtnl, + .exit = vxlan_exit_net, .id = &vxlan_net_id, .size = sizeof(struct vxlan_net), }; diff --git a/drivers/net/wan/framer/framer-core.c b/drivers/net/wan/framer/framer-core.c index c04dc88bda6c..33b358b99f70 100644 --- a/drivers/net/wan/framer/framer-core.c +++ b/drivers/net/wan/framer/framer-core.c @@ -384,7 +384,7 @@ static struct framer_provider *framer_provider_of_lookup(const struct device_nod return ERR_PTR(-EPROBE_DEFER); } -static struct framer *framer_of_get_from_provider(struct of_phandle_args *args) +static struct framer *framer_of_get_from_provider(const struct of_phandle_args *args) { struct framer_provider *framer_provider; struct framer *framer; @@ -735,7 +735,8 @@ EXPORT_SYMBOL_GPL(devm_framer_create); * should provide a custom of_xlate function that reads the *args* and returns * the appropriate framer. */ -struct framer *framer_provider_simple_of_xlate(struct device *dev, struct of_phandle_args *args) +struct framer *framer_provider_simple_of_xlate(struct device *dev, + const struct of_phandle_args *args) { struct class_dev_iter iter; struct framer *framer; @@ -768,7 +769,7 @@ EXPORT_SYMBOL_GPL(framer_provider_simple_of_xlate); struct framer_provider * __framer_provider_of_register(struct device *dev, struct module *owner, struct framer *(*of_xlate)(struct device *dev, - struct of_phandle_args *args)) + const struct of_phandle_args *args)) { struct framer_provider *framer_provider; @@ -830,7 +831,7 @@ static void devm_framer_provider_of_unregister(struct device *dev, void *res) struct framer_provider * __devm_framer_provider_of_register(struct device *dev, struct module *owner, struct framer *(*of_xlate)(struct device *dev, - struct of_phandle_args *args)) + const struct of_phandle_args *args)) { struct framer_provider **ptr, *framer_provider; diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c index cc70360364b7..abc41a7089fa 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c +++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c @@ -57,8 +57,6 @@ #define CHECK_Q_STOP_TIMEOUT_US 1000000 #define CHECK_Q_STOP_STEP_US 10000 -#define CLDMA_JUMBO_BUFF_SZ (63 * 1024 + sizeof(struct ccci_header)) - static void md_cd_queue_struct_reset(struct cldma_queue *queue, struct cldma_ctrl *md_ctrl, enum mtk_txrx tx_rx, unsigned int index) { @@ -161,7 +159,7 @@ static int t7xx_cldma_gpd_rx_from_q(struct cldma_queue *queue, int budget, bool skb_reset_tail_pointer(skb); skb_put(skb, le16_to_cpu(gpd->data_buff_len)); - ret = md_ctrl->recv_skb(queue, skb); + ret = queue->recv_skb(queue, skb); /* Break processing, will try again later */ if (ret < 0) return ret; @@ -897,13 +895,13 @@ static void t7xx_cldma_hw_start_send(struct cldma_ctrl *md_ctrl, int qno, /** * t7xx_cldma_set_recv_skb() - Set the callback to handle RX packets. - * @md_ctrl: CLDMA context structure. + * @queue: CLDMA queue. * @recv_skb: Receiving skb callback. */ -void t7xx_cldma_set_recv_skb(struct cldma_ctrl *md_ctrl, +void t7xx_cldma_set_recv_skb(struct cldma_queue *queue, int (*recv_skb)(struct cldma_queue *queue, struct sk_buff *skb)) { - md_ctrl->recv_skb = recv_skb; + queue->recv_skb = recv_skb; } /** @@ -993,6 +991,28 @@ allow_sleep: return ret; } +static void t7xx_cldma_adjust_config(struct cldma_ctrl *md_ctrl, enum cldma_cfg cfg_id) +{ + int qno; + + for (qno = 0; qno < CLDMA_RXQ_NUM; qno++) { + md_ctrl->rx_ring[qno].pkt_size = CLDMA_SHARED_Q_BUFF_SZ; + t7xx_cldma_set_recv_skb(&md_ctrl->rxq[qno], t7xx_port_proxy_recv_skb); + } + + md_ctrl->rx_ring[CLDMA_RXQ_NUM - 1].pkt_size = CLDMA_JUMBO_BUFF_SZ; + + for (qno = 0; qno < CLDMA_TXQ_NUM; qno++) + md_ctrl->tx_ring[qno].pkt_size = CLDMA_SHARED_Q_BUFF_SZ; + + if (cfg_id == CLDMA_DEDICATED_Q_CFG) { + md_ctrl->tx_ring[CLDMA_Q_IDX_DUMP].pkt_size = CLDMA_DEDICATED_Q_BUFF_SZ; + md_ctrl->rx_ring[CLDMA_Q_IDX_DUMP].pkt_size = CLDMA_DEDICATED_Q_BUFF_SZ; + t7xx_cldma_set_recv_skb(&md_ctrl->rxq[CLDMA_Q_IDX_DUMP], + t7xx_port_proxy_recv_skb_from_dedicated_queue); + } +} + static int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl) { char dma_pool_name[32]; @@ -1018,16 +1038,9 @@ static int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl) dev_err(md_ctrl->dev, "control TX ring init fail\n"); goto err_free_tx_ring; } - - md_ctrl->tx_ring[i].pkt_size = CLDMA_MTU; } for (j = 0; j < CLDMA_RXQ_NUM; j++) { - md_ctrl->rx_ring[j].pkt_size = CLDMA_MTU; - - if (j == CLDMA_RXQ_NUM - 1) - md_ctrl->rx_ring[j].pkt_size = CLDMA_JUMBO_BUFF_SZ; - ret = t7xx_cldma_rx_ring_init(md_ctrl, &md_ctrl->rx_ring[j]); if (ret) { dev_err(md_ctrl->dev, "Control RX ring init fail\n"); @@ -1094,6 +1107,7 @@ int t7xx_cldma_alloc(enum cldma_id hif_id, struct t7xx_pci_dev *t7xx_dev) { struct device *dev = &t7xx_dev->pdev->dev; struct cldma_ctrl *md_ctrl; + int qno; md_ctrl = devm_kzalloc(dev, sizeof(*md_ctrl), GFP_KERNEL); if (!md_ctrl) @@ -1102,7 +1116,9 @@ int t7xx_cldma_alloc(enum cldma_id hif_id, struct t7xx_pci_dev *t7xx_dev) md_ctrl->t7xx_dev = t7xx_dev; md_ctrl->dev = dev; md_ctrl->hif_id = hif_id; - md_ctrl->recv_skb = t7xx_cldma_default_recv_skb; + for (qno = 0; qno < CLDMA_RXQ_NUM; qno++) + md_ctrl->rxq[qno].recv_skb = t7xx_cldma_default_recv_skb; + t7xx_hw_info_init(md_ctrl); t7xx_dev->md->md_ctrl[hif_id] = md_ctrl; return 0; @@ -1332,9 +1348,10 @@ err_workqueue: return -ENOMEM; } -void t7xx_cldma_switch_cfg(struct cldma_ctrl *md_ctrl) +void t7xx_cldma_switch_cfg(struct cldma_ctrl *md_ctrl, enum cldma_cfg cfg_id) { t7xx_cldma_late_release(md_ctrl); + t7xx_cldma_adjust_config(md_ctrl, cfg_id); t7xx_cldma_late_init(md_ctrl); } diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.h b/drivers/net/wwan/t7xx/t7xx_hif_cldma.h index 4410bac6993a..f2d9941be9c8 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.h +++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.h @@ -31,6 +31,10 @@ #include "t7xx_cldma.h" #include "t7xx_pci.h" +#define CLDMA_JUMBO_BUFF_SZ (63 * 1024 + sizeof(struct ccci_header)) +#define CLDMA_SHARED_Q_BUFF_SZ 3584 +#define CLDMA_DEDICATED_Q_BUFF_SZ 2048 + /** * enum cldma_id - Identifiers for CLDMA HW units. * @CLDMA_ID_MD: Modem control channel. @@ -55,6 +59,11 @@ struct cldma_gpd { __le16 not_used2; }; +enum cldma_cfg { + CLDMA_SHARED_Q_CFG, + CLDMA_DEDICATED_Q_CFG, +}; + struct cldma_request { struct cldma_gpd *gpd; /* Virtual address for CPU */ dma_addr_t gpd_addr; /* Physical address for DMA */ @@ -82,6 +91,7 @@ struct cldma_queue { wait_queue_head_t req_wq; /* Only for TX */ struct workqueue_struct *worker; struct work_struct cldma_work; + int (*recv_skb)(struct cldma_queue *queue, struct sk_buff *skb); }; struct cldma_ctrl { @@ -101,24 +111,22 @@ struct cldma_ctrl { struct md_pm_entity *pm_entity; struct t7xx_cldma_hw hw_info; bool is_late_init; - int (*recv_skb)(struct cldma_queue *queue, struct sk_buff *skb); }; +#define CLDMA_Q_IDX_DUMP 1 #define GPD_FLAGS_HWO BIT(0) #define GPD_FLAGS_IOC BIT(7) #define GPD_DMAPOOL_ALIGN 16 -#define CLDMA_MTU 3584 /* 3.5kB */ - int t7xx_cldma_alloc(enum cldma_id hif_id, struct t7xx_pci_dev *t7xx_dev); void t7xx_cldma_hif_hw_init(struct cldma_ctrl *md_ctrl); int t7xx_cldma_init(struct cldma_ctrl *md_ctrl); void t7xx_cldma_exit(struct cldma_ctrl *md_ctrl); -void t7xx_cldma_switch_cfg(struct cldma_ctrl *md_ctrl); +void t7xx_cldma_switch_cfg(struct cldma_ctrl *md_ctrl, enum cldma_cfg cfg_id); void t7xx_cldma_start(struct cldma_ctrl *md_ctrl); int t7xx_cldma_stop(struct cldma_ctrl *md_ctrl); void t7xx_cldma_reset(struct cldma_ctrl *md_ctrl); -void t7xx_cldma_set_recv_skb(struct cldma_ctrl *md_ctrl, +void t7xx_cldma_set_recv_skb(struct cldma_queue *queue, int (*recv_skb)(struct cldma_queue *queue, struct sk_buff *skb)); int t7xx_cldma_send_skb(struct cldma_ctrl *md_ctrl, int qno, struct sk_buff *skb); void t7xx_cldma_stop_all_qs(struct cldma_ctrl *md_ctrl, enum mtk_txrx tx_rx); diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.c b/drivers/net/wwan/t7xx/t7xx_modem_ops.c index 24e7d491468e..8d864d4ed77f 100644 --- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c +++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c @@ -177,6 +177,11 @@ int t7xx_acpi_fldr_func(struct t7xx_pci_dev *t7xx_dev) return t7xx_acpi_reset(t7xx_dev, "_RST"); } +int t7xx_acpi_pldr_func(struct t7xx_pci_dev *t7xx_dev) +{ + return t7xx_acpi_reset(t7xx_dev, "MRST._RST"); +} + static void t7xx_reset_device_via_pmic(struct t7xx_pci_dev *t7xx_dev) { u32 val; @@ -192,6 +197,7 @@ static irqreturn_t t7xx_rgu_isr_thread(int irq, void *data) { struct t7xx_pci_dev *t7xx_dev = data; + t7xx_mode_update(t7xx_dev, T7XX_RESET); msleep(RGU_RESET_DELAY_MS); t7xx_reset_device_via_pmic(t7xx_dev); return IRQ_HANDLED; @@ -529,7 +535,7 @@ static void t7xx_md_hk_wq(struct work_struct *work) /* Clear the HS2 EXIT event appended in core_reset() */ t7xx_fsm_clr_event(ctl, FSM_EVENT_MD_HS2_EXIT); - t7xx_cldma_switch_cfg(md->md_ctrl[CLDMA_ID_MD]); + t7xx_cldma_switch_cfg(md->md_ctrl[CLDMA_ID_MD], CLDMA_SHARED_Q_CFG); t7xx_cldma_start(md->md_ctrl[CLDMA_ID_MD]); t7xx_fsm_broadcast_state(ctl, MD_STATE_WAITING_FOR_HS2); md->core_md.handshake_ongoing = true; @@ -544,7 +550,7 @@ static void t7xx_ap_hk_wq(struct work_struct *work) /* Clear the HS2 EXIT event appended in t7xx_core_reset(). */ t7xx_fsm_clr_event(ctl, FSM_EVENT_AP_HS2_EXIT); t7xx_cldma_stop(md->md_ctrl[CLDMA_ID_AP]); - t7xx_cldma_switch_cfg(md->md_ctrl[CLDMA_ID_AP]); + t7xx_cldma_switch_cfg(md->md_ctrl[CLDMA_ID_AP], CLDMA_SHARED_Q_CFG); t7xx_cldma_start(md->md_ctrl[CLDMA_ID_AP]); md->core_ap.handshake_ongoing = true; t7xx_core_hk_handler(md, &md->core_ap, ctl, FSM_EVENT_AP_HS2, FSM_EVENT_AP_HS2_EXIT); @@ -758,6 +764,7 @@ err_destroy_hswq: void t7xx_md_exit(struct t7xx_pci_dev *t7xx_dev) { + enum t7xx_mode mode = READ_ONCE(t7xx_dev->mode); struct t7xx_modem *md = t7xx_dev->md; t7xx_pcie_mac_clear_int(t7xx_dev, SAP_RGU_INT); @@ -765,7 +772,8 @@ void t7xx_md_exit(struct t7xx_pci_dev *t7xx_dev) if (!md->md_init_finish) return; - t7xx_fsm_append_cmd(md->fsm_ctl, FSM_CMD_PRE_STOP, FSM_CMD_FLAG_WAIT_FOR_COMPLETION); + if (mode != T7XX_RESET && mode != T7XX_UNKNOWN) + t7xx_fsm_append_cmd(md->fsm_ctl, FSM_CMD_PRE_STOP, FSM_CMD_FLAG_WAIT_FOR_COMPLETION); t7xx_port_proxy_uninit(md->port_prox); t7xx_cldma_exit(md->md_ctrl[CLDMA_ID_AP]); t7xx_cldma_exit(md->md_ctrl[CLDMA_ID_MD]); diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.h b/drivers/net/wwan/t7xx/t7xx_modem_ops.h index abe633cf7adc..b39e945a92e0 100644 --- a/drivers/net/wwan/t7xx/t7xx_modem_ops.h +++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.h @@ -85,6 +85,7 @@ int t7xx_md_init(struct t7xx_pci_dev *t7xx_dev); void t7xx_md_exit(struct t7xx_pci_dev *t7xx_dev); void t7xx_clear_rgu_irq(struct t7xx_pci_dev *t7xx_dev); int t7xx_acpi_fldr_func(struct t7xx_pci_dev *t7xx_dev); +int t7xx_acpi_pldr_func(struct t7xx_pci_dev *t7xx_dev); int t7xx_pci_mhccif_isr(struct t7xx_pci_dev *t7xx_dev); #endif /* __T7XX_MODEM_OPS_H__ */ diff --git a/drivers/net/wwan/t7xx/t7xx_pci.c b/drivers/net/wwan/t7xx/t7xx_pci.c index 91256e005b84..e0b1e7a616ca 100644 --- a/drivers/net/wwan/t7xx/t7xx_pci.c +++ b/drivers/net/wwan/t7xx/t7xx_pci.c @@ -52,6 +52,81 @@ #define PM_RESOURCE_POLL_TIMEOUT_US 10000 #define PM_RESOURCE_POLL_STEP_US 100 +static const char * const t7xx_mode_names[] = { + [T7XX_UNKNOWN] = "unknown", + [T7XX_READY] = "ready", + [T7XX_RESET] = "reset", + [T7XX_FASTBOOT_SWITCHING] = "fastboot_switching", + [T7XX_FASTBOOT_DOWNLOAD] = "fastboot_download", + [T7XX_FASTBOOT_DUMP] = "fastboot_dump", +}; + +static_assert(ARRAY_SIZE(t7xx_mode_names) == T7XX_MODE_LAST); + +static ssize_t t7xx_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct t7xx_pci_dev *t7xx_dev; + struct pci_dev *pdev; + int index = 0; + + pdev = to_pci_dev(dev); + t7xx_dev = pci_get_drvdata(pdev); + if (!t7xx_dev) + return -ENODEV; + + index = sysfs_match_string(t7xx_mode_names, buf); + if (index == T7XX_FASTBOOT_SWITCHING) { + WRITE_ONCE(t7xx_dev->mode, T7XX_FASTBOOT_SWITCHING); + } else if (index == T7XX_RESET) { + WRITE_ONCE(t7xx_dev->mode, T7XX_RESET); + t7xx_acpi_pldr_func(t7xx_dev); + } + + return count; +}; + +static ssize_t t7xx_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + enum t7xx_mode mode = T7XX_UNKNOWN; + struct t7xx_pci_dev *t7xx_dev; + struct pci_dev *pdev; + + pdev = to_pci_dev(dev); + t7xx_dev = pci_get_drvdata(pdev); + if (!t7xx_dev) + return -ENODEV; + + mode = READ_ONCE(t7xx_dev->mode); + if (mode < T7XX_MODE_LAST) + return sysfs_emit(buf, "%s\n", t7xx_mode_names[mode]); + + return sysfs_emit(buf, "%s\n", t7xx_mode_names[T7XX_UNKNOWN]); +} + +static DEVICE_ATTR_RW(t7xx_mode); + +static struct attribute *t7xx_mode_attr[] = { + &dev_attr_t7xx_mode.attr, + NULL +}; + +static const struct attribute_group t7xx_mode_attribute_group = { + .attrs = t7xx_mode_attr, +}; + +void t7xx_mode_update(struct t7xx_pci_dev *t7xx_dev, enum t7xx_mode mode) +{ + if (!t7xx_dev) + return; + + WRITE_ONCE(t7xx_dev->mode, mode); + sysfs_notify(&t7xx_dev->pdev->dev.kobj, NULL, "t7xx_mode"); +} + enum t7xx_pm_state { MTK_PM_EXCEPTION, MTK_PM_INIT, /* Device initialized, but handshake not completed */ @@ -108,7 +183,7 @@ static int t7xx_pci_pm_init(struct t7xx_pci_dev *t7xx_dev) pm_runtime_set_autosuspend_delay(&pdev->dev, PM_AUTOSUSPEND_MS); pm_runtime_use_autosuspend(&pdev->dev); - return t7xx_wait_pm_config(t7xx_dev); + return 0; } void t7xx_pci_pm_init_late(struct t7xx_pci_dev *t7xx_dev) @@ -279,7 +354,8 @@ static int __t7xx_pci_pm_suspend(struct pci_dev *pdev) int ret; t7xx_dev = pci_get_drvdata(pdev); - if (atomic_read(&t7xx_dev->md_pm_state) <= MTK_PM_INIT) { + if (atomic_read(&t7xx_dev->md_pm_state) <= MTK_PM_INIT || + READ_ONCE(t7xx_dev->mode) != T7XX_READY) { dev_err(&pdev->dev, "[PM] Exiting suspend, modem in invalid state\n"); return -EFAULT; } @@ -729,16 +805,28 @@ static int t7xx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) t7xx_pcie_mac_interrupts_dis(t7xx_dev); + ret = sysfs_create_group(&t7xx_dev->pdev->dev.kobj, + &t7xx_mode_attribute_group); + if (ret) + goto err_md_exit; + ret = t7xx_interrupt_init(t7xx_dev); - if (ret) { - t7xx_md_exit(t7xx_dev); - return ret; - } + if (ret) + goto err_remove_group; + t7xx_pcie_mac_set_int(t7xx_dev, MHCCIF_INT); t7xx_pcie_mac_interrupts_en(t7xx_dev); return 0; + +err_remove_group: + sysfs_remove_group(&t7xx_dev->pdev->dev.kobj, + &t7xx_mode_attribute_group); + +err_md_exit: + t7xx_md_exit(t7xx_dev); + return ret; } static void t7xx_pci_remove(struct pci_dev *pdev) @@ -747,6 +835,9 @@ static void t7xx_pci_remove(struct pci_dev *pdev) int i; t7xx_dev = pci_get_drvdata(pdev); + + sysfs_remove_group(&t7xx_dev->pdev->dev.kobj, + &t7xx_mode_attribute_group); t7xx_md_exit(t7xx_dev); for (i = 0; i < EXT_INT_NUM; i++) { diff --git a/drivers/net/wwan/t7xx/t7xx_pci.h b/drivers/net/wwan/t7xx/t7xx_pci.h index f08f1ab74469..49a11586d8d8 100644 --- a/drivers/net/wwan/t7xx/t7xx_pci.h +++ b/drivers/net/wwan/t7xx/t7xx_pci.h @@ -43,6 +43,16 @@ struct t7xx_addr_base { typedef irqreturn_t (*t7xx_intr_callback)(int irq, void *param); +enum t7xx_mode { + T7XX_UNKNOWN, + T7XX_READY, + T7XX_RESET, + T7XX_FASTBOOT_SWITCHING, + T7XX_FASTBOOT_DOWNLOAD, + T7XX_FASTBOOT_DUMP, + T7XX_MODE_LAST, /* must always be last */ +}; + /* struct t7xx_pci_dev - MTK device context structure * @intr_handler: array of handler function for request_threaded_irq * @intr_thread: array of thread_fn for request_threaded_irq @@ -59,6 +69,7 @@ typedef irqreturn_t (*t7xx_intr_callback)(int irq, void *param); * @md_pm_lock: protects PCIe sleep lock * @sleep_disable_count: PCIe L1.2 lock counter * @sleep_lock_acquire: indicates that sleep has been disabled + * @mode: indicates the device mode */ struct t7xx_pci_dev { t7xx_intr_callback intr_handler[EXT_INT_NUM]; @@ -82,6 +93,7 @@ struct t7xx_pci_dev { #ifdef CONFIG_WWAN_DEBUGFS struct dentry *debugfs_dir; #endif + u32 mode; }; enum t7xx_pm_id { @@ -120,5 +132,5 @@ int t7xx_pci_pm_entity_register(struct t7xx_pci_dev *t7xx_dev, struct md_pm_enti int t7xx_pci_pm_entity_unregister(struct t7xx_pci_dev *t7xx_dev, struct md_pm_entity *pm_entity); void t7xx_pci_pm_init_late(struct t7xx_pci_dev *t7xx_dev); void t7xx_pci_pm_exp_detected(struct t7xx_pci_dev *t7xx_dev); - +void t7xx_mode_update(struct t7xx_pci_dev *t7xx_dev, enum t7xx_mode mode); #endif /* __T7XX_PCI_H__ */ diff --git a/drivers/net/wwan/t7xx/t7xx_port.h b/drivers/net/wwan/t7xx/t7xx_port.h index 4ae8a00a8532..f74d3bab810d 100644 --- a/drivers/net/wwan/t7xx/t7xx_port.h +++ b/drivers/net/wwan/t7xx/t7xx_port.h @@ -75,6 +75,8 @@ enum port_ch { PORT_CH_DSS6_TX = 0x20df, PORT_CH_DSS7_RX = 0x20e0, PORT_CH_DSS7_TX = 0x20e1, + + PORT_CH_UNIMPORTANT = 0xffff, }; struct t7xx_port; @@ -135,11 +137,13 @@ struct t7xx_port { }; }; +int t7xx_get_port_mtu(struct t7xx_port *port); struct sk_buff *t7xx_port_alloc_skb(int payload); struct sk_buff *t7xx_ctrl_alloc_skb(int payload); int t7xx_port_enqueue_skb(struct t7xx_port *port, struct sk_buff *skb); int t7xx_port_send_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int pkt_header, unsigned int ex_msg); +int t7xx_port_send_raw_skb(struct t7xx_port *port, struct sk_buff *skb); int t7xx_port_send_ctl_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int msg, unsigned int ex_msg); diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.c b/drivers/net/wwan/t7xx/t7xx_port_proxy.c index 274846d39fbf..8f5e01705af2 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.c +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.c @@ -48,6 +48,9 @@ i < (proxy)->port_count; \ i++, (p) = &(proxy)->ports[i]) +#define T7XX_MAX_POSSIBLE_PORTS_NUM \ + (max(ARRAY_SIZE(t7xx_port_conf), ARRAY_SIZE(t7xx_early_port_conf))) + static const struct t7xx_port_conf t7xx_port_conf[] = { { .tx_ch = PORT_CH_UART2_TX, @@ -100,6 +103,21 @@ static const struct t7xx_port_conf t7xx_port_conf[] = { }, }; +static const struct t7xx_port_conf t7xx_early_port_conf[] = { + { + .tx_ch = PORT_CH_UNIMPORTANT, + .rx_ch = PORT_CH_UNIMPORTANT, + .txq_index = CLDMA_Q_IDX_DUMP, + .rxq_index = CLDMA_Q_IDX_DUMP, + .txq_exp_index = CLDMA_Q_IDX_DUMP, + .rxq_exp_index = CLDMA_Q_IDX_DUMP, + .path_id = CLDMA_ID_AP, + .ops = &wwan_sub_port_ops, + .name = "fastboot", + .port_type = WWAN_PORT_FASTBOOT, + }, +}; + static struct t7xx_port *t7xx_proxy_get_port_by_ch(struct port_proxy *port_prox, enum port_ch ch) { const struct t7xx_port_conf *port_conf; @@ -214,7 +232,17 @@ int t7xx_port_enqueue_skb(struct t7xx_port *port, struct sk_buff *skb) return 0; } -static int t7xx_port_send_raw_skb(struct t7xx_port *port, struct sk_buff *skb) +int t7xx_get_port_mtu(struct t7xx_port *port) +{ + enum cldma_id path_id = port->port_conf->path_id; + int tx_qno = t7xx_port_get_queue_no(port); + struct cldma_ctrl *md_ctrl; + + md_ctrl = port->t7xx_dev->md->md_ctrl[path_id]; + return md_ctrl->tx_ring[tx_qno].pkt_size; +} + +int t7xx_port_send_raw_skb(struct t7xx_port *port, struct sk_buff *skb) { enum cldma_id path_id = port->port_conf->path_id; struct cldma_ctrl *md_ctrl; @@ -329,6 +357,39 @@ static void t7xx_proxy_setup_ch_mapping(struct port_proxy *port_prox) } } +/** + * t7xx_port_proxy_recv_skb_from_dedicated_queue() - Dispatch early port received skb. + * @queue: CLDMA queue. + * @skb: Socket buffer. + * + * Return: + ** 0 - Packet consumed. + ** -ERROR - Failed to process skb. + */ +int t7xx_port_proxy_recv_skb_from_dedicated_queue(struct cldma_queue *queue, struct sk_buff *skb) +{ + struct t7xx_pci_dev *t7xx_dev = queue->md_ctrl->t7xx_dev; + struct port_proxy *port_prox = t7xx_dev->md->port_prox; + const struct t7xx_port_conf *port_conf; + struct t7xx_port *port; + int ret; + + port = &port_prox->ports[0]; + if (WARN_ON_ONCE(port->port_conf->rxq_index != queue->index)) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + + port_conf = port->port_conf; + ret = port_conf->ops->recv_skb(port, skb); + if (ret < 0 && ret != -ENOBUFS) { + dev_err(port->dev, "drop on RX ch %d, %d\n", port_conf->rx_ch, ret); + dev_kfree_skb_any(skb); + } + + return ret; +} + static struct t7xx_port *t7xx_port_proxy_find_port(struct t7xx_pci_dev *t7xx_dev, struct cldma_queue *queue, u16 channel) { @@ -359,7 +420,7 @@ static struct t7xx_port *t7xx_port_proxy_find_port(struct t7xx_pci_dev *t7xx_dev ** 0 - Packet consumed. ** -ERROR - Failed to process skb. */ -static int t7xx_port_proxy_recv_skb(struct cldma_queue *queue, struct sk_buff *skb) +int t7xx_port_proxy_recv_skb(struct cldma_queue *queue, struct sk_buff *skb) { struct ccci_header *ccci_h = (struct ccci_header *)skb->data; struct t7xx_pci_dev *t7xx_dev = queue->md_ctrl->t7xx_dev; @@ -444,33 +505,54 @@ static void t7xx_proxy_init_all_ports(struct t7xx_modem *md) spin_lock_init(&port->port_update_lock); port->chan_enable = false; - if (port_conf->ops->init) + if (port_conf->ops && port_conf->ops->init) port_conf->ops->init(port); } t7xx_proxy_setup_ch_mapping(port_prox); } +void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id) +{ + struct port_proxy *port_prox = md->port_prox; + const struct t7xx_port_conf *port_conf; + u32 port_count; + int i; + + t7xx_port_proxy_uninit(port_prox); + + if (cfg_id == PORT_CFG_ID_EARLY) { + port_conf = t7xx_early_port_conf; + port_count = ARRAY_SIZE(t7xx_early_port_conf); + } else { + port_conf = t7xx_port_conf; + port_count = ARRAY_SIZE(t7xx_port_conf); + } + + for (i = 0; i < port_count; i++) + port_prox->ports[i].port_conf = &port_conf[i]; + + port_prox->cfg_id = cfg_id; + port_prox->port_count = port_count; + + t7xx_proxy_init_all_ports(md); +} + static int t7xx_proxy_alloc(struct t7xx_modem *md) { - unsigned int port_count = ARRAY_SIZE(t7xx_port_conf); struct device *dev = &md->t7xx_dev->pdev->dev; struct port_proxy *port_prox; - int i; - port_prox = devm_kzalloc(dev, sizeof(*port_prox) + sizeof(struct t7xx_port) * port_count, + port_prox = devm_kzalloc(dev, sizeof(*port_prox) + + sizeof(struct t7xx_port) * T7XX_MAX_POSSIBLE_PORTS_NUM, GFP_KERNEL); if (!port_prox) return -ENOMEM; md->port_prox = port_prox; port_prox->dev = dev; + t7xx_port_proxy_set_cfg(md, PORT_CFG_ID_EARLY); - for (i = 0; i < port_count; i++) - port_prox->ports[i].port_conf = &t7xx_port_conf[i]; - - port_prox->port_count = port_count; - t7xx_proxy_init_all_ports(md); return 0; } @@ -492,8 +574,6 @@ int t7xx_port_proxy_init(struct t7xx_modem *md) if (ret) return ret; - t7xx_cldma_set_recv_skb(md->md_ctrl[CLDMA_ID_AP], t7xx_port_proxy_recv_skb); - t7xx_cldma_set_recv_skb(md->md_ctrl[CLDMA_ID_MD], t7xx_port_proxy_recv_skb); return 0; } @@ -505,7 +585,7 @@ void t7xx_port_proxy_uninit(struct port_proxy *port_prox) for_each_proxy_port(i, port, port_prox) { const struct t7xx_port_conf *port_conf = port->port_conf; - if (port_conf->ops->uninit) + if (port_conf->ops && port_conf->ops->uninit) port_conf->ops->uninit(port); } } diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h index 81d059fbc0fb..7f5706811445 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h @@ -31,11 +31,18 @@ #define RX_QUEUE_MAXLEN 32 #define CTRL_QUEUE_MAXLEN 16 +enum port_cfg_id { + PORT_CFG_ID_INVALID, + PORT_CFG_ID_NORMAL, + PORT_CFG_ID_EARLY, +}; + struct port_proxy { int port_count; struct list_head rx_ch_ports[PORT_CH_ID_MASK + 1]; struct list_head queue_ports[CLDMA_NUM][MTK_QUEUES]; struct device *dev; + enum port_cfg_id cfg_id; struct t7xx_port ports[]; }; @@ -98,5 +105,8 @@ void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg); int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id, bool en_flag); +void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id); +int t7xx_port_proxy_recv_skb(struct cldma_queue *queue, struct sk_buff *skb); +int t7xx_port_proxy_recv_skb_from_dedicated_queue(struct cldma_queue *queue, struct sk_buff *skb); #endif /* __T7XX_PORT_PROXY_H__ */ diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c index 17389c8f6600..4b23ba693f3f 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2021, MediaTek Inc. * Copyright (c) 2021-2022, Intel Corporation. + * Copyright (c) 2024, Fibocom Wireless Inc. * * Authors: * Amir Hanania <amir.hanania@intel.com> @@ -15,6 +16,7 @@ * Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com> * Eliot Lee <eliot.lee@intel.com> * Sreehari Kancharla <sreehari.kancharla@intel.com> + * Jinjian Song <jinjian.song@fibocom.com> */ #include <linux/atomic.h> @@ -33,7 +35,7 @@ #include "t7xx_port_proxy.h" #include "t7xx_state_monitor.h" -static int t7xx_port_ctrl_start(struct wwan_port *port) +static int t7xx_port_wwan_start(struct wwan_port *port) { struct t7xx_port *port_mtk = wwan_port_get_drvdata(port); @@ -44,30 +46,60 @@ static int t7xx_port_ctrl_start(struct wwan_port *port) return 0; } -static void t7xx_port_ctrl_stop(struct wwan_port *port) +static void t7xx_port_wwan_stop(struct wwan_port *port) { struct t7xx_port *port_mtk = wwan_port_get_drvdata(port); atomic_dec(&port_mtk->usage_cnt); } -static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) +static int t7xx_port_fastboot_tx(struct t7xx_port *port, struct sk_buff *skb) +{ + struct sk_buff *cur = skb, *tx_skb; + size_t actual, len, offset = 0; + int txq_mtu; + int ret; + + txq_mtu = t7xx_get_port_mtu(port); + if (txq_mtu < 0) + return -EINVAL; + + actual = cur->len; + while (actual) { + len = min_t(size_t, actual, txq_mtu); + tx_skb = __dev_alloc_skb(len, GFP_KERNEL); + if (!tx_skb) + return -ENOMEM; + + skb_put_data(tx_skb, cur->data + offset, len); + + ret = t7xx_port_send_raw_skb(port, tx_skb); + if (ret) { + dev_kfree_skb(tx_skb); + dev_err(port->dev, "Write error on fastboot port, %d\n", ret); + break; + } + offset += len; + actual -= len; + } + + dev_kfree_skb(skb); + return 0; +} + +static int t7xx_port_ctrl_tx(struct t7xx_port *port, struct sk_buff *skb) { - struct t7xx_port *port_private = wwan_port_get_drvdata(port); const struct t7xx_port_conf *port_conf; struct sk_buff *cur = skb, *cloned; struct t7xx_fsm_ctl *ctl; enum md_state md_state; int cnt = 0, ret; - if (!port_private->chan_enable) - return -EINVAL; - - port_conf = port_private->port_conf; - ctl = port_private->t7xx_dev->md->fsm_ctl; + port_conf = port->port_conf; + ctl = port->t7xx_dev->md->fsm_ctl; md_state = t7xx_fsm_get_md_state(ctl); if (md_state == MD_STATE_WAITING_FOR_HS1 || md_state == MD_STATE_WAITING_FOR_HS2) { - dev_warn(port_private->dev, "Cannot write to %s port when md_state=%d\n", + dev_warn(port->dev, "Cannot write to %s port when md_state=%d\n", port_conf->name, md_state); return -ENODEV; } @@ -75,10 +107,10 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) while (cur) { cloned = skb_clone(cur, GFP_KERNEL); cloned->len = skb_headlen(cur); - ret = t7xx_port_send_skb(port_private, cloned, 0, 0); + ret = t7xx_port_send_skb(port, cloned, 0, 0); if (ret) { dev_kfree_skb(cloned); - dev_err(port_private->dev, "Write error on %s port, %d\n", + dev_err(port->dev, "Write error on %s port, %d\n", port_conf->name, ret); return cnt ? cnt + ret : ret; } @@ -93,14 +125,53 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) return 0; } +static int t7xx_port_wwan_tx(struct wwan_port *port, struct sk_buff *skb) +{ + struct t7xx_port *port_private = wwan_port_get_drvdata(port); + const struct t7xx_port_conf *port_conf = port_private->port_conf; + int ret; + + if (!port_private->chan_enable) + return -EINVAL; + + if (port_conf->port_type != WWAN_PORT_FASTBOOT) + ret = t7xx_port_ctrl_tx(port_private, skb); + else + ret = t7xx_port_fastboot_tx(port_private, skb); + + return ret; +} + static const struct wwan_port_ops wwan_ops = { - .start = t7xx_port_ctrl_start, - .stop = t7xx_port_ctrl_stop, - .tx = t7xx_port_ctrl_tx, + .start = t7xx_port_wwan_start, + .stop = t7xx_port_wwan_stop, + .tx = t7xx_port_wwan_tx, }; +static void t7xx_port_wwan_create(struct t7xx_port *port) +{ + const struct t7xx_port_conf *port_conf = port->port_conf; + unsigned int header_len = sizeof(struct ccci_header), mtu; + struct wwan_port_caps caps; + + if (!port->wwan.wwan_port) { + mtu = t7xx_get_port_mtu(port); + caps.frag_len = mtu - header_len; + caps.headroom_len = header_len; + port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type, + &wwan_ops, &caps, port); + if (IS_ERR(port->wwan.wwan_port)) + dev_err(port->dev, "Unable to create WWAN port %s", port_conf->name); + } +} + static int t7xx_port_wwan_init(struct t7xx_port *port) { + const struct t7xx_port_conf *port_conf = port->port_conf; + + if (port_conf->port_type == WWAN_PORT_FASTBOOT) + t7xx_port_wwan_create(port); + port->rx_length_th = RX_QUEUE_MAXLEN; return 0; } @@ -152,20 +223,14 @@ static int t7xx_port_wwan_disable_chl(struct t7xx_port *port) static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int state) { const struct t7xx_port_conf *port_conf = port->port_conf; - unsigned int header_len = sizeof(struct ccci_header); - struct wwan_port_caps caps; + + if (port_conf->port_type == WWAN_PORT_FASTBOOT) + return; if (state != MD_STATE_READY) return; - if (!port->wwan.wwan_port) { - caps.frag_len = CLDMA_MTU - header_len; - caps.headroom_len = header_len; - port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type, - &wwan_ops, &caps, port); - if (IS_ERR(port->wwan.wwan_port)) - dev_err(port->dev, "Unable to create WWWAN port %s", port_conf->name); - } + t7xx_port_wwan_create(port); } struct port_ops wwan_sub_port_ops = { diff --git a/drivers/net/wwan/t7xx/t7xx_reg.h b/drivers/net/wwan/t7xx/t7xx_reg.h index c41d7d094c08..9c7dc72ac6f6 100644 --- a/drivers/net/wwan/t7xx/t7xx_reg.h +++ b/drivers/net/wwan/t7xx/t7xx_reg.h @@ -101,11 +101,33 @@ enum t7xx_pm_resume_state { PM_RESUME_REG_STATE_L2_EXP, }; +enum host_event_e { + HOST_EVENT_INIT = 0, + FASTBOOT_DL_NOTIFY = 0x3, +}; + #define T7XX_PCIE_MISC_DEV_STATUS 0x0d1c #define MISC_STAGE_MASK GENMASK(2, 0) #define MISC_RESET_TYPE_PLDR BIT(26) #define MISC_RESET_TYPE_FLDR BIT(27) -#define LINUX_STAGE 4 +#define MISC_RESET_TYPE_PLDR BIT(26) +#define MISC_LK_EVENT_MASK GENMASK(11, 8) +#define HOST_EVENT_MASK GENMASK(31, 28) + +enum lk_event_id { + LK_EVENT_NORMAL = 0, + LK_EVENT_CREATE_PD_PORT = 1, + LK_EVENT_CREATE_POST_DL_PORT = 2, + LK_EVENT_RESET = 7, +}; + +enum t7xx_device_stage { + T7XX_DEV_STAGE_INIT = 0, + T7XX_DEV_STAGE_BROM_PRE = 1, + T7XX_DEV_STAGE_BROM_POST = 2, + T7XX_DEV_STAGE_LK = 3, + T7XX_DEV_STAGE_LINUX = 4, +}; #define T7XX_PCIE_RESOURCE_STATUS 0x0d28 #define T7XX_PCIE_RESOURCE_STS_MSK GENMASK(4, 0) diff --git a/drivers/net/wwan/t7xx/t7xx_state_monitor.c b/drivers/net/wwan/t7xx/t7xx_state_monitor.c index 0bc97430211b..9889ca4621cf 100644 --- a/drivers/net/wwan/t7xx/t7xx_state_monitor.c +++ b/drivers/net/wwan/t7xx/t7xx_state_monitor.c @@ -47,6 +47,13 @@ #define FSM_MD_EX_PASS_TIMEOUT_MS 45000 #define FSM_CMD_TIMEOUT_MS 2000 +#define wait_for_expected_dev_stage(status) \ + read_poll_timeout(ioread32, status, \ + ((status & MISC_STAGE_MASK) == T7XX_DEV_STAGE_LINUX) || \ + ((status & MISC_STAGE_MASK) == T7XX_DEV_STAGE_LK), 100000, \ + 20000000, false, IREG_BASE(md->t7xx_dev) + \ + T7XX_PCIE_MISC_DEV_STATUS) + void t7xx_fsm_notifier_register(struct t7xx_modem *md, struct t7xx_fsm_notifier *notifier) { struct t7xx_fsm_ctl *ctl = md->fsm_ctl; @@ -206,6 +213,55 @@ static void fsm_routine_exception(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comm fsm_finish_command(ctl, cmd, 0); } +static void t7xx_host_event_notify(struct t7xx_modem *md, unsigned int event_id) +{ + u32 value; + + value = ioread32(IREG_BASE(md->t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS); + value &= ~HOST_EVENT_MASK; + value |= FIELD_PREP(HOST_EVENT_MASK, event_id); + iowrite32(value, IREG_BASE(md->t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS); +} + +static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int status) +{ + struct t7xx_modem *md = ctl->md; + struct cldma_ctrl *md_ctrl; + enum lk_event_id lk_event; + struct device *dev; + struct t7xx_port *port; + + dev = &md->t7xx_dev->pdev->dev; + lk_event = FIELD_GET(MISC_LK_EVENT_MASK, status); + switch (lk_event) { + case LK_EVENT_NORMAL: + case LK_EVENT_RESET: + break; + + case LK_EVENT_CREATE_PD_PORT: + case LK_EVENT_CREATE_POST_DL_PORT: + md_ctrl = md->md_ctrl[CLDMA_ID_AP]; + t7xx_cldma_hif_hw_init(md_ctrl); + t7xx_cldma_stop(md_ctrl); + t7xx_cldma_switch_cfg(md_ctrl, CLDMA_DEDICATED_Q_CFG); + + port = &ctl->md->port_prox->ports[0]; + port->port_conf->ops->enable_chl(port); + + t7xx_cldma_start(md_ctrl); + + if (lk_event == LK_EVENT_CREATE_POST_DL_PORT) + t7xx_mode_update(md->t7xx_dev, T7XX_FASTBOOT_DOWNLOAD); + else + t7xx_mode_update(md->t7xx_dev, T7XX_FASTBOOT_DUMP); + break; + + default: + dev_err(dev, "Invalid LK event %d\n", lk_event); + break; + } +} + static int fsm_stopped_handler(struct t7xx_fsm_ctl *ctl) { ctl->curr_state = FSM_STATE_STOPPED; @@ -226,8 +282,9 @@ static void fsm_routine_stopped(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comman static void fsm_routine_stopping(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_command *cmd) { - struct t7xx_pci_dev *t7xx_dev; - struct cldma_ctrl *md_ctrl; + struct cldma_ctrl *md_ctrl = ctl->md->md_ctrl[CLDMA_ID_MD]; + struct t7xx_pci_dev *t7xx_dev = ctl->md->t7xx_dev; + enum t7xx_mode mode = READ_ONCE(t7xx_dev->mode); int err; if (ctl->curr_state == FSM_STATE_STOPPED || ctl->curr_state == FSM_STATE_STOPPING) { @@ -235,18 +292,20 @@ static void fsm_routine_stopping(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comma return; } - md_ctrl = ctl->md->md_ctrl[CLDMA_ID_MD]; - t7xx_dev = ctl->md->t7xx_dev; - ctl->curr_state = FSM_STATE_STOPPING; t7xx_fsm_broadcast_state(ctl, MD_STATE_WAITING_TO_STOP); t7xx_cldma_stop(md_ctrl); - if (!ctl->md->rgu_irq_asserted) { - t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DRM_DISABLE_AP); - /* Wait for the DRM disable to take effect */ - msleep(FSM_DRM_DISABLE_DELAY_MS); + if (mode == T7XX_FASTBOOT_SWITCHING) + t7xx_host_event_notify(ctl->md, FASTBOOT_DL_NOTIFY); + + t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DRM_DISABLE_AP); + /* Wait for the DRM disable to take effect */ + msleep(FSM_DRM_DISABLE_DELAY_MS); + if (mode == T7XX_FASTBOOT_SWITCHING) { + t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DEVICE_RESET); + } else { err = t7xx_acpi_fldr_func(t7xx_dev); if (err) t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DEVICE_RESET); @@ -272,6 +331,7 @@ static void fsm_routine_ready(struct t7xx_fsm_ctl *ctl) ctl->curr_state = FSM_STATE_READY; t7xx_fsm_broadcast_ready_state(ctl); + t7xx_mode_update(md->t7xx_dev, T7XX_READY); t7xx_md_event_notify(md, FSM_READY); } @@ -317,7 +377,8 @@ static int fsm_routine_starting(struct t7xx_fsm_ctl *ctl) static void fsm_routine_start(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_command *cmd) { struct t7xx_modem *md = ctl->md; - u32 dev_status; + struct device *dev; + u32 status; int ret; if (!md) @@ -329,23 +390,53 @@ static void fsm_routine_start(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_command return; } + dev = &md->t7xx_dev->pdev->dev; ctl->curr_state = FSM_STATE_PRE_START; t7xx_md_event_notify(md, FSM_PRE_START); - ret = read_poll_timeout(ioread32, dev_status, - (dev_status & MISC_STAGE_MASK) == LINUX_STAGE, 20000, 2000000, - false, IREG_BASE(md->t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS); + ret = wait_for_expected_dev_stage(status); + if (ret) { - struct device *dev = &md->t7xx_dev->pdev->dev; + dev_err(dev, "read poll timeout %d\n", ret); + goto finish_command; + } - fsm_finish_command(ctl, cmd, -ETIMEDOUT); - dev_err(dev, "Invalid device status 0x%lx\n", dev_status & MISC_STAGE_MASK); - return; + if (status != ctl->status || cmd->flag != 0) { + u32 stage = FIELD_GET(MISC_STAGE_MASK, status); + + switch (stage) { + case T7XX_DEV_STAGE_INIT: + case T7XX_DEV_STAGE_BROM_PRE: + case T7XX_DEV_STAGE_BROM_POST: + dev_dbg(dev, "BROM_STAGE Entered\n"); + ret = t7xx_fsm_append_cmd(ctl, FSM_CMD_START, 0); + break; + + case T7XX_DEV_STAGE_LK: + dev_dbg(dev, "LK_STAGE Entered\n"); + t7xx_lk_stage_event_handling(ctl, status); + break; + + case T7XX_DEV_STAGE_LINUX: + dev_dbg(dev, "LINUX_STAGE Entered\n"); + t7xx_mhccif_mask_clr(md->t7xx_dev, D2H_INT_PORT_ENUM | + D2H_INT_ASYNC_MD_HK | D2H_INT_ASYNC_AP_HK); + if (cmd->flag == 0) + break; + t7xx_cldma_hif_hw_init(md->md_ctrl[CLDMA_ID_AP]); + t7xx_cldma_hif_hw_init(md->md_ctrl[CLDMA_ID_MD]); + t7xx_port_proxy_set_cfg(md, PORT_CFG_ID_NORMAL); + ret = fsm_routine_starting(ctl); + break; + + default: + break; + } + ctl->status = status; } - t7xx_cldma_hif_hw_init(md->md_ctrl[CLDMA_ID_AP]); - t7xx_cldma_hif_hw_init(md->md_ctrl[CLDMA_ID_MD]); - fsm_finish_command(ctl, cmd, fsm_routine_starting(ctl)); +finish_command: + fsm_finish_command(ctl, cmd, ret); } static int fsm_main_thread(void *data) @@ -517,6 +608,7 @@ void t7xx_fsm_reset(struct t7xx_modem *md) fsm_flush_event_cmd_qs(ctl); ctl->curr_state = FSM_STATE_STOPPED; ctl->exp_flg = false; + ctl->status = T7XX_DEV_STAGE_INIT; } int t7xx_fsm_init(struct t7xx_modem *md) diff --git a/drivers/net/wwan/t7xx/t7xx_state_monitor.h b/drivers/net/wwan/t7xx/t7xx_state_monitor.h index b0b3662ae6d7..7b0a9baf488c 100644 --- a/drivers/net/wwan/t7xx/t7xx_state_monitor.h +++ b/drivers/net/wwan/t7xx/t7xx_state_monitor.h @@ -96,6 +96,7 @@ struct t7xx_fsm_ctl { bool exp_flg; spinlock_t notifier_lock; /* Protects notifier list */ struct list_head notifier_list; + u32 status; /* Device boot stage */ }; struct t7xx_fsm_event { diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c index 72e01e550a16..b0030f3ed0f0 100644 --- a/drivers/net/wwan/wwan_core.c +++ b/drivers/net/wwan/wwan_core.c @@ -328,6 +328,10 @@ static const struct { .name = "XMMRPC", .devsuf = "xmmrpc", }, + [WWAN_PORT_FASTBOOT] = { + .name = "FASTBOOT", + .devsuf = "fastboot", + }, }; static ssize_t type_show(struct device *dev, struct device_attribute *attr, @@ -916,7 +920,7 @@ static int wwan_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], return 0; } -static struct device_type wwan_type = { .name = "wwan" }; +static const struct device_type wwan_type = { .name = "wwan" }; static struct net_device *wwan_rtnl_alloc(struct nlattr *tb[], const char *ifname, diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index d7503aef599f..ef76850d9bcd 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -104,13 +104,12 @@ bool provides_xdp_headroom = true; module_param(provides_xdp_headroom, bool, 0644); static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, - u8 status); + s8 status); static void make_tx_response(struct xenvif_queue *queue, - struct xen_netif_tx_request *txp, + const struct xen_netif_tx_request *txp, unsigned int extra_count, - s8 st); -static void push_tx_responses(struct xenvif_queue *queue); + s8 status); static void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx); @@ -208,13 +207,9 @@ static void xenvif_tx_err(struct xenvif_queue *queue, unsigned int extra_count, RING_IDX end) { RING_IDX cons = queue->tx.req_cons; - unsigned long flags; do { - spin_lock_irqsave(&queue->response_lock, flags); make_tx_response(queue, txp, extra_count, XEN_NETIF_RSP_ERROR); - push_tx_responses(queue); - spin_unlock_irqrestore(&queue->response_lock, flags); if (cons == end) break; RING_COPY_REQUEST(&queue->tx, cons++, txp); @@ -465,12 +460,7 @@ static void xenvif_get_requests(struct xenvif_queue *queue, for (shinfo->nr_frags = 0; nr_slots > 0 && shinfo->nr_frags < MAX_SKB_FRAGS; nr_slots--) { if (unlikely(!txp->size)) { - unsigned long flags; - - spin_lock_irqsave(&queue->response_lock, flags); make_tx_response(queue, txp, 0, XEN_NETIF_RSP_OKAY); - push_tx_responses(queue); - spin_unlock_irqrestore(&queue->response_lock, flags); ++txp; continue; } @@ -496,14 +486,8 @@ static void xenvif_get_requests(struct xenvif_queue *queue, for (shinfo->nr_frags = 0; shinfo->nr_frags < nr_slots; ++txp) { if (unlikely(!txp->size)) { - unsigned long flags; - - spin_lock_irqsave(&queue->response_lock, flags); make_tx_response(queue, txp, 0, XEN_NETIF_RSP_OKAY); - push_tx_responses(queue); - spin_unlock_irqrestore(&queue->response_lock, - flags); continue; } @@ -995,7 +979,6 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, (ret == 0) ? XEN_NETIF_RSP_OKAY : XEN_NETIF_RSP_ERROR); - push_tx_responses(queue); continue; } @@ -1007,7 +990,6 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, make_tx_response(queue, &txreq, extra_count, XEN_NETIF_RSP_OKAY); - push_tx_responses(queue); continue; } @@ -1433,8 +1415,35 @@ int xenvif_tx_action(struct xenvif_queue *queue, int budget) return work_done; } +static void _make_tx_response(struct xenvif_queue *queue, + const struct xen_netif_tx_request *txp, + unsigned int extra_count, + s8 status) +{ + RING_IDX i = queue->tx.rsp_prod_pvt; + struct xen_netif_tx_response *resp; + + resp = RING_GET_RESPONSE(&queue->tx, i); + resp->id = txp->id; + resp->status = status; + + while (extra_count-- != 0) + RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL; + + queue->tx.rsp_prod_pvt = ++i; +} + +static void push_tx_responses(struct xenvif_queue *queue) +{ + int notify; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify); + if (notify) + notify_remote_via_irq(queue->tx_irq); +} + static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, - u8 status) + s8 status) { struct pending_tx_info *pending_tx_info; pending_ring_idx_t index; @@ -1444,8 +1453,8 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, spin_lock_irqsave(&queue->response_lock, flags); - make_tx_response(queue, &pending_tx_info->req, - pending_tx_info->extra_count, status); + _make_tx_response(queue, &pending_tx_info->req, + pending_tx_info->extra_count, status); /* Release the pending index before pusing the Tx response so * its available before a new Tx request is pushed by the @@ -1459,32 +1468,19 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, spin_unlock_irqrestore(&queue->response_lock, flags); } - static void make_tx_response(struct xenvif_queue *queue, - struct xen_netif_tx_request *txp, + const struct xen_netif_tx_request *txp, unsigned int extra_count, - s8 st) + s8 status) { - RING_IDX i = queue->tx.rsp_prod_pvt; - struct xen_netif_tx_response *resp; - - resp = RING_GET_RESPONSE(&queue->tx, i); - resp->id = txp->id; - resp->status = st; - - while (extra_count-- != 0) - RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL; + unsigned long flags; - queue->tx.rsp_prod_pvt = ++i; -} + spin_lock_irqsave(&queue->response_lock, flags); -static void push_tx_responses(struct xenvif_queue *queue) -{ - int notify; + _make_tx_response(queue, txp, extra_count, status); + push_tx_responses(queue); - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify); - if (notify) - notify_remote_via_irq(queue->tx_irq); + spin_unlock_irqrestore(&queue->response_lock, flags); } static void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx) @@ -1782,5 +1778,6 @@ static void __exit netback_fini(void) } module_exit(netback_fini); +MODULE_DESCRIPTION("Xen backend network device module"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("xen-backend:vif"); |