Networking changes for 7.3.

Core & protocols
 ----------------
 
  - A few steps lowering rtnl_lock dependence:
    - per-netns netdev unregistration for select SW drivers
      (e.g. veth, ipvlan, tunnels)
    - rtnl_lock-less FIB rule changes (RTM_NEWRULE and RTM_DELRULE)
    - prepare software drivers and TC qdiscs for rtnl_lock-less GET
 
  - Support BIG TCP (>64kB TSO) in UDP tunnels (vxlan, geneve).
 
  - Support buffers larger than PAGE_SIZE in devmem zero-copy API.
 
  - Improve MPTCP handling of extreme memory pressure handling,
    when out-of-order queue had to be pruned.
 
  - Report the per-group user count via RTM_GETMULTICAST.
 
  - Expose the route deletion reason in RTM_DELROUTE.
 
  - Add a SO_RIGHTS_NOTRUNC option to UNIX sockets to enable more useful
    handling of LSM denials when receiving SCM_RIGHTS messages: instead
    of truncating the message at the first blocked fd, keep every fd slot
    and store the LSM errno in the blocked slot.
 
  - IPv6 Segment Routing - support looking up the post-encap SID
    (address) in a different/specified routing table.
 
  - Support PRP RedBox (interlink) creation.
 
  - Support per-nexthop UDP dst port in VXLAN.
 
  - Continue converting getsockopt callbacks in a number of protocols
    to iov_iter.
 
 Ethernet
 --------
 
  - Marge initial CXL support for AMD/Solarflare NICs (shared branch
    with the CXL tree).
 
  - New drivers:
    - ADIN1140 10BASE-T1S MACPHY
    - Initial skeleton of Intel iXD and ZTE Dinghai drivers.
 
  - High-speed NICs:
    - AMD/Pensando:
      - support firmware flashing
    - Cisco (enic):
      - SR-IOV V2 admin channel and MBOX protocol
    - Huawei (hns3):
      - support for ethtool pfc_prevention_tout
    - nVidia/Mellanox:
      - support sharing bandwidth control across interfaces of
        the same device
    - Marvell (octeontx2-pf):
      - link RQ page pools to netdev for Netlink stats
    - Google vNIC:
      - XDP metadata support for DQ RDA
    - Microsoft vNIC:
      - support forcing full-page RX buffers
 
  - Other NICs:
    - Synopsys IP:
      - eic7700: support for eth1
    - Microchip (lan743x):
      - support for RMII interface
    - Wangxun:
      - support for ethtool -G and -C for VFs
      - add Tx timeout and PCIe error handling
    - Intel (igb/igc):
      - RSS key get/set support
      - support for forcing link speed without auto-negotiation
 
  - Switches:
    - NXP (dpaa2):
      - support bonding/LAG offload
    - Mediatek:
      - mt7530: EN7528 support
      - initial support for MT7628
    - Micrel (ksz8/9):
      - refactoring work to move towards library model
      - PTP support for KSZ8463
    - nVidia/Mellanox:
      - support rtnl-lock-less ethtool callbacks
    - Realtek:
      - rtl8366rb: use generic RTL83xx code
      - support SGMII and HSGMII for RTL8367S
 
  - PHYs:
    - Airoha:
      - EcoNet EN7528 PHY support
    - DAPU Telecom
      - DAPU Telecom DAP8211R(I) Gigabit PHY support
    - Realtek:
      - support RTL8261C_CG
      - support RTL8261D
 
 Wireless
 --------
 
  - nl80211: per-link statistics support for multi-link operation
 
  - mac80211: AQL/airtime-fairness support for multicast
 
  - Merge Peripheral Authentication Service (PAS) / TEE support
    for ath12k (shared branch with the firmware/qcom tree).
 
  - New drivers:
    - mm81x for Morse Micro Long-Range S1G devices
    - nxpwifi for NXP devices (mostly forked off from mwifiex)
 
  - Driver changes:
    - Broadcom (brcmfmac):
      - DPP support, some Cypress part update
    - MediaTek (mt76):
      - mt7928 support
      - mt7925 NAN support
      - mt7996 AP powersave improvements
    - Qualcomm (ath12k):
      - much kernel infrastructure integration work
      - AHB platform MultiPD support
    - Realtek (rt89):
      - LED support
      - RTL8922DE support
      - dual-BT coex for RTL8922D
    - Intel:
      - new FW version support
 
 Bluetooth
 ---------
 
  - HCI: add support for Shorter Connection Interval (SCI) feature.
 
  - af_bluetooth: add minimal context analysis annotations.
 
  - Driver changes:
    - Intel:
      - add Bluetooth SAR revision 2 support
      - add vendor_reset PCI sysfs for PLDR
    - Mediatek:
      - add USB IDs for MT7902 and MT7922 devices
    - Realtek:
      - add USB IDs for 8761CU and 8852BE devices
    - NXP:
      - add M.2 Bluetooth device support using pwrseq
 
 Misc
 ----
 
  - DPLL support for manual/numerical oscillator control (NCO)
    (implement in zl3073x).
 
  - MCTP support for MCTP over USB v1.1 (DMTF DSP0283).
 
  - Power-over-Ethernet: support Realtek PSE controllers.
 
  - Remove the IBM EHEA driver.
 
  - Remove tulip/xircom_cb driver.
 
 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE6jPA+I1ugmIBA4hXMUZtbf5SIrsFAmqEwJ4ACgkQMUZtbf5S
 Irsegw//fmHJae525nxg3DHoXhrUz8EDDOVoLH6oyWyLQnh5bmbReAY/+oWA4m54
 3KKKO0b2rtgRvmY/7rnjAt3bjecYgCSjvZT7I+NosB0QbbBYc14PtHfYig9HffYm
 uCXfNJOk+aJ2QK4ncEvU2SjgE89Ya7cC+yARFBAwYx4zi/Qx24RB+ziOyvkQ8ksX
 atvMOZrnhwqvYUFOwnOLNHTpvdxB/ZsNwWY6iXcx6EYp9xrtPusbh3FlushWkwxH
 8cI/dNla44TcIKXAzRn0znRdgiEVmCMyHvOv7LKaOfy8P3I+knmuIf/mScYQqOEF
 T143HdXhVSBZFRtLtFKXIja/KsvCjX9lCeMn/2ak0brQDUREcacXxYbuZKDsNAAK
 zXt/+5qAcm/mO8W1gKR9Ulfli5bhFN4HKXgXMLjo5ucPtzfPxFN7HGxTiC3Cxv1v
 lSXexKaj74pNBVFmADrb5jWbq7oG+GzIdjzx3ycvm2q39Fr4nJ2SzrSPPNwc/ItQ
 IHv3tGLQKXlr8dl0+p2mDkRInmHXrawVNsB1UgN8E/jtcwT2QMwyWOV6s5G3uEDl
 a+0U/XsrPvDYBTUCRs/KaOJQGB90QkzLe9DATt159mf+rPzAX2/oCDo8xIEe+kWV
 aivP+YutFfMH/CSC9PMuvdLE2KmoPY4mibAeE4/4AYLKtJnc/yU=
 =zDto
 -----END PGP SIGNATURE-----

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

Pull networking updates from Jakub Kicinski:
 "One of the 'small improvements all over the place' releases for us.

  It's hard to draw any direct comparisons because summer vacations
  disrupted our patch processing (and presumably - generation) quite a
  bit.

  Quick and dirty count suggests we (Paolo and I) merged a very similar
  number of net (632) and net-next (648) patches. This is not telling
  the full story either because 1/3 to 1/2 of the net-next patches also
  *seem* like AI-driven low priority fixes, cleanups and clarifications.

  We are completely overwhelmed, of course. The glimmer of hope is that
  we secured sufficient LLM budget and access (thank you Meta!) to run
  reviews with multiple frontier models on each patch. This eliminates
  some hallucinations. That said, in terms of review, the LLMs can only
  do so much.

  The sad truth is that our APIs (especially for rare events like PCIe
  errors, timeouts etc) have always been racy, and now LLMs don't let us
  ignore that. I expect our direction for the next release will be to
  tweak the reviews a little bit more, but start shifting focus to
  letting the LLMs take care of the busy work - managing patchwork,
  automating common process complaints, editing commit messages, and
  maybe applying patches which already got "reviewed-by" tags from
  people we trust...

  Core & protocols:

   - A few steps lowering rtnl_lock dependence:
      - per-netns netdev unregistration for select SW drivers (e.g.
        veth, ipvlan, tunnels)
      - rtnl_lock-less FIB rule changes (RTM_NEWRULE and RTM_DELRULE)
      - prepare software drivers and TC qdiscs for rtnl_lock-less GET

   - Support BIG TCP (>64kB TSO) in UDP tunnels (vxlan, geneve)

   - Support buffers larger than PAGE_SIZE in devmem zero-copy API

   - Improve MPTCP handling of extreme memory pressure handling, when
     out-of-order queue had to be pruned

   - Report the per-group user count via RTM_GETMULTICAST

   - Expose the route deletion reason in RTM_DELROUTE

   - Add a SO_RIGHTS_NOTRUNC option to UNIX sockets to enable more
     useful handling of LSM denials when receiving SCM_RIGHTS messages:
     instead of truncating the message at the first blocked fd, keep
     every fd slot and store the LSM errno in the blocked slot

   - IPv6 Segment Routing - support looking up the post-encap SID
     (address) in a different/specified routing table

   - Support PRP RedBox (interlink) creation

   - Support per-nexthop UDP dst port in VXLAN

   - Continue converting getsockopt callbacks in a number of protocols
     to iov_iter

  Ethernet:

   - Merge initial CXL support for AMD/Solarflare NICs (shared branch
     with the CXL tree)

   - New drivers:
      - ADIN1140 10BASE-T1S MACPHY
      - Initial skeleton of Intel iXD and ZTE Dinghai drivers

   - High-speed NICs:
      - AMD/Pensando:
         - support firmware flashing
      - Cisco (enic):
         - SR-IOV V2 admin channel and MBOX protocol
      - Huawei (hns3):
         - support for ethtool pfc_prevention_tout
      - nVidia/Mellanox:
         - support sharing bandwidth control across interfaces
           of the same device
      - Marvell (octeontx2-pf):
         - link RQ page pools to netdev for Netlink stats
      - Google vNIC:
         - XDP metadata support for DQ RDA
      - Microsoft vNIC:
         - support forcing full-page RX buffers

   - Other NICs:
      - Synopsys IP:
         - eic7700: support for eth1
      - Microchip (lan743x):
         - support for RMII interface
      - Wangxun:
         - support for ethtool -G and -C for VFs
         - add Tx timeout and PCIe error handling
      - Intel (igb/igc):
         - RSS key get/set support
         - support for forcing link speed without auto-negotiation

   - Switches:
      - NXP (dpaa2):
         - support bonding/LAG offload
      - Mediatek:
         - mt7530: EN7528 support
         - initial support for MT7628
      - Micrel (ksz8/9):
         - refactoring work to move towards library model
         - PTP support for KSZ8463
      - nVidia/Mellanox:
         - support rtnl-lock-less ethtool callbacks
      - Realtek:
         - rtl8366rb: use generic RTL83xx code
         - support SGMII and HSGMII for RTL8367S

   - PHYs:
      - Airoha:
         - EcoNet EN7528 PHY support
      - DAPU Telecom
         - DAPU Telecom DAP8211R(I) Gigabit PHY support
      - Realtek:
         - support RTL8261C_CG
         - support RTL8261D

  Wireless:

   - nl80211: per-link statistics support for multi-link operation

   - mac80211: AQL/airtime-fairness support for multicast

   - Merge Peripheral Authentication Service (PAS) / TEE support for
     ath12k (shared branch with the firmware/qcom tree)

   - New drivers:
      - mm81x for Morse Micro Long-Range S1G devices
      - nxpwifi for NXP devices (mostly forked off from mwifiex)

   - Driver changes:
      - Broadcom (brcmfmac):
         - DPP support, some Cypress part update
      - MediaTek (mt76):
         - mt7928 support
         - mt7925 NAN support
         - mt7996 AP powersave improvements
      - Qualcomm (ath12k):
         - much kernel infrastructure integration work
         - AHB platform MultiPD support
      - Realtek (rt89):
         - LED support
         - RTL8922DE support
         - dual-BT coex for RTL8922D
      - Intel:
         - new FW version support

  Bluetooth:

   - HCI: add support for Shorter Connection Interval (SCI) feature

   - af_bluetooth: add minimal context analysis annotations

   - Driver changes:
      - Intel:
         - add Bluetooth SAR revision 2 support
         - add vendor_reset PCI sysfs for PLDR
      - Mediatek:
         - add USB IDs for MT7902 and MT7922 devices
      - Realtek:
         - add USB IDs for 8761CU and 8852BE devices
      - NXP:
         - add M.2 Bluetooth device support using pwrseq

  Misc:

   - DPLL support for manual/numerical oscillator control (NCO)
     (implement in zl3073x)

   - MCTP support for MCTP over USB v1.1 (DMTF DSP0283)

   - Power-over-Ethernet: support Realtek PSE controllers

   - Remove the IBM EHEA driver

   - Remove tulip/xircom_cb driver"

* tag 'net-next-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1433 commits)
  net/mlx5e: do not HW-GRO coalesce small frames
  net: openvswitch: fix nf_connlabels leak in ovs_ct_init
  net: add missing ref_tracker_dir_exit() to alloc_netdev_mqs()
  net: openvswitch: fix flow mask use-after-free on flow deletion
  sctp: stop processing a packet once its association is deleted
  dpll: zl3073x: add PTP clock support
  dpll: zl3073x: add channel ToD, phase step and TIE operations
  dpll: zl3073x: scale poll interval proportionally to timeout
  ptp: vmclock: prevent read-only mappings from becoming writable
  ipv4: reject undersized MTUs in ip_do_fragment()
  bonding: initialize err for empty target lists
  net: dsa: initial support for MT7628 embedded switch
  net: dsa: initial MT7628 tagging driver
  net: phy: mediatek: add phy driver for MT7628 built-in Fast Ethernet PHYs
  dt-bindings: net: dsa: add MT7628 ESW
  net: pse-pd: realtek-pse-mcu: add UART transport
  net: pse-pd: realtek-pse-mcu: add I2C transport
  net: pse-pd: add Realtek PSE MCU core
  dt-bindings: net: pse-pd: add bindings for Realtek PSE MCU
  vsock: use sock_error() to consume sk_err after a failed connect
  ...
This commit is contained in:
Linus Torvalds 2026-08-20 08:16:04 -07:00
commit 91ec203513
1437 changed files with 111397 additions and 28958 deletions

View File

@ -0,0 +1,15 @@
What: /sys/bus/pci/devices/<BDF>/vendor_reset
Date: 22-Jul-2026
KernelVersion: 6.17
Contact: linux-bluetooth@vger.kernel.org
Description: This read-write attribute allows userspace to trigger a
Product Level Device Reset (PLDR) on Intel PCIe Bluetooth
controllers. Reading the attribute displays the supported
reset type. Writing integer 0 triggers PLDR. Any other
input is rejected with -EINVAL.
PLDR resets the entire on-chip platform shared between
Bluetooth and WiFi. This means any driver attached to
the WiFi device that shares hardware with this Bluetooth
device will be released, the platform will be reset, and
both the Bluetooth and WiFi devices will be re-probed.

View File

@ -14,15 +14,22 @@ description:
provides up to 5 independent DPLL channels, up to 10 differential or
single-ended inputs and 10 differential or 20 single-ended outputs.
These devices support both I2C and SPI interfaces.
The ZL3064x line card timing ICs share the ZL3073x register map
and are described with a fallback to the register equivalent ZL3073x
part.
properties:
compatible:
enum:
- microchip,zl30731
- microchip,zl30732
- microchip,zl30733
- microchip,zl30734
- microchip,zl30735
oneOf:
- enum:
- microchip,zl30731
- microchip,zl30732
- microchip,zl30733
- microchip,zl30734
- microchip,zl30735
- items:
- const: microchip,zl30643
- const: microchip,zl30733
reg:
maxItems: 1

View File

@ -0,0 +1,71 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/adi,ad3306.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ADI ADIN1140 10BASE-T1S MAC-PHY
maintainers:
- Ciprian Regus <ciprian.regus@analog.com>
description: |
The ADIN1140 (also called AD3306) is a low power single port
10BASE-T1S MAC-PHY. It integrates an Ethernet PHY with a MAC
and all the associated analog circuitry.
The device tries to implement the Open Alliance TC6 10BASE-T1x MAC-PHY
Serial Interface specification and is compliant with the
IEEE 802.3cg-2019 Ethernet standard for 10 Mbps single pair
Ethernet (SPE). The device has a 4-wire SPI interface for
communication between the MAC and host processor.
allOf:
- $ref: /schemas/net/ethernet-controller.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
oneOf:
- items:
- const: adi,adin1140
- const: adi,ad3306
- const: adi,ad3306
reg:
maxItems: 1
spi-max-frequency:
maximum: 25000000
interrupts:
maxItems: 1
description: Interrupt from the MAC-PHY for receive data available
and error conditions
required:
- compatible
- reg
- interrupts
- spi-max-frequency
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
ethernet@0 {
compatible = "adi,ad3306";
reg = <0>;
spi-max-frequency = <23000000>;
interrupt-parent = <&gpio>;
interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
local-mac-address = [ 00 11 22 33 44 55 ];
};
};

View File

@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Altera GMII to SGMII Converter
maintainers:
- Matthew Gerlach <matthew.gerlach@altera.com>
- Maxime Chevallier <maxime.chevallier@bootlin.com>
description:
This binding describes the Altera GMII to SGMII converter.

View File

@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Altera SOCFPGA SoC DWMAC controller
maintainers:
- Matthew Gerlach <matthew.gerlach@altera.com>
- Maxime Chevallier <maxime.chevallier@bootlin.com>
description:
This binding describes the Altera SOCFPGA SoC implementation of the

View File

@ -16,7 +16,9 @@ allOf:
properties:
compatible:
oneOf:
- const: rockchip,rk3568v2-canfd
- enum:
- rockchip,rk3568v2-canfd
- rockchip,rk3588-canfd
- items:
- const: rockchip,rk3568v3-canfd
- const: rockchip,rk3568v2-canfd

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/can/ti,am3517-hecc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments High End CAN Controller (HECC)
maintainers:
- Eduard Bostina <egbostina@gmail.com>
allOf:
- $ref: can-controller.yaml#
properties:
compatible:
const: ti,am3517-hecc
reg:
maxItems: 3
reg-names:
items:
- const: hecc
- const: hecc-ram
- const: mbx
interrupts:
maxItems: 1
clocks:
maxItems: 1
ti,use-hecc1int:
type: boolean
description:
If provided, configures HECC to produce all interrupts on the
HECC1INT interrupt line. By default, the HECC0INT interrupt line
will be used.
default: false
xceiver-supply:
description: Regulator that powers the CAN transceiver.
required:
- compatible
- reg
- reg-names
- interrupts
- clocks
unevaluatedProperties: false
examples:
- |
can@5c050000 {
compatible = "ti,am3517-hecc";
reg = <0x5c050000 0x80>,
<0x5c053000 0x180>,
<0x5c052000 0x200>;
reg-names = "hecc", "hecc-ram", "mbx";
interrupts = <24>;
clocks = <&hecc_ck>;
};

View File

@ -1,32 +0,0 @@
Texas Instruments High End CAN Controller (HECC)
================================================
This file provides information, what the device node
for the hecc interface contains.
Required properties:
- compatible: "ti,am3517-hecc"
- reg: addresses and lengths of the register spaces for 'hecc', 'hecc-ram'
and 'mbx'
- reg-names :"hecc", "hecc-ram", "mbx"
- interrupts: interrupt mapping for the hecc interrupts sources
- clocks: clock phandles (see clock bindings for details)
Optional properties:
- ti,use-hecc1int: if provided configures HECC to produce all interrupts
on HECC1INT interrupt line. By default HECC0INT interrupt
line will be used.
- xceiver-supply: regulator that powers the CAN transceiver
Example:
For am3517evm board:
hecc: can@5c050000 {
compatible = "ti,am3517-hecc";
reg = <0x5c050000 0x80>,
<0x5c053000 0x180>,
<0x5c052000 0x200>;
reg-names = "hecc", "hecc-ram", "mbx";
interrupts = <24>;
clocks = <&hecc_ck>;
};

View File

@ -8,7 +8,7 @@ title:
Xilinx CAN and CANFD controller
maintainers:
- Appana Durga Kedareswara rao <appana.durga.rao@xilinx.com>
- Harini T <harini.t@amd.com>
properties:
compatible:
@ -53,6 +53,9 @@ properties:
$ref: /schemas/types.yaml#/definitions/flag
description: CAN TX_OL, TX_TL and RX FIFOs have ECC support(AXI CAN)
phys:
maxItems: 1
required:
- compatible
- reg

View File

@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/dptel,dap8211r.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY
maintainers:
- Artem Shimko <a.shimko.dev@gmail.com>
description: |
The DAP8211R(I) is a Gigabit Ethernet PHY with RGMII interface,
supporting IEEE 802.3az Energy Efficient Ethernet, IEEE 1588 SyncE,
and an internal packet generator for diagnostics.
Specifications:
- 10BASE-Te, 100BASE-TX, 1000BASE-T
- RGMII with configurable TX/RX clock delays (150 ps steps, 0-2250 ps)
- IEEE 802.3az-2010 Energy Efficient Ethernet
- IEEE 1588 SyncE support
- Internal packet generator and checker for link diagnostics
allOf:
- $ref: ethernet-phy.yaml#
properties:
compatible:
const: ethernet-phy-id0008.011b
reg:
maxItems: 1
rx-internal-delay-ps:
description:
RGMII RX clock delay in picoseconds (0 to maximum).
multipleOf: 150
maximum: 2250
default: 1950
tx-internal-delay-ps:
description:
RGMII TX clock delay in picoseconds (0 to maximum).
multipleOf: 150
maximum: 2250
default: 1950
unevaluatedProperties: false
examples:
- |
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethernet-phy@1 {
compatible = "ethernet-phy-id0008.011b";
reg = <1>;
rx-internal-delay-ps = <1950>;
tx-internal-delay-ps = <1950>;
};
};

View File

@ -100,6 +100,10 @@ properties:
Built-in switch of the Airoha AN7583 SoC
const: airoha,an7583-switch
- description:
Built-in switch of the EcoNet EN7528 SoC
const: econet,en7528-switch
reg:
maxItems: 1
@ -318,6 +322,7 @@ allOf:
- mediatek,mt7988-switch
- airoha,en7581-switch
- airoha,an7583-switch
- econet,en7528-switch
then:
$ref: "#/$defs/builtin-dsa-port"
properties:

View File

