1
0
Fork 0

Networking updates for 5.11

Core:
 
  - support "prefer busy polling" NAPI operation mode, where we defer softirq
    for some time expecting applications to periodically busy poll
 
  - AF_XDP: improve efficiency by more batching and hindering
            the adjacency cache prefetcher
 
  - af_packet: make packet_fanout.arr size configurable up to 64K
 
  - tcp: optimize TCP zero copy receive in presence of partial or unaligned
         reads making zero copy a performance win for much smaller messages
 
  - XDP: add bulk APIs for returning / freeing frames
 
  - sched: support fragmenting IP packets as they come out of conntrack
 
  - net: allow virtual netdevs to forward UDP L4 and fraglist GSO skbs
 
 BPF:
 
  - BPF switch from crude rlimit-based to memcg-based memory accounting
 
  - BPF type format information for kernel modules and related tracing
    enhancements
 
  - BPF implement task local storage for BPF LSM
 
  - allow the FENTRY/FEXIT/RAW_TP tracing programs to use bpf_sk_storage
 
 Protocols:
 
  - mptcp: improve multiple xmit streams support, memory accounting and
           many smaller improvements
 
  - TLS: support CHACHA20-POLY1305 cipher
 
  - seg6: add support for SRv6 End.DT4/DT6 behavior
 
  - sctp: Implement RFC 6951: UDP Encapsulation of SCTP
 
  - ppp_generic: add ability to bridge channels directly
 
  - bridge: Connectivity Fault Management (CFM) support as is defined in
            IEEE 802.1Q section 12.14.
 
 Drivers:
 
  - mlx5: make use of the new auxiliary bus to organize the driver internals
 
  - mlx5: more accurate port TX timestamping support
 
  - mlxsw:
    - improve the efficiency of offloaded next hop updates by using
      the new nexthop object API
    - support blackhole nexthops
    - support IEEE 802.1ad (Q-in-Q) bridging
 
  - rtw88: major bluetooth co-existance improvements
 
  - iwlwifi: support new 6 GHz frequency band
 
  - ath11k: Fast Initial Link Setup (FILS)
 
  - mt7915: dual band concurrent (DBDC) support
 
  - net: ipa: add basic support for IPA v4.5
 
 Refactor:
 
  - a few pieces of in_interrupt() cleanup work from Sebastian Andrzej Siewior
 
  - phy: add support for shared interrupts; get rid of multiple driver
         APIs and have the drivers write a full IRQ handler, slight growth
 	of driver code should be compensated by the simpler API which
 	also allows shared IRQs
 
  - add common code for handling netdev per-cpu counters
 
  - move TX packet re-allocation from Ethernet switch tag drivers to
    a central place
 
  - improve efficiency and rename nla_strlcpy
 
  - number of W=1 warning cleanups as we now catch those in a patchwork
    build bot
 
 Old code removal:
 
  - wan: delete the DLCI / SDLA drivers
 
  - wimax: move to staging
 
  - wifi: remove old WDS wifi bridging support
 
 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE6jPA+I1ugmIBA4hXMUZtbf5SIrsFAl/YXmUACgkQMUZtbf5S
 IrvSQBAAgOrt4EFopEvVqlTHZbqI45IEqgtXS+YWmlgnjZCgshyMj8q1yK1zzane
 qYxr/NNJ9kV3FdtaynmmHPgEEEfR5kJ/D3B2BsxYDkaDDrD0vbNsBGw+L+/Gbhxl
 N/5l/9FjLyLY1D+EErknuwR5XGuQ6BSDVaKQMhYOiK2hgdnAAI4hszo8Chf6wdD0
 XDBslQ7vpD/05r+eMj0IkS5dSAoGOIFXUxhJ5dqrDbRHiKsIyWqA3PLbYemfAhxI
 s2XckjfmSgGE3FKL8PSFu+EcfHbJQQjLcULJUnqgVcdwEEtRuE9ggEi52nZRXMWM
 4e8sQJAR9Fx7pZy0G1xfS149j6iPU5LjRlU9TNSpVABz14Vvvo3gEL6gyIdsz+xh
 hMN7UBdp0FEaP028CXoIYpaBesvQqj0BSndmee8qsYAtN6j+QKcM2AOSr7JN1uMH
 C/86EDoGAATiEQIVWJvnX5MPmlAoblyLA+RuVhmxkIBx2InGXkFmWqRkXT5l4jtk
 LVl8/TArR4alSQqLXictXCjYlCm9j5N4zFFtEVasSYi7/ZoPfgRNWT+lJ2R8Y+Zv
 +htzGaFuyj6RJTVeFQMrkl3whAtBamo2a0kwg45NnxmmXcspN6kJX1WOIy82+MhD
 Yht7uplSs7MGKA78q/CDU0XBeGjpABUvmplUQBIfrR/jKLW2730=
 =GXs1
 -----END PGP SIGNATURE-----

Merge tag 'net-next-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next

Pull networking updates from Jakub Kicinski:
 "Core:

   - support "prefer busy polling" NAPI operation mode, where we defer
     softirq for some time expecting applications to periodically busy
     poll

   - AF_XDP: improve efficiency by more batching and hindering the
     adjacency cache prefetcher

   - af_packet: make packet_fanout.arr size configurable up to 64K

   - tcp: optimize TCP zero copy receive in presence of partial or
     unaligned reads making zero copy a performance win for much smaller
     messages

   - XDP: add bulk APIs for returning / freeing frames

   - sched: support fragmenting IP packets as they come out of conntrack

   - net: allow virtual netdevs to forward UDP L4 and fraglist GSO skbs

  BPF:

   - BPF switch from crude rlimit-based to memcg-based memory accounting

   - BPF type format information for kernel modules and related tracing
     enhancements

   - BPF implement task local storage for BPF LSM

   - allow the FENTRY/FEXIT/RAW_TP tracing programs to use
     bpf_sk_storage

  Protocols:

   - mptcp: improve multiple xmit streams support, memory accounting and
     many smaller improvements

   - TLS: support CHACHA20-POLY1305 cipher

   - seg6: add support for SRv6 End.DT4/DT6 behavior

   - sctp: Implement RFC 6951: UDP Encapsulation of SCTP

   - ppp_generic: add ability to bridge channels directly

   - bridge: Connectivity Fault Management (CFM) support as is defined
     in IEEE 802.1Q section 12.14.

  Drivers:

   - mlx5: make use of the new auxiliary bus to organize the driver
     internals

   - mlx5: more accurate port TX timestamping support

   - mlxsw:
      - improve the efficiency of offloaded next hop updates by using
        the new nexthop object API
      - support blackhole nexthops
      - support IEEE 802.1ad (Q-in-Q) bridging

   - rtw88: major bluetooth co-existance improvements

   - iwlwifi: support new 6 GHz frequency band

   - ath11k: Fast Initial Link Setup (FILS)

   - mt7915: dual band concurrent (DBDC) support

   - net: ipa: add basic support for IPA v4.5

  Refactor:

   - a few pieces of in_interrupt() cleanup work from Sebastian Andrzej
     Siewior

   - phy: add support for shared interrupts; get rid of multiple driver
     APIs and have the drivers write a full IRQ handler, slight growth
     of driver code should be compensated by the simpler API which also
     allows shared IRQs

   - add common code for handling netdev per-cpu counters

   - move TX packet re-allocation from Ethernet switch tag drivers to a
     central place

   - improve efficiency and rename nla_strlcpy

   - number of W=1 warning cleanups as we now catch those in a patchwork
     build bot

  Old code removal:

   - wan: delete the DLCI / SDLA drivers

   - wimax: move to staging

   - wifi: remove old WDS wifi bridging support"

* tag 'net-next-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1922 commits)
  net: hns3: fix expression that is currently always true
  net: fix proc_fs init handling in af_packet and tls
  nfc: pn533: convert comma to semicolon
  af_vsock: Assign the vsock transport considering the vsock address flags
  af_vsock: Set VMADDR_FLAG_TO_HOST flag on the receive path
  vsock_addr: Check for supported flag values
  vm_sockets: Add VMADDR_FLAG_TO_HOST vsock flag
  vm_sockets: Add flags field in the vsock address data structure
  net: Disable NETIF_F_HW_TLS_TX when HW_CSUM is disabled
  tcp: Add logic to check for SYN w/ data in tcp_simple_retransmit
  net: mscc: ocelot: install MAC addresses in .ndo_set_rx_mode from process context
  nfc: s3fwrn5: Release the nfc firmware
  net: vxget: clean up sparse warnings
  mlxsw: spectrum_router: Use eXtended mezzanine to offload IPv4 router
  mlxsw: spectrum: Set KVH XLT cache mode for Spectrum2/3
  mlxsw: spectrum_router_xm: Introduce basic XM cache flushing
  mlxsw: reg: Add Router LPM Cache Enable Register
  mlxsw: reg: Add Router LPM Cache ML Delete Register
  mlxsw: spectrum_router_xm: Implement L-value tracking for M-index
  mlxsw: reg: Add XM Router M Table Register
  ...
zero-sugar-mainline-defconfig
Linus Torvalds 2020-12-15 13:22:29 -08:00
commit d635a69dd4
1879 changed files with 72099 additions and 38938 deletions

View File

@ -2510,15 +2510,6 @@ W: http://www.rdrop.com/users/paulmck/
D: RCU and variants D: RCU and variants
D: rcutorture module D: rcutorture module
N: Mike McLagan
E: mike.mclagan@linux.org
W: http://www.invlogic.com/~mmclagan
D: DLCI/FRAD drivers for Sangoma SDLAs
S: Innovative Logic Corp
S: Post Office Box 1068
S: Laurel, Maryland 20732
S: USA
N: Bradley McLean N: Bradley McLean
E: brad@bradpc.gaylord.com E: brad@bradpc.gaylord.com
D: Device driver hacker D: Device driver hacker

View File

@ -1,32 +0,0 @@
This ABI is deprecated and will be removed after 2021. It is
replaced with the batadv generic netlink family.
What: /sys/class/net/<iface>/batman-adv/elp_interval
Date: Feb 2014
Contact: Linus Lüssing <linus.luessing@web.de>
Description:
Defines the interval in milliseconds in which batman
emits probing packets for neighbor sensing (ELP).
What: /sys/class/net/<iface>/batman-adv/iface_status
Date: May 2010
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
Indicates the status of <iface> as it is seen by batman.
What: /sys/class/net/<iface>/batman-adv/mesh_iface
Date: May 2010
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
The /sys/class/net/<iface>/batman-adv/mesh_iface file
displays the batman mesh interface this <iface>
currently is associated with.
What: /sys/class/net/<iface>/batman-adv/throughput_override
Date: Feb 2014
Contact: Antonio Quartulli <a@unstable.cc>
description:
Defines the throughput value to be used by B.A.T.M.A.N. V
when estimating the link throughput using this interface.
If the value is set to 0 then batman-adv will try to
estimate the throughput by itself.

View File

@ -1,110 +0,0 @@
This ABI is deprecated and will be removed after 2021. It is
replaced with the batadv generic netlink family.
What: /sys/class/net/<mesh_iface>/mesh/aggregated_ogms
Date: May 2010
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
Indicates whether the batman protocol messages of the
mesh <mesh_iface> shall be aggregated or not.
What: /sys/class/net/<mesh_iface>/mesh/<vlan_subdir>/ap_isolation
Date: May 2011
Contact: Antonio Quartulli <a@unstable.cc>
Description:
Indicates whether the data traffic going from a
wireless client to another wireless client will be
silently dropped. <vlan_subdir> is empty when referring
to the untagged lan.
What: /sys/class/net/<mesh_iface>/mesh/bonding
Date: June 2010
Contact: Simon Wunderlich <sw@simonwunderlich.de>
Description:
Indicates whether the data traffic going through the
mesh will be sent using multiple interfaces at the
same time (if available).
What: /sys/class/net/<mesh_iface>/mesh/bridge_loop_avoidance
Date: November 2011
Contact: Simon Wunderlich <sw@simonwunderlich.de>
Description:
Indicates whether the bridge loop avoidance feature
is enabled. This feature detects and avoids loops
between the mesh and devices bridged with the soft
interface <mesh_iface>.
What: /sys/class/net/<mesh_iface>/mesh/fragmentation
Date: October 2010
Contact: Andreas Langer <an.langer@gmx.de>
Description:
Indicates whether the data traffic going through the
mesh will be fragmented or silently discarded if the
packet size exceeds the outgoing interface MTU.
What: /sys/class/net/<mesh_iface>/mesh/gw_bandwidth
Date: October 2010
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
Defines the bandwidth which is propagated by this
node if gw_mode was set to 'server'.
What: /sys/class/net/<mesh_iface>/mesh/gw_mode
Date: October 2010
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
Defines the state of the gateway features. Can be
either 'off', 'client' or 'server'.
What: /sys/class/net/<mesh_iface>/mesh/gw_sel_class
Date: October 2010
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
Defines the selection criteria this node will use
to choose a gateway if gw_mode was set to 'client'.
What: /sys/class/net/<mesh_iface>/mesh/hop_penalty
Date: Oct 2010
Contact: Linus Lüssing <linus.luessing@web.de>
Description:
Defines the penalty which will be applied to an
originator message's tq-field on every hop.
What: /sys/class/net/<mesh_iface>/mesh/isolation_mark
Date: Nov 2013
Contact: Antonio Quartulli <a@unstable.cc>
Description:
Defines the isolation mark (and its bitmask) which
is used to classify clients as "isolated" by the
Extended Isolation feature.
What: /sys/class/net/<mesh_iface>/mesh/multicast_mode
Date: Feb 2014
Contact: Linus Lüssing <linus.luessing@web.de>
Description:
Indicates whether multicast optimizations are enabled
or disabled. If set to zero then all nodes in the
mesh are going to use classic flooding for any
multicast packet with no optimizations.
What: /sys/class/net/<mesh_iface>/mesh/network_coding
Date: Nov 2012
Contact: Martin Hundeboll <martin@hundeboll.net>
Description:
Controls whether Network Coding (using some magic
to send fewer wifi packets but still the same
content) is enabled or not.
What: /sys/class/net/<mesh_iface>/mesh/orig_interval
Date: May 2010
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
Defines the interval in milliseconds in which batman
sends its protocol messages.
What: /sys/class/net/<mesh_iface>/mesh/routing_algo
Date: Dec 2011
Contact: Marek Lindner <mareklindner@neomailbox.ch>
Description:
Defines the routing procotol this mesh instance
uses to find the optimal paths through the mesh.

View File

@ -15,3 +15,11 @@ Description:
information with description of all internal kernel types. See information with description of all internal kernel types. See
Documentation/bpf/btf.rst for detailed description of format Documentation/bpf/btf.rst for detailed description of format
itself. itself.
What: /sys/kernel/btf/<module-name>
Date: Nov 2020
KernelVersion: 5.11
Contact: bpf@vger.kernel.org
Description:
Read-only binary attribute exposing kernel module's BTF type
information as an add-on to the kernel's BTF (/sys/kernel/btf/vmlinux).

View File

@ -120,7 +120,6 @@ configure specific aspects of kernel behavior to your liking.
unicode unicode
vga-softcursor vga-softcursor
video-output video-output
wimax/index
xfs xfs
.. only:: subproject and html .. only:: subproject and html

View File

@ -57,6 +57,7 @@ properties:
- const: per - const: per
clock-frequency: clock-frequency:
$ref: /schemas/types.yaml#/definitions/uint32
description: | description: |
The oscillator frequency driving the flexcan device, filled in by the The oscillator frequency driving the flexcan device, filled in by the
boot loader. This property should only be used the used operating system boot loader. This property should only be used the used operating system
@ -99,7 +100,7 @@ properties:
by default. by default.
0: clock source 0 (oscillator clock) 0: clock source 0 (oscillator clock)
1: clock source 1 (peripheral clock) 1: clock source 1 (peripheral clock)
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint8
default: 1 default: 1
minimum: 0 minimum: 0
maximum: 1 maximum: 1
@ -124,7 +125,7 @@ examples:
interrupts = <48 0x2>; interrupts = <48 0x2>;
interrupt-parent = <&mpic>; interrupt-parent = <&mpic>;
clock-frequency = <200000000>; clock-frequency = <200000000>;
fsl,clk-source = <0>; fsl,clk-source = /bits/ 8 <0>;
}; };
- | - |
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>

View File

@ -0,0 +1,127 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/dsa/hirschmann,hellcreek.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Hirschmann Hellcreek TSN Switch Device Tree Bindings
allOf:
- $ref: dsa.yaml#
maintainers:
- Andrew Lunn <andrew@lunn.ch>
- Florian Fainelli <f.fainelli@gmail.com>
- Vivien Didelot <vivien.didelot@gmail.com>
- Kurt Kanzenbach <kurt@linutronix.de>
description:
The Hellcreek TSN Switch IP is a 802.1Q Ethernet compliant switch. It supports
the Precision Time Protocol, Hardware Timestamping as well the Time Aware
Shaper.
properties:
compatible:
items:
- const: hirschmann,hellcreek-de1soc-r1
reg:
description:
The physical base address and size of TSN and PTP memory base
minItems: 2
maxItems: 2
reg-names:
items:
- const: tsn
- const: ptp
leds:
type: object
properties:
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^led@[01]$":
type: object
description: Hellcreek leds
$ref: ../../leds/common.yaml#
properties:
reg:
items:
- enum: [0, 1]
description: Led number
label: true
default-state: true
required:
- reg
additionalProperties: false
additionalProperties: false
required:
- compatible
- reg
- reg-names
- ethernet-ports
- leds
unevaluatedProperties: false
examples:
- |
switch0: switch@ff240000 {
compatible = "hirschmann,hellcreek-de1soc-r1";
reg = <0xff240000 0x1000>,
<0xff250000 0x1000>;
reg-names = "tsn", "ptp";
dsa,member = <0 0>;
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "cpu";
ethernet = <&gmac0>;
};
port@2 {
reg = <2>;
label = "lan0";
phy-handle = <&phy1>;
};
port@3 {
reg = <3>;
label = "lan1";
phy-handle = <&phy2>;
};
};
leds {
#address-cells = <1>;
#size-cells = <0>;
led@0 {
reg = <0>;
label = "sync_good";
default-state = "on";
};
led@1 {
reg = <1>;
label = "is_gm";
default-state = "off";
};
};
};

View File

