Merge 51e6d17809
("Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next") into android-mainline
First steps of the 5.12-rc1 merge, the large networking chunk. Resolves merge conflicts in: net/core/filter.c net/ipv6/route.c Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: Id1650a4e7ab7104647e85beddddb672f779d4d1f
This commit is contained in:
commit
4d1e11edcc
@ -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
|
||||
@ -10694,6 +10728,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
|
||||
@ -12524,6 +12560,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>
|
||||
@ -12841,6 +12885,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
|
||||
@ -17857,7 +17902,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
@ -649,7 +649,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
|
||||
|
||||
@ -1083,6 +1084,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)
|
||||
@ -1506,7 +1518,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 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
|
||||
}
|
||||
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 @@ config-space@CP11X_BASE {
|
||||
|
||||
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 @@ ipa: ipa@1e40000 {
|
||||
qcom,smem-state-names = "ipa-clock-enabled-valid",
|
||||
"ipa-clock-enabled";
|
||||
|
||||
modem-remoteproc = <&remoteproc_mpss>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -2367,8 +2367,6 @@ ipa: ipa@1e40000 {
|
||||
qcom,smem-state-names = "ipa-clock-enabled-valid",
|
||||
"ipa-clock-enabled";
|
||||
|
||||
modem-remoteproc = <&mss_pil>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -41,3 +41,21 @@ &uart1 {
|
||||
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 @@ uart_clk: uart-clk {
|
||||
#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 @@ spi6: spi@28146000 {
|
||||
#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 @@ 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:
|
||||
/* 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 @@ static int build_one_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
|
||||
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 @@ static int build_one_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
|
||||
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 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
|
||||
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 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
|
||||
/* 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 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
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 @@ xadd: if (is_imm8(insn->off))
|
||||
}
|
||||
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 @@ xadd: if (is_imm8(insn->off))
|
||||
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 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
* 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_cond_jmp: jmp_cond = get_cond_jmp_opcode(BPF_OP(code), false);
|
||||
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 @@ int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
|
||||
}
|
||||
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 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
* 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 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
* 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 @@ static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val)
|
||||
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 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
}
|
||||
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 @@ static void hci_uart_write_work(struct work_struct *work)
|
||||
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 @@ static int qca_setup(struct hci_uart *hu)
|
||||
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 @@ static void reset_device_work(struct work_struct *work)
|
||||
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 @@ static netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev)
|
||||
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 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||
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