@ -0,0 +1,96 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/dsa/mediatek,mt7628-esw.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mediatek MT7628 Embedded Ethernet Switch
maintainers:
- Joris Vaisvila <joey@tinyisr.com>
description:
The MT7628 SoC's built-in Ethernet Switch has five user ports and one
internally connected CPU port. The user ports are all connected to the SoC's
integrated Fast Ethernet PHYs. The switch registers are directly mapped in
the SoC's memory.
allOf:
- $ref: dsa.yaml#/$defs/ethernet-ports
properties:
compatible:
const: mediatek,mt7628-esw
reg:
maxItems: 1
resets:
items:
- description: internal switch block reset
- description: internal phy package reset
reset-names:
items:
- const: esw
- const: ephy
required:
- compatible
- reg
- resets
- reset-names
- ethernet-ports
unevaluatedProperties: false
examples:
- |
switch@10110000 {
compatible = "mediatek,mt7628-esw";
reg = <0x10110000 0x8000>;
resets = <&sysc 23>, <&sysc 24>;
reset-names = "esw", "ephy";
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
ethernet-port@0 {
reg = <0>;
phy-mode = "internal";
};
ethernet-port@1 {
reg = <1>;
phy-mode = "internal";
};
ethernet-port@2 {
reg = <2>;
phy-mode = "internal";
};
ethernet-port@3 {
reg = <3>;
phy-mode = "internal";
};
ethernet-port@4 {
reg = <4>;
phy-mode = "internal";
};
ethernet-port@6 {
reg = <6>;
phy-mode = "internal";
ethernet = <&ethernet>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View File

@ -20,16 +20,37 @@ select:
contains:
enum:
- eswin,eic7700-qos-eth
- eswin,eic7700-qos-eth-clk-inversion
required:
- compatible
allOf:
- $ref: snps,dwmac.yaml#
- if:
properties:
compatible:
contains:
const: eswin,eic7700-qos-eth
then:
properties:
tx-internal-delay-ps:
maximum: 2540
- if:
properties:
compatible:
contains:
const: eswin,eic7700-qos-eth-clk-inversion
then:
properties:
tx-internal-delay-ps:
minimum: 2000
properties:
compatible:
items:
- const: eswin,eic7700-qos-eth
- enum:
- eswin,eic7700-qos-eth
- eswin,eic7700-qos-eth-clk-inversion
- const: snps,dwmac-5.20
reg:
@ -63,10 +84,14 @@ properties:
- const: stmmaceth
rx-internal-delay-ps:
enum: [0, 200, 600, 1200, 1600, 1800, 2000, 2200, 2400]
minimum: 0
maximum: 2540
multipleOf: 20
tx-internal-delay-ps:
enum: [0, 200, 600, 1200, 1600, 1800, 2000, 2200, 2400]
minimum: 0
maximum: 4540
multipleOf: 20
eswin,hsp-sp-csr:
description:
@ -105,8 +130,6 @@ required:
- phy-mode
- resets
- reset-names
- rx-internal-delay-ps
- tx-internal-delay-ps
- eswin,hsp-sp-csr
unevaluatedProperties: false
@ -116,26 +139,51 @@ examples:
ethernet@50400000 {
compatible = "eswin,eic7700-qos-eth", "snps,dwmac-5.20";
reg = <0x50400000 0x10000>;
clocks = <&d0_clock 186>, <&d0_clock 171>, <&d0_clock 40>,
<&d0_clock 193>;
clock-names = "axi", "cfg", "stmmaceth", "tx";
interrupt-parent = <&plic>;
interrupts = <61>;
interrupt-names = "macirq";
phy-mode = "rgmii-id";
phy-handle = <&phy0>;
clocks = <&d0_clock 186>, <&d0_clock 171>, <&d0_clock 40>,
<&d0_clock 193>;
clock-names = "axi", "cfg", "stmmaceth", "tx";
resets = <&reset 95>;
reset-names = "stmmaceth";
rx-internal-delay-ps = <200>;
tx-internal-delay-ps = <200>;
eswin,hsp-sp-csr = <&hsp_sp_csr 0x100 0x108 0x118 0x114 0x11c>;
snps,axi-config = <&stmmac_axi_setup>;
phy-handle = <&phy0>;
phy-mode = "rgmii-id";
snps,aal;
snps,fixed-burst;
snps,tso;
snps,axi-config = <&stmmac_axi_setup>;
stmmac_axi_setup: stmmac-axi-config {
snps,blen = <0 0 0 0 16 8 4>;
snps,rd_osr_lmt = <2>;
snps,wr_osr_lmt = <2>;
};
};
ethernet@50410000 {
compatible = "eswin,eic7700-qos-eth-clk-inversion", "snps,dwmac-5.20";
reg = <0x50410000 0x10000>;
interrupt-parent = <&plic>;
interrupts = <70>;
interrupt-names = "macirq";
clocks = <&d0_clock 186>, <&d0_clock 171>, <&d0_clock 40>,
<&d0_clock 194>;
clock-names = "axi", "cfg", "stmmaceth", "tx";
resets = <&reset 94>;
reset-names = "stmmaceth";
eswin,hsp-sp-csr = <&hsp_sp_csr 0x200 0x208 0x218 0x214 0x21c>;
phy-handle = <&gmac1_phy0>;
phy-mode = "rgmii-id";
snps,aal;
snps,fixed-burst;
snps,tso;
snps,axi-config = <&stmmac_axi_setup_gmac1>;
stmmac_axi_setup_gmac1: stmmac-axi-config {
snps,blen = <0 0 0 0 16 8 4>;
snps,rd_osr_lmt = <2>;
snps,wr_osr_lmt = <2>;
};
};

View File

@ -234,7 +234,7 @@ examples:
<GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
};
queue-group@2d14000 {
queue-group@2d14000 {
reg = <0x0 0x2d14000 0x0 0x1000>;
interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,

View File

@ -0,0 +1,89 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/microchip,lan7800.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip LAN7800/LAN7801/LAN7850 Gigabit Ethernet controller
maintainers:
- Thangaraj Samynathan <Thangaraj.S@microchip.com>
- Rengarajan Sundararajan <Rengarajan.S@microchip.com>
description:
The LAN7800/LAN7801/LAN7850 devices are usually configured by
programming their OTP or with an external EEPROM, but some
platforms (e.g. Raspberry Pi 3 B+) have neither. The Device Tree
properties, if present, override the OTP and EEPROM.
allOf:
- $ref: /schemas/usb/usb-device.yaml#
- $ref: /schemas/net/ethernet-controller.yaml#
properties:
compatible:
enum:
- usb424,7800
- usb424,7801
- usb424,7850
reg:
maxItems: 1
description: USB port number
mdio:
$ref: /schemas/net/mdio.yaml#
unevaluatedProperties: false
patternProperties:
"^ethernet-phy(@[0-9a-f]+)?$":
$ref: /schemas/net/ethernet-phy.yaml#
unevaluatedProperties: false
type: object
properties:
microchip,led-modes:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 4
description:
Array of LED mode values for each of up to 4 LEDs.
Omitted LEDs are turned off. Allowed values are defined
in include/dt-bindings/net/microchip-lan78xx.h.
required:
- reg
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/net/microchip-lan78xx.h>
usb {
#address-cells = <1>;
#size-cells = <0>;
ethernet@1 {
compatible = "usb424,7800";
reg = <1>;
local-mac-address = [00 11 22 33 44 55];
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethernet-phy@1 {
reg = <1>;
microchip,led-modes = <
LAN78XX_LINK_1000_ACTIVITY
LAN78XX_LINK_10_100_ACTIVITY
>;
};
};
};
};
...

View File

@ -1,53 +0,0 @@
Microchip LAN78xx Gigabit Ethernet controller
The LAN78XX devices are usually configured by programming their OTP or with
an external EEPROM, but some platforms (e.g. Raspberry Pi 3 B+) have neither.
The Device Tree properties, if present, override the OTP and EEPROM.
Required properties:
- compatible: Should be one of "usb424,7800", "usb424,7801" or "usb424,7850".
The MAC address will be determined using the optional properties
defined in ethernet.txt.
Optional properties of the embedded PHY:
- microchip,led-modes: a 0..4 element vector, with each element configuring
the operating mode of an LED. Omitted LEDs are turned off. Allowed values
are defined in "include/dt-bindings/net/microchip-lan78xx.h".
Example:
/* Based on the configuration for a Raspberry Pi 3 B+ */
&usb {
usb-port@1 {
compatible = "usb424,2514";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
usb-port@1 {
compatible = "usb424,2514";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
ethernet: ethernet@1 {
compatible = "usb424,7800";
reg = <1>;
local-mac-address = [ 00 11 22 33 44 55 ];
mdio {
#address-cells = <0x1>;
#size-cells = <0x0>;
eth_phy: ethernet-phy@1 {
reg = <1>;
microchip,led-modes = <
LAN78XX_LINK_1000_ACTIVITY
LAN78XX_LINK_10_100_ACTIVITY
>;
};
};
};
};
};
};

View File

@ -35,7 +35,7 @@ properties:
- usb424,9906 # SMSC9505A USB Ethernet Device (HAL)
- usb424,9907 # SMSC9500 USB Ethernet Device (Alternate ID)
- usb424,9908 # SMSC9500A USB Ethernet Device (Alternate ID)
- usb424,9909 # SMSC9512/9514 USB Hub & Ethernet Device ID)
- usb424,9909 # SMSC9512/9514 USB Hub & Ethernet Device
- usb424,9e00 # SMSC9500A USB Ethernet Device
- usb424,9e01 # SMSC9505A USB Ethernet Device
- usb424,9e08 # SMSC LAN89530 USB Ethernet Device

View File

@ -140,8 +140,8 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h>
switch: ethernet-switch@e0000000 {
compatible = "microchip,lan966x-switch";
reg = <0xe0000000 0x0100000>,
<0xe2000000 0x0800000>;
reg = <0xe0000000 0x0100000>,
<0xe2000000 0x0800000>;
reg-names = "cpu", "gcb";
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "xtr";

View File

@ -210,9 +210,9 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h>
switch: switch@600000000 {
compatible = "microchip,sparx5-switch";
reg = <0 0x401000>,
<0x10004000 0x7fc000>,
<0x11010000 0xaf0000>;
reg = <0 0x401000>,
<0x10004000 0x7fc000>,
<0x11010000 0xaf0000>;
reg-names = "cpu", "devices", "gcb";
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "xtr";

View File

@ -0,0 +1,182 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/pse-pd/realtek,pse-mcu-gen1.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek PSE MCU
maintainers:
- Jonas Jelonek <jelonek.jonas@gmail.com>
description: |
A microcontroller (MCU) that manages the PSE (Power Sourcing Equipment)
hardware on a range of managed PoE switches. The host CPU talks only to
this MCU - over I2C/SMBus or UART - using a small message-based protocol;
the PSE silicon it drives sits behind the MCU and is never accessed
directly. For example, on the Zyxel GS1900-10HP the SoC reaches the MCU
over UART, and the MCU manages the on-board PSE chip.
This binding describes the MCU together with its Realtek firmware: the
firmware and its host protocol, which are stable across boards. The
microcontroller silicon is a general-purpose part that varies, and the
PSE silicon behind the MCU (Realtek RTL823x/RTL8239* or Broadcom
BCM59xxx) is reported by the MCU and detected at runtime - neither is
named here.
Two protocol generations exist, both Realtek's:
gen1 older boards, where the MCU fronts Broadcom PSE silicon
gen2 the altered protocol used with Realtek's own PSE silicon
On an I2C attachment the framing the MCU firmware expects is part of the
compatible: '-smbus' (reads carry a leading command byte and a repeated
start) or '-i2c' (bare block writes and reads). A UART attachment carries
no framing suffix; the transport is given by the parent 'serial' node.
Each board additionally carries a device-specific compatible that falls
back to one of the protocol compatibles above. Drivers bind on the
protocol compatible; the device-specific string identifies the board and
reserves a place for a future per-board quirk without having to retrofit
device trees already in the field.
properties:
compatible:
oneOf:
# UART
- items:
- enum:
- zyxel,gs1900-10hp-a1-pse
- const: realtek,pse-mcu-gen1
# I2C, SMBus framing
- items:
- enum:
- zyxel,gs1920-24hp-v2-pse
- const: realtek,pse-mcu-gen1-smbus
# UART
- items:
- enum:
- zyxel,gs1900-10hp-b1-pse
- zyxel,xmg1915-10ep-pse
- const: realtek,pse-mcu-gen2
# I2C, SMBus framing
- items:
- enum:
- zyxel,xs1930-12hp-pse
- const: realtek,pse-mcu-gen2-smbus
# I2C, raw framing
- items:
- enum:
- linksys,lgs328mpc-v2-pse
- const: realtek,pse-mcu-gen2-i2c
reg:
maxItems: 1
reset-gpios:
description: Reset line of the MCU.
maxItems: 1
disable-ports-gpios:
description:
Hardware gate that forces all ports into admin-disabled state while
asserted.
maxItems: 1
required:
- compatible
allOf:
- $ref: pse-controller.yaml#
# A '-smbus'/'-i2c' compatible is an I2C attachment: it has 'reg' and
# cannot carry serial bus properties. A bare gen compatible is a UART
# attachment: no 'reg', the transport comes from the parent serial node.
- if:
properties:
compatible:
contains:
enum:
- realtek,pse-mcu-gen1-smbus
- realtek,pse-mcu-gen2-smbus
- realtek,pse-mcu-gen2-i2c
then:
required:
- reg
properties:
current-speed: false
max-speed: false
else:
allOf:
- $ref: /schemas/serial/serial-peripheral-props.yaml#
properties:
reg: false
unevaluatedProperties: false
examples:
# SMBus-framed I2C attachment
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
ethernet-pse@20 {
compatible = "zyxel,xs1930-12hp-pse", "realtek,pse-mcu-gen2-smbus";
reg = <0x20>;
pse-pis {
#address-cells = <1>;
#size-cells = <0>;
pse-pi@0 {
reg = <0>;
#pse-cells = <0>;
};
};
};
};
# Raw-I2C-framed attachment
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
ethernet-pse@20 {
compatible = "linksys,lgs328mpc-v2-pse", "realtek,pse-mcu-gen2-i2c";
reg = <0x20>;
pse-pis {
#address-cells = <1>;
#size-cells = <0>;
pse-pi@0 {
reg = <0>;
#pse-cells = <0>;
};
};
};
};
# UART attachment
- |
serial {
ethernet-pse {
compatible = "zyxel,gs1900-10hp-a1-pse", "realtek,pse-mcu-gen1";
current-speed = <19200>;
pse-pis {
#address-cells = <1>;
#size-cells = <0>;
pse-pi@0 {
reg = <0>;
#pse-cells = <0>;
};
};
};
};

View File

@ -254,10 +254,10 @@ examples:
ethernet@15c30000 {
compatible = "renesas,r9a09g057-gbeth", "renesas,rzv2h-gbeth", "snps,dwmac-5.20";
reg = <0x15c30000 0x10000>;
clocks = <&cpg CPG_MOD 0xbd>, <&cpg CPG_MOD 0xbc>,
<&ptp_clock>, <&cpg CPG_MOD 0xb8>,
<&cpg CPG_MOD 0xb9>, <&cpg CPG_MOD 0xba>,
<&cpg CPG_MOD 0xbb>;
clocks = <&cpg CPG_MOD 0xbd>, <&cpg CPG_MOD 0xbc>,
<&ptp_clock>, <&cpg CPG_MOD 0xb8>,
<&cpg CPG_MOD 0xb9>, <&cpg CPG_MOD 0xba>,
<&cpg CPG_MOD 0xbb>;
clock-names = "stmmaceth", "pclk", "ptp_ref",
"tx", "rx", "tx-180", "rx-180";
resets = <&cpg 0xb0>;

View File

@ -461,6 +461,8 @@ patternProperties:
description: Dongwoon Anatech
"^dptechnics,.*":
description: DPTechnics
"^dptel,.*":
description: Guangdong Dapu Telecom Co., Ltd.
"^dragino,.*":
description: Dragino Technology Co., Limited
"^dream,.*":

View File

@ -91,6 +91,13 @@ following pin states:
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
a valid input for automatic selection algorithm
Pins that have the ``DPLL_PIN_CAPABILITIES_STATE_CONNECTED_OVERRIDE``
capability can additionally be set to ``DPLL_PIN_STATE_CONNECTED`` in
automatic mode, overriding the active input selection. This is useful
for automatic-only DPLL devices where mode cannot be switched to manual.
When such a pin is disconnected, the device returns to automatic input
selection.
The actual hardware status of a pin is reported via the operational
state (``DPLL_A_PIN_OPERSTATE``) attribute nested under the parent
device:
@ -109,8 +116,9 @@ Shared pins
A single pin object can be attached to multiple dpll devices.
Then there are two groups of configuration knobs:
1) Set on a pin - the configuration affects all dpll devices pin is
registered to (i.e., ``DPLL_A_PIN_FREQUENCY``),
1) Set on a pin - the configuration is a property of the pin itself and
applies to all dpll devices the pin is registered with
(i.e., ``DPLL_A_PIN_FREQUENCY``),
2) Set on a pin-dpll tuple - the configuration affects only selected
dpll device (i.e., ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE``,
``DPLL_A_PIN_DIRECTION``).
@ -500,9 +508,9 @@ as well as parameter being configured (``DPLL_A_MODE``).
``DPLL_CMD_PIN_SET`` - to target a pin user must provide a
``DPLL_A_PIN_ID``, which is unique identifier of a pin in the system.
Also configured pin parameters must be added.
If ``DPLL_A_PIN_FREQUENCY`` is configured, this affects all the dpll
devices that are connected with the pin, that is why frequency attribute
shall not be enclosed in ``DPLL_A_PIN_PARENT_DEVICE``.
If ``DPLL_A_PIN_FREQUENCY`` is configured, it is a property of the pin
itself and applies to all dpll devices the pin is registered with, so the
frequency attribute shall not be enclosed in ``DPLL_A_PIN_PARENT_DEVICE``.
Other attributes: ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE`` or
``DPLL_A_PIN_DIRECTION`` must be enclosed in
``DPLL_A_PIN_PARENT_DEVICE`` as their configuration relates to only one

View File

@ -895,6 +895,16 @@ attribute-sets:
resource-dump response. Bit 0 (dev) selects device-level
resources; bit 1 (port) selects port-level resources.
When absent all classes are returned.
-
name: parent-dev
type: nest
nested-attributes: dl-parent-dev
doc: |
Identifies the devlink instance which owns the parent rate node.
Used with rate-set and rate-new to parent a rate object to a node on
a different devlink instance, enabling cross-device rate scheduling.
When absent, the parent node is resolved on the same instance.
-
name: dl-dev-stats
subset-of: devlink
@ -1317,6 +1327,16 @@ attribute-sets:
Specifies the bandwidth share assigned to the Traffic Class.
The bandwidth for the traffic class is determined
in proportion to the sum of the shares of all configured classes.
-
name: dl-parent-dev
subset-of: devlink
attributes:
-
name: bus-name
-
name: dev-name
-
name: index
operations:
enum-model: directional
@ -2289,8 +2309,8 @@ operations:
dont-validate: [strict]
flags: [admin-perm]
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
pre: devlink-nl-pre-doit-parent-dev-optional
post: devlink-nl-post-doit-parent-dev-optional
request:
attributes:
- bus-name
@ -2303,6 +2323,7 @@ operations:
- rate-tx-weight
- rate-parent-node-name
- rate-tc-bws
- parent-dev
-
name: rate-new
@ -2311,8 +2332,8 @@ operations:
dont-validate: [strict]
flags: [admin-perm]
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
pre: devlink-nl-pre-doit-parent-dev-optional
post: devlink-nl-post-doit-parent-dev-optional
request:
attributes:
- bus-name
@ -2325,6 +2346,7 @@ operations:
- rate-tx-weight
- rate-parent-node-name
- rate-tc-bws
- parent-dev
-
name: rate-del

View File

@ -165,6 +165,13 @@ definitions:
-
name: gnss
doc: GNSS recovered clock
-
name: int-nco
doc: |
Device internal numerically controlled oscillator.
When connected as a DPLL input, the DPLL enters NCO mode
where the output frequency is adjusted by the host via
the PTP clock interface.
render-max: true
-
type: enum
@ -252,6 +259,12 @@ definitions:
-
name: state-can-change
doc: pin state can be changed
-
name: state-connected-override
doc: |
pin state can be set to connected regardless of current
DPLL device mode, overriding the active input selection.
Requires state-can-change to be set as well.
-
type: const
name: phase-offset-divider
@ -456,6 +469,9 @@ attribute-sets:
offset on the media associated with the pin. Inside
the pin-parent-device nest it represents the frequency
offset between the pin and its parent DPLL device.
For pins of type PIN_TYPE_INT_NCO this represents
the DPLL's current output frequency offset from its
nominal frequency.
Value is in PPM (parts per million).
This is a lower-precision version of
fractional-frequency-offset-ppt.
@ -502,6 +518,9 @@ attribute-sets:
offset on the media associated with the pin. Inside
the pin-parent-device nest it represents the frequency
offset between the pin and its parent DPLL device.
For pins of type PIN_TYPE_INT_NCO this represents
the DPLL's current output frequency offset from its
nominal frequency.
Value is in PPT (parts per trillion, 10^-12).
This is a higher-precision version of
fractional-frequency-offset.

View File

@ -6,6 +6,14 @@ doc: >-
netdev configuration over generic netlink.
definitions:
-
type: const
name: page-size
# Dummy value, codegen needs a number. The real value comes from
# the PAGE_SIZE macro in the header below.
value: 0
header: asm/page.h
scope: kernel
-
type: flags
name: xdp-act
@ -598,6 +606,16 @@ attribute-sets:
type: u32
checks:
min: 1
-
name: rx-page-size
doc: |
Size in bytes of each device page the NIC writes into from the bound
dmabuf. Must be a power of two and >= PAGE_SIZE; defaults to
PAGE_SIZE.
type: u32
checks:
min: page-size
max: u32-max
operations:
list:
@ -812,6 +830,7 @@ operations:
- ifindex
- fd
- queues
- rx-page-size
reply:
attributes:
- id

View File

@ -1139,10 +1139,10 @@ attribute-sets:
type: binary
-
name: fils-discovery
type: binary # TOOD: nest
type: binary # TODO: nest
-
name: unsol-bcast-probe-resp
type: binary # TOOD: nest
type: binary # TODO: nest
-
name: s1g-capability
type: binary

View File

@ -123,6 +123,9 @@ attribute-sets:
-
name: proto
type: u8
-
name: mc-users
type: u32
operations:
@ -176,6 +179,7 @@ operations:
value: 58
attributes: &mcaddr-attrs
- multicast
- mc-users
- cacheinfo
dump:
request:

View File

@ -928,6 +928,11 @@ attribute-sets:
name: vfinfo-list
type: nest
nested-attributes: vfinfo-list-attrs
doc: |
Per-VF details. The list holds at most 256 VFs, or 128 when
statistics are included, because it is one attribute and has to fit
in a u16 length. A device with more VFs than that reports a
truncated list; num-vf still carries the real count.
-
name: stats64
type: binary

View File

@ -78,6 +78,27 @@ definitions:
-
name: rta-used
type: u32
-
name: del-reason
type: enum
name-prefix: rt-del-reason-
enum-name: rt-del-reason
doc: |
Why the kernel deleted a route. New causes may be appended. The
value space is family-agnostic, a value is never reinterpreted
per address family.
entries:
-
name: unspec
doc: The deletion path does not record a cause.
-
name: expired
doc: The RTF_EXPIRES lifetime ran out and the route was garbage
collected.
-
name: ra-withdrawn
doc: A Router Advertisement withdrew the route with a zero
lifetime.
attribute-sets:
-
@ -185,6 +206,18 @@ attribute-sets:
type: u32
byte-order: big-endian
display-hint: hex
-
name: del-reason
type: u32
enum: del-reason
doc: |
Emitted only on RTM_DELROUTE notifications, and only when the
deletion path records a cause. An absent attribute means
either an older kernel or a deletion path that does not
record its cause, consumers must treat absent and unspec
identically. Currently only IPv6 deletion paths record a
cause. The attribute is notification-only, the kernel rejects
it in requests.
-
name: metrics
name-prefix: rtax-
@ -299,6 +332,7 @@ operations:
- dport
- nh-id
- flowlabel
- del-reason
dump:
request:
value: 26
@ -313,7 +347,35 @@ operations:
do:
request:
value: 24
attributes: *all-route-attrs
attributes: &route-req-attrs
- dst
- src
- iif
- oif
- gateway
- priority
- prefsrc
- metrics
- multipath
- flow
- cacheinfo
- table
- mark
- mfc-stats
- via
- newdst
- pref
- encap-type
- encap
- expires
- pad
- uid
- ttl-propagate
- ip-proto
- sport
- dport
- nh-id
- flowlabel
-
name: delroute
doc: Delete an existing route
@ -321,4 +383,23 @@ operations:
do:
request:
value: 25
attributes: *all-route-attrs
attributes: *route-req-attrs
-
name: newroute-ntf
doc: Notification about a created route.
value: 24
notify: getroute
-
name: delroute-ntf
doc: Notification about a deleted route.
value: 25
notify: getroute
mcast-groups:
list:
-
name: rtnlgrp-ipv4-route
value: 7
-
name: rtnlgrp-ipv6-route
value: 11

View File

@ -93,9 +93,9 @@
<ellipse cx="144.827" cy="159.143" rx="10.8866" ry="4.39308"/>
<ellipse cx="59.4364" cy="142.823" rx="7.36455" ry="4.39308"/>
<ellipse cx="144.827" cy="129.196" rx="10.8866" ry="4.39308"/>
<ellipse cx="143.077" cy="180.53" rx="10.8866" ry="4.39308"/>
</g>
<ellipse cx="110.386" cy="180.53" rx="10.8866" ry="4.39308" fill="#ffcb35" stroke="#000" stroke-linecap="square" stroke-width=".499999"/>
<ellipse cx="110.386" cy="180.53" rx="10.8866" ry="4.39308" fill="#28a4ff" stroke="#000" stroke-linecap="square" stroke-width=".499999"/>
<ellipse cx="143.077" cy="180.53" rx="10.8866" ry="4.39308" fill="#ffcb35" stroke="#000" stroke-linecap="square" stroke-width=".499999"/>
<text x="110.90907" y="179.42688" font-size="3.175px" xml:space="preserve"><tspan x="110.90907" y="179.42688" dy="0.60000002" text-align="center" text-anchor="middle">Accessible</tspan><tspan x="110.90907" y="183.39563"><tspan font-size="3.175px" text-align="center" text-anchor="middle">for S</tspan>W</tspan></text>
<text x="143.5869" y="179.52795" xml:space="preserve"><tspan x="143.5869" y="179.52795" dy="1 0 0 0 0 0" font-family="sans-serif" font-size="2.82222px" text-align="center" text-anchor="middle" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal">Inaccessible</tspan><tspan x="143.5869" y="183.36786" font-size="3.175px"><tspan font-size="3.175px" text-align="center" text-anchor="middle">for S</tspan>W</tspan></text>
<g font-size="3.175px">

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -102,6 +102,95 @@ currently in use, and that bank will used for the next boot::
# devlink dev flash pci/0000:b5:00.0 \
file pensando/dsc_fw_1.63.0-22.tar
Firmware Management (PLDM)
==========================
Firmware that supports PLDM can be updated using the devlink flash command
with a PLDM firmware package. The entire package can be updated at once::
# devlink dev flash pci/0000:b5:00.0 file firmware.pldmfw
Individual components can also be updated by specifying the component name::
# devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.cpld
Per-component update uses driver-defined component names (fw, fw.cpld,
etc.). Not all components support per-component update -
devlink will reject the request if the specified component cannot
be updated.
Gold (recovery) components can be updated by specifying the base component
name (e.g., ``fw`` for ``fw.gold``) with a goldfw package file when the
device supports per-component update. The ``.gold`` suffix in devlink info
output indicates the gold slot version, not a flash target.
Info versions (PLDM)
====================
Firmware that supports PLDM reports component versions using driver-defined
names. The driver reports the following component versions:
.. list-table:: devlink info versions for PLDM-capable firmware
:widths: 5 5 90
* - Name
- Type
- Description
* - ``fw``
- running, stored
- Main firmware
* - ``fw.gold``
- stored
- Gold (recovery) firmware
* - ``fw.bootloader``
- running, stored
- Boot loader
* - ``fw.cpld``
- running, stored
- CPLD
* - ``fw.secure``
- running, stored
- Secure boot firmware
* - ``fw.fpga``
- running, stored
- FPGA configuration
* - ``fw.suc``
- running, stored
- System Unit Controller firmware
* - ``fw.suc.bootloader``
- running, stored
- System Unit Controller bootloader
* - ``fw.uboot``
- running, stored
- U-Boot bootloader
* - ``asic.id``
- fixed
- The ASIC type for this device
* - ``asic.rev``
- fixed
- The revision of the ASIC for this device
Example output::
$ devlink dev info pci/0000:00:05.0
pci/0000:00:05.0:
driver pds_core
serial_number FLM18420073
versions:
fixed:
asic.id 0x0
asic.rev 0x0
running:
fw.bootloader 1.2.3
fw 1.3.0
fw.cpld 3.18
stored:
fw.bootloader 1.2.3
fw.gold 1.2.0
fw 1.3.0
fw.cpld 3.18
Health Reporters
================