@ -1,125 +0,0 @@
Microchip KSZ Series Ethernet switches
==================================
Required properties:
- compatible: For external switch chips, compatible string must be exactly one
of the following:
- "microchip,ksz8765"
- "microchip,ksz8794"
- "microchip,ksz8795"
- "microchip,ksz9477"
- "microchip,ksz9897"
- "microchip,ksz9896"
- "microchip,ksz9567"
- "microchip,ksz8565"
- "microchip,ksz9893"
- "microchip,ksz9563"
- "microchip,ksz8563"
Optional properties:
- reset-gpios : Should be a gpio specifier for a reset line
- microchip,synclko-125 : Set if the output SYNCLKO frequency should be set to
125MHz instead of 25MHz.
See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional
required and optional properties.
Examples:
Ethernet switch connected via SPI to the host, CPU port wired to eth0:
eth0: ethernet@10001000 {
fixed-link {
speed = <1000>;
full-duplex;
};
};
spi1: spi@f8008000 {
pinctrl-0 = <&pinctrl_spi_ksz>;
cs-gpios = <&pioC 25 0>;
id = <1>;
ksz9477: ksz9477@0 {
compatible = "microchip,ksz9477";
reg = <0>;
spi-max-frequency = <44000000>;
spi-cpha;
spi-cpol;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan1";
};
port@1 {
reg = <1>;
label = "lan2";
};
port@2 {
reg = <2>;
label = "lan3";
};
port@3 {
reg = <3>;
label = "lan4";
};
port@4 {
reg = <4>;
label = "lan5";
};
port@5 {
reg = <5>;
label = "cpu";
ethernet = <&eth0>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
ksz8565: ksz8565@0 {
compatible = "microchip,ksz8565";
reg = <0>;
spi-max-frequency = <44000000>;
spi-cpha;
spi-cpol;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan1";
};
port@1 {
reg = <1>;
label = "lan2";
};
port@2 {
reg = <2>;
label = "lan3";
};
port@3 {
reg = <3>;
label = "lan4";
};
port@6 {
reg = <6>;
label = "cpu";
ethernet = <&eth0>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
};

View File

@ -0,0 +1,148 @@
# SPDX-License-Identifier: GPL-2.0-only
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/dsa/microchip,ksz.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip KSZ Series Ethernet switches
maintainers:
- Marek Vasut <marex@denx.de>
- Woojung Huh <Woojung.Huh@microchip.com>
allOf:
- $ref: dsa.yaml#
properties:
# See Documentation/devicetree/bindings/net/dsa/dsa.yaml for a list of additional
# required and optional properties.
compatible:
enum:
- microchip,ksz8765
- microchip,ksz8794
- microchip,ksz8795
- microchip,ksz9477
- microchip,ksz9897
- microchip,ksz9896
- microchip,ksz9567
- microchip,ksz8565
- microchip,ksz9893
- microchip,ksz9563
- microchip,ksz8563
reset-gpios:
description:
Should be a gpio specifier for a reset line.
maxItems: 1
microchip,synclko-125:
$ref: /schemas/types.yaml#/definitions/flag
description:
Set if the output SYNCLKO frequency should be set to 125MHz instead of 25MHz.
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
// Ethernet switch connected via SPI to the host, CPU port wired to eth0:
eth0 {
fixed-link {
speed = <1000>;
full-duplex;
};
};
spi0 {
#address-cells = <1>;
#size-cells = <0>;
pinctrl-0 = <&pinctrl_spi_ksz>;
cs-gpios = <&pioC 25 0>;
id = <1>;
ksz9477: switch@0 {
compatible = "microchip,ksz9477";
reg = <0>;
reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>;
spi-max-frequency = <44000000>;
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan1";
};
port@1 {
reg = <1>;
label = "lan2";
};
port@2 {
reg = <2>;
label = "lan3";
};
port@3 {
reg = <3>;
label = "lan4";
};
port@4 {
reg = <4>;
label = "lan5";
};
port@5 {
reg = <5>;
label = "cpu";
ethernet = <&eth0>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
ksz8565: switch@1 {
compatible = "microchip,ksz8565";
reg = <1>;
spi-max-frequency = <44000000>;
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan1";
};
port@1 {
reg = <1>;
label = "lan2";
};
port@2 {
reg = <2>;
label = "lan3";
};
port@3 {
reg = <3>;
label = "lan4";
};
port@6 {
reg = <6>;
label = "cpu";
ethernet = <&eth0>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
};
...

View File

@ -15,6 +15,7 @@ Required properties:
- interrupts: Should contain ethernet controller interrupt - interrupts: Should contain ethernet controller interrupt
Optional properties: Optional properties:
- phy-handle: See ethernet.txt file in the same directory.
- phy-mode: See ethernet.txt file in the same directory. If the property is - phy-mode: See ethernet.txt file in the same directory. If the property is
absent, "rgmii" is assumed. Supported values are "rgmii*" and "rmii" for absent, "rgmii" is assumed. Supported values are "rgmii*" and "rmii" for
aspeed parts. Other (unknown) parts will accept any value. aspeed parts. Other (unknown) parts will accept any value.
@ -32,6 +33,9 @@ Optional properties:
- "MACCLK": The MAC IP clock - "MACCLK": The MAC IP clock
- "RCLK": Clock gate for the RMII RCLK - "RCLK": Clock gate for the RMII RCLK
Optional subnodes:
- mdio: See mdio.txt file in the same directory.
Example: Example:
mac0: ethernet@1e660000 { mac0: ethernet@1e660000 {
@ -40,3 +44,24 @@ Example:
interrupts = <2>; interrupts = <2>;
use-ncsi; use-ncsi;
}; };
Example with phy-handle:
mac1: ethernet@1e680000 {
compatible = "aspeed,ast2500-mac", "faraday,ftgmac100";
reg = <0x1e680000 0x180>;
interrupts = <2>;
phy-handle = <&phy>;
phy-mode = "rgmii";
mdio {
#address-cells = <1>;
#size-cells = <0>;
phy: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
};
};
};

View File

@ -16,6 +16,8 @@ Required properties:
Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC. Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC.
Use "cdns,zynqmp-gem" for Zynq Ultrascale+ MPSoC. Use "cdns,zynqmp-gem" for Zynq Ultrascale+ MPSoC.
Use "sifive,fu540-c000-gem" for SiFive FU540-C000 SoC. Use "sifive,fu540-c000-gem" for SiFive FU540-C000 SoC.
Use "microchip,sama7g5-emac" for Microchip SAMA7G5 ethernet interface.
Use "microchip,sama7g5-gem" for Microchip SAMA7G5 gigabit ethernet interface.
Or the generic form: "cdns,emac". Or the generic form: "cdns,emac".
- reg: Address and length of the register set for the device - reg: Address and length of the register set for the device
For "sifive,fu540-c000-gem", second range is required to specify the For "sifive,fu540-c000-gem", second range is required to specify the

View File

@ -6,11 +6,11 @@ Required properties:
- reg: address on the bus - reg: address on the bus
- interrupts: GPIO interrupt to which the chip is connected - interrupts: GPIO interrupt to which the chip is connected
- enable-gpios: Output GPIO pin used for enabling/disabling the chip - enable-gpios: Output GPIO pin used for enabling/disabling the chip
- firmware-gpios: Output GPIO pin used to enter firmware download mode
Optional SoC Specific Properties: Optional SoC Specific Properties:
- pinctrl-names: Contains only one value - "default". - pinctrl-names: Contains only one value - "default".
- pintctrl-0: Specifies the pin control groups used for this controller. - pintctrl-0: Specifies the pin control groups used for this controller.
- firmware-gpios: Output GPIO pin used to enter firmware download mode
Example (for ARM-based BeagleBone with NPC100 NFC controller on I2C2): Example (for ARM-based BeagleBone with NPC100 NFC controller on I2C2):

View File

@ -12,7 +12,9 @@ maintainers:
properties: properties:
compatible: compatible:
const: samsung,s3fwrn5-i2c enum:
- samsung,s3fwrn5-i2c
- samsung,s3fwrn82
en-gpios: en-gpios:
maxItems: 1 maxItems: 1
@ -47,10 +49,19 @@ additionalProperties: false
required: required:
- compatible - compatible
- en-gpios - en-gpios
- interrupts
- reg
- wake-gpios - wake-gpios
allOf:
- if:
properties:
compatible:
contains:
const: samsung,s3fwrn5-i2c
then:
required:
- interrupts
- reg
examples: examples:
- | - |
#include <dt-bindings/gpio/gpio.h> #include <dt-bindings/gpio/gpio.h>
@ -65,9 +76,23 @@ examples:
reg = <0x27>; reg = <0x27>;
interrupt-parent = <&gpa1>; interrupt-parent = <&gpa1>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; interrupts = <3 IRQ_TYPE_EDGE_RISING>;
en-gpios = <&gpf1 4 GPIO_ACTIVE_HIGH>; en-gpios = <&gpf1 4 GPIO_ACTIVE_HIGH>;
wake-gpios = <&gpj0 2 GPIO_ACTIVE_HIGH>; wake-gpios = <&gpj0 2 GPIO_ACTIVE_HIGH>;
}; };
}; };
# UART example on Raspberry Pi
- |
uart0 {
status = "okay";
nfc {
compatible = "samsung,s3fwrn82";
en-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
wake-gpios = <&gpio 16 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};

View File

@ -144,6 +144,12 @@ properties:
* reg * reg
* reg-names * reg-names
qcom,ath11k-calibration-variant:
$ref: /schemas/types.yaml#/definitions/string
description:
string to uniquely identify variant of the calibration data in the
board-2.bin for designs with colliding bus and device specific ids
required: required:
- compatible - compatible
- reg - reg

View File

@ -443,6 +443,8 @@ patternProperties:
description: HiDeep Inc. description: HiDeep Inc.
"^himax,.*": "^himax,.*":
description: Himax Technologies, Inc. description: Himax Technologies, Inc.
"^hirschmann,.*":
description: Hirschmann Automation and Control GmbH
"^hisilicon,.*": "^hisilicon,.*":
description: Hisilicon Limited. description: Hisilicon Limited.
"^hit,.*": "^hit,.*":

View File

@ -0,0 +1,234 @@
.. SPDX-License-Identifier: GPL-2.0-only
=============
Auxiliary Bus
=============
In some subsystems, the functionality of the core device (PCI/ACPI/other) is
too complex for a single device to be managed by a monolithic driver
(e.g. Sound Open Firmware), multiple devices might implement a common
intersection of functionality (e.g. NICs + RDMA), or a driver may want to
export an interface for another subsystem to drive (e.g. SIOV Physical Function
export Virtual Function management). A split of the functinoality into child-
devices representing sub-domains of functionality makes it possible to
compartmentalize, layer, and distribute domain-specific concerns via a Linux
device-driver model.
An example for this kind of requirement is the audio subsystem where a single
IP is handling multiple entities such as HDMI, Soundwire, local devices such as
mics/speakers etc. The split for the core's functionality can be arbitrary or
be defined by the DSP firmware topology and include hooks for test/debug. This
allows for the audio core device to be minimal and focused on hardware-specific
control and communication.
Each auxiliary_device represents a part of its parent functionality. The
generic behavior can be extended and specialized as needed by encapsulating an
auxiliary_device within other domain-specific structures and the use of .ops
callbacks. Devices on the auxiliary bus do not share any structures and the use
of a communication channel with the parent is domain-specific.
Note that ops are intended as a way to augment instance behavior within a class
of auxiliary devices, it is not the mechanism for exporting common
infrastructure from the parent. Consider EXPORT_SYMBOL_NS() to convey
infrastructure from the parent module to the auxiliary module(s).
When Should the Auxiliary Bus Be Used
=====================================
The auxiliary bus is to be used when a driver and one or more kernel modules,
who share a common header file with the driver, need a mechanism to connect and
provide access to a shared object allocated by the auxiliary_device's
registering driver. The registering driver for the auxiliary_device(s) and the
kernel module(s) registering auxiliary_drivers can be from the same subsystem,
or from multiple subsystems.
The emphasis here is on a common generic interface that keeps subsystem
customization out of the bus infrastructure.
One example is a PCI network device that is RDMA-capable and exports a child
device to be driven by an auxiliary_driver in the RDMA subsystem. The PCI
driver allocates and registers an auxiliary_device for each physical
function on the NIC. The RDMA driver registers an auxiliary_driver that claims
each of these auxiliary_devices. This conveys data/ops published by the parent
PCI device/driver to the RDMA auxiliary_driver.
Another use case is for the PCI device to be split out into multiple sub
functions. For each sub function an auxiliary_device is created. A PCI sub
function driver binds to such devices that creates its own one or more class
devices. A PCI sub function auxiliary device is likely to be contained in a
struct with additional attributes such as user defined sub function number and
optional attributes such as resources and a link to the parent device. These
attributes could be used by systemd/udev; and hence should be initialized
before a driver binds to an auxiliary_device.
A key requirement for utilizing the auxiliary bus is that there is no
dependency on a physical bus, device, register accesses or regmap support.
These individual devices split from the core cannot live on the platform bus as
they are not physical devices that are controlled by DT/ACPI. The same
argument applies for not using MFD in this scenario as MFD relies on individual
function devices being physical devices.
Auxiliary Device
================
An auxiliary_device represents a part of its parent device's functionality. It
is given a name that, combined with the registering drivers KBUILD_MODNAME,
creates a match_name that is used for driver binding, and an id that combined
with the match_name provide a unique name to register with the bus subsystem.
Registering an auxiliary_device is a two-step process. First call
auxiliary_device_init(), which checks several aspects of the auxiliary_device
struct and performs a device_initialize(). After this step completes, any
error state must have a call to auxiliary_device_uninit() in its resolution path.
The second step in registering an auxiliary_device is to perform a call to
auxiliary_device_add(), which sets the name of the device and add the device to
the bus.
Unregistering an auxiliary_device is also a two-step process to mirror the
register process. First call auxiliary_device_delete(), then call
auxiliary_device_uninit().
.. code-block:: c
struct auxiliary_device {
struct device dev;
const char *name;
u32 id;
};
If two auxiliary_devices both with a match_name "mod.foo" are registered onto
the bus, they must have unique id values (e.g. "x" and "y") so that the
registered devices names are "mod.foo.x" and "mod.foo.y". If match_name + id
are not unique, then the device_add fails and generates an error message.
The auxiliary_device.dev.type.release or auxiliary_device.dev.release must be
populated with a non-NULL pointer to successfully register the auxiliary_device.
The auxiliary_device.dev.parent must also be populated.
Auxiliary Device Memory Model and Lifespan
------------------------------------------
The registering driver is the entity that allocates memory for the
auxiliary_device and register it on the auxiliary bus. It is important to note
that, as opposed to the platform bus, the registering driver is wholly
responsible for the management for the memory used for the driver object.
A parent object, defined in the shared header file, contains the
auxiliary_device. It also contains a pointer to the shared object(s), which
also is defined in the shared header. Both the parent object and the shared
object(s) are allocated by the registering driver. This layout allows the
auxiliary_driver's registering module to perform a container_of() call to go
from the pointer to the auxiliary_device, that is passed during the call to the
auxiliary_driver's probe function, up to the parent object, and then have
access to the shared object(s).
The memory for the auxiliary_device is freed only in its release() callback
flow as defined by its registering driver.
The memory for the shared object(s) must have a lifespan equal to, or greater
than, the lifespan of the memory for the auxiliary_device. The auxiliary_driver
should only consider that this shared object is valid as long as the
auxiliary_device is still registered on the auxiliary bus. It is up to the
registering driver to manage (e.g. free or keep available) the memory for the
shared object beyond the life of the auxiliary_device.
The registering driver must unregister all auxiliary devices before its own
driver.remove() is completed.
Auxiliary Drivers
=================
Auxiliary drivers follow the standard driver model convention, where
discovery/enumeration is handled by the core, and drivers
provide probe() and remove() methods. They support power management
and shutdown notifications using the standard conventions.
.. code-block:: c
struct auxiliary_driver {
int (*probe)(struct auxiliary_device *,
const struct auxiliary_device_id *id);
void (*remove)(struct auxiliary_device *);
void (*shutdown)(struct auxiliary_device *);
int (*suspend)(struct auxiliary_device *, pm_message_t);
int (*resume)(struct auxiliary_device *);
struct device_driver driver;
const struct auxiliary_device_id *id_table;
};
Auxiliary drivers register themselves with the bus by calling
auxiliary_driver_register(). The id_table contains the match_names of auxiliary
devices that a driver can bind with.
Example Usage
=============
Auxiliary devices are created and registered by a subsystem-level core device
that needs to break up its functionality into smaller fragments. One way to
extend the scope of an auxiliary_device is to encapsulate it within a domain-
pecific structure defined by the parent device. This structure contains the
auxiliary_device and any associated shared data/callbacks needed to establish
the connection with the parent.
An example is:
.. code-block:: c
struct foo {
struct auxiliary_device auxdev;
void (*connect)(struct auxiliary_device *auxdev);
void (*disconnect)(struct auxiliary_device *auxdev);
void *data;
};
The parent device then registers the auxiliary_device by calling
auxiliary_device_init(), and then auxiliary_device_add(), with the pointer to
the auxdev member of the above structure. The parent provides a name for the
auxiliary_device that, combined with the parent's KBUILD_MODNAME, creates a
match_name that is be used for matching and binding with a driver.
Whenever an auxiliary_driver is registered, based on the match_name, the
auxiliary_driver's probe() is invoked for the matching devices. The
auxiliary_driver can also be encapsulated inside custom drivers that make the
core device's functionality extensible by adding additional domain-specific ops
as follows:
.. code-block:: c
struct my_ops {
void (*send)(struct auxiliary_device *auxdev);
void (*receive)(struct auxiliary_device *auxdev);
};
struct my_driver {
struct auxiliary_driver auxiliary_drv;
const struct my_ops ops;
};
An example of this type of usage is:
.. code-block:: c
const struct auxiliary_device_id my_auxiliary_id_table[] = {
{ .name = "foo_mod.foo_dev" },
{ },
};
const struct my_ops my_custom_ops = {
.send = my_tx,
.receive = my_rx,
};
const struct my_driver my_drv = {
.auxiliary_drv = {
.name = "myauxiliarydrv",
.id_table = my_auxiliary_id_table,
.probe = my_probe,
.remove = my_remove,
.shutdown = my_shutdown,
},
.ops = my_custom_ops,
};

View File

@ -73,6 +73,7 @@ available subsections can be seen below.
thermal/index thermal/index
fpga/index fpga/index
acpi/index acpi/index
auxiliary_bus
backlight/lp855x-driver.rst backlight/lp855x-driver.rst
connector connector
console console

View File

@ -228,20 +228,36 @@ send(2), sendto(2), sendmsg(2) and the recv* counterpart operations
on the socket as usual. There are also CAN specific socket options on the socket as usual. There are also CAN specific socket options
described below. described below.
The basic CAN frame structure and the sockaddr structure are defined The Classical CAN frame structure (aka CAN 2.0B), the CAN FD frame structure
in include/linux/can.h: and the sockaddr structure are defined in include/linux/can.h:
.. code-block:: C .. code-block:: C
struct can_frame { struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8 can_dlc; /* frame payload length in byte (0 .. 8) */ union {
/* CAN frame payload length in byte (0 .. CAN_MAX_DLEN)
* was previously named can_dlc so we need to carry that
* name for legacy support
*/
__u8 len;
__u8 can_dlc; /* deprecated */
};
__u8 __pad; /* padding */ __u8 __pad; /* padding */
__u8 __res0; /* reserved / padding */ __u8 __res0; /* reserved / padding */
__u8 __res1; /* reserved / padding */ __u8 len8_dlc; /* optional DLC for 8 byte payload length (9 .. 15) */
__u8 data[8] __attribute__((aligned(8))); __u8 data[8] __attribute__((aligned(8)));
}; };
Remark: The len element contains the payload length in bytes and should be
used instead of can_dlc. The deprecated can_dlc was misleadingly named as
it always contained the plain payload length in bytes and not the so called
'data length code' (DLC).
To pass the raw DLC from/to a Classical CAN network device the len8_dlc
element can contain values 9 .. 15 when the len element is 8 (the real
payload length for all DLC values greater or equal to 8).
The alignment of the (linear) payload data[] to a 64bit boundary The alignment of the (linear) payload data[] to a 64bit boundary
allows the user to define their own structs and unions to easily access allows the user to define their own structs and unions to easily access
the CAN payload. There is no given byteorder on the CAN bus by the CAN payload. There is no given byteorder on the CAN bus by
@ -260,6 +276,23 @@ PF_PACKET socket, that also binds to a specific interface:
/* transport protocol class address info (e.g. ISOTP) */ /* transport protocol class address info (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp; struct { canid_t rx_id, tx_id; } tp;
/* J1939 address information */
struct {
/* 8 byte name when using dynamic addressing */
__u64 name;
/* pgn:
* 8 bit: PS in PDU2 case, else 0
* 8 bit: PF
* 1 bit: DP
* 1 bit: reserved
*/
__u32 pgn;
/* 1 byte address */
__u8 addr;
} j1939;
/* reserved for future CAN protocols address information */ /* reserved for future CAN protocols address information */
} can_addr; } can_addr;
}; };
@ -371,7 +404,7 @@ kernel interfaces (ABI) which heavily rely on the CAN frame with fixed eight
bytes of payload (struct can_frame) like the CAN_RAW socket. Therefore e.g. bytes of payload (struct can_frame) like the CAN_RAW socket. Therefore e.g.
the CAN_RAW socket supports a new socket option CAN_RAW_FD_FRAMES that the CAN_RAW socket supports a new socket option CAN_RAW_FD_FRAMES that
switches the socket into a mode that allows the handling of CAN FD frames switches the socket into a mode that allows the handling of CAN FD frames
and (legacy) CAN frames simultaneously (see :ref:`socketcan-rawfd`). and Classical CAN frames simultaneously (see :ref:`socketcan-rawfd`).
The struct canfd_frame is defined in include/linux/can.h: The struct canfd_frame is defined in include/linux/can.h:
@ -397,7 +430,7 @@ code (DLC) of the struct can_frame was used as a length information as the
length and the DLC has a 1:1 mapping in the range of 0 .. 8. To preserve length and the DLC has a 1:1 mapping in the range of 0 .. 8. To preserve
the easy handling of the length information the canfd_frame.len element the easy handling of the length information the canfd_frame.len element
contains a plain length value from 0 .. 64. So both canfd_frame.len and contains a plain length value from 0 .. 64. So both canfd_frame.len and
can_frame.can_dlc are equal and contain a length information and no DLC. can_frame.len are equal and contain a length information and no DLC.
For details about the distinction of CAN and CAN FD capable devices and For details about the distinction of CAN and CAN FD capable devices and
the mapping to the bus-relevant data length code (DLC), see :ref:`socketcan-can-fd-driver`. the mapping to the bus-relevant data length code (DLC), see :ref:`socketcan-can-fd-driver`.
@ -407,7 +440,7 @@ definitions are specified for CAN specific MTUs in include/linux/can.h:
.. code-block:: C .. code-block:: C
#define CAN_MTU (sizeof(struct can_frame)) == 16 => 'legacy' CAN frame #define CAN_MTU (sizeof(struct can_frame)) == 16 => Classical CAN frame
#define CANFD_MTU (sizeof(struct canfd_frame)) == 72 => CAN FD frame #define CANFD_MTU (sizeof(struct canfd_frame)) == 72 => CAN FD frame
@ -609,7 +642,7 @@ Example:
printf("got CAN FD frame with length %d\n", cfd.len); printf("got CAN FD frame with length %d\n", cfd.len);
/* cfd.flags contains valid data */ /* cfd.flags contains valid data */
} else if (nbytes == CAN_MTU) { } else if (nbytes == CAN_MTU) {
printf("got legacy CAN frame with length %d\n", cfd.len); printf("got Classical CAN frame with length %d\n", cfd.len);
/* cfd.flags is undefined */ /* cfd.flags is undefined */
} else { } else {
fprintf(stderr, "read: invalid CAN(FD) frame\n"); fprintf(stderr, "read: invalid CAN(FD) frame\n");
@ -623,7 +656,7 @@ Example:
printf("%02X ", cfd.data[i]); printf("%02X ", cfd.data[i]);
When reading with size CANFD_MTU only returns CAN_MTU bytes that have When reading with size CANFD_MTU only returns CAN_MTU bytes that have
been received from the socket a legacy CAN frame has been read into the been received from the socket a Classical CAN frame has been read into the
provided CAN FD structure. Note that the canfd_frame.flags data field is provided CAN FD structure. Note that the canfd_frame.flags data field is
not specified in the struct can_frame and therefore it is only valid in not specified in the struct can_frame and therefore it is only valid in
CANFD_MTU sized CAN FD frames. CANFD_MTU sized CAN FD frames.
@ -633,7 +666,7 @@ Implementation hint for new CAN applications:
To build a CAN FD aware application use struct canfd_frame as basic CAN To build a CAN FD aware application use struct canfd_frame as basic CAN
data structure for CAN_RAW based applications. When the application is data structure for CAN_RAW based applications. When the application is
executed on an older Linux kernel and switching the CAN_RAW_FD_FRAMES executed on an older Linux kernel and switching the CAN_RAW_FD_FRAMES
socket option returns an error: No problem. You'll get legacy CAN frames socket option returns an error: No problem. You'll get Classical CAN frames
or CAN FD frames and can process them the same way. or CAN FD frames and can process them the same way.
When sending to CAN devices make sure that the device is capable to handle When sending to CAN devices make sure that the device is capable to handle
@ -842,6 +875,8 @@ TX_RESET_MULTI_IDX:
RX_RTR_FRAME: RX_RTR_FRAME:
Send reply for RTR-request (placed in op->frames[0]). Send reply for RTR-request (placed in op->frames[0]).
CAN_FD_FRAME:
The CAN frames following the bcm_msg_head are struct canfd_frame's
Broadcast Manager Transmission Timers Broadcast Manager Transmission Timers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1026,7 +1061,7 @@ Additional procfs files in /proc/net/can::
stats - SocketCAN core statistics (rx/tx frames, match ratios, ...) stats - SocketCAN core statistics (rx/tx frames, match ratios, ...)
reset_stats - manual statistic reset reset_stats - manual statistic reset
version - prints the SocketCAN core version and the ABI version version - prints SocketCAN core and ABI version (removed in Linux 5.10)
Writing Own CAN Protocol Modules Writing Own CAN Protocol Modules
@ -1070,7 +1105,7 @@ General Settings
dev->type = ARPHRD_CAN; /* the netdevice hardware type */ dev->type = ARPHRD_CAN; /* the netdevice hardware type */
dev->flags = IFF_NOARP; /* CAN has no arp */ dev->flags = IFF_NOARP; /* CAN has no arp */
dev->mtu = CAN_MTU; /* sizeof(struct can_frame) -> legacy CAN interface */ dev->mtu = CAN_MTU; /* sizeof(struct can_frame) -> Classical CAN interface */
or alternative, when the controller supports CAN with flexible data rate: or alternative, when the controller supports CAN with flexible data rate:
dev->mtu = CANFD_MTU; /* sizeof(struct canfd_frame) -> CAN FD interface */ dev->mtu = CANFD_MTU; /* sizeof(struct canfd_frame) -> CAN FD interface */
@ -1184,6 +1219,7 @@ Setting CAN device properties::
[ fd { on | off } ] [ fd { on | off } ]
[ fd-non-iso { on | off } ] [ fd-non-iso { on | off } ]
[ presume-ack { on | off } ] [ presume-ack { on | off } ]
[ cc-len8-dlc { on | off } ]
[ restart-ms TIME-MS ] [ restart-ms TIME-MS ]
[ restart ] [ restart ]
@ -1326,22 +1362,22 @@ arbitration phase and the payload phase of the CAN FD frame. Therefore a
second bit timing has to be specified in order to enable the CAN FD bitrate. second bit timing has to be specified in order to enable the CAN FD bitrate.
Additionally CAN FD capable CAN controllers support up to 64 bytes of Additionally CAN FD capable CAN controllers support up to 64 bytes of
payload. The representation of this length in can_frame.can_dlc and payload. The representation of this length in can_frame.len and
canfd_frame.len for userspace applications and inside the Linux network canfd_frame.len for userspace applications and inside the Linux network
layer is a plain value from 0 .. 64 instead of the CAN 'data length code'. layer is a plain value from 0 .. 64 instead of the CAN 'data length code'.
The data length code was a 1:1 mapping to the payload length in the legacy The data length code was a 1:1 mapping to the payload length in the Classical
CAN frames anyway. The payload length to the bus-relevant DLC mapping is CAN frames anyway. The payload length to the bus-relevant DLC mapping is
only performed inside the CAN drivers, preferably with the helper only performed inside the CAN drivers, preferably with the helper
functions can_dlc2len() and can_len2dlc(). functions can_fd_dlc2len() and can_fd_len2dlc().
The CAN netdevice driver capabilities can be distinguished by the network The CAN netdevice driver capabilities can be distinguished by the network
devices maximum transfer unit (MTU):: devices maximum transfer unit (MTU)::
MTU = 16 (CAN_MTU) => sizeof(struct can_frame) => 'legacy' CAN device MTU = 16 (CAN_MTU) => sizeof(struct can_frame) => Classical CAN device
MTU = 72 (CANFD_MTU) => sizeof(struct canfd_frame) => CAN FD capable device MTU = 72 (CANFD_MTU) => sizeof(struct canfd_frame) => CAN FD capable device
The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall. The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall.
N.B. CAN FD capable devices can also handle and send legacy CAN frames. N.B. CAN FD capable devices can also handle and send Classical CAN frames.
When configuring CAN FD capable CAN controllers an additional 'data' bitrate When configuring CAN FD capable CAN controllers an additional 'data' bitrate
has to be set. This bitrate for the data phase of the CAN FD frame has to be has to be set. This bitrate for the data phase of the CAN FD frame has to be

View File

@ -12,6 +12,7 @@ Contents
- `Overview`_ - `Overview`_
- `Drivers`_ - `Drivers`_
- `Basic packet flow`_ - `Basic packet flow`_
- `Devlink health reporters`_
Overview Overview
======== ========
@ -157,3 +158,52 @@ Egress
3. The SQ descriptor ring is maintained in buffers allocated from SQ mapped pool of NPA block LF. 3. The SQ descriptor ring is maintained in buffers allocated from SQ mapped pool of NPA block LF.
4. NIX block transmits the pkt on the designated channel. 4. NIX block transmits the pkt on the designated channel.
5. NPC MCAM entries can be installed to divert pkt onto a different channel. 5. NPC MCAM entries can be installed to divert pkt onto a different channel.
Devlink health reporters
========================
NPA Reporters
-------------
The NPA reporters are responsible for reporting and recovering the following group of errors
1. GENERAL events
- Error due to operation of unmapped PF.
- Error due to disabled alloc/free for other HW blocks (NIX, SSO, TIM, DPI and AURA).
2. ERROR events
- Fault due to NPA_AQ_INST_S read or NPA_AQ_RES_S write.
- AQ Doorbell Error.
3. RAS events
- RAS Error Reporting for NPA_AQ_INST_S/NPA_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 2872 recover 2872 last_dump_date 2020-12-10 last_dump_time 09:39:09 grace_period 0 auto_recover true auto_dump true
reporter hw_npa_gen
state healthy error 2872 recover 2872 last_dump_date 2020-12-11 last_dump_time 04:43:04 grace_period 0 auto_recover true auto_dump true
reporter hw_npa_err
state healthy error 2871 recover 2871 last_dump_date 2020-12-10 last_dump_time 09:39:17 grace_period 0 auto_recover true auto_dump true
reporter hw_npa_ras
state healthy error 0 recover 0 last_dump_date 2020-12-10 last_dump_time 09:32:40 grace_period 0 auto_recover true auto_dump true
Each reporter dumps the
- Error Type
- Error Register value
- Reason in words
For eg:
~# devlink health dump show pci/0002:01:00.0 reporter hw_npa_gen
NPA_AF_GENERAL:
NPA General Interrupt Reg : 1
NIX0: free disabled RX
~# devlink health dump show pci/0002:01:00.0 reporter hw_npa_intr
NPA_AF_RVU:
NPA RVU Interrupt Reg : 1
Unmap Slot Error
~# devlink health dump show pci/0002:01:00.0 reporter hw_npa_err
NPA_AF_ERR:
NPA Error Interrupt Reg : 4096
AQ Doorbell Error

View File

@ -476,6 +476,10 @@ be added to the following table:
* - ``esp_parsing`` * - ``esp_parsing``
- ``drop`` - ``drop``
- Traps packets dropped due to an error in the ESP header parsing - Traps packets dropped due to an error in the ESP header parsing
* - ``blackhole_nexthop``
- ``drop``
- Traps packets that the device decided to drop in case they hit a
blackhole nexthop
Driver-specific Packet Traps Driver-specific Packet Traps
============================ ============================

View File

@ -46,7 +46,7 @@ Resources
========= =========
The ``netdevsim`` driver exposes resources to control the number of FIB The ``netdevsim`` driver exposes resources to control the number of FIB
entries and FIB rule entries that the driver will allow. entries, FIB rule entries and nexthops that the driver will allow.
.. code:: shell .. code:: shell
@ -54,6 +54,7 @@ entries and FIB rule entries that the driver will allow.
$ devlink resource set netdevsim/netdevsim0 path /IPv4/fib-rules size 16 $ devlink resource set netdevsim/netdevsim0 path /IPv4/fib-rules size 16
$ devlink resource set netdevsim/netdevsim0 path /IPv6/fib size 64 $ devlink resource set netdevsim/netdevsim0 path /IPv6/fib size 64
$ devlink resource set netdevsim/netdevsim0 path /IPv6/fib-rules size 16 $ devlink resource set netdevsim/netdevsim0 path /IPv6/fib-rules size 16
$ devlink resource set netdevsim/netdevsim0 path /nexthops size 16
$ devlink dev reload netdevsim/netdevsim0 $ devlink dev reload netdevsim/netdevsim0
Driver-specific Traps Driver-specific Traps

View File

@ -1,44 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0
================
Frame Relay (FR)
================
Frame Relay (FR) support for linux is built into a two tiered system of device
drivers. The upper layer implements RFC1490 FR specification, and uses the
Data Link Connection Identifier (DLCI) as its hardware address. Usually these
are assigned by your network supplier, they give you the number/numbers of
the Virtual Connections (VC) assigned to you.
Each DLCI is a point-to-point link between your machine and a remote one.
As such, a separate device is needed to accommodate the routing. Within the
net-tools archives is 'dlcicfg'. This program will communicate with the
base "DLCI" device, and create new net devices named 'dlci00', 'dlci01'...
The configuration script will ask you how many DLCIs you need, as well as
how many DLCIs you want to assign to each Frame Relay Access Device (FRAD).
The DLCI uses a number of function calls to communicate with the FRAD, all
of which are stored in the FRAD's private data area. assoc/deassoc,
activate/deactivate and dlci_config. The DLCI supplies a receive function
to the FRAD to accept incoming packets.
With this initial offering, only 1 FRAD driver is available. With many thanks
to Sangoma Technologies, David Mandelstam & Gene Kozin, the S502A, S502E &
S508 are supported. This driver is currently set up for only FR, but as
Sangoma makes more firmware modules available, it can be updated to provide
them as well.
Configuration of the FRAD makes use of another net-tools program, 'fradcfg'.
This program makes use of a configuration file (which dlcicfg can also read)
to specify the types of boards to be configured as FRADs, as well as perform
any board specific configuration. The Sangoma module of fradcfg loads the
FR firmware into the card, sets the irq/port/memory information, and provides
an initial configuration.
Additional FRAD device drivers can be added as hardware is available.
At this time, the dlcicfg and fradcfg programs have not been incorporated into
the net-tools distribution. They can be found at ftp.invlogic.com, in
/pub/linux. Note that with OS/2 FTPD, you end up in /pub by default, so just
use 'cd linux'. v0.10 is for use on pre-2.0.3 and earlier, v0.15 is for
pre-2.0.4 and later.

View File

@ -52,7 +52,6 @@ Contents:
eql eql
fib_trie fib_trie
filter filter
framerelay
generic-hdlc generic-hdlc
generic_netlink generic_netlink
gen_stats gen_stats
@ -70,6 +69,7 @@ Contents:
lapb-module lapb-module
mac80211-injection mac80211-injection
mpls-sysctl mpls-sysctl
mptcp-sysctl
multiqueue multiqueue
netconsole netconsole
netdev-features netdev-features
@ -101,6 +101,7 @@ Contents:
tcp-thin tcp-thin
team team
timestamping timestamping
tipc
tproxy tproxy
tuntap tuntap
udplite udplite

View File

@ -1554,6 +1554,9 @@ igmpv3_unsolicited_report_interval - INTEGER
Default: 1000 (1 seconds) Default: 1000 (1 seconds)
ignore_routes_with_linkdown - BOOLEAN
Ignore routes whose link is down when performing a FIB lookup.
promote_secondaries - BOOLEAN promote_secondaries - BOOLEAN
When a primary IP address is removed from this interface When a primary IP address is removed from this interface
promote a corresponding secondary IP address instead of promote a corresponding secondary IP address instead of
@ -2642,6 +2645,37 @@ addr_scope_policy - INTEGER
Default: 1 Default: 1
udp_port - INTEGER
The listening port for the local UDP tunneling sock. Normally it's
using the IANA-assigned UDP port number 9899 (sctp-tunneling).
This UDP sock is used for processing the incoming UDP-encapsulated
SCTP packets (from RFC6951), and shared by all applications in the
same net namespace. This UDP sock will be closed when the value is
set to 0.
The value will also be used to set the src port of the UDP header
for the outgoing UDP-encapsulated SCTP packets. For the dest port,
please refer to 'encap_port' below.
Default: 0
encap_port - INTEGER
The default remote UDP encapsulation port.
This value is used to set the dest port of the UDP header for the
outgoing UDP-encapsulated SCTP packets by default. Users can also
change the value for each sock/asoc/transport by using setsockopt.
For further information, please refer to RFC6951.
Note that when connecting to a remote server, the client should set
this to the port that the UDP tunneling sock on the peer server is
listening to and the local UDP tunneling sock on the client also
must be started. On the server, it would get the encap_port from
the incoming packet's source port.
Default: 0
``/proc/sys/net/core/*`` ``/proc/sys/net/core/*``
======================== ========================

View File

@ -69,18 +69,56 @@ J1939 concepts
PGN PGN
--- ---
The J1939 protocol uses the 29-bit CAN identifier with the following structure:
============ ============== ====================
29 bit CAN-ID
--------------------------------------------------
Bit positions within the CAN-ID
--------------------------------------------------
28 ... 26 25 ... 8 7 ... 0
============ ============== ====================
Priority PGN SA (Source Address)
============ ============== ====================
The PGN (Parameter Group Number) is a number to identify a packet. The PGN The PGN (Parameter Group Number) is a number to identify a packet. The PGN
is composed as follows: is composed as follows:
1 bit : Reserved Bit
1 bit : Data Page ============ ============== ================= =================
8 bits : PF (PDU Format) PGN
8 bits : PS (PDU Specific) ------------------------------------------------------------------
Bit positions within the CAN-ID
------------------------------------------------------------------
25 24 23 ... 16 15 ... 8
============ ============== ================= =================
R (Reserved) DP (Data Page) PF (PDU Format) PS (PDU Specific)
============ ============== ================= =================
In J1939-21 distinction is made between PDU1 format (where PF < 240) and PDU2 In J1939-21 distinction is made between PDU1 format (where PF < 240) and PDU2
format (where PF >= 240). Furthermore, when using the PDU2 format, the PS-field format (where PF >= 240). Furthermore, when using the PDU2 format, the PS-field
contains a so-called Group Extension, which is part of the PGN. When using PDU2 contains a so-called Group Extension, which is part of the PGN. When using PDU2
format, the Group Extension is set in the PS-field. format, the Group Extension is set in the PS-field.
============== ========================
PDU1 Format (specific) (peer to peer)
----------------------------------------
Bit positions within the CAN-ID
----------------------------------------
23 ... 16 15 ... 8
============== ========================
00h ... EFh DA (Destination address)
============== ========================
============== ========================
PDU2 Format (global) (broadcast)
----------------------------------------
Bit positions within the CAN-ID
----------------------------------------
23 ... 16 15 ... 8
============== ========================
F0h ... FFh GE (Group Extenstion)
============== ========================
On the other hand, when using PDU1 format, the PS-field contains a so-called On the other hand, when using PDU1 format, the PS-field contains a so-called
Destination Address, which is _not_ part of the PGN. When communicating a PGN Destination Address, which is _not_ part of the PGN. When communicating a PGN
from user space to kernel (or vice versa) and PDU2 format is used, the PS-field from user space to kernel (or vice versa) and PDU2 format is used, the PS-field

View File

@ -83,27 +83,6 @@ SUN RPC subsystem
.. kernel-doc:: net/sunrpc/clnt.c .. kernel-doc:: net/sunrpc/clnt.c
:export: :export:
WiMAX
-----
.. kernel-doc:: net/wimax/op-msg.c
:export:
.. kernel-doc:: net/wimax/op-reset.c
:export:
.. kernel-doc:: net/wimax/op-rfkill.c
:export:
.. kernel-doc:: net/wimax/stack.c
:export:
.. kernel-doc:: include/net/wimax.h
:internal:
.. kernel-doc:: include/uapi/linux/wimax.h
:internal:
Network device support Network device support
====================== ======================

View File

@ -0,0 +1,26 @@
.. SPDX-License-Identifier: GPL-2.0
=====================
MPTCP Sysfs variables
=====================
/proc/sys/net/mptcp/* Variables
===============================
enabled - INTEGER
Control whether MPTCP sockets can be created.
MPTCP sockets can be created if the value is nonzero. This is
a per-namespace sysctl.
Default: 1
add_addr_timeout - INTEGER (seconds)
Set the timeout after which an ADD_ADDR control message will be
resent to an MPTCP peer that has not acknowledged a previous
ADD_ADDR message.
The default value matches TCP_RTO_MAX. This is a per-namespace
sysctl.
Default: 120

View File

@ -97,6 +97,14 @@ a page will cause no race conditions is enough.
* page_pool_get_dma_dir(): Retrieve the stored DMA direction. * page_pool_get_dma_dir(): Retrieve the stored DMA direction.
* page_pool_put_page_bulk(): Tries to refill a number of pages into the
ptr_ring cache holding ptr_ring producer lock. If the ptr_ring is full,
page_pool_put_page_bulk() will release leftover pages to the page allocator.
page_pool_put_page_bulk() is suitable to be run inside the driver NAPI tx
completion loop for the XDP_REDIRECT use case.
Please note the caller must not use data area after running
page_pool_put_page_bulk(), as this function overwrites it.
Coding examples Coding examples
=============== ===============

View File

@ -314,6 +314,22 @@ channel are:
it is connected to. It will return an EINVAL error if the channel it is connected to. It will return an EINVAL error if the channel
is not connected to an interface. is not connected to an interface.
* PPPIOCBRIDGECHAN bridges a channel with another. The argument should
point to an int containing the channel number of the channel to bridge
to. Once two channels are bridged, frames presented to one channel by
ppp_input() are passed to the bridge instance for onward transmission.
This allows frames to be switched from one channel into another: for
example, to pass PPPoE frames into a PPPoL2TP session. Since channel
bridging interrupts the normal ppp_input() path, a given channel may
not be part of a bridge at the same time as being part of a unit.
This ioctl will return an EALREADY error if the channel is already
part of a bridge or unit, or ENXIO if the requested channel does not
exist.
* PPPIOCUNBRIDGECHAN performs the inverse of PPPIOCBRIDGECHAN, unbridging
a channel pair. This ioctl will return an EINVAL error if the channel
does not form part of a bridge.
* All other ioctl commands are passed to the channel ioctl() function. * All other ioctl commands are passed to the channel ioctl() function.
The ioctl calls that are available on an instance that is attached to The ioctl calls that are available on an instance that is attached to

View File

@ -0,0 +1,100 @@
.. SPDX-License-Identifier: GPL-2.0
=================
Linux Kernel TIPC
=================
TIPC (Transparent Inter Process Communication) is a protocol that is
specially designed for intra-cluster communication.
For more information about TIPC, see http://tipc.sourceforge.net.
TIPC Base Types
---------------
.. kernel-doc:: net/tipc/subscr.h
:internal:
.. kernel-doc:: net/tipc/bearer.h
:internal:
.. kernel-doc:: net/tipc/name_table.h
:internal:
.. kernel-doc:: net/tipc/name_distr.h
:internal:
.. kernel-doc:: net/tipc/bcast.c
:internal:
TIPC Bearer Interfaces
----------------------
.. kernel-doc:: net/tipc/bearer.c
:internal:
.. kernel-doc:: net/tipc/udp_media.c
:internal:
TIPC Crypto Interfaces
----------------------
.. kernel-doc:: net/tipc/crypto.c
:internal:
TIPC Discoverer Interfaces
--------------------------
.. kernel-doc:: net/tipc/discover.c
:internal:
TIPC Link Interfaces
--------------------
.. kernel-doc:: net/tipc/link.c
:internal:
TIPC msg Interfaces
-------------------
.. kernel-doc:: net/tipc/msg.c
:internal:
TIPC Name Interfaces
--------------------
.. kernel-doc:: net/tipc/name_table.c
:internal:
.. kernel-doc:: net/tipc/name_distr.c
:internal:
TIPC Node Management Interfaces
-------------------------------
.. kernel-doc:: net/tipc/node.c
:internal:
TIPC Socket Interfaces
----------------------
.. kernel-doc:: net/tipc/socket.c
:internal:
TIPC Network Topology Interfaces
--------------------------------
.. kernel-doc:: net/tipc/subscr.c
:internal:
TIPC Server Interfaces
----------------------
.. kernel-doc:: net/tipc/topsrv.c
:internal:
TIPC Trace Interfaces
---------------------
.. kernel-doc:: net/tipc/trace.c
:internal:

View File

@ -524,7 +524,13 @@ on TCP retransmissions to handle corner cases is not acceptable.
TLS device features TLS device features
------------------- -------------------
Drivers should ignore the changes to TLS the device feature flags. Drivers should ignore the changes to the TLS device feature flags.
These flags will be acted upon accordingly by the core ``ktls`` code. These flags will be acted upon accordingly by the core ``ktls`` code.
TLS device feature flags only control adding of new TLS connection TLS device feature flags only control adding of new TLS connection
offloads, old connections will remain active after flags are cleared. offloads, old connections will remain active after flags are cleared.
TLS encryption cannot be offloaded to devices without checksum calculation
offload. Hence, TLS TX device feature flag requires NETIF_F_HW_CSUM being set.
Disabling the latter implies clearing the former. Disabling TX checksum offload
should not affect old connections, and drivers should make sure checksum
calculation does not break for them.

View File

@ -19,13 +19,11 @@ implementation of LAPB. Therefore the LAPB modules would be called by
unintelligent X.25 card drivers and not by intelligent ones, this would unintelligent X.25 card drivers and not by intelligent ones, this would
provide a uniform device driver interface, and simplify configuration. provide a uniform device driver interface, and simplify configuration.
To confuse matters a little, an 802.2 LLC implementation for Linux is being To confuse matters a little, an 802.2 LLC implementation is also possible
written which will allow X.25 to be run over an Ethernet (or Token Ring) and which could allow X.25 to be run over an Ethernet (or Token Ring) and
conform with the JNT "Pink Book", this will have a different interface to conform with the JNT "Pink Book", this would have a different interface to
the Packet Layer but there will be no confusion since the class of device the Packet Layer but there would be no confusion since the class of device
being served by the LLC will be completely separate from LAPB. The LLC being served by the LLC would be completely separate from LAPB.
implementation is being done as part of another protocol project (SNA) and
by a different author.
Just when you thought that it could not become more confusing, another Just when you thought that it could not become more confusing, another
option appeared, XOT. This allows X.25 Packet Layer frames to operate over option appeared, XOT. This allows X.25 Packet Layer frames to operate over

View File

@ -84,7 +84,6 @@ PPP_MAGIC 0x5002 ppp ``include/linux/
SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h`` SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c`` STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c``
X25_ASY_MAGIC 0x5303 x25_asy ``drivers/net/x25_asy.h``
SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h`` SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h``
AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h`` AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h``
TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h`` TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h``

View File

@ -90,7 +90,6 @@ PPP_MAGIC 0x5002 ppp ``include/linux/
SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h`` SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c`` STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c``
X25_ASY_MAGIC 0x5303 x25_asy ``drivers/net/x25_asy.h``
SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h`` SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h``
AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h`` AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h``
TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h`` TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h``

View File

@ -114,7 +114,6 @@ Todolist:
unicode unicode
vga-softcursor vga-softcursor
video-output video-output
wimax/index
xfs xfs
.. only:: subproject and html .. only:: subproject and html

View File

@ -73,7 +73,6 @@ PPP_MAGIC 0x5002 ppp ``include/linux/
SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h`` SSTATE_MAGIC 0x5302 serial_state ``include/linux/serial.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c`` STRIP_MAGIC 0x5303 strip ``drivers/net/strip.c``
X25_ASY_MAGIC 0x5303 x25_asy ``drivers/net/x25_asy.h``
SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h`` SIXPACK_MAGIC 0x5304 sixpack ``drivers/net/hamradio/6pack.h``
AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h`` AX25_MAGIC 0x5316 ax_disp ``drivers/net/mkiss.h``
TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h`` TTY_MAGIC 0x5401 tty_struct ``include/linux/tty.h``

View File

@ -3122,8 +3122,6 @@ Q: https://patchwork.open-mesh.org/project/batman/list/
B: https://www.open-mesh.org/projects/batman-adv/issues B: https://www.open-mesh.org/projects/batman-adv/issues
C: irc://chat.freenode.net/batman C: irc://chat.freenode.net/batman
T: git https://git.open-mesh.org/linux-merge.git T: git https://git.open-mesh.org/linux-merge.git
F: Documentation/ABI/obsolete/sysfs-class-net-batman-adv
F: Documentation/ABI/obsolete/sysfs-class-net-mesh
F: Documentation/networking/batman-adv.rst F: Documentation/networking/batman-adv.rst
F: include/uapi/linux/batadv_packet.h F: include/uapi/linux/batadv_packet.h
F: include/uapi/linux/batman_adv.h F: include/uapi/linux/batman_adv.h
@ -3207,8 +3205,9 @@ F: drivers/mtd/devices/block2mtd.c
BLUETOOTH DRIVERS BLUETOOTH DRIVERS
M: Marcel Holtmann <marcel@holtmann.org> M: Marcel Holtmann <marcel@holtmann.org>
M: Johan Hedberg <johan.hedberg@gmail.com> M: Johan Hedberg <johan.hedberg@gmail.com>
M: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
L: linux-bluetooth@vger.kernel.org L: linux-bluetooth@vger.kernel.org
S: Maintained S: Supported
W: http://www.bluez.org/ W: http://www.bluez.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
@ -3217,8 +3216,9 @@ F: drivers/bluetooth/
BLUETOOTH SUBSYSTEM BLUETOOTH SUBSYSTEM
M: Marcel Holtmann <marcel@holtmann.org> M: Marcel Holtmann <marcel@holtmann.org>
M: Johan Hedberg <johan.hedberg@gmail.com> M: Johan Hedberg <johan.hedberg@gmail.com>
M: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
L: linux-bluetooth@vger.kernel.org L: linux-bluetooth@vger.kernel.org
S: Maintained S: Supported
W: http://www.bluez.org/ W: http://www.bluez.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
@ -6935,12 +6935,6 @@ S: Maintained
W: http://floatingpoint.sourceforge.net/emulator/index.html W: http://floatingpoint.sourceforge.net/emulator/index.html
F: arch/x86/math-emu/ F: arch/x86/math-emu/
FRAME RELAY DLCI/FRAD (Sangoma drivers too)
L: netdev@vger.kernel.org
S: Orphan
F: drivers/net/wan/dlci.c
F: drivers/net/wan/sdla.c
FRAMEBUFFER LAYER FRAMEBUFFER LAYER
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
L: linux-fbdev@vger.kernel.org L: linux-fbdev@vger.kernel.org
@ -7938,6 +7932,15 @@ F: include/linux/hippidevice.h
F: include/uapi/linux/if_hippi.h F: include/uapi/linux/if_hippi.h
F: net/802/hippi.c F: net/802/hippi.c
HIRSCHMANN HELLCREEK ETHERNET SWITCH DRIVER
M: Kurt Kanzenbach <kurt@linutronix.de>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml
F: drivers/net/dsa/hirschmann/*
F: include/linux/platform_data/hirschmann-hellcreek.h
F: net/dsa/tag_hellcreek.c
HISILICON DMA DRIVER HISILICON DMA DRIVER
M: Zhou Wang <wangzhou1@hisilicon.com> M: Zhou Wang <wangzhou1@hisilicon.com>
L: dmaengine@vger.kernel.org L: dmaengine@vger.kernel.org
@ -9141,16 +9144,6 @@ W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
F: drivers/net/wireless/intel/iwlwifi/ F: drivers/net/wireless/intel/iwlwifi/
INTEL WIRELESS WIMAX CONNECTION 2400
M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
M: linux-wimax@intel.com
L: wimax@linuxwimax.org (subscribers-only)
S: Supported
W: http://linuxwimax.org
F: Documentation/admin-guide/wimax/i2400m.rst
F: drivers/net/wimax/i2400m/
F: include/uapi/linux/wimax/i2400m.h
INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER
M: Jithu Joseph <jithu.joseph@intel.com> M: Jithu Joseph <jithu.joseph@intel.com>
R: Maurice Ma <maurice.ma@intel.com> R: Maurice Ma <maurice.ma@intel.com>
@ -10540,6 +10533,7 @@ M: Srujana Challa <schalla@marvell.com>
L: linux-crypto@vger.kernel.org L: linux-crypto@vger.kernel.org
S: Maintained S: Maintained
F: drivers/crypto/marvell/ F: drivers/crypto/marvell/
F: include/linux/soc/marvell/octeontx2/
MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2) MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2)
M: Mirko Lindner <mlindner@marvell.com> M: Mirko Lindner <mlindner@marvell.com>
@ -10583,6 +10577,14 @@ L: netdev@vger.kernel.org
S: Maintained S: Maintained
F: drivers/net/ethernet/marvell/mvneta.* F: drivers/net/ethernet/marvell/mvneta.*
MARVELL MVPP2 ETHERNET DRIVER
M: Marcin Wojtas <mw@semihalf.com>
M: Russell King <linux@armlinux.org.uk>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/marvell-pp2.txt
F: drivers/net/ethernet/marvell/mvpp2/
MARVELL MWIFIEX WIRELESS DRIVER MARVELL MWIFIEX WIRELESS DRIVER
M: Amitkumar Karwar <amitkarwar@gmail.com> M: Amitkumar Karwar <amitkarwar@gmail.com>
M: Ganapathi Bhat <ganapathi.bhat@nxp.com> M: Ganapathi Bhat <ganapathi.bhat@nxp.com>
@ -10612,6 +10614,7 @@ M: hariprasad <hkelam@marvell.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
S: Supported S: Supported
F: drivers/net/ethernet/marvell/octeontx2/nic/ F: drivers/net/ethernet/marvell/octeontx2/nic/
F: include/linux/soc/marvell/octeontx2/
MARVELL OCTEONTX2 RVU ADMIN FUNCTION DRIVER MARVELL OCTEONTX2 RVU ADMIN FUNCTION DRIVER
M: Sunil Goutham <sgoutham@marvell.com> M: Sunil Goutham <sgoutham@marvell.com>
@ -11588,7 +11591,7 @@ M: Woojung Huh <woojung.huh@microchip.com>
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com> M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/net/dsa/ksz.txt F: Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml
F: drivers/net/dsa/microchip/* F: drivers/net/dsa/microchip/*
F: include/linux/platform_data/microchip-ksz.h F: include/linux/platform_data/microchip-ksz.h
F: net/dsa/tag_ksz.c F: net/dsa/tag_ksz.c
@ -12346,6 +12349,7 @@ L: mptcp@lists.01.org
S: Maintained S: Maintained
W: https://github.com/multipath-tcp/mptcp_net-next/wiki W: https://github.com/multipath-tcp/mptcp_net-next/wiki
B: https://github.com/multipath-tcp/mptcp_net-next/issues B: https://github.com/multipath-tcp/mptcp_net-next/issues
F: Documentation/networking/mptcp-sysctl.rst
F: include/net/mptcp.h F: include/net/mptcp.h
F: include/uapi/linux/mptcp.h F: include/uapi/linux/mptcp.h
F: net/mptcp/ F: net/mptcp/
@ -18999,18 +19003,6 @@ S: Supported
W: https://wireless.wiki.kernel.org/en/users/Drivers/wil6210 W: https://wireless.wiki.kernel.org/en/users/Drivers/wil6210
F: drivers/net/wireless/ath/wil6210/ F: drivers/net/wireless/ath/wil6210/
WIMAX STACK
M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
M: linux-wimax@intel.com
L: wimax@linuxwimax.org (subscribers-only)
S: Supported
W: http://linuxwimax.org
F: Documentation/admin-guide/wimax/wimax.rst
F: include/linux/wimax/debug.h
F: include/net/wimax.h
F: include/uapi/linux/wimax.h
F: net/wimax/
WINBOND CIR DRIVER WINBOND CIR DRIVER
M: David Härdeman <david@hardeman.nu> M: David Härdeman <david@hardeman.nu>
S: Maintained S: Maintained

View File

@ -124,6 +124,9 @@
#define SO_DETACH_REUSEPORT_BPF 68 #define SO_DETACH_REUSEPORT_BPF 68
#define SO_PREFER_BUSY_POLL 69
#define SO_BUSY_POLL_BUDGET 70
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 #if __BITS_PER_LONG == 64

View File

@ -141,7 +141,6 @@ CONFIG_HDLC_CISCO=m
CONFIG_HDLC_FR=m CONFIG_HDLC_FR=m
CONFIG_HDLC_PPP=m CONFIG_HDLC_PPP=m
CONFIG_HDLC_X25=m CONFIG_HDLC_X25=m
CONFIG_DLCI=m
CONFIG_WAN_ROUTER_DRIVERS=m CONFIG_WAN_ROUTER_DRIVERS=m
CONFIG_ATM_TCP=m CONFIG_ATM_TCP=m
# CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_KEYBOARD is not set

View File

@ -228,9 +228,7 @@ CONFIG_FARSYNC=m
CONFIG_DSCC4=m CONFIG_DSCC4=m
CONFIG_DSCC4_PCISYNC=y CONFIG_DSCC4_PCISYNC=y
CONFIG_DSCC4_PCI_RST=y CONFIG_DSCC4_PCI_RST=y
CONFIG_DLCI=m
CONFIG_LAPBETHER=m CONFIG_LAPBETHER=m
CONFIG_X25_ASY=m
# CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set # CONFIG_SERIO is not set

View File

@ -378,9 +378,7 @@ CONFIG_FARSYNC=m
CONFIG_DSCC4=m CONFIG_DSCC4=m
CONFIG_DSCC4_PCISYNC=y CONFIG_DSCC4_PCISYNC=y
CONFIG_DSCC4_PCI_RST=y CONFIG_DSCC4_PCI_RST=y
CONFIG_DLCI=m
CONFIG_LAPBETHER=m CONFIG_LAPBETHER=m
CONFIG_X25_ASY=m
# CONFIG_KEYBOARD_ATKBD is not set # CONFIG_KEYBOARD_ATKBD is not set
CONFIG_KEYBOARD_GPIO=y CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_MOUSE is not set

View File

@ -135,6 +135,9 @@
#define SO_DETACH_REUSEPORT_BPF 68 #define SO_DETACH_REUSEPORT_BPF 68
#define SO_PREFER_BUSY_POLL 69
#define SO_BUSY_POLL_BUDGET 70
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 #if __BITS_PER_LONG == 64

View File

@ -116,6 +116,9 @@
#define SO_DETACH_REUSEPORT_BPF 0x4042 #define SO_DETACH_REUSEPORT_BPF 0x4042
#define SO_PREFER_BUSY_POLL 0x4043
#define SO_BUSY_POLL_BUDGET 0x4044
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 #if __BITS_PER_LONG == 64

View File

@ -117,6 +117,9 @@
#define SO_DETACH_REUSEPORT_BPF 0x0047 #define SO_DETACH_REUSEPORT_BPF 0x0047
#define SO_PREFER_BUSY_POLL 0x0048
#define SO_BUSY_POLL_BUDGET 0x0049
#if !defined(__KERNEL__) #if !defined(__KERNEL__)

View File

@ -765,8 +765,7 @@ static void lanai_shutdown_tx_vci(struct lanai_dev *lanai,
struct sk_buff *skb; struct sk_buff *skb;
unsigned long flags, timeout; unsigned long flags, timeout;
int read, write, lastread = -1; int read, write, lastread = -1;
APRINTK(!in_interrupt(),
"lanai_shutdown_tx_vci called w/o process context!\n");
if (lvcc->vbase == NULL) /* We were never bound to a VCI */ if (lvcc->vbase == NULL) /* We were never bound to a VCI */
return; return;
/* 15.2.1 - wait for queue to drain */ /* 15.2.1 - wait for queue to drain */

View File

@ -130,8 +130,9 @@ static int ns_open(struct atm_vcc *vcc);
static void ns_close(struct atm_vcc *vcc); static void ns_close(struct atm_vcc *vcc);
static void fill_tst(ns_dev * card, int n, vc_map * vc); static void fill_tst(ns_dev * card, int n, vc_map * vc);
static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb); static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb);
static int ns_send_bh(struct atm_vcc *vcc, struct sk_buff *skb);
static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd, static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
struct sk_buff *skb); struct sk_buff *skb, bool may_sleep);
static void process_tsq(ns_dev * card); static void process_tsq(ns_dev * card);
static void drain_scq(ns_dev * card, scq_info * scq, int pos); static void drain_scq(ns_dev * card, scq_info * scq, int pos);
static void process_rsq(ns_dev * card); static void process_rsq(ns_dev * card);
@ -160,6 +161,7 @@ static const struct atmdev_ops atm_ops = {
.close = ns_close, .close = ns_close,
.ioctl = ns_ioctl, .ioctl = ns_ioctl,
.send = ns_send, .send = ns_send,
.send_bh = ns_send_bh,
.phy_put = ns_phy_put, .phy_put = ns_phy_put,
.phy_get = ns_phy_get, .phy_get = ns_phy_get,
.proc_read = ns_proc_read, .proc_read = ns_proc_read,
@ -1620,7 +1622,7 @@ static void fill_tst(ns_dev * card, int n, vc_map * vc)
card->tst_addr = new_tst; card->tst_addr = new_tst;
} }
static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb) static int _ns_send(struct atm_vcc *vcc, struct sk_buff *skb, bool may_sleep)
{ {
ns_dev *card; ns_dev *card;
vc_map *vc; vc_map *vc;
@ -1704,7 +1706,7 @@ static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb)
scq = card->scq0; scq = card->scq0;
} }
if (push_scqe(card, vc, scq, &scqe, skb) != 0) { if (push_scqe(card, vc, scq, &scqe, skb, may_sleep) != 0) {
atomic_inc(&vcc->stats->tx_err); atomic_inc(&vcc->stats->tx_err);
dma_unmap_single(&card->pcidev->dev, NS_PRV_DMA(skb), skb->len, dma_unmap_single(&card->pcidev->dev, NS_PRV_DMA(skb), skb->len,
DMA_TO_DEVICE); DMA_TO_DEVICE);
@ -1716,8 +1718,18 @@ static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb)
return 0; return 0;
} }
static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb)
{
return _ns_send(vcc, skb, true);
}
static int ns_send_bh(struct atm_vcc *vcc, struct sk_buff *skb)
{
return _ns_send(vcc, skb, false);
}
static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd, static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
struct sk_buff *skb) struct sk_buff *skb, bool may_sleep)
{ {
unsigned long flags; unsigned long flags;
ns_scqe tsr; ns_scqe tsr;
@ -1728,7 +1740,7 @@ static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
spin_lock_irqsave(&scq->lock, flags); spin_lock_irqsave(&scq->lock, flags);
while (scq->tail == scq->next) { while (scq->tail == scq->next) {
if (in_interrupt()) { if (!may_sleep) {
spin_unlock_irqrestore(&scq->lock, flags); spin_unlock_irqrestore(&scq->lock, flags);
printk("nicstar%d: Error pushing TBD.\n", card->index); printk("nicstar%d: Error pushing TBD.\n", card->index);
return 1; return 1;
@ -1773,7 +1785,7 @@ static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
int has_run = 0; int has_run = 0;
while (scq->tail == scq->next) { while (scq->tail == scq->next) {
if (in_interrupt()) { if (!may_sleep) {
data = scq_virt_to_bus(scq, scq->next); data = scq_virt_to_bus(scq, scq->next);
ns_write_sram(card, scq->scd, &data, 1); ns_write_sram(card, scq->scd, &data, 1);
spin_unlock_irqrestore(&scq->lock, flags); spin_unlock_irqrestore(&scq->lock, flags);

View File

@ -1,6 +1,9 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
menu "Generic Driver Options" menu "Generic Driver Options"
config AUXILIARY_BUS
bool
config UEVENT_HELPER config UEVENT_HELPER
bool "Support for uevent helper" bool "Support for uevent helper"
help help

View File

@ -7,6 +7,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
attribute_container.o transport_class.o \ attribute_container.o transport_class.o \
topology.o container.o property.o cacheinfo.o \ topology.o container.o property.o cacheinfo.o \
swnode.o swnode.o
obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
obj-y += power/ obj-y += power/
obj-$(CONFIG_ISA_BUS_API) += isa.o obj-$(CONFIG_ISA_BUS_API) += isa.o

View File

@ -0,0 +1,274 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020 Intel Corporation
*
* Please see Documentation/driver-api/auxiliary_bus.rst for more information.
*/
#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
#include <linux/device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/string.h>
#include <linux/auxiliary_bus.h>
static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
const struct auxiliary_device *auxdev)
{
for (; id->name[0]; id++) {
const char *p = strrchr(dev_name(&auxdev->dev), '.');
int match_size;
if (!p)
continue;
match_size = p - dev_name(&auxdev->dev);
/* use dev_name(&auxdev->dev) prefix before last '.' char to match to */
if (strlen(id->name) == match_size &&
!strncmp(dev_name(&auxdev->dev), id->name, match_size))
return id;
}
return NULL;
}
static int auxiliary_match(struct device *dev, struct device_driver *drv)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
struct auxiliary_driver *auxdrv = to_auxiliary_drv(drv);
return !!auxiliary_match_id(auxdrv->id_table, auxdev);
}
static int auxiliary_uevent(struct device *dev, struct kobj_uevent_env *env)
{
const char *name, *p;
name = dev_name(dev);
p = strrchr(name, '.');
return add_uevent_var(env, "MODALIAS=%s%.*s", AUXILIARY_MODULE_PREFIX,
(int)(p - name), name);
}
static const struct dev_pm_ops auxiliary_dev_pm_ops = {
SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, pm_generic_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
};
static int auxiliary_bus_probe(struct device *dev)
{
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
int ret;
ret = dev_pm_domain_attach(dev, true);
if (ret) {
dev_warn(dev, "Failed to attach to PM Domain : %d\n", ret);
return ret;
}
ret = auxdrv->probe(auxdev, auxiliary_match_id(auxdrv->id_table, auxdev));
if (ret)
dev_pm_domain_detach(dev, true);
return ret;
}
static int auxiliary_bus_remove(struct device *dev)
{
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
if (auxdrv->remove)
auxdrv->remove(auxdev);
dev_pm_domain_detach(dev, true);
return 0;
}
static void auxiliary_bus_shutdown(struct device *dev)
{
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
if (auxdrv->shutdown)
auxdrv->shutdown(auxdev);
}
static struct bus_type auxiliary_bus_type = {
.name = "auxiliary",
.probe = auxiliary_bus_probe,
.remove = auxiliary_bus_remove,
.shutdown = auxiliary_bus_shutdown,
.match = auxiliary_match,
.uevent = auxiliary_uevent,
.pm = &auxiliary_dev_pm_ops,
};
/**
* auxiliary_device_init - check auxiliary_device and initialize
* @auxdev: auxiliary device struct
*
* This is the first step in the two-step process to register an
* auxiliary_device.
*
* When this function returns an error code, then the device_initialize will
* *not* have been performed, and the caller will be responsible to free any
* memory allocated for the auxiliary_device in the error path directly.
*
* It returns 0 on success. On success, the device_initialize has been
* performed. After this point any error unwinding will need to include a call
* to auxiliary_device_uninit(). In this post-initialize error scenario, a call
* to the device's .release callback will be triggered, and all memory clean-up
* is expected to be handled there.
*/
int auxiliary_device_init(struct auxiliary_device *auxdev)
{
struct device *dev = &auxdev->dev;
if (!dev->parent) {
pr_err("auxiliary_device has a NULL dev->parent\n");
return -EINVAL;
}
if (!auxdev->name) {
pr_err("auxiliary_device has a NULL name\n");
return -EINVAL;
}
dev->bus = &auxiliary_bus_type;
device_initialize(&auxdev->dev);
return 0;
}
EXPORT_SYMBOL_GPL(auxiliary_device_init);
/**
* __auxiliary_device_add - add an auxiliary bus device
* @auxdev: auxiliary bus device to add to the bus
* @modname: name of the parent device's driver module
*
* This is the second step in the two-step process to register an
* auxiliary_device.
*
* This function must be called after a successful call to
* auxiliary_device_init(), which will perform the device_initialize. This
* means that if this returns an error code, then a call to
* auxiliary_device_uninit() must be performed so that the .release callback
* will be triggered to free the memory associated with the auxiliary_device.
*
* The expectation is that users will call the "auxiliary_device_add" macro so
* that the caller's KBUILD_MODNAME is automatically inserted for the modname
* parameter. Only if a user requires a custom name would this version be
* called directly.
*/
int __auxiliary_device_add(struct auxiliary_device *auxdev, const char *modname)
{
struct device *dev = &auxdev->dev;
int ret;
if (!modname) {
dev_err(dev, "auxiliary device modname is NULL\n");
return -EINVAL;
}
ret = dev_set_name(dev, "%s.%s.%d", modname, auxdev->name, auxdev->id);
if (ret) {
dev_err(dev, "auxiliary device dev_set_name failed: %d\n", ret);
return ret;
}
ret = device_add(dev);
if (ret)
dev_err(dev, "adding auxiliary device failed!: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(__auxiliary_device_add);
/**
* auxiliary_find_device - auxiliary device iterator for locating a particular device.
* @start: Device to begin with
* @data: Data to pass to match function
* @match: Callback function to check device
*
* This function returns a reference to a device that is 'found'
* for later use, as determined by the @match callback.
*
* The callback should return 0 if the device doesn't match and non-zero
* if it does. If the callback returns non-zero, this function will
* return to the caller and not iterate over any more devices.
*/
struct auxiliary_device *auxiliary_find_device(struct device *start,
const void *data,
int (*match)(struct device *dev, const void *data))
{
struct device *dev;
dev = bus_find_device(&auxiliary_bus_type, start, data, match);
if (!dev)
return NULL;
return to_auxiliary_dev(dev);
}
EXPORT_SYMBOL_GPL(auxiliary_find_device);
/**
* __auxiliary_driver_register - register a driver for auxiliary bus devices
* @auxdrv: auxiliary_driver structure
* @owner: owning module/driver
* @modname: KBUILD_MODNAME for parent driver
*/
int __auxiliary_driver_register(struct auxiliary_driver *auxdrv,
struct module *owner, const char *modname)
{
if (WARN_ON(!auxdrv->probe) || WARN_ON(!auxdrv->id_table))
return -EINVAL;
if (auxdrv->name)
auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s.%s", modname,
auxdrv->name);
else
auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s", modname);
if (!auxdrv->driver.name)
return -ENOMEM;
auxdrv->driver.owner = owner;
auxdrv->driver.bus = &auxiliary_bus_type;
auxdrv->driver.mod_name = modname;
return driver_register(&auxdrv->driver);
}
EXPORT_SYMBOL_GPL(__auxiliary_driver_register);
/**
* auxiliary_driver_unregister - unregister a driver
* @auxdrv: auxiliary_driver structure
*/
void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)
{
driver_unregister(&auxdrv->driver);
kfree(auxdrv->driver.name);
}
EXPORT_SYMBOL_GPL(auxiliary_driver_unregister);
static int __init auxiliary_bus_init(void)
{
return bus_register(&auxiliary_bus_type);
}
static void __exit auxiliary_bus_exit(void)
{
bus_unregister(&auxiliary_bus_type);
}
module_init(auxiliary_bus_init);
module_exit(auxiliary_bus_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Auxiliary Bus");
MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>");
MODULE_AUTHOR("Kiran Patil <kiran.patil@intel.com>");

View File

@ -437,31 +437,38 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
tlv = (struct intel_tlv *)skb->data; tlv = (struct intel_tlv *)skb->data;
switch (tlv->type) { switch (tlv->type) {
case INTEL_TLV_CNVI_TOP: case INTEL_TLV_CNVI_TOP:
version->cnvi_top = get_unaligned_le32(tlv->val); version->cnvi_top =
__le32_to_cpu(get_unaligned_le32(tlv->val));
break; break;
case INTEL_TLV_CNVR_TOP: case INTEL_TLV_CNVR_TOP:
version->cnvr_top = get_unaligned_le32(tlv->val); version->cnvr_top =
__le32_to_cpu(get_unaligned_le32(tlv->val));
break; break;
case INTEL_TLV_CNVI_BT: case INTEL_TLV_CNVI_BT:
version->cnvi_bt = get_unaligned_le32(tlv->val); version->cnvi_bt =
__le32_to_cpu(get_unaligned_le32(tlv->val));
break; break;
case INTEL_TLV_CNVR_BT: case INTEL_TLV_CNVR_BT:
version->cnvr_bt = get_unaligned_le32(tlv->val); version->cnvr_bt =
__le32_to_cpu(get_unaligned_le32(tlv->val));
break; break;
case INTEL_TLV_DEV_REV_ID: case INTEL_TLV_DEV_REV_ID:
version->dev_rev_id = get_unaligned_le16(tlv->val); version->dev_rev_id =
__le16_to_cpu(get_unaligned_le16(tlv->val));
break; break;
case INTEL_TLV_IMAGE_TYPE: case INTEL_TLV_IMAGE_TYPE:
version->img_type = tlv->val[0]; version->img_type = tlv->val[0];
break; break;
case INTEL_TLV_TIME_STAMP: case INTEL_TLV_TIME_STAMP:
version->timestamp = get_unaligned_le16(tlv->val); version->timestamp =
__le16_to_cpu(get_unaligned_le16(tlv->val));
break; break;
case INTEL_TLV_BUILD_TYPE: case INTEL_TLV_BUILD_TYPE:
version->build_type = tlv->val[0]; version->build_type = tlv->val[0];
break; break;
case INTEL_TLV_BUILD_NUM: case INTEL_TLV_BUILD_NUM:
version->build_num = get_unaligned_le32(tlv->val); version->build_num =
__le32_to_cpu(get_unaligned_le32(tlv->val));
break; break;
case INTEL_TLV_SECURE_BOOT: case INTEL_TLV_SECURE_BOOT:
version->secure_boot = tlv->val[0]; version->secure_boot = tlv->val[0];

View File

@ -132,6 +132,12 @@ struct intel_debug_features {
__u8 page1[16]; __u8 page1[16];
} __packed; } __packed;
#define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
#define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16))
#define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff)
#define INTEL_CNVX_TOP_STEP(cnvx_top) (((cnvx_top) & 0x0f000000) >> 24)
#define INTEL_CNVX_TOP_PACK_SWAB(t, s) __swab16(((__u16)(((t) << 4) | (s))))
#if IS_ENABLED(CONFIG_BT_INTEL) #if IS_ENABLED(CONFIG_BT_INTEL)
int btintel_check_bdaddr(struct hci_dev *hdev); int btintel_check_bdaddr(struct hci_dev *hdev);

View File

@ -704,7 +704,7 @@ static int mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
err = mtk_hci_wmt_sync(hdev, &wmt_params); err = mtk_hci_wmt_sync(hdev, &wmt_params);
if (err < 0) { if (err < 0) {
bt_dev_err(hdev, "Failed to power on data RAM (%d)", err); bt_dev_err(hdev, "Failed to power on data RAM (%d)", err);
return err; goto free_fw;
} }
fw_ptr = fw->data; fw_ptr = fw->data;

View File

@ -14,12 +14,11 @@
#define VERSION "0.1" #define VERSION "0.1"
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version, int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
enum qca_btsoc_type soc_type) enum qca_btsoc_type soc_type)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct edl_event_hdr *edl; struct edl_event_hdr *edl;
struct qca_btsoc_version *ver;
char cmd; char cmd;
int err = 0; int err = 0;
u8 event_type = HCI_EV_VENDOR; u8 event_type = HCI_EV_VENDOR;
@ -70,9 +69,9 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
} }
if (soc_type >= QCA_WCN3991) if (soc_type >= QCA_WCN3991)
memmove(&edl->data, &edl->data[1], sizeof(*ver)); memcpy(ver, edl->data + 1, sizeof(*ver));
else
ver = (struct qca_btsoc_version *)(edl->data); memcpy(ver, &edl->data, sizeof(*ver));
bt_dev_info(hdev, "QCA Product ID :0x%08x", bt_dev_info(hdev, "QCA Product ID :0x%08x",
le32_to_cpu(ver->product_id)); le32_to_cpu(ver->product_id));
@ -83,13 +82,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
bt_dev_info(hdev, "QCA Patch Version:0x%08x", bt_dev_info(hdev, "QCA Patch Version:0x%08x",
le16_to_cpu(ver->patch_ver)); le16_to_cpu(ver->patch_ver));
/* QCA chipset version can be decided by patch and SoC if (ver->soc_id == 0 || ver->rom_ver == 0)
* version, combination with upper 2 bytes from SoC
* and lower 2 bytes from patch will be used.
*/
*soc_version = (le32_to_cpu(ver->soc_id) << 16) |
(le16_to_cpu(ver->rom_ver) & 0x0000ffff);
if (*soc_version == 0)
err = -EILSEQ; err = -EILSEQ;
out: out:
@ -446,15 +439,20 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver, enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
const char *firmware_name) const char *firmware_name)
{ {
struct qca_fw_config config; struct qca_fw_config config;
int err; int err;
u8 rom_ver = 0; u8 rom_ver = 0;
u32 soc_ver;
bt_dev_dbg(hdev, "QCA setup on UART"); bt_dev_dbg(hdev, "QCA setup on UART");
soc_ver = get_soc_ver(ver.soc_id, ver.rom_ver);
bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
config.user_baud_rate = baudrate; config.user_baud_rate = baudrate;
/* Download rampatch file */ /* Download rampatch file */
@ -491,9 +489,15 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
if (firmware_name) if (firmware_name)
snprintf(config.fwname, sizeof(config.fwname), snprintf(config.fwname, sizeof(config.fwname),
"qca/%s", firmware_name); "qca/%s", firmware_name);
else if (qca_is_wcn399x(soc_type)) else if (qca_is_wcn399x(soc_type)) {
snprintf(config.fwname, sizeof(config.fwname), if (ver.soc_id == QCA_WCN3991_SOC_ID) {
"qca/crnv%02x.bin", rom_ver); snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02xu.bin", rom_ver);
} else {
snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02x.bin", rom_ver);
}
}
else if (soc_type == QCA_QCA6390) else if (soc_type == QCA_QCA6390)
snprintf(config.fwname, sizeof(config.fwname), snprintf(config.fwname, sizeof(config.fwname),
"qca/htnv%02x.bin", rom_ver); "qca/htnv%02x.bin", rom_ver);

View File

@ -34,6 +34,18 @@
#define QCA_HCI_CC_OPCODE 0xFC00 #define QCA_HCI_CC_OPCODE 0xFC00
#define QCA_HCI_CC_SUCCESS 0x00 #define QCA_HCI_CC_SUCCESS 0x00
#define QCA_WCN3991_SOC_ID (0x40014320)
/* QCA chipset version can be decided by patch and SoC
* version, combination with upper 2 bytes from SoC
* and lower 2 bytes from patch will be used.
*/
#define get_soc_ver(soc_id, rom_ver) \
((le32_to_cpu(soc_id) << 16) | (le16_to_cpu(rom_ver)))
#define QCA_FW_BUILD_VER_LEN 255
enum qca_baudrate { enum qca_baudrate {
QCA_BAUDRATE_115200 = 0, QCA_BAUDRATE_115200 = 0,
QCA_BAUDRATE_57600, QCA_BAUDRATE_57600,
@ -136,9 +148,9 @@ enum qca_btsoc_type {
int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr); int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver, enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
const char *firmware_name); const char *firmware_name);
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version, int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
enum qca_btsoc_type); enum qca_btsoc_type);
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev); int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
@ -155,13 +167,15 @@ static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdad
} }
static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver, enum qca_btsoc_type soc_type,
struct qca_btsoc_version ver,
const char *firmware_name) const char *firmware_name)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version, static inline int qca_read_soc_version(struct hci_dev *hdev,
struct qca_btsoc_version *ver,
enum qca_btsoc_type) enum qca_btsoc_type)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;

