mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller: "Here is what we have this merge window: 1) Support SW steering for mlx5 Connect-X6Dx, from Yevgeny Kliteynik. 2) Add RSS multi group support to octeontx2-pf driver, from Geetha Sowjanya. 3) Add support for KS8851 PHY. From Marek Vasut. 4) Add support for GarfieldPeak bluetooth controller from Kiran K. 5) Add support for half-duplex tcan4x5x can controllers. 6) Add batch skb rx processing to bcrm63xx_enet, from Sieng Piaw Liew. 7) Rework RX port offload infrastructure, particularly wrt, UDP tunneling, from Jakub Kicinski. 8) Add BCM72116 PHY support, from Florian Fainelli. 9) Remove Dsa specific notifiers, they are unnecessary. From Vladimir Oltean. 10) Add support for picosecond rx delay in dwmac-meson8b chips. From Martin Blumenstingl. 11) Support TSO on xfrm interfaces from Eyal Birger. 12) Add support for MP_PRIO to mptcp stack, from Geliang Tang. 13) Support BCM4908 integrated switch, from Rafał Miłecki. 14) Support for directly accessing kernel module variables via module BTF info, from Andrii Naryiko. 15) Add DASH (esktop and mobile Architecture for System Hardware) support to r8169 driver, from Heiner Kallweit. 16) Add rx vlan filtering to dpaa2-eth, from Ionut-robert Aron. 17) Add support for 100 base0x SFP devices, from Bjarni Jonasson. 18) Support link aggregation in DSA, from Tobias Waldekranz. 19) Support for bitwidse atomics in bpf, from Brendan Jackman. 20) SmartEEE support in at803x driver, from Russell King. 21) Add support for flow based tunneling to GTP, from Pravin B Shelar. 22) Allow arbitrary number of interconnrcts in ipa, from Alex Elder. 23) TLS RX offload for bonding, from Tariq Toukan. 24) RX decap offklload support in mac80211, from Felix Fietkou. 25) devlink health saupport in octeontx2-af, from George Cherian. 26) Add TTL attr to SCM_TIMESTAMP_OPT_STATS, from Yousuk Seung 27) Delegated actionss support in mptcp, from Paolo Abeni. 28) Support receive timestamping when doin zerocopy tcp receive. From Arjun Ray. 29) HTB offload support for mlx5, from Maxim Mikityanskiy. 30) UDP GRO forwarding, from Maxim Mikityanskiy. 31) TAPRIO offloading in dsa hellcreek driver, from Kurt Kanzenbach. 32) Weighted random twos choice algorithm for ipvs, from Darby Payne. 33) Fix netdev registration deadlock, from Johannes Berg. 34) Various conversions to new tasklet api, from EmilRenner Berthing. 35) Bulk skb allocations in veth, from Lorenzo Bianconi. 36) New ethtool interface for lane setting, from Danielle Ratson. 37) Offload failiure notifications for routes, from Amit Cohen. 38) BCM4908 support, from Rafał Miłecki. 39) Support several new iwlwifi chips, from Ihab Zhaika. 40) Flow drector support for ipv6 in i40e, from Przemyslaw Patynowski. 41) Support for mhi prrotocols, from Loic Poulain. 42) Optimize bpf program stats. 43) Implement RFC6056, for better port randomization, from Eric Dumazet. 44) hsr tag offloading support from George McCollister. 45) Netpoll support in qede, from Bhaskar Upadhaya. 46) 2005/400g speed support in bonding 3ad mode, from Nikolay Aleksandrov. 47) Netlink event support in mptcp, from Florian Westphal. 48) Better skbuff caching, from Alexander Lobakin. 49) MRP (Media Redundancy Protocol) offloading in DSA and a few drivers, from Horatiu Vultur. 50) mqprio saupport in mvneta, from Maxime Chevallier. 51) Remove of_phy_attach, no longer needed, from Florian Fainelli" * git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1766 commits) octeontx2-pf: Fix otx2_get_fecparam() cteontx2-pf: cn10k: Prevent harmless double shift bugs net: stmmac: Add PCI bus info to ethtool driver query output ptp: ptp_clockmatrix: clean-up - parenthesis around a == b are unnecessary ptp: ptp_clockmatrix: Simplify code - remove unnecessary `err` variable. ptp: ptp_clockmatrix: Coding style - tighten vertical spacing. ptp: ptp_clockmatrix: Clean-up dev_*() messages. ptp: ptp_clockmatrix: Remove unused header declarations. ptp: ptp_clockmatrix: Add alignment of 1 PPS to idtcm_perout_enable. ptp: ptp_clockmatrix: Add wait_for_sys_apll_dpll_lock. net: stmmac: dwmac-sun8i: Add a shutdown callback net: stmmac: dwmac-sun8i: Minor probe function cleanup net: stmmac: dwmac-sun8i: Use reset_control_reset net: stmmac: dwmac-sun8i: Remove unnecessary PHY power check net: stmmac: dwmac-sun8i: Return void from PHY unpower r8169: use macro pm_ptr net: mdio: Remove of_phy_attach() net: mscc: ocelot: select PACKING in the Kconfig net: re-solve some conflicts after net -> net-next merge net: dsa: tag_rtl4_a: Support also egress tags ...
This commit is contained in:
commit
51e6d17809
@ -337,3 +337,18 @@ Contact: netdev@vger.kernel.org
|
||||
Description:
|
||||
32-bit unsigned integer counting the number of times the link has
|
||||
been down
|
||||
|
||||
What: /sys/class/net/<iface>/threaded
|
||||
Date: Jan 2021
|
||||
KernelVersion: 5.12
|
||||
Contact: netdev@vger.kernel.org
|
||||
Description:
|
||||
Boolean value to control the threaded mode per device. User could
|
||||
set this value to enable/disable threaded mode for all napi
|
||||
belonging to this device, without the need to do device up/down.
|
||||
|
||||
Possible values:
|
||||
== ==================================
|
||||
0 threaded mode disabled for this dev
|
||||
1 threaded mode enabled for this dev
|
||||
== ==================================
|
||||
|
@ -3,5 +3,12 @@ Date: August 2018
|
||||
KernelVersion: 4.20
|
||||
Contact: netdev@vger.kernel.org
|
||||
Description:
|
||||
String indicating the type of tagging protocol used by the
|
||||
DSA slave network device.
|
||||
On read, this file returns a string indicating the type of
|
||||
tagging protocol used by the DSA network devices that are
|
||||
attached to this master interface.
|
||||
On write, this file changes the tagging protocol of the
|
||||
attached DSA switches, if this operation is supported by the
|
||||
driver. Changing the tagging protocol must be done with the DSA
|
||||
interfaces and the master interface all administratively down.
|
||||
See the "name" field of each registered struct dsa_device_ops
|
||||
for a list of valid values.
|
||||
|
@ -48,3 +48,13 @@ Description:
|
||||
|
||||
Write a number ranging from 1 to 254 to delete a previously
|
||||
created qmap mux based network device.
|
||||
|
||||
What: /sys/class/net/<qmimux iface>/qmap/mux_id
|
||||
Date: January 2021
|
||||
KernelVersion: 5.12
|
||||
Contact: Daniele Palmas <dnlplm@gmail.com>
|
||||
Description:
|
||||
Unsigned integer
|
||||
|
||||
Indicates the mux id associated to the qmimux network interface
|
||||
during its creation.
|
||||
|
@ -208,6 +208,12 @@ data structures and compile with kernel internal headers. Both of these
|
||||
kernel internals are subject to change and can break with newer kernels
|
||||
such that the program needs to be adapted accordingly.
|
||||
|
||||
Q: Are tracepoints part of the stable ABI?
|
||||
------------------------------------------
|
||||
A: NO. Tracepoints are tied to internal implementation details hence they are
|
||||
subject to change and can break with newer kernels. BPF programs need to change
|
||||
accordingly when this happens.
|
||||
|
||||
Q: How much stack space a BPF program uses?
|
||||
-------------------------------------------
|
||||
A: Currently all program types are limited to 512 bytes of stack
|
||||
|
@ -501,16 +501,19 @@ All LLVM releases can be found at: http://releases.llvm.org/
|
||||
|
||||
Q: Got it, so how do I build LLVM manually anyway?
|
||||
--------------------------------------------------
|
||||
A: You need cmake and gcc-c++ as build requisites for LLVM. Once you have
|
||||
that set up, proceed with building the latest LLVM and clang version
|
||||
A: We recommend that developers who want the fastest incremental builds
|
||||
use the Ninja build system, you can find it in your system's package
|
||||
manager, usually the package is ninja or ninja-build.
|
||||
|
||||
You need ninja, cmake and gcc-c++ as build requisites for LLVM. Once you
|
||||
have that set up, proceed with building the latest LLVM and clang version
|
||||
from the git repositories::
|
||||
|
||||
$ git clone https://github.com/llvm/llvm-project.git
|
||||
$ mkdir -p llvm-project/llvm/build/install
|
||||
$ mkdir -p llvm-project/llvm/build
|
||||
$ cd llvm-project/llvm/build
|
||||
$ cmake .. -G "Ninja" -DLLVM_TARGETS_TO_BUILD="BPF;X86" \
|
||||
-DLLVM_ENABLE_PROJECTS="clang" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_BUILD_RUNTIME=OFF
|
||||
$ ninja
|
||||
|
@ -74,17 +74,60 @@ allOf:
|
||||
Any configuration is ignored when the phy-mode is set to "rmii".
|
||||
|
||||
amlogic,rx-delay-ns:
|
||||
deprecated: true
|
||||
enum:
|
||||
- 0
|
||||
- 2
|
||||
default: 0
|
||||
description:
|
||||
The internal RGMII RX clock delay (provided by this IP block) in
|
||||
nanoseconds. When phy-mode is set to "rgmii" then the RX delay
|
||||
should be explicitly configured. When the phy-mode is set to
|
||||
either "rgmii-id" or "rgmii-rxid" the RX clock delay is already
|
||||
provided by the PHY. Any configuration is ignored when the
|
||||
phy-mode is set to "rmii".
|
||||
The internal RGMII RX clock delay in nanoseconds. Deprecated, use
|
||||
rx-internal-delay-ps instead.
|
||||
|
||||
rx-internal-delay-ps:
|
||||
default: 0
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- amlogic,meson8b-dwmac
|
||||
- amlogic,meson8m2-dwmac
|
||||
- amlogic,meson-gxbb-dwmac
|
||||
- amlogic,meson-axg-dwmac
|
||||
then:
|
||||
properties:
|
||||
rx-internal-delay-ps:
|
||||
enum:
|
||||
- 0
|
||||
- 2000
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- amlogic,meson-g12a-dwmac
|
||||
then:
|
||||
properties:
|
||||
rx-internal-delay-ps:
|
||||
enum:
|
||||
- 0
|
||||
- 200
|
||||
- 400
|
||||
- 600
|
||||
- 800
|
||||
- 1000
|
||||
- 1200
|
||||
- 1400
|
||||
- 1600
|
||||
- 1800
|
||||
- 2000
|
||||
- 2200
|
||||
- 2400
|
||||
- 2600
|
||||
- 2800
|
||||
- 3000
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
48
Documentation/devicetree/bindings/net/brcm,bcm4908-enet.yaml
Normal file
48
Documentation/devicetree/bindings/net/brcm,bcm4908-enet.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/brcm,bcm4908-enet.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM4908 Ethernet controller
|
||||
|
||||
description: Broadcom's Ethernet controller integrated into BCM4908 family SoCs
|
||||
|
||||
maintainers:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: brcm,bcm4908-enet
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description: RX interrupt
|
||||
|
||||
interrupt-names:
|
||||
const: rx
|
||||
|
||||
required:
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
ethernet@80002000 {
|
||||
compatible = "brcm,bcm4908-enet";
|
||||
reg = <0x80002000 0x1000>;
|
||||
|
||||
interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "rx";
|
||||
};
|
@ -1,108 +1,13 @@
|
||||
* Broadcom Starfighter 2 integrated swich
|
||||
|
||||
Required properties:
|
||||
See dsa/brcm,bcm7445-switch-v4.0.yaml for the documentation.
|
||||
|
||||
- compatible: should be one of
|
||||
"brcm,bcm7445-switch-v4.0"
|
||||
"brcm,bcm7278-switch-v4.0"
|
||||
"brcm,bcm7278-switch-v4.8"
|
||||
- reg: addresses and length of the register sets for the device, must be 6
|
||||
pairs of register addresses and lengths
|
||||
- interrupts: interrupts for the devices, must be two interrupts
|
||||
- #address-cells: must be 1, see dsa/dsa.txt
|
||||
- #size-cells: must be 0, see dsa/dsa.txt
|
||||
|
||||
Deprecated binding required properties:
|
||||
*Deprecated* binding required properties:
|
||||
|
||||
- dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt
|
||||
- dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt
|
||||
- #address-cells: must be 2, see dsa/dsa.txt
|
||||
|
||||
Subnodes:
|
||||
|
||||
The integrated switch subnode should be specified according to the binding
|
||||
described in dsa/dsa.txt.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reg-names: litteral names for the device base register addresses, when present
|
||||
must be: "core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb"
|
||||
|
||||
- interrupt-names: litternal names for the device interrupt lines, when present
|
||||
must be: "switch_0" and "switch_1"
|
||||
|
||||
- brcm,num-gphy: specify the maximum number of integrated gigabit PHYs in the
|
||||
switch
|
||||
|
||||
- brcm,num-rgmii-ports: specify the maximum number of RGMII interfaces supported
|
||||
by the switch
|
||||
|
||||
- brcm,fcb-pause-override: boolean property, if present indicates that the switch
|
||||
supports Failover Control Block pause override capability
|
||||
|
||||
- brcm,acb-packets-inflight: boolean property, if present indicates that the switch
|
||||
Admission Control Block supports reporting the number of packets in-flight in a
|
||||
switch queue
|
||||
|
||||
- resets: a single phandle and reset identifier pair. See
|
||||
Documentation/devicetree/bindings/reset/reset.txt for details.
|
||||
|
||||
- reset-names: If the "reset" property is specified, this property should have
|
||||
the value "switch" to denote the switch reset line.
|
||||
|
||||
- clocks: when provided, the first phandle is to the switch's main clock and
|
||||
is valid for both BCM7445 and BCM7278. The second phandle is only applicable
|
||||
to BCM7445 and is to support dividing the switch core clock.
|
||||
|
||||
- clock-names: when provided, the first phandle must be "sw_switch", and the
|
||||
second must be named "sw_switch_mdiv".
|
||||
|
||||
Port subnodes:
|
||||
|
||||
Optional properties:
|
||||
|
||||
- brcm,use-bcm-hdr: boolean property, if present, indicates that the switch
|
||||
port has Broadcom tags enabled (per-packet metadata)
|
||||
|
||||
Example:
|
||||
|
||||
switch_top@f0b00000 {
|
||||
compatible = "simple-bus";
|
||||
#size-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
ranges = <0 0xf0b00000 0x40804>;
|
||||
|
||||
ethernet_switch@0 {
|
||||
compatible = "brcm,bcm7445-switch-v4.0";
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
reg = <0x0 0x40000
|
||||
0x40000 0x110
|
||||
0x40340 0x30
|
||||
0x40380 0x30
|
||||
0x40400 0x34
|
||||
0x40600 0x208>;
|
||||
reg-names = "core", "reg", intrl2_0", "intrl2_1",
|
||||
"fcb, "acb";
|
||||
interrupts = <0 0x18 0
|
||||
0 0x19 0>;
|
||||
brcm,num-gphy = <1>;
|
||||
brcm,num-rgmii-ports = <2>;
|
||||
brcm,fcb-pause-override;
|
||||
brcm,acb-packets-inflight;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
label = "gphy";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Example using the old DSA DeviceTree binding:
|
||||
|
||||
switch_top@f0b00000 {
|
||||
@ -132,7 +37,7 @@ switch_top@f0b00000 {
|
||||
switch@0 {
|
||||
reg = <0 0>;
|
||||
#size-cells = <0>;
|
||||
#address-cells <1>;
|
||||
#address-cells = <1>;
|
||||
|
||||
port@0 {
|
||||
label = "gphy";
|
||||
|
@ -38,7 +38,7 @@ Following example uses irq pin number 3 of gpio0 for out of band wake-on-bt:
|
||||
compatible = "usb1286,204e";
|
||||
reg = <1>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupt-name = "wakeup";
|
||||
interrupt-names = "wakeup";
|
||||
interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
||||
};
|
||||
|
@ -110,6 +110,16 @@ properties:
|
||||
description:
|
||||
Enable CAN remote wakeup.
|
||||
|
||||
fsl,scu-index:
|
||||
description: |
|
||||
The scu index of CAN instance.
|
||||
For SoCs with SCU support, need setup stop mode via SCU firmware, so this
|
||||
property can help indicate a resource. It supports up to 3 CAN instances
|
||||
now.
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
minimum: 0
|
||||
maximum: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -137,4 +147,5 @@ examples:
|
||||
clocks = <&clks 1>, <&clks 2>;
|
||||
clock-names = "ipg", "per";
|
||||
fsl,stop-mode = <&gpr 0x34 28>;
|
||||
fsl,scu-index = /bits/ 8 <1>;
|
||||
};
|
||||
|
73
Documentation/devicetree/bindings/net/dsa/arrow,xrs700x.yaml
Normal file
73
Documentation/devicetree/bindings/net/dsa/arrow,xrs700x.yaml
Normal file
@ -0,0 +1,73 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/dsa/arrow,xrs700x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Arrow SpeedChips XRS7000 Series Switch Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: dsa.yaml#
|
||||
|
||||
maintainers:
|
||||
- George McCollister <george.mccollister@gmail.com>
|
||||
|
||||
description:
|
||||
The Arrow SpeedChips XRS7000 Series of single chip gigabit Ethernet switches
|
||||
are designed for critical networking applications. They have up to three
|
||||
RGMII ports and one RMII port and are managed via i2c or mdio.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- arrow,xrs7003e
|
||||
- arrow,xrs7003f
|
||||
- arrow,xrs7004e
|
||||
- arrow,xrs7004f
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
switch@8 {
|
||||
compatible = "arrow,xrs7004e";
|
||||
reg = <0x8>;
|
||||
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
ethernet-port@1 {
|
||||
reg = <1>;
|
||||
label = "lan0";
|
||||
phy-handle = <&swphy0>;
|
||||
phy-mode = "rgmii-id";
|
||||
};
|
||||
ethernet-port@2 {
|
||||
reg = <2>;
|
||||
label = "lan1";
|
||||
phy-handle = <&swphy1>;
|
||||
phy-mode = "rgmii-id";
|
||||
};
|
||||
ethernet-port@3 {
|
||||
reg = <3>;
|
||||
label = "cpu";
|
||||
ethernet = <&fec1>;
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
173
Documentation/devicetree/bindings/net/dsa/brcm,sf2.yaml
Normal file
173
Documentation/devicetree/bindings/net/dsa/brcm,sf2.yaml
Normal file
@ -0,0 +1,173 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/dsa/brcm,sf2.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom Starfighter 2 integrated swich
|
||||
|
||||
maintainers:
|
||||
- Florian Fainelli <f.fainelli@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- brcm,bcm4908-switch
|
||||
- brcm,bcm7278-switch-v4.0
|
||||
- brcm,bcm7278-switch-v4.8
|
||||
- brcm,bcm7445-switch-v4.0
|
||||
|
||||
reg:
|
||||
minItems: 6
|
||||
maxItems: 6
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: reg
|
||||
- const: intrl2_0
|
||||
- const: intrl2_1
|
||||
- const: fcb
|
||||
- const: acb
|
||||
|
||||
interrupts:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: switch_0
|
||||
- const: switch_1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
const: switch
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- description: switch's main clock
|
||||
- description: dividing of the switch core clock
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- const: sw_switch
|
||||
- const: sw_switch_mdiv
|
||||
|
||||
brcm,num-gphy:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: maximum number of integrated gigabit PHYs in the switch
|
||||
|
||||
brcm,num-rgmii-ports:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: maximum number of RGMII interfaces supported by the switch
|
||||
|
||||
brcm,fcb-pause-override:
|
||||
description: if present indicates that the switch supports Failover Control
|
||||
Block pause override capability
|
||||
type: boolean
|
||||
|
||||
brcm,acb-packets-inflight:
|
||||
description: if present indicates that the switch Admission Control Block
|
||||
supports reporting the number of packets in-flight in a switch queue
|
||||
type: boolean
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
ports:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
brcm,use-bcm-hdr:
|
||||
description: if present, indicates that the switch port has Broadcom
|
||||
tags enabled (per-packet metadata)
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- reg
|
||||
- interrupts
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
allOf:
|
||||
- $ref: "dsa.yaml#"
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- brcm,bcm7278-switch-v4.0
|
||||
- brcm,bcm7278-switch-v4.8
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
required:
|
||||
- clocks
|
||||
- clock-names
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: brcm,bcm7445-switch-v4.0
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
required:
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
switch@f0b00000 {
|
||||
compatible = "brcm,bcm7445-switch-v4.0";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0xf0b00000 0x40000>,
|
||||
<0xf0b40000 0x110>,
|
||||
<0xf0b40340 0x30>,
|
||||
<0xf0b40380 0x30>,
|
||||
<0xf0b40400 0x34>,
|
||||
<0xf0b40600 0x208>;
|
||||
reg-names = "core", "reg", "intrl2_0", "intrl2_1",
|
||||
"fcb", "acb";
|
||||
interrupts = <0 0x18 0>,
|
||||
<0 0x19 0>;
|
||||
clocks = <&sw_switch>, <&sw_switch_mdiv>;
|
||||
clock-names = "sw_switch", "sw_switch_mdiv";
|
||||
brcm,num-gphy = <1>;
|
||||
brcm,num-rgmii-ports = <2>;
|
||||
brcm,fcb-pause-override;
|
||||
brcm,acb-packets-inflight;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
label = "gphy";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
@ -76,6 +76,12 @@ phy-mode must be set, see also example 2 below!
|
||||
* mt7621: phy-mode = "rgmii-txid";
|
||||
* mt7623: phy-mode = "rgmii";
|
||||
|
||||
Optional properties:
|
||||
|
||||
- gpio-controller: Boolean; if defined, MT7530's LED controller will run on
|
||||
GPIO mode.
|
||||
- #gpio-cells: Must be 2 if gpio-controller is defined.
|
||||
|
||||
See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional
|
||||
required, optional properties and how the integrated switch subnodes must
|
||||
be specified.
|
||||
|
@ -89,6 +89,7 @@ properties:
|
||||
- trgmii
|
||||
- 1000base-x
|
||||
- 2500base-x
|
||||
- 5gbase-r
|
||||
- rxaui
|
||||
- xaui
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
* Marvell Armada 375 Ethernet Controller (PPv2.1)
|
||||
Marvell Armada 7K/8K Ethernet Controller (PPv2.2)
|
||||
Marvell CN913X Ethernet Controller (PPv2.3)
|
||||
|
||||
Required properties:
|
||||
|
||||
@ -12,10 +13,11 @@ Required properties:
|
||||
- common controller registers
|
||||
- LMS registers
|
||||
- one register area per Ethernet port
|
||||
For "marvell,armada-7k-pp2", must contain the following register
|
||||
For "marvell,armada-7k-pp2" used by 7K/8K and CN913X, must contain the following register
|
||||
sets:
|
||||
- packet processor registers
|
||||
- networking interfaces registers
|
||||
- CM3 address space used for TX Flow Control
|
||||
|
||||
- clocks: pointers to the reference clocks for this device, consequently:
|
||||
- main controller clock (for both armada-375-pp2 and armada-7k-pp2)
|
||||
@ -81,7 +83,7 @@ Example for marvell,armada-7k-pp2:
|
||||
|
||||
cpm_ethernet: ethernet@0 {
|
||||
compatible = "marvell,armada-7k-pp22";
|
||||
reg = <0x0 0x100000>, <0x129000 0xb000>;
|
||||
reg = <0x0 0x100000>, <0x129000 0xb000>, <0x220000 0x800>;
|
||||
clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>,
|
||||
<&cpm_syscon0 1 5>, <&cpm_syscon0 1 6>, <&cpm_syscon0 1 18>;
|
||||
clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk";
|
||||
|
@ -28,6 +28,10 @@ properties:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1, 2]
|
||||
|
||||
qca,disable-smarteee:
|
||||
description: Disable Atheros SmartEEE feature.
|
||||
type: boolean
|
||||
|
||||
qca,keep-pll-enabled:
|
||||
description: |
|
||||
If set, keep the PLL enabled even if there is no link. Useful if you
|
||||
@ -36,6 +40,18 @@ properties:
|
||||
Only supported on the AR8031.
|
||||
type: boolean
|
||||
|
||||
qca,smarteee-tw-us-100m:
|
||||
description: EEE Tw parameter for 100M links.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 255
|
||||
|
||||
qca,smarteee-tw-us-1g:
|
||||
description: EEE Tw parameter for gigabit links.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 255
|
||||
|
||||
vddio-supply:
|
||||
description: |
|
||||
RGMII I/O voltage regulator (see regulator/regulator.yaml).
|
||||
|
@ -113,13 +113,6 @@ properties:
|
||||
performing early IPA initialization, including loading and
|
||||
validating firwmare used by the GSI.
|
||||
|
||||
modem-remoteproc:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
This defines the phandle to the remoteproc node representing
|
||||
the modem subsystem. This is requied so the IPA driver can
|
||||
receive and act on notifications of modem up/down events.
|
||||
|
||||
memory-region:
|
||||
maxItems: 1
|
||||
description:
|
||||
@ -135,7 +128,6 @@ required:
|
||||
- interrupts
|
||||
- interconnects
|
||||
- qcom,smem-states
|
||||
- modem-remoteproc
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
@ -147,7 +139,7 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
#include <dt-bindings/interconnect/qcom,sdm845.h>
|
||||
|
||||
@ -168,7 +160,6 @@ examples:
|
||||
compatible = "qcom,sdm845-ipa";
|
||||
|
||||
modem-init;
|
||||
modem-remoteproc = <&mss_pil>;
|
||||
|
||||
iommus = <&apps_smmu 0x720 0x3>;
|
||||
reg = <0x1e40000 0x7000>,
|
||||
@ -178,8 +169,8 @@ examples:
|
||||
"ipa-shared",
|
||||
"gsi";
|
||||
|
||||
interrupts-extended = <&intc 0 311 IRQ_TYPE_EDGE_RISING>,
|
||||
<&intc 0 432 IRQ_TYPE_LEVEL_HIGH>,
|
||||
interrupts-extended = <&intc GIC_SPI 311 IRQ_TYPE_EDGE_RISING>,
|
||||
<&intc GIC_SPI 432 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<&ipa_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
|
||||
<&ipa_smp2p_in 1 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "ipa",
|
||||
|
@ -40,6 +40,7 @@ properties:
|
||||
- renesas,etheravb-r8a77980 # R-Car V3H
|
||||
- renesas,etheravb-r8a77990 # R-Car E3
|
||||
- renesas,etheravb-r8a77995 # R-Car D3
|
||||
- renesas,etheravb-r8a779a0 # R-Car V3U
|
||||
- const: renesas,etheravb-rcar-gen3 # R-Car Gen3 and RZ/G2
|
||||
|
||||
reg: true
|
||||
@ -170,6 +171,7 @@ allOf:
|
||||
- renesas,etheravb-r8a77965
|
||||
- renesas,etheravb-r8a77970
|
||||
- renesas,etheravb-r8a77980
|
||||
- renesas,etheravb-r8a779a0
|
||||
then:
|
||||
required:
|
||||
- tx-internal-delay-ps
|
||||
|
@ -4,7 +4,7 @@
|
||||
$id: http://devicetree.org/schemas/net/ti,k3-am654-cpsw-nuss.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: The TI AM654x/J721E SoC Gigabit Ethernet MAC (Media Access Controller) Device Tree Bindings
|
||||
title: The TI AM654x/J721E/AM642x SoC Gigabit Ethernet MAC (Media Access Controller) Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Grygorii Strashko <grygorii.strashko@ti.com>
|
||||
@ -13,19 +13,16 @@ maintainers:
|
||||
description:
|
||||
The TI AM654x/J721E SoC Gigabit Ethernet MAC (CPSW2G NUSS) has two ports
|
||||
(one external) and provides Ethernet packet communication for the device.
|
||||
CPSW2G NUSS features - the Reduced Gigabit Media Independent Interface (RGMII),
|
||||
Reduced Media Independent Interface (RMII), the Management Data
|
||||
Input/Output (MDIO) interface for physical layer device (PHY) management,
|
||||
new version of Common Platform Time Sync (CPTS), updated Address Lookup
|
||||
Engine (ALE).
|
||||
One external Ethernet port (port 1) with selectable RGMII/RMII interfaces and
|
||||
an internal Communications Port Programming Interface (CPPI5) (Host port 0).
|
||||
Host Port 0 CPPI Packet Streaming Interface interface supports 8 TX channels
|
||||
and one RX channels and operating by TI AM654x/J721E NAVSS Unified DMA
|
||||
Peripheral Root Complex (UDMA-P) controller.
|
||||
The CPSW2G NUSS is integrated into device MCU domain named MCU_CPSW0.
|
||||
The TI AM642x SoC Gigabit Ethernet MAC (CPSW3G NUSS) has three ports
|
||||
(two external) and provides Ethernet packet communication and switching.
|
||||
|
||||
Additional features
|
||||
The internal Communications Port Programming Interface (CPPI5) (Host port 0).
|
||||
Host Port 0 CPPI Packet Streaming Interface interface supports 8 TX channels
|
||||
and one RX channels and operating by NAVSS Unified DMA Peripheral Root
|
||||
Complex (UDMA-P) controller.
|
||||
|
||||
CPSWxG features
|
||||
updated Address Lookup Engine (ALE).
|
||||
priority level Quality Of Service (QOS) support (802.1p)
|
||||
Support for Audio/Video Bridging (P802.1Qav/D6.0)
|
||||
Support for IEEE 1588 Clock Synchronization (2008 Annex D, Annex E and Annex F)
|
||||
@ -38,10 +35,18 @@ description:
|
||||
VLAN support, 802.1Q compliant, Auto add port VLAN for untagged frames on
|
||||
ingress, Auto VLAN removal on egress and auto pad to minimum frame size.
|
||||
RX/TX csum offload
|
||||
Management Data Input/Output (MDIO) interface for PHYs management
|
||||
RMII/RGMII Interfaces support
|
||||
new version of Common Platform Time Sync (CPTS)
|
||||
|
||||
The CPSWxG NUSS is integrated into
|
||||
device MCU domain named MCU_CPSW0 on AM654x/J721E SoC.
|
||||
device MAIN domain named CPSW0 on AM642x SoC.
|
||||
|
||||
Specifications can be found at
|
||||
http://www.ti.com/lit/ug/spruid7e/spruid7e.pdf
|
||||
http://www.ti.com/lit/ug/spruil1a/spruil1a.pdf
|
||||
https://www.ti.com/lit/pdf/spruid7
|
||||
https://www.ti.com/lit/zip/spruil1
|
||||
https://www.ti.com/lit/pdf/spruim2
|
||||
|
||||
properties:
|
||||
"#address-cells": true
|
||||
@ -51,11 +56,12 @@ properties:
|
||||
oneOf:
|
||||
- const: ti,am654-cpsw-nuss
|
||||
- const: ti,j721e-cpsw-nuss
|
||||
- const: ti,am642-cpsw-nuss
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description:
|
||||
The physical base address and size of full the CPSW2G NUSS IO range
|
||||
The physical base address and size of full the CPSWxG NUSS IO range
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
@ -66,12 +72,16 @@ properties:
|
||||
dma-coherent: true
|
||||
|
||||
clocks:
|
||||
description: CPSW2G NUSS functional clock
|
||||
description: CPSWxG NUSS functional clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: fck
|
||||
|
||||
assigned-clock-parents: true
|
||||
|
||||
assigned-clocks: true
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
@ -99,16 +109,16 @@ properties:
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
port@1:
|
||||
port@[1-2]:
|
||||
type: object
|
||||
description: CPSW2G NUSS external ports
|
||||
description: CPSWxG NUSS external ports
|
||||
|
||||
$ref: ethernet-controller.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- const: 1
|
||||
minimum: 1
|
||||
maximum: 2
|
||||
description: CPSW port number
|
||||
|
||||
phys:
|
||||
|
@ -73,6 +73,13 @@ properties:
|
||||
items:
|
||||
- const: cpts
|
||||
|
||||
assigned-clock-parents: true
|
||||
|
||||
assigned-clocks: true
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
ti,cpts-ext-ts-inputs:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 8
|
||||
|
@ -0,0 +1,85 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/net/toshiba,visconti-dwmac.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Toshiba Visconti DWMAC Ethernet controller
|
||||
|
||||
maintainers:
|
||||
- Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- toshiba,visconti-dwmac
|
||||
required:
|
||||
- compatible
|
||||
|
||||
allOf:
|
||||
- $ref: "snps,dwmac.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- toshiba,visconti-dwmac
|
||||
- const: snps,dwmac-4.20a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: main clock
|
||||
- description: PHY reference clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: stmmaceth
|
||||
- const: phy_ref_clk
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
piether: ethernet@28000000 {
|
||||
compatible = "toshiba,visconti-dwmac", "snps,dwmac-4.20a";
|
||||
reg = <0 0x28000000 0 0x10000>;
|
||||
interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "macirq";
|
||||
clocks = <&clk300mhz>, <&clk125mhz>;
|
||||
clock-names = "stmmaceth", "phy_ref_clk";
|
||||
snps,txpbl = <4>;
|
||||
snps,rxpbl = <4>;
|
||||
snps,tso;
|
||||
phy-mode = "rgmii-id";
|
||||
phy-handle = <&phy0>;
|
||||
|
||||
mdio0 {
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
compatible = "snps,dwmac-mdio";
|
||||
|
||||
phy0: ethernet-phy@1 {
|
||||
device_type = "ethernet-phy";
|
||||
reg = <0x1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -38,6 +38,10 @@ Optional properties:
|
||||
1 to enable partial TX checksum offload,
|
||||
2 to enable full TX checksum offload
|
||||
- xlnx,rxcsum : Same values as xlnx,txcsum but for RX checksum offload
|
||||
- xlnx,switch-x-sgmii : Boolean to indicate the Ethernet core is configured to
|
||||
support both 1000BaseX and SGMII modes. If set, the phy-mode
|
||||
should be set to match the mode selected on core reset (i.e.
|
||||
by the basex_or_sgmii core input line).
|
||||
- clocks : AXI bus clock for the device. Refer to common clock bindings.
|
||||
Used to calculate MDIO clock divisor. If not specified, it is
|
||||
auto-detected from the CPU clock (but only on platforms where
|
||||
|
@ -1,5 +1,7 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
.. _auxiliary_bus:
|
||||
|
||||
=============
|
||||
Auxiliary Bus
|
||||
=============
|
||||
|
@ -951,6 +951,19 @@ xmit_hash_policy
|
||||
packets will be distributed according to the encapsulated
|
||||
flows.
|
||||
|
||||
vlan+srcmac
|
||||
|
||||
This policy uses a very rudimentary vlan ID and source mac
|
||||
hash to load-balance traffic per-vlan, with failover
|
||||
should one leg fail. The intended use case is for a bond
|
||||
shared by multiple virtual machines, all configured to
|
||||
use their own vlan, to give lacp-like functionality
|
||||
without requiring lacp-capable switching hardware.
|
||||
|
||||
The formula for the hash is simply
|
||||
|
||||
hash = (vlan ID) XOR (source MAC vendor) XOR (source MAC dev)
|
||||
|
||||
The default value is layer2. This option was added in bonding
|
||||
version 2.6.3. In earlier versions of bonding, this parameter
|
||||
does not exist, and the layer2 policy is the only policy. The
|
||||
|
@ -49,6 +49,7 @@ Contents:
|
||||
stmicro/stmmac
|
||||
ti/cpsw
|
||||
ti/cpsw_switchdev
|
||||
ti/am65_nuss_cpsw_switchdev
|
||||
ti/tlan
|
||||
toshiba/spider_net
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -217,3 +217,73 @@ For example::
|
||||
NPA_AF_ERR:
|
||||
NPA Error Interrupt Reg : 4096
|
||||
AQ Doorbell Error
|
||||
|
||||
|
||||
NIX Reporters
|
||||
-------------
|
||||
The NIX reporters are responsible for reporting and recovering the following group of errors:
|
||||
|
||||
1. GENERAL events
|
||||
|
||||
- Receive mirror/multicast packet drop due to insufficient buffer.
|
||||
- SMQ Flush operation.
|
||||
|
||||
2. ERROR events
|
||||
|
||||
- Memory Fault due to WQE read/write from multicast/mirror buffer.
|
||||
- Receive multicast/mirror replication list error.
|
||||
- Receive packet on an unmapped PF.
|
||||
- Fault due to NIX_AQ_INST_S read or NIX_AQ_RES_S write.
|
||||
- AQ Doorbell Error.
|
||||
|
||||
3. RAS events
|
||||
|
||||
- RAS Error Reporting for NIX Receive Multicast/Mirror Entry Structure.
|
||||
- RAS Error Reporting for WQE/Packet Data read from Multicast/Mirror Buffer..
|
||||
- RAS Error Reporting for NIX_AQ_INST_S/NIX_AQ_RES_S.
|
||||
|
||||
4. RVU events
|
||||
|
||||
- Error due to unmapped slot.
|
||||
|
||||
Sample Output::
|
||||
|
||||
~# ./devlink health
|
||||
pci/0002:01:00.0:
|
||||
reporter hw_npa_intr
|
||||
state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
|
||||
reporter hw_npa_gen
|
||||
state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
|
||||
reporter hw_npa_err
|
||||
state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
|
||||
reporter hw_npa_ras
|
||||
state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
|
||||
reporter hw_nix_intr
|
||||
state healthy error 1121 recover 1121 last_dump_date 2021-01-19 last_dump_time 05:42:26 grace_period 0 auto_recover true auto_dump true
|
||||
reporter hw_nix_gen
|
||||
state healthy error 949 recover 949 last_dump_date 2021-01-19 last_dump_time 05:42:43 grace_period 0 auto_recover true auto_dump true
|
||||
reporter hw_nix_err
|
||||
state healthy error 1147 recover 1147 last_dump_date 2021-01-19 last_dump_time 05:42:59 grace_period 0 auto_recover true auto_dump true
|
||||
reporter hw_nix_ras
|
||||
state healthy error 409 recover 409 last_dump_date 2021-01-19 last_dump_time 05:43:16 grace_period 0 auto_recover true auto_dump true
|
||||
|
||||
Each reporter dumps the
|
||||
|
||||
- Error Type
|
||||
- Error Register value
|
||||
- Reason in words
|
||||
|
||||
For example::
|
||||
|
||||
~# devlink health dump show pci/0002:01:00.0 reporter hw_nix_intr
|
||||
NIX_AF_RVU:
|
||||
NIX RVU Interrupt Reg : 1
|
||||
Unmap Slot Error
|
||||
~# devlink health dump show pci/0002:01:00.0 reporter hw_nix_gen
|
||||
NIX_AF_GENERAL:
|
||||
NIX General Interrupt Reg : 1
|
||||
Rx multicast pkt drop
|
||||
~# devlink health dump show pci/0002:01:00.0 reporter hw_nix_err
|
||||
NIX_AF_ERR:
|
||||
NIX Error Interrupt Reg : 64
|
||||
Rx on unmapped PF_FUNC
|
||||
|
@ -12,11 +12,13 @@ Contents
|
||||
- `Enabling the driver and kconfig options`_
|
||||
- `Devlink info`_
|
||||
- `Devlink parameters`_
|
||||
- `mlx5 subfunction`_
|
||||
- `mlx5 function attributes`_
|
||||
- `Devlink health reporters`_
|
||||
- `mlx5 tracepoints`_
|
||||
|
||||
Enabling the driver and kconfig options
|
||||
================================================
|
||||
=======================================
|
||||
|
||||
| mlx5 core is modular and most of the major mlx5 core driver features can be selected (compiled in/out)
|
||||
| at build time via kernel Kconfig flags.
|
||||
@ -97,6 +99,11 @@ Enabling the driver and kconfig options
|
||||
|
||||
| Provides low-level InfiniBand/RDMA and `RoCE <https://community.mellanox.com/s/article/recommended-network-configuration-examples-for-roce-deployment>`_ support.
|
||||
|
||||
**CONFIG_MLX5_SF=(y/n)**
|
||||
|
||||
| Build support for subfunction.
|
||||
| Subfunctons are more light weight than PCI SRIOV VFs. Choosing this option
|
||||
| will enable support for creating subfunction devices.
|
||||
|
||||
**External options** ( Choose if the corresponding mlx5 feature is required )
|
||||
|
||||
@ -176,6 +183,214 @@ User command examples:
|
||||
values:
|
||||
cmode driverinit value true
|
||||
|
||||
mlx5 subfunction
|
||||
================
|
||||
mlx5 supports subfunction management using devlink port (see :ref:`Documentation/networking/devlink/devlink-port.rst <devlink_port>`) interface.
|
||||
|
||||
A Subfunction has its own function capabilities and its own resources. This
|
||||
means a subfunction has its own dedicated queues (txq, rxq, cq, eq). These
|
||||
queues are neither shared nor stolen from the parent PCI function.
|
||||
|
||||
When a subfunction is RDMA capable, it has its own QP1, GID table and rdma
|
||||
resources neither shared nor stolen from the parent PCI function.
|
||||
|
||||
A subfunction has a dedicated window in PCI BAR space that is not shared
|
||||
with ther other subfunctions or the parent PCI function. This ensures that all
|
||||
devices (netdev, rdma, vdpa etc.) of the subfunction accesses only assigned
|
||||
PCI BAR space.
|
||||
|
||||
A Subfunction supports eswitch representation through which it supports tc
|
||||
offloads. The user configures eswitch to send/receive packets from/to
|
||||
the subfunction port.
|
||||
|
||||
Subfunctions share PCI level resources such as PCI MSI-X IRQs with
|
||||
other subfunctions and/or with its parent PCI function.
|
||||
|
||||
Example mlx5 software, system and device view::
|
||||
|
||||
_______
|
||||
| admin |
|
||||
| user |----------
|
||||
|_______| |
|
||||
| |
|
||||
____|____ __|______ _________________
|
||||
| | | | | |
|
||||
| devlink | | tc tool | | user |
|
||||
| tool | |_________| | applications |
|
||||
|_________| | |_________________|
|
||||
| | | |
|
||||
| | | | Userspace
|
||||
+---------|-------------|-------------------|----------|--------------------+
|
||||
| | +----------+ +----------+ Kernel
|
||||
| | | netdev | | rdma dev |
|
||||
| | +----------+ +----------+
|
||||
(devlink port add/del | ^ ^
|
||||
port function set) | | |
|
||||
| | +---------------|
|
||||
_____|___ | | _______|_______
|
||||
| | | | | mlx5 class |
|
||||
| devlink | +------------+ | | drivers |
|
||||
| kernel | | rep netdev | | |(mlx5_core,ib) |
|
||||
|_________| +------------+ | |_______________|
|
||||
| | | ^
|
||||
(devlink ops) | | (probe/remove)
|
||||
_________|________ | | ____|________
|
||||
| subfunction | | +---------------+ | subfunction |
|
||||
| management driver|----- | subfunction |---| driver |
|
||||
| (mlx5_core) | | auxiliary dev | | (mlx5_core) |
|
||||
|__________________| +---------------+ |_____________|
|
||||
| ^
|
||||
(sf add/del, vhca events) |
|
||||
| (device add/del)
|
||||
_____|____ ____|________
|
||||
| | | subfunction |
|
||||
| PCI NIC |---- activate/deactive events---->| host driver |
|
||||
|__________| | (mlx5_core) |
|
||||
|_____________|
|
||||
|
||||
Subfunction is created using devlink port interface.
|
||||
|
||||
- Change device to switchdev mode::
|
||||
|
||||
$ devlink dev eswitch set pci/0000:06:00.0 mode switchdev
|
||||
|
||||
- Add a devlink port of subfunction flaovur::
|
||||
|
||||
$ devlink port add pci/0000:06:00.0 flavour pcisf pfnum 0 sfnum 88
|
||||
pci/0000:06:00.0/32768: type eth netdev eth6 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
|
||||
function:
|
||||
hw_addr 00:00:00:00:00:00 state inactive opstate detached
|
||||
|
||||
- Show a devlink port of the subfunction::
|
||||
|
||||
$ devlink port show pci/0000:06:00.0/32768
|
||||
pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88
|
||||
function:
|
||||
hw_addr 00:00:00:00:00:00 state inactive opstate detached
|
||||
|
||||
- Delete a devlink port of subfunction after use::
|
||||
|
||||
$ devlink port del pci/0000:06:00.0/32768
|
||||
|
||||
mlx5 function attributes
|
||||
========================
|
||||
The mlx5 driver provides a mechanism to setup PCI VF/SF function attributes in
|
||||
a unified way for SmartNIC and non-SmartNIC.
|
||||
|
||||
This is supported only when the eswitch mode is set to switchdev. Port function
|
||||
configuration of the PCI VF/SF is supported through devlink eswitch port.
|
||||
|
||||
Port function attributes should be set before PCI VF/SF is enumerated by the
|
||||
driver.
|
||||
|
||||
MAC address setup
|
||||
-----------------
|
||||
mlx5 driver provides mechanism to setup the MAC address of the PCI VF/SF.
|
||||
|
||||
The configured MAC address of the PCI VF/SF will be used by netdevice and rdma
|
||||
device created for the PCI VF/SF.
|
||||
|
||||
- Get the MAC address of the VF identified by its unique devlink port index::
|
||||
|
||||
$ devlink port show pci/0000:06:00.0/2
|
||||
pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
|
||||
function:
|
||||
hw_addr 00:00:00:00:00:00
|
||||
|
||||
- Set the MAC address of the VF identified by its unique devlink port index::
|
||||
|
||||
$ devlink port function set pci/0000:06:00.0/2 hw_addr 00:11:22:33:44:55
|
||||
|
||||
$ devlink port show pci/0000:06:00.0/2
|
||||
pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
|
||||
function:
|
||||
hw_addr 00:11:22:33:44:55
|
||||
|
||||
- Get the MAC address of the SF identified by its unique devlink port index::
|
||||
|
||||
$ devlink port show pci/0000:06:00.0/32768
|
||||
pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88
|
||||
function:
|
||||
hw_addr 00:00:00:00:00:00
|
||||
|
||||
- Set the MAC address of the VF identified by its unique devlink port index::
|
||||
|
||||
$ devlink port function set pci/0000:06:00.0/32768 hw_addr 00:00:00:00:88:88
|
||||
|
||||
$ devlink port show pci/0000:06:00.0/32768
|
||||
pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcivf pfnum 0 sfnum 88
|
||||
function:
|
||||
hw_addr 00:00:00:00:88:88
|
||||
|
||||
SF state setup
|
||||
--------------
|
||||
To use the SF, the user must active the SF using the SF function state
|
||||
attribute.
|
||||
|
||||
- Get the state of the SF identified by its unique devlink port index::
|
||||
|
||||
$ devlink port show ens2f0npf0sf88
|
||||
pci/0000:06:00.0/32768: type eth netdev ens2f0npf0sf88 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
|
||||
function:
|
||||
hw_addr 00:00:00:00:88:88 state inactive opstate detached
|
||||
|
||||
- Activate the function and verify its state is active::
|
||||
|
||||
$ devlink port function set ens2f0npf0sf88 state active
|
||||
|
||||
$ devlink port show ens2f0npf0sf88
|
||||
pci/0000:06:00.0/32768: type eth netdev ens2f0npf0sf88 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
|
||||
function:
|
||||
hw_addr 00:00:00:00:88:88 state active opstate detached
|
||||
|
||||
Upon function activation, the PF driver instance gets the event from the device
|
||||
that a particular SF was activated. It's the cue to put the device on bus, probe
|
||||
it and instantiate the devlink instance and class specific auxiliary devices
|
||||
for it.
|
||||
|
||||
- Show the auxiliary device and port of the subfunction::
|
||||
|
||||
$ devlink dev show
|
||||
devlink dev show auxiliary/mlx5_core.sf.4
|
||||
|
||||
$ devlink port show auxiliary/mlx5_core.sf.4/1
|
||||
auxiliary/mlx5_core.sf.4/1: type eth netdev p0sf88 flavour virtual port 0 splittable false
|
||||
|
||||
$ rdma link show mlx5_0/1
|
||||
link mlx5_0/1 state ACTIVE physical_state LINK_UP netdev p0sf88
|
||||
|
||||
$ rdma dev show
|
||||
8: rocep6s0f1: node_type ca fw 16.29.0550 node_guid 248a:0703:00b3:d113 sys_image_guid 248a:0703:00b3:d112
|
||||
13: mlx5_0: node_type ca fw 16.29.0550 node_guid 0000:00ff:fe00:8888 sys_image_guid 248a:0703:00b3:d112
|
||||
|
||||
- Subfunction auxiliary device and class device hierarchy::
|
||||
|
||||
mlx5_core.sf.4
|
||||
(subfunction auxiliary device)
|
||||
/\
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
mlx5_core.eth.4 mlx5_core.rdma.4
|
||||
(sf eth aux dev) (sf rdma aux dev)
|
||||
| |
|
||||
| |
|
||||
p0sf88 mlx5_0
|
||||
(sf netdev) (sf rdma device)
|
||||
|
||||
Additionally, the SF port also gets the event when the driver attaches to the
|
||||
auxiliary device of the subfunction. This results in changing the operational
|
||||
state of the function. This provides visiblity to the user to decide when is it
|
||||
safe to delete the SF port for graceful termination of the subfunction.
|
||||
|
||||
- Show the SF port operational state::
|
||||
|
||||
$ devlink port show ens2f0npf0sf88
|
||||
pci/0000:06:00.0/32768: type eth netdev ens2f0npf0sf88 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
|
||||
function:
|
||||
hw_addr 00:00:00:00:88:88 state active opstate attached
|
||||
|
||||
Devlink health reporters
|
||||
========================
|
||||
|
||||
|
@ -0,0 +1,143 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===================================================================
|
||||
Texas Instruments K3 AM65 CPSW NUSS switchdev based ethernet driver
|
||||
===================================================================
|
||||
|
||||
:Version: 1.0
|
||||
|
||||
Port renaming
|
||||
=============
|
||||
|
||||
In order to rename via udev::
|
||||
|
||||
ip -d link show dev sw0p1 | grep switchid
|
||||
|
||||
SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}==<switchid>, \
|
||||
ATTR{phys_port_name}!="", NAME="sw0$attr{phys_port_name}"
|
||||
|
||||
|
||||
Multi mac mode
|
||||
==============
|
||||
|
||||
- The driver is operating in multi-mac mode by default, thus
|
||||
working as N individual network interfaces.
|
||||
|
||||
Devlink configuration parameters
|
||||
================================
|
||||
|
||||
See Documentation/networking/devlink/am65-nuss-cpsw-switch.rst
|
||||
|
||||
Enabling "switch"
|
||||
=================
|
||||
|
||||
The Switch mode can be enabled by configuring devlink driver parameter
|
||||
"switch_mode" to 1/true::
|
||||
|
||||
devlink dev param set platform/c000000.ethernet \
|
||||
name switch_mode value true cmode runtime
|
||||
|
||||
This can be done regardless of the state of Port's netdev devices - UP/DOWN, but
|
||||
Port's netdev devices have to be in UP before joining to the bridge to avoid
|
||||
overwriting of bridge configuration as CPSW switch driver completely reloads its
|
||||
configuration when first port changes its state to UP.
|
||||
|
||||
When the both interfaces joined the bridge - CPSW switch driver will enable
|
||||
marking packets with offload_fwd_mark flag.
|
||||
|
||||
All configuration is implemented via switchdev API.
|
||||
|
||||
Bridge setup
|
||||
============
|
||||
|
||||
::
|
||||
|
||||
devlink dev param set platform/c000000.ethernet \
|
||||
name switch_mode value true cmode runtime
|
||||
|
||||
ip link add name br0 type bridge
|
||||
ip link set dev br0 type bridge ageing_time 1000
|
||||
ip link set dev sw0p1 up
|
||||
ip link set dev sw0p2 up
|
||||
ip link set dev sw0p1 master br0
|
||||
ip link set dev sw0p2 master br0
|
||||
|
||||
[*] bridge vlan add dev br0 vid 1 pvid untagged self
|
||||
|
||||
[*] if vlan_filtering=1. where default_pvid=1
|
||||
|
||||
Note. Steps [*] are mandatory.
|
||||
|
||||
|
||||
On/off STP
|
||||
==========
|
||||
|
||||
::
|
||||
|
||||
ip link set dev BRDEV type bridge stp_state 1/0
|
||||
|
||||
VLAN configuration
|
||||
==================
|
||||
|
||||
::
|
||||
|
||||
bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1
|
||||
|
||||
Note. This step is mandatory for bridge/default_pvid.
|
||||
|
||||
Add extra VLANs
|
||||
===============
|
||||
|
||||
1. untagged::
|
||||
|
||||
bridge vlan add dev sw0p1 vid 100 pvid untagged master
|
||||
bridge vlan add dev sw0p2 vid 100 pvid untagged master
|
||||
bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100
|
||||
|
||||
2. tagged::
|
||||
|
||||
bridge vlan add dev sw0p1 vid 100 master
|
||||
bridge vlan add dev sw0p2 vid 100 master
|
||||
bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100
|
||||
|
||||
FDBs
|
||||
----
|
||||
|
||||
FDBs are automatically added on the appropriate switch port upon detection
|
||||
|
||||
Manually adding FDBs::
|
||||
|
||||
bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
|
||||
bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs
|
||||
|
||||
MDBs
|
||||
----
|
||||
|
||||
MDBs are automatically added on the appropriate switch port upon detection
|
||||
|
||||
Manually adding MDBs::
|
||||
|
||||
bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
|
||||
bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs
|
||||
|
||||
Multicast flooding
|
||||
==================
|
||||
CPU port mcast_flooding is always on
|
||||
|
||||
Turning flooding on/off on swithch ports:
|
||||
bridge link set dev sw0p1 mcast_flood on/off
|
||||
|
||||
Access and Trunk port
|
||||
=====================
|
||||
|
||||
::
|
||||
|
||||
bridge vlan add dev sw0p1 vid 100 pvid untagged master
|
||||
bridge vlan add dev sw0p2 vid 100 master
|
||||
|
||||
|
||||
bridge vlan add dev br0 vid 100 self
|
||||
ip link add link br0 name br0.100 type vlan id 100
|
||||
|
||||
Note. Setting PVID on Bridge device itself works only for
|
||||
default VLAN (default_pvid).
|
26
Documentation/networking/devlink/am65-nuss-cpsw-switch.rst
Normal file
26
Documentation/networking/devlink/am65-nuss-cpsw-switch.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==============================
|
||||
am65-cpsw-nuss devlink support
|
||||
==============================
|
||||
|
||||
This document describes the devlink features implemented by the ``am65-cpsw-nuss``
|
||||
device driver.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
The ``am65-cpsw-nuss`` driver implements the following driver-specific
|
||||
parameters.
|
||||
|
||||
.. list-table:: Driver-specific parameters implemented
|
||||
:widths: 5 5 5 85
|
||||
|
||||
* - Name
|
||||
- Type
|
||||
- Mode
|
||||
- Description
|
||||
* - ``switch_mode``
|
||||
- Boolean
|
||||
- runtime
|
||||
- Enable switch mode
|
199
Documentation/networking/devlink/devlink-port.rst
Normal file
199
Documentation/networking/devlink/devlink-port.rst
Normal file
@ -0,0 +1,199 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. _devlink_port:
|
||||
|
||||
============
|
||||
Devlink Port
|
||||
============
|
||||
|
||||
``devlink-port`` is a port that exists on the device. It has a logically
|
||||
separate ingress/egress point of the device. A devlink port can be any one
|
||||
of many flavours. A devlink port flavour along with port attributes
|
||||
describe what a port represents.
|
||||
|
||||
A device driver that intends to publish a devlink port sets the
|
||||
devlink port attributes and registers the devlink port.
|
||||
|
||||
Devlink port flavours are described below.
|
||||
|
||||
.. list-table:: List of devlink port flavours
|
||||
:widths: 33 90
|
||||
|
||||
* - Flavour
|
||||
- Description
|
||||
* - ``DEVLINK_PORT_FLAVOUR_PHYSICAL``
|
||||
- Any kind of physical port. This can be an eswitch physical port or any
|
||||
other physical port on the device.
|
||||
* - ``DEVLINK_PORT_FLAVOUR_DSA``
|
||||
- This indicates a DSA interconnect port.
|
||||
* - ``DEVLINK_PORT_FLAVOUR_CPU``
|
||||
- This indicates a CPU port applicable only to DSA.
|
||||
* - ``DEVLINK_PORT_FLAVOUR_PCI_PF``
|
||||
- This indicates an eswitch port representing a port of PCI
|
||||
physical function (PF).
|
||||
* - ``DEVLINK_PORT_FLAVOUR_PCI_VF``
|
||||
- This indicates an eswitch port representing a port of PCI
|
||||
virtual function (VF).
|
||||
* - ``DEVLINK_PORT_FLAVOUR_PCI_SF``
|
||||
- This indicates an eswitch port representing a port of PCI
|
||||
subfunction (SF).
|
||||
* - ``DEVLINK_PORT_FLAVOUR_VIRTUAL``
|
||||
- This indicates a virtual port for the PCI virtual function.
|
||||
|
||||
Devlink port can have a different type based on the link layer described below.
|
||||
|
||||
.. list-table:: List of devlink port types
|
||||
:widths: 23 90
|
||||
|
||||
* - Type
|
||||
- Description
|
||||
* - ``DEVLINK_PORT_TYPE_ETH``
|
||||
- Driver should set this port type when a link layer of the port is
|
||||
Ethernet.
|
||||
* - ``DEVLINK_PORT_TYPE_IB``
|
||||
- Driver should set this port type when a link layer of the port is
|
||||
InfiniBand.
|
||||
* - ``DEVLINK_PORT_TYPE_AUTO``
|
||||
- This type is indicated by the user when driver should detect the port
|
||||
type automatically.
|
||||
|
||||
PCI controllers
|
||||
---------------
|
||||
In most cases a PCI device has only one controller. A controller consists of
|
||||
potentially multiple physical, virtual functions and subfunctions. A function
|
||||
consists of one or more ports. This port is represented by the devlink eswitch
|
||||
port.
|
||||
|
||||
A PCI device connected to multiple CPUs or multiple PCI root complexes or a
|
||||
SmartNIC, however, may have multiple controllers. For a device with multiple
|
||||
controllers, each controller is distinguished by a unique controller number.
|
||||
An eswitch is on the PCI device which supports ports of multiple controllers.
|
||||
|
||||
An example view of a system with two controllers::
|
||||
|
||||
---------------------------------------------------------
|
||||
| |
|
||||
| --------- --------- ------- ------- |
|
||||
----------- | | vf(s) | | sf(s) | |vf(s)| |sf(s)| |
|
||||
| server | | ------- ----/---- ---/----- ------- ---/--- ---/--- |
|
||||
| pci rc |=== | pf0 |______/________/ | pf1 |___/_______/ |
|
||||
| connect | | ------- ------- |
|
||||
----------- | | controller_num=1 (no eswitch) |
|
||||
------|--------------------------------------------------
|
||||
(internal wire)
|
||||
|
|
||||
---------------------------------------------------------
|
||||
| devlink eswitch ports and reps |
|
||||
| ----------------------------------------------------- |
|
||||
| |ctrl-0 | ctrl-0 | ctrl-0 | ctrl-0 | ctrl-0 |ctrl-0 | |
|
||||
| |pf0 | pf0vfN | pf0sfN | pf1 | pf1vfN |pf1sfN | |
|
||||
| ----------------------------------------------------- |
|
||||
| |ctrl-1 | ctrl-1 | ctrl-1 | ctrl-1 | ctrl-1 |ctrl-1 | |
|
||||
| |pf0 | pf0vfN | pf0sfN | pf1 | pf1vfN |pf1sfN | |
|
||||
| ----------------------------------------------------- |
|
||||
| |
|
||||
| |
|
||||
----------- | --------- --------- ------- ------- |
|
||||
| smartNIC| | | vf(s) | | sf(s) | |vf(s)| |sf(s)| |
|
||||
| pci rc |==| ------- ----/---- ---/----- ------- ---/--- ---/--- |
|
||||
| connect | | | pf0 |______/________/ | pf1 |___/_______/ |
|
||||
----------- | ------- ------- |
|
||||
| |
|
||||
| local controller_num=0 (eswitch) |
|
||||
---------------------------------------------------------
|
||||
|
||||
In the above example, the external controller (identified by controller number = 1)
|
||||
doesn't have the eswitch. Local controller (identified by controller number = 0)
|
||||
has the eswitch. The Devlink instance on the local controller has eswitch
|
||||
devlink ports for both the controllers.
|
||||
|
||||
Function configuration
|
||||
======================
|
||||
|
||||
A user can configure the function attribute before enumerating the PCI
|
||||
function. Usually it means, user should configure function attribute
|
||||
before a bus specific device for the function is created. However, when
|
||||
SRIOV is enabled, virtual function devices are created on the PCI bus.
|
||||
Hence, function attribute should be configured before binding virtual
|
||||
function device to the driver. For subfunctions, this means user should
|
||||
configure port function attribute before activating the port function.
|
||||
|
||||
A user may set the hardware address of the function using
|
||||
'devlink port function set hw_addr' command. For Ethernet port function
|
||||
this means a MAC address.
|
||||
|
||||
Subfunction
|
||||
============
|
||||
|
||||
Subfunction is a lightweight function that has a parent PCI function on which
|
||||
it is deployed. Subfunction is created and deployed in unit of 1. Unlike
|
||||
SRIOV VFs, a subfunction doesn't require its own PCI virtual function.
|
||||
A subfunction communicates with the hardware through the parent PCI function.
|
||||
|
||||
To use a subfunction, 3 steps setup sequence is followed.
|
||||
(1) create - create a subfunction;
|
||||
(2) configure - configure subfunction attributes;
|
||||
(3) deploy - deploy the subfunction;
|
||||
|
||||
Subfunction management is done using devlink port user interface.
|
||||
User performs setup on the subfunction management device.
|
||||
|
||||
(1) Create
|
||||
----------
|
||||
A subfunction is created using a devlink port interface. A user adds the
|
||||
subfunction by adding a devlink port of subfunction flavour. The devlink
|
||||
kernel code calls down to subfunction management driver (devlink ops) and asks
|
||||
it to create a subfunction devlink port. Driver then instantiates the
|
||||
subfunction port and any associated objects such as health reporters and
|
||||
representor netdevice.
|
||||
|
||||
(2) Configure
|
||||
-------------
|
||||
A subfunction devlink port is created but it is not active yet. That means the
|
||||
entities are created on devlink side, the e-switch port representor is created,
|
||||
but the subfunction device itself it not created. A user might use e-switch port
|
||||
representor to do settings, putting it into bridge, adding TC rules, etc. A user
|
||||
might as well configure the hardware address (such as MAC address) of the
|
||||
subfunction while subfunction is inactive.
|
||||
|
||||
(3) Deploy
|
||||
----------
|
||||
Once a subfunction is configured, user must activate it to use it. Upon
|
||||
activation, subfunction management driver asks the subfunction management
|
||||
device to instantiate the subfunction device on particular PCI function.
|
||||
A subfunction device is created on the :ref:`Documentation/driver-api/auxiliary_bus.rst <auxiliary_bus>`.
|
||||
At this point a matching subfunction driver binds to the subfunction's auxiliary device.
|
||||
|
||||
Terms and Definitions
|
||||
=====================
|
||||
|
||||
.. list-table:: Terms and Definitions
|
||||
:widths: 22 90
|
||||
|
||||
* - Term
|
||||
- Definitions
|
||||
* - ``PCI device``
|
||||
- A physical PCI device having one or more PCI bus consists of one or
|
||||
more PCI controllers.
|
||||
* - ``PCI controller``
|
||||
- A controller consists of potentially multiple physical functions,
|
||||
virtual functions and subfunctions.
|
||||
* - ``Port function``
|
||||
- An object to manage the function of a port.
|
||||
* - ``Subfunction``
|
||||
- A lightweight function that has parent PCI function on which it is
|
||||
deployed.
|
||||
* - ``Subfunction device``
|
||||
- A bus device of the subfunction, usually on a auxiliary bus.
|
||||
* - ``Subfunction driver``
|
||||
- A device driver for the subfunction auxiliary device.
|
||||
* - ``Subfunction management device``
|
||||
- A PCI physical function that supports subfunction management.
|
||||
* - ``Subfunction management driver``
|
||||
- A device driver for PCI physical function that supports
|
||||
subfunction management using devlink port interface.
|
||||
* - ``Subfunction host driver``
|
||||
- A device driver for PCI physical function that hosts subfunction
|
||||
devices. In most cases it is same as subfunction management driver. When
|
||||
subfunction is used on external controller, subfunction management and
|
||||
host drivers are different.
|
@ -23,6 +23,20 @@ current size and related sub resources. To access a sub resource, you
|
||||
specify the path of the resource. For example ``/IPv4/fib`` is the id for
|
||||
the ``fib`` sub-resource under the ``IPv4`` resource.
|
||||
|
||||
Generic Resources
|
||||
=================
|
||||
|
||||
Generic resources are used to describe resources that can be shared by multiple
|
||||
device drivers and their description must be added to the following table:
|
||||
|
||||
.. list-table:: List of Generic Resources
|
||||
:widths: 10 90
|
||||
|
||||
* - Name
|
||||
- Description
|
||||
* - ``physical_ports``
|
||||
- A limited capacity of physical ports that the switch ASIC can support
|
||||
|
||||
example usage
|
||||
-------------
|
||||
|
||||
|
@ -480,6 +480,11 @@ be added to the following table:
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop in case they hit a
|
||||
blackhole nexthop
|
||||
* - ``dmac_filter``
|
||||
- ``drop``
|
||||
- Traps incoming packets that the device decided to drop because
|
||||
the destination MAC is not configured in the MAC table and
|
||||
the interface is not in promiscuous mode
|
||||
|
||||
Driver-specific Packet Traps
|
||||
============================
|
||||
|
@ -18,6 +18,7 @@ general.
|
||||
devlink-info
|
||||
devlink-flash
|
||||
devlink-params
|
||||
devlink-port
|
||||
devlink-region
|
||||
devlink-resource
|
||||
devlink-reload
|
||||
@ -44,3 +45,4 @@ parameters, info versions, and other features it supports.
|
||||
sja1105
|
||||
qed
|
||||
ti-cpsw-switch
|
||||
am65-nuss-cpsw-switch
|
||||
|
@ -273,10 +273,6 @@ will not make us go through the switch tagging protocol transmit function, so
|
||||
the Ethernet switch on the other end, expecting a tag will typically drop this
|
||||
frame.
|
||||
|
||||
Slave network devices check that the master network device is UP before allowing
|
||||
you to administratively bring UP these slave network devices. A common
|
||||
configuration mistake is forgetting to bring UP the master network device first.
|
||||
|
||||
Interactions with other subsystems
|
||||
==================================
|
||||
|
||||
|
@ -431,16 +431,17 @@ Request contents:
|
||||
``ETHTOOL_A_LINKMODES_SPEED`` u32 link speed (Mb/s)
|
||||
``ETHTOOL_A_LINKMODES_DUPLEX`` u8 duplex mode
|
||||
``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG`` u8 Master/slave port mode
|
||||
``ETHTOOL_A_LINKMODES_LANES`` u32 lanes
|
||||
========================================== ====== ==========================
|
||||
|
||||
``ETHTOOL_A_LINKMODES_OURS`` bit set allows setting advertised link modes. If
|
||||
autonegotiation is on (either set now or kept from before), advertised modes
|
||||
are not changed (no ``ETHTOOL_A_LINKMODES_OURS`` attribute) and at least one
|
||||
of speed and duplex is specified, kernel adjusts advertised modes to all
|
||||
supported modes matching speed, duplex or both (whatever is specified). This
|
||||
autoselection is done on ethtool side with ioctl interface, netlink interface
|
||||
is supposed to allow requesting changes without knowing what exactly kernel
|
||||
supports.
|
||||
of speed, duplex and lanes is specified, kernel adjusts advertised modes to all
|
||||
supported modes matching speed, duplex, lanes or all (whatever is specified).
|
||||
This autoselection is done on ethtool side with ioctl interface, netlink
|
||||
interface is supposed to allow requesting changes without knowing what exactly
|
||||
kernel supports.
|
||||
|
||||
|
||||
LINKSTATE_GET
|
||||
|
@ -1006,13 +1006,13 @@ Size modifier is one of ...
|
||||
|
||||
Mode modifier is one of::
|
||||
|
||||
BPF_IMM 0x00 /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
|
||||
BPF_ABS 0x20
|
||||
BPF_IND 0x40
|
||||
BPF_MEM 0x60
|
||||
BPF_LEN 0x80 /* classic BPF only, reserved in eBPF */
|
||||
BPF_MSH 0xa0 /* classic BPF only, reserved in eBPF */
|
||||
BPF_XADD 0xc0 /* eBPF only, exclusive add */
|
||||
BPF_IMM 0x00 /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
|
||||
BPF_ABS 0x20
|
||||
BPF_IND 0x40
|
||||
BPF_MEM 0x60
|
||||
BPF_LEN 0x80 /* classic BPF only, reserved in eBPF */
|
||||
BPF_MSH 0xa0 /* classic BPF only, reserved in eBPF */
|
||||
BPF_ATOMIC 0xc0 /* eBPF only, atomic operations */
|
||||
|
||||
eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
|
||||
(BPF_IND | <size> | BPF_LD) which are used to access packet data.
|
||||
@ -1044,16 +1044,57 @@ Unlike classic BPF instruction set, eBPF has generic load/store operations::
|
||||
BPF_MEM | <size> | BPF_STX: *(size *) (dst_reg + off) = src_reg
|
||||
BPF_MEM | <size> | BPF_ST: *(size *) (dst_reg + off) = imm32
|
||||
BPF_MEM | <size> | BPF_LDX: dst_reg = *(size *) (src_reg + off)
|
||||
BPF_XADD | BPF_W | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
|
||||
BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
|
||||
|
||||
Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW. Note that 1 and
|
||||
2 byte atomic increments are not supported.
|
||||
Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW.
|
||||
|
||||
eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM which consists
|
||||
It also includes atomic operations, which use the immediate field for extra
|
||||
encoding::
|
||||
|
||||
.imm = BPF_ADD, .code = BPF_ATOMIC | BPF_W | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
|
||||
.imm = BPF_ADD, .code = BPF_ATOMIC | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
|
||||
|
||||
The basic atomic operations supported are::
|
||||
|
||||
BPF_ADD
|
||||
BPF_AND
|
||||
BPF_OR
|
||||
BPF_XOR
|
||||
|
||||
Each having equivalent semantics with the ``BPF_ADD`` example, that is: the
|
||||
memory location addresed by ``dst_reg + off`` is atomically modified, with
|
||||
``src_reg`` as the other operand. If the ``BPF_FETCH`` flag is set in the
|
||||
immediate, then these operations also overwrite ``src_reg`` with the
|
||||
value that was in memory before it was modified.
|
||||
|
||||
The more special operations are::
|
||||
|
||||
BPF_XCHG
|
||||
|
||||
This atomically exchanges ``src_reg`` with the value addressed by ``dst_reg +
|
||||
off``. ::
|
||||
|
||||
BPF_CMPXCHG
|
||||
|
||||
This atomically compares the value addressed by ``dst_reg + off`` with
|
||||
``R0``. If they match it is replaced with ``src_reg``. In either case, the
|
||||
value that was there before is zero-extended and loaded back to ``R0``.
|
||||
|
||||
Note that 1 and 2 byte atomic operations are not supported.
|
||||
|
||||
Clang can generate atomic instructions by default when ``-mcpu=v3`` is
|
||||
enabled. If a lower version for ``-mcpu`` is set, the only atomic instruction
|
||||
Clang can generate is ``BPF_ADD`` *without* ``BPF_FETCH``. If you need to enable
|
||||
the atomics features, while keeping a lower ``-mcpu`` version, you can use
|
||||
``-Xclang -target-feature -Xclang +alu32``.
|
||||
|
||||
You may encounter ``BPF_XADD`` - this is a legacy name for ``BPF_ATOMIC``,
|
||||
referring to the exclusive-add operation encoded when the immediate field is
|
||||
zero.
|
||||
|
||||
eBPF has one 16-byte instruction: ``BPF_LD | BPF_DW | BPF_IMM`` which consists
|
||||
of two consecutive ``struct bpf_insn`` 8-byte blocks and interpreted as single
|
||||
instruction that loads 64-bit immediate value into a dst_reg.
|
||||
Classic BPF has similar instruction: BPF_LD | BPF_W | BPF_IMM which loads
|
||||
Classic BPF has similar instruction: ``BPF_LD | BPF_W | BPF_IMM`` which loads
|
||||
32-bit immediate value into a register.
|
||||
|
||||
eBPF verifier
|
||||
|
@ -178,6 +178,27 @@ min_adv_mss - INTEGER
|
||||
The advertised MSS depends on the first hop route MTU, but will
|
||||
never be lower than this setting.
|
||||
|
||||
fib_notify_on_flag_change - INTEGER
|
||||
Whether to emit RTM_NEWROUTE notifications whenever RTM_F_OFFLOAD/
|
||||
RTM_F_TRAP/RTM_F_OFFLOAD_FAILED flags are changed.
|
||||
|
||||
After installing a route to the kernel, user space receives an
|
||||
acknowledgment, which means the route was installed in the kernel,
|
||||
but not necessarily in hardware.
|
||||
It is also possible for a route already installed in hardware to change
|
||||
its action and therefore its flags. For example, a host route that is
|
||||
trapping packets can be "promoted" to perform decapsulation following
|
||||
the installation of an IPinIP/VXLAN tunnel.
|
||||
The notifications will indicate to user-space the state of the route.
|
||||
|
||||
Default: 0 (Do not emit notifications.)
|
||||
|
||||
Possible values:
|
||||
|
||||
- 0 - Do not emit notifications.
|
||||
- 1 - Emit notifications.
|
||||
- 2 - Emit notifications only for RTM_F_OFFLOAD_FAILED flag change.
|
||||
|
||||
IP Fragmentation:
|
||||
|
||||
ipfrag_high_thresh - LONG INTEGER
|
||||
@ -630,16 +651,15 @@ tcp_rmem - vector of 3 INTEGERs: min, default, max
|
||||
|
||||
default: initial size of receive buffer used by TCP sockets.
|
||||
This value overrides net.core.rmem_default used by other protocols.
|
||||
Default: 87380 bytes. This value results in window of 65535 with
|
||||
default setting of tcp_adv_win_scale and tcp_app_win:0 and a bit
|
||||
less for default tcp_app_win. See below about these variables.
|
||||
Default: 131072 bytes.
|
||||
This value results in initial window of 65535.
|
||||
|
||||
max: maximal size of receive buffer allowed for automatically
|
||||
selected receiver buffers for TCP socket. This value does not override
|
||||
net.core.rmem_max. Calling setsockopt() with SO_RCVBUF disables
|
||||
automatic tuning of that socket's receive buffer size, in which
|
||||
case this value is ignored.
|
||||
Default: between 87380B and 6MB, depending on RAM size.
|
||||
Default: between 131072 and 6MB, depending on RAM size.
|
||||
|
||||
tcp_sack - BOOLEAN
|
||||
Enable select acknowledgments (SACKS).
|
||||
@ -1425,6 +1445,25 @@ rp_filter - INTEGER
|
||||
Default value is 0. Note that some distributions enable it
|
||||
in startup scripts.
|
||||
|
||||
src_valid_mark - BOOLEAN
|
||||
- 0 - The fwmark of the packet is not included in reverse path
|
||||
route lookup. This allows for asymmetric routing configurations
|
||||
utilizing the fwmark in only one direction, e.g., transparent
|
||||
proxying.
|
||||
|
||||
- 1 - The fwmark of the packet is included in reverse path route
|
||||
lookup. This permits rp_filter to function when the fwmark is
|
||||
used for routing traffic in both directions.
|
||||
|
||||
This setting also affects the utilization of fmwark when
|
||||
performing source address selection for ICMP replies, or
|
||||
determining addresses stored for the IPOPT_TS_TSANDADDR and
|
||||
IPOPT_RR IP options.
|
||||
|
||||
The max value from conf/{all,interface}/src_valid_mark is used.
|
||||
|
||||
Default value is 0.
|
||||
|
||||
arp_filter - BOOLEAN
|
||||
- 1 - Allows you to have multiple network interfaces on the same
|
||||
subnet, and have the ARPs for each interface be answered
|
||||
@ -1775,6 +1814,27 @@ nexthop_compat_mode - BOOLEAN
|
||||
and extraneous notifications.
|
||||
Default: true (backward compat mode)
|
||||
|
||||
fib_notify_on_flag_change - INTEGER
|
||||
Whether to emit RTM_NEWROUTE notifications whenever RTM_F_OFFLOAD/
|
||||
RTM_F_TRAP/RTM_F_OFFLOAD_FAILED flags are changed.
|
||||
|
||||
After installing a route to the kernel, user space receives an
|
||||
acknowledgment, which means the route was installed in the kernel,
|
||||
but not necessarily in hardware.
|
||||
It is also possible for a route already installed in hardware to change
|
||||
its action and therefore its flags. For example, a host route that is
|
||||
trapping packets can be "promoted" to perform decapsulation following
|
||||
the installation of an IPinIP/VXLAN tunnel.
|
||||
The notifications will indicate to user-space the state of the route.
|
||||
|
||||
Default: 0 (Do not emit notifications.)
|
||||
|
||||
Possible values:
|
||||
|
||||
- 0 - Do not emit notifications.
|
||||
- 1 - Emit notifications.
|
||||
- 2 - Emit notifications only for RTM_F_OFFLOAD_FAILED flag change.
|
||||
|
||||
IPv6 Fragmentation:
|
||||
|
||||
ip6frag_high_thresh - INTEGER
|
||||
@ -1883,6 +1943,16 @@ accept_ra_defrtr - BOOLEAN
|
||||
- enabled if accept_ra is enabled.
|
||||
- disabled if accept_ra is disabled.
|
||||
|
||||
ra_defrtr_metric - UNSIGNED INTEGER
|
||||
Route metric for default route learned in Router Advertisement. This value
|
||||
will be assigned as metric for the default route learned via IPv6 Router
|
||||
Advertisement. Takes affect only if accept_ra_defrtr is enabled.
|
||||
|
||||
Possible values:
|
||||
1 to 0xFFFFFFFF
|
||||
|
||||
Default: IP6_RT_PRIO_USER i.e. 1024.
|
||||
|
||||
accept_ra_from_local - BOOLEAN
|
||||
Accept RA with source-address that is found on local machine
|
||||
if the RA is otherwise proper and able to be accepted.
|
||||
|
@ -272,6 +272,22 @@ to the mailing list, e.g.::
|
||||
Posting as one thread is discouraged because it confuses patchwork
|
||||
(as of patchwork 2.2.2).
|
||||
|
||||
Can I reproduce the checks from patchwork on my local machine?
|
||||
--------------------------------------------------------------
|
||||
|
||||
Checks in patchwork are mostly simple wrappers around existing kernel
|
||||
scripts, the sources are available at:
|
||||
|
||||
https://github.com/kuba-moo/nipa/tree/master/tests
|
||||
|
||||
Running all the builds and checks locally is a pain, can I post my patches and have the patchwork bot validate them?
|
||||
--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
No, you must ensure that your patches are ready by testing them locally
|
||||
before posting to the mailing list. The patchwork build bot instance
|
||||
gets overloaded very easily and netdev@vger really doesn't need more
|
||||
traffic if we can help it.
|
||||
|
||||
Any other tips to help ensure my net/net-next patch gets OK'd?
|
||||
--------------------------------------------------------------
|
||||
Attention to detail. Re-read your own work as if you were the
|
||||
|
@ -182,3 +182,24 @@ stricter than Hardware LRO. A packet stream merged by Hardware GRO must
|
||||
be re-segmentable by GSO or TSO back to the exact original packet stream.
|
||||
Hardware GRO is dependent on RXCSUM since every packet successfully merged
|
||||
by hardware must also have the checksum verified by hardware.
|
||||
|
||||
* hsr-tag-ins-offload
|
||||
|
||||
This should be set for devices which insert an HSR (High-availability Seamless
|
||||
Redundancy) or PRP (Parallel Redundancy Protocol) tag automatically.
|
||||
|
||||
* hsr-tag-rm-offload
|
||||
|
||||
This should be set for devices which remove HSR (High-availability Seamless
|
||||
Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically.
|
||||
|
||||
* hsr-fwd-offload
|
||||
|
||||
This should be set for devices which forward HSR (High-availability Seamless
|
||||
Redundancy) frames from one port to another in hardware.
|
||||
|
||||
* hsr-dup-offload
|
||||
|
||||
This should be set for devices which duplicate outgoing HSR (High-availability
|
||||
Seamless Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically
|
||||
frames in hardware.
|
||||
|
@ -216,7 +216,7 @@ put into an unsupported state.
|
||||
Lastly, once the controller is ready to handle network traffic, you call
|
||||
phy_start(phydev). This tells the PAL that you are ready, and configures the
|
||||
PHY to connect to the network. If the MAC interrupt of your network driver
|
||||
also handles PHY status changes, just set phydev->irq to PHY_IGNORE_INTERRUPT
|
||||
also handles PHY status changes, just set phydev->irq to PHY_MAC_INTERRUPT
|
||||
before you call phy_start and use phy_mac_interrupt() from the network
|
||||
driver. If you don't want to use interrupts, set phydev->irq to PHY_POLL.
|
||||
phy_start() enables the PHY interrupts (if applicable) and starts the
|
||||
@ -267,6 +267,12 @@ Some of the interface modes are described below:
|
||||
duplex, pause or other settings. This is dependent on the MAC and/or
|
||||
PHY behaviour.
|
||||
|
||||
``PHY_INTERFACE_MODE_5GBASER``
|
||||
This is the IEEE 802.3 Clause 129 defined 5GBASE-R protocol. It is
|
||||
identical to the 10GBASE-R protocol defined in Clause 49, with the
|
||||
exception that it operates at half the frequency. Please refer to the
|
||||
IEEE standard for the definition.
|
||||
|
||||
``PHY_INTERFACE_MODE_10GBASER``
|
||||
This is the IEEE 802.3 Clause 49 defined 10GBASE-R protocol used with
|
||||
various different mediums. Please refer to the IEEE standard for a
|
||||
@ -286,6 +292,11 @@ Some of the interface modes are described below:
|
||||
Note: due to legacy usage, some 10GBASE-R usage incorrectly makes
|
||||
use of this definition.
|
||||
|
||||
``PHY_INTERFACE_MODE_100BASEX``
|
||||
This defines IEEE 802.3 Clause 24. The link operates at a fixed data
|
||||
rate of 125Mpbs using a 4B/5B encoding scheme, resulting in an underlying
|
||||
data rate of 100Mpbs.
|
||||
|
||||
Pause frames / flow control
|
||||
===========================
|
||||
|
||||
|
@ -163,7 +163,7 @@ this documentation.
|
||||
err = phylink_of_phy_connect(priv->phylink, node, flags);
|
||||
|
||||
For the most part, ``flags`` can be zero; these flags are passed to
|
||||
the of_phy_attach() inside this function call if a PHY is specified
|
||||
the phy_attach_direct() inside this function call if a PHY is specified
|
||||
in the DT node ``node``.
|
||||
|
||||
``node`` should be the DT node which contains the network phy property,
|
||||
|
@ -314,7 +314,7 @@ https://lwn.net/Articles/576263/
|
||||
* TcpExtTCPOrigDataSent
|
||||
|
||||
This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
|
||||
explaination below::
|
||||
explanation below::
|
||||
|
||||
TCPOrigDataSent: number of outgoing packets with original data (excluding
|
||||
retransmission but including data-in-SYN). This counter is different from
|
||||
@ -324,7 +324,7 @@ explaination below::
|
||||
* TCPSynRetrans
|
||||
|
||||
This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
|
||||
explaination below::
|
||||
explanation below::
|
||||
|
||||
TCPSynRetrans: number of SYN and SYN/ACK retransmits to break down
|
||||
retransmissions into SYN, fast-retransmits, timeout retransmits, etc.
|
||||
@ -332,7 +332,7 @@ explaination below::
|
||||
* TCPFastOpenActiveFail
|
||||
|
||||
This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
|
||||
explaination below::
|
||||
explanation below::
|
||||
|
||||
TCPFastOpenActiveFail: Fast Open attempts (SYN/data) failed because
|
||||
the remote does not accept it or the attempts timed out.
|
||||
@ -382,7 +382,7 @@ Defined in `RFC1213 tcpAttemptFails`_.
|
||||
|
||||
Defined in `RFC1213 tcpOutRsts`_. The RFC says this counter indicates
|
||||
the 'segments sent containing the RST flag', but in linux kernel, this
|
||||
couner indicates the segments kerenl tried to send. The sending
|
||||
counter indicates the segments kernel tried to send. The sending
|
||||
process might be failed due to some errors (e.g. memory alloc failed).
|
||||
|
||||
.. _RFC1213 tcpOutRsts: https://tools.ietf.org/html/rfc1213#page-52
|
||||
@ -700,7 +700,7 @@ SACK option could have up to 4 blocks, they are checked
|
||||
individually. E.g., if 3 blocks of a SACk is invalid, the
|
||||
corresponding counter would be updated 3 times. The comment of the
|
||||
`Add counters for discarded SACK blocks`_ patch has additional
|
||||
explaination:
|
||||
explanation:
|
||||
|
||||
.. _Add counters for discarded SACK blocks: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=18f02545a9a16c9a89778b91a162ad16d510bb32
|
||||
|
||||
@ -829,7 +829,7 @@ PAWS check fails or the received sequence number is out of window.
|
||||
|
||||
* TcpExtTCPACKSkippedTimeWait
|
||||
|
||||
Tha ACK is skipped in Time-Wait status, the reason would be either
|
||||
The ACK is skipped in Time-Wait status, the reason would be either
|
||||
PAWS check failed or the received sequence number is out of window.
|
||||
|
||||
* TcpExtTCPACKSkippedChallenge
|
||||
@ -984,7 +984,7 @@ TcpExtSyncookiesRecv counter wont be updated.
|
||||
|
||||
Challenge ACK
|
||||
=============
|
||||
For details of challenge ACK, please refer the explaination of
|
||||
For details of challenge ACK, please refer the explanation of
|
||||
TcpExtTCPACKSkippedChallenge.
|
||||
|
||||
* TcpExtTCPChallengeACK
|
||||
@ -1002,7 +1002,7 @@ prune
|
||||
=====
|
||||
When a socket is under memory pressure, the TCP stack will try to
|
||||
reclaim memory from the receiving queue and out of order queue. One of
|
||||
the reclaiming method is 'collapse', which means allocate a big sbk,
|
||||
the reclaiming method is 'collapse', which means allocate a big skb,
|
||||
copy the contiguous skbs to the single big skb, and free these
|
||||
contiguous skbs.
|
||||
|
||||
@ -1163,7 +1163,7 @@ The server side nstat output::
|
||||
IpExtOutOctets 52 0.0
|
||||
IpExtInNoECTPkts 1 0.0
|
||||
|
||||
Input a string in nc client side again ('world' in our exmaple)::
|
||||
Input a string in nc client side again ('world' in our example)::
|
||||
|
||||
nstatuser@nstat-a:~$ nc -v nstat-b 9000
|
||||
Connection to nstat-b 9000 port [tcp/*] succeeded!
|
||||
@ -1211,7 +1211,7 @@ replied an ACK. But kernel handled them in different ways. When the
|
||||
TCP window scale option is not used, kernel will try to enable fast
|
||||
path immediately when the connection comes into the established state,
|
||||
but if the TCP window scale option is used, kernel will disable the
|
||||
fast path at first, and try to enable it after kerenl receives
|
||||
fast path at first, and try to enable it after kernel receives
|
||||
packets. We could use the 'ss' command to verify whether the window
|
||||
scale option is used. e.g. run below command on either server or
|
||||
client::
|
||||
@ -1343,7 +1343,7 @@ Check TcpExtTCPAbortOnMemory on client::
|
||||
nstatuser@nstat-a:~$ nstat | grep -i abort
|
||||
TcpExtTCPAbortOnMemory 54 0.0
|
||||
|
||||
Check orphane socket count on client::
|
||||
Check orphaned socket count on client::
|
||||
|
||||
nstatuser@nstat-a:~$ ss -s
|
||||
Total: 131 (kernel 0)
|
||||
@ -1685,7 +1685,7 @@ Send 3 SYN repeatly to nstat-b::
|
||||
|
||||
nstatuser@nstat-a:~$ for i in {1..3}; do sudo tcpreplay -i ens3 /tmp/syn_fixcsum.pcap; done
|
||||
|
||||
Check snmp cunter on nstat-b::
|
||||
Check snmp counter on nstat-b::
|
||||
|
||||
nstatuser@nstat-b:~$ nstat | grep -i skip
|
||||
TcpExtTCPACKSkippedSynRecv 1 0.0
|
||||
@ -1770,7 +1770,7 @@ string 'foo' in our example::
|
||||
Connection from nstat-a 42132 received!
|
||||
foo
|
||||
|
||||
On nstat-a, the tcpdump should have caputred the ACK. We should check
|
||||
On nstat-a, the tcpdump should have captured the ACK. We should check
|
||||
the source port numbers of the two nc clients::
|
||||
|
||||
nstatuser@nstat-a:~$ ss -ta '( dport = :9000 || dport = :9001 )' | tee
|
||||
@ -1778,7 +1778,7 @@ the source port numbers of the two nc clients::
|
||||
ESTAB 0 0 192.168.122.250:50208 192.168.122.251:9000
|
||||
ESTAB 0 0 192.168.122.250:42132 192.168.122.251:9001
|
||||
|
||||
Run tcprewrite, change port 9001 to port 9000, chagne port 42132 to
|
||||
Run tcprewrite, change port 9001 to port 9000, change port 42132 to
|
||||
port 50208::
|
||||
|
||||
nstatuser@nstat-a:~$ tcprewrite --infile /tmp/seq_pre.pcap --outfile /tmp/seq.pcap -r 9001:9000 -r 42132:50208 --fixcsum
|
||||
|
@ -55,7 +55,8 @@ struct __kernel_sock_timeval format.
|
||||
SO_TIMESTAMP_OLD returns incorrect timestamps after the year 2038
|
||||
on 32 bit machines.
|
||||
|
||||
1.2 SO_TIMESTAMPNS (also SO_TIMESTAMPNS_OLD and SO_TIMESTAMPNS_NEW):
|
||||
1.2 SO_TIMESTAMPNS (also SO_TIMESTAMPNS_OLD and SO_TIMESTAMPNS_NEW)
|
||||
-------------------------------------------------------------------
|
||||
|
||||
This option is identical to SO_TIMESTAMP except for the returned data type.
|
||||
Its struct timespec allows for higher resolution (ns) timestamps than the
|
||||
|
47
MAINTAINERS
47
MAINTAINERS
@ -2641,8 +2641,10 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwamatsu/linux-visconti.git
|
||||
F: Documentation/devicetree/bindings/arm/toshiba.yaml
|
||||
F: Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml
|
||||
F: Documentation/devicetree/bindings/pinctrl/toshiba,tmpv7700-pinctrl.yaml
|
||||
F: arch/arm64/boot/dts/toshiba/
|
||||
F: drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
|
||||
F: drivers/pinctrl/visconti/
|
||||
N: visconti
|
||||
|
||||
@ -2787,6 +2789,14 @@ F: arch/arm64/
|
||||
F: tools/testing/selftests/arm64/
|
||||
X: arch/arm64/boot/dts/
|
||||
|
||||
ARROW SPEEDCHIPS XRS7000 SERIES ETHERNET SWITCH DRIVER
|
||||
M: George McCollister <george.mccollister@gmail.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/dsa/arrow,xrs700x.yaml
|
||||
F: drivers/net/dsa/xrs700x/*
|
||||
F: net/dsa/tag_xrs700x.c
|
||||
|
||||
AS3645A LED FLASH CONTROLLER DRIVER
|
||||
M: Sakari Ailus <sakari.ailus@iki.fi>
|
||||
L: linux-leds@vger.kernel.org
|
||||
@ -3399,6 +3409,7 @@ L: openwrt-devel@lists.openwrt.org (subscribers-only)
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml
|
||||
F: drivers/net/dsa/b53/*
|
||||
F: include/linux/dsa/brcm.h
|
||||
F: include/linux/platform_data/b53.h
|
||||
|
||||
BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
|
||||
@ -3436,6 +3447,15 @@ F: Documentation/devicetree/bindings/mips/brcm/
|
||||
F: arch/mips/bcm47xx/*
|
||||
F: arch/mips/include/asm/mach-bcm47xx/*
|
||||
|
||||
BROADCOM BCM4908 ETHERNET DRIVER
|
||||
M: Rafał Miłecki <rafal@milecki.pl>
|
||||
M: bcm-kernel-feedback-list@broadcom.com
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/brcm,bcm4908-enet.yaml
|
||||
F: drivers/net/ethernet/broadcom/bcm4908_enet.*
|
||||
F: drivers/net/ethernet/broadcom/unimac.h
|
||||
|
||||
BROADCOM BCM5301X ARM ARCHITECTURE
|
||||
M: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
M: Rafał Miłecki <zajec5@gmail.com>
|
||||
@ -3624,6 +3644,7 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/net/brcm,bcmgenet.txt
|
||||
F: Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt
|
||||
F: drivers/net/ethernet/broadcom/genet/
|
||||
F: drivers/net/ethernet/broadcom/unimac.h
|
||||
F: drivers/net/mdio/mdio-bcm-unimac.c
|
||||
F: include/linux/platform_data/bcmgenet.h
|
||||
F: include/linux/platform_data/mdio-bcm-unimac.h
|
||||
@ -3657,6 +3678,15 @@ N: bcm88312
|
||||
N: hr2
|
||||
N: stingray
|
||||
|
||||
BROADCOM IPROC GBIT ETHERNET DRIVER
|
||||
M: Rafał Miłecki <rafal@milecki.pl>
|
||||
M: bcm-kernel-feedback-list@broadcom.com
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/brcm,amac.txt
|
||||
F: drivers/net/ethernet/broadcom/bgmac*
|
||||
F: drivers/net/ethernet/broadcom/unimac.h
|
||||
|
||||
BROADCOM KONA GPIO DRIVER
|
||||
M: Ray Jui <rjui@broadcom.com>
|
||||
L: bcm-kernel-feedback-list@broadcom.com
|
||||
@ -3736,6 +3766,7 @@ L: bcm-kernel-feedback-list@broadcom.com
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/net/ethernet/broadcom/bcmsysport.*
|
||||
F: drivers/net/ethernet/broadcom/unimac.h
|
||||
|
||||
BROADCOM TG3 GIGABIT ETHERNET DRIVER
|
||||
M: Siva Reddy Kallam <siva.kallam@broadcom.com>
|
||||
@ -3930,8 +3961,10 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
|
||||
F: Documentation/devicetree/bindings/net/can/
|
||||
F: drivers/net/can/
|
||||
F: include/linux/can/bittiming.h
|
||||
F: include/linux/can/dev.h
|
||||
F: include/linux/can/led.h
|
||||
F: include/linux/can/length.h
|
||||
F: include/linux/can/platform/
|
||||
F: include/linux/can/rx-offload.h
|
||||
F: include/uapi/linux/can/error.h
|
||||
@ -3947,6 +3980,7 @@ W: https://github.com/linux-can
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
|
||||
F: Documentation/networking/can.rst
|
||||
F: include/linux/can/can-ml.h
|
||||
F: include/linux/can/core.h
|
||||
F: include/linux/can/skb.h
|
||||
F: include/net/netns/can.h
|
||||
@ -10687,6 +10721,8 @@ M: Sunil Goutham <sgoutham@marvell.com>
|
||||
M: Linu Cherian <lcherian@marvell.com>
|
||||
M: Geetha sowjanya <gakula@marvell.com>
|
||||
M: Jerin Jacob <jerinj@marvell.com>
|
||||
M: hariprasad <hkelam@marvell.com>
|
||||
M: Subbaraya Sundeep <sbhatta@marvell.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst
|
||||
@ -12517,6 +12553,14 @@ F: include/net/nfc/
|
||||
F: include/uapi/linux/nfc.h
|
||||
F: net/nfc/
|
||||
|
||||
NFC VIRTUAL NCI DEVICE DRIVER
|
||||
M: Bongsu Jeon <bongsu.jeon@samsung.com>
|
||||
L: netdev@vger.kernel.org
|
||||
L: linux-nfc@lists.01.org (moderated for non-subscribers)
|
||||
S: Supported
|
||||
F: drivers/nfc/virtual_ncidev.c
|
||||
F: tools/testing/selftests/nci/
|
||||
|
||||
NFS, SUNRPC, AND LOCKD CLIENTS
|
||||
M: Trond Myklebust <trond.myklebust@hammerspace.com>
|
||||
M: Anna Schumaker <anna.schumaker@netapp.com>
|
||||
@ -12828,6 +12872,7 @@ F: drivers/net/dsa/ocelot/*
|
||||
F: drivers/net/ethernet/mscc/
|
||||
F: include/soc/mscc/ocelot*
|
||||
F: net/dsa/tag_ocelot.c
|
||||
F: net/dsa/tag_ocelot_8021q.c
|
||||
F: tools/testing/selftests/drivers/net/ocelot/*
|
||||
|
||||
OCXL (Open Coherent Accelerator Processor Interface OpenCAPI) DRIVER
|
||||
@ -17844,7 +17889,7 @@ M: Dan Murphy <dmurphy@ti.com>
|
||||
L: linux-can@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/can/tcan4x5x.txt
|
||||
F: drivers/net/can/m_can/tcan4x5x.c
|
||||
F: drivers/net/can/m_can/tcan4x5x*
|
||||
|
||||
TI TRF7970A NFC DRIVER
|
||||
M: Mark Greer <mgreer@animalcreek.com>
|
||||
|
16
Makefile
16
Makefile
@ -648,7 +648,8 @@ ifeq ($(KBUILD_EXTMOD),)
|
||||
core-y := init/ usr/
|
||||
drivers-y := drivers/ sound/
|
||||
drivers-$(CONFIG_SAMPLES) += samples/
|
||||
drivers-y += net/ virt/
|
||||
drivers-$(CONFIG_NET) += net/
|
||||
drivers-y += virt/
|
||||
libs-y := lib/
|
||||
endif # KBUILD_EXTMOD
|
||||
|
||||
@ -1081,6 +1082,17 @@ ifdef CONFIG_STACK_VALIDATION
|
||||
endif
|
||||
endif
|
||||
|
||||
PHONY += resolve_btfids_clean
|
||||
|
||||
resolve_btfids_O = $(abspath $(objtree))/tools/bpf/resolve_btfids
|
||||
|
||||
# tools/bpf/resolve_btfids directory might not exist
|
||||
# in output directory, skip its clean in that case
|
||||
resolve_btfids_clean:
|
||||
ifneq ($(wildcard $(resolve_btfids_O)),)
|
||||
$(Q)$(MAKE) -sC $(srctree)/tools/bpf/resolve_btfids O=$(resolve_btfids_O) clean
|
||||
endif
|
||||
|
||||
ifdef CONFIG_BPF
|
||||
ifdef CONFIG_DEBUG_INFO_BTF
|
||||
ifeq ($(has_libelf),1)
|
||||
@ -1490,7 +1502,7 @@ vmlinuxclean:
|
||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean
|
||||
$(Q)$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) clean)
|
||||
|
||||
clean: archclean vmlinuxclean
|
||||
clean: archclean vmlinuxclean resolve_btfids_clean
|
||||
|
||||
# mrproper - Delete all generated files, including .config
|
||||
#
|
||||
|
@ -1620,10 +1620,9 @@ exit:
|
||||
}
|
||||
emit_str_r(dst_lo, tmp2, off, ctx, BPF_SIZE(code));
|
||||
break;
|
||||
/* STX XADD: lock *(u32 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_W:
|
||||
/* STX XADD: lock *(u64 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_DW:
|
||||
/* Atomic ops */
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
goto notyet;
|
||||
/* STX: *(size *)(dst + off) = src */
|
||||
case BPF_STX | BPF_MEM | BPF_W:
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
CP11X_LABEL(ethernet): ethernet@0 {
|
||||
compatible = "marvell,armada-7k-pp22";
|
||||
reg = <0x0 0x100000>, <0x129000 0xb000>;
|
||||
reg = <0x0 0x100000>, <0x129000 0xb000>, <0x220000 0x800>;
|
||||
clocks = <&CP11X_LABEL(clk) 1 3>, <&CP11X_LABEL(clk) 1 9>,
|
||||
<&CP11X_LABEL(clk) 1 5>, <&CP11X_LABEL(clk) 1 6>,
|
||||
<&CP11X_LABEL(clk) 1 18>;
|
||||
|
@ -1434,8 +1434,6 @@
|
||||
qcom,smem-state-names = "ipa-clock-enabled-valid",
|
||||
"ipa-clock-enabled";
|
||||
|
||||
modem-remoteproc = <&remoteproc_mpss>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -2366,8 +2366,6 @@
|
||||
qcom,smem-state-names = "ipa-clock-enabled-valid",
|
||||
"ipa-clock-enabled";
|
||||
|
||||
modem-remoteproc = <&mss_pil>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -41,3 +41,21 @@
|
||||
clocks = <&uart_clk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
&piether {
|
||||
status = "okay";
|
||||
phy-handle = <&phy0>;
|
||||
phy-mode = "rgmii-id";
|
||||
clocks = <&clk300mhz>, <&clk125mhz>;
|
||||
clock-names = "stmmaceth", "phy_ref_clk";
|
||||
|
||||
mdio0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "snps,dwmac-mdio";
|
||||
phy0: ethernet-phy@1 {
|
||||
device_type = "ethernet-phy";
|
||||
reg = <0x1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -134,6 +134,20 @@
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
clk125mhz: clk125mhz {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <125000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clk125mhz";
|
||||
};
|
||||
|
||||
clk300mhz: clk300mhz {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <300000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clk300mhz";
|
||||
};
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
@ -384,6 +398,17 @@
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
piether: ethernet@28000000 {
|
||||
compatible = "toshiba,visconti-dwmac", "snps,dwmac-4.20a";
|
||||
reg = <0 0x28000000 0 0x10000>;
|
||||
interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "macirq";
|
||||
snps,txpbl = <4>;
|
||||
snps,rxpbl = <4>;
|
||||
snps,tso;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -875,10 +875,18 @@ emit_cond_jmp:
|
||||
}
|
||||
break;
|
||||
|
||||
/* STX XADD: lock *(u32 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_W:
|
||||
/* STX XADD: lock *(u64 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_DW:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err_once("unknown atomic op code %02x\n", insn->imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* STX XADD: lock *(u32 *)(dst + off) += src
|
||||
* and
|
||||
* STX XADD: lock *(u64 *)(dst + off) += src
|
||||
*/
|
||||
|
||||
if (!off) {
|
||||
reg = dst;
|
||||
} else {
|
||||
|
@ -1423,8 +1423,8 @@ jeq_common:
|
||||
case BPF_STX | BPF_H | BPF_MEM:
|
||||
case BPF_STX | BPF_W | BPF_MEM:
|
||||
case BPF_STX | BPF_DW | BPF_MEM:
|
||||
case BPF_STX | BPF_W | BPF_XADD:
|
||||
case BPF_STX | BPF_DW | BPF_XADD:
|
||||
case BPF_STX | BPF_W | BPF_ATOMIC:
|
||||
case BPF_STX | BPF_DW | BPF_ATOMIC:
|
||||
if (insn->dst_reg == BPF_REG_10) {
|
||||
ctx->flags |= EBPF_SEEN_FP;
|
||||
dst = MIPS_R_SP;
|
||||
@ -1438,7 +1438,12 @@ jeq_common:
|
||||
src = ebpf_to_mips_reg(ctx, insn, src_reg_no_fp);
|
||||
if (src < 0)
|
||||
return src;
|
||||
if (BPF_MODE(insn->code) == BPF_XADD) {
|
||||
if (BPF_MODE(insn->code) == BPF_ATOMIC) {
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err("ATOMIC OP %02x NOT HANDLED\n", insn->imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If mem_off does not fit within the 9 bit ll/sc
|
||||
* instruction immediate field, use a temp reg.
|
||||
|
@ -683,10 +683,18 @@ emit_clear:
|
||||
break;
|
||||
|
||||
/*
|
||||
* BPF_STX XADD (atomic_add)
|
||||
* BPF_STX ATOMIC (atomic ops)
|
||||
*/
|
||||
/* *(u32 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err_ratelimited(
|
||||
"eBPF filter atomic op code %02x (@%d) unsupported\n",
|
||||
code, i);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/* *(u32 *)(dst + off) += src */
|
||||
|
||||
/* Get EA into TMP_REG_1 */
|
||||
EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], dst_reg, off));
|
||||
tmp_idx = ctx->idx * 4;
|
||||
@ -699,8 +707,15 @@ emit_clear:
|
||||
/* we're done if this succeeded */
|
||||
PPC_BCC_SHORT(COND_NE, tmp_idx);
|
||||
break;
|
||||
/* *(u64 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_DW:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err_ratelimited(
|
||||
"eBPF filter atomic op code %02x (@%d) unsupported\n",
|
||||
code, i);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
/* *(u64 *)(dst + off) += src */
|
||||
|
||||
EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], dst_reg, off));
|
||||
tmp_idx = ctx->idx * 4;
|
||||
EMIT(PPC_RAW_LDARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0));
|
||||
|
@ -881,7 +881,7 @@ static int emit_store_r64(const s8 *dst, const s8 *src, s16 off,
|
||||
const s8 *rd = bpf_get_reg64(dst, tmp1, ctx);
|
||||
const s8 *rs = bpf_get_reg64(src, tmp2, ctx);
|
||||
|
||||
if (mode == BPF_XADD && size != BPF_W)
|
||||
if (mode == BPF_ATOMIC && size != BPF_W)
|
||||
return -1;
|
||||
|
||||
emit_imm(RV_REG_T0, off, ctx);
|
||||
@ -899,7 +899,7 @@ static int emit_store_r64(const s8 *dst, const s8 *src, s16 off,
|
||||
case BPF_MEM:
|
||||
emit(rv_sw(RV_REG_T0, 0, lo(rs)), ctx);
|
||||
break;
|
||||
case BPF_XADD:
|
||||
case BPF_ATOMIC: /* Only BPF_ADD supported */
|
||||
emit(rv_amoadd_w(RV_REG_ZERO, lo(rs), RV_REG_T0, 0, 0),
|
||||
ctx);
|
||||
break;
|
||||
@ -1260,7 +1260,6 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
case BPF_STX | BPF_MEM | BPF_H:
|
||||
case BPF_STX | BPF_MEM | BPF_W:
|
||||
case BPF_STX | BPF_MEM | BPF_DW:
|
||||
case BPF_STX | BPF_XADD | BPF_W:
|
||||
if (BPF_CLASS(code) == BPF_ST) {
|
||||
emit_imm32(tmp2, imm, ctx);
|
||||
src = tmp2;
|
||||
@ -1271,8 +1270,21 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_info_once(
|
||||
"bpf-jit: not supported: atomic operation %02x ***\n",
|
||||
insn->imm);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (emit_store_r64(dst, src, off, ctx, BPF_SIZE(code),
|
||||
BPF_MODE(code)))
|
||||
return -1;
|
||||
break;
|
||||
|
||||
/* No hardware support for 8-byte atomics in RV32. */
|
||||
case BPF_STX | BPF_XADD | BPF_DW:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
/* Fallthrough. */
|
||||
|
||||
notsupported:
|
||||
|
@ -1027,10 +1027,18 @@ out_be:
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit_sd(RV_REG_T1, 0, rs, ctx);
|
||||
break;
|
||||
/* STX XADD: lock *(u32 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_W:
|
||||
/* STX XADD: lock *(u64 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_DW:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err("bpf-jit: not supported: atomic operation %02x ***\n",
|
||||
insn->imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* atomic_add: lock *(u32 *)(dst + off) += src
|
||||
* atomic_add: lock *(u64 *)(dst + off) += src
|
||||
*/
|
||||
|
||||
if (off) {
|
||||
if (is_12b_int(off)) {
|
||||
emit_addi(RV_REG_T1, rd, off, ctx);
|
||||
|
@ -1205,18 +1205,23 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
||||
jit->seen |= SEEN_MEM;
|
||||
break;
|
||||
/*
|
||||
* BPF_STX XADD (atomic_add)
|
||||
* BPF_ATOMIC
|
||||
*/
|
||||
case BPF_STX | BPF_XADD | BPF_W: /* *(u32 *)(dst + off) += src */
|
||||
/* laal %w0,%src,off(%dst) */
|
||||
EMIT6_DISP_LH(0xeb000000, 0x00fa, REG_W0, src_reg,
|
||||
dst_reg, off);
|
||||
jit->seen |= SEEN_MEM;
|
||||
break;
|
||||
case BPF_STX | BPF_XADD | BPF_DW: /* *(u64 *)(dst + off) += src */
|
||||
/* laalg %w0,%src,off(%dst) */
|
||||
EMIT6_DISP_LH(0xeb000000, 0x00ea, REG_W0, src_reg,
|
||||
dst_reg, off);
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err("Unknown atomic operation %02x\n", insn->imm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* *(u32/u64 *)(dst + off) += src
|
||||
*
|
||||
* BFW_W: laal %w0,%src,off(%dst)
|
||||
* BPF_DW: laalg %w0,%src,off(%dst)
|
||||
*/
|
||||
EMIT6_DISP_LH(0xeb000000,
|
||||
BPF_SIZE(insn->code) == BPF_W ? 0x00fa : 0x00ea,
|
||||
REG_W0, src_reg, dst_reg, off);
|
||||
jit->seen |= SEEN_MEM;
|
||||
break;
|
||||
/*
|
||||
|
@ -1366,12 +1366,18 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
||||
break;
|
||||
}
|
||||
|
||||
/* STX XADD: lock *(u32 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_W: {
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W: {
|
||||
const u8 tmp = bpf2sparc[TMP_REG_1];
|
||||
const u8 tmp2 = bpf2sparc[TMP_REG_2];
|
||||
const u8 tmp3 = bpf2sparc[TMP_REG_3];
|
||||
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err_once("unknown atomic op %02x\n", insn->imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* lock *(u32 *)(dst + off) += src */
|
||||
|
||||
if (insn->dst_reg == BPF_REG_FP)
|
||||
ctx->saw_frame_pointer = true;
|
||||
|
||||
@ -1390,11 +1396,16 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
||||
break;
|
||||
}
|
||||
/* STX XADD: lock *(u64 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_DW: {
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW: {
|
||||
const u8 tmp = bpf2sparc[TMP_REG_1];
|
||||
const u8 tmp2 = bpf2sparc[TMP_REG_2];
|
||||
const u8 tmp3 = bpf2sparc[TMP_REG_3];
|
||||
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err_once("unknown atomic op %02x\n", insn->imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (insn->dst_reg == BPF_REG_FP)
|
||||
ctx->saw_frame_pointer = true;
|
||||
|
||||
|
@ -205,6 +205,18 @@ static u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg)
|
||||
return byte + reg2hex[dst_reg] + (reg2hex[src_reg] << 3);
|
||||
}
|
||||
|
||||
/* Some 1-byte opcodes for binary ALU operations */
|
||||
static u8 simple_alu_opcodes[] = {
|
||||
[BPF_ADD] = 0x01,
|
||||
[BPF_SUB] = 0x29,
|
||||
[BPF_AND] = 0x21,
|
||||
[BPF_OR] = 0x09,
|
||||
[BPF_XOR] = 0x31,
|
||||
[BPF_LSH] = 0xE0,
|
||||
[BPF_RSH] = 0xE8,
|
||||
[BPF_ARSH] = 0xF8,
|
||||
};
|
||||
|
||||
static void jit_fill_hole(void *area, unsigned int size)
|
||||
{
|
||||
/* Fill whole space with INT3 instructions */
|
||||
@ -681,6 +693,42 @@ static void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg)
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
/* Emit the suffix (ModR/M etc) for addressing *(ptr_reg + off) and val_reg */
|
||||
static void emit_insn_suffix(u8 **pprog, u32 ptr_reg, u32 val_reg, int off)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
if (is_imm8(off)) {
|
||||
/* 1-byte signed displacement.
|
||||
*
|
||||
* If off == 0 we could skip this and save one extra byte, but
|
||||
* special case of x86 R13 which always needs an offset is not
|
||||
* worth the hassle
|
||||
*/
|
||||
EMIT2(add_2reg(0x40, ptr_reg, val_reg), off);
|
||||
} else {
|
||||
/* 4-byte signed displacement */
|
||||
EMIT1_off32(add_2reg(0x80, ptr_reg, val_reg), off);
|
||||
}
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit a REX byte if it will be necessary to address these registers
|
||||
*/
|
||||
static void maybe_emit_mod(u8 **pprog, u32 dst_reg, u32 src_reg, bool is64)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
if (is64)
|
||||
EMIT1(add_2mod(0x48, dst_reg, src_reg));
|
||||
else if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, src_reg));
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
/* LDX: dst_reg = *(u8*)(src_reg + off) */
|
||||
static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
{
|
||||
@ -708,15 +756,7 @@ static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If insn->off == 0 we can save one extra byte, but
|
||||
* special case of x86 R13 which always needs an offset
|
||||
* is not worth the hassle
|
||||
*/
|
||||
if (is_imm8(off))
|
||||
EMIT2(add_2reg(0x40, src_reg, dst_reg), off);
|
||||
else
|
||||
EMIT1_off32(add_2reg(0x80, src_reg, dst_reg), off);
|
||||
emit_insn_suffix(&prog, src_reg, dst_reg, off);
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
@ -751,13 +791,53 @@ static void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
|
||||
break;
|
||||
}
|
||||
if (is_imm8(off))
|
||||
EMIT2(add_2reg(0x40, dst_reg, src_reg), off);
|
||||
else
|
||||
EMIT1_off32(add_2reg(0x80, dst_reg, src_reg), off);
|
||||
emit_insn_suffix(&prog, dst_reg, src_reg, off);
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static int emit_atomic(u8 **pprog, u8 atomic_op,
|
||||
u32 dst_reg, u32 src_reg, s16 off, u8 bpf_size)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
EMIT1(0xF0); /* lock prefix */
|
||||
|
||||
maybe_emit_mod(&prog, dst_reg, src_reg, bpf_size == BPF_DW);
|
||||
|
||||
/* emit opcode */
|
||||
switch (atomic_op) {
|
||||
case BPF_ADD:
|
||||
case BPF_SUB:
|
||||
case BPF_AND:
|
||||
case BPF_OR:
|
||||
case BPF_XOR:
|
||||
/* lock *(u32/u64*)(dst_reg + off) <op>= src_reg */
|
||||
EMIT1(simple_alu_opcodes[atomic_op]);
|
||||
break;
|
||||
case BPF_ADD | BPF_FETCH:
|
||||
/* src_reg = atomic_fetch_add(dst_reg + off, src_reg); */
|
||||
EMIT2(0x0F, 0xC1);
|
||||
break;
|
||||
case BPF_XCHG:
|
||||
/* src_reg = atomic_xchg(dst_reg + off, src_reg); */
|
||||
EMIT1(0x87);
|
||||
break;
|
||||
case BPF_CMPXCHG:
|
||||
/* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */
|
||||
EMIT2(0x0F, 0xB1);
|
||||
break;
|
||||
default:
|
||||
pr_err("bpf_jit: unknown atomic opcode %02x\n", atomic_op);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
emit_insn_suffix(&prog, dst_reg, src_reg, off);
|
||||
|
||||
*pprog = prog;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ex_handler_bpf(const struct exception_table_entry *x,
|
||||
struct pt_regs *regs, int trapnr,
|
||||
unsigned long error_code, unsigned long fault_addr)
|
||||
@ -789,8 +869,31 @@ static void detect_reg_usage(struct bpf_insn *insn, int insn_cnt,
|
||||
}
|
||||
}
|
||||
|
||||
static int emit_nops(u8 **pprog, int len)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int i, noplen, cnt = 0;
|
||||
|
||||
while (len > 0) {
|
||||
noplen = len;
|
||||
|
||||
if (noplen > ASM_NOP_MAX)
|
||||
noplen = ASM_NOP_MAX;
|
||||
|
||||
for (i = 0; i < noplen; i++)
|
||||
EMIT1(ideal_nops[noplen][i]);
|
||||
len -= noplen;
|
||||
}
|
||||
|
||||
*pprog = prog;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp)))
|
||||
|
||||
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
int oldproglen, struct jit_context *ctx)
|
||||
int oldproglen, struct jit_context *ctx, bool jmp_padding)
|
||||
{
|
||||
bool tail_call_reachable = bpf_prog->aux->tail_call_reachable;
|
||||
struct bpf_insn *insn = bpf_prog->insnsi;
|
||||
@ -800,8 +903,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
bool seen_exit = false;
|
||||
u8 temp[BPF_MAX_INSN_SIZE + BPF_INSN_SAFETY];
|
||||
int i, cnt = 0, excnt = 0;
|
||||
int proglen = 0;
|
||||
int ilen, proglen = 0;
|
||||
u8 *prog = temp;
|
||||
int err;
|
||||
|
||||
detect_reg_usage(insn, insn_cnt, callee_regs_used,
|
||||
&tail_call_seen);
|
||||
@ -813,17 +917,24 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
bpf_prog_was_classic(bpf_prog), tail_call_reachable,
|
||||
bpf_prog->aux->func_idx != 0);
|
||||
push_callee_regs(&prog, callee_regs_used);
|
||||
addrs[0] = prog - temp;
|
||||
|
||||
ilen = prog - temp;
|
||||
if (image)
|
||||
memcpy(image + proglen, temp, ilen);
|
||||
proglen += ilen;
|
||||
addrs[0] = proglen;
|
||||
prog = temp;
|
||||
|
||||
for (i = 1; i <= insn_cnt; i++, insn++) {
|
||||
const s32 imm32 = insn->imm;
|
||||
u32 dst_reg = insn->dst_reg;
|
||||
u32 src_reg = insn->src_reg;
|
||||
u8 b2 = 0, b3 = 0;
|
||||
u8 *start_of_ldx;
|
||||
s64 jmp_offset;
|
||||
u8 jmp_cond;
|
||||
int ilen;
|
||||
u8 *func;
|
||||
int nops;
|
||||
|
||||
switch (insn->code) {
|
||||
/* ALU */
|
||||
@ -837,17 +948,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
case BPF_ALU64 | BPF_AND | BPF_X:
|
||||
case BPF_ALU64 | BPF_OR | BPF_X:
|
||||
case BPF_ALU64 | BPF_XOR | BPF_X:
|
||||
switch (BPF_OP(insn->code)) {
|
||||
case BPF_ADD: b2 = 0x01; break;
|
||||
case BPF_SUB: b2 = 0x29; break;
|
||||
case BPF_AND: b2 = 0x21; break;
|
||||
case BPF_OR: b2 = 0x09; break;
|
||||
case BPF_XOR: b2 = 0x31; break;
|
||||
}
|
||||
if (BPF_CLASS(insn->code) == BPF_ALU64)
|
||||
EMIT1(add_2mod(0x48, dst_reg, src_reg));
|
||||
else if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, src_reg));
|
||||
maybe_emit_mod(&prog, dst_reg, src_reg,
|
||||
BPF_CLASS(insn->code) == BPF_ALU64);
|
||||
b2 = simple_alu_opcodes[BPF_OP(insn->code)];
|
||||
EMIT2(b2, add_2reg(0xC0, dst_reg, src_reg));
|
||||
break;
|
||||
|
||||
@ -1027,12 +1130,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
else if (is_ereg(dst_reg))
|
||||
EMIT1(add_1mod(0x40, dst_reg));
|
||||
|
||||
switch (BPF_OP(insn->code)) {
|
||||
case BPF_LSH: b3 = 0xE0; break;
|
||||
case BPF_RSH: b3 = 0xE8; break;
|
||||
case BPF_ARSH: b3 = 0xF8; break;
|
||||
}
|
||||
|
||||
b3 = simple_alu_opcodes[BPF_OP(insn->code)];
|
||||
if (imm32 == 1)
|
||||
EMIT2(0xD1, add_1reg(b3, dst_reg));
|
||||
else
|
||||
@ -1066,11 +1164,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
else if (is_ereg(dst_reg))
|
||||
EMIT1(add_1mod(0x40, dst_reg));
|
||||
|
||||
switch (BPF_OP(insn->code)) {
|
||||
case BPF_LSH: b3 = 0xE0; break;
|
||||
case BPF_RSH: b3 = 0xE8; break;
|
||||
case BPF_ARSH: b3 = 0xF8; break;
|
||||
}
|
||||
b3 = simple_alu_opcodes[BPF_OP(insn->code)];
|
||||
EMIT2(0xD3, add_1reg(b3, dst_reg));
|
||||
|
||||
if (src_reg != BPF_REG_4)
|
||||
@ -1185,12 +1279,30 @@ st: if (is_imm8(insn->off))
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
|
||||
case BPF_LDX | BPF_MEM | BPF_DW:
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
|
||||
if (BPF_MODE(insn->code) == BPF_PROBE_MEM) {
|
||||
/* test src_reg, src_reg */
|
||||
maybe_emit_mod(&prog, src_reg, src_reg, true); /* always 1 byte */
|
||||
EMIT2(0x85, add_2reg(0xC0, src_reg, src_reg));
|
||||
/* jne start_of_ldx */
|
||||
EMIT2(X86_JNE, 0);
|
||||
/* xor dst_reg, dst_reg */
|
||||
emit_mov_imm32(&prog, false, dst_reg, 0);
|
||||
/* jmp byte_after_ldx */
|
||||
EMIT2(0xEB, 0);
|
||||
|
||||
/* populate jmp_offset for JNE above */
|
||||
temp[4] = prog - temp - 5 /* sizeof(test + jne) */;
|
||||
start_of_ldx = prog;
|
||||
}
|
||||
emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
|
||||
if (BPF_MODE(insn->code) == BPF_PROBE_MEM) {
|
||||
struct exception_table_entry *ex;
|
||||
u8 *_insn = image + proglen;
|
||||
s64 delta;
|
||||
|
||||
/* populate jmp_offset for JMP above */
|
||||
start_of_ldx[-1] = prog - start_of_ldx;
|
||||
|
||||
if (!bpf_prog->aux->extable)
|
||||
break;
|
||||
|
||||
@ -1230,21 +1342,56 @@ st: if (is_imm8(insn->off))
|
||||
}
|
||||
break;
|
||||
|
||||
/* STX XADD: lock *(u32*)(dst_reg + off) += src_reg */
|
||||
case BPF_STX | BPF_XADD | BPF_W:
|
||||
/* Emit 'lock add dword ptr [rax + off], eax' */
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT3(0xF0, add_2mod(0x40, dst_reg, src_reg), 0x01);
|
||||
else
|
||||
EMIT2(0xF0, 0x01);
|
||||
goto xadd;
|
||||
case BPF_STX | BPF_XADD | BPF_DW:
|
||||
EMIT3(0xF0, add_2mod(0x48, dst_reg, src_reg), 0x01);
|
||||
xadd: if (is_imm8(insn->off))
|
||||
EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
|
||||
else
|
||||
EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
|
||||
insn->off);
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
if (insn->imm == (BPF_AND | BPF_FETCH) ||
|
||||
insn->imm == (BPF_OR | BPF_FETCH) ||
|
||||
insn->imm == (BPF_XOR | BPF_FETCH)) {
|
||||
u8 *branch_target;
|
||||
bool is64 = BPF_SIZE(insn->code) == BPF_DW;
|
||||
|
||||
/*
|
||||
* Can't be implemented with a single x86 insn.
|
||||
* Need to do a CMPXCHG loop.
|
||||
*/
|
||||
|
||||
/* Will need RAX as a CMPXCHG operand so save R0 */
|
||||
emit_mov_reg(&prog, true, BPF_REG_AX, BPF_REG_0);
|
||||
branch_target = prog;
|
||||
/* Load old value */
|
||||
emit_ldx(&prog, BPF_SIZE(insn->code),
|
||||
BPF_REG_0, dst_reg, insn->off);
|
||||
/*
|
||||
* Perform the (commutative) operation locally,
|
||||
* put the result in the AUX_REG.
|
||||
*/
|
||||
emit_mov_reg(&prog, is64, AUX_REG, BPF_REG_0);
|
||||
maybe_emit_mod(&prog, AUX_REG, src_reg, is64);
|
||||
EMIT2(simple_alu_opcodes[BPF_OP(insn->imm)],
|
||||
add_2reg(0xC0, AUX_REG, src_reg));
|
||||
/* Attempt to swap in new value */
|
||||
err = emit_atomic(&prog, BPF_CMPXCHG,
|
||||
dst_reg, AUX_REG, insn->off,
|
||||
BPF_SIZE(insn->code));
|
||||
if (WARN_ON(err))
|
||||
return err;
|
||||
/*
|
||||
* ZF tells us whether we won the race. If it's
|
||||
* cleared we need to try again.
|
||||
*/
|
||||
EMIT2(X86_JNE, -(prog - branch_target) - 2);
|
||||
/* Return the pre-modification value */
|
||||
emit_mov_reg(&prog, is64, src_reg, BPF_REG_0);
|
||||
/* Restore R0 after clobbering RAX */
|
||||
emit_mov_reg(&prog, true, BPF_REG_0, BPF_REG_AX);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
err = emit_atomic(&prog, insn->imm, dst_reg, src_reg,
|
||||
insn->off, BPF_SIZE(insn->code));
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
|
||||
/* call */
|
||||
@ -1295,20 +1442,16 @@ xadd: if (is_imm8(insn->off))
|
||||
case BPF_JMP32 | BPF_JSGE | BPF_X:
|
||||
case BPF_JMP32 | BPF_JSLE | BPF_X:
|
||||
/* cmp dst_reg, src_reg */
|
||||
if (BPF_CLASS(insn->code) == BPF_JMP)
|
||||
EMIT1(add_2mod(0x48, dst_reg, src_reg));
|
||||
else if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, src_reg));
|
||||
maybe_emit_mod(&prog, dst_reg, src_reg,
|
||||
BPF_CLASS(insn->code) == BPF_JMP);
|
||||
EMIT2(0x39, add_2reg(0xC0, dst_reg, src_reg));
|
||||
goto emit_cond_jmp;
|
||||
|
||||
case BPF_JMP | BPF_JSET | BPF_X:
|
||||
case BPF_JMP32 | BPF_JSET | BPF_X:
|
||||
/* test dst_reg, src_reg */
|
||||
if (BPF_CLASS(insn->code) == BPF_JMP)
|
||||
EMIT1(add_2mod(0x48, dst_reg, src_reg));
|
||||
else if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, src_reg));
|
||||
maybe_emit_mod(&prog, dst_reg, src_reg,
|
||||
BPF_CLASS(insn->code) == BPF_JMP);
|
||||
EMIT2(0x85, add_2reg(0xC0, dst_reg, src_reg));
|
||||
goto emit_cond_jmp;
|
||||
|
||||
@ -1344,10 +1487,8 @@ xadd: if (is_imm8(insn->off))
|
||||
case BPF_JMP32 | BPF_JSLE | BPF_K:
|
||||
/* test dst_reg, dst_reg to save one extra byte */
|
||||
if (imm32 == 0) {
|
||||
if (BPF_CLASS(insn->code) == BPF_JMP)
|
||||
EMIT1(add_2mod(0x48, dst_reg, dst_reg));
|
||||
else if (is_ereg(dst_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, dst_reg));
|
||||
maybe_emit_mod(&prog, dst_reg, dst_reg,
|
||||
BPF_CLASS(insn->code) == BPF_JMP);
|
||||
EMIT2(0x85, add_2reg(0xC0, dst_reg, dst_reg));
|
||||
goto emit_cond_jmp;
|
||||
}
|
||||
@ -1409,6 +1550,30 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
|
||||
}
|
||||
jmp_offset = addrs[i + insn->off] - addrs[i];
|
||||
if (is_imm8(jmp_offset)) {
|
||||
if (jmp_padding) {
|
||||
/* To keep the jmp_offset valid, the extra bytes are
|
||||
* padded before the jump insn, so we substract the
|
||||
* 2 bytes of jmp_cond insn from INSN_SZ_DIFF.
|
||||
*
|
||||
* If the previous pass already emits an imm8
|
||||
* jmp_cond, then this BPF insn won't shrink, so
|
||||
* "nops" is 0.
|
||||
*
|
||||
* On the other hand, if the previous pass emits an
|
||||
* imm32 jmp_cond, the extra 4 bytes(*) is padded to
|
||||
* keep the image from shrinking further.
|
||||
*
|
||||
* (*) imm32 jmp_cond is 6 bytes, and imm8 jmp_cond
|
||||
* is 2 bytes, so the size difference is 4 bytes.
|
||||
*/
|
||||
nops = INSN_SZ_DIFF - 2;
|
||||
if (nops != 0 && nops != 4) {
|
||||
pr_err("unexpected jmp_cond padding: %d bytes\n",
|
||||
nops);
|
||||
return -EFAULT;
|
||||
}
|
||||
cnt += emit_nops(&prog, nops);
|
||||
}
|
||||
EMIT2(jmp_cond, jmp_offset);
|
||||
} else if (is_simm32(jmp_offset)) {
|
||||
EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset);
|
||||
@ -1431,11 +1596,55 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
|
||||
else
|
||||
jmp_offset = addrs[i + insn->off] - addrs[i];
|
||||
|
||||
if (!jmp_offset)
|
||||
/* Optimize out nop jumps */
|
||||
if (!jmp_offset) {
|
||||
/*
|
||||
* If jmp_padding is enabled, the extra nops will
|
||||
* be inserted. Otherwise, optimize out nop jumps.
|
||||
*/
|
||||
if (jmp_padding) {
|
||||
/* There are 3 possible conditions.
|
||||
* (1) This BPF_JA is already optimized out in
|
||||
* the previous run, so there is no need
|
||||
* to pad any extra byte (0 byte).
|
||||
* (2) The previous pass emits an imm8 jmp,
|
||||
* so we pad 2 bytes to match the previous
|
||||
* insn size.
|
||||
* (3) Similarly, the previous pass emits an
|
||||
* imm32 jmp, and 5 bytes is padded.
|
||||
*/
|
||||
nops = INSN_SZ_DIFF;
|
||||
if (nops != 0 && nops != 2 && nops != 5) {
|
||||
pr_err("unexpected nop jump padding: %d bytes\n",
|
||||
nops);
|
||||
return -EFAULT;
|
||||
}
|
||||
cnt += emit_nops(&prog, nops);
|
||||
}
|
||||
break;
|
||||
}
|
||||
emit_jmp:
|
||||
if (is_imm8(jmp_offset)) {
|
||||
if (jmp_padding) {
|
||||
/* To avoid breaking jmp_offset, the extra bytes
|
||||
* are padded before the actual jmp insn, so
|
||||
* 2 bytes is substracted from INSN_SZ_DIFF.
|
||||
*
|
||||
* If the previous pass already emits an imm8
|
||||
* jmp, there is nothing to pad (0 byte).
|
||||
*
|
||||
* If it emits an imm32 jmp (5 bytes) previously
|
||||
* and now an imm8 jmp (2 bytes), then we pad
|
||||
* (5 - 2 = 3) bytes to stop the image from
|
||||
* shrinking further.
|
||||
*/
|
||||
nops = INSN_SZ_DIFF - 2;
|
||||
if (nops != 0 && nops != 3) {
|
||||
pr_err("unexpected jump padding: %d bytes\n",
|
||||
nops);
|
||||
return -EFAULT;
|
||||
}
|
||||
cnt += emit_nops(&prog, INSN_SZ_DIFF - 2);
|
||||
}
|
||||
EMIT2(0xEB, jmp_offset);
|
||||
} else if (is_simm32(jmp_offset)) {
|
||||
EMIT1_off32(0xE9, jmp_offset);
|
||||
@ -1531,17 +1740,25 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
|
||||
struct bpf_prog *p, int stack_size, bool mod_ret)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
u8 *jmp_insn;
|
||||
int cnt = 0;
|
||||
|
||||
if (p->aux->sleepable) {
|
||||
if (emit_call(&prog, __bpf_prog_enter_sleepable, prog))
|
||||
/* arg1: mov rdi, progs[i] */
|
||||
emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, (u32) (long) p);
|
||||
if (emit_call(&prog,
|
||||
p->aux->sleepable ? __bpf_prog_enter_sleepable :
|
||||
__bpf_prog_enter, prog))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (emit_call(&prog, __bpf_prog_enter, prog))
|
||||
return -EINVAL;
|
||||
/* remember prog start time returned by __bpf_prog_enter */
|
||||
emit_mov_reg(&prog, true, BPF_REG_6, BPF_REG_0);
|
||||
}
|
||||
/* remember prog start time returned by __bpf_prog_enter */
|
||||
emit_mov_reg(&prog, true, BPF_REG_6, BPF_REG_0);
|
||||
|
||||
/* if (__bpf_prog_enter*(prog) == 0)
|
||||
* goto skip_exec_of_prog;
|
||||
*/
|
||||
EMIT3(0x48, 0x85, 0xC0); /* test rax,rax */
|
||||
/* emit 2 nops that will be replaced with JE insn */
|
||||
jmp_insn = prog;
|
||||
emit_nops(&prog, 2);
|
||||
|
||||
/* arg1: lea rdi, [rbp - stack_size] */
|
||||
EMIT4(0x48, 0x8D, 0x7D, -stack_size);
|
||||
@ -1561,43 +1778,23 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
|
||||
if (mod_ret)
|
||||
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
|
||||
|
||||
if (p->aux->sleepable) {
|
||||
if (emit_call(&prog, __bpf_prog_exit_sleepable, prog))
|
||||
/* replace 2 nops with JE insn, since jmp target is known */
|
||||
jmp_insn[0] = X86_JE;
|
||||
jmp_insn[1] = prog - jmp_insn - 2;
|
||||
|
||||
/* arg1: mov rdi, progs[i] */
|
||||
emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, (u32) (long) p);
|
||||
/* arg2: mov rsi, rbx <- start time in nsec */
|
||||
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6);
|
||||
if (emit_call(&prog,
|
||||
p->aux->sleepable ? __bpf_prog_exit_sleepable :
|
||||
__bpf_prog_exit, prog))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* arg1: mov rdi, progs[i] */
|
||||
emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32,
|
||||
(u32) (long) p);
|
||||
/* arg2: mov rsi, rbx <- start time in nsec */
|
||||
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6);
|
||||
if (emit_call(&prog, __bpf_prog_exit, prog))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*pprog = prog;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emit_nops(u8 **pprog, unsigned int len)
|
||||
{
|
||||
unsigned int i, noplen;
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
while (len > 0) {
|
||||
noplen = len;
|
||||
|
||||
if (noplen > ASM_NOP_MAX)
|
||||
noplen = ASM_NOP_MAX;
|
||||
|
||||
for (i = 0; i < noplen; i++)
|
||||
EMIT1(ideal_nops[noplen][i]);
|
||||
len -= noplen;
|
||||
}
|
||||
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_align(u8 **pprog, u32 align)
|
||||
{
|
||||
u8 *target, *prog = *pprog;
|
||||
@ -1972,6 +2169,9 @@ struct x64_jit_data {
|
||||
struct jit_context ctx;
|
||||
};
|
||||
|
||||
#define MAX_PASSES 20
|
||||
#define PADDING_PASSES (MAX_PASSES - 5)
|
||||
|
||||
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_binary_header *header = NULL;
|
||||
@ -1981,6 +2181,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
struct jit_context ctx = {};
|
||||
bool tmp_blinded = false;
|
||||
bool extra_pass = false;
|
||||
bool padding = false;
|
||||
u8 *image = NULL;
|
||||
int *addrs;
|
||||
int pass;
|
||||
@ -2017,6 +2218,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
image = jit_data->image;
|
||||
header = jit_data->header;
|
||||
extra_pass = true;
|
||||
padding = true;
|
||||
goto skip_init_addrs;
|
||||
}
|
||||
addrs = kmalloc_array(prog->len + 1, sizeof(*addrs), GFP_KERNEL);
|
||||
@ -2042,8 +2244,10 @@ skip_init_addrs:
|
||||
* may converge on the last pass. In such case do one more
|
||||
* pass to emit the final image.
|
||||
*/
|
||||
for (pass = 0; pass < 20 || image; pass++) {
|
||||
proglen = do_jit(prog, addrs, image, oldproglen, &ctx);
|
||||
for (pass = 0; pass < MAX_PASSES || image; pass++) {
|
||||
if (!padding && pass >= PADDING_PASSES)
|
||||
padding = true;
|
||||
proglen = do_jit(prog, addrs, image, oldproglen, &ctx, padding);
|
||||
if (proglen <= 0) {
|
||||
out_image:
|
||||
image = NULL;
|
||||
|
@ -2243,10 +2243,8 @@ emit_jmp:
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
/* STX XADD: lock *(u32 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_W:
|
||||
/* STX XADD: lock *(u64 *)(dst + off) += src */
|
||||
case BPF_STX | BPF_XADD | BPF_DW:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
goto notyet;
|
||||
case BPF_JMP | BPF_EXIT:
|
||||
if (seen_exit) {
|
||||
|
@ -3743,16 +3743,7 @@ static int __init idt77252_init(void)
|
||||
struct sk_buff *skb;
|
||||
|
||||
printk("%s: at %p\n", __func__, idt77252_init);
|
||||
|
||||
if (sizeof(skb->cb) < sizeof(struct atm_skb_data) +
|
||||
sizeof(struct idt77252_skb_prv)) {
|
||||
printk(KERN_ERR "%s: skb->cb is too small (%lu < %lu)\n",
|
||||
__func__, (unsigned long) sizeof(skb->cb),
|
||||
(unsigned long) sizeof(struct atm_skb_data) +
|
||||
sizeof(struct idt77252_skb_prv));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct idt77252_skb_prv) + sizeof(struct atm_skb_data));
|
||||
return pci_register_driver(&idt77252_driver);
|
||||
}
|
||||
|
||||
|
@ -789,7 +789,7 @@ struct idt77252_skb_prv {
|
||||
struct scqe tbd; /* Transmit Buffer Descriptor */
|
||||
dma_addr_t paddr; /* DMA handle */
|
||||
u32 pool; /* sb_pool handle */
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define IDT77252_PRV_TBD(skb) \
|
||||
(((struct idt77252_skb_prv *)(ATM_SKB(skb)+1))->tbd)
|
||||
|
@ -245,7 +245,7 @@ static int xen_blkif_map(struct xen_blkif_ring *ring, grant_ref_t *gref,
|
||||
if (req_prod - rsp_prod > size)
|
||||
goto fail;
|
||||
|
||||
err = bind_interdomain_evtchn_to_irqhandler_lateeoi(blkif->domid,
|
||||
err = bind_interdomain_evtchn_to_irqhandler_lateeoi(blkif->be->dev,
|
||||
evtchn, xen_blkif_be_int, 0, "blkif-backend", ring);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
@ -437,38 +437,31 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
|
||||
tlv = (struct intel_tlv *)skb->data;
|
||||
switch (tlv->type) {
|
||||
case INTEL_TLV_CNVI_TOP:
|
||||
version->cnvi_top =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
version->cnvi_top = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_CNVR_TOP:
|
||||
version->cnvr_top =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
version->cnvr_top = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_CNVI_BT:
|
||||
version->cnvi_bt =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
version->cnvi_bt = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_CNVR_BT:
|
||||
version->cnvr_bt =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
version->cnvr_bt = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_DEV_REV_ID:
|
||||
version->dev_rev_id =
|
||||
__le16_to_cpu(get_unaligned_le16(tlv->val));
|
||||
version->dev_rev_id = get_unaligned_le16(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_IMAGE_TYPE:
|
||||
version->img_type = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_TIME_STAMP:
|
||||
version->timestamp =
|
||||
__le16_to_cpu(get_unaligned_le16(tlv->val));
|
||||
version->timestamp = get_unaligned_le16(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_BUILD_TYPE:
|
||||
version->build_type = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_BUILD_NUM:
|
||||
version->build_num =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
version->build_num = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_SECURE_BOOT:
|
||||
version->secure_boot = tlv->val[0];
|
||||
|
@ -442,15 +442,15 @@ static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size)
|
||||
}
|
||||
|
||||
switch ((&pkts[i])->lsize) {
|
||||
case 1:
|
||||
dlen = skb->data[(&pkts[i])->loff];
|
||||
break;
|
||||
case 2:
|
||||
dlen = get_unaligned_le16(skb->data +
|
||||
case 1:
|
||||
dlen = skb->data[(&pkts[i])->loff];
|
||||
break;
|
||||
case 2:
|
||||
dlen = get_unaligned_le16(skb->data +
|
||||
(&pkts[i])->loff);
|
||||
break;
|
||||
default:
|
||||
goto err_kfree_skb;
|
||||
break;
|
||||
default:
|
||||
goto err_kfree_skb;
|
||||
}
|
||||
|
||||
pad_size = skb->len - (&pkts[i])->hlen - dlen;
|
||||
|
@ -94,6 +94,53 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca_read_soc_version);
|
||||
|
||||
static int qca_read_fw_build_info(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct edl_event_hdr *edl;
|
||||
char cmd, build_label[QCA_FW_BUILD_VER_LEN];
|
||||
int build_lbl_len, err = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "QCA read fw build info");
|
||||
|
||||
cmd = EDL_GET_BUILD_INFO_CMD;
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
|
||||
&cmd, 0, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "Reading QCA fw build info failed (%d)",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
edl = (struct edl_event_hdr *)(skb->data);
|
||||
if (!edl) {
|
||||
bt_dev_err(hdev, "QCA read fw build info with no header");
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
|
||||
edl->rtype != EDL_GET_BUILD_INFO_CMD) {
|
||||
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
|
||||
edl->rtype);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
build_lbl_len = edl->data[0];
|
||||
if (build_lbl_len <= QCA_FW_BUILD_VER_LEN - 1) {
|
||||
memcpy(build_label, edl->data + 1, build_lbl_len);
|
||||
*(build_label + build_lbl_len) = '\0';
|
||||
}
|
||||
|
||||
hci_set_fw_info(hdev, "%s", build_label);
|
||||
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int qca_send_reset(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
@ -517,6 +564,19 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* WCN399x supports the Microsoft vendor extension with 0xFD70 as the
|
||||
* VsMsftOpCode.
|
||||
*/
|
||||
switch (soc_type) {
|
||||
case QCA_WCN3990:
|
||||
case QCA_WCN3991:
|
||||
case QCA_WCN3998:
|
||||
hci_set_msft_opcode(hdev, 0xFD70);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Perform HCI reset */
|
||||
err = qca_send_reset(hdev);
|
||||
if (err < 0) {
|
||||
@ -524,6 +584,13 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (soc_type == QCA_WCN3991) {
|
||||
/* get fw build info */
|
||||
err = qca_read_fw_build_info(hdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "QCA setup on UART is completed");
|
||||
|
||||
return 0;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define EDL_PATCH_CMD_LEN (1)
|
||||
#define EDL_PATCH_VER_REQ_CMD (0x19)
|
||||
#define EDL_PATCH_TLV_REQ_CMD (0x1E)
|
||||
#define EDL_GET_BUILD_INFO_CMD (0x20)
|
||||
#define EDL_NVM_ACCESS_SET_REQ_CMD (0x01)
|
||||
#define MAX_SIZE_PER_TLV_SEGMENT (243)
|
||||
#define QCA_PRE_SHUTDOWN_CMD (0xFC08)
|
||||
|
@ -142,12 +142,16 @@ static int btqcomsmd_probe(struct platform_device *pdev)
|
||||
|
||||
btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
|
||||
btqcomsmd_cmd_callback, btq);
|
||||
if (IS_ERR(btq->cmd_channel))
|
||||
return PTR_ERR(btq->cmd_channel);
|
||||
if (IS_ERR(btq->cmd_channel)) {
|
||||
ret = PTR_ERR(btq->cmd_channel);
|
||||
goto destroy_acl_channel;
|
||||
}
|
||||
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev)
|
||||
return -ENOMEM;
|
||||
if (!hdev) {
|
||||
ret = -ENOMEM;
|
||||
goto destroy_cmd_channel;
|
||||
}
|
||||
|
||||
hci_set_drvdata(hdev, btq);
|
||||
btq->hdev = hdev;
|
||||
@ -161,14 +165,21 @@ static int btqcomsmd_probe(struct platform_device *pdev)
|
||||
hdev->set_bdaddr = qca_set_bdaddr_rome;
|
||||
|
||||
ret = hci_register_dev(hdev);
|
||||
if (ret < 0) {
|
||||
hci_free_dev(hdev);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto hci_free_dev;
|
||||
|
||||
platform_set_drvdata(pdev, btq);
|
||||
|
||||
return 0;
|
||||
|
||||
hci_free_dev:
|
||||
hci_free_dev(hdev);
|
||||
destroy_cmd_channel:
|
||||
rpmsg_destroy_ept(btq->cmd_channel);
|
||||
destroy_acl_channel:
|
||||
rpmsg_destroy_ept(btq->acl_channel);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btqcomsmd_remove(struct platform_device *pdev)
|
||||
|
@ -38,6 +38,19 @@
|
||||
.hci_ver = (hciv), \
|
||||
.hci_bus = (bus)
|
||||
|
||||
enum btrtl_chip_id {
|
||||
CHIP_ID_8723A,
|
||||
CHIP_ID_8723B,
|
||||
CHIP_ID_8821A,
|
||||
CHIP_ID_8761A,
|
||||
CHIP_ID_8822B = 8,
|
||||
CHIP_ID_8723D,
|
||||
CHIP_ID_8821C,
|
||||
CHIP_ID_8822C = 13,
|
||||
CHIP_ID_8761B,
|
||||
CHIP_ID_8852A = 18,
|
||||
};
|
||||
|
||||
struct id_table {
|
||||
__u16 match_flags;
|
||||
__u16 lmp_subver;
|
||||
@ -58,6 +71,7 @@ struct btrtl_device_info {
|
||||
u8 *cfg_data;
|
||||
int cfg_len;
|
||||
bool drop_fw;
|
||||
int project_id;
|
||||
};
|
||||
|
||||
static const struct id_table ic_id_table[] = {
|
||||
@ -307,8 +321,10 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
|
||||
/* Find project_id in table */
|
||||
for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
|
||||
if (project_id == project_id_to_lmp_subver[i].id)
|
||||
if (project_id == project_id_to_lmp_subver[i].id) {
|
||||
btrtl_dev->project_id = project_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
|
||||
@ -658,6 +674,12 @@ out_free:
|
||||
}
|
||||
}
|
||||
|
||||
/* RTL8822CE supports the Microsoft vendor extension and uses 0xFCF0
|
||||
* for VsMsftOpCode.
|
||||
*/
|
||||
if (lmp_subver == RTL_ROM_LMP_8822B)
|
||||
hci_set_msft_opcode(hdev, 0xFCF0);
|
||||
|
||||
return btrtl_dev;
|
||||
|
||||
err_free:
|
||||
@ -708,13 +730,28 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
|
||||
ret = btrtl_download_firmware(hdev, btrtl_dev);
|
||||
|
||||
btrtl_free(btrtl_dev);
|
||||
|
||||
/* Enable controller to do both LE scan and BR/EDR inquiry
|
||||
* simultaneously.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
|
||||
/* Enable central-peripheral role (able to create new connections with
|
||||
* an existing connection in slave role).
|
||||
*/
|
||||
/* Enable WBS supported for the specific Realtek devices. */
|
||||
switch (btrtl_dev->project_id) {
|
||||
case CHIP_ID_8822C:
|
||||
case CHIP_ID_8852A:
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
|
||||
break;
|
||||
default:
|
||||
rtl_dev_dbg(hdev, "Central-peripheral role not enabled.");
|
||||
rtl_dev_dbg(hdev, "WBS supported not enabled.");
|
||||
break;
|
||||
}
|
||||
|
||||
btrtl_free(btrtl_dev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
|
||||
|
@ -368,6 +368,8 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_NEWGEN |
|
||||
BTUSB_WIDEBAND_SPEECH},
|
||||
{ USB_DEVICE(0x8087, 0x0033), .driver_info = BTUSB_INTEL_NEWGEN |
|
||||
BTUSB_WIDEBAND_SPEECH},
|
||||
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
||||
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
|
||||
@ -506,7 +508,6 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {
|
||||
#define BTUSB_HW_RESET_ACTIVE 12
|
||||
#define BTUSB_TX_WAIT_VND_EVT 13
|
||||
#define BTUSB_WAKEUP_DISABLE 14
|
||||
#define BTUSB_USE_ALT1_FOR_WBS 15
|
||||
|
||||
struct btusb_data {
|
||||
struct hci_dev *hdev;
|
||||
@ -1736,15 +1737,12 @@ static void btusb_work(struct work_struct *work)
|
||||
new_alts = data->sco_num;
|
||||
}
|
||||
} else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) {
|
||||
/* Check if Alt 6 is supported for Transparent audio */
|
||||
if (btusb_find_altsetting(data, 6)) {
|
||||
data->usb_alt6_packet_flow = true;
|
||||
new_alts = 6;
|
||||
} else if (test_bit(BTUSB_USE_ALT1_FOR_WBS, &data->flags)) {
|
||||
new_alts = 1;
|
||||
} else {
|
||||
bt_dev_err(hdev, "Device does not support ALT setting 6");
|
||||
}
|
||||
/* Bluetooth USB spec recommends alt 6 (63 bytes), but
|
||||
* many adapters do not support it. Alt 1 appears to
|
||||
* work for all adapters that do not have alt 6, and
|
||||
* which work with WBS at all.
|
||||
*/
|
||||
new_alts = btusb_find_altsetting(data, 6) ? 6 : 1;
|
||||
}
|
||||
|
||||
if (btusb_switch_alt_setting(hdev, new_alts) < 0)
|
||||
@ -1903,7 +1901,7 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
||||
le16_to_cpu(rp->lmp_subver) == 0x1012 &&
|
||||
le16_to_cpu(rp->hci_rev) == 0x0810 &&
|
||||
le16_to_cpu(rp->hci_ver) == BLUETOOTH_VER_4_0) {
|
||||
bt_dev_warn(hdev, "CSR: detected a fake CSR dongle using a Barrot 8041a02 chip, this chip is very buggy and may have issues\n");
|
||||
bt_dev_warn(hdev, "CSR: detected a fake CSR dongle using a Barrot 8041a02 chip, this chip is very buggy and may have issues");
|
||||
|
||||
pm_runtime_allow(&data->udev->dev);
|
||||
|
||||
@ -1911,7 +1909,7 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
||||
if (ret >= 0)
|
||||
msleep(200);
|
||||
else
|
||||
bt_dev_err(hdev, "Failed to suspend the device for Barrot 8041a02 receive-issue workaround\n");
|
||||
bt_dev_err(hdev, "Failed to suspend the device for Barrot 8041a02 receive-issue workaround");
|
||||
|
||||
pm_runtime_forbid(&data->udev->dev);
|
||||
|
||||
@ -2924,7 +2922,10 @@ finish:
|
||||
* extension are using 0xFC1E for VsMsftOpCode.
|
||||
*/
|
||||
switch (ver.hw_variant) {
|
||||
case 0x11: /* JfP */
|
||||
case 0x12: /* ThP */
|
||||
case 0x13: /* HrP */
|
||||
case 0x14: /* CcP */
|
||||
hci_set_msft_opcode(hdev, 0xFC1E);
|
||||
break;
|
||||
}
|
||||
@ -3127,6 +3128,12 @@ static int btusb_shutdown_intel_new(struct hci_dev *hdev)
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
|
||||
#define HCI_WMT_MAX_EVENT_SIZE 64
|
||||
/* It is for mt79xx download rom patch*/
|
||||
#define MTK_FW_ROM_PATCH_HEADER_SIZE 32
|
||||
#define MTK_FW_ROM_PATCH_GD_SIZE 64
|
||||
#define MTK_FW_ROM_PATCH_SEC_MAP_SIZE 64
|
||||
#define MTK_SEC_MAP_COMMON_SIZE 12
|
||||
#define MTK_SEC_MAP_NEED_SEND_SIZE 52
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_PATCH_DWNLD = 0x1,
|
||||
@ -3138,6 +3145,7 @@ enum {
|
||||
enum {
|
||||
BTMTK_WMT_INVALID,
|
||||
BTMTK_WMT_PATCH_UNDONE,
|
||||
BTMTK_WMT_PATCH_PROGRESS,
|
||||
BTMTK_WMT_PATCH_DONE,
|
||||
BTMTK_WMT_ON_UNDONE,
|
||||
BTMTK_WMT_ON_DONE,
|
||||
@ -3153,7 +3161,7 @@ struct btmtk_wmt_hdr {
|
||||
|
||||
struct btmtk_hci_wmt_cmd {
|
||||
struct btmtk_wmt_hdr hdr;
|
||||
u8 data[256];
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt {
|
||||
@ -3182,6 +3190,40 @@ struct btmtk_hci_wmt_params {
|
||||
u32 *status;
|
||||
};
|
||||
|
||||
struct btmtk_patch_header {
|
||||
u8 datetime[16];
|
||||
u8 platform[4];
|
||||
__le16 hwver;
|
||||
__le16 swver;
|
||||
__le32 magicnum;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_global_desc {
|
||||
__le32 patch_ver;
|
||||
__le32 sub_sys;
|
||||
__le32 feature_opt;
|
||||
__le32 section_num;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_section_map {
|
||||
__le32 sectype;
|
||||
__le32 secoffset;
|
||||
__le32 secsize;
|
||||
union {
|
||||
__le32 u4SecSpec[13];
|
||||
struct {
|
||||
__le32 dlAddr;
|
||||
__le32 dlsize;
|
||||
__le32 seckeyidx;
|
||||
__le32 alignlen;
|
||||
__le32 sectype;
|
||||
__le32 dlmodecrctype;
|
||||
__le32 crc;
|
||||
__le32 reserved[6];
|
||||
} bin_info_spec;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
{
|
||||
struct hci_dev *hdev = urb->context;
|
||||
@ -3199,7 +3241,7 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
hdev->stat.err_rx++;
|
||||
goto err_out;
|
||||
return;
|
||||
}
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
|
||||
@ -3217,13 +3259,18 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
*/
|
||||
if (test_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags)) {
|
||||
data->evt_skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!data->evt_skb)
|
||||
goto err_out;
|
||||
if (!data->evt_skb) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
err = hci_recv_frame(hdev, skb);
|
||||
if (err < 0)
|
||||
goto err_free_skb;
|
||||
if (err < 0) {
|
||||
kfree_skb(data->evt_skb);
|
||||
data->evt_skb = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(BTUSB_TX_WAIT_VND_EVT,
|
||||
&data->flags)) {
|
||||
@ -3232,11 +3279,6 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
wake_up_bit(&data->flags,
|
||||
BTUSB_TX_WAIT_VND_EVT);
|
||||
}
|
||||
err_out:
|
||||
return;
|
||||
err_free_skb:
|
||||
kfree_skb(data->evt_skb);
|
||||
data->evt_skb = NULL;
|
||||
return;
|
||||
} else if (urb->status == -ENOENT) {
|
||||
/* Avoid suspend failed when usb_kill_urb */
|
||||
@ -3252,7 +3294,7 @@ err_free_skb:
|
||||
* to generate the event. Otherwise, the WMT event cannot return from
|
||||
* the device successfully.
|
||||
*/
|
||||
udelay(100);
|
||||
udelay(500);
|
||||
|
||||
usb_anchor_urb(urb, &data->ctrl_anchor);
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
@ -3327,7 +3369,7 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
|
||||
struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
|
||||
u32 hlen, status = BTMTK_WMT_INVALID;
|
||||
struct btmtk_hci_wmt_evt *wmt_evt;
|
||||
struct btmtk_hci_wmt_cmd wc;
|
||||
struct btmtk_hci_wmt_cmd *wc;
|
||||
struct btmtk_wmt_hdr *hdr;
|
||||
int err;
|
||||
|
||||
@ -3341,20 +3383,24 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
|
||||
if (hlen > 255)
|
||||
return -EINVAL;
|
||||
|
||||
hdr = (struct btmtk_wmt_hdr *)&wc;
|
||||
wc = kzalloc(hlen, GFP_KERNEL);
|
||||
if (!wc)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = &wc->hdr;
|
||||
hdr->dir = 1;
|
||||
hdr->op = wmt_params->op;
|
||||
hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
|
||||
hdr->flag = wmt_params->flag;
|
||||
memcpy(wc.data, wmt_params->data, wmt_params->dlen);
|
||||
memcpy(wc->data, wmt_params->data, wmt_params->dlen);
|
||||
|
||||
set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
|
||||
err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc);
|
||||
err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc);
|
||||
|
||||
if (err < 0) {
|
||||
clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
return err;
|
||||
goto err_free_wc;
|
||||
}
|
||||
|
||||
/* The vendor specific WMT commands are all answered by a vendor
|
||||
@ -3371,13 +3417,14 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
|
||||
if (err == -EINTR) {
|
||||
bt_dev_err(hdev, "Execution of wmt command interrupted");
|
||||
clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
return err;
|
||||
goto err_free_wc;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Execution of wmt command timed out");
|
||||
clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
return -ETIMEDOUT;
|
||||
err = -ETIMEDOUT;
|
||||
goto err_free_wc;
|
||||
}
|
||||
|
||||
/* Parse and handle the return WMT event */
|
||||
@ -3405,6 +3452,14 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
|
||||
else
|
||||
status = BTMTK_WMT_ON_UNDONE;
|
||||
break;
|
||||
case BTMTK_WMT_PATCH_DWNLD:
|
||||
if (wmt_evt->whdr.flag == 2)
|
||||
status = BTMTK_WMT_PATCH_DONE;
|
||||
else if (wmt_evt->whdr.flag == 1)
|
||||
status = BTMTK_WMT_PATCH_PROGRESS;
|
||||
else
|
||||
status = BTMTK_WMT_PATCH_UNDONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wmt_params->status)
|
||||
@ -3413,6 +3468,119 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
|
||||
err_free_skb:
|
||||
kfree_skb(data->evt_skb);
|
||||
data->evt_skb = NULL;
|
||||
err_free_wc:
|
||||
kfree(wc);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
struct btmtk_global_desc *globaldesc = NULL;
|
||||
struct btmtk_section_map *sectionmap;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
const u8 *fw_bin_ptr;
|
||||
int err, dlen, i, status;
|
||||
u8 flag, first_block, retry;
|
||||
u32 section_num, dl_size, section_offset;
|
||||
u8 cmd[64];
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_bin_ptr = fw_ptr;
|
||||
globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
|
||||
section_num = globaldesc->section_num;
|
||||
|
||||
for (i = 0; i < section_num; i++) {
|
||||
first_block = 1;
|
||||
fw_ptr = fw_bin_ptr;
|
||||
sectionmap = (struct btmtk_section_map *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
|
||||
MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
|
||||
|
||||
section_offset = sectionmap->secoffset;
|
||||
dl_size = sectionmap->bin_info_spec.dlsize;
|
||||
|
||||
if (dl_size > 0) {
|
||||
retry = 20;
|
||||
while (retry > 0) {
|
||||
cmd[0] = 0; /* 0 means legacy dl mode. */
|
||||
memcpy(cmd + 1,
|
||||
fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
|
||||
MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i +
|
||||
MTK_SEC_MAP_COMMON_SIZE,
|
||||
MTK_SEC_MAP_NEED_SEND_SIZE + 1);
|
||||
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = &status;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = MTK_SEC_MAP_NEED_SEND_SIZE + 1;
|
||||
wmt_params.data = &cmd;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
if (status == BTMTK_WMT_PATCH_UNDONE) {
|
||||
break;
|
||||
} else if (status == BTMTK_WMT_PATCH_PROGRESS) {
|
||||
msleep(100);
|
||||
retry--;
|
||||
} else if (status == BTMTK_WMT_PATCH_DONE) {
|
||||
goto next_section;
|
||||
} else {
|
||||
bt_dev_err(hdev, "Failed wmt patch dwnld status (%d)",
|
||||
status);
|
||||
goto err_release_fw;
|
||||
}
|
||||
}
|
||||
|
||||
fw_ptr += section_offset;
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
while (dl_size > 0) {
|
||||
dlen = min_t(int, 250, dl_size);
|
||||
if (first_block == 1) {
|
||||
flag = 1;
|
||||
first_block = 0;
|
||||
} else if (dl_size - dlen <= 0) {
|
||||
flag = 3;
|
||||
} else {
|
||||
flag = 2;
|
||||
}
|
||||
|
||||
wmt_params.flag = flag;
|
||||
wmt_params.dlen = dlen;
|
||||
wmt_params.data = fw_ptr;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
dl_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
}
|
||||
next_section:
|
||||
continue;
|
||||
}
|
||||
/* Wait a few moments for firmware activation done */
|
||||
usleep_range(100000, 120000);
|
||||
|
||||
err_release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -3465,7 +3633,7 @@ static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
|
||||
while (fw_size > 0) {
|
||||
dlen = min_t(int, 250, fw_size);
|
||||
|
||||
/* Tell deivice the position in sequence */
|
||||
/* Tell device the position in sequence */
|
||||
if (fw_size - dlen <= 0)
|
||||
flag = 3;
|
||||
else if (fw_size < fw->size - 30)
|
||||
@ -3555,9 +3723,9 @@ err_free_buf:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_id_get(struct btusb_data *data, u32 *id)
|
||||
static int btusb_mtk_id_get(struct btusb_data *data, u32 reg, u32 *id)
|
||||
{
|
||||
return btusb_mtk_reg_read(data, 0x80000008, id);
|
||||
return btusb_mtk_reg_read(data, reg, id);
|
||||
}
|
||||
|
||||
static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
@ -3571,16 +3739,31 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
const char *fwname;
|
||||
int err, status;
|
||||
u32 dev_id;
|
||||
char fw_bin_name[64];
|
||||
u32 fw_version;
|
||||
u8 param;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
err = btusb_mtk_id_get(data, &dev_id);
|
||||
err = btusb_mtk_id_get(data, 0x80000008, &dev_id);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!dev_id) {
|
||||
err = btusb_mtk_id_get(data, 0x70010200, &dev_id);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
err = btusb_mtk_id_get(data, 0x80021004, &fw_version);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get fw version (%d)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
switch (dev_id) {
|
||||
case 0x7663:
|
||||
fwname = FIRMWARE_MT7663;
|
||||
@ -3588,8 +3771,28 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
case 0x7668:
|
||||
fwname = FIRMWARE_MT7668;
|
||||
break;
|
||||
case 0x7961:
|
||||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
err = btusb_mtk_setup_firmware_79xx(hdev, fw_bin_name);
|
||||
|
||||
/* Enable Bluetooth protocol */
|
||||
param = 1;
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
|
||||
return err;
|
||||
}
|
||||
goto done;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported support hardware variant (%08x)",
|
||||
bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
|
||||
dev_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -3665,6 +3868,7 @@ ignore_func_on:
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
done:
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long)ktime_to_ns(delta) >> 10;
|
||||
@ -3725,7 +3929,7 @@ static int marvell_config_oob_wake(struct hci_dev *hdev)
|
||||
|
||||
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
|
||||
if (!skb) {
|
||||
bt_dev_err(hdev, "%s: No memory\n", __func__);
|
||||
bt_dev_err(hdev, "%s: No memory", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -3734,7 +3938,7 @@ static int marvell_config_oob_wake(struct hci_dev *hdev)
|
||||
|
||||
ret = btusb_send_frame(hdev, skb);
|
||||
if (ret) {
|
||||
bt_dev_err(hdev, "%s: configuration failed\n", __func__);
|
||||
bt_dev_err(hdev, "%s: configuration failed", __func__);
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
@ -4069,6 +4273,13 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
||||
info = &qca_devices_table[i];
|
||||
}
|
||||
if (!info) {
|
||||
/* If the rom_version is not matched in the qca_devices_table
|
||||
* and the high ROM version is not zero, we assume this chip no
|
||||
* need to load the rampatch and nvm.
|
||||
*/
|
||||
if (ver_rom & ~0xffffU)
|
||||
return 0;
|
||||
|
||||
bt_dev_err(hdev, "don't support firmware rome 0x%x", ver_rom);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -4264,6 +4475,20 @@ static bool btusb_prevent_wake(struct hci_dev *hdev)
|
||||
return !device_may_wakeup(&data->udev->dev);
|
||||
}
|
||||
|
||||
static int btusb_shutdown_qca(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "HCI reset during shutdown failed");
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
@ -4523,6 +4748,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
if (id->driver_info & BTUSB_QCA_WCN6855) {
|
||||
data->setup_on_usb = btusb_setup_qca;
|
||||
hdev->shutdown = btusb_shutdown_qca;
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_wcn6855;
|
||||
hdev->cmd_timeout = btusb_qca_cmd_timeout;
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
@ -4548,10 +4774,6 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
* (DEVICE_REMOTE_WAKEUP)
|
||||
*/
|
||||
set_bit(BTUSB_WAKEUP_DISABLE, &data->flags);
|
||||
if (btusb_find_altsetting(data, 1))
|
||||
set_bit(BTUSB_USE_ALT1_FOR_WBS, &data->flags);
|
||||
else
|
||||
bt_dev_err(hdev, "Device does not support ALT setting 1");
|
||||
}
|
||||
|
||||
if (!reset)
|
||||
@ -4627,8 +4849,8 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
data->diag = NULL;
|
||||
}
|
||||
|
||||
if (enable_autosuspend)
|
||||
usb_enable_autosuspend(data->udev);
|
||||
if (!enable_autosuspend)
|
||||
usb_disable_autosuspend(data->udev);
|
||||
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0)
|
||||
@ -4688,6 +4910,9 @@ static void btusb_disconnect(struct usb_interface *intf)
|
||||
gpiod_put(data->reset_gpio);
|
||||
|
||||
hci_free_dev(hdev);
|
||||
|
||||
if (!enable_autosuspend)
|
||||
usb_enable_autosuspend(data->udev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -654,6 +654,7 @@ static const struct h4_recv_pkt bcm_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
||||
{ H4_RECV_ISO, .recv = hci_recv_frame },
|
||||
{ BCM_RECV_LM_DIAG, .recv = hci_recv_diag },
|
||||
{ BCM_RECV_NULL, .recv = hci_recv_diag },
|
||||
{ BCM_RECV_TYPE49, .recv = hci_recv_diag },
|
||||
|
@ -906,6 +906,11 @@ static int h5_btrtl_setup(struct h5 *h5)
|
||||
/* Give the device some time before the hci-core sends it a reset */
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
/* Enable controller to do both LE scan and BR/EDR inquiry
|
||||
* simultaneously.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &h5->hu->hdev->quirks);
|
||||
|
||||
out_free:
|
||||
btrtl_free(btrtl_dev);
|
||||
|
||||
@ -1022,6 +1027,8 @@ static const struct of_device_id rtl_bluetooth_of_match[] = {
|
||||
.data = (const void *)&rtl_vnd },
|
||||
{ .compatible = "realtek,rtl8723bs-bt",
|
||||
.data = (const void *)&rtl_vnd },
|
||||
{ .compatible = "realtek,rtl8723ds-bt",
|
||||
.data = (const void *)&rtl_vnd },
|
||||
#endif
|
||||
{ },
|
||||
};
|
||||
|
@ -127,10 +127,9 @@ int hci_uart_tx_wakeup(struct hci_uart *hu)
|
||||
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
|
||||
goto no_schedule;
|
||||
|
||||
if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
|
||||
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
|
||||
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
|
||||
if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state))
|
||||
goto no_schedule;
|
||||
}
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
@ -174,10 +173,10 @@ restart:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
|
||||
goto restart;
|
||||
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
wake_up_bit(&hu->tx_state, HCI_UART_SENDING);
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,8 @@
|
||||
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
|
||||
#define CMD_TRANS_TIMEOUT_MS 100
|
||||
#define MEMDUMP_TIMEOUT_MS 8000
|
||||
#define IBS_DISABLE_SSR_TIMEOUT_MS (MEMDUMP_TIMEOUT_MS + 1000)
|
||||
#define IBS_DISABLE_SSR_TIMEOUT_MS \
|
||||
(MEMDUMP_TIMEOUT_MS + FW_DOWNLOAD_TIMEOUT_MS)
|
||||
#define FW_DOWNLOAD_TIMEOUT_MS 3000
|
||||
|
||||
/* susclk rate */
|
||||
@ -76,7 +77,8 @@ enum qca_flags {
|
||||
QCA_MEMDUMP_COLLECTION,
|
||||
QCA_HW_ERROR_EVENT,
|
||||
QCA_SSR_TRIGGERED,
|
||||
QCA_BT_OFF
|
||||
QCA_BT_OFF,
|
||||
QCA_ROM_FW
|
||||
};
|
||||
|
||||
enum qca_capabilities {
|
||||
@ -1024,7 +1026,9 @@ static void qca_controller_memdump(struct work_struct *work)
|
||||
dump_size = __le32_to_cpu(dump->dump_size);
|
||||
if (!(dump_size)) {
|
||||
bt_dev_err(hu->hdev, "Rx invalid memdump size");
|
||||
kfree(qca_memdump);
|
||||
kfree_skb(skb);
|
||||
qca->qca_memdump = NULL;
|
||||
mutex_unlock(&qca->hci_memdump_lock);
|
||||
return;
|
||||
}
|
||||
@ -1661,6 +1665,7 @@ static int qca_setup(struct hci_uart *hu)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clear_bit(QCA_ROM_FW, &qca->flags);
|
||||
/* Patch downloading has to be done without IBS mode */
|
||||
set_bit(QCA_IBS_DISABLED, &qca->flags);
|
||||
|
||||
@ -1718,12 +1723,14 @@ retry:
|
||||
hu->hdev->cmd_timeout = qca_cmd_timeout;
|
||||
} else if (ret == -ENOENT) {
|
||||
/* No patch/nvm-config found, run with original fw/config */
|
||||
set_bit(QCA_ROM_FW, &qca->flags);
|
||||
ret = 0;
|
||||
} else if (ret == -EAGAIN) {
|
||||
/*
|
||||
* Userspace firmware loader will return -EAGAIN in case no
|
||||
* patch/nvm-config is found, so run with original fw/config.
|
||||
*/
|
||||
set_bit(QCA_ROM_FW, &qca->flags);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@ -2100,17 +2107,29 @@ static int __maybe_unused qca_suspend(struct device *dev)
|
||||
|
||||
set_bit(QCA_SUSPENDING, &qca->flags);
|
||||
|
||||
if (test_bit(QCA_BT_OFF, &qca->flags))
|
||||
/* if BT SoC is running with default firmware then it does not
|
||||
* support in-band sleep
|
||||
*/
|
||||
if (test_bit(QCA_ROM_FW, &qca->flags))
|
||||
return 0;
|
||||
|
||||
if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
|
||||
/* During SSR after memory dump collection, controller will be
|
||||
* powered off and then powered on.If controller is powered off
|
||||
* during SSR then we should wait until SSR is completed.
|
||||
*/
|
||||
if (test_bit(QCA_BT_OFF, &qca->flags) &&
|
||||
!test_bit(QCA_SSR_TRIGGERED, &qca->flags))
|
||||
return 0;
|
||||
|
||||
if (test_bit(QCA_IBS_DISABLED, &qca->flags) ||
|
||||
test_bit(QCA_SSR_TRIGGERED, &qca->flags)) {
|
||||
wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ?
|
||||
IBS_DISABLE_SSR_TIMEOUT_MS :
|
||||
FW_DOWNLOAD_TIMEOUT_MS;
|
||||
|
||||
/* QCA_IBS_DISABLED flag is set to true, During FW download
|
||||
* and during memory dump collection. It is reset to false,
|
||||
* After FW download complete and after memory dump collections.
|
||||
* After FW download complete.
|
||||
*/
|
||||
wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED,
|
||||
TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout));
|
||||
@ -2122,10 +2141,6 @@ static int __maybe_unused qca_suspend(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
/* After memory dump collection, Controller is powered off.*/
|
||||
if (test_bit(QCA_BT_OFF, &qca->flags))
|
||||
return 0;
|
||||
|
||||
cancel_work_sync(&qca->ws_awake_device);
|
||||
cancel_work_sync(&qca->ws_awake_rx);
|
||||
|
||||
|
@ -83,9 +83,9 @@ static void hci_uart_write_work(struct work_struct *work)
|
||||
hci_uart_tx_complete(hu, hci_skb_pkt_type(skb));
|
||||
kfree_skb(skb);
|
||||
}
|
||||
} while (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state));
|
||||
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
} while (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state));
|
||||
}
|
||||
|
||||
/* ------- Interface to HCI layer ------ */
|
||||
|
@ -840,6 +840,15 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev)
|
||||
endpoint_desc.id = endpoint2.id;
|
||||
endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev);
|
||||
|
||||
/*
|
||||
* We know that the device has an endpoint because we verified by
|
||||
* interrogating the firmware. This is the case when the device was not
|
||||
* yet discovered by the fsl-mc bus, thus the lookup returned NULL.
|
||||
* Differentiate this case by returning EPROBE_DEFER.
|
||||
*/
|
||||
if (!endpoint)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_get_endpoint);
|
||||
|
@ -260,6 +260,18 @@ int mhi_destroy_device(struct device *dev, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mhi_get_free_desc_count(struct mhi_device *mhi_dev,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ?
|
||||
mhi_dev->ul_chan : mhi_dev->dl_chan;
|
||||
struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
|
||||
|
||||
return get_nr_avail_ring_elements(mhi_cntrl, tre_ring);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_get_free_desc_count);
|
||||
|
||||
void mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason)
|
||||
{
|
||||
struct mhi_driver *mhi_drv;
|
||||
|
@ -42,6 +42,7 @@ config BONDING
|
||||
tristate "Bonding driver support"
|
||||
depends on INET
|
||||
depends on IPV6 || IPV6=n
|
||||
depends on TLS || TLS_DEVICE=n
|
||||
help
|
||||
Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
|
||||
Channels together. This is called 'Etherchannel' by Cisco,
|
||||
|
@ -36,7 +36,7 @@ obj-$(CONFIG_GTP) += gtp.o
|
||||
obj-$(CONFIG_NLMON) += nlmon.o
|
||||
obj-$(CONFIG_NET_VRF) += vrf.o
|
||||
obj-$(CONFIG_VSOCKMON) += vsockmon.o
|
||||
obj-$(CONFIG_MHI_NET) += mhi_net.o
|
||||
obj-$(CONFIG_MHI_NET) += mhi/
|
||||
|
||||
#
|
||||
# Networking Drivers
|
||||
|
@ -427,9 +427,9 @@ out:
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static void arcnet_reply_tasklet(unsigned long data)
|
||||
static void arcnet_reply_tasklet(struct tasklet_struct *t)
|
||||
{
|
||||
struct arcnet_local *lp = (struct arcnet_local *)data;
|
||||
struct arcnet_local *lp = from_tasklet(lp, t, reply_tasklet);
|
||||
|
||||
struct sk_buff *ackskb, *skb;
|
||||
struct sock_exterr_skb *serr;
|
||||
@ -530,8 +530,7 @@ int arcnet_open(struct net_device *dev)
|
||||
arc_cont(D_PROTO, "\n");
|
||||
}
|
||||
|
||||
tasklet_init(&lp->reply_tasklet, arcnet_reply_tasklet,
|
||||
(unsigned long)lp);
|
||||
tasklet_setup(&lp->reply_tasklet, arcnet_reply_tasklet);
|
||||
|
||||
arc_printk(D_INIT, dev, "arcnet_open: resetting card.\n");
|
||||
|
||||
|
@ -67,7 +67,7 @@ static void regdump(struct net_device *dev)
|
||||
/* set up the address register */
|
||||
count = 0;
|
||||
arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag,
|
||||
ioaddr, com20020_REG_W_ADDR_HI);
|
||||
ioaddr, COM20020_REG_W_ADDR_HI);
|
||||
arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
|
||||
|
||||
for (count = 0; count < 256 + 32; count++) {
|
||||
|
@ -240,12 +240,6 @@ static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port)
|
||||
tunnel_cfg.encap_destroy = NULL;
|
||||
setup_udp_tunnel_sock(bareudp->net, sock, &tunnel_cfg);
|
||||
|
||||
/* As the setup_udp_tunnel_sock does not call udp_encap_enable if the
|
||||
* socket type is v6 an explicit call to udp_encap_enable is needed.
|
||||
*/
|
||||
if (sock->sk->sk_family == AF_INET6)
|
||||
udp_encap_enable();
|
||||
|
||||
rcu_assign_pointer(bareudp->sock, sock);
|
||||
return 0;
|
||||
}
|
||||
@ -532,11 +526,12 @@ static void bareudp_setup(struct net_device *dev)
|
||||
dev->netdev_ops = &bareudp_netdev_ops;
|
||||
dev->needs_free_netdev = true;
|
||||
SET_NETDEV_DEVTYPE(dev, &bareudp_type);
|
||||
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
|
||||
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
|
||||
dev->features |= NETIF_F_RXCSUM;
|
||||
dev->features |= NETIF_F_LLTX;
|
||||
dev->features |= NETIF_F_GSO_SOFTWARE;
|
||||
dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
|
||||
dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
|
||||
dev->hw_features |= NETIF_F_RXCSUM;
|
||||
dev->hw_features |= NETIF_F_GSO_SOFTWARE;
|
||||
dev->hard_header_len = 0;
|
||||
dev->addr_len = 0;
|
||||
@ -658,7 +653,6 @@ static int bareudp_newlink(struct net *net, struct net_device *dev,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct bareudp_conf conf;
|
||||
LIST_HEAD(list_kill);
|
||||
int err;
|
||||
|
||||
err = bareudp2info(data, &conf, extack);
|
||||
@ -676,8 +670,7 @@ static int bareudp_newlink(struct net *net, struct net_device *dev,
|
||||
return 0;
|
||||
|
||||
err_unconfig:
|
||||
bareudp_dellink(dev, &list_kill);
|
||||
unregister_netdevice_many(&list_kill);
|
||||
bareudp_dellink(dev, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -729,7 +722,6 @@ struct net_device *bareudp_dev_create(struct net *net, const char *name,
|
||||
{
|
||||
struct nlattr *tb[IFLA_MAX + 1];
|
||||
struct net_device *dev;
|
||||
LIST_HEAD(list_kill);
|
||||
int err;
|
||||
|
||||
memset(tb, 0, sizeof(tb));
|
||||
@ -753,8 +745,7 @@ struct net_device *bareudp_dev_create(struct net *net, const char *name,
|
||||
|
||||
return dev;
|
||||
err:
|
||||
bareudp_dellink(dev, &list_kill);
|
||||
unregister_netdevice_many(&list_kill);
|
||||
bareudp_dellink(dev, NULL);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bareudp_dev_create);
|
||||
|
@ -73,6 +73,8 @@ enum ad_link_speed_type {
|
||||
AD_LINK_SPEED_50000MBPS,
|
||||
AD_LINK_SPEED_56000MBPS,
|
||||
AD_LINK_SPEED_100000MBPS,
|
||||
AD_LINK_SPEED_200000MBPS,
|
||||
AD_LINK_SPEED_400000MBPS,
|
||||
};
|
||||
|
||||
/* compare MAC addresses */
|
||||
@ -245,6 +247,8 @@ static inline int __check_agg_selection_timer(struct port *port)
|
||||
* %AD_LINK_SPEED_50000MBPS
|
||||
* %AD_LINK_SPEED_56000MBPS
|
||||
* %AD_LINK_SPEED_100000MBPS
|
||||
* %AD_LINK_SPEED_200000MBPS
|
||||
* %AD_LINK_SPEED_400000MBPS
|
||||
*/
|
||||
static u16 __get_link_speed(struct port *port)
|
||||
{
|
||||
@ -312,13 +316,21 @@ static u16 __get_link_speed(struct port *port)
|
||||
speed = AD_LINK_SPEED_100000MBPS;
|
||||
break;
|
||||
|
||||
case SPEED_200000:
|
||||
speed = AD_LINK_SPEED_200000MBPS;
|
||||
break;
|
||||
|
||||
case SPEED_400000:
|
||||
speed = AD_LINK_SPEED_400000MBPS;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown speed value from ethtool. shouldn't happen */
|
||||
if (slave->speed != SPEED_UNKNOWN)
|
||||
pr_warn_once("%s: (slave %s): unknown ethtool speed (%d) for port %d (set it to 0)\n",
|
||||
slave->bond->dev->name,
|
||||
slave->dev->name, slave->speed,
|
||||
port->actor_port_number);
|
||||
pr_err_once("%s: (slave %s): unknown ethtool speed (%d) for port %d (set it to 0)\n",
|
||||
slave->bond->dev->name,
|
||||
slave->dev->name, slave->speed,
|
||||
port->actor_port_number);
|
||||
speed = 0;
|
||||
break;
|
||||
}
|
||||
@ -733,6 +745,12 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator)
|
||||
case AD_LINK_SPEED_100000MBPS:
|
||||
bandwidth = nports * 100000;
|
||||
break;
|
||||
case AD_LINK_SPEED_200000MBPS:
|
||||
bandwidth = nports * 200000;
|
||||
break;
|
||||
case AD_LINK_SPEED_400000MBPS:
|
||||
bandwidth = nports * 400000;
|
||||
break;
|
||||
default:
|
||||
bandwidth = 0; /* to silence the compiler */
|
||||
}
|
||||
|
@ -83,6 +83,9 @@
|
||||
#include <net/bonding.h>
|
||||
#include <net/bond_3ad.h>
|
||||
#include <net/bond_alb.h>
|
||||
#if IS_ENABLED(CONFIG_TLS_DEVICE)
|
||||
#include <net/tls.h>
|
||||
#endif
|
||||
|
||||
#include "bonding_priv.h"
|
||||
|
||||
@ -164,7 +167,7 @@ module_param(xmit_hash_policy, charp, 0);
|
||||
MODULE_PARM_DESC(xmit_hash_policy, "balance-alb, balance-tlb, balance-xor, 802.3ad hashing method; "
|
||||
"0 for layer 2 (default), 1 for layer 3+4, "
|
||||
"2 for layer 2+3, 3 for encap layer 2+3, "
|
||||
"4 for encap layer 3+4");
|
||||
"4 for encap layer 3+4, 5 for vlan+srcmac");
|
||||
module_param(arp_interval, int, 0);
|
||||
MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
|
||||
module_param_array(arp_ip_target, charp, NULL, 0);
|
||||
@ -301,6 +304,19 @@ netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
|
||||
return dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
bool bond_sk_check(struct bonding *bond)
|
||||
{
|
||||
switch (BOND_MODE(bond)) {
|
||||
case BOND_MODE_8023AD:
|
||||
case BOND_MODE_XOR:
|
||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34)
|
||||
return true;
|
||||
fallthrough;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------- VLAN -----------------------------------*/
|
||||
|
||||
/* In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid,
|
||||
@ -1212,6 +1228,13 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
|
||||
netdev_features_t mask;
|
||||
struct slave *slave;
|
||||
|
||||
#if IS_ENABLED(CONFIG_TLS_DEVICE)
|
||||
if (bond_sk_check(bond))
|
||||
features |= BOND_TLS_FEATURES;
|
||||
else
|
||||
features &= ~BOND_TLS_FEATURES;
|
||||
#endif
|
||||
|
||||
mask = features;
|
||||
|
||||
features &= ~NETIF_F_ONE_FOR_ALL;
|
||||
@ -1434,6 +1457,8 @@ static enum netdev_lag_hash bond_lag_hash_type(struct bonding *bond,
|
||||
return NETDEV_LAG_HASH_E23;
|
||||
case BOND_XMIT_POLICY_ENCAP34:
|
||||
return NETDEV_LAG_HASH_E34;
|
||||
case BOND_XMIT_POLICY_VLAN_SRCMAC:
|
||||
return NETDEV_LAG_HASH_VLAN_SRCMAC;
|
||||
default:
|
||||
return NETDEV_LAG_HASH_UNKNOWN;
|
||||
}
|
||||
@ -1922,6 +1947,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
bond_lower_state_changed(new_slave);
|
||||
|
||||
res = bond_sysfs_slave_add(new_slave);
|
||||
if (res) {
|
||||
slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res);
|
||||
@ -3494,6 +3521,27 @@ static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk,
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 bond_vlan_srcmac_hash(struct sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb);
|
||||
u32 srcmac_vendor = 0, srcmac_dev = 0;
|
||||
u16 vlan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
srcmac_vendor = (srcmac_vendor << 8) | mac_hdr->h_source[i];
|
||||
|
||||
for (i = 3; i < ETH_ALEN; i++)
|
||||
srcmac_dev = (srcmac_dev << 8) | mac_hdr->h_source[i];
|
||||
|
||||
if (!skb_vlan_tag_present(skb))
|
||||
return srcmac_vendor ^ srcmac_dev;
|
||||
|
||||
vlan = skb_vlan_tag_get(skb);
|
||||
|
||||
return vlan ^ srcmac_vendor ^ srcmac_dev;
|
||||
}
|
||||
|
||||
/* Extract the appropriate headers based on bond's xmit policy */
|
||||
static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
|
||||
struct flow_keys *fk)
|
||||
@ -3501,10 +3549,14 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
|
||||
bool l34 = bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34;
|
||||
int noff, proto = -1;
|
||||
|
||||
if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23) {
|
||||
switch (bond->params.xmit_policy) {
|
||||
case BOND_XMIT_POLICY_ENCAP23:
|
||||
case BOND_XMIT_POLICY_ENCAP34:
|
||||
memset(fk, 0, sizeof(*fk));
|
||||
return __skb_flow_dissect(NULL, skb, &flow_keys_bonding,
|
||||
fk, NULL, 0, 0, 0, 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fk->ports.ports = 0;
|
||||
@ -3539,6 +3591,16 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 bond_ip_hash(u32 hash, struct flow_keys *flow)
|
||||
{
|
||||
hash ^= (__force u32)flow_get_u32_dst(flow) ^
|
||||
(__force u32)flow_get_u32_src(flow);
|
||||
hash ^= (hash >> 16);
|
||||
hash ^= (hash >> 8);
|
||||
/* discard lowest hash bit to deal with the common even ports pattern */
|
||||
return hash >> 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* bond_xmit_hash - generate a hash value based on the xmit policy
|
||||
* @bond: bonding device
|
||||
@ -3556,6 +3618,9 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
|
||||
skb->l4_hash)
|
||||
return skb->hash;
|
||||
|
||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_VLAN_SRCMAC)
|
||||
return bond_vlan_srcmac_hash(skb);
|
||||
|
||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
|
||||
!bond_flow_dissect(bond, skb, &flow))
|
||||
return bond_eth_hash(skb);
|
||||
@ -3569,12 +3634,8 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
|
||||
else
|
||||
memcpy(&hash, &flow.ports.ports, sizeof(hash));
|
||||
}
|
||||
hash ^= (__force u32)flow_get_u32_dst(&flow) ^
|
||||
(__force u32)flow_get_u32_src(&flow);
|
||||
hash ^= (hash >> 16);
|
||||
hash ^= (hash >> 8);
|
||||
|
||||
return hash >> 1;
|
||||
return bond_ip_hash(hash, &flow);
|
||||
}
|
||||
|
||||
/*-------------------------- Device entry points ----------------------------*/
|
||||
@ -4547,6 +4608,95 @@ static struct net_device *bond_xmit_get_slave(struct net_device *master_dev,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bond_sk_to_flow(struct sock *sk, struct flow_keys *flow)
|
||||
{
|
||||
switch (sk->sk_family) {
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case AF_INET6:
|
||||
if (sk->sk_ipv6only ||
|
||||
ipv6_addr_type(&sk->sk_v6_daddr) != IPV6_ADDR_MAPPED) {
|
||||
flow->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
|
||||
flow->addrs.v6addrs.src = inet6_sk(sk)->saddr;
|
||||
flow->addrs.v6addrs.dst = sk->sk_v6_daddr;
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
#endif
|
||||
default: /* AF_INET */
|
||||
flow->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
|
||||
flow->addrs.v4addrs.src = inet_sk(sk)->inet_rcv_saddr;
|
||||
flow->addrs.v4addrs.dst = inet_sk(sk)->inet_daddr;
|
||||
break;
|
||||
}
|
||||
|
||||
flow->ports.src = inet_sk(sk)->inet_sport;
|
||||
flow->ports.dst = inet_sk(sk)->inet_dport;
|
||||
}
|
||||
|
||||
/**
|
||||
* bond_sk_hash_l34 - generate a hash value based on the socket's L3 and L4 fields
|
||||
* @sk: socket to use for headers
|
||||
*
|
||||
* This function will extract the necessary field from the socket and use
|
||||
* them to generate a hash based on the LAYER34 xmit_policy.
|
||||
* Assumes that sk is a TCP or UDP socket.
|
||||
*/
|
||||
static u32 bond_sk_hash_l34(struct sock *sk)
|
||||
{
|
||||
struct flow_keys flow;
|
||||
u32 hash;
|
||||
|
||||
bond_sk_to_flow(sk, &flow);
|
||||
|
||||
/* L4 */
|
||||
memcpy(&hash, &flow.ports.ports, sizeof(hash));
|
||||
/* L3 */
|
||||
return bond_ip_hash(hash, &flow);
|
||||
}
|
||||
|
||||
static struct net_device *__bond_sk_get_lower_dev(struct bonding *bond,
|
||||
struct sock *sk)
|
||||
{
|
||||
struct bond_up_slave *slaves;
|
||||
struct slave *slave;
|
||||
unsigned int count;
|
||||
u32 hash;
|
||||
|
||||
slaves = rcu_dereference(bond->usable_slaves);
|
||||
count = slaves ? READ_ONCE(slaves->count) : 0;
|
||||
if (unlikely(!count))
|
||||
return NULL;
|
||||
|
||||
hash = bond_sk_hash_l34(sk);
|
||||
slave = slaves->arr[hash % count];
|
||||
|
||||
return slave->dev;
|
||||
}
|
||||
|
||||
static struct net_device *bond_sk_get_lower_dev(struct net_device *dev,
|
||||
struct sock *sk)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(dev);
|
||||
struct net_device *lower = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
if (bond_sk_check(bond))
|
||||
lower = __bond_sk_get_lower_dev(bond, sk);
|
||||
rcu_read_unlock();
|
||||
|
||||
return lower;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_TLS_DEVICE)
|
||||
static netdev_tx_t bond_tls_device_xmit(struct bonding *bond, struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
if (likely(bond_get_slave_by_dev(bond, tls_get_ctx(skb->sk)->netdev)))
|
||||
return bond_dev_queue_xmit(bond, skb, tls_get_ctx(skb->sk)->netdev);
|
||||
return bond_tx_drop(dev, skb);
|
||||
}
|
||||
#endif
|
||||
|
||||
static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(dev);
|
||||
@ -4555,6 +4705,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
|
||||
!bond_slave_override(bond, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
#if IS_ENABLED(CONFIG_TLS_DEVICE)
|
||||
if (skb->sk && tls_is_sk_tx_device_offloaded(skb->sk))
|
||||
return bond_tls_device_xmit(bond, skb, dev);
|
||||
#endif
|
||||
|
||||
switch (BOND_MODE(bond)) {
|
||||
case BOND_MODE_ROUNDROBIN:
|
||||
return bond_xmit_roundrobin(skb, dev);
|
||||
@ -4683,6 +4838,7 @@ static const struct net_device_ops bond_netdev_ops = {
|
||||
.ndo_fix_features = bond_fix_features,
|
||||
.ndo_features_check = passthru_features_check,
|
||||
.ndo_get_xmit_slave = bond_xmit_get_slave,
|
||||
.ndo_sk_get_lower_dev = bond_sk_get_lower_dev,
|
||||
};
|
||||
|
||||
static const struct device_type bond_type = {
|
||||
@ -4754,6 +4910,10 @@ void bond_setup(struct net_device *bond_dev)
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
|
||||
bond_dev->features |= BOND_XFRM_FEATURES;
|
||||
#endif /* CONFIG_XFRM_OFFLOAD */
|
||||
#if IS_ENABLED(CONFIG_TLS_DEVICE)
|
||||
if (bond_sk_check(bond))
|
||||
bond_dev->features |= BOND_TLS_FEATURES;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Destroy a bonding device.
|
||||
|
@ -96,12 +96,13 @@ static const struct bond_opt_value bond_pps_tbl[] = {
|
||||
};
|
||||
|
||||
static const struct bond_opt_value bond_xmit_hashtype_tbl[] = {
|
||||
{ "layer2", BOND_XMIT_POLICY_LAYER2, BOND_VALFLAG_DEFAULT},
|
||||
{ "layer3+4", BOND_XMIT_POLICY_LAYER34, 0},
|
||||
{ "layer2+3", BOND_XMIT_POLICY_LAYER23, 0},
|
||||
{ "encap2+3", BOND_XMIT_POLICY_ENCAP23, 0},
|
||||
{ "encap3+4", BOND_XMIT_POLICY_ENCAP34, 0},
|
||||
{ NULL, -1, 0},
|
||||
{ "layer2", BOND_XMIT_POLICY_LAYER2, BOND_VALFLAG_DEFAULT},
|
||||
{ "layer3+4", BOND_XMIT_POLICY_LAYER34, 0},
|
||||
{ "layer2+3", BOND_XMIT_POLICY_LAYER23, 0},
|
||||
{ "encap2+3", BOND_XMIT_POLICY_ENCAP23, 0},
|
||||
{ "encap3+4", BOND_XMIT_POLICY_ENCAP34, 0},
|
||||
{ "vlan+srcmac", BOND_XMIT_POLICY_VLAN_SRCMAC, 0},
|
||||
{ NULL, -1, 0},
|
||||
};
|
||||
|
||||
static const struct bond_opt_value bond_arp_validate_tbl[] = {
|
||||
@ -745,17 +746,30 @@ const struct bond_option *bond_opt_get(unsigned int option)
|
||||
return &bond_opts[option];
|
||||
}
|
||||
|
||||
static void bond_set_xfrm_features(struct net_device *bond_dev, u64 mode)
|
||||
static bool bond_set_xfrm_features(struct bonding *bond)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_XFRM_OFFLOAD))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (mode == BOND_MODE_ACTIVEBACKUP)
|
||||
bond_dev->wanted_features |= BOND_XFRM_FEATURES;
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
|
||||
bond->dev->wanted_features |= BOND_XFRM_FEATURES;
|
||||
else
|
||||
bond_dev->wanted_features &= ~BOND_XFRM_FEATURES;
|
||||
bond->dev->wanted_features &= ~BOND_XFRM_FEATURES;
|
||||
|
||||
netdev_update_features(bond_dev);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool bond_set_tls_features(struct bonding *bond)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_TLS_DEVICE))
|
||||
return false;
|
||||
|
||||
if (bond_sk_check(bond))
|
||||
bond->dev->wanted_features |= BOND_TLS_FEATURES;
|
||||
else
|
||||
bond->dev->wanted_features &= ~BOND_TLS_FEATURES;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int bond_option_mode_set(struct bonding *bond,
|
||||
@ -780,13 +794,20 @@ static int bond_option_mode_set(struct bonding *bond,
|
||||
if (newval->value == BOND_MODE_ALB)
|
||||
bond->params.tlb_dynamic_lb = 1;
|
||||
|
||||
if (bond->dev->reg_state == NETREG_REGISTERED)
|
||||
bond_set_xfrm_features(bond->dev, newval->value);
|
||||
|
||||
/* don't cache arp_validate between modes */
|
||||
bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
|
||||
bond->params.mode = newval->value;
|
||||
|
||||
if (bond->dev->reg_state == NETREG_REGISTERED) {
|
||||
bool update = false;
|
||||
|
||||
update |= bond_set_xfrm_features(bond);
|
||||
update |= bond_set_tls_features(bond);
|
||||
|
||||
if (update)
|
||||
netdev_update_features(bond->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1219,6 +1240,10 @@ static int bond_option_xmit_hash_policy_set(struct bonding *bond,
|
||||
newval->string, newval->value);
|
||||
bond->params.xmit_policy = newval->value;
|
||||
|
||||
if (bond->dev->reg_state == NETREG_REGISTERED)
|
||||
if (bond_set_tls_features(bond))
|
||||
netdev_update_features(bond->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -598,9 +598,9 @@ err:
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static void cfv_tx_release_tasklet(unsigned long drv)
|
||||
static void cfv_tx_release_tasklet(struct tasklet_struct *t)
|
||||
{
|
||||
struct cfv_info *cfv = (struct cfv_info *)drv;
|
||||
struct cfv_info *cfv = from_tasklet(cfv, t, tx_release_tasklet);
|
||||
cfv_release_used_buf(cfv->vq_tx);
|
||||
}
|
||||
|
||||
@ -716,9 +716,7 @@ static int cfv_probe(struct virtio_device *vdev)
|
||||
cfv->ctx.head = USHRT_MAX;
|
||||
netif_napi_add(netdev, &cfv->napi, cfv_rx_poll, CFV_DEFAULT_QUOTA);
|
||||
|
||||
tasklet_init(&cfv->tx_release_tasklet,
|
||||
cfv_tx_release_tasklet,
|
||||
(unsigned long)cfv);
|
||||
tasklet_setup(&cfv->tx_release_tasklet, cfv_tx_release_tasklet);
|
||||
|
||||
/* Carrier is off until netdevice is opened */
|
||||
netif_carrier_off(netdev);
|
||||
|
@ -7,12 +7,7 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o
|
||||
obj-$(CONFIG_CAN_VXCAN) += vxcan.o
|
||||
obj-$(CONFIG_CAN_SLCAN) += slcan.o
|
||||
|
||||
obj-$(CONFIG_CAN_DEV) += can-dev.o
|
||||
can-dev-y += dev.o
|
||||
can-dev-y += rx-offload.o
|
||||
|
||||
can-dev-$(CONFIG_CAN_LEDS) += led.o
|
||||
|
||||
obj-y += dev/
|
||||
obj-y += rcar/
|
||||
obj-y += spi/
|
||||
obj-y += usb/
|
||||
|
@ -484,7 +484,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
stats->tx_bytes += cf->len;
|
||||
|
||||
/* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */
|
||||
can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv));
|
||||
can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv), 0);
|
||||
|
||||
/*
|
||||
* we have to stop the queue and deliver all messages in case
|
||||
@ -856,7 +856,7 @@ static void at91_irq_tx(struct net_device *dev, u32 reg_sr)
|
||||
if (likely(reg_msr & AT91_MSR_MRDY &&
|
||||
~reg_msr & AT91_MSR_MABT)) {
|
||||
/* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */
|
||||
can_get_echo_skb(dev, mb - get_mb_tx_first(priv));
|
||||
can_get_echo_skb(dev, mb - get_mb_tx_first(priv), NULL);
|
||||
dev->stats.tx_packets++;
|
||||
can_led_event(dev, CAN_LED_EVENT_TX);
|
||||
}
|
||||
|
@ -476,7 +476,7 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
|
||||
*/
|
||||
c_can_setup_tx_object(dev, IF_TX, frame, idx);
|
||||
priv->dlc[idx] = frame->len;
|
||||
can_put_echo_skb(skb, dev, idx);
|
||||
can_put_echo_skb(skb, dev, idx, 0);
|
||||
|
||||
/* Update the active bits */
|
||||
atomic_add((1 << idx), &priv->tx_active);
|
||||
@ -733,7 +733,7 @@ static void c_can_do_tx(struct net_device *dev)
|
||||
pend &= ~(1 << idx);
|
||||
obj = idx + C_CAN_MSG_OBJ_TX_FIRST;
|
||||
c_can_inval_tx_object(dev, IF_RX, obj);
|
||||
can_get_echo_skb(dev, idx);
|
||||
can_get_echo_skb(dev, idx, NULL);
|
||||
bytes += priv->dlc[idx];
|
||||
pkts++;
|
||||
}
|
||||
|
@ -702,8 +702,8 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o)
|
||||
stats->tx_bytes += cf->len;
|
||||
stats->tx_packets++;
|
||||
|
||||
can_put_echo_skb(priv->tx_skb, dev, 0);
|
||||
can_get_echo_skb(dev, 0);
|
||||
can_put_echo_skb(priv->tx_skb, dev, 0, 0);
|
||||
can_get_echo_skb(dev, 0, NULL);
|
||||
priv->tx_skb = NULL;
|
||||
|
||||
netif_wake_queue(dev);
|
||||
|
File diff suppressed because it is too large
Load Diff
11
drivers/net/can/dev/Makefile
Normal file
11
drivers/net/can/dev/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_CAN_DEV) += can-dev.o
|
||||
can-dev-y += bittiming.o
|
||||
can-dev-y += dev.o
|
||||
can-dev-y += length.o
|
||||
can-dev-y += netlink.o
|
||||
can-dev-y += rx-offload.o
|
||||
can-dev-y += skb.o
|
||||
|
||||
can-dev-$(CONFIG_CAN_LEDS) += led.o
|
261
drivers/net/can/dev/bittiming.c
Normal file
261
drivers/net/can/dev/bittiming.c
Normal file
@ -0,0 +1,261 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
|
||||
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
|
||||
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*/
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
|
||||
#ifdef CONFIG_CAN_CALC_BITTIMING
|
||||
#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
|
||||
|
||||
/* Bit-timing calculation derived from:
|
||||
*
|
||||
* Code based on LinCAN sources and H8S2638 project
|
||||
* Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
|
||||
* Copyright 2005 Stanislav Marek
|
||||
* email: pisa@cmp.felk.cvut.cz
|
||||
*
|
||||
* Calculates proper bit-timing parameters for a specified bit-rate
|
||||
* and sample-point, which can then be used to set the bit-timing
|
||||
* registers of the CAN controller. You can find more information
|
||||
* in the header file linux/can/netlink.h.
|
||||
*/
|
||||
static int
|
||||
can_update_sample_point(const struct can_bittiming_const *btc,
|
||||
unsigned int sample_point_nominal, unsigned int tseg,
|
||||
unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
|
||||
unsigned int *sample_point_error_ptr)
|
||||
{
|
||||
unsigned int sample_point_error, best_sample_point_error = UINT_MAX;
|
||||
unsigned int sample_point, best_sample_point = 0;
|
||||
unsigned int tseg1, tseg2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= 1; i++) {
|
||||
tseg2 = tseg + CAN_SYNC_SEG -
|
||||
(sample_point_nominal * (tseg + CAN_SYNC_SEG)) /
|
||||
1000 - i;
|
||||
tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
|
||||
tseg1 = tseg - tseg2;
|
||||
if (tseg1 > btc->tseg1_max) {
|
||||
tseg1 = btc->tseg1_max;
|
||||
tseg2 = tseg - tseg1;
|
||||
}
|
||||
|
||||
sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) /
|
||||
(tseg + CAN_SYNC_SEG);
|
||||
sample_point_error = abs(sample_point_nominal - sample_point);
|
||||
|
||||
if (sample_point <= sample_point_nominal &&
|
||||
sample_point_error < best_sample_point_error) {
|
||||
best_sample_point = sample_point;
|
||||
best_sample_point_error = sample_point_error;
|
||||
*tseg1_ptr = tseg1;
|
||||
*tseg2_ptr = tseg2;
|
||||
}
|
||||
}
|
||||
|
||||
if (sample_point_error_ptr)
|
||||
*sample_point_error_ptr = best_sample_point_error;
|
||||
|
||||
return best_sample_point;
|
||||
}
|
||||
|
||||
int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
|
||||
const struct can_bittiming_const *btc)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
unsigned int bitrate; /* current bitrate */
|
||||
unsigned int bitrate_error; /* difference between current and nominal value */
|
||||
unsigned int best_bitrate_error = UINT_MAX;
|
||||
unsigned int sample_point_error; /* difference between current and nominal value */
|
||||
unsigned int best_sample_point_error = UINT_MAX;
|
||||
unsigned int sample_point_nominal; /* nominal sample point */
|
||||
unsigned int best_tseg = 0; /* current best value for tseg */
|
||||
unsigned int best_brp = 0; /* current best value for brp */
|
||||
unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
|
||||
u64 v64;
|
||||
|
||||
/* Use CiA recommended sample points */
|
||||
if (bt->sample_point) {
|
||||
sample_point_nominal = bt->sample_point;
|
||||
} else {
|
||||
if (bt->bitrate > 800000)
|
||||
sample_point_nominal = 750;
|
||||
else if (bt->bitrate > 500000)
|
||||
sample_point_nominal = 800;
|
||||
else
|
||||
sample_point_nominal = 875;
|
||||
}
|
||||
|
||||
/* tseg even = round down, odd = round up */
|
||||
for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
|
||||
tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
|
||||
tsegall = CAN_SYNC_SEG + tseg / 2;
|
||||
|
||||
/* Compute all possible tseg choices (tseg=tseg1+tseg2) */
|
||||
brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2;
|
||||
|
||||
/* choose brp step which is possible in system */
|
||||
brp = (brp / btc->brp_inc) * btc->brp_inc;
|
||||
if (brp < btc->brp_min || brp > btc->brp_max)
|
||||
continue;
|
||||
|
||||
bitrate = priv->clock.freq / (brp * tsegall);
|
||||
bitrate_error = abs(bt->bitrate - bitrate);
|
||||
|
||||
/* tseg brp biterror */
|
||||
if (bitrate_error > best_bitrate_error)
|
||||
continue;
|
||||
|
||||
/* reset sample point error if we have a better bitrate */
|
||||
if (bitrate_error < best_bitrate_error)
|
||||
best_sample_point_error = UINT_MAX;
|
||||
|
||||
can_update_sample_point(btc, sample_point_nominal, tseg / 2,
|
||||
&tseg1, &tseg2, &sample_point_error);
|
||||
if (sample_point_error > best_sample_point_error)
|
||||
continue;
|
||||
|
||||
best_sample_point_error = sample_point_error;
|
||||
best_bitrate_error = bitrate_error;
|
||||
best_tseg = tseg / 2;
|
||||
best_brp = brp;
|
||||
|
||||
if (bitrate_error == 0 && sample_point_error == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (best_bitrate_error) {
|
||||
/* Error in one-tenth of a percent */
|
||||
v64 = (u64)best_bitrate_error * 1000;
|
||||
do_div(v64, bt->bitrate);
|
||||
bitrate_error = (u32)v64;
|
||||
if (bitrate_error > CAN_CALC_MAX_ERROR) {
|
||||
netdev_err(dev,
|
||||
"bitrate error %d.%d%% too high\n",
|
||||
bitrate_error / 10, bitrate_error % 10);
|
||||
return -EDOM;
|
||||
}
|
||||
netdev_warn(dev, "bitrate error %d.%d%%\n",
|
||||
bitrate_error / 10, bitrate_error % 10);
|
||||
}
|
||||
|
||||
/* real sample point */
|
||||
bt->sample_point = can_update_sample_point(btc, sample_point_nominal,
|
||||
best_tseg, &tseg1, &tseg2,
|
||||
NULL);
|
||||
|
||||
v64 = (u64)best_brp * 1000 * 1000 * 1000;
|
||||
do_div(v64, priv->clock.freq);
|
||||
bt->tq = (u32)v64;
|
||||
bt->prop_seg = tseg1 / 2;
|
||||
bt->phase_seg1 = tseg1 - bt->prop_seg;
|
||||
bt->phase_seg2 = tseg2;
|
||||
|
||||
/* check for sjw user settings */
|
||||
if (!bt->sjw || !btc->sjw_max) {
|
||||
bt->sjw = 1;
|
||||
} else {
|
||||
/* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
|
||||
if (bt->sjw > btc->sjw_max)
|
||||
bt->sjw = btc->sjw_max;
|
||||
/* bt->sjw must not be higher than tseg2 */
|
||||
if (tseg2 < bt->sjw)
|
||||
bt->sjw = tseg2;
|
||||
}
|
||||
|
||||
bt->brp = best_brp;
|
||||
|
||||
/* real bitrate */
|
||||
bt->bitrate = priv->clock.freq /
|
||||
(bt->brp * (CAN_SYNC_SEG + tseg1 + tseg2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_CAN_CALC_BITTIMING */
|
||||
|
||||
/* Checks the validity of the specified bit-timing parameters prop_seg,
|
||||
* phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
|
||||
* prescaler value brp. You can find more information in the header
|
||||
* file linux/can/netlink.h.
|
||||
*/
|
||||
static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt,
|
||||
const struct can_bittiming_const *btc)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
int tseg1, alltseg;
|
||||
u64 brp64;
|
||||
|
||||
tseg1 = bt->prop_seg + bt->phase_seg1;
|
||||
if (!bt->sjw)
|
||||
bt->sjw = 1;
|
||||
if (bt->sjw > btc->sjw_max ||
|
||||
tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
|
||||
bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
|
||||
return -ERANGE;
|
||||
|
||||
brp64 = (u64)priv->clock.freq * (u64)bt->tq;
|
||||
if (btc->brp_inc > 1)
|
||||
do_div(brp64, btc->brp_inc);
|
||||
brp64 += 500000000UL - 1;
|
||||
do_div(brp64, 1000000000UL); /* the practicable BRP */
|
||||
if (btc->brp_inc > 1)
|
||||
brp64 *= btc->brp_inc;
|
||||
bt->brp = (u32)brp64;
|
||||
|
||||
if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
|
||||
return -EINVAL;
|
||||
|
||||
alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
|
||||
bt->bitrate = priv->clock.freq / (bt->brp * alltseg);
|
||||
bt->sample_point = ((tseg1 + 1) * 1000) / alltseg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Checks the validity of predefined bitrate settings */
|
||||
static int
|
||||
can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt,
|
||||
const u32 *bitrate_const,
|
||||
const unsigned int bitrate_const_cnt)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < bitrate_const_cnt; i++) {
|
||||
if (bt->bitrate == bitrate_const[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= priv->bitrate_const_cnt)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
|
||||
const struct can_bittiming_const *btc,
|
||||
const u32 *bitrate_const,
|
||||
const unsigned int bitrate_const_cnt)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Depending on the given can_bittiming parameter structure the CAN
|
||||
* timing parameters are calculated based on the provided bitrate OR
|
||||
* alternatively the CAN timing parameters (tq, prop_seg, etc.) are
|
||||
* provided directly which are then checked and fixed up.
|
||||
*/
|
||||
if (!bt->tq && bt->bitrate && btc)
|
||||
err = can_calc_bittiming(dev, bt, btc);
|
||||
else if (bt->tq && !bt->bitrate && btc)
|
||||
err = can_fixup_bittiming(dev, bt, btc);
|
||||
else if (!bt->tq && bt->bitrate && bitrate_const)
|
||||
err = can_validate_bitrate(dev, bt, bitrate_const,
|
||||
bitrate_const_cnt);
|
||||
else
|
||||
err = -EINVAL;
|
||||
|
||||
return err;
|
||||
}
|
468
drivers/net/can/dev/dev.c
Normal file
468
drivers/net/can/dev/dev.c
Normal file
@ -0,0 +1,468 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
|
||||
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
|
||||
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/can-ml.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/can/led.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define MOD_DESC "CAN device driver interface"
|
||||
|
||||
MODULE_DESCRIPTION(MOD_DESC);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
|
||||
|
||||
static void can_update_state_error_stats(struct net_device *dev,
|
||||
enum can_state new_state)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (new_state <= priv->state)
|
||||
return;
|
||||
|
||||
switch (new_state) {
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
priv->can_stats.error_warning++;
|
||||
break;
|
||||
case CAN_STATE_ERROR_PASSIVE:
|
||||
priv->can_stats.error_passive++;
|
||||
break;
|
||||
case CAN_STATE_BUS_OFF:
|
||||
priv->can_stats.bus_off++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int can_tx_state_to_frame(struct net_device *dev, enum can_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
return CAN_ERR_CRTL_ACTIVE;
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
return CAN_ERR_CRTL_TX_WARNING;
|
||||
case CAN_STATE_ERROR_PASSIVE:
|
||||
return CAN_ERR_CRTL_TX_PASSIVE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int can_rx_state_to_frame(struct net_device *dev, enum can_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
return CAN_ERR_CRTL_ACTIVE;
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
return CAN_ERR_CRTL_RX_WARNING;
|
||||
case CAN_STATE_ERROR_PASSIVE:
|
||||
return CAN_ERR_CRTL_RX_PASSIVE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *can_get_state_str(const enum can_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
return "Error Active";
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
return "Error Warning";
|
||||
case CAN_STATE_ERROR_PASSIVE:
|
||||
return "Error Passive";
|
||||
case CAN_STATE_BUS_OFF:
|
||||
return "Bus Off";
|
||||
case CAN_STATE_STOPPED:
|
||||
return "Stopped";
|
||||
case CAN_STATE_SLEEPING:
|
||||
return "Sleeping";
|
||||
default:
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
return "<unknown>";
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_get_state_str);
|
||||
|
||||
void can_change_state(struct net_device *dev, struct can_frame *cf,
|
||||
enum can_state tx_state, enum can_state rx_state)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
enum can_state new_state = max(tx_state, rx_state);
|
||||
|
||||
if (unlikely(new_state == priv->state)) {
|
||||
netdev_warn(dev, "%s: oops, state did not change", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
netdev_dbg(dev, "Controller changed from %s State (%d) into %s State (%d).\n",
|
||||
can_get_state_str(priv->state), priv->state,
|
||||
can_get_state_str(new_state), new_state);
|
||||
|
||||
can_update_state_error_stats(dev, new_state);
|
||||
priv->state = new_state;
|
||||
|
||||
if (!cf)
|
||||
return;
|
||||
|
||||
if (unlikely(new_state == CAN_STATE_BUS_OFF)) {
|
||||
cf->can_id |= CAN_ERR_BUSOFF;
|
||||
return;
|
||||
}
|
||||
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] |= tx_state >= rx_state ?
|
||||
can_tx_state_to_frame(dev, tx_state) : 0;
|
||||
cf->data[1] |= tx_state <= rx_state ?
|
||||
can_rx_state_to_frame(dev, rx_state) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_change_state);
|
||||
|
||||
/* CAN device restart for bus-off recovery */
|
||||
static void can_restart(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
struct sk_buff *skb;
|
||||
struct can_frame *cf;
|
||||
int err;
|
||||
|
||||
BUG_ON(netif_carrier_ok(dev));
|
||||
|
||||
/* No synchronization needed because the device is bus-off and
|
||||
* no messages can come in or go out.
|
||||
*/
|
||||
can_flush_echo_skb(dev);
|
||||
|
||||
/* send restart message upstream */
|
||||
skb = alloc_can_err_skb(dev, &cf);
|
||||
if (!skb)
|
||||
goto restart;
|
||||
|
||||
cf->can_id |= CAN_ERR_RESTARTED;
|
||||
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += cf->len;
|
||||
|
||||
netif_rx_ni(skb);
|
||||
|
||||
restart:
|
||||
netdev_dbg(dev, "restarted\n");
|
||||
priv->can_stats.restarts++;
|
||||
|
||||
/* Now restart the device */
|
||||
err = priv->do_set_mode(dev, CAN_MODE_START);
|
||||
|
||||
netif_carrier_on(dev);
|
||||
if (err)
|
||||
netdev_err(dev, "Error %d during restart", err);
|
||||
}
|
||||
|
||||
static void can_restart_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
struct can_priv *priv = container_of(dwork, struct can_priv,
|
||||
restart_work);
|
||||
|
||||
can_restart(priv->dev);
|
||||
}
|
||||
|
||||
int can_restart_now(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* A manual restart is only permitted if automatic restart is
|
||||
* disabled and the device is in the bus-off state
|
||||
*/
|
||||
if (priv->restart_ms)
|
||||
return -EINVAL;
|
||||
if (priv->state != CAN_STATE_BUS_OFF)
|
||||
return -EBUSY;
|
||||
|
||||
cancel_delayed_work_sync(&priv->restart_work);
|
||||
can_restart(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CAN bus-off
|
||||
*
|
||||
* This functions should be called when the device goes bus-off to
|
||||
* tell the netif layer that no more packets can be sent or received.
|
||||
* If enabled, a timer is started to trigger bus-off recovery.
|
||||
*/
|
||||
void can_bus_off(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (priv->restart_ms)
|
||||
netdev_info(dev, "bus-off, scheduling restart in %d ms\n",
|
||||
priv->restart_ms);
|
||||
else
|
||||
netdev_info(dev, "bus-off\n");
|
||||
|
||||
netif_carrier_off(dev);
|
||||
|
||||
if (priv->restart_ms)
|
||||
schedule_delayed_work(&priv->restart_work,
|
||||
msecs_to_jiffies(priv->restart_ms));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_bus_off);
|
||||
|
||||
void can_setup(struct net_device *dev)
|
||||
{
|
||||
dev->type = ARPHRD_CAN;
|
||||
dev->mtu = CAN_MTU;
|
||||
dev->hard_header_len = 0;
|
||||
dev->addr_len = 0;
|
||||
dev->tx_queue_len = 10;
|
||||
|
||||
/* New-style flags. */
|
||||
dev->flags = IFF_NOARP;
|
||||
dev->features = NETIF_F_HW_CSUM;
|
||||
}
|
||||
|
||||
/* Allocate and setup space for the CAN network device */
|
||||
struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
|
||||
unsigned int txqs, unsigned int rxqs)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct can_priv *priv;
|
||||
int size;
|
||||
|
||||
/* We put the driver's priv, the CAN mid layer priv and the
|
||||
* echo skb into the netdevice's priv. The memory layout for
|
||||
* the netdev_priv is like this:
|
||||
*
|
||||
* +-------------------------+
|
||||
* | driver's priv |
|
||||
* +-------------------------+
|
||||
* | struct can_ml_priv |
|
||||
* +-------------------------+
|
||||
* | array of struct sk_buff |
|
||||
* +-------------------------+
|
||||
*/
|
||||
|
||||
size = ALIGN(sizeof_priv, NETDEV_ALIGN) + sizeof(struct can_ml_priv);
|
||||
|
||||
if (echo_skb_max)
|
||||
size = ALIGN(size, sizeof(struct sk_buff *)) +
|
||||
echo_skb_max * sizeof(struct sk_buff *);
|
||||
|
||||
dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup,
|
||||
txqs, rxqs);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
priv->dev = dev;
|
||||
|
||||
dev->ml_priv = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN);
|
||||
|
||||
if (echo_skb_max) {
|
||||
priv->echo_skb_max = echo_skb_max;
|
||||
priv->echo_skb = (void *)priv +
|
||||
(size - echo_skb_max * sizeof(struct sk_buff *));
|
||||
}
|
||||
|
||||
priv->state = CAN_STATE_STOPPED;
|
||||
|
||||
INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alloc_candev_mqs);
|
||||
|
||||
/* Free space of the CAN network device */
|
||||
void free_candev(struct net_device *dev)
|
||||
{
|
||||
free_netdev(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(free_candev);
|
||||
|
||||
/* changing MTU and control mode for CAN/CANFD devices */
|
||||
int can_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Do not allow changing the MTU while running */
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
/* allow change of MTU according to the CANFD ability of the device */
|
||||
switch (new_mtu) {
|
||||
case CAN_MTU:
|
||||
/* 'CANFD-only' controllers can not switch to CAN_MTU */
|
||||
if (priv->ctrlmode_static & CAN_CTRLMODE_FD)
|
||||
return -EINVAL;
|
||||
|
||||
priv->ctrlmode &= ~CAN_CTRLMODE_FD;
|
||||
break;
|
||||
|
||||
case CANFD_MTU:
|
||||
/* check for potential CANFD ability */
|
||||
if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) &&
|
||||
!(priv->ctrlmode_static & CAN_CTRLMODE_FD))
|
||||
return -EINVAL;
|
||||
|
||||
priv->ctrlmode |= CAN_CTRLMODE_FD;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_change_mtu);
|
||||
|
||||
/* Common open function when the device gets opened.
|
||||
*
|
||||
* This function should be called in the open function of the device
|
||||
* driver.
|
||||
*/
|
||||
int open_candev(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (!priv->bittiming.bitrate) {
|
||||
netdev_err(dev, "bit-timing not yet defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* For CAN FD the data bitrate has to be >= the arbitration bitrate */
|
||||
if ((priv->ctrlmode & CAN_CTRLMODE_FD) &&
|
||||
(!priv->data_bittiming.bitrate ||
|
||||
priv->data_bittiming.bitrate < priv->bittiming.bitrate)) {
|
||||
netdev_err(dev, "incorrect/missing data bit-timing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Switch carrier on if device was stopped while in bus-off state */
|
||||
if (!netif_carrier_ok(dev))
|
||||
netif_carrier_on(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(open_candev);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* Common function that can be used to understand the limitation of
|
||||
* a transceiver when it provides no means to determine these limitations
|
||||
* at runtime.
|
||||
*/
|
||||
void of_can_transceiver(struct net_device *dev)
|
||||
{
|
||||
struct device_node *dn;
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
struct device_node *np = dev->dev.parent->of_node;
|
||||
int ret;
|
||||
|
||||
dn = of_get_child_by_name(np, "can-transceiver");
|
||||
if (!dn)
|
||||
return;
|
||||
|
||||
ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max);
|
||||
of_node_put(dn);
|
||||
if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max))
|
||||
netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_can_transceiver);
|
||||
#endif
|
||||
|
||||
/* Common close function for cleanup before the device gets closed.
|
||||
*
|
||||
* This function should be called in the close function of the device
|
||||
* driver.
|
||||
*/
|
||||
void close_candev(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
cancel_delayed_work_sync(&priv->restart_work);
|
||||
can_flush_echo_skb(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(close_candev);
|
||||
|
||||
/* Register the CAN network device */
|
||||
int register_candev(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Ensure termination_const, termination_const_cnt and
|
||||
* do_set_termination consistency. All must be either set or
|
||||
* unset.
|
||||
*/
|
||||
if ((!priv->termination_const != !priv->termination_const_cnt) ||
|
||||
(!priv->termination_const != !priv->do_set_termination))
|
||||
return -EINVAL;
|
||||
|
||||
if (!priv->bitrate_const != !priv->bitrate_const_cnt)
|
||||
return -EINVAL;
|
||||
|
||||
if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt)
|
||||
return -EINVAL;
|
||||
|
||||
dev->rtnl_link_ops = &can_link_ops;
|
||||
netif_carrier_off(dev);
|
||||
|
||||
return register_netdev(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_candev);
|
||||
|
||||
/* Unregister the CAN network device */
|
||||
void unregister_candev(struct net_device *dev)
|
||||
{
|
||||
unregister_netdev(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_candev);
|
||||
|
||||
/* Test if a network device is a candev based device
|
||||
* and return the can_priv* if so.
|
||||
*/
|
||||
struct can_priv *safe_candev_priv(struct net_device *dev)
|
||||
{
|
||||
if (dev->type != ARPHRD_CAN || dev->rtnl_link_ops != &can_link_ops)
|
||||
return NULL;
|
||||
|
||||
return netdev_priv(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(safe_candev_priv);
|
||||
|
||||
static __init int can_dev_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
can_led_notifier_init();
|
||||
|
||||
err = can_netlink_register();
|
||||
if (!err)
|
||||
pr_info(MOD_DESC "\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
module_init(can_dev_init);
|
||||
|
||||
static __exit void can_dev_exit(void)
|
||||
{
|
||||
can_netlink_unregister();
|
||||
|
||||
can_led_notifier_exit();
|
||||
}
|
||||
module_exit(can_dev_exit);
|
||||
|
||||
MODULE_ALIAS_RTNL_LINK("can");
|
95
drivers/net/can/dev/length.c
Normal file
95
drivers/net/can/dev/length.c
Normal file
@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (C) 2012, 2020 Oliver Hartkopp <socketcan@hartkopp.net>
|
||||
*/
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
|
||||
/* CAN DLC to real data length conversion helpers */
|
||||
|
||||
static const u8 dlc2len[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 12, 16, 20, 24, 32, 48, 64
|
||||
};
|
||||
|
||||
/* get data length from raw data length code (DLC) */
|
||||
u8 can_fd_dlc2len(u8 dlc)
|
||||
{
|
||||
return dlc2len[dlc & 0x0F];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_fd_dlc2len);
|
||||
|
||||
static const u8 len2dlc[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
|
||||
9, 9, 9, 9, /* 9 - 12 */
|
||||
10, 10, 10, 10, /* 13 - 16 */
|
||||
11, 11, 11, 11, /* 17 - 20 */
|
||||
12, 12, 12, 12, /* 21 - 24 */
|
||||
13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */
|
||||
14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */
|
||||
14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */
|
||||
15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */
|
||||
15, 15, 15, 15, 15, 15, 15, 15 /* 57 - 64 */
|
||||
};
|
||||
|
||||
/* map the sanitized data length to an appropriate data length code */
|
||||
u8 can_fd_len2dlc(u8 len)
|
||||
{
|
||||
/* check for length mapping table size at build time */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(len2dlc) != CANFD_MAX_DLEN + 1);
|
||||
|
||||
if (unlikely(len > CANFD_MAX_DLEN))
|
||||
return CANFD_MAX_DLC;
|
||||
|
||||
return len2dlc[len];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_fd_len2dlc);
|
||||
|
||||
/**
|
||||
* can_skb_get_frame_len() - Calculate the CAN Frame length in bytes
|
||||
* of a given skb.
|
||||
* @skb: socket buffer of a CAN message.
|
||||
*
|
||||
* Do a rough calculation: bit stuffing is ignored and length in bits
|
||||
* is rounded up to a length in bytes.
|
||||
*
|
||||
* Rationale: this function is to be used for the BQL functions
|
||||
* (netdev_sent_queue() and netdev_completed_queue()) which expect a
|
||||
* value in bytes. Just using skb->len is insufficient because it will
|
||||
* return the constant value of CAN(FD)_MTU. Doing the bit stuffing
|
||||
* calculation would be too expensive in term of computing resources
|
||||
* for no noticeable gain.
|
||||
*
|
||||
* Remarks: The payload of CAN FD frames with BRS flag are sent at a
|
||||
* different bitrate. Currently, the can-utils canbusload tool does
|
||||
* not support CAN-FD yet and so we could not run any benchmark to
|
||||
* measure the impact. There might be possible improvement here.
|
||||
*
|
||||
* Return: length in bytes.
|
||||
*/
|
||||
unsigned int can_skb_get_frame_len(const struct sk_buff *skb)
|
||||
{
|
||||
const struct canfd_frame *cf = (const struct canfd_frame *)skb->data;
|
||||
u8 len;
|
||||
|
||||
if (can_is_canfd_skb(skb))
|
||||
len = canfd_sanitize_len(cf->len);
|
||||
else if (cf->can_id & CAN_RTR_FLAG)
|
||||
len = 0;
|
||||
else
|
||||
len = cf->len;
|
||||
|
||||
if (can_is_canfd_skb(skb)) {
|
||||
if (cf->can_id & CAN_EFF_FLAG)
|
||||
len += CANFD_FRAME_OVERHEAD_EFF;
|
||||
else
|
||||
len += CANFD_FRAME_OVERHEAD_SFF;
|
||||
} else {
|
||||
if (cf->can_id & CAN_EFF_FLAG)
|
||||
len += CAN_FRAME_OVERHEAD_EFF;
|
||||
else
|
||||
len += CAN_FRAME_OVERHEAD_SFF;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_skb_get_frame_len);
|
379
drivers/net/can/dev/netlink.c
Normal file
379
drivers/net/can/dev/netlink.c
Normal file
@ -0,0 +1,379 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
|
||||
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
|
||||
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*/
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
#include <net/rtnetlink.h>
|
||||
|
||||
static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
|
||||
[IFLA_CAN_STATE] = { .type = NLA_U32 },
|
||||
[IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) },
|
||||
[IFLA_CAN_RESTART_MS] = { .type = NLA_U32 },
|
||||
[IFLA_CAN_RESTART] = { .type = NLA_U32 },
|
||||
[IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) },
|
||||
[IFLA_CAN_BITTIMING_CONST]
|
||||
= { .len = sizeof(struct can_bittiming_const) },
|
||||
[IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) },
|
||||
[IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) },
|
||||
[IFLA_CAN_DATA_BITTIMING]
|
||||
= { .len = sizeof(struct can_bittiming) },
|
||||
[IFLA_CAN_DATA_BITTIMING_CONST]
|
||||
= { .len = sizeof(struct can_bittiming_const) },
|
||||
[IFLA_CAN_TERMINATION] = { .type = NLA_U16 },
|
||||
};
|
||||
|
||||
static int can_validate(struct nlattr *tb[], struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
bool is_can_fd = false;
|
||||
|
||||
/* Make sure that valid CAN FD configurations always consist of
|
||||
* - nominal/arbitration bittiming
|
||||
* - data bittiming
|
||||
* - control mode with CAN_CTRLMODE_FD set
|
||||
*/
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
if (data[IFLA_CAN_CTRLMODE]) {
|
||||
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
|
||||
|
||||
is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD;
|
||||
}
|
||||
|
||||
if (is_can_fd) {
|
||||
if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING])
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (data[IFLA_CAN_DATA_BITTIMING]) {
|
||||
if (!is_can_fd || !data[IFLA_CAN_BITTIMING])
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_changelink(struct net_device *dev, struct nlattr *tb[],
|
||||
struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
/* We need synchronization with dev->stop() */
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (data[IFLA_CAN_BITTIMING]) {
|
||||
struct can_bittiming bt;
|
||||
|
||||
/* Do not allow changing bittiming while running */
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
/* Calculate bittiming parameters based on
|
||||
* bittiming_const if set, otherwise pass bitrate
|
||||
* directly via do_set_bitrate(). Bail out if neither
|
||||
* is given.
|
||||
*/
|
||||
if (!priv->bittiming_const && !priv->do_set_bittiming)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt));
|
||||
err = can_get_bittiming(dev, &bt,
|
||||
priv->bittiming_const,
|
||||
priv->bitrate_const,
|
||||
priv->bitrate_const_cnt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) {
|
||||
netdev_err(dev, "arbitration bitrate surpasses transceiver capabilities of %d bps\n",
|
||||
priv->bitrate_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&priv->bittiming, &bt, sizeof(bt));
|
||||
|
||||
if (priv->do_set_bittiming) {
|
||||
/* Finally, set the bit-timing registers */
|
||||
err = priv->do_set_bittiming(dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (data[IFLA_CAN_CTRLMODE]) {
|
||||
struct can_ctrlmode *cm;
|
||||
u32 ctrlstatic;
|
||||
u32 maskedflags;
|
||||
|
||||
/* Do not allow changing controller mode while running */
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
cm = nla_data(data[IFLA_CAN_CTRLMODE]);
|
||||
ctrlstatic = priv->ctrlmode_static;
|
||||
maskedflags = cm->flags & cm->mask;
|
||||
|
||||
/* check whether provided bits are allowed to be passed */
|
||||
if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* do not check for static fd-non-iso if 'fd' is disabled */
|
||||
if (!(maskedflags & CAN_CTRLMODE_FD))
|
||||
ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
|
||||
|
||||
/* make sure static options are provided by configuration */
|
||||
if ((maskedflags & ctrlstatic) != ctrlstatic)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* clear bits to be modified and copy the flag values */
|
||||
priv->ctrlmode &= ~cm->mask;
|
||||
priv->ctrlmode |= maskedflags;
|
||||
|
||||
/* CAN_CTRLMODE_FD can only be set when driver supports FD */
|
||||
if (priv->ctrlmode & CAN_CTRLMODE_FD)
|
||||
dev->mtu = CANFD_MTU;
|
||||
else
|
||||
dev->mtu = CAN_MTU;
|
||||
}
|
||||
|
||||
if (data[IFLA_CAN_RESTART_MS]) {
|
||||
/* Do not allow changing restart delay while running */
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]);
|
||||
}
|
||||
|
||||
if (data[IFLA_CAN_RESTART]) {
|
||||
/* Do not allow a restart while not running */
|
||||
if (!(dev->flags & IFF_UP))
|
||||
return -EINVAL;
|
||||
err = can_restart_now(dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (data[IFLA_CAN_DATA_BITTIMING]) {
|
||||
struct can_bittiming dbt;
|
||||
|
||||
/* Do not allow changing bittiming while running */
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
/* Calculate bittiming parameters based on
|
||||
* data_bittiming_const if set, otherwise pass bitrate
|
||||
* directly via do_set_bitrate(). Bail out if neither
|
||||
* is given.
|
||||
*/
|
||||
if (!priv->data_bittiming_const && !priv->do_set_data_bittiming)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]),
|
||||
sizeof(dbt));
|
||||
err = can_get_bittiming(dev, &dbt,
|
||||
priv->data_bittiming_const,
|
||||
priv->data_bitrate_const,
|
||||
priv->data_bitrate_const_cnt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) {
|
||||
netdev_err(dev, "canfd data bitrate surpasses transceiver capabilities of %d bps\n",
|
||||
priv->bitrate_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&priv->data_bittiming, &dbt, sizeof(dbt));
|
||||
|
||||
if (priv->do_set_data_bittiming) {
|
||||
/* Finally, set the bit-timing registers */
|
||||
err = priv->do_set_data_bittiming(dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (data[IFLA_CAN_TERMINATION]) {
|
||||
const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]);
|
||||
const unsigned int num_term = priv->termination_const_cnt;
|
||||
unsigned int i;
|
||||
|
||||
if (!priv->do_set_termination)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* check whether given value is supported by the interface */
|
||||
for (i = 0; i < num_term; i++) {
|
||||
if (termval == priv->termination_const[i])
|
||||
break;
|
||||
}
|
||||
if (i >= num_term)
|
||||
return -EINVAL;
|
||||
|
||||
/* Finally, set the termination value */
|
||||
err = priv->do_set_termination(dev, termval);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
priv->termination = termval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t can_get_size(const struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
size_t size = 0;
|
||||
|
||||
if (priv->bittiming.bitrate) /* IFLA_CAN_BITTIMING */
|
||||
size += nla_total_size(sizeof(struct can_bittiming));
|
||||
if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */
|
||||
size += nla_total_size(sizeof(struct can_bittiming_const));
|
||||
size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */
|
||||
size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */
|
||||
if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */
|
||||
size += nla_total_size(sizeof(struct can_berr_counter));
|
||||
if (priv->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */
|
||||
size += nla_total_size(sizeof(struct can_bittiming));
|
||||
if (priv->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
|
||||
size += nla_total_size(sizeof(struct can_bittiming_const));
|
||||
if (priv->termination_const) {
|
||||
size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */
|
||||
size += nla_total_size(sizeof(*priv->termination_const) * /* IFLA_CAN_TERMINATION_CONST */
|
||||
priv->termination_const_cnt);
|
||||
}
|
||||
if (priv->bitrate_const) /* IFLA_CAN_BITRATE_CONST */
|
||||
size += nla_total_size(sizeof(*priv->bitrate_const) *
|
||||
priv->bitrate_const_cnt);
|
||||
if (priv->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */
|
||||
size += nla_total_size(sizeof(*priv->data_bitrate_const) *
|
||||
priv->data_bitrate_const_cnt);
|
||||
size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
struct can_ctrlmode cm = {.flags = priv->ctrlmode};
|
||||
struct can_berr_counter bec = { };
|
||||
enum can_state state = priv->state;
|
||||
|
||||
if (priv->do_get_state)
|
||||
priv->do_get_state(dev, &state);
|
||||
|
||||
if ((priv->bittiming.bitrate &&
|
||||
nla_put(skb, IFLA_CAN_BITTIMING,
|
||||
sizeof(priv->bittiming), &priv->bittiming)) ||
|
||||
|
||||
(priv->bittiming_const &&
|
||||
nla_put(skb, IFLA_CAN_BITTIMING_CONST,
|
||||
sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
|
||||
|
||||
nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) ||
|
||||
nla_put_u32(skb, IFLA_CAN_STATE, state) ||
|
||||
nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
|
||||
nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
|
||||
|
||||
(priv->do_get_berr_counter &&
|
||||
!priv->do_get_berr_counter(dev, &bec) &&
|
||||
nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||
|
||||
|
||||
(priv->data_bittiming.bitrate &&
|
||||
nla_put(skb, IFLA_CAN_DATA_BITTIMING,
|
||||
sizeof(priv->data_bittiming), &priv->data_bittiming)) ||
|
||||
|
||||
(priv->data_bittiming_const &&
|
||||
nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
|
||||
sizeof(*priv->data_bittiming_const),
|
||||
priv->data_bittiming_const)) ||
|
||||
|
||||
(priv->termination_const &&
|
||||
(nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) ||
|
||||
nla_put(skb, IFLA_CAN_TERMINATION_CONST,
|
||||
sizeof(*priv->termination_const) *
|
||||
priv->termination_const_cnt,
|
||||
priv->termination_const))) ||
|
||||
|
||||
(priv->bitrate_const &&
|
||||
nla_put(skb, IFLA_CAN_BITRATE_CONST,
|
||||
sizeof(*priv->bitrate_const) *
|
||||
priv->bitrate_const_cnt,
|
||||
priv->bitrate_const)) ||
|
||||
|
||||
(priv->data_bitrate_const &&
|
||||
nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST,
|
||||
sizeof(*priv->data_bitrate_const) *
|
||||
priv->data_bitrate_const_cnt,
|
||||
priv->data_bitrate_const)) ||
|
||||
|
||||
(nla_put(skb, IFLA_CAN_BITRATE_MAX,
|
||||
sizeof(priv->bitrate_max),
|
||||
&priv->bitrate_max))
|
||||
)
|
||||
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t can_get_xstats_size(const struct net_device *dev)
|
||||
{
|
||||
return sizeof(struct can_device_stats);
|
||||
}
|
||||
|
||||
static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (nla_put(skb, IFLA_INFO_XSTATS,
|
||||
sizeof(priv->can_stats), &priv->can_stats))
|
||||
goto nla_put_failure;
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int can_newlink(struct net *src_net, struct net_device *dev,
|
||||
struct nlattr *tb[], struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static void can_dellink(struct net_device *dev, struct list_head *head)
|
||||
{
|
||||
}
|
||||
|
||||
struct rtnl_link_ops can_link_ops __read_mostly = {
|
||||
.kind = "can",
|
||||
.maxtype = IFLA_CAN_MAX,
|
||||
.policy = can_policy,
|
||||
.setup = can_setup,
|
||||
.validate = can_validate,
|
||||
.newlink = can_newlink,
|
||||
.changelink = can_changelink,
|
||||
.dellink = can_dellink,
|
||||
.get_size = can_get_size,
|
||||
.fill_info = can_fill_info,
|
||||
.get_xstats_size = can_get_xstats_size,
|
||||
.fill_xstats = can_fill_xstats,
|
||||
};
|
||||
|
||||
int can_netlink_register(void)
|
||||
{
|
||||
return rtnl_link_register(&can_link_ops);
|
||||
}
|
||||
|
||||
void can_netlink_unregister(void)
|
||||
{
|
||||
rtnl_link_unregister(&can_link_ops);
|
||||
}
|
@ -263,7 +263,8 @@ int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
|
||||
EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted);
|
||||
|
||||
unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
|
||||
unsigned int idx, u32 timestamp)
|
||||
unsigned int idx, u32 timestamp,
|
||||
unsigned int *frame_len_ptr)
|
||||
{
|
||||
struct net_device *dev = offload->dev;
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
@ -271,7 +272,7 @@ unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
|
||||
u8 len;
|
||||
int err;
|
||||
|
||||
skb = __can_get_echo_skb(dev, idx, &len);
|
||||
skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr);
|
||||
if (!skb)
|
||||
return 0;
|
||||
|
231
drivers/net/can/dev/skb.c
Normal file
231
drivers/net/can/dev/skb.c
Normal file
@ -0,0 +1,231 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
|
||||
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
|
||||
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*/
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
|
||||
/* Local echo of CAN messages
|
||||
*
|
||||
* CAN network devices *should* support a local echo functionality
|
||||
* (see Documentation/networking/can.rst). To test the handling of CAN
|
||||
* interfaces that do not support the local echo both driver types are
|
||||
* implemented. In the case that the driver does not support the echo
|
||||
* the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core
|
||||
* to perform the echo as a fallback solution.
|
||||
*/
|
||||
void can_flush_echo_skb(struct net_device *dev)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->echo_skb_max; i++) {
|
||||
if (priv->echo_skb[i]) {
|
||||
kfree_skb(priv->echo_skb[i]);
|
||||
priv->echo_skb[i] = NULL;
|
||||
stats->tx_dropped++;
|
||||
stats->tx_aborted_errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Put the skb on the stack to be looped backed locally lateron
|
||||
*
|
||||
* The function is typically called in the start_xmit function
|
||||
* of the device driver. The driver must protect access to
|
||||
* priv->echo_skb, if necessary.
|
||||
*/
|
||||
int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned int idx, unsigned int frame_len)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
BUG_ON(idx >= priv->echo_skb_max);
|
||||
|
||||
/* check flag whether this packet has to be looped back */
|
||||
if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK ||
|
||||
(skb->protocol != htons(ETH_P_CAN) &&
|
||||
skb->protocol != htons(ETH_P_CANFD))) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!priv->echo_skb[idx]) {
|
||||
skb = can_create_echo_skb(skb);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* make settings for echo to reduce code in irq context */
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
skb->dev = dev;
|
||||
|
||||
/* save frame_len to reuse it when transmission is completed */
|
||||
can_skb_prv(skb)->frame_len = frame_len;
|
||||
|
||||
skb_tx_timestamp(skb);
|
||||
|
||||
/* save this skb for tx interrupt echo handling */
|
||||
priv->echo_skb[idx] = skb;
|
||||
} else {
|
||||
/* locking problem with netif_stop_queue() ?? */
|
||||
netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx);
|
||||
kfree_skb(skb);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_put_echo_skb);
|
||||
|
||||
struct sk_buff *
|
||||
__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr,
|
||||
unsigned int *frame_len_ptr)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (idx >= priv->echo_skb_max) {
|
||||
netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
|
||||
__func__, idx, priv->echo_skb_max);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (priv->echo_skb[idx]) {
|
||||
/* Using "struct canfd_frame::len" for the frame
|
||||
* length is supported on both CAN and CANFD frames.
|
||||
*/
|
||||
struct sk_buff *skb = priv->echo_skb[idx];
|
||||
struct can_skb_priv *can_skb_priv = can_skb_prv(skb);
|
||||
struct canfd_frame *cf = (struct canfd_frame *)skb->data;
|
||||
|
||||
/* get the real payload length for netdev statistics */
|
||||
if (cf->can_id & CAN_RTR_FLAG)
|
||||
*len_ptr = 0;
|
||||
else
|
||||
*len_ptr = cf->len;
|
||||
|
||||
if (frame_len_ptr)
|
||||
*frame_len_ptr = can_skb_priv->frame_len;
|
||||
|
||||
priv->echo_skb[idx] = NULL;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the skb from the stack and loop it back locally
|
||||
*
|
||||
* The function is typically called when the TX done interrupt
|
||||
* is handled in the device driver. The driver must protect
|
||||
* access to priv->echo_skb, if necessary.
|
||||
*/
|
||||
unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx,
|
||||
unsigned int *frame_len_ptr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 len;
|
||||
|
||||
skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr);
|
||||
if (!skb)
|
||||
return 0;
|
||||
|
||||
skb_get(skb);
|
||||
if (netif_rx(skb) == NET_RX_SUCCESS)
|
||||
dev_consume_skb_any(skb);
|
||||
else
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_get_echo_skb);
|
||||
|
||||
/* Remove the skb from the stack and free it.
|
||||
*
|
||||
* The function is typically called when TX failed.
|
||||
*/
|
||||
void can_free_echo_skb(struct net_device *dev, unsigned int idx)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
BUG_ON(idx >= priv->echo_skb_max);
|
||||
|
||||
if (priv->echo_skb[idx]) {
|
||||
dev_kfree_skb_any(priv->echo_skb[idx]);
|
||||
priv->echo_skb[idx] = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_free_echo_skb);
|
||||
|
||||
struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
|
||||
sizeof(struct can_frame));
|
||||
if (unlikely(!skb))
|
||||
return NULL;
|
||||
|
||||
skb->protocol = htons(ETH_P_CAN);
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
can_skb_reserve(skb);
|
||||
can_skb_prv(skb)->ifindex = dev->ifindex;
|
||||
can_skb_prv(skb)->skbcnt = 0;
|
||||
|
||||
*cf = skb_put_zero(skb, sizeof(struct can_frame));
|
||||
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alloc_can_skb);
|
||||
|
||||
struct sk_buff *alloc_canfd_skb(struct net_device *dev,
|
||||
struct canfd_frame **cfd)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
|
||||
sizeof(struct canfd_frame));
|
||||
if (unlikely(!skb))
|
||||
return NULL;
|
||||
|
||||
skb->protocol = htons(ETH_P_CANFD);
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
can_skb_reserve(skb);
|
||||
can_skb_prv(skb)->ifindex = dev->ifindex;
|
||||
can_skb_prv(skb)->skbcnt = 0;
|
||||
|
||||
*cfd = skb_put_zero(skb, sizeof(struct canfd_frame));
|
||||
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alloc_canfd_skb);
|
||||
|
||||
struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_can_skb(dev, cf);
|
||||
if (unlikely(!skb))
|
||||
return NULL;
|
||||
|
||||
(*cf)->can_id = CAN_ERR_FLAG;
|
||||
(*cf)->len = CAN_ERR_DLC;
|
||||
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alloc_can_err_skb);
|
@ -9,6 +9,7 @@
|
||||
//
|
||||
// Based on code originally by Andrey Volkov <avolkov@varma-el.com>
|
||||
|
||||
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
@ -17,6 +18,7 @@
|
||||
#include <linux/can/rx-offload.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
@ -242,6 +244,8 @@
|
||||
#define FLEXCAN_QUIRK_SUPPORT_FD BIT(9)
|
||||
/* support memory detection and correction */
|
||||
#define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10)
|
||||
/* Setup stop mode with SCU firmware to support wakeup */
|
||||
#define FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW BIT(11)
|
||||
|
||||
/* Structure of the message buffer */
|
||||
struct flexcan_mb {
|
||||
@ -347,6 +351,7 @@ struct flexcan_priv {
|
||||
u8 mb_count;
|
||||
u8 mb_size;
|
||||
u8 clk_src; /* clock source of CAN Protocol Engine */
|
||||
u8 scu_idx;
|
||||
|
||||
u64 rx_mask;
|
||||
u64 tx_mask;
|
||||
@ -358,6 +363,9 @@ struct flexcan_priv {
|
||||
struct regulator *reg_xceiver;
|
||||
struct flexcan_stop_mode stm;
|
||||
|
||||
/* IPC handle when setup stop mode by System Controller firmware(scfw) */
|
||||
struct imx_sc_ipc *sc_ipc_handle;
|
||||
|
||||
/* Read and Write APIs */
|
||||
u32 (*read)(void __iomem *addr);
|
||||
void (*write)(u32 val, void __iomem *addr);
|
||||
@ -387,7 +395,7 @@ static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
|
||||
static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = {
|
||||
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
|
||||
FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
|
||||
FLEXCAN_QUIRK_SUPPORT_FD,
|
||||
FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW,
|
||||
};
|
||||
|
||||
static struct flexcan_devtype_data fsl_imx8mp_devtype_data = {
|
||||
@ -546,18 +554,42 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
|
||||
priv->write(reg_mcr, ®s->mcr);
|
||||
}
|
||||
|
||||
static int flexcan_stop_mode_enable_scfw(struct flexcan_priv *priv, bool enabled)
|
||||
{
|
||||
u8 idx = priv->scu_idx;
|
||||
u32 rsrc_id, val;
|
||||
|
||||
rsrc_id = IMX_SC_R_CAN(idx);
|
||||
|
||||
if (enabled)
|
||||
val = 1;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
/* stop mode request via scu firmware */
|
||||
return imx_sc_misc_set_control(priv->sc_ipc_handle, rsrc_id,
|
||||
IMX_SC_C_IPG_STOP, val);
|
||||
}
|
||||
|
||||
static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
||||
{
|
||||
struct flexcan_regs __iomem *regs = priv->regs;
|
||||
u32 reg_mcr;
|
||||
int ret;
|
||||
|
||||
reg_mcr = priv->read(®s->mcr);
|
||||
reg_mcr |= FLEXCAN_MCR_SLF_WAK;
|
||||
priv->write(reg_mcr, ®s->mcr);
|
||||
|
||||
/* enable stop request */
|
||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||
1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
||||
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) {
|
||||
ret = flexcan_stop_mode_enable_scfw(priv, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||
1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
||||
}
|
||||
|
||||
return flexcan_low_power_enter_ack(priv);
|
||||
}
|
||||
@ -566,10 +598,17 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
|
||||
{
|
||||
struct flexcan_regs __iomem *regs = priv->regs;
|
||||
u32 reg_mcr;
|
||||
int ret;
|
||||
|
||||
/* remove stop request */
|
||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||
1 << priv->stm.req_bit, 0);
|
||||
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) {
|
||||
ret = flexcan_stop_mode_enable_scfw(priv, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||
1 << priv->stm.req_bit, 0);
|
||||
}
|
||||
|
||||
reg_mcr = priv->read(®s->mcr);
|
||||
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
|
||||
@ -776,7 +815,7 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
|
||||
priv->write(data, &priv->tx_mb->data[i / sizeof(u32)]);
|
||||
}
|
||||
|
||||
can_put_echo_skb(skb, dev, 0);
|
||||
can_put_echo_skb(skb, dev, 0, 0);
|
||||
|
||||
priv->write(can_id, &priv->tx_mb->can_id);
|
||||
priv->write(ctrl, &priv->tx_mb->can_ctrl);
|
||||
@ -1083,8 +1122,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
|
||||
u32 reg_ctrl = priv->read(&priv->tx_mb->can_ctrl);
|
||||
|
||||
handled = IRQ_HANDLED;
|
||||
stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload,
|
||||
0, reg_ctrl << 16);
|
||||
stats->tx_bytes +=
|
||||
can_rx_offload_get_echo_skb(&priv->offload, 0,
|
||||
reg_ctrl << 16, NULL);
|
||||
stats->tx_packets++;
|
||||
can_led_event(dev, CAN_LED_EVENT_TX);
|
||||
|
||||
@ -1867,7 +1907,7 @@ static void unregister_flexcandev(struct net_device *dev)
|
||||
unregister_candev(dev);
|
||||
}
|
||||
|
||||
static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||
static int flexcan_setup_stop_mode_gpr(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
@ -1912,11 +1952,6 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||
"gpr %s req_gpr=0x02%x req_bit=%u\n",
|
||||
gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit);
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
|
||||
if (of_property_read_bool(np, "wakeup-source"))
|
||||
device_set_wakeup_enable(&pdev->dev, true);
|
||||
|
||||
return 0;
|
||||
|
||||
out_put_node:
|
||||
@ -1924,6 +1959,58 @@ out_put_node:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int flexcan_setup_stop_mode_scfw(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct flexcan_priv *priv;
|
||||
u8 scu_idx;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u8(pdev->dev.of_node, "fsl,scu-index", &scu_idx);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "failed to get scu index\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
priv->scu_idx = scu_idx;
|
||||
|
||||
/* this function could be deferred probe, return -EPROBE_DEFER */
|
||||
return imx_scu_get_handle(&priv->sc_ipc_handle);
|
||||
}
|
||||
|
||||
/* flexcan_setup_stop_mode - Setup stop mode for wakeup
|
||||
*
|
||||
* Return: = 0 setup stop mode successfully or doesn't support this feature
|
||||
* < 0 fail to setup stop mode (could be deferred probe)
|
||||
*/
|
||||
static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct flexcan_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
|
||||
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW)
|
||||
ret = flexcan_setup_stop_mode_scfw(pdev);
|
||||
else if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR)
|
||||
ret = flexcan_setup_stop_mode_gpr(pdev);
|
||||
else
|
||||
/* return 0 directly if doesn't support stop mode feature */
|
||||
return 0;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "wakeup-source"))
|
||||
device_set_wakeup_enable(&pdev->dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id flexcan_of_match[] = {
|
||||
{ .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, },
|
||||
{ .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, },
|
||||
@ -2054,17 +2141,20 @@ static int flexcan_probe(struct platform_device *pdev)
|
||||
goto failed_register;
|
||||
}
|
||||
|
||||
err = flexcan_setup_stop_mode(pdev);
|
||||
if (err < 0) {
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "setup stop mode failed\n");
|
||||
goto failed_setup_stop_mode;
|
||||
}
|
||||
|
||||
of_can_transceiver(dev);
|
||||
devm_can_led_init(dev);
|
||||
|
||||
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) {
|
||||
err = flexcan_setup_stop_mode(pdev);
|
||||
if (err)
|
||||
dev_dbg(&pdev->dev, "failed to setup stop-mode\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed_setup_stop_mode:
|
||||
unregister_flexcandev(dev);
|
||||
failed_register:
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user