View File

@ -35,6 +35,7 @@ Contents:
intel/idpf
intel/igb
intel/igbvf
intel/ixd
intel/ixgbe
intel/ixgbevf
intel/i40e

View File

@ -0,0 +1,39 @@
.. SPDX-License-Identifier: GPL-2.0+
==========================================================================
iXD Linux* Base Driver for the Intel(R) Control Plane Function
==========================================================================
Intel iXD Linux driver.
Copyright(C) 2025 Intel Corporation.
.. contents::
For questions related to hardware requirements, refer to the documentation
supplied with your Intel adapter. All hardware requirements listed apply to use
with Linux.
Identifying Your Adapter
========================
For information on how to identify your adapter, and for the latest Intel
network drivers, refer to the Intel Support website:
http://www.intel.com/support
Support
=======
For general information, go to the Intel support website at:
http://www.intel.com/support/
If an issue is identified with the released source code on a supported kernel
with a supported adapter, email the specific information related to the issue
to intel-wired-lan@lists.osuosl.org.
Trademarks
==========
Intel is a trademark or registered trademark of Intel Corporation or its
subsidiaries in the United States and/or other countries.
* Other names and brands may be claimed as the property of others.

View File

@ -165,3 +165,9 @@ own name.
- u32
- Controls the maximum number of MAC address filters that can be assigned
to a Virtual Function (VF).
* - ``max_sfs``
- u32
- The maximum number of subfunctions which can be created on the device.
Modifying this parameter may require a device restart and PCI bus
rescanning because the BAR layout may change. A value of 0 disables
subfunction creation.

View File

@ -107,6 +107,15 @@ doesn't have the eswitch. Local controller (identified by controller number = 0)
has the eswitch. The Devlink instance on the local controller has eswitch
devlink ports for both the controllers.
A non-zero controller number may also be used for ports that are not external.
For example, a SmartNIC may have additional local PCI physical functions
that are managed by the eswitch but are not on an external host. These
ports use a non-zero controller number to distinguish them from the eswitch
manager's own functions, while the external flag remains unset.
The ``phys_port_name`` includes the controller prefix (``c<controller_num>``)
whenever the controller number is non-zero, regardless of the external flag.
Function configuration
======================
@ -420,6 +429,8 @@ API allows to configure following rate object's parameters:
Parent node name. Parent node rate limits are considered as additional limits
to all node children limits. ``tx_max`` is an upper limit for children.
``tx_share`` is a total bandwidth distributed among children.
If the device supports cross-function scheduling, the parent can be from a
different function of the same underlying device.
``tc_bw``
Allow users to set the bandwidth allocation per traffic class on rate

View File

@ -31,10 +31,10 @@ sure to respect following rules:
- Lock ordering should be maintained. If driver needs to take instance
lock of both nested and parent instances at the same time, devlink
instance lock of the parent instance should be taken first, only then
instance lock of the nested instance could be taken.
- Driver should use object-specific helpers to setup the nested relationship
before registering the nested devlink instance:
instance lock of the nested instance should be taken first, only then
instance lock of the parent instance could be taken.
- Driver should use object-specific helpers to setup the
nested relationship:
- ``devl_nested_devlink_set()`` - called to setup devlink -> nested
devlink relationship (could be used for multiple nested instances).
@ -87,6 +87,7 @@ parameters, info versions, and other features it supports.
ice
ionic
iosm
ixd
ixgbe
kvaser_pciefd
kvaser_usb

View File

@ -0,0 +1,30 @@
.. SPDX-License-Identifier: GPL-2.0
===================
ixd devlink support
===================
This document describes the devlink features implemented by the ``ixd``
device driver.
Info versions
=============
The ``ixd`` driver reports the following versions
.. list-table:: devlink info versions implemented
:widths: 5 5 5 90
* - Name
- Type
- Example
- Description
* - ``device.type``
- fixed
- MEV
- The hardware type for this device
* - ``fw.mgmt.api``
- running
- 2.0
- 2-digit version number (major.minor) of the communication channel
(virtchnl) used by the device.

View File

@ -45,8 +45,13 @@ Parameters
- The range is between 1 and a device-specific max.
- Applies to each physical function (PF) independently, if the device
supports it. Otherwise, it applies symmetrically to all PFs.
* - ``max_sfs``
- permanent
- The range is between 0 and a device-specific max.
- Applies to each physical function (PF) independently.
Note: permanent parameters such as ``enable_sriov`` and ``total_vfs`` require FW reset to take effect
Note: permanent parameters such as ``enable_sriov``, ``total_vfs`` and ``max_sfs``
require FW reset to take effect
.. code-block:: bash
@ -419,3 +424,36 @@ User commands examples:
.. note::
This command can run over all interfaces such as PF/VF and representor ports.
Rates
=====
mlx5 devices can limit transmission of individual VFs or a group of them via
the devlink-rate API in switchdev mode.
User commands examples:
- Print the existing rates::
$ devlink port function rate show
- Set a max tx limit on traffic from VF0::
$ devlink port function rate set pci/0000:82:00.0/1 tx_max 10Gbit
- Create a rate group with a max tx limit and add two VFs to it::
$ devlink port function rate add pci/0000:82:00.0/group1 tx_max 10Gbit
$ devlink port function rate set pci/0000:82:00.0/1 parent group1
$ devlink port function rate set pci/0000:82:00.0/2 parent group1
- Same scenario, with a min guarantee of 20% of the bandwidth for the first VF::
$ devlink port function rate add pci/0000:82:00.0/group1 tx_max 10Gbit
$ devlink port function rate set pci/0000:82:00.0/1 parent group1 tx_share 2Gbit
$ devlink port function rate set pci/0000:82:00.0/2 parent group1
- Cross-device scheduling::
$ devlink port function rate add pci/0000:82:00.0/group1 tx_max 10Gbit
$ devlink port function rate set pci/0000:82:00.1/32769 parent pci/0000:82:00.0/group1

View File

@ -22,6 +22,7 @@ struct_mutex ra_mutex
struct_fib_rules_ops* rules_ops
struct_fib_table fib_main
struct_fib_table fib_default
spinlock_t fib_table_hash_lock
unsigned_int fib_rules_require_fldissect
bool fib_has_custom_rules
bool fib_has_custom_local_routes

View File

@ -421,10 +421,17 @@ running under the lock:
* ``NETDEV_CHANGENAME``
* ``NETDEV_REGISTER``
* ``NETDEV_UP``
* ``NETDEV_DOWN``
* ``NETDEV_GOING_DOWN``
The following notifiers are running without the lock:
* ``NETDEV_UNREGISTER``
Many SW devices (uppers) catch their lower's ``NETDEV_UNREGISTER``
events and may interact with them via ``dev_*()`` handlers, which take
the instance lock. Until we convert these devices to ``netif_*()`` variants,
``NETDEV_UNREGISTER`` stays unlocked.
There are no clear expectations for the remaining notifiers. Notifiers not on
the list may run with or without the instance lock, potentially even invoking
the same notifier type with and without the lock from different code paths.

View File

@ -454,7 +454,8 @@ Device drivers API
The include/linux/oa_tc6.h defines the following functions:
.. c:function:: struct oa_tc6 *oa_tc6_init(struct spi_device *spi, \
struct net_device *netdev)
struct net_device *netdev, \
struct oa_tc6_quirks *quirks)
Initialize OA TC6 lib.

View File

@ -206,6 +206,38 @@ enable is true we enable it, otherwise we disable it::
return ioctl(fd, TUNSETQUEUE, (void *)&ifr);
}
3.4 qdisc backpressure
----------------------
IFF_BACKPRESSURE can be set to enable qdisc backpressure. Without it, TX
drops occur when the internal ring buffer is full, so any attached qdisc
is effectively bypassed and applications only learn about congestion
through those drops.
With it, the kernel stops the queue instead, letting the qdisc hold and
schedule packets, so its AQM, shaping and fairness actually apply. This
helps protocols like TCP, which cut throughput in reaction to packet
drops. With IFF_BACKPRESSURE, drops then only occur as a rare race.
Backpressure requires a qdisc to be attached and has no effect with
noqueue.
The flag is a property of the TUN/TAP device rather than of the file
descriptor it was set on, so it applies to all queues of the device,
regardless of which process opened which queue.
The flag can only be changed while the device has at most one queue. On a
multiqueue device that already has a second queue attached or detached, a
later TUNSETIFF succeeds but leaves the flag as it is. All the other
TUNSETIFF flags behave the same way.
The txqueuelen can be reduced alongside this flag to further shift
buffering into the qdisc and reduce bufferbloat, at a possible
performance cost.
When running multiple network streams in parallel through a single
TUN/TAP queue, the flag may reduce performance due to the extra overhead
of the backpressure mechanism.
Universal TUN/TAP device driver Frequently Asked Question
=========================================================

View File

@ -203,6 +203,22 @@ For RFC postings specifically, if nobody responded in a week - reviewers
either missed the posting or have no strong opinions. If the code is ready,
repost as a PATCH.
There are 2 services actively providing LLM-generated review on posted patches:
- https://sashiko.dev/
- https://netdev-ai.bots.linux.dev/sashiko/
both use the Sashiko infrastructure on top of different models. Reviews are
available after 24h. Patch authors are expected to proactively look into the
AI-generated reviews and handle such feedback as any other kind of review:
either debate it or address it. In both cases a reply on the mailing list is
expected.
Authors are strongly encouraged to run LLM reviews on the posted patches in
advance of the actual post. Large series triggering a significant amount of
AI-generated feedback will likely get little attention from maintainers and
reviewers.
Emails saying just "ping" or "bump" are considered rude. If you can't figure
out the status of the patch from patchwork or where the discussion has
landed - describe your best guess and ask if it's correct. For example::
@ -210,6 +226,10 @@ landed - describe your best guess and ask if it's correct. For example::
I don't understand what the next steps are. Person X seems to be unhappy
with A, should I do B and repost the patches?
Don't reach out to maintainers or reviewers via private email and/or other
communications channels: all the discussion must remain public, and
requesting special attention is unfair towards the community, at best.
.. _Changes requested:
Changes requested

View File

@ -1900,6 +1900,21 @@ S: Supported
W: https://ez.analog.com/linux-software-drivers
F: drivers/dma/dma-axi-dmac.c
ANALOG DEVICES INC ETHERNET DRIVERS
M: Ciprian Regus <ciprian.regus@analog.com>
L: netdev@vger.kernel.org
S: Maintained
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/net/adi,ad3306.yaml
F: drivers/net/ethernet/adi/adin1140.c
ANALOG DEVICES INC ETHERNET PHY DRIVERS
M: Ciprian Regus <ciprian.regus@analog.com>
L: netdev@vger.kernel.org
S: Maintained
W: https://ez.analog.com/linux-software-drivers
F: drivers/net/phy/adin1140-phy.c
ANALOG DEVICES INC IIO DRIVERS
M: Nuno Sá <nuno.sa@analog.com>
M: Michael Hennerich <Michael.Hennerich@analog.com>
@ -3585,15 +3600,11 @@ M: Dinh Nguyen <dinguyen@kernel.org>
S: Maintained
F: drivers/clk/socfpga/
ARM/SOCFPGA DWMAC GLUE LAYER BINDINGS
M: Matthew Gerlach <matthew.gerlach@altera.com>
S: Maintained
F: Documentation/devicetree/bindings/net/altr,gmii-to-sgmii-2.0.yaml
F: Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml
ARM/SOCFPGA DWMAC GLUE LAYER
M: Maxime Chevallier <maxime.chevallier@bootlin.com>
S: Maintained
F: Documentation/devicetree/bindings/net/altr,gmii-to-sgmii-2.0.yaml
F: Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml
F: drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
ARM/SOCFPGA EDAC BINDINGS
@ -4253,7 +4264,6 @@ W: http://linux-atm.sourceforge.net
F: drivers/atm/
F: drivers/usb/atm/
F: include/linux/atm*
F: include/linux/sonet.h
F: include/uapi/linux/atm*
F: include/uapi/linux/sonet.h
F: net/atm/
@ -4703,6 +4713,7 @@ S: Supported
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-next.git
F: Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
F: Documentation/devicetree/bindings/net/bluetooth/
F: drivers/bluetooth/
@ -7848,6 +7859,7 @@ DPLL SUBSYSTEM
M: Vadim Fedorenko <vadim.fedorenko@linux.dev>
M: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
M: Jiri Pirko <jiri@resnulli.us>
R: Ivan Vecera <ivecera@redhat.com>
L: netdev@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/dpll/dpll-device.yaml
@ -9497,11 +9509,6 @@ L: linux-fbdev@vger.kernel.org
S: Maintained
F: drivers/video/fbdev/efifb.c
EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER
L: netdev@vger.kernel.org
S: Orphan
F: drivers/net/ethernet/ibm/ehea/
ELM327 CAN NETWORK DRIVER
M: Max Staudt <max@enpas.org>
L: linux-can@vger.kernel.org
@ -13043,8 +13050,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue.git
F: Documentation/networking/device_drivers/ethernet/intel/
F: drivers/net/ethernet/intel/
F: drivers/net/ethernet/intel/*/
F: include/linux/avf/virtchnl.h
F: include/linux/net/intel/*/
F: include/linux/net/intel/
INTEL ETHERNET PROTOCOL DRIVER FOR RDMA
M: Tatyana Nikolova <tatyana.e.nikolova@intel.com>
@ -18228,6 +18234,14 @@ F: drivers/regulator/mpq7920.c
F: drivers/regulator/mpq7920.h
F: include/linux/mfd/mp2629.h
MORSE MICRO MM81X WIRELESS DRIVER
M: Lachlan Hodges <lachlan.hodges@morsemicro.com>
M: Dan Callaghan <dan.callaghan@morsemicro.com>
R: Arien Judge <arien.judge@morsemicro.com>
L: linux-wireless@vger.kernel.org
S: Supported
F: drivers/net/wireless/morsemicro/
MOST(R) TECHNOLOGY DRIVER
M: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
M: Christian Gromm <christian.gromm@microchip.com>
@ -19139,6 +19153,7 @@ F: include/net/net_failover.h
NFC SUBSYSTEM
M: David Heidelberg <david@ixit.cz>
L: oe-linux-nfc@lists.linux.dev
C: https://matrix.to/#/#linux-nfc:ixit.cz
S: Maintained
T: git https://codeberg.org/linux-nfc/linux.git
F: Documentation/devicetree/bindings/net/nfc/
@ -19585,6 +19600,13 @@ S: Maintained
F: Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
F: drivers/ptp/ptp_netc.c
NXP NXPWIFI WIRELESS DRIVER
M: Jeff Chen <jeff.chen_1@nxp.com>
R: Francesco Dolcini <francesco@dolcini.it>
L: linux-wireless@vger.kernel.org
S: Maintained
F: drivers/net/wireless/nxp/
NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
M: Woodrow Douglass <wdouglass@carnegierobotics.com>
S: Maintained
@ -22130,6 +22152,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git
F: Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml
F: drivers/net/wireless/ath/ath9k/
QUALCOMM ATHEROS QCA8K DSA SWITCH DRIVER
M: Christian Marangi <ansuelsmth@gmail.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/dsa/qca8k.yaml
F: drivers/net/dsa/qca/qca8k*
F: net/dsa/tag_qca.c
QUALCOMM ATHEROS QCA7K ETHERNET DRIVER
M: Stefan Wahren <wahrenst@gmx.net>
L: netdev@vger.kernel.org
@ -22782,6 +22812,13 @@ S: Maintained
F: Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
F: drivers/watchdog/realtek_otto_wdt.c
REALTEK PSE MCU DRIVER
M: Jonas Jelonek <jelonek.jonas@gmail.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/pse-pd/realtek,pse-mcu-gen1.yaml
F: drivers/net/pse-pd/realtek-pse-mcu*
REALTEK RTL83xx SMI DSA ROUTER CHIPS
M: Linus Walleij <linusw@kernel.org>
M: Luiz Angelo Daros de Luca <luizluca@gmail.com>
@ -27302,6 +27339,7 @@ F: tools/testing/selftests/timers/
TIPC NETWORK LAYER
M: Jon Maloy <jmaloy@redhat.com>
M: Tung Quang Nguyen <tung.quang.nguyen@est.tech>
L: netdev@vger.kernel.org (core kernel code)
L: tipc-discussion@lists.sourceforge.net (user apps, general discussion)
S: Maintained
@ -28006,7 +28044,7 @@ M: Thangaraj Samynathan <Thangaraj.S@microchip.com>
M: UNGLinuxDriver@microchip.com
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/microchip,lan78xx.txt
F: Documentation/devicetree/bindings/net/microchip,lan7800.yaml
F: drivers/net/usb/lan78xx.*
F: include/dt-bindings/net/microchip-lan78xx.h
@ -29605,7 +29643,7 @@ F: Documentation/devicetree/bindings/net/xlnx,axi-ethernet.yaml
F: drivers/net/ethernet/xilinx/xilinx_axienet*
XILINX CAN DRIVER
M: Appana Durga Kedareswara rao <appana.durga.rao@xilinx.com>
M: Harini T <harini.t@amd.com>
L: linux-can@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/can/xilinx,can.yaml
@ -29914,6 +29952,12 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
F: sound/hda/codecs/senarytech.c
ZTE DINGHAI ETHERNET DRIVER
M: Junyang Han <han.junyang@zte.com.cn>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/zte/
THE REST
M: Linus Torvalds <torvalds@linux-foundation.org>
L: linux-kernel@vger.kernel.org

View File

@ -155,6 +155,8 @@
#define SO_INQ 84
#define SCM_INQ SO_INQ
#define SO_RIGHTS_NOTRUNC 85
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64

View File

@ -234,7 +234,6 @@ CONFIG_TULIP=m
CONFIG_WINBOND_840=m
CONFIG_DM9102=m
CONFIG_ULI526X=m
CONFIG_PCMCIA_XIRCOM=m
CONFIG_DL2K=m
CONFIG_SUNDANCE=m
CONFIG_E100=m

View File

@ -166,6 +166,8 @@
#define SO_INQ 84
#define SCM_INQ SO_INQ
#define SO_RIGHTS_NOTRUNC 85
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64

View File

@ -147,6 +147,8 @@
#define SO_INQ 0x4052
#define SCM_INQ SO_INQ
#define SO_RIGHTS_NOTRUNC 0x4053
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64

View File

@ -210,7 +210,6 @@ CONFIG_BNX2X=m
CONFIG_CHELSIO_T1=m
CONFIG_BE2NET=m
CONFIG_IBMVETH=m
CONFIG_EHEA=m
CONFIG_IBMVNIC=m
CONFIG_E100=y
CONFIG_E1000=y

View File

@ -412,7 +412,6 @@ CONFIG_TULIP_MMIO=y
CONFIG_WINBOND_840=m
CONFIG_DM9102=m
CONFIG_ULI526X=m
CONFIG_PCMCIA_XIRCOM=m
CONFIG_DL2K=m
CONFIG_SUNDANCE=m
CONFIG_FEC_MPC52xx=m

View File

@ -371,12 +371,6 @@ int devmem_is_allowed(unsigned long pfn)
}
#endif /* CONFIG_STRICT_DEVMEM */
/*
* This is defined in kernel/resource.c but only powerpc needs to export it, for
* the EHEA driver. Drop this when drivers/net/ethernet/ibm/ehea is removed.
*/
EXPORT_SYMBOL_GPL(walk_system_ram_range);
#ifdef CONFIG_EXECMEM
static struct execmem_info execmem_info __ro_after_init;

View File

@ -148,6 +148,8 @@
#define SO_INQ 0x005d
#define SCM_INQ SO_INQ
#define SO_RIGHTS_NOTRUNC 0x005e
#if !defined(__KERNEL__)

View File

@ -50,3 +50,5 @@ hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o
hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o
hci_uart-$(CONFIG_BT_HCIUART_AML) += hci_aml.o
hci_uart-objs := $(hci_uart-y)
CONTEXT_ANALYSIS := y

View File