View File

@ -18,23 +18,25 @@
#define VERSION "0.1" #define VERSION "0.1"
#define RTL_EPATCH_SIGNATURE "Realtech" #define RTL_EPATCH_SIGNATURE "Realtech"
#define RTL_ROM_LMP_3499 0x3499
#define RTL_ROM_LMP_8723A 0x1200 #define RTL_ROM_LMP_8723A 0x1200
#define RTL_ROM_LMP_8723B 0x8723 #define RTL_ROM_LMP_8723B 0x8723
#define RTL_ROM_LMP_8723D 0x8873
#define RTL_ROM_LMP_8821A 0x8821 #define RTL_ROM_LMP_8821A 0x8821
#define RTL_ROM_LMP_8761A 0x8761 #define RTL_ROM_LMP_8761A 0x8761
#define RTL_ROM_LMP_8822B 0x8822 #define RTL_ROM_LMP_8822B 0x8822
#define RTL_ROM_LMP_8852A 0x8852
#define RTL_CONFIG_MAGIC 0x8723ab55 #define RTL_CONFIG_MAGIC 0x8723ab55
#define IC_MATCH_FL_LMPSUBV (1 << 0) #define IC_MATCH_FL_LMPSUBV (1 << 0)
#define IC_MATCH_FL_HCIREV (1 << 1) #define IC_MATCH_FL_HCIREV (1 << 1)
#define IC_MATCH_FL_HCIVER (1 << 2) #define IC_MATCH_FL_HCIVER (1 << 2)
#define IC_MATCH_FL_HCIBUS (1 << 3) #define IC_MATCH_FL_HCIBUS (1 << 3)
#define IC_INFO(lmps, hcir) \ #define IC_INFO(lmps, hcir, hciv, bus) \
.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV, \ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \
.lmp_subver = (lmps), \ .lmp_subver = (lmps), \
.hci_rev = (hcir) .hci_rev = (hcir), \
.hci_ver = (hciv), \
.hci_bus = (bus)
struct id_table { struct id_table {
__u16 match_flags; __u16 match_flags;
@ -55,119 +57,100 @@ struct btrtl_device_info {
int fw_len; int fw_len;
u8 *cfg_data; u8 *cfg_data;
int cfg_len; int cfg_len;
bool drop_fw;
}; };
static const struct id_table ic_id_table[] = { static const struct id_table ic_id_table[] = {
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8723A, 0x0, /* 8723A */
.config_needed = false, { IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB),
.has_rom_version = false,
.fw_name = "rtl_bt/rtl8723a_fw.bin",
.cfg_name = NULL },
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_3499, 0x0,
.config_needed = false, .config_needed = false,
.has_rom_version = false, .has_rom_version = false,
.fw_name = "rtl_bt/rtl8723a_fw.bin", .fw_name = "rtl_bt/rtl8723a_fw.bin",
.cfg_name = NULL }, .cfg_name = NULL },
/* 8723BS */ /* 8723BS */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART),
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
.lmp_subver = RTL_ROM_LMP_8723B,
.hci_rev = 0xb,
.hci_ver = 6,
.hci_bus = HCI_UART,
.config_needed = true, .config_needed = true,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8723bs_fw.bin", .fw_name = "rtl_bt/rtl8723bs_fw.bin",
.cfg_name = "rtl_bt/rtl8723bs_config" }, .cfg_name = "rtl_bt/rtl8723bs_config" },
/* 8723B */ /* 8723B */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb), { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB),
.config_needed = false, .config_needed = false,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8723b_fw.bin", .fw_name = "rtl_bt/rtl8723b_fw.bin",
.cfg_name = "rtl_bt/rtl8723b_config" }, .cfg_name = "rtl_bt/rtl8723b_config" },
/* 8723D */ /* 8723D */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd), { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB),
.config_needed = true, .config_needed = true,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8723d_fw.bin", .fw_name = "rtl_bt/rtl8723d_fw.bin",
.cfg_name = "rtl_bt/rtl8723d_config" }, .cfg_name = "rtl_bt/rtl8723d_config" },
/* 8723DS */ /* 8723DS */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART),
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
.lmp_subver = RTL_ROM_LMP_8723B,
.hci_rev = 0xd,
.hci_ver = 8,
.hci_bus = HCI_UART,
.config_needed = true, .config_needed = true,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8723ds_fw.bin", .fw_name = "rtl_bt/rtl8723ds_fw.bin",
.cfg_name = "rtl_bt/rtl8723ds_config" }, .cfg_name = "rtl_bt/rtl8723ds_config" },
/* 8723DU */
{ IC_INFO(RTL_ROM_LMP_8723D, 0x826C),
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723d_fw.bin",
.cfg_name = "rtl_bt/rtl8723d_config" },
/* 8821A */ /* 8821A */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa), { IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB),
.config_needed = false, .config_needed = false,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8821a_fw.bin", .fw_name = "rtl_bt/rtl8821a_fw.bin",
.cfg_name = "rtl_bt/rtl8821a_config" }, .cfg_name = "rtl_bt/rtl8821a_config" },
/* 8821C */ /* 8821C */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc), { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
.config_needed = false, .config_needed = false,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8821c_fw.bin", .fw_name = "rtl_bt/rtl8821c_fw.bin",
.cfg_name = "rtl_bt/rtl8821c_config" }, .cfg_name = "rtl_bt/rtl8821c_config" },
/* 8761A */ /* 8761A */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xa), { IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB),
.config_needed = false, .config_needed = false,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8761a_fw.bin", .fw_name = "rtl_bt/rtl8761a_fw.bin",
.cfg_name = "rtl_bt/rtl8761a_config" }, .cfg_name = "rtl_bt/rtl8761a_config" },
/* 8761B */ /* 8761B */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb), { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB),
.config_needed = false, .config_needed = false,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8761b_fw.bin", .fw_name = "rtl_bt/rtl8761b_fw.bin",
.cfg_name = "rtl_bt/rtl8761b_config" }, .cfg_name = "rtl_bt/rtl8761b_config" },
/* 8822C with UART interface */ /* 8822C with UART interface */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
IC_MATCH_FL_HCIBUS,
.lmp_subver = RTL_ROM_LMP_8822B,
.hci_rev = 0x000c,
.hci_ver = 0x0a,
.hci_bus = HCI_UART,
.config_needed = true, .config_needed = true,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8822cs_fw.bin", .fw_name = "rtl_bt/rtl8822cs_fw.bin",
.cfg_name = "rtl_bt/rtl8822cs_config" }, .cfg_name = "rtl_bt/rtl8822cs_config" },
/* 8822C with USB interface */ /* 8822C with USB interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc), { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
.config_needed = false, .config_needed = false,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8822cu_fw.bin", .fw_name = "rtl_bt/rtl8822cu_fw.bin",
.cfg_name = "rtl_bt/rtl8822cu_config" }, .cfg_name = "rtl_bt/rtl8822cu_config" },
/* 8822B */ /* 8822B */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb), { IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
.config_needed = true, .config_needed = true,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8822b_fw.bin", .fw_name = "rtl_bt/rtl8822b_fw.bin",
.cfg_name = "rtl_bt/rtl8822b_config" }, .cfg_name = "rtl_bt/rtl8822b_config" },
/* 8852A */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8852au_fw.bin",
.cfg_name = "rtl_bt/rtl8852au_config" },
}; };
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
@ -275,6 +258,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */ { RTL_ROM_LMP_8821A, 10 }, /* 8821C */
{ RTL_ROM_LMP_8822B, 13 }, /* 8822C */ { RTL_ROM_LMP_8822B, 13 }, /* 8822C */
{ RTL_ROM_LMP_8761A, 14 }, /* 8761B */ { RTL_ROM_LMP_8761A, 14 }, /* 8761B */
{ RTL_ROM_LMP_8852A, 18 }, /* 8852A */
}; };
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3; min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
@ -563,6 +547,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
u16 hci_rev, lmp_subver; u16 hci_rev, lmp_subver;
u8 hci_ver; u8 hci_ver;
int ret; int ret;
u16 opcode;
u8 cmd[2];
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL); btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
if (!btrtl_dev) { if (!btrtl_dev) {
@ -584,6 +570,49 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
hci_ver = resp->hci_ver; hci_ver = resp->hci_ver;
hci_rev = le16_to_cpu(resp->hci_rev); hci_rev = le16_to_cpu(resp->hci_rev);
lmp_subver = le16_to_cpu(resp->lmp_subver); lmp_subver = le16_to_cpu(resp->lmp_subver);
if (resp->hci_ver == 0x8 && le16_to_cpu(resp->hci_rev) == 0x826c &&
resp->lmp_ver == 0x8 && le16_to_cpu(resp->lmp_subver) == 0xa99e)
btrtl_dev->drop_fw = true;
if (btrtl_dev->drop_fw) {
opcode = hci_opcode_pack(0x3f, 0x66);
cmd[0] = opcode & 0xff;
cmd[1] = opcode >> 8;
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
if (!skb)
goto out_free;
skb_put_data(skb, cmd, sizeof(cmd));
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
hdev->send(hdev, skb);
/* Ensure the above vendor command is sent to controller and
* process has done.
*/
msleep(200);
/* Read the local version again. Expect to have the vanilla
* version as cold boot.
*/
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
goto err_free;
}
resp = (struct hci_rp_read_local_version *)skb->data;
rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x",
resp->hci_ver, resp->hci_rev,
resp->lmp_ver, resp->lmp_subver);
hci_ver = resp->hci_ver;
hci_rev = le16_to_cpu(resp->hci_rev);
lmp_subver = le16_to_cpu(resp->lmp_subver);
}
out_free:
kfree_skb(skb); kfree_skb(skb);
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
@ -654,12 +683,12 @@ int btrtl_download_firmware(struct hci_dev *hdev,
switch (btrtl_dev->ic_info->lmp_subver) { switch (btrtl_dev->ic_info->lmp_subver) {
case RTL_ROM_LMP_8723A: case RTL_ROM_LMP_8723A:
case RTL_ROM_LMP_3499:
return btrtl_setup_rtl8723a(hdev, btrtl_dev); return btrtl_setup_rtl8723a(hdev, btrtl_dev);
case RTL_ROM_LMP_8723B: case RTL_ROM_LMP_8723B:
case RTL_ROM_LMP_8821A: case RTL_ROM_LMP_8821A:
case RTL_ROM_LMP_8761A: case RTL_ROM_LMP_8761A:
case RTL_ROM_LMP_8822B: case RTL_ROM_LMP_8822B:
case RTL_ROM_LMP_8852A:
return btrtl_setup_rtl8723b(hdev, btrtl_dev); return btrtl_setup_rtl8723b(hdev, btrtl_dev);
default: default:
rtl_dev_info(hdev, "assuming no firmware upload needed"); rtl_dev_info(hdev, "assuming no firmware upload needed");
@ -835,3 +864,5 @@ MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin");

View File

@ -60,6 +60,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_WIDEBAND_SPEECH 0x400000 #define BTUSB_WIDEBAND_SPEECH 0x400000
#define BTUSB_VALID_LE_STATES 0x800000 #define BTUSB_VALID_LE_STATES 0x800000
#define BTUSB_QCA_WCN6855 0x1000000 #define BTUSB_QCA_WCN6855 0x1000000
#define BTUSB_INTEL_NEWGEN 0x2000000
static const struct usb_device_id btusb_table[] = { static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */ /* Generic Bluetooth USB device */
@ -365,7 +366,7 @@ static const struct usb_device_id blacklist_table[] = {
BTUSB_WIDEBAND_SPEECH }, BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x8087, 0x0029), .driver_info = BTUSB_INTEL_NEW | { USB_DEVICE(0x8087, 0x0029), .driver_info = BTUSB_INTEL_NEW |
BTUSB_WIDEBAND_SPEECH }, BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_NEW | { USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_NEWGEN |
BTUSB_WIDEBAND_SPEECH}, BTUSB_WIDEBAND_SPEECH},
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR }, { USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL }, { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
@ -386,6 +387,10 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0bda, 0xb00c), .driver_info = BTUSB_REALTEK | { USB_DEVICE(0x0bda, 0xb00c), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH }, BTUSB_WIDEBAND_SPEECH },
/* Realtek 8852AE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0xc852), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Realtek Bluetooth devices */ /* Realtek Bluetooth devices */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_REALTEK }, .driver_info = BTUSB_REALTEK },
@ -394,6 +399,9 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x0e8d, 0xe0, 0x01, 0x01), { USB_VENDOR_AND_INTERFACE_INFO(0x0e8d, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_MEDIATEK }, .driver_info = BTUSB_MEDIATEK },
/* Additional MediaTek MT7615E Bluetooth devices */
{ USB_DEVICE(0x13d3, 0x3560), .driver_info = BTUSB_MEDIATEK},
/* Additional Realtek 8723AE Bluetooth devices */ /* Additional Realtek 8723AE Bluetooth devices */
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
@ -425,8 +433,26 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK },
/* Additional Realtek 8822CE Bluetooth devices */ /* Additional Realtek 8822CE Bluetooth devices */
{ USB_DEVICE(0x04ca, 0x4005), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x04ca, 0x4005), .driver_info = BTUSB_REALTEK |
{ USB_DEVICE(0x13d3, 0x3548), .driver_info = BTUSB_REALTEK }, BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04c5, 0x161f), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0b05, 0x18ef), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3548), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3549), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3553), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3555), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x2ff8, 0x3051), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x1358, 0xc123), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0bda, 0xc123), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Silicon Wave based devices */ /* Silicon Wave based devices */
{ USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE }, { USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE },
@ -1763,9 +1789,12 @@ static int btusb_setup_bcm92035(struct hci_dev *hdev)
static int btusb_setup_csr(struct hci_dev *hdev) static int btusb_setup_csr(struct hci_dev *hdev)
{ {
struct btusb_data *data = hci_get_drvdata(hdev);
u16 bcdDevice = le16_to_cpu(data->udev->descriptor.bcdDevice);
struct hci_rp_read_local_version *rp; struct hci_rp_read_local_version *rp;
struct sk_buff *skb; struct sk_buff *skb;
bool is_fake = false; bool is_fake = false;
int ret;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
@ -1832,6 +1861,12 @@ static int btusb_setup_csr(struct hci_dev *hdev)
le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_4_0) le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_4_0)
is_fake = true; is_fake = true;
/* Other clones which beat all the above checks */
else if (bcdDevice == 0x0134 &&
le16_to_cpu(rp->lmp_subver) == 0x0c5c &&
le16_to_cpu(rp->hci_ver) == BLUETOOTH_VER_2_0)
is_fake = true;
if (is_fake) { if (is_fake) {
bt_dev_warn(hdev, "CSR: Unbranded CSR clone detected; adding workarounds..."); bt_dev_warn(hdev, "CSR: Unbranded CSR clone detected; adding workarounds...");
@ -1848,6 +1883,43 @@ static int btusb_setup_csr(struct hci_dev *hdev)
*/ */
clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
clear_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); clear_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
/*
* Special workaround for clones with a Barrot 8041a02 chip,
* these clones are really messed-up:
* 1. Their bulk rx endpoint will never report any data unless
* the device was suspended at least once (yes really).
* 2. They will not wakeup when autosuspended and receiving data
* on their bulk rx endpoint from e.g. a keyboard or mouse
* (IOW remote-wakeup support is broken for the bulk endpoint).
*
* To fix 1. enable runtime-suspend, force-suspend the
* hci and then wake-it up by disabling runtime-suspend.
*
* To fix 2. clear the hci's can_wake flag, this way the hci
* will still be autosuspended when it is not open.
*/
if (bcdDevice == 0x8891 &&
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");
pm_runtime_allow(&data->udev->dev);
ret = pm_runtime_suspend(&data->udev->dev);
if (ret >= 0)
msleep(200);
else
bt_dev_err(hdev, "Failed to suspend the device for Barrot 8041a02 receive-issue workaround\n");
pm_runtime_forbid(&data->udev->dev);
device_set_wakeup_capable(&data->udev->dev, false);
/* Re-enable autosuspend if this was requested */
if (enable_autosuspend)
usb_enable_autosuspend(data->udev);
}
} }
kfree_skb(skb); kfree_skb(skb);
@ -2359,6 +2431,182 @@ static bool btusb_setup_intel_new_get_fw_name(struct intel_version *ver,
return true; return true;
} }
static void btusb_setup_intel_newgen_get_fw_name(const struct intel_version_tlv *ver_tlv,
char *fw_name, size_t len,
const char *suffix)
{
/* The firmware file name for new generation controllers will be
* ibt-<cnvi_top type+cnvi_top step>-<cnvr_top type+cnvr_top step>
*/
snprintf(fw_name, len, "intel/ibt-%04x-%04x.%s",
INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver_tlv->cnvi_top),
INTEL_CNVX_TOP_STEP(ver_tlv->cnvi_top)),
INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver_tlv->cnvr_top),
INTEL_CNVX_TOP_STEP(ver_tlv->cnvr_top)),
suffix);
}
static int btusb_intel_download_firmware_newgen(struct hci_dev *hdev,
struct intel_version_tlv *ver,
u32 *boot_param)
{
const struct firmware *fw;
char fwname[64];
int err;
struct btusb_data *data = hci_get_drvdata(hdev);
if (!ver || !boot_param)
return -EINVAL;
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
*/
if (INTEL_HW_PLATFORM(ver->cnvi_bt) != 0x37) {
bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)",
INTEL_HW_PLATFORM(ver->cnvi_bt));
return -EINVAL;
}
/* The firmware variant determines if the device is in bootloader
* mode or is running operational firmware. The value 0x03 identifies
* the bootloader and the value 0x23 identifies the operational
* firmware.
*
* When the operational firmware is already present, then only
* the check for valid Bluetooth device address is needed. This
* determines if the device will be added as configured or
* unconfigured controller.
*
* It is not possible to use the Secure Boot Parameters in this
* case since that command is only available in bootloader mode.
*/
if (ver->img_type == 0x03) {
clear_bit(BTUSB_BOOTLOADER, &data->flags);
btintel_check_bdaddr(hdev);
return 0;
}
/* Check for supported iBT hardware variants of this firmware
* loading method.
*
* This check has been put in place to ensure correct forward
* compatibility options when newer hardware variants come along.
*/
switch (INTEL_HW_VARIANT(ver->cnvi_bt)) {
case 0x17: /* TyP */
case 0x18: /* Slr */
case 0x19: /* Slr-F */
break;
default:
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
INTEL_HW_VARIANT(ver->cnvi_bt));
return -EINVAL;
}
/* If the device is not in bootloader mode, then the only possible
* choice is to return an error and abort the device initialization.
*/
if (ver->img_type != 0x01) {
bt_dev_err(hdev, "Unsupported Intel firmware variant (0x%x)",
ver->img_type);
return -ENODEV;
}
/* It is required that every single firmware fragment is acknowledged
* with a command complete event. If the boot parameters indicate
* that this bootloader does not send them, then abort the setup.
*/
if (ver->limited_cce != 0x00) {
bt_dev_err(hdev, "Unsupported Intel firmware loading method (0x%x)",
ver->limited_cce);
return -EINVAL;
}
/* Secure boot engine type should be either 1 (ECDSA) or 0 (RSA) */
if (ver->sbe_type > 0x01) {
bt_dev_err(hdev, "Unsupported Intel secure boot engine type (0x%x)",
ver->sbe_type);
return -EINVAL;
}
/* If the OTP has no valid Bluetooth device address, then there will
* also be no valid address for the operational firmware.
*/
if (!bacmp(&ver->otp_bd_addr, BDADDR_ANY)) {
bt_dev_info(hdev, "No device address configured");
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
}
btusb_setup_intel_newgen_get_fw_name(ver, fwname, sizeof(fwname), "sfi");
err = request_firmware(&fw, fwname, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to load Intel firmware file (%d)", err);
return err;
}
bt_dev_info(hdev, "Found device firmware: %s", fwname);
if (fw->size < 644) {
bt_dev_err(hdev, "Invalid size of firmware file (%zu)",
fw->size);
err = -EBADF;
goto done;
}
set_bit(BTUSB_DOWNLOADING, &data->flags);
/* Start firmware downloading and get boot parameter */
err = btintel_download_firmware_newgen(hdev, fw, boot_param,
INTEL_HW_VARIANT(ver->cnvi_bt),
ver->sbe_type);
if (err < 0) {
/* When FW download fails, send Intel Reset to retry
* FW download.
*/
btintel_reset_to_bootloader(hdev);
goto done;
}
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
bt_dev_info(hdev, "Waiting for firmware download to complete");
/* Before switching the device into operational mode and with that
* booting the loaded firmware, wait for the bootloader notification
* that all fragments have been successfully received.
*
* When the event processing receives the notification, then the
* BTUSB_DOWNLOADING flag will be cleared.
*
* The firmware loading should not take longer than 5 seconds
* and thus just timeout if that happens and fail the setup
* of this device.
*/
err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(5000));
if (err == -EINTR) {
bt_dev_err(hdev, "Firmware loading interrupted");
goto done;
}
if (err) {
bt_dev_err(hdev, "Firmware loading timeout");
err = -ETIMEDOUT;
btintel_reset_to_bootloader(hdev);
goto done;
}
if (test_bit(BTUSB_FIRMWARE_FAILED, &data->flags)) {
bt_dev_err(hdev, "Firmware loading failed");
err = -ENOEXEC;
goto done;
}
done:
release_firmware(fw);
return err;
}
static int btusb_intel_download_firmware(struct hci_dev *hdev, static int btusb_intel_download_firmware(struct hci_dev *hdev,
struct intel_version *ver, struct intel_version *ver,
struct intel_boot_params *params, struct intel_boot_params *params,
@ -2693,6 +2941,134 @@ finish:
return 0; return 0;
} }
static int btusb_setup_intel_newgen(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
u32 boot_param;
char ddcname[64];
ktime_t calltime, delta, rettime;
unsigned long long duration;
int err;
struct intel_debug_features features;
struct intel_version_tlv version;
bt_dev_dbg(hdev, "");
/* Set the default boot parameter to 0x0 and it is updated to
* SKU specific boot parameter after reading Intel_Write_Boot_Params
* command while downloading the firmware.
*/
boot_param = 0x00000000;
calltime = ktime_get();
/* Read the Intel version information to determine if the device
* is in bootloader mode or if it already has operational firmware
* loaded.
*/
err = btintel_read_version_tlv(hdev, &version);
if (err) {
bt_dev_err(hdev, "Intel Read version failed (%d)", err);
btintel_reset_to_bootloader(hdev);
return err;
}
btintel_version_info_tlv(hdev, &version);
err = btusb_intel_download_firmware_newgen(hdev, &version, &boot_param);
if (err)
return err;
/* check if controller is already having an operational firmware */
if (version.img_type == 0x03)
goto finish;
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration = (unsigned long long)ktime_to_ns(delta) >> 10;
bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration);
calltime = ktime_get();
set_bit(BTUSB_BOOTING, &data->flags);
err = btintel_send_intel_reset(hdev, boot_param);
if (err) {
bt_dev_err(hdev, "Intel Soft Reset failed (%d)", err);
btintel_reset_to_bootloader(hdev);
return err;
}
/* The bootloader will not indicate when the device is ready. This
* is done by the operational firmware sending bootup notification.
*
* Booting into operational firmware should not take longer than
* 1 second. However if that happens, then just fail the setup
* since something went wrong.
*/
bt_dev_info(hdev, "Waiting for device to boot");
err = wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(1000));
if (err == -EINTR) {
bt_dev_err(hdev, "Device boot interrupted");
return -EINTR;
}
if (err) {
bt_dev_err(hdev, "Device boot timeout");
btintel_reset_to_bootloader(hdev);
return -ETIMEDOUT;
}
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration = (unsigned long long)ktime_to_ns(delta) >> 10;
bt_dev_info(hdev, "Device booted in %llu usecs", duration);
clear_bit(BTUSB_BOOTLOADER, &data->flags);
btusb_setup_intel_newgen_get_fw_name(&version, ddcname, sizeof(ddcname),
"ddc");
/* Once the device is running in operational mode, it needs to
* apply the device configuration (DDC) parameters.
*
* The device can work without DDC parameters, so even if it
* fails to load the file, no need to fail the setup.
*/
btintel_load_ddc_config(hdev, ddcname);
/* Read the Intel supported features and if new exception formats
* supported, need to load the additional DDC config to enable.
*/
btintel_read_debug_features(hdev, &features);
/* Set DDC mask for available debug features */
btintel_set_debug_features(hdev, &features);
/* Read the Intel version information after loading the FW */
err = btintel_read_version_tlv(hdev, &version);
if (err)
return err;
btintel_version_info_tlv(hdev, &version);
finish:
/* Set the event mask for Intel specific vendor events. This enables
* a few extra events that are useful during general operation. It
* does not enable any debugging related events.
*
* The device will function correctly without these events enabled
* and thus no need to fail the setup.
*/
btintel_set_event_mask(hdev, false);
return 0;
}
static int btusb_shutdown_intel(struct hci_dev *hdev) static int btusb_shutdown_intel(struct hci_dev *hdev)
{ {
struct sk_buff *skb; struct sk_buff *skb;
@ -3067,7 +3443,7 @@ static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
if (err < 0) { if (err < 0) {
bt_dev_err(hdev, "Failed to power on data RAM (%d)", err); bt_dev_err(hdev, "Failed to power on data RAM (%d)", err);
return err; goto err_release_fw;
} }
fw_ptr = fw->data; fw_ptr = fw->data;
@ -3444,12 +3820,14 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
#define QCA_SYSCFG_UPDATED 0x40 #define QCA_SYSCFG_UPDATED 0x40
#define QCA_PATCH_UPDATED 0x80 #define QCA_PATCH_UPDATED 0x80
#define QCA_DFU_TIMEOUT 3000 #define QCA_DFU_TIMEOUT 3000
#define QCA_FLAG_MULTI_NVM 0x80
struct qca_version { struct qca_version {
__le32 rom_version; __le32 rom_version;
__le32 patch_version; __le32 patch_version;
__le32 ram_version; __le32 ram_version;
__le32 ref_clock; __le16 board_id;
__le16 flag;
__u8 reserved[4]; __u8 reserved[4];
} __packed; } __packed;
@ -3632,8 +4010,14 @@ static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
char fwname[64]; char fwname[64];
int err; int err;
snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x.bin", if (((ver->flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) {
le32_to_cpu(ver->rom_version)); snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x_%04x.bin",
le32_to_cpu(ver->rom_version),
le16_to_cpu(ver->board_id));
} else {
snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x.bin",
le32_to_cpu(ver->rom_version));
}
err = request_firmware(&fw, fwname, &hdev->dev); err = request_firmware(&fw, fwname, &hdev->dev);
if (err) { if (err) {
@ -3700,6 +4084,11 @@ static int btusb_setup_qca(struct hci_dev *hdev)
return err; return err;
} }
err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
sizeof(ver));
if (err < 0)
return err;
if (!(status & QCA_SYSCFG_UPDATED)) { if (!(status & QCA_SYSCFG_UPDATED)) {
err = btusb_setup_qca_load_nvm(hdev, &ver, info); err = btusb_setup_qca_load_nvm(hdev, &ver, info);
if (err < 0) if (err < 0)
@ -4078,6 +4467,24 @@ static int btusb_probe(struct usb_interface *intf,
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks); set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
} }
if (id->driver_info & BTUSB_INTEL_NEWGEN) {
hdev->manufacturer = 2;
hdev->send = btusb_send_frame_intel;
hdev->setup = btusb_setup_intel_newgen;
hdev->shutdown = btusb_shutdown_intel_new;
hdev->hw_error = btintel_hw_error;
hdev->set_diag = btintel_set_diag;
hdev->set_bdaddr = btintel_set_bdaddr;
hdev->cmd_timeout = btusb_intel_cmd_timeout;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
data->recv_event = btusb_recv_event_intel;
data->recv_bulk = btusb_recv_bulk_intel;
set_bit(BTUSB_BOOTLOADER, &data->flags);
}
if (id->driver_info & BTUSB_MARVELL) if (id->driver_info & BTUSB_MARVELL)
hdev->set_bdaddr = btusb_set_bdaddr_marvell; hdev->set_bdaddr = btusb_set_bdaddr_marvell;

