Merge "net: aquantia: support for 32-bit ARM target"

This commit is contained in:
qctecmdr 2020-10-03 04:38:39 -07:00 • committed by Gerrit - the friendly Code Review server
commit e35b5d5867
56 changed files with 27467 additions and 0 deletions

View file

@ -23,4 +23,12 @@ config AQTION
---help---
This enables the support for the aQuantia AQtion(tm) Ethernet card.
config AQFWD
tristate "aQuantia Forwarding driver"
depends on PCI && (X86_64 || ARM64 || ARM )
---help---
This enables the support for forwarding driver for the aQuantia AQtion(tm) Ethernet card.
source "drivers/net/ethernet/aquantia/atlantic-fwd/Kconfig"
endif # NET_VENDOR_AQUANTIA

View file

@ -4,3 +4,4 @@
#
obj-$(CONFIG_AQTION) += atlantic/
obj-$(CONFIG_AQFWD) += atlantic-fwd/

View file

@ -0,0 +1,283 @@
Supported hardware
------------------
AQC-107 / -108 / -109, revisions B0 / B1. FW versions 1.x / 2.x / 3.x
supported
Features / acceleration
-----------------------
- Message-signalled interrupts
- Hardware VLAN tag offloads
- Hardware checksum offloads:
- IPv4 header checksums
- UDP / TCP checksums with IPv4 and IPv6
- Hardware LSO for TCP v4/v6
- Hardware LRO for TCP v4/v6
- Coalescing TCP segments with TCP options not supported due to a
HW limitation
- Software GRO using the Linux NAPI GRO interface
- Hardware Rx header splitting
- RSS over up to 8 queues using MSI-X
- Linear skb receive mode
- N-tuple Rx filters
- Runtime power management
- Hardware MACSEC offload
Building and installation
-------------------------
1. Unpack the archive into a directory of your choice.
2. Change to that directory and compile the module:
$ make [KDIR=<path to kernel headers>]
Explicitly specifying KDIR is only necessary when the headers can't
be found automatically via the
/lib/modules/`uname -r`/{build,source} symlinks
3. Load the module
# insmod atlnew.ko [<module parameters>]
4. Unload the module
# rmmod atlnew
Device configuration
--------------------
# ip link set up dev <ifname>
# ip addr add <IP addr>/<CIDR prefix length> brd + dev <ifname>
Add a VLAN ID:
# ip link add link <ifname> name <ifname>.<VLAN id> type vlan id <VLAN id>
# ip link set up dev <ifname>.<VLAN id>
Remove a VLAN ID:
# ip link del dev <ifname>.<VLAN id>
Module parameters
-----------------
Module parameters may be specified at module load time on the insmod
command line or via modprobe.conf files. Some parameters marked as
[RW] below can also be changed at run time via files in
/sys/module/atlnew/parameters directory
- msi = < 1 | 0 > (default: 1) [RO]
Enable use of MSI interrupts.
- rx_refill_batch = <num> (default: 16) [RW]
Number of cleaned Rx ring descriptors to trigger Rx refill
- tx_clean_budget = <num> (default: 256) [RW]
Max number of processed Tx descriptors to clean in one pass of
NAPI poll
- max_queues = <num> (default: 8) [RO]
Max number of RSS queues. The actual number of queues will the
minimum of this parameter and the number of logical CPUs in
the system.
- rx_linear = <0 | 1> (default: 0)
Enables linear skb receive mode. Frames larger than
ATL_RX_BUF_SIZE defined in atl_ring.h will be dropped.
- min_intr_delay (default: 10) [RW]
When an interrupt is about to be asserted on an LRO-enabled
queue, LRO requires some lead time to close and flush
in-progress flows. This parameter sets the LRO interrupt delay
and provides the lower boundary for interrupt coalescing
delays settable via ethtool. When modified, the new value
takes effect on interface down/up cycle or on setting an
interrupt coalescing delay via ethtool.
- rx_mod_hyst (default: 10) [RW]
- tx_mod_hyst (default: 10) [RW]
Each ring has two interrupt coalescing timers. When an
interrupt-causing event happens, they're initialized with
values called minimum and maximum respectively. The former
timer is restarted each time another such event happens, while
the latter keeps running. When either timer reaches zero, the
interrupt is reported to the host. The minimum value is set
via ethtool. These module parameters set the difference
between the maximum and minimum values for Rx and Tx
paths. When modified, the new max values go into effect upon
interface down/up cycle or upon modifying ethtool interrupt
coalescing settings.
- keep_link = < 1 | 0 > (default: 0) [RO]
When set to 1, the ethernet link is established at PCI probe
time, and is not dropped on ifdown. This allows the rings
allocated to forwarding engines to handle traffic even when
linux network interface is down.
- sleep_delay = (default: 10000) [RW]
Delay in ms after link goes down to suspend the device if runtime power
management is turned on.
- macsec_bridge = < 1 | 0 > (default: 1) [RO]
When set to 0, several SecY can be created, macsec will filter by source mac
address to apply apropriate SecY.
By default, only 1 SecY can be created, macsec will handle all packets.
- rx/tx_ring_size = <8..8184> (default: 4096) [RO]
Default size for the rings.
VLAN filters
------------
Sixteen VLAN filters are programmable via the ethtool -N interface using
locations 0 through 15. The only flow-type supported is ether and only
VLAN id can be matched.
For VLAN filters only, it's possible not to specify an explicit
filter location. In this case an appropriate location will be assigned
automatically.
These filters are also used for HW filter acceleration for Linux VLAN
subdevices. Filters automatically added by Linux VLAN code can be
overridden using ethtool subject to restrictions described below:
* A special ring number 32 is used in ethtool interface to signify
'any ring'. An ethtool filter rule with ring set to 32 accepts a
packet leaving the ring to be determined using the standard RSS
mechanism. VLAN code adds rules in this manner. Such a rule can be
modified using ethool to set a specific destination ring.
* There can't be two filters with the same VID. An attempt to add a
duplicate filter using an explicit location different from the
location of existing filter with the same VID will fail. When the
location is not specified, the existing filter will be modified.
* An attempt to change the VID of a filter added by VLAN code using an
explicit location will fail. Either use a different explicit
location or no location.
* An attempt to delete a rule created by VLAN code will return
success, but the rule will remain in place. If a specific ring was
set in the rule, it will be reset to 'any ring'.
* If a VLAN sub-device is added when there're no unused filters, VLAN
promiscuous mode will be enabled. This will result in all VIDs being
accepted. Filters can still be used to direct specific VIDs to
specific rings.
* Even when all 16 filters are used, attempting to add a rule with a
new VID via ethtool will succeed as long as at least one existing
filter was set by VLAN code and still has 'any ring' as
destination. This filter will be re-used for the new rule and VLAN
promiscuous mode will be enabled.
* When a filter becomes available as a result of a deletion of a VLAN
subdevice or a deletion of an ethtool filter rule, and a VLAN
promiscuous mode is enabled, the vacated filter will be re-used for
another enabled VLAN subdevice. This may result in VLAN promiscuous
mode being turned off if that VLAN subdevice was the last one with
no filter allocated.
Ethertype filters
-----------------
Fifteen ethertype filters are programmable via the ethtool -N
interface using locations 16 through 30. The only flow-type supported
is ether and only the ethertype field can be matched.
N-tuple filters
---------------
Eight IPv4 n-tuple filters are supported, programmable using ethtool
-N command with filter locations 32 through 39.
Fields that can be matched are source / destination IP addresses, and,
for TCP / UDP / SCTP protocols, additionally source / destination
ports can be matched. Partial (masked) matches are not supported, a
mask for each field must consist of either all ones or all zeroes.
Each aligned group of four filters (locations 32 through 35 or 36
through 39) can alternatively be used as one IPv6 filter, using filter
locations 32 or 36.
Testing forwarding performance
------------------------------
Since this is a design for a router, the best way to test would be
between our aquantia port and another high speed port on the same
DUT. However, in the situation where the Aquantia interface is the
only high speed one, we have devised the following testing strategy
to emulate these two ports on same DUT.
One of the simplest ways is to set up two VLAN interfaces and forward
traffic between them as in the following example:
Set up two VLANs with examlple ids 42 and 43 on the Aquantia
interface, assign the switch port connected to it to both of those
VLANs.
# ip link set up dev <iface>
# ip link add link <iface> name <iface>.42 type vlan id 42
# ip link add link <iface> name <iface>.43 type vlan id 43
# ip link set up dev <iface>.42
# ip link set up dev <iface>.43
# ip addr add 10.42.42.1/24 brd + dev <iface>.42
# ip addr add 10.42.43.1/24 brd + dev <iface>.43
# sysctl net.ipv4.ip_forward=1
This sets up two VLAN interfaces with VLAN ids 42 and 43 and enables
IP routing. Two hosts should be set up to generate and terminate the
traffic:
On a host on the 42 VLAN with IP 10.42.42.xx run
$ iperf3 -s
On a host on the 43 VLAN run
$ iperf3 -c 10.42.42.xx
Another possibility is to use a single host running Linux as a link
partner both generating and terminating the traffic. Two VLAN
sub-interfaces should be configured on a high-speed port on that host,
belonging to VLAN 42 / IP network 10.42.42/24 and VLAN 43 / IP network
10.42.43/24 respectively.
Since, by default, traffic addressed to one of the local IPs is
received locally, a special configuration of Linux policy routing is
required to make such traffic actually go out on the wire to the DUT,
as described below, where <lp_iface> stands for the chosen network interface:
# ip rule add table local prio 100
# ip rule del table local prio 0
# ip rule add iif lo table 42 prio 42
# ip link set up dev <lp_iface>
# ip link add link <lp_iface> name <lp_iface>.42 type vlan id 42
# ip link add link <lp_iface> name <lp_iface>.43 type vlan id 43
# ip link set up dev <lp_iface>.42
# ip link set up dev <lp_iface>.43
# ip addr add 10.42.42.10/24 brd + dev <lp_iface>.42
# ip addr add 10.42.43.10/24 brd + dev <lp_iface>.43
# ip route add 10.42.42.10 via 10.42.43.1 table 42
# ip route add 10.42.43.10 via 10.42.42.1 table 42
This routes the locally-originated traffic, that is addressed to the local IP
of one of the VLAN interfaces, out via the other VLAN interface, instead of
being immediately looped back locally.
$ iperf3 -s
$ iperf3 -c 10.42.42.10
The following command can be used to restore the default routing
behavior:
# ip route flush table 42

View file

@ -0,0 +1,52 @@
#
# Aquantia atlantic-forwarding driver config
#
if AQFWD
config ATLFWD_FWD
bool "Enable forwarding-engine API"
default n
help
Say Y to enable the forwarding-engine API
config ATLFWD_FWD_RXBUF
int
prompt "Rx buffer space for forwarding engine's rings" if ATLFWD_FWD
range 0 320
default 160 if ATLFWD_FWD
default 0
help
Amount of Rx buffer to reserve for the forwarding-engine
rings. This sets the default value of the fwd_rx_buf_reserve
module option.
Value in kiB, 0 to 320, defaults to 160 if forwarding-engine
API enabled, 0 otherwise.
config ATLFWD_FWD_TXBUF
int
prompt "Tx buffer space for forwarding engine's rings" if ATLFWD_FWD
range 0 160
default 80 if ATLFWD_FWD
default 0
help
Amount of Tx buffer to reserve for the forwarding-engine
rings. This sets the default value of the fwd_tx_buf_reserve
module option.
Value in kiB, 0 to 320, defaults to 80 if forwarding-engine
API enabled, 0 otherwise.
config ATLFWD_FWD_NETLINK
bool "Enable netlink control socket for forwarding engine"
default n
depends on ATLFWD_FWD
help
Expose forwarding engine APIs over the netlink socket.
endif

View file

@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0-only
# Atlantic Network Driver
#
# Copyright(c) 2014-2019 aQuantia Corporation.
# Copyright(c) 2019-2020 Marvell International Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
obj-$(CONFIG_AQFWD) += atlantic-fwd.o
atlantic-fwd-objs := atl_fw.o \
atl2_fw.o \
atl_hw.o \
atl_main.o \
atl_ring.o \
atl_ethtool.o \
atl_trace.o \
atl_compat.o \
atl_hwmon.o \
atl_ptp.o \
atl_hw_ptp.o
atlantic-fwd-$(CONFIG_ATLFWD_FWD) += atl_fwd.o
atlantic-fwd-$(CONFIG_ATLFWD_FWD_NETLINK) += atl_fwdnl.o \
atl_fwdnl_params.o
atlantic-fwd-$(CONFIG_MACSEC) += atl_macsec.o macsec/macsec_api.o
ccflags-y += -I$(src)

View file

@ -0,0 +1,968 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/etherdevice.h>
#include "atl_common.h"
#include "atl_hw.h"
#include "atl2_fw.h"
#include "atl_fw.h"
#define ATL2_FW_READ_TRY_MAX 1000
#define atl2_shared_buffer_write(HW, ITEM, VARIABLE) \
atl2_mif_shared_buf_write(HW,\
(offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\
(u32 *)&VARIABLE, sizeof(VARIABLE) / sizeof(u32))
#define atl2_shared_buffer_get(HW, ITEM, VARIABLE) \
atl2_mif_shared_buf_get(HW, \
(offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\
(u32 *)&VARIABLE, \
sizeof(VARIABLE) / sizeof(u32))
/* This should never be used on non atomic fields,
* treat any > u32 read as non atomic.
*/
#define atl2_shared_buffer_read(HW, ITEM, VARIABLE) \
{\
BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \
sizeof(u32)) != 0,\
"Non aligned read " # ITEM);\
BUILD_BUG_ON_MSG(sizeof(VARIABLE) > sizeof(u32),\
"Non atomic read " # ITEM);\
atl2_mif_shared_buf_read(HW, \
(offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\
(u32 *)&VARIABLE, \
sizeof(VARIABLE) / sizeof(u32));\
}
static void atl2_mif_shared_buf_get(struct atl_hw *hw, int offset,
u32 *data, int len)
{
int i;
for (i = 0; i < len; i++)
data[i] = atl_read(hw, ATL2_MIF_SHARED_BUFFER_IN(offset + i));
}
static void atl2_mif_shared_boot_buf_write(struct atl_hw *hw, int offset,
u32 *data, int len)
{
int i;
for (i = 0; i < len / sizeof(u32); i++)
atl_write(hw, ATL2_MIF_SHARED_BUFFER_BOOT(i), data[i]);
}
static void atl2_mif_shared_buf_write(struct atl_hw *hw, int offset,
u32 *data, int len)
{
int i;
for (i = 0; i < len; i++)
atl_write(hw, ATL2_MIF_SHARED_BUFFER_IN(offset + i),
data[i]);
}
static void atl2_mif_shared_buf_read(struct atl_hw *hw, int offset,
u32 *data, int len)
{
int i;
for (i = 0; i < len; i++)
data[i] = atl_read(hw, ATL2_MIF_SHARED_BUFFER_OUT(offset + i));
}
static void atl2_mif_host_finished_write_set(struct atl_hw *hw, u32 finish)
{
atl_write_bits(hw, ATL2_MIF_HOST_FINISHED_WRITE, 0, 1, finish);
}
static u32 atl2_mif_mcp_finished_read_get(struct atl_hw *hw)
{
return atl_read(hw, ATL2_MIF_MCP_FINISHED_READ) & 1;
}
#define atl2_shared_buffer_read_safe(HW, ITEM, DATA) \
_atl2_shared_buffer_read_safe((HW), \
(offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\
sizeof(((struct fw_interface_out *)0)->ITEM) / sizeof(u32),\
(DATA))
/* There are two 16bit transaction counters. The one is incremented at start of
* changing non atomic value, the other one - at the end. So we need to wait for
* ma moment when they are equal each other at the reading non atomic memory and
* at the end of read.
*/
static int _atl2_shared_buffer_read_safe(struct atl_hw *hw,
uint32_t offset, uint32_t dwords,
void *data)
{
struct transaction_counter_s tid1, tid2;
int cnt = 0;
do {
do {
atl2_shared_buffer_read(hw, transaction_id, tid1);
cnt++;
if (cnt > ATL2_FW_READ_TRY_MAX)
return -ETIME;
if (tid1.transaction_cnt_a != tid1.transaction_cnt_b)
udelay(1);
} while (tid1.transaction_cnt_a != tid1.transaction_cnt_b);
atl2_mif_shared_buf_read(hw, offset, (u32 *)data, dwords);
atl2_shared_buffer_read(hw, transaction_id, tid2);
cnt++;
//TODO: no much sense to handle these errors
// therefore put WARN here to make it visible
if (cnt > ATL2_FW_READ_TRY_MAX)
return -ETIME;
} while (tid2.transaction_cnt_a != tid2.transaction_cnt_b ||
tid1.transaction_cnt_a != tid2.transaction_cnt_a);
return 0;
}
static inline int atl2_shared_buffer_finish_ack(struct atl_hw *hw)
{
u32 val;
int err = 0;
atl2_mif_host_finished_write_set(hw, 1U);
busy_wait(1000, udelay(100), val,
atl2_mif_mcp_finished_read_get(hw),
val != 0);
if (val != 0)
err = -ETIME;
WARN(err, "atl2_shared_buffer_finish_ack");
return err;
}
static int atl2_fw_get_filter_caps(struct atl_hw *hw)
{
struct atl_nic *nic = container_of(hw, struct atl_nic, hw);
struct filter_caps_s filter_caps;
u32 tag_top;
int err;
err = atl2_shared_buffer_read_safe(hw, filter_caps, &filter_caps);
if (err)
return err;
hw->art_base_index = filter_caps.rslv_tbl_base_index * 8;
hw->art_available = filter_caps.rslv_tbl_count * 8;
if (hw->art_available == 0)
hw->art_available = 128;
nic->rxf_flex.available = 1;
nic->rxf_flex.base_index = filter_caps.flexible_filter_mask >> 1;
nic->rxf_mac.base_index = filter_caps.l2_filters_base_index;
nic->rxf_mac.available = filter_caps.l2_filter_count;
nic->rxf_etype.base_index = filter_caps.ethertype_filter_base_index;
nic->rxf_etype.available = filter_caps.ethertype_filter_count;
nic->rxf_etype.tag_top =
(nic->rxf_etype.available >= ATL2_RPF_ETYPE_TAGS) ?
(ATL2_RPF_ETYPE_TAGS) : (ATL2_RPF_ETYPE_TAGS >> 1);
nic->rxf_vlan.base_index = filter_caps.vlan_filter_base_index;
/* 0 - no tag, 1 - reserved for vlan-filter-offload filters */
tag_top = (filter_caps.vlan_filter_count == ATL_VLAN_FLT_NUM) ?
(ATL_VLAN_FLT_NUM - 2) :
(ATL_VLAN_FLT_NUM / 2 - 2);
nic->rxf_vlan.available = min_t(u32, filter_caps.vlan_filter_count - 2,
tag_top);
nic->rxf_ntuple.l3_v4_base_index = filter_caps.l3_ip4_filter_base_index;
nic->rxf_ntuple.l3_v4_available = min_t(u32,
filter_caps.l3_ip4_filter_count,
ATL_NTUPLE_FLT_NUM - 1);
nic->rxf_ntuple.l3_v6_base_index = filter_caps.l3_ip6_filter_base_index;
nic->rxf_ntuple.l3_v6_available = filter_caps.l3_ip6_filter_count;
nic->rxf_ntuple.l4_base_index = filter_caps.l4_filter_base_index;
nic->rxf_ntuple.l4_available = min_t(u32, filter_caps.l4_filter_count,
ATL_NTUPLE_FLT_NUM - 1);
return 0;
}
static int __atl2_fw_wait_init(struct atl_hw *hw)
{
struct request_policy_s request_policy;
struct link_control_s link_control;
uint32_t mtu;
int err;
BUILD_BUG_ON_MSG(sizeof(struct link_options_s) != 0x4,
"linkOptions invalid size");
BUILD_BUG_ON_MSG(sizeof(struct thermal_shutdown_s) != 0x4,
"thermalShutdown invalid size");
BUILD_BUG_ON_MSG(sizeof(struct sleep_proxy_s) != 0x958,
"sleepProxy invalid size");
BUILD_BUG_ON_MSG(sizeof(struct pause_quanta_s) != 0x18,
"pauseQuanta invalid size");
BUILD_BUG_ON_MSG(sizeof(struct cable_diag_control_s) != 0x4,
"cableDiagControl invalid size");
BUILD_BUG_ON_MSG(sizeof(struct statistics_s) != 0x70,
"statistics_s invalid size");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in, mtu) != 0,
"mtu invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in, mac_address) != 0x8,
"macAddress invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in,
link_control) != 0x10,
"linkControl invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in,
link_options) != 0x18,
"linkOptions invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in,
thermal_shutdown) != 0x20,
"thermalShutdown invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in, sleep_proxy) != 0x28,
"sleepProxy invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in,
pause_quanta) != 0x984,
"pauseQuanta invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_in,
cable_diag_control) != 0xA44,
"cableDiagControl invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out, version) != 0x04,
"version invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out, link_status) != 0x14,
"linkStatus invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
wol_status) != 0x18,
"wolStatus invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
mac_health_monitor) != 0x610,
"macHealthMonitor invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
phy_health_monitor) != 0x620,
"phyHealthMonitor invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
cable_diag_status) != 0x630,
"cableDiagStatus invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
device_link_caps) != 0x648,
"deviceLinkCaps invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
sleep_proxy_caps) != 0x650,
"sleepProxyCaps invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
lkp_link_caps) != 0x660,
"lkpLinkCaps invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out, core_dump) != 0x668,
"coreDump invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out, stats) != 0x700,
"stats invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out, filter_caps) != 0x774,
"filter_caps invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out,
management_status) != 0x78c,
"management_status invalid offset");
BUILD_BUG_ON_MSG(offsetof(struct fw_interface_out, trace) != 0x800,
"trace invalid offset");
err = atl2_fw_get_filter_caps(hw);
if (err)
return err;
atl2_shared_buffer_get(hw, link_control, link_control);
link_control.mode = ATL2_HOST_MODE_ACTIVE;
atl2_shared_buffer_write(hw, link_control, link_control);
atl2_shared_buffer_get(hw, mtu, mtu);
mtu = ATL_MAX_MTU + ETH_FCS_LEN + ETH_HLEN;
atl2_shared_buffer_write(hw, mtu, mtu);
atl2_shared_buffer_get(hw, request_policy, request_policy);
request_policy.bcast.accept = 1;
request_policy.bcast.queue_or_tc = 1;
request_policy.bcast.rx_queue_tc_index = 0;
request_policy.mcast.accept = 1;
request_policy.mcast.queue_or_tc = 1;
request_policy.mcast.rx_queue_tc_index = 0;
request_policy.promisc.queue_or_tc = 1;
request_policy.promisc.rx_queue_tc_index = 0;
atl2_shared_buffer_write(hw, request_policy, request_policy);
return atl2_shared_buffer_finish_ack(hw);
}
int atl2_fw_set_filter_policy(struct atl_hw *hw, bool promisc, bool allmulti)
{
struct request_policy_s request_policy;
atl2_shared_buffer_get(hw, request_policy, request_policy);
request_policy.promisc.all = promisc;
request_policy.mcast.promisc = allmulti;
atl2_shared_buffer_write(hw, request_policy, request_policy);
return atl2_shared_buffer_finish_ack(hw);
}
static int atl2_fw_deinit(struct atl_hw *hw)
{
struct link_control_s link_control;
int err = 0;
atl2_shared_buffer_get(hw, link_control, link_control);
link_control.mode = ATL2_HOST_MODE_SHUTDOWN;
atl2_shared_buffer_write(hw, link_control, link_control);
err = atl2_shared_buffer_finish_ack(hw);
return err;
}
static inline unsigned int atl_link_adv(struct atl_link_state *lstate)
{
struct atl_hw *hw = container_of(lstate, struct atl_hw, link_state);
if (lstate->thermal_throttled
&& hw->thermal.flags & atl_thermal_throttle)
/* FW doesn't provide raw LP's advertized rates, only
* the rates adverized both by us and LP. Here we
* advertize not just the throttled_to rate, but also
* all the lower rates as well. That way if LP changes
* or dynamically starts to adverize a lower rate than
* throttled_to, we will notice that in
* atl_thermal_check() and switch to that lower
* rate there.
*/
return (lstate->advertized & ATL_EEE_MASK) |
(BIT(lstate->throttled_to + 1) - 1);
return lstate->advertized;
}
static bool atl2_fw_set_link_needed(struct atl_link_state *lstate)
{
struct atl_fc_state *fc = &lstate->fc;
bool ret = false;
if (fc->req != fc->prev_req)
ret = true;
if (atl_link_adv(lstate) != lstate->prev_advertized)
ret = true;
return ret;
}
static void atl2_set_rate(struct atl_hw *hw,
struct link_options_s *link_options)
{
unsigned int adv = atl_link_adv(&hw->link_state);
link_options->rate_10M_hd = !!(adv & BIT(atl_link_type_idx_10m_half));
link_options->rate_100M_hd = !!(adv & BIT(atl_link_type_idx_100m_half));
link_options->rate_1G_hd = !!(adv & BIT(atl_link_type_idx_1g_half));
link_options->rate_10M = !!(adv & BIT(atl_link_type_idx_10m));
link_options->rate_100M = !!(adv & BIT(atl_link_type_idx_100m));
link_options->rate_1G = !!(adv & BIT(atl_link_type_idx_1g));
link_options->rate_2P5G = !!(adv & BIT(atl_link_type_idx_2p5g));
link_options->rate_5G = !!(adv & BIT(atl_link_type_idx_5g));
link_options->rate_10G = !!(adv & BIT(atl_link_type_idx_10g));
link_options->rate_N5G = link_options->rate_5G;
link_options->rate_N2P5G = link_options->rate_2P5G;
}
static void atl2_set_eee(struct atl_link_state *lstate,
struct link_options_s *link_options, bool eee_enabled)
{
uint32_t eee_advertized = lstate->advertized >> ATL_EEE_BIT_OFFT;
if (!eee_enabled) {
link_options->eee_100M = 0;
link_options->eee_1G = 0;
link_options->eee_2P5G = 0;
link_options->eee_5G = 0;
link_options->eee_10G = 0;
return;
}
link_options->eee_100M = !!(eee_advertized & BIT(atl_link_type_idx_100m));
link_options->eee_1G = !!(eee_advertized & BIT(atl_link_type_idx_1g));
link_options->eee_2P5G = !!(eee_advertized & BIT(atl_link_type_idx_2p5g));
link_options->eee_5G = !!(eee_advertized & BIT(atl_link_type_idx_5g));
link_options->eee_10G = !!(eee_advertized & BIT(atl_link_type_idx_10g));
}
/* fw lock must be held */
static void __atl2_fw_set_link(struct atl_hw *hw)
{
struct atl_link_state *lstate = &hw->link_state;
struct link_options_s link_options;
int err = 0;
atl2_shared_buffer_get(hw, link_options, link_options);
link_options.pause_rx = link_options.pause_tx = 0;
if (lstate->fc.req & atl_fc_rx) {
link_options.pause_rx = 1;
link_options.pause_tx = 1;
}
if (lstate->fc.req & atl_fc_tx)
link_options.pause_tx ^= 1;
link_options.link_up = 1;
if (lstate->force_off)
link_options.link_up = 0;
atl2_set_rate(hw, &link_options);
atl2_set_eee(lstate, &link_options, lstate->eee_enabled);
atl2_shared_buffer_write(hw, link_options, link_options);
err = atl2_shared_buffer_finish_ack(hw);
lstate->prev_advertized = atl_link_adv(lstate);
lstate->fc.prev_req = lstate->fc.req;
}
static void atl2_fw_set_link(struct atl_hw *hw, bool force)
{
if (!force && !atl2_fw_set_link_needed(&hw->link_state))
return;
atl_lock_fw(hw);
__atl2_fw_set_link(hw);
atl_unlock_fw(hw);
}
static u32 a2_fw_caps_to_mask(struct device_link_caps_s *link_caps)
{
u32 supported = 0;
if (link_caps->rate_10G)
supported |= BIT(atl_link_type_idx_10g);
if (link_caps->rate_5G)
supported |= BIT(atl_link_type_idx_5g);
if (link_caps->rate_2P5G)
supported |= BIT(atl_link_type_idx_2p5g);
if (link_caps->rate_1G)
supported |= BIT(atl_link_type_idx_1g);
if (link_caps->rate_100M)
supported |= BIT(atl_link_type_idx_100m);
if (link_caps->rate_10M)
supported |= BIT(atl_link_type_idx_10m);
if (link_caps->rate_1G_hd)
supported |= BIT(atl_link_type_idx_1g_half);
if (link_caps->rate_100M_hd)
supported |= BIT(atl_link_type_idx_100m_half);
if (link_caps->rate_10M_hd)
supported |= BIT(atl_link_type_idx_10m_half);
if (link_caps->eee_10G)
supported |= BIT(atl_link_type_idx_10g) << ATL_EEE_BIT_OFFT;
if (link_caps->eee_5G)
supported |= BIT(atl_link_type_idx_5g) << ATL_EEE_BIT_OFFT;
if (link_caps->eee_2P5G)
supported |= BIT(atl_link_type_idx_2p5g) << ATL_EEE_BIT_OFFT;
if (link_caps->eee_1G)
supported |= BIT(atl_link_type_idx_1g) << ATL_EEE_BIT_OFFT;
if (link_caps->eee_100M)
supported |= BIT(atl_link_type_idx_100m) << ATL_EEE_BIT_OFFT;
return supported;
}
static u32 a2_fw_lkp_to_mask(struct lkp_link_caps_s *lkp_link_caps)
{
u32 rate = 0;
if (lkp_link_caps->rate_10G)
rate |= BIT(atl_link_type_idx_10g);
if (lkp_link_caps->rate_5G)
rate |= BIT(atl_link_type_idx_5g);
if (lkp_link_caps->rate_2P5G)
rate |= BIT(atl_link_type_idx_2p5g);
if (lkp_link_caps->rate_1G)
rate |= BIT(atl_link_type_idx_1g);
if (lkp_link_caps->rate_100M)
rate |= BIT(atl_link_type_idx_100m);
if (lkp_link_caps->rate_10M)
rate |= BIT(atl_link_type_idx_10m);
if (lkp_link_caps->rate_1G_hd)
rate |= BIT(atl_link_type_idx_1g_half);
if (lkp_link_caps->rate_100M_hd)
rate |= BIT(atl_link_type_idx_100m_half);
if (lkp_link_caps->rate_10M_hd)
rate |= BIT(atl_link_type_idx_10m_half);
if (lkp_link_caps->eee_10G)
rate |= BIT(atl_link_type_idx_10g) << ATL_EEE_BIT_OFFT;
if (lkp_link_caps->eee_5G)
rate |= BIT(atl_link_type_idx_5g) << ATL_EEE_BIT_OFFT;
if (lkp_link_caps->eee_2P5G)
rate |= BIT(atl_link_type_idx_2p5g) << ATL_EEE_BIT_OFFT;
if (lkp_link_caps->eee_1G)
rate |= BIT(atl_link_type_idx_1g) << ATL_EEE_BIT_OFFT;
if (lkp_link_caps->eee_100M)
rate |= BIT(atl_link_type_idx_100m) << ATL_EEE_BIT_OFFT;
return rate;
}
static int __atl2_fw_update_link_status(struct atl_hw *hw)
{
struct atl_link_state *lstate = &hw->link_state;
struct link_status_s link_status;
struct lkp_link_caps_s lkp_link_caps;
enum atl_fc_mode fc = atl_fc_none;
atl2_shared_buffer_read(hw, link_status, link_status);
switch (link_status.link_rate) {
case ATL2_FW_LINK_RATE_10G:
lstate->link = &atl_link_types[atl_link_type_idx_10g];
break;
case ATL2_FW_LINK_RATE_5G:
lstate->link = &atl_link_types[atl_link_type_idx_5g];
break;
case ATL2_FW_LINK_RATE_2G5:
lstate->link = &atl_link_types[atl_link_type_idx_2p5g];
break;
case ATL2_FW_LINK_RATE_1G:
lstate->link = link_status.duplex ?
&atl_link_types[atl_link_type_idx_1g] :
&atl_link_types[atl_link_type_idx_1g_half];
break;
case ATL2_FW_LINK_RATE_100M:
lstate->link = link_status.duplex ?
&atl_link_types[atl_link_type_idx_100m] :
&atl_link_types[atl_link_type_idx_100m_half];
break;
case ATL2_FW_LINK_RATE_10M:
lstate->link = link_status.duplex ?
&atl_link_types[atl_link_type_idx_10m] :
&atl_link_types[atl_link_type_idx_10m_half];
break;
default:
lstate->link = NULL;
}
if (link_status.pause_rx)
fc |= atl_fc_rx;
if (link_status.pause_tx)
fc |= atl_fc_tx;
lstate->fc.cur = fc;
lstate->eee = link_status.eee;
atl2_shared_buffer_read(hw, lkp_link_caps, lkp_link_caps);
lstate->lp_advertized = a2_fw_lkp_to_mask(&lkp_link_caps);
return 0;
}
static struct atl_link_type *atl2_fw_check_link(struct atl_hw *hw)
{
struct atl_link_type *link;
struct atl_link_state *lstate = &hw->link_state;
struct phy_health_monitor_s phy_health_monitor;
int ret = 0;
memset(&phy_health_monitor, 0, sizeof(phy_health_monitor));
atl_lock_fw(hw);
__atl2_fw_update_link_status(hw);
ret = atl2_shared_buffer_read_safe(hw, phy_health_monitor,
&phy_health_monitor);
atl_unlock_fw(hw);
atl_thermal_check(hw, phy_health_monitor.phy_hot_warning);
/* Thermal check might have reset link due to throttling */
link = lstate->link;
return link;
}
/* fw lock must be held */
static int __atl2_fw_get_link_caps(struct atl_hw *hw)
{
struct device_link_caps_s device_link_caps;
struct atl_mcp *mcp = &hw->mcp;
unsigned int supported = 0;
int ret = 0;
atl2_shared_buffer_read(hw, device_link_caps, device_link_caps);
mcp->wdog_disabled = true;
supported = a2_fw_caps_to_mask(&device_link_caps);
hw->link_state.supported = supported;
hw->link_state.lp_lowest = fls(supported) - 1;
mcp->caps_low = atl_fw2_wake_on_link_force;
return ret;
}
static int atl2_fw_restart_aneg(struct atl_hw *hw)
{
struct link_options_s link_options;
int ret = 0;
atl2_shared_buffer_get(hw, link_options, link_options);
link_options.link_renegotiate = 1;
atl2_shared_buffer_write(hw, link_options, link_options);
ret = atl2_shared_buffer_finish_ack(hw);
link_options.link_renegotiate = 0;
atl2_shared_buffer_write(hw, link_options, link_options);
return ret;
}
static void atl2_fw_set_default_link(struct atl_hw *hw)
{
struct atl_link_state *lstate = &hw->link_state;
lstate->autoneg = true;
lstate->advertized = hw->link_state.supported;
lstate->fc.req = atl_fc_full;
lstate->eee_enabled = 0;
lstate->advertized &= ~ATL_EEE_MASK;
}
static int atl2_fw_get_phy_temperature(struct atl_hw *hw, int *temp)
{
struct phy_health_monitor_s phy_health_monitor;
int ret = 0;
atl_lock_fw(hw);
ret = atl2_shared_buffer_read_safe(hw, phy_health_monitor,
&phy_health_monitor);
*temp = (int8_t)(phy_health_monitor.phy_temperature) * 1000;
atl_unlock_fw(hw);
return ret;
}
static int atl2_fw_get_mac_addr(struct atl_hw *hw, uint8_t *mac)
{
struct mac_address_s mac_address;
int err = 0;
atl2_shared_buffer_get(hw, mac_address, mac_address);
ether_addr_copy(mac, (u8 *)mac_address.mac_address);
return err;
}
/* fw lock must be held */
static int __atl2_fw_get_hbeat(struct atl_hw *hw, uint16_t *hbeat)
{
struct phy_health_monitor_s phy_health_monitor;
int ret = 0;
ret = atl2_shared_buffer_read_safe(hw, phy_health_monitor,
&phy_health_monitor);
*hbeat = phy_health_monitor.phy_heart_beat;
return ret;
}
static int atl2_fw_set_phy_loopback(struct atl_nic *nic, u32 mode)
{
struct device_link_caps_s device_link_caps;
bool on = !!(nic->priv_flags & BIT(mode));
struct atl_hw *hw = &nic->hw;
struct link_options_s link_options;
int ret = 0;
atl_lock_fw(hw);
atl2_shared_buffer_read(hw, device_link_caps, device_link_caps);
atl2_shared_buffer_get(hw, link_options, link_options);
switch (mode) {
case ATL_PF_LPB_INT_PHY:
if (!device_link_caps.internal_loopback) {
ret = -EOPNOTSUPP;
goto unlock;
}
link_options.internal_loopback = on;
break;
case ATL_PF_LPB_EXT_PHY:
if (!device_link_caps.external_loopback) {
ret = -EOPNOTSUPP;
goto unlock;
}
link_options.external_loopback = on;
break;
}
atl2_shared_buffer_write(hw, link_options, link_options);
ret = atl2_shared_buffer_finish_ack(hw);
unlock:
atl_unlock_fw(hw);
return ret;
}
static int atl2_fw_enable_wol(struct atl_hw *hw, unsigned int wol_mode)
{
struct link_options_s link_options;
struct link_control_s link_control;
struct wake_on_lan_s wake_on_lan;
struct mac_address_s mac_address;
int ret = 0;
atl_lock_fw(hw);
atl2_shared_buffer_get(hw, sleep_proxy, wake_on_lan);
if (wol_mode & atl_fw_wake_on_link) {
wake_on_lan.wake_on_link_up = 1;
if (test_bit(ATL_ST_UP, &hw->state))
wake_on_lan.restore_link_before_wake = 1;
}
if (wol_mode & atl_fw_wake_on_link_rtpm) {
wake_on_lan.wake_on_link_up = 1;
}
if (wol_mode & atl_fw_wake_on_magic) {
wake_on_lan.wake_on_magic_packet = 1;
if (test_bit(ATL_ST_UP, &hw->state))
wake_on_lan.restore_link_before_wake = 1;
}
ether_addr_copy(mac_address.mac_address, hw->mac_addr);
atl2_shared_buffer_write(hw, mac_address, mac_address);
atl2_shared_buffer_write(hw, sleep_proxy, wake_on_lan);
atl2_shared_buffer_get(hw, link_control, link_control);
link_control.mode = ATL2_HOST_MODE_SLEEP_PROXY;
atl2_shared_buffer_write(hw, link_control, link_control);
atl2_shared_buffer_get(hw, link_options, link_options);
link_options.link_up = 1;
atl2_shared_buffer_write(hw, link_options, link_options);
ret = atl2_shared_buffer_finish_ack(hw);
atl_unlock_fw(hw);
return ret;
}
static int atl2_fw_update_thermal(struct atl_hw *hw)
{
bool enable = !!(hw->thermal.flags & atl_thermal_monitor);
struct phy_health_monitor_s phy_health_monitor;
struct thermal_shutdown_s thermal_shutdown;
int ret = 0;
memset(&phy_health_monitor, 0, sizeof(phy_health_monitor));
atl_lock_fw(hw);
atl2_shared_buffer_get(hw, thermal_shutdown, thermal_shutdown);
thermal_shutdown.shutdown_enable = enable;
thermal_shutdown.shutdown_temp_threshold = hw->thermal.crit;
thermal_shutdown.warning_hot_tempThreshold = hw->thermal.high;
thermal_shutdown.warning_cold_temp_threshold = hw->thermal.low;
atl2_shared_buffer_write(hw, thermal_shutdown, thermal_shutdown);
ret = atl2_shared_buffer_finish_ack(hw);
atl2_shared_buffer_read_safe(hw, phy_health_monitor,
&phy_health_monitor);
atl_unlock_fw(hw);
/* Thresholds might have changed, recheck state. */
atl_thermal_check(hw, phy_health_monitor.phy_hot_warning);
return ret;
}
static int atl2_fw_set_pad_stripping(struct atl_hw *hw, bool on)
{
struct link_control_s link_control;
int err = 0;
atl_lock_fw(hw);
atl2_shared_buffer_get(hw, link_control, link_control);
link_control.enable_frame_padding_removal_rx = on;
atl2_shared_buffer_write(hw, link_control, link_control);
err = atl2_shared_buffer_finish_ack(hw);
atl_unlock_fw(hw);
return err;
}
#define ATL2_FW_CFG_DUMP_SIZE (4 + (sizeof(struct link_options_s) * 3) + \
(sizeof(struct link_control_s) * 3))
static int atl2_fw_dump_cfg(struct atl_hw *hw)
{
if (!hw->mcp.fw_cfg_dump)
hw->mcp.fw_cfg_dump = devm_kzalloc(&hw->pdev->dev,
ATL2_FW_CFG_DUMP_SIZE,
GFP_KERNEL);
if (!hw->mcp.fw_cfg_dump)
return -ENOMEM;
/* save link configuration */
hw->mcp.fw_cfg_dump[0] = ((2 * 3 * 4) << 16) | 3;
hw->mcp.fw_cfg_dump[1] = offsetof(struct fw_interface_in, link_control);
atl2_shared_buffer_get(hw, link_control, hw->mcp.fw_cfg_dump[2]);
hw->mcp.fw_cfg_dump[3] = ~0;
hw->mcp.fw_cfg_dump[4] = offsetof(struct fw_interface_in, link_options);
atl2_shared_buffer_get(hw, link_options, hw->mcp.fw_cfg_dump[5]);
hw->mcp.fw_cfg_dump[6] = ~0;
return 0;
}
static void atl2_confirm_buffer_write(struct atl_hw *hw,
uint32_t offset, uint32_t len)
{
struct data_buffer_status_s buffer_status;
buffer_status.data_offset = offset;
buffer_status.data_length = len;
atl2_shared_buffer_write(hw, data_buffer_status, buffer_status);
}
static int atl2_fw_restore_cfg(struct atl_hw *hw)
{
uint32_t req_adr = atl_read(hw, ATL2_MIF_BOOT_READ_REQ_ADR);
uint32_t req_len = atl_read(hw, ATL2_MIF_BOOT_READ_REQ_LEN);
if (req_adr == ATL2_ITI_ADDRESS_START) {
struct fw_iti_hdr iti_header;
memset(&iti_header, 0, sizeof(iti_header));
iti_header.instuction_bitmask = BIT(2);
iti_header.iti[0].type = 2;
iti_header.iti[0].length = ATL2_FW_CFG_DUMP_SIZE;
atl2_mif_shared_boot_buf_write(hw, 0, (void *)&iti_header,
sizeof(iti_header));
atl2_confirm_buffer_write(hw, req_adr, req_len);
atl2_mif_host_finished_write_set(hw, 1U);
return 0;
} else if (req_adr == ATL2_ITI_ADDRESS_BLOCK_1) {
atl2_confirm_buffer_write(hw, req_adr, req_len);
atl2_mif_shared_boot_buf_write(hw, 0,
(void *)hw->mcp.fw_cfg_dump, ATL2_FW_CFG_DUMP_SIZE);
atl2_mif_host_finished_write_set(hw, 1U);
return 0;
}
atl_dev_err("FW requests invalid address %#x", req_adr);
return -EFAULT;
}
static int atl2_fw_set_mediadetect(struct atl_hw *hw, bool on)
{
struct link_options_s link_options;
atl2_shared_buffer_get(hw, link_options, link_options);
link_options.low_power_autoneg = on;
atl2_shared_buffer_write(hw, link_options, link_options);
return atl2_shared_buffer_finish_ack(hw);
}
static int atl2_fw_unsupported(struct atl_hw *hw)
{
return -EOPNOTSUPP;
}
int atl2_get_fw_version(struct atl_hw *hw, u32 *fw_version)
{
struct mac_version_t mac_version;
atl2_shared_buffer_read(hw, version.mac, mac_version);
*fw_version = mac_version.major << 24 | mac_version.minor << 16 |
mac_version.build;
return 0;
}
static struct atl_fw_ops atl2_fw_ops = {
.__wait_fw_init = __atl2_fw_wait_init,
.deinit = atl2_fw_deinit,
.set_link = atl2_fw_set_link,
.check_link = atl2_fw_check_link,
.__get_link_caps = __atl2_fw_get_link_caps,
.restart_aneg = atl2_fw_restart_aneg,
.set_default_link = atl2_fw_set_default_link,
.get_phy_temperature = atl2_fw_get_phy_temperature,
.set_mediadetect = atl2_fw_set_mediadetect,
.send_macsec_req = (void *)atl2_fw_unsupported,
.set_pad_stripping = atl2_fw_set_pad_stripping,
.get_mac_addr = atl2_fw_get_mac_addr,
.__get_hbeat = __atl2_fw_get_hbeat,
.set_phy_loopback = atl2_fw_set_phy_loopback,
.enable_wol = atl2_fw_enable_wol,
.dump_cfg = (void *)atl2_fw_dump_cfg,
.restore_cfg = atl2_fw_restore_cfg,
.update_thermal = atl2_fw_update_thermal,
};
int atl2_fw_init(struct atl_hw *hw)
{
struct atl_mcp *mcp = &hw->mcp;
int ret;
atl2_get_fw_version(hw, &mcp->fw_rev);
mcp->ops = &atl2_fw_ops;
atl_dev_dbg("Detect ATL2FW %x\n", mcp->fw_rev);
ret = mcp->ops->__wait_fw_init(hw);
if (ret)
return ret;
mcp->fw_stat_addr = 0;
mcp->rpc_addr = 0;
ret = mcp->ops->__get_hbeat(hw, &mcp->phy_hbeat);
if (ret)
return ret;
mcp->next_wdog = jiffies + 2 * HZ;
ret = mcp->ops->__get_link_caps(hw);
if (ret)
return ret;
return ret;
}

View file

@ -0,0 +1,690 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL2_FW_H_
#define _ATL2_FW_H_
/* Start of HW byte packed interface declaration */
#pragma pack(push, 1)
/* F W A P I */
struct link_options_s {
uint32_t link_up:1;
uint32_t link_renegotiate:1;
uint32_t minimal_link_speed:1;
uint32_t internal_loopback:1;
uint32_t external_loopback:1;
uint32_t rate_10M_hd:1;
uint32_t rate_100M_hd:1;
uint32_t rate_1G_hd:1;
uint32_t rate_10M:1;
uint32_t rate_100M:1;
uint32_t rate_1G:1;
uint32_t rate_2P5G:1;
uint32_t rate_N2P5G:1;
uint32_t rate_5G:1;
uint32_t rate_N5G:1;
uint32_t rate_10G:1;
uint32_t eee_100M:1;
uint32_t eee_1G:1;
uint32_t eee_2P5G:1;
uint32_t eee_5G:1;
uint32_t eee_10G:1;
uint32_t rsvd3:2;
uint32_t low_power_autoneg:1;
uint32_t pause_rx:1;
uint32_t pause_tx:1;
uint32_t rsvd4:1;
uint32_t downshift:1;
uint32_t downshift_retry:4;
};
struct link_control_s {
uint32_t mode:4;
uint32_t disable_crc_corruption : 1;
uint32_t discard_short_frames : 1;
uint32_t flow_control_mode : 1;
uint32_t disable_length_check : 1;
uint32_t discard_errored_frames : 1;
uint32_t control_frame_enable : 1;
uint32_t enable_tx_padding : 1;
uint32_t enable_crc_forwarding : 1;
uint32_t enable_frame_padding_removal_rx: 1;
uint32_t promiscuous_mode: 1;
uint32_t rsvd:18;
};
struct thermal_shutdown_s {
uint32_t shutdown_enable :1;
uint32_t warning_enable :1;
uint32_t rsvd:6;
uint32_t shutdown_temp_threshold :8;
uint32_t warning_cold_temp_threshold :8;
uint32_t warning_hot_tempThreshold :8;
};
struct mac_address_s {
uint8_t mac_address[6];
};
struct sleep_proxy_s {
struct wake_on_lan_s {
uint32_t wake_on_magic_packet:1;
uint32_t wake_on_pattern:1;
uint32_t wake_on_link_up:1;
uint32_t wake_on_link_down:1;
uint32_t wake_on_ping:1;
uint32_t wake_on_timer:1;
uint32_t wake_on_link_mac_method:1;
uint32_t rsrvd1:1;
uint32_t restore_link_before_wake:1;
uint32_t rsvd:23;
uint32_t link_up_timeout;
uint32_t link_down_timeout;
uint32_t timer;
struct {
uint32_t mask[4];
uint32_t crc32;
} wake_up_patterns[8];
} wake_on_lan;
struct {
uint32_t arp_responder:1;
uint32_t echo_responder:1;
uint32_t igmp_client:1;
uint32_t echo_truncate:1;
uint32_t address_guard:1;
uint32_t ignore_fragmented:1;
uint32_t rsvd:2;
uint32_t echo_max_len:16;
uint32_t ipv4[8];
uint32_t reserved[8];
} ipv4_offload;
struct {
uint32_t ns_responder:1;
uint32_t echo_responder:1;
uint32_t mld_client:1;
uint32_t echo_truncate:1;
uint32_t address_guard:1;
uint32_t rsvd:3;
uint32_t echo_max_len:16;
uint32_t ipv6[16][4];
} ipv6_offload;
struct {
uint16_t ports[16];
} tcp_port_offload;
struct {
uint16_t ports[16];
} udp_port_offload;
struct ka4_offloads_s {
uint32_t retry_count;
uint32_t retry_interval;
struct ka4_offload_s {
uint32_t timeout;
uint16_t local_port;
uint16_t remote_port;
uint8_t remote_mac_addr[6];
uint32_t rsvd:16;
uint32_t rsvd2:32;
uint32_t rsvd3:32;
uint32_t rsvd4:16;
uint16_t win_size;
uint32_t seq_num;
uint32_t ack_num;
uint32_t local_ip;
uint32_t remote_ip;
} offloads[16];
} ka4_offload;
struct ka6_offloads_s {
uint32_t retry_count;
uint32_t retry_interval;
struct ka6_offload_s {
uint32_t timeout;
uint16_t local_port;
uint16_t remote_port;
uint8_t remote_mac_addr[6];
uint32_t rsvd:16;
uint32_t rsvd2:32;
uint32_t rsvd3:32;
uint32_t rsvd4:16;
uint16_t win_size;
uint32_t seq_num;
uint32_t ack_num;
uint32_t local_ip[4];
uint32_t remote_ip[4];
} offloads[16];
} ka6_offload;
struct {
uint32_t rr_count;
uint32_t rr_buf_len;
uint32_t idx_offset;
uint32_t rr__offset;
} mdns;
/* WARN: where this gap actually is not known */
uint32_t reserve_fw_gap:16;
};
struct ptp_s {
uint32_t enable:1;
};
struct pause_quanta_s {
uint16_t quanta_10M;
uint16_t threshold_10M;
uint16_t quanta_100M;
uint16_t threshold_100M;
uint16_t quanta_1G;
uint16_t threshold_1G;
uint16_t quanta_2P5G;
uint16_t threshold_2P5G;
uint16_t quanta_5G;
uint16_t threshold_5G;
uint16_t quanta_10G;
uint16_t threshold_10G;
};
struct data_buffer_status_s {
uint32_t data_offset;
uint32_t data_length;
};
struct device_caps_s {
uint32_t finite_flashless: 1;
uint32_t cable_diag: 1;
uint32_t ncsi: 1;
uint32_t avb: 1;
uint32_t rsvd: 28;
uint32_t rsvd2: 32;
};
struct version_s {
struct bundle_version_t {
uint32_t major:8;
uint32_t minor:8;
uint32_t build:16;
} bundle;
struct mac_version_t {
uint32_t major:8;
uint32_t minor:8;
uint32_t build:16;
} mac;
struct phy_version_t {
uint32_t major:8;
uint32_t minor:8;
uint32_t build:16;
} phy;
uint32_t rsvd:32;
};
struct link_status_s {
uint32_t link_state:4;
uint32_t link_rate:4;
uint32_t pause_tx:1;
uint32_t pause_rx:1;
uint32_t eee:1;
uint32_t duplex:1;
uint32_t rsvd:4;
uint32_t rsvd2:16;
};
struct wol_status_s {
uint32_t wake_count:8;
uint32_t wake_reason:8;
uint32_t wake_up_packet_length :12;
uint32_t wake_up_pattern_number :3;
uint32_t rsvd:1;
uint32_t wake_up_packet[379];
};
struct mac_health_monitor_s {
uint32_t mac_ready:1;
uint32_t mac_fault:1;
uint32_t mac_flashless_finished:1;
uint32_t rsvd:5;
uint32_t mac_temperature:8;
uint32_t mac_heart_beat:16;
uint32_t mac_fault_code:16;
uint32_t rsvd2:16;
};
struct phy_health_monitor_s {
uint32_t phy_ready:1;
uint32_t phy_fault:1;
uint32_t phy_hot_warning:1;
uint32_t rsvd:5;
uint32_t phy_temperature:8;
uint32_t phy_heart_beat:16;
uint32_t phy_fault_code:16;
uint32_t rsvd2:16;
};
struct device_link_caps_s {
uint32_t rsvd:3;
uint32_t internal_loopback:1;
uint32_t external_loopback:1;
uint32_t rate_10M_hd:1;
uint32_t rate_100M_hd:1;
uint32_t rate_1G_hd:1;
uint32_t rate_10M:1;
uint32_t rate_100M:1;
uint32_t rate_1G:1;
uint32_t rate_2P5G:1;
uint32_t rate_N2P5G:1;
uint32_t rate_5G:1;
uint32_t rate_N5G:1;
uint32_t rate_10G:1;
uint32_t rsvd3:1;
uint32_t eee_100M:1;
uint32_t eee_1G:1;
uint32_t eee_2P5G:1;
uint32_t rsvd4:1;
uint32_t eee_5G:1;
uint32_t rsvd5:1;
uint32_t eee_10G:1;
uint32_t pause_rx:1;
uint32_t pause_tx:1;
uint32_t pfc:1;
uint32_t downshift:1;
uint32_t downshift_retry:4;
};
struct sleep_proxy_caps_s {
uint32_t ipv4_offload:1;
uint32_t ipv6_offload:1;
uint32_t tcp_port_offload:1;
uint32_t udp_port_offload:1;
uint32_t ka4_offload:1;
uint32_t ka6_offload:1;
uint32_t mdns_offload:1;
uint32_t wake_on_ping:1;
uint32_t wake_on_magic_packet:1;
uint32_t wake_on_pattern:1;
uint32_t wake_on_timer:1;
uint32_t wake_on_link:1;
uint32_t wake_patterns_count:4;
uint32_t ipv4_count:8;
uint32_t ipv6_count:8;
uint32_t tcp_port_offload_count:8;
uint32_t udp_port_offload_count:8;
uint32_t tcp4_ka_count:8;
uint32_t tcp6_ka_count:8;
uint32_t igmp_offload:1;
uint32_t mld_offload:1;
uint32_t rsvd:30;
};
struct lkp_link_caps_s {
uint32_t rsvd:5;
uint32_t rate_10M_hd:1;
uint32_t rate_100M_hd:1;
uint32_t rate_1G_hd:1;
uint32_t rate_10M:1;
uint32_t rate_100M:1;
uint32_t rate_1G:1;
uint32_t rate_2P5G:1;
uint32_t rate_N2P5G:1;
uint32_t rate_5G:1;
uint32_t rate_N5G:1;
uint32_t rate_10G:1;
uint32_t rsvd2:1;
uint32_t eee_100M:1;
uint32_t eee_1G:1;
uint32_t eee_2P5G:1;
uint32_t rsvd3:1;
uint32_t eee_5G:1;
uint32_t rsvd4:1;
uint32_t eee_10G:1;
uint32_t pause_rx:1;
uint32_t pause_tx:1;
uint32_t rsvd5:6;
};
struct core_dump_s {
uint32_t reg0;
uint32_t reg1;
uint32_t reg2;
uint32_t hi;
uint32_t lo;
uint32_t regs[32];
};
struct trace_s {
uint32_t sync_counter;
uint32_t mem_buffer[0x1ff];
};
struct cable_diag_control_s {
uint32_t toggle :1;
uint32_t rsvd:7;
uint32_t wait_timeout_sec:8;
uint32_t rsvd2:16;
};
struct cable_diag_lane_data_s {
uint32_t result_code :8;
uint32_t dist :8;
uint32_t far_dist :8;
uint32_t rsvd:8;
};
struct cable_diag_status_s {
struct cable_diag_lane_data_s lane_data[4];
uint32_t transact_id:8;
uint32_t status:4;
uint32_t rsvd:20;
};
struct phy_fw_load_status_s {
uint32_t phy_fw_load_from_host :1;
uint32_t phy_fw_load_from_flash :1;
uint32_t phy_fw_load_from_d_c :1;
uint32_t phy_load_from_flash_failed :1;
uint32_t phy_load_from_host_failed :1;
uint32_t phy_load_from_d_c_failed :1;
uint32_t phy_hash_validation_failed :1;
uint32_t phy_fw_started :1;
uint32_t phy_stall_timeout :1;
uint32_t phy_unstall_timeout :1;
uint32_t phy_fw_start_timeout :1;
uint32_t phy_iram_load_error :1;
uint32_t phy_dram_load_error :1;
uint32_t phy_mcp_run_failed :1;
uint32_t phy_mcp_stall_failed :1;
uint32_t phy_mcp_unstall_failed :1;
uint32_t phy_wait_for_semaphore :1;
uint32_t phy_semaphore_locked :1;
uint32_t rsvd :2;
uint32_t phy_worst_block_upload_retry_number:4;
uint32_t phy_worst_upload_block_number :6;
uint32_t rsvd2:2;
};
struct statistics_s {
struct {
uint32_t link_up;
uint32_t link_down;
} link;
struct {
uint64_t tx_unicast_octets;
uint64_t tx_multicast_octets;
uint64_t tx_broadcast_octets;
uint64_t rx_unicast_octets;
uint64_t rx_multicast_octets;
uint64_t rx_broadcast_octets;
uint32_t tx_unicast_frames;
uint32_t tx_multicast_frames;
uint32_t tx_broadcast_frames;
uint32_t tx_errors;
uint32_t rx_unicast_frames;
uint32_t rx_multicast_frames;
uint32_t rx_broadcast_frames;
uint32_t rx_dropped_frames;
uint32_t rx_error_frames;
uint32_t tx_good_frames;
uint32_t rx_good_frames;
uint32_t reserve_fw_gap;
} msm;
uint32_t main_loop_cycles;
uint32_t reserve_fw_gap;
};
struct filter_caps_s {
uint8_t l2_filters_base_index:6;
uint8_t flexible_filter_mask:2;
uint8_t l2_filter_count;
uint8_t ethertype_filter_base_index;
uint8_t ethertype_filter_count;
uint8_t vlan_filter_base_index;
uint8_t vlan_filter_count;
uint8_t l3_ip4_filter_base_index:4;
uint8_t l3_ip4_filter_count:4;
uint8_t l3_ip6_filter_base_index:4;
uint8_t l3_ip6_filter_count:4;
uint8_t l4_filter_base_index:4;
uint8_t l4_filter_count:4;
uint8_t l4_flex_filter_base_index:4;
uint8_t l4_flex_filter_count:4;
uint8_t rslv_tbl_base_index;
uint8_t rslv_tbl_count;
};
struct request_policy_s {
struct {
uint8_t all:1;
uint8_t rsvd:1;
uint8_t rx_queue_tc_index:5;
uint8_t queue_or_tc:1;
} promisc;
struct {
uint8_t accept:1;
uint8_t rsvd:1;
uint8_t rx_queue_tc_index:5;
uint8_t queue_or_tc:1;
} bcast;
struct {
uint8_t accept:1;
uint8_t promisc:1;
uint8_t rx_queue_tc_index:5;
uint8_t queue_or_tc:1;
} mcast;
uint8_t rsvd:8;
};
struct fw_interface_in {
uint32_t mtu;
uint32_t rsvd1:32;
struct mac_address_s mac_address;
uint16_t rsvd;
struct link_control_s link_control;
uint32_t rsvd2:32;
struct link_options_s link_options;
uint32_t rsvd3:32;
struct thermal_shutdown_s thermal_shutdown;
uint32_t rsvd4:32;
struct sleep_proxy_s sleep_proxy;
uint32_t rsvd5:32;
struct pause_quanta_s pause_quanta[8];
struct cable_diag_control_s cable_diag_control;
uint32_t rsvd6:32;
struct data_buffer_status_s data_buffer_status;
uint32_t rsvd7:32;
struct request_policy_s request_policy;
};
struct transaction_counter_s {
uint32_t transaction_cnt_a:16;
uint32_t transaction_cnt_b:16;
};
struct management_status_s {
struct mac_address_s mac_address;
uint16_t vlan;
struct{
uint32_t enable : 1;
uint32_t rsvd:31;
} flags;
uint32_t rsvd1:32;
uint32_t rsvd2:32;
uint32_t rsvd3:32;
uint32_t rsvd4:32;
uint32_t rsvd5:32;
};
struct fw_interface_out {
struct transaction_counter_s transaction_id;
struct version_s version;
struct link_status_s link_status;
struct wol_status_s wol_status;
uint32_t rsvd:32;
uint32_t rsvd2:32;
struct mac_health_monitor_s mac_health_monitor;
uint32_t rsvd3:32;
uint32_t rsvd4:32;
struct phy_health_monitor_s phy_health_monitor;
uint32_t rsvd5:32;
uint32_t rsvd6:32;
struct cable_diag_status_s cable_diag_status;
uint32_t rsvd7:32;
struct device_link_caps_s device_link_caps;
uint32_t rsvd8:32;
struct sleep_proxy_caps_s sleep_proxy_caps;
uint32_t rsvd9:32;
struct lkp_link_caps_s lkp_link_caps;
uint32_t rsvd10:32;
struct core_dump_s core_dump;
uint32_t rsvd11:32;
struct statistics_s stats;
uint32_t rsvd12:32;
struct filter_caps_s filter_caps;
struct device_caps_s device_caps;
uint32_t rsvd13:32;
struct management_status_s management_status;
uint32_t reserve[21];
struct trace_s trace;
};
struct fw_iti_subblock_header {
uint32_t type :8;
uint32_t length :24;
};
struct fw_iti_hdr {
uint32_t instuction_bitmask;
uint32_t reserved;
struct fw_iti_subblock_header iti[6];
};
/* End of HW byte packed interface declaration */
#pragma pack(pop)
#define ATL2_FW_LINK_RATE_INVALID 0
#define ATL2_FW_LINK_RATE_10M 1
#define ATL2_FW_LINK_RATE_100M 2
#define ATL2_FW_LINK_RATE_1G 3
#define ATL2_FW_LINK_RATE_2G5 4
#define ATL2_FW_LINK_RATE_5G 5
#define ATL2_FW_LINK_RATE_10G 6
#define ATL2_HOST_MODE_INVALID 0
#define ATL2_HOST_MODE_ACTIVE 1
#define ATL2_HOST_MODE_SLEEP_PROXY 2
#define ATL2_HOST_MODE_LOW_POWER 3
#define ATL2_HOST_MODE_SHUTDOWN 4
#define ATL2_FW_CABLE_STATUS_OPEN_CIRCUIT 7
#define ATL2_FW_CABLE_STATUS_HIGH_MISMATCH 6
#define ATL2_FW_CABLE_STATUS_LOW_MISMATCH 5
#define ATL2_FW_CABLE_STATUS_SHORT_CIRCUIT 4
#define ATL2_FW_CABLE_STATUS_PAIR_D 3
#define ATL2_FW_CABLE_STATUS_PAIR_C 2
#define ATL2_FW_CABLE_STATUS_PAIR_B 1
#define ATL2_FW_CABLE_STATUS_OK 0
#define ATL2_FW_HOST_INTERRUPT_MAC_READY 0x0004
#define ATL2_FW_HOST_INTERRUPT_DATA_HANDLED 0x0100
#define ATL2_FW_HOST_INTERRUPT_LINK_UP 0x0200
#define ATL2_FW_HOST_INTERRUPT_LINK_DOWN 0x0400
#define ATL2_FW_HOST_INTERRUPT_PHY_FAULT 0x0800
#define ATL2_FW_HOST_INTERRUPT_MAC_FAULT 0x1000
#define ATL2_FW_HOST_INTERRUPT_TEMPERATURE_WARNING 0x2000
#define ATL2_FW_HOST_INTERRUPT_HEARTBEAT 0x4000
#define ATL2_ITI_ADDRESS_START 0x100000
#define ATL2_ITI_ADDRESS_BLOCK_1 (ATL2_ITI_ADDRESS_START +\
sizeof(struct fw_iti_hdr) / sizeof(uint32_t))
enum {
ATL2_MEMORY_MAILBOX_STATUS_FAIL = 0,
ATL2_MEMORY_MAILBOX_STATUS_SUCCESS = 1
};
enum {
ATL2_MEMORY_MAILBOX_TARGET_MEMORY = 0,
ATL2_MEMORY_MAILBOX_TARGET_MDIO = 1
};
enum {
ATL2_MEMORY_MAILBOX_OPERATION_READ = 0,
ATL2_MEMORY_MAILBOX_OPERATION_WRITE = 1
};
enum ATL2_WAKE_REASON {
ATL2_WAKE_REASON_UNKNOWN,
ATL2_WAKE_REASON_PANIC,
ATL2_WAKE_REASON_LINK,
ATL2_WAKE_REASON_TIMER,
ATL2_WAKE_REASON_RESERVED,
ATL2_WAKE_REASON_NAME_GUARD,
ATL2_WAKE_REASON_ADDR_GUARD,
ATL2_WAKE_REASON_TCPKA,
ATL2_WAKE_REASON_DATA_FLAG,
ATL2_WAKE_REASON_PING,
ATL2_WAKE_REASON_SYN,
ATL2_WAKE_REASON_UDP,
ATL2_WAKE_REASON_PATTERN,
ATL2_WAKE_REASON_MAGIC_PACKET
};
int atl2_fw_init(struct atl_hw *hw);
int atl2_get_fw_version(struct atl_hw *hw, u32 *fw_version);
int atl2_fw_set_filter_policy(struct atl_hw *hw, bool promisc, bool allmulti);
#endif

View file

@ -0,0 +1,446 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_COMMON_H_
#define _ATL_COMMON_H_
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/netdevice.h>
#include <linux/moduleparam.h>
#define ATL_VERSION "1.1.11"
struct atl_nic;
#include "atl_compat.h"
#include "atl_hw.h"
#include "atl_log.h"
#include "atl_ring_desc.h"
#include "atl_stats.h"
#define ATL_MAX_QUEUES 8
#include "atl_fwd.h"
struct atl_ptp;
enum {
ATL_RXF_VLAN_BASE = 0,
ATL_RXF_VLAN_MAX = ATL_VLAN_FLT_NUM,
ATL_RXF_ETYPE_BASE = ATL_RXF_VLAN_BASE + ATL_RXF_VLAN_MAX,
ATL_RXF_ETYPE_MAX = ATL_ETYPE_FLT_NUM,
ATL2_RPF_ETYPE_TAGS = 7,
ATL_RXF_NTUPLE_BASE = ATL_RXF_ETYPE_BASE + ATL_RXF_ETYPE_MAX,
ATL_RXF_NTUPLE_MAX = ATL_NTUPLE_FLT_NUM,
ATL_RXF_FLEX_BASE = ATL_RXF_NTUPLE_BASE + ATL_RXF_NTUPLE_MAX,
ATL_RXF_FLEX_MAX = 1,
};
enum atl_rxf_common_cmd {
ATL_RXF_EN = BIT(31),
ATL_RXF_RXQ_MSK = BIT(5) - 1,
ATL_RXF_ACT_SHIFT = 16,
ATL_RXF_ACT_MASK = BIT(3) - 1,
ATL_RXF_ACT_TOHOST = BIT(0) << ATL_RXF_ACT_SHIFT,
};
enum atl_ntuple_cmd {
ATL_NTC_EN = ATL_RXF_EN, /* Filter enabled */
ATL_NTC_V6 = BIT(30), /* IPv6 mode -- only valid in filters
* 0 and 4 */
ATL_NTC_SA = BIT(29), /* Match source address */
ATL_NTC_DA = BIT(28), /* Match destination address */
ATL_NTC_SP = BIT(27), /* Match source port */
ATL_NTC_DP = BIT(26), /* Match destination port */
ATL_NTC_PROTO = BIT(25), /* Match L4 proto */
ATL_NTC_ARP = BIT(24),
ATL_NTC_RXQ = BIT(23), /* Assign Rx queue */
ATL_NTC_ACT_SHIFT = ATL_RXF_ACT_SHIFT,
ATL_NTC_RXQ_SHIFT = 8,
ATL_NTC_RXQ_MASK = ATL_RXF_RXQ_MSK << ATL_NTC_RXQ_SHIFT,
ATL_NTC_L4_MASK = BIT(3) - 1,
ATL_NTC_L4_TCP = 0,
ATL_NTC_L4_UDP = 1,
ATL_NTC_L4_SCTP = 2,
ATL_NTC_L4_ICMP = 3,
};
enum atl2_ntuple_cmd {
ATL2_NTC_L3_IPV4_EN = BIT(0), /* Filter enabled */
ATL2_NTC_L3_IPV4_SA = BIT(1),
ATL2_NTC_L3_IPV4_DA = BIT(2),
ATL2_NTC_L3_IPV4_PROTO = BIT(3),
ATL2_NTC_L3_IPV4_TAG_SHIFT = 4,
ATL2_NTC_L3_IPV4_PROTO_SHIFT = 8,
ATL2_NTC_L3_IPV6_EN = BIT(0x10), /* Filter enabled */
ATL2_NTC_L3_IPV6_SA = BIT(0x11),
ATL2_NTC_L3_IPV6_DA = BIT(0x12),
ATL2_NTC_L3_IPV6_PROTO = BIT(0x13),
ATL2_NTC_L3_IPV6_TAG_SHIFT = 0x14,
ATL2_NTC_L3_IPV6_PROTO_SHIFT = 0x18,
ATL2_NTC_L4_EN = BIT(0), /* Filter enabled */
ATL2_NTC_L4_DP = BIT(1),
ATL2_NTC_L4_SP = BIT(2),
};
struct atl2_rxf_l3 {
union {
struct {
__be32 dst_ip4;
__be32 src_ip4;
};
struct {
__be32 dst_ip6[4];
__be32 src_ip6[4];
};
};
u16 proto;
u32 cmd;
u16 usage;
};
struct atl2_rxf_l4 {
__be16 dst_port;
__be16 src_port;
u32 cmd;
u16 usage;
};
struct atl_rxf_ntuple {
union {
struct {
__be32 dst_ip4[ATL_RXF_NTUPLE_MAX];
__be32 src_ip4[ATL_RXF_NTUPLE_MAX];
};
struct {
__be32 dst_ip6[ATL_RXF_NTUPLE_MAX][4];
__be32 src_ip6[ATL_RXF_NTUPLE_MAX][4];
};
};
__be16 dst_port[ATL_RXF_NTUPLE_MAX];
__be16 src_port[ATL_RXF_NTUPLE_MAX];
struct atl2_rxf_l3 l3v4[ATL_RXF_NTUPLE_MAX];
struct atl2_rxf_l3 l3v6[ATL_RXF_NTUPLE_MAX];
struct atl2_rxf_l4 l4[ATL_RXF_NTUPLE_MAX];
s8 l3_idx[ATL_RXF_NTUPLE_MAX];
bool is_ipv6[ATL_RXF_NTUPLE_MAX];
s8 l4_idx[ATL_RXF_NTUPLE_MAX];
uint32_t cmd[ATL_RXF_NTUPLE_MAX];
int count;
int l3_v4_base_index;
int l3_v4_available;
int l3_v6_base_index;
int l3_v6_available;
int l4_base_index;
int l4_available;
};
enum atl_vlan_cmd {
ATL_VLAN_EN = ATL_RXF_EN,
ATL_VLAN_RXQ = BIT(28),
ATL_VLAN_RXQ_SHIFT = 20,
ATL_VLAN_RXQ_MASK = ATL_RXF_RXQ_MSK << ATL_VLAN_RXQ_SHIFT,
ATL_VLAN_ACT_SHIFT = ATL_RXF_ACT_SHIFT,
ATL_VLAN_VID_MASK = BIT(12) - 1,
};
#define ATL_VID_MAP_LEN BITS_TO_LONGS(BIT(12))
struct atl_rxf_vlan {
uint32_t cmd[ATL_RXF_VLAN_MAX];
int count;
unsigned long map[ATL_VID_MAP_LEN];
int vlans_active;
int promisc_count;
int base_index;
int available;
};
enum atl_etype_cmd {
ATL_ETYPE_EN = ATL_RXF_EN,
ATL_ETYPE_RXQ = BIT(29),
ATL_ETYPE_RXQ_SHIFT = 20,
ATL_ETYPE_RXQ_MASK = ATL_RXF_RXQ_MSK << ATL_ETYPE_RXQ_SHIFT,
ATL_ETYPE_ACT_SHIFT = ATL_RXF_ACT_SHIFT,
ATL_ETYPE_VAL_MASK = BIT(16) - 1,
};
struct atl2_tag_policy {
u16 action;
u16 usage;
};
struct atl_rxf_etype {
uint32_t cmd[ATL_RXF_ETYPE_MAX];
int count;
struct atl2_tag_policy tags_policy[ATL_RXF_ETYPE_MAX];
int tag[ATL_RXF_ETYPE_MAX];
int base_index;
int available;
int tag_top;
};
struct atl_rxf_mac {
int base_index;
int available;
};
enum atl_flex_cmd {
ATL_FLEX_EN = ATL_RXF_EN,
ATL_FLEX_RXQ = BIT(30),
ATL_FLEX_RXQ_SHIFT = 20,
ATL_FLEX_RXQ_MASK = ATL_RXF_RXQ_MSK << ATL_FLEX_RXQ_SHIFT,
ATL_FLEX_ACT_SHIFT = ATL_RXF_ACT_SHIFT,
};
struct atl_rxf_flex {
uint32_t cmd[ATL_RXF_FLEX_MAX];
int count;
int base_index;
int available;
};
struct atl_queue_vec;
#define ATL_NUM_FWD_RINGS ATL_MAX_QUEUES
#define ATL_FWD_RING_BASE ATL_MAX_QUEUES /* Use TC 1 for offload
* engine rings */
#define ATL_NUM_MSI_VECS 32
enum {
ATL_IRQ_LINK = 0,
ATL_IRQ_PTP,
ATL_NUM_NON_RING_IRQS,
};
#define ATL_RXF_RING_ANY 32
#define ATL_FWD_MSI_BASE (ATL_MAX_QUEUES + ATL_NUM_NON_RING_IRQS)
enum atl_fwd_dir {
ATL_FWDIR_RX = 0,
ATL_FWDIR_TX = 1,
ATL_FWDIR_NUM,
};
struct atl_fwd {
unsigned long ring_map[ATL_FWDIR_NUM];
struct atl_fwd_ring *rings[ATL_FWDIR_NUM][ATL_NUM_FWD_RINGS];
unsigned long msi_map;
struct blocking_notifier_head nh_clients;
};
#if IS_ENABLED(CONFIG_ATLFWD_FWD_NETLINK)
struct atl_fwdnl {
struct atl_desc_ring ring_desc[ATL_NUM_FWD_RINGS * 2];
/* State of forced redirections */
int force_icmp_via;
int force_tx_via;
/* Deferred TX head cleanup */
struct delayed_work *tx_cleanup_wq;
u32 tx_bunch;
};
#endif /* CONFIG_ATLFWD_FWD_NETLINK */
struct atl_nic {
struct net_device *ndev;
struct atl_queue_vec *qvecs;
int nvecs;
struct atl_hw hw;
unsigned flags;
uint32_t priv_flags;
struct timer_list work_timer;
int max_mtu;
unsigned int requested_nvecs;
int requested_rx_size;
int requested_tx_size;
int rx_intr_delay;
int tx_intr_delay;
struct atl_global_stats stats;
spinlock_t stats_lock;
struct work_struct work;
#if IS_ENABLED(CONFIG_ATLFWD_FWD)
struct atl_fwd fwd;
#endif
#if IS_ENABLED(CONFIG_ATLFWD_FWD_NETLINK)
struct atl_fwdnl fwdnl;
#endif
struct atl_rxf_ntuple rxf_ntuple;
struct atl_rxf_vlan rxf_vlan;
struct atl_rxf_etype rxf_etype;
struct atl_rxf_mac rxf_mac;
struct atl_rxf_flex rxf_flex;
/* PTP support */
struct atl_ptp *ptp;
};
/* Flags only modified with RTNL lock held */
enum atl_nic_flags {
ATL_FL_MULTIPLE_VECTORS = BIT(0),
ATL_FL_WOL = BIT(1),
};
#define ATL_PF(_name) ATL_PF_ ## _name
#define ATL_PF_BIT(_name) ATL_PF_ ## _name ## _BIT
#define ATL_DEF_PF_BIT(_name) ATL_PF_BIT(_name) = BIT(ATL_PF(_name))
enum atl_priv_flags {
ATL_PF_LPB_SYS_PB,
ATL_PF_LPB_SYS_DMA,
ATL_PF_LPB_NET_DMA,
ATL_PF_LPB_INT_PHY,
ATL_PF_LPB_EXT_PHY,
ATL_PF_LPI_RX_MAC,
ATL_PF_LPI_TX_MAC,
ATL_PF_LPI_RX_PHY,
ATL_PF_LPI_TX_PHY,
ATL_PF_STATS_RESET,
ATL_PF_STRIP_PAD,
ATL_PF_MEDIA_DETECT,
};
enum atl_priv_flag_bits {
ATL_DEF_PF_BIT(LPB_SYS_PB),
ATL_DEF_PF_BIT(LPB_SYS_DMA),
ATL_DEF_PF_BIT(LPB_NET_DMA),
ATL_DEF_PF_BIT(LPB_INT_PHY),
ATL_DEF_PF_BIT(LPB_EXT_PHY),
ATL_PF_LPB_MASK = ATL_PF_BIT(LPB_SYS_DMA) | ATL_PF_BIT(LPB_SYS_PB) |
ATL_PF_BIT(LPB_NET_DMA) | ATL_PF_BIT(LPB_INT_PHY) |
ATL_PF_BIT(LPB_EXT_PHY),
ATL_DEF_PF_BIT(LPI_RX_MAC),
ATL_DEF_PF_BIT(LPI_TX_MAC),
ATL_DEF_PF_BIT(LPI_RX_PHY),
ATL_DEF_PF_BIT(LPI_TX_PHY),
ATL_PF_LPI_MASK = ATL_PF_BIT(LPI_RX_MAC) | ATL_PF_BIT(LPI_TX_MAC) |
ATL_PF_BIT(LPI_RX_PHY) | ATL_PF_BIT(LPI_TX_PHY),
ATL_DEF_PF_BIT(STATS_RESET),
ATL_DEF_PF_BIT(STRIP_PAD),
ATL_DEF_PF_BIT(MEDIA_DETECT),
ATL_PF_RW_MASK = ATL_PF_LPB_MASK | ATL_PF_BIT(STATS_RESET) |
ATL_PF_BIT(STRIP_PAD) | ATL_PF_BIT(MEDIA_DETECT),
ATL_PF_RO_MASK = ATL_PF_LPI_MASK,
};
#define ATL_MAX_MTU (16352 - ETH_FCS_LEN - ETH_HLEN)
#define ATL_MAX_RING_SIZE (8192 - 8)
#define ATL_RING_SIZE 4096
extern const char atl_driver_name[];
extern const struct ethtool_ops atl_ethtool_ops;
extern unsigned int atl_max_queues;
extern unsigned int atl_max_queues_non_msi;
extern unsigned atl_rx_linear;
extern unsigned atl_min_intr_delay;
extern bool atl_enable_msi;
extern bool atl_wq_non_msi;
extern unsigned int atl_tx_clean_budget;
extern unsigned int atl_tx_free_low;
extern unsigned int atl_tx_free_high;
#define atl_module_param(_name, _type, _mode) \
module_param_named(_name, atl_ ## _name, _type, _mode)
static inline void atl_intr_enable_non_ring(struct atl_nic *nic)
{
struct atl_hw *hw = &nic->hw;
atl_intr_enable(hw, hw->non_ring_intr_mask);
}
static inline void atl_intr_disable_non_ring(struct atl_nic *nic)
{
struct atl_hw *hw = &nic->hw;
atl_intr_disable(hw, hw->non_ring_intr_mask);
}
netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev);
int atl_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid);
int atl_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid);
void atl_set_rx_mode(struct net_device *ndev);
int atl_set_features(struct net_device *ndev, netdev_features_t features);
void atl_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *stats);
int atl_setup_datapath(struct atl_nic *nic);
void atl_clear_datapath(struct atl_nic *nic);
int atl_start_rings(struct atl_nic *nic);
void atl_stop_rings(struct atl_nic *nic);
void atl_clear_rdm_cache(struct atl_nic *nic);
void atl_clear_tdm_cache(struct atl_nic *nic);
int atl_alloc_rings(struct atl_nic *nic);
void atl_free_rings(struct atl_nic *nic);
irqreturn_t atl_ring_irq(int irq, void *priv);
void atl_ring_work(struct work_struct *work);
void atl_start_hw_global(struct atl_nic *nic);
int atl_intr_init(struct atl_nic *nic);
void atl_intr_release(struct atl_nic *nic);
int atl_hw_reset(struct atl_hw *hw);
int atl_fw_init(struct atl_hw *hw);
int atl_fw_configure(struct atl_hw *hw);
int atl_reconfigure(struct atl_nic *nic);
void atl_reset_stats(struct atl_nic *nic);
void atl_update_global_stats(struct atl_nic *nic);
void atl_set_loopback(struct atl_nic *nic, int idx, bool on);
void atl_set_intr_mod(struct atl_nic *nic);
void atl_update_ntuple_flt(struct atl_nic *nic, int idx);
void atl_set_vlan_promisc(struct atl_hw *hw, int promisc);
int atl_hwsem_get(struct atl_hw *hw, int idx);
void atl_hwsem_put(struct atl_hw *hw, int idx);
int __atl_msm_read(struct atl_hw *hw, uint32_t addr, uint32_t *val);
int atl_msm_read(struct atl_hw *hw, uint32_t addr, uint32_t *val);
int __atl_msm_write(struct atl_hw *hw, uint32_t addr, uint32_t val);
int atl_msm_write(struct atl_hw *hw, uint32_t addr, uint32_t val);
int atl_update_eth_stats(struct atl_nic *nic);
void atl_adjust_eth_stats(struct atl_ether_stats *stats,
struct atl_ether_stats *base, bool add);
void atl_fwd_release_rings(struct atl_nic *nic);
#if IS_ENABLED(CONFIG_ATLFWD_FWD)
enum atl_fwd_notify;
int atl_fwd_suspend_rings(struct atl_nic *nic);
int atl_fwd_resume_rings(struct atl_nic *nic);
void atl_fwd_notify(struct atl_nic *nic, enum atl_fwd_notify notif, void *data);
#else
static inline int atl_fwd_suspend_rings(struct atl_nic *nic) { return 0; }
static inline int atl_fwd_resume_rings(struct atl_nic *nic) { return 0; }
static inline void atl_fwd_notify(struct atl_nic *nic,
enum atl_fwd_notify notif, void *data) {}
#endif
int atl_get_lpi_timer(struct atl_nic *nic, uint32_t *lpi_delay);
void atl_refresh_rxfs(struct atl_nic *nic);
void atl_schedule_work(struct atl_nic *nic);
int atl_hwmon_init(struct atl_nic *nic);
void atl_thermal_check(struct atl_hw *hw, bool alarm);
int atl_update_thermal(struct atl_hw *hw);
int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val);
int atl_verify_thermal_limits(struct atl_hw *hw, struct atl_thermal *thermal);
int atl_do_reset(struct atl_nic *nic);
int atl_set_media_detect(struct atl_nic *nic, bool on);
#endif

View file

@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
* Portions Copyright (C) various contributors (see specific commit references)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "atl_common.h"
#include "atl_ring.h"
#include <linux/msi.h>
#include <linux/cpu.h>
#include <linux/interrupt.h>
#ifdef ATL_COMPAT_PCI_ALLOC_IRQ_VECTORS
/* From commit aff171641d181ea573380efc3f559c9de4741fc5 */
int atl_compat_pci_irq_vector(struct pci_dev *dev, unsigned int nr)
{
#ifdef CONFIG_PCI_MSI
if (dev->msix_enabled) {
struct msi_desc *entry;
int i = 0;
for_each_pci_msi_entry(entry, dev) {
if (i == nr)
return entry->irq;
i++;
}
WARN_ON_ONCE(1);
return -EINVAL;
}
if (dev->msi_enabled) {
struct msi_desc *entry = first_pci_msi_entry(dev);
if (WARN_ON_ONCE(nr >= entry->nvec_used))
return -EINVAL;
} else {
if (WARN_ON_ONCE(nr > 0))
return -EINVAL;
}
#endif
return dev->irq + nr;
}
int atl_compat_pci_alloc_irq_vectors(struct pci_dev *dev,
unsigned int min_vecs, unsigned int max_vecs, unsigned int flags)
{
int vecs = -ENOSPC;
if (flags & PCI_IRQ_MSIX) {
struct msix_entry *entries;
int i;
entries = kcalloc(max_vecs, sizeof(*entries), GFP_KERNEL);
if (!entries)
return -ENOMEM;
for (i = 0; i < max_vecs; i++)
entries[i].entry = i;
vecs = pci_enable_msix_range(dev, entries, min_vecs, max_vecs);
kfree(entries);
if (vecs > 0)
return vecs;
}
if (flags & PCI_IRQ_MSI) {
vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
if (vecs > 0)
return vecs;
}
/* use legacy irq if allowed */
if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1) {
pci_intx(dev, 1);
return 1;
}
return vecs;
}
#endif
#ifdef ATL_COMPAT_PCI_ENABLE_MSIX_RANGE
/* from commit 302a2523c277bea0bbe8340312b09507905849ed */
int atl_compat_pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
{
int nvec = maxvec;
int rc;
if (maxvec < minvec)
return -ERANGE;
do {
rc = pci_enable_msi_block(dev, nvec);
if (rc < 0) {
return rc;
} else if (rc > 0) {
if (rc < minvec)
return -ENOSPC;
nvec = rc;
}
} while (rc);
return nvec;
}
int atl_compat_pci_enable_msix_range(struct pci_dev *dev,
struct msix_entry *entries,
int minvec, int maxvec)
{
int nvec = maxvec;
int rc;
if (maxvec < minvec)
return -ERANGE;
do {
rc = pci_enable_msix(dev, entries, nvec);
if (rc < 0) {
return rc;
} else if (rc > 0) {
if (rc < minvec)
return -ENOSPC;
nvec = rc;
}
} while (rc);
return nvec;
}
#endif

View file

@ -0,0 +1,495 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
* Portions Copyright (C) various contributors (see specific commit references)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_COMPAT_H_
#define _ATL_COMPAT_H_
#include <linux/version.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#ifndef IS_REACHABLE
#define IS_REACHABLE defined
#endif
/* If the kernel is not RHEL / CentOS, then the 2 identifiers below will be
* undefined. Define them this way to simplify the checks below.
*/
#ifndef RHEL_RELEASE_CODE
#define RHEL_RELEASE_CODE 0
#endif
#ifndef RHEL_RELEASE_VERSION
#define RHEL_RELEASE_VERSION(a,b) 1
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
#define ATL_HAVE_MINMAX_MTU
#elif (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5)) && \
(RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0))
#define ndo_change_mtu ndo_change_mtu_rh74
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) || RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)
/* introduced in commit 3f1ac7a700d039c61d8d8b99f28d605d489a60cf */
#define ATL_HAVE_ETHTOOL_KSETTINGS
/* introduced in commit 72bb68721f80a1441e871b6afc9ab0b3793d5031 */
#define ATL_HAVE_IPV6_NTUPLE
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0) || RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)
/* introduced in commit 892311f66f2411b813ca631009356891a0c2b0a1 */
#define ATL_HAVE_RXHASH_TYPE
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) || RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)
/* introduced in commit 3de0b592394d17b2c41a261a6a493a521213f299 */
#define ATL_HAVE_ETHTOOL_RXHASH
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
/* ->ndo_get_stats64 return type was changed to void in commit
* bc1f44709cf27fb2a5766cadafe7e2ad5e9cb221. It's safe to just cast
* the pointer to avoid the warning because the only place
* ->ndo_get_stats64 was invoked before the change ignored the return
* value. */
#define ATL_COMPAT_CAST_NDO_GET_STATS64
#endif /* 4.11.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
/* introduced in commit 94842b4fc4d6b1691cfc86c6f5251f299d27f4ba */
#define ETHTOOL_LINK_MODE_2500baseT_Full_BIT 47
#define ETHTOOL_LINK_MODE_5000baseT_Full_BIT 48
#endif /* 4.10.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
/* from commit 1dff8083a024650c75a9c961c38082473ceae8cf */
#define page_to_virt(x) __va(PFN_PHYS(page_to_pfn(x)))
#endif /* 4.7.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
/* from commit d31eb342409b24e3d2e1989c775f3361e93acc08 */
/* Helpers to hide struct msi_desc implementation details */
#define msi_desc_to_dev(desc) (&(desc)->dev.dev)
#define dev_to_msi_list(dev) (&to_pci_dev((dev))->msi_list)
#define first_msi_entry(dev) \
list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list)
#define for_each_msi_entry(desc, dev) \
list_for_each_entry((desc), dev_to_msi_list((dev)), list)
#define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev)
#define for_each_pci_msi_entry(desc, pdev) \
for_each_msi_entry((desc), &(pdev)->dev)
static inline struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
{
return desc->dev;
}
#endif /* 3.19.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,3)
/* ->xmit_more introduced in commit
* 0b725a2ca61bedc33a2a63d0451d528b268cf975 for 3.18-rc1 */
static inline int skb_xmit_more(struct sk_buff *skb)
{
return 0;
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) || RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8, 2)
static inline int skb_xmit_more(struct sk_buff *skb)
{
return netdev_xmit_more();
}
#else /* 3.18.0-5.2.0 for vanilla, anything less than 5.2.0 for RHEL */
static inline int skb_xmit_more(struct sk_buff *skb)
{
return skb->xmit_more;
}
#endif /* 3.18.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) && \
RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7, 3)
typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
struct sk_buff *skb);
static inline u16 atlfwd_nl_pick0(struct net_device *dev, struct sk_buff *skb)
{
return 0;
}
#endif
/* NB! select_queue_fallback_t MUST be defined before #include on RHEL < 7.3 */
#include "atl_fwdnl.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) && \
RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7, 3)
static inline u16 atlfwd_nl_select_queue(struct net_device *dev,
struct sk_buff *skb)
{
return atlfwd_nl_select_queue_fallback(dev, skb, NULL, atlfwd_nl_pick0);
}
#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) && \
RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7, 3)
static inline u16 atlfwd_nl_select_queue(struct net_device *dev,
struct sk_buff *skb, void *accel_priv)
{
return atlfwd_nl_select_queue_fallback(
dev, skb, (struct net_device *)accel_priv, atlfwd_nl_pick0);
}
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) && \
RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0)
static inline u16 atlfwd_nl_select_queue(struct net_device *dev,
struct sk_buff *skb, void *accel_priv,
select_queue_fallback_t fallback)
{
return atlfwd_nl_select_queue_fallback(
dev, skb, (struct net_device *)accel_priv, fallback);
}
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
static inline u16 atlfwd_nl_select_queue(struct net_device *dev,
struct sk_buff *skb,
struct net_device *sb_dev,
select_queue_fallback_t fallback)
{
return atlfwd_nl_select_queue_fallback(dev, skb, sb_dev, fallback);
}
#else
static inline u16 atlfwd_nl_select_queue(struct net_device *dev,
struct sk_buff *skb,
struct net_device *sb_dev)
{
return atlfwd_nl_select_queue_fallback(dev, skb, sb_dev,
netdev_pick_tx);
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,6)
/* introduced in commit 686fef928bba6be13cabe639f154af7d72b63120 */
static inline void timer_setup(struct timer_list *timer,
void (*callback)(struct timer_list *), unsigned int flags)
{
setup_timer(timer, (void (*)(unsigned long))callback,
(unsigned long)timer);
}
#define from_timer(var, callback_timer, timer_fieldname) \
container_of(callback_timer, typeof(*var), timer_fieldname)
#endif /* 4.14.0 && RHEL < 7.6 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,5)
#define ATL_COMPAT_PCI_ALLOC_IRQ_VECTORS
int atl_compat_pci_alloc_irq_vectors(struct pci_dev *dev,
unsigned int min_vecs, unsigned int max_vecs, unsigned int flags);
static inline int pci_alloc_irq_vectors(struct pci_dev *dev,
unsigned int min_vecs, unsigned int max_vecs, unsigned int flags)
{
return atl_compat_pci_alloc_irq_vectors(dev, min_vecs,
max_vecs, flags);
}
int atl_compat_pci_irq_vector(struct pci_dev *dev, unsigned int nr);
static inline int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
{
return atl_compat_pci_irq_vector(dev, nr);
}
static inline void pci_free_irq_vectors(struct pci_dev *dev)
{
pci_disable_msix(dev);
pci_disable_msi(dev);
}
/* from commit 4fe0d154880bb6eb833cbe84fa6f385f400f0b9c */
#define PCI_IRQ_LEGACY (1 << 0) /* allow legacy interrupts */
#define PCI_IRQ_MSI (1 << 1) /* allow MSI interrupts */
#define PCI_IRQ_MSIX (1 << 2) /* allow MSI-X interrupts */
#endif /* 4.8.0 && RHEL < 7.5 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,4)
/* from commit be9d2e8927cef02076bb7b5b2637cd9f4be2e8df */
static inline int
pci_request_mem_regions(struct pci_dev *pdev, const char *name)
{
return pci_request_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM), name);
}
#endif /* 4.8.0 && RHEL < 7.4 */
#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,3)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
/* from commit fe896d1878949ea92ba547587bc3075cc688fb8f */
static inline void page_ref_inc(struct page *page)
{
atomic_inc(&page->_count);
}
/* introduced in commit 795bb1c00dd338aa0d12f9a7f1f4776fb3160416 */
#define napi_consume_skb(__skb, __budget) dev_consume_skb_any(__skb)
/* from commit 3f1ac7a700d039c61d8d8b99f28d605d489a60cf */
#define ETHTOOL_LINK_MODE_10baseT_Half_BIT 0
#define ETHTOOL_LINK_MODE_10baseT_Full_BIT 1
#define ETHTOOL_LINK_MODE_100baseT_Half_BIT 2
#define ETHTOOL_LINK_MODE_100baseT_Full_BIT 3
#define ETHTOOL_LINK_MODE_1000baseT_Half_BIT 4
#define ETHTOOL_LINK_MODE_1000baseT_Full_BIT 5
#define ETHTOOL_LINK_MODE_10000baseT_Full_BIT 12
/* IPv6 NFC API introduced in commit
* 72bb68721f80a1441e871b6afc9ab0b3793d5031 */
/* Define the IPv6 constants for kernels not supporting IPv6 in the
* NFC API to reduce the number of #ifdefs in the code. The constants
* themselves may already be defined for RSS hash management API, so
* #undef them first */
#undef TCP_V6_FLOW
#define TCP_V6_FLOW 0x05
#undef UDP_V6_FLOW
#define UDP_V6_FLOW 0x06
#undef SCTP_V6_FLOW
#define SCTP_V6_FLOW 0x07
#undef IPV6_USER_FLOW
#define IPV6_USER_FLOW 0x0e
#define IPV4_USER_FLOW IP_USER_FLOW
#endif /* 4.6.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
/* introduced in commit c7f5d105495a38ed09e70d825f75d9d7d5407264
* stub it */
static inline int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr)
{
return -ENODEV;
}
#endif /* 4.5.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
/* introduced in 2f064f3485cd29633ad1b3cfb00cc519509a3d72
* The new implementation can't be used directly as it requires
* changes to linux/mm code. Use the old check described in the
* commit with older kernels insted. It can lead to false positives,
* but as we only use it to determine whether the page is re-usable,
* the false positives can only decrease performance. */
static inline bool page_is_pfmemalloc(struct page *page)
{
return page->pfmemalloc && !page->mapping;
}
#endif /* 4.1.0 */
#endif /* RHEL_RELEASE_CODE < 7.3 */
#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,2)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
#define ETHTOOL_RX_FLOW_SPEC_RING 0x00000000FFFFFFFFLL
#define ETHTOOL_RX_FLOW_SPEC_RING_VF 0x000000FF00000000LL
#define ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF 32
static inline __u64 ethtool_get_flow_spec_ring(__u64 ring_cookie)
{
return ETHTOOL_RX_FLOW_SPEC_RING & ring_cookie;
};
static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie)
{
return (ETHTOOL_RX_FLOW_SPEC_RING_VF & ring_cookie) >>
ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
};
#endif /* 4.2.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
/* renamed in commit df8a39defad46b83694ea6dd868d332976d62cc0 */
#define skb_vlan_tag_present(__skb) vlan_tx_tag_present(__skb)
#define skb_vlan_tag_get(__skb) vlan_tx_tag_get(__skb)
#endif /* 4.0.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
/* introduced in commit 1077fa36f23e259858caf6f269a47393a5aff523
* use plain rmb() for now*/
#define dma_rmb() rmb()
/* from commit 9c0c112422a2a6b06fcddcaf21957676490cebba */
static inline int eth_skb_pad(struct sk_buff *skb)
{
unsigned int len = ETH_ZLEN;
unsigned int size = skb->len;
if (unlikely(size < len)) {
len -= size;
if (skb_pad(skb, len))
return -ENOMEM;
__skb_put(skb, len);
}
return 0;
}
/* introduced in commit 71dfda58aaaf4bf6b1bc59f9d8afa635fa1337d4 */
#define __dev_alloc_pages(__flags, __order) __skb_alloc_pages(__flags | __GFP_COMP, NULL, __order)
/* introduced in commit fd11a83dd3630ec6a60f8a702446532c5c7e1991 */
#define napi_alloc_skb(__napi, __len) netdev_alloc_skb_ip_align((__napi)->dev, __len)
/* introduced in commit 3b47d30396bae4f0bd1ff0dbcd7c4f5077e7df4e */
#define napi_complete_done(__napi, __work_done) napi_complete(__napi)
/* introduced in commit bc9ad166e38ae1cdcb5323a8aa45dff834d68bfa */
#define napi_schedule_irqoff(__napi) napi_schedule(__napi)
/* READ_ONCE() / WRITE_ONCE
* from commit 230fa253df6352af12ad0a16128760b5cb3f92df with changes
* from 43239cbe79fc369f5d2160bd7f69e28b5c50a58c and
* 7bd3e239d6c6d1cad276e8f130b386df4234dcd7 */
/* READ_ONCE / WRTIE_ONCE were also cherry-picked into 3.12.58 as
* b5be8baf9e0d5ea035588a0430b2af4989e07572 and
* dda458f0183649e8613a62bb59bec4e5acb883aa */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,58) || LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
static __always_inline void __read_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(__u8 *)res = *(volatile __u8 *)p; break;
case 2: *(__u16 *)res = *(volatile __u16 *)p; break;
case 4: *(__u32 *)res = *(volatile __u32 *)p; break;
case 8: *(__u64 *)res = *(volatile __u64 *)p; break;
default:
barrier();
__builtin_memcpy((void *)res, (const void *)p, size);
barrier();
}
}
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
default:
barrier();
__builtin_memcpy((void *)p, (const void *)res, size);
barrier();
}
}
#define READ_ONCE(x) \
({ typeof(x) __val; __read_once_size(&x, &__val, sizeof(__val)); __val; })
#define WRITE_ONCE(x, val) \
({ typeof(x) __val; __val = val; __write_once_size(&x, &__val, sizeof(__val)); __val; })
#endif /* 3.12.58 */
#endif /* 3.19.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
/* introduced in commit 56193d1bce2b2759cb4bdcc00cd05544894a0c90
* pull the whole head buffer len for now*/
#define eth_get_headlen(__data, __max_len) (__max_len)
#endif /* 3.18.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
/* from commit 286ab723d4b83d37deb4017008ef1444a95cfb0d */
static inline void ether_addr_copy(u8 *dst, const u8 *src)
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
*(u32 *)dst = *(const u32 *)src;
*(u16 *)(dst + 4) = *(const u16 *)(src + 4);
#else
u16 *a = (u16 *)dst;
const u16 *b = (const u16 *)src;
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
#endif
}
/* introduced in commit e6247027e5173c00efb2084d688d06ff835bc3b0 */
#define dev_consume_skb_any(__skb) dev_kfree_skb_any(__skb)
/* from commit 09323cc479316e046931a2c679932204b36fea6c */
enum pkt_hash_types {
PKT_HASH_TYPE_NONE, /* Undefined type */
PKT_HASH_TYPE_L2, /* Input: src_MAC, dest_MAC */
PKT_HASH_TYPE_L3, /* Input: src_IP, dst_IP */
PKT_HASH_TYPE_L4, /* Input: src_IP, dst_IP, src_port, dst_port */
};
static inline void
skb_set_hash(struct sk_buff *skb, __u32 hash, enum pkt_hash_types type)
{
skb->l4_rxhash = (type == PKT_HASH_TYPE_L4);
skb->rxhash = hash;
}
/* from commit 302a2523c277bea0bbe8340312b09507905849ed */
#define ATL_COMPAT_PCI_ENABLE_MSIX_RANGE
int atl_compat_pci_enable_msi_range(struct pci_dev *dev, int minvec,
int maxvec);
int atl_compat_pci_enable_msix_range(struct pci_dev *dev,
struct msix_entry *entries, int minvec, int maxvec);
static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec,
int maxvec)
{
return atl_compat_pci_enable_msi_range(dev, minvec, maxvec);
}
static inline int pci_enable_msix_range(struct pci_dev *dev,
struct msix_entry *entries, int minvec, int maxvec)
{
return atl_compat_pci_enable_msix_range(dev, entries, minvec, maxvec);
}
#endif /* 3.14.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,27)
/* from commit 4aa806b771d16b810771d86ce23c4c3160888db3
* also cherry-picked into 3.12-stable as
* 8bac7a35e60ca70c8d12ddbfdf28a8df5a976b2b */
static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
{
int rc = dma_set_mask(dev, mask);
if (rc == 0)
dma_set_coherent_mask(dev, mask);
return rc;
}
#endif /* 3.12.27 */
#endif /* RHEL_RELEASE_CODE < 7.2 */
#endif

View file

@ -0,0 +1,159 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_DESC_H_
#define _ATL_DESC_H_
#include <linux/kernel.h>
#if defined(__LITTLE_ENDIAN_BITFIELD)
struct __packed atl_tx_ctx {
unsigned long long :40; //0
unsigned tun_len:8; //40
unsigned out_len:16; //48
unsigned type:3; //64
unsigned idx:1; //67
unsigned vlan_tag:16; //68
unsigned cmd:4; //84
unsigned l2_len:7; //88
unsigned l3_len:9; //95
unsigned l4_len:8; //104
unsigned mss_len:16; //112
};
struct __packed atl_tx_desc {
unsigned long long daddr:64; //0
unsigned type:3; //64
unsigned :1; //67
unsigned len:16; //68
unsigned dd:1; //84
unsigned eop:1; //85
unsigned cmd:8; //86
unsigned :14; //94
unsigned ct_idx:1; //108
unsigned ct_en:1; //109
unsigned pay_len:18; //110
};
#define ATL_DATA_PER_TXD 16384 // despite ->len being 16 bits
enum atl_tx_desc_type {
tx_desc_type_desc = 1,
tx_desc_type_context = 2,
};
enum atl_tx_desc_cmd {
tx_desc_cmd_vlan = 1,
tx_desc_cmd_fcs = 2,
tx_desc_cmd_ipv4cs = 4,
tx_desc_cmd_l4cs = 8,
tx_desc_cmd_lso = 0x10,
tx_desc_cmd_wb = 0x20,
};
enum atl_tx_ctx_cmd {
ctx_cmd_snap = 1, // SNAP / ~802.3
ctx_cmd_ipv6 = 2, // IPv6 / ~IPv4
ctx_cmd_tcp = 4, // TCP / ~UDP
};
struct __packed atl_rx_desc {
uint64_t daddr; //0
union {
struct {
unsigned dd:1; //64
uint64_t haddr63:63; //65
};
uint64_t haddr;
};
};
struct __packed atl_rx_desc_wb {
unsigned rss_type:4; //0
unsigned pkt_type:8; //4
unsigned rdm_err:1; //12
unsigned :6; //13
unsigned rx_cntl:2; //19
unsigned sph:1; //21
unsigned hdr_len:10; //22
unsigned rss_hash:32; //32
unsigned dd:1; //64
unsigned eop:1; //65
unsigned rx_stat:4; //66
unsigned rx_estat:6; //70
unsigned rsc_cnt:4; //76
unsigned pkt_len:16; //80
unsigned next_desp:16; //96
unsigned vlan_tag:16; //112
};
struct __packed atl_rx_desc_hwts_wb {
u32 sec_hw;
u32 ns;
u32 dd:1;
u32 rsvd:1;
u32 sec_lw0:30;
u32 sec_lw1;
};
enum atl_rx_stat {
atl_rx_stat_mac_err = 1,
atl_rx_stat_ipv4_err = 2,
atl_rx_stat_l4_err = 4,
atl_rx_stat_l4_valid = 8,
atl_rx_stat_err_msk = atl_rx_stat_mac_err | atl_rx_stat_ipv4_err |
atl_rx_stat_l4_err,
};
enum atl_rx_estat {
atl_rx_estat_vlan_stripped = 1,
atl_rx_estat_l2_ucast_match = 2,
atl_rx_estat_vxlan = 1 << 2,
atl_rx_estat_nvgre = 2 << 2,
atl_rx_estat_geneve = 3 << 2,
atl_rx_estat_tun_msk = 3 << 2,
atl_rx_estat_outer_ipv4_err = 16,
atl_rx_estat_outer_ipv4_valid = 32,
};
enum atl_rx_pkt_type {
atl_rx_pkt_type_ipv4 = 0,
atl_rx_pkt_type_ipv6 = 1,
atl_rx_pkt_type_l3_other = 2,
atl_rx_pkt_type_l3_arp = 3,
atl_rx_pkt_type_l3_msk = 3,
atl_rx_pkt_type_tcp = 0 << 2,
atl_rx_pkt_type_udp = 1 << 2 ,
atl_rx_pkt_type_sctp = 2 << 2,
atl_rx_pkt_type_icmp = 3 << 2,
atl_rx_pkt_type_l4_msk = ((1 << 3) - 1) << 2,
atl_rx_pkt_type_vlan = 1 << 5,
atl_rx_pkt_type_dbl_vlan = 2 << 5,
atl_rx_pkt_type_vlan_msk = ((1 << 2) - 1) << 5,
};
#else // defined(__LITTLE_ENDIAN_BITFIELD)
#error XXX Fix bigendian bitfields
#endif // defined(__LITTLE_ENDIAN_BITFIELD)
union __packed atl_desc {
struct atl_rx_desc rx;
union {
struct atl_rx_desc_wb wb;
struct atl_rx_desc_hwts_wb hwts_wb;
};
struct atl_tx_ctx ctx;
struct atl_tx_desc tx;
uint8_t raw[16];
};
#endif

View file

@ -0,0 +1,421 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_DRVIFACE_H_
#define _ATL_DRVIFACE_H_
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
struct __attribute__((packed)) offloadKAv4 {
uint32_t timeout;
in_port_t local_port;
in_port_t remote_port;
uint8_t remote_mac_addr[6];
uint16_t win_size;
uint32_t seq_num;
uint32_t ack_num;
in_addr_t local_ip;
in_addr_t remote_ip;
};
struct __attribute__((packed)) offloadKAv6 {
uint32_t timeout;
in_port_t local_port;
in_port_t remote_port;
uint8_t remote_mac_addr[6];
uint16_t win_size;
uint32_t seq_num;
uint32_t ack_num;
struct in6_addr local_ip;
struct in6_addr remote_ip;
};
struct __attribute__((packed)) offloadIPInfo {
uint8_t v4LocalAddrCount;
uint8_t v4AddrCount;
uint8_t v6LocalAddrCount;
uint8_t v6AddrCount;
// FW will add the base to the following offset fields and will treat them as pointers.
// The offsets are relative to the start of this struct so that the struct is pretty much self-contained
// in_addr_t *
uint32_t v4AddrOfft;
// uint8_t *
uint32_t v4PrefixOfft;
// in6_addr *
uint32_t v6AddrOfft;
// uint8_t *
uint32_t v6PrefixOfft;
};
struct __attribute__((packed)) offloadPortInfo {
uint16_t UDPPortCount;
uint16_t TCPPortCount;
// in_port_t *
uint32_t UDPPortOfft; // See the comment in the offloadIPInfo struct
// in_port_t *
uint32_t TCPPortOfft;
};
struct __attribute__((packed)) offloadKAInfo {
uint16_t v4KACount;
uint16_t v6KACount;
uint32_t retryCount;
uint32_t retryInterval;
// struct offloadKAv4 *
uint32_t v4KAOfft; // See the comment in the offloadIPInfo struct
// struct offloadKAv6 *
uint32_t v6KAOfft;
};
struct __attribute__((packed)) offloadRRInfo {
uint32_t RRCount;
uint32_t RRBufLen;
// Offset to RR index table relative to the start of offloadRRInfo struct. The indices
// themselves are relative to the start of RR buffer. FW will add the buffer address
// and will treat them as pointers.
// uint8_t **
uint32_t RRIdxOfft;
// Offset to the RR buffer relative to the start of offloadRRInfo struct.
// uint8_t *
uint32_t RRBufOfft;
};
struct __attribute__((packed)) offloadInfo {
uint32_t version; // = 0 till it stabilizes some
uint32_t len; // The whole structure length including the variable-size buf
uint8_t macAddr[8];
struct offloadIPInfo ips;
struct offloadPortInfo ports;
struct offloadKAInfo kas;
struct offloadRRInfo rrs;
uint8_t buf[0];
};
#define FW_PACK_STRUCT __attribute__((packed))
#define DRV_REQUEST_SIZE 3072
#define DRV_MSG_PING 0x01
#define DRV_MSG_ARP 0x02
#define DRV_MSG_INJECT 0x03
#define DRV_MSG_WOL_ADD 0x04
#define DRV_MSG_WOL_REMOVE 0x05
#define DRV_MSG_ENABLE_WAKEUP 0x06
#define DRV_MSG_MSM 0x07
#define DRV_MSG_PROVISIONING 0x08
#define DRV_MSG_OFFLOAD_ADD 0x09
#define DRV_MSG_OFFLOAD_REMOVE 0x0A
#define DRV_MSG_MSM_EX 0x0B
#define DRV_MSG_SMBUS_PROXY 0x0C
#define DRV_PROV_APPLY 1
#define DRV_PROV_REPLACE 2
#define DRV_PROV_ADD 3
#define FW_RPC_INJECT_PACKET_LEN 1514U
typedef enum {
EVENT_DRIVER_ENABLE_WOL
} eDriverEvent;
//typedef enum {
// HOST_UNINIT = 0,
// HOST_RESET,
// HOST_INIT,
// HOST_RESERVED,
// HOST_SLEEP,
// HOST_INVALID
//} hostState_t;
struct drvMsgPing {
uint32_t ping;
} FW_PACK_STRUCT;
union IPAddr {
struct
{
uint8_t addr[16];
} FW_PACK_STRUCT v6;
struct
{
uint8_t padding[12];
uint8_t addr[4];
} FW_PACK_STRUCT v4;
} FW_PACK_STRUCT;
struct drvMsgArp {
uint8_t macAddr[6];
uint32_t uIpAddrCnt;
struct
{
union IPAddr addr;
union IPAddr mask;
} FW_PACK_STRUCT ip[1];
} FW_PACK_STRUCT;
struct drvMsgInject {
uint32_t len;
uint8_t packet[FW_RPC_INJECT_PACKET_LEN];
} FW_PACK_STRUCT;
enum ndisPmWoLPacket {
ndisPMWoLPacketUnspecified = 0,
ndisPMWoLPacketBitmapPattern,
ndisPMWoLPacketMagicPacket,
ndisPMWoLPacketIPv4TcpSyn,
ndisPMWoLPacketIPv6TcpSyn,
ndisPMWoLPacketEapolRequestIdMessage,
ndisPMWoLPacketMaximum
};
enum aqPmWoLPacket {
aqPMWoLPacketUnspecified = 0x10000,
aqPMWoLPacketArp,
aqPMWoLPacketIPv4Ping,
aqPMWoLPacketIpv6NsPacket,
aqPMWoLPacketIpv6Ping,
aqPMWoLReasonLinkUp,
aqPMWoLReasonLinkDown,
aqPMWoLPacketMaximum
};
enum ndisPmProtocolOffloadType {
ndisPMProtocolOffloadIdUnspecified,
ndisPMProtocolOffloadIdIPv4ARP,
ndisPMProtocolOffloadIdIPv6NS,
ndisPMProtocolOffload80211RSNRekey,
ndisPMProtocolOffloadIdMaximum
};
struct drvMsgEnableWakeup {
uint32_t patternMaskWindows;
uint32_t patternMaskAquantia;
uint32_t patternMaskOther;
uint32_t offloadsMaskWindows;
uint32_t offloadsMaskAquantia;
} FW_PACK_STRUCT;
struct drvMsgWoLAddIpv4TcpSynWoLPacketParameters {
uint32_t flags;
union {
uint8_t v8[4];
uint32_t v32;
} IPv4SourceAddress;
union {
uint8_t v8[4];
uint32_t v32;
} IPv4DestAddress;
union {
uint8_t v8[2];
uint16_t v16;
} TCPSourcePortNumber;
union {
uint8_t v8[2];
uint16_t v16;
} TCPDestPortNumber;
} FW_PACK_STRUCT;
struct drvMsgWoLAddIpv6TcpSynWoLPacketParameters {
uint32_t flags;
union {
uint8_t v8[16];
uint32_t v32[4];
} IPv6SourceAddress;
union {
uint8_t v8[16];
uint32_t v32[4];
} IPv6DestAddress;
union {
uint8_t v8[2];
uint16_t v16;
} TCPSourcePortNumber;
union {
uint8_t v8[2];
uint16_t v16;
} TCPDestPortNumber;
} FW_PACK_STRUCT;
struct drvMsgWoLAddIpv4PingWoLPacketParameters {
uint32_t flags;
union {
uint8_t v8[4];
uint32_t v32;
} IPv4SourceAddress;
union {
uint8_t v8[4];
uint32_t v32;
} IPv4DestAddress;
} FW_PACK_STRUCT;
struct drvMsgWoLAddIpv6PingWoLPacketParameters {
uint32_t flags;
union {
uint8_t v8[16];
uint32_t v32[4];
} IPv6SourceAddress;
union {
uint8_t v8[16];
uint32_t v32[4];
} IPv6DestAddress;
} FW_PACK_STRUCT;
struct drvMsgWoLAddEapolRequestIdMessageWoLPacketParameters {
uint32_t flags;
union {
uint8_t v8[4];
uint32_t v32;
} IPv4SourceAddress;
union {
uint8_t v8[4];
uint32_t v32;
} IPv4DestAddress;
} FW_PACK_STRUCT;
struct drvMsgWoLAddBitmapPattern {
uint32_t Flags;
uint32_t MaskOffset;
uint32_t MaskSize;
uint32_t PatternOffset;
uint32_t PatternSize;
} FW_PACK_STRUCT;
struct drvMsgWoLAddMagicPacketPattern {
uint8_t macAddr[6];
} FW_PACK_STRUCT;
struct drvMsgWoLAddArpWoLPacketParameters {
uint32_t flags;
union {
uint8_t v8[4];
uint32_t v32;
} IPv4Address;
} FW_PACK_STRUCT;
struct drvMsgWoLAddLinkUpWoLParameters {
uint32_t timeout;
} FW_PACK_STRUCT;
struct drvMsgWoLAddLinkDownWoLParameters {
uint32_t timeout;
} FW_PACK_STRUCT;
struct drvMsgWoLAdd {
uint32_t priority; // Currently not used
uint32_t packetType; // One of ndisPmWoLPacket or aqPmWoLPacket
uint32_t patternId; // Id to save - will be used in remove message
uint32_t nextWoLPatternOffset; // For chaining multiple additions in one request
// Depends on `parrernId`
union _WOL_PATTERN {
struct drvMsgWoLAddIpv4TcpSynWoLPacketParameters wolIpv4TcpSyn;
struct drvMsgWoLAddIpv6TcpSynWoLPacketParameters wolIpv6TcpSyn;
struct drvMsgWoLAddEapolRequestIdMessageWoLPacketParameters wolEapolRequestIdMessage;
struct drvMsgWoLAddBitmapPattern wolBitmap;
struct drvMsgWoLAddMagicPacketPattern wolMagicPacket;
struct drvMsgWoLAddIpv4PingWoLPacketParameters wolIpv4Ping;
struct drvMsgWoLAddIpv6PingWoLPacketParameters wolIpv6Ping;
struct drvMsgWoLAddArpWoLPacketParameters wolArp;
struct drvMsgWoLAddLinkUpWoLParameters wolLinkUpReason;
struct drvMsgWoLAddLinkDownWoLParameters wolLinkDownReason;
} wolPattern;
} FW_PACK_STRUCT;
struct drvMsgWoLRemove {
uint32_t id;
} FW_PACK_STRUCT;
struct ipv4ArpParameters {
uint32_t flags;
uint8_t remoteIPv4Address[4];
uint8_t hostIPv4Address[4];
uint8_t macAddress[6];
} FW_PACK_STRUCT;
struct ipv6NsParameters {
uint32_t flags;
union {
uint8_t v8[16];
uint32_t v32[4];
} remoteIPv6Address;
union {
uint8_t v8[16];
uint32_t v32[4];
} solicitedNodeIPv6Address;
union {
uint8_t v8[16];
uint32_t v32[4];
} targetIPv6Addresses[2];
uint8_t macAddress[6];
} FW_PACK_STRUCT;
struct drvMsgOffloadAdd {
uint32_t priority;
uint32_t protocolOffloadType;
uint32_t protocolOffloadId;
uint32_t nextProtocolOffloadOffset;
union {
struct ipv4ArpParameters ipv4Arp;
struct ipv6NsParameters ipv6Ns;
} wolOffload;
} FW_PACK_STRUCT;
struct drvMsgOffloadRemove {
uint32_t id;
} FW_PACK_STRUCT;
struct drvMsmSettings {
uint32_t msmReg054;
uint32_t msmReg058;
uint32_t msmReg05c;
uint32_t msmReg060;
uint32_t msmReg064;
uint32_t msmReg068;
uint32_t msmReg06c;
uint32_t msmReg070;
uint32_t flags; // Valid for message DRV_MSG_MSM_EX only
} FW_PACK_STRUCT;
//struct drvMsgProvisioning {
// uint32_t command;
// uint32_t len;
// provList_t list;
//} FW_PACK_STRUCT;
//struct drvMsgSmbusProxy {
// uint32_t typeMsg;
// union {
// struct smbusProxyWrite smbWrite;
// struct smbusProxyRead smbRead;
// struct smbusProxyGetStatus smbStatus;
// struct smbusProxyReadResp smbReadResp;
// } FW_PACK_STRUCT;
//} FW_PACK_STRUCT;
struct drvIface {
uint32_t msgId;
union {
struct drvMsgPing msgPing;
struct drvMsgArp msgArp;
struct drvMsgInject msgInject;
struct drvMsgWoLAdd msgWoLAdd;
struct drvMsgWoLRemove msgWoLRemove;
struct drvMsgEnableWakeup msgEnableWakeup;
struct drvMsmSettings msgMsm;
// struct drvMsgProvisioning msgProvisioning;
struct drvMsgOffloadAdd msgOffloadAdd;
struct drvMsgOffloadRemove msgOffloadRemove;
// struct drvMsgSmbusProxy msgSmbusProxy;
struct offloadInfo fw2xOffloads;
} FW_PACK_STRUCT;
} FW_PACK_STRUCT;
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_ETHTOOL_H_
#define _ATL_ETHTOOL_H_
enum atl_rxf_type {
ATL_RXF_VLAN = 0,
ATL_RXF_ETYPE,
ATL_RXF_NTUPLE,
ATL_RXF_FLEX,
};
s8 atl_reserve_filter(enum atl_rxf_type type);
void atl_release_filter(enum atl_rxf_type type);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,304 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_FW_H_
#define _ATL_FW_H_
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/ethtool.h>
struct atl_hw;
struct atl_nic;
struct atl_mcp {
uint32_t fw_rev;
struct atl_fw_ops *ops;
uint32_t fw_stat_addr;
uint32_t rpc_addr;
uint32_t fw_settings_addr;
uint32_t fw_settings_len;
uint32_t req_high;
uint32_t req_high_mask; /* Clears link rate-dependend bits */
uint32_t caps_low;
uint32_t caps_high;
uint32_t caps_ex;
struct mutex lock;
unsigned long next_wdog;
bool wdog_disabled;
uint16_t phy_hbeat;
uint32_t *fw_cfg_dump;
};
struct atl_link_type {
unsigned speed;
bool duplex;
unsigned ethtool_idx;
uint32_t fw_bits[2];
const char *name;
};
enum atl_link_type_index {
atl_link_type_idx_10m,
atl_link_type_idx_100m,
atl_link_type_idx_1g,
atl_link_type_idx_2p5g,
atl_link_type_idx_5g,
atl_link_type_idx_10g,
atl_link_type_idx_10m_half,
atl_link_type_idx_100m_half,
atl_link_type_idx_1g_half,
};
extern struct atl_link_type atl_link_types[];
extern const int atl_num_rates;
struct atl_fw2_thermal_cfg {
uint32_t msg_id;
uint8_t shutdown_temp;
uint8_t high_temp;
uint8_t normal_temp;
};
#define atl_for_each_rate(idx, type) \
for (idx = 0, type = atl_link_types; \
idx < atl_num_rates; \
idx++, type++)
#define atl_define_bit(_name, _bit) \
_name ## _shift = (_bit), \
_name = BIT(_name ## _shift),
enum atl_fw2_opts {
atl_define_bit(atl_fw2_pause, 3)
atl_define_bit(atl_fw2_asym_pause, 4)
atl_fw2_pause_mask = atl_fw2_pause | atl_fw2_asym_pause,
atl_define_bit(atl_fw2_fw_request, 12)
atl_define_bit(atl_fw2_macsec, 15)
atl_define_bit(atl_fw2_wake_on_link, 16)
atl_define_bit(atl_fw2_wake_on_link_force, 17)
atl_define_bit(atl_fw2_phy_temp, 18)
atl_define_bit(atl_fw2_set_thermal, 21)
atl_define_bit(atl_fw2_link_drop, 22)
atl_define_bit(atl_fw2_nic_proxy, 0x17)
atl_define_bit(atl_fw2_wol, 0x18)
atl_define_bit(atl_fw2_thermal_alarm, 29)
atl_define_bit(atl_fw2_statistics, 30)
};
enum atl_fw2_ex_caps {
atl_define_bit(atl_fw2_ex_caps_phy_ptp_en, 16)
atl_define_bit(atl_fw2_ex_caps_ptp_gpio_en, 20)
atl_define_bit(atl_fw2_ex_caps_phy_ctrl_ts_pin, 22)
atl_define_bit(atl_fw2_ex_caps_wol_ex, 23)
atl_define_bit(atl_fw2_ex_caps_mac_heartbeat, 25)
atl_define_bit(atl_fw2_ex_caps_msm_settings_apply, 26)
};
enum atl_fw2_wol_ex {
atl_define_bit(atl_fw2_wol_ex_wake_on_link_keep_rate, 0)
atl_define_bit(atl_fw2_wol_ex_wake_on_magic_keep_rate, 1)
};
enum atl_fw2_stat_offt {
atl_fw2_stat_phy_hbeat = 0x4c,
atl_fw2_stat_temp = 0x50,
atl_fw2_stat_ptp_offset = 0x64,
atl_fw2_stat_lcaps = 0x84,
atl_fw2_stat_settings_addr = 0x10c,
atl_fw2_stat_settings_len = 0x110,
atl_fw2_stat_caps_ex = 0x114,
atl_fw2_stat_gpio_pin = 0x118,
};
enum atl_fw2_settings_offt {
atl_fw2_setings_msm_opts = 0x90,
atl_fw2_setings_media_detect = 0x98,
atl_fw2_setings_wol_ex = 0x9c,
};
enum atl_fw2_msm_opts {
atl_define_bit(atl_fw2_settings_msm_opts_strip_pad, 0)
};
enum atl_fw2_fw_request {
atl_fw2_msm_settings_apply = 0x20,
};
enum atl_fc_mode {
atl_fc_none = 0,
atl_define_bit(atl_fc_rx, 0)
atl_define_bit(atl_fc_tx, 1)
atl_fc_full = atl_fc_rx | atl_fc_tx,
};
enum atl_thermal_flags {
atl_define_bit(atl_thermal_monitor, 0)
atl_define_bit(atl_thermal_throttle, 1)
atl_define_bit(atl_thermal_ignore_lims, 2)
};
struct atl_fc_state {
enum atl_fc_mode req;
enum atl_fc_mode prev_req;
enum atl_fc_mode cur;
};
enum atl_wake_flags {
atl_fw_wake_on_link = WAKE_PHY,
atl_fw_wake_on_magic = WAKE_MAGIC,
atl_fw_wake_on_link_rtpm = BIT(10),
};
#define ATL_EEE_BIT_OFFT 16
#define ATL_EEE_MASK ~(BIT(ATL_EEE_BIT_OFFT) - 1)
struct atl_link_state{
/* The following three bitmaps use alt_link_types[] indices
* as link bit positions. Conversion to/from ethtool bits is
* done in atl_ethtool.c. */
unsigned supported;
unsigned advertized;
unsigned lp_advertized;
unsigned prev_advertized;
int lp_lowest; /* Idx of lowest rate advertized by
* link partner in atl_link_types[] */
int throttled_to; /* Idx of the rate we're throttled to */
bool force_off;
bool thermal_throttled;
bool autoneg;
bool eee;
bool eee_enabled;
bool ptp_available;
bool ptp_datapath_up;
struct atl_link_type *link;
struct atl_fc_state fc;
};
enum macsec_msg_type {
macsec_cfg_msg = 0,
macsec_add_rx_sc_msg,
macsec_add_tx_sc_msg,
macsec_add_rx_sa_msg,
macsec_add_tx_sa_msg,
macsec_get_stats_msg,
};
struct __packed macsec_cfg_request {
u32 enabled;
u32 egress_threshold;
u32 ingress_threshold;
u32 interrupts_enabled;
};
struct __packed macsec_msg_fw_request {
u32 msg_id; /* not used */
u32 msg_type;
struct macsec_cfg_request cfg;
};
struct __packed macsec_msg_fw_response {
u32 result;
};
enum atl_gpio_pin_function {
GPIO_PIN_FUNCTION_NC,
GPIO_PIN_FUNCTION_VAUX_ENABLE,
GPIO_PIN_FUNCTION_EFUSE_BURN_ENABLE,
GPIO_PIN_FUNCTION_SFP_PLUS_DETECT,
GPIO_PIN_FUNCTION_TX_DISABLE,
GPIO_PIN_FUNCTION_RATE_SEL_0,
GPIO_PIN_FUNCTION_RATE_SEL_1,
GPIO_PIN_FUNCTION_TX_FAULT,
GPIO_PIN_FUNCTION_PTP0,
GPIO_PIN_FUNCTION_PTP1,
GPIO_PIN_FUNCTION_PTP2,
GPIO_PIN_FUNCTION_SIZE
};
struct __packed atl_ptp_offset_info {
u16 ingress_100;
u16 egress_100;
u16 ingress_1000;
u16 egress_1000;
u16 ingress_2500;
u16 egress_2500;
u16 ingress_5000;
u16 egress_5000;
u16 ingress_10000;
u16 egress_10000;
};
enum ptp_msg_type {
ptp_gpio_ctrl_msg = 0x11,
ptp_adj_freq_msg = 0x12,
ptp_adj_clock_msg = 0x13,
};
struct __packed ptp_gpio_ctrl {
u32 index;
u32 period;
u64 start;
};
struct __packed ptp_adj_freq {
u32 ns_mac;
u32 fns_mac;
u32 ns_phy;
u32 fns_phy;
u32 mac_ns_adj;
u32 mac_fns_adj;
};
struct __packed ptp_adj_clock {
u32 ns;
u32 sec;
int sign;
};
struct __packed ptp_msg_fw_request {
u32 msg_id;
union {
struct ptp_gpio_ctrl gpio_ctrl;
struct ptp_adj_freq adj_freq;
struct ptp_adj_clock adj_clock;
};
};
struct atl_fw_ops {
void (*set_link)(struct atl_hw *hw, bool force);
struct atl_link_type *(*check_link)(struct atl_hw *hw);
int (*__wait_fw_init)(struct atl_hw *hw);
int (*__get_link_caps)(struct atl_hw *hw);
int (*restart_aneg)(struct atl_hw *hw);
void (*set_default_link)(struct atl_hw *hw);
int (*enable_wol)(struct atl_hw *hw, unsigned int wol_mode);
int (*get_phy_temperature)(struct atl_hw *hw, int *temp);
int (*dump_cfg)(struct atl_hw *hw);
int (*restore_cfg)(struct atl_hw *hw);
int (*set_phy_loopback)(struct atl_nic *nic, u32 mode);
int (*set_mediadetect)(struct atl_hw *hw, bool on);
int (*set_pad_stripping)(struct atl_hw *hw, bool on);
int (*send_macsec_req)(struct atl_hw *hw,
struct macsec_msg_fw_request *msg,
struct macsec_msg_fw_response *resp);
int (*__get_hbeat)(struct atl_hw *hw, uint16_t *hbeat);
int (*get_mac_addr)(struct atl_hw *hw, uint8_t *buf);
int (*update_thermal)(struct atl_hw *hw);
int (*send_ptp_req)(struct atl_hw *hw, struct ptp_msg_fw_request *msg);
void (*set_ptp)(struct atl_hw *hw, bool on);
int (*deinit)(struct atl_hw *hw);
};
int atl_read_mcp_word(struct atl_hw *hw, uint32_t offt, uint32_t *val);
#endif

View file

@ -0,0 +1,905 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2018 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/etherdevice.h>
#include "atl_common.h"
#include "atl_desc.h"
static const char *atl_fwd_dir_str(struct atl_fwd_ring *ring)
{
return ring->flags & ATL_FWR_TX ? "Tx" : "Rx";
}
static int atl_fwd_ring_tx(struct atl_fwd_ring *ring)
{
return !!(ring->flags & ATL_FWR_TX);
}
static void *atl_fwd_frag_vaddr(struct atl_fwd_buf_frag *frag,
struct atl_fwd_mem_ops *ops)
{
if (ops->alloc_buf)
return frag->buf;
return page_to_virt(frag->page);
}
static int atl_fwd_get_frag(struct atl_fwd_ring *ring, int idx)
{
struct page *pg;
dma_addr_t daddr = 0;
struct device *dev = &ring->nic->hw.pdev->dev;
struct atl_fwd_bufs *bufs = ring->bufs;
struct atl_fwd_buf_frag *frag = &bufs->frags[idx];
struct atl_fwd_mem_ops *ops = ring->mem_ops;
if (ops->alloc_buf) {
void *buf = ops->alloc_buf(dev, bufs->frag_size,
&daddr, GFP_KERNEL, ops);
if (!IS_ERR_OR_NULL(buf)) {
frag->buf = buf;
frag->daddr = daddr;
return 0;
} else
return PTR_ERR(buf);
}
pg = __dev_alloc_pages(GFP_KERNEL, bufs->order);
if (!pg)
return -ENOMEM;
if (!(ring->flags & ATL_FWR_DONT_DMA_MAP)) {
daddr = dma_map_page(dev, pg, 0, PAGE_SIZE << bufs->order,
DMA_FROM_DEVICE);
if (dma_mapping_error(dev, daddr)) {
__free_pages(pg, bufs->order);
return -ENOMEM;
}
}
frag->daddr = daddr;
frag->page = pg;
return 0;
}
static void atl_fwd_free_bufs(struct atl_fwd_ring *ring)
{
struct atl_nic *nic = ring->nic;
struct device *dev = &nic->hw.pdev->dev;
struct atl_fwd_bufs *bufs = ring->bufs;
struct atl_fwd_mem_ops *ops = ring->mem_ops;
int ring_size = ring->hw.size;
int order;
size_t frag_size;
int i;
if (!bufs)
return;
order = bufs->order;
frag_size = bufs->frag_size;
if (bufs->daddr_vec)
dma_free_coherent(dev, ring_size * sizeof(dma_addr_t),
bufs->daddr_vec, bufs->daddr_vec_base);
if (ring->flags & ATL_FWR_WANT_VIRT_BUF_VEC)
kfree(bufs->vaddr_vec);
for (i = 0; i < bufs->num_pages; i++) {
struct atl_fwd_buf_frag *frag = &bufs->frags[i];
if (ops->free_buf) {
if (frag->buf)
ops->free_buf(frag->buf, dev, frag_size,
frag->daddr, ops);
continue;
}
if (frag->page) {
if (!(ring->flags & ATL_FWR_DONT_DMA_MAP))
dma_unmap_page(dev, frag->daddr,
PAGE_SIZE << order,
DMA_FROM_DEVICE);
__free_pages(frag->page, order);
}
}
kfree(bufs);
ring->bufs = NULL;
}
static int atl_fwd_alloc_bufs(struct atl_fwd_ring *ring, int order)
{
struct atl_nic *nic = ring->nic;
int flags = ring->flags;
int ring_size = ring->hw.size;
int buf_size = ring->buf_size;
struct device *dev = &nic->hw.pdev->dev;
struct atl_fwd_mem_ops *ops = ring->mem_ops;
struct atl_fwd_buf_frag *frag;
struct atl_fwd_bufs *bufs;
int num_pages, i;
int ret;
unsigned int pg_off = 0;
bool want_dvec = !!(flags & ATL_FWR_WANT_DMA_BUF_VEC);
bool want_vvec = !!(flags & ATL_FWR_WANT_VIRT_BUF_VEC);
size_t frag_size;
if (!(flags & ATL_FWR_ALLOC_BUFS))
return 0;
if (flags & ATL_FWR_CONTIG_BUFS) {
frag_size = buf_size * ring_size;
order = get_order(frag_size);
num_pages = 1;
} else {
int bufs_per_page;
frag_size = PAGE_SIZE << order;
bufs_per_page = frag_size / buf_size;
num_pages = ring_size / bufs_per_page +
!!(ring_size % bufs_per_page);
}
bufs = kzalloc(sizeof(*bufs) +
sizeof(struct atl_fwd_buf_frag) * num_pages,
GFP_KERNEL);
if (!bufs) {
atl_nic_err("%s: couldn't alloc buffers structure\n", __func__);
return -ENOMEM;
}
ring->bufs = bufs;
bufs->num_pages = num_pages;
bufs->order = order;
bufs->frag_size = frag_size;
for (i = 0; i < num_pages; i++) {
ret = atl_fwd_get_frag(ring, i);
if (ret) {
atl_nic_err("%s: couldn't alloc buffer page (order %d)\n",
__func__, order);
goto free;
}
}
frag = &bufs->frags[0];
if (want_dvec) {
ret = -ENOMEM;
bufs->daddr_vec = dma_alloc_coherent(dev,
ring_size * sizeof(dma_addr_t),
&bufs->daddr_vec_base, GFP_KERNEL);
if (!bufs->daddr_vec) {
atl_nic_err("%s: couldn't alloc DMA addr table\n",
__func__);
goto free;
}
} else
bufs->daddr_vec_base = frag[0].daddr;
if (want_vvec) {
ret = -ENOMEM;
bufs->vaddr_vec = kcalloc(ring_size, sizeof(void *),
GFP_KERNEL);
if (!bufs->vaddr_vec) {
atl_nic_err("%s: couldn't alloc virtual addr table\n",
__func__);
goto free;
}
} else
bufs->vaddr_vec = atl_fwd_frag_vaddr(frag, ops);
if (!(want_dvec || want_vvec))
return 0;
for (i = 0; i < ring_size; i++) {
dma_addr_t daddr = frag->daddr + pg_off;
if (want_dvec)
bufs->daddr_vec[i] = daddr;
if (want_vvec)
bufs->vaddr_vec[i] = atl_fwd_frag_vaddr(frag, ops) +
pg_off;
pg_off += buf_size;
if (pg_off + buf_size <= frag_size)
continue;
frag++;
pg_off = 0;
}
return 0;
free:
atl_fwd_free_bufs(ring);
return ret;
}
static void atl_fwd_update_im(struct atl_fwd_ring *ring)
{
struct atl_hw *hw = &ring->nic->hw;
int idx = ring->idx;
uint32_t addr, tx_reg;
if (hw->chip_id == ATL_ANTIGUA)
tx_reg = ATL2_TX_INTR_MOD_CTRL(idx);
else
tx_reg = ATL_TX_INTR_MOD_CTRL(idx);
addr = atl_fwd_ring_tx(ring) ? tx_reg :
ATL_RX_INTR_MOD_CTRL(idx);
atl_write(hw, addr, (ring->intr_mod_max / 2) << 0x10 |
(ring->intr_mod_min / 2) << 8 | 2);
}
static void atl_fwd_init_descr(struct atl_fwd_ring *fwd_ring)
{
struct atl_hw_ring *ring = &fwd_ring->hw;
int dir_tx = atl_fwd_ring_tx(fwd_ring);
struct atl_fwd_buf_frag *frag = NULL;
int buf_size = fwd_ring->buf_size;
int ring_size = ring->size;
size_t frag_size = 0;
unsigned int pg_off;
int i;
memset(ring->descs, 0, ring_size * sizeof(*ring->descs));
if (!(fwd_ring->flags & ATL_FWR_ALLOC_BUFS))
return;
frag = &fwd_ring->bufs->frags[0];
frag_size = fwd_ring->bufs->frag_size;
if (!(fwd_ring->flags & ATL_FWR_DONT_DMA_MAP)) {
for (pg_off = 0, i = 0; i < ring_size; i++) {
union atl_desc *desc = &ring->descs[i];
dma_addr_t daddr = frag->daddr + pg_off;
if (dir_tx) {
/* init both daddr and dd for both cases:
* daddr for head pointer writeback
* dd for the descriptor writeback */
desc->tx.daddr = daddr;
desc->tx.dd = 1;
} else {
desc->rx.daddr = daddr;
}
pg_off += buf_size;
if (pg_off + buf_size <= frag_size)
continue;
frag++;
pg_off = 0;
}
}
}
static void atl_fwd_init_ring(struct atl_fwd_ring *fwd_ring)
{
struct atl_hw *hw = &fwd_ring->nic->hw;
struct atl_hw_ring *ring = &fwd_ring->hw;
unsigned int flags = fwd_ring->flags;
int dir_tx = atl_fwd_ring_tx(fwd_ring);
int idx = fwd_ring->idx;
int lxo_bit = !!(flags & ATL_FWR_LXO);
/* Reinit descriptors as they could be stale after hardware reset */
atl_fwd_init_descr(fwd_ring);
atl_write(hw, ATL_RING_BASE_LSW(ring), ring->daddr);
atl_write(hw, ATL_RING_BASE_MSW(ring), upper_32_bits(ring->daddr));
if (dir_tx) {
atl_write(hw, ATL_TX_RING_THRESH(ring),
8 << 8 | 8 << 0x10 | 24 << 0x18);
atl_write(hw, ATL_TX_RING_CTL(ring), ring->size);
atl_write_bit(hw, ATL_TX_LSO_CTRL, idx, lxo_bit);
} else {
uint32_t ctrl = ring->size |
!!(flags & ATL_FWR_VLAN) << 29;
atl_write(hw, ATL_RX_RING_BUF_SIZE(ring),
fwd_ring->buf_size / 1024);
atl_write(hw, ATL_RX_RING_THRESH(ring),
8 << 0x10 | 24 << 0x18);
atl_write(hw, ATL_RX_RING_TAIL(ring), ring->size - 1);
atl_write(hw, ATL_RX_RING_CTL(ring), ctrl);
if (lxo_bit)
atl_write_bits(hw, ATL_RX_LRO_PKT_LIM(idx),
(idx & 7) * 4, 2, 3);
atl_write_bit(hw, ATL_RX_LRO_CTRL1, idx, lxo_bit);
atl_write_bit(hw, ATL_INTR_RSC_EN, idx, lxo_bit);
}
atl_fwd_update_im(fwd_ring);
}
void atl_fwd_release_ring(struct atl_fwd_ring *ring)
{
struct atl_nic *nic = ring->nic;
int idx = ring->idx;
int dir_tx = atl_fwd_ring_tx(ring);
struct atl_fwd *fwd = &nic->fwd;
unsigned long *map = &fwd->ring_map[dir_tx];
struct atl_fwd_ring **rings = fwd->rings[dir_tx];
struct atl_fwd_mem_ops *ops = ring->mem_ops;
struct device *dev = &nic->hw.pdev->dev;
struct atl_hw_ring *hwring = &ring->hw;
atl_fwd_disable_ring(ring);
if (ring->evt) {
atl_fwd_disable_event(ring->evt);
atl_fwd_release_event(ring->evt);
}
atl_do_reset(nic);
__clear_bit(idx, map);
rings[idx - ATL_FWD_RING_BASE] = NULL;
atl_fwd_free_bufs(ring);
if (ops->free_descs)
ops->free_descs(hwring->descs, dev,
hwring->size * sizeof(*hwring->descs), hwring->daddr,
ops);
else
atl_free_descs(nic, &ring->hw, 0);
kfree(ring);
}
EXPORT_SYMBOL(atl_fwd_release_ring);
static unsigned int atl_fwd_rx_mod_max = 25, atl_fwd_rx_mod_min = 15,
atl_fwd_tx_mod_max = 25, atl_fwd_tx_mod_min = 15;
atl_module_param(fwd_rx_mod_max, uint, 0644);
atl_module_param(fwd_rx_mod_min, uint, 0644);
atl_module_param(fwd_tx_mod_max, uint, 0644);
atl_module_param(fwd_tx_mod_min, uint, 0644);
static struct atl_fwd_mem_ops null_ops;
struct atl_fwd_ring *atl_fwd_request_ring(struct net_device *ndev,
int flags, int ring_size, int buf_size, int page_order,
struct atl_fwd_mem_ops *ops)
{
struct atl_nic *nic = netdev_priv(ndev);
struct atl_fwd *fwd = &nic->fwd;
int dir_tx = !!(flags & ATL_FWR_TX);
unsigned long *map = &fwd->ring_map[dir_tx];
struct atl_fwd_ring **rings = fwd->rings[dir_tx], *ring;
struct device *dev = &nic->hw.pdev->dev;
struct atl_hw_ring *hwring;
int ret = -ENOMEM;
int idx;
if (ring_size & 7 || ring_size > ATL_MAX_RING_SIZE) {
atl_nic_err("%s: bad ring size %d, must be no more than %d and a multiple of 8\n",
__func__, ring_size, ATL_MAX_RING_SIZE);
return ERR_PTR(-EINVAL);
}
if (buf_size & 1023 || buf_size > 16 * 1024) {
atl_nic_err("%s: bad buffer size %d, must be no more than 16k and a multiple of 1024\n",
__func__, buf_size);
return ERR_PTR(-EINVAL);
}
if (!ops)
ops = &null_ops;
if ((ops->alloc_buf && !ops->free_buf) ||
(ops->free_buf && !ops->alloc_buf)) {
atl_nic_err("%s: must provide either both buffer allocator and deallocator or none\n",
__func__);
return ERR_PTR(-EINVAL);
}
if ((ops->alloc_descs && !ops->free_descs) ||
(ops->free_descs && !ops->alloc_descs)) {
atl_nic_err("%s: must provide either both descriptor ring allocator and deallocator or none\n",
__func__);
return ERR_PTR(-EINVAL);
}
if ((flags & (ATL_FWR_WANT_DMA_BUF_VEC | ATL_FWR_DONT_DMA_MAP)) ==
(ATL_FWR_WANT_DMA_BUF_VEC | ATL_FWR_DONT_DMA_MAP)) {
atl_nic_err("%s: ATL_FWR_WANT_DMA_BUF_VEC and ATL_FWR_DONT_DMA_MAP flags are mutually exclusive\n",
__func__);
return ERR_PTR(-EINVAL);
}
idx = find_next_zero_bit(map, ATL_FWD_RING_BASE + ATL_NUM_FWD_RINGS,
ATL_FWD_RING_BASE);
if (idx >= ATL_FWD_RING_BASE + ATL_NUM_FWD_RINGS) {
atl_nic_err("%s: no more rings available\n", __func__);
return ERR_PTR(ret);
}
ring = kzalloc(sizeof(*ring), GFP_KERNEL);
if (!ring) {
atl_nic_err("%s: couldn't alloc ring structure\n", __func__);
return ERR_PTR(ret);
}
ring->nic = nic;
ring->idx = idx;
ring->flags = flags;
hwring = &ring->hw;
hwring->size = ring_size;
ring->buf_size = buf_size;
ring->mem_ops = ops;
if (ops->alloc_descs) {
void *descs;
dma_addr_t daddr;
descs = ops->alloc_descs(dev,
ring_size * sizeof(*hwring->descs), &daddr, GFP_KERNEL,
ops);
if (!IS_ERR_OR_NULL(descs)) {
hwring->descs = descs;
hwring->daddr = daddr;
ret = 0;
} else
ret = PTR_ERR(descs);
} else
ret = atl_alloc_descs(nic, hwring, 0);
if (ret) {
atl_nic_err("%s: couldn't alloc the ring\n", __func__);
goto free_ring;
}
hwring->reg_base = dir_tx ? ATL_TX_RING(idx) : ATL_RX_RING(idx);
ret = atl_fwd_alloc_bufs(ring, page_order);
if (ret)
goto free_descs;
__set_bit(idx, map);
rings[idx - ATL_FWD_RING_BASE] = ring;
if (dir_tx) {
ring->intr_mod_max = atl_fwd_tx_mod_max;
ring->intr_mod_min = atl_fwd_tx_mod_min;
} else {
ring->intr_mod_max = atl_fwd_rx_mod_max;
ring->intr_mod_min = atl_fwd_rx_mod_min;
}
atl_fwd_init_ring(ring);
return ring;
free_descs:
if (ops->free_descs)
ops->free_descs(hwring->descs, dev,
hwring->size * sizeof(*hwring->descs), hwring->daddr,
ops);
else
atl_free_descs(nic, hwring, 0);
free_ring:
kfree(ring);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(atl_fwd_request_ring);
int atl_fwd_set_ring_intr_mod(struct atl_fwd_ring *ring, int min, int max)
{
struct atl_nic *nic = ring->nic;
if (atl_fwd_ring_tx(ring) && ring->evt &&
ring->evt->flags & ATL_FWD_EVT_TXWB) {
struct atl_nic *nic = ring->nic;
atl_nic_err("%s: Interrupt moderation not supported for head pointer writeback events\n",
__func__);
return -EINVAL;
}
if (min >= 0) {
if (min > 511) {
atl_nic_err("%s: min delay out of range (0..511): %d\n",
__func__, min);
return -EINVAL;
}
ring->intr_mod_min = min;
}
if (max >= 0) {
if (max > 1023) {
atl_nic_err("%s: max delay out of range (0..1023): %d\n",
__func__, max);
return -EINVAL;
}
ring->intr_mod_max = max;
}
atl_fwd_update_im(ring);
return 0;
}
EXPORT_SYMBOL(atl_fwd_set_ring_intr_mod);
void atl_fwd_release_rings(struct atl_nic *nic)
{
struct atl_fwd_ring *ring;
int i;
for (i = 0; i < ATL_NUM_FWD_RINGS * ATL_FWDIR_NUM; i++) {
ring = nic->fwd.rings[i % ATL_FWDIR_NUM][i / ATL_FWDIR_NUM];
if (ring)
atl_fwd_release_ring(ring);
}
}
int atl_fwd_enable_ring(struct atl_fwd_ring *ring)
{
struct atl_hw *hw = &ring->nic->hw;
atl_set_bits(hw, ATL_RING_CTL(&ring->hw), BIT(31));
atl_clear_bits(hw, ATL_RING_CTL(&ring->hw), BIT(30));
ring->state |= ATL_FWR_ST_ENABLED;
return 0;
}
EXPORT_SYMBOL(atl_fwd_enable_ring);
void atl_fwd_disable_ring(struct atl_fwd_ring *ring)
{
struct atl_hw *hw = &ring->nic->hw;
if (!(ring->state & ATL_FWR_ST_ENABLED))
return;
atl_clear_bits(hw, ATL_RING_CTL(&ring->hw), BIT(31));
atl_set_bits(hw, ATL_RING_CTL(&ring->hw), BIT(30));
ring->state &= ~ATL_FWR_ST_ENABLED;
}
EXPORT_SYMBOL(atl_fwd_disable_ring);
static void __iomem *atl_msix_bar(struct atl_nic *nic)
{
struct pci_dev *pdev = nic->hw.pdev;
struct msi_desc *msi;
if (!pdev->msix_enabled)
return NULL;
msi = list_first_entry(dev_to_msi_list(&pdev->dev),
struct msi_desc, list);
return msi->mask_base;
}
static int atl_fwd_set_msix_vec(struct atl_nic *nic, struct atl_fwd_event *evt)
{
int idx = evt->idx;
uint64_t addr = evt->msi_addr;
uint32_t data = evt->msi_data;
uint32_t ctrl;
void __iomem *desc = atl_msix_bar(nic);
if (!desc)
return -EIO;
desc += idx * PCI_MSIX_ENTRY_SIZE;
/* MSI-X table updates must be atomic, so mask first */
ctrl = readl(desc + PCI_MSIX_ENTRY_VECTOR_CTRL);
writel(ctrl | PCI_MSIX_ENTRY_CTRL_MASKBIT,
desc + PCI_MSIX_ENTRY_VECTOR_CTRL);
/* Program the vector */
writel(addr & (BIT_ULL(32) - 1), desc + PCI_MSIX_ENTRY_LOWER_ADDR);
writel(addr >> 32, desc + PCI_MSIX_ENTRY_UPPER_ADDR);
writel(data, desc + PCI_MSIX_ENTRY_DATA);
/* Unmask */
writel(ctrl & ~PCI_MSIX_ENTRY_CTRL_MASKBIT,
desc + PCI_MSIX_ENTRY_VECTOR_CTRL);
return 0;
}
void atl_fwd_release_event(struct atl_fwd_event *evt)
{
struct atl_fwd_ring *ring = evt->ring;
struct atl_nic *nic = ring->nic;
unsigned long *map = &nic->fwd.msi_map;
int idx = evt->idx;
if (ring->evt != evt) {
atl_nic_err("%s: attempt to release unset event\n", __func__);
return;
}
atl_fwd_disable_event(evt);
ring->evt = NULL;
if (evt->flags & ATL_FWD_EVT_TXWB)
return;
__clear_bit(idx, map);
atl_set_intr_bits(&nic->hw, ring->idx,
atl_fwd_ring_tx(ring) ? -1 : ATL_NUM_MSI_VECS,
atl_fwd_ring_tx(ring) ? ATL_NUM_MSI_VECS : -1);
}
EXPORT_SYMBOL(atl_fwd_release_event);
static int atl_fwd_init_event(struct atl_fwd_event *evt)
{
struct atl_fwd_ring *ring = evt->ring;
int dir_tx = atl_fwd_ring_tx(ring);
struct atl_nic *nic = ring->nic;
struct atl_hw *hw = &nic->hw;
bool tx_wb = !!(evt->flags & ATL_FWD_EVT_TXWB);
int idx;
int ret;
if (tx_wb) {
struct atl_hw_ring *hwring = &ring->hw;
atl_write(hw, ATL_TX_RING_HEAD_WB_LSW(hwring),
evt->tx_head_wrb);
atl_write(hw, ATL_TX_RING_HEAD_WB_MSW(hwring),
upper_32_bits(evt->tx_head_wrb));
return 0;
}
idx = evt->idx;
ret = atl_fwd_set_msix_vec(nic, evt);
if (ret)
return ret;
atl_set_intr_bits(&nic->hw, ring->idx,
dir_tx ? -1 : idx,
dir_tx ? idx : -1);
atl_write_bit(hw, ATL_INTR_AUTO_CLEAR, idx, 1);
atl_write_bit(hw, ATL_INTR_AUTO_MASK, idx,
!!(evt->flags & ATL_FWD_EVT_AUTOMASK));
return 0;
}
int atl_fwd_request_event(struct atl_fwd_event *evt)
{
struct atl_fwd_ring *ring = evt->ring;
struct atl_nic *nic = ring->nic;
unsigned long *map = &nic->fwd.msi_map;
bool tx_wb = !!(evt->flags & ATL_FWD_EVT_TXWB);
int idx;
int ret;
if (ring->evt) {
atl_nic_err("%s: event already set for %s ring %d\n",
__func__, atl_fwd_dir_str(ring), ring->idx);
return -EEXIST;
}
if (!tx_wb && !(nic->flags & ATL_FL_MULTIPLE_VECTORS)) {
atl_nic_err("%s: MSI-X interrupts are disabled\n", __func__);
return -EINVAL;
}
if (tx_wb && !atl_fwd_ring_tx(ring)) {
atl_nic_err("%s: head pointer writeback events supported "
"on Tx rings only\n", __func__);
return -EINVAL;
}
if ((evt->flags & (ATL_FWD_EVT_TXWB | ATL_FWD_EVT_AUTOMASK)) ==
(ATL_FWD_EVT_TXWB | ATL_FWD_EVT_AUTOMASK)) {
atl_nic_err("%s: event automasking supported "
"for MSI events only\n", __func__);
return -EINVAL;
}
ring->evt = evt;
if (tx_wb) {
ret = atl_fwd_init_event(evt);
if (ret)
goto fail;
return 0;
}
idx = find_next_zero_bit(map, ATL_NUM_MSI_VECS, ATL_FWD_MSI_BASE);
if (idx >= ATL_NUM_MSI_VECS) {
atl_nic_err("%s: no MSI vectors left\n", __func__);
ret = -ENOMEM;
goto fail;
}
evt->idx = idx;
ret = atl_fwd_init_event(evt);
if (ret)
goto fail;
__set_bit(idx, map);
return 0;
fail:
ring->evt = NULL;
return ret;
}
EXPORT_SYMBOL(atl_fwd_request_event);
int atl_fwd_enable_event(struct atl_fwd_event *evt)
{
struct atl_fwd_ring *ring = evt->ring;
struct atl_hw *hw = &ring->nic->hw;
if (evt->flags & ATL_FWD_EVT_TXWB) {
if (ring->state & ATL_FWR_ST_ENABLED)
return -EINVAL;
atl_write_bit(hw, ATL_TX_RING_CTL(&ring->hw), 28, 1);
ring->state |= ATL_FWR_ST_EVT_ENABLED;
return 0;
}
atl_intr_enable(hw, BIT(evt->idx));
ring->state |= ATL_FWR_ST_EVT_ENABLED;
return 0;
}
EXPORT_SYMBOL(atl_fwd_enable_event);
int atl_fwd_disable_event(struct atl_fwd_event *evt)
{
struct atl_fwd_ring *ring = evt->ring;
struct atl_hw *hw = &ring->nic->hw;
if (evt->flags & ATL_FWD_EVT_TXWB) {
if (ring->state & ATL_FWR_ST_ENABLED)
return -EINVAL;
atl_write_bit(hw, ATL_TX_RING_CTL(&ring->hw), 28, 0);
ring->state &= ~ATL_FWR_ST_EVT_ENABLED;
return 0;
}
atl_intr_disable(hw, BIT(evt->idx));
ring->state &= ~ATL_FWR_ST_EVT_ENABLED;
return 0;
}
EXPORT_SYMBOL(atl_fwd_disable_event);
int atl_fwd_receive_skb(struct net_device *ndev, struct sk_buff *skb)
{
struct atl_nic *nic = netdev_priv(ndev);
nic->stats.rx_fwd.packets++;
nic->stats.rx_fwd.bytes += skb->len;
skb->protocol = eth_type_trans(skb, ndev);
return netif_rx(skb);
}
EXPORT_SYMBOL(atl_fwd_receive_skb);
int atl_fwd_napi_receive_skb(struct net_device *ndev, struct sk_buff *skb)
{
struct atl_nic *nic = netdev_priv(ndev);
nic->stats.rx_fwd.packets++;
nic->stats.rx_fwd.bytes += skb->len;
skb->protocol = eth_type_trans(skb, ndev);
return netif_receive_skb(skb);
}
EXPORT_SYMBOL(atl_fwd_napi_receive_skb);
int atl_fwd_transmit_skb(struct net_device *ndev, struct sk_buff *skb)
{
skb->dev = ndev;
return dev_queue_xmit(skb);
}
EXPORT_SYMBOL(atl_fwd_transmit_skb);
int atl_fwd_register_notifier(struct net_device *ndev,
struct notifier_block *n)
{
struct atl_nic *nic = netdev_priv(ndev);
struct atl_fwd *fwd = &nic->fwd;
return blocking_notifier_chain_register(&fwd->nh_clients, n);
}
EXPORT_SYMBOL(atl_fwd_register_notifier);
int atl_fwd_unregister_notifier(struct net_device *ndev,
struct notifier_block *n)
{
struct atl_nic *nic = netdev_priv(ndev);
struct atl_fwd *fwd = &nic->fwd;
return blocking_notifier_chain_unregister(&fwd->nh_clients, n);
}
EXPORT_SYMBOL(atl_fwd_unregister_notifier);
void atl_fwd_notify(struct atl_nic *nic, enum atl_fwd_notify notif, void *data)
{
blocking_notifier_call_chain(&nic->fwd.nh_clients, notif, data);
}
int atl_fwd_reconfigure_rings(struct atl_nic *nic)
{
struct atl_fwd_ring *ring;
int i;
int ret;
for (i = 0; i < ATL_NUM_FWD_RINGS * ATL_FWDIR_NUM; i++) {
ring = nic->fwd.rings[i % ATL_FWDIR_NUM][i / ATL_FWDIR_NUM];
if (!ring)
continue;
atl_fwd_init_ring(ring);
if (ring->evt) {
ret = atl_fwd_init_event(ring->evt);
if (ret)
return ret;
if (ring->state & ATL_FWR_ST_EVT_ENABLED) {
ret = atl_fwd_enable_event(ring->evt);
if (ret)
return ret;
}
}
}
return 0;
}
int atl_fwd_suspend_rings(struct atl_nic *nic)
{
atl_fwd_notify(nic, ATL_FWD_NOTIFY_RESET_PREPARE, NULL);
return 0;
}
int atl_fwd_resume_rings(struct atl_nic *nic)
{
struct atl_fwd_ring *ring;
int i;
int ret;
ret = atl_fwd_reconfigure_rings(nic);
if (ret)
goto err;
atl_fwd_notify(nic, ATL_FWD_NOTIFY_RESET_COMPLETE, NULL);
for (i = 0; i < ATL_NUM_FWD_RINGS * ATL_FWDIR_NUM; i++) {
ring = nic->fwd.rings[i % ATL_FWDIR_NUM][i / ATL_FWDIR_NUM];
if (!ring)
continue;
if ((ring->state & ATL_FWR_ST_ENABLED))
atl_fwd_enable_ring(ring);
}
err:
return ret;
}

View file

@ -0,0 +1,408 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2018 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_FWD_H_
#define _ATL_FWD_H_
#include "atl_common.h"
/* Each incompatible API change bumps the API version */
#define ATL_FWD_API_VERSION 3
struct atl_fwd_event;
struct atl_fwd_buf_frag {
union {
void *buf; /* Valid when custom allocator is
* used */
struct page *page; /* Valid otherwise */
};
dma_addr_t daddr;
};
/**
* atl_fwd_rxbufs - offload engine's ring's Rx buffers
*
* Buffers are allocated by the driver when a ring is created
*
* The entire buffer space for the ring may optionally be
* allocated as a single physically-contiguous block.
*
* Descriptors are overwritten with the write-back descriptor
* format on Rx and optionally on Tx. To simplify Rx descriptor
* refill by the offload engine, vectors containing virtual
* addresses and DMA-addresses of each buffer are provided in
* @vaddr_vec and @daddr_vec respectively if
* @ATL_FWR_WANT_VIRT_BUF_VEC or @ATL_FWR_WANT_DMA_BUF_VEC
* flags are set on @atl_fwd_request_ring().
*
* If the resepective buffer vector request flags are not set,
* @daddr_vec_base contains the DMA address of the first buffer
* page and @vaddr_vec contains its virtual address.
*
* @daddr_vec_base: DMA address of the base of the @daddr_vec
* @daddr_vec: A vector of buffers' DMA ddresses
* @vaddr_vec: A vector of buffers' virtual addresses
* or first buffer's virtual address
* depending on ring flags
*/
struct atl_fwd_bufs {
dma_addr_t daddr_vec_base;
dma_addr_t *daddr_vec;
void **vaddr_vec;
/* The following is not part of API and subject to change */
int num_pages;
int order;
size_t frag_size;
struct atl_fwd_buf_frag frags[0];
};
union atl_desc;
/**
* atl_hw_ring - low leverl descriptor ring structure
*
* @descs: Pointer to the descriptor ring
* @size: Number of descriptors in the ring
* @reg_base: Offset of ring's register block from start of
* BAR 0
* @daddr: DMA address of the ring
*/
/* atl_hw_ring defined in "atl_hw.h" */
/**
*
* atl_fwd_mem_ops - memory management handlers for the ring
*
* Custom memory allocators may be provided to
* @atl_fwd_request_ring() to allocate the memory for the
* descriptor ring and buffers.
*
* Unless @ATL_FWR_DONT_DMA_MAP is included in flags on
* @atl_fwd_request_ring() call, the buffer allocator must also
* DMA-map the memory they allocate. If @ATL_FWR_DONT_DMA_MAP is
* set, @daddr parameter may be ignored by the buffer allocator
* and should be ignored by the deallocator.
*
* The ring allocator must DMA-map the memory unconditionally
* because the mapping is required to program the hardware ring
* registers.
*
* A private data pointer @private is reserved for offload
* engine's use. A pointer to the ops struct is provided to each
* allocator / deallocator so they can get to the private
* data. The @ops argument can be ignored by allocator /
* deallocator functions if access to private data is not needed.
*
* Either both allocator and deallocator or none of them must be
* provided for each memory type.
*
* Deallocator functions may ignore the size parameter.
*/
struct atl_fwd_mem_ops {
void *(*alloc_descs)(struct device *dev, size_t size, dma_addr_t *daddr,
gfp_t gfp, struct atl_fwd_mem_ops *ops);
void *(*alloc_buf)(struct device *dev, size_t size, dma_addr_t *daddr,
gfp_t gfp, struct atl_fwd_mem_ops *ops);
void (*free_descs)(void *buf, struct device *dev, size_t size,
dma_addr_t daddr, struct atl_fwd_mem_ops *ops);
void (*free_buf)(void *buf, struct device *dev, size_t size,
dma_addr_t daddr, struct atl_fwd_mem_ops *ops);
void *private;
};
/**
* atl_fwd_ring - Offload engine-controlled ring
*
* Buffer space is allocated by the driver on ring creation.
*
* @hw: Low-level ring information
* @evt: Ring's event, either an MSI-X vector (either
* Tx or Rx) or head pointer writeback address
* (Tx ring only). NULL on ring allocation, set
* by atl_fwd_request_event()
* @bufs: Ring's buffers. Allocated only if
* @ATL_FWR_ALLOC_BUFS flag is set on ring
* request.
* @nic: struct atl_nic backreference
* @idx: Ring index
* @mem_ops: Memory allocators provided at ring creation
*/
struct atl_fwd_ring {
struct atl_hw_ring hw;
struct atl_fwd_event *evt;
struct atl_fwd_bufs *bufs;
struct atl_nic *nic;
int idx;
struct atl_fwd_mem_ops *mem_ops;
/* The following is not part of API and subject to change */
unsigned int flags;
unsigned long state;
int buf_size;
unsigned intr_mod_min;
unsigned intr_mod_max;
};
enum atl_fwd_event_flags {
ATL_FWD_EVT_TXWB = BIT(0), /* Event type: 0 for MSI, 1 for Tx
* head WB */
ATL_FWD_EVT_AUTOMASK = BIT(1), /* Disable event after
* raising, MSI only. */
};
/**
* atl_fwd_event - Ring's notification event
*
* @flags Event type and flags
* @ring Ring backreference
* @msi_addr MSI message address
* @msi_data MSI message data
* @idx MSI index (0 .. 31)
* @tx_head_wrb Tx head writeback location
*/
struct atl_fwd_event {
enum atl_fwd_event_flags flags;
struct atl_fwd_ring *ring;
union {
struct {
dma_addr_t msi_addr;
uint32_t msi_data;
int idx;
};
dma_addr_t tx_head_wrb;
};
};
enum atl_fwd_ring_flags {
ATL_FWR_TX = BIT(0), /* Direction: 0 for Rx, 1 for Tx */
ATL_FWR_VLAN = BIT(1), /* Enable VLAN tag stripping / insertion */
ATL_FWR_LXO = BIT(2), /* Enable LRO / LSO */
ATL_FWR_ALLOC_BUFS = BIT(3), /* Allocate buffers */
ATL_FWR_CONTIG_BUFS = BIT(4), /* Alloc buffers as physically
* contiguous. May fail if
* total buffer space required
* is larger than a max-order
* compound page. */
ATL_FWR_WANT_VIRT_BUF_VEC = BIT(5), /* Alloc and fill
* per-buffer virtual address
* vectors. If unset, first
* buffer's vaddr is provided
* in ring's @vaddr_vec */
ATL_FWR_WANT_DMA_BUF_VEC = BIT(6), /* Alloc and fill
* per-buffer DMA address
* vectors. If unset, first
* buffer's daddr is provided
* in ring's
* @daddr_vec_base */
ATL_FWR_DONT_DMA_MAP = BIT(7), /* Don't DMA-map buffers, the
* caller will take care of
* that. Mutually exclusive
* with
* @ATL_FWR_WANT_DMA_BUF_VECS
* If an external buffer
* allocator is provided to
* @atl_fwd_request_ring()
* along with this flag, the
* allocator need not provide
* the DMA address. */
};
/**
* atl_fwd_request_ring() - Create a ring for an offload engine
*
* @ndev: network device
* @flags: ring flags
* @ring_size: number of descriptors
* @buf_size: individual buffer's size
* @page_order: page order to use when @ATL_FWR_CONTIG_BUFS is
* not set
* @mem_ops: optional custom memory allocators, may be NULL
*
* atl_fwd_request_ring() creates a ring for an offload engine,
* allocates buffer memory if @ATL_FWR_ALLOC_BUFS flag is set,
* initializes ring's registers, and, unless the @ATL_FWR_DONT_DMA_MAP
* flag is set, fills the address fields in descriptors. Ring is
* inactive until explicitly enabled via atl_fwd_enable_ring().
*
* Buffers can be allocated either as a single physically-contiguous
* compound page, or as a sequence of compound pages of @page_order
* order. In the latter case, depending on the requested buffer size,
* tweaking the page order allows to pack buffers into buffer pages
* with less wasted space.
*
* Returns the ring pointer on success, ERR_PTR(error code) on failure
*/
struct atl_fwd_ring *atl_fwd_request_ring(struct net_device *ndev,
int flags, int ring_size, int buf_size, int page_order,
struct atl_fwd_mem_ops *mem_ops);
/**
* atl_fwd_release_ring() - Free offload engine's ring
*
* @ring: ring to be freed
*
* Stops the ring, frees buffers if they were allocated, disables and
* releases ring's event if non-NULL, and frees the ring.
*/
void atl_fwd_release_ring(struct atl_fwd_ring *ring);
/**
* atl_fwd_set_ring_intr_mod() - Set ring's interrupt moderation
* delays
*
* @ring: ring
* @min: min delay (0 - 511 uS)
* @max: max delay (0 - 1023 uS)
*
* Each ring has two configurable interrupt moderation timers. When an
* interrupt condition occurs (write-back of the final descriptor of a
* packet on receive, or writeback on a transmit descriptor with WB
* bit set), the min timer is restarted unconditionally and max timer
* is started only if it's not running yet. When any of the timers
* expires, the interrupt is signalled.
*
* Thus if a single interrupt event occurs it will be subjected to min
* delay. If subsequent events keep occuring with intervals less than
* min_delay between each other, the interrupt will be triggered
* max_delay after the initial event.
*
* Delays are internally represented in units of 2 microseconds, so
* the values supplied are rounded down to an even value.
*
* When called with negative @min or @max, the corresponding setting
* is left unchanged.
*
* Interrupt moderation is only supported for MSI-X vectors, not head
* pointer writeback events.
*
* Returns 0 on success or -EINVAL on attempt to set moderation delays
* for a ring with attached Tx WB event or when a requested delay is
* out of range.
*/
int atl_fwd_set_ring_intr_mod(struct atl_fwd_ring *ring, int min, int max);
/**
* atl_fwd_enable_channel() - Enable offload engine's ring
*
* @ring: ring to be enabled
*
* Starts the ring. Returns 0 on success or negative error code.
*/
int atl_fwd_enable_ring(struct atl_fwd_ring *ring);
/**
* atl_fwd_disable_channel() - Disable offload engine's ring
*
* @ring: ring to be disabled
*
* Stops the ring.
*/
void atl_fwd_disable_ring(struct atl_fwd_ring *ring);
/**
* atl_fwd_request_event() - Creates and attaches a ring notification
* event
*
* @evt: event structure
*
* Caller must allocate a struct atl_fwd_event and fill the @flags,
* @ring and either @tx_head_wrb or @msi_addr and @msi_data depending
* on the type bit in @flags. Event is created in disabled state.
*
* For an MSI event type, an MSI vector table slot is
* allocated and programmed, and it's index is saved in @evt->idx.
*
* @evt is then attached to the ring.
*
* Returns 0 on success or negative error code.
*/
int atl_fwd_request_event(struct atl_fwd_event *evt);
/**
* atl_fwd_release_event() - Release a ring notification event
*
* @evt: event structure
*
* Disables the event if enabled, frees the MSI vector for an MSI-type
* event and detaches @evt from the ring. The @evt structure itself is
* not freed.
*/
void atl_fwd_release_event(struct atl_fwd_event *evt);
/**
* atl_fwd_enable_event() - Enable a ring event
*
* @evt: event structure
*
* Enables the event.
*
* Returns 0 on success or negative error code.
*/
int atl_fwd_enable_event(struct atl_fwd_event *evt);
/**
* atl_fwd_disable_event() - Disable a ring event
*
* @evt: event structure
*
* Disables the event.
*
* Returns 0 on success or negative error code.
*/
int atl_fwd_disable_event(struct atl_fwd_event *evt);
int atl_fwd_receive_skb(struct net_device *ndev, struct sk_buff *skb);
int atl_fwd_transmit_skb(struct net_device *ndev, struct sk_buff *skb);
/**
* atl_fwd_napi_receive_skb() - post skb to the network stack
*
* @ndev: network device
* @skb: buffer to post
*
* This function may only be called from softirq context and interrupts
* should be enabled.
*/
int atl_fwd_napi_receive_skb(struct net_device *ndev, struct sk_buff *skb);
/**
* atl_fwd_register_notifier() - Register notifier for reset of device
*
* @ndev: network device
* @n: notifier block
*
* Register for notification on reset of device. The notifier callback
* receives a pointer to the affected device. Notification callback is
* expected to be synchronous. The receiver shall perform the expected actions
* upon the notification according to the notification type.
*
* Returns 0 on success or negative error code.
*/
int atl_fwd_register_notifier(struct net_device *ndev,
struct notifier_block *n);
int atl_fwd_unregister_notifier(struct net_device *ndev,
struct notifier_block *n);
enum atl_fwd_notify {
ATL_FWD_NOTIFY_RESET_PREPARE, /* receiver shall stop traffic and */
/* disable rings */
ATL_FWD_NOTIFY_RESET_COMPLETE, /* receiver shall refill descriptors and */
/* enable rings */
ATL_FWD_NOTIFY_MACSEC_ON,
ATL_FWD_NOTIFY_MACSEC_OFF,
};
enum atl_fwd_ring_state {
ATL_FWR_ST_ENABLED = BIT(0),
ATL_FWR_ST_EVT_ENABLED = BIT(1),
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_FWDNL_H_
#define _ATL_FWDNL_H_
/* family name */
#define ATL_FWD_GENL_NAME "atl_fwd"
/* commands */
enum atlfwd_nl_command {
ATL_FWD_CMD_UNSPEC,
ATL_FWD_CMD_REQUEST_RING,
ATL_FWD_CMD_RELEASE_RING,
ATL_FWD_CMD_ENABLE_RING,
ATL_FWD_CMD_DISABLE_RING,
ATL_FWD_CMD_DISABLE_REDIRECTIONS,
ATL_FWD_CMD_FORCE_ICMP_TX_VIA,
ATL_FWD_CMD_FORCE_TX_VIA,
ATL_FWD_CMD_RING_STATUS,
ATL_FWD_CMD_DUMP_RING,
ATL_FWD_CMD_SET_TX_BUNCH,
ATL_FWD_CMD_REQUEST_EVENT,
ATL_FWD_CMD_RELEASE_EVENT,
ATL_FWD_CMD_ENABLE_EVENT,
ATL_FWD_CMD_DISABLE_EVENT,
ATL_FWD_CMD_GET_RX_QUEUE,
ATL_FWD_CMD_GET_TX_QUEUE,
/* keep last */
NUM_ATL_FWD_CMD,
ATL_FWD_CMD_MAX = NUM_ATL_FWD_CMD - 1
};
enum atlfwd_nl_attribute {
ATL_FWD_ATTR_INVALID,
ATL_FWD_ATTR_IFNAME,
/* REQUEST_RING attributes */
ATL_FWD_ATTR_FLAGS,
ATL_FWD_ATTR_RING_SIZE,
ATL_FWD_ATTR_BUF_SIZE,
ATL_FWD_ATTR_PAGE_ORDER,
/* RELEASE_RING attributes */
ATL_FWD_ATTR_RING_INDEX,
/* ENABLE_RING / DISABLE_RING use RING_INDEX attribute above */
/* RING_STATUS reply attributes */
ATL_FWD_ATTR_RING_STATUS,
ATL_FWD_ATTR_RING_IS_TX,
ATL_FWD_ATTR_RING_FLAGS,
/* ATL_FWD_CMD_SET_TX_BUNCH atributes */
ATL_FWD_ATTR_TX_BUNCH_SIZE,
/* ATL_FWD_CMD_GET_(RX|TX)_QUEUE attributes */
ATL_FWD_ATTR_QUEUE_INDEX,
/* keep last */
NUM_ATL_FWD_ATTR,
ATL_FWD_ATTR_MAX = NUM_ATL_FWD_ATTR - 1
};
enum atlfwd_nl_ring_status {
ATL_FWD_RING_STATUS_INVALID,
ATL_FWD_RING_STATUS_RELEASED,
ATL_FWD_RING_STATUS_CREATED_DISABLED,
ATL_FWD_RING_STATUS_ENABLED,
/* keep last */
NUM_ATL_FWD_RING_STATUS,
ATL_FWD_RING_STATUS_MAX = NUM_ATL_FWD_RING_STATUS - 1
};
#ifdef __KERNEL__
#include <linux/netdevice.h>
#include <linux/version.h>
struct atl_fwd_ring;
struct atl_desc_ring;
int atlfwd_nl_init(void);
void atlfwd_nl_on_probe(struct net_device *ndev);
void atlfwd_nl_on_remove(struct net_device *ndev);
int atlfwd_nl_on_open(struct net_device *ndev);
void atlfwd_nl_exit(void);
netdev_tx_t atlfwd_nl_xmit(struct sk_buff *skb, struct net_device *ndev);
u16 atlfwd_nl_select_queue_fallback(struct net_device *dev, struct sk_buff *skb,
struct net_device *arg3,
select_queue_fallback_t fallback);
bool atlfwd_nl_is_tx_fwd_ring_created(struct net_device *ndev,
const int fwd_ring_index);
bool atlfwd_nl_is_rx_fwd_ring_created(struct net_device *ndev,
const int fwd_ring_index);
struct atl_fwd_ring *atlfwd_nl_get_fwd_ring(struct net_device *ndev,
const int ring_index);
struct atl_desc_ring *atlfwd_nl_get_fwd_ring_desc(struct atl_fwd_ring *ring);
bool is_atlfwd_device(const struct net_device *dev);
#endif
#endif

View file

@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include "atl_common.h"
unsigned int atlfwd_nl_tx_clean_threshold_msec = 200;
unsigned int atlfwd_nl_tx_clean_threshold_frac = 8;
bool atlfwd_nl_speculative_queue_stop = true;
unsigned int altfwd_nl_rx_poll_interval_msec = 200;
int atlfwd_nl_rx_clean_budget = NAPI_POLL_WEIGHT;
typedef void (*for_each_func_t)(struct net_device *ndev);
static void for_each_atlfwd_device(for_each_func_t f)
{
struct net_device *ndev;
struct net *ns;
rcu_read_lock();
for_each_net_rcu (ns) {
for_each_netdev_rcu (ns, ndev) {
if (!is_atlfwd_device(ndev))
continue;
f(ndev);
}
}
rcu_read_unlock();
}
static void force_tx_housekeeping(struct net_device *ndev)
{
struct atl_nic *nic = netdev_priv(ndev);
/* Schedule for ASAP execution */
schedule_delayed_work(nic->fwdnl.tx_cleanup_wq, 0);
}
static void force_rx_polling(struct net_device *ndev)
{
struct atl_desc_ring *desc = NULL;
struct atl_fwd_ring *ring = NULL;
int i;
for (i = 0; i != ATL_NUM_FWD_RINGS; i++) {
if (!atlfwd_nl_is_rx_fwd_ring_created(ndev, i))
continue;
ring = atlfwd_nl_get_fwd_ring(ndev, i);
if (unlikely(!(ring->state & ATL_FWR_ST_ENABLED)))
continue;
desc = atlfwd_nl_get_fwd_ring_desc(ring);
if (likely(desc != NULL && desc->rx_poll_timer != NULL)) {
desc->rx_poll_timer->expires =
jiffies +
msecs_to_jiffies(
altfwd_nl_rx_poll_interval_msec);
add_timer(desc->rx_poll_timer);
}
}
}
static int tx_clean_threshold_msec_set(const char *val,
const struct kernel_param *kp)
{
unsigned int msec;
int ret = kstrtouint(val, 10, &msec);
if (unlikely(ret != 0))
return -EINVAL;
if (msec != 0 && atlfwd_nl_tx_clean_threshold_msec == 0) {
/* Force extra TX housekeeping, if threshold is re-enabled */
for_each_atlfwd_device(force_tx_housekeeping);
}
return param_set_uint(val, kp);
}
static const struct kernel_param_ops fwdnl_tx_clean_threshold_msec_ops = {
.set = tx_clean_threshold_msec_set,
.get = param_get_uint,
};
static int tx_clean_threshold_frac_set(const char *val,
const struct kernel_param *kp)
{
unsigned int frac;
int ret = kstrtouint(val, 10, &frac);
if (unlikely(ret != 0))
return -EINVAL;
if (frac != 0 && atlfwd_nl_tx_clean_threshold_frac == 0) {
/* Force extra TX housekeeping, if threshold is re-enabled */
for_each_atlfwd_device(force_tx_housekeeping);
}
return param_set_uint(val, kp);
}
static const struct kernel_param_ops fwdnl_tx_clean_threshold_frac_ops = {
.set = tx_clean_threshold_frac_set,
.get = param_get_uint,
};
static int rx_poll_interval_msec_set(const char *val,
const struct kernel_param *kp)
{
unsigned int msec;
int ret = kstrtouint(val, 10, &msec);
if (unlikely(ret != 0))
return -EINVAL;
if (msec != 0 && altfwd_nl_rx_poll_interval_msec == 0) {
/* Kick off RX polling, if polling is re-enabled */
for_each_atlfwd_device(force_rx_polling);
}
return param_set_uint(val, kp);
}
static const struct kernel_param_ops fwdnl_rx_poll_interval_msec_ops = {
.set = rx_poll_interval_msec_set,
.get = param_get_uint,
};
module_param_cb(fwdnl_tx_clean_threshold_msec,
&fwdnl_tx_clean_threshold_msec_ops,
&atlfwd_nl_tx_clean_threshold_msec, 0644);
module_param_cb(fwdnl_tx_clean_threshold_frac,
&fwdnl_tx_clean_threshold_frac_ops,
&atlfwd_nl_tx_clean_threshold_frac, 0644);
module_param_named(fwdnl_speculative_queue_stop,
atlfwd_nl_speculative_queue_stop, bool, 0644);
module_param_cb(fwdnl_rx_poll_interval_msec, &fwdnl_rx_poll_interval_msec_ops,
&altfwd_nl_rx_poll_interval_msec, 0644);
module_param_named(fwdnl_rx_clean_budget, atlfwd_nl_rx_clean_budget, int, 0644);

View file

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_FWDNL_PARAMS_H_
#define _ATL_FWDNL_PARAMS_H_
extern unsigned int atlfwd_nl_tx_clean_threshold_msec;
extern unsigned int atlfwd_nl_tx_clean_threshold_frac;
extern bool atlfwd_nl_speculative_queue_stop;
extern unsigned int altfwd_nl_rx_poll_interval_msec;
extern int atlfwd_nl_rx_clean_budget;
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,382 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_HW_H_
#define _ATL_HW_H_
#include <linux/compiler.h>
#include <linux/if_ether.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include "atl_fw.h"
#include "atl_macsec.h"
#include "atl_regs.h"
union atl_desc;
struct atl_nic;
#define PCI_VENDOR_ID_AQUANTIA 0x1d6a
/* clock is 3.2 ns*/
#define ATL_HW_CLOCK_TO_US(clk) (clk * 32 / 10000)
#define ATL2_ACTION(ACTION, RSS, INDEX, VALID, TS_VALID) \
((((ACTION & 0x3U) << 8) | \
((RSS & 0x1U) << 7) | \
((INDEX & 0x3FU) << 2) | \
((TS_VALID & 0x1U) << 1)) | \
((VALID & 0x1U) << 0))
#define ATL2_ACTION_DROP ATL2_ACTION(0, 0, 0, 1, 0)
#define ATL2_ACTION_DISABLE ATL2_ACTION(0, 0, 0, 0, 0)
#define ATL2_ACTION_ASSIGN_QUEUE(QUEUE) ATL2_ACTION(1, 0, (QUEUE), 1, 0)
#define ATL2_ACTION_ASSIGN_TC(TC) ATL2_ACTION(1, 1, (TC), 1, 0)
enum {
ATL2_RPF_L2_PROMISC_OFF_INDEX = 0,
ATL2_RPF_VLAN_PROMISC_OFF_INDEX,
ATL2_RPF_L3L4_USER_INDEX,
ATL2_RPF_ET_PCP_USER_INDEX = ATL2_RPF_L3L4_USER_INDEX + 16,
ATL2_RPF_VLAN_USER_INDEX = ATL2_RPF_ET_PCP_USER_INDEX + 16,
ATL2_RPF_FLEX_USER_INDEX = ATL2_RPF_VLAN_USER_INDEX + 16,
ATL2_RPF_DEFAULT_RULE_INDEX = ATL2_RPF_FLEX_USER_INDEX + 1,
};
#define ATL2_RPF_TAG_UC_OFFSET 0x0
#define ATL2_RPF_TAG_ALLMC_OFFSET 0x6
#define ATL2_RPF_TAG_ET_OFFSET 0x7
#define ATL2_RPF_TAG_VLAN_OFFSET 0xA
#define ATL2_RPF_TAG_UNTAG_OFFSET 0xE
#define ATL2_RPF_TAG_L3_V4_OFFSET 0xF
#define ATL2_RPF_TAG_L3_V6_OFFSET 0x12
#define ATL2_RPF_TAG_L4_OFFSET 0x15
#define ATL2_RPF_TAG_L4_FLEX_OFFSET 0x18
#define ATL2_RPF_TAG_FLEX_OFFSET 0x1B
#define ATL2_RPF_TAG_PCP_OFFSET 0x1D
#define ATL2_RPF_TAG_UC_MASK (0x0000003F << ATL2_RPF_TAG_UC_OFFSET)
#define ATL2_RPF_TAG_ALLMC_MASK (0x00000001 << ATL2_RPF_TAG_ALLMC_OFFSET)
#define ATL2_RPF_TAG_UNTAG_MASK (0x00000001 << ATL2_RPF_TAG_UNTAG_OFFSET)
#define ATL2_RPF_TAG_VLAN_MASK (0x0000000F << ATL2_RPF_TAG_VLAN_OFFSET)
#define ATL2_RPF_TAG_ET_MASK (0x00000007 << ATL2_RPF_TAG_ET_OFFSET)
#define ATL2_RPF_TAG_L3_V4_MASK (0x00000007 << ATL2_RPF_TAG_L3_V4_OFFSET)
#define ATL2_RPF_TAG_L3_V6_MASK (0x00000007 << ATL2_RPF_TAG_L3_V6_OFFSET)
#define ATL2_RPF_TAG_L4_MASK (0x00000007 << ATL2_RPF_TAG_L4_OFFSET)
#define ATL2_RPF_TAG_FLEX_MASK (0x00000003 << ATL2_RPF_TAG_FLEX_OFFSET)
#define ATL2_RPF_TAG_PCP_MASK (0x00000007 << ATL2_RPF_TAG_PCP_OFFSET)
#define ATL2_RPF_TAG_BASE_BC (1 << ATL2_RPF_TAG_UC_OFFSET)
#define ATL2_RPF_TAG_BASE_UC (2 << ATL2_RPF_TAG_UC_OFFSET)
#define ATL2_FW_HOSTLOAD_REQ_LEN_MAX 0x1000
#define busy_wait(tries, wait, lvalue, fetch, cond) \
({ \
uint32_t _dummy = 0; \
int i = (tries); \
int orig = i; \
(void)_dummy; \
do { \
wait; \
(lvalue) = (fetch); \
} while ((cond) && --i); \
(orig - i); \
})
enum atl_chip {
ATL_UNKNOWN,
ATL_ATLANTIC,
ATL_ANTIGUA,
};
struct atl_thermal {
unsigned flags;
uint8_t crit;
uint8_t high;
uint8_t low;
};
extern struct atl_thermal atl_def_thermal;
enum atl_nic_state {
ATL_ST_ENABLED,
ATL_ST_CONFIGURED,
ATL_ST_RINGS_RUNNING,
/* ATL_ST_FWD_RINGS_RUNNING, */
ATL_ST_UP,
ATL_ST_WORK_SCHED,
ATL_ST_UPDATE_LINK,
ATL_ST_RESETTING,
ATL_ST_RESET_NEEDED,
ATL_ST_GLOBAL_CONF_NEEDED,
ATL_ST_START_NEEDED,
ATL_ST_DETACHED,
};
#define ATL_WAKE_SUPPORTED (WAKE_MAGIC | WAKE_PHY)
struct atl_hw {
atomic_t flags;
uint8_t __iomem *regs;
struct pci_dev *pdev;
unsigned long state;
enum atl_chip chip_id;
u32 chip_rev;
bool new_rpf;
struct atl_link_state link_state;
uint32_t lpi_timer;
unsigned wol_mode;
struct atl_mcp mcp;
uint32_t non_ring_intr_mask;
uint8_t mac_addr[ETH_ALEN];
#define ATL_RSS_KEY_SIZE 40
uint8_t rss_key[ATL_RSS_KEY_SIZE];
#define ATL_RSS_TBL_SIZE (1 << 6)
uint8_t rss_tbl[ATL_RSS_TBL_SIZE];
struct atl_thermal thermal;
#if IS_ENABLED(CONFIG_MACSEC) && defined(NETIF_F_HW_MACSEC)
struct atl_macsec_cfg macsec_cfg;
#endif
s64 ptp_clk_offset;
int art_base_index;
int art_available;
};
struct atl_hw_ring {
union atl_desc *descs;
uint32_t size;
uint32_t reg_base;
dma_addr_t daddr;
};
enum mcp_area {
MCP_AREA_CONFIG = 0x80000000,
MCP_AREA_SETTINGS = 0x20000000,
};
#define offset_ptr(ptr, ring, amount) \
({ \
struct atl_hw_ring *hw_ring = (ring); \
uint32_t size = hw_ring->size; \
\
uint32_t res = (ptr) + (amount); \
if ((int32_t)res < 0) \
res += size; \
else if (res >= size) \
res -= size; \
res; \
})
void atl_check_unplug(struct atl_hw *hw, uint32_t addr);
static inline uint32_t atl_read(struct atl_hw *hw, uint32_t addr)
{
uint8_t __iomem *base = READ_ONCE(hw->regs);
uint32_t val = 0xffffffff;
if (unlikely(!base))
return val;
val = readl(base + addr);
if (unlikely(val == 0xffffffff))
atl_check_unplug(hw, addr);
return val;
}
static inline void atl_write(struct atl_hw *hw, uint32_t addr, uint32_t val)
{
uint8_t __iomem *base = READ_ONCE(hw->regs);
if (unlikely(!base))
return;
writel(val, base + addr);
}
static inline void atl_write_mask_bits(struct atl_hw *hw, uint32_t addr,
uint32_t mask, uint32_t val)
{
atl_write(hw, addr,
(atl_read(hw, addr) & ~mask) | (val & mask));
}
static inline void atl_write_bits(struct atl_hw *hw, uint32_t addr,
uint32_t shift, uint32_t width, uint32_t val)
{
uint32_t mask = ((1u << width) - 1) << shift;
atl_write(hw, addr,
(atl_read(hw, addr) & ~mask) | ((val << shift) & mask));
}
static inline void atl_write_bit(struct atl_hw *hw, uint32_t addr,
uint32_t shift, uint32_t val)
{
atl_write_bits(hw, addr, shift, 1, val);
}
static inline void atl_set_bits(struct atl_hw *hw, uint32_t addr,
uint32_t bits)
{
atl_write(hw, addr, atl_read(hw, addr) | bits);
}
static inline void atl_clear_bits(struct atl_hw *hw, uint32_t addr,
uint32_t bits)
{
atl_write(hw, addr, atl_read(hw, addr) & ~bits);
}
static inline void atl_intr_enable(struct atl_hw *hw, uint32_t mask)
{
atl_write(hw, ATL_INTR_MSK_SET, mask);
}
static inline void atl_intr_disable(struct atl_hw *hw, uint32_t mask)
{
atl_write(hw, ATL_INTR_MSK_CLEAR, mask);
}
static inline void atl_intr_disable_all(struct atl_hw *hw)
{
atl_intr_disable(hw, 0xffffffff);
}
static inline unsigned atl_fw_major(struct atl_hw *hw)
{
return (hw->mcp.fw_rev >> 24) & 0xff;
}
static inline void atl_init_rss_table(struct atl_hw *hw, int nvecs)
{
int i;
for (i = 0; i < ATL_RSS_TBL_SIZE; i++)
hw->rss_tbl[i] = i % nvecs;
}
int atl2_act_rslvr_table_set(struct atl_hw *hw, u8 location,
u32 tag, u32 mask, u32 action);
static inline void atl2_rpf_vlan_flr_tag_set(struct atl_hw *hw, u32 tag,
u32 filter)
{
atl_write_bits(hw, ATL_RX_VLAN_FLT(filter), 12, 4, tag);
}
static inline void atl2_rpf_etht_flr_tag_set(struct atl_hw *hw, u32 tag,
u32 filter)
{
atl_write_bits(hw, ATL2_RX_ETYPE_TAG(filter), 0, 3, tag);
}
static inline void atl2_rpf_l3_v4_da_set(struct atl_hw *hw, u32 filter, u32 val)
{
u32 dword = filter % 4;
u32 addr_set = 6 + ((filter < 4) ? (0) : (1));
atl_write(hw, ATL2_RPF_L3_DA(addr_set) + 4 * dword, swab32(val));
}
static inline void atl2_rpf_l3_v4_sa_set(struct atl_hw *hw, u32 filter, u32 val)
{
u32 dword = filter % 4;
u32 addr_set = 6 + ((filter < 4) ? (0) : (1));
atl_write(hw, ATL2_RPF_L3_SA(addr_set) + 4 * dword, swab32(val));
}
static inline void atl2_rpf_l3_v6_sa_set(struct atl_hw *hw, u32 filter,
u32 val[4])
{
int i;
for (i = 0; i < 4; i++)
atl_write(hw, ATL2_RPF_L3_SA(filter) + 4 * i, swab32(val[i]));
}
static inline void atl2_rpf_l3_v6_da_set(struct atl_hw *hw, u32 filter,
u32 val[4])
{
int i;
for (i = 0; i < 4; i++)
atl_write(hw, ATL2_RPF_L3_DA(filter) + 4 * i, swab32(val[i]));
}
static inline void atl2_rpf_flex_flr_tag_set(struct atl_hw *hw, u32 tag,
u32 filter)
{
atl_write_bits(hw, ATL_RX_FLEX_FLT_CTRL(filter), 0x19, 2, tag);
}
int atl_read_mcp_mem(struct atl_hw *hw, uint32_t mcp_addr, void *host_addr,
unsigned size);
int atl_hwinit(struct atl_hw *hw, enum atl_chip chip_id);
void atl_refresh_link(struct atl_nic *nic);
void atl_set_rss_key(struct atl_hw *hw);
int atl_set_rss_tbl(struct atl_hw *hw);
void atl_set_uc_flt(struct atl_hw *hw, int idx, uint8_t mac_addr[ETH_ALEN]);
int atl_alloc_descs(struct atl_nic *nic, struct atl_hw_ring *ring, size_t extra);
void atl_free_descs(struct atl_nic *nic, struct atl_hw_ring *ring, size_t extra);
void atl_set_intr_bits(struct atl_hw *hw, int idx, int rxbit, int txbit);
int atl_alloc_link_intr(struct atl_nic *nic);
void atl_free_link_intr(struct atl_nic *nic);
int atl_write_mcp_mem(struct atl_hw *hw, uint32_t offt, void *addr,
size_t size, enum mcp_area area);
static inline int atl_write_fwcfg_word(struct atl_hw *hw, uint32_t offt,
uint32_t val)
{
return atl_write_mcp_mem(hw, offt, &val, sizeof(val), MCP_AREA_CONFIG);
}
static inline int atl_write_fwsettings_word(struct atl_hw *hw, uint32_t offt,
uint32_t val)
{
return atl_write_mcp_mem(hw, offt, &val, sizeof(val), MCP_AREA_SETTINGS);
}
static inline int atl_read_fwstat_word(struct atl_hw *hw, uint32_t offt,
uint32_t *val)
{
return atl_read_mcp_word(hw, offt + hw->mcp.fw_stat_addr, val);
}
static inline int atl_read_rpc_mem(struct atl_hw *hw, uint32_t offt,
uint32_t *val, size_t length)
{
return atl_read_mcp_mem(hw, offt + hw->mcp.rpc_addr, val, length);
}
static inline int atl_read_fwsettings_word(struct atl_hw *hw, uint32_t offt,
uint32_t *val)
{
if (offt >= hw->mcp.fw_settings_len)
return -EINVAL;
return atl_read_mcp_word(hw, offt + hw->mcp.fw_settings_addr, val);
}
static inline void atl_lock_fw(struct atl_hw *hw)
{
mutex_lock(&hw->mcp.lock);
}
static inline void atl_unlock_fw(struct atl_hw *hw)
{
mutex_unlock(&hw->mcp.lock);
}
void atl_fw_watchdog(struct atl_hw *hw);
#endif

View file

@ -0,0 +1,247 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/mdio.h>
#include "atl_hw_ptp.h"
#include "atl_desc.h"
#include "atl_mdio.h"
#define FRAC_PER_NS 0x100000000LL
#define ATL_HW_MAC_COUNTER_HZ 312500000ll
#define ATL_HW_PHY_COUNTER_HZ 160000000ll
/* register address for bitfield PTP Digital Clock Read Enable */
#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_ADR 0x00004628
/* lower bit position of bitfield PTP Digital Clock Read Enable */
#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_SHIFT 4
/* width of bitfield PTP Digital Clock Read Enable */
#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_WIDTH 1
/* register address for ptp counter reading */
#define HW_ATL_PCS_PTP_TS_VAL_ADDR(index) (0x00004900 + (index) * 0x4)
static void hw_atl_pcs_ptp_clock_read_enable(struct atl_hw *hw,
u32 ptp_clock_read_enable)
{
atl_write_bits(hw, HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_ADR,
HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_SHIFT,
HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_WIDTH,
ptp_clock_read_enable);
}
static u32 hw_atl_pcs_ptp_clock_get(struct atl_hw *hw, u32 index)
{
return atl_read(hw, HW_ATL_PCS_PTP_TS_VAL_ADDR(index));
}
#define get_ptp_ts_val_u64(self, indx) \
((u64)(hw_atl_pcs_ptp_clock_get(self, indx) & 0xffff))
void hw_atl_get_ptp_ts(struct atl_hw *hw, u64 *stamp)
{
u64 ns;
hw_atl_pcs_ptp_clock_read_enable(hw, 1);
hw_atl_pcs_ptp_clock_read_enable(hw, 0);
ns = (get_ptp_ts_val_u64(hw, 0) +
(get_ptp_ts_val_u64(hw, 1) << 16)) * NSEC_PER_SEC +
(get_ptp_ts_val_u64(hw, 3) +
(get_ptp_ts_val_u64(hw, 4) << 16));
*stamp = ns + hw->ptp_clk_offset;
}
static void hw_atl_adj_params_get(u64 freq, s64 adj, u32 *ns, u32 *fns)
{
/* For accuracy, the digit is extended */
s64 base_ns = ((adj + NSEC_PER_SEC) * NSEC_PER_SEC);
u64 nsi_frac = 0;
u64 nsi;
base_ns = div64_s64(base_ns, freq);
nsi = div64_u64(base_ns, NSEC_PER_SEC);
if (base_ns != nsi * NSEC_PER_SEC) {
s64 divisor = div64_s64((s64)NSEC_PER_SEC * NSEC_PER_SEC,
base_ns - nsi * NSEC_PER_SEC);
nsi_frac = div64_s64(FRAC_PER_NS * NSEC_PER_SEC, divisor);
}
*ns = (u32)nsi;
*fns = (u32)nsi_frac;
}
static void
hw_atl_mac_adj_param_calc(struct ptp_adj_freq *ptp_adj_freq, u64 phyfreq,
u64 macfreq)
{
s64 adj_fns_val;
s64 fns_in_sec_phy = phyfreq * (ptp_adj_freq->fns_phy +
FRAC_PER_NS * ptp_adj_freq->ns_phy);
s64 fns_in_sec_mac = macfreq * (ptp_adj_freq->fns_mac +
FRAC_PER_NS * ptp_adj_freq->ns_mac);
s64 fault_in_sec_phy = FRAC_PER_NS * NSEC_PER_SEC - fns_in_sec_phy;
s64 fault_in_sec_mac = FRAC_PER_NS * NSEC_PER_SEC - fns_in_sec_mac;
/* MAC MCP counter freq is macfreq / 4 */
s64 diff_in_mcp_overflow = (fault_in_sec_mac - fault_in_sec_phy) *
4 * FRAC_PER_NS;
diff_in_mcp_overflow = div64_s64(diff_in_mcp_overflow,
ATL_HW_MAC_COUNTER_HZ);
adj_fns_val = (ptp_adj_freq->fns_mac + FRAC_PER_NS *
ptp_adj_freq->ns_mac) + diff_in_mcp_overflow;
ptp_adj_freq->mac_ns_adj = div64_s64(adj_fns_val, FRAC_PER_NS);
ptp_adj_freq->mac_fns_adj = adj_fns_val - ptp_adj_freq->mac_ns_adj *
FRAC_PER_NS;
}
int hw_atl_adj_sys_clock(struct atl_hw *hw, s64 delta)
{
hw->ptp_clk_offset += delta;
atl_write(hw, ATL_RX_SPARE_CTRL0, lower_32_bits(hw->ptp_clk_offset));
atl_write(hw, ATL_RX_SPARE_CTRL1, upper_32_bits(hw->ptp_clk_offset));
return 0;
}
int hw_atl_ts_to_sys_clock(struct atl_hw *hw, u64 ts, u64 *time)
{
*time = hw->ptp_clk_offset + ts;
return 0;
}
int hw_atl_adj_clock_freq(struct atl_hw *hw, s32 ppb)
{
struct ptp_msg_fw_request fwreq;
struct atl_mcp *mcp = &hw->mcp;
memset(&fwreq, 0, sizeof(fwreq));
fwreq.msg_id = ptp_adj_freq_msg;
hw_atl_adj_params_get(ATL_HW_MAC_COUNTER_HZ, ppb,
&fwreq.adj_freq.ns_mac,
&fwreq.adj_freq.fns_mac);
hw_atl_adj_params_get(ATL_HW_PHY_COUNTER_HZ, ppb,
&fwreq.adj_freq.ns_phy,
&fwreq.adj_freq.fns_phy);
hw_atl_mac_adj_param_calc(&fwreq.adj_freq,
ATL_HW_PHY_COUNTER_HZ,
ATL_HW_MAC_COUNTER_HZ);
return mcp->ops->send_ptp_req(hw, &fwreq);
}
int hw_atl_gpio_pulse(struct atl_hw *hw, u32 index, u64 start, u32 period)
{
struct ptp_msg_fw_request fwreq;
struct atl_mcp *mcp = &hw->mcp;
memset(&fwreq, 0, sizeof(fwreq));
fwreq.msg_id = ptp_gpio_ctrl_msg;
fwreq.gpio_ctrl.index = index;
fwreq.gpio_ctrl.period = period;
/* Apply time offset */
fwreq.gpio_ctrl.start = start;
return mcp->ops->send_ptp_req(hw, &fwreq);
}
int hw_atl_extts_gpio_enable(struct atl_hw *hw, u32 index, u32 enable)
{
/* Enable/disable Sync1588 GPIO Timestamping */
return atl_mdio_write(hw, 0, MDIO_MMD_PCS, 0xc611, enable ? 0x71 : 0);
}
int hw_atl_get_sync_ts(struct atl_hw *hw, u64 *ts)
{
u16 nsec_l = 0;
u16 nsec_h = 0;
u16 sec_l = 0;
u16 sec_h = 0;
int ret;
if (!ts)
return -1;
/* PTP external GPIO clock seconds count 15:0 */
ret = atl_mdio_read(hw, 0, MDIO_MMD_PCS, 0xc914, &sec_l);
/* PTP external GPIO clock seconds count 31:16 */
if (!ret)
ret = atl_mdio_read(hw, 0, MDIO_MMD_PCS, 0xc915, &sec_h);
/* PTP external GPIO clock nanoseconds count 15:0 */
if (!ret)
ret = atl_mdio_read(hw, 0, MDIO_MMD_PCS, 0xc916, &nsec_l);
/* PTP external GPIO clock nanoseconds count 31:16 */
if (!ret)
ret = atl_mdio_read(hw, 0, MDIO_MMD_PCS, 0xc917, &nsec_h);
*ts = ((u64)nsec_h << 16) + nsec_l + (((u64)sec_h << 16) + sec_l) * NSEC_PER_SEC;
return ret;
}
u16 hw_atl_rx_extract_ts(struct atl_hw *hw, u8 *p, unsigned int len,
u64 *timestamp)
{
unsigned int offset = 14;
struct ethhdr *eth;
u64 sec;
u8 *ptr;
u32 ns;
if (len <= offset || !timestamp)
return 0;
/* The TIMESTAMP in the end of package has following format:
* (big-endian)
* struct {
* uint64_t sec;
* uint32_t ns;
* uint16_t stream_id;
* };
*/
ptr = p + (len - offset);
memcpy(&sec, ptr, sizeof(sec));
ptr += sizeof(sec);
memcpy(&ns, ptr, sizeof(ns));
sec = be64_to_cpu(sec) & 0xffffffffffffllu;
ns = be32_to_cpu(ns);
*timestamp = sec * NSEC_PER_SEC + ns + hw->ptp_clk_offset;
eth = (struct ethhdr *)p;
return (eth->h_proto == htons(ETH_P_1588)) ? 12 : 14;
}
void hw_atl_extract_hwts(struct atl_hw *hw, struct atl_rx_desc_hwts_wb *hwts_wb,
u64 *timestamp)
{
u64 tmp, sec, ns;
sec = 0;
tmp = hwts_wb->sec_lw0 & 0x3ff;
sec += tmp;
tmp = (u64)((hwts_wb->sec_lw1 >> 16) & 0xffff) << 10;
sec += tmp;
tmp = (u64)(hwts_wb->sec_hw & 0xfff) << 26;
sec += tmp;
tmp = (u64)((hwts_wb->sec_hw >> 22) & 0x3ff) << 38;
sec += tmp;
ns = sec * NSEC_PER_SEC + hwts_wb->ns;
if (timestamp)
*timestamp = ns + hw->ptp_clk_offset;
}

View file

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_HW_PTP_H_
#define _ATL_HW_PTP_H_
#include "atl_hw.h"
struct atl_rx_desc_hwts_wb;
void hw_atl_get_ptp_ts(struct atl_hw *hw, u64 *stamp);
int hw_atl_adj_sys_clock(struct atl_hw *hw, s64 delta);
int hw_atl_ts_to_sys_clock(struct atl_hw *hw, u64 ts, u64 *time);
int hw_atl_adj_clock_freq(struct atl_hw *hw, s32 ppb);
int hw_atl_gpio_pulse(struct atl_hw *hw, u32 index, u64 start, u32 period);
int hw_atl_extts_gpio_enable(struct atl_hw *hw, u32 index, u32 enable);
int hw_atl_get_sync_ts(struct atl_hw *hw, u64 *ts);
u16 hw_atl_rx_extract_ts(struct atl_hw *hw, u8 *p, unsigned int len,
u64 *timestamp);
void hw_atl_extract_hwts(struct atl_hw *hw, struct atl_rx_desc_hwts_wb *hwts_wb,
u64 *timestamp);
#endif

View file

@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2018 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "atl_common.h"
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(4,10,0)
static ssize_t atl_hwmon_set_flag(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
struct atl_hw *hw = dev_get_drvdata(dev);
bool val;
int ret;
if (strtobool(buf, &val) < 0)
return -EINVAL;
ret = atl_update_thermal_flag(hw, sattr->index, val);
return ret ?: size;
}
static ssize_t atl_hwmon_show_flag(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
struct atl_hw *hw = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n",
!!(hw->thermal.flags & BIT(sattr->index)));
}
#define ATL_HWMON_BIT_ATTR(_name, _bit) \
SENSOR_DEVICE_ATTR(_name, S_IRUGO | S_IWUSR, atl_hwmon_show_flag, \
atl_hwmon_set_flag, _bit)
static ATL_HWMON_BIT_ATTR(monitor, atl_thermal_monitor_shift);
static ATL_HWMON_BIT_ATTR(throttle, atl_thermal_throttle_shift);
static ATL_HWMON_BIT_ATTR(ignore_lims, atl_thermal_ignore_lims_shift);
static struct attribute *atl_hwmon_attrs[] = {
&sensor_dev_attr_monitor.dev_attr.attr,
&sensor_dev_attr_throttle.dev_attr.attr,
&sensor_dev_attr_ignore_lims.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(atl_hwmon);
static char *atl_hwmon_labels[] = {
"PHY Temperature",
};
static const uint32_t atl_hwmon_temp_config[] = {
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM,
0,
};
static const struct hwmon_channel_info atl_hwmon_temp = {
.type = hwmon_temp,
.config = atl_hwmon_temp_config,
};
static const struct hwmon_channel_info *atl_hwmon_info[] = {
&atl_hwmon_temp,
NULL,
};
static umode_t atl_hwmon_is_visible(const void *p,
enum hwmon_sensor_types type, uint32_t attr, int channel)
{
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input: case hwmon_temp_max_alarm: case hwmon_temp_label:
return S_IRUGO;
case hwmon_temp_crit: case hwmon_temp_max: case hwmon_temp_max_hyst:
return S_IRUGO | S_IWUSR;
}
return 0;
}
static int atl_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
uint32_t attr, int channel, long *val)
{
struct atl_hw *hw = dev_get_drvdata(dev);
int temp, ret;
if (type != hwmon_temp)
return -EINVAL;
switch (attr) {
case hwmon_temp_input:
ret = hw->mcp.ops->get_phy_temperature(hw, &temp);
if (ret)
return ret;
*val = temp;
break;
case hwmon_temp_crit:
*val = hw->thermal.crit * 1000;
break;
case hwmon_temp_max:
*val = hw->thermal.high * 1000;
break;
case hwmon_temp_max_hyst:
*val = hw->thermal.low * 1000;
break;
case hwmon_temp_max_alarm:
*val = hw->link_state.thermal_throttled;
break;
default:
return -EINVAL;
}
return 0;
}
static int atl_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
uint32_t attr, int channel, long val)
{
struct atl_hw *hw = dev_get_drvdata(dev);
uint8_t *ptr, old;
int ret;
if (type != hwmon_temp)
return -EINVAL;
switch (attr) {
case hwmon_temp_crit:
ptr = &hw->thermal.crit;
break;
case hwmon_temp_max:
ptr = &hw->thermal.high;
break;
case hwmon_temp_max_hyst:
ptr = &hw->thermal.low;
break;
default:
return -EINVAL;
}
old = *ptr;
*ptr = val / 1000;
ret = atl_update_thermal(hw);
if (ret)
*ptr = old;
return ret;
}
static int atl_hwmon_read_string(struct device *dev,
enum hwmon_sensor_types type, u32 attr, int channel, const char **str)
{
if (type != hwmon_temp || attr != hwmon_temp_label)
return -EINVAL;
*str = atl_hwmon_labels[channel];
return 0;
}
static const struct hwmon_ops atl_hwmon_ops = {
.is_visible = atl_hwmon_is_visible,
.read = atl_hwmon_read,
.read_string = atl_hwmon_read_string,
.write = atl_hwmon_write,
};
static const struct hwmon_chip_info atl_hwmon = {
.ops = &atl_hwmon_ops,
.info = atl_hwmon_info,
};
int atl_hwmon_init(struct atl_nic *nic)
{
struct device *hwmon_dev;
struct atl_hw *hw = &nic->hw;
hwmon_dev = devm_hwmon_device_register_with_info(&hw->pdev->dev,
nic->ndev->name, hw, &atl_hwmon, atl_hwmon_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
#else
int atl_hwmon_init(struct atl_nic *nic)
{
return 0;
}
#endif

View file

@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_LOG_H_
#define _ATL_LOG_H_
#ifdef __KERNEL__
#include <linux/device.h>
/* Logging conveniency macros.
*
* atl_dev_xxx are for low-level contexts and implicitly reference
* struct atl_hw *hw;
*
* atl_nic_xxx are for high-level contexts and implicitly reference
* struct atl_nic *nic;
*/
#define atl_dev_dbg(fmt, args...) \
dev_dbg(&hw->pdev->dev, fmt, ## args)
#define atl_dev_info(fmt, args...) \
dev_info(&hw->pdev->dev, fmt, ## args)
#define atl_dev_warn(fmt, args...) \
dev_warn(&hw->pdev->dev, fmt, ## args)
#define atl_dev_err(fmt, args...) \
dev_err(&hw->pdev->dev, fmt, ## args)
#define atl_nic_dbg(fmt, args...) \
dev_dbg(&nic->hw.pdev->dev, fmt, ## args)
#define atl_nic_info(fmt, args...) \
dev_info(&nic->hw.pdev->dev, fmt, ## args)
#define atl_nic_warn(fmt, args...) \
dev_warn(&nic->hw.pdev->dev, fmt, ## args)
#define atl_nic_err(fmt, args...) \
dev_err(&nic->hw.pdev->dev, fmt, ## args)
#define atl_dev_init_warn(fmt, args...) \
do { \
if (hw) \
atl_dev_warn(fmt, ## args); \
else \
printk(KERN_WARNING "%s: " fmt, atl_driver_name, ##args); \
} while (0)
#define atl_dev_init_err(fmt, args...) \
do { \
if (hw) \
atl_dev_err(fmt, ## args); \
else \
printk(KERN_ERR "%s: " fmt, atl_driver_name, ##args); \
} while (0)
#else /* __KERNEL__ */
/* Logging is disabled in case of unit tests */
#define atl_dev_dbg(fmt, args...)
#define atl_dev_info(fmt, args...)
#define atl_dev_warn(fmt, args...)
#define atl_dev_err(fmt, args...)
#define atl_nic_dbg(fmt, args...)
#define atl_nic_info(fmt, args...)
#define atl_nic_warn(fmt, args...)
#define atl_nic_err(fmt, args...)
#define atl_dev_init_warn(fmt, args...)
#define atl_dev_init_err(fmt, args...)
#endif /* __KERNEL__ */
#endif /* _ATL_LOG_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,139 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_MACSEC_H_
#define _ATL_MACSEC_H_
#include <linux/netdevice.h>
#if IS_ENABLED(CONFIG_MACSEC) && defined(NETIF_F_HW_MACSEC)
#include "net/macsec.h"
struct atl_hw;
struct atl_nic;
#define ATL_MACSEC_MAX_SC 32
#define ATL_MACSEC_MAX_SA 32
enum atl_macsec_sc_sa {
atl_macsec_sa_sc_4sa_8sc,
atl_macsec_sa_sc_not_used,
atl_macsec_sa_sc_2sa_16sc,
atl_macsec_sa_sc_1sa_32sc,
};
struct atl_macsec_common_stats {
/* Ingress Common Counters */
struct {
u64 ctl_pkts;
u64 tagged_miss_pkts;
u64 untagged_miss_pkts;
u64 notag_pkts;
u64 untagged_pkts;
u64 bad_tag_pkts;
u64 no_sci_pkts;
u64 unknown_sci_pkts;
u64 ctrl_prt_pass_pkts;
u64 unctrl_prt_pass_pkts;
u64 ctrl_prt_fail_pkts;
u64 unctrl_prt_fail_pkts;
u64 too_long_pkts;
u64 igpoc_ctl_pkts;
u64 ecc_error_pkts;
u64 unctrl_hit_drop_redir;
} in;
/* Egress Common Counters */
struct {
u64 ctl_pkts;
u64 unknown_sa_pkts;
u64 untagged_pkts;
u64 too_long;
u64 ecc_error_pkts;
u64 unctrl_hit_drop_redir;
} out;
};
/* Ingress SA Counters */
struct atl_macsec_rx_sa_stats {
u64 untagged_hit_pkts;
u64 ctrl_hit_drop_redir_pkts;
u64 not_using_sa;
u64 unused_sa;
u64 not_valid_pkts;
u64 invalid_pkts;
u64 ok_pkts;
u64 late_pkts;
u64 delayed_pkts;
u64 unchecked_pkts;
u64 validated_octets;
u64 decrypted_octets;
};
/* Egress SA Counters */
struct atl_macsec_tx_sa_stats {
u64 sa_hit_drop_redirect;
u64 sa_protected2_pkts;
u64 sa_protected_pkts;
u64 sa_encrypted_pkts;
};
/* Egress SC Counters */
struct atl_macsec_tx_sc_stats {
u64 sc_protected_pkts;
u64 sc_encrypted_pkts;
u64 sc_protected_octets;
u64 sc_encrypted_octets;
};
struct atl_macsec_txsc {
u32 hw_sc_idx;
unsigned long tx_sa_idx_busy;
const struct macsec_secy *sw_secy;
/* It is not OK to store key in driver but it is until ... */
u8 tx_sa_key[MACSEC_NUM_AN][MACSEC_KEYID_LEN];
struct atl_macsec_tx_sc_stats stats;
struct atl_macsec_tx_sa_stats tx_sa_stats[MACSEC_NUM_AN];
};
struct atl_macsec_rxsc {
u32 hw_sc_idx;
unsigned long rx_sa_idx_busy;
const struct macsec_secy *sw_secy;
const struct macsec_rx_sc *sw_rxsc;
/* TODO: we shouldn't store keys in the driver */
u8 rx_sa_key[MACSEC_NUM_AN][MACSEC_KEYID_LEN];
struct atl_macsec_rx_sa_stats rx_sa_stats[MACSEC_NUM_AN];
};
struct atl_macsec_cfg {
enum atl_macsec_sc_sa sc_sa;
/* Egress channel configuration */
unsigned long txsc_idx_busy;
struct atl_macsec_txsc atl_txsc[ATL_MACSEC_MAX_SC];
/* Ingress channel configuration */
unsigned long rxsc_idx_busy;
struct atl_macsec_rxsc atl_rxsc[ATL_MACSEC_MAX_SC];
struct atl_macsec_common_stats stats;
};
extern const struct macsec_ops atl_macsec_ops;
int atl_init_macsec(struct atl_hw *hw);
void atl_macsec_work(struct atl_nic *nic);
int atl_macsec_rx_sa_cnt(struct atl_hw *hw);
int atl_macsec_tx_sc_cnt(struct atl_hw *hw);
int atl_macsec_tx_sa_cnt(struct atl_hw *hw);
int atl_macsec_update_stats(struct atl_hw *hw);
#endif
#endif /* _ATL_MACSEC_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_MDIO_H_
#define _ATL_MDIO_H_
#include "atl_hw.h"
int atl_mdio_hwsem_get(struct atl_hw *hw);
void atl_mdio_hwsem_put(struct atl_hw *hw);
int __atl_mdio_read(struct atl_hw *hw, uint8_t prtad, uint8_t mmd,
uint16_t addr, uint16_t *val);
int atl_mdio_read(struct atl_hw *hw, uint8_t prtad, uint8_t mmd,
uint16_t addr, uint16_t *val);
int __atl_mdio_write(struct atl_hw *hw, uint8_t prtad, uint8_t mmd,
uint16_t addr, uint16_t val);
int atl_mdio_write(struct atl_hw *hw, uint8_t prtad, uint8_t mmd,
uint16_t addr, uint16_t val);
#endif /* _ATL_MDIO_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2014-2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ATL_PTP_H
#define ATL_PTP_H
#include <linux/net_tstamp.h>
#include "atl_compat.h"
struct atl_nic;
struct atl_queue_vec;
/* Common functions */
int atl_ptp_init(struct atl_nic *nic);
int atl_ptp_register(struct atl_nic *nic);
void atl_ptp_unregister(struct atl_nic *nic);
void atl_ptp_free(struct atl_nic *nic);
int atl_ptp_irq_alloc(struct atl_nic *nic);
void atl_ptp_irq_free(struct atl_nic *nic);
int atl_ptp_ring_alloc(struct atl_nic *nic);
void atl_ptp_ring_free(struct atl_nic *nic);
int atl_ptp_ring_start(struct atl_nic *nic);
void atl_ptp_ring_stop(struct atl_nic *nic);
void atl_ptp_work(struct atl_nic *nic);
void atl_ptp_tm_offset_set(struct atl_nic *nic, unsigned int mbps);
void atl_ptp_clock_init(struct atl_nic *nic);
int atl_ptp_qvec_intr(struct atl_queue_vec *qvec);
/* Traffic processing functions */
netdev_tx_t atl_ptp_start_xmit(struct atl_nic *nic, struct sk_buff *skb);
void atl_ptp_tx_hwtstamp(struct atl_nic *nic, u64 timestamp);
/* Check for PTP availability before calling! */
void atl_ptp_hwtstamp_config_get(struct atl_nic *nic,
struct hwtstamp_config *config);
int atl_ptp_hwtstamp_config_set(struct atl_nic *nic,
struct hwtstamp_config *config);
/* Return whether ring belongs to PTP or not*/
bool atl_is_ptp_ring(struct atl_nic *nic, struct atl_desc_ring *ring);
u16 atl_ptp_extract_ts(struct atl_nic *nic, struct sk_buff *skb, u8 *p,
unsigned int len);
struct ptp_clock *atl_ptp_get_ptp_clock(struct atl_nic *nic);
int atl_ptp_link_change(struct atl_nic *nic);
#endif /* ATL_PTP_H */

View file

@ -0,0 +1,224 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_REGS_H_
#define _ATL_REGS_H_
#define ATL_REG_STRIDE(base, stride, idx) ((base) + (stride) * (idx))
/* Ring registers common for Rx and Tx */
#define ATL_RING_OFFT(ring, offt) \
(((struct atl_hw_ring *)(ring))->reg_base + (offt))
#define ATL_RING_BASE_LSW(ring) ATL_RING_OFFT(ring, 0)
#define ATL_RING_BASE_MSW(ring) ATL_RING_OFFT(ring, 4)
#define ATL_RING_CTL(ring) ATL_RING_OFFT(ring, 8)
#define ATL_RING_HEAD(ring) ATL_RING_OFFT(ring, 0xc)
#define ATL_RING_TAIL(ring) ATL_RING_OFFT(ring, 0x10)
#define ATL_RING_STS(ring) ATL_RING_OFFT(ring, 0x14)
/* MIF @ 0x0000*/
#define ATL_GLOBAL_STD_CTRL 0
#define ATL_GLOBAL_FW_ID 0xc
#define ATL_GLOBAL_CHIP_ID 0x10
#define ATL_GLOBAL_CHIP_REV 0x14
#define ATL_GLOBAL_FW_IMAGE_ID 0x18
#define ATL_GLOBAL_MIF_ID 0x1c
#define ATL_GLOBAL_MBOX_CTRL 0x200
#define ATL_GLOBAL_MBOX_CRC 0x204
#define ATL_GLOBAL_MBOX_ADDR 0x208
#define ATL_GLOBAL_MBOX_DATA 0x20c
#define ATL_GLOBAL_MDIO_CTL 0x280
#define ATL_GLOBAL_MDIO_CMD 0x284
#define ATL_GLOBAL_MDIO_WDATA 0x288
#define ATL_GLOBAL_MDIO_ADDR 0x28c
#define ATL_GLOBAL_MDIO_RDATA 0x290
#define ATL2_MIF_BOOT_READ_REQ_ADR 0x328
#define ATL2_MIF_BOOT_READ_REQ_LEN 0x32c
#define ATL2_MIF_BOOT_REG_ADR 0x3040
#define ATL2_MCP_HOST_REQ_INT 0x0F00
#define ATL2_MCP_HOST_REQ_INT_CLR 0x0F08
#define ATL2_MCP_HOST_REQ_INT_MASK(idx) ATL_REG_STRIDE(0x0f0c, 4, (idx))
#define ATL2_MIF_SHARED_BUFFER_BOOT(idx) ATL_REG_STRIDE(0x00010000, 0x4, (idx))
#define ATL2_MIF_SHARED_BUFFER_IN(idx) ATL_REG_STRIDE(0x00012000, 0x4, (idx))
#define ATL2_MIF_SHARED_BUFFER_OUT(idx) ATL_REG_STRIDE(0x00013000, 0x4, (idx))
#define ATL2_MIF_HOST_FINISHED_WRITE 0x0e00
#define ATL2_MIF_MCP_FINISHED_READ 0x0e04
/* Scratch pads numbered starting from 1 */
#define ATL_MCP_SCRATCH(idx) ATL_REG_STRIDE(0x300 - 0x4, 0x4, idx)
#define ATL_MCP_SEM(idx) ATL_REG_STRIDE(0x3a0, 0x4, idx)
#define ATL_MCP_SEM_MDIO 0
#define ATL_MCP_SEM_MSM 1
#define ATL_MCP_SEM_MEM 2
#define ATL2_MCP_SEM_ACT_RSLVR 3
#define ATL_GLOBAL_CTRL2 0x404
#define ATL_GLOBAL_DAISY_CHAIN_STS1 0x704
enum mcp_scratchpad {
FW2_MBOX_DATA = 11, /* 0x328 */
FW2_MBOX_CMD = 12, /* 0x32c */
FW2_RPC_DATA = 14, /* 0x334 */
FW_STAT_STRUCT = 25, /* 0x360 */
FW2_EFUSE_SHADOW = 26, /* 0x364 */
FW1_LINK_REQ = 27,
FW2_LINK_REQ_LOW = 27, /* 0x368 */
FW1_LINK_STS = 28,
FW2_LINK_REQ_HIGH = 28, /* 0x36c */
FW2_LINK_RES_LOW = 29, /* 0x370 */
FW1_EFUSE_SHADOW = 30,
FW2_LINK_RES_HIGH = 30, /* 0x374 */
FW3_EXT_REQ = 31, /* 0x378 */
FW3_EXT_RES = 32, /* 0x37c */
RBL_STS = 35, /* 0x388 */
};
/* INTR @ 0x2000 */
#define ATL_INTR_STS 0x2000
#define ATL_INTR_MSK 0x2010
#define ATL_INTR_MSK_SET 0x2060
#define ATL_INTR_MSK_CLEAR 0x2070
#define ATL_INTR_AUTO_CLEAR 0x2080
#define ATL_INTR_AUTO_MASK 0x2090
#define ATL_INTR_RING_INTR_MAP(idx) ATL_REG_STRIDE(0x2100, 0x4, (idx) >> 1)
#define ATL_INTR_GEN_INTR_MAP4 0x218c
#define ATL_INTR_RSC_EN 0x2200
#define ATL_INTR_RSC_DELAY 0x2204
#define ATL_INTR_CTRL 0x2300
#define ATL_INTR_THRTL(idx) ATL_REG_STRIDE(0x2800, 4, idx)
/* MPI @ 0x4000 */
#define ATL_MPI_CTRL1 0x4000
#define ATL_MPI_MSM_CTRL 0x4018
#define ATL_MPI_MSM_ADDR 0x4400
#define ATL_MPI_MSM_WR 0x4404
#define ATL_MPI_MSM_RD 0x4408
/* RX @ 0x5000 */
#define ATL_RX_CTRL1 0x5000
#define ATL_RX_SPARE_CTRL0 0x50A0
#define ATL_RX_SPARE_CTRL1 0x50A4
#define ATL2_RX_FLT_L2_BC_TAG 0x50F0
#define ATL_RX_FLT_CTRL1 0x5100
#define ATL_RX_FLT_CTRL2 0x5104
#define ATL_UC_FLT_NUM 37
#define ATL_RX_UC_FLT_REG1(idx) ATL_REG_STRIDE(0x5110, 8, idx)
#define ATL_RX_UC_FLT_REG2(idx) ATL_REG_STRIDE(0x5114, 8, idx)
#define ATL_MC_FLT_NUM 8
#define ATL_RX_MC_FLT(idx) ATL_REG_STRIDE(0x5250, 4, idx)
#define ATL_RX_MC_FLT_MSK 0x5270
#define ATL_RX_VLAN_FLT_CTRL1 0x5280
#define ATL_VLAN_FLT_NUM 16
#define ATL_RX_VLAN_FLT(idx) ATL_REG_STRIDE(0x5290, 4, idx)
#define ATL_RX_ETYPE_FLT(idx) ATL_REG_STRIDE(0x5300, 4, idx)
#define ATL2_RX_ETYPE_TAG(idx) ATL_REG_STRIDE(0x5340, 4, idx)
#define ATL_ETYPE_FLT_NUM 16
#define ATL_NTUPLE_CTRL(idx) ATL_REG_STRIDE(0x5380, 4, idx)
#define ATL_NTUPLE_SADDR(idx) ATL_REG_STRIDE(0x53b0, 4, idx)
#define ATL_NTUPLE_DADDR(idx) ATL_REG_STRIDE(0x53d0, 4, idx)
#define ATL_NTUPLE_SPORT(idx) ATL_REG_STRIDE(0x5400, 4, idx)
#define ATL_NTUPLE_DPORT(idx) ATL_REG_STRIDE(0x5420, 4, idx)
#define ATL_NTUPLE_FLT_NUM 8
#define ATL_NTUPLE_V6_FLT_NUM 2
#define ATL_RX_FLEX_FLT_CTRL(idx) ATL_REG_STRIDE(0x5460, 0x20, idx)
#define ATL_FLEX_FLT_NUM 2
#define ATL_RX_RSS_CTRL 0x54c0
#define ATL_RX_RSS_KEY_ADDR 0x54d0
#define ATL_RX_RSS_KEY_WR_DATA 0x54d4
#define ATL_RX_RSS_KEY_RD_DATA 0x54d8
#define ATL_RX_RSS_TBL_ADDR 0x54e0
#define ATL_RX_RSS_TBL_WR_DATA 0x54e4
#define ATL_RX_RSS_TBL_RD_DATA 0x54e8
#define ATL2_RX_RSS_HASH_TYPE_ADR 0x54C8
#define ATL_RX_RPF_DBG_CNT_CTRL 0x5518
#define ATL_RX_RPF_HOST_CNT_LO 0x552c
#define ATL_RX_RPF_HOST_CNT_HI 0x5530
#define ATL_RX_RPF_LOST_CNT_LO 0x554c
#define ATL_RX_RPF_LOST_CNT_HI 0x5550
#define ATL_RX_PO_CTRL1 0x5580
#define ATL_RX_LRO_CTRL1 0x5590
#define ATL_RX_LRO_CTRL2 0x5594
#define ATL_RX_LRO_PKT_LIM_EN 0x5598
#define ATL_RX_LRO_PKT_LIM(idx) ATL_REG_STRIDE(0x55a0, 4, (idx) >> 3)
#define ATL_RX_LRO_TMRS 0x5620
#define ATL_RX_PBUF_CTRL1 0x5700
#define ATL_RX_PBUF_REG1(idx) ATL_REG_STRIDE(0x5710, 0x10, idx)
#define ATL_RX_PBUF_REG2(idx) ATL_REG_STRIDE(0x5714, 0x10, idx)
#define ATL2_RX_Q_TO_TC_MAP(tc) ATL_REG_STRIDE(0x5900, 0x4, tc)
#define ATL_RX_INTR_CTRL 0x5a30
#define ATL_RX_INTR_MOD_CTRL(idx) ATL_REG_STRIDE(0x5a40, 4, idx)
#define ATL2_RPF_RSS_REDIR(TC, INDEX) (0x6200 + \
(0x100 * (TC > 3 ? 1 : 0)) + (INDEX) * 4)
#define ATL2_RPF_L3_FLT(filter) ATL_REG_STRIDE(0x6500, 0x4, filter)
#define ATL2_RPF_L3_SA(filter) ATL_REG_STRIDE(0x6400, 0x10, filter)
#define ATL2_RPF_L3_DA(filter) ATL_REG_STRIDE(0x6480, 0x10, filter)
#define ATL2_RPF_L4_FLT(filter) ATL_REG_STRIDE(0x6520, 0x4, filter)
#define ATL2_RPF_REC_TAB_EN 0x00006ff0
#define ATL2_RPF_ACT_RSLVR_REQ_TAG(filter) ATL_REG_STRIDE(0x14000, 0x10, filter)
#define ATL2_RPF_ACT_RSLVR_TAG_MASK(filter) ATL_REG_STRIDE(0x14004, 0x10, filter)
#define ATL2_RPF_ACT_RSLVR_ACTN(filter) ATL_REG_STRIDE(0x14008, 0x10, filter)
/* Rx rings */
#define ATL_RX_RING(idx) ATL_REG_STRIDE(0x5b00, 0x20, idx)
#define ATL_RX_RING_BASE_LSW(ring) ATL_RING_BASE_LSW(ring)
#define ATL_RX_RING_BASE_MSW(ring) ATL_RING_BASE_MSW(ring)
#define ATL_RX_RING_CTL(ring) ATL_RING_CTL(ring)
#define ATL_RX_RING_HEAD(ring) ATL_RING_HEAD(ring)
#define ATL_RX_RING_TAIL(ring) ATL_RING_TAIL(ring)
#define ATL_RX_RING_STS(ring) ATL_RING_STS(ring)
#define ATL_RX_RING_BUF_SIZE(ring) ATL_RING_OFFT(ring, 0x18)
#define ATL_RX_RING_THRESH(ring) ATL_RING_OFFT(ring, 0x1c)
#define ATL_RX_DMA_STATS_CNT7 0x6818
/* TX @ 0x7000 */
#define ATL_TX_CTRL1 0x7000
#define ATL_TX_PO_CTRL1 0x7800
#define ATL_TX_LSO_CTRL 0x7810
#define ATL_TX_LSO_TCP_CTRL1 0x7820
#define ATL_TX_LSO_TCP_CTRL2 0x7824
#define ATL_TX_PBUF_CTRL1 0x7900
#define ATL_TX_PBUF_REG1(idx) ATL_REG_STRIDE(0x7910, 0x10, idx)
#define ATL_TX_PBUF_REG2(idx) ATL_REG_STRIDE(0x7914, 0x10, idx)
#define ATL_TX_INTR_CTRL 0x7b40
#define ATL2_TX_Q_TO_TC_MAP(tc) ATL_REG_STRIDE(0x799c, 0x4, tc)
/* Tx rings */
#define ATL_TX_RING(idx) ATL_REG_STRIDE(0x7c00, 0x40, idx)
#define ATL_TX_RING_BASE_LSW(ring) ATL_RING_BASE_LSW(ring)
#define ATL_TX_RING_BASE_MSW(ring) ATL_RING_BASE_MSW(ring)
#define ATL_TX_RING_CTL(ring) ATL_RING_CTL(ring)
#define ATL_TX_RING_HEAD(ring) ATL_RING_HEAD(ring)
#define ATL_TX_RING_TAIL(ring) ATL_RING_TAIL(ring)
#define ATL_TX_RING_STS(ring) ATL_RING_STS(ring)
#define ATL_TX_RING_THRESH(ring) ATL_RING_OFFT(ring, 0x18)
#define ATL_TX_RING_HEAD_WB_LSW(ring) ATL_RING_OFFT(ring, 0x1c)
#define ATL_TX_RING_HEAD_WB_MSW(ring) ATL_RING_OFFT(ring, 0x20)
#define ATL_TX_INTR_MOD_CTRL(idx) ATL_REG_STRIDE(0x8980, 0x4, idx)
#define ATL2_TX_INTR_MOD_CTRL(idx) ATL_REG_STRIDE(0x7c28, 0x40, idx)
/* MSM */
#define ATL_MSM_GEN_CTRL 0x8
#define ATL_MSM_GEN_STS 0x40
#define ATL_MSM_TX_LPI_DELAY 0x78
#define ATL_MSM_CTR_RX_PKTS_GOOD 0x88
#define ATL_MSM_CTR_RX_FCS_ERRS 0x90
#define ATL_MSM_CTR_RX_ALIGN_ERRS 0x98
#define ATL_MSM_CTR_TX_PAUSE 0xa0
#define ATL_MSM_CTR_RX_PAUSE 0xa8
#define ATL_MSM_CTR_RX_OCTETS_LO 0xd8
#define ATL_MSM_CTR_RX_OCTETS_HI 0xdc
#define ATL_MSM_CTR_RX_MULTICAST 0xE8
#define ATL_MSM_CTR_RX_BROADCAST 0xF0
#define ATL_MSM_CTR_RX_ERRS 0x120
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_RING_H_
#define _ATL_RING_H_
#include <linux/compiler.h>
#include "atl_common.h"
#include "atl_desc.h"
#include "atl_ring_desc.h"
#include "atl_ptp.h"
//#define ATL_RINGS_IN_UC_MEM
#define ATL_TX_DESC_WB
//#define ATL_TX_HEAD_WB
#define ATL_RX_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN)
#define ATL_RX_TAILROOM 64u
#define ATL_RX_HEAD_ORDER 0
#define ATL_RX_DATA_ORDER 0
/* Header space in skb. Must be a multiple of L1_CACHE_BYTES */
#define ATL_RX_HDR_SIZE 256u
#define ATL_RX_HDR_OVRHD SKB_DATA_ALIGN(ATL_RX_HEADROOM + \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
#define ATL_RX_BUF_SIZE 2048
#define ATL_MAX_RX_LINEAR_MTU (ATL_RX_BUF_SIZE - ETH_HLEN)
#define ring_space(ring) \
({ \
struct atl_desc_ring *__ring = (ring); \
uint32_t space = READ_ONCE(__ring->head) - \
READ_ONCE(__ring->tail) - 1; \
(int32_t)space < 0 ? space + __ring->hw.size : space; \
})
#define ring_occupied(ring) \
({ \
struct atl_desc_ring *__ring = (ring); \
uint32_t occupied = READ_ONCE(__ring->tail) - \
READ_ONCE(__ring->head); \
(int32_t)occupied < 0 ? occupied + __ring->hw.size \
: occupied; \
})
#define bump_ptr(ptr, ring, amount) \
({ \
struct atl_desc_ring *__ring = (ring); \
uint32_t __res = offset_ptr(ptr, &__ring->hw, amount); \
(ptr) = __res; \
__res; \
})
/* These don't have to be atomic, because Tx tail is only adjusted
* in ndo->start_xmit which is serialized by the stack and the rest are
* only adjusted in NAPI poll which is serialized by NAPI */
#define bump_tail(ring, amount) do { \
struct atl_desc_ring *__ring = (ring); \
uint32_t __ptr = READ_ONCE(__ring->tail); \
__ring->tail = offset_ptr(__ptr, &__ring->hw, amount);\
} while (0)
#define bump_head(ring, amount) do { \
struct atl_desc_ring *__ring = (ring); \
uint32_t __ptr = READ_ONCE(__ring->head); \
__ring->head = offset_ptr(__ptr, &__ring->hw, amount);\
} while (0)
struct atl_rxpage {
struct page *page;
dma_addr_t daddr;
unsigned mapcount; /* not atomic_t because accesses are
* serialized by NAPI */
unsigned order;
};
struct atl_pgref {
struct atl_rxpage *rxpage;
unsigned pg_off;
};
struct atl_cb {
struct atl_pgref pgref;
bool head;
};
#define ATL_CB(skb) ((struct atl_cb *)(skb)->cb)
struct atl_rxbuf {
struct sk_buff *skb;
struct atl_pgref head;
struct atl_pgref data;
};
struct atl_txbuf {
struct sk_buff *skb;
uint32_t last; /* index of eop descriptor */
unsigned bytes;
unsigned packets;
DEFINE_DMA_UNMAP_ADDR(daddr);
DEFINE_DMA_UNMAP_LEN(len);
};
struct legacy_irq_work {
struct work_struct work;
struct napi_struct *napi;
};
static inline struct legacy_irq_work *to_irq_work(struct work_struct *work)
{
return container_of(work, struct legacy_irq_work, work);
};
enum atl_queue_type {
ATL_QUEUE_REGULAR,
ATL_QUEUE_PTP,
ATL_QUEUE_HWTS,
};
struct ____cacheline_aligned atl_queue_vec {
struct atl_desc_ring tx;
struct atl_desc_ring rx;
struct napi_struct napi;
struct atl_nic *nic;
unsigned idx;
char name[IFNAMSIZ + 10];
cpumask_t affinity_hint;
enum atl_queue_type type;
struct work_struct *work;
};
#define atl_for_each_qvec(nic, qvec) \
for (qvec = &(nic)->qvecs[0]; \
qvec < &(nic)->qvecs[(nic)->nvecs]; qvec++)
static inline struct atl_hw *ring_hw(struct atl_desc_ring *ring)
{
return &ring->nic->hw;
}
void atl_init_qvec(struct atl_nic *nic, struct atl_queue_vec *qvec, int idx);
int atl_alloc_qvec(struct atl_queue_vec *qvec);
void atl_free_qvec(struct atl_queue_vec *qvec);
int atl_start_qvec(struct atl_queue_vec *qvec);
void atl_stop_qvec(struct atl_queue_vec *qvec);
int atl_poll_qvec(struct atl_queue_vec *qvec, int budget);
static inline int atl_qvec_intr(struct atl_queue_vec *qvec)
{
#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
if (unlikely(qvec->idx >= qvec->nic->nvecs))
return atl_ptp_qvec_intr(qvec);
#endif
return qvec->idx + ATL_NUM_NON_RING_IRQS;
}
static inline void *atl_buf_vaddr(struct atl_pgref *pgref)
{
return page_to_virt(pgref->rxpage->page) + pgref->pg_off;
}
static inline dma_addr_t atl_buf_daddr(struct atl_pgref *pgref)
{
return pgref->rxpage->daddr + pgref->pg_off;
}
void atl_get_ring_stats(struct atl_desc_ring *ring,
struct atl_ring_stats *stats);
#define atl_update_ring_stat(ring, stat, delta) \
do { \
struct atl_desc_ring *_ring = (ring); \
\
u64_stats_update_begin(&_ring->syncp); \
_ring->stats.stat += (delta); \
u64_stats_update_end(&_ring->syncp); \
} while (0)
int atl_init_rx_ring(struct atl_desc_ring *rx);
int atl_init_tx_ring(struct atl_desc_ring *tx);
netdev_tx_t atl_map_skb(struct sk_buff *skb, struct atl_desc_ring *ring);
int atl_tx_full(struct atl_desc_ring *ring, int needed);
typedef int (*rx_skb_handler_t)(struct atl_desc_ring *ring,
struct sk_buff *skb);
int atl_clean_rx(struct atl_desc_ring *ring, int budget,
rx_skb_handler_t rx_skb_func);
int atl_clean_hwts_rx(struct atl_desc_ring *ring, int budget);
void atl_clear_rx_bufs(struct atl_desc_ring *ring);
#ifdef ATL_RINGS_IN_UC_MEM
#define DECLARE_SCRATCH_DESC(_name) union atl_desc _name
#define DESC_PTR(_ring, _idx, _scratch) (&(_scratch))
#define COMMIT_DESC(_ring, _idx, _scratch) \
(_ring)->hw.descs[_idx] = (_scratch)
#define FETCH_DESC(_ring, _idx, _scratch) \
do { \
(_scratch) = READ_ONCE((_ring)->hw.descs[_idx]); \
dma_rmb(); \
} while(0)
#define DESC_RMB()
#else // ATL_RINGS_IN_UC_MEM
#define DECLARE_SCRATCH_DESC(_name)
#define DESC_PTR(_ring, _idx, _scratch) (&(_ring)->hw.descs[_idx])
#define COMMIT_DESC(_ring, _idx, _scratch)
#define FETCH_DESC(_ring, _idx, _scratch)
#define DESC_RMB() dma_rmb()
#endif // ATL_RINGS_IN_UC_MEM
#ifdef ATL_TX_HEAD_WB
#error Head ptr writeback not implemented
#elif !defined(ATL_TX_DESC_WB)
static inline uint32_t atl_get_tx_head(struct atl_desc_ring *ring)
{
return atl_read(ring_hw(ring), ATL_TX_RING_HEAD(ring->idx)) & 0x1fff;
}
#endif
#endif

View file

@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_RING_DESC_H_
#define _ATL_RING_DESC_H_
#include <linux/types.h>
#include <linux/u64_stats_sync.h>
#include "atl_desc.h"
#include "atl_hw.h"
#include "atl_stats.h"
struct atl_nic;
struct atl_fwd_event;
struct atl_desc_ring {
struct atl_hw_ring hw;
struct atl_nic *nic;
uint32_t head;
uint32_t tail;
union {
/* Rx ring only */
uint32_t next_to_recycle;
/* Tx ring only, template desc for atl_map_tx_skb() */
union atl_desc desc;
};
union {
struct atl_rxbuf *rxbufs;
struct atl_txbuf *txbufs;
void *bufs;
};
struct atl_queue_vec *qvec;
struct u64_stats_sync syncp;
struct atl_ring_stats stats;
#if IS_ENABLED(CONFIG_ATLFWD_FWD_NETLINK)
u32 tx_hw_head;
union {
struct atl_fwd_event *tx_evt;
struct atl_fwd_event *rx_evt;
};
/* RX ring polling */
struct timer_list *rx_poll_timer;
#endif
};
#endif /* _ATL_RING_DESC_H_ */

View file

@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATL_STATS_H_
#define _ATL_STATS_H_
#include <linux/types.h>
struct atl_rx_ring_stats {
uint64_t packets;
uint64_t bytes;
uint64_t linear_dropped;
uint64_t alloc_skb_failed;
uint64_t reused_head_page;
uint64_t reused_data_page;
uint64_t alloc_head_page;
uint64_t alloc_data_page;
uint64_t alloc_head_page_failed;
uint64_t alloc_data_page_failed;
uint64_t non_eop_descs;
uint64_t mac_err;
uint64_t csum_err;
uint64_t multicast;
};
struct atl_rx_fwd_ring_stats {
uint64_t packets;
uint64_t bytes;
};
struct atl_tx_ring_stats {
uint64_t packets;
uint64_t bytes;
uint64_t tx_busy;
uint64_t tx_restart;
uint64_t dma_map_failed;
};
struct atl_ring_stats {
union {
struct atl_rx_ring_stats rx;
struct atl_tx_ring_stats tx;
};
};
struct atl_ether_stats {
uint64_t rx_pause;
uint64_t tx_pause;
uint64_t rx_ether_drops;
uint64_t rx_ether_octets;
uint64_t rx_ether_pkts;
uint64_t rx_ether_broacasts;
uint64_t rx_ether_multicasts;
uint64_t rx_ether_crc_align_errs;
uint64_t rx_filter_host;
uint64_t rx_filter_lost;
};
struct atl_global_stats {
struct atl_rx_ring_stats rx;
struct atl_tx_ring_stats tx;
struct atl_rx_fwd_ring_stats rx_fwd;
/* MSM counters can't be reset without full HW reset, so
* store them in relative form:
* eth[i] == HW_counter - eth_base[i]
*/
struct atl_ether_stats eth;
struct atl_ether_stats eth_base;
};
struct atl_fwd_ring;
#if IS_ENABLED(CONFIG_ATLFWD_FWD_NETLINK)
void atl_fwd_get_ring_stats(struct atl_fwd_ring *ring,
struct atl_ring_stats *stats);
#endif
#endif /* _ATL_STATS_H_ */

View file

@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/skbuff.h>
#define CREATE_TRACE_POINTS
#include "atl_trace.h"

View file

@ -0,0 +1,274 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2017 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM atlnew
#if !defined(_ATL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _ATL_TRACE_H
#include <linux/tracepoint.h>
#include "atl_desc.h"
DECLARE_EVENT_CLASS(atl_dma_map_class,
TP_PROTO(int frag_idx, int ring_idx, dma_addr_t daddr, size_t size, struct sk_buff *skb,
void *vaddr),
TP_ARGS(frag_idx, ring_idx, daddr, size, skb, vaddr),
TP_STRUCT__entry(
__field(int, frag_idx)
__field(int, ring_idx)
__field(dma_addr_t, daddr)
__field(size_t, size)
__field(struct sk_buff *, skb)
__field(void *, vaddr)
),
TP_fast_assign(
__entry->frag_idx = frag_idx;
__entry->ring_idx = ring_idx;
__entry->daddr = daddr;
__entry->size = size;
__entry->skb = skb;
__entry->vaddr = vaddr;
),
TP_printk("idx %d ring idx %d daddr %pad len %#zx skb %p vaddr %p",
__entry->frag_idx, __entry->ring_idx, &__entry->daddr,
__entry->size, __entry->skb, __entry->vaddr)
);
#define DEFINE_MAP_EVENT(name) \
DEFINE_EVENT(atl_dma_map_class, name, \
TP_PROTO(int frag_idx, int ring_idx, \
dma_addr_t daddr, size_t size, \
struct sk_buff *skb, void *vaddr), \
TP_ARGS(frag_idx, ring_idx, daddr, size, skb, vaddr))
DEFINE_MAP_EVENT(atl_dma_map_head);
DEFINE_MAP_EVENT(atl_dma_map_frag);
DEFINE_MAP_EVENT(atl_dma_map_rxbuf);
DECLARE_EVENT_CLASS(atl_dma_unmap_class,
TP_PROTO(int frag_idx, int ring_idx, dma_addr_t daddr, size_t size,
struct sk_buff *skb),
TP_ARGS(frag_idx, ring_idx, daddr, size, skb),
TP_STRUCT__entry(
__field(int, frag_idx)
__field(int, ring_idx)
__field(dma_addr_t, daddr)
__field(size_t, size)
__field(struct sk_buff *, skb)
),
TP_fast_assign(
__entry->frag_idx = frag_idx;
__entry->ring_idx = ring_idx;
__entry->daddr = daddr;
__entry->size = size;
__entry->skb = skb;
),
TP_printk("idx %d ring idx %d daddr %pad len %#zx skb %p",
__entry->frag_idx, __entry->ring_idx, &__entry->daddr,
__entry->size, __entry->skb)
);
#define DEFINE_UNMAP_EVENT(name) \
DEFINE_EVENT(atl_dma_unmap_class, name, \
TP_PROTO(int frag_idx, int ring_idx, dma_addr_t daddr, \
size_t size, struct sk_buff *skb), \
TP_ARGS(frag_idx, ring_idx, daddr, size, skb))
DEFINE_UNMAP_EVENT(atl_dma_unmap_head);
DEFINE_UNMAP_EVENT(atl_dma_unmap_frag);
DEFINE_UNMAP_EVENT(atl_dma_unmap_rxbuf);
TRACE_EVENT(atl_fill_rx_desc,
TP_PROTO(int ring_idx, struct atl_rx_desc *desc),
TP_ARGS(ring_idx, desc),
TP_STRUCT__entry(
__field(int, ring_idx)
__field(dma_addr_t, daddr)
__field(dma_addr_t, haddr)
),
TP_fast_assign(
__entry->ring_idx = ring_idx;
__entry->daddr = desc->daddr;
__entry->haddr = desc->haddr;
),
TP_printk("[%d] daddr %pad", __entry->ring_idx, &__entry->daddr)
);
TRACE_EVENT(atl_sync_rx_range,
TP_PROTO(int ring_idx, dma_addr_t daddr, unsigned long pg_off,
size_t size),
TP_ARGS(ring_idx, daddr, pg_off, size),
TP_STRUCT__entry(
__field(int, ring_idx)
__field(dma_addr_t, daddr)
__field(unsigned long, pg_off)
__field(size_t, size)
),
TP_fast_assign(
__entry->ring_idx = ring_idx;
__entry->daddr = daddr;
__entry->pg_off = pg_off;
__entry->size = size;
),
TP_printk("[%d] daddr %pad pg_off %#lx size %#zx", __entry->ring_idx,
&__entry->daddr, __entry->pg_off, __entry->size)
);
#define DESCR_FIELD(DESCR, BIT_BEGIN, BIT_END) \
((DESCR >> BIT_END) &\
(BIT_ULL(BIT_BEGIN - BIT_END + 1) - 1))
TRACE_EVENT(atl_rx_descr,
TP_PROTO(int ring_idx, unsigned int pointer, u64 *descr),
TP_ARGS(ring_idx, pointer, descr),
TP_STRUCT__entry(
__field(unsigned int, ring_idx)
__field(unsigned int, pointer)
__field(u8, dd)
__field(u8, eop)
__field(u8, rx_stat)
__field(u8, rx_estat)
__field(u8, rsc_cnt)
__field(u16, pkt_len)
__field(u16, next_desp)
__field(u16, vlan_tag)
__field(u8, rss_type)
__field(u8, pkt_type)
__field(u8, rsvd)
__field(u8, rx_cntl)
__field(u8, sph)
__field(u16, hdr_len)
__field(u32, rss_hash)
),
TP_fast_assign(
__entry->ring_idx = ring_idx;
__entry->pointer = pointer;
__entry->rss_hash = DESCR_FIELD(descr[0], 63, 32);
__entry->hdr_len = DESCR_FIELD(descr[0], 31, 22);
__entry->sph = DESCR_FIELD(descr[0], 21, 21);
__entry->rx_cntl = DESCR_FIELD(descr[0], 20, 19);
__entry->rsvd = DESCR_FIELD(descr[0], 18, 12);
__entry->pkt_type = DESCR_FIELD(descr[0], 11, 4);
__entry->rss_type = DESCR_FIELD(descr[0], 3, 0);
__entry->vlan_tag = DESCR_FIELD(descr[1], 63, 48);
__entry->next_desp = DESCR_FIELD(descr[1], 47, 32);
__entry->pkt_len = DESCR_FIELD(descr[1], 31, 16);
__entry->rsc_cnt = DESCR_FIELD(descr[1], 15, 12);
__entry->rx_estat = DESCR_FIELD(descr[1], 11, 6);
__entry->rx_stat = DESCR_FIELD(descr[1], 5, 2);
__entry->eop = DESCR_FIELD(descr[1], 1, 1);
__entry->dd = DESCR_FIELD(descr[1], 0, 0);
),
TP_printk("ring=%d descr=%u rss_hash=0x%x hdr_len=%u sph=%u rx_cntl=%u rsvd=0x%x pkt_type=%u rss_type=%u vlan_tag=%u next_desp=%u pkt_len=%u rsc_cnt=%u rx_estat=0x%x rx_stat=0x%x eop=%u dd=%u",
__entry->ring_idx, __entry->pointer, __entry->rss_hash,
__entry->hdr_len, __entry->sph, __entry->rx_cntl,
__entry->rsvd, __entry->pkt_type, __entry->rss_type,
__entry->vlan_tag, __entry->next_desp, __entry->pkt_len,
__entry->rsc_cnt, __entry->rx_estat, __entry->rx_stat,
__entry->eop, __entry->dd)
);
TRACE_EVENT(atl_tx_descr,
TP_PROTO(int ring_idx, unsigned int pointer, u64 *descr),
TP_ARGS(ring_idx, pointer, descr),
TP_STRUCT__entry(
__field(unsigned int, ring_idx)
__field(unsigned int, pointer)
/* Tx Descriptor */
__field(u64, data_buf_addr)
__field(u32, pay_len)
__field(u8, ct_en)
__field(u8, ct_idx)
__field(u16, rsvd2)
__field(u8, tx_cmd)
__field(u8, eop)
__field(u8, dd)
__field(u16, buf_len)
__field(u8, rsvd1)
__field(u8, des_typ)
),
TP_fast_assign(
__entry->ring_idx = ring_idx;
__entry->pointer = pointer;
__entry->data_buf_addr = descr[0];
__entry->pay_len = DESCR_FIELD(descr[1], 63, 46);
__entry->ct_en = DESCR_FIELD(descr[1], 45, 45);
__entry->ct_idx = DESCR_FIELD(descr[1], 44, 44);
__entry->rsvd2 = DESCR_FIELD(descr[1], 43, 30);
__entry->tx_cmd = DESCR_FIELD(descr[1], 29, 22);
__entry->eop = DESCR_FIELD(descr[1], 21, 21);
__entry->dd = DESCR_FIELD(descr[1], 20, 20);
__entry->buf_len = DESCR_FIELD(descr[1], 19, 4);
__entry->rsvd1 = DESCR_FIELD(descr[1], 3, 3);
__entry->des_typ = DESCR_FIELD(descr[1], 2, 0);
),
TP_printk("ring=%d descr=%u pay_len=%u ct_en=%u ct_idx=%u rsvd2=0x%x tx_cmd=0x%x eop=%u dd=%u buf_len=%u rsvd1=%u des_typ=0x%x",
__entry->ring_idx, __entry->pointer, __entry->pay_len,
__entry->ct_en, __entry->ct_idx, __entry->rsvd2,
__entry->tx_cmd, __entry->eop, __entry->dd, __entry->buf_len,
__entry->rsvd1, __entry->des_typ)
);
TRACE_EVENT(atl_tx_context_descr,
TP_PROTO(int ring_idx, unsigned int pointer, u64 *descr),
TP_ARGS(ring_idx, pointer, descr),
TP_STRUCT__entry(
__field(unsigned int, ring_idx)
__field(unsigned int, pointer)
/* Tx Context Descriptor */
__field(u8, out_len)
__field(u8, tun_len)
__field(u64, resvd3)
__field(u16, mss_len)
__field(u8, l4_len)
__field(u8, l3_len)
__field(u8, l2_len)
__field(u8, ct_cmd)
__field(u16, vlan_tag)
__field(u8, ct_idx)
__field(u8, des_typ)
),
TP_fast_assign(
__entry->ring_idx = ring_idx;
__entry->pointer = pointer;
__entry->out_len = DESCR_FIELD(descr[0], 63, 56);
__entry->tun_len = DESCR_FIELD(descr[0], 55, 48);
__entry->resvd3 = DESCR_FIELD(descr[0], 47, 0);
__entry->mss_len = DESCR_FIELD(descr[1], 63, 48);
__entry->l4_len = DESCR_FIELD(descr[1], 47, 40);
__entry->l3_len = DESCR_FIELD(descr[1], 39, 31);
__entry->l2_len = DESCR_FIELD(descr[1], 30, 24);
__entry->ct_cmd = DESCR_FIELD(descr[1], 23, 20);
__entry->vlan_tag = DESCR_FIELD(descr[1], 19, 4);
__entry->ct_idx = DESCR_FIELD(descr[1], 3, 3);
__entry->des_typ = DESCR_FIELD(descr[1], 2, 0);
),
TP_printk("ring=%d descr=%u out_len=%u tun_len=%u resvd3=%llu mss_len=%u l4_len=%u l3_len=%u l2_len=0x%x ct_cmd=%u vlan_tag=%u ct_idx=%u des_typ=0x%x",
__entry->ring_idx, __entry->pointer, __entry->out_len,
__entry->tun_len, __entry->resvd3, __entry->mss_len,
__entry->l4_len, __entry->l3_len, __entry->l2_len,
__entry->ct_cmd, __entry->vlan_tag, __entry->ct_idx,
__entry->des_typ)
);
#endif /* _ATL_TRACE_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE atl_trace
#include <trace/define_trace.h>

View file

@ -0,0 +1,36 @@
# SPDX-License-Identifier: GPL-2.0-only
# Atlantic Network Driver
#
# Copyright (C) 2019 aQuantia Corporation
# Copyright (C) 2019-2020 Marvell International Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
PKGCONFIG?=$(shell which pkg-config)
ifeq ($(PKGCONFIG),)
$(info Do you have pkg-config installed? If not, install it, e.g. on Ubuntu)
$(info $$ apt install pkg-config)
$(error pkg-config not found)
endif
LIBMNL_EXISTS=$(shell $(PKGCONFIG) --exists libmnl && echo "yes" || echo "no")
ifeq ($(LIBMNL_EXISTS),no)
$(info Do you have libmnl-dev installed? If not, install it, e.g. on Ubuntu)
$(info $$ apt install libmnl-dev)
$(error libmnl not found)
endif
CFLAGS+=-Wall -Werror -Wfatal-errors $(shell $(PKGCONFIG) --cflags libmnl) -I..
LDLIBS+=$(shell $(PKGCONFIG) --libs libmnl)
.PHONY: all clean
all: atlfwdtool
atlfwdtool: atlfwdtool.c atlfwd_args.c atlfwd_msg.c atlfwd_reply.c
$(LINK.c) $^ $(LDLIBS) -o $@
clean:
$(RM) atlfwdtool
$(RM) *.o

View file

@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "atlfwd_args.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libmnl/libmnl.h"
static void print_usage(const char *binname)
{
fprintf(stderr,
"Usage: %s [-d devname] [-v] command <command_args>\n\n",
binname);
fprintf(stderr, "%s\n", "Options:");
fprintf(stderr, "\t%s\n",
"-d devname\t- specify device name to work with");
fprintf(stderr, "\t%s\n", "-v\t\t- verbose mode");
fprintf(stderr, "%s\n", "");
fprintf(stderr, "%s\n", "Supported commands:");
fprintf(stderr, "\t%s\n",
"request_ring <flags> <ring_size> <buf_size> <page_order>");
fprintf(stderr, "\t%s\n", "release_ring <ring_index>");
fprintf(stderr, "\t%s\n", "enable_ring <ring_index>");
fprintf(stderr, "\t%s\n", "disable_ring <ring_index>");
fprintf(stderr, "\t%s\n", "dump_ring <ring_index>");
fprintf(stderr, "\t%s\n", "request_event <ring_index>");
fprintf(stderr, "\t%s\n", "release_event <ring_index>");
fprintf(stderr, "\t%s\n", "enable_event <ring_index>");
fprintf(stderr, "\t%s\n", "disable_event <ring_index>");
fprintf(stderr, "\t%s\n", "get_rx_queue_index <ring_index>");
fprintf(stderr, "\t%s\n", "get_tx_queue_index <ring_index>");
fprintf(stderr, "%s\n", "");
fprintf(stderr, "\t%s\n", "force_icmp_tx_via <ring_index>");
fprintf(stderr, "\t%s\n", "force_tx_via <ring_index>");
fprintf(stderr, "\t%s\n", "disable_redirections");
fprintf(stderr, "%s\n", "");
fprintf(stderr, "\t%s\n", "ring_status [<ring_index>]");
fprintf(stderr, "\t%s\n", "set_tx_bunch <bunch_size>");
}
#define STR_EQUAL(s1, s2) (strncmp((s1), (s2), MNL_ARRAY_SIZE((s2))) == 0)
static enum atlfwd_nl_command get_command(const char *str)
{
static const char cmd_req_ring[] = "request_ring";
static const char cmd_rel_ring[] = "release_ring";
static const char cmd_enable_ring[] = "enable_ring";
static const char cmd_disable_ring[] = "disable_ring";
static const char cmd_req_event[] = "request_event";
static const char cmd_rel_event[] = "release_event";
static const char cmd_enable_event[] = "enable_event";
static const char cmd_disable_event[] = "disable_event";
static const char cmd_dump_ring[] = "dump_ring";
static const char cmd_get_rx_queue[] = "get_rx_queue_index";
static const char cmd_get_tx_queue[] = "get_tx_queue_index";
static const char cmd_disable_redirections[] = "disable_redirections";
static const char cmd_force_icmp_tx_via[] = "force_icmp_tx_via";
static const char cmd_force_tx_via[] = "force_tx_via";
static const char cmd_ring_status[] = "ring_status";
static const char cmd_set_tx_bunch[] = "set_tx_bunch";
if (STR_EQUAL(str, cmd_req_ring))
return ATL_FWD_CMD_REQUEST_RING;
if (STR_EQUAL(str, cmd_rel_ring))
return ATL_FWD_CMD_RELEASE_RING;
if (STR_EQUAL(str, cmd_enable_ring))
return ATL_FWD_CMD_ENABLE_RING;
if (STR_EQUAL(str, cmd_disable_ring))
return ATL_FWD_CMD_DISABLE_RING;
if (STR_EQUAL(str, cmd_dump_ring))
return ATL_FWD_CMD_DUMP_RING;
if (STR_EQUAL(str, cmd_get_rx_queue))
return ATL_FWD_CMD_GET_RX_QUEUE;
if (STR_EQUAL(str, cmd_get_tx_queue))
return ATL_FWD_CMD_GET_TX_QUEUE;
if (STR_EQUAL(str, cmd_req_event))
return ATL_FWD_CMD_REQUEST_EVENT;
if (STR_EQUAL(str, cmd_rel_event))
return ATL_FWD_CMD_RELEASE_EVENT;
if (STR_EQUAL(str, cmd_enable_event))
return ATL_FWD_CMD_ENABLE_EVENT;
if (STR_EQUAL(str, cmd_disable_event))
return ATL_FWD_CMD_DISABLE_EVENT;
if (STR_EQUAL(str, cmd_disable_redirections))
return ATL_FWD_CMD_DISABLE_REDIRECTIONS;
if (STR_EQUAL(str, cmd_force_icmp_tx_via))
return ATL_FWD_CMD_FORCE_ICMP_TX_VIA;
if (STR_EQUAL(str, cmd_force_tx_via))
return ATL_FWD_CMD_FORCE_TX_VIA;
if (STR_EQUAL(str, cmd_ring_status))
return ATL_FWD_CMD_RING_STATUS;
if (STR_EQUAL(str, cmd_set_tx_bunch))
return ATL_FWD_CMD_SET_TX_BUNCH;
return ATL_FWD_CMD_UNSPEC;
}
static const char *get_arg(const int argc, char **argv)
{
if (argc == optind) {
/* not enough arguments => exit immediately */
print_usage(argv[0]);
exit(EINVAL);
}
return argv[optind++];
}
static const char *get_last_arg(const int argc, char **argv)
{
const char *result = get_arg(argc, argv);
if (optind != argc) {
/* too many arguments => exit immediately */
print_usage(argv[0]);
exit(EINVAL);
}
return result;
}
struct atlfwd_args *parse_args(const int argc, char **argv)
{
static struct atlfwd_args parsed_args;
int opt;
while ((opt = getopt(argc, argv, "d:v")) != -1) {
switch (opt) {
case 'd':
parsed_args.devname = optarg;
break;
case 'v':
parsed_args.verbose = true;
break;
default:
print_usage(argv[0]);
return NULL;
}
}
parsed_args.cmd = get_command(get_arg(argc, argv));
switch (parsed_args.cmd) {
case ATL_FWD_CMD_REQUEST_RING:
parsed_args.flags = (uint32_t)atoi(get_arg(argc, argv));
parsed_args.ring_size = (uint32_t)atoi(get_arg(argc, argv));
parsed_args.buf_size = (uint32_t)atoi(get_arg(argc, argv));
parsed_args.page_order =
(uint32_t)atoi(get_last_arg(argc, argv));
break;
case ATL_FWD_CMD_RING_STATUS:
if (optind == argc) {
parsed_args.ring_index = -1;
break;
}
/* fall through */
case ATL_FWD_CMD_RELEASE_RING:
/* fall through */
case ATL_FWD_CMD_ENABLE_RING:
/* fall through */
case ATL_FWD_CMD_DISABLE_RING:
/* fall through */
case ATL_FWD_CMD_DUMP_RING:
/* fall through */
case ATL_FWD_CMD_REQUEST_EVENT:
/* fall through */
case ATL_FWD_CMD_RELEASE_EVENT:
/* fall through */
case ATL_FWD_CMD_ENABLE_EVENT:
/* fall through */
case ATL_FWD_CMD_DISABLE_EVENT:
/* fall through */
case ATL_FWD_CMD_GET_RX_QUEUE:
/* fall through */
case ATL_FWD_CMD_GET_TX_QUEUE:
/* fall through */
case ATL_FWD_CMD_FORCE_ICMP_TX_VIA:
/* fall through */
case ATL_FWD_CMD_FORCE_TX_VIA:
parsed_args.ring_index =
(int32_t)atoi(get_last_arg(argc, argv));
break;
case ATL_FWD_CMD_SET_TX_BUNCH:
parsed_args.tx_bunch = (uint32_t)atoi(get_last_arg(argc, argv));
break;
case ATL_FWD_CMD_DISABLE_REDIRECTIONS:
break;
default:
print_usage(argv[0]);
return NULL;
}
return &parsed_args;
}

View file

@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATLFWD_ARGS_H_
#define _ATLFWD_ARGS_H_
#include <stdbool.h>
#include <stdint.h>
#include "atl_fwdnl.h"
struct atlfwd_args {
char *devname;
bool verbose;
enum atlfwd_nl_command cmd;
union {
struct {
uint32_t flags;
uint32_t ring_size;
uint32_t buf_size;
uint32_t page_order;
};
int32_t ring_index;
uint32_t tx_bunch;
};
};
struct atlfwd_args *parse_args(const int argc, char **argv);
#endif

View file

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATLFWD_CTX_H_
#define _ATLFWD_CTX_H_
#include "libmnl/libmnl.h"
#include "linux/genetlink.h"
#include "atl_fwdnl.h"
struct nl_context {
struct mnl_socket *sock;
unsigned int port;
unsigned int seq;
char *msgbuf;
unsigned int msgbufsize;
struct nlmsghdr *nlhdr;
struct genlmsghdr *gnlhdr;
int family_id;
char *devname;
int verbose;
};
#endif

View file

@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "atlfwd_msg.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int atlnl_msg_alloc(struct nl_context *ctx, const int msg_type, const int cmd,
const unsigned int flags, const int version)
{
ctx->msgbuf = malloc(MNL_SOCKET_BUFFER_SIZE);
if (!ctx->msgbuf)
return -ENOMEM;
ctx->msgbufsize = MNL_SOCKET_BUFFER_SIZE;
memset(ctx->msgbuf, 0, MNL_SOCKET_BUFFER_SIZE);
struct nlmsghdr *nlhdr = mnl_nlmsg_put_header(ctx->msgbuf);
nlhdr->nlmsg_type = msg_type;
nlhdr->nlmsg_flags = flags;
nlhdr->nlmsg_seq = ++ctx->seq;
ctx->nlhdr = nlhdr;
struct genlmsghdr *gnlhdr =
mnl_nlmsg_put_extra_header(nlhdr, sizeof(*gnlhdr));
gnlhdr->cmd = cmd;
gnlhdr->version = version;
ctx->gnlhdr = gnlhdr;
return 0;
}
int atlnl_msg_realloc(struct nl_context *ctx, const unsigned int new_size)
{
const unsigned int old_size = ctx->msgbufsize;
unsigned int nlhdr_offset = (char *)ctx->nlhdr - ctx->msgbuf;
unsigned int gnlhdr_offset = (char *)ctx->gnlhdr - ctx->msgbuf;
char *new_buff = realloc(ctx->msgbuf, new_size);
if (!new_buff)
return -ENOMEM;
memset(new_buff + old_size, 0, new_size - old_size);
ctx->msgbuf = new_buff;
ctx->msgbufsize = new_size;
ctx->nlhdr = (struct nlmsghdr *)(new_buff + nlhdr_offset);
ctx->gnlhdr = (struct genlmsghdr *)(new_buff + gnlhdr_offset);
return 0;
}
void atlnl_msg_free(struct nl_context *ctx)
{
if (ctx->msgbuf)
free(ctx->msgbuf);
ctx->msgbuf = NULL;
ctx->msgbufsize = 0;
ctx->nlhdr = NULL;
ctx->gnlhdr = NULL;
}

View file

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATLFWD_MSG_H_
#define _ATLFWD_MSG_H_
#include "atlfwd_ctx.h"
int atlnl_msg_alloc(struct nl_context *ctx, const int msg_type, const int cmd,
const unsigned int flags, const int version);
int atlnl_msg_realloc(struct nl_context *ctx, const unsigned int new_size);
void atlnl_msg_free(struct nl_context *ctx);
#endif

View file

@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "atlfwd_reply.h"
#include <errno.h>
#include "atlfwd_msg.h"
int atlnl_process_ack(struct nl_context *ctx, const ssize_t len)
{
struct nlmsgerr *nlerr = mnl_nlmsg_get_payload(ctx->nlhdr);
unsigned int tlv_offset = sizeof(*nlerr);
if (len < NLMSG_HDRLEN + tlv_offset)
return -EFAULT;
#ifdef NLM_F_ACK_TLVS
if (ctx->nlhdr->nlmsg_flags & NLM_F_ACK_TLVS) {
if (!(ctx->nlhdr->nlmsg_flags & NLM_F_CAPPED))
tlv_offset += MNL_ALIGN(
mnl_nlmsg_get_payload_len(&nlerr->msg));
const struct nlattr *attr;
mnl_attr_for_each(attr, ctx->nlhdr, tlv_offset)
{
if (mnl_attr_get_type(attr) == NLMSGERR_ATTR_MSG &&
(ctx->verbose || nlerr->error)) {
const char *msg = mnl_attr_get_str(attr);
fprintf(stderr, "netlink %s: %s\n",
nlerr->error ? "error" : "warning",
msg);
}
}
}
#endif
if (nlerr->error) {
errno = -nlerr->error;
perror("netlink error");
} else if (ctx->verbose) {
printf("%s\n", "got ack");
}
return nlerr->error;
}
int atlnl_process_reply(struct nl_context *ctx, mnl_cb_t reply_cb)
{
int ret = 0;
atlnl_msg_realloc(ctx, 4096);
do {
ssize_t len = mnl_socket_recvfrom(ctx->sock, ctx->msgbuf,
ctx->msgbufsize);
if (len == 0)
return 0;
if (len < NLMSG_HDRLEN)
return -EFAULT;
ctx->nlhdr = (struct nlmsghdr *)ctx->msgbuf;
if (ctx->nlhdr->nlmsg_type == NLMSG_ERROR)
return atlnl_process_ack(ctx, len);
ctx->gnlhdr = mnl_nlmsg_get_payload(ctx->nlhdr);
ret = mnl_cb_run(ctx->msgbuf, len, ctx->seq, ctx->port,
reply_cb, ctx);
} while (ret > 0);
return ret;
}

View file

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ATLFWD_REPLY_H_
#define _ATLFWD_REPLY_H_
#include "atlfwd_ctx.h"
int atlnl_process_ack(struct nl_context *ctx, const ssize_t len);
int atlnl_process_reply(struct nl_context *ctx, mnl_cb_t reply_cb);
#endif

View file

@ -0,0 +1,418 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "atlfwd_args.h"
#include "atlfwd_ctx.h"
#include "atlfwd_msg.h"
#include "atlfwd_reply.h"
#define ATL_FWD_CMD_STR(cmd)\
[cmd] = #cmd
static const char *cmd_str[NUM_ATL_FWD_CMD] = {
ATL_FWD_CMD_STR(ATL_FWD_CMD_REQUEST_RING),
ATL_FWD_CMD_STR(ATL_FWD_CMD_RELEASE_RING),
ATL_FWD_CMD_STR(ATL_FWD_CMD_ENABLE_RING),
ATL_FWD_CMD_STR(ATL_FWD_CMD_DISABLE_RING),
ATL_FWD_CMD_STR(ATL_FWD_CMD_DISABLE_REDIRECTIONS),
ATL_FWD_CMD_STR(ATL_FWD_CMD_FORCE_ICMP_TX_VIA),
ATL_FWD_CMD_STR(ATL_FWD_CMD_FORCE_TX_VIA),
ATL_FWD_CMD_STR(ATL_FWD_CMD_RING_STATUS),
ATL_FWD_CMD_STR(ATL_FWD_CMD_DUMP_RING),
ATL_FWD_CMD_STR(ATL_FWD_CMD_SET_TX_BUNCH),
ATL_FWD_CMD_STR(ATL_FWD_CMD_REQUEST_EVENT),
ATL_FWD_CMD_STR(ATL_FWD_CMD_RELEASE_EVENT),
ATL_FWD_CMD_STR(ATL_FWD_CMD_ENABLE_EVENT),
ATL_FWD_CMD_STR(ATL_FWD_CMD_DISABLE_EVENT),
ATL_FWD_CMD_STR(ATL_FWD_CMD_GET_RX_QUEUE),
ATL_FWD_CMD_STR(ATL_FWD_CMD_GET_TX_QUEUE),
};
#define ATL_FWD_ATTR_STR(attr)\
[attr] = #attr
static const char *attr_str[NUM_ATL_FWD_ATTR] = {
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_FLAGS),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_RING_SIZE),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_BUF_SIZE),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_PAGE_ORDER),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_RING_INDEX),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_RING_STATUS),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_RING_IS_TX),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_RING_FLAGS),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_TX_BUNCH_SIZE),
ATL_FWD_ATTR_STR(ATL_FWD_ATTR_QUEUE_INDEX),
};
/* get atl_fwd family id */
static int atlnl_family_cb(const struct nlmsghdr *nlhdr, void *data)
{
struct nl_context *ctx = (struct nl_context *)data;
const struct nlattr *attr;
ctx->family_id = 0;
mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN)
{
if (mnl_attr_get_type(attr) == CTRL_ATTR_FAMILY_ID) {
ctx->family_id = mnl_attr_get_u16(attr);
break;
}
}
return (ctx->family_id ? MNL_CB_OK : MNL_CB_ERROR);
}
static int atlnl_get_family_id(struct nl_context *ctx)
{
int ret = atlnl_msg_alloc(ctx, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST | NLM_F_ACK, 1);
if (ctx->verbose)
printf("calling %s\n", "CTRL_CMD_GETFAMILY");
if (ret != 0)
goto err_msgfree;
mnl_attr_put_strz(ctx->nlhdr, CTRL_ATTR_FAMILY_NAME, ATL_FWD_GENL_NAME);
if (mnl_socket_sendto(ctx->sock, ctx->nlhdr, ctx->nlhdr->nlmsg_len) < 0)
goto err_msgfree;
ret = atlnl_process_reply(ctx, atlnl_family_cb);
if (ret < 0)
goto err_msgfree;
ret = ctx->family_id ? 0 : -EADDRNOTAVAIL;
if (ctx->verbose)
printf("family id: %d\n", ctx->family_id);
err_msgfree:
atlnl_msg_free(ctx);
return ret;
}
static int atlnl_reqring_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *attr;
int ring_index = -1;
mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN)
{
if (mnl_attr_get_type(attr) == ATL_FWD_ATTR_RING_INDEX) {
ring_index = (int)mnl_attr_get_u32(attr);
break;
}
}
if (ring_index == -1) {
fprintf(stderr, "Error: %s attribute is missing in reply\n",
attr_str[ATL_FWD_ATTR_RING_INDEX]);
return MNL_CB_ERROR;
}
printf("Ring index: %d\n", ring_index);
return MNL_CB_OK;
}
static int atlnl_getqueue_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *attr;
int queue = -1;
mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN)
{
if (mnl_attr_get_type(attr) == ATL_FWD_ATTR_QUEUE_INDEX) {
queue = (int)mnl_attr_get_u32(attr);
break;
}
}
if (queue == -1) {
fprintf(stderr, "Error: %s attribute is missing in reply\n",
attr_str[ATL_FWD_ATTR_QUEUE_INDEX]);
return MNL_CB_ERROR;
}
printf("%d\n", queue);
return MNL_CB_OK;
}
static bool is_ring_created(const enum atlfwd_nl_ring_status ring_status)
{
switch (ring_status) {
case ATL_FWD_RING_STATUS_CREATED_DISABLED:
case ATL_FWD_RING_STATUS_ENABLED:
return true;
default:
return false;
}
return false;
}
static const char *
ring_status_string(const enum atlfwd_nl_ring_status ring_status)
{
static const char *status_str[NUM_ATL_FWD_RING_STATUS] = {
[ATL_FWD_RING_STATUS_INVALID] = "invalid status",
[ATL_FWD_RING_STATUS_RELEASED] = "released",
[ATL_FWD_RING_STATUS_CREATED_DISABLED] = "disabled",
[ATL_FWD_RING_STATUS_ENABLED] = "enabled",
};
return (ring_status < NUM_ATL_FWD_RING_STATUS ?
status_str[ring_status] :
status_str[ATL_FWD_RING_STATUS_INVALID]);
}
struct ring_status {
enum atlfwd_nl_ring_status status;
bool is_tx;
int size;
unsigned int flags;
};
static void print_ring_status(const int ring_index,
const struct ring_status *status)
{
char prefix[16];
snprintf(prefix, MNL_ARRAY_SIZE(prefix), "fwd_ring_%d_", ring_index);
printf("%sstatus:\t%s\n", prefix, ring_status_string(status->status));
printf("%sdirection:\t%s\n", prefix, status->is_tx ? "tx" : "rx");
if (is_ring_created(status->status)) {
printf("%sflags:\t%d\n", prefix, status->flags);
printf("%ssize:\t%d\n", prefix, status->size);
} else {
printf("%sflags:\t%s\n", prefix, "n/a");
printf("%ssize:\t%s\n", prefix, "n/a");
}
}
static int atlnl_ringstatus_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *attr;
int ring_index = -1;
struct ring_status status = {
.status = ATL_FWD_RING_STATUS_INVALID,
.is_tx = false,
.size = 0,
.flags = 0,
};
mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN)
{
switch (mnl_attr_get_type(attr)) {
case ATL_FWD_ATTR_RING_INDEX:
if (ring_index >= 0)
print_ring_status(ring_index, &status);
ring_index = (int)mnl_attr_get_u32(attr);
break;
case ATL_FWD_ATTR_RING_STATUS:
status.status = (int)mnl_attr_get_u32(attr);
break;
case ATL_FWD_ATTR_RING_IS_TX:
status.is_tx = (bool)mnl_attr_get_u32(attr);
break;
case ATL_FWD_ATTR_RING_SIZE:
status.size = (int)mnl_attr_get_u32(attr);
break;
case ATL_FWD_ATTR_RING_FLAGS:
status.flags = (unsigned int)mnl_attr_get_u32(attr);
break;
default:
fprintf(stderr, "Error: %s\n",
"Unexpected attribute in reply");
break;
}
}
if (ring_index == -1) {
fprintf(stderr, "Error: %s\n", "Incorrect reply");
return MNL_CB_ERROR;
}
print_ring_status(ring_index, &status);
return MNL_CB_OK;
}
static int atlnl_cmd_generic_u32_args(struct nl_context *ctx,
const enum atlfwd_nl_command cmd,
mnl_cb_t reply_cb, const int attr_count,
...)
{
int ret = atlnl_msg_alloc(ctx, ctx->family_id, cmd,
NLM_F_REQUEST | NLM_F_ACK, 1);
va_list args;
int i;
if (ret != 0) {
fprintf(stderr, "Error: %s\n", "message allocation failed.");
goto err_msgfree;
}
if (ctx->verbose)
printf("calling %s\n",
cmd_str[cmd] ? cmd_str[cmd] : "unknown command");
if (ctx->devname != NULL)
mnl_attr_put_strz(ctx->nlhdr, ATL_FWD_ATTR_IFNAME,
ctx->devname);
va_start(args, attr_count);
for (i = 0; i != attr_count; i++) {
const enum atlfwd_nl_attribute attr =
va_arg(args, enum atlfwd_nl_attribute);
const uint32_t value = va_arg(args, uint32_t);
mnl_attr_put_u32(ctx->nlhdr, attr, value);
if (ctx->verbose)
printf("\t(%s: %u)\n",
attr_str[attr] ? attr_str[attr] :
"unknown attribute",
value);
}
va_end(args);
if (mnl_socket_sendto(ctx->sock, ctx->nlhdr, ctx->nlhdr->nlmsg_len) < 0)
goto err_msgfree;
ret = atlnl_process_reply(ctx, reply_cb);
err_msgfree:
atlnl_msg_free(ctx);
return ret;
}
int main(int argc, char **argv)
{
struct atlfwd_args *args = parse_args(argc, argv);
static struct nl_context nlctx;
int result = EINVAL;
int ret = 0;
if (!args)
return result;
nlctx.devname = args->devname;
nlctx.verbose = args->verbose;
result = ECONNREFUSED;
/* open socket */
nlctx.sock = mnl_socket_open(NETLINK_GENERIC);
if (!nlctx.sock)
return result;
#ifdef NETLINK_EXT_ACK
/* request extended acknowledgment */
unsigned int true_val = 1;
ret = mnl_socket_setsockopt(nlctx.sock, NETLINK_EXT_ACK, &true_val,
sizeof(true_val));
if (ret < 0)
goto err_sockclose;
#endif
/* bind and get the port id */
ret = mnl_socket_bind(nlctx.sock, 0, MNL_SOCKET_AUTOPID);
if (ret < 0) {
result = -ret;
goto err_sockclose;
}
nlctx.port = mnl_socket_get_portid(nlctx.sock);
/* parse command line options */
result = EINVAL;
/* obtain family id by name */
ret = atlnl_get_family_id(&nlctx);
if (ret < 0)
goto err_sockclose;
/* process command(s) */
switch (args->cmd) {
case ATL_FWD_CMD_REQUEST_RING:
ret = atlnl_cmd_generic_u32_args(
&nlctx, args->cmd, atlnl_reqring_cb, 4,
ATL_FWD_ATTR_FLAGS, args->flags, ATL_FWD_ATTR_RING_SIZE,
args->ring_size, ATL_FWD_ATTR_BUF_SIZE, args->buf_size,
ATL_FWD_ATTR_PAGE_ORDER, args->page_order);
break;
case ATL_FWD_CMD_RELEASE_RING:
/* fall through */
case ATL_FWD_CMD_ENABLE_RING:
/* fall through */
case ATL_FWD_CMD_DISABLE_RING:
/* fall through */
case ATL_FWD_CMD_DUMP_RING:
/* fall through */
case ATL_FWD_CMD_REQUEST_EVENT:
/* fall through */
case ATL_FWD_CMD_RELEASE_EVENT:
/* fall through */
case ATL_FWD_CMD_ENABLE_EVENT:
/* fall through */
case ATL_FWD_CMD_DISABLE_EVENT:
/* fall through */
case ATL_FWD_CMD_FORCE_ICMP_TX_VIA:
/* fall through */
case ATL_FWD_CMD_FORCE_TX_VIA:
ret = atlnl_cmd_generic_u32_args(&nlctx, args->cmd, NULL, 1,
ATL_FWD_ATTR_RING_INDEX,
(uint32_t)args->ring_index);
break;
case ATL_FWD_CMD_GET_RX_QUEUE:
/* fall through */
case ATL_FWD_CMD_GET_TX_QUEUE:
ret = atlnl_cmd_generic_u32_args(&nlctx, args->cmd,
atlnl_getqueue_cb, 1,
ATL_FWD_ATTR_RING_INDEX,
(uint32_t)args->ring_index);
break;
case ATL_FWD_CMD_RING_STATUS:
if (args->ring_index < 0)
ret = atlnl_cmd_generic_u32_args(
&nlctx, args->cmd, atlnl_ringstatus_cb, 0);
else
ret = atlnl_cmd_generic_u32_args(
&nlctx, args->cmd, atlnl_ringstatus_cb, 1,
ATL_FWD_ATTR_RING_INDEX,
(uint32_t)args->ring_index);
break;
case ATL_FWD_CMD_DISABLE_REDIRECTIONS:
ret = atlnl_cmd_generic_u32_args(&nlctx, args->cmd, NULL, 0);
break;
case ATL_FWD_CMD_SET_TX_BUNCH:
ret = atlnl_cmd_generic_u32_args(&nlctx, args->cmd, NULL, 1,
ATL_FWD_ATTR_TX_BUNCH_SIZE,
args->tx_bunch);
break;
default:
fprintf(stderr, "Unknown command %d\n", args->cmd);
goto err_sockclose;
}
result = 0;
if (ret < 0) {
result = -ret;
perror("cmd error");
}
err_sockclose:
mnl_socket_close(nlctx.sock);
return result;
}

View file

@ -0,0 +1,79 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef MSS_EGRESS_REGS_HEADER
#define MSS_EGRESS_REGS_HEADER
#define MSS_EGRESS_CTL_REGISTER_ADDR 0x00005002
#define MSS_EGRESS_SA_EXPIRED_STATUS_REGISTER_ADDR 0x00005060
#define MSS_EGRESS_SA_THRESHOLD_EXPIRED_STATUS_REGISTER_ADDR 0x00005062
#define MSS_EGRESS_LUT_ADDR_CTL_REGISTER_ADDR 0x00005080
#define MSS_EGRESS_LUT_CTL_REGISTER_ADDR 0x00005081
#define MSS_EGRESS_LUT_DATA_CTL_REGISTER_ADDR 0x000050A0
struct mss_egress_ctl_register {
union {
struct {
unsigned int soft_reset : 1;
unsigned int drop_kay_packet : 1;
unsigned int drop_egprc_lut_miss : 1;
unsigned int gcm_start : 1;
unsigned int gcm_test_mode : 1;
unsigned int unmatched_use_sc_0 : 1;
unsigned int drop_invalid_sa_sc_packets : 1;
unsigned int reserved0 : 1;
/* Should always be set to 0. */
unsigned int external_classification_enable : 1;
unsigned int icv_lsb_8bytes_enable : 1;
unsigned int high_prio : 1;
unsigned int clear_counter : 1;
unsigned int clear_global_time : 1;
unsigned int ethertype_explicit_sectag_lsb : 3;
} bits_0;
unsigned short word_0;
};
union {
struct {
unsigned int ethertype_explicit_sectag_msb : 13;
unsigned int reserved0 : 3;
} bits_1;
unsigned short word_1;
};
};
struct mss_egress_lut_addr_ctl_register {
union {
struct {
unsigned int lut_addr : 9;
unsigned int reserved0 : 3;
/* 0x0 : Egress MAC Control FIlter (CTLF) LUT
* 0x1 : Egress Classification LUT
* 0x2 : Egress SC/SA LUT
* 0x3 : Egress SMIB
*/
unsigned int lut_select : 4;
} bits_0;
unsigned short word_0;
};
};
struct mss_egress_lut_ctl_register {
union {
struct {
unsigned int reserved0 : 14;
unsigned int lut_read : 1;
unsigned int lut_write : 1;
} bits_0;
unsigned short word_0;
};
};
#endif /* MSS_EGRESS_REGS_HEADER */

View file

@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef MSS_INGRESS_REGS_HEADER
#define MSS_INGRESS_REGS_HEADER
#define SEC_INGRESS_PACKET_EDIT_CTL_REGISTER_ADDR 0x00007035
#define MSS_INGRESS_CTL_REGISTER_ADDR 0x0000800E
#define MSS_INGRESS_LUT_ADDR_CTL_REGISTER_ADDR 0x00008080
#define MSS_INGRESS_LUT_CTL_REGISTER_ADDR 0x00008081
#define MSS_INGRESS_LUT_DATA_CTL_REGISTER_ADDR 0x000080A0
struct mss_ingress_ctl_register {
union {
struct {
unsigned int soft_reset : 1;
unsigned int operation_point_to_point : 1;
unsigned int create_sci : 1;
/* Unused */
unsigned int mask_short_length_error : 1;
unsigned int drop_kay_packet : 1;
unsigned int drop_igprc_miss : 1;
/* Unused */
unsigned int check_icv : 1;
unsigned int clear_global_time : 1;
unsigned int clear_count : 1;
unsigned int high_prio : 1;
unsigned int remove_sectag : 1;
unsigned int global_validate_frames : 2;
unsigned int icv_lsb_8bytes_enabled : 1;
unsigned int reserved0 : 2;
} bits_0;
unsigned short word_0;
};
union {
struct {
unsigned int reserved0 : 16;
} bits_1;
unsigned short word_1;
};
};
struct mss_ingress_lut_addr_ctl_register {
union {
struct {
unsigned int lut_addr : 9;
unsigned int reserved0 : 3;
/* 0x0 : Ingress Pre-Security MAC Control FIlter
* (IGPRCTLF) LUT
* 0x1 : Ingress Pre-Security Classification LUT (IGPRC)
* 0x2 : Ingress Packet Format (IGPFMT) SAKey LUT
* 0x3 : Ingress Packet Format (IGPFMT) SC/SA LUT
* 0x4 : Ingress Post-Security Classification LUT
* (IGPOC)
* 0x5 : Ingress Post-Security MAC Control Filter
* (IGPOCTLF) LUT
* 0x6 : Ingress MIB (IGMIB)
*/
unsigned int lut_select : 4;
} bits_0;
unsigned short word_0;
};
};
struct mss_ingress_lut_ctl_register {
union {
struct {
unsigned int reserved0 : 14;
unsigned int lut_read : 1;
unsigned int lut_write : 1;
} bits_0;
unsigned short word_0;
};
};
#endif /* MSS_INGRESS_REGS_HEADER */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,334 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __MACSEC_API_H__
#define __MACSEC_API_H__
#include "atl_hw.h"
#include "macsec_struct.h"
#define NUMROWS_INGRESSPRECTLFRECORD 24
#define ROWOFFSET_INGRESSPRECTLFRECORD 0
#define NUMROWS_INGRESSPRECLASSRECORD 48
#define ROWOFFSET_INGRESSPRECLASSRECORD 0
#define NUMROWS_INGRESSPOSTCLASSRECORD 48
#define ROWOFFSET_INGRESSPOSTCLASSRECORD 0
#define NUMROWS_INGRESSSCRECORD 32
#define ROWOFFSET_INGRESSSCRECORD 0
#define NUMROWS_INGRESSSARECORD 32
#define ROWOFFSET_INGRESSSARECORD 32
#define NUMROWS_INGRESSSAKEYRECORD 32
#define ROWOFFSET_INGRESSSAKEYRECORD 0
#define NUMROWS_INGRESSPOSTCTLFRECORD 24
#define ROWOFFSET_INGRESSPOSTCTLFRECORD 0
#define NUMROWS_EGRESSCTLFRECORD 24
#define ROWOFFSET_EGRESSCTLFRECORD 0
#define NUMROWS_EGRESSCLASSRECORD 48
#define ROWOFFSET_EGRESSCLASSRECORD 0
#define NUMROWS_EGRESSSCRECORD 32
#define ROWOFFSET_EGRESSSCRECORD 0
#define NUMROWS_EGRESSSARECORD 32
#define ROWOFFSET_EGRESSSARECORD 32
#define NUMROWS_EGRESSSAKEYRECORD 32
#define ROWOFFSET_EGRESSSAKEYRECORD 96
/*! Read the raw table data from the specified row of the Egress CTL
* Filter table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 23).
*/
int aq_mss_get_egress_ctlf_record(struct atl_hw *hw,
struct aq_mss_egress_ctlf_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Egress CTL Filter table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 23).
*/
int aq_mss_set_egress_ctlf_record(struct atl_hw *hw,
const struct aq_mss_egress_ctlf_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Egress
* Packet Classifier table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 47).
*/
int aq_mss_get_egress_class_record(struct atl_hw *hw,
struct aq_mss_egress_class_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Egress Packet Classifier table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write (max 47).
*/
int aq_mss_set_egress_class_record(struct atl_hw *hw,
const struct aq_mss_egress_class_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Egress SC
* Lookup table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 31).
*/
int aq_mss_get_egress_sc_record(struct atl_hw *hw,
struct aq_mss_egress_sc_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Egress SC Lookup table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write (max 31).
*/
int aq_mss_set_egress_sc_record(struct atl_hw *hw,
const struct aq_mss_egress_sc_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Egress SA
* Lookup table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 31).
*/
int aq_mss_get_egress_sa_record(struct atl_hw *hw,
struct aq_mss_egress_sa_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Egress SA Lookup table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write (max 31).
*/
int aq_mss_set_egress_sa_record(struct atl_hw *hw,
const struct aq_mss_egress_sa_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Egress SA
* Key Lookup table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 31).
*/
int aq_mss_get_egress_sakey_record(struct atl_hw *hw,
struct aq_mss_egress_sakey_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Egress SA Key Lookup table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write (max 31).
*/
int aq_mss_set_egress_sakey_record(struct atl_hw *hw,
const struct aq_mss_egress_sakey_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Ingress
* Pre-MACSec CTL Filter table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 23).
*/
int aq_mss_get_ingress_prectlf_record(struct atl_hw *hw,
struct aq_mss_ingress_prectlf_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Ingress Pre-MACSec CTL Filter table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 23).
*/
int aq_mss_set_ingress_prectlf_record(
struct atl_hw *hw, const struct aq_mss_ingress_prectlf_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Ingress
* Pre-MACSec Packet Classifier table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 47).
*/
int aq_mss_get_ingress_preclass_record(
struct atl_hw *hw, struct aq_mss_ingress_preclass_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Ingress Pre-MACSec Packet Classifier table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 47).
*/
int aq_mss_set_ingress_preclass_record(
struct atl_hw *hw, const struct aq_mss_ingress_preclass_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Ingress SC
* Lookup table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 31).
*/
int aq_mss_get_ingress_sc_record(struct atl_hw *hw,
struct aq_mss_ingress_sc_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Ingress SC Lookup table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 31).
*/
int aq_mss_set_ingress_sc_record(struct atl_hw *hw,
const struct aq_mss_ingress_sc_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Ingress SA
* Lookup table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 31).
*/
int aq_mss_get_ingress_sa_record(struct atl_hw *hw,
struct aq_mss_ingress_sa_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Ingress SA Lookup table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 31).
*/
int aq_mss_set_ingress_sa_record(struct atl_hw *hw,
const struct aq_mss_ingress_sa_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Ingress SA
* Key Lookup table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 31).
*/
int aq_mss_get_ingress_sakey_record(struct atl_hw *hw,
struct aq_mss_ingress_sakey_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Ingress SA Key Lookup table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 31).
*/
int aq_mss_set_ingress_sakey_record(
struct atl_hw *hw, const struct aq_mss_ingress_sakey_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Ingress
* Post-MACSec Packet Classifier table, and unpack it into the
* fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 48).
*/
int aq_mss_get_ingress_postclass_record(
struct atl_hw *hw, struct aq_mss_ingress_postclass_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Ingress Post-MACSec Packet Classifier table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 48).
*/
int aq_mss_set_ingress_postclass_record(
struct atl_hw *hw, const struct aq_mss_ingress_postclass_record *rec,
u16 table_index);
/*! Read the raw table data from the specified row of the Ingress
* Post-MACSec CTL Filter table, and unpack it into the fields of rec.
* rec - [OUT] The raw table row data will be unpacked into the fields of rec.
* table_index - The table row to read (max 23).
*/
int aq_mss_get_ingress_postctlf_record(
struct atl_hw *hw, struct aq_mss_ingress_postctlf_record *rec,
u16 table_index);
/*! Pack the fields of rec, and write the packed data into the
* specified row of the Ingress Post-MACSec CTL Filter table.
* rec - [IN] The bitfield values to write to the table row.
* table_index - The table row to write(max 23).
*/
int aq_mss_set_ingress_postctlf_record(
struct atl_hw *hw, const struct aq_mss_ingress_postctlf_record *rec,
u16 table_index);
/*! Read the counters for the specified SC, and unpack them into the
* fields of counters.
* counters - [OUT] The raw table row data will be unpacked here.
* sc_index - The table row to read (max 31).
*/
int aq_mss_get_egress_sc_counters(struct atl_hw *hw,
struct aq_mss_egress_sc_counters *counters,
u16 sc_index);
/*! Read the counters for the specified SA, and unpack them into the
* fields of counters.
* counters - [OUT] The raw table row data will be unpacked here.
* sa_index - The table row to read (max 31).
*/
int aq_mss_get_egress_sa_counters(struct atl_hw *hw,
struct aq_mss_egress_sa_counters *counters,
u16 sa_index);
/*! Read the counters for the common egress counters, and unpack them
* into the fields of counters.
* counters - [OUT] The raw table row data will be unpacked here.
*/
int aq_mss_get_egress_common_counters(
struct atl_hw *hw, struct aq_mss_egress_common_counters *counters);
/*! Clear all Egress counters to 0.*/
int aq_mss_clear_egress_counters(struct atl_hw *hw);
/*! Read the counters for the specified SA, and unpack them into the
* fields of counters.
* counters - [OUT] The raw table row data will be unpacked here.
* sa_index - The table row to read (max 31).
*/
int aq_mss_get_ingress_sa_counters(struct atl_hw *hw,
struct aq_mss_ingress_sa_counters *counters,
u16 sa_index);
/*! Read the counters for the common ingress counters, and unpack them
* into the fields of counters.
* counters - [OUT] The raw table row data will be unpacked here.
*/
int aq_mss_get_ingress_common_counters(
struct atl_hw *hw, struct aq_mss_ingress_common_counters *counters);
/*! Clear all Ingress counters to 0. */
int aq_mss_clear_ingress_counters(struct atl_hw *hw);
/*! Get Egress SA expired. */
int aq_mss_get_egress_sa_expired(struct atl_hw *hw, u32 *expired);
/*! Get Egress SA threshold expired. */
int aq_mss_get_egress_sa_threshold_expired(struct atl_hw *hw,
u32 *expired);
/*! Set Egress SA expired. */
int aq_mss_set_egress_sa_expired(struct atl_hw *hw, u32 expired);
/*! Set Egress SA threshold expired. */
int aq_mss_set_egress_sa_threshold_expired(struct atl_hw *hw, u32 expired);
/*! Set Drop IGPRC miss packets */
int aq_mss_set_drop_igprc_miss_packets(struct atl_hw *hw, bool drop);
/*! Set Packet Edit Control register */
int aq_mss_set_packet_edit_control(struct atl_hw *hw, u32 control);
#endif /* __MACSEC_API_H__ */

View file

@ -0,0 +1,920 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Atlantic Network Driver
*
* Copyright (C) 2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _MACSEC_STRUCT_H_
#define _MACSEC_STRUCT_H_
/*! Represents the bitfields of a single row in the Egress CTL Filter
* table.
*/
struct aq_mss_egress_ctlf_record {
/*! This is used to store the 48 bit value used to compare SA, DA or
* halfDA+half SA value.
*/
u32 sa_da[2];
/*! This is used to store the 16 bit ethertype value used for
* comparison.
*/
u32 eth_type;
/*! The match mask is per-nibble. 0 means don't care, i.e. every value
* will match successfully. The total data is 64 bit, i.e. 16 nibbles
* masks.
*/
u32 match_mask;
/*! 0: No compare, i.e. This entry is not used
* 1: compare DA only
* 2: compare SA only
* 3: compare half DA + half SA
* 4: compare ether type only
* 5: compare DA + ethertype
* 6: compare SA + ethertype
* 7: compare DA+ range.
*/
u32 match_type;
/*! 0: Bypass the remaining modules if matched.
* 1: Forward to next module for more classifications.
*/
u32 action;
};
/*! Represents the bitfields of a single row in the Egress Packet
* Classifier table.
*/
struct aq_mss_egress_class_record {
/*! VLAN ID field. */
u32 vlan_id;
/*! VLAN UP field. */
u32 vlan_up;
/*! VLAN Present in the Packet. */
u32 vlan_valid;
/*! The 8 bit value used to compare with extracted value for byte 3. */
u32 byte3;
/*! The 8 bit value used to compare with extracted value for byte 2. */
u32 byte2;
/*! The 8 bit value used to compare with extracted value for byte 1. */
u32 byte1;
/*! The 8 bit value used to compare with extracted value for byte 0. */
u32 byte0;
/*! The 8 bit TCI field used to compare with extracted value. */
u32 tci;
/*! The 64 bit SCI field in the SecTAG. */
u32 sci[2];
/*! The 16 bit Ethertype (in the clear) field used to compare with
* extracted value.
*/
u32 eth_type;
/*! This is to specify the 40bit SNAP header if the SNAP header's mask
* is enabled.
*/
u32 snap[2];
/*! This is to specify the 24bit LLC header if the LLC header's mask is
* enabled.
*/
u32 llc;
/*! The 48 bit MAC_SA field used to compare with extracted value. */
u32 mac_sa[2];
/*! The 48 bit MAC_DA field used to compare with extracted value. */
u32 mac_da[2];
/*! The 32 bit Packet number used to compare with extracted value. */
u32 pn;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte3_location;
/*! 0: don't care
* 1: enable comparison of extracted byte pointed by byte 3 location.
*/
u32 byte3_mask;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte2_location;
/*! 0: don't care
* 1: enable comparison of extracted byte pointed by byte 2 location.
*/
u32 byte2_mask;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte1_location;
/*! 0: don't care
* 1: enable comparison of extracted byte pointed by byte 1 location.
*/
u32 byte1_mask;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte0_location;
/*! 0: don't care
* 1: enable comparison of extracted byte pointed by byte 0 location.
*/
u32 byte0_mask;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of extracted VLAN ID field.
*/
u32 vlan_id_mask;
/*! 0: don't care
* 1: enable comparison of extracted VLAN UP field.
*/
u32 vlan_up_mask;
/*! 0: don't care
* 1: enable comparison of extracted VLAN Valid field.
*/
u32 vlan_valid_mask;
/*! This is bit mask to enable comparison the 8 bit TCI field,
* including the AN field.
* For explicit SECTAG, AN is hardware controlled. For sending
* packet w/ explicit SECTAG, rest of the TCI fields are directly
* from the SECTAG.
*/
u32 tci_mask;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of SCI
* Note: If this field is not 0, this means the input packet's
* SECTAG is explicitly tagged and MACSEC module will only update
* the MSDU.
* PN number is hardware controlled.
*/
u32 sci_mask;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of Ethertype.
*/
u32 eth_type_mask;
/*! Mask is per-byte.
* 0: don't care and no SNAP header exist.
* 1: compare the SNAP header.
* If this bit is set to 1, the extracted filed will assume the
* SNAP header exist as encapsulated in 802.3 (RFC 1042). I.E. the
* next 5 bytes after the the LLC header is SNAP header.
*/
u32 snap_mask;
/*! 0: don't care and no LLC header exist.
* 1: compare the LLC header.
* If this bit is set to 1, the extracted filed will assume the
* LLC header exist as encapsulated in 802.3 (RFC 1042). I.E. the
* next three bytes after the 802.3MAC header is LLC header.
*/
u32 llc_mask;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of MAC_SA.
*/
u32 sa_mask;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of MAC_DA.
*/
u32 da_mask;
/*! Mask is per-byte. */
u32 pn_mask;
/*! Reserved. This bit should be always 0. */
u32 eight02dot2;
/*! 1: For explicit sectag case use TCI_SC from table
* 0: use TCI_SC from explicit sectag.
*/
u32 tci_sc;
/*! 1: For explicit sectag case,use TCI_V,ES,SCB,E,C from table
* 0: use TCI_V,ES,SCB,E,C from explicit sectag.
*/
u32 tci_87543;
/*! 1: indicates that incoming packet has explicit sectag. */
u32 exp_sectag_en;
/*! If packet matches and tagged as controlled-packet, this SC/SA
* index is used for later SC and SA table lookup.
*/
u32 sc_idx;
/*! This field is used to specify how many SA entries are
* associated with 1 SC entry.
* 2'b00: 1 SC has 4 SA.
* SC index is equivalent to {SC_Index[4:2], 1'b0}.
* SA index is equivalent to {SC_Index[4:2], SC entry's current AN[1:0]
* 2'b10: 1 SC has 2 SA.
* SC index is equivalent to SC_Index[4:1]
* SA index is equivalent to {SC_Index[4:1], SC entry's current AN[0]}
* 2'b11: 1 SC has 1 SA. No SC entry exists for the specific SA.
* SA index is equivalent to SC_Index[4:0]
* Note: if specified as 2'b11, hardware AN roll over is not
* supported.
*/
u32 sc_sa;
/*! 0: the packets will be sent to MAC FIFO
* 1: The packets will be sent to Debug/Loopback FIFO.
* If the above's action is drop, this bit has no meaning.
*/
u32 debug;
/*! 0: forward to remaining modules
* 1: bypass the next encryption modules. This packet is considered
* un-control packet.
* 2: drop
* 3: Reserved.
*/
u32 action;
/*! 0: Not valid entry. This entry is not used
* 1: valid entry.
*/
u32 valid;
};
/*! Represents the bitfields of a single row in the Egress SC Lookup table. */
struct aq_mss_egress_sc_record {
/*! This is to specify when the SC was first used. Set by HW. */
u32 start_time;
/*! This is to specify when the SC was last used. Set by HW. */
u32 stop_time;
/*! This is to specify which of the SA entries are used by current HW.
* Note: This value need to be set by SW after reset. It will be
* automatically updated by HW, if AN roll over is enabled.
*/
u32 curr_an;
/*! 0: Clear the SA Valid Bit after PN expiry.
* 1: Do not Clear the SA Valid bit after PN expiry of the current SA.
* When the Enable AN roll over is set, S/W does not need to
* program the new SA's and the H/W will automatically roll over
* between the SA's without session expiry.
* For normal operation, Enable AN Roll over will be set to '0'
* and in which case, the SW needs to program the new SA values
* after the current PN expires.
*/
u32 an_roll;
/*! This is the TCI field used if packet is not explicitly tagged. */
u32 tci;
/*! This value indicates the offset where the decryption will start.
* [[Values of 0, 4, 8-50].
*/
u32 enc_off;
/*! 0: Do not protect frames, all the packets will be forwarded
* unchanged. MIB counter (OutPktsUntagged) will be updated.
* 1: Protect.
*/
u32 protect;
/*! 0: when none of the SA related to SC has inUse set.
* 1: when either of the SA related to the SC has inUse set.
* This bit is set by HW.
*/
u32 recv;
/*! 0: H/W Clears this bit on the first use.
* 1: SW updates this entry, when programming the SC Table.
*/
u32 fresh;
/*! AES Key size
* 00 - 128bits
* 01 - 192bits
* 10 - 256bits
* 11 - Reserved.
*/
u32 sak_len;
/*! 0: Invalid SC
* 1: Valid SC.
*/
u32 valid;
};
/*! Represents the bitfields of a single row in the Egress SA Lookup table. */
struct aq_mss_egress_sa_record {
/*! This is to specify when the SC was first used. Set by HW. */
u32 start_time;
/*! This is to specify when the SC was last used. Set by HW. */
u32 stop_time;
/*! This is set by SW and updated by HW to store the Next PN number
* used for encryption.
*/
u32 next_pn;
/*! The Next_PN number is going to wrapped around from 0xFFFF_FFFF
* to 0. set by HW.
*/
u32 sat_pn;
/*! 0: This SA is in use.
* 1: This SA is Fresh and set by SW.
*/
u32 fresh;
/*! 0: Invalid SA
* 1: Valid SA.
*/
u32 valid;
};
/*! Represents the bitfields of a single row in the Egress SA Key
* Lookup table.
*/
struct aq_mss_egress_sakey_record {
/*! Key for AES-GCM processing. */
u32 key[8];
};
/*! Represents the bitfields of a single row in the Ingress Pre-MACSec
* CTL Filter table.
*/
struct aq_mss_ingress_prectlf_record {
/*! This is used to store the 48 bit value used to compare SA, DA
* or halfDA+half SA value.
*/
u32 sa_da[2];
/*! This is used to store the 16 bit ethertype value used for
* comparison.
*/
u32 eth_type;
/*! The match mask is per-nibble. 0 means don't care, i.e. every
* value will match successfully. The total data is 64 bit, i.e.
* 16 nibbles masks.
*/
u32 match_mask;
/*! 0: No compare, i.e. This entry is not used
* 1: compare DA only
* 2: compare SA only
* 3: compare half DA + half SA
* 4: compare ether type only
* 5: compare DA + ethertype
* 6: compare SA + ethertype
* 7: compare DA+ range.
*/
u32 match_type;
/*! 0: Bypass the remaining modules if matched.
* 1: Forward to next module for more classifications.
*/
u32 action;
};
/*! Represents the bitfields of a single row in the Ingress Pre-MACSec
* Packet Classifier table.
*/
struct aq_mss_ingress_preclass_record {
/*! The 64 bit SCI field used to compare with extracted value.
* Should have SCI value in case TCI[SCI_SEND] == 0. This will be
* used for ICV calculation.
*/
u32 sci[2];
/*! The 8 bit TCI field used to compare with extracted value. */
u32 tci;
/*! 8 bit encryption offset. */
u32 encr_offset;
/*! The 16 bit Ethertype (in the clear) field used to compare with
* extracted value.
*/
u32 eth_type;
/*! This is to specify the 40bit SNAP header if the SNAP header's
* mask is enabled.
*/
u32 snap[2];
/*! This is to specify the 24bit LLC header if the LLC header's
* mask is enabled.
*/
u32 llc;
/*! The 48 bit MAC_SA field used to compare with extracted value. */
u32 mac_sa[2];
/*! The 48 bit MAC_DA field used to compare with extracted value. */
u32 mac_da[2];
/*! 0: this is to compare with non-LPBK packet
* 1: this is to compare with LPBK packet.
* This value is used to compare with a controlled-tag which goes
* with the packet when looped back from Egress port.
*/
u32 lpbk_packet;
/*! The value of this bit mask will affects how the SC index and SA
* index created.
* 2'b00: 1 SC has 4 SA.
* SC index is equivalent to {SC_Index[4:2], 1'b0}.
* SA index is equivalent to {SC_Index[4:2], SECTAG's AN[1:0]}
* Here AN bits are not compared.
* 2'b10: 1 SC has 2 SA.
* SC index is equivalent to SC_Index[4:1]
* SA index is equivalent to {SC_Index[4:1], SECTAG's AN[0]}
* Compare AN[1] field only
* 2'b11: 1 SC has 1 SA. No SC entry exists for the specific SA.
* SA index is equivalent to SC_Index[4:0]
* AN[1:0] bits are compared.
* NOTE: This design is to supports different usage of AN. User
* can either ping-pong buffer 2 SA by using only the AN[0] bit.
* Or use 4 SA per SC by use AN[1:0] bits. Or even treat each SA
* as independent. i.e. AN[1:0] is just another matching pointer
* to select SA.
*/
u32 an_mask;
/*! This is bit mask to enable comparison the upper 6 bits TCI
* field, which does not include the AN field.
* 0: don't compare
* 1: enable comparison of the bits.
*/
u32 tci_mask;
/*! 0: don't care
* 1: enable comparison of SCI.
*/
u32 sci_mask;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of Ethertype.
*/
u32 eth_type_mask;
/*! Mask is per-byte.
* 0: don't care and no SNAP header exist.
* 1: compare the SNAP header.
* If this bit is set to 1, the extracted filed will assume the
* SNAP header exist as encapsulated in 802.3 (RFC 1042). I.E. the
* next 5 bytes after the the LLC header is SNAP header.
*/
u32 snap_mask;
/*! Mask is per-byte.
* 0: don't care and no LLC header exist.
* 1: compare the LLC header.
* If this bit is set to 1, the extracted filed will assume the
* LLC header exist as encapsulated in 802.3 (RFC 1042). I.E. the
* next three bytes after the 802.3MAC header is LLC header.
*/
u32 llc_mask;
/*! Reserved. This bit should be always 0. */
u32 _802_2_encapsulate;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of MAC_SA.
*/
u32 sa_mask;
/*! Mask is per-byte.
* 0: don't care
* 1: enable comparison of MAC_DA.
*/
u32 da_mask;
/*! 0: don't care
* 1: enable checking if this is loopback packet or not.
*/
u32 lpbk_mask;
/*! If packet matches and tagged as controlled-packet. This SC/SA
* index is used for later SC and SA table lookup.
*/
u32 sc_idx;
/*! 0: the packets will be sent to MAC FIFO
* 1: The packets will be sent to Debug/Loopback FIFO.
* If the above's action is drop. This bit has no meaning.
*/
u32 proc_dest;
/*! 0: Process: Forward to next two modules for 802.1AE decryption.
* 1: Process but keep SECTAG: Forward to next two modules for
* 802.1AE decryption but keep the MACSEC header with added error
* code information. ICV will be stripped for all control packets.
* 2: Bypass: Bypass the next two decryption modules but processed
* by post-classification.
* 3: Drop: drop this packet and update counts accordingly.
*/
u32 action;
/*! 0: This is a controlled-port packet if matched.
* 1: This is an uncontrolled-port packet if matched.
*/
u32 ctrl_unctrl;
/*! Use the SCI value from the Table if 'SC' bit of the input
* packet is not present.
*/
u32 sci_from_table;
/*! Reserved. */
u32 reserved;
/*! 0: Not valid entry. This entry is not used
* 1: valid entry.
*/
u32 valid;
};
/*! Represents the bitfields of a single row in the Ingress SC Lookup table. */
struct aq_mss_ingress_sc_record {
/*! This is to specify when the SC was first used. Set by HW. */
u32 stop_time;
/*! This is to specify when the SC was first used. Set by HW. */
u32 start_time;
/*! 0: Strict
* 1: Check
* 2: Disabled.
*/
u32 validate_frames;
/*! 1: Replay control enabled.
* 0: replay control disabled.
*/
u32 replay_protect;
/*! This is to specify the window range for anti-replay. Default is 0.
* 0: is strict order enforcement.
*/
u32 anti_replay_window;
/*! 0: when none of the SA related to SC has inUse set.
* 1: when either of the SA related to the SC has inUse set.
* This bit is set by HW.
*/
u32 receiving;
/*! 0: when hardware processed the SC for the first time, it clears
* this bit
* 1: This bit is set by SW, when it sets up the SC.
*/
u32 fresh;
/*! 0: The AN number will not automatically roll over if Next_PN is
* saturated.
* 1: The AN number will automatically roll over if Next_PN is
* saturated.
* Rollover is valid only after expiry. Normal roll over between
* SA's should be normal process.
*/
u32 an_rol;
/*! Reserved. */
u32 reserved;
/*! 0: Invalid SC
* 1: Valid SC.
*/
u32 valid;
};
/*! Represents the bitfields of a single row in the Ingress SA Lookup table. */
struct aq_mss_ingress_sa_record {
/*! This is to specify when the SC was first used. Set by HW. */
u32 stop_time;
/*! This is to specify when the SC was first used. Set by HW. */
u32 start_time;
/*! This is updated by HW to store the expected NextPN number for
* anti-replay.
*/
u32 next_pn;
/*! The Next_PN number is going to wrapped around from 0XFFFF_FFFF
* to 0. set by HW.
*/
u32 sat_nextpn;
/*! 0: This SA is not yet used.
* 1: This SA is inUse.
*/
u32 in_use;
/*! 0: when hardware processed the SC for the first time, it clears
* this timer
* 1: This bit is set by SW, when it sets up the SC.
*/
u32 fresh;
/*! Reserved. */
u32 reserved;
/*! 0: Invalid SA.
* 1: Valid SA.
*/
u32 valid;
};
/*! Represents the bitfields of a single row in the Ingress SA Key
* Lookup table.
*/
struct aq_mss_ingress_sakey_record {
/*! Key for AES-GCM processing. */
u32 key[8];
/*! AES key size
* 00 - 128bits
* 01 - 192bits
* 10 - 256bits
* 11 - reserved.
*/
u32 key_len;
};
/*! Represents the bitfields of a single row in the Ingress Post-
* MACSec Packet Classifier table.
*/
struct aq_mss_ingress_postclass_record {
/*! The 8 bit value used to compare with extracted value for byte 0. */
u32 byte0;
/*! The 8 bit value used to compare with extracted value for byte 1. */
u32 byte1;
/*! The 8 bit value used to compare with extracted value for byte 2. */
u32 byte2;
/*! The 8 bit value used to compare with extracted value for byte 3. */
u32 byte3;
/*! Ethertype in the packet. */
u32 eth_type;
/*! Ether Type value > 1500 (0x5dc). */
u32 eth_type_valid;
/*! VLAN ID after parsing. */
u32 vlan_id;
/*! VLAN priority after parsing. */
u32 vlan_up;
/*! Valid VLAN coding. */
u32 vlan_valid;
/*! SA index. */
u32 sai;
/*! SAI hit, i.e. controlled packet. */
u32 sai_hit;
/*! Mask for payload ethertype field. */
u32 eth_type_mask;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte3_location;
/*! Mask for Byte Offset 3. */
u32 byte3_mask;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte2_location;
/*! Mask for Byte Offset 2. */
u32 byte2_mask;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte1_location;
/*! Mask for Byte Offset 1. */
u32 byte1_mask;
/*! 0~63: byte location used extracted by packets comparator, which
* can be anything from the first 64 bytes of the MAC packets.
* This byte location counted from MAC' DA address. i.e. set to 0
* will point to byte 0 of DA address.
*/
u32 byte0_location;
/*! Mask for Byte Offset 0. */
u32 byte0_mask;
/*! Mask for Ethertype valid field. Indicates 802.3 vs. Other. */
u32 eth_type_valid_mask;
/*! Mask for VLAN ID field. */
u32 vlan_id_mask;
/*! Mask for VLAN UP field. */
u32 vlan_up_mask;
/*! Mask for VLAN valid field. */
u32 vlan_valid_mask;
/*! Mask for SAI. */
u32 sai_mask;
/*! Mask for SAI_HIT. */
u32 sai_hit_mask;
/*! Action if only first level matches and second level does not.
* 0: pass
* 1: drop (fail).
*/
u32 firstlevel_actions;
/*! Action if both first and second level matched.
* 0: pass
* 1: drop (fail).
*/
u32 secondlevel_actions;
/*! Reserved. */
u32 reserved;
/*! 0: Not valid entry. This entry is not used
* 1: valid entry.
*/
u32 valid;
};
/*! Represents the bitfields of a single row in the Ingress Post-
* MACSec CTL Filter table.
*/
struct aq_mss_ingress_postctlf_record {
/*! This is used to store the 48 bit value used to compare SA, DA
* or halfDA+half SA value.
*/
u32 sa_da[2];
/*! This is used to store the 16 bit ethertype value used for
* comparison.
*/
u32 eth_type;
/*! The match mask is per-nibble. 0 means don't care, i.e. every
* value will match successfully. The total data is 64 bit, i.e.
* 16 nibbles masks.
*/
u32 match_mask;
/*! 0: No compare, i.e. This entry is not used
* 1: compare DA only
* 2: compare SA only
* 3: compare half DA + half SA
* 4: compare ether type only
* 5: compare DA + ethertype
* 6: compare SA + ethertype
* 7: compare DA+ range.
*/
u32 match_type;
/*! 0: Bypass the remaining modules if matched.
* 1: Forward to next module for more classifications.
*/
u32 action;
};
/*! Represents the Egress MIB counters for a single SC. Counters are
* 64 bits, lower 32 bits in field[0].
*/
struct aq_mss_egress_sc_counters {
/*! The number of integrity protected but not encrypted packets
* for this transmitting SC.
*/
u32 sc_protected_pkts[2];
/*! The number of integrity protected and encrypted packets for
* this transmitting SC.
*/
u32 sc_encrypted_pkts[2];
/*! The number of plain text octets that are integrity protected
* but not encrypted on the transmitting SC.
*/
u32 sc_protected_octets[2];
/*! The number of plain text octets that are integrity protected
* and encrypted on the transmitting SC.
*/
u32 sc_encrypted_octets[2];
};
/*! Represents the Egress MIB counters for a single SA. Counters are
* 64 bits, lower 32 bits in field[0].
*/
struct aq_mss_egress_sa_counters {
/*! The number of dropped packets for this transmitting SA. */
u32 sa_hit_drop_redirect[2];
/*! TODO */
u32 sa_protected2_pkts[2];
/*! The number of integrity protected but not encrypted packets
* for this transmitting SA.
*/
u32 sa_protected_pkts[2];
/*! The number of integrity protected and encrypted packets for
* this transmitting SA.
*/
u32 sa_encrypted_pkts[2];
};
/*! Represents the common Egress MIB counters; the counter not
* associated with a particular SC/SA. Counters are 64 bits, lower 32
* bits in field[0].
*/
struct aq_mss_egress_common_counters {
/*! The number of transmitted packets classified as MAC_CTL packets. */
u32 ctl_pkt[2];
/*! The number of transmitted packets that did not match any rows
* in the Egress Packet Classifier table.
*/
u32 unknown_sa_pkts[2];
/*! The number of transmitted packets where the SC table entry has
* protect=0 (so packets are forwarded unchanged).
*/
u32 untagged_pkts[2];
/*! The number of transmitted packets discarded because the packet
* length is greater than the ifMtu of the Common Port interface.
*/
u32 too_long[2];
/*! The number of transmitted packets for which table memory was
* affected by an ECC error during processing.
*/
u32 ecc_error_pkts[2];
/*! The number of transmitted packets for where the matched row in
* the Egress Packet Classifier table has action=drop.
*/
u32 unctrl_hit_drop_redir[2];
};
/*! Represents the Ingress MIB counters for a single SA. Counters are
* 64 bits, lower 32 bits in field[0].
*/
struct aq_mss_ingress_sa_counters {
/*! For this SA, the number of received packets without a SecTAG. */
u32 untagged_hit_pkts[2];
/*! For this SA, the number of received packets that were dropped. */
u32 ctrl_hit_drop_redir_pkts[2];
/*! For this SA which is not currently in use, the number of
* received packets that have been discarded, and have either the
* packets encrypted or the matched row in the Ingress SC Lookup
* table has validate_frames=Strict.
*/
u32 not_using_sa[2];
/*! For this SA which is not currently in use, the number of
* received, unencrypted, packets with the matched row in the
* Ingress SC Lookup table has validate_frames!=Strict.
*/
u32 unused_sa[2];
/*! For this SA, the number discarded packets with the condition
* that the packets are not valid and one of the following
* conditions are true: either the matched row in the Ingress SC
* Lookup table has validate_frames=Strict or the packets
* encrypted.
*/
u32 not_valid_pkts[2];
/*! For this SA, the number of packets with the condition that the
* packets are not valid and the matched row in the Ingress SC
* Lookup table has validate_frames=Check.
*/
u32 invalid_pkts[2];
/*! For this SA, the number of validated packets. */
u32 ok_pkts[2];
/*! For this SC, the number of received packets that have been
* discarded with the condition: the matched row in the Ingress
* SC Lookup table has replay_protect=1 and the PN of the packet
* is lower than the lower bound replay check PN.
*/
u32 late_pkts[2];
/*! For this SA, the number of packets with the condition that the
* PN of the packets is lower than the lower bound replay
* protection PN.
*/
u32 delayed_pkts[2];
/*! For this SC, the number of packets with the following condition:
* - the matched row in the Ingress SC Lookup table has
* replay_protect=0 or
* - the matched row in the Ingress SC Lookup table has
* replay_protect=1 and the packet is not encrypted and the
* integrity check has failed or
* - the matched row in the Ingress SC Lookup table has
* replay_protect=1 and the packet is encrypted and integrity
* check has failed.
*/
u32 unchecked_pkts[2];
/*! The number of octets of plaintext recovered from received
* packets that were integrity protected but not encrypted.
*/
u32 validated_octets[2];
/*! The number of octets of plaintext recovered from received
* packets that were integrity protected and encrypted.
*/
u32 decrypted_octets[2];
};
/*! Represents the common Ingress MIB counters; the counter not
* associated with a particular SA. Counters are 64 bits, lower 32
* bits in field[0].
*/
struct aq_mss_ingress_common_counters {
/*! The number of received packets classified as MAC_CTL packets. */
u32 ctl_pkts[2];
/*! The number of received packets with the MAC security tag
* (SecTAG), not matching any rows in the Ingress Pre-MACSec
* Packet Classifier table.
*/
u32 tagged_miss_pkts[2];
/*! The number of received packets without the MAC security tag
* (SecTAG), not matching any rows in the Ingress Pre-MACSec
* Packet Classifier table.
*/
u32 untagged_miss_pkts[2];
/*! The number of received packets discarded without the MAC
* security tag (SecTAG) and with the matched row in the Ingress
* SC Lookup table having validate_frames=Strict.
*/
u32 notag_pkts[2];
/*! The number of received packets without the MAC security tag
* (SecTAG) and with the matched row in the Ingress SC Lookup
* table having validate_frames!=Strict.
*/
u32 untagged_pkts[2];
/*! The number of received packets discarded with an invalid
* SecTAG or a zero value PN or an invalid ICV.
*/
u32 bad_tag_pkts[2];
/*! The number of received packets discarded with unknown SCI
* information with the condition:
* the matched row in the Ingress SC Lookup table has
* validate_frames=Strict or the C bit in the SecTAG is set.
*/
u32 no_sci_pkts[2];
/*! The number of received packets with unknown SCI with the condition:
* The matched row in the Ingress SC Lookup table has
* validate_frames!=Strict and the C bit in the SecTAG is not set.
*/
u32 unknown_sci_pkts[2];
/*! The number of received packets by the controlled port service
* that passed the Ingress Post-MACSec Packet Classifier table
* check.
*/
u32 ctrl_prt_pass_pkts[2];
/*! The number of received packets by the uncontrolled port
* service that passed the Ingress Post-MACSec Packet Classifier
* table check.
*/
u32 unctrl_prt_pass_pkts[2];
/*! The number of received packets by the controlled port service
* that failed the Ingress Post-MACSec Packet Classifier table
* check.
*/
u32 ctrl_prt_fail_pkts[2];
/*! The number of received packets by the uncontrolled port
* service that failed the Ingress Post-MACSec Packet Classifier
* table check.
*/
u32 unctrl_prt_fail_pkts[2];
/*! The number of received packets discarded because the packet
* length is greater than the ifMtu of the Common Port interface.
*/
u32 too_long_pkts[2];
/*! The number of received packets classified as MAC_CTL by the
* Ingress Post-MACSec CTL Filter table.
*/
u32 igpoc_ctl_pkts[2];
/*! The number of received packets for which table memory was
* affected by an ECC error during processing.
*/
u32 ecc_error_pkts[2];
/*! The number of received packets by the uncontrolled port
* service that were dropped.
*/
u32 unctrl_hit_drop_redir[2];
};
#endif

View file

@ -0,0 +1,352 @@
Version 1.1.11
==============
[ATLDRV-1567] - forwarding driver issue with adj_freq
Version 1.1.10
==============
[ATLDRV-1559] - Add support of icmp proto to the rx filters
[ATLDRV-1401] - A2: Filtering
[ATLDRV-1513] - PM runtime failure
Version 1.1.9
=============
[ATLDRV-1505] - FWD: New dev ids
[ATLDRV-1529] - FWD: Segmentation fault occurs for user operations
[ATLDRV-1542] - FWD: build fails on 5.8-rcX
[ATLDRV-1552] - Fwd: build fails with old gcc
[ATLDRV-1553] - Fwd: BUG message when LOCKDEP is enabled
Version 1.1.8
=============
[ATLDRV-1438] - Fwd: port PTP from Linux driver
Version 1.1.7
=============
[ATLDRV-1469] - AQ083: After suspend resume EAPOL frames is not received
[ATLDRV-1497] - Incorrect temperature is displayed in hwmon
[ATLDRV-1519] - AQ086: Configuring default ring size
Version 1.1.6
=============
[ATLDRV-1355] - Sporadically unable to set EEE
[ATLDRV-1407] - AQ078: Link comes back up after rmmod
[ATLDRV-1416] - Failed probe during stress remove/rescan
[ATLDRV-1435] - AQ080: MACSEC offload packet loss under concurrent streams
[ATLDRV-1436] - AQ079: non-MACSEC_Data packets received in macsec0
[ATLDRV-1456] - FWD: BUG: KASAN: use-after-free in atl_remove+0x1c9/0x220 [atlantic_fwd]
[ATLDRV-1458] - AQ082: Accounting for RX bytes and packets
[ATLDRV-1441] - FWD: Run Flexera Code Insight
[ATLDRV-1457] - FWD: Linux-5.7.0 support
Version 1.1.5
=============
[ATLDRV-1382] - Failed install Driver 1.1.4 on Antigua
[ATLDRV-1384] - WOL does not work on Antigua
Version 1.1.4
==============
[ATLDRV-1371] - Add supported eee with 2.5G and 5G
[ATLDRV-1376] - Send notification when macsec offload turned on/off
[ATLDRV-1378] - check FW capability instead of FW version for MSM Apply Settings
Version 1.1.3
==============
[ATLDRV-1185] - Enable stripping of padding doesnt work on Felicity card
[ATLDRV-1351] - Problem with setting mtu
[ATLDRV-1352] - Implement stripping of padding
[ATLDRV-1354] - Incorrect thermal thresholds
[ATLDRV-1359] - Secure all packets when macsec service ON
Version 1.1.2
==============
[ATLDRV-1348] - FWD: IPv6 and L4 ntuple filters not work
Version 1.1.1
==============
[ATLDRV-1315] - driver 1.1.0 crashes at start on A1
[ATLDRV-1317] - A2: Incorrect ntuple L4 filter behavior
[ATLDRV-1325] - A2: can not turn off flow control on Antigua
[ATLDRV-1326] - A2: Cannot get EEE settings on Antigua
[ATLDRV-1331] - A2: update reset sequence
Version 1.1.0
==============
[ATLDRV-1210] - A2: FWD: Basic functionality and bringup
[ATLDRV-1212] - A2: FWD: RX flow filters infrastructure
Version 1.0.31
==============
[ATLDRV-1243] - Clean up MACsec implementation
[ATLDRV-1135] - ethtool shows incorrect Flow Control information
[ATLDRV-1229] - HW access on mdo_dev_stop(ctx->prepare)
[ATLDRV-1256] - AQ072: Static analysis error in atl_setup_datapath()
[ATLDRV-1303] - Make driver C90 compilable
[ATLDRV-1064] - Fwd: Software flowcontrol workaround adaptations
Version 1.0.30
==============
[ATLDRV-1217] - FWD driver refactoring for unit tests
[ATLDRV-1102] - TCP/UDP checksum workaround for non-zero padded packets doesn't work
[ATLDRV-1138] - vlan promiscuous mode is not enabled, when deleting the last vlan interface
[ATLDRV-1139] - after ifconfig -multicast Unicast filters registers have valid values for multicast addresses
[ATLDRV-1174] - FWD: deleting MacSec iface doesn't clear Ingress/Egress SA and SAKey records
[ATLDRV-1193] - No link UP if driver was started in MSI mode before being started in legacy mode
[ATLDRV-1208] - Driver crashes after link down up in D3 state
[ATLDRV-1209] - FWD: Kernel panic after configuring macsec
[ATLDRV-1218] - Kernel panic upon macsec0 deletion
[ATLDRV-1220] - AQ069: atl_fwd_receive_skb() support NAPI
[ATLDRV-1222] - atl_rotate_keys() produces unexpected results for 192-bit keys (24-bytes)
[ATLDRV-1194] - Add trace of descriptors for normal traffic
Version 1.0.29
==============
[ATLDRV-1139] - after ifconfig -multicast Unicast filters registers have valid values for multicast addresses
[ATLDRV-1170] - macsec key cleaned on SA updates
[ATLDRV-1171] - AQ066: MSI index leak for head pointer writeback event
[ATLDRV-1172] - AQ068: shift-count-overflow error in DESCR_FIELD()
[ATLDRV-1187] - atlfwdtool not compiled on Centos
[ATLDRV-1186] - FWD: MACsec offload (cleanup)
Version 1.0.28
==============
[ATLDRV-1128] - Traffic ingress on FWD ring
[ATLDRV-1157] - Apply upstream patch for macsec offload to 4.14
[ATLDRV-1158] - Implement Macsec EGRESS offload
[ATLDRV-1159] - Implement Macsec INGRESS offload
[ATLDRV-1160] - Provide macsec statistics to ethtool -s
[ATLDRV-1161] - Add get_tx_queue_index command to atlfwdtool
[ATLDRV-1153] - traffic dropped when lro enabled
[ATLDRV-777] - FWD: BCM: Better support for systems with legacy interrupts
Version 1.0.27
==============
[ATLDRV-1132] - Media detect is disabled after sleep
[ATLDRV-1129] - Add support for egress FWD queue pause/resume
Version 1.0.26
==============
[ATLDRV-660] - Wrong device ids are present in atl_main.c
[ATLDRV-1113] - VLAN-PROMISC is turned off when PROMISC flag is set
[ATLDRV-1124] - link is not up at 2.5G speed
[ATLDRV-1105] - Allocate/destroy/disable/enable channels
[ATLDRV-1114] - Send traffic via FWD ring
[ATLDRV-1116] - Add support for FWD rings status / stats via ethtool
[ATLDRV-1120] - reproduce descriptor writeback on disabled ring
[ATLDRV-1126] - Print statistics for created FWD rings only
Version 1.0.25
==============
[ATLDRV-1118] - Page fault in atl_fwd_init_ring() upon ring creation (ATL_FWR_ALLOC_BUFS is not set)
[ATLDRV-1119] - AQ058: again Bug CR 2491137 - Crash caused due to 32 Tx descs are zero'ed
Version 1.0.24
==============
[ATLDRV-1112] - descriptor corruption after suspend/resume
[ATLDRV-1097] - EEE must be disabled by-default
[ATLDRV-1099] - Strange EEE configuration behavior
[ATLDRV-1107] - Strange message in dmesg
[ATLDRV-1108] - FW not loaded after FWD driver unloaded on B0
[ATLDRV-899] - Fwd driver: add more loopbacks to ethtool private flags
[ATLDRV-1057] - Support for network loopback
Version 1.0.23
==============
[ATLDRV-983] - runtime D3 support
[ATLDRV-1023] - AQ056: Wake on link race condition with host power manament
Version 1.0.22
==============
fix build of fwd api
Version 1.0.21
==============
[ATLDRV-871] - pause frame does not turn off
[ATLDRV-994] - failed set StripEtherPadding
[ATLDRV-1062] - FWD: IPA: AQ058: Crash caused due to 32 Tx descs are zero'ed
[ATLDRV-1066] - AQ062: atl_update_eth_stats() - sleeping function called from invalid context
[ATLDRV-1067] - AQ061: AQC FWD ring descriptor memory reset
[ATLDRV-1068] - AQ060: AQC Rx accessing buffer address from previous atl_fwd_request_ring()
[ATLDRV-1075] - AQ063: v1.0.20 driver - atl_update_thermal_flag() missing atl_unlock_fw()
[ATLDRV-803] - AQ040: Validation for Interrupt Moderation in FWD API
[ATLDRV-1022] - Media detect control
Version 1.0.20
==============
net: aquantia: Re-initialize fwd rings on driver resume
Do entire suspend from dev_pm_ops->suspend() (AQ052)
Prevent atl_update_global_stats() calls when suspended (AQ056) (ATLDRV-1021)
Implement thermal throttling (ATLDRV-1009)
Add log message when high temperature treshold is reached (ATLDRV-998))
Implement FW heartbeat watchdog (AQ010) (ATLDRV-502)
Workaround checksum offload bugs (ATLDRV-1013)
Version 1.0.19
==============
Fix freeing of buf vaddr vector in FWD API (AQ050)
Add a private data pointer to atl_fwd_mem_ops (AQ051)
Implement wake-on-link-up support (AQ049)
Introduce the forwarding engine API versioning (AQ048)
Fix build errors (AQ047)
Delay enabling bus-mastering until after reset sequence (AQ045)
Validate interrupt moderation delays in FWD API (AQ040)
Version 1.0.18
==============
Fix double free in the new fwd ring memory allocation code.
Fix 32-bit warnings (AQ036)
Add a pointer for forwarding engine's private data. (AQ043)
Version 1.0.17
==============
Add compatibility to kernel 4.1
create empty atl_hwmon_init for old kernels
Fix legacy interrupt handling
Fix MSM register writes
Free link interrupt before reconfiguring the rings
Implement FW settings table access
Use new FW interface to control ethernet padding stripping
Disable LRO by default until all issues are resolved.
Clamp max number of rings as reported by ethtool to num_present_cpus() (AQ037)
Use __GFP_NOWARN for Rx buffers only in NAPI poll (AQ037)
Put PHY into low-power state on ifdown or disabling link
Improve error reporting for forwarding-engine ring creation (AQ041)
Allow custom memory allocators for atl_fwd_request_ring() (AQ041)
Version 1.0.16
==============
Introduce the config options for forwarding API (AQ033)
Implement catchall ethtool filter (AQ034)
Fix MSI-X vectors being disabled on cpu unplug (AQ032)
Fix Kconfig changes for AQ033
Version 1.0.15
==============
Reset statistics via private flag
Implement interrupt moderation for offload engine's rings
Introduce a FW mutex, handle link interrupts in a workqueue
Implement PHY temperature sensor
Report ring's and buffer's physical addresses on fwd ring request
Implement ethtool control for stripping Rx ethernet frame's padding
Version 1.0.14
==============
Remove old IPA implementation (replaced by atl_fwd.[ch])
Fix forwarding engine interface
Change the driver name to reflect the new module name
Implement Linux PM support
Implement WoL support
Version 1.0.13
==============
Integrate Linux VLAN filter hw acceleration with ethtool filters
Rename the module to atlantic-fwd
Version 1.0.12
==============
Expose PHY LPI state via ethtool private flags
Version 1.0.11
==============
Minor IPA interface fixes
Turn on/off Rx XOFF flow control when needed
Fix advertizing EEE link modes and show LPI timer
Add some HW counters from RMON MIB
Add Host and Lost counters to statistics
Add more Felicity PCI IDs
Add ethtool private flags tracking LPI state
Version 1.0.10
==============
Add module parameter to force align IP headers
Version 1.0.9
==============
Add AQC100 / -111 / -112 IDs
Fix adding vlan with vlan id > 64
Fix ethtool IPv4 filter reporting (AQ022)
Implement ethtool interface for ethertype filters.
Restore backward compat for Linux 4.4
Version 1.0.8
==============
Fix checkpatch.pl and smatch warnings and errors
Version 1.0.7
==============
Make Tx ring free space watermarks configurable
Misc filter fixes
Implement ethtool interface for VLAN filters
Implement pause statistics
Version 1.0.6
==============
Implement compat fixes for kernels without IPv6 ethtool NFC API
Version 1.0.5
==============
Fix ifdown / ifup link state race
Fix autoneg re-enable on pre-ethtool_ksettings kernels
Implement n-tuple Rx filters
Misc clean-ups
Version 1.0.4
==============
Switch to using spin lock for updating global stats
Fix out-of-bounds dma_sync_single_range() for packet header
Fix an Rx error corner case
Refactor delayed dma-unmapping of headers for RSC
Implement HW loopbacks
Implement interrupt coalescing ethtool method
Version 1.0.3
==============
Fix crash in mutex_lock on init
Add compat cast for ->ndo_get_stats64 for old kernels
Get rid of struct atl_common_stats
Refactor ring structure to remove the need for -fplan9-extensions
Drop -fplan9-extensions
Version 1.0.2
==============
Implement setting the MAC address
Rework VLAN filters
Implement per-ring counters
Fix Tx queue start / stop handling
Implement basic netdev stats
Implement ethtool stats
Version 1.0.1
==============
Update the README
Refactor the ring structure
Fix LRO timers timebase
Initial Qualcomm IPA support
Refactor Link management
Implement Flow Control
Implement EEE
Set default link on init
Implement dynamic datapath reconfiguration
Implement ethtool -> [gs]et_ringparam, set_channels
Version 1.0.0
==============
Add dma-mapping tracepoints
Implement VLAN offloads
Implement promiscuous mode and UC / MC filters
Implement HW header splitting, fix Rx buffer mgmt for RSC
Fix multicast filter support
Implement new reset sequence
Implement multi-fw switch
Implement new link management
Introduce link interrupt
Implement ethtool set_settings and _ksettings variants
Implement multiring support
Implement RSS support
Enable jumbo frames
Fix ethtool RSS methods
Add RDM / TDM descriptor thresholds
Switch to INTR block based interrupt throttling
Enable LRO
Implement linear skb mode