@ -301,6 +301,11 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
return -EILSEQ;
}
break;
default:
bt_dev_err(data->hdev, "unknown packet type 0x%02x",
pkt_type);
return -EILSEQ;
}
skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
@ -319,6 +324,13 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
}
}
if (len > skb_tailroom(data->reassembly)) {
bt_dev_err(data->hdev, "block exceeds packet length");
kfree_skb(data->reassembly);
data->reassembly = NULL;
return -EILSEQ;
}
if (len > 0)
skb_put_data(data->reassembly, buf, len);
@ -353,6 +365,13 @@ static void bfusb_rx_complete(struct urb *urb)
skb_put(skb, count);
while (count) {
if (count < 2) {
bt_dev_err(data->hdev, "short block header");
kfree_skb(data->reassembly);
data->reassembly = NULL;
break;
}
hdr = buf[0] | (buf[1] << 8);
if (hdr & 0x4000) {
@ -360,16 +379,28 @@ static void bfusb_rx_complete(struct urb *urb)
count -= 2;
buf += 2;
} else {
if (count < 3) {
bt_dev_err(data->hdev, "short block header");
kfree_skb(data->reassembly);
data->reassembly = NULL;
break;
}
len = (buf[2] == 0) ? 256 : buf[2];
count -= 3;
buf += 3;
}
if (count < len)
if (count < len) {
bt_dev_err(data->hdev, "block extends over URB buffer ranges");
kfree_skb(data->reassembly);
data->reassembly = NULL;
break;
}
if ((hdr & 0xe1) == 0xc1)
bfusb_recv_block(data, hdr, buf, len);
if ((hdr & 0xe1) == 0xc1 &&
bfusb_recv_block(data, hdr, buf, len) < 0)
data->hdev->stat.err_rx++;
count -= len;
buf += len;

View File

@ -51,6 +51,7 @@ enum {
#define BTINTEL_BT_DOMAIN 0x12
#define BTINTEL_SAR_LEGACY 0
#define BTINTEL_SAR_INC_PWR 1
#define BTINTEL_SAR_REV2 2
#define BTINTEL_SAR_INC_PWR_SUPPORTED 0
#define CMD_WRITE_BOOT_PARAMS 0xfc0e
@ -2745,9 +2746,7 @@ static u8 btintel_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
* based on their connection handle value range.
*/
if (iso_capable(hdev) && hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) {
__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
if (hci_handle(handle) >= BTINTEL_ISODATA_HANDLE_BASE)
if (hci_acl_handle(skb) >= BTINTEL_ISODATA_HANDLE_BASE)
return HCI_ISODATA_PKT;
}
@ -3104,6 +3103,111 @@ static int btintel_set_mutual_sar(struct hci_dev *hdev, struct btintel_sar_inc_p
return 0;
}
/* btintel_send_sar_rev2_band - send DDC command for one Rev2 sub-band
*
* Each DDC 0x0311-0x0316 carries 2 bytes: [ChainA_value, ChainB_value].
* cmd->len = 4 (2 id + 2 data)
* HCI total = 5 bytes (1 len + 4)
*/
static int btintel_send_sar_rev2_band(struct hci_dev *hdev,
struct btintel_cp_ddc_write *cmd,
u16 id, u8 chain_a, u8 chain_b)
{
cmd->len = 4;
cmd->id = cpu_to_le16(id);
cmd->data[0] = chain_a;
cmd->data[1] = chain_b;
return btintel_send_sar_ddc(hdev, cmd, 5);
}
static int btintel_set_sar_rev2(struct hci_dev *hdev,
struct btintel_sar_rev2 *sar)
{
struct btintel_cp_ddc_write *cmd;
struct sk_buff *skb;
u8 buffer[64];
u8 enable;
int ret;
cmd = (void *)buffer;
/* DDC 0x019e: enable/disable increased power mode SAR (1 byte) */
cmd->len = 3;
cmd->id = cpu_to_le16(0x019e);
cmd->data[0] = (sar->inc_power_mode == BTINTEL_SAR_INC_PWR_SUPPORTED) ?
0x01 : 0x00;
ret = btintel_send_sar_ddc(hdev, cmd, 4);
if (ret)
return ret;
/* DDC 0x0311-0x0316: per sub-band ChainA + ChainB limits */
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0311,
sar->chain_a.subband_2g4,
sar->chain_b.subband_2g4);
if (ret)
return ret;
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0312,
sar->chain_a.subband_5g2,
sar->chain_b.subband_5g2);
if (ret)
return ret;
/* 0x0313 and 0x0314 both carry the 5G8/5G9 value */
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0313,
sar->chain_a.subband_5g8_5g9,
sar->chain_b.subband_5g8_5g9);
if (ret)
return ret;
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0314,
sar->chain_a.subband_5g8_5g9,
sar->chain_b.subband_5g8_5g9);
if (ret)
return ret;
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0315,
sar->chain_a.subband_6g1,
sar->chain_b.subband_6g1);
if (ret)
return ret;
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0316,
sar->chain_a.subband_6g3,
sar->chain_b.subband_6g3);
if (ret)
return ret;
/* Notify firmware that SAR initialisation is complete */
enable = 0x01;
skb = __hci_cmd_sync(hdev, 0xfe25, sizeof(enable), &enable, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_warn(hdev, "Failed to send Intel SAR Rev2 Enable (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
return 0;
}
static int btintel_sar_rev2_send_to_device(struct hci_dev *hdev,
struct btintel_sar_rev2 *sar,
struct intel_version_tlv *ver)
{
u16 cnvi = ver->cnvi_top & 0xfff;
u16 cnvr = ver->cnvr_top & 0xfff;
if (cnvi < BTINTEL_CNVI_BLAZARI || cnvr != BTINTEL_CNVR_WHP2) {
bt_dev_dbg(hdev, "BT SAR Rev2 not supported on this platform (cnvi=0x%x cnvr=0x%x)",
cnvi, cnvr);
return -EOPNOTSUPP;
}
bt_dev_info(hdev, "Applying Bluetooth SAR Rev2");
return btintel_set_sar_rev2(hdev, sar);
}
static int btintel_sar_send_to_device(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar,
struct intel_version_tlv *ver)
{
@ -3130,6 +3234,7 @@ static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv *
{
union acpi_object *bt_pkg, *buffer = NULL;
struct btintel_sar_inc_pwr sar;
struct btintel_sar_rev2 sar_rev2;
acpi_status status;
u8 revision;
int ret;
@ -3150,14 +3255,96 @@ static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv *
goto error;
}
if (buffer->package.elements[0].type != ACPI_TYPE_INTEGER) {
bt_dev_warn(hdev, "BT_SAR: unexpected ACPI type for revision field");
ret = -EINVAL;
goto error;
}
revision = buffer->package.elements[0].integer.value;
if (revision > BTINTEL_SAR_INC_PWR) {
if (revision > BTINTEL_SAR_REV2) {
bt_dev_dbg(hdev, "BT_SAR: revision: 0x%2.2x not supported", revision);
ret = -EOPNOTSUPP;
goto error;
}
if (revision == BTINTEL_SAR_REV2 && bt_pkg->package.count == 13) {
/* Element layout: 0 = domain ID (BTINTEL_BT_DOMAIN, 0x12),
* 1 = bt_sar_bios (u32), 2 = inc_power_mode (u32),
* 3..12 = per-chain sub-band limits (u8 each).
*/
static const u64 rev2_max[13] = {
U8_MAX, /* domain ID */
U32_MAX, U32_MAX, /* bt_sar_bios, inc_power_mode */
U8_MAX, U8_MAX, U8_MAX, U8_MAX, U8_MAX, /* chain A */
U8_MAX, U8_MAX, U8_MAX, U8_MAX, U8_MAX, /* chain B */
};
union acpi_object *e;
int i;
for (i = 0; i < 13; i++) {
e = &bt_pkg->package.elements[i];
if (e->type != ACPI_TYPE_INTEGER) {
bt_dev_warn(hdev, "BT SAR Rev2: unexpected ACPI type at element %d",
i);
ret = -EINVAL;
goto error;
}
if (e->integer.value > rev2_max[i]) {
bt_dev_warn(hdev, "BT SAR Rev2: element %d value 0x%llx out of range",
i, e->integer.value);
ret = -ERANGE;
goto error;
}
}
memset(&sar_rev2, 0, sizeof(sar_rev2));
sar_rev2.revision = revision;
sar_rev2.bt_sar_bios = bt_pkg->package.elements[1].integer.value;
if (sar_rev2.bt_sar_bios != 1) {
bt_dev_warn(hdev, "Bluetooth SAR Rev2 is not enabled");
ret = -EOPNOTSUPP;
goto error;
}
sar_rev2.inc_power_mode = bt_pkg->package.elements[2].integer.value;
sar_rev2.chain_a.subband_2g4 = bt_pkg->package.elements[3].integer.value;
sar_rev2.chain_a.subband_5g2 = bt_pkg->package.elements[4].integer.value;
sar_rev2.chain_a.subband_5g8_5g9 = bt_pkg->package.elements[5].integer.value;
sar_rev2.chain_a.subband_6g1 = bt_pkg->package.elements[6].integer.value;
sar_rev2.chain_a.subband_6g3 = bt_pkg->package.elements[7].integer.value;
sar_rev2.chain_b.subband_2g4 = bt_pkg->package.elements[8].integer.value;
sar_rev2.chain_b.subband_5g2 = bt_pkg->package.elements[9].integer.value;
sar_rev2.chain_b.subband_5g8_5g9 = bt_pkg->package.elements[10].integer.value;
sar_rev2.chain_b.subband_6g1 = bt_pkg->package.elements[11].integer.value;
sar_rev2.chain_b.subband_6g3 = bt_pkg->package.elements[12].integer.value;
bt_dev_dbg(hdev, "BT SAR Rev2: revision=%u bt_sar_bios=%u inc_power_mode=%u",
sar_rev2.revision, sar_rev2.bt_sar_bios, sar_rev2.inc_power_mode);
bt_dev_dbg(hdev, "BT SAR Rev2 Chain A: 2g4=%u 5g2=%u 5g8_5g9=%u 6g1=%u 6g3=%u",
sar_rev2.chain_a.subband_2g4, sar_rev2.chain_a.subband_5g2,
sar_rev2.chain_a.subband_5g8_5g9, sar_rev2.chain_a.subband_6g1,
sar_rev2.chain_a.subband_6g3);
bt_dev_dbg(hdev, "BT SAR Rev2 Chain B: 2g4=%u 5g2=%u 5g8_5g9=%u 6g1=%u 6g3=%u",
sar_rev2.chain_b.subband_2g4, sar_rev2.chain_b.subband_5g2,
sar_rev2.chain_b.subband_5g8_5g9, sar_rev2.chain_b.subband_6g1,
sar_rev2.chain_b.subband_6g3);
ret = btintel_sar_rev2_send_to_device(hdev, &sar_rev2, ver);
goto error;
}
if (revision == BTINTEL_SAR_REV2) {
bt_dev_warn(hdev, "BT SAR Rev2: unexpected ACPI package count %d (expected 13)",
bt_pkg->package.count);
ret = -EINVAL;
goto error;
}
memset(&sar, 0, sizeof(sar));
if (revision == BTINTEL_SAR_LEGACY && bt_pkg->package.count == 8) {
@ -3804,8 +3991,7 @@ int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
struct hci_event_hdr *hdr = (void *)skb->data;
const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 };
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
hdr->plen > 0) {
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff) {
const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
@ -3834,7 +4020,7 @@ int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
/* Handle all diagnostics events separately. May still call
* hci_recv_frame.
*/
if (len >= sizeof(diagnostics_hdr) &&
if (len + 1 >= sizeof(diagnostics_hdr) &&
memcmp(&skb->data[2], diagnostics_hdr,
sizeof(diagnostics_hdr)) == 0) {
return btintel_diagnostics(hdev, skb);

View File

@ -65,6 +65,7 @@ struct intel_tlv {
/* CNVR */
#define BTINTEL_CNVR_FMP2 0x910
#define BTINTEL_CNVR_WHP2 0xA10 /* Whale Peak2 - Panther Lake */
#define BTINTEL_IMG_BOOTLOADER 0x01 /* Bootloader image */
#define BTINTEL_IMG_IML 0x02 /* Intermediate image */
@ -204,6 +205,23 @@ struct btintel_sar_inc_pwr {
u8 le_lr;
};
/* Bluetooth SAR feature (BRDS), Revision 2 - per-chain sub-band power limits */
struct btintel_sar_band_limits {
u8 subband_2g4;
u8 subband_5g2;
u8 subband_5g8_5g9;
u8 subband_6g1;
u8 subband_6g3;
};
struct btintel_sar_rev2 {
u8 revision;
u32 bt_sar_bios; /* 1: BIOS-managed SAR enabled */
u32 inc_power_mode; /* 0: supported, 1: disabled */
struct btintel_sar_band_limits chain_a;
struct btintel_sar_band_limits chain_b;
};
#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)

View File

@ -1446,72 +1446,134 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
return err;
}
/* Queue a coredump dump_traces() pass.
*
* Returns true if a new coredump was queued, false if one was already
* in-flight (the BTINTEL_PCIE_COREDUMP_INPROGRESS bit serves as the
* single-writer guard for the @coredump_work item) or the workqueue is
* disabled (reset / remove in progress).
*
* Always queue this AFTER any companion event-reader work (hwexp /
* fwtrigger) so that, on the ordered @dump_workqueue, the event reader
* runs first and populates dmp_hdr.event_type / event_id before
* dump_traces consumes them.
*/
static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data,
u16 trigger_reason)
{
if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
return false;
data->dmp_hdr.trigger_reason = trigger_reason;
if (queue_work(data->dump_workqueue, &data->coredump_work))
return true;
/* Workqueue is disabled (reset/remove drained it). Release the
* guard so a later trigger, after re-probe, can succeed.
*/
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
return false;
}
static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data)
{
bt_dev_dbg(data->hdev, "Received firmware smart trigger cause");
if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags))
/* Per-work guard: deduplicate concurrent FW-trigger interrupts.
* Cleared at the tail of btintel_pcie_fwtrigger_worker().
*/
if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
&data->flags))
return;
/* Trigger device core dump when there is FW assert */
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
if (!queue_work(data->dump_workqueue, &data->fwtrigger_work)) {
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
return;
}
queue_work(data->coredump_workqueue, &data->coredump_work);
/* Queue coredump after the fwtrigger event reader so dmp_hdr.event_*
* is populated before dump_traces consumes it.
*/
btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
}
static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
{
bt_dev_err(data->hdev, "Received hw exception interrupt");
/* CORE_HALTED is the single-writer guard for this handler. It is
* set once on first HW exception and cleared only by re-probe
* (data is reallocated), so it also serializes hwexp_work
* scheduling without needing a separate bit.
*/
if (test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
return;
if (test_and_set_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags))
return;
/* Queue companion coredump first so it is appended after hwexp_work
* on the ordered @dump_workqueue (preserves the original
* coredump-then-hwexp ordering).
*/
btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
/* Trigger device core dump when there is HW exception */
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
queue_work(data->coredump_workqueue, &data->coredump_work);
queue_work(data->dump_workqueue, &data->hwexp_work);
}
static void btintel_pcie_coredump_worker(struct work_struct *work)
{
struct btintel_pcie_data *data = container_of(work,
struct btintel_pcie_data, coredump_work);
int err;
/* hdev is NULL until setup_hdev() succeeds, and is cleared on
* teardown after disable_work_sync() drains us; bail in that case.
*/
if (!data->hdev)
goto out;
btintel_pcie_dump_traces(data->hdev);
out:
/* Release guard last so a new trigger can run only after this
* pass has fully completed (including dev_coredumpv()).
*/
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
}
static void btintel_pcie_hwexp_worker(struct work_struct *work)
{
struct btintel_pcie_data *data = container_of(work,
struct btintel_pcie_data, hwexp_work);
if (!data->hdev)
return;
if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) {
err = btintel_pcie_dump_fwtrigger_event(data);
if (err)
bt_dev_warn(data->hdev, "failed to log fwtrigger event");
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
}
/* Unlike usb products, controller will not send hardware exception
* event on exception. Instead controller writes the hardware event
* to device memory along with optional debug events, raises MSIX
* and halts. Driver shall read the exception event from device
* memory and passes it to the stack for further processing.
*
* Re-entry is gated by BTINTEL_PCIE_CORE_HALTED in the IRQ
* handler, which is only cleared by re-probe; no per-work bit
* is needed here.
*/
btintel_pcie_read_hwexp(data);
}
if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
btintel_pcie_dump_traces(data->hdev);
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
}
static void btintel_pcie_fwtrigger_worker(struct work_struct *work)
{
struct btintel_pcie_data *data = container_of(work,
struct btintel_pcie_data, fwtrigger_work);
int err;
if (test_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) {
/* Unlike usb products, controller will not send hardware
* exception event on exception. Instead controller writes the
* hardware event to device memory along with optional debug
* events, raises MSIX and halts. Driver shall read the
* exception event from device memory and passes it stack for
* further processing.
*/
btintel_pcie_read_hwexp(data);
clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
}
if (!data->hdev)
goto out;
err = btintel_pcie_dump_fwtrigger_event(data);
if (err)
bt_dev_warn(data->hdev, "failed to log fwtrigger event");
out:
/* Release guard last; matches set in fw_trigger handler. */
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
}
static void btintel_pcie_rx_work(struct work_struct *work)
@ -2363,7 +2425,6 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
err = -EINVAL;
goto exit_error;
break;
}
data->dmp_hdr.cnvi_top = ver_tlv.cnvi_top;
@ -2488,8 +2549,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
}
}
static void btintel_pcie_reset(struct hci_dev *hdev);
static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
{
union acpi_object *obj, argv4;
@ -2650,20 +2709,22 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
btintel_pcie_synchronize_irqs(data);
flush_work(&data->rx_work);
/* Drain any in-flight coredump and block new ones across reset.
* Safe from self-deadlock: coredump_work runs on a separate wq.
/* Drain any in-flight dump workers and block new ones across reset.
* Safe from self-deadlock: they all run on a separate wq.
*/
disable_work_sync(&data->coredump_work);
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
bt_dev_dbg(data->hdev, "Release bluetooth interface");
/* Both reset paths follow the same contract: on success they
* destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs
* the coredump_work with disable count 0), so enable_work() must
* the dump workers with disable count 0), so enable_work() must
* NOT be called on the success path. Only the FLR path can fail
* with 'data' still alive, in which case we balance the
* disable_work_sync() above so a later successful reset is not
* permanently blocked.
* disable_work_sync() calls above so a later successful reset is
* not permanently blocked.
*
* pci_lock_rescan_remove() (held above) serializes against PCI
* device addition/removal (hotplug), so no device can be added to
@ -2674,64 +2735,134 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
goto out;
}
if (btintel_pcie_perform_flr(data))
if (btintel_pcie_perform_flr(data)) {
enable_work(&data->coredump_work);
enable_work(&data->hwexp_work);
enable_work(&data->fwtrigger_work);
}
out:
pci_dev_put(pdev);
pci_unlock_rescan_remove();
}
static void btintel_pcie_reset(struct hci_dev *hdev)
/* Schedule a device reset of the requested type.
*
* BTINTEL_PCIE_RECOVERY_IN_PROGRESS serializes all reset requesters
* (sysfs reset attribute, hci_cmd_timeout(), hw_error, resume error
* path, etc.) so that:
*
* - dev_data->reset_type is written by exactly one caller (the
* thread that wins test_and_set_bit), eliminating the race where
* a second hw_error could clobber an already-scheduled reset's
* type;
* - the write happens AFTER the bit is set, so reset_work observes
* it through schedule_work()'s memory ordering;
* - losers return without touching reset_type or scheduling the
* work, so concurrent triggers are silently coalesced into the
* in-flight one (whose recovery will reinitialize the device
* regardless of the dropped trigger's variant).
*
* The bit is cleared only by .remove() / re-probe via fresh devm
* allocation, which is the intended one-shot semantics: a reset
* tears down and re-probes 'data', so there is no "in-flight"
* reset to follow up after device_reprobe() succeeds.
*/
static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
enum btintel_pcie_reset_type type)
{
struct btintel_pcie_data *data;
data = hci_get_drvdata(hdev);
if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
return;
if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
return;
data->reset_type = type;
pci_dev_get(data->pdev);
schedule_work(&data->reset_work);
}
static void btintel_pcie_hci_reset(struct hci_dev *hdev)
{
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
}
static ssize_t vendor_reset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int val;
struct pci_dev *pdev = to_pci_dev(dev);
struct btintel_pcie_data *data = pci_get_drvdata(pdev);
if (!data || !data->hdev)
return -ENODEV;
if (kstrtouint(buf, 10, &val) || val != 0) {
bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
return -EINVAL;
}
bt_dev_info(data->hdev, "PLDR triggered via sysfs");
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
return count;
}
static ssize_t vendor_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "0 - PLDR\n");
}
static DEVICE_ATTR_RW(vendor_reset);
static struct attribute *btintel_pcie_attrs[] = {
&dev_attr_vendor_reset.attr,
NULL,
};
ATTRIBUTE_GROUPS(btintel_pcie);
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
{
struct btintel_pcie_dev_recovery *data;
struct btintel_pcie_dev_recovery *rec;
struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev);
struct pci_dev *pdev = dev_data->pdev;
enum btintel_pcie_reset_type type;
time64_t retry_window;
if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &dev_data->flags))
return;
btintel_pcie_dump_debug_registers(hdev);
data = btintel_pcie_get_recovery(pdev, &hdev->dev);
if (!data)
rec = btintel_pcie_get_recovery(pdev, &hdev->dev);
if (!rec)
return;
if (code == 0x13)
dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR;
else
dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
type = (code == 0x13) ? BTINTEL_PCIE_IOSF_PRR_PLDR
: BTINTEL_PCIE_IOSF_PRR_FLR;
bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code,
dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
retry_window = ktime_get_boottime_seconds() - data->last_error;
type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
retry_window = ktime_get_boottime_seconds() - rec->last_error;
if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
rec->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d",
BTINTEL_PCIE_FLR_MAX_RETRY, data->count);
BTINTEL_PCIE_FLR_MAX_RETRY, rec->count);
bt_dev_dbg(hdev, "Boot time: %lld seconds",
ktime_get_boottime_seconds());
bt_dev_dbg(hdev, "last error at: %lld seconds",
data->last_error);
rec->last_error);
return;
}
btintel_pcie_inc_recovery_count(pdev, &hdev->dev);
btintel_pcie_reset(hdev);
btintel_pcie_request_reset(dev_data, type);
}
static bool btintel_pcie_wakeup(struct hci_dev *hdev)
@ -2821,7 +2952,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
hdev->hw_error = btintel_pcie_hw_error;
hdev->set_diag = btintel_set_diag;
hdev->set_bdaddr = btintel_set_bdaddr;
hdev->reset = btintel_pcie_reset;
hdev->reset = btintel_pcie_hci_reset;
hdev->wakeup = btintel_pcie_wakeup;
hdev->hci_drv = &btintel_pcie_hci_drv;
@ -2869,8 +3000,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
if (!data->workqueue)
return -ENOMEM;
data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
if (!data->coredump_workqueue) {
data->dump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
if (!data->dump_workqueue) {
destroy_workqueue(data->workqueue);
return -ENOMEM;
}
@ -2879,6 +3010,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
INIT_WORK(&data->reset_work, btintel_pcie_reset_work);
INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker);
INIT_WORK(&data->fwtrigger_work, btintel_pcie_fwtrigger_worker);
data->boot_stage_cache = 0x00;
data->img_resp_cache = 0x00;
@ -2921,7 +3054,7 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
/* reset device before exit */
btintel_pcie_reset_bt(data);
destroy_workqueue(data->coredump_workqueue);
destroy_workqueue(data->dump_workqueue);
pci_clear_master(pdev);
@ -2940,12 +3073,14 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
return;
}
/* Permanently block coredump triggers and drain the worker before
* tearing down. Must run before cancel_work_sync(&reset_work) so
* the disable counter stays >= 1 even after reset_work()'s
/* Permanently block all dump triggers and drain the workers before
* tearing down. Must run before disable_work_sync(&reset_work) so
* the disable counters stay >= 1 even after reset_work()'s
* balanced enable_work() (counter 2 -> 1, never reaching 0).
*/
disable_work_sync(&data->coredump_work);
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
/* Cancel pending reset work. Skip only when remove() is called from
* within the reset work itself (PLDR device_reprobe path) to avoid
@ -2973,7 +3108,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
btintel_pcie_release_hdev(data);
destroy_workqueue(data->coredump_workqueue);
destroy_workqueue(data->dump_workqueue);
destroy_workqueue(data->workqueue);
btintel_pcie_free(data);
@ -2992,16 +3127,8 @@ static void btintel_pcie_coredump(struct device *dev)
if (!data)
return;
if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
return;
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER;
/* queue_work() returns false if the work is disabled (reset or
* remove in progress); clear the in-progress bit so a later
* trigger can succeed once the work is re-enabled.
*/
if (!queue_work(data->coredump_workqueue, &data->coredump_work))
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
btintel_pcie_queue_coredump(data,
BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER);
}
#endif
@ -3113,8 +3240,7 @@ static int btintel_pcie_resume(struct device *dev)
if (data->pm_sx_event == PM_EVENT_FREEZE ||
data->pm_sx_event == PM_EVENT_HIBERNATE) {
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
btintel_pcie_reset(data->hdev);
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
return 0;
}
@ -3138,14 +3264,10 @@ static int btintel_pcie_resume(struct device *dev)
if (btintel_pcie_in_error(data) ||
btintel_pcie_in_device_halt(data)) {
bt_dev_err(data->hdev, "Controller in error state for D0 entry");
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS,
&data->flags)) {
data->dmp_hdr.trigger_reason =
BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
queue_work(data->coredump_workqueue, &data->coredump_work);
}
btintel_pcie_queue_coredump(data,
BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
btintel_pcie_reset(data->hdev);
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
}
return err;
}
@ -3165,6 +3287,7 @@ static struct pci_driver btintel_pcie_driver = {
.probe = btintel_pcie_probe,
.remove = btintel_pcie_remove,
.driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
.dev_groups = btintel_pcie_groups,
#ifdef CONFIG_DEV_COREDUMP
.driver.coredump = btintel_pcie_coredump
#endif

View File

@ -118,7 +118,6 @@ enum {
enum {
BTINTEL_PCIE_CORE_HALTED,
BTINTEL_PCIE_HWEXP_INPROGRESS,
BTINTEL_PCIE_COREDUMP_INPROGRESS,
BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
@ -466,8 +465,11 @@ struct btintel_pcie_dump_header {
* @workqueue: workqueue for RX work
* @rx_skb_q: SKB queue for RX packet
* @rx_work: RX work struct to process the RX packet in @rx_skb_q
* @coredump_workqueue: dedicated workqueue for coredump collection
* @coredump_work: work struct for coredump trace collection
* @dump_workqueue: dedicated ordered workqueue serializing the coredump,
* hardware exception, and firmware-trigger dump workers
* @coredump_work: work struct for DRAM trace coredump collection
* @hwexp_work: work struct for hardware exception event read
* @fwtrigger_work: work struct for firmware-triggered diagnostic event read
* @dma_pool: DMA pool for descriptors, index array and ci
* @dma_p_addr: DMA address for pool
* @dma_v_addr: address of pool
@ -516,8 +518,10 @@ struct btintel_pcie_data {
struct work_struct rx_work;
struct work_struct reset_work;
struct workqueue_struct *coredump_workqueue;
struct workqueue_struct *dump_workqueue;
struct work_struct coredump_work;
struct work_struct hwexp_work;
struct work_struct fwtrigger_work;
struct dma_pool *dma_pool;
dma_addr_t dma_p_addr;

View File

@ -43,10 +43,17 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
if (skb->len < sizeof(*hdr))
return true;
if (hdr->evt == HCI_EV_CMD_COMPLETE) {
struct hci_ev_cmd_complete *ec;
u16 opcode;
if (hdr->plen < sizeof(*ec) ||
skb->len < HCI_EVENT_HDR_SIZE + sizeof(*ec))
return true;
ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
opcode = __le16_to_cpu(ec->opcode);
@ -74,6 +81,9 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
struct btmrvl_event *event;
int ret = 0;
if (skb->len <= offsetof(typeof(*event), data[0]))
return -EINVAL;
event = (struct btmrvl_event *) skb->data;
if (event->ec != 0xff) {
BT_DBG("Not Marvell Event=%x", event->ec);
@ -83,6 +93,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
switch (event->data[0]) {
case BT_EVENT_AUTO_SLEEP_MODE:
if (skb->len <= offsetof(typeof(*event), data[2]))
return -EINVAL;
if (!event->data[2]) {
if (event->data[1] == BT_PS_ENABLE)
adapter->psmode = 1;
@ -96,6 +108,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
break;
case BT_EVENT_HOST_SLEEP_CONFIG:
if (skb->len <= offsetof(typeof(*event), data[3]))
return -EINVAL;
if (!event->data[3])
BT_DBG("gpio=%x, gap=%x", event->data[1],
event->data[2]);
@ -104,6 +118,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
break;
case BT_EVENT_HOST_SLEEP_ENABLE:
if (skb->len <= offsetof(typeof(*event), data[1]))
return -EINVAL;
if (!event->data[1]) {
adapter->hs_state = HS_ACTIVATED;
if (adapter->psmode)
@ -116,6 +132,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
break;
case BT_EVENT_MODULE_CFG_REQ:
if (skb->len <= offsetof(typeof(*event), data[2]))
return -EINVAL;
if (priv->btmrvl_dev.sendcmdflag &&
event->data[1] == MODULE_BRINGUP_REQ) {
BT_DBG("EVENT:%s",
@ -133,6 +151,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
break;
case BT_EVENT_POWER_STATE:
if (skb->len <= offsetof(typeof(*event), data[1]))
return -EINVAL;
if (event->data[1] == BT_PS_SLEEP)
adapter->ps_state = PS_SLEEP;
BT_DBG("EVENT:%s",

View File

@ -799,7 +799,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
skb_pull(skb, SDIO_HEADER_LEN);
if (btmrvl_process_event(priv, skb))
hci_recv_frame(hdev, skb);
kfree_skb(skb);
hdev->stat.byte_rx += buf_len;
break;

View File

@ -1480,6 +1480,9 @@ static void btmtksdio_remove(struct sdio_func *func)
if (test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
btmtksdio_close(hdev);
if (bdev->data->pm_runtime_supported)
pm_runtime_dont_use_autosuspend(bdev->dev);
/* Be consistent the state in btmtksdio_probe */
pm_runtime_get_noresume(bdev->dev);

View File

@ -9,6 +9,8 @@
#include <linux/serdev.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/pwrseq/consumer.h>
#include <linux/skbuff.h>
#include <linux/unaligned.h>
#include <linux/firmware.h>
@ -211,6 +213,7 @@ struct btnxpuart_dev {
struct ps_data psdata;
struct btnxpuart_data *nxp_data;
struct pwrseq_desc *pwrseq;
struct reset_control *pdn;
struct hci_uart hu;
};
@ -1331,19 +1334,7 @@ static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev)
static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
{
static const u8 ir_hw_err[] = { HCI_EV_HARDWARE_ERROR,
0x01, BTNXPUART_IR_HW_ERR };
struct sk_buff *skb;
skb = bt_skb_alloc(3, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
skb_put_data(skb, ir_hw_err, 3);
/* Inject Hardware Error to upper stack */
return hci_recv_frame(hdev, skb);
return __hci_reset_dev(hdev, BTNXPUART_IR_HW_ERR);
}
/* Firmware dump */
@ -1872,11 +1863,26 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
return err;
}
if (of_graph_is_present(dev_of_node(&serdev->ctrl->dev))) {
struct pwrseq_desc *pwrseq;
pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
if (IS_ERR(pwrseq))
return dev_err_probe(&serdev->dev, PTR_ERR(pwrseq),
"failed to get pwrseq\n");
nxpdev->pwrseq = pwrseq;
err = pwrseq_power_on(pwrseq);
if (err)
goto err_pwrseq_put;
}
/* Initialize and register HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
dev_err(&serdev->dev, "Can't allocate HCI device\n");
return -ENOMEM;
err = -ENOMEM;
goto err_pwrseq_put;
}
reset_control_deassert(nxpdev->pdn);
@ -1907,23 +1913,31 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
if (bacmp(&ba, BDADDR_ANY))
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
if (hci_register_dev(hdev) < 0) {
err = hci_register_dev(hdev);
if (err < 0) {
dev_err(&serdev->dev, "Can't register HCI device\n");
goto probe_fail;
}
if (ps_setup(hdev))
goto probe_fail;
if (ps_setup(hdev)) {
err = -ENODEV;
goto probe_fail_unregister;
}
hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr,
nxp_coredump_notify);
return 0;
probe_fail_unregister:
hci_unregister_dev(hdev);
probe_fail:
reset_control_assert(nxpdev->pdn);
hci_free_dev(hdev);
return -ENODEV;
err_pwrseq_put:
if (nxpdev->pwrseq)
pwrseq_put(nxpdev->pwrseq);
return err;
}
static void nxp_serdev_remove(struct serdev_device *serdev)
@ -1950,6 +1964,8 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
ps_cleanup(nxpdev);
hci_unregister_dev(hdev);
reset_control_assert(nxpdev->pdn);
if (nxpdev->pwrseq)
pwrseq_put(nxpdev->pwrseq);
hci_free_dev(hdev);
}

View File

@ -190,25 +190,6 @@ static int qca_send_patch_config_cmd(struct hci_dev *hdev)
return err;
}
static int qca_send_reset(struct hci_dev *hdev)
{
struct sk_buff *skb;
int err;
bt_dev_dbg(hdev, "QCA HCI_RESET");
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Reset failed (%d)", err);
return err;
}
kfree_skb(skb);
return 0;
}
static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
{
u8 cmd;
@ -990,11 +971,12 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
}
/* Perform HCI reset */
err = qca_send_reset(hdev);
err = __hci_reset_sync(hdev);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to run HCI_RESET (%d)", err);
return err;
}
bt_dev_dbg(hdev, "QCA HCI_RESET succeed");
switch (soc_type) {
case QCA_WCN3991:
@ -1029,8 +1011,7 @@ int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
baswap(&bdaddr_swapped, bdaddr);
skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6,
&bdaddr_swapped, HCI_EV_VENDOR,
HCI_INIT_TIMEOUT);
&bdaddr_swapped, 0, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Change address cmd failed (%d)", err);

View File

@ -107,7 +107,6 @@ static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
return -ENOMEM;
h_adapter->priv = priv;
ops->set_bt_context(priv, h_adapter);
h_adapter->proto_ops = ops;
hdev = hci_alloc_dev();
@ -136,6 +135,8 @@ static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
goto err;
}
ops->set_bt_context(priv, h_adapter);
return 0;
err:
h_adapter->hdev = NULL;

View File

@ -297,6 +297,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3503), .driver_info = BTUSB_QCA_ROME |
BTUSB_WIDEBAND_SPEECH },
/* QCA WCN6855 chipset */
{ USB_DEVICE(0x0489, 0xe0c7), .driver_info = BTUSB_QCA_WCN6855 |
@ -679,6 +681,8 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x13d3, 0x3606), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
/* MediaTek MT7902 Bluetooth devices */
{ USB_DEVICE(0x0489, 0xe156), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0e8d, 0x1ede), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3579), .driver_info = BTUSB_MEDIATEK |
@ -796,6 +800,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3625), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3627), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3628), .driver_info = BTUSB_MEDIATEK |
@ -850,6 +856,12 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8761CU Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0b05, 0x1d70), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
@ -882,6 +894,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0bda, 0xc123), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x1357, 0xc123), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0cb5, 0xc547), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
@ -937,6 +951,10 @@ struct qca_dump_info {
u16 ram_dump_seqno;
};
struct btqca_data {
struct qca_dump_info qca_dump;
};
#define BTUSB_MAX_ISOC_FRAMES 10
#define BTUSB_INTR_RUNNING 0
@ -1010,6 +1028,7 @@ struct btusb_data {
bool usb_alt6_packet_flow;
int isoc_altsetting;
int suspend_count;
const struct usb_device_id *match_id;
int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
int (*recv_acl)(struct hci_dev *hdev, struct sk_buff *skb);
@ -1022,8 +1041,6 @@ struct btusb_data {
int (*disconnect)(struct hci_dev *hdev);
int oob_wake_irq; /* irq for out-of-band wake-on-bt */
struct qca_dump_info qca_dump;
};
static void btusb_reset(struct hci_dev *hdev)
@ -1235,14 +1252,16 @@ static inline void btusb_free_frags(struct btusb_data *data)
spin_unlock_irqrestore(&data->rxlock, flags);
}
static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
static int btusb_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
if (data->intr_interval) {
/* Trigger dequeue immediately if an event is received */
schedule_delayed_work(&data->rx_work, 0);
}
return data->recv_event(data->hdev, skb);
return data->recv_event(hdev, skb);
}
static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
@ -1302,7 +1321,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
}
/* Complete frame */
btusb_recv_event(data, skb);
btusb_recv_event(data->hdev, skb);
skb = NULL;
}
}
@ -1313,13 +1332,15 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
return err;
}
static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
static int btusb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
/* Only queue ACL packet if intr_interval is set as it means
* force_poll_sync has been enabled.
*/
if (!data->intr_interval)
return data->recv_acl(data->hdev, skb);
return data->recv_acl(hdev, skb);
skb_queue_tail(&data->acl_q, skb);
schedule_delayed_work(&data->rx_work, data->intr_interval);
@ -1358,10 +1379,8 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
hci_skb_expect(skb) -= len;
if (skb->len == HCI_ACL_HDR_SIZE) {
__le16 dlen = hci_acl_hdr(skb)->dlen;
/* Complete ACL header */
hci_skb_expect(skb) = __le16_to_cpu(dlen);
hci_skb_expect(skb) = hci_acl_dlen(skb);
if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
@ -1374,7 +1393,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
if (!hci_skb_expect(skb)) {
/* Complete frame */
btusb_recv_acl(data, skb);
btusb_recv_acl(data->hdev, skb);
skb = NULL;
}
}
@ -2053,6 +2072,14 @@ static void btusb_stop_traffic(struct btusb_data *data)
usb_kill_anchored_urbs(&data->ctrl_anchor);
}
static void btusb_prepare_reset(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
}
static int btusb_close(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
@ -2783,7 +2810,7 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
{
if (skb->len >= HCI_EVENT_HDR_SIZE + 1 &&
skb->data[0] == HCI_VENDOR_PKT &&
skb->data[0] == HCI_EV_VENDOR &&
skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
struct rtk_dev_coredump_hdr hdr = {
.code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
@ -2897,8 +2924,7 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
/* Release MediaTek ISO data interface */
btusb_mtk_release_iso_intf(hdev);
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
btusb_prepare_reset(hdev);
/* Toggle the hard reset line. The MediaTek device is going to
* yank itself off the USB and then replug. The cleanup is handled
@ -3072,14 +3098,15 @@ static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
const bdaddr_t *bdaddr)
{
bdaddr_t bdaddr_swapped;
struct sk_buff *skb;
u8 buf[6];
long ret;
memcpy(buf, bdaddr, sizeof(bdaddr_t));
baswap(&bdaddr_swapped, bdaddr);
skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(buf), buf,
HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(bdaddr_swapped),
&bdaddr_swapped, HCI_EV_CMD_COMPLETE,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
bt_dev_err(hdev, "Change address command failed (%ld)", ret);
@ -3115,14 +3142,15 @@ struct qca_dump_hdr {
static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb)
{
char buf[128];
struct btusb_data *btdata = hci_get_drvdata(hdev);
struct btqca_data *btqca_data = hci_get_priv(hdev);
struct qca_dump_info *qca_dump_ptr = &btqca_data->qca_dump;
snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n",
btdata->qca_dump.controller_id);
qca_dump_ptr->controller_id);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n",
btdata->qca_dump.fw_version);
qca_dump_ptr->fw_version);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Driver: %s\nVendor: qca\n",
@ -3130,7 +3158,7 @@ static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb)
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "VID: 0x%x\nPID:0x%x\n",
btdata->qca_dump.id_vendor, btdata->qca_dump.id_product);
qca_dump_ptr->id_vendor, qca_dump_ptr->id_product);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Lmp Subversion: 0x%x\n",
@ -3159,6 +3187,8 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
struct qca_dump_hdr *dump_hdr;
struct btusb_data *btdata = hci_get_drvdata(hdev);
struct btqca_data *btqca_data = hci_get_priv(hdev);
struct qca_dump_info *qca_dump_ptr = &btqca_data->qca_dump;
struct usb_device *udev = btdata->udev;
pkt_type = hci_skb_pkt_type(skb);
@ -3186,8 +3216,8 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
goto out;
}
btdata->qca_dump.ram_dump_size = dump_size;
btdata->qca_dump.ram_dump_seqno = 0;
qca_dump_ptr->ram_dump_size = dump_size;
qca_dump_ptr->ram_dump_seqno = 0;
skb_pull(skb, offsetof(struct qca_dump_hdr, data0));
@ -3199,29 +3229,29 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
skb_pull(skb, offsetof(struct qca_dump_hdr, data));
}
if (!btdata->qca_dump.ram_dump_size) {
if (!qca_dump_ptr->ram_dump_size) {
ret = -EINVAL;
bt_dev_err(hdev, "memdump is not active");
goto out;
}
if ((seqno > btdata->qca_dump.ram_dump_seqno + 1) && (seqno != QCA_LAST_SEQUENCE_NUM)) {
dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - btdata->qca_dump.ram_dump_seqno - 1);
if ((seqno > qca_dump_ptr->ram_dump_seqno + 1) && seqno != QCA_LAST_SEQUENCE_NUM) {
dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - qca_dump_ptr->ram_dump_seqno - 1);
hci_devcd_append_pattern(hdev, 0x0, dump_size);
bt_dev_err(hdev,
"expected memdump seqno(%u) is not received(%u)\n",
btdata->qca_dump.ram_dump_seqno, seqno);
btdata->qca_dump.ram_dump_seqno = seqno;
qca_dump_ptr->ram_dump_seqno, seqno);
qca_dump_ptr->ram_dump_seqno = seqno;
kfree_skb(skb);
return ret;
}
hci_devcd_append(hdev, skb);
btdata->qca_dump.ram_dump_seqno++;
qca_dump_ptr->ram_dump_seqno++;
if (seqno == QCA_LAST_SEQUENCE_NUM) {
bt_dev_info(hdev,
"memdump done: pkts(%u), total(%u)\n",
btdata->qca_dump.ram_dump_seqno, btdata->qca_dump.ram_dump_size);
qca_dump_ptr->ram_dump_seqno, qca_dump_ptr->ram_dump_size);
hci_devcd_complete(hdev);
goto out;
@ -3229,10 +3259,10 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
return ret;
out:
if (btdata->qca_dump.ram_dump_size)
if (qca_dump_ptr->ram_dump_size)
usb_enable_autosuspend(udev);
btdata->qca_dump.ram_dump_size = 0;
btdata->qca_dump.ram_dump_seqno = 0;
qca_dump_ptr->ram_dump_size = 0;
qca_dump_ptr->ram_dump_seqno = 0;
clear_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags);
if (ret < 0)
@ -3257,7 +3287,7 @@ static bool acl_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
goto out;
event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR)
goto out;
dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
@ -3283,7 +3313,7 @@ static bool evt_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
return false;
event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR)
goto out;
dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
@ -3695,8 +3725,12 @@ static int btusb_setup_qca(struct hci_dev *hdev)
if (err)
return err;
btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version);
if (btdata->match_id->driver_info & BTUSB_QCA_WCN6855) {
struct btqca_data *btqca_data = hci_get_priv(hdev);
btqca_data->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
btqca_data->qca_dump.controller_id = le32_to_cpu(ver.rom_version);
}
if (!(status & QCA_SYSCFG_UPDATED)) {
err = btusb_setup_qca_load_nvm(hdev, &ver, info);
@ -3877,16 +3911,13 @@ static bool btusb_wakeup(struct hci_dev *hdev)
static int btusb_shutdown_qca(struct hci_dev *hdev)
{
struct sk_buff *skb;
int err;
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = __hci_reset_sync(hdev);
if (err)
bt_dev_err(hdev, "HCI reset during shutdown failed");
return PTR_ERR(skb);
}
kfree_skb(skb);
return 0;
return err;
}
static ssize_t force_poll_sync_read(struct file *file, char __user *user_buf,
@ -4070,7 +4101,7 @@ static int btusb_probe(struct usb_interface *intf,
struct btusb_data *data;
struct hci_dev *hdev;
unsigned ifnum_base;
int err, priv_size;
int err, priv_size = 0;
BT_DBG("intf %p id %p", intf, id);
@ -4089,7 +4120,7 @@ static int btusb_probe(struct usb_interface *intf,
id = match;
}
if (id->driver_info == BTUSB_IGNORE)
if (id->driver_info & BTUSB_IGNORE)
return -ENODEV;
if (id->driver_info & BTUSB_ATH3012) {
@ -4107,6 +4138,7 @@ static int btusb_probe(struct usb_interface *intf,
if (!data)
return -ENOMEM;
data->match_id = id;
err = usb_find_common_endpoints(intf->cur_altsetting, &data->bulk_rx_ep,
&data->bulk_tx_ep, &data->intr_ep, NULL);
if (err)
@ -4140,8 +4172,6 @@ static int btusb_probe(struct usb_interface *intf,
init_usb_anchor(&data->ctrl_anchor);
spin_lock_init(&data->rxlock);
priv_size = 0;
data->recv_event = hci_recv_frame;
data->recv_bulk = btusb_recv_bulk;
@ -4160,6 +4190,9 @@ static int btusb_probe(struct usb_interface *intf,
} else if (id->driver_info & BTUSB_MEDIATEK) {
/* Allocate extra space for Mediatek device */
priv_size += sizeof(struct btmtk_data);
} else if (id->driver_info & BTUSB_QCA_WCN6855) {
/* Allocate extra space for QCA WCN6855 device */
priv_size += sizeof(struct btqca_data);
}
data->recv_acl = hci_recv_frame;
@ -4302,8 +4335,10 @@ static int btusb_probe(struct usb_interface *intf,
}
if (id->driver_info & BTUSB_QCA_WCN6855) {
data->qca_dump.id_vendor = id->idVendor;
data->qca_dump.id_product = id->idProduct;
struct btqca_data *btqca_data = hci_get_priv(hdev);
btqca_data->qca_dump.id_vendor = id->idVendor;
btqca_data->qca_dump.id_product = id->idProduct;
data->recv_event = btusb_recv_evt_qca;
data->recv_acl = btusb_recv_acl_qca;
hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL);

View File

@ -247,7 +247,7 @@ static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name)
struct hci_uart *hu = hci_get_drvdata(hdev);
struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev);
const struct firmware *firmware = NULL;
struct aml_fw_len *fw_len = NULL;
const struct aml_fw_len *fw_len = NULL;
u8 *iccm_start = NULL, *dccm_start = NULL;
u32 iccm_len, dccm_len;
u32 value = 0;
@ -281,7 +281,21 @@ static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name)
goto exit;
}
fw_len = (struct aml_fw_len *)firmware->data;
if (firmware->size < sizeof(*fw_len)) {
bt_dev_err(hdev, "Firmware is too small for its header");
ret = -EINVAL;
goto exit;
}
fw_len = (const struct aml_fw_len *)firmware->data;
if (fw_len->iccm_len < amldev->aml_dev_data->iccm_offset ||
fw_len->iccm_len > firmware->size - sizeof(*fw_len) ||
fw_len->dccm_len > firmware->size - sizeof(*fw_len) -
fw_len->iccm_len) {
bt_dev_err(hdev, "Invalid firmware segment lengths");
ret = -EINVAL;
goto exit;
}
/* Download ICCM */
iccm_start = (u8 *)(firmware->data) + sizeof(struct aml_fw_len)