View File

@ -245,6 +245,9 @@ static int h5_close(struct hci_uart *hu)
skb_queue_purge(&h5->rel); skb_queue_purge(&h5->rel);
skb_queue_purge(&h5->unrel); skb_queue_purge(&h5->unrel);
kfree_skb(h5->rx_skb);
h5->rx_skb = NULL;
if (h5->vnd && h5->vnd->close) if (h5->vnd && h5->vnd->close)
h5->vnd->close(h5); h5->vnd->close(h5);
@ -1001,6 +1004,7 @@ static struct h5_vnd rtl_vnd = {
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct acpi_device_id h5_acpi_match[] = { static const struct acpi_device_id h5_acpi_match[] = {
#ifdef CONFIG_BT_HCIUART_RTL #ifdef CONFIG_BT_HCIUART_RTL
{ "OBDA0623", (kernel_ulong_t)&rtl_vnd },
{ "OBDA8723", (kernel_ulong_t)&rtl_vnd }, { "OBDA8723", (kernel_ulong_t)&rtl_vnd },
#endif #endif
{ }, { },

View File

@ -626,6 +626,7 @@ static int ll_setup(struct hci_uart *hu)
gpiod_set_value_cansleep(lldev->enable_gpio, 0); gpiod_set_value_cansleep(lldev->enable_gpio, 0);
msleep(5); msleep(5);
gpiod_set_value_cansleep(lldev->enable_gpio, 1); gpiod_set_value_cansleep(lldev->enable_gpio, 1);
mdelay(100);
err = serdev_device_wait_for_cts(serdev, true, 200); err = serdev_device_wait_for_cts(serdev, true, 200);
if (err) { if (err) {
bt_dev_err(hu->hdev, "Failed to get CTS"); bt_dev_err(hu->hdev, "Failed to get CTS");

View File

@ -50,6 +50,8 @@
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000 #define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
#define CMD_TRANS_TIMEOUT_MS 100 #define CMD_TRANS_TIMEOUT_MS 100
#define MEMDUMP_TIMEOUT_MS 8000 #define MEMDUMP_TIMEOUT_MS 8000
#define IBS_DISABLE_SSR_TIMEOUT_MS (MEMDUMP_TIMEOUT_MS + 1000)
#define FW_DOWNLOAD_TIMEOUT_MS 3000
/* susclk rate */ /* susclk rate */
#define SUSCLK_RATE_32KHZ 32768 #define SUSCLK_RATE_32KHZ 32768
@ -68,16 +70,18 @@
#define QCA_MEMDUMP_BYTE 0xFB #define QCA_MEMDUMP_BYTE 0xFB
enum qca_flags { enum qca_flags {
QCA_IBS_ENABLED, QCA_IBS_DISABLED,
QCA_DROP_VENDOR_EVENT, QCA_DROP_VENDOR_EVENT,
QCA_SUSPENDING, QCA_SUSPENDING,
QCA_MEMDUMP_COLLECTION, QCA_MEMDUMP_COLLECTION,
QCA_HW_ERROR_EVENT, QCA_HW_ERROR_EVENT,
QCA_SSR_TRIGGERED QCA_SSR_TRIGGERED,
QCA_BT_OFF
}; };
enum qca_capabilities { enum qca_capabilities {
QCA_CAP_WIDEBAND_SPEECH = BIT(0), QCA_CAP_WIDEBAND_SPEECH = BIT(0),
QCA_CAP_VALID_LE_STATES = BIT(1),
}; };
/* HCI_IBS transmit side sleep protocol states */ /* HCI_IBS transmit side sleep protocol states */
@ -630,7 +634,7 @@ static void qca_debugfs_init(struct hci_dev *hdev)
ibs_dir = debugfs_create_dir("ibs", hdev->debugfs); ibs_dir = debugfs_create_dir("ibs", hdev->debugfs);
/* read only */ /* read only */
mode = S_IRUGO; mode = 0444;
debugfs_create_u8("tx_ibs_state", mode, ibs_dir, &qca->tx_ibs_state); debugfs_create_u8("tx_ibs_state", mode, ibs_dir, &qca->tx_ibs_state);
debugfs_create_u8("rx_ibs_state", mode, ibs_dir, &qca->rx_ibs_state); debugfs_create_u8("rx_ibs_state", mode, ibs_dir, &qca->rx_ibs_state);
debugfs_create_u64("ibs_sent_sleeps", mode, ibs_dir, debugfs_create_u64("ibs_sent_sleeps", mode, ibs_dir,
@ -657,7 +661,7 @@ static void qca_debugfs_init(struct hci_dev *hdev)
debugfs_create_u32("vote_off_ms", mode, ibs_dir, &qca->vote_off_ms); debugfs_create_u32("vote_off_ms", mode, ibs_dir, &qca->vote_off_ms);
/* read/write */ /* read/write */
mode = S_IRUGO | S_IWUSR; mode = 0644;
debugfs_create_u32("wake_retrans", mode, ibs_dir, &qca->wake_retrans); debugfs_create_u32("wake_retrans", mode, ibs_dir, &qca->wake_retrans);
debugfs_create_u32("tx_idle_delay", mode, ibs_dir, debugfs_create_u32("tx_idle_delay", mode, ibs_dir,
&qca->tx_idle_delay); &qca->tx_idle_delay);
@ -869,7 +873,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
* Out-Of-Band(GPIOs control) sleep is selected. * Out-Of-Band(GPIOs control) sleep is selected.
* Don't wake the device up when suspending. * Don't wake the device up when suspending.
*/ */
if (!test_bit(QCA_IBS_ENABLED, &qca->flags) || if (test_bit(QCA_IBS_DISABLED, &qca->flags) ||
test_bit(QCA_SUSPENDING, &qca->flags)) { test_bit(QCA_SUSPENDING, &qca->flags)) {
skb_queue_tail(&qca->txq, skb); skb_queue_tail(&qca->txq, skb);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
@ -1014,7 +1018,7 @@ static void qca_controller_memdump(struct work_struct *work)
* the controller to send the dump is 8 seconds. let us * the controller to send the dump is 8 seconds. let us
* start timer to handle this asynchronous activity. * start timer to handle this asynchronous activity.
*/ */
clear_bit(QCA_IBS_ENABLED, &qca->flags); set_bit(QCA_IBS_DISABLED, &qca->flags);
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
dump = (void *) skb->data; dump = (void *) skb->data;
dump_size = __le32_to_cpu(dump->dump_size); dump_size = __le32_to_cpu(dump->dump_size);
@ -1301,7 +1305,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
/* Give the controller time to process the request */ /* Give the controller time to process the request */
if (qca_is_wcn399x(qca_soc_type(hu))) if (qca_is_wcn399x(qca_soc_type(hu)))
msleep(10); usleep_range(1000, 10000);
else else
msleep(300); msleep(300);
@ -1349,7 +1353,7 @@ static int qca_send_power_pulse(struct hci_uart *hu, bool on)
if (on) if (on)
msleep(100); msleep(100);
else else
msleep(10); usleep_range(1000, 10000);
return 0; return 0;
} }
@ -1618,6 +1622,7 @@ static int qca_power_on(struct hci_dev *hdev)
struct hci_uart *hu = hci_get_drvdata(hdev); struct hci_uart *hu = hci_get_drvdata(hdev);
enum qca_btsoc_type soc_type = qca_soc_type(hu); enum qca_btsoc_type soc_type = qca_soc_type(hu);
struct qca_serdev *qcadev; struct qca_serdev *qcadev;
struct qca_data *qca = hu->priv;
int ret = 0; int ret = 0;
/* Non-serdev device usually is powered by external power /* Non-serdev device usually is powered by external power
@ -1637,6 +1642,7 @@ static int qca_power_on(struct hci_dev *hdev)
} }
} }
clear_bit(QCA_BT_OFF, &qca->flags);
return ret; return ret;
} }
@ -1649,14 +1655,14 @@ static int qca_setup(struct hci_uart *hu)
enum qca_btsoc_type soc_type = qca_soc_type(hu); enum qca_btsoc_type soc_type = qca_soc_type(hu);
const char *firmware_name = qca_get_firmware_name(hu); const char *firmware_name = qca_get_firmware_name(hu);
int ret; int ret;
int soc_ver = 0; struct qca_btsoc_version ver;
ret = qca_check_speeds(hu); ret = qca_check_speeds(hu);
if (ret) if (ret)
return ret; return ret;
/* Patch downloading has to be done without IBS mode */ /* Patch downloading has to be done without IBS mode */
clear_bit(QCA_IBS_ENABLED, &qca->flags); set_bit(QCA_IBS_DISABLED, &qca->flags);
/* Enable controller to do both LE scan and BR/EDR inquiry /* Enable controller to do both LE scan and BR/EDR inquiry
* simultaneously. * simultaneously.
@ -1671,16 +1677,16 @@ static int qca_setup(struct hci_uart *hu)
retry: retry:
ret = qca_power_on(hdev); ret = qca_power_on(hdev);
if (ret) if (ret)
return ret; goto out;
clear_bit(QCA_SSR_TRIGGERED, &qca->flags); clear_bit(QCA_SSR_TRIGGERED, &qca->flags);
if (qca_is_wcn399x(soc_type)) { if (qca_is_wcn399x(soc_type)) {
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
ret = qca_read_soc_version(hdev, &soc_ver, soc_type); ret = qca_read_soc_version(hdev, &ver, soc_type);
if (ret) if (ret)
return ret; goto out;
} else { } else {
qca_set_speed(hu, QCA_INIT_SPEED); qca_set_speed(hu, QCA_INIT_SPEED);
} }
@ -1690,24 +1696,23 @@ retry:
if (speed) { if (speed) {
ret = qca_set_speed(hu, QCA_OPER_SPEED); ret = qca_set_speed(hu, QCA_OPER_SPEED);
if (ret) if (ret)
return ret; goto out;
qca_baudrate = qca_get_baudrate_value(speed); qca_baudrate = qca_get_baudrate_value(speed);
} }
if (!qca_is_wcn399x(soc_type)) { if (!qca_is_wcn399x(soc_type)) {
/* Get QCA version information */ /* Get QCA version information */
ret = qca_read_soc_version(hdev, &soc_ver, soc_type); ret = qca_read_soc_version(hdev, &ver, soc_type);
if (ret) if (ret)
return ret; goto out;
} }
bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
/* Setup patch / NVM configurations */ /* Setup patch / NVM configurations */
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver, ret = qca_uart_setup(hdev, qca_baudrate, soc_type, ver,
firmware_name); firmware_name);
if (!ret) { if (!ret) {
set_bit(QCA_IBS_ENABLED, &qca->flags); clear_bit(QCA_IBS_DISABLED, &qca->flags);
qca_debugfs_init(hdev); qca_debugfs_init(hdev);
hu->hdev->hw_error = qca_hw_error; hu->hdev->hw_error = qca_hw_error;
hu->hdev->cmd_timeout = qca_cmd_timeout; hu->hdev->cmd_timeout = qca_cmd_timeout;
@ -1720,20 +1725,22 @@ retry:
* patch/nvm-config is found, so run with original fw/config. * patch/nvm-config is found, so run with original fw/config.
*/ */
ret = 0; ret = 0;
} else { }
if (retries < MAX_INIT_RETRIES) {
qca_power_shutdown(hu); out:
if (hu->serdev) { if (ret && retries < MAX_INIT_RETRIES) {
serdev_device_close(hu->serdev); bt_dev_warn(hdev, "Retry BT power ON:%d", retries);
ret = serdev_device_open(hu->serdev); qca_power_shutdown(hu);
if (ret) { if (hu->serdev) {
bt_dev_err(hdev, "failed to open port"); serdev_device_close(hu->serdev);
return ret; ret = serdev_device_open(hu->serdev);
} if (ret) {
bt_dev_err(hdev, "failed to open port");
return ret;
} }
retries++;
goto retry;
} }
retries++;
goto retry;
} }
/* Setup bdaddr */ /* Setup bdaddr */
@ -1780,7 +1787,7 @@ static const struct qca_device_data qca_soc_data_wcn3991 = {
{ "vddch0", 450000 }, { "vddch0", 450000 },
}, },
.num_vregs = 4, .num_vregs = 4,
.capabilities = QCA_CAP_WIDEBAND_SPEECH, .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
}; };
static const struct qca_device_data qca_soc_data_wcn3998 = { static const struct qca_device_data qca_soc_data_wcn3998 = {
@ -1813,7 +1820,7 @@ static void qca_power_shutdown(struct hci_uart *hu)
* data in skb's. * data in skb's.
*/ */
spin_lock_irqsave(&qca->hci_ibs_lock, flags); spin_lock_irqsave(&qca->hci_ibs_lock, flags);
clear_bit(QCA_IBS_ENABLED, &qca->flags); set_bit(QCA_IBS_DISABLED, &qca->flags);
qca_flush(hu); qca_flush(hu);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
@ -1830,6 +1837,8 @@ static void qca_power_shutdown(struct hci_uart *hu)
} else if (qcadev->bt_en) { } else if (qcadev->bt_en) {
gpiod_set_value_cansleep(qcadev->bt_en, 0); gpiod_set_value_cansleep(qcadev->bt_en, 0);
} }
set_bit(QCA_BT_OFF, &qca->flags);
} }
static int qca_power_off(struct hci_dev *hdev) static int qca_power_off(struct hci_dev *hdev)
@ -2017,11 +2026,17 @@ static int qca_serdev_probe(struct serdev_device *serdev)
hdev->shutdown = qca_power_off; hdev->shutdown = qca_power_off;
} }
/* Wideband speech support must be set per driver since it can't be if (data) {
* queried via hci. /* Wideband speech support must be set per driver since it can't
*/ * be queried via hci. Same with the valid le states quirk.
if (data && (data->capabilities & QCA_CAP_WIDEBAND_SPEECH)) */
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); if (data->capabilities & QCA_CAP_WIDEBAND_SPEECH)
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
&hdev->quirks);
if (data->capabilities & QCA_CAP_VALID_LE_STATES)
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
}
return 0; return 0;
} }
@ -2081,11 +2096,34 @@ static int __maybe_unused qca_suspend(struct device *dev)
bool tx_pending = false; bool tx_pending = false;
int ret = 0; int ret = 0;
u8 cmd; u8 cmd;
u32 wait_timeout = 0;
set_bit(QCA_SUSPENDING, &qca->flags); set_bit(QCA_SUSPENDING, &qca->flags);
/* Device is downloading patch or doesn't support in-band sleep. */ if (test_bit(QCA_BT_OFF, &qca->flags))
if (!test_bit(QCA_IBS_ENABLED, &qca->flags)) return 0;
if (test_bit(QCA_IBS_DISABLED, &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.
*/
wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED,
TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout));
if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
bt_dev_err(hu->hdev, "SSR or FW download time out");
ret = -ETIMEDOUT;
goto error;
}
}
/* After memory dump collection, Controller is powered off.*/
if (test_bit(QCA_BT_OFF, &qca->flags))
return 0; return 0;
cancel_work_sync(&qca->ws_awake_device); cancel_work_sync(&qca->ws_awake_device);

View File

@ -758,7 +758,6 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
mhi_chan->offload_ch = ch_cfg->offload_channel; mhi_chan->offload_ch = ch_cfg->offload_channel;
mhi_chan->db_cfg.reset_req = ch_cfg->doorbell_mode_switch; mhi_chan->db_cfg.reset_req = ch_cfg->doorbell_mode_switch;
mhi_chan->pre_alloc = ch_cfg->auto_queue; mhi_chan->pre_alloc = ch_cfg->auto_queue;
mhi_chan->auto_start = ch_cfg->auto_start;
/* /*
* If MHI host allocates buffers, then the channel direction * If MHI host allocates buffers, then the channel direction
@ -1160,11 +1159,6 @@ static int mhi_driver_probe(struct device *dev)
goto exit_probe; goto exit_probe;
ul_chan->xfer_cb = mhi_drv->ul_xfer_cb; ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
if (ul_chan->auto_start) {
ret = mhi_prepare_channel(mhi_cntrl, ul_chan);
if (ret)
goto exit_probe;
}
} }
ret = -EINVAL; ret = -EINVAL;
@ -1198,9 +1192,6 @@ static int mhi_driver_probe(struct device *dev)
if (ret) if (ret)
goto exit_probe; goto exit_probe;
if (dl_chan && dl_chan->auto_start)
mhi_prepare_channel(mhi_cntrl, dl_chan);
mhi_device_put(mhi_dev); mhi_device_put(mhi_dev);
return ret; return ret;

View File

@ -563,7 +563,6 @@ struct mhi_chan {
bool configured; bool configured;
bool offload_ch; bool offload_ch;
bool pre_alloc; bool pre_alloc;
bool auto_start;
bool wake_capable; bool wake_capable;
}; };

View File

@ -1165,6 +1165,17 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
} }
EXPORT_SYMBOL_GPL(mhi_queue_buf); EXPORT_SYMBOL_GPL(mhi_queue_buf);
bool mhi_queue_is_full(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 mhi_is_ring_full(mhi_cntrl, tre_ring);
}
EXPORT_SYMBOL_GPL(mhi_queue_is_full);
int mhi_send_cmd(struct mhi_controller *mhi_cntrl, int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan, struct mhi_chan *mhi_chan,
enum mhi_cmd_type cmd) enum mhi_cmd_type cmd)

View File

@ -545,14 +545,10 @@ static void cgr_cb(struct qman_portal *qm, struct qman_cgr *cgr, int congested)
} }
} }
static int caam_qi_napi_schedule(struct qman_portal *p, struct caam_napi *np) static int caam_qi_napi_schedule(struct qman_portal *p, struct caam_napi *np,
bool sched_napi)
{ {
/* if (sched_napi) {
* In case of threaded ISR, for RT kernels in_irq() does not return
* appropriate value, so use in_serving_softirq to distinguish between
* softirq and irq contexts.
*/
if (unlikely(in_irq() || !in_serving_softirq())) {
/* Disable QMan IRQ source and invoke NAPI */ /* Disable QMan IRQ source and invoke NAPI */
qman_p_irqsource_remove(p, QM_PIRQ_DQRI); qman_p_irqsource_remove(p, QM_PIRQ_DQRI);
np->p = p; np->p = p;
@ -564,7 +560,8 @@ static int caam_qi_napi_schedule(struct qman_portal *p, struct caam_napi *np)
static enum qman_cb_dqrr_result caam_rsp_fq_dqrr_cb(struct qman_portal *p, static enum qman_cb_dqrr_result caam_rsp_fq_dqrr_cb(struct qman_portal *p,
struct qman_fq *rsp_fq, struct qman_fq *rsp_fq,
const struct qm_dqrr_entry *dqrr) const struct qm_dqrr_entry *dqrr,
bool sched_napi)
{ {
struct caam_napi *caam_napi = raw_cpu_ptr(&pcpu_qipriv.caam_napi); struct caam_napi *caam_napi = raw_cpu_ptr(&pcpu_qipriv.caam_napi);
struct caam_drv_req *drv_req; struct caam_drv_req *drv_req;
@ -573,7 +570,7 @@ static enum qman_cb_dqrr_result caam_rsp_fq_dqrr_cb(struct qman_portal *p,
struct caam_drv_private *priv = dev_get_drvdata(qidev); struct caam_drv_private *priv = dev_get_drvdata(qidev);
u32 status; u32 status;
if (caam_qi_napi_schedule(p, caam_napi)) if (caam_qi_napi_schedule(p, caam_napi, sched_napi))
return qman_cb_dqrr_stop; return qman_cb_dqrr_stop;
fd = &dqrr->fd; fd = &dqrr->fd;

View File

@ -932,7 +932,7 @@ static int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
if (tb[RDMA_NLDEV_ATTR_DEV_NAME]) { if (tb[RDMA_NLDEV_ATTR_DEV_NAME]) {
char name[IB_DEVICE_NAME_MAX] = {}; char name[IB_DEVICE_NAME_MAX] = {};
nla_strlcpy(name, tb[RDMA_NLDEV_ATTR_DEV_NAME], nla_strscpy(name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
IB_DEVICE_NAME_MAX); IB_DEVICE_NAME_MAX);
if (strlen(name) == 0) { if (strlen(name) == 0) {
err = -EINVAL; err = -EINVAL;
@ -1529,13 +1529,13 @@ static int nldev_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
!tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME]) !tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME])
return -EINVAL; return -EINVAL;
nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME], nla_strscpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
sizeof(ibdev_name)); sizeof(ibdev_name));
if (strchr(ibdev_name, '%') || strlen(ibdev_name) == 0) if (strchr(ibdev_name, '%') || strlen(ibdev_name) == 0)
return -EINVAL; return -EINVAL;
nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type)); nla_strscpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
nla_strlcpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME], nla_strscpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME],
sizeof(ndev_name)); sizeof(ndev_name));
ndev = dev_get_by_name(sock_net(skb->sk), ndev_name); ndev = dev_get_by_name(sock_net(skb->sk), ndev_name);
@ -1602,7 +1602,7 @@ static int nldev_get_chardev(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err || !tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE]) if (err || !tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE])
return -EINVAL; return -EINVAL;
nla_strlcpy(client_name, tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE], nla_strscpy(client_name, tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE],
sizeof(client_name)); sizeof(client_name));
if (tb[RDMA_NLDEV_ATTR_DEV_INDEX]) { if (tb[RDMA_NLDEV_ATTR_DEV_INDEX]) {

View File

@ -1686,7 +1686,6 @@ static void hfi1_ipoib_ib_rcv(struct hfi1_packet *packet)
u32 extra_bytes; u32 extra_bytes;
u32 tlen, qpnum; u32 tlen, qpnum;
bool do_work, do_cnp; bool do_work, do_cnp;
struct hfi1_ipoib_dev_priv *priv;
trace_hfi1_rcvhdr(packet); trace_hfi1_rcvhdr(packet);
@ -1734,8 +1733,7 @@ static void hfi1_ipoib_ib_rcv(struct hfi1_packet *packet)
if (unlikely(!skb)) if (unlikely(!skb))
goto drop; goto drop;
priv = hfi1_ipoib_priv(netdev); dev_sw_netstats_rx_add(netdev, skb->len);
hfi1_ipoib_update_rx_netstats(priv, 1, skb->len);
skb->dev = netdev; skb->dev = netdev;
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;

View File

@ -110,7 +110,6 @@ struct hfi1_ipoib_dev_priv {
const struct net_device_ops *netdev_ops; const struct net_device_ops *netdev_ops;
struct rvt_qp *qp; struct rvt_qp *qp;
struct pcpu_sw_netstats __percpu *netstats;
}; };
/* hfi1 ipoib rdma netdev's private data structure */ /* hfi1 ipoib rdma netdev's private data structure */
@ -126,32 +125,6 @@ hfi1_ipoib_priv(const struct net_device *dev)
return &((struct hfi1_ipoib_rdma_netdev *)netdev_priv(dev))->dev_priv; return &((struct hfi1_ipoib_rdma_netdev *)netdev_priv(dev))->dev_priv;
} }
static inline void
hfi1_ipoib_update_rx_netstats(struct hfi1_ipoib_dev_priv *priv,
u64 packets,
u64 bytes)
{
struct pcpu_sw_netstats *netstats = this_cpu_ptr(priv->netstats);
u64_stats_update_begin(&netstats->syncp);
netstats->rx_packets += packets;
netstats->rx_bytes += bytes;
u64_stats_update_end(&netstats->syncp);
}
static inline void
hfi1_ipoib_update_tx_netstats(struct hfi1_ipoib_dev_priv *priv,
u64 packets,
u64 bytes)
{
struct pcpu_sw_netstats *netstats = this_cpu_ptr(priv->netstats);
u64_stats_update_begin(&netstats->syncp);
netstats->tx_packets += packets;
netstats->tx_bytes += bytes;
u64_stats_update_end(&netstats->syncp);
}
int hfi1_ipoib_send_dma(struct net_device *dev, int hfi1_ipoib_send_dma(struct net_device *dev,
struct sk_buff *skb, struct sk_buff *skb,
struct ib_ah *address, struct ib_ah *address,

View File

@ -21,7 +21,7 @@ static int hfi1_ipoib_dev_init(struct net_device *dev)
struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
int ret; int ret;
priv->netstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
ret = priv->netdev_ops->ndo_init(dev); ret = priv->netdev_ops->ndo_init(dev);
if (ret) if (ret)
@ -93,21 +93,12 @@ static int hfi1_ipoib_dev_stop(struct net_device *dev)
return priv->netdev_ops->ndo_stop(dev); return priv->netdev_ops->ndo_stop(dev);
} }
static void hfi1_ipoib_dev_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *storage)
{
struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
netdev_stats_to_stats64(storage, &dev->stats);
dev_fetch_sw_netstats(storage, priv->netstats);
}
static const struct net_device_ops hfi1_ipoib_netdev_ops = { static const struct net_device_ops hfi1_ipoib_netdev_ops = {
.ndo_init = hfi1_ipoib_dev_init, .ndo_init = hfi1_ipoib_dev_init,
.ndo_uninit = hfi1_ipoib_dev_uninit, .ndo_uninit = hfi1_ipoib_dev_uninit,
.ndo_open = hfi1_ipoib_dev_open, .ndo_open = hfi1_ipoib_dev_open,
.ndo_stop = hfi1_ipoib_dev_stop, .ndo_stop = hfi1_ipoib_dev_stop,
.ndo_get_stats64 = hfi1_ipoib_dev_get_stats64, .ndo_get_stats64 = dev_get_tstats64,
}; };
static int hfi1_ipoib_send(struct net_device *dev, static int hfi1_ipoib_send(struct net_device *dev,
@ -182,7 +173,7 @@ static void hfi1_ipoib_netdev_dtor(struct net_device *dev)
hfi1_ipoib_txreq_deinit(priv); hfi1_ipoib_txreq_deinit(priv);
hfi1_ipoib_rxq_deinit(priv->netdev); hfi1_ipoib_rxq_deinit(priv->netdev);
free_percpu(priv->netstats); free_percpu(dev->tstats);
} }
static void hfi1_ipoib_free_rdma_netdev(struct net_device *dev) static void hfi1_ipoib_free_rdma_netdev(struct net_device *dev)

View File

@ -121,7 +121,7 @@ static void hfi1_ipoib_free_tx(struct ipoib_txreq *tx, int budget)
struct hfi1_ipoib_dev_priv *priv = tx->priv; struct hfi1_ipoib_dev_priv *priv = tx->priv;
if (likely(!tx->sdma_status)) { if (likely(!tx->sdma_status)) {
hfi1_ipoib_update_tx_netstats(priv, 1, tx->skb->len); dev_sw_netstats_tx_add(priv->netdev, 1, tx->skb->len);
} else { } else {
++priv->netdev->stats.tx_errors; ++priv->netdev->stats.tx_errors;
dd_dev_warn(priv->dd, dd_dev_warn(priv->dd,

View File

@ -138,13 +138,6 @@ static int mlx5_ib_create_counters(struct ib_counters *counters,
} }
static bool is_mdev_switchdev_mode(const struct mlx5_core_dev *mdev)
{
return MLX5_ESWITCH_MANAGER(mdev) &&
mlx5_ib_eswitch_mode(mdev->priv.eswitch) ==
MLX5_ESWITCH_OFFLOADS;
}
static const struct mlx5_ib_counters *get_counters(struct mlx5_ib_dev *dev, static const struct mlx5_ib_counters *get_counters(struct mlx5_ib_dev *dev,
u8 port_num) u8 port_num)
{ {

View File

@ -13,7 +13,7 @@ mlx5_ib_set_vport_rep(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
struct mlx5_ib_dev *ibdev; struct mlx5_ib_dev *ibdev;
int vport_index; int vport_index;
ibdev = mlx5_ib_get_uplink_ibdev(dev->priv.eswitch); ibdev = mlx5_eswitch_uplink_get_proto_dev(dev->priv.eswitch, REP_IB);
vport_index = rep->vport_index; vport_index = rep->vport_index;
ibdev->port[vport_index].rep = rep; ibdev->port[vport_index].rep = rep;
@ -33,6 +33,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
const struct mlx5_ib_profile *profile; const struct mlx5_ib_profile *profile;
struct mlx5_ib_dev *ibdev; struct mlx5_ib_dev *ibdev;
int vport_index; int vport_index;
int ret;
if (rep->vport == MLX5_VPORT_UPLINK) if (rep->vport == MLX5_VPORT_UPLINK)
profile = &raw_eth_profile; profile = &raw_eth_profile;
@ -46,8 +47,8 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
ibdev->port = kcalloc(num_ports, sizeof(*ibdev->port), ibdev->port = kcalloc(num_ports, sizeof(*ibdev->port),
GFP_KERNEL); GFP_KERNEL);
if (!ibdev->port) { if (!ibdev->port) {
ib_dealloc_device(&ibdev->ib_dev); ret = -ENOMEM;
return -ENOMEM; goto fail_port;
} }
ibdev->is_rep = true; ibdev->is_rep = true;
@ -58,12 +59,24 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
ibdev->mdev = dev; ibdev->mdev = dev;
ibdev->num_ports = num_ports; ibdev->num_ports = num_ports;
if (!__mlx5_ib_add(ibdev, profile)) ret = __mlx5_ib_add(ibdev, profile);
return -EINVAL; if (ret)
goto fail_add;
rep->rep_data[REP_IB].priv = ibdev; rep->rep_data[REP_IB].priv = ibdev;
return 0; return 0;
fail_add:
kfree(ibdev->port);
fail_port:
ib_dealloc_device(&ibdev->ib_dev);
return ret;
}
static void *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
{
return rep->rep_data[REP_IB].priv;
} }
static void static void
@ -83,59 +96,18 @@ mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
__mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX); __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
} }
static void *mlx5_ib_vport_get_proto_dev(struct mlx5_eswitch_rep *rep)
{
return mlx5_ib_rep_to_dev(rep);
}
static const struct mlx5_eswitch_rep_ops rep_ops = { static const struct mlx5_eswitch_rep_ops rep_ops = {
.load = mlx5_ib_vport_rep_load, .load = mlx5_ib_vport_rep_load,
.unload = mlx5_ib_vport_rep_unload, .unload = mlx5_ib_vport_rep_unload,
.get_proto_dev = mlx5_ib_vport_get_proto_dev, .get_proto_dev = mlx5_ib_rep_to_dev,
}; };
void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev)
{
struct mlx5_eswitch *esw = mdev->priv.eswitch;
mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB);
}
void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev)
{
struct mlx5_eswitch *esw = mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
}
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
{
return mlx5_eswitch_mode(esw);
}
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
u16 vport_num)
{
return mlx5_eswitch_get_proto_dev(esw, vport_num, REP_IB);
}
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw, struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
u16 vport_num) u16 vport_num)
{ {
return mlx5_eswitch_get_proto_dev(esw, vport_num, REP_ETH); return mlx5_eswitch_get_proto_dev(esw, vport_num, REP_ETH);
} }
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
{
return mlx5_eswitch_uplink_get_proto_dev(esw, REP_IB);
}
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
u16 vport_num)
{
return mlx5_eswitch_vport_rep(esw, vport_num);
}
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev, struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq, struct mlx5_ib_sq *sq,
u16 port) u16 port)
@ -154,3 +126,49 @@ struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
return mlx5_eswitch_add_send_to_vport_rule(esw, rep->vport, return mlx5_eswitch_add_send_to_vport_rule(esw, rep->vport,
sq->base.mqp.qpn); sq->base.mqp.qpn);
} }
static int mlx5r_rep_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB);
return 0;
}
static void mlx5r_rep_remove(struct auxiliary_device *adev)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
}
static const struct auxiliary_device_id mlx5r_rep_id_table[] = {
{ .name = MLX5_ADEV_NAME ".rdma-rep", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx5r_rep_id_table);
static struct auxiliary_driver mlx5r_rep_driver = {
.name = "rep",
.probe = mlx5r_rep_probe,
.remove = mlx5r_rep_remove,
.id_table = mlx5r_rep_id_table,
};
int mlx5r_rep_init(void)
{
return auxiliary_driver_register(&mlx5r_rep_driver);
}
void mlx5r_rep_cleanup(void)
{
auxiliary_driver_unregister(&mlx5r_rep_driver);
}

View File

@ -12,47 +12,16 @@
extern const struct mlx5_ib_profile raw_eth_profile; extern const struct mlx5_ib_profile raw_eth_profile;
#ifdef CONFIG_MLX5_ESWITCH #ifdef CONFIG_MLX5_ESWITCH
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw); int mlx5r_rep_init(void);
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw, void mlx5r_rep_cleanup(void);
u16 vport_num);
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw);
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
u16 vport_num);
void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev);
void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev);
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev, struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq, struct mlx5_ib_sq *sq,
u16 port); u16 port);
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw, struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
u16 vport_num); u16 vport_num);
#else /* CONFIG_MLX5_ESWITCH */ #else /* CONFIG_MLX5_ESWITCH */
static inline u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw) static inline int mlx5r_rep_init(void) { return 0; }
{ static inline void mlx5r_rep_cleanup(void) {}
return MLX5_ESWITCH_NONE;
}
static inline
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
u16 vport_num)
{
return NULL;
}
static inline
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
{
return NULL;
}
static inline
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
u16 vport_num)
{
return NULL;
}
static inline void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev) {}
static inline void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev) {}
static inline static inline
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev, struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq, struct mlx5_ib_sq *sq,
@ -68,10 +37,4 @@ struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
return NULL; return NULL;
} }
#endif #endif
static inline
struct mlx5_ib_dev *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
{
return rep->rep_data[REP_IB].priv;
}
#endif /* __MLX5_IB_REP_H__ */ #endif /* __MLX5_IB_REP_H__ */

View File

@ -4593,8 +4593,8 @@ void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
ib_dealloc_device(&dev->ib_dev); ib_dealloc_device(&dev->ib_dev);
} }
void *__mlx5_ib_add(struct mlx5_ib_dev *dev, int __mlx5_ib_add(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile) const struct mlx5_ib_profile *profile)
{ {
int err; int err;
int i; int i;
@ -4610,13 +4610,16 @@ void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
} }
dev->ib_active = true; dev->ib_active = true;
return 0;
return dev;
err_out: err_out:
__mlx5_ib_remove(dev, profile, i); /* Clean up stages which were initialized */
while (i) {
return NULL; i--;
if (profile->stage[i].cleanup)
profile->stage[i].cleanup(dev);
}
return -ENOMEM;
} }
static const struct mlx5_ib_profile pf_profile = { static const struct mlx5_ib_profile pf_profile = {
@ -4739,8 +4742,11 @@ const struct mlx5_ib_profile raw_eth_profile = {
NULL), NULL),
}; };
static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev) static int mlx5r_mp_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{ {
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_ib_multiport_info *mpi; struct mlx5_ib_multiport_info *mpi;
struct mlx5_ib_dev *dev; struct mlx5_ib_dev *dev;
bool bound = false; bool bound = false;
@ -4748,15 +4754,14 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
mpi = kzalloc(sizeof(*mpi), GFP_KERNEL); mpi = kzalloc(sizeof(*mpi), GFP_KERNEL);
if (!mpi) if (!mpi)
return NULL; return -ENOMEM;
mpi->mdev = mdev; mpi->mdev = mdev;
err = mlx5_query_nic_vport_system_image_guid(mdev, err = mlx5_query_nic_vport_system_image_guid(mdev,
&mpi->sys_image_guid); &mpi->sys_image_guid);
if (err) { if (err) {
kfree(mpi); kfree(mpi);
return NULL; return err;
} }
mutex_lock(&mlx5_ib_multiport_mutex); mutex_lock(&mlx5_ib_multiport_mutex);
@ -4777,40 +4782,46 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
} }
mutex_unlock(&mlx5_ib_multiport_mutex); mutex_unlock(&mlx5_ib_multiport_mutex);
return mpi; dev_set_drvdata(&adev->dev, mpi);
return 0;
} }
static void *mlx5_ib_add(struct mlx5_core_dev *mdev) static void mlx5r_mp_remove(struct auxiliary_device *adev)
{ {
struct mlx5_ib_multiport_info *mpi;
mpi = dev_get_drvdata(&adev->dev);
mutex_lock(&mlx5_ib_multiport_mutex);
if (mpi->ibdev)
mlx5_ib_unbind_slave_port(mpi->ibdev, mpi);
list_del(&mpi->list);
mutex_unlock(&mlx5_ib_multiport_mutex);
kfree(mpi);
}
static int mlx5r_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
const struct mlx5_ib_profile *profile; const struct mlx5_ib_profile *profile;
int port_type_cap, num_ports, ret;
enum rdma_link_layer ll; enum rdma_link_layer ll;
struct mlx5_ib_dev *dev; struct mlx5_ib_dev *dev;
int port_type_cap;
int num_ports;
if (MLX5_ESWITCH_MANAGER(mdev) &&
mlx5_ib_eswitch_mode(mdev->priv.eswitch) == MLX5_ESWITCH_OFFLOADS) {
if (!mlx5_core_mp_enabled(mdev))
mlx5_ib_register_vport_reps(mdev);
return mdev;
}
port_type_cap = MLX5_CAP_GEN(mdev, port_type); port_type_cap = MLX5_CAP_GEN(mdev, port_type);
ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap); ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
if (mlx5_core_is_mp_slave(mdev) && ll == IB_LINK_LAYER_ETHERNET)
return mlx5_ib_add_slave_port(mdev);
num_ports = max(MLX5_CAP_GEN(mdev, num_ports), num_ports = max(MLX5_CAP_GEN(mdev, num_ports),
MLX5_CAP_GEN(mdev, num_vhca_ports)); MLX5_CAP_GEN(mdev, num_vhca_ports));
dev = ib_alloc_device(mlx5_ib_dev, ib_dev); dev = ib_alloc_device(mlx5_ib_dev, ib_dev);
if (!dev) if (!dev)
return NULL; return -ENOMEM;
dev->port = kcalloc(num_ports, sizeof(*dev->port), dev->port = kcalloc(num_ports, sizeof(*dev->port),
GFP_KERNEL); GFP_KERNEL);
if (!dev->port) { if (!dev->port) {
ib_dealloc_device(&dev->ib_dev); ib_dealloc_device(&dev->ib_dev);
return NULL; return -ENOMEM;
} }
dev->mdev = mdev; dev->mdev = mdev;
@ -4821,38 +4832,50 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
else else
profile = &pf_profile; profile = &pf_profile;
return __mlx5_ib_add(dev, profile); ret = __mlx5_ib_add(dev, profile);
if (ret) {
kfree(dev->port);
ib_dealloc_device(&dev->ib_dev);
return ret;
}
dev_set_drvdata(&adev->dev, dev);
return 0;
} }
static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context) static void mlx5r_remove(struct auxiliary_device *adev)
{ {
struct mlx5_ib_multiport_info *mpi;
struct mlx5_ib_dev *dev; struct mlx5_ib_dev *dev;
if (MLX5_ESWITCH_MANAGER(mdev) && context == mdev) { dev = dev_get_drvdata(&adev->dev);
mlx5_ib_unregister_vport_reps(mdev);
return;
}
if (mlx5_core_is_mp_slave(mdev)) {
mpi = context;
mutex_lock(&mlx5_ib_multiport_mutex);
if (mpi->ibdev)
mlx5_ib_unbind_slave_port(mpi->ibdev, mpi);
list_del(&mpi->list);
mutex_unlock(&mlx5_ib_multiport_mutex);
kfree(mpi);
return;
}
dev = context;
__mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX); __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
} }
static struct mlx5_interface mlx5_ib_interface = { static const struct auxiliary_device_id mlx5r_mp_id_table[] = {
.add = mlx5_ib_add, { .name = MLX5_ADEV_NAME ".multiport", },
.remove = mlx5_ib_remove, {},
.protocol = MLX5_INTERFACE_PROTOCOL_IB, };
static const struct auxiliary_device_id mlx5r_id_table[] = {
{ .name = MLX5_ADEV_NAME ".rdma", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx5r_mp_id_table);
MODULE_DEVICE_TABLE(auxiliary, mlx5r_id_table);
static struct auxiliary_driver mlx5r_mp_driver = {
.name = "multiport",
.probe = mlx5r_mp_probe,
.remove = mlx5r_mp_remove,
.id_table = mlx5r_mp_id_table,
};
static struct auxiliary_driver mlx5r_driver = {
.name = "rdma",
.probe = mlx5r_probe,
.remove = mlx5r_remove,
.id_table = mlx5r_id_table,
}; };
unsigned long mlx5_ib_get_xlt_emergency_page(void) unsigned long mlx5_ib_get_xlt_emergency_page(void)
@ -4868,7 +4891,7 @@ void mlx5_ib_put_xlt_emergency_page(void)
static int __init mlx5_ib_init(void) static int __init mlx5_ib_init(void)
{ {
int err; int ret;
xlt_emergency_page = __get_free_page(GFP_KERNEL); xlt_emergency_page = __get_free_page(GFP_KERNEL);
if (!xlt_emergency_page) if (!xlt_emergency_page)
@ -4883,15 +4906,33 @@ static int __init mlx5_ib_init(void)
} }
mlx5_ib_odp_init(); mlx5_ib_odp_init();
ret = mlx5r_rep_init();
if (ret)
goto rep_err;
ret = auxiliary_driver_register(&mlx5r_mp_driver);
if (ret)
goto mp_err;
ret = auxiliary_driver_register(&mlx5r_driver);
if (ret)
goto drv_err;
return 0;
err = mlx5_register_interface(&mlx5_ib_interface); drv_err:
auxiliary_driver_unregister(&mlx5r_mp_driver);
return err; mp_err:
mlx5r_rep_cleanup();
rep_err:
destroy_workqueue(mlx5_ib_event_wq);
free_page((unsigned long)xlt_emergency_page);
return ret;
} }
static void __exit mlx5_ib_cleanup(void) static void __exit mlx5_ib_cleanup(void)
{ {
mlx5_unregister_interface(&mlx5_ib_interface); auxiliary_driver_unregister(&mlx5r_driver);
auxiliary_driver_unregister(&mlx5r_mp_driver);
mlx5r_rep_cleanup();
destroy_workqueue(mlx5_ib_event_wq); destroy_workqueue(mlx5_ib_event_wq);
mutex_destroy(&xlt_emergency_page_mutex); mutex_destroy(&xlt_emergency_page_mutex);
free_page(xlt_emergency_page); free_page(xlt_emergency_page);

View File

@ -1317,8 +1317,8 @@ extern const struct mmu_interval_notifier_ops mlx5_mn_ops;
void __mlx5_ib_remove(struct mlx5_ib_dev *dev, void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile, const struct mlx5_ib_profile *profile,
int stage); int stage);
void *__mlx5_ib_add(struct mlx5_ib_dev *dev, int __mlx5_ib_add(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile); const struct mlx5_ib_profile *profile);
int mlx5_ib_get_vf_config(struct ib_device *device, int vf, int mlx5_ib_get_vf_config(struct ib_device *device, int vf,
u8 port, struct ifla_vf_info *info); u8 port, struct ifla_vf_info *info);

View File

@ -11,6 +11,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/ethtool.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/major.h> #include <linux/major.h>

View File

@ -11,6 +11,7 @@
* the project's page is at https://linuxtv.org * the project's page is at https://linuxtv.org
*/ */
#include <linux/ethtool.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> #include <linux/string.h>

View File

@ -287,7 +287,7 @@ config GTP
3GPP TS 29.060 standards. 3GPP TS 29.060 standards.
To compile this drivers as a module, choose M here: the module To compile this drivers as a module, choose M here: the module
wil be called gtp. will be called gtp.
config MACSEC config MACSEC
tristate "IEEE 802.1AE MAC-level encryption (MACsec)" tristate "IEEE 802.1AE MAC-level encryption (MACsec)"
@ -426,6 +426,13 @@ config VSOCKMON
mostly intended for developers or support to debug vsock issues. If mostly intended for developers or support to debug vsock issues. If
unsure, say N. unsure, say N.
config MHI_NET
tristate "MHI network driver"
depends on MHI_BUS
help
This is the network driver for MHI bus. It can be used with
QCOM based WWAN modems (like SDX55). Say Y or M.
endif # NET_CORE endif # NET_CORE
config SUNGEM_PHY config SUNGEM_PHY
@ -489,8 +496,6 @@ source "drivers/net/usb/Kconfig"
source "drivers/net/wireless/Kconfig" source "drivers/net/wireless/Kconfig"
source "drivers/net/wimax/Kconfig"
source "drivers/net/wan/Kconfig" source "drivers/net/wan/Kconfig"
source "drivers/net/ieee802154/Kconfig" source "drivers/net/ieee802154/Kconfig"

View File

@ -36,6 +36,7 @@ obj-$(CONFIG_GTP) += gtp.o
obj-$(CONFIG_NLMON) += nlmon.o obj-$(CONFIG_NLMON) += nlmon.o
obj-$(CONFIG_NET_VRF) += vrf.o obj-$(CONFIG_NET_VRF) += vrf.o
obj-$(CONFIG_VSOCKMON) += vsockmon.o obj-$(CONFIG_VSOCKMON) += vsockmon.o
obj-$(CONFIG_MHI_NET) += mhi_net.o
# #
# Networking Drivers # Networking Drivers
@ -66,7 +67,6 @@ obj-$(CONFIG_NET_SB1000) += sb1000.o
obj-$(CONFIG_SUNGEM_PHY) += sungem_phy.o obj-$(CONFIG_SUNGEM_PHY) += sungem_phy.o
obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_WAN) += wan/
obj-$(CONFIG_WLAN) += wireless/ obj-$(CONFIG_WLAN) += wireless/
obj-$(CONFIG_WIMAX) += wimax/
obj-$(CONFIG_IEEE802154) += ieee802154/ obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_VMXNET3) += vmxnet3/ obj-$(CONFIG_VMXNET3) += vmxnet3/