View File

@ -1314,174 +1314,174 @@ static struct bcm_device_data bcm43430_device_data = {
};
static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E00" },
{ "BCM2E01" },
{ "BCM2E02" },
{ "BCM2E03" },
{ "BCM2E04" },
{ "BCM2E05" },
{ "BCM2E06" },
{ "BCM2E07" },
{ "BCM2E08" },
{ "BCM2E09" },
{ "BCM2E0A" },
{ "BCM2E0B" },
{ "BCM2E0C" },
{ "BCM2E0D" },
{ "BCM2E0E" },
{ "BCM2E0F" },
{ "BCM2E10" },
{ "BCM2E11" },
{ "BCM2E12" },
{ "BCM2E13" },
{ "BCM2E14" },
{ "BCM2E15" },
{ "BCM2E16" },
{ "BCM2E17" },
{ "BCM2E18" },
{ "BCM2E19" },
{ "BCM2E1A" },
{ "BCM2E1B" },
{ "BCM2E1C" },
{ "BCM2E1D" },
{ "BCM2E1F" },
{ "BCM2E20" },
{ "BCM2E21" },
{ "BCM2E22" },
{ "BCM2E23" },
{ "BCM2E24" },
{ "BCM2E25" },
{ "BCM2E26" },
{ "BCM2E27" },
{ "BCM2E28" },
{ "BCM2E29" },
{ "BCM2E2A" },
{ "BCM2E2B" },
{ "BCM2E2C" },
{ "BCM2E2D" },
{ "BCM2E2E" },
{ "BCM2E2F" },
{ "BCM2E30" },
{ "BCM2E31" },
{ "BCM2E32" },
{ "BCM2E33" },
{ "BCM2E34" },
{ "BCM2E35" },
{ "BCM2E36" },
{ "BCM2E37" },
{ "BCM2E38" },
{ "BCM2E39" },
{ "BCM2E3A" },
{ "BCM2E3B" },
{ "BCM2E3C" },
{ "BCM2E3D" },
{ "BCM2E3E" },
{ "BCM2E3F" },
{ "BCM2E40" },
{ "BCM2E41" },
{ "BCM2E42" },
{ "BCM2E43" },
{ "BCM2E44" },
{ "BCM2E45" },
{ "BCM2E46" },
{ "BCM2E47" },
{ "BCM2E48" },
{ "BCM2E49" },
{ "BCM2E4A" },
{ "BCM2E4B" },
{ "BCM2E4C" },
{ "BCM2E4D" },
{ "BCM2E4E" },
{ "BCM2E4F" },
{ "BCM2E50" },
{ "BCM2E51" },
{ "BCM2E52" },
{ "BCM2E53" },
{ "BCM2E54" },
{ "BCM2E55" },
{ "BCM2E56" },
{ "BCM2E57" },
{ "BCM2E58" },
{ "BCM2E59" },
{ "BCM2E5A" },
{ "BCM2E5B" },
{ "BCM2E5C" },
{ "BCM2E5D" },
{ "BCM2E5E" },
{ "BCM2E5F" },
{ "BCM2E60" },
{ "BCM2E61" },
{ "BCM2E62" },
{ "BCM2E63" },
{ "BCM2E64" },
{ "BCM2E65" },
{ "BCM2E66" },
{ "BCM2E67" },
{ "BCM2E68" },
{ "BCM2E69" },
{ "BCM2E6B" },
{ "BCM2E6D" },
{ "BCM2E6E" },
{ "BCM2E6F" },
{ "BCM2E70" },
{ "BCM2E71" },
{ "BCM2E72" },
{ "BCM2E73" },
{ "BCM2E74", (long)&bcm43430_device_data },
{ "BCM2E75", (long)&bcm43430_device_data },
{ "BCM2E76" },
{ "BCM2E77" },
{ "BCM2E78" },
{ "BCM2E79" },
{ "BCM2E7A" },
{ "BCM2E7B", (long)&bcm43430_device_data },
{ "BCM2E7C" },
{ "BCM2E7D" },
{ "BCM2E7E" },
{ "BCM2E7F" },
{ "BCM2E80", (long)&bcm43430_device_data },
{ "BCM2E81" },
{ "BCM2E82" },
{ "BCM2E83" },
{ "BCM2E84" },
{ "BCM2E85" },
{ "BCM2E86" },
{ "BCM2E87" },
{ "BCM2E88" },
{ "BCM2E89", (long)&bcm43430_device_data },
{ "BCM2E8A" },
{ "BCM2E8B" },
{ "BCM2E8C" },
{ "BCM2E8D" },
{ "BCM2E8E" },
{ "BCM2E90" },
{ "BCM2E92" },
{ "BCM2E93" },
{ "BCM2E94", (long)&bcm43430_device_data },
{ "BCM2E95" },
{ "BCM2E96" },
{ "BCM2E97" },
{ "BCM2E98" },
{ "BCM2E99", (long)&bcm43430_device_data },
{ "BCM2E9A" },
{ "BCM2E9B", (long)&bcm43430_device_data },
{ "BCM2E9C" },
{ "BCM2E9D" },
{ "BCM2E9F", (long)&bcm43430_device_data },
{ "BCM2EA0" },
{ "BCM2EA1" },
{ "BCM2EA2", (long)&bcm43430_device_data },
{ "BCM2EA3", (long)&bcm43430_device_data },
{ "BCM2EA4", (long)&bcm43430_device_data }, /* bcm43455 */
{ "BCM2EA5" },
{ "BCM2EA6" },
{ "BCM2EA7" },
{ "BCM2EA8" },
{ "BCM2EA9" },
{ "BCM2EAA", (long)&bcm43430_device_data },
{ "BCM2EAB", (long)&bcm43430_device_data },
{ "BCM2EAC", (long)&bcm43430_device_data },
{ },
{ .id = "BCM2E00" },
{ .id = "BCM2E01" },
{ .id = "BCM2E02" },
{ .id = "BCM2E03" },
{ .id = "BCM2E04" },
{ .id = "BCM2E05" },
{ .id = "BCM2E06" },
{ .id = "BCM2E07" },
{ .id = "BCM2E08" },
{ .id = "BCM2E09" },
{ .id = "BCM2E0A" },
{ .id = "BCM2E0B" },
{ .id = "BCM2E0C" },
{ .id = "BCM2E0D" },
{ .id = "BCM2E0E" },
{ .id = "BCM2E0F" },
{ .id = "BCM2E10" },
{ .id = "BCM2E11" },
{ .id = "BCM2E12" },
{ .id = "BCM2E13" },
{ .id = "BCM2E14" },
{ .id = "BCM2E15" },
{ .id = "BCM2E16" },
{ .id = "BCM2E17" },
{ .id = "BCM2E18" },
{ .id = "BCM2E19" },
{ .id = "BCM2E1A" },
{ .id = "BCM2E1B" },
{ .id = "BCM2E1C" },
{ .id = "BCM2E1D" },
{ .id = "BCM2E1F" },
{ .id = "BCM2E20" },
{ .id = "BCM2E21" },
{ .id = "BCM2E22" },
{ .id = "BCM2E23" },
{ .id = "BCM2E24" },
{ .id = "BCM2E25" },
{ .id = "BCM2E26" },
{ .id = "BCM2E27" },
{ .id = "BCM2E28" },
{ .id = "BCM2E29" },
{ .id = "BCM2E2A" },
{ .id = "BCM2E2B" },
{ .id = "BCM2E2C" },
{ .id = "BCM2E2D" },
{ .id = "BCM2E2E" },
{ .id = "BCM2E2F" },
{ .id = "BCM2E30" },
{ .id = "BCM2E31" },
{ .id = "BCM2E32" },
{ .id = "BCM2E33" },
{ .id = "BCM2E34" },
{ .id = "BCM2E35" },
{ .id = "BCM2E36" },
{ .id = "BCM2E37" },
{ .id = "BCM2E38" },
{ .id = "BCM2E39" },
{ .id = "BCM2E3A" },
{ .id = "BCM2E3B" },
{ .id = "BCM2E3C" },
{ .id = "BCM2E3D" },
{ .id = "BCM2E3E" },
{ .id = "BCM2E3F" },
{ .id = "BCM2E40" },
{ .id = "BCM2E41" },
{ .id = "BCM2E42" },
{ .id = "BCM2E43" },
{ .id = "BCM2E44" },
{ .id = "BCM2E45" },
{ .id = "BCM2E46" },
{ .id = "BCM2E47" },
{ .id = "BCM2E48" },
{ .id = "BCM2E49" },
{ .id = "BCM2E4A" },
{ .id = "BCM2E4B" },
{ .id = "BCM2E4C" },
{ .id = "BCM2E4D" },
{ .id = "BCM2E4E" },
{ .id = "BCM2E4F" },
{ .id = "BCM2E50" },
{ .id = "BCM2E51" },
{ .id = "BCM2E52" },
{ .id = "BCM2E53" },
{ .id = "BCM2E54" },
{ .id = "BCM2E55" },
{ .id = "BCM2E56" },
{ .id = "BCM2E57" },
{ .id = "BCM2E58" },
{ .id = "BCM2E59" },
{ .id = "BCM2E5A" },
{ .id = "BCM2E5B" },
{ .id = "BCM2E5C" },
{ .id = "BCM2E5D" },
{ .id = "BCM2E5E" },
{ .id = "BCM2E5F" },
{ .id = "BCM2E60" },
{ .id = "BCM2E61" },
{ .id = "BCM2E62" },
{ .id = "BCM2E63" },
{ .id = "BCM2E64" },
{ .id = "BCM2E65" },
{ .id = "BCM2E66" },
{ .id = "BCM2E67" },
{ .id = "BCM2E68" },
{ .id = "BCM2E69" },
{ .id = "BCM2E6B" },
{ .id = "BCM2E6D" },
{ .id = "BCM2E6E" },
{ .id = "BCM2E6F" },
{ .id = "BCM2E70" },
{ .id = "BCM2E71" },
{ .id = "BCM2E72" },
{ .id = "BCM2E73" },
{ .id = "BCM2E74", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E75", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E76" },
{ .id = "BCM2E77" },
{ .id = "BCM2E78" },
{ .id = "BCM2E79" },
{ .id = "BCM2E7A" },
{ .id = "BCM2E7B", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E7C" },
{ .id = "BCM2E7D" },
{ .id = "BCM2E7E" },
{ .id = "BCM2E7F" },
{ .id = "BCM2E80", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E81" },
{ .id = "BCM2E82" },
{ .id = "BCM2E83" },
{ .id = "BCM2E84" },
{ .id = "BCM2E85" },
{ .id = "BCM2E86" },
{ .id = "BCM2E87" },
{ .id = "BCM2E88" },
{ .id = "BCM2E89", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E8A" },
{ .id = "BCM2E8B" },
{ .id = "BCM2E8C" },
{ .id = "BCM2E8D" },
{ .id = "BCM2E8E" },
{ .id = "BCM2E90" },
{ .id = "BCM2E92" },
{ .id = "BCM2E93" },
{ .id = "BCM2E94", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E95" },
{ .id = "BCM2E96" },
{ .id = "BCM2E97" },
{ .id = "BCM2E98" },
{ .id = "BCM2E99", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E9A" },
{ .id = "BCM2E9B", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2E9C" },
{ .id = "BCM2E9D" },
{ .id = "BCM2E9F", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2EA0" },
{ .id = "BCM2EA1" },
{ .id = "BCM2EA2", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2EA3", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2EA4", .driver_data = (long)&bcm43430_device_data }, /* bcm43455 */
{ .id = "BCM2EA5" },
{ .id = "BCM2EA6" },
{ .id = "BCM2EA7" },
{ .id = "BCM2EA8" },
{ .id = "BCM2EA9" },
{ .id = "BCM2EAA", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2EAB", .driver_data = (long)&bcm43430_device_data },
{ .id = "BCM2EAC", .driver_data = (long)&bcm43430_device_data },
{ }
};
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
#endif

View File

@ -194,7 +194,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
return NULL;
}
if (hciextn && chan == 5) {
if (hciextn && chan == 5 && len > HCI_COMMAND_HDR_SIZE) {
__le16 opcode = ((struct hci_command_hdr *)data)->opcode;
/* Vendor specific commands */
@ -402,6 +402,9 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu)
u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
/* spot "conf" pkts and reply with a "conf rsp" pkt */
if (bcsp->rx_skb->len < 8)
return;
if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);

View File

@ -1124,10 +1124,10 @@ static const struct h5_device_data h5_data_rtl8723bs = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id h5_acpi_match[] = {
#ifdef CONFIG_BT_HCIUART_RTL
{ "OBDA0623", (kernel_ulong_t)&h5_data_rtl8723bs },
{ "OBDA8723", (kernel_ulong_t)&h5_data_rtl8723bs },
{ .id = "OBDA0623", .driver_data = (kernel_ulong_t)&h5_data_rtl8723bs },
{ .id = "OBDA8723", .driver_data = (kernel_ulong_t)&h5_data_rtl8723bs },
#endif
{ },
{ }
};
MODULE_DEVICE_TABLE(acpi, h5_acpi_match);
#endif

View File

@ -1057,8 +1057,8 @@ static const struct hci_uart_proto intel_proto = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id intel_acpi_match[] = {
{ "INT33E1", 0 },
{ "INT33E3", 0 },
{ .id = "INT33E1" },
{ .id = "INT33E3" },
{ }
};
MODULE_DEVICE_TABLE(acpi, intel_acpi_match);

View File

@ -163,6 +163,12 @@ static void hci_uart_write_work(struct work_struct *work)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
len = tty->ops->write(tty, skb->data, skb->len);
if (len < 0 || len > skb->len) {
hdev->stat.err_tx++;
kfree_skb(skb);
continue;
}
hdev->stat.byte_tx += len;
skb_pull(skb, len);
@ -756,9 +762,9 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
hu->proto = p;
err = hci_uart_register_dev(hu);
if (err) {
if (err)
return err;
}
set_bit(HCI_UART_PROTO_READY, &hu->flags);
clear_bit(HCI_UART_PROTO_INIT, &hu->flags);

View File

@ -354,9 +354,29 @@ static int nokia_setup_fw(struct hci_uart *hu)
u16 opcode;
struct sk_buff *skb;
if (pkt_size > fw_size - 2) {
err = -EINVAL;
dev_err(dev, "%s: Malformed firmware packet\n",
hu->hdev->name);
goto done;
}
switch (pkt_type) {
case HCI_COMMAND_PKT:
if (pkt_size < 1 + HCI_COMMAND_HDR_SIZE) {
err = -EINVAL;
dev_err(dev, "%s: Malformed firmware command\n",
hu->hdev->name);
goto done;
}
cmd = (struct hci_command_hdr *)(fw_ptr + 3);
if (cmd->plen > pkt_size - 1 - HCI_COMMAND_HDR_SIZE) {
err = -EINVAL;
dev_err(dev, "%s: Truncated firmware command\n",
hu->hdev->name);
goto done;
}
opcode = le16_to_cpu(cmd->opcode);
skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen,

View File

@ -1239,8 +1239,8 @@ static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
* received we store dump into a file before closing hci. This
* dump will help in triaging the issues.
*/
if ((skb->data[0] == HCI_VENDOR_PKT) &&
(get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE))
if (skb->data[0] == HCI_EV_VENDOR &&
get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE)
return qca_controller_memdump_event(hdev, skb);
return hci_recv_frame(hdev, skb);
@ -2792,12 +2792,12 @@ MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
{ "QCOM2066", (kernel_ulong_t)&qca_soc_data_qca2066 },
{ "QCOM6390", (kernel_ulong_t)&qca_soc_data_qca6390 },
{ "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
{ "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
{ "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 },
{ },
{ .id = "QCOM2066", .driver_data = (kernel_ulong_t)&qca_soc_data_qca2066 },
{ .id = "QCOM6390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
{ .id = "DLA16390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
{ .id = "DLB16390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
{ .id = "DLB26390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
{ }
};
MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
#endif

View File

@ -120,9 +120,13 @@ static int virtbt_setup_zephyr(struct hci_dev *hdev)
if (IS_ERR(skb))
return PTR_ERR(skb);
bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
/* Bounded print: the backend controls skb->len. */
if (skb->len > 1) {
int len = skb->len - 1;
hci_set_fw_info(hdev, "%s", skb->data + 1);
bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1));
hci_set_fw_info(hdev, "%.*s", len, skb->data + 1);
}
kfree_skb(skb);
return 0;

View File

@ -101,6 +101,8 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
struct dentry *cxl_debugfs_create_dir(const char *dir);
int cxl_dpa_set_part(struct cxl_endpoint_decoder *cxled,
enum cxl_partition_mode mode);
struct cxl_memdev_state;
int cxl_mem_get_partition_info(struct cxl_memdev_state *mds);
int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size);
int cxl_dpa_free(struct cxl_endpoint_decoder *cxled);
resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled);

View File

@ -1152,7 +1152,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_mem_get_event_records, "CXL");
*
* See CXL @8.2.9.5.2.1 Get Partition Info
*/
static int cxl_mem_get_partition_info(struct cxl_memdev_state *mds)
int cxl_mem_get_partition_info(struct cxl_memdev_state *mds)
{
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
struct cxl_mbox_get_partition_info pi;
@ -1308,55 +1308,6 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
return -EBUSY;
}
static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
{
int i = info->nr_partitions;
if (size == 0)
return;
info->part[i].range = (struct range) {
.start = start,
.end = start + size - 1,
};
info->part[i].mode = mode;
info->nr_partitions++;
}
int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
{
struct cxl_dev_state *cxlds = &mds->cxlds;
struct device *dev = cxlds->dev;
int rc;
if (!cxlds->media_ready) {
info->size = 0;
return 0;
}
info->size = mds->total_bytes;
if (mds->partition_align_bytes == 0) {
add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM);
add_part(info, mds->volatile_only_bytes,
mds->persistent_only_bytes, CXL_PARTMODE_PMEM);
return 0;
}
rc = cxl_mem_get_partition_info(mds);
if (rc) {
dev_err(dev, "Failed to query partition information\n");
return rc;
}
add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM);
add_part(info, mds->active_volatile_bytes, mds->active_persistent_bytes,
CXL_PARTMODE_PMEM);
return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL");
int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
{
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;

View File

@ -594,6 +594,73 @@ bool is_cxl_memdev(const struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "CXL");
static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
{
int i = info->nr_partitions;
if (size == 0)
return;
info->part[i].range = (struct range) {
.start = start,
.end = start + size - 1,
};
info->part[i].mode = mode;
info->nr_partitions++;
}
int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
{
struct cxl_dev_state *cxlds = &mds->cxlds;
struct device *dev = cxlds->dev;
int rc;
if (!cxlds->media_ready) {
info->size = 0;
return 0;
}
info->size = mds->total_bytes;
if (mds->partition_align_bytes == 0) {
add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM);
add_part(info, mds->volatile_only_bytes,
mds->persistent_only_bytes, CXL_PARTMODE_PMEM);
return 0;
}
rc = cxl_mem_get_partition_info(mds);
if (rc) {
dev_err(dev, "Failed to query partition information\n");
return rc;
}
add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM);
add_part(info, mds->active_volatile_bytes, mds->active_persistent_bytes,
CXL_PARTMODE_PMEM);
return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL");
/**
* cxl_set_capacity: initialize dpa by a driver without a mailbox.
*
* @cxlds: pointer to cxl_dev_state
* @capacity: device volatile memory size
*/
int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
{
struct cxl_dpa_info range_info = {
.size = capacity,
};
add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
return cxl_dpa_setup(cxlds, &range_info);
}
EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
/**
* set_exclusive_cxl_commands() - atomically disable user cxl commands
* @mds: The device state to operate on

View File

@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pci-doe.h>
#include <cxl/pci.h>
#include <linux/aer.h>
#include <cxlpci.h>
#include <cxlmem.h>

View File

@ -11,6 +11,7 @@
#include <linux/idr.h>
#include <linux/node.h>
#include <cxl/einj.h>
#include <cxl/pci.h>
#include <cxlmem.h>
#include <cxlpci.h>
#include <cxl.h>

View File

@ -4,6 +4,7 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <cxl/pci.h>
#include <cxlmem.h>
#include <cxlpci.h>
#include <pmu.h>

View File

@ -13,16 +13,6 @@
*/
#define CXL_PCI_DEFAULT_MAX_VECTORS 16
/* Register Block Identifier (RBI) */
enum cxl_regloc_type {
CXL_REGLOC_RBI_EMPTY = 0,
CXL_REGLOC_RBI_COMPONENT,
CXL_REGLOC_RBI_VIRT,
CXL_REGLOC_RBI_MEMDEV,
CXL_REGLOC_RBI_PMU,
CXL_REGLOC_RBI_TYPES
};
/*
* Table Access DOE, CDAT Read Entry Response
*
@ -112,6 +102,4 @@ static inline void devm_cxl_port_ras_setup(struct cxl_port *port)
}
#endif
int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map);
#endif /* __CXL_PCI_H__ */

View File

@ -11,6 +11,7 @@
#include <linux/pci.h>
#include <linux/aer.h>
#include <linux/io.h>
#include <cxl/pci.h>
#include <cxl/mailbox.h>
#include "cxlmem.h"
#include "cxlpci.h"

View File

@ -876,19 +876,29 @@ int
dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
const struct dpll_pin_ops *ops, void *priv)
{
const struct dpll_device_ops *dev_ops;
int ret;
if (WARN_ON(!ops) ||
WARN_ON(!ops->state_on_dpll_get) ||
WARN_ON(!ops->direction_get) ||
WARN_ON(ops->measured_freq_get &&
(!dpll_device_ops(dpll)->freq_monitor_get ||
!dpll_device_ops(dpll)->freq_monitor_set)) ||
WARN_ON(ops->supported_ffo && !ops->ffo_get))
WARN_ON(ops->supported_ffo && !ops->ffo_get) ||
WARN_ON((pin->prop.capabilities &
DPLL_PIN_CAPABILITIES_STATE_CONNECTED_OVERRIDE) &&
!(pin->prop.capabilities &
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE)))
return -EINVAL;
mutex_lock(&dpll_lock);
dev_ops = dpll_device_ops(dpll);
if (WARN_ON(ops->measured_freq_get &&
(!dev_ops || !dev_ops->freq_monitor_get ||
!dev_ops->freq_monitor_set))) {
ret = -EINVAL;
goto out_unlock;
}
/*
* For pins identified via firmware (pin->fwnode), allow registration
* even if the pin's (module, clock_id) differs from the target DPLL.
@ -1081,12 +1091,8 @@ EXPORT_SYMBOL_GPL(dpll_pin_ref_sync_pair_add);
static struct dpll_device_registration *
dpll_device_registration_first(struct dpll_device *dpll)
{
struct dpll_device_registration *reg;
reg = list_first_entry_or_null((struct list_head *)&dpll->registration_list,
struct dpll_device_registration, list);
WARN_ON(!reg);
return reg;
return list_first_entry_or_null((struct list_head *)&dpll->registration_list,
struct dpll_device_registration, list);
}
void *dpll_priv(struct dpll_device *dpll)
@ -1094,6 +1100,8 @@ void *dpll_priv(struct dpll_device *dpll)
struct dpll_device_registration *reg;
reg = dpll_device_registration_first(dpll);
if (!reg)
return NULL;
return reg->priv;
}
@ -1102,6 +1110,8 @@ const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
struct dpll_device_registration *reg;
reg = dpll_device_registration_first(dpll);
if (!reg)
return NULL;
return reg->ops;
}

View File

@ -66,6 +66,22 @@ static bool dpll_pin_available(struct dpll_pin *pin)
return false;
}
static bool dpll_device_registered(struct dpll_device *dpll)
{
return dpll_device_ops(dpll);
}
static struct dpll_pin_ref *dpll_pin_first_registered_ref(struct dpll_pin *pin)
{
struct dpll_pin_ref *ref;
unsigned long i;
xa_for_each(&pin->dpll_refs, i, ref)
if (dpll_device_registered(ref->dpll))
return ref;
return NULL;
}
/**
* dpll_msg_add_pin_handle - attach pin handle attribute to a given message
* @msg: pointer to sk_buff message to attach a pin handle
@ -656,6 +672,8 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
int ret;
xa_for_each(&pin->dpll_refs, index, ref) {
if (!dpll_device_registered(ref->dpll))
continue;
attr = nla_nest_start(msg, DPLL_A_PIN_PARENT_DEVICE);
if (!attr)
return -EMSGSIZE;
@ -700,9 +718,10 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
int ret;
ref = dpll_pin_own_dpll_ref_first(pin);
if (!ref || !dpll_device_registered(ref->dpll))
ref = dpll_pin_first_registered_ref(pin);
if (!ref)
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
ASSERT_NOT_NULL(ref);
return -ENODEV;
ret = dpll_msg_add_pin_handle(msg, pin);
if (ret)
@ -1079,10 +1098,9 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
struct netlink_ext_ack *extack)
{
u64 freq = nla_get_u64(a), old_freq;
struct dpll_pin_ref *ref, *failed;
const struct dpll_pin_ops *ops;
struct dpll_pin_ref *ref;
struct dpll_device *dpll;
unsigned long i;
int ret;
if (!dpll_pin_is_freq_supported(pin, freq)) {
@ -1090,22 +1108,17 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
return -EINVAL;
}
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
if ((!ops->frequency_set || !ops->frequency_get) &&
ref->dpll->module == pin->module &&
ref->dpll->clock_id == pin->clock_id) {
NL_SET_ERR_MSG(extack,
"frequency set not supported by the device");
return -EOPNOTSUPP;
}
}
ref = dpll_pin_own_dpll_ref_first(pin);
if (!ref) {
if (!ref || !dpll_device_registered(ref->dpll)) {
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
return -ENODEV;
}
ops = dpll_pin_ops(ref);
if (!ops->frequency_set || !ops->frequency_get) {
NL_SET_ERR_MSG(extack,
"frequency set not supported by the device");
return -EOPNOTSUPP;
}
dpll = ref->dpll;
ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
dpll_priv(dpll), &old_freq, extack);
@ -1116,68 +1129,42 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
if (freq == old_freq)
return 0;
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
if (!ops->frequency_set)
continue;
dpll = ref->dpll;
ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), freq, extack);
if (ret) {
failed = ref;
NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",
dpll->id);
goto rollback;
}
ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), freq, extack);
if (ret) {
NL_SET_ERR_MSG_FMT(extack,
"frequency set failed for dpll_id:%u",
dpll->id);
return ret;
}
__dpll_pin_change_ntf(pin);
return 0;
rollback:
xa_for_each(&pin->dpll_refs, i, ref) {
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
if (!ops->frequency_set)
continue;
dpll = ref->dpll;
if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), old_freq, extack))
NL_SET_ERR_MSG(extack, "set frequency rollback failed");
}
return ret;
}
static int
dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
struct netlink_ext_ack *extack)
{
struct dpll_pin_ref *ref, *failed;
const struct dpll_pin_ops *ops;
struct dpll_pin_esync esync;
u64 freq = nla_get_u64(a);
struct dpll_pin_ref *ref;
struct dpll_device *dpll;
bool supported = false;
unsigned long i;
int ret;
int ret, i;
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
if ((!ops->esync_set || !ops->esync_get) &&
ref->dpll->module == pin->module &&
ref->dpll->clock_id == pin->clock_id) {
NL_SET_ERR_MSG(extack,
"embedded sync feature is not supported by this device");
return -EOPNOTSUPP;
}
}
ref = dpll_pin_own_dpll_ref_first(pin);
if (!ref) {
if (!ref || !dpll_device_registered(ref->dpll)) {
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
return -ENODEV;
}
ops = dpll_pin_ops(ref);
if (!ops->esync_set || !ops->esync_get) {
NL_SET_ERR_MSG(extack,
"embedded sync feature is not supported by this device");
return -EOPNOTSUPP;
}
dpll = ref->dpll;
ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
dpll_priv(dpll), &esync, extack);
@ -1196,44 +1183,17 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
return -EINVAL;
}
xa_for_each(&pin->dpll_refs, i, ref) {
void *pin_dpll_priv;
ops = dpll_pin_ops(ref);
if (!ops->esync_set)
continue;
dpll = ref->dpll;
pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
freq, extack);
if (ret) {
failed = ref;
NL_SET_ERR_MSG_FMT(extack,
"embedded sync frequency set failed for dpll_id: %u",
dpll->id);
goto rollback;
}
ret = ops->esync_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
dpll_priv(dpll), freq, extack);
if (ret) {
NL_SET_ERR_MSG_FMT(extack,
"embedded sync frequency set failed for dpll_id: %u",
dpll->id);
return ret;
}
__dpll_pin_change_ntf(pin);
return 0;
rollback:
xa_for_each(&pin->dpll_refs, i, ref) {
void *pin_dpll_priv;
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
if (!ops->esync_set)
continue;
dpll = ref->dpll;
pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
esync.freq, extack))
NL_SET_ERR_MSG(extack, "set embedded sync frequency rollback failed");
}
return ret;
}
static int
@ -1241,14 +1201,12 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
unsigned long ref_sync_pin_idx,
const enum dpll_pin_state state,
struct netlink_ext_ack *extack)
{
struct dpll_pin_ref *ref, *failed;
const struct dpll_pin_ops *ops;
enum dpll_pin_state old_state;
struct dpll_pin *ref_sync_pin;
struct dpll_pin_ref *ref;
struct dpll_device *dpll;
unsigned long i;
int ret;
ref_sync_pin = xa_find(&pin->ref_sync_pins, &ref_sync_pin_idx,
@ -1262,7 +1220,7 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
return -EINVAL;
}
ref = dpll_pin_own_dpll_ref_first(pin);
if (!ref) {
if (!ref || !dpll_device_registered(ref->dpll)) {
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
return -ENODEV;
}
@ -1282,42 +1240,20 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
}
if (state == old_state)
return 0;
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
if (!ops->ref_sync_set)
continue;
dpll = ref->dpll;
ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
ref_sync_pin,
dpll_pin_on_dpll_priv(dpll,
ref_sync_pin),
state, extack);
if (ret) {
failed = ref;
NL_SET_ERR_MSG_FMT(extack, "reference sync set failed for dpll_id:%u",
dpll->id);
goto rollback;
}
ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
ref_sync_pin,
dpll_pin_on_dpll_priv(dpll, ref_sync_pin),
state, extack);
if (ret) {
NL_SET_ERR_MSG_FMT(extack,
"reference sync set failed for dpll_id:%u",
dpll->id);
return ret;
}
__dpll_pin_change_ntf(pin);
return 0;
rollback:
xa_for_each(&pin->dpll_refs, i, ref) {
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
if (!ops->ref_sync_set)
continue;
dpll = ref->dpll;
if (ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
ref_sync_pin,
dpll_pin_on_dpll_priv(dpll, ref_sync_pin),
old_state, extack))
NL_SET_ERR_MSG(extack, "set reference sync rollback failed");
}
return ret;
}
static int
@ -1478,11 +1414,10 @@ static int
dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
struct netlink_ext_ack *extack)
{
struct dpll_pin_ref *ref, *failed;
const struct dpll_pin_ops *ops;
s32 phase_adj, old_phase_adj;
struct dpll_pin_ref *ref;
struct dpll_device *dpll;
unsigned long i;
int ret;
phase_adj = nla_get_s32(phase_adj_attr);
@ -1499,21 +1434,16 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
return -EINVAL;
}
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
if ((!ops->phase_adjust_set || !ops->phase_adjust_get) &&
ref->dpll->module == pin->module &&
ref->dpll->clock_id == pin->clock_id) {
NL_SET_ERR_MSG(extack, "phase adjust not supported");
return -EOPNOTSUPP;
}
}
ref = dpll_pin_own_dpll_ref_first(pin);
if (!ref) {
if (!ref || !dpll_device_registered(ref->dpll)) {
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
return -ENODEV;
}
ops = dpll_pin_ops(ref);
if (!ops->phase_adjust_set || !ops->phase_adjust_get) {
NL_SET_ERR_MSG(extack, "phase adjust not supported");
return -EOPNOTSUPP;
}
dpll = ref->dpll;
ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), &old_phase_adj,
@ -1525,41 +1455,17 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
if (phase_adj == old_phase_adj)
return 0;
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
if (!ops->phase_adjust_set)
continue;
dpll = ref->dpll;
ret = ops->phase_adjust_set(pin,
dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), phase_adj,
extack);
if (ret) {
failed = ref;
NL_SET_ERR_MSG_FMT(extack,
"phase adjust set failed for dpll_id:%u",
dpll->id);
goto rollback;
}
ret = ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), phase_adj, extack);
if (ret) {
NL_SET_ERR_MSG_FMT(extack,
"phase adjust set failed for dpll_id:%u",
dpll->id);
return ret;
}
__dpll_pin_change_ntf(pin);
return 0;
rollback:
xa_for_each(&pin->dpll_refs, i, ref) {
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
if (!ops->phase_adjust_set)
continue;
dpll = ref->dpll;
if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), old_phase_adj,
extack))
NL_SET_ERR_MSG(extack, "set phase adjust rollback failed");
}
return ret;
}
static int
@ -1581,7 +1487,7 @@ dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
return -EINVAL;
}
pdpll_idx = nla_get_u32(tb[DPLL_A_PIN_PARENT_ID]);
dpll = xa_load(&dpll_device_xa, pdpll_idx);
dpll = dpll_device_get_by_id(pdpll_idx);
if (!dpll) {
NL_SET_ERR_MSG(extack, "parent device not found");
return -EINVAL;
@ -1873,6 +1779,10 @@ int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
ret = dpll_cmd_pin_get_one(skb, pin, cb->extack);
if (ret) {
genlmsg_cancel(skb, hdr);
if (ret == -ENODEV) {
ret = 0;
continue;
}
break;
}
genlmsg_end(skb, hdr);

View File

@ -61,7 +61,7 @@ static const struct nla_policy dpll_pin_id_get_nl_policy[DPLL_A_PIN_TYPE + 1] =
[DPLL_A_PIN_BOARD_LABEL] = { .type = NLA_NUL_STRING, },
[DPLL_A_PIN_PANEL_LABEL] = { .type = NLA_NUL_STRING, },
[DPLL_A_PIN_PACKAGE_LABEL] = { .type = NLA_NUL_STRING, },
[DPLL_A_PIN_TYPE] = NLA_POLICY_RANGE(NLA_U32, 1, 5),
[DPLL_A_PIN_TYPE] = NLA_POLICY_RANGE(NLA_U32, 1, 6),
};
/* DPLL_CMD_PIN_GET - do */

View File

@ -2,7 +2,7 @@
config ZL3073X
tristate "Microchip Azurite DPLL/PTP/SyncE devices" if COMPILE_TEST
depends on NET
depends on NET && PTP_1588_CLOCK
select DPLL
select NET_DEVLINK
select REGMAP
@ -16,7 +16,7 @@ config ZL3073X
config ZL3073X_I2C
tristate "I2C bus implementation for Microchip Azurite devices"
depends on I2C && NET
depends on I2C && NET && PTP_1588_CLOCK
select REGMAP_I2C
select ZL3073X
help
@ -28,7 +28,7 @@ config ZL3073X_I2C
config ZL3073X_SPI
tristate "SPI bus implementation for Microchip Azurite devices"
depends on NET && SPI
depends on NET && SPI && PTP_1588_CLOCK
select REGMAP_SPI
select ZL3073X
help

View File

@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/string.h>
#include <linux/types.h>
@ -31,9 +33,18 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
if (rc)
return rc;
/* Read df_offset vs tracked reference */
/* Read df_offset only when locked to a reference. In NCO mode
* df_offset was captured at entry by nco_mode_set() - preserve it.
*/
if (!zl3073x_chan_is_locked(chan)) {
if (!zl3073x_chan_mode_is_nco(chan))
chan->df_offset = ZL_DPLL_DF_OFFSET_UNKNOWN;
return 0;
}
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index),
ZL_DPLL_DF_READ_SEM);
ZL_DPLL_DF_READ_SEM,
ZL_POLL_DF_READ_TIMEOUT_US);
if (rc)
return rc;
@ -43,7 +54,8 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
return rc;
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index),
ZL_DPLL_DF_READ_SEM);
ZL_DPLL_DF_READ_SEM,
ZL_POLL_DF_READ_TIMEOUT_US);
if (rc)
return rc;
@ -56,13 +68,103 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
return 0;
}
/**
* zl3073x_chan_nco_mode_set - switch DPLL channel to NCO mode
* @zldev: pointer to zl3073x_dev structure
* @index: DPLL channel index
*
* Switches the channel to NCO mode and reads the df_offset
* auto-captured by nco_auto_read directly from the register.
* No DF_READ handshake is needed as nco_auto_read populates
* the register before the mode switch completes.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_chan *chan = &zldev->chan[index];
u8 prev_mode, df_read;
u64 val;
int rc;
prev_mode = zl3073x_chan_mode_get(chan);
/* nco_auto_read captures the tracking offset at NCO entry only
* from reflock, auto or holdover mode. From freerun the captured
* value is not meaningful.
*/
if (prev_mode == ZL_DPLL_MODE_REFSEL_MODE_FREERUN) {
zl3073x_chan_mode_set(chan, ZL_DPLL_MODE_REFSEL_MODE_NCO);
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
chan->mode_refsel);
if (rc) {
zl3073x_chan_mode_set(chan, prev_mode);
return rc;
}
chan->df_offset = ZL_DPLL_DF_OFFSET_UNKNOWN;
return 0;
}
/* Configure df_read for nco_auto_read:
* ref_ofst=0 - reads offset relative to master clock (not input ref)
* cmd=CMD_ACC_I - accumulated I-part covering both locked and
* holdover entry.
*
* No semaphore is set - this only configures what the df_offset
* value represents after the mode switch; nco_auto_read performs
* the actual read automatically.
*/
df_read = FIELD_PREP(ZL_DPLL_DF_READ_REF_OFST, 0) |
FIELD_PREP(ZL_DPLL_DF_READ_CMD, ZL_DPLL_DF_READ_CMD_ACC_I);
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_DF_READ(index), df_read);
if (rc)
return rc;
/* Wait for df_read configuration to take effect before
* triggering nco_auto_read via mode switch. The worst-case
* internal register update time is 25 ms.
*/
fsleep(25000);
zl3073x_chan_mode_set(chan, ZL_DPLL_MODE_REFSEL_MODE_NCO);
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
chan->mode_refsel);
if (rc) {
zl3073x_chan_mode_set(chan, prev_mode);
return rc;
}
/* Wait for nco_auto_read to populate df_offset. The worst-case
* internal register update time is 25 ms.
*/
fsleep(25000);
/* Read df_offset captured by nco_auto_read during mode switch.
* No DF_READ semaphore handshake needed. Mode switch already
* succeeded, so don't propagate a read failure back to userspace.
*/
rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_DF_OFFSET(index), &val);
if (rc) {
dev_warn(zldev->dev,
"Failed to read DPLL%u df_offset: %pe\n",
index, ERR_PTR(rc));
chan->df_offset = ZL_DPLL_DF_OFFSET_UNKNOWN;
} else {
chan->df_offset = sign_extend64(val, 47);
}
return 0;
}
/**
* zl3073x_chan_state_fetch - fetch DPLL channel state from hardware
* @zldev: pointer to zl3073x_dev structure
* @index: DPLL channel index to fetch state for
*
* Reads the mode_refsel register and reference priority registers for
* the given DPLL channel and stores the raw values for later use.
* Reads the mode_refsel, status and reference priority registers for
* the given DPLL channel and stores the values for later use.
*
* Return: 0 on success, <0 on error
*/
@ -71,6 +173,10 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
struct zl3073x_chan *chan = &zldev->chan[index];
int rc, i;
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_CTRL(index), &chan->ctrl);
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
&chan->mode_refsel);
if (rc)
@ -83,6 +189,13 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
if (rc)
return rc;
/* If firmware left the channel in NCO mode, mark df_offset as
* unknown - we cannot know whether the preconditions for a valid
* nco_auto_read capture were met.
*/
if (zl3073x_chan_mode_is_nco(chan))
chan->df_offset = ZL_DPLL_DF_OFFSET_UNKNOWN;
dev_dbg(zldev->dev,
"DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n",
index, zl3073x_chan_lock_state_get(chan),
@ -122,6 +235,322 @@ const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
return &zldev->chan[index];
}
/**
* zl3073x_chan_tod_ready_wait - wait for ToD semaphore to clear
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
*
* Checks the ToD control register semaphore bit. If clear, returns
* immediately. Otherwise polls until the bit is cleared by the device.
*
* Return:
* * 0 - success
* * %-EBUSY - timeout
* * %-EOPNOTSUPP - unknown command detected
* * negative - other error
*/
int zl3073x_chan_tod_ready_wait(struct zl3073x_dev *zldev, u8 ch)
{
unsigned int timeout;
u8 tod_ctrl;
int rc;
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch), &tod_ctrl);
if (rc)
return rc;
if (!(tod_ctrl & ZL_DPLL_TOD_CTRL_SEM))
return 0;
switch (FIELD_GET(ZL_DPLL_TOD_CTRL_CMD, tod_ctrl)) {
case ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ:
timeout = ZL_POLL_TOD_WR_TIMEOUT_US;
break;
case ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT:
case ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ:
timeout = ZL_POLL_TOD_RD_TIMEOUT_US;
break;
default:
return -EOPNOTSUPP;
}
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),
ZL_DPLL_TOD_CTRL_SEM, timeout);
return rc == -ETIMEDOUT ? -EBUSY : rc;
}
/**
* zl3073x_chan_tod_ctrl - issue ToD command
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
* @cmd: ToD command to execute
*
* Writes the semaphore and command to dpll_tod_ctrl. The caller must
* ensure the device is ready (semaphore clear) before calling and
* must wait for completion if needed.
*
* Return: 0 on success, <0 on error
*/
static int zl3073x_chan_tod_ctrl(struct zl3073x_dev *zldev, u8 ch, u8 cmd)
{
return zl3073x_write_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),
ZL_DPLL_TOD_CTRL_SEM | cmd);
}
/**
* zl3073x_chan_tod_read - read ToD registers after issuing a command
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
* @next_hz: if true, read predicted ToD at next 1 Hz; otherwise read current
* @ts: timespec to store the result
* @sts: optional system timestamp pair for cross-timestamping
*
* Context: Caller must serialize all zl3073x_chan_tod_* calls externally.
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,
bool next_hz, struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
u32 nsec;
u64 sec;
u8 cmd;
int rc;
if (next_hz)
cmd = ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ;
else
cmd = ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT;
/* Wait for any previous ToD operation to complete */
rc = zl3073x_chan_tod_ready_wait(zldev, ch);
if (rc)
return rc;
ptp_read_system_prets(sts);
rc = zl3073x_chan_tod_ctrl(zldev, ch, cmd);
if (rc)
return rc;
rc = zl3073x_chan_tod_ready_wait(zldev, ch);
if (rc)
return rc;
ptp_read_system_postts(sts);
rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), &sec);
if (rc)
return rc;
/* HW nanoseconds are always in [0, NSEC_PER_SEC) range */
rc = zl3073x_read_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), &nsec);
if (rc)
return rc;
ts->tv_sec = sec;
ts->tv_nsec = nsec;
return 0;
}
/**
* zl3073x_chan_tod_write - write ToD registers and trigger 1 Hz update
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
* @ts: time to set
*
* Context: Caller must serialize all zl3073x_chan_tod_* calls externally.
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,
struct timespec64 ts)
{
int rc;
/* Wait for any previous ToD operation to complete */
rc = zl3073x_chan_tod_ready_wait(zldev, ch);
if (rc)
return rc;
rc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), ts.tv_sec);
if (rc)
return rc;
rc = zl3073x_write_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), ts.tv_nsec);
if (rc)
return rc;
return zl3073x_chan_tod_ctrl(zldev, ch,
ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ);
}
/**
* zl3073x_chan_tod_adjust - atomic ToD read-modify-write with rollover guard
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
* @delta: time adjustment to apply
*
* Reads the next-Hz ToD and current ToD, then checks whether enough time
* remains before the next 1 Hz rollover to safely complete the write.
* Re-reads if the 1 Hz tick crossed between the two reads or if less
* than 20 ms remains before the next rollover. Applies @delta and writes
* the result back.
*
* Context: Caller must serialize all zl3073x_chan_tod_* calls externally.
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,
struct timespec64 delta)
{
#define ZL_TOD_MAX_RETRIES 20
static const long threshold_ns = 20 * NSEC_PER_MSEC;
struct timespec64 ts_next, ts_cur, diff;
int rc, i;
for (i = 0; i < ZL_TOD_MAX_RETRIES; i++) {
rc = zl3073x_chan_tod_read(zldev, ch, true, &ts_next, NULL);
if (rc)
return rc;
rc = zl3073x_chan_tod_read(zldev, ch, false, &ts_cur, NULL);
if (rc)
return rc;
/* Ensure the 1 Hz tick did not cross between the two reads
* and that enough margin remains to complete the write.
*/
diff = timespec64_sub(ts_next, ts_cur);
if (diff.tv_sec > 0 ||
(!diff.tv_sec && diff.tv_nsec >= threshold_ns))
break;
}
if (i == ZL_TOD_MAX_RETRIES) {
dev_warn(zldev->dev,
"DPLL%u ToD adjust failed to get stable margin\n",
ch);
return -EBUSY;
}
/* Apply delta to the next-Hz ToD */
ts_next = timespec64_add(ts_next, delta);
if (!timespec64_valid_settod(&ts_next))
return -EINVAL;
return zl3073x_chan_tod_write(zldev, ch, ts_next);
#undef ZL_TOD_MAX_RETRIES
}
/**
* zl3073x_chan_df_offset_set - write delta frequency offset to hardware
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
* @offset: frequency offset in 2^-48 steps
*
* Context: Caller must hold the per-DPLL lock.
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset)
{
int rc;
rc = zl3073x_write_u48(zldev, ZL_REG_DPLL_DF_OFFSET(ch), offset);
if (!rc)
zldev->chan[ch].df_offset = offset;
return rc;
}
/**
* zl3073x_chan_tie_write - adjust DPLL phase using TIE write
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
* @delta_ns: phase adjustment in nanoseconds (must be in (-1s, 1s))
*
* Converts nanoseconds to TIE units (0.01 ps) and writes TIE data
* to the specified channel.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns)
{
s64 tie_data;
int rc;
guard(mutex)(&zldev->tie_lock);
/* Wait for any previous TIE operation to complete */
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TIE_CTRL,
ZL_DPLL_TIE_CTRL_OP,
ZL_POLL_TIE_WR_TIMEOUT_US);
if (rc)
return rc;
/* Convert ns to TIE units (0.01 ps = 10^-14 s) */
tie_data = delta_ns * 100000LL;
rc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TIE_DATA(ch), tie_data);
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL_MASK, BIT(ch));
if (rc)
return rc;
return zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL,
ZL_DPLL_TIE_CTRL_OP_WR);
}
/**
* zl3073x_chan_phase_step - execute one output phase step operation
* @zldev: pointer to zl3073x device
* @ch: DPLL channel index
* @out_mask: bitmask of outputs to step
* @step_cycles: phase step in synthesizer clock cycles
* @tod_step: also step the ToD counter
*
* All masked outputs must use synthesizers of the same frequency since
* the step value is in synthesizer clock cycles.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,
u16 out_mask, s32 step_cycles,
bool tod_step)
{
u8 ctrl;
int rc;
guard(mutex)(&zldev->phase_step_lock);
/* Wait for any previous phase step operation to complete */
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL,
ZL_OUTPUT_PHASE_STEP_CTRL_OP,
ZL_POLL_PHASE_STEP_TIMEOUT_US);
if (rc)
return rc;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_STEP_DATA,
step_cycles);
if (rc)
return rc;
rc = zl3073x_write_u16(zldev, ZL_REG_OUTPUT_PHASE_STEP_MASK, out_mask);
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_NUMBER, 1);
if (rc)
return rc;
ctrl = FIELD_PREP(ZL_OUTPUT_PHASE_STEP_CTRL_DPLL, ch) |
FIELD_PREP(ZL_OUTPUT_PHASE_STEP_CTRL_OP,
ZL_OUTPUT_PHASE_STEP_CTRL_OP_WRITE);
if (tod_step)
ctrl |= ZL_OUTPUT_PHASE_STEP_CTRL_TOD_STEP;
return zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL, ctrl);
}
/**
* zl3073x_chan_state_set - commit DPLL channel state changes to hardware
* @zldev: pointer to zl3073x_dev structure
@ -145,7 +574,15 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg)))
return 0;
/* Direct register write for mode_refsel */
/* Direct register writes for ctrl and mode_refsel */
if (dchan->ctrl != chan->ctrl) {
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_CTRL(index),
chan->ctrl);
if (rc)
return rc;
dchan->ctrl = chan->ctrl;
}
if (dchan->mode_refsel != chan->mode_refsel) {
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
chan->mode_refsel);