View File

@ -510,7 +510,7 @@ static const struct net_device_ops bareudp_netdev_ops = {
.ndo_open = bareudp_open, .ndo_open = bareudp_open,
.ndo_stop = bareudp_stop, .ndo_stop = bareudp_stop,
.ndo_start_xmit = bareudp_xmit, .ndo_start_xmit = bareudp_xmit,
.ndo_get_stats64 = ip_tunnel_get_stats64, .ndo_get_stats64 = dev_get_tstats64,
.ndo_fill_metadata_dst = bareudp_fill_metadata_dst, .ndo_fill_metadata_dst = bareudp_fill_metadata_dst,
}; };
@ -522,7 +522,7 @@ static const struct nla_policy bareudp_policy[IFLA_BAREUDP_MAX + 1] = {
}; };
/* Info for udev, that this is a virtual tunnel endpoint */ /* Info for udev, that this is a virtual tunnel endpoint */
static struct device_type bareudp_type = { static const struct device_type bareudp_type = {
.name = "bareudp", .name = "bareudp",
}; };

View File

@ -1228,14 +1228,14 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
} }
#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ #define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \ NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
NETIF_F_HIGHDMA | NETIF_F_LRO) NETIF_F_HIGHDMA | NETIF_F_LRO)
#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ #define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_RXCSUM | NETIF_F_ALL_TSO) NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
#define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ #define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_ALL_TSO) NETIF_F_GSO_SOFTWARE)
static void bond_compute_features(struct bonding *bond) static void bond_compute_features(struct bonding *bond)
@ -1291,8 +1291,7 @@ done:
bond_dev->vlan_features = vlan_features; bond_dev->vlan_features = vlan_features;
bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL | bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_STAG_TX;
NETIF_F_GSO_UDP_L4;
#ifdef CONFIG_XFRM_OFFLOAD #ifdef CONFIG_XFRM_OFFLOAD
bond_dev->hw_enc_features |= xfrm_features; bond_dev->hw_enc_features |= xfrm_features;
#endif /* CONFIG_XFRM_OFFLOAD */ #endif /* CONFIG_XFRM_OFFLOAD */
@ -4746,16 +4745,14 @@ void bond_setup(struct net_device *bond_dev)
NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER; NETIF_F_HW_VLAN_CTAG_FILTER;
bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL | NETIF_F_GSO_UDP_L4; bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
#ifdef CONFIG_XFRM_OFFLOAD
bond_dev->hw_features |= BOND_XFRM_FEATURES;
#endif /* CONFIG_XFRM_OFFLOAD */
bond_dev->features |= bond_dev->hw_features; bond_dev->features |= bond_dev->hw_features;
bond_dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; bond_dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
#ifdef CONFIG_XFRM_OFFLOAD #ifdef CONFIG_XFRM_OFFLOAD
/* Disable XFRM features if this isn't an active-backup config */ bond_dev->hw_features |= BOND_XFRM_FEATURES;
if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) /* Only enable XFRM features if this is an active-backup config */
bond_dev->features &= ~BOND_XFRM_FEATURES; if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
bond_dev->features |= BOND_XFRM_FEATURES;
#endif /* CONFIG_XFRM_OFFLOAD */ #endif /* CONFIG_XFRM_OFFLOAD */
} }

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/ethtool.h>
#include <linux/export.h> #include <linux/export.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>

View File

@ -468,7 +468,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
} }
reg_mid = at91_can_id_to_reg_mid(cf->can_id); reg_mid = at91_can_id_to_reg_mid(cf->can_id);
reg_mcr = ((cf->can_id & CAN_RTR_FLAG) ? AT91_MCR_MRTR : 0) | reg_mcr = ((cf->can_id & CAN_RTR_FLAG) ? AT91_MCR_MRTR : 0) |
(cf->can_dlc << 16) | AT91_MCR_MTCR; (cf->len << 16) | AT91_MCR_MTCR;
/* disable MB while writing ID (see datasheet) */ /* disable MB while writing ID (see datasheet) */
set_mb_mode(priv, mb, AT91_MB_MODE_DISABLED); set_mb_mode(priv, mb, AT91_MB_MODE_DISABLED);
@ -481,7 +481,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* This triggers transmission */ /* This triggers transmission */
at91_write(priv, AT91_MCR(mb), reg_mcr); at91_write(priv, AT91_MCR(mb), reg_mcr);
stats->tx_bytes += cf->can_dlc; stats->tx_bytes += cf->len;
/* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */ /* _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));
@ -554,7 +554,7 @@ static void at91_rx_overflow_err(struct net_device *dev)
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
} }
@ -580,7 +580,7 @@ static void at91_read_mb(struct net_device *dev, unsigned int mb,
cf->can_id = (reg_mid >> 18) & CAN_SFF_MASK; cf->can_id = (reg_mid >> 18) & CAN_SFF_MASK;
reg_msr = at91_read(priv, AT91_MSR(mb)); reg_msr = at91_read(priv, AT91_MSR(mb));
cf->can_dlc = get_can_dlc((reg_msr >> 16) & 0xf); cf->len = can_cc_dlc2len((reg_msr >> 16) & 0xf);
if (reg_msr & AT91_MSR_MRTR) if (reg_msr & AT91_MSR_MRTR)
cf->can_id |= CAN_RTR_FLAG; cf->can_id |= CAN_RTR_FLAG;
@ -619,7 +619,7 @@ static void at91_read_msg(struct net_device *dev, unsigned int mb)
at91_read_mb(dev, mb, cf); at91_read_mb(dev, mb, cf);
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
can_led_event(dev, CAN_LED_EVENT_RX); can_led_event(dev, CAN_LED_EVENT_RX);
@ -780,7 +780,7 @@ static int at91_poll_err(struct net_device *dev, int quota, u32 reg_sr)
at91_poll_err_frame(dev, cf, reg_sr); at91_poll_err_frame(dev, cf, reg_sr);
dev->stats.rx_packets++; dev->stats.rx_packets++;
dev->stats.rx_bytes += cf->can_dlc; dev->stats.rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 1; return 1;
@ -1047,7 +1047,7 @@ static void at91_irq_err(struct net_device *dev)
at91_irq_err_state(dev, cf, new_state); at91_irq_err_state(dev, cf, new_state);
dev->stats.rx_packets++; dev->stats.rx_packets++;
dev->stats.rx_bytes += cf->can_dlc; dev->stats.rx_bytes += cf->len;
netif_rx(skb); netif_rx(skb);
priv->can.state = new_state; priv->can.state = new_state;

View File

@ -306,7 +306,7 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
struct can_frame *frame, int idx) struct can_frame *frame, int idx)
{ {
struct c_can_priv *priv = netdev_priv(dev); struct c_can_priv *priv = netdev_priv(dev);
u16 ctrl = IF_MCONT_TX | frame->can_dlc; u16 ctrl = IF_MCONT_TX | frame->len;
bool rtr = frame->can_id & CAN_RTR_FLAG; bool rtr = frame->can_id & CAN_RTR_FLAG;
u32 arb = IF_ARB_MSGVAL; u32 arb = IF_ARB_MSGVAL;
int i; int i;
@ -339,7 +339,7 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
if (priv->type == BOSCH_D_CAN) { if (priv->type == BOSCH_D_CAN) {
u32 data = 0, dreg = C_CAN_IFACE(DATA1_REG, iface); u32 data = 0, dreg = C_CAN_IFACE(DATA1_REG, iface);
for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) { for (i = 0; i < frame->len; i += 4, dreg += 2) {
data = (u32)frame->data[i]; data = (u32)frame->data[i];
data |= (u32)frame->data[i + 1] << 8; data |= (u32)frame->data[i + 1] << 8;
data |= (u32)frame->data[i + 2] << 16; data |= (u32)frame->data[i + 2] << 16;
@ -347,7 +347,7 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
priv->write_reg32(priv, dreg, data); priv->write_reg32(priv, dreg, data);
} }
} else { } else {
for (i = 0; i < frame->can_dlc; i += 2) { for (i = 0; i < frame->len; i += 2) {
priv->write_reg(priv, priv->write_reg(priv,
C_CAN_IFACE(DATA1_REG, iface) + i / 2, C_CAN_IFACE(DATA1_REG, iface) + i / 2,
frame->data[i] | frame->data[i] |
@ -397,7 +397,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
return -ENOMEM; return -ENOMEM;
} }
frame->can_dlc = get_can_dlc(ctrl & 0x0F); frame->len = can_cc_dlc2len(ctrl & 0x0F);
arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface)); arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface));
@ -412,7 +412,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
int i, dreg = C_CAN_IFACE(DATA1_REG, iface); int i, dreg = C_CAN_IFACE(DATA1_REG, iface);
if (priv->type == BOSCH_D_CAN) { if (priv->type == BOSCH_D_CAN) {
for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) { for (i = 0; i < frame->len; i += 4, dreg += 2) {
data = priv->read_reg32(priv, dreg); data = priv->read_reg32(priv, dreg);
frame->data[i] = data; frame->data[i] = data;
frame->data[i + 1] = data >> 8; frame->data[i + 1] = data >> 8;
@ -420,7 +420,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
frame->data[i + 3] = data >> 24; frame->data[i + 3] = data >> 24;
} }
} else { } else {
for (i = 0; i < frame->can_dlc; i += 2, dreg++) { for (i = 0; i < frame->len; i += 2, dreg++) {
data = priv->read_reg(priv, dreg); data = priv->read_reg(priv, dreg);
frame->data[i] = data; frame->data[i] = data;
frame->data[i + 1] = data >> 8; frame->data[i + 1] = data >> 8;
@ -429,7 +429,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += frame->can_dlc; stats->rx_bytes += frame->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 0; return 0;
@ -475,7 +475,7 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
* transmit as we might race against do_tx(). * transmit as we might race against do_tx().
*/ */
c_can_setup_tx_object(dev, IF_TX, frame, idx); c_can_setup_tx_object(dev, IF_TX, frame, idx);
priv->dlc[idx] = frame->can_dlc; priv->dlc[idx] = frame->len;
can_put_echo_skb(skb, dev, idx); can_put_echo_skb(skb, dev, idx);
/* Update the active bits */ /* Update the active bits */
@ -977,7 +977,7 @@ static int c_can_handle_state_change(struct net_device *dev,
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 1; return 1;
@ -1047,7 +1047,7 @@ static int c_can_handle_bus_err(struct net_device *dev,
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 1; return 1;
} }

View File

@ -390,7 +390,7 @@ static void cc770_tx(struct net_device *dev, int mo)
u32 id; u32 id;
int i; int i;
dlc = cf->can_dlc; dlc = cf->len;
id = cf->can_id; id = cf->can_id;
rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR; rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR;
@ -470,7 +470,7 @@ static void cc770_rx(struct net_device *dev, unsigned int mo, u8 ctrl1)
cf->can_id = CAN_RTR_FLAG; cf->can_id = CAN_RTR_FLAG;
if (config & MSGCFG_XTD) if (config & MSGCFG_XTD)
cf->can_id |= CAN_EFF_FLAG; cf->can_id |= CAN_EFF_FLAG;
cf->can_dlc = 0; cf->len = 0;
} else { } else {
if (config & MSGCFG_XTD) { if (config & MSGCFG_XTD) {
id = cc770_read_reg(priv, msgobj[mo].id[3]); id = cc770_read_reg(priv, msgobj[mo].id[3]);
@ -486,13 +486,13 @@ static void cc770_rx(struct net_device *dev, unsigned int mo, u8 ctrl1)
} }
cf->can_id = id; cf->can_id = id;
cf->can_dlc = get_can_dlc((config & 0xf0) >> 4); cf->len = can_cc_dlc2len((config & 0xf0) >> 4);
for (i = 0; i < cf->can_dlc; i++) for (i = 0; i < cf->len; i++)
cf->data[i] = cc770_read_reg(priv, msgobj[mo].data[i]); cf->data[i] = cc770_read_reg(priv, msgobj[mo].data[i]);
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_rx(skb); netif_rx(skb);
} }
@ -572,7 +572,7 @@ static int cc770_err(struct net_device *dev, u8 status)
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_rx(skb); netif_rx(skb);
return 0; return 0;
@ -699,7 +699,7 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o)
} }
cf = (struct can_frame *)priv->tx_skb->data; cf = (struct can_frame *)priv->tx_skb->data;
stats->tx_bytes += cf->can_dlc; stats->tx_bytes += cf->len;
stats->tx_packets++; stats->tx_packets++;
can_put_echo_skb(priv->tx_skb, dev, 0); can_put_echo_skb(priv->tx_skb, dev, 0);

View File

@ -30,12 +30,12 @@ MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 12, 16, 20, 24, 32, 48, 64}; 8, 12, 16, 20, 24, 32, 48, 64};
/* get data length from can_dlc with sanitized can_dlc */ /* get data length from raw data length code (DLC) */
u8 can_dlc2len(u8 can_dlc) u8 can_fd_dlc2len(u8 dlc)
{ {
return dlc2len[can_dlc & 0x0F]; return dlc2len[dlc & 0x0F];
} }
EXPORT_SYMBOL_GPL(can_dlc2len); EXPORT_SYMBOL_GPL(can_fd_dlc2len);
static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */ static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
9, 9, 9, 9, /* 9 - 12 */ 9, 9, 9, 9, /* 9 - 12 */
@ -49,14 +49,14 @@ static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */ 15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */
/* map the sanitized data length to an appropriate data length code */ /* map the sanitized data length to an appropriate data length code */
u8 can_len2dlc(u8 len) u8 can_fd_len2dlc(u8 len)
{ {
if (unlikely(len > 64)) if (unlikely(len > 64))
return 0xF; return 0xF;
return len2dlc[len]; return len2dlc[len];
} }
EXPORT_SYMBOL_GPL(can_len2dlc); EXPORT_SYMBOL_GPL(can_fd_len2dlc);
#ifdef CONFIG_CAN_CALC_BITTIMING #ifdef CONFIG_CAN_CALC_BITTIMING
#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ #define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
@ -595,7 +595,7 @@ static void can_restart(struct net_device *dev)
netif_rx_ni(skb); netif_rx_ni(skb);
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
restart: restart:
netdev_dbg(dev, "restarted\n"); netdev_dbg(dev, "restarted\n");
@ -737,7 +737,7 @@ struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
return NULL; return NULL;
(*cf)->can_id = CAN_ERR_FLAG; (*cf)->can_id = CAN_ERR_FLAG;
(*cf)->can_dlc = CAN_ERR_DLC; (*cf)->len = CAN_ERR_DLC;
return skb; return skb;
} }

View File

@ -236,8 +236,8 @@
#define FLEXCAN_QUIRK_BROKEN_PERR_STATE BIT(6) #define FLEXCAN_QUIRK_BROKEN_PERR_STATE BIT(6)
/* default to BE register access */ /* default to BE register access */
#define FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN BIT(7) #define FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN BIT(7)
/* Setup stop mode to support wakeup */ /* Setup stop mode with GPR to support wakeup */
#define FLEXCAN_QUIRK_SETUP_STOP_MODE BIT(8) #define FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR BIT(8)
/* Support CAN-FD mode */ /* Support CAN-FD mode */
#define FLEXCAN_QUIRK_SUPPORT_FD BIT(9) #define FLEXCAN_QUIRK_SUPPORT_FD BIT(9)
/* support memory detection and correction */ /* support memory detection and correction */
@ -381,7 +381,7 @@ static const struct flexcan_devtype_data fsl_imx28_devtype_data = {
static const struct flexcan_devtype_data fsl_imx6q_devtype_data = { static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
FLEXCAN_QUIRK_SETUP_STOP_MODE, FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR,
}; };
static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = { static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = {
@ -393,7 +393,7 @@ static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = {
static struct flexcan_devtype_data fsl_imx8mp_devtype_data = { static struct flexcan_devtype_data fsl_imx8mp_devtype_data = {
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP |
FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_SETUP_STOP_MODE | FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR |
FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SUPPORT_ECC, FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SUPPORT_ECC,
}; };
@ -746,7 +746,7 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
u32 can_id; u32 can_id;
u32 data; u32 data;
u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | ((can_len2dlc(cfd->len)) << 16); u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | ((can_fd_len2dlc(cfd->len)) << 16);
int i; int i;
if (can_dropped_invalid_skb(dev, skb)) if (can_dropped_invalid_skb(dev, skb))
@ -1000,12 +1000,12 @@ static struct sk_buff *flexcan_mailbox_read(struct can_rx_offload *offload,
cfd->can_id = (reg_id >> 18) & CAN_SFF_MASK; cfd->can_id = (reg_id >> 18) & CAN_SFF_MASK;
if (reg_ctrl & FLEXCAN_MB_CNT_EDL) { if (reg_ctrl & FLEXCAN_MB_CNT_EDL) {
cfd->len = can_dlc2len(get_canfd_dlc((reg_ctrl >> 16) & 0xf)); cfd->len = can_fd_dlc2len((reg_ctrl >> 16) & 0xf);
if (reg_ctrl & FLEXCAN_MB_CNT_BRS) if (reg_ctrl & FLEXCAN_MB_CNT_BRS)
cfd->flags |= CANFD_BRS; cfd->flags |= CANFD_BRS;
} else { } else {
cfd->len = get_can_dlc((reg_ctrl >> 16) & 0xf); cfd->len = can_cc_dlc2len((reg_ctrl >> 16) & 0xf);
if (reg_ctrl & FLEXCAN_MB_CNT_RTR) if (reg_ctrl & FLEXCAN_MB_CNT_RTR)
cfd->can_id |= CAN_RTR_FLAG; cfd->can_id |= CAN_RTR_FLAG;
@ -1346,6 +1346,72 @@ static void flexcan_ram_init(struct net_device *dev)
priv->write(reg_ctrl2, &regs->ctrl2); priv->write(reg_ctrl2, &regs->ctrl2);
} }
static int flexcan_rx_offload_setup(struct net_device *dev)
{
struct flexcan_priv *priv = netdev_priv(dev);
int err;
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
priv->mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN;
else
priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN;
priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
(sizeof(priv->regs->mb[1]) / priv->mb_size);
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
priv->tx_mb_reserved =
flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP);
else
priv->tx_mb_reserved =
flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_FIFO);
priv->tx_mb_idx = priv->mb_count - 1;
priv->tx_mb = flexcan_get_mb(priv, priv->tx_mb_idx);
priv->tx_mask = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
priv->offload.mailbox_read = flexcan_mailbox_read;
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST;
priv->offload.mb_last = priv->mb_count - 2;
priv->rx_mask = GENMASK_ULL(priv->offload.mb_last,
priv->offload.mb_first);
err = can_rx_offload_add_timestamp(dev, &priv->offload);
} else {
priv->rx_mask = FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
err = can_rx_offload_add_fifo(dev, &priv->offload,
FLEXCAN_NAPI_WEIGHT);
}
return err;
}
static void flexcan_chip_interrupts_enable(const struct net_device *dev)
{
const struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
u64 reg_imask;
disable_irq(dev->irq);
priv->write(priv->reg_ctrl_default, &regs->ctrl);
reg_imask = priv->rx_mask | priv->tx_mask;
priv->write(upper_32_bits(reg_imask), &regs->imask2);
priv->write(lower_32_bits(reg_imask), &regs->imask1);
enable_irq(dev->irq);
}
static void flexcan_chip_interrupts_disable(const struct net_device *dev)
{
const struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
priv->write(0, &regs->imask2);
priv->write(0, &regs->imask1);
priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
&regs->ctrl);
}
/* flexcan_chip_start /* flexcan_chip_start
* *
* this functions is entered with clocks enabled * this functions is entered with clocks enabled
@ -1356,7 +1422,6 @@ static int flexcan_chip_start(struct net_device *dev)
struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs; struct flexcan_regs __iomem *regs = priv->regs;
u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr; u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr;
u64 reg_imask;
int err, i; int err, i;
struct flexcan_mb __iomem *mb; struct flexcan_mb __iomem *mb;
@ -1574,14 +1639,6 @@ static int flexcan_chip_start(struct net_device *dev)
priv->can.state = CAN_STATE_ERROR_ACTIVE; priv->can.state = CAN_STATE_ERROR_ACTIVE;
/* enable interrupts atomically */
disable_irq(dev->irq);
priv->write(priv->reg_ctrl_default, &regs->ctrl);
reg_imask = priv->rx_mask | priv->tx_mask;
priv->write(upper_32_bits(reg_imask), &regs->imask2);
priv->write(lower_32_bits(reg_imask), &regs->imask1);
enable_irq(dev->irq);
/* print chip status */ /* print chip status */
netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__,
priv->read(&regs->mcr), priv->read(&regs->ctrl)); priv->read(&regs->mcr), priv->read(&regs->ctrl));
@ -1600,7 +1657,6 @@ static int flexcan_chip_start(struct net_device *dev)
static int __flexcan_chip_stop(struct net_device *dev, bool disable_on_error) static int __flexcan_chip_stop(struct net_device *dev, bool disable_on_error)
{ {
struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
int err; int err;
/* freeze + disable module */ /* freeze + disable module */
@ -1611,12 +1667,6 @@ static int __flexcan_chip_stop(struct net_device *dev, bool disable_on_error)
if (err && !disable_on_error) if (err && !disable_on_error)
goto out_chip_unfreeze; goto out_chip_unfreeze;
/* Disable all interrupts */
priv->write(0, &regs->imask2);
priv->write(0, &regs->imask1);
priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
&regs->ctrl);
priv->can.state = CAN_STATE_STOPPED; priv->can.state = CAN_STATE_STOPPED;
return 0; return 0;
@ -1662,61 +1712,33 @@ static int flexcan_open(struct net_device *dev)
if (err) if (err)
goto out_close; goto out_close;
err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev); err = flexcan_rx_offload_setup(dev);
if (err) if (err)
goto out_transceiver_disable; goto out_transceiver_disable;
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
priv->mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN;
else
priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN;
priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
(sizeof(priv->regs->mb[1]) / priv->mb_size);
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
priv->tx_mb_reserved =
flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP);
else
priv->tx_mb_reserved =
flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_FIFO);
priv->tx_mb_idx = priv->mb_count - 1;
priv->tx_mb = flexcan_get_mb(priv, priv->tx_mb_idx);
priv->tx_mask = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
priv->offload.mailbox_read = flexcan_mailbox_read;
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST;
priv->offload.mb_last = priv->mb_count - 2;
priv->rx_mask = GENMASK_ULL(priv->offload.mb_last,
priv->offload.mb_first);
err = can_rx_offload_add_timestamp(dev, &priv->offload);
} else {
priv->rx_mask = FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
err = can_rx_offload_add_fifo(dev, &priv->offload,
FLEXCAN_NAPI_WEIGHT);
}
if (err)
goto out_free_irq;
/* start chip and queuing */
err = flexcan_chip_start(dev); err = flexcan_chip_start(dev);
if (err) if (err)
goto out_offload_del; goto out_can_rx_offload_del;
can_rx_offload_enable(&priv->offload);
err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
if (err)
goto out_can_rx_offload_disable;
flexcan_chip_interrupts_enable(dev);
can_led_event(dev, CAN_LED_EVENT_OPEN); can_led_event(dev, CAN_LED_EVENT_OPEN);
can_rx_offload_enable(&priv->offload);
netif_start_queue(dev); netif_start_queue(dev);
return 0; return 0;
out_offload_del: out_can_rx_offload_disable:
can_rx_offload_disable(&priv->offload);
flexcan_chip_stop(dev);
out_can_rx_offload_del:
can_rx_offload_del(&priv->offload); can_rx_offload_del(&priv->offload);
out_free_irq:
free_irq(dev->irq, dev);
out_transceiver_disable: out_transceiver_disable:
flexcan_transceiver_disable(priv); flexcan_transceiver_disable(priv);
out_close: out_close:
@ -1732,14 +1754,15 @@ static int flexcan_close(struct net_device *dev)
struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_priv *priv = netdev_priv(dev);
netif_stop_queue(dev); netif_stop_queue(dev);
flexcan_chip_interrupts_disable(dev);
free_irq(dev->irq, dev);
can_rx_offload_disable(&priv->offload); can_rx_offload_disable(&priv->offload);
flexcan_chip_stop_disable_on_error(dev); flexcan_chip_stop_disable_on_error(dev);
can_rx_offload_del(&priv->offload); can_rx_offload_del(&priv->offload);
free_irq(dev->irq, dev);
flexcan_transceiver_disable(priv); flexcan_transceiver_disable(priv);
close_candev(dev); close_candev(dev);
pm_runtime_put(priv->dev); pm_runtime_put(priv->dev);
can_led_event(dev, CAN_LED_EVENT_STOP); can_led_event(dev, CAN_LED_EVENT_STOP);
@ -1757,6 +1780,8 @@ static int flexcan_set_mode(struct net_device *dev, enum can_mode mode)
if (err) if (err)
return err; return err;
flexcan_chip_interrupts_enable(dev);
netif_wake_queue(dev); netif_wake_queue(dev);
break; break;
@ -1915,15 +1940,8 @@ static const struct of_device_id flexcan_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, flexcan_of_match); MODULE_DEVICE_TABLE(of, flexcan_of_match);
static const struct platform_device_id flexcan_id_table[] = {
{ .name = "flexcan", .driver_data = (kernel_ulong_t)&fsl_p1010_devtype_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, flexcan_id_table);
static int flexcan_probe(struct platform_device *pdev) static int flexcan_probe(struct platform_device *pdev)
{ {
const struct of_device_id *of_id;
const struct flexcan_devtype_data *devtype_data; const struct flexcan_devtype_data *devtype_data;
struct net_device *dev; struct net_device *dev;
struct flexcan_priv *priv; struct flexcan_priv *priv;
@ -1972,15 +1990,7 @@ static int flexcan_probe(struct platform_device *pdev)
if (IS_ERR(regs)) if (IS_ERR(regs))
return PTR_ERR(regs); return PTR_ERR(regs);
of_id = of_match_device(flexcan_of_match, &pdev->dev); devtype_data = of_device_get_match_data(&pdev->dev);
if (of_id) {
devtype_data = of_id->data;
} else if (platform_get_device_id(pdev)->driver_data) {
devtype_data = (struct flexcan_devtype_data *)
platform_get_device_id(pdev)->driver_data;
} else {
return -ENODEV;
}
if ((devtype_data->quirks & FLEXCAN_QUIRK_SUPPORT_FD) && if ((devtype_data->quirks & FLEXCAN_QUIRK_SUPPORT_FD) &&
!(devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)) { !(devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)) {
@ -2047,7 +2057,7 @@ static int flexcan_probe(struct platform_device *pdev)
of_can_transceiver(dev); of_can_transceiver(dev);
devm_can_led_init(dev); devm_can_led_init(dev);
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE) { if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) {
err = flexcan_setup_stop_mode(pdev); err = flexcan_setup_stop_mode(pdev);
if (err) if (err)
dev_dbg(&pdev->dev, "failed to setup stop-mode\n"); dev_dbg(&pdev->dev, "failed to setup stop-mode\n");
@ -2095,6 +2105,8 @@ static int __maybe_unused flexcan_suspend(struct device *device)
if (err) if (err)
return err; return err;
flexcan_chip_interrupts_disable(dev);
err = pinctrl_pm_select_sleep_state(device); err = pinctrl_pm_select_sleep_state(device);
if (err) if (err)
return err; return err;
@ -2130,6 +2142,8 @@ static int __maybe_unused flexcan_resume(struct device *device)
err = flexcan_chip_start(dev); err = flexcan_chip_start(dev);
if (err) if (err)
return err; return err;
flexcan_chip_interrupts_enable(dev);
} }
} }
@ -2206,7 +2220,6 @@ static struct platform_driver flexcan_driver = {
}, },
.probe = flexcan_probe, .probe = flexcan_probe,
.remove = flexcan_remove, .remove = flexcan_remove,
.id_table = flexcan_id_table,
}; };
module_platform_driver(flexcan_driver); module_platform_driver(flexcan_driver);

View File