View File

@ -5,14 +5,17 @@
#include <linux/bitfield.h>
#include <linux/stddef.h>
#include <linux/time64.h>
#include <linux/types.h>
#include "regs.h"
struct ptp_system_timestamp;
struct zl3073x_dev;
/**
* struct zl3073x_chan - DPLL channel state
* @ctrl: DPLL control register value
* @mode_refsel: mode and reference selection register value
* @ref_prio: reference priority registers (4 bits per ref, P/N packed)
* @mon_status: monitor status register value
@ -21,6 +24,7 @@ struct zl3073x_dev;
*/
struct zl3073x_chan {
struct_group(cfg,
u8 ctrl;
u8 mode_refsel;
u8 ref_prio[ZL3073X_NUM_REFS / 2];
);
@ -38,6 +42,22 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_chan *chan);
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index);
int zl3073x_chan_tod_ready_wait(struct zl3073x_dev *zldev, u8 ch);
int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,
bool next_hz, struct timespec64 *ts,
struct ptp_system_timestamp *sts);
int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,
struct timespec64 ts);
int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,
struct timespec64 delta);
int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,
u16 out_mask, s32 step_cycles, bool tod_step);
int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset);
int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns);
/**
* zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference
@ -152,6 +172,66 @@ static inline u8 zl3073x_chan_lock_state_get(const struct zl3073x_chan *chan)
return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status);
}
/**
* zl3073x_chan_is_locked - check if channel is locked to a reference
* @chan: pointer to channel state
*
* Return: true if channel is locked, false otherwise
*/
static inline bool zl3073x_chan_is_locked(const struct zl3073x_chan *chan)
{
u8 lock_state = zl3073x_chan_lock_state_get(chan);
return lock_state == ZL_DPLL_MON_STATUS_STATE_LOCK;
}
/**
* zl3073x_chan_mode_is_auto - check if channel is in automatic mode
* @chan: pointer to channel state
*
* Return: true if channel is in automatic mode, false otherwise
*/
static inline bool zl3073x_chan_mode_is_auto(const struct zl3073x_chan *chan)
{
return zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_AUTO;
}
/**
* zl3073x_chan_mode_is_nco - check if channel is in NCO mode
* @chan: pointer to channel state
*
* Return: true if channel is in NCO mode, false otherwise
*/
static inline bool zl3073x_chan_mode_is_nco(const struct zl3073x_chan *chan)
{
return zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_NCO;
}
/**
* zl3073x_chan_mode_is_reflock - check if channel is in reflock mode
* @chan: pointer to channel state
*
* Return: true if channel is in reflock mode, false otherwise
*/
static inline bool zl3073x_chan_mode_is_reflock(const struct zl3073x_chan *chan)
{
return zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;
}
/**
* zl3073x_chan_mode_supports_tie - check if channel mode supports TIE write
* @chan: pointer to channel state
*
* TIE write is supported in AUTO and REFLOCK modes regardless of lock state.
*
* Return: true if TIE write is supported, false otherwise
*/
static inline bool
zl3073x_chan_mode_supports_tie(const struct zl3073x_chan *chan)
{
return zl3073x_chan_mode_is_auto(chan) ||
zl3073x_chan_mode_is_reflock(chan);
}
/**
* zl3073x_chan_is_ho_ready - check if holdover is ready
* @chan: pointer to channel state

View File

@ -25,6 +25,7 @@
static const struct zl3073x_chip_info zl3073x_chip_ids[] = {
ZL_CHIP_INFO(0x0E30, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E3B, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E93, 1, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E94, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
@ -311,17 +312,17 @@ int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val)
* @zldev: zl3073x device pointer
* @reg: register to poll (has to be 8bit register)
* @mask: bit mask for polling
* @timeout_us: timeout in microseconds
*
* Waits for bits specified by @mask in register @reg value to be cleared
* by the device.
*
* Returns: 0 on success, <0 on error
*/
int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask)
int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg,
u8 mask, unsigned int timeout_us)
{
/* Register polling sleep & timeout */
#define ZL_POLL_SLEEP_US 10
#define ZL_POLL_TIMEOUT_US 2000000
unsigned int sleep_us = timeout_us / 50;
unsigned int val;
/* Check the register is 8bit */
@ -335,7 +336,7 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask)
reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET;
return regmap_read_poll_timeout(zldev->regmap, reg, val, !(val & mask),
ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US);
sleep_us, timeout_us);
}
int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
@ -354,7 +355,8 @@ int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
return rc;
/* Wait for the operation to actually finish */
return zl3073x_poll_zero_u8(zldev, op_reg, op_val);
return zl3073x_poll_zero_u8(zldev, op_reg, op_val,
ZL_POLL_MB_TIMEOUT_US);
}
/**
@ -377,8 +379,8 @@ zl3073x_do_hwreg_op(struct zl3073x_dev *zldev, u8 op)
return rc;
/* Poll for completion - pending bit cleared */
return zl3073x_poll_zero_u8(zldev, ZL_REG_HWREG_OP,
ZL_HWREG_OP_PENDING);
return zl3073x_poll_zero_u8(zldev, ZL_REG_HWREG_OP, ZL_HWREG_OP_PENDING,
ZL_POLL_HWREG_TIMEOUT_US);
}
/**
@ -509,6 +511,11 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
int rc;
u8 i;
rc = zl3073x_read_u16(zldev, ZL_REG_OUTPUT_STEP_TIME_MASK,
&zldev->out_step_time_mask);
if (rc)
return rc;
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
rc = zl3073x_ref_state_fetch(zldev, i);
if (rc) {
@ -566,19 +573,7 @@ zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
}
}
static void
zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev)
{
int i, rc;
for (i = 0; i < zldev->info->num_channels; i++) {
rc = zl3073x_chan_state_update(zldev, i);
if (rc)
dev_warn(zldev->dev,
"Failed to get DPLL%u state: %pe\n", i,
ERR_PTR(rc));
}
}
/**
* zl3073x_ref_phase_offsets_update - update reference phase offsets
@ -609,7 +604,8 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
* to be zero to ensure that the measured data are coherent.
*/
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST,
ZL_REF_PHASE_ERR_READ_RQST_RD);
ZL_REF_PHASE_ERR_READ_RQST_RD,
ZL_POLL_PHASE_ERR_TIMEOUT_US);
if (rc)
return rc;
@ -628,7 +624,8 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
/* Wait for finish */
return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST,
ZL_REF_PHASE_ERR_READ_RQST_RD);
ZL_REF_PHASE_ERR_READ_RQST_RD,
ZL_POLL_PHASE_ERR_TIMEOUT_US);
}
/**
@ -648,7 +645,8 @@ zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
/* Wait for previous measurement to finish */
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
ZL_REF_FREQ_MEAS_CTRL,
ZL_POLL_FREQ_MEAS_TIMEOUT_US);
if (rc)
return rc;
@ -669,7 +667,8 @@ zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
/* Wait for finish */
return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
ZL_REF_FREQ_MEAS_CTRL,
ZL_POLL_FREQ_MEAS_TIMEOUT_US);
}
/**
@ -715,9 +714,6 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
/* Update input references' states */
zl3073x_dev_ref_states_update(zldev);
/* Update DPLL channels' states */
zl3073x_dev_chan_states_update(zldev);
/* Update DPLL-to-connected-ref phase offsets registers */
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
if (rc)
@ -727,7 +723,7 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
/* Update measured input reference frequencies if frequency
* monitoring is enabled.
*/
if (zldev->freq_monitor) {
if (READ_ONCE(zldev->freq_monitor)) {
rc = zl3073x_ref_freq_meas_update(zldev);
if (rc)
dev_warn(zldev->dev,
@ -763,7 +759,7 @@ int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
return rc;
/* Save the new factor */
zldev->phase_avg_factor = factor;
WRITE_ONCE(zldev->phase_avg_factor, factor);
return 0;
}
@ -1043,6 +1039,14 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev)
* and/or polls are required to be done atomically.
*/
rc = devm_mutex_init(zldev->dev, &zldev->multiop_lock);
if (rc)
return dev_err_probe(zldev->dev, rc,
"Failed to initialize mutex\n");
rc = devm_mutex_init(zldev->dev, &zldev->phase_step_lock);
if (rc)
return dev_err_probe(zldev->dev, rc,
"Failed to initialize mutex\n");
rc = devm_mutex_init(zldev->dev, &zldev->tie_lock);
if (rc)
return dev_err_probe(zldev->dev, rc,
"Failed to initialize mutex\n");

View File

@ -7,6 +7,7 @@
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/time64.h>
#include <linux/types.h>
#include "chan.h"
@ -19,6 +20,16 @@ struct device;
struct regmap;
struct zl3073x_dpll;
/* Per-operation poll timeouts */
#define ZL_POLL_DF_READ_TIMEOUT_US (25 * USEC_PER_MSEC)
#define ZL_POLL_FREQ_MEAS_TIMEOUT_US (50 * USEC_PER_MSEC)
#define ZL_POLL_HWREG_TIMEOUT_US (50 * USEC_PER_MSEC)
#define ZL_POLL_MB_TIMEOUT_US (30 * USEC_PER_MSEC)
#define ZL_POLL_PHASE_ERR_TIMEOUT_US (50 * USEC_PER_MSEC)
#define ZL_POLL_PHASE_STEP_TIMEOUT_US (3000 * USEC_PER_MSEC)
#define ZL_POLL_TIE_WR_TIMEOUT_US (1000 * USEC_PER_MSEC)
#define ZL_POLL_TOD_RD_TIMEOUT_US (30 * USEC_PER_MSEC)
#define ZL_POLL_TOD_WR_TIMEOUT_US (1000 * USEC_PER_MSEC)
enum zl3073x_flags {
ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
@ -48,6 +59,8 @@ struct zl3073x_chip_info {
* @regmap: regmap to access device registers
* @info: detected chip info
* @multiop_lock: to serialize multiple register operations
* @tie_lock: to serialize TIE write operations
* @phase_step_lock: to serialize output phase step operations
* @ref: array of input references' invariants
* @out: array of outs' invariants
* @synth: array of synths' invariants
@ -56,6 +69,7 @@ struct zl3073x_chip_info {
* @kworker: thread for periodic work
* @work: periodic work
* @clock_id: clock id of the device
* @out_step_time_mask: output step-time mask (device-global)
* @phase_avg_factor: phase offset measurement averaging factor
* @freq_monitor: is frequency monitor enabled
*/
@ -64,6 +78,8 @@ struct zl3073x_dev {
struct regmap *regmap;
const struct zl3073x_chip_info *info;
struct mutex multiop_lock;
struct mutex tie_lock;
struct mutex phase_step_lock;
/* Invariants */
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
@ -80,6 +96,7 @@ struct zl3073x_dev {
/* Per-chip parameters */
u64 clock_id;
u16 out_step_time_mask;
u8 phase_avg_factor;
bool freq_monitor;
};
@ -94,7 +111,7 @@ void zl3073x_dev_stop(struct zl3073x_dev *zldev);
static inline u8 zl3073x_dev_phase_avg_factor_get(struct zl3073x_dev *zldev)
{
return zldev->phase_avg_factor;
return READ_ONCE(zldev->phase_avg_factor);
}
int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor);
@ -127,7 +144,8 @@ struct zl3073x_hwreg_seq_item {
int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
unsigned int mask_reg, u16 mask_val);
int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask);
int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg,
u8 mask, unsigned int timeout_us);
int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val);
int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val);
int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val);
@ -300,6 +318,19 @@ zl3073x_dev_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
return zl3073x_synth_is_enabled(synth) && zl3073x_out_is_enabled(out);
}
/**
* zl3073x_dev_out_is_stepped - check if output is in step-time mask
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: true if output is affected by step-time operations
*/
static inline bool
zl3073x_dev_out_is_stepped(struct zl3073x_dev *zldev, u8 index)
{
return !!(zldev->out_step_time_mask & BIT(index));
}
/**
* zl3073x_dev_out_dpll_get - get DPLL ID the output is driven by
* @zldev: pointer to zl3073x device

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
#include <linux/dpll.h>
#include <linux/list.h>
#include <linux/ptp_clock_kernel.h>
#include "core.h"
@ -18,8 +19,12 @@
* @ops: DPLL device operations for this instance
* @dpll_dev: pointer to registered DPLL device
* @tracker: tracking object for the acquired reference
* @lock: per-DPLL mutex serializing all operations
* @type: DPLL type (PPS or EEC)
* @lock_status: last saved DPLL lock status
* @pins: list of pins
* @ptp_info: PTP clock info
* @ptp_clock: registered PTP clock (or NULL)
*/
struct zl3073x_dpll {
struct list_head list;
@ -30,8 +35,12 @@ struct zl3073x_dpll {
struct dpll_device_ops ops;
struct dpll_device *dpll_dev;
dpll_tracker tracker;
struct mutex lock;
enum dpll_type type;
enum dpll_lock_status lock_status;
struct list_head pins;
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp_clock;
};
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);