@ -1201,12 +1201,12 @@ static int grcan_receive(struct net_device *dev, int budget)
cf->can_id = ((slot[0] & GRCAN_MSG_BID) cf->can_id = ((slot[0] & GRCAN_MSG_BID)
>> GRCAN_MSG_BID_BIT); >> GRCAN_MSG_BID_BIT);
} }
cf->can_dlc = get_can_dlc((slot[1] & GRCAN_MSG_DLC) cf->len = can_cc_dlc2len((slot[1] & GRCAN_MSG_DLC)
>> GRCAN_MSG_DLC_BIT); >> GRCAN_MSG_DLC_BIT);
if (rtr) { if (rtr) {
cf->can_id |= CAN_RTR_FLAG; cf->can_id |= CAN_RTR_FLAG;
} else { } else {
for (i = 0; i < cf->can_dlc; i++) { for (i = 0; i < cf->len; i++) {
j = GRCAN_MSG_DATA_SLOT_INDEX(i); j = GRCAN_MSG_DATA_SLOT_INDEX(i);
shift = GRCAN_MSG_DATA_SHIFT(i); shift = GRCAN_MSG_DATA_SHIFT(i);
cf->data[i] = (u8)(slot[j] >> shift); cf->data[i] = (u8)(slot[j] >> shift);
@ -1215,7 +1215,7 @@ static int grcan_receive(struct net_device *dev, int budget)
/* Update statistics and read pointer */ /* Update statistics and read pointer */
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
rd = grcan_ring_add(rd, GRCAN_MSG_SIZE, dma->rx.size); rd = grcan_ring_add(rd, GRCAN_MSG_SIZE, dma->rx.size);
@ -1399,7 +1399,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
eff = cf->can_id & CAN_EFF_FLAG; eff = cf->can_id & CAN_EFF_FLAG;
rtr = cf->can_id & CAN_RTR_FLAG; rtr = cf->can_id & CAN_RTR_FLAG;
id = cf->can_id & (eff ? CAN_EFF_MASK : CAN_SFF_MASK); id = cf->can_id & (eff ? CAN_EFF_MASK : CAN_SFF_MASK);
dlc = cf->can_dlc; dlc = cf->len;
if (eff) if (eff)
tmp = (id << GRCAN_MSG_EID_BIT) & GRCAN_MSG_EID; tmp = (id << GRCAN_MSG_EID_BIT) & GRCAN_MSG_EID;
else else
@ -1447,7 +1447,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
* can_put_echo_skb would be an error unless other measures are * can_put_echo_skb would be an error unless other measures are
* taken. * taken.
*/ */
priv->txdlc[slotindex] = cf->can_dlc; /* Store dlc for statistics */ priv->txdlc[slotindex] = cf->len; /* Store dlc for statistics */
can_put_echo_skb(skb, dev, slotindex); can_put_echo_skb(skb, dev, slotindex);
/* Make sure everything is written before allowing hardware to /* Make sure everything is written before allowing hardware to

View File

@ -271,9 +271,9 @@ static void ifi_canfd_read_fifo(struct net_device *ndev)
dlc = (rxdlc >> IFI_CANFD_RXFIFO_DLC_DLC_OFFSET) & dlc = (rxdlc >> IFI_CANFD_RXFIFO_DLC_DLC_OFFSET) &
IFI_CANFD_RXFIFO_DLC_DLC_MASK; IFI_CANFD_RXFIFO_DLC_DLC_MASK;
if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL) if (rxdlc & IFI_CANFD_RXFIFO_DLC_EDL)
cf->len = can_dlc2len(dlc); cf->len = can_fd_dlc2len(dlc);
else else
cf->len = get_can_dlc(dlc); cf->len = can_cc_dlc2len(dlc);
rxid = readl(priv->base + IFI_CANFD_RXFIFO_ID); rxid = readl(priv->base + IFI_CANFD_RXFIFO_ID);
id = (rxid >> IFI_CANFD_RXFIFO_ID_ID_OFFSET); id = (rxid >> IFI_CANFD_RXFIFO_ID_ID_OFFSET);
@ -431,7 +431,7 @@ static int ifi_canfd_handle_lec_err(struct net_device *ndev)
writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR); writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR);
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 1; return 1;
@ -523,7 +523,7 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev,
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 1; return 1;
@ -900,7 +900,7 @@ static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb,
txid = cf->can_id & CAN_SFF_MASK; txid = cf->can_id & CAN_SFF_MASK;
} }
txdlc = can_len2dlc(cf->len); txdlc = can_fd_len2dlc(cf->len);
if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) { if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) {
txdlc |= IFI_CANFD_TXFIFO_DLC_EDL; txdlc |= IFI_CANFD_TXFIFO_DLC_EDL;
if (cf->flags & CANFD_BRS) if (cf->flags & CANFD_BRS)

View File

@ -916,10 +916,10 @@ static void ican3_to_can_frame(struct ican3_dev *mod,
cf->can_id |= desc->data[0] << 3; cf->can_id |= desc->data[0] << 3;
cf->can_id |= (desc->data[1] & 0xe0) >> 5; cf->can_id |= (desc->data[1] & 0xe0) >> 5;
cf->can_dlc = get_can_dlc(desc->data[1] & ICAN3_CAN_DLC_MASK); cf->len = can_cc_dlc2len(desc->data[1] & ICAN3_CAN_DLC_MASK);
memcpy(cf->data, &desc->data[2], cf->can_dlc); memcpy(cf->data, &desc->data[2], cf->len);
} else { } else {
cf->can_dlc = get_can_dlc(desc->data[0] & ICAN3_CAN_DLC_MASK); cf->len = can_cc_dlc2len(desc->data[0] & ICAN3_CAN_DLC_MASK);
if (desc->data[0] & ICAN3_EFF_RTR) if (desc->data[0] & ICAN3_EFF_RTR)
cf->can_id |= CAN_RTR_FLAG; cf->can_id |= CAN_RTR_FLAG;
@ -934,7 +934,7 @@ static void ican3_to_can_frame(struct ican3_dev *mod,
cf->can_id |= desc->data[3] >> 5; /* 2-0 */ cf->can_id |= desc->data[3] >> 5; /* 2-0 */
} }
memcpy(cf->data, &desc->data[6], cf->can_dlc); memcpy(cf->data, &desc->data[6], cf->len);
} }
} }
@ -947,7 +947,7 @@ static void can_frame_to_ican3(struct ican3_dev *mod,
/* we always use the extended format, with the ECHO flag set */ /* we always use the extended format, with the ECHO flag set */
desc->command = ICAN3_CAN_TYPE_EFF; desc->command = ICAN3_CAN_TYPE_EFF;
desc->data[0] |= cf->can_dlc; desc->data[0] |= cf->len;
desc->data[1] |= ICAN3_ECHO; desc->data[1] |= ICAN3_ECHO;
/* support single transmission (no retries) mode */ /* support single transmission (no retries) mode */
@ -970,7 +970,7 @@ static void can_frame_to_ican3(struct ican3_dev *mod,
} }
/* copy the data bits into the descriptor */ /* copy the data bits into the descriptor */
memcpy(&desc->data[6], cf->data, cf->can_dlc); memcpy(&desc->data[6], cf->data, cf->len);
} }
/* /*
@ -1294,7 +1294,7 @@ static unsigned int ican3_get_echo_skb(struct ican3_dev *mod)
} }
cf = (struct can_frame *)skb->data; cf = (struct can_frame *)skb->data;
dlc = cf->can_dlc; dlc = cf->len;
/* check flag whether this packet has to be looped back */ /* check flag whether this packet has to be looped back */
if (skb->pkt_type != PACKET_LOOPBACK) { if (skb->pkt_type != PACKET_LOOPBACK) {
@ -1332,10 +1332,10 @@ static bool ican3_echo_skb_matches(struct ican3_dev *mod, struct sk_buff *skb)
if (cf->can_id != echo_cf->can_id) if (cf->can_id != echo_cf->can_id)
return false; return false;
if (cf->can_dlc != echo_cf->can_dlc) if (cf->len != echo_cf->len)
return false; return false;
return memcmp(cf->data, echo_cf->data, cf->can_dlc) == 0; return memcmp(cf->data, echo_cf->data, cf->len) == 0;
} }
/* /*
@ -1421,7 +1421,7 @@ static int ican3_recv_skb(struct ican3_dev *mod)
/* update statistics, receive the skb */ /* update statistics, receive the skb */
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
err_noalloc: err_noalloc:

View File

@ -742,7 +742,7 @@ static int kvaser_pciefd_prepare_tx_packet(struct kvaser_pciefd_tx_packet *p,
p->header[0] |= KVASER_PCIEFD_RPACKET_IDE; p->header[0] |= KVASER_PCIEFD_RPACKET_IDE;
p->header[0] |= cf->can_id & CAN_EFF_MASK; p->header[0] |= cf->can_id & CAN_EFF_MASK;
p->header[1] |= can_len2dlc(cf->len) << KVASER_PCIEFD_RPACKET_DLC_SHIFT; p->header[1] |= can_fd_len2dlc(cf->len) << KVASER_PCIEFD_RPACKET_DLC_SHIFT;
p->header[1] |= KVASER_PCIEFD_TPACKET_AREQ; p->header[1] |= KVASER_PCIEFD_TPACKET_AREQ;
if (can_is_canfd_skb(skb)) { if (can_is_canfd_skb(skb)) {
@ -1176,7 +1176,7 @@ static int kvaser_pciefd_handle_data_packet(struct kvaser_pciefd *pcie,
if (p->header[0] & KVASER_PCIEFD_RPACKET_IDE) if (p->header[0] & KVASER_PCIEFD_RPACKET_IDE)
cf->can_id |= CAN_EFF_FLAG; cf->can_id |= CAN_EFF_FLAG;
cf->len = can_dlc2len(p->header[1] >> KVASER_PCIEFD_RPACKET_DLC_SHIFT); cf->len = can_fd_dlc2len(p->header[1] >> KVASER_PCIEFD_RPACKET_DLC_SHIFT);
if (p->header[0] & KVASER_PCIEFD_RPACKET_RTR) if (p->header[0] & KVASER_PCIEFD_RPACKET_RTR)
cf->can_id |= CAN_RTR_FLAG; cf->can_id |= CAN_RTR_FLAG;
@ -1301,7 +1301,7 @@ static int kvaser_pciefd_rx_error_frame(struct kvaser_pciefd_can *can,
cf->data[7] = bec.rxerr; cf->data[7] = bec.rxerr;
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_rx(skb); netif_rx(skb);
return 0; return 0;
@ -1500,7 +1500,7 @@ static void kvaser_pciefd_handle_nack_packet(struct kvaser_pciefd_can *can,
if (skb) { if (skb) {
cf->can_id |= CAN_ERR_BUSERROR; cf->can_id |= CAN_ERR_BUSERROR;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
stats->rx_packets++; stats->rx_packets++;
netif_rx(skb); netif_rx(skb);
} else { } else {
@ -1602,7 +1602,7 @@ static int kvaser_pciefd_read_packet(struct kvaser_pciefd *pcie, int *start_pos,
if (!(p->header[0] & KVASER_PCIEFD_RPACKET_RTR)) { if (!(p->header[0] & KVASER_PCIEFD_RPACKET_RTR)) {
u8 data_len; u8 data_len;
data_len = can_dlc2len(p->header[1] >> data_len = can_fd_dlc2len(p->header[1] >>
KVASER_PCIEFD_RPACKET_DLC_SHIFT); KVASER_PCIEFD_RPACKET_DLC_SHIFT);
pos += DIV_ROUND_UP(data_len, 4); pos += DIV_ROUND_UP(data_len, 4);
} }

View File

@ -1,21 +1,28 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config CAN_M_CAN menuconfig CAN_M_CAN
tristate "Bosch M_CAN support" tristate "Bosch M_CAN support"
help help
Say Y here if you want support for Bosch M_CAN controller framework. Say Y here if you want support for Bosch M_CAN controller framework.
This is common support for devices that embed the Bosch M_CAN IP. This is common support for devices that embed the Bosch M_CAN IP.
if CAN_M_CAN
config CAN_M_CAN_PCI
tristate "Generic PCI Bus based M_CAN driver"
depends on PCI
help
Say Y here if you want to support Bosch M_CAN controller connected
to the pci bus.
config CAN_M_CAN_PLATFORM config CAN_M_CAN_PLATFORM
tristate "Bosch M_CAN support for io-mapped devices" tristate "Bosch M_CAN support for io-mapped devices"
depends on HAS_IOMEM depends on HAS_IOMEM
depends on CAN_M_CAN
help help
Say Y here if you want support for IO Mapped Bosch M_CAN controller. Say Y here if you want support for IO Mapped Bosch M_CAN controller.
This support is for devices that have the Bosch M_CAN controller This support is for devices that have the Bosch M_CAN controller
IP embedded into the device and the IP is IO Mapped to the processor. IP embedded into the device and the IP is IO Mapped to the processor.
config CAN_M_CAN_TCAN4X5X config CAN_M_CAN_TCAN4X5X
depends on CAN_M_CAN
depends on SPI depends on SPI
select REGMAP_SPI select REGMAP_SPI
tristate "TCAN4X5X M_CAN device" tristate "TCAN4X5X M_CAN device"
@ -23,3 +30,5 @@ config CAN_M_CAN_TCAN4X5X
Say Y here if you want support for Texas Instruments TCAN4x5x Say Y here if you want support for Texas Instruments TCAN4x5x
M_CAN controller. This device is a peripheral device that uses the M_CAN controller. This device is a peripheral device that uses the
SPI bus for communication. SPI bus for communication.
endif

View File

@ -4,5 +4,6 @@
# #
obj-$(CONFIG_CAN_M_CAN) += m_can.o obj-$(CONFIG_CAN_M_CAN) += m_can.o
obj-$(CONFIG_CAN_M_CAN_PCI) += m_can_pci.o
obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o

View File

@ -5,8 +5,7 @@
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
/* Bosch M_CAN user manual can be obtained from: /* Bosch M_CAN user manual can be obtained from:
* http://www.bosch-semiconductors.de/media/pdf_1/ipmodules_1/m_can/ * https://github.com/linux-can/can-doc/tree/master/m_can
* mcan_users_manual_v302.pdf
*/ */
#include <linux/interrupt.h> #include <linux/interrupt.h>
@ -40,7 +39,7 @@ enum m_can_reg {
M_CAN_TOCV = 0x2c, M_CAN_TOCV = 0x2c,
M_CAN_ECR = 0x40, M_CAN_ECR = 0x40,
M_CAN_PSR = 0x44, M_CAN_PSR = 0x44,
/* TDCR Register only available for version >=3.1.x */ /* TDCR Register only available for version >=3.1.x */
M_CAN_TDCR = 0x48, M_CAN_TDCR = 0x48,
M_CAN_IR = 0x50, M_CAN_IR = 0x50,
M_CAN_IE = 0x54, M_CAN_IE = 0x54,
@ -336,7 +335,7 @@ static u32 m_can_fifo_read(struct m_can_classdev *cdev,
u32 fgi, unsigned int offset) u32 fgi, unsigned int offset)
{ {
u32 addr_offset = cdev->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE + u32 addr_offset = cdev->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE +
offset; offset;
return cdev->ops->read_fifo(cdev, addr_offset); return cdev->ops->read_fifo(cdev, addr_offset);
} }
@ -345,7 +344,7 @@ static void m_can_fifo_write(struct m_can_classdev *cdev,
u32 fpi, unsigned int offset, u32 val) u32 fpi, unsigned int offset, u32 val)
{ {
u32 addr_offset = cdev->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE + u32 addr_offset = cdev->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE +
offset; offset;
cdev->ops->write_fifo(cdev, addr_offset, val); cdev->ops->write_fifo(cdev, addr_offset, val);
} }
@ -359,17 +358,17 @@ static inline void m_can_fifo_write_no_off(struct m_can_classdev *cdev,
static u32 m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset) static u32 m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset)
{ {
u32 addr_offset = cdev->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE + u32 addr_offset = cdev->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE +
offset; offset;
return cdev->ops->read_fifo(cdev, addr_offset); return cdev->ops->read_fifo(cdev, addr_offset);
} }
static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev) static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev)
{ {
return !!(m_can_read(cdev, M_CAN_TXFQS) & TXFQS_TFQF); return !!(m_can_read(cdev, M_CAN_TXFQS) & TXFQS_TFQF);
} }
void m_can_config_endisable(struct m_can_classdev *cdev, bool enable) static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable)
{ {
u32 cccr = m_can_read(cdev, M_CAN_CCCR); u32 cccr = m_can_read(cdev, M_CAN_CCCR);
u32 timeout = 10; u32 timeout = 10;
@ -380,10 +379,6 @@ void m_can_config_endisable(struct m_can_classdev *cdev, bool enable)
cccr &= ~CCCR_CSR; cccr &= ~CCCR_CSR;
if (enable) { if (enable) {
/* Clear the Clock stop request if it was set */
if (cccr & CCCR_CSR)
cccr &= ~CCCR_CSR;
/* enable m_can configuration */ /* enable m_can configuration */
m_can_write(cdev, M_CAN_CCCR, cccr | CCCR_INIT); m_can_write(cdev, M_CAN_CCCR, cccr | CCCR_INIT);
udelay(5); udelay(5);
@ -457,9 +452,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
} }
if (dlc & RX_BUF_FDF) if (dlc & RX_BUF_FDF)
cf->len = can_dlc2len((dlc >> 16) & 0x0F); cf->len = can_fd_dlc2len((dlc >> 16) & 0x0F);
else else
cf->len = get_can_dlc((dlc >> 16) & 0x0F); cf->len = can_cc_dlc2len((dlc >> 16) & 0x0F);
id = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID); id = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID);
if (id & RX_BUF_XTD) if (id & RX_BUF_XTD)
@ -596,7 +591,7 @@ static int m_can_handle_lec_err(struct net_device *dev,
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 1; return 1;
@ -617,18 +612,10 @@ static int __m_can_get_berr_counter(const struct net_device *dev,
static int m_can_clk_start(struct m_can_classdev *cdev) static int m_can_clk_start(struct m_can_classdev *cdev)
{ {
int err;
if (cdev->pm_clock_support == 0) if (cdev->pm_clock_support == 0)
return 0; return 0;
err = pm_runtime_get_sync(cdev->dev); return pm_runtime_resume_and_get(cdev->dev);
if (err < 0) {
pm_runtime_put_noidle(cdev->dev);
return err;
}
return 0;
} }
static void m_can_clk_stop(struct m_can_classdev *cdev) static void m_can_clk_stop(struct m_can_classdev *cdev)
@ -723,7 +710,7 @@ static int m_can_handle_state_change(struct net_device *dev,
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_receive_skb(skb); netif_receive_skb(skb);
return 1; return 1;
@ -926,14 +913,13 @@ static void m_can_echo_tx_event(struct net_device *dev)
m_can_txefs = m_can_read(cdev, M_CAN_TXEFS); m_can_txefs = m_can_read(cdev, M_CAN_TXEFS);
/* Get Tx Event fifo element count */ /* Get Tx Event fifo element count */
txe_count = (m_can_txefs & TXEFS_EFFL_MASK) txe_count = (m_can_txefs & TXEFS_EFFL_MASK) >> TXEFS_EFFL_SHIFT;
>> TXEFS_EFFL_SHIFT;
/* Get and process all sent elements */ /* Get and process all sent elements */
for (i = 0; i < txe_count; i++) { for (i = 0; i < txe_count; i++) {
/* retrieve get index */ /* retrieve get index */
fgi = (m_can_read(cdev, M_CAN_TXEFS) & TXEFS_EFGI_MASK) fgi = (m_can_read(cdev, M_CAN_TXEFS) & TXEFS_EFGI_MASK) >>
>> TXEFS_EFGI_SHIFT; TXEFS_EFGI_SHIFT;
/* get message marker */ /* get message marker */
msg_mark = (m_can_txe_fifo_read(cdev, fgi, 4) & msg_mark = (m_can_txe_fifo_read(cdev, fgi, 4) &
@ -1092,7 +1078,7 @@ static int m_can_set_bittiming(struct net_device *dev)
* Transmitter Delay Compensation Section * Transmitter Delay Compensation Section
*/ */
tdco = (cdev->can.clock.freq / 1000) * tdco = (cdev->can.clock.freq / 1000) *
ssp / dbt->bitrate; ssp / dbt->bitrate;
/* Max valid TDCO value is 127 */ /* Max valid TDCO value is 127 */
if (tdco > 127) { if (tdco > 127) {
@ -1107,9 +1093,9 @@ static int m_can_set_bittiming(struct net_device *dev)
} }
reg_btp |= (brp << DBTP_DBRP_SHIFT) | reg_btp |= (brp << DBTP_DBRP_SHIFT) |
(sjw << DBTP_DSJW_SHIFT) | (sjw << DBTP_DSJW_SHIFT) |
(tseg1 << DBTP_DTSEG1_SHIFT) | (tseg1 << DBTP_DTSEG1_SHIFT) |
(tseg2 << DBTP_DTSEG2_SHIFT); (tseg2 << DBTP_DTSEG2_SHIFT);
m_can_write(cdev, M_CAN_DBTP, reg_btp); m_can_write(cdev, M_CAN_DBTP, reg_btp);
} }
@ -1142,7 +1128,7 @@ static void m_can_chip_config(struct net_device *dev)
if (cdev->version == 30) { if (cdev->version == 30) {
/* only support one Tx Buffer currently */ /* only support one Tx Buffer currently */
m_can_write(cdev, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) | m_can_write(cdev, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) |
cdev->mcfg[MRAM_TXB].off); cdev->mcfg[MRAM_TXB].off);
} else { } else {
/* TX FIFO is used for newer IP Core versions */ /* TX FIFO is used for newer IP Core versions */
m_can_write(cdev, M_CAN_TXBC, m_can_write(cdev, M_CAN_TXBC,
@ -1156,7 +1142,7 @@ static void m_can_chip_config(struct net_device *dev)
/* TX Event FIFO */ /* TX Event FIFO */
if (cdev->version == 30) { if (cdev->version == 30) {
m_can_write(cdev, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) | m_can_write(cdev, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) |
cdev->mcfg[MRAM_TXE].off); cdev->mcfg[MRAM_TXE].off);
} else { } else {
/* Full TX Event FIFO is used */ /* Full TX Event FIFO is used */
m_can_write(cdev, M_CAN_TXEFC, m_can_write(cdev, M_CAN_TXEFC,
@ -1168,27 +1154,27 @@ static void m_can_chip_config(struct net_device *dev)
/* rx fifo configuration, blocking mode, fifo size 1 */ /* rx fifo configuration, blocking mode, fifo size 1 */
m_can_write(cdev, M_CAN_RXF0C, m_can_write(cdev, M_CAN_RXF0C,
(cdev->mcfg[MRAM_RXF0].num << RXFC_FS_SHIFT) | (cdev->mcfg[MRAM_RXF0].num << RXFC_FS_SHIFT) |
cdev->mcfg[MRAM_RXF0].off); cdev->mcfg[MRAM_RXF0].off);
m_can_write(cdev, M_CAN_RXF1C, m_can_write(cdev, M_CAN_RXF1C,
(cdev->mcfg[MRAM_RXF1].num << RXFC_FS_SHIFT) | (cdev->mcfg[MRAM_RXF1].num << RXFC_FS_SHIFT) |
cdev->mcfg[MRAM_RXF1].off); cdev->mcfg[MRAM_RXF1].off);
cccr = m_can_read(cdev, M_CAN_CCCR); cccr = m_can_read(cdev, M_CAN_CCCR);
test = m_can_read(cdev, M_CAN_TEST); test = m_can_read(cdev, M_CAN_TEST);
test &= ~TEST_LBCK; test &= ~TEST_LBCK;
if (cdev->version == 30) { if (cdev->version == 30) {
/* Version 3.0.x */ /* Version 3.0.x */
cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_DAR | cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_DAR |
(CCCR_CMR_MASK << CCCR_CMR_SHIFT) | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
(CCCR_CME_MASK << CCCR_CME_SHIFT)); (CCCR_CME_MASK << CCCR_CME_SHIFT));
if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) if (cdev->can.ctrlmode & CAN_CTRLMODE_FD)
cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;
} else { } else {
/* Version 3.1.x or 3.2.x */ /* Version 3.1.x or 3.2.x */
cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE | cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE |
CCCR_NISO | CCCR_DAR); CCCR_NISO | CCCR_DAR);
@ -1333,80 +1319,79 @@ static bool m_can_niso_supported(struct m_can_classdev *cdev)
return !niso_timeout; return !niso_timeout;
} }
static int m_can_dev_setup(struct m_can_classdev *m_can_dev) static int m_can_dev_setup(struct m_can_classdev *cdev)
{ {
struct net_device *dev = m_can_dev->net; struct net_device *dev = cdev->net;
int m_can_version; int m_can_version;
m_can_version = m_can_check_core_release(m_can_dev); m_can_version = m_can_check_core_release(cdev);
/* return if unsupported version */ /* return if unsupported version */
if (!m_can_version) { if (!m_can_version) {
dev_err(m_can_dev->dev, "Unsupported version number: %2d", dev_err(cdev->dev, "Unsupported version number: %2d",
m_can_version); m_can_version);
return -EINVAL; return -EINVAL;
} }
if (!m_can_dev->is_peripheral) if (!cdev->is_peripheral)
netif_napi_add(dev, &m_can_dev->napi, netif_napi_add(dev, &cdev->napi,
m_can_poll, M_CAN_NAPI_WEIGHT); m_can_poll, M_CAN_NAPI_WEIGHT);
/* Shared properties of all M_CAN versions */ /* Shared properties of all M_CAN versions */
m_can_dev->version = m_can_version; cdev->version = m_can_version;
m_can_dev->can.do_set_mode = m_can_set_mode; cdev->can.do_set_mode = m_can_set_mode;
m_can_dev->can.do_get_berr_counter = m_can_get_berr_counter; cdev->can.do_get_berr_counter = m_can_get_berr_counter;
/* Set M_CAN supported operations */ /* Set M_CAN supported operations */
m_can_dev->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | cdev->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_BERR_REPORTING |
CAN_CTRLMODE_FD | CAN_CTRLMODE_FD |
CAN_CTRLMODE_ONE_SHOT; CAN_CTRLMODE_ONE_SHOT;
/* Set properties depending on M_CAN version */ /* Set properties depending on M_CAN version */
switch (m_can_dev->version) { switch (cdev->version) {
case 30: case 30:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */ /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
m_can_dev->can.bittiming_const = m_can_dev->bit_timing ? cdev->can.bittiming_const = cdev->bit_timing ?
m_can_dev->bit_timing : &m_can_bittiming_const_30X; cdev->bit_timing : &m_can_bittiming_const_30X;
m_can_dev->can.data_bittiming_const = m_can_dev->data_timing ? cdev->can.data_bittiming_const = cdev->data_timing ?
m_can_dev->data_timing : cdev->data_timing :
&m_can_data_bittiming_const_30X; &m_can_data_bittiming_const_30X;
break; break;
case 31: case 31:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */ /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
m_can_dev->can.bittiming_const = m_can_dev->bit_timing ? cdev->can.bittiming_const = cdev->bit_timing ?
m_can_dev->bit_timing : &m_can_bittiming_const_31X; cdev->bit_timing : &m_can_bittiming_const_31X;
m_can_dev->can.data_bittiming_const = m_can_dev->data_timing ? cdev->can.data_bittiming_const = cdev->data_timing ?
m_can_dev->data_timing : cdev->data_timing :
&m_can_data_bittiming_const_31X; &m_can_data_bittiming_const_31X;
break; break;
case 32: case 32:
case 33: case 33:
/* Support both MCAN version v3.2.x and v3.3.0 */ /* Support both MCAN version v3.2.x and v3.3.0 */
m_can_dev->can.bittiming_const = m_can_dev->bit_timing ? cdev->can.bittiming_const = cdev->bit_timing ?
m_can_dev->bit_timing : &m_can_bittiming_const_31X; cdev->bit_timing : &m_can_bittiming_const_31X;
m_can_dev->can.data_bittiming_const = m_can_dev->data_timing ? cdev->can.data_bittiming_const = cdev->data_timing ?
m_can_dev->data_timing : cdev->data_timing :
&m_can_data_bittiming_const_31X; &m_can_data_bittiming_const_31X;
m_can_dev->can.ctrlmode_supported |= cdev->can.ctrlmode_supported |=
(m_can_niso_supported(m_can_dev) (m_can_niso_supported(cdev) ?
? CAN_CTRLMODE_FD_NON_ISO CAN_CTRLMODE_FD_NON_ISO : 0);
: 0);
break; break;
default: default:
dev_err(m_can_dev->dev, "Unsupported version number: %2d", dev_err(cdev->dev, "Unsupported version number: %2d",
m_can_dev->version); cdev->version);
return -EINVAL; return -EINVAL;
} }
if (m_can_dev->ops->init) if (cdev->ops->init)
m_can_dev->ops->init(m_can_dev); cdev->ops->init(cdev);
return 0; return 0;
} }
@ -1491,7 +1476,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
/* message ram configuration */ /* message ram configuration */
m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, id); m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, id);
m_can_fifo_write(cdev, 0, M_CAN_FIFO_DLC, m_can_fifo_write(cdev, 0, M_CAN_FIFO_DLC,
can_len2dlc(cf->len) << 16); can_fd_len2dlc(cf->len) << 16);
for (i = 0; i < cf->len; i += 4) for (i = 0; i < cf->len; i += 4)
m_can_fifo_write(cdev, 0, m_can_fifo_write(cdev, 0,
@ -1539,7 +1524,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
/* get put index for frame */ /* get put index for frame */
putidx = ((m_can_read(cdev, M_CAN_TXFQS) & TXFQS_TFQPI_MASK) putidx = ((m_can_read(cdev, M_CAN_TXFQS) & TXFQS_TFQPI_MASK)
>> TXFQS_TFQPI_SHIFT); >> TXFQS_TFQPI_SHIFT);
/* Write ID Field to FIFO Element */ /* Write ID Field to FIFO Element */
m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, id); m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, id);
@ -1559,7 +1544,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DLC, m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DLC,
((putidx << TX_BUF_MM_SHIFT) & ((putidx << TX_BUF_MM_SHIFT) &
TX_BUF_MM_MASK) | TX_BUF_MM_MASK) |
(can_len2dlc(cf->len) << 16) | (can_fd_len2dlc(cf->len) << 16) |
fdflags | TX_BUF_EFC); fdflags | TX_BUF_EFC);
for (i = 0; i < cf->len; i += 4) for (i = 0; i < cf->len; i += 4)
@ -1586,7 +1571,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
static void m_can_tx_work_queue(struct work_struct *ws) static void m_can_tx_work_queue(struct work_struct *ws)
{ {
struct m_can_classdev *cdev = container_of(ws, struct m_can_classdev, struct m_can_classdev *cdev = container_of(ws, struct m_can_classdev,
tx_work); tx_work);
m_can_tx_handler(cdev); m_can_tx_handler(cdev);
cdev->tx_skb = NULL; cdev->tx_skb = NULL;
@ -1710,26 +1695,26 @@ static void m_can_of_parse_mram(struct m_can_classdev *cdev,
cdev->mcfg[MRAM_SIDF].off = mram_config_vals[0]; cdev->mcfg[MRAM_SIDF].off = mram_config_vals[0];
cdev->mcfg[MRAM_SIDF].num = mram_config_vals[1]; cdev->mcfg[MRAM_SIDF].num = mram_config_vals[1];
cdev->mcfg[MRAM_XIDF].off = cdev->mcfg[MRAM_SIDF].off + cdev->mcfg[MRAM_XIDF].off = cdev->mcfg[MRAM_SIDF].off +
cdev->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE; cdev->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE;
cdev->mcfg[MRAM_XIDF].num = mram_config_vals[2]; cdev->mcfg[MRAM_XIDF].num = mram_config_vals[2];
cdev->mcfg[MRAM_RXF0].off = cdev->mcfg[MRAM_XIDF].off + cdev->mcfg[MRAM_RXF0].off = cdev->mcfg[MRAM_XIDF].off +
cdev->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE; cdev->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE;
cdev->mcfg[MRAM_RXF0].num = mram_config_vals[3] & cdev->mcfg[MRAM_RXF0].num = mram_config_vals[3] &
(RXFC_FS_MASK >> RXFC_FS_SHIFT); (RXFC_FS_MASK >> RXFC_FS_SHIFT);
cdev->mcfg[MRAM_RXF1].off = cdev->mcfg[MRAM_RXF0].off + cdev->mcfg[MRAM_RXF1].off = cdev->mcfg[MRAM_RXF0].off +
cdev->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE; cdev->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE;
cdev->mcfg[MRAM_RXF1].num = mram_config_vals[4] & cdev->mcfg[MRAM_RXF1].num = mram_config_vals[4] &
(RXFC_FS_MASK >> RXFC_FS_SHIFT); (RXFC_FS_MASK >> RXFC_FS_SHIFT);
cdev->mcfg[MRAM_RXB].off = cdev->mcfg[MRAM_RXF1].off + cdev->mcfg[MRAM_RXB].off = cdev->mcfg[MRAM_RXF1].off +
cdev->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE; cdev->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE;
cdev->mcfg[MRAM_RXB].num = mram_config_vals[5]; cdev->mcfg[MRAM_RXB].num = mram_config_vals[5];
cdev->mcfg[MRAM_TXE].off = cdev->mcfg[MRAM_RXB].off + cdev->mcfg[MRAM_TXE].off = cdev->mcfg[MRAM_RXB].off +
cdev->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE; cdev->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE;
cdev->mcfg[MRAM_TXE].num = mram_config_vals[6]; cdev->mcfg[MRAM_TXE].num = mram_config_vals[6];
cdev->mcfg[MRAM_TXB].off = cdev->mcfg[MRAM_TXE].off + cdev->mcfg[MRAM_TXB].off = cdev->mcfg[MRAM_TXE].off +
cdev->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE; cdev->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE;
cdev->mcfg[MRAM_TXB].num = mram_config_vals[7] & cdev->mcfg[MRAM_TXB].num = mram_config_vals[7] &
(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT); (TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);
dev_dbg(cdev->dev, dev_dbg(cdev->dev,
"sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", "sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
@ -1758,15 +1743,15 @@ void m_can_init_ram(struct m_can_classdev *cdev)
} }
EXPORT_SYMBOL_GPL(m_can_init_ram); EXPORT_SYMBOL_GPL(m_can_init_ram);
int m_can_class_get_clocks(struct m_can_classdev *m_can_dev) int m_can_class_get_clocks(struct m_can_classdev *cdev)
{ {
int ret = 0; int ret = 0;
m_can_dev->hclk = devm_clk_get(m_can_dev->dev, "hclk"); cdev->hclk = devm_clk_get(cdev->dev, "hclk");
m_can_dev->cclk = devm_clk_get(m_can_dev->dev, "cclk"); cdev->cclk = devm_clk_get(cdev->dev, "cclk");
if (IS_ERR(m_can_dev->cclk)) { if (IS_ERR(cdev->cclk)) {
dev_err(m_can_dev->dev, "no clock found\n"); dev_err(cdev->dev, "no clock found\n");
ret = -ENODEV; ret = -ENODEV;
} }
@ -1774,7 +1759,8 @@ int m_can_class_get_clocks(struct m_can_classdev *m_can_dev)
} }
EXPORT_SYMBOL_GPL(m_can_class_get_clocks); EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
struct m_can_classdev *m_can_class_allocate_dev(struct device *dev) struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,
int sizeof_priv)
{ {
struct m_can_classdev *class_dev = NULL; struct m_can_classdev *class_dev = NULL;
u32 mram_config_vals[MRAM_CFG_LEN]; u32 mram_config_vals[MRAM_CFG_LEN];
@ -1797,7 +1783,7 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev)
tx_fifo_size = mram_config_vals[7]; tx_fifo_size = mram_config_vals[7];
/* allocate the m_can device */ /* allocate the m_can device */
net_dev = alloc_candev(sizeof(*class_dev), tx_fifo_size); net_dev = alloc_candev(sizeof_priv, tx_fifo_size);
if (!net_dev) { if (!net_dev) {
dev_err(dev, "Failed to allocate CAN device"); dev_err(dev, "Failed to allocate CAN device");
goto out; goto out;
@ -1825,54 +1811,56 @@ void m_can_class_free_dev(struct net_device *net)
} }
EXPORT_SYMBOL_GPL(m_can_class_free_dev); EXPORT_SYMBOL_GPL(m_can_class_free_dev);
int m_can_class_register(struct m_can_classdev *m_can_dev) int m_can_class_register(struct m_can_classdev *cdev)
{ {
int ret; int ret;
if (m_can_dev->pm_clock_support) { if (cdev->pm_clock_support) {
pm_runtime_enable(m_can_dev->dev); ret = m_can_clk_start(cdev);
ret = m_can_clk_start(m_can_dev);
if (ret) if (ret)
goto pm_runtime_fail; return ret;
} }
ret = m_can_dev_setup(m_can_dev); ret = m_can_dev_setup(cdev);
if (ret) if (ret)
goto clk_disable; goto clk_disable;
ret = register_m_can_dev(m_can_dev->net); ret = register_m_can_dev(cdev->net);
if (ret) { if (ret) {
dev_err(m_can_dev->dev, "registering %s failed (err=%d)\n", dev_err(cdev->dev, "registering %s failed (err=%d)\n",
m_can_dev->net->name, ret); cdev->net->name, ret);
goto clk_disable; goto clk_disable;
} }
devm_can_led_init(m_can_dev->net); devm_can_led_init(cdev->net);
of_can_transceiver(m_can_dev->net); of_can_transceiver(cdev->net);
dev_info(m_can_dev->dev, "%s device registered (irq=%d, version=%d)\n", dev_info(cdev->dev, "%s device registered (irq=%d, version=%d)\n",
KBUILD_MODNAME, m_can_dev->net->irq, m_can_dev->version); KBUILD_MODNAME, cdev->net->irq, cdev->version);
/* Probe finished /* Probe finished
* Stop clocks. They will be reactivated once the M_CAN device is opened * Stop clocks. They will be reactivated once the M_CAN device is opened
*/ */
clk_disable: clk_disable:
m_can_clk_stop(m_can_dev); m_can_clk_stop(cdev);
pm_runtime_fail:
if (ret) {
if (m_can_dev->pm_clock_support)
pm_runtime_disable(m_can_dev->dev);
}
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(m_can_class_register); EXPORT_SYMBOL_GPL(m_can_class_register);
void m_can_class_unregister(struct m_can_classdev *cdev)
{
unregister_candev(cdev->net);
m_can_clk_stop(cdev);
}
EXPORT_SYMBOL_GPL(m_can_class_unregister);
int m_can_class_suspend(struct device *dev) int m_can_class_suspend(struct device *dev)
{ {
struct net_device *ndev = dev_get_drvdata(dev); struct m_can_classdev *cdev = dev_get_drvdata(dev);
struct m_can_classdev *cdev = netdev_priv(ndev); struct net_device *ndev = cdev->net;
if (netif_running(ndev)) { if (netif_running(ndev)) {
netif_stop_queue(ndev); netif_stop_queue(ndev);
@ -1891,8 +1879,8 @@ EXPORT_SYMBOL_GPL(m_can_class_suspend);
int m_can_class_resume(struct device *dev) int m_can_class_resume(struct device *dev)
{ {
struct net_device *ndev = dev_get_drvdata(dev); struct m_can_classdev *cdev = dev_get_drvdata(dev);
struct m_can_classdev *cdev = netdev_priv(ndev); struct net_device *ndev = cdev->net;
pinctrl_pm_select_default_state(dev); pinctrl_pm_select_default_state(dev);
@ -1915,14 +1903,6 @@ int m_can_class_resume(struct device *dev)
} }
EXPORT_SYMBOL_GPL(m_can_class_resume); EXPORT_SYMBOL_GPL(m_can_class_resume);
void m_can_class_unregister(struct m_can_classdev *m_can_dev)
{
unregister_candev(m_can_dev->net);
m_can_clk_stop(m_can_dev);
}
EXPORT_SYMBOL_GPL(m_can_class_unregister);
MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>"); MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -86,10 +86,7 @@ struct m_can_classdev {
struct m_can_ops *ops; struct m_can_ops *ops;
void *device_data;
int version; int version;
int freq;
u32 irqstatus; u32 irqstatus;
int pm_clock_support; int pm_clock_support;
@ -98,13 +95,12 @@ struct m_can_classdev {
struct mram_cfg mcfg[MRAM_CFG_NUM]; struct mram_cfg mcfg[MRAM_CFG_NUM];
}; };
struct m_can_classdev *m_can_class_allocate_dev(struct device *dev); struct m_can_classdev *m_can_class_allocate_dev(struct device *dev, int sizeof_priv);
void m_can_class_free_dev(struct net_device *net); void m_can_class_free_dev(struct net_device *net);
int m_can_class_register(struct m_can_classdev *cdev); int m_can_class_register(struct m_can_classdev *cdev);
void m_can_class_unregister(struct m_can_classdev *cdev); void m_can_class_unregister(struct m_can_classdev *cdev);
int m_can_class_get_clocks(struct m_can_classdev *cdev); int m_can_class_get_clocks(struct m_can_classdev *cdev);
void m_can_init_ram(struct m_can_classdev *priv); void m_can_init_ram(struct m_can_classdev *priv);
void m_can_config_endisable(struct m_can_classdev *priv, bool enable);
int m_can_class_suspend(struct device *dev); int m_can_class_suspend(struct device *dev);
int m_can_class_resume(struct device *dev); int m_can_class_resume(struct device *dev);

View File

@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCI Specific M_CAN Glue
*
* Copyright (C) 2018-2020 Intel Corporation
* Author: Felipe Balbi (Intel)
* Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
* Author: Raymond Tan <raymond.tan@intel.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include "m_can.h"
#define M_CAN_PCI_MMIO_BAR 0
#define M_CAN_CLOCK_FREQ_EHL 100000000
#define CTL_CSR_INT_CTL_OFFSET 0x508
struct m_can_pci_priv {
struct m_can_classdev cdev;
void __iomem *base;
};
static inline struct m_can_pci_priv *cdev_to_priv(struct m_can_classdev *cdev)
{
return container_of(cdev, struct m_can_pci_priv, cdev);
}
static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
{
struct m_can_pci_priv *priv = cdev_to_priv(cdev);
return readl(priv->base + reg);
}
static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset)
{
struct m_can_pci_priv *priv = cdev_to_priv(cdev);
return readl(priv->base + offset);
}
static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
{
struct m_can_pci_priv *priv = cdev_to_priv(cdev);
writel(val, priv->base + reg);
return 0;
}
static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val)
{
struct m_can_pci_priv *priv = cdev_to_priv(cdev);
writel(val, priv->base + offset);
return 0;
}
static struct m_can_ops m_can_pci_ops = {
.read_reg = iomap_read_reg,
.write_reg = iomap_write_reg,
.write_fifo = iomap_write_fifo,
.read_fifo = iomap_read_fifo,
};
static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
struct device *dev = &pci->dev;
struct m_can_classdev *mcan_class;
struct m_can_pci_priv *priv;
void __iomem *base;
int ret;
ret = pcim_enable_device(pci);
if (ret)
return ret;
pci_set_master(pci);
ret = pcim_iomap_regions(pci, BIT(M_CAN_PCI_MMIO_BAR), pci_name(pci));
if (ret)
return ret;
base = pcim_iomap_table(pci)[M_CAN_PCI_MMIO_BAR];
if (!base) {
dev_err(dev, "failed to map BARs\n");
return -ENOMEM;
}
mcan_class = m_can_class_allocate_dev(&pci->dev,
sizeof(struct m_can_pci_priv));
if (!mcan_class)
return -ENOMEM;
priv = cdev_to_priv(mcan_class);
priv->base = base;
ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_ALL_TYPES);
if (ret < 0)
return ret;
mcan_class->dev = &pci->dev;
mcan_class->net->irq = pci_irq_vector(pci, 0);
mcan_class->pm_clock_support = 1;
mcan_class->can.clock.freq = id->driver_data;
mcan_class->ops = &m_can_pci_ops;
pci_set_drvdata(pci, mcan_class);
ret = m_can_class_register(mcan_class);
if (ret)
goto err;
/* Enable interrupt control at CAN wrapper IP */
writel(0x1, base + CTL_CSR_INT_CTL_OFFSET);
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
pm_runtime_put_noidle(dev);
pm_runtime_allow(dev);
return 0;
err:
pci_free_irq_vectors(pci);
return ret;
}
static void m_can_pci_remove(struct pci_dev *pci)
{
struct m_can_classdev *mcan_class = pci_get_drvdata(pci);
struct m_can_pci_priv *priv = cdev_to_priv(mcan_class);
pm_runtime_forbid(&pci->dev);
pm_runtime_get_noresume(&pci->dev);
/* Disable interrupt control at CAN wrapper IP */
writel(0x0, priv->base + CTL_CSR_INT_CTL_OFFSET);
m_can_class_unregister(mcan_class);
pci_free_irq_vectors(pci);
}
static __maybe_unused int m_can_pci_suspend(struct device *dev)
{
return m_can_class_suspend(dev);
}
static __maybe_unused int m_can_pci_resume(struct device *dev)
{
return m_can_class_resume(dev);
}
static SIMPLE_DEV_PM_OPS(m_can_pci_pm_ops,
m_can_pci_suspend, m_can_pci_resume);
static const struct pci_device_id m_can_pci_id_table[] = {
{ PCI_VDEVICE(INTEL, 0x4bc1), M_CAN_CLOCK_FREQ_EHL, },
{ PCI_VDEVICE(INTEL, 0x4bc2), M_CAN_CLOCK_FREQ_EHL, },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, m_can_pci_id_table);
static struct pci_driver m_can_pci_driver = {
.name = "m_can_pci",
.probe = m_can_pci_probe,
.remove = m_can_pci_remove,
.id_table = m_can_pci_id_table,
.driver = {
.pm = &m_can_pci_pm_ops,
},
};
module_pci_driver(m_can_pci_driver);
MODULE_AUTHOR("Felipe Balbi (Intel)");
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller on PCI bus");

View File

@ -10,27 +10,34 @@
#include "m_can.h" #include "m_can.h"
struct m_can_plat_priv { struct m_can_plat_priv {
struct m_can_classdev cdev;
void __iomem *base; void __iomem *base;
void __iomem *mram_base; void __iomem *mram_base;
}; };
static inline struct m_can_plat_priv *cdev_to_priv(struct m_can_classdev *cdev)
{
return container_of(cdev, struct m_can_plat_priv, cdev);
}
static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg) static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
{ {
struct m_can_plat_priv *priv = cdev->device_data; struct m_can_plat_priv *priv = cdev_to_priv(cdev);
return readl(priv->base + reg); return readl(priv->base + reg);
} }
static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset) static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset)
{ {
struct m_can_plat_priv *priv = cdev->device_data; struct m_can_plat_priv *priv = cdev_to_priv(cdev);
return readl(priv->mram_base + offset); return readl(priv->mram_base + offset);
} }
static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
{ {
struct m_can_plat_priv *priv = cdev->device_data; struct m_can_plat_priv *priv = cdev_to_priv(cdev);
writel(val, priv->base + reg); writel(val, priv->base + reg);
@ -39,7 +46,7 @@ static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val) static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val)
{ {
struct m_can_plat_priv *priv = cdev->device_data; struct m_can_plat_priv *priv = cdev_to_priv(cdev);
writel(val, priv->mram_base + offset); writel(val, priv->mram_base + offset);
@ -62,17 +69,12 @@ static int m_can_plat_probe(struct platform_device *pdev)
void __iomem *mram_addr; void __iomem *mram_addr;
int irq, ret = 0; int irq, ret = 0;
mcan_class = m_can_class_allocate_dev(&pdev->dev); mcan_class = m_can_class_allocate_dev(&pdev->dev,
sizeof(struct m_can_plat_priv));
if (!mcan_class) if (!mcan_class)
return -ENOMEM; return -ENOMEM;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); priv = cdev_to_priv(mcan_class);
if (!priv) {
ret = -ENOMEM;
goto probe_fail;
}
mcan_class->device_data = priv;
ret = m_can_class_get_clocks(mcan_class); ret = m_can_class_get_clocks(mcan_class);
if (ret) if (ret)
@ -111,12 +113,19 @@ static int m_can_plat_probe(struct platform_device *pdev)
mcan_class->is_peripheral = false; mcan_class->is_peripheral = false;
platform_set_drvdata(pdev, mcan_class->net); platform_set_drvdata(pdev, mcan_class);
m_can_init_ram(mcan_class); m_can_init_ram(mcan_class);
return m_can_class_register(mcan_class); pm_runtime_enable(mcan_class->dev);
ret = m_can_class_register(mcan_class);
if (ret)
goto out_runtime_disable;
return ret;
out_runtime_disable:
pm_runtime_disable(mcan_class->dev);
probe_fail: probe_fail:
m_can_class_free_dev(mcan_class->net); m_can_class_free_dev(mcan_class->net);
return ret; return ret;
@ -134,22 +143,20 @@ static __maybe_unused int m_can_resume(struct device *dev)
static int m_can_plat_remove(struct platform_device *pdev) static int m_can_plat_remove(struct platform_device *pdev)
{ {
struct net_device *dev = platform_get_drvdata(pdev); struct m_can_plat_priv *priv = platform_get_drvdata(pdev);
struct m_can_classdev *mcan_class = netdev_priv(dev); struct m_can_classdev *mcan_class = &priv->cdev;
m_can_class_unregister(mcan_class); m_can_class_unregister(mcan_class);
m_can_class_free_dev(mcan_class->net); m_can_class_free_dev(mcan_class->net);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }
static int __maybe_unused m_can_runtime_suspend(struct device *dev) static int __maybe_unused m_can_runtime_suspend(struct device *dev)
{ {
struct net_device *ndev = dev_get_drvdata(dev); struct m_can_plat_priv *priv = dev_get_drvdata(dev);
struct m_can_classdev *mcan_class = netdev_priv(ndev); struct m_can_classdev *mcan_class = &priv->cdev;
clk_disable_unprepare(mcan_class->cclk); clk_disable_unprepare(mcan_class->cclk);
clk_disable_unprepare(mcan_class->hclk); clk_disable_unprepare(mcan_class->hclk);
@ -159,8 +166,8 @@ static int __maybe_unused m_can_runtime_suspend(struct device *dev)
static int __maybe_unused m_can_runtime_resume(struct device *dev) static int __maybe_unused m_can_runtime_resume(struct device *dev)
{ {
struct net_device *ndev = dev_get_drvdata(dev); struct m_can_plat_priv *priv = dev_get_drvdata(dev);
struct m_can_classdev *mcan_class = netdev_priv(ndev); struct m_can_classdev *mcan_class = &priv->cdev;
int err; int err;
err = clk_prepare_enable(mcan_class->hclk); err = clk_prepare_enable(mcan_class->hclk);

View File

@ -114,21 +114,23 @@
#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29)) #define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
struct tcan4x5x_priv { struct tcan4x5x_priv {
struct m_can_classdev cdev;
struct regmap *regmap; struct regmap *regmap;
struct spi_device *spi; struct spi_device *spi;
struct m_can_classdev *mcan_dev;
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
struct gpio_desc *device_wake_gpio; struct gpio_desc *device_wake_gpio;
struct gpio_desc *device_state_gpio; struct gpio_desc *device_state_gpio;
struct regulator *power; struct regulator *power;
/* Register based ip */
int mram_start;
int reg_offset;
}; };
static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
{
return container_of(cdev, struct tcan4x5x_priv, cdev);
}
static struct can_bittiming_const tcan4x5x_bittiming_const = { static struct can_bittiming_const tcan4x5x_bittiming_const = {
.name = DEVICE_NAME, .name = DEVICE_NAME,
.tseg1_min = 2, .tseg1_min = 2,
@ -257,37 +259,37 @@ static struct regmap_bus tcan4x5x_bus = {
static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg) static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg)
{ {
struct tcan4x5x_priv *priv = cdev->device_data; struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
u32 val; u32 val;
regmap_read(priv->regmap, priv->reg_offset + reg, &val); regmap_read(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, &val);
return val; return val;
} }
static u32 tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset) static u32 tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset)
{ {
struct tcan4x5x_priv *priv = cdev->device_data; struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
u32 val; u32 val;
regmap_read(priv->regmap, priv->mram_start + addr_offset, &val); regmap_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, &val);
return val; return val;
} }
static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val) static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val)
{ {
struct tcan4x5x_priv *priv = cdev->device_data; struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
return regmap_write(priv->regmap, priv->reg_offset + reg, val); return regmap_write(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, val);
} }
static int tcan4x5x_write_fifo(struct m_can_classdev *cdev, static int tcan4x5x_write_fifo(struct m_can_classdev *cdev,
int addr_offset, int val) int addr_offset, int val)
{ {
struct tcan4x5x_priv *priv = cdev->device_data; struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
return regmap_write(priv->regmap, priv->mram_start + addr_offset, val); return regmap_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val);
} }
static int tcan4x5x_power_enable(struct regulator *reg, int enable) static int tcan4x5x_power_enable(struct regulator *reg, int enable)
@ -304,7 +306,7 @@ static int tcan4x5x_power_enable(struct regulator *reg, int enable)
static int tcan4x5x_write_tcan_reg(struct m_can_classdev *cdev, static int tcan4x5x_write_tcan_reg(struct m_can_classdev *cdev,
int reg, int val) int reg, int val)
{ {
struct tcan4x5x_priv *priv = cdev->device_data; struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
return regmap_write(priv->regmap, reg, val); return regmap_write(priv->regmap, reg, val);
} }
@ -328,17 +330,13 @@ static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev)
if (ret) if (ret)
return ret; return ret;
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS, return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS,
TCAN4X5X_CLEAR_ALL_INT); TCAN4X5X_CLEAR_ALL_INT);
if (ret)
return ret;
return ret;
} }
static int tcan4x5x_init(struct m_can_classdev *cdev) static int tcan4x5x_init(struct m_can_classdev *cdev)
{ {
struct tcan4x5x_priv *tcan4x5x = cdev->device_data; struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
int ret; int ret;
tcan4x5x_check_wake(tcan4x5x); tcan4x5x_check_wake(tcan4x5x);
@ -365,7 +363,7 @@ static int tcan4x5x_init(struct m_can_classdev *cdev)
static int tcan4x5x_disable_wake(struct m_can_classdev *cdev) static int tcan4x5x_disable_wake(struct m_can_classdev *cdev)
{ {
struct tcan4x5x_priv *tcan4x5x = cdev->device_data; struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG, return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
TCAN4X5X_DISABLE_WAKE_MSK, 0x00); TCAN4X5X_DISABLE_WAKE_MSK, 0x00);
@ -373,15 +371,15 @@ static int tcan4x5x_disable_wake(struct m_can_classdev *cdev)
static int tcan4x5x_disable_state(struct m_can_classdev *cdev) static int tcan4x5x_disable_state(struct m_can_classdev *cdev)
{ {
struct tcan4x5x_priv *tcan4x5x = cdev->device_data; struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG, return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
TCAN4X5X_DISABLE_INH_MSK, 0x01); TCAN4X5X_DISABLE_INH_MSK, 0x01);
} }
static int tcan4x5x_parse_config(struct m_can_classdev *cdev) static int tcan4x5x_get_gpios(struct m_can_classdev *cdev)
{ {
struct tcan4x5x_priv *tcan4x5x = cdev->device_data; struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
int ret; int ret;
tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake", tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake",
@ -435,15 +433,12 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
struct m_can_classdev *mcan_class; struct m_can_classdev *mcan_class;
int freq, ret; int freq, ret;
mcan_class = m_can_class_allocate_dev(&spi->dev); mcan_class = m_can_class_allocate_dev(&spi->dev,
sizeof(struct tcan4x5x_priv));
if (!mcan_class) if (!mcan_class)
return -ENOMEM; return -ENOMEM;
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); priv = cdev_to_priv(mcan_class);
if (!priv) {
ret = -ENOMEM;
goto out_m_can_class_free_dev;
}
priv->power = devm_regulator_get_optional(&spi->dev, "vsup"); priv->power = devm_regulator_get_optional(&spi->dev, "vsup");
if (PTR_ERR(priv->power) == -EPROBE_DEFER) { if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
@ -453,8 +448,6 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
priv->power = NULL; priv->power = NULL;
} }
mcan_class->device_data = priv;
m_can_class_get_clocks(mcan_class); m_can_class_get_clocks(mcan_class);
if (IS_ERR(mcan_class->cclk)) { if (IS_ERR(mcan_class->cclk)) {
dev_err(&spi->dev, "no CAN clock source defined\n"); dev_err(&spi->dev, "no CAN clock source defined\n");
@ -469,10 +462,7 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
goto out_m_can_class_free_dev; goto out_m_can_class_free_dev;
} }
priv->reg_offset = TCAN4X5X_MCAN_OFFSET;
priv->mram_start = TCAN4X5X_MRAM_START;
priv->spi = spi; priv->spi = spi;
priv->mcan_dev = mcan_class;
mcan_class->pm_clock_support = 0; mcan_class->pm_clock_support = 0;
mcan_class->can.clock.freq = freq; mcan_class->can.clock.freq = freq;
@ -502,7 +492,7 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
if (ret) if (ret)
goto out_m_can_class_free_dev; goto out_m_can_class_free_dev;
ret = tcan4x5x_parse_config(mcan_class); ret = tcan4x5x_get_gpios(mcan_class);
if (ret) if (ret)
goto out_power; goto out_power;
@ -521,8 +511,6 @@ out_power:
tcan4x5x_power_enable(priv->power, 0); tcan4x5x_power_enable(priv->power, 0);
out_m_can_class_free_dev: out_m_can_class_free_dev:
m_can_class_free_dev(mcan_class->net); m_can_class_free_dev(mcan_class->net);
dev_err(&spi->dev, "Probe failed, err=%d\n", ret);
return ret; return ret;
} }
@ -530,11 +518,11 @@ static int tcan4x5x_can_remove(struct spi_device *spi)
{ {
struct tcan4x5x_priv *priv = spi_get_drvdata(spi); struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
m_can_class_unregister(priv->mcan_dev); m_can_class_unregister(&priv->cdev);
tcan4x5x_power_enable(priv->power, 0); tcan4x5x_power_enable(priv->power, 0);
m_can_class_free_dev(priv->mcan_dev->net); m_can_class_free_dev(priv->cdev.net);
return 0; return 0;
} }

View File

@ -250,16 +250,16 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
void __iomem *data = &regs->tx.dsr1_0; void __iomem *data = &regs->tx.dsr1_0;
u16 *payload = (u16 *)frame->data; u16 *payload = (u16 *)frame->data;
for (i = 0; i < frame->can_dlc / 2; i++) { for (i = 0; i < frame->len / 2; i++) {
out_be16(data, *payload++); out_be16(data, *payload++);
data += 2 + _MSCAN_RESERVED_DSR_SIZE; data += 2 + _MSCAN_RESERVED_DSR_SIZE;
} }
/* write remaining byte if necessary */ /* write remaining byte if necessary */
if (frame->can_dlc & 1) if (frame->len & 1)
out_8(data, frame->data[frame->can_dlc - 1]); out_8(data, frame->data[frame->len - 1]);
} }
out_8(&regs->tx.dlr, frame->can_dlc); out_8(&regs->tx.dlr, frame->len);
out_8(&regs->tx.tbpr, priv->cur_pri); out_8(&regs->tx.tbpr, priv->cur_pri);
/* Start transmission. */ /* Start transmission. */
@ -312,19 +312,19 @@ static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame)
if (can_id & 1) if (can_id & 1)
frame->can_id |= CAN_RTR_FLAG; frame->can_id |= CAN_RTR_FLAG;
frame->can_dlc = get_can_dlc(in_8(&regs->rx.dlr) & 0xf); frame->len = can_cc_dlc2len(in_8(&regs->rx.dlr) & 0xf);
if (!(frame->can_id & CAN_RTR_FLAG)) { if (!(frame->can_id & CAN_RTR_FLAG)) {
void __iomem *data = &regs->rx.dsr1_0; void __iomem *data = &regs->rx.dsr1_0;
u16 *payload = (u16 *)frame->data; u16 *payload = (u16 *)frame->data;
for (i = 0; i < frame->can_dlc / 2; i++) { for (i = 0; i < frame->len / 2; i++) {
*payload++ = in_be16(data); *payload++ = in_be16(data);
data += 2 + _MSCAN_RESERVED_DSR_SIZE; data += 2 + _MSCAN_RESERVED_DSR_SIZE;
} }
/* read remaining byte if necessary */ /* read remaining byte if necessary */
if (frame->can_dlc & 1) if (frame->len & 1)
frame->data[frame->can_dlc - 1] = in_8(data); frame->data[frame->len - 1] = in_8(data);
} }
out_8(&regs->canrflg, MSCAN_RXF); out_8(&regs->canrflg, MSCAN_RXF);
@ -372,7 +372,7 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame,
} }
} }
priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
frame->can_dlc = CAN_ERR_DLC; frame->len = CAN_ERR_DLC;
out_8(&regs->canrflg, MSCAN_ERR_IF); out_8(&regs->canrflg, MSCAN_ERR_IF);
} }
@ -407,7 +407,7 @@ static int mscan_rx_poll(struct napi_struct *napi, int quota)
mscan_get_err_frame(dev, frame, canrflg); mscan_get_err_frame(dev, frame, canrflg);
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += frame->can_dlc; stats->rx_bytes += frame->len;
work_done++; work_done++;
netif_receive_skb(skb); netif_receive_skb(skb);
} }

View File

@ -563,7 +563,7 @@ static void pch_can_error(struct net_device *ndev, u32 status)
netif_receive_skb(skb); netif_receive_skb(skb);
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
} }
static irqreturn_t pch_can_interrupt(int irq, void *dev_id) static irqreturn_t pch_can_interrupt(int irq, void *dev_id)
@ -683,10 +683,10 @@ static int pch_can_rx_normal(struct net_device *ndev, u32 obj_num, int quota)
if (id2 & PCH_ID2_DIR) if (id2 & PCH_ID2_DIR)
cf->can_id |= CAN_RTR_FLAG; cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc((ioread32(&priv->regs-> cf->len = can_cc_dlc2len((ioread32(&priv->regs->
ifregs[0].mcont)) & 0xF); ifregs[0].mcont)) & 0xF);
for (i = 0; i < cf->can_dlc; i += 2) { for (i = 0; i < cf->len; i += 2) {
data_reg = ioread16(&priv->regs->ifregs[0].data[i / 2]); data_reg = ioread16(&priv->regs->ifregs[0].data[i / 2]);
cf->data[i] = data_reg; cf->data[i] = data_reg;
cf->data[i + 1] = data_reg >> 8; cf->data[i + 1] = data_reg >> 8;
@ -696,7 +696,7 @@ static int pch_can_rx_normal(struct net_device *ndev, u32 obj_num, int quota)
rcv_pkts++; rcv_pkts++;
stats->rx_packets++; stats->rx_packets++;
quota--; quota--;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
pch_fifo_thresh(priv, obj_num); pch_fifo_thresh(priv, obj_num);
obj_num++; obj_num++;
@ -715,7 +715,7 @@ static void pch_can_tx_complete(struct net_device *ndev, u32 int_stat)
iowrite32(PCH_CMASK_RX_TX_GET | PCH_CMASK_CLRINTPND, iowrite32(PCH_CMASK_RX_TX_GET | PCH_CMASK_CLRINTPND,
&priv->regs->ifregs[1].cmask); &priv->regs->ifregs[1].cmask);
pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, int_stat); pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, int_stat);
dlc = get_can_dlc(ioread32(&priv->regs->ifregs[1].mcont) & dlc = can_cc_dlc2len(ioread32(&priv->regs->ifregs[1].mcont) &
PCH_IF_MCONT_DLC); PCH_IF_MCONT_DLC);
stats->tx_bytes += dlc; stats->tx_bytes += dlc;
stats->tx_packets++; stats->tx_packets++;
@ -919,7 +919,7 @@ static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev)
iowrite32(id2, &priv->regs->ifregs[1].id2); iowrite32(id2, &priv->regs->ifregs[1].id2);
/* Copy data to register */ /* Copy data to register */
for (i = 0; i < cf->can_dlc; i += 2) { for (i = 0; i < cf->len; i += 2) {
iowrite16(cf->data[i] | (cf->data[i + 1] << 8), iowrite16(cf->data[i] | (cf->data[i + 1] << 8),
&priv->regs->ifregs[1].data[i / 2]); &priv->regs->ifregs[1].data[i / 2]);
} }
@ -927,7 +927,7 @@ static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev)
can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1); can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1);
/* Set the size of the data. Update if2_mcont */ /* Set the size of the data. Update if2_mcont */
iowrite32(cf->can_dlc | PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT | iowrite32(cf->len | PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT |
PCH_IF_MCONT_TXIE, &priv->regs->ifregs[1].mcont); PCH_IF_MCONT_TXIE, &priv->regs->ifregs[1].mcont);
pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, tx_obj_no); pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, tx_obj_no);

View File

@ -257,9 +257,9 @@ static int pucan_handle_can_rx(struct peak_canfd_priv *priv,
u8 cf_len; u8 cf_len;
if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN)
cf_len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(msg))); cf_len = can_fd_dlc2len(pucan_msg_get_dlc(msg));
else else
cf_len = get_can_dlc(pucan_msg_get_dlc(msg)); cf_len = can_cc_dlc2len(pucan_msg_get_dlc(msg));
/* if this frame is an echo, */ /* if this frame is an echo, */
if (rx_msg_flags & PUCAN_MSG_LOOPED_BACK) { if (rx_msg_flags & PUCAN_MSG_LOOPED_BACK) {
@ -410,7 +410,7 @@ static int pucan_handle_status(struct peak_canfd_priv *priv,
} }
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
pucan_netif_rx(skb, msg->ts_low, msg->ts_high); pucan_netif_rx(skb, msg->ts_low, msg->ts_high);
return 0; return 0;
@ -438,7 +438,7 @@ static int pucan_handle_cache_critical(struct peak_canfd_priv *priv)
cf->data[6] = priv->bec.txerr; cf->data[6] = priv->bec.txerr;
cf->data[7] = priv->bec.rxerr; cf->data[7] = priv->bec.rxerr;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
stats->rx_packets++; stats->rx_packets++;
netif_rx(skb); netif_rx(skb);
@ -652,7 +652,7 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
unsigned long flags; unsigned long flags;
bool should_stop_tx_queue; bool should_stop_tx_queue;
int room_left; int room_left;
u8 can_dlc; u8 len;
if (can_dropped_invalid_skb(ndev, skb)) if (can_dropped_invalid_skb(ndev, skb))
return NETDEV_TX_OK; return NETDEV_TX_OK;
@ -682,7 +682,7 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
if (can_is_canfd_skb(skb)) { if (can_is_canfd_skb(skb)) {
/* CAN FD frame format */ /* CAN FD frame format */
can_dlc = can_len2dlc(cf->len); len = can_fd_len2dlc(cf->len);
msg_flags |= PUCAN_MSG_EXT_DATA_LEN; msg_flags |= PUCAN_MSG_EXT_DATA_LEN;
@ -693,7 +693,7 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
msg_flags |= PUCAN_MSG_ERROR_STATE_IND; msg_flags |= PUCAN_MSG_ERROR_STATE_IND;
} else { } else {
/* CAN 2.0 frame format */ /* CAN 2.0 frame format */
can_dlc = cf->len; len = cf->len;
if (cf->can_id & CAN_RTR_FLAG) if (cf->can_id & CAN_RTR_FLAG)
msg_flags |= PUCAN_MSG_RTR; msg_flags |= PUCAN_MSG_RTR;
@ -707,7 +707,7 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
msg_flags |= PUCAN_MSG_SELF_RECEIVE; msg_flags |= PUCAN_MSG_SELF_RECEIVE;
msg->flags = cpu_to_le16(msg_flags); msg->flags = cpu_to_le16(msg_flags);
msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, can_dlc); msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, len);
memcpy(msg->d, cf->data, cf->len); memcpy(msg->d, cf->data, cf->len);
/* struct msg client field is used as an index in the echo skbs ring */ /* struct msg client field is used as an index in the echo skbs ring */

View File

@ -364,7 +364,7 @@ static void rcar_can_error(struct net_device *ndev)
if (skb) { if (skb) {
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
netif_rx(skb); netif_rx(skb);
} }
} }
@ -607,16 +607,16 @@ static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb,
if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */ if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */
data |= RCAR_CAN_RTR; data |= RCAR_CAN_RTR;
} else { } else {
for (i = 0; i < cf->can_dlc; i++) for (i = 0; i < cf->len; i++)
writeb(cf->data[i], writeb(cf->data[i],
&priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]); &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]);
} }
writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id); writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id);
writeb(cf->can_dlc, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc); writeb(cf->len, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc);
priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->can_dlc; priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->len;
can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH); can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH);
priv->tx_head++; priv->tx_head++;
/* Start Tx: write 0xff to the TFPCR register to increment /* Start Tx: write 0xff to the TFPCR register to increment
@ -659,18 +659,18 @@ static void rcar_can_rx_pkt(struct rcar_can_priv *priv)
cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK; cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK;
dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc); dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc);
cf->can_dlc = get_can_dlc(dlc); cf->len = can_cc_dlc2len(dlc);
if (data & RCAR_CAN_RTR) { if (data & RCAR_CAN_RTR) {
cf->can_id |= CAN_RTR_FLAG; cf->can_id |= CAN_RTR_FLAG;
} else { } else {
for (dlc = 0; dlc < cf->can_dlc; dlc++) for (dlc = 0; dlc < cf->len; dlc++)
cf->data[dlc] = cf->data[dlc] =
readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]); readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]);
} }
can_led_event(priv->ndev, CAN_LED_EVENT_RX); can_led_event(priv->ndev, CAN_LED_EVENT_RX);
stats->rx_bytes += cf->can_dlc; stats->rx_bytes += cf->len;
stats->rx_packets++; stats->rx_packets++;
netif_receive_skb(skb); netif_receive_skb(skb);
} }

Some files were not shown because too many files have changed in this diff Show More