View File

@ -85,12 +85,8 @@ int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
&out->phase_comp);
if (rc)
return rc;
return rc;
return zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
&out->phase_comp);
}
/**

View File

@ -5,6 +5,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/limits.h>
/*
* Hardware limits for ZL3073x chip family
@ -17,6 +18,7 @@
#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
ZL3073X_NUM_OUTPUT_PINS)
#define ZL3073X_NCO_PIN_ID ZL3073X_NUM_PINS
/*
* Register address structure:
@ -164,10 +166,32 @@
#define ZL_DPLL_MODE_REFSEL_MODE_NCO 4
#define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4)
#define ZL_REG_DPLL_CTRL(_idx) \
ZL_REG_IDX(_idx, 5, 0x05, 1, ZL3073X_MAX_CHANNELS, 4)
#define ZL_DPLL_CTRL_TIE_CLEAR BIT(0)
#define ZL_DPLL_CTRL_TOD_STEP_RST BIT(2)
#define ZL_DPLL_CTRL_NCO_AUTO_READ BIT(7)
#define ZL_REG_DPLL_DF_READ(_idx) \
ZL_REG_IDX(_idx, 5, 0x28, 1, ZL3073X_MAX_CHANNELS, 1)
#define ZL_DPLL_DF_READ_SEM BIT(4)
#define ZL_DPLL_DF_READ_REF_OFST BIT(3)
#define ZL_DPLL_DF_READ_CMD GENMASK(2, 0)
#define ZL_DPLL_DF_READ_CMD_ACC_I 4
#define ZL_REG_DPLL_TIE_CTRL ZL_REG(5, 0x30, 1)
#define ZL_DPLL_TIE_CTRL_OP GENMASK(2, 0)
#define ZL_DPLL_TIE_CTRL_OP_WR 4
#define ZL_REG_DPLL_TIE_CTRL_MASK ZL_REG(5, 0x31, 1)
#define ZL_REG_DPLL_TOD_CTRL(_idx) \
ZL_REG_IDX(_idx, 5, 0x38, 1, ZL3073X_MAX_CHANNELS, 1)
#define ZL_DPLL_TOD_CTRL_SEM BIT(4)
#define ZL_DPLL_TOD_CTRL_CMD GENMASK(3, 0)
#define ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ 1
#define ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT 8
#define ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ 9
#define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1)
#define ZL_DPLL_MEAS_CTRL_EN BIT(0)
@ -183,6 +207,9 @@
/*******************************
* Register Pages 6-7, DPLL Data
*
* Per-channel registers with stride 0x20. Channels 0-3 reside on page 6,
* channel 4 on page 7.
*******************************/
#define ZL_REG_DPLL_DF_OFFSET_03(_idx) \
@ -190,6 +217,25 @@
#define ZL_REG_DPLL_DF_OFFSET_4 ZL_REG(7, 0x00, 6)
#define ZL_REG_DPLL_DF_OFFSET(_idx) \
((_idx) < 4 ? ZL_REG_DPLL_DF_OFFSET_03(_idx) : ZL_REG_DPLL_DF_OFFSET_4)
#define ZL_DPLL_DF_OFFSET_UNKNOWN S64_MIN
#define ZL_REG_DPLL_TIE_DATA_03(_idx) \
ZL_REG_IDX(_idx, 6, 0x0C, 6, 4, 0x20)
#define ZL_REG_DPLL_TIE_DATA_4 ZL_REG(7, 0x0C, 6)
#define ZL_REG_DPLL_TIE_DATA(_idx) \
((_idx) < 4 ? ZL_REG_DPLL_TIE_DATA_03(_idx) : ZL_REG_DPLL_TIE_DATA_4)
#define ZL_REG_DPLL_TOD_SEC_03(_idx) \
ZL_REG_IDX(_idx, 6, 0x12, 6, 4, 0x20)
#define ZL_REG_DPLL_TOD_SEC_4 ZL_REG(7, 0x12, 6)
#define ZL_REG_DPLL_TOD_SEC(_idx) \
((_idx) < 4 ? ZL_REG_DPLL_TOD_SEC_03(_idx) : ZL_REG_DPLL_TOD_SEC_4)
#define ZL_REG_DPLL_TOD_NS_03(_idx) \
ZL_REG_IDX(_idx, 6, 0x18, 4, 4, 0x20)
#define ZL_REG_DPLL_TOD_NS_4 ZL_REG(7, 0x18, 4)
#define ZL_REG_DPLL_TOD_NS(_idx) \
((_idx) < 4 ? ZL_REG_DPLL_TOD_NS_03(_idx) : ZL_REG_DPLL_TOD_NS_4)
/***********************************
* Register Page 9, Synth and Output
@ -210,6 +256,23 @@
#define ZL_OUTPUT_CTRL_EN BIT(0)
#define ZL_OUTPUT_CTRL_SYNTH_SEL GENMASK(6, 4)
#define ZL_REG_OUTPUT_STEP_TIME_MASK ZL_REG(9, 0x36, 2)
#define ZL_REG_OUTPUT_PHASE_STEP_CTRL ZL_REG(9, 0x38, 1)
#define ZL_OUTPUT_PHASE_STEP_CTRL_DPLL GENMASK(6, 4)
#define ZL_OUTPUT_PHASE_STEP_CTRL_TOD_STEP BIT(3)
#define ZL_OUTPUT_PHASE_STEP_CTRL_OP GENMASK(1, 0)
#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_NONE 0
#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_RESET 1
#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_READ 2
#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_WRITE 3
#define ZL_REG_OUTPUT_PHASE_STEP_NUMBER ZL_REG(9, 0x39, 1)
#define ZL_REG_OUTPUT_PHASE_STEP_MASK ZL_REG(9, 0x3a, 2)
#define ZL_REG_OUTPUT_PHASE_STEP_DATA ZL_REG(9, 0x3c, 4)
/*******************************
* Register Page 10, Ref Mailbox
*******************************/

View File

@ -36,7 +36,7 @@ static struct sk_buff *rdma_build_skb(struct net_device *netdev,
uh->source =
htons(rdma_flow_label_to_udp_sport(ah_attr->grh.flow_label));
uh->dest = htons(ROCE_V2_UDP_DPORT);
uh->len = htons(sizeof(struct udphdr));
udp_set_len_short(uh, sizeof(struct udphdr));
if (is_ipv4) {
skb_push(skb, sizeof(struct iphdr));

View File

@ -242,7 +242,7 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
pkt->port_num = 1;
pkt->hdr = (u8 *)(udph + 1);
pkt->mask = RXE_GRH_MASK;
pkt->paylen = be16_to_cpu(udph->len) - sizeof(*udph);
pkt->paylen = udp_get_len_short(udph) - sizeof(*udph);
/* remove udp header */
skb_pull(skb, sizeof(struct udphdr));
@ -305,7 +305,7 @@ static void prepare_udp_hdr(struct sk_buff *skb, __be16 src_port,
udph->dest = dst_port;
udph->source = src_port;
udph->len = htons(skb->len);
udp_set_len_short(udph, skb->len);
udph->check = 0;
}

View File

@ -208,6 +208,9 @@ static const struct mmc_fixup __maybe_unused sdio_fixup_methods[] = {
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_F0,
add_limit_rate_quirk, 150000000),
SDIO_FIXUP(SDIO_VENDOR_ID_NXP, SDIO_DEVICE_ID_NXP_IW61X_BASE,
add_quirk, MMC_QUIRK_BLKSZ_FOR_BYTE_MODE),
END_FIXUP
};

View File

@ -667,7 +667,7 @@ static void amt_send_discovery(struct amt_dev *amt)
udph = udp_hdr(skb);
udph->source = amt->gw_port;
udph->dest = amt->relay_port;
udph->len = htons(sizeof(*udph) + sizeof(*amtd));
udp_set_len_short(udph, sizeof(*udph) + sizeof(*amtd));
udph->check = 0;
offset = skb_transport_offset(skb);
skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
@ -708,11 +708,13 @@ static void amt_send_request(struct amt_dev *amt, bool v6)
struct iphdr *iph;
struct rtable *rt;
struct flowi4 fl4;
__be32 remote_ip;
struct sock *sk;
u32 len;
int err;
rcu_read_lock();
remote_ip = READ_ONCE(amt->remote_ip);
sk = rcu_dereference(amt->sk);
if (!sk)
goto out;
@ -721,7 +723,7 @@ static void amt_send_request(struct amt_dev *amt, bool v6)
goto out;
rt = ip_route_output_ports(amt->net, &fl4, sk,
amt->remote_ip, amt->local_ip,
remote_ip, amt->local_ip,
amt->gw_port, amt->relay_port,
IPPROTO_UDP, 0,
amt->stream_dev->ifindex);
@ -758,11 +760,11 @@ static void amt_send_request(struct amt_dev *amt, bool v6)
udph = udp_hdr(skb);
udph->source = amt->gw_port;
udph->dest = amt->relay_port;
udph->len = htons(sizeof(*amtrh) + sizeof(*udph));
udp_set_len_short(udph, sizeof(*amtrh) + sizeof(*udph));
udph->check = 0;
offset = skb_transport_offset(skb);
skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
udph->check = csum_tcpudp_magic(amt->local_ip, amt->remote_ip,
udph->check = csum_tcpudp_magic(amt->local_ip, remote_ip,
sizeof(*udph) + sizeof(*amtrh),
IPPROTO_UDP, skb->csum);
@ -773,7 +775,7 @@ static void amt_send_request(struct amt_dev *amt, bool v6)
iph->tos = AMT_TOS;
iph->frag_off = 0;
iph->ttl = ip4_dst_hoplimit(&rt->dst);
iph->daddr = amt->remote_ip;
iph->daddr = remote_ip;
iph->saddr = amt->local_ip;
iph->protocol = IPPROTO_UDP;
iph->tot_len = htons(len);
@ -962,7 +964,7 @@ static void amt_event_send_request(struct amt_dev *amt)
amt->qi = AMT_INIT_REQ_TIMEOUT;
WRITE_ONCE(amt->ready4, false);
WRITE_ONCE(amt->ready6, false);
amt->remote_ip = 0;
WRITE_ONCE(amt->remote_ip, 0);
amt_update_gw_status(amt, AMT_STATUS_INIT, false);
amt->req_cnt = 0;
amt->nonce = 0;
@ -999,6 +1001,7 @@ static bool amt_send_membership_update(struct amt_dev *amt,
struct sk_buff *skb,
bool v6)
{
__be32 remote_ip = READ_ONCE(amt->remote_ip);
struct amt_header_membership_update *amtmu;
struct iphdr *iph;
struct flowi4 fl4;
@ -1018,13 +1021,13 @@ static bool amt_send_membership_update(struct amt_dev *amt,
skb_reset_inner_headers(skb);
memset(&fl4, 0, sizeof(struct flowi4));
fl4.flowi4_oif = amt->stream_dev->ifindex;
fl4.daddr = amt->remote_ip;
fl4.daddr = remote_ip;
fl4.saddr = amt->local_ip;
fl4.flowi4_dscp = inet_dsfield_to_dscp(AMT_TOS);
fl4.flowi4_proto = IPPROTO_UDP;
rt = ip_route_output_key(amt->net, &fl4);
if (IS_ERR(rt)) {
netdev_dbg(amt->dev, "no route to %pI4\n", &amt->remote_ip);
netdev_dbg(amt->dev, "no route to %pI4\n", &remote_ip);
return true;
}
@ -2286,8 +2289,8 @@ static bool amt_advertisement_handler(struct amt_dev *amt, struct sk_buff *skb)
amt->nonce != amta->nonce)
return true;
amt->remote_ip = amta->ip4;
netdev_dbg(amt->dev, "advertised remote ip = %pI4\n", &amt->remote_ip);
WRITE_ONCE(amt->remote_ip, amta->ip4);
netdev_dbg(amt->dev, "advertised remote ip = %pI4\n", &amta->ip4);
mod_delayed_work(amt_wq, &amt->req_wq, 0);
amt_update_gw_status(amt, AMT_STATUS_RECEIVED_ADVERTISEMENT, true);
@ -2647,7 +2650,7 @@ static void amt_send_advertisement(struct amt_dev *amt, __be32 nonce,
udph = udp_hdr(skb);
udph->source = amt->relay_port;
udph->dest = dport;
udph->len = htons(sizeof(*amta) + sizeof(*udph));
udp_set_len_short(udph, sizeof(*amta) + sizeof(*udph));
udph->check = 0;
offset = skb_transport_offset(skb);
skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
@ -2811,6 +2814,7 @@ static void amt_gw_rcv(struct amt_dev *amt, struct sk_buff *skb)
static int amt_rcv(struct sock *sk, struct sk_buff *skb)
{
struct amt_dev *amt;
__be32 remote_ip;
__be32 saddr;
int type;
bool err;
@ -2822,6 +2826,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
kfree_skb(skb);
goto out;
}
remote_ip = READ_ONCE(amt->remote_ip);
skb->dev = amt->dev;
saddr = ip_hdr(skb)->saddr;
@ -2846,7 +2851,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
}
goto out;
case AMT_MSG_MULTICAST_DATA:
if (saddr != amt->remote_ip) {
if (saddr != remote_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
@ -2857,7 +2862,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
else
goto out;
case AMT_MSG_MEMBERSHIP_QUERY:
if (saddr != amt->remote_ip) {
if (saddr != remote_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
@ -3045,7 +3050,7 @@ static int amt_dev_open(struct net_device *dev)
}
amt->req_cnt = 0;
amt->remote_ip = 0;
WRITE_ONCE(amt->remote_ip, 0);
amt->nonce = 0;
get_random_bytes(&amt->key, sizeof(siphash_key_t));
@ -3090,7 +3095,7 @@ static int amt_dev_stop(struct net_device *dev)
amt->ready4 = false;
amt->ready6 = false;
amt->req_cnt = 0;
amt->remote_ip = 0;
WRITE_ONCE(amt->remote_ip, 0);
list_for_each_entry_safe(tunnel, tmp, &amt->tunnel_list, list) {
list_del_rcu(&tunnel->list);
@ -3221,6 +3226,9 @@ static int amt_newlink(struct net_device *dev,
struct nlattr **tb = params->tb;
int err = -EINVAL;
if (!net_eq(link_net, dev_net(dev)))
return err;
amt->net = link_net;
amt->mode = nla_get_u32(data[IFLA_AMT_MODE]);
@ -3289,7 +3297,7 @@ static int amt_newlink(struct net_device *dev,
"gateway port must not be 0");
goto err;
}
amt->remote_ip = 0;
WRITE_ONCE(amt->remote_ip, 0);
amt->discovery_ip = nla_get_in_addr(data[IFLA_AMT_DISCOVERY_IP]);
if (ipv4_is_loopback(amt->discovery_ip) ||
ipv4_is_zeronet(amt->discovery_ip) ||
@ -3355,8 +3363,10 @@ static size_t amt_get_size(const struct net_device *dev)
static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct amt_dev *amt = netdev_priv(dev);
const struct amt_dev *amt = netdev_priv(dev);
__be32 remote_ip;
rcu_read_lock();
if (nla_put_u32(skb, IFLA_AMT_MODE, amt->mode))
goto nla_put_failure;
if (nla_put_be16(skb, IFLA_AMT_RELAY_PORT, amt->relay_port))
@ -3369,15 +3379,19 @@ static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev)
goto nla_put_failure;
if (nla_put_in_addr(skb, IFLA_AMT_DISCOVERY_IP, amt->discovery_ip))
goto nla_put_failure;
if (amt->remote_ip)
if (nla_put_in_addr(skb, IFLA_AMT_REMOTE_IP, amt->remote_ip))
remote_ip = READ_ONCE(amt->remote_ip);
if (remote_ip)
if (nla_put_in_addr(skb, IFLA_AMT_REMOTE_IP, remote_ip))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_AMT_MAX_TUNNELS, amt->max_tunnels))
goto nla_put_failure;
rcu_read_unlock();
return 0;
nla_put_failure:
rcu_read_unlock();
return -EMSGSIZE;
}

View File

@ -36,6 +36,7 @@ static unsigned int bareudp_net_id;
struct bareudp_net {
struct list_head bareudp_list;
struct mutex lock;
};
struct bareudp_conf {
@ -636,10 +637,15 @@ static struct bareudp_dev *bareudp_find_dev(struct bareudp_net *bn,
{
struct bareudp_dev *bareudp, *t = NULL;
mutex_lock(&bn->lock);
list_for_each_entry(bareudp, &bn->bareudp_list, next) {
if (conf->port == bareudp->port)
t = bareudp;
}
mutex_unlock(&bn->lock);
return t;
}
@ -675,7 +681,10 @@ static int bareudp_configure(struct net *net, struct net_device *dev,
if (err)
return err;
mutex_lock(&bn->lock);
list_add(&bareudp->next, &bn->bareudp_list);
mutex_unlock(&bn->lock);
return 0;
}
@ -692,12 +701,26 @@ static int bareudp_link_config(struct net_device *dev,
return 0;
}
static void bareudp_dellink(struct net_device *dev, struct list_head *head)
static void __bareudp_dellink(struct net *net, struct net_device *dev,
struct list_head *head)
{
struct bareudp_dev *bareudp = netdev_priv(dev);
list_del(&bareudp->next);
unregister_netdevice_queue(dev, head);
list_del_init(&bareudp->next);
unregister_netdevice_queue_net(net, dev, head);
}
static void bareudp_dellink(struct net_device *dev, struct list_head *head)
{
struct bareudp_dev *bareudp = netdev_priv(dev);
struct bareudp_net *bn;
bn = net_generic(bareudp->net, bareudp_net_id);
mutex_lock(&bn->lock);
if (!list_empty(&bareudp->next))
__bareudp_dellink(dev_net(dev), dev, head);
mutex_unlock(&bn->lock);
}
static int bareudp_newlink(struct net_device *dev,
@ -776,6 +799,8 @@ static __net_init int bareudp_init_net(struct net *net)
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
INIT_LIST_HEAD(&bn->bareudp_list);
mutex_init(&bn->lock);
return 0;
}
@ -785,13 +810,25 @@ static void __net_exit bareudp_exit_rtnl_net(struct net *net,
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
struct bareudp_dev *bareudp, *next;
mutex_lock(&bn->lock);
list_for_each_entry_safe(bareudp, next, &bn->bareudp_list, next)
bareudp_dellink(bareudp->dev, dev_kill_list);
__bareudp_dellink(net, bareudp->dev, dev_kill_list);
mutex_unlock(&bn->lock);
}
static void __net_exit bareudp_exit_net(struct net *net)
{
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
WARN_ON_ONCE(!list_empty(&bn->bareudp_list));
}
static struct pernet_operations bareudp_net_ops = {
.init = bareudp_init_net,
.exit_rtnl = bareudp_exit_rtnl_net,
.exit = bareudp_exit_net,
.id = &bareudp_net_id,
.size = sizeof(struct bareudp_net),
};

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